Allow null to default convertion only if column has default

This commit is contained in:
kssenii 2021-04-23 09:50:56 +00:00
parent 55d2627a27
commit 54a7a6ef0d
7 changed files with 34 additions and 21 deletions

View File

@ -10,10 +10,11 @@ AddingDefaultBlockOutputStream::AddingDefaultBlockOutputStream(
const BlockOutputStreamPtr & output_,
const Block & header_,
const ColumnsDescription & columns_,
ContextPtr context_)
ContextPtr context_,
bool null_as_default_)
: output(output_), header(header_)
{
auto dag = addMissingDefaults(header_, output->getHeader().getNamesAndTypesList(), columns_, context_);
auto dag = addMissingDefaults(header_, output->getHeader().getNamesAndTypesList(), columns_, context_, null_as_default_);
adding_defaults_actions = std::make_shared<ExpressionActions>(std::move(dag), ExpressionActionsSettings::fromContext(context_));
}

View File

@ -26,7 +26,8 @@ public:
const BlockOutputStreamPtr & output_,
const Block & header_,
const ColumnsDescription & columns_,
ContextPtr context_);
ContextPtr context_,
bool null_as_default_ = false);
Block getHeader() const override { return header; }
void write(const Block & block) override;

View File

@ -242,11 +242,12 @@ BlockIO InterpreterInsertQuery::execute()
const auto & input_columns = res.pipeline.getHeader().getColumns();
const auto & query_columns = query_sample_block.getColumns();
const auto & output_columns = metadata_snapshot->getColumns();
size_t col_idx = 0;
for (const auto & column : query_columns_names_and_types)
{
if (input_columns[col_idx]->isNullable() && !query_columns[col_idx]->isNullable())
if (input_columns[col_idx]->isNullable() && !query_columns[col_idx]->isNullable() && output_columns.hasDefault(column.name))
{
auto nullable_column = ColumnNullable::create(query_columns[col_idx], ColumnUInt8::create(query_columns[col_idx]->size(), 0));
ColumnWithTypeAndName new_column(std::move(nullable_column), std::make_shared<DataTypeNullable>(column.type), column.name);
@ -282,10 +283,12 @@ BlockIO InterpreterInsertQuery::execute()
out = std::make_shared<CheckConstraintsBlockOutputStream>(
query.table_id, out, out->getHeader(), metadata_snapshot->getConstraints(), getContext());
bool null_as_default = query.select && getContext()->getSettingsRef().insert_null_as_default;
/// Actually we don't know structure of input blocks from query/table,
/// because some clients break insertion protocol (columns != header)
out = std::make_shared<AddingDefaultBlockOutputStream>(
out, query_sample_block, metadata_snapshot->getColumns(), getContext());
out, query_sample_block, metadata_snapshot->getColumns(), getContext(), null_as_default);
/// It's important to squash blocks as early as possible (before other transforms),
/// because other transforms may work inefficient if block size is small.

View File

@ -19,7 +19,8 @@ ActionsDAGPtr addMissingDefaults(
const Block & header,
const NamesAndTypesList & required_columns,
const ColumnsDescription & columns,
ContextPtr context)
ContextPtr context,
bool null_as_default)
{
auto actions = std::make_shared<ActionsDAG>(header.getColumnsWithTypeAndName());
auto & index = actions->getIndex();
@ -80,7 +81,7 @@ ActionsDAGPtr addMissingDefaults(
}
/// Computes explicitly specified values by default and materialized columns.
if (auto dag = evaluateMissingDefaults(actions->getResultColumns(), required_columns, columns, context))
if (auto dag = evaluateMissingDefaults(actions->getResultColumns(), required_columns, columns, context, true, null_as_default))
actions = ActionsDAG::merge(std::move(*actions), std::move(*dag));
else
/// Removes unused columns and reorders result.

View File

@ -24,5 +24,6 @@ using ActionsDAGPtr = std::shared_ptr<ActionsDAG>;
* All three types of columns are materialized (not constants).
*/
ActionsDAGPtr addMissingDefaults(
const Block & header, const NamesAndTypesList & required_columns, const ColumnsDescription & columns, ContextPtr context);
const Block & header, const NamesAndTypesList & required_columns,
const ColumnsDescription & columns, ContextPtr context, bool null_as_default = false);
}

View File

@ -24,17 +24,19 @@ namespace
{
/// Add all required expressions for missing columns calculation
void addDefaultRequiredExpressionsRecursively(const Block & block, const String & required_column_name, DataTypePtr required_column_type, const ColumnsDescription & columns, ASTPtr default_expr_list_accum, NameSet & added_columns)
void addDefaultRequiredExpressionsRecursively(
const Block & block, const String & required_column_name, DataTypePtr required_column_type,
const ColumnsDescription & columns, ASTPtr default_expr_list_accum, NameSet & added_columns, bool null_as_default)
{
checkStackSize();
bool is_column_in_query = block.has(required_column_name);
bool from_nullable_to_non_nullable = false;
bool convert_null_to_default = false;
if (is_column_in_query)
from_nullable_to_non_nullable = block.findByName(required_column_name)->type->isNullable() && !required_column_type->isNullable();
convert_null_to_default = null_as_default && block.findByName(required_column_name)->type->isNullable() && !required_column_type->isNullable();
if ((is_column_in_query && !from_nullable_to_non_nullable) || added_columns.count(required_column_name))
if ((is_column_in_query && !convert_null_to_default) || added_columns.count(required_column_name))
return;
auto column_default = columns.getDefault(required_column_name);
@ -50,10 +52,10 @@ void addDefaultRequiredExpressionsRecursively(const Block & block, const String
RequiredSourceColumnsVisitor(columns_context).visit(column_default_expr);
NameSet required_columns_names = columns_context.requiredColumns();
if (is_column_in_query && from_nullable_to_non_nullable)
if (is_column_in_query && convert_null_to_default)
{
auto null_as_default_func = makeASTFunction("ifNull", std::make_shared<ASTIdentifier>(required_column_name), column_default_expr);
default_expr_list_accum->children.emplace_back(setAlias(null_as_default_func, required_column_name));
auto null_as_default_expr = makeASTFunction("ifNull", std::make_shared<ASTIdentifier>(required_column_name), column_default_expr);
default_expr_list_accum->children.emplace_back(setAlias(null_as_default_expr, required_column_name));
}
else
{
@ -64,17 +66,17 @@ void addDefaultRequiredExpressionsRecursively(const Block & block, const String
added_columns.emplace(required_column_name);
for (const auto & next_required_column_name : required_columns_names)
addDefaultRequiredExpressionsRecursively(block, next_required_column_name, required_column_type, columns, default_expr_list_accum, added_columns);
addDefaultRequiredExpressionsRecursively(block, next_required_column_name, required_column_type, columns, default_expr_list_accum, added_columns, null_as_default);
}
}
ASTPtr defaultRequiredExpressions(const Block & block, const NamesAndTypesList & required_columns, const ColumnsDescription & columns)
ASTPtr defaultRequiredExpressions(const Block & block, const NamesAndTypesList & required_columns, const ColumnsDescription & columns, bool null_as_default)
{
ASTPtr default_expr_list = std::make_shared<ASTExpressionList>();
NameSet added_columns;
for (const auto & column : required_columns)
addDefaultRequiredExpressionsRecursively(block, column.name, column.type, columns, default_expr_list, added_columns);
addDefaultRequiredExpressionsRecursively(block, column.name, column.type, columns, default_expr_list, added_columns, null_as_default);
if (default_expr_list->children.empty())
return nullptr;
@ -147,12 +149,14 @@ ActionsDAGPtr evaluateMissingDefaults(
const Block & header,
const NamesAndTypesList & required_columns,
const ColumnsDescription & columns,
ContextPtr context, bool save_unneeded_columns)
ContextPtr context,
bool save_unneeded_columns,
bool null_as_default)
{
if (!columns.hasDefaults())
return nullptr;
ASTPtr expr_list = defaultRequiredExpressions(header, required_columns, columns);
ASTPtr expr_list = defaultRequiredExpressions(header, required_columns, columns, null_as_default);
return createExpressions(header, expr_list, save_unneeded_columns, required_columns, context);
}

View File

@ -23,7 +23,9 @@ ActionsDAGPtr evaluateMissingDefaults(
const Block & header,
const NamesAndTypesList & required_columns,
const ColumnsDescription & columns,
ContextPtr context, bool save_unneeded_columns = true);
ContextPtr context,
bool save_unneeded_columns = true,
bool null_as_default = false);
/// Tries to convert columns in block to required_columns
void performRequiredConversions(Block & block, const NamesAndTypesList & required_columns, ContextPtr context);