11import { loadModule , parsePlPgSQLSync } from '@libpg-query/parser' ;
22import { deparseSync , PLpgSQLParseResult } from '../src' ;
3+ import { PLpgSQLTestUtils } from '../test-utils' ;
34
45describe ( 'plpgsql-deparser bug fixes' , ( ) => {
6+ let testUtils : PLpgSQLTestUtils ;
7+
58 beforeAll ( async ( ) => {
69 await loadModule ( ) ;
10+ testUtils = new PLpgSQLTestUtils ( ) ;
711 } ) ;
812
913 describe ( 'PERFORM SELECT fix' , ( ) => {
10- it ( 'should strip SELECT keyword from PERFORM statements' , ( ) => {
14+ it ( 'should strip SELECT keyword from PERFORM statements' , async ( ) => {
1115 const sql = `CREATE FUNCTION test_perform() RETURNS void
1216LANGUAGE plpgsql
1317AS $$
@@ -16,15 +20,18 @@ BEGIN
1620END;
1721$$` ;
1822
23+ // Round-trip test: parse -> deparse -> reparse -> compare ASTs
24+ await testUtils . expectAstMatch ( 'PERFORM basic' , sql ) ;
25+
26+ // Also verify specific output characteristics
1927 const parsed = parsePlPgSQLSync ( sql ) as unknown as PLpgSQLParseResult ;
2028 const deparsed = deparseSync ( parsed ) ;
21-
2229 expect ( deparsed ) . toMatchSnapshot ( ) ;
2330 expect ( deparsed ) . toContain ( 'PERFORM pg_sleep' ) ;
2431 expect ( deparsed ) . not . toMatch ( / P E R F O R M \s + S E L E C T / i) ;
2532 } ) ;
2633
27- it ( 'should handle PERFORM with complex expressions' , ( ) => {
34+ it ( 'should handle PERFORM with complex expressions' , async ( ) => {
2835 const sql = `CREATE FUNCTION test_perform_complex() RETURNS void
2936LANGUAGE plpgsql
3037AS $$
@@ -34,14 +41,15 @@ BEGIN
3441END;
3542$$` ;
3643
44+ await testUtils . expectAstMatch ( 'PERFORM complex' , sql ) ;
45+
3746 const parsed = parsePlPgSQLSync ( sql ) as unknown as PLpgSQLParseResult ;
3847 const deparsed = deparseSync ( parsed ) ;
39-
4048 expect ( deparsed ) . toMatchSnapshot ( ) ;
4149 expect ( deparsed ) . not . toMatch ( / P E R F O R M \s + S E L E C T / i) ;
4250 } ) ;
4351
44- it ( 'should handle PERFORM with subquery' , ( ) => {
52+ it ( 'should handle PERFORM with subquery' , async ( ) => {
4553 const sql = `CREATE FUNCTION test_perform_subquery() RETURNS void
4654LANGUAGE plpgsql
4755AS $$
@@ -50,16 +58,17 @@ BEGIN
5058END;
5159$$` ;
5260
61+ await testUtils . expectAstMatch ( 'PERFORM subquery' , sql ) ;
62+
5363 const parsed = parsePlPgSQLSync ( sql ) as unknown as PLpgSQLParseResult ;
5464 const deparsed = deparseSync ( parsed ) ;
55-
5665 expect ( deparsed ) . toMatchSnapshot ( ) ;
5766 expect ( deparsed ) . not . toMatch ( / P E R F O R M \s + S E L E C T / i) ;
5867 } ) ;
5968 } ) ;
6069
6170 describe ( 'INTO clause depth-aware scanner' , ( ) => {
62- it ( 'should insert INTO at correct position for simple SELECT' , ( ) => {
71+ it ( 'should insert INTO at correct position for simple SELECT' , async ( ) => {
6372 const sql = `CREATE FUNCTION test_into_simple() RETURNS integer
6473LANGUAGE plpgsql
6574AS $$
@@ -71,14 +80,15 @@ BEGIN
7180END;
7281$$` ;
7382
83+ await testUtils . expectAstMatch ( 'INTO simple' , sql ) ;
84+
7485 const parsed = parsePlPgSQLSync ( sql ) as unknown as PLpgSQLParseResult ;
7586 const deparsed = deparseSync ( parsed ) ;
76-
7787 expect ( deparsed ) . toMatchSnapshot ( ) ;
7888 expect ( deparsed ) . toContain ( 'INTO' ) ;
7989 } ) ;
8090
81- it ( 'should not insert INTO inside subqueries' , ( ) => {
91+ it ( 'should not insert INTO inside subqueries' , async ( ) => {
8292 const sql = `CREATE FUNCTION test_into_subquery() RETURNS integer
8393LANGUAGE plpgsql
8494AS $$
@@ -90,13 +100,14 @@ BEGIN
90100END;
91101$$` ;
92102
103+ await testUtils . expectAstMatch ( 'INTO subquery' , sql ) ;
104+
93105 const parsed = parsePlPgSQLSync ( sql ) as unknown as PLpgSQLParseResult ;
94106 const deparsed = deparseSync ( parsed ) ;
95-
96107 expect ( deparsed ) . toMatchSnapshot ( ) ;
97108 } ) ;
98109
99- it ( 'should handle INTO with CTE' , ( ) => {
110+ it ( 'should handle INTO with CTE' , async ( ) => {
100111 const sql = `CREATE FUNCTION test_into_cte() RETURNS integer
101112LANGUAGE plpgsql
102113AS $$
@@ -111,13 +122,14 @@ BEGIN
111122END;
112123$$` ;
113124
125+ await testUtils . expectAstMatch ( 'INTO CTE' , sql ) ;
126+
114127 const parsed = parsePlPgSQLSync ( sql ) as unknown as PLpgSQLParseResult ;
115128 const deparsed = deparseSync ( parsed ) ;
116-
117129 expect ( deparsed ) . toMatchSnapshot ( ) ;
118130 } ) ;
119131
120- it ( 'should handle INTO with UNION' , ( ) => {
132+ it ( 'should handle INTO with UNION' , async ( ) => {
121133 const sql = `CREATE FUNCTION test_into_union() RETURNS integer
122134LANGUAGE plpgsql
123135AS $$
@@ -133,13 +145,14 @@ BEGIN
133145END;
134146$$` ;
135147
148+ await testUtils . expectAstMatch ( 'INTO UNION' , sql ) ;
149+
136150 const parsed = parsePlPgSQLSync ( sql ) as unknown as PLpgSQLParseResult ;
137151 const deparsed = deparseSync ( parsed ) ;
138-
139152 expect ( deparsed ) . toMatchSnapshot ( ) ;
140153 } ) ;
141154
142- it ( 'should handle INTO with quoted identifiers' , ( ) => {
155+ it ( 'should handle INTO with quoted identifiers' , async ( ) => {
143156 const sql = `CREATE FUNCTION test_into_quoted() RETURNS text
144157LANGUAGE plpgsql
145158AS $$
@@ -151,13 +164,14 @@ BEGIN
151164END;
152165$$` ;
153166
167+ await testUtils . expectAstMatch ( 'INTO quoted' , sql ) ;
168+
154169 const parsed = parsePlPgSQLSync ( sql ) as unknown as PLpgSQLParseResult ;
155170 const deparsed = deparseSync ( parsed ) ;
156-
157171 expect ( deparsed ) . toMatchSnapshot ( ) ;
158172 } ) ;
159173
160- it ( 'should handle INTO with dollar-quoted strings' , ( ) => {
174+ it ( 'should handle INTO with dollar-quoted strings' , async ( ) => {
161175 const sql = `CREATE FUNCTION test_into_dollar_quote() RETURNS text
162176LANGUAGE plpgsql
163177AS $$
@@ -169,13 +183,14 @@ BEGIN
169183END;
170184$$` ;
171185
186+ await testUtils . expectAstMatch ( 'INTO dollar-quote' , sql ) ;
187+
172188 const parsed = parsePlPgSQLSync ( sql ) as unknown as PLpgSQLParseResult ;
173189 const deparsed = deparseSync ( parsed ) ;
174-
175190 expect ( deparsed ) . toMatchSnapshot ( ) ;
176191 } ) ;
177192
178- it ( 'should handle INTO STRICT' , ( ) => {
193+ it ( 'should handle INTO STRICT' , async ( ) => {
179194 const sql = `CREATE FUNCTION test_into_strict() RETURNS integer
180195LANGUAGE plpgsql
181196AS $$
@@ -187,16 +202,17 @@ BEGIN
187202END;
188203$$` ;
189204
205+ await testUtils . expectAstMatch ( 'INTO STRICT' , sql ) ;
206+
190207 const parsed = parsePlPgSQLSync ( sql ) as unknown as PLpgSQLParseResult ;
191208 const deparsed = deparseSync ( parsed ) ;
192-
193209 expect ( deparsed ) . toMatchSnapshot ( ) ;
194210 expect ( deparsed ) . toContain ( 'STRICT' ) ;
195211 } ) ;
196212 } ) ;
197213
198214 describe ( 'Record field qualification (recfield)' , ( ) => {
199- it ( 'should qualify record fields with parent record name in triggers' , ( ) => {
215+ it ( 'should qualify record fields with parent record name in triggers' , async ( ) => {
200216 const sql = `CREATE FUNCTION test_trigger() RETURNS trigger
201217LANGUAGE plpgsql
202218AS $$
@@ -208,13 +224,14 @@ BEGIN
208224END;
209225$$` ;
210226
227+ await testUtils . expectAstMatch ( 'recfield trigger' , sql ) ;
228+
211229 const parsed = parsePlPgSQLSync ( sql ) as unknown as PLpgSQLParseResult ;
212230 const deparsed = deparseSync ( parsed ) ;
213-
214231 expect ( deparsed ) . toMatchSnapshot ( ) ;
215232 } ) ;
216233
217- it ( 'should handle OLD and NEW record references' , ( ) => {
234+ it ( 'should handle OLD and NEW record references' , async ( ) => {
218235 const sql = `CREATE FUNCTION test_trigger_old_new() RETURNS trigger
219236LANGUAGE plpgsql
220237AS $$
@@ -226,13 +243,14 @@ BEGIN
226243END;
227244$$` ;
228245
246+ await testUtils . expectAstMatch ( 'recfield OLD NEW' , sql ) ;
247+
229248 const parsed = parsePlPgSQLSync ( sql ) as unknown as PLpgSQLParseResult ;
230249 const deparsed = deparseSync ( parsed ) ;
231-
232250 expect ( deparsed ) . toMatchSnapshot ( ) ;
233251 } ) ;
234252
235- it ( 'should handle record field assignment' , ( ) => {
253+ it ( 'should handle record field assignment' , async ( ) => {
236254 const sql = `CREATE FUNCTION test_record_assign() RETURNS trigger
237255LANGUAGE plpgsql
238256AS $$
@@ -244,13 +262,14 @@ BEGIN
244262END;
245263$$` ;
246264
265+ await testUtils . expectAstMatch ( 'recfield assignment' , sql ) ;
266+
247267 const parsed = parsePlPgSQLSync ( sql ) as unknown as PLpgSQLParseResult ;
248268 const deparsed = deparseSync ( parsed ) ;
249-
250269 expect ( deparsed ) . toMatchSnapshot ( ) ;
251270 } ) ;
252271
253- it ( 'should handle SELECT INTO with record fields' , ( ) => {
272+ it ( 'should handle SELECT INTO with record fields' , async ( ) => {
254273 const sql = `CREATE FUNCTION test_select_into_record() RETURNS trigger
255274LANGUAGE plpgsql
256275AS $$
@@ -260,13 +279,14 @@ BEGIN
260279END;
261280$$` ;
262281
282+ await testUtils . expectAstMatch ( 'recfield SELECT INTO' , sql ) ;
283+
263284 const parsed = parsePlPgSQLSync ( sql ) as unknown as PLpgSQLParseResult ;
264285 const deparsed = deparseSync ( parsed ) ;
265-
266286 expect ( deparsed ) . toMatchSnapshot ( ) ;
267287 } ) ;
268288
269- it ( 'should handle custom record types' , ( ) => {
289+ it ( 'should handle custom record types' , async ( ) => {
270290 const sql = `CREATE FUNCTION test_custom_record() RETURNS void
271291LANGUAGE plpgsql
272292AS $$
@@ -279,15 +299,16 @@ BEGIN
279299END;
280300$$` ;
281301
302+ await testUtils . expectAstMatch ( 'recfield custom record' , sql ) ;
303+
282304 const parsed = parsePlPgSQLSync ( sql ) as unknown as PLpgSQLParseResult ;
283305 const deparsed = deparseSync ( parsed ) ;
284-
285306 expect ( deparsed ) . toMatchSnapshot ( ) ;
286307 } ) ;
287308 } ) ;
288309
289310 describe ( 'combined scenarios' , ( ) => {
290- it ( 'should handle PERFORM with record fields' , ( ) => {
311+ it ( 'should handle PERFORM with record fields' , async ( ) => {
291312 const sql = `CREATE FUNCTION test_perform_record() RETURNS trigger
292313LANGUAGE plpgsql
293314AS $$
@@ -297,14 +318,15 @@ BEGIN
297318END;
298319$$` ;
299320
321+ await testUtils . expectAstMatch ( 'combined PERFORM recfield' , sql ) ;
322+
300323 const parsed = parsePlPgSQLSync ( sql ) as unknown as PLpgSQLParseResult ;
301324 const deparsed = deparseSync ( parsed ) ;
302-
303325 expect ( deparsed ) . toMatchSnapshot ( ) ;
304326 expect ( deparsed ) . not . toMatch ( / P E R F O R M \s + S E L E C T / i) ;
305327 } ) ;
306328
307- it ( 'should handle SELECT INTO with subquery and record fields' , ( ) => {
329+ it ( 'should handle SELECT INTO with subquery and record fields' , async ( ) => {
308330 const sql = `CREATE FUNCTION test_complex_trigger() RETURNS trigger
309331LANGUAGE plpgsql
310332AS $$
@@ -319,9 +341,10 @@ BEGIN
319341END;
320342$$` ;
321343
344+ await testUtils . expectAstMatch ( 'combined INTO recfield' , sql ) ;
345+
322346 const parsed = parsePlPgSQLSync ( sql ) as unknown as PLpgSQLParseResult ;
323347 const deparsed = deparseSync ( parsed ) ;
324-
325348 expect ( deparsed ) . toMatchSnapshot ( ) ;
326349 } ) ;
327350 } ) ;
0 commit comments