fix(sqlserver): accept cursor declarations in SQL analysis
Closes #5860
This commit is contained in:
1 parent
2db2041680
commit
7f405eacfb
4 files changed
+375
-4
No files matched your search
@@ -25,7 +25,8 @@ export interface SqlSemanticDiagnosticVisibleRange {
|
||||
}
|
||||
|
||||
export function sqlSemanticDiagnosticRangesForViewport(sql: string, visibleRanges: readonly SqlSemanticDiagnosticVisibleRange[], databaseType?: DatabaseType): SqlTextRange[] {
|
||||
const statements = executableStatementRanges(sql, databaseType);
|
||||
const executableRanges = executableStatementRanges(sql, databaseType);
|
||||
const statements = databaseType === "sqlserver" ? mergeSqlServerDiagnosticBatchRanges(sql, executableRanges) : executableRanges;
|
||||
if (statements.length === 0 || visibleRanges.length === 0) return [];
|
||||
|
||||
const selected: SqlTextRange[] = [];
|
||||
@@ -41,6 +42,68 @@ export function sqlSemanticDiagnosticRangesForViewport(sql: string, visibleRange
|
||||
return selected;
|
||||
}
|
||||
|
||||
function mergeSqlServerDiagnosticBatchRanges(sql: string, statements: readonly SqlTextRange[]): SqlTextRange[] {
|
||||
if (statements.length < 2) return [...statements];
|
||||
|
||||
const ranges: SqlTextRange[] = [];
|
||||
let batch: SqlTextRange[] = [statements[0]];
|
||||
for (const statement of statements.slice(1)) {
|
||||
if (sqlServerGapContainsGoSeparator(sql.slice(batch[batch.length - 1].to, statement.from))) {
|
||||
pushSqlServerDiagnosticBatchRanges(ranges, sql, batch);
|
||||
batch = [];
|
||||
}
|
||||
batch.push(statement);
|
||||
}
|
||||
pushSqlServerDiagnosticBatchRanges(ranges, sql, batch);
|
||||
return ranges;
|
||||
}
|
||||
|
||||
function pushSqlServerDiagnosticBatchRanges(ranges: SqlTextRange[], sql: string, batch: readonly SqlTextRange[]) {
|
||||
if (batch.length > 1 && batch.some((statement) => sqlServerStatementNeedsBatchContext(statement.sql))) {
|
||||
const from = batch[0].from;
|
||||
const to = batch[batch.length - 1].to;
|
||||
ranges.push({ from, to, sql: sql.slice(from, to) });
|
||||
} else {
|
||||
ranges.push(...batch);
|
||||
}
|
||||
}
|
||||
|
||||
function sqlServerStatementNeedsBatchContext(sql: string): boolean {
|
||||
return /^\s*(?:WHILE|BEGIN|END|IF|ELSE|TRY|CATCH)\b/i.test(sql) || /^\s*DECLARE\s+(?:\[[^\]]+\]|"[^"]+"|[A-Z_@#][\w@$#]*)\s+CURSOR\b/i.test(sql);
|
||||
}
|
||||
|
||||
function sqlServerGapContainsGoSeparator(gap: string): boolean {
|
||||
let inBlockComment = false;
|
||||
let lineStart = 0;
|
||||
while (lineStart <= gap.length) {
|
||||
const newline = gap.indexOf("\n", lineStart);
|
||||
const lineEnd = newline < 0 ? gap.length : newline;
|
||||
const line = gap.slice(lineStart, lineEnd).replace(/\r$/, "");
|
||||
if (!inBlockComment && /^\s*go(?:\s+\d+)?\s*$/i.test(line)) return true;
|
||||
|
||||
let offset = 0;
|
||||
while (offset < line.length) {
|
||||
if (inBlockComment) {
|
||||
const close = line.indexOf("*/", offset);
|
||||
if (close < 0) break;
|
||||
inBlockComment = false;
|
||||
offset = close + 2;
|
||||
continue;
|
||||
}
|
||||
const open = line.indexOf("/*", offset);
|
||||
if (open < 0) break;
|
||||
const lineComment = line.indexOf("--", offset);
|
||||
if (lineComment >= 0 && lineComment < open) break;
|
||||
inBlockComment = true;
|
||||
offset = open + 2;
|
||||
}
|
||||
|
||||
if (newline < 0) break;
|
||||
lineStart = newline + 1;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function buildSqlSemanticDiagnostics(analysis: SqlReferenceAnalysis, schema: SqlSemanticDiagnosticSchema): SqlSemanticDiagnostic[] {
|
||||
const diagnostics: SqlSemanticDiagnostic[] = [];
|
||||
const tables = analysis.tables.filter((table) => table.name.trim());
|
||||
|
||||
Reference in new issue
Block a user