@@ -153,6 +153,66 @@ $$`;
153153 } ) ;
154154 } ) ;
155155
156+ describe ( 'idempotent hydration' , ( ) => {
157+ it ( 'should handle already hydrated AST without errors' , ( ) => {
158+ const sql = `CREATE FUNCTION test_func() RETURNS void
159+ LANGUAGE plpgsql
160+ AS $$
161+ DECLARE
162+ v_user "my-schema".users;
163+ BEGIN
164+ v_user := (SELECT * FROM "my-schema".users LIMIT 1);
165+ RETURN;
166+ END;
167+ $$` ;
168+
169+ const parsed = parsePlPgSQLSync ( sql ) as unknown as PLpgSQLParseResult ;
170+
171+ // First hydration
172+ const result1 = hydratePlpgsqlAst ( parsed ) ;
173+ expect ( result1 . errors ) . toHaveLength ( 0 ) ;
174+
175+ // Second hydration on already hydrated AST should not throw
176+ const result2 = hydratePlpgsqlAst ( result1 . ast ) ;
177+ expect ( result2 . errors ) . toHaveLength ( 0 ) ;
178+
179+ // The AST should still be valid and deparseable
180+ const dehydrated = dehydratePlpgsqlAst ( result2 . ast ) ;
181+ const deparsed = deparseSync ( dehydrated ) ;
182+ expect ( deparsed ) . toContain ( 'my-schema' ) ;
183+ } ) ;
184+
185+ it ( 'should return already hydrated expressions unchanged' , ( ) => {
186+ const sql = `CREATE FUNCTION test_func() RETURNS integer
187+ LANGUAGE plpgsql
188+ AS $$
189+ DECLARE
190+ v_result integer;
191+ BEGIN
192+ v_result := 10 + 20;
193+ RETURN v_result;
194+ END;
195+ $$` ;
196+
197+ const parsed = parsePlPgSQLSync ( sql ) as unknown as PLpgSQLParseResult ;
198+
199+ // First hydration
200+ const result1 = hydratePlpgsqlAst ( parsed ) ;
201+ const assignExpr1 = findExprByKind ( result1 . ast , 'assign' ) ;
202+ expect ( assignExpr1 ) . toBeDefined ( ) ;
203+
204+ // Second hydration
205+ const result2 = hydratePlpgsqlAst ( result1 . ast ) ;
206+ const assignExpr2 = findExprByKind ( result2 . ast , 'assign' ) ;
207+
208+ // The expression should be the same (unchanged)
209+ expect ( assignExpr2 ) . toBeDefined ( ) ;
210+ expect ( assignExpr2 . kind ) . toBe ( 'assign' ) ;
211+ expect ( assignExpr2 . target ) . toBe ( assignExpr1 . target ) ;
212+ expect ( assignExpr2 . value ) . toBe ( assignExpr1 . value ) ;
213+ } ) ;
214+ } ) ;
215+
156216 describe ( 'heterogeneous deparse (AST-based transformations)' , ( ) => {
157217 it ( 'should deparse modified sql-expr AST nodes (schema renaming)' , ( ) => {
158218 // Note: This test only checks RangeVar nodes in SQL expressions.
0 commit comments