HappenLee commented on code in PR #66942:
URL: https://github.com/apache/doris/pull/66942#discussion_r3851192568


##########
fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/AggCombinerFunctionBuilder.java:
##########
@@ -159,7 +181,8 @@ public String parameterDisplayString() {
 
     public static boolean isAggStateCombinator(String name) {
         return name.toLowerCase().endsWith(STATE_SUFFIX) || 
name.toLowerCase().endsWith(MERGE_SUFFIX)
-                || name.toLowerCase().endsWith(UNION_SUFFIX) || 
name.toLowerCase().endsWith(FOREACH_SUFFIX);
+                || name.toLowerCase().endsWith(UNION_SUFFIX) || 
name.toLowerCase().endsWith(COMBINE_SUFFIX)

Review Comment:
   Fixed in 97fd4e04175. I removed the `_combine` nested-name fallback from 
`FunctionRegistry.isAggregateFunction()`. This intentionally matches the 
existing `_union` pre-binding behavior for now: dynamic combinator names are no 
longer classified as aggregate from the base function name alone, while 
`findFunctionBuilder()` still performs argument-aware final resolution. 
Therefore a valid one-argument `avg_combine` is still synthesized at final 
resolution, but an exact two-argument scalar UDF named `avg_combine` falls 
through to that UDF and its HAVING/ORDER BY arguments bind in aggregate-output 
scope. I added `avg_combine`/`avg_union` classification assertions and an exact 
two-argument alias UDF collision case in both HAVING and ORDER BY. The registry 
UT passed; the integration test source compiled, but the local embedded-FE 
setup could not start because this host exhausted its TCP ephemeral port pool 
before the test methods ran. We will unify dynamic combinator 
pre-binding/priority 
 separately.



##########
be/src/exprs/aggregate/aggregate_function_state_combine.h:
##########
@@ -0,0 +1,231 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#pragma once
+
+#include <utility>
+
+#include "exprs/aggregate/aggregate_function.h"
+
+namespace doris {
+const static std::string AGG_COMBINE_SUFFIX = "_combine";
+
+class AggregateStateCombine final : public 
IAggregateFunctionHelper<AggregateStateCombine> {
+public:
+    AggregateStateCombine(AggregateFunctionPtr function, const DataTypes& 
argument_types_,
+                          DataTypePtr return_type)
+            : IAggregateFunctionHelper(argument_types_),
+              _function(std::move(function)),
+              _return_type(std::move(return_type)) {}
+
+    static AggregateFunctionPtr create(AggregateFunctionPtr function,
+                                       const DataTypes& argument_types_,
+                                       const DataTypePtr& return_type) {
+        if (function == nullptr) {
+            return nullptr;
+        }
+        return std::make_shared<AggregateStateCombine>(function, 
argument_types_, return_type);
+    }
+
+    void set_version(const int version_) override {
+        IAggregateFunctionHelper::set_version(version_);
+        _function->set_version(version_);
+    }
+
+    void create(AggregateDataPtr __restrict place) const override { 
_function->create(place); }
+
+    void destroy_vec(AggregateDataPtr __restrict place,
+                     const size_t num_rows) const noexcept override {
+        _function->destroy_vec(place, num_rows);
+    }
+
+    String get_name() const override { return _function->get_name() + 
AGG_COMBINE_SUFFIX; }
+
+    DataTypePtr get_return_type() const override { return _return_type; }
+
+    void add(AggregateDataPtr __restrict place, const IColumn** columns, 
ssize_t row_num,
+             Arena& arena) const override {
+        _function->add(place, columns, row_num, arena);
+    }
+
+    void add_batch(size_t batch_size, AggregateDataPtr* places, size_t 
place_offset,
+                   const IColumn** columns, Arena& arena, bool agg_many) const 
override {
+        _function->add_batch(batch_size, places, place_offset, columns, arena, 
agg_many);
+    }
+
+    void add_batch_selected(size_t batch_size, AggregateDataPtr* places, 
size_t place_offset,
+                            const IColumn** columns, Arena& arena) const 
override {
+        _function->add_batch_selected(batch_size, places, place_offset, 
columns, arena);
+    }
+
+    void add_batch_single_place(size_t batch_size, AggregateDataPtr place, 
const IColumn** columns,
+                                Arena& arena) const override {
+        _function->add_batch_single_place(batch_size, place, columns, arena);
+    }
+
+    void add_batch_range(size_t batch_begin, size_t batch_end, 
AggregateDataPtr place,
+                         const IColumn** columns, Arena& arena, bool has_null) 
override {
+        _function->add_batch_range(batch_begin, batch_end, place, columns, 
arena, has_null);
+    }
+
+    void add_range_single_place(int64_t partition_start, int64_t 
partition_end, int64_t frame_start,
+                                int64_t frame_end, AggregateDataPtr place, 
const IColumn** columns,
+                                Arena& arena, UInt8* use_null_result,
+                                UInt8* could_use_previous_result) const 
override {
+        _function->add_range_single_place(partition_start, partition_end, 
frame_start, frame_end,
+                                          place, columns, arena, 
use_null_result,
+                                          could_use_previous_result);
+    }
+
+    void reset(AggregateDataPtr place) const override { 
_function->reset(place); }
+
+    void merge(AggregateDataPtr __restrict place, ConstAggregateDataPtr rhs,
+               Arena& arena) const override {
+        _function->merge(place, rhs, arena);
+    }
+
+    void merge_vec(const AggregateDataPtr __restrict* __restrict places, 
size_t offset,
+                   ConstAggregateDataPtr __restrict rhs, Arena& arena,
+                   const size_t num_rows) const override {
+        _function->merge_vec(places, offset, rhs, arena, num_rows);
+    }
+
+    void merge_vec_selected(const AggregateDataPtr __restrict* __restrict 
places, size_t offset,
+                            ConstAggregateDataPtr __restrict rhs, Arena& arena,
+                            const size_t num_rows) const override {
+        _function->merge_vec_selected(places, offset, rhs, arena, num_rows);
+    }
+
+    void serialize(ConstAggregateDataPtr __restrict place, BufferWritable& 
buf) const override {
+        _function->serialize(place, buf);
+    }
+
+    void serialize_vec(const std::vector<AggregateDataPtr>& places, size_t 
offset,
+                       BufferWritable& buf, const size_t num_rows) const 
override {
+        _function->serialize_vec(places, offset, buf, num_rows);
+    }
+
+    void serialize_to_column(const std::vector<AggregateDataPtr>& places, 
size_t offset,
+                             MutableColumnPtr& dst, const size_t num_rows) 
const override {
+        _function->serialize_to_column(places, offset, dst, num_rows);
+    }
+
+    void serialize_without_key_to_column(ConstAggregateDataPtr __restrict 
place,
+                                         IColumn& to) const override {
+        _function->serialize_without_key_to_column(place, to);
+    }
+
+    void deserialize(AggregateDataPtr __restrict place, BufferReadable& buf,
+                     Arena& arena) const override {
+        _function->deserialize(place, buf, arena);
+    }
+
+    void deserialize_vec(AggregateDataPtr places, const ColumnString* column, 
Arena& arena,
+                         size_t num_rows) const override {
+        _function->deserialize_vec(places, column, arena, num_rows);
+    }
+
+    void deserialize_and_merge_vec(const AggregateDataPtr* places, size_t 
offset,
+                                   AggregateDataPtr rhs, const IColumn* 
column, Arena& arena,
+                                   const size_t num_rows) const override {
+        _function->deserialize_and_merge_vec(places, offset, rhs, column, 
arena, num_rows);
+    }
+
+    void deserialize_and_merge_vec_selected(const AggregateDataPtr* places, 
size_t offset,
+                                            AggregateDataPtr rhs, const 
IColumn* column,
+                                            Arena& arena, const size_t 
num_rows) const override {
+        _function->deserialize_and_merge_vec_selected(places, offset, rhs, 
column, arena, num_rows);
+    }
+
+    void deserialize_and_merge(AggregateDataPtr __restrict place, 
AggregateDataPtr __restrict rhs,
+                               BufferReadable& buf, Arena& arena) const 
override {
+        _function->deserialize_and_merge(place, rhs, buf, arena);
+    }
+
+    void deserialize_and_merge_from_column_range(AggregateDataPtr __restrict 
place,
+                                                 const IColumn& column, size_t 
begin, size_t end,
+                                                 Arena& arena) const override {
+        _function->deserialize_and_merge_from_column_range(place, column, 
begin, end, arena);
+    }
+
+    void insert_result_into(ConstAggregateDataPtr __restrict place, IColumn& 
to) const override {
+        auto serialized_column = _function->create_serialize_column();
+        _function->serialize_without_key_to_column(place, *serialized_column);
+        DORIS_CHECK_EQ(serialized_column->size(), 1);
+        to.insert_from(*serialized_column, 0);
+    }
+
+    void insert_result_into_vec(const std::vector<AggregateDataPtr>& places, 
const size_t offset,
+                                IColumn& to, const size_t num_rows) const 
override {
+        auto serialized_column = _function->create_serialize_column();
+        _function->serialize_to_column(places, offset, serialized_column, 
num_rows);
+        DORIS_CHECK_EQ(serialized_column->size(), num_rows);
+        to.insert_range_from(*serialized_column, 0, num_rows);

Review Comment:
   Fixed in 97fd4e04175. Empty single-row destinations now call 
`serialize_without_key_to_column()` directly, and empty vector destinations 
pass an ownership-asserted mutable output column directly to 
`serialize_to_column()`, removing the temporary column and second deep copy. 
Nonempty destinations retain the temporary append-safe fallback required by 
serializers such as nullable `count` that resize their output. I also added an 
8-group x 4096-row `array_agg` state test that runs combine serialization 
through the empty vector fast path, merges every serialized state back, and 
validates all grouped array sizes. `AggregateStateCombineTest.*` passes all 4 
tests.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to