[Refactor](function) Reconstruct default logic for const args. (#17830)
This commit is contained in:
@ -280,12 +280,12 @@ template <typename Transform>
|
||||
struct TransformerToStringTwoArgument {
|
||||
static void vector_constant(FunctionContext* context,
|
||||
const PaddedPODArray<typename Transform::FromType>& ts,
|
||||
const std::string& format, ColumnString::Chars& res_data,
|
||||
const StringRef& format, ColumnString::Chars& res_data,
|
||||
ColumnString::Offsets& res_offsets,
|
||||
PaddedPODArray<UInt8>& null_map) {
|
||||
auto len = ts.size();
|
||||
res_offsets.resize(len);
|
||||
res_data.reserve(len * format.size() + len);
|
||||
res_data.reserve(len * format.size + len);
|
||||
null_map.resize_fill(len, false);
|
||||
|
||||
size_t offset = 0;
|
||||
@ -294,12 +294,10 @@ struct TransformerToStringTwoArgument {
|
||||
size_t new_offset;
|
||||
bool is_null;
|
||||
if constexpr (is_specialization_of_v<Transform, FromUnixTimeImpl>) {
|
||||
std::tie(new_offset, is_null) =
|
||||
Transform::execute(t, StringRef(format.c_str(), format.size()), res_data,
|
||||
offset, context->state()->timezone_obj());
|
||||
} else {
|
||||
std::tie(new_offset, is_null) = Transform::execute(
|
||||
t, StringRef(format.c_str(), format.size()), res_data, offset);
|
||||
t, format, res_data, offset, context->state()->timezone_obj());
|
||||
} else {
|
||||
std::tie(new_offset, is_null) = Transform::execute(t, format, res_data, offset);
|
||||
}
|
||||
res_offsets[i] = new_offset;
|
||||
null_map[i] = is_null;
|
||||
|
||||
@ -128,8 +128,8 @@ NullPresence get_null_presence(const Block& block, const ColumnNumbers& args) {
|
||||
return res;
|
||||
}
|
||||
|
||||
bool allArgumentsAreConstants(const Block& block, const ColumnNumbers& args) {
|
||||
for (auto arg : args) {
|
||||
bool all_arguments_are_constant(const Block& block, const ColumnNumbers& args) {
|
||||
for (const auto& arg : args) {
|
||||
if (!is_column_const(*block.get_by_position(arg).column)) {
|
||||
return false;
|
||||
}
|
||||
@ -138,53 +138,51 @@ bool allArgumentsAreConstants(const Block& block, const ColumnNumbers& args) {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
inline Status PreparedFunctionImpl::_execute_skipped_constant_deal(
|
||||
FunctionContext* context, Block& block, const ColumnNumbers& args, size_t result,
|
||||
size_t input_rows_count, bool dry_run) {
|
||||
bool executed = false;
|
||||
RETURN_IF_ERROR(default_implementation_for_nulls(context, block, args, result, input_rows_count,
|
||||
dry_run, &executed));
|
||||
if (executed) {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
if (dry_run) {
|
||||
return execute_impl_dry_run(context, block, args, result, input_rows_count);
|
||||
} else {
|
||||
return execute_impl(context, block, args, result, input_rows_count);
|
||||
}
|
||||
}
|
||||
|
||||
Status PreparedFunctionImpl::default_implementation_for_constant_arguments(
|
||||
FunctionContext* context, Block& block, const ColumnNumbers& args, size_t result,
|
||||
size_t input_rows_count, bool dry_run, bool* executed) {
|
||||
*executed = false;
|
||||
ColumnNumbers arguments_to_remain_constants = get_arguments_that_are_always_constant();
|
||||
ColumnNumbers args_expect_const = get_arguments_that_are_always_constant();
|
||||
|
||||
/// Check that these arguments are really constant.
|
||||
for (auto arg_num : arguments_to_remain_constants) {
|
||||
// Check that these arguments are really constant.
|
||||
for (auto arg_num : args_expect_const) {
|
||||
if (arg_num < args.size() &&
|
||||
!is_column_const(*block.get_by_position(args[arg_num]).column)) {
|
||||
return Status::RuntimeError("Argument at index {} for function {} must be constant",
|
||||
arg_num, get_name());
|
||||
return Status::InvalidArgument("Argument at index {} for function {} must be constant",
|
||||
arg_num, get_name());
|
||||
}
|
||||
}
|
||||
|
||||
if (args.empty() || !use_default_implementation_for_constants() ||
|
||||
!allArgumentsAreConstants(block, args)) {
|
||||
!all_arguments_are_constant(block, args)) {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
// now all columns is const.
|
||||
Block temporary_block;
|
||||
bool have_converted_columns = false;
|
||||
|
||||
size_t arguments_size = args.size();
|
||||
for (size_t arg_num = 0; arg_num < arguments_size; ++arg_num) {
|
||||
const ColumnWithTypeAndName& column = block.get_by_position(args[arg_num]);
|
||||
|
||||
if (arguments_to_remain_constants.end() != std::find(arguments_to_remain_constants.begin(),
|
||||
arguments_to_remain_constants.end(),
|
||||
arg_num)) {
|
||||
temporary_block.insert({column.column->clone_resized(1), column.type, column.name});
|
||||
} else {
|
||||
have_converted_columns = true;
|
||||
temporary_block.insert(
|
||||
{assert_cast<const ColumnConst*>(column.column.get())->get_data_column_ptr(),
|
||||
column.type, column.name});
|
||||
}
|
||||
}
|
||||
|
||||
/** When using default implementation for constants, the function requires at least one argument
|
||||
* not in "arguments_to_remain_constants" set. Otherwise we get infinite recursion.
|
||||
*/
|
||||
if (!have_converted_columns) {
|
||||
return Status::RuntimeError(
|
||||
"Number of arguments for function {} doesn't match: the function "
|
||||
"requires more arguments",
|
||||
get_name());
|
||||
temporary_block.insert(
|
||||
{assert_cast<const ColumnConst*>(column.column.get())->get_data_column_ptr(),
|
||||
column.type, column.name});
|
||||
}
|
||||
|
||||
temporary_block.insert(block.get_by_position(result));
|
||||
@ -194,9 +192,9 @@ Status PreparedFunctionImpl::default_implementation_for_constant_arguments(
|
||||
temporary_argument_numbers[i] = i;
|
||||
}
|
||||
|
||||
RETURN_IF_ERROR(execute_without_low_cardinality_columns(
|
||||
context, temporary_block, temporary_argument_numbers, arguments_size,
|
||||
temporary_block.rows(), dry_run));
|
||||
RETURN_IF_ERROR(_execute_skipped_constant_deal(context, temporary_block,
|
||||
temporary_argument_numbers, arguments_size,
|
||||
temporary_block.rows(), dry_run));
|
||||
|
||||
ColumnPtr result_column;
|
||||
/// extremely rare case, when we have function with completely const arguments
|
||||
@ -248,21 +246,14 @@ Status PreparedFunctionImpl::execute_without_low_cardinality_columns(
|
||||
FunctionContext* context, Block& block, const ColumnNumbers& args, size_t result,
|
||||
size_t input_rows_count, bool dry_run) {
|
||||
bool executed = false;
|
||||
|
||||
RETURN_IF_ERROR(default_implementation_for_constant_arguments(
|
||||
context, block, args, result, input_rows_count, dry_run, &executed));
|
||||
if (executed) {
|
||||
return Status::OK();
|
||||
}
|
||||
RETURN_IF_ERROR(default_implementation_for_nulls(context, block, args, result, input_rows_count,
|
||||
dry_run, &executed));
|
||||
if (executed) {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
if (dry_run)
|
||||
return execute_impl_dry_run(context, block, args, result, input_rows_count);
|
||||
else
|
||||
return execute_impl(context, block, args, result, input_rows_count);
|
||||
return _execute_skipped_constant_deal(context, block, args, result, input_rows_count, dry_run);
|
||||
}
|
||||
|
||||
Status PreparedFunctionImpl::execute(FunctionContext* context, Block& block,
|
||||
|
||||
@ -90,7 +90,7 @@ protected:
|
||||
|
||||
/** If the function have non-zero number of arguments,
|
||||
* and if all arguments are constant, that we could automatically provide default implementation:
|
||||
* arguments are converted to ordinary columns with single value, then function is executed as usual,
|
||||
* arguments are converted to ordinary columns with single value which is not const, then function is executed as usual,
|
||||
* and then the result is converted to constant column.
|
||||
*/
|
||||
virtual bool use_default_implementation_for_constants() const { return false; }
|
||||
@ -102,6 +102,7 @@ protected:
|
||||
virtual bool use_default_implementation_for_low_cardinality_columns() const { return true; }
|
||||
|
||||
/** Some arguments could remain constant during this implementation.
|
||||
* Every argument required const must write here and no checks elsewhere.
|
||||
*/
|
||||
virtual ColumnNumbers get_arguments_that_are_always_constant() const { return {}; }
|
||||
|
||||
@ -116,6 +117,9 @@ private:
|
||||
Status execute_without_low_cardinality_columns(FunctionContext* context, Block& block,
|
||||
const ColumnNumbers& arguments, size_t result,
|
||||
size_t input_rows_count, bool dry_run);
|
||||
Status _execute_skipped_constant_deal(FunctionContext* context, Block& block,
|
||||
const ColumnNumbers& args, size_t result,
|
||||
size_t input_rows_count, bool dry_run);
|
||||
};
|
||||
|
||||
/// Function with known arguments and return type.
|
||||
|
||||
@ -20,6 +20,7 @@
|
||||
#include "vec/columns/column_const.h"
|
||||
#include "vec/columns/column_nullable.h"
|
||||
#include "vec/columns/columns_number.h"
|
||||
#include "vec/common/string_ref.h"
|
||||
#include "vec/data_types/data_type_date.h"
|
||||
#include "vec/data_types/data_type_date_time.h"
|
||||
#include "vec/data_types/data_type_nullable.h"
|
||||
@ -72,21 +73,15 @@ public:
|
||||
|
||||
if (arguments.size() == 2) {
|
||||
const IColumn& source_col1 = *block.get_by_position(arguments[1]).column;
|
||||
if (const auto* delta_const_column =
|
||||
typeid_cast<const ColumnConst*>(&source_col1)) {
|
||||
TransformerToStringTwoArgument<Transform>::vector_constant(
|
||||
context, sources->get_data(),
|
||||
delta_const_column->get_field().get<String>(), col_res->get_chars(),
|
||||
col_res->get_offsets(), vec_null_map_to);
|
||||
} else {
|
||||
return Status::InternalError(
|
||||
"Illegal column {} is not const {}",
|
||||
block.get_by_position(arguments[1]).column->get_name(), name);
|
||||
}
|
||||
} else {
|
||||
StringRef formatter =
|
||||
source_col1.get_data_at(0); // for both ColumnString or ColumnConst.
|
||||
TransformerToStringTwoArgument<Transform>::vector_constant(
|
||||
context, sources->get_data(), "%Y-%m-%d %H:%i:%s", col_res->get_chars(),
|
||||
context, sources->get_data(), formatter, col_res->get_chars(),
|
||||
col_res->get_offsets(), vec_null_map_to);
|
||||
} else { //default argument
|
||||
TransformerToStringTwoArgument<Transform>::vector_constant(
|
||||
context, sources->get_data(), StringRef("%Y-%m-%d %H:%i:%s"),
|
||||
col_res->get_chars(), col_res->get_offsets(), vec_null_map_to);
|
||||
}
|
||||
|
||||
if (nullable_column) {
|
||||
|
||||
@ -1762,6 +1762,7 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: need to be rewrited in a more efficient way. compare BMH/KMP/...
|
||||
size_t split_str(size_t& pos, const StringRef str_ref, StringRef delimiter_ref) {
|
||||
size_t old_size = pos;
|
||||
size_t str_size = str_ref.size;
|
||||
|
||||
@ -20,6 +20,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vec/columns/column_const.h"
|
||||
#include "vec/columns/columns_number.h"
|
||||
#ifdef __SSE4_1__
|
||||
#include <smmintrin.h>
|
||||
#else
|
||||
@ -500,17 +502,18 @@ public:
|
||||
|
||||
static Status get_scale_arg(const ColumnWithTypeAndName& arguments, Int16* scale) {
|
||||
const IColumn& scale_column = *arguments.column;
|
||||
if (!is_column_const(scale_column)) {
|
||||
return Status::InvalidArgument("2nd argument for function {} should be constant", name);
|
||||
}
|
||||
|
||||
Field scale_field = assert_cast<const ColumnConst&>(scale_column).get_field();
|
||||
Int32 scale64 = static_cast<const ColumnInt32*>(
|
||||
&(is_column_const(scale_column)
|
||||
? static_cast<const ColumnConst*>(&scale_column)
|
||||
->get_data_column()
|
||||
: scale_column))
|
||||
->get_element(0);
|
||||
|
||||
Int64 scale64 = scale_field.get<Int64>();
|
||||
if (scale64 > std::numeric_limits<Int16>::max() ||
|
||||
scale64 < std::numeric_limits<Int16>::min()) {
|
||||
return Status::InvalidArgument("Scale argument for function {} is too large: {}", name,
|
||||
scale64);
|
||||
return Status::InvalidArgument("Scale argument for function {} is out of bound: {}",
|
||||
name, scale64);
|
||||
}
|
||||
|
||||
*scale = scale64;
|
||||
|
||||
Reference in New Issue
Block a user