pgsql-deparser 17.18.5 → 17.19.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.
@@ -1,10 +1,33 @@
1
1
  "use strict";
2
+ /**
3
+ * Auto-generated file with types stripped for better tree-shaking
4
+ * DO NOT EDIT - Generated by strip-deparser-types.ts
5
+ */
2
6
  Object.defineProperty(exports, "__esModule", { value: true });
3
7
  exports.Deparser = void 0;
4
- const base_1 = require("./visitors/base");
5
- const sql_formatter_1 = require("./utils/sql-formatter");
6
8
  const quotes_1 = require("@pgsql/quotes");
7
9
  const list_utils_1 = require("./utils/list-utils");
10
+ const sql_formatter_1 = require("./utils/sql-formatter");
11
+ const base_1 = require("./visitors/base");
12
+ // WindowDef.frameOptions bit flags (src/include/nodes/parsenodes.h)
13
+ const FRAMEOPTION_NONDEFAULT = 0x00001;
14
+ const FRAMEOPTION_RANGE = 0x00002;
15
+ const FRAMEOPTION_ROWS = 0x00004;
16
+ const FRAMEOPTION_GROUPS = 0x00008;
17
+ const FRAMEOPTION_BETWEEN = 0x00010;
18
+ const FRAMEOPTION_START_UNBOUNDED_PRECEDING = 0x00020;
19
+ const FRAMEOPTION_END_UNBOUNDED_PRECEDING = 0x00040;
20
+ const FRAMEOPTION_START_UNBOUNDED_FOLLOWING = 0x00080;
21
+ const FRAMEOPTION_END_UNBOUNDED_FOLLOWING = 0x00100;
22
+ const FRAMEOPTION_START_CURRENT_ROW = 0x00200;
23
+ const FRAMEOPTION_END_CURRENT_ROW = 0x00400;
24
+ const FRAMEOPTION_START_OFFSET_PRECEDING = 0x00800;
25
+ const FRAMEOPTION_END_OFFSET_PRECEDING = 0x01000;
26
+ const FRAMEOPTION_START_OFFSET_FOLLOWING = 0x02000;
27
+ const FRAMEOPTION_END_OFFSET_FOLLOWING = 0x04000;
28
+ const FRAMEOPTION_EXCLUDE_CURRENT_ROW = 0x08000;
29
+ const FRAMEOPTION_EXCLUDE_GROUP = 0x10000;
30
+ const FRAMEOPTION_EXCLUDE_TIES = 0x20000;
8
31
  /**
9
32
  * List of real PostgreSQL built-in types as they appear in pg_catalog.pg_type.typname.
10
33
  * These are stored in lowercase in PostgreSQL system catalogs.
@@ -103,6 +126,36 @@ function isWrappedParseResult(obj) {
103
126
  * The deparser automatically detects bare ParseResult objects for backward
104
127
  * compatibility and wraps them internally for consistent processing.
105
128
  */
129
+ function formatBitString(bsval) {
130
+ const quote = (value) => value.replace(/'/g, "''");
131
+ if (/^x[0-9A-Fa-f]*$/.test(bsval)) {
132
+ return `x'${bsval.substring(1)}'`;
133
+ }
134
+ if (bsval.startsWith('b')) {
135
+ return `b'${quote(bsval.substring(1))}'`;
136
+ }
137
+ return `b'${quote(bsval)}'`;
138
+ }
139
+ // A dollar-quoted body ends at the first occurrence of its tag, including one
140
+ // formed by the body's trailing characters plus the closing tag (`...$` + `$$`).
141
+ function dollarTagConflicts(body, tag) {
142
+ return (body + tag).indexOf(tag) < body.length;
143
+ }
144
+ const NUMERIC_LITERAL = /^[+-]?(?:0[xX](?:_?[0-9A-Fa-f])+|0[oO](?:_?[0-7])+|0[bB](?:_?[01])+|(?:\d(?:_?\d)*(?:\.(?:\d(?:_?\d)*)?)?|\.\d(?:_?\d)*)(?:[eE][+-]?\d(?:_?\d)*)?)$/;
145
+ function formatInteger(ival) {
146
+ const value = String(ival);
147
+ if (!/^[+-]?\d+$/.test(value)) {
148
+ throw new Error(`Invalid integer literal: ${value}`);
149
+ }
150
+ return value;
151
+ }
152
+ function formatNumeric(fval) {
153
+ const value = String(fval);
154
+ if (!NUMERIC_LITERAL.test(value)) {
155
+ throw new Error(`Invalid numeric literal: ${value}`);
156
+ }
157
+ return value;
158
+ }
106
159
  class Deparser {
107
160
  tree;
108
161
  options;
@@ -162,10 +215,19 @@ class Deparser {
162
215
  */
163
216
  getFunctionDelimiter(body) {
164
217
  const delimiter = this.options.functionDelimiter || '$$';
165
- if (body.includes(delimiter)) {
166
- return this.options.functionDelimiterFallback || '$EOFCODE$';
218
+ if (!dollarTagConflicts(body, delimiter)) {
219
+ return delimiter;
220
+ }
221
+ const fallback = this.options.functionDelimiterFallback || '$EOFCODE$';
222
+ if (!dollarTagConflicts(body, fallback)) {
223
+ return fallback;
224
+ }
225
+ const base = fallback.slice(0, -1);
226
+ let counter = 1;
227
+ while (dollarTagConflicts(body, `${base}${counter}$`)) {
228
+ counter++;
167
229
  }
168
- return delimiter;
230
+ return `${base}${counter}$`;
169
231
  }
170
232
  /**
171
233
  * Maps ObjectType enum values to their corresponding SQL keywords
@@ -368,16 +430,16 @@ class Deparser {
368
430
  const leftStmt = this.SelectStmt(node.larg, context);
369
431
  const rightStmt = this.SelectStmt(node.rarg, context);
370
432
  // Add parentheses if the operand is a set operation OR has ORDER BY/LIMIT clauses OR has WITH clause
371
- const leftNeedsParens = node.larg && ((node.larg.op && node.larg.op !== 'SETOP_NONE') ||
372
- node.larg.sortClause ||
373
- node.larg.limitCount ||
374
- node.larg.limitOffset ||
375
- node.larg.withClause);
376
- const rightNeedsParens = node.rarg && ((node.rarg.op && node.rarg.op !== 'SETOP_NONE') ||
377
- node.rarg.sortClause ||
378
- node.rarg.limitCount ||
379
- node.rarg.limitOffset ||
380
- node.rarg.withClause);
433
+ const leftNeedsParens = node.larg && (((node.larg).op && (node.larg).op !== 'SETOP_NONE') ||
434
+ (node.larg).sortClause ||
435
+ (node.larg).limitCount ||
436
+ (node.larg).limitOffset ||
437
+ (node.larg).withClause);
438
+ const rightNeedsParens = node.rarg && (((node.rarg).op && (node.rarg).op !== 'SETOP_NONE') ||
439
+ (node.rarg).sortClause ||
440
+ (node.rarg).limitCount ||
441
+ (node.rarg).limitOffset ||
442
+ (node.rarg).withClause);
381
443
  if (leftNeedsParens) {
382
444
  output.push(context.parens(leftStmt));
383
445
  }
@@ -442,12 +504,7 @@ class Deparser {
442
504
  // Check if single target is complex - if so, use multiline format
443
505
  if (this.isComplexSelectTarget(targetNode)) {
444
506
  output.push('SELECT' + distinctPart);
445
- if (this.containsMultilineStringLiteral(target)) {
446
- output.push(target);
447
- }
448
- else {
449
- output.push(context.indent(target));
450
- }
507
+ output.push(context.indent(target));
451
508
  }
452
509
  else {
453
510
  output.push('SELECT' + distinctPart + ' ' + target);
@@ -457,9 +514,6 @@ class Deparser {
457
514
  const targetStrings = targetList
458
515
  .map(e => {
459
516
  const targetStr = this.visit(e, context.spawn('SelectStmt', { select: true }));
460
- if (this.containsMultilineStringLiteral(targetStr)) {
461
- return targetStr;
462
- }
463
517
  return context.indent(targetStr);
464
518
  });
465
519
  const formattedTargets = targetStrings.join(',' + context.newline());
@@ -511,9 +565,6 @@ class Deparser {
511
565
  return context.parens(values.join(', '));
512
566
  });
513
567
  const indentedTuples = lists.map(tuple => {
514
- if (this.containsMultilineStringLiteral(tuple)) {
515
- return tuple;
516
- }
517
568
  return context.indent(tuple);
518
569
  });
519
570
  output.push(indentedTuples.join(',\n'));
@@ -533,9 +584,6 @@ class Deparser {
533
584
  const groupItems = groupList
534
585
  .map(e => {
535
586
  const groupStr = this.visit(e, context.spawn('SelectStmt', { group: true, indentLevel: context.indentLevel + 1 }));
536
- if (this.containsMultilineStringLiteral(groupStr)) {
537
- return groupStr;
538
- }
539
587
  return context.indent(groupStr);
540
588
  })
541
589
  .join(',' + context.newline());
@@ -554,12 +602,7 @@ class Deparser {
554
602
  if (context.isPretty()) {
555
603
  output.push('HAVING');
556
604
  const havingStr = this.visit(node.havingClause, context);
557
- if (this.containsMultilineStringLiteral(havingStr)) {
558
- output.push(havingStr);
559
- }
560
- else {
561
- output.push(context.indent(havingStr));
562
- }
605
+ output.push(context.indent(havingStr));
563
606
  }
564
607
  else {
565
608
  output.push('HAVING');
@@ -580,9 +623,6 @@ class Deparser {
580
623
  const sortItems = sortList
581
624
  .map(e => {
582
625
  const sortStr = this.visit(e, context.spawn('SelectStmt', { sort: true, indentLevel: context.indentLevel + 1 }));
583
- if (this.containsMultilineStringLiteral(sortStr)) {
584
- return sortStr;
585
- }
586
626
  return context.indent(sortStr);
587
627
  })
588
628
  .join(',' + context.newline());
@@ -846,15 +886,15 @@ class Deparser {
846
886
  getOperatorPrecedence(operator) {
847
887
  const precedence = {
848
888
  '||': 1, // string concatenation
849
- 'OR': 2, // logical OR
850
- 'AND': 3, // logical AND
851
- 'NOT': 4, // logical NOT
852
- 'IS': 5, // IS NULL, IS NOT NULL, etc.
853
- 'IN': 5, // IN, NOT IN
854
- 'BETWEEN': 5, // BETWEEN, NOT BETWEEN
855
- 'LIKE': 5, // LIKE, ILIKE, SIMILAR TO
856
- 'ILIKE': 5,
857
- 'SIMILAR': 5,
889
+ OR: 2, // logical OR
890
+ AND: 3, // logical AND
891
+ NOT: 4, // logical NOT
892
+ IS: 5, // IS NULL, IS NOT NULL, etc.
893
+ IN: 5, // IN, NOT IN
894
+ BETWEEN: 5, // BETWEEN, NOT BETWEEN
895
+ LIKE: 5, // LIKE, ILIKE, SIMILAR TO
896
+ ILIKE: 5,
897
+ SIMILAR: 5,
858
898
  '<': 6, // comparison operators
859
899
  '<=': 6,
860
900
  '>': 6,
@@ -984,6 +1024,12 @@ class Deparser {
984
1024
  output.push(context.parens(columnNames.join(', ')));
985
1025
  }
986
1026
  }
1027
+ if (node.override === 'OVERRIDING_USER_VALUE') {
1028
+ output.push('OVERRIDING USER VALUE');
1029
+ }
1030
+ else if (node.override === 'OVERRIDING_SYSTEM_VALUE') {
1031
+ output.push('OVERRIDING SYSTEM VALUE');
1032
+ }
987
1033
  if (node.selectStmt) {
988
1034
  output.push(this.visit(node.selectStmt, context));
989
1035
  }
@@ -1029,7 +1075,7 @@ class Deparser {
1029
1075
  if (context.isPretty()) {
1030
1076
  // Put each assignment on its own line for pretty printing
1031
1077
  const indentedTargets = targets.map(target => context.indent(target));
1032
- output.push('\n' + indentedTargets.join(',\n'));
1078
+ output[output.length - 1] += '\n' + indentedTargets.join(',\n');
1033
1079
  }
1034
1080
  else {
1035
1081
  output.push(targets.join(', '));
@@ -1045,11 +1091,8 @@ class Deparser {
1045
1091
  output.push('DO NOTHING');
1046
1092
  }
1047
1093
  }
1048
- if (node.returningList) {
1049
- output.push('RETURNING');
1050
- const returningList = list_utils_1.ListUtils.unwrapList(node.returningList);
1051
- const returns = returningList.map(ret => this.visit(ret, context));
1052
- output.push(returns.join(', '));
1094
+ if (node.returningClause) {
1095
+ output.push(this.ReturningClause(node.returningClause, context));
1053
1096
  }
1054
1097
  return output.join(' ');
1055
1098
  }
@@ -1063,38 +1106,8 @@ class Deparser {
1063
1106
  output.push(this.RangeVar(node.relation, context));
1064
1107
  }
1065
1108
  output.push('SET');
1066
- const targetList = list_utils_1.ListUtils.unwrapList(node.targetList);
1067
- if (targetList && targetList.length) {
1068
- const firstTarget = targetList[0];
1069
- const processedTargets = new Set();
1070
- const assignmentParts = [];
1071
- for (let i = 0; i < targetList.length; i++) {
1072
- if (processedTargets.has(i))
1073
- continue;
1074
- const target = targetList[i];
1075
- const multiAssignRef = target.ResTarget?.val?.MultiAssignRef;
1076
- if (multiAssignRef) {
1077
- const relatedTargets = [];
1078
- for (let j = i; j < targetList.length; j++) {
1079
- const otherTarget = targetList[j];
1080
- const otherMultiAssignRef = otherTarget.ResTarget?.val?.MultiAssignRef;
1081
- if (otherMultiAssignRef &&
1082
- JSON.stringify(otherMultiAssignRef.source) === JSON.stringify(multiAssignRef.source)) {
1083
- relatedTargets.push(otherTarget);
1084
- processedTargets.add(j);
1085
- }
1086
- }
1087
- const names = relatedTargets.map(t => t.ResTarget.name);
1088
- const multiAssignment = `${context.parens(names.join(', '))} = ${this.visit(multiAssignRef.source, context)}`;
1089
- assignmentParts.push(multiAssignment);
1090
- }
1091
- else {
1092
- // Handle regular single-column assignment
1093
- assignmentParts.push(this.visit(target, context.spawn('UpdateStmt', { update: true })));
1094
- processedTargets.add(i);
1095
- }
1096
- }
1097
- output.push(assignmentParts.join(','));
1109
+ if (node.targetList && node.targetList.length > 0) {
1110
+ output.push(this.formatUpdateAssignments(node.targetList, context));
1098
1111
  }
1099
1112
  if (node.fromClause) {
1100
1113
  output.push('FROM');
@@ -1106,12 +1119,41 @@ class Deparser {
1106
1119
  output.push('WHERE');
1107
1120
  output.push(this.visit(node.whereClause, context));
1108
1121
  }
1109
- if (node.returningList) {
1110
- output.push('RETURNING');
1111
- output.push(this.deparseReturningList(node.returningList, context));
1122
+ if (node.returningClause) {
1123
+ output.push(this.ReturningClause(node.returningClause, context));
1112
1124
  }
1113
1125
  return output.join(' ');
1114
1126
  }
1127
+ formatUpdateAssignments(targets, context) {
1128
+ const targetList = list_utils_1.ListUtils.unwrapList(targets);
1129
+ const processedTargets = new Set();
1130
+ const assignmentParts = [];
1131
+ for (let i = 0; i < targetList.length; i++) {
1132
+ if (processedTargets.has(i))
1133
+ continue;
1134
+ const target = targetList[i];
1135
+ const multiAssignRef = target.ResTarget?.val?.MultiAssignRef;
1136
+ if (multiAssignRef) {
1137
+ const relatedTargets = [];
1138
+ for (let j = i; j < targetList.length; j++) {
1139
+ const otherTarget = targetList[j];
1140
+ const otherMultiAssignRef = otherTarget.ResTarget?.val?.MultiAssignRef;
1141
+ if (otherMultiAssignRef &&
1142
+ JSON.stringify(otherMultiAssignRef.source) === JSON.stringify(multiAssignRef.source)) {
1143
+ relatedTargets.push(otherTarget);
1144
+ processedTargets.add(j);
1145
+ }
1146
+ }
1147
+ const names = relatedTargets.map(t => quotes_1.QuoteUtils.quoteIdentifier(t.ResTarget.name));
1148
+ assignmentParts.push(`${context.parens(names.join(', '))} = ${this.visit(multiAssignRef.source, context)}`);
1149
+ }
1150
+ else {
1151
+ assignmentParts.push(this.visit(target, context.spawn('UpdateStmt', { update: true })));
1152
+ processedTargets.add(i);
1153
+ }
1154
+ }
1155
+ return assignmentParts.join(', ');
1156
+ }
1115
1157
  DeleteStmt(node, context) {
1116
1158
  try {
1117
1159
  const output = [];
@@ -1159,13 +1201,12 @@ class Deparser {
1159
1201
  throw new Error(`Error deparsing DeleteStmt: ${error instanceof Error ? error.message : String(error)}`);
1160
1202
  }
1161
1203
  }
1162
- if (node.returningList) {
1163
- output.push('RETURNING');
1204
+ if (node.returningClause) {
1164
1205
  try {
1165
- output.push(this.deparseReturningList(node.returningList, context));
1206
+ output.push(this.ReturningClause(node.returningClause, context));
1166
1207
  }
1167
1208
  catch (error) {
1168
- console.warn(`Error processing returningList in DeleteStmt: ${error instanceof Error ? error.message : String(error)}`);
1209
+ console.warn(`Error processing returningClause in DeleteStmt: ${error instanceof Error ? error.message : String(error)}`);
1169
1210
  throw new Error(`Error deparsing DeleteStmt: ${error instanceof Error ? error.message : String(error)}`);
1170
1211
  }
1171
1212
  }
@@ -1186,9 +1227,6 @@ class Deparser {
1186
1227
  const cteStrings = ctes.map((cte, index) => {
1187
1228
  const cteStr = this.visit(cte, context);
1188
1229
  const prefix = index === 0 ? context.newline() : ',' + context.newline();
1189
- if (this.containsMultilineStringLiteral(cteStr)) {
1190
- return prefix + cteStr;
1191
- }
1192
1230
  return prefix + context.indent(cteStr);
1193
1231
  });
1194
1232
  output.push(cteStrings.join(''));
@@ -1243,6 +1281,48 @@ class Deparser {
1243
1281
  }
1244
1282
  return output.join(' ');
1245
1283
  }
1284
+ ATAlterConstraint(node, context) {
1285
+ const output = ['ALTER CONSTRAINT'];
1286
+ if (node.conname) {
1287
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.conname));
1288
+ }
1289
+ if (node.alterEnforceability) {
1290
+ output.push(node.is_enforced ? 'ENFORCED' : 'NOT ENFORCED');
1291
+ }
1292
+ if (node.alterDeferrability) {
1293
+ output.push(node.deferrable ? 'DEFERRABLE' : 'NOT DEFERRABLE');
1294
+ if (node.initdeferred) {
1295
+ output.push('INITIALLY DEFERRED');
1296
+ }
1297
+ }
1298
+ if (node.alterInheritability) {
1299
+ output.push(node.noinherit ? 'NO INHERIT' : 'INHERIT');
1300
+ }
1301
+ return output.join(' ');
1302
+ }
1303
+ deparseKeyAttrs(attrs, withPeriod, context) {
1304
+ const items = list_utils_1.ListUtils.unwrapList(attrs).map(attr => this.visit(attr, context));
1305
+ if (withPeriod && items.length > 0) {
1306
+ items[items.length - 1] = `PERIOD ${items[items.length - 1]}`;
1307
+ }
1308
+ return items.join(', ');
1309
+ }
1310
+ ReturningClause(node, context) {
1311
+ const output = ['RETURNING'];
1312
+ if (node.options && node.options.length > 0) {
1313
+ const options = list_utils_1.ListUtils.unwrapList(node.options)
1314
+ .map(option => this.visit(option, context));
1315
+ output.push(`WITH (${options.join(', ')})`);
1316
+ }
1317
+ if (node.exprs && node.exprs.length > 0) {
1318
+ output.push(this.deparseReturningList(node.exprs, context));
1319
+ }
1320
+ return output.join(' ');
1321
+ }
1322
+ ReturningOption(node, context) {
1323
+ const keyword = node.option === 'RETURNING_OPTION_NEW' ? 'NEW' : 'OLD';
1324
+ return `${keyword} AS ${quotes_1.QuoteUtils.quoteIdentifier(node.value)}`;
1325
+ }
1246
1326
  deparseReturningList(list, context) {
1247
1327
  return list_utils_1.ListUtils.unwrapList(list)
1248
1328
  .filter(item => item != null && this.getNodeType(item) !== 'undefined')
@@ -1321,7 +1401,9 @@ class Deparser {
1321
1401
  const source = this.visit(args[1], context);
1322
1402
  // EXTRACT requires an unquoted uppercase keyword (EPOCH, YEAR, etc.)
1323
1403
  // but A_Const sval deparsing wraps it in single quotes — strip and uppercase
1324
- if (field.startsWith("'") && field.endsWith("'")) {
1404
+ // only when it is spelled the way the bare-keyword form parses back
1405
+ if (/^'(?:year|month|day|hour|minute|second)'$/.test(field)
1406
+ || (/^'[a-z_][a-z0-9_]*'$/.test(field) && (0, quotes_1.keywordKindOf)(field.slice(1, -1)) === 'NO_KEYWORD')) {
1325
1407
  field = field.slice(1, -1).toUpperCase();
1326
1408
  }
1327
1409
  return `EXTRACT(${field} FROM ${source})`;
@@ -1483,43 +1565,41 @@ class Deparser {
1483
1565
  result += ` FILTER (WHERE ${this.visit(node.agg_filter, context)})`;
1484
1566
  }
1485
1567
  if (node.over) {
1486
- // Handle named window references first
1487
- if (node.over.name) {
1488
- result += ` OVER ${node.over.name}`;
1489
- }
1490
- else {
1491
- const windowParts = [];
1492
- if (node.over.partitionClause) {
1493
- const partitions = list_utils_1.ListUtils.unwrapList(node.over.partitionClause);
1494
- const partitionStrs = partitions.map(p => this.visit(p, context));
1495
- windowParts.push(`PARTITION BY ${partitionStrs.join(', ')}`);
1496
- }
1497
- if (node.over.orderClause) {
1498
- const orders = list_utils_1.ListUtils.unwrapList(node.over.orderClause);
1499
- const orderStrs = orders.map(o => this.visit(o, context));
1500
- windowParts.push(`ORDER BY ${orderStrs.join(', ')}`);
1501
- }
1502
- // Handle window frame specifications using the dedicated formatWindowFrame method
1503
- const frameClause = this.formatWindowFrame(node.over, context.spawn('FuncCall'));
1504
- if (frameClause) {
1505
- windowParts.push(frameClause);
1506
- }
1507
- if (windowParts.length > 0) {
1508
- if (context.isPretty() && windowParts.length > 1) {
1509
- const formattedParts = windowParts.map(part => context.indent(part));
1510
- result += ` OVER (${context.newline()}${formattedParts.join(context.newline())}${context.newline()})`;
1511
- }
1512
- else {
1513
- result += ` OVER (${windowParts.join(' ')})`;
1514
- }
1515
- }
1516
- else {
1517
- result += ` OVER ()`;
1518
- }
1519
- }
1568
+ result += this.formatOverClause(node.over, context);
1520
1569
  }
1521
1570
  return result;
1522
1571
  }
1572
+ formatOverClause(over, context) {
1573
+ if (over.name) {
1574
+ return ` OVER ${over.name}`;
1575
+ }
1576
+ const windowParts = [];
1577
+ if (over.refname) {
1578
+ windowParts.push(over.refname);
1579
+ }
1580
+ if (over.partitionClause) {
1581
+ const partitions = list_utils_1.ListUtils.unwrapList(over.partitionClause);
1582
+ const partitionStrs = partitions.map(p => this.visit(p, context));
1583
+ windowParts.push(`PARTITION BY ${partitionStrs.join(', ')}`);
1584
+ }
1585
+ if (over.orderClause) {
1586
+ const orders = list_utils_1.ListUtils.unwrapList(over.orderClause);
1587
+ const orderStrs = orders.map(o => this.visit(o, context));
1588
+ windowParts.push(`ORDER BY ${orderStrs.join(', ')}`);
1589
+ }
1590
+ const frameClause = this.formatWindowFrame(over, context.spawn('FuncCall'));
1591
+ if (frameClause) {
1592
+ windowParts.push(frameClause);
1593
+ }
1594
+ if (windowParts.length === 0) {
1595
+ return ' OVER ()';
1596
+ }
1597
+ if (context.isPretty() && windowParts.length > 1) {
1598
+ const formattedParts = windowParts.map(part => context.indent(part));
1599
+ return ` OVER (${context.newline()}${formattedParts.join(context.newline())}${context.newline()})`;
1600
+ }
1601
+ return ` OVER (${windowParts.join(' ')})`;
1602
+ }
1523
1603
  FuncExpr(node, context) {
1524
1604
  const funcName = `func_${node.funcid}`;
1525
1605
  const args = node.args ? list_utils_1.ListUtils.unwrapList(node.args).map(arg => this.visit(arg, context)).join(', ') : '';
@@ -1530,39 +1610,39 @@ class Deparser {
1530
1610
  if (nodeAny.ival !== undefined) {
1531
1611
  if (typeof nodeAny.ival === 'object' && nodeAny.ival !== null) {
1532
1612
  if (nodeAny.ival.ival !== undefined) {
1533
- return nodeAny.ival.ival.toString();
1613
+ return formatInteger(nodeAny.ival.ival);
1534
1614
  }
1535
1615
  else if (Object.keys(nodeAny.ival).length === 0) {
1536
1616
  return '0';
1537
1617
  }
1538
1618
  else {
1539
- return nodeAny.ival.toString();
1619
+ return formatInteger(nodeAny.ival);
1540
1620
  }
1541
1621
  }
1542
1622
  else if (nodeAny.ival === null) {
1543
1623
  return 'NULL';
1544
1624
  }
1545
1625
  else {
1546
- return nodeAny.ival.toString();
1626
+ return formatInteger(nodeAny.ival);
1547
1627
  }
1548
1628
  }
1549
1629
  else if (nodeAny.fval !== undefined) {
1550
1630
  if (typeof nodeAny.fval === 'object' && nodeAny.fval !== null) {
1551
1631
  if (nodeAny.fval.fval !== undefined) {
1552
- return nodeAny.fval.fval.toString();
1632
+ return formatNumeric(nodeAny.fval.fval);
1553
1633
  }
1554
1634
  else if (Object.keys(nodeAny.fval).length === 0) {
1555
1635
  return '0.0';
1556
1636
  }
1557
1637
  else {
1558
- return nodeAny.fval.toString();
1638
+ return formatNumeric(nodeAny.fval);
1559
1639
  }
1560
1640
  }
1561
1641
  else if (nodeAny.fval === null) {
1562
1642
  return 'NULL';
1563
1643
  }
1564
1644
  else {
1565
- return nodeAny.fval.toString();
1645
+ return formatNumeric(nodeAny.fval);
1566
1646
  }
1567
1647
  }
1568
1648
  else if (nodeAny.sval !== undefined) {
@@ -1609,15 +1689,7 @@ class Deparser {
1609
1689
  else if (nodeAny.bsval !== undefined) {
1610
1690
  if (typeof nodeAny.bsval === 'object' && nodeAny.bsval !== null) {
1611
1691
  if (nodeAny.bsval.bsval !== undefined) {
1612
- const bsval = nodeAny.bsval.bsval;
1613
- // Check if this is a hexadecimal bit string (starts with x and contains only hex digits)
1614
- if (bsval.startsWith('x') && /^x[0-9A-Fa-f]+$/.test(bsval)) {
1615
- return `x'${bsval.substring(1)}'`;
1616
- }
1617
- if (bsval.startsWith('b')) {
1618
- return `b'${bsval.substring(1)}'`;
1619
- }
1620
- return `b'${bsval}'`;
1692
+ return formatBitString(nodeAny.bsval.bsval);
1621
1693
  }
1622
1694
  else if (Object.keys(nodeAny.bsval).length === 0) {
1623
1695
  return "''";
@@ -1630,23 +1702,15 @@ class Deparser {
1630
1702
  return 'NULL';
1631
1703
  }
1632
1704
  else {
1633
- const bsval = nodeAny.bsval;
1634
- // Check if this is a hexadecimal bit string (starts with x and contains only hex digits)
1635
- if (bsval.startsWith('x') && /^x[0-9A-Fa-f]+$/.test(bsval)) {
1636
- return `x'${bsval.substring(1)}'`;
1637
- }
1638
- if (bsval.startsWith('b')) {
1639
- return `b'${bsval.substring(1)}'`;
1640
- }
1641
- return `b'${bsval}'`;
1705
+ return formatBitString(nodeAny.bsval);
1642
1706
  }
1643
1707
  }
1644
1708
  if (nodeAny.val) {
1645
1709
  if (nodeAny.val.Integer?.ival !== undefined) {
1646
- return nodeAny.val.Integer.ival.toString();
1710
+ return formatInteger(nodeAny.val.Integer.ival);
1647
1711
  }
1648
1712
  else if (nodeAny.val.Float?.fval !== undefined) {
1649
- return nodeAny.val.Float.fval.toString();
1713
+ return formatNumeric(nodeAny.val.Float.fval);
1650
1714
  }
1651
1715
  else if (nodeAny.val.String?.sval !== undefined) {
1652
1716
  return quotes_1.QuoteUtils.escape(nodeAny.val.String.sval);
@@ -1655,7 +1719,7 @@ class Deparser {
1655
1719
  return nodeAny.val.Boolean.boolval ? 'true' : 'false';
1656
1720
  }
1657
1721
  else if (nodeAny.val.BitString?.bsval !== undefined) {
1658
- return nodeAny.val.BitString.bsval;
1722
+ return formatBitString(nodeAny.val.BitString.bsval);
1659
1723
  }
1660
1724
  }
1661
1725
  if (nodeAny.isnull === true) {
@@ -1667,15 +1731,15 @@ class Deparser {
1667
1731
  }
1668
1732
  if (nodeAny.Integer !== undefined) {
1669
1733
  if (typeof nodeAny.Integer === 'object' && nodeAny.Integer.ival !== undefined) {
1670
- return nodeAny.Integer.ival.toString();
1734
+ return formatInteger(nodeAny.Integer.ival);
1671
1735
  }
1672
- return nodeAny.Integer.toString();
1736
+ return formatInteger(nodeAny.Integer);
1673
1737
  }
1674
1738
  if (nodeAny.Float !== undefined) {
1675
1739
  if (typeof nodeAny.Float === 'object' && nodeAny.Float.fval !== undefined) {
1676
- return nodeAny.Float.fval.toString();
1740
+ return formatNumeric(nodeAny.Float.fval);
1677
1741
  }
1678
- return nodeAny.Float.toString();
1742
+ return formatNumeric(nodeAny.Float);
1679
1743
  }
1680
1744
  if (nodeAny.String !== undefined) {
1681
1745
  if (typeof nodeAny.String === 'object' && nodeAny.String.sval !== undefined) {
@@ -2137,7 +2201,9 @@ class Deparser {
2137
2201
  A_Indirection(node, context) {
2138
2202
  let argStr = this.visit(node.arg, context);
2139
2203
  const argType = this.getNodeType(node.arg);
2140
- if (argType === 'TypeCast' || argType === 'SubLink' || argType === 'A_Expr' || argType === 'FuncCall' || argType === 'A_Indirection' || argType === 'ColumnRef' || argType === 'RowExpr') {
2204
+ // Grammar: only a parameter takes indirection bare; every other target is
2205
+ // `'(' a_expr ')' opt_indirection` (a plain column subscript is the same node).
2206
+ if (argType !== 'ParamRef') {
2141
2207
  argStr = `(${argStr})`;
2142
2208
  }
2143
2209
  const output = [argStr];
@@ -2165,21 +2231,11 @@ class Deparser {
2165
2231
  if (context.isPretty() && args.length > 0) {
2166
2232
  for (const arg of args) {
2167
2233
  const whenClause = this.visit(arg, context);
2168
- if (this.containsMultilineStringLiteral(whenClause)) {
2169
- output.push(context.newline() + whenClause);
2170
- }
2171
- else {
2172
- output.push(context.newline() + context.indent(whenClause));
2173
- }
2234
+ output.push(context.newline() + context.indent(whenClause));
2174
2235
  }
2175
2236
  if (node.defresult) {
2176
2237
  const elseResult = this.visit(node.defresult, context);
2177
- if (this.containsMultilineStringLiteral(elseResult)) {
2178
- output.push(context.newline() + 'ELSE ' + elseResult);
2179
- }
2180
- else {
2181
- output.push(context.newline() + context.indent('ELSE ' + elseResult));
2182
- }
2238
+ output.push(context.newline() + context.indent('ELSE ' + elseResult));
2183
2239
  }
2184
2240
  output.push(context.newline() + 'END');
2185
2241
  return output.join(' ');
@@ -2294,16 +2350,16 @@ class Deparser {
2294
2350
  }
2295
2351
  preserveOperatorDefElemCase(defName) {
2296
2352
  const caseMap = {
2297
- 'leftarg': 'Leftarg',
2298
- 'rightarg': 'Rightarg',
2299
- 'procedure': 'Procedure',
2300
- 'function': 'Function',
2301
- 'commutator': 'Commutator',
2302
- 'negator': 'Negator',
2303
- 'restrict': 'Restrict',
2304
- 'join': 'Join',
2305
- 'hashes': 'Hashes',
2306
- 'merges': 'Merges'
2353
+ leftarg: 'Leftarg',
2354
+ rightarg: 'Rightarg',
2355
+ procedure: 'Procedure',
2356
+ function: 'Function',
2357
+ commutator: 'Commutator',
2358
+ negator: 'Negator',
2359
+ restrict: 'Restrict',
2360
+ join: 'Join',
2361
+ hashes: 'Hashes',
2362
+ merges: 'Merges'
2307
2363
  };
2308
2364
  return caseMap[defName.toLowerCase()] || defName;
2309
2365
  }
@@ -2326,24 +2382,16 @@ class Deparser {
2326
2382
  return quotes_1.QuoteUtils.quoteIdentifier(value);
2327
2383
  }
2328
2384
  Integer(node, context) {
2329
- return node.ival?.toString() || '0';
2385
+ return node.ival === undefined || node.ival === null ? '0' : formatInteger(node.ival);
2330
2386
  }
2331
2387
  Float(node, context) {
2332
- return node.fval || '0.0';
2388
+ return node.fval ? formatNumeric(node.fval) : '0.0';
2333
2389
  }
2334
2390
  Boolean(node, context) {
2335
2391
  return node.boolval ? 'true' : 'false';
2336
2392
  }
2337
2393
  BitString(node, context) {
2338
- // Check if this is a hexadecimal bit string (starts with x)
2339
- if (node.bsval.startsWith('x')) {
2340
- return `x'${node.bsval.substring(1)}'`;
2341
- }
2342
- if (node.bsval.startsWith('b')) {
2343
- return `b'${node.bsval.substring(1)}'`;
2344
- }
2345
- // Fallback for raw values without prefix
2346
- return `b'${node.bsval}'`;
2394
+ return formatBitString(node.bsval);
2347
2395
  }
2348
2396
  Null(node, context) {
2349
2397
  return 'NULL';
@@ -2382,7 +2430,7 @@ class Deparser {
2382
2430
  output.push(context.parens(elementStrs.join(', ')));
2383
2431
  }
2384
2432
  }
2385
- else if (node.tableElts) {
2433
+ else if (node.tableElts && !node.partbound) {
2386
2434
  const elements = list_utils_1.ListUtils.unwrapList(node.tableElts);
2387
2435
  const elementStrs = elements.map(el => {
2388
2436
  return this.deparse(el, context.spawn('CreateStmt'));
@@ -2410,7 +2458,16 @@ class Deparser {
2410
2458
  const inherits = list_utils_1.ListUtils.unwrapList(node.inhRelations);
2411
2459
  const inheritStrs = inherits.map(rel => this.visit(rel, context));
2412
2460
  output.push(inheritStrs[0]);
2413
- if (node.partbound.strategy === 'l' && node.partbound.listdatums) {
2461
+ if (node.tableElts) {
2462
+ const elementStrs = list_utils_1.ListUtils.unwrapList(node.tableElts).map(el => {
2463
+ return this.deparse(el, context.spawn('CreateStmt'));
2464
+ });
2465
+ output.push(context.parens(elementStrs.join(', ')));
2466
+ }
2467
+ if (node.partbound.is_default) {
2468
+ output.push('DEFAULT');
2469
+ }
2470
+ else if (node.partbound.strategy === 'l' && node.partbound.listdatums) {
2414
2471
  output.push('FOR VALUES IN');
2415
2472
  const listValues = list_utils_1.ListUtils.unwrapList(node.partbound.listdatums)
2416
2473
  .map(datum => this.visit(datum, context))
@@ -2433,13 +2490,9 @@ class Deparser {
2433
2490
  output.push(`(${upperValues})`);
2434
2491
  }
2435
2492
  }
2436
- else if (node.partbound.strategy === 'h' && node.partbound.modulus !== undefined) {
2493
+ else if (node.partbound.strategy === 'h') {
2437
2494
  output.push('FOR VALUES WITH');
2438
- const remainder = node.partbound.remainder !== undefined ? node.partbound.remainder : 0;
2439
- output.push(`(MODULUS ${node.partbound.modulus}, REMAINDER ${remainder})`);
2440
- }
2441
- else if (node.partbound.is_default) {
2442
- output.push('DEFAULT');
2495
+ output.push(`(MODULUS ${node.partbound.modulus ?? 0}, REMAINDER ${node.partbound.remainder ?? 0})`);
2443
2496
  }
2444
2497
  }
2445
2498
  else if (node.inhRelations) {
@@ -2505,6 +2558,12 @@ class Deparser {
2505
2558
  if (node.typeName) {
2506
2559
  output.push(this.TypeName(node.typeName, context));
2507
2560
  }
2561
+ if (node.storage_name) {
2562
+ output.push('STORAGE', node.storage_name.toUpperCase());
2563
+ }
2564
+ if (node.compression) {
2565
+ output.push('COMPRESSION', quotes_1.QuoteUtils.quoteIdentifier(node.compression));
2566
+ }
2508
2567
  if (node.fdwoptions && node.fdwoptions.length > 0) {
2509
2568
  output.push('OPTIONS');
2510
2569
  const columnContext = context.spawn('ColumnDef');
@@ -2539,7 +2598,7 @@ class Deparser {
2539
2598
  Constraint(node, context) {
2540
2599
  const output = [];
2541
2600
  // Handle constraint name if present
2542
- if (node.conname && (node.contype === 'CONSTR_CHECK' || node.contype === 'CONSTR_UNIQUE' || node.contype === 'CONSTR_PRIMARY' || node.contype === 'CONSTR_FOREIGN')) {
2601
+ if (node.conname && (node.contype === 'CONSTR_CHECK' || node.contype === 'CONSTR_UNIQUE' || node.contype === 'CONSTR_PRIMARY' || node.contype === 'CONSTR_FOREIGN' || node.contype === 'CONSTR_NOTNULL' || node.contype === 'CONSTR_EXCLUSION')) {
2543
2602
  output.push('CONSTRAINT');
2544
2603
  output.push(quotes_1.QuoteUtils.quoteIdentifier(node.conname));
2545
2604
  }
@@ -2549,11 +2608,24 @@ class Deparser {
2549
2608
  break;
2550
2609
  case 'CONSTR_NOTNULL':
2551
2610
  output.push('NOT NULL');
2611
+ if (node.keys && node.keys.length > 0 && !context.isColumnConstraint) {
2612
+ const keyList = list_utils_1.ListUtils.unwrapList(node.keys)
2613
+ .map(key => this.visit(key, context))
2614
+ .join(', ');
2615
+ output.push(keyList);
2616
+ }
2617
+ if (node.is_no_inherit) {
2618
+ output.push('NO INHERIT');
2619
+ }
2620
+ if (node.skip_validation) {
2621
+ output.push('NOT VALID');
2622
+ }
2552
2623
  break;
2553
2624
  case 'CONSTR_DEFAULT':
2554
2625
  output.push('DEFAULT');
2555
2626
  if (node.raw_expr) {
2556
- output.push(this.visit(node.raw_expr, context));
2627
+ const defaultExpr = this.visit(node.raw_expr, context);
2628
+ output.push(this.needsBExprParens(node.raw_expr) ? `(${defaultExpr})` : defaultExpr);
2557
2629
  }
2558
2630
  break;
2559
2631
  case 'CONSTR_CHECK':
@@ -2577,8 +2649,13 @@ class Deparser {
2577
2649
  output.push(context.parens(this.visit(node.raw_expr, context)));
2578
2650
  }
2579
2651
  }
2580
- // Handle NOT VALID for check constraints
2581
- if (node.skip_validation) {
2652
+ // PG18: constraints are enforced unless explicitly NOT ENFORCED
2653
+ // (libpg-query omits false booleans, so undefined means not enforced)
2654
+ if (node.is_enforced !== true && !context.isDomainConstraint) {
2655
+ output.push('NOT ENFORCED');
2656
+ }
2657
+ else if (node.skip_validation) {
2658
+ // NOT ENFORCED implies NOT VALID, so only emit NOT VALID when enforced
2582
2659
  output.push('NOT VALID');
2583
2660
  }
2584
2661
  // Handle NO INHERIT for check constraints - only for table constraints, not domain constraints
@@ -2598,7 +2675,7 @@ class Deparser {
2598
2675
  if (node.raw_expr) {
2599
2676
  output.push(context.parens(this.visit(node.raw_expr, context)));
2600
2677
  }
2601
- output.push('STORED');
2678
+ output.push(node.generated_kind === 'v' ? 'VIRTUAL' : 'STORED');
2602
2679
  break;
2603
2680
  case 'CONSTR_IDENTITY':
2604
2681
  output.push('GENERATED');
@@ -2674,10 +2751,12 @@ class Deparser {
2674
2751
  case 'CONSTR_PRIMARY':
2675
2752
  output.push('PRIMARY KEY');
2676
2753
  if (node.keys && node.keys.length > 0) {
2677
- const keyList = list_utils_1.ListUtils.unwrapList(node.keys)
2678
- .map(key => this.visit(key, context))
2679
- .join(', ');
2680
- output.push(`(${keyList})`);
2754
+ const keyItems = list_utils_1.ListUtils.unwrapList(node.keys)
2755
+ .map(key => this.visit(key, context));
2756
+ if (node.without_overlaps) {
2757
+ keyItems[keyItems.length - 1] += ' WITHOUT OVERLAPS';
2758
+ }
2759
+ output.push(`(${keyItems.join(', ')})`);
2681
2760
  }
2682
2761
  if (node.indexname) {
2683
2762
  output.push('USING INDEX');
@@ -2695,10 +2774,12 @@ class Deparser {
2695
2774
  output.push('NULLS NOT DISTINCT');
2696
2775
  }
2697
2776
  if (node.keys && node.keys.length > 0) {
2698
- const keyList = list_utils_1.ListUtils.unwrapList(node.keys)
2699
- .map(key => this.visit(key, context))
2700
- .join(', ');
2701
- output.push(`(${keyList})`);
2777
+ const keyItems = list_utils_1.ListUtils.unwrapList(node.keys)
2778
+ .map(key => this.visit(key, context));
2779
+ if (node.without_overlaps) {
2780
+ keyItems[keyItems.length - 1] += ' WITHOUT OVERLAPS';
2781
+ }
2782
+ output.push(`(${keyItems.join(', ')})`);
2702
2783
  }
2703
2784
  if (node.indexname) {
2704
2785
  output.push('USING INDEX');
@@ -2711,20 +2792,14 @@ class Deparser {
2711
2792
  if (context.isPretty()) {
2712
2793
  output.push('\n' + context.indent('FOREIGN KEY'));
2713
2794
  if (node.fk_attrs && node.fk_attrs.length > 0) {
2714
- const fkAttrs = list_utils_1.ListUtils.unwrapList(node.fk_attrs)
2715
- .map(attr => this.visit(attr, context))
2716
- .join(', ');
2717
- output.push(`(${fkAttrs})`);
2795
+ output.push(`(${this.deparseKeyAttrs(node.fk_attrs, node.fk_with_period, context)})`);
2718
2796
  }
2719
2797
  output.push('\n' + context.indent('REFERENCES'));
2720
2798
  }
2721
2799
  else {
2722
2800
  output.push('FOREIGN KEY');
2723
2801
  if (node.fk_attrs && node.fk_attrs.length > 0) {
2724
- const fkAttrs = list_utils_1.ListUtils.unwrapList(node.fk_attrs)
2725
- .map(attr => this.visit(attr, context))
2726
- .join(', ');
2727
- output.push(`(${fkAttrs})`);
2802
+ output.push(`(${this.deparseKeyAttrs(node.fk_attrs, node.fk_with_period, context)})`);
2728
2803
  }
2729
2804
  output.push('REFERENCES');
2730
2805
  }
@@ -2747,9 +2822,7 @@ class Deparser {
2747
2822
  }
2748
2823
  }
2749
2824
  if (node.pk_attrs && node.pk_attrs.length > 0) {
2750
- const pkAttrs = list_utils_1.ListUtils.unwrapList(node.pk_attrs)
2751
- .map(attr => this.visit(attr, context))
2752
- .join(', ');
2825
+ const pkAttrs = this.deparseKeyAttrs(node.pk_attrs, node.pk_with_period, context);
2753
2826
  if (context.isPretty() && !context.isColumnConstraint) {
2754
2827
  const lastIndex = output.length - 1;
2755
2828
  if (lastIndex >= 0) {
@@ -2820,6 +2893,15 @@ class Deparser {
2820
2893
  deleteClause += 'SET DEFAULT';
2821
2894
  break;
2822
2895
  }
2896
+ if ((node.fk_del_action === 'n' || node.fk_del_action === 'd') && node.fk_del_set_cols && node.fk_del_set_cols.length > 0) {
2897
+ const setColumns = list_utils_1.ListUtils.unwrapList(node.fk_del_set_cols)
2898
+ .map(column => {
2899
+ const stringNode = column.String;
2900
+ return quotes_1.QuoteUtils.quoteIdentifier(stringNode.sval || stringNode.str || '');
2901
+ })
2902
+ .join(', ');
2903
+ deleteClause += ` (${setColumns})`;
2904
+ }
2823
2905
  if (context.isPretty()) {
2824
2906
  output.push('\n' + context.indent(deleteClause));
2825
2907
  }
@@ -2828,8 +2910,18 @@ class Deparser {
2828
2910
  output.push(deleteClause.replace('ON DELETE ', ''));
2829
2911
  }
2830
2912
  }
2831
- // Handle NOT VALID for foreign key constraints - only for table constraints, not domain constraints
2832
- if (node.skip_validation && !context.isDomainConstraint) {
2913
+ // PG18: constraints are enforced unless explicitly NOT ENFORCED
2914
+ // (libpg-query omits false booleans, so undefined means not enforced)
2915
+ if (node.is_enforced !== true && !context.isDomainConstraint) {
2916
+ if (context.isPretty() && !context.isColumnConstraint) {
2917
+ output.push('\n' + context.indent('NOT ENFORCED'));
2918
+ }
2919
+ else {
2920
+ output.push('NOT ENFORCED');
2921
+ }
2922
+ }
2923
+ else if (node.skip_validation && !context.isDomainConstraint) {
2924
+ // NOT ENFORCED implies NOT VALID, so only emit NOT VALID when enforced
2833
2925
  if (context.isPretty() && !context.isColumnConstraint) {
2834
2926
  output.push('\n' + context.indent('NOT VALID'));
2835
2927
  }
@@ -2850,6 +2942,12 @@ class Deparser {
2850
2942
  case 'CONSTR_ATTR_IMMEDIATE':
2851
2943
  output.push('INITIALLY IMMEDIATE');
2852
2944
  break;
2945
+ case 'CONSTR_ATTR_ENFORCED':
2946
+ output.push('ENFORCED');
2947
+ break;
2948
+ case 'CONSTR_ATTR_NOT_ENFORCED':
2949
+ output.push('NOT ENFORCED');
2950
+ break;
2853
2951
  case 'CONSTR_EXCLUSION':
2854
2952
  output.push('EXCLUDE');
2855
2953
  if (node.access_method) {
@@ -2891,7 +2989,7 @@ class Deparser {
2891
2989
  break;
2892
2990
  }
2893
2991
  // Handle deferrable constraints for all constraint types that support it
2894
- if (node.contype === 'CONSTR_PRIMARY' || node.contype === 'CONSTR_UNIQUE' || node.contype === 'CONSTR_FOREIGN') {
2992
+ if (node.contype === 'CONSTR_PRIMARY' || node.contype === 'CONSTR_UNIQUE' || node.contype === 'CONSTR_FOREIGN' || node.contype === 'CONSTR_EXCLUSION') {
2895
2993
  if (node.deferrable) {
2896
2994
  if (context.isPretty() && node.contype === 'CONSTR_FOREIGN') {
2897
2995
  output.push('\n' + context.indent('DEFERRABLE'));
@@ -2920,6 +3018,9 @@ class Deparser {
2920
3018
  output.push('NOT DEFERRABLE');
2921
3019
  }
2922
3020
  }
3021
+ else if (node.initdeferred) {
3022
+ output.push('INITIALLY DEFERRED');
3023
+ }
2923
3024
  }
2924
3025
  if (context.isPretty() && node.contype === 'CONSTR_FOREIGN') {
2925
3026
  let result = '';
@@ -2985,6 +3086,9 @@ class Deparser {
2985
3086
  output.push(node.name);
2986
3087
  }
2987
3088
  const windowParts = [];
3089
+ if (node.refname) {
3090
+ windowParts.push(node.refname);
3091
+ }
2988
3092
  if (node.partitionClause) {
2989
3093
  const partitions = list_utils_1.ListUtils.unwrapList(node.partitionClause);
2990
3094
  const partitionStrs = partitions.map(p => this.visit(p, context));
@@ -2995,12 +3099,9 @@ class Deparser {
2995
3099
  const orderStrs = orders.map(o => this.visit(o, context));
2996
3100
  windowParts.push(`ORDER BY ${orderStrs.join(', ')}`);
2997
3101
  }
2998
- // Only add frame clause if frameOptions indicates non-default framing
2999
- if (node.frameOptions && node.frameOptions !== 1058) {
3000
- const frameClause = this.formatWindowFrame(node, context.spawn('WindowDef'));
3001
- if (frameClause) {
3002
- windowParts.push(frameClause);
3003
- }
3102
+ const frameClause = this.formatWindowFrame(node, context.spawn('WindowDef'));
3103
+ if (frameClause) {
3104
+ windowParts.push(frameClause);
3004
3105
  }
3005
3106
  if (windowParts.length > 0) {
3006
3107
  if (node.name) {
@@ -3017,114 +3118,96 @@ class Deparser {
3017
3118
  }
3018
3119
  return output.join(' ');
3019
3120
  }
3121
+ formatIdentityColumnOptions(def, context) {
3122
+ const seqContext = context.spawn('AlterSeqStmt');
3123
+ return list_utils_1.ListUtils.unwrapList(def).map(item => {
3124
+ const el = item.DefElem;
3125
+ if (!el)
3126
+ return this.visit(item, context);
3127
+ if (el.defname === 'generated') {
3128
+ const ival = el.arg?.Integer?.ival ?? 0;
3129
+ return ival === 97 ? 'SET GENERATED ALWAYS' : 'SET GENERATED BY DEFAULT';
3130
+ }
3131
+ if (el.defname === 'restart') {
3132
+ return el.arg ? `RESTART WITH ${this.visit(el.arg, context)}` : 'RESTART';
3133
+ }
3134
+ return `SET ${this.visit(item, seqContext)}`;
3135
+ }).join(' ');
3136
+ }
3137
+ // b_expr (used by DEFAULT) excludes IN/LIKE/BETWEEN and boolean operators.
3138
+ needsBExprParens(node) {
3139
+ const nodeType = this.getNodeType(node);
3140
+ if (nodeType === 'BoolExpr')
3141
+ return true;
3142
+ if (nodeType === 'A_Expr') {
3143
+ const kind = this.getNodeData(node).kind;
3144
+ return kind !== 'AEXPR_OP' && kind !== 'AEXPR_DISTINCT' && kind !== 'AEXPR_NOT_DISTINCT';
3145
+ }
3146
+ return false;
3147
+ }
3020
3148
  formatWindowFrame(node, context) {
3021
- if (!node.frameOptions)
3149
+ if (!node.frameOptions || !(node.frameOptions & FRAMEOPTION_NONDEFAULT))
3022
3150
  return null;
3023
3151
  const frameOptions = node.frameOptions;
3024
- const EXCLUDE_MASK = 0x8000 | 0x10000 | 0x20000;
3025
- const baseFrameOptions = frameOptions & ~EXCLUDE_MASK;
3026
3152
  const frameParts = [];
3027
- if (frameOptions & 0x01) { // FRAMEOPTION_NONDEFAULT
3028
- if (frameOptions & 0x02) { // FRAMEOPTION_RANGE
3029
- frameParts.push('RANGE');
3030
- }
3031
- else if (frameOptions & 0x04) { // FRAMEOPTION_ROWS
3032
- frameParts.push('ROWS');
3033
- }
3034
- else if (frameOptions & 0x08) { // FRAMEOPTION_GROUPS
3035
- frameParts.push('GROUPS');
3036
- }
3153
+ if (frameOptions & FRAMEOPTION_RANGE) {
3154
+ frameParts.push('RANGE');
3155
+ }
3156
+ else if (frameOptions & FRAMEOPTION_ROWS) {
3157
+ frameParts.push('ROWS');
3158
+ }
3159
+ else if (frameOptions & FRAMEOPTION_GROUPS) {
3160
+ frameParts.push('GROUPS');
3037
3161
  }
3038
3162
  if (frameParts.length === 0)
3039
3163
  return null;
3040
- const boundsParts = [];
3041
- // Handle specific frameOptions values that have known mappings
3042
- if (baseFrameOptions === 789) {
3043
- boundsParts.push('CURRENT ROW');
3044
- boundsParts.push('AND UNBOUNDED FOLLOWING');
3164
+ let start = null;
3165
+ if (frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING) {
3166
+ start = 'UNBOUNDED PRECEDING';
3045
3167
  }
3046
- else if (baseFrameOptions === 1077) {
3047
- boundsParts.push('UNBOUNDED PRECEDING');
3048
- boundsParts.push('AND CURRENT ROW');
3168
+ else if (frameOptions & FRAMEOPTION_START_UNBOUNDED_FOLLOWING) {
3169
+ start = 'UNBOUNDED FOLLOWING';
3049
3170
  }
3050
- else if (baseFrameOptions === 18453) {
3051
- if (node.startOffset && node.endOffset) {
3052
- boundsParts.push(`${this.visit(node.startOffset, context)} PRECEDING`);
3053
- boundsParts.push(`AND ${this.visit(node.endOffset, context)} FOLLOWING`);
3054
- }
3171
+ else if (frameOptions & FRAMEOPTION_START_CURRENT_ROW) {
3172
+ start = 'CURRENT ROW';
3055
3173
  }
3056
- else if (baseFrameOptions === 1557) {
3057
- boundsParts.push('CURRENT ROW');
3058
- boundsParts.push('AND CURRENT ROW');
3174
+ else if (frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING && node.startOffset) {
3175
+ start = `${this.visit(node.startOffset, context)} PRECEDING`;
3059
3176
  }
3060
- else if (baseFrameOptions === 16917) {
3061
- boundsParts.push('CURRENT ROW');
3062
- if (node.endOffset) {
3063
- boundsParts.push(`AND ${this.visit(node.endOffset, context)} FOLLOWING`);
3064
- }
3177
+ else if (frameOptions & FRAMEOPTION_START_OFFSET_FOLLOWING && node.startOffset) {
3178
+ start = `${this.visit(node.startOffset, context)} FOLLOWING`;
3065
3179
  }
3066
- else if (baseFrameOptions === 1058) {
3067
- return null;
3180
+ let end = null;
3181
+ if (frameOptions & FRAMEOPTION_END_UNBOUNDED_PRECEDING) {
3182
+ end = 'UNBOUNDED PRECEDING';
3068
3183
  }
3069
- else {
3070
- // Handle start bound - prioritize explicit offset values over bit flags
3071
- if (node.startOffset) {
3072
- if (frameOptions & 0x400) { // FRAMEOPTION_START_VALUE_PRECEDING
3073
- boundsParts.push(`${this.visit(node.startOffset, context)} PRECEDING`);
3074
- }
3075
- else if (frameOptions & 0x800) { // FRAMEOPTION_START_VALUE_FOLLOWING
3076
- boundsParts.push(`${this.visit(node.startOffset, context)} FOLLOWING`);
3077
- }
3078
- else {
3079
- boundsParts.push(`${this.visit(node.startOffset, context)} PRECEDING`);
3080
- }
3081
- }
3082
- else if (frameOptions & 0x10) { // FRAMEOPTION_START_UNBOUNDED_PRECEDING
3083
- boundsParts.push('UNBOUNDED PRECEDING');
3084
- }
3085
- else if (frameOptions & 0x20) { // FRAMEOPTION_START_CURRENT_ROW
3086
- boundsParts.push('CURRENT ROW');
3087
- }
3088
- // Handle end bound - prioritize explicit offset values over bit flags
3089
- if (node.endOffset) {
3090
- if (boundsParts.length > 0) {
3091
- if (frameOptions & 0x1000) { // FRAMEOPTION_END_VALUE_PRECEDING
3092
- boundsParts.push(`AND ${this.visit(node.endOffset, context)} PRECEDING`);
3093
- }
3094
- else if (frameOptions & 0x2000) { // FRAMEOPTION_END_VALUE_FOLLOWING
3095
- boundsParts.push(`AND ${this.visit(node.endOffset, context)} FOLLOWING`);
3096
- }
3097
- else {
3098
- boundsParts.push(`AND ${this.visit(node.endOffset, context)} FOLLOWING`);
3099
- }
3100
- }
3101
- }
3102
- else if (frameOptions & 0x80) { // FRAMEOPTION_END_UNBOUNDED_FOLLOWING
3103
- if (boundsParts.length > 0) {
3104
- boundsParts.push('AND UNBOUNDED FOLLOWING');
3105
- }
3106
- }
3107
- else if (frameOptions & 0x100) { // FRAMEOPTION_END_CURRENT_ROW
3108
- if (boundsParts.length > 0) {
3109
- boundsParts.push('AND CURRENT ROW');
3110
- }
3184
+ else if (frameOptions & FRAMEOPTION_END_UNBOUNDED_FOLLOWING) {
3185
+ end = 'UNBOUNDED FOLLOWING';
3186
+ }
3187
+ else if (frameOptions & FRAMEOPTION_END_CURRENT_ROW) {
3188
+ end = 'CURRENT ROW';
3189
+ }
3190
+ else if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING && node.endOffset) {
3191
+ end = `${this.visit(node.endOffset, context)} PRECEDING`;
3192
+ }
3193
+ else if (frameOptions & FRAMEOPTION_END_OFFSET_FOLLOWING && node.endOffset) {
3194
+ end = `${this.visit(node.endOffset, context)} FOLLOWING`;
3195
+ }
3196
+ if (start) {
3197
+ if (frameOptions & FRAMEOPTION_BETWEEN) {
3198
+ frameParts.push(`BETWEEN ${start} AND ${end ?? 'CURRENT ROW'}`);
3111
3199
  }
3112
- else if (boundsParts.length > 0) {
3113
- boundsParts.push('AND CURRENT ROW');
3200
+ else {
3201
+ frameParts.push(start);
3114
3202
  }
3115
3203
  }
3116
- if (boundsParts.length > 0) {
3117
- frameParts.push('BETWEEN');
3118
- frameParts.push(boundsParts.join(' '));
3119
- }
3120
- // EXCLUDE clause
3121
- if (frameOptions & 0x8000) { // FRAMEOPTION_EXCLUDE_CURRENT_ROW
3204
+ if (frameOptions & FRAMEOPTION_EXCLUDE_CURRENT_ROW) {
3122
3205
  frameParts.push('EXCLUDE CURRENT ROW');
3123
3206
  }
3124
- else if (frameOptions & 0x10000) { // FRAMEOPTION_EXCLUDE_GROUP
3207
+ else if (frameOptions & FRAMEOPTION_EXCLUDE_GROUP) {
3125
3208
  frameParts.push('EXCLUDE GROUP');
3126
3209
  }
3127
- else if (frameOptions & 0x20000) { // FRAMEOPTION_EXCLUDE_TIES
3210
+ else if (frameOptions & FRAMEOPTION_EXCLUDE_TIES) {
3128
3211
  frameParts.push('EXCLUDE TIES');
3129
3212
  }
3130
3213
  return frameParts.join(' ');
@@ -3203,8 +3286,35 @@ class Deparser {
3203
3286
  if (node.ctequery) {
3204
3287
  output.push(context.parens(this.visit(node.ctequery, context)));
3205
3288
  }
3289
+ if (node.search_clause) {
3290
+ const sc = node.search_clause;
3291
+ const cols = list_utils_1.ListUtils.unwrapList(sc.search_col_list).map(c => this.visit(c, context));
3292
+ output.push('SEARCH', sc.search_breadth_first ? 'BREADTH' : 'DEPTH', 'FIRST BY');
3293
+ output.push(cols.join(', '));
3294
+ output.push('SET', quotes_1.QuoteUtils.quoteIdentifier(sc.search_seq_column));
3295
+ }
3296
+ if (node.cycle_clause) {
3297
+ const cc = node.cycle_clause;
3298
+ const cols = list_utils_1.ListUtils.unwrapList(cc.cycle_col_list).map(c => this.visit(c, context));
3299
+ output.push('CYCLE', cols.join(', '));
3300
+ output.push('SET', quotes_1.QuoteUtils.quoteIdentifier(cc.cycle_mark_column));
3301
+ if (cc.cycle_mark_value && cc.cycle_mark_default) {
3302
+ output.push('TO', this.formatAexprConst(cc.cycle_mark_value, context));
3303
+ output.push('DEFAULT', this.formatAexprConst(cc.cycle_mark_default, context));
3304
+ }
3305
+ output.push('USING', quotes_1.QuoteUtils.quoteIdentifier(cc.cycle_path_column));
3306
+ }
3206
3307
  return output.join(' ');
3207
3308
  }
3309
+ // Renders a constant in the restricted AexprConst grammar (e.g. `point '(1,1)'`
3310
+ // rather than `CAST('(1,1)' AS point)`).
3311
+ formatAexprConst(node, context) {
3312
+ const cast = node.TypeCast;
3313
+ if (cast?.arg?.A_Const?.sval && cast.typeName) {
3314
+ return `${this.TypeName(cast.typeName, context)} ${this.visit(cast.arg, context)}`;
3315
+ }
3316
+ return this.visit(node, context);
3317
+ }
3208
3318
  ParamRef(node, context) {
3209
3319
  return `$${node.number}`;
3210
3320
  }
@@ -3473,9 +3583,8 @@ class Deparser {
3473
3583
  output.push('INCLUDE');
3474
3584
  output.push(context.parens(includeStrs.join(', ')));
3475
3585
  }
3476
- if (node.whereClause) {
3477
- output.push('WHERE');
3478
- output.push(this.visit(node.whereClause, context));
3586
+ if (node.nulls_not_distinct) {
3587
+ output.push('NULLS NOT DISTINCT');
3479
3588
  }
3480
3589
  if (node.options && node.options.length > 0) {
3481
3590
  const indexContext = context.spawn('IndexStmt');
@@ -3483,13 +3592,14 @@ class Deparser {
3483
3592
  output.push('WITH');
3484
3593
  output.push(context.parens(optionStrs.join(', ')));
3485
3594
  }
3486
- if (node.nulls_not_distinct) {
3487
- output.push('NULLS NOT DISTINCT');
3488
- }
3489
3595
  if (node.tableSpace) {
3490
3596
  output.push('TABLESPACE');
3491
3597
  output.push(quotes_1.QuoteUtils.quoteIdentifier(node.tableSpace));
3492
3598
  }
3599
+ if (node.whereClause) {
3600
+ output.push('WHERE');
3601
+ output.push(this.visit(node.whereClause, context));
3602
+ }
3493
3603
  return output.join(' ');
3494
3604
  }
3495
3605
  IndexElem(node, context) {
@@ -3513,7 +3623,7 @@ class Deparser {
3513
3623
  const opclassOpts = list_utils_1.ListUtils.unwrapList(node.opclassopts).map(opt => {
3514
3624
  if (opt.DefElem && opt.DefElem.arg && this.getNodeType(opt.DefElem.arg) === 'String') {
3515
3625
  const stringData = this.getNodeData(opt.DefElem.arg);
3516
- return `${opt.DefElem.defname}='${stringData.sval}'`;
3626
+ return `${quotes_1.QuoteUtils.quoteIdentifierAfterDot(opt.DefElem.defname)}=${quotes_1.QuoteUtils.escape(stringData.sval)}`;
3517
3627
  }
3518
3628
  return this.visit(opt, context.spawn('IndexElem'));
3519
3629
  });
@@ -3564,12 +3674,12 @@ class Deparser {
3564
3674
  }
3565
3675
  PartitionCmd(node, context) {
3566
3676
  const output = [];
3677
+ if (node.name) {
3678
+ output.push(this.RangeVar(node.name, context));
3679
+ }
3567
3680
  if (node.concurrent) {
3568
3681
  output.push('CONCURRENTLY');
3569
3682
  }
3570
- if (node.name) {
3571
- output.push(this.visit(node.name, context));
3572
- }
3573
3683
  if (node.bound) {
3574
3684
  if (node.bound.strategy === 'l' && node.bound.listdatums) {
3575
3685
  output.push('FOR VALUES IN');
@@ -3594,9 +3704,9 @@ class Deparser {
3594
3704
  output.push(`(${upperValues})`);
3595
3705
  }
3596
3706
  }
3597
- else if (node.bound.strategy === 'h' && node.bound.modulus !== undefined && node.bound.remainder !== undefined) {
3707
+ else if (node.bound.strategy === 'h') {
3598
3708
  output.push('FOR VALUES WITH');
3599
- output.push(`(modulus ${node.bound.modulus}, remainder ${node.bound.remainder})`);
3709
+ output.push(`(MODULUS ${node.bound.modulus ?? 0}, REMAINDER ${node.bound.remainder ?? 0})`);
3600
3710
  }
3601
3711
  else if (node.bound.is_default) {
3602
3712
  output.push('DEFAULT');
@@ -3727,12 +3837,7 @@ class Deparser {
3727
3837
  if (context.isPretty()) {
3728
3838
  // For complex JOIN conditions, format with proper indentation
3729
3839
  if (qualsStr.includes('AND') || qualsStr.includes('OR') || qualsStr.length > 50) {
3730
- if (this.containsMultilineStringLiteral(qualsStr)) {
3731
- output.push(` ON ${qualsStr}`);
3732
- }
3733
- else {
3734
- output.push(` ON${context.newline()}${context.indent(qualsStr)}`);
3735
- }
3840
+ output.push(` ON${context.newline()}${context.indent(qualsStr)}`);
3736
3841
  }
3737
3842
  else {
3738
3843
  output.push(` ON ${qualsStr}`);
@@ -3814,19 +3919,19 @@ class Deparser {
3814
3919
  case 'TRANS_STMT_PREPARE':
3815
3920
  output.push('PREPARE TRANSACTION');
3816
3921
  if (node.gid) {
3817
- output.push(`'${node.gid}'`);
3922
+ output.push(quotes_1.QuoteUtils.escape(node.gid));
3818
3923
  }
3819
3924
  break;
3820
3925
  case 'TRANS_STMT_COMMIT_PREPARED':
3821
3926
  output.push('COMMIT PREPARED');
3822
3927
  if (node.gid) {
3823
- output.push(`'${node.gid}'`);
3928
+ output.push(quotes_1.QuoteUtils.escape(node.gid));
3824
3929
  }
3825
3930
  break;
3826
3931
  case 'TRANS_STMT_ROLLBACK_PREPARED':
3827
3932
  output.push('ROLLBACK PREPARED');
3828
3933
  if (node.gid) {
3829
- output.push(`'${node.gid}'`);
3934
+ output.push(quotes_1.QuoteUtils.escape(node.gid));
3830
3935
  }
3831
3936
  break;
3832
3937
  default:
@@ -3893,17 +3998,25 @@ class Deparser {
3893
3998
  switch (node.kind) {
3894
3999
  case 'VAR_SET_VALUE':
3895
4000
  const localPrefix = node.is_local ? 'LOCAL ' : '';
4001
+ if (node.jumble_args && node.name === 'xmloption' && node.args) {
4002
+ const xmlValue = this.getNodeData(list_utils_1.ListUtils.unwrapList(node.args)[0]);
4003
+ const xmlOption = (typeof xmlValue.sval === 'object' ? xmlValue.sval.sval : xmlValue.sval) || '';
4004
+ return `SET ${localPrefix}XML OPTION ${xmlOption.toUpperCase()}`;
4005
+ }
3896
4006
  const args = node.args ? list_utils_1.ListUtils.unwrapList(node.args).map(arg => {
3897
4007
  const nodeData = this.getNodeData(arg);
3898
4008
  if (nodeData.sval !== undefined) {
3899
4009
  const svalValue = typeof nodeData.sval === 'object' ? nodeData.sval.sval : nodeData.sval;
3900
- if (svalValue === '' || svalValue.includes(' ') || svalValue.includes('-') || /[A-Z]/.test(svalValue) || /^\d/.test(svalValue) || svalValue.includes('.') || svalValue.includes('$') || svalValue.toLowerCase() === 'all' || /^[+-]\d/.test(svalValue)) {
3901
- return `'${svalValue}'`;
4010
+ if (!/^[a-z_][a-z0-9_]*$/.test(svalValue) || svalValue.includes(' ') || svalValue.includes('-') || /[A-Z]/.test(svalValue) || /^\d/.test(svalValue) || svalValue.includes('.') || svalValue.includes('$') || svalValue.toLowerCase() === 'all' || /^[+-]\d/.test(svalValue)) {
4011
+ return quotes_1.QuoteUtils.escape(svalValue);
3902
4012
  }
3903
4013
  return svalValue;
3904
4014
  }
3905
4015
  return this.visit(arg, context);
3906
4016
  }).join(', ') : '';
4017
+ if (node.jumble_args && node.name === 'timezone') {
4018
+ return `SET ${localPrefix}TIME ZONE ${args}`;
4019
+ }
3907
4020
  // Handle args - always include TO clause if args exist (even if empty string)
3908
4021
  const paramName = quotes_1.QuoteUtils.quoteIdentifier(node.name);
3909
4022
  if (!node.args || node.args.length === 0) {
@@ -3911,6 +4024,9 @@ class Deparser {
3911
4024
  }
3912
4025
  return `SET ${localPrefix}${paramName} TO ${args}`;
3913
4026
  case 'VAR_SET_DEFAULT':
4027
+ if (node.jumble_args && node.name === 'timezone') {
4028
+ return `SET ${node.is_local ? 'LOCAL ' : ''}TIME ZONE DEFAULT`;
4029
+ }
3914
4030
  const defaultParamName = quotes_1.QuoteUtils.quoteIdentifier(node.name);
3915
4031
  return `SET ${defaultParamName} TO DEFAULT`;
3916
4032
  case 'VAR_SET_CURRENT':
@@ -4215,7 +4331,7 @@ class Deparser {
4215
4331
  if (objList && objList.List && objList.List.items) {
4216
4332
  const items = objList.List.items.map((item) => {
4217
4333
  if (item.String && item.String.sval) {
4218
- return item.String.sval;
4334
+ return quotes_1.QuoteUtils.quoteIdentifier(item.String.sval);
4219
4335
  }
4220
4336
  return this.visit(item, context);
4221
4337
  });
@@ -4438,10 +4554,10 @@ class Deparser {
4438
4554
  output.push('PROGRAM');
4439
4555
  }
4440
4556
  if (node.filename) {
4441
- output.push(`'${node.filename}'`);
4557
+ output.push(quotes_1.QuoteUtils.escape(node.filename));
4442
4558
  }
4443
4559
  else {
4444
- output.push('STDIN');
4560
+ output.push(node.is_from ? 'STDIN' : 'STDOUT');
4445
4561
  }
4446
4562
  if (node.options && node.options.length > 0) {
4447
4563
  output.push('WITH');
@@ -4673,6 +4789,9 @@ class Deparser {
4673
4789
  const nodeData = this.getNodeData(node.def);
4674
4790
  if (nodeData && nodeData.typeName) {
4675
4791
  output.push(this.TypeName(nodeData.typeName, context));
4792
+ if (nodeData.collClause) {
4793
+ output.push(this.CollateClause(nodeData.collClause, context));
4794
+ }
4676
4795
  // Handle USING clause (stored in raw_default for ALTER COLUMN TYPE)
4677
4796
  if (nodeData.raw_default) {
4678
4797
  output.push('USING');
@@ -5022,13 +5141,6 @@ class Deparser {
5022
5141
  output.push('IF EXISTS');
5023
5142
  }
5024
5143
  break;
5025
- case 'AT_CheckNotNull':
5026
- output.push('ALTER COLUMN');
5027
- if (node.name) {
5028
- output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
5029
- }
5030
- output.push('SET NOT NULL');
5031
- break;
5032
5144
  case 'AT_AddIndex':
5033
5145
  output.push('ADD');
5034
5146
  if (node.def) {
@@ -5054,23 +5166,28 @@ class Deparser {
5054
5166
  }
5055
5167
  break;
5056
5168
  case 'AT_AlterConstraint':
5057
- output.push('ALTER CONSTRAINT');
5058
- if (node.def && this.getNodeType(node.def) === 'Constraint') {
5059
- const constraintData = this.getNodeData(node.def);
5060
- if (constraintData.conname) {
5061
- output.push(quotes_1.QuoteUtils.quoteIdentifier(constraintData.conname));
5062
- if (constraintData.deferrable !== undefined) {
5063
- output.push(constraintData.deferrable ? 'DEFERRABLE' : 'NOT DEFERRABLE');
5064
- }
5065
- if (constraintData.initdeferred !== undefined) {
5066
- output.push(constraintData.initdeferred ? 'INITIALLY DEFERRED' : 'INITIALLY IMMEDIATE');
5169
+ if (node.def && this.getNodeType(node.def) === 'ATAlterConstraint') {
5170
+ output.push(this.ATAlterConstraint(this.getNodeData(node.def), context));
5171
+ }
5172
+ else {
5173
+ output.push('ALTER CONSTRAINT');
5174
+ if (node.def && this.getNodeType(node.def) === 'Constraint') {
5175
+ const constraintData = this.getNodeData(node.def);
5176
+ if (constraintData.conname) {
5177
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(constraintData.conname));
5178
+ if (constraintData.deferrable !== undefined) {
5179
+ output.push(constraintData.deferrable ? 'DEFERRABLE' : 'NOT DEFERRABLE');
5180
+ }
5181
+ if (constraintData.initdeferred !== undefined) {
5182
+ output.push(constraintData.initdeferred ? 'INITIALLY DEFERRED' : 'INITIALLY IMMEDIATE');
5183
+ }
5067
5184
  }
5068
5185
  }
5069
- }
5070
- else if (node.name) {
5071
- output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
5072
- if (node.def) {
5073
- output.push(this.visit(node.def, context));
5186
+ else if (node.name) {
5187
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
5188
+ if (node.def) {
5189
+ output.push(this.visit(node.def, context));
5190
+ }
5074
5191
  }
5075
5192
  }
5076
5193
  break;
@@ -5155,9 +5272,8 @@ class Deparser {
5155
5272
  if (node.name) {
5156
5273
  output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
5157
5274
  }
5158
- output.push('SET');
5159
5275
  if (node.def) {
5160
- output.push(this.visit(node.def, context));
5276
+ output.push(this.formatIdentityColumnOptions(node.def, context));
5161
5277
  }
5162
5278
  break;
5163
5279
  case 'AT_DropIdentity':
@@ -5166,11 +5282,8 @@ class Deparser {
5166
5282
  output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
5167
5283
  }
5168
5284
  output.push('DROP IDENTITY');
5169
- if (node.behavior === 'DROP_CASCADE') {
5170
- output.push('CASCADE');
5171
- }
5172
- else if (node.behavior === 'DROP_RESTRICT') {
5173
- output.push('RESTRICT');
5285
+ if (node.missing_ok) {
5286
+ output.push('IF EXISTS');
5174
5287
  }
5175
5288
  break;
5176
5289
  case 'AT_ReAddStatistics':
@@ -5264,6 +5377,7 @@ class Deparser {
5264
5377
  if (bodyType === 'List') {
5265
5378
  const statements = list_utils_1.ListUtils.unwrapList(node.sql_body);
5266
5379
  if (statements.length === 0 || (statements.length === 1 && Object.keys(statements[0]).length === 0)) {
5380
+ // empty body: nothing to emit
5267
5381
  }
5268
5382
  else {
5269
5383
  // Handle nested List structure in BEGIN ATOMIC blocks
@@ -5411,19 +5525,22 @@ class Deparser {
5411
5525
  if (node.arg && this.getNodeType(node.arg) === 'List') {
5412
5526
  const listData = this.getNodeData(node.arg);
5413
5527
  const listItems = list_utils_1.ListUtils.unwrapList(listData.items);
5528
+ const preservedName = this.preserveOperatorDefElemCase(node.defname);
5414
5529
  if (listItems.length === 1 && listItems[0].String) {
5415
- const preservedName = this.preserveOperatorDefElemCase(node.defname);
5416
5530
  return `${preservedName} = ${listItems[0].String.sval}`;
5417
5531
  }
5532
+ if (listItems.length > 1 && listItems.every((item) => item.String)) {
5533
+ return `${preservedName} = OPERATOR(${this.qualifiedOperatorName(listItems)})`;
5534
+ }
5418
5535
  }
5419
5536
  }
5420
5537
  // Handle IndexElem opclassopts - MUST be first to preserve string types
5421
5538
  if (context.parentNodeTypes.includes('IndexElem')) {
5422
5539
  if (node.arg && this.getNodeType(node.arg) === 'String') {
5423
5540
  const stringData = this.getNodeData(node.arg);
5424
- return `${node.defname}='${stringData.sval}'`;
5541
+ return `${quotes_1.QuoteUtils.quoteIdentifierAfterDot(node.defname)}=${quotes_1.QuoteUtils.escape(stringData.sval)}`;
5425
5542
  }
5426
- return `${node.defname}=${this.visit(node.arg, context.spawn('DefElem'))}`;
5543
+ return `${quotes_1.QuoteUtils.quoteIdentifierAfterDot(node.defname)}=${this.formatOptionValue(node.arg, this.visit(node.arg, context.spawn('DefElem')))}`;
5427
5544
  }
5428
5545
  // Handle CREATE OPERATOR boolean flags - MUST be first to preserve case
5429
5546
  if (context.parentNodeTypes.includes('DefineStmt') &&
@@ -5448,9 +5565,7 @@ class Deparser {
5448
5565
  const defElemContext = context.spawn('DefElem');
5449
5566
  const argValue = this.visit(node.arg, defElemContext);
5450
5567
  if (context.parentNodeTypes.includes('CreateFdwStmt') || context.parentNodeTypes.includes('AlterFdwStmt')) {
5451
- const finalValue = typeof argValue === 'string' && !argValue.startsWith("'")
5452
- ? `'${argValue}'`
5453
- : argValue;
5568
+ const finalValue = this.quoteDefElemArg(node.arg, argValue);
5454
5569
  const quotedDefname = quotes_1.QuoteUtils.quoteIdentifier(node.defname);
5455
5570
  if (node.defaction === 'DEFELEM_ADD') {
5456
5571
  return `ADD ${quotedDefname} ${finalValue}`;
@@ -5463,9 +5578,7 @@ class Deparser {
5463
5578
  }
5464
5579
  return `${quotedDefname} ${finalValue}`;
5465
5580
  }
5466
- const quotedValue = typeof argValue === 'string' && !argValue.startsWith("'")
5467
- ? `'${argValue}'`
5468
- : argValue;
5581
+ const quotedValue = this.quoteDefElemArg(node.arg, argValue);
5469
5582
  if (node.defaction === 'DEFELEM_ADD') {
5470
5583
  return `ADD ${node.defname} ${quotedValue}`;
5471
5584
  }
@@ -5497,9 +5610,7 @@ class Deparser {
5497
5610
  }
5498
5611
  const defElemContext = context.spawn('DefElem');
5499
5612
  const argValue = this.visit(node.arg, defElemContext);
5500
- const quotedValue = typeof argValue === 'string' && !argValue.startsWith("'")
5501
- ? `'${argValue}'`
5502
- : argValue;
5613
+ const quotedValue = this.quoteDefElemArg(node.arg, argValue);
5503
5614
  return `PASSWORD ${quotedValue}`;
5504
5615
  }
5505
5616
  }
@@ -5522,6 +5633,9 @@ class Deparser {
5522
5633
  if (listItems.length === 1 && listItems[0].String) {
5523
5634
  return `${node.defname} = ${listItems[0].String.sval}`;
5524
5635
  }
5636
+ if (listItems.length > 1 && listItems.every((item) => item.String)) {
5637
+ return `${node.defname} = OPERATOR(${this.qualifiedOperatorName(listItems)})`;
5638
+ }
5525
5639
  }
5526
5640
  }
5527
5641
  if (context.parentNodeTypes.includes('CreatedbStmt') || context.parentNodeTypes.includes('DropdbStmt')) {
@@ -5564,9 +5678,7 @@ class Deparser {
5564
5678
  }
5565
5679
  }
5566
5680
  if (node.defname === 'validUntil') {
5567
- const quotedValue = typeof argValue === 'string' && !argValue.startsWith("'")
5568
- ? `'${argValue}'`
5569
- : argValue;
5681
+ const quotedValue = this.quoteDefElemArg(node.arg, argValue);
5570
5682
  return `VALID UNTIL ${quotedValue}`;
5571
5683
  }
5572
5684
  if (node.defname === 'adminmembers') {
@@ -5605,6 +5717,9 @@ class Deparser {
5605
5717
  if (node.arg && this.getNodeType(node.arg) === 'List') {
5606
5718
  const listData = this.getNodeData(node.arg);
5607
5719
  const listItems = list_utils_1.ListUtils.unwrapList(listData.items);
5720
+ if (listItems.length === 1 && listItems[0].String?.sval === 'none') {
5721
+ return 'OWNED BY NONE';
5722
+ }
5608
5723
  const parts = listItems.map(item => {
5609
5724
  const itemData = this.getNodeData(item);
5610
5725
  if (this.getNodeType(item) === 'String') {
@@ -5743,10 +5858,8 @@ class Deparser {
5743
5858
  if (context.parentNodeTypes.includes('AlterExtensionStmt')) {
5744
5859
  if (node.defname === 'new_version') {
5745
5860
  // argValue is unquoted due to DefElem context, so we need to quote it
5746
- const quotedValue = typeof argValue === 'string' && !argValue.startsWith("'")
5747
- ? `'${argValue}'`
5748
- : argValue;
5749
- return `UPDATE TO ${quotedValue}`;
5861
+ const quotedValue = this.quoteDefElemArg(node.arg, argValue);
5862
+ return `TO ${quotedValue}`;
5750
5863
  }
5751
5864
  if (node.defname === 'schema') {
5752
5865
  return `SET SCHEMA ${argValue}`;
@@ -5766,28 +5879,37 @@ class Deparser {
5766
5879
  if (node.defname === 'cascade') {
5767
5880
  return argValue === 'true' ? 'CASCADE' : '';
5768
5881
  }
5882
+ if (node.defname === 'new_version') {
5883
+ return `VERSION ${this.quoteDefElemArg(node.arg, argValue)}`;
5884
+ }
5885
+ if (node.defname === 'schema' && node.arg && this.getNodeType(node.arg) === 'String') {
5886
+ return `SCHEMA ${quotes_1.QuoteUtils.quoteIdentifier(this.getNodeData(node.arg).sval)}`;
5887
+ }
5769
5888
  return `${node.defname.toUpperCase()} ${argValue}`;
5770
5889
  }
5771
5890
  // Handle IndexStmt WITH clause options - no quotes, compact formatting
5772
5891
  if (context.parentNodeTypes.includes('IndexStmt')) {
5773
- return `${node.defname}=${argValue}`;
5892
+ return `${quotes_1.QuoteUtils.quoteIdentifierAfterDot(node.defname)}=${this.formatOptionValue(node.arg, argValue)}`;
5774
5893
  }
5775
5894
  // Handle IndexElem opclassopts - preserve string values as strings
5776
5895
  if (context.parentNodeTypes.includes('IndexElem')) {
5777
5896
  if (node.arg && this.getNodeType(node.arg) === 'String') {
5778
5897
  const stringData = this.getNodeData(node.arg);
5779
- return `${node.defname}='${stringData.sval}'`;
5898
+ return `${quotes_1.QuoteUtils.quoteIdentifierAfterDot(node.defname)}=${quotes_1.QuoteUtils.escape(stringData.sval)}`;
5780
5899
  }
5781
- return `${node.defname}=${argValue}`;
5900
+ return `${quotes_1.QuoteUtils.quoteIdentifierAfterDot(node.defname)}=${this.formatOptionValue(node.arg, argValue)}`;
5782
5901
  }
5783
5902
  // Handle CreateStmt table options - no quotes, compact formatting
5784
5903
  if (context.parentNodeTypes.includes('CreateStmt')) {
5904
+ const relOptName = node.defnamespace
5905
+ ? `${quotes_1.QuoteUtils.quoteIdentifier(node.defnamespace)}.${quotes_1.QuoteUtils.quoteIdentifier(node.defname)}`
5906
+ : quotes_1.QuoteUtils.quoteIdentifier(node.defname);
5785
5907
  // For numeric values, use the raw value without quotes
5786
5908
  if (node.arg && this.getNodeType(node.arg) === 'Integer') {
5787
5909
  const integerData = this.getNodeData(node.arg);
5788
- return `${node.defname}=${integerData.ival}`;
5910
+ return `${relOptName}=${formatInteger(integerData.ival)}`;
5789
5911
  }
5790
- return `${node.defname}=${argValue}`;
5912
+ return `${relOptName}=${this.formatOptionValue(node.arg, argValue)}`;
5791
5913
  }
5792
5914
  // Handle CreateEventTrigStmt WHEN clause - use IN syntax for List arguments
5793
5915
  if (context.parentNodeTypes.includes('CreateEventTrigStmt')) {
@@ -5797,7 +5919,7 @@ class Deparser {
5797
5919
  const values = listItems.map(item => {
5798
5920
  if (this.getNodeType(item) === 'String') {
5799
5921
  const stringData = this.getNodeData(item);
5800
- return `'${stringData.sval || ''}'`;
5922
+ return quotes_1.QuoteUtils.escape(stringData.sval || '');
5801
5923
  }
5802
5924
  return this.visit(item, context);
5803
5925
  });
@@ -5812,7 +5934,7 @@ class Deparser {
5812
5934
  if (node.arg && this.getNodeType(node.arg) === 'TypeName') {
5813
5935
  return `${optionName} = ${argValue}`;
5814
5936
  }
5815
- return `${optionName} = ${argValue}`;
5937
+ return `${optionName} = ${this.formatOptionValue(node.arg, argValue)}`;
5816
5938
  }
5817
5939
  // Handle ViewStmt WITH options - don't quote numeric values
5818
5940
  if (context.parentNodeTypes.includes('ViewStmt')) {
@@ -5826,15 +5948,27 @@ class Deparser {
5826
5948
  }
5827
5949
  // Handle CopyStmt WITH clause options - uppercase format without quotes
5828
5950
  if (context.parentNodeTypes.includes('CopyStmt')) {
5829
- if (node.defname === 'format' && node.arg && this.getNodeType(node.arg) === 'String') {
5830
- const stringData = this.getNodeData(node.arg);
5831
- return `FORMAT ${stringData.sval.toUpperCase()}`;
5951
+ const optName = node.defname.toUpperCase();
5952
+ if (!node.arg) {
5953
+ return optName;
5954
+ }
5955
+ const copyArgType = this.getNodeType(node.arg);
5956
+ if (copyArgType === 'Boolean' && this.getNodeData(node.arg).boolval !== false) {
5957
+ return optName;
5832
5958
  }
5833
- // Handle other COPY options with uppercase defname
5834
- if (node.arg) {
5835
- return `${node.defname.toUpperCase()} ${argValue}`;
5959
+ if (copyArgType === 'List') {
5960
+ const items = list_utils_1.ListUtils.unwrapList(this.getNodeData(node.arg).items)
5961
+ .map(item => this.visit(item, context));
5962
+ return `${optName} (${items.join(', ')})`;
5963
+ }
5964
+ if (copyArgType === 'String') {
5965
+ const sval = this.getNodeData(node.arg).sval;
5966
+ if (node.defname === 'format') {
5967
+ return `${optName} ${sval}`;
5968
+ }
5969
+ return `${optName} ${quotes_1.QuoteUtils.escape(sval)}`;
5836
5970
  }
5837
- return node.defname.toUpperCase();
5971
+ return `${optName} ${argValue}`;
5838
5972
  }
5839
5973
  // Handle CREATE OPERATOR and CREATE TYPE context
5840
5974
  if (context.parentNodeTypes.includes('DefineStmt')) {
@@ -5873,7 +6007,7 @@ class Deparser {
5873
6007
  return `${quotedDefname} = ${stringData.sval}`;
5874
6008
  }
5875
6009
  // Regular string literals get single quotes
5876
- return `${quotedDefname} = '${stringData.sval}'`;
6010
+ return `${quotedDefname} = ${quotes_1.QuoteUtils.escape(stringData.sval)}`;
5877
6011
  }
5878
6012
  return `${quotedDefname} = ${argValue}`;
5879
6013
  }
@@ -5894,7 +6028,7 @@ class Deparser {
5894
6028
  return `${node.defname} = ${stringData.sval}`;
5895
6029
  }
5896
6030
  // Regular string literals get single quotes
5897
- return `${node.defname} = '${stringData.sval}'`;
6031
+ return `${node.defname} = ${quotes_1.QuoteUtils.escape(stringData.sval)}`;
5898
6032
  }
5899
6033
  if (node.arg && this.getNodeType(node.arg) === 'Boolean') {
5900
6034
  const boolData = this.getNodeData(node.arg);
@@ -5915,6 +6049,10 @@ class Deparser {
5915
6049
  return `${node.defname} = ${argValue}`;
5916
6050
  }
5917
6051
  }
6052
+ const argType = this.getNodeType(node.arg);
6053
+ if (argType === 'TypeName' || argType === 'Integer' || argType === 'Float' || argType === 'Boolean') {
6054
+ return `${node.defname} = ${argValue}`;
6055
+ }
5918
6056
  const quotedValue = typeof argValue === 'string'
5919
6057
  ? quotes_1.QuoteUtils.escape(argValue)
5920
6058
  : argValue;
@@ -5940,7 +6078,7 @@ class Deparser {
5940
6078
  }
5941
6079
  if (node.location) {
5942
6080
  output.push('LOCATION');
5943
- output.push(`'${node.location}'`);
6081
+ output.push(quotes_1.QuoteUtils.escape(node.location));
5944
6082
  }
5945
6083
  if (node.options && node.options.length > 0) {
5946
6084
  output.push('WITH');
@@ -6004,6 +6142,7 @@ class Deparser {
6004
6142
  if (node.extname) {
6005
6143
  output.push(this.quoteIfNeeded(node.extname));
6006
6144
  }
6145
+ output.push('UPDATE');
6007
6146
  if (node.options && node.options.length > 0) {
6008
6147
  const extContext = context.spawn('AlterExtensionStmt');
6009
6148
  const options = list_utils_1.ListUtils.unwrapList(node.options)
@@ -6273,7 +6412,7 @@ class Deparser {
6273
6412
  }
6274
6413
  if (node.payload !== null && node.payload !== undefined) {
6275
6414
  output.push(',');
6276
- output.push(`'${node.payload}'`);
6415
+ output.push(quotes_1.QuoteUtils.escape(node.payload));
6277
6416
  }
6278
6417
  return output.join(' ');
6279
6418
  }
@@ -6301,7 +6440,7 @@ class Deparser {
6301
6440
  if (!node.filename) {
6302
6441
  throw new Error('LoadStmt requires filename');
6303
6442
  }
6304
- return `LOAD '${node.filename}'`;
6443
+ return `LOAD ${quotes_1.QuoteUtils.escape(node.filename)}`;
6305
6444
  }
6306
6445
  DiscardStmt(node, context) {
6307
6446
  switch (node.target) {
@@ -6317,6 +6456,13 @@ class Deparser {
6317
6456
  throw new Error(`Unsupported DiscardStmt target: ${node.target}`);
6318
6457
  }
6319
6458
  }
6459
+ /** `schema.op` for an operator name list: schema parts quoted, the operator symbol as is. */
6460
+ qualifiedOperatorName(objname) {
6461
+ const names = list_utils_1.ListUtils.unwrapList(objname ?? []).map((n) => (n.String ? n.String.sval : 'unknown'));
6462
+ if (names.length === 0)
6463
+ return 'unknown';
6464
+ return [...names.slice(0, -1).map((n) => quotes_1.QuoteUtils.quoteIdentifier(n)), names[names.length - 1]].join('.');
6465
+ }
6320
6466
  CommentStmt(node, context) {
6321
6467
  const output = ['COMMENT ON'];
6322
6468
  if (node.objtype) {
@@ -6348,6 +6494,9 @@ class Deparser {
6348
6494
  case 'OBJECT_TABCONSTRAINT':
6349
6495
  output.push('CONSTRAINT');
6350
6496
  break;
6497
+ case 'OBJECT_DOMCONSTRAINT':
6498
+ output.push('CONSTRAINT');
6499
+ break;
6351
6500
  case 'OBJECT_TRIGGER':
6352
6501
  output.push('TRIGGER');
6353
6502
  break;
@@ -6424,23 +6573,30 @@ class Deparser {
6424
6573
  output.push(objectParts.join('.'));
6425
6574
  }
6426
6575
  }
6427
- else if (node.objtype === 'OBJECT_TRIGGER') {
6576
+ else if (node.objtype === 'OBJECT_DOMCONSTRAINT') {
6428
6577
  if (objectParts.length === 2) {
6429
- const [table, trigger] = objectParts;
6430
- output.push(trigger);
6578
+ const [domain, constraint] = objectParts;
6579
+ output.push(constraint, 'ON DOMAIN', domain);
6580
+ }
6581
+ else {
6582
+ output.push(objectParts.join('.'));
6583
+ }
6584
+ }
6585
+ else if (node.objtype === 'OBJECT_TRIGGER') {
6586
+ if (objectParts.length >= 2) {
6587
+ output.push(objectParts[objectParts.length - 1]);
6431
6588
  output.push('ON');
6432
- output.push(table);
6589
+ output.push(objectParts.slice(0, -1).join('.'));
6433
6590
  }
6434
6591
  else {
6435
6592
  output.push(objectParts.join('.'));
6436
6593
  }
6437
6594
  }
6438
6595
  else if (node.objtype === 'OBJECT_RULE') {
6439
- if (objectParts.length === 2) {
6440
- const [table, rule] = objectParts;
6441
- output.push(rule);
6596
+ if (objectParts.length >= 2) {
6597
+ output.push(objectParts[objectParts.length - 1]);
6442
6598
  output.push('ON');
6443
- output.push(table);
6599
+ output.push(objectParts.slice(0, -1).join('.'));
6444
6600
  }
6445
6601
  else {
6446
6602
  output.push(objectParts.join('.'));
@@ -6451,8 +6607,7 @@ class Deparser {
6451
6607
  // For operators, we need to handle ObjectWithArgs structure
6452
6608
  if (node.object && node.object.ObjectWithArgs) {
6453
6609
  const objWithArgs = node.object.ObjectWithArgs;
6454
- let operatorName = objWithArgs.objname && objWithArgs.objname[0] && objWithArgs.objname[0].String
6455
- ? objWithArgs.objname[0].String.sval : 'unknown';
6610
+ let operatorName = this.qualifiedOperatorName(objWithArgs.objname);
6456
6611
  if (operatorName.startsWith('"') && operatorName.endsWith('"')) {
6457
6612
  operatorName = operatorName.slice(1, -1);
6458
6613
  }
@@ -6490,7 +6645,13 @@ class Deparser {
6490
6645
  }
6491
6646
  else if (node.objtype === 'OBJECT_POLICY') {
6492
6647
  // Handle POLICY syntax: COMMENT ON POLICY policy_name ON table_name IS 'comment'
6493
- if (objectParts.length === 2) {
6648
+ if (objectParts.length === 3) {
6649
+ const [schema, table, policy] = objectParts;
6650
+ output.push(policy);
6651
+ output.push('ON');
6652
+ output.push(`${schema}.${table}`);
6653
+ }
6654
+ else if (objectParts.length === 2) {
6494
6655
  const [table, policy] = objectParts;
6495
6656
  output.push(policy);
6496
6657
  output.push('ON');
@@ -6513,6 +6674,9 @@ class Deparser {
6513
6674
  output.push(objectParts.join('.'));
6514
6675
  }
6515
6676
  }
6677
+ else if (node.objtype === 'OBJECT_CAST' && objectParts.length === 2) {
6678
+ output.push(`(${objectParts[0]} AS ${objectParts[1]})`);
6679
+ }
6516
6680
  else {
6517
6681
  output.push(objectParts.join('.'));
6518
6682
  }
@@ -6521,8 +6685,7 @@ class Deparser {
6521
6685
  else if (node.objtype === 'OBJECT_OPERATOR' && node.object && node.object.ObjectWithArgs) {
6522
6686
  // Handle direct ObjectWithArgs for OPERATOR syntax: COMMENT ON OPERATOR -(NONE, integer) IS 'comment'
6523
6687
  const objWithArgs = node.object.ObjectWithArgs;
6524
- let operatorName = objWithArgs.objname && objWithArgs.objname[0] && objWithArgs.objname[0].String
6525
- ? objWithArgs.objname[0].String.sval : 'unknown';
6688
+ let operatorName = this.qualifiedOperatorName(objWithArgs.objname);
6526
6689
  // Remove quotes from operator name if present
6527
6690
  if (operatorName.startsWith('"') && operatorName.endsWith('"')) {
6528
6691
  operatorName = operatorName.slice(1, -1);
@@ -6755,7 +6918,7 @@ class Deparser {
6755
6918
  output.push(quotes_1.QuoteUtils.quoteIdentifier(node.pubname));
6756
6919
  }
6757
6920
  if (node.pubobjects && node.pubobjects.length > 0) {
6758
- output.push('FOR', 'TABLE');
6921
+ output.push('FOR');
6759
6922
  const tables = list_utils_1.ListUtils.unwrapList(node.pubobjects).map(table => this.visit(table, context));
6760
6923
  output.push(tables.join(', '));
6761
6924
  }
@@ -6776,7 +6939,7 @@ class Deparser {
6776
6939
  }
6777
6940
  output.push('CONNECTION');
6778
6941
  if (node.conninfo) {
6779
- output.push(`'${node.conninfo}'`);
6942
+ output.push(quotes_1.QuoteUtils.escape(node.conninfo));
6780
6943
  }
6781
6944
  output.push('PUBLICATION');
6782
6945
  if (node.publication && node.publication.length > 0) {
@@ -6795,8 +6958,9 @@ class Deparser {
6795
6958
  if (node.pubname) {
6796
6959
  output.push(quotes_1.QuoteUtils.quoteIdentifier(node.pubname));
6797
6960
  }
6798
- if (node.action) {
6799
- switch (node.action) {
6961
+ const hasObjects = node.pubobjects && node.pubobjects.length > 0;
6962
+ if (hasObjects) {
6963
+ switch (node.action || 'AP_AddObjects') {
6800
6964
  case 'AP_AddObjects':
6801
6965
  output.push('ADD');
6802
6966
  break;
@@ -6809,17 +6973,11 @@ class Deparser {
6809
6973
  default:
6810
6974
  throw new Error(`Unsupported AlterPublicationStmt action: ${node.action}`);
6811
6975
  }
6812
- }
6813
- if (node.for_all_tables) {
6814
- output.push('FOR ALL TABLES');
6815
- }
6816
- else if (node.pubobjects && node.pubobjects.length > 0) {
6817
- output.push('FOR TABLE');
6818
6976
  const objects = list_utils_1.ListUtils.unwrapList(node.pubobjects).map(obj => this.visit(obj, context));
6819
6977
  output.push(objects.join(', '));
6820
6978
  }
6821
- if (node.options && node.options.length > 0) {
6822
- output.push('WITH');
6979
+ else if (node.options && node.options.length > 0) {
6980
+ output.push('SET');
6823
6981
  const options = list_utils_1.ListUtils.unwrapList(node.options).map(opt => this.visit(opt, context));
6824
6982
  output.push(`(${options.join(', ')})`);
6825
6983
  }
@@ -6830,42 +6988,67 @@ class Deparser {
6830
6988
  if (node.subname) {
6831
6989
  output.push(quotes_1.QuoteUtils.quoteIdentifier(node.subname));
6832
6990
  }
6833
- if (node.kind) {
6834
- switch (node.kind) {
6991
+ const options = node.options && node.options.length > 0
6992
+ ? `(${list_utils_1.ListUtils.unwrapList(node.options).map(opt => this.visit(opt, context)).join(', ')})`
6993
+ : null;
6994
+ const publications = node.publication && node.publication.length > 0
6995
+ ? list_utils_1.ListUtils.unwrapList(node.publication).map(pub => this.visit(pub, context)).join(', ')
6996
+ : null;
6997
+ {
6998
+ switch (node.kind || 'ALTER_SUBSCRIPTION_OPTIONS') {
6835
6999
  case 'ALTER_SUBSCRIPTION_OPTIONS':
6836
7000
  output.push('SET');
7001
+ if (options)
7002
+ output.push(options);
6837
7003
  break;
6838
7004
  case 'ALTER_SUBSCRIPTION_CONNECTION':
6839
7005
  output.push('CONNECTION');
6840
7006
  if (node.conninfo) {
6841
- output.push(`'${node.conninfo}'`);
7007
+ output.push(quotes_1.QuoteUtils.escape(node.conninfo));
6842
7008
  }
6843
7009
  break;
6844
7010
  case 'ALTER_SUBSCRIPTION_SET_PUBLICATION':
6845
7011
  output.push('SET PUBLICATION');
6846
- if (node.publication && node.publication.length > 0) {
6847
- const publications = list_utils_1.ListUtils.unwrapList(node.publication).map(pub => this.visit(pub, context));
6848
- output.push(publications.join(', '));
6849
- }
7012
+ if (publications)
7013
+ output.push(publications);
7014
+ if (options)
7015
+ output.push('WITH', options);
7016
+ break;
7017
+ case 'ALTER_SUBSCRIPTION_ADD_PUBLICATION':
7018
+ output.push('ADD PUBLICATION');
7019
+ if (publications)
7020
+ output.push(publications);
7021
+ if (options)
7022
+ output.push('WITH', options);
7023
+ break;
7024
+ case 'ALTER_SUBSCRIPTION_DROP_PUBLICATION':
7025
+ output.push('DROP PUBLICATION');
7026
+ if (publications)
7027
+ output.push(publications);
7028
+ if (options)
7029
+ output.push('WITH', options);
6850
7030
  break;
6851
7031
  case 'ALTER_SUBSCRIPTION_REFRESH':
6852
7032
  output.push('REFRESH PUBLICATION');
7033
+ if (options)
7034
+ output.push('WITH', options);
6853
7035
  break;
6854
- case 'ALTER_SUBSCRIPTION_ENABLED':
6855
- output.push('ENABLE');
7036
+ case 'ALTER_SUBSCRIPTION_ENABLED': {
7037
+ const enabled = list_utils_1.ListUtils.unwrapList(node.options).find(opt => opt.DefElem?.defname === 'enabled');
7038
+ const arg = enabled?.DefElem?.arg;
7039
+ const isEnabled = arg?.Boolean ? arg.Boolean.boolval !== false : true;
7040
+ output.push(isEnabled ? 'ENABLE' : 'DISABLE');
6856
7041
  break;
7042
+ }
6857
7043
  case 'ALTER_SUBSCRIPTION_SKIP':
6858
7044
  output.push('SKIP');
7045
+ if (options)
7046
+ output.push(options);
6859
7047
  break;
6860
7048
  default:
6861
7049
  throw new Error(`Unsupported AlterSubscriptionStmt kind: ${node.kind}`);
6862
7050
  }
6863
7051
  }
6864
- if (node.options && node.options.length > 0) {
6865
- output.push('WITH');
6866
- const options = list_utils_1.ListUtils.unwrapList(node.options).map(opt => this.visit(opt, context));
6867
- output.push(`(${options.join(', ')})`);
6868
- }
6869
7052
  return output.join(' ');
6870
7053
  }
6871
7054
  DropSubscriptionStmt(node, context) {
@@ -7694,6 +7877,15 @@ class Deparser {
7694
7877
  output.push(this.visit(node.object, context));
7695
7878
  }
7696
7879
  }
7880
+ else if ((node.renameType === 'OBJECT_TYPE' || node.renameType === 'OBJECT_DOMAIN') && node.object.List) {
7881
+ // Qualified type names - join List parts with dots
7882
+ const items = list_utils_1.ListUtils.unwrapList(node.object);
7883
+ const parts = items
7884
+ .map((item) => item.String?.sval)
7885
+ .filter((s) => typeof s === 'string')
7886
+ .map((s) => this.quoteIfNeeded(s));
7887
+ output.push(parts.join('.'));
7888
+ }
7697
7889
  else if (node.renameType === 'OBJECT_SCHEMA' && node.object.List) {
7698
7890
  // Handle schema names - extract from List structure
7699
7891
  const items = list_utils_1.ListUtils.unwrapList(node.object);
@@ -7833,6 +8025,9 @@ class Deparser {
7833
8025
  else if (node.objtype === 'OBJECT_LANGUAGE') {
7834
8026
  output.push('LANGUAGE');
7835
8027
  }
8028
+ else if (node.objtype === 'OBJECT_SEQUENCE') {
8029
+ output.push('SEQUENCE');
8030
+ }
7836
8031
  else if (node.objtype === 'OBJECT_FUNCTION') {
7837
8032
  output.push('FUNCTION');
7838
8033
  }
@@ -7858,8 +8053,11 @@ class Deparser {
7858
8053
  output.push('DATABASE');
7859
8054
  }
7860
8055
  if (node.objects && node.objects.length > 0) {
8056
+ const qualifiedKinds = ['OBJECT_TYPE', 'OBJECT_DOMAIN'];
7861
8057
  const objects = list_utils_1.ListUtils.unwrapList(node.objects)
7862
- .map(obj => this.visit(obj, context))
8058
+ .map(obj => qualifiedKinds.includes(node.objtype) && obj.List
8059
+ ? list_utils_1.ListUtils.unwrapList(obj.List.items).map((item) => this.visit(item, context)).join('.')
8060
+ : this.visit(obj, context))
7863
8061
  .join(', ');
7864
8062
  output.push(objects);
7865
8063
  }
@@ -7898,8 +8096,11 @@ class Deparser {
7898
8096
  break;
7899
8097
  default:
7900
8098
  if (node.objects && node.objects.length > 0) {
8099
+ const qualifiedKinds = ['OBJECT_TYPE', 'OBJECT_DOMAIN'];
7901
8100
  const objects = list_utils_1.ListUtils.unwrapList(node.objects)
7902
- .map(obj => this.visit(obj, context))
8101
+ .map(obj => qualifiedKinds.includes(node.objtype) && obj.List
8102
+ ? list_utils_1.ListUtils.unwrapList(obj.List.items).map((item) => this.visit(item, context)).join('.')
8103
+ : this.visit(obj, context))
7903
8104
  .join(', ');
7904
8105
  output.push(objects);
7905
8106
  }
@@ -7919,6 +8120,9 @@ class Deparser {
7919
8120
  if (node.grant_option && node.is_grant) {
7920
8121
  output.push('WITH GRANT OPTION');
7921
8122
  }
8123
+ if (node.grantor) {
8124
+ output.push('GRANTED BY', this.RoleSpec(node.grantor, context));
8125
+ }
7922
8126
  // Only add behavior clauses for REVOKE statements, not for GRANT statements in ALTER DEFAULT PRIVILEGES
7923
8127
  if (!node.is_grant) {
7924
8128
  if (node.behavior === 'DROP_CASCADE') {
@@ -7932,57 +8136,34 @@ class Deparser {
7932
8136
  }
7933
8137
  GrantRoleStmt(node, context) {
7934
8138
  const output = [];
7935
- // Check for inherit, admin, and set options first to place them correctly
7936
- let hasInheritOption = false;
7937
- let hasAdminOption = false;
7938
- let hasSetOption = false;
7939
- let inheritValue;
7940
- let adminValue;
7941
- let setValue;
7942
- if (node.opt && node.opt.length > 0) {
7943
- const options = list_utils_1.ListUtils.unwrapList(node.opt);
7944
- const inheritOption = options.find(opt => opt.DefElem && opt.DefElem.defname === 'inherit');
7945
- const adminOption = options.find(opt => (opt.String && opt.String.sval === 'admin') ||
7946
- (opt.DefElem && opt.DefElem.defname === 'admin'));
7947
- const setOption = options.find(opt => opt.DefElem && opt.DefElem.defname === 'set');
7948
- if (inheritOption && inheritOption.DefElem) {
7949
- hasInheritOption = true;
7950
- inheritValue = inheritOption.DefElem.arg?.Boolean?.boolval;
7951
- }
7952
- if (adminOption) {
7953
- hasAdminOption = true;
7954
- if (adminOption.DefElem && adminOption.DefElem.arg) {
7955
- adminValue = adminOption.DefElem.arg.Boolean?.boolval;
7956
- }
7957
- }
7958
- if (setOption && setOption.DefElem) {
7959
- hasSetOption = true;
7960
- setValue = setOption.DefElem.arg?.Boolean?.boolval;
7961
- }
7962
- }
8139
+ const options = list_utils_1.ListUtils.unwrapList(node.opt)
8140
+ .map(opt => opt.DefElem)
8141
+ .filter((opt) => !!opt && !!opt.defname);
8142
+ const optionValue = (opt) => {
8143
+ if (!opt.arg)
8144
+ return true;
8145
+ const argType = this.getNodeType(opt.arg);
8146
+ const argData = this.getNodeData(opt.arg);
8147
+ if (argType === 'Boolean')
8148
+ return argData.boolval !== false;
8149
+ if (argType === 'String')
8150
+ return argData.sval !== 'false';
8151
+ return true;
8152
+ };
8153
+ const roles = list_utils_1.ListUtils.unwrapList(node.granted_roles)
8154
+ .map(role => role.AccessPriv?.priv_name
8155
+ ? quotes_1.QuoteUtils.quoteIdentifier(role.AccessPriv.priv_name)
8156
+ : this.visit(role, context))
8157
+ .join(', ');
7963
8158
  if (node.is_grant) {
7964
- output.push('GRANT');
8159
+ output.push('GRANT', roles, 'TO');
7965
8160
  }
7966
8161
  else {
7967
8162
  output.push('REVOKE');
7968
- if (hasInheritOption) {
7969
- output.push('INHERIT OPTION FOR');
7970
- }
7971
- else if (hasAdminOption) {
7972
- output.push('ADMIN OPTION FOR');
8163
+ if (options.length > 0) {
8164
+ output.push(`${options[0].defname.toUpperCase()} OPTION FOR`);
7973
8165
  }
7974
- }
7975
- if (node.granted_roles && node.granted_roles.length > 0) {
7976
- const roles = list_utils_1.ListUtils.unwrapList(node.granted_roles)
7977
- .map(role => this.visit(role, context))
7978
- .join(', ');
7979
- output.push(roles);
7980
- }
7981
- if (node.is_grant) {
7982
- output.push('TO');
7983
- }
7984
- else {
7985
- output.push('FROM');
8166
+ output.push(roles, 'FROM');
7986
8167
  }
7987
8168
  if (node.grantee_roles && node.grantee_roles.length > 0) {
7988
8169
  const grantees = list_utils_1.ListUtils.unwrapList(node.grantee_roles)
@@ -7990,38 +8171,15 @@ class Deparser {
7990
8171
  .join(', ');
7991
8172
  output.push(grantees);
7992
8173
  }
7993
- if (node.is_grant) {
7994
- const withOptions = [];
7995
- if (hasAdminOption) {
7996
- if (adminValue === true) {
7997
- withOptions.push('ADMIN OPTION');
7998
- }
7999
- else if (adminValue === false) {
8000
- withOptions.push('ADMIN FALSE');
8001
- }
8002
- else {
8003
- withOptions.push('ADMIN OPTION');
8004
- }
8005
- }
8006
- if (hasInheritOption) {
8007
- if (inheritValue === true) {
8008
- withOptions.push('INHERIT OPTION');
8009
- }
8010
- else if (inheritValue === false) {
8011
- withOptions.push('INHERIT FALSE');
8012
- }
8013
- }
8014
- if (hasSetOption) {
8015
- if (setValue === true) {
8016
- withOptions.push('SET TRUE');
8017
- }
8018
- else if (setValue === false) {
8019
- withOptions.push('SET FALSE');
8020
- }
8021
- }
8022
- if (withOptions.length > 0) {
8023
- output.push('WITH', withOptions.join(', '));
8024
- }
8174
+ if (node.is_grant && options.length > 0) {
8175
+ const withOptions = options.map(opt => `${opt.defname.toUpperCase()} ${optionValue(opt) ? 'TRUE' : 'FALSE'}`);
8176
+ output.push('WITH', withOptions.join(', '));
8177
+ }
8178
+ if (node.grantor) {
8179
+ output.push('GRANTED BY', this.RoleSpec(node.grantor, context));
8180
+ }
8181
+ if (!node.is_grant && node.behavior === 'DROP_CASCADE') {
8182
+ output.push('CASCADE');
8025
8183
  }
8026
8184
  return output.join(' ');
8027
8185
  }
@@ -8070,7 +8228,7 @@ class Deparser {
8070
8228
  }
8071
8229
  output.push('IS');
8072
8230
  if (node.label) {
8073
- output.push(`'${node.label}'`);
8231
+ output.push(quotes_1.QuoteUtils.escape(node.label));
8074
8232
  }
8075
8233
  else {
8076
8234
  output.push('NULL');
@@ -8127,10 +8285,10 @@ class Deparser {
8127
8285
  output.push(conversionName);
8128
8286
  }
8129
8287
  if (node.for_encoding_name) {
8130
- output.push('FOR', `'${node.for_encoding_name}'`);
8288
+ output.push('FOR', quotes_1.QuoteUtils.escape(node.for_encoding_name));
8131
8289
  }
8132
8290
  if (node.to_encoding_name) {
8133
- output.push('TO', `'${node.to_encoding_name}'`);
8291
+ output.push('TO', quotes_1.QuoteUtils.escape(node.to_encoding_name));
8134
8292
  }
8135
8293
  if (node.func_name && node.func_name.length > 0) {
8136
8294
  output.push('FROM');
@@ -8293,6 +8451,9 @@ class Deparser {
8293
8451
  .join(' ');
8294
8452
  components.push(context.indent('REFERENCING ' + transitionClauses));
8295
8453
  }
8454
+ if (node.constrrel) {
8455
+ components.push(context.indent('FROM ' + this.RangeVar(node.constrrel, context)));
8456
+ }
8296
8457
  if (node.deferrable) {
8297
8458
  components.push(context.indent('DEFERRABLE'));
8298
8459
  }
@@ -8314,7 +8475,7 @@ class Deparser {
8314
8475
  const funcName = list_utils_1.ListUtils.unwrapList(node.funcname)
8315
8476
  .map(name => this.visit(name, context))
8316
8477
  .join('.');
8317
- executeStr += ' PROCEDURE ' + funcName;
8478
+ executeStr += ' FUNCTION ' + funcName;
8318
8479
  }
8319
8480
  if (node.args && node.args.length > 0) {
8320
8481
  const argContext = context.spawn('CreateTrigStmt', { isStringLiteral: true });
@@ -8572,15 +8733,14 @@ class Deparser {
8572
8733
  .join(' ');
8573
8734
  output.push(whenClauses);
8574
8735
  }
8575
- if (node.returningList && node.returningList.length > 0) {
8576
- output.push('RETURNING');
8577
- const returningList = list_utils_1.ListUtils.unwrapList(node.returningList)
8578
- .map(target => this.visit(target, context))
8579
- .join(', ');
8580
- output.push(returningList);
8736
+ if (node.returningClause) {
8737
+ output.push(this.ReturningClause(node.returningClause, context));
8581
8738
  }
8582
8739
  return output.join(' ');
8583
8740
  }
8741
+ MergeSupportFunc(node, context) {
8742
+ return 'merge_action()';
8743
+ }
8584
8744
  MergeWhenClause(node, context) {
8585
8745
  const output = ['WHEN'];
8586
8746
  switch (node.matchKind) {
@@ -8602,10 +8762,9 @@ class Deparser {
8602
8762
  switch (node.commandType) {
8603
8763
  case 'CMD_UPDATE': {
8604
8764
  output.push('UPDATE SET');
8605
- const assignments = list_utils_1.ListUtils.unwrapList(node.targetList)
8606
- .map(target => this.visit(target, context.spawn('UpdateStmt', { update: true })))
8607
- .join(', ');
8608
- output.push(assignments);
8765
+ if (node.targetList && node.targetList.length > 0) {
8766
+ output.push(this.formatUpdateAssignments(node.targetList, context));
8767
+ }
8609
8768
  break;
8610
8769
  }
8611
8770
  case 'CMD_INSERT': {
@@ -9010,6 +9169,13 @@ class Deparser {
9010
9169
  output.push('USING');
9011
9170
  output.push(node.into.accessMethod);
9012
9171
  }
9172
+ if (node.into && node.into.options && node.into.options.length > 0) {
9173
+ output.push('WITH');
9174
+ const options = list_utils_1.ListUtils.unwrapList(node.into.options)
9175
+ .map(option => this.visit(option, context.spawn('CreateStmt')))
9176
+ .join(', ');
9177
+ output.push(`(${options})`);
9178
+ }
9013
9179
  if (node.into && node.into.onCommit && node.into.onCommit !== 'ONCOMMIT_NOOP') {
9014
9180
  output.push('ON COMMIT');
9015
9181
  switch (node.into.onCommit) {
@@ -9024,17 +9190,13 @@ class Deparser {
9024
9190
  break;
9025
9191
  }
9026
9192
  }
9193
+ if (node.into && node.into.tableSpaceName) {
9194
+ output.push('TABLESPACE', quotes_1.QuoteUtils.quoteIdentifier(node.into.tableSpaceName));
9195
+ }
9027
9196
  output.push('AS');
9028
9197
  if (node.query) {
9029
9198
  output.push(this.visit(node.query, context));
9030
9199
  }
9031
- if (node.into && node.into.options && node.into.options.length > 0) {
9032
- output.push('WITH');
9033
- const options = list_utils_1.ListUtils.unwrapList(node.into.options)
9034
- .map(option => this.visit(option, context))
9035
- .join(', ');
9036
- output.push(`(${options})`);
9037
- }
9038
9200
  if (node.into && node.into.skipData) {
9039
9201
  output.push('WITH NO DATA');
9040
9202
  }
@@ -9111,6 +9273,9 @@ class Deparser {
9111
9273
  if (listItems.length === 1 && listItems[0].String) {
9112
9274
  return `${preservedDefName} = ${listItems[0].String.sval}`;
9113
9275
  }
9276
+ if (listItems.length > 1 && listItems.every(item => item.String)) {
9277
+ return `${preservedDefName} = OPERATOR(${this.qualifiedOperatorName(listItems)})`;
9278
+ }
9114
9279
  }
9115
9280
  // For commutator/negator, we already handled them above
9116
9281
  if ((defName.toLowerCase() === 'commutator' || defName.toLowerCase() === 'negator')) {
@@ -9264,7 +9429,7 @@ class Deparser {
9264
9429
  const preservedDefName = quotedDefName !== defName ? quotedDefName : defName;
9265
9430
  // Handle String arguments with single quotes for string literals
9266
9431
  if (defValue.String) {
9267
- return `${preservedDefName} = '${defValue.String.sval}'`;
9432
+ return `${preservedDefName} = ${quotes_1.QuoteUtils.escape(defValue.String.sval)}`;
9268
9433
  }
9269
9434
  return `${preservedDefName} = ${this.visit(defValue, context)}`;
9270
9435
  }
@@ -9387,7 +9552,7 @@ class Deparser {
9387
9552
  // For CREATE COLLATION, ensure String nodes are quoted as string literals
9388
9553
  let valueStr;
9389
9554
  if (defValue.String) {
9390
- valueStr = `'${defValue.String.sval}'`;
9555
+ valueStr = quotes_1.QuoteUtils.escape(defValue.String.sval);
9391
9556
  }
9392
9557
  else {
9393
9558
  valueStr = this.visit(defValue, context);
@@ -9507,7 +9672,7 @@ class Deparser {
9507
9672
  }
9508
9673
  if (node.whereClause) {
9509
9674
  output.push('WHERE');
9510
- output.push(this.visit(node.whereClause, context));
9675
+ output.push(`(${this.visit(node.whereClause, context)})`);
9511
9676
  }
9512
9677
  return output.join(' ');
9513
9678
  }
@@ -9654,6 +9819,23 @@ class Deparser {
9654
9819
  }
9655
9820
  return output.join(' ');
9656
9821
  }
9822
+ quoteDefElemArg(arg, argValue) {
9823
+ if (arg && this.getNodeType(arg) === 'String') {
9824
+ return quotes_1.QuoteUtils.escape(this.getNodeData(arg).sval || '');
9825
+ }
9826
+ return typeof argValue === 'string' && !argValue.startsWith("'")
9827
+ ? quotes_1.QuoteUtils.escape(argValue)
9828
+ : argValue;
9829
+ }
9830
+ formatOptionValue(arg, argValue) {
9831
+ if (arg && this.getNodeType(arg) === 'String') {
9832
+ const sval = this.getNodeData(arg).sval || '';
9833
+ return /^[a-z_][a-z0-9_]*$/.test(sval) && (0, quotes_1.keywordKindOf)(sval) === 'NO_KEYWORD'
9834
+ ? sval
9835
+ : quotes_1.QuoteUtils.escape(sval);
9836
+ }
9837
+ return argValue;
9838
+ }
9657
9839
  Var(node, context) {
9658
9840
  if (node.varno && node.varattno) {
9659
9841
  return `$${node.varno}.${node.varattno}`;
@@ -9693,7 +9875,7 @@ class Deparser {
9693
9875
  }
9694
9876
  if (node.rowexpr) {
9695
9877
  output.push(',');
9696
- output.push(`'${this.visit(node.rowexpr, context)}'`);
9878
+ output.push(this.visit(node.rowexpr, context));
9697
9879
  }
9698
9880
  if (node.colexprs && node.colexprs.length > 0) {
9699
9881
  output.push('COLUMNS');
@@ -9710,25 +9892,40 @@ class Deparser {
9710
9892
  if (node.lateral) {
9711
9893
  output.push('LATERAL');
9712
9894
  }
9713
- if (node.docexpr) {
9714
- output.push(this.visit(node.docexpr, context));
9895
+ const args = [];
9896
+ if (node.namespaces && node.namespaces.length > 0) {
9897
+ const namespaces = list_utils_1.ListUtils.unwrapList(node.namespaces).map(ns => {
9898
+ const target = this.getNodeData(ns);
9899
+ const uri = this.visit(target.val, context);
9900
+ return target.name ? `${uri} AS ${quotes_1.QuoteUtils.quoteIdentifier(target.name)}` : `DEFAULT ${uri}`;
9901
+ });
9902
+ args.push(`XMLNAMESPACES(${namespaces.join(', ')}),`);
9715
9903
  }
9716
9904
  if (node.rowexpr) {
9717
- output.push('PASSING');
9718
- output.push(this.visit(node.rowexpr, context));
9905
+ args.push(this.formatXmlTableExpr(node.rowexpr, context));
9906
+ }
9907
+ if (node.docexpr) {
9908
+ args.push('PASSING');
9909
+ args.push(this.formatXmlTableExpr(node.docexpr, context));
9719
9910
  }
9720
9911
  if (node.columns && node.columns.length > 0) {
9721
- output.push('COLUMNS');
9722
- const columns = list_utils_1.ListUtils.unwrapList(node.columns)
9912
+ args.push('COLUMNS');
9913
+ args.push(list_utils_1.ListUtils.unwrapList(node.columns)
9723
9914
  .map(col => this.visit(col, context))
9724
- .join(', ');
9725
- output.push(`(${columns})`);
9915
+ .join(', '));
9726
9916
  }
9917
+ output.push(`XMLTABLE(${args.join(' ')})`);
9727
9918
  if (node.alias) {
9728
9919
  output.push(this.Alias(node.alias, context));
9729
9920
  }
9730
9921
  return output.join(' ');
9731
9922
  }
9923
+ formatXmlTableExpr(expr, context) {
9924
+ const value = this.visit(expr, context);
9925
+ return ['A_Const', 'ColumnRef', 'ParamRef', 'FuncCall'].includes(this.getNodeType(expr))
9926
+ ? value
9927
+ : `(${value})`;
9928
+ }
9732
9929
  RangeTableFuncCol(node, context) {
9733
9930
  const output = [];
9734
9931
  if (node.colname) {
@@ -9742,32 +9939,193 @@ class Deparser {
9742
9939
  }
9743
9940
  if (node.colexpr) {
9744
9941
  output.push('PATH');
9745
- output.push(`'${this.visit(node.colexpr, context)}'`);
9942
+ output.push(this.visit(node.colexpr, context));
9746
9943
  }
9747
9944
  if (node.coldefexpr) {
9748
9945
  output.push('DEFAULT');
9749
9946
  output.push(this.visit(node.coldefexpr, context));
9750
9947
  }
9948
+ if (node.is_not_null) {
9949
+ output.push('NOT NULL');
9950
+ }
9751
9951
  return output.join(' ');
9752
9952
  }
9953
+ formatJsonFormat(format) {
9954
+ if (!format || !format.format_type || format.format_type === 'JS_FORMAT_DEFAULT') {
9955
+ return null;
9956
+ }
9957
+ const parts = ['FORMAT JSON'];
9958
+ switch (format.encoding) {
9959
+ case 'JS_ENC_UTF8':
9960
+ parts.push('ENCODING UTF8');
9961
+ break;
9962
+ case 'JS_ENC_UTF16':
9963
+ parts.push('ENCODING UTF16');
9964
+ break;
9965
+ case 'JS_ENC_UTF32':
9966
+ parts.push('ENCODING UTF32');
9967
+ break;
9968
+ }
9969
+ return parts.join(' ');
9970
+ }
9971
+ formatJsonOutput(output, context) {
9972
+ if (!output || !output.typeName)
9973
+ return null;
9974
+ const parts = ['RETURNING', this.TypeName(output.typeName, context)];
9975
+ const format = this.formatJsonFormat(output.returning?.format);
9976
+ if (format)
9977
+ parts.push(format);
9978
+ return parts.join(' ');
9979
+ }
9980
+ formatJsonNullClause(absentOnNull) {
9981
+ return absentOnNull ? 'ABSENT ON NULL' : 'NULL ON NULL';
9982
+ }
9983
+ formatJsonUniqueClause(unique) {
9984
+ return unique ? 'WITH UNIQUE KEYS' : null;
9985
+ }
9986
+ JsonValueExpr(node, context) {
9987
+ const parts = [];
9988
+ if (node.raw_expr) {
9989
+ parts.push(this.visit(node.raw_expr, context));
9990
+ }
9991
+ const format = this.formatJsonFormat(node.format);
9992
+ if (format)
9993
+ parts.push(format);
9994
+ return parts.join(' ');
9995
+ }
9996
+ JsonKeyValue(node, context) {
9997
+ const key = node.key ? this.visit(node.key, context) : '';
9998
+ const value = node.value ? this.JsonValueExpr(node.value, context) : '';
9999
+ return `${key} : ${value}`;
10000
+ }
10001
+ JsonObjectConstructor(node, context) {
10002
+ const args = [];
10003
+ if (node.exprs && node.exprs.length > 0) {
10004
+ args.push(list_utils_1.ListUtils.unwrapList(node.exprs).map(e => this.visit(e, context)).join(', '));
10005
+ args.push(this.formatJsonNullClause(node.absent_on_null));
10006
+ const unique = this.formatJsonUniqueClause(node.unique);
10007
+ if (unique)
10008
+ args.push(unique);
10009
+ }
10010
+ const output = this.formatJsonOutput(node.output, context);
10011
+ if (output)
10012
+ args.push(output);
10013
+ return `JSON_OBJECT(${args.join(' ')})`;
10014
+ }
10015
+ JsonArrayConstructor(node, context) {
10016
+ const args = [];
10017
+ if (node.exprs && node.exprs.length > 0) {
10018
+ args.push(list_utils_1.ListUtils.unwrapList(node.exprs).map(e => this.visit(e, context)).join(', '));
10019
+ args.push(this.formatJsonNullClause(node.absent_on_null));
10020
+ }
10021
+ const output = this.formatJsonOutput(node.output, context);
10022
+ if (output)
10023
+ args.push(output);
10024
+ return `JSON_ARRAY(${args.join(' ')})`;
10025
+ }
9753
10026
  JsonArrayQueryConstructor(node, context) {
9754
- const output = ['JSON_ARRAYAGG'];
10027
+ const args = [];
9755
10028
  if (node.query) {
9756
- output.push(`(${this.visit(node.query, context)})`);
10029
+ args.push(this.visit(node.query, context));
10030
+ }
10031
+ const format = this.formatJsonFormat(node.format);
10032
+ if (format)
10033
+ args.push(format);
10034
+ const output = this.formatJsonOutput(node.output, context);
10035
+ if (output)
10036
+ args.push(output);
10037
+ return `JSON_ARRAY(${args.join(' ')})`;
10038
+ }
10039
+ formatJsonAggSuffix(ctor, context) {
10040
+ if (!ctor)
10041
+ return '';
10042
+ let result = '';
10043
+ if (ctor.agg_filter) {
10044
+ result += ` FILTER (WHERE ${this.visit(ctor.agg_filter, context)})`;
9757
10045
  }
9758
- if (node.format) {
9759
- output.push('FORMAT JSON');
10046
+ if (ctor.over) {
10047
+ result += this.formatOverClause(ctor.over, context);
9760
10048
  }
9761
- if (node.output) {
9762
- output.push('RETURNING TEXT');
10049
+ return result;
10050
+ }
10051
+ JsonObjectAgg(node, context) {
10052
+ const args = [];
10053
+ if (node.arg) {
10054
+ args.push(this.JsonKeyValue(node.arg, context));
10055
+ }
10056
+ args.push(this.formatJsonNullClause(node.absent_on_null));
10057
+ const unique = this.formatJsonUniqueClause(node.unique);
10058
+ if (unique)
10059
+ args.push(unique);
10060
+ const output = this.formatJsonOutput(node.constructor?.output, context);
10061
+ if (output)
10062
+ args.push(output);
10063
+ return `JSON_OBJECTAGG(${args.join(' ')})${this.formatJsonAggSuffix(node.constructor, context)}`;
10064
+ }
10065
+ JsonArrayAgg(node, context) {
10066
+ const args = [];
10067
+ if (node.arg) {
10068
+ args.push(this.JsonValueExpr(node.arg, context));
9763
10069
  }
9764
- if (node.absent_on_null) {
9765
- output.push('ABSENT ON NULL');
10070
+ const aggOrder = node.constructor?.agg_order;
10071
+ if (aggOrder && aggOrder.length > 0) {
10072
+ const orderItems = list_utils_1.ListUtils.unwrapList(aggOrder).map(item => this.visit(item, context));
10073
+ args.push(`ORDER BY ${orderItems.join(', ')}`);
9766
10074
  }
9767
- else {
9768
- output.push('NULL ON NULL');
10075
+ args.push(this.formatJsonNullClause(node.absent_on_null));
10076
+ const output = this.formatJsonOutput(node.constructor?.output, context);
10077
+ if (output)
10078
+ args.push(output);
10079
+ return `JSON_ARRAYAGG(${args.join(' ')})${this.formatJsonAggSuffix(node.constructor, context)}`;
10080
+ }
10081
+ JsonParseExpr(node, context) {
10082
+ const args = [];
10083
+ if (node.expr) {
10084
+ args.push(this.JsonValueExpr(node.expr, context));
9769
10085
  }
9770
- return output.join(' ');
10086
+ const unique = this.formatJsonUniqueClause(node.unique_keys);
10087
+ if (unique)
10088
+ args.push(unique);
10089
+ return `JSON(${args.join(' ')})`;
10090
+ }
10091
+ JsonScalarExpr(node, context) {
10092
+ const expr = node.expr ? this.visit(node.expr, context) : '';
10093
+ return `JSON_SCALAR(${expr})`;
10094
+ }
10095
+ JsonSerializeExpr(node, context) {
10096
+ const args = [];
10097
+ if (node.expr) {
10098
+ args.push(this.JsonValueExpr(node.expr, context));
10099
+ }
10100
+ const output = this.formatJsonOutput(node.output, context);
10101
+ if (output)
10102
+ args.push(output);
10103
+ return `JSON_SERIALIZE(${args.join(' ')})`;
10104
+ }
10105
+ JsonIsPredicate(node, context) {
10106
+ const parts = [];
10107
+ if (node.expr) {
10108
+ parts.push(this.visit(node.expr, context));
10109
+ }
10110
+ const format = this.formatJsonFormat(node.format);
10111
+ if (format)
10112
+ parts.push(format);
10113
+ parts.push('IS JSON');
10114
+ switch (node.item_type) {
10115
+ case 'JS_TYPE_OBJECT':
10116
+ parts.push('OBJECT');
10117
+ break;
10118
+ case 'JS_TYPE_ARRAY':
10119
+ parts.push('ARRAY');
10120
+ break;
10121
+ case 'JS_TYPE_SCALAR':
10122
+ parts.push('SCALAR');
10123
+ break;
10124
+ }
10125
+ if (node.unique_keys) {
10126
+ parts.push('WITH UNIQUE KEYS');
10127
+ }
10128
+ return parts.join(' ');
9771
10129
  }
9772
10130
  RangeFunction(node, context) {
9773
10131
  const output = [];
@@ -9961,6 +10319,7 @@ class Deparser {
9961
10319
  rootParts.push('STANDALONE NO VALUE');
9962
10320
  }
9963
10321
  else if (standaloneArg.A_Const.ival.ival === 3) {
10322
+ // STANDALONE omitted
9964
10323
  }
9965
10324
  else if (Object.keys(standaloneArg.A_Const.ival).length === 0) {
9966
10325
  rootParts.push('STANDALONE YES');
@@ -10553,9 +10912,5 @@ class Deparser {
10553
10912
  }
10554
10913
  return output.join(' ');
10555
10914
  }
10556
- containsMultilineStringLiteral(content) {
10557
- const stringLiteralRegex = /'[^']*\n[^']*'/g;
10558
- return stringLiteralRegex.test(content);
10559
- }
10560
10915
  }
10561
10916
  exports.Deparser = Deparser;