Fix defaultImplementationForNulls returning wrong constness at 0 rows#100293
Fix defaultImplementationForNulls returning wrong constness at 0 rows#100293alexey-milovidov merged 6 commits intomasterfrom
Conversation
`defaultImplementationForNulls` had an early return at `input_rows_count == 0` that always produced a non-const column, even when all arguments were constant. This caused `ActionsDAG::updateHeader` (which evaluates on 0-row headers) to produce non-const results for functions like `concat` that use the default null handling, while functions like `divide` (which handle nulls themselves) correctly produced const results. The inconsistency manifested when two UNION DISTINCT branches used different functions — one branch's output column was const while the other's was not, causing "Block structure mismatch" or "non constant in source stream but must be constant in result" errors. Fix: move the 0-row check after the `all_columns_constant` analysis so that functions with all-const arguments correctly produce const results at 0 rows. Also fix `TotalsHavingTransform` to use `updateHeader` (same as the main output port) instead of `ExpressionActions::execute` for computing the totals port header, ensuring both ports have consistent column constness. Found by AST fuzzer: https://s3.amazonaws.com/clickhouse-test-reports/json.html?REF=master&sha=1ca65921c124da4ad3595798f0a72ef47706ecd6&name_0=MasterCI&name_1=AST%20fuzzer%20%28amd_debug%29 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Workflow [PR], commit [040b5e3] Summary: ✅ AI ReviewSummaryThis PR fixes a real constness mismatch on 0-row headers between Missing context
ClickHouse Rules
Final Verdict
|
| -- the Union step's converting actions would fail with "Block structure mismatch" or | ||
| -- "non constant in source stream but must be constant in result". | ||
|
|
||
| DROP TABLE IF EXISTS t_totals_const; |
There was a problem hiding this comment.
The test is good, it reproduces the issue.
The `transform` function casts WHEN values to the expression type. When WHEN values are Nullable (e.g. `CAST(NULL AS Nullable(Int32))`) but the expression is non-Nullable, the cast fails with "Cannot convert NULL value to non-Nullable type". This became visible after the `defaultImplementationForNulls` change in this branch made `updateHeader` actually execute functions at 0 rows with all-constant args, triggering the cast path in `transform`. Fix by checking upfront whether `transform` can handle the types: use `tryGetLeastSupertype` and a Nullable compatibility check to determine applicability, rather than failing during execution. When `transform` cannot handle the types, fall through to the `multiIf` path which handles Nullable WHEN values correctly via `caseWhenEquals`. https://s3.amazonaws.com/clickhouse-test-reports/json.html?PR=100293&sha=f067c3bec9fe4a7a07e810a27c9587ea9f777bfb&name_0=PR&name_1=SQLLogic%20test Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
LLVM Coverage Report
PR changed lines: PR changed-lines coverage: 98.57% (69/70, 0 noise lines excluded) |
| if (e.code() != ErrorCodes::NOT_IMPLEMENTED) | ||
| throw; | ||
| FunctionBasePtr function_base; | ||
| try |
There was a problem hiding this comment.
However, as it fixed the bug, let it live.
Closes #100289.
defaultImplementationForNullshad an early return atinput_rows_count == 0that always produced a non-const column, even when all arguments were constant. This causedActionsDAG::updateHeader(which evaluates on 0-row headers) to produce non-const results for functions likeconcatthat use the default null handling, while functions likedivide(which handle nulls themselves) correctly produced const results.The inconsistency manifested when two
UNION DISTINCTbranches used different functions — one branch's output column was const while the other's was not, causing "Block structure mismatch" or "non constant in source stream but must be constant in result" errors.Two fixes:
defaultImplementationForNulls(IFunction.cpp): move the 0-row early return after theall_columns_constantanalysis, so functions with all-const arguments correctly produce const results at 0 rows.TotalsHavingTransform: useupdateHeader(same as the main output port) instead ofExpressionActions::executefor computing the totals port header, ensuring both ports have consistent column constness.Found by AST fuzzer: https://s3.amazonaws.com/clickhouse-test-reports/json.html?REF=master&sha=1ca65921c124da4ad3595798f0a72ef47706ecd6&name_0=MasterCI&name_1=AST%20fuzzer%20%28amd_debug%29
Changelog category (leave one):
Changelog entry (a user-readable short description of the changes that goes into CHANGELOG.md):
Fix "Block structure mismatch" exception when using
GROUP BY ... WITH TOTALS HAVINGcombined withUNION DISTINCTand nullable expressions.Documentation entry for user-facing changes