Skip to content

Commit e689ce1

Browse files
committed
fix(plpgsql-deparser): dehydrate hydrated type-names via AST (handle CAST deparse form), preserving array bounds
1 parent 4213e2c commit e689ce1

2 files changed

Lines changed: 46 additions & 2 deletions

File tree

‎packages/plpgsql-deparser/__tests__/hydrate.test.ts‎

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,47 @@ $$`;
274274
expect(deparsedBody).not.toContain('old-schema');
275275
});
276276

277+
it('should preserve array bounds when dehydrating modified type-name nodes', () => {
278+
const sql = `CREATE FUNCTION test_func() RETURNS void
279+
LANGUAGE plpgsql
280+
AS $$
281+
DECLARE
282+
v_items "old-schema".mytype[];
283+
v_item "old-schema".mytype;
284+
BEGIN
285+
NULL;
286+
END;
287+
$$`;
288+
289+
const parsed = parsePlPgSQLSync(sql) as unknown as PLpgSQLParseResult;
290+
const { ast: hydratedAst } = hydratePlpgsqlAst(parsed);
291+
292+
// Rename schema inside the hydrated TypeName AST nodes
293+
const renameTypeNames = (obj: any): void => {
294+
if (obj === null || typeof obj !== 'object') return;
295+
if ('PLpgSQL_type' in obj) {
296+
const typname = obj.PLpgSQL_type.typname;
297+
if (typname && typeof typname === 'object' && typname.kind === 'type-name') {
298+
for (const name of typname.typeNameNode?.names ?? []) {
299+
if (name?.String?.sval === 'old-schema') {
300+
name.String.sval = 'new_schema';
301+
}
302+
}
303+
}
304+
}
305+
for (const value of Object.values(obj)) renameTypeNames(value);
306+
};
307+
renameTypeNames(hydratedAst);
308+
309+
const dehydratedAst = dehydratePlpgsqlAst(hydratedAst);
310+
const deparsedBody = deparseSync(dehydratedAst);
311+
312+
// The renamed array type must keep its [] bounds
313+
expect(deparsedBody).toContain('new_schema.mytype[]');
314+
expect(deparsedBody).toContain('v_item new_schema.mytype;');
315+
expect(deparsedBody).not.toContain('old-schema');
316+
});
317+
277318
it('should deparse modified assign AST nodes (schema renaming in assignments)', () => {
278319
const sql = `CREATE FUNCTION test_func() RETURNS void
279320
LANGUAGE plpgsql

‎packages/plpgsql-deparser/src/hydrate.ts‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -652,8 +652,11 @@ function deparseTypeNameNode(typeNameNode: Node, sqlDeparseOptions?: DeparserOpt
652652
}
653653
} as any;
654654
const deparsed = Deparser.deparse(wrappedStmt, sqlDeparseOptions);
655-
// Extract the type name from "SELECT NULL::typename"
656-
const match = deparsed.match(/SELECT\s+NULL::(.+)/i);
655+
// Extract the type name from "SELECT NULL::typename" or
656+
// "SELECT CAST(NULL AS typename)" depending on the deparser's cast style
657+
const match =
658+
deparsed.match(/SELECT\s+NULL::(.+)/i) ||
659+
deparsed.match(/^SELECT\s+CAST\(NULL\s+AS\s+(.+)\)[\s;]*$/i);
657660
if (match) {
658661
return match[1].trim().replace(/;$/, '');
659662
}

0 commit comments

Comments
 (0)