From eddb3325aea4e836b4ed070f6fc8d86e8fc34ec1 Mon Sep 17 00:00:00 2001 From: Roee Malis Date: Wed, 1 Jul 2026 23:26:29 +0300 Subject: [PATCH 1/2] fix --- pegjs/sqlite.pegjs | 2 +- test/sqlite.spec.js | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/pegjs/sqlite.pegjs b/pegjs/sqlite.pegjs index 399b1258d..7f61ee162 100644 --- a/pegjs/sqlite.pegjs +++ b/pegjs/sqlite.pegjs @@ -1316,7 +1316,7 @@ with_clause } cte_definition - = name:(literal_string / ident_name / table_name) __ columns:cte_column_definition? __ KW_AS __ LPAREN __ stmt:union_stmt __ RPAREN { + = name:(literal_string / ident_name / table_name) __ columns:cte_column_definition? __ KW_AS __ LPAREN __ stmt:(value_clause / union_stmt) __ RPAREN { if (typeof name === 'string') name = { type: 'default', value: name } if (name.table) name = { type: 'default', value: name.table } return { name, stmt, columns }; diff --git a/test/sqlite.spec.js b/test/sqlite.spec.js index aaf48514c..327282b19 100644 --- a/test/sqlite.spec.js +++ b/test/sqlite.spec.js @@ -342,4 +342,16 @@ describe('sqlite', () => { 'SELECT * FROM "t1" CROSS JOIN "t2" INNER JOIN "t3" ON "t2"."id" = "t3"."id"' ); }); + + it('should support VALUES in a CTE', () => { + const sql = `WITH table1 (name, match_strategy) AS ( + VALUES ('Name1', 'exact'), ('Name2', 'exact') + ) + SELECT a.name + FROM table2 a + JOIN table1 m ON lower(a.name) = lower(m.name)`; + expect(getParsedSql(sql)).to.be.equal( + `WITH "table1"("name", "match_strategy") AS (VALUES ('Name1','exact'), ('Name2','exact')) SELECT "a"."name" FROM "table2" AS "a" INNER JOIN "table1" AS "m" ON lower("a"."name") = lower("m"."name")` + ); + }); }) From 50c1a4866b0d36463ff590f621c31e0ac29d5c9d Mon Sep 17 00:00:00 2001 From: Roee Malis Date: Wed, 1 Jul 2026 23:33:49 +0300 Subject: [PATCH 2/2] better tesst --- test/sqlite.spec.js | 72 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 67 insertions(+), 5 deletions(-) diff --git a/test/sqlite.spec.js b/test/sqlite.spec.js index 327282b19..20d3ffbf1 100644 --- a/test/sqlite.spec.js +++ b/test/sqlite.spec.js @@ -344,14 +344,76 @@ describe('sqlite', () => { }); it('should support VALUES in a CTE', () => { - const sql = `WITH table1 (name, match_strategy) AS ( - VALUES ('Name1', 'exact'), ('Name2', 'exact') + const sql = `WITH cte1 (col1, col2) AS ( + VALUES ('Name1', 'type1'), ('Name2', 'type1') ) SELECT a.name - FROM table2 a - JOIN table1 m ON lower(a.name) = lower(m.name)`; + FROM table1 a + JOIN cte1 c ON lower(a.name) = lower(c.col1)`; expect(getParsedSql(sql)).to.be.equal( - `WITH "table1"("name", "match_strategy") AS (VALUES ('Name1','exact'), ('Name2','exact')) SELECT "a"."name" FROM "table2" AS "a" INNER JOIN "table1" AS "m" ON lower("a"."name") = lower("m"."name")` + `WITH "cte1"("col1", "col2") AS (VALUES ('Name1','type1'), ('Name2','type1')) SELECT "a"."name" FROM "table1" AS "a" INNER JOIN "cte1" AS "c" ON lower("a"."name") = lower("c"."col1")` + ); + }); + + it('should support single-column VALUES in a CTE', () => { + const sql = `WITH cte1 (col1) AS ( + VALUES ('Value1'), ('Value2') + ) + SELECT a.name FROM table1 a JOIN cte1 c ON lower(a.name) = lower(c.col1)`; + expect(getParsedSql(sql)).to.be.equal( + `WITH "cte1"("col1") AS (VALUES ('Value1'), ('Value2')) SELECT "a"."name" FROM "table1" AS "a" INNER JOIN "cte1" AS "c" ON lower("a"."name") = lower("c"."col1")` + ); + }); + + it('should support VALUES in a CTE without an explicit column list', () => { + const sql = `WITH cte1 AS ( + VALUES ('Value1'), ('Value2') + ) + SELECT * FROM cte1`; + expect(getParsedSql(sql)).to.be.equal( + `WITH "cte1" AS (VALUES ('Value1'), ('Value2')) SELECT * FROM "cte1"` + ); + }); + + it('should support VALUES in a CTE with a trailing semicolon', () => { + const sql = `WITH cte1 (col1) AS ( + VALUES ('Value1'), ('Value2') + ) + SELECT a.name FROM table1 a JOIN cte1 c ON lower(a.name) = lower(c.col1);`; + expect(getParsedSql(sql)).to.be.equal( + `WITH "cte1"("col1") AS (VALUES ('Value1'), ('Value2')) SELECT "a"."name" FROM "table1" AS "a" INNER JOIN "cte1" AS "c" ON lower("a"."name") = lower("c"."col1")` + ); + }); + + it('should support multiple CTEs with VALUES and SELECT', () => { + const sql = `WITH cte1 (col1) AS ( + VALUES ('Value1'), ('Value2') + ), + cte2 AS ( + SELECT name FROM table1 WHERE name IS NOT NULL + ) + SELECT f.name, c.col1 + FROM cte2 f + JOIN cte1 c ON lower(f.name) = lower(c.col1)`; + expect(getParsedSql(sql)).to.be.equal( + `WITH "cte1"("col1") AS (VALUES ('Value1'), ('Value2')), "cte2" AS (SELECT "name" FROM "table1" WHERE "name" IS NOT NULL) SELECT "f"."name", "c"."col1" FROM "cte2" AS "f" INNER JOIN "cte1" AS "c" ON lower("f"."name") = lower("c"."col1")` + ); + }); + + it('should support VALUES and UNION ALL CTEs in the same query', () => { + const sql = `WITH cte1 (col1, col2) AS ( + VALUES ('Name1', 'type1') + ), + cte2 AS ( + SELECT 'Name2' AS col1, 'type1' AS col2 + UNION ALL + SELECT 'Name3', 'type1' + ) + SELECT v.col1 FROM cte1 v + UNION ALL + SELECT u.col1 FROM cte2 u`; + expect(getParsedSql(sql)).to.be.equal( + `WITH "cte1"("col1", "col2") AS (VALUES ('Name1','type1')), "cte2" AS (SELECT 'Name2' AS "col1", 'type1' AS "col2" UNION ALL SELECT 'Name3', 'type1') SELECT "v"."col1" FROM "cte1" AS "v" UNION ALL SELECT "u"."col1" FROM "cte2" AS "u"` ); }); })