pgsql-deparser 17.7.2 → 17.8.1
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 +44 -2
- package/deparser.d.ts +2 -0
- package/deparser.js +363 -86
- package/esm/deparser.js +363 -86
- package/esm/utils/sql-formatter.js +11 -2
- package/package.json +2 -2
- package/utils/sql-formatter.d.ts +3 -1
- package/utils/sql-formatter.js +11 -2
package/esm/deparser.js
CHANGED
|
@@ -48,7 +48,7 @@ export class Deparser {
|
|
|
48
48
|
tree;
|
|
49
49
|
options;
|
|
50
50
|
constructor(tree, opts = {}) {
|
|
51
|
-
this.formatter = new SqlFormatter(opts.newline, opts.tab);
|
|
51
|
+
this.formatter = new SqlFormatter(opts.newline, opts.tab, opts.pretty);
|
|
52
52
|
// Set default options
|
|
53
53
|
this.options = {
|
|
54
54
|
functionDelimiter: '$$',
|
|
@@ -181,7 +181,9 @@ export class Deparser {
|
|
|
181
181
|
}
|
|
182
182
|
if (!node.op || node.op === 'SETOP_NONE') {
|
|
183
183
|
if (node.valuesLists == null) {
|
|
184
|
-
|
|
184
|
+
if (!this.formatter.isPretty() || !node.targetList) {
|
|
185
|
+
output.push('SELECT');
|
|
186
|
+
}
|
|
185
187
|
}
|
|
186
188
|
}
|
|
187
189
|
else {
|
|
@@ -227,41 +229,82 @@ export class Deparser {
|
|
|
227
229
|
output.push(rightStmt);
|
|
228
230
|
}
|
|
229
231
|
}
|
|
232
|
+
// Handle DISTINCT clause - in pretty mode, we'll include it in the SELECT clause
|
|
233
|
+
let distinctPart = '';
|
|
230
234
|
if (node.distinctClause) {
|
|
231
235
|
const distinctClause = ListUtils.unwrapList(node.distinctClause);
|
|
232
236
|
if (distinctClause.length > 0 && Object.keys(distinctClause[0]).length > 0) {
|
|
233
|
-
output.push('DISTINCT ON');
|
|
234
237
|
const clause = distinctClause
|
|
235
238
|
.map(e => this.visit(e, { ...context, select: true }))
|
|
236
239
|
.join(', ');
|
|
237
|
-
|
|
240
|
+
distinctPart = ' DISTINCT ON ' + this.formatter.parens(clause);
|
|
238
241
|
}
|
|
239
242
|
else {
|
|
240
|
-
|
|
243
|
+
distinctPart = ' DISTINCT';
|
|
244
|
+
}
|
|
245
|
+
if (!this.formatter.isPretty()) {
|
|
246
|
+
if (distinctClause.length > 0 && Object.keys(distinctClause[0]).length > 0) {
|
|
247
|
+
output.push('DISTINCT ON');
|
|
248
|
+
const clause = distinctClause
|
|
249
|
+
.map(e => this.visit(e, { ...context, select: true }))
|
|
250
|
+
.join(', ');
|
|
251
|
+
output.push(this.formatter.parens(clause));
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
output.push('DISTINCT');
|
|
255
|
+
}
|
|
241
256
|
}
|
|
242
257
|
}
|
|
243
258
|
if (node.targetList) {
|
|
244
259
|
const targetList = ListUtils.unwrapList(node.targetList);
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
260
|
+
if (this.formatter.isPretty()) {
|
|
261
|
+
const targetStrings = targetList
|
|
262
|
+
.map(e => {
|
|
263
|
+
const targetStr = this.visit(e, { ...context, select: true });
|
|
264
|
+
if (this.containsMultilineStringLiteral(targetStr)) {
|
|
265
|
+
return targetStr;
|
|
266
|
+
}
|
|
267
|
+
return this.formatter.indent(targetStr);
|
|
268
|
+
});
|
|
269
|
+
const formattedTargets = targetStrings.join(',' + this.formatter.newline());
|
|
270
|
+
output.push('SELECT' + distinctPart);
|
|
271
|
+
output.push(formattedTargets);
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
const targets = targetList
|
|
275
|
+
.map(e => this.visit(e, { ...context, select: true }))
|
|
276
|
+
.join(', ');
|
|
277
|
+
output.push(targets);
|
|
278
|
+
}
|
|
249
279
|
}
|
|
250
280
|
if (node.intoClause) {
|
|
251
281
|
output.push('INTO');
|
|
252
282
|
output.push(this.IntoClause(node.intoClause, context));
|
|
253
283
|
}
|
|
254
284
|
if (node.fromClause) {
|
|
255
|
-
output.push('FROM');
|
|
256
285
|
const fromList = ListUtils.unwrapList(node.fromClause);
|
|
257
286
|
const fromItems = fromList
|
|
258
287
|
.map(e => this.deparse(e, { ...context, from: true }))
|
|
259
288
|
.join(', ');
|
|
260
|
-
output.push(fromItems);
|
|
289
|
+
output.push('FROM ' + fromItems.trim());
|
|
261
290
|
}
|
|
262
291
|
if (node.whereClause) {
|
|
263
|
-
|
|
264
|
-
|
|
292
|
+
if (this.formatter.isPretty()) {
|
|
293
|
+
output.push('WHERE');
|
|
294
|
+
const whereExpr = this.visit(node.whereClause, context);
|
|
295
|
+
const lines = whereExpr.split(this.formatter.newline());
|
|
296
|
+
const indentedLines = lines.map((line, index) => {
|
|
297
|
+
if (index === 0) {
|
|
298
|
+
return this.formatter.indent(line);
|
|
299
|
+
}
|
|
300
|
+
return line;
|
|
301
|
+
});
|
|
302
|
+
output.push(indentedLines.join(this.formatter.newline()));
|
|
303
|
+
}
|
|
304
|
+
else {
|
|
305
|
+
output.push('WHERE');
|
|
306
|
+
output.push(this.visit(node.whereClause, context));
|
|
307
|
+
}
|
|
265
308
|
}
|
|
266
309
|
if (node.valuesLists) {
|
|
267
310
|
output.push('VALUES');
|
|
@@ -272,16 +315,43 @@ export class Deparser {
|
|
|
272
315
|
output.push(lists.join(', '));
|
|
273
316
|
}
|
|
274
317
|
if (node.groupClause) {
|
|
275
|
-
output.push('GROUP BY');
|
|
276
318
|
const groupList = ListUtils.unwrapList(node.groupClause);
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
319
|
+
if (this.formatter.isPretty()) {
|
|
320
|
+
const groupItems = groupList
|
|
321
|
+
.map(e => {
|
|
322
|
+
const groupStr = this.visit(e, { ...context, group: true });
|
|
323
|
+
if (this.containsMultilineStringLiteral(groupStr)) {
|
|
324
|
+
return groupStr;
|
|
325
|
+
}
|
|
326
|
+
return this.formatter.indent(groupStr);
|
|
327
|
+
})
|
|
328
|
+
.join(',' + this.formatter.newline());
|
|
329
|
+
output.push('GROUP BY');
|
|
330
|
+
output.push(groupItems);
|
|
331
|
+
}
|
|
332
|
+
else {
|
|
333
|
+
output.push('GROUP BY');
|
|
334
|
+
const groupItems = groupList
|
|
335
|
+
.map(e => this.visit(e, { ...context, group: true }))
|
|
336
|
+
.join(', ');
|
|
337
|
+
output.push(groupItems);
|
|
338
|
+
}
|
|
281
339
|
}
|
|
282
340
|
if (node.havingClause) {
|
|
283
|
-
|
|
284
|
-
|
|
341
|
+
if (this.formatter.isPretty()) {
|
|
342
|
+
output.push('HAVING');
|
|
343
|
+
const havingStr = this.visit(node.havingClause, context);
|
|
344
|
+
if (this.containsMultilineStringLiteral(havingStr)) {
|
|
345
|
+
output.push(havingStr);
|
|
346
|
+
}
|
|
347
|
+
else {
|
|
348
|
+
output.push(this.formatter.indent(havingStr));
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
else {
|
|
352
|
+
output.push('HAVING');
|
|
353
|
+
output.push(this.visit(node.havingClause, context));
|
|
354
|
+
}
|
|
285
355
|
}
|
|
286
356
|
if (node.windowClause) {
|
|
287
357
|
output.push('WINDOW');
|
|
@@ -292,20 +362,33 @@ export class Deparser {
|
|
|
292
362
|
output.push(windowClauses);
|
|
293
363
|
}
|
|
294
364
|
if (node.sortClause) {
|
|
295
|
-
output.push('ORDER BY');
|
|
296
365
|
const sortList = ListUtils.unwrapList(node.sortClause);
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
366
|
+
if (this.formatter.isPretty()) {
|
|
367
|
+
const sortItems = sortList
|
|
368
|
+
.map(e => {
|
|
369
|
+
const sortStr = this.visit(e, { ...context, sort: true });
|
|
370
|
+
if (this.containsMultilineStringLiteral(sortStr)) {
|
|
371
|
+
return sortStr;
|
|
372
|
+
}
|
|
373
|
+
return this.formatter.indent(sortStr);
|
|
374
|
+
})
|
|
375
|
+
.join(',' + this.formatter.newline());
|
|
376
|
+
output.push('ORDER BY');
|
|
377
|
+
output.push(sortItems);
|
|
378
|
+
}
|
|
379
|
+
else {
|
|
380
|
+
output.push('ORDER BY');
|
|
381
|
+
const sortItems = sortList
|
|
382
|
+
.map(e => this.visit(e, { ...context, sort: true }))
|
|
383
|
+
.join(', ');
|
|
384
|
+
output.push(sortItems);
|
|
385
|
+
}
|
|
301
386
|
}
|
|
302
387
|
if (node.limitCount) {
|
|
303
|
-
output.push('LIMIT');
|
|
304
|
-
output.push(this.visit(node.limitCount, context));
|
|
388
|
+
output.push('LIMIT ' + this.visit(node.limitCount, context));
|
|
305
389
|
}
|
|
306
390
|
if (node.limitOffset) {
|
|
307
|
-
output.push('OFFSET');
|
|
308
|
-
output.push(this.visit(node.limitOffset, context));
|
|
391
|
+
output.push('OFFSET ' + this.visit(node.limitOffset, context));
|
|
309
392
|
}
|
|
310
393
|
if (node.lockingClause) {
|
|
311
394
|
const lockingList = ListUtils.unwrapList(node.lockingClause);
|
|
@@ -314,6 +397,10 @@ export class Deparser {
|
|
|
314
397
|
.join(' ');
|
|
315
398
|
output.push(lockingClauses);
|
|
316
399
|
}
|
|
400
|
+
if (this.formatter.isPretty()) {
|
|
401
|
+
const filteredOutput = output.filter(item => item.trim() !== '');
|
|
402
|
+
return filteredOutput.join(this.formatter.newline());
|
|
403
|
+
}
|
|
317
404
|
return output.join(' ');
|
|
318
405
|
}
|
|
319
406
|
A_Expr(node, context) {
|
|
@@ -803,9 +890,24 @@ export class Deparser {
|
|
|
803
890
|
if (node.recursive) {
|
|
804
891
|
output.push('RECURSIVE');
|
|
805
892
|
}
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
893
|
+
if (node.ctes && node.ctes.length > 0) {
|
|
894
|
+
const ctes = ListUtils.unwrapList(node.ctes);
|
|
895
|
+
if (this.formatter.isPretty()) {
|
|
896
|
+
const cteStrings = ctes.map((cte, index) => {
|
|
897
|
+
const cteStr = this.visit(cte, context);
|
|
898
|
+
const prefix = index === 0 ? this.formatter.newline() : ',' + this.formatter.newline();
|
|
899
|
+
if (this.containsMultilineStringLiteral(cteStr)) {
|
|
900
|
+
return prefix + cteStr;
|
|
901
|
+
}
|
|
902
|
+
return prefix + this.formatter.indent(cteStr);
|
|
903
|
+
});
|
|
904
|
+
output.push(cteStrings.join(''));
|
|
905
|
+
}
|
|
906
|
+
else {
|
|
907
|
+
const cteStrings = ctes.map(cte => this.visit(cte, context));
|
|
908
|
+
output.push(cteStrings.join(', '));
|
|
909
|
+
}
|
|
910
|
+
}
|
|
809
911
|
return output.join(' ');
|
|
810
912
|
}
|
|
811
913
|
ResTarget(node, context) {
|
|
@@ -889,11 +991,23 @@ export class Deparser {
|
|
|
889
991
|
// return formatStr.replace('%s', () => andArgs); // ✅ Function callback prevents interpretation
|
|
890
992
|
switch (boolop) {
|
|
891
993
|
case 'AND_EXPR':
|
|
892
|
-
|
|
893
|
-
|
|
994
|
+
if (this.formatter.isPretty() && args.length > 1) {
|
|
995
|
+
const andArgs = args.map(arg => this.visit(arg, boolContext)).join(this.formatter.newline() + ' AND ');
|
|
996
|
+
return formatStr.replace('%s', () => andArgs);
|
|
997
|
+
}
|
|
998
|
+
else {
|
|
999
|
+
const andArgs = args.map(arg => this.visit(arg, boolContext)).join(' AND ');
|
|
1000
|
+
return formatStr.replace('%s', () => andArgs);
|
|
1001
|
+
}
|
|
894
1002
|
case 'OR_EXPR':
|
|
895
|
-
|
|
896
|
-
|
|
1003
|
+
if (this.formatter.isPretty() && args.length > 1) {
|
|
1004
|
+
const orArgs = args.map(arg => this.visit(arg, boolContext)).join(this.formatter.newline() + ' OR ');
|
|
1005
|
+
return formatStr.replace('%s', () => orArgs);
|
|
1006
|
+
}
|
|
1007
|
+
else {
|
|
1008
|
+
const orArgs = args.map(arg => this.visit(arg, boolContext)).join(' OR ');
|
|
1009
|
+
return formatStr.replace('%s', () => orArgs);
|
|
1010
|
+
}
|
|
897
1011
|
case 'NOT_EXPR':
|
|
898
1012
|
return `NOT (${this.visit(args[0], context)})`;
|
|
899
1013
|
default:
|
|
@@ -1095,7 +1209,13 @@ export class Deparser {
|
|
|
1095
1209
|
windowParts.push(frameClause);
|
|
1096
1210
|
}
|
|
1097
1211
|
if (windowParts.length > 0) {
|
|
1098
|
-
|
|
1212
|
+
if (this.formatter.isPretty() && windowParts.length > 1) {
|
|
1213
|
+
const formattedParts = windowParts.map(part => this.formatter.indent(part));
|
|
1214
|
+
result += ` OVER (${this.formatter.newline()}${formattedParts.join(this.formatter.newline())}${this.formatter.newline()})`;
|
|
1215
|
+
}
|
|
1216
|
+
else {
|
|
1217
|
+
result += ` OVER (${windowParts.join(' ')})`;
|
|
1218
|
+
}
|
|
1099
1219
|
}
|
|
1100
1220
|
else {
|
|
1101
1221
|
result += ` OVER ()`;
|
|
@@ -1719,15 +1839,39 @@ export class Deparser {
|
|
|
1719
1839
|
output.push(this.visit(node.arg, context));
|
|
1720
1840
|
}
|
|
1721
1841
|
const args = ListUtils.unwrapList(node.args);
|
|
1722
|
-
|
|
1723
|
-
|
|
1842
|
+
if (this.formatter.isPretty() && args.length > 0) {
|
|
1843
|
+
for (const arg of args) {
|
|
1844
|
+
const whenClause = this.visit(arg, context);
|
|
1845
|
+
if (this.containsMultilineStringLiteral(whenClause)) {
|
|
1846
|
+
output.push(this.formatter.newline() + whenClause);
|
|
1847
|
+
}
|
|
1848
|
+
else {
|
|
1849
|
+
output.push(this.formatter.newline() + this.formatter.indent(whenClause));
|
|
1850
|
+
}
|
|
1851
|
+
}
|
|
1852
|
+
if (node.defresult) {
|
|
1853
|
+
const elseResult = this.visit(node.defresult, context);
|
|
1854
|
+
if (this.containsMultilineStringLiteral(elseResult)) {
|
|
1855
|
+
output.push(this.formatter.newline() + 'ELSE ' + elseResult);
|
|
1856
|
+
}
|
|
1857
|
+
else {
|
|
1858
|
+
output.push(this.formatter.newline() + this.formatter.indent('ELSE ' + elseResult));
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
output.push(this.formatter.newline() + 'END');
|
|
1862
|
+
return output.join(' ');
|
|
1724
1863
|
}
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1864
|
+
else {
|
|
1865
|
+
for (const arg of args) {
|
|
1866
|
+
output.push(this.visit(arg, context));
|
|
1867
|
+
}
|
|
1868
|
+
if (node.defresult) {
|
|
1869
|
+
output.push('ELSE');
|
|
1870
|
+
output.push(this.visit(node.defresult, context));
|
|
1871
|
+
}
|
|
1872
|
+
output.push('END');
|
|
1873
|
+
return output.join(' ');
|
|
1728
1874
|
}
|
|
1729
|
-
output.push('END');
|
|
1730
|
-
return output.join(' ');
|
|
1731
1875
|
}
|
|
1732
1876
|
CoalesceExpr(node, context) {
|
|
1733
1877
|
const args = ListUtils.unwrapList(node.args);
|
|
@@ -1940,7 +2084,13 @@ export class Deparser {
|
|
|
1940
2084
|
const elementStrs = elements.map(el => {
|
|
1941
2085
|
return this.deparse(el, context);
|
|
1942
2086
|
});
|
|
1943
|
-
|
|
2087
|
+
if (this.formatter.isPretty()) {
|
|
2088
|
+
const formattedElements = elementStrs.map(el => this.formatter.indent(el)).join(',' + this.formatter.newline());
|
|
2089
|
+
output.push('(' + this.formatter.newline() + formattedElements + this.formatter.newline() + ')');
|
|
2090
|
+
}
|
|
2091
|
+
else {
|
|
2092
|
+
output.push(this.formatter.parens(elementStrs.join(', ')));
|
|
2093
|
+
}
|
|
1944
2094
|
}
|
|
1945
2095
|
else if (!node.partbound) {
|
|
1946
2096
|
output.push(this.formatter.parens(''));
|
|
@@ -2225,38 +2375,52 @@ export class Deparser {
|
|
|
2225
2375
|
}
|
|
2226
2376
|
}
|
|
2227
2377
|
if (node.fk_upd_action && node.fk_upd_action !== 'a') {
|
|
2228
|
-
|
|
2378
|
+
let updateClause = 'ON UPDATE ';
|
|
2229
2379
|
switch (node.fk_upd_action) {
|
|
2230
2380
|
case 'r':
|
|
2231
|
-
|
|
2381
|
+
updateClause += 'RESTRICT';
|
|
2232
2382
|
break;
|
|
2233
2383
|
case 'c':
|
|
2234
|
-
|
|
2384
|
+
updateClause += 'CASCADE';
|
|
2235
2385
|
break;
|
|
2236
2386
|
case 'n':
|
|
2237
|
-
|
|
2387
|
+
updateClause += 'SET NULL';
|
|
2238
2388
|
break;
|
|
2239
2389
|
case 'd':
|
|
2240
|
-
|
|
2390
|
+
updateClause += 'SET DEFAULT';
|
|
2241
2391
|
break;
|
|
2242
2392
|
}
|
|
2393
|
+
if (this.formatter.isPretty()) {
|
|
2394
|
+
output.push('\n' + this.formatter.indent(updateClause));
|
|
2395
|
+
}
|
|
2396
|
+
else {
|
|
2397
|
+
output.push('ON UPDATE');
|
|
2398
|
+
output.push(updateClause.replace('ON UPDATE ', ''));
|
|
2399
|
+
}
|
|
2243
2400
|
}
|
|
2244
2401
|
if (node.fk_del_action && node.fk_del_action !== 'a') {
|
|
2245
|
-
|
|
2402
|
+
let deleteClause = 'ON DELETE ';
|
|
2246
2403
|
switch (node.fk_del_action) {
|
|
2247
2404
|
case 'r':
|
|
2248
|
-
|
|
2405
|
+
deleteClause += 'RESTRICT';
|
|
2249
2406
|
break;
|
|
2250
2407
|
case 'c':
|
|
2251
|
-
|
|
2408
|
+
deleteClause += 'CASCADE';
|
|
2252
2409
|
break;
|
|
2253
2410
|
case 'n':
|
|
2254
|
-
|
|
2411
|
+
deleteClause += 'SET NULL';
|
|
2255
2412
|
break;
|
|
2256
2413
|
case 'd':
|
|
2257
|
-
|
|
2414
|
+
deleteClause += 'SET DEFAULT';
|
|
2258
2415
|
break;
|
|
2259
2416
|
}
|
|
2417
|
+
if (this.formatter.isPretty()) {
|
|
2418
|
+
output.push('\n' + this.formatter.indent(deleteClause));
|
|
2419
|
+
}
|
|
2420
|
+
else {
|
|
2421
|
+
output.push('ON DELETE');
|
|
2422
|
+
output.push(deleteClause.replace('ON DELETE ', ''));
|
|
2423
|
+
}
|
|
2260
2424
|
}
|
|
2261
2425
|
// Handle NOT VALID for foreign key constraints - only for table constraints, not domain constraints
|
|
2262
2426
|
if (node.skip_validation && !context.isDomainConstraint) {
|
|
@@ -2314,17 +2478,48 @@ export class Deparser {
|
|
|
2314
2478
|
// Handle deferrable constraints for all constraint types that support it
|
|
2315
2479
|
if (node.contype === 'CONSTR_PRIMARY' || node.contype === 'CONSTR_UNIQUE' || node.contype === 'CONSTR_FOREIGN') {
|
|
2316
2480
|
if (node.deferrable) {
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2481
|
+
if (this.formatter.isPretty() && node.contype === 'CONSTR_FOREIGN') {
|
|
2482
|
+
output.push('\n' + this.formatter.indent('DEFERRABLE'));
|
|
2483
|
+
if (node.initdeferred === true) {
|
|
2484
|
+
output.push('\n' + this.formatter.indent('INITIALLY DEFERRED'));
|
|
2485
|
+
}
|
|
2486
|
+
else if (node.initdeferred === false) {
|
|
2487
|
+
output.push('\n' + this.formatter.indent('INITIALLY IMMEDIATE'));
|
|
2488
|
+
}
|
|
2320
2489
|
}
|
|
2321
|
-
else
|
|
2322
|
-
output.push('
|
|
2490
|
+
else {
|
|
2491
|
+
output.push('DEFERRABLE');
|
|
2492
|
+
if (node.initdeferred === true) {
|
|
2493
|
+
output.push('INITIALLY DEFERRED');
|
|
2494
|
+
}
|
|
2495
|
+
else if (node.initdeferred === false) {
|
|
2496
|
+
output.push('INITIALLY IMMEDIATE');
|
|
2497
|
+
}
|
|
2323
2498
|
}
|
|
2324
2499
|
}
|
|
2325
2500
|
else if (node.deferrable === false) {
|
|
2326
|
-
|
|
2501
|
+
if (this.formatter.isPretty() && node.contype === 'CONSTR_FOREIGN') {
|
|
2502
|
+
output.push('\n' + this.formatter.indent('NOT DEFERRABLE'));
|
|
2503
|
+
}
|
|
2504
|
+
else {
|
|
2505
|
+
output.push('NOT DEFERRABLE');
|
|
2506
|
+
}
|
|
2507
|
+
}
|
|
2508
|
+
}
|
|
2509
|
+
if (this.formatter.isPretty() && node.contype === 'CONSTR_FOREIGN') {
|
|
2510
|
+
let result = '';
|
|
2511
|
+
for (let i = 0; i < output.length; i++) {
|
|
2512
|
+
if (output[i].startsWith('\n')) {
|
|
2513
|
+
result += output[i];
|
|
2514
|
+
}
|
|
2515
|
+
else {
|
|
2516
|
+
if (i > 0 && !output[i - 1].startsWith('\n')) {
|
|
2517
|
+
result += ' ';
|
|
2518
|
+
}
|
|
2519
|
+
result += output[i];
|
|
2520
|
+
}
|
|
2327
2521
|
}
|
|
2522
|
+
return result;
|
|
2328
2523
|
}
|
|
2329
2524
|
return output.join(' ');
|
|
2330
2525
|
}
|
|
@@ -3048,11 +3243,9 @@ export class Deparser {
|
|
|
3048
3243
|
}
|
|
3049
3244
|
switch (node.jointype) {
|
|
3050
3245
|
case 'JOIN_INNER':
|
|
3051
|
-
// Handle NATURAL JOIN first - it has isNatural=true (NATURAL already added above)
|
|
3052
3246
|
if (node.isNatural) {
|
|
3053
3247
|
joinStr += 'JOIN';
|
|
3054
3248
|
}
|
|
3055
|
-
// Handle CROSS JOIN case - when there's no quals, no usingClause, and not natural
|
|
3056
3249
|
else if (!node.quals && (!node.usingClause || node.usingClause.length === 0)) {
|
|
3057
3250
|
joinStr += 'CROSS JOIN';
|
|
3058
3251
|
}
|
|
@@ -3072,26 +3265,63 @@ export class Deparser {
|
|
|
3072
3265
|
default:
|
|
3073
3266
|
joinStr += 'JOIN';
|
|
3074
3267
|
}
|
|
3075
|
-
output.push(joinStr);
|
|
3076
3268
|
if (node.rarg) {
|
|
3077
3269
|
let rargStr = this.visit(node.rarg, context);
|
|
3078
3270
|
if (node.rarg && 'JoinExpr' in node.rarg && !node.rarg.JoinExpr.alias) {
|
|
3079
3271
|
rargStr = `(${rargStr})`;
|
|
3080
3272
|
}
|
|
3081
|
-
|
|
3273
|
+
if (this.formatter.isPretty()) {
|
|
3274
|
+
output.push(this.formatter.newline() + joinStr + ' ' + rargStr);
|
|
3275
|
+
}
|
|
3276
|
+
else {
|
|
3277
|
+
output.push(joinStr + ' ' + rargStr);
|
|
3278
|
+
}
|
|
3279
|
+
}
|
|
3280
|
+
else {
|
|
3281
|
+
if (this.formatter.isPretty()) {
|
|
3282
|
+
output.push(this.formatter.newline() + joinStr);
|
|
3283
|
+
}
|
|
3284
|
+
else {
|
|
3285
|
+
output.push(joinStr);
|
|
3286
|
+
}
|
|
3082
3287
|
}
|
|
3083
3288
|
if (node.usingClause && node.usingClause.length > 0) {
|
|
3084
|
-
output.push('USING');
|
|
3085
3289
|
const usingList = ListUtils.unwrapList(node.usingClause);
|
|
3086
3290
|
const columnNames = usingList.map(col => this.visit(col, context));
|
|
3087
|
-
|
|
3291
|
+
if (this.formatter.isPretty()) {
|
|
3292
|
+
output.push(` USING (${columnNames.join(', ')})`);
|
|
3293
|
+
}
|
|
3294
|
+
else {
|
|
3295
|
+
output.push(`USING (${columnNames.join(', ')})`);
|
|
3296
|
+
}
|
|
3088
3297
|
}
|
|
3089
3298
|
else if (node.quals) {
|
|
3090
|
-
|
|
3091
|
-
|
|
3299
|
+
const qualsStr = this.visit(node.quals, context);
|
|
3300
|
+
if (this.formatter.isPretty()) {
|
|
3301
|
+
// For complex JOIN conditions, format with proper indentation
|
|
3302
|
+
if (qualsStr.includes('AND') || qualsStr.includes('OR') || qualsStr.length > 50) {
|
|
3303
|
+
if (this.containsMultilineStringLiteral(qualsStr)) {
|
|
3304
|
+
output.push(` ON ${qualsStr}`);
|
|
3305
|
+
}
|
|
3306
|
+
else {
|
|
3307
|
+
output.push(` ON${this.formatter.newline()}${this.formatter.indent(qualsStr)}`);
|
|
3308
|
+
}
|
|
3309
|
+
}
|
|
3310
|
+
else {
|
|
3311
|
+
output.push(` ON ${qualsStr}`);
|
|
3312
|
+
}
|
|
3313
|
+
}
|
|
3314
|
+
else {
|
|
3315
|
+
output.push(`ON ${qualsStr}`);
|
|
3316
|
+
}
|
|
3317
|
+
}
|
|
3318
|
+
let result;
|
|
3319
|
+
if (this.formatter.isPretty()) {
|
|
3320
|
+
result = output.join('');
|
|
3321
|
+
}
|
|
3322
|
+
else {
|
|
3323
|
+
result = output.join(' ');
|
|
3092
3324
|
}
|
|
3093
|
-
let result = output.join(' ');
|
|
3094
|
-
// Handle join_using_alias first (for USING clause aliases like "AS x")
|
|
3095
3325
|
if (node.join_using_alias && node.join_using_alias.aliasname) {
|
|
3096
3326
|
let aliasStr = node.join_using_alias.aliasname;
|
|
3097
3327
|
if (node.join_using_alias.colnames && node.join_using_alias.colnames.length > 0) {
|
|
@@ -3101,7 +3331,6 @@ export class Deparser {
|
|
|
3101
3331
|
}
|
|
3102
3332
|
result += ` AS ${aliasStr}`;
|
|
3103
3333
|
}
|
|
3104
|
-
// Handle regular alias (for outer table aliases like "y")
|
|
3105
3334
|
if (node.alias && node.alias.aliasname) {
|
|
3106
3335
|
let aliasStr = node.alias.aliasname;
|
|
3107
3336
|
if (node.alias.colnames && node.alias.colnames.length > 0) {
|
|
@@ -5820,38 +6049,82 @@ export class Deparser {
|
|
|
5820
6049
|
return output.join(' ');
|
|
5821
6050
|
}
|
|
5822
6051
|
CreatePolicyStmt(node, context) {
|
|
5823
|
-
const output = [
|
|
6052
|
+
const output = [];
|
|
6053
|
+
const initialParts = ['CREATE', 'POLICY'];
|
|
5824
6054
|
if (node.policy_name) {
|
|
5825
|
-
|
|
6055
|
+
initialParts.push(`"${node.policy_name}"`);
|
|
5826
6056
|
}
|
|
5827
|
-
output.push('
|
|
6057
|
+
output.push(initialParts.join(' '));
|
|
6058
|
+
// Add ON clause on new line in pretty mode
|
|
5828
6059
|
if (node.table) {
|
|
5829
|
-
|
|
6060
|
+
if (this.formatter.isPretty()) {
|
|
6061
|
+
output.push(this.formatter.newline() + this.formatter.indent(`ON ${this.RangeVar(node.table, context)}`));
|
|
6062
|
+
}
|
|
6063
|
+
else {
|
|
6064
|
+
output.push('ON');
|
|
6065
|
+
output.push(this.RangeVar(node.table, context));
|
|
6066
|
+
}
|
|
5830
6067
|
}
|
|
5831
6068
|
// Handle AS RESTRICTIVE/PERMISSIVE clause
|
|
5832
6069
|
if (node.permissive === undefined) {
|
|
5833
|
-
|
|
6070
|
+
if (this.formatter.isPretty()) {
|
|
6071
|
+
output.push(this.formatter.newline() + this.formatter.indent('AS RESTRICTIVE'));
|
|
6072
|
+
}
|
|
6073
|
+
else {
|
|
6074
|
+
output.push('AS', 'RESTRICTIVE');
|
|
6075
|
+
}
|
|
5834
6076
|
}
|
|
5835
6077
|
else if (node.permissive === true) {
|
|
5836
|
-
|
|
6078
|
+
if (this.formatter.isPretty()) {
|
|
6079
|
+
output.push(this.formatter.newline() + this.formatter.indent('AS PERMISSIVE'));
|
|
6080
|
+
}
|
|
6081
|
+
else {
|
|
6082
|
+
output.push('AS', 'PERMISSIVE');
|
|
6083
|
+
}
|
|
5837
6084
|
}
|
|
5838
6085
|
if (node.cmd_name) {
|
|
5839
|
-
|
|
6086
|
+
if (this.formatter.isPretty()) {
|
|
6087
|
+
output.push(this.formatter.newline() + this.formatter.indent(`FOR ${node.cmd_name.toUpperCase()}`));
|
|
6088
|
+
}
|
|
6089
|
+
else {
|
|
6090
|
+
output.push('FOR', node.cmd_name.toUpperCase());
|
|
6091
|
+
}
|
|
5840
6092
|
}
|
|
5841
6093
|
if (node.roles && node.roles.length > 0) {
|
|
5842
|
-
output.push('TO');
|
|
5843
6094
|
const roles = ListUtils.unwrapList(node.roles).map(role => this.visit(role, context));
|
|
5844
|
-
|
|
6095
|
+
if (this.formatter.isPretty()) {
|
|
6096
|
+
output.push(this.formatter.newline() + this.formatter.indent(`TO ${roles.join(', ')}`));
|
|
6097
|
+
}
|
|
6098
|
+
else {
|
|
6099
|
+
output.push('TO');
|
|
6100
|
+
output.push(roles.join(', '));
|
|
6101
|
+
}
|
|
5845
6102
|
}
|
|
5846
6103
|
if (node.qual) {
|
|
5847
|
-
|
|
5848
|
-
|
|
6104
|
+
if (this.formatter.isPretty()) {
|
|
6105
|
+
const qualExpr = this.visit(node.qual, context);
|
|
6106
|
+
output.push(this.formatter.newline() + this.formatter.indent('USING ('));
|
|
6107
|
+
output.push(this.formatter.newline() + this.formatter.indent(this.formatter.indent(qualExpr)));
|
|
6108
|
+
output.push(this.formatter.newline() + this.formatter.indent(')'));
|
|
6109
|
+
}
|
|
6110
|
+
else {
|
|
6111
|
+
output.push('USING');
|
|
6112
|
+
output.push(`(${this.visit(node.qual, context)})`);
|
|
6113
|
+
}
|
|
5849
6114
|
}
|
|
5850
6115
|
if (node.with_check) {
|
|
5851
|
-
|
|
5852
|
-
|
|
6116
|
+
if (this.formatter.isPretty()) {
|
|
6117
|
+
const checkExpr = this.visit(node.with_check, context);
|
|
6118
|
+
output.push(this.formatter.newline() + this.formatter.indent('WITH CHECK ('));
|
|
6119
|
+
output.push(this.formatter.newline() + this.formatter.indent(this.formatter.indent(checkExpr)));
|
|
6120
|
+
output.push(this.formatter.newline() + this.formatter.indent(')'));
|
|
6121
|
+
}
|
|
6122
|
+
else {
|
|
6123
|
+
output.push('WITH CHECK');
|
|
6124
|
+
output.push(`(${this.visit(node.with_check, context)})`);
|
|
6125
|
+
}
|
|
5853
6126
|
}
|
|
5854
|
-
return output.join(' ');
|
|
6127
|
+
return this.formatter.isPretty() ? output.join('') : output.join(' ');
|
|
5855
6128
|
}
|
|
5856
6129
|
AlterPolicyStmt(node, context) {
|
|
5857
6130
|
const output = ['ALTER', 'POLICY'];
|
|
@@ -9717,4 +9990,8 @@ export class Deparser {
|
|
|
9717
9990
|
}
|
|
9718
9991
|
return output.join(' ');
|
|
9719
9992
|
}
|
|
9993
|
+
containsMultilineStringLiteral(content) {
|
|
9994
|
+
const stringLiteralRegex = /'[^']*\n[^']*'/g;
|
|
9995
|
+
return stringLiteralRegex.test(content);
|
|
9996
|
+
}
|
|
9720
9997
|
}
|