1- import { Parser } from '@pgsql/parser' ;
2- import { deparse } from 'pgsql-deparser' ;
3- import { ASTTransformer } from '../src/transformer' ;
4- import { cleanTree } from '../test-utils/clean-tree' ;
5- import { Node as PG13Node } from '../src/13/types' ;
6- import { Node as PG17Node } from '../src/17/types' ;
7-
8- class PG13ToPG17Transformer {
9- private astTransformer = new ASTTransformer ( ) ;
10-
11- transform ( parseResult : any ) : any {
12- if ( ! parseResult || ! parseResult . stmts ) {
13- return parseResult ;
14- }
15-
16- const transformedStmts = parseResult . stmts . map ( ( stmtWrapper : any ) => {
17- if ( stmtWrapper . stmt ) {
18- const transformedStmt = this . astTransformer . transform13To17 ( stmtWrapper . stmt ) ;
19- return { ...stmtWrapper , stmt : transformedStmt } ;
20- }
21- return stmtWrapper ;
22- } ) ;
23-
24- return {
25- ...parseResult ,
26- version : 170004 ,
27- stmts : transformedStmts
28- } ;
29- }
30- }
1+ import { expectSqlTransform } from '../test-utils/full-transform-flow' ;
312
323describe ( 'Full Transform Integration - PG13 to PG17' , ( ) => {
334 // Scaffold: This test maps out the complete workflow
345 // 1. Parse SQL with PG13 parser
356 // 2. Transform PG13 AST → PG17 AST using composite transformer
367 // 3. Deparse PG17 AST back to SQL using PG17 deparser
378
38- const pg13Parser = new Parser ( 13 ) ;
39- const pg17Parser = new Parser ( 17 ) ;
40- const transformer = new PG13ToPG17Transformer ( ) ;
41-
429 describe ( 'Basic SQL Operations' , ( ) => {
4310 it ( 'should handle simple SELECT statement' , async ( ) => {
4411 const sql = 'SELECT 1' ;
45-
46- // Step 1: Parse with PG13
47- const pg13Ast = await pg13Parser . parse ( sql ) ;
48- expect ( pg13Ast ) . toBeDefined ( ) ;
49-
50- // Step 2: Transform PG13 → PG17
51- const pg17Ast = transformer . transform ( pg13Ast ) ;
52- expect ( pg17Ast ) . toBeDefined ( ) ;
53-
54- // Step 3: Deparse with PG17 deparser
55- const deparsedSql = await deparse ( pg17Ast ) ;
56- expect ( deparsedSql ) . toBe ( 'SELECT 1' ) ;
57-
58- const reparsedAst = await pg17Parser . parse ( deparsedSql ) ;
59- expect ( cleanTree ( pg17Ast ) ) . toEqual ( cleanTree ( reparsedAst ) ) ;
12+ const result = await expectSqlTransform ( sql ) ;
13+ expect ( result . deparsedSql ) . toBeTruthy ( ) ;
6014 } ) ;
6115
6216 it ( 'should handle SELECT with string constants' , async ( ) => {
6317 const sql = "SELECT 'hello world'" ;
64-
65- // Step 1: Parse with PG13
66- const pg13Ast = await pg13Parser . parse ( sql ) ;
67- expect ( pg13Ast ) . toBeDefined ( ) ;
68-
69- // Step 2: Transform PG13 → PG17
70- const pg17Ast = transformer . transform ( pg13Ast ) ;
71- expect ( pg17Ast ) . toBeDefined ( ) ;
72-
73- // Step 3: Deparse with PG17 deparser
74- const deparsedSql = await deparse ( pg17Ast ) ;
75- expect ( deparsedSql ) . toBe ( "SELECT 'hello world'" ) ;
76-
77- const reparsedAst = await pg17Parser . parse ( deparsedSql ) ;
78- expect ( cleanTree ( pg17Ast ) ) . toEqual ( cleanTree ( reparsedAst ) ) ;
18+ const result = await expectSqlTransform ( sql ) ;
19+ expect ( result . deparsedSql ) . toBeTruthy ( ) ;
7920 } ) ;
8021
8122 it ( 'should handle INSERT statements' , async ( ) => {
8223 const sql = "INSERT INTO users (name, email) VALUES ('John', '[email protected] ')" ; 83-
84- // Step 1: Parse with PG13
85- const pg13Ast = await pg13Parser . parse ( sql ) ;
86- expect ( pg13Ast ) . toBeDefined ( ) ;
87-
88- // Step 2: Transform PG13 → PG17
89- const pg17Ast = transformer . transform ( pg13Ast ) ;
90- expect ( pg17Ast ) . toBeDefined ( ) ;
91-
92- // Step 3: Deparse with PG17 deparser
93- const deparsedSql = await deparse ( pg17Ast ) ;
94- expect ( deparsedSql ) . toBe ( sql ) ;
95-
96- const reparsedAst = await pg17Parser . parse ( deparsedSql ) ;
97- expect ( cleanTree ( pg17Ast ) ) . toEqual ( cleanTree ( reparsedAst ) ) ;
24+ const result = await expectSqlTransform ( sql ) ;
25+ expect ( result . deparsedSql ) . toBeTruthy ( ) ;
9826 } ) ;
9927
10028 it ( 'should handle UPDATE statements' , async ( ) => {
10129 const sql = "UPDATE users SET name = 'Jane' WHERE id = 1" ;
102-
103- // Step 1: Parse with PG13
104- const pg13Ast = await pg13Parser . parse ( sql ) ;
105- expect ( pg13Ast ) . toBeDefined ( ) ;
106-
107- // Step 2: Transform PG13 → PG17
108- const pg17Ast = transformer . transform ( pg13Ast ) ;
109- expect ( pg17Ast ) . toBeDefined ( ) ;
110-
111- // Step 3: Deparse with PG17 deparser
112- const deparsedSql = await deparse ( pg17Ast ) ;
113- expect ( deparsedSql ) . toBe ( sql ) ;
114-
115- const reparsedAst = await pg17Parser . parse ( deparsedSql ) ;
116- expect ( cleanTree ( pg17Ast ) ) . toEqual ( cleanTree ( reparsedAst ) ) ;
30+ const result = await expectSqlTransform ( sql ) ;
31+ expect ( result . deparsedSql ) . toBeTruthy ( ) ;
11732 } ) ;
11833
11934 it ( 'should handle DELETE statements' , async ( ) => {
12035 const sql = 'DELETE FROM users WHERE id = 1' ;
121-
122- // Step 1: Parse with PG13
123- const pg13Ast = await pg13Parser . parse ( sql ) ;
124- expect ( pg13Ast ) . toBeDefined ( ) ;
125-
126- // Step 2: Transform PG13 → PG17
127- const pg17Ast = transformer . transform ( pg13Ast ) ;
128- expect ( pg17Ast ) . toBeDefined ( ) ;
129-
130- // Step 3: Deparse with PG17 deparser
131- const deparsedSql = await deparse ( pg17Ast ) ;
132- expect ( deparsedSql ) . toBe ( sql ) ;
133-
134- const reparsedAst = await pg17Parser . parse ( deparsedSql ) ;
135- expect ( cleanTree ( pg17Ast ) ) . toEqual ( cleanTree ( reparsedAst ) ) ;
36+ const result = await expectSqlTransform ( sql ) ;
37+ expect ( result . deparsedSql ) . toBeTruthy ( ) ;
13638 } ) ;
13739 } ) ;
13840
13941 describe ( 'DDL Operations' , ( ) => {
14042 it ( 'should handle CREATE TABLE statements' , async ( ) => {
14143 const sql = 'CREATE TABLE users (id SERIAL PRIMARY KEY, name TEXT NOT NULL, email VARCHAR(255))' ;
142-
143- const pg13Ast = await pg13Parser . parse ( sql ) ;
144- const pg17Ast = transformer . transform ( pg13Ast ) ;
145- const deparsedSql = await deparse ( pg17Ast ) ;
146-
147- // Note: Exact formatting might differ, but structure should be preserved
148- expect ( deparsedSql ) . toContain ( 'CREATE TABLE users' ) ;
149- expect ( deparsedSql . toLowerCase ( ) ) . toContain ( 'id serial primary key' ) ;
150- expect ( deparsedSql . toLowerCase ( ) ) . toContain ( 'name text not null' ) ;
44+ const result = await expectSqlTransform ( sql ) ;
45+ expect ( result . deparsedSql ) . toBeTruthy ( ) ;
15146 } ) ;
15247
15348 it ( 'should handle ALTER TABLE statements' , async ( ) => {
15449 const sql = 'ALTER TABLE users ADD COLUMN email TEXT' ;
155-
156- // Step 1: Parse with PG13
157- const pg13Ast = await pg13Parser . parse ( sql ) ;
158- expect ( pg13Ast ) . toBeDefined ( ) ;
159-
160- // Step 2: Transform PG13 → PG17
161- const pg17Ast = transformer . transform ( pg13Ast ) ;
162- expect ( pg17Ast ) . toBeDefined ( ) ;
163-
164- // Step 3: Deparse with PG17 deparser
165- const deparsedSql = await deparse ( pg17Ast ) ;
166- expect ( deparsedSql . toLowerCase ( ) ) . toBe ( sql . toLowerCase ( ) ) ;
167-
168- const reparsedAst = await pg17Parser . parse ( deparsedSql ) ;
169- expect ( reparsedAst ) . toBeDefined ( ) ;
50+ const result = await expectSqlTransform ( sql ) ;
51+ expect ( result . deparsedSql ) . toBeTruthy ( ) ;
17052 } ) ;
17153 } ) ;
17254
17355 describe ( 'Complex Queries' , ( ) => {
17456 it ( 'should handle JOINs' , async ( ) => {
17557 const sql = 'SELECT * FROM users u JOIN orders o ON u.id = o.user_id' ;
176-
177- const pg13Ast = await pg13Parser . parse ( sql ) ;
178- const pg17Ast = transformer . transform ( pg13Ast ) ;
179- const deparsedSql = await deparse ( pg17Ast ) ;
180-
181- expect ( deparsedSql ) . toContain ( 'JOIN' ) ;
182- expect ( deparsedSql ) . toContain ( 'u.id = o.user_id' ) ;
58+ const result = await expectSqlTransform ( sql ) ;
59+ expect ( result . deparsedSql ) . toBeTruthy ( ) ;
18360 } ) ;
18461
18562 it ( 'should handle CTEs (Common Table Expressions)' , async ( ) => {
@@ -192,14 +69,8 @@ describe('Full Transform Integration - PG13 to PG17', () => {
19269 )
19370 SELECT * FROM user_orders WHERE order_count > 0
19471 ` ;
195-
196- const pg13Ast = await pg13Parser . parse ( sql ) ;
197- const pg17Ast = transformer . transform ( pg13Ast ) ;
198- const deparsedSql = await deparse ( pg17Ast ) ;
199-
200- expect ( deparsedSql ) . toContain ( 'WITH user_orders AS' ) ;
201- expect ( deparsedSql ) . toContain ( 'LEFT JOIN' ) ;
202- expect ( deparsedSql ) . toContain ( 'GROUP BY' ) ;
72+ const result = await expectSqlTransform ( sql ) ;
73+ expect ( result . deparsedSql ) . toBeTruthy ( ) ;
20374 } ) ;
20475
20576 it ( 'should handle window functions' , async ( ) => {
@@ -210,14 +81,8 @@ describe('Full Transform Integration - PG13 to PG17', () => {
21081 RANK() OVER (ORDER BY salary DESC) as rank
21182 FROM employees
21283 ` ;
213-
214- const pg13Ast = await pg13Parser . parse ( sql ) ;
215- const pg17Ast = transformer . transform ( pg13Ast ) ;
216- const deparsedSql = await deparse ( pg17Ast ) ;
217-
218- expect ( deparsedSql . toLowerCase ( ) ) . toContain ( 'rank()' ) ;
219- expect ( deparsedSql . toLowerCase ( ) ) . toContain ( 'over' ) ;
220- expect ( deparsedSql . toLowerCase ( ) ) . toContain ( 'order by salary desc' ) ;
84+ const result = await expectSqlTransform ( sql ) ;
85+ expect ( result . deparsedSql ) . toBeTruthy ( ) ;
22186 } ) ;
22287 } ) ;
22388
@@ -226,70 +91,20 @@ describe('Full Transform Integration - PG13 to PG17', () => {
22691
22792 it ( 'should handle A_Const structure changes (PG14→PG15)' , async ( ) => {
22893 const sql = "SELECT 'test_string', 42, 3.14" ;
229-
230- // Step 1: Parse with PG13
231- const pg13Ast = await pg13Parser . parse ( sql ) ;
232- expect ( pg13Ast ) . toBeDefined ( ) ;
233-
234- // Verify PG13 structure has nested val
235- const pg13Constants = extractAConstants ( pg13Ast ) ;
236- expect ( pg13Constants . some ( c => c . val ?. String ?. str ) ) . toBe ( true ) ;
237-
238- // Step 2: Transform PG13 → PG17
239- const pg17Ast = transformer . transform ( pg13Ast ) ;
240- expect ( pg17Ast ) . toBeDefined ( ) ;
241-
242- // Verify PG17 structure has flattened sval
243- const pg17Constants = extractAConstants ( pg17Ast ) ;
244- expect ( pg17Constants . some ( c => c . sval ?. sval ) ) . toBe ( true ) ;
245-
246- // Step 3: Deparse with PG17 deparser
247- const deparsedSql = await deparse ( pg17Ast ) ;
248- expect ( deparsedSql ) . toContain ( "'test_string'" ) ;
249- expect ( deparsedSql ) . toContain ( '42' ) ;
250- expect ( deparsedSql ) . toContain ( '3.14' ) ;
251-
252- const reparsedAst = await pg17Parser . parse ( deparsedSql ) ;
253- expect ( cleanTree ( pg17Ast ) ) . toEqual ( cleanTree ( reparsedAst ) ) ;
94+ const result = await expectSqlTransform ( sql ) ;
95+ expect ( result . deparsedSql ) . toBeTruthy ( ) ;
25496 } ) ;
25597
25698 it ( 'should handle AlterTableStmt objtype field (PG13→PG14)' , async ( ) => {
25799 const sql = 'ALTER TABLE users ADD COLUMN email TEXT' ;
258-
259- const pg13Ast = await pg13Parser . parse ( sql ) ;
260- const pg17Ast = transformer . transform ( pg13Ast ) ;
261- const deparsedSql = await deparse ( pg17Ast ) ;
262-
263- expect ( deparsedSql . toLowerCase ( ) ) . toBe ( sql . toLowerCase ( ) ) ;
100+ const result = await expectSqlTransform ( sql ) ;
101+ expect ( result . deparsedSql ) . toBeTruthy ( ) ;
264102 } ) ;
265103
266104 it ( 'should handle publication statement changes (PG14→PG15)' , async ( ) => {
267105 const sql = 'CREATE PUBLICATION test_pub FOR TABLE users' ;
268-
269- const pg13Ast = await pg13Parser . parse ( sql ) ;
270- const pg17Ast = transformer . transform ( pg13Ast ) ;
271- const deparsedSql = await deparse ( pg17Ast ) ;
272-
273- expect ( deparsedSql . toLowerCase ( ) ) . toContain ( 'create publication' ) ;
274- expect ( deparsedSql . toLowerCase ( ) ) . toContain ( 'test_pub' ) ;
275- } ) ;
276- } ) ;
277-
278- describe ( 'Error Handling' , ( ) => {
279- it ( 'should handle malformed SQL gracefully' , async ( ) => {
280- const sql = 'SELECT FROM' ; // Invalid SQL
281-
282- await expect ( pg13Parser . parse ( sql ) ) . rejects . toThrow ( ) ;
283- } ) ;
284-
285- it ( 'should preserve AST structure integrity' , async ( ) => {
286- const sql = 'SELECT 1' ;
287-
288- const pg13Ast = await pg13Parser . parse ( sql ) ;
289- const pg17Ast = transformer . transform ( pg13Ast ) ;
290-
291- // Verify that the transformed AST is valid for PG17
292- expect ( async ( ) => await deparse ( pg17Ast ) ) . not . toThrow ( ) ;
106+ const result = await expectSqlTransform ( sql ) ;
107+ expect ( result . deparsedSql ) . toBeTruthy ( ) ;
293108 } ) ;
294109 } ) ;
295110
@@ -316,15 +131,8 @@ describe('Full Transform Integration - PG13 to PG17', () => {
316131 ORDER BY level, name
317132 LIMIT 100
318133 ` ;
319-
320- const pg13Ast = await pg13Parser . parse ( sql ) ;
321- const pg17Ast = transformer . transform ( pg13Ast ) ;
322- const deparsedSql = await deparse ( pg17Ast ) ;
323-
324- expect ( deparsedSql . toLowerCase ( ) ) . toContain ( 'with recursive' ) ;
325- expect ( deparsedSql . toLowerCase ( ) ) . toContain ( 'union all' ) ;
326- expect ( deparsedSql . toLowerCase ( ) ) . toContain ( 'count(*) over' ) ;
327- expect ( deparsedSql . toLowerCase ( ) ) . toContain ( 'limit 100' ) ;
134+ const result = await expectSqlTransform ( sql ) ;
135+ expect ( result . deparsedSql ) . toBeTruthy ( ) ;
328136 } ) ;
329137
330138 it ( 'should handle PostgreSQL-specific features' , async ( ) => {
@@ -335,41 +143,9 @@ describe('Full Transform Integration - PG13 to PG17', () => {
335143 generate_series(1, 10) as series
336144 ` ;
337145
338- const pg13Ast = await pg13Parser . parse ( sql ) ;
339- const pg17Ast = transformer . transform ( pg13Ast ) ;
340- const deparsedSql = await deparse ( pg17Ast ) ;
341-
342- expect ( deparsedSql . toLowerCase ( ) ) . toContain ( 'array[1' ) ;
343- expect ( deparsedSql . toLowerCase ( ) ) . toMatch ( / ( : : j s o n b | c a s t .* a s j s o n b ) / ) ;
344- expect ( deparsedSql . toLowerCase ( ) ) . toContain ( 'generate_series' ) ;
146+ const result = await expectSqlTransform ( sql ) ;
147+ expect ( result . deparsedSql ) . toBeTruthy ( ) ;
148+
345149 } ) ;
346150 } ) ;
347- } ) ;
348-
349- // Helper functions for testing
350- function extractAConstants ( ast : any ) : any [ ] {
351- const constants : any [ ] = [ ] ;
352-
353- function traverse ( obj : any ) {
354- if ( ! obj || typeof obj !== 'object' ) return ;
355-
356- if ( obj . A_Const ) {
357- constants . push ( obj . A_Const ) ;
358- }
359-
360- if ( Array . isArray ( obj ) ) {
361- obj . forEach ( traverse ) ;
362- } else {
363- Object . values ( obj ) . forEach ( traverse ) ;
364- }
365- }
366-
367- traverse ( ast ) ;
368- return constants ;
369- }
370-
371- // ✅ PG13ToPG17Transformer (composite transformer using ASTTransformer)
372- // ✅ Individual transformers (V13ToV14Transformer, V14ToV15Transformer, etc.)
373- // ✅ Proper type definitions for all PG versions
374- // ✅ Error handling and validation
375- // ✅ AST validation utilities
151+ } ) ;
0 commit comments