Skip to content

fix(sql): CAST(NULL AS VARCHAR) answers NULL, not the text 'null' - #426

Merged
fupelaqu merged 1 commit into
mainfrom
fix/cast-null-varchar
Oct 18, 2026
Merged

fupelaqu merged 1 commit into
mainfrom
fix/cast-null-varchar

Conversation

@fupelaqu

Copy link
Copy Markdown
Contributor

Summary

  • CAST(NULL AS VARCHAR) answers NULL instead of the text 'null'. A NULL literal converted to any type now renders null: the literal is never nullable, so the VARCHAR arm wrapped it unguarded in String.valueOf(null).
  • A set-operation leg's slot builds its reader once instead of twice (arrow's plan-time typing for set operations, SOFTNETWORK-APP/softclient4es-arrow#198).

Behaviour (measured on Elasticsearch 8.18.3 through the gateway)

statement 0.23.0 main this PR
SELECT CAST(NULL AS VARCHAR) AS sv FROM t NULL text 'null' (regression) NULL
SELECT n, CAST(NULL AS VARCHAR) AS sv, CAST(NULL AS INT) AS iv FROM t sv = text 'null' text 'null' NULL
CAST(NULL AS INT) (control) NULL NULL NULL

Every other cast is unchanged; set-operation answers are unchanged (the reader change only stops building the same reader twice).

Tests

  • CastConversionSpec: a NULL cast to VARCHAR, CHAR, TEXT, KEYWORD, INT, BIGINT, DOUBLE, REAL, BOOLEAN, DATE, TIME, TIMESTAMP and DATETIME renders null, alone and beside other columns; it failed on main with String.valueOf(null).
  • DottedColumnSpec (shared testkit, every major's runner): CAST(NULL AS VARCHAR) answers NULL on every row route, alone and beside other columns.
  • Local runs: sql/test 1,847 / 1,847, core/test 1,259 / 1,259, es8java/test green (the two first-run failures were the new test's REPL harness, since moved, and a fresh-worktree resource ordering artefact that passes on rerun).
  • Downstream, built against this core published locally:
    • softclient4es-extensions: community/test 223 / 223 and its ES 8 views suite 20 / 20, with no change needed.
    • softclient4es-arrow (SOFTNETWORK-APP/softclient4es-arrow#198, set-operation typing on core's leg schemas): unit suites green, ES 8 ADBC 78 / 78 and Flight 249 / 249, ES 6.8 perf(core): guard tryParseAsDateTime with a cheap temporal-shape check (arrow#139) #198 blocks 12 / 12 and 11 / 11, the Python Flight job 31 / 31, + compile, and its regression gate against arrow 264eac3 on the same jars: 0 violations. Its new test of the CAST(NULL AS VARCHAR) workaround needs this core.
  • CI's lint line passes on a clone of the committed bytes.

🤖 Generated with Claude Code

…leg's reader is built once

A NULL literal converted to any type now renders `null`. The literal is never `nullable`, so the VARCHAR arm wrapped it unguarded in `String.valueOf(null)` and Elasticsearch answered the TEXT "null": beside other columns since 0.23.0, and alone in the projection on main, where 0.23.0 answered NULL (measured on Elasticsearch 8.18.3). CAST(NULL AS INT) and every other target already answered NULL and are unchanged.

A set-operation leg's slot built its reader twice, once to see whether it exists and once after its dates were routed; when routing changes nothing, which is every readable date, the first reader is the slot's. Answers are unchanged; arrow's plan-time typing does the work once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@fupelaqu
fupelaqu marked this pull request as draft October 17, 2026 17:30
@fupelaqu
fupelaqu marked this pull request as ready for review October 18, 2026 06:07
@fupelaqu
fupelaqu merged commit 8af2344 into main Oct 18, 2026
4 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