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

MDEV-25080 Implement ColumnStore-side changes for pushdown of SELECT_LEX_UNITs.

This commit is contained in:
Gagan Goel
2022-08-31 16:46:15 -04:00
parent 8671f55784
commit 45a779f743
11 changed files with 551 additions and 126 deletions

View File

@ -45,7 +45,8 @@ group_by_handler* create_columnstore_group_by_handler(THD* thd, Query* query);
derived_handler* create_columnstore_derived_handler(THD* thd, TABLE_LIST* derived); derived_handler* create_columnstore_derived_handler(THD* thd, TABLE_LIST* derived);
select_handler* create_columnstore_select_handler(THD* thd, SELECT_LEX* sel); select_handler* create_columnstore_select_handler(THD* thd, SELECT_LEX* sel_lex, SELECT_LEX_UNIT* sel_unit);
select_handler* create_columnstore_unit_handler(THD* thd, SELECT_LEX_UNIT* sel_unit);
/* Variables for example share methods */ /* Variables for example share methods */
@ -1835,6 +1836,7 @@ static int columnstore_init_func(void* p)
mcs_hton->create_group_by = create_columnstore_group_by_handler; mcs_hton->create_group_by = create_columnstore_group_by_handler;
mcs_hton->create_derived = create_columnstore_derived_handler; mcs_hton->create_derived = create_columnstore_derived_handler;
mcs_hton->create_select = create_columnstore_select_handler; mcs_hton->create_select = create_columnstore_select_handler;
mcs_hton->create_unit = create_columnstore_unit_handler;
mcs_hton->db_type = DB_TYPE_AUTOASSIGN; mcs_hton->db_type = DB_TYPE_AUTOASSIGN;
#ifdef HAVE_PSI_INTERFACE #ifdef HAVE_PSI_INTERFACE

View File

@ -6681,7 +6681,8 @@ void setExecutionParams(gp_walk_info& gwi, SCSEP& csep)
* RETURNS * RETURNS
* error id as an int * error id as an int
***********************************************************/ ***********************************************************/
int processFrom(bool& isUnion, SELECT_LEX& select_lex, gp_walk_info& gwi, SCSEP& csep) int processFrom(bool& isUnion, SELECT_LEX& select_lex, gp_walk_info& gwi, SCSEP& csep,
bool isSelectHandlerTop, bool isSelectLexUnit)
{ {
// populate table map and trigger syscolumn cache for all the tables (@bug 1637). // 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 // all tables on FROM list must have at least one col in colmap
@ -6820,7 +6821,7 @@ int processFrom(bool& isUnion, SELECT_LEX& select_lex, gp_walk_info& gwi, SCSEP&
// Existed pushdown handlers won't get in this scope // Existed pushdown handlers won't get in this scope
// except UNION pushdown that is to come. // except UNION pushdown that is to come.
// is_unit_op() give a segv for derived_handler's SELECT_LEX // is_unit_op() give a segv for derived_handler's SELECT_LEX
if (!isUnion && select_lex.master_unit()->is_unit_op()) if (!isUnion && (!isSelectHandlerTop || isSelectLexUnit) && select_lex.master_unit()->is_unit_op())
{ {
// MCOL-2178 isUnion member only assigned, never used // MCOL-2178 isUnion member only assigned, never used
// MIGR::infinidb_vtable.isUnion = true; // MIGR::infinidb_vtable.isUnion = true;
@ -7378,7 +7379,8 @@ void buildInToExistsFilter(gp_walk_info& gwi, SELECT_LEX& select_lex)
* error id as an int * error id as an int
***********************************************************/ ***********************************************************/
int getSelectPlan(gp_walk_info& gwi, SELECT_LEX& select_lex, SCSEP& csep, bool isUnion, int getSelectPlan(gp_walk_info& gwi, SELECT_LEX& select_lex, SCSEP& csep, bool isUnion,
bool isSelectHandlerTop, const std::vector<COND*>& condStack) bool isSelectHandlerTop, bool isSelectLexUnit,
const std::vector<COND*>& condStack)
{ {
#ifdef DEBUG_WALK_COND #ifdef DEBUG_WALK_COND
cerr << "getSelectPlan()" << endl; cerr << "getSelectPlan()" << endl;
@ -7406,7 +7408,8 @@ int getSelectPlan(gp_walk_info& gwi, SELECT_LEX& select_lex, SCSEP& csep, bool i
CalpontSelectExecutionPlan::SelectList derivedTbList; CalpontSelectExecutionPlan::SelectList derivedTbList;
// @bug 1796. Remember table order on the FROM list. // @bug 1796. Remember table order on the FROM list.
gwi.clauseType = FROM; gwi.clauseType = FROM;
if ((rc = processFrom(isUnion, select_lex, gwi, csep))) if ((rc = processFrom(isUnion, select_lex, gwi, csep, isSelectHandlerTop,
isSelectLexUnit)))
{ {
return rc; return rc;
} }
@ -8799,9 +8802,10 @@ int cs_get_derived_plan(ha_columnstore_derived_handler* handler, THD* thd, SCSEP
return 0; return 0;
} }
int cs_get_select_plan(ha_columnstore_select_handler* handler, THD* thd, SCSEP& csep, gp_walk_info& gwi) int cs_get_select_plan(ha_columnstore_select_handler* handler, THD* thd, SCSEP& csep, gp_walk_info& gwi,
bool isSelectLexUnit)
{ {
SELECT_LEX& select_lex = *handler->select; SELECT_LEX& select_lex = handler->select_lex ? *handler->select_lex : *handler->lex_unit->first_select();
if (select_lex.where) if (select_lex.where)
{ {
@ -8813,7 +8817,7 @@ int cs_get_select_plan(ha_columnstore_select_handler* handler, THD* thd, SCSEP&
convertOuterJoinToInnerJoin(&select_lex.top_join_list, gwi.tableOnExprList, gwi.condList, convertOuterJoinToInnerJoin(&select_lex.top_join_list, gwi.tableOnExprList, gwi.condList,
handler->tableOuterJoinMap); handler->tableOuterJoinMap);
int status = getSelectPlan(gwi, select_lex, csep, false, true); int status = getSelectPlan(gwi, select_lex, csep, false, true, isSelectLexUnit);
if (status > 0) if (status > 0)
return ER_INTERNAL_ERROR; return ER_INTERNAL_ERROR;

View File

@ -1392,7 +1392,7 @@ uint32_t doUpdateDelete(THD* thd, gp_walk_info& gwi, const std::vector<COND*>& c
gwi.clauseType = WHERE; gwi.clauseType = WHERE;
if (getSelectPlan(gwi, select_lex, updateCP, false, false, condStack) != if (getSelectPlan(gwi, select_lex, updateCP, false, false, false, condStack) !=
0) //@Bug 3030 Modify the error message for unsupported functions 0) //@Bug 3030 Modify the error message for unsupported functions
{ {
if (gwi.cs_vtable_is_update_with_derive) if (gwi.cs_vtable_is_update_with_derive)
@ -4886,7 +4886,7 @@ int ha_mcs_impl_group_by_end(TABLE* table)
* RETURN: * RETURN:
* rc as int * rc as int
***********************************************************/ ***********************************************************/
int ha_mcs_impl_pushdown_init(mcs_handler_info* handler_info, TABLE* table) int ha_mcs_impl_pushdown_init(mcs_handler_info* handler_info, TABLE* table, bool isSelectLexUnit)
{ {
IDEBUG(cout << "pushdown_init for table " << endl); IDEBUG(cout << "pushdown_init for table " << endl);
THD* thd = current_thd; THD* thd = current_thd;
@ -5076,7 +5076,7 @@ int ha_mcs_impl_pushdown_init(mcs_handler_info* handler_info, TABLE* table)
if (handler_info->hndl_type == mcs_handler_types_t::SELECT) if (handler_info->hndl_type == mcs_handler_types_t::SELECT)
{ {
sh = reinterpret_cast<ha_columnstore_select_handler*>(handler_info->hndl_ptr); sh = reinterpret_cast<ha_columnstore_select_handler*>(handler_info->hndl_ptr);
status = cs_get_select_plan(sh, thd, csep, gwi); status = cs_get_select_plan(sh, thd, csep, gwi, isSelectLexUnit);
} }
else if (handler_info->hndl_type == DERIVED) else if (handler_info->hndl_type == DERIVED)
{ {

View File

@ -44,7 +44,7 @@ extern int ha_mcs_impl_direct_update_delete_rows(bool execute, ha_rows* affected
const std::vector<COND*>& condStack); const std::vector<COND*>& condStack);
extern int ha_mcs_impl_delete_row(); extern int ha_mcs_impl_delete_row();
extern int ha_mcs_impl_rnd_pos(uchar* buf, uchar* pos); extern int ha_mcs_impl_rnd_pos(uchar* buf, uchar* pos);
extern int ha_mcs_impl_pushdown_init(mcs_handler_info* handler_info, TABLE* table); extern int ha_mcs_impl_pushdown_init(mcs_handler_info* handler_info, TABLE* table, bool isSelectLexUnit = false);
extern int ha_mcs_impl_select_next(uchar* buf, TABLE* table, long timeZone); extern int ha_mcs_impl_select_next(uchar* buf, TABLE* table, long timeZone);
extern int ha_mcs_impl_group_by_init(mcs_handler_info* handler_info, TABLE* table); extern int ha_mcs_impl_group_by_init(mcs_handler_info* handler_info, TABLE* table);
extern int ha_mcs_impl_group_by_next(TABLE* table, long timeZone); extern int ha_mcs_impl_group_by_next(TABLE* table, long timeZone);

View File

@ -397,9 +397,9 @@ int cp_get_group_plan(THD* thd, execplan::SCSEP& csep, cal_impl_if::cal_group_in
int cs_get_derived_plan(ha_columnstore_derived_handler* handler, THD* thd, execplan::SCSEP& csep, int cs_get_derived_plan(ha_columnstore_derived_handler* handler, THD* thd, execplan::SCSEP& csep,
gp_walk_info& gwi); gp_walk_info& gwi);
int cs_get_select_plan(ha_columnstore_select_handler* handler, THD* thd, execplan::SCSEP& csep, int cs_get_select_plan(ha_columnstore_select_handler* handler, THD* thd, execplan::SCSEP& csep,
gp_walk_info& gwi); gp_walk_info& gwi, bool isSelectLexUnit);
int getSelectPlan(gp_walk_info& gwi, SELECT_LEX& select_lex, execplan::SCSEP& csep, bool isUnion = false, int getSelectPlan(gp_walk_info& gwi, SELECT_LEX& select_lex, execplan::SCSEP& csep, bool isUnion = false,
bool isSelectHandlerTop = false, bool isSelectHandlerTop = false, bool isSelectLexUnit = false,
const std::vector<COND*>& condStack = std::vector<COND*>()); const std::vector<COND*>& condStack = std::vector<COND*>());
int getGroupPlan(gp_walk_info& gwi, SELECT_LEX& select_lex, execplan::SCSEP& csep, cal_group_info& gi, int getGroupPlan(gp_walk_info& gwi, SELECT_LEX& select_lex, execplan::SCSEP& csep, cal_group_info& gi,
bool isUnion = false); bool isUnion = false);

View File

@ -728,20 +728,22 @@ int ha_mcs_group_by_handler::end_scan()
DBUG_RETURN(rc); DBUG_RETURN(rc);
} }
/*@brief create_columnstore_select_handler- Creates handler*/ /*@brief create_columnstore_select_handler_- Creates handler
/************************************************************ ************************************************************
* DESCRIPTION: * DESCRIPTION:
* Creates a select handler if there is no non-equi JOIN, e.g * Creates a select handler if there is no non-equi JOIN, e.g
* t1.c1 > t2.c2 and logical OR in the filter predicates. * t1.c1 > t2.c2 and logical OR in the filter predicates.
* More details in server/sql/select_handler.h * More details in server/sql/select_handler.h
* PARAMETERS: * PARAMETERS:
* thd - THD pointer. * thd - THD pointer.
* select_lex - SELECT_LEX* that describes the query. * sel_lex - SELECT_LEX* that describes the query.
* sel_unit - SELECT_LEX_UNIT* that describes the query.
* Only one of sel_lex and sel_unit is not null.
* RETURN: * RETURN:
* select_handler if possible * select_handler if possible
* NULL in other case * NULL in other case
***********************************************************/ ***********************************************************/
select_handler* create_columnstore_select_handler(THD* thd, SELECT_LEX* select_lex) select_handler* create_columnstore_select_handler_(THD* thd, SELECT_LEX* sel_lex, SELECT_LEX_UNIT* sel_unit)
{ {
mcs_select_handler_mode_t select_handler_mode = get_select_handler_mode(thd); mcs_select_handler_mode_t select_handler_mode = get_select_handler_mode(thd);
@ -778,60 +780,96 @@ select_handler* create_columnstore_select_handler(THD* thd, SELECT_LEX* select_l
return nullptr; return nullptr;
} }
// Iterate and traverse through the item list and the JOIN cond std::vector<SELECT_LEX*> select_lex_vec;
// and do not create SH if the unsupported (set_user_var)
// function is present. if (sel_unit && !sel_lex)
TABLE_LIST* table_ptr = select_lex->get_table_list();
for (; table_ptr; table_ptr = table_ptr->next_global)
{ {
if (check_user_var(table_ptr->select_lex)) for (SELECT_LEX* sl = sel_unit->first_select(); sl; sl = sl->next_select())
{ {
return nullptr; select_lex_vec.push_back(sl);
}
}
else
{
select_lex_vec.push_back(sel_lex);
}
for (size_t i = 0; i < select_lex_vec.size(); i++)
{
SELECT_LEX* select_lex = select_lex_vec[i];
// Iterate and traverse through the item list and the JOIN cond
// and do not create SH if the unsupported (set_user_var)
// function is present.
TABLE_LIST* table_ptr = select_lex->get_table_list();
for (; table_ptr; table_ptr = table_ptr->next_global)
{
if (check_user_var(table_ptr->select_lex))
{
return nullptr;
}
} }
} }
// We apply dedicated rewrites from MDB here so MDB's data structures // We apply dedicated rewrites from MDB here so MDB's data structures
// becomes dirty and CS has to raise an error in case of any problem // becomes dirty and CS has to raise an error in case of any problem
// or unsupported feature. // or unsupported feature.
ha_columnstore_select_handler* handler = new ha_columnstore_select_handler(thd, select_lex); ha_columnstore_select_handler* handler;
JOIN* join = select_lex->join; if (sel_unit && sel_lex)
if (select_lex->first_cond_optimization && select_lex->handle_derived(thd->lex, DT_MERGE))
{ {
if (!thd->is_error()) handler = new ha_columnstore_select_handler(thd, sel_lex, sel_unit);
{ }
my_printf_error(ER_INTERNAL_ERROR, "%s", MYF(0), "Error occured in select_lex::handle_derived()"); else if (sel_unit)
} {
handler = new ha_columnstore_select_handler(thd, sel_unit);
return handler; }
else
{
handler = new ha_columnstore_select_handler(thd, sel_lex);
} }
// This is partially taken from JOIN::optimize_inner() in sql/sql_select.cc for (size_t i = 0; i < select_lex_vec.size(); i++)
if (select_lex->first_cond_optimization)
{ {
create_explain_query_if_not_exists(thd->lex, thd->mem_root); SELECT_LEX* select_lex = select_lex_vec[i];
Query_arena *arena, backup; JOIN* join = select_lex->join;
arena = thd->activate_stmt_arena_if_needed(&backup);
COND* conds = join->conds;
select_lex->where = conds;
if (isPS) if (select_lex->first_cond_optimization && select_lex->handle_derived(thd->lex, DT_MERGE))
{ {
select_lex->prep_where = conds ? conds->copy_andor_structure(thd) : 0; if (!thd->is_error())
{
my_printf_error(ER_INTERNAL_ERROR, "%s", MYF(0), "Error occured in select_lex::handle_derived()");
}
return handler;
} }
select_lex->update_used_tables(); // This is partially taken from JOIN::optimize_inner() in sql/sql_select.cc
if (select_lex->first_cond_optimization)
{
create_explain_query_if_not_exists(thd->lex, thd->mem_root);
Query_arena *arena, backup;
arena = thd->activate_stmt_arena_if_needed(&backup);
COND* conds = join->conds;
select_lex->where = conds;
if (arena) if (isPS)
thd->restore_active_arena(arena, &backup); {
select_lex->prep_where = conds ? conds->copy_andor_structure(thd) : 0;
}
select_lex->update_used_tables();
if (arena)
thd->restore_active_arena(arena, &backup);
#ifdef DEBUG_WALK_COND #ifdef DEBUG_WALK_COND
if (conds) if (conds)
{ {
conds->traverse_cond(cal_impl_if::debug_walk, NULL, Item::POSTFIX); conds->traverse_cond(cal_impl_if::debug_walk, NULL, Item::POSTFIX);
} }
#endif #endif
}
} }
// Attempt to execute the query using the select handler. // Attempt to execute the query using the select handler.
@ -840,69 +878,45 @@ select_handler* create_columnstore_select_handler(THD* thd, SELECT_LEX* select_l
// Skip execution for EXPLAIN queries // Skip execution for EXPLAIN queries
if (!thd->lex->describe) if (!thd->lex->describe)
{ {
// This is taken from JOIN::optimize() for (size_t i = 0; i < select_lex_vec.size(); i++)
join->fields = &select_lex->item_list;
// Instantiate handler::table, which is the place for the result set.
if (handler->prepare())
{ {
// check fallback SELECT_LEX* select_lex = select_lex_vec[i];
if (select_handler_mode == mcs_select_handler_mode_t::AUTO) // columnstore_select_handler=AUTO JOIN* join = select_lex->join;
// This is taken from JOIN::optimize()
join->fields = &select_lex->item_list;
// Instantiate handler::table, which is the place for the result set.
if ((i == 0) && handler->prepare())
{ {
push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 9999, // check fallback
"MCS select_handler execution failed. Falling back to server execution"); if (select_handler_mode == mcs_select_handler_mode_t::AUTO) // columnstore_select_handler=AUTO
restore_query_state(handler); {
delete handler; push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 9999,
return nullptr; "MCS select_handler execution failed. Falling back to server execution");
restore_query_state(handler);
delete handler;
return nullptr;
}
// error out
if (!thd->is_error())
{
my_printf_error(ER_INTERNAL_ERROR, "%s", MYF(0), "Error occured in handler->prepare()");
}
return handler;
} }
// error out // Prepare query execution
if (!thd->is_error()) // This is taken from JOIN::exec_inner()
{ if (!select_lex->outer_select() && // (1)
my_printf_error(ER_INTERNAL_ERROR, "%s", MYF(0), "Error occured in handler->prepare()"); select_lex != select_lex->master_unit()->fake_select_lex) // (2)
} thd->lex->set_limit_rows_examined();
return handler; if ((!sel_unit || sel_lex) && !join->tables_list &&
} (join->table_count || !select_lex->with_sum_func) &&
!select_lex->have_window_funcs())
// Prepare query execution
// This is taken from JOIN::exec_inner()
if (!select_lex->outer_select() && // (1)
select_lex != select_lex->master_unit()->fake_select_lex) // (2)
thd->lex->set_limit_rows_examined();
if (!join->tables_list && (join->table_count || !select_lex->with_sum_func) &&
!select_lex->have_window_funcs())
{
if (!thd->is_error())
{
restore_query_state(handler);
delete handler;
return nullptr;
}
return handler;
}
if (!join->zero_result_cause && join->exec_const_cond && !join->exec_const_cond->val_int())
join->zero_result_cause = "Impossible WHERE noticed after reading const tables";
// We've called exec_const_cond->val_int(). This may have caused an error.
if (unlikely(thd->is_error()))
{
// error out
handler->pushdown_init_rc = 1;
return handler;
}
if (join->zero_result_cause)
{
if (join->select_lex->have_window_funcs() && join->send_row_on_empty_set())
{
join->const_tables = join->table_count;
join->first_select = sub_select_postjoin_aggr;
}
else
{ {
if (!thd->is_error()) if (!thd->is_error())
{ {
@ -913,23 +927,56 @@ select_handler* create_columnstore_select_handler(THD* thd, SELECT_LEX* select_l
return handler; return handler;
} }
}
if ((join->select_lex->options & OPTION_SCHEMA_TABLE) && if (!join->zero_result_cause && join->exec_const_cond && !join->exec_const_cond->val_int())
get_schema_tables_result(join, PROCESSED_BY_JOIN_EXEC)) join->zero_result_cause = "Impossible WHERE noticed after reading const tables";
{
if (!thd->is_error()) // We've called exec_const_cond->val_int(). This may have caused an error.
if (unlikely(thd->is_error()))
{ {
my_printf_error(ER_INTERNAL_ERROR, "%s", MYF(0), "Error occured in get_schema_tables_result()"); // error out
handler->pushdown_init_rc = 1;
return handler;
} }
return handler; if (join->zero_result_cause)
{
if (join->select_lex->have_window_funcs() && join->send_row_on_empty_set())
{
join->const_tables = join->table_count;
join->first_select = sub_select_postjoin_aggr;
}
else
{
if (!thd->is_error())
{
restore_query_state(handler);
delete handler;
return nullptr;
}
return handler;
}
}
if ((join->select_lex->options & OPTION_SCHEMA_TABLE) &&
get_schema_tables_result(join, PROCESSED_BY_JOIN_EXEC))
{
if (!thd->is_error())
{
my_printf_error(ER_INTERNAL_ERROR, "%s", MYF(0), "Error occured in get_schema_tables_result()");
}
return handler;
}
} }
handler->scan_initialized = true; handler->scan_initialized = true;
mcs_handler_info mhi(reinterpret_cast<void*>(handler), SELECT); mcs_handler_info mhi(reinterpret_cast<void*>(handler), SELECT);
if ((handler->pushdown_init_rc = ha_mcs_impl_pushdown_init(&mhi, handler->table))) bool isSelectLexUnit = (sel_unit && !sel_lex) ? true : false;
if ((handler->pushdown_init_rc = ha_mcs_impl_pushdown_init(&mhi, handler->table, isSelectLexUnit)))
{ {
// check fallback // check fallback
if (select_handler_mode == mcs_select_handler_mode_t::AUTO) if (select_handler_mode == mcs_select_handler_mode_t::AUTO)
@ -967,31 +1014,83 @@ select_handler* create_columnstore_select_handler(THD* thd, SELECT_LEX* select_l
return handler; return handler;
} }
// Unset select_lex::first_cond_optimization for (size_t i = 0; i < select_lex_vec.size(); i++)
if (select_lex->first_cond_optimization)
{ {
first_cond_optimization_flag_toggle(select_lex, &first_cond_optimization_flag_unset); SELECT_LEX* select_lex = select_lex_vec[i];
// Unset select_lex::first_cond_optimization
if (select_lex->first_cond_optimization)
{
first_cond_optimization_flag_toggle(select_lex, &first_cond_optimization_flag_unset);
}
} }
} }
return handler; return handler;
} }
select_handler* create_columnstore_select_handler(THD* thd, SELECT_LEX* select_lex, SELECT_LEX_UNIT* sel_unit)
{
return create_columnstore_select_handler_(thd, select_lex, sel_unit);
}
select_handler* create_columnstore_unit_handler(THD* thd, SELECT_LEX_UNIT* sel_unit)
{
return create_columnstore_select_handler_(thd, 0, sel_unit);
}
/*********************************************************** /***********************************************************
* DESCRIPTION: * DESCRIPTION:
* select_handler constructor * select_handler constructor
* PARAMETERS: * PARAMETERS:
* thd - THD pointer. * thd - THD pointer.
* select_lex - sematic tree for the query. * sel_lex - semantic tree for the query.
***********************************************************/ ***********************************************************/
ha_columnstore_select_handler::ha_columnstore_select_handler(THD* thd, SELECT_LEX* select_lex) ha_columnstore_select_handler::ha_columnstore_select_handler(THD* thd, SELECT_LEX* sel_lex)
: select_handler(thd, mcs_hton) : select_handler(thd, mcs_hton, sel_lex)
, prepared(false)
, scan_ended(false)
, scan_initialized(false)
, pushdown_init_rc(0)
{
const char* timeZone = thd->variables.time_zone->get_name()->ptr();
dataconvert::timeZoneToOffset(timeZone, strlen(timeZone), &time_zone);
}
/***********************************************************
* DESCRIPTION:
* select_handler constructor
* PARAMETERS:
* thd - THD pointer.
* sel_unit - semantic tree for the query.
***********************************************************/
ha_columnstore_select_handler::ha_columnstore_select_handler(THD* thd, SELECT_LEX_UNIT* sel_unit)
: select_handler(thd, mcs_hton, sel_unit)
, prepared(false)
, scan_ended(false)
, scan_initialized(false)
, pushdown_init_rc(0)
{
const char* timeZone = thd->variables.time_zone->get_name()->ptr();
dataconvert::timeZoneToOffset(timeZone, strlen(timeZone), &time_zone);
}
/***********************************************************
* DESCRIPTION:
* select_handler constructor
* PARAMETERS:
* thd - THD pointer.
* sel_lex - semantic tree for the query.
* sel_unit - unit containing SELECT_LEX's
***********************************************************/
ha_columnstore_select_handler::ha_columnstore_select_handler(THD* thd, SELECT_LEX* sel_lex,
SELECT_LEX_UNIT* sel_unit)
: select_handler(thd, mcs_hton, sel_lex, sel_unit)
, prepared(false) , prepared(false)
, scan_ended(false) , scan_ended(false)
, scan_initialized(false) , scan_initialized(false)
, pushdown_init_rc(0) , pushdown_init_rc(0)
{ {
select = select_lex;
const char* timeZone = thd->variables.time_zone->get_name()->ptr(); const char* timeZone = thd->variables.time_zone->get_name()->ptr();
dataconvert::timeZoneToOffset(timeZone, strlen(timeZone), &time_zone); dataconvert::timeZoneToOffset(timeZone, strlen(timeZone), &time_zone);
} }
@ -1074,7 +1173,7 @@ bool ha_columnstore_select_handler::prepare()
prepared = true; prepared = true;
if ((!table && !(table = create_tmp_table(thd, select))) || table->fill_item_list(&result_columns)) if ((!table && !(table = create_tmp_table(thd))) || table->fill_item_list(&result_columns))
{ {
pushdown_init_rc = 1; pushdown_init_rc = 1;
DBUG_RETURN(true); DBUG_RETURN(true);

View File

@ -151,7 +151,9 @@ class ha_columnstore_select_handler : public select_handler
// This will be used to restore to the original state later in case // This will be used to restore to the original state later in case
// query execution fails using the select_handler. // query execution fails using the select_handler.
cal_impl_if::TableOuterJoinMap tableOuterJoinMap; cal_impl_if::TableOuterJoinMap tableOuterJoinMap;
ha_columnstore_select_handler(THD* thd_arg, SELECT_LEX* sel); ha_columnstore_select_handler(THD* thd_arg, SELECT_LEX* sel_lex);
ha_columnstore_select_handler(THD* thd_arg, SELECT_LEX_UNIT* sel_unit);
ha_columnstore_select_handler(THD* thd_arg, SELECT_LEX* sel_lex, SELECT_LEX_UNIT* sel_unit);
~ha_columnstore_select_handler(); ~ha_columnstore_select_handler();
int init_scan() override; int init_scan() override;
int next_row() override; int next_row() override;

View File

@ -0,0 +1,114 @@
#
# MDEV-25080: Allow pushdown of queries involving UNIONs
# in outer select to foreign engines
#
# Uncomment the actual SELECTs and add ORDER BY clause
# after MCOL-5222 is fixed
#
CREATE USER IF NOT EXISTS'cejuser'@'localhost' IDENTIFIED BY 'Vagrant1|0000001';
GRANT ALL PRIVILEGES ON *.* TO 'cejuser'@'localhost';
FLUSH PRIVILEGES;
DROP DATABASE IF EXISTS mdev25080;
CREATE DATABASE mdev25080;
USE mdev25080;
CREATE TABLE t1 (a varchar(30)) ENGINE=ColumnStore;
CREATE TABLE t2 (a varchar(30)) ENGINE=ColumnStore;
CREATE TABLE t3 (a varchar(30)) ENGINE=MyISAM;
CREATE TABLE t4 (a varchar(30)) ENGINE=MyISAM;
INSERT INTO t1 VALUES ('abc'), ('bcd'), ('cde');
INSERT INTO t2 VALUES ('bcd'), ('cde'), ('def'), ('efg');
INSERT INTO t3 VALUES ('t3_myisam1'), ('t3_myisam2'), ('t3_myisam3');
INSERT INTO t4 VALUES ('t4_myisam1'), ('t4_myisam2'), ('t4_myisam3');
# Pushdown of the whole UNION
EXPLAIN SELECT * FROM t1 UNION SELECT * FROM t2;
id select_type table type possible_keys key key_len ref rows Extra
NULL PUSHED UNION NULL NULL NULL NULL NULL NULL NULL NULL
EXPLAIN SELECT * FROM t1 UNION ALL SELECT * FROM t2;
id select_type table type possible_keys key key_len ref rows Extra
NULL PUSHED UNION NULL NULL NULL NULL NULL NULL NULL NULL
# UNION with a foreign engine
EXPLAIN SELECT * FROM t1 UNION SELECT * FROM t3;
id select_type table type possible_keys key key_len ref rows Extra
NULL PUSHED UNION NULL NULL NULL NULL NULL NULL NULL NULL
# More than two SELECTs in a UNIT:
EXPLAIN SELECT * FROM t1 UNION
SELECT * FROM t2 UNION ALL
SELECT * FROM t1;
id select_type table type possible_keys key key_len ref rows Extra
NULL PUSHED UNION NULL NULL NULL NULL NULL NULL NULL NULL
EXPLAIN (SELECT * FROM t1 UNION
SELECT * FROM t2) UNION ALL
SELECT * FROM t1;
id select_type table type possible_keys key key_len ref rows Extra
NULL PUSHED UNION NULL NULL NULL NULL NULL NULL NULL NULL
EXPLAIN SELECT * FROM t1 UNION
SELECT * FROM t2 UNION ALL
SELECT * FROM t3 UNION
SELECT * FROM t4;
id select_type table type possible_keys key key_len ref rows Extra
NULL PUSHED UNION NULL NULL NULL NULL NULL NULL NULL NULL
EXPLAIN (SELECT * FROM t1 UNION
SELECT * FROM t2) UNION ALL
(SELECT * FROM t3 UNION
SELECT * FROM t4);
id select_type table type possible_keys key key_len ref rows Extra
NULL PUSHED UNION NULL NULL NULL NULL NULL NULL NULL NULL
EXPLAIN
SELECT count(*) FROM t1 UNION
SELECT count(*) FROM t2 UNION ALL
SELECT count(*)+20 FROM t2 UNION
SELECT count(*)+5 FROM t1;
id select_type table type possible_keys key key_len ref rows Extra
NULL PUSHED UNION NULL NULL NULL NULL NULL NULL NULL NULL
# UNION inside a derived table: the whole derived table must be pushed
SELECT a FROM
(SELECT a FROM t1 UNION ALL SELECT a FROM t2) q ORDER BY a;
a
abc
bcd
bcd
cde
cde
def
efg
EXPLAIN
SELECT a FROM
(SELECT a FROM t1 UNION ALL SELECT a FROM t2) q ORDER BY a;
id select_type table type possible_keys key key_len ref rows Extra
1 PUSHED SELECT NULL NULL NULL NULL NULL NULL NULL NULL
SELECT a FROM
(SELECT a FROM t1 UNION ALL SELECT a FROM t3) q ORDER BY a;
a
abc
bcd
cde
t3_myisam1
t3_myisam2
t3_myisam3
EXPLAIN
SELECT a FROM
(SELECT a FROM t1 UNION ALL SELECT a FROM t3) q ORDER BY a;
id select_type table type possible_keys key key_len ref rows Extra
1 PUSHED SELECT NULL NULL NULL NULL NULL NULL NULL NULL
# Prepared statements
PREPARE stmt FROM "EXPLAIN SELECT * FROM t1 UNION
SELECT * FROM t2";
EXECUTE stmt;
id select_type table type possible_keys key key_len ref rows Extra
NULL PUSHED UNION NULL NULL NULL NULL NULL NULL NULL NULL
EXECUTE stmt;
id select_type table type possible_keys key key_len ref rows Extra
NULL PUSHED UNION NULL NULL NULL NULL NULL NULL NULL NULL
PREPARE stmt FROM "EXPLAIN (SELECT * FROM t1 UNION
SELECT * FROM t2) UNION ALL
(SELECT * FROM t1 UNION
SELECT * FROM t2)";
EXECUTE stmt;
id select_type table type possible_keys key key_len ref rows Extra
NULL PUSHED UNION NULL NULL NULL NULL NULL NULL NULL NULL
EXECUTE stmt;
id select_type table type possible_keys key key_len ref rows Extra
NULL PUSHED UNION NULL NULL NULL NULL NULL NULL NULL NULL
DROP USER 'cejuser'@'localhost';
DROP TABLE t1, t2, t3, t4;
DROP DATABASE mdev25080;

View File

@ -5,6 +5,9 @@ USE mcs_union;
# #
# MCOL-4700 Wrong result of a UNION for INT and INT UNSIGNED # MCOL-4700 Wrong result of a UNION for INT and INT UNSIGNED
# #
# Move the UNIONs from the subqueries to outer selects and add
# ORDER BY clause after MCOL-5222 is fixed
#
CREATE TABLE t1 (a INT, b INT UNSIGNED); CREATE TABLE t1 (a INT, b INT UNSIGNED);
INSERT INTO t1 VALUES (-1, 1), (-1, 1), (-2, 2); INSERT INTO t1 VALUES (-1, 1), (-1, 1), (-2, 2);
SELECT * FROM (SELECT * FROM t1 UNION SELECT * FROM t1) tu ORDER BY b; SELECT * FROM (SELECT * FROM t1 UNION SELECT * FROM t1) tu ORDER BY b;
@ -862,4 +865,27 @@ a
16777213 16777213
9223372036854775807 9223372036854775807
DROP TABLE t1,t2; DROP TABLE t1,t2;
#
# Union of tables containing different string data types
#
# Uncomment the actual SELECTs and add ORDER BY clause
# after MCOL-5222 is fixed
#
CREATE TABLE t1 (a CHAR(6));
INSERT INTO t1 VALUES ('t13abc'), ('t13xx'), ('common');
CREATE TABLE t2 (a VARCHAR(8));
INSERT INTO t2 VALUES ('t14abcde'), ('t14xyzzz'), ('common');
EXPLAIN SELECT * FROM t1 UNION SELECT * FROM t2;
id select_type table type possible_keys key key_len ref rows Extra
NULL PUSHED UNION NULL NULL NULL NULL NULL NULL NULL NULL
EXPLAIN SELECT * FROM t2 UNION ALL SELECT * FROM t1;
id select_type table type possible_keys key key_len ref rows Extra
NULL PUSHED UNION NULL NULL NULL NULL NULL NULL NULL NULL
EXPLAIN SELECT * FROM t1 UNION SELECT * FROM t2 UNION SELECT '123456789000';
id select_type table type possible_keys key key_len ref rows Extra
NULL PUSHED UNION NULL NULL NULL NULL NULL NULL NULL NULL
EXPLAIN SELECT * FROM t1 UNION SELECT '123456789000' UNION SELECT * FROM t2;
id select_type table type possible_keys key key_len ref rows Extra
NULL PUSHED UNION NULL NULL NULL NULL NULL NULL NULL NULL
DROP TABLE t1,t2;
DROP DATABASE mcs_union; DROP DATABASE mcs_union;

View File

@ -0,0 +1,154 @@
--echo #
--echo # MDEV-25080: Allow pushdown of queries involving UNIONs
--echo # in outer select to foreign engines
--echo #
--echo # Uncomment the actual SELECTs and add ORDER BY clause
--echo # after MCOL-5222 is fixed
--echo #
--source ../include/have_columnstore.inc
#
# Enable cross engine join
# Configure user and password in Columnstore.xml file
#
--exec $MCS_MCSSETCONFIG CrossEngineSupport User 'cejuser'
--exec $MCS_MCSSETCONFIG CrossEngineSupport Password 'Vagrant1|0000001'
--exec $MCS_MCSSETCONFIG CrossEngineSupport Port $MASTER_MYPORT
#
# Create corresponding in the server
#
--disable_warnings
CREATE USER IF NOT EXISTS'cejuser'@'localhost' IDENTIFIED BY 'Vagrant1|0000001';
--enable_warnings
GRANT ALL PRIVILEGES ON *.* TO 'cejuser'@'localhost';
FLUSH PRIVILEGES;
--disable_warnings
DROP DATABASE IF EXISTS mdev25080;
--enable_warnings
CREATE DATABASE mdev25080;
USE mdev25080;
CREATE TABLE t1 (a varchar(30)) ENGINE=ColumnStore;
CREATE TABLE t2 (a varchar(30)) ENGINE=ColumnStore;
CREATE TABLE t3 (a varchar(30)) ENGINE=MyISAM;
CREATE TABLE t4 (a varchar(30)) ENGINE=MyISAM;
INSERT INTO t1 VALUES ('abc'), ('bcd'), ('cde');
INSERT INTO t2 VALUES ('bcd'), ('cde'), ('def'), ('efg');
INSERT INTO t3 VALUES ('t3_myisam1'), ('t3_myisam2'), ('t3_myisam3');
INSERT INTO t4 VALUES ('t4_myisam1'), ('t4_myisam2'), ('t4_myisam3');
--echo # Pushdown of the whole UNION
#SELECT * FROM t1 UNION SELECT * FROM t2;
EXPLAIN SELECT * FROM t1 UNION SELECT * FROM t2;
#SELECT * FROM t1 UNION ALL SELECT * FROM t2;
EXPLAIN SELECT * FROM t1 UNION ALL SELECT * FROM t2;
--echo # UNION with a foreign engine
#SELECT * FROM t1 UNION SELECT * FROM t3;
EXPLAIN SELECT * FROM t1 UNION SELECT * FROM t3;
--echo # More than two SELECTs in a UNIT:
#SELECT * FROM t1 UNION
# SELECT * FROM t2 UNION ALL
# SELECT * FROM t1;
EXPLAIN SELECT * FROM t1 UNION
SELECT * FROM t2 UNION ALL
SELECT * FROM t1;
#(SELECT * FROM t1 UNION
# SELECT * FROM t2) UNION ALL
# SELECT * FROM t1;
EXPLAIN (SELECT * FROM t1 UNION
SELECT * FROM t2) UNION ALL
SELECT * FROM t1;
#SELECT * FROM t1 UNION
# SELECT * FROM t2 UNION ALL
# SELECT * FROM t3 UNION
# SELECT * FROM t4;
EXPLAIN SELECT * FROM t1 UNION
SELECT * FROM t2 UNION ALL
SELECT * FROM t3 UNION
SELECT * FROM t4;
#(SELECT * FROM t1 UNION
# SELECT * FROM t2) UNION ALL
# (SELECT * FROM t3 UNION
# SELECT * FROM t4);
EXPLAIN (SELECT * FROM t1 UNION
SELECT * FROM t2) UNION ALL
(SELECT * FROM t3 UNION
SELECT * FROM t4);
#SELECT count(*) FROM t1 UNION
# SELECT count(*) FROM t2 UNION ALL
# SELECT count(*)+20 FROM t2 UNION
# SELECT count(*)+5 FROM t1;
EXPLAIN
SELECT count(*) FROM t1 UNION
SELECT count(*) FROM t2 UNION ALL
SELECT count(*)+20 FROM t2 UNION
SELECT count(*)+5 FROM t1;
--echo # UNION inside a derived table: the whole derived table must be pushed
SELECT a FROM
(SELECT a FROM t1 UNION ALL SELECT a FROM t2) q ORDER BY a;
EXPLAIN
SELECT a FROM
(SELECT a FROM t1 UNION ALL SELECT a FROM t2) q ORDER BY a;
SELECT a FROM
(SELECT a FROM t1 UNION ALL SELECT a FROM t3) q ORDER BY a;
EXPLAIN
SELECT a FROM
(SELECT a FROM t1 UNION ALL SELECT a FROM t3) q ORDER BY a;
--echo # Prepared statements
#PREPARE stmt FROM "SELECT * FROM t1 UNION
# SELECT * FROM t2";
#EXECUTE stmt;
#EXECUTE stmt;
#EXECUTE stmt;
PREPARE stmt FROM "EXPLAIN SELECT * FROM t1 UNION
SELECT * FROM t2";
EXECUTE stmt;
EXECUTE stmt;
#PREPARE stmt FROM "(SELECT * FROM t1 UNION
# SELECT * FROM t2) UNION ALL
# (SELECT * FROM t1 UNION
# SELECT * FROM t2)";
#EXECUTE stmt;
#EXECUTE stmt;
#EXECUTE stmt;
PREPARE stmt FROM "EXPLAIN (SELECT * FROM t1 UNION
SELECT * FROM t2) UNION ALL
(SELECT * FROM t1 UNION
SELECT * FROM t2)";
EXECUTE stmt;
EXECUTE stmt;
DROP USER 'cejuser'@'localhost';
DROP TABLE t1, t2, t3, t4;
DROP DATABASE mdev25080;

View File

@ -12,6 +12,9 @@ USE mcs_union;
--echo # --echo #
--echo # MCOL-4700 Wrong result of a UNION for INT and INT UNSIGNED --echo # MCOL-4700 Wrong result of a UNION for INT and INT UNSIGNED
--echo # --echo #
--echo # Move the UNIONs from the subqueries to outer selects and add
--echo # ORDER BY clause after MCOL-5222 is fixed
--echo #
CREATE TABLE t1 (a INT, b INT UNSIGNED); CREATE TABLE t1 (a INT, b INT UNSIGNED);
INSERT INTO t1 VALUES (-1, 1), (-1, 1), (-2, 2); INSERT INTO t1 VALUES (-1, 1), (-1, 1), (-2, 2);
@ -273,4 +276,25 @@ SELECT * FROM (SELECT a FROM t1 UNION ALL SELECT a FROM t2) tu ORDER BY a;
SELECT * FROM (SELECT a FROM t2 UNION ALL SELECT a FROM t1) tu ORDER BY a; SELECT * FROM (SELECT a FROM t2 UNION ALL SELECT a FROM t1) tu ORDER BY a;
DROP TABLE t1,t2; DROP TABLE t1,t2;
--echo #
--echo # Union of tables containing different string data types
--echo #
--echo # Uncomment the actual SELECTs and add ORDER BY clause
--echo # after MCOL-5222 is fixed
--echo #
CREATE TABLE t1 (a CHAR(6));
INSERT INTO t1 VALUES ('t13abc'), ('t13xx'), ('common');
CREATE TABLE t2 (a VARCHAR(8));
INSERT INTO t2 VALUES ('t14abcde'), ('t14xyzzz'), ('common');
#SELECT * FROM t1 UNION SELECT * FROM t2;
EXPLAIN SELECT * FROM t1 UNION SELECT * FROM t2;
#SELECT * FROM t2 UNION ALL SELECT * FROM t1;
EXPLAIN SELECT * FROM t2 UNION ALL SELECT * FROM t1;
#SELECT * FROM t1 UNION SELECT * FROM t2 UNION SELECT '123456789000';
EXPLAIN SELECT * FROM t1 UNION SELECT * FROM t2 UNION SELECT '123456789000';
#SELECT * FROM t1 UNION SELECT '123456789000' UNION SELECT * FROM t2;
EXPLAIN SELECT * FROM t1 UNION SELECT '123456789000' UNION SELECT * FROM t2;
DROP TABLE t1,t2;
DROP DATABASE mcs_union; DROP DATABASE mcs_union;