568 lines
9.0 KiB
TypeScript
568 lines
9.0 KiB
TypeScript
const SIMPLE_LOWER_IDENTIFIER = /^[a-z_][a-z0-9_$]*$/;
|
|
const SIMPLE_MYSQL_IDENTIFIER = /^[A-Za-z_$\u0080-\uFFFF][A-Za-z0-9_$\u0080-\uFFFF]*$/u;
|
|
const SIMPLE_DAMENG_IDENTIFIER = /^[A-Z_][A-Z0-9_]*$/;
|
|
const SIMPLE_ORACLE_IDENTIFIER = /^[A-Z][A-Z0-9_$#]*$/;
|
|
|
|
// PostgreSQL pg_get_keywords() categories R and T. GaussDB's PostgreSQL-
|
|
// compatible modes use the same identifier constraints for bare names.
|
|
const POSTGRES_RESERVED_IDENTIFIER_KEYWORDS = new Set([
|
|
"all",
|
|
"analyse",
|
|
"analyze",
|
|
"and",
|
|
"any",
|
|
"array",
|
|
"as",
|
|
"asc",
|
|
"asymmetric",
|
|
"authorization",
|
|
"binary",
|
|
"both",
|
|
"case",
|
|
"cast",
|
|
"check",
|
|
"collate",
|
|
"collation",
|
|
"column",
|
|
"concurrently",
|
|
"constraint",
|
|
"create",
|
|
"cross",
|
|
"current_catalog",
|
|
"current_date",
|
|
"current_role",
|
|
"current_schema",
|
|
"current_time",
|
|
"current_timestamp",
|
|
"current_user",
|
|
"default",
|
|
"deferrable",
|
|
"desc",
|
|
"distinct",
|
|
"do",
|
|
"else",
|
|
"end",
|
|
"except",
|
|
"false",
|
|
"fetch",
|
|
"for",
|
|
"foreign",
|
|
"freeze",
|
|
"from",
|
|
"full",
|
|
"grant",
|
|
"group",
|
|
"having",
|
|
"ilike",
|
|
"in",
|
|
"initially",
|
|
"inner",
|
|
"intersect",
|
|
"into",
|
|
"is",
|
|
"isnull",
|
|
"join",
|
|
"lateral",
|
|
"leading",
|
|
"left",
|
|
"like",
|
|
"limit",
|
|
"localtime",
|
|
"localtimestamp",
|
|
"natural",
|
|
"not",
|
|
"notnull",
|
|
"null",
|
|
"offset",
|
|
"on",
|
|
"only",
|
|
"or",
|
|
"order",
|
|
"outer",
|
|
"overlaps",
|
|
"placing",
|
|
"primary",
|
|
"references",
|
|
"returning",
|
|
"right",
|
|
"select",
|
|
"session_user",
|
|
"similar",
|
|
"some",
|
|
"symmetric",
|
|
"system_user",
|
|
"table",
|
|
"tablesample",
|
|
"then",
|
|
"to",
|
|
"trailing",
|
|
"true",
|
|
"union",
|
|
"unique",
|
|
"user",
|
|
"using",
|
|
"variadic",
|
|
"verbose",
|
|
"when",
|
|
"where",
|
|
"window",
|
|
"with",
|
|
]);
|
|
|
|
// GaussDB MySQL-compatible drivers report a backtick. These additional words
|
|
// cover MySQL-only names that cannot safely be emitted as bare identifiers.
|
|
const MYSQL_ONLY_RESERVED_IDENTIFIER_KEYWORDS = new Set([
|
|
"accessible",
|
|
"auto_increment",
|
|
"change",
|
|
"database",
|
|
"databases",
|
|
"delayed",
|
|
"describe",
|
|
"div",
|
|
"dual",
|
|
"enclosed",
|
|
"escaped",
|
|
"explain",
|
|
"force",
|
|
"fulltext",
|
|
"high_priority",
|
|
"ignore",
|
|
"index",
|
|
"infile",
|
|
"key",
|
|
"keys",
|
|
"kill",
|
|
"linear",
|
|
"lines",
|
|
"load",
|
|
"lock",
|
|
"low_priority",
|
|
"master_ssl_verify_server_cert",
|
|
"maxvalue",
|
|
"mediumint",
|
|
"mod",
|
|
"no_write_to_binlog",
|
|
"optimize",
|
|
"optionally",
|
|
"outfile",
|
|
"partition",
|
|
"purge",
|
|
"range",
|
|
"read_write",
|
|
"regexp",
|
|
"release",
|
|
"rename",
|
|
"replace",
|
|
"require",
|
|
"rlike",
|
|
"schema",
|
|
"schemas",
|
|
"separator",
|
|
"show",
|
|
"spatial",
|
|
"sql_big_result",
|
|
"sql_calc_found_rows",
|
|
"sql_small_result",
|
|
"ssl",
|
|
"starting",
|
|
"straight_join",
|
|
"terminated",
|
|
"tinyint",
|
|
"unlock",
|
|
"unsigned",
|
|
"use",
|
|
"utc_date",
|
|
"utc_time",
|
|
"utc_timestamp",
|
|
"values",
|
|
"varbinary",
|
|
"varchar",
|
|
"write",
|
|
"xor",
|
|
"zerofill",
|
|
]);
|
|
|
|
// Oracle 11g SQL reserved words. Ordinary unquoted Oracle identifiers are
|
|
// uppercase-folded, so only uppercase names outside this set are safe to unwrap.
|
|
const ORACLE_RESERVED_IDENTIFIER_KEYWORDS = new Set([
|
|
"ACCESS",
|
|
"ADD",
|
|
"ALL",
|
|
"ALTER",
|
|
"AND",
|
|
"ANY",
|
|
"AS",
|
|
"ASC",
|
|
"AUDIT",
|
|
"BETWEEN",
|
|
"BY",
|
|
"CHAR",
|
|
"CHECK",
|
|
"CLUSTER",
|
|
"COLUMN",
|
|
"COMMENT",
|
|
"COMPRESS",
|
|
"CONNECT",
|
|
"CREATE",
|
|
"CURRENT",
|
|
"DATE",
|
|
"DECIMAL",
|
|
"DEFAULT",
|
|
"DELETE",
|
|
"DESC",
|
|
"DISTINCT",
|
|
"DROP",
|
|
"ELSE",
|
|
"EXCLUSIVE",
|
|
"EXISTS",
|
|
"FILE",
|
|
"FLOAT",
|
|
"FOR",
|
|
"FROM",
|
|
"GRANT",
|
|
"GROUP",
|
|
"HAVING",
|
|
"IDENTIFIED",
|
|
"IMMEDIATE",
|
|
"IN",
|
|
"INCREMENT",
|
|
"INDEX",
|
|
"INITIAL",
|
|
"INSERT",
|
|
"INTEGER",
|
|
"INTERSECT",
|
|
"INTO",
|
|
"IS",
|
|
"LEVEL",
|
|
"LIKE",
|
|
"LOCK",
|
|
"LONG",
|
|
"MAXEXTENTS",
|
|
"MINUS",
|
|
"MLSLABEL",
|
|
"MODE",
|
|
"MODIFY",
|
|
"NOAUDIT",
|
|
"NOCOMPRESS",
|
|
"NOT",
|
|
"NOWAIT",
|
|
"NULL",
|
|
"NUMBER",
|
|
"OF",
|
|
"OFFLINE",
|
|
"ON",
|
|
"ONLINE",
|
|
"OPTION",
|
|
"OR",
|
|
"ORDER",
|
|
"PCTFREE",
|
|
"PRIOR",
|
|
"PRIVILEGES",
|
|
"PUBLIC",
|
|
"RAW",
|
|
"RENAME",
|
|
"RESOURCE",
|
|
"REVOKE",
|
|
"ROW",
|
|
"ROWID",
|
|
"ROWNUM",
|
|
"ROWS",
|
|
"SELECT",
|
|
"SESSION",
|
|
"SET",
|
|
"SHARE",
|
|
"SIZE",
|
|
"SMALLINT",
|
|
"START",
|
|
"SUCCESSFUL",
|
|
"SYNONYM",
|
|
"SYSDATE",
|
|
"TABLE",
|
|
"THEN",
|
|
"TO",
|
|
"TRIGGER",
|
|
"UID",
|
|
"UNION",
|
|
"UNIQUE",
|
|
"UPDATE",
|
|
"USER",
|
|
"VALIDATE",
|
|
"VALUES",
|
|
"VARCHAR",
|
|
"VARCHAR2",
|
|
"VIEW",
|
|
"WHENEVER",
|
|
"WHERE",
|
|
"WITH",
|
|
]);
|
|
|
|
// V$RESERVED_WORDS rows marked RESERVED = Y across the supported DM8 test
|
|
// instances. DM's native identifier rules use uppercase ordinary names.
|
|
const DAMENG_RESERVED_IDENTIFIER_KEYWORDS = new Set([
|
|
"ABSOLUTE",
|
|
"ABSTRACT",
|
|
"ADD",
|
|
"ADMIN",
|
|
"ALL",
|
|
"ALTER",
|
|
"AND",
|
|
"ANY",
|
|
"ARRAY",
|
|
"ARRAYLEN",
|
|
"AS",
|
|
"ASC",
|
|
"ASSIGN",
|
|
"AUDIT",
|
|
"AUTHORIZATION",
|
|
"AUTO_INCREMENT",
|
|
"BEGIN",
|
|
"BETWEEN",
|
|
"BIGDATEDIFF",
|
|
"BINARY",
|
|
"BOOL",
|
|
"BOTH",
|
|
"BREAK",
|
|
"BSTRING",
|
|
"BY",
|
|
"BYTE",
|
|
"CALL",
|
|
"CASE",
|
|
"CAST",
|
|
"CATCH",
|
|
"CHAR",
|
|
"CHECK",
|
|
"CLASS",
|
|
"CLUSTER",
|
|
"CLUSTERBTR",
|
|
"COLLATION",
|
|
"COLLECT",
|
|
"COLUMN",
|
|
"COMMENT",
|
|
"COMMIT",
|
|
"COMMITWORK",
|
|
"CONNECT",
|
|
"CONNECT_BY_ROOT",
|
|
"CONST",
|
|
"CONSTRAINT",
|
|
"CONTAINS",
|
|
"CONTEXT",
|
|
"CONTINUE",
|
|
"CONVERT",
|
|
"CORRESPONDING",
|
|
"CREATE",
|
|
"CROSS",
|
|
"CRYPTO",
|
|
"CUBE",
|
|
"CURRENT",
|
|
"CURSOR",
|
|
"DATEADD",
|
|
"DATEDIFF",
|
|
"DATEPART",
|
|
"DECIMAL",
|
|
"DECLARE",
|
|
"DECODE",
|
|
"DEFAULT",
|
|
"DELETE",
|
|
"DESC",
|
|
"DISABLE",
|
|
"DISKSPACE",
|
|
"DISTINCT",
|
|
"DISTRIBUTED",
|
|
"DO",
|
|
"DOMAIN",
|
|
"DOUBLE",
|
|
"DROP",
|
|
"ELSE",
|
|
"ELSEIF",
|
|
"ELSIF",
|
|
"ENABLE",
|
|
"END",
|
|
"EQU",
|
|
"EXCHANGE",
|
|
"EXEC",
|
|
"EXECUTE",
|
|
"EXISTS",
|
|
"EXIT",
|
|
"EXPLAIN",
|
|
"EXTERN",
|
|
"EXTRACT",
|
|
"FETCH",
|
|
"FINAL",
|
|
"FINALLY",
|
|
"FIRST",
|
|
"FLASHBACK",
|
|
"FLOAT",
|
|
"FOR",
|
|
"FOREIGN",
|
|
"FROM",
|
|
"FULL",
|
|
"FULLY",
|
|
"FUNCTION",
|
|
"GET",
|
|
"GOTO",
|
|
"GRANT",
|
|
"GROUP",
|
|
"GROUPING",
|
|
"HAVING",
|
|
"IDENTITY",
|
|
"IF",
|
|
"IFNULL",
|
|
"IMMEDIATE",
|
|
"IN",
|
|
"INDEX",
|
|
"INLINE",
|
|
"INNER",
|
|
"INSERT",
|
|
"INT",
|
|
"INTERSECT",
|
|
"INTERVAL",
|
|
"INTO",
|
|
"IS",
|
|
"JOIN",
|
|
"KEEP",
|
|
"LEADING",
|
|
"LEFT",
|
|
"LEXER",
|
|
"LIKE",
|
|
"LIST",
|
|
"LNNVL",
|
|
"LOGIN",
|
|
"LOOP",
|
|
"MEMBER",
|
|
"MINUS",
|
|
"MULTISET",
|
|
"NATURAL",
|
|
"NEW",
|
|
"NEXT",
|
|
"NOCOPY",
|
|
"NOCYCLE",
|
|
"NOT",
|
|
"NULL",
|
|
"OBJECT",
|
|
"OF",
|
|
"ON",
|
|
"OR",
|
|
"ORDER",
|
|
"OUT",
|
|
"OVER",
|
|
"OVERLAY",
|
|
"OVERRIDE",
|
|
"PARTITION",
|
|
"PENDANT",
|
|
"PERCENT",
|
|
"PRIMARY",
|
|
"PRINT",
|
|
"PRIOR",
|
|
"PRIVATE",
|
|
"PRIVILEGES",
|
|
"PROCEDURE",
|
|
"PROTECTED",
|
|
"PUBLIC",
|
|
"RAISE",
|
|
"RECORD",
|
|
"REF",
|
|
"REFERENCE",
|
|
"REFERENCES",
|
|
"REFERENCING",
|
|
"RELATIVE",
|
|
"REPEAT",
|
|
"REPLICATE",
|
|
"RETURN",
|
|
"RETURNING",
|
|
"REVERSE",
|
|
"REVOKE",
|
|
"RIGHT",
|
|
"ROLLBACK",
|
|
"ROLLUP",
|
|
"ROW",
|
|
"ROWNUM",
|
|
"ROWS",
|
|
"SAVEPOINT",
|
|
"SBYTE",
|
|
"SCHEMA",
|
|
"SEALED",
|
|
"SECTION",
|
|
"SELECT",
|
|
"SET",
|
|
"SETS",
|
|
"SHORT",
|
|
"SIZEOF",
|
|
"SOME",
|
|
"STATIC",
|
|
"STRUCT",
|
|
"SUBPARTITION",
|
|
"SWITCH",
|
|
"SYNONYM",
|
|
"TABLE",
|
|
"THROW",
|
|
"TIMESTAMPADD",
|
|
"TIMESTAMPDIFF",
|
|
"TO",
|
|
"TOP",
|
|
"TRAILING",
|
|
"TRIGGER",
|
|
"TRIM",
|
|
"TRUNCATE",
|
|
"TRY",
|
|
"TYPEDEF",
|
|
"TYPEOF",
|
|
"UINT",
|
|
"ULONG",
|
|
"UNION",
|
|
"UNIQUE",
|
|
"UNTIL",
|
|
"UPDATE",
|
|
"USER",
|
|
"USHORT",
|
|
"USING",
|
|
"VALUES",
|
|
"VARRAY",
|
|
"VERIFY",
|
|
"VIEW",
|
|
"VIRTUAL",
|
|
"VOID",
|
|
"WHEN",
|
|
"WHENEVER",
|
|
"WHERE",
|
|
"WHILE",
|
|
"WITH",
|
|
"WITHIN",
|
|
"XMLAGG",
|
|
"XMLPARSE",
|
|
"XMLTABLE",
|
|
]);
|
|
|
|
export function isExplicitlyQuotedSqlIdentifier(value: string): boolean {
|
|
if (value.length < 2) return false;
|
|
return (value.startsWith('"') && value.endsWith('"')) || (value.startsWith("`") && value.endsWith("`")) || (value.startsWith("[") && value.endsWith("]"));
|
|
}
|
|
|
|
export function requiresPostgresIdentifierQuote(identifier: string, additionalKeywords?: ReadonlySet<string>): boolean {
|
|
if (!SIMPLE_LOWER_IDENTIFIER.test(identifier)) return true;
|
|
return POSTGRES_RESERVED_IDENTIFIER_KEYWORDS.has(identifier) || additionalKeywords?.has(identifier) === true;
|
|
}
|
|
|
|
export function requiresMysqlIdentifierQuote(identifier: string, additionalKeywords?: ReadonlySet<string>): boolean {
|
|
if (!SIMPLE_MYSQL_IDENTIFIER.test(identifier)) return true;
|
|
const normalized = identifier.toLowerCase();
|
|
return POSTGRES_RESERVED_IDENTIFIER_KEYWORDS.has(normalized) || additionalKeywords?.has(normalized) === true || MYSQL_ONLY_RESERVED_IDENTIFIER_KEYWORDS.has(normalized);
|
|
}
|
|
|
|
export function requiresOracleIdentifierQuote(identifier: string): boolean {
|
|
return !SIMPLE_ORACLE_IDENTIFIER.test(identifier) || ORACLE_RESERVED_IDENTIFIER_KEYWORDS.has(identifier);
|
|
}
|
|
|
|
export function requiresDamengIdentifierQuote(identifier: string): boolean {
|
|
return !SIMPLE_DAMENG_IDENTIFIER.test(identifier) || DAMENG_RESERVED_IDENTIFIER_KEYWORDS.has(identifier);
|
|
}
|
|
|
|
export function quoteGaussDbJdbcIdentifier(identifier: string, identifierQuote: string): string {
|
|
if (isExplicitlyQuotedSqlIdentifier(identifier)) return identifier;
|
|
const quote = identifierQuote.trim();
|
|
if (!quote) return identifier;
|
|
const requiresQuote = quote === "`" ? requiresPostgresIdentifierQuote(identifier) || MYSQL_ONLY_RESERVED_IDENTIFIER_KEYWORDS.has(identifier.toLowerCase()) : requiresPostgresIdentifierQuote(identifier);
|
|
if (!requiresQuote) return identifier;
|
|
return `${quote}${identifier.replaceAll(quote, quote + quote)}${quote}`;
|
|
}
|