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/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
- output.push('SELECT');
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
- output.push(this.formatter.parens(clause));
240
+ distinctPart = ' DISTINCT ON ' + this.formatter.parens(clause);
238
241
  }
239
242
  else {
240
- output.push('DISTINCT');
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
- const targets = targetList
246
- .map(e => this.visit(e, { ...context, select: true }))
247
- .join(', ');
248
- output.push(targets);
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
- output.push('WHERE');
264
- output.push(this.visit(node.whereClause, context));
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
- const groupItems = groupList
278
- .map(e => this.visit(e, { ...context, group: true }))
279
- .join(', ');
280
- output.push(groupItems);
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
- output.push('HAVING');
284
- output.push(this.visit(node.havingClause, context));
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
- const sortItems = sortList
298
- .map(e => this.visit(e, { ...context, sort: true }))
299
- .join(', ');
300
- output.push(sortItems);
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
- const ctes = ListUtils.unwrapList(node.ctes);
807
- const cteStrs = ctes.map(cte => this.visit(cte, context));
808
- output.push(cteStrs.join(', '));
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
- const andArgs = args.map(arg => this.visit(arg, boolContext)).join(' AND ');
893
- return formatStr.replace('%s', () => andArgs);
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
- const orArgs = args.map(arg => this.visit(arg, boolContext)).join(' OR ');
896
- return formatStr.replace('%s', () => orArgs);
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
- result += ` OVER (${windowParts.join(' ')})`;
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
- for (const arg of args) {
1723
- output.push(this.visit(arg, context));
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
- if (node.defresult) {
1726
- output.push('ELSE');
1727
- output.push(this.visit(node.defresult, context));
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
- output.push(this.formatter.parens(elementStrs.join(', ')));
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
- output.push('ON UPDATE');
2378
+ let updateClause = 'ON UPDATE ';
2229
2379
  switch (node.fk_upd_action) {
2230
2380
  case 'r':
2231
- output.push('RESTRICT');
2381
+ updateClause += 'RESTRICT';
2232
2382
  break;
2233
2383
  case 'c':
2234
- output.push('CASCADE');
2384
+ updateClause += 'CASCADE';
2235
2385
  break;
2236
2386
  case 'n':
2237
- output.push('SET NULL');
2387
+ updateClause += 'SET NULL';
2238
2388
  break;
2239
2389
  case 'd':
2240
- output.push('SET DEFAULT');
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
- output.push('ON DELETE');
2402
+ let deleteClause = 'ON DELETE ';
2246
2403
  switch (node.fk_del_action) {
2247
2404
  case 'r':
2248
- output.push('RESTRICT');
2405
+ deleteClause += 'RESTRICT';
2249
2406
  break;
2250
2407
  case 'c':
2251
- output.push('CASCADE');
2408
+ deleteClause += 'CASCADE';
2252
2409
  break;
2253
2410
  case 'n':
2254
- output.push('SET NULL');
2411
+ deleteClause += 'SET NULL';
2255
2412
  break;
2256
2413
  case 'd':
2257
- output.push('SET DEFAULT');
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
- output.push('DEFERRABLE');
2318
- if (node.initdeferred === true) {
2319
- output.push('INITIALLY DEFERRED');
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 if (node.initdeferred === false) {
2322
- output.push('INITIALLY IMMEDIATE');
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
- output.push('NOT DEFERRABLE');
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
- output.push(rargStr);
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
- output.push(`(${columnNames.join(', ')})`);
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
- output.push('ON');
3091
- output.push(this.visit(node.quals, context));
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 = ['CREATE', 'POLICY'];
6052
+ const output = [];
6053
+ const initialParts = ['CREATE', 'POLICY'];
5824
6054
  if (node.policy_name) {
5825
- output.push(`"${node.policy_name}"`);
6055
+ initialParts.push(`"${node.policy_name}"`);
5826
6056
  }
5827
- output.push('ON');
6057
+ output.push(initialParts.join(' '));
6058
+ // Add ON clause on new line in pretty mode
5828
6059
  if (node.table) {
5829
- output.push(this.RangeVar(node.table, context));
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
- output.push('AS', 'RESTRICTIVE');
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
- output.push('AS', 'PERMISSIVE');
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
- output.push('FOR', node.cmd_name.toUpperCase());
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
- output.push(roles.join(', '));
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
- output.push('USING');
5848
- output.push(`(${this.visit(node.qual, context)})`);
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
- output.push('WITH CHECK');
5852
- output.push(`(${this.visit(node.with_check, context)})`);
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
  }