dbms: JOINs: development [#METR-11370].

This commit is contained in:
Alexey Milovidov 2014-07-05 00:30:06 +04:00
parent 7ef01201ef
commit 18f670defa
3 changed files with 90 additions and 54 deletions

View File

@ -98,15 +98,12 @@ public:
*/
Sets getSetsWithSubqueries();
Joins getJoinsWithSubqueries();
const Tables & getExternalTables() { return external_tables; }
/// Если ast - запрос SELECT, получает имена (алиасы) и типы столбцов из секции SELECT.
Block getSelectSampleBlock();
/// Все новые временные таблицы, полученные при выполнении подзапросов GLOBAL IN.
Tables external_tables;
std::unordered_map<String, BlockInputStreamPtr> external_data;
size_t external_table_id = 1;
/// Создаем какие сможем Set из секции In для использования индекса по ним
void makeSetsForIndex();
@ -169,6 +166,12 @@ private:
/// Нужно ли подготавливать к выполнению глобальные подзапросы при анализировании запроса.
bool do_global;
/// Все новые временные таблицы, полученные при выполнении подзапросов GLOBAL IN/JOIN.
Tables external_tables;
std::unordered_map<String, BlockInputStreamPtr> external_data;
size_t external_table_id = 1;
void init();
static NamesAndTypesList::iterator findColumn(const String & name, NamesAndTypesList & cols);
@ -194,13 +197,19 @@ private:
void normalizeTree();
void normalizeTreeImpl(ASTPtr & ast, MapOfASTs & finished_asts, SetOfASTs & current_asts, std::string current_alias);
/// Находит в запросе использования внешних таблиц. Заполняет external_tables.
void findExternalTables(ASTPtr & ast);
/// Превратить перечисление значений или подзапрос в ASTSet. node - функция in или notIn.
void makeSet(ASTFunction * node, const Block & sample_block);
/// Запустить подзапрос в секции GLOBAL IN/JOIN, создать временную таблицу типа Memory и запомнить эту пару в переменной external_tables.
/// Находит глобальные подзапросы в секциях GLOBAL IN/JOIN. Заполняет external_tables, external_data.
void initGlobalSubqueriesAndExternalTables();
void initGlobalSubqueries(ASTPtr & ast);
/// Находит в запросе использования внешних таблиц (в виде идентификаторов таблиц). Заполняет external_tables.
void findExternalTables(ASTPtr & ast);
/** Инициализировать InterpreterSelectQuery для подзапроса в секции GLOBAL IN/JOIN,
* создать временную таблицу типа Memory и запомнить это в словаре external_tables, external_data.
*/
void addExternalStorage(ASTPtr & subquery_or_table_name);
void getArrayJoinedColumns();

View File

@ -44,12 +44,9 @@ void ExpressionAnalyzer::init()
/// Создаёт словарь aliases: alias -> ASTPtr
createAliasesDict(ast);
/// Common subexpression elimination. Rewrite rules. addExternalStorage.
/// Common subexpression elimination. Rewrite rules.
normalizeTree();
/// Создаёт словарь external_tables: name -> StoragePtr.
findExternalTables(ast);
/// array_join_alias_to_name, array_join_result_to_source.
getArrayJoinedColumns();
@ -58,6 +55,10 @@ void ExpressionAnalyzer::init()
/// has_aggregation, aggregation_keys, aggregate_descriptions, aggregated_columns.
analyzeAggregation();
/// external_tables, external_data.
/// Заменяет глобальные подзапросы на сгенерированные имена временных таблиц, которые будут отправлены на удалённые серверы.
initGlobalSubqueriesAndExternalTables();
}
@ -131,6 +132,56 @@ void ExpressionAnalyzer::analyzeAggregation()
}
void ExpressionAnalyzer::initGlobalSubqueriesAndExternalTables()
{
initGlobalSubqueries(ast);
/// Создаёт словарь external_tables: name -> StoragePtr.
findExternalTables(ast);
}
void ExpressionAnalyzer::initGlobalSubqueries(ASTPtr & ast)
{
/// Рекурсивные вызовы. Не опускаемся в подзапросы.
for (auto & child : ast->children)
if (!typeid_cast<ASTSelectQuery *>(&*child))
initGlobalSubqueries(child);
/// Действия, выполняемые снизу вверх.
if (ASTFunction * node = typeid_cast<ASTFunction *>(&*ast))
{
/// Для GLOBAL IN.
if (do_global && (node->name == "globalIn" || node->name == "globalNotIn"))
addExternalStorage(node->arguments->children.at(1));
}
else if (ASTJoin * node = typeid_cast<ASTJoin *>(&*ast))
{
/// Для GLOBAL JOIN.
if (do_global && node->locality == ASTJoin::Global)
addExternalStorage(node->table);
}
}
void ExpressionAnalyzer::findExternalTables(ASTPtr & ast)
{
/// Обход снизу. Намеренно опускаемся в подзапросы.
for (auto & child : ast->children)
findExternalTables(child);
/// Если идентификатор типа таблица
StoragePtr external_storage;
if (ASTIdentifier * node = typeid_cast<ASTIdentifier *>(&*ast))
if (node->kind == ASTIdentifier::Kind::Table)
if ((external_storage = context.tryGetExternalTable(node->name)))
external_tables[node->name] = external_storage;
}
NamesAndTypesList::iterator ExpressionAnalyzer::findColumn(const String & name, NamesAndTypesList & cols)
{
return std::find_if(cols.begin(), cols.end(),
@ -145,17 +196,17 @@ void ExpressionAnalyzer::createAliasesDict(ASTPtr & ast, int ignore_levels)
ASTSelectQuery * select = typeid_cast<ASTSelectQuery *>(&*ast);
/// Обход снизу-вверх. Не опускаемся в подзапросы.
for (ASTs::iterator it = ast->children.begin(); it != ast->children.end(); ++it)
for (auto & child : ast->children)
{
int new_ignore_levels = std::max(0, ignore_levels - 1);
/// Алиасы верхнего уровня в секции ARRAY JOIN имеют особый смысл, их добавлять не будем
/// (пропустим сам expression list и его детей).
if (select && *it == select->array_join_expression_list)
if (select && child == select->array_join_expression_list)
new_ignore_levels = 2;
if (!typeid_cast<ASTSelectQuery *>(&**it))
createAliasesDict(*it, new_ignore_levels);
if (!typeid_cast<ASTSelectQuery *>(&*child))
createAliasesDict(child, new_ignore_levels);
}
if (ignore_levels > 0)
@ -295,9 +346,9 @@ void ExpressionAnalyzer::normalizeTreeImpl(ASTPtr & ast, MapOfASTs & finished_as
/// Рекурсивные вызовы. Не опускаемся в подзапросы.
for (ASTs::iterator it = ast->children.begin(); it != ast->children.end(); ++it)
if (!typeid_cast<ASTSelectQuery *>(&**it))
normalizeTreeImpl(*it, finished_asts, current_asts, current_alias);
for (auto & child : ast->children)
if (!typeid_cast<ASTSelectQuery *>(&*child))
normalizeTreeImpl(child, finished_asts, current_asts, current_alias);
/// Если секция WHERE или HAVING состоит из одного алиаса, ссылку нужно заменить не только в children, но и в where_expression и having_expression.
if (ASTSelectQuery * select = typeid_cast<ASTSelectQuery *>(&*ast))
@ -333,21 +384,12 @@ void ExpressionAnalyzer::normalizeTreeImpl(ASTPtr & ast, MapOfASTs & finished_as
{
node->kind = ASTFunction::FUNCTION;
}
/// Для GLOBAL IN.
if (do_global && (node->name == "globalIn" || node->name == "globalNotIn"))
addExternalStorage(node->arguments->children.at(1));
}
if (ASTJoin * node = typeid_cast<ASTJoin *>(&*ast))
else if (ASTJoin * node = typeid_cast<ASTJoin *>(&*ast))
{
/// может быть указано JOIN t, где t - таблица, что равносильно JOIN (SELECT * FROM t).
if (ASTIdentifier * right = typeid_cast<ASTIdentifier *>(&*node->table))
right->kind = ASTIdentifier::Table;
/// Для GLOBAL JOIN.
if (do_global && node->locality == ASTJoin::Global)
addExternalStorage(node->table);
}
current_asts.erase(initial_ast);
@ -390,22 +432,6 @@ void ExpressionAnalyzer::makeSetsForIndexImpl(ASTPtr & node, const Block & sampl
}
void ExpressionAnalyzer::findExternalTables(ASTPtr & ast)
{
/// Обход снизу. Намеренно опускаемся в подзапросы.
for (ASTs::iterator it = ast->children.begin(); it != ast->children.end(); ++it)
findExternalTables(*it);
/// Если идентификатор типа таблица
StoragePtr external_storage;
if (ASTIdentifier * node = typeid_cast<ASTIdentifier *>(&*ast))
if (node->kind == ASTIdentifier::Kind::Table)
if ((external_storage = context.tryGetExternalTable(node->name)))
external_tables[node->name] = external_storage;
}
void ExpressionAnalyzer::addExternalStorage(ASTPtr & subquery_or_table_name)
{
/// Сгенерируем имя для внешней таблицы.
@ -500,7 +526,7 @@ void ExpressionAnalyzer::makeSet(ASTFunction * node, const Block & sample_block)
if (typeid_cast<ASTSet *>(&*arg))
return;
/// Если подзапрос или имя таблицы для селекта
/// Если подзапрос или имя таблицы для SELECT.
if (typeid_cast<ASTSubquery *>(&*arg) || typeid_cast<ASTIdentifier *>(&*arg))
{
/// Получаем поток блоков для подзапроса, отдаем его множеству, и кладём это множество на место подзапроса.
@ -854,9 +880,9 @@ void ExpressionAnalyzer::getArrayJoinedColumnsImpl(ASTPtr ast)
}
else
{
for (ASTs::iterator it = ast->children.begin(); it != ast->children.end(); ++it)
if (!typeid_cast<ASTSelectQuery *>(&**it))
getArrayJoinedColumnsImpl(*it);
for (auto & child : ast->children)
if (!typeid_cast<ASTSelectQuery *>(&*child))
getArrayJoinedColumnsImpl(child);
}
}
@ -917,6 +943,7 @@ void ExpressionAnalyzer::getActionsImpl(ASTPtr ast, bool no_subqueries, bool onl
{
/// Найдем тип первого аргумента (потом getActionsImpl вызовется для него снова и ни на что не повлияет).
getActionsImpl(node->arguments->children[0], no_subqueries, only_consts, actions_stack);
/// Превратим tuple или подзапрос в множество.
makeSet(node, actions_stack.getSampleBlock());
}
@ -1104,8 +1131,8 @@ void ExpressionAnalyzer::getActionsImpl(ASTPtr ast, bool no_subqueries, bool onl
}
else
{
for (ASTs::iterator it = ast->children.begin(); it != ast->children.end(); ++it)
getActionsImpl(*it, no_subqueries, only_consts, actions_stack);
for (auto & child : ast->children)
getActionsImpl(child, no_subqueries, only_consts, actions_stack);
}
}

View File

@ -79,7 +79,7 @@ void InterpreterSelectQuery::init(BlockInputStreamPtr input_, const NamesAndType
query_analyzer = new ExpressionAnalyzer(query_ptr, context, storage, subquery_depth, true);
/// Сохраняем в query context новые временные таблицы
for (auto & it : query_analyzer->external_tables)
for (auto & it : query_analyzer->getExternalTables())
if (!(context.tryGetExternalTable(it.first)))
context.addExternalTable(it.first, it.second);
@ -505,7 +505,7 @@ QueryProcessingStage::Enum InterpreterSelectQuery::executeFetchColumns(BlockInpu
if (!interpreter_subquery)
{
if (storage->isRemote())
storage->storeExternalTables(query_analyzer->external_tables);
storage->storeExternalTables(query_analyzer->getExternalTables());
streams = storage->read(required_columns, query_ptr, settings_for_storage, from_stage, settings.max_block_size, settings.max_threads);
for (auto stream : streams)
{