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.d.ts +2 -0
- package/deparser.js +486 -149
- package/esm/deparser.js +486 -149
- package/package.json +3 -3
- package/visitors/base.d.ts +1 -0
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
|
-
|
|
262
|
-
|
|
263
|
-
const
|
|
264
|
-
if
|
|
265
|
-
|
|
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
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
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
|
-
|
|
311
|
-
|
|
312
|
-
const
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
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
|
-
|
|
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
|
|
1885
|
-
if (typeName === 'bpchar'
|
|
1886
|
-
const names =
|
|
1887
|
-
|
|
1888
|
-
names[0]
|
|
1889
|
-
names[1]
|
|
1890
|
-
|
|
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
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
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 =>
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2602
|
+
matchClause = 'MATCH FULL';
|
|
2371
2603
|
break;
|
|
2372
2604
|
case 'p':
|
|
2373
|
-
|
|
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
|
-
|
|
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, '')
|
|
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, '')
|
|
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, '')
|
|
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, '')
|
|
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 '
|
|
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 '
|
|
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
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
7778
|
-
|
|
7779
|
-
timing
|
|
7780
|
-
|
|
7781
|
-
|
|
7782
|
-
|
|
7783
|
-
|
|
7784
|
-
|
|
7785
|
-
|
|
7786
|
-
|
|
7787
|
-
events
|
|
7788
|
-
|
|
7789
|
-
events
|
|
7790
|
-
|
|
7791
|
-
events
|
|
7792
|
-
|
|
7793
|
-
|
|
7794
|
-
|
|
7795
|
-
|
|
7796
|
-
|
|
7797
|
-
|
|
7798
|
-
|
|
7799
|
-
.
|
|
7800
|
-
|
|
7801
|
-
|
|
7802
|
-
|
|
7803
|
-
|
|
7804
|
-
|
|
7805
|
-
|
|
7806
|
-
|
|
7807
|
-
|
|
7808
|
-
|
|
7809
|
-
|
|
7810
|
-
|
|
7811
|
-
|
|
7812
|
-
|
|
7813
|
-
|
|
7814
|
-
|
|
7815
|
-
|
|
7816
|
-
|
|
7817
|
-
|
|
7818
|
-
|
|
7819
|
-
|
|
7820
|
-
.
|
|
7821
|
-
|
|
7822
|
-
|
|
7823
|
-
|
|
7824
|
-
|
|
7825
|
-
|
|
7826
|
-
|
|
7827
|
-
|
|
7828
|
-
|
|
7829
|
-
|
|
7830
|
-
|
|
7831
|
-
|
|
7832
|
-
|
|
7833
|
-
|
|
7834
|
-
|
|
7835
|
-
|
|
7836
|
-
|
|
7837
|
-
|
|
7838
|
-
|
|
7839
|
-
|
|
7840
|
-
|
|
7841
|
-
|
|
7842
|
-
|
|
7843
|
-
|
|
7844
|
-
|
|
7845
|
-
|
|
7846
|
-
|
|
7847
|
-
|
|
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
|
-
|
|
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');
|