mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-07-06 18:41:42 +08:00
🐛 fix(sql-editor): 修复存储过程定义执行截断
This commit is contained in:
@@ -18,7 +18,7 @@ import { applyQueryAutoLimit } from '../utils/queryAutoLimit';
|
||||
import { extractQueryResultTableRef, type QueryResultTableRef } from '../utils/queryResultTable';
|
||||
import { quoteIdentPart } from '../utils/sql';
|
||||
import { formatSqlExecutionError } from '../utils/sqlErrorSemantics';
|
||||
import { resolveCurrentSqlStatementRange, resolveExecutableSql } from '../utils/sqlStatementSelection';
|
||||
import { findSqlStatementRanges, resolveCurrentSqlStatementRange, resolveExecutableSql } from '../utils/sqlStatementSelection';
|
||||
import { isMacLikePlatform } from '../utils/appearance';
|
||||
import { splitSidebarQualifiedName } from '../utils/sidebarLocate';
|
||||
import { normalizeSidebarViewName } from '../utils/sidebarMetadata';
|
||||
@@ -741,65 +741,6 @@ const areSqlStatementListsEqual = (left: string[], right: string[]): boolean =>
|
||||
&& left.every((statement, index) => normalizeExecutedSqlKey(statement) === normalizeExecutedSqlKey(right[index]))
|
||||
);
|
||||
|
||||
const isSqlIdentifierStart = (ch: string): boolean => /^[A-Za-z_]$/.test(ch);
|
||||
|
||||
const isSqlIdentifierPart = (ch: string): boolean => /^[A-Za-z0-9_$#]$/.test(ch);
|
||||
|
||||
const skipSqlWhitespaceAndComments = (text: string, position: number): number => {
|
||||
let index = position;
|
||||
while (index < text.length) {
|
||||
const ch = text[index];
|
||||
const next = index + 1 < text.length ? text[index + 1] : '';
|
||||
if (ch === ' ' || ch === '\t' || ch === '\n' || ch === '\r' || ch === '\f') {
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
if (ch === '-' && next === '-') {
|
||||
index += 2;
|
||||
while (index < text.length && text[index] !== '\n') index += 1;
|
||||
continue;
|
||||
}
|
||||
if (ch === '/' && next === '*') {
|
||||
index += 2;
|
||||
while (index + 1 < text.length && !(text[index] === '*' && text[index + 1] === '/')) {
|
||||
index += 1;
|
||||
}
|
||||
if (index + 1 < text.length) index += 2;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return index;
|
||||
};
|
||||
|
||||
const nextSqlSignificantToken = (text: string, position: number): string => {
|
||||
const index = skipSqlWhitespaceAndComments(text, position);
|
||||
if (index >= text.length || !isSqlIdentifierStart(text[index])) return '';
|
||||
let end = index + 1;
|
||||
while (end < text.length && isSqlIdentifierPart(text[end])) end += 1;
|
||||
return text.slice(index, end).toLowerCase();
|
||||
};
|
||||
|
||||
const nextSqlSignificantChar = (text: string, position: number): string => {
|
||||
const index = skipSqlWhitespaceAndComments(text, position);
|
||||
return index >= text.length ? '' : text[index];
|
||||
};
|
||||
|
||||
const shouldEnterPlsqlBeginBlock = (text: string, tokenEnd: number): boolean => {
|
||||
const nextChar = nextSqlSignificantChar(text, tokenEnd);
|
||||
if (!nextChar || nextChar === ';') return false;
|
||||
return !['transaction', 'work', 'isolation', 'read', 'write'].includes(nextSqlSignificantToken(text, tokenEnd));
|
||||
};
|
||||
|
||||
const shouldEnterPlsqlDeclareBlock = (text: string, tokenEnd: number): boolean => {
|
||||
const nextToken = nextSqlSignificantToken(text, tokenEnd);
|
||||
return Boolean(nextToken);
|
||||
};
|
||||
|
||||
const isPlsqlControlEnd = (text: string, tokenEnd: number): boolean => (
|
||||
['if', 'loop', 'case'].includes(nextSqlSignificantToken(text, tokenEnd))
|
||||
);
|
||||
|
||||
const normalizeEditorPosition = (position: any): { lineNumber: number; column: number } | null => {
|
||||
if (!position) return null;
|
||||
const lineNumber = Number(position.positionLineNumber ?? position.lineNumber ?? position.endLineNumber ?? position.startLineNumber ?? position.selectionStartLineNumber);
|
||||
@@ -3834,169 +3775,7 @@ const QueryEditor: React.FC<{ tab: TabData; isActive?: boolean }> = ({ tab, isAc
|
||||
];
|
||||
|
||||
const splitSQLStatements = (sql: string): string[] => {
|
||||
const text = (sql || '').replace(/\r\n/g, '\n');
|
||||
const statements: string[] = [];
|
||||
|
||||
let cur = '';
|
||||
let inSingle = false;
|
||||
let inDouble = false;
|
||||
let inBacktick = false;
|
||||
let escaped = false;
|
||||
let inLineComment = false;
|
||||
let inBlockComment = false;
|
||||
let dollarTag: string | null = null; // postgres/kingbase: $$...$$ or $tag$...$tag$
|
||||
let plsqlDepth = 0;
|
||||
let plsqlDeclareBeginSkips = 0;
|
||||
let justClosedPLSQLBlock = false;
|
||||
|
||||
const push = () => {
|
||||
const s = cur.trim();
|
||||
if (s) statements.push(s);
|
||||
cur = '';
|
||||
};
|
||||
|
||||
const isWS = (ch: string) => ch === ' ' || ch === '\t' || ch === '\n' || ch === '\r';
|
||||
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
const ch = text[i];
|
||||
const next = i + 1 < text.length ? text[i + 1] : '';
|
||||
const prev = i > 0 ? text[i - 1] : '';
|
||||
const next2 = i + 2 < text.length ? text[i + 2] : '';
|
||||
|
||||
if (!inSingle && !inDouble && !inBacktick) {
|
||||
if (inLineComment) {
|
||||
cur += ch;
|
||||
if (ch === '\n') inLineComment = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inBlockComment) {
|
||||
cur += ch;
|
||||
if (ch === '*' && next === '/') {
|
||||
cur += next;
|
||||
i++;
|
||||
inBlockComment = false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Start comments
|
||||
if (ch === '/' && next === '*') {
|
||||
cur += ch + next;
|
||||
i++;
|
||||
inBlockComment = true;
|
||||
continue;
|
||||
}
|
||||
if (ch === '#') {
|
||||
cur += ch;
|
||||
inLineComment = true;
|
||||
continue;
|
||||
}
|
||||
if (ch === '-' && next === '-' && (i === 0 || isWS(prev)) && (next2 === '' || isWS(next2))) {
|
||||
cur += ch + next;
|
||||
i++;
|
||||
inLineComment = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Dollar-quoted strings (PG/Kingbase)
|
||||
if (dollarTag) {
|
||||
if (text.startsWith(dollarTag, i)) {
|
||||
cur += dollarTag;
|
||||
i += dollarTag.length - 1;
|
||||
dollarTag = null;
|
||||
} else {
|
||||
cur += ch;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (ch === '$') {
|
||||
const m = text.slice(i).match(/^\$[A-Za-z0-9_]*\$/);
|
||||
if (m && m[0]) {
|
||||
dollarTag = m[0];
|
||||
cur += dollarTag;
|
||||
i += dollarTag.length - 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (escaped) {
|
||||
cur += ch;
|
||||
escaped = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((inSingle || inDouble) && ch === '\\') {
|
||||
cur += ch;
|
||||
escaped = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!inDouble && !inBacktick && ch === '\'') {
|
||||
inSingle = !inSingle;
|
||||
cur += ch;
|
||||
continue;
|
||||
}
|
||||
if (!inSingle && !inBacktick && ch === '"') {
|
||||
inDouble = !inDouble;
|
||||
cur += ch;
|
||||
continue;
|
||||
}
|
||||
if (!inSingle && !inDouble && ch === '`') {
|
||||
inBacktick = !inBacktick;
|
||||
cur += ch;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!inSingle && !inDouble && !inBacktick && !dollarTag && isSqlIdentifierStart(ch)) {
|
||||
let end = i + 1;
|
||||
while (end < text.length && isSqlIdentifierPart(text[end])) {
|
||||
end += 1;
|
||||
}
|
||||
const token = text.slice(i, end).toLowerCase();
|
||||
if (token === 'begin' && plsqlDeclareBeginSkips > 0) {
|
||||
plsqlDeclareBeginSkips -= 1;
|
||||
justClosedPLSQLBlock = false;
|
||||
} else if (token === 'begin' && shouldEnterPlsqlBeginBlock(text, end)) {
|
||||
plsqlDepth += 1;
|
||||
justClosedPLSQLBlock = false;
|
||||
} else if (token === 'declare' && shouldEnterPlsqlDeclareBlock(text, end)) {
|
||||
plsqlDepth += 1;
|
||||
plsqlDeclareBeginSkips += 1;
|
||||
justClosedPLSQLBlock = false;
|
||||
} else if (token === 'end' && plsqlDepth > 0 && !isPlsqlControlEnd(text, end)) {
|
||||
plsqlDepth -= 1;
|
||||
if (plsqlDeclareBeginSkips > plsqlDepth) {
|
||||
plsqlDeclareBeginSkips = plsqlDepth;
|
||||
}
|
||||
justClosedPLSQLBlock = plsqlDepth === 0;
|
||||
}
|
||||
cur += text.slice(i, end);
|
||||
i = end - 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!inSingle && !inDouble && !inBacktick && !dollarTag && (ch === ';' || ch === ';')) {
|
||||
if (plsqlDepth > 0) {
|
||||
cur += ch;
|
||||
continue;
|
||||
}
|
||||
if (justClosedPLSQLBlock) {
|
||||
cur += ch;
|
||||
push();
|
||||
justClosedPLSQLBlock = false;
|
||||
continue;
|
||||
}
|
||||
push();
|
||||
continue;
|
||||
}
|
||||
|
||||
cur += ch;
|
||||
}
|
||||
|
||||
push();
|
||||
return statements;
|
||||
return findSqlStatementRanges(sql).map((range) => range.text);
|
||||
};
|
||||
|
||||
const getSelectedSQL = (): string => {
|
||||
|
||||
Reference in New Issue
Block a user