#pragma once #include #include #include #include #include #include #include #include #include namespace DB { namespace ErrorCodes { extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; } /** Return an UInt8 containing 0 or 1. */ struct AndImpl { static inline bool isSaturable() { return true; } static inline bool isSaturatedValue(UInt8 a) { return !a; } static inline UInt8 apply(UInt8 a, UInt8 b) { return a && b; } }; struct OrImpl { static inline bool isSaturable() { return true; } static inline bool isSaturatedValue(UInt8 a) { return a; } static inline UInt8 apply(UInt8 a, UInt8 b) { return a || b; } }; struct XorImpl { static inline bool isSaturable() { return false; } static inline bool isSaturatedValue(UInt8) { return false; } static inline UInt8 apply(UInt8 a, UInt8 b) { return (!a) != (!b); } }; template struct NotImpl { using ResultType = UInt8; static inline UInt8 apply(A a) { return !a; } }; using UInt8Container = ColumnUInt8::Container; using UInt8ColumnPtrs = std::vector; template struct AssociativeOperationImpl { /// Erases the N last columns from `in` (if there are less, then all) and puts into `result` their combination. static void NO_INLINE execute(UInt8ColumnPtrs & in, UInt8Container & result) { if (N > in.size()) { AssociativeOperationImpl::execute(in, result); return; } AssociativeOperationImpl operation(in); in.erase(in.end() - N, in.end()); size_t n = result.size(); for (size_t i = 0; i < n; ++i) { result[i] = operation.apply(i); } } const UInt8Container & vec; AssociativeOperationImpl continuation; /// Remembers the last N columns from `in`. AssociativeOperationImpl(UInt8ColumnPtrs & in) : vec(in[in.size() - N]->getData()), continuation(in) {} /// Returns a combination of values in the i-th row of all columns stored in the constructor. inline UInt8 apply(size_t i) const { if (Op::isSaturable()) { UInt8 a = vec[i]; return Op::isSaturatedValue(a) ? a : continuation.apply(i); } else { return Op::apply(vec[i], continuation.apply(i)); } } }; template struct AssociativeOperationImpl { static void execute(UInt8ColumnPtrs &, UInt8Container &) { throw Exception("Logical error: AssociativeOperationImpl::execute called", ErrorCodes::LOGICAL_ERROR); } const UInt8Container & vec; AssociativeOperationImpl(UInt8ColumnPtrs & in) : vec(in[in.size() - 1]->getData()) {} inline UInt8 apply(size_t i) const { return vec[i]; } }; template class FunctionAnyArityLogical : public IFunction { public: static constexpr auto name = Name::name; static FunctionPtr create(const Context &) { return std::make_shared(); }; private: bool extractConstColumns(ColumnRawPtrs & in, UInt8 & res) { bool has_res = false; for (int i = static_cast(in.size()) - 1; i >= 0; --i) { if (in[i]->isColumnConst()) { UInt8 x = !!in[i]->getUInt(0); if (has_res) { res = Impl::apply(res, x); } else { res = x; has_res = true; } in.erase(in.begin() + i); } } return has_res; } template bool convertTypeToUInt8(const IColumn * column, UInt8Container & res) { auto col = checkAndGetColumn>(column); if (!col) return false; const typename ColumnVector::Container & vec = col->getData(); size_t n = res.size(); for (size_t i = 0; i < n; ++i) res[i] = !!vec[i]; return true; } void convertToUInt8(const IColumn * column, UInt8Container & res) { if (!convertTypeToUInt8(column, res) && !convertTypeToUInt8(column, res) && !convertTypeToUInt8(column, res) && !convertTypeToUInt8(column, res) && !convertTypeToUInt8(column, res) && !convertTypeToUInt8(column, res) && !convertTypeToUInt8(column, res) && !convertTypeToUInt8(column, res) && !convertTypeToUInt8(column, res)) throw Exception("Unexpected type of column: " + column->getName(), ErrorCodes::ILLEGAL_COLUMN); } public: String getName() const override { return name; } bool isVariadic() const override { return true; } size_t getNumberOfArguments() const override { return 0; } /// 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 { if (arguments.size() < 2) throw Exception("Number of arguments for function " + getName() + " doesn't match: passed " + toString(arguments.size()) + ", should be at least 2.", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); for (size_t i = 0; i < arguments.size(); ++i) if (!arguments[i]->isNumber()) throw Exception("Illegal type (" + arguments[i]->getName() + ") of " + toString(i + 1) + " argument of function " + getName(), ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); return std::make_shared(); } void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result) override { size_t num_arguments = arguments.size(); ColumnRawPtrs in(num_arguments); for (size_t i = 0; i < num_arguments; ++i) in[i] = block.getByPosition(arguments[i]).column.get(); size_t rows = in[0]->size(); /// Combine all constant columns into a single value. UInt8 const_val = 0; bool has_consts = extractConstColumns(in, const_val); // If this value uniquely determines the result, return it. if (has_consts && (in.empty() || Impl::apply(const_val, 0) == Impl::apply(const_val, 1))) { if (!in.empty()) const_val = Impl::apply(const_val, 0); block.getByPosition(result).column = DataTypeUInt8().createColumnConst(rows, toField(const_val)); return; } /// If this value is a neutral element, let's forget about it. if (has_consts && Impl::apply(const_val, 0) == 0 && Impl::apply(const_val, 1) == 1) has_consts = false; auto col_res = ColumnUInt8::create(); UInt8Container & vec_res = col_res->getData(); if (has_consts) { vec_res.assign(rows, const_val); in.push_back(col_res.get()); } else { vec_res.resize(rows); } /// Convert all columns to UInt8 UInt8ColumnPtrs uint8_in; Columns converted_columns; for (const IColumn * column : in) { if (auto uint8_column = checkAndGetColumn(column)) uint8_in.push_back(uint8_column); else { auto converted_column = ColumnUInt8::create(rows); convertToUInt8(column, converted_column->getData()); uint8_in.push_back(converted_column.get()); converted_columns.emplace_back(std::move(converted_column)); } } /// Effeciently combine all the columns of the correct type. while (uint8_in.size() > 1) { /// With a large block size, combining 6 columns per pass is the fastest. /// When small - more, is faster. AssociativeOperationImpl::execute(uint8_in, vec_res); uint8_in.push_back(col_res.get()); } /// This is possible if there is exactly one non-constant among the arguments, and it is of type UInt8. if (uint8_in[0] != col_res.get()) vec_res.assign(uint8_in[0]->getData()); block.getByPosition(result).column = std::move(col_res); } }; template