feat(3vl): SQL three-valued logic for AND/OR/NOT + null cast pass-through#63
Closed
Felipe705x wants to merge 4 commits into
Closed
feat(3vl): SQL three-valued logic for AND/OR/NOT + null cast pass-through#63Felipe705x wants to merge 4 commits into
Felipe705x wants to merge 4 commits into
Conversation
afcee05 to
d79b426
Compare
Evaluate the boolean connectives under SQL 3VL with short-circuit. `AND`/`OR` get dedicated arms in `run_expr`, outside the implicit argument-cast wrapper, so a `Null` or `Failure` operand (missing attribute, unbound var, type error) is read as the 3VL unknown rather than collapsing the whole expression: true OR null -> true false AND null -> false false OR null -> null true AND null -> null null OR null -> null null AND null -> null NOT null -> null `Failure` is treated as the unknown input, matching the existing IsNull/Coalesce/aggregate convention, so e.g. `WHERE x.status OR true` returns all rows when `status` is absent (previously: every row dropped). The unreachable `eval_binop` And/Or arms are updated to the same table for any direct caller. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
`null AS T` returns null for any target type instead of failing the membership check. This satisfies `null AS T -> null` and lets a null operand slip through the implicit argument cast used by the generic operator wrapper, so it reaches the 3VL logic (the runtime relaxation of the SimpleType-only `△(bop)` check). The `IS`/`TYPED` predicate is untouched — it calls `value_is_type` directly, not this arm. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Override the codomain (c̃od) check for the short-circuit connectives: `AND`/`OR` type as `B` unless BOTH operands meet bottom (⊥) against `B`. A ⊥/Null operand beside a valid Bool still yields Bool (e.g. `Integer OR true : B`); only when both sides are incompatible with Bool does the expression degrade to ⊥ with a warning. All other operators keep the default delta-based codomain check. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Covers the OR/AND/NOT truth tables, missing-property-as-unknown, the `null AS T` pass-through, invalid-cast handling (empty in WHERE, null cell in RETURN), and the typechecker short-circuit. Includes WHERE-clause differentiators that fail under the pre-fix behavior (e.g. `WHERE x.status OR true` returned no rows), and no-regression guards pinning the pre-existing comparison-operator behavior (handled in a follow-up). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
d79b426 to
f508fab
Compare
This was referenced Jul 4, 2026
Collaborator
Author
|
Superseded by #73 — closing. #73 reaches the same goal at the correct altitude, validated against both the ISO GQL and FPPC oracles:
Known ISO gap (pre-existing, out of scope, shrunk by #73): essential type errors / |
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.
What & why
Make
nullpropagation follow SQL three-valued logic (3VL) for the boolean connectivesAND/OR/NOT.Today a null/missing operand of a logical operator is routed through the implicit argument-cast wrapper, which turns a missing attribute (
Failure) or anullinto a hard failure and drops the whole row. The headline symptom:When
statusis absent this returns no rows (every row dropped) instead of all of them. Correct 3VL:unknown OR true = true.Changes
Runtime —
src/runtime/engine.rsAND/ORarms inrun_expr, outside the operator wrapper, with real short-circuit (true OR _,false AND _) and the 3VL truth table. BothValue::NullandFailure(missing attribute / unbound var / type error) are read as the 3VL unknown, matching the existingIsNull/Coalesce/aggregate convention.NOT null → null.null AS T → nullin theBinOp::Asarm —nullinhabits every type; this also lets a null operand reach the 3VL logic (the runtime relaxation of theSimpleType-only argument check) without touching theIS/TYPEDpredicate.eval_binopAND/ORarms updated to the same 3VL table for any direct caller.Typechecker —
src/typing/checker.rsc̃od) check forAND/OR: result isBunless both operands meet bottom (⊥) againstB(a⊥/null operand beside a valid Bool still yields Bool — e.g.Integer OR true → B).Truth tables (now)
true OR null → true·false OR null → null·false AND null → false·true AND null → null·null · null → null·NOT null → null.Scope / no-regression
Boolean connectives only. Comparison/equality operators (
=,<>,<, …) keep their pre-existing behavior in this PR — including the knownnull = null → truewart. 3VL for comparisons + thecmp_values↔eval_binop(pushdown) unification is a follow-up PR. New tests pin the current comparison behavior as a no-regression guard.Tests —
tests/three_valued_logic_test.rs(17)3VL truth tables, missing-property-as-unknown, WHERE-clause differentiators that fail under the old behavior,
null/invalid cast handling, the typechecker short-circuit, and comparison no-regression guards.Verified the differentiators genuinely catch the bug: reverting just the two source files (test file kept) makes 7 of 17 tests fail — exactly the 3VL differentiators (e.g.
WHERE x.status OR true→[]old vs["A","B","C"]fixed) — while the 10 guards stay green.Checklist
cargo fmt --allcargo clippy --workspace --all-targets -- -D clippy::allcargo test --lib+ the integration list inCLAUDE.md) — no regressions🤖 Generated with Claude Code