1#pragma once
2
3#include <Core/Types.h>
4#include <Core/Defines.h>
5#include <DataTypes/IDataType.h>
6#include <Functions/IFunctionImpl.h>
7#include <IO/WriteHelpers.h>
8#include <type_traits>
9
10
11#if USE_EMBEDDED_COMPILER
12#include <DataTypes/Native.h>
13
14#pragma GCC diagnostic push
15#pragma GCC diagnostic ignored "-Wunused-parameter"
16#include <llvm/IR/IRBuilder.h>
17#pragma GCC diagnostic pop
18#endif
19
20
21
22/** Logical functions AND, OR, XOR and NOT support three-valued (or ternary) logic
23 * https://en.wikibooks.org/wiki/Structured_Query_Language/NULLs_and_the_Three_Valued_Logic
24 *
25 * Functions XOR and NOT rely on "default implementation for NULLs":
26 * - if any of the arguments is of Nullable type, the return value type is Nullable
27 * - if any of the arguments is NULL, the return value is NULL
28 *
29 * Functions AND and OR provide their own special implementations for ternary logic
30 */
31
32namespace DB
33{
34namespace FunctionsLogicalDetail
35{
36namespace Ternary
37{
38 using ResultType = UInt8;
39
40 static constexpr UInt8 False = 0;
41 static constexpr UInt8 True = -1;
42 static constexpr UInt8 Null = 1;
43
44 template <typename T>
45 inline ResultType makeValue(T value)
46 {
47 return value != 0 ? Ternary::True : Ternary::False;
48 }
49
50 template <typename T>
51 inline ResultType makeValue(T value, bool is_null)
52 {
53 if (is_null)
54 return Ternary::Null;
55 return makeValue<T>(value);
56 }
57}
58
59
60struct AndImpl
61{
62 using ResultType = UInt8;
63
64 static inline constexpr bool isSaturable() { return true; }
65 static inline constexpr bool isSaturatedValue(UInt8 a) { return a == Ternary::False; }
66 static inline constexpr ResultType apply(UInt8 a, UInt8 b) { return a & b; }
67 static inline constexpr bool specialImplementationForNulls() { return true; }
68};
69
70struct OrImpl
71{
72 using ResultType = UInt8;
73
74 static inline constexpr bool isSaturable() { return true; }
75 static inline constexpr bool isSaturatedValue(UInt8 a) { return a == Ternary::True; }
76 static inline constexpr ResultType apply(UInt8 a, UInt8 b) { return a | b; }
77 static inline constexpr bool specialImplementationForNulls() { return true; }
78};
79
80struct XorImpl
81{
82 using ResultType = UInt8;
83
84 static inline constexpr bool isSaturable() { return false; }
85 static inline constexpr bool isSaturatedValue(bool) { return false; }
86 /** Considering that CH uses UInt8 for representation of boolean values this function
87 * returns 255 as "true" but the current implementation of logical functions suggests that
88 * any nonzero value is "true" as well. Also the current code provides no guarantee
89 * for "true" to be represented with the value of 1.
90 */
91 static inline constexpr ResultType apply(UInt8 a, UInt8 b) { return (a != b) ? Ternary::True : Ternary::False; }
92 static inline constexpr bool specialImplementationForNulls() { return false; }
93
94#if USE_EMBEDDED_COMPILER
95 static inline llvm::Value * apply(llvm::IRBuilder<> & builder, llvm::Value * a, llvm::Value * b)
96 {
97 return builder.CreateXor(a, b);
98 }
99#endif
100};
101
102template <typename A>
103struct NotImpl
104{
105 using ResultType = UInt8;
106
107 static inline ResultType apply(A a)
108 {
109 return !a;
110 }
111
112#if USE_EMBEDDED_COMPILER
113 static inline llvm::Value * apply(llvm::IRBuilder<> & builder, llvm::Value * a)
114 {
115 return builder.CreateNot(a);
116 }
117#endif
118};
119
120template <typename Impl, typename Name>
121class FunctionAnyArityLogical : public IFunction
122{
123public:
124 static constexpr auto name = Name::name;
125 static FunctionPtr create(const Context &) { return std::make_shared<FunctionAnyArityLogical>(); }
126
127public:
128 String getName() const override
129 {
130 return name;
131 }
132
133 bool isVariadic() const override { return true; }
134 size_t getNumberOfArguments() const override { return 0; }
135
136 bool useDefaultImplementationForNulls() const override { return !Impl::specialImplementationForNulls(); }
137
138 /// Get result types by argument types. If the function does not apply to these arguments, throw an exception.
139 DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override;
140
141 void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result_index, size_t input_rows_count) override;
142
143#if USE_EMBEDDED_COMPILER
144 bool isCompilableImpl(const DataTypes &) const override { return useDefaultImplementationForNulls(); }
145
146 llvm::Value * compileImpl(llvm::IRBuilderBase & builder, const DataTypes & types, ValuePlaceholders values) const override
147 {
148 auto & b = static_cast<llvm::IRBuilder<> &>(builder);
149 if constexpr (!Impl::isSaturable())
150 {
151 auto * result = nativeBoolCast(b, types[0], values[0]());
152 for (size_t i = 1; i < types.size(); i++)
153 result = Impl::apply(b, result, nativeBoolCast(b, types[i], values[i]()));
154 return b.CreateSelect(result, b.getInt8(1), b.getInt8(0));
155 }
156 constexpr bool breakOnTrue = Impl::isSaturatedValue(true);
157 auto * next = b.GetInsertBlock();
158 auto * stop = llvm::BasicBlock::Create(next->getContext(), "", next->getParent());
159 b.SetInsertPoint(stop);
160 auto * phi = b.CreatePHI(b.getInt8Ty(), values.size());
161 for (size_t i = 0; i < types.size(); i++)
162 {
163 b.SetInsertPoint(next);
164 auto * value = values[i]();
165 auto * truth = nativeBoolCast(b, types[i], value);
166 if (!types[i]->equals(DataTypeUInt8{}))
167 value = b.CreateSelect(truth, b.getInt8(1), b.getInt8(0));
168 phi->addIncoming(value, b.GetInsertBlock());
169 if (i + 1 < types.size())
170 {
171 next = llvm::BasicBlock::Create(next->getContext(), "", next->getParent());
172 b.CreateCondBr(truth, breakOnTrue ? stop : next, breakOnTrue ? next : stop);
173 }
174 }
175 b.CreateBr(stop);
176 b.SetInsertPoint(stop);
177 return phi;
178 }
179#endif
180};
181
182
183template <template <typename> class Impl, typename Name>
184class FunctionUnaryLogical : public IFunction
185{
186public:
187 static constexpr auto name = Name::name;
188 static FunctionPtr create(const Context &) { return std::make_shared<FunctionUnaryLogical>(); }
189
190public:
191 String getName() const override
192 {
193 return name;
194 }
195
196 size_t getNumberOfArguments() const override { return 1; }
197
198 DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override;
199
200 bool useDefaultImplementationForConstants() const override { return true; }
201
202 void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t /*input_rows_count*/) override;
203
204#if USE_EMBEDDED_COMPILER
205 bool isCompilableImpl(const DataTypes &) const override { return true; }
206
207 llvm::Value * compileImpl(llvm::IRBuilderBase & builder, const DataTypes & types, ValuePlaceholders values) const override
208 {
209 auto & b = static_cast<llvm::IRBuilder<> &>(builder);
210 return b.CreateSelect(Impl<UInt8>::apply(b, nativeBoolCast(b, types[0], values[0]())), b.getInt8(1), b.getInt8(0));
211 }
212#endif
213};
214
215}
216
217struct NameAnd { static constexpr auto name = "and"; };
218struct NameOr { static constexpr auto name = "or"; };
219struct NameXor { static constexpr auto name = "xor"; };
220struct NameNot { static constexpr auto name = "not"; };
221
222using FunctionAnd = FunctionsLogicalDetail::FunctionAnyArityLogical<FunctionsLogicalDetail::AndImpl, NameAnd>;
223using FunctionOr = FunctionsLogicalDetail::FunctionAnyArityLogical<FunctionsLogicalDetail::OrImpl, NameOr>;
224using FunctionXor = FunctionsLogicalDetail::FunctionAnyArityLogical<FunctionsLogicalDetail::XorImpl, NameXor>;
225using FunctionNot = FunctionsLogicalDetail::FunctionUnaryLogical<FunctionsLogicalDetail::NotImpl, NameNot>;
226
227}
228