pgsql-deparser 15.1.0 → 15.2.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 +23 -16
- package/deparser/deparser.d.ts +37 -28
- package/deparser/deparser.js +1413 -948
- package/deparser/index.d.ts +3 -1
- package/deparser/index.js +5 -1
- package/deparser/utils/sql-formatter.d.ts +8 -0
- package/deparser/utils/sql-formatter.js +104 -1
- package/esm/deparser/deparser.js +1396 -931
- package/esm/deparser/index.js +3 -1
- package/esm/deparser/utils/sql-formatter.js +103 -1
- package/esm/index.js +63 -7
- package/esm/v15-to-v16.js +16 -5
- package/esm/v15-to-v18-direct.js +66 -0
- package/esm/v16-to-v17.js +50 -1
- package/esm/v17-to-v18.js +209 -0
- package/index.d.ts +13 -4
- package/index.js +66 -6
- package/package.json +7 -6
- package/v15-to-v16.js +16 -5
- package/v15-to-v18-direct.d.ts +23 -0
- package/v15-to-v18-direct.js +70 -0
- package/v16-to-v17.d.ts +3 -0
- package/v16-to-v17.js +50 -1
- package/v17-to-v18.d.ts +55 -0
- package/v17-to-v18.js +213 -0
package/esm/deparser/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Auto-generated file with types stripped for better tree-shaking
|
|
3
3
|
* DO NOT EDIT - Generated by strip-deparser-types.ts
|
|
4
4
|
*/
|
|
5
|
-
import { Deparser } from
|
|
5
|
+
import { Deparser } from './deparser';
|
|
6
6
|
const deparseMethod = Deparser.deparse;
|
|
7
7
|
// Export the original sync version as deparseSync
|
|
8
8
|
export const deparseSync = deparseMethod;
|
|
@@ -11,3 +11,5 @@ export const deparse = async (...args) => {
|
|
|
11
11
|
return deparseMethod(...args);
|
|
12
12
|
};
|
|
13
13
|
export { Deparser };
|
|
14
|
+
export { splitSqlLines } from './utils/sql-formatter';
|
|
15
|
+
export { QuoteUtils } from '@pgsql/quotes';
|
|
@@ -2,6 +2,108 @@
|
|
|
2
2
|
* Auto-generated file with types stripped for better tree-shaking
|
|
3
3
|
* DO NOT EDIT - Generated by strip-deparser-types.ts
|
|
4
4
|
*/
|
|
5
|
+
const isIdentChar = (ch) => ch !== undefined && (/[A-Za-z0-9_$]/.test(ch) || ch.charCodeAt(0) >= 0x80);
|
|
6
|
+
const DOLLAR_TAG = /\$(?:[A-Za-z_\u0080-\uffff][A-Za-z0-9_\u0080-\uffff]*)?\$/y;
|
|
7
|
+
/**
|
|
8
|
+
* Splits SQL text into lines, breaking only at newlines that sit between
|
|
9
|
+
* tokens. A newline inside a string literal ('...', E'...', $tag$...$tag$),
|
|
10
|
+
* a quoted identifier or a block comment is part of that token's value and
|
|
11
|
+
* stays inside its line, so callers can re-indent the lines without changing
|
|
12
|
+
* what the SQL means.
|
|
13
|
+
*/
|
|
14
|
+
export function splitSqlLines(text, newlineChar = '\n') {
|
|
15
|
+
if (!newlineChar || !text.includes(newlineChar)) {
|
|
16
|
+
return [text];
|
|
17
|
+
}
|
|
18
|
+
const lines = [];
|
|
19
|
+
const length = text.length;
|
|
20
|
+
let lineStart = 0;
|
|
21
|
+
let i = 0;
|
|
22
|
+
while (i < length) {
|
|
23
|
+
const ch = text[i];
|
|
24
|
+
if (ch === "'") {
|
|
25
|
+
const prev = text[i - 1];
|
|
26
|
+
const backslashEscapes = (prev === 'E' || prev === 'e') && !isIdentChar(text[i - 2]);
|
|
27
|
+
i++;
|
|
28
|
+
while (i < length) {
|
|
29
|
+
if (backslashEscapes && text[i] === '\\') {
|
|
30
|
+
i += 2;
|
|
31
|
+
}
|
|
32
|
+
else if (text[i] === "'") {
|
|
33
|
+
if (text[i + 1] === "'") {
|
|
34
|
+
i += 2;
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
i++;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
i++;
|
|
45
|
+
}
|
|
46
|
+
else if (ch === '"') {
|
|
47
|
+
i++;
|
|
48
|
+
while (i < length) {
|
|
49
|
+
if (text[i] === '"') {
|
|
50
|
+
if (text[i + 1] === '"') {
|
|
51
|
+
i += 2;
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
i++;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
i++;
|
|
62
|
+
}
|
|
63
|
+
else if (ch === '$' && !isIdentChar(text[i - 1])) {
|
|
64
|
+
DOLLAR_TAG.lastIndex = i;
|
|
65
|
+
const tag = DOLLAR_TAG.exec(text);
|
|
66
|
+
if (tag) {
|
|
67
|
+
const close = text.indexOf(tag[0], i + tag[0].length);
|
|
68
|
+
i = close === -1 ? length : close + tag[0].length;
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
i++;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
else if (ch === '-' && text[i + 1] === '-') {
|
|
75
|
+
const end = text.indexOf('\n', i);
|
|
76
|
+
i = end === -1 ? length : end;
|
|
77
|
+
}
|
|
78
|
+
else if (ch === '/' && text[i + 1] === '*') {
|
|
79
|
+
let depth = 1;
|
|
80
|
+
i += 2;
|
|
81
|
+
while (i < length && depth > 0) {
|
|
82
|
+
if (text[i] === '/' && text[i + 1] === '*') {
|
|
83
|
+
depth++;
|
|
84
|
+
i += 2;
|
|
85
|
+
}
|
|
86
|
+
else if (text[i] === '*' && text[i + 1] === '/') {
|
|
87
|
+
depth--;
|
|
88
|
+
i += 2;
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
i++;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
else if (text.startsWith(newlineChar, i)) {
|
|
96
|
+
lines.push(text.slice(lineStart, i));
|
|
97
|
+
i += newlineChar.length;
|
|
98
|
+
lineStart = i;
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
i++;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
lines.push(text.slice(lineStart));
|
|
105
|
+
return lines;
|
|
106
|
+
}
|
|
5
107
|
export class SqlFormatter {
|
|
6
108
|
newlineChar;
|
|
7
109
|
tabChar;
|
|
@@ -19,7 +121,7 @@ export class SqlFormatter {
|
|
|
19
121
|
return text;
|
|
20
122
|
}
|
|
21
123
|
const indentation = this.tabChar.repeat(count);
|
|
22
|
-
return text
|
|
124
|
+
return splitSqlLines(text, this.newlineChar).map(line => line.trim() ? indentation + line : line).join(this.newlineChar);
|
|
23
125
|
}
|
|
24
126
|
parens(content) {
|
|
25
127
|
return `(${content})`;
|
package/esm/index.js
CHANGED
|
@@ -2,14 +2,70 @@
|
|
|
2
2
|
* Deparser for PostgreSQL version 15
|
|
3
3
|
* Auto-generated by generate-version-deparsers.ts
|
|
4
4
|
*/
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
const tx = new
|
|
5
|
+
import { Deparser as Deparser18 } from './deparser';
|
|
6
|
+
import { PG15ToPG18Transformer } from './v15-to-v18-direct';
|
|
7
|
+
const tx = new PG15ToPG18Transformer();
|
|
8
|
+
const bare = (names, name) => Array.isArray(names) && names.length === 1 && (names[0]?.String?.sval ?? names[0]?.String?.str) === name;
|
|
9
|
+
const v16to17 = tx.v16to17;
|
|
10
|
+
const upgradeTypeName = v16to17.TypeName.bind(v16to17);
|
|
11
|
+
v16to17.TypeName = (node, context) => {
|
|
12
|
+
const out = upgradeTypeName(node, context);
|
|
13
|
+
if (bare(node.names, 'json'))
|
|
14
|
+
out.TypeName.names = [{ String: { sval: 'json' } }];
|
|
15
|
+
return out;
|
|
16
|
+
};
|
|
17
|
+
const upgradeTypeCast = v16to17.TypeCast.bind(v16to17);
|
|
18
|
+
v16to17.TypeCast = (node, context) => {
|
|
19
|
+
const out = upgradeTypeCast(node, context);
|
|
20
|
+
const typeName = out.TypeCast.typeName?.TypeName ?? out.TypeCast.typeName;
|
|
21
|
+
if (typeName && bare((node.typeName?.TypeName ?? node.typeName)?.names, 'json')) {
|
|
22
|
+
typeName.names = [{ String: { sval: 'json' } }];
|
|
23
|
+
}
|
|
24
|
+
return out;
|
|
25
|
+
};
|
|
26
|
+
const v15to16 = tx.v15to16;
|
|
27
|
+
const upgradeFuncCall = v15to16.FuncCall.bind(v15to16);
|
|
28
|
+
v15to16.FuncCall = (node, context) => {
|
|
29
|
+
const out = upgradeFuncCall(node, context);
|
|
30
|
+
if (out.FuncCall && bare(node.funcname, 'json_object')) {
|
|
31
|
+
out.FuncCall.funcname = [{ String: { sval: 'json_object' } }];
|
|
32
|
+
}
|
|
33
|
+
return out;
|
|
34
|
+
};
|
|
35
|
+
const toAst18 = (query) => Array.isArray(query) ? query.map(node => tx.transform(node)) : tx.transform(query);
|
|
36
|
+
export class Deparser extends Deparser18 {
|
|
37
|
+
constructor(tree, opts = {}) {
|
|
38
|
+
super(toAst18(tree), opts);
|
|
39
|
+
}
|
|
40
|
+
static deparse(query, opts = {}) {
|
|
41
|
+
return new Deparser(query, opts).deparseQuery();
|
|
42
|
+
}
|
|
43
|
+
DefElem(node, context) {
|
|
44
|
+
if (!node.arg && node.defaction !== 'DEFELEM_DROP' && context.parentNodeTypes.includes('AlterOperatorStmt')) {
|
|
45
|
+
return `${node.defname} = NONE`;
|
|
46
|
+
}
|
|
47
|
+
return super.DefElem(node, context);
|
|
48
|
+
}
|
|
49
|
+
GrantRoleStmt(node, context) {
|
|
50
|
+
const sql = super.GrantRoleStmt(node, context);
|
|
51
|
+
const opts = (Array.isArray(node.opt) ? node.opt : []).map((opt) => opt.DefElem);
|
|
52
|
+
const arg = opts[0]?.arg;
|
|
53
|
+
const off = arg?.Boolean ? arg.Boolean.boolval !== true : arg?.String?.sval === 'false';
|
|
54
|
+
if (!node.is_grant || opts.length !== 1 || opts[0]?.defname !== 'admin' || off)
|
|
55
|
+
return sql;
|
|
56
|
+
// locate the option by what the statement prints without GRANTED BY, so a role name can never match
|
|
57
|
+
const head = node.grantor ? super.GrantRoleStmt({ ...node, grantor: undefined }, context) : sql;
|
|
58
|
+
const option = ' WITH ADMIN TRUE';
|
|
59
|
+
if (!head.endsWith(option) || !sql.startsWith(head))
|
|
60
|
+
return sql;
|
|
61
|
+
return head.slice(0, -option.length) + ' WITH ADMIN OPTION' + sql.slice(head.length);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
8
64
|
export async function deparse(query, opts) {
|
|
9
|
-
|
|
10
|
-
return await deparse17(ast17, opts);
|
|
65
|
+
return Deparser.deparse(query, opts);
|
|
11
66
|
}
|
|
12
67
|
export function deparseSync(query, opts) {
|
|
13
|
-
|
|
14
|
-
return deparseSync17(ast17, opts);
|
|
68
|
+
return Deparser.deparse(query, opts);
|
|
15
69
|
}
|
|
70
|
+
export { splitSqlLines } from './deparser/utils/sql-formatter';
|
|
71
|
+
export { QuoteUtils } from '@pgsql/quotes';
|
package/esm/v15-to-v16.js
CHANGED
|
@@ -790,9 +790,9 @@ export class V15ToV16Transformer {
|
|
|
790
790
|
const partspec = { ...node.partspec };
|
|
791
791
|
if (partspec.strategy !== undefined) {
|
|
792
792
|
const strategyMap = {
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
793
|
+
range: 'PARTITION_STRATEGY_RANGE',
|
|
794
|
+
list: 'PARTITION_STRATEGY_LIST',
|
|
795
|
+
hash: 'PARTITION_STRATEGY_HASH'
|
|
796
796
|
};
|
|
797
797
|
partspec.strategy = strategyMap[partspec.strategy] || partspec.strategy;
|
|
798
798
|
}
|
|
@@ -854,6 +854,9 @@ export class V15ToV16Transformer {
|
|
|
854
854
|
if (node.storage !== undefined) {
|
|
855
855
|
result.storage = node.storage;
|
|
856
856
|
}
|
|
857
|
+
if (node.compression !== undefined) {
|
|
858
|
+
result.compression = node.compression;
|
|
859
|
+
}
|
|
857
860
|
if (node.raw_default !== undefined) {
|
|
858
861
|
result.raw_default = this.transform(node.raw_default, context);
|
|
859
862
|
}
|
|
@@ -976,6 +979,11 @@ export class V15ToV16Transformer {
|
|
|
976
979
|
if (node.fk_del_action !== undefined) {
|
|
977
980
|
result.fk_del_action = node.fk_del_action;
|
|
978
981
|
}
|
|
982
|
+
if (node.fk_del_set_cols !== undefined) {
|
|
983
|
+
result.fk_del_set_cols = Array.isArray(node.fk_del_set_cols)
|
|
984
|
+
? node.fk_del_set_cols.map((item) => this.transform(item, context))
|
|
985
|
+
: this.transform(node.fk_del_set_cols, context);
|
|
986
|
+
}
|
|
979
987
|
if (node.old_conpfeqop !== undefined) {
|
|
980
988
|
result.old_conpfeqop = Array.isArray(node.old_conpfeqop)
|
|
981
989
|
? node.old_conpfeqop.map((item) => this.transform(item, context))
|
|
@@ -2551,13 +2559,13 @@ export class V15ToV16Transformer {
|
|
|
2551
2559
|
result.opt = [
|
|
2552
2560
|
{
|
|
2553
2561
|
DefElem: {
|
|
2554
|
-
defname:
|
|
2562
|
+
defname: 'admin',
|
|
2555
2563
|
arg: {
|
|
2556
2564
|
Boolean: {
|
|
2557
2565
|
boolval: true
|
|
2558
2566
|
}
|
|
2559
2567
|
},
|
|
2560
|
-
defaction:
|
|
2568
|
+
defaction: 'DEFELEM_UNSPEC'
|
|
2561
2569
|
}
|
|
2562
2570
|
}
|
|
2563
2571
|
];
|
|
@@ -2568,6 +2576,9 @@ export class V15ToV16Transformer {
|
|
|
2568
2576
|
? nodeAny.opt.map((item) => this.transform(item, context))
|
|
2569
2577
|
: this.transform(nodeAny.opt, context);
|
|
2570
2578
|
}
|
|
2579
|
+
if (node.grantor !== undefined) {
|
|
2580
|
+
result.grantor = node.grantor;
|
|
2581
|
+
}
|
|
2571
2582
|
return { GrantRoleStmt: result };
|
|
2572
2583
|
}
|
|
2573
2584
|
SecLabelStmt(node, context) {
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-generated file with types stripped for better tree-shaking
|
|
3
|
+
* DO NOT EDIT - Generated by strip-direct-transformer-types.ts
|
|
4
|
+
*/
|
|
5
|
+
import { V15ToV16Transformer } from "./v15-to-v16";
|
|
6
|
+
import { V16ToV17Transformer } from "./v16-to-v17";
|
|
7
|
+
import { V17ToV18Transformer } from "./v17-to-v18";
|
|
8
|
+
/**
|
|
9
|
+
* Direct transformer from PG15 to PG18
|
|
10
|
+
* This transformer chains v15->...->v18 transformations
|
|
11
|
+
*/
|
|
12
|
+
export class PG15ToPG18Transformer {
|
|
13
|
+
v15to16 = new V15ToV16Transformer();
|
|
14
|
+
v16to17 = new V16ToV17Transformer();
|
|
15
|
+
v17to18 = new V17ToV18Transformer();
|
|
16
|
+
transform(node) {
|
|
17
|
+
if (this.isParseResult(node)) {
|
|
18
|
+
// Transform through the chain: v15->v16->v17->v18
|
|
19
|
+
const v16Stmts = node.stmts.map((stmtWrapper) => {
|
|
20
|
+
if (stmtWrapper.stmt) {
|
|
21
|
+
const stmt = this.v15to16.transform(stmtWrapper.stmt, { parentNodeTypes: [] });
|
|
22
|
+
return { ...stmtWrapper, stmt };
|
|
23
|
+
}
|
|
24
|
+
return stmtWrapper;
|
|
25
|
+
});
|
|
26
|
+
const v17Stmts = v16Stmts.map((stmtWrapper) => {
|
|
27
|
+
if (stmtWrapper.stmt) {
|
|
28
|
+
const stmt = this.v16to17.transform(stmtWrapper.stmt, { parentNodeTypes: [] });
|
|
29
|
+
return { ...stmtWrapper, stmt };
|
|
30
|
+
}
|
|
31
|
+
return stmtWrapper;
|
|
32
|
+
});
|
|
33
|
+
const v18Stmts = v17Stmts.map((stmtWrapper) => {
|
|
34
|
+
if (stmtWrapper.stmt) {
|
|
35
|
+
const stmt = this.v17to18.transform(stmtWrapper.stmt, { parentNodeTypes: [] });
|
|
36
|
+
return { ...stmtWrapper, stmt };
|
|
37
|
+
}
|
|
38
|
+
return stmtWrapper;
|
|
39
|
+
});
|
|
40
|
+
return {
|
|
41
|
+
...node,
|
|
42
|
+
version: 180004, // PG18 version
|
|
43
|
+
stmts: v18Stmts
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
// Otherwise, transform as a regular node through the chain
|
|
47
|
+
const v16Node = this.v15to16.transform(node, { parentNodeTypes: [] });
|
|
48
|
+
const v17Node = this.v16to17.transform(v16Node, { parentNodeTypes: [] });
|
|
49
|
+
return this.v17to18.transform(v17Node, { parentNodeTypes: [] });
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Transform a single statement from PG15 to PG18
|
|
53
|
+
* @deprecated Use transform() instead, which handles all node types
|
|
54
|
+
*/
|
|
55
|
+
transformStatement(stmt) {
|
|
56
|
+
const v16Stmt = this.v15to16.transform(stmt, { parentNodeTypes: [] });
|
|
57
|
+
const v17Stmt = this.v16to17.transform(v16Stmt, { parentNodeTypes: [] });
|
|
58
|
+
return this.v17to18.transform(v17Stmt, { parentNodeTypes: [] });
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Type guard to check if a node is a ParseResult
|
|
62
|
+
*/
|
|
63
|
+
isParseResult(node) {
|
|
64
|
+
return node && typeof node === 'object' && 'version' in node && 'stmts' in node;
|
|
65
|
+
}
|
|
66
|
+
}
|
package/esm/v16-to-v17.js
CHANGED
|
@@ -652,6 +652,12 @@ export class V16ToV17Transformer {
|
|
|
652
652
|
if (node.storage !== undefined) {
|
|
653
653
|
result.storage = node.storage;
|
|
654
654
|
}
|
|
655
|
+
if (node.storage_name !== undefined) {
|
|
656
|
+
result.storage_name = node.storage_name;
|
|
657
|
+
}
|
|
658
|
+
if (node.compression !== undefined) {
|
|
659
|
+
result.compression = node.compression;
|
|
660
|
+
}
|
|
655
661
|
if (node.raw_default !== undefined) {
|
|
656
662
|
result.raw_default = this.transform(node.raw_default, context);
|
|
657
663
|
}
|
|
@@ -1110,7 +1116,50 @@ export class V16ToV17Transformer {
|
|
|
1110
1116
|
return { AlterOpFamilyStmt: node };
|
|
1111
1117
|
}
|
|
1112
1118
|
MergeStmt(node, context) {
|
|
1113
|
-
|
|
1119
|
+
const result = {};
|
|
1120
|
+
if (node.relation !== undefined) {
|
|
1121
|
+
result.relation = node.relation;
|
|
1122
|
+
}
|
|
1123
|
+
if (node.sourceRelation !== undefined) {
|
|
1124
|
+
result.sourceRelation = this.transform(node.sourceRelation, context);
|
|
1125
|
+
}
|
|
1126
|
+
if (node.joinCondition !== undefined) {
|
|
1127
|
+
result.joinCondition = this.transform(node.joinCondition, context);
|
|
1128
|
+
}
|
|
1129
|
+
if (node.mergeWhenClauses !== undefined) {
|
|
1130
|
+
result.mergeWhenClauses = Array.isArray(node.mergeWhenClauses)
|
|
1131
|
+
? node.mergeWhenClauses.map(item => this.transform(item, context))
|
|
1132
|
+
: this.transform(node.mergeWhenClauses, context);
|
|
1133
|
+
}
|
|
1134
|
+
if (node.withClause !== undefined) {
|
|
1135
|
+
result.withClause = this.WithClause(node.withClause, context);
|
|
1136
|
+
}
|
|
1137
|
+
return { MergeStmt: result };
|
|
1138
|
+
}
|
|
1139
|
+
MergeWhenClause(node, context) {
|
|
1140
|
+
const result = {};
|
|
1141
|
+
// v17 replaces the boolean matched with matchKind
|
|
1142
|
+
result.matchKind = node.matched ? 'MERGE_WHEN_MATCHED' : 'MERGE_WHEN_NOT_MATCHED_BY_TARGET';
|
|
1143
|
+
if (node.commandType !== undefined) {
|
|
1144
|
+
result.commandType = node.commandType;
|
|
1145
|
+
}
|
|
1146
|
+
if (node.override !== undefined) {
|
|
1147
|
+
result.override = node.override;
|
|
1148
|
+
}
|
|
1149
|
+
if (node.condition !== undefined) {
|
|
1150
|
+
result.condition = this.transform(node.condition, context);
|
|
1151
|
+
}
|
|
1152
|
+
if (node.targetList !== undefined) {
|
|
1153
|
+
result.targetList = Array.isArray(node.targetList)
|
|
1154
|
+
? node.targetList.map(item => this.transform(item, context))
|
|
1155
|
+
: this.transform(node.targetList, context);
|
|
1156
|
+
}
|
|
1157
|
+
if (node.values !== undefined) {
|
|
1158
|
+
result.values = Array.isArray(node.values)
|
|
1159
|
+
? node.values.map(item => this.transform(item, context))
|
|
1160
|
+
: this.transform(node.values, context);
|
|
1161
|
+
}
|
|
1162
|
+
return { MergeWhenClause: result };
|
|
1114
1163
|
}
|
|
1115
1164
|
AlterTableMoveAllStmt(node, context) {
|
|
1116
1165
|
return { AlterTableMoveAllStmt: node };
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-generated file with types stripped for better tree-shaking
|
|
3
|
+
* DO NOT EDIT - Generated by strip-transformer-types.ts
|
|
4
|
+
*/
|
|
5
|
+
// @ts-nocheck
|
|
6
|
+
/**
|
|
7
|
+
* V17 to V18 AST Transformer
|
|
8
|
+
* Transforms PostgreSQL v17 AST nodes to v18 format
|
|
9
|
+
*
|
|
10
|
+
* Structural changes in v18:
|
|
11
|
+
* - InsertStmt/UpdateStmt/DeleteStmt/MergeStmt: returningList -> returningClause { exprs }
|
|
12
|
+
* - Constraint: new is_enforced flag (true by default for CHECK/FOREIGN/NOTNULL)
|
|
13
|
+
* - Constraint: CONSTR_GENERATED gains generated_kind ('s' = STORED, 'v' = VIRTUAL)
|
|
14
|
+
* - Column-level NOT NULL constraints gain initially_valid: true
|
|
15
|
+
*/
|
|
16
|
+
export class V17ToV18Transformer {
|
|
17
|
+
transform(node, context = { parentNodeTypes: [] }) {
|
|
18
|
+
if (node == null) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
if (typeof node === 'number' || node instanceof Number) {
|
|
22
|
+
return node;
|
|
23
|
+
}
|
|
24
|
+
if (typeof node === 'string') {
|
|
25
|
+
return node;
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
return this.visit(node, context);
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
const nodeType = Object.keys(node)[0];
|
|
32
|
+
throw new Error(`Error transforming ${nodeType}: ${error.message}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
visit(node, context = { parentNodeTypes: [] }) {
|
|
36
|
+
if (node && typeof node === 'object' && 'version' in node && 'stmts' in node) {
|
|
37
|
+
return this.ParseResult(node, context);
|
|
38
|
+
}
|
|
39
|
+
const nodeType = this.getNodeType(node);
|
|
40
|
+
// Handle empty objects
|
|
41
|
+
if (!nodeType) {
|
|
42
|
+
return {};
|
|
43
|
+
}
|
|
44
|
+
const nodeData = this.getNodeData(node);
|
|
45
|
+
const methodName = nodeType;
|
|
46
|
+
if (typeof this[methodName] === 'function') {
|
|
47
|
+
const childContext = {
|
|
48
|
+
...context,
|
|
49
|
+
parentNodeTypes: [...context.parentNodeTypes, nodeType]
|
|
50
|
+
};
|
|
51
|
+
const result = this[methodName](nodeData, childContext);
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
// No specific method: recurse into children so nested statements
|
|
55
|
+
// (e.g. DML inside CTEs) still get transformed
|
|
56
|
+
return this.walkValue(node, context);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Generic recursive walk that re-dispatches wrapped nodes with a
|
|
60
|
+
* dedicated transformer method and deep-copies everything else.
|
|
61
|
+
*/
|
|
62
|
+
walkValue(value, context) {
|
|
63
|
+
if (value == null || typeof value !== 'object') {
|
|
64
|
+
return value;
|
|
65
|
+
}
|
|
66
|
+
if (Array.isArray(value)) {
|
|
67
|
+
return value.map(item => this.walkValue(item, context));
|
|
68
|
+
}
|
|
69
|
+
const keys = Object.keys(value);
|
|
70
|
+
if (keys.length === 1 && typeof this[keys[0]] === 'function') {
|
|
71
|
+
// Wrapped node with a dedicated transformer method
|
|
72
|
+
return this.visit(value, context);
|
|
73
|
+
}
|
|
74
|
+
const result = {};
|
|
75
|
+
for (const key of keys) {
|
|
76
|
+
result[key] = this.walkValue(value[key], context);
|
|
77
|
+
}
|
|
78
|
+
return result;
|
|
79
|
+
}
|
|
80
|
+
getNodeType(node) {
|
|
81
|
+
return Object.keys(node)[0];
|
|
82
|
+
}
|
|
83
|
+
getNodeData(node) {
|
|
84
|
+
const keys = Object.keys(node);
|
|
85
|
+
if (keys.length === 1 && typeof node[keys[0]] === 'object') {
|
|
86
|
+
return node[keys[0]];
|
|
87
|
+
}
|
|
88
|
+
return node;
|
|
89
|
+
}
|
|
90
|
+
ParseResult(node, context) {
|
|
91
|
+
if (node && typeof node === 'object' && 'version' in node && 'stmts' in node) {
|
|
92
|
+
return {
|
|
93
|
+
version: 180004, // PG18 version
|
|
94
|
+
stmts: node.stmts.map((stmt) => {
|
|
95
|
+
if (stmt && typeof stmt === 'object' && 'stmt' in stmt) {
|
|
96
|
+
return {
|
|
97
|
+
...stmt,
|
|
98
|
+
stmt: this.transform(stmt.stmt, context)
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
return this.transform(stmt, context);
|
|
102
|
+
})
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
return node;
|
|
106
|
+
}
|
|
107
|
+
RawStmt(node, context) {
|
|
108
|
+
const result = {};
|
|
109
|
+
if (node.stmt !== undefined) {
|
|
110
|
+
result.stmt = this.transform(node.stmt, context);
|
|
111
|
+
}
|
|
112
|
+
if (node.stmt_location !== undefined) {
|
|
113
|
+
result.stmt_location = node.stmt_location;
|
|
114
|
+
}
|
|
115
|
+
if (node.stmt_len !== undefined) {
|
|
116
|
+
result.stmt_len = node.stmt_len;
|
|
117
|
+
}
|
|
118
|
+
return { RawStmt: result };
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Convert a v17 returningList into a v18 ReturningClause
|
|
122
|
+
*/
|
|
123
|
+
transformReturningList(returningList, context) {
|
|
124
|
+
return {
|
|
125
|
+
exprs: returningList.map(item => this.transform(item, context))
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
transformDmlStatement(nodeType, node, context) {
|
|
129
|
+
const result = {};
|
|
130
|
+
for (const key of Object.keys(node)) {
|
|
131
|
+
if (key === 'returningList') {
|
|
132
|
+
result.returningClause = this.transformReturningList(node.returningList, context);
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
result[key] = this.walkValue(node[key], context);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return { [nodeType]: result };
|
|
139
|
+
}
|
|
140
|
+
InsertStmt(node, context) {
|
|
141
|
+
return this.transformDmlStatement('InsertStmt', node, context);
|
|
142
|
+
}
|
|
143
|
+
UpdateStmt(node, context) {
|
|
144
|
+
return this.transformDmlStatement('UpdateStmt', node, context);
|
|
145
|
+
}
|
|
146
|
+
DeleteStmt(node, context) {
|
|
147
|
+
return this.transformDmlStatement('DeleteStmt', node, context);
|
|
148
|
+
}
|
|
149
|
+
MergeStmt(node, context) {
|
|
150
|
+
return this.transformDmlStatement('MergeStmt', node, context);
|
|
151
|
+
}
|
|
152
|
+
AlterTableCmd(node, context) {
|
|
153
|
+
const result = {};
|
|
154
|
+
for (const key of Object.keys(node)) {
|
|
155
|
+
result[key] = this.walkValue(node[key], context);
|
|
156
|
+
}
|
|
157
|
+
// v18 wraps ALTER CONSTRAINT definitions in the new ATAlterConstraint node
|
|
158
|
+
if (node.subtype === 'AT_AlterConstraint' && node.def?.Constraint) {
|
|
159
|
+
const constraint = node.def.Constraint;
|
|
160
|
+
const alterConstraint = {};
|
|
161
|
+
if (constraint.conname !== undefined) {
|
|
162
|
+
alterConstraint.conname = constraint.conname;
|
|
163
|
+
}
|
|
164
|
+
alterConstraint.is_enforced = true;
|
|
165
|
+
// v17 ALTER CONSTRAINT could only change deferrability
|
|
166
|
+
alterConstraint.alterDeferrability = true;
|
|
167
|
+
if (constraint.deferrable !== undefined) {
|
|
168
|
+
alterConstraint.deferrable = constraint.deferrable;
|
|
169
|
+
}
|
|
170
|
+
if (constraint.initdeferred !== undefined) {
|
|
171
|
+
alterConstraint.initdeferred = constraint.initdeferred;
|
|
172
|
+
}
|
|
173
|
+
result.def = { ATAlterConstraint: alterConstraint };
|
|
174
|
+
}
|
|
175
|
+
return { AlterTableCmd: result };
|
|
176
|
+
}
|
|
177
|
+
VariableSetStmt(node, context) {
|
|
178
|
+
const result = {};
|
|
179
|
+
for (const key of Object.keys(node)) {
|
|
180
|
+
result[key] = this.walkValue(node[key], context);
|
|
181
|
+
}
|
|
182
|
+
// v18 marks SET TRANSACTION / SESSION CHARACTERISTICS args as jumbled
|
|
183
|
+
if (node.kind === 'VAR_SET_MULTI' && (node.name === 'TRANSACTION' || node.name === 'SESSION CHARACTERISTICS')) {
|
|
184
|
+
result.jumble_args = true;
|
|
185
|
+
}
|
|
186
|
+
return { VariableSetStmt: result };
|
|
187
|
+
}
|
|
188
|
+
Constraint(node, context) {
|
|
189
|
+
const result = {};
|
|
190
|
+
for (const key of Object.keys(node)) {
|
|
191
|
+
result[key] = this.walkValue(node[key], context);
|
|
192
|
+
}
|
|
193
|
+
// v18 constraints are enforced by default; v17 grammar had no ENFORCED syntax
|
|
194
|
+
if (node.contype === 'CONSTR_CHECK' || node.contype === 'CONSTR_FOREIGN') {
|
|
195
|
+
result.is_enforced = true;
|
|
196
|
+
}
|
|
197
|
+
if (node.contype === 'CONSTR_NOTNULL') {
|
|
198
|
+
// v17 only produces column-level NOT NULL constraints, which in v18
|
|
199
|
+
// carry is_enforced and initially_valid
|
|
200
|
+
result.is_enforced = true;
|
|
201
|
+
result.initially_valid = true;
|
|
202
|
+
}
|
|
203
|
+
if (node.contype === 'CONSTR_GENERATED' && result.generated_kind === undefined) {
|
|
204
|
+
// v17 only supports STORED generated columns
|
|
205
|
+
result.generated_kind = 's';
|
|
206
|
+
}
|
|
207
|
+
return { Constraint: result };
|
|
208
|
+
}
|
|
209
|
+
}
|
package/index.d.ts
CHANGED
|
@@ -3,7 +3,16 @@
|
|
|
3
3
|
* Auto-generated by generate-version-deparsers.ts
|
|
4
4
|
*/
|
|
5
5
|
import { Node, ParseResult } from '@pgsql/types';
|
|
6
|
-
import { DeparserOptions } from './deparser';
|
|
7
|
-
|
|
8
|
-
export declare
|
|
9
|
-
|
|
6
|
+
import { Deparser as Deparser18, DeparserOptions } from './deparser';
|
|
7
|
+
type Query = Node | Node[] | ParseResult;
|
|
8
|
+
export declare class Deparser extends Deparser18 {
|
|
9
|
+
constructor(tree: Query, opts?: DeparserOptions);
|
|
10
|
+
static deparse(query: Query, opts?: DeparserOptions): string;
|
|
11
|
+
DefElem(node: any, context: any): string;
|
|
12
|
+
GrantRoleStmt(node: any, context: any): string;
|
|
13
|
+
}
|
|
14
|
+
export declare function deparse(query: Query, opts?: DeparserOptions): Promise<string>;
|
|
15
|
+
export declare function deparseSync(query: Query, opts?: DeparserOptions): string;
|
|
16
|
+
export { DeparserOptions };
|
|
17
|
+
export { splitSqlLines } from './deparser/utils/sql-formatter';
|
|
18
|
+
export { QuoteUtils } from '@pgsql/quotes';
|