pgsql-deparser 18.4.4 → 18.4.5
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 +0 -1
- package/deparser.js +24 -54
- package/esm/deparser.js +24 -54
- package/esm/index.js +1 -0
- package/esm/utils/sql-formatter.js +103 -1
- package/index.d.ts +1 -0
- package/index.js +3 -1
- package/package.json +2 -2
- package/utils/sql-formatter.d.ts +8 -0
- package/utils/sql-formatter.js +104 -1
package/deparser.d.ts
CHANGED
|
@@ -306,5 +306,4 @@ export declare class Deparser implements DeparserVisitor {
|
|
|
306
306
|
AlterObjectSchemaStmt(node: t.AlterObjectSchemaStmt, context: DeparserContext): string;
|
|
307
307
|
AlterRoleSetStmt(node: t.AlterRoleSetStmt, context: DeparserContext): string;
|
|
308
308
|
CreateForeignTableStmt(node: t.CreateForeignTableStmt, context: DeparserContext): string;
|
|
309
|
-
private containsMultilineStringLiteral;
|
|
310
309
|
}
|
package/deparser.js
CHANGED
|
@@ -500,12 +500,7 @@ class Deparser {
|
|
|
500
500
|
// Check if single target is complex - if so, use multiline format
|
|
501
501
|
if (this.isComplexSelectTarget(targetNode)) {
|
|
502
502
|
output.push('SELECT' + distinctPart);
|
|
503
|
-
|
|
504
|
-
output.push(target);
|
|
505
|
-
}
|
|
506
|
-
else {
|
|
507
|
-
output.push(context.indent(target));
|
|
508
|
-
}
|
|
503
|
+
output.push(context.indent(target));
|
|
509
504
|
}
|
|
510
505
|
else {
|
|
511
506
|
output.push('SELECT' + distinctPart + ' ' + target);
|
|
@@ -515,9 +510,6 @@ class Deparser {
|
|
|
515
510
|
const targetStrings = targetList
|
|
516
511
|
.map(e => {
|
|
517
512
|
const targetStr = this.visit(e, context.spawn('SelectStmt', { select: true }));
|
|
518
|
-
if (this.containsMultilineStringLiteral(targetStr)) {
|
|
519
|
-
return targetStr;
|
|
520
|
-
}
|
|
521
513
|
return context.indent(targetStr);
|
|
522
514
|
});
|
|
523
515
|
const formattedTargets = targetStrings.join(',' + context.newline());
|
|
@@ -569,9 +561,6 @@ class Deparser {
|
|
|
569
561
|
return context.parens(values.join(', '));
|
|
570
562
|
});
|
|
571
563
|
const indentedTuples = lists.map(tuple => {
|
|
572
|
-
if (this.containsMultilineStringLiteral(tuple)) {
|
|
573
|
-
return tuple;
|
|
574
|
-
}
|
|
575
564
|
return context.indent(tuple);
|
|
576
565
|
});
|
|
577
566
|
output.push(indentedTuples.join(',\n'));
|
|
@@ -591,9 +580,6 @@ class Deparser {
|
|
|
591
580
|
const groupItems = groupList
|
|
592
581
|
.map(e => {
|
|
593
582
|
const groupStr = this.visit(e, context.spawn('SelectStmt', { group: true, indentLevel: context.indentLevel + 1 }));
|
|
594
|
-
if (this.containsMultilineStringLiteral(groupStr)) {
|
|
595
|
-
return groupStr;
|
|
596
|
-
}
|
|
597
583
|
return context.indent(groupStr);
|
|
598
584
|
})
|
|
599
585
|
.join(',' + context.newline());
|
|
@@ -612,12 +598,7 @@ class Deparser {
|
|
|
612
598
|
if (context.isPretty()) {
|
|
613
599
|
output.push('HAVING');
|
|
614
600
|
const havingStr = this.visit(node.havingClause, context);
|
|
615
|
-
|
|
616
|
-
output.push(havingStr);
|
|
617
|
-
}
|
|
618
|
-
else {
|
|
619
|
-
output.push(context.indent(havingStr));
|
|
620
|
-
}
|
|
601
|
+
output.push(context.indent(havingStr));
|
|
621
602
|
}
|
|
622
603
|
else {
|
|
623
604
|
output.push('HAVING');
|
|
@@ -638,9 +619,6 @@ class Deparser {
|
|
|
638
619
|
const sortItems = sortList
|
|
639
620
|
.map(e => {
|
|
640
621
|
const sortStr = this.visit(e, context.spawn('SelectStmt', { sort: true, indentLevel: context.indentLevel + 1 }));
|
|
641
|
-
if (this.containsMultilineStringLiteral(sortStr)) {
|
|
642
|
-
return sortStr;
|
|
643
|
-
}
|
|
644
622
|
return context.indent(sortStr);
|
|
645
623
|
})
|
|
646
624
|
.join(',' + context.newline());
|
|
@@ -1093,7 +1071,7 @@ class Deparser {
|
|
|
1093
1071
|
if (context.isPretty()) {
|
|
1094
1072
|
// Put each assignment on its own line for pretty printing
|
|
1095
1073
|
const indentedTargets = targets.map(target => context.indent(target));
|
|
1096
|
-
output.
|
|
1074
|
+
output[output.length - 1] += '\n' + indentedTargets.join(',\n');
|
|
1097
1075
|
}
|
|
1098
1076
|
else {
|
|
1099
1077
|
output.push(targets.join(', '));
|
|
@@ -1245,9 +1223,6 @@ class Deparser {
|
|
|
1245
1223
|
const cteStrings = ctes.map((cte, index) => {
|
|
1246
1224
|
const cteStr = this.visit(cte, context);
|
|
1247
1225
|
const prefix = index === 0 ? context.newline() : ',' + context.newline();
|
|
1248
|
-
if (this.containsMultilineStringLiteral(cteStr)) {
|
|
1249
|
-
return prefix + cteStr;
|
|
1250
|
-
}
|
|
1251
1226
|
return prefix + context.indent(cteStr);
|
|
1252
1227
|
});
|
|
1253
1228
|
output.push(cteStrings.join(''));
|
|
@@ -1422,7 +1397,9 @@ class Deparser {
|
|
|
1422
1397
|
const source = this.visit(args[1], context);
|
|
1423
1398
|
// EXTRACT requires an unquoted uppercase keyword (EPOCH, YEAR, etc.)
|
|
1424
1399
|
// but A_Const sval deparsing wraps it in single quotes — strip and uppercase
|
|
1425
|
-
|
|
1400
|
+
// only when it is spelled the way the bare-keyword form parses back
|
|
1401
|
+
if (/^'(?:year|month|day|hour|minute|second)'$/.test(field)
|
|
1402
|
+
|| (/^'[a-z_][a-z0-9_]*'$/.test(field) && (0, quotes_1.keywordKindOf)(field.slice(1, -1)) === 'NO_KEYWORD')) {
|
|
1426
1403
|
field = field.slice(1, -1).toUpperCase();
|
|
1427
1404
|
}
|
|
1428
1405
|
return `EXTRACT(${field} FROM ${source})`;
|
|
@@ -2220,7 +2197,9 @@ class Deparser {
|
|
|
2220
2197
|
A_Indirection(node, context) {
|
|
2221
2198
|
let argStr = this.visit(node.arg, context);
|
|
2222
2199
|
const argType = this.getNodeType(node.arg);
|
|
2223
|
-
|
|
2200
|
+
// Grammar: only a parameter takes indirection bare; every other target is
|
|
2201
|
+
// `'(' a_expr ')' opt_indirection` (a plain column subscript is the same node).
|
|
2202
|
+
if (argType !== 'ParamRef') {
|
|
2224
2203
|
argStr = `(${argStr})`;
|
|
2225
2204
|
}
|
|
2226
2205
|
const output = [argStr];
|
|
@@ -2248,21 +2227,11 @@ class Deparser {
|
|
|
2248
2227
|
if (context.isPretty() && args.length > 0) {
|
|
2249
2228
|
for (const arg of args) {
|
|
2250
2229
|
const whenClause = this.visit(arg, context);
|
|
2251
|
-
|
|
2252
|
-
output.push(context.newline() + whenClause);
|
|
2253
|
-
}
|
|
2254
|
-
else {
|
|
2255
|
-
output.push(context.newline() + context.indent(whenClause));
|
|
2256
|
-
}
|
|
2230
|
+
output.push(context.newline() + context.indent(whenClause));
|
|
2257
2231
|
}
|
|
2258
2232
|
if (node.defresult) {
|
|
2259
2233
|
const elseResult = this.visit(node.defresult, context);
|
|
2260
|
-
|
|
2261
|
-
output.push(context.newline() + 'ELSE ' + elseResult);
|
|
2262
|
-
}
|
|
2263
|
-
else {
|
|
2264
|
-
output.push(context.newline() + context.indent('ELSE ' + elseResult));
|
|
2265
|
-
}
|
|
2234
|
+
output.push(context.newline() + context.indent('ELSE ' + elseResult));
|
|
2266
2235
|
}
|
|
2267
2236
|
output.push(context.newline() + 'END');
|
|
2268
2237
|
return output.join(' ');
|
|
@@ -3864,12 +3833,7 @@ class Deparser {
|
|
|
3864
3833
|
if (context.isPretty()) {
|
|
3865
3834
|
// For complex JOIN conditions, format with proper indentation
|
|
3866
3835
|
if (qualsStr.includes('AND') || qualsStr.includes('OR') || qualsStr.length > 50) {
|
|
3867
|
-
|
|
3868
|
-
output.push(` ON ${qualsStr}`);
|
|
3869
|
-
}
|
|
3870
|
-
else {
|
|
3871
|
-
output.push(` ON${context.newline()}${context.indent(qualsStr)}`);
|
|
3872
|
-
}
|
|
3836
|
+
output.push(` ON${context.newline()}${context.indent(qualsStr)}`);
|
|
3873
3837
|
}
|
|
3874
3838
|
else {
|
|
3875
3839
|
output.push(` ON ${qualsStr}`);
|
|
@@ -5891,7 +5855,7 @@ class Deparser {
|
|
|
5891
5855
|
if (node.defname === 'new_version') {
|
|
5892
5856
|
// argValue is unquoted due to DefElem context, so we need to quote it
|
|
5893
5857
|
const quotedValue = this.quoteDefElemArg(node.arg, argValue);
|
|
5894
|
-
return `
|
|
5858
|
+
return `TO ${quotedValue}`;
|
|
5895
5859
|
}
|
|
5896
5860
|
if (node.defname === 'schema') {
|
|
5897
5861
|
return `SET SCHEMA ${argValue}`;
|
|
@@ -5911,6 +5875,12 @@ class Deparser {
|
|
|
5911
5875
|
if (node.defname === 'cascade') {
|
|
5912
5876
|
return argValue === 'true' ? 'CASCADE' : '';
|
|
5913
5877
|
}
|
|
5878
|
+
if (node.defname === 'new_version') {
|
|
5879
|
+
return `VERSION ${this.quoteDefElemArg(node.arg, argValue)}`;
|
|
5880
|
+
}
|
|
5881
|
+
if (node.defname === 'schema' && node.arg && this.getNodeType(node.arg) === 'String') {
|
|
5882
|
+
return `SCHEMA ${quotes_1.QuoteUtils.quoteIdentifier(this.getNodeData(node.arg).sval)}`;
|
|
5883
|
+
}
|
|
5914
5884
|
return `${node.defname.toUpperCase()} ${argValue}`;
|
|
5915
5885
|
}
|
|
5916
5886
|
// Handle IndexStmt WITH clause options - no quotes, compact formatting
|
|
@@ -6168,6 +6138,7 @@ class Deparser {
|
|
|
6168
6138
|
if (node.extname) {
|
|
6169
6139
|
output.push(this.quoteIfNeeded(node.extname));
|
|
6170
6140
|
}
|
|
6141
|
+
output.push('UPDATE');
|
|
6171
6142
|
if (node.options && node.options.length > 0) {
|
|
6172
6143
|
const extContext = context.spawn('AlterExtensionStmt');
|
|
6173
6144
|
const options = list_utils_1.ListUtils.unwrapList(node.options)
|
|
@@ -8476,6 +8447,9 @@ class Deparser {
|
|
|
8476
8447
|
.join(' ');
|
|
8477
8448
|
components.push(context.indent('REFERENCING ' + transitionClauses));
|
|
8478
8449
|
}
|
|
8450
|
+
if (node.constrrel) {
|
|
8451
|
+
components.push(context.indent('FROM ' + this.RangeVar(node.constrrel, context)));
|
|
8452
|
+
}
|
|
8479
8453
|
if (node.deferrable) {
|
|
8480
8454
|
components.push(context.indent('DEFERRABLE'));
|
|
8481
8455
|
}
|
|
@@ -8497,7 +8471,7 @@ class Deparser {
|
|
|
8497
8471
|
const funcName = list_utils_1.ListUtils.unwrapList(node.funcname)
|
|
8498
8472
|
.map(name => this.visit(name, context))
|
|
8499
8473
|
.join('.');
|
|
8500
|
-
executeStr += '
|
|
8474
|
+
executeStr += ' FUNCTION ' + funcName;
|
|
8501
8475
|
}
|
|
8502
8476
|
if (node.args && node.args.length > 0) {
|
|
8503
8477
|
const argContext = context.spawn('CreateTrigStmt', { isStringLiteral: true });
|
|
@@ -10934,9 +10908,5 @@ class Deparser {
|
|
|
10934
10908
|
}
|
|
10935
10909
|
return output.join(' ');
|
|
10936
10910
|
}
|
|
10937
|
-
containsMultilineStringLiteral(content) {
|
|
10938
|
-
const stringLiteralRegex = /'[^']*\n[^']*'/g;
|
|
10939
|
-
return stringLiteralRegex.test(content);
|
|
10940
|
-
}
|
|
10941
10911
|
}
|
|
10942
10912
|
exports.Deparser = Deparser;
|
package/esm/deparser.js
CHANGED
|
@@ -497,12 +497,7 @@ export class Deparser {
|
|
|
497
497
|
// Check if single target is complex - if so, use multiline format
|
|
498
498
|
if (this.isComplexSelectTarget(targetNode)) {
|
|
499
499
|
output.push('SELECT' + distinctPart);
|
|
500
|
-
|
|
501
|
-
output.push(target);
|
|
502
|
-
}
|
|
503
|
-
else {
|
|
504
|
-
output.push(context.indent(target));
|
|
505
|
-
}
|
|
500
|
+
output.push(context.indent(target));
|
|
506
501
|
}
|
|
507
502
|
else {
|
|
508
503
|
output.push('SELECT' + distinctPart + ' ' + target);
|
|
@@ -512,9 +507,6 @@ export class Deparser {
|
|
|
512
507
|
const targetStrings = targetList
|
|
513
508
|
.map(e => {
|
|
514
509
|
const targetStr = this.visit(e, context.spawn('SelectStmt', { select: true }));
|
|
515
|
-
if (this.containsMultilineStringLiteral(targetStr)) {
|
|
516
|
-
return targetStr;
|
|
517
|
-
}
|
|
518
510
|
return context.indent(targetStr);
|
|
519
511
|
});
|
|
520
512
|
const formattedTargets = targetStrings.join(',' + context.newline());
|
|
@@ -566,9 +558,6 @@ export class Deparser {
|
|
|
566
558
|
return context.parens(values.join(', '));
|
|
567
559
|
});
|
|
568
560
|
const indentedTuples = lists.map(tuple => {
|
|
569
|
-
if (this.containsMultilineStringLiteral(tuple)) {
|
|
570
|
-
return tuple;
|
|
571
|
-
}
|
|
572
561
|
return context.indent(tuple);
|
|
573
562
|
});
|
|
574
563
|
output.push(indentedTuples.join(',\n'));
|
|
@@ -588,9 +577,6 @@ export class Deparser {
|
|
|
588
577
|
const groupItems = groupList
|
|
589
578
|
.map(e => {
|
|
590
579
|
const groupStr = this.visit(e, context.spawn('SelectStmt', { group: true, indentLevel: context.indentLevel + 1 }));
|
|
591
|
-
if (this.containsMultilineStringLiteral(groupStr)) {
|
|
592
|
-
return groupStr;
|
|
593
|
-
}
|
|
594
580
|
return context.indent(groupStr);
|
|
595
581
|
})
|
|
596
582
|
.join(',' + context.newline());
|
|
@@ -609,12 +595,7 @@ export class Deparser {
|
|
|
609
595
|
if (context.isPretty()) {
|
|
610
596
|
output.push('HAVING');
|
|
611
597
|
const havingStr = this.visit(node.havingClause, context);
|
|
612
|
-
|
|
613
|
-
output.push(havingStr);
|
|
614
|
-
}
|
|
615
|
-
else {
|
|
616
|
-
output.push(context.indent(havingStr));
|
|
617
|
-
}
|
|
598
|
+
output.push(context.indent(havingStr));
|
|
618
599
|
}
|
|
619
600
|
else {
|
|
620
601
|
output.push('HAVING');
|
|
@@ -635,9 +616,6 @@ export class Deparser {
|
|
|
635
616
|
const sortItems = sortList
|
|
636
617
|
.map(e => {
|
|
637
618
|
const sortStr = this.visit(e, context.spawn('SelectStmt', { sort: true, indentLevel: context.indentLevel + 1 }));
|
|
638
|
-
if (this.containsMultilineStringLiteral(sortStr)) {
|
|
639
|
-
return sortStr;
|
|
640
|
-
}
|
|
641
619
|
return context.indent(sortStr);
|
|
642
620
|
})
|
|
643
621
|
.join(',' + context.newline());
|
|
@@ -1090,7 +1068,7 @@ export class Deparser {
|
|
|
1090
1068
|
if (context.isPretty()) {
|
|
1091
1069
|
// Put each assignment on its own line for pretty printing
|
|
1092
1070
|
const indentedTargets = targets.map(target => context.indent(target));
|
|
1093
|
-
output.
|
|
1071
|
+
output[output.length - 1] += '\n' + indentedTargets.join(',\n');
|
|
1094
1072
|
}
|
|
1095
1073
|
else {
|
|
1096
1074
|
output.push(targets.join(', '));
|
|
@@ -1242,9 +1220,6 @@ export class Deparser {
|
|
|
1242
1220
|
const cteStrings = ctes.map((cte, index) => {
|
|
1243
1221
|
const cteStr = this.visit(cte, context);
|
|
1244
1222
|
const prefix = index === 0 ? context.newline() : ',' + context.newline();
|
|
1245
|
-
if (this.containsMultilineStringLiteral(cteStr)) {
|
|
1246
|
-
return prefix + cteStr;
|
|
1247
|
-
}
|
|
1248
1223
|
return prefix + context.indent(cteStr);
|
|
1249
1224
|
});
|
|
1250
1225
|
output.push(cteStrings.join(''));
|
|
@@ -1419,7 +1394,9 @@ export class Deparser {
|
|
|
1419
1394
|
const source = this.visit(args[1], context);
|
|
1420
1395
|
// EXTRACT requires an unquoted uppercase keyword (EPOCH, YEAR, etc.)
|
|
1421
1396
|
// but A_Const sval deparsing wraps it in single quotes — strip and uppercase
|
|
1422
|
-
|
|
1397
|
+
// only when it is spelled the way the bare-keyword form parses back
|
|
1398
|
+
if (/^'(?:year|month|day|hour|minute|second)'$/.test(field)
|
|
1399
|
+
|| (/^'[a-z_][a-z0-9_]*'$/.test(field) && keywordKindOf(field.slice(1, -1)) === 'NO_KEYWORD')) {
|
|
1423
1400
|
field = field.slice(1, -1).toUpperCase();
|
|
1424
1401
|
}
|
|
1425
1402
|
return `EXTRACT(${field} FROM ${source})`;
|
|
@@ -2217,7 +2194,9 @@ export class Deparser {
|
|
|
2217
2194
|
A_Indirection(node, context) {
|
|
2218
2195
|
let argStr = this.visit(node.arg, context);
|
|
2219
2196
|
const argType = this.getNodeType(node.arg);
|
|
2220
|
-
|
|
2197
|
+
// Grammar: only a parameter takes indirection bare; every other target is
|
|
2198
|
+
// `'(' a_expr ')' opt_indirection` (a plain column subscript is the same node).
|
|
2199
|
+
if (argType !== 'ParamRef') {
|
|
2221
2200
|
argStr = `(${argStr})`;
|
|
2222
2201
|
}
|
|
2223
2202
|
const output = [argStr];
|
|
@@ -2245,21 +2224,11 @@ export class Deparser {
|
|
|
2245
2224
|
if (context.isPretty() && args.length > 0) {
|
|
2246
2225
|
for (const arg of args) {
|
|
2247
2226
|
const whenClause = this.visit(arg, context);
|
|
2248
|
-
|
|
2249
|
-
output.push(context.newline() + whenClause);
|
|
2250
|
-
}
|
|
2251
|
-
else {
|
|
2252
|
-
output.push(context.newline() + context.indent(whenClause));
|
|
2253
|
-
}
|
|
2227
|
+
output.push(context.newline() + context.indent(whenClause));
|
|
2254
2228
|
}
|
|
2255
2229
|
if (node.defresult) {
|
|
2256
2230
|
const elseResult = this.visit(node.defresult, context);
|
|
2257
|
-
|
|
2258
|
-
output.push(context.newline() + 'ELSE ' + elseResult);
|
|
2259
|
-
}
|
|
2260
|
-
else {
|
|
2261
|
-
output.push(context.newline() + context.indent('ELSE ' + elseResult));
|
|
2262
|
-
}
|
|
2231
|
+
output.push(context.newline() + context.indent('ELSE ' + elseResult));
|
|
2263
2232
|
}
|
|
2264
2233
|
output.push(context.newline() + 'END');
|
|
2265
2234
|
return output.join(' ');
|
|
@@ -3861,12 +3830,7 @@ export class Deparser {
|
|
|
3861
3830
|
if (context.isPretty()) {
|
|
3862
3831
|
// For complex JOIN conditions, format with proper indentation
|
|
3863
3832
|
if (qualsStr.includes('AND') || qualsStr.includes('OR') || qualsStr.length > 50) {
|
|
3864
|
-
|
|
3865
|
-
output.push(` ON ${qualsStr}`);
|
|
3866
|
-
}
|
|
3867
|
-
else {
|
|
3868
|
-
output.push(` ON${context.newline()}${context.indent(qualsStr)}`);
|
|
3869
|
-
}
|
|
3833
|
+
output.push(` ON${context.newline()}${context.indent(qualsStr)}`);
|
|
3870
3834
|
}
|
|
3871
3835
|
else {
|
|
3872
3836
|
output.push(` ON ${qualsStr}`);
|
|
@@ -5888,7 +5852,7 @@ export class Deparser {
|
|
|
5888
5852
|
if (node.defname === 'new_version') {
|
|
5889
5853
|
// argValue is unquoted due to DefElem context, so we need to quote it
|
|
5890
5854
|
const quotedValue = this.quoteDefElemArg(node.arg, argValue);
|
|
5891
|
-
return `
|
|
5855
|
+
return `TO ${quotedValue}`;
|
|
5892
5856
|
}
|
|
5893
5857
|
if (node.defname === 'schema') {
|
|
5894
5858
|
return `SET SCHEMA ${argValue}`;
|
|
@@ -5908,6 +5872,12 @@ export class Deparser {
|
|
|
5908
5872
|
if (node.defname === 'cascade') {
|
|
5909
5873
|
return argValue === 'true' ? 'CASCADE' : '';
|
|
5910
5874
|
}
|
|
5875
|
+
if (node.defname === 'new_version') {
|
|
5876
|
+
return `VERSION ${this.quoteDefElemArg(node.arg, argValue)}`;
|
|
5877
|
+
}
|
|
5878
|
+
if (node.defname === 'schema' && node.arg && this.getNodeType(node.arg) === 'String') {
|
|
5879
|
+
return `SCHEMA ${QuoteUtils.quoteIdentifier(this.getNodeData(node.arg).sval)}`;
|
|
5880
|
+
}
|
|
5911
5881
|
return `${node.defname.toUpperCase()} ${argValue}`;
|
|
5912
5882
|
}
|
|
5913
5883
|
// Handle IndexStmt WITH clause options - no quotes, compact formatting
|
|
@@ -6165,6 +6135,7 @@ export class Deparser {
|
|
|
6165
6135
|
if (node.extname) {
|
|
6166
6136
|
output.push(this.quoteIfNeeded(node.extname));
|
|
6167
6137
|
}
|
|
6138
|
+
output.push('UPDATE');
|
|
6168
6139
|
if (node.options && node.options.length > 0) {
|
|
6169
6140
|
const extContext = context.spawn('AlterExtensionStmt');
|
|
6170
6141
|
const options = ListUtils.unwrapList(node.options)
|
|
@@ -8473,6 +8444,9 @@ export class Deparser {
|
|
|
8473
8444
|
.join(' ');
|
|
8474
8445
|
components.push(context.indent('REFERENCING ' + transitionClauses));
|
|
8475
8446
|
}
|
|
8447
|
+
if (node.constrrel) {
|
|
8448
|
+
components.push(context.indent('FROM ' + this.RangeVar(node.constrrel, context)));
|
|
8449
|
+
}
|
|
8476
8450
|
if (node.deferrable) {
|
|
8477
8451
|
components.push(context.indent('DEFERRABLE'));
|
|
8478
8452
|
}
|
|
@@ -8494,7 +8468,7 @@ export class Deparser {
|
|
|
8494
8468
|
const funcName = ListUtils.unwrapList(node.funcname)
|
|
8495
8469
|
.map(name => this.visit(name, context))
|
|
8496
8470
|
.join('.');
|
|
8497
|
-
executeStr += '
|
|
8471
|
+
executeStr += ' FUNCTION ' + funcName;
|
|
8498
8472
|
}
|
|
8499
8473
|
if (node.args && node.args.length > 0) {
|
|
8500
8474
|
const argContext = context.spawn('CreateTrigStmt', { isStringLiteral: true });
|
|
@@ -10931,8 +10905,4 @@ export class Deparser {
|
|
|
10931
10905
|
}
|
|
10932
10906
|
return output.join(' ');
|
|
10933
10907
|
}
|
|
10934
|
-
containsMultilineStringLiteral(content) {
|
|
10935
|
-
const stringLiteralRegex = /'[^']*\n[^']*'/g;
|
|
10936
|
-
return stringLiteralRegex.test(content);
|
|
10937
|
-
}
|
|
10938
10908
|
}
|
package/esm/index.js
CHANGED
|
@@ -1,3 +1,105 @@
|
|
|
1
|
+
const isIdentChar = (ch) => ch !== undefined && (/[A-Za-z0-9_$]/.test(ch) || ch.charCodeAt(0) >= 0x80);
|
|
2
|
+
const DOLLAR_TAG = /\$(?:[A-Za-z_\u0080-\uffff][A-Za-z0-9_\u0080-\uffff]*)?\$/y;
|
|
3
|
+
/**
|
|
4
|
+
* Splits SQL text into lines, breaking only at newlines that sit between
|
|
5
|
+
* tokens. A newline inside a string literal ('...', E'...', $tag$...$tag$),
|
|
6
|
+
* a quoted identifier or a block comment is part of that token's value and
|
|
7
|
+
* stays inside its line, so callers can re-indent the lines without changing
|
|
8
|
+
* what the SQL means.
|
|
9
|
+
*/
|
|
10
|
+
export function splitSqlLines(text, newlineChar = '\n') {
|
|
11
|
+
if (!newlineChar || !text.includes(newlineChar)) {
|
|
12
|
+
return [text];
|
|
13
|
+
}
|
|
14
|
+
const lines = [];
|
|
15
|
+
const length = text.length;
|
|
16
|
+
let lineStart = 0;
|
|
17
|
+
let i = 0;
|
|
18
|
+
while (i < length) {
|
|
19
|
+
const ch = text[i];
|
|
20
|
+
if (ch === "'") {
|
|
21
|
+
const prev = text[i - 1];
|
|
22
|
+
const backslashEscapes = (prev === 'E' || prev === 'e') && !isIdentChar(text[i - 2]);
|
|
23
|
+
i++;
|
|
24
|
+
while (i < length) {
|
|
25
|
+
if (backslashEscapes && text[i] === '\\') {
|
|
26
|
+
i += 2;
|
|
27
|
+
}
|
|
28
|
+
else if (text[i] === "'") {
|
|
29
|
+
if (text[i + 1] === "'") {
|
|
30
|
+
i += 2;
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
i++;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
i++;
|
|
41
|
+
}
|
|
42
|
+
else if (ch === '"') {
|
|
43
|
+
i++;
|
|
44
|
+
while (i < length) {
|
|
45
|
+
if (text[i] === '"') {
|
|
46
|
+
if (text[i + 1] === '"') {
|
|
47
|
+
i += 2;
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
i++;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
i++;
|
|
58
|
+
}
|
|
59
|
+
else if (ch === '$' && !isIdentChar(text[i - 1])) {
|
|
60
|
+
DOLLAR_TAG.lastIndex = i;
|
|
61
|
+
const tag = DOLLAR_TAG.exec(text);
|
|
62
|
+
if (tag) {
|
|
63
|
+
const close = text.indexOf(tag[0], i + tag[0].length);
|
|
64
|
+
i = close === -1 ? length : close + tag[0].length;
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
i++;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else if (ch === '-' && text[i + 1] === '-') {
|
|
71
|
+
const end = text.indexOf('\n', i);
|
|
72
|
+
i = end === -1 ? length : end;
|
|
73
|
+
}
|
|
74
|
+
else if (ch === '/' && text[i + 1] === '*') {
|
|
75
|
+
let depth = 1;
|
|
76
|
+
i += 2;
|
|
77
|
+
while (i < length && depth > 0) {
|
|
78
|
+
if (text[i] === '/' && text[i + 1] === '*') {
|
|
79
|
+
depth++;
|
|
80
|
+
i += 2;
|
|
81
|
+
}
|
|
82
|
+
else if (text[i] === '*' && text[i + 1] === '/') {
|
|
83
|
+
depth--;
|
|
84
|
+
i += 2;
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
i++;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
else if (text.startsWith(newlineChar, i)) {
|
|
92
|
+
lines.push(text.slice(lineStart, i));
|
|
93
|
+
i += newlineChar.length;
|
|
94
|
+
lineStart = i;
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
i++;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
lines.push(text.slice(lineStart));
|
|
101
|
+
return lines;
|
|
102
|
+
}
|
|
1
103
|
export class SqlFormatter {
|
|
2
104
|
newlineChar;
|
|
3
105
|
tabChar;
|
|
@@ -15,7 +117,7 @@ export class SqlFormatter {
|
|
|
15
117
|
return text;
|
|
16
118
|
}
|
|
17
119
|
const indentation = this.tabChar.repeat(count);
|
|
18
|
-
return text
|
|
120
|
+
return splitSqlLines(text, this.newlineChar).map(line => line.trim() ? indentation + line : line).join(this.newlineChar);
|
|
19
121
|
}
|
|
20
122
|
parens(content) {
|
|
21
123
|
return `(${content})`;
|
package/index.d.ts
CHANGED
|
@@ -3,4 +3,5 @@ declare const deparseMethod: typeof Deparser.deparse;
|
|
|
3
3
|
export declare const deparseSync: typeof Deparser.deparse;
|
|
4
4
|
export declare const deparse: (...args: Parameters<typeof deparseMethod>) => Promise<ReturnType<typeof deparseMethod>>;
|
|
5
5
|
export { Deparser, DeparserOptions };
|
|
6
|
+
export { splitSqlLines } from './utils/sql-formatter';
|
|
6
7
|
export { QuoteUtils } from '@pgsql/quotes';
|
package/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.QuoteUtils = exports.Deparser = exports.deparse = exports.deparseSync = void 0;
|
|
3
|
+
exports.QuoteUtils = exports.splitSqlLines = exports.Deparser = exports.deparse = exports.deparseSync = void 0;
|
|
4
4
|
const deparser_1 = require("./deparser");
|
|
5
5
|
Object.defineProperty(exports, "Deparser", { enumerable: true, get: function () { return deparser_1.Deparser; } });
|
|
6
6
|
const deparseMethod = deparser_1.Deparser.deparse;
|
|
@@ -11,5 +11,7 @@ const deparse = async (...args) => {
|
|
|
11
11
|
return deparseMethod(...args);
|
|
12
12
|
};
|
|
13
13
|
exports.deparse = deparse;
|
|
14
|
+
var sql_formatter_1 = require("./utils/sql-formatter");
|
|
15
|
+
Object.defineProperty(exports, "splitSqlLines", { enumerable: true, get: function () { return sql_formatter_1.splitSqlLines; } });
|
|
14
16
|
var quotes_1 = require("@pgsql/quotes");
|
|
15
17
|
Object.defineProperty(exports, "QuoteUtils", { enumerable: true, get: function () { return quotes_1.QuoteUtils; } });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pgsql-deparser",
|
|
3
|
-
"version": "18.4.
|
|
3
|
+
"version": "18.4.5",
|
|
4
4
|
"author": "Constructive <developers@constructive.io>",
|
|
5
5
|
"description": "PostgreSQL AST Deparser",
|
|
6
6
|
"main": "index.js",
|
|
@@ -60,5 +60,5 @@
|
|
|
60
60
|
"@pgsql/quotes": "18.2.4",
|
|
61
61
|
"@pgsql/types": "^18.0.0"
|
|
62
62
|
},
|
|
63
|
-
"gitHead": "
|
|
63
|
+
"gitHead": "2ea833400ad4f4b440b318b84fe9f0364db686fb"
|
|
64
64
|
}
|
package/utils/sql-formatter.d.ts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Splits SQL text into lines, breaking only at newlines that sit between
|
|
3
|
+
* tokens. A newline inside a string literal ('...', E'...', $tag$...$tag$),
|
|
4
|
+
* a quoted identifier or a block comment is part of that token's value and
|
|
5
|
+
* stays inside its line, so callers can re-indent the lines without changing
|
|
6
|
+
* what the SQL means.
|
|
7
|
+
*/
|
|
8
|
+
export declare function splitSqlLines(text: string, newlineChar?: string): string[];
|
|
1
9
|
export declare class SqlFormatter {
|
|
2
10
|
private newlineChar;
|
|
3
11
|
private tabChar;
|
package/utils/sql-formatter.js
CHANGED
|
@@ -1,6 +1,109 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SqlFormatter = void 0;
|
|
4
|
+
exports.splitSqlLines = splitSqlLines;
|
|
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
|
+
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
|
+
}
|
|
4
107
|
class SqlFormatter {
|
|
5
108
|
newlineChar;
|
|
6
109
|
tabChar;
|
|
@@ -18,7 +121,7 @@ class SqlFormatter {
|
|
|
18
121
|
return text;
|
|
19
122
|
}
|
|
20
123
|
const indentation = this.tabChar.repeat(count);
|
|
21
|
-
return text
|
|
124
|
+
return splitSqlLines(text, this.newlineChar).map(line => line.trim() ? indentation + line : line).join(this.newlineChar);
|
|
22
125
|
}
|
|
23
126
|
parens(content) {
|
|
24
127
|
return `(${content})`;
|