Skip to content

docs(parity): P46-P48, found writing the countries examples - #79

Merged
TimelordUK merged 2 commits into
mainfrom
docs/p46-p48-parity-findings
Sep 12, 2026
Merged

TimelordUK merged 2 commits into
mainfrom
docs/p46-p48-parity-findings

Conversation

@TimelordUK

Copy link
Copy Markdown
Owner

Nine new sections in examples/countries.sql, and the three divergences that writing them turned up.

The examples

The Russia query hand-typed its fourteen neighbour codes into a SPLIT list. The generic form unpacks borders for every country: CROSS JOIN a table of slot numbers onto the rows and pull the nth code out with SPLIT_PART. 250 countries become 649 border pairs, and the generator runs once rather than per row. Same trick reused for languages.

On top of that, staged through #border_pairs and #edges:

  • most neighbours (China 16, Russia 14)
  • the land borders that leave their own continent
  • doubly landlocked — falls out as exactly Liechtenstein and Uzbekistan
  • border symmetry check — forward/reverse keys, LEFT JOIN … IS NULL. Finds the one hole in the data: Sri Lanka lists IND, India does not list LKA
  • haversine from latlng with RADIANS/SIN/COS/ACOS — no geo functions needed — plus CONVERT for miles. New Zealand 18,764 km from London
  • widest and tightest land borders, deduped to one direction per pair
  • currency unions, language reach, island nations with the flag emoji
  • ROW_NUMBER/SUM OVER with a REPEAT bar chart

Runs clean end to end in ~2.7s; the examples harness passes.

The findings

Two of the three are silent wrong answers, and none came from widening the corpus — they came from asking a real dataset ordinary questions.

[P46] A column-to-column comparison in WHERE returns zero rows. Not one operator or one type: WHERE region = region matches nothing. The AST is correct, and the same comparison projected in the SELECT list is right, so it is the WHERE evaluator. Same "zero rows reads as no data" disguise that made P30 dangerous — found here only because the answer was known in advance (Russia borders China, so the result cannot be empty).

[P47] MIN/MAX compare by type variant before value. DataValue derives PartialOrd, so every Float outranks every Integer for MAX (and the reverse for MIN) whatever the numbers are. MAX(area) over countries.csv answers 34.2 rather than Russia's 17,098,242, from a GROUP BY that looks entirely ordinary. compare_datavalues already has the correct Integer↔Float arms, which is why ORDER BY and the batched window path agree with DuckDB. Also recorded: SQL_CLI_BATCH_WINDOW=0 changes the answer, because the row-wise window path carries the same bare comparison — a documented speed opt-out switching a result from right to wrong.

[P48] A comparison with a NULL operand projects false instead of NULL. The P18/P19 three-valued rule at a site the 2026-08-22 fix did not reach. Found while building P46's anchor case — the control that was meant to AGREE came back DIFFER for an unrelated reason.

Corpus

Seven new cases across tiers 01, 02 and 10 — six DIFFER and one AGREE anchor, the latter being the projected comparison that localises P46 to the WHERE evaluator rather than to comparison in general. Verified against the pinned DuckDB 1.5.5; the parity contract holds at 188 cases, 157 AGREE (was 181 / 156).

Also updated

  • P40's "what works today" understated itself. The slot trick above means a bounded explode works today, with the generator invoked once and the scalar doing the per-row work — it sidesteps both of P40's stacked defects rather than working around them. Recorded with the SQL and its two honest costs (hand-picked bound, one scan per slot).
  • P45 gains the CROSS JOIN range(...) spelling, which fails with a different message than the JOIN form and so reads as a different bug.
  • NEXT in the fix-order table is now P46–P48, with the previous NEXT relabelled "then" — two of these are silent-wrong of the P30 family, which is the criterion that let P27–P30 jump the queue before. Easy to put back if you disagree with the sequencing.
  • A fourth entry in the discovery lessons: writing examples is discovery. Unlike the corpus, examples are written to be interesting rather than exhaustive, which is how they wander outside the tested envelope.

The two workarounds in the examples file carry their reason in a comment and cite the entry, so they do not read as arbitrary style: the region comparison goes through the SELECT list because of P46, and the area aggregate is CAST to FLOAT because of P47.

No engine code changed in this PR — findings and cases only.

🤖 Generated with Claude Code

https://claude.ai/code/session_01GfDkQm9s234uEDhdX6rvmf

TimelordUK and others added 2 commits September 12, 2026 21:08
Three divergences, all found asking countries.csv ordinary questions rather
than by widening the corpus. Two are silent wrong answers.

P46 - a column-to-column comparison in WHERE returns zero rows. Not one
operator or one type: `WHERE region = region` matches nothing. The AST is
correct, so it is the WHERE evaluator, and the same comparison projected in
the SELECT list is right. Same "zero rows reads as no data" disguise as P30.

P47 - MIN/MAX compare by type variant before value. DataValue derives
PartialOrd, so every Float outranks every Integer for MAX (and the reverse
for MIN) whatever the numbers are: MAX(area) over countries.csv answers 34.2
instead of Russia's 17098242. compare_datavalues already does this correctly,
which is why ORDER BY and the batched window path agree with DuckDB. Also
recorded: SQL_CLI_BATCH_WINDOW=0 changes the answer, because the row-wise
window path shares the bare comparison.

P48 - a comparison with a NULL operand projects false instead of NULL. The
P18/P19 three-valued rule at a site the 2026-08-22 fix did not reach. Found
while building P46's anchor case: the control that was meant to AGREE came
back DIFFER for an unrelated reason.

Corpus grows by seven cases (six DIFFER, one AGREE anchor) across tiers 01,
02 and 10; the contract holds at 188 cases, 157 AGREE. Verified against the
pinned DuckDB 1.5.5.

Also updated:
- P40's "what works today" understated itself. CROSS JOIN a slot table and
  take one SPLIT_PART per slot, and a bounded explode works today with the
  generator running once - it sidesteps both stacked defects rather than
  working around them. Recorded with the SQL and its two honest costs.
- P45 gains the CROSS JOIN spelling, which fails with a different message.
- NEXT in the fix-order table is now P46-P48; the previous NEXT is "then".
- A fourth discovery lesson: writing examples is discovery. Examples are
  written to be interesting rather than exhaustive, which is how they wander
  outside the tested envelope.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01GfDkQm9s234uEDhdX6rvmf
The Russia query hand-typed its fourteen neighbour codes into a SPLIT list.
Unpack borders for every country instead: CROSS JOIN a table of slot numbers
onto the rows and pull the nth code out with SPLIT_PART, which turns 250
countries into 649 border pairs and needs no per-row generator call. Same
trick reused for the languages column.

Nine sections on top of that, staged through #border_pairs and #edges:

- most neighbours (China 16, Russia 14)
- the land borders that leave their own continent
- doubly landlocked - falls out as exactly Liechtenstein and Uzbekistan
- border symmetry: forward and reverse keys, LEFT JOIN, IS NULL. Finds the
  one hole in the data - Sri Lanka lists IND, India does not list LKA
- haversine from latlng with RADIANS/SIN/COS/ACOS, no geo functions needed,
  plus CONVERT for miles. New Zealand 18764 km from London
- widest and tightest land borders, deduped to one direction per pair
- currency unions, language reach, island nations with the flag emoji
- ROW_NUMBER/SUM OVER with a REPEAT bar chart

Two workarounds carry their reason in a comment and cite the entry: the
region comparison is done in the SELECT list because of P46, and the area
aggregate is CAST to FLOAT because of P47.

Runs clean end to end in ~2.7s; the examples harness passes.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01GfDkQm9s234uEDhdX6rvmf
@TimelordUK
TimelordUK merged commit 58d8f5a into main Sep 12, 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