This is an automated email from the ASF dual-hosted git repository.

panxiaolei pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/master by this push:
     new ac27e4b7690 [env](compiler) Reduce compile time of 
aggregate_function_reader_replace.cpp (#62047)
ac27e4b7690 is described below

commit ac27e4b76903b3f5fe805770586984a0d4e1e211
Author: Mryange <[email protected]>
AuthorDate: Fri Apr 3 16:36:42 2026 +0800

    [env](compiler) Reduce compile time of 
aggregate_function_reader_replace.cpp (#62047)
    
    `aggregate_function_reader_replace.cpp` took **86.91s** to compile
    because it included `aggregate_function_reader_first_last.h`, which
    pulled in heavy template machinery: a 28-case column-type switch × 4
    bool combinations × 4 factory functions = ~448
    `make_shared<ReaderFunctionData<...>>` instantiations in a single TU.
    
    ### How is it solved?
    
    1. **Decouple from `aggregate_function_reader_first_last.h`**: The
    reader/load path now has a self-contained, lightweight implementation
    directly in the `.cpp` file. The shared header is no longer included.
    
    2. **Introduce `PointerStore<ArgIsNullable>` /
    `CopyStore<ArgIsNullable>`**: Two small storage classes with a uniform
    interface (`is_null`, `set_value<SkipNull>`, `insert_into`, `reset`).
    Reader path uses zero-copy `PointerStore`; load path uses `CopyStore`
    with `Field` deep-copy.
    
    3. **Use 3 bool template params `<IsFirst, SkipNull, ArgIsNullable>`**:
    `IsCopy` is derived as `!IsFirst`. `ArgIsNullable` is resolved at
    registration time (nullable vs non-nullable factory map), eliminating
    runtime branches. `result_is_nullable` stays as a runtime bool (called
    once per group).
    
    4. **All column operations use `IColumn` virtual dispatch**: No
    column-type-specific template instantiations. Performance impact is
    negligible — `Field` operations and the `add()` virtual call itself
    dominate.
    
    5. **Clean up `aggregate_function_reader_first_last.h`**: Removed
    `ReaderFunctionFirstData`, `ReaderFunctionLastData`,
    `ReaderFunctionFirstNonNullData`, `ReaderFunctionLastNonNullData`,
    `ReaderFunctionData`, `create_function_single_value`, the 28-case
    switch, and the `CREATE_READER_FUNCTION_WITH_NAME_AND_DATA` macro — all
    now unused. Also removed unnecessary includes (`column_array.h`,
    `column_map.h`, etc.).
    
    ### Compile time
    
    | Stage | Time |
    |-------|------|
    | Before | 86.91s |
    | After | **17.0s (↓80%)** |
---
 .../aggregate_function_reader_first_last.h         | 304 +--------------------
 .../aggregate_function_reader_replace.cpp          | 262 ++++++++++++++++--
 2 files changed, 242 insertions(+), 324 deletions(-)

diff --git a/be/src/exprs/aggregate/aggregate_function_reader_first_last.h 
b/be/src/exprs/aggregate/aggregate_function_reader_first_last.h
index 4a2caaee43f..b3826723f1e 100644
--- a/be/src/exprs/aggregate/aggregate_function_reader_first_last.h
+++ b/be/src/exprs/aggregate/aggregate_function_reader_first_last.h
@@ -19,17 +19,7 @@
 
 #include <type_traits>
 
-#include "core/column/column_array.h"
-#include "core/column/column_map.h"
 #include "core/column/column_nullable.h"
-#include "core/column/column_struct.h"
-#include "core/column/column_variant.h"
-#include "core/column/column_vector.h"
-#include "core/data_type/data_type_decimal.h"
-#include "core/data_type/data_type_nullable.h"
-#include "core/data_type/data_type_string.h"
-#include "exprs/aggregate/aggregate_function.h"
-#include "exprs/function/function.h"
 
 namespace doris {
 #include "common/compile_check_begin.h"
@@ -177,297 +167,5 @@ protected:
     bool _has_value = false;
 };
 
-template <typename ColVecType, bool result_is_nullable, bool arg_is_nullable, 
bool is_copy>
-struct ReaderFunctionFirstData
-        : ReaderFirstAndLastData<ColVecType, result_is_nullable, 
arg_is_nullable, is_copy> {
-    void add(int64_t row, const IColumn** columns) {
-        if (this->has_set_value()) {
-            return;
-        }
-        this->set_value(columns, row);
-    }
-    static const char* name() { return "first_value"; }
-};
-
-template <typename ColVecType, bool result_is_nullable, bool arg_is_nullable, 
bool is_copy>
-struct ReaderFunctionFirstNonNullData
-        : ReaderFirstAndLastData<ColVecType, result_is_nullable, 
arg_is_nullable, is_copy> {
-    void add(int64_t row, const IColumn** columns) {
-        if (this->has_set_value()) {
-            return;
-        }
-        if constexpr (ReaderFirstAndLastData<ColVecType, result_is_nullable, 
arg_is_nullable,
-                                             is_copy>::nullable) {
-            const auto* nullable_column =
-                    assert_cast<const ColumnNullable*, 
TypeCheckOnRelease::DISABLE>(columns[0]);
-            if (nullable_column->is_null_at(row)) {
-                return;
-            }
-        }
-        this->set_value(columns, row);
-    }
-    static const char* name() { return "first_non_null_value"; }
-};
-
-template <typename ColVecType, bool result_is_nullable, bool arg_is_nullable, 
bool is_copy>
-struct ReaderFunctionLastData
-        : ReaderFirstAndLastData<ColVecType, result_is_nullable, 
arg_is_nullable, is_copy> {
-    void add(int64_t row, const IColumn** columns) { this->set_value(columns, 
row); }
-    static const char* name() { return "last_value"; }
-};
-
-template <typename ColVecType, bool result_is_nullable, bool arg_is_nullable, 
bool is_copy>
-struct ReaderFunctionLastNonNullData
-        : ReaderFirstAndLastData<ColVecType, result_is_nullable, 
arg_is_nullable, is_copy> {
-    void add(int64_t row, const IColumn** columns) {
-        if constexpr (ReaderFirstAndLastData<ColVecType, result_is_nullable, 
arg_is_nullable,
-                                             is_copy>::nullable) {
-            const auto* nullable_column =
-                    assert_cast<const ColumnNullable*, 
TypeCheckOnRelease::DISABLE>(columns[0]);
-            if (nullable_column->is_null_at(row)) {
-                return;
-            }
-        }
-        this->set_value(columns, row);
-    }
-
-    static const char* name() { return "last_non_null_value"; }
-};
-
-template <typename Data>
-class ReaderFunctionData final
-        : public IAggregateFunctionDataHelper<Data, ReaderFunctionData<Data>> {
-public:
-    ReaderFunctionData(const DataTypes& argument_types_)
-            : IAggregateFunctionDataHelper<Data, 
ReaderFunctionData<Data>>(argument_types_),
-              _argument_type(argument_types_[0]) {}
-
-    String get_name() const override { return Data::name(); }
-
-    DataTypePtr get_return_type() const override {
-        if constexpr (Data::result_nullable) {
-            return make_nullable(_argument_type);
-        } else {
-            return _argument_type;
-        }
-    }
-
-    void insert_result_into(ConstAggregateDataPtr place, IColumn& to) const 
override {
-        this->data(place).insert_result_into(to);
-    }
-
-    void add(AggregateDataPtr place, const IColumn** columns, ssize_t row_num,
-             Arena&) const override {
-        this->data(place).add(row_num, columns);
-    }
-
-    void reset(AggregateDataPtr place) const override { 
this->data(place).reset(); }
-
-    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*, UInt8*) const override {
-        throw doris::Exception(
-                Status::FatalError("ReaderFunctionData do not support 
add_range_single_place"));
-    }
-    void merge(AggregateDataPtr place, ConstAggregateDataPtr rhs, Arena&) 
const override {
-        throw doris::Exception(Status::FatalError("ReaderFunctionData do not 
support merge"));
-    }
-    void serialize(ConstAggregateDataPtr place, BufferWritable& buf) const 
override {
-        throw doris::Exception(Status::FatalError("ReaderFunctionData do not 
support serialize"));
-    }
-    void deserialize(AggregateDataPtr place, BufferReadable& buf, Arena&) 
const override {
-        throw doris::Exception(Status::FatalError("ReaderFunctionData do not 
support deserialize"));
-    }
-
-private:
-    DataTypePtr _argument_type;
-};
-
-template <template <typename, bool, bool, bool> class FunctionData, bool 
result_is_nullable,
-          bool arg_is_nullable, bool is_copy = false>
-AggregateFunctionPtr create_function_single_value(const String& name,
-                                                  const DataTypes& 
argument_types) {
-    switch (argument_types[0]->get_primitive_type()) {
-    case PrimitiveType::TYPE_BOOLEAN: {
-        return std::make_shared<ReaderFunctionData<
-                FunctionData<ColumnUInt8, result_is_nullable, arg_is_nullable, 
is_copy>>>(
-                argument_types);
-    }
-    case PrimitiveType::TYPE_TINYINT: {
-        return std::make_shared<ReaderFunctionData<
-                FunctionData<ColumnInt8, result_is_nullable, arg_is_nullable, 
is_copy>>>(
-                argument_types);
-    }
-    case PrimitiveType::TYPE_SMALLINT: {
-        return std::make_shared<ReaderFunctionData<
-                FunctionData<ColumnInt16, result_is_nullable, arg_is_nullable, 
is_copy>>>(
-                argument_types);
-    }
-    case PrimitiveType::TYPE_INT: {
-        return std::make_shared<ReaderFunctionData<
-                FunctionData<ColumnInt32, result_is_nullable, arg_is_nullable, 
is_copy>>>(
-                argument_types);
-    }
-    case PrimitiveType::TYPE_BIGINT: {
-        return std::make_shared<ReaderFunctionData<
-                FunctionData<ColumnInt64, result_is_nullable, arg_is_nullable, 
is_copy>>>(
-                argument_types);
-    }
-    case PrimitiveType::TYPE_LARGEINT: {
-        return std::make_shared<ReaderFunctionData<
-                FunctionData<ColumnInt128, result_is_nullable, 
arg_is_nullable, is_copy>>>(
-                argument_types);
-    }
-    case PrimitiveType::TYPE_FLOAT: {
-        return std::make_shared<ReaderFunctionData<
-                FunctionData<ColumnFloat32, result_is_nullable, 
arg_is_nullable, is_copy>>>(
-                argument_types);
-    }
-    case PrimitiveType::TYPE_DOUBLE: {
-        return std::make_shared<ReaderFunctionData<
-                FunctionData<ColumnFloat64, result_is_nullable, 
arg_is_nullable, is_copy>>>(
-                argument_types);
-    }
-    case PrimitiveType::TYPE_DECIMAL32: {
-        return std::make_shared<ReaderFunctionData<
-                FunctionData<ColumnDecimal32, result_is_nullable, 
arg_is_nullable, is_copy>>>(
-                argument_types);
-    }
-    case PrimitiveType::TYPE_DECIMAL64: {
-        return std::make_shared<ReaderFunctionData<
-                FunctionData<ColumnDecimal64, result_is_nullable, 
arg_is_nullable, is_copy>>>(
-                argument_types);
-    }
-    case PrimitiveType::TYPE_DECIMAL128I: {
-        return std::make_shared<ReaderFunctionData<
-                FunctionData<ColumnDecimal128V3, result_is_nullable, 
arg_is_nullable, is_copy>>>(
-                argument_types);
-    }
-    case PrimitiveType::TYPE_DECIMALV2: {
-        return std::make_shared<ReaderFunctionData<
-                FunctionData<ColumnDecimal128V2, result_is_nullable, 
arg_is_nullable, is_copy>>>(
-                argument_types);
-    }
-    case PrimitiveType::TYPE_DECIMAL256: {
-        return std::make_shared<ReaderFunctionData<
-                FunctionData<ColumnDecimal256, result_is_nullable, 
arg_is_nullable, is_copy>>>(
-                argument_types);
-    }
-    case PrimitiveType::TYPE_STRING:
-    case PrimitiveType::TYPE_CHAR:
-    case PrimitiveType::TYPE_VARCHAR:
-    case PrimitiveType::TYPE_JSONB: {
-        return std::make_shared<ReaderFunctionData<
-                FunctionData<ColumnString, result_is_nullable, 
arg_is_nullable, is_copy>>>(
-                argument_types);
-    }
-    case PrimitiveType::TYPE_DATE: {
-        return std::make_shared<ReaderFunctionData<
-                FunctionData<ColumnDate, result_is_nullable, arg_is_nullable, 
is_copy>>>(
-                argument_types);
-    }
-    case PrimitiveType::TYPE_DATETIME: {
-        return std::make_shared<ReaderFunctionData<
-                FunctionData<ColumnDateTime, result_is_nullable, 
arg_is_nullable, is_copy>>>(
-                argument_types);
-    }
-    case PrimitiveType::TYPE_DATETIMEV2: {
-        return std::make_shared<ReaderFunctionData<
-                FunctionData<ColumnDateTimeV2, result_is_nullable, 
arg_is_nullable, is_copy>>>(
-                argument_types);
-    }
-    case PrimitiveType::TYPE_TIMESTAMPTZ: {
-        return std::make_shared<ReaderFunctionData<
-                FunctionData<ColumnTimeStampTz, result_is_nullable, 
arg_is_nullable, is_copy>>>(
-                argument_types);
-    }
-    case PrimitiveType::TYPE_DATEV2: {
-        return std::make_shared<ReaderFunctionData<
-                FunctionData<ColumnDateV2, result_is_nullable, 
arg_is_nullable, is_copy>>>(
-                argument_types);
-    }
-    case PrimitiveType::TYPE_IPV4: {
-        return std::make_shared<ReaderFunctionData<
-                FunctionData<ColumnIPv4, result_is_nullable, arg_is_nullable, 
is_copy>>>(
-                argument_types);
-    }
-    case PrimitiveType::TYPE_IPV6: {
-        return std::make_shared<ReaderFunctionData<
-                FunctionData<ColumnIPv6, result_is_nullable, arg_is_nullable, 
is_copy>>>(
-                argument_types);
-    }
-    case PrimitiveType::TYPE_ARRAY: {
-        return std::make_shared<ReaderFunctionData<
-                FunctionData<ColumnArray, result_is_nullable, arg_is_nullable, 
is_copy>>>(
-                argument_types);
-    }
-    case PrimitiveType::TYPE_MAP: {
-        return std::make_shared<ReaderFunctionData<
-                FunctionData<ColumnMap, result_is_nullable, arg_is_nullable, 
is_copy>>>(
-                argument_types);
-    }
-    case PrimitiveType::TYPE_STRUCT: {
-        return std::make_shared<ReaderFunctionData<
-                FunctionData<ColumnStruct, result_is_nullable, 
arg_is_nullable, is_copy>>>(
-                argument_types);
-    }
-    case PrimitiveType::TYPE_VARIANT: {
-        return std::make_shared<ReaderFunctionData<
-                FunctionData<ColumnVariant, result_is_nullable, 
arg_is_nullable, is_copy>>>(
-                argument_types);
-    }
-    case PrimitiveType::TYPE_BITMAP: {
-        return std::make_shared<ReaderFunctionData<
-                FunctionData<ColumnBitmap, result_is_nullable, 
arg_is_nullable, is_copy>>>(
-                argument_types);
-    }
-    case PrimitiveType::TYPE_HLL: {
-        return std::make_shared<ReaderFunctionData<
-                FunctionData<ColumnHLL, result_is_nullable, arg_is_nullable, 
is_copy>>>(
-                argument_types);
-    }
-    case PrimitiveType::TYPE_QUANTILE_STATE: {
-        return std::make_shared<ReaderFunctionData<
-                FunctionData<ColumnQuantileState, result_is_nullable, 
arg_is_nullable, is_copy>>>(
-                argument_types);
-    }
-    default:
-        LOG(WARNING) << "with unknowed type, failed in  
create_aggregate_function_" << name
-                     << " and type is: " << argument_types[0]->get_name();
-        return nullptr;
-    }
-}
-
-#define CREATE_READER_FUNCTION_WITH_NAME_AND_DATA(CREATE_FUNCTION_NAME, 
FUNCTION_DATA)      \
-    template <bool is_copy>                                                    
             \
-    AggregateFunctionPtr CREATE_FUNCTION_NAME(                                 
             \
-            const std::string& name, const DataTypes& argument_types,          
             \
-            const DataTypePtr& result_type, bool result_is_nullable,           
             \
-            const AggregateFunctionAttr& attr) {                               
             \
-        const bool arg_is_nullable = argument_types[0]->is_nullable();         
             \
-        AggregateFunctionPtr res = nullptr;                                    
             \
-        std::visit(                                                            
             \
-                [&](auto result_is_nullable, auto arg_is_nullable) {           
             \
-                    res = AggregateFunctionPtr(                                
             \
-                            create_function_single_value<FUNCTION_DATA, 
result_is_nullable, \
-                                                         arg_is_nullable, 
is_copy>(         \
-                                    name, argument_types));                    
             \
-                },                                                             
             \
-                make_bool_variant(result_is_nullable), 
make_bool_variant(arg_is_nullable)); \
-        if (!res) {                                                            
             \
-            LOG(WARNING) << " failed in  create_aggregate_function_" << name   
             \
-                         << " and type is: " << argument_types[0]->get_name(); 
             \
-        }                                                                      
             \
-        return res;                                                            
             \
-    }
-
-CREATE_READER_FUNCTION_WITH_NAME_AND_DATA(create_aggregate_function_first, 
ReaderFunctionFirstData);
-CREATE_READER_FUNCTION_WITH_NAME_AND_DATA(create_aggregate_function_first_non_null_value,
-                                          ReaderFunctionFirstNonNullData);
-CREATE_READER_FUNCTION_WITH_NAME_AND_DATA(create_aggregate_function_last, 
ReaderFunctionLastData);
-CREATE_READER_FUNCTION_WITH_NAME_AND_DATA(create_aggregate_function_last_non_null_value,
-                                          ReaderFunctionLastNonNullData);
-#undef CREATE_READER_FUNCTION_WITH_NAME_AND_DATA
-
 } // namespace doris
-#include "common/compile_check_end.h"
\ No newline at end of file
+#include "common/compile_check_end.h"
diff --git a/be/src/exprs/aggregate/aggregate_function_reader_replace.cpp 
b/be/src/exprs/aggregate/aggregate_function_reader_replace.cpp
index da0c13d2a88..9b732ec24e9 100644
--- a/be/src/exprs/aggregate/aggregate_function_reader_replace.cpp
+++ b/be/src/exprs/aggregate/aggregate_function_reader_replace.cpp
@@ -15,36 +15,256 @@
 // specific language governing permissions and limitations
 // under the License.
 
+// Self-contained implementation for replace/replace_if_not_null reader/load 
aggregation.
+// Deliberately does NOT include aggregate_function_reader_first_last.h to 
avoid pulling in
+// heavy template machinery shared with the window-function path.
+
+#include "core/column/column_nullable.h"
+#include "core/field.h"
+#include "exprs/aggregate/aggregate_function.h"
 #include "exprs/aggregate/aggregate_function_reader.h"
-#include "exprs/aggregate/aggregate_function_reader_first_last.h"
 #include "exprs/aggregate/aggregate_function_simple_factory.h"
 
 namespace doris {
 #include "common/compile_check_begin.h"
 
+// ---------------------------------------------------------------------------
+// Storage layer: PointerStore / CopyStore
+// Uniform interface: is_null(), set_value<SkipNull>(), insert_into(), reset().
+// set_value returns false when SkipNull && the row is null (caller should not
+// update _has_value); returns true otherwise.
+// ---------------------------------------------------------------------------
+
+// Zero-copy storage: keeps a pointer into the source column (reader path).
+template <bool ArgIsNullable>
+struct PointerStore {
+    const IColumn* _ptr = nullptr;
+    size_t _offset = 0;
+
+    bool is_null() const {
+        if (_ptr == nullptr) {
+            return true;
+        }
+        if constexpr (ArgIsNullable) {
+            return assert_cast<const ColumnNullable*, 
TypeCheckOnRelease::DISABLE>(_ptr)
+                    ->is_null_at(_offset);
+        }
+        return false;
+    }
+
+    template <bool SkipNull>
+    bool set_value(const IColumn* column, size_t row) {
+        if constexpr (SkipNull && ArgIsNullable) {
+            const auto* nc =
+                    assert_cast<const ColumnNullable*, 
TypeCheckOnRelease::DISABLE>(column);
+            if (nc->is_null_at(row)) {
+                return false;
+            }
+        }
+        _ptr = column;
+        _offset = row;
+        return true;
+    }
+
+    void insert_into(IColumn& to) const {
+        if constexpr (ArgIsNullable) {
+            const auto* nc = assert_cast<const ColumnNullable*, 
TypeCheckOnRelease::DISABLE>(_ptr);
+            to.insert_from(nc->get_nested_column(), _offset);
+        } else {
+            to.insert_from(*_ptr, _offset);
+        }
+    }
+
+    void reset() {
+        _ptr = nullptr;
+        _offset = 0;
+    }
+};
+
+// Deep-copy storage: copies the value into a Field (load path).
+template <bool ArgIsNullable>
+struct CopyStore {
+    Field _value;
+    bool _is_null = true;
+
+    bool is_null() const { return _is_null; }
+
+    template <bool SkipNull>
+    bool set_value(const IColumn* column, size_t row) {
+        if constexpr (ArgIsNullable) {
+            const auto* nc =
+                    assert_cast<const ColumnNullable*, 
TypeCheckOnRelease::DISABLE>(column);
+            if (nc->is_null_at(row)) {
+                if constexpr (SkipNull) {
+                    return false;
+                }
+                _is_null = true;
+                return true;
+            }
+            nc->get_nested_column().get(row, _value);
+        } else {
+            column->get(row, _value);
+        }
+        _is_null = false;
+        return true;
+    }
+
+    void insert_into(IColumn& to) const { to.insert(_value); }
+
+    void reset() {
+        _is_null = true;
+        _value = {};
+    }
+};
+
+// ---------------------------------------------------------------------------
+// Data layer: ReaderReplaceData
+// Template params: IsFirst, SkipNull, ArgIsNullable
+// IsCopy is derived: reader (IsFirst=true) deep-copies via Field because the 
source
+// column will be reused; load (IsFirst=false) keeps a zero-copy pointer 
because
+// insert_result_into is called while the column is still alive.
+// ---------------------------------------------------------------------------
+template <bool IsFirst, bool SkipNull, bool ArgIsNullable>
+struct ReaderReplaceData {
+    static constexpr bool IsCopy = IsFirst;
+    using Store = std::conditional_t<IsCopy, CopyStore<ArgIsNullable>, 
PointerStore<ArgIsNullable>>;
+
+    Store _store;
+    bool _has_value = false;
+
+    void add(int64_t row, const IColumn** columns) {
+        if constexpr (IsFirst) {
+            if (_has_value) {
+                return;
+            }
+        }
+        if (_store.template set_value<SkipNull>(columns[0], row)) {
+            _has_value = true;
+        }
+    }
+
+    void insert_result_into(IColumn& to, bool result_is_nullable) const {
+        if (result_is_nullable) {
+            auto& nullable_col = assert_cast<ColumnNullable&>(to);
+            if (!_has_value || _store.is_null()) {
+                nullable_col.insert_default();
+            } else {
+                nullable_col.get_null_map_data().push_back(0);
+                _store.insert_into(nullable_col.get_nested_column());
+            }
+        } else {
+            _store.insert_into(to);
+        }
+    }
+
+    void reset() {
+        _has_value = false;
+        _store.reset();
+    }
+};
+
+// ---------------------------------------------------------------------------
+// Aggregate function class
+// ---------------------------------------------------------------------------
+template <bool IsFirst, bool SkipNull, bool ArgIsNullable>
+class ReaderReplaceFunction final
+        : public IAggregateFunctionDataHelper<
+                  ReaderReplaceData<IsFirst, SkipNull, ArgIsNullable>,
+                  ReaderReplaceFunction<IsFirst, SkipNull, ArgIsNullable>> {
+    using Data = ReaderReplaceData<IsFirst, SkipNull, ArgIsNullable>;
+
+public:
+    ReaderReplaceFunction(const DataTypes& argument_types_, bool 
result_is_nullable)
+            : IAggregateFunctionDataHelper<Data,
+                                           ReaderReplaceFunction<IsFirst, 
SkipNull, ArgIsNullable>>(
+                      argument_types_),
+              _argument_type(argument_types_[0]),
+              _result_is_nullable(result_is_nullable) {}
+
+    String get_name() const override { return "reader_replace"; }
+
+    DataTypePtr get_return_type() const override {
+        if (_result_is_nullable) {
+            return make_nullable(_argument_type);
+        }
+        return _argument_type;
+    }
+
+    void add(AggregateDataPtr place, const IColumn** columns, ssize_t row_num,
+             Arena&) const override {
+        this->data(place).add(row_num, columns);
+    }
+
+    void insert_result_into(ConstAggregateDataPtr place, IColumn& to) const 
override {
+        this->data(place).insert_result_into(to, _result_is_nullable);
+    }
+
+    void reset(AggregateDataPtr place) const override { 
this->data(place).reset(); }
+
+    void add_range_single_place(int64_t, int64_t, int64_t, int64_t, 
AggregateDataPtr,
+                                const IColumn**, Arena&, UInt8*, UInt8*) const 
override {
+        throw doris::Exception(Status::FatalError(
+                "ReaderReplaceFunction does not support 
add_range_single_place"));
+    }
+    void merge(AggregateDataPtr, ConstAggregateDataPtr, Arena&) const override 
{
+        throw doris::Exception(Status::FatalError("ReaderReplaceFunction does 
not support merge"));
+    }
+    void serialize(ConstAggregateDataPtr, BufferWritable&) const override {
+        throw doris::Exception(
+                Status::FatalError("ReaderReplaceFunction does not support 
serialize"));
+    }
+    void deserialize(AggregateDataPtr, BufferReadable&, Arena&) const override 
{
+        throw doris::Exception(
+                Status::FatalError("ReaderReplaceFunction does not support 
deserialize"));
+    }
+
+private:
+    DataTypePtr _argument_type;
+    bool _result_is_nullable;
+};
+
+// ---------------------------------------------------------------------------
+// Factory helpers
+// ---------------------------------------------------------------------------
+template <bool IsFirst, bool SkipNull, bool ArgIsNullable>
+static AggregateFunctionPtr create_reader_replace(const std::string& /*name*/,
+                                                  const DataTypes& 
argument_types_,
+                                                  const DataTypePtr& 
/*result_type*/,
+                                                  bool result_is_nullable,
+                                                  const AggregateFunctionAttr& 
/*attr*/) {
+    return std::make_shared<ReaderReplaceFunction<IsFirst, SkipNull, 
ArgIsNullable>>(
+            argument_types_, result_is_nullable);
+}
+
+// ---------------------------------------------------------------------------
+// Registration
+// ---------------------------------------------------------------------------
+
 // only replace function in load/reader do different agg operation.
 // because Doris can ensure that the data is globally ordered in reader, but 
cannot in load
-// 1. reader, get the first value of input data.
-// 2. load, get the last value of input data.
+// 1. reader: get the first value of input data  (IsFirst=true  → CopyStore, 
deep copy)
+// 2. load:   get the last  value of input data  (IsFirst=false → 
PointerStore, zero-copy)
 void 
register_aggregate_function_replace_reader_load(AggregateFunctionSimpleFactory& 
factory) {
-    auto register_function = [&](const std::string& name, const std::string& 
suffix,
-                                 const AggregateFunctionCreator& creator, bool 
nullable) {
-        factory.register_function(name + suffix, creator, nullable);
-    };
-
-    register_function("replace", AGG_READER_SUFFIX, 
create_aggregate_function_first<true>, false);
-    register_function("replace", AGG_READER_SUFFIX, 
create_aggregate_function_first<true>, true);
-    register_function("replace", AGG_LOAD_SUFFIX, 
create_aggregate_function_last<false>, false);
-    register_function("replace", AGG_LOAD_SUFFIX, 
create_aggregate_function_last<false>, true);
-
-    register_function("replace_if_not_null", AGG_READER_SUFFIX,
-                      create_aggregate_function_first_non_null_value<true>, 
false);
-    register_function("replace_if_not_null", AGG_READER_SUFFIX,
-                      create_aggregate_function_first_non_null_value<true>, 
true);
-    register_function("replace_if_not_null", AGG_LOAD_SUFFIX,
-                      create_aggregate_function_last_non_null_value<false>, 
false);
-    register_function("replace_if_not_null", AGG_LOAD_SUFFIX,
-                      create_aggregate_function_last_non_null_value<false>, 
true);
+    auto reg = [&](const std::string& name, const std::string& suffix,
+                   const AggregateFunctionCreator& creator,
+                   bool nullable) { factory.register_function(name + suffix, 
creator, nullable); };
+
+    //                                         IsFirst SkipNull ArgNullable
+    // replace – reader (first, pointer, accept null)
+    reg("replace", AGG_READER_SUFFIX, create_reader_replace<true, false, 
false>, false);
+    reg("replace", AGG_READER_SUFFIX, create_reader_replace<true, false, 
true>, true);
+
+    // replace – load (last, copy, accept null)
+    reg("replace", AGG_LOAD_SUFFIX, create_reader_replace<false, false, 
false>, false);
+    reg("replace", AGG_LOAD_SUFFIX, create_reader_replace<false, false, true>, 
true);
+
+    // replace_if_not_null – reader (first, pointer, skip null)
+    reg("replace_if_not_null", AGG_READER_SUFFIX, create_reader_replace<true, 
true, false>, false);
+    reg("replace_if_not_null", AGG_READER_SUFFIX, create_reader_replace<true, 
true, true>, true);
+
+    // replace_if_not_null – load (last, copy, skip null)
+    reg("replace_if_not_null", AGG_LOAD_SUFFIX, create_reader_replace<false, 
true, false>, false);
+    reg("replace_if_not_null", AGG_LOAD_SUFFIX, create_reader_replace<false, 
true, true>, true);
 }
 
 } // namespace doris


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

Reply via email to