This is an automated email from the ASF dual-hosted git repository. lihaopeng pushed a commit to branch vectorized in repository https://gitbox.apache.org/repos/asf/incubator-doris.git
commit 990723c15b346f0838314e53c8138e574176a5b9 Author: zhangstar333 <87313068+zhangstar...@users.noreply.github.com> AuthorDate: Thu Jan 13 20:27:34 2022 +0800 [Vectorized][Function] Support function stddev/variance/stddev_samp/variance_samp (#7734) --- be/src/exprs/aggregate_functions.cpp | 2 +- be/src/vec/CMakeLists.txt | 1 + .../vec/aggregate_functions/aggregate_function.h | 5 + .../aggregate_functions/aggregate_function_null.h | 9 +- .../aggregate_function_simple_factory.cpp | 6 +- .../aggregate_function_simple_factory.h | 20 +- .../aggregate_function_stddev.cpp | 101 ++++++++ .../aggregate_function_stddev.h | 285 +++++++++++++++++++++ .../java/org/apache/doris/catalog/FunctionSet.java | 68 +++++ 9 files changed, 486 insertions(+), 11 deletions(-) diff --git a/be/src/exprs/aggregate_functions.cpp b/be/src/exprs/aggregate_functions.cpp index a22c3ee..93166cf 100644 --- a/be/src/exprs/aggregate_functions.cpp +++ b/be/src/exprs/aggregate_functions.cpp @@ -1874,8 +1874,8 @@ static double compute_knuth_variance(const KnuthVarianceState& state, bool pop) static DecimalV2Value decimalv2_compute_knuth_variance(const DecimalV2KnuthVarianceState& state, bool pop) { DecimalV2Value new_count = DecimalV2Value(); - new_count.assign_from_double(state.count); if (state.count == 1) return new_count; + new_count.assign_from_double(state.count); DecimalV2Value new_m2 = DecimalV2Value::from_decimal_val(state.m2); if (pop) return new_m2 / new_count; diff --git a/be/src/vec/CMakeLists.txt b/be/src/vec/CMakeLists.txt index aa302ce..9c4d947 100644 --- a/be/src/vec/CMakeLists.txt +++ b/be/src/vec/CMakeLists.txt @@ -31,6 +31,7 @@ set(VEC_FILES aggregate_functions/aggregate_function_bitmap.cpp aggregate_functions/aggregate_function_reader.cpp aggregate_functions/aggregate_function_window.cpp + aggregate_functions/aggregate_function_stddev.cpp aggregate_functions/aggregate_function_simple_factory.cpp columns/collator.cpp columns/column.cpp diff --git a/be/src/vec/aggregate_functions/aggregate_function.h b/be/src/vec/aggregate_functions/aggregate_function.h index 412382f..4c2ef36 100644 --- a/be/src/vec/aggregate_functions/aggregate_function.h +++ b/be/src/vec/aggregate_functions/aggregate_function.h @@ -114,6 +114,11 @@ public: */ virtual bool is_state() const { return false; } + /// if return false, during insert_result_into function, you colud get nullable result column, + /// so could insert to null value by yourself, rather than by AggregateFunctionNullBase; + /// because you maybe be calculate a invalid value, but want to use null replace it; + virtual bool insert_to_null_default() const { return true; } + /** The inner loop that uses the function pointer is better than using the virtual function. * The reason is that in the case of virtual functions GCC 5.1.2 generates code, * which, at each iteration of the loop, reloads the function address (the offset value in the virtual function table) from memory to the register. diff --git a/be/src/vec/aggregate_functions/aggregate_function_null.h b/be/src/vec/aggregate_functions/aggregate_function_null.h index 4c61e2b..9458d7d 100644 --- a/be/src/vec/aggregate_functions/aggregate_function_null.h +++ b/be/src/vec/aggregate_functions/aggregate_function_null.h @@ -144,9 +144,12 @@ public: if constexpr (result_is_nullable) { ColumnNullable& to_concrete = assert_cast<ColumnNullable&>(to); if (get_flag(place)) { - nested_function->insert_result_into(nested_place(place), - to_concrete.get_nested_column()); - to_concrete.get_null_map_data().push_back(0); + if (nested_function->insert_to_null_default()) { + nested_function->insert_result_into(nested_place(place), to_concrete.get_nested_column()); + to_concrete.get_null_map_data().push_back(0); + } else { + nested_function->insert_result_into(nested_place(place), to); //want to insert into null value by self + } } else { to_concrete.insert_default(); } diff --git a/be/src/vec/aggregate_functions/aggregate_function_simple_factory.cpp b/be/src/vec/aggregate_functions/aggregate_function_simple_factory.cpp index 8a1995b..ba1b2ba 100644 --- a/be/src/vec/aggregate_functions/aggregate_function_simple_factory.cpp +++ b/be/src/vec/aggregate_functions/aggregate_function_simple_factory.cpp @@ -35,6 +35,7 @@ void register_aggregate_function_combinator_distinct(AggregateFunctionSimpleFact void register_aggregate_function_bitmap(AggregateFunctionSimpleFactory& factory); void register_aggregate_function_window_rank(AggregateFunctionSimpleFactory& factory); void register_aggregate_function_window_lead_lag(AggregateFunctionSimpleFactory& factory); +void register_aggregate_function_stddev_variance(AggregateFunctionSimpleFactory& factory); AggregateFunctionSimpleFactory& AggregateFunctionSimpleFactory::instance() { static std::once_flag oc; static AggregateFunctionSimpleFactory instance; @@ -49,10 +50,11 @@ AggregateFunctionSimpleFactory& AggregateFunctionSimpleFactory::instance() { register_aggregate_function_HLL_union_agg(instance); register_aggregate_function_reader(instance); // register aggregate function for agg reader register_aggregate_function_window_rank(instance); - + register_aggregate_function_stddev_variance(instance); + // if you only register function with no nullable, and wants to add nullable automatically, you should place function above this line register_aggregate_function_combinator_null(instance); - + register_aggregate_function_reader_no_spread(instance); register_aggregate_function_window_lead_lag(instance); }); diff --git a/be/src/vec/aggregate_functions/aggregate_function_simple_factory.h b/be/src/vec/aggregate_functions/aggregate_function_simple_factory.h index d12c27a..1bac4f1 100644 --- a/be/src/vec/aggregate_functions/aggregate_function_simple_factory.h +++ b/be/src/vec/aggregate_functions/aggregate_function_simple_factory.h @@ -45,7 +45,7 @@ private: AggregateFunctions aggregate_functions; AggregateFunctions nullable_aggregate_functions; - + std::unordered_map<std::string, std::string> function_alias; public: void register_nullable_function_combinator(const Creator& creator) { for (const auto& entity : aggregate_functions) { @@ -77,15 +77,21 @@ public: nullable = true; } } + + std::string name_str = name; + if (function_alias.count(name)) { + name_str = function_alias[name]; + } + if (nullable) { - return nullable_aggregate_functions.find(name) == nullable_aggregate_functions.end() + return nullable_aggregate_functions.find(name_str) == nullable_aggregate_functions.end() ? nullptr - : nullable_aggregate_functions[name](name, argument_types, parameters, + : nullable_aggregate_functions[name_str](name_str, argument_types, parameters, result_is_nullable); } else { - return aggregate_functions.find(name) == aggregate_functions.end() + return aggregate_functions.find(name_str) == aggregate_functions.end() ? nullptr - : aggregate_functions[name](name, argument_types, parameters, + : aggregate_functions[name_str](name_str, argument_types, parameters, result_is_nullable); } } @@ -98,6 +104,10 @@ public: } } + void register_alias(const std::string& name, const std::string& alias) { + function_alias[alias] = name; + } + public: static AggregateFunctionSimpleFactory& instance(); }; diff --git a/be/src/vec/aggregate_functions/aggregate_function_stddev.cpp b/be/src/vec/aggregate_functions/aggregate_function_stddev.cpp new file mode 100644 index 0000000..f1794d6 --- /dev/null +++ b/be/src/vec/aggregate_functions/aggregate_function_stddev.cpp @@ -0,0 +1,101 @@ +// 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. + +#include "vec/aggregate_functions/aggregate_function_stddev.h" + +#include "common/logging.h" +#include "vec/aggregate_functions/aggregate_function_simple_factory.h" +#include "vec/aggregate_functions/factory_helpers.h" +#include "vec/aggregate_functions/helpers.h" +namespace doris::vectorized { + +template <template <typename> class AggregateFunctionTemplate, template <typename> class NameData, + template <typename, typename> class Data, bool is_stddev> +static IAggregateFunction* create_function_single_value(const String& name, + const DataTypes& argument_types, + const Array& parameters) { + auto type = argument_types[0].get(); + if (type->is_nullable()) { + type = assert_cast<const DataTypeNullable*>(type)->get_nested_type().get(); + } + WhichDataType which(*type); + +#define DISPATCH(TYPE) \ + if (which.idx == TypeIndex::TYPE) \ + return new AggregateFunctionTemplate<NameData<Data<TYPE, BaseData<TYPE, is_stddev>>>>( \ + argument_types); + FOR_NUMERIC_TYPES(DISPATCH) +#undef DISPATCH + if (which.is_decimal()) { + return new AggregateFunctionTemplate< + NameData<Data<Decimal128, BaseDatadecimal<is_stddev>>>>(argument_types); + } + DCHECK(false) << "with unknowed type, failed in create_aggregate_function_stddev_variance"; + return nullptr; +} + +template <bool is_stddev> +AggregateFunctionPtr create_aggregate_function_variance_samp(const std::string& name, + const DataTypes& argument_types, + const Array& parameters, + const bool result_is_nullable) { + return AggregateFunctionPtr( + create_function_single_value<AggregateFunctionStddevSamp, VarianceSampData, SampData, + is_stddev>(name, argument_types, parameters)); +} + +template <bool is_stddev> +AggregateFunctionPtr create_aggregate_function_stddev_samp(const std::string& name, + const DataTypes& argument_types, + const Array& parameters, + const bool result_is_nullable) { + return AggregateFunctionPtr( + create_function_single_value<AggregateFunctionStddevSamp, StddevSampData, SampData, + is_stddev>(name, argument_types, parameters)); +} + +template <bool is_stddev> +AggregateFunctionPtr create_aggregate_function_variance_pop(const std::string& name, + const DataTypes& argument_types, + const Array& parameters, + const bool result_is_nullable) { + return AggregateFunctionPtr( + create_function_single_value<AggregateFunctionStddevSamp, VarianceData, PopData, + is_stddev>(name, argument_types, parameters)); +} + +template <bool is_stddev> +AggregateFunctionPtr create_aggregate_function_stddev_pop(const std::string& name, + const DataTypes& argument_types, + const Array& parameters, + const bool result_is_nullable) { + return AggregateFunctionPtr( + create_function_single_value<AggregateFunctionStddevSamp, StddevData, PopData, + is_stddev>(name, argument_types, parameters)); +} + +void register_aggregate_function_stddev_variance(AggregateFunctionSimpleFactory& factory) { + factory.register_function("variance_samp", create_aggregate_function_variance_samp<false>); + factory.register_function("variance", create_aggregate_function_variance_pop<false>); + factory.register_alias("variance_samp", "var_samp"); + factory.register_alias("variance", "var_pop"); + factory.register_alias("variance", "variance_pop"); + factory.register_function("stddev_samp", create_aggregate_function_stddev_samp<true>); + factory.register_function("stddev", create_aggregate_function_stddev_pop<true>); + factory.register_alias("stddev", "stddev_pop"); +} +} // namespace doris::vectorized \ No newline at end of file diff --git a/be/src/vec/aggregate_functions/aggregate_function_stddev.h b/be/src/vec/aggregate_functions/aggregate_function_stddev.h new file mode 100644 index 0000000..6cdba200 --- /dev/null +++ b/be/src/vec/aggregate_functions/aggregate_function_stddev.h @@ -0,0 +1,285 @@ +// 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 "common/status.h" +#include "vec/aggregate_functions/aggregate_function.h" +#include "vec/columns/columns_number.h" +#include "vec/data_types/data_type_decimal.h" +#include "vec/data_types/data_type_number.h" +#include "vec/io/io_helper.h" +namespace doris::vectorized { + +template <typename T, bool is_stddev> +struct BaseData { + BaseData() : mean(0.0), m2(0.0), count(0) {} + + void write(BufferWritable& buf) const { + write_binary(mean, buf); + write_binary(m2, buf); + write_binary(count, buf); + } + + void read(BufferReadable& buf) { + read_binary(mean, buf); + read_binary(m2, buf); + read_binary(count, buf); + } + + void reset() { + mean = 0.0; + m2 = 0.0; + count = 0; + } + + double get_result(double res) const { + if constexpr (is_stddev) { + return std::sqrt(res); + } else { + return res; + } + } + + double get_pop_result() const { + if (count == 1) { + return 0.0; + } + double res = m2 / count; + return get_result(res); + } + + double get_samp_result() const { + double res = m2 / (count - 1); + return get_result(res); + } + + static const DataTypePtr get_return_type() { + return make_nullable(std::make_shared<DataTypeNumber<Float64>>()); + } + + void merge(const BaseData& rhs) { + if (rhs.count == 0) { + return; + } + double delta = mean - rhs.mean; + double sum_count = count + rhs.count; + mean = rhs.mean + delta * count / sum_count; + m2 = rhs.m2 + m2 + (delta * delta) * rhs.count * count / sum_count; + count = sum_count; + } + + void add(const IColumn** columns, size_t row_num) { + const auto& sources = static_cast<const ColumnVector<T>&>(*columns[0]); + double source_data = sources.get_data()[row_num]; + + double delta = source_data - mean; + double r = delta / (1 + count); + mean += r; + m2 += count * delta * r; + count += 1; + } + + double mean; + double m2; + int64_t count; +}; + +template <bool is_stddev> +struct BaseDatadecimal { + BaseDatadecimal() : mean(0), m2(0), count(0) {} + + void write(BufferWritable& buf) const { + write_binary(mean, buf); + write_binary(m2, buf); + write_binary(count, buf); + } + + void read(BufferReadable& buf) { + read_binary(mean, buf); + read_binary(m2, buf); + read_binary(count, buf); + } + + void reset() { + mean = DecimalV2Value(); + m2 = DecimalV2Value(); + count = {}; + } + + DecimalV2Value get_result(DecimalV2Value res) const { + if constexpr (is_stddev) { + return DecimalV2Value::sqrt(res); + } else { + return res; + } + } + + DecimalV2Value get_pop_result() const { + DecimalV2Value new_count = DecimalV2Value(); + if (count == 1) { + return new_count; + } + DecimalV2Value res = m2 / new_count.assign_from_double(count); + return get_result(res); + } + + DecimalV2Value get_samp_result() const { + DecimalV2Value new_count = DecimalV2Value(); + DecimalV2Value res = m2 / new_count.assign_from_double(count - 1); + return get_result(res); + } + + static const DataTypePtr get_return_type() { + return make_nullable(std::make_shared<DataTypeDecimal<Decimal128>>(27, 9)); + } + + void merge(const BaseDatadecimal& rhs) { + if (rhs.count == 0) { + return; + } + DecimalV2Value new_count = DecimalV2Value(); + new_count.assign_from_double(count); + DecimalV2Value rhs_count = DecimalV2Value(); + rhs_count.assign_from_double(rhs.count); + + DecimalV2Value delta = mean - rhs.mean; + DecimalV2Value sum_count = new_count + rhs_count; + mean = rhs.mean + delta * (new_count / sum_count); + m2 = rhs.m2 + m2 + (delta * delta) * (rhs_count * new_count / sum_count); + count += rhs.count; + } + + void add(const IColumn** columns, size_t row_num) { + DecimalV2Value source_data = DecimalV2Value(); + const auto& sources = static_cast<const ColumnDecimal<Decimal128>&>(*columns[0]); + source_data = (DecimalV2Value)sources.get_data()[row_num]; + + DecimalV2Value new_count = DecimalV2Value(); + new_count.assign_from_double(count); + DecimalV2Value increase_count = DecimalV2Value(); + increase_count.assign_from_double(1 + count); + + DecimalV2Value delta = source_data - mean; + DecimalV2Value r = delta / increase_count; + mean += r; + m2 += new_count * delta * r; + count += 1; + } + + DecimalV2Value mean; + DecimalV2Value m2; + int64_t count; +}; + +template <typename T, typename Data> +struct PopData : Data { + using ColVecResult = std::conditional_t<IsDecimalNumber<T>, ColumnDecimal<Decimal128>, ColumnVector<Float64>>; + void insert_result_into(IColumn& to) const { + ColumnNullable& nullable_column = assert_cast<ColumnNullable&>(to); + auto& col = static_cast<ColVecResult&>(nullable_column.get_nested_column()); + if constexpr (IsDecimalNumber<T>) { + col.get_data().push_back(this->get_pop_result().value()); + } else { + col.get_data().push_back(this->get_pop_result()); + } + nullable_column.get_null_map_data().push_back(0); + } +}; + +template <typename T, typename Data> +struct SampData : Data { + using ColVecResult = std::conditional_t<IsDecimalNumber<T>, ColumnDecimal<Decimal128>, ColumnVector<Float64>>; + void insert_result_into(IColumn& to) const { + ColumnNullable& nullable_column = assert_cast<ColumnNullable&>(to); + if (this->count == 1) { + nullable_column.insert_default(); + } else { + auto& col = static_cast<ColVecResult&>(nullable_column.get_nested_column()); + if constexpr (IsDecimalNumber<T>) { + col.get_data().push_back(this->get_samp_result().value()); + } else { + col.get_data().push_back(this->get_samp_result()); + } + nullable_column.get_null_map_data().push_back(0); + } + } +}; + +template <typename Data> +struct StddevData : Data { + static const char* name() { return "stddev"; } +}; + +template <typename Data> +struct VarianceData : Data { + static const char* name() { return "variance"; } +}; + +template <typename Data> +struct VarianceSampData : Data { + static const char* name() { return "variance_samp"; } +}; + +template <typename Data> +struct StddevSampData : Data { + static const char* name() { return "stddev_samp"; } +}; + +template <typename Data> +class AggregateFunctionStddevSamp final + : public IAggregateFunctionDataHelper<Data, AggregateFunctionStddevSamp<Data>> { +public: + AggregateFunctionStddevSamp(const DataTypes& argument_types_) + : IAggregateFunctionDataHelper<Data, AggregateFunctionStddevSamp<Data>>(argument_types_, + {}) {} + + String get_name() const override { return Data::name(); } + + bool insert_to_null_default() const override { return false; } + + DataTypePtr get_return_type() const override { return Data::get_return_type(); } + + void add(AggregateDataPtr __restrict place, const IColumn** columns, size_t row_num, + Arena*) const override { + this->data(place).add(columns, row_num); + } + + void reset(AggregateDataPtr __restrict place) const override { this->data(place).reset(); } + + void merge(AggregateDataPtr __restrict place, ConstAggregateDataPtr rhs, + Arena*) const override { + this->data(place).merge(this->data(rhs)); + } + + void serialize(ConstAggregateDataPtr __restrict place, BufferWritable& buf) const override { + this->data(place).write(buf); + } + + void deserialize(AggregateDataPtr __restrict place, BufferReadable& buf, + Arena*) const override { + this->data(place).read(buf); + } + + void insert_result_into(ConstAggregateDataPtr __restrict place, IColumn& to) const override { + this->data(place).insert_result_into(to); + } + + const char* get_header_file_path() const override { return __FILE__; } +}; + +} // namespace doris::vectorized diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/FunctionSet.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/FunctionSet.java index 7c0cde1..0c8079c 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/catalog/FunctionSet.java +++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/FunctionSet.java @@ -1674,6 +1674,74 @@ public class FunctionSet<min_initIN9doris_udf12DecimalV2ValEEEvPNS2_15FunctionCo null, prefix + STDDEV_POP_FINALIZE_SYMBOL.get(t), false, false, false)); + //vec stddev stddev_samp stddev_pop + addBuiltin(AggregateFunction.createBuiltin("stddev", + Lists.newArrayList(t), STDDEV_RETTYPE_SYMBOL.get(t), t, + prefix + STDDEV_INIT_SYMBOL.get(t), + prefix + STDDEV_UPDATE_SYMBOL.get(t), + prefix + STDDEV_MERGE_SYMBOL.get(t), + null, + prefix + STDDEV_POP_FINALIZE_SYMBOL.get(t), + false, false, false, true)); + addBuiltin(AggregateFunction.createBuiltin("stddev_samp", + Lists.newArrayList(t), STDDEV_RETTYPE_SYMBOL.get(t), t, + prefix + STDDEV_INIT_SYMBOL.get(t), + prefix + STDDEV_UPDATE_SYMBOL.get(t), + prefix + STDDEV_MERGE_SYMBOL.get(t), + null, + prefix + STDDEV_FINALIZE_SYMBOL.get(t), + false, false, false, true)); + addBuiltin(AggregateFunction.createBuiltin("stddev_pop", + Lists.newArrayList(t), STDDEV_RETTYPE_SYMBOL.get(t), t, + prefix + STDDEV_INIT_SYMBOL.get(t), + prefix + STDDEV_UPDATE_SYMBOL.get(t), + prefix + STDDEV_MERGE_SYMBOL.get(t), + null, + prefix + STDDEV_POP_FINALIZE_SYMBOL.get(t), + false, false, false, true)); + + //vec: variance variance_samp var_samp variance_pop var_pop + addBuiltin(AggregateFunction.createBuiltin("variance", + Lists.newArrayList(t), STDDEV_RETTYPE_SYMBOL.get(t), t, + prefix + STDDEV_INIT_SYMBOL.get(t), + prefix + STDDEV_UPDATE_SYMBOL.get(t), + prefix + STDDEV_MERGE_SYMBOL.get(t), + null, + prefix + VAR_POP_FINALIZE_SYMBOL.get(t), + false, false, false, true)); + addBuiltin(AggregateFunction.createBuiltin("variance_pop", + Lists.newArrayList(t), STDDEV_RETTYPE_SYMBOL.get(t), t, + prefix + STDDEV_INIT_SYMBOL.get(t), + prefix + STDDEV_UPDATE_SYMBOL.get(t), + prefix + STDDEV_MERGE_SYMBOL.get(t), + null, + prefix + VAR_POP_FINALIZE_SYMBOL.get(t), + false, false, false, true)); + addBuiltin(AggregateFunction.createBuiltin("var_pop", + Lists.newArrayList(t), STDDEV_RETTYPE_SYMBOL.get(t), t, + prefix + STDDEV_INIT_SYMBOL.get(t), + prefix + STDDEV_UPDATE_SYMBOL.get(t), + prefix + STDDEV_MERGE_SYMBOL.get(t), + null, + prefix + VAR_POP_FINALIZE_SYMBOL.get(t), + false, false, false, true)); + addBuiltin(AggregateFunction.createBuiltin("variance_samp", + Lists.newArrayList(t), STDDEV_RETTYPE_SYMBOL.get(t), t, + prefix + STDDEV_INIT_SYMBOL.get(t), + prefix + STDDEV_UPDATE_SYMBOL.get(t), + prefix + STDDEV_MERGE_SYMBOL.get(t), + null, + prefix + VAR_FINALIZE_SYMBOL.get(t), + false, false, false, true)); + addBuiltin(AggregateFunction.createBuiltin("var_samp", + Lists.newArrayList(t), STDDEV_RETTYPE_SYMBOL.get(t), t, + prefix + STDDEV_INIT_SYMBOL.get(t), + prefix + STDDEV_UPDATE_SYMBOL.get(t), + prefix + STDDEV_MERGE_SYMBOL.get(t), + null, + prefix + VAR_FINALIZE_SYMBOL.get(t), + false, false, false, true)); + addBuiltin(AggregateFunction.createBuiltin("variance", Lists.newArrayList(t), STDDEV_RETTYPE_SYMBOL.get(t), Type.VARCHAR, prefix + STDDEV_INIT_SYMBOL.get(t), --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For additional commands, e-mail: commits-h...@doris.apache.org