safe tables order in select * with multiple joins

This commit is contained in:
chertus 2019-04-04 15:14:10 +03:00
parent 5295b89fd9
commit e578020bd3
3 changed files with 37 additions and 22 deletions

View File

@ -33,30 +33,46 @@ namespace
{
/// Replace asterisks in select_expression_list with column identifiers
struct ExtractAsterisksMatcher
class ExtractAsterisksMatcher
{
public:
using Visitor = InDepthNodeVisitor<ExtractAsterisksMatcher, true>;
struct Data
{
std::unordered_map<String, NamesAndTypesList> table_columns;
std::vector<String> tables_order;
std::shared_ptr<ASTExpressionList> new_select_expression_list;
Data(const Context & context, const std::vector<const ASTTableExpression *> & table_expressions)
{
tables_order.reserve(table_expressions.size());
for (const auto & expr : table_expressions)
{
if (expr->subquery)
{
table_columns.clear();
tables_order.clear();
break;
}
String table_name = DatabaseAndTableWithAlias(*expr, context.getCurrentDatabase()).getQualifiedNamePrefix(false);
NamesAndTypesList columns = getNamesAndTypeListFromTableExpression(*expr, context);
tables_order.push_back(table_name);
table_columns.emplace(std::move(table_name), std::move(columns));
}
}
void addTableColumns(const String & table_name)
{
auto it = table_columns.find(table_name);
if (it == table_columns.end())
throw Exception("Unknown qualified identifier: " + table_name, ErrorCodes::UNKNOWN_IDENTIFIER);
for (const auto & column : it->second)
new_select_expression_list->children.push_back(
std::make_shared<ASTIdentifier>(std::vector<String>{it->first, column.name}));
}
};
static bool needChildVisit(ASTPtr &, const ASTPtr &) { return false; }
@ -69,6 +85,7 @@ struct ExtractAsterisksMatcher
visit(*t, ast, data);
}
private:
static void visit(ASTSelectQuery & node, ASTPtr &, Data & data)
{
if (data.table_columns.empty())
@ -101,10 +118,8 @@ struct ExtractAsterisksMatcher
{
has_asterisks = true;
for (auto & pr : data.table_columns)
for (const auto & column : pr.second)
data.new_select_expression_list->children.push_back(
std::make_shared<ASTIdentifier>(std::vector<String>{pr.first, column.name}));
for (auto & table_name : data.tables_order)
data.addTableColumns(table_name);
}
else if (child->as<ASTQualifiedAsterisk>())
{
@ -114,13 +129,7 @@ struct ExtractAsterisksMatcher
throw Exception("Logical error: qualified asterisk must have exactly one child", ErrorCodes::LOGICAL_ERROR);
ASTIdentifier & identifier = child->children[0]->as<ASTIdentifier &>();
auto it = data.table_columns.find(identifier.name);
if (it == data.table_columns.end())
throw Exception("Unknown qualified identifier: " + identifier.name, ErrorCodes::UNKNOWN_IDENTIFIER);
for (const auto & column : it->second)
data.new_select_expression_list->children.push_back(
std::make_shared<ASTIdentifier>(std::vector<String>{it->first, column.name}));
data.addTableColumns(identifier.name);
}
else
data.new_select_expression_list->children.push_back(child);

View File

@ -32,14 +32,18 @@
6 6 60 60
12 12 120 120
18 18 180 180
0 0 0 0 0
6 6 60 60 600
12 12 120 120 1200
18 18 180 180 1800
0 0 0 0 0
60 600 6 60 6
120 1200 12 120 12
180 1800 18 180 18
┌─t1.a─┬─t2.a─┬─t2.b─┬─t3.b─┬─t3.c─┐
│ 0 │ 0 │ 0 │ 0 │ 0 │
│ 6 │ 6 │ 60 │ 60 │ 600 │
│ 12 │ 12 │ 120 │ 120 │ 1200 │
│ 18 │ 18 │ 180 │ 180 │ 1800 │
└──────┴──────┴──────┴──────┴──────┘
┌─t1.a─┬─t2.a─┬─t2.b─┬─t3.b─┬─t3.c─┐
│ 0 │ 0 │ 0 │ 0 │ 0 │
│ 6 │ 6 │ 60 │ 60 │ 600 │
│ 12 │ 12 │ 120 │ 120 │ 1200 │
│ 18 │ 18 │ 180 │ 180 │ 1800 │
└──────┴──────┴──────┴──────┴──────┘
0 0 0 0 0 0 0
6 6 60 60 66 66 120
12 12 120 120 132 132 240

View File

@ -60,12 +60,14 @@ join table3 as t3 on table2.b = table3.b;
select t1.*, t2.*, t3.*
from table1 as t1
join table2 as t2 on table1.a = table2.a
join table3 as t3 on table2.b = table3.b;
join table3 as t3 on table2.b = table3.b
FORMAT PrettyCompactNoEscapes;
select *
from table1 as t1
join table2 as t2 on t1.a = t2.a
join table3 as t3 on t2.b = t3.b;
join table3 as t3 on t2.b = t3.b
FORMAT PrettyCompactNoEscapes;
select t1.a as t1_a, t2.a as t2_a, t2.b as t2_b, t3.b as t3_b,
(t1.a + table2.b) as t1_t2_x, (table1.a + table3.b) as t1_t3_x, (t2.b + t3.b) as t2_t3_x