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 33fc9d5574972f..4a2caaee43feaf 100644 --- a/be/src/exprs/aggregate/aggregate_function_reader_first_last.h +++ b/be/src/exprs/aggregate/aggregate_function_reader_first_last.h @@ -17,6 +17,8 @@ #pragma once +#include + #include "core/column/column_array.h" #include "core/column/column_map.h" #include "core/column/column_nullable.h" @@ -32,7 +34,7 @@ namespace doris { #include "common/compile_check_begin.h" -template +template struct Value { public: bool is_null() const { @@ -46,6 +48,7 @@ struct Value { return false; } + template void insert_into(IColumn& to) const { if constexpr (arg_is_nullable) { const auto* col = assert_cast(_ptr); @@ -56,6 +59,17 @@ struct Value { } } + // Non-template version: virtual dispatch on IColumn::insert_from. + // Used by window path where devirtualization is not beneficial. + void insert_into(IColumn& to) const { + if constexpr (arg_is_nullable) { + const auto* col = assert_cast(_ptr); + to.insert_from(col->get_nested_column(), _offset); + } else { + to.insert_from(*_ptr, _offset); + } + } + void set_value(const IColumn* column, size_t row) { _ptr = column; _offset = row; @@ -71,15 +85,17 @@ struct Value { size_t _offset = 0; }; -template -struct CopiedValue : public Value { +template +struct CopiedValue : public Value { public: + template void insert_into(IColumn& to) const { assert_cast(to).insert(_copied_value); } bool is_null() const { return this->_ptr == nullptr; } + template void set_value(const IColumn* column, size_t row) { // here _ptr, maybe null at row, so call reset to set nullptr // But we will use is_null() check first, others have set _ptr column to a meaningless address @@ -108,8 +124,8 @@ struct CopiedValue : public Value { template struct ReaderFirstAndLastData { public: - using StoreType = std::conditional_t, - Value>; + using StoreType = + std::conditional_t, Value>; static constexpr bool nullable = arg_is_nullable; static constexpr bool result_nullable = result_is_nullable; @@ -126,17 +142,29 @@ struct ReaderFirstAndLastData { } else { auto& col = assert_cast(to); col.get_null_map_data().push_back(0); - _data_value.insert_into(col.get_nested_column()); + if constexpr (!std::is_same_v) { + _data_value.template insert_into(col.get_nested_column()); + } else { + _data_value.insert_into(col.get_nested_column()); + } } } else { - _data_value.insert_into(to); + if constexpr (!std::is_same_v) { + _data_value.template insert_into(to); + } else { + _data_value.insert_into(to); + } } } // here not check the columns[0] is null at the row, // but it is need to check in other void set_value(const IColumn** columns, size_t pos) { - _data_value.set_value(columns[0], pos); + if constexpr (is_copy) { + _data_value.template set_value(columns[0], pos); + } else { + _data_value.set_value(columns[0], pos); + } _has_value = true; } diff --git a/be/src/exprs/aggregate/aggregate_function_window.cpp b/be/src/exprs/aggregate/aggregate_function_window.cpp index 01e35bb999a3e1..41bbebdf0c8f50 100644 --- a/be/src/exprs/aggregate/aggregate_function_window.cpp +++ b/be/src/exprs/aggregate/aggregate_function_window.cpp @@ -49,11 +49,17 @@ AggregateFunctionPtr create_aggregate_function_window_first(const std::string& n const DataTypePtr& result_type, const bool result_is_nullable, const AggregateFunctionAttr& attr); +AggregateFunctionPtr create_aggregate_function_window_first_ignore_null( + const std::string& name, const DataTypes& argument_types, const DataTypePtr& result_type, + const bool result_is_nullable, const AggregateFunctionAttr& attr); AggregateFunctionPtr create_aggregate_function_window_last(const std::string& name, const DataTypes& argument_types, const DataTypePtr& result_type, const bool result_is_nullable, const AggregateFunctionAttr& attr); +AggregateFunctionPtr create_aggregate_function_window_last_ignore_null( + const std::string& name, const DataTypes& argument_types, const DataTypePtr& result_type, + const bool result_is_nullable, const AggregateFunctionAttr& attr); AggregateFunctionPtr create_aggregate_function_window_nth_value(const std::string& name, const DataTypes& argument_types, const DataTypePtr& result_type, @@ -89,8 +95,36 @@ void register_aggregate_function_window_lead_lag_first_last( AggregateFunctionSimpleFactory& factory) { factory.register_function_both("lead", create_aggregate_function_window_lead); factory.register_function_both("lag", create_aggregate_function_window_lag); - factory.register_function_both("first_value", create_aggregate_function_window_first); - factory.register_function_both("last_value", create_aggregate_function_window_last); + // FE rewrites first_value(k1, false) → first_value(k1), so argument_types.size() == 2 + // means arg_ignore_null = true. Dispatch at registration to avoid runtime branching + // that would double template instantiations. + factory.register_function_both( + "first_value", + [](const std::string& name, const DataTypes& argument_types, + const DataTypePtr& result_type, const bool result_is_nullable, + const AggregateFunctionAttr& attr) -> AggregateFunctionPtr { + if (argument_types.size() == 2) { + return create_aggregate_function_window_first_ignore_null( + name, argument_types, result_type, result_is_nullable, attr); + } + return create_aggregate_function_window_first(name, argument_types, result_type, + result_is_nullable, attr); + }); + factory.register_function_both( + "last_value", + [](const std::string& name, const DataTypes& argument_types, + const DataTypePtr& result_type, const bool result_is_nullable, + const AggregateFunctionAttr& attr) -> AggregateFunctionPtr { + if (argument_types.size() == 2) { + return create_aggregate_function_window_last_ignore_null( + name, argument_types, result_type, result_is_nullable, attr); + } + return create_aggregate_function_window_last(name, argument_types, result_type, + result_is_nullable, attr); + }); + // nth_value always has 2 args (column, N) from FE. + // WindowFunctionNthValueImpl does not implement ignore-null logic, + // so register directly without dispatch. factory.register_function_both("nth_value", create_aggregate_function_window_nth_value); } diff --git a/be/src/exprs/aggregate/aggregate_function_window.h b/be/src/exprs/aggregate/aggregate_function_window.h index fe7556c47b6738..9b654c77ff5eb1 100644 --- a/be/src/exprs/aggregate/aggregate_function_window.h +++ b/be/src/exprs/aggregate/aggregate_function_window.h @@ -397,15 +397,15 @@ class WindowFunctionNTile final void deserialize(AggregateDataPtr place, BufferReadable& buf, Arena&) const override {} }; -template +template struct FirstLastData - : public ReaderFirstAndLastData { + : public ReaderFirstAndLastData { public: void set_is_null() { this->_data_value.reset(); } }; -template -struct NthValueData : public FirstLastData { +template +struct NthValueData : public FirstLastData { public: void reset() { this->_data_value.reset(); @@ -418,8 +418,8 @@ struct NthValueData : public FirstLastData -struct BaseValue : public Value { +template +struct BaseValue : public Value { public: bool is_null() const { return this->_ptr == nullptr; } // because _ptr pointer to first_argument or third argument, so it's difficult to cast ptr @@ -427,7 +427,7 @@ struct BaseValue : public Value { StringRef get_value() const { return this->_ptr->get_data_at(this->_offset); } }; -template +template struct LeadLagData { public: static constexpr bool result_nullable = result_is_nullable; @@ -492,7 +492,7 @@ struct LeadLagData { int64_t get_offset_value() const { return _offset_value; } private: - BaseValue _data_value; + BaseValue _data_value; bool _is_inited = false; int64_t _offset_value = 0; }; diff --git a/be/src/exprs/aggregate/aggregate_function_window_first.cpp b/be/src/exprs/aggregate/aggregate_function_window_first.cpp index 63b7054d5d40d7..79051c09665dc8 100644 --- a/be/src/exprs/aggregate/aggregate_function_window_first.cpp +++ b/be/src/exprs/aggregate/aggregate_function_window_first.cpp @@ -20,8 +20,11 @@ namespace doris { #include "common/compile_check_begin.h" -CREATE_WINDOW_FUNCTION_WITH_NAME_AND_DATA(create_aggregate_function_window_first, FirstLastData, - WindowFunctionFirstImpl); +CREATE_WINDOW_FUNCTION_DIRECT(create_aggregate_function_window_first, FirstLastData, + WindowFunctionFirstImpl, false); + +CREATE_WINDOW_FUNCTION_DIRECT(create_aggregate_function_window_first_ignore_null, FirstLastData, + WindowFunctionFirstImpl, true); #include "common/compile_check_end.h" } // namespace doris diff --git a/be/src/exprs/aggregate/aggregate_function_window_impl.h b/be/src/exprs/aggregate/aggregate_function_window_impl.h index c3f6f0d4033156..09fbb199736779 100644 --- a/be/src/exprs/aggregate/aggregate_function_window_impl.h +++ b/be/src/exprs/aggregate/aggregate_function_window_impl.h @@ -29,328 +29,30 @@ namespace doris { #include "common/compile_check_begin.h" -template