pgsql-deparser 17.7.1 → 17.8.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 +44 -2
- package/deparser.d.ts +2 -0
- package/deparser.js +312 -78
- package/esm/deparser.js +312 -78
- 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,23 @@ 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 => {
|
|
897
|
+
const cteStr = this.visit(cte, context);
|
|
898
|
+
if (this.containsMultilineStringLiteral(cteStr)) {
|
|
899
|
+
return this.formatter.newline() + cteStr;
|
|
900
|
+
}
|
|
901
|
+
return this.formatter.newline() + this.formatter.indent(cteStr);
|
|
902
|
+
});
|
|
903
|
+
output.push(cteStrings.join(','));
|
|
904
|
+
}
|
|
905
|
+
else {
|
|
906
|
+
const cteStrings = ctes.map(cte => this.visit(cte, context));
|
|
907
|
+
output.push(cteStrings.join(', '));
|
|
908
|
+
}
|
|
909
|
+
}
|
|
809
910
|
return output.join(' ');
|
|
810
911
|
}
|
|
811
912
|
ResTarget(node, context) {
|
|
@@ -889,11 +990,23 @@ export class Deparser {
|
|
|
889
990
|
// return formatStr.replace('%s', () => andArgs); // ✅ Function callback prevents interpretation
|
|
890
991
|
switch (boolop) {
|
|
891
992
|
case 'AND_EXPR':
|
|
892
|
-
|
|
893
|
-
|
|
993
|
+
if (this.formatter.isPretty() && args.length > 1) {
|
|
994
|
+
const andArgs = args.map(arg => this.visit(arg, boolContext)).join(this.formatter.newline() + ' AND ');
|
|
995
|
+
return formatStr.replace('%s', () => andArgs);
|
|
996
|
+
}
|
|
997
|
+
else {
|
|
998
|
+
const andArgs = args.map(arg => this.visit(arg, boolContext)).join(' AND ');
|
|
999
|
+
return formatStr.replace('%s', () => andArgs);
|
|
1000
|
+
}
|
|
894
1001
|
case 'OR_EXPR':
|
|
895
|
-
|
|
896
|
-
|
|
1002
|
+
if (this.formatter.isPretty() && args.length > 1) {
|
|
1003
|
+
const orArgs = args.map(arg => this.visit(arg, boolContext)).join(this.formatter.newline() + ' OR ');
|
|
1004
|
+
return formatStr.replace('%s', () => orArgs);
|
|
1005
|
+
}
|
|
1006
|
+
else {
|
|
1007
|
+
const orArgs = args.map(arg => this.visit(arg, boolContext)).join(' OR ');
|
|
1008
|
+
return formatStr.replace('%s', () => orArgs);
|
|
1009
|
+
}
|
|
897
1010
|
case 'NOT_EXPR':
|
|
898
1011
|
return `NOT (${this.visit(args[0], context)})`;
|
|
899
1012
|
default:
|
|
@@ -1940,7 +2053,13 @@ export class Deparser {
|
|
|
1940
2053
|
const elementStrs = elements.map(el => {
|
|
1941
2054
|
return this.deparse(el, context);
|
|
1942
2055
|
});
|
|
1943
|
-
|
|
2056
|
+
if (this.formatter.isPretty()) {
|
|
2057
|
+
const formattedElements = elementStrs.map(el => this.formatter.indent(el)).join(',' + this.formatter.newline());
|
|
2058
|
+
output.push('(' + this.formatter.newline() + formattedElements + this.formatter.newline() + ')');
|
|
2059
|
+
}
|
|
2060
|
+
else {
|
|
2061
|
+
output.push(this.formatter.parens(elementStrs.join(', ')));
|
|
2062
|
+
}
|
|
1944
2063
|
}
|
|
1945
2064
|
else if (!node.partbound) {
|
|
1946
2065
|
output.push(this.formatter.parens(''));
|
|
@@ -2225,38 +2344,52 @@ export class Deparser {
|
|
|
2225
2344
|
}
|
|
2226
2345
|
}
|
|
2227
2346
|
if (node.fk_upd_action && node.fk_upd_action !== 'a') {
|
|
2228
|
-
|
|
2347
|
+
let updateClause = 'ON UPDATE ';
|
|
2229
2348
|
switch (node.fk_upd_action) {
|
|
2230
2349
|
case 'r':
|
|
2231
|
-
|
|
2350
|
+
updateClause += 'RESTRICT';
|
|
2232
2351
|
break;
|
|
2233
2352
|
case 'c':
|
|
2234
|
-
|
|
2353
|
+
updateClause += 'CASCADE';
|
|
2235
2354
|
break;
|
|
2236
2355
|
case 'n':
|
|
2237
|
-
|
|
2356
|
+
updateClause += 'SET NULL';
|
|
2238
2357
|
break;
|
|
2239
2358
|
case 'd':
|
|
2240
|
-
|
|
2359
|
+
updateClause += 'SET DEFAULT';
|
|
2241
2360
|
break;
|
|
2242
2361
|
}
|
|
2362
|
+
if (this.formatter.isPretty()) {
|
|
2363
|
+
output.push('\n' + this.formatter.indent(updateClause));
|
|
2364
|
+
}
|
|
2365
|
+
else {
|
|
2366
|
+
output.push('ON UPDATE');
|
|
2367
|
+
output.push(updateClause.replace('ON UPDATE ', ''));
|
|
2368
|
+
}
|
|
2243
2369
|
}
|
|
2244
2370
|
if (node.fk_del_action && node.fk_del_action !== 'a') {
|
|
2245
|
-
|
|
2371
|
+
let deleteClause = 'ON DELETE ';
|
|
2246
2372
|
switch (node.fk_del_action) {
|
|
2247
2373
|
case 'r':
|
|
2248
|
-
|
|
2374
|
+
deleteClause += 'RESTRICT';
|
|
2249
2375
|
break;
|
|
2250
2376
|
case 'c':
|
|
2251
|
-
|
|
2377
|
+
deleteClause += 'CASCADE';
|
|
2252
2378
|
break;
|
|
2253
2379
|
case 'n':
|
|
2254
|
-
|
|
2380
|
+
deleteClause += 'SET NULL';
|
|
2255
2381
|
break;
|
|
2256
2382
|
case 'd':
|
|
2257
|
-
|
|
2383
|
+
deleteClause += 'SET DEFAULT';
|
|
2258
2384
|
break;
|
|
2259
2385
|
}
|
|
2386
|
+
if (this.formatter.isPretty()) {
|
|
2387
|
+
output.push('\n' + this.formatter.indent(deleteClause));
|
|
2388
|
+
}
|
|
2389
|
+
else {
|
|
2390
|
+
output.push('ON DELETE');
|
|
2391
|
+
output.push(deleteClause.replace('ON DELETE ', ''));
|
|
2392
|
+
}
|
|
2260
2393
|
}
|
|
2261
2394
|
// Handle NOT VALID for foreign key constraints - only for table constraints, not domain constraints
|
|
2262
2395
|
if (node.skip_validation && !context.isDomainConstraint) {
|
|
@@ -2314,17 +2447,48 @@ export class Deparser {
|
|
|
2314
2447
|
// Handle deferrable constraints for all constraint types that support it
|
|
2315
2448
|
if (node.contype === 'CONSTR_PRIMARY' || node.contype === 'CONSTR_UNIQUE' || node.contype === 'CONSTR_FOREIGN') {
|
|
2316
2449
|
if (node.deferrable) {
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2450
|
+
if (this.formatter.isPretty() && node.contype === 'CONSTR_FOREIGN') {
|
|
2451
|
+
output.push('\n' + this.formatter.indent('DEFERRABLE'));
|
|
2452
|
+
if (node.initdeferred === true) {
|
|
2453
|
+
output.push('\n' + this.formatter.indent('INITIALLY DEFERRED'));
|
|
2454
|
+
}
|
|
2455
|
+
else if (node.initdeferred === false) {
|
|
2456
|
+
output.push('\n' + this.formatter.indent('INITIALLY IMMEDIATE'));
|
|
2457
|
+
}
|
|
2320
2458
|
}
|
|
2321
|
-
else
|
|
2322
|
-
output.push('
|
|
2459
|
+
else {
|
|
2460
|
+
output.push('DEFERRABLE');
|
|
2461
|
+
if (node.initdeferred === true) {
|
|
2462
|
+
output.push('INITIALLY DEFERRED');
|
|
2463
|
+
}
|
|
2464
|
+
else if (node.initdeferred === false) {
|
|
2465
|
+
output.push('INITIALLY IMMEDIATE');
|
|
2466
|
+
}
|
|
2323
2467
|
}
|
|
2324
2468
|
}
|
|
2325
2469
|
else if (node.deferrable === false) {
|
|
2326
|
-
|
|
2470
|
+
if (this.formatter.isPretty() && node.contype === 'CONSTR_FOREIGN') {
|
|
2471
|
+
output.push('\n' + this.formatter.indent('NOT DEFERRABLE'));
|
|
2472
|
+
}
|
|
2473
|
+
else {
|
|
2474
|
+
output.push('NOT DEFERRABLE');
|
|
2475
|
+
}
|
|
2476
|
+
}
|
|
2477
|
+
}
|
|
2478
|
+
if (this.formatter.isPretty() && node.contype === 'CONSTR_FOREIGN') {
|
|
2479
|
+
let result = '';
|
|
2480
|
+
for (let i = 0; i < output.length; i++) {
|
|
2481
|
+
if (output[i].startsWith('\n')) {
|
|
2482
|
+
result += output[i];
|
|
2483
|
+
}
|
|
2484
|
+
else {
|
|
2485
|
+
if (i > 0 && !output[i - 1].startsWith('\n')) {
|
|
2486
|
+
result += ' ';
|
|
2487
|
+
}
|
|
2488
|
+
result += output[i];
|
|
2489
|
+
}
|
|
2327
2490
|
}
|
|
2491
|
+
return result;
|
|
2328
2492
|
}
|
|
2329
2493
|
return output.join(' ');
|
|
2330
2494
|
}
|
|
@@ -3048,11 +3212,9 @@ export class Deparser {
|
|
|
3048
3212
|
}
|
|
3049
3213
|
switch (node.jointype) {
|
|
3050
3214
|
case 'JOIN_INNER':
|
|
3051
|
-
// Handle NATURAL JOIN first - it has isNatural=true (NATURAL already added above)
|
|
3052
3215
|
if (node.isNatural) {
|
|
3053
3216
|
joinStr += 'JOIN';
|
|
3054
3217
|
}
|
|
3055
|
-
// Handle CROSS JOIN case - when there's no quals, no usingClause, and not natural
|
|
3056
3218
|
else if (!node.quals && (!node.usingClause || node.usingClause.length === 0)) {
|
|
3057
3219
|
joinStr += 'CROSS JOIN';
|
|
3058
3220
|
}
|
|
@@ -3072,26 +3234,51 @@ export class Deparser {
|
|
|
3072
3234
|
default:
|
|
3073
3235
|
joinStr += 'JOIN';
|
|
3074
3236
|
}
|
|
3075
|
-
output.push(joinStr);
|
|
3076
3237
|
if (node.rarg) {
|
|
3077
3238
|
let rargStr = this.visit(node.rarg, context);
|
|
3078
3239
|
if (node.rarg && 'JoinExpr' in node.rarg && !node.rarg.JoinExpr.alias) {
|
|
3079
3240
|
rargStr = `(${rargStr})`;
|
|
3080
3241
|
}
|
|
3081
|
-
|
|
3242
|
+
if (this.formatter.isPretty()) {
|
|
3243
|
+
output.push(this.formatter.newline() + joinStr + ' ' + rargStr);
|
|
3244
|
+
}
|
|
3245
|
+
else {
|
|
3246
|
+
output.push(joinStr + ' ' + rargStr);
|
|
3247
|
+
}
|
|
3248
|
+
}
|
|
3249
|
+
else {
|
|
3250
|
+
if (this.formatter.isPretty()) {
|
|
3251
|
+
output.push(this.formatter.newline() + joinStr);
|
|
3252
|
+
}
|
|
3253
|
+
else {
|
|
3254
|
+
output.push(joinStr);
|
|
3255
|
+
}
|
|
3082
3256
|
}
|
|
3083
3257
|
if (node.usingClause && node.usingClause.length > 0) {
|
|
3084
|
-
output.push('USING');
|
|
3085
3258
|
const usingList = ListUtils.unwrapList(node.usingClause);
|
|
3086
3259
|
const columnNames = usingList.map(col => this.visit(col, context));
|
|
3087
|
-
|
|
3260
|
+
if (this.formatter.isPretty()) {
|
|
3261
|
+
output.push(` USING (${columnNames.join(', ')})`);
|
|
3262
|
+
}
|
|
3263
|
+
else {
|
|
3264
|
+
output.push(`USING (${columnNames.join(', ')})`);
|
|
3265
|
+
}
|
|
3088
3266
|
}
|
|
3089
3267
|
else if (node.quals) {
|
|
3090
|
-
|
|
3091
|
-
|
|
3268
|
+
if (this.formatter.isPretty()) {
|
|
3269
|
+
output.push(` ON ${this.visit(node.quals, context)}`);
|
|
3270
|
+
}
|
|
3271
|
+
else {
|
|
3272
|
+
output.push(`ON ${this.visit(node.quals, context)}`);
|
|
3273
|
+
}
|
|
3274
|
+
}
|
|
3275
|
+
let result;
|
|
3276
|
+
if (this.formatter.isPretty()) {
|
|
3277
|
+
result = output.join('');
|
|
3278
|
+
}
|
|
3279
|
+
else {
|
|
3280
|
+
result = output.join(' ');
|
|
3092
3281
|
}
|
|
3093
|
-
let result = output.join(' ');
|
|
3094
|
-
// Handle join_using_alias first (for USING clause aliases like "AS x")
|
|
3095
3282
|
if (node.join_using_alias && node.join_using_alias.aliasname) {
|
|
3096
3283
|
let aliasStr = node.join_using_alias.aliasname;
|
|
3097
3284
|
if (node.join_using_alias.colnames && node.join_using_alias.colnames.length > 0) {
|
|
@@ -3101,7 +3288,6 @@ export class Deparser {
|
|
|
3101
3288
|
}
|
|
3102
3289
|
result += ` AS ${aliasStr}`;
|
|
3103
3290
|
}
|
|
3104
|
-
// Handle regular alias (for outer table aliases like "y")
|
|
3105
3291
|
if (node.alias && node.alias.aliasname) {
|
|
3106
3292
|
let aliasStr = node.alias.aliasname;
|
|
3107
3293
|
if (node.alias.colnames && node.alias.colnames.length > 0) {
|
|
@@ -5820,38 +6006,82 @@ export class Deparser {
|
|
|
5820
6006
|
return output.join(' ');
|
|
5821
6007
|
}
|
|
5822
6008
|
CreatePolicyStmt(node, context) {
|
|
5823
|
-
const output = [
|
|
6009
|
+
const output = [];
|
|
6010
|
+
const initialParts = ['CREATE', 'POLICY'];
|
|
5824
6011
|
if (node.policy_name) {
|
|
5825
|
-
|
|
6012
|
+
initialParts.push(`"${node.policy_name}"`);
|
|
5826
6013
|
}
|
|
5827
|
-
output.push('
|
|
6014
|
+
output.push(initialParts.join(' '));
|
|
6015
|
+
// Add ON clause on new line in pretty mode
|
|
5828
6016
|
if (node.table) {
|
|
5829
|
-
|
|
6017
|
+
if (this.formatter.isPretty()) {
|
|
6018
|
+
output.push(this.formatter.newline() + this.formatter.indent(`ON ${this.RangeVar(node.table, context)}`));
|
|
6019
|
+
}
|
|
6020
|
+
else {
|
|
6021
|
+
output.push('ON');
|
|
6022
|
+
output.push(this.RangeVar(node.table, context));
|
|
6023
|
+
}
|
|
5830
6024
|
}
|
|
5831
6025
|
// Handle AS RESTRICTIVE/PERMISSIVE clause
|
|
5832
6026
|
if (node.permissive === undefined) {
|
|
5833
|
-
|
|
6027
|
+
if (this.formatter.isPretty()) {
|
|
6028
|
+
output.push(this.formatter.newline() + this.formatter.indent('AS RESTRICTIVE'));
|
|
6029
|
+
}
|
|
6030
|
+
else {
|
|
6031
|
+
output.push('AS', 'RESTRICTIVE');
|
|
6032
|
+
}
|
|
5834
6033
|
}
|
|
5835
6034
|
else if (node.permissive === true) {
|
|
5836
|
-
|
|
6035
|
+
if (this.formatter.isPretty()) {
|
|
6036
|
+
output.push(this.formatter.newline() + this.formatter.indent('AS PERMISSIVE'));
|
|
6037
|
+
}
|
|
6038
|
+
else {
|
|
6039
|
+
output.push('AS', 'PERMISSIVE');
|
|
6040
|
+
}
|
|
5837
6041
|
}
|
|
5838
6042
|
if (node.cmd_name) {
|
|
5839
|
-
|
|
6043
|
+
if (this.formatter.isPretty()) {
|
|
6044
|
+
output.push(this.formatter.newline() + this.formatter.indent(`FOR ${node.cmd_name.toUpperCase()}`));
|
|
6045
|
+
}
|
|
6046
|
+
else {
|
|
6047
|
+
output.push('FOR', node.cmd_name.toUpperCase());
|
|
6048
|
+
}
|
|
5840
6049
|
}
|
|
5841
6050
|
if (node.roles && node.roles.length > 0) {
|
|
5842
|
-
output.push('TO');
|
|
5843
6051
|
const roles = ListUtils.unwrapList(node.roles).map(role => this.visit(role, context));
|
|
5844
|
-
|
|
6052
|
+
if (this.formatter.isPretty()) {
|
|
6053
|
+
output.push(this.formatter.newline() + this.formatter.indent(`TO ${roles.join(', ')}`));
|
|
6054
|
+
}
|
|
6055
|
+
else {
|
|
6056
|
+
output.push('TO');
|
|
6057
|
+
output.push(roles.join(', '));
|
|
6058
|
+
}
|
|
5845
6059
|
}
|
|
5846
6060
|
if (node.qual) {
|
|
5847
|
-
|
|
5848
|
-
|
|
6061
|
+
if (this.formatter.isPretty()) {
|
|
6062
|
+
const qualExpr = this.visit(node.qual, context);
|
|
6063
|
+
output.push(this.formatter.newline() + this.formatter.indent('USING ('));
|
|
6064
|
+
output.push(this.formatter.newline() + this.formatter.indent(this.formatter.indent(qualExpr)));
|
|
6065
|
+
output.push(this.formatter.newline() + this.formatter.indent(')'));
|
|
6066
|
+
}
|
|
6067
|
+
else {
|
|
6068
|
+
output.push('USING');
|
|
6069
|
+
output.push(`(${this.visit(node.qual, context)})`);
|
|
6070
|
+
}
|
|
5849
6071
|
}
|
|
5850
6072
|
if (node.with_check) {
|
|
5851
|
-
|
|
5852
|
-
|
|
6073
|
+
if (this.formatter.isPretty()) {
|
|
6074
|
+
const checkExpr = this.visit(node.with_check, context);
|
|
6075
|
+
output.push(this.formatter.newline() + this.formatter.indent('WITH CHECK ('));
|
|
6076
|
+
output.push(this.formatter.newline() + this.formatter.indent(this.formatter.indent(checkExpr)));
|
|
6077
|
+
output.push(this.formatter.newline() + this.formatter.indent(')'));
|
|
6078
|
+
}
|
|
6079
|
+
else {
|
|
6080
|
+
output.push('WITH CHECK');
|
|
6081
|
+
output.push(`(${this.visit(node.with_check, context)})`);
|
|
6082
|
+
}
|
|
5853
6083
|
}
|
|
5854
|
-
return output.join(' ');
|
|
6084
|
+
return this.formatter.isPretty() ? output.join('') : output.join(' ');
|
|
5855
6085
|
}
|
|
5856
6086
|
AlterPolicyStmt(node, context) {
|
|
5857
6087
|
const output = ['ALTER', 'POLICY'];
|
|
@@ -9717,4 +9947,8 @@ export class Deparser {
|
|
|
9717
9947
|
}
|
|
9718
9948
|
return output.join(' ');
|
|
9719
9949
|
}
|
|
9950
|
+
containsMultilineStringLiteral(content) {
|
|
9951
|
+
const stringLiteralRegex = /'[^']*\n[^']*'/g;
|
|
9952
|
+
return stringLiteralRegex.test(content);
|
|
9953
|
+
}
|
|
9720
9954
|
}
|
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
export class SqlFormatter {
|
|
2
2
|
newlineChar;
|
|
3
3
|
tabChar;
|
|
4
|
-
|
|
4
|
+
prettyMode;
|
|
5
|
+
constructor(newlineChar = '\n', tabChar = ' ', prettyMode = false) {
|
|
5
6
|
this.newlineChar = newlineChar;
|
|
6
7
|
this.tabChar = tabChar;
|
|
8
|
+
this.prettyMode = prettyMode;
|
|
7
9
|
}
|
|
8
10
|
format(parts, separator = ' ') {
|
|
9
11
|
return parts.filter(part => part !== null && part !== undefined && part !== '').join(separator);
|
|
10
12
|
}
|
|
11
13
|
indent(text, count = 1) {
|
|
12
|
-
|
|
14
|
+
if (!this.prettyMode) {
|
|
15
|
+
return text;
|
|
16
|
+
}
|
|
17
|
+
const indentation = this.tabChar.repeat(count);
|
|
18
|
+
return text.split(this.newlineChar).map(line => line.trim() ? indentation + line : line).join(this.newlineChar);
|
|
13
19
|
}
|
|
14
20
|
parens(content) {
|
|
15
21
|
return `(${content})`;
|
|
@@ -20,4 +26,7 @@ export class SqlFormatter {
|
|
|
20
26
|
tab() {
|
|
21
27
|
return this.tabChar;
|
|
22
28
|
}
|
|
29
|
+
isPretty() {
|
|
30
|
+
return this.prettyMode;
|
|
31
|
+
}
|
|
23
32
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pgsql-deparser",
|
|
3
|
-
"version": "17.
|
|
3
|
+
"version": "17.8.0",
|
|
4
4
|
"author": "Dan Lynch <pyramation@gmail.com>",
|
|
5
5
|
"description": "PostgreSQL AST Deparser",
|
|
6
6
|
"main": "index.js",
|
|
@@ -51,5 +51,5 @@
|
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"@pgsql/types": "^17.6.1"
|
|
53
53
|
},
|
|
54
|
-
"gitHead": "
|
|
54
|
+
"gitHead": "785243803d4759f52f8fbe24cc8c0a0b99ba9b11"
|
|
55
55
|
}
|
package/utils/sql-formatter.d.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
export declare class SqlFormatter {
|
|
2
2
|
private newlineChar;
|
|
3
3
|
private tabChar;
|
|
4
|
-
|
|
4
|
+
private prettyMode;
|
|
5
|
+
constructor(newlineChar?: string, tabChar?: string, prettyMode?: boolean);
|
|
5
6
|
format(parts: string[], separator?: string): string;
|
|
6
7
|
indent(text: string, count?: number): string;
|
|
7
8
|
parens(content: string): string;
|
|
8
9
|
newline(): string;
|
|
9
10
|
tab(): string;
|
|
11
|
+
isPretty(): boolean;
|
|
10
12
|
}
|