diff options
| author | vitalyisaev <[email protected]> | 2023-11-14 09:58:56 +0300 |
|---|---|---|
| committer | vitalyisaev <[email protected]> | 2023-11-14 10:20:20 +0300 |
| commit | c2b2dfd9827a400a8495e172a56343462e3ceb82 (patch) | |
| tree | cd4e4f597d01bede4c82dffeb2d780d0a9046bd0 /contrib/clickhouse/src/Functions/FunctionsCodingIP.cpp | |
| parent | d4ae8f119e67808cb0cf776ba6e0cf95296f2df7 (diff) | |
YQ Connector: move tests from yql to ydb (OSS)
Перенос папки с тестами на Коннектор из папки yql в папку ydb (синхронизируется с github).
Diffstat (limited to 'contrib/clickhouse/src/Functions/FunctionsCodingIP.cpp')
| -rw-r--r-- | contrib/clickhouse/src/Functions/FunctionsCodingIP.cpp | 1177 |
1 files changed, 1177 insertions, 0 deletions
diff --git a/contrib/clickhouse/src/Functions/FunctionsCodingIP.cpp b/contrib/clickhouse/src/Functions/FunctionsCodingIP.cpp new file mode 100644 index 00000000000..7bdbac6531d --- /dev/null +++ b/contrib/clickhouse/src/Functions/FunctionsCodingIP.cpp @@ -0,0 +1,1177 @@ +#include <functional> +#pragma clang diagnostic ignored "-Wreserved-identifier" + +#include <Functions/FunctionsCodingIP.h> + +#include <Columns/ColumnArray.h> +#include <Columns/ColumnConst.h> +#include <Columns/ColumnFixedString.h> +#include <Columns/ColumnString.h> +#include <Columns/ColumnTuple.h> +#include <Columns/ColumnsNumber.h> +#include <Columns/ColumnNullable.h> +#include <DataTypes/DataTypeDate.h> +#include <DataTypes/DataTypeFactory.h> +#include <DataTypes/DataTypeFixedString.h> +#include <DataTypes/DataTypeString.h> +#include <DataTypes/DataTypeTuple.h> +#include <DataTypes/DataTypesNumber.h> +#include <DataTypes/DataTypeIPv4andIPv6.h> +#include <Functions/FunctionFactory.h> +#include <Functions/FunctionHelpers.h> +#include <Functions/IFunction.h> +#include <Interpreters/Context.h> +#include <IO/WriteHelpers.h> +#include <Common/IPv6ToBinary.h> +#include <Common/formatIPv6.h> +#include <base/hex.h> +#include <Common/typeid_cast.h> + +#include <arpa/inet.h> +#include <type_traits> +#include <array> + + +namespace DB +{ + +namespace ErrorCodes +{ + extern const int ILLEGAL_TYPE_OF_ARGUMENT; + extern const int ILLEGAL_COLUMN; +} + + +/** Encoding functions for network addresses: + * + * IPv6NumToString (num) - See below. + * IPv6StringToNum(string) - Convert, for example, '::1' to 1 and vice versa. + */ +class FunctionIPv6NumToString : public IFunction +{ +public: + static constexpr auto name = "IPv6NumToString"; + static FunctionPtr create(ContextPtr) { return std::make_shared<FunctionIPv6NumToString>(); } + + String getName() const override { return name; } + + size_t getNumberOfArguments() const override { return 1; } + bool isInjective(const ColumnsWithTypeAndName &) const override { return true; } + bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return false; } + + DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override + { + const auto * arg_string = checkAndGetDataType<DataTypeFixedString>(arguments[0].get()); + const auto * arg_ipv6 = checkAndGetDataType<DataTypeIPv6>(arguments[0].get()); + if (!arg_ipv6 && !(arg_string && arg_string->getN() == IPV6_BINARY_LENGTH)) + throw Exception( + ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, + "Illegal type {} of argument of function {}, expected IPv6 or FixedString({})", + arguments[0]->getName(), getName(), IPV6_BINARY_LENGTH + ); + + return std::make_shared<DataTypeString>(); + } + + bool useDefaultImplementationForConstants() const override { return true; } + + ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t input_rows_count) const override + { + const ColumnPtr & column = arguments[0].column; + const auto * col_ipv6 = checkAndGetColumn<ColumnIPv6>(column.get()); + const auto * col_string = checkAndGetColumn<ColumnFixedString>(column.get()); + if (!col_ipv6 && !(col_string && col_string->getN() == IPV6_BINARY_LENGTH)) + throw Exception( + ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, + "Illegal column {} of argument of function {}, expected IPv6 or FixedString({})", + arguments[0].name, getName(), IPV6_BINARY_LENGTH + ); + + auto col_res = ColumnString::create(); + ColumnString::Chars & vec_res = col_res->getChars(); + ColumnString::Offsets & offsets_res = col_res->getOffsets(); + vec_res.resize(input_rows_count * (IPV6_MAX_TEXT_LENGTH + 1)); + offsets_res.resize(input_rows_count); + + auto * begin = reinterpret_cast<char *>(vec_res.data()); + auto * pos = begin; + + if (col_ipv6) + { + const auto & vec_in = col_ipv6->getData(); + + for (size_t i = 0; i < input_rows_count; ++i) + { + formatIPv6(reinterpret_cast<const unsigned char *>(&vec_in[i]), pos); + offsets_res[i] = pos - begin; + } + } + else + { + const auto & vec_in = col_string->getChars(); + + for (size_t i = 0; i < input_rows_count; ++i) + { + formatIPv6(reinterpret_cast<const unsigned char *>(&vec_in[i * IPV6_BINARY_LENGTH]), pos); + offsets_res[i] = pos - begin; + } + } + + vec_res.resize(pos - begin); + return col_res; + } +}; + + +class FunctionCutIPv6 : public IFunction +{ +public: + static constexpr auto name = "cutIPv6"; + static FunctionPtr create(ContextPtr) { return std::make_shared<FunctionCutIPv6>(); } + + String getName() const override { return name; } + + size_t getNumberOfArguments() const override { return 3; } + + bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return false; } + + DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override + { + if (!checkAndGetDataType<DataTypeIPv6>(arguments[0].get())) + { + const auto * ptr = checkAndGetDataType<DataTypeFixedString>(arguments[0].get()); + if (!ptr || ptr->getN() != IPV6_BINARY_LENGTH) + throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, + "Illegal type {} of argument 1 of function {}, expected FixedString({})", + arguments[0]->getName(), getName(), toString(IPV6_BINARY_LENGTH)); + } + + if (!WhichDataType(arguments[1]).isUInt8()) + throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal type {} of argument 2 of function {}", + arguments[1]->getName(), getName()); + + if (!WhichDataType(arguments[2]).isUInt8()) + throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal type {} of argument 3 of function {}", + arguments[2]->getName(), getName()); + + return std::make_shared<DataTypeString>(); + } + + bool useDefaultImplementationForConstants() const override { return true; } + ColumnNumbers getArgumentsThatAreAlwaysConstant() const override { return {1, 2}; } + + ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t input_rows_count) const override + { + const auto & col_type_name = arguments[0]; + const ColumnPtr & column = col_type_name.column; + + const auto & col_ipv6_zeroed_tail_bytes_type = arguments[1]; + const auto & col_ipv6_zeroed_tail_bytes = col_ipv6_zeroed_tail_bytes_type.column; + const auto & col_ipv4_zeroed_tail_bytes_type = arguments[2]; + const auto & col_ipv4_zeroed_tail_bytes = col_ipv4_zeroed_tail_bytes_type.column; + + const auto * col_in_str = checkAndGetColumn<ColumnFixedString>(column.get()); + const auto * col_in_ip = checkAndGetColumn<ColumnIPv6>(column.get()); + + if (!col_in_str && !col_in_ip) + throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Illegal column {} of argument of function {}", + arguments[0].column->getName(), getName()); + + if (col_in_str && col_in_str->getN() != IPV6_BINARY_LENGTH) + throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, + "Illegal type {} of column {} argument of function {}, expected FixedString({})", + col_type_name.type->getName(), col_in_str->getName(), + getName(), toString(IPV6_BINARY_LENGTH)); + + const auto * ipv6_zeroed_tail_bytes = checkAndGetColumnConst<ColumnVector<UInt8>>(col_ipv6_zeroed_tail_bytes.get()); + if (!ipv6_zeroed_tail_bytes) + throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal type {} of argument 2 of function {}", + col_ipv6_zeroed_tail_bytes_type.type->getName(), getName()); + + UInt8 ipv6_zeroed_tail_bytes_count = ipv6_zeroed_tail_bytes->getValue<UInt8>(); + if (ipv6_zeroed_tail_bytes_count > IPV6_BINARY_LENGTH) + throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal value for argument 2 {} of function {}", + col_ipv6_zeroed_tail_bytes_type.type->getName(), getName()); + + const auto * ipv4_zeroed_tail_bytes = checkAndGetColumnConst<ColumnVector<UInt8>>(col_ipv4_zeroed_tail_bytes.get()); + if (!ipv4_zeroed_tail_bytes) + throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal type {} of argument 3 of function {}", + col_ipv4_zeroed_tail_bytes_type.type->getName(), getName()); + + UInt8 ipv4_zeroed_tail_bytes_count = ipv4_zeroed_tail_bytes->getValue<UInt8>(); + if (ipv4_zeroed_tail_bytes_count > IPV6_BINARY_LENGTH) + throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal value for argument 3 {} of function {}", + col_ipv4_zeroed_tail_bytes_type.type->getName(), getName()); + + auto col_res = ColumnString::create(); + ColumnString::Chars & vec_res = col_res->getChars(); + ColumnString::Offsets & offsets_res = col_res->getOffsets(); + vec_res.resize(input_rows_count * (IPV6_MAX_TEXT_LENGTH + 1)); + offsets_res.resize(input_rows_count); + + auto * begin = reinterpret_cast<char *>(vec_res.data()); + auto * pos = begin; + + if (col_in_str) + { + const auto & vec_in = col_in_str->getChars(); + + for (size_t offset = 0, i = 0; i < input_rows_count; offset += IPV6_BINARY_LENGTH, ++i) + { + const auto * address = &vec_in[offset]; + UInt8 zeroed_tail_bytes_count = isIPv4Mapped(address) ? ipv4_zeroed_tail_bytes_count : ipv6_zeroed_tail_bytes_count; + cutAddress(reinterpret_cast<const unsigned char *>(address), pos, zeroed_tail_bytes_count); + offsets_res[i] = pos - begin; + } + } + else + { + const auto & vec_in = col_in_ip->getData(); + + for (size_t i = 0; i < input_rows_count; ++i) + { + const auto * address = reinterpret_cast<const UInt8 *>(&vec_in[i]); + UInt8 zeroed_tail_bytes_count = isIPv4Mapped(address) ? ipv4_zeroed_tail_bytes_count : ipv6_zeroed_tail_bytes_count; + cutAddress(reinterpret_cast<const unsigned char *>(address), pos, zeroed_tail_bytes_count); + offsets_res[i] = pos - begin; + } + } + + vec_res.resize(pos - begin); + + return col_res; + } + +private: + static bool isIPv4Mapped(const UInt8 * address) + { + return (unalignedLoadLittleEndian<UInt64>(address) == 0) && + ((unalignedLoadLittleEndian<UInt64>(address + 8) & 0x00000000FFFFFFFFull) == 0x00000000FFFF0000ull); + } + + static void cutAddress(const unsigned char * address, char *& dst, UInt8 zeroed_tail_bytes_count) + { + formatIPv6(address, dst, zeroed_tail_bytes_count); + } +}; + +template <IPStringToNumExceptionMode exception_mode> +class FunctionIPv6StringToNum : public IFunction +{ +public: + static constexpr auto name = exception_mode == IPStringToNumExceptionMode::Throw + ? "IPv6StringToNum" + : (exception_mode == IPStringToNumExceptionMode::Default ? "IPv6StringToNumOrDefault" : "IPv6StringToNumOrNull"); + + static FunctionPtr create(ContextPtr context) { return std::make_shared<FunctionIPv6StringToNum>(context); } + + explicit FunctionIPv6StringToNum(ContextPtr context) + : cast_ipv4_ipv6_default_on_conversion_error(context->getSettingsRef().cast_ipv4_ipv6_default_on_conversion_error) + { + } + + String getName() const override { return name; } + + size_t getNumberOfArguments() const override { return 1; } + + bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return false; } + + bool useDefaultImplementationForConstants() const override { return true; } + + bool useDefaultImplementationForNulls() const override { return false; } + + DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override + { + if (!isStringOrFixedString(removeNullable(arguments[0]))) + { + throw Exception( + ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal type {} of argument of function {}", arguments[0]->getName(), getName()); + } + + auto result_type = std::make_shared<DataTypeFixedString>(IPV6_BINARY_LENGTH); + + if constexpr (exception_mode == IPStringToNumExceptionMode::Null) + { + return makeNullable(result_type); + } + + return arguments[0]->isNullable() ? makeNullable(result_type) : result_type; + } + + ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t /*input_rows_count*/) const override + { + ColumnPtr column = arguments[0].column; + ColumnPtr null_map_column; + const NullMap * null_map = nullptr; + if (column->isNullable()) + { + const auto * column_nullable = assert_cast<const ColumnNullable *>(column.get()); + column = column_nullable->getNestedColumnPtr(); + null_map_column = column_nullable->getNullMapColumnPtr(); + null_map = &column_nullable->getNullMapData(); + } + + if constexpr (exception_mode == IPStringToNumExceptionMode::Throw) + { + if (cast_ipv4_ipv6_default_on_conversion_error) + { + auto result = convertToIPv6<IPStringToNumExceptionMode::Default, ColumnFixedString>(column, null_map); + if (null_map && !result->isNullable()) + return ColumnNullable::create(result, null_map_column); + return result; + } + } + + auto result = convertToIPv6<exception_mode, ColumnFixedString>(column, null_map); + if (null_map && !result->isNullable()) + return ColumnNullable::create(IColumn::mutate(result), IColumn::mutate(null_map_column)); + return result; + } + +private: + bool cast_ipv4_ipv6_default_on_conversion_error = false; +}; + + +/** If mask_tail_octets > 0, the last specified number of octets will be filled with "xxx". + */ +template <size_t mask_tail_octets, typename Name> +class FunctionIPv4NumToString : public IFunction +{ +private: + template <typename ArgType> + ColumnPtr executeTyped(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t /*input_rows_count*/) const + { + using ColumnType = ColumnVector<ArgType>; + + const ColumnPtr & column = arguments[0].column; + + if (const ColumnType * col = typeid_cast<const ColumnType *>(column.get())) + { + const typename ColumnType::Container & vec_in = col->getData(); + + auto col_res = ColumnString::create(); + + ColumnString::Chars & vec_res = col_res->getChars(); + ColumnString::Offsets & offsets_res = col_res->getOffsets(); + + vec_res.resize(vec_in.size() * (IPV4_MAX_TEXT_LENGTH + 1)); /// the longest value is: 255.255.255.255\0 + offsets_res.resize(vec_in.size()); + char * begin = reinterpret_cast<char *>(vec_res.data()); + char * pos = begin; + + for (size_t i = 0; i < vec_in.size(); ++i) + { + DB::formatIPv4(reinterpret_cast<const unsigned char*>(&vec_in[i]), sizeof(ArgType), pos, mask_tail_octets, "xxx"); + offsets_res[i] = pos - begin; + } + + vec_res.resize(pos - begin); + + return col_res; + } + else + throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Illegal column {} of argument of function {}", + arguments[0].column->getName(), getName()); + } +public: + static constexpr auto name = Name::name; + static FunctionPtr create(ContextPtr) { return std::make_shared<FunctionIPv4NumToString<mask_tail_octets, Name>>(); } + + String getName() const override + { + return name; + } + + size_t getNumberOfArguments() const override { return 1; } + bool isInjective(const ColumnsWithTypeAndName &) const override { return mask_tail_octets == 0; } + bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return false; } + + DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override + { + WhichDataType arg_type(arguments[0]); + if (!(arg_type.isIPv4() || arg_type.isUInt8() || arg_type.isUInt16() || arg_type.isUInt32())) + throw Exception( + ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, + "Illegal type {} of first argument of function {}, expected IPv4 or UInt8 or UInt16 or UInt32", + arguments[0]->getName(), getName() + ); + + return std::make_shared<DataTypeString>(); + } + + bool useDefaultImplementationForConstants() const override { return true; } + + ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr & ret_type, size_t input_rows_count) const override + { + + switch (arguments[0].type->getTypeId()) + { + case TypeIndex::IPv4: return executeTyped<IPv4>(arguments, ret_type, input_rows_count); + case TypeIndex::UInt8: return executeTyped<UInt8>(arguments, ret_type, input_rows_count); + case TypeIndex::UInt16: return executeTyped<UInt16>(arguments, ret_type, input_rows_count); + case TypeIndex::UInt32: return executeTyped<UInt32>(arguments, ret_type, input_rows_count); + default: break; + } + + throw Exception( + ErrorCodes::ILLEGAL_COLUMN, + "Illegal column {} of argument of function {}, expected IPv4 or UInt8 or UInt16 or UInt32", + arguments[0].column->getName(), getName() + ); + } +}; + +template <IPStringToNumExceptionMode exception_mode> +class FunctionIPv4StringToNum : public IFunction +{ +public: + static constexpr auto name = exception_mode == IPStringToNumExceptionMode::Throw + ? "IPv4StringToNum" + : (exception_mode == IPStringToNumExceptionMode::Default ? "IPv4StringToNumOrDefault" : "IPv4StringToNumOrNull"); + + static FunctionPtr create(ContextPtr context) { return std::make_shared<FunctionIPv4StringToNum>(context); } + + explicit FunctionIPv4StringToNum(ContextPtr context) + : cast_ipv4_ipv6_default_on_conversion_error(context->getSettingsRef().cast_ipv4_ipv6_default_on_conversion_error) + { + } + + String getName() const override { return name; } + + size_t getNumberOfArguments() const override { return 1; } + + bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return false; } + + bool useDefaultImplementationForConstants() const override { return true; } + + bool useDefaultImplementationForNulls() const override { return false; } + + DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override + { + if (!isString(removeNullable(arguments[0]))) + { + throw Exception( + ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal type {} of argument of function {}", arguments[0]->getName(), getName()); + } + + auto result_type = std::make_shared<DataTypeUInt32>(); + + if constexpr (exception_mode == IPStringToNumExceptionMode::Null) + { + return makeNullable(result_type); + } + + return arguments[0]->isNullable() ? makeNullable(result_type) : result_type; + } + + ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t /*input_rows_count*/) const override + { + ColumnPtr column = arguments[0].column; + ColumnPtr null_map_column; + const NullMap * null_map = nullptr; + if (column->isNullable()) + { + const auto * column_nullable = assert_cast<const ColumnNullable *>(column.get()); + column = column_nullable->getNestedColumnPtr(); + null_map_column = column_nullable->getNullMapColumnPtr(); + null_map = &column_nullable->getNullMapData(); + } + + if constexpr (exception_mode == IPStringToNumExceptionMode::Throw) + { + if (cast_ipv4_ipv6_default_on_conversion_error) + { + auto result = convertToIPv4<IPStringToNumExceptionMode::Default, ColumnUInt32>(column, null_map); + if (null_map && !result->isNullable()) + return ColumnNullable::create(result, null_map_column); + return result; + } + } + + auto result = convertToIPv4<exception_mode, ColumnUInt32>(column, null_map); + if (null_map && !result->isNullable()) + return ColumnNullable::create(IColumn::mutate(result), IColumn::mutate(null_map_column)); + return result; + } + +private: + bool cast_ipv4_ipv6_default_on_conversion_error = false; +}; + + +class FunctionIPv4ToIPv6 : public IFunction +{ +public: + static constexpr auto name = "IPv4ToIPv6"; + static FunctionPtr create(ContextPtr) { return std::make_shared<FunctionIPv4ToIPv6>(); } + + String getName() const override { return name; } + + size_t getNumberOfArguments() const override { return 1; } + bool isInjective(const ColumnsWithTypeAndName &) const override { return true; } + bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return false; } + + /// for backward compatibility IPv4ToIPv6 is overloaded, and result type depends on type of argument - + /// if it is UInt32 (presenting IPv4) then result is FixedString(16), if IPv4 - result is IPv6 + DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override + { + const auto * dt_uint32 = checkAndGetDataType<DataTypeUInt32>(arguments[0].get()); + const auto * dt_ipv4 = checkAndGetDataType<DataTypeIPv4>(arguments[0].get()); + if (!dt_uint32 && !dt_ipv4) + throw Exception( + ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, + "Illegal type {} of argument of function {}", arguments[0]->getName(), getName() + ); + + if (dt_uint32) + return std::make_shared<DataTypeFixedString>(16); + return std::make_shared<DataTypeIPv6>(); + } + + bool useDefaultImplementationForConstants() const override { return true; } + + ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t /*input_rows_count*/) const override + { + const auto & col_type_name = arguments[0]; + const ColumnPtr & column = col_type_name.column; + + if (const auto * col_in = checkAndGetColumn<ColumnIPv4>(*column)) + { + auto col_res = ColumnIPv6::create(); + + auto & vec_res = col_res->getData(); + vec_res.resize(col_in->size()); + + const auto & vec_in = col_in->getData(); + + for (size_t i = 0; i < vec_res.size(); ++i) + mapIPv4ToIPv6(vec_in[i], reinterpret_cast<UInt8 *>(&vec_res[i].toUnderType())); + + return col_res; + } + + if (const auto * col_in = checkAndGetColumn<ColumnUInt32>(*column)) + { + auto col_res = ColumnFixedString::create(IPV6_BINARY_LENGTH); + + auto & vec_res = col_res->getChars(); + vec_res.resize(col_in->size() * IPV6_BINARY_LENGTH); + + const auto & vec_in = col_in->getData(); + + for (size_t out_offset = 0, i = 0; out_offset < vec_res.size(); out_offset += IPV6_BINARY_LENGTH, ++i) + mapIPv4ToIPv6(vec_in[i], &vec_res[out_offset]); + + return col_res; + } + + throw Exception( + ErrorCodes::ILLEGAL_COLUMN, + "Illegal column {} of argument of function {}", arguments[0].column->getName(), getName() + ); + } + +private: + static void mapIPv4ToIPv6(UInt32 in, UInt8 * buf) + { + unalignedStore<UInt64>(buf, 0); + +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + unalignedStoreLittleEndian<UInt64>(buf + 8, 0x00000000FFFF0000ull | (static_cast<UInt64>(ntohl(in)) << 32)); +#else + unalignedStoreLittleEndian<UInt64>(buf + 8, 0x00000000FFFF0000ull | (static_cast<UInt64>(std::byteswap(in)) << 32)); +#endif + } +}; + +class FunctionMACNumToString : public IFunction +{ +public: + static constexpr auto name = "MACNumToString"; + static FunctionPtr create(ContextPtr) { return std::make_shared<FunctionMACNumToString>(); } + + String getName() const override + { + return name; + } + + size_t getNumberOfArguments() const override { return 1; } + bool isInjective(const ColumnsWithTypeAndName &) const override { return true; } + bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return false; } + + DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override + { + if (!WhichDataType(arguments[0]).isUInt64()) + throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal type {} of argument of function {}, expected UInt64", + arguments[0]->getName(), getName()); + + return std::make_shared<DataTypeString>(); + } + + static void formatMAC(UInt64 mac, UInt8 * out) + { + /// MAC address is represented in UInt64 in natural order (so, MAC addresses are compared in same order as UInt64). + /// Higher two bytes in UInt64 are just ignored. + + writeHexByteUppercase(mac >> 40, &out[0]); + out[2] = ':'; + writeHexByteUppercase(mac >> 32, &out[3]); + out[5] = ':'; + writeHexByteUppercase(mac >> 24, &out[6]); + out[8] = ':'; + writeHexByteUppercase(mac >> 16, &out[9]); + out[11] = ':'; + writeHexByteUppercase(mac >> 8, &out[12]); + out[14] = ':'; + writeHexByteUppercase(mac, &out[15]); + out[17] = '\0'; + } + + bool useDefaultImplementationForConstants() const override { return true; } + + ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t /*input_rows_count*/) const override + { + const ColumnPtr & column = arguments[0].column; + + if (const ColumnUInt64 * col = typeid_cast<const ColumnUInt64 *>(column.get())) + { + const ColumnUInt64::Container & vec_in = col->getData(); + + auto col_res = ColumnString::create(); + + ColumnString::Chars & vec_res = col_res->getChars(); + ColumnString::Offsets & offsets_res = col_res->getOffsets(); + + vec_res.resize(vec_in.size() * 18); /// the value is: xx:xx:xx:xx:xx:xx\0 + offsets_res.resize(vec_in.size()); + + size_t current_offset = 0; + for (size_t i = 0; i < vec_in.size(); ++i) + { + formatMAC(vec_in[i], &vec_res[current_offset]); + current_offset += 18; + offsets_res[i] = current_offset; + } + + return col_res; + } + else + throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Illegal column {} of argument of function {}", + arguments[0].column->getName(), getName()); + } +}; + + +struct ParseMACImpl +{ + static constexpr size_t min_string_size = 17; + static constexpr size_t max_string_size = 17; + + /** Example: 01:02:03:04:05:06. + * There could be any separators instead of : and them are just ignored. + * The order of resulting integers are correspond to the order of MAC address. + * If there are any chars other than valid hex digits for bytes, the behaviour is implementation specific. + */ + static UInt64 parse(const char * pos) + { + return (static_cast<UInt64>(unhex(pos[0])) << 44) + | (static_cast<UInt64>(unhex(pos[1])) << 40) + | (static_cast<UInt64>(unhex(pos[3])) << 36) + | (static_cast<UInt64>(unhex(pos[4])) << 32) + | (static_cast<UInt64>(unhex(pos[6])) << 28) + | (static_cast<UInt64>(unhex(pos[7])) << 24) + | (static_cast<UInt64>(unhex(pos[9])) << 20) + | (static_cast<UInt64>(unhex(pos[10])) << 16) + | (static_cast<UInt64>(unhex(pos[12])) << 12) + | (static_cast<UInt64>(unhex(pos[13])) << 8) + | (static_cast<UInt64>(unhex(pos[15])) << 4) + | (static_cast<UInt64>(unhex(pos[16]))); + } + + static constexpr auto name = "MACStringToNum"; +}; + +struct ParseOUIImpl +{ + static constexpr size_t min_string_size = 8; + static constexpr size_t max_string_size = 17; + + /** OUI is the first three bytes of MAC address. + * Example: 01:02:03. + */ + static UInt64 parse(const char * pos) + { + return (static_cast<UInt64>(unhex(pos[0])) << 20) + | (static_cast<UInt64>(unhex(pos[1])) << 16) + | (static_cast<UInt64>(unhex(pos[3])) << 12) + | (static_cast<UInt64>(unhex(pos[4])) << 8) + | (static_cast<UInt64>(unhex(pos[6])) << 4) + | (static_cast<UInt64>(unhex(pos[7]))); + } + + static constexpr auto name = "MACStringToOUI"; +}; + + +template <typename Impl> +class FunctionMACStringTo : public IFunction +{ +public: + static constexpr auto name = Impl::name; + static FunctionPtr create(ContextPtr) { return std::make_shared<FunctionMACStringTo<Impl>>(); } + + String getName() const override + { + return name; + } + + size_t getNumberOfArguments() const override { return 1; } + + bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return false; } + + DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override + { + if (!isString(arguments[0])) + throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal type {} of argument of function {}", + arguments[0]->getName(), getName()); + + return std::make_shared<DataTypeUInt64>(); + } + + bool useDefaultImplementationForConstants() const override { return true; } + + ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t /*input_rows_count*/) const override + { + const ColumnPtr & column = arguments[0].column; + + if (const ColumnString * col = checkAndGetColumn<ColumnString>(column.get())) + { + auto col_res = ColumnUInt64::create(); + + ColumnUInt64::Container & vec_res = col_res->getData(); + vec_res.resize(col->size()); + + const ColumnString::Chars & vec_src = col->getChars(); + const ColumnString::Offsets & offsets_src = col->getOffsets(); + size_t prev_offset = 0; + + for (size_t i = 0; i < vec_res.size(); ++i) + { + size_t current_offset = offsets_src[i]; + size_t string_size = current_offset - prev_offset - 1; /// mind the terminating zero byte + + if (string_size >= Impl::min_string_size && string_size <= Impl::max_string_size) + vec_res[i] = Impl::parse(reinterpret_cast<const char *>(&vec_src[prev_offset])); + else + vec_res[i] = 0; + + prev_offset = current_offset; + } + + return col_res; + } + else + throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Illegal column {} of argument of function {}", + arguments[0].column->getName(), getName()); + } +}; + +class FunctionIPv6CIDRToRange : public IFunction +{ +private: + +#if defined(__SSE2__) + +#include <emmintrin.h> + + static inline void applyCIDRMask(const char * __restrict src, char * __restrict dst_lower, char * __restrict dst_upper, UInt8 bits_to_keep) + { + __m128i mask = _mm_loadu_si128(reinterpret_cast<const __m128i *>(getCIDRMaskIPv6(bits_to_keep).data())); + __m128i lower = _mm_and_si128(_mm_loadu_si128(reinterpret_cast<const __m128i *>(src)), mask); + _mm_storeu_si128(reinterpret_cast<__m128i *>(dst_lower), lower); + + __m128i inv_mask = _mm_xor_si128(mask, _mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128())); + __m128i upper = _mm_or_si128(lower, inv_mask); + _mm_storeu_si128(reinterpret_cast<__m128i *>(dst_upper), upper); + } + +#else + + /// NOTE IPv6 is stored in memory in big endian format that makes some difficulties. + static void applyCIDRMask(const char * __restrict src, char * __restrict dst_lower, char * __restrict dst_upper, UInt8 bits_to_keep) + { + const auto & mask = getCIDRMaskIPv6(bits_to_keep); + + for (size_t i = 0; i < 16; ++i) + { + dst_lower[i] = src[i] & mask[i]; + dst_upper[i] = dst_lower[i] | ~mask[i]; + } + } + +#endif + +public: + static constexpr auto name = "IPv6CIDRToRange"; + static FunctionPtr create(ContextPtr) { return std::make_shared<FunctionIPv6CIDRToRange>(); } + + String getName() const override { return name; } + size_t getNumberOfArguments() const override { return 2; } + bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return false; } + + DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override + { + const auto * ipv6 = checkAndGetDataType<DataTypeIPv6>(arguments[0].get()); + const auto * str = checkAndGetDataType<DataTypeFixedString>(arguments[0].get()); + if (!ipv6 && !(str && str->getN() == IPV6_BINARY_LENGTH)) + throw Exception( + ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, + "Illegal type {} of first argument of function {}, expected IPv6 or FixedString({})", + arguments[0]->getName(), getName(), IPV6_BINARY_LENGTH + ); + + const DataTypePtr & second_argument = arguments[1]; + if (!isUInt8(second_argument)) + throw Exception( + ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, + "Illegal type {} of second argument of function {}, expected UInt8", + second_argument->getName(), getName() + ); + + DataTypePtr element = std::make_shared<DataTypeIPv6>(); + return std::make_shared<DataTypeTuple>(DataTypes{element, element}); + } + + bool useDefaultImplementationForConstants() const override { return true; } + + + ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t input_rows_count) const override + { + const auto & col_type_name_cidr = arguments[1]; + const ColumnPtr & column_cidr = col_type_name_cidr.column; + + const auto * col_const_cidr_in = checkAndGetColumnConst<ColumnUInt8>(column_cidr.get()); + const auto * col_cidr_in = checkAndGetColumn<ColumnUInt8>(column_cidr.get()); + + if (!col_const_cidr_in && !col_cidr_in) + throw Exception( + ErrorCodes::ILLEGAL_COLUMN, + "Illegal column {} of argument of function {}", + arguments[1].column->getName(), getName() + ); + + const auto & col_type_name_ip = arguments[0]; + const ColumnPtr & column_ip = col_type_name_ip.column; + + const auto * col_const_ip_in = checkAndGetColumnConst<ColumnIPv6>(column_ip.get()); + const auto * col_ip_in = checkAndGetColumn<ColumnIPv6>(column_ip.get()); + + const auto * col_const_str_in = checkAndGetColumnConst<ColumnFixedString>(column_ip.get()); + const auto * col_str_in = checkAndGetColumn<ColumnFixedString>(column_ip.get()); + + std::function<const char *(size_t)> get_ip_data; + if (col_const_ip_in) + get_ip_data = [col_const_ip_in](size_t) { return col_const_ip_in->getDataAt(0).data; }; + else if (col_const_str_in) + get_ip_data = [col_const_str_in](size_t) { return col_const_str_in->getDataAt(0).data; }; + else if (col_ip_in) + get_ip_data = [col_ip_in](size_t i) { return reinterpret_cast<const char *>(&col_ip_in->getData()[i]); }; + else if (col_str_in) + get_ip_data = [col_str_in](size_t i) { return reinterpret_cast<const char *>(&col_str_in->getChars().data()[i * IPV6_BINARY_LENGTH]); }; + else + throw Exception( + ErrorCodes::ILLEGAL_COLUMN, + "Illegal column {} of argument of function {}", + arguments[0].column->getName(), getName() + ); + + auto col_res_lower_range = ColumnIPv6::create(); + auto col_res_upper_range = ColumnIPv6::create(); + + auto & vec_res_lower_range = col_res_lower_range->getData(); + vec_res_lower_range.resize(input_rows_count); + + auto & vec_res_upper_range = col_res_upper_range->getData(); + vec_res_upper_range.resize(input_rows_count); + + static constexpr UInt8 max_cidr_mask = IPV6_BINARY_LENGTH * 8; + + for (size_t i = 0; i < input_rows_count; ++i) + { + UInt8 cidr = col_const_cidr_in + ? col_const_cidr_in->getValue<UInt8>() + : col_cidr_in->getData()[i]; + + cidr = std::min(cidr, max_cidr_mask); + + applyCIDRMask(get_ip_data(i), reinterpret_cast<char *>(&vec_res_lower_range[i]), reinterpret_cast<char *>(&vec_res_upper_range[i]), cidr); + } + + return ColumnTuple::create(Columns{std::move(col_res_lower_range), std::move(col_res_upper_range)}); + } +}; + + +class FunctionIPv4CIDRToRange : public IFunction +{ +private: + static inline std::pair<UInt32, UInt32> applyCIDRMask(UInt32 src, UInt8 bits_to_keep) + { + if (bits_to_keep >= 8 * sizeof(UInt32)) + return { src, src }; + if (bits_to_keep == 0) + return { static_cast<UInt32>(0), static_cast<UInt32>(-1) }; + + UInt32 mask = static_cast<UInt32>(-1) << (8 * sizeof(UInt32) - bits_to_keep); + UInt32 lower = src & mask; + UInt32 upper = lower | ~mask; + + return { lower, upper }; + } + + template <typename ArgType> + ColumnPtr executeTyped(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t input_rows_count) const + { + using ColumnType = ColumnVector<ArgType>; + const auto & col_type_name_ip = arguments[0]; + const ColumnPtr & column_ip = col_type_name_ip.column; + + const auto * col_const_ip_in = checkAndGetColumnConst<ColumnType>(column_ip.get()); + const auto * col_ip_in = checkAndGetColumn<ColumnType>(column_ip.get()); + + const auto & col_type_name_cidr = arguments[1]; + const ColumnPtr & column_cidr = col_type_name_cidr.column; + + const auto * col_const_cidr_in = checkAndGetColumnConst<ColumnUInt8>(column_cidr.get()); + const auto * col_cidr_in = checkAndGetColumn<ColumnUInt8>(column_cidr.get()); + + auto col_res_lower_range = ColumnIPv4::create(); + auto col_res_upper_range = ColumnIPv4::create(); + + auto & vec_res_lower_range = col_res_lower_range->getData(); + vec_res_lower_range.resize(input_rows_count); + + auto & vec_res_upper_range = col_res_upper_range->getData(); + vec_res_upper_range.resize(input_rows_count); + + for (size_t i = 0; i < input_rows_count; ++i) + { + ArgType ip = col_const_ip_in + ? col_const_ip_in->template getValue<ArgType>() + : col_ip_in->getData()[i]; + + UInt8 cidr = col_const_cidr_in + ? col_const_cidr_in->getValue<UInt8>() + : col_cidr_in->getData()[i]; + + std::tie(vec_res_lower_range[i], vec_res_upper_range[i]) = applyCIDRMask(ip, cidr); + } + + return ColumnTuple::create(Columns{std::move(col_res_lower_range), std::move(col_res_upper_range)}); + } + +public: + static constexpr auto name = "IPv4CIDRToRange"; + static FunctionPtr create(ContextPtr) { return std::make_shared<FunctionIPv4CIDRToRange>(); } + + String getName() const override { return name; } + size_t getNumberOfArguments() const override { return 2; } + bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return false; } + + DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override + { + WhichDataType arg_type(arguments[0]); + if (!(arg_type.isIPv4() || arg_type.isUInt8() || arg_type.isUInt16() || arg_type.isUInt32())) + throw Exception( + ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, + "Illegal type {} of first argument of function {}, expected IPv4 or UInt8 or UInt16 or UInt32", + arguments[0]->getName(), getName() + ); + + + const DataTypePtr & second_argument = arguments[1]; + if (!isUInt8(second_argument)) + throw Exception( + ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, + "Illegal type {} of second argument of function {}, expected UInt8", + second_argument->getName(), getName() + ); + + DataTypePtr element = DataTypeFactory::instance().get("IPv4"); + return std::make_shared<DataTypeTuple>(DataTypes{element, element}); + } + + bool useDefaultImplementationForConstants() const override { return true; } + + + ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr & ret_type, size_t input_rows_count) const override + { + if (arguments[1].type->getTypeId() != TypeIndex::UInt8) + throw Exception( + ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, + "Illegal type {} of second argument of function {}, expected UInt8", arguments[1].type->getName(), getName() + ); + + switch (arguments[0].type->getTypeId()) + { + case TypeIndex::IPv4: return executeTyped<IPv4>(arguments, ret_type, input_rows_count); + case TypeIndex::UInt8: return executeTyped<UInt8>(arguments, ret_type, input_rows_count); + case TypeIndex::UInt16: return executeTyped<UInt16>(arguments, ret_type, input_rows_count); + case TypeIndex::UInt32: return executeTyped<UInt32>(arguments, ret_type, input_rows_count); + default: break; + } + + throw Exception( + ErrorCodes::ILLEGAL_COLUMN, + "Illegal column {} of argument of function {}, expected IPv4 or UInt8 or UInt16 or UInt32", + arguments[0].column->getName(), getName() + ); + } +}; + +class FunctionIsIPv4String : public IFunction +{ +public: + static constexpr auto name = "isIPv4String"; + + static FunctionPtr create(ContextPtr) { return std::make_shared<FunctionIsIPv4String>(); } + + String getName() const override { return name; } + + size_t getNumberOfArguments() const override { return 1; } + + bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return false; } + + bool useDefaultImplementationForConstants() const override { return true; } + + DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override + { + if (!isString(arguments[0])) + throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal type {} of argument of function {}", + arguments[0]->getName(), getName()); + return std::make_shared<DataTypeUInt8>(); + } + + ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t /*input_rows_count*/) const override + { + const ColumnString * input_column = checkAndGetColumn<ColumnString>(arguments[0].column.get()); + + if (!input_column) + { + throw Exception( + ErrorCodes::ILLEGAL_COLUMN, "Illegal column {} of argument of function {}", arguments[0].column->getName(), getName()); + } + + auto col_res = ColumnUInt8::create(); + + ColumnUInt8::Container & vec_res = col_res->getData(); + vec_res.resize(input_column->size()); + + const ColumnString::Chars & vec_src = input_column->getChars(); + const ColumnString::Offsets & offsets_src = input_column->getOffsets(); + size_t prev_offset = 0; + UInt32 result = 0; + + for (size_t i = 0; i < vec_res.size(); ++i) + { + vec_res[i] = DB::parseIPv4whole(reinterpret_cast<const char *>(&vec_src[prev_offset]), reinterpret_cast<unsigned char *>(&result)); + prev_offset = offsets_src[i]; + } + + return col_res; + } +}; + +class FunctionIsIPv6String : public IFunction +{ +public: + static constexpr auto name = "isIPv6String"; + + static FunctionPtr create(ContextPtr) { return std::make_shared<FunctionIsIPv6String>(); } + + String getName() const override { return name; } + + size_t getNumberOfArguments() const override { return 1; } + + bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return false; } + + bool useDefaultImplementationForConstants() const override { return true; } + + DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override + { + if (!isString(arguments[0])) + { + throw Exception( + ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal type {} of argument of function {}", arguments[0]->getName(), getName()); + } + + return std::make_shared<DataTypeUInt8>(); + } + + ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t /*input_rows_count*/) const override + { + const ColumnString * input_column = checkAndGetColumn<ColumnString>(arguments[0].column.get()); + if (!input_column) + { + throw Exception( + ErrorCodes::ILLEGAL_COLUMN, "Illegal column {} of argument of function {}", arguments[0].column->getName(), getName()); + } + + auto col_res = ColumnUInt8::create(); + + ColumnUInt8::Container & vec_res = col_res->getData(); + vec_res.resize(input_column->size()); + + const ColumnString::Chars & vec_src = input_column->getChars(); + const ColumnString::Offsets & offsets_src = input_column->getOffsets(); + size_t prev_offset = 0; + char buffer[IPV6_BINARY_LENGTH]; + + for (size_t i = 0; i < vec_res.size(); ++i) + { + vec_res[i] = DB::parseIPv6whole(reinterpret_cast<const char *>(&vec_src[prev_offset]), + reinterpret_cast<const char *>(&vec_src[offsets_src[i] - 1]), + reinterpret_cast<unsigned char *>(buffer)); + prev_offset = offsets_src[i]; + } + + return col_res; + } +}; + +struct NameFunctionIPv4NumToString { static constexpr auto name = "IPv4NumToString"; }; +struct NameFunctionIPv4NumToStringClassC { static constexpr auto name = "IPv4NumToStringClassC"; }; + +REGISTER_FUNCTION(Coding) +{ + factory.registerFunction<FunctionCutIPv6>(); + factory.registerFunction<FunctionIPv4ToIPv6>(); + factory.registerFunction<FunctionMACNumToString>(); + factory.registerFunction<FunctionMACStringTo<ParseMACImpl>>(); + factory.registerFunction<FunctionMACStringTo<ParseOUIImpl>>(); + factory.registerFunction<FunctionIPv6CIDRToRange>(); + factory.registerFunction<FunctionIPv4CIDRToRange>(); + factory.registerFunction<FunctionIsIPv4String>(); + factory.registerFunction<FunctionIsIPv6String>(); + + factory.registerFunction<FunctionIPv4NumToString<0, NameFunctionIPv4NumToString>>(); + factory.registerFunction<FunctionIPv4NumToString<1, NameFunctionIPv4NumToStringClassC>>(); + + factory.registerFunction<FunctionIPv4StringToNum<IPStringToNumExceptionMode::Throw>>(); + factory.registerFunction<FunctionIPv4StringToNum<IPStringToNumExceptionMode::Default>>(); + factory.registerFunction<FunctionIPv4StringToNum<IPStringToNumExceptionMode::Null>>(); + + factory.registerFunction<FunctionIPv6NumToString>(); + factory.registerFunction<FunctionIPv6StringToNum<IPStringToNumExceptionMode::Throw>>(); + factory.registerFunction<FunctionIPv6StringToNum<IPStringToNumExceptionMode::Default>>(); + factory.registerFunction<FunctionIPv6StringToNum<IPStringToNumExceptionMode::Null>>(); + + /// MySQL compatibility aliases: + factory.registerAlias("INET_ATON", FunctionIPv4StringToNum<IPStringToNumExceptionMode::Throw>::name, FunctionFactory::CaseInsensitive); + factory.registerAlias("INET6_NTOA", FunctionIPv6NumToString::name, FunctionFactory::CaseInsensitive); + factory.registerAlias("INET6_ATON", FunctionIPv6StringToNum<IPStringToNumExceptionMode::Throw>::name, FunctionFactory::CaseInsensitive); + factory.registerAlias("INET_NTOA", NameFunctionIPv4NumToString::name, FunctionFactory::CaseInsensitive); +} + +} |
