feat: validate JOIN ON operands in strict mode - #217
Merged
Conversation
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
force-pushed
the
feature/strict-join-on-validation
branch
from
July 26, 2026 18:40
2d44c45 to
99ecca5
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #205.
Problem
Strict mode validated the
SELECTlist and (since #128/#148) theWHEREclause. Nothing else:select id from users where nope = 1QueryTypeError<'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.idwhereo.user_idwas meant) — which compiles, runs, and returns a wrong result set instead of an error.Fix
ExtractJoinOnText(src/from.ts) collects every top-levelONcondition in the FROM clause, joining multiple groups withand. It is depth-tracked, so anONinside a derived table's own subquery belongs to that subquery's parse, not this one. The collected text goes through the existingWhereClauseErrorscan — the operands are ordinary bare or qualified column references against the same sources, whichValidateWhereOperandalready handles (placeholders and literals short-circuit as before).ParsedStatementnow carries the FROM clause as rawfromTextrather 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 aQueryTypeErrorin strict mode, with no clause boundary; it now states the boundary explicitly, andtests/join-on-strict.test-d.tslocks it in.Tests
tests/join-on-strict.test-d.ts(16 assertions): unknown column and unknown alias inON, second join in a chain,LEFT JOIN, compounda = b and c = dconditions, derived-table joins,CROSS JOINwith noON, placeholders, aWHEREtypo alongside a validON, non-strict staying permissive, and the three out-of-scope clauses. Five of them fail onmaster.🤖 Generated with Claude Code