Skip to content

Commit 7fb41e6

Browse files
authored
Merge pull request ClickHouse#78364 from YjyJeff/feature/extend_is_ip_address_in_range
Extend the isIPAddressInRange function to String, IPv4, IPv6, Nullable(String) Nullable(IPv4) and Nullable(IPv6) data types
2 parents 673d5c4 + 1090fa4 commit 7fb41e6

File tree

5 files changed

+367
-33
lines changed

5 files changed

+367
-33
lines changed

docs/en/sql-reference/functions/ip-address-functions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -670,7 +670,7 @@ This function accepts both IPv4 and IPv6 addresses (and networks) represented as
670670

671671
**Arguments**
672672

673-
- `address` — An IPv4 or IPv6 address. [String](../data-types/string.md).
673+
- `address` — An IPv4 or IPv6 address. [String](../data-types/string.md), [IPv4](../data-types/ipv4.md), [IPv6](../data-types/ipv6.md), `Nullable(String)`, `Nullable(IPv4)` and `Nullable(IPv6)`.
674674
- `prefix` — An IPv4 or IPv6 network prefix in CIDR. [String](../data-types/string.md).
675675

676676
**Returned value**

src/Functions/isIPAddressContainedIn.cpp

Lines changed: 168 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include <Columns/ColumnConst.h>
22
#include <Columns/ColumnString.h>
33
#include <Columns/ColumnsNumber.h>
4+
#include <Columns/ColumnNullable.h>
45
#include <Common/IPv6ToBinary.h>
56
#include <Common/formatIPv6.h>
67
#include <DataTypes/DataTypeNullable.h>
@@ -23,23 +24,65 @@ namespace DB::ErrorCodes
2324
namespace
2425
{
2526

27+
enum class IPKind
28+
{
29+
IPv4,
30+
IPv6,
31+
String
32+
};
33+
34+
template <IPKind kind>
35+
struct IPTrait
36+
{
37+
};
38+
39+
template <>
40+
struct IPTrait<IPKind::IPv4>
41+
{
42+
using ColumnType = DB::ColumnIPv4;
43+
using ElementType = DB::IPv4;
44+
};
45+
46+
template <>
47+
struct IPTrait<IPKind::IPv6>
48+
{
49+
using ColumnType = DB::ColumnIPv6;
50+
using ElementType = DB::UInt128;
51+
};
52+
53+
template <>
54+
struct IPTrait<IPKind::String>
55+
{
56+
using ColumnType = DB::ColumnString;
57+
using ElementType = std::string_view;
58+
};
59+
2660
class IPAddressVariant
2761
{
2862
public:
63+
explicit IPAddressVariant(DB::IPv4 addr_): addr(addr_)
64+
{
65+
}
66+
67+
explicit IPAddressVariant(UInt128 addr_)
68+
{
69+
addr = IPv6AddrType();
70+
auto * dst = std::get<IPv6AddrType>(addr).data();
71+
const char * src = reinterpret_cast<const char *>(&addr_.items);
72+
memcpy(dst, src, IPV6_BINARY_LENGTH);
73+
}
2974

30-
explicit IPAddressVariant(std::string_view address_str)
75+
explicit IPAddressVariant(std::string_view addr_)
3176
{
3277
UInt32 v4;
33-
if (DB::parseIPv4whole(address_str.data(), address_str.data() + address_str.size(), reinterpret_cast<unsigned char *>(&v4)))
34-
{
78+
if (DB::parseIPv4whole(addr_.data(), addr_.data() + addr_.size(), reinterpret_cast<unsigned char *>(&v4)))
3579
addr = v4;
36-
}
3780
else
3881
{
3982
addr = IPv6AddrType();
40-
bool success = DB::parseIPv6whole(address_str.data(), address_str.data() + address_str.size(), std::get<IPv6AddrType>(addr).data());
83+
bool success = DB::parseIPv6whole(addr_.data(), addr_.data() + addr_.size(), std::get<IPv6AddrType>(addr).data());
4184
if (!success)
42-
throw DB::Exception(DB::ErrorCodes::CANNOT_PARSE_TEXT, "Neither IPv4 nor IPv6 address: '{}'", address_str);
85+
throw DB::Exception(DB::ErrorCodes::CANNOT_PARSE_TEXT, "Neither IPv4 nor IPv6 address: '{}'", addr_);
4386
}
4487
}
4588

@@ -80,13 +123,13 @@ IPAddressCIDR parseIPWithCIDR(std::string_view cidr_str)
80123
throw DB::Exception(DB::ErrorCodes::CANNOT_PARSE_TEXT, "The text does not contain '/': {}", std::string(cidr_str));
81124

82125
std::string_view addr_str = cidr_str.substr(0, pos_slash);
83-
IPAddressVariant addr(addr_str);
126+
auto addr = IPAddressVariant(addr_str);
84127

85128
uint8_t prefix = 0;
86129
auto prefix_str = cidr_str.substr(pos_slash+1);
87130

88131
const auto * prefix_str_end = prefix_str.data() + prefix_str.size();
89-
auto [parse_end, parse_error] = std::from_chars(prefix_str.data(), prefix_str_end, prefix); /// NOLINT(bugprone-suspicious-stringview-data-usage)
132+
auto [parse_end, parse_error] = std::from_chars(prefix_str.data(), prefix_str_end, prefix); /// NOLINT(bugprone-suspicious-stringview-data-usage)
90133
uint8_t max_prefix = (addr.asV6() ? IPV6_BINARY_LENGTH : IPV4_BINARY_LENGTH) * 8;
91134
bool has_error = parse_error != std::errc() || parse_end != prefix_str_end || prefix > max_prefix;
92135
if (has_error)
@@ -122,6 +165,37 @@ namespace DB
122165
static FunctionPtr create(ContextPtr) { return std::make_shared<FunctionIsIPAddressContainedIn>(); }
123166
bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return true; }
124167

168+
template <IPKind kind>
169+
static inline IPAddressVariant parseIP(const IPTrait<kind>::ColumnType * col_addr, size_t n)
170+
{
171+
if constexpr (kind == IPKind::IPv4 || kind == IPKind::IPv6)
172+
{
173+
return IPAddressVariant(col_addr->getElement(n));
174+
}
175+
return IPAddressVariant(col_addr->getDataAt(n).toView());
176+
}
177+
178+
#pragma clang diagnostic ignored "-Wshadow"
179+
#define EXEC_IMPL(col, func, ...) \
180+
if (const auto * ipv4_column = dynamic_cast<const ColumnIPv4 *>(&(col))) \
181+
return func<IPKind::IPv4>(ipv4_column, __VA_ARGS__); \
182+
else if (const auto * ipv6_column = dynamic_cast<const ColumnIPv6 *>(&(col))) \
183+
return func<IPKind::IPv6>(ipv6_column, __VA_ARGS__); \
184+
else if (const auto * string_column = dynamic_cast<const ColumnString *>(&(col))) \
185+
return func<IPKind::String>(string_column, __VA_ARGS__);
186+
187+
static std::optional<IPAddressVariant> parseConstantIP(const ColumnConst & col_addr)
188+
{
189+
EXEC_IMPL(col_addr.getDataColumn(), parseIP, 0)
190+
else if (col_addr.onlyNull())
191+
return std::nullopt;
192+
else if (const auto * nullable_column = dynamic_cast<const ColumnNullable *>(&col_addr.getDataColumn()))
193+
{
194+
EXEC_IMPL(nullable_column->getNestedColumn(), parseIP, 0) ///NO-LINT
195+
}
196+
throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "The IP column type must be one of: String, IPv4, IPv6, Nullable(IPv4), Nullable(IPv6), or Nullable(String).");
197+
}
198+
125199
ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr & /* return_type */, size_t input_rows_count) const override
126200
{
127201
const IColumn * col_addr = arguments[0].column.get();
@@ -133,7 +207,6 @@ namespace DB
133207
return executeImpl(*col_addr_const, *col_cidr_const, input_rows_count);
134208
return executeImpl(*col_addr_const, *col_cidr, input_rows_count);
135209
}
136-
137210
if (const auto * col_cidr_const = checkAndGetAnyColumnConst(col_cidr))
138211
return executeImpl(*col_addr, *col_cidr_const, input_rows_count);
139212
return executeImpl(*col_addr, *col_cidr, input_rows_count);
@@ -149,14 +222,19 @@ namespace DB
149222
const DataTypePtr & addr_type = arguments[0];
150223
const DataTypePtr & prefix_type = arguments[1];
151224

152-
if (!isString(addr_type) || !isString(prefix_type))
153-
throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "The arguments of function {} must be String", getName());
225+
WhichDataType type = WhichDataType(addr_type);
226+
if (const auto * nullable_type = dynamic_cast<const DataTypeNullable *>(&*addr_type))
227+
type = WhichDataType(nullable_type->getNestedType());
154228

155-
return std::make_shared<DataTypeUInt8>();
156-
}
229+
if (!(type.isString() || type.isIPv4() || type.isIPv6()) || !isString(prefix_type))
230+
throw Exception(
231+
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
232+
"The first argument of function {} must be one of: String, IPv4, IPv6, Nullable(String), Nullable(IPv4), or "
233+
"Nullable(IPv6) and the second argument must be String. Type of the first argument: {}, type of the second argument: {}",
234+
getName(),
235+
addr_type->getName(),
236+
prefix_type->getName());
157237

158-
DataTypePtr getReturnTypeForDefaultImplementationForDynamic() const override
159-
{
160238
return std::make_shared<DataTypeUInt8>();
161239
}
162240

@@ -180,69 +258,128 @@ namespace DB
180258
const ColumnConst & col_cidr_const,
181259
size_t input_rows_count)
182260
{
183-
const auto & col_addr = col_addr_const.getDataColumn();
184261
const auto & col_cidr = col_cidr_const.getDataColumn();
185262

186-
const auto addr = IPAddressVariant(col_addr.getDataAt(0).toView());
263+
const auto addr = parseConstantIP(col_addr_const);
187264
const auto cidr = parseIPWithCIDR(col_cidr.getDataAt(0).toView());
188265

189266
ColumnUInt8::MutablePtr col_res = ColumnUInt8::create(1);
190267
ColumnUInt8::Container & vec_res = col_res->getData();
191268

192-
vec_res[0] = isAddressInRange(addr, cidr) ? 1 : 0;
269+
vec_res[0] = addr.has_value() && isAddressInRange(*addr, cidr) ? 1 : 0;
193270

194271
return ColumnConst::create(std::move(col_res), input_rows_count);
195272
}
196273

197274
/// Address is constant.
198275
static ColumnPtr executeImpl(const ColumnConst & col_addr_const, const IColumn & col_cidr, size_t input_rows_count)
199276
{
200-
const auto & col_addr = col_addr_const.getDataColumn();
201-
202-
const auto addr = IPAddressVariant(col_addr.getDataAt(0).toView());
277+
const auto addr = parseConstantIP(col_addr_const);
278+
if (!addr.has_value())
279+
return ColumnUInt8::create(input_rows_count, 0);
203280

204281
ColumnUInt8::MutablePtr col_res = ColumnUInt8::create(input_rows_count);
205282
ColumnUInt8::Container & vec_res = col_res->getData();
206283

207284
for (size_t i = 0; i < input_rows_count; ++i)
208285
{
209286
const auto cidr = parseIPWithCIDR(col_cidr.getDataAt(i).toView());
287+
vec_res[i] = isAddressInRange(*addr, cidr) ? 1 : 0;
288+
}
289+
return col_res;
290+
}
291+
292+
template <IPKind kind>
293+
static ColumnPtr executeImpl(const IPTrait<kind>::ColumnType * col_addr, const IPAddressCIDR & cidr, size_t input_rows_count)
294+
{
295+
ColumnUInt8::MutablePtr col_res = ColumnUInt8::create(input_rows_count);
296+
ColumnUInt8::Container & vec_res = col_res->getData();
297+
298+
for (size_t i = 0; i < input_rows_count; ++i)
299+
{
300+
const auto addr = parseIP<kind>(col_addr, i);
210301
vec_res[i] = isAddressInRange(addr, cidr) ? 1 : 0;
211302
}
212303
return col_res;
213304
}
214305

306+
template <IPKind kind>
307+
static ColumnPtr executeImpl(
308+
const IPTrait<kind>::ColumnType * col_addr,
309+
const ColumnNullable * nullable_column,
310+
const IPAddressCIDR & cidr,
311+
size_t input_rows_count)
312+
{
313+
ColumnUInt8::MutablePtr col_res = ColumnUInt8::create(input_rows_count);
314+
ColumnUInt8::Container & vec_res = col_res->getData();
315+
316+
for (size_t i = 0; i < input_rows_count; ++i)
317+
{
318+
if (nullable_column->isNullAt(i))
319+
vec_res[i] = 0;
320+
else
321+
{
322+
const auto addr = parseIP<kind>(col_addr, i);
323+
vec_res[i] = isAddressInRange(addr, cidr) ? 1 : 0;
324+
}
325+
}
326+
return col_res;
327+
}
328+
329+
template <typename T>
330+
static ColumnPtr executeImpl(const IColumn & col_addr, const T & cidr, size_t input_rows_count)
331+
{
332+
EXEC_IMPL(col_addr, executeImpl, cidr, input_rows_count)
333+
else if (const auto * nullable_column = dynamic_cast<const ColumnNullable *>(&col_addr))
334+
{
335+
EXEC_IMPL(nullable_column->getNestedColumn(), executeImpl, nullable_column, cidr, input_rows_count)
336+
}
337+
throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "The IP column type must be one of: String, IPv4, IPv6, Nullable(IPv4), Nullable(IPv6), or Nullable(String).");
338+
}
339+
215340
/// CIDR is constant.
216341
static ColumnPtr executeImpl(const IColumn & col_addr, const ColumnConst & col_cidr_const, size_t input_rows_count)
217342
{
218-
const auto & col_cidr = col_cidr_const.getDataColumn();
219-
220-
const auto cidr = parseIPWithCIDR(col_cidr.getDataAt(0).toView());
343+
const auto cidr = parseIPWithCIDR(col_cidr_const.getDataAt(0).toView());
344+
return executeImpl<IPAddressCIDR>(col_addr, cidr, input_rows_count);
345+
}
221346

347+
template <IPKind kind>
348+
static ColumnPtr executeImpl(const IPTrait<kind>::ColumnType * col_addr, const IColumn & col_cidr, size_t input_rows_count)
349+
{
222350
ColumnUInt8::MutablePtr col_res = ColumnUInt8::create(input_rows_count);
223351
ColumnUInt8::Container & vec_res = col_res->getData();
352+
224353
for (size_t i = 0; i < input_rows_count; ++i)
225354
{
226-
const auto addr = IPAddressVariant(col_addr.getDataAt(i).toView());
355+
const auto addr = parseIP<kind>(col_addr, i);
356+
const auto cidr = parseIPWithCIDR(col_cidr.getDataAt(i).toView());
227357
vec_res[i] = isAddressInRange(addr, cidr) ? 1 : 0;
228358
}
229359
return col_res;
230360
}
231361

232-
/// Neither are constant.
233-
static ColumnPtr executeImpl(const IColumn & col_addr, const IColumn & col_cidr, size_t input_rows_count)
362+
template <IPKind kind>
363+
static ColumnPtr executeImpl(
364+
const IPTrait<kind>::ColumnType * col_addr,
365+
const ColumnNullable * nullable_column,
366+
const IColumn & col_cidr,
367+
size_t input_rows_count)
234368
{
235369
ColumnUInt8::MutablePtr col_res = ColumnUInt8::create(input_rows_count);
236370
ColumnUInt8::Container & vec_res = col_res->getData();
237371

238372
for (size_t i = 0; i < input_rows_count; ++i)
239373
{
240-
const auto addr = IPAddressVariant(col_addr.getDataAt(i).toView());
241374
const auto cidr = parseIPWithCIDR(col_cidr.getDataAt(i).toView());
242-
243-
vec_res[i] = isAddressInRange(addr, cidr) ? 1 : 0;
375+
if (nullable_column->isNullAt(i))
376+
vec_res[i] = 0;
377+
else
378+
{
379+
const auto addr = parseIP<kind>(col_addr, i);
380+
vec_res[i] = isAddressInRange(addr, cidr) ? 1 : 0;
381+
}
244382
}
245-
246383
return col_res;
247384
}
248385
};

tests/queries/0_stateless/01774_ip_address_in_range.sql

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ SELECT '# Wrong argument types';
5858

5959
SELECT isIPAddressInRange(100, '127.0.0.0/8'); -- { serverError ILLEGAL_TYPE_OF_ARGUMENT }
6060
SELECT isIPAddressInRange(NULL, '127.0.0.0/8'); -- { serverError ILLEGAL_TYPE_OF_ARGUMENT }
61-
SELECT isIPAddressInRange(CAST(NULL, 'Nullable(String)'), '127.0.0.0/8'); -- { serverError ILLEGAL_TYPE_OF_ARGUMENT }
6261
SELECT isIPAddressInRange('127.0.0.1', 100); -- { serverError ILLEGAL_TYPE_OF_ARGUMENT }
6362
SELECT isIPAddressInRange(100, NULL); -- { serverError ILLEGAL_TYPE_OF_ARGUMENT }
6463
WITH arrayJoin([NULL, NULL, NULL, NULL]) AS prefix SELECT isIPAddressInRange([NULL, NULL, 0, 255, 0], prefix); -- { serverError ILLEGAL_TYPE_OF_ARGUMENT }

0 commit comments

Comments
 (0)