#include #include #include #include #include #include #include #include namespace DB { namespace ErrorCodes { extern const int ILLEGAL_TYPE_OF_ARGUMENT; } /// tupleHammingDistance function: (Tuple(...), Tuple(...))-> N /// Return the number of non-equal tuple elements class FunctionTupleHammingDistance : public ITupleFunction { public: static constexpr auto name = "tupleHammingDistance"; explicit FunctionTupleHammingDistance(ContextPtr context_) : ITupleFunction(context_) {} static FunctionPtr create(ContextPtr context_) { return std::make_shared(context_); } String getName() const override { return name; } size_t getNumberOfArguments() const override { return 2; } bool useDefaultImplementationForConstants() const override { return true; } bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return false; } DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override { const auto * left_tuple = checkAndGetDataType(arguments[0].type.get()); const auto * right_tuple = checkAndGetDataType(arguments[1].type.get()); if (!left_tuple) throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Argument 0 of function {} should be tuple, got {}", getName(), arguments[0].type->getName()); if (!right_tuple) throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Argument 1 of function {} should be tuple, got {}", getName(), arguments[1].type->getName()); const auto & left_types = left_tuple->getElements(); const auto & right_types = right_tuple->getElements(); Columns left_elements; Columns right_elements; if (arguments[0].column) left_elements = getTupleElements(*arguments[0].column); if (arguments[1].column) right_elements = getTupleElements(*arguments[1].column); if (left_types.size() != right_types.size()) throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Expected tuples of the same size as arguments of function {}. Got {} and {}", getName(), arguments[0].type->getName(), arguments[1].type->getName()); size_t tuple_size = left_types.size(); if (tuple_size == 0) return std::make_shared(); auto compare = FunctionFactory::instance().get("notEquals", context); auto plus = FunctionFactory::instance().get("plus", context); DataTypePtr res_type; for (size_t i = 0; i < tuple_size; ++i) { try { ColumnWithTypeAndName left{left_elements.empty() ? nullptr : left_elements[i], left_types[i], {}}; ColumnWithTypeAndName right{right_elements.empty() ? nullptr : right_elements[i], right_types[i], {}}; auto elem_compare = compare->build(ColumnsWithTypeAndName{left, right}); if (i == 0) { res_type = elem_compare->getResultType(); continue; } ColumnWithTypeAndName left_type{res_type, {}}; ColumnWithTypeAndName right_type{elem_compare->getResultType(), {}}; auto plus_elem = plus->build({left_type, right_type}); res_type = plus_elem->getResultType(); } catch (DB::Exception & e) { e.addMessage("While executing function {} for tuple element {}", getName(), i); throw; } } return res_type; } ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t input_rows_count) const override { const auto * left_tuple = checkAndGetDataType(arguments[0].type.get()); const auto * right_tuple = checkAndGetDataType(arguments[1].type.get()); const auto & left_types = left_tuple->getElements(); const auto & right_types = right_tuple->getElements(); auto left_elements = getTupleElements(*arguments[0].column); auto right_elements = getTupleElements(*arguments[1].column); size_t tuple_size = left_elements.size(); if (tuple_size == 0) return DataTypeUInt8().createColumnConstWithDefaultValue(input_rows_count); auto compare = FunctionFactory::instance().get("notEquals", context); auto plus = FunctionFactory::instance().get("plus", context); ColumnWithTypeAndName res; for (size_t i = 0; i < tuple_size; ++i) { ColumnWithTypeAndName left{left_elements[i], left_types[i], {}}; ColumnWithTypeAndName right{right_elements[i], right_types[i], {}}; auto elem_compare = compare->build(ColumnsWithTypeAndName{left, right}); ColumnWithTypeAndName column; column.type = elem_compare->getResultType(); column.column = elem_compare->execute({left, right}, column.type, input_rows_count); if (i == 0) { res = std::move(column); } else { auto plus_elem = plus->build({res, column}); auto res_type = plus_elem->getResultType(); res.column = plus_elem->execute({res, column}, res_type, input_rows_count); res.type = res_type; } } return res.column; } }; REGISTER_FUNCTION(TupleHammingDistance) { factory.registerFunction(); } }