throw on ambiguous qualified column

This commit is contained in:
chertus 2019-10-18 00:08:28 +03:00
parent 483108f46f
commit 0f6176cc2d
6 changed files with 95 additions and 26 deletions

View File

@ -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)

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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.

View File

@ -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)

View File

@ -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;