ClickHouse/src/Functions/FunctionsLogical.h

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

274 lines
10 KiB
C++
Raw Normal View History

2011-08-22 01:01:01 +00:00
#pragma once
2021-10-02 07:13:14 +00:00
#include <base/types.h>
#include <Core/Defines.h>
#include <DataTypes/IDataType.h>
2021-04-27 11:10:38 +00:00
#include <DataTypes/DataTypesNumber.h>
2021-05-17 07:30:42 +00:00
#include <Functions/IFunction.h>
#include <IO/WriteHelpers.h>
#include <type_traits>
#include <Interpreters/Context_fwd.h>
2011-08-22 01:01:01 +00:00
2018-09-03 04:57:01 +00:00
2018-04-30 12:33:40 +00:00
#if USE_EMBEDDED_COMPILER
2023-04-11 18:04:30 +00:00
# include <DataTypes/Native.h>
# include <llvm/IR/IRBuilder.h>
2018-04-30 12:33:40 +00:00
#endif
2011-08-22 01:01:01 +00:00
/** Logical functions AND, OR, XOR and NOT support three-valued (or ternary) logic
* https://en.wikibooks.org/wiki/Structured_Query_Language/NULLs_and_the_Three_Valued_Logic
*
* Functions XOR and NOT rely on "default implementation for NULLs":
* - if any of the arguments is of Nullable type, the return value type is Nullable
* - if any of the arguments is NULL, the return value is NULL
*
* Functions AND and OR provide their own special implementations for ternary logic
2011-08-22 01:01:01 +00:00
*/
namespace DB
{
2021-04-27 13:12:57 +00:00
struct NameAnd { static constexpr auto name = "and"; };
struct NameOr { static constexpr auto name = "or"; };
struct NameXor { static constexpr auto name = "xor"; };
struct NameNot { static constexpr auto name = "not"; };
namespace FunctionsLogicalDetail
2019-06-22 10:49:19 +00:00
{
namespace Ternary
2019-06-22 10:49:19 +00:00
{
using ResultType = UInt8;
2019-06-22 10:49:19 +00:00
/** These values are carefully picked so that they could be efficiently evaluated with bitwise operations, which
* are feasible for auto-vectorization by the compiler. The expression for the ternary value evaluation writes:
2020-07-07 20:56:40 +00:00
*
* ternary_value = ((value << 1) | is_null) & (1 << !is_null)
*
* The truth table of the above formula lists:
* +---------------+--------------+-------------+
* | is_null\value | 0 | 1 |
* +---------------+--------------+-------------+
* | 0 | 0b00 (False) | 0b10 (True) |
* | 1 | 0b01 (Null) | 0b01 (Null) |
* +---------------+--------------+-------------+
*
* As the numerical values of False, Null and True are assigned in ascending order, the "and" and "or" of
* ternary logic could be implemented with minimum and maximum respectively, which are also vectorizable.
* https://en.wikipedia.org/wiki/Three-valued_logic
2020-07-07 20:56:40 +00:00
*
* This logic does not apply for "not" and "xor" - they work with default implementation for NULLs:
* anything with NULL returns NULL, otherwise use conventional two-valued logic.
*/
static constexpr UInt8 False = 0; /// 0b00
static constexpr UInt8 Null = 1; /// 0b01
static constexpr UInt8 True = 2; /// 0b10
2019-06-22 10:49:19 +00:00
template <typename T>
inline ResultType makeValue(T value)
2019-06-22 10:49:19 +00:00
{
return value != 0 ? Ternary::True : Ternary::False;
2019-06-22 10:49:19 +00:00
}
2019-07-18 08:07:24 +00:00
template <typename T>
inline ResultType makeValue(T value, bool is_null)
2019-06-22 10:49:19 +00:00
{
if (is_null)
return Ternary::Null;
2019-07-18 08:07:24 +00:00
return makeValue<T>(value);
2019-06-22 10:49:19 +00:00
}
}
2011-08-22 01:01:01 +00:00
struct AndImpl
{
using ResultType = UInt8;
static inline constexpr bool isSaturable() { return true; }
2020-07-07 20:56:40 +00:00
/// Final value in two-valued logic (no further operations with True, False will change this value)
static inline constexpr bool isSaturatedValue(bool a) { return !a; }
2020-07-07 20:56:40 +00:00
/// Final value in three-valued logic (no further operations with True, False, Null will change this value)
static inline constexpr bool isSaturatedValueTernary(UInt8 a) { return a == Ternary::False; }
2020-07-07 20:56:40 +00:00
static inline constexpr ResultType apply(UInt8 a, UInt8 b) { return a & b; }
2020-07-07 20:56:40 +00:00
static inline constexpr ResultType ternaryApply(UInt8 a, UInt8 b) { return std::min(a, b); }
2020-07-07 20:56:40 +00:00
/// Will use three-valued logic for NULLs (see above) or default implementation (any operation with NULL returns NULL).
2019-06-22 10:49:19 +00:00
static inline constexpr bool specialImplementationForNulls() { return true; }
2011-08-22 01:01:01 +00:00
};
struct OrImpl
{
using ResultType = UInt8;
static inline constexpr bool isSaturable() { return true; }
static inline constexpr bool isSaturatedValue(bool a) { return a; }
static inline constexpr bool isSaturatedValueTernary(UInt8 a) { return a == Ternary::True; }
static inline constexpr ResultType apply(UInt8 a, UInt8 b) { return a | b; }
static inline constexpr ResultType ternaryApply(UInt8 a, UInt8 b) { return std::max(a, b); }
2018-04-30 12:33:40 +00:00
static inline constexpr bool specialImplementationForNulls() { return true; }
};
2011-08-22 01:01:01 +00:00
struct XorImpl
{
using ResultType = UInt8;
static inline constexpr bool isSaturable() { return false; }
static inline constexpr bool isSaturatedValue(bool) { return false; }
static inline constexpr bool isSaturatedValueTernary(UInt8) { return false; }
static inline constexpr ResultType apply(UInt8 a, UInt8 b) { return a != b; }
static inline constexpr ResultType ternaryApply(UInt8 a, UInt8 b) { return a != b; }
2018-04-30 12:33:40 +00:00
static inline constexpr bool specialImplementationForNulls() { return false; }
#if USE_EMBEDDED_COMPILER
static inline llvm::Value * apply(llvm::IRBuilder<> & builder, llvm::Value * a, llvm::Value * b)
{
return builder.CreateXor(a, b);
}
#endif
};
2011-08-22 01:01:01 +00:00
2017-09-15 12:16:12 +00:00
template <typename A>
struct NotImpl
{
using ResultType = UInt8;
2011-08-22 01:01:01 +00:00
static inline ResultType apply(A a)
2011-08-22 01:01:01 +00:00
{
2022-09-10 02:34:56 +00:00
return !static_cast<bool>(a);
2011-08-22 01:01:01 +00:00
}
2018-04-30 12:33:40 +00:00
#if USE_EMBEDDED_COMPILER
static inline llvm::Value * apply(llvm::IRBuilder<> & builder, llvm::Value * a)
{
return builder.CreateNot(a);
}
#endif
2011-08-22 01:01:01 +00:00
};
2017-12-25 05:48:53 +00:00
template <typename Impl, typename Name>
class FunctionAnyArityLogical : public IFunction
2011-08-22 01:01:01 +00:00
{
public:
static constexpr auto name = Name::name;
2021-06-01 12:20:52 +00:00
static FunctionPtr create(ContextPtr) { return std::make_shared<FunctionAnyArityLogical>(); }
String getName() const override
2011-08-22 01:01:01 +00:00
{
return name;
2011-08-22 01:01:01 +00:00
}
2016-12-29 19:38:10 +00:00
bool isVariadic() const override { return true; }
2021-06-22 16:21:23 +00:00
bool isShortCircuit(ShortCircuitSettings & settings, size_t /*number_of_arguments*/) const override
{
2024-01-30 21:26:43 +00:00
settings.arguments_with_disabled_lazy_execution.insert(0);
2021-06-22 16:21:23 +00:00
settings.enable_lazy_execution_for_common_descendants_of_arguments = true;
settings.force_enable_lazy_execution = false;
return name == NameAnd::name || name == NameOr::name;
}
ColumnPtr executeShortCircuit(ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type) const;
2021-06-22 16:21:23 +00:00
bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return false; }
2016-12-29 19:38:10 +00:00
size_t getNumberOfArguments() const override { return 0; }
bool canBeExecutedOnLowCardinalityDictionary() const override { return false; }
bool useDefaultImplementationForNulls() const override { return !Impl::specialImplementationForNulls(); }
2017-05-27 15:45:25 +00:00
/// Get result types by argument types. If the function does not apply to these arguments, throw an exception.
DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override;
2021-06-11 10:02:56 +00:00
ColumnPtr executeImpl(const ColumnsWithTypeAndName & args, const DataTypePtr & result_type, size_t input_rows_count) const override;
2021-05-24 11:25:02 +00:00
ColumnPtr getConstantResultForNonConstArguments(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type) const override;
2021-05-23 22:06:38 +00:00
2018-04-30 12:33:40 +00:00
#if USE_EMBEDDED_COMPILER
2023-06-03 18:31:08 +00:00
bool isCompilableImpl(const DataTypes &, const DataTypePtr &) const override { return useDefaultImplementationForNulls(); }
2018-04-30 12:33:40 +00:00
2023-06-03 18:31:08 +00:00
llvm::Value * compileImpl(llvm::IRBuilderBase & builder, const ValuesWithType & values, const DataTypePtr &) const override
2018-04-30 12:33:40 +00:00
{
2023-06-03 18:31:08 +00:00
assert(!values.empty());
2018-04-30 12:33:40 +00:00
auto & b = static_cast<llvm::IRBuilder<> &>(builder);
if constexpr (!Impl::isSaturable())
{
2023-06-03 18:31:08 +00:00
auto * result = nativeBoolCast(b, values[0]);
for (size_t i = 1; i < values.size(); ++i)
result = Impl::apply(b, result, nativeBoolCast(b, values[i]));
2018-04-30 12:33:40 +00:00
return b.CreateSelect(result, b.getInt8(1), b.getInt8(0));
}
2023-06-03 18:31:08 +00:00
constexpr bool break_on_true = Impl::isSaturatedValue(true);
2018-04-30 12:33:40 +00:00
auto * next = b.GetInsertBlock();
auto * stop = llvm::BasicBlock::Create(next->getContext(), "", next->getParent());
b.SetInsertPoint(stop);
2023-06-03 18:31:08 +00:00
auto * phi = b.CreatePHI(b.getInt8Ty(), static_cast<unsigned>(values.size()));
2023-06-03 18:31:08 +00:00
for (size_t i = 0; i < values.size(); ++i)
2018-04-30 12:33:40 +00:00
{
b.SetInsertPoint(next);
2023-06-03 18:31:08 +00:00
auto * value = values[i].value;
auto * truth = nativeBoolCast(b, values[i]);
if (!values[i].type->equals(DataTypeUInt8{}))
2018-04-30 12:33:40 +00:00
value = b.CreateSelect(truth, b.getInt8(1), b.getInt8(0));
phi->addIncoming(value, b.GetInsertBlock());
2023-06-03 18:31:08 +00:00
if (i + 1 < values.size())
2018-04-30 12:33:40 +00:00
{
next = llvm::BasicBlock::Create(next->getContext(), "", next->getParent());
b.CreateCondBr(truth, break_on_true ? stop : next, break_on_true ? next : stop);
2018-04-30 12:33:40 +00:00
}
}
2023-06-03 18:31:08 +00:00
2018-04-30 12:33:40 +00:00
b.CreateBr(stop);
b.SetInsertPoint(stop);
2023-06-03 18:31:08 +00:00
2018-04-30 12:33:40 +00:00
return phi;
}
#endif
2011-08-22 01:01:01 +00:00
};
template <template <typename> class Impl, typename Name>
class FunctionUnaryLogical : public IFunction
{
public:
static constexpr auto name = Name::name;
2021-06-01 12:20:52 +00:00
static FunctionPtr create(ContextPtr) { return std::make_shared<FunctionUnaryLogical>(); }
2011-08-22 01:01:01 +00:00
String getName() const override
2011-08-22 01:01:01 +00:00
{
return name;
2011-08-22 01:01:01 +00:00
}
2016-12-29 19:38:10 +00:00
size_t getNumberOfArguments() const override { return 1; }
DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override;
bool useDefaultImplementationForConstants() const override { return true; }
2021-06-22 16:21:23 +00:00
bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return false; }
ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t /*input_rows_count*/) const override;
2018-04-30 12:33:40 +00:00
#if USE_EMBEDDED_COMPILER
2023-06-03 18:31:08 +00:00
bool isCompilableImpl(const DataTypes &, const DataTypePtr &) const override { return true; }
2018-04-30 12:33:40 +00:00
2023-06-03 18:31:08 +00:00
llvm::Value * compileImpl(llvm::IRBuilderBase & builder, const ValuesWithType & values, const DataTypePtr &) const override
2018-04-30 12:33:40 +00:00
{
auto & b = static_cast<llvm::IRBuilder<> &>(builder);
2023-06-03 18:31:08 +00:00
return b.CreateSelect(Impl<UInt8>::apply(b, nativeBoolCast(b, values[0])), b.getInt8(1), b.getInt8(0));
2018-04-30 12:33:40 +00:00
}
#endif
2011-08-22 01:01:01 +00:00
};
}
2011-08-22 01:01:01 +00:00
using FunctionAnd = FunctionsLogicalDetail::FunctionAnyArityLogical<FunctionsLogicalDetail::AndImpl, NameAnd>;
using FunctionOr = FunctionsLogicalDetail::FunctionAnyArityLogical<FunctionsLogicalDetail::OrImpl, NameOr>;
using FunctionXor = FunctionsLogicalDetail::FunctionAnyArityLogical<FunctionsLogicalDetail::XorImpl, NameXor>;
using FunctionNot = FunctionsLogicalDetail::FunctionUnaryLogical<FunctionsLogicalDetail::NotImpl, NameNot>;
2011-08-22 01:01:01 +00:00
}