2011-08-22 01:01:01 +00:00
|
|
|
#pragma once
|
|
|
|
|
2019-07-16 20:57:11 +00:00
|
|
|
#include <Core/Types.h>
|
|
|
|
#include <Core/Defines.h>
|
|
|
|
#include <DataTypes/IDataType.h>
|
2019-12-09 13:12:54 +00:00
|
|
|
#include <Functions/IFunctionImpl.h>
|
2019-07-16 20:57:11 +00:00
|
|
|
#include <IO/WriteHelpers.h>
|
2017-07-23 08:40:43 +00:00
|
|
|
#include <type_traits>
|
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
|
2018-09-03 06:31:19 +00:00
|
|
|
#include <DataTypes/Native.h>
|
|
|
|
|
2018-05-06 09:29:57 +00:00
|
|
|
#pragma GCC diagnostic push
|
|
|
|
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
2019-06-05 11:52:39 +00:00
|
|
|
#include <llvm/IR/IRBuilder.h>
|
2018-05-06 09:29:57 +00:00
|
|
|
#pragma GCC diagnostic pop
|
2018-04-30 12:33:40 +00:00
|
|
|
#endif
|
|
|
|
|
2011-08-22 01:01:01 +00:00
|
|
|
|
2019-07-16 20:57:11 +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
|
2017-12-25 06:33:51 +00:00
|
|
|
*
|
2019-07-16 20:57:11 +00:00
|
|
|
* 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
|
2017-12-25 06:33:51 +00:00
|
|
|
*
|
2019-07-16 20:57:11 +00:00
|
|
|
* Functions AND and OR provide their own special implementations for ternary logic
|
2011-08-22 01:01:01 +00:00
|
|
|
*/
|
|
|
|
|
2019-07-16 20:57:11 +00:00
|
|
|
namespace DB
|
2019-06-22 10:49:19 +00:00
|
|
|
{
|
2019-07-16 20:57:11 +00:00
|
|
|
namespace FunctionsLogicalDetail
|
2019-06-22 10:49:19 +00:00
|
|
|
{
|
2019-07-16 20:57:11 +00:00
|
|
|
namespace Ternary
|
2019-06-22 10:49:19 +00:00
|
|
|
{
|
2019-07-16 20:57:11 +00:00
|
|
|
using ResultType = UInt8;
|
2019-06-22 10:49:19 +00:00
|
|
|
|
2019-07-16 20:57:11 +00:00
|
|
|
static constexpr UInt8 False = 0;
|
|
|
|
static constexpr UInt8 True = -1;
|
|
|
|
static constexpr UInt8 Null = 1;
|
2019-06-22 10:49:19 +00:00
|
|
|
|
2019-07-16 20:57:11 +00:00
|
|
|
template <typename T>
|
|
|
|
inline ResultType makeValue(T value)
|
2019-06-22 10:49:19 +00:00
|
|
|
{
|
2019-07-16 20:57:11 +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
|
|
|
|
2019-07-16 20:57:11 +00:00
|
|
|
template <typename T>
|
|
|
|
inline ResultType makeValue(T value, bool is_null)
|
2019-06-22 10:49:19 +00:00
|
|
|
{
|
2019-07-16 20:57:11 +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
|
|
|
|
{
|
2019-07-16 20:57:11 +00:00
|
|
|
using ResultType = UInt8;
|
2017-12-25 06:33:51 +00:00
|
|
|
|
2019-07-16 20:57:11 +00:00
|
|
|
static inline constexpr bool isSaturable() { return true; }
|
|
|
|
static inline constexpr bool isSaturatedValue(UInt8 a) { return a == Ternary::False; }
|
|
|
|
static inline constexpr ResultType apply(UInt8 a, UInt8 b) { return a & b; }
|
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
|
|
|
|
{
|
2019-07-16 20:57:11 +00:00
|
|
|
using ResultType = UInt8;
|
2017-12-25 06:33:51 +00:00
|
|
|
|
2019-07-16 20:57:11 +00:00
|
|
|
static inline constexpr bool isSaturable() { return true; }
|
|
|
|
static inline constexpr bool isSaturatedValue(UInt8 a) { return a == Ternary::True; }
|
|
|
|
static inline constexpr ResultType apply(UInt8 a, UInt8 b) { return a | b; }
|
2018-04-30 12:33:40 +00:00
|
|
|
static inline constexpr bool specialImplementationForNulls() { return true; }
|
2014-02-13 10:24:56 +00:00
|
|
|
};
|
2011-08-22 01:01:01 +00:00
|
|
|
|
2014-02-13 10:24:56 +00:00
|
|
|
struct XorImpl
|
|
|
|
{
|
2019-07-16 20:57:11 +00:00
|
|
|
using ResultType = UInt8;
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2019-07-16 20:57:11 +00:00
|
|
|
static inline constexpr bool isSaturable() { return false; }
|
|
|
|
static inline constexpr bool isSaturatedValue(bool) { return false; }
|
2020-01-18 20:49:12 +00:00
|
|
|
static inline constexpr ResultType apply(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
|
2014-02-13 10:24:56 +00:00
|
|
|
};
|
2011-08-22 01:01:01 +00:00
|
|
|
|
2017-09-15 12:16:12 +00:00
|
|
|
template <typename A>
|
2014-02-13 10:24:56 +00:00
|
|
|
struct NotImpl
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
using ResultType = UInt8;
|
2011-08-22 01:01:01 +00:00
|
|
|
|
2019-07-16 20:57:11 +00:00
|
|
|
static inline ResultType apply(A a)
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
|
|
|
return !a;
|
|
|
|
}
|
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>
|
2014-02-13 10:24:56 +00:00
|
|
|
class FunctionAnyArityLogical : public IFunction
|
2011-08-22 01:01:01 +00:00
|
|
|
{
|
2014-11-12 17:23:26 +00:00
|
|
|
public:
|
2017-04-01 07:20:54 +00:00
|
|
|
static constexpr auto name = Name::name;
|
2018-06-03 20:39:06 +00:00
|
|
|
static FunctionPtr create(const Context &) { return std::make_shared<FunctionAnyArityLogical>(); }
|
2014-11-12 17:23:26 +00:00
|
|
|
|
2011-08-22 01:01:01 +00:00
|
|
|
public:
|
2017-04-01 07:20:54 +00:00
|
|
|
String getName() const override
|
|
|
|
{
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isVariadic() const override { return true; }
|
|
|
|
size_t getNumberOfArguments() const override { return 0; }
|
|
|
|
|
2017-12-25 06:33:51 +00:00
|
|
|
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.
|
2019-07-16 20:57:11 +00:00
|
|
|
DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override;
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2019-07-16 20:57:11 +00:00
|
|
|
void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result_index, size_t input_rows_count) override;
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2018-04-30 12:33:40 +00:00
|
|
|
#if USE_EMBEDDED_COMPILER
|
2019-07-16 20:57:11 +00:00
|
|
|
bool isCompilableImpl(const DataTypes &) const override { return useDefaultImplementationForNulls(); }
|
2018-04-30 12:33:40 +00:00
|
|
|
|
|
|
|
llvm::Value * compileImpl(llvm::IRBuilderBase & builder, const DataTypes & types, ValuePlaceholders values) const override
|
|
|
|
{
|
|
|
|
auto & b = static_cast<llvm::IRBuilder<> &>(builder);
|
|
|
|
if constexpr (!Impl::isSaturable())
|
|
|
|
{
|
2018-05-07 19:21:23 +00:00
|
|
|
auto * result = nativeBoolCast(b, types[0], values[0]());
|
2018-04-30 12:33:40 +00:00
|
|
|
for (size_t i = 1; i < types.size(); i++)
|
2018-05-07 19:21:23 +00:00
|
|
|
result = Impl::apply(b, result, nativeBoolCast(b, types[i], values[i]()));
|
2018-04-30 12:33:40 +00:00
|
|
|
return b.CreateSelect(result, b.getInt8(1), b.getInt8(0));
|
|
|
|
}
|
|
|
|
constexpr bool breakOnTrue = Impl::isSaturatedValue(true);
|
|
|
|
auto * next = b.GetInsertBlock();
|
|
|
|
auto * stop = llvm::BasicBlock::Create(next->getContext(), "", next->getParent());
|
|
|
|
b.SetInsertPoint(stop);
|
|
|
|
auto * phi = b.CreatePHI(b.getInt8Ty(), values.size());
|
|
|
|
for (size_t i = 0; i < types.size(); i++)
|
|
|
|
{
|
|
|
|
b.SetInsertPoint(next);
|
|
|
|
auto * value = values[i]();
|
2018-05-07 19:21:23 +00:00
|
|
|
auto * truth = nativeBoolCast(b, types[i], value);
|
2018-04-30 12:33:40 +00:00
|
|
|
if (!types[i]->equals(DataTypeUInt8{}))
|
|
|
|
value = b.CreateSelect(truth, b.getInt8(1), b.getInt8(0));
|
|
|
|
phi->addIncoming(value, b.GetInsertBlock());
|
|
|
|
if (i + 1 < types.size())
|
|
|
|
{
|
|
|
|
next = llvm::BasicBlock::Create(next->getContext(), "", next->getParent());
|
|
|
|
b.CreateCondBr(truth, breakOnTrue ? stop : next, breakOnTrue ? next : stop);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
b.CreateBr(stop);
|
|
|
|
b.SetInsertPoint(stop);
|
|
|
|
return phi;
|
|
|
|
}
|
|
|
|
#endif
|
2011-08-22 01:01:01 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
template <template <typename> class Impl, typename Name>
|
|
|
|
class FunctionUnaryLogical : public IFunction
|
|
|
|
{
|
2014-11-12 17:23:26 +00:00
|
|
|
public:
|
2017-04-01 07:20:54 +00:00
|
|
|
static constexpr auto name = Name::name;
|
2018-06-03 20:39:06 +00:00
|
|
|
static FunctionPtr create(const Context &) { return std::make_shared<FunctionUnaryLogical>(); }
|
2011-08-22 01:01:01 +00:00
|
|
|
|
|
|
|
public:
|
2017-04-01 07:20:54 +00:00
|
|
|
String getName() const override
|
|
|
|
{
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t getNumberOfArguments() const override { return 1; }
|
|
|
|
|
2019-07-16 20:57:11 +00:00
|
|
|
DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override;
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2017-07-23 08:40:43 +00:00
|
|
|
bool useDefaultImplementationForConstants() const override { return true; }
|
|
|
|
|
2019-07-16 20:57:11 +00:00
|
|
|
void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t /*input_rows_count*/) override;
|
2018-04-30 12:33:40 +00:00
|
|
|
|
|
|
|
#if USE_EMBEDDED_COMPILER
|
|
|
|
bool isCompilableImpl(const DataTypes &) const override { return true; }
|
|
|
|
|
|
|
|
llvm::Value * compileImpl(llvm::IRBuilderBase & builder, const DataTypes & types, ValuePlaceholders values) const override
|
|
|
|
{
|
|
|
|
auto & b = static_cast<llvm::IRBuilder<> &>(builder);
|
2018-05-07 19:21:23 +00:00
|
|
|
return b.CreateSelect(Impl<UInt8>::apply(b, nativeBoolCast(b, types[0], 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
|
|
|
};
|
|
|
|
|
2019-07-16 20:57:11 +00:00
|
|
|
}
|
2011-08-22 01:01:01 +00:00
|
|
|
|
2017-07-23 08:40:43 +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"; };
|
2011-08-22 01:01:01 +00:00
|
|
|
|
2019-07-16 20:57:11 +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
|
|
|
|
|
|
|
}
|