diff --git a/src/DataTypes/DataTypeNullable.cpp b/src/DataTypes/DataTypeNullable.cpp index 64b060e521b..c3b734686f8 100644 --- a/src/DataTypes/DataTypeNullable.cpp +++ b/src/DataTypes/DataTypeNullable.cpp @@ -546,7 +546,7 @@ ColumnPtr DataTypeNullable::getSubcolumn(const String & subcolumn_name, const IC { const auto & column_nullable = assert_cast(column); if (subcolumn_name == "null") - return column_nullable.getNullMapColumnPtr()->assumeMutable(); + return column_nullable.getNullMapColumnPtr(); return nested_data_type->getSubcolumn(subcolumn_name, column_nullable.getNestedColumn()); } diff --git a/src/Storages/ColumnsDescription.cpp b/src/Storages/ColumnsDescription.cpp index b4ddde7c0f3..bba577928bf 100644 --- a/src/Storages/ColumnsDescription.cpp +++ b/src/Storages/ColumnsDescription.cpp @@ -331,8 +331,7 @@ NamesAndTypesList ColumnsDescription::getAll() const bool ColumnsDescription::has(const String & column_name) const { - return columns.get<1>().find(column_name) != columns.get<1>().end() - || subcolumns.find(column_name) != subcolumns.end(); + return columns.get<1>().find(column_name) != columns.get<1>().end(); } bool ColumnsDescription::hasNested(const String & column_name) const @@ -341,6 +340,16 @@ bool ColumnsDescription::hasNested(const String & column_name) const return range.first != range.second && range.first->name.length() > column_name.length(); } +bool ColumnsDescription::hasSubcolumn(const String & column_name) const +{ + return subcolumns.find(column_name) != subcolumns.end(); +} + +bool ColumnsDescription::hasInStorageOrSubcolumn(const String & column_name) const +{ + return has(column_name) || hasSubcolumn(column_name); +} + const ColumnDescription & ColumnsDescription::get(const String & column_name) const { auto it = columns.get<1>().find(column_name); diff --git a/src/Storages/ColumnsDescription.h b/src/Storages/ColumnsDescription.h index 1990c565b65..26e30004544 100644 --- a/src/Storages/ColumnsDescription.h +++ b/src/Storages/ColumnsDescription.h @@ -85,6 +85,8 @@ public: bool has(const String & column_name) const; bool hasNested(const String & column_name) const; + bool hasSubcolumn(const String & column_name) const; + bool hasInStorageOrSubcolumn(const String & column_name) const; const ColumnDescription & get(const String & column_name) const; template diff --git a/src/Storages/MergeTree/IMergeTreeDataPart.cpp b/src/Storages/MergeTree/IMergeTreeDataPart.cpp index 5a2c17a7b98..2e91d32db1c 100644 --- a/src/Storages/MergeTree/IMergeTreeDataPart.cpp +++ b/src/Storages/MergeTree/IMergeTreeDataPart.cpp @@ -879,6 +879,8 @@ void IMergeTreeDataPart::loadColumns(bool require) { String path = getFullRelativePath() + "columns.txt"; auto metadata_snapshot = storage.getInMemoryMetadataPtr(); + NamesAndTypesList loaded_columns; + if (!volume->getDisk()->exists(path)) { /// We can get list of columns only from columns.txt in compact parts. @@ -888,25 +890,23 @@ void IMergeTreeDataPart::loadColumns(bool require) /// If there is no file with a list of columns, write it down. for (const NameAndTypePair & column : metadata_snapshot->getColumns().getAllPhysical()) if (volume->getDisk()->exists(getFullRelativePath() + getFileNameForColumn(column) + ".bin")) - columns.push_back(column); + loaded_columns.push_back(column); if (columns.empty()) throw Exception("No columns in part " + name, ErrorCodes::NO_FILE_IN_DATA_PART); { auto buf = volume->getDisk()->writeFile(path + ".tmp", 4096); - columns.writeText(*buf); + loaded_columns.writeText(*buf); } volume->getDisk()->moveFile(path + ".tmp", path); } else { - columns.readText(*volume->getDisk()->readFile(path)); + loaded_columns.readText(*volume->getDisk()->readFile(path)); } - size_t pos = 0; - for (const auto & column : columns) - column_name_to_position.emplace(column.name, pos++); + setColumns(loaded_columns); } bool IMergeTreeDataPart::shallParticipateInMerges(const StoragePolicyPtr & storage_policy) const diff --git a/tests/queries/0_stateless/01475_read_subcolumns_3.reference b/tests/queries/0_stateless/01475_read_subcolumns_3.reference new file mode 100644 index 00000000000..0fc48bd797a --- /dev/null +++ b/tests/queries/0_stateless/01475_read_subcolumns_3.reference @@ -0,0 +1,16 @@ +Nullable +2 +2 +2 +2 +Map +2 +2 +2 +2 +2 +2 +1 2 +2 2 +3 3 +1 diff --git a/tests/queries/0_stateless/01475_read_subcolumns_3.sql b/tests/queries/0_stateless/01475_read_subcolumns_3.sql new file mode 100644 index 00000000000..66bcd7dbc91 --- /dev/null +++ b/tests/queries/0_stateless/01475_read_subcolumns_3.sql @@ -0,0 +1,39 @@ +DROP TABLE IF EXISTS null_subcolumns; + +SELECT 'Nullable'; +CREATE TABLE null_subcolumns (id UInt32, n Nullable(String)) ENGINE = MergeTree ORDER BY id; + +INSERT INTO null_subcolumns VALUES (1, 'foo') (2, NULL) (3, NULL) (4, 'abc'); + +SELECT count() FROM null_subcolumns WHERE n.null; +SELECT count() FROM null_subcolumns PREWHERE n.null; + +-- Check, that subcolumns will be available after restart. +DETACH TABLE null_subcolumns; +ATTACH TABLE null_subcolumns; + +SELECT count() FROM null_subcolumns WHERE n.null; +SELECT count() FROM null_subcolumns PREWHERE n.null; + +DROP TABLE null_subcolumns; +DROP TABLE IF EXISTS map_subcolumns; + +SELECT 'Map'; +SET allow_experimental_map_type = 1; +CREATE TABLE map_subcolumns (id UInt32, m Map(String, UInt32)) ENGINE = MergeTree ORDER BY id; +INSERT INTO map_subcolumns VALUES (1, map('a', 1, 'b', 2)) (2, map('a', 3, 'c', 4)), (3, map('b', 5, 'c', 6, 'd', 7)); + +SELECT count() FROM map_subcolumns WHERE has(m.keys, 'a'); +SELECT count() FROM map_subcolumns PREWHERE has(m.keys, 'b'); + +SELECT count() FROM map_subcolumns WHERE arrayMax(m.values) > 3; +SELECT count() FROM map_subcolumns PREWHERE arrayMax(m.values) > 3; + +DETACH TABLE map_subcolumns; +ATTACH TABLE map_subcolumns; + +SELECT count() FROM map_subcolumns WHERE has(m.keys, 'a'); +SELECT count() FROM map_subcolumns PREWHERE has(m.keys, 'b'); + +SELECT id, m.size0 FROM map_subcolumns; +SELECT count() FROM map_subcolumns WHERE m.size0 > 2;