ClickHouse
253 строки · 9.0 Кб
1#include <Columns/ColumnConst.h>2#include <Columns/ColumnString.h>3#include <Columns/ColumnsNumber.h>4#include <Common/IPv6ToBinary.h>5#include <Common/formatIPv6.h>6#include <DataTypes/DataTypeNullable.h>7#include <DataTypes/DataTypesNumber.h>8#include <Functions/IFunction.h>9#include <Functions/FunctionFactory.h>10#include <Functions/FunctionHelpers.h>11#include <variant>12#include <charconv>13
14
15#include <Common/logger_useful.h>16namespace DB::ErrorCodes17{
18extern const int CANNOT_PARSE_TEXT;19extern const int ILLEGAL_TYPE_OF_ARGUMENT;20extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;21}
22
23namespace
24{
25
26class IPAddressVariant27{
28public:29
30explicit IPAddressVariant(std::string_view address_str)31{32UInt32 v4;33if (DB::parseIPv4whole(address_str.begin(), address_str.end(), reinterpret_cast<unsigned char *>(&v4)))34{35addr = v4;36}37else38{39addr = IPv6AddrType();40bool success = DB::parseIPv6whole(address_str.begin(), address_str.end(), std::get<IPv6AddrType>(addr).data());41if (!success)42throw DB::Exception(DB::ErrorCodes::CANNOT_PARSE_TEXT, "Neither IPv4 nor IPv6 address: '{}'", address_str);43}44}45
46UInt32 asV4() const47{48if (const auto * val = std::get_if<IPv4AddrType>(&addr))49return *val;50return 0;51}52
53const uint8_t * asV6() const54{55if (const auto * val = std::get_if<IPv6AddrType>(&addr))56return val->data();57return nullptr;58}59
60private:61using IPv4AddrType = UInt32;62using IPv6AddrType = std::array<uint8_t, IPV6_BINARY_LENGTH>;63
64std::variant<IPv4AddrType, IPv6AddrType> addr;65};66
67struct IPAddressCIDR68{
69IPAddressVariant address;70UInt8 prefix;71};72
73IPAddressCIDR parseIPWithCIDR(std::string_view cidr_str)74{
75size_t pos_slash = cidr_str.find('/');76
77if (pos_slash == 0)78throw DB::Exception(DB::ErrorCodes::CANNOT_PARSE_TEXT, "Error parsing IP address with prefix: {}", std::string(cidr_str));79if (pos_slash == std::string_view::npos)80throw DB::Exception(DB::ErrorCodes::CANNOT_PARSE_TEXT, "The text does not contain '/': {}", std::string(cidr_str));81
82std::string_view addr_str = cidr_str.substr(0, pos_slash);83IPAddressVariant addr(addr_str);84
85uint8_t prefix = 0;86auto prefix_str = cidr_str.substr(pos_slash+1);87
88const auto * prefix_str_end = prefix_str.data() + prefix_str.size();89auto [parse_end, parse_error] = std::from_chars(prefix_str.data(), prefix_str_end, prefix);90uint8_t max_prefix = (addr.asV6() ? IPV6_BINARY_LENGTH : IPV4_BINARY_LENGTH) * 8;91bool has_error = parse_error != std::errc() || parse_end != prefix_str_end || prefix > max_prefix;92if (has_error)93throw DB::Exception(DB::ErrorCodes::CANNOT_PARSE_TEXT, "The CIDR has a malformed prefix bits: {}", std::string(cidr_str));94
95return {addr, static_cast<UInt8>(prefix)};96}
97
98inline bool isAddressInRange(const IPAddressVariant & address, const IPAddressCIDR & cidr)99{
100if (const auto * cidr_v6 = cidr.address.asV6())101{102if (const auto * addr_v6 = address.asV6())103return DB::matchIPv6Subnet(addr_v6, cidr_v6, cidr.prefix);104}105else106{107if (!address.asV6())108return DB::matchIPv4Subnet(address.asV4(), cidr.address.asV4(), cidr.prefix);109}110return false;111}
112
113}
114
115namespace DB116{
117class FunctionIsIPAddressContainedIn : public IFunction118{119public:120static constexpr auto name = "isIPAddressInRange";121String getName() const override { return name; }122static FunctionPtr create(ContextPtr) { return std::make_shared<FunctionIsIPAddressContainedIn>(); }123bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return true; }124
125ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr & /* return_type */, size_t input_rows_count) const override126{127const IColumn * col_addr = arguments[0].column.get();128const IColumn * col_cidr = arguments[1].column.get();129
130if (const auto * col_addr_const = checkAndGetAnyColumnConst(col_addr))131{132if (const auto * col_cidr_const = checkAndGetAnyColumnConst(col_cidr))133return executeImpl(*col_addr_const, *col_cidr_const, input_rows_count);134else135return executeImpl(*col_addr_const, *col_cidr, input_rows_count);136}137else138{139if (const auto * col_cidr_const = checkAndGetAnyColumnConst(col_cidr))140return executeImpl(*col_addr, *col_cidr_const, input_rows_count);141else142return executeImpl(*col_addr, *col_cidr, input_rows_count);143}144}145
146DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override147{148if (arguments.size() != 2)149throw Exception(ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH,150"Number of arguments for function {} doesn't match: passed {}, should be 2",151getName(), arguments.size());152
153const DataTypePtr & addr_type = arguments[0];154const DataTypePtr & prefix_type = arguments[1];155
156if (!isString(addr_type) || !isString(prefix_type))157throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "The arguments of function {} must be String", getName());158
159return std::make_shared<DataTypeUInt8>();160}161
162size_t getNumberOfArguments() const override { return 2; }163bool useDefaultImplementationForNulls() const override { return false; }164
165private:166/// Like checkAndGetColumnConst() but this function doesn't167/// care about the type of data column.168static const ColumnConst * checkAndGetAnyColumnConst(const IColumn * column)169{170if (!column || !isColumnConst(*column))171return nullptr;172
173return assert_cast<const ColumnConst *>(column);174}175
176/// Both columns are constant.177static ColumnPtr executeImpl(178const ColumnConst & col_addr_const,179const ColumnConst & col_cidr_const,180size_t input_rows_count)181{182const auto & col_addr = col_addr_const.getDataColumn();183const auto & col_cidr = col_cidr_const.getDataColumn();184
185const auto addr = IPAddressVariant(col_addr.getDataAt(0).toView());186const auto cidr = parseIPWithCIDR(col_cidr.getDataAt(0).toView());187
188ColumnUInt8::MutablePtr col_res = ColumnUInt8::create(1);189ColumnUInt8::Container & vec_res = col_res->getData();190
191vec_res[0] = isAddressInRange(addr, cidr) ? 1 : 0;192
193return ColumnConst::create(std::move(col_res), input_rows_count);194}195
196/// Address is constant.197static ColumnPtr executeImpl(const ColumnConst & col_addr_const, const IColumn & col_cidr, size_t input_rows_count)198{199const auto & col_addr = col_addr_const.getDataColumn();200
201const auto addr = IPAddressVariant(col_addr.getDataAt(0).toView());202
203ColumnUInt8::MutablePtr col_res = ColumnUInt8::create(input_rows_count);204ColumnUInt8::Container & vec_res = col_res->getData();205
206for (size_t i = 0; i < input_rows_count; ++i)207{208const auto cidr = parseIPWithCIDR(col_cidr.getDataAt(i).toView());209vec_res[i] = isAddressInRange(addr, cidr) ? 1 : 0;210}211return col_res;212}213
214/// CIDR is constant.215static ColumnPtr executeImpl(const IColumn & col_addr, const ColumnConst & col_cidr_const, size_t input_rows_count)216{217const auto & col_cidr = col_cidr_const.getDataColumn();218
219const auto cidr = parseIPWithCIDR(col_cidr.getDataAt(0).toView());220
221ColumnUInt8::MutablePtr col_res = ColumnUInt8::create(input_rows_count);222ColumnUInt8::Container & vec_res = col_res->getData();223for (size_t i = 0; i < input_rows_count; ++i)224{225const auto addr = IPAddressVariant(col_addr.getDataAt(i).toView());226vec_res[i] = isAddressInRange(addr, cidr) ? 1 : 0;227}228return col_res;229}230
231/// Neither are constant.232static ColumnPtr executeImpl(const IColumn & col_addr, const IColumn & col_cidr, size_t input_rows_count)233{234ColumnUInt8::MutablePtr col_res = ColumnUInt8::create(input_rows_count);235ColumnUInt8::Container & vec_res = col_res->getData();236
237for (size_t i = 0; i < input_rows_count; ++i)238{239const auto addr = IPAddressVariant(col_addr.getDataAt(i).toView());240const auto cidr = parseIPWithCIDR(col_cidr.getDataAt(i).toView());241
242vec_res[i] = isAddressInRange(addr, cidr) ? 1 : 0;243}244
245return col_res;246}247};248
249REGISTER_FUNCTION(IsIPAddressContainedIn)250{251factory.registerFunction<FunctionIsIPAddressContainedIn>();252}253}
254