#pragma once #include #include #include #include #include #include #include #include namespace DB { namespace ErrorCodes { extern const int ILLEGAL_TYPE_OF_ARGUMENT; extern const int BAD_ARGUMENTS; } template class FunctionConsistentHashImpl : public IFunction { public: static constexpr auto name = Impl::name; static FunctionPtr create(ContextPtr) { return std::make_shared>(); } String getName() const override { return name; } size_t getNumberOfArguments() const override { return 2; } bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return true; } DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override { if (!isInteger(arguments[0]) && !isIPv4(arguments[0])) throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal type {} of the first argument of function {}", arguments[0]->getName(), getName()); if (arguments[0]->getSizeOfValueInMemory() > sizeof(HashType)) throw Exception(ErrorCodes::BAD_ARGUMENTS, "Function {} accepts {}-bit integers at most, got {}", getName(), sizeof(HashType) * 8, arguments[0]->getName()); if (!isInteger(arguments[1])) throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal type {} of the second argument of function {}", arguments[1]->getName(), getName()); return std::make_shared>(); } bool useDefaultImplementationForConstants() const override { return true; } ColumnNumbers getArgumentsThatAreAlwaysConstant() const override { return {1}; } ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t /*input_rows_count*/) const override { if (isColumnConst(*arguments[1].column)) return executeConstBuckets(arguments); else throw Exception(ErrorCodes::BAD_ARGUMENTS, "The second argument of function {} (number of buckets) must be constant", getName()); } private: using HashType = typename Impl::HashType; using ResultType = typename Impl::ResultType; using BucketsType = typename Impl::BucketsType; template inline BucketsType checkBucketsRange(T buckets) const { if (unlikely(buckets <= 0)) throw Exception(ErrorCodes::BAD_ARGUMENTS, "The second argument of function {} (number of buckets) must be positive number", getName()); if (unlikely(static_cast(buckets) > Impl::max_buckets)) throw Exception(ErrorCodes::BAD_ARGUMENTS, "The value of the second argument of function {} " "(number of buckets) must not be greater than {}", getName(), Impl::max_buckets); return static_cast(buckets); } ColumnPtr executeConstBuckets(const ColumnsWithTypeAndName & arguments) const { Field buckets_field = (*arguments[1].column)[0]; BucketsType num_buckets; if (buckets_field.getType() == Field::Types::Int64) num_buckets = checkBucketsRange(buckets_field.get()); else if (buckets_field.getType() == Field::Types::UInt64) num_buckets = checkBucketsRange(buckets_field.get()); else throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal type {} of the second argument of function {}", buckets_field.getTypeName(), getName()); const auto & hash_col = arguments[0].column; const IDataType * hash_type = arguments[0].type.get(); auto res_col = ColumnVector::create(); WhichDataType which(hash_type); if (which.isUInt8()) executeType(hash_col, num_buckets, res_col.get()); else if (which.isUInt16()) executeType(hash_col, num_buckets, res_col.get()); else if (which.isUInt32()) executeType(hash_col, num_buckets, res_col.get()); else if (which.isUInt64()) executeType(hash_col, num_buckets, res_col.get()); else if (which.isInt8()) executeType(hash_col, num_buckets, res_col.get()); else if (which.isInt16()) executeType(hash_col, num_buckets, res_col.get()); else if (which.isInt32()) executeType(hash_col, num_buckets, res_col.get()); else if (which.isInt64()) executeType(hash_col, num_buckets, res_col.get()); else if (which.isIPv4()) executeType(hash_col, num_buckets, res_col.get()); else throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal type {} of the first argument of function {}", hash_type->getName(), getName()); return res_col; } template void executeType(const ColumnPtr & col_hash_ptr, BucketsType num_buckets, ColumnVector * col_result) const { auto col_hash = checkAndGetColumn>(col_hash_ptr.get()); if (!col_hash) throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal type of the first argument of function {}", getName()); auto & vec_result = col_result->getData(); const auto & vec_hash = col_hash->getData(); size_t size = vec_hash.size(); vec_result.resize(size); for (size_t i = 0; i < size; ++i) vec_result[i] = Impl::apply(static_cast(vec_hash[i]), num_buckets); } }; }