Skip to content

Fix/temp table qualified and join coercion - #21

Merged
TimelordUK merged 3 commits into
mainfrom
fix/temp-table-qualified-and-join-coercion
Jun 10, 2026
Merged

Fix/temp table qualified and join coercion#21
TimelordUK merged 3 commits into
mainfrom
fix/temp-table-qualified-and-join-coercion

Conversation

@TimelordUK

Copy link
Copy Markdown
Owner

No description provided.

TimelordUK and others added 3 commits June 10, 2026 13:29
resolve_select_columns did a qualified-only lookup using the raw table
alias and no fallback, so a bare qualified column in the projection
(`SELECT f.region FROM #tmp f` or `... FROM data f`) failed with
"Table 'f' may not support qualified column names" whenever the source
was a single base or temp table — whose columns carry no qualified_name.

WHERE and computed-expression clauses already resolve this via
ExecutionContext::resolve_column_index (qualified, then unqualified
fallback). Align the projection resolver: try the qualified name first
(JOIN/CTE columns carry one), then fall back to an unqualified
case-insensitive lookup before erroring.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Equi-joins indexed keys in a HashMap<DataValue, _>, which keys on the
exact DataValue variant. String("220") and Integer(220) therefore never
collided, so a join between a string column (e.g. a value pulled from a
JSON blob via SUBSTR) and an integer column silently produced no matches
— even though WHERE a = b coerces and matches them. InternedString vs
String had the same problem.

Add canonical_join_key(), which folds keys to a canonical form before
hashing/comparing: numeric-looking strings become numbers, interned
strings collapse to plain strings, and whole floats collapse to integers
(so 220.0 matches 220). Apply it on both the build and probe sides of the
hash-join paths and in the nested-loop equality comparator so JOIN
equality is consistent with WHERE equality.

Adds unit tests for canonical_join_key and end-to-end regression tests
(also covering the aliased-projection fix) via the real SELECT INTO path.

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

The first pass coerced every numeric-looking string, so two string keys
like "007" and "7" also matched. Make coercion conditional: only when the
two join columns hold different value *kinds* (a string column vs a
numeric column) do string keys get parsed to numbers. Two string columns
join on exact text, so casting both sides with TO_STRING(...) is a
deliberate opt-out of numeric matching.

The kind is sampled from each column's first non-null value rather than
its declared data_type, because a materialized temp table does not carry
reliable column types (a column of integers can still be typed String) —
relying on data_type regressed the real string-vs-integer case to zero
matches. Whole floats still always fold to integers so int/float numeric
columns join. The nested-loop path defers to value_comparisons so its
equality already matches WHERE semantics pairwise.

Adds a regression test that two string keys are not coerced.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@TimelordUK
TimelordUK merged commit fb0c083 into main Jun 10, 2026
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