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