Skip to content

feat(parser): wire mysql + sqlite parsers for INNER JOIN queries - #48

Merged
JagritGumber merged 3 commits into
mainfrom
feat/mysql-sqlite-parser-inner-join
Apr 20, 2026
Merged

feat(parser): wire mysql + sqlite parsers for INNER JOIN queries#48
JagritGumber merged 3 commits into
mainfrom
feat/mysql-sqlite-parser-inner-join

Conversation

@JagritGumber

@JagritGumber JagritGumber commented Apr 19, 2026

Copy link
Copy Markdown
Owner

Summary

Third PR in the JOIN series (C15c+d combined). Mirrors #47 for the remaining two dialects: mysql and sqlite.

Stacked on #47 (`feat/postgres-parser-inner-join`). When #47 merges, GitHub auto-retargets to main.

Why combined into one PR

C15c and C15d in the plan were separate PRs per dialect, but the changes are 90% identical (~50 LOC each, same pattern). Splitting would create two PRs that mostly duplicate each other for review effort. Bundling stays small (~100 LOC of dialect changes) and the symmetry is easy to review side-by-side.

Changes

Shared helper lifted into joins.rs:

  • `resolve_multi_table_columns(cols_part, sql, schema_tables, source_file)` — was in postgres.rs as a private function, now `pub` in joins.rs and shared across all three dialects
  • `JOIN_DETECT_RE` — also `pub` from joins.rs

mysql.rs + sqlite.rs each:

  • Add `&[TableDef]` schema_tables param to `resolve_return_columns`
  • Branch on `JOIN_DETECT_RE` to route to the multi-table resolver
  • 3 new unit tests covering: qualified column resolution, `SELECT *` rejection, `LEFT JOIN` rejection with v1.2 pointer

Test plan

  • `cargo test --workspace` — 164 lib + 11 CLI + 2 e2e + 4 typecheck tests pass
  • `cargo clippy --workspace --all-targets -- -D warnings` — clean

Next

C16 — codegen emits disambiguated row fields when joined columns share names (e.g. both `users.id` and `orgs.id` selected → `users_id` and `orgs_id` in the generated row type).


Open in Devin Review

devin-ai-integration[bot]

This comment was marked as resolved.

…solver

When a query contains the JOIN keyword, route each SELECT column through
the multi-table resolver from parser::joins (landed in the prior PR).
Qualified columns now resolve across the alias map and land in QueryDef.returns
with source_table populated.

Queries without JOIN keep the existing single-table path unchanged —
ensure_supported_select_expr still rejects qualified selects there.
Lifting that restriction for single-table queries is a separate effort
(PR #32 was in flight before this series).

Changes:
- resolve_return_columns gains a &[TableDef] schema_tables param
- JOIN_DETECT_RE gates multi-table resolution
- resolve_return_columns_multi_table rejects SELECT * across joins with
  a clear v1.2 pointer
- Parser propagates schema_tables from parse_queries to resolve_return_columns

Tests:
- 5 new postgres parser unit tests (qualified columns, AS aliases, SELECT *
  rejection, LEFT JOIN rejection, single-table rejection still active)
- Updated the cli test that was asserting the old rejection — it now
  asserts the JOIN query succeeds (renamed from cli_generate_rejects_
  qualified_selects to cli_generate_accepts_multi_table_inner_join)

158 lib + 11 CLI + 2 e2e + 4 typecheck tests all pass. Clippy clean.
…ives

Devin flagged that `JOIN_DETECT_RE.is_match(sql)` matches `\bJOIN\b`
anywhere in the SQL, which false-triggers on queries where the JOIN
lives inside a subquery (e.g. `WHERE id IN (SELECT ... JOIN ...)`).
The outer query's single-table SELECT was then misrouted to the
multi-table resolver and failed with a confusing error.

Fix: route through `parser::joins::has_outer_join`, which scopes the
check to the outer FROM body via FROM_CLAUSE_RE. JOINs inside
subqueries stay behind the WHERE boundary and no longer trip the
outer detection.

Also drop the local JOIN_DETECT_RE static (was unused after the
switch) and the stale imports. New regression test covers the
subquery case end-to-end through parse_queries.
@JagritGumber
JagritGumber force-pushed the feat/postgres-parser-inner-join branch from 06917c9 to 4ab2d7d Compare April 20, 2026 00:17
Mirror PR #47 (postgres) for the remaining two dialects. Both
mysql.rs and sqlite.rs gain:

- A `&[TableDef]` schema_tables param on resolve_return_columns
- JOIN_DETECT_RE check that routes to the multi-table resolver path
- 3 new unit tests per dialect (qualified columns, SELECT *
  rejection, LEFT JOIN rejection)

Also lift the resolve_return_columns_multi_table helper that lived
in postgres.rs into parser/joins.rs as resolve_multi_table_columns,
shared across all three dialects to avoid copy-paste. Same goes
for the JOIN_DETECT_RE constant — now `pub` from joins.rs.

164 lib + 11 CLI + 2 e2e + 4 typecheck tests all pass. Clippy clean.

After this PR all three dialects accept INNER JOIN queries with
qualified columns. Codegen consuming source_table is the final
step (C16).
@JagritGumber
JagritGumber force-pushed the feat/mysql-sqlite-parser-inner-join branch from bbb94ba to c48bc07 Compare April 20, 2026 00:24
@JagritGumber
JagritGumber changed the base branch from feat/postgres-parser-inner-join to main April 20, 2026 00:43
@JagritGumber
JagritGumber merged commit a776b20 into main Apr 20, 2026
1 of 2 checks passed
@JagritGumber
JagritGumber deleted the feat/mysql-sqlite-parser-inner-join branch April 20, 2026 00:47
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