Skip to content

feat: validate JOIN ON operands in strict mode - #217

Merged
tiagolauer merged 1 commit into
masterfrom
feature/strict-join-on-validation
Jul 26, 2026
Merged

feat: validate JOIN ON operands in strict mode#217
tiagolauer merged 1 commit into
masterfrom
feature/strict-join-on-validation

Conversation

@tiagolauer

Copy link
Copy Markdown
Owner

Closes #205.

Problem

Strict mode validated the SELECT list and (since #128/#148) the WHERE clause. Nothing else:

query before
select id from users where nope = 1 QueryTypeError<'unknown column: nope'>
select u.id from users u join orders o on u.id = o.nope { id: number }

The join condition is the highest-traffic place to get a column name wrong, and the usual mistake is a wrong side (o.id where o.user_id was meant) — which compiles, runs, and returns a wrong result set instead of an error.

Fix

ExtractJoinOnText (src/from.ts) collects every top-level ON condition in the FROM clause, joining multiple groups with and. It is depth-tracked, so an ON inside a derived table's own subquery belongs to that subquery's parse, not this one. The collected text goes through the existing WhereClauseError scan — the operands are ordinary bare or qualified column references against the same sources, which ValidateWhereOperand already handles (placeholders and literals short-circuit as before).

ParsedStatement now carries the FROM clause as raw fromText rather than pre-extracted conditions, so the scan is never instantiated for a non-strict query.

GROUP BY / HAVING / ORDER BY

Deliberately still out of scope — they resolve against SELECT-list aliases, ordinals and aggregates, not only source columns. The README said only that unknown columns become a QueryTypeError in strict mode, with no clause boundary; it now states the boundary explicitly, and tests/join-on-strict.test-d.ts locks it in.

Tests

tests/join-on-strict.test-d.ts (16 assertions): unknown column and unknown alias in ON, second join in a chain, LEFT JOIN, compound a = b and c = d conditions, derived-table joins, CROSS JOIN with no ON, placeholders, a WHERE typo alongside a valid ON, non-strict staying permissive, and the three out-of-scope clauses. Five of them fail on master.

🤖 Generated with Claude Code

Strict mode checked the SELECT list and the WHERE clause, so a typo in a
join condition passed cleanly - and a wrong side (`o.id` where `o.user_id`
was meant) compiles, runs, and returns a wrong result set rather than an
error, which is exactly what strict mode exists to prevent.

ExtractJoinOnText collects every top-level ON condition in the FROM clause,
depth-tracked so a derived table's own inner ON is left to that query's
parse, and hands the result to the existing WhereClauseError scan - the
operands are ordinary bare or qualified column references against the same
sources. The FROM clause text is carried on ParsedStatement as raw text so
the scan is only instantiated when strict mode actually runs it.

GROUP BY, HAVING and ORDER BY stay out of scope: they resolve against
SELECT-list aliases, ordinals and aggregates, not only source columns. That
boundary is now documented in the README and locked in by tests.

Fixes #205

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@tiagolauer
tiagolauer force-pushed the feature/strict-join-on-validation branch from 2d44c45 to 99ecca5 Compare July 26, 2026 18:40
@tiagolauer
tiagolauer merged commit 0458876 into master Jul 26, 2026
6 checks passed
@tiagolauer
tiagolauer deleted the feature/strict-join-on-validation branch July 26, 2026 18:42
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.

strict mode: JOIN ON operands are never validated, and neither are GROUP BY / HAVING / ORDER BY

1 participant