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/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,23 @@ 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 => {
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
- const andArgs = args.map(arg => this.visit(arg, boolContext)).join(' AND ');
893
- return formatStr.replace('%s', () => andArgs);
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
- const orArgs = args.map(arg => this.visit(arg, boolContext)).join(' OR ');
896
- return formatStr.replace('%s', () => orArgs);
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
- output.push(this.formatter.parens(elementStrs.join(', ')));
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
- output.push('ON UPDATE');
2347
+ let updateClause = 'ON UPDATE ';
2229
2348
  switch (node.fk_upd_action) {
2230
2349
  case 'r':
2231
- output.push('RESTRICT');
2350
+ updateClause += 'RESTRICT';
2232
2351
  break;
2233
2352
  case 'c':
2234
- output.push('CASCADE');
2353
+ updateClause += 'CASCADE';
2235
2354
  break;
2236
2355
  case 'n':
2237
- output.push('SET NULL');
2356
+ updateClause += 'SET NULL';
2238
2357
  break;
2239
2358
  case 'd':
2240
- output.push('SET DEFAULT');
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
- output.push('ON DELETE');
2371
+ let deleteClause = 'ON DELETE ';
2246
2372
  switch (node.fk_del_action) {
2247
2373
  case 'r':
2248
- output.push('RESTRICT');
2374
+ deleteClause += 'RESTRICT';
2249
2375
  break;
2250
2376
  case 'c':
2251
- output.push('CASCADE');
2377
+ deleteClause += 'CASCADE';
2252
2378
  break;
2253
2379
  case 'n':
2254
- output.push('SET NULL');
2380
+ deleteClause += 'SET NULL';
2255
2381
  break;
2256
2382
  case 'd':
2257
- output.push('SET DEFAULT');
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
- output.push('DEFERRABLE');
2318
- if (node.initdeferred === true) {
2319
- output.push('INITIALLY DEFERRED');
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 if (node.initdeferred === false) {
2322
- output.push('INITIALLY IMMEDIATE');
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
- output.push('NOT DEFERRABLE');
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
- output.push(rargStr);
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
- output.push(`(${columnNames.join(', ')})`);
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
- output.push('ON');
3091
- output.push(this.visit(node.quals, context));
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 = ['CREATE', 'POLICY'];
6009
+ const output = [];
6010
+ const initialParts = ['CREATE', 'POLICY'];
5824
6011
  if (node.policy_name) {
5825
- output.push(`"${node.policy_name}"`);
6012
+ initialParts.push(`"${node.policy_name}"`);
5826
6013
  }
5827
- output.push('ON');
6014
+ output.push(initialParts.join(' '));
6015
+ // Add ON clause on new line in pretty mode
5828
6016
  if (node.table) {
5829
- output.push(this.RangeVar(node.table, context));
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
- output.push('AS', 'RESTRICTIVE');
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
- output.push('AS', 'PERMISSIVE');
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
- output.push('FOR', node.cmd_name.toUpperCase());
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
- output.push(roles.join(', '));
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
- output.push('USING');
5848
- output.push(`(${this.visit(node.qual, context)})`);
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
- output.push('WITH CHECK');
5852
- output.push(`(${this.visit(node.with_check, context)})`);
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
- constructor(newlineChar = '\n', tabChar = ' ') {
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
- return text;
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.7.1",
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": "f4b9c0f9f3a8162fe463879a4b0af9238c77e087"
54
+ "gitHead": "785243803d4759f52f8fbe24cc8c0a0b99ba9b11"
55
55
  }
@@ -1,10 +1,12 @@
1
1
  export declare class SqlFormatter {
2
2
  private newlineChar;
3
3
  private tabChar;
4
- constructor(newlineChar?: string, tabChar?: string);
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
  }