// Copyright 2023 PingCAP, Inc. // // Licensed 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace DB { /// uniq extern const String uniq_raw_res_name; struct AggregateFunctionUniqUniquesHashSetData : AggregationCollatorsWrapper { void write(WriteBuffer & buf) const { set.write(buf); writeCollators(buf); } void read(ReadBuffer & buf) { set.read(buf); readCollators(buf); } using Set = UniquesHashSet>; Set set; static String getName() { return "uniq"; } }; /// For a function that takes multiple arguments. Such a function pre-hashes them in advance, so TrivialHash is used here. struct AggregateFunctionUniqUniquesHashSetDataForVariadic : AggregationCollatorsWrapper { void write(WriteBuffer & buf) const { set.write(buf); writeCollators(buf); } void read(ReadBuffer & buf) { set.read(buf); readCollators(buf); } using Set = UniquesHashSet; Set set; static String getName() { return "uniq"; } }; struct AggregateFunctionUniqUniquesHashSetDataForVariadicRawRes : AggregationCollatorsWrapper { void write(WriteBuffer & buf) const { set.write(buf); writeCollators(buf); } void read(ReadBuffer & buf) { set.read(buf); readCollators(buf); } using Set = UniquesHashSet; Set set; static String getName() { return uniq_raw_res_name; } }; /// uniqHLL12 template struct AggregateFunctionUniqHLL12Data : AggregationCollatorsWrapper { void write(WriteBuffer & buf) const { set.write(buf); writeCollators(buf); } void read(ReadBuffer & buf) { set.read(buf); readCollators(buf); } using Set = HyperLogLogWithSmallSetOptimization; Set set; static String getName() { return "uniqHLL12"; } }; template <> struct AggregateFunctionUniqHLL12Data : AggregationCollatorsWrapper { void write(WriteBuffer & buf) const { set.write(buf); writeCollators(buf); } void read(ReadBuffer & buf) { set.read(buf); readCollators(buf); } using Set = HyperLogLogWithSmallSetOptimization; Set set; static String getName() { return "uniqHLL12"; } }; template <> struct AggregateFunctionUniqHLL12Data : AggregationCollatorsWrapper { void write(WriteBuffer & buf) const { set.write(buf); writeCollators(buf); } void read(ReadBuffer & buf) { set.read(buf); readCollators(buf); } using Set = HyperLogLogWithSmallSetOptimization; Set set; static String getName() { return "uniqHLL12"; } }; struct AggregateFunctionUniqHLL12DataForVariadic : AggregationCollatorsWrapper { void write(WriteBuffer & buf) const { set.write(buf); writeCollators(buf); } void read(ReadBuffer & buf) { set.read(buf); readCollators(buf); } using Set = HyperLogLogWithSmallSetOptimization; Set set; static String getName() { return "uniqHLL12"; } }; /// uniqExact template struct AggregateFunctionUniqExactData : AggregationCollatorsWrapper { void write(WriteBuffer & buf) const { set.write(buf); writeCollators(buf); } void read(ReadBuffer & buf) { set.read(buf); readCollators(buf); } using Key = T; /// When creating, the hash table must be small. using Set = HashSet, HashTableGrower<4>, HashTableAllocatorWithStackMemory>; Set set; static String getName() { return "uniqExact"; } }; /// For rows, we put the SipHash values (128 bits) into the hash table. template <> struct AggregateFunctionUniqExactData : AggregationCollatorsWrapper { void write(WriteBuffer & buf) const { set.write(buf); writeCollators(buf); } void read(ReadBuffer & buf) { set.read(buf); readCollators(buf); } using Key = UInt128; /// When creating, the hash table must be small. using Set = HashSet, HashTableAllocatorWithStackMemory>; Set set; static String getName() { return "uniqExact"; } }; template struct AggregateFunctionUniqCombinedData : AggregationCollatorsWrapper { void write(WriteBuffer & buf) const { set.write(buf); writeCollators(buf); } void read(ReadBuffer & buf) { set.read(buf); readCollators(buf); } using Key = UInt32; using Set = CombinedCardinalityEstimator< Key, HashSet>, 16, 14, 17, TrivialHash, UInt32, HyperLogLogBiasEstimator, HyperLogLogMode::FullFeatured>; Set set; static String getName() { return "uniqCombined"; } }; template <> struct AggregateFunctionUniqCombinedData : AggregationCollatorsWrapper { void write(WriteBuffer & buf) const { set.write(buf); writeCollators(buf); } void read(ReadBuffer & buf) { set.read(buf); readCollators(buf); } using Key = UInt64; using Set = CombinedCardinalityEstimator< Key, HashSet>, 16, 14, 17, TrivialHash, UInt64, HyperLogLogBiasEstimator, HyperLogLogMode::FullFeatured>; Set set; static String getName() { return "uniqCombined"; } }; namespace detail { /** Hash function for uniq. */ template struct AggregateFunctionUniqTraits { static UInt64 hash(T x) { return x; } }; template <> struct AggregateFunctionUniqTraits { static UInt64 hash(UInt128 x) { return sipHash64(x); } }; template <> struct AggregateFunctionUniqTraits { static UInt64 hash(Float32 x) { UInt64 res = 0; memcpy(reinterpret_cast(&res), reinterpret_cast(&x), sizeof(x)); return res; } }; template <> struct AggregateFunctionUniqTraits { static UInt64 hash(Float64 x) { UInt64 res = 0; memcpy(reinterpret_cast(&res), reinterpret_cast(&x), sizeof(x)); return res; } }; /** Hash function for uniqCombined. */ template struct AggregateFunctionUniqCombinedTraits { static UInt32 hash(T x) { return static_cast(intHash64(x)); } }; template <> struct AggregateFunctionUniqCombinedTraits { static UInt32 hash(UInt128 x) { return sipHash64(x); } }; template <> struct AggregateFunctionUniqCombinedTraits { static UInt32 hash(Float32 x) { UInt64 res = 0; memcpy(reinterpret_cast(&res), reinterpret_cast(&x), sizeof(x)); return static_cast(intHash64(res)); } }; template <> struct AggregateFunctionUniqCombinedTraits { static UInt32 hash(Float64 x) { UInt64 res = 0; memcpy(reinterpret_cast(&res), reinterpret_cast(&x), sizeof(x)); return static_cast(intHash64(res)); } }; /** The structure for the delegation work to add one element to the `uniq` aggregate functions. * Used for partial specialization to add strings. */ template struct OneAdder { static void ALWAYS_INLINE add(Data & data, const IColumn & column, size_t row_num) { if constexpr ( std::is_same_v || std::is_same_v>) { if constexpr (!std::is_same_v) { const auto & value = static_cast &>(column).getData()[row_num]; data.set.insert(AggregateFunctionUniqTraits::hash(value)); } else { StringRef value = column.getDataAt(row_num); value = data.getUpdatedValueForCollator(value, 0); data.set.insert(CityHash_v1_0_2::CityHash64(value.data, value.size)); } } else if constexpr (std::is_same_v>) { if constexpr (!std::is_same_v) { const auto & value = static_cast &>(column).getData()[row_num]; data.set.insert(AggregateFunctionUniqCombinedTraits::hash(value)); } else { StringRef value = column.getDataAt(row_num); value = data.getUpdatedValueForCollator(value, 0); data.set.insert(CityHash_v1_0_2::CityHash64(value.data, value.size)); } } else if constexpr (std::is_same_v>) { if constexpr (!std::is_same_v) { data.set.insert(static_cast &>(column).getData()[row_num]); } else { StringRef value = column.getDataAt(row_num); value = data.getUpdatedValueForCollator(value, 0); UInt128 key; SipHash hash; hash.update(value.data, value.size); hash.get128(key); data.set.insert(key); } } } }; } // namespace detail /// Calculates the number of different values approximately or exactly. template class AggregateFunctionUniq final : public IAggregateFunctionDataHelper, true> { public: String getName() const override { return Data::getName(); } DataTypePtr getReturnType() const override { return std::make_shared(); } void add(AggregateDataPtr __restrict place, const IColumn ** columns, size_t row_num, Arena *) const override { detail::OneAdder::add(this->data(place), *columns[0], row_num); } void merge(AggregateDataPtr __restrict place, ConstAggregateDataPtr rhs, Arena *) const override { this->data(place).set.merge(this->data(rhs).set); } void serialize(ConstAggregateDataPtr __restrict place, WriteBuffer & buf) const override { this->data(place).write(buf); } void deserialize(AggregateDataPtr __restrict place, ReadBuffer & buf, Arena *) const override { this->data(place).read(buf); } void insertResultInto(ConstAggregateDataPtr __restrict place, IColumn & to, Arena *) const override { static_cast(to).getData().push_back(this->data(place).set.size()); } const char * getHeaderFilePath() const override { return __FILE__; } }; /** For multiple arguments. To compute, hashes them. * You can pass multiple arguments as is; You can also pass one argument - a tuple. * But (for the possibility of efficient implementation), you can not pass several arguments, among which there are tuples. */ template class AggregateFunctionUniqVariadic final : public IAggregateFunctionDataHelper< Data, AggregateFunctionUniqVariadic, true> { private: static constexpr bool is_exact = std::is_same_v>; size_t num_args = 0; public: AggregateFunctionUniqVariadic(const DataTypes & arguments) { if (argument_is_tuple) num_args = typeid_cast(*arguments[0]).getElements().size(); else num_args = arguments.size(); } String getName() const override { return Data::getName(); } DataTypePtr getReturnType() const override { if constexpr (raw_result) return std::make_shared(); else return std::make_shared(); } void add(AggregateDataPtr __restrict place, const IColumn ** columns, size_t row_num, Arena *) const override { this->data(place).set.insert( UniqVariadicHash::apply(this->data(place), num_args, columns, row_num)); } void merge(AggregateDataPtr __restrict place, ConstAggregateDataPtr rhs, Arena *) const override { this->data(place).set.merge(this->data(rhs).set); } void serialize(ConstAggregateDataPtr __restrict place, WriteBuffer & buf) const override { this->data(place).set.write(buf); } void deserialize(AggregateDataPtr __restrict place, ReadBuffer & buf, Arena *) const override { this->data(place).set.read(buf); } void insertResultInto(ConstAggregateDataPtr __restrict place, IColumn & to, Arena *) const override { if constexpr (raw_result) { WriteBufferFromOwnString buf; serialize(place, buf); static_cast(to).insertData(buf.str().data(), buf.count()); } else static_cast(to).getData().push_back(this->data(place).set.size()); } const char * getHeaderFilePath() const override { return __FILE__; } }; } // namespace DB