mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-20 00:17:44 +00:00
fix(structure): unquote plain Oracle identifiers when quoting off
This commit is contained in:
@@ -27,7 +27,7 @@ import { createDbxCodeMirrorSqlDialect } from "@/lib/editor/codemirrorSqlDialect
|
||||
import { useToast } from "@/composables/useToast";
|
||||
import { type SqlHighlighter, createShikiSqlHighlighter } from "@/lib/sql/sqlHighlighter";
|
||||
import { joinSqlStatementsForScript } from "@/lib/sql/sqlBatchScript";
|
||||
import { omitDdlIdentifierQuotes } from "@/lib/sql/ddlDisplay";
|
||||
import { formatGeneratedDdlIdentifierQuotes } from "@/lib/sql/ddlDisplay";
|
||||
import { splitSqlStatementRanges } from "@/lib/sql/sqlStatementRanges";
|
||||
import { copyToClipboard } from "@/lib/common/clipboard";
|
||||
import { formatSqlForDisplay, sqlFormatDialectForDbType } from "@/lib/sql/sqlFormatter";
|
||||
@@ -1767,7 +1767,7 @@ async function refreshSqlPreview() {
|
||||
if (requestId !== sqlPreviewRequestId) return;
|
||||
const statements = [...result.statements, ...ownerResult.statements, ...(mysqlAutoIncrementStatement ? [mysqlAutoIncrementStatement] : [])];
|
||||
// SQLite type-change apply regenerates this revision-checked plan, so its preview must stay byte-for-byte aligned.
|
||||
pendingStatements.value = settingsStore.editorSettings.generateSqlQuoteIdentifiers || hasSqliteTypeChange.value ? statements : statements.map((statement) => omitDdlIdentifierQuotes(statement, sqlFormatDialectForDbType(databaseType.value)));
|
||||
pendingStatements.value = settingsStore.editorSettings.generateSqlQuoteIdentifiers !== false || hasSqliteTypeChange.value ? statements : statements.map((statement) => formatGeneratedDdlIdentifierQuotes(statement, sqlFormatDialectForDbType(databaseType.value), false));
|
||||
warnings.value = [...result.warnings, ...ownerResult.warnings];
|
||||
sqliteSchemaRevision.value = "schemaRevision" in result && typeof result.schemaRevision === "string" ? result.schemaRevision : undefined;
|
||||
} catch (e: any) {
|
||||
|
||||
+16
-1
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { omitDdlIdentifierQuotes } from "@/lib/sql/ddlDisplay";
|
||||
import { formatGeneratedDdlIdentifierQuotes, omitDdlIdentifierQuotes } from "@/lib/sql/ddlDisplay";
|
||||
|
||||
describe("omitDdlIdentifierQuotes", () => {
|
||||
it("removes safe MySQL identifier quotes without changing literals or comments", () => {
|
||||
@@ -40,4 +40,19 @@ describe("omitDdlIdentifierQuotes", () => {
|
||||
const ddl = 'CREATE TABLE "CamelCase" ("lowercase" INT, "WITH SPACE" INT, "ORDER" INT, "A$B" INT)';
|
||||
expect(omitDdlIdentifierQuotes(ddl, "dameng")).toBe(ddl);
|
||||
});
|
||||
|
||||
it("folds safe Oracle identifiers when generated SQL quoting is disabled", () => {
|
||||
const ddl = 'ALTER TABLE "SYSTEM"."TEST" RENAME COLUMN "ABCD" TO "AbCd";';
|
||||
expect(formatGeneratedDdlIdentifierQuotes(ddl, "oracle", false)).toBe("ALTER TABLE SYSTEM.TEST RENAME COLUMN ABCD TO ABCD;");
|
||||
});
|
||||
|
||||
it("keeps case-sensitive Dameng identifiers quoted when generated SQL quoting is disabled", () => {
|
||||
const ddl = 'ALTER TABLE "user_login_log" ADD "status" INT; ALTER TABLE "CamelCase" ADD "order" INT;';
|
||||
expect(formatGeneratedDdlIdentifierQuotes(ddl, "dameng", false)).toBe(ddl);
|
||||
});
|
||||
|
||||
it("preserves generated identifier quotes when the setting is enabled", () => {
|
||||
const ddl = 'ALTER TABLE "SYSTEM"."TEST" RENAME COLUMN "ABCD" TO "AbCd";';
|
||||
expect(formatGeneratedDdlIdentifierQuotes(ddl, "oracle", true)).toBe(ddl);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -43,3 +43,34 @@ export function omitDdlIdentifierQuotes(sql: string, dialect: SqlFormatDialect):
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const SIMPLE_ORACLE_IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_$#]*$/;
|
||||
|
||||
/**
|
||||
* Applies the identifier-quoting preference to SQL generated by a structure
|
||||
* editor. Oracle folds bare identifiers to uppercase, so safe quoted names
|
||||
* are uppercased before their quotes are removed. Dameng keeps its stricter
|
||||
* case-sensitive identifier rules through omitDdlIdentifierQuotes instead.
|
||||
*/
|
||||
export function formatGeneratedDdlIdentifierQuotes(sql: string, dialect: SqlFormatDialect, quoteIdentifiers: boolean): string {
|
||||
if (quoteIdentifiers) return sql;
|
||||
if (dialect !== "oracle") return omitDdlIdentifierQuotes(sql, dialect);
|
||||
|
||||
const tokens = tokenizeSqlSemantic(sql, dialect);
|
||||
const replacements: Array<{ start: number; end: number; value: string }> = [];
|
||||
|
||||
for (const token of tokens) {
|
||||
if (token.kind !== "quoted_identifier") continue;
|
||||
const identifier = unquoteSqlSemanticIdentifier(token);
|
||||
if (SIMPLE_ORACLE_IDENTIFIER.test(identifier) && !requiresOracleIdentifierQuote(identifier.toUpperCase())) {
|
||||
replacements.push({ start: token.span.start, end: token.span.end, value: identifier.toUpperCase() });
|
||||
}
|
||||
}
|
||||
|
||||
let result = sql;
|
||||
for (let index = replacements.length - 1; index >= 0; index -= 1) {
|
||||
const replacement = replacements[index]!;
|
||||
result = `${result.slice(0, replacement.start)}${replacement.value}${result.slice(replacement.end)}`;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user