#pragma once #include #include #include #include #include #include #include namespace DB { namespace ErrorCodes { extern const int ILLEGAL_COLUMN; extern const int ILLEGAL_TYPE_OF_ARGUMENT; extern const int PARAMETER_OUT_OF_BOUND; extern const int TOO_FEW_ARGUMENTS_FOR_FUNCTION; } template struct FunctionBitTestMany : public IFunction { public: static constexpr auto name = Name::name; static FunctionPtr create(ContextPtr) { return std::make_shared(); } String getName() const override { return name; } bool isVariadic() const override { return true; } bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return false; } size_t getNumberOfArguments() const override { return 0; } DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override { if (arguments.size() < 2) throw Exception(ErrorCodes::TOO_FEW_ARGUMENTS_FOR_FUNCTION, "Number of arguments for function {} doesn't match: " "passed {}, should be at least 2.", getName(), arguments.size()); const auto & first_arg = arguments.front(); if (!isInteger(first_arg)) throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal type {} of first argument of function {}", first_arg->getName(), getName()); for (size_t i = 1; i < arguments.size(); ++i) { const auto & pos_arg = arguments[i]; if (!isUInt(pos_arg)) throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal type {} of {} argument of function {}", pos_arg->getName(), i, getName()); } return std::make_shared(); } DataTypePtr getReturnTypeForDefaultImplementationForDynamic() const override { return std::make_shared(); } ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, size_t input_rows_count) const override { const auto * value_col = arguments.front().column.get(); ColumnPtr res; if (!((res = execute(arguments, result_type, value_col, input_rows_count)) || (res = execute(arguments, result_type, value_col, input_rows_count)) || (res = execute(arguments, result_type, value_col, input_rows_count)) || (res = execute(arguments, result_type, value_col, input_rows_count)) || (res = execute(arguments, result_type, value_col, input_rows_count)) || (res = execute(arguments, result_type, value_col, input_rows_count)) || (res = execute(arguments, result_type, value_col, input_rows_count)) || (res = execute(arguments, result_type, value_col, input_rows_count)))) throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Illegal column {} of argument of function {}", value_col->getName(), getName()); return res; } private: template ColumnPtr execute( const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, const IColumn * const value_col_untyped, size_t input_rows_count) const { if (const auto value_col = checkAndGetColumn>(value_col_untyped)) { bool is_const; const auto const_mask = createConstMaskIfConst(arguments, is_const); const auto & val = value_col->getData(); auto out_col = ColumnVector::create(input_rows_count); auto & out = out_col->getData(); if (is_const) { for (size_t i = 0; i < input_rows_count; ++i) out[i] = Impl::apply(val[i], const_mask); } else { const auto mask = createMask(input_rows_count, arguments); for (size_t i = 0; i < input_rows_count; ++i) out[i] = Impl::apply(val[i], mask[i]); } return out_col; } if (const auto value_col_const = checkAndGetColumnConst>(value_col_untyped)) { bool is_const; const auto const_mask = createConstMaskIfConst(arguments, is_const); const auto val = value_col_const->template getValue(); if (is_const) { return result_type->createColumnConst(input_rows_count, toField(Impl::apply(val, const_mask))); } const auto mask = createMask(input_rows_count, arguments); auto out_col = ColumnVector::create(input_rows_count); auto & out = out_col->getData(); for (size_t i = 0; i < input_rows_count; ++i) out[i] = Impl::apply(val, mask[i]); return out_col; } return nullptr; } template ValueType createConstMaskIfConst(const ColumnsWithTypeAndName & arguments, bool & out_is_const) const { out_is_const = true; ValueType mask = 0; for (size_t i = 1; i < arguments.size(); ++i) { if (auto pos_col_const = checkAndGetColumnConst>(arguments[i].column.get())) { const auto pos = pos_col_const->getUInt(0); if (pos < 8 * sizeof(ValueType)) mask = mask | (ValueType(1) << pos); else throw Exception(ErrorCodes::PARAMETER_OUT_OF_BOUND, "The bit position argument {} is out of bounds for number", static_cast(pos)); } else { out_is_const = false; return {}; } } return mask; } template PaddedPODArray createMask(const size_t size, const ColumnsWithTypeAndName & arguments) const { PaddedPODArray mask(size, ValueType{}); for (size_t i = 1; i < arguments.size(); ++i) { const auto * pos_col = arguments[i].column.get(); if (!addToMaskImpl(mask, pos_col) && !addToMaskImpl(mask, pos_col) && !addToMaskImpl(mask, pos_col) && !addToMaskImpl(mask, pos_col)) throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Illegal column {} of argument of function {}", pos_col->getName(), getName()); } return mask; } template bool NO_SANITIZE_UNDEFINED addToMaskImpl(PaddedPODArray & mask, const IColumn * const pos_col_untyped) const { if (const auto pos_col = checkAndGetColumn>(pos_col_untyped)) { const auto & pos = pos_col->getData(); for (size_t i = 0; i < mask.size(); ++i) if (pos[i] < 8 * sizeof(ValueType)) mask[i] = mask[i] | (ValueType(1) << pos[i]); else throw Exception(ErrorCodes::PARAMETER_OUT_OF_BOUND, "The bit position argument {} is out of bounds for number", static_cast(pos[i])); return true; } if (const auto pos_col_const = checkAndGetColumnConst>(pos_col_untyped)) { const auto & pos = pos_col_const->template getValue(); if (pos >= 8 * sizeof(ValueType)) throw Exception( ErrorCodes::PARAMETER_OUT_OF_BOUND, "The bit position argument {} is out of bounds for number", static_cast(pos)); const auto new_mask = ValueType(1) << pos; for (size_t i = 0; i < mask.size(); ++i) mask[i] = mask[i] | new_mask; return true; } return false; } }; }