fix(structure): keep Oracle case-sensitive identifiers quoted when quoting off

This commit is contained in:
zipg authored and GitHub committed 2026-09-21 12:52:44 +08:00
1 parent f8ee34f937
commit f19d842b8f
8 files changed
+140 -12

No files matched your search

+16
View File
@@ -142,6 +142,22 @@ describe("omitDdlIdentifierQuotes", () => {
const ddl = 'ALTER TABLE "SYSTEM"."TEST" RENAME COLUMN "ABCD" TO "AbCd";';
expect(formatGeneratedDdlIdentifierQuotes(ddl, "oracle", true)).toBe(ddl);
});
it("keeps case-sensitive Oracle identifiers quoted when the edited table preserves case (#9649)", () => {
const ddl = 'ALTER TABLE "DBX_TEST"."T_9649" MODIFY ("cName" VARCHAR2(120 BYTE));\nALTER TABLE "DBX_TEST"."T_9649" ADD ("cNabcs" clob);\nCOMMENT ON COLUMN "DBX_TEST"."T_9649"."cName" IS \'中文名\';';
const expected = 'ALTER TABLE DBX_TEST.T_9649 MODIFY ("cName" VARCHAR2(120 BYTE));\nALTER TABLE DBX_TEST.T_9649 ADD ("cNabcs" clob);\nCOMMENT ON COLUMN DBX_TEST.T_9649."cName" IS \'中文名\';';
expect(formatGeneratedDdlIdentifierQuotes(ddl, "oracle", false, { preserveCaseSensitiveIdentifiers: true })).toBe(expected);
});
it("still folds plain Oracle identifiers while preserving case-sensitive ones (#9649)", () => {
const ddl = 'ALTER TABLE "SYSTEM"."TEST" ADD ("ID" NUMBER(10), "NEW_COL" VARCHAR2(20), "with space" VARCHAR2(20));';
expect(formatGeneratedDdlIdentifierQuotes(ddl, "oracle", false, { preserveCaseSensitiveIdentifiers: true })).toBe('ALTER TABLE SYSTEM.TEST ADD (ID NUMBER(10), NEW_COL VARCHAR2(20), "with space" VARCHAR2(20));');
});
it("ignores the case-preservation option for dialects that keep their own rules", () => {
const ddl = 'ALTER TABLE "user_login_log" ADD "status" INT;';
expect(formatGeneratedDdlIdentifierQuotes(ddl, "dameng", false, { preserveCaseSensitiveIdentifiers: true })).toBe(ddl);
});
});
describe("ddlFormatDialectFor", () => {
+17 -4
View File
@@ -64,25 +64,38 @@ export function omitDdlIdentifierQuotes(sql: string, dialect: SqlFormatDialect):
const SIMPLE_ORACLE_IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_$#]*$/;
export interface GeneratedDdlIdentifierQuoteOptions {
/**
* Set by structure editors whose edited table already stores a lowercase or
* mixed-case identifier. Oracle folds bare identifiers to uppercase, so
* unquoting `"cName"` turns the statement into `CNAME` — a different column,
* which fails with ORA-00904 or silently creates a differently-cased column.
* Preserving the case keeps quotes on every name that would change identity
* while plain names keep the folding behaviour requested in #8997.
*/
preserveCaseSensitiveIdentifiers?: boolean;
}
/**
* 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 {
export function formatGeneratedDdlIdentifierQuotes(sql: string, dialect: SqlFormatDialect, quoteIdentifiers: boolean, options: GeneratedDdlIdentifierQuoteOptions = {}): string {
if (quoteIdentifiers) return sql;
if (dialect !== "oracle") return omitDdlIdentifierQuotes(sql, dialect);
const preserveCaseSensitive = options.preserveCaseSensitiveIdentifiers === true;
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() });
}
if (!SIMPLE_ORACLE_IDENTIFIER.test(identifier) || requiresOracleIdentifierQuote(identifier.toUpperCase())) continue;
if (preserveCaseSensitive && identifier !== identifier.toUpperCase()) continue;
replacements.push({ start: token.span.start, end: token.span.end, value: identifier.toUpperCase() });
}
let result = sql;