pgsql-deparser 16.0.0 → 17.1.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 +45 -88
- package/deparser.d.ts +253 -0
- package/{deparser/deparser.js → deparser.js} +180 -579
- package/esm/{deparser/deparser.js → deparser.js} +180 -579
- package/esm/index.js +3 -15
- package/esm/utils/index.js +90 -0
- package/esm/{deparser/utils → utils}/list-utils.js +0 -4
- package/esm/{deparser/utils → utils}/quote-utils.js +0 -34
- package/esm/utils/sql-formatter.js +23 -0
- package/esm/{deparser/visitors → visitors}/base.js +0 -4
- package/index.d.ts +3 -9
- package/index.js +4 -16
- package/package.json +27 -14
- package/utils/index.d.ts +4 -0
- package/utils/index.js +97 -0
- package/{deparser/utils → utils}/list-utils.d.ts +0 -4
- package/{deparser/utils → utils}/list-utils.js +0 -4
- package/utils/quote-utils.d.ts +5 -0
- package/{deparser/utils → utils}/quote-utils.js +0 -34
- package/{deparser/utils → utils}/sql-formatter.d.ts +1 -7
- package/{deparser/utils → utils}/sql-formatter.js +2 -15
- package/{deparser/visitors → visitors}/base.d.ts +5 -8
- package/{deparser/visitors → visitors}/base.js +0 -4
- package/deparser/deparser.d.ts +0 -301
- package/deparser/index.d.ts +0 -9
- package/deparser/index.js +0 -17
- package/deparser/utils/quote-utils.d.ts +0 -24
- package/esm/deparser/index.js +0 -13
- package/esm/deparser/utils/sql-formatter.js +0 -36
- package/esm/v16-to-v17-direct.js +0 -44
- package/esm/v16-to-v17.js +0 -1488
- package/v16-to-v17-direct.d.ts +0 -21
- package/v16-to-v17-direct.js +0 -48
- package/v16-to-v17.d.ts +0 -638
- package/v16-to-v17.js +0 -1492
|
@@ -1,116 +1,27 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Auto-generated file with types stripped for better tree-shaking
|
|
3
|
-
* DO NOT EDIT - Generated by strip-deparser-types.ts
|
|
4
|
-
*/
|
|
5
1
|
import { SqlFormatter } from './utils/sql-formatter';
|
|
6
2
|
import { QuoteUtils } from './utils/quote-utils';
|
|
7
3
|
import { ListUtils } from './utils/list-utils';
|
|
8
|
-
// Type guards for better type safety
|
|
9
|
-
function isParseResult(obj) {
|
|
10
|
-
// A ParseResult is an object that could have stmts (but not required)
|
|
11
|
-
// and is not already wrapped as a Node
|
|
12
|
-
// IMPORTANT: ParseResult.stmts is "repeated RawStmt" in protobuf, meaning
|
|
13
|
-
// the array contains RawStmt objects inline (not wrapped as { RawStmt: ... })
|
|
14
|
-
// Example: { version: 170004, stmts: [{ stmt: {...}, stmt_len: 32 }] }
|
|
15
|
-
return obj && typeof obj === 'object' &&
|
|
16
|
-
!Array.isArray(obj) &&
|
|
17
|
-
!('ParseResult' in obj) &&
|
|
18
|
-
!('RawStmt' in obj) &&
|
|
19
|
-
// Check if it looks like a ParseResult (has stmts or version)
|
|
20
|
-
('stmts' in obj || 'version' in obj);
|
|
21
|
-
}
|
|
22
|
-
function isWrappedParseResult(obj) {
|
|
23
|
-
return obj && typeof obj === 'object' && 'ParseResult' in obj;
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Deparser - Converts PostgreSQL AST nodes back to SQL strings
|
|
27
|
-
*
|
|
28
|
-
* Entry Points:
|
|
29
|
-
* 1. ParseResult (from libpg-query) - The complete parse result
|
|
30
|
-
* Structure: { version: number, stmts: RawStmt[] }
|
|
31
|
-
* Note: stmts is "repeated RawStmt" in protobuf, so array contains RawStmt
|
|
32
|
-
* objects inline (not wrapped as { RawStmt: ... } nodes)
|
|
33
|
-
* Example: { version: 170004, stmts: [{ stmt: {...}, stmt_len: 32 }] }
|
|
34
|
-
*
|
|
35
|
-
* 2. Wrapped ParseResult - When explicitly wrapped as a Node
|
|
36
|
-
* Structure: { ParseResult: { version: number, stmts: RawStmt[] } }
|
|
37
|
-
*
|
|
38
|
-
* 3. Wrapped RawStmt - When explicitly wrapped as a Node
|
|
39
|
-
* Structure: { RawStmt: { stmt: Node, stmt_len?: number } }
|
|
40
|
-
*
|
|
41
|
-
* 4. Array of Nodes - Multiple statements to deparse
|
|
42
|
-
* Can be: Node[] (e.g., SelectStmt, InsertStmt, etc.)
|
|
43
|
-
*
|
|
44
|
-
* 5. Single Node - Individual statement node
|
|
45
|
-
* Example: { SelectStmt: {...} }, { InsertStmt: {...} }, etc.
|
|
46
|
-
*
|
|
47
|
-
* The deparser automatically detects bare ParseResult objects for backward
|
|
48
|
-
* compatibility and wraps them internally for consistent processing.
|
|
49
|
-
*/
|
|
50
4
|
export class Deparser {
|
|
51
5
|
formatter;
|
|
52
6
|
tree;
|
|
53
|
-
options;
|
|
54
7
|
constructor(tree, opts = {}) {
|
|
55
|
-
this.formatter = new SqlFormatter(opts.newline, opts.tab
|
|
56
|
-
//
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
functionDelimiterFallback: '$EOFCODE$',
|
|
60
|
-
...opts
|
|
61
|
-
};
|
|
62
|
-
// Handle different input types
|
|
63
|
-
if (isParseResult(tree)) {
|
|
64
|
-
// Duck-typed ParseResult (backward compatibility)
|
|
65
|
-
// Wrap it as a proper Node for consistent handling
|
|
66
|
-
this.tree = [{ ParseResult: tree }];
|
|
67
|
-
}
|
|
68
|
-
else if (Array.isArray(tree)) {
|
|
69
|
-
// Array of Nodes
|
|
70
|
-
this.tree = tree;
|
|
8
|
+
this.formatter = new SqlFormatter(opts.newline, opts.tab);
|
|
9
|
+
// Handle parsed query objects that contain both version and stmts
|
|
10
|
+
if (tree && typeof tree === 'object' && !Array.isArray(tree) && 'stmts' in tree) {
|
|
11
|
+
this.tree = tree.stmts;
|
|
71
12
|
}
|
|
72
13
|
else {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* Static method to deparse PostgreSQL AST nodes to SQL
|
|
79
|
-
* @param query - Can be:
|
|
80
|
-
* - ParseResult from libpg-query (e.g., { version: 170004, stmts: [...] })
|
|
81
|
-
* - Wrapped ParseResult node (e.g., { ParseResult: {...} })
|
|
82
|
-
* - Wrapped RawStmt node (e.g., { RawStmt: {...} })
|
|
83
|
-
* - Array of Nodes
|
|
84
|
-
* - Single Node (e.g., { SelectStmt: {...} })
|
|
85
|
-
* @param opts - Deparser options for formatting
|
|
86
|
-
* @returns The deparsed SQL string
|
|
87
|
-
*/
|
|
14
|
+
this.tree = Array.isArray(tree) ? tree : [tree];
|
|
15
|
+
}
|
|
16
|
+
}
|
|
88
17
|
static deparse(query, opts = {}) {
|
|
89
18
|
return new Deparser(query, opts).deparseQuery();
|
|
90
19
|
}
|
|
91
20
|
deparseQuery() {
|
|
92
21
|
return this.tree
|
|
93
|
-
.map(node =>
|
|
94
|
-
// All nodes should go through the standard deparse method
|
|
95
|
-
// which will route to the appropriate handler
|
|
96
|
-
const result = this.deparse(node);
|
|
97
|
-
return result || '';
|
|
98
|
-
})
|
|
99
|
-
.filter(result => result !== '')
|
|
22
|
+
.map(node => this.deparse(node))
|
|
100
23
|
.join(this.formatter.newline() + this.formatter.newline());
|
|
101
24
|
}
|
|
102
|
-
/**
|
|
103
|
-
* Get the appropriate function delimiter based on the body content
|
|
104
|
-
* @param body The function body to check
|
|
105
|
-
* @returns The delimiter to use
|
|
106
|
-
*/
|
|
107
|
-
getFunctionDelimiter(body) {
|
|
108
|
-
const delimiter = this.options.functionDelimiter || '$$';
|
|
109
|
-
if (body.includes(delimiter)) {
|
|
110
|
-
return this.options.functionDelimiterFallback || '$EOFCODE$';
|
|
111
|
-
}
|
|
112
|
-
return delimiter;
|
|
113
|
-
}
|
|
114
25
|
deparse(node, context = { parentNodeTypes: [] }) {
|
|
115
26
|
if (node == null) {
|
|
116
27
|
return null;
|
|
@@ -128,10 +39,6 @@ export class Deparser {
|
|
|
128
39
|
}
|
|
129
40
|
visit(node, context = { parentNodeTypes: [] }) {
|
|
130
41
|
const nodeType = this.getNodeType(node);
|
|
131
|
-
// Handle empty objects
|
|
132
|
-
if (!nodeType) {
|
|
133
|
-
return '';
|
|
134
|
-
}
|
|
135
42
|
const nodeData = this.getNodeData(node);
|
|
136
43
|
const methodName = nodeType;
|
|
137
44
|
if (typeof this[methodName] === 'function') {
|
|
@@ -154,29 +61,24 @@ export class Deparser {
|
|
|
154
61
|
}
|
|
155
62
|
return node;
|
|
156
63
|
}
|
|
157
|
-
ParseResult(node, context) {
|
|
158
|
-
if (!node.stmts || node.stmts.length === 0) {
|
|
159
|
-
return '';
|
|
160
|
-
}
|
|
161
|
-
// Deparse each RawStmt in the ParseResult
|
|
162
|
-
// Note: node.stmts is "repeated RawStmt" so contains RawStmt objects inline
|
|
163
|
-
// Each element has structure: { stmt: Node, stmt_len?: number, stmt_location?: number }
|
|
164
|
-
return node.stmts
|
|
165
|
-
.filter((rawStmt) => rawStmt != null)
|
|
166
|
-
.map((rawStmt) => this.RawStmt(rawStmt, context))
|
|
167
|
-
.filter((result) => result !== '')
|
|
168
|
-
.join(this.formatter.newline() + this.formatter.newline());
|
|
169
|
-
}
|
|
170
64
|
RawStmt(node, context) {
|
|
171
|
-
if (!node.stmt) {
|
|
172
|
-
return '';
|
|
173
|
-
}
|
|
174
|
-
const deparsedStmt = this.deparse(node.stmt, context);
|
|
175
|
-
// Add semicolon if stmt_len is provided (indicates it had one in original)
|
|
176
65
|
if (node.stmt_len) {
|
|
177
|
-
return
|
|
66
|
+
return this.deparse(node.stmt, context) + ';';
|
|
67
|
+
}
|
|
68
|
+
return this.deparse(node.stmt, context);
|
|
69
|
+
}
|
|
70
|
+
stmt(node, context = { parentNodeTypes: [] }) {
|
|
71
|
+
// Handle stmt wrapper nodes that contain the actual statement
|
|
72
|
+
const keys = Object.keys(node);
|
|
73
|
+
if (keys.length === 1) {
|
|
74
|
+
const statementType = keys[0];
|
|
75
|
+
const methodName = statementType;
|
|
76
|
+
if (typeof this[methodName] === 'function') {
|
|
77
|
+
return this[methodName](node[statementType], context);
|
|
78
|
+
}
|
|
79
|
+
throw new Error(`Deparser does not handle statement type: ${statementType}`);
|
|
178
80
|
}
|
|
179
|
-
return
|
|
81
|
+
return '';
|
|
180
82
|
}
|
|
181
83
|
SelectStmt(node, context) {
|
|
182
84
|
const output = [];
|
|
@@ -185,25 +87,23 @@ export class Deparser {
|
|
|
185
87
|
}
|
|
186
88
|
if (!node.op || node.op === 'SETOP_NONE') {
|
|
187
89
|
if (node.valuesLists == null) {
|
|
188
|
-
|
|
189
|
-
output.push('SELECT');
|
|
190
|
-
}
|
|
90
|
+
output.push('SELECT');
|
|
191
91
|
}
|
|
192
92
|
}
|
|
193
93
|
else {
|
|
194
94
|
const leftStmt = this.SelectStmt(node.larg, context);
|
|
195
95
|
const rightStmt = this.SelectStmt(node.rarg, context);
|
|
196
96
|
// Add parentheses if the operand is a set operation OR has ORDER BY/LIMIT clauses OR has WITH clause
|
|
197
|
-
const leftNeedsParens = node.larg && ((
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
const rightNeedsParens = node.rarg && ((
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
97
|
+
const leftNeedsParens = node.larg && ((node.larg.op && node.larg.op !== 'SETOP_NONE') ||
|
|
98
|
+
node.larg.sortClause ||
|
|
99
|
+
node.larg.limitCount ||
|
|
100
|
+
node.larg.limitOffset ||
|
|
101
|
+
node.larg.withClause);
|
|
102
|
+
const rightNeedsParens = node.rarg && ((node.rarg.op && node.rarg.op !== 'SETOP_NONE') ||
|
|
103
|
+
node.rarg.sortClause ||
|
|
104
|
+
node.rarg.limitCount ||
|
|
105
|
+
node.rarg.limitOffset ||
|
|
106
|
+
node.rarg.withClause);
|
|
207
107
|
if (leftNeedsParens) {
|
|
208
108
|
output.push(this.formatter.parens(leftStmt));
|
|
209
109
|
}
|
|
@@ -233,82 +133,41 @@ export class Deparser {
|
|
|
233
133
|
output.push(rightStmt);
|
|
234
134
|
}
|
|
235
135
|
}
|
|
236
|
-
// Handle DISTINCT clause - in pretty mode, we'll include it in the SELECT clause
|
|
237
|
-
let distinctPart = '';
|
|
238
136
|
if (node.distinctClause) {
|
|
239
137
|
const distinctClause = ListUtils.unwrapList(node.distinctClause);
|
|
240
138
|
if (distinctClause.length > 0 && Object.keys(distinctClause[0]).length > 0) {
|
|
139
|
+
output.push('DISTINCT ON');
|
|
241
140
|
const clause = distinctClause
|
|
242
141
|
.map(e => this.visit(e, { ...context, select: true }))
|
|
243
142
|
.join(', ');
|
|
244
|
-
|
|
143
|
+
output.push(this.formatter.parens(clause));
|
|
245
144
|
}
|
|
246
145
|
else {
|
|
247
|
-
|
|
248
|
-
}
|
|
249
|
-
if (!this.formatter.isPretty()) {
|
|
250
|
-
if (distinctClause.length > 0 && Object.keys(distinctClause[0]).length > 0) {
|
|
251
|
-
output.push('DISTINCT ON');
|
|
252
|
-
const clause = distinctClause
|
|
253
|
-
.map(e => this.visit(e, { ...context, select: true }))
|
|
254
|
-
.join(', ');
|
|
255
|
-
output.push(this.formatter.parens(clause));
|
|
256
|
-
}
|
|
257
|
-
else {
|
|
258
|
-
output.push('DISTINCT');
|
|
259
|
-
}
|
|
146
|
+
output.push('DISTINCT');
|
|
260
147
|
}
|
|
261
148
|
}
|
|
262
149
|
if (node.targetList) {
|
|
263
150
|
const targetList = ListUtils.unwrapList(node.targetList);
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
if (this.containsMultilineStringLiteral(targetStr)) {
|
|
269
|
-
return targetStr;
|
|
270
|
-
}
|
|
271
|
-
return this.formatter.indent(targetStr);
|
|
272
|
-
});
|
|
273
|
-
const formattedTargets = targetStrings.join(',' + this.formatter.newline());
|
|
274
|
-
output.push('SELECT' + distinctPart);
|
|
275
|
-
output.push(formattedTargets);
|
|
276
|
-
}
|
|
277
|
-
else {
|
|
278
|
-
const targets = targetList
|
|
279
|
-
.map(e => this.visit(e, { ...context, select: true }))
|
|
280
|
-
.join(', ');
|
|
281
|
-
output.push(targets);
|
|
282
|
-
}
|
|
151
|
+
const targets = targetList
|
|
152
|
+
.map(e => this.visit(e, { ...context, select: true }))
|
|
153
|
+
.join(', ');
|
|
154
|
+
output.push(targets);
|
|
283
155
|
}
|
|
284
156
|
if (node.intoClause) {
|
|
285
157
|
output.push('INTO');
|
|
286
158
|
output.push(this.IntoClause(node.intoClause, context));
|
|
287
159
|
}
|
|
288
160
|
if (node.fromClause) {
|
|
161
|
+
output.push('FROM');
|
|
289
162
|
const fromList = ListUtils.unwrapList(node.fromClause);
|
|
290
163
|
const fromItems = fromList
|
|
291
164
|
.map(e => this.deparse(e, { ...context, from: true }))
|
|
292
165
|
.join(', ');
|
|
293
|
-
output.push(
|
|
166
|
+
output.push(fromItems);
|
|
294
167
|
}
|
|
295
168
|
if (node.whereClause) {
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
const whereExpr = this.visit(node.whereClause, context);
|
|
299
|
-
const lines = whereExpr.split(this.formatter.newline());
|
|
300
|
-
const indentedLines = lines.map((line, index) => {
|
|
301
|
-
if (index === 0) {
|
|
302
|
-
return this.formatter.indent(line);
|
|
303
|
-
}
|
|
304
|
-
return line;
|
|
305
|
-
});
|
|
306
|
-
output.push(indentedLines.join(this.formatter.newline()));
|
|
307
|
-
}
|
|
308
|
-
else {
|
|
309
|
-
output.push('WHERE');
|
|
310
|
-
output.push(this.visit(node.whereClause, context));
|
|
311
|
-
}
|
|
169
|
+
output.push('WHERE');
|
|
170
|
+
output.push(this.visit(node.whereClause, context));
|
|
312
171
|
}
|
|
313
172
|
if (node.valuesLists) {
|
|
314
173
|
output.push('VALUES');
|
|
@@ -319,43 +178,16 @@ export class Deparser {
|
|
|
319
178
|
output.push(lists.join(', '));
|
|
320
179
|
}
|
|
321
180
|
if (node.groupClause) {
|
|
181
|
+
output.push('GROUP BY');
|
|
322
182
|
const groupList = ListUtils.unwrapList(node.groupClause);
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
if (this.containsMultilineStringLiteral(groupStr)) {
|
|
328
|
-
return groupStr;
|
|
329
|
-
}
|
|
330
|
-
return this.formatter.indent(groupStr);
|
|
331
|
-
})
|
|
332
|
-
.join(',' + this.formatter.newline());
|
|
333
|
-
output.push('GROUP BY');
|
|
334
|
-
output.push(groupItems);
|
|
335
|
-
}
|
|
336
|
-
else {
|
|
337
|
-
output.push('GROUP BY');
|
|
338
|
-
const groupItems = groupList
|
|
339
|
-
.map(e => this.visit(e, { ...context, group: true }))
|
|
340
|
-
.join(', ');
|
|
341
|
-
output.push(groupItems);
|
|
342
|
-
}
|
|
183
|
+
const groupItems = groupList
|
|
184
|
+
.map(e => this.visit(e, { ...context, group: true }))
|
|
185
|
+
.join(', ');
|
|
186
|
+
output.push(groupItems);
|
|
343
187
|
}
|
|
344
188
|
if (node.havingClause) {
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
const havingStr = this.visit(node.havingClause, context);
|
|
348
|
-
if (this.containsMultilineStringLiteral(havingStr)) {
|
|
349
|
-
output.push(havingStr);
|
|
350
|
-
}
|
|
351
|
-
else {
|
|
352
|
-
output.push(this.formatter.indent(havingStr));
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
else {
|
|
356
|
-
output.push('HAVING');
|
|
357
|
-
output.push(this.visit(node.havingClause, context));
|
|
358
|
-
}
|
|
189
|
+
output.push('HAVING');
|
|
190
|
+
output.push(this.visit(node.havingClause, context));
|
|
359
191
|
}
|
|
360
192
|
if (node.windowClause) {
|
|
361
193
|
output.push('WINDOW');
|
|
@@ -366,33 +198,20 @@ export class Deparser {
|
|
|
366
198
|
output.push(windowClauses);
|
|
367
199
|
}
|
|
368
200
|
if (node.sortClause) {
|
|
201
|
+
output.push('ORDER BY');
|
|
369
202
|
const sortList = ListUtils.unwrapList(node.sortClause);
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
if (this.containsMultilineStringLiteral(sortStr)) {
|
|
375
|
-
return sortStr;
|
|
376
|
-
}
|
|
377
|
-
return this.formatter.indent(sortStr);
|
|
378
|
-
})
|
|
379
|
-
.join(',' + this.formatter.newline());
|
|
380
|
-
output.push('ORDER BY');
|
|
381
|
-
output.push(sortItems);
|
|
382
|
-
}
|
|
383
|
-
else {
|
|
384
|
-
output.push('ORDER BY');
|
|
385
|
-
const sortItems = sortList
|
|
386
|
-
.map(e => this.visit(e, { ...context, sort: true }))
|
|
387
|
-
.join(', ');
|
|
388
|
-
output.push(sortItems);
|
|
389
|
-
}
|
|
203
|
+
const sortItems = sortList
|
|
204
|
+
.map(e => this.visit(e, { ...context, sort: true }))
|
|
205
|
+
.join(', ');
|
|
206
|
+
output.push(sortItems);
|
|
390
207
|
}
|
|
391
208
|
if (node.limitCount) {
|
|
392
|
-
output.push('LIMIT
|
|
209
|
+
output.push('LIMIT');
|
|
210
|
+
output.push(this.visit(node.limitCount, context));
|
|
393
211
|
}
|
|
394
212
|
if (node.limitOffset) {
|
|
395
|
-
output.push('OFFSET
|
|
213
|
+
output.push('OFFSET');
|
|
214
|
+
output.push(this.visit(node.limitOffset, context));
|
|
396
215
|
}
|
|
397
216
|
if (node.lockingClause) {
|
|
398
217
|
const lockingList = ListUtils.unwrapList(node.lockingClause);
|
|
@@ -401,10 +220,6 @@ export class Deparser {
|
|
|
401
220
|
.join(' ');
|
|
402
221
|
output.push(lockingClauses);
|
|
403
222
|
}
|
|
404
|
-
if (this.formatter.isPretty()) {
|
|
405
|
-
const filteredOutput = output.filter(item => item.trim() !== '');
|
|
406
|
-
return filteredOutput.join(this.formatter.newline());
|
|
407
|
-
}
|
|
408
223
|
return output.join(' ');
|
|
409
224
|
}
|
|
410
225
|
A_Expr(node, context) {
|
|
@@ -469,38 +284,18 @@ export class Deparser {
|
|
|
469
284
|
'ALL',
|
|
470
285
|
this.formatter.parens(this.visit(rexpr, context))
|
|
471
286
|
]);
|
|
472
|
-
case 'AEXPR_DISTINCT':
|
|
473
|
-
let leftExpr = this.visit(lexpr, context);
|
|
474
|
-
let rightExpr = this.visit(rexpr, context);
|
|
475
|
-
// Add parentheses for complex expressions
|
|
476
|
-
if (lexpr && this.isComplexExpression(lexpr)) {
|
|
477
|
-
leftExpr = this.formatter.parens(leftExpr);
|
|
478
|
-
}
|
|
479
|
-
if (rexpr && this.isComplexExpression(rexpr)) {
|
|
480
|
-
rightExpr = this.formatter.parens(rightExpr);
|
|
481
|
-
}
|
|
287
|
+
case 'AEXPR_DISTINCT':
|
|
482
288
|
return this.formatter.format([
|
|
483
|
-
|
|
289
|
+
this.visit(lexpr, context),
|
|
484
290
|
'IS DISTINCT FROM',
|
|
485
|
-
|
|
291
|
+
this.visit(rexpr, context)
|
|
486
292
|
]);
|
|
487
|
-
|
|
488
|
-
case 'AEXPR_NOT_DISTINCT': {
|
|
489
|
-
let leftExpr = this.visit(lexpr, context);
|
|
490
|
-
let rightExpr = this.visit(rexpr, context);
|
|
491
|
-
// Add parentheses for complex expressions
|
|
492
|
-
if (lexpr && this.isComplexExpression(lexpr)) {
|
|
493
|
-
leftExpr = this.formatter.parens(leftExpr);
|
|
494
|
-
}
|
|
495
|
-
if (rexpr && this.isComplexExpression(rexpr)) {
|
|
496
|
-
rightExpr = this.formatter.parens(rightExpr);
|
|
497
|
-
}
|
|
293
|
+
case 'AEXPR_NOT_DISTINCT':
|
|
498
294
|
return this.formatter.format([
|
|
499
|
-
|
|
295
|
+
this.visit(lexpr, context),
|
|
500
296
|
'IS NOT DISTINCT FROM',
|
|
501
|
-
|
|
297
|
+
this.visit(rexpr, context)
|
|
502
298
|
]);
|
|
503
|
-
}
|
|
504
299
|
case 'AEXPR_NULLIF':
|
|
505
300
|
return this.formatter.format([
|
|
506
301
|
'NULLIF',
|
|
@@ -894,24 +689,9 @@ export class Deparser {
|
|
|
894
689
|
if (node.recursive) {
|
|
895
690
|
output.push('RECURSIVE');
|
|
896
691
|
}
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
const cteStrings = ctes.map((cte, index) => {
|
|
901
|
-
const cteStr = this.visit(cte, context);
|
|
902
|
-
const prefix = index === 0 ? this.formatter.newline() : ',' + this.formatter.newline();
|
|
903
|
-
if (this.containsMultilineStringLiteral(cteStr)) {
|
|
904
|
-
return prefix + cteStr;
|
|
905
|
-
}
|
|
906
|
-
return prefix + this.formatter.indent(cteStr);
|
|
907
|
-
});
|
|
908
|
-
output.push(cteStrings.join(''));
|
|
909
|
-
}
|
|
910
|
-
else {
|
|
911
|
-
const cteStrings = ctes.map(cte => this.visit(cte, context));
|
|
912
|
-
output.push(cteStrings.join(', '));
|
|
913
|
-
}
|
|
914
|
-
}
|
|
692
|
+
const ctes = ListUtils.unwrapList(node.ctes);
|
|
693
|
+
const cteStrs = ctes.map(cte => this.visit(cte, context));
|
|
694
|
+
output.push(cteStrs.join(', '));
|
|
915
695
|
return output.join(' ');
|
|
916
696
|
}
|
|
917
697
|
ResTarget(node, context) {
|
|
@@ -990,28 +770,13 @@ export class Deparser {
|
|
|
990
770
|
formatStr = '(%s)';
|
|
991
771
|
}
|
|
992
772
|
const boolContext = { ...context, bool: true };
|
|
993
|
-
// explanation of our syntax/fix below:
|
|
994
|
-
// return formatStr.replace('%s', andArgs); // ❌ Interprets $ as special syntax
|
|
995
|
-
// return formatStr.replace('%s', () => andArgs); // ✅ Function callback prevents interpretation
|
|
996
773
|
switch (boolop) {
|
|
997
774
|
case 'AND_EXPR':
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
return formatStr.replace('%s', () => andArgs);
|
|
1001
|
-
}
|
|
1002
|
-
else {
|
|
1003
|
-
const andArgs = args.map(arg => this.visit(arg, boolContext)).join(' AND ');
|
|
1004
|
-
return formatStr.replace('%s', () => andArgs);
|
|
1005
|
-
}
|
|
775
|
+
const andArgs = args.map(arg => this.visit(arg, boolContext)).join(' AND ');
|
|
776
|
+
return formatStr.replace('%s', andArgs);
|
|
1006
777
|
case 'OR_EXPR':
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
return formatStr.replace('%s', () => orArgs);
|
|
1010
|
-
}
|
|
1011
|
-
else {
|
|
1012
|
-
const orArgs = args.map(arg => this.visit(arg, boolContext)).join(' OR ');
|
|
1013
|
-
return formatStr.replace('%s', () => orArgs);
|
|
1014
|
-
}
|
|
778
|
+
const orArgs = args.map(arg => this.visit(arg, boolContext)).join(' OR ');
|
|
779
|
+
return formatStr.replace('%s', orArgs);
|
|
1015
780
|
case 'NOT_EXPR':
|
|
1016
781
|
return `NOT (${this.visit(args[0], context)})`;
|
|
1017
782
|
default:
|
|
@@ -1213,13 +978,7 @@ export class Deparser {
|
|
|
1213
978
|
windowParts.push(frameClause);
|
|
1214
979
|
}
|
|
1215
980
|
if (windowParts.length > 0) {
|
|
1216
|
-
|
|
1217
|
-
const formattedParts = windowParts.map(part => this.formatter.indent(part));
|
|
1218
|
-
result += ` OVER (${this.formatter.newline()}${formattedParts.join(this.formatter.newline())}${this.formatter.newline()})`;
|
|
1219
|
-
}
|
|
1220
|
-
else {
|
|
1221
|
-
result += ` OVER (${windowParts.join(' ')})`;
|
|
1222
|
-
}
|
|
981
|
+
result += ` OVER (${windowParts.join(' ')})`;
|
|
1223
982
|
}
|
|
1224
983
|
else {
|
|
1225
984
|
result += ` OVER ()`;
|
|
@@ -1276,23 +1035,23 @@ export class Deparser {
|
|
|
1276
1035
|
else if (nodeAny.sval !== undefined) {
|
|
1277
1036
|
if (typeof nodeAny.sval === 'object' && nodeAny.sval !== null) {
|
|
1278
1037
|
if (nodeAny.sval.sval !== undefined) {
|
|
1279
|
-
return QuoteUtils.
|
|
1038
|
+
return QuoteUtils.escape(nodeAny.sval.sval);
|
|
1280
1039
|
}
|
|
1281
1040
|
else if (nodeAny.sval.String && nodeAny.sval.String.sval !== undefined) {
|
|
1282
|
-
return QuoteUtils.
|
|
1041
|
+
return QuoteUtils.escape(nodeAny.sval.String.sval);
|
|
1283
1042
|
}
|
|
1284
1043
|
else if (Object.keys(nodeAny.sval).length === 0) {
|
|
1285
1044
|
return "''";
|
|
1286
1045
|
}
|
|
1287
1046
|
else {
|
|
1288
|
-
return QuoteUtils.
|
|
1047
|
+
return QuoteUtils.escape(nodeAny.sval.toString());
|
|
1289
1048
|
}
|
|
1290
1049
|
}
|
|
1291
1050
|
else if (nodeAny.sval === null) {
|
|
1292
1051
|
return 'NULL';
|
|
1293
1052
|
}
|
|
1294
1053
|
else {
|
|
1295
|
-
return QuoteUtils.
|
|
1054
|
+
return QuoteUtils.escape(nodeAny.sval);
|
|
1296
1055
|
}
|
|
1297
1056
|
}
|
|
1298
1057
|
else if (nodeAny.boolval !== undefined) {
|
|
@@ -1431,7 +1190,7 @@ export class Deparser {
|
|
|
1431
1190
|
}
|
|
1432
1191
|
let args = null;
|
|
1433
1192
|
if (node.typmods) {
|
|
1434
|
-
const isInterval = names.some(
|
|
1193
|
+
const isInterval = names.some(name => {
|
|
1435
1194
|
const nameStr = typeof name === 'string' ? name : (name.String?.sval || name.String?.str);
|
|
1436
1195
|
return nameStr === 'interval';
|
|
1437
1196
|
});
|
|
@@ -1600,14 +1359,7 @@ export class Deparser {
|
|
|
1600
1359
|
output.push('ONLY');
|
|
1601
1360
|
}
|
|
1602
1361
|
let tableName = '';
|
|
1603
|
-
if (node.
|
|
1604
|
-
tableName = QuoteUtils.quote(node.catalogname);
|
|
1605
|
-
if (node.schemaname) {
|
|
1606
|
-
tableName += '.' + QuoteUtils.quote(node.schemaname);
|
|
1607
|
-
}
|
|
1608
|
-
tableName += '.' + QuoteUtils.quote(node.relname);
|
|
1609
|
-
}
|
|
1610
|
-
else if (node.schemaname) {
|
|
1362
|
+
if (node.schemaname) {
|
|
1611
1363
|
tableName = QuoteUtils.quote(node.schemaname) + '.' + QuoteUtils.quote(node.relname);
|
|
1612
1364
|
}
|
|
1613
1365
|
else {
|
|
@@ -1843,39 +1595,15 @@ export class Deparser {
|
|
|
1843
1595
|
output.push(this.visit(node.arg, context));
|
|
1844
1596
|
}
|
|
1845
1597
|
const args = ListUtils.unwrapList(node.args);
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
const whenClause = this.visit(arg, context);
|
|
1849
|
-
if (this.containsMultilineStringLiteral(whenClause)) {
|
|
1850
|
-
output.push(this.formatter.newline() + whenClause);
|
|
1851
|
-
}
|
|
1852
|
-
else {
|
|
1853
|
-
output.push(this.formatter.newline() + this.formatter.indent(whenClause));
|
|
1854
|
-
}
|
|
1855
|
-
}
|
|
1856
|
-
if (node.defresult) {
|
|
1857
|
-
const elseResult = this.visit(node.defresult, context);
|
|
1858
|
-
if (this.containsMultilineStringLiteral(elseResult)) {
|
|
1859
|
-
output.push(this.formatter.newline() + 'ELSE ' + elseResult);
|
|
1860
|
-
}
|
|
1861
|
-
else {
|
|
1862
|
-
output.push(this.formatter.newline() + this.formatter.indent('ELSE ' + elseResult));
|
|
1863
|
-
}
|
|
1864
|
-
}
|
|
1865
|
-
output.push(this.formatter.newline() + 'END');
|
|
1866
|
-
return output.join(' ');
|
|
1598
|
+
for (const arg of args) {
|
|
1599
|
+
output.push(this.visit(arg, context));
|
|
1867
1600
|
}
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
}
|
|
1872
|
-
if (node.defresult) {
|
|
1873
|
-
output.push('ELSE');
|
|
1874
|
-
output.push(this.visit(node.defresult, context));
|
|
1875
|
-
}
|
|
1876
|
-
output.push('END');
|
|
1877
|
-
return output.join(' ');
|
|
1601
|
+
if (node.defresult) {
|
|
1602
|
+
output.push('ELSE');
|
|
1603
|
+
output.push(this.visit(node.defresult, context));
|
|
1878
1604
|
}
|
|
1605
|
+
output.push('END');
|
|
1606
|
+
return output.join(' ');
|
|
1879
1607
|
}
|
|
1880
1608
|
CoalesceExpr(node, context) {
|
|
1881
1609
|
const args = ListUtils.unwrapList(node.args);
|
|
@@ -2010,7 +1738,7 @@ export class Deparser {
|
|
|
2010
1738
|
}
|
|
2011
1739
|
String(node, context) {
|
|
2012
1740
|
if (context.isStringLiteral || context.isEnumValue) {
|
|
2013
|
-
return
|
|
1741
|
+
return `'${node.sval || ''}'`;
|
|
2014
1742
|
}
|
|
2015
1743
|
const value = node.sval || '';
|
|
2016
1744
|
if (context.parentNodeTypes.includes('DefElem') ||
|
|
@@ -2088,13 +1816,7 @@ export class Deparser {
|
|
|
2088
1816
|
const elementStrs = elements.map(el => {
|
|
2089
1817
|
return this.deparse(el, context);
|
|
2090
1818
|
});
|
|
2091
|
-
|
|
2092
|
-
const formattedElements = elementStrs.map(el => this.formatter.indent(el)).join(',' + this.formatter.newline());
|
|
2093
|
-
output.push('(' + this.formatter.newline() + formattedElements + this.formatter.newline() + ')');
|
|
2094
|
-
}
|
|
2095
|
-
else {
|
|
2096
|
-
output.push(this.formatter.parens(elementStrs.join(', ')));
|
|
2097
|
-
}
|
|
1819
|
+
output.push(this.formatter.parens(elementStrs.join(', ')));
|
|
2098
1820
|
}
|
|
2099
1821
|
else if (!node.partbound) {
|
|
2100
1822
|
output.push(this.formatter.parens(''));
|
|
@@ -2333,9 +2055,6 @@ export class Deparser {
|
|
|
2333
2055
|
break;
|
|
2334
2056
|
case 'CONSTR_UNIQUE':
|
|
2335
2057
|
output.push('UNIQUE');
|
|
2336
|
-
if (node.nulls_not_distinct) {
|
|
2337
|
-
output.push('NULLS NOT DISTINCT');
|
|
2338
|
-
}
|
|
2339
2058
|
if (node.keys && node.keys.length > 0) {
|
|
2340
2059
|
const keyList = ListUtils.unwrapList(node.keys)
|
|
2341
2060
|
.map(key => this.visit(key, context))
|
|
@@ -2379,52 +2098,38 @@ export class Deparser {
|
|
|
2379
2098
|
}
|
|
2380
2099
|
}
|
|
2381
2100
|
if (node.fk_upd_action && node.fk_upd_action !== 'a') {
|
|
2382
|
-
|
|
2101
|
+
output.push('ON UPDATE');
|
|
2383
2102
|
switch (node.fk_upd_action) {
|
|
2384
2103
|
case 'r':
|
|
2385
|
-
|
|
2104
|
+
output.push('RESTRICT');
|
|
2386
2105
|
break;
|
|
2387
2106
|
case 'c':
|
|
2388
|
-
|
|
2107
|
+
output.push('CASCADE');
|
|
2389
2108
|
break;
|
|
2390
2109
|
case 'n':
|
|
2391
|
-
|
|
2110
|
+
output.push('SET NULL');
|
|
2392
2111
|
break;
|
|
2393
2112
|
case 'd':
|
|
2394
|
-
|
|
2113
|
+
output.push('SET DEFAULT');
|
|
2395
2114
|
break;
|
|
2396
2115
|
}
|
|
2397
|
-
if (this.formatter.isPretty()) {
|
|
2398
|
-
output.push('\n' + this.formatter.indent(updateClause));
|
|
2399
|
-
}
|
|
2400
|
-
else {
|
|
2401
|
-
output.push('ON UPDATE');
|
|
2402
|
-
output.push(updateClause.replace('ON UPDATE ', ''));
|
|
2403
|
-
}
|
|
2404
2116
|
}
|
|
2405
2117
|
if (node.fk_del_action && node.fk_del_action !== 'a') {
|
|
2406
|
-
|
|
2118
|
+
output.push('ON DELETE');
|
|
2407
2119
|
switch (node.fk_del_action) {
|
|
2408
2120
|
case 'r':
|
|
2409
|
-
|
|
2121
|
+
output.push('RESTRICT');
|
|
2410
2122
|
break;
|
|
2411
2123
|
case 'c':
|
|
2412
|
-
|
|
2124
|
+
output.push('CASCADE');
|
|
2413
2125
|
break;
|
|
2414
2126
|
case 'n':
|
|
2415
|
-
|
|
2127
|
+
output.push('SET NULL');
|
|
2416
2128
|
break;
|
|
2417
2129
|
case 'd':
|
|
2418
|
-
|
|
2130
|
+
output.push('SET DEFAULT');
|
|
2419
2131
|
break;
|
|
2420
2132
|
}
|
|
2421
|
-
if (this.formatter.isPretty()) {
|
|
2422
|
-
output.push('\n' + this.formatter.indent(deleteClause));
|
|
2423
|
-
}
|
|
2424
|
-
else {
|
|
2425
|
-
output.push('ON DELETE');
|
|
2426
|
-
output.push(deleteClause.replace('ON DELETE ', ''));
|
|
2427
|
-
}
|
|
2428
2133
|
}
|
|
2429
2134
|
// Handle NOT VALID for foreign key constraints - only for table constraints, not domain constraints
|
|
2430
2135
|
if (node.skip_validation && !context.isDomainConstraint) {
|
|
@@ -2482,48 +2187,17 @@ export class Deparser {
|
|
|
2482
2187
|
// Handle deferrable constraints for all constraint types that support it
|
|
2483
2188
|
if (node.contype === 'CONSTR_PRIMARY' || node.contype === 'CONSTR_UNIQUE' || node.contype === 'CONSTR_FOREIGN') {
|
|
2484
2189
|
if (node.deferrable) {
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
output.push('\n' + this.formatter.indent('INITIALLY DEFERRED'));
|
|
2489
|
-
}
|
|
2490
|
-
else if (node.initdeferred === false) {
|
|
2491
|
-
output.push('\n' + this.formatter.indent('INITIALLY IMMEDIATE'));
|
|
2492
|
-
}
|
|
2190
|
+
output.push('DEFERRABLE');
|
|
2191
|
+
if (node.initdeferred === true) {
|
|
2192
|
+
output.push('INITIALLY DEFERRED');
|
|
2493
2193
|
}
|
|
2494
|
-
else {
|
|
2495
|
-
output.push('
|
|
2496
|
-
if (node.initdeferred === true) {
|
|
2497
|
-
output.push('INITIALLY DEFERRED');
|
|
2498
|
-
}
|
|
2499
|
-
else if (node.initdeferred === false) {
|
|
2500
|
-
output.push('INITIALLY IMMEDIATE');
|
|
2501
|
-
}
|
|
2194
|
+
else if (node.initdeferred === false) {
|
|
2195
|
+
output.push('INITIALLY IMMEDIATE');
|
|
2502
2196
|
}
|
|
2503
2197
|
}
|
|
2504
2198
|
else if (node.deferrable === false) {
|
|
2505
|
-
|
|
2506
|
-
output.push('\n' + this.formatter.indent('NOT DEFERRABLE'));
|
|
2507
|
-
}
|
|
2508
|
-
else {
|
|
2509
|
-
output.push('NOT DEFERRABLE');
|
|
2510
|
-
}
|
|
2511
|
-
}
|
|
2512
|
-
}
|
|
2513
|
-
if (this.formatter.isPretty() && node.contype === 'CONSTR_FOREIGN') {
|
|
2514
|
-
let result = '';
|
|
2515
|
-
for (let i = 0; i < output.length; i++) {
|
|
2516
|
-
if (output[i].startsWith('\n')) {
|
|
2517
|
-
result += output[i];
|
|
2518
|
-
}
|
|
2519
|
-
else {
|
|
2520
|
-
if (i > 0 && !output[i - 1].startsWith('\n')) {
|
|
2521
|
-
result += ' ';
|
|
2522
|
-
}
|
|
2523
|
-
result += output[i];
|
|
2524
|
-
}
|
|
2199
|
+
output.push('NOT DEFERRABLE');
|
|
2525
2200
|
}
|
|
2526
|
-
return result;
|
|
2527
2201
|
}
|
|
2528
2202
|
return output.join(' ');
|
|
2529
2203
|
}
|
|
@@ -3247,9 +2921,11 @@ export class Deparser {
|
|
|
3247
2921
|
}
|
|
3248
2922
|
switch (node.jointype) {
|
|
3249
2923
|
case 'JOIN_INNER':
|
|
2924
|
+
// Handle NATURAL JOIN first - it has isNatural=true (NATURAL already added above)
|
|
3250
2925
|
if (node.isNatural) {
|
|
3251
2926
|
joinStr += 'JOIN';
|
|
3252
2927
|
}
|
|
2928
|
+
// Handle CROSS JOIN case - when there's no quals, no usingClause, and not natural
|
|
3253
2929
|
else if (!node.quals && (!node.usingClause || node.usingClause.length === 0)) {
|
|
3254
2930
|
joinStr += 'CROSS JOIN';
|
|
3255
2931
|
}
|
|
@@ -3269,63 +2945,26 @@ export class Deparser {
|
|
|
3269
2945
|
default:
|
|
3270
2946
|
joinStr += 'JOIN';
|
|
3271
2947
|
}
|
|
2948
|
+
output.push(joinStr);
|
|
3272
2949
|
if (node.rarg) {
|
|
3273
2950
|
let rargStr = this.visit(node.rarg, context);
|
|
3274
2951
|
if (node.rarg && 'JoinExpr' in node.rarg && !node.rarg.JoinExpr.alias) {
|
|
3275
2952
|
rargStr = `(${rargStr})`;
|
|
3276
2953
|
}
|
|
3277
|
-
|
|
3278
|
-
output.push(this.formatter.newline() + joinStr + ' ' + rargStr);
|
|
3279
|
-
}
|
|
3280
|
-
else {
|
|
3281
|
-
output.push(joinStr + ' ' + rargStr);
|
|
3282
|
-
}
|
|
3283
|
-
}
|
|
3284
|
-
else {
|
|
3285
|
-
if (this.formatter.isPretty()) {
|
|
3286
|
-
output.push(this.formatter.newline() + joinStr);
|
|
3287
|
-
}
|
|
3288
|
-
else {
|
|
3289
|
-
output.push(joinStr);
|
|
3290
|
-
}
|
|
2954
|
+
output.push(rargStr);
|
|
3291
2955
|
}
|
|
3292
2956
|
if (node.usingClause && node.usingClause.length > 0) {
|
|
2957
|
+
output.push('USING');
|
|
3293
2958
|
const usingList = ListUtils.unwrapList(node.usingClause);
|
|
3294
2959
|
const columnNames = usingList.map(col => this.visit(col, context));
|
|
3295
|
-
|
|
3296
|
-
output.push(` USING (${columnNames.join(', ')})`);
|
|
3297
|
-
}
|
|
3298
|
-
else {
|
|
3299
|
-
output.push(`USING (${columnNames.join(', ')})`);
|
|
3300
|
-
}
|
|
2960
|
+
output.push(`(${columnNames.join(', ')})`);
|
|
3301
2961
|
}
|
|
3302
2962
|
else if (node.quals) {
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
// For complex JOIN conditions, format with proper indentation
|
|
3306
|
-
if (qualsStr.includes('AND') || qualsStr.includes('OR') || qualsStr.length > 50) {
|
|
3307
|
-
if (this.containsMultilineStringLiteral(qualsStr)) {
|
|
3308
|
-
output.push(` ON ${qualsStr}`);
|
|
3309
|
-
}
|
|
3310
|
-
else {
|
|
3311
|
-
output.push(` ON${this.formatter.newline()}${this.formatter.indent(qualsStr)}`);
|
|
3312
|
-
}
|
|
3313
|
-
}
|
|
3314
|
-
else {
|
|
3315
|
-
output.push(` ON ${qualsStr}`);
|
|
3316
|
-
}
|
|
3317
|
-
}
|
|
3318
|
-
else {
|
|
3319
|
-
output.push(`ON ${qualsStr}`);
|
|
3320
|
-
}
|
|
3321
|
-
}
|
|
3322
|
-
let result;
|
|
3323
|
-
if (this.formatter.isPretty()) {
|
|
3324
|
-
result = output.join('');
|
|
3325
|
-
}
|
|
3326
|
-
else {
|
|
3327
|
-
result = output.join(' ');
|
|
2963
|
+
output.push('ON');
|
|
2964
|
+
output.push(this.visit(node.quals, context));
|
|
3328
2965
|
}
|
|
2966
|
+
let result = output.join(' ');
|
|
2967
|
+
// Handle join_using_alias first (for USING clause aliases like "AS x")
|
|
3329
2968
|
if (node.join_using_alias && node.join_using_alias.aliasname) {
|
|
3330
2969
|
let aliasStr = node.join_using_alias.aliasname;
|
|
3331
2970
|
if (node.join_using_alias.colnames && node.join_using_alias.colnames.length > 0) {
|
|
@@ -3335,6 +2974,7 @@ export class Deparser {
|
|
|
3335
2974
|
}
|
|
3336
2975
|
result += ` AS ${aliasStr}`;
|
|
3337
2976
|
}
|
|
2977
|
+
// Handle regular alias (for outer table aliases like "y")
|
|
3338
2978
|
if (node.alias && node.alias.aliasname) {
|
|
3339
2979
|
let aliasStr = node.alias.aliasname;
|
|
3340
2980
|
if (node.alias.colnames && node.alias.colnames.length > 0) {
|
|
@@ -5146,17 +4786,17 @@ export class Deparser {
|
|
|
5146
4786
|
}
|
|
5147
4787
|
if (context.parentNodeTypes.includes('DoStmt')) {
|
|
5148
4788
|
if (node.defname === 'as') {
|
|
5149
|
-
const defElemContext = { ...context, parentNodeTypes: [...context.parentNodeTypes, 'DefElem'] };
|
|
5150
|
-
const argValue = node.arg ? this.visit(node.arg, defElemContext) : '';
|
|
5151
4789
|
if (Array.isArray(argValue)) {
|
|
5152
4790
|
const bodyParts = argValue;
|
|
5153
|
-
|
|
5154
|
-
|
|
5155
|
-
|
|
4791
|
+
if (bodyParts.length === 1) {
|
|
4792
|
+
return `$$${bodyParts[0]}$$`;
|
|
4793
|
+
}
|
|
4794
|
+
else {
|
|
4795
|
+
return `$$${bodyParts.join('')}$$`;
|
|
4796
|
+
}
|
|
5156
4797
|
}
|
|
5157
4798
|
else {
|
|
5158
|
-
|
|
5159
|
-
return `${delimiter}${argValue}${delimiter}`;
|
|
4799
|
+
return `$$${argValue}$$`;
|
|
5160
4800
|
}
|
|
5161
4801
|
}
|
|
5162
4802
|
return '';
|
|
@@ -5175,14 +4815,16 @@ export class Deparser {
|
|
|
5175
4815
|
});
|
|
5176
4816
|
if (bodyParts.length === 1) {
|
|
5177
4817
|
const body = bodyParts[0];
|
|
5178
|
-
|
|
5179
|
-
|
|
4818
|
+
// Check if body contains $$ to avoid conflicts
|
|
4819
|
+
if (body.includes('$$')) {
|
|
4820
|
+
return `AS '${body.replace(/'/g, "''")}'`;
|
|
4821
|
+
}
|
|
4822
|
+
else {
|
|
4823
|
+
return `AS $$${body}$$`;
|
|
4824
|
+
}
|
|
5180
4825
|
}
|
|
5181
4826
|
else {
|
|
5182
|
-
return `AS ${bodyParts.map((part) => {
|
|
5183
|
-
const delimiter = this.getFunctionDelimiter(part);
|
|
5184
|
-
return `${delimiter}${part}${delimiter}`;
|
|
5185
|
-
}).join(', ')}`;
|
|
4827
|
+
return `AS ${bodyParts.map((part) => `$$${part}$$`).join(', ')}`;
|
|
5186
4828
|
}
|
|
5187
4829
|
}
|
|
5188
4830
|
// Handle Array type (legacy support)
|
|
@@ -5190,20 +4832,27 @@ export class Deparser {
|
|
|
5190
4832
|
const bodyParts = argValue;
|
|
5191
4833
|
if (bodyParts.length === 1) {
|
|
5192
4834
|
const body = bodyParts[0];
|
|
5193
|
-
|
|
5194
|
-
|
|
4835
|
+
// Check if body contains $$ to avoid conflicts
|
|
4836
|
+
if (body.includes('$$')) {
|
|
4837
|
+
return `AS '${body.replace(/'/g, "''")}'`;
|
|
4838
|
+
}
|
|
4839
|
+
else {
|
|
4840
|
+
return `AS $$${body}$$`;
|
|
4841
|
+
}
|
|
5195
4842
|
}
|
|
5196
4843
|
else {
|
|
5197
|
-
return `AS ${bodyParts.map(part => {
|
|
5198
|
-
const delimiter = this.getFunctionDelimiter(part);
|
|
5199
|
-
return `${delimiter}${part}${delimiter}`;
|
|
5200
|
-
}).join(', ')}`;
|
|
4844
|
+
return `AS ${bodyParts.map(part => `$$${part}$$`).join(', ')}`;
|
|
5201
4845
|
}
|
|
5202
4846
|
}
|
|
5203
4847
|
// Handle String type (single function body)
|
|
5204
4848
|
else {
|
|
5205
|
-
|
|
5206
|
-
|
|
4849
|
+
// Check if argValue contains $$ to avoid conflicts
|
|
4850
|
+
if (argValue.includes('$$')) {
|
|
4851
|
+
return `AS '${argValue.replace(/'/g, "''")}'`;
|
|
4852
|
+
}
|
|
4853
|
+
else {
|
|
4854
|
+
return `AS $$${argValue}$$`;
|
|
4855
|
+
}
|
|
5207
4856
|
}
|
|
5208
4857
|
}
|
|
5209
4858
|
if (node.defname === 'language') {
|
|
@@ -5317,18 +4966,6 @@ export class Deparser {
|
|
|
5317
4966
|
: argValue;
|
|
5318
4967
|
return `${node.defname} = ${quotedValue}`;
|
|
5319
4968
|
}
|
|
5320
|
-
// Handle CopyStmt WITH clause options - uppercase format without quotes
|
|
5321
|
-
if (context.parentNodeTypes.includes('CopyStmt')) {
|
|
5322
|
-
if (node.defname === 'format' && node.arg && this.getNodeType(node.arg) === 'String') {
|
|
5323
|
-
const stringData = this.getNodeData(node.arg);
|
|
5324
|
-
return `FORMAT ${stringData.sval.toUpperCase()}`;
|
|
5325
|
-
}
|
|
5326
|
-
// Handle other COPY options with uppercase defname
|
|
5327
|
-
if (node.arg) {
|
|
5328
|
-
return `${node.defname.toUpperCase()} ${argValue}`;
|
|
5329
|
-
}
|
|
5330
|
-
return node.defname.toUpperCase();
|
|
5331
|
-
}
|
|
5332
4969
|
// Handle CREATE OPERATOR and CREATE TYPE context
|
|
5333
4970
|
if (context.parentNodeTypes.includes('DefineStmt')) {
|
|
5334
4971
|
const preservedName = this.preserveOperatorDefElemCase(node.defname);
|
|
@@ -6019,7 +5656,8 @@ export class Deparser {
|
|
|
6019
5656
|
output.push('NULL');
|
|
6020
5657
|
}
|
|
6021
5658
|
else if (node.comment) {
|
|
6022
|
-
|
|
5659
|
+
const escapedComment = node.comment.replace(/'/g, "''");
|
|
5660
|
+
output.push(`'${escapedComment}'`);
|
|
6023
5661
|
}
|
|
6024
5662
|
return output.join(' ');
|
|
6025
5663
|
}
|
|
@@ -6053,82 +5691,38 @@ export class Deparser {
|
|
|
6053
5691
|
return output.join(' ');
|
|
6054
5692
|
}
|
|
6055
5693
|
CreatePolicyStmt(node, context) {
|
|
6056
|
-
const output = [];
|
|
6057
|
-
const initialParts = ['CREATE', 'POLICY'];
|
|
5694
|
+
const output = ['CREATE', 'POLICY'];
|
|
6058
5695
|
if (node.policy_name) {
|
|
6059
|
-
|
|
5696
|
+
output.push(`"${node.policy_name}"`);
|
|
6060
5697
|
}
|
|
6061
|
-
output.push(
|
|
6062
|
-
// Add ON clause on new line in pretty mode
|
|
5698
|
+
output.push('ON');
|
|
6063
5699
|
if (node.table) {
|
|
6064
|
-
|
|
6065
|
-
output.push(this.formatter.newline() + this.formatter.indent(`ON ${this.RangeVar(node.table, context)}`));
|
|
6066
|
-
}
|
|
6067
|
-
else {
|
|
6068
|
-
output.push('ON');
|
|
6069
|
-
output.push(this.RangeVar(node.table, context));
|
|
6070
|
-
}
|
|
5700
|
+
output.push(this.RangeVar(node.table, context));
|
|
6071
5701
|
}
|
|
6072
5702
|
// Handle AS RESTRICTIVE/PERMISSIVE clause
|
|
6073
5703
|
if (node.permissive === undefined) {
|
|
6074
|
-
|
|
6075
|
-
output.push(this.formatter.newline() + this.formatter.indent('AS RESTRICTIVE'));
|
|
6076
|
-
}
|
|
6077
|
-
else {
|
|
6078
|
-
output.push('AS', 'RESTRICTIVE');
|
|
6079
|
-
}
|
|
5704
|
+
output.push('AS', 'RESTRICTIVE');
|
|
6080
5705
|
}
|
|
6081
5706
|
else if (node.permissive === true) {
|
|
6082
|
-
|
|
6083
|
-
output.push(this.formatter.newline() + this.formatter.indent('AS PERMISSIVE'));
|
|
6084
|
-
}
|
|
6085
|
-
else {
|
|
6086
|
-
output.push('AS', 'PERMISSIVE');
|
|
6087
|
-
}
|
|
5707
|
+
output.push('AS', 'PERMISSIVE');
|
|
6088
5708
|
}
|
|
6089
5709
|
if (node.cmd_name) {
|
|
6090
|
-
|
|
6091
|
-
output.push(this.formatter.newline() + this.formatter.indent(`FOR ${node.cmd_name.toUpperCase()}`));
|
|
6092
|
-
}
|
|
6093
|
-
else {
|
|
6094
|
-
output.push('FOR', node.cmd_name.toUpperCase());
|
|
6095
|
-
}
|
|
5710
|
+
output.push('FOR', node.cmd_name.toUpperCase());
|
|
6096
5711
|
}
|
|
6097
5712
|
if (node.roles && node.roles.length > 0) {
|
|
5713
|
+
output.push('TO');
|
|
6098
5714
|
const roles = ListUtils.unwrapList(node.roles).map(role => this.visit(role, context));
|
|
6099
|
-
|
|
6100
|
-
output.push(this.formatter.newline() + this.formatter.indent(`TO ${roles.join(', ')}`));
|
|
6101
|
-
}
|
|
6102
|
-
else {
|
|
6103
|
-
output.push('TO');
|
|
6104
|
-
output.push(roles.join(', '));
|
|
6105
|
-
}
|
|
5715
|
+
output.push(roles.join(', '));
|
|
6106
5716
|
}
|
|
6107
5717
|
if (node.qual) {
|
|
6108
|
-
|
|
6109
|
-
|
|
6110
|
-
output.push(this.formatter.newline() + this.formatter.indent('USING ('));
|
|
6111
|
-
output.push(this.formatter.newline() + this.formatter.indent(this.formatter.indent(qualExpr)));
|
|
6112
|
-
output.push(this.formatter.newline() + this.formatter.indent(')'));
|
|
6113
|
-
}
|
|
6114
|
-
else {
|
|
6115
|
-
output.push('USING');
|
|
6116
|
-
output.push(`(${this.visit(node.qual, context)})`);
|
|
6117
|
-
}
|
|
5718
|
+
output.push('USING');
|
|
5719
|
+
output.push(`(${this.visit(node.qual, context)})`);
|
|
6118
5720
|
}
|
|
6119
5721
|
if (node.with_check) {
|
|
6120
|
-
|
|
6121
|
-
|
|
6122
|
-
output.push(this.formatter.newline() + this.formatter.indent('WITH CHECK ('));
|
|
6123
|
-
output.push(this.formatter.newline() + this.formatter.indent(this.formatter.indent(checkExpr)));
|
|
6124
|
-
output.push(this.formatter.newline() + this.formatter.indent(')'));
|
|
6125
|
-
}
|
|
6126
|
-
else {
|
|
6127
|
-
output.push('WITH CHECK');
|
|
6128
|
-
output.push(`(${this.visit(node.with_check, context)})`);
|
|
6129
|
-
}
|
|
5722
|
+
output.push('WITH CHECK');
|
|
5723
|
+
output.push(`(${this.visit(node.with_check, context)})`);
|
|
6130
5724
|
}
|
|
6131
|
-
return
|
|
5725
|
+
return output.join(' ');
|
|
6132
5726
|
}
|
|
6133
5727
|
AlterPolicyStmt(node, context) {
|
|
6134
5728
|
const output = ['ALTER', 'POLICY'];
|
|
@@ -6368,12 +5962,12 @@ export class Deparser {
|
|
|
6368
5962
|
processedArgs.push(`LANGUAGE ${langValue}`);
|
|
6369
5963
|
}
|
|
6370
5964
|
else if (defElem.defname === 'as') {
|
|
6371
|
-
// Handle code block with
|
|
5965
|
+
// Handle code block with dollar quoting
|
|
6372
5966
|
const argNodeType = this.getNodeType(defElem.arg);
|
|
6373
5967
|
if (argNodeType === 'String') {
|
|
6374
5968
|
const stringNode = this.getNodeData(defElem.arg);
|
|
6375
|
-
const
|
|
6376
|
-
processedArgs.push(`${
|
|
5969
|
+
const dollarTag = this.generateUniqueDollarTag(stringNode.sval);
|
|
5970
|
+
processedArgs.push(`${dollarTag}${stringNode.sval}${dollarTag}`);
|
|
6377
5971
|
}
|
|
6378
5972
|
else {
|
|
6379
5973
|
processedArgs.push(this.visit(defElem.arg, doContext));
|
|
@@ -6407,11 +6001,9 @@ export class Deparser {
|
|
|
6407
6001
|
}
|
|
6408
6002
|
InlineCodeBlock(node, context) {
|
|
6409
6003
|
if (node.source_text) {
|
|
6410
|
-
|
|
6411
|
-
return `${delimiter}${node.source_text}${delimiter}`;
|
|
6004
|
+
return `$$${node.source_text}$$`;
|
|
6412
6005
|
}
|
|
6413
|
-
|
|
6414
|
-
return `${delimiter}${delimiter}`;
|
|
6006
|
+
return '$$$$';
|
|
6415
6007
|
}
|
|
6416
6008
|
CallContext(node, context) {
|
|
6417
6009
|
if (node.atomic !== undefined) {
|
|
@@ -9994,8 +9586,17 @@ export class Deparser {
|
|
|
9994
9586
|
}
|
|
9995
9587
|
return output.join(' ');
|
|
9996
9588
|
}
|
|
9997
|
-
|
|
9998
|
-
|
|
9999
|
-
|
|
9589
|
+
version(node, context) {
|
|
9590
|
+
// Handle version node - typically just return the version number
|
|
9591
|
+
if (typeof node === 'number') {
|
|
9592
|
+
return node.toString();
|
|
9593
|
+
}
|
|
9594
|
+
if (typeof node === 'string') {
|
|
9595
|
+
return node;
|
|
9596
|
+
}
|
|
9597
|
+
if (node && typeof node === 'object' && node.version) {
|
|
9598
|
+
return node.version.toString();
|
|
9599
|
+
}
|
|
9600
|
+
return '';
|
|
10000
9601
|
}
|
|
10001
9602
|
}
|