Fix alias on default columns

This commit is contained in:
alesapin 2020-09-15 14:17:58 +03:00
parent a75254766e
commit 8828a78174
5 changed files with 87 additions and 39 deletions

View File

@ -12,59 +12,76 @@ namespace ErrorCodes
extern const int LOGICAL_ERROR;
}
namespace
{
/// Columns absent in part may depend on other absent columns so we are
/// searching all required columns recursively. Return true if found at least
/// one existing column in part.
bool injectRequiredColumnsRecursively(
const String & column_name,
const ColumnsDescription & storage_columns,
const MergeTreeData::AlterConversions & alter_conversions,
const MergeTreeData::DataPartPtr & part,
Names & columns,
NameSet & required_columns,
NameSet & injected_columns)
{
String column_name_in_part = column_name;
if (alter_conversions.isColumnRenamed(column_name_in_part))
column_name_in_part = alter_conversions.getColumnOldName(column_name_in_part);
/// column has files and hence does not require evaluation
if (storage_columns.hasPhysical(column_name) && part->hasColumnFiles(column_name_in_part, *storage_columns.getPhysical(column_name).type))
{
/// ensure each column is added only once
if (required_columns.count(column_name) == 0)
{
columns.emplace_back(column_name);
required_columns.emplace(column_name);
injected_columns.emplace(column_name);
}
return true;
}
/// Column doesn't have default value and don't exist in part
/// don't need to add to required set.
const auto column_default = storage_columns.getDefault(column_name);
if (!column_default)
return false;
/// collect identifiers required for evaluation
IdentifierNameSet identifiers;
column_default->expression->collectIdentifierNames(identifiers);
bool result = false;
for (const auto & identifier : identifiers)
result |= injectRequiredColumnsRecursively(identifier, storage_columns, alter_conversions, part, columns, required_columns, injected_columns);
return result;
}
}
NameSet injectRequiredColumns(const MergeTreeData & storage, const StorageMetadataPtr & metadata_snapshot, const MergeTreeData::DataPartPtr & part, Names & columns)
{
NameSet required_columns{std::begin(columns), std::end(columns)};
NameSet injected_columns;
auto all_column_files_missing = true;
bool have_at_least_one_physical_column = false;
const auto & storage_columns = metadata_snapshot->getColumns();
auto alter_conversions = storage.getAlterConversionsForPart(part);
for (size_t i = 0; i < columns.size(); ++i)
{
/// possibly renamed
auto column_name_in_part = columns[i];
if (alter_conversions.isColumnRenamed(column_name_in_part))
column_name_in_part = alter_conversions.getColumnOldName(column_name_in_part);
/// column has files and hence does not require evaluation
if (part->hasColumnFiles(column_name_in_part, *storage_columns.getPhysical(columns[i]).type))
{
all_column_files_missing = false;
continue;
}
const auto column_default = storage_columns.getDefault(columns[i]);
if (!column_default)
continue;
/// collect identifiers required for evaluation
IdentifierNameSet identifiers;
column_default->expression->collectIdentifierNames(identifiers);
for (const auto & identifier : identifiers)
{
if (storage_columns.hasPhysical(identifier))
{
/// ensure each column is added only once
if (required_columns.count(identifier) == 0)
{
columns.emplace_back(identifier);
required_columns.emplace(identifier);
injected_columns.emplace(identifier);
}
}
}
}
have_at_least_one_physical_column |= injectRequiredColumnsRecursively(
columns[i], storage_columns, alter_conversions,
part, columns, required_columns, injected_columns);
/** Add a column of the minimum size.
* Used in case when no column is needed or files are missing, but at least you need to know number of rows.
* Adds to the columns.
*/
if (all_column_files_missing)
if (!have_at_least_one_physical_column)
{
const auto minimum_size_column_name = part->getColumnNameWithMinumumCompressedSize(metadata_snapshot);
columns.push_back(minimum_size_column_name);

View File

@ -1,5 +1,6 @@
1 1
1 1 1
1
2 2 4
2 2 2 4
3 3 9

View File

@ -2,12 +2,16 @@ DROP TABLE IF EXISTS table_with_defaults_on_aliases;
CREATE TABLE table_with_defaults_on_aliases (col1 UInt32, col2 ALIAS col1, col3 DEFAULT col2) Engine = MergeTree() ORDER BY tuple();
SYSTEM STOP MERGES table_with_defaults_on_aliases;
INSERT INTO table_with_defaults_on_aliases (col1) VALUES (1);
SELECT * FROM table_with_defaults_on_aliases WHERE col1 = 1;
SELECT col1, col2, col3 FROM table_with_defaults_on_aliases WHERE col1 = 1;
SELECT col3 FROM table_with_defaults_on_aliases; -- important to check without WHERE
ALTER TABLE table_with_defaults_on_aliases ADD COLUMN col4 UInt64 DEFAULT col2 * col3;
INSERT INTO table_with_defaults_on_aliases (col1) VALUES (2);
@ -24,7 +28,6 @@ SELECT * FROM table_with_defaults_on_aliases WHERE col1 = 3;
SELECT col1, col2, col3, col4, col5 FROM table_with_defaults_on_aliases WHERE col1 = 3;
ALTER TABLE table_with_defaults_on_aliases ADD COLUMN col6 UInt64 MATERIALIZED col2 * col4;
DROP TABLE IF EXISTS table_with_defaults_on_aliases;

View File

@ -0,0 +1,6 @@
a1 b1
a2 b2
a3 b3
c1
c2
c3

View File

@ -0,0 +1,21 @@
DROP TABLE IF EXISTS test_new_col;
CREATE TABLE test_new_col
(
`_csv` String,
`csv_as_array` Array(String) ALIAS splitByChar(';',_csv),
`csv_col1` String DEFAULT csv_as_array[1],
`csv_col2` String DEFAULT csv_as_array[2]
)
ENGINE = MergeTree
ORDER BY tuple();
INSERT INTO test_new_col (_csv) VALUES ('a1;b1;c1;d1'), ('a2;b2;c2;d2'), ('a3;b3;c3;d3');
SELECT csv_col1, csv_col2 FROM test_new_col ORDER BY csv_col1;
ALTER TABLE test_new_col ADD COLUMN `csv_col3` String DEFAULT csv_as_array[3];
SELECT csv_col3 FROM test_new_col ORDER BY csv_col3;
DROP TABLE IF EXISTS test_new_col;