pgsql-deparser 18.3.7 → 18.3.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/deparser.js CHANGED
@@ -5,6 +5,25 @@ const quotes_1 = require("@pgsql/quotes");
5
5
  const list_utils_1 = require("./utils/list-utils");
6
6
  const sql_formatter_1 = require("./utils/sql-formatter");
7
7
  const base_1 = require("./visitors/base");
8
+ // WindowDef.frameOptions bit flags (src/include/nodes/parsenodes.h)
9
+ const FRAMEOPTION_NONDEFAULT = 0x00001;
10
+ const FRAMEOPTION_RANGE = 0x00002;
11
+ const FRAMEOPTION_ROWS = 0x00004;
12
+ const FRAMEOPTION_GROUPS = 0x00008;
13
+ const FRAMEOPTION_BETWEEN = 0x00010;
14
+ const FRAMEOPTION_START_UNBOUNDED_PRECEDING = 0x00020;
15
+ const FRAMEOPTION_END_UNBOUNDED_PRECEDING = 0x00040;
16
+ const FRAMEOPTION_START_UNBOUNDED_FOLLOWING = 0x00080;
17
+ const FRAMEOPTION_END_UNBOUNDED_FOLLOWING = 0x00100;
18
+ const FRAMEOPTION_START_CURRENT_ROW = 0x00200;
19
+ const FRAMEOPTION_END_CURRENT_ROW = 0x00400;
20
+ const FRAMEOPTION_START_OFFSET_PRECEDING = 0x00800;
21
+ const FRAMEOPTION_END_OFFSET_PRECEDING = 0x01000;
22
+ const FRAMEOPTION_START_OFFSET_FOLLOWING = 0x02000;
23
+ const FRAMEOPTION_END_OFFSET_FOLLOWING = 0x04000;
24
+ const FRAMEOPTION_EXCLUDE_CURRENT_ROW = 0x08000;
25
+ const FRAMEOPTION_EXCLUDE_GROUP = 0x10000;
26
+ const FRAMEOPTION_EXCLUDE_TIES = 0x20000;
8
27
  /**
9
28
  * List of real PostgreSQL built-in types as they appear in pg_catalog.pg_type.typname.
10
29
  * These are stored in lowercase in PostgreSQL system catalogs.
@@ -103,6 +122,16 @@ function isWrappedParseResult(obj) {
103
122
  * The deparser automatically detects bare ParseResult objects for backward
104
123
  * compatibility and wraps them internally for consistent processing.
105
124
  */
125
+ function formatBitString(bsval) {
126
+ const quote = (value) => value.replace(/'/g, "''");
127
+ if (/^x[0-9A-Fa-f]+$/.test(bsval)) {
128
+ return `x'${bsval.substring(1)}'`;
129
+ }
130
+ if (bsval.startsWith('b')) {
131
+ return `b'${quote(bsval.substring(1))}'`;
132
+ }
133
+ return `b'${quote(bsval)}'`;
134
+ }
106
135
  class Deparser {
107
136
  tree;
108
137
  options;
@@ -984,6 +1013,12 @@ class Deparser {
984
1013
  output.push(context.parens(columnNames.join(', ')));
985
1014
  }
986
1015
  }
1016
+ if (node.override === 'OVERRIDING_USER_VALUE') {
1017
+ output.push('OVERRIDING USER VALUE');
1018
+ }
1019
+ else if (node.override === 'OVERRIDING_SYSTEM_VALUE') {
1020
+ output.push('OVERRIDING SYSTEM VALUE');
1021
+ }
987
1022
  if (node.selectStmt) {
988
1023
  output.push(this.visit(node.selectStmt, context));
989
1024
  }
@@ -1060,38 +1095,8 @@ class Deparser {
1060
1095
  output.push(this.RangeVar(node.relation, context));
1061
1096
  }
1062
1097
  output.push('SET');
1063
- const targetList = list_utils_1.ListUtils.unwrapList(node.targetList);
1064
- if (targetList && targetList.length) {
1065
- const firstTarget = targetList[0];
1066
- const processedTargets = new Set();
1067
- const assignmentParts = [];
1068
- for (let i = 0; i < targetList.length; i++) {
1069
- if (processedTargets.has(i))
1070
- continue;
1071
- const target = targetList[i];
1072
- const multiAssignRef = target.ResTarget?.val?.MultiAssignRef;
1073
- if (multiAssignRef) {
1074
- const relatedTargets = [];
1075
- for (let j = i; j < targetList.length; j++) {
1076
- const otherTarget = targetList[j];
1077
- const otherMultiAssignRef = otherTarget.ResTarget?.val?.MultiAssignRef;
1078
- if (otherMultiAssignRef &&
1079
- JSON.stringify(otherMultiAssignRef.source) === JSON.stringify(multiAssignRef.source)) {
1080
- relatedTargets.push(otherTarget);
1081
- processedTargets.add(j);
1082
- }
1083
- }
1084
- const names = relatedTargets.map(t => t.ResTarget.name);
1085
- const multiAssignment = `${context.parens(names.join(', '))} = ${this.visit(multiAssignRef.source, context)}`;
1086
- assignmentParts.push(multiAssignment);
1087
- }
1088
- else {
1089
- // Handle regular single-column assignment
1090
- assignmentParts.push(this.visit(target, context.spawn('UpdateStmt', { update: true })));
1091
- processedTargets.add(i);
1092
- }
1093
- }
1094
- output.push(assignmentParts.join(','));
1098
+ if (node.targetList && node.targetList.length > 0) {
1099
+ output.push(this.formatUpdateAssignments(node.targetList, context));
1095
1100
  }
1096
1101
  if (node.fromClause) {
1097
1102
  output.push('FROM');
@@ -1108,6 +1113,36 @@ class Deparser {
1108
1113
  }
1109
1114
  return output.join(' ');
1110
1115
  }
1116
+ formatUpdateAssignments(targets, context) {
1117
+ const targetList = list_utils_1.ListUtils.unwrapList(targets);
1118
+ const processedTargets = new Set();
1119
+ const assignmentParts = [];
1120
+ for (let i = 0; i < targetList.length; i++) {
1121
+ if (processedTargets.has(i))
1122
+ continue;
1123
+ const target = targetList[i];
1124
+ const multiAssignRef = target.ResTarget?.val?.MultiAssignRef;
1125
+ if (multiAssignRef) {
1126
+ const relatedTargets = [];
1127
+ for (let j = i; j < targetList.length; j++) {
1128
+ const otherTarget = targetList[j];
1129
+ const otherMultiAssignRef = otherTarget.ResTarget?.val?.MultiAssignRef;
1130
+ if (otherMultiAssignRef &&
1131
+ JSON.stringify(otherMultiAssignRef.source) === JSON.stringify(multiAssignRef.source)) {
1132
+ relatedTargets.push(otherTarget);
1133
+ processedTargets.add(j);
1134
+ }
1135
+ }
1136
+ const names = relatedTargets.map(t => quotes_1.QuoteUtils.quoteIdentifier(t.ResTarget.name));
1137
+ assignmentParts.push(`${context.parens(names.join(', '))} = ${this.visit(multiAssignRef.source, context)}`);
1138
+ }
1139
+ else {
1140
+ assignmentParts.push(this.visit(target, context.spawn('UpdateStmt', { update: true })));
1141
+ processedTargets.add(i);
1142
+ }
1143
+ }
1144
+ return assignmentParts.join(', ');
1145
+ }
1111
1146
  DeleteStmt(node, context) {
1112
1147
  try {
1113
1148
  const output = [];
@@ -1520,43 +1555,41 @@ class Deparser {
1520
1555
  result += ` FILTER (WHERE ${this.visit(node.agg_filter, context)})`;
1521
1556
  }
1522
1557
  if (node.over) {
1523
- // Handle named window references first
1524
- if (node.over.name) {
1525
- result += ` OVER ${node.over.name}`;
1526
- }
1527
- else {
1528
- const windowParts = [];
1529
- if (node.over.partitionClause) {
1530
- const partitions = list_utils_1.ListUtils.unwrapList(node.over.partitionClause);
1531
- const partitionStrs = partitions.map(p => this.visit(p, context));
1532
- windowParts.push(`PARTITION BY ${partitionStrs.join(', ')}`);
1533
- }
1534
- if (node.over.orderClause) {
1535
- const orders = list_utils_1.ListUtils.unwrapList(node.over.orderClause);
1536
- const orderStrs = orders.map(o => this.visit(o, context));
1537
- windowParts.push(`ORDER BY ${orderStrs.join(', ')}`);
1538
- }
1539
- // Handle window frame specifications using the dedicated formatWindowFrame method
1540
- const frameClause = this.formatWindowFrame(node.over, context.spawn('FuncCall'));
1541
- if (frameClause) {
1542
- windowParts.push(frameClause);
1543
- }
1544
- if (windowParts.length > 0) {
1545
- if (context.isPretty() && windowParts.length > 1) {
1546
- const formattedParts = windowParts.map(part => context.indent(part));
1547
- result += ` OVER (${context.newline()}${formattedParts.join(context.newline())}${context.newline()})`;
1548
- }
1549
- else {
1550
- result += ` OVER (${windowParts.join(' ')})`;
1551
- }
1552
- }
1553
- else {
1554
- result += ` OVER ()`;
1555
- }
1556
- }
1558
+ result += this.formatOverClause(node.over, context);
1557
1559
  }
1558
1560
  return result;
1559
1561
  }
1562
+ formatOverClause(over, context) {
1563
+ if (over.name) {
1564
+ return ` OVER ${over.name}`;
1565
+ }
1566
+ const windowParts = [];
1567
+ if (over.refname) {
1568
+ windowParts.push(over.refname);
1569
+ }
1570
+ if (over.partitionClause) {
1571
+ const partitions = list_utils_1.ListUtils.unwrapList(over.partitionClause);
1572
+ const partitionStrs = partitions.map(p => this.visit(p, context));
1573
+ windowParts.push(`PARTITION BY ${partitionStrs.join(', ')}`);
1574
+ }
1575
+ if (over.orderClause) {
1576
+ const orders = list_utils_1.ListUtils.unwrapList(over.orderClause);
1577
+ const orderStrs = orders.map(o => this.visit(o, context));
1578
+ windowParts.push(`ORDER BY ${orderStrs.join(', ')}`);
1579
+ }
1580
+ const frameClause = this.formatWindowFrame(over, context.spawn('FuncCall'));
1581
+ if (frameClause) {
1582
+ windowParts.push(frameClause);
1583
+ }
1584
+ if (windowParts.length === 0) {
1585
+ return ' OVER ()';
1586
+ }
1587
+ if (context.isPretty() && windowParts.length > 1) {
1588
+ const formattedParts = windowParts.map(part => context.indent(part));
1589
+ return ` OVER (${context.newline()}${formattedParts.join(context.newline())}${context.newline()})`;
1590
+ }
1591
+ return ` OVER (${windowParts.join(' ')})`;
1592
+ }
1560
1593
  FuncExpr(node, context) {
1561
1594
  const funcName = `func_${node.funcid}`;
1562
1595
  const args = node.args ? list_utils_1.ListUtils.unwrapList(node.args).map(arg => this.visit(arg, context)).join(', ') : '';
@@ -1646,15 +1679,7 @@ class Deparser {
1646
1679
  else if (nodeAny.bsval !== undefined) {
1647
1680
  if (typeof nodeAny.bsval === 'object' && nodeAny.bsval !== null) {
1648
1681
  if (nodeAny.bsval.bsval !== undefined) {
1649
- const bsval = nodeAny.bsval.bsval;
1650
- // Check if this is a hexadecimal bit string (starts with x and contains only hex digits)
1651
- if (bsval.startsWith('x') && /^x[0-9A-Fa-f]+$/.test(bsval)) {
1652
- return `x'${bsval.substring(1)}'`;
1653
- }
1654
- if (bsval.startsWith('b')) {
1655
- return `b'${bsval.substring(1)}'`;
1656
- }
1657
- return `b'${bsval}'`;
1682
+ return formatBitString(nodeAny.bsval.bsval);
1658
1683
  }
1659
1684
  else if (Object.keys(nodeAny.bsval).length === 0) {
1660
1685
  return "''";
@@ -1667,15 +1692,7 @@ class Deparser {
1667
1692
  return 'NULL';
1668
1693
  }
1669
1694
  else {
1670
- const bsval = nodeAny.bsval;
1671
- // Check if this is a hexadecimal bit string (starts with x and contains only hex digits)
1672
- if (bsval.startsWith('x') && /^x[0-9A-Fa-f]+$/.test(bsval)) {
1673
- return `x'${bsval.substring(1)}'`;
1674
- }
1675
- if (bsval.startsWith('b')) {
1676
- return `b'${bsval.substring(1)}'`;
1677
- }
1678
- return `b'${bsval}'`;
1695
+ return formatBitString(nodeAny.bsval);
1679
1696
  }
1680
1697
  }
1681
1698
  if (nodeAny.val) {
@@ -1692,7 +1709,7 @@ class Deparser {
1692
1709
  return nodeAny.val.Boolean.boolval ? 'true' : 'false';
1693
1710
  }
1694
1711
  else if (nodeAny.val.BitString?.bsval !== undefined) {
1695
- return nodeAny.val.BitString.bsval;
1712
+ return formatBitString(nodeAny.val.BitString.bsval);
1696
1713
  }
1697
1714
  }
1698
1715
  if (nodeAny.isnull === true) {
@@ -2372,15 +2389,7 @@ class Deparser {
2372
2389
  return node.boolval ? 'true' : 'false';
2373
2390
  }
2374
2391
  BitString(node, context) {
2375
- // Check if this is a hexadecimal bit string (starts with x)
2376
- if (node.bsval.startsWith('x')) {
2377
- return `x'${node.bsval.substring(1)}'`;
2378
- }
2379
- if (node.bsval.startsWith('b')) {
2380
- return `b'${node.bsval.substring(1)}'`;
2381
- }
2382
- // Fallback for raw values without prefix
2383
- return `b'${node.bsval}'`;
2392
+ return formatBitString(node.bsval);
2384
2393
  }
2385
2394
  Null(node, context) {
2386
2395
  return 'NULL';
@@ -2419,7 +2428,7 @@ class Deparser {
2419
2428
  output.push(context.parens(elementStrs.join(', ')));
2420
2429
  }
2421
2430
  }
2422
- else if (node.tableElts) {
2431
+ else if (node.tableElts && !node.partbound) {
2423
2432
  const elements = list_utils_1.ListUtils.unwrapList(node.tableElts);
2424
2433
  const elementStrs = elements.map(el => {
2425
2434
  return this.deparse(el, context.spawn('CreateStmt'));
@@ -2447,7 +2456,16 @@ class Deparser {
2447
2456
  const inherits = list_utils_1.ListUtils.unwrapList(node.inhRelations);
2448
2457
  const inheritStrs = inherits.map(rel => this.visit(rel, context));
2449
2458
  output.push(inheritStrs[0]);
2450
- if (node.partbound.strategy === 'l' && node.partbound.listdatums) {
2459
+ if (node.tableElts) {
2460
+ const elementStrs = list_utils_1.ListUtils.unwrapList(node.tableElts).map(el => {
2461
+ return this.deparse(el, context.spawn('CreateStmt'));
2462
+ });
2463
+ output.push(context.parens(elementStrs.join(', ')));
2464
+ }
2465
+ if (node.partbound.is_default) {
2466
+ output.push('DEFAULT');
2467
+ }
2468
+ else if (node.partbound.strategy === 'l' && node.partbound.listdatums) {
2451
2469
  output.push('FOR VALUES IN');
2452
2470
  const listValues = list_utils_1.ListUtils.unwrapList(node.partbound.listdatums)
2453
2471
  .map(datum => this.visit(datum, context))
@@ -2470,13 +2488,9 @@ class Deparser {
2470
2488
  output.push(`(${upperValues})`);
2471
2489
  }
2472
2490
  }
2473
- else if (node.partbound.strategy === 'h' && node.partbound.modulus !== undefined) {
2491
+ else if (node.partbound.strategy === 'h') {
2474
2492
  output.push('FOR VALUES WITH');
2475
- const remainder = node.partbound.remainder !== undefined ? node.partbound.remainder : 0;
2476
- output.push(`(MODULUS ${node.partbound.modulus}, REMAINDER ${remainder})`);
2477
- }
2478
- else if (node.partbound.is_default) {
2479
- output.push('DEFAULT');
2493
+ output.push(`(MODULUS ${node.partbound.modulus ?? 0}, REMAINDER ${node.partbound.remainder ?? 0})`);
2480
2494
  }
2481
2495
  }
2482
2496
  else if (node.inhRelations) {
@@ -2542,6 +2556,12 @@ class Deparser {
2542
2556
  if (node.typeName) {
2543
2557
  output.push(this.TypeName(node.typeName, context));
2544
2558
  }
2559
+ if (node.storage_name) {
2560
+ output.push('STORAGE', node.storage_name.toUpperCase());
2561
+ }
2562
+ if (node.compression) {
2563
+ output.push('COMPRESSION', quotes_1.QuoteUtils.quoteIdentifier(node.compression));
2564
+ }
2545
2565
  if (node.fdwoptions && node.fdwoptions.length > 0) {
2546
2566
  output.push('OPTIONS');
2547
2567
  const columnContext = context.spawn('ColumnDef');
@@ -2602,7 +2622,8 @@ class Deparser {
2602
2622
  case 'CONSTR_DEFAULT':
2603
2623
  output.push('DEFAULT');
2604
2624
  if (node.raw_expr) {
2605
- output.push(this.visit(node.raw_expr, context));
2625
+ const defaultExpr = this.visit(node.raw_expr, context);
2626
+ output.push(this.needsBExprParens(node.raw_expr) ? `(${defaultExpr})` : defaultExpr);
2606
2627
  }
2607
2628
  break;
2608
2629
  case 'CONSTR_CHECK':
@@ -2966,7 +2987,7 @@ class Deparser {
2966
2987
  break;
2967
2988
  }
2968
2989
  // Handle deferrable constraints for all constraint types that support it
2969
- if (node.contype === 'CONSTR_PRIMARY' || node.contype === 'CONSTR_UNIQUE' || node.contype === 'CONSTR_FOREIGN') {
2990
+ if (node.contype === 'CONSTR_PRIMARY' || node.contype === 'CONSTR_UNIQUE' || node.contype === 'CONSTR_FOREIGN' || node.contype === 'CONSTR_EXCLUSION') {
2970
2991
  if (node.deferrable) {
2971
2992
  if (context.isPretty() && node.contype === 'CONSTR_FOREIGN') {
2972
2993
  output.push('\n' + context.indent('DEFERRABLE'));
@@ -2995,6 +3016,9 @@ class Deparser {
2995
3016
  output.push('NOT DEFERRABLE');
2996
3017
  }
2997
3018
  }
3019
+ else if (node.initdeferred) {
3020
+ output.push('INITIALLY DEFERRED');
3021
+ }
2998
3022
  }
2999
3023
  if (context.isPretty() && node.contype === 'CONSTR_FOREIGN') {
3000
3024
  let result = '';
@@ -3060,6 +3084,9 @@ class Deparser {
3060
3084
  output.push(node.name);
3061
3085
  }
3062
3086
  const windowParts = [];
3087
+ if (node.refname) {
3088
+ windowParts.push(node.refname);
3089
+ }
3063
3090
  if (node.partitionClause) {
3064
3091
  const partitions = list_utils_1.ListUtils.unwrapList(node.partitionClause);
3065
3092
  const partitionStrs = partitions.map(p => this.visit(p, context));
@@ -3070,12 +3097,9 @@ class Deparser {
3070
3097
  const orderStrs = orders.map(o => this.visit(o, context));
3071
3098
  windowParts.push(`ORDER BY ${orderStrs.join(', ')}`);
3072
3099
  }
3073
- // Only add frame clause if frameOptions indicates non-default framing
3074
- if (node.frameOptions && node.frameOptions !== 1058) {
3075
- const frameClause = this.formatWindowFrame(node, context.spawn('WindowDef'));
3076
- if (frameClause) {
3077
- windowParts.push(frameClause);
3078
- }
3100
+ const frameClause = this.formatWindowFrame(node, context.spawn('WindowDef'));
3101
+ if (frameClause) {
3102
+ windowParts.push(frameClause);
3079
3103
  }
3080
3104
  if (windowParts.length > 0) {
3081
3105
  if (node.name) {
@@ -3092,114 +3116,96 @@ class Deparser {
3092
3116
  }
3093
3117
  return output.join(' ');
3094
3118
  }
3119
+ formatIdentityColumnOptions(def, context) {
3120
+ const seqContext = context.spawn('AlterSeqStmt');
3121
+ return list_utils_1.ListUtils.unwrapList(def).map(item => {
3122
+ const el = item.DefElem;
3123
+ if (!el)
3124
+ return this.visit(item, context);
3125
+ if (el.defname === 'generated') {
3126
+ const ival = el.arg?.Integer?.ival ?? 0;
3127
+ return ival === 97 ? 'SET GENERATED ALWAYS' : 'SET GENERATED BY DEFAULT';
3128
+ }
3129
+ if (el.defname === 'restart') {
3130
+ return el.arg ? `RESTART WITH ${this.visit(el.arg, context)}` : 'RESTART';
3131
+ }
3132
+ return `SET ${this.visit(item, seqContext)}`;
3133
+ }).join(' ');
3134
+ }
3135
+ // b_expr (used by DEFAULT) excludes IN/LIKE/BETWEEN and boolean operators.
3136
+ needsBExprParens(node) {
3137
+ const nodeType = this.getNodeType(node);
3138
+ if (nodeType === 'BoolExpr')
3139
+ return true;
3140
+ if (nodeType === 'A_Expr') {
3141
+ const kind = this.getNodeData(node).kind;
3142
+ return kind !== 'AEXPR_OP' && kind !== 'AEXPR_DISTINCT' && kind !== 'AEXPR_NOT_DISTINCT';
3143
+ }
3144
+ return false;
3145
+ }
3095
3146
  formatWindowFrame(node, context) {
3096
- if (!node.frameOptions)
3147
+ if (!node.frameOptions || !(node.frameOptions & FRAMEOPTION_NONDEFAULT))
3097
3148
  return null;
3098
3149
  const frameOptions = node.frameOptions;
3099
- const EXCLUDE_MASK = 0x8000 | 0x10000 | 0x20000;
3100
- const baseFrameOptions = frameOptions & ~EXCLUDE_MASK;
3101
3150
  const frameParts = [];
3102
- if (frameOptions & 0x01) { // FRAMEOPTION_NONDEFAULT
3103
- if (frameOptions & 0x02) { // FRAMEOPTION_RANGE
3104
- frameParts.push('RANGE');
3105
- }
3106
- else if (frameOptions & 0x04) { // FRAMEOPTION_ROWS
3107
- frameParts.push('ROWS');
3108
- }
3109
- else if (frameOptions & 0x08) { // FRAMEOPTION_GROUPS
3110
- frameParts.push('GROUPS');
3111
- }
3151
+ if (frameOptions & FRAMEOPTION_RANGE) {
3152
+ frameParts.push('RANGE');
3153
+ }
3154
+ else if (frameOptions & FRAMEOPTION_ROWS) {
3155
+ frameParts.push('ROWS');
3156
+ }
3157
+ else if (frameOptions & FRAMEOPTION_GROUPS) {
3158
+ frameParts.push('GROUPS');
3112
3159
  }
3113
3160
  if (frameParts.length === 0)
3114
3161
  return null;
3115
- const boundsParts = [];
3116
- // Handle specific frameOptions values that have known mappings
3117
- if (baseFrameOptions === 789) {
3118
- boundsParts.push('CURRENT ROW');
3119
- boundsParts.push('AND UNBOUNDED FOLLOWING');
3162
+ let start = null;
3163
+ if (frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING) {
3164
+ start = 'UNBOUNDED PRECEDING';
3120
3165
  }
3121
- else if (baseFrameOptions === 1077) {
3122
- boundsParts.push('UNBOUNDED PRECEDING');
3123
- boundsParts.push('AND CURRENT ROW');
3166
+ else if (frameOptions & FRAMEOPTION_START_UNBOUNDED_FOLLOWING) {
3167
+ start = 'UNBOUNDED FOLLOWING';
3124
3168
  }
3125
- else if (baseFrameOptions === 18453) {
3126
- if (node.startOffset && node.endOffset) {
3127
- boundsParts.push(`${this.visit(node.startOffset, context)} PRECEDING`);
3128
- boundsParts.push(`AND ${this.visit(node.endOffset, context)} FOLLOWING`);
3129
- }
3169
+ else if (frameOptions & FRAMEOPTION_START_CURRENT_ROW) {
3170
+ start = 'CURRENT ROW';
3130
3171
  }
3131
- else if (baseFrameOptions === 1557) {
3132
- boundsParts.push('CURRENT ROW');
3133
- boundsParts.push('AND CURRENT ROW');
3172
+ else if (frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING && node.startOffset) {
3173
+ start = `${this.visit(node.startOffset, context)} PRECEDING`;
3134
3174
  }
3135
- else if (baseFrameOptions === 16917) {
3136
- boundsParts.push('CURRENT ROW');
3137
- if (node.endOffset) {
3138
- boundsParts.push(`AND ${this.visit(node.endOffset, context)} FOLLOWING`);
3139
- }
3175
+ else if (frameOptions & FRAMEOPTION_START_OFFSET_FOLLOWING && node.startOffset) {
3176
+ start = `${this.visit(node.startOffset, context)} FOLLOWING`;
3140
3177
  }
3141
- else if (baseFrameOptions === 1058) {
3142
- return null;
3178
+ let end = null;
3179
+ if (frameOptions & FRAMEOPTION_END_UNBOUNDED_PRECEDING) {
3180
+ end = 'UNBOUNDED PRECEDING';
3143
3181
  }
3144
- else {
3145
- // Handle start bound - prioritize explicit offset values over bit flags
3146
- if (node.startOffset) {
3147
- if (frameOptions & 0x400) { // FRAMEOPTION_START_VALUE_PRECEDING
3148
- boundsParts.push(`${this.visit(node.startOffset, context)} PRECEDING`);
3149
- }
3150
- else if (frameOptions & 0x800) { // FRAMEOPTION_START_VALUE_FOLLOWING
3151
- boundsParts.push(`${this.visit(node.startOffset, context)} FOLLOWING`);
3152
- }
3153
- else {
3154
- boundsParts.push(`${this.visit(node.startOffset, context)} PRECEDING`);
3155
- }
3156
- }
3157
- else if (frameOptions & 0x10) { // FRAMEOPTION_START_UNBOUNDED_PRECEDING
3158
- boundsParts.push('UNBOUNDED PRECEDING');
3159
- }
3160
- else if (frameOptions & 0x20) { // FRAMEOPTION_START_CURRENT_ROW
3161
- boundsParts.push('CURRENT ROW');
3162
- }
3163
- // Handle end bound - prioritize explicit offset values over bit flags
3164
- if (node.endOffset) {
3165
- if (boundsParts.length > 0) {
3166
- if (frameOptions & 0x1000) { // FRAMEOPTION_END_VALUE_PRECEDING
3167
- boundsParts.push(`AND ${this.visit(node.endOffset, context)} PRECEDING`);
3168
- }
3169
- else if (frameOptions & 0x2000) { // FRAMEOPTION_END_VALUE_FOLLOWING
3170
- boundsParts.push(`AND ${this.visit(node.endOffset, context)} FOLLOWING`);
3171
- }
3172
- else {
3173
- boundsParts.push(`AND ${this.visit(node.endOffset, context)} FOLLOWING`);
3174
- }
3175
- }
3176
- }
3177
- else if (frameOptions & 0x80) { // FRAMEOPTION_END_UNBOUNDED_FOLLOWING
3178
- if (boundsParts.length > 0) {
3179
- boundsParts.push('AND UNBOUNDED FOLLOWING');
3180
- }
3181
- }
3182
- else if (frameOptions & 0x100) { // FRAMEOPTION_END_CURRENT_ROW
3183
- if (boundsParts.length > 0) {
3184
- boundsParts.push('AND CURRENT ROW');
3185
- }
3182
+ else if (frameOptions & FRAMEOPTION_END_UNBOUNDED_FOLLOWING) {
3183
+ end = 'UNBOUNDED FOLLOWING';
3184
+ }
3185
+ else if (frameOptions & FRAMEOPTION_END_CURRENT_ROW) {
3186
+ end = 'CURRENT ROW';
3187
+ }
3188
+ else if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING && node.endOffset) {
3189
+ end = `${this.visit(node.endOffset, context)} PRECEDING`;
3190
+ }
3191
+ else if (frameOptions & FRAMEOPTION_END_OFFSET_FOLLOWING && node.endOffset) {
3192
+ end = `${this.visit(node.endOffset, context)} FOLLOWING`;
3193
+ }
3194
+ if (start) {
3195
+ if (frameOptions & FRAMEOPTION_BETWEEN) {
3196
+ frameParts.push(`BETWEEN ${start} AND ${end ?? 'CURRENT ROW'}`);
3186
3197
  }
3187
- else if (boundsParts.length > 0) {
3188
- boundsParts.push('AND CURRENT ROW');
3198
+ else {
3199
+ frameParts.push(start);
3189
3200
  }
3190
3201
  }
3191
- if (boundsParts.length > 0) {
3192
- frameParts.push('BETWEEN');
3193
- frameParts.push(boundsParts.join(' '));
3194
- }
3195
- // EXCLUDE clause
3196
- if (frameOptions & 0x8000) { // FRAMEOPTION_EXCLUDE_CURRENT_ROW
3202
+ if (frameOptions & FRAMEOPTION_EXCLUDE_CURRENT_ROW) {
3197
3203
  frameParts.push('EXCLUDE CURRENT ROW');
3198
3204
  }
3199
- else if (frameOptions & 0x10000) { // FRAMEOPTION_EXCLUDE_GROUP
3205
+ else if (frameOptions & FRAMEOPTION_EXCLUDE_GROUP) {
3200
3206
  frameParts.push('EXCLUDE GROUP');
3201
3207
  }
3202
- else if (frameOptions & 0x20000) { // FRAMEOPTION_EXCLUDE_TIES
3208
+ else if (frameOptions & FRAMEOPTION_EXCLUDE_TIES) {
3203
3209
  frameParts.push('EXCLUDE TIES');
3204
3210
  }
3205
3211
  return frameParts.join(' ');
@@ -3278,8 +3284,35 @@ class Deparser {
3278
3284
  if (node.ctequery) {
3279
3285
  output.push(context.parens(this.visit(node.ctequery, context)));
3280
3286
  }
3287
+ if (node.search_clause) {
3288
+ const sc = node.search_clause;
3289
+ const cols = list_utils_1.ListUtils.unwrapList(sc.search_col_list).map(c => this.visit(c, context));
3290
+ output.push('SEARCH', sc.search_breadth_first ? 'BREADTH' : 'DEPTH', 'FIRST BY');
3291
+ output.push(cols.join(', '));
3292
+ output.push('SET', quotes_1.QuoteUtils.quoteIdentifier(sc.search_seq_column));
3293
+ }
3294
+ if (node.cycle_clause) {
3295
+ const cc = node.cycle_clause;
3296
+ const cols = list_utils_1.ListUtils.unwrapList(cc.cycle_col_list).map(c => this.visit(c, context));
3297
+ output.push('CYCLE', cols.join(', '));
3298
+ output.push('SET', quotes_1.QuoteUtils.quoteIdentifier(cc.cycle_mark_column));
3299
+ if (cc.cycle_mark_value && cc.cycle_mark_default) {
3300
+ output.push('TO', this.formatAexprConst(cc.cycle_mark_value, context));
3301
+ output.push('DEFAULT', this.formatAexprConst(cc.cycle_mark_default, context));
3302
+ }
3303
+ output.push('USING', quotes_1.QuoteUtils.quoteIdentifier(cc.cycle_path_column));
3304
+ }
3281
3305
  return output.join(' ');
3282
3306
  }
3307
+ // Renders a constant in the restricted AexprConst grammar (e.g. `point '(1,1)'`
3308
+ // rather than `CAST('(1,1)' AS point)`).
3309
+ formatAexprConst(node, context) {
3310
+ const cast = node.TypeCast;
3311
+ if (cast?.arg?.A_Const?.sval && cast.typeName) {
3312
+ return `${this.TypeName(cast.typeName, context)} ${this.visit(cast.arg, context)}`;
3313
+ }
3314
+ return this.visit(node, context);
3315
+ }
3283
3316
  ParamRef(node, context) {
3284
3317
  return `$${node.number}`;
3285
3318
  }
@@ -3669,9 +3702,9 @@ class Deparser {
3669
3702
  output.push(`(${upperValues})`);
3670
3703
  }
3671
3704
  }
3672
- else if (node.bound.strategy === 'h' && node.bound.modulus !== undefined) {
3705
+ else if (node.bound.strategy === 'h') {
3673
3706
  output.push('FOR VALUES WITH');
3674
- output.push(`(MODULUS ${node.bound.modulus}, REMAINDER ${node.bound.remainder ?? 0})`);
3707
+ output.push(`(MODULUS ${node.bound.modulus ?? 0}, REMAINDER ${node.bound.remainder ?? 0})`);
3675
3708
  }
3676
3709
  else if (node.bound.is_default) {
3677
3710
  output.push('DEFAULT');
@@ -4524,10 +4557,10 @@ class Deparser {
4524
4557
  output.push('PROGRAM');
4525
4558
  }
4526
4559
  if (node.filename) {
4527
- output.push(`'${node.filename}'`);
4560
+ output.push(quotes_1.QuoteUtils.escape(node.filename));
4528
4561
  }
4529
4562
  else {
4530
- output.push('STDIN');
4563
+ output.push(node.is_from ? 'STDIN' : 'STDOUT');
4531
4564
  }
4532
4565
  if (node.options && node.options.length > 0) {
4533
4566
  output.push('WITH');
@@ -4759,6 +4792,9 @@ class Deparser {
4759
4792
  const nodeData = this.getNodeData(node.def);
4760
4793
  if (nodeData && nodeData.typeName) {
4761
4794
  output.push(this.TypeName(nodeData.typeName, context));
4795
+ if (nodeData.collClause) {
4796
+ output.push(this.CollateClause(nodeData.collClause, context));
4797
+ }
4762
4798
  // Handle USING clause (stored in raw_default for ALTER COLUMN TYPE)
4763
4799
  if (nodeData.raw_default) {
4764
4800
  output.push('USING');
@@ -5239,9 +5275,8 @@ class Deparser {
5239
5275
  if (node.name) {
5240
5276
  output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
5241
5277
  }
5242
- output.push('SET');
5243
5278
  if (node.def) {
5244
- output.push(this.visit(node.def, context));
5279
+ output.push(this.formatIdentityColumnOptions(node.def, context));
5245
5280
  }
5246
5281
  break;
5247
5282
  case 'AT_DropIdentity':
@@ -5250,11 +5285,8 @@ class Deparser {
5250
5285
  output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
5251
5286
  }
5252
5287
  output.push('DROP IDENTITY');
5253
- if (node.behavior === 'DROP_CASCADE') {
5254
- output.push('CASCADE');
5255
- }
5256
- else if (node.behavior === 'DROP_RESTRICT') {
5257
- output.push('RESTRICT');
5288
+ if (node.missing_ok) {
5289
+ output.push('IF EXISTS');
5258
5290
  }
5259
5291
  break;
5260
5292
  case 'AT_ReAddStatistics':
@@ -5690,6 +5722,9 @@ class Deparser {
5690
5722
  if (node.arg && this.getNodeType(node.arg) === 'List') {
5691
5723
  const listData = this.getNodeData(node.arg);
5692
5724
  const listItems = list_utils_1.ListUtils.unwrapList(listData.items);
5725
+ if (listItems.length === 1 && listItems[0].String?.sval === 'none') {
5726
+ return 'OWNED BY NONE';
5727
+ }
5693
5728
  const parts = listItems.map(item => {
5694
5729
  const itemData = this.getNodeData(item);
5695
5730
  if (this.getNodeType(item) === 'String') {
@@ -5867,12 +5902,15 @@ class Deparser {
5867
5902
  }
5868
5903
  // Handle CreateStmt table options - no quotes, compact formatting
5869
5904
  if (context.parentNodeTypes.includes('CreateStmt')) {
5905
+ const relOptName = node.defnamespace
5906
+ ? `${quotes_1.QuoteUtils.quoteIdentifier(node.defnamespace)}.${quotes_1.QuoteUtils.quoteIdentifier(node.defname)}`
5907
+ : quotes_1.QuoteUtils.quoteIdentifier(node.defname);
5870
5908
  // For numeric values, use the raw value without quotes
5871
5909
  if (node.arg && this.getNodeType(node.arg) === 'Integer') {
5872
5910
  const integerData = this.getNodeData(node.arg);
5873
- return `${node.defname}=${integerData.ival}`;
5911
+ return `${relOptName}=${integerData.ival}`;
5874
5912
  }
5875
- return `${node.defname}=${argValue}`;
5913
+ return `${relOptName}=${argValue}`;
5876
5914
  }
5877
5915
  // Handle CreateEventTrigStmt WHEN clause - use IN syntax for List arguments
5878
5916
  if (context.parentNodeTypes.includes('CreateEventTrigStmt')) {
@@ -5911,15 +5949,27 @@ class Deparser {
5911
5949
  }
5912
5950
  // Handle CopyStmt WITH clause options - uppercase format without quotes
5913
5951
  if (context.parentNodeTypes.includes('CopyStmt')) {
5914
- if (node.defname === 'format' && node.arg && this.getNodeType(node.arg) === 'String') {
5915
- const stringData = this.getNodeData(node.arg);
5916
- return `FORMAT ${stringData.sval.toUpperCase()}`;
5952
+ const optName = node.defname.toUpperCase();
5953
+ if (!node.arg) {
5954
+ return optName;
5955
+ }
5956
+ const copyArgType = this.getNodeType(node.arg);
5957
+ if (copyArgType === 'Boolean' && this.getNodeData(node.arg).boolval !== false) {
5958
+ return optName;
5959
+ }
5960
+ if (copyArgType === 'List') {
5961
+ const items = list_utils_1.ListUtils.unwrapList(this.getNodeData(node.arg).items)
5962
+ .map(item => this.visit(item, context));
5963
+ return `${optName} (${items.join(', ')})`;
5917
5964
  }
5918
- // Handle other COPY options with uppercase defname
5919
- if (node.arg) {
5920
- return `${node.defname.toUpperCase()} ${argValue}`;
5965
+ if (copyArgType === 'String') {
5966
+ const sval = this.getNodeData(node.arg).sval;
5967
+ if (node.defname === 'format') {
5968
+ return `${optName} ${sval}`;
5969
+ }
5970
+ return `${optName} ${quotes_1.QuoteUtils.escape(sval)}`;
5921
5971
  }
5922
- return node.defname.toUpperCase();
5972
+ return `${optName} ${argValue}`;
5923
5973
  }
5924
5974
  // Handle CREATE OPERATOR and CREATE TYPE context
5925
5975
  if (context.parentNodeTypes.includes('DefineStmt')) {
@@ -6000,6 +6050,10 @@ class Deparser {
6000
6050
  return `${node.defname} = ${argValue}`;
6001
6051
  }
6002
6052
  }
6053
+ const argType = this.getNodeType(node.arg);
6054
+ if (argType === 'TypeName' || argType === 'Integer' || argType === 'Float' || argType === 'Boolean') {
6055
+ return `${node.defname} = ${argValue}`;
6056
+ }
6003
6057
  const quotedValue = typeof argValue === 'string'
6004
6058
  ? quotes_1.QuoteUtils.escape(argValue)
6005
6059
  : argValue;
@@ -6433,6 +6487,9 @@ class Deparser {
6433
6487
  case 'OBJECT_TABCONSTRAINT':
6434
6488
  output.push('CONSTRAINT');
6435
6489
  break;
6490
+ case 'OBJECT_DOMCONSTRAINT':
6491
+ output.push('CONSTRAINT');
6492
+ break;
6436
6493
  case 'OBJECT_TRIGGER':
6437
6494
  output.push('TRIGGER');
6438
6495
  break;
@@ -6509,6 +6566,15 @@ class Deparser {
6509
6566
  output.push(objectParts.join('.'));
6510
6567
  }
6511
6568
  }
6569
+ else if (node.objtype === 'OBJECT_DOMCONSTRAINT') {
6570
+ if (objectParts.length === 2) {
6571
+ const [domain, constraint] = objectParts;
6572
+ output.push(constraint, 'ON DOMAIN', domain);
6573
+ }
6574
+ else {
6575
+ output.push(objectParts.join('.'));
6576
+ }
6577
+ }
6512
6578
  else if (node.objtype === 'OBJECT_TRIGGER') {
6513
6579
  if (objectParts.length === 2) {
6514
6580
  const [table, trigger] = objectParts;
@@ -6840,7 +6906,7 @@ class Deparser {
6840
6906
  output.push(quotes_1.QuoteUtils.quoteIdentifier(node.pubname));
6841
6907
  }
6842
6908
  if (node.pubobjects && node.pubobjects.length > 0) {
6843
- output.push('FOR', 'TABLE');
6909
+ output.push('FOR');
6844
6910
  const tables = list_utils_1.ListUtils.unwrapList(node.pubobjects).map(table => this.visit(table, context));
6845
6911
  output.push(tables.join(', '));
6846
6912
  }
@@ -6880,8 +6946,9 @@ class Deparser {
6880
6946
  if (node.pubname) {
6881
6947
  output.push(quotes_1.QuoteUtils.quoteIdentifier(node.pubname));
6882
6948
  }
6883
- if (node.action) {
6884
- switch (node.action) {
6949
+ const hasObjects = node.pubobjects && node.pubobjects.length > 0;
6950
+ if (hasObjects) {
6951
+ switch (node.action || 'AP_AddObjects') {
6885
6952
  case 'AP_AddObjects':
6886
6953
  output.push('ADD');
6887
6954
  break;
@@ -6894,17 +6961,11 @@ class Deparser {
6894
6961
  default:
6895
6962
  throw new Error(`Unsupported AlterPublicationStmt action: ${node.action}`);
6896
6963
  }
6897
- }
6898
- if (node.for_all_tables) {
6899
- output.push('FOR ALL TABLES');
6900
- }
6901
- else if (node.pubobjects && node.pubobjects.length > 0) {
6902
- output.push('FOR TABLE');
6903
6964
  const objects = list_utils_1.ListUtils.unwrapList(node.pubobjects).map(obj => this.visit(obj, context));
6904
6965
  output.push(objects.join(', '));
6905
6966
  }
6906
- if (node.options && node.options.length > 0) {
6907
- output.push('WITH');
6967
+ else if (node.options && node.options.length > 0) {
6968
+ output.push('SET');
6908
6969
  const options = list_utils_1.ListUtils.unwrapList(node.options).map(opt => this.visit(opt, context));
6909
6970
  output.push(`(${options.join(', ')})`);
6910
6971
  }
@@ -6915,42 +6976,67 @@ class Deparser {
6915
6976
  if (node.subname) {
6916
6977
  output.push(quotes_1.QuoteUtils.quoteIdentifier(node.subname));
6917
6978
  }
6918
- if (node.kind) {
6919
- switch (node.kind) {
6979
+ const options = node.options && node.options.length > 0
6980
+ ? `(${list_utils_1.ListUtils.unwrapList(node.options).map(opt => this.visit(opt, context)).join(', ')})`
6981
+ : null;
6982
+ const publications = node.publication && node.publication.length > 0
6983
+ ? list_utils_1.ListUtils.unwrapList(node.publication).map(pub => this.visit(pub, context)).join(', ')
6984
+ : null;
6985
+ {
6986
+ switch (node.kind || 'ALTER_SUBSCRIPTION_OPTIONS') {
6920
6987
  case 'ALTER_SUBSCRIPTION_OPTIONS':
6921
6988
  output.push('SET');
6989
+ if (options)
6990
+ output.push(options);
6922
6991
  break;
6923
6992
  case 'ALTER_SUBSCRIPTION_CONNECTION':
6924
6993
  output.push('CONNECTION');
6925
6994
  if (node.conninfo) {
6926
- output.push(`'${node.conninfo}'`);
6995
+ output.push(quotes_1.QuoteUtils.escape(node.conninfo));
6927
6996
  }
6928
6997
  break;
6929
6998
  case 'ALTER_SUBSCRIPTION_SET_PUBLICATION':
6930
6999
  output.push('SET PUBLICATION');
6931
- if (node.publication && node.publication.length > 0) {
6932
- const publications = list_utils_1.ListUtils.unwrapList(node.publication).map(pub => this.visit(pub, context));
6933
- output.push(publications.join(', '));
6934
- }
7000
+ if (publications)
7001
+ output.push(publications);
7002
+ if (options)
7003
+ output.push('WITH', options);
7004
+ break;
7005
+ case 'ALTER_SUBSCRIPTION_ADD_PUBLICATION':
7006
+ output.push('ADD PUBLICATION');
7007
+ if (publications)
7008
+ output.push(publications);
7009
+ if (options)
7010
+ output.push('WITH', options);
7011
+ break;
7012
+ case 'ALTER_SUBSCRIPTION_DROP_PUBLICATION':
7013
+ output.push('DROP PUBLICATION');
7014
+ if (publications)
7015
+ output.push(publications);
7016
+ if (options)
7017
+ output.push('WITH', options);
6935
7018
  break;
6936
7019
  case 'ALTER_SUBSCRIPTION_REFRESH':
6937
7020
  output.push('REFRESH PUBLICATION');
7021
+ if (options)
7022
+ output.push('WITH', options);
6938
7023
  break;
6939
- case 'ALTER_SUBSCRIPTION_ENABLED':
6940
- output.push('ENABLE');
7024
+ case 'ALTER_SUBSCRIPTION_ENABLED': {
7025
+ const enabled = list_utils_1.ListUtils.unwrapList(node.options).find(opt => opt.DefElem?.defname === 'enabled');
7026
+ const arg = enabled?.DefElem?.arg;
7027
+ const isEnabled = arg?.Boolean ? arg.Boolean.boolval !== false : true;
7028
+ output.push(isEnabled ? 'ENABLE' : 'DISABLE');
6941
7029
  break;
7030
+ }
6942
7031
  case 'ALTER_SUBSCRIPTION_SKIP':
6943
7032
  output.push('SKIP');
7033
+ if (options)
7034
+ output.push(options);
6944
7035
  break;
6945
7036
  default:
6946
7037
  throw new Error(`Unsupported AlterSubscriptionStmt kind: ${node.kind}`);
6947
7038
  }
6948
7039
  }
6949
- if (node.options && node.options.length > 0) {
6950
- output.push('WITH');
6951
- const options = list_utils_1.ListUtils.unwrapList(node.options).map(opt => this.visit(opt, context));
6952
- output.push(`(${options.join(', ')})`);
6953
- }
6954
7040
  return output.join(' ');
6955
7041
  }
6956
7042
  DropSubscriptionStmt(node, context) {
@@ -8016,6 +8102,9 @@ class Deparser {
8016
8102
  if (node.grant_option && node.is_grant) {
8017
8103
  output.push('WITH GRANT OPTION');
8018
8104
  }
8105
+ if (node.grantor) {
8106
+ output.push('GRANTED BY', this.RoleSpec(node.grantor, context));
8107
+ }
8019
8108
  // Only add behavior clauses for REVOKE statements, not for GRANT statements in ALTER DEFAULT PRIVILEGES
8020
8109
  if (!node.is_grant) {
8021
8110
  if (node.behavior === 'DROP_CASCADE') {
@@ -8029,57 +8118,34 @@ class Deparser {
8029
8118
  }
8030
8119
  GrantRoleStmt(node, context) {
8031
8120
  const output = [];
8032
- // Check for inherit, admin, and set options first to place them correctly
8033
- let hasInheritOption = false;
8034
- let hasAdminOption = false;
8035
- let hasSetOption = false;
8036
- let inheritValue;
8037
- let adminValue;
8038
- let setValue;
8039
- if (node.opt && node.opt.length > 0) {
8040
- const options = list_utils_1.ListUtils.unwrapList(node.opt);
8041
- const inheritOption = options.find(opt => opt.DefElem && opt.DefElem.defname === 'inherit');
8042
- const adminOption = options.find(opt => (opt.String && opt.String.sval === 'admin') ||
8043
- (opt.DefElem && opt.DefElem.defname === 'admin'));
8044
- const setOption = options.find(opt => opt.DefElem && opt.DefElem.defname === 'set');
8045
- if (inheritOption && inheritOption.DefElem) {
8046
- hasInheritOption = true;
8047
- inheritValue = inheritOption.DefElem.arg?.Boolean?.boolval;
8048
- }
8049
- if (adminOption) {
8050
- hasAdminOption = true;
8051
- if (adminOption.DefElem && adminOption.DefElem.arg) {
8052
- adminValue = adminOption.DefElem.arg.Boolean?.boolval;
8053
- }
8054
- }
8055
- if (setOption && setOption.DefElem) {
8056
- hasSetOption = true;
8057
- setValue = setOption.DefElem.arg?.Boolean?.boolval;
8058
- }
8059
- }
8121
+ const options = list_utils_1.ListUtils.unwrapList(node.opt)
8122
+ .map(opt => opt.DefElem)
8123
+ .filter((opt) => !!opt && !!opt.defname);
8124
+ const optionValue = (opt) => {
8125
+ if (!opt.arg)
8126
+ return true;
8127
+ const argType = this.getNodeType(opt.arg);
8128
+ const argData = this.getNodeData(opt.arg);
8129
+ if (argType === 'Boolean')
8130
+ return argData.boolval !== false;
8131
+ if (argType === 'String')
8132
+ return argData.sval !== 'false';
8133
+ return true;
8134
+ };
8135
+ const roles = list_utils_1.ListUtils.unwrapList(node.granted_roles)
8136
+ .map(role => role.AccessPriv?.priv_name
8137
+ ? quotes_1.QuoteUtils.quoteIdentifier(role.AccessPriv.priv_name)
8138
+ : this.visit(role, context))
8139
+ .join(', ');
8060
8140
  if (node.is_grant) {
8061
- output.push('GRANT');
8141
+ output.push('GRANT', roles, 'TO');
8062
8142
  }
8063
8143
  else {
8064
8144
  output.push('REVOKE');
8065
- if (hasInheritOption) {
8066
- output.push('INHERIT OPTION FOR');
8067
- }
8068
- else if (hasAdminOption) {
8069
- output.push('ADMIN OPTION FOR');
8145
+ if (options.length > 0) {
8146
+ output.push(`${options[0].defname.toUpperCase()} OPTION FOR`);
8070
8147
  }
8071
- }
8072
- if (node.granted_roles && node.granted_roles.length > 0) {
8073
- const roles = list_utils_1.ListUtils.unwrapList(node.granted_roles)
8074
- .map(role => this.visit(role, context))
8075
- .join(', ');
8076
- output.push(roles);
8077
- }
8078
- if (node.is_grant) {
8079
- output.push('TO');
8080
- }
8081
- else {
8082
- output.push('FROM');
8148
+ output.push(roles, 'FROM');
8083
8149
  }
8084
8150
  if (node.grantee_roles && node.grantee_roles.length > 0) {
8085
8151
  const grantees = list_utils_1.ListUtils.unwrapList(node.grantee_roles)
@@ -8087,38 +8153,15 @@ class Deparser {
8087
8153
  .join(', ');
8088
8154
  output.push(grantees);
8089
8155
  }
8090
- if (node.is_grant) {
8091
- const withOptions = [];
8092
- if (hasAdminOption) {
8093
- if (adminValue === true) {
8094
- withOptions.push('ADMIN OPTION');
8095
- }
8096
- else if (adminValue === false) {
8097
- withOptions.push('ADMIN FALSE');
8098
- }
8099
- else {
8100
- withOptions.push('ADMIN OPTION');
8101
- }
8102
- }
8103
- if (hasInheritOption) {
8104
- if (inheritValue === true) {
8105
- withOptions.push('INHERIT OPTION');
8106
- }
8107
- else if (inheritValue === false) {
8108
- withOptions.push('INHERIT FALSE');
8109
- }
8110
- }
8111
- if (hasSetOption) {
8112
- if (setValue === true) {
8113
- withOptions.push('SET TRUE');
8114
- }
8115
- else if (setValue === false) {
8116
- withOptions.push('SET FALSE');
8117
- }
8118
- }
8119
- if (withOptions.length > 0) {
8120
- output.push('WITH', withOptions.join(', '));
8121
- }
8156
+ if (node.is_grant && options.length > 0) {
8157
+ const withOptions = options.map(opt => `${opt.defname.toUpperCase()} ${optionValue(opt) ? 'TRUE' : 'FALSE'}`);
8158
+ output.push('WITH', withOptions.join(', '));
8159
+ }
8160
+ if (node.grantor) {
8161
+ output.push('GRANTED BY', this.RoleSpec(node.grantor, context));
8162
+ }
8163
+ if (!node.is_grant && node.behavior === 'DROP_CASCADE') {
8164
+ output.push('CASCADE');
8122
8165
  }
8123
8166
  return output.join(' ');
8124
8167
  }
@@ -8698,10 +8741,9 @@ class Deparser {
8698
8741
  switch (node.commandType) {
8699
8742
  case 'CMD_UPDATE': {
8700
8743
  output.push('UPDATE SET');
8701
- const assignments = list_utils_1.ListUtils.unwrapList(node.targetList)
8702
- .map(target => this.visit(target, context.spawn('UpdateStmt', { update: true })))
8703
- .join(', ');
8704
- output.push(assignments);
8744
+ if (node.targetList && node.targetList.length > 0) {
8745
+ output.push(this.formatUpdateAssignments(node.targetList, context));
8746
+ }
8705
8747
  break;
8706
8748
  }
8707
8749
  case 'CMD_INSERT': {
@@ -9106,6 +9148,13 @@ class Deparser {
9106
9148
  output.push('USING');
9107
9149
  output.push(node.into.accessMethod);
9108
9150
  }
9151
+ if (node.into && node.into.options && node.into.options.length > 0) {
9152
+ output.push('WITH');
9153
+ const options = list_utils_1.ListUtils.unwrapList(node.into.options)
9154
+ .map(option => this.visit(option, context.spawn('CreateStmt')))
9155
+ .join(', ');
9156
+ output.push(`(${options})`);
9157
+ }
9109
9158
  if (node.into && node.into.onCommit && node.into.onCommit !== 'ONCOMMIT_NOOP') {
9110
9159
  output.push('ON COMMIT');
9111
9160
  switch (node.into.onCommit) {
@@ -9120,17 +9169,13 @@ class Deparser {
9120
9169
  break;
9121
9170
  }
9122
9171
  }
9172
+ if (node.into && node.into.tableSpaceName) {
9173
+ output.push('TABLESPACE', quotes_1.QuoteUtils.quoteIdentifier(node.into.tableSpaceName));
9174
+ }
9123
9175
  output.push('AS');
9124
9176
  if (node.query) {
9125
9177
  output.push(this.visit(node.query, context));
9126
9178
  }
9127
- if (node.into && node.into.options && node.into.options.length > 0) {
9128
- output.push('WITH');
9129
- const options = list_utils_1.ListUtils.unwrapList(node.into.options)
9130
- .map(option => this.visit(option, context))
9131
- .join(', ');
9132
- output.push(`(${options})`);
9133
- }
9134
9179
  if (node.into && node.into.skipData) {
9135
9180
  output.push('WITH NO DATA');
9136
9181
  }
@@ -9603,7 +9648,7 @@ class Deparser {
9603
9648
  }
9604
9649
  if (node.whereClause) {
9605
9650
  output.push('WHERE');
9606
- output.push(this.visit(node.whereClause, context));
9651
+ output.push(`(${this.visit(node.whereClause, context)})`);
9607
9652
  }
9608
9653
  return output.join(' ');
9609
9654
  }
@@ -9846,24 +9891,182 @@ class Deparser {
9846
9891
  }
9847
9892
  return output.join(' ');
9848
9893
  }
9894
+ formatJsonFormat(format) {
9895
+ if (!format || !format.format_type || format.format_type === 'JS_FORMAT_DEFAULT') {
9896
+ return null;
9897
+ }
9898
+ const parts = ['FORMAT JSON'];
9899
+ switch (format.encoding) {
9900
+ case 'JS_ENC_UTF8':
9901
+ parts.push('ENCODING UTF8');
9902
+ break;
9903
+ case 'JS_ENC_UTF16':
9904
+ parts.push('ENCODING UTF16');
9905
+ break;
9906
+ case 'JS_ENC_UTF32':
9907
+ parts.push('ENCODING UTF32');
9908
+ break;
9909
+ }
9910
+ return parts.join(' ');
9911
+ }
9912
+ formatJsonOutput(output, context) {
9913
+ if (!output || !output.typeName)
9914
+ return null;
9915
+ const parts = ['RETURNING', this.TypeName(output.typeName, context)];
9916
+ const format = this.formatJsonFormat(output.returning?.format);
9917
+ if (format)
9918
+ parts.push(format);
9919
+ return parts.join(' ');
9920
+ }
9921
+ formatJsonNullClause(absentOnNull) {
9922
+ return absentOnNull ? 'ABSENT ON NULL' : 'NULL ON NULL';
9923
+ }
9924
+ formatJsonUniqueClause(unique) {
9925
+ return unique ? 'WITH UNIQUE KEYS' : null;
9926
+ }
9927
+ JsonValueExpr(node, context) {
9928
+ const parts = [];
9929
+ if (node.raw_expr) {
9930
+ parts.push(this.visit(node.raw_expr, context));
9931
+ }
9932
+ const format = this.formatJsonFormat(node.format);
9933
+ if (format)
9934
+ parts.push(format);
9935
+ return parts.join(' ');
9936
+ }
9937
+ JsonKeyValue(node, context) {
9938
+ const key = node.key ? this.visit(node.key, context) : '';
9939
+ const value = node.value ? this.JsonValueExpr(node.value, context) : '';
9940
+ return `${key} : ${value}`;
9941
+ }
9942
+ JsonObjectConstructor(node, context) {
9943
+ const args = [];
9944
+ if (node.exprs && node.exprs.length > 0) {
9945
+ args.push(list_utils_1.ListUtils.unwrapList(node.exprs).map(e => this.visit(e, context)).join(', '));
9946
+ args.push(this.formatJsonNullClause(node.absent_on_null));
9947
+ const unique = this.formatJsonUniqueClause(node.unique);
9948
+ if (unique)
9949
+ args.push(unique);
9950
+ }
9951
+ const output = this.formatJsonOutput(node.output, context);
9952
+ if (output)
9953
+ args.push(output);
9954
+ return `JSON_OBJECT(${args.join(' ')})`;
9955
+ }
9956
+ JsonArrayConstructor(node, context) {
9957
+ const args = [];
9958
+ if (node.exprs && node.exprs.length > 0) {
9959
+ args.push(list_utils_1.ListUtils.unwrapList(node.exprs).map(e => this.visit(e, context)).join(', '));
9960
+ args.push(this.formatJsonNullClause(node.absent_on_null));
9961
+ }
9962
+ const output = this.formatJsonOutput(node.output, context);
9963
+ if (output)
9964
+ args.push(output);
9965
+ return `JSON_ARRAY(${args.join(' ')})`;
9966
+ }
9849
9967
  JsonArrayQueryConstructor(node, context) {
9850
- const output = ['JSON_ARRAYAGG'];
9968
+ const args = [];
9851
9969
  if (node.query) {
9852
- output.push(`(${this.visit(node.query, context)})`);
9970
+ args.push(this.visit(node.query, context));
9971
+ }
9972
+ const format = this.formatJsonFormat(node.format);
9973
+ if (format)
9974
+ args.push(format);
9975
+ const output = this.formatJsonOutput(node.output, context);
9976
+ if (output)
9977
+ args.push(output);
9978
+ return `JSON_ARRAY(${args.join(' ')})`;
9979
+ }
9980
+ formatJsonAggSuffix(ctor, context) {
9981
+ if (!ctor)
9982
+ return '';
9983
+ let result = '';
9984
+ if (ctor.agg_filter) {
9985
+ result += ` FILTER (WHERE ${this.visit(ctor.agg_filter, context)})`;
9853
9986
  }
9854
- if (node.format) {
9855
- output.push('FORMAT JSON');
9987
+ if (ctor.over) {
9988
+ result += this.formatOverClause(ctor.over, context);
9856
9989
  }
9857
- if (node.output) {
9858
- output.push('RETURNING TEXT');
9990
+ return result;
9991
+ }
9992
+ JsonObjectAgg(node, context) {
9993
+ const args = [];
9994
+ if (node.arg) {
9995
+ args.push(this.JsonKeyValue(node.arg, context));
9996
+ }
9997
+ args.push(this.formatJsonNullClause(node.absent_on_null));
9998
+ const unique = this.formatJsonUniqueClause(node.unique);
9999
+ if (unique)
10000
+ args.push(unique);
10001
+ const output = this.formatJsonOutput(node.constructor?.output, context);
10002
+ if (output)
10003
+ args.push(output);
10004
+ return `JSON_OBJECTAGG(${args.join(' ')})${this.formatJsonAggSuffix(node.constructor, context)}`;
10005
+ }
10006
+ JsonArrayAgg(node, context) {
10007
+ const args = [];
10008
+ if (node.arg) {
10009
+ args.push(this.JsonValueExpr(node.arg, context));
9859
10010
  }
9860
- if (node.absent_on_null) {
9861
- output.push('ABSENT ON NULL');
10011
+ const aggOrder = node.constructor?.agg_order;
10012
+ if (aggOrder && aggOrder.length > 0) {
10013
+ const orderItems = list_utils_1.ListUtils.unwrapList(aggOrder).map(item => this.visit(item, context));
10014
+ args.push(`ORDER BY ${orderItems.join(', ')}`);
9862
10015
  }
9863
- else {
9864
- output.push('NULL ON NULL');
10016
+ args.push(this.formatJsonNullClause(node.absent_on_null));
10017
+ const output = this.formatJsonOutput(node.constructor?.output, context);
10018
+ if (output)
10019
+ args.push(output);
10020
+ return `JSON_ARRAYAGG(${args.join(' ')})${this.formatJsonAggSuffix(node.constructor, context)}`;
10021
+ }
10022
+ JsonParseExpr(node, context) {
10023
+ const args = [];
10024
+ if (node.expr) {
10025
+ args.push(this.JsonValueExpr(node.expr, context));
9865
10026
  }
9866
- return output.join(' ');
10027
+ const unique = this.formatJsonUniqueClause(node.unique_keys);
10028
+ if (unique)
10029
+ args.push(unique);
10030
+ return `JSON(${args.join(' ')})`;
10031
+ }
10032
+ JsonScalarExpr(node, context) {
10033
+ const expr = node.expr ? this.visit(node.expr, context) : '';
10034
+ return `JSON_SCALAR(${expr})`;
10035
+ }
10036
+ JsonSerializeExpr(node, context) {
10037
+ const args = [];
10038
+ if (node.expr) {
10039
+ args.push(this.JsonValueExpr(node.expr, context));
10040
+ }
10041
+ const output = this.formatJsonOutput(node.output, context);
10042
+ if (output)
10043
+ args.push(output);
10044
+ return `JSON_SERIALIZE(${args.join(' ')})`;
10045
+ }
10046
+ JsonIsPredicate(node, context) {
10047
+ const parts = [];
10048
+ if (node.expr) {
10049
+ parts.push(this.visit(node.expr, context));
10050
+ }
10051
+ const format = this.formatJsonFormat(node.format);
10052
+ if (format)
10053
+ parts.push(format);
10054
+ parts.push('IS JSON');
10055
+ switch (node.item_type) {
10056
+ case 'JS_TYPE_OBJECT':
10057
+ parts.push('OBJECT');
10058
+ break;
10059
+ case 'JS_TYPE_ARRAY':
10060
+ parts.push('ARRAY');
10061
+ break;
10062
+ case 'JS_TYPE_SCALAR':
10063
+ parts.push('SCALAR');
10064
+ break;
10065
+ }
10066
+ if (node.unique_keys) {
10067
+ parts.push('WITH UNIQUE KEYS');
10068
+ }
10069
+ return parts.join(' ');
9867
10070
  }
9868
10071
  RangeFunction(node, context) {
9869
10072
  const output = [];