pgsql-deparser 17.12.2 → 17.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/deparser.d.ts +0 -2
- package/deparser.js +183 -225
- package/esm/deparser.js +183 -225
- package/esm/kwlist.js +531 -0
- package/esm/utils/quote-utils.js +136 -46
- package/kwlist.d.ts +16 -0
- package/kwlist.js +535 -0
- package/package.json +4 -3
- package/utils/quote-utils.d.ts +47 -2
- package/utils/quote-utils.js +136 -46
package/esm/utils/quote-utils.js
CHANGED
|
@@ -1,50 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
'all', 'analyse', 'analyze', 'and', 'any', 'array', 'as', 'asc', 'asymmetric',
|
|
3
|
-
'authorization', 'binary', 'both', 'case', 'cast', 'check', 'collate', 'collation',
|
|
4
|
-
'column', 'concurrently', 'constraint', 'create', 'cross', 'current_catalog',
|
|
5
|
-
'current_date', 'current_role', 'current_schema', 'current_time', 'current_timestamp',
|
|
6
|
-
'current_user', 'default', 'deferrable', 'desc', 'distinct', 'do', 'else', 'end',
|
|
7
|
-
'except', 'false', 'fetch', 'for', 'foreign', 'freeze', 'from', 'full', 'grant',
|
|
8
|
-
'group', 'having', 'ilike', 'in', 'initially', 'inner', 'intersect', 'into', 'is',
|
|
9
|
-
'isnull', 'join', 'lateral', 'leading', 'left', 'like', 'limit', 'localtime',
|
|
10
|
-
'localtimestamp', 'natural', 'not', 'notnull', 'null', 'offset', 'on', 'only',
|
|
11
|
-
'or', 'order', 'outer', 'overlaps', 'placing', 'primary', 'references', 'returning',
|
|
12
|
-
'right', 'select', 'session_user', 'similar', 'some', 'symmetric', 'table', 'tablesample',
|
|
13
|
-
'then', 'to', 'trailing', 'true', 'union', 'unique', 'user', 'using', 'variadic',
|
|
14
|
-
'verbose', 'when', 'where', 'window', 'with'
|
|
15
|
-
]);
|
|
1
|
+
import { keywordKindOf } from '../kwlist';
|
|
16
2
|
export class QuoteUtils {
|
|
17
|
-
static needsQuotes(value) {
|
|
18
|
-
if (!value || typeof value !== 'string') {
|
|
19
|
-
return false;
|
|
20
|
-
}
|
|
21
|
-
const lowerValue = value.toLowerCase();
|
|
22
|
-
if (RESERVED_WORDS.has(lowerValue)) {
|
|
23
|
-
return true;
|
|
24
|
-
}
|
|
25
|
-
if (!/^[a-z_][a-z0-9_$]*$/i.test(value)) {
|
|
26
|
-
return true;
|
|
27
|
-
}
|
|
28
|
-
if (value !== value.toLowerCase()) {
|
|
29
|
-
return true;
|
|
30
|
-
}
|
|
31
|
-
return false;
|
|
32
|
-
}
|
|
33
|
-
static quote(value) {
|
|
34
|
-
if (value == null) {
|
|
35
|
-
return null;
|
|
36
|
-
}
|
|
37
|
-
if (Array.isArray(value)) {
|
|
38
|
-
return value.map(v => this.quote(v));
|
|
39
|
-
}
|
|
40
|
-
if (typeof value !== 'string') {
|
|
41
|
-
return value;
|
|
42
|
-
}
|
|
43
|
-
if (this.needsQuotes(value)) {
|
|
44
|
-
return `"${value}"`;
|
|
45
|
-
}
|
|
46
|
-
return value;
|
|
47
|
-
}
|
|
48
3
|
static escape(literal) {
|
|
49
4
|
return `'${literal.replace(/'/g, "''")}'`;
|
|
50
5
|
}
|
|
@@ -78,4 +33,139 @@ export class QuoteUtils {
|
|
|
78
33
|
// unless it's a raw \x... bytea-style literal.
|
|
79
34
|
return !/^\\x[0-9a-fA-F]+$/i.test(value) && value.includes('\\');
|
|
80
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* Quote an identifier only if needed
|
|
38
|
+
*
|
|
39
|
+
* This is a TypeScript port of PostgreSQL's quote_identifier() function from ruleutils.c
|
|
40
|
+
* https://github.com/postgres/postgres/blob/fab5cd3dd1323f9e66efeb676c4bb212ff340204/src/backend/utils/adt/ruleutils.c#L13055-L13137
|
|
41
|
+
*
|
|
42
|
+
* Can avoid quoting if ident starts with a lowercase letter or underscore
|
|
43
|
+
* and contains only lowercase letters, digits, and underscores, *and* is
|
|
44
|
+
* not any SQL keyword. Otherwise, supply quotes.
|
|
45
|
+
*
|
|
46
|
+
* When quotes are needed, embedded double quotes are properly escaped as "".
|
|
47
|
+
*/
|
|
48
|
+
static quoteIdentifier(ident) {
|
|
49
|
+
if (!ident)
|
|
50
|
+
return ident;
|
|
51
|
+
let safe = true;
|
|
52
|
+
// Check first character: must be lowercase letter or underscore
|
|
53
|
+
const firstChar = ident[0];
|
|
54
|
+
if (!((firstChar >= 'a' && firstChar <= 'z') || firstChar === '_')) {
|
|
55
|
+
safe = false;
|
|
56
|
+
}
|
|
57
|
+
// Check all characters
|
|
58
|
+
for (let i = 0; i < ident.length; i++) {
|
|
59
|
+
const ch = ident[i];
|
|
60
|
+
if ((ch >= 'a' && ch <= 'z') ||
|
|
61
|
+
(ch >= '0' && ch <= '9') ||
|
|
62
|
+
(ch === '_')) {
|
|
63
|
+
// okay
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
safe = false;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (safe) {
|
|
70
|
+
// Check for keyword. We quote keywords except for unreserved ones.
|
|
71
|
+
// (In some cases we could avoid quoting a col_name or type_func_name
|
|
72
|
+
// keyword, but it seems much harder than it's worth to tell that.)
|
|
73
|
+
const kwKind = keywordKindOf(ident);
|
|
74
|
+
if (kwKind !== 'NO_KEYWORD' && kwKind !== 'UNRESERVED_KEYWORD') {
|
|
75
|
+
safe = false;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (safe) {
|
|
79
|
+
return ident; // no change needed
|
|
80
|
+
}
|
|
81
|
+
// Build quoted identifier with escaped embedded quotes
|
|
82
|
+
let result = '"';
|
|
83
|
+
for (let i = 0; i < ident.length; i++) {
|
|
84
|
+
const ch = ident[i];
|
|
85
|
+
if (ch === '"') {
|
|
86
|
+
result += '"'; // escape " as ""
|
|
87
|
+
}
|
|
88
|
+
result += ch;
|
|
89
|
+
}
|
|
90
|
+
result += '"';
|
|
91
|
+
return result;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Quote an identifier that appears after a dot in a qualified name.
|
|
95
|
+
*
|
|
96
|
+
* In PostgreSQL's grammar, identifiers that appear after a dot (e.g., schema.name,
|
|
97
|
+
* table.column) are in a more permissive position that accepts all keyword categories
|
|
98
|
+
* including RESERVED_KEYWORD. This means we only need to quote for lexical reasons
|
|
99
|
+
* (uppercase, special characters, leading digits) not for keyword reasons.
|
|
100
|
+
*
|
|
101
|
+
* Empirically verified: `myschema.select`, `myschema.float`, `t.from` all parse
|
|
102
|
+
* successfully in PostgreSQL without quotes.
|
|
103
|
+
*/
|
|
104
|
+
static quoteIdentifierAfterDot(ident) {
|
|
105
|
+
if (!ident)
|
|
106
|
+
return ident;
|
|
107
|
+
let safe = true;
|
|
108
|
+
const firstChar = ident[0];
|
|
109
|
+
if (!((firstChar >= 'a' && firstChar <= 'z') || firstChar === '_')) {
|
|
110
|
+
safe = false;
|
|
111
|
+
}
|
|
112
|
+
for (let i = 0; i < ident.length; i++) {
|
|
113
|
+
const ch = ident[i];
|
|
114
|
+
if ((ch >= 'a' && ch <= 'z') ||
|
|
115
|
+
(ch >= '0' && ch <= '9') ||
|
|
116
|
+
(ch === '_')) {
|
|
117
|
+
// okay
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
safe = false;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (safe) {
|
|
124
|
+
return ident;
|
|
125
|
+
}
|
|
126
|
+
let result = '"';
|
|
127
|
+
for (let i = 0; i < ident.length; i++) {
|
|
128
|
+
const ch = ident[i];
|
|
129
|
+
if (ch === '"') {
|
|
130
|
+
result += '"';
|
|
131
|
+
}
|
|
132
|
+
result += ch;
|
|
133
|
+
}
|
|
134
|
+
result += '"';
|
|
135
|
+
return result;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Quote a dotted name (e.g., schema.table, catalog.schema.table).
|
|
139
|
+
*
|
|
140
|
+
* The first part uses strict quoting (keywords are quoted), while subsequent
|
|
141
|
+
* parts use relaxed quoting (keywords allowed, only quote for lexical reasons).
|
|
142
|
+
*
|
|
143
|
+
* This reflects PostgreSQL's grammar where the first identifier in a statement
|
|
144
|
+
* may conflict with keywords, but identifiers after a dot are in a more
|
|
145
|
+
* permissive position.
|
|
146
|
+
*/
|
|
147
|
+
static quoteDottedName(parts) {
|
|
148
|
+
if (!parts || parts.length === 0)
|
|
149
|
+
return '';
|
|
150
|
+
if (parts.length === 1) {
|
|
151
|
+
return QuoteUtils.quoteIdentifier(parts[0]);
|
|
152
|
+
}
|
|
153
|
+
return parts.map((part, index) => index === 0 ? QuoteUtils.quoteIdentifier(part) : QuoteUtils.quoteIdentifierAfterDot(part)).join('.');
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Quote a possibly-qualified identifier
|
|
157
|
+
*
|
|
158
|
+
* This is inspired by PostgreSQL's quote_qualified_identifier() function from ruleutils.c
|
|
159
|
+
* but uses relaxed quoting for the tail component since PostgreSQL's grammar accepts
|
|
160
|
+
* all keywords in qualified name positions.
|
|
161
|
+
*
|
|
162
|
+
* Return a name of the form qualifier.ident, or just ident if qualifier
|
|
163
|
+
* is null/undefined, quoting each component if necessary.
|
|
164
|
+
*/
|
|
165
|
+
static quoteQualifiedIdentifier(qualifier, ident) {
|
|
166
|
+
if (qualifier) {
|
|
167
|
+
return `${QuoteUtils.quoteIdentifier(qualifier)}.${QuoteUtils.quoteIdentifierAfterDot(ident)}`;
|
|
168
|
+
}
|
|
169
|
+
return QuoteUtils.quoteIdentifier(ident);
|
|
170
|
+
}
|
|
81
171
|
}
|
package/kwlist.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generated from PostgreSQL kwlist.h
|
|
3
|
+
* DO NOT EDIT BY HAND.
|
|
4
|
+
*/
|
|
5
|
+
export type KeywordKind = "NO_KEYWORD" | "UNRESERVED_KEYWORD" | "COL_NAME_KEYWORD" | "TYPE_FUNC_NAME_KEYWORD" | "RESERVED_KEYWORD";
|
|
6
|
+
export declare const kwlist: {
|
|
7
|
+
readonly UNRESERVED_KEYWORD: readonly ["abort", "absent", "absolute", "access", "action", "add", "admin", "after", "aggregate", "also", "alter", "always", "asensitive", "assertion", "assignment", "at", "atomic", "attach", "attribute", "backward", "before", "begin", "breadth", "by", "cache", "call", "called", "cascade", "cascaded", "catalog", "chain", "characteristics", "checkpoint", "class", "close", "cluster", "columns", "comment", "comments", "commit", "committed", "compression", "conditional", "configuration", "conflict", "connection", "constraints", "content", "continue", "conversion", "copy", "cost", "csv", "cube", "current", "cursor", "cycle", "data", "database", "day", "deallocate", "declare", "defaults", "deferred", "definer", "delete", "delimiter", "delimiters", "depends", "depth", "detach", "dictionary", "disable", "discard", "document", "domain", "double", "drop", "each", "empty", "enable", "encoding", "encrypted", "enforced", "enum", "error", "escape", "event", "exclude", "excluding", "exclusive", "execute", "explain", "expression", "extension", "external", "family", "filter", "finalize", "first", "following", "force", "format", "forward", "function", "functions", "generated", "global", "granted", "groups", "handler", "header", "hold", "hour", "identity", "if", "ignore", "immediate", "immutable", "implicit", "import", "include", "including", "increment", "indent", "index", "indexes", "inherit", "inherits", "inline", "input", "insensitive", "insert", "instead", "invoker", "isolation", "keep", "key", "keys", "label", "language", "large", "last", "leakproof", "level", "listen", "load", "local", "location", "lock", "locked", "logged", "lsn", "mapping", "match", "matched", "materialized", "maxvalue", "merge", "method", "minute", "minvalue", "mode", "month", "move", "name", "names", "nested", "new", "next", "nfc", "nfd", "nfkc", "nfkd", "no", "normalized", "nothing", "notify", "nowait", "nulls", "object", "objects", "of", "off", "oids", "old", "omit", "operator", "option", "options", "ordinality", "others", "over", "overriding", "owned", "owner", "parallel", "parameter", "parser", "partial", "partition", "partitions", "passing", "password", "path", "period", "plan", "plans", "policy", "preceding", "prepare", "prepared", "preserve", "prior", "privileges", "procedural", "procedure", "procedures", "program", "publication", "quote", "quotes", "range", "read", "reassign", "recursive", "ref", "referencing", "refresh", "reindex", "relative", "release", "rename", "repeatable", "replace", "replica", "reset", "respect", "restart", "restrict", "return", "returns", "revoke", "role", "rollback", "rollup", "routine", "routines", "rows", "rule", "savepoint", "scalar", "schema", "schemas", "scroll", "search", "second", "security", "sequence", "sequences", "serializable", "server", "session", "set", "sets", "share", "show", "simple", "skip", "snapshot", "source", "split", "sql", "stable", "standalone", "start", "statement", "statistics", "stdin", "stdout", "storage", "stored", "strict", "string", "strip", "subscription", "support", "sysid", "system", "tables", "tablespace", "target", "temp", "template", "temporary", "text", "ties", "transaction", "transform", "trigger", "truncate", "trusted", "type", "types", "uescape", "unbounded", "uncommitted", "unconditional", "unencrypted", "unknown", "unlisten", "unlogged", "until", "update", "vacuum", "valid", "validate", "validator", "value", "varying", "version", "view", "views", "virtual", "volatile", "wait", "whitespace", "within", "without", "work", "wrapper", "write", "xml", "year", "yes", "zone"];
|
|
8
|
+
readonly RESERVED_KEYWORD: readonly ["all", "analyse", "analyze", "and", "any", "array", "as", "asc", "asymmetric", "both", "case", "cast", "check", "collate", "column", "constraint", "create", "current_catalog", "current_date", "current_role", "current_time", "current_timestamp", "current_user", "default", "deferrable", "desc", "distinct", "do", "else", "end", "except", "false", "fetch", "for", "foreign", "from", "grant", "group", "having", "in", "initially", "intersect", "into", "lateral", "leading", "limit", "localtime", "localtimestamp", "not", "null", "offset", "on", "only", "or", "order", "placing", "primary", "references", "returning", "select", "session_user", "some", "symmetric", "system_user", "table", "then", "to", "trailing", "true", "union", "unique", "user", "using", "variadic", "when", "where", "window", "with"];
|
|
9
|
+
readonly TYPE_FUNC_NAME_KEYWORD: readonly ["authorization", "binary", "collation", "concurrently", "cross", "current_schema", "freeze", "full", "ilike", "inner", "is", "isnull", "join", "left", "like", "natural", "notnull", "outer", "overlaps", "right", "similar", "tablesample", "verbose"];
|
|
10
|
+
readonly COL_NAME_KEYWORD: readonly ["between", "bigint", "bit", "boolean", "char", "character", "coalesce", "dec", "decimal", "exists", "extract", "float", "greatest", "grouping", "inout", "int", "integer", "interval", "json", "json_array", "json_arrayagg", "json_exists", "json_object", "json_objectagg", "json_query", "json_scalar", "json_serialize", "json_table", "json_value", "least", "merge_action", "national", "nchar", "none", "normalize", "nullif", "numeric", "out", "overlay", "position", "precision", "real", "row", "setof", "smallint", "substring", "time", "timestamp", "treat", "trim", "values", "varchar", "xmlattributes", "xmlconcat", "xmlelement", "xmlexists", "xmlforest", "xmlnamespaces", "xmlparse", "xmlpi", "xmlroot", "xmlserialize", "xmltable"];
|
|
11
|
+
};
|
|
12
|
+
export declare const RESERVED_KEYWORDS: Set<string>;
|
|
13
|
+
export declare const UNRESERVED_KEYWORDS: Set<string>;
|
|
14
|
+
export declare const COL_NAME_KEYWORDS: Set<string>;
|
|
15
|
+
export declare const TYPE_FUNC_NAME_KEYWORDS: Set<string>;
|
|
16
|
+
export declare function keywordKindOf(word: string): KeywordKind;
|