You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: __fixtures__/generated/generated.json
+9Lines changed: 9 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -60,6 +60,15 @@
60
60
"pretty/quoting-5.sql": "CREATE FUNCTION faker.boolean() RETURNS boolean AS $EOFCODE$\nBEGIN\n RETURN random() < 0.5;\nEND;\n$EOFCODE$ LANGUAGE plpgsql",
61
61
"pretty/quoting-6.sql": "CREATE FUNCTION faker.\"boolean\"() RETURNS boolean AS $EOFCODE$\nBEGIN\n RETURN random() < 0.5;\nEND;\n$EOFCODE$ LANGUAGE plpgsql",
62
62
"pretty/quoting-7.sql": "CREATE DOMAIN origin AS text CHECK (value = pg_catalog.\"substring\"(value, '^(https?://[^/]*)'))",
Copy file name to clipboardExpand all lines: packages/deparser/QUOTING-RULES.md
+97Lines changed: 97 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -203,6 +203,99 @@ The only difference is that `quoteIdentifierAfterDot()` does not check for keywo
203
203
|`interval`|`"interval"`|`interval`|
204
204
|`my-col`|`"my-col"`|`"my-col"`|
205
205
206
+
## Type-Name Quoting Policy
207
+
208
+
Type names in PostgreSQL have their own quoting policy that is less strict than standalone identifiers but different from after-dot identifiers.
209
+
210
+
### The Problem
211
+
212
+
When you have a user-defined schema-qualified type like `myschema.json`, the type name `json` is a `COL_NAME_KEYWORD`. Using strict quoting (`quoteIdentifier()`) would produce `myschema."json"`, which is unnecessarily verbose.
213
+
214
+
However, we cannot use the fully relaxed after-dot policy (`quoteIdentifierAfterDot()`) because type names are not in the same permissive grammar slot as identifiers after a dot in qualified names.
215
+
216
+
### The Type-Name Quoting Algorithm
217
+
218
+
The `QuoteUtils.quoteIdentifierTypeName()` function implements a middle-ground policy:
219
+
220
+
-**Quote for lexical reasons**: uppercase, special characters, leading digits, embedded quotes
221
+
-**Quote only RESERVED_KEYWORD**: keywords like `select`, `from`, `where` must be quoted
222
+
-**Allow COL_NAME_KEYWORD unquoted**: keywords like `json`, `int`, `boolean` are allowed
223
+
-**Allow TYPE_FUNC_NAME_KEYWORD unquoted**: keywords like `interval`, `left`, `right` are allowed
224
+
225
+
```
226
+
function quoteIdentifierTypeName(ident):
227
+
if ident is empty:
228
+
return ident
229
+
230
+
safe = true
231
+
232
+
// Rule 1: First character must be lowercase letter or underscore
233
+
if first_char not in [a-z_]:
234
+
safe = false
235
+
236
+
// Rule 2: All characters must be in safe set
237
+
for each char in ident:
238
+
if char not in [a-z0-9_]:
239
+
safe = false
240
+
241
+
// Rule 3: Only quote RESERVED_KEYWORD (not COL_NAME_KEYWORD or TYPE_FUNC_NAME_KEYWORD)
242
+
if safe:
243
+
kwKind = keywordKindOf(ident)
244
+
if kwKind == RESERVED_KEYWORD:
245
+
safe = false
246
+
247
+
if safe:
248
+
return ident // No quoting needed
249
+
250
+
// Build quoted identifier with escaped embedded quotes
Use `quoteTypeDottedName()` in the `TypeName` handler for non-pg_catalog types. This ensures that user-defined types with keyword names are emitted with minimal quoting.
298
+
206
299
## Composition Helpers
207
300
208
301
### quoteDottedName(parts: string[])
@@ -359,13 +452,17 @@ When updating to support new PostgreSQL versions, ensure `kwlist.ts` is synchron
359
452
| Dotted name (multi-part) |`quoteDottedName()`|`schema.table`, `schema.function`|
360
453
| Two-part qualified name |`quoteQualifiedIdentifier()`|`schema.table`|
361
454
| After-dot component only |`quoteIdentifierAfterDot()`| Indirection field access |
455
+
| Type name (single or multi-part) |`quoteTypeDottedName()`|`myschema.json`, `custom.int`|
456
+
| Type name component only |`quoteIdentifierTypeName()`| Type name part |
362
457
| String literal |`escape()` or `formatEString()`| String values in SQL |
363
458
364
459
## Test Fixtures
365
460
366
461
The quoting behavior is verified by test fixtures in `__fixtures__/kitchen-sink/pretty/`:
367
462
368
463
-`quoting-1.sql` through `quoting-7.sql`: Test cases for `faker.float`, `faker.interval`, `faker.boolean`, and `pg_catalog.substring`
464
+
-`quoting-8.sql` through `quoting-13.sql`: Test cases for type casts with `json`, `jsonb`, `boolean`, `interval`, `int`
465
+
-`quoting-14.sql` through `quoting-16.sql`: Test cases for user-defined schema-qualified types with keyword names (`myschema.json`, `custom.int`, `myapp.boolean`)
369
466
370
467
The corresponding snapshots in `__tests__/pretty/__snapshots__/quoting-pretty.test.ts.snap` demonstrate the expected output with minimal quoting.
0 commit comments