pgsql-deparser 14.0.1 → 14.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.
@@ -5,9 +5,66 @@
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.Deparser = void 0;
8
+ const base_1 = require("./visitors/base");
8
9
  const sql_formatter_1 = require("./utils/sql-formatter");
9
10
  const quote_utils_1 = require("./utils/quote-utils");
10
11
  const list_utils_1 = require("./utils/list-utils");
12
+ /**
13
+ * List of real PostgreSQL built-in types as they appear in pg_catalog.pg_type.typname.
14
+ * These are stored in lowercase in PostgreSQL system catalogs.
15
+ * Use these for lookups, validations, or introspection logic.
16
+ */
17
+ const pgCatalogTypes = [
18
+ // Integers
19
+ 'int2', // smallint
20
+ 'int4', // integer
21
+ 'int8', // bigint
22
+ // Floating-point & numeric
23
+ 'float4', // real
24
+ 'float8', // double precision
25
+ 'numeric', // arbitrary precision (aka "decimal")
26
+ // Text & string
27
+ 'varchar', // variable-length string
28
+ 'char', // internal one-byte type (used in special cases)
29
+ 'bpchar', // blank-padded char(n)
30
+ 'text', // unlimited string
31
+ 'bool', // boolean
32
+ // Dates & times
33
+ 'date', // calendar date
34
+ 'time', // time without time zone
35
+ 'timetz', // time with time zone
36
+ 'timestamp', // timestamp without time zone
37
+ 'timestamptz', // timestamp with time zone
38
+ 'interval', // duration
39
+ // Binary & structured
40
+ 'bytea', // binary data
41
+ 'uuid', // universally unique identifier
42
+ // JSON & XML
43
+ 'json', // textual JSON
44
+ 'jsonb', // binary JSON
45
+ 'xml', // XML format
46
+ // Money & bitstrings
47
+ 'money', // currency value
48
+ 'bit', // fixed-length bit string
49
+ 'varbit', // variable-length bit string
50
+ // Network types
51
+ 'inet', // IPv4 or IPv6 address
52
+ 'cidr', // network address
53
+ 'macaddr', // MAC address (6 bytes)
54
+ 'macaddr8' // MAC address (8 bytes)
55
+ ];
56
+ /**
57
+ * Parser-level type aliases accepted by PostgreSQL SQL syntax,
58
+ * but not present in pg_catalog.pg_type. These are resolved to
59
+ * real types during parsing and never appear in introspection.
60
+ */
61
+ const pgCatalogTypeAliases = [
62
+ ['numeric', ['decimal', 'dec']],
63
+ ['int4', ['int', 'integer']],
64
+ ['float8', ['float']],
65
+ ['bpchar', ['character']],
66
+ ['varchar', ['character varying']]
67
+ ];
11
68
  // Type guards for better type safety
12
69
  function isParseResult(obj) {
13
70
  // A ParseResult is an object that could have stmts (but not required)
@@ -51,11 +108,9 @@ function isWrappedParseResult(obj) {
51
108
  * compatibility and wraps them internally for consistent processing.
52
109
  */
53
110
  class Deparser {
54
- formatter;
55
111
  tree;
56
112
  options;
57
113
  constructor(tree, opts = {}) {
58
- this.formatter = new sql_formatter_1.SqlFormatter(opts.newline, opts.tab, opts.pretty);
59
114
  // Set default options
60
115
  this.options = {
61
116
  functionDelimiter: '$$',
@@ -92,15 +147,17 @@ class Deparser {
92
147
  return new Deparser(query, opts).deparseQuery();
93
148
  }
94
149
  deparseQuery() {
150
+ const formatter = new sql_formatter_1.SqlFormatter(this.options.newline, this.options.tab, this.options.pretty);
151
+ const context = new base_1.DeparserContext({ formatter, prettyMode: this.options.pretty });
95
152
  return this.tree
96
153
  .map(node => {
97
154
  // All nodes should go through the standard deparse method
98
155
  // which will route to the appropriate handler
99
- const result = this.deparse(node);
156
+ const result = this.deparse(node, context);
100
157
  return result || '';
101
158
  })
102
159
  .filter(result => result !== '')
103
- .join(this.formatter.newline() + this.formatter.newline());
160
+ .join(context.newline() + context.newline());
104
161
  }
105
162
  /**
106
163
  * Get the appropriate function delimiter based on the body content
@@ -114,10 +171,14 @@ class Deparser {
114
171
  }
115
172
  return delimiter;
116
173
  }
117
- deparse(node, context = { parentNodeTypes: [] }) {
174
+ deparse(node, context) {
118
175
  if (node == null) {
119
176
  return null;
120
177
  }
178
+ if (!context) {
179
+ const formatter = new sql_formatter_1.SqlFormatter(this.options.newline, this.options.tab, this.options.pretty);
180
+ context = new base_1.DeparserContext({ formatter, prettyMode: this.options.pretty });
181
+ }
121
182
  if (typeof node === 'number' || node instanceof Number) {
122
183
  return node.toString();
123
184
  }
@@ -129,7 +190,11 @@ class Deparser {
129
190
  throw new Error(`Error deparsing ${nodeType}: ${error.message}`);
130
191
  }
131
192
  }
132
- visit(node, context = { parentNodeTypes: [] }) {
193
+ visit(node, context) {
194
+ if (!context) {
195
+ const formatter = new sql_formatter_1.SqlFormatter(this.options.newline, this.options.tab, this.options.pretty);
196
+ context = new base_1.DeparserContext({ formatter, prettyMode: this.options.pretty });
197
+ }
133
198
  const nodeType = this.getNodeType(node);
134
199
  // Handle empty objects
135
200
  if (!nodeType) {
@@ -138,11 +203,7 @@ class Deparser {
138
203
  const nodeData = this.getNodeData(node);
139
204
  const methodName = nodeType;
140
205
  if (typeof this[methodName] === 'function') {
141
- const childContext = {
142
- ...context,
143
- parentNodeTypes: [...context.parentNodeTypes, nodeType]
144
- };
145
- const result = this[methodName](nodeData, childContext);
206
+ const result = this[methodName](nodeData, context);
146
207
  return result;
147
208
  }
148
209
  throw new Error(`Deparser does not handle node type: ${nodeType}`);
@@ -168,7 +229,7 @@ class Deparser {
168
229
  .filter((rawStmt) => rawStmt != null)
169
230
  .map((rawStmt) => this.RawStmt(rawStmt, context))
170
231
  .filter((result) => result !== '')
171
- .join(this.formatter.newline() + this.formatter.newline());
232
+ .join(context.newline() + context.newline());
172
233
  }
173
234
  RawStmt(node, context) {
174
235
  if (!node.stmt) {
@@ -188,7 +249,7 @@ class Deparser {
188
249
  }
189
250
  if (!node.op || node.op === 'SETOP_NONE') {
190
251
  if (node.valuesLists == null) {
191
- if (!this.formatter.isPretty() || !node.targetList) {
252
+ if (!context.isPretty() || !node.targetList) {
192
253
  output.push('SELECT');
193
254
  }
194
255
  }
@@ -208,7 +269,7 @@ class Deparser {
208
269
  (node.rarg).limitOffset ||
209
270
  (node.rarg).withClause);
210
271
  if (leftNeedsParens) {
211
- output.push(this.formatter.parens(leftStmt));
272
+ output.push(context.parens(leftStmt));
212
273
  }
213
274
  else {
214
275
  output.push(leftStmt);
@@ -230,7 +291,7 @@ class Deparser {
230
291
  output.push('ALL');
231
292
  }
232
293
  if (rightNeedsParens) {
233
- output.push(this.formatter.parens(rightStmt));
294
+ output.push(context.parens(rightStmt));
234
295
  }
235
296
  else {
236
297
  output.push(rightStmt);
@@ -242,20 +303,20 @@ class Deparser {
242
303
  const distinctClause = list_utils_1.ListUtils.unwrapList(node.distinctClause);
243
304
  if (distinctClause.length > 0 && Object.keys(distinctClause[0]).length > 0) {
244
305
  const clause = distinctClause
245
- .map(e => this.visit(e, { ...context, select: true }))
306
+ .map(e => this.visit(e, context.spawn('SelectStmt', { select: true })))
246
307
  .join(', ');
247
- distinctPart = ' DISTINCT ON ' + this.formatter.parens(clause);
308
+ distinctPart = ' DISTINCT ON ' + context.parens(clause);
248
309
  }
249
310
  else {
250
311
  distinctPart = ' DISTINCT';
251
312
  }
252
- if (!this.formatter.isPretty()) {
313
+ if (!context.isPretty()) {
253
314
  if (distinctClause.length > 0 && Object.keys(distinctClause[0]).length > 0) {
254
315
  output.push('DISTINCT ON');
255
316
  const clause = distinctClause
256
- .map(e => this.visit(e, { ...context, select: true }))
317
+ .map(e => this.visit(e, context.spawn('SelectStmt', { select: true })))
257
318
  .join(', ');
258
- output.push(this.formatter.parens(clause));
319
+ output.push(context.parens(clause));
259
320
  }
260
321
  else {
261
322
  output.push('DISTINCT');
@@ -264,22 +325,41 @@ class Deparser {
264
325
  }
265
326
  if (node.targetList) {
266
327
  const targetList = list_utils_1.ListUtils.unwrapList(node.targetList);
267
- if (this.formatter.isPretty()) {
268
- const targetStrings = targetList
269
- .map(e => {
270
- const targetStr = this.visit(e, { ...context, select: true });
271
- if (this.containsMultilineStringLiteral(targetStr)) {
272
- return targetStr;
328
+ if (context.isPretty()) {
329
+ if (targetList.length === 1) {
330
+ const targetNode = targetList[0];
331
+ const target = this.visit(targetNode, context.spawn('SelectStmt', { select: true }));
332
+ // Check if single target is complex - if so, use multiline format
333
+ if (this.isComplexSelectTarget(targetNode)) {
334
+ output.push('SELECT' + distinctPart);
335
+ if (this.containsMultilineStringLiteral(target)) {
336
+ output.push(target);
337
+ }
338
+ else {
339
+ output.push(context.indent(target));
340
+ }
273
341
  }
274
- return this.formatter.indent(targetStr);
275
- });
276
- const formattedTargets = targetStrings.join(',' + this.formatter.newline());
277
- output.push('SELECT' + distinctPart);
278
- output.push(formattedTargets);
342
+ else {
343
+ output.push('SELECT' + distinctPart + ' ' + target);
344
+ }
345
+ }
346
+ else {
347
+ const targetStrings = targetList
348
+ .map(e => {
349
+ const targetStr = this.visit(e, context.spawn('SelectStmt', { select: true }));
350
+ if (this.containsMultilineStringLiteral(targetStr)) {
351
+ return targetStr;
352
+ }
353
+ return context.indent(targetStr);
354
+ });
355
+ const formattedTargets = targetStrings.join(',' + context.newline());
356
+ output.push('SELECT' + distinctPart);
357
+ output.push(formattedTargets);
358
+ }
279
359
  }
280
360
  else {
281
361
  const targets = targetList
282
- .map(e => this.visit(e, { ...context, select: true }))
362
+ .map(e => this.visit(e, context.spawn('SelectStmt', { select: true })))
283
363
  .join(', ');
284
364
  output.push(targets);
285
365
  }
@@ -291,22 +371,22 @@ class Deparser {
291
371
  if (node.fromClause) {
292
372
  const fromList = list_utils_1.ListUtils.unwrapList(node.fromClause);
293
373
  const fromItems = fromList
294
- .map(e => this.deparse(e, { ...context, from: true }))
374
+ .map(e => this.deparse(e, context.spawn('SelectStmt', { from: true })))
295
375
  .join(', ');
296
376
  output.push('FROM ' + fromItems.trim());
297
377
  }
298
378
  if (node.whereClause) {
299
- if (this.formatter.isPretty()) {
379
+ if (context.isPretty()) {
300
380
  output.push('WHERE');
301
381
  const whereExpr = this.visit(node.whereClause, context);
302
- const lines = whereExpr.split(this.formatter.newline());
382
+ const lines = whereExpr.split(context.newline());
303
383
  const indentedLines = lines.map((line, index) => {
304
384
  if (index === 0) {
305
- return this.formatter.indent(line);
385
+ return context.indent(line);
306
386
  }
307
387
  return line;
308
388
  });
309
- output.push(indentedLines.join(this.formatter.newline()));
389
+ output.push(indentedLines.join(context.newline()));
310
390
  }
311
391
  else {
312
392
  output.push('WHERE');
@@ -314,45 +394,61 @@ class Deparser {
314
394
  }
315
395
  }
316
396
  if (node.valuesLists) {
317
- output.push('VALUES');
318
- const lists = list_utils_1.ListUtils.unwrapList(node.valuesLists).map(list => {
319
- const values = list_utils_1.ListUtils.unwrapList(list).map(val => this.visit(val, context));
320
- return this.formatter.parens(values.join(', '));
321
- });
322
- output.push(lists.join(', '));
397
+ if (context.isPretty()) {
398
+ output.push('VALUES');
399
+ const lists = list_utils_1.ListUtils.unwrapList(node.valuesLists).map(list => {
400
+ const values = list_utils_1.ListUtils.unwrapList(list).map(val => this.visit(val, context));
401
+ return context.parens(values.join(', '));
402
+ });
403
+ const indentedTuples = lists.map(tuple => {
404
+ if (this.containsMultilineStringLiteral(tuple)) {
405
+ return tuple;
406
+ }
407
+ return context.indent(tuple);
408
+ });
409
+ output.push(indentedTuples.join(',\n'));
410
+ }
411
+ else {
412
+ output.push('VALUES');
413
+ const lists = list_utils_1.ListUtils.unwrapList(node.valuesLists).map(list => {
414
+ const values = list_utils_1.ListUtils.unwrapList(list).map(val => this.visit(val, context));
415
+ return context.parens(values.join(', '));
416
+ });
417
+ output.push(lists.join(', '));
418
+ }
323
419
  }
324
420
  if (node.groupClause) {
325
421
  const groupList = list_utils_1.ListUtils.unwrapList(node.groupClause);
326
- if (this.formatter.isPretty()) {
422
+ if (context.isPretty()) {
327
423
  const groupItems = groupList
328
424
  .map(e => {
329
- const groupStr = this.visit(e, { ...context, group: true });
425
+ const groupStr = this.visit(e, context.spawn('SelectStmt', { group: true, indentLevel: context.indentLevel + 1 }));
330
426
  if (this.containsMultilineStringLiteral(groupStr)) {
331
427
  return groupStr;
332
428
  }
333
- return this.formatter.indent(groupStr);
429
+ return context.indent(groupStr);
334
430
  })
335
- .join(',' + this.formatter.newline());
431
+ .join(',' + context.newline());
336
432
  output.push('GROUP BY');
337
433
  output.push(groupItems);
338
434
  }
339
435
  else {
340
436
  output.push('GROUP BY');
341
437
  const groupItems = groupList
342
- .map(e => this.visit(e, { ...context, group: true }))
438
+ .map(e => this.visit(e, context.spawn('SelectStmt', { group: true })))
343
439
  .join(', ');
344
440
  output.push(groupItems);
345
441
  }
346
442
  }
347
443
  if (node.havingClause) {
348
- if (this.formatter.isPretty()) {
444
+ if (context.isPretty()) {
349
445
  output.push('HAVING');
350
446
  const havingStr = this.visit(node.havingClause, context);
351
447
  if (this.containsMultilineStringLiteral(havingStr)) {
352
448
  output.push(havingStr);
353
449
  }
354
450
  else {
355
- output.push(this.formatter.indent(havingStr));
451
+ output.push(context.indent(havingStr));
356
452
  }
357
453
  }
358
454
  else {
@@ -370,23 +466,23 @@ class Deparser {
370
466
  }
371
467
  if (node.sortClause) {
372
468
  const sortList = list_utils_1.ListUtils.unwrapList(node.sortClause);
373
- if (this.formatter.isPretty()) {
469
+ if (context.isPretty()) {
374
470
  const sortItems = sortList
375
471
  .map(e => {
376
- const sortStr = this.visit(e, { ...context, sort: true });
472
+ const sortStr = this.visit(e, context.spawn('SelectStmt', { sort: true, indentLevel: context.indentLevel + 1 }));
377
473
  if (this.containsMultilineStringLiteral(sortStr)) {
378
474
  return sortStr;
379
475
  }
380
- return this.formatter.indent(sortStr);
476
+ return context.indent(sortStr);
381
477
  })
382
- .join(',' + this.formatter.newline());
478
+ .join(',' + context.newline());
383
479
  output.push('ORDER BY');
384
480
  output.push(sortItems);
385
481
  }
386
482
  else {
387
483
  output.push('ORDER BY');
388
484
  const sortItems = sortList
389
- .map(e => this.visit(e, { ...context, sort: true }))
485
+ .map(e => this.visit(e, context.spawn('SelectStmt', { sort: true })))
390
486
  .join(', ');
391
487
  output.push(sortItems);
392
488
  }
@@ -404,9 +500,9 @@ class Deparser {
404
500
  .join(' ');
405
501
  output.push(lockingClauses);
406
502
  }
407
- if (this.formatter.isPretty()) {
503
+ if (context.isPretty()) {
408
504
  const filteredOutput = output.filter(item => item.trim() !== '');
409
- return filteredOutput.join(this.formatter.newline());
505
+ return filteredOutput.join(context.newline());
410
506
  }
411
507
  return output.join(' ');
412
508
  }
@@ -418,13 +514,13 @@ class Deparser {
418
514
  switch (kind) {
419
515
  case 'AEXPR_OP':
420
516
  if (lexpr && rexpr) {
421
- const operator = this.deparseOperatorName(name);
517
+ const operator = this.deparseOperatorName(name, context);
422
518
  let leftExpr = this.visit(lexpr, context);
423
519
  let rightExpr = this.visit(rexpr, context);
424
520
  // Check if left expression needs parentheses
425
521
  let leftNeedsParens = false;
426
522
  if (lexpr && 'A_Expr' in lexpr && lexpr.A_Expr?.kind === 'AEXPR_OP') {
427
- const leftOp = this.deparseOperatorName(list_utils_1.ListUtils.unwrapList(lexpr.A_Expr.name));
523
+ const leftOp = this.deparseOperatorName(list_utils_1.ListUtils.unwrapList(lexpr.A_Expr.name), context);
428
524
  if (this.needsParentheses(leftOp, operator, 'left')) {
429
525
  leftNeedsParens = true;
430
526
  }
@@ -433,12 +529,12 @@ class Deparser {
433
529
  leftNeedsParens = true;
434
530
  }
435
531
  if (leftNeedsParens) {
436
- leftExpr = this.formatter.parens(leftExpr);
532
+ leftExpr = context.parens(leftExpr);
437
533
  }
438
534
  // Check if right expression needs parentheses
439
535
  let rightNeedsParens = false;
440
536
  if (rexpr && 'A_Expr' in rexpr && rexpr.A_Expr?.kind === 'AEXPR_OP') {
441
- const rightOp = this.deparseOperatorName(list_utils_1.ListUtils.unwrapList(rexpr.A_Expr.name));
537
+ const rightOp = this.deparseOperatorName(list_utils_1.ListUtils.unwrapList(rexpr.A_Expr.name), context);
442
538
  if (this.needsParentheses(rightOp, operator, 'right')) {
443
539
  rightNeedsParens = true;
444
540
  }
@@ -447,42 +543,42 @@ class Deparser {
447
543
  rightNeedsParens = true;
448
544
  }
449
545
  if (rightNeedsParens) {
450
- rightExpr = this.formatter.parens(rightExpr);
546
+ rightExpr = context.parens(rightExpr);
451
547
  }
452
- return this.formatter.format([leftExpr, operator, rightExpr]);
548
+ return context.format([leftExpr, operator, rightExpr]);
453
549
  }
454
550
  else if (rexpr) {
455
- return this.formatter.format([
456
- this.deparseOperatorName(name),
551
+ return context.format([
552
+ this.deparseOperatorName(name, context),
457
553
  this.visit(rexpr, context)
458
554
  ]);
459
555
  }
460
556
  break;
461
557
  case 'AEXPR_OP_ANY':
462
- return this.formatter.format([
558
+ return context.format([
463
559
  this.visit(lexpr, context),
464
- this.deparseOperatorName(name),
560
+ this.deparseOperatorName(name, context),
465
561
  'ANY',
466
- this.formatter.parens(this.visit(rexpr, context))
562
+ context.parens(this.visit(rexpr, context))
467
563
  ]);
468
564
  case 'AEXPR_OP_ALL':
469
- return this.formatter.format([
565
+ return context.format([
470
566
  this.visit(lexpr, context),
471
- this.deparseOperatorName(name),
567
+ this.deparseOperatorName(name, context),
472
568
  'ALL',
473
- this.formatter.parens(this.visit(rexpr, context))
569
+ context.parens(this.visit(rexpr, context))
474
570
  ]);
475
571
  case 'AEXPR_DISTINCT': {
476
572
  let leftExpr = this.visit(lexpr, context);
477
573
  let rightExpr = this.visit(rexpr, context);
478
574
  // Add parentheses for complex expressions
479
575
  if (lexpr && this.isComplexExpression(lexpr)) {
480
- leftExpr = this.formatter.parens(leftExpr);
576
+ leftExpr = context.parens(leftExpr);
481
577
  }
482
578
  if (rexpr && this.isComplexExpression(rexpr)) {
483
- rightExpr = this.formatter.parens(rightExpr);
579
+ rightExpr = context.parens(rightExpr);
484
580
  }
485
- return this.formatter.format([
581
+ return context.format([
486
582
  leftExpr,
487
583
  'IS DISTINCT FROM',
488
584
  rightExpr
@@ -493,75 +589,75 @@ class Deparser {
493
589
  let rightExpr = this.visit(rexpr, context);
494
590
  // Add parentheses for complex expressions
495
591
  if (lexpr && this.isComplexExpression(lexpr)) {
496
- leftExpr = this.formatter.parens(leftExpr);
592
+ leftExpr = context.parens(leftExpr);
497
593
  }
498
594
  if (rexpr && this.isComplexExpression(rexpr)) {
499
- rightExpr = this.formatter.parens(rightExpr);
595
+ rightExpr = context.parens(rightExpr);
500
596
  }
501
- return this.formatter.format([
597
+ return context.format([
502
598
  leftExpr,
503
599
  'IS NOT DISTINCT FROM',
504
600
  rightExpr
505
601
  ]);
506
602
  }
507
603
  case 'AEXPR_NULLIF':
508
- return this.formatter.format([
604
+ return context.format([
509
605
  'NULLIF',
510
- this.formatter.parens([
606
+ context.parens([
511
607
  this.visit(lexpr, context),
512
608
  this.visit(rexpr, context)
513
609
  ].join(', '))
514
610
  ]);
515
611
  case 'AEXPR_IN':
516
- const inOperator = this.deparseOperatorName(name);
612
+ const inOperator = this.deparseOperatorName(name, context);
517
613
  if (inOperator === '<>' || inOperator === '!=') {
518
- return this.formatter.format([
614
+ return context.format([
519
615
  this.visit(lexpr, context),
520
616
  'NOT IN',
521
- this.formatter.parens(this.visit(rexpr, context))
617
+ context.parens(this.visit(rexpr, context))
522
618
  ]);
523
619
  }
524
620
  else {
525
- return this.formatter.format([
621
+ return context.format([
526
622
  this.visit(lexpr, context),
527
623
  'IN',
528
- this.formatter.parens(this.visit(rexpr, context))
624
+ context.parens(this.visit(rexpr, context))
529
625
  ]);
530
626
  }
531
627
  case 'AEXPR_LIKE':
532
- const likeOp = this.deparseOperatorName(name);
628
+ const likeOp = this.deparseOperatorName(name, context);
533
629
  if (likeOp === '!~~') {
534
- return this.formatter.format([
630
+ return context.format([
535
631
  this.visit(lexpr, context),
536
632
  'NOT LIKE',
537
633
  this.visit(rexpr, context)
538
634
  ]);
539
635
  }
540
636
  else {
541
- return this.formatter.format([
637
+ return context.format([
542
638
  this.visit(lexpr, context),
543
639
  'LIKE',
544
640
  this.visit(rexpr, context)
545
641
  ]);
546
642
  }
547
643
  case 'AEXPR_ILIKE':
548
- const ilikeOp = this.deparseOperatorName(name);
644
+ const ilikeOp = this.deparseOperatorName(name, context);
549
645
  if (ilikeOp === '!~~*') {
550
- return this.formatter.format([
646
+ return context.format([
551
647
  this.visit(lexpr, context),
552
648
  'NOT ILIKE',
553
649
  this.visit(rexpr, context)
554
650
  ]);
555
651
  }
556
652
  else {
557
- return this.formatter.format([
653
+ return context.format([
558
654
  this.visit(lexpr, context),
559
655
  'ILIKE',
560
656
  this.visit(rexpr, context)
561
657
  ]);
562
658
  }
563
659
  case 'AEXPR_SIMILAR':
564
- const similarOp = this.deparseOperatorName(name);
660
+ const similarOp = this.deparseOperatorName(name, context);
565
661
  let rightExpr;
566
662
  if (rexpr && 'FuncCall' in rexpr &&
567
663
  rexpr.FuncCall?.funcname?.length === 2 &&
@@ -577,39 +673,39 @@ class Deparser {
577
673
  rightExpr = this.visit(rexpr, context);
578
674
  }
579
675
  if (similarOp === '!~') {
580
- return this.formatter.format([
676
+ return context.format([
581
677
  this.visit(lexpr, context),
582
678
  'NOT SIMILAR TO',
583
679
  rightExpr
584
680
  ]);
585
681
  }
586
682
  else {
587
- return this.formatter.format([
683
+ return context.format([
588
684
  this.visit(lexpr, context),
589
685
  'SIMILAR TO',
590
686
  rightExpr
591
687
  ]);
592
688
  }
593
689
  case 'AEXPR_BETWEEN':
594
- return this.formatter.format([
690
+ return context.format([
595
691
  this.visit(lexpr, context),
596
692
  'BETWEEN',
597
693
  this.visitBetweenRange(rexpr, context)
598
694
  ]);
599
695
  case 'AEXPR_NOT_BETWEEN':
600
- return this.formatter.format([
696
+ return context.format([
601
697
  this.visit(lexpr, context),
602
698
  'NOT BETWEEN',
603
699
  this.visitBetweenRange(rexpr, context)
604
700
  ]);
605
701
  case 'AEXPR_BETWEEN_SYM':
606
- return this.formatter.format([
702
+ return context.format([
607
703
  this.visit(lexpr, context),
608
704
  'BETWEEN SYMMETRIC',
609
705
  this.visitBetweenRange(rexpr, context)
610
706
  ]);
611
707
  case 'AEXPR_NOT_BETWEEN_SYM':
612
- return this.formatter.format([
708
+ return context.format([
613
709
  this.visit(lexpr, context),
614
710
  'NOT BETWEEN SYMMETRIC',
615
711
  this.visitBetweenRange(rexpr, context)
@@ -617,7 +713,7 @@ class Deparser {
617
713
  }
618
714
  throw new Error(`Unhandled A_Expr kind: ${kind}`);
619
715
  }
620
- deparseOperatorName(name) {
716
+ deparseOperatorName(name, context) {
621
717
  if (!name || name.length === 0) {
622
718
  return '';
623
719
  }
@@ -625,7 +721,7 @@ class Deparser {
625
721
  if (n.String) {
626
722
  return n.String.sval || n.String.str;
627
723
  }
628
- return this.visit(n, { parentNodeTypes: [] });
724
+ return this.visit(n, context);
629
725
  });
630
726
  if (parts.length > 1) {
631
727
  return `OPERATOR(${parts.join('.')})`;
@@ -688,6 +784,64 @@ class Deparser {
688
784
  node.SubLink ||
689
785
  node.A_Expr);
690
786
  }
787
+ isComplexSelectTarget(node) {
788
+ if (!node)
789
+ return false;
790
+ if (node.ResTarget?.val) {
791
+ return this.isComplexExpression(node.ResTarget.val);
792
+ }
793
+ // Always complex: CASE expressions
794
+ if (node.CaseExpr)
795
+ return true;
796
+ // Always complex: Subqueries and subselects
797
+ if (node.SubLink)
798
+ return true;
799
+ // Always complex: Boolean tests and expressions
800
+ if (node.NullTest || node.BooleanTest || node.BoolExpr)
801
+ return true;
802
+ // COALESCE and similar functions - complex if multiple arguments
803
+ if (node.CoalesceExpr) {
804
+ const args = node.CoalesceExpr.args;
805
+ if (args && Array.isArray(args) && args.length > 1)
806
+ return true;
807
+ }
808
+ // Function calls - complex if multiple args or has clauses
809
+ if (node.FuncCall) {
810
+ const funcCall = node.FuncCall;
811
+ const args = funcCall.args ? (Array.isArray(funcCall.args) ? funcCall.args : [funcCall.args]) : [];
812
+ // Complex if has window clause, filter, order by, etc.
813
+ if (funcCall.over || funcCall.agg_filter || funcCall.agg_order || funcCall.agg_distinct) {
814
+ return true;
815
+ }
816
+ // Complex if multiple arguments
817
+ if (args.length > 1)
818
+ return true;
819
+ if (args.length === 1) {
820
+ return this.isComplexSelectTarget(args[0]);
821
+ }
822
+ }
823
+ if (node.A_Expr) {
824
+ const expr = node.A_Expr;
825
+ // Check if operands are complex
826
+ if (expr.lexpr && this.isComplexSelectTarget(expr.lexpr))
827
+ return true;
828
+ if (expr.rexpr && this.isComplexSelectTarget(expr.rexpr))
829
+ return true;
830
+ return false;
831
+ }
832
+ if (node.TypeCast) {
833
+ return this.isComplexSelectTarget(node.TypeCast.arg);
834
+ }
835
+ if (node.A_ArrayExpr)
836
+ return true;
837
+ if (node.A_Indirection) {
838
+ return this.isComplexSelectTarget(node.A_Indirection.arg);
839
+ }
840
+ if (node.A_Const || node.ColumnRef || node.ParamRef || node.A_Star) {
841
+ return false;
842
+ }
843
+ return false;
844
+ }
691
845
  visitBetweenRange(rexpr, context) {
692
846
  if (rexpr && 'List' in rexpr && rexpr.List?.items) {
693
847
  const items = rexpr.List.items.map((item) => this.visit(item, context));
@@ -704,9 +858,16 @@ class Deparser {
704
858
  output.push(this.RangeVar(node.relation, context));
705
859
  if (node.cols) {
706
860
  const cols = list_utils_1.ListUtils.unwrapList(node.cols);
707
- const insertContext = { ...context, insertColumns: true };
861
+ const insertContext = context.spawn('InsertStmt', { insertColumns: true });
708
862
  const columnNames = cols.map(col => this.visit(col, insertContext));
709
- output.push(this.formatter.parens(columnNames.join(', ')));
863
+ if (context.isPretty()) {
864
+ // Always format columns in multiline parentheses for pretty printing
865
+ const indentedColumns = columnNames.map(col => context.indent(col));
866
+ output.push('(\n' + indentedColumns.join(',\n') + '\n)');
867
+ }
868
+ else {
869
+ output.push(context.parens(columnNames.join(', ')));
870
+ }
710
871
  }
711
872
  if (node.selectStmt) {
712
873
  output.push(this.visit(node.selectStmt, context));
@@ -727,7 +888,7 @@ class Deparser {
727
888
  else if (infer.indexElems) {
728
889
  const elems = list_utils_1.ListUtils.unwrapList(infer.indexElems);
729
890
  const indexElems = elems.map(elem => this.visit(elem, context));
730
- output.push(this.formatter.parens(indexElems.join(', ')));
891
+ output.push(context.parens(indexElems.join(', ')));
731
892
  }
732
893
  // Handle WHERE clause for conflict detection
733
894
  if (infer.whereClause) {
@@ -743,12 +904,12 @@ class Deparser {
743
904
  if (firstTarget.ResTarget?.val?.MultiAssignRef && targetList.every(target => target.ResTarget?.val?.MultiAssignRef)) {
744
905
  const sortedTargets = targetList.sort((a, b) => a.ResTarget.val.MultiAssignRef.colno - b.ResTarget.val.MultiAssignRef.colno);
745
906
  const names = sortedTargets.map(target => target.ResTarget.name);
746
- output.push(this.formatter.parens(names.join(', ')));
907
+ output.push(context.parens(names.join(', ')));
747
908
  output.push('=');
748
909
  output.push(this.visit(firstTarget.ResTarget.val.MultiAssignRef.source, context));
749
910
  }
750
911
  else {
751
- const updateContext = { ...context, update: true };
912
+ const updateContext = context.spawn('UpdateStmt', { update: true });
752
913
  const targets = targetList.map(target => this.visit(target, updateContext));
753
914
  output.push(targets.join(', '));
754
915
  }
@@ -802,12 +963,12 @@ class Deparser {
802
963
  }
803
964
  }
804
965
  const names = relatedTargets.map(t => t.ResTarget.name);
805
- const multiAssignment = `${this.formatter.parens(names.join(', '))} = ${this.visit(multiAssignRef.source, context)}`;
966
+ const multiAssignment = `${context.parens(names.join(', '))} = ${this.visit(multiAssignRef.source, context)}`;
806
967
  assignmentParts.push(multiAssignment);
807
968
  }
808
969
  else {
809
970
  // Handle regular single-column assignment
810
- assignmentParts.push(this.visit(target, { ...context, update: true }));
971
+ assignmentParts.push(this.visit(target, context.spawn('UpdateStmt', { update: true })));
811
972
  processedTargets.add(i);
812
973
  }
813
974
  }
@@ -899,14 +1060,14 @@ class Deparser {
899
1060
  }
900
1061
  if (node.ctes && node.ctes.length > 0) {
901
1062
  const ctes = list_utils_1.ListUtils.unwrapList(node.ctes);
902
- if (this.formatter.isPretty()) {
1063
+ if (context.isPretty()) {
903
1064
  const cteStrings = ctes.map((cte, index) => {
904
1065
  const cteStr = this.visit(cte, context);
905
- const prefix = index === 0 ? this.formatter.newline() : ',' + this.formatter.newline();
1066
+ const prefix = index === 0 ? context.newline() : ',' + context.newline();
906
1067
  if (this.containsMultilineStringLiteral(cteStr)) {
907
1068
  return prefix + cteStr;
908
1069
  }
909
- return prefix + this.formatter.indent(cteStr);
1070
+ return prefix + context.indent(cteStr);
910
1071
  });
911
1072
  output.push(cteStrings.join(''));
912
1073
  }
@@ -992,14 +1153,14 @@ class Deparser {
992
1153
  if (context.bool) {
993
1154
  formatStr = '(%s)';
994
1155
  }
995
- const boolContext = { ...context, bool: true };
1156
+ const boolContext = context.spawn('BoolExpr', { bool: true });
996
1157
  // explanation of our syntax/fix below:
997
1158
  // return formatStr.replace('%s', andArgs); // ❌ Interprets $ as special syntax
998
1159
  // return formatStr.replace('%s', () => andArgs); // ✅ Function callback prevents interpretation
999
1160
  switch (boolop) {
1000
1161
  case 'AND_EXPR':
1001
- if (this.formatter.isPretty() && args.length > 1) {
1002
- const andArgs = args.map(arg => this.visit(arg, boolContext)).join(this.formatter.newline() + ' AND ');
1162
+ if (context.isPretty() && args.length > 1) {
1163
+ const andArgs = args.map(arg => this.visit(arg, boolContext)).join(context.newline() + context.indent('AND '));
1003
1164
  return formatStr.replace('%s', () => andArgs);
1004
1165
  }
1005
1166
  else {
@@ -1007,8 +1168,8 @@ class Deparser {
1007
1168
  return formatStr.replace('%s', () => andArgs);
1008
1169
  }
1009
1170
  case 'OR_EXPR':
1010
- if (this.formatter.isPretty() && args.length > 1) {
1011
- const orArgs = args.map(arg => this.visit(arg, boolContext)).join(this.formatter.newline() + ' OR ');
1171
+ if (context.isPretty() && args.length > 1) {
1172
+ const orArgs = args.map(arg => this.visit(arg, boolContext)).join(context.newline() + context.indent('OR '));
1012
1173
  return formatStr.replace('%s', () => orArgs);
1013
1174
  }
1014
1175
  else {
@@ -1139,9 +1300,9 @@ class Deparser {
1139
1300
  const timezone = this.visit(args[0], context);
1140
1301
  // Add parentheses around timestamp if it contains arithmetic operations
1141
1302
  if (args[1] && 'A_Expr' in args[1] && args[1].A_Expr?.kind === 'AEXPR_OP') {
1142
- const op = this.deparseOperatorName(list_utils_1.ListUtils.unwrapList(args[1].A_Expr.name));
1303
+ const op = this.deparseOperatorName(list_utils_1.ListUtils.unwrapList(args[1].A_Expr.name), context);
1143
1304
  if (op === '+' || op === '-' || op === '*' || op === '/') {
1144
- timestamp = this.formatter.parens(timestamp);
1305
+ timestamp = context.parens(timestamp);
1145
1306
  }
1146
1307
  }
1147
1308
  return `${timestamp} AT TIME ZONE ${timezone}`;
@@ -1211,14 +1372,14 @@ class Deparser {
1211
1372
  windowParts.push(`ORDER BY ${orderStrs.join(', ')}`);
1212
1373
  }
1213
1374
  // Handle window frame specifications using the dedicated formatWindowFrame method
1214
- const frameClause = this.formatWindowFrame(node.over);
1375
+ const frameClause = this.formatWindowFrame(node.over, context.spawn('FuncCall'));
1215
1376
  if (frameClause) {
1216
1377
  windowParts.push(frameClause);
1217
1378
  }
1218
1379
  if (windowParts.length > 0) {
1219
- if (this.formatter.isPretty() && windowParts.length > 1) {
1220
- const formattedParts = windowParts.map(part => this.formatter.indent(part));
1221
- result += ` OVER (${this.formatter.newline()}${formattedParts.join(this.formatter.newline())}${this.formatter.newline()})`;
1380
+ if (context.isPretty() && windowParts.length > 1) {
1381
+ const formattedParts = windowParts.map(part => context.indent(part));
1382
+ result += ` OVER (${context.newline()}${formattedParts.join(context.newline())}${context.newline()})`;
1222
1383
  }
1223
1384
  else {
1224
1385
  result += ` OVER (${windowParts.join(' ')})`;
@@ -1498,9 +1659,6 @@ class Deparser {
1498
1659
  return output.join(' ');
1499
1660
  }
1500
1661
  if (catalog === 'pg_catalog') {
1501
- const builtinTypes = ['int2', 'int4', 'int8', 'float4', 'float8', 'numeric', 'decimal',
1502
- 'varchar', 'char', 'bpchar', 'text', 'bool', 'date', 'time', 'timestamp',
1503
- 'timestamptz', 'interval', 'bytea', 'uuid', 'json', 'jsonb'];
1504
1662
  let typeName = `${catalog}.${type}`;
1505
1663
  if (type === 'bpchar' && args) {
1506
1664
  typeName = 'char';
@@ -1557,6 +1715,24 @@ class Deparser {
1557
1715
  typeName = 'time with time zone';
1558
1716
  }
1559
1717
  }
1718
+ else if (type === 'timestamp') {
1719
+ if (args) {
1720
+ typeName = `timestamp(${args})`;
1721
+ args = null; // Don't apply args again in mods()
1722
+ }
1723
+ else {
1724
+ typeName = 'timestamp';
1725
+ }
1726
+ }
1727
+ else if (type === 'time') {
1728
+ if (args) {
1729
+ typeName = `time(${args})`;
1730
+ args = null; // Don't apply args again in mods()
1731
+ }
1732
+ else {
1733
+ typeName = 'time';
1734
+ }
1735
+ }
1560
1736
  let result = mods(typeName, args);
1561
1737
  if (node.arrayBounds && node.arrayBounds.length > 0) {
1562
1738
  result += formatArrayBounds(node.arrayBounds);
@@ -1586,7 +1762,7 @@ class Deparser {
1586
1762
  }
1587
1763
  return this.quoteIfNeeded(colStr);
1588
1764
  });
1589
- output.push('AS', this.quoteIfNeeded(name) + this.formatter.parens(quotedColnames.join(', ')));
1765
+ output.push('AS', this.quoteIfNeeded(name) + context.parens(quotedColnames.join(', ')));
1590
1766
  }
1591
1767
  else {
1592
1768
  output.push('AS', this.quoteIfNeeded(name));
@@ -1598,7 +1774,7 @@ class Deparser {
1598
1774
  // Handle ONLY keyword for inheritance control (but not for type definitions, ALTER TYPE, or CREATE FOREIGN TABLE)
1599
1775
  if (node && (!('inh' in node) || node.inh === undefined) &&
1600
1776
  !context.parentNodeTypes.includes('CompositeTypeStmt') &&
1601
- !context.parentNodeTypes.includes('AlterTypeStmt') &&
1777
+ (!context.parentNodeTypes.includes('AlterTypeStmt') && context.objtype !== 'OBJECT_TYPE') &&
1602
1778
  !context.parentNodeTypes.includes('CreateForeignTableStmt')) {
1603
1779
  output.push('ONLY');
1604
1780
  }
@@ -1795,6 +1971,18 @@ class Deparser {
1795
1971
  return `pg_catalog.${typeName}`;
1796
1972
  }
1797
1973
  }
1974
+ isPgCatalogType(typeName) {
1975
+ const cleanTypeName = typeName.replace(/^pg_catalog\./, '');
1976
+ if (pgCatalogTypes.includes(cleanTypeName)) {
1977
+ return true;
1978
+ }
1979
+ for (const [realType, aliases] of pgCatalogTypeAliases) {
1980
+ if (aliases.includes(cleanTypeName)) {
1981
+ return true;
1982
+ }
1983
+ }
1984
+ return false;
1985
+ }
1798
1986
  A_ArrayExpr(node, context) {
1799
1987
  const elements = list_utils_1.ListUtils.unwrapList(node.elements);
1800
1988
  const elementStrs = elements.map(el => this.visit(el, context));
@@ -1846,26 +2034,26 @@ class Deparser {
1846
2034
  output.push(this.visit(node.arg, context));
1847
2035
  }
1848
2036
  const args = list_utils_1.ListUtils.unwrapList(node.args);
1849
- if (this.formatter.isPretty() && args.length > 0) {
2037
+ if (context.isPretty() && args.length > 0) {
1850
2038
  for (const arg of args) {
1851
2039
  const whenClause = this.visit(arg, context);
1852
2040
  if (this.containsMultilineStringLiteral(whenClause)) {
1853
- output.push(this.formatter.newline() + whenClause);
2041
+ output.push(context.newline() + whenClause);
1854
2042
  }
1855
2043
  else {
1856
- output.push(this.formatter.newline() + this.formatter.indent(whenClause));
2044
+ output.push(context.newline() + context.indent(whenClause));
1857
2045
  }
1858
2046
  }
1859
2047
  if (node.defresult) {
1860
2048
  const elseResult = this.visit(node.defresult, context);
1861
2049
  if (this.containsMultilineStringLiteral(elseResult)) {
1862
- output.push(this.formatter.newline() + 'ELSE ' + elseResult);
2050
+ output.push(context.newline() + 'ELSE ' + elseResult);
1863
2051
  }
1864
2052
  else {
1865
- output.push(this.formatter.newline() + this.formatter.indent('ELSE ' + elseResult));
2053
+ output.push(context.newline() + context.indent('ELSE ' + elseResult));
1866
2054
  }
1867
2055
  }
1868
- output.push(this.formatter.newline() + 'END');
2056
+ output.push(context.newline() + 'END');
1869
2057
  return output.join(' ');
1870
2058
  }
1871
2059
  else {
@@ -1888,28 +2076,29 @@ class Deparser {
1888
2076
  TypeCast(node, context) {
1889
2077
  const arg = this.visit(node.arg, context);
1890
2078
  const typeName = this.TypeName(node.typeName, context);
1891
- // Check if this is a bpchar typecast that should use traditional char syntax
1892
- if (typeName === 'bpchar' && node.typeName && node.typeName.names) {
1893
- const names = list_utils_1.ListUtils.unwrapList(node.typeName.names);
1894
- if (names.length === 2 &&
1895
- names[0].String?.sval === 'pg_catalog' &&
1896
- names[1].String?.sval === 'bpchar') {
1897
- return `char ${arg}`;
2079
+ // Check if this is a bpchar typecast that should preserve original syntax for AST consistency
2080
+ if (typeName === 'bpchar' || typeName === 'pg_catalog.bpchar') {
2081
+ const names = node.typeName?.names;
2082
+ const isQualifiedBpchar = names && names.length === 2 &&
2083
+ names[0]?.String?.sval === 'pg_catalog' &&
2084
+ names[1]?.String?.sval === 'bpchar';
2085
+ if (isQualifiedBpchar) {
2086
+ return `CAST(${arg} AS ${typeName})`;
1898
2087
  }
1899
2088
  }
1900
- // Check if the argument is a complex expression that should preserve CAST syntax
1901
- const argType = this.getNodeType(node.arg);
1902
- const isComplexExpression = argType === 'A_Expr' || argType === 'FuncCall' || argType === 'OpExpr';
1903
- if (!isComplexExpression && (typeName.startsWith('interval') ||
1904
- typeName.startsWith('char') ||
1905
- typeName === '"char"' ||
1906
- typeName.startsWith('bpchar') ||
1907
- typeName === 'bytea' ||
1908
- typeName === 'orderedarray' ||
1909
- typeName === 'date')) {
1910
- // Remove pg_catalog prefix for :: syntax
1911
- const cleanTypeName = typeName.replace('pg_catalog.', '');
1912
- return `${arg}::${cleanTypeName}`;
2089
+ if (this.isPgCatalogType(typeName)) {
2090
+ const argType = this.getNodeType(node.arg);
2091
+ const isSimpleArgument = argType === 'A_Const' || argType === 'ColumnRef';
2092
+ const isFunctionCall = argType === 'FuncCall';
2093
+ if (isSimpleArgument || isFunctionCall) {
2094
+ // For simple arguments, avoid :: syntax if they have complex structure
2095
+ if (isSimpleArgument && (arg.includes('(') || arg.startsWith('-'))) {
2096
+ }
2097
+ else {
2098
+ const cleanTypeName = typeName.replace('pg_catalog.', '');
2099
+ return `${arg}::${cleanTypeName}`;
2100
+ }
2101
+ }
1913
2102
  }
1914
2103
  return `CAST(${arg} AS ${typeName})`;
1915
2104
  }
@@ -1932,7 +2121,7 @@ class Deparser {
1932
2121
  }
1933
2122
  BooleanTest(node, context) {
1934
2123
  const output = [];
1935
- const boolContext = { ...context, bool: true };
2124
+ const boolContext = context.spawn('BooleanTest', { bool: true });
1936
2125
  output.push(this.visit(node.arg, boolContext));
1937
2126
  switch (node.booltesttype) {
1938
2127
  case 'IS_TRUE':
@@ -2083,24 +2272,31 @@ class Deparser {
2083
2272
  const elementStrs = elements.map(el => {
2084
2273
  return this.deparse(el, context);
2085
2274
  });
2086
- output.push(this.formatter.parens(elementStrs.join(', ')));
2275
+ output.push(context.parens(elementStrs.join(', ')));
2087
2276
  }
2088
2277
  }
2089
2278
  else if (node.tableElts) {
2090
2279
  const elements = list_utils_1.ListUtils.unwrapList(node.tableElts);
2091
2280
  const elementStrs = elements.map(el => {
2092
- return this.deparse(el, context);
2281
+ return this.deparse(el, context.spawn('CreateStmt'));
2093
2282
  });
2094
- if (this.formatter.isPretty()) {
2095
- const formattedElements = elementStrs.map(el => this.formatter.indent(el)).join(',' + this.formatter.newline());
2096
- output.push('(' + this.formatter.newline() + formattedElements + this.formatter.newline() + ')');
2283
+ if (context.isPretty()) {
2284
+ const formattedElements = elementStrs.map(el => {
2285
+ const trimmedEl = el.trim();
2286
+ // Remove leading newlines from constraint elements to avoid extra blank lines
2287
+ if (trimmedEl.startsWith('\n')) {
2288
+ return context.indent(trimmedEl.substring(1));
2289
+ }
2290
+ return context.indent(trimmedEl);
2291
+ }).join(',' + context.newline());
2292
+ output.push('(' + context.newline() + formattedElements + context.newline() + ')');
2097
2293
  }
2098
2294
  else {
2099
- output.push(this.formatter.parens(elementStrs.join(', ')));
2295
+ output.push(context.parens(elementStrs.join(', ')));
2100
2296
  }
2101
2297
  }
2102
2298
  else if (!node.partbound) {
2103
- output.push(this.formatter.parens(''));
2299
+ output.push(context.parens(''));
2104
2300
  }
2105
2301
  if (node.partbound && node.inhRelations && node.inhRelations.length > 0) {
2106
2302
  output.push('PARTITION OF');
@@ -2143,7 +2339,7 @@ class Deparser {
2143
2339
  output.push('INHERITS');
2144
2340
  const inherits = list_utils_1.ListUtils.unwrapList(node.inhRelations);
2145
2341
  const inheritStrs = inherits.map(rel => this.visit(rel, context));
2146
- output.push(this.formatter.parens(inheritStrs.join(', ')));
2342
+ output.push(context.parens(inheritStrs.join(', ')));
2147
2343
  }
2148
2344
  if (node.partspec) {
2149
2345
  output.push('PARTITION BY');
@@ -2181,7 +2377,7 @@ class Deparser {
2181
2377
  }
2182
2378
  // Handle table options like WITH (fillfactor=10)
2183
2379
  if (node.options && node.options.length > 0) {
2184
- const createStmtContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'CreateStmt'] };
2380
+ const createStmtContext = context.spawn('CreateStmt');
2185
2381
  const optionStrs = node.options.map((option) => {
2186
2382
  return this.deparse(option, createStmtContext);
2187
2383
  });
@@ -2204,7 +2400,7 @@ class Deparser {
2204
2400
  }
2205
2401
  if (node.fdwoptions && node.fdwoptions.length > 0) {
2206
2402
  output.push('OPTIONS');
2207
- const columnContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'ColumnDef'] };
2403
+ const columnContext = context.spawn('ColumnDef');
2208
2404
  const options = list_utils_1.ListUtils.unwrapList(node.fdwoptions).map(opt => this.visit(opt, columnContext));
2209
2405
  output.push(`(${options.join(', ')})`);
2210
2406
  }
@@ -2214,7 +2410,7 @@ class Deparser {
2214
2410
  if (node.constraints) {
2215
2411
  const constraints = list_utils_1.ListUtils.unwrapList(node.constraints);
2216
2412
  const constraintStrs = constraints.map(constraint => {
2217
- const columnConstraintContext = { ...context, isColumnConstraint: true };
2413
+ const columnConstraintContext = context.spawn('ColumnDef', { isColumnConstraint: true });
2218
2414
  return this.visit(constraint, columnConstraintContext);
2219
2415
  });
2220
2416
  output.push(...constraintStrs);
@@ -2254,9 +2450,25 @@ class Deparser {
2254
2450
  }
2255
2451
  break;
2256
2452
  case 'CONSTR_CHECK':
2257
- output.push('CHECK');
2453
+ if (context.isPretty() && !context.isColumnConstraint) {
2454
+ output.push('\n' + context.indent('CHECK'));
2455
+ }
2456
+ else {
2457
+ output.push('CHECK');
2458
+ }
2258
2459
  if (node.raw_expr) {
2259
- output.push(this.formatter.parens(this.visit(node.raw_expr, context)));
2460
+ if (context.isPretty()) {
2461
+ const checkExpr = this.visit(node.raw_expr, context);
2462
+ if (checkExpr.includes('\n')) {
2463
+ output.push('(\n' + context.indent(checkExpr) + '\n)');
2464
+ }
2465
+ else {
2466
+ output.push(`(${checkExpr})`);
2467
+ }
2468
+ }
2469
+ else {
2470
+ output.push(context.parens(this.visit(node.raw_expr, context)));
2471
+ }
2260
2472
  }
2261
2473
  // Handle NOT VALID for check constraints
2262
2474
  if (node.skip_validation) {
@@ -2277,7 +2489,7 @@ class Deparser {
2277
2489
  }
2278
2490
  output.push('AS');
2279
2491
  if (node.raw_expr) {
2280
- output.push(this.formatter.parens(this.visit(node.raw_expr, context)));
2492
+ output.push(context.parens(this.visit(node.raw_expr, context)));
2281
2493
  }
2282
2494
  output.push('STORED');
2283
2495
  break;
@@ -2295,30 +2507,61 @@ class Deparser {
2295
2507
  .map(option => {
2296
2508
  if (option.DefElem) {
2297
2509
  const defElem = option.DefElem;
2298
- const argValue = defElem.arg ? this.visit(defElem.arg, context) : '';
2299
- if (defElem.defname === 'start') {
2510
+ if (defElem.defname === 'sequence_name') {
2511
+ if (defElem.arg && defElem.arg.List) {
2512
+ const nameList = list_utils_1.ListUtils.unwrapList(defElem.arg)
2513
+ .map(item => this.visit(item, context))
2514
+ .join('.');
2515
+ return `SEQUENCE NAME ${nameList}`;
2516
+ }
2517
+ return 'SEQUENCE NAME';
2518
+ }
2519
+ else if (defElem.defname === 'start') {
2520
+ const argValue = defElem.arg ? this.visit(defElem.arg, context) : '';
2300
2521
  return `START WITH ${argValue}`;
2301
2522
  }
2302
2523
  else if (defElem.defname === 'increment') {
2524
+ const argValue = defElem.arg ? this.visit(defElem.arg, context) : '';
2303
2525
  return `INCREMENT BY ${argValue}`;
2304
2526
  }
2305
2527
  else if (defElem.defname === 'minvalue') {
2306
- return `MINVALUE ${argValue}`;
2528
+ if (defElem.arg) {
2529
+ const argValue = this.visit(defElem.arg, context);
2530
+ return `MINVALUE ${argValue}`;
2531
+ }
2532
+ else {
2533
+ return 'NO MINVALUE';
2534
+ }
2307
2535
  }
2308
2536
  else if (defElem.defname === 'maxvalue') {
2309
- return `MAXVALUE ${argValue}`;
2537
+ if (defElem.arg) {
2538
+ const argValue = this.visit(defElem.arg, context);
2539
+ return `MAXVALUE ${argValue}`;
2540
+ }
2541
+ else {
2542
+ return 'NO MAXVALUE';
2543
+ }
2310
2544
  }
2311
2545
  else if (defElem.defname === 'cache') {
2546
+ const argValue = defElem.arg ? this.visit(defElem.arg, context) : '';
2312
2547
  return `CACHE ${argValue}`;
2313
2548
  }
2314
2549
  else if (defElem.defname === 'cycle') {
2550
+ const argValue = defElem.arg ? this.visit(defElem.arg, context) : '';
2315
2551
  return argValue === 'true' ? 'CYCLE' : 'NO CYCLE';
2316
2552
  }
2553
+ const argValue = defElem.arg ? this.visit(defElem.arg, context) : '';
2317
2554
  return `${defElem.defname.toUpperCase()} ${argValue}`;
2318
2555
  }
2319
2556
  return this.visit(option, context);
2320
2557
  });
2321
- output.push(`(${optionStrs.join(' ')})`);
2558
+ if (context.isPretty()) {
2559
+ const indentedOptions = optionStrs.map(option => context.indent(option));
2560
+ output.push('(\n' + indentedOptions.join('\n') + '\n)');
2561
+ }
2562
+ else {
2563
+ output.push(`(${optionStrs.join(' ')})`);
2564
+ }
2322
2565
  }
2323
2566
  break;
2324
2567
  case 'CONSTR_PRIMARY':
@@ -2335,7 +2578,12 @@ class Deparser {
2335
2578
  }
2336
2579
  break;
2337
2580
  case 'CONSTR_UNIQUE':
2338
- output.push('UNIQUE');
2581
+ if (context.isPretty() && !context.isColumnConstraint) {
2582
+ output.push('\n' + context.indent('UNIQUE'));
2583
+ }
2584
+ else {
2585
+ output.push('UNIQUE');
2586
+ }
2339
2587
  if (node.nulls_not_distinct) {
2340
2588
  output.push('NULLS NOT DISTINCT');
2341
2589
  }
@@ -2353,33 +2601,77 @@ class Deparser {
2353
2601
  case 'CONSTR_FOREIGN':
2354
2602
  // Only add "FOREIGN KEY" for table-level constraints, not column-level constraints
2355
2603
  if (!context.isColumnConstraint) {
2356
- output.push('FOREIGN KEY');
2357
- if (node.fk_attrs && node.fk_attrs.length > 0) {
2358
- const fkAttrs = list_utils_1.ListUtils.unwrapList(node.fk_attrs)
2359
- .map(attr => this.visit(attr, context))
2360
- .join(', ');
2361
- output.push(`(${fkAttrs})`);
2604
+ if (context.isPretty()) {
2605
+ output.push('\n' + context.indent('FOREIGN KEY'));
2606
+ if (node.fk_attrs && node.fk_attrs.length > 0) {
2607
+ const fkAttrs = list_utils_1.ListUtils.unwrapList(node.fk_attrs)
2608
+ .map(attr => this.visit(attr, context))
2609
+ .join(', ');
2610
+ output.push(`(${fkAttrs})`);
2611
+ }
2612
+ output.push('\n' + context.indent('REFERENCES'));
2613
+ }
2614
+ else {
2615
+ output.push('FOREIGN KEY');
2616
+ if (node.fk_attrs && node.fk_attrs.length > 0) {
2617
+ const fkAttrs = list_utils_1.ListUtils.unwrapList(node.fk_attrs)
2618
+ .map(attr => this.visit(attr, context))
2619
+ .join(', ');
2620
+ output.push(`(${fkAttrs})`);
2621
+ }
2622
+ output.push('REFERENCES');
2362
2623
  }
2363
2624
  }
2364
- output.push('REFERENCES');
2625
+ else {
2626
+ output.push('REFERENCES');
2627
+ }
2365
2628
  if (node.pktable) {
2366
- output.push(this.RangeVar(node.pktable, context));
2629
+ if (context.isPretty() && !context.isColumnConstraint) {
2630
+ const lastIndex = output.length - 1;
2631
+ if (lastIndex >= 0 && output[lastIndex].includes('REFERENCES')) {
2632
+ output[lastIndex] += ' ' + this.RangeVar(node.pktable, context);
2633
+ }
2634
+ else {
2635
+ output.push(this.RangeVar(node.pktable, context));
2636
+ }
2637
+ }
2638
+ else {
2639
+ output.push(this.RangeVar(node.pktable, context));
2640
+ }
2367
2641
  }
2368
2642
  if (node.pk_attrs && node.pk_attrs.length > 0) {
2369
2643
  const pkAttrs = list_utils_1.ListUtils.unwrapList(node.pk_attrs)
2370
2644
  .map(attr => this.visit(attr, context))
2371
2645
  .join(', ');
2372
- output.push(`(${pkAttrs})`);
2646
+ if (context.isPretty() && !context.isColumnConstraint) {
2647
+ const lastIndex = output.length - 1;
2648
+ if (lastIndex >= 0) {
2649
+ output[lastIndex] += ` (${pkAttrs})`;
2650
+ }
2651
+ else {
2652
+ output.push(`(${pkAttrs})`);
2653
+ }
2654
+ }
2655
+ else {
2656
+ output.push(`(${pkAttrs})`);
2657
+ }
2373
2658
  }
2374
2659
  if (node.fk_matchtype && node.fk_matchtype !== 's') {
2660
+ let matchClause = '';
2375
2661
  switch (node.fk_matchtype) {
2376
2662
  case 'f':
2377
- output.push('MATCH FULL');
2663
+ matchClause = 'MATCH FULL';
2378
2664
  break;
2379
2665
  case 'p':
2380
- output.push('MATCH PARTIAL');
2666
+ matchClause = 'MATCH PARTIAL';
2381
2667
  break;
2382
2668
  }
2669
+ if (context.isPretty() && !context.isColumnConstraint) {
2670
+ output.push('\n' + context.indent(matchClause));
2671
+ }
2672
+ else {
2673
+ output.push(matchClause);
2674
+ }
2383
2675
  }
2384
2676
  if (node.fk_upd_action && node.fk_upd_action !== 'a') {
2385
2677
  let updateClause = 'ON UPDATE ';
@@ -2397,8 +2689,8 @@ class Deparser {
2397
2689
  updateClause += 'SET DEFAULT';
2398
2690
  break;
2399
2691
  }
2400
- if (this.formatter.isPretty()) {
2401
- output.push('\n' + this.formatter.indent(updateClause));
2692
+ if (context.isPretty()) {
2693
+ output.push('\n' + context.indent(updateClause));
2402
2694
  }
2403
2695
  else {
2404
2696
  output.push('ON UPDATE');
@@ -2421,8 +2713,8 @@ class Deparser {
2421
2713
  deleteClause += 'SET DEFAULT';
2422
2714
  break;
2423
2715
  }
2424
- if (this.formatter.isPretty()) {
2425
- output.push('\n' + this.formatter.indent(deleteClause));
2716
+ if (context.isPretty()) {
2717
+ output.push('\n' + context.indent(deleteClause));
2426
2718
  }
2427
2719
  else {
2428
2720
  output.push('ON DELETE');
@@ -2431,7 +2723,12 @@ class Deparser {
2431
2723
  }
2432
2724
  // Handle NOT VALID for foreign key constraints - only for table constraints, not domain constraints
2433
2725
  if (node.skip_validation && !context.isDomainConstraint) {
2434
- output.push('NOT VALID');
2726
+ if (context.isPretty() && !context.isColumnConstraint) {
2727
+ output.push('\n' + context.indent('NOT VALID'));
2728
+ }
2729
+ else {
2730
+ output.push('NOT VALID');
2731
+ }
2435
2732
  }
2436
2733
  break;
2437
2734
  case 'CONSTR_ATTR_DEFERRABLE':
@@ -2485,13 +2782,13 @@ class Deparser {
2485
2782
  // Handle deferrable constraints for all constraint types that support it
2486
2783
  if (node.contype === 'CONSTR_PRIMARY' || node.contype === 'CONSTR_UNIQUE' || node.contype === 'CONSTR_FOREIGN') {
2487
2784
  if (node.deferrable) {
2488
- if (this.formatter.isPretty() && node.contype === 'CONSTR_FOREIGN') {
2489
- output.push('\n' + this.formatter.indent('DEFERRABLE'));
2785
+ if (context.isPretty() && node.contype === 'CONSTR_FOREIGN') {
2786
+ output.push('\n' + context.indent('DEFERRABLE'));
2490
2787
  if (node.initdeferred === true) {
2491
- output.push('\n' + this.formatter.indent('INITIALLY DEFERRED'));
2788
+ output.push('\n' + context.indent('INITIALLY DEFERRED'));
2492
2789
  }
2493
2790
  else if (node.initdeferred === false) {
2494
- output.push('\n' + this.formatter.indent('INITIALLY IMMEDIATE'));
2791
+ output.push('\n' + context.indent('INITIALLY IMMEDIATE'));
2495
2792
  }
2496
2793
  }
2497
2794
  else {
@@ -2505,15 +2802,15 @@ class Deparser {
2505
2802
  }
2506
2803
  }
2507
2804
  else if (node.deferrable === false) {
2508
- if (this.formatter.isPretty() && node.contype === 'CONSTR_FOREIGN') {
2509
- output.push('\n' + this.formatter.indent('NOT DEFERRABLE'));
2805
+ if (context.isPretty() && node.contype === 'CONSTR_FOREIGN') {
2806
+ output.push('\n' + context.indent('NOT DEFERRABLE'));
2510
2807
  }
2511
2808
  else {
2512
2809
  output.push('NOT DEFERRABLE');
2513
2810
  }
2514
2811
  }
2515
2812
  }
2516
- if (this.formatter.isPretty() && node.contype === 'CONSTR_FOREIGN') {
2813
+ if (context.isPretty() && node.contype === 'CONSTR_FOREIGN') {
2517
2814
  let result = '';
2518
2815
  for (let i = 0; i < output.length; i++) {
2519
2816
  if (output[i].startsWith('\n')) {
@@ -2531,12 +2828,12 @@ class Deparser {
2531
2828
  return output.join(' ');
2532
2829
  }
2533
2830
  SubLink(node, context) {
2534
- const subselect = this.formatter.parens(this.visit(node.subselect, context));
2831
+ const subselect = context.parens(this.visit(node.subselect, context));
2535
2832
  switch (node.subLinkType) {
2536
2833
  case 'ANY_SUBLINK':
2537
2834
  if (node.testexpr && node.operName) {
2538
2835
  const testExpr = this.visit(node.testexpr, context);
2539
- const operator = this.deparseOperatorName(node.operName);
2836
+ const operator = this.deparseOperatorName(node.operName, context);
2540
2837
  return `${testExpr} ${operator} ANY ${subselect}`;
2541
2838
  }
2542
2839
  else if (node.testexpr) {
@@ -2547,7 +2844,7 @@ class Deparser {
2547
2844
  case 'ALL_SUBLINK':
2548
2845
  if (node.testexpr && node.operName) {
2549
2846
  const testExpr = this.visit(node.testexpr, context);
2550
- const operator = this.deparseOperatorName(node.operName);
2847
+ const operator = this.deparseOperatorName(node.operName, context);
2551
2848
  return `${testExpr} ${operator} ALL ${subselect}`;
2552
2849
  }
2553
2850
  return subselect;
@@ -2589,7 +2886,7 @@ class Deparser {
2589
2886
  }
2590
2887
  // Only add frame clause if frameOptions indicates non-default framing
2591
2888
  if (node.frameOptions && node.frameOptions !== 1058) {
2592
- const frameClause = this.formatWindowFrame(node);
2889
+ const frameClause = this.formatWindowFrame(node, context.spawn('WindowDef'));
2593
2890
  if (frameClause) {
2594
2891
  windowParts.push(frameClause);
2595
2892
  }
@@ -2609,7 +2906,7 @@ class Deparser {
2609
2906
  }
2610
2907
  return output.join(' ');
2611
2908
  }
2612
- formatWindowFrame(node) {
2909
+ formatWindowFrame(node, context) {
2613
2910
  if (!node.frameOptions)
2614
2911
  return null;
2615
2912
  const frameOptions = node.frameOptions;
@@ -2618,7 +2915,7 @@ class Deparser {
2618
2915
  if (frameOptions & 0x02) { // FRAMEOPTION_RANGE
2619
2916
  frameParts.push('RANGE');
2620
2917
  }
2621
- else if (frameOptions & 0x04) { // FRAMEOPTION_ROWS
2918
+ else if (frameOptions & 0x04) { // FRAMEOPTION_ROWS
2622
2919
  frameParts.push('ROWS');
2623
2920
  }
2624
2921
  else if (frameOptions & 0x08) { // FRAMEOPTION_GROUPS
@@ -2639,8 +2936,8 @@ class Deparser {
2639
2936
  }
2640
2937
  else if (frameOptions === 18453) {
2641
2938
  if (node.startOffset && node.endOffset) {
2642
- boundsParts.push(`${this.visit(node.startOffset, { parentNodeTypes: [] })} PRECEDING`);
2643
- boundsParts.push(`AND ${this.visit(node.endOffset, { parentNodeTypes: [] })} FOLLOWING`);
2939
+ boundsParts.push(`${this.visit(node.startOffset, context)} PRECEDING`);
2940
+ boundsParts.push(`AND ${this.visit(node.endOffset, context)} FOLLOWING`);
2644
2941
  }
2645
2942
  }
2646
2943
  else if (frameOptions === 1557) {
@@ -2650,7 +2947,7 @@ class Deparser {
2650
2947
  else if (frameOptions === 16917) {
2651
2948
  boundsParts.push('CURRENT ROW');
2652
2949
  if (node.endOffset) {
2653
- boundsParts.push(`AND ${this.visit(node.endOffset, { parentNodeTypes: [] })} FOLLOWING`);
2950
+ boundsParts.push(`AND ${this.visit(node.endOffset, context)} FOLLOWING`);
2654
2951
  }
2655
2952
  }
2656
2953
  else if (frameOptions === 1058) {
@@ -2660,13 +2957,13 @@ class Deparser {
2660
2957
  // Handle start bound - prioritize explicit offset values over bit flags
2661
2958
  if (node.startOffset) {
2662
2959
  if (frameOptions & 0x400) { // FRAMEOPTION_START_VALUE_PRECEDING
2663
- boundsParts.push(`${this.visit(node.startOffset, { parentNodeTypes: [] })} PRECEDING`);
2960
+ boundsParts.push(`${this.visit(node.startOffset, context)} PRECEDING`);
2664
2961
  }
2665
2962
  else if (frameOptions & 0x800) { // FRAMEOPTION_START_VALUE_FOLLOWING
2666
- boundsParts.push(`${this.visit(node.startOffset, { parentNodeTypes: [] })} FOLLOWING`);
2963
+ boundsParts.push(`${this.visit(node.startOffset, context)} FOLLOWING`);
2667
2964
  }
2668
2965
  else {
2669
- boundsParts.push(`${this.visit(node.startOffset, { parentNodeTypes: [] })} PRECEDING`);
2966
+ boundsParts.push(`${this.visit(node.startOffset, context)} PRECEDING`);
2670
2967
  }
2671
2968
  }
2672
2969
  else if (frameOptions & 0x10) { // FRAMEOPTION_START_UNBOUNDED_PRECEDING
@@ -2679,13 +2976,13 @@ class Deparser {
2679
2976
  if (node.endOffset) {
2680
2977
  if (boundsParts.length > 0) {
2681
2978
  if (frameOptions & 0x1000) { // FRAMEOPTION_END_VALUE_PRECEDING
2682
- boundsParts.push(`AND ${this.visit(node.endOffset, { parentNodeTypes: [] })} PRECEDING`);
2979
+ boundsParts.push(`AND ${this.visit(node.endOffset, context)} PRECEDING`);
2683
2980
  }
2684
2981
  else if (frameOptions & 0x2000) { // FRAMEOPTION_END_VALUE_FOLLOWING
2685
- boundsParts.push(`AND ${this.visit(node.endOffset, { parentNodeTypes: [] })} FOLLOWING`);
2982
+ boundsParts.push(`AND ${this.visit(node.endOffset, context)} FOLLOWING`);
2686
2983
  }
2687
2984
  else {
2688
- boundsParts.push(`AND ${this.visit(node.endOffset, { parentNodeTypes: [] })} FOLLOWING`);
2985
+ boundsParts.push(`AND ${this.visit(node.endOffset, context)} FOLLOWING`);
2689
2986
  }
2690
2987
  }
2691
2988
  }
@@ -2770,7 +3067,7 @@ class Deparser {
2770
3067
  const colnames = list_utils_1.ListUtils.unwrapList(node.aliascolnames);
2771
3068
  const colnameStrs = colnames.map(col => this.visit(col, context));
2772
3069
  // Don't add space before column list parentheses to match original formatting
2773
- output[output.length - 1] += this.formatter.parens(colnameStrs.join(', '));
3070
+ output[output.length - 1] += context.parens(colnameStrs.join(', '));
2774
3071
  }
2775
3072
  output.push('AS');
2776
3073
  // Handle materialization clauses
@@ -2781,7 +3078,7 @@ class Deparser {
2781
3078
  output.push('MATERIALIZED');
2782
3079
  }
2783
3080
  if (node.ctequery) {
2784
- output.push(this.formatter.parens(this.visit(node.ctequery, context)));
3081
+ output.push(context.parens(this.visit(node.ctequery, context)));
2785
3082
  }
2786
3083
  return output.join(' ');
2787
3084
  }
@@ -2857,7 +3154,7 @@ class Deparser {
2857
3154
  DistinctExpr(node, context) {
2858
3155
  const args = list_utils_1.ListUtils.unwrapList(node.args);
2859
3156
  if (args.length === 2) {
2860
- const literalContext = { ...context, isStringLiteral: true };
3157
+ const literalContext = context.spawn('DistinctExpr', { isStringLiteral: true });
2861
3158
  const left = this.visit(args[0], literalContext);
2862
3159
  const right = this.visit(args[1], literalContext);
2863
3160
  return `${left} IS DISTINCT FROM ${right}`;
@@ -2867,7 +3164,7 @@ class Deparser {
2867
3164
  NullIfExpr(node, context) {
2868
3165
  const args = list_utils_1.ListUtils.unwrapList(node.args);
2869
3166
  if (args.length === 2) {
2870
- const literalContext = { ...context, isStringLiteral: true };
3167
+ const literalContext = context.spawn('NullIfExpr', { isStringLiteral: true });
2871
3168
  const left = this.visit(args[0], literalContext);
2872
3169
  const right = this.visit(args[1], literalContext);
2873
3170
  return `NULLIF(${left}, ${right})`;
@@ -2946,7 +3243,7 @@ class Deparser {
2946
3243
  }
2947
3244
  RelabelType(node, context) {
2948
3245
  if (node.arg) {
2949
- const literalContext = { ...context, isStringLiteral: true };
3246
+ const literalContext = context.spawn('RelabelType', { isStringLiteral: true });
2950
3247
  return this.visit(node.arg, literalContext);
2951
3248
  }
2952
3249
  return '';
@@ -2965,7 +3262,7 @@ class Deparser {
2965
3262
  }
2966
3263
  ConvertRowtypeExpr(node, context) {
2967
3264
  if (node.arg) {
2968
- const literalContext = { ...context, isStringLiteral: true };
3265
+ const literalContext = context.spawn('ConvertRowtypeExpr', { isStringLiteral: true });
2969
3266
  return this.visit(node.arg, literalContext);
2970
3267
  }
2971
3268
  return '';
@@ -2996,10 +3293,10 @@ class Deparser {
2996
3293
  }
2997
3294
  if (node.aliases && node.aliases.length > 0) {
2998
3295
  const aliasStrs = list_utils_1.ListUtils.unwrapList(node.aliases).map(alias => this.visit(alias, context));
2999
- output.push(this.formatter.parens(aliasStrs.join(', ')));
3296
+ output.push(context.parens(aliasStrs.join(', ')));
3000
3297
  }
3001
3298
  if (node.options && node.options.length > 0) {
3002
- const viewContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'ViewStmt'] };
3299
+ const viewContext = context.spawn('ViewStmt');
3003
3300
  const optionStrs = list_utils_1.ListUtils.unwrapList(node.options)
3004
3301
  .map(option => this.visit(option, viewContext));
3005
3302
  output.push(`WITH (${optionStrs.join(', ')})`);
@@ -3046,22 +3343,22 @@ class Deparser {
3046
3343
  }
3047
3344
  if (node.indexParams && node.indexParams.length > 0) {
3048
3345
  const paramStrs = list_utils_1.ListUtils.unwrapList(node.indexParams).map(param => this.visit(param, context));
3049
- output.push(this.formatter.parens(paramStrs.join(', ')));
3346
+ output.push(context.parens(paramStrs.join(', ')));
3050
3347
  }
3051
3348
  if (node.indexIncludingParams && node.indexIncludingParams.length > 0) {
3052
3349
  const includeStrs = list_utils_1.ListUtils.unwrapList(node.indexIncludingParams).map(param => this.visit(param, context));
3053
3350
  output.push('INCLUDE');
3054
- output.push(this.formatter.parens(includeStrs.join(', ')));
3351
+ output.push(context.parens(includeStrs.join(', ')));
3055
3352
  }
3056
3353
  if (node.whereClause) {
3057
3354
  output.push('WHERE');
3058
3355
  output.push(this.visit(node.whereClause, context));
3059
3356
  }
3060
3357
  if (node.options && node.options.length > 0) {
3061
- const indexContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'IndexStmt'] };
3358
+ const indexContext = context.spawn('IndexStmt');
3062
3359
  const optionStrs = list_utils_1.ListUtils.unwrapList(node.options).map(option => this.visit(option, indexContext));
3063
3360
  output.push('WITH');
3064
- output.push(this.formatter.parens(optionStrs.join(', ')));
3361
+ output.push(context.parens(optionStrs.join(', ')));
3065
3362
  }
3066
3363
  if (node.nulls_not_distinct) {
3067
3364
  output.push('NULLS NOT DISTINCT');
@@ -3078,7 +3375,7 @@ class Deparser {
3078
3375
  output.push(quote_utils_1.QuoteUtils.quote(node.name));
3079
3376
  }
3080
3377
  else if (node.expr) {
3081
- output.push(this.formatter.parens(this.visit(node.expr, context)));
3378
+ output.push(context.parens(this.visit(node.expr, context)));
3082
3379
  }
3083
3380
  if (node.collation && node.collation.length > 0) {
3084
3381
  const collationStrs = list_utils_1.ListUtils.unwrapList(node.collation).map(coll => this.visit(coll, context));
@@ -3095,7 +3392,7 @@ class Deparser {
3095
3392
  const stringData = this.getNodeData(opt.DefElem.arg);
3096
3393
  return `${opt.DefElem.defname}='${stringData.sval}'`;
3097
3394
  }
3098
- return this.visit(opt, context);
3395
+ return this.visit(opt, context.spawn('IndexElem'));
3099
3396
  });
3100
3397
  opclassStr += `(${opclassOpts.join(', ')})`;
3101
3398
  }
@@ -3129,7 +3426,7 @@ class Deparser {
3129
3426
  output.push(quote_utils_1.QuoteUtils.quote(node.name));
3130
3427
  }
3131
3428
  else if (node.expr) {
3132
- output.push(this.formatter.parens(this.visit(node.expr, context)));
3429
+ output.push(context.parens(this.visit(node.expr, context)));
3133
3430
  }
3134
3431
  if (node.collation && node.collation.length > 0) {
3135
3432
  const collationStrs = list_utils_1.ListUtils.unwrapList(node.collation).map(coll => this.visit(coll, context));
@@ -3277,16 +3574,16 @@ class Deparser {
3277
3574
  if (node.rarg && 'JoinExpr' in node.rarg && !node.rarg.JoinExpr.alias) {
3278
3575
  rargStr = `(${rargStr})`;
3279
3576
  }
3280
- if (this.formatter.isPretty()) {
3281
- output.push(this.formatter.newline() + joinStr + ' ' + rargStr);
3577
+ if (context.isPretty()) {
3578
+ output.push(context.newline() + joinStr + ' ' + rargStr);
3282
3579
  }
3283
3580
  else {
3284
3581
  output.push(joinStr + ' ' + rargStr);
3285
3582
  }
3286
3583
  }
3287
3584
  else {
3288
- if (this.formatter.isPretty()) {
3289
- output.push(this.formatter.newline() + joinStr);
3585
+ if (context.isPretty()) {
3586
+ output.push(context.newline() + joinStr);
3290
3587
  }
3291
3588
  else {
3292
3589
  output.push(joinStr);
@@ -3295,7 +3592,7 @@ class Deparser {
3295
3592
  if (node.usingClause && node.usingClause.length > 0) {
3296
3593
  const usingList = list_utils_1.ListUtils.unwrapList(node.usingClause);
3297
3594
  const columnNames = usingList.map(col => this.visit(col, context));
3298
- if (this.formatter.isPretty()) {
3595
+ if (context.isPretty()) {
3299
3596
  output.push(` USING (${columnNames.join(', ')})`);
3300
3597
  }
3301
3598
  else {
@@ -3304,14 +3601,14 @@ class Deparser {
3304
3601
  }
3305
3602
  else if (node.quals) {
3306
3603
  const qualsStr = this.visit(node.quals, context);
3307
- if (this.formatter.isPretty()) {
3604
+ if (context.isPretty()) {
3308
3605
  // For complex JOIN conditions, format with proper indentation
3309
3606
  if (qualsStr.includes('AND') || qualsStr.includes('OR') || qualsStr.length > 50) {
3310
3607
  if (this.containsMultilineStringLiteral(qualsStr)) {
3311
3608
  output.push(` ON ${qualsStr}`);
3312
3609
  }
3313
3610
  else {
3314
- output.push(` ON${this.formatter.newline()}${this.formatter.indent(qualsStr)}`);
3611
+ output.push(` ON${context.newline()}${context.indent(qualsStr)}`);
3315
3612
  }
3316
3613
  }
3317
3614
  else {
@@ -3323,7 +3620,7 @@ class Deparser {
3323
3620
  }
3324
3621
  }
3325
3622
  let result;
3326
- if (this.formatter.isPretty()) {
3623
+ if (context.isPretty()) {
3327
3624
  result = output.join('');
3328
3625
  }
3329
3626
  else {
@@ -3430,8 +3727,8 @@ class Deparser {
3430
3727
  else if (nodeData.sval !== undefined) {
3431
3728
  // Handle nested sval structure: { sval: { sval: "value" } }
3432
3729
  const svalValue = typeof nodeData.sval === 'object' ? nodeData.sval.sval : nodeData.sval;
3433
- const stringValue = svalValue.replace(/'/g, '').toLowerCase();
3434
- boolValue = stringValue === 'on' || stringValue === 'true';
3730
+ const stringValue = svalValue.replace(/'/g, '');
3731
+ boolValue = stringValue.toLowerCase() === 'on' || stringValue.toLowerCase() === 'true';
3435
3732
  }
3436
3733
  }
3437
3734
  return boolValue ? 'READ ONLY' : 'READ WRITE';
@@ -3454,8 +3751,8 @@ class Deparser {
3454
3751
  else if (nodeData.sval !== undefined) {
3455
3752
  // Handle nested sval structure: { sval: { sval: "value" } }
3456
3753
  const svalValue = typeof nodeData.sval === 'object' ? nodeData.sval.sval : nodeData.sval;
3457
- const stringValue = svalValue.replace(/'/g, '').toLowerCase();
3458
- boolValue = stringValue === 'on' || stringValue === 'true';
3754
+ const stringValue = svalValue.replace(/'/g, '');
3755
+ boolValue = stringValue.toLowerCase() === 'on' || stringValue.toLowerCase() === 'true';
3459
3756
  }
3460
3757
  }
3461
3758
  return boolValue ? 'DEFERRABLE' : 'NOT DEFERRABLE';
@@ -3522,8 +3819,8 @@ class Deparser {
3522
3819
  else if (nodeData.sval !== undefined) {
3523
3820
  // Handle nested sval structure: { sval: { sval: "value" } }
3524
3821
  const svalValue = typeof nodeData.sval === 'object' ? nodeData.sval.sval : nodeData.sval;
3525
- const stringValue = svalValue.replace(/'/g, '').toLowerCase();
3526
- boolValue = stringValue === 'on' || stringValue === 'true';
3822
+ const stringValue = svalValue.replace(/'/g, '');
3823
+ boolValue = stringValue.toLowerCase() === 'on' || stringValue.toLowerCase() === 'true';
3527
3824
  }
3528
3825
  }
3529
3826
  transactionOptions.push(boolValue ? 'READ ONLY' : 'READ WRITE');
@@ -3541,8 +3838,8 @@ class Deparser {
3541
3838
  else if (nodeData.sval !== undefined) {
3542
3839
  // Handle nested sval structure: { sval: { sval: "value" } }
3543
3840
  const svalValue = typeof nodeData.sval === 'object' ? nodeData.sval.sval : nodeData.sval;
3544
- const stringValue = svalValue.replace(/'/g, '').toLowerCase();
3545
- boolValue = stringValue === 'on' || stringValue === 'true';
3841
+ const stringValue = svalValue.replace(/'/g, '');
3842
+ boolValue = stringValue.toLowerCase() === 'on' || stringValue.toLowerCase() === 'true';
3546
3843
  }
3547
3844
  }
3548
3845
  transactionOptions.push(boolValue ? 'DEFERRABLE' : 'NOT DEFERRABLE');
@@ -3608,7 +3905,7 @@ class Deparser {
3608
3905
  }
3609
3906
  switch (node.roletype) {
3610
3907
  case 'ROLESPEC_PUBLIC':
3611
- return 'public';
3908
+ return 'PUBLIC';
3612
3909
  case 'ROLESPEC_CURRENT_USER':
3613
3910
  return 'CURRENT_USER';
3614
3911
  case 'ROLESPEC_SESSION_USER':
@@ -3616,7 +3913,7 @@ class Deparser {
3616
3913
  case 'ROLESPEC_CURRENT_ROLE':
3617
3914
  return 'CURRENT_ROLE';
3618
3915
  default:
3619
- return 'public';
3916
+ return 'PUBLIC';
3620
3917
  }
3621
3918
  }
3622
3919
  roletype(node, context) {
@@ -3926,7 +4223,7 @@ class Deparser {
3926
4223
  }).filter((name) => name && name.trim());
3927
4224
  return items.join('.');
3928
4225
  }
3929
- const objContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'DropStmt'], objtype: node.removeType };
4226
+ const objContext = context.spawn('DropStmt', { objtype: node.removeType });
3930
4227
  const objName = this.visit(objList, objContext);
3931
4228
  return objName;
3932
4229
  }).filter((name) => name && name.trim()).join(', ');
@@ -4026,7 +4323,7 @@ class Deparser {
4026
4323
  if (node.options && node.options.length > 0) {
4027
4324
  output.push('WITH');
4028
4325
  const optionsStr = list_utils_1.ListUtils.unwrapList(node.options)
4029
- .map(opt => this.visit(opt, context))
4326
+ .map(opt => this.visit(opt, context.spawn('CopyStmt')))
4030
4327
  .join(', ');
4031
4328
  output.push(`(${optionsStr})`);
4032
4329
  }
@@ -4072,18 +4369,28 @@ class Deparser {
4072
4369
  if (node.missing_ok) {
4073
4370
  output.push('IF EXISTS');
4074
4371
  }
4075
- const alterContext = node.objtype === 'OBJECT_TYPE'
4076
- ? { ...context, parentNodeTypes: [...context.parentNodeTypes, 'AlterTypeStmt'] }
4077
- : context;
4372
+ const alterContext = context.spawn('AlterTableStmt', { objtype: node.objtype });
4078
4373
  if (node.relation) {
4079
4374
  const relationStr = this.RangeVar(node.relation, alterContext);
4080
4375
  output.push(relationStr);
4081
4376
  }
4082
4377
  if (node.cmds && node.cmds.length > 0) {
4083
- const commandsStr = list_utils_1.ListUtils.unwrapList(node.cmds)
4084
- .map(cmd => this.visit(cmd, alterContext))
4085
- .join(', ');
4086
- output.push(commandsStr);
4378
+ const commands = list_utils_1.ListUtils.unwrapList(node.cmds);
4379
+ if (context.isPretty()) {
4380
+ const commandsStr = commands
4381
+ .map(cmd => {
4382
+ const cmdStr = this.visit(cmd, alterContext);
4383
+ return context.newline() + context.indent(cmdStr);
4384
+ })
4385
+ .join(',');
4386
+ output.push(commandsStr);
4387
+ }
4388
+ else {
4389
+ const commandsStr = commands
4390
+ .map(cmd => this.visit(cmd, alterContext))
4391
+ .join(', ');
4392
+ output.push(commandsStr);
4393
+ }
4087
4394
  }
4088
4395
  return output.join(' ');
4089
4396
  }
@@ -4092,7 +4399,7 @@ class Deparser {
4092
4399
  if (node.subtype) {
4093
4400
  switch (node.subtype) {
4094
4401
  case 'AT_AddColumn':
4095
- if (context.parentNodeTypes.includes('AlterTypeStmt')) {
4402
+ if (context.objtype === 'OBJECT_TYPE') {
4096
4403
  output.push('ADD ATTRIBUTE');
4097
4404
  }
4098
4405
  else {
@@ -4103,35 +4410,99 @@ class Deparser {
4103
4410
  }
4104
4411
  if (node.def) {
4105
4412
  const colDefData = this.getNodeData(node.def);
4106
- const parts = [];
4107
- if (colDefData.colname) {
4108
- parts.push(quote_utils_1.QuoteUtils.quote(colDefData.colname));
4109
- }
4110
- if (colDefData.typeName) {
4111
- parts.push(this.TypeName(colDefData.typeName, context));
4112
- }
4113
- if (colDefData.fdwoptions && colDefData.fdwoptions.length > 0) {
4114
- parts.push('OPTIONS');
4115
- const columnContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'ColumnDef'] };
4116
- const options = list_utils_1.ListUtils.unwrapList(colDefData.fdwoptions).map(opt => this.visit(opt, columnContext));
4117
- parts.push(`(${options.join(', ')})`);
4118
- }
4119
- if (colDefData.constraints) {
4120
- const constraints = list_utils_1.ListUtils.unwrapList(colDefData.constraints);
4121
- const constraintStrs = constraints.map(constraint => {
4122
- const columnConstraintContext = { ...context, isColumnConstraint: true };
4123
- return this.visit(constraint, columnConstraintContext);
4124
- });
4125
- parts.push(...constraintStrs);
4126
- }
4127
- if (colDefData.raw_default) {
4128
- parts.push('DEFAULT');
4129
- parts.push(this.visit(colDefData.raw_default, context));
4413
+ if (context.isPretty()) {
4414
+ const parts = [];
4415
+ const indentedParts = [];
4416
+ if (colDefData.colname) {
4417
+ parts.push(quote_utils_1.QuoteUtils.quote(colDefData.colname));
4418
+ }
4419
+ if (colDefData.typeName) {
4420
+ parts.push(this.TypeName(colDefData.typeName, context));
4421
+ }
4422
+ if (colDefData.is_not_null) {
4423
+ indentedParts.push('NOT NULL');
4424
+ }
4425
+ if (colDefData.collClause) {
4426
+ indentedParts.push(this.CollateClause(colDefData.collClause, context));
4427
+ }
4428
+ if (colDefData.constraints) {
4429
+ const constraints = list_utils_1.ListUtils.unwrapList(colDefData.constraints);
4430
+ constraints.forEach(constraint => {
4431
+ const columnConstraintContext = context.spawn('ColumnDef', { isColumnConstraint: true });
4432
+ const constraintStr = this.visit(constraint, columnConstraintContext);
4433
+ if (constraintStr.includes('REFERENCES') && constraintStr.includes('ON DELETE')) {
4434
+ const refMatch = constraintStr.match(/^(.*REFERENCES[^)]*\([^)]*\))\s*(ON\s+DELETE\s+CASCADE.*)$/);
4435
+ if (refMatch) {
4436
+ indentedParts.push(refMatch[1]);
4437
+ indentedParts.push(refMatch[2]);
4438
+ }
4439
+ else {
4440
+ indentedParts.push(constraintStr);
4441
+ }
4442
+ }
4443
+ else if (constraintStr === 'UNIQUE' && colDefData.raw_default) {
4444
+ const defaultStr = 'DEFAULT ' + this.visit(colDefData.raw_default, context);
4445
+ indentedParts.push('UNIQUE ' + defaultStr);
4446
+ }
4447
+ else {
4448
+ indentedParts.push(constraintStr);
4449
+ }
4450
+ });
4451
+ }
4452
+ if (colDefData.raw_default && !colDefData.constraints?.some((c) => {
4453
+ const constraintStr = this.visit(c, context.spawn('ColumnDef', { isColumnConstraint: true }));
4454
+ return constraintStr === 'UNIQUE';
4455
+ })) {
4456
+ const defaultStr = 'DEFAULT ' + this.visit(colDefData.raw_default, context);
4457
+ indentedParts.push(defaultStr);
4458
+ }
4459
+ if (colDefData.fdwoptions && colDefData.fdwoptions.length > 0) {
4460
+ indentedParts.push('OPTIONS');
4461
+ const columnContext = context.spawn('ColumnDef');
4462
+ const options = list_utils_1.ListUtils.unwrapList(colDefData.fdwoptions).map(opt => this.visit(opt, columnContext));
4463
+ indentedParts.push(`(${options.join(', ')})`);
4464
+ }
4465
+ let result = parts.join(' ');
4466
+ if (indentedParts.length > 0) {
4467
+ const indentedStr = indentedParts.map(part => context.indent(part)).join(context.newline());
4468
+ result += context.newline() + indentedStr;
4469
+ }
4470
+ output.push(result);
4130
4471
  }
4131
- if (colDefData.is_not_null) {
4132
- parts.push('NOT NULL');
4472
+ else {
4473
+ const parts = [];
4474
+ if (colDefData.colname) {
4475
+ parts.push(quote_utils_1.QuoteUtils.quote(colDefData.colname));
4476
+ }
4477
+ if (colDefData.typeName) {
4478
+ parts.push(this.TypeName(colDefData.typeName, context));
4479
+ }
4480
+ if (colDefData.collClause) {
4481
+ parts.push(this.CollateClause(colDefData.collClause, context));
4482
+ }
4483
+ if (colDefData.fdwoptions && colDefData.fdwoptions.length > 0) {
4484
+ parts.push('OPTIONS');
4485
+ const columnContext = context.spawn('ColumnDef');
4486
+ const options = list_utils_1.ListUtils.unwrapList(colDefData.fdwoptions).map(opt => this.visit(opt, columnContext));
4487
+ parts.push(`(${options.join(', ')})`);
4488
+ }
4489
+ if (colDefData.constraints) {
4490
+ const constraints = list_utils_1.ListUtils.unwrapList(colDefData.constraints);
4491
+ const constraintStrs = constraints.map(constraint => {
4492
+ const columnConstraintContext = context.spawn('ColumnDef', { isColumnConstraint: true });
4493
+ return this.visit(constraint, columnConstraintContext);
4494
+ });
4495
+ parts.push(...constraintStrs);
4496
+ }
4497
+ if (colDefData.raw_default) {
4498
+ parts.push('DEFAULT');
4499
+ parts.push(this.visit(colDefData.raw_default, context));
4500
+ }
4501
+ if (colDefData.is_not_null) {
4502
+ parts.push('NOT NULL');
4503
+ }
4504
+ output.push(parts.join(' '));
4133
4505
  }
4134
- output.push(parts.join(' '));
4135
4506
  }
4136
4507
  if (node.behavior === 'DROP_CASCADE') {
4137
4508
  output.push('CASCADE');
@@ -4139,7 +4510,7 @@ class Deparser {
4139
4510
  break;
4140
4511
  case 'AT_DropColumn':
4141
4512
  if (node.missing_ok) {
4142
- if (context.parentNodeTypes.includes('AlterTypeStmt')) {
4513
+ if (context.objtype === 'OBJECT_TYPE') {
4143
4514
  output.push('DROP ATTRIBUTE IF EXISTS');
4144
4515
  }
4145
4516
  else {
@@ -4147,7 +4518,7 @@ class Deparser {
4147
4518
  }
4148
4519
  }
4149
4520
  else {
4150
- if (context.parentNodeTypes.includes('AlterTypeStmt')) {
4521
+ if (context.objtype === 'OBJECT_TYPE') {
4151
4522
  output.push('DROP ATTRIBUTE');
4152
4523
  }
4153
4524
  else {
@@ -4165,7 +4536,7 @@ class Deparser {
4165
4536
  }
4166
4537
  break;
4167
4538
  case 'AT_AlterColumnType':
4168
- if (context.parentNodeTypes.includes('AlterTypeStmt')) {
4539
+ if (context.objtype === 'OBJECT_TYPE') {
4169
4540
  output.push('ALTER ATTRIBUTE');
4170
4541
  }
4171
4542
  else {
@@ -4229,7 +4600,7 @@ class Deparser {
4229
4600
  case 'AT_SetRelOptions':
4230
4601
  output.push('SET');
4231
4602
  if (node.def) {
4232
- const alterTableContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'AlterTableCmd'], subtype: 'AT_SetRelOptions' };
4603
+ const alterTableContext = context.spawn('AlterTableCmd', { subtype: 'AT_SetRelOptions' });
4233
4604
  const options = list_utils_1.ListUtils.unwrapList(node.def)
4234
4605
  .map(option => this.visit(option, alterTableContext))
4235
4606
  .join(', ');
@@ -4242,7 +4613,7 @@ class Deparser {
4242
4613
  case 'AT_ResetRelOptions':
4243
4614
  output.push('RESET');
4244
4615
  if (node.def) {
4245
- const alterTableContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'AlterTableCmd'], subtype: 'AT_ResetRelOptions' };
4616
+ const alterTableContext = context.spawn('AlterTableCmd', { subtype: 'AT_ResetRelOptions' });
4246
4617
  const options = list_utils_1.ListUtils.unwrapList(node.def)
4247
4618
  .map(option => this.visit(option, alterTableContext))
4248
4619
  .join(', ');
@@ -4337,7 +4708,7 @@ class Deparser {
4337
4708
  }
4338
4709
  output.push('SET');
4339
4710
  if (node.def) {
4340
- const alterTableContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'AlterTableCmd'], subtype: 'AT_SetOptions' };
4711
+ const alterTableContext = context.spawn('AlterTableCmd', { subtype: 'AT_SetOptions' });
4341
4712
  const options = list_utils_1.ListUtils.unwrapList(node.def)
4342
4713
  .map(option => this.visit(option, alterTableContext))
4343
4714
  .join(', ');
@@ -4354,7 +4725,7 @@ class Deparser {
4354
4725
  }
4355
4726
  output.push('RESET');
4356
4727
  if (node.def) {
4357
- const alterTableContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'AlterTableCmd'], subtype: 'AT_ResetOptions' };
4728
+ const alterTableContext = context.spawn('AlterTableCmd', { subtype: 'AT_ResetOptions' });
4358
4729
  const options = list_utils_1.ListUtils.unwrapList(node.def)
4359
4730
  .map(option => this.visit(option, alterTableContext))
4360
4731
  .join(', ');
@@ -4595,7 +4966,7 @@ class Deparser {
4595
4966
  }
4596
4967
  output.push('OPTIONS');
4597
4968
  if (node.def) {
4598
- const alterColumnContext = { ...context, alterColumnOptions: true };
4969
+ const alterColumnContext = context.spawn('AlterTableCmd', { alterColumnOptions: true });
4599
4970
  const options = list_utils_1.ListUtils.unwrapList(node.def)
4600
4971
  .map(option => this.visit(option, alterColumnContext))
4601
4972
  .join(', ');
@@ -4635,7 +5006,7 @@ class Deparser {
4635
5006
  case 'AT_GenericOptions':
4636
5007
  output.push('OPTIONS');
4637
5008
  if (node.def) {
4638
- const alterTableContext = { ...context, alterTableOptions: true };
5009
+ const alterTableContext = context.spawn('AlterTableCmd', { alterTableOptions: true });
4639
5010
  const options = list_utils_1.ListUtils.unwrapList(node.def)
4640
5011
  .map(option => this.visit(option, alterTableContext))
4641
5012
  .join(', ');
@@ -4647,11 +5018,10 @@ class Deparser {
4647
5018
  if (node.name) {
4648
5019
  output.push(quote_utils_1.QuoteUtils.quote(node.name));
4649
5020
  }
4650
- output.push('ADD GENERATED');
5021
+ output.push('ADD');
4651
5022
  if (node.def) {
4652
5023
  output.push(this.visit(node.def, context));
4653
5024
  }
4654
- output.push('AS IDENTITY');
4655
5025
  break;
4656
5026
  case 'AT_SetIdentity':
4657
5027
  output.push('ALTER COLUMN');
@@ -4739,7 +5109,7 @@ class Deparser {
4739
5109
  output.push(this.TypeName(node.returnType, context));
4740
5110
  }
4741
5111
  if (node.options && node.options.length > 0) {
4742
- const funcContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'CreateFunctionStmt'] };
5112
+ const funcContext = context.spawn('CreateFunctionStmt');
4743
5113
  const options = node.options.map((opt) => this.visit(opt, funcContext));
4744
5114
  output.push(...options);
4745
5115
  }
@@ -4826,7 +5196,7 @@ class Deparser {
4826
5196
  }
4827
5197
  output.push('AS', 'ENUM');
4828
5198
  if (node.vals && node.vals.length > 0) {
4829
- const enumContext = { ...context, isEnumValue: true };
5199
+ const enumContext = context.spawn('CreateEnumStmt', { isEnumValue: true });
4830
5200
  const values = list_utils_1.ListUtils.unwrapList(node.vals)
4831
5201
  .map(val => this.visit(val, enumContext))
4832
5202
  .join(', ');
@@ -4881,9 +5251,8 @@ class Deparser {
4881
5251
  output.push(roleName);
4882
5252
  }
4883
5253
  if (node.options) {
4884
- const roleContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'CreateRoleStmt'] };
4885
5254
  const options = list_utils_1.ListUtils.unwrapList(node.options)
4886
- .map(option => this.visit(option, roleContext))
5255
+ .map(option => this.visit(option, context.spawn('CreateRoleStmt')))
4887
5256
  .join(' ');
4888
5257
  if (options) {
4889
5258
  output.push('WITH');
@@ -4914,7 +5283,7 @@ class Deparser {
4914
5283
  const stringData = this.getNodeData(node.arg);
4915
5284
  return `${node.defname}='${stringData.sval}'`;
4916
5285
  }
4917
- return `${node.defname}=${this.visit(node.arg, { ...context, parentNodeTypes: [...context.parentNodeTypes, 'DefElem'] })}`;
5286
+ return `${node.defname}=${this.visit(node.arg, context.spawn('DefElem'))}`;
4918
5287
  }
4919
5288
  // Handle CREATE OPERATOR boolean flags - MUST be first to preserve case
4920
5289
  if (context.parentNodeTypes.includes('DefineStmt') &&
@@ -4930,13 +5299,13 @@ class Deparser {
4930
5299
  if (!node.arg) {
4931
5300
  return `NO ${node.defname.toUpperCase()}`;
4932
5301
  }
4933
- const defElemContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'DefElem'] };
5302
+ const defElemContext = context.spawn('DefElem');
4934
5303
  const argValue = this.visit(node.arg, defElemContext);
4935
5304
  return `${node.defname.toUpperCase()} ${argValue}`;
4936
5305
  }
4937
5306
  // Handle OPTIONS clause - use space format, not equals format
4938
5307
  if (node.arg) {
4939
- const defElemContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'DefElem'] };
5308
+ const defElemContext = context.spawn('DefElem');
4940
5309
  const argValue = this.visit(node.arg, defElemContext);
4941
5310
  if (context.parentNodeTypes.includes('CreateFdwStmt') || context.parentNodeTypes.includes('AlterFdwStmt')) {
4942
5311
  const finalValue = typeof argValue === 'string' && !argValue.startsWith("'")
@@ -4990,7 +5359,7 @@ class Deparser {
4990
5359
  if (!node.arg) {
4991
5360
  return 'PASSWORD NULL';
4992
5361
  }
4993
- const defElemContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'DefElem'] };
5362
+ const defElemContext = context.spawn('DefElem');
4994
5363
  const argValue = this.visit(node.arg, defElemContext);
4995
5364
  const quotedValue = typeof argValue === 'string' && !argValue.startsWith("'")
4996
5365
  ? `'${argValue}'`
@@ -4999,7 +5368,7 @@ class Deparser {
4999
5368
  }
5000
5369
  }
5001
5370
  if (node.arg) {
5002
- const defElemContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'DefElem'] };
5371
+ const defElemContext = context.spawn('DefElem');
5003
5372
  const argValue = this.visit(node.arg, defElemContext);
5004
5373
  if (context.parentNodeTypes.includes('AlterOperatorStmt')) {
5005
5374
  if (node.arg && this.getNodeType(node.arg) === 'TypeName') {
@@ -5075,7 +5444,7 @@ class Deparser {
5075
5444
  if (node.defname === 'sysid') {
5076
5445
  return `SYSID ${argValue}`;
5077
5446
  }
5078
- if (argValue === 'true') {
5447
+ if (String(argValue) === 'true') {
5079
5448
  // Handle special cases where the positive form has a different name
5080
5449
  if (node.defname === 'isreplication') {
5081
5450
  return 'REPLICATION';
@@ -5085,7 +5454,7 @@ class Deparser {
5085
5454
  }
5086
5455
  return node.defname.toUpperCase();
5087
5456
  }
5088
- else if (argValue === 'false') {
5457
+ else if (String(argValue) === 'false') {
5089
5458
  // Handle special cases where the negative form has a different name
5090
5459
  if (node.defname === 'canlogin') {
5091
5460
  return 'NOLOGIN';
@@ -5149,7 +5518,7 @@ class Deparser {
5149
5518
  }
5150
5519
  if (context.parentNodeTypes.includes('DoStmt')) {
5151
5520
  if (node.defname === 'as') {
5152
- const defElemContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'DefElem'] };
5521
+ const defElemContext = context.spawn('DefElem');
5153
5522
  const argValue = node.arg ? this.visit(node.arg, defElemContext) : '';
5154
5523
  if (Array.isArray(argValue)) {
5155
5524
  const bodyParts = argValue;
@@ -5440,7 +5809,7 @@ class Deparser {
5440
5809
  }
5441
5810
  if (node.options && node.options.length > 0) {
5442
5811
  output.push('WITH');
5443
- const tsContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'CreateTableSpaceStmt'] };
5812
+ const tsContext = context.spawn('CreateTableSpaceStmt');
5444
5813
  const options = list_utils_1.ListUtils.unwrapList(node.options)
5445
5814
  .map(option => this.visit(option, tsContext))
5446
5815
  .join(', ');
@@ -5470,7 +5839,7 @@ class Deparser {
5470
5839
  output.push('SET');
5471
5840
  }
5472
5841
  if (node.options && node.options.length > 0) {
5473
- const tablespaceContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'AlterTableSpaceOptionsStmt'] };
5842
+ const tablespaceContext = context.spawn('AlterTableSpaceOptionsStmt');
5474
5843
  const options = list_utils_1.ListUtils.unwrapList(node.options)
5475
5844
  .map(option => this.visit(option, tablespaceContext))
5476
5845
  .join(', ');
@@ -5487,7 +5856,7 @@ class Deparser {
5487
5856
  output.push(this.quoteIfNeeded(node.extname));
5488
5857
  }
5489
5858
  if (node.options && node.options.length > 0) {
5490
- const extContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'CreateExtensionStmt'] };
5859
+ const extContext = context.spawn('CreateExtensionStmt');
5491
5860
  const options = list_utils_1.ListUtils.unwrapList(node.options)
5492
5861
  .map(option => this.visit(option, extContext))
5493
5862
  .join(' ');
@@ -5501,7 +5870,7 @@ class Deparser {
5501
5870
  output.push(this.quoteIfNeeded(node.extname));
5502
5871
  }
5503
5872
  if (node.options && node.options.length > 0) {
5504
- const extContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'AlterExtensionStmt'] };
5873
+ const extContext = context.spawn('AlterExtensionStmt');
5505
5874
  const options = list_utils_1.ListUtils.unwrapList(node.options)
5506
5875
  .map(option => this.visit(option, extContext))
5507
5876
  .join(' ');
@@ -5515,7 +5884,7 @@ class Deparser {
5515
5884
  output.push(node.fdwname);
5516
5885
  }
5517
5886
  if (node.func_options && node.func_options.length > 0) {
5518
- const fdwContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'CreateFdwStmt'] };
5887
+ const fdwContext = context.spawn('CreateFdwStmt');
5519
5888
  const funcOptions = list_utils_1.ListUtils.unwrapList(node.func_options)
5520
5889
  .map(option => this.visit(option, fdwContext))
5521
5890
  .join(' ');
@@ -5523,7 +5892,7 @@ class Deparser {
5523
5892
  }
5524
5893
  if (node.options && node.options.length > 0) {
5525
5894
  output.push('OPTIONS');
5526
- const fdwContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'CreateFdwStmt'] };
5895
+ const fdwContext = context.spawn('CreateFdwStmt');
5527
5896
  const options = list_utils_1.ListUtils.unwrapList(node.options)
5528
5897
  .map(option => this.visit(option, fdwContext))
5529
5898
  .join(', ');
@@ -5625,7 +5994,7 @@ class Deparser {
5625
5994
  output.push('ADD');
5626
5995
  if (node.def) {
5627
5996
  // Pass domain context to avoid adding constraint names for domain constraints
5628
- const domainContext = { ...context, isDomainConstraint: true };
5997
+ const domainContext = context.spawn('CreateDomainStmt', { isDomainConstraint: true });
5629
5998
  output.push(this.visit(node.def, domainContext));
5630
5999
  }
5631
6000
  break;
@@ -5651,7 +6020,7 @@ class Deparser {
5651
6020
  output.push('ADD');
5652
6021
  if (node.def) {
5653
6022
  // Pass domain context to avoid adding constraint names for domain constraints
5654
- const domainContext = { ...context, isDomainConstraint: true };
6023
+ const domainContext = context.spawn('CreateDomainStmt', { isDomainConstraint: true });
5655
6024
  output.push(this.visit(node.def, domainContext));
5656
6025
  }
5657
6026
  break;
@@ -6013,7 +6382,7 @@ class Deparser {
6013
6382
  output.push(`${operatorName}(${args.join(', ')})`);
6014
6383
  }
6015
6384
  else {
6016
- const objContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'CommentStmt'], objtype: node.objtype };
6385
+ const objContext = context.spawn('CommentStmt', { objtype: node.objtype });
6017
6386
  output.push(this.visit(node.object, objContext));
6018
6387
  }
6019
6388
  }
@@ -6059,13 +6428,13 @@ class Deparser {
6059
6428
  const output = [];
6060
6429
  const initialParts = ['CREATE', 'POLICY'];
6061
6430
  if (node.policy_name) {
6062
- initialParts.push(`"${node.policy_name}"`);
6431
+ initialParts.push(quote_utils_1.QuoteUtils.quote(node.policy_name));
6063
6432
  }
6064
6433
  output.push(initialParts.join(' '));
6065
6434
  // Add ON clause on new line in pretty mode
6066
6435
  if (node.table) {
6067
- if (this.formatter.isPretty()) {
6068
- output.push(this.formatter.newline() + this.formatter.indent(`ON ${this.RangeVar(node.table, context)}`));
6436
+ if (context.isPretty()) {
6437
+ output.push(context.newline() + context.indent(`ON ${this.RangeVar(node.table, context)}`));
6069
6438
  }
6070
6439
  else {
6071
6440
  output.push('ON');
@@ -6074,24 +6443,24 @@ class Deparser {
6074
6443
  }
6075
6444
  // Handle AS RESTRICTIVE/PERMISSIVE clause
6076
6445
  if (node.permissive === undefined) {
6077
- if (this.formatter.isPretty()) {
6078
- output.push(this.formatter.newline() + this.formatter.indent('AS RESTRICTIVE'));
6446
+ if (context.isPretty()) {
6447
+ output.push(context.newline() + context.indent('AS RESTRICTIVE'));
6079
6448
  }
6080
6449
  else {
6081
6450
  output.push('AS', 'RESTRICTIVE');
6082
6451
  }
6083
6452
  }
6084
6453
  else if (node.permissive === true) {
6085
- if (this.formatter.isPretty()) {
6086
- output.push(this.formatter.newline() + this.formatter.indent('AS PERMISSIVE'));
6454
+ if (context.isPretty()) {
6455
+ output.push(context.newline() + context.indent('AS PERMISSIVE'));
6087
6456
  }
6088
6457
  else {
6089
6458
  output.push('AS', 'PERMISSIVE');
6090
6459
  }
6091
6460
  }
6092
6461
  if (node.cmd_name) {
6093
- if (this.formatter.isPretty()) {
6094
- output.push(this.formatter.newline() + this.formatter.indent(`FOR ${node.cmd_name.toUpperCase()}`));
6462
+ if (context.isPretty()) {
6463
+ output.push(context.newline() + context.indent(`FOR ${node.cmd_name.toUpperCase()}`));
6095
6464
  }
6096
6465
  else {
6097
6466
  output.push('FOR', node.cmd_name.toUpperCase());
@@ -6099,8 +6468,8 @@ class Deparser {
6099
6468
  }
6100
6469
  if (node.roles && node.roles.length > 0) {
6101
6470
  const roles = list_utils_1.ListUtils.unwrapList(node.roles).map(role => this.visit(role, context));
6102
- if (this.formatter.isPretty()) {
6103
- output.push(this.formatter.newline() + this.formatter.indent(`TO ${roles.join(', ')}`));
6471
+ if (context.isPretty()) {
6472
+ output.push(context.newline() + context.indent(`TO ${roles.join(', ')}`));
6104
6473
  }
6105
6474
  else {
6106
6475
  output.push('TO');
@@ -6108,11 +6477,11 @@ class Deparser {
6108
6477
  }
6109
6478
  }
6110
6479
  if (node.qual) {
6111
- if (this.formatter.isPretty()) {
6480
+ if (context.isPretty()) {
6112
6481
  const qualExpr = this.visit(node.qual, context);
6113
- output.push(this.formatter.newline() + this.formatter.indent('USING ('));
6114
- output.push(this.formatter.newline() + this.formatter.indent(this.formatter.indent(qualExpr)));
6115
- output.push(this.formatter.newline() + this.formatter.indent(')'));
6482
+ output.push(context.newline() + context.indent('USING ('));
6483
+ output.push(context.newline() + context.indent(context.indent(qualExpr)));
6484
+ output.push(context.newline() + context.indent(')'));
6116
6485
  }
6117
6486
  else {
6118
6487
  output.push('USING');
@@ -6120,23 +6489,23 @@ class Deparser {
6120
6489
  }
6121
6490
  }
6122
6491
  if (node.with_check) {
6123
- if (this.formatter.isPretty()) {
6492
+ if (context.isPretty()) {
6124
6493
  const checkExpr = this.visit(node.with_check, context);
6125
- output.push(this.formatter.newline() + this.formatter.indent('WITH CHECK ('));
6126
- output.push(this.formatter.newline() + this.formatter.indent(this.formatter.indent(checkExpr)));
6127
- output.push(this.formatter.newline() + this.formatter.indent(')'));
6494
+ output.push(context.newline() + context.indent('WITH CHECK ('));
6495
+ output.push(context.newline() + context.indent(context.indent(checkExpr)));
6496
+ output.push(context.newline() + context.indent(')'));
6128
6497
  }
6129
6498
  else {
6130
6499
  output.push('WITH CHECK');
6131
6500
  output.push(`(${this.visit(node.with_check, context)})`);
6132
6501
  }
6133
6502
  }
6134
- return this.formatter.isPretty() ? output.join('') : output.join(' ');
6503
+ return context.isPretty() ? output.join('') : output.join(' ');
6135
6504
  }
6136
6505
  AlterPolicyStmt(node, context) {
6137
6506
  const output = ['ALTER', 'POLICY'];
6138
6507
  if (node.policy_name) {
6139
- output.push(`"${node.policy_name}"`);
6508
+ output.push(quote_utils_1.QuoteUtils.quote(node.policy_name));
6140
6509
  }
6141
6510
  if (node.table) {
6142
6511
  output.push('ON');
@@ -6176,7 +6545,7 @@ class Deparser {
6176
6545
  }
6177
6546
  if (node.options && node.options.length > 0) {
6178
6547
  output.push('OPTIONS');
6179
- const userMappingContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'CreateUserMappingStmt'] };
6548
+ const userMappingContext = context.spawn('CreateUserMappingStmt');
6180
6549
  const options = list_utils_1.ListUtils.unwrapList(node.options).map(opt => this.visit(opt, userMappingContext));
6181
6550
  output.push(`(${options.join(', ')})`);
6182
6551
  }
@@ -6359,7 +6728,7 @@ class Deparser {
6359
6728
  DoStmt(node, context) {
6360
6729
  const output = ['DO'];
6361
6730
  if (node.args && node.args.length > 0) {
6362
- const doContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'DoStmt'] };
6731
+ const doContext = context.spawn('DoStmt');
6363
6732
  const args = list_utils_1.ListUtils.unwrapList(node.args);
6364
6733
  const processedArgs = [];
6365
6734
  for (const arg of args) {
@@ -6664,7 +7033,7 @@ class Deparser {
6664
7033
  ObjectWithArgs(node, context) {
6665
7034
  let result = '';
6666
7035
  if (node.objname && node.objname.length > 0) {
6667
- const objContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'ObjectWithArgs'] };
7036
+ const objContext = context.spawn('ObjectWithArgs');
6668
7037
  const names = list_utils_1.ListUtils.unwrapList(node.objname).map(name => this.visit(name, objContext));
6669
7038
  result = names.join('.');
6670
7039
  }
@@ -6706,7 +7075,7 @@ class Deparser {
6706
7075
  }
6707
7076
  output.push('SET');
6708
7077
  if (node.options && node.options.length > 0) {
6709
- const alterOpContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'AlterOperatorStmt'] };
7078
+ const alterOpContext = context.spawn('AlterOperatorStmt');
6710
7079
  const options = list_utils_1.ListUtils.unwrapList(node.options).map(opt => this.visit(opt, alterOpContext));
6711
7080
  output.push(`(${options.join(', ')})`);
6712
7081
  }
@@ -6718,13 +7087,13 @@ class Deparser {
6718
7087
  output.push(quote_utils_1.QuoteUtils.quote(node.fdwname));
6719
7088
  }
6720
7089
  if (node.func_options && node.func_options.length > 0) {
6721
- const fdwContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'AlterFdwStmt'] };
7090
+ const fdwContext = context.spawn('AlterFdwStmt');
6722
7091
  const funcOptions = list_utils_1.ListUtils.unwrapList(node.func_options).map(opt => this.visit(opt, fdwContext));
6723
7092
  output.push(funcOptions.join(' '));
6724
7093
  }
6725
7094
  if (node.options && node.options.length > 0) {
6726
7095
  output.push('OPTIONS');
6727
- const fdwContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'AlterFdwStmt'] };
7096
+ const fdwContext = context.spawn('AlterFdwStmt');
6728
7097
  const options = list_utils_1.ListUtils.unwrapList(node.options).map(opt => this.visit(opt, fdwContext));
6729
7098
  output.push(`(${options.join(', ')})`);
6730
7099
  }
@@ -6750,7 +7119,7 @@ class Deparser {
6750
7119
  if (node.options && node.options.length > 0) {
6751
7120
  output.push('OPTIONS');
6752
7121
  output.push('(');
6753
- const optionsContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'CreateForeignServerStmt'] };
7122
+ const optionsContext = context.spawn('CreateForeignServerStmt');
6754
7123
  const options = list_utils_1.ListUtils.unwrapList(node.options).map(opt => this.visit(opt, optionsContext));
6755
7124
  output.push(options.join(', '));
6756
7125
  output.push(')');
@@ -6768,7 +7137,7 @@ class Deparser {
6768
7137
  if (node.options && node.options.length > 0) {
6769
7138
  output.push('OPTIONS');
6770
7139
  output.push('(');
6771
- const optionsContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'AlterForeignServerStmt'] };
7140
+ const optionsContext = context.spawn('AlterForeignServerStmt');
6772
7141
  const options = list_utils_1.ListUtils.unwrapList(node.options).map(opt => this.visit(opt, optionsContext));
6773
7142
  output.push(options.join(', '));
6774
7143
  output.push(')');
@@ -6789,7 +7158,7 @@ class Deparser {
6789
7158
  }
6790
7159
  if (node.options && node.options.length > 0) {
6791
7160
  output.push('OPTIONS');
6792
- const userMappingContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'AlterUserMappingStmt'] };
7161
+ const userMappingContext = context.spawn('AlterUserMappingStmt');
6793
7162
  const options = list_utils_1.ListUtils.unwrapList(node.options).map(opt => this.visit(opt, userMappingContext));
6794
7163
  output.push(`(${options.join(', ')})`);
6795
7164
  }
@@ -6849,7 +7218,7 @@ class Deparser {
6849
7218
  output.push(quote_utils_1.QuoteUtils.quote(node.local_schema));
6850
7219
  }
6851
7220
  if (node.options && node.options.length > 0) {
6852
- const importSchemaContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'ImportForeignSchemaStmt'] };
7221
+ const importSchemaContext = context.spawn('ImportForeignSchemaStmt');
6853
7222
  const options = list_utils_1.ListUtils.unwrapList(node.options).map(opt => this.visit(opt, importSchemaContext));
6854
7223
  output.push(`OPTIONS (${options.join(', ')})`);
6855
7224
  }
@@ -6884,7 +7253,7 @@ class Deparser {
6884
7253
  ExplainStmt(node, context) {
6885
7254
  const output = ['EXPLAIN'];
6886
7255
  if (node.options && node.options.length > 0) {
6887
- const explainContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'ExplainStmt'] };
7256
+ const explainContext = context.spawn('ExplainStmt');
6888
7257
  const options = list_utils_1.ListUtils.unwrapList(node.options).map(option => this.visit(option, explainContext));
6889
7258
  output.push(`(${options.join(', ')})`);
6890
7259
  }
@@ -7142,9 +7511,7 @@ class Deparser {
7142
7511
  output.push(this.RangeVar(node.relation, context));
7143
7512
  }
7144
7513
  else if (node.relation) {
7145
- const rangeVarContext = node.relationType === 'OBJECT_TYPE'
7146
- ? { ...context, parentNodeTypes: [...context.parentNodeTypes, 'AlterTypeStmt'] }
7147
- : context;
7514
+ const rangeVarContext = context.spawn('RenameStmt', { objtype: node.relationType });
7148
7515
  // Add ON keyword for policy operations
7149
7516
  if (node.renameType === 'OBJECT_POLICY') {
7150
7517
  output.push('ON');
@@ -7326,7 +7693,7 @@ class Deparser {
7326
7693
  }
7327
7694
  }
7328
7695
  if (node.privileges && node.privileges.length > 0) {
7329
- const privilegeContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'GrantStmt'] };
7696
+ const privilegeContext = context.spawn('GrantStmt');
7330
7697
  const privileges = list_utils_1.ListUtils.unwrapList(node.privileges)
7331
7698
  .map(priv => this.visit(priv, privilegeContext))
7332
7699
  .join(', ');
@@ -7634,7 +8001,12 @@ class Deparser {
7634
8001
  }
7635
8002
  if (node.action) {
7636
8003
  const actionStr = this.GrantStmt(node.action, context);
7637
- output.push(actionStr);
8004
+ if (context.isPretty()) {
8005
+ return output.join(' ') + context.newline() + context.indent(actionStr);
8006
+ }
8007
+ else {
8008
+ output.push(actionStr);
8009
+ }
7638
8010
  }
7639
8011
  return output.join(' ');
7640
8012
  }
@@ -7781,84 +8153,158 @@ class Deparser {
7781
8153
  if (node.trigname) {
7782
8154
  output.push(quote_utils_1.QuoteUtils.quote(node.trigname));
7783
8155
  }
7784
- const timing = [];
7785
- if (node.timing & 2)
7786
- timing.push('BEFORE');
7787
- else if (node.timing & 64)
7788
- timing.push('INSTEAD OF');
7789
- else
7790
- timing.push('AFTER'); // Default timing when no specific timing is set
7791
- output.push(timing.join(' '));
7792
- const events = [];
7793
- if (node.events & 4)
7794
- events.push('INSERT');
7795
- if (node.events & 8)
7796
- events.push('DELETE');
7797
- if (node.events & 16)
7798
- events.push('UPDATE');
7799
- if (node.events & 32)
7800
- events.push('TRUNCATE');
7801
- output.push(events.join(' OR '));
7802
- if (node.columns && node.columns.length > 0) {
7803
- output.push('OF');
7804
- const columnNames = list_utils_1.ListUtils.unwrapList(node.columns)
7805
- .map(col => this.visit(col, context))
7806
- .join(', ');
7807
- output.push(columnNames);
7808
- }
7809
- output.push('ON');
7810
- if (node.relation) {
7811
- output.push(this.RangeVar(node.relation, context));
7812
- }
7813
- if (node.constrrel) {
7814
- output.push('FROM');
7815
- output.push(this.RangeVar(node.constrrel, context));
7816
- }
7817
- if (node.deferrable) {
7818
- output.push('DEFERRABLE');
7819
- }
7820
- if (node.initdeferred) {
7821
- output.push('INITIALLY DEFERRED');
7822
- }
7823
- // Handle REFERENCING clauses
7824
- if (node.transitionRels && node.transitionRels.length > 0) {
7825
- output.push('REFERENCING');
7826
- const transitionClauses = list_utils_1.ListUtils.unwrapList(node.transitionRels)
7827
- .map(rel => this.visit(rel, context))
7828
- .join(' ');
7829
- output.push(transitionClauses);
7830
- }
7831
- if (node.row) {
7832
- output.push('FOR EACH ROW');
7833
- }
7834
- else {
7835
- output.push('FOR EACH STATEMENT');
7836
- }
7837
- if (node.whenClause) {
7838
- output.push('WHEN');
7839
- output.push('(');
7840
- output.push(this.visit(node.whenClause, context));
7841
- output.push(')');
7842
- }
7843
- output.push('EXECUTE');
7844
- if (node.funcname && node.funcname.length > 0) {
7845
- const funcName = list_utils_1.ListUtils.unwrapList(node.funcname)
7846
- .map(name => this.visit(name, context))
7847
- .join('.');
7848
- output.push('FUNCTION', funcName);
7849
- }
7850
- if (node.args && node.args.length > 0) {
7851
- output.push('(');
7852
- const args = list_utils_1.ListUtils.unwrapList(node.args)
7853
- .map(arg => this.visit(arg, context))
7854
- .join(', ');
7855
- output.push(args);
7856
- output.push(')');
8156
+ if (context.isPretty()) {
8157
+ const components = [];
8158
+ const timing = [];
8159
+ if (node.timing & 2)
8160
+ timing.push('BEFORE');
8161
+ else if (node.timing & 64)
8162
+ timing.push('INSTEAD OF');
8163
+ else
8164
+ timing.push('AFTER');
8165
+ const events = [];
8166
+ if (node.events & 4)
8167
+ events.push('INSERT');
8168
+ if (node.events & 8)
8169
+ events.push('DELETE');
8170
+ if (node.events & 16) {
8171
+ let updateStr = 'UPDATE';
8172
+ if (node.columns && node.columns.length > 0) {
8173
+ const columnNames = list_utils_1.ListUtils.unwrapList(node.columns)
8174
+ .map(col => this.visit(col, context))
8175
+ .join(', ');
8176
+ updateStr += ' OF ' + columnNames;
8177
+ }
8178
+ events.push(updateStr);
8179
+ }
8180
+ if (node.events & 32)
8181
+ events.push('TRUNCATE');
8182
+ components.push(context.indent(timing.join(' ') + ' ' + events.join(' OR ')));
8183
+ if (node.relation) {
8184
+ components.push(context.indent('ON ' + this.RangeVar(node.relation, context)));
8185
+ }
8186
+ if (node.transitionRels && node.transitionRels.length > 0) {
8187
+ const transitionClauses = list_utils_1.ListUtils.unwrapList(node.transitionRels)
8188
+ .map(rel => this.visit(rel, context))
8189
+ .join(' ');
8190
+ components.push(context.indent('REFERENCING ' + transitionClauses));
8191
+ }
8192
+ if (node.deferrable) {
8193
+ components.push(context.indent('DEFERRABLE'));
8194
+ }
8195
+ if (node.initdeferred) {
8196
+ components.push(context.indent('INITIALLY DEFERRED'));
8197
+ }
8198
+ if (node.row) {
8199
+ components.push(context.indent('FOR EACH ROW'));
8200
+ }
8201
+ else {
8202
+ components.push(context.indent('FOR EACH STATEMENT'));
8203
+ }
8204
+ if (node.whenClause) {
8205
+ const whenStr = 'WHEN (' + this.visit(node.whenClause, context) + ')';
8206
+ components.push(context.indent(whenStr));
8207
+ }
8208
+ let executeStr = 'EXECUTE';
8209
+ if (node.funcname && node.funcname.length > 0) {
8210
+ const funcName = list_utils_1.ListUtils.unwrapList(node.funcname)
8211
+ .map(name => this.visit(name, context))
8212
+ .join('.');
8213
+ executeStr += ' PROCEDURE ' + funcName;
8214
+ }
8215
+ if (node.args && node.args.length > 0) {
8216
+ const argContext = context.spawn('CreateTrigStmt', { isStringLiteral: true });
8217
+ const args = list_utils_1.ListUtils.unwrapList(node.args)
8218
+ .map(arg => this.visit(arg, argContext))
8219
+ .join(', ');
8220
+ executeStr += '(' + args + ')';
8221
+ }
8222
+ else {
8223
+ executeStr += '()';
8224
+ }
8225
+ components.push(context.indent(executeStr));
8226
+ return output.join(' ') + context.newline() + components.join(context.newline());
7857
8227
  }
7858
8228
  else {
7859
- output.push('()');
8229
+ const timing = [];
8230
+ if (node.timing & 2)
8231
+ timing.push('BEFORE');
8232
+ else if (node.timing & 64)
8233
+ timing.push('INSTEAD OF');
8234
+ else
8235
+ timing.push('AFTER');
8236
+ output.push(timing.join(' '));
8237
+ const events = [];
8238
+ if (node.events & 4)
8239
+ events.push('INSERT');
8240
+ if (node.events & 8)
8241
+ events.push('DELETE');
8242
+ if (node.events & 16)
8243
+ events.push('UPDATE');
8244
+ if (node.events & 32)
8245
+ events.push('TRUNCATE');
8246
+ output.push(events.join(' OR '));
8247
+ if (node.columns && node.columns.length > 0) {
8248
+ output.push('OF');
8249
+ const columnNames = list_utils_1.ListUtils.unwrapList(node.columns)
8250
+ .map(col => this.visit(col, context))
8251
+ .join(', ');
8252
+ output.push(columnNames);
8253
+ }
8254
+ output.push('ON');
8255
+ if (node.relation) {
8256
+ output.push(this.RangeVar(node.relation, context));
8257
+ }
8258
+ if (node.constrrel) {
8259
+ output.push('FROM');
8260
+ output.push(this.RangeVar(node.constrrel, context));
8261
+ }
8262
+ if (node.deferrable) {
8263
+ output.push('DEFERRABLE');
8264
+ }
8265
+ if (node.initdeferred) {
8266
+ output.push('INITIALLY DEFERRED');
8267
+ }
8268
+ if (node.transitionRels && node.transitionRels.length > 0) {
8269
+ output.push('REFERENCING');
8270
+ const transitionClauses = list_utils_1.ListUtils.unwrapList(node.transitionRels)
8271
+ .map(rel => this.visit(rel, context))
8272
+ .join(' ');
8273
+ output.push(transitionClauses);
8274
+ }
8275
+ if (node.row) {
8276
+ output.push('FOR EACH ROW');
8277
+ }
8278
+ else {
8279
+ output.push('FOR EACH STATEMENT');
8280
+ }
8281
+ if (node.whenClause) {
8282
+ output.push('WHEN');
8283
+ output.push('(');
8284
+ output.push(this.visit(node.whenClause, context));
8285
+ output.push(')');
8286
+ }
8287
+ output.push('EXECUTE');
8288
+ if (node.funcname && node.funcname.length > 0) {
8289
+ const funcName = list_utils_1.ListUtils.unwrapList(node.funcname)
8290
+ .map(name => this.visit(name, context))
8291
+ .join('.');
8292
+ output.push('FUNCTION', funcName);
8293
+ }
8294
+ if (node.args && node.args.length > 0) {
8295
+ output.push('(');
8296
+ const argContext = context.spawn('CreateTrigStmt', { isStringLiteral: true });
8297
+ const args = list_utils_1.ListUtils.unwrapList(node.args)
8298
+ .map(arg => this.visit(arg, argContext))
8299
+ .join(', ');
8300
+ output.push(args);
8301
+ output.push(')');
8302
+ }
8303
+ else {
8304
+ output.push('()');
8305
+ }
8306
+ return output.join(' ');
7860
8307
  }
7861
- return output.join(' ');
7862
8308
  }
7863
8309
  TriggerTransition(node, context) {
7864
8310
  const output = [];
@@ -7884,7 +8330,7 @@ class Deparser {
7884
8330
  }
7885
8331
  if (node.whenclause && node.whenclause.length > 0) {
7886
8332
  output.push('WHEN');
7887
- const eventTriggerContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'CreateEventTrigStmt'] };
8333
+ const eventTriggerContext = context.spawn('CreateEventTrigStmt');
7888
8334
  const conditions = list_utils_1.ListUtils.unwrapList(node.whenclause)
7889
8335
  .map(condition => this.visit(condition, eventTriggerContext))
7890
8336
  .join(' AND ');
@@ -8073,7 +8519,7 @@ class Deparser {
8073
8519
  output.push(sequenceName.join('.'));
8074
8520
  }
8075
8521
  if (node.options && node.options.length > 0) {
8076
- const seqContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'CreateSeqStmt'] };
8522
+ const seqContext = context.spawn('CreateSeqStmt');
8077
8523
  const optionStrs = list_utils_1.ListUtils.unwrapList(node.options)
8078
8524
  .filter(option => option != null && this.getNodeType(option) !== 'undefined')
8079
8525
  .map(option => {
@@ -8110,7 +8556,7 @@ class Deparser {
8110
8556
  output.push(sequenceName.join('.'));
8111
8557
  }
8112
8558
  if (node.options && node.options.length > 0) {
8113
- const seqContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'AlterSeqStmt'] };
8559
+ const seqContext = context.spawn('AlterSeqStmt');
8114
8560
  const optionStrs = list_utils_1.ListUtils.unwrapList(node.options)
8115
8561
  .filter(option => option && option !== undefined)
8116
8562
  .map(option => {
@@ -8139,7 +8585,7 @@ class Deparser {
8139
8585
  CompositeTypeStmt(node, context) {
8140
8586
  const output = ['CREATE', 'TYPE'];
8141
8587
  if (node.typevar) {
8142
- const typeContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'CompositeTypeStmt'] };
8588
+ const typeContext = context.spawn('CompositeTypeStmt');
8143
8589
  output.push(this.RangeVar(node.typevar, typeContext));
8144
8590
  }
8145
8591
  output.push('AS');
@@ -8240,7 +8686,7 @@ class Deparser {
8240
8686
  output.push(this.RoleSpec(node.role, context));
8241
8687
  }
8242
8688
  if (node.options) {
8243
- const roleContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'AlterRoleStmt'] };
8689
+ const roleContext = context.spawn('AlterRoleStmt');
8244
8690
  // Handle GROUP operations specially based on action value
8245
8691
  if (isGroupStatement) {
8246
8692
  const roleMembersOption = list_utils_1.ListUtils.unwrapList(node.options).find(option => option.DefElem && option.DefElem.defname === 'rolemembers');
@@ -8436,14 +8882,14 @@ class Deparser {
8436
8882
  AccessPriv(node, context) {
8437
8883
  const output = [];
8438
8884
  if (node.priv_name) {
8439
- output.push(node.priv_name);
8885
+ output.push(node.priv_name.toUpperCase());
8440
8886
  }
8441
8887
  else {
8442
8888
  output.push('ALL');
8443
8889
  }
8444
8890
  if (node.cols && node.cols.length > 0) {
8445
8891
  output.push('(');
8446
- const colContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'AccessPriv'] };
8892
+ const colContext = context.spawn('AccessPriv');
8447
8893
  const columns = list_utils_1.ListUtils.unwrapList(node.cols).map(col => this.visit(col, colContext));
8448
8894
  output.push(columns.join(', '));
8449
8895
  output.push(')');
@@ -8523,7 +8969,7 @@ class Deparser {
8523
8969
  output.push(list_utils_1.ListUtils.unwrapList(node.defnames).map(name => this.visit(name, context)).join('.'));
8524
8970
  }
8525
8971
  if (node.definition && node.definition.length > 0) {
8526
- const defineStmtContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'DefineStmt'] };
8972
+ const defineStmtContext = context.spawn('DefineStmt');
8527
8973
  const definitions = list_utils_1.ListUtils.unwrapList(node.definition).map(def => {
8528
8974
  return this.visit(def, defineStmtContext);
8529
8975
  });
@@ -9009,7 +9455,7 @@ class Deparser {
9009
9455
  const operatorNumber = node.number !== undefined ? node.number : 0;
9010
9456
  output.push(operatorNumber.toString());
9011
9457
  if (node.name) {
9012
- const opClassContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'CreateOpClassItem'] };
9458
+ const opClassContext = context.spawn('CreateOpClassItem');
9013
9459
  output.push(this.ObjectWithArgs(node.name, opClassContext));
9014
9460
  }
9015
9461
  }
@@ -9019,7 +9465,7 @@ class Deparser {
9019
9465
  const functionNumber = node.number !== undefined ? node.number : 0;
9020
9466
  output.push(functionNumber.toString());
9021
9467
  if (node.name) {
9022
- const opClassContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'CreateOpClassItem'] };
9468
+ const opClassContext = context.spawn('CreateOpClassItem');
9023
9469
  output.push(this.ObjectWithArgs(node.name, opClassContext));
9024
9470
  }
9025
9471
  }
@@ -9717,7 +10163,7 @@ class Deparser {
9717
10163
  output.push(this.ObjectWithArgs(node.func, context));
9718
10164
  }
9719
10165
  if (node.actions && node.actions.length > 0) {
9720
- const alterFunctionContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'AlterFunctionStmt'] };
10166
+ const alterFunctionContext = context.spawn('AlterFunctionStmt');
9721
10167
  const actionStrs = list_utils_1.ListUtils.unwrapList(node.actions).map(action => this.visit(action, alterFunctionContext));
9722
10168
  output.push(actionStrs.join(' '));
9723
10169
  }
@@ -9961,7 +10407,7 @@ class Deparser {
9961
10407
  CreateForeignTableStmt(node, context) {
9962
10408
  const output = ['CREATE FOREIGN TABLE'];
9963
10409
  if (node.base && node.base.relation) {
9964
- const relationContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'CreateForeignTableStmt'] };
10410
+ const relationContext = context.spawn('CreateForeignTableStmt');
9965
10411
  // Handle relation node directly as RangeVar since it contains the RangeVar properties
9966
10412
  output.push(this.RangeVar(node.base.relation, relationContext));
9967
10413
  }
@@ -9991,7 +10437,7 @@ class Deparser {
9991
10437
  output.push(quote_utils_1.QuoteUtils.quote(node.servername));
9992
10438
  }
9993
10439
  if (node.options && node.options.length > 0) {
9994
- const foreignTableContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'CreateForeignTableStmt'] };
10440
+ const foreignTableContext = context.spawn('CreateForeignTableStmt');
9995
10441
  const optionStrs = list_utils_1.ListUtils.unwrapList(node.options).map(opt => this.visit(opt, foreignTableContext));
9996
10442
  output.push(`OPTIONS (${optionStrs.join(', ')})`);
9997
10443
  }