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