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 BlockOutputStreamPtr & output_,
const Block & header_, const Block & header_,
const ColumnsDescription & columns_, const ColumnsDescription & columns_,
ContextPtr context_) ContextPtr context_,
bool null_as_default_)
: output(output_), header(header_) : 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_)); adding_defaults_actions = std::make_shared<ExpressionActions>(std::move(dag), ExpressionActionsSettings::fromContext(context_));
} }

View File

@ -26,7 +26,8 @@ public:
const BlockOutputStreamPtr & output_, const BlockOutputStreamPtr & output_,
const Block & header_, const Block & header_,
const ColumnsDescription & columns_, const ColumnsDescription & columns_,
ContextPtr context_); ContextPtr context_,
bool null_as_default_ = false);
Block getHeader() const override { return header; } Block getHeader() const override { return header; }
void write(const Block & block) override; 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 & input_columns = res.pipeline.getHeader().getColumns();
const auto & query_columns = query_sample_block.getColumns(); const auto & query_columns = query_sample_block.getColumns();
const auto & output_columns = metadata_snapshot->getColumns();
size_t col_idx = 0; size_t col_idx = 0;
for (const auto & column : query_columns_names_and_types) 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)); 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); 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>( out = std::make_shared<CheckConstraintsBlockOutputStream>(
query.table_id, out, out->getHeader(), metadata_snapshot->getConstraints(), getContext()); 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, /// Actually we don't know structure of input blocks from query/table,
/// because some clients break insertion protocol (columns != header) /// because some clients break insertion protocol (columns != header)
out = std::make_shared<AddingDefaultBlockOutputStream>( 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), /// It's important to squash blocks as early as possible (before other transforms),
/// because other transforms may work inefficient if block size is small. /// because other transforms may work inefficient if block size is small.

View File

@ -19,7 +19,8 @@ ActionsDAGPtr addMissingDefaults(
const Block & header, const Block & header,
const NamesAndTypesList & required_columns, const NamesAndTypesList & required_columns,
const ColumnsDescription & columns, const ColumnsDescription & columns,
ContextPtr context) ContextPtr context,
bool null_as_default)
{ {
auto actions = std::make_shared<ActionsDAG>(header.getColumnsWithTypeAndName()); auto actions = std::make_shared<ActionsDAG>(header.getColumnsWithTypeAndName());
auto & index = actions->getIndex(); auto & index = actions->getIndex();
@ -80,7 +81,7 @@ ActionsDAGPtr addMissingDefaults(
} }
/// Computes explicitly specified values by default and materialized columns. /// 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)); actions = ActionsDAG::merge(std::move(*actions), std::move(*dag));
else else
/// Removes unused columns and reorders result. /// 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). * All three types of columns are materialized (not constants).
*/ */
ActionsDAGPtr addMissingDefaults( 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 /// 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(); checkStackSize();
bool is_column_in_query = block.has(required_column_name); 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) 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; return;
auto column_default = columns.getDefault(required_column_name); 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); RequiredSourceColumnsVisitor(columns_context).visit(column_default_expr);
NameSet required_columns_names = columns_context.requiredColumns(); 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); 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_func, required_column_name)); default_expr_list_accum->children.emplace_back(setAlias(null_as_default_expr, required_column_name));
} }
else else
{ {
@ -64,17 +66,17 @@ void addDefaultRequiredExpressionsRecursively(const Block & block, const String
added_columns.emplace(required_column_name); added_columns.emplace(required_column_name);
for (const auto & next_required_column_name : required_columns_names) 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>(); ASTPtr default_expr_list = std::make_shared<ASTExpressionList>();
NameSet added_columns; NameSet added_columns;
for (const auto & column : required_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()) if (default_expr_list->children.empty())
return nullptr; return nullptr;
@ -147,12 +149,14 @@ ActionsDAGPtr evaluateMissingDefaults(
const Block & header, const Block & header,
const NamesAndTypesList & required_columns, const NamesAndTypesList & required_columns,
const ColumnsDescription & columns, const ColumnsDescription & columns,
ContextPtr context, bool save_unneeded_columns) ContextPtr context,
bool save_unneeded_columns,
bool null_as_default)
{ {
if (!columns.hasDefaults()) if (!columns.hasDefaults())
return nullptr; 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); return createExpressions(header, expr_list, save_unneeded_columns, required_columns, context);
} }

View File

@ -23,7 +23,9 @@ ActionsDAGPtr evaluateMissingDefaults(
const Block & header, const Block & header,
const NamesAndTypesList & required_columns, const NamesAndTypesList & required_columns,
const ColumnsDescription & 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 /// Tries to convert columns in block to required_columns
void performRequiredConversions(Block & block, const NamesAndTypesList & required_columns, ContextPtr context); void performRequiredConversions(Block & block, const NamesAndTypesList & required_columns, ContextPtr context);