Skip to content

[fix](be) Support a VARBINARY result in CASE WHEN - #69568

Merged
morningman merged 1 commit into
apache:masterfrom
morningman:b2m-t35v-case-when-varbinary
Oct 26, 2026
Merged

morningman merged 1 commit into
apache:masterfrom
morningman:b2m-t35v-case-when-varbinary

Conversation

@morningman

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

Issue Number: None

Related PR: #69567

Problem Summary:

In short. A CASE WHEN whose result type is VARBINARY and that has two or more WHEN branches failed in BE with argument_type VarBinary not supported. BE now selects VARBINARY values the way it selects strings.

Background. VARBINARY values reach queries through external tables (Iceberg, Paimon and Hive binary columns map to VARBINARY) and through CAST(... AS VARBINARY). BE evaluates CASE WHEN in VCaseExpr: it computes, for every row, the index of the branch that the row takes, and then VCaseExpr::_execute_update_result builds the result column, dispatching on the result's primitive type to a typed copy loop. Nereids rewrites a CASE with a single WHEN and an ELSE into if(), whose generic path copies values through IColumn::insert_from and accepts any column type.

The problem, and what it cost. The dispatch in _execute_update_result had no entry for TYPE_VARBINARY, so it fell through to NOT_IMPLEMENTED_ERROR. Every query with such a CASE failed, whatever the constant-folding mode (the CASE is evaluated by BE, not folded), for example

SELECT CASE WHEN number % 3 = 0 THEN CAST(concat('a', number) AS VARBINARY)
            WHEN number % 3 = 1 THEN CAST('y' AS VARBINARY)
            ELSE X'FF' END
FROM numbers("number" = "6");
-- ERROR 1105 (HY000): errCode = 2, detailMessage = (127.0.0.1)[NOT_IMPLEMENTED_ERROR][E3]
-- argument_type Nullable(VarBinary) not supported

Only a CASE with one WHEN and an ELSE worked, because it is evaluated as if(). branch-4.2 has the same gap.

How this PR fixes it. TYPE_VARBINARY now maps to ColumnVarbinary, and ColumnVarbinary takes the per-row copy (update_result_normal) that CASE already uses for strings, both for a nullable result (including CASE without ELSE, whose unmatched rows are NULL) and for a non-nullable one. FE needs no change.

Results.

CASE WHEN with a VARBINARY result Before After
Two or more WHEN branches, with ELSE fails: argument_type VarBinary not supported returns the selected bytes
Two or more WHEN branches, without ELSE fails: argument_type Nullable(VarBinary) not supported returns the selected bytes, NULL for unmatched rows
One WHEN with ELSE (rewritten to if()) works works (unchanged)
  • BE unit tests: VCaseVarbinaryTest adds two cases through _execute_update_result: a non-nullable result with ELSE (a column branch, a constant branch, NUL and non-UTF-8 bytes) and a nullable result without ELSE (NULL from a branch and from unmatched rows). Both fail without the change with the error above. VCaseSelectionTest still passes (50 cases in total).
  • Regression test: query_p0/sql_functions/binary_functions/test_varbinary_case_when runs CASE with and without ELSE over a table, over numbers() and on constants, in the three constant-folding modes (FE folding, folding by BE, no folding); the results match in all modes.

Release note

Fix: a CASE WHEN with two or more WHEN branches and a VARBINARY result failed with "argument_type VarBinary not supported".

Check List (For Author)

  • Test

    • Regression test
    • Unit Test
    • Manual test (add detailed scripts or steps below)
    • No need to test or manual test. Explain why:
      • This is a refactor/code format and no logic has been changed.
      • Previous test can cover this change.
      • No code files have been changed.
      • Other reason
  • Behavior changed:

    • No.
    • Yes.
  • Does this need documentation?

    • No.
    • Yes.

Check List (For Reviewer who merge this PR)

  • Confirm the release note
  • Confirm test cases
  • Confirm document
  • Add branch pick label

### What problem does this PR solve?

Issue Number: None

Related PR: apache#69567

Problem Summary: A CASE WHEN whose result type is VARBINARY failed in BE
with "argument_type VarBinary not supported" (Nullable(VarBinary) for a
nullable result), e.g.

    SELECT CASE WHEN number % 3 = 0 THEN CAST(concat('a', number) AS VARBINARY)
                WHEN number % 3 = 1 THEN CAST('y' AS VARBINARY)
                ELSE CAST('x' AS VARBINARY) END
    FROM numbers('number' = '6')

in every constant-folding mode. VCaseExpr::_execute_update_result dispatches
the result column by primitive type and had no TYPE_VARBINARY entry. A CASE
with a single WHEN and an ELSE did not fail only because Nereids rewrites it
to if(), which copies values through IColumn::insert_from for any column.

TYPE_VARBINARY now maps to ColumnVarbinary and takes the per-row copy that
CASE uses for strings (update_result_normal), with and without a nullable
result. branch-4.2 has the same gap.

### Release note

Fix: CASE WHEN with two or more WHEN branches and a VARBINARY result failed with "argument_type VarBinary not supported".

### Check List (For Author)

- Test: Unit Test / Regression test
    - VCaseVarbinaryTest: a VARBINARY result with ELSE (column, constant branch, NUL and non-UTF-8 bytes) and a nullable one without ELSE (NULL rows from a branch and from no match); both fail without the change
    - Regression query_p0 suite for CASE WHEN with a VARBINARY result in the three folding modes
- Behavior changed: No
- Does this need documentation: No

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
@hello-stephen

Copy link
Copy Markdown
Contributor

Thank you for your contribution to Apache Doris.
Don't know what should be done next? See How to process your PR.

Please clearly describe your PR:

  1. What problem was fixed (it's best to include specific error reporting information). How it was fixed.
  2. Which behaviors were modified. What was the previous behavior, what is it now, why was it modified, and what possible impacts might there be.
  3. What features were added. Why was this function added?
  4. Which code was refactored and why was this part of the code refactored?
  5. Which functions were optimized and what is the difference before and after the optimization?

@morningman

Copy link
Copy Markdown
Contributor Author

run buildall

@morningman

Copy link
Copy Markdown
Contributor Author

/review

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed all four changed files at 2e14addd82146902041b3ea7a66768d4515f7b35 and the relevant FE coercion, CASE/IF execution, column insertion and exception paths. No actionable findings were identified. There were no existing inline findings to carry forward and no additional user focus points.

The new TYPE_VARBINARY dispatch uses ColumnVarbinary and the established per-row selection path for both nullable and non-nullable results. Constant branches are unpacked before nullable checks; selected nested values and null-map entries use the same source row; missing ELSE inserts NULL. Binary values are copied into result-owned inline or arena storage, preserving NUL and non-UTF-8 bytes.

Required checkpoint Conclusion
Goal and evidence The change addresses unsupported multi-WHEN VARBINARY results. Two direct BE unit tests and regression queries target the dispatch and observable bytes/NULLs.
Scope and clarity The three production additions are focused and reuse existing selection code.
Concurrency and locking No shared mutable state, threads or locks are introduced.
Lifecycle and static initialization Inputs remain owned during selection; the fresh result owns copied values. No new static initialization or reference cycle.
Configuration No configuration items are added; tests vary existing folding settings.
Compatibility No symbol signature, wire or storage format changes. Older BEs retain their prior lack of support for these CASE results.
Thrift/protobuf field IDs No generated protocol definitions are changed.
Parallel paths Both nullable modes and branch-index widths use the new dispatch. Single-WHEN CASE rewrites to IF; IF and short-circuit CASE already support VARBINARY through generic insertion.
Conditional checks The compile-time type condition correctly excludes VARBINARY from numeric selection. No speculative runtime error check is added.
Test coverage Covers column/constant branches, selected NULLs, implicit NULL, empty bytes, embedded NUL/high bytes and three folding modes. Dedicated long-value and constant-nullable VARBINARY cases are absent; their mechanisms were checked statically.
Expected results All 12 new golden sections match the SQL by inspection. Test execution and output-generation provenance were not independently verified.
Observability Existing error propagation and expression/type checks are sufficient for this local selection change; no new metrics/logs are needed.
Transactions and persistence Production transaction, visibility/version, delete-bitmap, EditLog and persistence paths are unchanged.
Data writes and atomicity No production write path is modified; test inserts use existing behavior.
FE/BE variable propagation No new transmitted variables. Existing CASE coercion and nullable metadata agree with the selected BE column type.
Performance Reuses row-wise copying without materializing constant branch columns. Selection remains O(rows * WHEN-count), followed by O(rows + branches + selected bytes) copying; numeric dispatch is unchanged.
Other issues No further actionable issue found in error propagation, COW ownership, header conventions, or the remaining changed code.

Review coverage is complete for the static scope. Builds, unit/regression tests, formatter/static-analysis execution, and release ELF/runtime compatibility checks were not run under the review-only contract. The review does not independently establish runtime test results.

Review converged after 2 round(s).

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-H: Total hot run time: 28284 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 2e14addd82146902041b3ea7a66768d4515f7b35, data reload: false

------ Round 1 ----------------------------------
============================================
q1	17587	5101	5033	5033
q2	2113	345	251	251
q3	10226	1384	806	806
q4	4672	463	348	348
q5	7433	791	525	525
q6	180	188	152	152
q7	747	789	596	596
q8	9243	1300	1107	1107
q9	5438	4202	4171	4171
q10	6832	1276	1034	1034
q11	441	248	237	237
q12	652	395	278	278
q13	18079	3537	3017	3017
q14	269	279	244	244
q15	q16	728	709	661	661
q17	1174	742	567	567
q18	6975	6170	6062	6062
q19	1105	959	566	566
q20	385	332	218	218
q21	4668	2151	2324	2151
q22	345	294	260	260
Total cold run time: 99292 ms
Total hot run time: 28284 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	5566	5603	5620	5603
q2	655	569	554	554
q3	5187	5384	4924	4924
q4	1986	2140	1545	1545
q5	5260	5157	5115	5115
q6	232	185	193	185
q7	2434	2070	1832	1832
q8	3010	2601	2733	2601
q9	7949	7998	8019	7998
q10	3719	3702	3250	3250
q11	610	434	400	400
q12	860	744	534	534
q13	3622	3811	3264	3264
q14	335	321	296	296
q15	q16	746	732	638	638
q17	8307	7299	6875	6875
q18	13033	12157	12774	12157
q19	899	776	776	776
q20	2166	2145	1927	1927
q21	5523	4665	4760	4665
q22	519	459	406	406
Total cold run time: 72618 ms
Total hot run time: 65545 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 150437 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit 2e14addd82146902041b3ea7a66768d4515f7b35, data reload: false

query5	4314	575	442	442
query6	420	205	188	188
query7	4815	451	228	228
query8	338	166	152	152
query9	8745	3119	3112	3112
query10	469	311	251	251
query11	6073	3886	3639	3639
query12	155	98	91	91
query13	1276	461	340	340
query14	6151	3934	3688	3688
query14_1	3562	3441	3448	3441
query15	205	196	183	183
query16	943	431	402	402
query17	861	692	529	529
query18	2420	429	318	318
query19	197	166	137	137
query20	88	87	85	85
query21	209	133	107	107
query22	13045	13028	12936	12936
query23	13456	12806	12400	12400
query23_1	12548	12487	12515	12487
query24	7364	845	466	466
query24_1	492	490	481	481
query25	536	416	391	391
query26	1253	247	137	137
query27	2811	439	279	279
query28	4672	1942	1921	1921
query29	1547	571	438	438
query30	301	223	184	184
query31	1003	924	809	809
query32	146	92	91	91
query33	516	288	238	238
query34	874	835	506	506
query35	729	755	639	639
query36	800	802	730	730
query37	128	99	84	84
query38	1756	1728	1654	1654
query39	773	787	735	735
query39_1	754	726	736	726
query40	221	112	93	93
query41	66	65	61	61
query42	89	84	79	79
query43	346	363	309	309
query44	1334	679	680	679
query45	183	175	163	163
query46	833	961	570	570
query47	2924	2893	2439	2439
query48	296	300	226	226
query49	569	402	290	290
query50	697	283	210	210
query51	10708	10855	10553	10553
query52	78	81	71	71
query53	197	214	151	151
query54	244	203	189	189
query55	74	65	65	65
query56	227	217	222	217
query57	1655	1651	1565	1565
query58	287	263	264	263
query59	2106	2122	1953	1953
query60	283	240	224	224
query61	150	145	145	145
query62	386	334	287	287
query63	194	155	162	155
query64	2739	957	768	768
query65	3327	3296	3286	3286
query66	1781	412	309	309
query67	20012	19821	19927	19821
query68	2942	1019	609	609
query69	385	283	249	249
query70	854	796	784	784
query71	304	230	214	214
query72	2630	2607	2253	2253
query73	511	529	290	290
query74	4621	4515	4339	4339
query75	2318	2291	1928	1928
query76	2317	1006	593	593
query77	344	379	276	276
query78	9591	9693	9108	9108
query79	876	829	523	523
query80	457	407	346	346
query81	507	312	274	274
query82	204	133	101	101
query83	197	200	179	179
query84	292	124	96	96
query85	774	446	362	362
query86	251	237	221	221
query87	1982	1979	1798	1798
query88	3594	2658	2639	2639
query89	315	276	243	243
query90	2098	170	171	170
query91	160	150	126	126
query92	98	91	87	87
query93	989	975	578	578
query94	455	316	278	278
query95	591	400	308	308
query96	660	490	232	232
query97	2441	2419	2282	2282
query98	156	160	147	147
query99	743	772	633	633
Total cold run time: 233163 ms
Total hot run time: 150437 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
ClickBench: Total hot run time: 24.41 s
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/clickbench-tools
ClickBench test result on commit 2e14addd82146902041b3ea7a66768d4515f7b35, data reload: false

query1	0.01	0.01	0.01
query2	0.09	0.05	0.05
query3	0.25	0.08	0.08
query4	1.61	0.11	0.10
query5	0.26	0.25	0.24
query6	1.16	0.69	0.69
query7	0.04	0.00	0.00
query8	0.05	0.04	0.03
query9	0.40	0.38	0.34
query10	0.63	0.59	0.59
query11	0.21	0.15	0.14
query12	0.19	0.15	0.14
query13	0.49	0.48	0.48
query14	1.04	1.02	1.01
query15	0.63	0.62	0.60
query16	0.35	0.37	0.36
query17	1.16	1.15	1.13
query18	0.19	0.18	0.19
query19	2.07	1.92	1.96
query20	0.02	0.01	0.01
query21	15.48	0.20	0.14
query22	4.94	0.05	0.04
query23	16.29	0.32	0.12
query24	3.08	0.50	0.51
query25	0.09	0.08	0.07
query26	1.26	0.20	0.16
query27	0.05	0.04	0.03
query28	3.80	0.69	0.35
query29	12.48	4.16	3.26
query30	0.28	0.14	0.10
query31	2.78	0.56	0.33
query32	3.22	0.61	0.50
query33	3.19	3.19	3.31
query34	15.35	4.04	3.40
query35	3.34	3.34	3.33
query36	0.56	0.45	0.42
query37	0.10	0.06	0.06
query38	0.05	0.04	0.04
query39	0.03	0.04	0.03
query40	0.18	0.15	0.15
query41	0.09	0.03	0.03
query42	0.04	0.03	0.03
query43	0.04	0.03	0.03
Total cold run time: 97.57 s
Total hot run time: 24.41 s

@hello-stephen

Copy link
Copy Markdown
Contributor

BE UT Coverage Report

Increment line coverage 100.00% (1/1) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 66.59% (31996/48047)
Line Coverage 51.42% (337720/656796)
Region Coverage 46.78% (273302/584191)
Branch Coverage 48.52% (128801/265486)

@hello-stephen

Copy link
Copy Markdown
Contributor

BE Regression && UT Coverage Report

Increment line coverage 100.00% (1/1) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 68.60% (31936/46552)
Line Coverage 53.28% (346212/649759)
Region Coverage 48.87% (284469/582081)
Branch Coverage 50.24% (133047/264837)

@morningman

Copy link
Copy Markdown
Contributor Author

run cloud_p0

@hello-stephen

Copy link
Copy Markdown
Contributor

BE Regression && UT Coverage Report

Increment line coverage 100.00% (1/1) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 68.60% (31936/46552)
Line Coverage 53.28% (346212/649759)
Region Coverage 48.87% (284469/582081)
Branch Coverage 50.24% (133047/264837)

@morningman
morningman merged commit 708531d into apache:master Oct 26, 2026
38 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants