1#include <AggregateFunctions/AggregateFunctionUniqCombined.h>
2
3#include <AggregateFunctions/AggregateFunctionFactory.h>
4#include <AggregateFunctions/Helpers.h>
5
6#include <DataTypes/DataTypeDate.h>
7#include <DataTypes/DataTypeDateTime.h>
8
9#include <functional>
10#include "registerAggregateFunctions.h"
11
12namespace DB
13{
14namespace ErrorCodes
15{
16 extern const int ILLEGAL_TYPE_OF_ARGUMENT;
17 extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;
18 extern const int ARGUMENT_OUT_OF_BOUND;
19}
20
21namespace
22{
23 template <UInt8 K, typename HashValueType>
24 struct WithK
25 {
26 template <typename T>
27 using AggregateFunction = AggregateFunctionUniqCombined<T, K, HashValueType>;
28
29 template <bool is_exact, bool argument_is_tuple>
30 using AggregateFunctionVariadic = AggregateFunctionUniqCombinedVariadic<is_exact, argument_is_tuple, K, HashValueType>;
31 };
32
33 template <UInt8 K, typename HashValueType>
34 AggregateFunctionPtr createAggregateFunctionWithK(const DataTypes & argument_types, const Array & params)
35 {
36 /// We use exact hash function if the arguments are not contiguous in memory, because only exact hash function has support for this case.
37 bool use_exact_hash_function = !isAllArgumentsContiguousInMemory(argument_types);
38
39 if (argument_types.size() == 1)
40 {
41 const IDataType & argument_type = *argument_types[0];
42
43 AggregateFunctionPtr res(createWithNumericType<WithK<K, HashValueType>::template AggregateFunction>(*argument_types[0], argument_types, params));
44
45 WhichDataType which(argument_type);
46 if (res)
47 return res;
48 else if (which.isDate())
49 return std::make_shared<typename WithK<K, HashValueType>::template AggregateFunction<DataTypeDate::FieldType>>(argument_types, params);
50 else if (which.isDateTime())
51 return std::make_shared<typename WithK<K, HashValueType>::template AggregateFunction<DataTypeDateTime::FieldType>>(argument_types, params);
52 else if (which.isStringOrFixedString())
53 return std::make_shared<typename WithK<K, HashValueType>::template AggregateFunction<String>>(argument_types, params);
54 else if (which.isUUID())
55 return std::make_shared<typename WithK<K, HashValueType>::template AggregateFunction<DataTypeUUID::FieldType>>(argument_types, params);
56 else if (which.isTuple())
57 {
58 if (use_exact_hash_function)
59 return std::make_shared<typename WithK<K, HashValueType>::template AggregateFunctionVariadic<true, true>>(argument_types, params);
60 else
61 return std::make_shared<typename WithK<K, HashValueType>::template AggregateFunctionVariadic<false, true>>(argument_types, params);
62 }
63 }
64
65 /// "Variadic" method also works as a fallback generic case for a single argument.
66 if (use_exact_hash_function)
67 return std::make_shared<typename WithK<K, HashValueType>::template AggregateFunctionVariadic<true, false>>(argument_types, params);
68 else
69 return std::make_shared<typename WithK<K, HashValueType>::template AggregateFunctionVariadic<false, false>>(argument_types, params);
70 }
71
72 template <UInt8 K>
73 AggregateFunctionPtr createAggregateFunctionWithHashType(bool use_64_bit_hash, const DataTypes & argument_types, const Array & params)
74 {
75 if (use_64_bit_hash)
76 return createAggregateFunctionWithK<K, UInt64>(argument_types, params);
77 else
78 return createAggregateFunctionWithK<K, UInt32>(argument_types, params);
79 }
80
81 AggregateFunctionPtr createAggregateFunctionUniqCombined(bool use_64_bit_hash,
82 const std::string & name, const DataTypes & argument_types, const Array & params)
83 {
84 /// log2 of the number of cells in HyperLogLog.
85 /// Reasonable default value, selected to be comparable in quality with "uniq" aggregate function.
86 UInt8 precision = 17;
87
88 if (!params.empty())
89 {
90 if (params.size() != 1)
91 throw Exception(
92 "Aggregate function " + name + " requires one parameter or less.", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
93
94 UInt64 precision_param = applyVisitor(FieldVisitorConvertToNumber<UInt64>(), params[0]);
95 // This range is hardcoded below
96 if (precision_param > 20 || precision_param < 12)
97 throw Exception(
98 "Parameter for aggregate function " + name + " is out or range: [12, 20].", ErrorCodes::ARGUMENT_OUT_OF_BOUND);
99 precision = precision_param;
100 }
101
102 if (argument_types.empty())
103 throw Exception("Incorrect number of arguments for aggregate function " + name, ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
104
105 switch (precision)
106 {
107 case 12:
108 return createAggregateFunctionWithHashType<12>(use_64_bit_hash, argument_types, params);
109 case 13:
110 return createAggregateFunctionWithHashType<13>(use_64_bit_hash, argument_types, params);
111 case 14:
112 return createAggregateFunctionWithHashType<14>(use_64_bit_hash, argument_types, params);
113 case 15:
114 return createAggregateFunctionWithHashType<15>(use_64_bit_hash, argument_types, params);
115 case 16:
116 return createAggregateFunctionWithHashType<16>(use_64_bit_hash, argument_types, params);
117 case 17:
118 return createAggregateFunctionWithHashType<17>(use_64_bit_hash, argument_types, params);
119 case 18:
120 return createAggregateFunctionWithHashType<18>(use_64_bit_hash, argument_types, params);
121 case 19:
122 return createAggregateFunctionWithHashType<19>(use_64_bit_hash, argument_types, params);
123 case 20:
124 return createAggregateFunctionWithHashType<20>(use_64_bit_hash, argument_types, params);
125 }
126
127 __builtin_unreachable();
128 }
129
130}
131
132void registerAggregateFunctionUniqCombined(AggregateFunctionFactory & factory)
133{
134 using namespace std::placeholders;
135 factory.registerFunction("uniqCombined", std::bind(createAggregateFunctionUniqCombined, false, _1, _2, _3));
136 factory.registerFunction("uniqCombined64", std::bind(createAggregateFunctionUniqCombined, true, _1, _2, _3));
137}
138
139}
140