prettier-plugin-postgresql 0.1.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.
Files changed (40) hide show
  1. package/README.md +197 -0
  2. package/bin/dotnet/Google.Protobuf.dll +0 -0
  3. package/bin/dotnet/PgScriptDom.deps.json +95 -0
  4. package/bin/dotnet/PgScriptDom.dll +0 -0
  5. package/bin/dotnet/PrettierSql.Core.dll +0 -0
  6. package/bin/dotnet/libpg_query.dylib +0 -0
  7. package/bin/dotnet/pgsqlparser.dll +0 -0
  8. package/bin/dotnet/runtimes/linux-x64/native/libpg_query.so +0 -0
  9. package/bin/dotnet/runtimes/osx-arm64/native/libpg_query.dylib +0 -0
  10. package/bin/dotnet/runtimes/osx-x64/native/libpg_query.dylib +0 -0
  11. package/bin/dotnet/runtimes/win-x64/native/libpg_query.dll +0 -0
  12. package/dist/index.d.ts +5 -0
  13. package/dist/index.d.ts.map +1 -0
  14. package/dist/index.js +21 -0
  15. package/dist/index.js.map +1 -0
  16. package/dist/language.d.ts +3 -0
  17. package/dist/language.d.ts.map +1 -0
  18. package/dist/language.js +9 -0
  19. package/dist/language.js.map +1 -0
  20. package/dist/parser/index.d.ts +5 -0
  21. package/dist/parser/index.d.ts.map +1 -0
  22. package/dist/parser/index.js +58 -0
  23. package/dist/parser/index.js.map +1 -0
  24. package/dist/printer/expressions.d.ts +8 -0
  25. package/dist/printer/expressions.d.ts.map +1 -0
  26. package/dist/printer/expressions.js +623 -0
  27. package/dist/printer/expressions.js.map +1 -0
  28. package/dist/printer/helpers.d.ts +6 -0
  29. package/dist/printer/helpers.d.ts.map +1 -0
  30. package/dist/printer/helpers.js +18 -0
  31. package/dist/printer/helpers.js.map +1 -0
  32. package/dist/printer/index.d.ts +4 -0
  33. package/dist/printer/index.d.ts.map +1 -0
  34. package/dist/printer/index.js +23 -0
  35. package/dist/printer/index.js.map +1 -0
  36. package/dist/printer/statements.d.ts +11 -0
  37. package/dist/printer/statements.d.ts.map +1 -0
  38. package/dist/printer/statements.js +1533 -0
  39. package/dist/printer/statements.js.map +1 -0
  40. package/package.json +59 -0
@@ -0,0 +1,1533 @@
1
+ import { keyword, hardline, join, indent, group, softline, softSep, hardSep, getDensity, } from '@prettier-sql/core/printer/utils';
2
+ import { prop, propArr, propStr, propBool, rangeVarName, qualifiedName } from './helpers.js';
3
+ import { printExpression, printWindowDef } from './expressions.js';
4
+ // ---------------------------------------------------------------------------
5
+ // Script root
6
+ // ---------------------------------------------------------------------------
7
+ export function printScript(node, opts) {
8
+ const statements = propArr(node, 'statements');
9
+ if (statements.length === 0)
10
+ return '';
11
+ const docs = statements.map((s) => printStatementWithComments(s, opts));
12
+ return [...join([hardline, hardline], docs), hardline];
13
+ }
14
+ function printStatementWithComments(node, opts) {
15
+ const leading = node.leadingComments;
16
+ const body = printStatement(node, opts);
17
+ const trailing = node.trailingComment ? [' ', node.trailingComment] : '';
18
+ if (!leading?.length)
19
+ return [body, trailing];
20
+ return [join(hardline, leading), hardline, body, trailing];
21
+ }
22
+ // ---------------------------------------------------------------------------
23
+ // Statement dispatcher
24
+ // ---------------------------------------------------------------------------
25
+ export function printStatement(node, opts) {
26
+ switch (node.type) {
27
+ case 'SelectStatement': return printSelect(node, opts);
28
+ case 'InsertStatement': return printInsert(node, opts);
29
+ case 'UpdateStatement': return printUpdate(node, opts);
30
+ case 'DeleteStatement': return printDelete(node, opts);
31
+ case 'SetOpStatement': return printSetOp(node, opts);
32
+ case 'ValuesStatement': return printValues(node, opts);
33
+ case 'CreateTableStatement': return printCreateTable(node, opts);
34
+ case 'AlterTableStatement': return printAlterTable(node, opts);
35
+ case 'CreateViewStatement': return printCreateView(node, opts);
36
+ case 'CreateFunctionStatement': return printCreateFunction(node, opts);
37
+ case 'CreateIndexStatement': return printCreateIndex(node, opts);
38
+ case 'DropStatement': return printDrop(node, opts);
39
+ case 'TruncateStatement': return printTruncate(node, opts);
40
+ case 'TransactionStatement': return printTransaction(node, opts);
41
+ case 'VariableSetStatement': return printVariableSet(node, opts);
42
+ case 'VariableShowStatement': return printVariableShow(node, opts);
43
+ case 'GrantStatement': return printGrant(node, opts);
44
+ case 'RevokeStatement': return printRevoke(node, opts);
45
+ case 'CreateRoleStatement': return printCreateRole(node, opts);
46
+ case 'AlterRoleStatement': return printAlterRole(node, opts);
47
+ case 'RenameStatement': return printRename(node, opts);
48
+ case 'CreateTypeStatement': return printCreateType(node, opts);
49
+ case 'AlterTypeStatement': return printAlterType(node, opts);
50
+ case 'CreateSequenceStatement': return printCreateSequence(node, opts);
51
+ case 'AlterSequenceStatement': return printAlterSequence(node, opts);
52
+ case 'CreateSchemaStatement': return printCreateSchema(node, opts);
53
+ case 'CreateExtensionStatement': return printCreateExtension(node, opts);
54
+ case 'CreateTableAsStatement': return printCreateTableAs(node, opts);
55
+ case 'CreateMatViewStatement': return printCreateMatView(node, opts);
56
+ case 'CreateTriggerStatement': return printCreateTrigger(node, opts);
57
+ case 'CommentStatement': return printComment(node, opts);
58
+ case 'CallStatement': return printCall(node, opts);
59
+ case 'DoStatement': return printDo(node, opts);
60
+ case 'MergeStatement': return printMerge(node, opts);
61
+ case 'AlterFunctionStatement': return printAlterFunction(node, opts);
62
+ case 'RefreshMatViewStatement': return printRefreshMatView(node, opts);
63
+ case 'SelectIntoStatement': return printSelectInto(node, opts);
64
+ case 'RuleStatement': return printRule(node, opts);
65
+ case 'CreatePolicyStatement': return printCreatePolicy(node, opts);
66
+ case 'AlterPolicyStatement': return printAlterPolicy(node, opts);
67
+ case 'DeclareCursorStatement': return printDeclareCursor(node, opts);
68
+ case 'FetchStatement': return printFetch(node, opts);
69
+ case 'ClosePortalStatement': return printClosePortal(node, opts);
70
+ case 'CopyStatement': return printCopy(node, opts);
71
+ case 'ExplainStatement': return printExplain(node, opts);
72
+ case 'PrepareStatement': return printPrepare(node, opts);
73
+ case 'ExecuteStatement': return printExecute(node, opts);
74
+ case 'DeallocateStatement': return printDeallocate(node, opts);
75
+ case 'ListenStatement': return printListen(node, opts);
76
+ case 'UnlistenStatement': return printUnlisten(node, opts);
77
+ case 'NotifyStatement': return printNotify(node, opts);
78
+ case 'LockStatement': return printLockTable(node, opts);
79
+ case 'CreateTablePartitionOfStatement': return printCreateTablePartitionOf(node, opts);
80
+ case 'VacuumStatement': return printVacuum(node, opts);
81
+ case 'ClusterStatement': return printCluster(node, opts);
82
+ case 'ReindexStatement': return printReindex(node, opts);
83
+ case 'CreateForeignServerStatement': return printCreateForeignServer(node, opts);
84
+ case 'CreateForeignTableStatement': return printCreateForeignTable(node, opts);
85
+ case 'CreateUserMappingStatement': return printCreateUserMapping(node, opts);
86
+ case 'ImportForeignSchemaStatement': return printImportForeignSchema(node, opts);
87
+ case 'CreatePublicationStatement': return printCreatePublication(node, opts);
88
+ case 'CreateSubscriptionStatement': return printCreateSubscription(node, opts);
89
+ case 'DropSubscriptionStatement': return printDropSubscription(node, opts);
90
+ case 'CreateAggregateStatement': return printCreateAggregate(node, opts);
91
+ case 'CreateOperatorStatement': return printCreateOperator(node, opts);
92
+ case 'CreateCollationStatement': return printCreateCollation(node, opts);
93
+ case 'SecurityLabelStatement': return printSecurityLabel(node, opts);
94
+ case 'AlterOwnerStatement': return printAlterOwner(node, opts);
95
+ case 'AlterObjectSchemaStatement': return printAlterObjectSchema(node, opts);
96
+ default: return node.text ?? node.type;
97
+ }
98
+ }
99
+ /**
100
+ * Print a SELECT, SET-op, or DML as a query expression (no trailing semicolon).
101
+ * Used when a query appears as a sub-expression: subquery, CTE body, INSERT source.
102
+ */
103
+ export function printQueryExpr(node, opts) {
104
+ switch (node.type) {
105
+ case 'SetOpStatement': return printSetOpBody(node, opts);
106
+ case 'InsertStatement': return printInsertBody(node, opts);
107
+ case 'UpdateStatement': return printUpdateBody(node, opts);
108
+ case 'DeleteStatement': return printDeleteBody(node, opts);
109
+ default: return printSelectBody(node, opts);
110
+ }
111
+ }
112
+ // ---------------------------------------------------------------------------
113
+ // Shared helpers
114
+ // ---------------------------------------------------------------------------
115
+ function printWith(opts) {
116
+ return function printNode(n) {
117
+ return n.type.endsWith('Statement')
118
+ ? printQueryExpr(n, opts)
119
+ : printExpression(n, opts, printNode);
120
+ };
121
+ }
122
+ /**
123
+ * Density-aware boolean clause (WHERE / HAVING / ON CONFLICT WHERE).
124
+ * Single predicate: inline in compact/standard; indented in spacious.
125
+ * Multi-predicate (BoolExpr AND/OR): always indented.
126
+ */
127
+ function printBoolClause(kw, where, opts, printNode) {
128
+ const makeKeyword = (k) => keyword(k, opts);
129
+ const density = getDensity(opts);
130
+ const isMulti = where.type === 'BoolExpr' && (propStr(where, 'op') ?? 'AND') !== 'NOT';
131
+ const inline = density !== 'spacious' && !isMulti;
132
+ const body = printNode(where);
133
+ return [makeKeyword(kw), inline ? [' ', body] : indent([hardline, body])];
134
+ }
135
+ /**
136
+ * Single-item keyword clause (GROUP BY, ORDER BY) — stays inline in standard/compact.
137
+ */
138
+ function printListClause(kw, items, opts, printNode) {
139
+ const makeKeyword = (k) => keyword(k, opts);
140
+ const inline = getDensity(opts) !== 'spacious' && items.length === 1;
141
+ const body = join(hardSep(opts), items.map(printNode));
142
+ return [makeKeyword(kw), inline ? [' ', body] : indent([hardline, body])];
143
+ }
144
+ /**
145
+ * FROM clause: single non-join item stays inline; joins and multiple items are indented.
146
+ */
147
+ function printFromClause(items, opts, printNode) {
148
+ const makeKeyword = (k) => keyword(k, opts);
149
+ const inline = items.length === 1 && items[0].type !== 'JoinExpr';
150
+ const body = join([',', hardline], items.map(printNode));
151
+ return [makeKeyword('FROM'), inline ? [' ', body] : indent([hardline, body])];
152
+ }
153
+ // ---------------------------------------------------------------------------
154
+ // SELECT
155
+ // ---------------------------------------------------------------------------
156
+ function printCtes(ctes, opts, printNode) {
157
+ const makeKeyword = (k) => keyword(k, opts);
158
+ const cteList = propArr(ctes, 'ctes');
159
+ const recursive = propBool(ctes, 'recursive');
160
+ const cteKw = recursive ? makeKeyword('WITH RECURSIVE') : makeKeyword('WITH');
161
+ const cteDocs = cteList.map((cte) => {
162
+ const name = propStr(cte, 'name') ?? '';
163
+ const query = prop(cte, 'query');
164
+ const search = prop(cte, 'search');
165
+ const cycle = prop(cte, 'cycle');
166
+ const parts = [name, ' ', makeKeyword('AS'), ' (', indent([hardline, query ? printNode(query) : '']), hardline, ')'];
167
+ if (search) {
168
+ const breadthFirst = propBool(search, 'breadthFirst');
169
+ const cols = search.props?.['columns'] ?? [];
170
+ const seqCol = propStr(search, 'seqColumn') ?? '';
171
+ const firstLast = breadthFirst ? makeKeyword('BREADTH FIRST') : makeKeyword('DEPTH FIRST');
172
+ parts.push(hardline, makeKeyword('SEARCH'), ' ', firstLast, ' ', makeKeyword('BY'), ' ', join(', ', cols), ' ', makeKeyword('SET'), ' ', seqCol);
173
+ }
174
+ if (cycle) {
175
+ const cols = cycle.props?.['columns'] ?? [];
176
+ const markCol = propStr(cycle, 'markColumn') ?? '';
177
+ const pathCol = propStr(cycle, 'pathColumn') ?? '';
178
+ parts.push(hardline, makeKeyword('CYCLE'), ' ', join(', ', cols), ' ', makeKeyword('SET'), ' ', markCol, ' ', makeKeyword('USING'), ' ', pathCol);
179
+ }
180
+ return parts;
181
+ });
182
+ return [[cteKw, indent([hardline, join([',', hardline], cteDocs)])]];
183
+ }
184
+ function printSelectBody(node, opts) {
185
+ const makeKeyword = (k) => keyword(k, opts);
186
+ const printNode = printWith(opts);
187
+ const ctes = prop(node, 'ctes');
188
+ const distinct = propBool(node, 'distinct');
189
+ const targets = propArr(node, 'targetList');
190
+ const from = propArr(node, 'from');
191
+ const where = prop(node, 'where');
192
+ const groupBy = propArr(node, 'groupBy');
193
+ const having = prop(node, 'having');
194
+ const orderBy = propArr(node, 'orderBy');
195
+ const limit = prop(node, 'limit');
196
+ const offset = prop(node, 'offset');
197
+ const parts = [];
198
+ if (ctes) {
199
+ parts.push(...printCtes(ctes, opts, printNode));
200
+ }
201
+ const distinctOn = propArr(node, 'distinctOn');
202
+ const selectKw = distinctOn.length > 0
203
+ ? [makeKeyword('SELECT'), ' ', makeKeyword('DISTINCT ON'), ' (', join(', ', distinctOn.map(printNode)), ')']
204
+ : distinct
205
+ ? [makeKeyword('SELECT'), ' ', makeKeyword('DISTINCT')]
206
+ : makeKeyword('SELECT');
207
+ const selectInline = getDensity(opts) !== 'spacious' && targets.length === 1;
208
+ const targetDoc = join(hardSep(opts), targets.map(printNode));
209
+ parts.push([selectKw, selectInline ? [' ', targetDoc] : indent([hardline, targetDoc])]);
210
+ if (from.length > 0) {
211
+ parts.push(printFromClause(from, opts, printNode));
212
+ }
213
+ if (where)
214
+ parts.push(printBoolClause('WHERE', where, opts, printNode));
215
+ if (groupBy.length > 0)
216
+ parts.push(printListClause('GROUP BY', groupBy, opts, printNode));
217
+ if (having)
218
+ parts.push(printBoolClause('HAVING', having, opts, printNode));
219
+ if (orderBy.length > 0)
220
+ parts.push(printListClause('ORDER BY', orderBy, opts, printNode));
221
+ if (limit)
222
+ parts.push([makeKeyword('LIMIT'), ' ', printNode(limit)]);
223
+ if (offset)
224
+ parts.push([makeKeyword('OFFSET'), ' ', printNode(offset)]);
225
+ const locking = propArr(node, 'locking');
226
+ for (const lc of locking) {
227
+ const strength = propStr(lc, 'strength') ?? 'FOR UPDATE';
228
+ const tables = propArr(lc, 'tables');
229
+ const waitPolicy = propStr(lc, 'waitPolicy');
230
+ const ofPart = tables.length > 0
231
+ ? [' ', makeKeyword('OF'), ' ', join(', ', tables.map((t) => rangeVarName(t)))]
232
+ : '';
233
+ const waitPart = waitPolicy ? [' ', makeKeyword(waitPolicy)] : '';
234
+ parts.push([makeKeyword(strength), ofPart, waitPart]);
235
+ }
236
+ // Named WINDOW clauses: WINDOW w AS (PARTITION BY ... ORDER BY ...)
237
+ const windowClauses = propArr(node, 'windowClauses');
238
+ if (windowClauses.length > 0) {
239
+ const wDocs = windowClauses.map((w) => {
240
+ const wName = propStr(w, 'name') ?? '';
241
+ const wSpec = printWindowDef(w, opts, printNode);
242
+ return [wName, ' ', makeKeyword('AS'), ' (', wSpec, ')'];
243
+ });
244
+ parts.push([makeKeyword('WINDOW'), indent([hardline, join(hardSep(opts), wDocs)])]);
245
+ }
246
+ return group(join(hardline, parts));
247
+ }
248
+ function printSelect(node, opts) {
249
+ return [printSelectBody(node, opts), ';'];
250
+ }
251
+ // ---------------------------------------------------------------------------
252
+ // INSERT
253
+ // ---------------------------------------------------------------------------
254
+ function printInsertBody(node, opts) {
255
+ const makeKeyword = (k) => keyword(k, opts);
256
+ const printNode = printWith(opts);
257
+ const ctes = prop(node, 'ctes');
258
+ const target = prop(node, 'target');
259
+ const columns = propArr(node, 'columns');
260
+ const override = propStr(node, 'override');
261
+ const source = prop(node, 'source');
262
+ const onConflict = prop(node, 'onConflict');
263
+ const returning = propArr(node, 'returning');
264
+ const cteParts = ctes ? printCtes(ctes, opts, printNode) : [];
265
+ const colsPart = columns.length > 0
266
+ ? group([' (', indent([softline, join(softSep(opts), columns.map((c) => propStr(c, 'name') ?? ''))]), softline, ')'])
267
+ : '';
268
+ const overridePart = override ? [' ', makeKeyword(`OVERRIDING ${override} VALUE`)] : '';
269
+ const sourcePart = source?.type === 'DefaultValues'
270
+ ? [hardline, makeKeyword('DEFAULT VALUES')]
271
+ : source?.type === 'ValuesStatement'
272
+ ? printValuesRows(source, opts, printNode)
273
+ : source
274
+ ? [hardline, printQueryExpr(source, opts)]
275
+ : '';
276
+ const parts = [
277
+ ...cteParts,
278
+ [makeKeyword('INSERT INTO'), ' ', rangeVarName(target), colsPart, overridePart, sourcePart],
279
+ ];
280
+ if (onConflict)
281
+ parts.push(printOnConflict(onConflict, opts, printNode));
282
+ if (returning.length > 0) {
283
+ parts.push(printListClause('RETURNING', returning, opts, printNode));
284
+ }
285
+ return group(join(hardline, parts));
286
+ }
287
+ function printInsert(node, opts) {
288
+ return [printInsertBody(node, opts), ';'];
289
+ }
290
+ // ---------------------------------------------------------------------------
291
+ // UPDATE
292
+ // ---------------------------------------------------------------------------
293
+ function printUpdateBody(node, opts) {
294
+ const makeKeyword = (k) => keyword(k, opts);
295
+ const printNode = printWith(opts);
296
+ const ctes = prop(node, 'ctes');
297
+ const target = prop(node, 'target');
298
+ const sets = propArr(node, 'sets');
299
+ const from = propArr(node, 'from');
300
+ const where = prop(node, 'where');
301
+ const returning = propArr(node, 'returning');
302
+ const density = getDensity(opts);
303
+ const setDocs = sets.map((s) => {
304
+ const name = propStr(s, 'name') ?? '';
305
+ const val = prop(s, 'val');
306
+ return [name, ' = ', val ? printNode(val) : ''];
307
+ });
308
+ const parts = ctes ? printCtes(ctes, opts, printNode) : [];
309
+ parts.push([makeKeyword('UPDATE'), ' ', rangeVarName(target)], [
310
+ makeKeyword('SET'),
311
+ density !== 'spacious' && setDocs.length === 1
312
+ ? [' ', setDocs[0]]
313
+ : indent([hardline, join(hardSep(opts), setDocs)]),
314
+ ]);
315
+ if (from.length > 0) {
316
+ parts.push(printFromClause(from, opts, printNode));
317
+ }
318
+ if (where)
319
+ parts.push(printBoolClause('WHERE', where, opts, printNode));
320
+ if (returning.length > 0) {
321
+ parts.push(printListClause('RETURNING', returning, opts, printNode));
322
+ }
323
+ return group(join(hardline, parts));
324
+ }
325
+ function printUpdate(node, opts) {
326
+ return [printUpdateBody(node, opts), ';'];
327
+ }
328
+ // ---------------------------------------------------------------------------
329
+ // DELETE
330
+ // ---------------------------------------------------------------------------
331
+ function printDeleteBody(node, opts) {
332
+ const makeKeyword = (k) => keyword(k, opts);
333
+ const printNode = printWith(opts);
334
+ const ctes = prop(node, 'ctes');
335
+ const target = prop(node, 'target');
336
+ const using = propArr(node, 'using');
337
+ const where = prop(node, 'where');
338
+ const returning = propArr(node, 'returning');
339
+ const parts = ctes ? printCtes(ctes, opts, printNode) : [];
340
+ parts.push([makeKeyword('DELETE FROM'), ' ', rangeVarName(target)]);
341
+ if (using.length > 0)
342
+ parts.push(printListClause('USING', using, opts, printNode));
343
+ if (where)
344
+ parts.push(printBoolClause('WHERE', where, opts, printNode));
345
+ if (returning.length > 0) {
346
+ parts.push(printListClause('RETURNING', returning, opts, printNode));
347
+ }
348
+ return group(join(hardline, parts));
349
+ }
350
+ function printDelete(node, opts) {
351
+ return [printDeleteBody(node, opts), ';'];
352
+ }
353
+ // ---------------------------------------------------------------------------
354
+ // SET operations (UNION / INTERSECT / EXCEPT)
355
+ // ---------------------------------------------------------------------------
356
+ function printSetOpBody(node, opts) {
357
+ const makeKeyword = (k) => keyword(k, opts);
358
+ const op = propStr(node, 'op') ?? 'UNION';
359
+ const all = propBool(node, 'all');
360
+ const lhs = prop(node, 'lhs');
361
+ const rhs = prop(node, 'rhs');
362
+ const opKw = all ? makeKeyword(`${op} ALL`) : makeKeyword(op);
363
+ return [
364
+ lhs ? printQueryExpr(lhs, opts) : '',
365
+ hardline, opKw, hardline,
366
+ rhs ? printQueryExpr(rhs, opts) : '',
367
+ ];
368
+ }
369
+ function printSetOp(node, opts) {
370
+ return [printSetOpBody(node, opts), ';'];
371
+ }
372
+ // ---------------------------------------------------------------------------
373
+ // VALUES
374
+ // ---------------------------------------------------------------------------
375
+ /**
376
+ * Render VALUES rows without a trailing semicolon — used as INSERT source.
377
+ * Uses softSep within each row and hardSep between rows, matching tsql style.
378
+ */
379
+ function printValuesRows(node, opts, printNode) {
380
+ const makeKeyword = (k) => keyword(k, opts);
381
+ const rows = propArr(node, 'rows');
382
+ const rowDocs = rows.map((row) => {
383
+ const items = propArr(row, 'items').map(printNode);
384
+ return group(['(', indent([softline, join(softSep(opts), items)]), softline, ')']);
385
+ });
386
+ if (rowDocs.length === 1) {
387
+ return [hardline, makeKeyword('VALUES'), ' ', rowDocs[0]];
388
+ }
389
+ return [hardline, makeKeyword('VALUES'), indent([hardline, join(hardSep(opts), rowDocs)])];
390
+ }
391
+ function printValues(node, opts) {
392
+ return [printValuesRows(node, opts, printWith(opts)), ';'];
393
+ }
394
+ // ---------------------------------------------------------------------------
395
+ // ON CONFLICT
396
+ // ---------------------------------------------------------------------------
397
+ function printOnConflict(node, opts, printNode) {
398
+ const makeKeyword = (k) => keyword(k, opts);
399
+ const action = propStr(node, 'action') ?? 'NOTHING';
400
+ const target = prop(node, 'target');
401
+ const sets = propArr(node, 'sets');
402
+ const where = prop(node, 'where');
403
+ let targetDoc = '';
404
+ if (target) {
405
+ const cols = propArr(target, 'columns');
406
+ const constraint = propStr(target, 'constraint');
407
+ if (constraint) {
408
+ targetDoc = [' ', makeKeyword('ON CONSTRAINT'), ' ', constraint];
409
+ }
410
+ else if (cols.length > 0) {
411
+ targetDoc = group([' (', indent([softline, join(softSep(opts), cols.map((c) => propStr(c, 'name') ?? ''))]), softline, ')']);
412
+ }
413
+ }
414
+ if (action === 'NOTHING') {
415
+ return [makeKeyword('ON CONFLICT'), targetDoc, ' ', makeKeyword('DO NOTHING')];
416
+ }
417
+ // DO UPDATE SET
418
+ const setDocs = sets.map((s) => {
419
+ const name = propStr(s, 'name') ?? '';
420
+ const val = prop(s, 'val');
421
+ return [name, ' = ', val ? printNode(val) : ''];
422
+ });
423
+ const density = getDensity(opts);
424
+ const parts = [
425
+ [makeKeyword('ON CONFLICT'), targetDoc, ' ', makeKeyword('DO UPDATE')],
426
+ [
427
+ makeKeyword('SET'),
428
+ density !== 'spacious' && setDocs.length === 1
429
+ ? [' ', setDocs[0]]
430
+ : indent([hardline, join(hardSep(opts), setDocs)]),
431
+ ],
432
+ ];
433
+ if (where)
434
+ parts.push(printBoolClause('WHERE', where, opts, printNode));
435
+ return join(hardline, parts);
436
+ }
437
+ // ---------------------------------------------------------------------------
438
+ // DDL
439
+ // ---------------------------------------------------------------------------
440
+ function printCreateTable(node, opts) {
441
+ const makeKeyword = (k) => keyword(k, opts);
442
+ const printNode = printWith(opts);
443
+ const name = prop(node, 'name');
444
+ const columns = propArr(node, 'columns');
445
+ const partitionBy = prop(node, 'partitionBy');
446
+ const partitionDoc = partitionBy
447
+ ? [hardline, makeKeyword('PARTITION BY'), ' ', makeKeyword(propStr(partitionBy, 'strategy') ?? 'RANGE'),
448
+ ' (', join(', ', partitionBy.props?.['columns'] ?? []), ')']
449
+ : '';
450
+ return [
451
+ makeKeyword('CREATE TABLE'), ' ', rangeVarName(name), ' (',
452
+ indent([hardline, join([',', hardline], columns.map(printNode))]),
453
+ hardline, ')',
454
+ partitionDoc,
455
+ ';',
456
+ ];
457
+ }
458
+ function printAlterTable(node, opts) {
459
+ const makeKeyword = (k) => keyword(k, opts);
460
+ const printNode = printWith(opts);
461
+ const name = prop(node, 'name');
462
+ const commands = propArr(node, 'commands');
463
+ return [
464
+ makeKeyword('ALTER TABLE'), ' ', rangeVarName(name),
465
+ indent([hardline, join([',', hardline], commands.map(printNode))]),
466
+ ';',
467
+ ];
468
+ }
469
+ function printCreateView(node, opts) {
470
+ const makeKeyword = (k) => keyword(k, opts);
471
+ const name = prop(node, 'name');
472
+ const body = prop(node, 'body');
473
+ return [
474
+ makeKeyword('CREATE VIEW'), ' ', rangeVarName(name), hardline,
475
+ makeKeyword('AS'), hardline,
476
+ body ? printQueryExpr(body, opts) : '',
477
+ ';',
478
+ ];
479
+ }
480
+ function printCreateFunction(node, opts) {
481
+ const makeKeyword = (k) => keyword(k, opts);
482
+ const printNode = printWith(opts);
483
+ const name = propStr(node, 'name') ?? '';
484
+ const parameters = propArr(node, 'parameters');
485
+ const returnType = propStr(node, 'returnType');
486
+ const returnsTable = propArr(node, 'returnsTable');
487
+ const language = propStr(node, 'language');
488
+ const body = propStr(node, 'body');
489
+ const parts = [
490
+ makeKeyword('CREATE FUNCTION'), ' ', name,
491
+ '(', join(', ', parameters.map(printNode)), ')',
492
+ ];
493
+ if (returnsTable.length > 0) {
494
+ parts.push(hardline, makeKeyword('RETURNS TABLE'), ' (', join(', ', returnsTable.map(printNode)), ')');
495
+ }
496
+ else if (returnType) {
497
+ parts.push(hardline, makeKeyword('RETURNS'), ' ', makeKeyword(returnType));
498
+ }
499
+ if (language)
500
+ parts.push(hardline, makeKeyword('LANGUAGE'), ' ', language);
501
+ if (body != null) {
502
+ parts.push(hardline, makeKeyword('AS'), ' ', '$$', body, '$$');
503
+ }
504
+ return [join('', parts), ';'];
505
+ }
506
+ function printCreateIndex(node, opts) {
507
+ const makeKeyword = (k) => keyword(k, opts);
508
+ const printNode = printWith(opts);
509
+ const unique = propBool(node, 'unique');
510
+ const concurrent = propBool(node, 'concurrent');
511
+ const ifNotExists = propBool(node, 'ifNotExists');
512
+ const indexName = propStr(node, 'indexName') ?? '';
513
+ const relation = prop(node, 'relation');
514
+ const columns = propArr(node, 'columns');
515
+ const including = propArr(node, 'including');
516
+ const accessMethod = propStr(node, 'accessMethod');
517
+ const where = prop(node, 'where');
518
+ const keyword1 = unique ? makeKeyword('CREATE UNIQUE INDEX') : makeKeyword('CREATE INDEX');
519
+ const parts = [keyword1];
520
+ if (concurrent)
521
+ parts.push(' ', makeKeyword('CONCURRENTLY'));
522
+ if (ifNotExists)
523
+ parts.push(' ', makeKeyword('IF NOT EXISTS'));
524
+ parts.push(' ', indexName, ' ', makeKeyword('ON'), ' ', rangeVarName(relation));
525
+ if (accessMethod)
526
+ parts.push(' ', makeKeyword('USING'), ' ', accessMethod);
527
+ parts.push(' (', join(', ', columns.map(printNode)), ')');
528
+ if (including.length > 0)
529
+ parts.push(' ', makeKeyword('INCLUDE'), ' (', join(', ', including.map(printNode)), ')');
530
+ if (where)
531
+ parts.push(' ', makeKeyword('WHERE'), ' ', printNode(where));
532
+ parts.push(';');
533
+ return parts;
534
+ }
535
+ function printTruncate(node, opts) {
536
+ const makeKeyword = (k) => keyword(k, opts);
537
+ const relations = propArr(node, 'relations');
538
+ const restart = propBool(node, 'restartSeqs');
539
+ const cascade = propBool(node, 'cascade');
540
+ return [
541
+ makeKeyword('TRUNCATE TABLE'), ' ', join(', ', relations.map(rangeVarName)),
542
+ restart ? [' ', makeKeyword('RESTART IDENTITY')] : '',
543
+ cascade ? [' ', makeKeyword('CASCADE')] : '',
544
+ ';',
545
+ ];
546
+ }
547
+ function printDrop(node, opts) {
548
+ const makeKeyword = (k) => keyword(k, opts);
549
+ const objectType = propStr(node, 'objectType') ?? '';
550
+ const names = node.props?.['names'] ?? [];
551
+ const ifExists = propBool(node, 'ifExists');
552
+ const cascade = propBool(node, 'cascade');
553
+ return [
554
+ makeKeyword('DROP'), ' ', makeKeyword(objectType),
555
+ ifExists ? [' ', makeKeyword('IF EXISTS')] : '',
556
+ names.length > 0 ? [' ', join(', ', names)] : '',
557
+ cascade ? [' ', makeKeyword('CASCADE')] : '',
558
+ ';',
559
+ ];
560
+ }
561
+ // ---------------------------------------------------------------------------
562
+ // SET / SHOW / RESET
563
+ // ---------------------------------------------------------------------------
564
+ function printVariableSet(node, opts) {
565
+ const makeKeyword = (k) => keyword(k, opts);
566
+ const kind = propStr(node, 'kind') ?? 'SET';
567
+ const name = propStr(node, 'name') ?? '';
568
+ const values = node.props?.['values'] ?? [];
569
+ const local = propBool(node, 'local');
570
+ if (kind === 'RESET ALL')
571
+ return [makeKeyword('RESET ALL'), ';'];
572
+ if (kind === 'RESET')
573
+ return [[makeKeyword('RESET'), ' ', name], ';'];
574
+ const localKw = local ? [makeKeyword('LOCAL'), ' '] : '';
575
+ if (kind === 'SET DEFAULT') {
576
+ return [[makeKeyword('SET'), ' ', localKw, name, ' ', makeKeyword('TO'), ' ', makeKeyword('DEFAULT')], ';'];
577
+ }
578
+ // SET name = value(s)
579
+ // - Starts with digit or contains special chars: must be quoted
580
+ // - Otherwise: lowercase (PostgreSQL normalizes unquoted identifiers)
581
+ const valDocs = values.map((v) => {
582
+ if (/^[0-9]/.test(v) || /[^a-zA-Z0-9_$]/.test(v))
583
+ return `'${v.replace(/'/g, "''")}'`;
584
+ return v.toLowerCase();
585
+ });
586
+ return [[makeKeyword('SET'), ' ', localKw, name, ' = ', join(', ', valDocs)], ';'];
587
+ }
588
+ function printVariableShow(node, opts) {
589
+ const makeKeyword = (k) => keyword(k, opts);
590
+ const name = propStr(node, 'name') ?? '';
591
+ return [[makeKeyword('SHOW'), ' ', name], ';'];
592
+ }
593
+ // ---------------------------------------------------------------------------
594
+ // GRANT / REVOKE
595
+ // ---------------------------------------------------------------------------
596
+ function printGrantRevoke(node, opts, isGrant) {
597
+ const makeKeyword = (k) => keyword(k, opts);
598
+ const printNode = printWith(opts);
599
+ const privs = node.props?.['privs'] ?? [];
600
+ const objtype = propStr(node, 'objtype') ?? '';
601
+ const objects = propArr(node, 'objects');
602
+ const grantees = node.props?.['grantees'] ?? [];
603
+ const grantOption = propBool(node, 'grantOption');
604
+ const cascade = propBool(node, 'cascade');
605
+ const privsDoc = privs.length > 0 ? join(', ', privs.map(makeKeyword)) : makeKeyword('ALL PRIVILEGES');
606
+ const verb = isGrant ? makeKeyword('GRANT') : makeKeyword('REVOKE');
607
+ const toFrom = isGrant ? makeKeyword('TO') : makeKeyword('FROM');
608
+ const objectsDoc = objects.length > 0
609
+ ? join(', ', objects.map(printNode))
610
+ : '';
611
+ const parts = [
612
+ [verb, ' ', privsDoc, ' ', makeKeyword('ON'), ' ', makeKeyword(objtype), objectsDoc ? [' ', objectsDoc] : ''],
613
+ [toFrom, ' ', join(', ', grantees)],
614
+ ];
615
+ if (isGrant && grantOption)
616
+ parts.push(makeKeyword('WITH GRANT OPTION'));
617
+ if (!isGrant && cascade)
618
+ parts.push(makeKeyword('CASCADE'));
619
+ return [join(hardline, parts), ';'];
620
+ }
621
+ function printGrant(node, opts) {
622
+ return printGrantRevoke(node, opts, true);
623
+ }
624
+ function printRevoke(node, opts) {
625
+ return printGrantRevoke(node, opts, false);
626
+ }
627
+ // ---------------------------------------------------------------------------
628
+ // CREATE / ALTER ROLE
629
+ // ---------------------------------------------------------------------------
630
+ function printCreateRole(node, opts) {
631
+ const makeKeyword = (k) => keyword(k, opts);
632
+ const stmtType = propStr(node, 'stmtType') ?? 'ROLE';
633
+ const name = propStr(node, 'name') ?? '';
634
+ const options = node.props?.['options'] ?? [];
635
+ const parts = [[makeKeyword(`CREATE ${stmtType}`), ' ', name]];
636
+ if (options.length > 0)
637
+ parts.push(join(' ', options.map(makeKeyword)));
638
+ return [join(hardline, parts), ';'];
639
+ }
640
+ function printAlterRole(node, opts) {
641
+ const makeKeyword = (k) => keyword(k, opts);
642
+ const name = propStr(node, 'name') ?? '';
643
+ const options = node.props?.['options'] ?? [];
644
+ const parts = [[makeKeyword('ALTER ROLE'), ' ', name]];
645
+ if (options.length > 0)
646
+ parts.push(join(' ', options.map(makeKeyword)));
647
+ return [join(hardline, parts), ';'];
648
+ }
649
+ // ---------------------------------------------------------------------------
650
+ // RENAME (ALTER TABLE ... RENAME / ALTER INDEX ... RENAME / etc.)
651
+ // ---------------------------------------------------------------------------
652
+ function printRename(node, opts) {
653
+ const makeKeyword = (k) => keyword(k, opts);
654
+ const renameType = propStr(node, 'renameType') ?? 'RENAME TABLE';
655
+ const relation = prop(node, 'relation');
656
+ const objName = propStr(node, 'objName');
657
+ const oldName = propStr(node, 'oldName');
658
+ const newName = propStr(node, 'newName') ?? '';
659
+ if (renameType === 'RENAME TABLE') {
660
+ return [[makeKeyword('ALTER TABLE'), ' ', rangeVarName(relation), ' ', makeKeyword('RENAME TO'), ' ', newName], ';'];
661
+ }
662
+ if (renameType === 'RENAME COLUMN') {
663
+ return [[makeKeyword('ALTER TABLE'), ' ', rangeVarName(relation), ' ', makeKeyword('RENAME COLUMN'), ' ', oldName ?? '', ' ', makeKeyword('TO'), ' ', newName], ';'];
664
+ }
665
+ // FUNCTION, PROCEDURE — use objName + arg types
666
+ if (renameType === 'RENAME FUNCTION' || renameType === 'RENAME PROCEDURE') {
667
+ const objKw = renameType.replace('RENAME ', '');
668
+ const argTypes = node.props?.['objArgTypes'] ?? [];
669
+ const argList = argTypes.length > 0 ? ['(', join(', ', argTypes.map((t) => makeKeyword(t))), ')'] : '()';
670
+ return [[makeKeyword(`ALTER ${objKw}`), ' ', objName ?? '', argList, ' ', makeKeyword('RENAME TO'), ' ', newName], ';'];
671
+ }
672
+ // INDEX, SCHEMA, VIEW, MATERIALIZED VIEW, SEQUENCE, TYPE, TRIGGER ...
673
+ const objKw = renameType.replace('RENAME ', '');
674
+ return [[makeKeyword(`ALTER ${objKw}`), ' ', objName ?? rangeVarName(relation) ?? oldName ?? '', ' ', makeKeyword('RENAME TO'), ' ', newName], ';'];
675
+ }
676
+ // ---------------------------------------------------------------------------
677
+ // CREATE TYPE / ALTER TYPE
678
+ // ---------------------------------------------------------------------------
679
+ function printCreateType(node, opts) {
680
+ const makeKeyword = (k) => keyword(k, opts);
681
+ const printNode = printWith(opts);
682
+ const kind = propStr(node, 'kind') ?? 'COMPOSITE';
683
+ const typeName = propStr(node, 'typeName') ?? '';
684
+ const columns = propArr(node, 'columns');
685
+ const values = node.props?.['values'] ?? [];
686
+ if (kind === 'ENUM') {
687
+ const valList = values.length > 0
688
+ ? ['(', indent([hardline, join([',', hardline], values.map((v) => `'${v}'`))]), hardline, ')']
689
+ : '()';
690
+ return [[makeKeyword('CREATE TYPE'), ' ', typeName, ' ', makeKeyword('AS ENUM'), ' ', valList], ';'];
691
+ }
692
+ // COMPOSITE
693
+ return [
694
+ makeKeyword('CREATE TYPE'), ' ', typeName, ' ', makeKeyword('AS'), ' (',
695
+ indent([hardline, join([',', hardline], columns.map(printNode))]),
696
+ hardline, ');',
697
+ ];
698
+ }
699
+ function printAlterType(node, opts) {
700
+ const makeKeyword = (k) => keyword(k, opts);
701
+ const typeName = propStr(node, 'typeName') ?? '';
702
+ const newVal = propStr(node, 'newVal') ?? '';
703
+ const neighbor = propStr(node, 'neighbor');
704
+ const isAfter = propBool(node, 'isAfter');
705
+ const ifNotExists = propBool(node, 'ifNotExists');
706
+ const ifNotExistsDoc = ifNotExists ? [makeKeyword('IF NOT EXISTS'), ' '] : '';
707
+ const placement = neighbor
708
+ ? [' ', isAfter ? makeKeyword('AFTER') : makeKeyword('BEFORE'), ' ', `'${neighbor}'`]
709
+ : '';
710
+ return [[makeKeyword('ALTER TYPE'), ' ', typeName, ' ', makeKeyword('ADD VALUE'), ' ', ifNotExistsDoc, `'${newVal}'`, placement], ';'];
711
+ }
712
+ // ---------------------------------------------------------------------------
713
+ // CREATE / ALTER SEQUENCE
714
+ // ---------------------------------------------------------------------------
715
+ function printCreateSequence(node, opts) {
716
+ const makeKeyword = (k) => keyword(k, opts);
717
+ const schema = propStr(node, 'schema');
718
+ const name = propStr(node, 'name') ?? '';
719
+ const ifNotExists = propBool(node, 'ifNotExists');
720
+ const options = node.props?.['options'] ?? [];
721
+ const qname = qualifiedName(schema, name);
722
+ const ifNotExistsDoc = ifNotExists ? [makeKeyword('IF NOT EXISTS'), ' '] : '';
723
+ const parts = [[makeKeyword('CREATE SEQUENCE'), ' ', ifNotExistsDoc, qname]];
724
+ for (const opt of options)
725
+ parts.push(makeKeyword(opt));
726
+ return [join(hardline, parts), ';'];
727
+ }
728
+ function printAlterSequence(node, opts) {
729
+ const makeKeyword = (k) => keyword(k, opts);
730
+ const schema = propStr(node, 'schema');
731
+ const name = propStr(node, 'name') ?? '';
732
+ const options = node.props?.['options'] ?? [];
733
+ const qname = qualifiedName(schema, name);
734
+ const parts = [[makeKeyword('ALTER SEQUENCE'), ' ', qname]];
735
+ for (const opt of options)
736
+ parts.push(makeKeyword(opt));
737
+ return [join(hardline, parts), ';'];
738
+ }
739
+ // ---------------------------------------------------------------------------
740
+ // CREATE SCHEMA
741
+ // ---------------------------------------------------------------------------
742
+ function printCreateSchema(node, opts) {
743
+ const makeKeyword = (k) => keyword(k, opts);
744
+ const name = propStr(node, 'name') ?? '';
745
+ const authRole = propStr(node, 'authRole');
746
+ const ifNotExists = propBool(node, 'ifNotExists');
747
+ const ifNotExistsDoc = ifNotExists ? [makeKeyword('IF NOT EXISTS'), ' '] : '';
748
+ const authDoc = authRole ? [' ', makeKeyword('AUTHORIZATION'), ' ', authRole] : '';
749
+ return [[makeKeyword('CREATE SCHEMA'), ' ', ifNotExistsDoc, name, authDoc], ';'];
750
+ }
751
+ // ---------------------------------------------------------------------------
752
+ // CREATE EXTENSION
753
+ // ---------------------------------------------------------------------------
754
+ function printCreateExtension(node, opts) {
755
+ const makeKeyword = (k) => keyword(k, opts);
756
+ const name = propStr(node, 'name') ?? '';
757
+ const ifNotExists = propBool(node, 'ifNotExists');
758
+ const schema = propStr(node, 'schema');
759
+ const version = propStr(node, 'version');
760
+ const ifNotExistsDoc = ifNotExists ? [makeKeyword('IF NOT EXISTS'), ' '] : '';
761
+ const schemaDoc = schema ? [hardline, makeKeyword('SCHEMA'), ' ', schema] : '';
762
+ const versionDoc = version ? [hardline, makeKeyword('VERSION'), ' ', `'${version}'`] : '';
763
+ return [[makeKeyword('CREATE EXTENSION'), ' ', ifNotExistsDoc, `"${name}"`, schemaDoc, versionDoc], ';'];
764
+ }
765
+ // ---------------------------------------------------------------------------
766
+ // CREATE TABLE AS / CREATE MATERIALIZED VIEW
767
+ // ---------------------------------------------------------------------------
768
+ function printCreateTableAs(node, opts) {
769
+ return printCreateAsQuery(node, opts, 'CREATE TABLE');
770
+ }
771
+ function printCreateMatView(node, opts) {
772
+ return printCreateAsQuery(node, opts, 'CREATE MATERIALIZED VIEW');
773
+ }
774
+ function printCreateAsQuery(node, opts, kw) {
775
+ const makeKeyword = (k) => keyword(k, opts);
776
+ const schema = propStr(node, 'schema');
777
+ const name = propStr(node, 'name') ?? '';
778
+ const ifNotExists = propBool(node, 'ifNotExists');
779
+ const query = prop(node, 'query');
780
+ const qname = qualifiedName(schema, name);
781
+ const ifNotExistsDoc = ifNotExists ? [makeKeyword('IF NOT EXISTS'), ' '] : '';
782
+ return [
783
+ makeKeyword(kw), ' ', ifNotExistsDoc, qname, ' ', makeKeyword('AS'),
784
+ hardline, query ? printQueryExpr(query, opts) : '',
785
+ ';',
786
+ ];
787
+ }
788
+ // ---------------------------------------------------------------------------
789
+ // CREATE TRIGGER
790
+ // ---------------------------------------------------------------------------
791
+ function printCreateTrigger(node, opts) {
792
+ const makeKeyword = (k) => keyword(k, opts);
793
+ const printNode = printWith(opts);
794
+ const name = propStr(node, 'name') ?? '';
795
+ const timing = propStr(node, 'timing') ?? 'AFTER';
796
+ const events = node.props?.['events'] ?? [];
797
+ const relation = prop(node, 'relation');
798
+ const forEach = propStr(node, 'forEach') ?? 'ROW';
799
+ const funcName = propStr(node, 'funcName') ?? '';
800
+ const when = prop(node, 'when');
801
+ const eventDoc = join([' ', makeKeyword('OR'), ' '], events.map(makeKeyword));
802
+ const whenDoc = when ? [hardline, makeKeyword('WHEN'), ' (', printNode(when), ')'] : '';
803
+ return [
804
+ makeKeyword('CREATE TRIGGER'), ' ', name,
805
+ hardline, makeKeyword(timing), ' ', eventDoc, ' ', makeKeyword('ON'), ' ', rangeVarName(relation),
806
+ hardline, makeKeyword(`FOR EACH ${forEach}`),
807
+ whenDoc,
808
+ hardline, makeKeyword('EXECUTE FUNCTION'), ' ', funcName, '()',
809
+ ';',
810
+ ];
811
+ }
812
+ // ---------------------------------------------------------------------------
813
+ // COMMENT ON
814
+ // ---------------------------------------------------------------------------
815
+ function printComment(node, opts) {
816
+ const makeKeyword = (k) => keyword(k, opts);
817
+ const objtype = propStr(node, 'objtype') ?? '';
818
+ const object = propStr(node, 'object') ?? '';
819
+ const comment = propStr(node, 'comment');
820
+ const commentVal = comment != null ? `'${comment.replace(/'/g, "''")}'` : makeKeyword('NULL');
821
+ return [[makeKeyword('COMMENT ON'), ' ', makeKeyword(objtype), ' ', object, ' ', makeKeyword('IS'), ' ', commentVal], ';'];
822
+ }
823
+ // ---------------------------------------------------------------------------
824
+ // Transaction control
825
+ // ---------------------------------------------------------------------------
826
+ function printTransaction(node, opts) {
827
+ const makeKeyword = (k) => keyword(k, opts);
828
+ const kind = propStr(node, 'kind') ?? 'COMMIT';
829
+ const savepoint = propStr(node, 'savepoint');
830
+ const gid = propStr(node, 'gid');
831
+ const options = node.props?.['options'] ?? [];
832
+ const parts = [makeKeyword(kind)];
833
+ // SAVEPOINT, RELEASE SAVEPOINT, ROLLBACK TO SAVEPOINT all take a savepoint name
834
+ if (kind === 'RELEASE')
835
+ parts.push(' ', makeKeyword('SAVEPOINT'));
836
+ if (kind === 'ROLLBACK TO')
837
+ parts.push(' ', makeKeyword('SAVEPOINT'));
838
+ if (savepoint)
839
+ parts.push(' ', savepoint);
840
+ if (gid)
841
+ parts.push(' ', `'${gid}'`);
842
+ if (options.length > 0)
843
+ parts.push(' ', join(', ', options.map((o) => makeKeyword(o))));
844
+ return [parts, ';'];
845
+ }
846
+ // ---------------------------------------------------------------------------
847
+ // CALL
848
+ // ---------------------------------------------------------------------------
849
+ function printCall(node, opts) {
850
+ const makeKeyword = (k) => keyword(k, opts);
851
+ const printNode = printWith(opts);
852
+ const call = prop(node, 'call');
853
+ return [[makeKeyword('CALL'), ' ', call ? printNode(call) : ''], ';'];
854
+ }
855
+ // ---------------------------------------------------------------------------
856
+ // DO
857
+ // ---------------------------------------------------------------------------
858
+ function printDo(node, opts) {
859
+ const makeKeyword = (k) => keyword(k, opts);
860
+ const language = propStr(node, 'language') ?? 'plpgsql';
861
+ const body = propStr(node, 'body') ?? '';
862
+ // Standard convention: body first, LANGUAGE after
863
+ return [[makeKeyword('DO'), ' ', '$$', body, '$$', hardline, makeKeyword('LANGUAGE'), ' ', language], ';'];
864
+ }
865
+ // ---------------------------------------------------------------------------
866
+ // MERGE
867
+ // ---------------------------------------------------------------------------
868
+ function printMerge(node, opts) {
869
+ const makeKeyword = (k) => keyword(k, opts);
870
+ const printNode = printWith(opts);
871
+ const target = prop(node, 'target');
872
+ const source = prop(node, 'source');
873
+ const on = prop(node, 'on');
874
+ const whens = propArr(node, 'whens');
875
+ const returning = propArr(node, 'returning');
876
+ const ctes = prop(node, 'ctes');
877
+ const parts = [];
878
+ if (ctes)
879
+ parts.push(...printCtes(ctes, opts, printNode));
880
+ parts.push([makeKeyword('MERGE INTO'), ' ', target ? printNode(target) : '']);
881
+ parts.push([makeKeyword('USING'), ' ', source ? printNode(source) : '']);
882
+ parts.push([makeKeyword('ON'), ' ', on ? printNode(on) : '']);
883
+ for (const w of whens) {
884
+ const matchKind = propStr(w, 'matchKind') ?? 'MATCHED';
885
+ const cmd = propStr(w, 'cmd') ?? 'DO NOTHING';
886
+ const condition = prop(w, 'condition');
887
+ const targets = propArr(w, 'targets');
888
+ const values = propArr(w, 'values');
889
+ let whenLine = [makeKeyword('WHEN'), ' ', makeKeyword(matchKind)];
890
+ if (condition)
891
+ whenLine = [whenLine, ' ', makeKeyword('AND'), ' ', printNode(condition)];
892
+ whenLine = [whenLine, ' ', makeKeyword('THEN')];
893
+ let actionDoc;
894
+ if (cmd === 'DO NOTHING') {
895
+ actionDoc = makeKeyword('DO NOTHING');
896
+ }
897
+ else if (cmd === 'DELETE') {
898
+ actionDoc = makeKeyword('DELETE');
899
+ }
900
+ else if (cmd === 'UPDATE') {
901
+ // ResTarget: name=column, val=value → "col = val"
902
+ const assignments = targets.map((t) => {
903
+ const column = propStr(t, 'name') ?? '';
904
+ const val = prop(t, 'val');
905
+ return [column, ' = ', val ? printNode(val) : ''];
906
+ });
907
+ actionDoc = [makeKeyword('UPDATE SET'), indent([hardline, join(hardSep(opts), assignments)])];
908
+ }
909
+ else { // INSERT
910
+ const cols = targets.filter((t) => t.type === 'ResTarget').map((t) => propStr(t, 'name') ?? '');
911
+ const colList = cols.length > 0 ? [' (', join(', ', cols), ')'] : '';
912
+ const valList = values.length > 0
913
+ ? [makeKeyword('VALUES'), ' (', join(', ', values.map(printNode)), ')']
914
+ : [makeKeyword('DEFAULT VALUES')];
915
+ actionDoc = [makeKeyword('INSERT'), colList, hardline, valList];
916
+ }
917
+ parts.push([whenLine, indent([hardline, actionDoc])]);
918
+ }
919
+ if (returning.length > 0) {
920
+ parts.push(printListClause('RETURNING', returning, opts, printNode));
921
+ }
922
+ return [join(hardline, parts), ';'];
923
+ }
924
+ // ---------------------------------------------------------------------------
925
+ // ALTER FUNCTION / PROCEDURE
926
+ // ---------------------------------------------------------------------------
927
+ function printAlterFunction(node, opts) {
928
+ const makeKeyword = (k) => keyword(k, opts);
929
+ const name = propStr(node, 'name') ?? '';
930
+ const argTypes = node.props?.['argTypes'] ?? [];
931
+ const rename = propStr(node, 'rename');
932
+ const options = node.props?.['options'] ?? [];
933
+ const argList = argTypes.length > 0
934
+ ? ['(', join(', ', argTypes.map((t) => makeKeyword(t))), ')']
935
+ : '()';
936
+ if (rename) {
937
+ return [[makeKeyword('ALTER FUNCTION'), ' ', name, argList, ' ', makeKeyword('RENAME TO'), ' ', rename], ';'];
938
+ }
939
+ const optionDocs = options.map((o) => {
940
+ switch (o.name) {
941
+ case 'volatility': return makeKeyword(o.value ?? '');
942
+ case 'cost': return [makeKeyword('COST'), ' ', o.value ?? ''];
943
+ case 'rows': return [makeKeyword('ROWS'), ' ', o.value ?? ''];
944
+ case 'called': return makeKeyword(o.value === 'true' ? 'CALLED ON NULL INPUT' : 'STRICT');
945
+ case 'security': return makeKeyword(o.value === 'true' ? 'SECURITY DEFINER' : 'SECURITY INVOKER');
946
+ default: return [makeKeyword('SET'), ' ', o.name, ' = ', o.value ?? ''];
947
+ }
948
+ });
949
+ return [[makeKeyword('ALTER FUNCTION'), ' ', name, argList, indent([hardline, join(hardline, optionDocs)])], ';'];
950
+ }
951
+ // ---------------------------------------------------------------------------
952
+ // ALTER OWNER / ALTER ... SET SCHEMA
953
+ // ---------------------------------------------------------------------------
954
+ function printAlterOwner(node, opts) {
955
+ const makeKeyword = (k) => keyword(k, opts);
956
+ const objType = propStr(node, 'objType') ?? '';
957
+ const name = propStr(node, 'name') ?? '';
958
+ const newOwner = propStr(node, 'newOwner') ?? '';
959
+ return [[makeKeyword(`ALTER ${objType}`), ' ', name, ' ', makeKeyword('OWNER TO'), ' ', newOwner], ';'];
960
+ }
961
+ function printAlterObjectSchema(node, opts) {
962
+ const makeKeyword = (k) => keyword(k, opts);
963
+ const objType = propStr(node, 'objType') ?? '';
964
+ const name = propStr(node, 'name') ?? '';
965
+ const newSchema = propStr(node, 'newSchema') ?? '';
966
+ return [[makeKeyword(`ALTER ${objType}`), ' ', name, ' ', makeKeyword('SET SCHEMA'), ' ', newSchema], ';'];
967
+ }
968
+ // ---------------------------------------------------------------------------
969
+ // REFRESH MATERIALIZED VIEW
970
+ // ---------------------------------------------------------------------------
971
+ function printRefreshMatView(node, opts) {
972
+ const makeKeyword = (k) => keyword(k, opts);
973
+ const name = prop(node, 'name');
974
+ const concurrent = propBool(node, 'concurrent');
975
+ return [
976
+ makeKeyword('REFRESH MATERIALIZED VIEW'),
977
+ concurrent ? [' ', makeKeyword('CONCURRENTLY')] : '',
978
+ ' ', rangeVarName(name),
979
+ ';',
980
+ ];
981
+ }
982
+ // ---------------------------------------------------------------------------
983
+ // SELECT INTO
984
+ // ---------------------------------------------------------------------------
985
+ function printSelectInto(node, opts) {
986
+ const makeKeyword = (k) => keyword(k, opts);
987
+ const printNode = printWith(opts);
988
+ const temp = propBool(node, 'temp');
989
+ const into = prop(node, 'into');
990
+ const targets = propArr(node, 'targetList');
991
+ const from = propArr(node, 'from');
992
+ const where = prop(node, 'where');
993
+ const groupBy = propArr(node, 'groupBy');
994
+ const having = prop(node, 'having');
995
+ const orderBy = propArr(node, 'orderBy');
996
+ const limit = prop(node, 'limit');
997
+ const offset = prop(node, 'offset');
998
+ const parts = [];
999
+ parts.push(printListClause('SELECT', targets, opts, printNode));
1000
+ const intoKw = temp ? [makeKeyword('INTO'), ' ', makeKeyword('TEMP')] : makeKeyword('INTO');
1001
+ parts.push([intoKw, indent([hardline, rangeVarName(into)])]);
1002
+ if (from.length > 0) {
1003
+ parts.push(printFromClause(from, opts, printNode));
1004
+ }
1005
+ if (where)
1006
+ parts.push(printBoolClause('WHERE', where, opts, printNode));
1007
+ if (groupBy.length > 0)
1008
+ parts.push(printListClause('GROUP BY', groupBy, opts, printNode));
1009
+ if (having)
1010
+ parts.push(printBoolClause('HAVING', having, opts, printNode));
1011
+ if (orderBy.length > 0)
1012
+ parts.push(printListClause('ORDER BY', orderBy, opts, printNode));
1013
+ if (limit)
1014
+ parts.push([makeKeyword('LIMIT'), ' ', printNode(limit)]);
1015
+ if (offset)
1016
+ parts.push([makeKeyword('OFFSET'), ' ', printNode(offset)]);
1017
+ return [join(hardline, parts), ';'];
1018
+ }
1019
+ // ---------------------------------------------------------------------------
1020
+ // CREATE RULE
1021
+ // ---------------------------------------------------------------------------
1022
+ function printRule(node, opts) {
1023
+ const makeKeyword = (k) => keyword(k, opts);
1024
+ const printNode = printWith(opts);
1025
+ const ruleName = propStr(node, 'ruleName') ?? '';
1026
+ const relation = prop(node, 'relation');
1027
+ const event = propStr(node, 'event') ?? 'SELECT';
1028
+ const instead = propBool(node, 'instead');
1029
+ const doInstead = propBool(node, 'doInstead');
1030
+ const where = prop(node, 'where');
1031
+ const actions = propArr(node, 'actions');
1032
+ const parts = [];
1033
+ parts.push([makeKeyword('CREATE RULE'), ' ', ruleName]);
1034
+ parts.push([makeKeyword('AS ON'), ' ', makeKeyword(event)]);
1035
+ parts.push([makeKeyword('TO'), ' ', rangeVarName(relation)]);
1036
+ if (where)
1037
+ parts.push(printBoolClause('WHERE', where, opts, printNode));
1038
+ const doKw = instead ? makeKeyword('DO INSTEAD') : doInstead ? makeKeyword('DO ALSO') : makeKeyword('DO ALSO');
1039
+ if (actions.length === 0) {
1040
+ parts.push([doKw, ' ', makeKeyword('NOTHING')]);
1041
+ }
1042
+ else if (actions.length === 1) {
1043
+ const action = actions[0];
1044
+ if (action.type === 'NothingStmt') {
1045
+ parts.push([doKw, ' ', makeKeyword('NOTHING')]);
1046
+ }
1047
+ else {
1048
+ parts.push([doKw, indent([hardline, printQueryExpr(action, opts)])]);
1049
+ }
1050
+ }
1051
+ else {
1052
+ const actionDocs = actions.map((a) => a.type === 'NothingStmt' ? makeKeyword('NOTHING') : printQueryExpr(a, opts));
1053
+ parts.push([doKw, ' (', indent([hardline, join([';', hardline], actionDocs)]), hardline, ')']);
1054
+ }
1055
+ return [join(hardline, parts), ';'];
1056
+ }
1057
+ // ---------------------------------------------------------------------------
1058
+ // Row Security Policies
1059
+ // ---------------------------------------------------------------------------
1060
+ function printCreatePolicy(node, opts) {
1061
+ const makeKeyword = (k) => keyword(k, opts);
1062
+ const printNode = printWith(opts);
1063
+ const policyName = propStr(node, 'policyName') ?? '';
1064
+ const table = prop(node, 'table');
1065
+ const cmdName = propStr(node, 'cmdName');
1066
+ const permissive = node.props?.['permissive']; // false = RESTRICTIVE, null = PERMISSIVE (default)
1067
+ const using = prop(node, 'using');
1068
+ const withCheck = prop(node, 'withCheck');
1069
+ const parts = [];
1070
+ parts.push([makeKeyword('CREATE POLICY'), ' ', policyName]);
1071
+ const onPart = permissive === false
1072
+ ? [makeKeyword('AS'), ' ', makeKeyword('RESTRICTIVE'), ' ', makeKeyword('ON'), ' ', rangeVarName(table)]
1073
+ : [makeKeyword('ON'), ' ', rangeVarName(table)];
1074
+ parts.push(onPart);
1075
+ if (cmdName)
1076
+ parts.push([makeKeyword('FOR'), ' ', makeKeyword(cmdName)]);
1077
+ if (using)
1078
+ parts.push([makeKeyword('USING'), ' (', printNode(using), ')']);
1079
+ if (withCheck)
1080
+ parts.push([makeKeyword('WITH CHECK'), ' (', printNode(withCheck), ')']);
1081
+ return [join(hardline, parts), ';'];
1082
+ }
1083
+ function printAlterPolicy(node, opts) {
1084
+ const makeKeyword = (k) => keyword(k, opts);
1085
+ const printNode = printWith(opts);
1086
+ const policyName = propStr(node, 'policyName') ?? '';
1087
+ const table = prop(node, 'table');
1088
+ const using = prop(node, 'using');
1089
+ const withCheck = prop(node, 'withCheck');
1090
+ const parts = [];
1091
+ parts.push([makeKeyword('ALTER POLICY'), ' ', policyName]);
1092
+ parts.push([makeKeyword('ON'), ' ', rangeVarName(table)]);
1093
+ if (using)
1094
+ parts.push([makeKeyword('USING'), ' (', printNode(using), ')']);
1095
+ if (withCheck)
1096
+ parts.push([makeKeyword('WITH CHECK'), ' (', printNode(withCheck), ')']);
1097
+ return [join(hardline, parts), ';'];
1098
+ }
1099
+ // ---------------------------------------------------------------------------
1100
+ // Cursors
1101
+ // ---------------------------------------------------------------------------
1102
+ function printDeclareCursor(node, opts) {
1103
+ const makeKeyword = (k) => keyword(k, opts);
1104
+ const name = propStr(node, 'name') ?? '';
1105
+ const scroll = propBool(node, 'scroll');
1106
+ const noScroll = propBool(node, 'noScroll');
1107
+ const insensitive = propBool(node, 'insensitive');
1108
+ const binary = propBool(node, 'binary');
1109
+ const query = prop(node, 'query');
1110
+ const scrollKw = noScroll ? [' ', makeKeyword('NO SCROLL')] : scroll ? [' ', makeKeyword('SCROLL')] : '';
1111
+ const binaryKw = binary ? [makeKeyword('BINARY'), ' '] : '';
1112
+ const insensKw = insensitive ? [makeKeyword('INSENSITIVE'), ' '] : '';
1113
+ return [
1114
+ [makeKeyword('DECLARE'), ' ', name, scrollKw, ' ', insensKw, binaryKw, makeKeyword('CURSOR'), ' ', makeKeyword('FOR')],
1115
+ hardline,
1116
+ query ? printQueryExpr(query, opts) : '',
1117
+ ';',
1118
+ ];
1119
+ }
1120
+ function printFetch(node, opts) {
1121
+ const makeKeyword = (k) => keyword(k, opts);
1122
+ const direction = propStr(node, 'direction') ?? 'NEXT';
1123
+ const count = node.props?.['count'];
1124
+ const cursor = propStr(node, 'cursor') ?? '';
1125
+ const isMove = propBool(node, 'isMove');
1126
+ const verb = isMove ? makeKeyword('MOVE') : makeKeyword('FETCH');
1127
+ let dirDoc;
1128
+ if (count !== undefined && count !== null && direction !== 'ALL') {
1129
+ // FETCH FORWARD 10 / FETCH ABSOLUTE 5 etc.
1130
+ dirDoc = [makeKeyword(direction), ' ', String(count)];
1131
+ }
1132
+ else {
1133
+ dirDoc = makeKeyword(direction);
1134
+ }
1135
+ return [[verb, ' ', dirDoc, ' ', makeKeyword('FROM'), ' ', cursor], ';'];
1136
+ }
1137
+ function printClosePortal(node, opts) {
1138
+ const makeKeyword = (k) => keyword(k, opts);
1139
+ const cursor = propStr(node, 'cursor');
1140
+ return [[makeKeyword('CLOSE'), ' ', cursor ?? makeKeyword('ALL')], ';'];
1141
+ }
1142
+ // ---------------------------------------------------------------------------
1143
+ // COPY
1144
+ // ---------------------------------------------------------------------------
1145
+ function printCopy(node, opts) {
1146
+ const makeKeyword = (k) => keyword(k, opts);
1147
+ const relation = prop(node, 'relation');
1148
+ const query = prop(node, 'query');
1149
+ const columns = node.props?.['columns'] ?? [];
1150
+ const isFrom = propBool(node, 'isFrom');
1151
+ const isProgram = propBool(node, 'isProgram');
1152
+ const filename = propStr(node, 'filename');
1153
+ const options = node.props?.['options'] ?? [];
1154
+ const colsPart = columns.length > 0 ? [' (', join(', ', columns), ')'] : '';
1155
+ let sourceDest;
1156
+ if (relation) {
1157
+ sourceDest = [rangeVarName(relation), colsPart];
1158
+ }
1159
+ else if (query) {
1160
+ sourceDest = ['(', indent([hardline, printQueryExpr(query, opts)]), hardline, ')'];
1161
+ }
1162
+ else {
1163
+ sourceDest = '';
1164
+ }
1165
+ const dirKw = isFrom ? makeKeyword('FROM') : makeKeyword('TO');
1166
+ let dest;
1167
+ if (isProgram && filename) {
1168
+ dest = [makeKeyword('PROGRAM'), ' ', `'${filename}'`];
1169
+ }
1170
+ else if (filename) {
1171
+ dest = `'${filename}'`;
1172
+ }
1173
+ else {
1174
+ dest = makeKeyword('STDOUT');
1175
+ }
1176
+ let optionPart = '';
1177
+ if (options.length > 0) {
1178
+ const optDocs = options.map((o) => {
1179
+ const val = o.value ?? '';
1180
+ // Quote if not already quoted, not a boolean, and not a plain identifier
1181
+ const fmtVal = val.startsWith("'") || val === 'true' || val === 'false'
1182
+ ? val
1183
+ : /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(val) ? val : `'${val.replace(/'/g, "''")}'`;
1184
+ return [makeKeyword(o.name.toUpperCase()), ' ', fmtVal];
1185
+ });
1186
+ optionPart = [' (', join(', ', optDocs), ')'];
1187
+ }
1188
+ return [[makeKeyword('COPY'), ' ', sourceDest, ' ', dirKw, ' ', dest, optionPart], ';'];
1189
+ }
1190
+ // ---------------------------------------------------------------------------
1191
+ // EXPLAIN
1192
+ // ---------------------------------------------------------------------------
1193
+ function printExplain(node, opts) {
1194
+ const makeKeyword = (k) => keyword(k, opts);
1195
+ const query = prop(node, 'query');
1196
+ const options = node.props?.['options'] ?? [];
1197
+ // Simple cases: no options at all, or just ANALYZE
1198
+ const analyzeOnly = options.length === 1 && options[0].name === 'analyze';
1199
+ const verboseOnly = options.length === 1 && options[0].name === 'verbose';
1200
+ if (options.length === 0) {
1201
+ return [[makeKeyword('EXPLAIN'), ' ', query ? printQueryExpr(query, opts) : ''], ';'];
1202
+ }
1203
+ if (analyzeOnly) {
1204
+ return [[makeKeyword('EXPLAIN'), ' ', makeKeyword('ANALYZE'), ' ', query ? printQueryExpr(query, opts) : ''], ';'];
1205
+ }
1206
+ if (verboseOnly) {
1207
+ return [[makeKeyword('EXPLAIN'), ' ', makeKeyword('VERBOSE'), ' ', query ? printQueryExpr(query, opts) : ''], ';'];
1208
+ }
1209
+ const optDocs = options.map((o) => {
1210
+ const val = o.value === 'true' ? makeKeyword('true') : o.value === 'false' ? makeKeyword('false') : o.value;
1211
+ return [makeKeyword(o.name.toUpperCase()), ' ', val];
1212
+ });
1213
+ return [[makeKeyword('EXPLAIN'), ' (', join(', ', optDocs), ') ', query ? printQueryExpr(query, opts) : ''], ';'];
1214
+ }
1215
+ // ---------------------------------------------------------------------------
1216
+ // PREPARE / EXECUTE / DEALLOCATE
1217
+ // ---------------------------------------------------------------------------
1218
+ function printPrepare(node, opts) {
1219
+ const makeKeyword = (k) => keyword(k, opts);
1220
+ const name = propStr(node, 'name') ?? '';
1221
+ const argTypes = node.props?.['argTypes'] ?? [];
1222
+ const query = prop(node, 'query');
1223
+ const argsPart = argTypes.length > 0
1224
+ ? ['(', join(', ', argTypes.map((t) => makeKeyword(t))), ')']
1225
+ : '';
1226
+ return [
1227
+ [makeKeyword('PREPARE'), ' ', name, argsPart, ' ', makeKeyword('AS')],
1228
+ hardline,
1229
+ query ? printQueryExpr(query, opts) : '',
1230
+ ';',
1231
+ ];
1232
+ }
1233
+ function printExecute(node, opts) {
1234
+ const makeKeyword = (k) => keyword(k, opts);
1235
+ const printNode = printWith(opts);
1236
+ const name = propStr(node, 'name') ?? '';
1237
+ const params = propArr(node, 'params');
1238
+ const paramsPart = params.length > 0
1239
+ ? ['(', join(', ', params.map(printNode)), ')']
1240
+ : '';
1241
+ return [[makeKeyword('EXECUTE'), ' ', name, paramsPart], ';'];
1242
+ }
1243
+ function printDeallocate(node, opts) {
1244
+ const makeKeyword = (k) => keyword(k, opts);
1245
+ const name = propStr(node, 'name');
1246
+ return [[makeKeyword('DEALLOCATE'), ' ', name ? name : makeKeyword('ALL')], ';'];
1247
+ }
1248
+ // ---------------------------------------------------------------------------
1249
+ // LISTEN / UNLISTEN / NOTIFY
1250
+ // ---------------------------------------------------------------------------
1251
+ function printListen(node, opts) {
1252
+ const makeKeyword = (k) => keyword(k, opts);
1253
+ const channel = propStr(node, 'channel') ?? '';
1254
+ return [[makeKeyword('LISTEN'), ' ', channel], ';'];
1255
+ }
1256
+ function printUnlisten(node, opts) {
1257
+ const makeKeyword = (k) => keyword(k, opts);
1258
+ const channel = propStr(node, 'channel');
1259
+ return [[makeKeyword('UNLISTEN'), ' ', channel ? channel : '*'], ';'];
1260
+ }
1261
+ function printNotify(node, opts) {
1262
+ const makeKeyword = (k) => keyword(k, opts);
1263
+ const channel = propStr(node, 'channel') ?? '';
1264
+ const payload = propStr(node, 'payload');
1265
+ const payloadPart = payload ? [', ', `'${payload}'`] : '';
1266
+ return [[makeKeyword('NOTIFY'), ' ', channel, payloadPart], ';'];
1267
+ }
1268
+ // ---------------------------------------------------------------------------
1269
+ // LOCK TABLE
1270
+ // ---------------------------------------------------------------------------
1271
+ function printLockTable(node, opts) {
1272
+ const makeKeyword = (k) => keyword(k, opts);
1273
+ const relations = propArr(node, 'relations');
1274
+ const mode = propStr(node, 'mode') ?? 'ACCESS EXCLUSIVE';
1275
+ const nowait = propBool(node, 'nowait');
1276
+ return [
1277
+ makeKeyword('LOCK TABLE'), ' ', join(', ', relations.map(rangeVarName)),
1278
+ ' ', makeKeyword('IN'), ' ', makeKeyword(mode), ' ', makeKeyword('MODE'),
1279
+ nowait ? [' ', makeKeyword('NOWAIT')] : '',
1280
+ ';',
1281
+ ];
1282
+ }
1283
+ // ---------------------------------------------------------------------------
1284
+ // P4: CREATE TABLE PARTITION OF
1285
+ // ---------------------------------------------------------------------------
1286
+ function printCreateTablePartitionOf(node, opts) {
1287
+ const makeKeyword = (k) => keyword(k, opts);
1288
+ const name = prop(node, 'name');
1289
+ const parent = prop(node, 'parent');
1290
+ const bound = prop(node, 'bound');
1291
+ let boundDoc = '';
1292
+ if (bound) {
1293
+ const isDefault = propBool(bound, 'isDefault');
1294
+ const lower = bound.props?.['lower'] ?? [];
1295
+ const upper = bound.props?.['upper'] ?? [];
1296
+ const listDatums = bound.props?.['listDatums'] ?? [];
1297
+ const modulus = bound.props?.['modulus'];
1298
+ const remainder = bound.props?.['remainder'];
1299
+ if (isDefault) {
1300
+ boundDoc = [hardline, makeKeyword('DEFAULT')];
1301
+ }
1302
+ else if (lower.length > 0 || upper.length > 0) {
1303
+ boundDoc = [
1304
+ hardline, makeKeyword('FOR VALUES FROM'),
1305
+ ' (', join(', ', lower), ')',
1306
+ ' ', makeKeyword('TO'),
1307
+ ' (', join(', ', upper), ')',
1308
+ ];
1309
+ }
1310
+ else if (listDatums.length > 0) {
1311
+ boundDoc = [hardline, makeKeyword('FOR VALUES IN'), ' (', join(', ', listDatums), ')'];
1312
+ }
1313
+ else if (modulus !== undefined && remainder !== undefined) {
1314
+ boundDoc = [hardline, makeKeyword('FOR VALUES WITH'), ' (', makeKeyword('MODULUS'), ' ', String(modulus), ', ', makeKeyword('REMAINDER'), ' ', String(remainder), ')'];
1315
+ }
1316
+ }
1317
+ return [
1318
+ makeKeyword('CREATE TABLE'), ' ', rangeVarName(name), hardline,
1319
+ indent([makeKeyword('PARTITION OF'), ' ', rangeVarName(parent)]),
1320
+ boundDoc,
1321
+ ';',
1322
+ ];
1323
+ }
1324
+ // ---------------------------------------------------------------------------
1325
+ // P4: VACUUM / ANALYZE / CLUSTER / REINDEX
1326
+ // ---------------------------------------------------------------------------
1327
+ function printVacuum(node, opts) {
1328
+ const makeKeyword = (k) => keyword(k, opts);
1329
+ const isVacuum = propBool(node, 'isVacuum');
1330
+ const options = node.props?.['options'] ?? [];
1331
+ const relations = propArr(node, 'relations');
1332
+ const relDoc = relations.length > 0
1333
+ ? [' ', join(', ', relations.map(rangeVarName))]
1334
+ : '';
1335
+ if (!isVacuum) {
1336
+ return [[makeKeyword('ANALYZE'), relDoc], ';'];
1337
+ }
1338
+ if (options.length === 0) {
1339
+ return [[makeKeyword('VACUUM'), relDoc], ';'];
1340
+ }
1341
+ if (options.length === 1 && (options[0] === 'VERBOSE' || options[0] === 'ANALYZE')) {
1342
+ return [[makeKeyword('VACUUM'), ' ', makeKeyword(options[0]), relDoc], ';'];
1343
+ }
1344
+ return [[makeKeyword('VACUUM'), ' (', join(', ', options.map((o) => makeKeyword(o.toLowerCase()))), ')', relDoc], ';'];
1345
+ }
1346
+ function printCluster(node, opts) {
1347
+ const makeKeyword = (k) => keyword(k, opts);
1348
+ const relation = prop(node, 'relation');
1349
+ const indexName = propStr(node, 'indexName');
1350
+ return [
1351
+ [makeKeyword('CLUSTER'), ' ', rangeVarName(relation),
1352
+ indexName ? [' ', makeKeyword('USING'), ' ', indexName] : ''],
1353
+ ';',
1354
+ ];
1355
+ }
1356
+ function printReindex(node, opts) {
1357
+ const makeKeyword = (k) => keyword(k, opts);
1358
+ const kind = propStr(node, 'kind') ?? 'TABLE';
1359
+ const relation = prop(node, 'relation');
1360
+ const options = node.props?.['options'] ?? [];
1361
+ const optDoc = options.length > 0
1362
+ ? [' (', join(', ', options.map((o) => makeKeyword(o.toLowerCase()))), ')']
1363
+ : '';
1364
+ return [
1365
+ [makeKeyword('REINDEX'), optDoc, ' ', makeKeyword(kind), ' ', rangeVarName(relation)],
1366
+ ';',
1367
+ ];
1368
+ }
1369
+ // ---------------------------------------------------------------------------
1370
+ // P4: Foreign Data Wrappers
1371
+ // ---------------------------------------------------------------------------
1372
+ function printFdwOptions(node, opts) {
1373
+ const makeKeyword = (k) => keyword(k, opts);
1374
+ const options = propArr(node, 'options');
1375
+ if (options.length === 0)
1376
+ return '';
1377
+ const pairs = options.map((o) => {
1378
+ const key = propStr(o, 'key') ?? '';
1379
+ const val = propStr(o, 'val') ?? '';
1380
+ return [key, " '", val, "'"].join('');
1381
+ });
1382
+ return [' ', makeKeyword('OPTIONS'), ' (', join(', ', pairs), ')'];
1383
+ }
1384
+ function printCreateForeignServer(node, opts) {
1385
+ const makeKeyword = (k) => keyword(k, opts);
1386
+ const name = propStr(node, 'name') ?? '';
1387
+ const fdwName = propStr(node, 'fdwName') ?? '';
1388
+ return [
1389
+ [makeKeyword('CREATE SERVER'), ' ', name, hardline,
1390
+ indent([makeKeyword('FOREIGN DATA WRAPPER'), ' ', fdwName]),
1391
+ printFdwOptions(node, opts)],
1392
+ ';',
1393
+ ];
1394
+ }
1395
+ function printCreateForeignTable(node, opts) {
1396
+ const makeKeyword = (k) => keyword(k, opts);
1397
+ const printNode = printWith(opts);
1398
+ const name = prop(node, 'name');
1399
+ const columns = propArr(node, 'columns');
1400
+ const serverName = propStr(node, 'serverName') ?? '';
1401
+ return [
1402
+ makeKeyword('CREATE FOREIGN TABLE'), ' ', rangeVarName(name), ' (',
1403
+ indent([hardline, join([',', hardline], columns.map(printNode))]),
1404
+ hardline, ')',
1405
+ hardline, indent([makeKeyword('SERVER'), ' ', serverName]),
1406
+ printFdwOptions(node, opts),
1407
+ ';',
1408
+ ];
1409
+ }
1410
+ function printCreateUserMapping(node, opts) {
1411
+ const makeKeyword = (k) => keyword(k, opts);
1412
+ const user = propStr(node, 'user') ?? '';
1413
+ const serverName = propStr(node, 'serverName') ?? '';
1414
+ return [
1415
+ [makeKeyword('CREATE USER MAPPING FOR'), ' ', makeKeyword(user), hardline,
1416
+ indent([makeKeyword('SERVER'), ' ', serverName]),
1417
+ printFdwOptions(node, opts)],
1418
+ ';',
1419
+ ];
1420
+ }
1421
+ function printImportForeignSchema(node, opts) {
1422
+ const makeKeyword = (k) => keyword(k, opts);
1423
+ const remoteSchema = propStr(node, 'remoteSchema') ?? '';
1424
+ const serverName = propStr(node, 'serverName') ?? '';
1425
+ const localSchema = propStr(node, 'localSchema') ?? '';
1426
+ return [
1427
+ [makeKeyword('IMPORT FOREIGN SCHEMA'), ' ', remoteSchema, hardline,
1428
+ makeKeyword('FROM SERVER'), ' ', serverName, hardline,
1429
+ makeKeyword('INTO'), ' ', localSchema],
1430
+ ';',
1431
+ ];
1432
+ }
1433
+ // ---------------------------------------------------------------------------
1434
+ // P4: Logical Replication
1435
+ // ---------------------------------------------------------------------------
1436
+ function printCreatePublication(node, opts) {
1437
+ const makeKeyword = (k) => keyword(k, opts);
1438
+ const name = propStr(node, 'name') ?? '';
1439
+ const tables = propArr(node, 'tables');
1440
+ const forAll = propBool(node, 'forAllTables');
1441
+ let forPart;
1442
+ if (forAll) {
1443
+ forPart = [' ', makeKeyword('FOR ALL TABLES')];
1444
+ }
1445
+ else if (tables.length > 0) {
1446
+ forPart = [hardline, indent([makeKeyword('FOR TABLE'), ' ', join(', ', tables.map(rangeVarName))])];
1447
+ }
1448
+ else {
1449
+ forPart = '';
1450
+ }
1451
+ return [[makeKeyword('CREATE PUBLICATION'), ' ', name, forPart], ';'];
1452
+ }
1453
+ function printCreateSubscription(node, opts) {
1454
+ const makeKeyword = (k) => keyword(k, opts);
1455
+ const name = propStr(node, 'name') ?? '';
1456
+ const conninfo = propStr(node, 'conninfo') ?? '';
1457
+ const publications = node.props?.['publications'] ?? [];
1458
+ return [
1459
+ [makeKeyword('CREATE SUBSCRIPTION'), ' ', name, hardline,
1460
+ indent([makeKeyword('CONNECTION'), " '", conninfo, "'"]), hardline,
1461
+ indent([makeKeyword('PUBLICATION'), ' ', join(', ', publications)])],
1462
+ ';',
1463
+ ];
1464
+ }
1465
+ function printDropSubscription(node, opts) {
1466
+ const makeKeyword = (k) => keyword(k, opts);
1467
+ const name = propStr(node, 'name') ?? '';
1468
+ const ifExists = propBool(node, 'ifExists');
1469
+ return [
1470
+ [makeKeyword('DROP SUBSCRIPTION'), ifExists ? [' ', makeKeyword('IF EXISTS')] : '', ' ', name],
1471
+ ';',
1472
+ ];
1473
+ }
1474
+ // ---------------------------------------------------------------------------
1475
+ // P4: CREATE AGGREGATE / OPERATOR / COLLATION
1476
+ // ---------------------------------------------------------------------------
1477
+ function printDefOptions(options, _opts) {
1478
+ return join([',', hardline], options.map((o) => {
1479
+ const key = propStr(o, 'key') ?? '';
1480
+ const val = propStr(o, 'val');
1481
+ return val ? [key, ' = ', val] : [key];
1482
+ }));
1483
+ }
1484
+ function printCreateAggregate(node, opts) {
1485
+ const makeKeyword = (k) => keyword(k, opts);
1486
+ const name = propStr(node, 'name') ?? '';
1487
+ const argTypes = node.props?.['argTypes'] ?? [];
1488
+ const options = propArr(node, 'options');
1489
+ return [
1490
+ makeKeyword('CREATE AGGREGATE'), ' ', name, ' (', join(', ', argTypes.map((t) => makeKeyword(t))), ') (',
1491
+ indent([hardline, printDefOptions(options, opts)]),
1492
+ hardline, ');',
1493
+ ];
1494
+ }
1495
+ function printCreateOperator(node, opts) {
1496
+ const makeKeyword = (k) => keyword(k, opts);
1497
+ const name = propStr(node, 'name') ?? '';
1498
+ const options = propArr(node, 'options');
1499
+ return [
1500
+ makeKeyword('CREATE OPERATOR'), ' ', name, ' (',
1501
+ indent([hardline, printDefOptions(options, opts)]),
1502
+ hardline, ');',
1503
+ ];
1504
+ }
1505
+ function printCreateCollation(node, opts) {
1506
+ const makeKeyword = (k) => keyword(k, opts);
1507
+ const name = propStr(node, 'name') ?? '';
1508
+ const fromName = propStr(node, 'fromName');
1509
+ const options = propArr(node, 'options');
1510
+ if (fromName) {
1511
+ return [[makeKeyword('CREATE COLLATION'), ' ', name, ' ', makeKeyword('FROM'), ' ', `"${fromName}"`], ';'];
1512
+ }
1513
+ return [
1514
+ makeKeyword('CREATE COLLATION'), ' ', name, ' (',
1515
+ printDefOptions(options, opts),
1516
+ ');',
1517
+ ];
1518
+ }
1519
+ // ---------------------------------------------------------------------------
1520
+ // P4: Security Labels
1521
+ // ---------------------------------------------------------------------------
1522
+ function printSecurityLabel(node, opts) {
1523
+ const makeKeyword = (k) => keyword(k, opts);
1524
+ const provider = propStr(node, 'provider') ?? '';
1525
+ const objType = propStr(node, 'objType') ?? 'table';
1526
+ const objName = propStr(node, 'objName') ?? '';
1527
+ const label = propStr(node, 'label') ?? '';
1528
+ return [
1529
+ [makeKeyword('SECURITY LABEL FOR'), ' ', provider, ' ', makeKeyword('ON'), ' ', makeKeyword(objType), ' ', objName, ' ', makeKeyword('IS'), " '", label, "'"],
1530
+ ';',
1531
+ ];
1532
+ }
1533
+ //# sourceMappingURL=statements.js.map