1
0
mirror of https://github.com/mariadb-corporation/mariadb-columnstore-engine.git synced 2025-07-30 19:23:07 +03:00

MCOL-2178 SH now allows to fallback to other pushdown handlers.

SH query execution migrated from SH::init() into create_SH().
There is a session variable columnstore_processing_handlers_fallback
that allows to fallback to DH, GBH if SH fails. DH now uses semantic
tree check for unsupported features to allow to fallback to GBH or
storage API.

Fixes GBH related bug when create_GBH() returns a handler for
queries with impossible WHERE/HAVING.

Fixed bug in FromSubquery::transform() where isUnion is set to true.

Enabled RTTI b/c server team enabled it for MDB.

Removed unused code supposed to be used with vtable.
This commit is contained in:
Roman Nozdrin
2019-08-25 04:05:59 +03:00
parent 4ef1133d9f
commit 2c63258537
10 changed files with 469 additions and 167 deletions

View File

@ -14,12 +14,13 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
// ha_calpont.cpp includes this file.
void check_walk(const Item* item, void* arg);
void mutate_optimizer_flags(THD *thd_)
{
// MCOL-2178 Disable all optimizer flags as it was in the fork.
// MCOL-2178 Disable all optimizer flags as it was in the fork.
// CS restores it later in SH::scan_end() and in case of an error
// in SH::scan_init()
set_original_optimizer_flags(thd_->variables.optimizer_switch, thd_);
@ -38,65 +39,196 @@ void restore_optimizer_flags(THD *thd_)
set_original_optimizer_flags(0, thd_);
}
}
/*@brief check_walk - It traverses filter conditions*/
/************************************************************
* DESCRIPTION:
* It traverses filter predicates looking for unsupported
* JOIN types: non-equi JOIN, e.g t1.c1 > t2.c2;
* logical OR.
* PARAMETERS:
* thd - THD pointer.
* derived - TABLE_LIST* to work with.
* RETURN:
* derived_handler if possible
* NULL in other case
***********************************************************/
void check_walk(const Item* item, void* arg)
/*@brief find_tables - This traverses Item */
/**********************************************************
* DESCRIPTION:
* This f() pushes TABLE of an Item_field to a list
* provided. The list is used for JOIN predicate check in
* is_joinkeys_predicate().
* PARAMETERS:
* Item * Item to check
* RETURN:
***********************************************************/
void find_tables(const Item* item, void* arg)
{
bool* unsupported_feature = static_cast<bool*>(arg);
if (typeid(*item) == typeid(Item_field))
{
Item_field *ifp= (Item_field*)item;
List<TABLE> *tables_list= (List<TABLE>*)arg;
tables_list->push_back(ifp->field->table);
}
}
/*@brief is_joinkeys_predicate - This traverses Item_func*/
/***********************************************************
* DESCRIPTION:
* This f() walks Item_func and checks whether it contains
* JOIN predicate
* PARAMETERS:
* Item_func * Item to walk
* RETURN:
* BOOL false if Item_func isn't a JOIN predicate
* BOOL true otherwise
***********************************************************/
bool is_joinkeys_predicate(const Item_func *ifp)
{
bool result = false;
if(ifp->argument_count() == 2)
{
if (ifp->arguments()[0]->type() == Item::FIELD_ITEM &&
ifp->arguments()[1]->type() == Item::FIELD_ITEM)
{
Item_field* left= reinterpret_cast<Item_field*>(ifp->arguments()[0]);
Item_field* right= reinterpret_cast<Item_field*>(ifp->arguments()[1]);
// If MDB crashes here with non-fixed Item_field and field == NULL
// there must be a check over on_expr for a different SELECT_LEX.
// e.g. checking subquery with ON from upper query.
if (left->field->table != right->field->table)
{
result= true;
}
}
else
{
List<TABLE>llt; List<TABLE>rlt;
Item *left= ifp->arguments()[0];
Item *right= ifp->arguments()[1];
// Search for tables inside left and right expressions
// and compare them
left->traverse_cond(find_tables, (void*)&llt, Item::POSTFIX);
right->traverse_cond(find_tables, (void*)&rlt, Item::POSTFIX);
// TODO Find the way to have more then one element or prove
// the idea is useless.
if (llt.elements && rlt.elements && (llt.elem(0) != rlt.elem(0)))
{
result= true;
}
}
}
return result;
}
/*@brief find_nonequi_join - This traverses Item */
/************************************************************
* DESCRIPTION:
* This f() walks Item and looks for a non-equi join
* predicates
* PARAMETERS:
* Item * Item to walk
* RETURN:
***********************************************************/
void find_nonequi_join(const Item* item, void *arg)
{
bool *unsupported_feature = reinterpret_cast<bool*>(arg);
if ( *unsupported_feature )
return;
if (item->type() == Item::FUNC_ITEM)
{
const Item_func* ifp = reinterpret_cast<const Item_func*>(item);
//TODO Check for IN
//NOT IN + correlated subquery
if (ifp->functype() != Item_func::EQ_FUNC)
{
if (is_joinkeys_predicate(ifp))
*unsupported_feature = true;
else if (ifp->functype() == Item_func::NOT_FUNC
&& ifp->arguments()[0]->type() == Item::EXPR_CACHE_ITEM)
{
check_walk(ifp->arguments()[0], arg);
}
}
}
}
/*@brief find_join - This traverses Item */
/************************************************************
* DESCRIPTION:
* This f() walks traverses Item looking for JOIN, SEMI-JOIN
* predicates.
* PARAMETERS:
* Item * Item to traverse
* RETURN:
***********************************************************/
void find_join(const Item* item, void* arg)
{
bool *unsupported_feature = reinterpret_cast<bool*>(arg);
if ( *unsupported_feature )
return;
if (item->type() == Item::FUNC_ITEM)
{
const Item_func* ifp = reinterpret_cast<const Item_func*>(item);
//TODO Check for IN
//NOT IN + correlated subquery
{
if (is_joinkeys_predicate(ifp))
*unsupported_feature = true;
else if (ifp->functype() == Item_func::NOT_FUNC
&& ifp->arguments()[0]->type() == Item::EXPR_CACHE_ITEM)
{
check_walk(ifp->arguments()[0], arg);
}
}
}
}
/*@brief save_join_predicate - This traverses Item */
/************************************************************
* DESCRIPTION:
* This f() walks Item and saves found JOIN predicates into
* a List. The list will be used for a simple CROSS JOIN
* check in create_DH.
* PARAMETERS:
* Item * Item to walk
* RETURN:
***********************************************************/
void save_join_predicates(const Item* item, void* arg)
{
if (item->type() == Item::FUNC_ITEM)
{
const Item_func* ifp= reinterpret_cast<const Item_func*>(item);
if (is_joinkeys_predicate(ifp))
{
List<Item> *join_preds_list= (List<Item>*)arg;
join_preds_list->push_back(const_cast<Item*>(item));
}
}
}
/*@brief check_walk - It traverses filter conditions */
/************************************************************
* DESCRIPTION:
* It traverses filter predicates looking for unsupported
* JOIN types: non-equi JOIN, e.g t1.c1 > t2.c2;
* logical OR.
* PARAMETERS:
* thd - THD pointer.
* derived - TABLE_LIST* to work with.
* RETURN:
***********************************************************/
void check_walk(const Item* item, void* arg)
{
bool *unsupported_feature = reinterpret_cast<bool*>(arg);
if ( *unsupported_feature )
return;
switch (item->type())
{
case Item::FUNC_ITEM:
{
const Item_func* ifp = static_cast<const Item_func*>(item);
if ( ifp->functype() != Item_func::EQ_FUNC ) // NON-equi JOIN
{
if ( ifp->argument_count() == 2 &&
ifp->arguments()[0]->type() == Item::FIELD_ITEM &&
ifp->arguments()[1]->type() == Item::FIELD_ITEM )
{
Item_field* left= static_cast<Item_field*>(ifp->arguments()[0]);
Item_field* right= static_cast<Item_field*>(ifp->arguments()[1]);
if ( left->field->table != right->field->table )
{
*unsupported_feature = true;
return;
}
}
else // IN + correlated subquery
{
if ( ifp->functype() == Item_func::NOT_FUNC
&& ifp->arguments()[0]->type() == Item::EXPR_CACHE_ITEM )
{
check_walk(ifp->arguments()[0], arg);
}
}
}
find_nonequi_join(item, arg);
break;
}
case Item::EXPR_CACHE_ITEM: // IN + correlated subquery
{
const Item_cache_wrapper* icw = static_cast<const Item_cache_wrapper*>(item);
const Item_cache_wrapper* icw = reinterpret_cast<const Item_cache_wrapper*>(item);
if ( icw->get_orig_item()->type() == Item::FUNC_ITEM )
{
const Item_func *ifp = static_cast<const Item_func*>(icw->get_orig_item());
const Item_func *ifp = reinterpret_cast<const Item_func*>(icw->get_orig_item());
if ( ifp->argument_count() == 2 &&
( ifp->arguments()[0]->type() == Item::Item::SUBSELECT_ITEM
|| ifp->arguments()[1]->type() == Item::Item::SUBSELECT_ITEM ))
@ -107,13 +239,24 @@ void check_walk(const Item* item, void* arg)
}
break;
}
case Item::COND_ITEM: // OR in JOIN conds is unsupported yet
case Item::COND_ITEM: // OR contains JOIN conds thats is unsupported yet
{
Item_cond* icp = (Item_cond*)item;
if ( is_cond_or(icp) )
{
*unsupported_feature = true;
bool left_flag= false, right_flag= false;
if (icp->argument_list()->elements >= 2)
{
Item *left; Item *right;
List_iterator<Item> li(*icp->argument_list());
left = li++; right = li++;
left->traverse_cond(find_join, (void*)&left_flag, Item::POSTFIX);
right->traverse_cond(find_join, (void*)&right_flag, Item::POSTFIX);
if (left_flag && right_flag)
{
*unsupported_feature = true;
}
}
}
break;
}
@ -172,9 +315,9 @@ create_calpont_group_by_handler(THD* thd, Query* query)
unsupported_feature = select_lex->is_correlated;
// Impossible HAVING or WHERE
if ( ( !unsupported_feature && select_lex->having_value == Item::COND_FALSE )
|| ( select_lex->cond_count > 0
&& select_lex->cond_value == Item::COND_FALSE ) )
if (!unsupported_feature &&
(select_lex->having_value == Item::COND_FALSE
|| select_lex->cond_value == Item::COND_FALSE ))
{
unsupported_feature = true;
}
@ -188,8 +331,7 @@ create_calpont_group_by_handler(THD* thd, Query* query)
if (join != 0)
icp = reinterpret_cast<Item_cond*>(join->conds);
if ( unsupported_feature == false
&& icp )
if (unsupported_feature == false && icp)
{
icp->traverse_cond(check_walk, &unsupported_feature, Item::POSTFIX);
}
@ -198,16 +340,14 @@ create_calpont_group_by_handler(THD* thd, Query* query)
if (select_lex->where != 0)
icp = reinterpret_cast<Item_cond*>(select_lex->where);
if ( unsupported_feature == false
&& icp )
if (unsupported_feature == false && icp)
{
icp->traverse_cond(check_walk, &unsupported_feature, Item::POSTFIX);
}
}
} // unsupported features check ends here
if ( !unsupported_feature )
if (!unsupported_feature)
{
handler = new ha_calpont_group_by_handler(thd, query);
@ -248,25 +388,70 @@ create_columnstore_derived_handler(THD* thd, TABLE_LIST *derived)
}
SELECT_LEX_UNIT *unit= derived->derived;
SELECT_LEX *sl= unit->first_select();
bool unsupported_feature = false;
// Select_handler use the short-cut that effectively disables
// INSERT..SELECT and LDI
if ( (thd->lex)->sql_command == SQLCOM_INSERT_SELECT
|| (thd->lex)->sql_command == SQLCOM_CREATE_TABLE )
{
SELECT_LEX select_lex = *unit->first_select();
JOIN* join = select_lex.join;
Item_cond* icp = 0;
unsupported_feature = true;
}
if (join != 0)
icp = reinterpret_cast<Item_cond*>(join->conds);
// Impossible HAVING or WHERE
// TODO replace with function call
if ( unsupported_feature
|| sl->having_value == Item::COND_FALSE
|| sl->cond_value == Item::COND_FALSE )
{
unsupported_feature = true;
}
if (!join)
// JOIN expression from WHERE, ON expressions
JOIN* join= sl->join;
//TODO DRRTUY Make a proper tree traverse
//To search for CROSS JOIN-s we use tree invariant
//G(V,E) where [V] = [E]+1
List<Item> join_preds_list;
TABLE_LIST *tl;
for (tl= sl->get_table_list(); tl; tl= tl->next_local)
{
Item_cond* where_icp= 0;
Item_cond* on_icp= 0;
if (tl->where != 0)
{
icp = reinterpret_cast<Item_cond*>(select_lex.where);
where_icp= reinterpret_cast<Item_cond*>(tl->where);
}
if ( icp )
if (where_icp)
{
//icp->traverse_cond(check_walk, &unsupported_feature, Item::POSTFIX);
where_icp->traverse_cond(check_walk, &unsupported_feature, Item::POSTFIX);
where_icp->traverse_cond(save_join_predicates, &join_preds_list, Item::POSTFIX);
}
// Looking for JOIN with ON expression through
// TABLE_LIST in FROM until CS meets unsupported feature
if (tl->on_expr)
{
on_icp= reinterpret_cast<Item_cond*>(tl->on_expr);
on_icp->traverse_cond(check_walk, &unsupported_feature, Item::POSTFIX);
on_icp->traverse_cond(save_join_predicates, &join_preds_list, Item::POSTFIX);
}
}
// CROSS JOIN w/o conditions isn't supported until MCOL-301
// is ready.
if (join && join->table_count >= 2 && !join_preds_list.elements)
{
unsupported_feature= true;
}
// CROSS JOIN with not enough JOIN predicates
if(!unsupported_feature && join
&& join_preds_list.elements < join->table_count-1)
{
unsupported_feature= true;
}
if ( !unsupported_feature )
@ -307,16 +492,9 @@ ha_columnstore_derived_handler::~ha_columnstore_derived_handler()
***********************************************************/
int ha_columnstore_derived_handler::init_scan()
{
char query_buff[4096];
DBUG_ENTER("ha_columnstore_derived_handler::init_scan");
// Save query for logging
String derived_query(query_buff, sizeof(query_buff), thd->charset());
derived_query.length(0);
derived->derived->print(&derived_query, QT_ORDINARY);
mcs_handler_info mhi = mcs_handler_info(static_cast<void*>(this), DERIVED);
mcs_handler_info mhi = mcs_handler_info(reinterpret_cast<void*>(this), DERIVED);
// this::table is the place for the result set
int rc = ha_cs_impl_pushdown_init(&mhi, table);
@ -465,7 +643,7 @@ create_columnstore_select_handler(THD* thd, SELECT_LEX* select_lex)
bool unsupported_feature = false;
// Select_handler use the short-cut that effectively disables
// INSERT..SELECT and LDI
if ( (thd->lex)->sql_command == SQLCOM_INSERT_SELECT
if ( (thd->lex)->sql_command == SQLCOM_INSERT_SELECT
|| (thd->lex)->sql_command == SQLCOM_CREATE_TABLE )
{
@ -481,51 +659,30 @@ create_columnstore_select_handler(THD* thd, SELECT_LEX* select_lex)
unsupported_feature = true;
}
// Unsupported query check.
if ( !unsupported_feature )
{
// JOIN expression from WHERE, ON expressions
JOIN* join = select_lex->join;
Item_cond* where_icp = 0;
Item_cond* on_icp = 0;
if (join != 0)
{
where_icp = reinterpret_cast<Item_cond*>(join->conds);
}
if ( where_icp )
{
//where_icp->traverse_cond(check_walk, &unsupported_feature, Item::POSTFIX);
}
// Looking for JOIN with ON expression through
// TABLE_LIST in FROM until CS meets unsupported feature
TABLE_LIST* table_ptr = select_lex->get_table_list();
for (; !unsupported_feature && table_ptr; table_ptr = table_ptr->next_global)
{
if(table_ptr->on_expr)
{
on_icp = reinterpret_cast<Item_cond*>(table_ptr->on_expr);
//on_icp->traverse_cond(check_walk, &unsupported_feature, Item::POSTFIX);
}
}
// CROSS JOIN w/o conditions isn't supported until MCOL-301
// is ready.
if (join && join->table_count >= 2 && ( !where_icp && !on_icp ))
{
unsupported_feature = true;
}
}
// Next block tries to execute the query using SH very early to fallback
// if execution fails.
if (!unsupported_feature)
{
handler = new ha_columnstore_select_handler(thd, select_lex);
handler= new ha_columnstore_select_handler(thd, select_lex);
mutate_optimizer_flags(thd);
mcs_handler_info mhi= mcs_handler_info(reinterpret_cast<void*>(handler), SELECT);
// this::table is the place for the result set
int rc= ha_cs_impl_pushdown_init(&mhi, handler->table);
// Return SH if query execution is fine or fallback is disabled
if (!rc || !get_fallback_knob(thd))
return handler;
// Reset the DA and restore optimizer flags
// to allow query to fallback to other handlers
if (thd->get_stmt_da()->is_error())
{
thd->get_stmt_da()->reset_diagnostics_area();
restore_optimizer_flags(thd);
}
}
return handler;
return NULL;
}
/***********************************************************
@ -561,18 +718,11 @@ ha_columnstore_select_handler::~ha_columnstore_select_handler()
***********************************************************/
int ha_columnstore_select_handler::init_scan()
{
char query_buff[4096];
DBUG_ENTER("ha_columnstore_select_handler::init_scan");
// Save query for logging
String select_query(query_buff, sizeof(query_buff), thd->charset());
select_query.length(0);
select->print(thd, &select_query, QT_ORDINARY);
mcs_handler_info mhi = mcs_handler_info(static_cast<void*>(this), SELECT);
// this::table is the place for the result set
int rc = ha_cs_impl_pushdown_init(&mhi, table);
// Dummy init for SH. Actual init happens in create_SH
// to allow fallback to other handlers if SH fails.
int rc = 0;
DBUG_RETURN(rc);
}
@ -591,7 +741,7 @@ int ha_columnstore_select_handler::next_row()
{
DBUG_ENTER("ha_columnstore_select_handler::next_row");
int rc = ha_calpont_impl_rnd_next(table->record[0], table);
int rc= ha_cs_impl_select_next(table->record[0], table);
DBUG_RETURN(rc);
}