Skip to content

parser: support common table expression (WITH clause) - #23

Open
LordofAvernus wants to merge 1 commit into
sjjian:release-4.0.2from
LordofAvernus:feat/mysql-cte-support
Open

parser: support common table expression (WITH clause)#23
LordofAvernus wants to merge 1 commit into
sjjian:release-4.0.2from
LordofAvernus:feat/mysql-cte-support

Conversation

@LordofAvernus

Copy link
Copy Markdown

What problem does this PR solve?

WITH ... AS (...) SELECT/INSERT/UPDATE/DELETE (MySQL 8.0 CTE) fails to parse on the
current release-4.0.2, e.g.:

WITH cte AS (SELECT 1 AS id) SELECT * FROM cte
=> line 1 column 4 near "WITH cte AS (SELECT 1 AS id) SELECT * FROM cte"

Downstream consumers (SQLE) therefore cannot build an AST for CTE statements and end up
misclassifying them (e.g. a CTE + SELECT being reported as DDL instead of DQL).

What is changed and how it works

  • ast: add CommonTableExpression and WithClause (IsRecursive, CTEs), with
    Restore and Accept implemented so the clause round-trips.
  • ast: add an optional With *WithClause field to SelectStmt, InsertStmt,
    UpdateStmt and DeleteStmt; Restore/Accept of each statement handle it when
    non-nil, so behaviour is unchanged for statements without a WITH clause.
  • parser.y: split each DML rule into a *NoWith variant and let the
    WithClause-prefixed rule reuse it; add WithClause, WithList, CommonTableExpr,
    IdentList, IdentListWithParenOpt.
  • misc.go / parser.y: add the RECURSIVE token.
  • parser.go: regenerated from parser.y.

Compatibility note

RECURSIVE is introduced as a reserved keyword, matching MySQL 8.0 and upstream
TiDB's parser. As a consequence recursive can no longer be used as a bare identifier:

CREATE TABLE t (recursive int)   -- parsed before, rejected now
SELECT recursive FROM t          -- parsed before, rejected now

If keeping recursive usable as an identifier matters more than matching MySQL 8.0
behaviour here, please say so and I will rework it as an unreserved keyword.

Tests

  • New cte_test.go covering SELECT/INSERT/UPDATE/DELETE with a WITH clause,
    column name lists, multiple CTEs, WITH RECURSIVE, invalid inputs, plus AST-level
    assertions. Restore round-trip is verified through the existing RunTest helper.
  • Full suite (go test ./...) shows no new failures: the 3 pre-existing failures
    (TestKeywordConsistent, TestscanString, TestDMLStmt) and the ast vet failure
    are identical on b6199b7 without this change.

Made with Cursor

MySQL 8.0 CTE statements (`WITH ... AS (...) SELECT/INSERT/UPDATE/DELETE`)
previously failed to parse, so downstream consumers could not build an AST
for them and had to fall back to treating such statements as unknown.

Add `ast.WithClause` / `ast.CommonTableExpression` and attach an optional
`With` field to SelectStmt, InsertStmt, UpdateStmt and DeleteStmt, with
Restore and Accept support so the clause round-trips.

Grammar-wise each DML rule is split into a `*NoWith` variant that the
`WithClause`-prefixed rule reuses, and `RECURSIVE` is introduced as a
reserved keyword, matching MySQL 8.0 and upstream TiDB.

Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant