ClickHouse/dbms/src/Interpreters/IdentifierSemantic.cpp

263 lines
8.3 KiB
C++
Raw Normal View History

#include <Common/typeid_cast.h>
#include <Interpreters/IdentifierSemantic.h>
2020-03-12 18:04:29 +00:00
#include <Storages/StorageID.h>
namespace DB
{
2019-10-17 21:08:28 +00:00
namespace ErrorCodes
{
2020-02-25 18:02:41 +00:00
extern const int LOGICAL_ERROR;
2019-10-17 21:08:28 +00:00
extern const int AMBIGUOUS_COLUMN_NAME;
}
2019-10-16 17:33:53 +00:00
namespace
{
template <typename T>
std::optional<size_t> tryChooseTable(const ASTIdentifier & identifier, const std::vector<T> & tables, bool allow_ambiguous)
2019-10-16 17:33:53 +00:00
{
2019-10-17 21:08:28 +00:00
using ColumnMatch = IdentifierSemantic::ColumnMatch;
size_t best_table_pos = 0;
2019-10-17 21:08:28 +00:00
auto best_match = ColumnMatch::NoMatch;
size_t same_match = 0;
2019-10-16 17:33:53 +00:00
for (size_t i = 0; i < tables.size(); ++i)
2019-10-17 21:08:28 +00:00
{
auto match = IdentifierSemantic::canReferColumnToTable(identifier, tables[i]);
2019-10-24 13:04:50 +00:00
if (match != ColumnMatch::NoMatch)
2019-10-17 21:08:28 +00:00
{
2019-10-24 13:04:50 +00:00
if (match > best_match)
2019-10-16 17:33:53 +00:00
{
best_match = match;
best_table_pos = i;
2019-10-17 21:08:28 +00:00
same_match = 0;
2019-10-16 17:33:53 +00:00
}
2019-10-17 21:08:28 +00:00
else if (match == best_match)
++same_match;
}
}
2019-10-16 17:33:53 +00:00
2019-10-24 13:04:50 +00:00
if ((best_match != ColumnMatch::NoMatch) && same_match)
2019-10-17 21:08:28 +00:00
{
if (!allow_ambiguous)
throw Exception("Ambiguous column '" + identifier.name + "'", ErrorCodes::AMBIGUOUS_COLUMN_NAME);
best_match = ColumnMatch::Ambiguous;
return {};
2019-10-17 21:08:28 +00:00
}
if (best_match != ColumnMatch::NoMatch)
return best_table_pos;
return {};
2019-10-16 17:33:53 +00:00
}
}
std::optional<String> IdentifierSemantic::getColumnName(const ASTIdentifier & node)
{
if (!node.semantic->special)
return node.name;
return {};
}
std::optional<String> IdentifierSemantic::getColumnName(const ASTPtr & ast)
{
if (ast)
2019-03-11 13:22:51 +00:00
if (const auto * id = ast->as<ASTIdentifier>())
if (!id->semantic->special)
return id->name;
return {};
}
std::optional<String> IdentifierSemantic::getTableName(const ASTIdentifier & node)
{
if (node.semantic->special)
return node.name;
return {};
}
std::optional<String> IdentifierSemantic::getTableName(const ASTPtr & ast)
{
if (ast)
2019-03-11 13:22:51 +00:00
if (const auto * id = ast->as<ASTIdentifier>())
if (id->semantic->special)
return id->name;
return {};
}
std::optional<ASTIdentifier> IdentifierSemantic::uncover(const ASTIdentifier & identifier)
{
if (identifier.semantic->covered)
{
std::vector<String> name_parts = identifier.name_parts;
return ASTIdentifier(std::move(name_parts));
}
return {};
}
void IdentifierSemantic::coverName(ASTIdentifier & identifier, const String & alias)
{
identifier.setShortName(alias);
identifier.semantic->covered = true;
}
2019-02-11 19:14:57 +00:00
bool IdentifierSemantic::canBeAlias(const ASTIdentifier & identifier)
{
return identifier.semantic->can_be_alias;
}
void IdentifierSemantic::setMembership(ASTIdentifier & identifier, size_t table_pos)
{
identifier.semantic->membership = table_pos;
identifier.semantic->can_be_alias = false;
}
std::optional<size_t> IdentifierSemantic::getMembership(const ASTIdentifier & identifier)
{
return identifier.semantic->membership;
}
2019-02-11 19:14:57 +00:00
std::optional<size_t> IdentifierSemantic::chooseTable(const ASTIdentifier & identifier, const std::vector<DatabaseAndTableWithAlias> & tables,
bool ambiguous)
{
return tryChooseTable<DatabaseAndTableWithAlias>(identifier, tables, ambiguous);
2019-10-16 17:33:53 +00:00
}
std::optional<size_t> IdentifierSemantic::chooseTable(const ASTIdentifier & identifier, const std::vector<TableWithColumnNames> & tables,
bool ambiguous)
2019-10-16 17:33:53 +00:00
{
return tryChooseTable<TableWithColumnNames>(identifier, tables, ambiguous);
}
std::optional<size_t> IdentifierSemantic::chooseTable(const ASTIdentifier & identifier, const std::vector<TableWithColumnNamesAndTypes> & tables,
bool ambiguous)
{
return tryChooseTable<TableWithColumnNamesAndTypes>(identifier, tables, ambiguous);
}
2020-03-12 18:04:29 +00:00
StorageID IdentifierSemantic::extractDatabaseAndTable(const ASTIdentifier & identifier)
{
if (identifier.name_parts.size() > 2)
throw Exception("Logical error: more than two components in table expression", ErrorCodes::LOGICAL_ERROR);
if (identifier.name_parts.size() == 2)
2020-03-12 18:04:29 +00:00
return { identifier.name_parts[0], identifier.name_parts[1], identifier.uuid };
return { "", identifier.name, identifier.uuid };
}
2019-11-13 16:49:29 +00:00
std::optional<String> IdentifierSemantic::extractNestedName(const ASTIdentifier & identifier, const String & table_name)
{
if (identifier.name_parts.size() == 3 && table_name == identifier.name_parts[0])
return identifier.name_parts[1] + '.' + identifier.name_parts[2];
else if (identifier.name_parts.size() == 2)
return identifier.name_parts[0] + '.' + identifier.name_parts[1];
return {};
}
bool IdentifierSemantic::doesIdentifierBelongTo(const ASTIdentifier & identifier, const String & database, const String & table)
{
size_t num_components = identifier.name_parts.size();
if (num_components >= 3)
return identifier.name_parts[0] == database &&
identifier.name_parts[1] == table;
return false;
}
bool IdentifierSemantic::doesIdentifierBelongTo(const ASTIdentifier & identifier, const String & table)
{
size_t num_components = identifier.name_parts.size();
if (num_components >= 2)
return identifier.name_parts[0] == table;
return false;
}
2019-10-17 21:08:28 +00:00
IdentifierSemantic::ColumnMatch IdentifierSemantic::canReferColumnToTable(const ASTIdentifier & identifier,
const DatabaseAndTableWithAlias & db_and_table)
{
/// database.table.column
if (doesIdentifierBelongTo(identifier, db_and_table.database, db_and_table.table))
return ColumnMatch::DbAndTable;
2019-10-17 21:08:28 +00:00
/// alias.column
if (doesIdentifierBelongTo(identifier, db_and_table.alias))
return ColumnMatch::TableAlias;
2019-10-17 21:08:28 +00:00
/// table.column
if (doesIdentifierBelongTo(identifier, db_and_table.table))
{
if (!db_and_table.alias.empty())
return ColumnMatch::AliasedTableName;
else
return ColumnMatch::TableName;
}
return ColumnMatch::NoMatch;
}
IdentifierSemantic::ColumnMatch IdentifierSemantic::canReferColumnToTable(const ASTIdentifier & identifier,
const TableWithColumnNames & db_and_table)
{
/// TODO: ColumnName match logic is disabled cause caller's code is not ready for it
return canReferColumnToTable(identifier, db_and_table.table);
}
IdentifierSemantic::ColumnMatch IdentifierSemantic::canReferColumnToTable(const ASTIdentifier & identifier,
const TableWithColumnNamesAndTypes & db_and_table)
{
ColumnMatch match = canReferColumnToTable(identifier, db_and_table.table);
if (match == ColumnMatch::NoMatch && identifier.isShort() && db_and_table.hasColumn(identifier.shortName()))
match = ColumnMatch::ColumnName;
return match;
}
/// Strip qualificators from left side of column name.
/// Example: 'database.table.name' -> 'name'.
void IdentifierSemantic::setColumnShortName(ASTIdentifier & identifier, const DatabaseAndTableWithAlias & db_and_table)
{
2019-10-17 21:08:28 +00:00
auto match = IdentifierSemantic::canReferColumnToTable(identifier, db_and_table);
size_t to_strip = 0;
switch (match)
{
case ColumnMatch::TableName:
case ColumnMatch::AliasedTableName:
2019-10-17 21:08:28 +00:00
case ColumnMatch::TableAlias:
to_strip = 1;
break;
case ColumnMatch::DbAndTable:
2019-10-17 21:08:28 +00:00
to_strip = 2;
break;
default:
break;
}
if (!to_strip)
return;
std::vector<String> stripped(identifier.name_parts.begin() + to_strip, identifier.name_parts.end());
DB::String new_name;
for (const auto & part : stripped)
{
if (!new_name.empty())
new_name += '.';
new_name += part;
}
identifier.name.swap(new_name);
}
void IdentifierSemantic::setColumnLongName(ASTIdentifier & identifier, const DatabaseAndTableWithAlias & db_and_table)
{
String prefix = db_and_table.getQualifiedNamePrefix();
if (!prefix.empty())
{
String short_name = identifier.shortName();
identifier.name = prefix + short_name;
prefix.resize(prefix.size() - 1); /// crop dot
identifier.name_parts = {prefix, short_name};
}
}
}