Skip to content

[feat](array) support trim_array function - #67397

Merged
HappenLee merged 3 commits into
apache:masterfrom
vajaw:feature/trim-array
Sep 21, 2026
Merged

HappenLee merged 3 commits into
apache:masterfrom
vajaw:feature/trim-array

Conversation

@vajaw

@vajaw vajaw commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

Issue Number: Related to #48203

Related PR:
apache/doris-website#4106

Problem Summary:

Add the trim_array(array, n) scalar function. The BE implementation
trims trailing elements directly from array storage and avoids virtual
function calls in the hot loop.

Register the function and its signature in the Nereids planner. Add FE
constant folding so constant invocations can be evaluated during
planning.

Cover normal and constant columns, NULL values, nullable elements,
nested arrays, multiple element types, zero, negative and out-of-range
sizes, and the maximum BIGINT value.

Release note

Add the TRIM_ARRAY scalar function.

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

Test results:

  • BE ASAN build passed.

  • FunctionArrayTrimTest.all_argument_combinations passed.

  • FE unit tests passed: 2 tests, 0 failures and 0 errors.

  • The trim_array regression suite passed with no mismatches.

  • run-be-ut.sh and run-regression-test.sh completed
    successfully for the relevant tests.

  • Behavior changed:

    • No.
    • Yes. Adds the new trim_array scalar function.
  • Does this need documentation?

Check List (For Reviewer who merge this PR)

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

Implement trim_array in BE and register its Nereids signature.

Add constant folding, BE tests for const-column combinations, and
regression coverage for nulls, nested arrays, boundary sizes, and types.

Related to apache#48203
@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?

@vajaw

vajaw commented Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

run buildall

@linrrzqqq linrrzqqq self-assigned this Sep 3, 2026
@vajaw vajaw changed the title Support trim_array function [feat](array) support trim_array function Sep 3, 2026
@hello-stephen

Copy link
Copy Markdown
Contributor

FE UT Coverage Report

Increment line coverage 40.91% (9/22) 🎉
Increment coverage report
Complete coverage report

@vajaw

vajaw commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

run vault_p0

@vajaw

vajaw commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

/review

@hello-stephen

Copy link
Copy Markdown
Contributor

BE Regression && UT Coverage Report

Increment line coverage 100% (0/0) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 76.28% (34651/45425)
Line Coverage 61.37% (391172/637400)
Region Coverage 57.45% (328322/571522)
Branch Coverage 58.40% (149994/256828)

@hello-stephen

Copy link
Copy Markdown
Contributor

FE Regression Coverage Report

Increment line coverage 25.61% (21/82) 🎉
Increment coverage report
Complete coverage report

if (UNLIKELY(size < 0)) {
return Status::InvalidArgument("size must not be negative: {}", size);
}
if (UNLIKELY(static_cast<size_t>(size) > cardinality)) {

@linrrzqqq linrrzqqq Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

这里估计得重写 use_default_implementation_for_nulls

外层默认的 null 处理,对于 array 列来说会替换为[], which cardinality == 0, 所以如果使用默认处理框架,L156应该会报错,得手动处理 null

Status execute_impl(FunctionContext* context, Block& block, const ColumnNumbers& arguments,
uint32_t result, size_t input_rows_count) const override {
auto array_column =
block.get_by_position(arguments[0]).column->convert_to_full_column_if_const();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

use unpack_if_const and index_check_const better, you can refer to: #64175

Comment on lines +165 to +166
auto offset_column = ColumnInt64::create(input_rows_count, 1);
slice_array(dst, src, *offset_column, length_column.get());

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

slice_array 里面看起来 array 的每个元素都调用了一次虚函数insert_from/insert_default, 这里就别用了,每行调用一次insert_range_from应该就行, 参考:

Suggested change
auto offset_column = ColumnInt64::create(input_rows_count, 1);
slice_array(dst, src, *offset_column, length_column.get());
size_t keep = cardinality - size;
if (keep > 0) {
result->insert_range_from(src.array_col->get_data(), offset, keep);
}
res_offset += keep;
res_offset_data.push_back(res_offset);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

作为一个支持新函数的 pr,我认为这个 pr 里面非必要尽量别改动原有的test_util结构

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

需要加强一波测试,这里面 null 只和 size = 0 配对,还有需要支持(const, col), (col, const) 这种行为的测试等等

// under the License.

suite("test_trim_array") {
qt_trim_two "select trim_array([1, 2, 3, 4], 2)"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

also need testFoldConst to double check

@linrrzqqq

Copy link
Copy Markdown
Collaborator

/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.

Review status: complete (converged after two rounds)

I found one material BE allocation issue and left it inline.

Checkpoint conclusions:

  • Goal and proof: the new trim_array(array, n) behavior is implemented across BE execution, Nereids binding/constant folding, visitor/registration paths, and regression/unit coverage; valid, NULL, nested, boundary, and mixed const/vector cases are represented.
  • Scope: all 10 changed files and the relevant framework, column, registration, and evaluator paths were reviewed. The change stays focused on the new function and its tests.
  • Concurrency, lifecycle, and configuration: this stateless scalar function adds no shared mutable state, threads, lifecycle hook, or configuration surface.
  • Compatibility and parallel paths: FE signature/nullability/folding and BE factory/runtime routes are aligned. No persisted format, RPC, mixed-version field, storage layout, or alternate execution registration is affected.
  • Conditions and error handling: NULL precedence and negative/out-of-range validation are consistent between FE and BE; the const/nullable physical-shape and offset paths are valid on this head.
  • Tests and results: the BE, FE, and regression cases cover core valid/error/null/type combinations and their expected results are consistent with the implementation. I did not run builds or tests because the review instructions prohibit builds; the PR reports the targeted BE ASAN, BE/FE unit, and regression runs passed.
  • Observability, persistence, transactions, and data writes: not applicable; this function has no distributed operation, durable state, catalog mutation, transaction visibility, or data-write path.
  • Performance and memory: the prior per-element virtual insertion concern is fixed by bulk range copying, but the output reserve still uses the physical source size and creates the material peak-allocation/reallocation issue described inline.
  • Other: all six existing live review threads were duplicate-fenced, and no second distinct issue survived the final sweep.

User-provided focus: none beyond the full PR review.
Reviewed head: 89106bc9371f9ac6d0d6942885a6d61d20b9dfd0.

ColumnArray::ColumnOffsets::create());
auto& result_data = result_array->get_data();
auto& result_offsets = result_array->get_offsets();
result_data.reserve(src.array_col->get_data().size());

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.

result_data.reserve uses the physical source element count before any trim size is examined. For a block that trims large numeric/nullable arrays completely, this eagerly allocates almost another full input buffer (and nullable null map) while the input is still live, even though the result data is empty; near the memory limit that can fail an otherwise tiny-result query. A const array has the opposite problem because its physical source contains one array while the logical result may repeat it for every row, so this estimate under-reserves and grows repeatedly. This is separate from the earlier per-element insertion thread. Please validate sizes/build offsets in a first pass, compute the overflow-checked total retained element count, reserve that exact total, and then bulk-copy the retained ranges.

Comment on lines +22 to +37
qt_trim_zero "select trim_array([1, 2, 3, 4], 0)"
qt_trim_one "select trim_array([1, 2, 3, 4], 1)"
qt_trim_all "select trim_array([1, 2, 3, 4], 4)"
qt_trim_string "select trim_array(['a', 'b', 'c', 'd'], 1)"
qt_trim_null_element "select trim_array(['a', 'b', null, 'd'], 1)"
qt_trim_nested "select trim_array([[1, 2, 3], [4, 5, 6]], 1)"
qt_trim_empty "select trim_array(cast([] as array<int>), 0)"
qt_trim_boolean "select trim_array(cast([true, false, true] as array<boolean>), 1)"
qt_trim_tinyint "select trim_array(cast([-128, 0, 127] as array<tinyint>), 1)"
qt_trim_bigint "select trim_array(cast([-9223372036854775808, 0, 9223372036854775807] as array<bigint>), 1)"
qt_trim_double "select trim_array(cast([-1.7976931348623157E308, 0.0, 1.7976931348623157E308] as array<double>), 1)"
qt_trim_decimal "select trim_array(cast([-99999999.99, 0.00, 99999999.99] as array<decimal(10, 2)>), 1)"
qt_trim_date "select trim_array(cast(['0000-01-01', '2024-02-29', '9999-12-31'] as array<date>), 1)"
qt_trim_null_array "select trim_array(cast(null as array<int>), 0)"
qt_trim_null_array_invalid_size "select trim_array(cast(null as array<int>), 9223372036854775807)"
qt_trim_null_size "select trim_array([1, 2, 3], cast(null as bigint))"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

这些常量的,最好都再测一遍testFoldConst 保证 be 和 fe 常量折叠 行为一致

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

感谢您的审查!两个评论已在11f8aa5c中得到处理,能否请您再看一遍?谢谢。
1、BE实现目前会在第一遍遍历中验证修剪大小并计算输出偏移量,同时进行溢出检查。随后在第二遍遍历中预留总保留元素数量,并批量复制保留的范围。
2、目前,所有17个成功的常量输入案例均采用testFoldConst来验证前端常量折叠与后端执行的一致性,涵盖NULL值、可空元素、嵌套数组、空数组及不同元素类型。

@vajaw

vajaw commented Sep 7, 2026

Copy link
Copy Markdown
Contributor Author

/review

1 similar comment
@linrrzqqq

Copy link
Copy Markdown
Collaborator

/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.

Review status: complete (converged after two rounds)

No blocking findings remain on the current head. From a code-review perspective, this looks ready; I am submitting a comment-only review with no inline comments.

Checkpoint conclusions:

  • Goal and proof: trim_array(array, size) is implemented end to end across BE execution, Nereids registration, typing, constant folding, visitor/lowering paths, and focused BE/FE/regression coverage. The implementation preserves the array type and retained-element order, propagates outer NULLs, preserves nullable elements, and rejects negative or over-cardinality sizes.
  • Scope and reuse: all 10 changed files and the relevant function framework, column-copy, evaluator, registration, and test-harness paths were reviewed. Existing abstractions are reused and the change stays focused on the new scalar function.
  • Conditions and error handling: all const/vector argument shapes, outer-NULL precedence, empty/full trims, cumulative offsets, overflow checking, nested range copying, and invalid-size errors were traced. Invalid literal fold attempts return the original expression and reach BE, so the regression error cases cover the BE branches.
  • Compatibility and parallel paths: FE signatures/nullability/folding and the registered BE symbol/runtime behavior align. No persisted format, RPC field, storage layout, mixed-version protocol, catalog state, or alternate execution path is changed.
  • Concurrency, lifecycle, configuration, observability, transactions, and writes: not applicable; this stateless scalar function introduces no shared mutable state, lifecycle hook, configuration surface, distributed operation, durable state, transaction boundary, or data-write path.
  • Performance and memory: the implementation bulk-copies retained ranges and reserves the exact overflow-checked retained element count; the earlier per-element-copy and source-sized-reservation concerns are resolved on this head.
  • Tests and results: the added tests cover value/type preservation, invalid sizes, visitor/child rewriting, all const/vector shapes, outer NULLs, nullable and nested elements, representative element types, fold-versus-runtime parity, ordering, and exact error text. I inspected the test code and expected results but did not run builds or tests because the review instructions prohibit them.
  • Other: the existing live threads were treated as duplicate fences. One Round 1 test-routing candidate was independently disproved in Round 2, and no distinct issue survived the final missed-area sweep.

User-provided focus: none beyond the full PR review.

Reviewed head: 11f8aa5ca2aeb304ebe958d4be219902325a9bf3.

@linrrzqqq

Copy link
Copy Markdown
Collaborator

run buildall

@hello-stephen

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

------ Round 1 ----------------------------------
============================================
q1	17552	3120	3130	3120
q2	2110	260	216	216
q3	10236	852	520	520
q4	4666	245	202	202
q5	7680	555	385	385
q6	142	116	96	96
q7	612	498	377	377
q8	9238	861	944	861
q9	3398	2385	2387	2385
q10	6517	867	703	703
q11	396	198	180	180
q12	632	263	203	203
q13	18102	1536	1137	1137
q14	158	147	142	142
q15	q16	432	392	378	378
q17	1413	899	810	810
q18	3097	2240	2237	2237
q19	1285	904	717	717
q20	367	288	199	199
q21	5574	1620	1849	1620
q22	325	269	229	229
Total cold run time: 93932 ms
Total hot run time: 16717 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	3450	3363	3406	3363
q2	512	385	377	377
q3	2255	2313	2183	2183
q4	1170	1163	884	884
q5	2182	2097	2078	2078
q6	167	122	93	93
q7	1012	911	856	856
q8	1592	1397	1409	1397
q9	3107	3074	3050	3050
q10	1829	1773	1629	1629
q11	357	264	252	252
q12	454	432	336	336
q13	1498	1527	1146	1146
q14	163	182	178	178
q15	q16	402	396	354	354
q17	3567	3323	3190	3190
q18	4772	4378	4700	4378
q19	886	820	943	820
q20	1006	953	820	820
q21	3871	3114	3314	3114
q22	404	357	332	332
Total cold run time: 34656 ms
Total hot run time: 30830 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 81782 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 11f8aa5ca2aeb304ebe958d4be219902325a9bf3, data reload: false

query5	4257	410	326	326
query6	374	132	135	132
query7	4975	423	238	238
query8	282	122	124	122
query9	8669	2862	2854	2854
query10	394	218	206	206
query11	5392	1041	912	912
query12	117	73	72	72
query13	1196	448	326	326
query14	5993	2188	2084	2084
query14_1	1970	1946	1948	1946
query15	175	121	109	109
query16	904	374	339	339
query17	782	446	369	369
query18	2328	327	237	237
query19	186	142	108	108
query20	74	73	70	70
query21	208	104	87	87
query22	5436	5250	5271	5250
query23	6807	6310	6097	6097
query23_1	6080	6113	6103	6103
query24	7389	1088	743	743
query24_1	745	772	790	772
query25	452	296	255	255
query26	1233	251	127	127
query27	2780	411	258	258
query28	4687	1478	1512	1478
query29	929	439	359	359
query30	250	155	131	131
query31	828	405	330	330
query32	129	79	82	79
query33	477	225	174	174
query34	999	818	495	495
query35	396	403	354	354
query36	578	570	535	535
query37	122	90	70	70
query38	995	844	812	812
query39	499	503	494	494
query39_1	463	444	476	444
query40	205	94	83	83
query41	59	56	56	56
query42	78	73	72	72
query43	245	242	210	210
query44	994	545	551	545
query45	111	108	104	104
query46	773	860	543	543
query47	766	737	708	708
query48	308	310	229	229
query49	558	291	191	191
query50	732	264	192	192
query51	7957	7966	7996	7966
query52	69	74	61	61
query53	196	190	151	151
query54	207	158	155	155
query55	72	60	60	60
query56	196	182	176	176
query57	716	686	704	686
query58	202	152	159	152
query59	1214	1224	1096	1096
query60	230	180	176	176
query61	108	131	124	124
query62	361	206	185	185
query63	171	138	142	138
query64	2696	706	594	594
query65	1615	1658	1591	1591
query66	1809	264	222	222
query67	9943	9655	9464	9464
query68	2862	1229	758	758
query69	340	225	203	203
query70	682	605	616	605
query71	257	177	159	159
query72	2251	1884	1499	1499
query73	659	607	331	331
query74	1841	1216	1118	1118
query75	1181	1106	963	963
query76	2293	723	547	547
query77	253	261	214	214
query78	3979	3616	3198	3198
query79	2742	832	574	574
query80	1631	317	271	271
query81	525	153	135	135
query82	637	132	96	96
query83	278	202	189	189
query84	295	110	85	85
query85	780	339	287	287
query86	465	176	190	176
query87	1002	969	897	897
query88	2931	2112	2103	2103
query89	299	194	172	172
query90	2028	127	140	127
query91	127	120	94	94
query92	91	74	72	72
query93	1928	1057	734	734
query94	647	240	204	204
query95	519	313	225	225
query96	776	558	288	288
query97	1066	1030	1011	1011
query98	172	138	140	138
query99	418	353	308	308
Total cold run time: 178527 ms
Total hot run time: 81782 ms

@hello-stephen

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

query1	0.01	0.00	0.00
query2	0.07	0.04	0.04
query3	0.26	0.09	0.10
query4	1.60	0.09	0.10
query5	0.17	0.16	0.15
query6	1.26	0.71	0.73
query7	0.03	0.00	0.00
query8	0.05	0.03	0.04
query9	0.29	0.22	0.21
query10	0.34	0.33	0.36
query11	0.16	0.11	0.10
query12	0.15	0.12	0.12
query13	0.31	0.31	0.32
query14	0.45	0.45	0.46
query15	0.35	0.35	0.35
query16	0.21	0.22	0.20
query17	0.72	0.69	0.67
query18	0.19	0.18	0.17
query19	1.13	1.11	1.13
query20	0.02	0.01	0.01
query21	15.49	0.16	0.12
query22	5.08	0.05	0.04
query23	16.16	0.26	0.11
query24	3.05	0.32	0.26
query25	0.11	0.04	0.04
query26	0.73	0.17	0.12
query27	0.04	0.03	0.04
query28	3.61	0.56	0.28
query29	12.47	3.24	2.57
query30	0.26	0.12	0.13
query31	2.75	0.38	0.17
query32	3.54	0.33	0.23
query33	1.36	1.51	1.40
query34	15.38	2.23	1.77
query35	1.77	1.74	1.75
query36	0.47	0.30	0.27
query37	0.06	0.04	0.04
query38	0.04	0.03	0.03
query39	0.03	0.02	0.02
query40	0.11	0.08	0.08
query41	0.07	0.02	0.03
query42	0.03	0.02	0.03
query43	0.04	0.03	0.03
Total cold run time: 90.42 s
Total hot run time: 14.65 s

@hello-stephen

Copy link
Copy Markdown
Contributor

FE UT Coverage Report

Increment line coverage 95.45% (21/22) 🎉
Increment coverage report
Complete coverage report

@hello-stephen

Copy link
Copy Markdown
Contributor

FE Regression Coverage Report

Increment line coverage 95.45% (21/22) 🎉
Increment coverage report
Complete coverage report

@linrrzqqq

Copy link
Copy Markdown
Collaborator

run beut

@hello-stephen

Copy link
Copy Markdown
Contributor

BE Regression && UT Coverage Report

Increment line coverage 100% (0/0) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 76.49% (34927/45665)
Line Coverage 61.51% (394017/640548)
Region Coverage 57.67% (330991/573954)
Branch Coverage 58.49% (151085/258299)

@linrrzqqq linrrzqqq left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM

@HappenLee HappenLee 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.

LGTM

@HappenLee HappenLee 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.

LGTM

@HappenLee
HappenLee dismissed github-actions[bot]’s stale review September 21, 2026 01:53

This automated review was superseded by the subsequent automated review of the current head 11f8aa5: #67397 (review) . That review explicitly confirms the earlier source-sized reservation issue is resolved and no blocking findings remain. Dismissing the stale changes-requested status so the PR can proceed through normal merge checks.

@HappenLee
HappenLee merged commit fdbb409 into apache:master Sep 21, 2026
39 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.

5 participants