pgsql-deparser 17.7.2 → 17.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -2
- package/deparser.d.ts +2 -0
- package/deparser.js +312 -78
- package/esm/deparser.js +312 -78
- package/esm/utils/sql-formatter.js +11 -2
- package/package.json +2 -2
- package/utils/sql-formatter.d.ts +3 -1
- package/utils/sql-formatter.js +11 -2
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ npm install pgsql-deparser
|
|
|
24
24
|
|
|
25
25
|
## Features
|
|
26
26
|
|
|
27
|
-
* ⚡ **Pure TypeScript Performance** – Zero dependencies, no WASM, no compilation - just blazing fast SQL generation
|
|
27
|
+
* ⚡ **Pure TypeScript Performance** – Zero runtime dependencies, no WASM, no compilation - just blazing fast SQL generation
|
|
28
28
|
* 🪶 **Ultra Lightweight** – Minimal footprint with laser-focused functionality for AST-to-SQL conversion only
|
|
29
29
|
* 🧪 **Battle-Tested Reliability** – Validated against 23,000+ SQL statements ensuring production-grade stability
|
|
30
30
|
* 🌍 **Universal Compatibility** – Runs anywhere JavaScript does - browsers, Node.js, edge functions, you name it
|
|
@@ -69,6 +69,48 @@ console.log(deparse(stmt));
|
|
|
69
69
|
// Output: SELECT * FROM another_table
|
|
70
70
|
```
|
|
71
71
|
|
|
72
|
+
## Options
|
|
73
|
+
|
|
74
|
+
The deparser accepts optional configuration for formatting and output control:
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
import { deparseSync as deparse } from 'pgsql-deparser';
|
|
78
|
+
|
|
79
|
+
const options = {
|
|
80
|
+
pretty: true, // Enable pretty formatting (default: false)
|
|
81
|
+
newline: '\n', // Newline character (default: '\n')
|
|
82
|
+
tab: ' ', // Tab/indentation character (default: ' ')
|
|
83
|
+
semicolons: true // Add semicolons to statements (default: true)
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const sql = deparse(ast, options);
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
| Option | Type | Default | Description |
|
|
90
|
+
|--------|------|---------|-------------|
|
|
91
|
+
| `pretty` | `boolean` | `false` | Enable pretty formatting with indentation and line breaks |
|
|
92
|
+
| `newline` | `string` | `'\n'` | Character(s) used for line breaks |
|
|
93
|
+
| `tab` | `string` | `' '` | Character(s) used for indentation |
|
|
94
|
+
| `semicolons` | `boolean` | `true` | Add semicolons to SQL statements |
|
|
95
|
+
|
|
96
|
+
**Pretty formatting example:**
|
|
97
|
+
```ts
|
|
98
|
+
// Without pretty formatting
|
|
99
|
+
const sql1 = deparse(selectAst, { pretty: false });
|
|
100
|
+
// "SELECT id, name FROM users WHERE active = true;"
|
|
101
|
+
|
|
102
|
+
// With pretty formatting
|
|
103
|
+
const sql2 = deparse(selectAst, { pretty: true });
|
|
104
|
+
// SELECT
|
|
105
|
+
// id,
|
|
106
|
+
// name
|
|
107
|
+
// FROM users
|
|
108
|
+
// WHERE
|
|
109
|
+
// active = true;
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
For complete documentation and advanced options, see [DEPARSER_USAGE.md](../../DEPARSER_USAGE.md).
|
|
113
|
+
|
|
72
114
|
## Why Use `pgsql-deparser`?
|
|
73
115
|
|
|
74
116
|
`pgsql-deparser` is particularly useful in development environments where native dependencies are problematic or in applications where only the deparser functionality is required. Its independence from the full `pgsql-parser` package allows for more focused and lightweight SQL generation tasks.
|
|
@@ -98,4 +140,4 @@ Built on the excellent work of several contributors:
|
|
|
98
140
|
|
|
99
141
|
AS DESCRIBED IN THE LICENSES, THE SOFTWARE IS PROVIDED "AS IS", AT YOUR OWN RISK, AND WITHOUT WARRANTIES OF ANY KIND.
|
|
100
142
|
|
|
101
|
-
No developer or entity involved in creating Software will be liable for any claims or damages whatsoever associated with your use, inability to use, or your interaction with other users of the Software code or Software CLI, including any direct, indirect, incidental, special, exemplary, punitive or consequential damages, or loss of profits, cryptocurrencies, tokens, or anything else of value.
|
|
143
|
+
No developer or entity involved in creating Software will be liable for any claims or damages whatsoever associated with your use, inability to use, or your interaction with other users of the Software code or Software CLI, including any direct, indirect, incidental, special, exemplary, punitive or consequential damages, or loss of profits, cryptocurrencies, tokens, or anything else of value.
|
package/deparser.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export interface DeparserOptions {
|
|
|
6
6
|
tab?: string;
|
|
7
7
|
functionDelimiter?: string;
|
|
8
8
|
functionDelimiterFallback?: string;
|
|
9
|
+
pretty?: boolean;
|
|
9
10
|
}
|
|
10
11
|
/**
|
|
11
12
|
* Deparser - Converts PostgreSQL AST nodes back to SQL strings
|
|
@@ -294,4 +295,5 @@ export declare class Deparser implements DeparserVisitor {
|
|
|
294
295
|
AlterObjectSchemaStmt(node: t.AlterObjectSchemaStmt, context: DeparserContext): string;
|
|
295
296
|
AlterRoleSetStmt(node: t.AlterRoleSetStmt, context: DeparserContext): string;
|
|
296
297
|
CreateForeignTableStmt(node: t.CreateForeignTableStmt, context: DeparserContext): string;
|
|
298
|
+
private containsMultilineStringLiteral;
|
|
297
299
|
}
|
package/deparser.js
CHANGED
|
@@ -51,7 +51,7 @@ class Deparser {
|
|
|
51
51
|
tree;
|
|
52
52
|
options;
|
|
53
53
|
constructor(tree, opts = {}) {
|
|
54
|
-
this.formatter = new sql_formatter_1.SqlFormatter(opts.newline, opts.tab);
|
|
54
|
+
this.formatter = new sql_formatter_1.SqlFormatter(opts.newline, opts.tab, opts.pretty);
|
|
55
55
|
// Set default options
|
|
56
56
|
this.options = {
|
|
57
57
|
functionDelimiter: '$$',
|
|
@@ -184,7 +184,9 @@ class Deparser {
|
|
|
184
184
|
}
|
|
185
185
|
if (!node.op || node.op === 'SETOP_NONE') {
|
|
186
186
|
if (node.valuesLists == null) {
|
|
187
|
-
|
|
187
|
+
if (!this.formatter.isPretty() || !node.targetList) {
|
|
188
|
+
output.push('SELECT');
|
|
189
|
+
}
|
|
188
190
|
}
|
|
189
191
|
}
|
|
190
192
|
else {
|
|
@@ -230,41 +232,82 @@ class Deparser {
|
|
|
230
232
|
output.push(rightStmt);
|
|
231
233
|
}
|
|
232
234
|
}
|
|
235
|
+
// Handle DISTINCT clause - in pretty mode, we'll include it in the SELECT clause
|
|
236
|
+
let distinctPart = '';
|
|
233
237
|
if (node.distinctClause) {
|
|
234
238
|
const distinctClause = list_utils_1.ListUtils.unwrapList(node.distinctClause);
|
|
235
239
|
if (distinctClause.length > 0 && Object.keys(distinctClause[0]).length > 0) {
|
|
236
|
-
output.push('DISTINCT ON');
|
|
237
240
|
const clause = distinctClause
|
|
238
241
|
.map(e => this.visit(e, { ...context, select: true }))
|
|
239
242
|
.join(', ');
|
|
240
|
-
|
|
243
|
+
distinctPart = ' DISTINCT ON ' + this.formatter.parens(clause);
|
|
241
244
|
}
|
|
242
245
|
else {
|
|
243
|
-
|
|
246
|
+
distinctPart = ' DISTINCT';
|
|
247
|
+
}
|
|
248
|
+
if (!this.formatter.isPretty()) {
|
|
249
|
+
if (distinctClause.length > 0 && Object.keys(distinctClause[0]).length > 0) {
|
|
250
|
+
output.push('DISTINCT ON');
|
|
251
|
+
const clause = distinctClause
|
|
252
|
+
.map(e => this.visit(e, { ...context, select: true }))
|
|
253
|
+
.join(', ');
|
|
254
|
+
output.push(this.formatter.parens(clause));
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
output.push('DISTINCT');
|
|
258
|
+
}
|
|
244
259
|
}
|
|
245
260
|
}
|
|
246
261
|
if (node.targetList) {
|
|
247
262
|
const targetList = list_utils_1.ListUtils.unwrapList(node.targetList);
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
263
|
+
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;
|
|
269
|
+
}
|
|
270
|
+
return this.formatter.indent(targetStr);
|
|
271
|
+
});
|
|
272
|
+
const formattedTargets = targetStrings.join(',' + this.formatter.newline());
|
|
273
|
+
output.push('SELECT' + distinctPart);
|
|
274
|
+
output.push(formattedTargets);
|
|
275
|
+
}
|
|
276
|
+
else {
|
|
277
|
+
const targets = targetList
|
|
278
|
+
.map(e => this.visit(e, { ...context, select: true }))
|
|
279
|
+
.join(', ');
|
|
280
|
+
output.push(targets);
|
|
281
|
+
}
|
|
252
282
|
}
|
|
253
283
|
if (node.intoClause) {
|
|
254
284
|
output.push('INTO');
|
|
255
285
|
output.push(this.IntoClause(node.intoClause, context));
|
|
256
286
|
}
|
|
257
287
|
if (node.fromClause) {
|
|
258
|
-
output.push('FROM');
|
|
259
288
|
const fromList = list_utils_1.ListUtils.unwrapList(node.fromClause);
|
|
260
289
|
const fromItems = fromList
|
|
261
290
|
.map(e => this.deparse(e, { ...context, from: true }))
|
|
262
291
|
.join(', ');
|
|
263
|
-
output.push(fromItems);
|
|
292
|
+
output.push('FROM ' + fromItems.trim());
|
|
264
293
|
}
|
|
265
294
|
if (node.whereClause) {
|
|
266
|
-
|
|
267
|
-
|
|
295
|
+
if (this.formatter.isPretty()) {
|
|
296
|
+
output.push('WHERE');
|
|
297
|
+
const whereExpr = this.visit(node.whereClause, context);
|
|
298
|
+
const lines = whereExpr.split(this.formatter.newline());
|
|
299
|
+
const indentedLines = lines.map((line, index) => {
|
|
300
|
+
if (index === 0) {
|
|
301
|
+
return this.formatter.indent(line);
|
|
302
|
+
}
|
|
303
|
+
return line;
|
|
304
|
+
});
|
|
305
|
+
output.push(indentedLines.join(this.formatter.newline()));
|
|
306
|
+
}
|
|
307
|
+
else {
|
|
308
|
+
output.push('WHERE');
|
|
309
|
+
output.push(this.visit(node.whereClause, context));
|
|
310
|
+
}
|
|
268
311
|
}
|
|
269
312
|
if (node.valuesLists) {
|
|
270
313
|
output.push('VALUES');
|
|
@@ -275,16 +318,43 @@ class Deparser {
|
|
|
275
318
|
output.push(lists.join(', '));
|
|
276
319
|
}
|
|
277
320
|
if (node.groupClause) {
|
|
278
|
-
output.push('GROUP BY');
|
|
279
321
|
const groupList = list_utils_1.ListUtils.unwrapList(node.groupClause);
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
322
|
+
if (this.formatter.isPretty()) {
|
|
323
|
+
const groupItems = groupList
|
|
324
|
+
.map(e => {
|
|
325
|
+
const groupStr = this.visit(e, { ...context, group: true });
|
|
326
|
+
if (this.containsMultilineStringLiteral(groupStr)) {
|
|
327
|
+
return groupStr;
|
|
328
|
+
}
|
|
329
|
+
return this.formatter.indent(groupStr);
|
|
330
|
+
})
|
|
331
|
+
.join(',' + this.formatter.newline());
|
|
332
|
+
output.push('GROUP BY');
|
|
333
|
+
output.push(groupItems);
|
|
334
|
+
}
|
|
335
|
+
else {
|
|
336
|
+
output.push('GROUP BY');
|
|
337
|
+
const groupItems = groupList
|
|
338
|
+
.map(e => this.visit(e, { ...context, group: true }))
|
|
339
|
+
.join(', ');
|
|
340
|
+
output.push(groupItems);
|
|
341
|
+
}
|
|
284
342
|
}
|
|
285
343
|
if (node.havingClause) {
|
|
286
|
-
|
|
287
|
-
|
|
344
|
+
if (this.formatter.isPretty()) {
|
|
345
|
+
output.push('HAVING');
|
|
346
|
+
const havingStr = this.visit(node.havingClause, context);
|
|
347
|
+
if (this.containsMultilineStringLiteral(havingStr)) {
|
|
348
|
+
output.push(havingStr);
|
|
349
|
+
}
|
|
350
|
+
else {
|
|
351
|
+
output.push(this.formatter.indent(havingStr));
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
else {
|
|
355
|
+
output.push('HAVING');
|
|
356
|
+
output.push(this.visit(node.havingClause, context));
|
|
357
|
+
}
|
|
288
358
|
}
|
|
289
359
|
if (node.windowClause) {
|
|
290
360
|
output.push('WINDOW');
|
|
@@ -295,20 +365,33 @@ class Deparser {
|
|
|
295
365
|
output.push(windowClauses);
|
|
296
366
|
}
|
|
297
367
|
if (node.sortClause) {
|
|
298
|
-
output.push('ORDER BY');
|
|
299
368
|
const sortList = list_utils_1.ListUtils.unwrapList(node.sortClause);
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
369
|
+
if (this.formatter.isPretty()) {
|
|
370
|
+
const sortItems = sortList
|
|
371
|
+
.map(e => {
|
|
372
|
+
const sortStr = this.visit(e, { ...context, sort: true });
|
|
373
|
+
if (this.containsMultilineStringLiteral(sortStr)) {
|
|
374
|
+
return sortStr;
|
|
375
|
+
}
|
|
376
|
+
return this.formatter.indent(sortStr);
|
|
377
|
+
})
|
|
378
|
+
.join(',' + this.formatter.newline());
|
|
379
|
+
output.push('ORDER BY');
|
|
380
|
+
output.push(sortItems);
|
|
381
|
+
}
|
|
382
|
+
else {
|
|
383
|
+
output.push('ORDER BY');
|
|
384
|
+
const sortItems = sortList
|
|
385
|
+
.map(e => this.visit(e, { ...context, sort: true }))
|
|
386
|
+
.join(', ');
|
|
387
|
+
output.push(sortItems);
|
|
388
|
+
}
|
|
304
389
|
}
|
|
305
390
|
if (node.limitCount) {
|
|
306
|
-
output.push('LIMIT');
|
|
307
|
-
output.push(this.visit(node.limitCount, context));
|
|
391
|
+
output.push('LIMIT ' + this.visit(node.limitCount, context));
|
|
308
392
|
}
|
|
309
393
|
if (node.limitOffset) {
|
|
310
|
-
output.push('OFFSET');
|
|
311
|
-
output.push(this.visit(node.limitOffset, context));
|
|
394
|
+
output.push('OFFSET ' + this.visit(node.limitOffset, context));
|
|
312
395
|
}
|
|
313
396
|
if (node.lockingClause) {
|
|
314
397
|
const lockingList = list_utils_1.ListUtils.unwrapList(node.lockingClause);
|
|
@@ -317,6 +400,10 @@ class Deparser {
|
|
|
317
400
|
.join(' ');
|
|
318
401
|
output.push(lockingClauses);
|
|
319
402
|
}
|
|
403
|
+
if (this.formatter.isPretty()) {
|
|
404
|
+
const filteredOutput = output.filter(item => item.trim() !== '');
|
|
405
|
+
return filteredOutput.join(this.formatter.newline());
|
|
406
|
+
}
|
|
320
407
|
return output.join(' ');
|
|
321
408
|
}
|
|
322
409
|
A_Expr(node, context) {
|
|
@@ -806,9 +893,23 @@ class Deparser {
|
|
|
806
893
|
if (node.recursive) {
|
|
807
894
|
output.push('RECURSIVE');
|
|
808
895
|
}
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
896
|
+
if (node.ctes && node.ctes.length > 0) {
|
|
897
|
+
const ctes = list_utils_1.ListUtils.unwrapList(node.ctes);
|
|
898
|
+
if (this.formatter.isPretty()) {
|
|
899
|
+
const cteStrings = ctes.map(cte => {
|
|
900
|
+
const cteStr = this.visit(cte, context);
|
|
901
|
+
if (this.containsMultilineStringLiteral(cteStr)) {
|
|
902
|
+
return this.formatter.newline() + cteStr;
|
|
903
|
+
}
|
|
904
|
+
return this.formatter.newline() + this.formatter.indent(cteStr);
|
|
905
|
+
});
|
|
906
|
+
output.push(cteStrings.join(','));
|
|
907
|
+
}
|
|
908
|
+
else {
|
|
909
|
+
const cteStrings = ctes.map(cte => this.visit(cte, context));
|
|
910
|
+
output.push(cteStrings.join(', '));
|
|
911
|
+
}
|
|
912
|
+
}
|
|
812
913
|
return output.join(' ');
|
|
813
914
|
}
|
|
814
915
|
ResTarget(node, context) {
|
|
@@ -892,11 +993,23 @@ class Deparser {
|
|
|
892
993
|
// return formatStr.replace('%s', () => andArgs); // ✅ Function callback prevents interpretation
|
|
893
994
|
switch (boolop) {
|
|
894
995
|
case 'AND_EXPR':
|
|
895
|
-
|
|
896
|
-
|
|
996
|
+
if (this.formatter.isPretty() && args.length > 1) {
|
|
997
|
+
const andArgs = args.map(arg => this.visit(arg, boolContext)).join(this.formatter.newline() + ' AND ');
|
|
998
|
+
return formatStr.replace('%s', () => andArgs);
|
|
999
|
+
}
|
|
1000
|
+
else {
|
|
1001
|
+
const andArgs = args.map(arg => this.visit(arg, boolContext)).join(' AND ');
|
|
1002
|
+
return formatStr.replace('%s', () => andArgs);
|
|
1003
|
+
}
|
|
897
1004
|
case 'OR_EXPR':
|
|
898
|
-
|
|
899
|
-
|
|
1005
|
+
if (this.formatter.isPretty() && args.length > 1) {
|
|
1006
|
+
const orArgs = args.map(arg => this.visit(arg, boolContext)).join(this.formatter.newline() + ' OR ');
|
|
1007
|
+
return formatStr.replace('%s', () => orArgs);
|
|
1008
|
+
}
|
|
1009
|
+
else {
|
|
1010
|
+
const orArgs = args.map(arg => this.visit(arg, boolContext)).join(' OR ');
|
|
1011
|
+
return formatStr.replace('%s', () => orArgs);
|
|
1012
|
+
}
|
|
900
1013
|
case 'NOT_EXPR':
|
|
901
1014
|
return `NOT (${this.visit(args[0], context)})`;
|
|
902
1015
|
default:
|
|
@@ -1943,7 +2056,13 @@ class Deparser {
|
|
|
1943
2056
|
const elementStrs = elements.map(el => {
|
|
1944
2057
|
return this.deparse(el, context);
|
|
1945
2058
|
});
|
|
1946
|
-
|
|
2059
|
+
if (this.formatter.isPretty()) {
|
|
2060
|
+
const formattedElements = elementStrs.map(el => this.formatter.indent(el)).join(',' + this.formatter.newline());
|
|
2061
|
+
output.push('(' + this.formatter.newline() + formattedElements + this.formatter.newline() + ')');
|
|
2062
|
+
}
|
|
2063
|
+
else {
|
|
2064
|
+
output.push(this.formatter.parens(elementStrs.join(', ')));
|
|
2065
|
+
}
|
|
1947
2066
|
}
|
|
1948
2067
|
else if (!node.partbound) {
|
|
1949
2068
|
output.push(this.formatter.parens(''));
|
|
@@ -2228,38 +2347,52 @@ class Deparser {
|
|
|
2228
2347
|
}
|
|
2229
2348
|
}
|
|
2230
2349
|
if (node.fk_upd_action && node.fk_upd_action !== 'a') {
|
|
2231
|
-
|
|
2350
|
+
let updateClause = 'ON UPDATE ';
|
|
2232
2351
|
switch (node.fk_upd_action) {
|
|
2233
2352
|
case 'r':
|
|
2234
|
-
|
|
2353
|
+
updateClause += 'RESTRICT';
|
|
2235
2354
|
break;
|
|
2236
2355
|
case 'c':
|
|
2237
|
-
|
|
2356
|
+
updateClause += 'CASCADE';
|
|
2238
2357
|
break;
|
|
2239
2358
|
case 'n':
|
|
2240
|
-
|
|
2359
|
+
updateClause += 'SET NULL';
|
|
2241
2360
|
break;
|
|
2242
2361
|
case 'd':
|
|
2243
|
-
|
|
2362
|
+
updateClause += 'SET DEFAULT';
|
|
2244
2363
|
break;
|
|
2245
2364
|
}
|
|
2365
|
+
if (this.formatter.isPretty()) {
|
|
2366
|
+
output.push('\n' + this.formatter.indent(updateClause));
|
|
2367
|
+
}
|
|
2368
|
+
else {
|
|
2369
|
+
output.push('ON UPDATE');
|
|
2370
|
+
output.push(updateClause.replace('ON UPDATE ', ''));
|
|
2371
|
+
}
|
|
2246
2372
|
}
|
|
2247
2373
|
if (node.fk_del_action && node.fk_del_action !== 'a') {
|
|
2248
|
-
|
|
2374
|
+
let deleteClause = 'ON DELETE ';
|
|
2249
2375
|
switch (node.fk_del_action) {
|
|
2250
2376
|
case 'r':
|
|
2251
|
-
|
|
2377
|
+
deleteClause += 'RESTRICT';
|
|
2252
2378
|
break;
|
|
2253
2379
|
case 'c':
|
|
2254
|
-
|
|
2380
|
+
deleteClause += 'CASCADE';
|
|
2255
2381
|
break;
|
|
2256
2382
|
case 'n':
|
|
2257
|
-
|
|
2383
|
+
deleteClause += 'SET NULL';
|
|
2258
2384
|
break;
|
|
2259
2385
|
case 'd':
|
|
2260
|
-
|
|
2386
|
+
deleteClause += 'SET DEFAULT';
|
|
2261
2387
|
break;
|
|
2262
2388
|
}
|
|
2389
|
+
if (this.formatter.isPretty()) {
|
|
2390
|
+
output.push('\n' + this.formatter.indent(deleteClause));
|
|
2391
|
+
}
|
|
2392
|
+
else {
|
|
2393
|
+
output.push('ON DELETE');
|
|
2394
|
+
output.push(deleteClause.replace('ON DELETE ', ''));
|
|
2395
|
+
}
|
|
2263
2396
|
}
|
|
2264
2397
|
// Handle NOT VALID for foreign key constraints - only for table constraints, not domain constraints
|
|
2265
2398
|
if (node.skip_validation && !context.isDomainConstraint) {
|
|
@@ -2317,17 +2450,48 @@ class Deparser {
|
|
|
2317
2450
|
// Handle deferrable constraints for all constraint types that support it
|
|
2318
2451
|
if (node.contype === 'CONSTR_PRIMARY' || node.contype === 'CONSTR_UNIQUE' || node.contype === 'CONSTR_FOREIGN') {
|
|
2319
2452
|
if (node.deferrable) {
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2453
|
+
if (this.formatter.isPretty() && node.contype === 'CONSTR_FOREIGN') {
|
|
2454
|
+
output.push('\n' + this.formatter.indent('DEFERRABLE'));
|
|
2455
|
+
if (node.initdeferred === true) {
|
|
2456
|
+
output.push('\n' + this.formatter.indent('INITIALLY DEFERRED'));
|
|
2457
|
+
}
|
|
2458
|
+
else if (node.initdeferred === false) {
|
|
2459
|
+
output.push('\n' + this.formatter.indent('INITIALLY IMMEDIATE'));
|
|
2460
|
+
}
|
|
2323
2461
|
}
|
|
2324
|
-
else
|
|
2325
|
-
output.push('
|
|
2462
|
+
else {
|
|
2463
|
+
output.push('DEFERRABLE');
|
|
2464
|
+
if (node.initdeferred === true) {
|
|
2465
|
+
output.push('INITIALLY DEFERRED');
|
|
2466
|
+
}
|
|
2467
|
+
else if (node.initdeferred === false) {
|
|
2468
|
+
output.push('INITIALLY IMMEDIATE');
|
|
2469
|
+
}
|
|
2326
2470
|
}
|
|
2327
2471
|
}
|
|
2328
2472
|
else if (node.deferrable === false) {
|
|
2329
|
-
|
|
2473
|
+
if (this.formatter.isPretty() && node.contype === 'CONSTR_FOREIGN') {
|
|
2474
|
+
output.push('\n' + this.formatter.indent('NOT DEFERRABLE'));
|
|
2475
|
+
}
|
|
2476
|
+
else {
|
|
2477
|
+
output.push('NOT DEFERRABLE');
|
|
2478
|
+
}
|
|
2479
|
+
}
|
|
2480
|
+
}
|
|
2481
|
+
if (this.formatter.isPretty() && node.contype === 'CONSTR_FOREIGN') {
|
|
2482
|
+
let result = '';
|
|
2483
|
+
for (let i = 0; i < output.length; i++) {
|
|
2484
|
+
if (output[i].startsWith('\n')) {
|
|
2485
|
+
result += output[i];
|
|
2486
|
+
}
|
|
2487
|
+
else {
|
|
2488
|
+
if (i > 0 && !output[i - 1].startsWith('\n')) {
|
|
2489
|
+
result += ' ';
|
|
2490
|
+
}
|
|
2491
|
+
result += output[i];
|
|
2492
|
+
}
|
|
2330
2493
|
}
|
|
2494
|
+
return result;
|
|
2331
2495
|
}
|
|
2332
2496
|
return output.join(' ');
|
|
2333
2497
|
}
|
|
@@ -3051,11 +3215,9 @@ class Deparser {
|
|
|
3051
3215
|
}
|
|
3052
3216
|
switch (node.jointype) {
|
|
3053
3217
|
case 'JOIN_INNER':
|
|
3054
|
-
// Handle NATURAL JOIN first - it has isNatural=true (NATURAL already added above)
|
|
3055
3218
|
if (node.isNatural) {
|
|
3056
3219
|
joinStr += 'JOIN';
|
|
3057
3220
|
}
|
|
3058
|
-
// Handle CROSS JOIN case - when there's no quals, no usingClause, and not natural
|
|
3059
3221
|
else if (!node.quals && (!node.usingClause || node.usingClause.length === 0)) {
|
|
3060
3222
|
joinStr += 'CROSS JOIN';
|
|
3061
3223
|
}
|
|
@@ -3075,26 +3237,51 @@ class Deparser {
|
|
|
3075
3237
|
default:
|
|
3076
3238
|
joinStr += 'JOIN';
|
|
3077
3239
|
}
|
|
3078
|
-
output.push(joinStr);
|
|
3079
3240
|
if (node.rarg) {
|
|
3080
3241
|
let rargStr = this.visit(node.rarg, context);
|
|
3081
3242
|
if (node.rarg && 'JoinExpr' in node.rarg && !node.rarg.JoinExpr.alias) {
|
|
3082
3243
|
rargStr = `(${rargStr})`;
|
|
3083
3244
|
}
|
|
3084
|
-
|
|
3245
|
+
if (this.formatter.isPretty()) {
|
|
3246
|
+
output.push(this.formatter.newline() + joinStr + ' ' + rargStr);
|
|
3247
|
+
}
|
|
3248
|
+
else {
|
|
3249
|
+
output.push(joinStr + ' ' + rargStr);
|
|
3250
|
+
}
|
|
3251
|
+
}
|
|
3252
|
+
else {
|
|
3253
|
+
if (this.formatter.isPretty()) {
|
|
3254
|
+
output.push(this.formatter.newline() + joinStr);
|
|
3255
|
+
}
|
|
3256
|
+
else {
|
|
3257
|
+
output.push(joinStr);
|
|
3258
|
+
}
|
|
3085
3259
|
}
|
|
3086
3260
|
if (node.usingClause && node.usingClause.length > 0) {
|
|
3087
|
-
output.push('USING');
|
|
3088
3261
|
const usingList = list_utils_1.ListUtils.unwrapList(node.usingClause);
|
|
3089
3262
|
const columnNames = usingList.map(col => this.visit(col, context));
|
|
3090
|
-
|
|
3263
|
+
if (this.formatter.isPretty()) {
|
|
3264
|
+
output.push(` USING (${columnNames.join(', ')})`);
|
|
3265
|
+
}
|
|
3266
|
+
else {
|
|
3267
|
+
output.push(`USING (${columnNames.join(', ')})`);
|
|
3268
|
+
}
|
|
3091
3269
|
}
|
|
3092
3270
|
else if (node.quals) {
|
|
3093
|
-
|
|
3094
|
-
|
|
3271
|
+
if (this.formatter.isPretty()) {
|
|
3272
|
+
output.push(` ON ${this.visit(node.quals, context)}`);
|
|
3273
|
+
}
|
|
3274
|
+
else {
|
|
3275
|
+
output.push(`ON ${this.visit(node.quals, context)}`);
|
|
3276
|
+
}
|
|
3277
|
+
}
|
|
3278
|
+
let result;
|
|
3279
|
+
if (this.formatter.isPretty()) {
|
|
3280
|
+
result = output.join('');
|
|
3281
|
+
}
|
|
3282
|
+
else {
|
|
3283
|
+
result = output.join(' ');
|
|
3095
3284
|
}
|
|
3096
|
-
let result = output.join(' ');
|
|
3097
|
-
// Handle join_using_alias first (for USING clause aliases like "AS x")
|
|
3098
3285
|
if (node.join_using_alias && node.join_using_alias.aliasname) {
|
|
3099
3286
|
let aliasStr = node.join_using_alias.aliasname;
|
|
3100
3287
|
if (node.join_using_alias.colnames && node.join_using_alias.colnames.length > 0) {
|
|
@@ -3104,7 +3291,6 @@ class Deparser {
|
|
|
3104
3291
|
}
|
|
3105
3292
|
result += ` AS ${aliasStr}`;
|
|
3106
3293
|
}
|
|
3107
|
-
// Handle regular alias (for outer table aliases like "y")
|
|
3108
3294
|
if (node.alias && node.alias.aliasname) {
|
|
3109
3295
|
let aliasStr = node.alias.aliasname;
|
|
3110
3296
|
if (node.alias.colnames && node.alias.colnames.length > 0) {
|
|
@@ -5823,38 +6009,82 @@ class Deparser {
|
|
|
5823
6009
|
return output.join(' ');
|
|
5824
6010
|
}
|
|
5825
6011
|
CreatePolicyStmt(node, context) {
|
|
5826
|
-
const output = [
|
|
6012
|
+
const output = [];
|
|
6013
|
+
const initialParts = ['CREATE', 'POLICY'];
|
|
5827
6014
|
if (node.policy_name) {
|
|
5828
|
-
|
|
6015
|
+
initialParts.push(`"${node.policy_name}"`);
|
|
5829
6016
|
}
|
|
5830
|
-
output.push('
|
|
6017
|
+
output.push(initialParts.join(' '));
|
|
6018
|
+
// Add ON clause on new line in pretty mode
|
|
5831
6019
|
if (node.table) {
|
|
5832
|
-
|
|
6020
|
+
if (this.formatter.isPretty()) {
|
|
6021
|
+
output.push(this.formatter.newline() + this.formatter.indent(`ON ${this.RangeVar(node.table, context)}`));
|
|
6022
|
+
}
|
|
6023
|
+
else {
|
|
6024
|
+
output.push('ON');
|
|
6025
|
+
output.push(this.RangeVar(node.table, context));
|
|
6026
|
+
}
|
|
5833
6027
|
}
|
|
5834
6028
|
// Handle AS RESTRICTIVE/PERMISSIVE clause
|
|
5835
6029
|
if (node.permissive === undefined) {
|
|
5836
|
-
|
|
6030
|
+
if (this.formatter.isPretty()) {
|
|
6031
|
+
output.push(this.formatter.newline() + this.formatter.indent('AS RESTRICTIVE'));
|
|
6032
|
+
}
|
|
6033
|
+
else {
|
|
6034
|
+
output.push('AS', 'RESTRICTIVE');
|
|
6035
|
+
}
|
|
5837
6036
|
}
|
|
5838
6037
|
else if (node.permissive === true) {
|
|
5839
|
-
|
|
6038
|
+
if (this.formatter.isPretty()) {
|
|
6039
|
+
output.push(this.formatter.newline() + this.formatter.indent('AS PERMISSIVE'));
|
|
6040
|
+
}
|
|
6041
|
+
else {
|
|
6042
|
+
output.push('AS', 'PERMISSIVE');
|
|
6043
|
+
}
|
|
5840
6044
|
}
|
|
5841
6045
|
if (node.cmd_name) {
|
|
5842
|
-
|
|
6046
|
+
if (this.formatter.isPretty()) {
|
|
6047
|
+
output.push(this.formatter.newline() + this.formatter.indent(`FOR ${node.cmd_name.toUpperCase()}`));
|
|
6048
|
+
}
|
|
6049
|
+
else {
|
|
6050
|
+
output.push('FOR', node.cmd_name.toUpperCase());
|
|
6051
|
+
}
|
|
5843
6052
|
}
|
|
5844
6053
|
if (node.roles && node.roles.length > 0) {
|
|
5845
|
-
output.push('TO');
|
|
5846
6054
|
const roles = list_utils_1.ListUtils.unwrapList(node.roles).map(role => this.visit(role, context));
|
|
5847
|
-
|
|
6055
|
+
if (this.formatter.isPretty()) {
|
|
6056
|
+
output.push(this.formatter.newline() + this.formatter.indent(`TO ${roles.join(', ')}`));
|
|
6057
|
+
}
|
|
6058
|
+
else {
|
|
6059
|
+
output.push('TO');
|
|
6060
|
+
output.push(roles.join(', '));
|
|
6061
|
+
}
|
|
5848
6062
|
}
|
|
5849
6063
|
if (node.qual) {
|
|
5850
|
-
|
|
5851
|
-
|
|
6064
|
+
if (this.formatter.isPretty()) {
|
|
6065
|
+
const qualExpr = this.visit(node.qual, context);
|
|
6066
|
+
output.push(this.formatter.newline() + this.formatter.indent('USING ('));
|
|
6067
|
+
output.push(this.formatter.newline() + this.formatter.indent(this.formatter.indent(qualExpr)));
|
|
6068
|
+
output.push(this.formatter.newline() + this.formatter.indent(')'));
|
|
6069
|
+
}
|
|
6070
|
+
else {
|
|
6071
|
+
output.push('USING');
|
|
6072
|
+
output.push(`(${this.visit(node.qual, context)})`);
|
|
6073
|
+
}
|
|
5852
6074
|
}
|
|
5853
6075
|
if (node.with_check) {
|
|
5854
|
-
|
|
5855
|
-
|
|
6076
|
+
if (this.formatter.isPretty()) {
|
|
6077
|
+
const checkExpr = this.visit(node.with_check, context);
|
|
6078
|
+
output.push(this.formatter.newline() + this.formatter.indent('WITH CHECK ('));
|
|
6079
|
+
output.push(this.formatter.newline() + this.formatter.indent(this.formatter.indent(checkExpr)));
|
|
6080
|
+
output.push(this.formatter.newline() + this.formatter.indent(')'));
|
|
6081
|
+
}
|
|
6082
|
+
else {
|
|
6083
|
+
output.push('WITH CHECK');
|
|
6084
|
+
output.push(`(${this.visit(node.with_check, context)})`);
|
|
6085
|
+
}
|
|
5856
6086
|
}
|
|
5857
|
-
return output.join(' ');
|
|
6087
|
+
return this.formatter.isPretty() ? output.join('') : output.join(' ');
|
|
5858
6088
|
}
|
|
5859
6089
|
AlterPolicyStmt(node, context) {
|
|
5860
6090
|
const output = ['ALTER', 'POLICY'];
|
|
@@ -9720,5 +9950,9 @@ class Deparser {
|
|
|
9720
9950
|
}
|
|
9721
9951
|
return output.join(' ');
|
|
9722
9952
|
}
|
|
9953
|
+
containsMultilineStringLiteral(content) {
|
|
9954
|
+
const stringLiteralRegex = /'[^']*\n[^']*'/g;
|
|
9955
|
+
return stringLiteralRegex.test(content);
|
|
9956
|
+
}
|
|
9723
9957
|
}
|
|
9724
9958
|
exports.Deparser = Deparser;
|