mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-09-21 01:00:48 +00:00
fix 'There's no column' error for materialized and alias columns
This commit is contained in:
parent
142932ebdf
commit
8c9ca6891f
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -20,7 +20,7 @@ const DatabaseAndTableWithAlias & extractTable(const DatabaseAndTableWithAlias &
|
||||
|
||||
const DatabaseAndTableWithAlias & extractTable(const TableWithColumnNames & table)
|
||||
{
|
||||
return table.first;
|
||||
return table.table;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
return columns;
|
||||
};
|
||||
tables_with_columns.emplace_back(DatabaseAndTableWithAlias{}, 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));
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
@ -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;
|
Loading…
Reference in New Issue
Block a user