2013-05-24 10:49:19 +00:00
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
|
|
#include <DB/Parsers/IAST.h>
|
|
|
|
|
#include <DB/Parsers/ASTFunction.h>
|
|
|
|
|
#include <DB/Parsers/ASTExpressionList.h>
|
|
|
|
|
#include <DB/Parsers/ASTSelectQuery.h>
|
|
|
|
|
|
|
|
|
|
#include <DB/Interpreters/Context.h>
|
|
|
|
|
#include <DB/Interpreters/Aggregator.h>
|
|
|
|
|
#include <DB/Interpreters/ExpressionActions.h>
|
2014-03-04 11:26:55 +00:00
|
|
|
|
#include "Set.h"
|
2013-05-24 10:49:19 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace DB
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
/** Превращает выражение из синтаксического дерева в последовательность действий для его выполнения.
|
2014-03-19 11:44:41 +00:00
|
|
|
|
*
|
|
|
|
|
* NOTE: если ast - запрос SELECT из таблицы, структура этой таблицы не должна меняться во все время жизни ExpressionAnalyzer-а.
|
2013-05-24 10:49:19 +00:00
|
|
|
|
*/
|
|
|
|
|
class ExpressionAnalyzer : private boost::noncopyable
|
|
|
|
|
{
|
|
|
|
|
public:
|
2013-05-29 11:46:51 +00:00
|
|
|
|
ExpressionAnalyzer(const ASTPtr & ast_, const Context & context_, size_t subquery_depth_ = 0)
|
|
|
|
|
: ast(ast_), context(context_), settings(context.getSettings()),
|
2014-01-10 13:24:50 +00:00
|
|
|
|
subquery_depth(subquery_depth_), columns(context.getColumns()), storage(getTable())
|
2013-05-24 10:49:19 +00:00
|
|
|
|
{
|
|
|
|
|
init();
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-26 10:52:22 +00:00
|
|
|
|
ExpressionAnalyzer(const ASTPtr & ast_, const Context & context_, StoragePtr storage_, size_t subquery_depth_ = 0)
|
|
|
|
|
: ast(ast_), context(context_), settings(context.getSettings()),
|
|
|
|
|
subquery_depth(subquery_depth_), columns(context.getColumns()), storage(storage_ ? storage_ : getTable())
|
|
|
|
|
{
|
|
|
|
|
init();
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-24 10:49:19 +00:00
|
|
|
|
/// columns - список известных столбцов (которых можно достать из таблицы).
|
2013-05-29 11:46:51 +00:00
|
|
|
|
ExpressionAnalyzer(const ASTPtr & ast_, const Context & context_, const NamesAndTypesList & columns_, size_t subquery_depth_ = 0)
|
|
|
|
|
: ast(ast_), context(context_), settings(context.getSettings()),
|
2014-01-10 13:24:50 +00:00
|
|
|
|
subquery_depth(subquery_depth_), columns(columns_), storage(getTable())
|
2013-05-24 10:49:19 +00:00
|
|
|
|
{
|
|
|
|
|
init();
|
|
|
|
|
}
|
2013-05-28 11:54:37 +00:00
|
|
|
|
|
|
|
|
|
/// Есть ли в выражении агрегатные функции или секция GROUP BY или HAVING.
|
|
|
|
|
bool hasAggregation() { return has_aggregation; }
|
|
|
|
|
|
|
|
|
|
/// Получить список ключей агрегирования и описаний агрегатных функций, если в запросе есть GROUP BY.
|
|
|
|
|
void getAggregateInfo(Names & key_names, AggregateDescriptions & aggregates);
|
2013-05-29 11:46:51 +00:00
|
|
|
|
|
2013-06-20 13:50:55 +00:00
|
|
|
|
/// Получить набор столбцов, которые достаточно прочесть для вычисления выражения.
|
2013-05-30 16:52:21 +00:00
|
|
|
|
Names getRequiredColumns();
|
2014-03-04 11:26:55 +00:00
|
|
|
|
|
2013-05-28 14:24:20 +00:00
|
|
|
|
/** Эти методы позволяют собрать цепочку преобразований над блоком, получающую значения в нужных секциях запроса.
|
|
|
|
|
*
|
|
|
|
|
* Пример использования:
|
|
|
|
|
* ExpressionActionsChain chain;
|
|
|
|
|
* analyzer.appendWhere(chain);
|
|
|
|
|
* chain.addStep();
|
|
|
|
|
* analyzer.appendSelect(chain);
|
|
|
|
|
* analyzer.appendOrderBy(chain);
|
|
|
|
|
* chain.finalize();
|
2014-03-28 12:13:58 +00:00
|
|
|
|
*
|
|
|
|
|
* Если указано only_types=true, не выполняет подзапросы в соответствующих частях запроса. Полученные таким
|
|
|
|
|
* образом действия не следует выполнять, они нужны только чтобы получить список столбцов с их типами.
|
2013-05-28 14:24:20 +00:00
|
|
|
|
*/
|
2013-05-28 11:54:37 +00:00
|
|
|
|
|
2014-03-14 14:52:48 +00:00
|
|
|
|
void processGlobalOperations();
|
|
|
|
|
|
2013-05-28 11:54:37 +00:00
|
|
|
|
/// До агрегации:
|
2014-03-28 12:13:58 +00:00
|
|
|
|
bool appendArrayJoin(ExpressionActionsChain & chain, bool only_types);
|
|
|
|
|
bool appendWhere(ExpressionActionsChain & chain, bool only_types);
|
|
|
|
|
bool appendGroupBy(ExpressionActionsChain & chain, bool only_types);
|
|
|
|
|
void appendAggregateFunctionsArguments(ExpressionActionsChain & chain, bool only_types);
|
2013-05-28 11:54:37 +00:00
|
|
|
|
|
|
|
|
|
/// После агрегации:
|
2014-03-28 12:13:58 +00:00
|
|
|
|
bool appendHaving(ExpressionActionsChain & chain, bool only_types);
|
|
|
|
|
void appendSelect(ExpressionActionsChain & chain, bool only_types);
|
|
|
|
|
bool appendOrderBy(ExpressionActionsChain & chain, bool only_types);
|
2013-05-28 14:24:20 +00:00
|
|
|
|
/// Удаляет все столбцы кроме выбираемых SELECT, упорядочивает оставшиеся столбцы и переименовывает их в алиасы.
|
2014-03-28 12:13:58 +00:00
|
|
|
|
void appendProjectResult(ExpressionActionsChain & chain, bool only_types);
|
2013-05-28 11:54:37 +00:00
|
|
|
|
|
|
|
|
|
/// Если ast не запрос SELECT, просто получает все действия для вычисления выражения.
|
2013-06-03 13:17:17 +00:00
|
|
|
|
/// Если project_result, в выходном блоке останутся только вычисленные значения в нужном порядке, переименованные в алиасы.
|
|
|
|
|
/// Иначе, из блока будут удаляться только временные столбцы.
|
|
|
|
|
ExpressionActionsPtr getActions(bool project_result);
|
2013-05-24 10:49:19 +00:00
|
|
|
|
|
|
|
|
|
/// Действия, которые можно сделать над пустым блоком: добавление констант и применение функций, зависящих только от констант.
|
|
|
|
|
/// Не выполняет подзапросы.
|
|
|
|
|
ExpressionActionsPtr getConstActions();
|
2013-05-28 11:54:37 +00:00
|
|
|
|
|
2014-03-04 11:26:55 +00:00
|
|
|
|
/** Множества, для создания которых нужно будет выполнить подзапрос.
|
|
|
|
|
* Только множества, нужные для выполнения действий, возвращенных из уже вызванных append* или getActions.
|
|
|
|
|
* То есть, нужно вызвать getSubquerySets после всех вызовов append* или getActions и создать все возвращенные множества перед выполнением действий.
|
|
|
|
|
*/
|
|
|
|
|
Sets getSetsWithSubqueries();
|
|
|
|
|
|
2013-05-30 16:52:21 +00:00
|
|
|
|
/// Если ast - запрос SELECT, получает имена (алиасы) и типы столбцов из секции SELECT.
|
|
|
|
|
Block getSelectSampleBlock();
|
2013-05-24 10:49:19 +00:00
|
|
|
|
|
2014-03-27 17:58:25 +00:00
|
|
|
|
/// Все новые временные таблицы, полученные при выполнении подзапросов GLOBAL IN.
|
2014-04-01 14:06:58 +00:00
|
|
|
|
Tables external_tables;
|
2014-03-31 14:49:43 +00:00
|
|
|
|
|
2014-04-09 10:32:52 +00:00
|
|
|
|
/// Создаем какие сможем Set из секции In для использования индекса по ним
|
2014-04-08 12:54:32 +00:00
|
|
|
|
/// ordered_set нужен если в In используется индекс
|
2014-04-09 10:32:52 +00:00
|
|
|
|
void makeExplicitSetsForIndex(bool create_ordered_set);
|
2013-05-24 10:49:19 +00:00
|
|
|
|
private:
|
2013-05-30 16:52:21 +00:00
|
|
|
|
typedef std::set<String> NamesSet;
|
|
|
|
|
|
2013-05-24 10:49:19 +00:00
|
|
|
|
ASTPtr ast;
|
|
|
|
|
ASTSelectQuery * select_query;
|
|
|
|
|
const Context & context;
|
|
|
|
|
Settings settings;
|
2013-05-29 11:46:51 +00:00
|
|
|
|
size_t subquery_depth;
|
2013-05-24 10:49:19 +00:00
|
|
|
|
|
2013-06-21 10:31:31 +00:00
|
|
|
|
/// Столбцы, которые упоминаются в выражении, но не были заданы в конструкторе.
|
|
|
|
|
NameSet unknown_required_columns;
|
|
|
|
|
|
2014-01-10 13:24:50 +00:00
|
|
|
|
/// Исходные столбцы.
|
2013-05-24 10:49:19 +00:00
|
|
|
|
NamesAndTypesList columns;
|
2014-03-28 12:13:58 +00:00
|
|
|
|
/// Столбцы после ARRAY JOIN и/или агрегации.
|
2013-05-29 11:46:51 +00:00
|
|
|
|
NamesAndTypesList aggregated_columns;
|
2013-10-17 13:32:32 +00:00
|
|
|
|
|
2013-05-24 10:49:19 +00:00
|
|
|
|
/// Таблица, из которой делается запрос. Используется для sign-rewrite'а
|
|
|
|
|
const StoragePtr storage;
|
|
|
|
|
/// Имя поля Sign в таблице. Непусто, если нужно осуществлять sign-rewrite
|
|
|
|
|
String sign_column_name;
|
|
|
|
|
|
|
|
|
|
bool has_aggregation;
|
|
|
|
|
NamesAndTypesList aggregation_keys;
|
|
|
|
|
AggregateDescriptions aggregate_descriptions;
|
2014-03-04 11:26:55 +00:00
|
|
|
|
|
|
|
|
|
std::map<std::string, SetPtr> sets_with_subqueries;
|
2013-05-24 10:49:19 +00:00
|
|
|
|
typedef std::map<String, ASTPtr> Aliases;
|
|
|
|
|
Aliases aliases;
|
|
|
|
|
|
2013-06-15 07:10:06 +00:00
|
|
|
|
typedef std::set<const IAST *> SetOfASTs;
|
2013-05-24 10:49:19 +00:00
|
|
|
|
typedef std::map<ASTPtr, ASTPtr> MapOfASTs;
|
2013-06-11 16:21:25 +00:00
|
|
|
|
|
2013-10-21 11:33:25 +00:00
|
|
|
|
/// Какой столбец нужно по-ARRAY-JOIN-ить, чтобы получить указанный.
|
|
|
|
|
/// Например, для SELECT s.v ... ARRAY JOIN a AS s сюда попадет "s.v"->"a.v".
|
|
|
|
|
NameToNameMap array_join_result_to_source;
|
|
|
|
|
|
|
|
|
|
/// Для секции ARRAY JOIN отображение из алиаса в полное столбца
|
|
|
|
|
/// Например, для ARRAY JOIN [1,2] AS b сюда попадет "b"->"array(1,2)".
|
|
|
|
|
NameToNameMap array_join_alias_to_name;
|
2013-08-01 13:29:32 +00:00
|
|
|
|
|
2013-06-11 16:21:25 +00:00
|
|
|
|
/** Для getActionsImpl.
|
|
|
|
|
* Стек из ExpressionActions, соответствующих вложенным лямбда-выражениям.
|
|
|
|
|
* Новое действие нужно добавлять на самый высокий возможный уровень.
|
|
|
|
|
* Например, в выражении "select arrayMap(x -> x + column1 * column2, array1)"
|
|
|
|
|
* вычисление произведения нужно делать вне лямбда-выражения (оно не зависит от x), а вычисление суммы - внутри (зависит от x).
|
|
|
|
|
*/
|
|
|
|
|
struct ScopeStack
|
|
|
|
|
{
|
|
|
|
|
struct Level
|
|
|
|
|
{
|
|
|
|
|
ExpressionActionsPtr actions;
|
|
|
|
|
NameSet new_columns;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
typedef std::vector<Level> Levels;
|
|
|
|
|
|
|
|
|
|
Levels stack;
|
|
|
|
|
Settings settings;
|
|
|
|
|
|
|
|
|
|
ScopeStack(const ExpressionActions & actions, const Settings & settings_)
|
|
|
|
|
: settings(settings_)
|
|
|
|
|
{
|
|
|
|
|
stack.push_back(Level());
|
|
|
|
|
stack.back().actions = new ExpressionActions(actions);
|
2013-06-14 18:55:34 +00:00
|
|
|
|
const NamesAndTypesList & input_columns = actions.getSampleBlock().getColumnsList();
|
2013-06-11 16:21:25 +00:00
|
|
|
|
for (NamesAndTypesList::const_iterator it = input_columns.begin(); it != input_columns.end(); ++it)
|
|
|
|
|
stack.back().new_columns.insert(it->first);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void pushLevel(const NamesAndTypesList & input_columns)
|
|
|
|
|
{
|
|
|
|
|
stack.push_back(Level());
|
|
|
|
|
Level & prev = stack[stack.size() - 2];
|
2013-06-14 18:20:20 +00:00
|
|
|
|
|
|
|
|
|
ColumnsWithNameAndType prev_columns = prev.actions->getSampleBlock().getColumns();
|
|
|
|
|
|
|
|
|
|
ColumnsWithNameAndType all_columns;
|
|
|
|
|
NameSet new_names;
|
|
|
|
|
|
2013-06-11 16:21:25 +00:00
|
|
|
|
for (NamesAndTypesList::const_iterator it = input_columns.begin(); it != input_columns.end(); ++it)
|
|
|
|
|
{
|
2014-04-08 07:58:53 +00:00
|
|
|
|
all_columns.push_back(ColumnWithNameAndType(nullptr, it->second, it->first));
|
2013-06-14 18:20:20 +00:00
|
|
|
|
new_names.insert(it->first);
|
2013-06-11 16:21:25 +00:00
|
|
|
|
stack.back().new_columns.insert(it->first);
|
|
|
|
|
}
|
2013-06-14 18:20:20 +00:00
|
|
|
|
|
|
|
|
|
for (ColumnsWithNameAndType::const_iterator it = prev_columns.begin(); it != prev_columns.end(); ++it)
|
|
|
|
|
{
|
|
|
|
|
if (!new_names.count(it->name))
|
|
|
|
|
all_columns.push_back(*it);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
stack.back().actions = new ExpressionActions(all_columns, settings);
|
2013-06-11 16:21:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t getColumnLevel(const std::string & name)
|
|
|
|
|
{
|
|
|
|
|
for (int i = static_cast<int>(stack.size()) - 1; i >= 0; --i)
|
|
|
|
|
{
|
|
|
|
|
if (stack[i].new_columns.count(name))
|
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
throw Exception("Unknown identifier: " + name, ErrorCodes::UNKNOWN_IDENTIFIER);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void addAction(const ExpressionActions::Action & action, const Names & additional_required_columns = Names())
|
|
|
|
|
{
|
|
|
|
|
size_t level = 0;
|
|
|
|
|
for (size_t i = 0; i < additional_required_columns.size(); ++i)
|
|
|
|
|
level = std::max(level, getColumnLevel(additional_required_columns[i]));
|
|
|
|
|
Names required = action.getNeededColumns();
|
|
|
|
|
for (size_t i = 0; i < required.size(); ++i)
|
|
|
|
|
level = std::max(level, getColumnLevel(required[i]));
|
|
|
|
|
|
|
|
|
|
Names added;
|
|
|
|
|
stack[level].actions->add(action, added);
|
|
|
|
|
|
|
|
|
|
stack[level].new_columns.insert(added.begin(), added.end());
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < added.size(); ++i)
|
|
|
|
|
{
|
|
|
|
|
const ColumnWithNameAndType & col = stack[level].actions->getSampleBlock().getByName(added[i]);
|
|
|
|
|
for (size_t j = level + 1; j < stack.size(); ++j)
|
|
|
|
|
stack[j].actions->addInput(col);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ExpressionActionsPtr popLevel()
|
|
|
|
|
{
|
|
|
|
|
ExpressionActionsPtr res = stack.back().actions;
|
|
|
|
|
stack.pop_back();
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const Block & getSampleBlock()
|
|
|
|
|
{
|
|
|
|
|
return stack.back().actions->getSampleBlock();
|
|
|
|
|
}
|
|
|
|
|
};
|
2013-05-24 10:49:19 +00:00
|
|
|
|
|
|
|
|
|
void init();
|
|
|
|
|
|
2013-05-29 11:46:51 +00:00
|
|
|
|
NamesAndTypesList::iterator findColumn(const String & name, NamesAndTypesList & cols);
|
|
|
|
|
NamesAndTypesList::iterator findColumn(const String & name) { return findColumn(name, columns); }
|
2013-07-31 11:28:55 +00:00
|
|
|
|
|
2013-06-20 13:50:55 +00:00
|
|
|
|
void removeUnusedColumns();
|
|
|
|
|
|
2013-05-24 10:49:19 +00:00
|
|
|
|
/** Создать словарь алиасов.
|
|
|
|
|
*/
|
2013-10-17 13:32:32 +00:00
|
|
|
|
void createAliasesDict(ASTPtr & ast, int ignore_levels = 0);
|
2013-05-24 10:49:19 +00:00
|
|
|
|
|
|
|
|
|
/** Для узлов-звёздочек - раскрыть их в список всех столбцов.
|
|
|
|
|
* Для узлов-литералов - подставить алиасы.
|
|
|
|
|
* Для агрегатных функций - если нужно, сделать sign rewrite.
|
|
|
|
|
*/
|
|
|
|
|
void normalizeTree();
|
2013-06-18 09:43:35 +00:00
|
|
|
|
void normalizeTreeImpl(ASTPtr & ast, MapOfASTs & finished_asts, SetOfASTs & current_asts, std::string current_alias, bool in_sign_rewritten);
|
2014-03-14 14:52:48 +00:00
|
|
|
|
|
2014-03-27 17:58:25 +00:00
|
|
|
|
/// Обходит запрос и сохраняет найденные глобальные функции (например GLOBAL IN)
|
2014-03-14 15:42:30 +00:00
|
|
|
|
void findGlobalFunctions(ASTPtr & ast, std::vector<ASTPtr> & global_nodes);
|
2014-04-01 14:06:58 +00:00
|
|
|
|
void findExternalTables(ASTPtr & ast);
|
|
|
|
|
|
2013-05-29 11:46:51 +00:00
|
|
|
|
/// Превратить перечисление значений или подзапрос в ASTSet. node - функция in или notIn.
|
2013-06-11 16:21:25 +00:00
|
|
|
|
void makeSet(ASTFunction * node, const Block & sample_block);
|
2014-03-27 17:58:25 +00:00
|
|
|
|
/// Выполнить подзапрос в секции GLOBAL IN и запомнить результат во временную таблицу типа memory
|
2014-03-14 15:42:30 +00:00
|
|
|
|
/// Все новые временные таблицы хранятся в переменной external_tables
|
2014-03-14 14:52:48 +00:00
|
|
|
|
void addExternalStorage(ASTFunction * node, size_t & name_id);
|
|
|
|
|
|
2013-08-01 13:29:32 +00:00
|
|
|
|
void getArrayJoinedColumns();
|
|
|
|
|
void getArrayJoinedColumnsImpl(ASTPtr ast);
|
|
|
|
|
void addMultipleArrayJoinAction(ExpressionActions & actions);
|
2013-07-26 16:11:31 +00:00
|
|
|
|
|
|
|
|
|
void getActionsImpl(ASTPtr ast, bool no_subqueries, bool only_consts, ScopeStack & actions_stack);
|
2013-05-24 10:49:19 +00:00
|
|
|
|
|
2013-06-11 16:21:25 +00:00
|
|
|
|
void getRootActionsImpl(ASTPtr ast, bool no_subqueries, bool only_consts, ExpressionActions & actions);
|
2013-05-24 10:49:19 +00:00
|
|
|
|
|
2014-03-28 12:13:58 +00:00
|
|
|
|
void getActionsBeforeAggregationImpl(ASTPtr ast, ExpressionActions & actions, bool no_subqueries);
|
2013-05-29 11:46:51 +00:00
|
|
|
|
|
2013-05-24 10:49:19 +00:00
|
|
|
|
/// Добавить агрегатные функции в aggregate_descriptions.
|
2013-05-29 11:46:51 +00:00
|
|
|
|
/// Установить has_aggregation=true, если есть хоть одна агрегатная функция.
|
2013-05-24 10:49:19 +00:00
|
|
|
|
void getAggregatesImpl(ASTPtr ast, ExpressionActions & actions);
|
2013-10-17 13:32:32 +00:00
|
|
|
|
|
2013-05-30 16:59:16 +00:00
|
|
|
|
void getRequiredColumnsImpl(ASTPtr ast, NamesSet & required_columns, NamesSet & ignored_names);
|
2013-05-29 11:46:51 +00:00
|
|
|
|
|
2013-05-24 10:49:19 +00:00
|
|
|
|
/// Получить таблицу, из которой идет запрос
|
|
|
|
|
StoragePtr getTable();
|
|
|
|
|
|
|
|
|
|
/// Получить имя столбца Sign
|
|
|
|
|
String getSignColumnName();
|
|
|
|
|
|
|
|
|
|
/// Проверить нужно ли переписывать агрегатные функции для учета Sign
|
|
|
|
|
bool needSignRewrite();
|
|
|
|
|
|
|
|
|
|
/// Попробовать переписать агрегатную функцию для учета Sign
|
2013-06-03 12:02:59 +00:00
|
|
|
|
bool considerSignRewrite(ASTPtr & ast);
|
2013-05-24 10:49:19 +00:00
|
|
|
|
|
|
|
|
|
ASTPtr createSignColumn();
|
|
|
|
|
|
|
|
|
|
/// Заменить count() на sum(Sign)
|
|
|
|
|
ASTPtr rewriteCount(const ASTFunction * node);
|
|
|
|
|
/// Заменить sum(x) на sum(x * Sign)
|
|
|
|
|
ASTPtr rewriteSum(const ASTFunction * node);
|
|
|
|
|
/// Заменить avg(x) на sum(Sign * x) / sum(Sign)
|
|
|
|
|
ASTPtr rewriteAvg(const ASTFunction * node);
|
2013-05-28 11:54:37 +00:00
|
|
|
|
|
2013-05-28 14:24:20 +00:00
|
|
|
|
void initChain(ExpressionActionsChain & chain, NamesAndTypesList & columns);
|
|
|
|
|
|
2013-05-28 11:54:37 +00:00
|
|
|
|
void assertSelect();
|
|
|
|
|
void assertAggregation();
|
2013-05-29 11:46:51 +00:00
|
|
|
|
void assertArrayJoin();
|
2014-03-31 14:49:43 +00:00
|
|
|
|
|
2014-04-01 10:09:22 +00:00
|
|
|
|
void makeExplicitSet(ASTFunction * node, const Block & sample_block, bool create_ordered_set);
|
|
|
|
|
void makeExplicitSetsRecursively(ASTPtr & node, const Block & sample_block, bool create_ordered_set);
|
2013-05-24 10:49:19 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
}
|