This is an automated email from the ASF dual-hosted git repository.
yiguolei pushed a commit to branch branch-4.1
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-4.1 by this push:
new eac094afa0f branch-4.1: [Opt](func) Improve AggFunc Percentile
performance (#62520) (#65677)
eac094afa0f is described below
commit eac094afa0f7427e26ee4b61ab93e995bc59d85d
Author: linrrarity <[email protected]>
AuthorDate: Thu Jul 16 11:12:44 2026 +0800
branch-4.1: [Opt](func) Improve AggFunc Percentile performance (#62520)
(#65677)
pick: https://github.com/apache/doris/pull/62520
---
be/src/exprs/aggregate/aggregate_function.h | 1 +
.../aggregate/aggregate_function_percentile.cpp | 8 +-
.../aggregate/aggregate_function_percentile.h | 389 ++++++++++++++++++++-
.../aggregate/aggregate_function_simple_factory.h | 9 +
be/src/exprs/vectorized_agg_fn.cpp | 6 +
be/src/util/{counts.h => percentile_util.h} | 74 ++++
be/test/exprs/aggregate/agg_test.cpp | 49 +++
be/test/util/counts_test.cpp | 82 -----
be/test/util/percentile_util_test.cpp | 290 +++++++++++++++
.../nereids/load/NereidsStreamLoadPlanner.java | 1 +
.../expression/rules/FoldConstantRuleOnBE.java | 1 +
.../org/apache/doris/qe/CoordinatorContext.java | 1 +
.../org/apache/doris/qe/NereidsCoordinator.java | 1 +
.../java/org/apache/doris/qe/SessionVariable.java | 1 +
14 files changed, 819 insertions(+), 94 deletions(-)
diff --git a/be/src/exprs/aggregate/aggregate_function.h
b/be/src/exprs/aggregate/aggregate_function.h
index 94eaaa9ad72..ae44e5b1187 100644
--- a/be/src/exprs/aggregate/aggregate_function.h
+++ b/be/src/exprs/aggregate/aggregate_function.h
@@ -47,6 +47,7 @@ struct AggregateFunctionAttr {
bool is_window_function {false};
bool is_foreach {false};
bool enable_aggregate_function_null_v2 {false};
+ bool new_version_percentile {false};
std::vector<std::string> column_names;
};
diff --git a/be/src/exprs/aggregate/aggregate_function_percentile.cpp
b/be/src/exprs/aggregate/aggregate_function_percentile.cpp
index 61d8e78d7b8..86457929c29 100644
--- a/be/src/exprs/aggregate/aggregate_function_percentile.cpp
+++ b/be/src/exprs/aggregate/aggregate_function_percentile.cpp
@@ -64,9 +64,13 @@ void
register_aggregate_function_percentile(AggregateFunctionSimpleFactory& fact
using creator = creator_with_type_list<TYPE_TINYINT, TYPE_SMALLINT,
TYPE_INT, TYPE_BIGINT,
TYPE_LARGEINT, TYPE_FLOAT,
TYPE_DOUBLE>;
factory.register_function_both("percentile",
creator::creator<AggregateFunctionPercentile>);
- factory.register_alias("percentile", "percentile_cont");
factory.register_function_both("percentile_array",
creator::creator<AggregateFunctionPercentileArray>);
+ factory.register_function_both("percentile_v2",
+
creator::creator<AggregateFunctionPercentileV2>);
+ factory.register_function_both("percentile_array_v2",
+
creator::creator<AggregateFunctionPercentileArrayV2>);
+ factory.register_alias("percentile", "percentile_cont");
}
void register_percentile_approx_old_function(AggregateFunctionSimpleFactory&
factory) {
@@ -89,4 +93,4 @@ void
register_aggregate_function_percentile_approx(AggregateFunctionSimpleFactor
}
#include "common/compile_check_end.h"
-} // namespace doris
\ No newline at end of file
+} // namespace doris
diff --git a/be/src/exprs/aggregate/aggregate_function_percentile.h
b/be/src/exprs/aggregate/aggregate_function_percentile.h
index e36e29a478f..2f88f3a2550 100644
--- a/be/src/exprs/aggregate/aggregate_function_percentile.h
+++ b/be/src/exprs/aggregate/aggregate_function_percentile.h
@@ -36,10 +36,11 @@
#include "core/data_type/data_type_array.h"
#include "core/data_type/data_type_nullable.h"
#include "core/data_type/data_type_number.h"
+#include "core/pod_array.h"
#include "core/pod_array_fwd.h"
#include "core/types.h"
#include "exprs/aggregate/aggregate_function.h"
-#include "util/counts.h"
+#include "util/percentile_util.h"
#include "util/tdigest.h"
namespace doris {
@@ -48,14 +49,6 @@ namespace doris {
class Arena;
class BufferReadable;
-inline void check_quantile(double quantile) {
- if (quantile < 0 || quantile > 1) {
- throw Exception(ErrorCode::INVALID_ARGUMENT,
- "quantile in func percentile should in [0, 1], but
real data is:" +
- std::to_string(quantile));
- }
-}
-
struct PercentileApproxState {
static constexpr double INIT_QUANTILE = -1.0;
PercentileApproxState() = default;
@@ -437,6 +430,193 @@ struct PercentileState {
}
};
+template <PrimitiveType T>
+struct PercentileExactState {
+ using ValueType = typename PrimitiveTypeTraits<T>::CppType;
+ static constexpr size_t bytes_in_arena = 64 - sizeof(PODArray<ValueType>);
+ using Array = PODArrayWithStackMemory<ValueType, bytes_in_arena>;
+
+ void add_single_range(const ValueType* data, size_t count, double
quantile) {
+ if (!inited_flag) {
+ _set_single_level(quantile);
+ inited_flag = true;
+ }
+ _append(data, count);
+ }
+
+ void add_many_range(const ValueType* data, size_t count,
+ const PaddedPODArray<Float64>& quantiles_data, const
NullMap& null_maps,
+ size_t start, int64_t arg_size) {
+ if (!inited_flag) {
+ _set_many_levels(quantiles_data, null_maps, start, arg_size);
+ inited_flag = true;
+ }
+ if (levels.empty()) {
+ return;
+ }
+ _append(data, count);
+ }
+
+ void write(BufferWritable& buf) const {
+ buf.write_binary(inited_flag);
+ if (!inited_flag) {
+ return;
+ }
+
+ levels.write(buf);
+ size_t size = values.size();
+ buf.write_binary(size);
+ if (size > 0) {
+ buf.write(reinterpret_cast<const char*>(values.data()),
sizeof(ValueType) * size);
+ }
+ }
+
+ void read(BufferReadable& buf) {
+ reset();
+ buf.read_binary(inited_flag);
+ if (!inited_flag) {
+ return;
+ }
+
+ levels.read(buf);
+ size_t size = 0;
+ buf.read_binary(size);
+ values.resize(size);
+ if (size > 0) {
+ auto raw = buf.read(sizeof(ValueType) * size);
+ memcpy(values.data(), raw.data, raw.size);
+ }
+ }
+
+ void merge(const PercentileExactState& rhs) {
+ if (!rhs.inited_flag) {
+ return;
+ }
+
+ if (!inited_flag) {
+ levels = rhs.levels;
+ inited_flag = true;
+ } else {
+ levels.merge(rhs.levels);
+ }
+ _append(rhs.values.data(), rhs.values.size());
+ }
+
+ void reset() {
+ values.clear();
+ levels.clear();
+ inited_flag = false;
+ }
+
+ double get() const {
+ if (!inited_flag || levels.empty() || values.empty()) {
+ return 0.0;
+ }
+
+ DCHECK_EQ(levels.quantiles.size(), 1);
+ return _get_result(levels.quantiles[0]);
+ }
+
+ void insert_result_into(IColumn& to) const {
+ auto& column_data = assert_cast<ColumnFloat64&>(to).get_data();
+ if (!inited_flag || levels.empty() || values.empty()) {
+ return;
+ }
+
+ size_t old_size = column_data.size();
+ size_t size = levels.quantiles.size();
+ column_data.resize(old_size + size);
+ auto* result = column_data.data() + old_size;
+
+ if (values.size() == 1) {
+ for (size_t i = 0; i < size; ++i) {
+ result[i] = static_cast<double>(values.front());
+ }
+ return;
+ }
+
+ size_t prev_index = 0;
+ const auto& quantiles = levels.quantiles;
+ const auto& permutation = levels.get_permutation();
+ for (size_t i = 0; i < size; ++i) {
+ auto level_index = permutation[i];
+ auto level = quantiles[level_index];
+ double u = static_cast<double>(values.size() - 1) * level;
+ auto index = static_cast<size_t>(u);
+
+ if (index + 1 >= values.size()) {
+ result[level_index] =
+ static_cast<double>(*std::max_element(values.begin(),
values.end()));
+ } else {
+ std::nth_element(values.begin() + prev_index, values.begin() +
index, values.end());
+ auto* nth_elem = std::min_element(values.begin() + index + 1,
values.end());
+ result[level_index] =
+ static_cast<double>(values[index]) +
+ (u - static_cast<double>(index)) *
(static_cast<double>(*nth_elem) -
+
static_cast<double>(values[index]));
+ prev_index = index;
+ }
+ }
+ }
+
+private:
+ void _set_single_level(double quantile) {
+ DCHECK(levels.empty());
+ check_quantile(quantile);
+ levels.quantiles.push_back(quantile);
+ levels.permutation.push_back(0);
+ }
+
+ void _set_many_levels(const PaddedPODArray<Float64>& quantiles_data, const
NullMap& null_maps,
+ size_t start, int64_t arg_size) {
+ DCHECK(levels.empty());
+ size_t size = cast_set<size_t>(arg_size);
+ levels.quantiles.resize(size);
+ levels.permutation.resize(size);
+ for (size_t i = 0; i < size; ++i) {
+ if (null_maps[start + i]) {
+ throw Exception(ErrorCode::INVALID_ARGUMENT,
+ "quantiles in func percentile_array should not
have null");
+ }
+ check_quantile(quantiles_data[start + i]);
+ levels.quantiles[i] = quantiles_data[start + i];
+ levels.permutation[i] = i;
+ }
+ }
+
+ void _append(const ValueType* data, size_t count) {
+ if (count == 0) {
+ return;
+ }
+ values.reserve(values.size() + count);
+ values.insert_assume_reserved(data, data + count);
+ }
+
+ double _get_result(double quantile) const {
+ if (values.size() == 1) {
+ return static_cast<double>(values.front());
+ }
+
+ double u = static_cast<double>(values.size() - 1) * quantile;
+ auto index = static_cast<size_t>(u);
+
+ if (index + 1 >= values.size()) {
+ return static_cast<double>(*std::max_element(values.begin(),
values.end()));
+ }
+
+ std::nth_element(values.begin(), values.begin() + index, values.end());
+ auto* nth_elem = std::min_element(values.begin() + index + 1,
values.end());
+
+ return static_cast<double>(values[index]) +
+ (u - static_cast<double>(index)) *
+ (static_cast<double>(*nth_elem) -
static_cast<double>(values[index]));
+ }
+
+ mutable Array values;
+ mutable PercentileLevels levels;
+ bool inited_flag = false;
+};
+
template <PrimitiveType T>
class AggregateFunctionPercentile final
: public IAggregateFunctionDataHelper<PercentileState<T>,
AggregateFunctionPercentile<T>>,
@@ -569,5 +749,194 @@ public:
}
};
+template <PrimitiveType T>
+class AggregateFunctionPercentileV2 final
+ : public IAggregateFunctionDataHelper<PercentileExactState<T>,
+
AggregateFunctionPercentileV2<T>>,
+ MultiExpression,
+ NullableAggregateFunction {
+public:
+ using ColVecType = typename PrimitiveTypeTraits<T>::ColumnType;
+ using Base =
+ IAggregateFunctionDataHelper<PercentileExactState<T>,
AggregateFunctionPercentileV2<T>>;
+ AggregateFunctionPercentileV2(const DataTypes& argument_types_) :
Base(argument_types_) {}
+
+ String get_name() const override { return "percentile_v2"; }
+
+ DataTypePtr get_return_type() const override { return
std::make_shared<DataTypeFloat64>(); }
+
+ void add(AggregateDataPtr __restrict place, const IColumn** columns,
ssize_t row_num,
+ Arena&) const override {
+ const auto& sources =
+ assert_cast<const ColVecType&,
TypeCheckOnRelease::DISABLE>(*columns[0]);
+ const auto& quantile =
+ assert_cast<const ColumnFloat64&,
TypeCheckOnRelease::DISABLE>(*columns[1]);
+
AggregateFunctionPercentileV2::data(place).add_single_range(&sources.get_data()[row_num],
1,
+
quantile.get_data()[0]);
+ }
+
+ void add_batch_single_place(size_t batch_size, AggregateDataPtr place,
const IColumn** columns,
+ Arena&) const override {
+ const auto& sources =
+ assert_cast<const ColVecType&,
TypeCheckOnRelease::DISABLE>(*columns[0]);
+ const auto& quantile =
+ assert_cast<const ColumnFloat64&,
TypeCheckOnRelease::DISABLE>(*columns[1]);
+ DCHECK_EQ(sources.get_data().size(), batch_size);
+ AggregateFunctionPercentileV2::data(place).add_single_range(
+ sources.get_data().data(), batch_size, quantile.get_data()[0]);
+ }
+
+ void add_batch_range(size_t batch_begin, size_t batch_end,
AggregateDataPtr place,
+ const IColumn** columns, Arena&, bool has_null)
override {
+ const auto& sources =
+ assert_cast<const ColVecType&,
TypeCheckOnRelease::DISABLE>(*columns[0]);
+ const auto& quantile =
+ assert_cast<const ColumnFloat64&,
TypeCheckOnRelease::DISABLE>(*columns[1]);
+ DCHECK(!has_null);
+ AggregateFunctionPercentileV2::data(place).add_single_range(
+ sources.get_data().data() + batch_begin, batch_end -
batch_begin + 1,
+ quantile.get_data()[0]);
+ }
+
+ void reset(AggregateDataPtr __restrict place) const override {
+ AggregateFunctionPercentileV2::data(place).reset();
+ }
+
+ void merge(AggregateDataPtr __restrict place, ConstAggregateDataPtr rhs,
+ Arena&) const override {
+
AggregateFunctionPercentileV2::data(place).merge(AggregateFunctionPercentileV2::data(rhs));
+ }
+
+ void serialize(ConstAggregateDataPtr __restrict place, BufferWritable&
buf) const override {
+ AggregateFunctionPercentileV2::data(place).write(buf);
+ }
+
+ void deserialize(AggregateDataPtr __restrict place, BufferReadable& buf,
+ Arena&) const override {
+ AggregateFunctionPercentileV2::data(place).read(buf);
+ }
+
+ void insert_result_into(ConstAggregateDataPtr __restrict place, IColumn&
to) const override {
+ auto& col = assert_cast<ColumnFloat64&>(to);
+ col.insert_value(AggregateFunctionPercentileV2::data(place).get());
+ }
+};
+
+template <PrimitiveType T>
+class AggregateFunctionPercentileArrayV2 final
+ : public IAggregateFunctionDataHelper<PercentileExactState<T>,
+
AggregateFunctionPercentileArrayV2<T>>,
+ MultiExpression,
+ NotNullableAggregateFunction {
+public:
+ using ColVecType = typename PrimitiveTypeTraits<T>::ColumnType;
+ using Base = IAggregateFunctionDataHelper<PercentileExactState<T>,
+
AggregateFunctionPercentileArrayV2<T>>;
+ AggregateFunctionPercentileArrayV2(const DataTypes& argument_types_) :
Base(argument_types_) {}
+
+ String get_name() const override { return "percentile_array_v2"; }
+
+ DataTypePtr get_return_type() const override {
+ return
std::make_shared<DataTypeArray>(make_nullable(std::make_shared<DataTypeFloat64>()));
+ }
+
+ void add(AggregateDataPtr __restrict place, const IColumn** columns,
ssize_t row_num,
+ Arena&) const override {
+ const auto& sources =
+ assert_cast<const ColVecType&,
TypeCheckOnRelease::DISABLE>(*columns[0]);
+ const auto& quantile_array =
+ assert_cast<const ColumnArray&,
TypeCheckOnRelease::DISABLE>(*columns[1]);
+ const auto& offset_column_data = quantile_array.get_offsets();
+ const auto& null_maps = assert_cast<const ColumnNullable&,
TypeCheckOnRelease::DISABLE>(
+ quantile_array.get_data())
+ .get_null_map_data();
+ const auto& nested_column = assert_cast<const ColumnNullable&,
TypeCheckOnRelease::DISABLE>(
+ quantile_array.get_data())
+ .get_nested_column();
+ const auto& nested_column_data =
+ assert_cast<const ColumnFloat64&,
TypeCheckOnRelease::DISABLE>(nested_column);
+ size_t start = row_num == 0 ? 0 : offset_column_data[row_num - 1];
+ AggregateFunctionPercentileArrayV2::data(place).add_many_range(
+ &sources.get_data()[row_num], 1,
nested_column_data.get_data(), null_maps, start,
+ cast_set<int64_t>(offset_column_data[row_num] - start));
+ }
+
+ void add_batch_single_place(size_t batch_size, AggregateDataPtr place,
const IColumn** columns,
+ Arena&) const override {
+ const auto& sources =
+ assert_cast<const ColVecType&,
TypeCheckOnRelease::DISABLE>(*columns[0]);
+ const auto& quantile_array =
+ assert_cast<const ColumnArray&,
TypeCheckOnRelease::DISABLE>(*columns[1]);
+ const auto& offset_column_data = quantile_array.get_offsets();
+ const auto& null_maps = assert_cast<const ColumnNullable&,
TypeCheckOnRelease::DISABLE>(
+ quantile_array.get_data())
+ .get_null_map_data();
+ const auto& nested_column = assert_cast<const ColumnNullable&,
TypeCheckOnRelease::DISABLE>(
+ quantile_array.get_data())
+ .get_nested_column();
+ const auto& nested_column_data =
+ assert_cast<const ColumnFloat64&,
TypeCheckOnRelease::DISABLE>(nested_column);
+ DCHECK_EQ(sources.get_data().size(), batch_size);
+ AggregateFunctionPercentileArrayV2::data(place).add_many_range(
+ sources.get_data().data(), batch_size,
nested_column_data.get_data(), null_maps, 0,
+ cast_set<int64_t>(offset_column_data[0]));
+ }
+
+ void add_batch_range(size_t batch_begin, size_t batch_end,
AggregateDataPtr place,
+ const IColumn** columns, Arena&, bool has_null)
override {
+ const auto& sources =
+ assert_cast<const ColVecType&,
TypeCheckOnRelease::DISABLE>(*columns[0]);
+ const auto& quantile_array =
+ assert_cast<const ColumnArray&,
TypeCheckOnRelease::DISABLE>(*columns[1]);
+ const auto& offset_column_data = quantile_array.get_offsets();
+ const auto& null_maps = assert_cast<const ColumnNullable&,
TypeCheckOnRelease::DISABLE>(
+ quantile_array.get_data())
+ .get_null_map_data();
+ const auto& nested_column = assert_cast<const ColumnNullable&,
TypeCheckOnRelease::DISABLE>(
+ quantile_array.get_data())
+ .get_nested_column();
+ const auto& nested_column_data =
+ assert_cast<const ColumnFloat64&,
TypeCheckOnRelease::DISABLE>(nested_column);
+ DCHECK(!has_null);
+ size_t start = batch_begin == 0 ? 0 : offset_column_data[batch_begin -
1];
+ AggregateFunctionPercentileArrayV2::data(place).add_many_range(
+ sources.get_data().data() + batch_begin, batch_end -
batch_begin + 1,
+ nested_column_data.get_data(), null_maps, start,
+ cast_set<int64_t>(offset_column_data[batch_begin] - start));
+ }
+
+ void reset(AggregateDataPtr __restrict place) const override {
+ AggregateFunctionPercentileArrayV2::data(place).reset();
+ }
+
+ void merge(AggregateDataPtr __restrict place, ConstAggregateDataPtr rhs,
+ Arena&) const override {
+ AggregateFunctionPercentileArrayV2::data(place).merge(
+ AggregateFunctionPercentileArrayV2::data(rhs));
+ }
+
+ void serialize(ConstAggregateDataPtr __restrict place, BufferWritable&
buf) const override {
+ AggregateFunctionPercentileArrayV2::data(place).write(buf);
+ }
+
+ void deserialize(AggregateDataPtr __restrict place, BufferReadable& buf,
+ Arena&) const override {
+ AggregateFunctionPercentileArrayV2::data(place).read(buf);
+ }
+
+ void insert_result_into(ConstAggregateDataPtr __restrict place, IColumn&
to) const override {
+ auto& to_arr = assert_cast<ColumnArray&>(to);
+ auto& to_nested_col = to_arr.get_data();
+ if (to_nested_col.is_nullable()) {
+ auto* col_null = reinterpret_cast<ColumnNullable*>(&to_nested_col);
+ AggregateFunctionPercentileArrayV2::data(place).insert_result_into(
+ col_null->get_nested_column());
+
col_null->get_null_map_data().resize_fill(col_null->get_nested_column().size(),
0);
+ } else {
+
AggregateFunctionPercentileArrayV2::data(place).insert_result_into(to_nested_col);
+ }
+ to_arr.get_offsets().push_back(to_nested_col.size());
+ }
+};
#include "common/compile_check_end.h"
-} // namespace doris
\ No newline at end of file
+} // namespace doris
diff --git a/be/src/exprs/aggregate/aggregate_function_simple_factory.h
b/be/src/exprs/aggregate/aggregate_function_simple_factory.h
index 16f331aa6db..48e9edba5dd 100644
--- a/be/src/exprs/aggregate/aggregate_function_simple_factory.h
+++ b/be/src/exprs/aggregate/aggregate_function_simple_factory.h
@@ -136,6 +136,15 @@ public:
if (function_alias.contains(name)) {
name_str = function_alias[name];
}
+
+ if (attr.new_version_percentile) {
+ if (name_str == "percentile" || name_str == "percentile_cont") {
+ name_str = "percentile_v2";
+ } else if (name_str == "percentile_array") {
+ name_str = "percentile_array_v2";
+ }
+ }
+
if (nullable) {
return nullable_aggregate_functions.find(name_str) ==
nullable_aggregate_functions.end()
? nullptr
diff --git a/be/src/exprs/vectorized_agg_fn.cpp
b/be/src/exprs/vectorized_agg_fn.cpp
index 4eac3830c71..a093693dbc7 100644
--- a/be/src/exprs/vectorized_agg_fn.cpp
+++ b/be/src/exprs/vectorized_agg_fn.cpp
@@ -230,6 +230,9 @@ Status AggFnEvaluator::prepare(RuntimeState* state, const
RowDescriptor& desc,
.is_foreach = is_foreach,
.enable_aggregate_function_null_v2 =
state->enable_aggregate_function_null_v2(),
+ .new_version_percentile =
+
state->query_options().__isset.new_version_percentile &&
+ state->query_options().new_version_percentile,
.column_names = std::move(column_names)});
} else {
_function = AggregateFunctionSimpleFactory::instance().get(
@@ -239,6 +242,9 @@ Status AggFnEvaluator::prepare(RuntimeState* state, const
RowDescriptor& desc,
.is_foreach = is_foreach,
.enable_aggregate_function_null_v2 =
state->enable_aggregate_function_null_v2(),
+ .new_version_percentile =
+
state->query_options().__isset.new_version_percentile &&
+ state->query_options().new_version_percentile,
.column_names = std::move(column_names)});
}
}
diff --git a/be/src/util/counts.h b/be/src/util/percentile_util.h
similarity index 79%
rename from be/src/util/counts.h
rename to be/src/util/percentile_util.h
index a0299f6318b..05003e5cb63 100644
--- a/be/src/util/counts.h
+++ b/be/src/util/percentile_util.h
@@ -21,13 +21,27 @@
#include <algorithm>
#include <cmath>
+#include <cstdint>
#include <queue>
+#include <string>
+#include <vector>
+#include "common/cast_set.h"
+#include "common/exception.h"
+#include "core/column/column_nullable.h"
#include "core/pod_array.h"
#include "core/string_buffer.hpp"
namespace doris {
+inline void check_quantile(double quantile) {
+ if (quantile < 0 || quantile > 1) {
+ throw Exception(ErrorCode::INVALID_ARGUMENT,
+ "quantile in func percentile should in [0, 1], but
real data is:" +
+ std::to_string(quantile));
+ }
+}
+
template <typename Ty>
class Counts {
public:
@@ -223,4 +237,64 @@ private:
std::vector<PODArray<Ty>> _sorted_nums_vec;
};
+class PercentileLevels {
+public:
+ void merge(const PercentileLevels& rhs) {
+ if (rhs.empty()) {
+ return;
+ }
+
+ if (empty()) {
+ quantiles = rhs.quantiles;
+ permutation = rhs.permutation;
+ return;
+ }
+
+ DCHECK_EQ(quantiles.size(), rhs.quantiles.size());
+ for (size_t i = 0; i < quantiles.size(); ++i) {
+ DCHECK_EQ(quantiles[i], rhs.quantiles[i]);
+ }
+ }
+
+ void write(BufferWritable& buf) const {
+ int size_num = cast_set<int>(quantiles.size());
+ buf.write_binary(size_num);
+ for (const auto& quantile : quantiles) {
+ buf.write_binary(quantile);
+ }
+ }
+
+ void read(BufferReadable& buf) {
+ int size_num = 0;
+ buf.read_binary(size_num);
+
+ quantiles.resize(size_num);
+ permutation.resize(size_num);
+ for (int i = 0; i < size_num; ++i) {
+ buf.read_binary(quantiles[i]);
+ permutation[i] = cast_set<size_t>(i);
+ }
+ }
+
+ void clear() {
+ quantiles.clear();
+ permutation.clear();
+ }
+
+ bool empty() const { return quantiles.empty(); }
+
+ const std::vector<size_t>& get_permutation() const {
+ sort_permutation();
+ return permutation;
+ }
+
+ void sort_permutation() const {
+ pdqsort(permutation.begin(), permutation.end(),
+ [this](size_t lhs, size_t rhs) { return quantiles[lhs] <
quantiles[rhs]; });
+ }
+
+ std::vector<double> quantiles;
+ mutable std::vector<size_t> permutation;
+};
+
} // namespace doris
diff --git a/be/test/exprs/aggregate/agg_test.cpp
b/be/test/exprs/aggregate/agg_test.cpp
index 44116c752a3..459c24cdb6a 100644
--- a/be/test/exprs/aggregate/agg_test.cpp
+++ b/be/test/exprs/aggregate/agg_test.cpp
@@ -28,6 +28,7 @@
#include "core/column/column_nullable.h"
#include "core/column/column_string.h"
#include "core/column/column_vector.h"
+#include "core/data_type/data_type_array.h"
#include "core/data_type/data_type_date.h"
#include "core/data_type/data_type_date_time.h"
#include "core/data_type/data_type_nullable.h"
@@ -49,6 +50,7 @@ void
register_aggregate_function_sum(AggregateFunctionSimpleFactory& factory);
void register_aggregate_function_topn(AggregateFunctionSimpleFactory& factory);
void register_aggregate_function_bit(AggregateFunctionSimpleFactory& factory);
void register_aggregate_function_minmax(AggregateFunctionSimpleFactory&
factory);
+void register_aggregate_function_percentile(AggregateFunctionSimpleFactory&
factory);
void
register_aggregate_function_replace_reader_load(AggregateFunctionSimpleFactory&
factory);
TEST(AggTest, basic_test) {
@@ -136,6 +138,53 @@ TEST(AggTest, window_function_test) {
EXPECT_EQ(size2, 1);
}
+TEST(AggTest, percentile_query_option_routes_default_names_to_v2) {
+ AggregateFunctionSimpleFactory factory;
+ register_aggregate_function_percentile(factory);
+ int be_version = BeExecVersionManager::get_newest_version();
+
+ DataTypes percentile_types = {std::make_shared<DataTypeInt64>(),
+ std::make_shared<DataTypeFloat64>()};
+ auto percentile_result_type = std::make_shared<DataTypeFloat64>();
+ auto percentile_v1 =
+ factory.get("percentile", percentile_types,
percentile_result_type, false, be_version);
+ ASSERT_NE(percentile_v1, nullptr);
+ EXPECT_EQ(percentile_v1->get_name(), "percentile");
+
+ auto percentile_v2 =
+ factory.get("percentile", percentile_types,
percentile_result_type, false, be_version,
+ {.new_version_percentile = true, .column_names = {}});
+ ASSERT_NE(percentile_v2, nullptr);
+ EXPECT_EQ(percentile_v2->get_name(), "percentile_v2");
+
+ auto percentile_cont_v1 = factory.get("percentile_cont", percentile_types,
+ percentile_result_type, false,
be_version);
+ ASSERT_NE(percentile_cont_v1, nullptr);
+ EXPECT_EQ(percentile_cont_v1->get_name(), "percentile");
+
+ auto percentile_cont_v2 =
+ factory.get("percentile_cont", percentile_types,
percentile_result_type, false,
+ be_version, {.new_version_percentile = true,
.column_names = {}});
+ ASSERT_NE(percentile_cont_v2, nullptr);
+ EXPECT_EQ(percentile_cont_v2->get_name(), "percentile_v2");
+
+ DataTypes percentile_array_types = {
+ std::make_shared<DataTypeInt64>(),
+
std::make_shared<DataTypeArray>(make_nullable(std::make_shared<DataTypeFloat64>()))};
+ auto percentile_array_result_type =
+
std::make_shared<DataTypeArray>(make_nullable(std::make_shared<DataTypeFloat64>()));
+ auto percentile_array_v1 = factory.get("percentile_array",
percentile_array_types,
+ percentile_array_result_type,
false, be_version);
+ ASSERT_NE(percentile_array_v1, nullptr);
+ EXPECT_EQ(percentile_array_v1->get_name(), "percentile_array");
+
+ auto percentile_array_v2 =
+ factory.get("percentile_array", percentile_array_types,
percentile_array_result_type,
+ false, be_version, {.new_version_percentile = true,
.column_names = {}});
+ ASSERT_NE(percentile_array_v2, nullptr);
+ EXPECT_EQ(percentile_array_v2->get_name(), "percentile_array_v2");
+}
+
TEST(AggTest, window_function_test2) {
AggregateFunctionSimpleFactory factory;
register_aggregate_function_sum(factory);
diff --git a/be/test/util/counts_test.cpp b/be/test/util/counts_test.cpp
deleted file mode 100644
index 72f0fc122df..00000000000
--- a/be/test/util/counts_test.cpp
+++ /dev/null
@@ -1,82 +0,0 @@
-// 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 "util/counts.h"
-
-#include <gtest/gtest-message.h>
-#include <gtest/gtest-test-part.h>
-
-#include <cstdint>
-
-#include "gtest/gtest_pred_impl.h"
-
-namespace doris {
-
-class TCountsTest : public testing::Test {};
-
-TEST_F(TCountsTest, TotalTest) {
- Counts<int64_t> counts;
- // 1 1 1 2 5 7 7 9 9 19
- // >>> import numpy as np
- // >>> a = np.array([1,1,1,2,5,7,7,9,9,19])
- // >>> p = np.percentile(a, 20)
- counts.increment(1, 3);
- counts.increment(5, 1);
- counts.increment(2, 1);
- counts.increment(9, 1);
- counts.increment(9, 1);
- counts.increment(19, 1);
- counts.increment(7, 2);
-
- double result = counts.terminate(0.2);
- EXPECT_EQ(1, result);
-
- auto cs = ColumnString::create();
- BufferWritable bw(*cs);
- counts.serialize(bw);
- bw.commit();
-
- Counts<int64_t> other;
- StringRef res(cs->get_chars().data(), cs->get_chars().size());
- BufferReadable br(res);
- other.unserialize(br);
- double result1 = other.terminate(0.2);
- EXPECT_EQ(result, result1);
-
- Counts<int64_t> other1;
- other1.increment(1, 1);
- other1.increment(100, 3);
- other1.increment(50, 3);
- other1.increment(10, 1);
- other1.increment(99, 2);
-
- // deserialize other1
- cs->clear();
- other1.serialize(bw);
- bw.commit();
- Counts<int64_t> other1_deserialized;
- BufferReadable br1(res);
- other1_deserialized.unserialize(br1);
-
- Counts<int64_t> merge_res;
- merge_res.merge(&other);
- merge_res.merge(&other1_deserialized);
- // 1 1 1 1 2 5 7 7 9 9 10 19 50 50 50 99 99 100 100 100
- EXPECT_EQ(merge_res.terminate(0.3), 6.4);
-}
-
-} // namespace doris
diff --git a/be/test/util/percentile_util_test.cpp
b/be/test/util/percentile_util_test.cpp
new file mode 100644
index 00000000000..5f6126978e6
--- /dev/null
+++ b/be/test/util/percentile_util_test.cpp
@@ -0,0 +1,290 @@
+// 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 "util/percentile_util.h"
+
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <cstdint>
+#include <vector>
+
+namespace doris {
+
+class PercentileUtilTest : public testing::Test {};
+
+TEST_F(PercentileUtilTest, CountsTotalTest) {
+ Counts<int64_t> counts;
+ // 1 1 1 2 5 7 7 9 9 19
+ // >>> import numpy as np
+ // >>> a = np.array([1,1,1,2,5,7,7,9,9,19])
+ // >>> p = np.percentile(a, 20)
+ counts.increment(1, 3);
+ counts.increment(5, 1);
+ counts.increment(2, 1);
+ counts.increment(9, 1);
+ counts.increment(9, 1);
+ counts.increment(19, 1);
+ counts.increment(7, 2);
+
+ double result = counts.terminate(0.2);
+ EXPECT_EQ(1, result);
+
+ auto cs = ColumnString::create();
+ BufferWritable bw(*cs);
+ counts.serialize(bw);
+ bw.commit();
+
+ Counts<int64_t> other;
+ StringRef res(cs->get_chars().data(), cs->get_chars().size());
+ BufferReadable br(res);
+ other.unserialize(br);
+ double result1 = other.terminate(0.2);
+ EXPECT_EQ(result, result1);
+
+ Counts<int64_t> other1;
+ other1.increment(1, 1);
+ other1.increment(100, 3);
+ other1.increment(50, 3);
+ other1.increment(10, 1);
+ other1.increment(99, 2);
+
+ // deserialize other1
+ cs->clear();
+ other1.serialize(bw);
+ bw.commit();
+ Counts<int64_t> other1_deserialized;
+ BufferReadable br1(res);
+ other1_deserialized.unserialize(br1);
+
+ Counts<int64_t> merge_res;
+ merge_res.merge(&other);
+ merge_res.merge(&other1_deserialized);
+ // 1 1 1 1 2 5 7 7 9 9 10 19 50 50 50 99 99 100 100 100
+ EXPECT_EQ(merge_res.terminate(0.3), 6.4);
+}
+
+TEST_F(PercentileUtilTest, CountsBoundaryBehavior) {
+ Counts<int64_t> empty_counts;
+ EXPECT_DOUBLE_EQ(0.0, empty_counts.terminate(0.5));
+
+ Counts<int64_t> single_counts;
+ single_counts.increment(42);
+ EXPECT_DOUBLE_EQ(42.0, single_counts.terminate(0.0));
+ EXPECT_DOUBLE_EQ(42.0, single_counts.terminate(0.5));
+ EXPECT_DOUBLE_EQ(42.0, single_counts.terminate(1.0));
+}
+
+TEST_F(PercentileUtilTest, CountsSerializeMergedState) {
+ Counts<int64_t> left;
+ left.increment(5);
+ left.increment(1);
+
+ auto col = ColumnString::create();
+ BufferWritable writer(*col);
+ left.serialize(writer);
+ writer.commit();
+
+ StringRef left_data(col->get_chars().data(), col->get_chars().size());
+ BufferReadable left_reader(left_data);
+ Counts<int64_t> left_sorted;
+ left_sorted.unserialize(left_reader);
+
+ Counts<int64_t> right;
+ right.increment(9);
+ right.increment(7);
+
+ col->clear();
+ right.serialize(writer);
+ writer.commit();
+
+ StringRef right_data(col->get_chars().data(), col->get_chars().size());
+ BufferReadable right_reader(right_data);
+ Counts<int64_t> right_sorted;
+ right_sorted.unserialize(right_reader);
+
+ Counts<int64_t> merged;
+ merged.merge(&left_sorted);
+ merged.merge(&right_sorted);
+
+ col->clear();
+ merged.serialize(writer);
+ writer.commit();
+
+ StringRef merged_data(col->get_chars().data(), col->get_chars().size());
+ BufferReadable merged_reader(merged_data);
+ Counts<int64_t> restored;
+ restored.unserialize(merged_reader);
+
+ EXPECT_DOUBLE_EQ(1.0, restored.terminate(0.0));
+ EXPECT_DOUBLE_EQ(6.0, restored.terminate(0.5));
+ EXPECT_DOUBLE_EQ(9.0, restored.terminate(1.0));
+}
+
+TEST_F(PercentileUtilTest, CheckQuantileBoundary) {
+ EXPECT_NO_THROW(check_quantile(0.0));
+ EXPECT_NO_THROW(check_quantile(0.5));
+ EXPECT_NO_THROW(check_quantile(1.0));
+ EXPECT_THROW(check_quantile(-0.0001), Exception);
+ EXPECT_THROW(check_quantile(1.0001), Exception);
+}
+
+TEST_F(PercentileUtilTest, EmptyLevelsState) {
+ PercentileLevels levels;
+ EXPECT_TRUE(levels.empty());
+ EXPECT_TRUE(levels.quantiles.empty());
+ EXPECT_TRUE(levels.permutation.empty());
+
+ auto col = ColumnString::create();
+ BufferWritable writer(*col);
+ levels.write(writer);
+ writer.commit();
+
+ StringRef data(col->get_chars().data(), col->get_chars().size());
+ BufferReadable reader(data);
+
+ PercentileLevels restored;
+ restored.read(reader);
+ EXPECT_TRUE(restored.empty());
+ EXPECT_TRUE(restored.quantiles.empty());
+ EXPECT_TRUE(restored.permutation.empty());
+}
+
+TEST_F(PercentileUtilTest, SortPermutationKeepsQuantiles) {
+ PercentileLevels levels;
+ levels.quantiles = {0.56, 0.12, 0.45, 0.23};
+ levels.permutation = {0, 1, 2, 3};
+
+ const auto& permutation = levels.get_permutation();
+
+ EXPECT_EQ((std::vector<size_t> {1, 3, 2, 0}), permutation);
+ EXPECT_EQ((std::vector<size_t> {1, 3, 2, 0}), levels.permutation);
+ EXPECT_EQ((std::vector<double> {0.56, 0.12, 0.45, 0.23}),
levels.quantiles);
+}
+
+TEST_F(PercentileUtilTest, SortPermutationWithDuplicateQuantiles) {
+ PercentileLevels levels;
+ levels.quantiles = {0.7, 0.2, 0.2, 0.9, 0.7};
+ levels.permutation = {0, 1, 2, 3, 4};
+
+ const auto& permutation = levels.get_permutation();
+
+ ASSERT_EQ(levels.quantiles.size(), permutation.size());
+ std::vector<size_t> sorted_perm = permutation;
+ std::sort(sorted_perm.begin(), sorted_perm.end());
+ EXPECT_EQ((std::vector<size_t> {0, 1, 2, 3, 4}), sorted_perm);
+ for (size_t i = 1; i < permutation.size(); ++i) {
+ EXPECT_LE(levels.quantiles[permutation[i - 1]],
levels.quantiles[permutation[i]]);
+ }
+}
+
+TEST_F(PercentileUtilTest, WriteReadDefersPermutationSort) {
+ PercentileLevels levels;
+ levels.quantiles = {0.56, 0.12, 0.45, 0.23};
+ levels.permutation = {0, 1, 2, 3};
+
+ auto col = ColumnString::create();
+ BufferWritable writer(*col);
+ levels.write(writer);
+ writer.commit();
+
+ StringRef data(col->get_chars().data(), col->get_chars().size());
+ BufferReadable reader(data);
+
+ PercentileLevels restored;
+ restored.read(reader);
+
+ EXPECT_EQ(levels.quantiles, restored.quantiles);
+ EXPECT_EQ((std::vector<size_t> {0, 1, 2, 3}), restored.permutation);
+ EXPECT_EQ((std::vector<size_t> {1, 3, 2, 0}), restored.get_permutation());
+}
+
+TEST_F(PercentileUtilTest, ReadBuildsPermutationFromUnsortedQuantiles) {
+ auto col = ColumnString::create();
+ BufferWritable writer(*col);
+ writer.write_binary(4);
+ writer.write_binary(0.56);
+ writer.write_binary(0.12);
+ writer.write_binary(0.45);
+ writer.write_binary(0.23);
+ writer.commit();
+
+ StringRef data(col->get_chars().data(), col->get_chars().size());
+ BufferReadable reader(data);
+
+ PercentileLevels levels;
+ levels.read(reader);
+
+ EXPECT_EQ((std::vector<double> {0.56, 0.12, 0.45, 0.23}),
levels.quantiles);
+ EXPECT_EQ((std::vector<size_t> {0, 1, 2, 3}), levels.permutation);
+ EXPECT_EQ((std::vector<size_t> {1, 3, 2, 0}), levels.get_permutation());
+}
+
+TEST_F(PercentileUtilTest, MergeFromEmptyCopiesState) {
+ PercentileLevels src;
+ src.quantiles = {0.56, 0.12, 0.45, 0.23};
+ src.permutation = {0, 1, 2, 3};
+
+ PercentileLevels dst;
+ dst.merge(src);
+
+ EXPECT_EQ(src.quantiles, dst.quantiles);
+ EXPECT_EQ(src.permutation, dst.permutation);
+}
+
+TEST_F(PercentileUtilTest, MergeEmptyRightKeepsLeft) {
+ PercentileLevels lhs;
+ lhs.quantiles = {0.56, 0.12, 0.45, 0.23};
+ lhs.permutation = {0, 1, 2, 3};
+
+ PercentileLevels rhs;
+ lhs.merge(rhs);
+
+ EXPECT_EQ((std::vector<double> {0.56, 0.12, 0.45, 0.23}), lhs.quantiles);
+ EXPECT_EQ((std::vector<size_t> {0, 1, 2, 3}), lhs.permutation);
+ EXPECT_EQ((std::vector<size_t> {1, 3, 2, 0}), lhs.get_permutation());
+}
+
+TEST_F(PercentileUtilTest, MergeSameStateKeepsState) {
+ PercentileLevels lhs;
+ lhs.quantiles = {0.56, 0.12, 0.45, 0.23};
+ lhs.permutation = {0, 1, 2, 3};
+
+ PercentileLevels rhs;
+ rhs.quantiles = lhs.quantiles;
+ rhs.permutation = lhs.permutation;
+
+ lhs.merge(rhs);
+
+ EXPECT_EQ((std::vector<double> {0.56, 0.12, 0.45, 0.23}), lhs.quantiles);
+ EXPECT_EQ((std::vector<size_t> {0, 1, 2, 3}), lhs.permutation);
+ EXPECT_EQ((std::vector<size_t> {1, 3, 2, 0}), lhs.get_permutation());
+}
+
+TEST_F(PercentileUtilTest, ClearResetsState) {
+ PercentileLevels levels;
+ levels.quantiles = {0.56, 0.12, 0.45, 0.23};
+ levels.permutation = {1, 3, 2, 0};
+
+ levels.clear();
+
+ EXPECT_TRUE(levels.empty());
+ EXPECT_TRUE(levels.quantiles.empty());
+ EXPECT_TRUE(levels.permutation.empty());
+}
+
+} // namespace doris
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/load/NereidsStreamLoadPlanner.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/load/NereidsStreamLoadPlanner.java
index 136133e0797..191a74260be 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/load/NereidsStreamLoadPlanner.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/load/NereidsStreamLoadPlanner.java
@@ -321,6 +321,7 @@ public class NereidsStreamLoadPlanner {
: false;
queryOptions.setEnableMemtableOnSinkNode(enableMemtableOnSinkNode);
queryOptions.setNewVersionUnixTimestamp(true);
+ queryOptions.setNewVersionPercentile(true);
params.setQueryOptions(queryOptions);
TQueryGlobals queryGlobals = new TQueryGlobals();
queryGlobals.setNowString(TimeUtils.getDatetimeFormatWithTimeZone().format(LocalDateTime.now()));
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/FoldConstantRuleOnBE.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/FoldConstantRuleOnBE.java
index 331d70aac8e..76b57eece78 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/FoldConstantRuleOnBE.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/FoldConstantRuleOnBE.java
@@ -320,6 +320,7 @@ public class FoldConstantRuleOnBE implements
ExpressionPatternRuleFactory {
tQueryOptions.setBeExecVersion(Config.be_exec_version);
tQueryOptions.setEnableDecimal256(context.getSessionVariable().isEnableDecimal256());
tQueryOptions.setNewVersionUnixTimestamp(true);
+ tQueryOptions.setNewVersionPercentile(true);
tQueryOptions.setEnableStrictCast(SessionVariable.enableStrictCast());
TFoldConstantParams tParams = new TFoldConstantParams(paramMap,
queryGlobals);
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/qe/CoordinatorContext.java
b/fe/fe-core/src/main/java/org/apache/doris/qe/CoordinatorContext.java
index 945cf66bbfe..61aee063452 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/CoordinatorContext.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/CoordinatorContext.java
@@ -308,6 +308,7 @@ public class CoordinatorContext {
queryOptions.setProfileLevel(2);
queryOptions.setBeExecVersion(Config.be_exec_version);
queryOptions.setNewVersionUnixTimestamp(true);
+ queryOptions.setNewVersionPercentile(true);
TQueryGlobals queryGlobals = new TQueryGlobals();
queryGlobals.setNowString(TimeUtils.getDatetimeFormatWithTimeZone().format(LocalDateTime.now()));
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/qe/NereidsCoordinator.java
b/fe/fe-core/src/main/java/org/apache/doris/qe/NereidsCoordinator.java
index 4084e4a84e8..f8c7509f102 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/NereidsCoordinator.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/NereidsCoordinator.java
@@ -492,6 +492,7 @@ public class NereidsCoordinator extends Coordinator {
// Set this field to true to avoid data entering the normal cache LRU
queue
this.coordinatorContext.queryOptions.setDisableFileCache(true);
this.coordinatorContext.queryOptions.setNewVersionUnixTimestamp(true);
+ this.coordinatorContext.queryOptions.setNewVersionPercentile(true);
}
private void setForQuery() {
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java
b/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java
index c9020b6c9dc..c140ead2369 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java
@@ -5637,6 +5637,7 @@ public class SessionVariable implements Serializable,
Writable {
tResult.setEnableStrictCast(enableStrictCast());
tResult.setEnableInsertStrict(enableInsertStrict);
tResult.setNewVersionUnixTimestamp(true); // once FE upgraded, always
use new version
+ tResult.setNewVersionPercentile(true);
tResult.setHnswEfSearch(hnswEFSearch);
tResult.setHnswCheckRelativeDistance(hnswCheckRelativeDistance);
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]