[feat](array) support trim_array function - #67397
Conversation
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
|
Thank you for your contribution to Apache Doris. Please clearly describe your PR:
|
|
run buildall |
FE UT Coverage ReportIncrement line coverage |
|
run vault_p0 |
|
/review |
BE Regression && UT Coverage ReportIncrement line coverage Increment coverage report
|
FE Regression Coverage ReportIncrement line coverage |
| if (UNLIKELY(size < 0)) { | ||
| return Status::InvalidArgument("size must not be negative: {}", size); | ||
| } | ||
| if (UNLIKELY(static_cast<size_t>(size) > cardinality)) { |
There was a problem hiding this comment.
这里估计得重写 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(); |
There was a problem hiding this comment.
use unpack_if_const and index_check_const better, you can refer to: #64175
| auto offset_column = ColumnInt64::create(input_rows_count, 1); | ||
| slice_array(dst, src, *offset_column, length_column.get()); |
There was a problem hiding this comment.
slice_array 里面看起来 array 的每个元素都调用了一次虚函数insert_from/insert_default, 这里就别用了,每行调用一次insert_range_from应该就行, 参考:
| 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); |
There was a problem hiding this comment.
作为一个支持新函数的 pr,我认为这个 pr 里面非必要尽量别改动原有的test_util结构
There was a problem hiding this comment.
需要加强一波测试,这里面 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)" |
There was a problem hiding this comment.
also need testFoldConst to double check
|
/review |
There was a problem hiding this comment.
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()); |
There was a problem hiding this comment.
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.
| 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))" |
There was a problem hiding this comment.
这些常量的,最好都再测一遍testFoldConst 保证 be 和 fe 常量折叠 行为一致
There was a problem hiding this comment.
感谢您的审查!两个评论已在11f8aa5c中得到处理,能否请您再看一遍?谢谢。
1、BE实现目前会在第一遍遍历中验证修剪大小并计算输出偏移量,同时进行溢出检查。随后在第二遍遍历中预留总保留元素数量,并批量复制保留的范围。
2、目前,所有17个成功的常量输入案例均采用testFoldConst来验证前端常量折叠与后端执行的一致性,涵盖NULL值、可空元素、嵌套数组、空数组及不同元素类型。
|
/review |
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 implementationtrims 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 results:
BE ASAN build passed.
FunctionArrayTrimTest.all_argument_combinationspassed.FE unit tests passed: 2 tests, 0 failures and 0 errors.
The trim_array regression suite passed with no mismatches.
run-be-ut.shandrun-regression-test.shcompletedsuccessfully for the relevant tests.
Behavior changed:
trim_arrayscalar function.Does this need documentation?
[doc] Add trim_array function documentation doris-website#4106
Check List (For Reviewer who merge this PR)