Skip to content

R13 slice 4 finished: AND / OR / NOT / CASE delegate; one truth rule (P61, P62, D4) - #99

Merged
TimelordUK merged 7 commits into
mainfrom
refactor/r13-slice4d-and-or-not-case
Oct 1, 2026
Merged

TimelordUK merged 7 commits into
mainfrom
refactor/r13-slice4d-and-or-not-case

Conversation

@TimelordUK

Copy link
Copy Markdown
Owner

Finishes R13 slice 4. WHERE now evaluates nothing itself: evaluate_expression is the P15 window guard followed by one call to the value evaluator.

Behaviour changes (both were silent wrong answers)

  • P62: a value used as a truth value now follows D4, DuckDB's cast to boolean. This was a user decision. Text is read as a boolean (true/t/yes/y/1, false/f/no/n/0, any case), and any other text or a date is an error.
    • Before, WHERE flag read 'no' and 'f' as TRUE because they are non-empty, while AND/OR/NOT rejected all text.
    • Queries that used WHERE name to mean "name is not empty" now fail. Write name <> '' instead.
  • P61: WHERE's CASE with no ELSE answered FALSE, so NOT (CASE ... END) selected rows. The implicit ELSE is NULL, which is UNKNOWN.

Commits

  1. Pin: a sixth evaluator matrix with DuckDB-generated expectations (19 divergences) and four corpus cases over the new data/predicate_text.csv. No engine change.
  2. Trilean::from_value becomes the one truth rule and returns Result. Three of the five truth tables are removed. Exactly the 15 P62 entries are now fixed; parity goes from 183 to 185.
  3. CASE delegates (closes P61; parity 186). The window guard becomes a structural walk, pinned first under AND/OR/NOT/CASE.
  4. NOT/AND/OR delegate. This is a provable no-op: the parity report is byte-identical, and over trades_100000.csv every shape runs at load time.
  5. Docs: R13, P61, P62 and D4. Also carries the P58–P60 / R14 / T19 docs commit that missed fix(parser): accept quoted column after alias qualifier (alias."col n… #97, so docs: file P58-P60, R14, T19 from the qualified quoted column work #98 is superseded.

Also: fix(clippy) computes PHI() instead of writing the golden-ratio literal. CI's clippy 1.99 denies that literal (approx_constant), which is what failed #98.

Left for slice 6 (logged in R13)

  • HAVING's is_truthy and IIF still have their own truth tables.
  • JOIN compares with a plain comparison (P52).
  • WhereClause's list of conditions joined by connectors duplicates AND/OR. Only QUALIFY merged into an existing WHERE still produces more than one condition.

Tests

  • cargo test: 842 lib + 506 integration + 2 doc tests, all passing.
  • Parity (runner.py --check): 186 AGREE / 217, contract holds.
  • Examples: all FORMAL tests pass. Of the 4 SMOKE failures, three are missing data files; the fourth, expander_rewriters, is a window function over an aggregate, untouched by this branch but not confirmed against a main build.
  • The Python suite was not run locally (uv can't build pandas on this Windows machine); CI runs it.

🤖 Generated with Claude Code

TimelordUK and others added 7 commits October 1, 2026 17:53
- P58: alias."quoted col" - expression positions fixed on this branch;
  JOIN ON keys still fail (hash join re-splits the flattened name at the
  last dot).
- P59: quoted table alias ("l".col, FROM t "l") not accepted.
- P60: GROUP BY an unknown column silently forms one NULL group
  (group_by_expressions.rs unwrap_or(Null)).
- R14: column references resolved by several hand-written copies;
  inventory, target shape and slices, sequenced after R13.
- T19: completion does not know table aliases (l.<tab> offers methods).

DuckDB 1.5.5 answers checked by hand against data/countries.csv.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ice 4)

AND / OR / NOT / CASE cannot delegate to the value evaluator without one
rule for "is this value true?", and there were five. A new evaluator
matrix (EXPECTED_TRUTH / EXPECTED_TRUTH_BARE, DuckDB-generated per row)
records 19 divergences:

- P61: WHERE's CASE with no ELSE answers FALSE, so NOT selects the row.
- P62: text is TRUE when non-empty in WHERE and CASE WHEN ('f', 'no'
  pass), while AND / OR / NOT refuse all text; dates are TRUE.
- D4 (user decision): follow DuckDB's cast to boolean everywhere.

Four corpus cases over new data/predicate_text.csv and null_edges.csv,
pinned in their current buckets. No engine change; parity 183 AGREE / 217.
check_matrix gains check_matrix_with to run a subset of evaluators.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… R13 slice 4)

Trilean::from_value is now the only place that decides whether a value
is true, and returns Result: a number is TRUE when non-zero, NULL is
UNKNOWN, text is read as a boolean (true/t/yes/y/1, false/f/no/n/0, any
case), and any other text or a date is an error.

Three of the five copies go: WHERE's evaluate_value_as_predicate table,
the value evaluator's truth_of (AND / OR / NOT) and its CASE WHEN
evaluate_condition_as_bool. HAVING's is_truthy and IIF remain for
slice 6.

Closes P62: 'f' and 'no' were TRUE in WHERE and CASE WHEN because they
are non-empty, while AND / OR / NOT refused all text. Exactly the 15
P62 matrix entries went FIXED; parity moved by exactly the three P62
cases, 183 -> 185 AGREE / 217. FORMAL examples unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ce 4)

WHERE's evaluate_case_expression_as_bool and evaluate_expression_as_bool
go; a CASE in WHERE is evaluated by the value evaluator and read back
through Trilean::from_value, like every other delegated predicate.

Closes P61: WHERE's CASE answered FALSE when no WHEN matched and there
was no ELSE, so NOT (CASE ... END) selected those rows. The implicit
ELSE is NULL, i.e. UNKNOWN.

The P15 window guard can no longer sit at each leaf, since the value
evaluator does not come back to WHERE on the way down:
reject_unlifted_windows walks the predicate's structure (AND / OR / NOT,
CASE conditions and results) to the operands WHERE used to guard one at
a time. Pinned first, under AND / OR / NOT / CASE, against the old code.

Exactly the four P61 matrix entries went FIXED; parity moved by exactly
the P61 case, 185 -> 186 AGREE / 217.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
clippy 1.99 (CI) denies the literal under approx_constant and points at
f64::consts::GOLDEN_RATIO, which the 1.98 toolchain lacks. Computing
(1 + sqrt 5) / 2 compiles on both and gives the same f64,
1.618033988749895. Broke the Complete Test Suite on PR #98.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…slice 4)

evaluate_expression is now the P15 window guard followed by one call to
the value evaluator, for every predicate shape. evaluate_binary_op
(WHERE's AND / OR recursion), the NOT arm and evaluate_value_as_predicate
go: the value evaluator's AND / OR / NOT already use the same Trilean
truth tables and, since D4, the same truth rule.

A provable no-op: evaluator matrix unchanged, WHERE unit tests green,
parity report byte-identical (186 AGREE / 217). Over trades_100000.csv
AND / OR / NOT / CASE predicates all run at load time (~2.4 s).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…2, D4)

R13 slice 4 marked done; result block with the pin, the five truth
tables, D4, CASE before AND / OR / NOT, and the WhereClause connector
list logged as the copy-paste left for slice 6. P61 and P62 FIXED;
corpus summary 217 cases, 186 AGREE.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@TimelordUK
TimelordUK merged commit 1113cfd into main Oct 1, 2026
8 checks passed
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