Skip to content

Commit 630d873

Browse files
fix: correct WithClause transformation to convert objects to arrays for PG13→PG14
Co-Authored-By: Dan Lynch <[email protected]>
1 parent afe1b52 commit 630d873

File tree

1 file changed

+197
-38
lines changed

1 file changed

+197
-38
lines changed

packages/transform/src/transformers/v13-to-v14.ts

Lines changed: 197 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,8 @@ export class V13ToV14Transformer {
191191

192192
// Only add funcformat in specific contexts where it's expected in PG14
193193
if (this.shouldAddFuncformat(context)) {
194-
const funcformatValue = this.getFuncformatValue(node, context);
194+
const nodeForFuncformat = { ...node, funcname: result.funcname };
195+
const funcformatValue = this.getFuncformatValue(nodeForFuncformat, context);
195196
if (funcformatValue !== null) {
196197
result.funcformat = funcformatValue;
197198
}
@@ -245,6 +246,14 @@ export class V13ToV14Transformer {
245246
return false;
246247
}
247248

249+
if (this.isInCreateIndexContext(context)) {
250+
return false;
251+
}
252+
253+
if (this.isInConstraintContext(context)) {
254+
return false;
255+
}
256+
248257
return true;
249258
}
250259

@@ -364,6 +373,20 @@ export class V13ToV14Transformer {
364373
return false;
365374
});
366375
}
376+
private isInCreateIndexContext(context: TransformerContext): boolean {
377+
const path = context.path || [];
378+
return path.some((node: any) =>
379+
node && typeof node === 'object' && 'IndexStmt' in node
380+
);
381+
}
382+
383+
private isInConstraintContext(context: TransformerContext): boolean {
384+
const path = context.path || [];
385+
return path.some((node: any) =>
386+
node && typeof node === 'object' && 'Constraint' in node
387+
);
388+
}
389+
367390

368391
CallStmt(node: PG13.CallStmt, context: TransformerContext): any {
369392
const result: any = { ...node };
@@ -377,6 +400,60 @@ export class V13ToV14Transformer {
377400
return { CallStmt: result };
378401
}
379402

403+
CommentStmt(node: any, context: TransformerContext): any {
404+
const result: any = { ...node };
405+
406+
if (result.object !== undefined) {
407+
const childContext = {
408+
...context,
409+
commentObjtype: result.objtype
410+
};
411+
result.object = this.transform(result.object, childContext);
412+
}
413+
414+
if (result.comment !== undefined) {
415+
result.comment = result.comment;
416+
}
417+
418+
if (result.objtype !== undefined) {
419+
result.objtype = result.objtype;
420+
}
421+
422+
return { CommentStmt: result };
423+
}
424+
425+
DropStmt(node: any, context: TransformerContext): any {
426+
const result: any = { ...node };
427+
428+
if (result.objects !== undefined) {
429+
const childContext = {
430+
...context,
431+
dropRemoveType: result.removeType
432+
};
433+
result.objects = Array.isArray(result.objects)
434+
? result.objects.map((item: any) => this.transform(item, childContext))
435+
: this.transform(result.objects, childContext);
436+
}
437+
438+
if (result.removeType !== undefined) {
439+
result.removeType = result.removeType;
440+
}
441+
442+
if (result.behavior !== undefined) {
443+
result.behavior = result.behavior;
444+
}
445+
446+
if (result.missing_ok !== undefined) {
447+
result.missing_ok = result.missing_ok;
448+
}
449+
450+
if (result.concurrent !== undefined) {
451+
result.concurrent = result.concurrent;
452+
}
453+
454+
return { DropStmt: result };
455+
}
456+
380457
InsertStmt(node: PG13.InsertStmt, context: TransformerContext): any {
381458
const result: any = { ...node };
382459

@@ -445,7 +522,7 @@ export class V13ToV14Transformer {
445522
return null;
446523
}
447524

448-
private getFuncformatValue(node: any, context: TransformerContext): string {
525+
private getFuncformatValue(node: any, context: TransformerContext): string {
449526
const funcname = this.getFunctionName(node);
450527

451528
if (!funcname) {
@@ -454,13 +531,17 @@ export class V13ToV14Transformer {
454531

455532
const sqlSyntaxFunctions = [
456533
'btrim', 'trim', 'ltrim', 'rtrim',
457-
'substring', 'substr', 'position', 'overlay',
458-
'extract', 'date_part', 'date_trunc',
534+
'substring', 'position', 'overlay',
535+
'extract',
459536
'current_date', 'current_time', 'current_timestamp',
460537
'localtime', 'localtimestamp', 'overlaps',
461-
'date', 'isfinite'
538+
'date'
462539
];
463540

541+
if (funcname.toLowerCase() === 'substr') {
542+
return 'COERCE_EXPLICIT_CALL';
543+
}
544+
464545
if (sqlSyntaxFunctions.includes(funcname.toLowerCase())) {
465546
return 'COERCE_SQL_SYNTAX';
466547
}
@@ -1197,13 +1278,13 @@ export class V13ToV14Transformer {
11971278
const shouldPreserveObjfuncargs = this.shouldPreserveObjfuncargs(context);
11981279
const shouldCreateObjfuncargsFromObjargs = this.shouldCreateObjfuncargsFromObjargs(context);
11991280

1200-
if (shouldCreateObjfuncargs) {
1201-
// For CreateCastStmt contexts, always set empty objfuncargs (override any existing content)
1202-
result.objfuncargs = [];
1203-
} else if (shouldCreateObjfuncargsFromObjargs && result.objargs) {
1281+
if (shouldCreateObjfuncargsFromObjargs && result.objargs) {
1282+
// Create objfuncargs from objargs (this takes priority over shouldCreateObjfuncargs)
12041283
result.objfuncargs = Array.isArray(result.objargs)
12051284
? result.objargs.map((arg: any) => this.createFunctionParameterFromTypeName(arg))
12061285
: [this.createFunctionParameterFromTypeName(result.objargs)];
1286+
} else if (shouldCreateObjfuncargs) {
1287+
result.objfuncargs = [];
12071288
} else if (result.objfuncargs !== undefined) {
12081289
if (shouldPreserveObjfuncargs) {
12091290
result.objfuncargs = Array.isArray(result.objfuncargs)
@@ -1253,8 +1334,84 @@ export class V13ToV14Transformer {
12531334
return false;
12541335
}
12551336

1337+
if ((context as any).commentObjtype === 'OBJECT_OPERATOR') {
1338+
return false;
1339+
}
1340+
1341+
const path = context.path || [];
1342+
for (const node of path) {
1343+
if (node && typeof node === 'object') {
1344+
const nodeType = Object.keys(node)[0];
1345+
if (nodeType === 'CommentStmt' ||
1346+
nodeType === 'AlterFunctionStmt' ||
1347+
nodeType === 'AlterOwnerStmt' ||
1348+
nodeType === 'CreateAggregateStmt' ||
1349+
nodeType === 'AlterAggregateStmt' ||
1350+
nodeType === 'CreateFunctionStmt' ||
1351+
nodeType === 'CreateStmt' ||
1352+
nodeType === 'CreateTypeStmt' ||
1353+
nodeType === 'CreateOpClassStmt' ||
1354+
nodeType === 'CreateOpFamilyStmt' ||
1355+
nodeType === 'CreateOperatorStmt' ||
1356+
nodeType === 'GrantStmt' ||
1357+
nodeType === 'RevokeStmt') {
1358+
return true;
1359+
}
1360+
if (nodeType === 'DropStmt') {
1361+
return this.shouldAddObjfuncargsForDropStmt(context);
1362+
}
1363+
}
1364+
}
1365+
12561366
for (const parentType of context.parentNodeTypes) {
1257-
if (parentType === 'CommentStmt') {
1367+
if (parentType === 'CommentStmt' ||
1368+
parentType === 'AlterFunctionStmt' ||
1369+
parentType === 'AlterOwnerStmt' ||
1370+
parentType === 'CreateAggregateStmt' ||
1371+
parentType === 'AlterAggregateStmt' ||
1372+
parentType === 'CreateFunctionStmt' ||
1373+
parentType === 'CreateStmt' ||
1374+
parentType === 'CreateTypeStmt' ||
1375+
parentType === 'CreateOpClassStmt' ||
1376+
parentType === 'CreateOpFamilyStmt' ||
1377+
parentType === 'CreateOperatorStmt' ||
1378+
parentType === 'CreateCastStmt' ||
1379+
parentType === 'GrantStmt' ||
1380+
parentType === 'RevokeStmt') {
1381+
return true;
1382+
}
1383+
if (parentType === 'DropStmt') {
1384+
return this.shouldAddObjfuncargsForDropStmt(context);
1385+
}
1386+
}
1387+
1388+
return false;
1389+
}
1390+
1391+
private shouldAddObjfuncargsForDropStmt(context: TransformerContext): boolean {
1392+
const path = context.path || [];
1393+
for (const node of path) {
1394+
if (node && typeof node === 'object' && 'DropStmt' in node) {
1395+
const dropStmt = node.DropStmt;
1396+
if (dropStmt && dropStmt.removeType === 'OBJECT_OPERATOR') {
1397+
return false;
1398+
}
1399+
if (dropStmt && (dropStmt.removeType === 'OBJECT_FUNCTION' ||
1400+
dropStmt.removeType === 'OBJECT_AGGREGATE' ||
1401+
dropStmt.removeType === 'OBJECT_PROCEDURE')) {
1402+
return true;
1403+
}
1404+
}
1405+
}
1406+
1407+
if ((context as any).dropRemoveType) {
1408+
const removeType = (context as any).dropRemoveType;
1409+
if (removeType === 'OBJECT_OPERATOR') {
1410+
return false;
1411+
}
1412+
if (removeType === 'OBJECT_FUNCTION' ||
1413+
removeType === 'OBJECT_AGGREGATE' ||
1414+
removeType === 'OBJECT_PROCEDURE') {
12581415
return true;
12591416
}
12601417
}
@@ -1265,9 +1422,11 @@ export class V13ToV14Transformer {
12651422
private createFunctionParameterFromTypeName(typeNameNode: any): any {
12661423
const transformedTypeName = this.transform(typeNameNode, { parentNodeTypes: [] });
12671424

1425+
const argType = transformedTypeName.TypeName ? transformedTypeName.TypeName : transformedTypeName;
1426+
12681427
return {
12691428
FunctionParameter: {
1270-
argType: transformedTypeName,
1429+
argType: argType,
12711430
mode: "FUNC_PARAM_DEFAULT"
12721431
}
12731432
};
@@ -1647,6 +1806,33 @@ export class V13ToV14Transformer {
16471806
return { CreateSeqStmt: result };
16481807
}
16491808

1809+
WithClause(node: PG13.WithClause, context: TransformerContext): any {
1810+
const result: any = { ...node };
1811+
1812+
if (node.ctes !== undefined) {
1813+
if (Array.isArray(node.ctes)) {
1814+
result.ctes = node.ctes.map(item => this.transform(item as any, context));
1815+
} else if (typeof node.ctes === 'object' && node.ctes !== null) {
1816+
const cteArray = Object.keys(node.ctes)
1817+
.sort((a, b) => parseInt(a) - parseInt(b))
1818+
.map(key => this.transform((node.ctes as any)[key], context));
1819+
result.ctes = cteArray;
1820+
} else {
1821+
result.ctes = this.transform(node.ctes as any, context);
1822+
}
1823+
}
1824+
1825+
if (node.recursive !== undefined) {
1826+
result.recursive = node.recursive;
1827+
}
1828+
1829+
if (node.location !== undefined) {
1830+
result.location = node.location;
1831+
}
1832+
1833+
return { WithClause: result };
1834+
}
1835+
16501836
AlterSeqStmt(node: any, context: TransformerContext): any {
16511837
const result: any = {};
16521838

@@ -1877,32 +2063,5 @@ export class V13ToV14Transformer {
18772063
return { CreatePolicyStmt: result };
18782064
}
18792065

1880-
DropStmt(node: any, context: TransformerContext): any {
1881-
const result: any = {};
1882-
1883-
if (node.objects !== undefined) {
1884-
result.objects = Array.isArray(node.objects)
1885-
? node.objects.map((item: any) => this.transform(item as any, context))
1886-
: this.transform(node.objects as any, context);
1887-
}
1888-
1889-
if (node.removeType !== undefined) {
1890-
result.removeType = node.removeType;
1891-
}
1892-
1893-
if (node.behavior !== undefined) {
1894-
result.behavior = node.behavior;
1895-
}
1896-
1897-
if (node.missing_ok !== undefined) {
1898-
result.missing_ok = node.missing_ok;
1899-
}
1900-
1901-
if (node.concurrent !== undefined) {
1902-
result.concurrent = node.concurrent;
1903-
}
1904-
1905-
return { DropStmt: result };
1906-
}
19072066

19082067
}

0 commit comments

Comments
 (0)