1
0
mirror of https://github.com/mariadb-corporation/mariadb-columnstore-engine.git synced 2025-07-29 08:21:15 +03:00

MCOL-4680 FROM subquery containing nested joins returns an error.

Main theme of the patch is to fix joins processing in the plugin
code. We now use SELECT_LEX::top_join_list and process the nested
joins recursively, instead of SELECT_LEX::table_list struct which
we earlier used to build the join filters. The earlier approach
did not process certain nested join ON expressions, causing certain
queries to incorrectly error out such as that described in MCOL-4680.

In addition, some legacy code is also removed.
This commit is contained in:
Gagan Goel
2021-04-23 05:18:26 -04:00
parent 71c16fcb56
commit 22c7fb7c01
6 changed files with 273 additions and 272 deletions

View File

@ -134,6 +134,15 @@ public:
namespace cal_impl_if
{
CalpontSystemCatalog::TableAliasName makeTableAliasName(TABLE_LIST* table)
{
return make_aliasview(
(table->db.length ? table->db.str : ""),
(table->table_name.length ? table->table_name.str : ""),
(table->alias.length ? table->alias.str : ""),
getViewName(table), true, lower_case_table_names);
}
//@bug5228. need to escape backtick `
string escapeBackTick(const char* str)
{
@ -1239,245 +1248,122 @@ void debug_walk(const Item* item, void* arg)
}
#endif
void buildNestedTableOuterJoin(gp_walk_info& gwi, TABLE_LIST* table_ptr)
void buildNestedJoinLeafTables(List<TABLE_LIST>& join_list,
std::set<execplan::CalpontSystemCatalog::TableAliasName>& leafTables)
{
TABLE_LIST* table;
List_iterator<TABLE_LIST> li(table_ptr->nested_join->join_list);
TABLE_LIST *table;
List_iterator<TABLE_LIST> li(join_list);
while ((table = li++))
{
gwi.innerTables.clear();
if (table->outer_join)
{
CalpontSystemCatalog::TableAliasName ta = make_aliasview(
(table->db.length ? table->db.str : ""),
(table->table_name.length ? table->table_name.str : ""),
(table->alias.length ? table->alias.str : ""),
getViewName(table), true, lower_case_table_names);
gwi.innerTables.insert(ta);
}
if (table->nested_join)
buildNestedJoinLeafTables(table->nested_join->join_list, leafTables);
else
{
TABLE_LIST* tab;
List_iterator<TABLE_LIST> li(table->nested_join->join_list);
while ((tab = li++))
{
CalpontSystemCatalog::TableAliasName ta = make_aliasview(
(tab->db.length ? tab->db.str : ""),
(tab->table_name.length ? tab->table_name.str : ""),
(tab->alias.length ? tab->alias.str : ""),
getViewName(tab), true, lower_case_table_names);
gwi.innerTables.insert(ta);
}
}
if (table->on_expr)
{
Item_cond* expr = reinterpret_cast<Item_cond*>(table->on_expr);
#ifdef DEBUG_WALK_COND
expr->traverse_cond(debug_walk, &gwi, Item::POSTFIX);
#endif
expr->traverse_cond(gp_walk, &gwi, Item::POSTFIX);
}
if (table->nested_join && &(table->nested_join->join_list))
{
buildNestedTableOuterJoin(gwi, table);
CalpontSystemCatalog::TableAliasName tan = makeTableAliasName(table);
leafTables.insert(tan);
}
}
}
uint32_t buildOuterJoin(gp_walk_info& gwi, SELECT_LEX& select_lex)
uint32_t buildJoin(gp_walk_info& gwi, List<TABLE_LIST>& join_list,
std::stack<execplan::ParseTree*>& outerJoinStack)
{
// check non-collapsed outer join
// this set contains all processed embedded joins. duplicate joins are ignored
set<TABLE_LIST*> embeddingSet;
TABLE_LIST* table_ptr = select_lex.get_table_list();
gp_walk_info gwi_outer = gwi;
gwi_outer.subQuery = NULL;
gwi_outer.hasSubSelect = false;
vector <Item_field*> tmpVec;
TABLE_LIST *table;
List_iterator<TABLE_LIST> li(join_list);
for (; table_ptr; table_ptr = table_ptr->next_local)
while ((table = li++))
{
gwi_outer.innerTables.clear();
clearStacks(gwi_outer);
gwi_outer.subQuery = NULL;
gwi_outer.hasSubSelect = false;
// Make sure we don't process the derived table nests again,
// they were already handled by FromSubQuery::transform()
if (table->nested_join && !table->derived)
buildJoin(gwi, table->nested_join->join_list, outerJoinStack);
// View is already processed in view::transform
// @bug5319. view is sometimes treated as derived table and
// fromSub::transform does not build outer join filters.
if (!table_ptr->derived && table_ptr->view && !gwi.subQuery)
continue;
CalpontSystemCatalog:: TableAliasName tan = make_aliasview(
(table_ptr->db.length ? table_ptr->db.str : ""),
(table_ptr->table_name.length ? table_ptr->table_name.str : ""),
(table_ptr->alias.length ? table_ptr->alias.str : ""),
getViewName(table_ptr), true, lower_case_table_names);
if ((table_ptr->outer_join & (JOIN_TYPE_LEFT | JOIN_TYPE_RIGHT)) && table_ptr->on_expr)
if (table->on_expr)
{
// inner tables block
Item_cond* expr = reinterpret_cast<Item_cond*>(table_ptr->on_expr);
gwi_outer.innerTables.insert(tan);
Item_cond* expr = reinterpret_cast<Item_cond*>(table->on_expr);
if (table_ptr->nested_join && &(table_ptr->nested_join->join_list))
if (table->outer_join & (JOIN_TYPE_LEFT | JOIN_TYPE_RIGHT))
{
TABLE_LIST* table;
List_iterator<TABLE_LIST> li(table_ptr->nested_join->join_list);
// inner tables block
gp_walk_info gwi_outer = gwi;
gwi_outer.subQuery = NULL;
gwi_outer.hasSubSelect = false;
gwi_outer.innerTables.clear();
clearStacks(gwi_outer);
while ((table = li++))
// recursively build the leaf tables for this nested join node
if (table->nested_join)
buildNestedJoinLeafTables(table->nested_join->join_list,
gwi_outer.innerTables);
else // this is a leaf table
{
CalpontSystemCatalog::TableAliasName ta = make_aliasview(
(table->db.length ? table->db.str : ""),
(table->table_name.length ? table->table_name.str : ""),
(table->alias.length ? table->alias.str : ""),
getViewName(table), true, lower_case_table_names);
gwi_outer.innerTables.insert(ta);
CalpontSystemCatalog::TableAliasName tan = makeTableAliasName(table);
gwi_outer.innerTables.insert(tan);
}
}
#ifdef DEBUG_WALK_COND
cerr << "inner tables: " << endl;
set<CalpontSystemCatalog::TableAliasName>::const_iterator it;
for (it = gwi_outer.innerTables.begin(); it != gwi_outer.innerTables.end(); ++it)
cerr << (*it) << " ";
cerr << endl;
if (table_ptr->alias.length)
cerr << table_ptr->alias.str;
else if (table_ptr->alias.length)
cerr << table_ptr->alias.str;
cerr << " outer table expression: " << endl;
expr->traverse_cond(debug_walk, &gwi_outer, Item::POSTFIX);
cerr << " outer table expression: " << endl;
expr->traverse_cond(debug_walk, &gwi_outer, Item::POSTFIX);
#endif
expr->traverse_cond(gp_walk, &gwi_outer, Item::POSTFIX);
}
else if (table_ptr->nested_join && &(table_ptr->nested_join->join_list))
{
buildNestedTableOuterJoin(gwi_outer, table_ptr);
}
// this part is ambiguous. Not quite sure how MySQL's lay out the outer join filters in the structure
else if (table_ptr->embedding && table_ptr->embedding->outer_join && table_ptr->embedding->on_expr)
{
// all the tables in nested_join are inner tables.
TABLE_LIST* table;
List_iterator<TABLE_LIST> li(table_ptr->embedding->nested_join->join_list);
gwi_outer.innerTables.clear();
while ((table = li++))
{
CalpontSystemCatalog:: TableAliasName ta = make_aliasview(
(table->db.length ? table->db.str : ""),
(table->table_name.length ? table->table_name.str : ""),
(table->alias.length ? table->alias.str : ""),
getViewName(table), true, lower_case_table_names);
gwi_outer.innerTables.insert(ta);
}
expr->traverse_cond(gp_walk, &gwi_outer, Item::POSTFIX);
if (embeddingSet.find(table_ptr->embedding) != embeddingSet.end())
continue;
embeddingSet.insert(table_ptr->embedding);
Item_cond* expr = reinterpret_cast<Item_cond*>(table_ptr->embedding->on_expr);
#ifdef DEBUG_WALK_COND
cerr << "inner tables: " << endl;
set<CalpontSystemCatalog::TableAliasName>::const_iterator it;
for (it = gwi_outer.innerTables.begin(); it != gwi_outer.innerTables.end(); ++it)
cerr << (*it) << " ";
cerr << endl;
expr->traverse_cond(debug_walk, &gwi_outer, Item::POSTFIX);
#endif
expr->traverse_cond(gp_walk, &gwi_outer, Item::POSTFIX);
}
// *DRRTUY This part is to be removed in 1.4.3
#if 0
// @bug 2849
/*else if (table_ptr->embedding && table_ptr->embedding->nested_join)
{
// if this is dervied table process phase, mysql may have not developed the plan
// completely. Return and let it finish. It will come to rnd_init again.
if (table_ptr->embedding->is_natural_join && table_ptr->derived)
{
if (gwi.thd->derived_tables_processing)
// Error out subquery in outer join on filter for now
if (gwi_outer.hasSubSelect)
{
// MCOL-2178 isUnion member only assigned, never used
//MIGR::infinidb_vtable.isUnion = false;
gwi.cs_vtable_is_update_with_derive = true;
gwi.fatalParseError = true;
gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_OUTER_JOIN_SUBSELECT);
setError(gwi.thd, ER_INTERNAL_ERROR, gwi.parseErrorText);
return -1;
}
}
if (embeddingSet.find(table_ptr->embedding) != embeddingSet.end())
continue;
// build outerjoinon filter
ParseTree* filters = NULL, *ptp = NULL, *rhs = NULL;
gwi_outer.innerTables.clear();
gwi_outer.innerTables.insert(tan);
embeddingSet.insert(table_ptr->embedding);
List<TABLE_LIST>* inners = &(table_ptr->embedding->nested_join->join_list);
List_iterator_fast<TABLE_LIST> li(*inners);
TABLE_LIST* curr;
while ((curr = li++))
{
if (curr->on_expr)
while (!gwi_outer.ptWorkStack.empty())
{
if (!curr->outer_join) // only handle nested JOIN for now
{
gwi_outer.innerTables.clear();
Item_cond* expr = reinterpret_cast<Item_cond*>(curr->on_expr);
filters = gwi_outer.ptWorkStack.top();
gwi_outer.ptWorkStack.pop();
#ifdef DEBUG_WALK_COND
expr->traverse_cond(debug_walk, &gwi_outer, Item::POSTFIX);
#endif
expr->traverse_cond(gp_walk, &gwi_outer, Item::POSTFIX);
}
if (gwi_outer.ptWorkStack.empty())
break;
ptp = new ParseTree(new LogicOperator("and"));
ptp->left(filters);
rhs = gwi_outer.ptWorkStack.top();
gwi_outer.ptWorkStack.pop();
ptp->right(rhs);
gwi_outer.ptWorkStack.push(ptp);
}
// should have only 1 pt left in stack.
if (filters)
{
SPTP on_sp(filters);
OuterJoinOnFilter* onFilter = new OuterJoinOnFilter(on_sp);
ParseTree* pt = new ParseTree(onFilter);
outerJoinStack.push(pt);
}
}
} */
else // inner join
{
expr->traverse_cond(gp_walk, &gwi, Item::POSTFIX);
#ifdef DEBUG_WALK_COND
cerr << " inner join expression: " << endl;
expr->traverse_cond(debug_walk, &gwi, Item::POSTFIX);
#endif
// Error out subquery in outer join on filter for now
if (gwi_outer.hasSubSelect)
{
gwi.fatalParseError = true;
gwi.parseErrorText = IDBErrorInfo::instance()->errorMsg(ERR_OUTER_JOIN_SUBSELECT);
setError(gwi.thd, ER_INTERNAL_ERROR, gwi.parseErrorText);
return -1;
}
// build outerjoinon filter
ParseTree* filters = NULL, *ptp = NULL, *lhs = NULL;
while (!gwi_outer.ptWorkStack.empty())
{
filters = gwi_outer.ptWorkStack.top();
gwi_outer.ptWorkStack.pop();
if (gwi_outer.ptWorkStack.empty())
break;
ptp = new ParseTree(new LogicOperator("and"));
ptp->right(filters);
lhs = gwi_outer.ptWorkStack.top();
gwi_outer.ptWorkStack.pop();
ptp->left(lhs);
gwi_outer.ptWorkStack.push(ptp);
}
// should have only 1 pt left in stack.
if (filters)
{
SPTP on_sp(filters);
OuterJoinOnFilter* onFilter = new OuterJoinOnFilter(on_sp);
ParseTree* pt = new ParseTree(onFilter);
gwi.ptWorkStack.push(pt);
}
}
}
embeddingSet.clear();
return 0;
}
@ -6289,16 +6175,13 @@ void setExecutionParams(gp_walk_info &gwi, SCSEP &csep)
* FROM part of the query.
* isUnion tells that CS processes FROM taken from UNION UNIT.
* The notion is described in MDB code.
* on_expr_list ON expressions used in OUTER JOINs. These are
* later used in processWhere()
* RETURNS
* error id as an int
***********************************************************/
int processFrom(bool &isUnion,
SELECT_LEX &select_lex,
gp_walk_info &gwi,
SCSEP &csep,
List<Item> &on_expr_list)
SCSEP &csep)
{
// populate table map and trigger syscolumn cache for all the tables (@bug 1637).
// all tables on FROM list must have at least one col in colmap
@ -6328,12 +6211,6 @@ int processFrom(bool &isUnion,
return ER_CHECK_NOT_IMPLEMENTED;
}
// Save on_expr to use it for WHERE processing
if (!(table_ptr->outer_join & (JOIN_TYPE_LEFT | JOIN_TYPE_RIGHT)) && table_ptr->on_expr)
{
on_expr_list.push_back(table_ptr->on_expr);
}
string viewName = getViewName(table_ptr);
if (lower_case_table_names)
{
@ -6483,15 +6360,12 @@ int processFrom(bool &isUnion,
* DESCRIPTION:
* This function processes conditions from either JOIN->conds
* or SELECT_LEX->where|prep_where
* on_expr_list ON expressions used in OUTER JOINs. These are
* populated used in processFrom()
* RETURNS
* error id as an int
***********************************************************/
int processWhere(SELECT_LEX &select_lex,
gp_walk_info &gwi,
SCSEP &csep,
List<Item> &on_expr_list,
const std::vector<COND*>& condStack)
{
JOIN* join = select_lex.join;
@ -6609,27 +6483,6 @@ int processWhere(SELECT_LEX &select_lex,
(dynamic_cast<ConstantColumn*>(gwi.rcWorkStack.top()))->timeZone(gwi.thd->variables.time_zone->get_name()->ptr());
}
#ifdef DEBUG_WALK_COND
std::cerr << "------------------ ON_EXPR -----------------------" << endl;
#endif
// MCOL-3593 MDB now doesn't rewrite and/or consolidate ON and WHERE expressions
// and CS handles INNER ON expressions here.
if (!on_expr_list.is_empty())
{
List_iterator<Item> on_expr_it(on_expr_list);
Item_cond *on_expr = NULL;
while((on_expr = reinterpret_cast<Item_cond*>(on_expr_it++)))
{
on_expr->traverse_cond(gp_walk, &gwi, Item::POSTFIX);
#ifdef DEBUG_WALK_COND
on_expr->traverse_cond(debug_walk, &gwi, Item::POSTFIX);
#endif
}
}
#ifdef DEBUG_WALK_COND
std::cerr << "-------------------------------------------------\n" << std::endl;
#endif
// ZZ - the followinig debug shows the structure of nested outer join. should
// use a recursive function.
#ifdef OUTER_JOIN_DEBUG
@ -6694,26 +6547,31 @@ int processWhere(SELECT_LEX &select_lex,
}
#endif
uint32_t failed = buildOuterJoin(gwi, select_lex);
uint32_t failed = 0;
if (failed) return failed;
// InfiniDB bug5764 requires outer joins to be appended to the
// end of the filter list. This causes outer join filters to
// have a higher join id than inner join filters.
// TODO MCOL-4680 Figure out why this is the case, and possibly
// eliminate this requirement.
std::stack<execplan::ParseTree*> outerJoinStack;
// @bug5764. build outer join for view, make sure outerjoin filter is appended
// to the end of the filter list.
for (uint i = 0; i < gwi.viewList.size(); i++)
{
failed = gwi.viewList[i]->processOuterJoin(gwi);
if (failed)
break;
}
if (failed != 0)
if ((failed = buildJoin(gwi, select_lex.top_join_list, outerJoinStack)))
return failed;
if (gwi.subQuery)
{
for (uint i = 0; i < gwi.viewList.size(); i++)
{
if ((failed = gwi.viewList[i]->processJoin(gwi, outerJoinStack)))
return failed;
}
}
ParseTree* filters = NULL;
ParseTree* outerJoinFilters = NULL;
ParseTree* ptp = NULL;
ParseTree* lhs = NULL;
ParseTree* rhs = NULL;
// @bug 2932. for "select * from region where r_name" case. if icp not null and
// ptWorkStack empty, the item is in rcWorkStack.
@ -6734,13 +6592,45 @@ int processWhere(SELECT_LEX &select_lex,
break;
ptp = new ParseTree(new LogicOperator("and"));
ptp->right(filters);
lhs = gwi.ptWorkStack.top();
ptp->left(filters);
rhs = gwi.ptWorkStack.top();
gwi.ptWorkStack.pop();
ptp->left(lhs);
ptp->right(rhs);
gwi.ptWorkStack.push(ptp);
}
while (!outerJoinStack.empty())
{
outerJoinFilters = outerJoinStack.top();
outerJoinStack.pop();
if (outerJoinStack.empty())
break;
ptp = new ParseTree(new LogicOperator("and"));
ptp->left(outerJoinFilters);
rhs = outerJoinStack.top();
outerJoinStack.pop();
ptp->right(rhs);
outerJoinStack.push(ptp);
}
// Append outer join filters at the end of inner join filters.
// JLF_ExecPlanToJobList::walkTree processes ParseTree::left
// before ParseTree::right which is what we intend to do in the
// below.
if (filters && outerJoinFilters)
{
ptp = new ParseTree(new LogicOperator("and"));
ptp->left(filters);
ptp->right(outerJoinFilters);
filters = ptp;
}
else if (outerJoinFilters)
{
filters = outerJoinFilters;
}
if (filters)
{
csep->filters(filters);
@ -6928,15 +6818,15 @@ int getSelectPlan(gp_walk_info& gwi, SELECT_LEX& select_lex,
CalpontSelectExecutionPlan::SelectList derivedTbList;
// @bug 1796. Remember table order on the FROM list.
gwi.clauseType = FROM;
List<Item> on_expr_list;
if ((rc = processFrom(isUnion, select_lex, gwi, csep, on_expr_list)))
if ((rc = processFrom(isUnion, select_lex, gwi, csep)))
{
return rc;
}
bool unionSel = (!isUnion && select_lex.master_unit()->is_unit_op()) ? true : false;
gwi.clauseType = WHERE;
if ((rc = processWhere(select_lex, gwi, csep, on_expr_list, condStack)))
if ((rc = processWhere(select_lex, gwi, csep, condStack)))
{
return rc;
}
@ -8628,26 +8518,36 @@ int getGroupPlan(gp_walk_info& gwi, SELECT_LEX& select_lex, SCSEP& csep, cal_gro
SELECT_LEX tmp_select_lex;
tmp_select_lex.table_list.first = gi.groupByTables;
uint32_t failed = buildOuterJoin(gwi, tmp_select_lex);
// InfiniDB bug5764 requires outer joins to be appended to the
// end of the filter list. This causes outer join filters to
// have a higher join id than inner join filters.
// TODO MCOL-4680 Figure out why this is the case, and possibly
// eliminate this requirement.
std::stack<execplan::ParseTree*> outerJoinStack;
uint32_t failed = buildJoin(gwi, tmp_select_lex.top_join_list, outerJoinStack);
if (failed) return failed;
// @bug5764. build outer join for view, make sure outerjoin filter is appended
// to the end of the filter list.
for (uint i = 0; i < gwi.viewList.size(); i++)
if (gwi.subQuery)
{
failed = gwi.viewList[i]->processOuterJoin(gwi);
for (uint i = 0; i < gwi.viewList.size(); i++)
{
failed = gwi.viewList[i]->processJoin(gwi, outerJoinStack);
if (failed)
break;
if (failed)
break;
}
}
if (failed != 0)
return failed;
ParseTree* filters = NULL;
ParseTree* outerJoinFilters = NULL;
ParseTree* ptp = NULL;
ParseTree* lhs = NULL;
ParseTree* rhs = NULL;
// @bug 2932. for "select * from region where r_name" case. if icp not null and
// ptWorkStack empty, the item is in rcWorkStack.
@ -8668,15 +8568,45 @@ int getGroupPlan(gp_walk_info& gwi, SELECT_LEX& select_lex, SCSEP& csep, cal_gro
break;
ptp = new ParseTree(new LogicOperator("and"));
//ptp->left(filters);
ptp->right(filters);
lhs = gwi.ptWorkStack.top();
ptp->left(filters);
rhs = gwi.ptWorkStack.top();
gwi.ptWorkStack.pop();
//ptp->right(rhs);
ptp->left(lhs);
ptp->right(rhs);
gwi.ptWorkStack.push(ptp);
}
while (!outerJoinStack.empty())
{
outerJoinFilters = outerJoinStack.top();
outerJoinStack.pop();
if (outerJoinStack.empty())
break;
ptp = new ParseTree(new LogicOperator("and"));
ptp->left(outerJoinFilters);
rhs = outerJoinStack.top();
outerJoinStack.pop();
ptp->right(rhs);
outerJoinStack.push(ptp);
}
// Append outer join filters at the end of inner join filters.
// JLF_ExecPlanToJobList::walkTree processes ParseTree::left
// before ParseTree::right which is what we intend to do in the
// below.
if (filters && outerJoinFilters)
{
ptp = new ParseTree(new LogicOperator("and"));
ptp->left(filters);
ptp->right(outerJoinFilters);
filters = ptp;
}
else if (outerJoinFilters)
{
filters = outerJoinFilters;
}
if (filters)
{
csep->filters(filters);

View File

@ -373,7 +373,7 @@ bool buildRowColumnFilter(gp_walk_info* gwip, execplan::RowColumn* rhs, execplan
bool buildPredicateItem(Item_func* ifp, gp_walk_info* gwip);
void collectAllCols(gp_walk_info& gwi, Item_field* ifp);
void buildSubselectFunc(Item_func* ifp, gp_walk_info* gwip);
uint32_t buildOuterJoin(gp_walk_info& gwi, SELECT_LEX& select_lex);
uint32_t buildJoin(gp_walk_info& gwi, List<TABLE_LIST>& join_list, std::stack<execplan::ParseTree*>& outerJoinStack);
std::string getViewName(TABLE_LIST* table_ptr);
bool buildConstPredicate(Item_func* ifp, execplan::ReturnedColumn* rhs, gp_walk_info* gwip);
execplan::CalpontSystemCatalog::ColType fieldType_MysqlToIDB (const Field* field);

View File

@ -44,7 +44,8 @@ using namespace execplan;
namespace cal_impl_if
{
extern uint32_t buildOuterJoin(gp_walk_info& gwi, SELECT_LEX& select_lex);
extern uint32_t buildJoin(gp_walk_info& gwi, List<TABLE_LIST>& join_list,
std::stack<execplan::ParseTree*>& outerJoinStack);
extern string getViewName(TABLE_LIST* table_ptr);
CalpontSystemCatalog::TableAliasName& View::viewName()
@ -182,9 +183,9 @@ void View::transform()
}
}
uint32_t View::processOuterJoin(gp_walk_info& gwi)
uint32_t View::processJoin(gp_walk_info& gwi, std::stack<execplan::ParseTree*>& outerJoinStack)
{
return buildOuterJoin(gwi, fSelect);
return buildJoin(gwi, fSelect.top_join_list, outerJoinStack);
}
}

View File

@ -53,7 +53,7 @@ public:
parent select.
*/
void transform();
uint32_t processOuterJoin(gp_walk_info& gwi);
uint32_t processJoin(gp_walk_info& gwi, std::stack<execplan::ParseTree*>&);
private:
SELECT_LEX fSelect;