mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-25 17:12:03 +00:00
commit
06a8a421a2
@ -443,7 +443,7 @@ namespace ErrorCodes
|
||||
extern const int CANNOT_PTHREAD_ATTR = 468;
|
||||
extern const int VIOLATED_CONSTRAINT = 469;
|
||||
extern const int QUERY_IS_NOT_SUPPORTED_IN_LIVE_VIEW = 470;
|
||||
extern const int SETTINGS_ARE_NOT_SUPPORTED = 471;
|
||||
extern const int INVALID_SETTING_VALUE = 471;
|
||||
extern const int READONLY_SETTING = 472;
|
||||
extern const int DEADLOCK_AVOIDED = 473;
|
||||
extern const int INVALID_TEMPLATE_FORMAT = 474;
|
||||
|
@ -398,6 +398,7 @@ struct Settings : public SettingsCollection<Settings>
|
||||
M(SettingBool, partial_revokes, false, "Makes it possible to revoke privileges partially.", 0) \
|
||||
M(SettingBool, deduplicate_blocks_in_dependent_materialized_views, false, "Should deduplicate blocks for materialized views if the block is not a duplicate for the table. Use true to always deduplicate in dependent tables.", 0) \
|
||||
M(SettingBool, use_compact_format_in_distributed_parts_names, false, "Changes format of directories names for distributed table insert parts.", 0) \
|
||||
M(SettingUInt64, multiple_joins_rewriter_version, 1, "1 or 2. Second rewriter version knows about table columns and keep not clashed names as is.", 0) \
|
||||
\
|
||||
/** Obsolete settings that do nothing but left for compatibility reasons. Remove each one after half a year of obsolescence. */ \
|
||||
\
|
||||
|
@ -4,7 +4,6 @@
|
||||
#include <Interpreters/CrossToInnerJoinVisitor.h>
|
||||
#include <Interpreters/DatabaseAndTableWithAlias.h>
|
||||
#include <Interpreters/IdentifierSemantic.h>
|
||||
#include <Interpreters/QueryAliasesVisitor.h>
|
||||
#include <Interpreters/misc.h>
|
||||
#include <Parsers/ASTSelectQuery.h>
|
||||
#include <Parsers/ASTSubquery.h>
|
||||
@ -95,7 +94,7 @@ public:
|
||||
|
||||
CheckExpressionVisitorData(const std::vector<JoinedElement> & tables_,
|
||||
const std::vector<TableWithColumnNamesAndTypes> & tables_with_columns,
|
||||
Aliases && aliases_)
|
||||
const Aliases & aliases_)
|
||||
: joined_tables(tables_)
|
||||
, tables(tables_with_columns)
|
||||
, aliases(aliases_)
|
||||
@ -168,7 +167,7 @@ private:
|
||||
const std::vector<JoinedElement> & joined_tables;
|
||||
const std::vector<TableWithColumnNamesAndTypes> & tables;
|
||||
std::map<size_t, std::vector<ASTPtr>> asts_to_join_on;
|
||||
Aliases aliases;
|
||||
const Aliases & aliases;
|
||||
bool ands_only;
|
||||
|
||||
size_t canMoveEqualsToJoinOn(const ASTFunction & node)
|
||||
@ -323,13 +322,7 @@ void CrossToInnerJoinMatcher::visit(ASTSelectQuery & select, ASTPtr &, Data & da
|
||||
if (!select.where())
|
||||
return;
|
||||
|
||||
Aliases aliases;
|
||||
QueryAliasesVisitor::Data query_aliases_data{aliases};
|
||||
if (ASTPtr with = select.with())
|
||||
QueryAliasesVisitor(query_aliases_data).visit(with);
|
||||
QueryAliasesVisitor(query_aliases_data).visit(select.select());
|
||||
|
||||
CheckExpressionVisitor::Data visitor_data{joined_tables, data.tables_with_columns, std::move(aliases)};
|
||||
CheckExpressionVisitor::Data visitor_data{joined_tables, data.tables_with_columns, data.aliases};
|
||||
CheckExpressionVisitor(visitor_data).visit(select.where());
|
||||
|
||||
if (visitor_data.complex())
|
||||
|
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <Interpreters/InDepthNodeVisitor.h>
|
||||
#include <Interpreters/Aliases.h>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
@ -15,6 +16,7 @@ public:
|
||||
struct Data
|
||||
{
|
||||
const std::vector<TableWithColumnNamesAndTypes> & tables_with_columns;
|
||||
const Aliases & aliases;
|
||||
const String current_database;
|
||||
bool done = false;
|
||||
};
|
||||
|
@ -52,6 +52,7 @@
|
||||
#include <Interpreters/AnalyzedJoin.h>
|
||||
#include <Interpreters/Join.h>
|
||||
#include <Interpreters/JoinedTables.h>
|
||||
#include <Interpreters/QueryAliasesVisitor.h>
|
||||
|
||||
#include <Storages/MergeTree/MergeTreeData.h>
|
||||
#include <Storages/MergeTree/MergeTreeWhereOptimizer.h>
|
||||
@ -115,6 +116,7 @@ namespace ErrorCodes
|
||||
extern const int PARAMETER_OUT_OF_BOUND;
|
||||
extern const int INVALID_LIMIT_EXPRESSION;
|
||||
extern const int INVALID_WITH_FILL_EXPRESSION;
|
||||
extern const int INVALID_SETTING_VALUE;
|
||||
}
|
||||
|
||||
/// Assumes `storage` is set and the table filter (row-level security) is not empty.
|
||||
@ -264,13 +266,24 @@ InterpreterSelectQuery::InterpreterSelectQuery(
|
||||
/// Rewrite JOINs
|
||||
if (!has_input && joined_tables.tablesCount() > 1)
|
||||
{
|
||||
CrossToInnerJoinVisitor::Data cross_to_inner{joined_tables.tablesWithColumns(), context->getCurrentDatabase()};
|
||||
ASTSelectQuery & select = getSelectQuery();
|
||||
|
||||
Aliases aliases;
|
||||
if (ASTPtr with = select.with())
|
||||
QueryAliasesNoSubqueriesVisitor(aliases).visit(with);
|
||||
QueryAliasesNoSubqueriesVisitor(aliases).visit(select.select());
|
||||
|
||||
CrossToInnerJoinVisitor::Data cross_to_inner{joined_tables.tablesWithColumns(), aliases, context->getCurrentDatabase()};
|
||||
CrossToInnerJoinVisitor(cross_to_inner).visit(query_ptr);
|
||||
|
||||
JoinToSubqueryTransformVisitor::Data join_to_subs_data{*context};
|
||||
size_t rewriter_version = settings.multiple_joins_rewriter_version;
|
||||
if (!rewriter_version || rewriter_version > 2)
|
||||
throw Exception("Bad multiple_joins_rewriter_version setting value: " + settings.multiple_joins_rewriter_version.toString(),
|
||||
ErrorCodes::INVALID_SETTING_VALUE);
|
||||
JoinToSubqueryTransformVisitor::Data join_to_subs_data{joined_tables.tablesWithColumns(), aliases, rewriter_version};
|
||||
JoinToSubqueryTransformVisitor(join_to_subs_data).visit(query_ptr);
|
||||
|
||||
joined_tables.reset(getSelectQuery());
|
||||
joined_tables.reset(select);
|
||||
joined_tables.resolveTables();
|
||||
|
||||
if (storage && joined_tables.isLeftTableSubquery())
|
||||
|
@ -4,8 +4,6 @@
|
||||
#include <Interpreters/IdentifierSemantic.h>
|
||||
#include <Interpreters/AsteriskSemantic.h>
|
||||
#include <Interpreters/DatabaseAndTableWithAlias.h>
|
||||
#include <Interpreters/Context.h>
|
||||
#include <Interpreters/getTableExpressions.h>
|
||||
#include <Parsers/ASTSelectQuery.h>
|
||||
#include <Parsers/ASTSubquery.h>
|
||||
#include <Parsers/ASTTablesInSelectQuery.h>
|
||||
@ -31,6 +29,24 @@ namespace ErrorCodes
|
||||
namespace
|
||||
{
|
||||
|
||||
/// @note we use `--` prefix for unique short names and `--.` for subqueries.
|
||||
/// It expects that user do not use names starting with `--` and column names starting with dot.
|
||||
ASTPtr makeSubqueryTemplate()
|
||||
{
|
||||
ParserTablesInSelectQueryElement parser(true);
|
||||
ASTPtr subquery_template = parseQuery(parser, "(select * from _t) as `--.s`", 0);
|
||||
if (!subquery_template)
|
||||
throw Exception("Cannot parse subquery template", ErrorCodes::LOGICAL_ERROR);
|
||||
return subquery_template;
|
||||
}
|
||||
|
||||
ASTPtr makeSubqueryQualifiedAsterisk()
|
||||
{
|
||||
auto asterisk = std::make_shared<ASTQualifiedAsterisk>();
|
||||
asterisk->children.emplace_back(std::make_shared<ASTIdentifier>("--.s"));
|
||||
return asterisk;
|
||||
}
|
||||
|
||||
/// Replace asterisks in select_expression_list with column identifiers
|
||||
class ExtractAsterisksMatcher
|
||||
{
|
||||
@ -41,20 +57,13 @@ public:
|
||||
std::vector<String> tables_order;
|
||||
std::shared_ptr<ASTExpressionList> new_select_expression_list;
|
||||
|
||||
Data(const Context & context, const std::vector<const ASTTableExpression *> & table_expressions)
|
||||
explicit Data(const std::vector<TableWithColumnNamesAndTypes> & tables)
|
||||
{
|
||||
tables_order.reserve(table_expressions.size());
|
||||
for (const auto & expr : table_expressions)
|
||||
tables_order.reserve(tables.size());
|
||||
for (const auto & table : tables)
|
||||
{
|
||||
if (expr->subquery)
|
||||
{
|
||||
table_columns.clear();
|
||||
tables_order.clear();
|
||||
break;
|
||||
}
|
||||
|
||||
String table_name = DatabaseAndTableWithAlias(*expr, context.getCurrentDatabase()).getQualifiedNamePrefix(false);
|
||||
NamesAndTypesList columns = getColumnsFromTableExpression(*expr, context);
|
||||
String table_name = table.table.getQualifiedNamePrefix(false);
|
||||
NamesAndTypesList columns = table.columns;
|
||||
tables_order.push_back(table_name);
|
||||
table_columns.emplace(std::move(table_name), std::move(columns));
|
||||
}
|
||||
@ -258,7 +267,6 @@ struct AppendSemanticVisitorData
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/// Replaces table elements with pair.
|
||||
struct RewriteTablesVisitorData
|
||||
{
|
||||
@ -279,23 +287,7 @@ struct RewriteTablesVisitorData
|
||||
}
|
||||
};
|
||||
|
||||
/// Attach alias to the first visited subquery
|
||||
struct SetSubqueryAliasVisitorData
|
||||
{
|
||||
using TypeToVisit = ASTSubquery;
|
||||
|
||||
const String & alias;
|
||||
bool done = false;
|
||||
|
||||
void visit(ASTSubquery &, ASTPtr & ast)
|
||||
{
|
||||
if (done)
|
||||
return;
|
||||
ast->setAlias(alias);
|
||||
done = true;
|
||||
}
|
||||
};
|
||||
|
||||
template <size_t version = 1>
|
||||
bool needRewrite(ASTSelectQuery & select, std::vector<const ASTTableExpression *> & table_expressions)
|
||||
{
|
||||
if (!select.tables())
|
||||
@ -346,7 +338,7 @@ bool needRewrite(ASTSelectQuery & select, std::vector<const ASTTableExpression *
|
||||
return false;
|
||||
|
||||
/// it's not trivial to support mix of JOIN ON & JOIN USING cause of short names
|
||||
if (num_using)
|
||||
if (num_using && version <= 1)
|
||||
throw Exception("Multiple JOIN does not support USING", ErrorCodes::NOT_IMPLEMENTED);
|
||||
if (num_array_join)
|
||||
throw Exception("Multiple JOIN does not support mix with ARRAY JOINs", ErrorCodes::NOT_IMPLEMENTED);
|
||||
@ -355,23 +347,434 @@ bool needRewrite(ASTSelectQuery & select, std::vector<const ASTTableExpression *
|
||||
|
||||
using RewriteMatcher = OneTypeMatcher<RewriteTablesVisitorData>;
|
||||
using RewriteVisitor = InDepthNodeVisitor<RewriteMatcher, true>;
|
||||
using SetSubqueryAliasMatcher = OneTypeMatcher<SetSubqueryAliasVisitorData>;
|
||||
using SetSubqueryAliasVisitor = InDepthNodeVisitor<SetSubqueryAliasMatcher, true>;
|
||||
using ExtractAsterisksVisitor = ConstInDepthNodeVisitor<ExtractAsterisksMatcher, true>;
|
||||
using ColumnAliasesVisitor = ConstInDepthNodeVisitor<ColumnAliasesMatcher, true>;
|
||||
using AppendSemanticMatcher = OneTypeMatcher<AppendSemanticVisitorData>;
|
||||
using AppendSemanticVisitor = InDepthNodeVisitor<AppendSemanticMatcher, true>;
|
||||
|
||||
/// V2 specific visitors
|
||||
|
||||
struct CollectColumnIdentifiersMatcher
|
||||
{
|
||||
using Data = std::vector<ASTIdentifier *>;
|
||||
|
||||
static bool needChildVisit(const ASTPtr & node, const ASTPtr &)
|
||||
{
|
||||
/// Do not go into subqueries. Do not collect table identifiers. Do not get identifier from 't.*'.
|
||||
return !node->as<ASTSubquery>() &&
|
||||
!node->as<ASTTablesInSelectQuery>() &&
|
||||
!node->as<ASTQualifiedAsterisk>();
|
||||
}
|
||||
|
||||
static void visit(const ASTPtr & ast, Data & data)
|
||||
{
|
||||
if (auto * t = ast->as<ASTIdentifier>())
|
||||
visit(*t, ast, data);
|
||||
}
|
||||
|
||||
static void visit(const ASTIdentifier & ident, const ASTPtr &, Data & data)
|
||||
{
|
||||
data.push_back(const_cast<ASTIdentifier *>(&ident));
|
||||
}
|
||||
};
|
||||
using CollectColumnIdentifiersVisitor = ConstInDepthNodeVisitor<CollectColumnIdentifiersMatcher, true>;
|
||||
|
||||
struct CheckAliasDependencyVisitorData
|
||||
{
|
||||
using TypeToVisit = ASTIdentifier;
|
||||
|
||||
const Aliases & aliases;
|
||||
const ASTIdentifier * dependency = nullptr;
|
||||
|
||||
void visit(ASTIdentifier & ident, ASTPtr &)
|
||||
{
|
||||
if (!dependency && aliases.count(ident.name))
|
||||
dependency = &ident;
|
||||
}
|
||||
};
|
||||
using CheckAliasDependencyMatcher = OneTypeMatcher<CheckAliasDependencyVisitorData>;
|
||||
using CheckAliasDependencyVisitor = InDepthNodeVisitor<CheckAliasDependencyMatcher, true>;
|
||||
|
||||
struct RewriteWithAliasMatcher
|
||||
{
|
||||
using Data = std::unordered_map<String, ASTPtr>;
|
||||
|
||||
static bool needChildVisit(const ASTPtr & node, const ASTPtr &)
|
||||
{
|
||||
return !node->as<ASTSubquery>();
|
||||
}
|
||||
|
||||
static void visit(ASTPtr & ast, Data & data)
|
||||
{
|
||||
String alias = ast->tryGetAlias();
|
||||
if (!alias.empty())
|
||||
{
|
||||
auto it = data.find(alias);
|
||||
if (it != data.end() && it->second.get() == ast.get())
|
||||
ast = std::make_shared<ASTIdentifier>(alias);
|
||||
}
|
||||
}
|
||||
};
|
||||
using RewriteWithAliasVisitor = InDepthNodeVisitor<RewriteWithAliasMatcher, true>;
|
||||
|
||||
class SubqueryExpressionsRewriteMatcher
|
||||
{
|
||||
public:
|
||||
struct Data
|
||||
{
|
||||
ASTPtr expression_list;
|
||||
bool done = false;
|
||||
};
|
||||
|
||||
static bool needChildVisit(ASTPtr & node, ASTPtr &)
|
||||
{
|
||||
return !node->as<ASTSelectQuery>();
|
||||
}
|
||||
|
||||
static void visit(ASTPtr & ast, Data & data)
|
||||
{
|
||||
if (auto * t = ast->as<ASTSelectQuery>())
|
||||
visit(*t, ast, data);
|
||||
}
|
||||
|
||||
private:
|
||||
static void visit(ASTSelectQuery & select, ASTPtr &, Data & data)
|
||||
{
|
||||
if (!data.done)
|
||||
select.setExpression(ASTSelectQuery::Expression::SELECT, std::move(data.expression_list));
|
||||
data.done = true;
|
||||
}
|
||||
};
|
||||
using SubqueryExpressionsRewriteVisitor = InDepthNodeVisitor<SubqueryExpressionsRewriteMatcher, true>;
|
||||
|
||||
struct TableNeededColumns
|
||||
{
|
||||
const DatabaseAndTableWithAlias & table;
|
||||
NameSet no_clashes = {};
|
||||
NameSet alias_clashes = {};
|
||||
std::unordered_map<String, String> column_clashes = {};
|
||||
|
||||
void fillExpressionList(ASTExpressionList & expression_list) const
|
||||
{
|
||||
size_t columns_count = no_clashes.size() + column_clashes.size() + alias_clashes.size();
|
||||
expression_list.children.reserve(expression_list.children.size() + columns_count);
|
||||
|
||||
String table_name = table.getQualifiedNamePrefix(false);
|
||||
|
||||
for (auto & column : no_clashes)
|
||||
addShortName(column, expression_list);
|
||||
|
||||
for (auto & column : alias_clashes)
|
||||
addShortName(column, expression_list);
|
||||
|
||||
for (auto & [column, alias] : column_clashes)
|
||||
addAliasedName(table_name, column, alias, expression_list);
|
||||
}
|
||||
|
||||
static void addShortName(const String & column, ASTExpressionList & expression_list)
|
||||
{
|
||||
auto ident = std::make_shared<ASTIdentifier>(column);
|
||||
expression_list.children.emplace_back(std::move(ident));
|
||||
}
|
||||
|
||||
/// t.x as `some`
|
||||
static void addAliasedName(const String & table, const String & column, const String & alias, ASTExpressionList & expression_list)
|
||||
{
|
||||
auto ident = std::make_shared<ASTIdentifier>(std::vector<String>{table, column});
|
||||
ident->setAlias(alias);
|
||||
expression_list.children.emplace_back(std::move(ident));
|
||||
}
|
||||
};
|
||||
|
||||
class UniqueShortNames
|
||||
{
|
||||
public:
|
||||
/// We know that long names are unique (do not clashes with others).
|
||||
/// So we could make unique names base on this knolage by adding some unused prefix.
|
||||
static constexpr const char * pattern = "--";
|
||||
|
||||
String longToShort(const String & long_name)
|
||||
{
|
||||
auto it = long_to_short.find(long_name);
|
||||
if (it != long_to_short.end())
|
||||
return it->second;
|
||||
|
||||
String short_name = generateUniqueName(long_name);
|
||||
long_to_short.emplace(long_name, short_name);
|
||||
return short_name;
|
||||
}
|
||||
|
||||
private:
|
||||
std::unordered_map<String, String> long_to_short;
|
||||
|
||||
String generateUniqueName(const String & long_name)
|
||||
{
|
||||
return String(pattern) + long_name;
|
||||
}
|
||||
};
|
||||
|
||||
size_t countTablesWithColumn(const std::vector<TableWithColumnNamesAndTypes> & tables, const String & short_name)
|
||||
{
|
||||
size_t count = 0;
|
||||
for (auto & table : tables)
|
||||
if (table.hasColumn(short_name))
|
||||
++count;
|
||||
return count;
|
||||
}
|
||||
|
||||
/// 'select `--t.x`, `--t.x`, ...' -> 'select `--t.x` as `t.x`, `t.x`, ...'
|
||||
void restoreName(ASTIdentifier & ident, const String & original_name, NameSet & restored_names)
|
||||
{
|
||||
if (!ident.tryGetAlias().empty())
|
||||
return;
|
||||
if (original_name.empty())
|
||||
return;
|
||||
|
||||
if (!restored_names.count(original_name))
|
||||
{
|
||||
ident.setAlias(original_name);
|
||||
restored_names.emplace(original_name);
|
||||
}
|
||||
else
|
||||
ident.setShortName(original_name);
|
||||
}
|
||||
|
||||
/// Find clashes and normalize names
|
||||
/// 1. If column name has no clashes make all its occurrences short: 'table.column' -> 'column', 'table_alias.column' -> 'column'.
|
||||
/// 2. If column name can't be short cause of alias with same name generate and use unique name for it.
|
||||
/// 3. If column clashes with another column generate and use unique names for them.
|
||||
/// 4. If column clashes with another column and it's short - it's 'ambiguous column' error.
|
||||
/// 5. If column clashes with alias add short column name to select list. It would be removed later if not needed.
|
||||
std::vector<TableNeededColumns> normalizeColumnNamesExtractNeeded(
|
||||
const std::vector<TableWithColumnNamesAndTypes> & tables,
|
||||
const Aliases & aliases,
|
||||
const std::vector<ASTIdentifier *> & identifiers,
|
||||
const std::unordered_set<ASTIdentifier *> & public_identifiers,
|
||||
UniqueShortNames & unique_names)
|
||||
{
|
||||
size_t last_table_pos = tables.size() - 1;
|
||||
|
||||
NameSet restored_names;
|
||||
std::vector<TableNeededColumns> needed_columns;
|
||||
needed_columns.reserve(tables.size());
|
||||
for (auto & table : tables)
|
||||
needed_columns.push_back(TableNeededColumns{table.table});
|
||||
|
||||
for (ASTIdentifier * ident : identifiers)
|
||||
{
|
||||
bool got_alias = aliases.count(ident->name);
|
||||
|
||||
if (auto table_pos = IdentifierSemantic::chooseTable(*ident, tables))
|
||||
{
|
||||
if (!ident->isShort())
|
||||
{
|
||||
if (got_alias)
|
||||
throw Exception("Alias clashes with qualified column '" + ident->name + "'", ErrorCodes::AMBIGUOUS_COLUMN_NAME);
|
||||
|
||||
String short_name = ident->shortName();
|
||||
String original_long_name;
|
||||
if (public_identifiers.count(ident))
|
||||
original_long_name = ident->name;
|
||||
|
||||
size_t count = countTablesWithColumn(tables, short_name);
|
||||
|
||||
if (count > 1 || aliases.count(short_name))
|
||||
{
|
||||
auto & table = tables[*table_pos];
|
||||
IdentifierSemantic::setColumnLongName(*ident, table.table); /// table.column -> table_alias.column
|
||||
auto & unique_long_name = ident->name;
|
||||
|
||||
/// For tables moved into subselects we need unique short names for clashed names
|
||||
if (*table_pos != last_table_pos)
|
||||
{
|
||||
String unique_short_name = unique_names.longToShort(unique_long_name);
|
||||
ident->setShortName(unique_short_name);
|
||||
needed_columns[*table_pos].column_clashes.emplace(short_name, unique_short_name);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ident->setShortName(short_name); /// table.column -> column
|
||||
needed_columns[*table_pos].no_clashes.emplace(short_name);
|
||||
}
|
||||
|
||||
restoreName(*ident, original_long_name, restored_names);
|
||||
}
|
||||
else if (got_alias)
|
||||
needed_columns[*table_pos].alias_clashes.emplace(ident->shortName());
|
||||
else
|
||||
needed_columns[*table_pos].no_clashes.emplace(ident->shortName());
|
||||
}
|
||||
else if (!got_alias)
|
||||
throw Exception("Unknown column name '" + ident->name + "'", ErrorCodes::UNKNOWN_IDENTIFIER);
|
||||
}
|
||||
|
||||
return needed_columns;
|
||||
}
|
||||
|
||||
/// Make expression list for current subselect
|
||||
std::shared_ptr<ASTExpressionList> subqueryExpressionList(
|
||||
size_t table_pos,
|
||||
const std::vector<TableNeededColumns> & needed_columns,
|
||||
const std::vector<std::vector<ASTPtr>> & alias_pushdown)
|
||||
{
|
||||
auto expression_list = std::make_shared<ASTExpressionList>();
|
||||
|
||||
/// First time extract needed left table columns manually.
|
||||
/// Next times extract left table columns via QualifiedAsterisk: `--s`.*
|
||||
if (table_pos == 1)
|
||||
needed_columns[0].fillExpressionList(*expression_list);
|
||||
else
|
||||
expression_list->children.emplace_back(makeSubqueryQualifiedAsterisk());
|
||||
|
||||
/// Add needed right table columns
|
||||
needed_columns[table_pos].fillExpressionList(*expression_list);
|
||||
|
||||
for (auto & expr : alias_pushdown[table_pos])
|
||||
expression_list->children.emplace_back(std::move(expr));
|
||||
|
||||
return expression_list;
|
||||
}
|
||||
|
||||
} /// namelesspace
|
||||
|
||||
|
||||
void JoinToSubqueryTransformMatcher::visit(ASTPtr & ast, Data & data)
|
||||
{
|
||||
if (auto * t = ast->as<ASTSelectQuery>())
|
||||
visit(*t, ast, data);
|
||||
{
|
||||
if (data.version == 1)
|
||||
visitV1(*t, ast, data);
|
||||
else
|
||||
visitV2(*t, ast, data);
|
||||
}
|
||||
}
|
||||
|
||||
void JoinToSubqueryTransformMatcher::visit(ASTSelectQuery & select, ASTPtr &, Data & data)
|
||||
/// The reason for V2: not to alias columns without clashes.
|
||||
/// It allows USING and 'select *' for queries with subselects. It doesn't need AsterisksSemantic and related stuff.
|
||||
/// 1. Expand asterisks in select expression list.
|
||||
/// 2. Normalize column names and find name clashes
|
||||
/// 3. Rewrite multiple JOINs with subqueries:
|
||||
/// SELECT ... FROM (SELECT `--.s`.*, ... FROM (...) AS `--.s` JOIN tableY ON ...) AS `--.s` JOIN tableZ ON ...'
|
||||
/// 4. Push down expressions of aliases used in ON section into expression list of first reletad subquery
|
||||
void JoinToSubqueryTransformMatcher::visitV2(ASTSelectQuery & select, ASTPtr & ast, Data & data)
|
||||
{
|
||||
std::vector<const ASTTableExpression *> table_expressions;
|
||||
if (!needRewrite<2>(select, table_expressions))
|
||||
return;
|
||||
|
||||
auto & src_tables = select.tables()->children;
|
||||
size_t tables_count = src_tables.size();
|
||||
|
||||
if (table_expressions.size() != data.tables.size() ||
|
||||
tables_count != data.tables.size())
|
||||
throw Exception("Inconsistent tables count in JOIN rewriter", ErrorCodes::LOGICAL_ERROR);
|
||||
|
||||
/// Replace * and t.* with columns in select expression list.
|
||||
{
|
||||
ExtractAsterisksVisitor::Data asterisks_data(data.tables);
|
||||
ExtractAsterisksVisitor(asterisks_data).visit(select.select());
|
||||
if (asterisks_data.new_select_expression_list)
|
||||
select.setExpression(ASTSelectQuery::Expression::SELECT, std::move(asterisks_data.new_select_expression_list));
|
||||
}
|
||||
|
||||
/// Collect column identifiers
|
||||
|
||||
std::vector<ASTIdentifier *> identifiers;
|
||||
CollectColumnIdentifiersVisitor(identifiers).visit(ast);
|
||||
|
||||
std::vector<ASTIdentifier *> using_identifiers;
|
||||
std::vector<std::vector<ASTPtr>> alias_pushdown(tables_count);
|
||||
std::unordered_map<String, ASTPtr> on_aliases;
|
||||
|
||||
/// Collect columns from JOIN sections. Detect if we have aliases there (they need pushdown).
|
||||
for (size_t table_pos = 0; table_pos < tables_count; ++table_pos)
|
||||
{
|
||||
auto * table = src_tables[table_pos]->as<ASTTablesInSelectQueryElement>();
|
||||
if (table->table_join)
|
||||
{
|
||||
auto & join = table->table_join->as<ASTTableJoin &>();
|
||||
if (join.on_expression)
|
||||
{
|
||||
std::vector<ASTIdentifier *> on_identifiers;
|
||||
CollectColumnIdentifiersVisitor(on_identifiers).visit(join.on_expression);
|
||||
identifiers.insert(identifiers.end(), on_identifiers.begin(), on_identifiers.end());
|
||||
|
||||
/// Extract aliases used in ON section for pushdown. Exclude the last table.
|
||||
if (table_pos < tables_count - 1)
|
||||
{
|
||||
for (auto * ident : on_identifiers)
|
||||
{
|
||||
auto it = data.aliases.find(ident->name);
|
||||
if (!on_aliases.count(ident->name) && it != data.aliases.end())
|
||||
{
|
||||
auto alias_expression = it->second;
|
||||
alias_pushdown[table_pos].push_back(alias_expression);
|
||||
on_aliases[ident->name] = alias_expression;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (join.using_expression_list)
|
||||
CollectColumnIdentifiersVisitor(using_identifiers).visit(join.on_expression);
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if alias expression is too complex to push it down.
|
||||
for (auto & expr : on_aliases)
|
||||
{
|
||||
CheckAliasDependencyVisitor::Data check{data.aliases};
|
||||
CheckAliasDependencyVisitor(check).visit(expr.second);
|
||||
if (check.dependency)
|
||||
throw Exception("Cannot rewrite JOINs. Alias '" + expr.first +
|
||||
"' used in ON section depends on another alias '" + check.dependency->name + "'",
|
||||
ErrorCodes::NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
/// Check same name in aliases, USING and ON sections. Cannot push down alias to ON through USING cause of name masquerading.
|
||||
for (auto * ident : using_identifiers)
|
||||
if (on_aliases.count(ident->name))
|
||||
throw Exception("Cannot rewrite JOINs. Alias '" + ident->name + "' appears both in ON and USING", ErrorCodes::NOT_IMPLEMENTED);
|
||||
using_identifiers.clear();
|
||||
|
||||
/// Replace pushdowned expressions with aliases names in original expression lists.
|
||||
RewriteWithAliasVisitor(on_aliases).visit(ast);
|
||||
on_aliases.clear();
|
||||
|
||||
/// We need to know if identifier is public. If so we have too keep its output name.
|
||||
std::unordered_set<ASTIdentifier *> public_identifiers;
|
||||
for (auto & top_level_child : select.select()->children)
|
||||
if (auto * ident = top_level_child->as<ASTIdentifier>())
|
||||
public_identifiers.insert(ident);
|
||||
|
||||
UniqueShortNames unique_names;
|
||||
std::vector<TableNeededColumns> needed_columns =
|
||||
normalizeColumnNamesExtractNeeded(data.tables, data.aliases, identifiers, public_identifiers, unique_names);
|
||||
|
||||
/// Rewrite JOINs with subselects
|
||||
|
||||
ASTPtr left_table = src_tables[0];
|
||||
|
||||
static ASTPtr subquery_template = makeSubqueryTemplate();
|
||||
|
||||
for (size_t i = 1; i < src_tables.size() - 1; ++i)
|
||||
{
|
||||
auto expression_list = subqueryExpressionList(i, needed_columns, alias_pushdown);
|
||||
|
||||
ASTPtr subquery = subquery_template->clone();
|
||||
SubqueryExpressionsRewriteVisitor::Data expr_rewrite_data{std::move(expression_list)};
|
||||
SubqueryExpressionsRewriteVisitor(expr_rewrite_data).visit(subquery);
|
||||
|
||||
left_table = replaceJoin(left_table, src_tables[i], subquery);
|
||||
}
|
||||
|
||||
RewriteVisitor::Data visitor_data{left_table, src_tables.back()};
|
||||
RewriteVisitor(visitor_data).visit(select.refTables());
|
||||
|
||||
data.done = true;
|
||||
}
|
||||
|
||||
void JoinToSubqueryTransformMatcher::visitV1(ASTSelectQuery & select, ASTPtr &, Data & data)
|
||||
{
|
||||
using RevertedAliases = AsteriskSemantic::RevertedAliases;
|
||||
|
||||
@ -379,9 +782,17 @@ void JoinToSubqueryTransformMatcher::visit(ASTSelectQuery & select, ASTPtr &, Da
|
||||
if (!needRewrite(select, table_expressions))
|
||||
return;
|
||||
|
||||
ExtractAsterisksVisitor::Data asterisks_data(data.context, table_expressions);
|
||||
if (!asterisks_data.table_columns.empty())
|
||||
if (table_expressions.size() != data.tables.size())
|
||||
throw Exception("Inconsistent tables count in JOIN rewriter", ErrorCodes::LOGICAL_ERROR);
|
||||
|
||||
bool has_subquery = false;
|
||||
for (const auto & expr : table_expressions)
|
||||
if (expr->subquery)
|
||||
has_subquery = true;
|
||||
|
||||
if (!has_subquery)
|
||||
{
|
||||
ExtractAsterisksVisitor::Data asterisks_data(data.tables);
|
||||
ExtractAsterisksVisitor(asterisks_data).visit(select.select());
|
||||
if (asterisks_data.new_select_expression_list)
|
||||
select.setExpression(ASTSelectQuery::Expression::SELECT, std::move(asterisks_data.new_select_expression_list));
|
||||
@ -390,6 +801,7 @@ void JoinToSubqueryTransformMatcher::visit(ASTSelectQuery & select, ASTPtr &, Da
|
||||
ColumnAliasesVisitor::Data aliases_data(getDatabaseAndTables(select, ""));
|
||||
if (select.select())
|
||||
{
|
||||
/// TODO: there's a bug here. We need to publish only top-level ASTIdentifiers but visitor extracts all.
|
||||
aliases_data.public_names = true;
|
||||
ColumnAliasesVisitor(aliases_data).visit(select.select());
|
||||
aliases_data.public_names = false;
|
||||
@ -425,20 +837,14 @@ void JoinToSubqueryTransformMatcher::visit(ASTSelectQuery & select, ASTPtr &, Da
|
||||
auto & src_tables = select.tables()->children;
|
||||
ASTPtr left_table = src_tables[0];
|
||||
|
||||
static ASTPtr subquery_template = makeSubqueryTemplate();
|
||||
|
||||
for (size_t i = 1; i < src_tables.size() - 1; ++i)
|
||||
{
|
||||
left_table = replaceJoin(left_table, src_tables[i]);
|
||||
left_table = replaceJoin(left_table, src_tables[i], subquery_template->clone());
|
||||
if (!left_table)
|
||||
throw Exception("Cannot replace tables with subselect", ErrorCodes::LOGICAL_ERROR);
|
||||
|
||||
/// attach an alias to subquery.
|
||||
/// TODO: remove setting check after testing period
|
||||
if (data.context.getSettingsRef().joined_subquery_requires_alias)
|
||||
{
|
||||
SetSubqueryAliasVisitor::Data alias_data{String("--.join") + std::to_string(i)};
|
||||
SetSubqueryAliasVisitor(alias_data).visit(left_table);
|
||||
}
|
||||
|
||||
/// attach data to generated asterisk
|
||||
AppendSemanticVisitor::Data semantic_data{rev_aliases, false};
|
||||
AppendSemanticVisitor(semantic_data).visit(left_table);
|
||||
@ -451,16 +857,7 @@ void JoinToSubqueryTransformMatcher::visit(ASTSelectQuery & select, ASTPtr &, Da
|
||||
data.done = true;
|
||||
}
|
||||
|
||||
static ASTPtr makeSubqueryTemplate()
|
||||
{
|
||||
ParserTablesInSelectQueryElement parser(true);
|
||||
ASTPtr subquery_template = parseQuery(parser, "(select * from _t)", 0);
|
||||
if (!subquery_template)
|
||||
throw Exception("Cannot parse subquery template", ErrorCodes::LOGICAL_ERROR);
|
||||
return subquery_template;
|
||||
}
|
||||
|
||||
ASTPtr JoinToSubqueryTransformMatcher::replaceJoin(ASTPtr ast_left, ASTPtr ast_right)
|
||||
ASTPtr JoinToSubqueryTransformMatcher::replaceJoin(ASTPtr ast_left, ASTPtr ast_right, ASTPtr subquery_template)
|
||||
{
|
||||
const auto * left = ast_left->as<ASTTablesInSelectQueryElement>();
|
||||
const auto * right = ast_right->as<ASTTablesInSelectQueryElement>();
|
||||
@ -470,13 +867,10 @@ ASTPtr JoinToSubqueryTransformMatcher::replaceJoin(ASTPtr ast_left, ASTPtr ast_r
|
||||
if (!right->table_join)
|
||||
throw Exception("Table join expected", ErrorCodes::LOGICAL_ERROR);
|
||||
|
||||
static ASTPtr subquery_template = makeSubqueryTemplate();
|
||||
|
||||
/// replace '_t' with pair of joined tables
|
||||
ASTPtr res = subquery_template->clone();
|
||||
RewriteVisitor::Data visitor_data{ast_left, ast_right};
|
||||
RewriteVisitor(visitor_data).visit(res);
|
||||
return res;
|
||||
RewriteVisitor(visitor_data).visit(subquery_template);
|
||||
return subquery_template;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <Interpreters/InDepthNodeVisitor.h>
|
||||
#include <Interpreters/DatabaseAndTableWithAlias.h>
|
||||
#include <Interpreters/Aliases.h>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
@ -16,7 +18,9 @@ class JoinToSubqueryTransformMatcher
|
||||
public:
|
||||
struct Data
|
||||
{
|
||||
const Context & context;
|
||||
const std::vector<TableWithColumnNamesAndTypes> & tables;
|
||||
const Aliases & aliases;
|
||||
size_t version = 1;
|
||||
bool done = false;
|
||||
};
|
||||
|
||||
@ -39,10 +43,13 @@ private:
|
||||
/// TablesInSelectQueryElement [source1]
|
||||
/// TablesInSelectQueryElement [source2]
|
||||
///
|
||||
static void visit(ASTSelectQuery & select, ASTPtr & ast, Data & data);
|
||||
static void visitV1(ASTSelectQuery & select, ASTPtr & ast, Data & data);
|
||||
|
||||
/// V2 uses information about tables' columns to rewrite queries.
|
||||
static void visitV2(ASTSelectQuery & select, ASTPtr & ast, Data & data);
|
||||
|
||||
/// @return combined TablesInSelectQueryElement or nullptr if cannot rewrite
|
||||
static ASTPtr replaceJoin(ASTPtr left, ASTPtr right);
|
||||
static ASTPtr replaceJoin(ASTPtr left, ASTPtr right, ASTPtr subquery_template);
|
||||
};
|
||||
|
||||
using JoinToSubqueryTransformVisitor = InDepthNodeVisitor<JoinToSubqueryTransformMatcher, true>;
|
||||
|
@ -30,13 +30,21 @@ static String wrongAliasMessage(const ASTPtr & ast, const ASTPtr & prev_ast, con
|
||||
}
|
||||
|
||||
|
||||
bool QueryAliasesMatcher::needChildVisit(const ASTPtr & node, const ASTPtr &)
|
||||
bool QueryAliasesWithSubqueries::needChildVisit(const ASTPtr & node, const ASTPtr &)
|
||||
{
|
||||
/// Don't descent into table functions and subqueries and special case for ArrayJoin.
|
||||
return !(node->as<ASTTableExpression>() || node->as<ASTSelectWithUnionQuery>() || node->as<ASTArrayJoin>());
|
||||
}
|
||||
|
||||
void QueryAliasesMatcher::visit(const ASTPtr & ast, Data & data)
|
||||
bool QueryAliasesNoSubqueries::needChildVisit(const ASTPtr & node, const ASTPtr & child)
|
||||
{
|
||||
if (node->as<ASTSubquery>())
|
||||
return false;
|
||||
return QueryAliasesWithSubqueries::needChildVisit(node, child);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void QueryAliasesMatcher<T>::visit(const ASTPtr & ast, Data & data)
|
||||
{
|
||||
if (auto * s = ast->as<ASTSubquery>())
|
||||
visit(*s, ast, data);
|
||||
@ -48,7 +56,8 @@ void QueryAliasesMatcher::visit(const ASTPtr & ast, Data & data)
|
||||
visitOther(ast, data);
|
||||
}
|
||||
|
||||
void QueryAliasesMatcher::visit(const ASTSelectQuery & select, const ASTPtr &, Data &)
|
||||
template <typename T>
|
||||
void QueryAliasesMatcher<T>::visit(const ASTSelectQuery & select, const ASTPtr &, Data &)
|
||||
{
|
||||
ASTPtr with = select.with();
|
||||
if (!with)
|
||||
@ -61,7 +70,8 @@ void QueryAliasesMatcher::visit(const ASTSelectQuery & select, const ASTPtr &, D
|
||||
|
||||
/// The top-level aliases in the ARRAY JOIN section have a special meaning, we will not add them
|
||||
/// (skip the expression list itself and its children).
|
||||
void QueryAliasesMatcher::visit(const ASTArrayJoin &, const ASTPtr & ast, Data & data)
|
||||
template <typename T>
|
||||
void QueryAliasesMatcher<T>::visit(const ASTArrayJoin &, const ASTPtr & ast, Data & data)
|
||||
{
|
||||
visitOther(ast, data);
|
||||
|
||||
@ -79,10 +89,11 @@ void QueryAliasesMatcher::visit(const ASTArrayJoin &, const ASTPtr & ast, Data &
|
||||
/// set unique aliases for all subqueries. this is needed, because:
|
||||
/// 1) content of subqueries could change after recursive analysis, and auto-generated column names could become incorrect
|
||||
/// 2) result of different scalar subqueries can be cached inside expressions compilation cache and must have different names
|
||||
void QueryAliasesMatcher::visit(const ASTSubquery & const_subquery, const ASTPtr & ast, Data & data)
|
||||
template <typename T>
|
||||
void QueryAliasesMatcher<T>::visit(const ASTSubquery & const_subquery, const ASTPtr & ast, Data & data)
|
||||
{
|
||||
auto & aliases = data;
|
||||
ASTSubquery & subquery = const_cast<ASTSubquery &>(const_subquery);
|
||||
Aliases & aliases = data.aliases;
|
||||
|
||||
static std::atomic_uint64_t subquery_index = 0;
|
||||
|
||||
@ -99,15 +110,15 @@ void QueryAliasesMatcher::visit(const ASTSubquery & const_subquery, const ASTPtr
|
||||
aliases[alias] = ast;
|
||||
}
|
||||
else
|
||||
visitOther(ast, data);
|
||||
visitOther(ast, aliases);
|
||||
|
||||
subquery.prefer_alias_to_column_name = true;
|
||||
}
|
||||
|
||||
void QueryAliasesMatcher::visitOther(const ASTPtr & ast, Data & data)
|
||||
template <typename T>
|
||||
void QueryAliasesMatcher<T>::visitOther(const ASTPtr & ast, Data & data)
|
||||
{
|
||||
Aliases & aliases = data.aliases;
|
||||
|
||||
auto & aliases = data;
|
||||
String alias = ast->tryGetAlias();
|
||||
if (!alias.empty())
|
||||
{
|
||||
@ -118,4 +129,8 @@ void QueryAliasesMatcher::visitOther(const ASTPtr & ast, Data & data)
|
||||
}
|
||||
}
|
||||
|
||||
/// Explicit template instantiations
|
||||
template class QueryAliasesMatcher<QueryAliasesWithSubqueries>;
|
||||
template class QueryAliasesMatcher<QueryAliasesNoSubqueries>;
|
||||
|
||||
}
|
||||
|
@ -11,19 +11,27 @@ class ASTSubquery;
|
||||
struct ASTTableExpression;
|
||||
struct ASTArrayJoin;
|
||||
|
||||
struct QueryAliasesWithSubqueries
|
||||
{
|
||||
static bool needChildVisit(const ASTPtr & node, const ASTPtr & child);
|
||||
};
|
||||
|
||||
struct QueryAliasesNoSubqueries
|
||||
{
|
||||
static bool needChildVisit(const ASTPtr & node, const ASTPtr & child);
|
||||
};
|
||||
|
||||
/// Visits AST node to collect aliases.
|
||||
template <typename Helper>
|
||||
class QueryAliasesMatcher
|
||||
{
|
||||
public:
|
||||
using Visitor = ConstInDepthNodeVisitor<QueryAliasesMatcher, false>;
|
||||
|
||||
struct Data
|
||||
{
|
||||
Aliases & aliases;
|
||||
};
|
||||
using Data = Aliases;
|
||||
|
||||
static void visit(const ASTPtr & ast, Data & data);
|
||||
static bool needChildVisit(const ASTPtr & node, const ASTPtr & child);
|
||||
static bool needChildVisit(const ASTPtr & node, const ASTPtr & child) { return Helper::needChildVisit(node, child); }
|
||||
|
||||
private:
|
||||
static void visit(const ASTSelectQuery & select, const ASTPtr & ast, Data & data);
|
||||
@ -33,6 +41,7 @@ private:
|
||||
};
|
||||
|
||||
/// Visits AST nodes and collect their aliases in one map (with links to source nodes).
|
||||
using QueryAliasesVisitor = QueryAliasesMatcher::Visitor;
|
||||
using QueryAliasesVisitor = QueryAliasesMatcher<QueryAliasesWithSubqueries>::Visitor;
|
||||
using QueryAliasesNoSubqueriesVisitor = QueryAliasesMatcher<QueryAliasesNoSubqueries>::Visitor;
|
||||
|
||||
}
|
||||
|
@ -894,8 +894,7 @@ void SyntaxAnalyzer::normalize(ASTPtr & query, Aliases & aliases, const Settings
|
||||
CustomizeFunctionsVisitor(data).visit(query);
|
||||
|
||||
/// Creates a dictionary `aliases`: alias -> ASTPtr
|
||||
QueryAliasesVisitor::Data query_aliases_data{aliases};
|
||||
QueryAliasesVisitor(query_aliases_data).visit(query);
|
||||
QueryAliasesVisitor(aliases).visit(query);
|
||||
|
||||
/// Mark table ASTIdentifiers with not a column marker
|
||||
MarkTableIdentifiersVisitor::Data identifiers_data{aliases};
|
||||
|
@ -43,3 +43,15 @@ y.b: 0
|
||||
│ 1 │ 1 │ 1 │ 1 │ 1 │ 1 │
|
||||
│ 2 │ 2 │ 0 │ 0 │ 0 │ 0 │
|
||||
└─────┴─────┴─────┴─────┴─────┴─────┘
|
||||
┌─s.a─┬─s.a─┬─s_b─┬─s.b─┐
|
||||
│ 1 │ 1 │ 1 │ 1 │
|
||||
│ 0 │ 0 │ 0 │ 0 │
|
||||
└─────┴─────┴─────┴─────┘
|
||||
┌─y.a─┬─y.a─┬─y_b─┬─y.b─┐
|
||||
│ 1 │ 1 │ 1 │ 1 │
|
||||
│ 0 │ 0 │ 0 │ 0 │
|
||||
└─────┴─────┴─────┴─────┘
|
||||
┌─t.a─┬─t_a─┬─s.a─┬─s_a─┬─y.a─┬─y_a─┐
|
||||
│ 1 │ 1 │ 1 │ 1 │ 1 │ 1 │
|
||||
│ 2 │ 2 │ 0 │ 0 │ 0 │ 0 │
|
||||
└─────┴─────┴─────┴─────┴─────┴─────┘
|
||||
|
@ -39,6 +39,28 @@ left join y on y.b = s.b
|
||||
order by t.a
|
||||
format PrettyCompactNoEscapes;
|
||||
|
||||
set multiple_joins_rewriter_version = 1;
|
||||
|
||||
select s.a, s.a, s.b as s_b, s.b from t
|
||||
left join s on s.a = t.a
|
||||
left join y on s.b = y.b
|
||||
order by t.a
|
||||
format PrettyCompactNoEscapes;
|
||||
|
||||
select y.a, y.a, y.b as y_b, y.b from t
|
||||
left join s on s.a = t.a
|
||||
left join y on y.b = s.b
|
||||
order by t.a
|
||||
format PrettyCompactNoEscapes;
|
||||
|
||||
select t.a, t.a as t_a, s.a, s.a as s_a, y.a, y.a as y_a from t
|
||||
left join s on t.a = s.a
|
||||
left join y on y.b = s.b
|
||||
order by t.a
|
||||
format PrettyCompactNoEscapes;
|
||||
|
||||
set multiple_joins_rewriter_version = 2;
|
||||
|
||||
select s.a, s.a, s.b as s_b, s.b from t
|
||||
left join s on s.a = t.a
|
||||
left join y on s.b = y.b
|
||||
|
@ -1,17 +1,17 @@
|
||||
SELECT a\nFROM t1_00849\nCROSS JOIN t2_00849
|
||||
SELECT a\nFROM t1_00849\nALL INNER JOIN t2_00849 ON a = t2_00849.a\nWHERE a = t2_00849.a
|
||||
SELECT a\nFROM t1_00849\nALL INNER JOIN t2_00849 ON b = t2_00849.b\nWHERE b = t2_00849.b
|
||||
SELECT `--t1_00849.a` AS `t1_00849.a`\nFROM \n(\n SELECT \n a AS `--t1_00849.a`, \n b, \n t2_00849.a AS `--t2_00849.a`, \n t2_00849.b\n FROM t1_00849\n ALL INNER JOIN t2_00849 ON `--t1_00849.a` = `--t2_00849.a`\n)\nALL INNER JOIN t3_00849 ON `--t1_00849.a` = a\nWHERE (`--t1_00849.a` = `--t2_00849.a`) AND (`--t1_00849.a` = a)
|
||||
SELECT `--t1_00849.a` AS `t1_00849.a`\nFROM \n(\n SELECT \n a AS `--t1_00849.a`, \n b AS `--t1_00849.b`, \n t2_00849.a, \n t2_00849.b AS `--t2_00849.b`\n FROM t1_00849\n ALL INNER JOIN t2_00849 ON `--t1_00849.b` = `--t2_00849.b`\n)\nALL INNER JOIN t3_00849 ON `--t1_00849.b` = b\nWHERE (`--t1_00849.b` = `--t2_00849.b`) AND (`--t1_00849.b` = b)
|
||||
SELECT `--t1_00849.a` AS `t1_00849.a`\nFROM \n(\n SELECT \n `--t1_00849.a`, \n b, \n `--t2_00849.a`, \n `t2_00849.b`, \n a AS `--t3_00849.a`, \n t3_00849.b\n FROM \n (\n SELECT \n a AS `--t1_00849.a`, \n b, \n t2_00849.a AS `--t2_00849.a`, \n t2_00849.b\n FROM t1_00849\n ALL INNER JOIN t2_00849 ON `--t1_00849.a` = `--t2_00849.a`\n )\n ALL INNER JOIN t3_00849 ON `--t1_00849.a` = `--t3_00849.a`\n)\nALL INNER JOIN t4_00849 ON `--t1_00849.a` = a\nWHERE (`--t1_00849.a` = `--t2_00849.a`) AND (`--t1_00849.a` = `--t3_00849.a`) AND (`--t1_00849.a` = a)
|
||||
SELECT `--t1_00849.a` AS `t1_00849.a`\nFROM \n(\n SELECT \n `--t1_00849.a`, \n `--t1_00849.b`, \n `t2_00849.a`, \n `--t2_00849.b`, \n a, \n b AS `--t3_00849.b`\n FROM \n (\n SELECT \n a AS `--t1_00849.a`, \n b AS `--t1_00849.b`, \n t2_00849.a, \n t2_00849.b AS `--t2_00849.b`\n FROM t1_00849\n ALL INNER JOIN t2_00849 ON `--t1_00849.b` = `--t2_00849.b`\n )\n ALL INNER JOIN t3_00849 ON `--t1_00849.b` = `--t3_00849.b`\n)\nALL INNER JOIN t4_00849 ON `--t1_00849.b` = b\nWHERE (`--t1_00849.b` = `--t2_00849.b`) AND (`--t1_00849.b` = `--t3_00849.b`) AND (`--t1_00849.b` = b)
|
||||
SELECT `--t1_00849.a` AS `t1_00849.a`\nFROM \n(\n SELECT \n `--t1_00849.a`, \n b, \n `--t2_00849.a`, \n `t2_00849.b`, \n a AS `--t3_00849.a`, \n t3_00849.b\n FROM \n (\n SELECT \n a AS `--t1_00849.a`, \n b, \n t2_00849.a AS `--t2_00849.a`, \n t2_00849.b\n FROM t1_00849\n ALL INNER JOIN t2_00849 ON `--t2_00849.a` = `--t1_00849.a`\n )\n ALL INNER JOIN t3_00849 ON `--t2_00849.a` = `--t3_00849.a`\n)\nALL INNER JOIN t4_00849 ON `--t2_00849.a` = a\nWHERE (`--t2_00849.a` = `--t1_00849.a`) AND (`--t2_00849.a` = `--t3_00849.a`) AND (`--t2_00849.a` = a)
|
||||
SELECT `--t1_00849.a` AS `t1_00849.a`\nFROM \n(\n SELECT \n `--t1_00849.a`, \n b, \n `--t2_00849.a`, \n `t2_00849.b`, \n a AS `--t3_00849.a`, \n t3_00849.b\n FROM \n (\n SELECT \n a AS `--t1_00849.a`, \n b, \n t2_00849.a AS `--t2_00849.a`, \n t2_00849.b\n FROM t1_00849\n CROSS JOIN t2_00849\n )\n ALL INNER JOIN t3_00849 ON (`--t3_00849.a` = `--t1_00849.a`) AND (`--t3_00849.a` = `--t2_00849.a`)\n)\nALL INNER JOIN t4_00849 ON `--t3_00849.a` = a\nWHERE (`--t3_00849.a` = `--t1_00849.a`) AND (`--t3_00849.a` = `--t2_00849.a`) AND (`--t3_00849.a` = a)
|
||||
SELECT `--t1_00849.a` AS `t1_00849.a`\nFROM \n(\n SELECT \n `--t1_00849.a`, \n b, \n `--t2_00849.a`, \n `t2_00849.b`, \n a AS `--t3_00849.a`, \n t3_00849.b\n FROM \n (\n SELECT \n a AS `--t1_00849.a`, \n b, \n t2_00849.a AS `--t2_00849.a`, \n t2_00849.b\n FROM t1_00849\n CROSS JOIN t2_00849\n )\n CROSS JOIN t3_00849\n)\nALL INNER JOIN t4_00849 ON (a = `--t1_00849.a`) AND (a = `--t2_00849.a`) AND (a = `--t3_00849.a`)\nWHERE (a = `--t1_00849.a`) AND (a = `--t2_00849.a`) AND (a = `--t3_00849.a`)
|
||||
SELECT `--t1_00849.a` AS `t1_00849.a`\nFROM \n(\n SELECT \n `--t1_00849.a`, \n b, \n `--t2_00849.a`, \n `t2_00849.b`, \n a AS `--t3_00849.a`, \n t3_00849.b\n FROM \n (\n SELECT \n a AS `--t1_00849.a`, \n b, \n t2_00849.a AS `--t2_00849.a`, \n t2_00849.b\n FROM t1_00849\n ALL INNER JOIN t2_00849 ON `--t1_00849.a` = `--t2_00849.a`\n )\n ALL INNER JOIN t3_00849 ON `--t2_00849.a` = `--t3_00849.a`\n)\nALL INNER JOIN t4_00849 ON `--t3_00849.a` = a\nWHERE (`--t1_00849.a` = `--t2_00849.a`) AND (`--t2_00849.a` = `--t3_00849.a`) AND (`--t3_00849.a` = a)
|
||||
SELECT `--t1_00849.a` AS `t1_00849.a`\nFROM \n(\n SELECT \n `--t1_00849.a`, \n b, \n `t2_00849.a`, \n `t2_00849.b`, \n a, \n t3_00849.b\n FROM \n (\n SELECT \n a AS `--t1_00849.a`, \n b, \n t2_00849.a, \n t2_00849.b\n FROM t1_00849\n CROSS JOIN t2_00849\n )\n CROSS JOIN t3_00849\n)\nCROSS JOIN t4_00849
|
||||
SELECT `--t1_00849.a` AS `t1_00849.a`\nFROM \n(\n SELECT \n `--t1_00849.a`, \n b, \n `t2_00849.a`, \n `t2_00849.b`, \n a, \n t3_00849.b\n FROM \n (\n SELECT \n a AS `--t1_00849.a`, \n b, \n t2_00849.a, \n t2_00849.b\n FROM t1_00849\n CROSS JOIN t2_00849\n )\n CROSS JOIN t3_00849\n)\nCROSS JOIN t4_00849
|
||||
SELECT `--t1_00849.a` AS `t1_00849.a`\nFROM \n(\n SELECT \n a AS `--t1_00849.a`, \n b, \n t2_00849.a AS `--t2_00849.a`, \n t2_00849.b\n FROM t1_00849\n ALL INNER JOIN t2_00849 ON `--t1_00849.a` = `--t2_00849.a`\n)\nCROSS JOIN t3_00849
|
||||
SELECT `--t1_00849.a` AS `t1_00849.a`\nFROM \n(\n SELECT \n a AS `--t1_00849.a`, \n b, \n t2_00849.a AS `--t2_00849.a`, \n t2_00849.b\n FROM t1_00849\n ALL INNER JOIN t2_00849 ON `--t1_00849.a` = `--t2_00849.a`\n) AS `--.s`\nALL INNER JOIN t3_00849 ON `--t1_00849.a` = a\nWHERE (`--t1_00849.a` = `--t2_00849.a`) AND (`--t1_00849.a` = a)
|
||||
SELECT `--t1_00849.a` AS `t1_00849.a`\nFROM \n(\n SELECT \n a AS `--t1_00849.a`, \n b AS `--t1_00849.b`, \n t2_00849.a, \n t2_00849.b AS `--t2_00849.b`\n FROM t1_00849\n ALL INNER JOIN t2_00849 ON `--t1_00849.b` = `--t2_00849.b`\n) AS `--.s`\nALL INNER JOIN t3_00849 ON `--t1_00849.b` = b\nWHERE (`--t1_00849.b` = `--t2_00849.b`) AND (`--t1_00849.b` = b)
|
||||
SELECT `--t1_00849.a` AS `t1_00849.a`\nFROM \n(\n SELECT \n `--t1_00849.a`, \n b, \n `--t2_00849.a`, \n `t2_00849.b`, \n a AS `--t3_00849.a`, \n t3_00849.b\n FROM \n (\n SELECT \n a AS `--t1_00849.a`, \n b, \n t2_00849.a AS `--t2_00849.a`, \n t2_00849.b\n FROM t1_00849\n ALL INNER JOIN t2_00849 ON `--t1_00849.a` = `--t2_00849.a`\n ) AS `--.s`\n ALL INNER JOIN t3_00849 ON `--t1_00849.a` = `--t3_00849.a`\n) AS `--.s`\nALL INNER JOIN t4_00849 ON `--t1_00849.a` = a\nWHERE (`--t1_00849.a` = `--t2_00849.a`) AND (`--t1_00849.a` = `--t3_00849.a`) AND (`--t1_00849.a` = a)
|
||||
SELECT `--t1_00849.a` AS `t1_00849.a`\nFROM \n(\n SELECT \n `--t1_00849.a`, \n `--t1_00849.b`, \n `t2_00849.a`, \n `--t2_00849.b`, \n a, \n b AS `--t3_00849.b`\n FROM \n (\n SELECT \n a AS `--t1_00849.a`, \n b AS `--t1_00849.b`, \n t2_00849.a, \n t2_00849.b AS `--t2_00849.b`\n FROM t1_00849\n ALL INNER JOIN t2_00849 ON `--t1_00849.b` = `--t2_00849.b`\n ) AS `--.s`\n ALL INNER JOIN t3_00849 ON `--t1_00849.b` = `--t3_00849.b`\n) AS `--.s`\nALL INNER JOIN t4_00849 ON `--t1_00849.b` = b\nWHERE (`--t1_00849.b` = `--t2_00849.b`) AND (`--t1_00849.b` = `--t3_00849.b`) AND (`--t1_00849.b` = b)
|
||||
SELECT `--t1_00849.a` AS `t1_00849.a`\nFROM \n(\n SELECT \n `--t1_00849.a`, \n b, \n `--t2_00849.a`, \n `t2_00849.b`, \n a AS `--t3_00849.a`, \n t3_00849.b\n FROM \n (\n SELECT \n a AS `--t1_00849.a`, \n b, \n t2_00849.a AS `--t2_00849.a`, \n t2_00849.b\n FROM t1_00849\n ALL INNER JOIN t2_00849 ON `--t2_00849.a` = `--t1_00849.a`\n ) AS `--.s`\n ALL INNER JOIN t3_00849 ON `--t2_00849.a` = `--t3_00849.a`\n) AS `--.s`\nALL INNER JOIN t4_00849 ON `--t2_00849.a` = a\nWHERE (`--t2_00849.a` = `--t1_00849.a`) AND (`--t2_00849.a` = `--t3_00849.a`) AND (`--t2_00849.a` = a)
|
||||
SELECT `--t1_00849.a` AS `t1_00849.a`\nFROM \n(\n SELECT \n `--t1_00849.a`, \n b, \n `--t2_00849.a`, \n `t2_00849.b`, \n a AS `--t3_00849.a`, \n t3_00849.b\n FROM \n (\n SELECT \n a AS `--t1_00849.a`, \n b, \n t2_00849.a AS `--t2_00849.a`, \n t2_00849.b\n FROM t1_00849\n CROSS JOIN t2_00849\n ) AS `--.s`\n ALL INNER JOIN t3_00849 ON (`--t3_00849.a` = `--t1_00849.a`) AND (`--t3_00849.a` = `--t2_00849.a`)\n) AS `--.s`\nALL INNER JOIN t4_00849 ON `--t3_00849.a` = a\nWHERE (`--t3_00849.a` = `--t1_00849.a`) AND (`--t3_00849.a` = `--t2_00849.a`) AND (`--t3_00849.a` = a)
|
||||
SELECT `--t1_00849.a` AS `t1_00849.a`\nFROM \n(\n SELECT \n `--t1_00849.a`, \n b, \n `--t2_00849.a`, \n `t2_00849.b`, \n a AS `--t3_00849.a`, \n t3_00849.b\n FROM \n (\n SELECT \n a AS `--t1_00849.a`, \n b, \n t2_00849.a AS `--t2_00849.a`, \n t2_00849.b\n FROM t1_00849\n CROSS JOIN t2_00849\n ) AS `--.s`\n CROSS JOIN t3_00849\n) AS `--.s`\nALL INNER JOIN t4_00849 ON (a = `--t1_00849.a`) AND (a = `--t2_00849.a`) AND (a = `--t3_00849.a`)\nWHERE (a = `--t1_00849.a`) AND (a = `--t2_00849.a`) AND (a = `--t3_00849.a`)
|
||||
SELECT `--t1_00849.a` AS `t1_00849.a`\nFROM \n(\n SELECT \n `--t1_00849.a`, \n b, \n `--t2_00849.a`, \n `t2_00849.b`, \n a AS `--t3_00849.a`, \n t3_00849.b\n FROM \n (\n SELECT \n a AS `--t1_00849.a`, \n b, \n t2_00849.a AS `--t2_00849.a`, \n t2_00849.b\n FROM t1_00849\n ALL INNER JOIN t2_00849 ON `--t1_00849.a` = `--t2_00849.a`\n ) AS `--.s`\n ALL INNER JOIN t3_00849 ON `--t2_00849.a` = `--t3_00849.a`\n) AS `--.s`\nALL INNER JOIN t4_00849 ON `--t3_00849.a` = a\nWHERE (`--t1_00849.a` = `--t2_00849.a`) AND (`--t2_00849.a` = `--t3_00849.a`) AND (`--t3_00849.a` = a)
|
||||
SELECT `--t1_00849.a` AS `t1_00849.a`\nFROM \n(\n SELECT \n `--t1_00849.a`, \n b, \n `t2_00849.a`, \n `t2_00849.b`, \n a, \n t3_00849.b\n FROM \n (\n SELECT \n a AS `--t1_00849.a`, \n b, \n t2_00849.a, \n t2_00849.b\n FROM t1_00849\n CROSS JOIN t2_00849\n ) AS `--.s`\n CROSS JOIN t3_00849\n) AS `--.s`\nCROSS JOIN t4_00849
|
||||
SELECT `--t1_00849.a` AS `t1_00849.a`\nFROM \n(\n SELECT \n `--t1_00849.a`, \n b, \n `t2_00849.a`, \n `t2_00849.b`, \n a, \n t3_00849.b\n FROM \n (\n SELECT \n a AS `--t1_00849.a`, \n b, \n t2_00849.a, \n t2_00849.b\n FROM t1_00849\n CROSS JOIN t2_00849\n ) AS `--.s`\n CROSS JOIN t3_00849\n) AS `--.s`\nCROSS JOIN t4_00849
|
||||
SELECT `--t1_00849.a` AS `t1_00849.a`\nFROM \n(\n SELECT \n a AS `--t1_00849.a`, \n b, \n t2_00849.a AS `--t2_00849.a`, \n t2_00849.b\n FROM t1_00849\n ALL INNER JOIN t2_00849 ON `--t1_00849.a` = `--t2_00849.a`\n) AS `--.s`\nCROSS JOIN t3_00849
|
||||
SELECT * FROM t1, t2
|
||||
1 1 1 1
|
||||
1 1 1 \N
|
||||
|
@ -1,6 +1,6 @@
|
||||
SET enable_debug_queries = 1;
|
||||
SET enable_optimize_predicate_expression = 0;
|
||||
SET joined_subquery_requires_alias = 0;
|
||||
SET multiple_joins_rewriter_version = 1;
|
||||
|
||||
DROP TABLE IF EXISTS t1_00849;
|
||||
DROP TABLE IF EXISTS t2_00849;
|
||||
|
@ -0,0 +1,55 @@
|
||||
SELECT a\nFROM t1\nCROSS JOIN t2
|
||||
SELECT a\nFROM t1\nALL INNER JOIN t2 ON a = t2.a\nWHERE a = t2.a
|
||||
SELECT a\nFROM t1\nALL INNER JOIN t2 ON b = t2.b\nWHERE b = t2.b
|
||||
SELECT `--t1.a` AS `t1.a`\nFROM \n(\n SELECT \n a AS `--t1.a`, \n t2.a AS `--t2.a`\n FROM t1\n ALL INNER JOIN t2 ON `--t1.a` = `--t2.a`\n) AS `--.s`\nALL INNER JOIN t3 ON `--t1.a` = a\nWHERE (`--t1.a` = `--t2.a`) AND (`--t1.a` = a)
|
||||
SELECT `--t1.a` AS `t1.a`\nFROM \n(\n SELECT \n b AS `--t1.b`, \n a AS `--t1.a`, \n t2.b AS `--t2.b`\n FROM t1\n ALL INNER JOIN t2 ON `--t1.b` = `--t2.b`\n) AS `--.s`\nALL INNER JOIN t3 ON `--t1.b` = b\nWHERE (`--t1.b` = `--t2.b`) AND (`--t1.b` = b)
|
||||
SELECT `--t1.a` AS `t1.a`\nFROM \n(\n SELECT \n `--t1.a`, \n `--t2.a`, \n a AS `--t3.a`\n FROM \n (\n SELECT \n a AS `--t1.a`, \n t2.a AS `--t2.a`\n FROM t1\n ALL INNER JOIN t2 ON `--t1.a` = `--t2.a`\n ) AS `--.s`\n ALL INNER JOIN t3 ON `--t1.a` = `--t3.a`\n) AS `--.s`\nALL INNER JOIN t4 ON `--t1.a` = a\nWHERE (`--t1.a` = `--t2.a`) AND (`--t1.a` = `--t3.a`) AND (`--t1.a` = a)
|
||||
SELECT `--t1.a` AS `t1.a`\nFROM \n(\n SELECT \n `--t1.b`, \n `--t1.a`, \n `--t2.b`, \n b AS `--t3.b`\n FROM \n (\n SELECT \n b AS `--t1.b`, \n a AS `--t1.a`, \n t2.b AS `--t2.b`\n FROM t1\n ALL INNER JOIN t2 ON `--t1.b` = `--t2.b`\n ) AS `--.s`\n ALL INNER JOIN t3 ON `--t1.b` = `--t3.b`\n) AS `--.s`\nALL INNER JOIN t4 ON `--t1.b` = b\nWHERE (`--t1.b` = `--t2.b`) AND (`--t1.b` = `--t3.b`) AND (`--t1.b` = b)
|
||||
SELECT `--t1.a` AS `t1.a`\nFROM \n(\n SELECT \n `--t1.a`, \n `--t2.a`, \n a AS `--t3.a`\n FROM \n (\n SELECT \n a AS `--t1.a`, \n t2.a AS `--t2.a`\n FROM t1\n ALL INNER JOIN t2 ON `--t2.a` = `--t1.a`\n ) AS `--.s`\n ALL INNER JOIN t3 ON `--t2.a` = `--t3.a`\n) AS `--.s`\nALL INNER JOIN t4 ON `--t2.a` = a\nWHERE (`--t2.a` = `--t1.a`) AND (`--t2.a` = `--t3.a`) AND (`--t2.a` = a)
|
||||
SELECT `--t1.a` AS `t1.a`\nFROM \n(\n SELECT \n `--t1.a`, \n `--t2.a`, \n a AS `--t3.a`\n FROM \n (\n SELECT \n a AS `--t1.a`, \n t2.a AS `--t2.a`\n FROM t1\n CROSS JOIN t2\n ) AS `--.s`\n ALL INNER JOIN t3 ON (`--t3.a` = `--t1.a`) AND (`--t3.a` = `--t2.a`)\n) AS `--.s`\nALL INNER JOIN t4 ON `--t3.a` = a\nWHERE (`--t3.a` = `--t1.a`) AND (`--t3.a` = `--t2.a`) AND (`--t3.a` = a)
|
||||
SELECT `--t1.a` AS `t1.a`\nFROM \n(\n SELECT \n `--t1.a`, \n `--t2.a`, \n a AS `--t3.a`\n FROM \n (\n SELECT \n a AS `--t1.a`, \n t2.a AS `--t2.a`\n FROM t1\n CROSS JOIN t2\n ) AS `--.s`\n CROSS JOIN t3\n) AS `--.s`\nALL INNER JOIN t4 ON (a = `--t1.a`) AND (a = `--t2.a`) AND (a = `--t3.a`)\nWHERE (a = `--t1.a`) AND (a = `--t2.a`) AND (a = `--t3.a`)
|
||||
SELECT `--t1.a` AS `t1.a`\nFROM \n(\n SELECT \n `--t1.a`, \n `--t2.a`, \n a AS `--t3.a`\n FROM \n (\n SELECT \n a AS `--t1.a`, \n t2.a AS `--t2.a`\n FROM t1\n ALL INNER JOIN t2 ON `--t1.a` = `--t2.a`\n ) AS `--.s`\n ALL INNER JOIN t3 ON `--t2.a` = `--t3.a`\n) AS `--.s`\nALL INNER JOIN t4 ON `--t3.a` = a\nWHERE (`--t1.a` = `--t2.a`) AND (`--t2.a` = `--t3.a`) AND (`--t3.a` = a)
|
||||
SELECT `--t1.a` AS `t1.a`\nFROM \n(\n SELECT `--t1.a`\n FROM \n (\n SELECT a AS `--t1.a`\n FROM t1\n CROSS JOIN t2\n ) AS `--.s`\n CROSS JOIN t3\n) AS `--.s`\nCROSS JOIN t4
|
||||
SELECT `--t1.a` AS `t1.a`\nFROM \n(\n SELECT `--t1.a`\n FROM \n (\n SELECT a AS `--t1.a`\n FROM t1\n CROSS JOIN t2\n ) AS `--.s`\n CROSS JOIN t3\n) AS `--.s`\nCROSS JOIN t4
|
||||
SELECT `--t1.a` AS `t1.a`\nFROM \n(\n SELECT \n a AS `--t1.a`, \n t2.a AS `--t2.a`\n FROM t1\n ALL INNER JOIN t2 ON `--t1.a` = `--t2.a`\n) AS `--.s`\nCROSS JOIN t3
|
||||
SELECT * FROM t1, t2
|
||||
1 1 1 1
|
||||
1 1 1 \N
|
||||
2 2 1 1
|
||||
2 2 1 \N
|
||||
3 3 1 1
|
||||
3 3 1 \N
|
||||
4 4 1 1
|
||||
4 4 1 \N
|
||||
SELECT * FROM t1, t2 WHERE t1.a = t2.a
|
||||
1 1 1 1
|
||||
1 1 1 \N
|
||||
SELECT t1.a, t2.a FROM t1, t2 WHERE t1.b = t2.b
|
||||
1 1
|
||||
SELECT t1.a, t2.b, t3.b FROM t1, t2, t3 WHERE t1.a = t2.a AND t1.a = t3.a
|
||||
1 1 1
|
||||
1 1 \N
|
||||
1 \N 1
|
||||
1 \N \N
|
||||
SELECT t1.a, t2.b, t3.b FROM t1, t2, t3 WHERE t1.b = t2.b AND t1.b = t3.b
|
||||
1 1 1
|
||||
SELECT t1.a, t2.b, t3.b, t4.b FROM t1, t2, t3, t4 WHERE t1.a = t2.a AND t1.a = t3.a AND t1.a = t4.a
|
||||
1 1 1 1
|
||||
1 1 1 \N
|
||||
1 1 \N 1
|
||||
1 1 \N \N
|
||||
1 \N 1 1
|
||||
1 \N 1 \N
|
||||
1 \N \N 1
|
||||
1 \N \N \N
|
||||
SELECT t1.a, t2.b, t3.b, t4.b FROM t1, t2, t3, t4 WHERE t1.b = t2.b AND t1.b = t3.b AND t1.b = t4.b
|
||||
1 1 1 1
|
||||
SELECT t1.a, t2.b, t3.b, t4.b FROM t1, t2, t3, t4 WHERE t1.a = t2.a AND t2.a = t3.a AND t3.a = t4.a
|
||||
1 1 1 1
|
||||
1 1 1 \N
|
||||
1 1 \N 1
|
||||
1 1 \N \N
|
||||
1 \N 1 1
|
||||
1 \N 1 \N
|
||||
1 \N \N 1
|
||||
1 \N \N \N
|
@ -0,0 +1,69 @@
|
||||
SET enable_debug_queries = 1;
|
||||
SET enable_optimize_predicate_expression = 0;
|
||||
SET multiple_joins_rewriter_version = 2;
|
||||
|
||||
DROP TABLE IF EXISTS t1;
|
||||
DROP TABLE IF EXISTS t2;
|
||||
DROP TABLE IF EXISTS t3;
|
||||
DROP TABLE IF EXISTS t4;
|
||||
|
||||
CREATE TABLE t1 (a UInt32, b Nullable(Int32)) ENGINE = Memory;
|
||||
CREATE TABLE t2 (a UInt32, b Nullable(Int32)) ENGINE = Memory;
|
||||
CREATE TABLE t3 (a UInt32, b Nullable(Int32)) ENGINE = Memory;
|
||||
CREATE TABLE t4 (a UInt32, b Nullable(Int32)) ENGINE = Memory;
|
||||
|
||||
ANALYZE SELECT t1.a FROM t1, t2;
|
||||
ANALYZE SELECT t1.a FROM t1, t2 WHERE t1.a = t2.a;
|
||||
ANALYZE SELECT t1.a FROM t1, t2 WHERE t1.b = t2.b;
|
||||
ANALYZE SELECT t1.a FROM t1, t2, t3 WHERE t1.a = t2.a AND t1.a = t3.a;
|
||||
ANALYZE SELECT t1.a FROM t1, t2, t3 WHERE t1.b = t2.b AND t1.b = t3.b;
|
||||
ANALYZE SELECT t1.a FROM t1, t2, t3, t4 WHERE t1.a = t2.a AND t1.a = t3.a AND t1.a = t4.a;
|
||||
ANALYZE SELECT t1.a FROM t1, t2, t3, t4 WHERE t1.b = t2.b AND t1.b = t3.b AND t1.b = t4.b;
|
||||
|
||||
ANALYZE SELECT t1.a FROM t1, t2, t3, t4 WHERE t2.a = t1.a AND t2.a = t3.a AND t2.a = t4.a;
|
||||
ANALYZE SELECT t1.a FROM t1, t2, t3, t4 WHERE t3.a = t1.a AND t3.a = t2.a AND t3.a = t4.a;
|
||||
ANALYZE SELECT t1.a FROM t1, t2, t3, t4 WHERE t4.a = t1.a AND t4.a = t2.a AND t4.a = t3.a;
|
||||
ANALYZE SELECT t1.a FROM t1, t2, t3, t4 WHERE t1.a = t2.a AND t2.a = t3.a AND t3.a = t4.a;
|
||||
|
||||
ANALYZE SELECT t1.a FROM t1, t2, t3, t4;
|
||||
ANALYZE SELECT t1.a FROM t1 CROSS JOIN t2 CROSS JOIN t3 CROSS JOIN t4;
|
||||
|
||||
ANALYZE SELECT t1.a FROM t1, t2 CROSS JOIN t3; -- { serverError 48 }
|
||||
ANALYZE SELECT t1.a FROM t1 JOIN t2 USING a CROSS JOIN t3; -- { serverError 48 }
|
||||
ANALYZE SELECT t1.a FROM t1 JOIN t2 ON t1.a = t2.a CROSS JOIN t3;
|
||||
|
||||
INSERT INTO t1 values (1,1), (2,2), (3,3), (4,4);
|
||||
INSERT INTO t2 values (1,1), (1, Null);
|
||||
INSERT INTO t3 values (1,1), (1, Null);
|
||||
INSERT INTO t4 values (1,1), (1, Null);
|
||||
|
||||
SELECT 'SELECT * FROM t1, t2';
|
||||
SELECT * FROM t1, t2
|
||||
ORDER BY t1.a, t2.b;
|
||||
SELECT 'SELECT * FROM t1, t2 WHERE t1.a = t2.a';
|
||||
SELECT * FROM t1, t2 WHERE t1.a = t2.a
|
||||
ORDER BY t1.a, t2.b;
|
||||
SELECT 'SELECT t1.a, t2.a FROM t1, t2 WHERE t1.b = t2.b';
|
||||
SELECT t1.a, t2.b FROM t1, t2 WHERE t1.b = t2.b;
|
||||
SELECT 'SELECT t1.a, t2.b, t3.b FROM t1, t2, t3 WHERE t1.a = t2.a AND t1.a = t3.a';
|
||||
SELECT t1.a, t2.b, t3.b FROM t1, t2, t3
|
||||
WHERE t1.a = t2.a AND t1.a = t3.a
|
||||
ORDER BY t2.b, t3.b;
|
||||
SELECT 'SELECT t1.a, t2.b, t3.b FROM t1, t2, t3 WHERE t1.b = t2.b AND t1.b = t3.b';
|
||||
SELECT t1.a, t2.b, t3.b FROM t1, t2, t3 WHERE t1.b = t2.b AND t1.b = t3.b;
|
||||
SELECT 'SELECT t1.a, t2.b, t3.b, t4.b FROM t1, t2, t3, t4 WHERE t1.a = t2.a AND t1.a = t3.a AND t1.a = t4.a';
|
||||
SELECT t1.a, t2.b, t3.b, t4.b FROM t1, t2, t3, t4
|
||||
WHERE t1.a = t2.a AND t1.a = t3.a AND t1.a = t4.a
|
||||
ORDER BY t2.b, t3.b, t4.b;
|
||||
SELECT 'SELECT t1.a, t2.b, t3.b, t4.b FROM t1, t2, t3, t4 WHERE t1.b = t2.b AND t1.b = t3.b AND t1.b = t4.b';
|
||||
SELECT t1.a, t2.b, t3.b, t4.b FROM t1, t2, t3, t4
|
||||
WHERE t1.b = t2.b AND t1.b = t3.b AND t1.b = t4.b;
|
||||
SELECT 'SELECT t1.a, t2.b, t3.b, t4.b FROM t1, t2, t3, t4 WHERE t1.a = t2.a AND t2.a = t3.a AND t3.a = t4.a';
|
||||
SELECT t1.a, t2.b, t3.b, t4.b FROM t1, t2, t3, t4
|
||||
WHERE t1.a = t2.a AND t2.a = t3.a AND t3.a = t4.a
|
||||
ORDER BY t2.b, t3.b, t4.b;
|
||||
|
||||
DROP TABLE t1;
|
||||
DROP TABLE t2;
|
||||
DROP TABLE t3;
|
||||
DROP TABLE t4;
|
@ -3,3 +3,4 @@
|
||||
0
|
||||
0 0
|
||||
0 0 0
|
||||
0 0 0
|
||||
|
@ -4,6 +4,14 @@ select t1.* from system.one t1 join system.one t2 on t1.dummy = t2.dummy join sy
|
||||
select t2.*, t3.* from system.one t1 join system.one t2 on t1.dummy = t2.dummy join system.one t3 ON t1.dummy = t3.dummy;
|
||||
select t1.dummy, t2.*, t3.dummy from system.one t1 join system.one t2 on t1.dummy = t2.dummy join system.one t3 ON t1.dummy = t3.dummy;
|
||||
|
||||
set multiple_joins_rewriter_version = 1;
|
||||
|
||||
select t1.dummy, t2.*, t3.dummy from (select * from system.one) t1
|
||||
join system.one t2 on t1.dummy = t2.dummy
|
||||
join system.one t3 ON t1.dummy = t3.dummy; -- { serverError 48 }
|
||||
|
||||
set multiple_joins_rewriter_version = 2;
|
||||
|
||||
select t1.dummy, t2.*, t3.dummy from (select * from system.one) t1
|
||||
join system.one t2 on t1.dummy = t2.dummy
|
||||
join system.one t3 ON t1.dummy = t3.dummy;
|
||||
|
Loading…
Reference in New Issue
Block a user