pgsql-deparser 14.1.1 → 14.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.
@@ -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 "./deparser";
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.split(this.newlineChar).map(line => line.trim() ? indentation + line : line).join(this.newlineChar);
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,87 @@
2
2
  * Deparser for PostgreSQL version 14
3
3
  * Auto-generated by generate-version-deparsers.ts
4
4
  */
5
- import { deparse as deparse17, deparseSync as deparseSync17 } from './deparser';
6
- import { PG14ToPG17Transformer } from './v14-to-v17-direct';
7
- const tx = new PG14ToPG17Transformer();
5
+ import { Deparser as Deparser18 } from './deparser';
6
+ import { PG14ToPG18Transformer } from './v14-to-v18-direct';
7
+ const tx = new PG14ToPG18Transformer();
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
+ publicationTableListed = false;
64
+ CreatePublicationStmt(node, context) {
65
+ this.publicationTableListed = false;
66
+ return super.CreatePublicationStmt(node, context);
67
+ }
68
+ AlterPublicationStmt(node, context) {
69
+ this.publicationTableListed = false;
70
+ return super.AlterPublicationStmt(node, context);
71
+ }
72
+ PublicationObjSpec(node, context) {
73
+ const sql = super.PublicationObjSpec(node, context);
74
+ if (node.pubobjtype !== 'PUBLICATIONOBJ_TABLE')
75
+ return sql;
76
+ const first = !this.publicationTableListed;
77
+ this.publicationTableListed = true;
78
+ return first ? sql : sql.replace(/^TABLE /, '');
79
+ }
80
+ }
8
81
  export async function deparse(query, opts) {
9
- const ast17 = tx.transform(query);
10
- return await deparse17(ast17, opts);
82
+ return Deparser.deparse(query, opts);
11
83
  }
12
84
  export function deparseSync(query, opts) {
13
- const ast17 = tx.transform(query);
14
- return deparseSync17(ast17, opts);
85
+ return Deparser.deparse(query, opts);
15
86
  }
87
+ export { splitSqlLines } from './deparser/utils/sql-formatter';
88
+ export { QuoteUtils } from '@pgsql/quotes';
package/esm/v14-to-v15.js CHANGED
@@ -828,8 +828,21 @@ export class V14ToV15Transformer {
828
828
  const result = this.transformGenericNode(node, context);
829
829
  return { StatsElem: result };
830
830
  }
831
+ // PG15 replaced the publication's list of tables with a list of PublicationObjSpec
832
+ publicationTablesToObjects(result) {
833
+ if (result.tables === undefined)
834
+ return;
835
+ result.pubobjects = (Array.isArray(result.tables) ? result.tables : [result.tables]).map((table) => ({
836
+ PublicationObjSpec: {
837
+ pubobjtype: 'PUBLICATIONOBJ_TABLE',
838
+ pubtable: { relation: table.RangeVar ?? table }
839
+ }
840
+ }));
841
+ delete result.tables;
842
+ }
831
843
  CreatePublicationStmt(node, context) {
832
844
  const result = this.transformGenericNode(node, context);
845
+ this.publicationTablesToObjects(result);
833
846
  return { CreatePublicationStmt: result };
834
847
  }
835
848
  CreateSubscriptionStmt(node, context) {
@@ -838,10 +851,31 @@ export class V14ToV15Transformer {
838
851
  }
839
852
  AlterPublicationStmt(node, context) {
840
853
  const result = this.transformGenericNode(node, context);
854
+ this.publicationTablesToObjects(result);
855
+ if (result.tableAction !== undefined) {
856
+ const actions = {
857
+ DEFELEM_ADD: 'AP_AddObjects',
858
+ DEFELEM_DROP: 'AP_DropObjects',
859
+ DEFELEM_SET: 'AP_SetObjects'
860
+ };
861
+ if (actions[result.tableAction]) {
862
+ result.action = actions[result.tableAction];
863
+ }
864
+ delete result.tableAction;
865
+ }
841
866
  return { AlterPublicationStmt: result };
842
867
  }
843
868
  AlterSubscriptionStmt(node, context) {
844
869
  const result = this.transformGenericNode(node, context);
870
+ // ENABLE / DISABLE carried an Integer before PG15 introduced the Boolean node
871
+ if (result.kind === 'ALTER_SUBSCRIPTION_ENABLED' && Array.isArray(result.options)) {
872
+ result.options = result.options.map((option) => {
873
+ const ival = option?.DefElem?.arg?.Integer;
874
+ if (option?.DefElem?.defname !== 'enabled' || !ival)
875
+ return option;
876
+ return { DefElem: { ...option.DefElem, arg: { Boolean: { boolval: ival.ival === 1 } } } };
877
+ });
878
+ }
845
879
  return { AlterSubscriptionStmt: result };
846
880
  }
847
881
  DropSubscriptionStmt(node, context) {
@@ -0,0 +1,77 @@
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 { V14ToV15Transformer } from "./v14-to-v15";
6
+ import { V15ToV16Transformer } from "./v15-to-v16";
7
+ import { V16ToV17Transformer } from "./v16-to-v17";
8
+ import { V17ToV18Transformer } from "./v17-to-v18";
9
+ /**
10
+ * Direct transformer from PG14 to PG18
11
+ * This transformer chains v14->...->v18 transformations
12
+ */
13
+ export class PG14ToPG18Transformer {
14
+ v14to15 = new V14ToV15Transformer();
15
+ v15to16 = new V15ToV16Transformer();
16
+ v16to17 = new V16ToV17Transformer();
17
+ v17to18 = new V17ToV18Transformer();
18
+ transform(node) {
19
+ if (this.isParseResult(node)) {
20
+ // Transform through the chain: v14->v15->v16->v17->v18
21
+ const v15Stmts = node.stmts.map((stmtWrapper) => {
22
+ if (stmtWrapper.stmt) {
23
+ const stmt = this.v14to15.transform(stmtWrapper.stmt, { parentNodeTypes: [] });
24
+ return { ...stmtWrapper, stmt };
25
+ }
26
+ return stmtWrapper;
27
+ });
28
+ const v16Stmts = v15Stmts.map((stmtWrapper) => {
29
+ if (stmtWrapper.stmt) {
30
+ const stmt = this.v15to16.transform(stmtWrapper.stmt, { parentNodeTypes: [] });
31
+ return { ...stmtWrapper, stmt };
32
+ }
33
+ return stmtWrapper;
34
+ });
35
+ const v17Stmts = v16Stmts.map((stmtWrapper) => {
36
+ if (stmtWrapper.stmt) {
37
+ const stmt = this.v16to17.transform(stmtWrapper.stmt, { parentNodeTypes: [] });
38
+ return { ...stmtWrapper, stmt };
39
+ }
40
+ return stmtWrapper;
41
+ });
42
+ const v18Stmts = v17Stmts.map((stmtWrapper) => {
43
+ if (stmtWrapper.stmt) {
44
+ const stmt = this.v17to18.transform(stmtWrapper.stmt, { parentNodeTypes: [] });
45
+ return { ...stmtWrapper, stmt };
46
+ }
47
+ return stmtWrapper;
48
+ });
49
+ return {
50
+ ...node,
51
+ version: 180004, // PG18 version
52
+ stmts: v18Stmts
53
+ };
54
+ }
55
+ // Otherwise, transform as a regular node through the chain
56
+ const v15Node = this.v14to15.transform(node, { parentNodeTypes: [] });
57
+ const v16Node = this.v15to16.transform(v15Node, { parentNodeTypes: [] });
58
+ const v17Node = this.v16to17.transform(v16Node, { parentNodeTypes: [] });
59
+ return this.v17to18.transform(v17Node, { parentNodeTypes: [] });
60
+ }
61
+ /**
62
+ * Transform a single statement from PG14 to PG18
63
+ * @deprecated Use transform() instead, which handles all node types
64
+ */
65
+ transformStatement(stmt) {
66
+ const v15Stmt = this.v14to15.transform(stmt, { parentNodeTypes: [] });
67
+ const v16Stmt = this.v15to16.transform(v15Stmt, { parentNodeTypes: [] });
68
+ const v17Stmt = this.v16to17.transform(v16Stmt, { parentNodeTypes: [] });
69
+ return this.v17to18.transform(v17Stmt, { parentNodeTypes: [] });
70
+ }
71
+ /**
72
+ * Type guard to check if a node is a ParseResult
73
+ */
74
+ isParseResult(node) {
75
+ return node && typeof node === 'object' && 'version' in node && 'stmts' in node;
76
+ }
77
+ }
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
- 'range': 'PARTITION_STRATEGY_RANGE',
794
- 'list': 'PARTITION_STRATEGY_LIST',
795
- 'hash': 'PARTITION_STRATEGY_HASH'
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: "admin",
2562
+ defname: 'admin',
2555
2563
  arg: {
2556
2564
  Boolean: {
2557
2565
  boolval: true
2558
2566
  }
2559
2567
  },
2560
- defaction: "DEFELEM_UNSPEC"
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) {
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
- return { MergeStmt: node };
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 };