fix 'There's no column' error for materialized and alias columns

This commit is contained in:
chertus 2019-12-13 21:46:55 +03:00
parent 142932ebdf
commit 8c9ca6891f
11 changed files with 143 additions and 73 deletions

View File

@ -2,6 +2,7 @@
#include <Core/Names.h>
#include <Core/Types.h>
#include <Core/NamesAndTypes.h>
#include <Parsers/IAST_fwd.h>
#include <memory>
@ -36,7 +37,23 @@ struct DatabaseAndTableWithAlias
bool satisfies(const DatabaseAndTableWithAlias & table, bool table_may_be_an_alias);
};
using TableWithColumnNames = std::pair<DatabaseAndTableWithAlias, Names>;
struct TableWithColumnNames
{
DatabaseAndTableWithAlias table;
Names columns;
Names hidden_columns;
TableWithColumnNames(const DatabaseAndTableWithAlias & table_, const Names & columns_)
: table(table_)
, columns(columns_)
{}
void addHiddenColumns(const NamesAndTypesList & addition)
{
for (auto & column : addition)
hidden_columns.push_back(column.name);
}
};
std::vector<DatabaseAndTableWithAlias> getDatabaseAndTables(const ASTSelectQuery & select_query, const String & current_database);
std::optional<DatabaseAndTableWithAlias> getDatabaseAndTable(const ASTSelectQuery & select, size_t table_number);

View File

@ -18,11 +18,12 @@ void FindIdentifierBestTableData::visit(ASTIdentifier & identifier, ASTPtr &)
{
for (const auto & table_names : tables)
{
if (std::find(table_names.second.begin(), table_names.second.end(), identifier.name) != table_names.second.end())
auto & columns = table_names.columns;
if (std::find(columns.begin(), columns.end(), identifier.name) != columns.end())
{
// TODO: make sure no collision ever happens
if (!best_table)
best_table = &table_names.first;
best_table = &table_names.table;
}
}
}
@ -30,7 +31,7 @@ void FindIdentifierBestTableData::visit(ASTIdentifier & identifier, ASTPtr &)
{
size_t best_table_pos = 0;
if (IdentifierSemantic::chooseTable(identifier, tables, best_table_pos))
best_table = &tables[best_table_pos].first;
best_table = &tables[best_table_pos].table;
}
identifier_table.emplace_back(&identifier, best_table);

View File

@ -20,7 +20,7 @@ const DatabaseAndTableWithAlias & extractTable(const DatabaseAndTableWithAlias &
const DatabaseAndTableWithAlias & extractTable(const TableWithColumnNames & table)
{
return table.first;
return table.table;
}
template <typename T>

View File

@ -87,7 +87,8 @@ bool PredicateExpressionsOptimizer::optimizeImpl(
/// split predicate with `and`
std::vector<ASTPtr> outer_predicate_expressions = splitConjunctionPredicate(outer_expression);
std::vector<TableWithColumnNames> tables_with_columns = getDatabaseAndTablesWithColumnNames(*ast_select, context);
std::vector<const ASTTableExpression *> table_expressions = getTableExpressions(*ast_select);
std::vector<TableWithColumnNames> tables_with_columns = getDatabaseAndTablesWithColumnNames(table_expressions, context);
bool is_rewrite_subquery = false;
for (auto & outer_predicate : outer_predicate_expressions)

View File

@ -100,43 +100,20 @@ void collectSourceColumns(const ColumnsDescription & columns, NamesAndTypesList
}
}
std::vector<TableWithColumnNames> getTablesWithColumns(const ASTSelectQuery & select_query, const Context & context,
const ASTTablesInSelectQueryElement * table_join_node,
NamesAndTypesList & columns_from_joined_table,
std::function<Names()> get_column_names)
std::vector<TableWithColumnNames> getTablesWithColumns(const std::vector<const ASTTableExpression * > & table_expressions,
const Context & context)
{
std::vector<TableWithColumnNames> tables_with_columns = getDatabaseAndTablesWithColumnNames(select_query, context);
std::vector<TableWithColumnNames> tables_with_columns = getDatabaseAndTablesWithColumnNames(table_expressions, context);
auto & settings = context.getSettingsRef();
if (settings.joined_subquery_requires_alias && tables_with_columns.size() > 1)
{
for (auto & pr : tables_with_columns)
if (pr.first.table.empty() && pr.first.alias.empty())
if (pr.table.table.empty() && pr.table.alias.empty())
throw Exception("Not unique subquery in FROM requires an alias (or joined_subquery_requires_alias=0 to disable restriction).",
ErrorCodes::ALIAS_REQUIRED);
}
TableWithColumnNames joined_table;
if (table_join_node)
{
const auto & joined_expression = table_join_node->table_expression->as<ASTTableExpression &>();
columns_from_joined_table = getColumnsFromTableExpression(joined_expression, context);
joined_table.first = DatabaseAndTableWithAlias(joined_expression, context.getCurrentDatabase());
for (const auto & column : columns_from_joined_table)
joined_table.second.push_back(column.name);
}
/// If empty make table(s) with list of source and joined columns
if (tables_with_columns.empty())
{
tables_with_columns.emplace_back(DatabaseAndTableWithAlias{}, get_column_names());
if (!joined_table.second.empty())
tables_with_columns.emplace_back(std::move(joined_table));
}
return tables_with_columns;
}
@ -859,24 +836,36 @@ SyntaxAnalyzerResultPtr SyntaxAnalyzer::analyze(
replaceJoinedTable(table_join_node);
}
auto get_column_names = [&]() -> Names
std::vector<const ASTTableExpression *> table_expressions = getTableExpressions(*select_query);
auto tables_with_columns = getTablesWithColumns(table_expressions, context);
if (tables_with_columns.empty())
{
if (storage)
return storage->getColumns().getOrdinary().getNames();
{
const ColumnsDescription & starage_columns = storage->getColumns();
tables_with_columns.emplace_back(DatabaseAndTableWithAlias{}, starage_columns.getOrdinary().getNames());
auto & table = tables_with_columns.back();
table.addHiddenColumns(starage_columns.getMaterialized());
table.addHiddenColumns(starage_columns.getAliases());
table.addHiddenColumns(starage_columns.getVirtuals());
}
else
{
Names columns;
columns.reserve(result.source_columns.size());
for (const auto & column : result.source_columns)
columns.push_back(column.name);
tables_with_columns.emplace_back(DatabaseAndTableWithAlias{}, columns);
}
}
Names columns;
columns.reserve(result.source_columns.size());
for (const auto & column : result.source_columns)
columns.push_back(column.name);
return columns;
};
auto tables_with_columns = getTablesWithColumns(*select_query, context, table_join_node,
result.analyzed_join->columns_from_joined_table, get_column_names);
if (tables_with_columns.size() > 1)
if (table_expressions.size() > 1)
{
result.analyzed_join->columns_from_joined_table = getColumnsFromTableExpression(*table_expressions[1], context);
result.analyzed_join->deduplicateAndQualifyColumnNames(
source_columns_set, tables_with_columns[1].first.getQualifiedNamePrefix());
source_columns_set, tables_with_columns[1].table.getQualifiedNamePrefix());
}
translateQualifiedNames(query, *select_query, source_columns_set, std::move(tables_with_columns));

View File

@ -31,12 +31,12 @@ namespace ErrorCodes
bool TranslateQualifiedNamesMatcher::Data::unknownColumn(size_t table_pos, const ASTIdentifier & identifier) const
{
const auto & table = tables[table_pos].first;
const auto & table = tables[table_pos].table;
auto nested1 = IdentifierSemantic::extractNestedName(identifier, table.table);
auto nested2 = IdentifierSemantic::extractNestedName(identifier, table.alias);
String short_name = identifier.shortName();
const Names & column_names = tables[table_pos].second;
const Names & column_names = tables[table_pos].columns;
for (auto & known_name : column_names)
{
if (short_name == known_name)
@ -46,6 +46,18 @@ bool TranslateQualifiedNamesMatcher::Data::unknownColumn(size_t table_pos, const
if (nested2 && *nested2 == known_name)
return false;
}
const Names & hidden_names = tables[table_pos].hidden_columns;
for (auto & known_name : hidden_names)
{
if (short_name == known_name)
return false;
if (nested1 && *nested1 == known_name)
return false;
if (nested2 && *nested2 == known_name)
return false;
}
return !column_names.empty();
}
@ -88,7 +100,7 @@ void TranslateQualifiedNamesMatcher::visit(ASTIdentifier & identifier, ASTPtr &,
{
if (data.unknownColumn(table_pos, identifier))
{
String table_name = data.tables[table_pos].first.getQualifiedNamePrefix(false);
String table_name = data.tables[table_pos].table.getQualifiedNamePrefix(false);
throw Exception("There's no column '" + identifier.name + "' in table '" + table_name + "'",
ErrorCodes::UNKNOWN_IDENTIFIER);
}
@ -96,7 +108,7 @@ void TranslateQualifiedNamesMatcher::visit(ASTIdentifier & identifier, ASTPtr &,
IdentifierSemantic::setMembership(identifier, table_pos);
/// In case if column from the joined table are in source columns, change it's name to qualified.
auto & table = data.tables[table_pos].first;
auto & table = data.tables[table_pos].table;
if (table_pos && data.hasColumn(short_name))
IdentifierSemantic::setColumnLongName(identifier, table);
else
@ -128,7 +140,7 @@ void TranslateQualifiedNamesMatcher::visit(const ASTQualifiedAsterisk & , const
DatabaseAndTableWithAlias db_and_table(ident);
for (const auto & known_table : data.tables)
if (db_and_table.satisfies(known_table.first, true))
if (db_and_table.satisfies(known_table.table, true))
return;
throw Exception("Unknown qualified identifier: " + ident->getAliasOrColumnName(), ErrorCodes::UNKNOWN_IDENTIFIER);
@ -216,13 +228,13 @@ void TranslateQualifiedNamesMatcher::visit(ASTExpressionList & node, const ASTPt
if (const auto * asterisk = child->as<ASTAsterisk>())
{
bool first_table = true;
for (const auto & [table, table_columns] : tables_with_columns)
for (const auto & table : tables_with_columns)
{
for (const auto & column_name : table_columns)
for (const auto & column_name : table.columns)
{
if (first_table || !data.join_using_columns.count(column_name))
{
addIdentifier(node.children, table, column_name, AsteriskSemantic::getAliases(*asterisk));
addIdentifier(node.children, table.table, column_name, AsteriskSemantic::getAliases(*asterisk));
}
}
@ -232,13 +244,13 @@ void TranslateQualifiedNamesMatcher::visit(ASTExpressionList & node, const ASTPt
else if (const auto * asterisk_pattern = child->as<ASTColumnsMatcher>())
{
bool first_table = true;
for (const auto & [table, table_columns] : tables_with_columns)
for (const auto & table : tables_with_columns)
{
for (const auto & column_name : table_columns)
for (const auto & column_name : table.columns)
{
if (asterisk_pattern->isColumnMatching(column_name) && (first_table || !data.join_using_columns.count(column_name)))
{
addIdentifier(node.children, table, column_name, AsteriskSemantic::getAliases(*asterisk_pattern));
addIdentifier(node.children, table.table, column_name, AsteriskSemantic::getAliases(*asterisk_pattern));
}
}
@ -249,13 +261,13 @@ void TranslateQualifiedNamesMatcher::visit(ASTExpressionList & node, const ASTPt
{
DatabaseAndTableWithAlias ident_db_and_name(qualified_asterisk->children[0]);
for (const auto & [table, table_columns] : tables_with_columns)
for (const auto & table : tables_with_columns)
{
if (ident_db_and_name.satisfies(table, true))
if (ident_db_and_name.satisfies(table.table, true))
{
for (const auto & column_name : table_columns)
for (const auto & column_name : table.columns)
{
addIdentifier(node.children, table, column_name, AsteriskSemantic::getAliases(*qualified_asterisk));
addIdentifier(node.children, table.table, column_name, AsteriskSemantic::getAliases(*qualified_asterisk));
}
break;
}

View File

@ -72,7 +72,8 @@ ASTPtr extractTableExpression(const ASTSelectQuery & select, size_t table_number
return nullptr;
}
NamesAndTypesList getColumnsFromTableExpression(const ASTTableExpression & table_expression, const Context & context)
static NamesAndTypesList getColumnsFromTableExpression(const ASTTableExpression & table_expression, const Context & context,
NamesAndTypesList & materialized, NamesAndTypesList & aliases, NamesAndTypesList & virtuals)
{
NamesAndTypesList names_and_type_list;
if (table_expression.subquery)
@ -85,34 +86,60 @@ NamesAndTypesList getColumnsFromTableExpression(const ASTTableExpression & table
const auto table_function = table_expression.table_function;
auto query_context = const_cast<Context *>(&context.getQueryContext());
const auto & function_storage = query_context->executeTableFunction(table_function);
names_and_type_list = function_storage->getSampleBlockNonMaterialized().getNamesAndTypesList();
auto & columns = function_storage->getColumns();
names_and_type_list = columns.getOrdinary();
materialized = columns.getMaterialized();
aliases = columns.getAliases();
virtuals = columns.getVirtuals();
}
else if (table_expression.database_and_table_name)
{
DatabaseAndTableWithAlias database_table(table_expression.database_and_table_name);
const auto & table = context.getTable(database_table.database, database_table.table);
names_and_type_list = table->getSampleBlockNonMaterialized().getNamesAndTypesList();
auto & columns = table->getColumns();
names_and_type_list = columns.getOrdinary();
materialized = columns.getMaterialized();
aliases = columns.getAliases();
virtuals = columns.getVirtuals();
}
return names_and_type_list;
}
std::vector<TableWithColumnNames> getDatabaseAndTablesWithColumnNames(const ASTSelectQuery & select_query, const Context & context)
NamesAndTypesList getColumnsFromTableExpression(const ASTTableExpression & table_expression, const Context & context)
{
NamesAndTypesList materialized;
NamesAndTypesList aliases;
NamesAndTypesList virtuals;
return getColumnsFromTableExpression(table_expression, context, materialized, aliases, virtuals);
}
std::vector<TableWithColumnNames> getDatabaseAndTablesWithColumnNames(const std::vector<const ASTTableExpression *> & table_expressions,
const Context & context, bool remove_duplicates)
{
std::vector<TableWithColumnNames> tables_with_columns;
if (select_query.tables() && !select_query.tables()->children.empty())
if (!table_expressions.empty())
{
String current_database = context.getCurrentDatabase();
for (const ASTTableExpression * table_expression : getTableExpressions(select_query))
for (const ASTTableExpression * table_expression : table_expressions)
{
DatabaseAndTableWithAlias table_name(*table_expression, current_database);
NamesAndTypesList names_and_types = getColumnsFromTableExpression(*table_expression, context);
removeDuplicateColumns(names_and_types);
NamesAndTypesList materialized;
NamesAndTypesList aliases;
NamesAndTypesList virtuals;
NamesAndTypesList names_and_types = getColumnsFromTableExpression(*table_expression, context, materialized, aliases, virtuals);
if (remove_duplicates)
removeDuplicateColumns(names_and_types);
tables_with_columns.emplace_back(std::move(table_name), names_and_types.getNames());
auto & table = tables_with_columns.back();
table.addHiddenColumns(materialized);
table.addHiddenColumns(aliases);
table.addHiddenColumns(virtuals);
}
}

View File

@ -17,6 +17,7 @@ const ASTTableExpression * getTableExpression(const ASTSelectQuery & select, siz
ASTPtr extractTableExpression(const ASTSelectQuery & select, size_t table_number);
NamesAndTypesList getColumnsFromTableExpression(const ASTTableExpression & table_expression, const Context & context);
std::vector<TableWithColumnNames> getDatabaseAndTablesWithColumnNames(const ASTSelectQuery & select_query, const Context & context);
std::vector<TableWithColumnNames> getDatabaseAndTablesWithColumnNames(const std::vector<const ASTTableExpression *> & table_expressions,
const Context & context, bool remove_duplicates = true);
}

View File

@ -239,8 +239,7 @@ static const ASTTablesInSelectQueryElement * getFirstTableJoin(const ASTSelectQu
if (!joined_table)
joined_table = &tables_element;
else
throw Exception("Multiple JOIN disabled or does not support the query. "
"'set allow_experimental_multiple_joins_emulation' to enable.", ErrorCodes::NOT_IMPLEMENTED);
throw Exception("Multiple JOIN disabled or does not support the query.", ErrorCodes::NOT_IMPLEMENTED);
}
}

View File

@ -0,0 +1,5 @@
2010-01-01 00:00:00
2010-01-01 00:00:00
2010-01-01 00:00:00
2010-01-01 00:00:00
2010-01-01 00:00:00

View File

@ -0,0 +1,18 @@
DROP TABLE IF EXISTS requests;
CREATE TABLE requests (
event_time DateTime,
event_date Date MATERIALIZED toDate(event_time),
event_tm DateTime ALIAS event_time
) ENGINE = MergeTree ORDER BY (event_time);
INSERT INTO requests (event_time) VALUES ('2010-01-01 00:00:00');
select * from requests where event_date > '2000-01-01';
select * from requests as t where t.event_date > '2000-01-01';
select * from requests as "t" where "t".event_date > '2000-01-01';
select * from requests as t where t.event_tm > toDate('2000-01-01');
select * from requests as `t` where `t`.event_tm > toDate('2000-01-01');
DROP TABLE requests;