From 277bbc19ecb29e8ceefe6f5e726dbd543d8777ff Mon Sep 17 00:00:00 2001 From: psteinroe Date: Thu, 5 Mar 2026 10:28:33 +0100 Subject: [PATCH 1/2] fix(splitter): handle CTE inside CREATE VIEW statements The splitter incorrectly split `CREATE VIEW ... AS WITH cte AS (...) SELECT ...` into two statements at the `SELECT` keyword after the CTE closing parenthesis. When WITH_KW is excluded (inside CREATE), detect CTE patterns and parse them properly so the following DML keyword is not treated as a new statement. Closes #135 Co-Authored-By: Claude Opus 4.6 --- crates/pgls_statement_splitter/src/splitter/common.rs | 9 +++++++++ .../tests/data/create_table_with_storage__1.sql | 1 + .../tests/data/create_view_with_cte__1.sql | 1 + 3 files changed, 11 insertions(+) create mode 100644 crates/pgls_statement_splitter/tests/data/create_table_with_storage__1.sql create mode 100644 crates/pgls_statement_splitter/tests/data/create_view_with_cte__1.sql diff --git a/crates/pgls_statement_splitter/src/splitter/common.rs b/crates/pgls_statement_splitter/src/splitter/common.rs index 18703e592..d37a4bc7b 100644 --- a/crates/pgls_statement_splitter/src/splitter/common.rs +++ b/crates/pgls_statement_splitter/src/splitter/common.rs @@ -224,6 +224,15 @@ pub(crate) fn unknown(p: &mut Splitter, exclude: &[SyntaxKind]) -> SplitterResul begin_end(p)?; } }, + t if t == SyntaxKind::WITH_KW + && exclude.contains(&SyntaxKind::WITH_KW) + && matches!( + p.look_ahead(true), + SyntaxKind::IDENT | SyntaxKind::RECURSIVE_KW + ) => + { + cte(p)?; + } t => match at_statement_start(t, exclude) { Some(SyntaxKind::SELECT_KW) => { let prev = p.look_back(true); diff --git a/crates/pgls_statement_splitter/tests/data/create_table_with_storage__1.sql b/crates/pgls_statement_splitter/tests/data/create_table_with_storage__1.sql new file mode 100644 index 000000000..98ed6429c --- /dev/null +++ b/crates/pgls_statement_splitter/tests/data/create_table_with_storage__1.sql @@ -0,0 +1 @@ +CREATE TABLE foo (id int) WITH (fillfactor=70); diff --git a/crates/pgls_statement_splitter/tests/data/create_view_with_cte__1.sql b/crates/pgls_statement_splitter/tests/data/create_view_with_cte__1.sql new file mode 100644 index 000000000..1e0191929 --- /dev/null +++ b/crates/pgls_statement_splitter/tests/data/create_view_with_cte__1.sql @@ -0,0 +1 @@ +CREATE OR REPLACE VIEW profile_view AS WITH user_cte AS (SELECT * FROM accounts) SELECT * FROM user_cte; From 8eaee0d59dcba47b776d0f019a7a4771cb3aae0c Mon Sep 17 00:00:00 2001 From: psteinroe Date: Thu, 12 Mar 2026 23:12:12 +0100 Subject: [PATCH 2/2] progress --- crates/pgls_statement_splitter/src/splitter/common.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/pgls_statement_splitter/src/splitter/common.rs b/crates/pgls_statement_splitter/src/splitter/common.rs index d37a4bc7b..f98b5cf84 100644 --- a/crates/pgls_statement_splitter/src/splitter/common.rs +++ b/crates/pgls_statement_splitter/src/splitter/common.rs @@ -224,6 +224,9 @@ pub(crate) fn unknown(p: &mut Splitter, exclude: &[SyntaxKind]) -> SplitterResul begin_end(p)?; } }, + // When WITH_KW is excluded (e.g. inside CREATE) and followed by + // an identifier or RECURSIVE, it starts a CTE. Parse it as such + // so the following DML keyword is not treated as a new statement. t if t == SyntaxKind::WITH_KW && exclude.contains(&SyntaxKind::WITH_KW) && matches!(