pgsql-deparser 17.18.4 → 17.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -9
- package/deparser/deparser.d.ts +311 -0
- package/{deparser.js → deparser/deparser.js} +1012 -577
- package/deparser/index.d.ts +11 -0
- package/deparser/index.js +21 -0
- package/{utils → deparser/utils}/list-utils.d.ts +4 -0
- package/{utils → deparser/utils}/list-utils.js +4 -0
- package/deparser/utils/sql-formatter.d.ts +24 -0
- package/deparser/utils/sql-formatter.js +143 -0
- package/{visitors → deparser/visitors}/base.d.ts +8 -5
- package/{visitors → deparser/visitors}/base.js +4 -0
- package/esm/{deparser.js → deparser/deparser.js} +1013 -578
- package/esm/deparser/index.js +15 -0
- package/esm/{utils → deparser/utils}/list-utils.js +4 -0
- package/esm/deparser/utils/sql-formatter.js +138 -0
- package/esm/{visitors → deparser/visitors}/base.js +4 -0
- package/esm/index.js +23 -9
- package/esm/v17-to-v18-direct.js +43 -0
- package/esm/v17-to-v18.js +209 -0
- package/index.d.ts +15 -5
- package/index.js +27 -10
- package/package.json +14 -36
- package/v17-to-v18-direct.d.ts +21 -0
- package/v17-to-v18-direct.js +47 -0
- package/v17-to-v18.d.ts +55 -0
- package/v17-to-v18.js +213 -0
- package/deparser.d.ts +0 -304
- package/esm/utils/sql-formatter.js +0 -32
- package/utils/sql-formatter.d.ts +0 -12
- package/utils/sql-formatter.js +0 -36
|
@@ -1,7 +1,30 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Auto-generated file with types stripped for better tree-shaking
|
|
3
|
+
* DO NOT EDIT - Generated by strip-deparser-types.ts
|
|
4
|
+
*/
|
|
5
|
+
import { keywordKindOf, QuoteUtils } from '@pgsql/quotes';
|
|
4
6
|
import { ListUtils } from './utils/list-utils';
|
|
7
|
+
import { SqlFormatter } from './utils/sql-formatter';
|
|
8
|
+
import { DeparserContext } from './visitors/base';
|
|
9
|
+
// WindowDef.frameOptions bit flags (src/include/nodes/parsenodes.h)
|
|
10
|
+
const FRAMEOPTION_NONDEFAULT = 0x00001;
|
|
11
|
+
const FRAMEOPTION_RANGE = 0x00002;
|
|
12
|
+
const FRAMEOPTION_ROWS = 0x00004;
|
|
13
|
+
const FRAMEOPTION_GROUPS = 0x00008;
|
|
14
|
+
const FRAMEOPTION_BETWEEN = 0x00010;
|
|
15
|
+
const FRAMEOPTION_START_UNBOUNDED_PRECEDING = 0x00020;
|
|
16
|
+
const FRAMEOPTION_END_UNBOUNDED_PRECEDING = 0x00040;
|
|
17
|
+
const FRAMEOPTION_START_UNBOUNDED_FOLLOWING = 0x00080;
|
|
18
|
+
const FRAMEOPTION_END_UNBOUNDED_FOLLOWING = 0x00100;
|
|
19
|
+
const FRAMEOPTION_START_CURRENT_ROW = 0x00200;
|
|
20
|
+
const FRAMEOPTION_END_CURRENT_ROW = 0x00400;
|
|
21
|
+
const FRAMEOPTION_START_OFFSET_PRECEDING = 0x00800;
|
|
22
|
+
const FRAMEOPTION_END_OFFSET_PRECEDING = 0x01000;
|
|
23
|
+
const FRAMEOPTION_START_OFFSET_FOLLOWING = 0x02000;
|
|
24
|
+
const FRAMEOPTION_END_OFFSET_FOLLOWING = 0x04000;
|
|
25
|
+
const FRAMEOPTION_EXCLUDE_CURRENT_ROW = 0x08000;
|
|
26
|
+
const FRAMEOPTION_EXCLUDE_GROUP = 0x10000;
|
|
27
|
+
const FRAMEOPTION_EXCLUDE_TIES = 0x20000;
|
|
5
28
|
/**
|
|
6
29
|
* List of real PostgreSQL built-in types as they appear in pg_catalog.pg_type.typname.
|
|
7
30
|
* These are stored in lowercase in PostgreSQL system catalogs.
|
|
@@ -100,6 +123,36 @@ function isWrappedParseResult(obj) {
|
|
|
100
123
|
* The deparser automatically detects bare ParseResult objects for backward
|
|
101
124
|
* compatibility and wraps them internally for consistent processing.
|
|
102
125
|
*/
|
|
126
|
+
function formatBitString(bsval) {
|
|
127
|
+
const quote = (value) => value.replace(/'/g, "''");
|
|
128
|
+
if (/^x[0-9A-Fa-f]*$/.test(bsval)) {
|
|
129
|
+
return `x'${bsval.substring(1)}'`;
|
|
130
|
+
}
|
|
131
|
+
if (bsval.startsWith('b')) {
|
|
132
|
+
return `b'${quote(bsval.substring(1))}'`;
|
|
133
|
+
}
|
|
134
|
+
return `b'${quote(bsval)}'`;
|
|
135
|
+
}
|
|
136
|
+
// A dollar-quoted body ends at the first occurrence of its tag, including one
|
|
137
|
+
// formed by the body's trailing characters plus the closing tag (`...$` + `$$`).
|
|
138
|
+
function dollarTagConflicts(body, tag) {
|
|
139
|
+
return (body + tag).indexOf(tag) < body.length;
|
|
140
|
+
}
|
|
141
|
+
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)*)?)$/;
|
|
142
|
+
function formatInteger(ival) {
|
|
143
|
+
const value = String(ival);
|
|
144
|
+
if (!/^[+-]?\d+$/.test(value)) {
|
|
145
|
+
throw new Error(`Invalid integer literal: ${value}`);
|
|
146
|
+
}
|
|
147
|
+
return value;
|
|
148
|
+
}
|
|
149
|
+
function formatNumeric(fval) {
|
|
150
|
+
const value = String(fval);
|
|
151
|
+
if (!NUMERIC_LITERAL.test(value)) {
|
|
152
|
+
throw new Error(`Invalid numeric literal: ${value}`);
|
|
153
|
+
}
|
|
154
|
+
return value;
|
|
155
|
+
}
|
|
103
156
|
export class Deparser {
|
|
104
157
|
tree;
|
|
105
158
|
options;
|
|
@@ -159,10 +212,19 @@ export class Deparser {
|
|
|
159
212
|
*/
|
|
160
213
|
getFunctionDelimiter(body) {
|
|
161
214
|
const delimiter = this.options.functionDelimiter || '$$';
|
|
162
|
-
if (body
|
|
163
|
-
return
|
|
215
|
+
if (!dollarTagConflicts(body, delimiter)) {
|
|
216
|
+
return delimiter;
|
|
164
217
|
}
|
|
165
|
-
|
|
218
|
+
const fallback = this.options.functionDelimiterFallback || '$EOFCODE$';
|
|
219
|
+
if (!dollarTagConflicts(body, fallback)) {
|
|
220
|
+
return fallback;
|
|
221
|
+
}
|
|
222
|
+
const base = fallback.slice(0, -1);
|
|
223
|
+
let counter = 1;
|
|
224
|
+
while (dollarTagConflicts(body, `${base}${counter}$`)) {
|
|
225
|
+
counter++;
|
|
226
|
+
}
|
|
227
|
+
return `${base}${counter}$`;
|
|
166
228
|
}
|
|
167
229
|
/**
|
|
168
230
|
* Maps ObjectType enum values to their corresponding SQL keywords
|
|
@@ -365,16 +427,16 @@ export class Deparser {
|
|
|
365
427
|
const leftStmt = this.SelectStmt(node.larg, context);
|
|
366
428
|
const rightStmt = this.SelectStmt(node.rarg, context);
|
|
367
429
|
// Add parentheses if the operand is a set operation OR has ORDER BY/LIMIT clauses OR has WITH clause
|
|
368
|
-
const leftNeedsParens = node.larg && ((node.larg.op && node.larg.op !== 'SETOP_NONE') ||
|
|
369
|
-
node.larg.sortClause ||
|
|
370
|
-
node.larg.limitCount ||
|
|
371
|
-
node.larg.limitOffset ||
|
|
372
|
-
node.larg.withClause);
|
|
373
|
-
const rightNeedsParens = node.rarg && ((node.rarg.op && node.rarg.op !== 'SETOP_NONE') ||
|
|
374
|
-
node.rarg.sortClause ||
|
|
375
|
-
node.rarg.limitCount ||
|
|
376
|
-
node.rarg.limitOffset ||
|
|
377
|
-
node.rarg.withClause);
|
|
430
|
+
const leftNeedsParens = node.larg && (((node.larg).op && (node.larg).op !== 'SETOP_NONE') ||
|
|
431
|
+
(node.larg).sortClause ||
|
|
432
|
+
(node.larg).limitCount ||
|
|
433
|
+
(node.larg).limitOffset ||
|
|
434
|
+
(node.larg).withClause);
|
|
435
|
+
const rightNeedsParens = node.rarg && (((node.rarg).op && (node.rarg).op !== 'SETOP_NONE') ||
|
|
436
|
+
(node.rarg).sortClause ||
|
|
437
|
+
(node.rarg).limitCount ||
|
|
438
|
+
(node.rarg).limitOffset ||
|
|
439
|
+
(node.rarg).withClause);
|
|
378
440
|
if (leftNeedsParens) {
|
|
379
441
|
output.push(context.parens(leftStmt));
|
|
380
442
|
}
|
|
@@ -439,12 +501,7 @@ export class Deparser {
|
|
|
439
501
|
// Check if single target is complex - if so, use multiline format
|
|
440
502
|
if (this.isComplexSelectTarget(targetNode)) {
|
|
441
503
|
output.push('SELECT' + distinctPart);
|
|
442
|
-
|
|
443
|
-
output.push(target);
|
|
444
|
-
}
|
|
445
|
-
else {
|
|
446
|
-
output.push(context.indent(target));
|
|
447
|
-
}
|
|
504
|
+
output.push(context.indent(target));
|
|
448
505
|
}
|
|
449
506
|
else {
|
|
450
507
|
output.push('SELECT' + distinctPart + ' ' + target);
|
|
@@ -454,9 +511,6 @@ export class Deparser {
|
|
|
454
511
|
const targetStrings = targetList
|
|
455
512
|
.map(e => {
|
|
456
513
|
const targetStr = this.visit(e, context.spawn('SelectStmt', { select: true }));
|
|
457
|
-
if (this.containsMultilineStringLiteral(targetStr)) {
|
|
458
|
-
return targetStr;
|
|
459
|
-
}
|
|
460
514
|
return context.indent(targetStr);
|
|
461
515
|
});
|
|
462
516
|
const formattedTargets = targetStrings.join(',' + context.newline());
|
|
@@ -508,9 +562,6 @@ export class Deparser {
|
|
|
508
562
|
return context.parens(values.join(', '));
|
|
509
563
|
});
|
|
510
564
|
const indentedTuples = lists.map(tuple => {
|
|
511
|
-
if (this.containsMultilineStringLiteral(tuple)) {
|
|
512
|
-
return tuple;
|
|
513
|
-
}
|
|
514
565
|
return context.indent(tuple);
|
|
515
566
|
});
|
|
516
567
|
output.push(indentedTuples.join(',\n'));
|
|
@@ -530,9 +581,6 @@ export class Deparser {
|
|
|
530
581
|
const groupItems = groupList
|
|
531
582
|
.map(e => {
|
|
532
583
|
const groupStr = this.visit(e, context.spawn('SelectStmt', { group: true, indentLevel: context.indentLevel + 1 }));
|
|
533
|
-
if (this.containsMultilineStringLiteral(groupStr)) {
|
|
534
|
-
return groupStr;
|
|
535
|
-
}
|
|
536
584
|
return context.indent(groupStr);
|
|
537
585
|
})
|
|
538
586
|
.join(',' + context.newline());
|
|
@@ -551,12 +599,7 @@ export class Deparser {
|
|
|
551
599
|
if (context.isPretty()) {
|
|
552
600
|
output.push('HAVING');
|
|
553
601
|
const havingStr = this.visit(node.havingClause, context);
|
|
554
|
-
|
|
555
|
-
output.push(havingStr);
|
|
556
|
-
}
|
|
557
|
-
else {
|
|
558
|
-
output.push(context.indent(havingStr));
|
|
559
|
-
}
|
|
602
|
+
output.push(context.indent(havingStr));
|
|
560
603
|
}
|
|
561
604
|
else {
|
|
562
605
|
output.push('HAVING');
|
|
@@ -577,9 +620,6 @@ export class Deparser {
|
|
|
577
620
|
const sortItems = sortList
|
|
578
621
|
.map(e => {
|
|
579
622
|
const sortStr = this.visit(e, context.spawn('SelectStmt', { sort: true, indentLevel: context.indentLevel + 1 }));
|
|
580
|
-
if (this.containsMultilineStringLiteral(sortStr)) {
|
|
581
|
-
return sortStr;
|
|
582
|
-
}
|
|
583
623
|
return context.indent(sortStr);
|
|
584
624
|
})
|
|
585
625
|
.join(',' + context.newline());
|
|
@@ -843,15 +883,15 @@ export class Deparser {
|
|
|
843
883
|
getOperatorPrecedence(operator) {
|
|
844
884
|
const precedence = {
|
|
845
885
|
'||': 1, // string concatenation
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
886
|
+
OR: 2, // logical OR
|
|
887
|
+
AND: 3, // logical AND
|
|
888
|
+
NOT: 4, // logical NOT
|
|
889
|
+
IS: 5, // IS NULL, IS NOT NULL, etc.
|
|
890
|
+
IN: 5, // IN, NOT IN
|
|
891
|
+
BETWEEN: 5, // BETWEEN, NOT BETWEEN
|
|
892
|
+
LIKE: 5, // LIKE, ILIKE, SIMILAR TO
|
|
893
|
+
ILIKE: 5,
|
|
894
|
+
SIMILAR: 5,
|
|
855
895
|
'<': 6, // comparison operators
|
|
856
896
|
'<=': 6,
|
|
857
897
|
'>': 6,
|
|
@@ -981,6 +1021,12 @@ export class Deparser {
|
|
|
981
1021
|
output.push(context.parens(columnNames.join(', ')));
|
|
982
1022
|
}
|
|
983
1023
|
}
|
|
1024
|
+
if (node.override === 'OVERRIDING_USER_VALUE') {
|
|
1025
|
+
output.push('OVERRIDING USER VALUE');
|
|
1026
|
+
}
|
|
1027
|
+
else if (node.override === 'OVERRIDING_SYSTEM_VALUE') {
|
|
1028
|
+
output.push('OVERRIDING SYSTEM VALUE');
|
|
1029
|
+
}
|
|
984
1030
|
if (node.selectStmt) {
|
|
985
1031
|
output.push(this.visit(node.selectStmt, context));
|
|
986
1032
|
}
|
|
@@ -1026,7 +1072,7 @@ export class Deparser {
|
|
|
1026
1072
|
if (context.isPretty()) {
|
|
1027
1073
|
// Put each assignment on its own line for pretty printing
|
|
1028
1074
|
const indentedTargets = targets.map(target => context.indent(target));
|
|
1029
|
-
output.
|
|
1075
|
+
output[output.length - 1] += '\n' + indentedTargets.join(',\n');
|
|
1030
1076
|
}
|
|
1031
1077
|
else {
|
|
1032
1078
|
output.push(targets.join(', '));
|
|
@@ -1042,11 +1088,8 @@ export class Deparser {
|
|
|
1042
1088
|
output.push('DO NOTHING');
|
|
1043
1089
|
}
|
|
1044
1090
|
}
|
|
1045
|
-
if (node.
|
|
1046
|
-
output.push(
|
|
1047
|
-
const returningList = ListUtils.unwrapList(node.returningList);
|
|
1048
|
-
const returns = returningList.map(ret => this.visit(ret, context));
|
|
1049
|
-
output.push(returns.join(', '));
|
|
1091
|
+
if (node.returningClause) {
|
|
1092
|
+
output.push(this.ReturningClause(node.returningClause, context));
|
|
1050
1093
|
}
|
|
1051
1094
|
return output.join(' ');
|
|
1052
1095
|
}
|
|
@@ -1060,38 +1103,8 @@ export class Deparser {
|
|
|
1060
1103
|
output.push(this.RangeVar(node.relation, context));
|
|
1061
1104
|
}
|
|
1062
1105
|
output.push('SET');
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
const firstTarget = targetList[0];
|
|
1066
|
-
const processedTargets = new Set();
|
|
1067
|
-
const assignmentParts = [];
|
|
1068
|
-
for (let i = 0; i < targetList.length; i++) {
|
|
1069
|
-
if (processedTargets.has(i))
|
|
1070
|
-
continue;
|
|
1071
|
-
const target = targetList[i];
|
|
1072
|
-
const multiAssignRef = target.ResTarget?.val?.MultiAssignRef;
|
|
1073
|
-
if (multiAssignRef) {
|
|
1074
|
-
const relatedTargets = [];
|
|
1075
|
-
for (let j = i; j < targetList.length; j++) {
|
|
1076
|
-
const otherTarget = targetList[j];
|
|
1077
|
-
const otherMultiAssignRef = otherTarget.ResTarget?.val?.MultiAssignRef;
|
|
1078
|
-
if (otherMultiAssignRef &&
|
|
1079
|
-
JSON.stringify(otherMultiAssignRef.source) === JSON.stringify(multiAssignRef.source)) {
|
|
1080
|
-
relatedTargets.push(otherTarget);
|
|
1081
|
-
processedTargets.add(j);
|
|
1082
|
-
}
|
|
1083
|
-
}
|
|
1084
|
-
const names = relatedTargets.map(t => t.ResTarget.name);
|
|
1085
|
-
const multiAssignment = `${context.parens(names.join(', '))} = ${this.visit(multiAssignRef.source, context)}`;
|
|
1086
|
-
assignmentParts.push(multiAssignment);
|
|
1087
|
-
}
|
|
1088
|
-
else {
|
|
1089
|
-
// Handle regular single-column assignment
|
|
1090
|
-
assignmentParts.push(this.visit(target, context.spawn('UpdateStmt', { update: true })));
|
|
1091
|
-
processedTargets.add(i);
|
|
1092
|
-
}
|
|
1093
|
-
}
|
|
1094
|
-
output.push(assignmentParts.join(','));
|
|
1106
|
+
if (node.targetList && node.targetList.length > 0) {
|
|
1107
|
+
output.push(this.formatUpdateAssignments(node.targetList, context));
|
|
1095
1108
|
}
|
|
1096
1109
|
if (node.fromClause) {
|
|
1097
1110
|
output.push('FROM');
|
|
@@ -1103,12 +1116,41 @@ export class Deparser {
|
|
|
1103
1116
|
output.push('WHERE');
|
|
1104
1117
|
output.push(this.visit(node.whereClause, context));
|
|
1105
1118
|
}
|
|
1106
|
-
if (node.
|
|
1107
|
-
output.push(
|
|
1108
|
-
output.push(this.deparseReturningList(node.returningList, context));
|
|
1119
|
+
if (node.returningClause) {
|
|
1120
|
+
output.push(this.ReturningClause(node.returningClause, context));
|
|
1109
1121
|
}
|
|
1110
1122
|
return output.join(' ');
|
|
1111
1123
|
}
|
|
1124
|
+
formatUpdateAssignments(targets, context) {
|
|
1125
|
+
const targetList = ListUtils.unwrapList(targets);
|
|
1126
|
+
const processedTargets = new Set();
|
|
1127
|
+
const assignmentParts = [];
|
|
1128
|
+
for (let i = 0; i < targetList.length; i++) {
|
|
1129
|
+
if (processedTargets.has(i))
|
|
1130
|
+
continue;
|
|
1131
|
+
const target = targetList[i];
|
|
1132
|
+
const multiAssignRef = target.ResTarget?.val?.MultiAssignRef;
|
|
1133
|
+
if (multiAssignRef) {
|
|
1134
|
+
const relatedTargets = [];
|
|
1135
|
+
for (let j = i; j < targetList.length; j++) {
|
|
1136
|
+
const otherTarget = targetList[j];
|
|
1137
|
+
const otherMultiAssignRef = otherTarget.ResTarget?.val?.MultiAssignRef;
|
|
1138
|
+
if (otherMultiAssignRef &&
|
|
1139
|
+
JSON.stringify(otherMultiAssignRef.source) === JSON.stringify(multiAssignRef.source)) {
|
|
1140
|
+
relatedTargets.push(otherTarget);
|
|
1141
|
+
processedTargets.add(j);
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
const names = relatedTargets.map(t => QuoteUtils.quoteIdentifier(t.ResTarget.name));
|
|
1145
|
+
assignmentParts.push(`${context.parens(names.join(', '))} = ${this.visit(multiAssignRef.source, context)}`);
|
|
1146
|
+
}
|
|
1147
|
+
else {
|
|
1148
|
+
assignmentParts.push(this.visit(target, context.spawn('UpdateStmt', { update: true })));
|
|
1149
|
+
processedTargets.add(i);
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
return assignmentParts.join(', ');
|
|
1153
|
+
}
|
|
1112
1154
|
DeleteStmt(node, context) {
|
|
1113
1155
|
try {
|
|
1114
1156
|
const output = [];
|
|
@@ -1156,13 +1198,12 @@ export class Deparser {
|
|
|
1156
1198
|
throw new Error(`Error deparsing DeleteStmt: ${error instanceof Error ? error.message : String(error)}`);
|
|
1157
1199
|
}
|
|
1158
1200
|
}
|
|
1159
|
-
if (node.
|
|
1160
|
-
output.push('RETURNING');
|
|
1201
|
+
if (node.returningClause) {
|
|
1161
1202
|
try {
|
|
1162
|
-
output.push(this.
|
|
1203
|
+
output.push(this.ReturningClause(node.returningClause, context));
|
|
1163
1204
|
}
|
|
1164
1205
|
catch (error) {
|
|
1165
|
-
console.warn(`Error processing
|
|
1206
|
+
console.warn(`Error processing returningClause in DeleteStmt: ${error instanceof Error ? error.message : String(error)}`);
|
|
1166
1207
|
throw new Error(`Error deparsing DeleteStmt: ${error instanceof Error ? error.message : String(error)}`);
|
|
1167
1208
|
}
|
|
1168
1209
|
}
|
|
@@ -1183,9 +1224,6 @@ export class Deparser {
|
|
|
1183
1224
|
const cteStrings = ctes.map((cte, index) => {
|
|
1184
1225
|
const cteStr = this.visit(cte, context);
|
|
1185
1226
|
const prefix = index === 0 ? context.newline() : ',' + context.newline();
|
|
1186
|
-
if (this.containsMultilineStringLiteral(cteStr)) {
|
|
1187
|
-
return prefix + cteStr;
|
|
1188
|
-
}
|
|
1189
1227
|
return prefix + context.indent(cteStr);
|
|
1190
1228
|
});
|
|
1191
1229
|
output.push(cteStrings.join(''));
|
|
@@ -1240,6 +1278,48 @@ export class Deparser {
|
|
|
1240
1278
|
}
|
|
1241
1279
|
return output.join(' ');
|
|
1242
1280
|
}
|
|
1281
|
+
ATAlterConstraint(node, context) {
|
|
1282
|
+
const output = ['ALTER CONSTRAINT'];
|
|
1283
|
+
if (node.conname) {
|
|
1284
|
+
output.push(QuoteUtils.quoteIdentifier(node.conname));
|
|
1285
|
+
}
|
|
1286
|
+
if (node.alterEnforceability) {
|
|
1287
|
+
output.push(node.is_enforced ? 'ENFORCED' : 'NOT ENFORCED');
|
|
1288
|
+
}
|
|
1289
|
+
if (node.alterDeferrability) {
|
|
1290
|
+
output.push(node.deferrable ? 'DEFERRABLE' : 'NOT DEFERRABLE');
|
|
1291
|
+
if (node.initdeferred) {
|
|
1292
|
+
output.push('INITIALLY DEFERRED');
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
if (node.alterInheritability) {
|
|
1296
|
+
output.push(node.noinherit ? 'NO INHERIT' : 'INHERIT');
|
|
1297
|
+
}
|
|
1298
|
+
return output.join(' ');
|
|
1299
|
+
}
|
|
1300
|
+
deparseKeyAttrs(attrs, withPeriod, context) {
|
|
1301
|
+
const items = ListUtils.unwrapList(attrs).map(attr => this.visit(attr, context));
|
|
1302
|
+
if (withPeriod && items.length > 0) {
|
|
1303
|
+
items[items.length - 1] = `PERIOD ${items[items.length - 1]}`;
|
|
1304
|
+
}
|
|
1305
|
+
return items.join(', ');
|
|
1306
|
+
}
|
|
1307
|
+
ReturningClause(node, context) {
|
|
1308
|
+
const output = ['RETURNING'];
|
|
1309
|
+
if (node.options && node.options.length > 0) {
|
|
1310
|
+
const options = ListUtils.unwrapList(node.options)
|
|
1311
|
+
.map(option => this.visit(option, context));
|
|
1312
|
+
output.push(`WITH (${options.join(', ')})`);
|
|
1313
|
+
}
|
|
1314
|
+
if (node.exprs && node.exprs.length > 0) {
|
|
1315
|
+
output.push(this.deparseReturningList(node.exprs, context));
|
|
1316
|
+
}
|
|
1317
|
+
return output.join(' ');
|
|
1318
|
+
}
|
|
1319
|
+
ReturningOption(node, context) {
|
|
1320
|
+
const keyword = node.option === 'RETURNING_OPTION_NEW' ? 'NEW' : 'OLD';
|
|
1321
|
+
return `${keyword} AS ${QuoteUtils.quoteIdentifier(node.value)}`;
|
|
1322
|
+
}
|
|
1243
1323
|
deparseReturningList(list, context) {
|
|
1244
1324
|
return ListUtils.unwrapList(list)
|
|
1245
1325
|
.filter(item => item != null && this.getNodeType(item) !== 'undefined')
|
|
@@ -1318,7 +1398,9 @@ export class Deparser {
|
|
|
1318
1398
|
const source = this.visit(args[1], context);
|
|
1319
1399
|
// EXTRACT requires an unquoted uppercase keyword (EPOCH, YEAR, etc.)
|
|
1320
1400
|
// but A_Const sval deparsing wraps it in single quotes — strip and uppercase
|
|
1321
|
-
|
|
1401
|
+
// only when it is spelled the way the bare-keyword form parses back
|
|
1402
|
+
if (/^'(?:year|month|day|hour|minute|second)'$/.test(field)
|
|
1403
|
+
|| (/^'[a-z_][a-z0-9_]*'$/.test(field) && keywordKindOf(field.slice(1, -1)) === 'NO_KEYWORD')) {
|
|
1322
1404
|
field = field.slice(1, -1).toUpperCase();
|
|
1323
1405
|
}
|
|
1324
1406
|
return `EXTRACT(${field} FROM ${source})`;
|
|
@@ -1480,43 +1562,41 @@ export class Deparser {
|
|
|
1480
1562
|
result += ` FILTER (WHERE ${this.visit(node.agg_filter, context)})`;
|
|
1481
1563
|
}
|
|
1482
1564
|
if (node.over) {
|
|
1483
|
-
|
|
1484
|
-
if (node.over.name) {
|
|
1485
|
-
result += ` OVER ${node.over.name}`;
|
|
1486
|
-
}
|
|
1487
|
-
else {
|
|
1488
|
-
const windowParts = [];
|
|
1489
|
-
if (node.over.partitionClause) {
|
|
1490
|
-
const partitions = ListUtils.unwrapList(node.over.partitionClause);
|
|
1491
|
-
const partitionStrs = partitions.map(p => this.visit(p, context));
|
|
1492
|
-
windowParts.push(`PARTITION BY ${partitionStrs.join(', ')}`);
|
|
1493
|
-
}
|
|
1494
|
-
if (node.over.orderClause) {
|
|
1495
|
-
const orders = ListUtils.unwrapList(node.over.orderClause);
|
|
1496
|
-
const orderStrs = orders.map(o => this.visit(o, context));
|
|
1497
|
-
windowParts.push(`ORDER BY ${orderStrs.join(', ')}`);
|
|
1498
|
-
}
|
|
1499
|
-
// Handle window frame specifications using the dedicated formatWindowFrame method
|
|
1500
|
-
const frameClause = this.formatWindowFrame(node.over, context.spawn('FuncCall'));
|
|
1501
|
-
if (frameClause) {
|
|
1502
|
-
windowParts.push(frameClause);
|
|
1503
|
-
}
|
|
1504
|
-
if (windowParts.length > 0) {
|
|
1505
|
-
if (context.isPretty() && windowParts.length > 1) {
|
|
1506
|
-
const formattedParts = windowParts.map(part => context.indent(part));
|
|
1507
|
-
result += ` OVER (${context.newline()}${formattedParts.join(context.newline())}${context.newline()})`;
|
|
1508
|
-
}
|
|
1509
|
-
else {
|
|
1510
|
-
result += ` OVER (${windowParts.join(' ')})`;
|
|
1511
|
-
}
|
|
1512
|
-
}
|
|
1513
|
-
else {
|
|
1514
|
-
result += ` OVER ()`;
|
|
1515
|
-
}
|
|
1516
|
-
}
|
|
1565
|
+
result += this.formatOverClause(node.over, context);
|
|
1517
1566
|
}
|
|
1518
1567
|
return result;
|
|
1519
1568
|
}
|
|
1569
|
+
formatOverClause(over, context) {
|
|
1570
|
+
if (over.name) {
|
|
1571
|
+
return ` OVER ${over.name}`;
|
|
1572
|
+
}
|
|
1573
|
+
const windowParts = [];
|
|
1574
|
+
if (over.refname) {
|
|
1575
|
+
windowParts.push(over.refname);
|
|
1576
|
+
}
|
|
1577
|
+
if (over.partitionClause) {
|
|
1578
|
+
const partitions = ListUtils.unwrapList(over.partitionClause);
|
|
1579
|
+
const partitionStrs = partitions.map(p => this.visit(p, context));
|
|
1580
|
+
windowParts.push(`PARTITION BY ${partitionStrs.join(', ')}`);
|
|
1581
|
+
}
|
|
1582
|
+
if (over.orderClause) {
|
|
1583
|
+
const orders = ListUtils.unwrapList(over.orderClause);
|
|
1584
|
+
const orderStrs = orders.map(o => this.visit(o, context));
|
|
1585
|
+
windowParts.push(`ORDER BY ${orderStrs.join(', ')}`);
|
|
1586
|
+
}
|
|
1587
|
+
const frameClause = this.formatWindowFrame(over, context.spawn('FuncCall'));
|
|
1588
|
+
if (frameClause) {
|
|
1589
|
+
windowParts.push(frameClause);
|
|
1590
|
+
}
|
|
1591
|
+
if (windowParts.length === 0) {
|
|
1592
|
+
return ' OVER ()';
|
|
1593
|
+
}
|
|
1594
|
+
if (context.isPretty() && windowParts.length > 1) {
|
|
1595
|
+
const formattedParts = windowParts.map(part => context.indent(part));
|
|
1596
|
+
return ` OVER (${context.newline()}${formattedParts.join(context.newline())}${context.newline()})`;
|
|
1597
|
+
}
|
|
1598
|
+
return ` OVER (${windowParts.join(' ')})`;
|
|
1599
|
+
}
|
|
1520
1600
|
FuncExpr(node, context) {
|
|
1521
1601
|
const funcName = `func_${node.funcid}`;
|
|
1522
1602
|
const args = node.args ? ListUtils.unwrapList(node.args).map(arg => this.visit(arg, context)).join(', ') : '';
|
|
@@ -1527,39 +1607,39 @@ export class Deparser {
|
|
|
1527
1607
|
if (nodeAny.ival !== undefined) {
|
|
1528
1608
|
if (typeof nodeAny.ival === 'object' && nodeAny.ival !== null) {
|
|
1529
1609
|
if (nodeAny.ival.ival !== undefined) {
|
|
1530
|
-
return nodeAny.ival.ival
|
|
1610
|
+
return formatInteger(nodeAny.ival.ival);
|
|
1531
1611
|
}
|
|
1532
1612
|
else if (Object.keys(nodeAny.ival).length === 0) {
|
|
1533
1613
|
return '0';
|
|
1534
1614
|
}
|
|
1535
1615
|
else {
|
|
1536
|
-
return nodeAny.ival
|
|
1616
|
+
return formatInteger(nodeAny.ival);
|
|
1537
1617
|
}
|
|
1538
1618
|
}
|
|
1539
1619
|
else if (nodeAny.ival === null) {
|
|
1540
1620
|
return 'NULL';
|
|
1541
1621
|
}
|
|
1542
1622
|
else {
|
|
1543
|
-
return nodeAny.ival
|
|
1623
|
+
return formatInteger(nodeAny.ival);
|
|
1544
1624
|
}
|
|
1545
1625
|
}
|
|
1546
1626
|
else if (nodeAny.fval !== undefined) {
|
|
1547
1627
|
if (typeof nodeAny.fval === 'object' && nodeAny.fval !== null) {
|
|
1548
1628
|
if (nodeAny.fval.fval !== undefined) {
|
|
1549
|
-
return nodeAny.fval.fval
|
|
1629
|
+
return formatNumeric(nodeAny.fval.fval);
|
|
1550
1630
|
}
|
|
1551
1631
|
else if (Object.keys(nodeAny.fval).length === 0) {
|
|
1552
1632
|
return '0.0';
|
|
1553
1633
|
}
|
|
1554
1634
|
else {
|
|
1555
|
-
return nodeAny.fval
|
|
1635
|
+
return formatNumeric(nodeAny.fval);
|
|
1556
1636
|
}
|
|
1557
1637
|
}
|
|
1558
1638
|
else if (nodeAny.fval === null) {
|
|
1559
1639
|
return 'NULL';
|
|
1560
1640
|
}
|
|
1561
1641
|
else {
|
|
1562
|
-
return nodeAny.fval
|
|
1642
|
+
return formatNumeric(nodeAny.fval);
|
|
1563
1643
|
}
|
|
1564
1644
|
}
|
|
1565
1645
|
else if (nodeAny.sval !== undefined) {
|
|
@@ -1606,15 +1686,7 @@ export class Deparser {
|
|
|
1606
1686
|
else if (nodeAny.bsval !== undefined) {
|
|
1607
1687
|
if (typeof nodeAny.bsval === 'object' && nodeAny.bsval !== null) {
|
|
1608
1688
|
if (nodeAny.bsval.bsval !== undefined) {
|
|
1609
|
-
|
|
1610
|
-
// Check if this is a hexadecimal bit string (starts with x and contains only hex digits)
|
|
1611
|
-
if (bsval.startsWith('x') && /^x[0-9A-Fa-f]+$/.test(bsval)) {
|
|
1612
|
-
return `x'${bsval.substring(1)}'`;
|
|
1613
|
-
}
|
|
1614
|
-
if (bsval.startsWith('b')) {
|
|
1615
|
-
return `b'${bsval.substring(1)}'`;
|
|
1616
|
-
}
|
|
1617
|
-
return `b'${bsval}'`;
|
|
1689
|
+
return formatBitString(nodeAny.bsval.bsval);
|
|
1618
1690
|
}
|
|
1619
1691
|
else if (Object.keys(nodeAny.bsval).length === 0) {
|
|
1620
1692
|
return "''";
|
|
@@ -1627,23 +1699,15 @@ export class Deparser {
|
|
|
1627
1699
|
return 'NULL';
|
|
1628
1700
|
}
|
|
1629
1701
|
else {
|
|
1630
|
-
|
|
1631
|
-
// Check if this is a hexadecimal bit string (starts with x and contains only hex digits)
|
|
1632
|
-
if (bsval.startsWith('x') && /^x[0-9A-Fa-f]+$/.test(bsval)) {
|
|
1633
|
-
return `x'${bsval.substring(1)}'`;
|
|
1634
|
-
}
|
|
1635
|
-
if (bsval.startsWith('b')) {
|
|
1636
|
-
return `b'${bsval.substring(1)}'`;
|
|
1637
|
-
}
|
|
1638
|
-
return `b'${bsval}'`;
|
|
1702
|
+
return formatBitString(nodeAny.bsval);
|
|
1639
1703
|
}
|
|
1640
1704
|
}
|
|
1641
1705
|
if (nodeAny.val) {
|
|
1642
1706
|
if (nodeAny.val.Integer?.ival !== undefined) {
|
|
1643
|
-
return nodeAny.val.Integer.ival
|
|
1707
|
+
return formatInteger(nodeAny.val.Integer.ival);
|
|
1644
1708
|
}
|
|
1645
1709
|
else if (nodeAny.val.Float?.fval !== undefined) {
|
|
1646
|
-
return nodeAny.val.Float.fval
|
|
1710
|
+
return formatNumeric(nodeAny.val.Float.fval);
|
|
1647
1711
|
}
|
|
1648
1712
|
else if (nodeAny.val.String?.sval !== undefined) {
|
|
1649
1713
|
return QuoteUtils.escape(nodeAny.val.String.sval);
|
|
@@ -1652,7 +1716,7 @@ export class Deparser {
|
|
|
1652
1716
|
return nodeAny.val.Boolean.boolval ? 'true' : 'false';
|
|
1653
1717
|
}
|
|
1654
1718
|
else if (nodeAny.val.BitString?.bsval !== undefined) {
|
|
1655
|
-
return nodeAny.val.BitString.bsval;
|
|
1719
|
+
return formatBitString(nodeAny.val.BitString.bsval);
|
|
1656
1720
|
}
|
|
1657
1721
|
}
|
|
1658
1722
|
if (nodeAny.isnull === true) {
|
|
@@ -1664,15 +1728,15 @@ export class Deparser {
|
|
|
1664
1728
|
}
|
|
1665
1729
|
if (nodeAny.Integer !== undefined) {
|
|
1666
1730
|
if (typeof nodeAny.Integer === 'object' && nodeAny.Integer.ival !== undefined) {
|
|
1667
|
-
return nodeAny.Integer.ival
|
|
1731
|
+
return formatInteger(nodeAny.Integer.ival);
|
|
1668
1732
|
}
|
|
1669
|
-
return nodeAny.Integer
|
|
1733
|
+
return formatInteger(nodeAny.Integer);
|
|
1670
1734
|
}
|
|
1671
1735
|
if (nodeAny.Float !== undefined) {
|
|
1672
1736
|
if (typeof nodeAny.Float === 'object' && nodeAny.Float.fval !== undefined) {
|
|
1673
|
-
return nodeAny.Float.fval
|
|
1737
|
+
return formatNumeric(nodeAny.Float.fval);
|
|
1674
1738
|
}
|
|
1675
|
-
return nodeAny.Float
|
|
1739
|
+
return formatNumeric(nodeAny.Float);
|
|
1676
1740
|
}
|
|
1677
1741
|
if (nodeAny.String !== undefined) {
|
|
1678
1742
|
if (typeof nodeAny.String === 'object' && nodeAny.String.sval !== undefined) {
|
|
@@ -2134,7 +2198,9 @@ export class Deparser {
|
|
|
2134
2198
|
A_Indirection(node, context) {
|
|
2135
2199
|
let argStr = this.visit(node.arg, context);
|
|
2136
2200
|
const argType = this.getNodeType(node.arg);
|
|
2137
|
-
|
|
2201
|
+
// Grammar: only a parameter takes indirection bare; every other target is
|
|
2202
|
+
// `'(' a_expr ')' opt_indirection` (a plain column subscript is the same node).
|
|
2203
|
+
if (argType !== 'ParamRef') {
|
|
2138
2204
|
argStr = `(${argStr})`;
|
|
2139
2205
|
}
|
|
2140
2206
|
const output = [argStr];
|
|
@@ -2162,21 +2228,11 @@ export class Deparser {
|
|
|
2162
2228
|
if (context.isPretty() && args.length > 0) {
|
|
2163
2229
|
for (const arg of args) {
|
|
2164
2230
|
const whenClause = this.visit(arg, context);
|
|
2165
|
-
|
|
2166
|
-
output.push(context.newline() + whenClause);
|
|
2167
|
-
}
|
|
2168
|
-
else {
|
|
2169
|
-
output.push(context.newline() + context.indent(whenClause));
|
|
2170
|
-
}
|
|
2231
|
+
output.push(context.newline() + context.indent(whenClause));
|
|
2171
2232
|
}
|
|
2172
2233
|
if (node.defresult) {
|
|
2173
2234
|
const elseResult = this.visit(node.defresult, context);
|
|
2174
|
-
|
|
2175
|
-
output.push(context.newline() + 'ELSE ' + elseResult);
|
|
2176
|
-
}
|
|
2177
|
-
else {
|
|
2178
|
-
output.push(context.newline() + context.indent('ELSE ' + elseResult));
|
|
2179
|
-
}
|
|
2235
|
+
output.push(context.newline() + context.indent('ELSE ' + elseResult));
|
|
2180
2236
|
}
|
|
2181
2237
|
output.push(context.newline() + 'END');
|
|
2182
2238
|
return output.join(' ');
|
|
@@ -2291,16 +2347,16 @@ export class Deparser {
|
|
|
2291
2347
|
}
|
|
2292
2348
|
preserveOperatorDefElemCase(defName) {
|
|
2293
2349
|
const caseMap = {
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2350
|
+
leftarg: 'Leftarg',
|
|
2351
|
+
rightarg: 'Rightarg',
|
|
2352
|
+
procedure: 'Procedure',
|
|
2353
|
+
function: 'Function',
|
|
2354
|
+
commutator: 'Commutator',
|
|
2355
|
+
negator: 'Negator',
|
|
2356
|
+
restrict: 'Restrict',
|
|
2357
|
+
join: 'Join',
|
|
2358
|
+
hashes: 'Hashes',
|
|
2359
|
+
merges: 'Merges'
|
|
2304
2360
|
};
|
|
2305
2361
|
return caseMap[defName.toLowerCase()] || defName;
|
|
2306
2362
|
}
|
|
@@ -2323,24 +2379,16 @@ export class Deparser {
|
|
|
2323
2379
|
return QuoteUtils.quoteIdentifier(value);
|
|
2324
2380
|
}
|
|
2325
2381
|
Integer(node, context) {
|
|
2326
|
-
return node.ival
|
|
2382
|
+
return node.ival === undefined || node.ival === null ? '0' : formatInteger(node.ival);
|
|
2327
2383
|
}
|
|
2328
2384
|
Float(node, context) {
|
|
2329
|
-
return node.fval
|
|
2385
|
+
return node.fval ? formatNumeric(node.fval) : '0.0';
|
|
2330
2386
|
}
|
|
2331
2387
|
Boolean(node, context) {
|
|
2332
2388
|
return node.boolval ? 'true' : 'false';
|
|
2333
2389
|
}
|
|
2334
2390
|
BitString(node, context) {
|
|
2335
|
-
|
|
2336
|
-
if (node.bsval.startsWith('x')) {
|
|
2337
|
-
return `x'${node.bsval.substring(1)}'`;
|
|
2338
|
-
}
|
|
2339
|
-
if (node.bsval.startsWith('b')) {
|
|
2340
|
-
return `b'${node.bsval.substring(1)}'`;
|
|
2341
|
-
}
|
|
2342
|
-
// Fallback for raw values without prefix
|
|
2343
|
-
return `b'${node.bsval}'`;
|
|
2391
|
+
return formatBitString(node.bsval);
|
|
2344
2392
|
}
|
|
2345
2393
|
Null(node, context) {
|
|
2346
2394
|
return 'NULL';
|
|
@@ -2379,7 +2427,7 @@ export class Deparser {
|
|
|
2379
2427
|
output.push(context.parens(elementStrs.join(', ')));
|
|
2380
2428
|
}
|
|
2381
2429
|
}
|
|
2382
|
-
else if (node.tableElts) {
|
|
2430
|
+
else if (node.tableElts && !node.partbound) {
|
|
2383
2431
|
const elements = ListUtils.unwrapList(node.tableElts);
|
|
2384
2432
|
const elementStrs = elements.map(el => {
|
|
2385
2433
|
return this.deparse(el, context.spawn('CreateStmt'));
|
|
@@ -2407,7 +2455,16 @@ export class Deparser {
|
|
|
2407
2455
|
const inherits = ListUtils.unwrapList(node.inhRelations);
|
|
2408
2456
|
const inheritStrs = inherits.map(rel => this.visit(rel, context));
|
|
2409
2457
|
output.push(inheritStrs[0]);
|
|
2410
|
-
if (node.
|
|
2458
|
+
if (node.tableElts) {
|
|
2459
|
+
const elementStrs = ListUtils.unwrapList(node.tableElts).map(el => {
|
|
2460
|
+
return this.deparse(el, context.spawn('CreateStmt'));
|
|
2461
|
+
});
|
|
2462
|
+
output.push(context.parens(elementStrs.join(', ')));
|
|
2463
|
+
}
|
|
2464
|
+
if (node.partbound.is_default) {
|
|
2465
|
+
output.push('DEFAULT');
|
|
2466
|
+
}
|
|
2467
|
+
else if (node.partbound.strategy === 'l' && node.partbound.listdatums) {
|
|
2411
2468
|
output.push('FOR VALUES IN');
|
|
2412
2469
|
const listValues = ListUtils.unwrapList(node.partbound.listdatums)
|
|
2413
2470
|
.map(datum => this.visit(datum, context))
|
|
@@ -2430,13 +2487,9 @@ export class Deparser {
|
|
|
2430
2487
|
output.push(`(${upperValues})`);
|
|
2431
2488
|
}
|
|
2432
2489
|
}
|
|
2433
|
-
else if (node.partbound.strategy === 'h'
|
|
2490
|
+
else if (node.partbound.strategy === 'h') {
|
|
2434
2491
|
output.push('FOR VALUES WITH');
|
|
2435
|
-
|
|
2436
|
-
output.push(`(MODULUS ${node.partbound.modulus}, REMAINDER ${remainder})`);
|
|
2437
|
-
}
|
|
2438
|
-
else if (node.partbound.is_default) {
|
|
2439
|
-
output.push('DEFAULT');
|
|
2492
|
+
output.push(`(MODULUS ${node.partbound.modulus ?? 0}, REMAINDER ${node.partbound.remainder ?? 0})`);
|
|
2440
2493
|
}
|
|
2441
2494
|
}
|
|
2442
2495
|
else if (node.inhRelations) {
|
|
@@ -2502,6 +2555,12 @@ export class Deparser {
|
|
|
2502
2555
|
if (node.typeName) {
|
|
2503
2556
|
output.push(this.TypeName(node.typeName, context));
|
|
2504
2557
|
}
|
|
2558
|
+
if (node.storage_name) {
|
|
2559
|
+
output.push('STORAGE', node.storage_name.toUpperCase());
|
|
2560
|
+
}
|
|
2561
|
+
if (node.compression) {
|
|
2562
|
+
output.push('COMPRESSION', QuoteUtils.quoteIdentifier(node.compression));
|
|
2563
|
+
}
|
|
2505
2564
|
if (node.fdwoptions && node.fdwoptions.length > 0) {
|
|
2506
2565
|
output.push('OPTIONS');
|
|
2507
2566
|
const columnContext = context.spawn('ColumnDef');
|
|
@@ -2536,7 +2595,7 @@ export class Deparser {
|
|
|
2536
2595
|
Constraint(node, context) {
|
|
2537
2596
|
const output = [];
|
|
2538
2597
|
// Handle constraint name if present
|
|
2539
|
-
if (node.conname && (node.contype === 'CONSTR_CHECK' || node.contype === 'CONSTR_UNIQUE' || node.contype === 'CONSTR_PRIMARY' || node.contype === 'CONSTR_FOREIGN')) {
|
|
2598
|
+
if (node.conname && (node.contype === 'CONSTR_CHECK' || node.contype === 'CONSTR_UNIQUE' || node.contype === 'CONSTR_PRIMARY' || node.contype === 'CONSTR_FOREIGN' || node.contype === 'CONSTR_NOTNULL' || node.contype === 'CONSTR_EXCLUSION')) {
|
|
2540
2599
|
output.push('CONSTRAINT');
|
|
2541
2600
|
output.push(QuoteUtils.quoteIdentifier(node.conname));
|
|
2542
2601
|
}
|
|
@@ -2546,11 +2605,24 @@ export class Deparser {
|
|
|
2546
2605
|
break;
|
|
2547
2606
|
case 'CONSTR_NOTNULL':
|
|
2548
2607
|
output.push('NOT NULL');
|
|
2608
|
+
if (node.keys && node.keys.length > 0 && !context.isColumnConstraint) {
|
|
2609
|
+
const keyList = ListUtils.unwrapList(node.keys)
|
|
2610
|
+
.map(key => this.visit(key, context))
|
|
2611
|
+
.join(', ');
|
|
2612
|
+
output.push(keyList);
|
|
2613
|
+
}
|
|
2614
|
+
if (node.is_no_inherit) {
|
|
2615
|
+
output.push('NO INHERIT');
|
|
2616
|
+
}
|
|
2617
|
+
if (node.skip_validation) {
|
|
2618
|
+
output.push('NOT VALID');
|
|
2619
|
+
}
|
|
2549
2620
|
break;
|
|
2550
2621
|
case 'CONSTR_DEFAULT':
|
|
2551
2622
|
output.push('DEFAULT');
|
|
2552
2623
|
if (node.raw_expr) {
|
|
2553
|
-
|
|
2624
|
+
const defaultExpr = this.visit(node.raw_expr, context);
|
|
2625
|
+
output.push(this.needsBExprParens(node.raw_expr) ? `(${defaultExpr})` : defaultExpr);
|
|
2554
2626
|
}
|
|
2555
2627
|
break;
|
|
2556
2628
|
case 'CONSTR_CHECK':
|
|
@@ -2574,8 +2646,13 @@ export class Deparser {
|
|
|
2574
2646
|
output.push(context.parens(this.visit(node.raw_expr, context)));
|
|
2575
2647
|
}
|
|
2576
2648
|
}
|
|
2577
|
-
//
|
|
2578
|
-
|
|
2649
|
+
// PG18: constraints are enforced unless explicitly NOT ENFORCED
|
|
2650
|
+
// (libpg-query omits false booleans, so undefined means not enforced)
|
|
2651
|
+
if (node.is_enforced !== true && !context.isDomainConstraint) {
|
|
2652
|
+
output.push('NOT ENFORCED');
|
|
2653
|
+
}
|
|
2654
|
+
else if (node.skip_validation) {
|
|
2655
|
+
// NOT ENFORCED implies NOT VALID, so only emit NOT VALID when enforced
|
|
2579
2656
|
output.push('NOT VALID');
|
|
2580
2657
|
}
|
|
2581
2658
|
// Handle NO INHERIT for check constraints - only for table constraints, not domain constraints
|
|
@@ -2595,7 +2672,7 @@ export class Deparser {
|
|
|
2595
2672
|
if (node.raw_expr) {
|
|
2596
2673
|
output.push(context.parens(this.visit(node.raw_expr, context)));
|
|
2597
2674
|
}
|
|
2598
|
-
output.push('STORED');
|
|
2675
|
+
output.push(node.generated_kind === 'v' ? 'VIRTUAL' : 'STORED');
|
|
2599
2676
|
break;
|
|
2600
2677
|
case 'CONSTR_IDENTITY':
|
|
2601
2678
|
output.push('GENERATED');
|
|
@@ -2671,10 +2748,12 @@ export class Deparser {
|
|
|
2671
2748
|
case 'CONSTR_PRIMARY':
|
|
2672
2749
|
output.push('PRIMARY KEY');
|
|
2673
2750
|
if (node.keys && node.keys.length > 0) {
|
|
2674
|
-
const
|
|
2675
|
-
.map(key => this.visit(key, context))
|
|
2676
|
-
|
|
2677
|
-
|
|
2751
|
+
const keyItems = ListUtils.unwrapList(node.keys)
|
|
2752
|
+
.map(key => this.visit(key, context));
|
|
2753
|
+
if (node.without_overlaps) {
|
|
2754
|
+
keyItems[keyItems.length - 1] += ' WITHOUT OVERLAPS';
|
|
2755
|
+
}
|
|
2756
|
+
output.push(`(${keyItems.join(', ')})`);
|
|
2678
2757
|
}
|
|
2679
2758
|
if (node.indexname) {
|
|
2680
2759
|
output.push('USING INDEX');
|
|
@@ -2692,10 +2771,12 @@ export class Deparser {
|
|
|
2692
2771
|
output.push('NULLS NOT DISTINCT');
|
|
2693
2772
|
}
|
|
2694
2773
|
if (node.keys && node.keys.length > 0) {
|
|
2695
|
-
const
|
|
2696
|
-
.map(key => this.visit(key, context))
|
|
2697
|
-
|
|
2698
|
-
|
|
2774
|
+
const keyItems = ListUtils.unwrapList(node.keys)
|
|
2775
|
+
.map(key => this.visit(key, context));
|
|
2776
|
+
if (node.without_overlaps) {
|
|
2777
|
+
keyItems[keyItems.length - 1] += ' WITHOUT OVERLAPS';
|
|
2778
|
+
}
|
|
2779
|
+
output.push(`(${keyItems.join(', ')})`);
|
|
2699
2780
|
}
|
|
2700
2781
|
if (node.indexname) {
|
|
2701
2782
|
output.push('USING INDEX');
|
|
@@ -2708,20 +2789,14 @@ export class Deparser {
|
|
|
2708
2789
|
if (context.isPretty()) {
|
|
2709
2790
|
output.push('\n' + context.indent('FOREIGN KEY'));
|
|
2710
2791
|
if (node.fk_attrs && node.fk_attrs.length > 0) {
|
|
2711
|
-
|
|
2712
|
-
.map(attr => this.visit(attr, context))
|
|
2713
|
-
.join(', ');
|
|
2714
|
-
output.push(`(${fkAttrs})`);
|
|
2792
|
+
output.push(`(${this.deparseKeyAttrs(node.fk_attrs, node.fk_with_period, context)})`);
|
|
2715
2793
|
}
|
|
2716
2794
|
output.push('\n' + context.indent('REFERENCES'));
|
|
2717
2795
|
}
|
|
2718
2796
|
else {
|
|
2719
2797
|
output.push('FOREIGN KEY');
|
|
2720
2798
|
if (node.fk_attrs && node.fk_attrs.length > 0) {
|
|
2721
|
-
|
|
2722
|
-
.map(attr => this.visit(attr, context))
|
|
2723
|
-
.join(', ');
|
|
2724
|
-
output.push(`(${fkAttrs})`);
|
|
2799
|
+
output.push(`(${this.deparseKeyAttrs(node.fk_attrs, node.fk_with_period, context)})`);
|
|
2725
2800
|
}
|
|
2726
2801
|
output.push('REFERENCES');
|
|
2727
2802
|
}
|
|
@@ -2744,9 +2819,7 @@ export class Deparser {
|
|
|
2744
2819
|
}
|
|
2745
2820
|
}
|
|
2746
2821
|
if (node.pk_attrs && node.pk_attrs.length > 0) {
|
|
2747
|
-
const pkAttrs =
|
|
2748
|
-
.map(attr => this.visit(attr, context))
|
|
2749
|
-
.join(', ');
|
|
2822
|
+
const pkAttrs = this.deparseKeyAttrs(node.pk_attrs, node.pk_with_period, context);
|
|
2750
2823
|
if (context.isPretty() && !context.isColumnConstraint) {
|
|
2751
2824
|
const lastIndex = output.length - 1;
|
|
2752
2825
|
if (lastIndex >= 0) {
|
|
@@ -2817,6 +2890,15 @@ export class Deparser {
|
|
|
2817
2890
|
deleteClause += 'SET DEFAULT';
|
|
2818
2891
|
break;
|
|
2819
2892
|
}
|
|
2893
|
+
if ((node.fk_del_action === 'n' || node.fk_del_action === 'd') && node.fk_del_set_cols && node.fk_del_set_cols.length > 0) {
|
|
2894
|
+
const setColumns = ListUtils.unwrapList(node.fk_del_set_cols)
|
|
2895
|
+
.map(column => {
|
|
2896
|
+
const stringNode = column.String;
|
|
2897
|
+
return QuoteUtils.quoteIdentifier(stringNode.sval || stringNode.str || '');
|
|
2898
|
+
})
|
|
2899
|
+
.join(', ');
|
|
2900
|
+
deleteClause += ` (${setColumns})`;
|
|
2901
|
+
}
|
|
2820
2902
|
if (context.isPretty()) {
|
|
2821
2903
|
output.push('\n' + context.indent(deleteClause));
|
|
2822
2904
|
}
|
|
@@ -2825,8 +2907,18 @@ export class Deparser {
|
|
|
2825
2907
|
output.push(deleteClause.replace('ON DELETE ', ''));
|
|
2826
2908
|
}
|
|
2827
2909
|
}
|
|
2828
|
-
//
|
|
2829
|
-
|
|
2910
|
+
// PG18: constraints are enforced unless explicitly NOT ENFORCED
|
|
2911
|
+
// (libpg-query omits false booleans, so undefined means not enforced)
|
|
2912
|
+
if (node.is_enforced !== true && !context.isDomainConstraint) {
|
|
2913
|
+
if (context.isPretty() && !context.isColumnConstraint) {
|
|
2914
|
+
output.push('\n' + context.indent('NOT ENFORCED'));
|
|
2915
|
+
}
|
|
2916
|
+
else {
|
|
2917
|
+
output.push('NOT ENFORCED');
|
|
2918
|
+
}
|
|
2919
|
+
}
|
|
2920
|
+
else if (node.skip_validation && !context.isDomainConstraint) {
|
|
2921
|
+
// NOT ENFORCED implies NOT VALID, so only emit NOT VALID when enforced
|
|
2830
2922
|
if (context.isPretty() && !context.isColumnConstraint) {
|
|
2831
2923
|
output.push('\n' + context.indent('NOT VALID'));
|
|
2832
2924
|
}
|
|
@@ -2847,6 +2939,12 @@ export class Deparser {
|
|
|
2847
2939
|
case 'CONSTR_ATTR_IMMEDIATE':
|
|
2848
2940
|
output.push('INITIALLY IMMEDIATE');
|
|
2849
2941
|
break;
|
|
2942
|
+
case 'CONSTR_ATTR_ENFORCED':
|
|
2943
|
+
output.push('ENFORCED');
|
|
2944
|
+
break;
|
|
2945
|
+
case 'CONSTR_ATTR_NOT_ENFORCED':
|
|
2946
|
+
output.push('NOT ENFORCED');
|
|
2947
|
+
break;
|
|
2850
2948
|
case 'CONSTR_EXCLUSION':
|
|
2851
2949
|
output.push('EXCLUDE');
|
|
2852
2950
|
if (node.access_method) {
|
|
@@ -2888,7 +2986,7 @@ export class Deparser {
|
|
|
2888
2986
|
break;
|
|
2889
2987
|
}
|
|
2890
2988
|
// Handle deferrable constraints for all constraint types that support it
|
|
2891
|
-
if (node.contype === 'CONSTR_PRIMARY' || node.contype === 'CONSTR_UNIQUE' || node.contype === 'CONSTR_FOREIGN') {
|
|
2989
|
+
if (node.contype === 'CONSTR_PRIMARY' || node.contype === 'CONSTR_UNIQUE' || node.contype === 'CONSTR_FOREIGN' || node.contype === 'CONSTR_EXCLUSION') {
|
|
2892
2990
|
if (node.deferrable) {
|
|
2893
2991
|
if (context.isPretty() && node.contype === 'CONSTR_FOREIGN') {
|
|
2894
2992
|
output.push('\n' + context.indent('DEFERRABLE'));
|
|
@@ -2917,6 +3015,9 @@ export class Deparser {
|
|
|
2917
3015
|
output.push('NOT DEFERRABLE');
|
|
2918
3016
|
}
|
|
2919
3017
|
}
|
|
3018
|
+
else if (node.initdeferred) {
|
|
3019
|
+
output.push('INITIALLY DEFERRED');
|
|
3020
|
+
}
|
|
2920
3021
|
}
|
|
2921
3022
|
if (context.isPretty() && node.contype === 'CONSTR_FOREIGN') {
|
|
2922
3023
|
let result = '';
|
|
@@ -2982,6 +3083,9 @@ export class Deparser {
|
|
|
2982
3083
|
output.push(node.name);
|
|
2983
3084
|
}
|
|
2984
3085
|
const windowParts = [];
|
|
3086
|
+
if (node.refname) {
|
|
3087
|
+
windowParts.push(node.refname);
|
|
3088
|
+
}
|
|
2985
3089
|
if (node.partitionClause) {
|
|
2986
3090
|
const partitions = ListUtils.unwrapList(node.partitionClause);
|
|
2987
3091
|
const partitionStrs = partitions.map(p => this.visit(p, context));
|
|
@@ -2992,12 +3096,9 @@ export class Deparser {
|
|
|
2992
3096
|
const orderStrs = orders.map(o => this.visit(o, context));
|
|
2993
3097
|
windowParts.push(`ORDER BY ${orderStrs.join(', ')}`);
|
|
2994
3098
|
}
|
|
2995
|
-
|
|
2996
|
-
if (
|
|
2997
|
-
|
|
2998
|
-
if (frameClause) {
|
|
2999
|
-
windowParts.push(frameClause);
|
|
3000
|
-
}
|
|
3099
|
+
const frameClause = this.formatWindowFrame(node, context.spawn('WindowDef'));
|
|
3100
|
+
if (frameClause) {
|
|
3101
|
+
windowParts.push(frameClause);
|
|
3001
3102
|
}
|
|
3002
3103
|
if (windowParts.length > 0) {
|
|
3003
3104
|
if (node.name) {
|
|
@@ -3014,114 +3115,96 @@ export class Deparser {
|
|
|
3014
3115
|
}
|
|
3015
3116
|
return output.join(' ');
|
|
3016
3117
|
}
|
|
3118
|
+
formatIdentityColumnOptions(def, context) {
|
|
3119
|
+
const seqContext = context.spawn('AlterSeqStmt');
|
|
3120
|
+
return ListUtils.unwrapList(def).map(item => {
|
|
3121
|
+
const el = item.DefElem;
|
|
3122
|
+
if (!el)
|
|
3123
|
+
return this.visit(item, context);
|
|
3124
|
+
if (el.defname === 'generated') {
|
|
3125
|
+
const ival = el.arg?.Integer?.ival ?? 0;
|
|
3126
|
+
return ival === 97 ? 'SET GENERATED ALWAYS' : 'SET GENERATED BY DEFAULT';
|
|
3127
|
+
}
|
|
3128
|
+
if (el.defname === 'restart') {
|
|
3129
|
+
return el.arg ? `RESTART WITH ${this.visit(el.arg, context)}` : 'RESTART';
|
|
3130
|
+
}
|
|
3131
|
+
return `SET ${this.visit(item, seqContext)}`;
|
|
3132
|
+
}).join(' ');
|
|
3133
|
+
}
|
|
3134
|
+
// b_expr (used by DEFAULT) excludes IN/LIKE/BETWEEN and boolean operators.
|
|
3135
|
+
needsBExprParens(node) {
|
|
3136
|
+
const nodeType = this.getNodeType(node);
|
|
3137
|
+
if (nodeType === 'BoolExpr')
|
|
3138
|
+
return true;
|
|
3139
|
+
if (nodeType === 'A_Expr') {
|
|
3140
|
+
const kind = this.getNodeData(node).kind;
|
|
3141
|
+
return kind !== 'AEXPR_OP' && kind !== 'AEXPR_DISTINCT' && kind !== 'AEXPR_NOT_DISTINCT';
|
|
3142
|
+
}
|
|
3143
|
+
return false;
|
|
3144
|
+
}
|
|
3017
3145
|
formatWindowFrame(node, context) {
|
|
3018
|
-
if (!node.frameOptions)
|
|
3146
|
+
if (!node.frameOptions || !(node.frameOptions & FRAMEOPTION_NONDEFAULT))
|
|
3019
3147
|
return null;
|
|
3020
3148
|
const frameOptions = node.frameOptions;
|
|
3021
|
-
const EXCLUDE_MASK = 0x8000 | 0x10000 | 0x20000;
|
|
3022
|
-
const baseFrameOptions = frameOptions & ~EXCLUDE_MASK;
|
|
3023
3149
|
const frameParts = [];
|
|
3024
|
-
if (frameOptions &
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
frameParts.push('GROUPS');
|
|
3033
|
-
}
|
|
3150
|
+
if (frameOptions & FRAMEOPTION_RANGE) {
|
|
3151
|
+
frameParts.push('RANGE');
|
|
3152
|
+
}
|
|
3153
|
+
else if (frameOptions & FRAMEOPTION_ROWS) {
|
|
3154
|
+
frameParts.push('ROWS');
|
|
3155
|
+
}
|
|
3156
|
+
else if (frameOptions & FRAMEOPTION_GROUPS) {
|
|
3157
|
+
frameParts.push('GROUPS');
|
|
3034
3158
|
}
|
|
3035
3159
|
if (frameParts.length === 0)
|
|
3036
3160
|
return null;
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
boundsParts.push('CURRENT ROW');
|
|
3041
|
-
boundsParts.push('AND UNBOUNDED FOLLOWING');
|
|
3161
|
+
let start = null;
|
|
3162
|
+
if (frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING) {
|
|
3163
|
+
start = 'UNBOUNDED PRECEDING';
|
|
3042
3164
|
}
|
|
3043
|
-
else if (
|
|
3044
|
-
|
|
3045
|
-
boundsParts.push('AND CURRENT ROW');
|
|
3165
|
+
else if (frameOptions & FRAMEOPTION_START_UNBOUNDED_FOLLOWING) {
|
|
3166
|
+
start = 'UNBOUNDED FOLLOWING';
|
|
3046
3167
|
}
|
|
3047
|
-
else if (
|
|
3048
|
-
|
|
3049
|
-
boundsParts.push(`${this.visit(node.startOffset, context)} PRECEDING`);
|
|
3050
|
-
boundsParts.push(`AND ${this.visit(node.endOffset, context)} FOLLOWING`);
|
|
3051
|
-
}
|
|
3168
|
+
else if (frameOptions & FRAMEOPTION_START_CURRENT_ROW) {
|
|
3169
|
+
start = 'CURRENT ROW';
|
|
3052
3170
|
}
|
|
3053
|
-
else if (
|
|
3054
|
-
|
|
3055
|
-
boundsParts.push('AND CURRENT ROW');
|
|
3171
|
+
else if (frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING && node.startOffset) {
|
|
3172
|
+
start = `${this.visit(node.startOffset, context)} PRECEDING`;
|
|
3056
3173
|
}
|
|
3057
|
-
else if (
|
|
3058
|
-
|
|
3059
|
-
if (node.endOffset) {
|
|
3060
|
-
boundsParts.push(`AND ${this.visit(node.endOffset, context)} FOLLOWING`);
|
|
3061
|
-
}
|
|
3174
|
+
else if (frameOptions & FRAMEOPTION_START_OFFSET_FOLLOWING && node.startOffset) {
|
|
3175
|
+
start = `${this.visit(node.startOffset, context)} FOLLOWING`;
|
|
3062
3176
|
}
|
|
3063
|
-
|
|
3064
|
-
|
|
3177
|
+
let end = null;
|
|
3178
|
+
if (frameOptions & FRAMEOPTION_END_UNBOUNDED_PRECEDING) {
|
|
3179
|
+
end = 'UNBOUNDED PRECEDING';
|
|
3065
3180
|
}
|
|
3066
|
-
else {
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
}
|
|
3082
|
-
else if (frameOptions & 0x20) { // FRAMEOPTION_START_CURRENT_ROW
|
|
3083
|
-
boundsParts.push('CURRENT ROW');
|
|
3084
|
-
}
|
|
3085
|
-
// Handle end bound - prioritize explicit offset values over bit flags
|
|
3086
|
-
if (node.endOffset) {
|
|
3087
|
-
if (boundsParts.length > 0) {
|
|
3088
|
-
if (frameOptions & 0x1000) { // FRAMEOPTION_END_VALUE_PRECEDING
|
|
3089
|
-
boundsParts.push(`AND ${this.visit(node.endOffset, context)} PRECEDING`);
|
|
3090
|
-
}
|
|
3091
|
-
else if (frameOptions & 0x2000) { // FRAMEOPTION_END_VALUE_FOLLOWING
|
|
3092
|
-
boundsParts.push(`AND ${this.visit(node.endOffset, context)} FOLLOWING`);
|
|
3093
|
-
}
|
|
3094
|
-
else {
|
|
3095
|
-
boundsParts.push(`AND ${this.visit(node.endOffset, context)} FOLLOWING`);
|
|
3096
|
-
}
|
|
3097
|
-
}
|
|
3098
|
-
}
|
|
3099
|
-
else if (frameOptions & 0x80) { // FRAMEOPTION_END_UNBOUNDED_FOLLOWING
|
|
3100
|
-
if (boundsParts.length > 0) {
|
|
3101
|
-
boundsParts.push('AND UNBOUNDED FOLLOWING');
|
|
3102
|
-
}
|
|
3103
|
-
}
|
|
3104
|
-
else if (frameOptions & 0x100) { // FRAMEOPTION_END_CURRENT_ROW
|
|
3105
|
-
if (boundsParts.length > 0) {
|
|
3106
|
-
boundsParts.push('AND CURRENT ROW');
|
|
3107
|
-
}
|
|
3181
|
+
else if (frameOptions & FRAMEOPTION_END_UNBOUNDED_FOLLOWING) {
|
|
3182
|
+
end = 'UNBOUNDED FOLLOWING';
|
|
3183
|
+
}
|
|
3184
|
+
else if (frameOptions & FRAMEOPTION_END_CURRENT_ROW) {
|
|
3185
|
+
end = 'CURRENT ROW';
|
|
3186
|
+
}
|
|
3187
|
+
else if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING && node.endOffset) {
|
|
3188
|
+
end = `${this.visit(node.endOffset, context)} PRECEDING`;
|
|
3189
|
+
}
|
|
3190
|
+
else if (frameOptions & FRAMEOPTION_END_OFFSET_FOLLOWING && node.endOffset) {
|
|
3191
|
+
end = `${this.visit(node.endOffset, context)} FOLLOWING`;
|
|
3192
|
+
}
|
|
3193
|
+
if (start) {
|
|
3194
|
+
if (frameOptions & FRAMEOPTION_BETWEEN) {
|
|
3195
|
+
frameParts.push(`BETWEEN ${start} AND ${end ?? 'CURRENT ROW'}`);
|
|
3108
3196
|
}
|
|
3109
|
-
else
|
|
3110
|
-
|
|
3197
|
+
else {
|
|
3198
|
+
frameParts.push(start);
|
|
3111
3199
|
}
|
|
3112
3200
|
}
|
|
3113
|
-
if (
|
|
3114
|
-
frameParts.push('BETWEEN');
|
|
3115
|
-
frameParts.push(boundsParts.join(' '));
|
|
3116
|
-
}
|
|
3117
|
-
// EXCLUDE clause
|
|
3118
|
-
if (frameOptions & 0x8000) { // FRAMEOPTION_EXCLUDE_CURRENT_ROW
|
|
3201
|
+
if (frameOptions & FRAMEOPTION_EXCLUDE_CURRENT_ROW) {
|
|
3119
3202
|
frameParts.push('EXCLUDE CURRENT ROW');
|
|
3120
3203
|
}
|
|
3121
|
-
else if (frameOptions &
|
|
3204
|
+
else if (frameOptions & FRAMEOPTION_EXCLUDE_GROUP) {
|
|
3122
3205
|
frameParts.push('EXCLUDE GROUP');
|
|
3123
3206
|
}
|
|
3124
|
-
else if (frameOptions &
|
|
3207
|
+
else if (frameOptions & FRAMEOPTION_EXCLUDE_TIES) {
|
|
3125
3208
|
frameParts.push('EXCLUDE TIES');
|
|
3126
3209
|
}
|
|
3127
3210
|
return frameParts.join(' ');
|
|
@@ -3200,8 +3283,35 @@ export class Deparser {
|
|
|
3200
3283
|
if (node.ctequery) {
|
|
3201
3284
|
output.push(context.parens(this.visit(node.ctequery, context)));
|
|
3202
3285
|
}
|
|
3286
|
+
if (node.search_clause) {
|
|
3287
|
+
const sc = node.search_clause;
|
|
3288
|
+
const cols = ListUtils.unwrapList(sc.search_col_list).map(c => this.visit(c, context));
|
|
3289
|
+
output.push('SEARCH', sc.search_breadth_first ? 'BREADTH' : 'DEPTH', 'FIRST BY');
|
|
3290
|
+
output.push(cols.join(', '));
|
|
3291
|
+
output.push('SET', QuoteUtils.quoteIdentifier(sc.search_seq_column));
|
|
3292
|
+
}
|
|
3293
|
+
if (node.cycle_clause) {
|
|
3294
|
+
const cc = node.cycle_clause;
|
|
3295
|
+
const cols = ListUtils.unwrapList(cc.cycle_col_list).map(c => this.visit(c, context));
|
|
3296
|
+
output.push('CYCLE', cols.join(', '));
|
|
3297
|
+
output.push('SET', QuoteUtils.quoteIdentifier(cc.cycle_mark_column));
|
|
3298
|
+
if (cc.cycle_mark_value && cc.cycle_mark_default) {
|
|
3299
|
+
output.push('TO', this.formatAexprConst(cc.cycle_mark_value, context));
|
|
3300
|
+
output.push('DEFAULT', this.formatAexprConst(cc.cycle_mark_default, context));
|
|
3301
|
+
}
|
|
3302
|
+
output.push('USING', QuoteUtils.quoteIdentifier(cc.cycle_path_column));
|
|
3303
|
+
}
|
|
3203
3304
|
return output.join(' ');
|
|
3204
3305
|
}
|
|
3306
|
+
// Renders a constant in the restricted AexprConst grammar (e.g. `point '(1,1)'`
|
|
3307
|
+
// rather than `CAST('(1,1)' AS point)`).
|
|
3308
|
+
formatAexprConst(node, context) {
|
|
3309
|
+
const cast = node.TypeCast;
|
|
3310
|
+
if (cast?.arg?.A_Const?.sval && cast.typeName) {
|
|
3311
|
+
return `${this.TypeName(cast.typeName, context)} ${this.visit(cast.arg, context)}`;
|
|
3312
|
+
}
|
|
3313
|
+
return this.visit(node, context);
|
|
3314
|
+
}
|
|
3205
3315
|
ParamRef(node, context) {
|
|
3206
3316
|
return `$${node.number}`;
|
|
3207
3317
|
}
|
|
@@ -3470,9 +3580,8 @@ export class Deparser {
|
|
|
3470
3580
|
output.push('INCLUDE');
|
|
3471
3581
|
output.push(context.parens(includeStrs.join(', ')));
|
|
3472
3582
|
}
|
|
3473
|
-
if (node.
|
|
3474
|
-
output.push('
|
|
3475
|
-
output.push(this.visit(node.whereClause, context));
|
|
3583
|
+
if (node.nulls_not_distinct) {
|
|
3584
|
+
output.push('NULLS NOT DISTINCT');
|
|
3476
3585
|
}
|
|
3477
3586
|
if (node.options && node.options.length > 0) {
|
|
3478
3587
|
const indexContext = context.spawn('IndexStmt');
|
|
@@ -3480,13 +3589,14 @@ export class Deparser {
|
|
|
3480
3589
|
output.push('WITH');
|
|
3481
3590
|
output.push(context.parens(optionStrs.join(', ')));
|
|
3482
3591
|
}
|
|
3483
|
-
if (node.nulls_not_distinct) {
|
|
3484
|
-
output.push('NULLS NOT DISTINCT');
|
|
3485
|
-
}
|
|
3486
3592
|
if (node.tableSpace) {
|
|
3487
3593
|
output.push('TABLESPACE');
|
|
3488
3594
|
output.push(QuoteUtils.quoteIdentifier(node.tableSpace));
|
|
3489
3595
|
}
|
|
3596
|
+
if (node.whereClause) {
|
|
3597
|
+
output.push('WHERE');
|
|
3598
|
+
output.push(this.visit(node.whereClause, context));
|
|
3599
|
+
}
|
|
3490
3600
|
return output.join(' ');
|
|
3491
3601
|
}
|
|
3492
3602
|
IndexElem(node, context) {
|
|
@@ -3510,7 +3620,7 @@ export class Deparser {
|
|
|
3510
3620
|
const opclassOpts = ListUtils.unwrapList(node.opclassopts).map(opt => {
|
|
3511
3621
|
if (opt.DefElem && opt.DefElem.arg && this.getNodeType(opt.DefElem.arg) === 'String') {
|
|
3512
3622
|
const stringData = this.getNodeData(opt.DefElem.arg);
|
|
3513
|
-
return `${opt.DefElem.defname}
|
|
3623
|
+
return `${QuoteUtils.quoteIdentifierAfterDot(opt.DefElem.defname)}=${QuoteUtils.escape(stringData.sval)}`;
|
|
3514
3624
|
}
|
|
3515
3625
|
return this.visit(opt, context.spawn('IndexElem'));
|
|
3516
3626
|
});
|
|
@@ -3561,12 +3671,12 @@ export class Deparser {
|
|
|
3561
3671
|
}
|
|
3562
3672
|
PartitionCmd(node, context) {
|
|
3563
3673
|
const output = [];
|
|
3674
|
+
if (node.name) {
|
|
3675
|
+
output.push(this.RangeVar(node.name, context));
|
|
3676
|
+
}
|
|
3564
3677
|
if (node.concurrent) {
|
|
3565
3678
|
output.push('CONCURRENTLY');
|
|
3566
3679
|
}
|
|
3567
|
-
if (node.name) {
|
|
3568
|
-
output.push(this.visit(node.name, context));
|
|
3569
|
-
}
|
|
3570
3680
|
if (node.bound) {
|
|
3571
3681
|
if (node.bound.strategy === 'l' && node.bound.listdatums) {
|
|
3572
3682
|
output.push('FOR VALUES IN');
|
|
@@ -3591,9 +3701,9 @@ export class Deparser {
|
|
|
3591
3701
|
output.push(`(${upperValues})`);
|
|
3592
3702
|
}
|
|
3593
3703
|
}
|
|
3594
|
-
else if (node.bound.strategy === 'h'
|
|
3704
|
+
else if (node.bound.strategy === 'h') {
|
|
3595
3705
|
output.push('FOR VALUES WITH');
|
|
3596
|
-
output.push(`(
|
|
3706
|
+
output.push(`(MODULUS ${node.bound.modulus ?? 0}, REMAINDER ${node.bound.remainder ?? 0})`);
|
|
3597
3707
|
}
|
|
3598
3708
|
else if (node.bound.is_default) {
|
|
3599
3709
|
output.push('DEFAULT');
|
|
@@ -3724,12 +3834,7 @@ export class Deparser {
|
|
|
3724
3834
|
if (context.isPretty()) {
|
|
3725
3835
|
// For complex JOIN conditions, format with proper indentation
|
|
3726
3836
|
if (qualsStr.includes('AND') || qualsStr.includes('OR') || qualsStr.length > 50) {
|
|
3727
|
-
|
|
3728
|
-
output.push(` ON ${qualsStr}`);
|
|
3729
|
-
}
|
|
3730
|
-
else {
|
|
3731
|
-
output.push(` ON${context.newline()}${context.indent(qualsStr)}`);
|
|
3732
|
-
}
|
|
3837
|
+
output.push(` ON${context.newline()}${context.indent(qualsStr)}`);
|
|
3733
3838
|
}
|
|
3734
3839
|
else {
|
|
3735
3840
|
output.push(` ON ${qualsStr}`);
|
|
@@ -3811,19 +3916,19 @@ export class Deparser {
|
|
|
3811
3916
|
case 'TRANS_STMT_PREPARE':
|
|
3812
3917
|
output.push('PREPARE TRANSACTION');
|
|
3813
3918
|
if (node.gid) {
|
|
3814
|
-
output.push(
|
|
3919
|
+
output.push(QuoteUtils.escape(node.gid));
|
|
3815
3920
|
}
|
|
3816
3921
|
break;
|
|
3817
3922
|
case 'TRANS_STMT_COMMIT_PREPARED':
|
|
3818
3923
|
output.push('COMMIT PREPARED');
|
|
3819
3924
|
if (node.gid) {
|
|
3820
|
-
output.push(
|
|
3925
|
+
output.push(QuoteUtils.escape(node.gid));
|
|
3821
3926
|
}
|
|
3822
3927
|
break;
|
|
3823
3928
|
case 'TRANS_STMT_ROLLBACK_PREPARED':
|
|
3824
3929
|
output.push('ROLLBACK PREPARED');
|
|
3825
3930
|
if (node.gid) {
|
|
3826
|
-
output.push(
|
|
3931
|
+
output.push(QuoteUtils.escape(node.gid));
|
|
3827
3932
|
}
|
|
3828
3933
|
break;
|
|
3829
3934
|
default:
|
|
@@ -3890,17 +3995,25 @@ export class Deparser {
|
|
|
3890
3995
|
switch (node.kind) {
|
|
3891
3996
|
case 'VAR_SET_VALUE':
|
|
3892
3997
|
const localPrefix = node.is_local ? 'LOCAL ' : '';
|
|
3998
|
+
if (node.jumble_args && node.name === 'xmloption' && node.args) {
|
|
3999
|
+
const xmlValue = this.getNodeData(ListUtils.unwrapList(node.args)[0]);
|
|
4000
|
+
const xmlOption = (typeof xmlValue.sval === 'object' ? xmlValue.sval.sval : xmlValue.sval) || '';
|
|
4001
|
+
return `SET ${localPrefix}XML OPTION ${xmlOption.toUpperCase()}`;
|
|
4002
|
+
}
|
|
3893
4003
|
const args = node.args ? ListUtils.unwrapList(node.args).map(arg => {
|
|
3894
4004
|
const nodeData = this.getNodeData(arg);
|
|
3895
4005
|
if (nodeData.sval !== undefined) {
|
|
3896
4006
|
const svalValue = typeof nodeData.sval === 'object' ? nodeData.sval.sval : nodeData.sval;
|
|
3897
|
-
if (svalValue
|
|
3898
|
-
return
|
|
4007
|
+
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)) {
|
|
4008
|
+
return QuoteUtils.escape(svalValue);
|
|
3899
4009
|
}
|
|
3900
4010
|
return svalValue;
|
|
3901
4011
|
}
|
|
3902
4012
|
return this.visit(arg, context);
|
|
3903
4013
|
}).join(', ') : '';
|
|
4014
|
+
if (node.jumble_args && node.name === 'timezone') {
|
|
4015
|
+
return `SET ${localPrefix}TIME ZONE ${args}`;
|
|
4016
|
+
}
|
|
3904
4017
|
// Handle args - always include TO clause if args exist (even if empty string)
|
|
3905
4018
|
const paramName = QuoteUtils.quoteIdentifier(node.name);
|
|
3906
4019
|
if (!node.args || node.args.length === 0) {
|
|
@@ -3908,6 +4021,9 @@ export class Deparser {
|
|
|
3908
4021
|
}
|
|
3909
4022
|
return `SET ${localPrefix}${paramName} TO ${args}`;
|
|
3910
4023
|
case 'VAR_SET_DEFAULT':
|
|
4024
|
+
if (node.jumble_args && node.name === 'timezone') {
|
|
4025
|
+
return `SET ${node.is_local ? 'LOCAL ' : ''}TIME ZONE DEFAULT`;
|
|
4026
|
+
}
|
|
3911
4027
|
const defaultParamName = QuoteUtils.quoteIdentifier(node.name);
|
|
3912
4028
|
return `SET ${defaultParamName} TO DEFAULT`;
|
|
3913
4029
|
case 'VAR_SET_CURRENT':
|
|
@@ -4212,7 +4328,7 @@ export class Deparser {
|
|
|
4212
4328
|
if (objList && objList.List && objList.List.items) {
|
|
4213
4329
|
const items = objList.List.items.map((item) => {
|
|
4214
4330
|
if (item.String && item.String.sval) {
|
|
4215
|
-
return item.String.sval;
|
|
4331
|
+
return QuoteUtils.quoteIdentifier(item.String.sval);
|
|
4216
4332
|
}
|
|
4217
4333
|
return this.visit(item, context);
|
|
4218
4334
|
});
|
|
@@ -4435,10 +4551,10 @@ export class Deparser {
|
|
|
4435
4551
|
output.push('PROGRAM');
|
|
4436
4552
|
}
|
|
4437
4553
|
if (node.filename) {
|
|
4438
|
-
output.push(
|
|
4554
|
+
output.push(QuoteUtils.escape(node.filename));
|
|
4439
4555
|
}
|
|
4440
4556
|
else {
|
|
4441
|
-
output.push('STDIN');
|
|
4557
|
+
output.push(node.is_from ? 'STDIN' : 'STDOUT');
|
|
4442
4558
|
}
|
|
4443
4559
|
if (node.options && node.options.length > 0) {
|
|
4444
4560
|
output.push('WITH');
|
|
@@ -4670,6 +4786,9 @@ export class Deparser {
|
|
|
4670
4786
|
const nodeData = this.getNodeData(node.def);
|
|
4671
4787
|
if (nodeData && nodeData.typeName) {
|
|
4672
4788
|
output.push(this.TypeName(nodeData.typeName, context));
|
|
4789
|
+
if (nodeData.collClause) {
|
|
4790
|
+
output.push(this.CollateClause(nodeData.collClause, context));
|
|
4791
|
+
}
|
|
4673
4792
|
// Handle USING clause (stored in raw_default for ALTER COLUMN TYPE)
|
|
4674
4793
|
if (nodeData.raw_default) {
|
|
4675
4794
|
output.push('USING');
|
|
@@ -5019,13 +5138,6 @@ export class Deparser {
|
|
|
5019
5138
|
output.push('IF EXISTS');
|
|
5020
5139
|
}
|
|
5021
5140
|
break;
|
|
5022
|
-
case 'AT_CheckNotNull':
|
|
5023
|
-
output.push('ALTER COLUMN');
|
|
5024
|
-
if (node.name) {
|
|
5025
|
-
output.push(QuoteUtils.quoteIdentifier(node.name));
|
|
5026
|
-
}
|
|
5027
|
-
output.push('SET NOT NULL');
|
|
5028
|
-
break;
|
|
5029
5141
|
case 'AT_AddIndex':
|
|
5030
5142
|
output.push('ADD');
|
|
5031
5143
|
if (node.def) {
|
|
@@ -5051,23 +5163,28 @@ export class Deparser {
|
|
|
5051
5163
|
}
|
|
5052
5164
|
break;
|
|
5053
5165
|
case 'AT_AlterConstraint':
|
|
5054
|
-
|
|
5055
|
-
|
|
5056
|
-
|
|
5057
|
-
|
|
5058
|
-
|
|
5059
|
-
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
|
|
5063
|
-
|
|
5166
|
+
if (node.def && this.getNodeType(node.def) === 'ATAlterConstraint') {
|
|
5167
|
+
output.push(this.ATAlterConstraint(this.getNodeData(node.def), context));
|
|
5168
|
+
}
|
|
5169
|
+
else {
|
|
5170
|
+
output.push('ALTER CONSTRAINT');
|
|
5171
|
+
if (node.def && this.getNodeType(node.def) === 'Constraint') {
|
|
5172
|
+
const constraintData = this.getNodeData(node.def);
|
|
5173
|
+
if (constraintData.conname) {
|
|
5174
|
+
output.push(QuoteUtils.quoteIdentifier(constraintData.conname));
|
|
5175
|
+
if (constraintData.deferrable !== undefined) {
|
|
5176
|
+
output.push(constraintData.deferrable ? 'DEFERRABLE' : 'NOT DEFERRABLE');
|
|
5177
|
+
}
|
|
5178
|
+
if (constraintData.initdeferred !== undefined) {
|
|
5179
|
+
output.push(constraintData.initdeferred ? 'INITIALLY DEFERRED' : 'INITIALLY IMMEDIATE');
|
|
5180
|
+
}
|
|
5064
5181
|
}
|
|
5065
5182
|
}
|
|
5066
|
-
|
|
5067
|
-
|
|
5068
|
-
|
|
5069
|
-
|
|
5070
|
-
|
|
5183
|
+
else if (node.name) {
|
|
5184
|
+
output.push(QuoteUtils.quoteIdentifier(node.name));
|
|
5185
|
+
if (node.def) {
|
|
5186
|
+
output.push(this.visit(node.def, context));
|
|
5187
|
+
}
|
|
5071
5188
|
}
|
|
5072
5189
|
}
|
|
5073
5190
|
break;
|
|
@@ -5152,9 +5269,8 @@ export class Deparser {
|
|
|
5152
5269
|
if (node.name) {
|
|
5153
5270
|
output.push(QuoteUtils.quoteIdentifier(node.name));
|
|
5154
5271
|
}
|
|
5155
|
-
output.push('SET');
|
|
5156
5272
|
if (node.def) {
|
|
5157
|
-
output.push(this.
|
|
5273
|
+
output.push(this.formatIdentityColumnOptions(node.def, context));
|
|
5158
5274
|
}
|
|
5159
5275
|
break;
|
|
5160
5276
|
case 'AT_DropIdentity':
|
|
@@ -5163,11 +5279,8 @@ export class Deparser {
|
|
|
5163
5279
|
output.push(QuoteUtils.quoteIdentifier(node.name));
|
|
5164
5280
|
}
|
|
5165
5281
|
output.push('DROP IDENTITY');
|
|
5166
|
-
if (node.
|
|
5167
|
-
output.push('
|
|
5168
|
-
}
|
|
5169
|
-
else if (node.behavior === 'DROP_RESTRICT') {
|
|
5170
|
-
output.push('RESTRICT');
|
|
5282
|
+
if (node.missing_ok) {
|
|
5283
|
+
output.push('IF EXISTS');
|
|
5171
5284
|
}
|
|
5172
5285
|
break;
|
|
5173
5286
|
case 'AT_ReAddStatistics':
|
|
@@ -5261,6 +5374,7 @@ export class Deparser {
|
|
|
5261
5374
|
if (bodyType === 'List') {
|
|
5262
5375
|
const statements = ListUtils.unwrapList(node.sql_body);
|
|
5263
5376
|
if (statements.length === 0 || (statements.length === 1 && Object.keys(statements[0]).length === 0)) {
|
|
5377
|
+
// empty body: nothing to emit
|
|
5264
5378
|
}
|
|
5265
5379
|
else {
|
|
5266
5380
|
// Handle nested List structure in BEGIN ATOMIC blocks
|
|
@@ -5408,19 +5522,22 @@ export class Deparser {
|
|
|
5408
5522
|
if (node.arg && this.getNodeType(node.arg) === 'List') {
|
|
5409
5523
|
const listData = this.getNodeData(node.arg);
|
|
5410
5524
|
const listItems = ListUtils.unwrapList(listData.items);
|
|
5525
|
+
const preservedName = this.preserveOperatorDefElemCase(node.defname);
|
|
5411
5526
|
if (listItems.length === 1 && listItems[0].String) {
|
|
5412
|
-
const preservedName = this.preserveOperatorDefElemCase(node.defname);
|
|
5413
5527
|
return `${preservedName} = ${listItems[0].String.sval}`;
|
|
5414
5528
|
}
|
|
5529
|
+
if (listItems.length > 1 && listItems.every((item) => item.String)) {
|
|
5530
|
+
return `${preservedName} = OPERATOR(${this.qualifiedOperatorName(listItems)})`;
|
|
5531
|
+
}
|
|
5415
5532
|
}
|
|
5416
5533
|
}
|
|
5417
5534
|
// Handle IndexElem opclassopts - MUST be first to preserve string types
|
|
5418
5535
|
if (context.parentNodeTypes.includes('IndexElem')) {
|
|
5419
5536
|
if (node.arg && this.getNodeType(node.arg) === 'String') {
|
|
5420
5537
|
const stringData = this.getNodeData(node.arg);
|
|
5421
|
-
return `${node.defname}
|
|
5538
|
+
return `${QuoteUtils.quoteIdentifierAfterDot(node.defname)}=${QuoteUtils.escape(stringData.sval)}`;
|
|
5422
5539
|
}
|
|
5423
|
-
return `${node.defname}=${this.visit(node.arg, context.spawn('DefElem'))}`;
|
|
5540
|
+
return `${QuoteUtils.quoteIdentifierAfterDot(node.defname)}=${this.formatOptionValue(node.arg, this.visit(node.arg, context.spawn('DefElem')))}`;
|
|
5424
5541
|
}
|
|
5425
5542
|
// Handle CREATE OPERATOR boolean flags - MUST be first to preserve case
|
|
5426
5543
|
if (context.parentNodeTypes.includes('DefineStmt') &&
|
|
@@ -5445,9 +5562,7 @@ export class Deparser {
|
|
|
5445
5562
|
const defElemContext = context.spawn('DefElem');
|
|
5446
5563
|
const argValue = this.visit(node.arg, defElemContext);
|
|
5447
5564
|
if (context.parentNodeTypes.includes('CreateFdwStmt') || context.parentNodeTypes.includes('AlterFdwStmt')) {
|
|
5448
|
-
const finalValue =
|
|
5449
|
-
? `'${argValue}'`
|
|
5450
|
-
: argValue;
|
|
5565
|
+
const finalValue = this.quoteDefElemArg(node.arg, argValue);
|
|
5451
5566
|
const quotedDefname = QuoteUtils.quoteIdentifier(node.defname);
|
|
5452
5567
|
if (node.defaction === 'DEFELEM_ADD') {
|
|
5453
5568
|
return `ADD ${quotedDefname} ${finalValue}`;
|
|
@@ -5460,9 +5575,7 @@ export class Deparser {
|
|
|
5460
5575
|
}
|
|
5461
5576
|
return `${quotedDefname} ${finalValue}`;
|
|
5462
5577
|
}
|
|
5463
|
-
const quotedValue =
|
|
5464
|
-
? `'${argValue}'`
|
|
5465
|
-
: argValue;
|
|
5578
|
+
const quotedValue = this.quoteDefElemArg(node.arg, argValue);
|
|
5466
5579
|
if (node.defaction === 'DEFELEM_ADD') {
|
|
5467
5580
|
return `ADD ${node.defname} ${quotedValue}`;
|
|
5468
5581
|
}
|
|
@@ -5494,9 +5607,7 @@ export class Deparser {
|
|
|
5494
5607
|
}
|
|
5495
5608
|
const defElemContext = context.spawn('DefElem');
|
|
5496
5609
|
const argValue = this.visit(node.arg, defElemContext);
|
|
5497
|
-
const quotedValue =
|
|
5498
|
-
? `'${argValue}'`
|
|
5499
|
-
: argValue;
|
|
5610
|
+
const quotedValue = this.quoteDefElemArg(node.arg, argValue);
|
|
5500
5611
|
return `PASSWORD ${quotedValue}`;
|
|
5501
5612
|
}
|
|
5502
5613
|
}
|
|
@@ -5519,6 +5630,9 @@ export class Deparser {
|
|
|
5519
5630
|
if (listItems.length === 1 && listItems[0].String) {
|
|
5520
5631
|
return `${node.defname} = ${listItems[0].String.sval}`;
|
|
5521
5632
|
}
|
|
5633
|
+
if (listItems.length > 1 && listItems.every((item) => item.String)) {
|
|
5634
|
+
return `${node.defname} = OPERATOR(${this.qualifiedOperatorName(listItems)})`;
|
|
5635
|
+
}
|
|
5522
5636
|
}
|
|
5523
5637
|
}
|
|
5524
5638
|
if (context.parentNodeTypes.includes('CreatedbStmt') || context.parentNodeTypes.includes('DropdbStmt')) {
|
|
@@ -5561,9 +5675,7 @@ export class Deparser {
|
|
|
5561
5675
|
}
|
|
5562
5676
|
}
|
|
5563
5677
|
if (node.defname === 'validUntil') {
|
|
5564
|
-
const quotedValue =
|
|
5565
|
-
? `'${argValue}'`
|
|
5566
|
-
: argValue;
|
|
5678
|
+
const quotedValue = this.quoteDefElemArg(node.arg, argValue);
|
|
5567
5679
|
return `VALID UNTIL ${quotedValue}`;
|
|
5568
5680
|
}
|
|
5569
5681
|
if (node.defname === 'adminmembers') {
|
|
@@ -5602,6 +5714,9 @@ export class Deparser {
|
|
|
5602
5714
|
if (node.arg && this.getNodeType(node.arg) === 'List') {
|
|
5603
5715
|
const listData = this.getNodeData(node.arg);
|
|
5604
5716
|
const listItems = ListUtils.unwrapList(listData.items);
|
|
5717
|
+
if (listItems.length === 1 && listItems[0].String?.sval === 'none') {
|
|
5718
|
+
return 'OWNED BY NONE';
|
|
5719
|
+
}
|
|
5605
5720
|
const parts = listItems.map(item => {
|
|
5606
5721
|
const itemData = this.getNodeData(item);
|
|
5607
5722
|
if (this.getNodeType(item) === 'String') {
|
|
@@ -5740,10 +5855,8 @@ export class Deparser {
|
|
|
5740
5855
|
if (context.parentNodeTypes.includes('AlterExtensionStmt')) {
|
|
5741
5856
|
if (node.defname === 'new_version') {
|
|
5742
5857
|
// argValue is unquoted due to DefElem context, so we need to quote it
|
|
5743
|
-
const quotedValue =
|
|
5744
|
-
|
|
5745
|
-
: argValue;
|
|
5746
|
-
return `UPDATE TO ${quotedValue}`;
|
|
5858
|
+
const quotedValue = this.quoteDefElemArg(node.arg, argValue);
|
|
5859
|
+
return `TO ${quotedValue}`;
|
|
5747
5860
|
}
|
|
5748
5861
|
if (node.defname === 'schema') {
|
|
5749
5862
|
return `SET SCHEMA ${argValue}`;
|
|
@@ -5763,28 +5876,37 @@ export class Deparser {
|
|
|
5763
5876
|
if (node.defname === 'cascade') {
|
|
5764
5877
|
return argValue === 'true' ? 'CASCADE' : '';
|
|
5765
5878
|
}
|
|
5879
|
+
if (node.defname === 'new_version') {
|
|
5880
|
+
return `VERSION ${this.quoteDefElemArg(node.arg, argValue)}`;
|
|
5881
|
+
}
|
|
5882
|
+
if (node.defname === 'schema' && node.arg && this.getNodeType(node.arg) === 'String') {
|
|
5883
|
+
return `SCHEMA ${QuoteUtils.quoteIdentifier(this.getNodeData(node.arg).sval)}`;
|
|
5884
|
+
}
|
|
5766
5885
|
return `${node.defname.toUpperCase()} ${argValue}`;
|
|
5767
5886
|
}
|
|
5768
5887
|
// Handle IndexStmt WITH clause options - no quotes, compact formatting
|
|
5769
5888
|
if (context.parentNodeTypes.includes('IndexStmt')) {
|
|
5770
|
-
return `${node.defname}=${argValue}`;
|
|
5889
|
+
return `${QuoteUtils.quoteIdentifierAfterDot(node.defname)}=${this.formatOptionValue(node.arg, argValue)}`;
|
|
5771
5890
|
}
|
|
5772
5891
|
// Handle IndexElem opclassopts - preserve string values as strings
|
|
5773
5892
|
if (context.parentNodeTypes.includes('IndexElem')) {
|
|
5774
5893
|
if (node.arg && this.getNodeType(node.arg) === 'String') {
|
|
5775
5894
|
const stringData = this.getNodeData(node.arg);
|
|
5776
|
-
return `${node.defname}
|
|
5895
|
+
return `${QuoteUtils.quoteIdentifierAfterDot(node.defname)}=${QuoteUtils.escape(stringData.sval)}`;
|
|
5777
5896
|
}
|
|
5778
|
-
return `${node.defname}=${argValue}`;
|
|
5897
|
+
return `${QuoteUtils.quoteIdentifierAfterDot(node.defname)}=${this.formatOptionValue(node.arg, argValue)}`;
|
|
5779
5898
|
}
|
|
5780
5899
|
// Handle CreateStmt table options - no quotes, compact formatting
|
|
5781
5900
|
if (context.parentNodeTypes.includes('CreateStmt')) {
|
|
5901
|
+
const relOptName = node.defnamespace
|
|
5902
|
+
? `${QuoteUtils.quoteIdentifier(node.defnamespace)}.${QuoteUtils.quoteIdentifier(node.defname)}`
|
|
5903
|
+
: QuoteUtils.quoteIdentifier(node.defname);
|
|
5782
5904
|
// For numeric values, use the raw value without quotes
|
|
5783
5905
|
if (node.arg && this.getNodeType(node.arg) === 'Integer') {
|
|
5784
5906
|
const integerData = this.getNodeData(node.arg);
|
|
5785
|
-
return `${
|
|
5907
|
+
return `${relOptName}=${formatInteger(integerData.ival)}`;
|
|
5786
5908
|
}
|
|
5787
|
-
return `${
|
|
5909
|
+
return `${relOptName}=${this.formatOptionValue(node.arg, argValue)}`;
|
|
5788
5910
|
}
|
|
5789
5911
|
// Handle CreateEventTrigStmt WHEN clause - use IN syntax for List arguments
|
|
5790
5912
|
if (context.parentNodeTypes.includes('CreateEventTrigStmt')) {
|
|
@@ -5794,7 +5916,7 @@ export class Deparser {
|
|
|
5794
5916
|
const values = listItems.map(item => {
|
|
5795
5917
|
if (this.getNodeType(item) === 'String') {
|
|
5796
5918
|
const stringData = this.getNodeData(item);
|
|
5797
|
-
return
|
|
5919
|
+
return QuoteUtils.escape(stringData.sval || '');
|
|
5798
5920
|
}
|
|
5799
5921
|
return this.visit(item, context);
|
|
5800
5922
|
});
|
|
@@ -5809,7 +5931,7 @@ export class Deparser {
|
|
|
5809
5931
|
if (node.arg && this.getNodeType(node.arg) === 'TypeName') {
|
|
5810
5932
|
return `${optionName} = ${argValue}`;
|
|
5811
5933
|
}
|
|
5812
|
-
return `${optionName} = ${argValue}`;
|
|
5934
|
+
return `${optionName} = ${this.formatOptionValue(node.arg, argValue)}`;
|
|
5813
5935
|
}
|
|
5814
5936
|
// Handle ViewStmt WITH options - don't quote numeric values
|
|
5815
5937
|
if (context.parentNodeTypes.includes('ViewStmt')) {
|
|
@@ -5823,15 +5945,27 @@ export class Deparser {
|
|
|
5823
5945
|
}
|
|
5824
5946
|
// Handle CopyStmt WITH clause options - uppercase format without quotes
|
|
5825
5947
|
if (context.parentNodeTypes.includes('CopyStmt')) {
|
|
5826
|
-
|
|
5827
|
-
|
|
5828
|
-
return
|
|
5948
|
+
const optName = node.defname.toUpperCase();
|
|
5949
|
+
if (!node.arg) {
|
|
5950
|
+
return optName;
|
|
5951
|
+
}
|
|
5952
|
+
const copyArgType = this.getNodeType(node.arg);
|
|
5953
|
+
if (copyArgType === 'Boolean' && this.getNodeData(node.arg).boolval !== false) {
|
|
5954
|
+
return optName;
|
|
5829
5955
|
}
|
|
5830
|
-
|
|
5831
|
-
|
|
5832
|
-
|
|
5956
|
+
if (copyArgType === 'List') {
|
|
5957
|
+
const items = ListUtils.unwrapList(this.getNodeData(node.arg).items)
|
|
5958
|
+
.map(item => this.visit(item, context));
|
|
5959
|
+
return `${optName} (${items.join(', ')})`;
|
|
5833
5960
|
}
|
|
5834
|
-
|
|
5961
|
+
if (copyArgType === 'String') {
|
|
5962
|
+
const sval = this.getNodeData(node.arg).sval;
|
|
5963
|
+
if (node.defname === 'format') {
|
|
5964
|
+
return `${optName} ${sval}`;
|
|
5965
|
+
}
|
|
5966
|
+
return `${optName} ${QuoteUtils.escape(sval)}`;
|
|
5967
|
+
}
|
|
5968
|
+
return `${optName} ${argValue}`;
|
|
5835
5969
|
}
|
|
5836
5970
|
// Handle CREATE OPERATOR and CREATE TYPE context
|
|
5837
5971
|
if (context.parentNodeTypes.includes('DefineStmt')) {
|
|
@@ -5870,7 +6004,7 @@ export class Deparser {
|
|
|
5870
6004
|
return `${quotedDefname} = ${stringData.sval}`;
|
|
5871
6005
|
}
|
|
5872
6006
|
// Regular string literals get single quotes
|
|
5873
|
-
return `${quotedDefname} =
|
|
6007
|
+
return `${quotedDefname} = ${QuoteUtils.escape(stringData.sval)}`;
|
|
5874
6008
|
}
|
|
5875
6009
|
return `${quotedDefname} = ${argValue}`;
|
|
5876
6010
|
}
|
|
@@ -5891,7 +6025,7 @@ export class Deparser {
|
|
|
5891
6025
|
return `${node.defname} = ${stringData.sval}`;
|
|
5892
6026
|
}
|
|
5893
6027
|
// Regular string literals get single quotes
|
|
5894
|
-
return `${node.defname} =
|
|
6028
|
+
return `${node.defname} = ${QuoteUtils.escape(stringData.sval)}`;
|
|
5895
6029
|
}
|
|
5896
6030
|
if (node.arg && this.getNodeType(node.arg) === 'Boolean') {
|
|
5897
6031
|
const boolData = this.getNodeData(node.arg);
|
|
@@ -5912,6 +6046,10 @@ export class Deparser {
|
|
|
5912
6046
|
return `${node.defname} = ${argValue}`;
|
|
5913
6047
|
}
|
|
5914
6048
|
}
|
|
6049
|
+
const argType = this.getNodeType(node.arg);
|
|
6050
|
+
if (argType === 'TypeName' || argType === 'Integer' || argType === 'Float' || argType === 'Boolean') {
|
|
6051
|
+
return `${node.defname} = ${argValue}`;
|
|
6052
|
+
}
|
|
5915
6053
|
const quotedValue = typeof argValue === 'string'
|
|
5916
6054
|
? QuoteUtils.escape(argValue)
|
|
5917
6055
|
: argValue;
|
|
@@ -5937,7 +6075,7 @@ export class Deparser {
|
|
|
5937
6075
|
}
|
|
5938
6076
|
if (node.location) {
|
|
5939
6077
|
output.push('LOCATION');
|
|
5940
|
-
output.push(
|
|
6078
|
+
output.push(QuoteUtils.escape(node.location));
|
|
5941
6079
|
}
|
|
5942
6080
|
if (node.options && node.options.length > 0) {
|
|
5943
6081
|
output.push('WITH');
|
|
@@ -6001,6 +6139,7 @@ export class Deparser {
|
|
|
6001
6139
|
if (node.extname) {
|
|
6002
6140
|
output.push(this.quoteIfNeeded(node.extname));
|
|
6003
6141
|
}
|
|
6142
|
+
output.push('UPDATE');
|
|
6004
6143
|
if (node.options && node.options.length > 0) {
|
|
6005
6144
|
const extContext = context.spawn('AlterExtensionStmt');
|
|
6006
6145
|
const options = ListUtils.unwrapList(node.options)
|
|
@@ -6270,7 +6409,7 @@ export class Deparser {
|
|
|
6270
6409
|
}
|
|
6271
6410
|
if (node.payload !== null && node.payload !== undefined) {
|
|
6272
6411
|
output.push(',');
|
|
6273
|
-
output.push(
|
|
6412
|
+
output.push(QuoteUtils.escape(node.payload));
|
|
6274
6413
|
}
|
|
6275
6414
|
return output.join(' ');
|
|
6276
6415
|
}
|
|
@@ -6298,7 +6437,7 @@ export class Deparser {
|
|
|
6298
6437
|
if (!node.filename) {
|
|
6299
6438
|
throw new Error('LoadStmt requires filename');
|
|
6300
6439
|
}
|
|
6301
|
-
return `LOAD
|
|
6440
|
+
return `LOAD ${QuoteUtils.escape(node.filename)}`;
|
|
6302
6441
|
}
|
|
6303
6442
|
DiscardStmt(node, context) {
|
|
6304
6443
|
switch (node.target) {
|
|
@@ -6314,6 +6453,13 @@ export class Deparser {
|
|
|
6314
6453
|
throw new Error(`Unsupported DiscardStmt target: ${node.target}`);
|
|
6315
6454
|
}
|
|
6316
6455
|
}
|
|
6456
|
+
/** `schema.op` for an operator name list: schema parts quoted, the operator symbol as is. */
|
|
6457
|
+
qualifiedOperatorName(objname) {
|
|
6458
|
+
const names = ListUtils.unwrapList(objname ?? []).map((n) => (n.String ? n.String.sval : 'unknown'));
|
|
6459
|
+
if (names.length === 0)
|
|
6460
|
+
return 'unknown';
|
|
6461
|
+
return [...names.slice(0, -1).map((n) => QuoteUtils.quoteIdentifier(n)), names[names.length - 1]].join('.');
|
|
6462
|
+
}
|
|
6317
6463
|
CommentStmt(node, context) {
|
|
6318
6464
|
const output = ['COMMENT ON'];
|
|
6319
6465
|
if (node.objtype) {
|
|
@@ -6345,6 +6491,9 @@ export class Deparser {
|
|
|
6345
6491
|
case 'OBJECT_TABCONSTRAINT':
|
|
6346
6492
|
output.push('CONSTRAINT');
|
|
6347
6493
|
break;
|
|
6494
|
+
case 'OBJECT_DOMCONSTRAINT':
|
|
6495
|
+
output.push('CONSTRAINT');
|
|
6496
|
+
break;
|
|
6348
6497
|
case 'OBJECT_TRIGGER':
|
|
6349
6498
|
output.push('TRIGGER');
|
|
6350
6499
|
break;
|
|
@@ -6421,23 +6570,30 @@ export class Deparser {
|
|
|
6421
6570
|
output.push(objectParts.join('.'));
|
|
6422
6571
|
}
|
|
6423
6572
|
}
|
|
6424
|
-
else if (node.objtype === '
|
|
6573
|
+
else if (node.objtype === 'OBJECT_DOMCONSTRAINT') {
|
|
6425
6574
|
if (objectParts.length === 2) {
|
|
6426
|
-
const [
|
|
6427
|
-
output.push(
|
|
6575
|
+
const [domain, constraint] = objectParts;
|
|
6576
|
+
output.push(constraint, 'ON DOMAIN', domain);
|
|
6577
|
+
}
|
|
6578
|
+
else {
|
|
6579
|
+
output.push(objectParts.join('.'));
|
|
6580
|
+
}
|
|
6581
|
+
}
|
|
6582
|
+
else if (node.objtype === 'OBJECT_TRIGGER') {
|
|
6583
|
+
if (objectParts.length >= 2) {
|
|
6584
|
+
output.push(objectParts[objectParts.length - 1]);
|
|
6428
6585
|
output.push('ON');
|
|
6429
|
-
output.push(
|
|
6586
|
+
output.push(objectParts.slice(0, -1).join('.'));
|
|
6430
6587
|
}
|
|
6431
6588
|
else {
|
|
6432
6589
|
output.push(objectParts.join('.'));
|
|
6433
6590
|
}
|
|
6434
6591
|
}
|
|
6435
6592
|
else if (node.objtype === 'OBJECT_RULE') {
|
|
6436
|
-
if (objectParts.length
|
|
6437
|
-
|
|
6438
|
-
output.push(rule);
|
|
6593
|
+
if (objectParts.length >= 2) {
|
|
6594
|
+
output.push(objectParts[objectParts.length - 1]);
|
|
6439
6595
|
output.push('ON');
|
|
6440
|
-
output.push(
|
|
6596
|
+
output.push(objectParts.slice(0, -1).join('.'));
|
|
6441
6597
|
}
|
|
6442
6598
|
else {
|
|
6443
6599
|
output.push(objectParts.join('.'));
|
|
@@ -6448,8 +6604,7 @@ export class Deparser {
|
|
|
6448
6604
|
// For operators, we need to handle ObjectWithArgs structure
|
|
6449
6605
|
if (node.object && node.object.ObjectWithArgs) {
|
|
6450
6606
|
const objWithArgs = node.object.ObjectWithArgs;
|
|
6451
|
-
let operatorName =
|
|
6452
|
-
? objWithArgs.objname[0].String.sval : 'unknown';
|
|
6607
|
+
let operatorName = this.qualifiedOperatorName(objWithArgs.objname);
|
|
6453
6608
|
if (operatorName.startsWith('"') && operatorName.endsWith('"')) {
|
|
6454
6609
|
operatorName = operatorName.slice(1, -1);
|
|
6455
6610
|
}
|
|
@@ -6487,7 +6642,13 @@ export class Deparser {
|
|
|
6487
6642
|
}
|
|
6488
6643
|
else if (node.objtype === 'OBJECT_POLICY') {
|
|
6489
6644
|
// Handle POLICY syntax: COMMENT ON POLICY policy_name ON table_name IS 'comment'
|
|
6490
|
-
if (objectParts.length ===
|
|
6645
|
+
if (objectParts.length === 3) {
|
|
6646
|
+
const [schema, table, policy] = objectParts;
|
|
6647
|
+
output.push(policy);
|
|
6648
|
+
output.push('ON');
|
|
6649
|
+
output.push(`${schema}.${table}`);
|
|
6650
|
+
}
|
|
6651
|
+
else if (objectParts.length === 2) {
|
|
6491
6652
|
const [table, policy] = objectParts;
|
|
6492
6653
|
output.push(policy);
|
|
6493
6654
|
output.push('ON');
|
|
@@ -6510,6 +6671,9 @@ export class Deparser {
|
|
|
6510
6671
|
output.push(objectParts.join('.'));
|
|
6511
6672
|
}
|
|
6512
6673
|
}
|
|
6674
|
+
else if (node.objtype === 'OBJECT_CAST' && objectParts.length === 2) {
|
|
6675
|
+
output.push(`(${objectParts[0]} AS ${objectParts[1]})`);
|
|
6676
|
+
}
|
|
6513
6677
|
else {
|
|
6514
6678
|
output.push(objectParts.join('.'));
|
|
6515
6679
|
}
|
|
@@ -6518,8 +6682,7 @@ export class Deparser {
|
|
|
6518
6682
|
else if (node.objtype === 'OBJECT_OPERATOR' && node.object && node.object.ObjectWithArgs) {
|
|
6519
6683
|
// Handle direct ObjectWithArgs for OPERATOR syntax: COMMENT ON OPERATOR -(NONE, integer) IS 'comment'
|
|
6520
6684
|
const objWithArgs = node.object.ObjectWithArgs;
|
|
6521
|
-
let operatorName =
|
|
6522
|
-
? objWithArgs.objname[0].String.sval : 'unknown';
|
|
6685
|
+
let operatorName = this.qualifiedOperatorName(objWithArgs.objname);
|
|
6523
6686
|
// Remove quotes from operator name if present
|
|
6524
6687
|
if (operatorName.startsWith('"') && operatorName.endsWith('"')) {
|
|
6525
6688
|
operatorName = operatorName.slice(1, -1);
|
|
@@ -6752,7 +6915,7 @@ export class Deparser {
|
|
|
6752
6915
|
output.push(QuoteUtils.quoteIdentifier(node.pubname));
|
|
6753
6916
|
}
|
|
6754
6917
|
if (node.pubobjects && node.pubobjects.length > 0) {
|
|
6755
|
-
output.push('FOR'
|
|
6918
|
+
output.push('FOR');
|
|
6756
6919
|
const tables = ListUtils.unwrapList(node.pubobjects).map(table => this.visit(table, context));
|
|
6757
6920
|
output.push(tables.join(', '));
|
|
6758
6921
|
}
|
|
@@ -6773,7 +6936,7 @@ export class Deparser {
|
|
|
6773
6936
|
}
|
|
6774
6937
|
output.push('CONNECTION');
|
|
6775
6938
|
if (node.conninfo) {
|
|
6776
|
-
output.push(
|
|
6939
|
+
output.push(QuoteUtils.escape(node.conninfo));
|
|
6777
6940
|
}
|
|
6778
6941
|
output.push('PUBLICATION');
|
|
6779
6942
|
if (node.publication && node.publication.length > 0) {
|
|
@@ -6792,8 +6955,9 @@ export class Deparser {
|
|
|
6792
6955
|
if (node.pubname) {
|
|
6793
6956
|
output.push(QuoteUtils.quoteIdentifier(node.pubname));
|
|
6794
6957
|
}
|
|
6795
|
-
|
|
6796
|
-
|
|
6958
|
+
const hasObjects = node.pubobjects && node.pubobjects.length > 0;
|
|
6959
|
+
if (hasObjects) {
|
|
6960
|
+
switch (node.action || 'AP_AddObjects') {
|
|
6797
6961
|
case 'AP_AddObjects':
|
|
6798
6962
|
output.push('ADD');
|
|
6799
6963
|
break;
|
|
@@ -6806,17 +6970,11 @@ export class Deparser {
|
|
|
6806
6970
|
default:
|
|
6807
6971
|
throw new Error(`Unsupported AlterPublicationStmt action: ${node.action}`);
|
|
6808
6972
|
}
|
|
6809
|
-
}
|
|
6810
|
-
if (node.for_all_tables) {
|
|
6811
|
-
output.push('FOR ALL TABLES');
|
|
6812
|
-
}
|
|
6813
|
-
else if (node.pubobjects && node.pubobjects.length > 0) {
|
|
6814
|
-
output.push('FOR TABLE');
|
|
6815
6973
|
const objects = ListUtils.unwrapList(node.pubobjects).map(obj => this.visit(obj, context));
|
|
6816
6974
|
output.push(objects.join(', '));
|
|
6817
6975
|
}
|
|
6818
|
-
if (node.options && node.options.length > 0) {
|
|
6819
|
-
output.push('
|
|
6976
|
+
else if (node.options && node.options.length > 0) {
|
|
6977
|
+
output.push('SET');
|
|
6820
6978
|
const options = ListUtils.unwrapList(node.options).map(opt => this.visit(opt, context));
|
|
6821
6979
|
output.push(`(${options.join(', ')})`);
|
|
6822
6980
|
}
|
|
@@ -6827,42 +6985,67 @@ export class Deparser {
|
|
|
6827
6985
|
if (node.subname) {
|
|
6828
6986
|
output.push(QuoteUtils.quoteIdentifier(node.subname));
|
|
6829
6987
|
}
|
|
6830
|
-
|
|
6831
|
-
|
|
6988
|
+
const options = node.options && node.options.length > 0
|
|
6989
|
+
? `(${ListUtils.unwrapList(node.options).map(opt => this.visit(opt, context)).join(', ')})`
|
|
6990
|
+
: null;
|
|
6991
|
+
const publications = node.publication && node.publication.length > 0
|
|
6992
|
+
? ListUtils.unwrapList(node.publication).map(pub => this.visit(pub, context)).join(', ')
|
|
6993
|
+
: null;
|
|
6994
|
+
{
|
|
6995
|
+
switch (node.kind || 'ALTER_SUBSCRIPTION_OPTIONS') {
|
|
6832
6996
|
case 'ALTER_SUBSCRIPTION_OPTIONS':
|
|
6833
6997
|
output.push('SET');
|
|
6998
|
+
if (options)
|
|
6999
|
+
output.push(options);
|
|
6834
7000
|
break;
|
|
6835
7001
|
case 'ALTER_SUBSCRIPTION_CONNECTION':
|
|
6836
7002
|
output.push('CONNECTION');
|
|
6837
7003
|
if (node.conninfo) {
|
|
6838
|
-
output.push(
|
|
7004
|
+
output.push(QuoteUtils.escape(node.conninfo));
|
|
6839
7005
|
}
|
|
6840
7006
|
break;
|
|
6841
7007
|
case 'ALTER_SUBSCRIPTION_SET_PUBLICATION':
|
|
6842
7008
|
output.push('SET PUBLICATION');
|
|
6843
|
-
if (
|
|
6844
|
-
|
|
6845
|
-
|
|
6846
|
-
|
|
7009
|
+
if (publications)
|
|
7010
|
+
output.push(publications);
|
|
7011
|
+
if (options)
|
|
7012
|
+
output.push('WITH', options);
|
|
7013
|
+
break;
|
|
7014
|
+
case 'ALTER_SUBSCRIPTION_ADD_PUBLICATION':
|
|
7015
|
+
output.push('ADD PUBLICATION');
|
|
7016
|
+
if (publications)
|
|
7017
|
+
output.push(publications);
|
|
7018
|
+
if (options)
|
|
7019
|
+
output.push('WITH', options);
|
|
7020
|
+
break;
|
|
7021
|
+
case 'ALTER_SUBSCRIPTION_DROP_PUBLICATION':
|
|
7022
|
+
output.push('DROP PUBLICATION');
|
|
7023
|
+
if (publications)
|
|
7024
|
+
output.push(publications);
|
|
7025
|
+
if (options)
|
|
7026
|
+
output.push('WITH', options);
|
|
6847
7027
|
break;
|
|
6848
7028
|
case 'ALTER_SUBSCRIPTION_REFRESH':
|
|
6849
7029
|
output.push('REFRESH PUBLICATION');
|
|
7030
|
+
if (options)
|
|
7031
|
+
output.push('WITH', options);
|
|
6850
7032
|
break;
|
|
6851
|
-
case 'ALTER_SUBSCRIPTION_ENABLED':
|
|
6852
|
-
|
|
7033
|
+
case 'ALTER_SUBSCRIPTION_ENABLED': {
|
|
7034
|
+
const enabled = ListUtils.unwrapList(node.options).find(opt => opt.DefElem?.defname === 'enabled');
|
|
7035
|
+
const arg = enabled?.DefElem?.arg;
|
|
7036
|
+
const isEnabled = arg?.Boolean ? arg.Boolean.boolval !== false : true;
|
|
7037
|
+
output.push(isEnabled ? 'ENABLE' : 'DISABLE');
|
|
6853
7038
|
break;
|
|
7039
|
+
}
|
|
6854
7040
|
case 'ALTER_SUBSCRIPTION_SKIP':
|
|
6855
7041
|
output.push('SKIP');
|
|
7042
|
+
if (options)
|
|
7043
|
+
output.push(options);
|
|
6856
7044
|
break;
|
|
6857
7045
|
default:
|
|
6858
7046
|
throw new Error(`Unsupported AlterSubscriptionStmt kind: ${node.kind}`);
|
|
6859
7047
|
}
|
|
6860
7048
|
}
|
|
6861
|
-
if (node.options && node.options.length > 0) {
|
|
6862
|
-
output.push('WITH');
|
|
6863
|
-
const options = ListUtils.unwrapList(node.options).map(opt => this.visit(opt, context));
|
|
6864
|
-
output.push(`(${options.join(', ')})`);
|
|
6865
|
-
}
|
|
6866
7049
|
return output.join(' ');
|
|
6867
7050
|
}
|
|
6868
7051
|
DropSubscriptionStmt(node, context) {
|
|
@@ -7691,6 +7874,15 @@ export class Deparser {
|
|
|
7691
7874
|
output.push(this.visit(node.object, context));
|
|
7692
7875
|
}
|
|
7693
7876
|
}
|
|
7877
|
+
else if ((node.renameType === 'OBJECT_TYPE' || node.renameType === 'OBJECT_DOMAIN') && node.object.List) {
|
|
7878
|
+
// Qualified type names - join List parts with dots
|
|
7879
|
+
const items = ListUtils.unwrapList(node.object);
|
|
7880
|
+
const parts = items
|
|
7881
|
+
.map((item) => item.String?.sval)
|
|
7882
|
+
.filter((s) => typeof s === 'string')
|
|
7883
|
+
.map((s) => this.quoteIfNeeded(s));
|
|
7884
|
+
output.push(parts.join('.'));
|
|
7885
|
+
}
|
|
7694
7886
|
else if (node.renameType === 'OBJECT_SCHEMA' && node.object.List) {
|
|
7695
7887
|
// Handle schema names - extract from List structure
|
|
7696
7888
|
const items = ListUtils.unwrapList(node.object);
|
|
@@ -7830,6 +8022,9 @@ export class Deparser {
|
|
|
7830
8022
|
else if (node.objtype === 'OBJECT_LANGUAGE') {
|
|
7831
8023
|
output.push('LANGUAGE');
|
|
7832
8024
|
}
|
|
8025
|
+
else if (node.objtype === 'OBJECT_SEQUENCE') {
|
|
8026
|
+
output.push('SEQUENCE');
|
|
8027
|
+
}
|
|
7833
8028
|
else if (node.objtype === 'OBJECT_FUNCTION') {
|
|
7834
8029
|
output.push('FUNCTION');
|
|
7835
8030
|
}
|
|
@@ -7855,8 +8050,11 @@ export class Deparser {
|
|
|
7855
8050
|
output.push('DATABASE');
|
|
7856
8051
|
}
|
|
7857
8052
|
if (node.objects && node.objects.length > 0) {
|
|
8053
|
+
const qualifiedKinds = ['OBJECT_TYPE', 'OBJECT_DOMAIN'];
|
|
7858
8054
|
const objects = ListUtils.unwrapList(node.objects)
|
|
7859
|
-
.map(obj =>
|
|
8055
|
+
.map(obj => qualifiedKinds.includes(node.objtype) && obj.List
|
|
8056
|
+
? ListUtils.unwrapList(obj.List.items).map((item) => this.visit(item, context)).join('.')
|
|
8057
|
+
: this.visit(obj, context))
|
|
7860
8058
|
.join(', ');
|
|
7861
8059
|
output.push(objects);
|
|
7862
8060
|
}
|
|
@@ -7895,8 +8093,11 @@ export class Deparser {
|
|
|
7895
8093
|
break;
|
|
7896
8094
|
default:
|
|
7897
8095
|
if (node.objects && node.objects.length > 0) {
|
|
8096
|
+
const qualifiedKinds = ['OBJECT_TYPE', 'OBJECT_DOMAIN'];
|
|
7898
8097
|
const objects = ListUtils.unwrapList(node.objects)
|
|
7899
|
-
.map(obj =>
|
|
8098
|
+
.map(obj => qualifiedKinds.includes(node.objtype) && obj.List
|
|
8099
|
+
? ListUtils.unwrapList(obj.List.items).map((item) => this.visit(item, context)).join('.')
|
|
8100
|
+
: this.visit(obj, context))
|
|
7900
8101
|
.join(', ');
|
|
7901
8102
|
output.push(objects);
|
|
7902
8103
|
}
|
|
@@ -7916,6 +8117,9 @@ export class Deparser {
|
|
|
7916
8117
|
if (node.grant_option && node.is_grant) {
|
|
7917
8118
|
output.push('WITH GRANT OPTION');
|
|
7918
8119
|
}
|
|
8120
|
+
if (node.grantor) {
|
|
8121
|
+
output.push('GRANTED BY', this.RoleSpec(node.grantor, context));
|
|
8122
|
+
}
|
|
7919
8123
|
// Only add behavior clauses for REVOKE statements, not for GRANT statements in ALTER DEFAULT PRIVILEGES
|
|
7920
8124
|
if (!node.is_grant) {
|
|
7921
8125
|
if (node.behavior === 'DROP_CASCADE') {
|
|
@@ -7929,57 +8133,34 @@ export class Deparser {
|
|
|
7929
8133
|
}
|
|
7930
8134
|
GrantRoleStmt(node, context) {
|
|
7931
8135
|
const output = [];
|
|
7932
|
-
|
|
7933
|
-
|
|
7934
|
-
|
|
7935
|
-
|
|
7936
|
-
|
|
7937
|
-
|
|
7938
|
-
|
|
7939
|
-
|
|
7940
|
-
|
|
7941
|
-
|
|
7942
|
-
|
|
7943
|
-
|
|
7944
|
-
|
|
7945
|
-
|
|
7946
|
-
|
|
7947
|
-
|
|
7948
|
-
|
|
7949
|
-
|
|
7950
|
-
|
|
7951
|
-
if (adminOption.DefElem && adminOption.DefElem.arg) {
|
|
7952
|
-
adminValue = adminOption.DefElem.arg.Boolean?.boolval;
|
|
7953
|
-
}
|
|
7954
|
-
}
|
|
7955
|
-
if (setOption && setOption.DefElem) {
|
|
7956
|
-
hasSetOption = true;
|
|
7957
|
-
setValue = setOption.DefElem.arg?.Boolean?.boolval;
|
|
7958
|
-
}
|
|
7959
|
-
}
|
|
8136
|
+
const options = ListUtils.unwrapList(node.opt)
|
|
8137
|
+
.map(opt => opt.DefElem)
|
|
8138
|
+
.filter((opt) => !!opt && !!opt.defname);
|
|
8139
|
+
const optionValue = (opt) => {
|
|
8140
|
+
if (!opt.arg)
|
|
8141
|
+
return true;
|
|
8142
|
+
const argType = this.getNodeType(opt.arg);
|
|
8143
|
+
const argData = this.getNodeData(opt.arg);
|
|
8144
|
+
if (argType === 'Boolean')
|
|
8145
|
+
return argData.boolval !== false;
|
|
8146
|
+
if (argType === 'String')
|
|
8147
|
+
return argData.sval !== 'false';
|
|
8148
|
+
return true;
|
|
8149
|
+
};
|
|
8150
|
+
const roles = ListUtils.unwrapList(node.granted_roles)
|
|
8151
|
+
.map(role => role.AccessPriv?.priv_name
|
|
8152
|
+
? QuoteUtils.quoteIdentifier(role.AccessPriv.priv_name)
|
|
8153
|
+
: this.visit(role, context))
|
|
8154
|
+
.join(', ');
|
|
7960
8155
|
if (node.is_grant) {
|
|
7961
|
-
output.push('GRANT');
|
|
8156
|
+
output.push('GRANT', roles, 'TO');
|
|
7962
8157
|
}
|
|
7963
8158
|
else {
|
|
7964
8159
|
output.push('REVOKE');
|
|
7965
|
-
if (
|
|
7966
|
-
output.push(
|
|
7967
|
-
}
|
|
7968
|
-
else if (hasAdminOption) {
|
|
7969
|
-
output.push('ADMIN OPTION FOR');
|
|
8160
|
+
if (options.length > 0) {
|
|
8161
|
+
output.push(`${options[0].defname.toUpperCase()} OPTION FOR`);
|
|
7970
8162
|
}
|
|
7971
|
-
|
|
7972
|
-
if (node.granted_roles && node.granted_roles.length > 0) {
|
|
7973
|
-
const roles = ListUtils.unwrapList(node.granted_roles)
|
|
7974
|
-
.map(role => this.visit(role, context))
|
|
7975
|
-
.join(', ');
|
|
7976
|
-
output.push(roles);
|
|
7977
|
-
}
|
|
7978
|
-
if (node.is_grant) {
|
|
7979
|
-
output.push('TO');
|
|
7980
|
-
}
|
|
7981
|
-
else {
|
|
7982
|
-
output.push('FROM');
|
|
8163
|
+
output.push(roles, 'FROM');
|
|
7983
8164
|
}
|
|
7984
8165
|
if (node.grantee_roles && node.grantee_roles.length > 0) {
|
|
7985
8166
|
const grantees = ListUtils.unwrapList(node.grantee_roles)
|
|
@@ -7987,38 +8168,15 @@ export class Deparser {
|
|
|
7987
8168
|
.join(', ');
|
|
7988
8169
|
output.push(grantees);
|
|
7989
8170
|
}
|
|
7990
|
-
if (node.is_grant) {
|
|
7991
|
-
const withOptions =
|
|
7992
|
-
|
|
7993
|
-
|
|
7994
|
-
|
|
7995
|
-
|
|
7996
|
-
|
|
7997
|
-
|
|
7998
|
-
|
|
7999
|
-
else {
|
|
8000
|
-
withOptions.push('ADMIN OPTION');
|
|
8001
|
-
}
|
|
8002
|
-
}
|
|
8003
|
-
if (hasInheritOption) {
|
|
8004
|
-
if (inheritValue === true) {
|
|
8005
|
-
withOptions.push('INHERIT OPTION');
|
|
8006
|
-
}
|
|
8007
|
-
else if (inheritValue === false) {
|
|
8008
|
-
withOptions.push('INHERIT FALSE');
|
|
8009
|
-
}
|
|
8010
|
-
}
|
|
8011
|
-
if (hasSetOption) {
|
|
8012
|
-
if (setValue === true) {
|
|
8013
|
-
withOptions.push('SET TRUE');
|
|
8014
|
-
}
|
|
8015
|
-
else if (setValue === false) {
|
|
8016
|
-
withOptions.push('SET FALSE');
|
|
8017
|
-
}
|
|
8018
|
-
}
|
|
8019
|
-
if (withOptions.length > 0) {
|
|
8020
|
-
output.push('WITH', withOptions.join(', '));
|
|
8021
|
-
}
|
|
8171
|
+
if (node.is_grant && options.length > 0) {
|
|
8172
|
+
const withOptions = options.map(opt => `${opt.defname.toUpperCase()} ${optionValue(opt) ? 'TRUE' : 'FALSE'}`);
|
|
8173
|
+
output.push('WITH', withOptions.join(', '));
|
|
8174
|
+
}
|
|
8175
|
+
if (node.grantor) {
|
|
8176
|
+
output.push('GRANTED BY', this.RoleSpec(node.grantor, context));
|
|
8177
|
+
}
|
|
8178
|
+
if (!node.is_grant && node.behavior === 'DROP_CASCADE') {
|
|
8179
|
+
output.push('CASCADE');
|
|
8022
8180
|
}
|
|
8023
8181
|
return output.join(' ');
|
|
8024
8182
|
}
|
|
@@ -8053,11 +8211,21 @@ export class Deparser {
|
|
|
8053
8211
|
}
|
|
8054
8212
|
}
|
|
8055
8213
|
if (node.object) {
|
|
8056
|
-
|
|
8214
|
+
// Qualified names arrive as a List of String nodes and must be
|
|
8215
|
+
// dot-joined (visiting the List directly would comma-join them).
|
|
8216
|
+
if (typeof node.object === 'object' && 'List' in node.object) {
|
|
8217
|
+
const list = node.object.List;
|
|
8218
|
+
const objectParts = ListUtils.unwrapList(list.items)
|
|
8219
|
+
.map(item => this.visit(item, context));
|
|
8220
|
+
output.push(objectParts.join('.'));
|
|
8221
|
+
}
|
|
8222
|
+
else {
|
|
8223
|
+
output.push(this.visit(node.object, context));
|
|
8224
|
+
}
|
|
8057
8225
|
}
|
|
8058
8226
|
output.push('IS');
|
|
8059
8227
|
if (node.label) {
|
|
8060
|
-
output.push(
|
|
8228
|
+
output.push(QuoteUtils.escape(node.label));
|
|
8061
8229
|
}
|
|
8062
8230
|
else {
|
|
8063
8231
|
output.push('NULL');
|
|
@@ -8114,10 +8282,10 @@ export class Deparser {
|
|
|
8114
8282
|
output.push(conversionName);
|
|
8115
8283
|
}
|
|
8116
8284
|
if (node.for_encoding_name) {
|
|
8117
|
-
output.push('FOR',
|
|
8285
|
+
output.push('FOR', QuoteUtils.escape(node.for_encoding_name));
|
|
8118
8286
|
}
|
|
8119
8287
|
if (node.to_encoding_name) {
|
|
8120
|
-
output.push('TO',
|
|
8288
|
+
output.push('TO', QuoteUtils.escape(node.to_encoding_name));
|
|
8121
8289
|
}
|
|
8122
8290
|
if (node.func_name && node.func_name.length > 0) {
|
|
8123
8291
|
output.push('FROM');
|
|
@@ -8280,6 +8448,9 @@ export class Deparser {
|
|
|
8280
8448
|
.join(' ');
|
|
8281
8449
|
components.push(context.indent('REFERENCING ' + transitionClauses));
|
|
8282
8450
|
}
|
|
8451
|
+
if (node.constrrel) {
|
|
8452
|
+
components.push(context.indent('FROM ' + this.RangeVar(node.constrrel, context)));
|
|
8453
|
+
}
|
|
8283
8454
|
if (node.deferrable) {
|
|
8284
8455
|
components.push(context.indent('DEFERRABLE'));
|
|
8285
8456
|
}
|
|
@@ -8301,7 +8472,7 @@ export class Deparser {
|
|
|
8301
8472
|
const funcName = ListUtils.unwrapList(node.funcname)
|
|
8302
8473
|
.map(name => this.visit(name, context))
|
|
8303
8474
|
.join('.');
|
|
8304
|
-
executeStr += '
|
|
8475
|
+
executeStr += ' FUNCTION ' + funcName;
|
|
8305
8476
|
}
|
|
8306
8477
|
if (node.args && node.args.length > 0) {
|
|
8307
8478
|
const argContext = context.spawn('CreateTrigStmt', { isStringLiteral: true });
|
|
@@ -8559,6 +8730,74 @@ export class Deparser {
|
|
|
8559
8730
|
.join(' ');
|
|
8560
8731
|
output.push(whenClauses);
|
|
8561
8732
|
}
|
|
8733
|
+
if (node.returningClause) {
|
|
8734
|
+
output.push(this.ReturningClause(node.returningClause, context));
|
|
8735
|
+
}
|
|
8736
|
+
return output.join(' ');
|
|
8737
|
+
}
|
|
8738
|
+
MergeSupportFunc(node, context) {
|
|
8739
|
+
return 'merge_action()';
|
|
8740
|
+
}
|
|
8741
|
+
MergeWhenClause(node, context) {
|
|
8742
|
+
const output = ['WHEN'];
|
|
8743
|
+
switch (node.matchKind) {
|
|
8744
|
+
case 'MERGE_WHEN_MATCHED':
|
|
8745
|
+
output.push('MATCHED');
|
|
8746
|
+
break;
|
|
8747
|
+
case 'MERGE_WHEN_NOT_MATCHED_BY_SOURCE':
|
|
8748
|
+
output.push('NOT MATCHED BY SOURCE');
|
|
8749
|
+
break;
|
|
8750
|
+
case 'MERGE_WHEN_NOT_MATCHED_BY_TARGET':
|
|
8751
|
+
output.push('NOT MATCHED');
|
|
8752
|
+
break;
|
|
8753
|
+
}
|
|
8754
|
+
if (node.condition) {
|
|
8755
|
+
output.push('AND');
|
|
8756
|
+
output.push(this.visit(node.condition, context));
|
|
8757
|
+
}
|
|
8758
|
+
output.push('THEN');
|
|
8759
|
+
switch (node.commandType) {
|
|
8760
|
+
case 'CMD_UPDATE': {
|
|
8761
|
+
output.push('UPDATE SET');
|
|
8762
|
+
if (node.targetList && node.targetList.length > 0) {
|
|
8763
|
+
output.push(this.formatUpdateAssignments(node.targetList, context));
|
|
8764
|
+
}
|
|
8765
|
+
break;
|
|
8766
|
+
}
|
|
8767
|
+
case 'CMD_INSERT': {
|
|
8768
|
+
output.push('INSERT');
|
|
8769
|
+
const targetList = ListUtils.unwrapList(node.targetList);
|
|
8770
|
+
if (targetList.length > 0) {
|
|
8771
|
+
const columns = targetList
|
|
8772
|
+
.map(target => QuoteUtils.quoteIdentifier(target.ResTarget?.name))
|
|
8773
|
+
.join(', ');
|
|
8774
|
+
output.push(`(${columns})`);
|
|
8775
|
+
}
|
|
8776
|
+
if (node.override === 'OVERRIDING_USER_VALUE') {
|
|
8777
|
+
output.push('OVERRIDING USER VALUE');
|
|
8778
|
+
}
|
|
8779
|
+
else if (node.override === 'OVERRIDING_SYSTEM_VALUE') {
|
|
8780
|
+
output.push('OVERRIDING SYSTEM VALUE');
|
|
8781
|
+
}
|
|
8782
|
+
const values = ListUtils.unwrapList(node.values);
|
|
8783
|
+
if (values.length > 0) {
|
|
8784
|
+
const valueItems = values
|
|
8785
|
+
.map(value => this.visit(value, context))
|
|
8786
|
+
.join(', ');
|
|
8787
|
+
output.push(`VALUES (${valueItems})`);
|
|
8788
|
+
}
|
|
8789
|
+
else {
|
|
8790
|
+
output.push('DEFAULT VALUES');
|
|
8791
|
+
}
|
|
8792
|
+
break;
|
|
8793
|
+
}
|
|
8794
|
+
case 'CMD_DELETE':
|
|
8795
|
+
output.push('DELETE');
|
|
8796
|
+
break;
|
|
8797
|
+
case 'CMD_NOTHING':
|
|
8798
|
+
output.push('DO NOTHING');
|
|
8799
|
+
break;
|
|
8800
|
+
}
|
|
8562
8801
|
return output.join(' ');
|
|
8563
8802
|
}
|
|
8564
8803
|
AlterTableMoveAllStmt(node, context) {
|
|
@@ -8927,6 +9166,13 @@ export class Deparser {
|
|
|
8927
9166
|
output.push('USING');
|
|
8928
9167
|
output.push(node.into.accessMethod);
|
|
8929
9168
|
}
|
|
9169
|
+
if (node.into && node.into.options && node.into.options.length > 0) {
|
|
9170
|
+
output.push('WITH');
|
|
9171
|
+
const options = ListUtils.unwrapList(node.into.options)
|
|
9172
|
+
.map(option => this.visit(option, context.spawn('CreateStmt')))
|
|
9173
|
+
.join(', ');
|
|
9174
|
+
output.push(`(${options})`);
|
|
9175
|
+
}
|
|
8930
9176
|
if (node.into && node.into.onCommit && node.into.onCommit !== 'ONCOMMIT_NOOP') {
|
|
8931
9177
|
output.push('ON COMMIT');
|
|
8932
9178
|
switch (node.into.onCommit) {
|
|
@@ -8941,17 +9187,13 @@ export class Deparser {
|
|
|
8941
9187
|
break;
|
|
8942
9188
|
}
|
|
8943
9189
|
}
|
|
9190
|
+
if (node.into && node.into.tableSpaceName) {
|
|
9191
|
+
output.push('TABLESPACE', QuoteUtils.quoteIdentifier(node.into.tableSpaceName));
|
|
9192
|
+
}
|
|
8944
9193
|
output.push('AS');
|
|
8945
9194
|
if (node.query) {
|
|
8946
9195
|
output.push(this.visit(node.query, context));
|
|
8947
9196
|
}
|
|
8948
|
-
if (node.into && node.into.options && node.into.options.length > 0) {
|
|
8949
|
-
output.push('WITH');
|
|
8950
|
-
const options = ListUtils.unwrapList(node.into.options)
|
|
8951
|
-
.map(option => this.visit(option, context))
|
|
8952
|
-
.join(', ');
|
|
8953
|
-
output.push(`(${options})`);
|
|
8954
|
-
}
|
|
8955
9197
|
if (node.into && node.into.skipData) {
|
|
8956
9198
|
output.push('WITH NO DATA');
|
|
8957
9199
|
}
|
|
@@ -9028,6 +9270,9 @@ export class Deparser {
|
|
|
9028
9270
|
if (listItems.length === 1 && listItems[0].String) {
|
|
9029
9271
|
return `${preservedDefName} = ${listItems[0].String.sval}`;
|
|
9030
9272
|
}
|
|
9273
|
+
if (listItems.length > 1 && listItems.every(item => item.String)) {
|
|
9274
|
+
return `${preservedDefName} = OPERATOR(${this.qualifiedOperatorName(listItems)})`;
|
|
9275
|
+
}
|
|
9031
9276
|
}
|
|
9032
9277
|
// For commutator/negator, we already handled them above
|
|
9033
9278
|
if ((defName.toLowerCase() === 'commutator' || defName.toLowerCase() === 'negator')) {
|
|
@@ -9181,7 +9426,7 @@ export class Deparser {
|
|
|
9181
9426
|
const preservedDefName = quotedDefName !== defName ? quotedDefName : defName;
|
|
9182
9427
|
// Handle String arguments with single quotes for string literals
|
|
9183
9428
|
if (defValue.String) {
|
|
9184
|
-
return `${preservedDefName} =
|
|
9429
|
+
return `${preservedDefName} = ${QuoteUtils.escape(defValue.String.sval)}`;
|
|
9185
9430
|
}
|
|
9186
9431
|
return `${preservedDefName} = ${this.visit(defValue, context)}`;
|
|
9187
9432
|
}
|
|
@@ -9304,7 +9549,7 @@ export class Deparser {
|
|
|
9304
9549
|
// For CREATE COLLATION, ensure String nodes are quoted as string literals
|
|
9305
9550
|
let valueStr;
|
|
9306
9551
|
if (defValue.String) {
|
|
9307
|
-
valueStr =
|
|
9552
|
+
valueStr = QuoteUtils.escape(defValue.String.sval);
|
|
9308
9553
|
}
|
|
9309
9554
|
else {
|
|
9310
9555
|
valueStr = this.visit(defValue, context);
|
|
@@ -9424,7 +9669,7 @@ export class Deparser {
|
|
|
9424
9669
|
}
|
|
9425
9670
|
if (node.whereClause) {
|
|
9426
9671
|
output.push('WHERE');
|
|
9427
|
-
output.push(this.visit(node.whereClause, context));
|
|
9672
|
+
output.push(`(${this.visit(node.whereClause, context)})`);
|
|
9428
9673
|
}
|
|
9429
9674
|
return output.join(' ');
|
|
9430
9675
|
}
|
|
@@ -9571,6 +9816,23 @@ export class Deparser {
|
|
|
9571
9816
|
}
|
|
9572
9817
|
return output.join(' ');
|
|
9573
9818
|
}
|
|
9819
|
+
quoteDefElemArg(arg, argValue) {
|
|
9820
|
+
if (arg && this.getNodeType(arg) === 'String') {
|
|
9821
|
+
return QuoteUtils.escape(this.getNodeData(arg).sval || '');
|
|
9822
|
+
}
|
|
9823
|
+
return typeof argValue === 'string' && !argValue.startsWith("'")
|
|
9824
|
+
? QuoteUtils.escape(argValue)
|
|
9825
|
+
: argValue;
|
|
9826
|
+
}
|
|
9827
|
+
formatOptionValue(arg, argValue) {
|
|
9828
|
+
if (arg && this.getNodeType(arg) === 'String') {
|
|
9829
|
+
const sval = this.getNodeData(arg).sval || '';
|
|
9830
|
+
return /^[a-z_][a-z0-9_]*$/.test(sval) && keywordKindOf(sval) === 'NO_KEYWORD'
|
|
9831
|
+
? sval
|
|
9832
|
+
: QuoteUtils.escape(sval);
|
|
9833
|
+
}
|
|
9834
|
+
return argValue;
|
|
9835
|
+
}
|
|
9574
9836
|
Var(node, context) {
|
|
9575
9837
|
if (node.varno && node.varattno) {
|
|
9576
9838
|
return `$${node.varno}.${node.varattno}`;
|
|
@@ -9610,7 +9872,7 @@ export class Deparser {
|
|
|
9610
9872
|
}
|
|
9611
9873
|
if (node.rowexpr) {
|
|
9612
9874
|
output.push(',');
|
|
9613
|
-
output.push(
|
|
9875
|
+
output.push(this.visit(node.rowexpr, context));
|
|
9614
9876
|
}
|
|
9615
9877
|
if (node.colexprs && node.colexprs.length > 0) {
|
|
9616
9878
|
output.push('COLUMNS');
|
|
@@ -9627,25 +9889,40 @@ export class Deparser {
|
|
|
9627
9889
|
if (node.lateral) {
|
|
9628
9890
|
output.push('LATERAL');
|
|
9629
9891
|
}
|
|
9630
|
-
|
|
9631
|
-
|
|
9892
|
+
const args = [];
|
|
9893
|
+
if (node.namespaces && node.namespaces.length > 0) {
|
|
9894
|
+
const namespaces = ListUtils.unwrapList(node.namespaces).map(ns => {
|
|
9895
|
+
const target = this.getNodeData(ns);
|
|
9896
|
+
const uri = this.visit(target.val, context);
|
|
9897
|
+
return target.name ? `${uri} AS ${QuoteUtils.quoteIdentifier(target.name)}` : `DEFAULT ${uri}`;
|
|
9898
|
+
});
|
|
9899
|
+
args.push(`XMLNAMESPACES(${namespaces.join(', ')}),`);
|
|
9632
9900
|
}
|
|
9633
9901
|
if (node.rowexpr) {
|
|
9634
|
-
|
|
9635
|
-
|
|
9902
|
+
args.push(this.formatXmlTableExpr(node.rowexpr, context));
|
|
9903
|
+
}
|
|
9904
|
+
if (node.docexpr) {
|
|
9905
|
+
args.push('PASSING');
|
|
9906
|
+
args.push(this.formatXmlTableExpr(node.docexpr, context));
|
|
9636
9907
|
}
|
|
9637
9908
|
if (node.columns && node.columns.length > 0) {
|
|
9638
|
-
|
|
9639
|
-
|
|
9909
|
+
args.push('COLUMNS');
|
|
9910
|
+
args.push(ListUtils.unwrapList(node.columns)
|
|
9640
9911
|
.map(col => this.visit(col, context))
|
|
9641
|
-
.join(', ');
|
|
9642
|
-
output.push(`(${columns})`);
|
|
9912
|
+
.join(', '));
|
|
9643
9913
|
}
|
|
9914
|
+
output.push(`XMLTABLE(${args.join(' ')})`);
|
|
9644
9915
|
if (node.alias) {
|
|
9645
9916
|
output.push(this.Alias(node.alias, context));
|
|
9646
9917
|
}
|
|
9647
9918
|
return output.join(' ');
|
|
9648
9919
|
}
|
|
9920
|
+
formatXmlTableExpr(expr, context) {
|
|
9921
|
+
const value = this.visit(expr, context);
|
|
9922
|
+
return ['A_Const', 'ColumnRef', 'ParamRef', 'FuncCall'].includes(this.getNodeType(expr))
|
|
9923
|
+
? value
|
|
9924
|
+
: `(${value})`;
|
|
9925
|
+
}
|
|
9649
9926
|
RangeTableFuncCol(node, context) {
|
|
9650
9927
|
const output = [];
|
|
9651
9928
|
if (node.colname) {
|
|
@@ -9659,32 +9936,193 @@ export class Deparser {
|
|
|
9659
9936
|
}
|
|
9660
9937
|
if (node.colexpr) {
|
|
9661
9938
|
output.push('PATH');
|
|
9662
|
-
output.push(
|
|
9939
|
+
output.push(this.visit(node.colexpr, context));
|
|
9663
9940
|
}
|
|
9664
9941
|
if (node.coldefexpr) {
|
|
9665
9942
|
output.push('DEFAULT');
|
|
9666
9943
|
output.push(this.visit(node.coldefexpr, context));
|
|
9667
9944
|
}
|
|
9945
|
+
if (node.is_not_null) {
|
|
9946
|
+
output.push('NOT NULL');
|
|
9947
|
+
}
|
|
9668
9948
|
return output.join(' ');
|
|
9669
9949
|
}
|
|
9950
|
+
formatJsonFormat(format) {
|
|
9951
|
+
if (!format || !format.format_type || format.format_type === 'JS_FORMAT_DEFAULT') {
|
|
9952
|
+
return null;
|
|
9953
|
+
}
|
|
9954
|
+
const parts = ['FORMAT JSON'];
|
|
9955
|
+
switch (format.encoding) {
|
|
9956
|
+
case 'JS_ENC_UTF8':
|
|
9957
|
+
parts.push('ENCODING UTF8');
|
|
9958
|
+
break;
|
|
9959
|
+
case 'JS_ENC_UTF16':
|
|
9960
|
+
parts.push('ENCODING UTF16');
|
|
9961
|
+
break;
|
|
9962
|
+
case 'JS_ENC_UTF32':
|
|
9963
|
+
parts.push('ENCODING UTF32');
|
|
9964
|
+
break;
|
|
9965
|
+
}
|
|
9966
|
+
return parts.join(' ');
|
|
9967
|
+
}
|
|
9968
|
+
formatJsonOutput(output, context) {
|
|
9969
|
+
if (!output || !output.typeName)
|
|
9970
|
+
return null;
|
|
9971
|
+
const parts = ['RETURNING', this.TypeName(output.typeName, context)];
|
|
9972
|
+
const format = this.formatJsonFormat(output.returning?.format);
|
|
9973
|
+
if (format)
|
|
9974
|
+
parts.push(format);
|
|
9975
|
+
return parts.join(' ');
|
|
9976
|
+
}
|
|
9977
|
+
formatJsonNullClause(absentOnNull) {
|
|
9978
|
+
return absentOnNull ? 'ABSENT ON NULL' : 'NULL ON NULL';
|
|
9979
|
+
}
|
|
9980
|
+
formatJsonUniqueClause(unique) {
|
|
9981
|
+
return unique ? 'WITH UNIQUE KEYS' : null;
|
|
9982
|
+
}
|
|
9983
|
+
JsonValueExpr(node, context) {
|
|
9984
|
+
const parts = [];
|
|
9985
|
+
if (node.raw_expr) {
|
|
9986
|
+
parts.push(this.visit(node.raw_expr, context));
|
|
9987
|
+
}
|
|
9988
|
+
const format = this.formatJsonFormat(node.format);
|
|
9989
|
+
if (format)
|
|
9990
|
+
parts.push(format);
|
|
9991
|
+
return parts.join(' ');
|
|
9992
|
+
}
|
|
9993
|
+
JsonKeyValue(node, context) {
|
|
9994
|
+
const key = node.key ? this.visit(node.key, context) : '';
|
|
9995
|
+
const value = node.value ? this.JsonValueExpr(node.value, context) : '';
|
|
9996
|
+
return `${key} : ${value}`;
|
|
9997
|
+
}
|
|
9998
|
+
JsonObjectConstructor(node, context) {
|
|
9999
|
+
const args = [];
|
|
10000
|
+
if (node.exprs && node.exprs.length > 0) {
|
|
10001
|
+
args.push(ListUtils.unwrapList(node.exprs).map(e => this.visit(e, context)).join(', '));
|
|
10002
|
+
args.push(this.formatJsonNullClause(node.absent_on_null));
|
|
10003
|
+
const unique = this.formatJsonUniqueClause(node.unique);
|
|
10004
|
+
if (unique)
|
|
10005
|
+
args.push(unique);
|
|
10006
|
+
}
|
|
10007
|
+
const output = this.formatJsonOutput(node.output, context);
|
|
10008
|
+
if (output)
|
|
10009
|
+
args.push(output);
|
|
10010
|
+
return `JSON_OBJECT(${args.join(' ')})`;
|
|
10011
|
+
}
|
|
10012
|
+
JsonArrayConstructor(node, context) {
|
|
10013
|
+
const args = [];
|
|
10014
|
+
if (node.exprs && node.exprs.length > 0) {
|
|
10015
|
+
args.push(ListUtils.unwrapList(node.exprs).map(e => this.visit(e, context)).join(', '));
|
|
10016
|
+
args.push(this.formatJsonNullClause(node.absent_on_null));
|
|
10017
|
+
}
|
|
10018
|
+
const output = this.formatJsonOutput(node.output, context);
|
|
10019
|
+
if (output)
|
|
10020
|
+
args.push(output);
|
|
10021
|
+
return `JSON_ARRAY(${args.join(' ')})`;
|
|
10022
|
+
}
|
|
9670
10023
|
JsonArrayQueryConstructor(node, context) {
|
|
9671
|
-
const
|
|
10024
|
+
const args = [];
|
|
9672
10025
|
if (node.query) {
|
|
9673
|
-
|
|
10026
|
+
args.push(this.visit(node.query, context));
|
|
10027
|
+
}
|
|
10028
|
+
const format = this.formatJsonFormat(node.format);
|
|
10029
|
+
if (format)
|
|
10030
|
+
args.push(format);
|
|
10031
|
+
const output = this.formatJsonOutput(node.output, context);
|
|
10032
|
+
if (output)
|
|
10033
|
+
args.push(output);
|
|
10034
|
+
return `JSON_ARRAY(${args.join(' ')})`;
|
|
10035
|
+
}
|
|
10036
|
+
formatJsonAggSuffix(ctor, context) {
|
|
10037
|
+
if (!ctor)
|
|
10038
|
+
return '';
|
|
10039
|
+
let result = '';
|
|
10040
|
+
if (ctor.agg_filter) {
|
|
10041
|
+
result += ` FILTER (WHERE ${this.visit(ctor.agg_filter, context)})`;
|
|
9674
10042
|
}
|
|
9675
|
-
if (
|
|
9676
|
-
|
|
10043
|
+
if (ctor.over) {
|
|
10044
|
+
result += this.formatOverClause(ctor.over, context);
|
|
9677
10045
|
}
|
|
9678
|
-
|
|
9679
|
-
|
|
10046
|
+
return result;
|
|
10047
|
+
}
|
|
10048
|
+
JsonObjectAgg(node, context) {
|
|
10049
|
+
const args = [];
|
|
10050
|
+
if (node.arg) {
|
|
10051
|
+
args.push(this.JsonKeyValue(node.arg, context));
|
|
10052
|
+
}
|
|
10053
|
+
args.push(this.formatJsonNullClause(node.absent_on_null));
|
|
10054
|
+
const unique = this.formatJsonUniqueClause(node.unique);
|
|
10055
|
+
if (unique)
|
|
10056
|
+
args.push(unique);
|
|
10057
|
+
const output = this.formatJsonOutput(node.constructor?.output, context);
|
|
10058
|
+
if (output)
|
|
10059
|
+
args.push(output);
|
|
10060
|
+
return `JSON_OBJECTAGG(${args.join(' ')})${this.formatJsonAggSuffix(node.constructor, context)}`;
|
|
10061
|
+
}
|
|
10062
|
+
JsonArrayAgg(node, context) {
|
|
10063
|
+
const args = [];
|
|
10064
|
+
if (node.arg) {
|
|
10065
|
+
args.push(this.JsonValueExpr(node.arg, context));
|
|
9680
10066
|
}
|
|
9681
|
-
|
|
9682
|
-
|
|
10067
|
+
const aggOrder = node.constructor?.agg_order;
|
|
10068
|
+
if (aggOrder && aggOrder.length > 0) {
|
|
10069
|
+
const orderItems = ListUtils.unwrapList(aggOrder).map(item => this.visit(item, context));
|
|
10070
|
+
args.push(`ORDER BY ${orderItems.join(', ')}`);
|
|
9683
10071
|
}
|
|
9684
|
-
|
|
9685
|
-
|
|
10072
|
+
args.push(this.formatJsonNullClause(node.absent_on_null));
|
|
10073
|
+
const output = this.formatJsonOutput(node.constructor?.output, context);
|
|
10074
|
+
if (output)
|
|
10075
|
+
args.push(output);
|
|
10076
|
+
return `JSON_ARRAYAGG(${args.join(' ')})${this.formatJsonAggSuffix(node.constructor, context)}`;
|
|
10077
|
+
}
|
|
10078
|
+
JsonParseExpr(node, context) {
|
|
10079
|
+
const args = [];
|
|
10080
|
+
if (node.expr) {
|
|
10081
|
+
args.push(this.JsonValueExpr(node.expr, context));
|
|
9686
10082
|
}
|
|
9687
|
-
|
|
10083
|
+
const unique = this.formatJsonUniqueClause(node.unique_keys);
|
|
10084
|
+
if (unique)
|
|
10085
|
+
args.push(unique);
|
|
10086
|
+
return `JSON(${args.join(' ')})`;
|
|
10087
|
+
}
|
|
10088
|
+
JsonScalarExpr(node, context) {
|
|
10089
|
+
const expr = node.expr ? this.visit(node.expr, context) : '';
|
|
10090
|
+
return `JSON_SCALAR(${expr})`;
|
|
10091
|
+
}
|
|
10092
|
+
JsonSerializeExpr(node, context) {
|
|
10093
|
+
const args = [];
|
|
10094
|
+
if (node.expr) {
|
|
10095
|
+
args.push(this.JsonValueExpr(node.expr, context));
|
|
10096
|
+
}
|
|
10097
|
+
const output = this.formatJsonOutput(node.output, context);
|
|
10098
|
+
if (output)
|
|
10099
|
+
args.push(output);
|
|
10100
|
+
return `JSON_SERIALIZE(${args.join(' ')})`;
|
|
10101
|
+
}
|
|
10102
|
+
JsonIsPredicate(node, context) {
|
|
10103
|
+
const parts = [];
|
|
10104
|
+
if (node.expr) {
|
|
10105
|
+
parts.push(this.visit(node.expr, context));
|
|
10106
|
+
}
|
|
10107
|
+
const format = this.formatJsonFormat(node.format);
|
|
10108
|
+
if (format)
|
|
10109
|
+
parts.push(format);
|
|
10110
|
+
parts.push('IS JSON');
|
|
10111
|
+
switch (node.item_type) {
|
|
10112
|
+
case 'JS_TYPE_OBJECT':
|
|
10113
|
+
parts.push('OBJECT');
|
|
10114
|
+
break;
|
|
10115
|
+
case 'JS_TYPE_ARRAY':
|
|
10116
|
+
parts.push('ARRAY');
|
|
10117
|
+
break;
|
|
10118
|
+
case 'JS_TYPE_SCALAR':
|
|
10119
|
+
parts.push('SCALAR');
|
|
10120
|
+
break;
|
|
10121
|
+
}
|
|
10122
|
+
if (node.unique_keys) {
|
|
10123
|
+
parts.push('WITH UNIQUE KEYS');
|
|
10124
|
+
}
|
|
10125
|
+
return parts.join(' ');
|
|
9688
10126
|
}
|
|
9689
10127
|
RangeFunction(node, context) {
|
|
9690
10128
|
const output = [];
|
|
@@ -9878,6 +10316,7 @@ export class Deparser {
|
|
|
9878
10316
|
rootParts.push('STANDALONE NO VALUE');
|
|
9879
10317
|
}
|
|
9880
10318
|
else if (standaloneArg.A_Const.ival.ival === 3) {
|
|
10319
|
+
// STANDALONE omitted
|
|
9881
10320
|
}
|
|
9882
10321
|
else if (Object.keys(standaloneArg.A_Const.ival).length === 0) {
|
|
9883
10322
|
rootParts.push('STANDALONE YES');
|
|
@@ -10470,8 +10909,4 @@ export class Deparser {
|
|
|
10470
10909
|
}
|
|
10471
10910
|
return output.join(' ');
|
|
10472
10911
|
}
|
|
10473
|
-
containsMultilineStringLiteral(content) {
|
|
10474
|
-
const stringLiteralRegex = /'[^']*\n[^']*'/g;
|
|
10475
|
-
return stringLiteralRegex.test(content);
|
|
10476
|
-
}
|
|
10477
10912
|
}
|