pgsql-deparser 18.3.8 → 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 +3 -25
- package/deparser.js +117 -93
- package/esm/deparser.js +117 -93
- package/package.json +2 -2
package/deparser.d.ts
CHANGED
|
@@ -8,31 +8,6 @@ export interface DeparserOptions {
|
|
|
8
8
|
functionDelimiterFallback?: string;
|
|
9
9
|
pretty?: boolean;
|
|
10
10
|
}
|
|
11
|
-
/**
|
|
12
|
-
* Deparser - Converts PostgreSQL AST nodes back to SQL strings
|
|
13
|
-
*
|
|
14
|
-
* Entry Points:
|
|
15
|
-
* 1. ParseResult (from libpg-query) - The complete parse result
|
|
16
|
-
* Structure: { version: number, stmts: RawStmt[] }
|
|
17
|
-
* Note: stmts is "repeated RawStmt" in protobuf, so array contains RawStmt
|
|
18
|
-
* objects inline (not wrapped as { RawStmt: ... } nodes)
|
|
19
|
-
* Example: { version: 170004, stmts: [{ stmt: {...}, stmt_len: 32 }] }
|
|
20
|
-
*
|
|
21
|
-
* 2. Wrapped ParseResult - When explicitly wrapped as a Node
|
|
22
|
-
* Structure: { ParseResult: { version: number, stmts: RawStmt[] } }
|
|
23
|
-
*
|
|
24
|
-
* 3. Wrapped RawStmt - When explicitly wrapped as a Node
|
|
25
|
-
* Structure: { RawStmt: { stmt: Node, stmt_len?: number } }
|
|
26
|
-
*
|
|
27
|
-
* 4. Array of Nodes - Multiple statements to deparse
|
|
28
|
-
* Can be: Node[] (e.g., SelectStmt, InsertStmt, etc.)
|
|
29
|
-
*
|
|
30
|
-
* 5. Single Node - Individual statement node
|
|
31
|
-
* Example: { SelectStmt: {...} }, { InsertStmt: {...} }, etc.
|
|
32
|
-
*
|
|
33
|
-
* The deparser automatically detects bare ParseResult objects for backward
|
|
34
|
-
* compatibility and wraps them internally for consistent processing.
|
|
35
|
-
*/
|
|
36
11
|
export declare class Deparser implements DeparserVisitor {
|
|
37
12
|
private tree;
|
|
38
13
|
private options;
|
|
@@ -285,9 +260,12 @@ export declare class Deparser implements DeparserVisitor {
|
|
|
285
260
|
OnConflictExpr(node: t.OnConflictExpr, context: DeparserContext): string;
|
|
286
261
|
ScanToken(node: t.ScanToken, context: DeparserContext): string;
|
|
287
262
|
CreateOpClassItem(node: t.CreateOpClassItem, context: DeparserContext): string;
|
|
263
|
+
private quoteDefElemArg;
|
|
264
|
+
private formatOptionValue;
|
|
288
265
|
Var(node: t.Var, context: DeparserContext): string;
|
|
289
266
|
TableFunc(node: t.TableFunc, context: DeparserContext): string;
|
|
290
267
|
RangeTableFunc(node: t.RangeTableFunc, context: DeparserContext): string;
|
|
268
|
+
private formatXmlTableExpr;
|
|
291
269
|
RangeTableFuncCol(node: t.RangeTableFuncCol, context: DeparserContext): string;
|
|
292
270
|
formatJsonFormat(format: t.JsonFormat | undefined): string | null;
|
|
293
271
|
formatJsonOutput(output: t.JsonOutput | undefined, context: DeparserContext): string | null;
|
package/deparser.js
CHANGED
|
@@ -122,6 +122,31 @@ function isWrappedParseResult(obj) {
|
|
|
122
122
|
* The deparser automatically detects bare ParseResult objects for backward
|
|
123
123
|
* compatibility and wraps them internally for consistent processing.
|
|
124
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
|
+
}
|
|
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
|
+
}
|
|
125
150
|
class Deparser {
|
|
126
151
|
tree;
|
|
127
152
|
options;
|
|
@@ -1590,39 +1615,39 @@ class Deparser {
|
|
|
1590
1615
|
if (nodeAny.ival !== undefined) {
|
|
1591
1616
|
if (typeof nodeAny.ival === 'object' && nodeAny.ival !== null) {
|
|
1592
1617
|
if (nodeAny.ival.ival !== undefined) {
|
|
1593
|
-
return nodeAny.ival.ival
|
|
1618
|
+
return formatInteger(nodeAny.ival.ival);
|
|
1594
1619
|
}
|
|
1595
1620
|
else if (Object.keys(nodeAny.ival).length === 0) {
|
|
1596
1621
|
return '0';
|
|
1597
1622
|
}
|
|
1598
1623
|
else {
|
|
1599
|
-
return nodeAny.ival
|
|
1624
|
+
return formatInteger(nodeAny.ival);
|
|
1600
1625
|
}
|
|
1601
1626
|
}
|
|
1602
1627
|
else if (nodeAny.ival === null) {
|
|
1603
1628
|
return 'NULL';
|
|
1604
1629
|
}
|
|
1605
1630
|
else {
|
|
1606
|
-
return nodeAny.ival
|
|
1631
|
+
return formatInteger(nodeAny.ival);
|
|
1607
1632
|
}
|
|
1608
1633
|
}
|
|
1609
1634
|
else if (nodeAny.fval !== undefined) {
|
|
1610
1635
|
if (typeof nodeAny.fval === 'object' && nodeAny.fval !== null) {
|
|
1611
1636
|
if (nodeAny.fval.fval !== undefined) {
|
|
1612
|
-
return nodeAny.fval.fval
|
|
1637
|
+
return formatNumeric(nodeAny.fval.fval);
|
|
1613
1638
|
}
|
|
1614
1639
|
else if (Object.keys(nodeAny.fval).length === 0) {
|
|
1615
1640
|
return '0.0';
|
|
1616
1641
|
}
|
|
1617
1642
|
else {
|
|
1618
|
-
return nodeAny.fval
|
|
1643
|
+
return formatNumeric(nodeAny.fval);
|
|
1619
1644
|
}
|
|
1620
1645
|
}
|
|
1621
1646
|
else if (nodeAny.fval === null) {
|
|
1622
1647
|
return 'NULL';
|
|
1623
1648
|
}
|
|
1624
1649
|
else {
|
|
1625
|
-
return nodeAny.fval
|
|
1650
|
+
return formatNumeric(nodeAny.fval);
|
|
1626
1651
|
}
|
|
1627
1652
|
}
|
|
1628
1653
|
else if (nodeAny.sval !== undefined) {
|
|
@@ -1669,15 +1694,7 @@ class Deparser {
|
|
|
1669
1694
|
else if (nodeAny.bsval !== undefined) {
|
|
1670
1695
|
if (typeof nodeAny.bsval === 'object' && nodeAny.bsval !== null) {
|
|
1671
1696
|
if (nodeAny.bsval.bsval !== undefined) {
|
|
1672
|
-
|
|
1673
|
-
// Check if this is a hexadecimal bit string (starts with x and contains only hex digits)
|
|
1674
|
-
if (bsval.startsWith('x') && /^x[0-9A-Fa-f]+$/.test(bsval)) {
|
|
1675
|
-
return `x'${bsval.substring(1)}'`;
|
|
1676
|
-
}
|
|
1677
|
-
if (bsval.startsWith('b')) {
|
|
1678
|
-
return `b'${bsval.substring(1)}'`;
|
|
1679
|
-
}
|
|
1680
|
-
return `b'${bsval}'`;
|
|
1697
|
+
return formatBitString(nodeAny.bsval.bsval);
|
|
1681
1698
|
}
|
|
1682
1699
|
else if (Object.keys(nodeAny.bsval).length === 0) {
|
|
1683
1700
|
return "''";
|
|
@@ -1690,23 +1707,15 @@ class Deparser {
|
|
|
1690
1707
|
return 'NULL';
|
|
1691
1708
|
}
|
|
1692
1709
|
else {
|
|
1693
|
-
|
|
1694
|
-
// Check if this is a hexadecimal bit string (starts with x and contains only hex digits)
|
|
1695
|
-
if (bsval.startsWith('x') && /^x[0-9A-Fa-f]+$/.test(bsval)) {
|
|
1696
|
-
return `x'${bsval.substring(1)}'`;
|
|
1697
|
-
}
|
|
1698
|
-
if (bsval.startsWith('b')) {
|
|
1699
|
-
return `b'${bsval.substring(1)}'`;
|
|
1700
|
-
}
|
|
1701
|
-
return `b'${bsval}'`;
|
|
1710
|
+
return formatBitString(nodeAny.bsval);
|
|
1702
1711
|
}
|
|
1703
1712
|
}
|
|
1704
1713
|
if (nodeAny.val) {
|
|
1705
1714
|
if (nodeAny.val.Integer?.ival !== undefined) {
|
|
1706
|
-
return nodeAny.val.Integer.ival
|
|
1715
|
+
return formatInteger(nodeAny.val.Integer.ival);
|
|
1707
1716
|
}
|
|
1708
1717
|
else if (nodeAny.val.Float?.fval !== undefined) {
|
|
1709
|
-
return nodeAny.val.Float.fval
|
|
1718
|
+
return formatNumeric(nodeAny.val.Float.fval);
|
|
1710
1719
|
}
|
|
1711
1720
|
else if (nodeAny.val.String?.sval !== undefined) {
|
|
1712
1721
|
return quotes_1.QuoteUtils.escape(nodeAny.val.String.sval);
|
|
@@ -1715,7 +1724,7 @@ class Deparser {
|
|
|
1715
1724
|
return nodeAny.val.Boolean.boolval ? 'true' : 'false';
|
|
1716
1725
|
}
|
|
1717
1726
|
else if (nodeAny.val.BitString?.bsval !== undefined) {
|
|
1718
|
-
return nodeAny.val.BitString.bsval;
|
|
1727
|
+
return formatBitString(nodeAny.val.BitString.bsval);
|
|
1719
1728
|
}
|
|
1720
1729
|
}
|
|
1721
1730
|
if (nodeAny.isnull === true) {
|
|
@@ -1727,15 +1736,15 @@ class Deparser {
|
|
|
1727
1736
|
}
|
|
1728
1737
|
if (nodeAny.Integer !== undefined) {
|
|
1729
1738
|
if (typeof nodeAny.Integer === 'object' && nodeAny.Integer.ival !== undefined) {
|
|
1730
|
-
return nodeAny.Integer.ival
|
|
1739
|
+
return formatInteger(nodeAny.Integer.ival);
|
|
1731
1740
|
}
|
|
1732
|
-
return nodeAny.Integer
|
|
1741
|
+
return formatInteger(nodeAny.Integer);
|
|
1733
1742
|
}
|
|
1734
1743
|
if (nodeAny.Float !== undefined) {
|
|
1735
1744
|
if (typeof nodeAny.Float === 'object' && nodeAny.Float.fval !== undefined) {
|
|
1736
|
-
return nodeAny.Float.fval
|
|
1745
|
+
return formatNumeric(nodeAny.Float.fval);
|
|
1737
1746
|
}
|
|
1738
|
-
return nodeAny.Float
|
|
1747
|
+
return formatNumeric(nodeAny.Float);
|
|
1739
1748
|
}
|
|
1740
1749
|
if (nodeAny.String !== undefined) {
|
|
1741
1750
|
if (typeof nodeAny.String === 'object' && nodeAny.String.sval !== undefined) {
|
|
@@ -2386,24 +2395,16 @@ class Deparser {
|
|
|
2386
2395
|
return quotes_1.QuoteUtils.quoteIdentifier(value);
|
|
2387
2396
|
}
|
|
2388
2397
|
Integer(node, context) {
|
|
2389
|
-
return node.ival
|
|
2398
|
+
return node.ival === undefined || node.ival === null ? '0' : formatInteger(node.ival);
|
|
2390
2399
|
}
|
|
2391
2400
|
Float(node, context) {
|
|
2392
|
-
return node.fval
|
|
2401
|
+
return node.fval ? formatNumeric(node.fval) : '0.0';
|
|
2393
2402
|
}
|
|
2394
2403
|
Boolean(node, context) {
|
|
2395
2404
|
return node.boolval ? 'true' : 'false';
|
|
2396
2405
|
}
|
|
2397
2406
|
BitString(node, context) {
|
|
2398
|
-
|
|
2399
|
-
if (node.bsval.startsWith('x')) {
|
|
2400
|
-
return `x'${node.bsval.substring(1)}'`;
|
|
2401
|
-
}
|
|
2402
|
-
if (node.bsval.startsWith('b')) {
|
|
2403
|
-
return `b'${node.bsval.substring(1)}'`;
|
|
2404
|
-
}
|
|
2405
|
-
// Fallback for raw values without prefix
|
|
2406
|
-
return `b'${node.bsval}'`;
|
|
2407
|
+
return formatBitString(node.bsval);
|
|
2407
2408
|
}
|
|
2408
2409
|
Null(node, context) {
|
|
2409
2410
|
return 'NULL';
|
|
@@ -3635,7 +3636,7 @@ class Deparser {
|
|
|
3635
3636
|
const opclassOpts = list_utils_1.ListUtils.unwrapList(node.opclassopts).map(opt => {
|
|
3636
3637
|
if (opt.DefElem && opt.DefElem.arg && this.getNodeType(opt.DefElem.arg) === 'String') {
|
|
3637
3638
|
const stringData = this.getNodeData(opt.DefElem.arg);
|
|
3638
|
-
return `${opt.DefElem.defname}
|
|
3639
|
+
return `${quotes_1.QuoteUtils.quoteIdentifierAfterDot(opt.DefElem.defname)}=${quotes_1.QuoteUtils.escape(stringData.sval)}`;
|
|
3639
3640
|
}
|
|
3640
3641
|
return this.visit(opt, context.spawn('IndexElem'));
|
|
3641
3642
|
});
|
|
@@ -3936,19 +3937,19 @@ class Deparser {
|
|
|
3936
3937
|
case 'TRANS_STMT_PREPARE':
|
|
3937
3938
|
output.push('PREPARE TRANSACTION');
|
|
3938
3939
|
if (node.gid) {
|
|
3939
|
-
output.push(
|
|
3940
|
+
output.push(quotes_1.QuoteUtils.escape(node.gid));
|
|
3940
3941
|
}
|
|
3941
3942
|
break;
|
|
3942
3943
|
case 'TRANS_STMT_COMMIT_PREPARED':
|
|
3943
3944
|
output.push('COMMIT PREPARED');
|
|
3944
3945
|
if (node.gid) {
|
|
3945
|
-
output.push(
|
|
3946
|
+
output.push(quotes_1.QuoteUtils.escape(node.gid));
|
|
3946
3947
|
}
|
|
3947
3948
|
break;
|
|
3948
3949
|
case 'TRANS_STMT_ROLLBACK_PREPARED':
|
|
3949
3950
|
output.push('ROLLBACK PREPARED');
|
|
3950
3951
|
if (node.gid) {
|
|
3951
|
-
output.push(
|
|
3952
|
+
output.push(quotes_1.QuoteUtils.escape(node.gid));
|
|
3952
3953
|
}
|
|
3953
3954
|
break;
|
|
3954
3955
|
default:
|
|
@@ -4024,8 +4025,8 @@ class Deparser {
|
|
|
4024
4025
|
const nodeData = this.getNodeData(arg);
|
|
4025
4026
|
if (nodeData.sval !== undefined) {
|
|
4026
4027
|
const svalValue = typeof nodeData.sval === 'object' ? nodeData.sval.sval : nodeData.sval;
|
|
4027
|
-
if (svalValue
|
|
4028
|
-
return
|
|
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);
|
|
4029
4030
|
}
|
|
4030
4031
|
return svalValue;
|
|
4031
4032
|
}
|
|
@@ -5552,9 +5553,9 @@ class Deparser {
|
|
|
5552
5553
|
if (context.parentNodeTypes.includes('IndexElem')) {
|
|
5553
5554
|
if (node.arg && this.getNodeType(node.arg) === 'String') {
|
|
5554
5555
|
const stringData = this.getNodeData(node.arg);
|
|
5555
|
-
return `${node.defname}
|
|
5556
|
+
return `${quotes_1.QuoteUtils.quoteIdentifierAfterDot(node.defname)}=${quotes_1.QuoteUtils.escape(stringData.sval)}`;
|
|
5556
5557
|
}
|
|
5557
|
-
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')))}`;
|
|
5558
5559
|
}
|
|
5559
5560
|
// Handle CREATE OPERATOR boolean flags - MUST be first to preserve case
|
|
5560
5561
|
if (context.parentNodeTypes.includes('DefineStmt') &&
|
|
@@ -5579,9 +5580,7 @@ class Deparser {
|
|
|
5579
5580
|
const defElemContext = context.spawn('DefElem');
|
|
5580
5581
|
const argValue = this.visit(node.arg, defElemContext);
|
|
5581
5582
|
if (context.parentNodeTypes.includes('CreateFdwStmt') || context.parentNodeTypes.includes('AlterFdwStmt')) {
|
|
5582
|
-
const finalValue =
|
|
5583
|
-
? `'${argValue}'`
|
|
5584
|
-
: argValue;
|
|
5583
|
+
const finalValue = this.quoteDefElemArg(node.arg, argValue);
|
|
5585
5584
|
const quotedDefname = quotes_1.QuoteUtils.quoteIdentifier(node.defname);
|
|
5586
5585
|
if (node.defaction === 'DEFELEM_ADD') {
|
|
5587
5586
|
return `ADD ${quotedDefname} ${finalValue}`;
|
|
@@ -5594,9 +5593,7 @@ class Deparser {
|
|
|
5594
5593
|
}
|
|
5595
5594
|
return `${quotedDefname} ${finalValue}`;
|
|
5596
5595
|
}
|
|
5597
|
-
const quotedValue =
|
|
5598
|
-
? `'${argValue}'`
|
|
5599
|
-
: argValue;
|
|
5596
|
+
const quotedValue = this.quoteDefElemArg(node.arg, argValue);
|
|
5600
5597
|
if (node.defaction === 'DEFELEM_ADD') {
|
|
5601
5598
|
return `ADD ${node.defname} ${quotedValue}`;
|
|
5602
5599
|
}
|
|
@@ -5628,9 +5625,7 @@ class Deparser {
|
|
|
5628
5625
|
}
|
|
5629
5626
|
const defElemContext = context.spawn('DefElem');
|
|
5630
5627
|
const argValue = this.visit(node.arg, defElemContext);
|
|
5631
|
-
const quotedValue =
|
|
5632
|
-
? `'${argValue}'`
|
|
5633
|
-
: argValue;
|
|
5628
|
+
const quotedValue = this.quoteDefElemArg(node.arg, argValue);
|
|
5634
5629
|
return `PASSWORD ${quotedValue}`;
|
|
5635
5630
|
}
|
|
5636
5631
|
}
|
|
@@ -5695,9 +5690,7 @@ class Deparser {
|
|
|
5695
5690
|
}
|
|
5696
5691
|
}
|
|
5697
5692
|
if (node.defname === 'validUntil') {
|
|
5698
|
-
const quotedValue =
|
|
5699
|
-
? `'${argValue}'`
|
|
5700
|
-
: argValue;
|
|
5693
|
+
const quotedValue = this.quoteDefElemArg(node.arg, argValue);
|
|
5701
5694
|
return `VALID UNTIL ${quotedValue}`;
|
|
5702
5695
|
}
|
|
5703
5696
|
if (node.defname === 'adminmembers') {
|
|
@@ -5877,9 +5870,7 @@ class Deparser {
|
|
|
5877
5870
|
if (context.parentNodeTypes.includes('AlterExtensionStmt')) {
|
|
5878
5871
|
if (node.defname === 'new_version') {
|
|
5879
5872
|
// argValue is unquoted due to DefElem context, so we need to quote it
|
|
5880
|
-
const quotedValue =
|
|
5881
|
-
? `'${argValue}'`
|
|
5882
|
-
: argValue;
|
|
5873
|
+
const quotedValue = this.quoteDefElemArg(node.arg, argValue);
|
|
5883
5874
|
return `UPDATE TO ${quotedValue}`;
|
|
5884
5875
|
}
|
|
5885
5876
|
if (node.defname === 'schema') {
|
|
@@ -5904,15 +5895,15 @@ class Deparser {
|
|
|
5904
5895
|
}
|
|
5905
5896
|
// Handle IndexStmt WITH clause options - no quotes, compact formatting
|
|
5906
5897
|
if (context.parentNodeTypes.includes('IndexStmt')) {
|
|
5907
|
-
return `${node.defname}=${argValue}`;
|
|
5898
|
+
return `${quotes_1.QuoteUtils.quoteIdentifierAfterDot(node.defname)}=${this.formatOptionValue(node.arg, argValue)}`;
|
|
5908
5899
|
}
|
|
5909
5900
|
// Handle IndexElem opclassopts - preserve string values as strings
|
|
5910
5901
|
if (context.parentNodeTypes.includes('IndexElem')) {
|
|
5911
5902
|
if (node.arg && this.getNodeType(node.arg) === 'String') {
|
|
5912
5903
|
const stringData = this.getNodeData(node.arg);
|
|
5913
|
-
return `${node.defname}
|
|
5904
|
+
return `${quotes_1.QuoteUtils.quoteIdentifierAfterDot(node.defname)}=${quotes_1.QuoteUtils.escape(stringData.sval)}`;
|
|
5914
5905
|
}
|
|
5915
|
-
return `${node.defname}=${argValue}`;
|
|
5906
|
+
return `${quotes_1.QuoteUtils.quoteIdentifierAfterDot(node.defname)}=${this.formatOptionValue(node.arg, argValue)}`;
|
|
5916
5907
|
}
|
|
5917
5908
|
// Handle CreateStmt table options - no quotes, compact formatting
|
|
5918
5909
|
if (context.parentNodeTypes.includes('CreateStmt')) {
|
|
@@ -5922,9 +5913,9 @@ class Deparser {
|
|
|
5922
5913
|
// For numeric values, use the raw value without quotes
|
|
5923
5914
|
if (node.arg && this.getNodeType(node.arg) === 'Integer') {
|
|
5924
5915
|
const integerData = this.getNodeData(node.arg);
|
|
5925
|
-
return `${relOptName}=${integerData.ival}`;
|
|
5916
|
+
return `${relOptName}=${formatInteger(integerData.ival)}`;
|
|
5926
5917
|
}
|
|
5927
|
-
return `${relOptName}=${argValue}`;
|
|
5918
|
+
return `${relOptName}=${this.formatOptionValue(node.arg, argValue)}`;
|
|
5928
5919
|
}
|
|
5929
5920
|
// Handle CreateEventTrigStmt WHEN clause - use IN syntax for List arguments
|
|
5930
5921
|
if (context.parentNodeTypes.includes('CreateEventTrigStmt')) {
|
|
@@ -5934,7 +5925,7 @@ class Deparser {
|
|
|
5934
5925
|
const values = listItems.map(item => {
|
|
5935
5926
|
if (this.getNodeType(item) === 'String') {
|
|
5936
5927
|
const stringData = this.getNodeData(item);
|
|
5937
|
-
return
|
|
5928
|
+
return quotes_1.QuoteUtils.escape(stringData.sval || '');
|
|
5938
5929
|
}
|
|
5939
5930
|
return this.visit(item, context);
|
|
5940
5931
|
});
|
|
@@ -5949,7 +5940,7 @@ class Deparser {
|
|
|
5949
5940
|
if (node.arg && this.getNodeType(node.arg) === 'TypeName') {
|
|
5950
5941
|
return `${optionName} = ${argValue}`;
|
|
5951
5942
|
}
|
|
5952
|
-
return `${optionName} = ${argValue}`;
|
|
5943
|
+
return `${optionName} = ${this.formatOptionValue(node.arg, argValue)}`;
|
|
5953
5944
|
}
|
|
5954
5945
|
// Handle ViewStmt WITH options - don't quote numeric values
|
|
5955
5946
|
if (context.parentNodeTypes.includes('ViewStmt')) {
|
|
@@ -6022,7 +6013,7 @@ class Deparser {
|
|
|
6022
6013
|
return `${quotedDefname} = ${stringData.sval}`;
|
|
6023
6014
|
}
|
|
6024
6015
|
// Regular string literals get single quotes
|
|
6025
|
-
return `${quotedDefname} =
|
|
6016
|
+
return `${quotedDefname} = ${quotes_1.QuoteUtils.escape(stringData.sval)}`;
|
|
6026
6017
|
}
|
|
6027
6018
|
return `${quotedDefname} = ${argValue}`;
|
|
6028
6019
|
}
|
|
@@ -6043,7 +6034,7 @@ class Deparser {
|
|
|
6043
6034
|
return `${node.defname} = ${stringData.sval}`;
|
|
6044
6035
|
}
|
|
6045
6036
|
// Regular string literals get single quotes
|
|
6046
|
-
return `${node.defname} =
|
|
6037
|
+
return `${node.defname} = ${quotes_1.QuoteUtils.escape(stringData.sval)}`;
|
|
6047
6038
|
}
|
|
6048
6039
|
if (node.arg && this.getNodeType(node.arg) === 'Boolean') {
|
|
6049
6040
|
const boolData = this.getNodeData(node.arg);
|
|
@@ -6093,7 +6084,7 @@ class Deparser {
|
|
|
6093
6084
|
}
|
|
6094
6085
|
if (node.location) {
|
|
6095
6086
|
output.push('LOCATION');
|
|
6096
|
-
output.push(
|
|
6087
|
+
output.push(quotes_1.QuoteUtils.escape(node.location));
|
|
6097
6088
|
}
|
|
6098
6089
|
if (node.options && node.options.length > 0) {
|
|
6099
6090
|
output.push('WITH');
|
|
@@ -6426,7 +6417,7 @@ class Deparser {
|
|
|
6426
6417
|
}
|
|
6427
6418
|
if (node.payload !== null && node.payload !== undefined) {
|
|
6428
6419
|
output.push(',');
|
|
6429
|
-
output.push(
|
|
6420
|
+
output.push(quotes_1.QuoteUtils.escape(node.payload));
|
|
6430
6421
|
}
|
|
6431
6422
|
return output.join(' ');
|
|
6432
6423
|
}
|
|
@@ -6454,7 +6445,7 @@ class Deparser {
|
|
|
6454
6445
|
if (!node.filename) {
|
|
6455
6446
|
throw new Error('LoadStmt requires filename');
|
|
6456
6447
|
}
|
|
6457
|
-
return `LOAD
|
|
6448
|
+
return `LOAD ${quotes_1.QuoteUtils.escape(node.filename)}`;
|
|
6458
6449
|
}
|
|
6459
6450
|
DiscardStmt(node, context) {
|
|
6460
6451
|
switch (node.target) {
|
|
@@ -6941,7 +6932,7 @@ class Deparser {
|
|
|
6941
6932
|
}
|
|
6942
6933
|
output.push('CONNECTION');
|
|
6943
6934
|
if (node.conninfo) {
|
|
6944
|
-
output.push(
|
|
6935
|
+
output.push(quotes_1.QuoteUtils.escape(node.conninfo));
|
|
6945
6936
|
}
|
|
6946
6937
|
output.push('PUBLICATION');
|
|
6947
6938
|
if (node.publication && node.publication.length > 0) {
|
|
@@ -8224,7 +8215,7 @@ class Deparser {
|
|
|
8224
8215
|
}
|
|
8225
8216
|
output.push('IS');
|
|
8226
8217
|
if (node.label) {
|
|
8227
|
-
output.push(
|
|
8218
|
+
output.push(quotes_1.QuoteUtils.escape(node.label));
|
|
8228
8219
|
}
|
|
8229
8220
|
else {
|
|
8230
8221
|
output.push('NULL');
|
|
@@ -8281,10 +8272,10 @@ class Deparser {
|
|
|
8281
8272
|
output.push(conversionName);
|
|
8282
8273
|
}
|
|
8283
8274
|
if (node.for_encoding_name) {
|
|
8284
|
-
output.push('FOR',
|
|
8275
|
+
output.push('FOR', quotes_1.QuoteUtils.escape(node.for_encoding_name));
|
|
8285
8276
|
}
|
|
8286
8277
|
if (node.to_encoding_name) {
|
|
8287
|
-
output.push('TO',
|
|
8278
|
+
output.push('TO', quotes_1.QuoteUtils.escape(node.to_encoding_name));
|
|
8288
8279
|
}
|
|
8289
8280
|
if (node.func_name && node.func_name.length > 0) {
|
|
8290
8281
|
output.push('FROM');
|
|
@@ -9419,7 +9410,7 @@ class Deparser {
|
|
|
9419
9410
|
const preservedDefName = quotedDefName !== defName ? quotedDefName : defName;
|
|
9420
9411
|
// Handle String arguments with single quotes for string literals
|
|
9421
9412
|
if (defValue.String) {
|
|
9422
|
-
return `${preservedDefName} =
|
|
9413
|
+
return `${preservedDefName} = ${quotes_1.QuoteUtils.escape(defValue.String.sval)}`;
|
|
9423
9414
|
}
|
|
9424
9415
|
return `${preservedDefName} = ${this.visit(defValue, context)}`;
|
|
9425
9416
|
}
|
|
@@ -9542,7 +9533,7 @@ class Deparser {
|
|
|
9542
9533
|
// For CREATE COLLATION, ensure String nodes are quoted as string literals
|
|
9543
9534
|
let valueStr;
|
|
9544
9535
|
if (defValue.String) {
|
|
9545
|
-
valueStr =
|
|
9536
|
+
valueStr = quotes_1.QuoteUtils.escape(defValue.String.sval);
|
|
9546
9537
|
}
|
|
9547
9538
|
else {
|
|
9548
9539
|
valueStr = this.visit(defValue, context);
|
|
@@ -9809,6 +9800,21 @@ class Deparser {
|
|
|
9809
9800
|
}
|
|
9810
9801
|
return output.join(' ');
|
|
9811
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
|
+
}
|
|
9812
9818
|
Var(node, context) {
|
|
9813
9819
|
if (node.varno && node.varattno) {
|
|
9814
9820
|
return `$${node.varno}.${node.varattno}`;
|
|
@@ -9848,7 +9854,7 @@ class Deparser {
|
|
|
9848
9854
|
}
|
|
9849
9855
|
if (node.rowexpr) {
|
|
9850
9856
|
output.push(',');
|
|
9851
|
-
output.push(
|
|
9857
|
+
output.push(this.visit(node.rowexpr, context));
|
|
9852
9858
|
}
|
|
9853
9859
|
if (node.colexprs && node.colexprs.length > 0) {
|
|
9854
9860
|
output.push('COLUMNS');
|
|
@@ -9865,25 +9871,40 @@ class Deparser {
|
|
|
9865
9871
|
if (node.lateral) {
|
|
9866
9872
|
output.push('LATERAL');
|
|
9867
9873
|
}
|
|
9868
|
-
|
|
9869
|
-
|
|
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(', ')}),`);
|
|
9870
9882
|
}
|
|
9871
9883
|
if (node.rowexpr) {
|
|
9872
|
-
|
|
9873
|
-
|
|
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));
|
|
9874
9889
|
}
|
|
9875
9890
|
if (node.columns && node.columns.length > 0) {
|
|
9876
|
-
|
|
9877
|
-
|
|
9891
|
+
args.push('COLUMNS');
|
|
9892
|
+
args.push(list_utils_1.ListUtils.unwrapList(node.columns)
|
|
9878
9893
|
.map(col => this.visit(col, context))
|
|
9879
|
-
.join(', ');
|
|
9880
|
-
output.push(`(${columns})`);
|
|
9894
|
+
.join(', '));
|
|
9881
9895
|
}
|
|
9896
|
+
output.push(`XMLTABLE(${args.join(' ')})`);
|
|
9882
9897
|
if (node.alias) {
|
|
9883
9898
|
output.push(this.Alias(node.alias, context));
|
|
9884
9899
|
}
|
|
9885
9900
|
return output.join(' ');
|
|
9886
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
|
+
}
|
|
9887
9908
|
RangeTableFuncCol(node, context) {
|
|
9888
9909
|
const output = [];
|
|
9889
9910
|
if (node.colname) {
|
|
@@ -9897,12 +9918,15 @@ class Deparser {
|
|
|
9897
9918
|
}
|
|
9898
9919
|
if (node.colexpr) {
|
|
9899
9920
|
output.push('PATH');
|
|
9900
|
-
output.push(
|
|
9921
|
+
output.push(this.visit(node.colexpr, context));
|
|
9901
9922
|
}
|
|
9902
9923
|
if (node.coldefexpr) {
|
|
9903
9924
|
output.push('DEFAULT');
|
|
9904
9925
|
output.push(this.visit(node.coldefexpr, context));
|
|
9905
9926
|
}
|
|
9927
|
+
if (node.is_not_null) {
|
|
9928
|
+
output.push('NOT NULL');
|
|
9929
|
+
}
|
|
9906
9930
|
return output.join(' ');
|
|
9907
9931
|
}
|
|
9908
9932
|
formatJsonFormat(format) {
|
package/esm/deparser.js
CHANGED
|
@@ -119,6 +119,31 @@ function isWrappedParseResult(obj) {
|
|
|
119
119
|
* The deparser automatically detects bare ParseResult objects for backward
|
|
120
120
|
* compatibility and wraps them internally for consistent processing.
|
|
121
121
|
*/
|
|
122
|
+
function formatBitString(bsval) {
|
|
123
|
+
const quote = (value) => value.replace(/'/g, "''");
|
|
124
|
+
if (/^x[0-9A-Fa-f]+$/.test(bsval)) {
|
|
125
|
+
return `x'${bsval.substring(1)}'`;
|
|
126
|
+
}
|
|
127
|
+
if (bsval.startsWith('b')) {
|
|
128
|
+
return `b'${quote(bsval.substring(1))}'`;
|
|
129
|
+
}
|
|
130
|
+
return `b'${quote(bsval)}'`;
|
|
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
|
+
}
|
|
122
147
|
export class Deparser {
|
|
123
148
|
tree;
|
|
124
149
|
options;
|
|
@@ -1587,39 +1612,39 @@ export class Deparser {
|
|
|
1587
1612
|
if (nodeAny.ival !== undefined) {
|
|
1588
1613
|
if (typeof nodeAny.ival === 'object' && nodeAny.ival !== null) {
|
|
1589
1614
|
if (nodeAny.ival.ival !== undefined) {
|
|
1590
|
-
return nodeAny.ival.ival
|
|
1615
|
+
return formatInteger(nodeAny.ival.ival);
|
|
1591
1616
|
}
|
|
1592
1617
|
else if (Object.keys(nodeAny.ival).length === 0) {
|
|
1593
1618
|
return '0';
|
|
1594
1619
|
}
|
|
1595
1620
|
else {
|
|
1596
|
-
return nodeAny.ival
|
|
1621
|
+
return formatInteger(nodeAny.ival);
|
|
1597
1622
|
}
|
|
1598
1623
|
}
|
|
1599
1624
|
else if (nodeAny.ival === null) {
|
|
1600
1625
|
return 'NULL';
|
|
1601
1626
|
}
|
|
1602
1627
|
else {
|
|
1603
|
-
return nodeAny.ival
|
|
1628
|
+
return formatInteger(nodeAny.ival);
|
|
1604
1629
|
}
|
|
1605
1630
|
}
|
|
1606
1631
|
else if (nodeAny.fval !== undefined) {
|
|
1607
1632
|
if (typeof nodeAny.fval === 'object' && nodeAny.fval !== null) {
|
|
1608
1633
|
if (nodeAny.fval.fval !== undefined) {
|
|
1609
|
-
return nodeAny.fval.fval
|
|
1634
|
+
return formatNumeric(nodeAny.fval.fval);
|
|
1610
1635
|
}
|
|
1611
1636
|
else if (Object.keys(nodeAny.fval).length === 0) {
|
|
1612
1637
|
return '0.0';
|
|
1613
1638
|
}
|
|
1614
1639
|
else {
|
|
1615
|
-
return nodeAny.fval
|
|
1640
|
+
return formatNumeric(nodeAny.fval);
|
|
1616
1641
|
}
|
|
1617
1642
|
}
|
|
1618
1643
|
else if (nodeAny.fval === null) {
|
|
1619
1644
|
return 'NULL';
|
|
1620
1645
|
}
|
|
1621
1646
|
else {
|
|
1622
|
-
return nodeAny.fval
|
|
1647
|
+
return formatNumeric(nodeAny.fval);
|
|
1623
1648
|
}
|
|
1624
1649
|
}
|
|
1625
1650
|
else if (nodeAny.sval !== undefined) {
|
|
@@ -1666,15 +1691,7 @@ export class Deparser {
|
|
|
1666
1691
|
else if (nodeAny.bsval !== undefined) {
|
|
1667
1692
|
if (typeof nodeAny.bsval === 'object' && nodeAny.bsval !== null) {
|
|
1668
1693
|
if (nodeAny.bsval.bsval !== undefined) {
|
|
1669
|
-
|
|
1670
|
-
// Check if this is a hexadecimal bit string (starts with x and contains only hex digits)
|
|
1671
|
-
if (bsval.startsWith('x') && /^x[0-9A-Fa-f]+$/.test(bsval)) {
|
|
1672
|
-
return `x'${bsval.substring(1)}'`;
|
|
1673
|
-
}
|
|
1674
|
-
if (bsval.startsWith('b')) {
|
|
1675
|
-
return `b'${bsval.substring(1)}'`;
|
|
1676
|
-
}
|
|
1677
|
-
return `b'${bsval}'`;
|
|
1694
|
+
return formatBitString(nodeAny.bsval.bsval);
|
|
1678
1695
|
}
|
|
1679
1696
|
else if (Object.keys(nodeAny.bsval).length === 0) {
|
|
1680
1697
|
return "''";
|
|
@@ -1687,23 +1704,15 @@ export class Deparser {
|
|
|
1687
1704
|
return 'NULL';
|
|
1688
1705
|
}
|
|
1689
1706
|
else {
|
|
1690
|
-
|
|
1691
|
-
// Check if this is a hexadecimal bit string (starts with x and contains only hex digits)
|
|
1692
|
-
if (bsval.startsWith('x') && /^x[0-9A-Fa-f]+$/.test(bsval)) {
|
|
1693
|
-
return `x'${bsval.substring(1)}'`;
|
|
1694
|
-
}
|
|
1695
|
-
if (bsval.startsWith('b')) {
|
|
1696
|
-
return `b'${bsval.substring(1)}'`;
|
|
1697
|
-
}
|
|
1698
|
-
return `b'${bsval}'`;
|
|
1707
|
+
return formatBitString(nodeAny.bsval);
|
|
1699
1708
|
}
|
|
1700
1709
|
}
|
|
1701
1710
|
if (nodeAny.val) {
|
|
1702
1711
|
if (nodeAny.val.Integer?.ival !== undefined) {
|
|
1703
|
-
return nodeAny.val.Integer.ival
|
|
1712
|
+
return formatInteger(nodeAny.val.Integer.ival);
|
|
1704
1713
|
}
|
|
1705
1714
|
else if (nodeAny.val.Float?.fval !== undefined) {
|
|
1706
|
-
return nodeAny.val.Float.fval
|
|
1715
|
+
return formatNumeric(nodeAny.val.Float.fval);
|
|
1707
1716
|
}
|
|
1708
1717
|
else if (nodeAny.val.String?.sval !== undefined) {
|
|
1709
1718
|
return QuoteUtils.escape(nodeAny.val.String.sval);
|
|
@@ -1712,7 +1721,7 @@ export class Deparser {
|
|
|
1712
1721
|
return nodeAny.val.Boolean.boolval ? 'true' : 'false';
|
|
1713
1722
|
}
|
|
1714
1723
|
else if (nodeAny.val.BitString?.bsval !== undefined) {
|
|
1715
|
-
return nodeAny.val.BitString.bsval;
|
|
1724
|
+
return formatBitString(nodeAny.val.BitString.bsval);
|
|
1716
1725
|
}
|
|
1717
1726
|
}
|
|
1718
1727
|
if (nodeAny.isnull === true) {
|
|
@@ -1724,15 +1733,15 @@ export class Deparser {
|
|
|
1724
1733
|
}
|
|
1725
1734
|
if (nodeAny.Integer !== undefined) {
|
|
1726
1735
|
if (typeof nodeAny.Integer === 'object' && nodeAny.Integer.ival !== undefined) {
|
|
1727
|
-
return nodeAny.Integer.ival
|
|
1736
|
+
return formatInteger(nodeAny.Integer.ival);
|
|
1728
1737
|
}
|
|
1729
|
-
return nodeAny.Integer
|
|
1738
|
+
return formatInteger(nodeAny.Integer);
|
|
1730
1739
|
}
|
|
1731
1740
|
if (nodeAny.Float !== undefined) {
|
|
1732
1741
|
if (typeof nodeAny.Float === 'object' && nodeAny.Float.fval !== undefined) {
|
|
1733
|
-
return nodeAny.Float.fval
|
|
1742
|
+
return formatNumeric(nodeAny.Float.fval);
|
|
1734
1743
|
}
|
|
1735
|
-
return nodeAny.Float
|
|
1744
|
+
return formatNumeric(nodeAny.Float);
|
|
1736
1745
|
}
|
|
1737
1746
|
if (nodeAny.String !== undefined) {
|
|
1738
1747
|
if (typeof nodeAny.String === 'object' && nodeAny.String.sval !== undefined) {
|
|
@@ -2383,24 +2392,16 @@ export class Deparser {
|
|
|
2383
2392
|
return QuoteUtils.quoteIdentifier(value);
|
|
2384
2393
|
}
|
|
2385
2394
|
Integer(node, context) {
|
|
2386
|
-
return node.ival
|
|
2395
|
+
return node.ival === undefined || node.ival === null ? '0' : formatInteger(node.ival);
|
|
2387
2396
|
}
|
|
2388
2397
|
Float(node, context) {
|
|
2389
|
-
return node.fval
|
|
2398
|
+
return node.fval ? formatNumeric(node.fval) : '0.0';
|
|
2390
2399
|
}
|
|
2391
2400
|
Boolean(node, context) {
|
|
2392
2401
|
return node.boolval ? 'true' : 'false';
|
|
2393
2402
|
}
|
|
2394
2403
|
BitString(node, context) {
|
|
2395
|
-
|
|
2396
|
-
if (node.bsval.startsWith('x')) {
|
|
2397
|
-
return `x'${node.bsval.substring(1)}'`;
|
|
2398
|
-
}
|
|
2399
|
-
if (node.bsval.startsWith('b')) {
|
|
2400
|
-
return `b'${node.bsval.substring(1)}'`;
|
|
2401
|
-
}
|
|
2402
|
-
// Fallback for raw values without prefix
|
|
2403
|
-
return `b'${node.bsval}'`;
|
|
2404
|
+
return formatBitString(node.bsval);
|
|
2404
2405
|
}
|
|
2405
2406
|
Null(node, context) {
|
|
2406
2407
|
return 'NULL';
|
|
@@ -3632,7 +3633,7 @@ export class Deparser {
|
|
|
3632
3633
|
const opclassOpts = ListUtils.unwrapList(node.opclassopts).map(opt => {
|
|
3633
3634
|
if (opt.DefElem && opt.DefElem.arg && this.getNodeType(opt.DefElem.arg) === 'String') {
|
|
3634
3635
|
const stringData = this.getNodeData(opt.DefElem.arg);
|
|
3635
|
-
return `${opt.DefElem.defname}
|
|
3636
|
+
return `${QuoteUtils.quoteIdentifierAfterDot(opt.DefElem.defname)}=${QuoteUtils.escape(stringData.sval)}`;
|
|
3636
3637
|
}
|
|
3637
3638
|
return this.visit(opt, context.spawn('IndexElem'));
|
|
3638
3639
|
});
|
|
@@ -3933,19 +3934,19 @@ export class Deparser {
|
|
|
3933
3934
|
case 'TRANS_STMT_PREPARE':
|
|
3934
3935
|
output.push('PREPARE TRANSACTION');
|
|
3935
3936
|
if (node.gid) {
|
|
3936
|
-
output.push(
|
|
3937
|
+
output.push(QuoteUtils.escape(node.gid));
|
|
3937
3938
|
}
|
|
3938
3939
|
break;
|
|
3939
3940
|
case 'TRANS_STMT_COMMIT_PREPARED':
|
|
3940
3941
|
output.push('COMMIT PREPARED');
|
|
3941
3942
|
if (node.gid) {
|
|
3942
|
-
output.push(
|
|
3943
|
+
output.push(QuoteUtils.escape(node.gid));
|
|
3943
3944
|
}
|
|
3944
3945
|
break;
|
|
3945
3946
|
case 'TRANS_STMT_ROLLBACK_PREPARED':
|
|
3946
3947
|
output.push('ROLLBACK PREPARED');
|
|
3947
3948
|
if (node.gid) {
|
|
3948
|
-
output.push(
|
|
3949
|
+
output.push(QuoteUtils.escape(node.gid));
|
|
3949
3950
|
}
|
|
3950
3951
|
break;
|
|
3951
3952
|
default:
|
|
@@ -4021,8 +4022,8 @@ export class Deparser {
|
|
|
4021
4022
|
const nodeData = this.getNodeData(arg);
|
|
4022
4023
|
if (nodeData.sval !== undefined) {
|
|
4023
4024
|
const svalValue = typeof nodeData.sval === 'object' ? nodeData.sval.sval : nodeData.sval;
|
|
4024
|
-
if (svalValue
|
|
4025
|
-
return
|
|
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);
|
|
4026
4027
|
}
|
|
4027
4028
|
return svalValue;
|
|
4028
4029
|
}
|
|
@@ -5549,9 +5550,9 @@ export class Deparser {
|
|
|
5549
5550
|
if (context.parentNodeTypes.includes('IndexElem')) {
|
|
5550
5551
|
if (node.arg && this.getNodeType(node.arg) === 'String') {
|
|
5551
5552
|
const stringData = this.getNodeData(node.arg);
|
|
5552
|
-
return `${node.defname}
|
|
5553
|
+
return `${QuoteUtils.quoteIdentifierAfterDot(node.defname)}=${QuoteUtils.escape(stringData.sval)}`;
|
|
5553
5554
|
}
|
|
5554
|
-
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')))}`;
|
|
5555
5556
|
}
|
|
5556
5557
|
// Handle CREATE OPERATOR boolean flags - MUST be first to preserve case
|
|
5557
5558
|
if (context.parentNodeTypes.includes('DefineStmt') &&
|
|
@@ -5576,9 +5577,7 @@ export class Deparser {
|
|
|
5576
5577
|
const defElemContext = context.spawn('DefElem');
|
|
5577
5578
|
const argValue = this.visit(node.arg, defElemContext);
|
|
5578
5579
|
if (context.parentNodeTypes.includes('CreateFdwStmt') || context.parentNodeTypes.includes('AlterFdwStmt')) {
|
|
5579
|
-
const finalValue =
|
|
5580
|
-
? `'${argValue}'`
|
|
5581
|
-
: argValue;
|
|
5580
|
+
const finalValue = this.quoteDefElemArg(node.arg, argValue);
|
|
5582
5581
|
const quotedDefname = QuoteUtils.quoteIdentifier(node.defname);
|
|
5583
5582
|
if (node.defaction === 'DEFELEM_ADD') {
|
|
5584
5583
|
return `ADD ${quotedDefname} ${finalValue}`;
|
|
@@ -5591,9 +5590,7 @@ export class Deparser {
|
|
|
5591
5590
|
}
|
|
5592
5591
|
return `${quotedDefname} ${finalValue}`;
|
|
5593
5592
|
}
|
|
5594
|
-
const quotedValue =
|
|
5595
|
-
? `'${argValue}'`
|
|
5596
|
-
: argValue;
|
|
5593
|
+
const quotedValue = this.quoteDefElemArg(node.arg, argValue);
|
|
5597
5594
|
if (node.defaction === 'DEFELEM_ADD') {
|
|
5598
5595
|
return `ADD ${node.defname} ${quotedValue}`;
|
|
5599
5596
|
}
|
|
@@ -5625,9 +5622,7 @@ export class Deparser {
|
|
|
5625
5622
|
}
|
|
5626
5623
|
const defElemContext = context.spawn('DefElem');
|
|
5627
5624
|
const argValue = this.visit(node.arg, defElemContext);
|
|
5628
|
-
const quotedValue =
|
|
5629
|
-
? `'${argValue}'`
|
|
5630
|
-
: argValue;
|
|
5625
|
+
const quotedValue = this.quoteDefElemArg(node.arg, argValue);
|
|
5631
5626
|
return `PASSWORD ${quotedValue}`;
|
|
5632
5627
|
}
|
|
5633
5628
|
}
|
|
@@ -5692,9 +5687,7 @@ export class Deparser {
|
|
|
5692
5687
|
}
|
|
5693
5688
|
}
|
|
5694
5689
|
if (node.defname === 'validUntil') {
|
|
5695
|
-
const quotedValue =
|
|
5696
|
-
? `'${argValue}'`
|
|
5697
|
-
: argValue;
|
|
5690
|
+
const quotedValue = this.quoteDefElemArg(node.arg, argValue);
|
|
5698
5691
|
return `VALID UNTIL ${quotedValue}`;
|
|
5699
5692
|
}
|
|
5700
5693
|
if (node.defname === 'adminmembers') {
|
|
@@ -5874,9 +5867,7 @@ export class Deparser {
|
|
|
5874
5867
|
if (context.parentNodeTypes.includes('AlterExtensionStmt')) {
|
|
5875
5868
|
if (node.defname === 'new_version') {
|
|
5876
5869
|
// argValue is unquoted due to DefElem context, so we need to quote it
|
|
5877
|
-
const quotedValue =
|
|
5878
|
-
? `'${argValue}'`
|
|
5879
|
-
: argValue;
|
|
5870
|
+
const quotedValue = this.quoteDefElemArg(node.arg, argValue);
|
|
5880
5871
|
return `UPDATE TO ${quotedValue}`;
|
|
5881
5872
|
}
|
|
5882
5873
|
if (node.defname === 'schema') {
|
|
@@ -5901,15 +5892,15 @@ export class Deparser {
|
|
|
5901
5892
|
}
|
|
5902
5893
|
// Handle IndexStmt WITH clause options - no quotes, compact formatting
|
|
5903
5894
|
if (context.parentNodeTypes.includes('IndexStmt')) {
|
|
5904
|
-
return `${node.defname}=${argValue}`;
|
|
5895
|
+
return `${QuoteUtils.quoteIdentifierAfterDot(node.defname)}=${this.formatOptionValue(node.arg, argValue)}`;
|
|
5905
5896
|
}
|
|
5906
5897
|
// Handle IndexElem opclassopts - preserve string values as strings
|
|
5907
5898
|
if (context.parentNodeTypes.includes('IndexElem')) {
|
|
5908
5899
|
if (node.arg && this.getNodeType(node.arg) === 'String') {
|
|
5909
5900
|
const stringData = this.getNodeData(node.arg);
|
|
5910
|
-
return `${node.defname}
|
|
5901
|
+
return `${QuoteUtils.quoteIdentifierAfterDot(node.defname)}=${QuoteUtils.escape(stringData.sval)}`;
|
|
5911
5902
|
}
|
|
5912
|
-
return `${node.defname}=${argValue}`;
|
|
5903
|
+
return `${QuoteUtils.quoteIdentifierAfterDot(node.defname)}=${this.formatOptionValue(node.arg, argValue)}`;
|
|
5913
5904
|
}
|
|
5914
5905
|
// Handle CreateStmt table options - no quotes, compact formatting
|
|
5915
5906
|
if (context.parentNodeTypes.includes('CreateStmt')) {
|
|
@@ -5919,9 +5910,9 @@ export class Deparser {
|
|
|
5919
5910
|
// For numeric values, use the raw value without quotes
|
|
5920
5911
|
if (node.arg && this.getNodeType(node.arg) === 'Integer') {
|
|
5921
5912
|
const integerData = this.getNodeData(node.arg);
|
|
5922
|
-
return `${relOptName}=${integerData.ival}`;
|
|
5913
|
+
return `${relOptName}=${formatInteger(integerData.ival)}`;
|
|
5923
5914
|
}
|
|
5924
|
-
return `${relOptName}=${argValue}`;
|
|
5915
|
+
return `${relOptName}=${this.formatOptionValue(node.arg, argValue)}`;
|
|
5925
5916
|
}
|
|
5926
5917
|
// Handle CreateEventTrigStmt WHEN clause - use IN syntax for List arguments
|
|
5927
5918
|
if (context.parentNodeTypes.includes('CreateEventTrigStmt')) {
|
|
@@ -5931,7 +5922,7 @@ export class Deparser {
|
|
|
5931
5922
|
const values = listItems.map(item => {
|
|
5932
5923
|
if (this.getNodeType(item) === 'String') {
|
|
5933
5924
|
const stringData = this.getNodeData(item);
|
|
5934
|
-
return
|
|
5925
|
+
return QuoteUtils.escape(stringData.sval || '');
|
|
5935
5926
|
}
|
|
5936
5927
|
return this.visit(item, context);
|
|
5937
5928
|
});
|
|
@@ -5946,7 +5937,7 @@ export class Deparser {
|
|
|
5946
5937
|
if (node.arg && this.getNodeType(node.arg) === 'TypeName') {
|
|
5947
5938
|
return `${optionName} = ${argValue}`;
|
|
5948
5939
|
}
|
|
5949
|
-
return `${optionName} = ${argValue}`;
|
|
5940
|
+
return `${optionName} = ${this.formatOptionValue(node.arg, argValue)}`;
|
|
5950
5941
|
}
|
|
5951
5942
|
// Handle ViewStmt WITH options - don't quote numeric values
|
|
5952
5943
|
if (context.parentNodeTypes.includes('ViewStmt')) {
|
|
@@ -6019,7 +6010,7 @@ export class Deparser {
|
|
|
6019
6010
|
return `${quotedDefname} = ${stringData.sval}`;
|
|
6020
6011
|
}
|
|
6021
6012
|
// Regular string literals get single quotes
|
|
6022
|
-
return `${quotedDefname} =
|
|
6013
|
+
return `${quotedDefname} = ${QuoteUtils.escape(stringData.sval)}`;
|
|
6023
6014
|
}
|
|
6024
6015
|
return `${quotedDefname} = ${argValue}`;
|
|
6025
6016
|
}
|
|
@@ -6040,7 +6031,7 @@ export class Deparser {
|
|
|
6040
6031
|
return `${node.defname} = ${stringData.sval}`;
|
|
6041
6032
|
}
|
|
6042
6033
|
// Regular string literals get single quotes
|
|
6043
|
-
return `${node.defname} =
|
|
6034
|
+
return `${node.defname} = ${QuoteUtils.escape(stringData.sval)}`;
|
|
6044
6035
|
}
|
|
6045
6036
|
if (node.arg && this.getNodeType(node.arg) === 'Boolean') {
|
|
6046
6037
|
const boolData = this.getNodeData(node.arg);
|
|
@@ -6090,7 +6081,7 @@ export class Deparser {
|
|
|
6090
6081
|
}
|
|
6091
6082
|
if (node.location) {
|
|
6092
6083
|
output.push('LOCATION');
|
|
6093
|
-
output.push(
|
|
6084
|
+
output.push(QuoteUtils.escape(node.location));
|
|
6094
6085
|
}
|
|
6095
6086
|
if (node.options && node.options.length > 0) {
|
|
6096
6087
|
output.push('WITH');
|
|
@@ -6423,7 +6414,7 @@ export class Deparser {
|
|
|
6423
6414
|
}
|
|
6424
6415
|
if (node.payload !== null && node.payload !== undefined) {
|
|
6425
6416
|
output.push(',');
|
|
6426
|
-
output.push(
|
|
6417
|
+
output.push(QuoteUtils.escape(node.payload));
|
|
6427
6418
|
}
|
|
6428
6419
|
return output.join(' ');
|
|
6429
6420
|
}
|
|
@@ -6451,7 +6442,7 @@ export class Deparser {
|
|
|
6451
6442
|
if (!node.filename) {
|
|
6452
6443
|
throw new Error('LoadStmt requires filename');
|
|
6453
6444
|
}
|
|
6454
|
-
return `LOAD
|
|
6445
|
+
return `LOAD ${QuoteUtils.escape(node.filename)}`;
|
|
6455
6446
|
}
|
|
6456
6447
|
DiscardStmt(node, context) {
|
|
6457
6448
|
switch (node.target) {
|
|
@@ -6938,7 +6929,7 @@ export class Deparser {
|
|
|
6938
6929
|
}
|
|
6939
6930
|
output.push('CONNECTION');
|
|
6940
6931
|
if (node.conninfo) {
|
|
6941
|
-
output.push(
|
|
6932
|
+
output.push(QuoteUtils.escape(node.conninfo));
|
|
6942
6933
|
}
|
|
6943
6934
|
output.push('PUBLICATION');
|
|
6944
6935
|
if (node.publication && node.publication.length > 0) {
|
|
@@ -8221,7 +8212,7 @@ export class Deparser {
|
|
|
8221
8212
|
}
|
|
8222
8213
|
output.push('IS');
|
|
8223
8214
|
if (node.label) {
|
|
8224
|
-
output.push(
|
|
8215
|
+
output.push(QuoteUtils.escape(node.label));
|
|
8225
8216
|
}
|
|
8226
8217
|
else {
|
|
8227
8218
|
output.push('NULL');
|
|
@@ -8278,10 +8269,10 @@ export class Deparser {
|
|
|
8278
8269
|
output.push(conversionName);
|
|
8279
8270
|
}
|
|
8280
8271
|
if (node.for_encoding_name) {
|
|
8281
|
-
output.push('FOR',
|
|
8272
|
+
output.push('FOR', QuoteUtils.escape(node.for_encoding_name));
|
|
8282
8273
|
}
|
|
8283
8274
|
if (node.to_encoding_name) {
|
|
8284
|
-
output.push('TO',
|
|
8275
|
+
output.push('TO', QuoteUtils.escape(node.to_encoding_name));
|
|
8285
8276
|
}
|
|
8286
8277
|
if (node.func_name && node.func_name.length > 0) {
|
|
8287
8278
|
output.push('FROM');
|
|
@@ -9416,7 +9407,7 @@ export class Deparser {
|
|
|
9416
9407
|
const preservedDefName = quotedDefName !== defName ? quotedDefName : defName;
|
|
9417
9408
|
// Handle String arguments with single quotes for string literals
|
|
9418
9409
|
if (defValue.String) {
|
|
9419
|
-
return `${preservedDefName} =
|
|
9410
|
+
return `${preservedDefName} = ${QuoteUtils.escape(defValue.String.sval)}`;
|
|
9420
9411
|
}
|
|
9421
9412
|
return `${preservedDefName} = ${this.visit(defValue, context)}`;
|
|
9422
9413
|
}
|
|
@@ -9539,7 +9530,7 @@ export class Deparser {
|
|
|
9539
9530
|
// For CREATE COLLATION, ensure String nodes are quoted as string literals
|
|
9540
9531
|
let valueStr;
|
|
9541
9532
|
if (defValue.String) {
|
|
9542
|
-
valueStr =
|
|
9533
|
+
valueStr = QuoteUtils.escape(defValue.String.sval);
|
|
9543
9534
|
}
|
|
9544
9535
|
else {
|
|
9545
9536
|
valueStr = this.visit(defValue, context);
|
|
@@ -9806,6 +9797,21 @@ export class Deparser {
|
|
|
9806
9797
|
}
|
|
9807
9798
|
return output.join(' ');
|
|
9808
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
|
+
}
|
|
9809
9815
|
Var(node, context) {
|
|
9810
9816
|
if (node.varno && node.varattno) {
|
|
9811
9817
|
return `$${node.varno}.${node.varattno}`;
|
|
@@ -9845,7 +9851,7 @@ export class Deparser {
|
|
|
9845
9851
|
}
|
|
9846
9852
|
if (node.rowexpr) {
|
|
9847
9853
|
output.push(',');
|
|
9848
|
-
output.push(
|
|
9854
|
+
output.push(this.visit(node.rowexpr, context));
|
|
9849
9855
|
}
|
|
9850
9856
|
if (node.colexprs && node.colexprs.length > 0) {
|
|
9851
9857
|
output.push('COLUMNS');
|
|
@@ -9862,25 +9868,40 @@ export class Deparser {
|
|
|
9862
9868
|
if (node.lateral) {
|
|
9863
9869
|
output.push('LATERAL');
|
|
9864
9870
|
}
|
|
9865
|
-
|
|
9866
|
-
|
|
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(', ')}),`);
|
|
9867
9879
|
}
|
|
9868
9880
|
if (node.rowexpr) {
|
|
9869
|
-
|
|
9870
|
-
|
|
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));
|
|
9871
9886
|
}
|
|
9872
9887
|
if (node.columns && node.columns.length > 0) {
|
|
9873
|
-
|
|
9874
|
-
|
|
9888
|
+
args.push('COLUMNS');
|
|
9889
|
+
args.push(ListUtils.unwrapList(node.columns)
|
|
9875
9890
|
.map(col => this.visit(col, context))
|
|
9876
|
-
.join(', ');
|
|
9877
|
-
output.push(`(${columns})`);
|
|
9891
|
+
.join(', '));
|
|
9878
9892
|
}
|
|
9893
|
+
output.push(`XMLTABLE(${args.join(' ')})`);
|
|
9879
9894
|
if (node.alias) {
|
|
9880
9895
|
output.push(this.Alias(node.alias, context));
|
|
9881
9896
|
}
|
|
9882
9897
|
return output.join(' ');
|
|
9883
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
|
+
}
|
|
9884
9905
|
RangeTableFuncCol(node, context) {
|
|
9885
9906
|
const output = [];
|
|
9886
9907
|
if (node.colname) {
|
|
@@ -9894,12 +9915,15 @@ export class Deparser {
|
|
|
9894
9915
|
}
|
|
9895
9916
|
if (node.colexpr) {
|
|
9896
9917
|
output.push('PATH');
|
|
9897
|
-
output.push(
|
|
9918
|
+
output.push(this.visit(node.colexpr, context));
|
|
9898
9919
|
}
|
|
9899
9920
|
if (node.coldefexpr) {
|
|
9900
9921
|
output.push('DEFAULT');
|
|
9901
9922
|
output.push(this.visit(node.coldefexpr, context));
|
|
9902
9923
|
}
|
|
9924
|
+
if (node.is_not_null) {
|
|
9925
|
+
output.push('NOT NULL');
|
|
9926
|
+
}
|
|
9903
9927
|
return output.join(' ');
|
|
9904
9928
|
}
|
|
9905
9929
|
formatJsonFormat(format) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pgsql-deparser",
|
|
3
|
-
"version": "18.3.
|
|
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": "
|
|
63
|
+
"gitHead": "24882cc67810aaa1af8dd241594634cf54105126"
|
|
64
64
|
}
|