pgsql-deparser 17.14.0 → 17.15.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/deparser.js +3 -2
- package/esm/deparser.js +3 -2
- package/esm/utils/quote-utils.js +69 -0
- package/package.json +2 -2
- package/utils/quote-utils.d.ts +22 -0
- package/utils/quote-utils.js +69 -0
package/deparser.js
CHANGED
|
@@ -1851,8 +1851,9 @@ class Deparser {
|
|
|
1851
1851
|
return output.join(' ');
|
|
1852
1852
|
}
|
|
1853
1853
|
}
|
|
1854
|
-
|
|
1855
|
-
|
|
1854
|
+
// Use type-name quoting for non-pg_catalog types
|
|
1855
|
+
// This allows keywords like 'json', 'int', 'boolean' to remain unquoted in type positions
|
|
1856
|
+
let result = mods(quote_utils_1.QuoteUtils.quoteTypeDottedName(names), args);
|
|
1856
1857
|
if (node.arrayBounds && node.arrayBounds.length > 0) {
|
|
1857
1858
|
result += formatArrayBounds(node.arrayBounds);
|
|
1858
1859
|
}
|
package/esm/deparser.js
CHANGED
|
@@ -1848,8 +1848,9 @@ export class Deparser {
|
|
|
1848
1848
|
return output.join(' ');
|
|
1849
1849
|
}
|
|
1850
1850
|
}
|
|
1851
|
-
|
|
1852
|
-
|
|
1851
|
+
// Use type-name quoting for non-pg_catalog types
|
|
1852
|
+
// This allows keywords like 'json', 'int', 'boolean' to remain unquoted in type positions
|
|
1853
|
+
let result = mods(QuoteUtils.quoteTypeDottedName(names), args);
|
|
1853
1854
|
if (node.arrayBounds && node.arrayBounds.length > 0) {
|
|
1854
1855
|
result += formatArrayBounds(node.arrayBounds);
|
|
1855
1856
|
}
|
package/esm/utils/quote-utils.js
CHANGED
|
@@ -168,4 +168,73 @@ export class QuoteUtils {
|
|
|
168
168
|
}
|
|
169
169
|
return QuoteUtils.quoteIdentifier(ident);
|
|
170
170
|
}
|
|
171
|
+
/**
|
|
172
|
+
* Quote an identifier that appears as a type name.
|
|
173
|
+
*
|
|
174
|
+
* Type names in PostgreSQL have a less strict quoting policy than standalone identifiers.
|
|
175
|
+
* In type positions, COL_NAME_KEYWORD and TYPE_FUNC_NAME_KEYWORD are allowed unquoted
|
|
176
|
+
* (e.g., 'json', 'int', 'boolean', 'interval'). Only RESERVED_KEYWORD must be quoted.
|
|
177
|
+
*
|
|
178
|
+
* This is different from:
|
|
179
|
+
* - quoteIdentifier(): quotes all keywords except UNRESERVED_KEYWORD
|
|
180
|
+
* - quoteIdentifierAfterDot(): only quotes for lexical reasons (no keyword checking)
|
|
181
|
+
*
|
|
182
|
+
* Type names still need quoting for lexical reasons (uppercase, special chars, etc.).
|
|
183
|
+
*/
|
|
184
|
+
static quoteIdentifierTypeName(ident) {
|
|
185
|
+
if (!ident)
|
|
186
|
+
return ident;
|
|
187
|
+
let safe = true;
|
|
188
|
+
// Check first character: must be lowercase letter or underscore
|
|
189
|
+
const firstChar = ident[0];
|
|
190
|
+
if (!((firstChar >= 'a' && firstChar <= 'z') || firstChar === '_')) {
|
|
191
|
+
safe = false;
|
|
192
|
+
}
|
|
193
|
+
// Check all characters
|
|
194
|
+
for (let i = 0; i < ident.length; i++) {
|
|
195
|
+
const ch = ident[i];
|
|
196
|
+
if ((ch >= 'a' && ch <= 'z') ||
|
|
197
|
+
(ch >= '0' && ch <= '9') ||
|
|
198
|
+
(ch === '_')) {
|
|
199
|
+
// okay
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
safe = false;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
if (safe) {
|
|
206
|
+
// For type names, only quote RESERVED_KEYWORD
|
|
207
|
+
// COL_NAME_KEYWORD and TYPE_FUNC_NAME_KEYWORD are allowed unquoted in type positions
|
|
208
|
+
const kwKind = keywordKindOf(ident);
|
|
209
|
+
if (kwKind === 'RESERVED_KEYWORD') {
|
|
210
|
+
safe = false;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
if (safe) {
|
|
214
|
+
return ident; // no change needed
|
|
215
|
+
}
|
|
216
|
+
// Build quoted identifier with escaped embedded quotes
|
|
217
|
+
let result = '"';
|
|
218
|
+
for (let i = 0; i < ident.length; i++) {
|
|
219
|
+
const ch = ident[i];
|
|
220
|
+
if (ch === '"') {
|
|
221
|
+
result += '"'; // escape " as ""
|
|
222
|
+
}
|
|
223
|
+
result += ch;
|
|
224
|
+
}
|
|
225
|
+
result += '"';
|
|
226
|
+
return result;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Quote a dotted type name (e.g., schema.typename).
|
|
230
|
+
*
|
|
231
|
+
* For type names, we use type-name quoting for all parts since the entire
|
|
232
|
+
* qualified name is in a type context. This allows keywords like 'json',
|
|
233
|
+
* 'int', 'boolean' to remain unquoted in user-defined schema-qualified types.
|
|
234
|
+
*/
|
|
235
|
+
static quoteTypeDottedName(parts) {
|
|
236
|
+
if (!parts || parts.length === 0)
|
|
237
|
+
return '';
|
|
238
|
+
return parts.map(part => QuoteUtils.quoteIdentifierTypeName(part)).join('.');
|
|
239
|
+
}
|
|
171
240
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pgsql-deparser",
|
|
3
|
-
"version": "17.
|
|
3
|
+
"version": "17.15.0",
|
|
4
4
|
"author": "Constructive <developers@constructive.io>",
|
|
5
5
|
"description": "PostgreSQL AST Deparser",
|
|
6
6
|
"main": "index.js",
|
|
@@ -60,5 +60,5 @@
|
|
|
60
60
|
"dependencies": {
|
|
61
61
|
"@pgsql/types": "^17.6.2"
|
|
62
62
|
},
|
|
63
|
-
"gitHead": "
|
|
63
|
+
"gitHead": "df8eb8b89aee325cd9005599bc2ac67a746aade7"
|
|
64
64
|
}
|
package/utils/quote-utils.d.ts
CHANGED
|
@@ -62,4 +62,26 @@ export declare class QuoteUtils {
|
|
|
62
62
|
* is null/undefined, quoting each component if necessary.
|
|
63
63
|
*/
|
|
64
64
|
static quoteQualifiedIdentifier(qualifier: string | null | undefined, ident: string): string;
|
|
65
|
+
/**
|
|
66
|
+
* Quote an identifier that appears as a type name.
|
|
67
|
+
*
|
|
68
|
+
* Type names in PostgreSQL have a less strict quoting policy than standalone identifiers.
|
|
69
|
+
* In type positions, COL_NAME_KEYWORD and TYPE_FUNC_NAME_KEYWORD are allowed unquoted
|
|
70
|
+
* (e.g., 'json', 'int', 'boolean', 'interval'). Only RESERVED_KEYWORD must be quoted.
|
|
71
|
+
*
|
|
72
|
+
* This is different from:
|
|
73
|
+
* - quoteIdentifier(): quotes all keywords except UNRESERVED_KEYWORD
|
|
74
|
+
* - quoteIdentifierAfterDot(): only quotes for lexical reasons (no keyword checking)
|
|
75
|
+
*
|
|
76
|
+
* Type names still need quoting for lexical reasons (uppercase, special chars, etc.).
|
|
77
|
+
*/
|
|
78
|
+
static quoteIdentifierTypeName(ident: string): string;
|
|
79
|
+
/**
|
|
80
|
+
* Quote a dotted type name (e.g., schema.typename).
|
|
81
|
+
*
|
|
82
|
+
* For type names, we use type-name quoting for all parts since the entire
|
|
83
|
+
* qualified name is in a type context. This allows keywords like 'json',
|
|
84
|
+
* 'int', 'boolean' to remain unquoted in user-defined schema-qualified types.
|
|
85
|
+
*/
|
|
86
|
+
static quoteTypeDottedName(parts: string[]): string;
|
|
65
87
|
}
|
package/utils/quote-utils.js
CHANGED
|
@@ -171,5 +171,74 @@ class QuoteUtils {
|
|
|
171
171
|
}
|
|
172
172
|
return QuoteUtils.quoteIdentifier(ident);
|
|
173
173
|
}
|
|
174
|
+
/**
|
|
175
|
+
* Quote an identifier that appears as a type name.
|
|
176
|
+
*
|
|
177
|
+
* Type names in PostgreSQL have a less strict quoting policy than standalone identifiers.
|
|
178
|
+
* In type positions, COL_NAME_KEYWORD and TYPE_FUNC_NAME_KEYWORD are allowed unquoted
|
|
179
|
+
* (e.g., 'json', 'int', 'boolean', 'interval'). Only RESERVED_KEYWORD must be quoted.
|
|
180
|
+
*
|
|
181
|
+
* This is different from:
|
|
182
|
+
* - quoteIdentifier(): quotes all keywords except UNRESERVED_KEYWORD
|
|
183
|
+
* - quoteIdentifierAfterDot(): only quotes for lexical reasons (no keyword checking)
|
|
184
|
+
*
|
|
185
|
+
* Type names still need quoting for lexical reasons (uppercase, special chars, etc.).
|
|
186
|
+
*/
|
|
187
|
+
static quoteIdentifierTypeName(ident) {
|
|
188
|
+
if (!ident)
|
|
189
|
+
return ident;
|
|
190
|
+
let safe = true;
|
|
191
|
+
// Check first character: must be lowercase letter or underscore
|
|
192
|
+
const firstChar = ident[0];
|
|
193
|
+
if (!((firstChar >= 'a' && firstChar <= 'z') || firstChar === '_')) {
|
|
194
|
+
safe = false;
|
|
195
|
+
}
|
|
196
|
+
// Check all characters
|
|
197
|
+
for (let i = 0; i < ident.length; i++) {
|
|
198
|
+
const ch = ident[i];
|
|
199
|
+
if ((ch >= 'a' && ch <= 'z') ||
|
|
200
|
+
(ch >= '0' && ch <= '9') ||
|
|
201
|
+
(ch === '_')) {
|
|
202
|
+
// okay
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
safe = false;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
if (safe) {
|
|
209
|
+
// For type names, only quote RESERVED_KEYWORD
|
|
210
|
+
// COL_NAME_KEYWORD and TYPE_FUNC_NAME_KEYWORD are allowed unquoted in type positions
|
|
211
|
+
const kwKind = (0, kwlist_1.keywordKindOf)(ident);
|
|
212
|
+
if (kwKind === 'RESERVED_KEYWORD') {
|
|
213
|
+
safe = false;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (safe) {
|
|
217
|
+
return ident; // no change needed
|
|
218
|
+
}
|
|
219
|
+
// Build quoted identifier with escaped embedded quotes
|
|
220
|
+
let result = '"';
|
|
221
|
+
for (let i = 0; i < ident.length; i++) {
|
|
222
|
+
const ch = ident[i];
|
|
223
|
+
if (ch === '"') {
|
|
224
|
+
result += '"'; // escape " as ""
|
|
225
|
+
}
|
|
226
|
+
result += ch;
|
|
227
|
+
}
|
|
228
|
+
result += '"';
|
|
229
|
+
return result;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Quote a dotted type name (e.g., schema.typename).
|
|
233
|
+
*
|
|
234
|
+
* For type names, we use type-name quoting for all parts since the entire
|
|
235
|
+
* qualified name is in a type context. This allows keywords like 'json',
|
|
236
|
+
* 'int', 'boolean' to remain unquoted in user-defined schema-qualified types.
|
|
237
|
+
*/
|
|
238
|
+
static quoteTypeDottedName(parts) {
|
|
239
|
+
if (!parts || parts.length === 0)
|
|
240
|
+
return '';
|
|
241
|
+
return parts.map(part => QuoteUtils.quoteIdentifierTypeName(part)).join('.');
|
|
242
|
+
}
|
|
174
243
|
}
|
|
175
244
|
exports.QuoteUtils = QuoteUtils;
|