pgsql-deparser 18.3.9 → 18.3.10

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.d.ts CHANGED
@@ -260,9 +260,12 @@ export declare class Deparser implements DeparserVisitor {
260
260
  OnConflictExpr(node: t.OnConflictExpr, context: DeparserContext): string;
261
261
  ScanToken(node: t.ScanToken, context: DeparserContext): string;
262
262
  CreateOpClassItem(node: t.CreateOpClassItem, context: DeparserContext): string;
263
+ private quoteDefElemArg;
264
+ private formatOptionValue;
263
265
  Var(node: t.Var, context: DeparserContext): string;
264
266
  TableFunc(node: t.TableFunc, context: DeparserContext): string;
265
267
  RangeTableFunc(node: t.RangeTableFunc, context: DeparserContext): string;
268
+ private formatXmlTableExpr;
266
269
  RangeTableFuncCol(node: t.RangeTableFuncCol, context: DeparserContext): string;
267
270
  formatJsonFormat(format: t.JsonFormat | undefined): string | null;
268
271
  formatJsonOutput(output: t.JsonOutput | undefined, context: DeparserContext): string | null;
package/deparser.js CHANGED
@@ -132,6 +132,21 @@ function formatBitString(bsval) {
132
132
  }
133
133
  return `b'${quote(bsval)}'`;
134
134
  }
135
+ 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)*)?)$/;
136
+ function formatInteger(ival) {
137
+ const value = String(ival);
138
+ if (!/^[+-]?\d+$/.test(value)) {
139
+ throw new Error(`Invalid integer literal: ${value}`);
140
+ }
141
+ return value;
142
+ }
143
+ function formatNumeric(fval) {
144
+ const value = String(fval);
145
+ if (!NUMERIC_LITERAL.test(value)) {
146
+ throw new Error(`Invalid numeric literal: ${value}`);
147
+ }
148
+ return value;
149
+ }
135
150
  class Deparser {
136
151
  tree;
137
152
  options;
@@ -1600,39 +1615,39 @@ class Deparser {
1600
1615
  if (nodeAny.ival !== undefined) {
1601
1616
  if (typeof nodeAny.ival === 'object' && nodeAny.ival !== null) {
1602
1617
  if (nodeAny.ival.ival !== undefined) {
1603
- return nodeAny.ival.ival.toString();
1618
+ return formatInteger(nodeAny.ival.ival);
1604
1619
  }
1605
1620
  else if (Object.keys(nodeAny.ival).length === 0) {
1606
1621
  return '0';
1607
1622
  }
1608
1623
  else {
1609
- return nodeAny.ival.toString();
1624
+ return formatInteger(nodeAny.ival);
1610
1625
  }
1611
1626
  }
1612
1627
  else if (nodeAny.ival === null) {
1613
1628
  return 'NULL';
1614
1629
  }
1615
1630
  else {
1616
- return nodeAny.ival.toString();
1631
+ return formatInteger(nodeAny.ival);
1617
1632
  }
1618
1633
  }
1619
1634
  else if (nodeAny.fval !== undefined) {
1620
1635
  if (typeof nodeAny.fval === 'object' && nodeAny.fval !== null) {
1621
1636
  if (nodeAny.fval.fval !== undefined) {
1622
- return nodeAny.fval.fval.toString();
1637
+ return formatNumeric(nodeAny.fval.fval);
1623
1638
  }
1624
1639
  else if (Object.keys(nodeAny.fval).length === 0) {
1625
1640
  return '0.0';
1626
1641
  }
1627
1642
  else {
1628
- return nodeAny.fval.toString();
1643
+ return formatNumeric(nodeAny.fval);
1629
1644
  }
1630
1645
  }
1631
1646
  else if (nodeAny.fval === null) {
1632
1647
  return 'NULL';
1633
1648
  }
1634
1649
  else {
1635
- return nodeAny.fval.toString();
1650
+ return formatNumeric(nodeAny.fval);
1636
1651
  }
1637
1652
  }
1638
1653
  else if (nodeAny.sval !== undefined) {
@@ -1697,10 +1712,10 @@ class Deparser {
1697
1712
  }
1698
1713
  if (nodeAny.val) {
1699
1714
  if (nodeAny.val.Integer?.ival !== undefined) {
1700
- return nodeAny.val.Integer.ival.toString();
1715
+ return formatInteger(nodeAny.val.Integer.ival);
1701
1716
  }
1702
1717
  else if (nodeAny.val.Float?.fval !== undefined) {
1703
- return nodeAny.val.Float.fval.toString();
1718
+ return formatNumeric(nodeAny.val.Float.fval);
1704
1719
  }
1705
1720
  else if (nodeAny.val.String?.sval !== undefined) {
1706
1721
  return quotes_1.QuoteUtils.escape(nodeAny.val.String.sval);
@@ -1721,15 +1736,15 @@ class Deparser {
1721
1736
  }
1722
1737
  if (nodeAny.Integer !== undefined) {
1723
1738
  if (typeof nodeAny.Integer === 'object' && nodeAny.Integer.ival !== undefined) {
1724
- return nodeAny.Integer.ival.toString();
1739
+ return formatInteger(nodeAny.Integer.ival);
1725
1740
  }
1726
- return nodeAny.Integer.toString();
1741
+ return formatInteger(nodeAny.Integer);
1727
1742
  }
1728
1743
  if (nodeAny.Float !== undefined) {
1729
1744
  if (typeof nodeAny.Float === 'object' && nodeAny.Float.fval !== undefined) {
1730
- return nodeAny.Float.fval.toString();
1745
+ return formatNumeric(nodeAny.Float.fval);
1731
1746
  }
1732
- return nodeAny.Float.toString();
1747
+ return formatNumeric(nodeAny.Float);
1733
1748
  }
1734
1749
  if (nodeAny.String !== undefined) {
1735
1750
  if (typeof nodeAny.String === 'object' && nodeAny.String.sval !== undefined) {
@@ -2380,10 +2395,10 @@ class Deparser {
2380
2395
  return quotes_1.QuoteUtils.quoteIdentifier(value);
2381
2396
  }
2382
2397
  Integer(node, context) {
2383
- return node.ival?.toString() || '0';
2398
+ return node.ival === undefined || node.ival === null ? '0' : formatInteger(node.ival);
2384
2399
  }
2385
2400
  Float(node, context) {
2386
- return node.fval || '0.0';
2401
+ return node.fval ? formatNumeric(node.fval) : '0.0';
2387
2402
  }
2388
2403
  Boolean(node, context) {
2389
2404
  return node.boolval ? 'true' : 'false';
@@ -3621,7 +3636,7 @@ class Deparser {
3621
3636
  const opclassOpts = list_utils_1.ListUtils.unwrapList(node.opclassopts).map(opt => {
3622
3637
  if (opt.DefElem && opt.DefElem.arg && this.getNodeType(opt.DefElem.arg) === 'String') {
3623
3638
  const stringData = this.getNodeData(opt.DefElem.arg);
3624
- return `${opt.DefElem.defname}='${stringData.sval}'`;
3639
+ return `${quotes_1.QuoteUtils.quoteIdentifierAfterDot(opt.DefElem.defname)}=${quotes_1.QuoteUtils.escape(stringData.sval)}`;
3625
3640
  }
3626
3641
  return this.visit(opt, context.spawn('IndexElem'));
3627
3642
  });
@@ -3922,19 +3937,19 @@ class Deparser {
3922
3937
  case 'TRANS_STMT_PREPARE':
3923
3938
  output.push('PREPARE TRANSACTION');
3924
3939
  if (node.gid) {
3925
- output.push(`'${node.gid}'`);
3940
+ output.push(quotes_1.QuoteUtils.escape(node.gid));
3926
3941
  }
3927
3942
  break;
3928
3943
  case 'TRANS_STMT_COMMIT_PREPARED':
3929
3944
  output.push('COMMIT PREPARED');
3930
3945
  if (node.gid) {
3931
- output.push(`'${node.gid}'`);
3946
+ output.push(quotes_1.QuoteUtils.escape(node.gid));
3932
3947
  }
3933
3948
  break;
3934
3949
  case 'TRANS_STMT_ROLLBACK_PREPARED':
3935
3950
  output.push('ROLLBACK PREPARED');
3936
3951
  if (node.gid) {
3937
- output.push(`'${node.gid}'`);
3952
+ output.push(quotes_1.QuoteUtils.escape(node.gid));
3938
3953
  }
3939
3954
  break;
3940
3955
  default:
@@ -4010,8 +4025,8 @@ class Deparser {
4010
4025
  const nodeData = this.getNodeData(arg);
4011
4026
  if (nodeData.sval !== undefined) {
4012
4027
  const svalValue = typeof nodeData.sval === 'object' ? nodeData.sval.sval : nodeData.sval;
4013
- if (svalValue === '' || svalValue.includes(' ') || svalValue.includes('-') || /[A-Z]/.test(svalValue) || /^\d/.test(svalValue) || svalValue.includes('.') || svalValue.includes('$') || svalValue.toLowerCase() === 'all' || /^[+-]\d/.test(svalValue)) {
4014
- return `'${svalValue}'`;
4028
+ 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)) {
4029
+ return quotes_1.QuoteUtils.escape(svalValue);
4015
4030
  }
4016
4031
  return svalValue;
4017
4032
  }
@@ -5538,9 +5553,9 @@ class Deparser {
5538
5553
  if (context.parentNodeTypes.includes('IndexElem')) {
5539
5554
  if (node.arg && this.getNodeType(node.arg) === 'String') {
5540
5555
  const stringData = this.getNodeData(node.arg);
5541
- return `${node.defname}='${stringData.sval}'`;
5556
+ return `${quotes_1.QuoteUtils.quoteIdentifierAfterDot(node.defname)}=${quotes_1.QuoteUtils.escape(stringData.sval)}`;
5542
5557
  }
5543
- return `${node.defname}=${this.visit(node.arg, context.spawn('DefElem'))}`;
5558
+ return `${quotes_1.QuoteUtils.quoteIdentifierAfterDot(node.defname)}=${this.formatOptionValue(node.arg, this.visit(node.arg, context.spawn('DefElem')))}`;
5544
5559
  }
5545
5560
  // Handle CREATE OPERATOR boolean flags - MUST be first to preserve case
5546
5561
  if (context.parentNodeTypes.includes('DefineStmt') &&
@@ -5565,9 +5580,7 @@ class Deparser {
5565
5580
  const defElemContext = context.spawn('DefElem');
5566
5581
  const argValue = this.visit(node.arg, defElemContext);
5567
5582
  if (context.parentNodeTypes.includes('CreateFdwStmt') || context.parentNodeTypes.includes('AlterFdwStmt')) {
5568
- const finalValue = typeof argValue === 'string' && !argValue.startsWith("'")
5569
- ? `'${argValue}'`
5570
- : argValue;
5583
+ const finalValue = this.quoteDefElemArg(node.arg, argValue);
5571
5584
  const quotedDefname = quotes_1.QuoteUtils.quoteIdentifier(node.defname);
5572
5585
  if (node.defaction === 'DEFELEM_ADD') {
5573
5586
  return `ADD ${quotedDefname} ${finalValue}`;
@@ -5580,9 +5593,7 @@ class Deparser {
5580
5593
  }
5581
5594
  return `${quotedDefname} ${finalValue}`;
5582
5595
  }
5583
- const quotedValue = typeof argValue === 'string' && !argValue.startsWith("'")
5584
- ? `'${argValue}'`
5585
- : argValue;
5596
+ const quotedValue = this.quoteDefElemArg(node.arg, argValue);
5586
5597
  if (node.defaction === 'DEFELEM_ADD') {
5587
5598
  return `ADD ${node.defname} ${quotedValue}`;
5588
5599
  }
@@ -5614,9 +5625,7 @@ class Deparser {
5614
5625
  }
5615
5626
  const defElemContext = context.spawn('DefElem');
5616
5627
  const argValue = this.visit(node.arg, defElemContext);
5617
- const quotedValue = typeof argValue === 'string' && !argValue.startsWith("'")
5618
- ? `'${argValue}'`
5619
- : argValue;
5628
+ const quotedValue = this.quoteDefElemArg(node.arg, argValue);
5620
5629
  return `PASSWORD ${quotedValue}`;
5621
5630
  }
5622
5631
  }
@@ -5681,9 +5690,7 @@ class Deparser {
5681
5690
  }
5682
5691
  }
5683
5692
  if (node.defname === 'validUntil') {
5684
- const quotedValue = typeof argValue === 'string' && !argValue.startsWith("'")
5685
- ? `'${argValue}'`
5686
- : argValue;
5693
+ const quotedValue = this.quoteDefElemArg(node.arg, argValue);
5687
5694
  return `VALID UNTIL ${quotedValue}`;
5688
5695
  }
5689
5696
  if (node.defname === 'adminmembers') {
@@ -5863,9 +5870,7 @@ class Deparser {
5863
5870
  if (context.parentNodeTypes.includes('AlterExtensionStmt')) {
5864
5871
  if (node.defname === 'new_version') {
5865
5872
  // argValue is unquoted due to DefElem context, so we need to quote it
5866
- const quotedValue = typeof argValue === 'string' && !argValue.startsWith("'")
5867
- ? `'${argValue}'`
5868
- : argValue;
5873
+ const quotedValue = this.quoteDefElemArg(node.arg, argValue);
5869
5874
  return `UPDATE TO ${quotedValue}`;
5870
5875
  }
5871
5876
  if (node.defname === 'schema') {
@@ -5890,15 +5895,15 @@ class Deparser {
5890
5895
  }
5891
5896
  // Handle IndexStmt WITH clause options - no quotes, compact formatting
5892
5897
  if (context.parentNodeTypes.includes('IndexStmt')) {
5893
- return `${node.defname}=${argValue}`;
5898
+ return `${quotes_1.QuoteUtils.quoteIdentifierAfterDot(node.defname)}=${this.formatOptionValue(node.arg, argValue)}`;
5894
5899
  }
5895
5900
  // Handle IndexElem opclassopts - preserve string values as strings
5896
5901
  if (context.parentNodeTypes.includes('IndexElem')) {
5897
5902
  if (node.arg && this.getNodeType(node.arg) === 'String') {
5898
5903
  const stringData = this.getNodeData(node.arg);
5899
- return `${node.defname}='${stringData.sval}'`;
5904
+ return `${quotes_1.QuoteUtils.quoteIdentifierAfterDot(node.defname)}=${quotes_1.QuoteUtils.escape(stringData.sval)}`;
5900
5905
  }
5901
- return `${node.defname}=${argValue}`;
5906
+ return `${quotes_1.QuoteUtils.quoteIdentifierAfterDot(node.defname)}=${this.formatOptionValue(node.arg, argValue)}`;
5902
5907
  }
5903
5908
  // Handle CreateStmt table options - no quotes, compact formatting
5904
5909
  if (context.parentNodeTypes.includes('CreateStmt')) {
@@ -5908,9 +5913,9 @@ class Deparser {
5908
5913
  // For numeric values, use the raw value without quotes
5909
5914
  if (node.arg && this.getNodeType(node.arg) === 'Integer') {
5910
5915
  const integerData = this.getNodeData(node.arg);
5911
- return `${relOptName}=${integerData.ival}`;
5916
+ return `${relOptName}=${formatInteger(integerData.ival)}`;
5912
5917
  }
5913
- return `${relOptName}=${argValue}`;
5918
+ return `${relOptName}=${this.formatOptionValue(node.arg, argValue)}`;
5914
5919
  }
5915
5920
  // Handle CreateEventTrigStmt WHEN clause - use IN syntax for List arguments
5916
5921
  if (context.parentNodeTypes.includes('CreateEventTrigStmt')) {
@@ -5920,7 +5925,7 @@ class Deparser {
5920
5925
  const values = listItems.map(item => {
5921
5926
  if (this.getNodeType(item) === 'String') {
5922
5927
  const stringData = this.getNodeData(item);
5923
- return `'${stringData.sval || ''}'`;
5928
+ return quotes_1.QuoteUtils.escape(stringData.sval || '');
5924
5929
  }
5925
5930
  return this.visit(item, context);
5926
5931
  });
@@ -5935,7 +5940,7 @@ class Deparser {
5935
5940
  if (node.arg && this.getNodeType(node.arg) === 'TypeName') {
5936
5941
  return `${optionName} = ${argValue}`;
5937
5942
  }
5938
- return `${optionName} = ${argValue}`;
5943
+ return `${optionName} = ${this.formatOptionValue(node.arg, argValue)}`;
5939
5944
  }
5940
5945
  // Handle ViewStmt WITH options - don't quote numeric values
5941
5946
  if (context.parentNodeTypes.includes('ViewStmt')) {
@@ -6008,7 +6013,7 @@ class Deparser {
6008
6013
  return `${quotedDefname} = ${stringData.sval}`;
6009
6014
  }
6010
6015
  // Regular string literals get single quotes
6011
- return `${quotedDefname} = '${stringData.sval}'`;
6016
+ return `${quotedDefname} = ${quotes_1.QuoteUtils.escape(stringData.sval)}`;
6012
6017
  }
6013
6018
  return `${quotedDefname} = ${argValue}`;
6014
6019
  }
@@ -6029,7 +6034,7 @@ class Deparser {
6029
6034
  return `${node.defname} = ${stringData.sval}`;
6030
6035
  }
6031
6036
  // Regular string literals get single quotes
6032
- return `${node.defname} = '${stringData.sval}'`;
6037
+ return `${node.defname} = ${quotes_1.QuoteUtils.escape(stringData.sval)}`;
6033
6038
  }
6034
6039
  if (node.arg && this.getNodeType(node.arg) === 'Boolean') {
6035
6040
  const boolData = this.getNodeData(node.arg);
@@ -6079,7 +6084,7 @@ class Deparser {
6079
6084
  }
6080
6085
  if (node.location) {
6081
6086
  output.push('LOCATION');
6082
- output.push(`'${node.location}'`);
6087
+ output.push(quotes_1.QuoteUtils.escape(node.location));
6083
6088
  }
6084
6089
  if (node.options && node.options.length > 0) {
6085
6090
  output.push('WITH');
@@ -6412,7 +6417,7 @@ class Deparser {
6412
6417
  }
6413
6418
  if (node.payload !== null && node.payload !== undefined) {
6414
6419
  output.push(',');
6415
- output.push(`'${node.payload}'`);
6420
+ output.push(quotes_1.QuoteUtils.escape(node.payload));
6416
6421
  }
6417
6422
  return output.join(' ');
6418
6423
  }
@@ -6440,7 +6445,7 @@ class Deparser {
6440
6445
  if (!node.filename) {
6441
6446
  throw new Error('LoadStmt requires filename');
6442
6447
  }
6443
- return `LOAD '${node.filename}'`;
6448
+ return `LOAD ${quotes_1.QuoteUtils.escape(node.filename)}`;
6444
6449
  }
6445
6450
  DiscardStmt(node, context) {
6446
6451
  switch (node.target) {
@@ -6927,7 +6932,7 @@ class Deparser {
6927
6932
  }
6928
6933
  output.push('CONNECTION');
6929
6934
  if (node.conninfo) {
6930
- output.push(`'${node.conninfo}'`);
6935
+ output.push(quotes_1.QuoteUtils.escape(node.conninfo));
6931
6936
  }
6932
6937
  output.push('PUBLICATION');
6933
6938
  if (node.publication && node.publication.length > 0) {
@@ -8210,7 +8215,7 @@ class Deparser {
8210
8215
  }
8211
8216
  output.push('IS');
8212
8217
  if (node.label) {
8213
- output.push(`'${node.label}'`);
8218
+ output.push(quotes_1.QuoteUtils.escape(node.label));
8214
8219
  }
8215
8220
  else {
8216
8221
  output.push('NULL');
@@ -8267,10 +8272,10 @@ class Deparser {
8267
8272
  output.push(conversionName);
8268
8273
  }
8269
8274
  if (node.for_encoding_name) {
8270
- output.push('FOR', `'${node.for_encoding_name}'`);
8275
+ output.push('FOR', quotes_1.QuoteUtils.escape(node.for_encoding_name));
8271
8276
  }
8272
8277
  if (node.to_encoding_name) {
8273
- output.push('TO', `'${node.to_encoding_name}'`);
8278
+ output.push('TO', quotes_1.QuoteUtils.escape(node.to_encoding_name));
8274
8279
  }
8275
8280
  if (node.func_name && node.func_name.length > 0) {
8276
8281
  output.push('FROM');
@@ -9405,7 +9410,7 @@ class Deparser {
9405
9410
  const preservedDefName = quotedDefName !== defName ? quotedDefName : defName;
9406
9411
  // Handle String arguments with single quotes for string literals
9407
9412
  if (defValue.String) {
9408
- return `${preservedDefName} = '${defValue.String.sval}'`;
9413
+ return `${preservedDefName} = ${quotes_1.QuoteUtils.escape(defValue.String.sval)}`;
9409
9414
  }
9410
9415
  return `${preservedDefName} = ${this.visit(defValue, context)}`;
9411
9416
  }
@@ -9528,7 +9533,7 @@ class Deparser {
9528
9533
  // For CREATE COLLATION, ensure String nodes are quoted as string literals
9529
9534
  let valueStr;
9530
9535
  if (defValue.String) {
9531
- valueStr = `'${defValue.String.sval}'`;
9536
+ valueStr = quotes_1.QuoteUtils.escape(defValue.String.sval);
9532
9537
  }
9533
9538
  else {
9534
9539
  valueStr = this.visit(defValue, context);
@@ -9795,6 +9800,21 @@ class Deparser {
9795
9800
  }
9796
9801
  return output.join(' ');
9797
9802
  }
9803
+ quoteDefElemArg(arg, argValue) {
9804
+ if (arg && this.getNodeType(arg) === 'String') {
9805
+ return quotes_1.QuoteUtils.escape(this.getNodeData(arg).sval || '');
9806
+ }
9807
+ return typeof argValue === 'string' && !argValue.startsWith("'")
9808
+ ? quotes_1.QuoteUtils.escape(argValue)
9809
+ : argValue;
9810
+ }
9811
+ formatOptionValue(arg, argValue) {
9812
+ if (arg && this.getNodeType(arg) === 'String') {
9813
+ const sval = this.getNodeData(arg).sval || '';
9814
+ return /^[A-Za-z_][A-Za-z0-9_]*$/.test(sval) ? sval : quotes_1.QuoteUtils.escape(sval);
9815
+ }
9816
+ return argValue;
9817
+ }
9798
9818
  Var(node, context) {
9799
9819
  if (node.varno && node.varattno) {
9800
9820
  return `$${node.varno}.${node.varattno}`;
@@ -9834,7 +9854,7 @@ class Deparser {
9834
9854
  }
9835
9855
  if (node.rowexpr) {
9836
9856
  output.push(',');
9837
- output.push(`'${this.visit(node.rowexpr, context)}'`);
9857
+ output.push(this.visit(node.rowexpr, context));
9838
9858
  }
9839
9859
  if (node.colexprs && node.colexprs.length > 0) {
9840
9860
  output.push('COLUMNS');
@@ -9851,25 +9871,40 @@ class Deparser {
9851
9871
  if (node.lateral) {
9852
9872
  output.push('LATERAL');
9853
9873
  }
9854
- if (node.docexpr) {
9855
- output.push(this.visit(node.docexpr, context));
9874
+ const args = [];
9875
+ if (node.namespaces && node.namespaces.length > 0) {
9876
+ const namespaces = list_utils_1.ListUtils.unwrapList(node.namespaces).map(ns => {
9877
+ const target = this.getNodeData(ns);
9878
+ const uri = this.visit(target.val, context);
9879
+ return target.name ? `${uri} AS ${quotes_1.QuoteUtils.quoteIdentifier(target.name)}` : `DEFAULT ${uri}`;
9880
+ });
9881
+ args.push(`XMLNAMESPACES(${namespaces.join(', ')}),`);
9856
9882
  }
9857
9883
  if (node.rowexpr) {
9858
- output.push('PASSING');
9859
- output.push(this.visit(node.rowexpr, context));
9884
+ args.push(this.formatXmlTableExpr(node.rowexpr, context));
9885
+ }
9886
+ if (node.docexpr) {
9887
+ args.push('PASSING');
9888
+ args.push(this.formatXmlTableExpr(node.docexpr, context));
9860
9889
  }
9861
9890
  if (node.columns && node.columns.length > 0) {
9862
- output.push('COLUMNS');
9863
- const columns = list_utils_1.ListUtils.unwrapList(node.columns)
9891
+ args.push('COLUMNS');
9892
+ args.push(list_utils_1.ListUtils.unwrapList(node.columns)
9864
9893
  .map(col => this.visit(col, context))
9865
- .join(', ');
9866
- output.push(`(${columns})`);
9894
+ .join(', '));
9867
9895
  }
9896
+ output.push(`XMLTABLE(${args.join(' ')})`);
9868
9897
  if (node.alias) {
9869
9898
  output.push(this.Alias(node.alias, context));
9870
9899
  }
9871
9900
  return output.join(' ');
9872
9901
  }
9902
+ formatXmlTableExpr(expr, context) {
9903
+ const value = this.visit(expr, context);
9904
+ return ['A_Const', 'ColumnRef', 'ParamRef', 'FuncCall'].includes(this.getNodeType(expr))
9905
+ ? value
9906
+ : `(${value})`;
9907
+ }
9873
9908
  RangeTableFuncCol(node, context) {
9874
9909
  const output = [];
9875
9910
  if (node.colname) {
@@ -9883,12 +9918,15 @@ class Deparser {
9883
9918
  }
9884
9919
  if (node.colexpr) {
9885
9920
  output.push('PATH');
9886
- output.push(`'${this.visit(node.colexpr, context)}'`);
9921
+ output.push(this.visit(node.colexpr, context));
9887
9922
  }
9888
9923
  if (node.coldefexpr) {
9889
9924
  output.push('DEFAULT');
9890
9925
  output.push(this.visit(node.coldefexpr, context));
9891
9926
  }
9927
+ if (node.is_not_null) {
9928
+ output.push('NOT NULL');
9929
+ }
9892
9930
  return output.join(' ');
9893
9931
  }
9894
9932
  formatJsonFormat(format) {
package/esm/deparser.js CHANGED
@@ -129,6 +129,21 @@ function formatBitString(bsval) {
129
129
  }
130
130
  return `b'${quote(bsval)}'`;
131
131
  }
132
+ 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)*)?)$/;
133
+ function formatInteger(ival) {
134
+ const value = String(ival);
135
+ if (!/^[+-]?\d+$/.test(value)) {
136
+ throw new Error(`Invalid integer literal: ${value}`);
137
+ }
138
+ return value;
139
+ }
140
+ function formatNumeric(fval) {
141
+ const value = String(fval);
142
+ if (!NUMERIC_LITERAL.test(value)) {
143
+ throw new Error(`Invalid numeric literal: ${value}`);
144
+ }
145
+ return value;
146
+ }
132
147
  export class Deparser {
133
148
  tree;
134
149
  options;
@@ -1597,39 +1612,39 @@ export class Deparser {
1597
1612
  if (nodeAny.ival !== undefined) {
1598
1613
  if (typeof nodeAny.ival === 'object' && nodeAny.ival !== null) {
1599
1614
  if (nodeAny.ival.ival !== undefined) {
1600
- return nodeAny.ival.ival.toString();
1615
+ return formatInteger(nodeAny.ival.ival);
1601
1616
  }
1602
1617
  else if (Object.keys(nodeAny.ival).length === 0) {
1603
1618
  return '0';
1604
1619
  }
1605
1620
  else {
1606
- return nodeAny.ival.toString();
1621
+ return formatInteger(nodeAny.ival);
1607
1622
  }
1608
1623
  }
1609
1624
  else if (nodeAny.ival === null) {
1610
1625
  return 'NULL';
1611
1626
  }
1612
1627
  else {
1613
- return nodeAny.ival.toString();
1628
+ return formatInteger(nodeAny.ival);
1614
1629
  }
1615
1630
  }
1616
1631
  else if (nodeAny.fval !== undefined) {
1617
1632
  if (typeof nodeAny.fval === 'object' && nodeAny.fval !== null) {
1618
1633
  if (nodeAny.fval.fval !== undefined) {
1619
- return nodeAny.fval.fval.toString();
1634
+ return formatNumeric(nodeAny.fval.fval);
1620
1635
  }
1621
1636
  else if (Object.keys(nodeAny.fval).length === 0) {
1622
1637
  return '0.0';
1623
1638
  }
1624
1639
  else {
1625
- return nodeAny.fval.toString();
1640
+ return formatNumeric(nodeAny.fval);
1626
1641
  }
1627
1642
  }
1628
1643
  else if (nodeAny.fval === null) {
1629
1644
  return 'NULL';
1630
1645
  }
1631
1646
  else {
1632
- return nodeAny.fval.toString();
1647
+ return formatNumeric(nodeAny.fval);
1633
1648
  }
1634
1649
  }
1635
1650
  else if (nodeAny.sval !== undefined) {
@@ -1694,10 +1709,10 @@ export class Deparser {
1694
1709
  }
1695
1710
  if (nodeAny.val) {
1696
1711
  if (nodeAny.val.Integer?.ival !== undefined) {
1697
- return nodeAny.val.Integer.ival.toString();
1712
+ return formatInteger(nodeAny.val.Integer.ival);
1698
1713
  }
1699
1714
  else if (nodeAny.val.Float?.fval !== undefined) {
1700
- return nodeAny.val.Float.fval.toString();
1715
+ return formatNumeric(nodeAny.val.Float.fval);
1701
1716
  }
1702
1717
  else if (nodeAny.val.String?.sval !== undefined) {
1703
1718
  return QuoteUtils.escape(nodeAny.val.String.sval);
@@ -1718,15 +1733,15 @@ export class Deparser {
1718
1733
  }
1719
1734
  if (nodeAny.Integer !== undefined) {
1720
1735
  if (typeof nodeAny.Integer === 'object' && nodeAny.Integer.ival !== undefined) {
1721
- return nodeAny.Integer.ival.toString();
1736
+ return formatInteger(nodeAny.Integer.ival);
1722
1737
  }
1723
- return nodeAny.Integer.toString();
1738
+ return formatInteger(nodeAny.Integer);
1724
1739
  }
1725
1740
  if (nodeAny.Float !== undefined) {
1726
1741
  if (typeof nodeAny.Float === 'object' && nodeAny.Float.fval !== undefined) {
1727
- return nodeAny.Float.fval.toString();
1742
+ return formatNumeric(nodeAny.Float.fval);
1728
1743
  }
1729
- return nodeAny.Float.toString();
1744
+ return formatNumeric(nodeAny.Float);
1730
1745
  }
1731
1746
  if (nodeAny.String !== undefined) {
1732
1747
  if (typeof nodeAny.String === 'object' && nodeAny.String.sval !== undefined) {
@@ -2377,10 +2392,10 @@ export class Deparser {
2377
2392
  return QuoteUtils.quoteIdentifier(value);
2378
2393
  }
2379
2394
  Integer(node, context) {
2380
- return node.ival?.toString() || '0';
2395
+ return node.ival === undefined || node.ival === null ? '0' : formatInteger(node.ival);
2381
2396
  }
2382
2397
  Float(node, context) {
2383
- return node.fval || '0.0';
2398
+ return node.fval ? formatNumeric(node.fval) : '0.0';
2384
2399
  }
2385
2400
  Boolean(node, context) {
2386
2401
  return node.boolval ? 'true' : 'false';
@@ -3618,7 +3633,7 @@ export class Deparser {
3618
3633
  const opclassOpts = ListUtils.unwrapList(node.opclassopts).map(opt => {
3619
3634
  if (opt.DefElem && opt.DefElem.arg && this.getNodeType(opt.DefElem.arg) === 'String') {
3620
3635
  const stringData = this.getNodeData(opt.DefElem.arg);
3621
- return `${opt.DefElem.defname}='${stringData.sval}'`;
3636
+ return `${QuoteUtils.quoteIdentifierAfterDot(opt.DefElem.defname)}=${QuoteUtils.escape(stringData.sval)}`;
3622
3637
  }
3623
3638
  return this.visit(opt, context.spawn('IndexElem'));
3624
3639
  });
@@ -3919,19 +3934,19 @@ export class Deparser {
3919
3934
  case 'TRANS_STMT_PREPARE':
3920
3935
  output.push('PREPARE TRANSACTION');
3921
3936
  if (node.gid) {
3922
- output.push(`'${node.gid}'`);
3937
+ output.push(QuoteUtils.escape(node.gid));
3923
3938
  }
3924
3939
  break;
3925
3940
  case 'TRANS_STMT_COMMIT_PREPARED':
3926
3941
  output.push('COMMIT PREPARED');
3927
3942
  if (node.gid) {
3928
- output.push(`'${node.gid}'`);
3943
+ output.push(QuoteUtils.escape(node.gid));
3929
3944
  }
3930
3945
  break;
3931
3946
  case 'TRANS_STMT_ROLLBACK_PREPARED':
3932
3947
  output.push('ROLLBACK PREPARED');
3933
3948
  if (node.gid) {
3934
- output.push(`'${node.gid}'`);
3949
+ output.push(QuoteUtils.escape(node.gid));
3935
3950
  }
3936
3951
  break;
3937
3952
  default:
@@ -4007,8 +4022,8 @@ export class Deparser {
4007
4022
  const nodeData = this.getNodeData(arg);
4008
4023
  if (nodeData.sval !== undefined) {
4009
4024
  const svalValue = typeof nodeData.sval === 'object' ? nodeData.sval.sval : nodeData.sval;
4010
- if (svalValue === '' || svalValue.includes(' ') || svalValue.includes('-') || /[A-Z]/.test(svalValue) || /^\d/.test(svalValue) || svalValue.includes('.') || svalValue.includes('$') || svalValue.toLowerCase() === 'all' || /^[+-]\d/.test(svalValue)) {
4011
- return `'${svalValue}'`;
4025
+ 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)) {
4026
+ return QuoteUtils.escape(svalValue);
4012
4027
  }
4013
4028
  return svalValue;
4014
4029
  }
@@ -5535,9 +5550,9 @@ export class Deparser {
5535
5550
  if (context.parentNodeTypes.includes('IndexElem')) {
5536
5551
  if (node.arg && this.getNodeType(node.arg) === 'String') {
5537
5552
  const stringData = this.getNodeData(node.arg);
5538
- return `${node.defname}='${stringData.sval}'`;
5553
+ return `${QuoteUtils.quoteIdentifierAfterDot(node.defname)}=${QuoteUtils.escape(stringData.sval)}`;
5539
5554
  }
5540
- return `${node.defname}=${this.visit(node.arg, context.spawn('DefElem'))}`;
5555
+ return `${QuoteUtils.quoteIdentifierAfterDot(node.defname)}=${this.formatOptionValue(node.arg, this.visit(node.arg, context.spawn('DefElem')))}`;
5541
5556
  }
5542
5557
  // Handle CREATE OPERATOR boolean flags - MUST be first to preserve case
5543
5558
  if (context.parentNodeTypes.includes('DefineStmt') &&
@@ -5562,9 +5577,7 @@ export class Deparser {
5562
5577
  const defElemContext = context.spawn('DefElem');
5563
5578
  const argValue = this.visit(node.arg, defElemContext);
5564
5579
  if (context.parentNodeTypes.includes('CreateFdwStmt') || context.parentNodeTypes.includes('AlterFdwStmt')) {
5565
- const finalValue = typeof argValue === 'string' && !argValue.startsWith("'")
5566
- ? `'${argValue}'`
5567
- : argValue;
5580
+ const finalValue = this.quoteDefElemArg(node.arg, argValue);
5568
5581
  const quotedDefname = QuoteUtils.quoteIdentifier(node.defname);
5569
5582
  if (node.defaction === 'DEFELEM_ADD') {
5570
5583
  return `ADD ${quotedDefname} ${finalValue}`;
@@ -5577,9 +5590,7 @@ export class Deparser {
5577
5590
  }
5578
5591
  return `${quotedDefname} ${finalValue}`;
5579
5592
  }
5580
- const quotedValue = typeof argValue === 'string' && !argValue.startsWith("'")
5581
- ? `'${argValue}'`
5582
- : argValue;
5593
+ const quotedValue = this.quoteDefElemArg(node.arg, argValue);
5583
5594
  if (node.defaction === 'DEFELEM_ADD') {
5584
5595
  return `ADD ${node.defname} ${quotedValue}`;
5585
5596
  }
@@ -5611,9 +5622,7 @@ export class Deparser {
5611
5622
  }
5612
5623
  const defElemContext = context.spawn('DefElem');
5613
5624
  const argValue = this.visit(node.arg, defElemContext);
5614
- const quotedValue = typeof argValue === 'string' && !argValue.startsWith("'")
5615
- ? `'${argValue}'`
5616
- : argValue;
5625
+ const quotedValue = this.quoteDefElemArg(node.arg, argValue);
5617
5626
  return `PASSWORD ${quotedValue}`;
5618
5627
  }
5619
5628
  }
@@ -5678,9 +5687,7 @@ export class Deparser {
5678
5687
  }
5679
5688
  }
5680
5689
  if (node.defname === 'validUntil') {
5681
- const quotedValue = typeof argValue === 'string' && !argValue.startsWith("'")
5682
- ? `'${argValue}'`
5683
- : argValue;
5690
+ const quotedValue = this.quoteDefElemArg(node.arg, argValue);
5684
5691
  return `VALID UNTIL ${quotedValue}`;
5685
5692
  }
5686
5693
  if (node.defname === 'adminmembers') {
@@ -5860,9 +5867,7 @@ export class Deparser {
5860
5867
  if (context.parentNodeTypes.includes('AlterExtensionStmt')) {
5861
5868
  if (node.defname === 'new_version') {
5862
5869
  // argValue is unquoted due to DefElem context, so we need to quote it
5863
- const quotedValue = typeof argValue === 'string' && !argValue.startsWith("'")
5864
- ? `'${argValue}'`
5865
- : argValue;
5870
+ const quotedValue = this.quoteDefElemArg(node.arg, argValue);
5866
5871
  return `UPDATE TO ${quotedValue}`;
5867
5872
  }
5868
5873
  if (node.defname === 'schema') {
@@ -5887,15 +5892,15 @@ export class Deparser {
5887
5892
  }
5888
5893
  // Handle IndexStmt WITH clause options - no quotes, compact formatting
5889
5894
  if (context.parentNodeTypes.includes('IndexStmt')) {
5890
- return `${node.defname}=${argValue}`;
5895
+ return `${QuoteUtils.quoteIdentifierAfterDot(node.defname)}=${this.formatOptionValue(node.arg, argValue)}`;
5891
5896
  }
5892
5897
  // Handle IndexElem opclassopts - preserve string values as strings
5893
5898
  if (context.parentNodeTypes.includes('IndexElem')) {
5894
5899
  if (node.arg && this.getNodeType(node.arg) === 'String') {
5895
5900
  const stringData = this.getNodeData(node.arg);
5896
- return `${node.defname}='${stringData.sval}'`;
5901
+ return `${QuoteUtils.quoteIdentifierAfterDot(node.defname)}=${QuoteUtils.escape(stringData.sval)}`;
5897
5902
  }
5898
- return `${node.defname}=${argValue}`;
5903
+ return `${QuoteUtils.quoteIdentifierAfterDot(node.defname)}=${this.formatOptionValue(node.arg, argValue)}`;
5899
5904
  }
5900
5905
  // Handle CreateStmt table options - no quotes, compact formatting
5901
5906
  if (context.parentNodeTypes.includes('CreateStmt')) {
@@ -5905,9 +5910,9 @@ export class Deparser {
5905
5910
  // For numeric values, use the raw value without quotes
5906
5911
  if (node.arg && this.getNodeType(node.arg) === 'Integer') {
5907
5912
  const integerData = this.getNodeData(node.arg);
5908
- return `${relOptName}=${integerData.ival}`;
5913
+ return `${relOptName}=${formatInteger(integerData.ival)}`;
5909
5914
  }
5910
- return `${relOptName}=${argValue}`;
5915
+ return `${relOptName}=${this.formatOptionValue(node.arg, argValue)}`;
5911
5916
  }
5912
5917
  // Handle CreateEventTrigStmt WHEN clause - use IN syntax for List arguments
5913
5918
  if (context.parentNodeTypes.includes('CreateEventTrigStmt')) {
@@ -5917,7 +5922,7 @@ export class Deparser {
5917
5922
  const values = listItems.map(item => {
5918
5923
  if (this.getNodeType(item) === 'String') {
5919
5924
  const stringData = this.getNodeData(item);
5920
- return `'${stringData.sval || ''}'`;
5925
+ return QuoteUtils.escape(stringData.sval || '');
5921
5926
  }
5922
5927
  return this.visit(item, context);
5923
5928
  });
@@ -5932,7 +5937,7 @@ export class Deparser {
5932
5937
  if (node.arg && this.getNodeType(node.arg) === 'TypeName') {
5933
5938
  return `${optionName} = ${argValue}`;
5934
5939
  }
5935
- return `${optionName} = ${argValue}`;
5940
+ return `${optionName} = ${this.formatOptionValue(node.arg, argValue)}`;
5936
5941
  }
5937
5942
  // Handle ViewStmt WITH options - don't quote numeric values
5938
5943
  if (context.parentNodeTypes.includes('ViewStmt')) {
@@ -6005,7 +6010,7 @@ export class Deparser {
6005
6010
  return `${quotedDefname} = ${stringData.sval}`;
6006
6011
  }
6007
6012
  // Regular string literals get single quotes
6008
- return `${quotedDefname} = '${stringData.sval}'`;
6013
+ return `${quotedDefname} = ${QuoteUtils.escape(stringData.sval)}`;
6009
6014
  }
6010
6015
  return `${quotedDefname} = ${argValue}`;
6011
6016
  }
@@ -6026,7 +6031,7 @@ export class Deparser {
6026
6031
  return `${node.defname} = ${stringData.sval}`;
6027
6032
  }
6028
6033
  // Regular string literals get single quotes
6029
- return `${node.defname} = '${stringData.sval}'`;
6034
+ return `${node.defname} = ${QuoteUtils.escape(stringData.sval)}`;
6030
6035
  }
6031
6036
  if (node.arg && this.getNodeType(node.arg) === 'Boolean') {
6032
6037
  const boolData = this.getNodeData(node.arg);
@@ -6076,7 +6081,7 @@ export class Deparser {
6076
6081
  }
6077
6082
  if (node.location) {
6078
6083
  output.push('LOCATION');
6079
- output.push(`'${node.location}'`);
6084
+ output.push(QuoteUtils.escape(node.location));
6080
6085
  }
6081
6086
  if (node.options && node.options.length > 0) {
6082
6087
  output.push('WITH');
@@ -6409,7 +6414,7 @@ export class Deparser {
6409
6414
  }
6410
6415
  if (node.payload !== null && node.payload !== undefined) {
6411
6416
  output.push(',');
6412
- output.push(`'${node.payload}'`);
6417
+ output.push(QuoteUtils.escape(node.payload));
6413
6418
  }
6414
6419
  return output.join(' ');
6415
6420
  }
@@ -6437,7 +6442,7 @@ export class Deparser {
6437
6442
  if (!node.filename) {
6438
6443
  throw new Error('LoadStmt requires filename');
6439
6444
  }
6440
- return `LOAD '${node.filename}'`;
6445
+ return `LOAD ${QuoteUtils.escape(node.filename)}`;
6441
6446
  }
6442
6447
  DiscardStmt(node, context) {
6443
6448
  switch (node.target) {
@@ -6924,7 +6929,7 @@ export class Deparser {
6924
6929
  }
6925
6930
  output.push('CONNECTION');
6926
6931
  if (node.conninfo) {
6927
- output.push(`'${node.conninfo}'`);
6932
+ output.push(QuoteUtils.escape(node.conninfo));
6928
6933
  }
6929
6934
  output.push('PUBLICATION');
6930
6935
  if (node.publication && node.publication.length > 0) {
@@ -8207,7 +8212,7 @@ export class Deparser {
8207
8212
  }
8208
8213
  output.push('IS');
8209
8214
  if (node.label) {
8210
- output.push(`'${node.label}'`);
8215
+ output.push(QuoteUtils.escape(node.label));
8211
8216
  }
8212
8217
  else {
8213
8218
  output.push('NULL');
@@ -8264,10 +8269,10 @@ export class Deparser {
8264
8269
  output.push(conversionName);
8265
8270
  }
8266
8271
  if (node.for_encoding_name) {
8267
- output.push('FOR', `'${node.for_encoding_name}'`);
8272
+ output.push('FOR', QuoteUtils.escape(node.for_encoding_name));
8268
8273
  }
8269
8274
  if (node.to_encoding_name) {
8270
- output.push('TO', `'${node.to_encoding_name}'`);
8275
+ output.push('TO', QuoteUtils.escape(node.to_encoding_name));
8271
8276
  }
8272
8277
  if (node.func_name && node.func_name.length > 0) {
8273
8278
  output.push('FROM');
@@ -9402,7 +9407,7 @@ export class Deparser {
9402
9407
  const preservedDefName = quotedDefName !== defName ? quotedDefName : defName;
9403
9408
  // Handle String arguments with single quotes for string literals
9404
9409
  if (defValue.String) {
9405
- return `${preservedDefName} = '${defValue.String.sval}'`;
9410
+ return `${preservedDefName} = ${QuoteUtils.escape(defValue.String.sval)}`;
9406
9411
  }
9407
9412
  return `${preservedDefName} = ${this.visit(defValue, context)}`;
9408
9413
  }
@@ -9525,7 +9530,7 @@ export class Deparser {
9525
9530
  // For CREATE COLLATION, ensure String nodes are quoted as string literals
9526
9531
  let valueStr;
9527
9532
  if (defValue.String) {
9528
- valueStr = `'${defValue.String.sval}'`;
9533
+ valueStr = QuoteUtils.escape(defValue.String.sval);
9529
9534
  }
9530
9535
  else {
9531
9536
  valueStr = this.visit(defValue, context);
@@ -9792,6 +9797,21 @@ export class Deparser {
9792
9797
  }
9793
9798
  return output.join(' ');
9794
9799
  }
9800
+ quoteDefElemArg(arg, argValue) {
9801
+ if (arg && this.getNodeType(arg) === 'String') {
9802
+ return QuoteUtils.escape(this.getNodeData(arg).sval || '');
9803
+ }
9804
+ return typeof argValue === 'string' && !argValue.startsWith("'")
9805
+ ? QuoteUtils.escape(argValue)
9806
+ : argValue;
9807
+ }
9808
+ formatOptionValue(arg, argValue) {
9809
+ if (arg && this.getNodeType(arg) === 'String') {
9810
+ const sval = this.getNodeData(arg).sval || '';
9811
+ return /^[A-Za-z_][A-Za-z0-9_]*$/.test(sval) ? sval : QuoteUtils.escape(sval);
9812
+ }
9813
+ return argValue;
9814
+ }
9795
9815
  Var(node, context) {
9796
9816
  if (node.varno && node.varattno) {
9797
9817
  return `$${node.varno}.${node.varattno}`;
@@ -9831,7 +9851,7 @@ export class Deparser {
9831
9851
  }
9832
9852
  if (node.rowexpr) {
9833
9853
  output.push(',');
9834
- output.push(`'${this.visit(node.rowexpr, context)}'`);
9854
+ output.push(this.visit(node.rowexpr, context));
9835
9855
  }
9836
9856
  if (node.colexprs && node.colexprs.length > 0) {
9837
9857
  output.push('COLUMNS');
@@ -9848,25 +9868,40 @@ export class Deparser {
9848
9868
  if (node.lateral) {
9849
9869
  output.push('LATERAL');
9850
9870
  }
9851
- if (node.docexpr) {
9852
- output.push(this.visit(node.docexpr, context));
9871
+ const args = [];
9872
+ if (node.namespaces && node.namespaces.length > 0) {
9873
+ const namespaces = ListUtils.unwrapList(node.namespaces).map(ns => {
9874
+ const target = this.getNodeData(ns);
9875
+ const uri = this.visit(target.val, context);
9876
+ return target.name ? `${uri} AS ${QuoteUtils.quoteIdentifier(target.name)}` : `DEFAULT ${uri}`;
9877
+ });
9878
+ args.push(`XMLNAMESPACES(${namespaces.join(', ')}),`);
9853
9879
  }
9854
9880
  if (node.rowexpr) {
9855
- output.push('PASSING');
9856
- output.push(this.visit(node.rowexpr, context));
9881
+ args.push(this.formatXmlTableExpr(node.rowexpr, context));
9882
+ }
9883
+ if (node.docexpr) {
9884
+ args.push('PASSING');
9885
+ args.push(this.formatXmlTableExpr(node.docexpr, context));
9857
9886
  }
9858
9887
  if (node.columns && node.columns.length > 0) {
9859
- output.push('COLUMNS');
9860
- const columns = ListUtils.unwrapList(node.columns)
9888
+ args.push('COLUMNS');
9889
+ args.push(ListUtils.unwrapList(node.columns)
9861
9890
  .map(col => this.visit(col, context))
9862
- .join(', ');
9863
- output.push(`(${columns})`);
9891
+ .join(', '));
9864
9892
  }
9893
+ output.push(`XMLTABLE(${args.join(' ')})`);
9865
9894
  if (node.alias) {
9866
9895
  output.push(this.Alias(node.alias, context));
9867
9896
  }
9868
9897
  return output.join(' ');
9869
9898
  }
9899
+ formatXmlTableExpr(expr, context) {
9900
+ const value = this.visit(expr, context);
9901
+ return ['A_Const', 'ColumnRef', 'ParamRef', 'FuncCall'].includes(this.getNodeType(expr))
9902
+ ? value
9903
+ : `(${value})`;
9904
+ }
9870
9905
  RangeTableFuncCol(node, context) {
9871
9906
  const output = [];
9872
9907
  if (node.colname) {
@@ -9880,12 +9915,15 @@ export class Deparser {
9880
9915
  }
9881
9916
  if (node.colexpr) {
9882
9917
  output.push('PATH');
9883
- output.push(`'${this.visit(node.colexpr, context)}'`);
9918
+ output.push(this.visit(node.colexpr, context));
9884
9919
  }
9885
9920
  if (node.coldefexpr) {
9886
9921
  output.push('DEFAULT');
9887
9922
  output.push(this.visit(node.coldefexpr, context));
9888
9923
  }
9924
+ if (node.is_not_null) {
9925
+ output.push('NOT NULL');
9926
+ }
9889
9927
  return output.join(' ');
9890
9928
  }
9891
9929
  formatJsonFormat(format) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pgsql-deparser",
3
- "version": "18.3.9",
3
+ "version": "18.3.10",
4
4
  "author": "Constructive <developers@constructive.io>",
5
5
  "description": "PostgreSQL AST Deparser",
6
6
  "main": "index.js",
@@ -60,5 +60,5 @@
60
60
  "@pgsql/quotes": "18.2.4",
61
61
  "@pgsql/types": "^18.0.0"
62
62
  },
63
- "gitHead": "506aa3b63613eea04b78afc1ac440bc84b8a07ad"
63
+ "gitHead": "24882cc67810aaa1af8dd241594634cf54105126"
64
64
  }