grammar: admit WITH inside subqueries / CTE bodies (ADR-0032 §10.3)

ADR-0032 §10.3 says cte_bindings lives on the scope frame, with
inner subqueries free to declare their own CTEs that shadow outer
ones. The grammar didn't actually admit nested WITH inside
SQL_SELECT_COMPOUND — a real ADR-vs-implementation gap.

Closes the gap by making SQL_SELECT_COMPOUND a Choice between a
WITH-prefixed form and a plain form. The naive Optional-prefix
approach silently broke the paren-vs-subquery dispatch in
sql_expr.rs's PAREN_GROUP: Optional matches 0 bytes, committing
the Seq, so SELECT_CORE's NoMatch on `(a + b)` became Failed and
the Choice couldn't fall through to or_expr. The Choice-fronted
form keeps the fast NoMatch on non-WITH non-SELECT first tokens.

Side effect: scalar subquery / IN / EXISTS / derived-table
bodies now admit a leading WITH too, which matches standard SQL.

Updated two tests that were guarding the old `(WITH …)` rejection
behavior. Added one new harvest test exercising nested-WITH inside
a CTE body — the harvest's `expand_binding` mechanism already
handled the data correctly; the grammar gap was the sole blocker.

Test totals: 1414 → 1415 passing (+1 nested-with-in-cte test).
Clippy clean.
This commit is contained in:
claude@clouddev1
2026-05-20 20:34:42 +00:00
parent dd37a1cbfc
commit fd259048da
3 changed files with 78 additions and 17 deletions
+8 -9
View File
@@ -706,17 +706,16 @@ mod tests {
#[test]
fn scalar_subquery_dispatches_against_paren_group() {
// Both `(or_expr)` and `(SELECT …)` start with `(`.
// The ADR factors the `(` and the first inside token
// discriminates — `SELECT` → subquery, anything else
// → expression. (Per ADR-0032 §1 / §9, subqueries
// recurse through `SQL_SELECT_COMPOUND` which omits
// the outer `WITH` — so `(WITH …)` is NOT admitted as
// a scalar subquery; that form is only valid at
// statement top-level.)
// Both `(or_expr)` and `(SELECT …)` / `(WITH …)` start
// with `(`. The ADR factors the `(` and the first inside
// token discriminates — `SELECT` / `WITH` → subquery,
// anything else → expression. Per ADR-0032 §10.3, a
// subquery body may declare its own CTEs (shadowing
// outer ones), so `(WITH …)` IS admitted as a scalar
// subquery shape.
good("(a + 1)");
good("(select 1)");
bad("(with x as (select 1) select * from x)");
good("(with x as (select 1) select * from x)");
}
#[test]