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]