mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-20 00:17:44 +00:00
fix(oracle): format byte and code-point function variants as functions
Closes #11251
This commit is contained in:
@@ -28,6 +28,28 @@ function resolveClickHouseIdentifierSafeDialect(sqlFormatter: SqlFormatterModule
|
||||
return clickHouseIdentifierSafeDialect;
|
||||
}
|
||||
|
||||
// sql-formatter's plsql dialect only registers a minimal subset of character
|
||||
// functions (e.g. SUBSTR, INSTR, LENGTH) and omits Oracle's byte/multibyte/code-point
|
||||
// variants. Register them as reserved function names so functionCase normalizes them.
|
||||
const ORACLE_EXTENDED_FUNCTION_NAMES = ["SUBSTRB", "SUBSTR2", "SUBSTR4", "SUBSTRC", "INSTRB", "INSTR2", "INSTR4", "INSTRC", "LENGTHB", "LENGTH2", "LENGTH4", "LENGTHC"];
|
||||
let oracleExtendedDialect: SqlFormatterModule["plsql"] | null = null;
|
||||
|
||||
function resolveOracleExtendedDialect(sqlFormatter: SqlFormatterModule): SqlFormatterModule["plsql"] {
|
||||
if (oracleExtendedDialect) return oracleExtendedDialect;
|
||||
|
||||
const existingFunctions = new Set(sqlFormatter.plsql.tokenizerOptions.reservedFunctionNames);
|
||||
const extraFunctions = ORACLE_EXTENDED_FUNCTION_NAMES.filter((name) => !existingFunctions.has(name));
|
||||
|
||||
oracleExtendedDialect = {
|
||||
...sqlFormatter.plsql,
|
||||
tokenizerOptions: {
|
||||
...sqlFormatter.plsql.tokenizerOptions,
|
||||
reservedFunctionNames: [...sqlFormatter.plsql.tokenizerOptions.reservedFunctionNames, ...extraFunctions],
|
||||
},
|
||||
};
|
||||
return oracleExtendedDialect;
|
||||
}
|
||||
|
||||
export function canFormatSqlForDatabaseType(dbType: string | null | undefined): boolean {
|
||||
return dbType !== "redis" && dbType !== "victoriametrics";
|
||||
}
|
||||
@@ -367,7 +389,7 @@ export async function formatSqlText(sql: string, dialect: SqlFormatDialect = "ge
|
||||
},
|
||||
}
|
||||
: options;
|
||||
const resolvedDialect = dialect === "clickhouse" ? resolveClickHouseIdentifierSafeDialect(sqlFormatter) : undefined;
|
||||
const resolvedDialect = dialect === "clickhouse" ? resolveClickHouseIdentifierSafeDialect(sqlFormatter) : dialect === "oracle" ? resolveOracleExtendedDialect(sqlFormatter) : undefined;
|
||||
const formatWithFallback = (input: string): string => {
|
||||
try {
|
||||
if (resolvedDialect) {
|
||||
|
||||
+18
@@ -301,3 +301,21 @@ test("compressSqlText preserves unterminated block comments", () => {
|
||||
const sql = "DELETE FROM users /* unfinished";
|
||||
assert.equal(compressSqlText(sql), sql);
|
||||
});
|
||||
|
||||
test("Oracle: formats byte and code-point function variants with functionCase", async () => {
|
||||
const formattedLower = await formatSqlText("SELECT SUBSTR(a, 1), SUBSTRB(b, 1), INSTRB(c, 'x'), LENGTHB(d) FROM dual", "oracle", {
|
||||
functionCase: "lower",
|
||||
});
|
||||
assert.match(formattedLower, /\bsubstr\(/);
|
||||
assert.match(formattedLower, /\bsubstrb\(/);
|
||||
assert.match(formattedLower, /\binstrb\(/);
|
||||
assert.match(formattedLower, /\blengthb\(/);
|
||||
|
||||
const formattedUpper = await formatSqlText("select substr(a, 1), substrb(b, 1), instrb(c, 'x'), lengthb(d) from dual", "oracle", {
|
||||
functionCase: "upper",
|
||||
});
|
||||
assert.match(formattedUpper, /\bSUBSTR\(/);
|
||||
assert.match(formattedUpper, /\bSUBSTRB\(/);
|
||||
assert.match(formattedUpper, /\bINSTRB\(/);
|
||||
assert.match(formattedUpper, /\bLENGTHB\(/);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user