#include #include #include #include #include #include #include #include #include #include namespace DB { namespace ErrorCodes { extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; extern const int ILLEGAL_COLUMN; } namespace { /** Return ColumnNullable of src, with null map as OR-ed null maps of args columns in blocks. * Or ColumnConst(ColumnNullable) if the result is always NULL or if the result is constant and always not NULL. */ ColumnPtr wrapInNullable(const ColumnPtr & src, Block & block, const ColumnNumbers & args, size_t result) { ColumnPtr result_null_map_column; /// If result is already nullable. ColumnPtr src_not_nullable = src; if (src->onlyNull()) return src; else if (src->isColumnNullable()) { src_not_nullable = static_cast(*src).getNestedColumnPtr(); result_null_map_column = static_cast(*src).getNullMapColumnPtr(); } for (const auto & arg : args) { const ColumnWithTypeAndName & elem = block.getByPosition(arg); if (!elem.type->isNullable()) continue; /// Const Nullable that are NULL. if (elem.column->onlyNull()) return block.getByPosition(result).type->createColumnConst(block.rows(), Null()); if (elem.column->isColumnConst()) continue; if (elem.column->isColumnNullable()) { const ColumnPtr & null_map_column = static_cast(*elem.column).getNullMapColumnPtr(); if (!result_null_map_column) { result_null_map_column = null_map_column; } else { MutableColumnPtr mutable_result_null_map_column = (*std::move(result_null_map_column)).mutate(); NullMap & result_null_map = static_cast(*mutable_result_null_map_column).getData(); const NullMap & src_null_map = static_cast(*null_map_column).getData(); for (size_t i = 0, size = result_null_map.size(); i < size; ++i) if (src_null_map[i]) result_null_map[i] = 1; result_null_map_column = std::move(mutable_result_null_map_column); } } } if (!result_null_map_column) return makeNullable(src); if (src_not_nullable->isColumnConst()) return ColumnNullable::create(src_not_nullable->convertToFullColumnIfConst(), result_null_map_column); else return ColumnNullable::create(src_not_nullable, result_null_map_column); } struct NullPresence { bool has_nullable = false; bool has_null_constant = false; }; NullPresence getNullPresense(const Block & block, const ColumnNumbers & args) { NullPresence res; for (const auto & arg : args) { const auto & elem = block.getByPosition(arg); if (!res.has_nullable) res.has_nullable = elem.type->isNullable(); if (!res.has_null_constant) res.has_null_constant = elem.type->onlyNull(); } return res; } NullPresence getNullPresense(const ColumnsWithTypeAndName & args) { NullPresence res; for (const auto & elem : args) { if (!res.has_nullable) res.has_nullable = elem.type->isNullable(); if (!res.has_null_constant) res.has_null_constant = elem.type->onlyNull(); } return res; } bool allArgumentsAreConstants(const Block & block, const ColumnNumbers & args) { for (auto arg : args) if (!block.getByPosition(arg).column->isColumnConst()) return false; return true; } } bool PreparedFunctionImpl::defaultImplementationForConstantArguments(Block & block, const ColumnNumbers & args, size_t result) { ColumnNumbers arguments_to_remain_constants = getArgumentsThatAreAlwaysConstant(); /// Check that these arguments are really constant. for (auto arg_num : arguments_to_remain_constants) if (arg_num < args.size() && !block.getByPosition(args[arg_num]).column->isColumnConst()) throw Exception("Argument at index " + toString(arg_num) + " for function " + getName() + " must be constant", ErrorCodes::ILLEGAL_COLUMN); if (args.empty() || !useDefaultImplementationForConstants() || !allArgumentsAreConstants(block, args)) return false; 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.getByPosition(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); else { have_converted_columns = true; temporary_block.insert({ static_cast(column.column.get())->getDataColumnPtr(), 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) throw Exception("Number of arguments for function " + getName() + " doesn't match: the function requires more arguments", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); temporary_block.insert(block.getByPosition(result)); ColumnNumbers temporary_argument_numbers(arguments_size); for (size_t i = 0; i < arguments_size; ++i) temporary_argument_numbers[i] = i; execute(temporary_block, temporary_argument_numbers, arguments_size); block.getByPosition(result).column = ColumnConst::create(temporary_block.getByPosition(arguments_size).column, block.rows()); return true; } bool PreparedFunctionImpl::defaultImplementationForNulls(Block & block, const ColumnNumbers & args, size_t result) { if (args.empty() || !useDefaultImplementationForNulls()) return false; NullPresence null_presence = getNullPresense(block, args); if (null_presence.has_null_constant) { block.getByPosition(result).column = block.getByPosition(result).type->createColumnConst(block.rows(), Null()); return true; } if (null_presence.has_nullable) { Block temporary_block = createBlockWithNestedColumns(block, args, result); execute(temporary_block, args, result); block.getByPosition(result).column = wrapInNullable(temporary_block.getByPosition(result).column, block, args, result); return true; } return false; } void PreparedFunctionImpl::execute(Block & block, const ColumnNumbers & args, size_t result) { if (defaultImplementationForConstantArguments(block, args, result)) return; if (defaultImplementationForNulls(block, args, result)) return; executeImpl(block, args, result); } void FunctionBuilderImpl::checkNumberOfArguments(size_t number_of_arguments) const { if (isVariadic()) return; size_t expected_number_of_arguments = getNumberOfArguments(); if (number_of_arguments != expected_number_of_arguments) throw Exception("Number of arguments for function " + getName() + " doesn't match: passed " + toString(number_of_arguments) + ", should be " + toString(expected_number_of_arguments), ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); } DataTypePtr FunctionBuilderImpl::getReturnType(const ColumnsWithTypeAndName & arguments) const { checkNumberOfArguments(arguments.size()); if (!arguments.empty() && useDefaultImplementationForNulls()) { NullPresence null_presense = getNullPresense(arguments); if (null_presense.has_null_constant) { return makeNullable(std::make_shared()); } if (null_presense.has_nullable) { Block nested_block = createBlockWithNestedColumns(Block(arguments), ext::collection_cast(ext::range(0, arguments.size()))); auto return_type = getReturnTypeImpl(ColumnsWithTypeAndName(nested_block.begin(), nested_block.end())); return makeNullable(return_type); } } return getReturnTypeImpl(arguments); } }