pgsql-deparser 17.8.1 → 17.8.2

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
@@ -1,6 +1,62 @@
1
1
  import { SqlFormatter } from './utils/sql-formatter';
2
2
  import { QuoteUtils } from './utils/quote-utils';
3
3
  import { ListUtils } from './utils/list-utils';
4
+ /**
5
+ * List of real PostgreSQL built-in types as they appear in pg_catalog.pg_type.typname.
6
+ * These are stored in lowercase in PostgreSQL system catalogs.
7
+ * Use these for lookups, validations, or introspection logic.
8
+ */
9
+ const pgCatalogTypes = [
10
+ // Integers
11
+ 'int2', // smallint
12
+ 'int4', // integer
13
+ 'int8', // bigint
14
+ // Floating-point & numeric
15
+ 'float4', // real
16
+ 'float8', // double precision
17
+ 'numeric', // arbitrary precision (aka "decimal")
18
+ // Text & string
19
+ 'varchar', // variable-length string
20
+ 'char', // internal one-byte type (used in special cases)
21
+ 'bpchar', // blank-padded char(n)
22
+ 'text', // unlimited string
23
+ 'bool', // boolean
24
+ // Dates & times
25
+ 'date', // calendar date
26
+ 'time', // time without time zone
27
+ 'timetz', // time with time zone
28
+ 'timestamp', // timestamp without time zone
29
+ 'timestamptz', // timestamp with time zone
30
+ 'interval', // duration
31
+ // Binary & structured
32
+ 'bytea', // binary data
33
+ 'uuid', // universally unique identifier
34
+ // JSON & XML
35
+ 'json', // textual JSON
36
+ 'jsonb', // binary JSON
37
+ 'xml', // XML format
38
+ // Money & bitstrings
39
+ 'money', // currency value
40
+ 'bit', // fixed-length bit string
41
+ 'varbit', // variable-length bit string
42
+ // Network types
43
+ 'inet', // IPv4 or IPv6 address
44
+ 'cidr', // network address
45
+ 'macaddr', // MAC address (6 bytes)
46
+ 'macaddr8' // MAC address (8 bytes)
47
+ ];
48
+ /**
49
+ * Parser-level type aliases accepted by PostgreSQL SQL syntax,
50
+ * but not present in pg_catalog.pg_type. These are resolved to
51
+ * real types during parsing and never appear in introspection.
52
+ */
53
+ const pgCatalogTypeAliases = [
54
+ ['numeric', ['decimal', 'dec']],
55
+ ['int4', ['int', 'integer']],
56
+ ['float8', ['float']],
57
+ ['bpchar', ['character']],
58
+ ['varchar', ['character varying']]
59
+ ];
4
60
  // Type guards for better type safety
5
61
  function isParseResult(obj) {
6
62
  // A ParseResult is an object that could have stmts (but not required)
@@ -258,17 +314,36 @@ export class Deparser {
258
314
  if (node.targetList) {
259
315
  const targetList = ListUtils.unwrapList(node.targetList);
260
316
  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;
317
+ if (targetList.length === 1) {
318
+ const targetNode = targetList[0];
319
+ const target = this.visit(targetNode, { ...context, select: true });
320
+ // Check if single target is complex - if so, use multiline format
321
+ if (this.isComplexSelectTarget(targetNode)) {
322
+ output.push('SELECT' + distinctPart);
323
+ if (this.containsMultilineStringLiteral(target)) {
324
+ output.push(target);
325
+ }
326
+ else {
327
+ output.push(this.formatter.indent(target));
328
+ }
266
329
  }
267
- return this.formatter.indent(targetStr);
268
- });
269
- const formattedTargets = targetStrings.join(',' + this.formatter.newline());
270
- output.push('SELECT' + distinctPart);
271
- output.push(formattedTargets);
330
+ else {
331
+ output.push('SELECT' + distinctPart + ' ' + target);
332
+ }
333
+ }
334
+ else {
335
+ const targetStrings = targetList
336
+ .map(e => {
337
+ const targetStr = this.visit(e, { ...context, select: true });
338
+ if (this.containsMultilineStringLiteral(targetStr)) {
339
+ return targetStr;
340
+ }
341
+ return this.formatter.indent(targetStr);
342
+ });
343
+ const formattedTargets = targetStrings.join(',' + this.formatter.newline());
344
+ output.push('SELECT' + distinctPart);
345
+ output.push(formattedTargets);
346
+ }
272
347
  }
273
348
  else {
274
349
  const targets = targetList
@@ -307,12 +382,28 @@ export class Deparser {
307
382
  }
308
383
  }
309
384
  if (node.valuesLists) {
310
- output.push('VALUES');
311
- const lists = ListUtils.unwrapList(node.valuesLists).map(list => {
312
- const values = ListUtils.unwrapList(list).map(val => this.visit(val, context));
313
- return this.formatter.parens(values.join(', '));
314
- });
315
- output.push(lists.join(', '));
385
+ if (this.formatter.isPretty()) {
386
+ output.push('VALUES');
387
+ const lists = ListUtils.unwrapList(node.valuesLists).map(list => {
388
+ const values = ListUtils.unwrapList(list).map(val => this.visit(val, context));
389
+ return this.formatter.parens(values.join(', '));
390
+ });
391
+ const indentedTuples = lists.map(tuple => {
392
+ if (this.containsMultilineStringLiteral(tuple)) {
393
+ return tuple;
394
+ }
395
+ return this.formatter.indent(tuple);
396
+ });
397
+ output.push(indentedTuples.join(',\n'));
398
+ }
399
+ else {
400
+ output.push('VALUES');
401
+ const lists = ListUtils.unwrapList(node.valuesLists).map(list => {
402
+ const values = ListUtils.unwrapList(list).map(val => this.visit(val, context));
403
+ return this.formatter.parens(values.join(', '));
404
+ });
405
+ output.push(lists.join(', '));
406
+ }
316
407
  }
317
408
  if (node.groupClause) {
318
409
  const groupList = ListUtils.unwrapList(node.groupClause);
@@ -681,6 +772,64 @@ export class Deparser {
681
772
  node.SubLink ||
682
773
  node.A_Expr);
683
774
  }
775
+ isComplexSelectTarget(node) {
776
+ if (!node)
777
+ return false;
778
+ if (node.ResTarget?.val) {
779
+ return this.isComplexExpression(node.ResTarget.val);
780
+ }
781
+ // Always complex: CASE expressions
782
+ if (node.CaseExpr)
783
+ return true;
784
+ // Always complex: Subqueries and subselects
785
+ if (node.SubLink)
786
+ return true;
787
+ // Always complex: Boolean tests and expressions
788
+ if (node.NullTest || node.BooleanTest || node.BoolExpr)
789
+ return true;
790
+ // COALESCE and similar functions - complex if multiple arguments
791
+ if (node.CoalesceExpr) {
792
+ const args = node.CoalesceExpr.args;
793
+ if (args && Array.isArray(args) && args.length > 1)
794
+ return true;
795
+ }
796
+ // Function calls - complex if multiple args or has clauses
797
+ if (node.FuncCall) {
798
+ const funcCall = node.FuncCall;
799
+ const args = funcCall.args ? (Array.isArray(funcCall.args) ? funcCall.args : [funcCall.args]) : [];
800
+ // Complex if has window clause, filter, order by, etc.
801
+ if (funcCall.over || funcCall.agg_filter || funcCall.agg_order || funcCall.agg_distinct) {
802
+ return true;
803
+ }
804
+ // Complex if multiple arguments
805
+ if (args.length > 1)
806
+ return true;
807
+ if (args.length === 1) {
808
+ return this.isComplexSelectTarget(args[0]);
809
+ }
810
+ }
811
+ if (node.A_Expr) {
812
+ const expr = node.A_Expr;
813
+ // Check if operands are complex
814
+ if (expr.lexpr && this.isComplexSelectTarget(expr.lexpr))
815
+ return true;
816
+ if (expr.rexpr && this.isComplexSelectTarget(expr.rexpr))
817
+ return true;
818
+ return false;
819
+ }
820
+ if (node.TypeCast) {
821
+ return this.isComplexSelectTarget(node.TypeCast.arg);
822
+ }
823
+ if (node.A_ArrayExpr)
824
+ return true;
825
+ if (node.A_Indirection) {
826
+ return this.isComplexSelectTarget(node.A_Indirection.arg);
827
+ }
828
+ if (node.A_Const || node.ColumnRef || node.ParamRef || node.A_Star) {
829
+ return false;
830
+ }
831
+ return false;
832
+ }
684
833
  visitBetweenRange(rexpr, context) {
685
834
  if (rexpr && 'List' in rexpr && rexpr.List?.items) {
686
835
  const items = rexpr.List.items.map((item) => this.visit(item, context));
@@ -699,7 +848,14 @@ export class Deparser {
699
848
  const cols = ListUtils.unwrapList(node.cols);
700
849
  const insertContext = { ...context, insertColumns: true };
701
850
  const columnNames = cols.map(col => this.visit(col, insertContext));
702
- output.push(this.formatter.parens(columnNames.join(', ')));
851
+ if (this.formatter.isPretty()) {
852
+ // Always format columns in multiline parentheses for pretty printing
853
+ const indentedColumns = columnNames.map(col => this.formatter.indent(col));
854
+ output.push('(\n' + indentedColumns.join(',\n') + '\n)');
855
+ }
856
+ else {
857
+ output.push(this.formatter.parens(columnNames.join(', ')));
858
+ }
703
859
  }
704
860
  if (node.selectStmt) {
705
861
  output.push(this.visit(node.selectStmt, context));
@@ -1491,9 +1647,6 @@ export class Deparser {
1491
1647
  return output.join(' ');
1492
1648
  }
1493
1649
  if (catalog === 'pg_catalog') {
1494
- const builtinTypes = ['int2', 'int4', 'int8', 'float4', 'float8', 'numeric', 'decimal',
1495
- 'varchar', 'char', 'bpchar', 'text', 'bool', 'date', 'time', 'timestamp',
1496
- 'timestamptz', 'interval', 'bytea', 'uuid', 'json', 'jsonb'];
1497
1650
  let typeName = `${catalog}.${type}`;
1498
1651
  if (type === 'bpchar' && args) {
1499
1652
  typeName = 'char';
@@ -1788,6 +1941,18 @@ export class Deparser {
1788
1941
  return `pg_catalog.${typeName}`;
1789
1942
  }
1790
1943
  }
1944
+ isPgCatalogType(typeName) {
1945
+ const cleanTypeName = typeName.replace(/^pg_catalog\./, '');
1946
+ if (pgCatalogTypes.includes(cleanTypeName)) {
1947
+ return true;
1948
+ }
1949
+ for (const [realType, aliases] of pgCatalogTypeAliases) {
1950
+ if (aliases.includes(cleanTypeName)) {
1951
+ return true;
1952
+ }
1953
+ }
1954
+ return false;
1955
+ }
1791
1956
  A_ArrayExpr(node, context) {
1792
1957
  const elements = ListUtils.unwrapList(node.elements);
1793
1958
  const elementStrs = elements.map(el => this.visit(el, context));
@@ -1881,28 +2046,29 @@ export class Deparser {
1881
2046
  TypeCast(node, context) {
1882
2047
  const arg = this.visit(node.arg, context);
1883
2048
  const typeName = this.TypeName(node.typeName, context);
1884
- // Check if this is a bpchar typecast that should use traditional char syntax
1885
- if (typeName === 'bpchar' && node.typeName && node.typeName.names) {
1886
- const names = ListUtils.unwrapList(node.typeName.names);
1887
- if (names.length === 2 &&
1888
- names[0].String?.sval === 'pg_catalog' &&
1889
- names[1].String?.sval === 'bpchar') {
1890
- return `char ${arg}`;
2049
+ // Check if this is a bpchar typecast that should preserve original syntax for AST consistency
2050
+ if (typeName === 'bpchar' || typeName === 'pg_catalog.bpchar') {
2051
+ const names = node.typeName?.names;
2052
+ const isQualifiedBpchar = names && names.length === 2 &&
2053
+ names[0]?.String?.sval === 'pg_catalog' &&
2054
+ names[1]?.String?.sval === 'bpchar';
2055
+ if (isQualifiedBpchar) {
2056
+ return `CAST(${arg} AS ${typeName})`;
1891
2057
  }
1892
2058
  }
1893
- // Check if the argument is a complex expression that should preserve CAST syntax
1894
- const argType = this.getNodeType(node.arg);
1895
- const isComplexExpression = argType === 'A_Expr' || argType === 'FuncCall' || argType === 'OpExpr';
1896
- if (!isComplexExpression && (typeName.startsWith('interval') ||
1897
- typeName.startsWith('char') ||
1898
- typeName === '"char"' ||
1899
- typeName.startsWith('bpchar') ||
1900
- typeName === 'bytea' ||
1901
- typeName === 'orderedarray' ||
1902
- typeName === 'date')) {
1903
- // Remove pg_catalog prefix for :: syntax
1904
- const cleanTypeName = typeName.replace('pg_catalog.', '');
1905
- return `${arg}::${cleanTypeName}`;
2059
+ if (this.isPgCatalogType(typeName)) {
2060
+ const argType = this.getNodeType(node.arg);
2061
+ const isSimpleArgument = argType === 'A_Const' || argType === 'ColumnRef';
2062
+ const isFunctionCall = argType === 'FuncCall';
2063
+ if (isSimpleArgument || isFunctionCall) {
2064
+ // For simple arguments, avoid :: syntax if they have complex structure
2065
+ if (isSimpleArgument && (arg.includes('(') || arg.startsWith('-'))) {
2066
+ }
2067
+ else {
2068
+ const cleanTypeName = typeName.replace('pg_catalog.', '');
2069
+ return `${arg}::${cleanTypeName}`;
2070
+ }
2071
+ }
1906
2072
  }
1907
2073
  return `CAST(${arg} AS ${typeName})`;
1908
2074
  }
@@ -2085,7 +2251,14 @@ export class Deparser {
2085
2251
  return this.deparse(el, context);
2086
2252
  });
2087
2253
  if (this.formatter.isPretty()) {
2088
- const formattedElements = elementStrs.map(el => this.formatter.indent(el)).join(',' + this.formatter.newline());
2254
+ const formattedElements = elementStrs.map(el => {
2255
+ const trimmedEl = el.trim();
2256
+ // Remove leading newlines from constraint elements to avoid extra blank lines
2257
+ if (trimmedEl.startsWith('\n')) {
2258
+ return this.formatter.indent(trimmedEl.substring(1));
2259
+ }
2260
+ return this.formatter.indent(trimmedEl);
2261
+ }).join(',' + this.formatter.newline());
2089
2262
  output.push('(' + this.formatter.newline() + formattedElements + this.formatter.newline() + ')');
2090
2263
  }
2091
2264
  else {
@@ -2247,9 +2420,25 @@ export class Deparser {
2247
2420
  }
2248
2421
  break;
2249
2422
  case 'CONSTR_CHECK':
2250
- output.push('CHECK');
2423
+ if (this.formatter.isPretty() && !context.isColumnConstraint) {
2424
+ output.push('\n' + this.formatter.indent('CHECK'));
2425
+ }
2426
+ else {
2427
+ output.push('CHECK');
2428
+ }
2251
2429
  if (node.raw_expr) {
2252
- output.push(this.formatter.parens(this.visit(node.raw_expr, context)));
2430
+ if (this.formatter.isPretty()) {
2431
+ const checkExpr = this.visit(node.raw_expr, context);
2432
+ if (checkExpr.includes('\n')) {
2433
+ output.push('(\n' + this.formatter.indent(checkExpr) + '\n)');
2434
+ }
2435
+ else {
2436
+ output.push(`(${checkExpr})`);
2437
+ }
2438
+ }
2439
+ else {
2440
+ output.push(this.formatter.parens(this.visit(node.raw_expr, context)));
2441
+ }
2253
2442
  }
2254
2443
  // Handle NOT VALID for check constraints
2255
2444
  if (node.skip_validation) {
@@ -2328,7 +2517,12 @@ export class Deparser {
2328
2517
  }
2329
2518
  break;
2330
2519
  case 'CONSTR_UNIQUE':
2331
- output.push('UNIQUE');
2520
+ if (this.formatter.isPretty() && !context.isColumnConstraint) {
2521
+ output.push('\n' + this.formatter.indent('UNIQUE'));
2522
+ }
2523
+ else {
2524
+ output.push('UNIQUE');
2525
+ }
2332
2526
  if (node.nulls_not_distinct) {
2333
2527
  output.push('NULLS NOT DISTINCT');
2334
2528
  }
@@ -2346,33 +2540,77 @@ export class Deparser {
2346
2540
  case 'CONSTR_FOREIGN':
2347
2541
  // Only add "FOREIGN KEY" for table-level constraints, not column-level constraints
2348
2542
  if (!context.isColumnConstraint) {
2349
- output.push('FOREIGN KEY');
2350
- if (node.fk_attrs && node.fk_attrs.length > 0) {
2351
- const fkAttrs = ListUtils.unwrapList(node.fk_attrs)
2352
- .map(attr => this.visit(attr, context))
2353
- .join(', ');
2354
- output.push(`(${fkAttrs})`);
2543
+ if (this.formatter.isPretty()) {
2544
+ output.push('\n' + this.formatter.indent('FOREIGN KEY'));
2545
+ if (node.fk_attrs && node.fk_attrs.length > 0) {
2546
+ const fkAttrs = ListUtils.unwrapList(node.fk_attrs)
2547
+ .map(attr => this.visit(attr, context))
2548
+ .join(', ');
2549
+ output.push(`(${fkAttrs})`);
2550
+ }
2551
+ output.push('\n' + this.formatter.indent('REFERENCES'));
2355
2552
  }
2553
+ else {
2554
+ output.push('FOREIGN KEY');
2555
+ if (node.fk_attrs && node.fk_attrs.length > 0) {
2556
+ const fkAttrs = ListUtils.unwrapList(node.fk_attrs)
2557
+ .map(attr => this.visit(attr, context))
2558
+ .join(', ');
2559
+ output.push(`(${fkAttrs})`);
2560
+ }
2561
+ output.push('REFERENCES');
2562
+ }
2563
+ }
2564
+ else {
2565
+ output.push('REFERENCES');
2356
2566
  }
2357
- output.push('REFERENCES');
2358
2567
  if (node.pktable) {
2359
- output.push(this.RangeVar(node.pktable, context));
2568
+ if (this.formatter.isPretty() && !context.isColumnConstraint) {
2569
+ const lastIndex = output.length - 1;
2570
+ if (lastIndex >= 0 && output[lastIndex].includes('REFERENCES')) {
2571
+ output[lastIndex] += ' ' + this.RangeVar(node.pktable, context);
2572
+ }
2573
+ else {
2574
+ output.push(this.RangeVar(node.pktable, context));
2575
+ }
2576
+ }
2577
+ else {
2578
+ output.push(this.RangeVar(node.pktable, context));
2579
+ }
2360
2580
  }
2361
2581
  if (node.pk_attrs && node.pk_attrs.length > 0) {
2362
2582
  const pkAttrs = ListUtils.unwrapList(node.pk_attrs)
2363
2583
  .map(attr => this.visit(attr, context))
2364
2584
  .join(', ');
2365
- output.push(`(${pkAttrs})`);
2585
+ if (this.formatter.isPretty() && !context.isColumnConstraint) {
2586
+ const lastIndex = output.length - 1;
2587
+ if (lastIndex >= 0) {
2588
+ output[lastIndex] += ` (${pkAttrs})`;
2589
+ }
2590
+ else {
2591
+ output.push(`(${pkAttrs})`);
2592
+ }
2593
+ }
2594
+ else {
2595
+ output.push(`(${pkAttrs})`);
2596
+ }
2366
2597
  }
2367
2598
  if (node.fk_matchtype && node.fk_matchtype !== 's') {
2599
+ let matchClause = '';
2368
2600
  switch (node.fk_matchtype) {
2369
2601
  case 'f':
2370
- output.push('MATCH FULL');
2602
+ matchClause = 'MATCH FULL';
2371
2603
  break;
2372
2604
  case 'p':
2373
- output.push('MATCH PARTIAL');
2605
+ matchClause = 'MATCH PARTIAL';
2374
2606
  break;
2375
2607
  }
2608
+ if (this.formatter.isPretty() && !context.isColumnConstraint) {
2609
+ output.push('\n' + this.formatter.indent(matchClause));
2610
+ }
2611
+ else {
2612
+ output.push(matchClause);
2613
+ }
2376
2614
  }
2377
2615
  if (node.fk_upd_action && node.fk_upd_action !== 'a') {
2378
2616
  let updateClause = 'ON UPDATE ';
@@ -2424,7 +2662,12 @@ export class Deparser {
2424
2662
  }
2425
2663
  // Handle NOT VALID for foreign key constraints - only for table constraints, not domain constraints
2426
2664
  if (node.skip_validation && !context.isDomainConstraint) {
2427
- output.push('NOT VALID');
2665
+ if (this.formatter.isPretty() && !context.isColumnConstraint) {
2666
+ output.push('\n' + this.formatter.indent('NOT VALID'));
2667
+ }
2668
+ else {
2669
+ output.push('NOT VALID');
2670
+ }
2428
2671
  }
2429
2672
  break;
2430
2673
  case 'CONSTR_ATTR_DEFERRABLE':
@@ -3423,8 +3666,8 @@ export class Deparser {
3423
3666
  else if (nodeData.sval !== undefined) {
3424
3667
  // Handle nested sval structure: { sval: { sval: "value" } }
3425
3668
  const svalValue = typeof nodeData.sval === 'object' ? nodeData.sval.sval : nodeData.sval;
3426
- const stringValue = svalValue.replace(/'/g, '').toLowerCase();
3427
- boolValue = stringValue === 'on' || stringValue === 'true';
3669
+ const stringValue = svalValue.replace(/'/g, '');
3670
+ boolValue = stringValue.toLowerCase() === 'on' || stringValue.toLowerCase() === 'true';
3428
3671
  }
3429
3672
  }
3430
3673
  return boolValue ? 'READ ONLY' : 'READ WRITE';
@@ -3447,8 +3690,8 @@ export class Deparser {
3447
3690
  else if (nodeData.sval !== undefined) {
3448
3691
  // Handle nested sval structure: { sval: { sval: "value" } }
3449
3692
  const svalValue = typeof nodeData.sval === 'object' ? nodeData.sval.sval : nodeData.sval;
3450
- const stringValue = svalValue.replace(/'/g, '').toLowerCase();
3451
- boolValue = stringValue === 'on' || stringValue === 'true';
3693
+ const stringValue = svalValue.replace(/'/g, '');
3694
+ boolValue = stringValue.toLowerCase() === 'on' || stringValue.toLowerCase() === 'true';
3452
3695
  }
3453
3696
  }
3454
3697
  return boolValue ? 'DEFERRABLE' : 'NOT DEFERRABLE';
@@ -3515,8 +3758,8 @@ export class Deparser {
3515
3758
  else if (nodeData.sval !== undefined) {
3516
3759
  // Handle nested sval structure: { sval: { sval: "value" } }
3517
3760
  const svalValue = typeof nodeData.sval === 'object' ? nodeData.sval.sval : nodeData.sval;
3518
- const stringValue = svalValue.replace(/'/g, '').toLowerCase();
3519
- boolValue = stringValue === 'on' || stringValue === 'true';
3761
+ const stringValue = svalValue.replace(/'/g, '');
3762
+ boolValue = stringValue.toLowerCase() === 'on' || stringValue.toLowerCase() === 'true';
3520
3763
  }
3521
3764
  }
3522
3765
  transactionOptions.push(boolValue ? 'READ ONLY' : 'READ WRITE');
@@ -3534,8 +3777,8 @@ export class Deparser {
3534
3777
  else if (nodeData.sval !== undefined) {
3535
3778
  // Handle nested sval structure: { sval: { sval: "value" } }
3536
3779
  const svalValue = typeof nodeData.sval === 'object' ? nodeData.sval.sval : nodeData.sval;
3537
- const stringValue = svalValue.replace(/'/g, '').toLowerCase();
3538
- boolValue = stringValue === 'on' || stringValue === 'true';
3780
+ const stringValue = svalValue.replace(/'/g, '');
3781
+ boolValue = stringValue.toLowerCase() === 'on' || stringValue.toLowerCase() === 'true';
3539
3782
  }
3540
3783
  }
3541
3784
  transactionOptions.push(boolValue ? 'DEFERRABLE' : 'NOT DEFERRABLE');
@@ -3601,7 +3844,7 @@ export class Deparser {
3601
3844
  }
3602
3845
  switch (node.roletype) {
3603
3846
  case 'ROLESPEC_PUBLIC':
3604
- return 'public';
3847
+ return 'PUBLIC';
3605
3848
  case 'ROLESPEC_CURRENT_USER':
3606
3849
  return 'CURRENT_USER';
3607
3850
  case 'ROLESPEC_SESSION_USER':
@@ -3609,7 +3852,7 @@ export class Deparser {
3609
3852
  case 'ROLESPEC_CURRENT_ROLE':
3610
3853
  return 'CURRENT_ROLE';
3611
3854
  default:
3612
- return 'public';
3855
+ return 'PUBLIC';
3613
3856
  }
3614
3857
  }
3615
3858
  roletype(node, context) {
@@ -4073,10 +4316,25 @@ export class Deparser {
4073
4316
  output.push(relationStr);
4074
4317
  }
4075
4318
  if (node.cmds && node.cmds.length > 0) {
4076
- const commandsStr = ListUtils.unwrapList(node.cmds)
4077
- .map(cmd => this.visit(cmd, alterContext))
4078
- .join(', ');
4079
- output.push(commandsStr);
4319
+ const commands = ListUtils.unwrapList(node.cmds);
4320
+ if (this.formatter.isPretty()) {
4321
+ const commandsStr = commands
4322
+ .map(cmd => {
4323
+ const cmdStr = this.visit(cmd, alterContext);
4324
+ if (cmdStr.startsWith('ADD CONSTRAINT') || cmdStr.startsWith('ADD ')) {
4325
+ return this.formatter.newline() + this.formatter.indent(cmdStr);
4326
+ }
4327
+ return cmdStr;
4328
+ })
4329
+ .join(',');
4330
+ output.push(commandsStr);
4331
+ }
4332
+ else {
4333
+ const commandsStr = commands
4334
+ .map(cmd => this.visit(cmd, alterContext))
4335
+ .join(', ');
4336
+ output.push(commandsStr);
4337
+ }
4080
4338
  }
4081
4339
  return output.join(' ');
4082
4340
  }
@@ -6052,7 +6310,7 @@ export class Deparser {
6052
6310
  const output = [];
6053
6311
  const initialParts = ['CREATE', 'POLICY'];
6054
6312
  if (node.policy_name) {
6055
- initialParts.push(`"${node.policy_name}"`);
6313
+ initialParts.push(QuoteUtils.quote(node.policy_name));
6056
6314
  }
6057
6315
  output.push(initialParts.join(' '));
6058
6316
  // Add ON clause on new line in pretty mode
@@ -6129,7 +6387,7 @@ export class Deparser {
6129
6387
  AlterPolicyStmt(node, context) {
6130
6388
  const output = ['ALTER', 'POLICY'];
6131
6389
  if (node.policy_name) {
6132
- output.push(`"${node.policy_name}"`);
6390
+ output.push(QuoteUtils.quote(node.policy_name));
6133
6391
  }
6134
6392
  if (node.table) {
6135
6393
  output.push('ON');
@@ -7627,7 +7885,12 @@ export class Deparser {
7627
7885
  }
7628
7886
  if (node.action) {
7629
7887
  const actionStr = this.GrantStmt(node.action, context);
7630
- output.push(actionStr);
7888
+ if (this.formatter.isPretty()) {
7889
+ return output.join(' ') + this.formatter.newline() + this.formatter.indent(actionStr);
7890
+ }
7891
+ else {
7892
+ output.push(actionStr);
7893
+ }
7631
7894
  }
7632
7895
  return output.join(' ');
7633
7896
  }
@@ -7774,84 +8037,158 @@ export class Deparser {
7774
8037
  if (node.trigname) {
7775
8038
  output.push(QuoteUtils.quote(node.trigname));
7776
8039
  }
7777
- const timing = [];
7778
- if (node.timing & 2)
7779
- timing.push('BEFORE');
7780
- else if (node.timing & 64)
7781
- timing.push('INSTEAD OF');
7782
- else
7783
- timing.push('AFTER'); // Default timing when no specific timing is set
7784
- output.push(timing.join(' '));
7785
- const events = [];
7786
- if (node.events & 4)
7787
- events.push('INSERT');
7788
- if (node.events & 8)
7789
- events.push('DELETE');
7790
- if (node.events & 16)
7791
- events.push('UPDATE');
7792
- if (node.events & 32)
7793
- events.push('TRUNCATE');
7794
- output.push(events.join(' OR '));
7795
- if (node.columns && node.columns.length > 0) {
7796
- output.push('OF');
7797
- const columnNames = ListUtils.unwrapList(node.columns)
7798
- .map(col => this.visit(col, context))
7799
- .join(', ');
7800
- output.push(columnNames);
7801
- }
7802
- output.push('ON');
7803
- if (node.relation) {
7804
- output.push(this.RangeVar(node.relation, context));
7805
- }
7806
- if (node.constrrel) {
7807
- output.push('FROM');
7808
- output.push(this.RangeVar(node.constrrel, context));
7809
- }
7810
- if (node.deferrable) {
7811
- output.push('DEFERRABLE');
7812
- }
7813
- if (node.initdeferred) {
7814
- output.push('INITIALLY DEFERRED');
7815
- }
7816
- // Handle REFERENCING clauses
7817
- if (node.transitionRels && node.transitionRels.length > 0) {
7818
- output.push('REFERENCING');
7819
- const transitionClauses = ListUtils.unwrapList(node.transitionRels)
7820
- .map(rel => this.visit(rel, context))
7821
- .join(' ');
7822
- output.push(transitionClauses);
7823
- }
7824
- if (node.row) {
7825
- output.push('FOR EACH ROW');
7826
- }
7827
- else {
7828
- output.push('FOR EACH STATEMENT');
7829
- }
7830
- if (node.whenClause) {
7831
- output.push('WHEN');
7832
- output.push('(');
7833
- output.push(this.visit(node.whenClause, context));
7834
- output.push(')');
7835
- }
7836
- output.push('EXECUTE');
7837
- if (node.funcname && node.funcname.length > 0) {
7838
- const funcName = ListUtils.unwrapList(node.funcname)
7839
- .map(name => this.visit(name, context))
7840
- .join('.');
7841
- output.push('FUNCTION', funcName);
7842
- }
7843
- if (node.args && node.args.length > 0) {
7844
- output.push('(');
7845
- const args = ListUtils.unwrapList(node.args)
7846
- .map(arg => this.visit(arg, context))
7847
- .join(', ');
7848
- output.push(args);
7849
- output.push(')');
8040
+ if (this.formatter.isPretty()) {
8041
+ const components = [];
8042
+ const timing = [];
8043
+ if (node.timing & 2)
8044
+ timing.push('BEFORE');
8045
+ else if (node.timing & 64)
8046
+ timing.push('INSTEAD OF');
8047
+ else
8048
+ timing.push('AFTER');
8049
+ const events = [];
8050
+ if (node.events & 4)
8051
+ events.push('INSERT');
8052
+ if (node.events & 8)
8053
+ events.push('DELETE');
8054
+ if (node.events & 16) {
8055
+ let updateStr = 'UPDATE';
8056
+ if (node.columns && node.columns.length > 0) {
8057
+ const columnNames = ListUtils.unwrapList(node.columns)
8058
+ .map(col => this.visit(col, context))
8059
+ .join(', ');
8060
+ updateStr += ' OF ' + columnNames;
8061
+ }
8062
+ events.push(updateStr);
8063
+ }
8064
+ if (node.events & 32)
8065
+ events.push('TRUNCATE');
8066
+ components.push(this.formatter.indent(timing.join(' ') + ' ' + events.join(' OR ')));
8067
+ if (node.relation) {
8068
+ components.push(this.formatter.indent('ON ' + this.RangeVar(node.relation, context)));
8069
+ }
8070
+ if (node.transitionRels && node.transitionRels.length > 0) {
8071
+ const transitionClauses = ListUtils.unwrapList(node.transitionRels)
8072
+ .map(rel => this.visit(rel, context))
8073
+ .join(' ');
8074
+ components.push(this.formatter.indent('REFERENCING ' + transitionClauses));
8075
+ }
8076
+ if (node.deferrable) {
8077
+ components.push(this.formatter.indent('DEFERRABLE'));
8078
+ }
8079
+ if (node.initdeferred) {
8080
+ components.push(this.formatter.indent('INITIALLY DEFERRED'));
8081
+ }
8082
+ if (node.row) {
8083
+ components.push(this.formatter.indent('FOR EACH ROW'));
8084
+ }
8085
+ else {
8086
+ components.push(this.formatter.indent('FOR EACH STATEMENT'));
8087
+ }
8088
+ if (node.whenClause) {
8089
+ const whenStr = 'WHEN (' + this.visit(node.whenClause, context) + ')';
8090
+ components.push(this.formatter.indent(whenStr));
8091
+ }
8092
+ let executeStr = 'EXECUTE';
8093
+ if (node.funcname && node.funcname.length > 0) {
8094
+ const funcName = ListUtils.unwrapList(node.funcname)
8095
+ .map(name => this.visit(name, context))
8096
+ .join('.');
8097
+ executeStr += ' PROCEDURE ' + funcName;
8098
+ }
8099
+ if (node.args && node.args.length > 0) {
8100
+ const argContext = { ...context, isStringLiteral: true };
8101
+ const args = ListUtils.unwrapList(node.args)
8102
+ .map(arg => this.visit(arg, argContext))
8103
+ .join(', ');
8104
+ executeStr += '(' + args + ')';
8105
+ }
8106
+ else {
8107
+ executeStr += '()';
8108
+ }
8109
+ components.push(this.formatter.indent(executeStr));
8110
+ return output.join(' ') + this.formatter.newline() + components.join(this.formatter.newline());
7850
8111
  }
7851
8112
  else {
7852
- output.push('()');
8113
+ const timing = [];
8114
+ if (node.timing & 2)
8115
+ timing.push('BEFORE');
8116
+ else if (node.timing & 64)
8117
+ timing.push('INSTEAD OF');
8118
+ else
8119
+ timing.push('AFTER');
8120
+ output.push(timing.join(' '));
8121
+ const events = [];
8122
+ if (node.events & 4)
8123
+ events.push('INSERT');
8124
+ if (node.events & 8)
8125
+ events.push('DELETE');
8126
+ if (node.events & 16)
8127
+ events.push('UPDATE');
8128
+ if (node.events & 32)
8129
+ events.push('TRUNCATE');
8130
+ output.push(events.join(' OR '));
8131
+ if (node.columns && node.columns.length > 0) {
8132
+ output.push('OF');
8133
+ const columnNames = ListUtils.unwrapList(node.columns)
8134
+ .map(col => this.visit(col, context))
8135
+ .join(', ');
8136
+ output.push(columnNames);
8137
+ }
8138
+ output.push('ON');
8139
+ if (node.relation) {
8140
+ output.push(this.RangeVar(node.relation, context));
8141
+ }
8142
+ if (node.constrrel) {
8143
+ output.push('FROM');
8144
+ output.push(this.RangeVar(node.constrrel, context));
8145
+ }
8146
+ if (node.deferrable) {
8147
+ output.push('DEFERRABLE');
8148
+ }
8149
+ if (node.initdeferred) {
8150
+ output.push('INITIALLY DEFERRED');
8151
+ }
8152
+ if (node.transitionRels && node.transitionRels.length > 0) {
8153
+ output.push('REFERENCING');
8154
+ const transitionClauses = ListUtils.unwrapList(node.transitionRels)
8155
+ .map(rel => this.visit(rel, context))
8156
+ .join(' ');
8157
+ output.push(transitionClauses);
8158
+ }
8159
+ if (node.row) {
8160
+ output.push('FOR EACH ROW');
8161
+ }
8162
+ else {
8163
+ output.push('FOR EACH STATEMENT');
8164
+ }
8165
+ if (node.whenClause) {
8166
+ output.push('WHEN');
8167
+ output.push('(');
8168
+ output.push(this.visit(node.whenClause, context));
8169
+ output.push(')');
8170
+ }
8171
+ output.push('EXECUTE');
8172
+ if (node.funcname && node.funcname.length > 0) {
8173
+ const funcName = ListUtils.unwrapList(node.funcname)
8174
+ .map(name => this.visit(name, context))
8175
+ .join('.');
8176
+ output.push('FUNCTION', funcName);
8177
+ }
8178
+ if (node.args && node.args.length > 0) {
8179
+ output.push('(');
8180
+ const argContext = { ...context, isStringLiteral: true };
8181
+ const args = ListUtils.unwrapList(node.args)
8182
+ .map(arg => this.visit(arg, argContext))
8183
+ .join(', ');
8184
+ output.push(args);
8185
+ output.push(')');
8186
+ }
8187
+ else {
8188
+ output.push('()');
8189
+ }
8190
+ return output.join(' ');
7853
8191
  }
7854
- return output.join(' ');
7855
8192
  }
7856
8193
  TriggerTransition(node, context) {
7857
8194
  const output = [];
@@ -8429,7 +8766,7 @@ export class Deparser {
8429
8766
  AccessPriv(node, context) {
8430
8767
  const output = [];
8431
8768
  if (node.priv_name) {
8432
- output.push(node.priv_name);
8769
+ output.push(node.priv_name.toUpperCase());
8433
8770
  }
8434
8771
  else {
8435
8772
  output.push('ALL');