diff --git a/src/Interpreters/MutationsInterpreter.cpp b/src/Interpreters/MutationsInterpreter.cpp index d6cf42a9915..fdc8f93d3c9 100644 --- a/src/Interpreters/MutationsInterpreter.cpp +++ b/src/Interpreters/MutationsInterpreter.cpp @@ -63,7 +63,6 @@ namespace ErrorCodes extern const int NO_SUCH_COLUMN_IN_TABLE; extern const int CANNOT_UPDATE_COLUMN; extern const int UNEXPECTED_EXPRESSION; - extern const int THERE_IS_NO_COLUMN; extern const int ILLEGAL_STATISTICS; } @@ -593,10 +592,6 @@ void MutationsInterpreter::prepare(bool dry_run) if (available_columns_set.emplace(name).second) available_columns.push_back(name); } - else if (!available_columns_set.contains(name)) - { - throw Exception(ErrorCodes::THERE_IS_NO_COLUMN, "Column {} is updated but not requested to read", name); - } updated_columns.insert(name); } @@ -1097,6 +1092,21 @@ void MutationsInterpreter::prepareMutationStages(std::vector & prepared_s } } + auto storage_columns = metadata_snapshot->getColumns().getNamesOfPhysical(); + + /// Add persistent virtual columns if the whole part is rewritten, + /// because we should preserve them in parts after mutation. + if (prepared_stages.back().isAffectingAllColumns(storage_columns)) + { + for (const auto & column_name : available_columns) + { + if (column_name == RowExistsColumn::name && has_filters && !deleted_mask_updated) + continue; + + prepared_stages.back().output_columns.insert(column_name); + } + } + /// Now, calculate `expressions_chain` for each stage except the first. /// Do it backwards to propagate information about columns required as input for a stage to the previous stage. for (int64_t i = prepared_stages.size() - 1; i >= 0; --i) diff --git a/src/Interpreters/MutationsInterpreter.h b/src/Interpreters/MutationsInterpreter.h index b70c9c5ddb0..7d32d4fb275 100644 --- a/src/Interpreters/MutationsInterpreter.h +++ b/src/Interpreters/MutationsInterpreter.h @@ -171,7 +171,12 @@ private: Source source; StorageMetadataPtr metadata_snapshot; MutationCommands commands; + + /// List of columns in table or in data part that can be updated by mutation. + /// If mutation affects all columns (e.g. DELETE), all of this columns + /// must be returned by pipeline created in MutationsInterpreter. Names available_columns; + ContextPtr context; Settings settings; SelectQueryOptions select_limits; diff --git a/tests/queries/0_stateless/03275_block_number_update.reference b/tests/queries/0_stateless/03275_block_number_update.reference new file mode 100644 index 00000000000..e7d6081b647 --- /dev/null +++ b/tests/queries/0_stateless/03275_block_number_update.reference @@ -0,0 +1,4 @@ +2 +3 +2 +3 diff --git a/tests/queries/0_stateless/03275_block_number_update.sql b/tests/queries/0_stateless/03275_block_number_update.sql new file mode 100644 index 00000000000..ca8160b48f6 --- /dev/null +++ b/tests/queries/0_stateless/03275_block_number_update.sql @@ -0,0 +1,27 @@ +DROP TABLE IF EXISTS t_block_number_mut; + +SET mutations_sync = 2; + +CREATE TABLE t_block_number_mut (n int) ENGINE = MergeTree ORDER BY tuple() SETTINGS enable_block_number_column = 1, min_bytes_for_wide_part = 0; + +INSERT INTO t_block_number_mut VALUES (1) (2); + +OPTIMIZE TABLE t_block_number_mut FINAL; + +ALTER TABLE t_block_number_mut UPDATE n = n + 1 WHERE 1; + +SELECT * FROM t_block_number_mut; + +DROP TABLE IF EXISTS t_block_number_mut; + +CREATE TABLE t_block_number_mut (n int) ENGINE = MergeTree ORDER BY tuple() SETTINGS enable_block_number_column = 1, min_bytes_for_wide_part = '1G'; + +INSERT INTO t_block_number_mut VALUES (1) (2); + +OPTIMIZE TABLE t_block_number_mut FINAL; + +ALTER TABLE t_block_number_mut UPDATE n = n + 1 WHERE 1; + +SELECT * FROM t_block_number_mut; + +DROP TABLE IF EXISTS t_block_number_mut;