blob: 18c2033ff8dfd39e24e8a51a22f773cfd707cb25 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
// This file is copied from
// https://github.com/ClickHouse/ClickHouse/blob/master/src/Functions/FunctionsCodingIP.cpp
// and modified by Doris
#pragma once
#include <glog/logging.h>
#include "vec/columns/column.h"
#include "vec/columns/column_string.h"
#include "vec/columns/column_vector.h"
#include "vec/common/format_ip.h"
#include "vec/core/column_with_type_and_name.h"
#include "vec/data_types/data_type_ipv6.h"
#include "vec/data_types/data_type_number.h"
#include "vec/data_types/data_type_string.h"
#include "vec/functions/function.h"
#include "vec/functions/function_helpers.h"
#include "vec/functions/simple_function_factory.h"
#include "vec/runtime/ip_address_cidr.h"
namespace doris::vectorized {
class FunctionIPv4NumToString : public IFunction {
private:
template <typename ArgType>
Status execute_type(Block& block, const ColumnWithTypeAndName& argument, size_t result) const {
using ColumnType = ColumnVector<ArgType>;
const ColumnPtr& column = argument.column;
if (const ColumnType* col = typeid_cast<const ColumnType*>(column.get())) {
const typename ColumnType::Container& vec_in = col->get_data();
auto col_res = ColumnString::create();
ColumnString::Chars& vec_res = col_res->get_chars();
ColumnString::Offsets& offsets_res = col_res->get_offsets();
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;
auto null_map = ColumnUInt8::create(vec_in.size(), 0);
size_t src_size = std::min(sizeof(ArgType), (unsigned long)4);
for (size_t i = 0; i < vec_in.size(); ++i) {
auto value = vec_in[i];
if (value < IPV4_MIN_NUM_VALUE || value > IPV4_MAX_NUM_VALUE) {
offsets_res[i] = pos - begin;
null_map->get_data()[i] = 1;
} else {
formatIPv4(reinterpret_cast<const unsigned char*>(&vec_in[i]), src_size, pos);
offsets_res[i] = pos - begin;
}
}
vec_res.resize(pos - begin);
block.replace_by_position(
result, ColumnNullable::create(std::move(col_res), std::move(null_map)));
return Status::OK();
} else
return Status::RuntimeError("Illegal column {} of argument of function {}",
argument.column->get_name(), get_name());
}
public:
static constexpr auto name = "ipv4_num_to_string";
static FunctionPtr create() { return std::make_shared<FunctionIPv4NumToString>(); }
String get_name() const override { return name; }
size_t get_number_of_arguments() const override { return 1; }
DataTypePtr get_return_type_impl(const DataTypes& arguments) const override {
return make_nullable(std::make_shared<DataTypeString>());
}
bool use_default_implementation_for_nulls() const override { return true; }
Status execute_impl(FunctionContext* context, Block& block, const ColumnNumbers& arguments,
size_t result, size_t input_rows_count) const override {
ColumnWithTypeAndName& argument = block.get_by_position(arguments[0]);
switch (argument.type->get_type_id()) {
case TypeIndex::Int8:
return execute_type<Int8>(block, argument, result);
case TypeIndex::Int16:
return execute_type<Int16>(block, argument, result);
case TypeIndex::Int32:
return execute_type<Int32>(block, argument, result);
case TypeIndex::Int64:
return execute_type<Int64>(block, argument, result);
default:
break;
}
return Status::RuntimeError(
"Illegal column {} of argument of function {}, expected Int8 or Int16 or Int32 or "
"Int64",
argument.name, get_name());
}
};
enum class IPStringToNumExceptionMode : uint8_t { Throw, Default, Null };
static inline bool tryParseIPv4(const char* pos, Int64& result_value) {
return parseIPv4whole(pos, reinterpret_cast<unsigned char*>(&result_value));
}
template <IPStringToNumExceptionMode exception_mode, typename ToColumn>
ColumnPtr convertToIPv4(ColumnPtr column, const PaddedPODArray<UInt8>* null_map = nullptr) {
const ColumnString* column_string = check_and_get_column<ColumnString>(column.get());
if (!column_string) {
throw Exception(ErrorCode::INVALID_ARGUMENT,
"Illegal column {} of argument of function {}, expected String",
column->get_name());
}
size_t column_size = column_string->size();
ColumnUInt8::MutablePtr col_null_map_to;
ColumnUInt8::Container* vec_null_map_to = nullptr;
if constexpr (exception_mode == IPStringToNumExceptionMode::Null) {
col_null_map_to = ColumnUInt8::create(column_size, false);
vec_null_map_to = &col_null_map_to->get_data();
}
auto col_res = ToColumn::create();
auto& vec_res = col_res->get_data();
vec_res.resize(column_size);
const ColumnString::Chars& vec_src = column_string->get_chars();
const ColumnString::Offsets& offsets_src = column_string->get_offsets();
size_t prev_offset = 0;
for (size_t i = 0; i < vec_res.size(); ++i) {
if (null_map && (*null_map)[i]) {
vec_res[i] = 0;
prev_offset = offsets_src[i];
if constexpr (exception_mode == IPStringToNumExceptionMode::Null) {
(*vec_null_map_to)[i] = true;
}
continue;
}
const char* src_start = reinterpret_cast<const char*>(&vec_src[prev_offset]);
size_t src_length = (i < vec_res.size() - 1) ? (offsets_src[i] - prev_offset)
: (vec_src.size() - prev_offset);
std::string src(src_start, src_length);
bool parse_result = tryParseIPv4(src.c_str(), vec_res[i]);
if (!parse_result) {
if constexpr (exception_mode == IPStringToNumExceptionMode::Throw) {
throw Exception(ErrorCode::INVALID_ARGUMENT, "Invalid IPv4 value");
} else if constexpr (exception_mode == IPStringToNumExceptionMode::Default) {
vec_res[i] = 0;
} else if constexpr (exception_mode == IPStringToNumExceptionMode::Null) {
(*vec_null_map_to)[i] = true;
vec_res[i] = 0;
}
}
prev_offset = offsets_src[i];
}
if constexpr (exception_mode == IPStringToNumExceptionMode::Null)
return ColumnNullable::create(std::move(col_res), std::move(col_null_map_to));
return col_res;
}
template <IPStringToNumExceptionMode exception_mode>
class FunctionIPv4StringToNum : public IFunction {
public:
static constexpr auto name = exception_mode == IPStringToNumExceptionMode::Throw
? "ipv4_string_to_num"
: (exception_mode == IPStringToNumExceptionMode::Default
? "ipv4_string_to_num_or_default"
: "ipv4_string_to_num_or_null");
static FunctionPtr create() {
return std::make_shared<FunctionIPv4StringToNum<exception_mode>>();
}
String get_name() const override { return name; }
size_t get_number_of_arguments() const override { return 1; }
DataTypePtr get_return_type_impl(const DataTypes& arguments) const override {
if (!is_string(remove_nullable(arguments[0]))) {
throw Exception(ErrorCode::INVALID_ARGUMENT,
"Illegal type {} of argument of function {}", arguments[0]->get_name(),
get_name());
}
auto result_type = std::make_shared<DataTypeInt64>();
if constexpr (exception_mode == IPStringToNumExceptionMode::Null) {
return make_nullable(result_type);
}
return arguments[0]->is_nullable() ? make_nullable(result_type) : result_type;
}
bool use_default_implementation_for_nulls() const override { return false; }
Status execute_impl(FunctionContext* context, Block& block, const ColumnNumbers& arguments,
size_t result, size_t input_rows_count) const override {
ColumnPtr column = block.get_by_position(arguments[0]).column;
ColumnPtr null_map_column;
const NullMap* null_map = nullptr;
if (column->is_nullable()) {
const auto* column_nullable = assert_cast<const ColumnNullable*>(column.get());
column = column_nullable->get_nested_column_ptr();
null_map_column = column_nullable->get_null_map_column_ptr();
null_map = &column_nullable->get_null_map_data();
}
auto col_res = convertToIPv4<exception_mode, ColumnInt64>(column, null_map);
if (null_map && !col_res->is_nullable()) {
block.replace_by_position(result,
ColumnNullable::create(IColumn::mutate(col_res),
IColumn::mutate(null_map_column)));
return Status::OK();
}
block.replace_by_position(result, col_res);
return Status::OK();
}
};
template <typename T>
void process_ipv6_column(const ColumnPtr& column, size_t input_rows_count,
ColumnString::Chars& vec_res, ColumnString::Offsets& offsets_res,
ColumnUInt8::MutablePtr& null_map, unsigned char* ipv6_address_data) {
auto* begin = reinterpret_cast<char*>(vec_res.data());
auto* pos = begin;
const auto* col = check_and_get_column<T>(column.get());
for (size_t i = 0; i < input_rows_count; ++i) {
bool is_empty = false;
if constexpr (std::is_same_v<T, ColumnIPv6>) {
const auto& vec_in = col->get_data();
memcpy(ipv6_address_data, reinterpret_cast<const unsigned char*>(&vec_in[i]),
IPV6_BINARY_LENGTH);
} else {
const auto str_ref = col->get_data_at(i);
const char* value = str_ref.data;
size_t value_size = str_ref.size;
if (value_size > IPV6_BINARY_LENGTH || value == nullptr || value_size == 0) {
is_empty = true;
} else {
memcpy(ipv6_address_data, value, value_size);
memset(ipv6_address_data + value_size, 0, IPV6_BINARY_LENGTH - value_size);
}
}
if (is_empty) {
offsets_res[i] = pos - begin;
null_map->get_data()[i] = 1;
} else {
formatIPv6(ipv6_address_data, pos);
offsets_res[i] = pos - begin;
}
}
}
class FunctionIPv6NumToString : public IFunction {
public:
static constexpr auto name = "ipv6_num_to_string";
static FunctionPtr create() { return std::make_shared<FunctionIPv6NumToString>(); }
String get_name() const override { return name; }
size_t get_number_of_arguments() const override { return 1; }
DataTypePtr get_return_type_impl(const DataTypes& arguments) const override {
const auto* arg_string = check_and_get_data_type<DataTypeString>(arguments[0].get());
const auto* arg_ipv6 = check_and_get_data_type<DataTypeIPv6>(arguments[0].get());
if (!arg_ipv6 && !(arg_string))
throw Exception(ErrorCode::INVALID_ARGUMENT,
"Illegal type {} of argument of function {}, expected IPv6 or String",
arguments[0]->get_name(), get_name());
return make_nullable(std::make_shared<DataTypeString>());
}
bool use_default_implementation_for_nulls() const override { return true; }
Status execute_impl(FunctionContext* context, Block& block, const ColumnNumbers& arguments,
size_t result, size_t input_rows_count) const override {
const ColumnPtr& column = block.get_by_position(arguments[0]).column;
const auto* col_ipv6 = check_and_get_column<ColumnIPv6>(column.get());
const auto* col_string = check_and_get_column<ColumnString>(column.get());
if (!col_ipv6 && !col_string)
throw Exception(ErrorCode::INVALID_ARGUMENT,
"Illegal column {} of argument of function {}, expected IPv6 or String",
column->get_name(), get_name());
auto col_res = ColumnString::create();
ColumnString::Chars& vec_res = col_res->get_chars();
ColumnString::Offsets& offsets_res = col_res->get_offsets();
vec_res.resize(input_rows_count * (IPV6_MAX_TEXT_LENGTH + 1));
offsets_res.resize(input_rows_count);
auto null_map = ColumnUInt8::create(input_rows_count, 0);
unsigned char ipv6_address_data[IPV6_BINARY_LENGTH];
if (col_ipv6) {
process_ipv6_column<ColumnIPv6>(column, input_rows_count, vec_res, offsets_res,
null_map, ipv6_address_data);
} else {
process_ipv6_column<ColumnString>(column, input_rows_count, vec_res, offsets_res,
null_map, ipv6_address_data);
}
block.replace_by_position(result,
ColumnNullable::create(std::move(col_res), std::move(null_map)));
return Status::OK();
}
};
namespace detail {
template <IPStringToNumExceptionMode exception_mode, typename ToColumn = ColumnIPv6,
typename StringColumnType>
ColumnPtr convertToIPv6(const StringColumnType& string_column,
const PaddedPODArray<UInt8>* null_map = nullptr) {
if constexpr (!std::is_same_v<ToColumn, ColumnString> &&
!std::is_same_v<ToColumn, ColumnIPv6>) {
throw Exception(ErrorCode::INVALID_ARGUMENT,
"Illegal return column type {}. Expected IPv6 or String",
TypeName<typename ToColumn::ValueType>::get());
}
const size_t column_size = string_column.size();
ColumnUInt8::MutablePtr col_null_map_to;
ColumnUInt8::Container* vec_null_map_to = nullptr;
if constexpr (exception_mode == IPStringToNumExceptionMode::Null) {
col_null_map_to = ColumnUInt8::create(column_size, false);
vec_null_map_to = &col_null_map_to->get_data();
}
/// This is a special treatment for source column of type String
/// to preserve previous behavior when IPv6 was a domain type of String
if constexpr (std::is_same_v<StringColumnType, ColumnString>) {
if (string_column.get_offsets()[0] - 1 == IPV6_BINARY_LENGTH) {
if constexpr (std::is_same_v<ToColumn, ColumnString>) {
auto col_res = ColumnString::create();
if constexpr (exception_mode == IPStringToNumExceptionMode::Null) {
col_null_map_to = ColumnUInt8::create(column_size, false);
if (null_map) {
memcpy(col_null_map_to->get_data().data(), null_map->data(), column_size);
}
return ColumnNullable::create(std::move(col_res), std::move(col_null_map_to));
}
return col_res;
} else {
auto col_res = ColumnIPv6::create();
auto& vec_res = col_res->get_data();
vec_res.resize(column_size);
memcpy(vec_res.data(), string_column.get_chars().data(),
column_size * IPV6_BINARY_LENGTH);
if constexpr (exception_mode == IPStringToNumExceptionMode::Null) {
col_null_map_to = ColumnUInt8::create(column_size, false);
if (null_map) {
memcpy(col_null_map_to->get_data().data(), null_map->data(), column_size);
}
return ColumnNullable::create(std::move(col_res), std::move(col_null_map_to));
}
return col_res;
}
}
}
auto column_create = [](size_t column_size) -> typename ToColumn::MutablePtr {
if constexpr (std::is_same_v<ToColumn, ColumnString>) {
auto column_string = ColumnString::create();
column_string->get_chars().reserve(column_size * IPV6_BINARY_LENGTH);
column_string->get_offsets().reserve(column_size);
return column_string;
} else {
return ColumnIPv6::create();
}
};
auto get_vector = [](auto& col_res, size_t col_size) -> decltype(auto) {
if constexpr (std::is_same_v<ToColumn, ColumnString>) {
auto& vec_res = col_res->get_chars();
vec_res.resize(col_size * IPV6_BINARY_LENGTH);
return (vec_res);
} else {
auto& vec_res = col_res->get_data();
vec_res.resize(col_size);
return (vec_res);
}
};
auto col_res = column_create(column_size);
auto& vec_res = get_vector(col_res, column_size);
using Chars = typename StringColumnType::Chars;
const Chars& vec_src = string_column.get_chars();
size_t src_offset = 0;
char src_ipv4_buf[sizeof("::ffff:") + IPV4_MAX_TEXT_LENGTH + 1] = "::ffff:";
/// ColumnString contains not null terminated strings. But functions parseIPv6, parseIPv4 expect null terminated string.
/// TODO fix this - now parseIPv6/parseIPv4 accept end iterator, so can be parsed in-place
std::string string_buffer;
int offset_inc = 1;
if constexpr (std::is_same_v<ToColumn, ColumnString>) {
offset_inc = IPV6_BINARY_LENGTH;
}
for (size_t out_offset = 0, i = 0; i < column_size; out_offset += offset_inc, ++i) {
size_t src_next_offset = src_offset;
const char* src_value = nullptr;
auto* res_value = reinterpret_cast<unsigned char*>(&vec_res[out_offset]);
if constexpr (std::is_same_v<StringColumnType, ColumnString>) {
src_value = reinterpret_cast<const char*>(&vec_src[src_offset]);
src_next_offset = string_column.get_offsets()[i];
string_buffer.assign(src_value, src_next_offset - src_offset);
src_value = string_buffer.c_str();
}
if (null_map && (*null_map)[i]) {
if (exception_mode == IPStringToNumExceptionMode::Throw) {
throw Exception(ErrorCode::INVALID_ARGUMENT, "Invalid IPv6 value");
} else if (exception_mode == IPStringToNumExceptionMode::Default) {
std::fill_n(&vec_res[out_offset], offset_inc, 0);
} else {
std::fill_n(&vec_res[out_offset], offset_inc, 0);
(*vec_null_map_to)[i] = true;
if constexpr (std::is_same_v<ToColumn, ColumnString>) {
auto* column_string = assert_cast<ColumnString*>(col_res.get());
column_string->get_offsets().push_back((i + 1) * IPV6_BINARY_LENGTH);
}
}
src_offset = src_next_offset;
continue;
}
bool parse_result = false;
Int64 dummy_result = 0;
/// For both cases below: In case of failure, the function parseIPv6 fills vec_res with zero bytes.
/// If the source IP address is parsable as an IPv4 address, then transform it into a valid IPv6 address.
/// Keeping it simple by just prefixing `::ffff:` to the IPv4 address to represent it as a valid IPv6 address.
size_t string_length = src_next_offset - src_offset;
if (string_length != 0) {
if (tryParseIPv4(src_value, dummy_result)) {
strcat(src_ipv4_buf, src_value);
parse_result = parseIPv6whole(src_ipv4_buf, res_value);
} else {
parse_result = parseIPv6whole(src_value, res_value);
}
}
if (parse_result && string_length != 0) {
if constexpr (std::is_same_v<ToColumn, ColumnString>) {
auto* column_string = assert_cast<ColumnString*>(col_res.get());
std::copy(res_value, res_value + IPV6_BINARY_LENGTH,
column_string->get_chars().begin() + i * IPV6_BINARY_LENGTH);
column_string->get_offsets().push_back((i + 1) * IPV6_BINARY_LENGTH);
} else {
col_res->insert_data(reinterpret_cast<const char*>(res_value), IPV6_BINARY_LENGTH);
}
} else {
if (exception_mode == IPStringToNumExceptionMode::Throw) {
throw Exception(ErrorCode::INVALID_ARGUMENT, "Invalid IPv6 value");
}
std::fill_n(&vec_res[out_offset], offset_inc, 0);
if constexpr (std::is_same_v<ToColumn, ColumnString>) {
auto* column_string = assert_cast<ColumnString*>(col_res.get());
column_string->get_offsets().push_back((i + 1) * IPV6_BINARY_LENGTH);
}
if constexpr (exception_mode == IPStringToNumExceptionMode::Null) {
(*vec_null_map_to)[i] = true;
}
}
src_offset = src_next_offset;
}
if constexpr (exception_mode == IPStringToNumExceptionMode::Null) {
return ColumnNullable::create(std::move(col_res), std::move(col_null_map_to));
}
return col_res;
}
} // namespace detail
template <IPStringToNumExceptionMode exception_mode, typename ToColumn = ColumnIPv6>
ColumnPtr convertToIPv6(ColumnPtr column, const PaddedPODArray<UInt8>* null_map = nullptr) {
if (const auto* column_input_string = check_and_get_column<ColumnString>(column.get())) {
auto result =
detail::convertToIPv6<exception_mode, ToColumn>(*column_input_string, null_map);
return result;
} else {
throw Exception(ErrorCode::INVALID_ARGUMENT, "Illegal column type {}. Expected String",
column->get_name());
}
}
template <IPStringToNumExceptionMode exception_mode>
class FunctionIPv6StringToNum : public IFunction {
public:
static constexpr auto name = exception_mode == IPStringToNumExceptionMode::Throw
? "ipv6_string_to_num"
: (exception_mode == IPStringToNumExceptionMode::Default
? "ipv6_string_to_num_or_default"
: "ipv6_string_to_num_or_null");
static FunctionPtr create() {
return std::make_shared<FunctionIPv6StringToNum<exception_mode>>();
}
String get_name() const override { return name; }
size_t get_number_of_arguments() const override { return 1; }
bool use_default_implementation_for_nulls() const override { return false; }
DataTypePtr get_return_type_impl(const DataTypes& arguments) const override {
if (!is_string(remove_nullable(arguments[0]))) {
throw Exception(ErrorCode::INVALID_ARGUMENT,
"Illegal type {} of argument of function {}", arguments[0]->get_name(),
get_name());
}
auto result_type = std::make_shared<DataTypeString>();
if constexpr (exception_mode == IPStringToNumExceptionMode::Null) {
return make_nullable(result_type);
}
return result_type;
}
Status execute_impl(FunctionContext* context, Block& block, const ColumnNumbers& arguments,
size_t result, size_t input_rows_count) const override {
ColumnPtr column = block.get_by_position(arguments[0]).column;
ColumnPtr null_map_column;
const NullMap* null_map = nullptr;
if (column->is_nullable()) {
const auto* column_nullable = assert_cast<const ColumnNullable*>(column.get());
column = column_nullable->get_nested_column_ptr();
if constexpr (exception_mode == IPStringToNumExceptionMode::Null) {
null_map_column = column_nullable->get_null_map_column_ptr();
null_map = &column_nullable->get_null_map_data();
}
}
auto col_res = convertToIPv6<exception_mode, ColumnString>(column, null_map);
if (null_map && !col_res->is_nullable()) {
block.replace_by_position(result,
ColumnNullable::create(IColumn::mutate(col_res),
IColumn::mutate(null_map_column)));
return Status::OK();
}
block.replace_by_position(result, col_res);
return Status::OK();
}
};
class FunctionIsIPv4String : public IFunction {
private:
Status execute_type(Block& block, const ColumnWithTypeAndName& argument, size_t result) const {
const ColumnPtr& column = argument.column;
if (const auto* nullable_src = typeid_cast<const ColumnNullable*>(column.get())) {
size_t col_size = nullable_src->size();
auto col_res = ColumnUInt8::create(col_size, 0);
auto null_map = ColumnUInt8::create(col_size, 0);
auto& col_res_data = col_res->get_data();
auto& null_map_data = null_map->get_data();
for (size_t i = 0; i < col_size; ++i) {
if (nullable_src->is_null_at(i)) {
null_map_data[i] = 1;
} else {
StringRef ipv4_str = nullable_src->get_data_at(i);
if (IPv4Value::is_valid_string(ipv4_str.data, ipv4_str.size)) {
col_res_data[i] = 1;
}
}
}
block.replace_by_position(
result, ColumnNullable::create(std::move(col_res), std::move(null_map)));
return Status::OK();
} else if (const auto* col_src = typeid_cast<const ColumnString*>(column.get())) {
size_t col_size = col_src->size();
auto col_res = ColumnUInt8::create(col_size, 0);
auto null_map = ColumnUInt8::create(col_size, 0);
auto& col_res_data = col_res->get_data();
for (size_t i = 0; i < col_size; ++i) {
StringRef ipv4_str = col_src->get_data_at(i);
if (IPv4Value::is_valid_string(ipv4_str.data, ipv4_str.size)) {
col_res_data[i] = 1;
}
}
block.replace_by_position(
result, ColumnNullable::create(std::move(col_res), std::move(null_map)));
return Status::OK();
} else {
return Status::RuntimeError("Illegal column {} of argument of function {}",
argument.column->get_name(), get_name());
}
}
public:
static constexpr auto name = "is_ipv4_string";
static FunctionPtr create() { return std::make_shared<FunctionIsIPv4String>(); }
String get_name() const override { return name; }
size_t get_number_of_arguments() const override { return 1; }
DataTypePtr get_return_type_impl(const DataTypes& arguments) const override {
return make_nullable(std::make_shared<DataTypeUInt8>());
}
bool use_default_implementation_for_nulls() const override { return true; }
Status execute_impl(FunctionContext* context, Block& block, const ColumnNumbers& arguments,
size_t result, size_t input_rows_count) const override {
ColumnWithTypeAndName& argument = block.get_by_position(arguments[0]);
DCHECK(argument.type->get_type_id() == TypeIndex::String);
return execute_type(block, argument, result);
}
};
class FunctionIsIPv6String : public IFunction {
private:
Status execute_type(Block& block, const ColumnWithTypeAndName& argument, size_t result) const {
const ColumnPtr& column = argument.column;
if (const auto* nullable_src = typeid_cast<const ColumnNullable*>(column.get())) {
size_t col_size = nullable_src->size();
auto col_res = ColumnUInt8::create(col_size, 0);
auto null_map = ColumnUInt8::create(col_size, 0);
auto& col_res_data = col_res->get_data();
auto& null_map_data = null_map->get_data();
for (size_t i = 0; i < col_size; ++i) {
if (nullable_src->is_null_at(i)) {
null_map_data[i] = 1;
} else {
StringRef ipv6_str = nullable_src->get_data_at(i);
if (IPv6Value::is_valid_string(ipv6_str.data, ipv6_str.size)) {
col_res_data[i] = 1;
}
}
}
block.replace_by_position(
result, ColumnNullable::create(std::move(col_res), std::move(null_map)));
return Status::OK();
} else if (const auto* col_src = typeid_cast<const ColumnString*>(column.get())) {
size_t col_size = col_src->size();
auto col_res = ColumnUInt8::create(col_size, 0);
auto null_map = ColumnUInt8::create(col_size, 0);
auto& col_res_data = col_res->get_data();
for (size_t i = 0; i < col_size; ++i) {
StringRef ipv6_str = col_src->get_data_at(i);
if (IPv6Value::is_valid_string(ipv6_str.data, ipv6_str.size)) {
col_res_data[i] = 1;
}
}
block.replace_by_position(
result, ColumnNullable::create(std::move(col_res), std::move(null_map)));
return Status::OK();
} else {
return Status::RuntimeError("Illegal column {} of argument of function {}",
argument.column->get_name(), get_name());
}
}
public:
static constexpr auto name = "is_ipv6_string";
static FunctionPtr create() { return std::make_shared<FunctionIsIPv6String>(); }
String get_name() const override { return name; }
size_t get_number_of_arguments() const override { return 1; }
DataTypePtr get_return_type_impl(const DataTypes& arguments) const override {
return make_nullable(std::make_shared<DataTypeUInt8>());
}
bool use_default_implementation_for_nulls() const override { return true; }
Status execute_impl(FunctionContext* context, Block& block, const ColumnNumbers& arguments,
size_t result, size_t input_rows_count) const override {
ColumnWithTypeAndName& argument = block.get_by_position(arguments[0]);
DCHECK(argument.type->get_type_id() == TypeIndex::String);
return execute_type(block, argument, result);
}
};
class FunctionIsIPAddressInRange : public IFunction {
public:
static constexpr auto name = "is_ip_address_in_range";
static FunctionPtr create() { return std::make_shared<FunctionIsIPAddressInRange>(); }
String get_name() const override { return name; }
size_t get_number_of_arguments() const override { return 2; }
DataTypePtr get_return_type_impl(const DataTypes& arguments) const override {
if (arguments.size() != 2) {
throw Exception(
ErrorCode::INVALID_ARGUMENT,
"Number of arguments for function {} doesn't match: passed {}, should be 2",
get_name(), arguments.size());
}
const auto& addr_type = arguments[0];
const auto& cidr_type = arguments[1];
if (!is_string(remove_nullable(addr_type)) || !is_string(remove_nullable(cidr_type))) {
throw Exception(ErrorCode::INVALID_ARGUMENT,
"The arguments of function {} must be String", get_name());
}
return std::make_shared<DataTypeUInt8>();
}
bool use_default_implementation_for_nulls() const override { return false; }
Status execute_impl(FunctionContext* context, Block& block, const ColumnNumbers& arguments,
size_t result, size_t input_rows_count) const override {
ColumnPtr& addr_column = block.get_by_position(arguments[0]).column;
ColumnPtr& cidr_column = block.get_by_position(arguments[1]).column;
const ColumnString* str_addr_column = nullptr;
const ColumnString* str_cidr_column = nullptr;
const NullMap* nullmap_addr = nullptr;
const NullMap* nullmap_cidr = nullptr;
if (addr_column->is_nullable()) {
const auto* addr_column_nullable =
assert_cast<const ColumnNullable*>(addr_column.get());
str_addr_column =
check_and_get_column<ColumnString>(addr_column_nullable->get_nested_column());
nullmap_addr = &addr_column_nullable->get_null_map_data();
} else {
str_addr_column = check_and_get_column<ColumnString>(addr_column.get());
}
if (cidr_column->is_nullable()) {
const auto* cidr_column_nullable =
assert_cast<const ColumnNullable*>(cidr_column.get());
str_cidr_column =
check_and_get_column<ColumnString>(cidr_column_nullable->get_nested_column());
nullmap_cidr = &cidr_column_nullable->get_null_map_data();
} else {
str_cidr_column = check_and_get_column<ColumnString>(cidr_column.get());
}
if (!str_addr_column) {
throw Exception(ErrorCode::INVALID_ARGUMENT,
"Illegal column {} of argument of function {}, expected String",
addr_column->get_name(), get_name());
}
if (!str_cidr_column) {
throw Exception(ErrorCode::INVALID_ARGUMENT,
"Illegal column {} of argument of function {}, expected String",
cidr_column->get_name(), get_name());
}
auto col_res = ColumnUInt8::create(input_rows_count, 0);
auto& col_res_data = col_res->get_data();
for (size_t i = 0; i < input_rows_count; ++i) {
if (nullmap_addr && (*nullmap_addr)[i]) {
throw Exception(ErrorCode::INVALID_ARGUMENT,
"The arguments of function {} must be String, not NULL",
get_name());
}
if (nullmap_cidr && (*nullmap_cidr)[i]) {
throw Exception(ErrorCode::INVALID_ARGUMENT,
"The arguments of function {} must be String, not NULL",
get_name());
}
const auto addr = IPAddressVariant(str_addr_column->get_data_at(i).to_string_view());
const auto cidr = parse_ip_with_cidr(str_cidr_column->get_data_at(i).to_string_view());
col_res_data[i] = is_address_in_range(addr, cidr) ? 1 : 0;
}
block.replace_by_position(result, std::move(col_res));
return Status::OK();
}
};
} // namespace doris::vectorized