mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-21 23:21:59 +00:00
throw on ambiguous qualified column
This commit is contained in:
parent
483108f46f
commit
0f6176cc2d
@ -5,6 +5,11 @@
|
||||
namespace DB
|
||||
{
|
||||
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int AMBIGUOUS_COLUMN_NAME;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
@ -19,18 +24,37 @@ const DatabaseAndTableWithAlias & extractTable(const TableWithColumnNames & tabl
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool tryChooseTable(const ASTIdentifier & identifier, const std::vector<T> & tables, size_t & best_table_pos)
|
||||
IdentifierSemantic::ColumnMatch tryChooseTable(const ASTIdentifier & identifier, const std::vector<T> & tables,
|
||||
size_t & best_table_pos, bool allow_ambiguous)
|
||||
{
|
||||
using ColumnMatch = IdentifierSemantic::ColumnMatch;
|
||||
|
||||
best_table_pos = 0;
|
||||
size_t best_match = 0;
|
||||
auto best_match = ColumnMatch::NoMatch;
|
||||
size_t same_match = 0;
|
||||
|
||||
for (size_t i = 0; i < tables.size(); ++i)
|
||||
if (size_t match = IdentifierSemantic::canReferColumnToTable(identifier, extractTable(tables[i])))
|
||||
if (match > best_match)
|
||||
{
|
||||
auto match = IdentifierSemantic::canReferColumnToTable(identifier, extractTable(tables[i]));
|
||||
if (value(match))
|
||||
{
|
||||
if (value(match) > value(best_match))
|
||||
{
|
||||
best_match = match;
|
||||
best_table_pos = i;
|
||||
same_match = 0;
|
||||
}
|
||||
else if (match == best_match)
|
||||
++same_match;
|
||||
}
|
||||
}
|
||||
|
||||
if (value(best_match) && same_match)
|
||||
{
|
||||
if (!allow_ambiguous)
|
||||
throw Exception("Ambiguous column '" + identifier.name + "'", ErrorCodes::AMBIGUOUS_COLUMN_NAME);
|
||||
return ColumnMatch::Ambiguous;
|
||||
}
|
||||
return best_match;
|
||||
}
|
||||
|
||||
@ -89,15 +113,15 @@ size_t IdentifierSemantic::getMembership(const ASTIdentifier & identifier)
|
||||
}
|
||||
|
||||
bool IdentifierSemantic::chooseTable(const ASTIdentifier & identifier, const std::vector<DatabaseAndTableWithAlias> & tables,
|
||||
size_t & best_table_pos)
|
||||
size_t & best_table_pos, bool ambiguous)
|
||||
{
|
||||
return tryChooseTable<DatabaseAndTableWithAlias>(identifier, tables, best_table_pos);
|
||||
return value(tryChooseTable<DatabaseAndTableWithAlias>(identifier, tables, best_table_pos, ambiguous));
|
||||
}
|
||||
|
||||
bool IdentifierSemantic::chooseTable(const ASTIdentifier & identifier, const std::vector<TableWithColumnNames> & tables,
|
||||
size_t & best_table_pos)
|
||||
size_t & best_table_pos, bool ambiguous)
|
||||
{
|
||||
return tryChooseTable<TableWithColumnNames>(identifier, tables, best_table_pos);
|
||||
return value(tryChooseTable<TableWithColumnNames>(identifier, tables, best_table_pos, ambiguous));
|
||||
}
|
||||
|
||||
std::pair<String, String> IdentifierSemantic::extractDatabaseAndTable(const ASTIdentifier & identifier)
|
||||
@ -127,18 +151,22 @@ bool IdentifierSemantic::doesIdentifierBelongTo(const ASTIdentifier & identifier
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t IdentifierSemantic::canReferColumnToTable(const ASTIdentifier & identifier, const DatabaseAndTableWithAlias & db_and_table)
|
||||
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 2;
|
||||
return ColumnMatch::DatabaseAndTable;
|
||||
|
||||
/// table.column or alias.column.
|
||||
if (doesIdentifierBelongTo(identifier, db_and_table.table) ||
|
||||
doesIdentifierBelongTo(identifier, db_and_table.alias))
|
||||
return 1;
|
||||
/// alias.column
|
||||
if (doesIdentifierBelongTo(identifier, db_and_table.alias))
|
||||
return ColumnMatch::TableAlias;
|
||||
|
||||
return 0;
|
||||
/// table.column
|
||||
if (doesIdentifierBelongTo(identifier, db_and_table.table))
|
||||
return ColumnMatch::TableName;
|
||||
|
||||
return ColumnMatch::NoMatch;
|
||||
}
|
||||
|
||||
/// Checks that ast is ASTIdentifier and remove num_qualifiers_to_strip components from left.
|
||||
@ -162,10 +190,23 @@ void IdentifierSemantic::setColumnShortName(ASTIdentifier & identifier, size_t t
|
||||
|
||||
void IdentifierSemantic::setColumnNormalName(ASTIdentifier & identifier, const DatabaseAndTableWithAlias & db_and_table)
|
||||
{
|
||||
size_t match = IdentifierSemantic::canReferColumnToTable(identifier, db_and_table);
|
||||
auto match = IdentifierSemantic::canReferColumnToTable(identifier, db_and_table);
|
||||
size_t to_strip = 0;
|
||||
switch (match)
|
||||
{
|
||||
case ColumnMatch::TableName:
|
||||
case ColumnMatch::TableAlias:
|
||||
to_strip = 1;
|
||||
break;
|
||||
case ColumnMatch::DatabaseAndTable:
|
||||
to_strip = 2;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
setColumnShortName(identifier, match);
|
||||
if (match)
|
||||
setColumnShortName(identifier, to_strip);
|
||||
if (value(match))
|
||||
identifier.semantic->can_be_alias = false;
|
||||
|
||||
if (identifier.semantic->need_long_name)
|
||||
|
@ -17,6 +17,16 @@ struct IdentifierSemanticImpl
|
||||
/// Static calss to manipulate IdentifierSemanticImpl via ASTIdentifier
|
||||
struct IdentifierSemantic
|
||||
{
|
||||
enum class ColumnMatch
|
||||
{
|
||||
NoMatch,
|
||||
ColumnName, /// table has column with same name
|
||||
TableName, /// column qualified with table name
|
||||
DatabaseAndTable, /// column qualified with database and table name
|
||||
TableAlias, /// column qualified with table alias
|
||||
Ambiguous,
|
||||
};
|
||||
|
||||
/// @returns name for column identifiers
|
||||
static std::optional<String> getColumnName(const ASTIdentifier & node);
|
||||
static std::optional<String> getColumnName(const ASTPtr & ast);
|
||||
@ -26,7 +36,7 @@ struct IdentifierSemantic
|
||||
static std::optional<String> getTableName(const ASTPtr & ast);
|
||||
static std::pair<String, String> extractDatabaseAndTable(const ASTIdentifier & identifier);
|
||||
|
||||
static size_t canReferColumnToTable(const ASTIdentifier & identifier, const DatabaseAndTableWithAlias & db_and_table);
|
||||
static ColumnMatch canReferColumnToTable(const ASTIdentifier & identifier, const DatabaseAndTableWithAlias & db_and_table);
|
||||
static String columnNormalName(const ASTIdentifier & identifier, const DatabaseAndTableWithAlias & db_and_table);
|
||||
static String columnLongName(const ASTIdentifier & identifier, const DatabaseAndTableWithAlias & db_and_table);
|
||||
static void setColumnNormalName(ASTIdentifier & identifier, const DatabaseAndTableWithAlias & db_and_table);
|
||||
@ -35,8 +45,10 @@ struct IdentifierSemantic
|
||||
static bool canBeAlias(const ASTIdentifier & identifier);
|
||||
static void setMembership(ASTIdentifier & identifier, size_t table_no);
|
||||
static size_t getMembership(const ASTIdentifier & identifier);
|
||||
static bool chooseTable(const ASTIdentifier &, const std::vector<DatabaseAndTableWithAlias> & tables, size_t & best_table_pos);
|
||||
static bool chooseTable(const ASTIdentifier &, const std::vector<TableWithColumnNames> & tables, size_t & best_table_pos);
|
||||
static bool chooseTable(const ASTIdentifier &, const std::vector<DatabaseAndTableWithAlias> & tables, size_t & best_table_pos,
|
||||
bool ambiguous = false);
|
||||
static bool chooseTable(const ASTIdentifier &, const std::vector<TableWithColumnNames> & tables, size_t & best_table_pos,
|
||||
bool ambiguous = false);
|
||||
|
||||
private:
|
||||
static bool doesIdentifierBelongTo(const ASTIdentifier & identifier, const String & database, const String & table);
|
||||
@ -44,4 +56,9 @@ private:
|
||||
static void setColumnShortName(ASTIdentifier & identifier, size_t match);
|
||||
};
|
||||
|
||||
inline UInt32 value(IdentifierSemantic::ColumnMatch match)
|
||||
{
|
||||
return static_cast<UInt32>(match);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -226,7 +226,7 @@ struct ColumnAliasesMatcher
|
||||
String long_name;
|
||||
for (auto & table : data.tables)
|
||||
{
|
||||
if (IdentifierSemantic::canReferColumnToTable(node, table))
|
||||
if (value(IdentifierSemantic::canReferColumnToTable(node, table)))
|
||||
{
|
||||
if (!long_name.empty())
|
||||
throw Exception("Cannot refer column '" + node.name + "' to one table", ErrorCodes::AMBIGUOUS_COLUMN_NAME);
|
||||
|
@ -62,7 +62,8 @@ void TranslateQualifiedNamesMatcher::visit(ASTIdentifier & identifier, ASTPtr &,
|
||||
if (IdentifierSemantic::getColumnName(identifier))
|
||||
{
|
||||
size_t best_table_pos = 0;
|
||||
if (IdentifierSemantic::chooseTable(identifier, data.tables, best_table_pos))
|
||||
bool allow_ambiguous = data.join_using_columns.count(identifier.shortName());
|
||||
if (IdentifierSemantic::chooseTable(identifier, data.tables, best_table_pos, allow_ambiguous))
|
||||
IdentifierSemantic::setMembership(identifier, best_table_pos + 1);
|
||||
|
||||
/// In case if column from the joined table are in source columns, change it's name to qualified.
|
||||
|
@ -34,10 +34,20 @@ ASTIdentifier::ASTIdentifier(const String & name_, std::vector<String> && name_p
|
||||
, name_parts(name_parts_)
|
||||
, semantic(std::make_shared<IdentifierSemanticImpl>())
|
||||
{
|
||||
if (name_parts.size() && name_parts[0] == "")
|
||||
name_parts.erase(name_parts.begin());
|
||||
|
||||
if (name == "")
|
||||
{
|
||||
if (name_parts.size() == 2)
|
||||
name = name_parts[0] + '.' + name_parts[1];
|
||||
else if (name_parts.size() == 1)
|
||||
name = name_parts[0];
|
||||
}
|
||||
}
|
||||
|
||||
ASTIdentifier::ASTIdentifier(std::vector<String> && name_parts_)
|
||||
: ASTIdentifier(name_parts_.at(0) + '.' + name_parts_.at(1), std::move(name_parts_))
|
||||
: ASTIdentifier("", std::move(name_parts_))
|
||||
{}
|
||||
|
||||
void ASTIdentifier::setShortName(const String & new_name)
|
||||
|
@ -1,9 +1,9 @@
|
||||
SET enable_debug_queries = 1;
|
||||
|
||||
set allow_experimental_cross_to_join_conversion = 0;
|
||||
select * from system.one cross join system.one;
|
||||
select * from system.one l cross join system.one r;
|
||||
set allow_experimental_cross_to_join_conversion = 1;
|
||||
select * from system.one cross join system.one;
|
||||
select * from system.one l cross join system.one r;
|
||||
|
||||
DROP TABLE IF EXISTS t1_00826;
|
||||
DROP TABLE IF EXISTS t2_00826;
|
||||
|
Loading…
Reference in New Issue
Block a user