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

MCOL-1482 An UPDATE operation on a non-ColumnStore table involving a

cross-engine join with a ColumnStore table errors out.

ColumnStore cannot directly update a foreign table. We detect whether
a multi-table UPDATE operation is performed on a foreign table, if so,
do not create the select_handler and let the server execute the UPDATE
operation instead.
This commit is contained in:
Gagan Goel
2021-06-10 10:48:12 +00:00
parent 28fd12a008
commit 49255f5cbd
6 changed files with 180 additions and 71 deletions

View File

@ -6325,6 +6325,48 @@ bool isMCSTable(TABLE* table_ptr)
return false; return false;
} }
bool isForeignTableUpdate(THD* thd)
{
LEX* lex = thd->lex;
if (lex->sql_command != SQLCOM_UPDATE_MULTI)
return false;
Item_field* item;
List_iterator_fast<Item> field_it(lex->first_select_lex()->item_list);
while ((item = (Item_field*) field_it++))
{
if (item->field && item->field->table && !isMCSTable(item->field->table))
return true;
}
return false;
}
// This function is different from isForeignTableUpdate()
// above as it only checks if any of the tables involved
// in the multi-table update statement is a foreign table,
// irrespective of whether the update is performed on the
// foreign table or not, as in isForeignTableUpdate().
bool isUpdateHasForeignTable(THD* thd)
{
LEX* lex = thd->lex;
if (lex->sql_command != SQLCOM_UPDATE_MULTI)
return false;
TABLE_LIST* table_ptr = lex->first_select_lex()->get_table_list();
for (; table_ptr; table_ptr = table_ptr->next_local)
{
if (table_ptr->table && !isMCSTable(table_ptr->table))
return true;
}
return false;
}
/*@brief set some runtime params to run the query */ /*@brief set some runtime params to run the query */
/*********************************************************** /***********************************************************
* DESCRIPTION: * DESCRIPTION:
@ -6576,10 +6618,7 @@ int processWhere(SELECT_LEX &select_lex,
else if (select_lex.where) else if (select_lex.where)
icp = select_lex.where; icp = select_lex.where;
} }
else if (!join && ( ((gwi.thd->lex)->sql_command == SQLCOM_UPDATE ) || else if (!join && isUpdateOrDeleteStatement(gwi.thd->lex->sql_command))
((gwi.thd->lex)->sql_command == SQLCOM_DELETE ) ||
((gwi.thd->lex)->sql_command == SQLCOM_UPDATE_MULTI ) ||
((gwi.thd->lex)->sql_command == SQLCOM_DELETE_MULTI )))
{ {
isUpdateDelete = true; isUpdateDelete = true;
} }
@ -7392,7 +7431,7 @@ int getSelectPlan(gp_walk_info& gwi, SELECT_LEX& select_lex,
} }
//@Bug 3030 Add error check for dml statement //@Bug 3030 Add error check for dml statement
if ( ((gwi.thd->lex)->sql_command == SQLCOM_UPDATE ) || ((gwi.thd->lex)->sql_command == SQLCOM_DELETE ) || ((gwi.thd->lex)->sql_command == SQLCOM_UPDATE_MULTI ) || ((gwi.thd->lex)->sql_command == SQLCOM_DELETE_MULTI ) ) if (isUpdateOrDeleteStatement(gwi.thd->lex->sql_command))
{ {
if ( after_size - before_size != 0 ) if ( after_size - before_size != 0 )
{ {
@ -7426,7 +7465,7 @@ int getSelectPlan(gp_walk_info& gwi, SELECT_LEX& select_lex,
case REAL_RESULT: case REAL_RESULT:
case TIME_RESULT: case TIME_RESULT:
{ {
if ( ((gwi.thd->lex)->sql_command == SQLCOM_UPDATE ) || ((gwi.thd->lex)->sql_command == SQLCOM_DELETE ) || ((gwi.thd->lex)->sql_command == SQLCOM_UPDATE_MULTI ) || ((gwi.thd->lex)->sql_command == SQLCOM_DELETE_MULTI )) if (isUpdateOrDeleteStatement(gwi.thd->lex->sql_command))
{ } { }
else else
{ {
@ -7458,7 +7497,7 @@ int getSelectPlan(gp_walk_info& gwi, SELECT_LEX& select_lex,
case Item::NULL_ITEM: case Item::NULL_ITEM:
{ {
if ( ((gwi.thd->lex)->sql_command == SQLCOM_UPDATE ) || ((gwi.thd->lex)->sql_command == SQLCOM_DELETE ) || ((gwi.thd->lex)->sql_command == SQLCOM_UPDATE_MULTI ) || ((gwi.thd->lex)->sql_command == SQLCOM_DELETE_MULTI ) ) if (isUpdateOrDeleteStatement(gwi.thd->lex->sql_command))
{ } { }
else else
{ {
@ -9258,7 +9297,7 @@ int getGroupPlan(gp_walk_info& gwi, SELECT_LEX& select_lex, SCSEP& csep, cal_gro
} }
//@Bug 3030 Add error check for dml statement //@Bug 3030 Add error check for dml statement
if ( ((gwi.thd->lex)->sql_command == SQLCOM_UPDATE ) || ((gwi.thd->lex)->sql_command == SQLCOM_DELETE ) || ((gwi.thd->lex)->sql_command == SQLCOM_UPDATE_MULTI ) || ((gwi.thd->lex)->sql_command == SQLCOM_DELETE_MULTI ) ) if (isUpdateOrDeleteStatement(gwi.thd->lex->sql_command))
{ {
if ( after_size - before_size != 0 ) if ( after_size - before_size != 0 )
{ {
@ -9289,7 +9328,7 @@ int getGroupPlan(gp_walk_info& gwi, SELECT_LEX& select_lex, SCSEP& csep, cal_gro
case REAL_RESULT: case REAL_RESULT:
case TIME_RESULT: case TIME_RESULT:
{ {
if ( ((gwi.thd->lex)->sql_command == SQLCOM_UPDATE ) || ((gwi.thd->lex)->sql_command == SQLCOM_DELETE ) || ((gwi.thd->lex)->sql_command == SQLCOM_UPDATE_MULTI ) || ((gwi.thd->lex)->sql_command == SQLCOM_DELETE_MULTI )) if (isUpdateOrDeleteStatement(gwi.thd->lex->sql_command))
{ } { }
else else
{ {
@ -9321,7 +9360,7 @@ int getGroupPlan(gp_walk_info& gwi, SELECT_LEX& select_lex, SCSEP& csep, cal_gro
case Item::NULL_ITEM: case Item::NULL_ITEM:
{ {
if ( ((gwi.thd->lex)->sql_command == SQLCOM_UPDATE ) || ((gwi.thd->lex)->sql_command == SQLCOM_DELETE ) || ((gwi.thd->lex)->sql_command == SQLCOM_UPDATE_MULTI ) || ((gwi.thd->lex)->sql_command == SQLCOM_DELETE_MULTI ) ) if (isUpdateOrDeleteStatement(gwi.thd->lex->sql_command))
{ } { }
else else
{ {

View File

@ -854,7 +854,7 @@ uint32_t doUpdateDelete(THD* thd, gp_walk_info& gwi, const std::vector<COND*>& c
{ {
Message::Args args; Message::Args args;
if (((thd->lex)->sql_command == SQLCOM_UPDATE) || ((thd->lex)->sql_command == SQLCOM_UPDATE_MULTI)) if (isUpdateStatement(thd->lex->sql_command))
args.add("Update"); args.add("Update");
#if 0 #if 0
else if (thd->rgi_slave && thd->rgi_slave->m_table_map.count() != 0) else if (thd->rgi_slave && thd->rgi_slave->m_table_map.count() != 0)
@ -909,7 +909,7 @@ uint32_t doUpdateDelete(THD* thd, gp_walk_info& gwi, const std::vector<COND*>& c
updateCP->isDML(true); updateCP->isDML(true);
//@Bug 2753. the memory already freed by destructor of UpdateSqlStatement //@Bug 2753. the memory already freed by destructor of UpdateSqlStatement
if (((thd->lex)->sql_command == SQLCOM_UPDATE) || ((thd->lex)->sql_command == SQLCOM_UPDATE_MULTI)) if (isUpdateStatement(thd->lex->sql_command))
{ {
ColumnAssignment* columnAssignmentPtr; ColumnAssignment* columnAssignmentPtr;
Item_field* item; Item_field* item;
@ -1202,8 +1202,7 @@ uint32_t doUpdateDelete(THD* thd, gp_walk_info& gwi, const std::vector<COND*>& c
// Exit early if there is nothing to update // Exit early if there is nothing to update
if (colAssignmentListPtr->empty() && if (colAssignmentListPtr->empty() &&
(((thd->lex)->sql_command == SQLCOM_UPDATE) || isUpdateStatement(thd->lex->sql_command))
((thd->lex)->sql_command == SQLCOM_UPDATE_MULTI)))
{ {
ci->affectedRows = 0; ci->affectedRows = 0;
return 0; return 0;
@ -1216,7 +1215,7 @@ uint32_t doUpdateDelete(THD* thd, gp_walk_info& gwi, const std::vector<COND*>& c
CalpontSystemCatalog::TableName aTableName; CalpontSystemCatalog::TableName aTableName;
TABLE_LIST* first_table = 0; TABLE_LIST* first_table = 0;
if (( (thd->lex)->sql_command == SQLCOM_UPDATE ) || ( (thd->lex)->sql_command == SQLCOM_UPDATE_MULTI ) ) if (isUpdateStatement(thd->lex->sql_command))
{ {
aTableName.schema = schemaName; aTableName.schema = schemaName;
aTableName.table = tableName; aTableName.table = tableName;
@ -1238,10 +1237,10 @@ uint32_t doUpdateDelete(THD* thd, gp_walk_info& gwi, const std::vector<COND*>& c
IDEBUG( cout << "STMT: " << dmlStmt << " and sessionID " << thd->thread_id << endl ); IDEBUG( cout << "STMT: " << dmlStmt << " and sessionID " << thd->thread_id << endl );
VendorDMLStatement dmlStatement(dmlStmt, sessionID); VendorDMLStatement dmlStatement(dmlStmt, sessionID);
if (( (thd->lex)->sql_command == SQLCOM_UPDATE ) || ( (thd->lex)->sql_command == SQLCOM_UPDATE_MULTI ) ) if (isUpdateStatement(thd->lex->sql_command))
dmlStatement.set_DMLStatementType( DML_UPDATE ); dmlStatement.set_DMLStatementType(DML_UPDATE);
else else
dmlStatement.set_DMLStatementType( DML_DELETE ); dmlStatement.set_DMLStatementType(DML_DELETE);
TableName* qualifiedTablName = new TableName(); TableName* qualifiedTablName = new TableName();
@ -1249,7 +1248,7 @@ uint32_t doUpdateDelete(THD* thd, gp_walk_info& gwi, const std::vector<COND*>& c
//@Bug 2753. To make sure the momory is freed. //@Bug 2753. To make sure the momory is freed.
updateStmt.fColAssignmentListPtr = colAssignmentListPtr; updateStmt.fColAssignmentListPtr = colAssignmentListPtr;
if (( (thd->lex)->sql_command == SQLCOM_UPDATE ) || ( (thd->lex)->sql_command == SQLCOM_UPDATE_MULTI ) ) if (isUpdateStatement(thd->lex->sql_command))
{ {
qualifiedTablName->fName = tableName; qualifiedTablName->fName = tableName;
qualifiedTablName->fSchema = schemaName; qualifiedTablName->fSchema = schemaName;
@ -1343,7 +1342,7 @@ uint32_t doUpdateDelete(THD* thd, gp_walk_info& gwi, const std::vector<COND*>& c
List<Item> items; List<Item> items;
SELECT_LEX select_lex; SELECT_LEX select_lex;
if (( (thd->lex)->sql_command == SQLCOM_UPDATE ) || ( (thd->lex)->sql_command == SQLCOM_UPDATE_MULTI ) ) if (isUpdateStatement(thd->lex->sql_command))
{ {
items = (thd->lex->first_select_lex()->item_list); items = (thd->lex->first_select_lex()->item_list);
thd->lex->first_select_lex()->item_list = thd->lex->value_list; thd->lex->first_select_lex()->item_list = thd->lex->value_list;
@ -1492,7 +1491,7 @@ uint32_t doUpdateDelete(THD* thd, gp_walk_info& gwi, const std::vector<COND*>& c
returnedCols.push_back((updateCP->columnMap()).begin()->second); returnedCols.push_back((updateCP->columnMap()).begin()->second);
//@Bug 6123. get the correct returned columnlist //@Bug 6123. get the correct returned columnlist
if (( (thd->lex)->sql_command == SQLCOM_DELETE ) || ( (thd->lex)->sql_command == SQLCOM_DELETE_MULTI ) ) if (isDeleteStatement(thd->lex->sql_command))
{ {
returnedCols.clear(); returnedCols.clear();
//choose the smallest column to project //choose the smallest column to project
@ -1543,7 +1542,7 @@ uint32_t doUpdateDelete(THD* thd, gp_walk_info& gwi, const std::vector<COND*>& c
updateCP->returnedCols( returnedCols ); updateCP->returnedCols( returnedCols );
if (( (thd->lex)->sql_command == SQLCOM_UPDATE ) || ( (thd->lex)->sql_command == SQLCOM_UPDATE_MULTI ) ) if (isUpdateStatement(thd->lex->sql_command))
{ {
const ParseTree* ptsub = updateCP->filters(); const ParseTree* ptsub = updateCP->filters();
@ -1561,8 +1560,7 @@ uint32_t doUpdateDelete(THD* thd, gp_walk_info& gwi, const std::vector<COND*>& c
ptsub->walk(makeUpdateSemiJoin, &tbList[0] ); ptsub->walk(makeUpdateSemiJoin, &tbList[0] );
} }
if (( (thd->lex)->sql_command == SQLCOM_UPDATE ) || ( (thd->lex)->sql_command == SQLCOM_UPDATE_MULTI ) ) thd->lex->first_select_lex()->item_list = items;
thd->lex->first_select_lex()->item_list = items;
} }
} }
@ -2039,10 +2037,7 @@ int ha_mcs::impl_rnd_init(TABLE* table, const std::vector<COND*>& condStack)
UPDATE innotab1 SET a=100 WHERE a NOT IN (SELECT a FROM cstab1 WHERE a=1); UPDATE innotab1 SET a=100 WHERE a NOT IN (SELECT a FROM cstab1 WHERE a=1);
*/ */
if (!isReadOnly() && // make sure the current table is being modified if (!isReadOnly() && // make sure the current table is being modified
(thd->lex->sql_command == SQLCOM_UPDATE || isUpdateOrDeleteStatement(thd->lex->sql_command))
thd->lex->sql_command == SQLCOM_DELETE ||
thd->lex->sql_command == SQLCOM_DELETE_MULTI ||
thd->lex->sql_command == SQLCOM_UPDATE_MULTI))
return doUpdateDelete(thd, gwi, condStack); return doUpdateDelete(thd, gwi, condStack);
uint32_t sessionID = tid2sid(thd->thread_id); uint32_t sessionID = tid2sid(thd->thread_id);
@ -2372,8 +2367,10 @@ int ha_mcs_impl_rnd_next(uchar* buf, TABLE* table)
thd->lex->sql_command == SQLCOM_LOAD)) thd->lex->sql_command == SQLCOM_LOAD))
return HA_ERR_END_OF_FILE; return HA_ERR_END_OF_FILE;
if (((thd->lex)->sql_command == SQLCOM_UPDATE) || ((thd->lex)->sql_command == SQLCOM_DELETE) || if ((thd->lex->sql_command == SQLCOM_UPDATE) ||
((thd->lex)->sql_command == SQLCOM_DELETE_MULTI) || ((thd->lex)->sql_command == SQLCOM_UPDATE_MULTI)) (thd->lex->sql_command == SQLCOM_DELETE) ||
(thd->lex->sql_command == SQLCOM_DELETE_MULTI) ||
((thd->lex->sql_command == SQLCOM_UPDATE_MULTI) && !isForeignTableUpdate(thd)))
return HA_ERR_END_OF_FILE; return HA_ERR_END_OF_FILE;
// @bug 2547 // @bug 2547
@ -2461,22 +2458,15 @@ int ha_mcs_impl_rnd_end(TABLE* table, bool is_pushdown_hand)
thd->lex->sql_command == SQLCOM_LOAD)) thd->lex->sql_command == SQLCOM_LOAD))
return 0; return 0;
// MCOL-2178 isUnion member only assigned, never used
// if (is_pushdown_hand)
// {
// MIGR::infinidb_vtable.isUnion = false;
// }
if (get_fe_conn_info_ptr() != NULL) if (get_fe_conn_info_ptr() != NULL)
ci = reinterpret_cast<cal_connection_info*>(get_fe_conn_info_ptr()); ci = reinterpret_cast<cal_connection_info*>(get_fe_conn_info_ptr());
if ( (thd->lex)->sql_command == SQLCOM_ALTER_TABLE ) if ( (thd->lex)->sql_command == SQLCOM_ALTER_TABLE )
return rc; return rc;
if (((thd->lex)->sql_command == SQLCOM_UPDATE) || if ((thd->lex->sql_command == SQLCOM_UPDATE) ||
((thd->lex)->sql_command == SQLCOM_DELETE) || (thd->lex->sql_command == SQLCOM_DELETE) ||
((thd->lex)->sql_command == SQLCOM_DELETE_MULTI) || (thd->lex->sql_command == SQLCOM_DELETE_MULTI) ||
((thd->lex)->sql_command == SQLCOM_UPDATE_MULTI)) ((thd->lex->sql_command == SQLCOM_UPDATE_MULTI) && !isForeignTableUpdate(thd)))
return rc; return rc;
if (!ci) if (!ci)
@ -3668,10 +3658,7 @@ COND* ha_mcs_impl_cond_push(COND* cond, TABLE* table, std::vector<COND*>& condSt
{ {
THD* thd = current_thd; THD* thd = current_thd;
if (((thd->lex)->sql_command == SQLCOM_UPDATE) || if (isUpdateOrDeleteStatement(thd->lex->sql_command))
((thd->lex)->sql_command == SQLCOM_UPDATE_MULTI) ||
((thd->lex)->sql_command == SQLCOM_DELETE) ||
((thd->lex)->sql_command == SQLCOM_DELETE_MULTI))
{ {
condStack.push_back(cond); condStack.push_back(cond);
return nullptr; return nullptr;
@ -3921,7 +3908,6 @@ int ha_mcs_impl_group_by_init(mcs_handler_info *handler_info, TABLE* table)
idbassert(ci != 0); idbassert(ci != 0);
if (thd->killed == KILL_QUERY || thd->killed == KILL_QUERY_HARD) if (thd->killed == KILL_QUERY || thd->killed == KILL_QUERY_HARD)
{ {
force_close_fep_conn(thd, ci); force_close_fep_conn(thd, ci);
@ -4326,15 +4312,12 @@ int ha_mcs_impl_group_by_next(TABLE* table)
{ {
THD* thd = current_thd; THD* thd = current_thd;
if (((thd->lex)->sql_command == SQLCOM_UPDATE) || ((thd->lex)->sql_command == SQLCOM_DELETE) || if ((thd->lex->sql_command == SQLCOM_UPDATE) ||
((thd->lex)->sql_command == SQLCOM_DELETE_MULTI) || ((thd->lex)->sql_command == SQLCOM_UPDATE_MULTI)) (thd->lex->sql_command == SQLCOM_DELETE) ||
(thd->lex->sql_command == SQLCOM_DELETE_MULTI) ||
(thd->lex->sql_command == SQLCOM_UPDATE_MULTI && !isForeignTableUpdate(thd)))
return HA_ERR_END_OF_FILE; return HA_ERR_END_OF_FILE;
// @bug 2547
// MCOL-2178
// if (MIGR::infinidb_vtable.impossibleWhereOnUnion)
// return HA_ERR_END_OF_FILE;
if (get_fe_conn_info_ptr() == nullptr) if (get_fe_conn_info_ptr() == nullptr)
set_fe_conn_info_ptr((void*)new cal_connection_info()); set_fe_conn_info_ptr((void*)new cal_connection_info());
@ -4426,9 +4409,6 @@ int ha_mcs_impl_group_by_end(TABLE* table)
thd->lex->sql_command == SQLCOM_LOAD)) thd->lex->sql_command == SQLCOM_LOAD))
return 0; return 0;
// MCOL-2178 isUnion member only assigned, never used
// MIGR::infinidb_vtable.isUnion = false;
if (get_fe_conn_info_ptr() != NULL) if (get_fe_conn_info_ptr() != NULL)
ci = reinterpret_cast<cal_connection_info*>(get_fe_conn_info_ptr()); ci = reinterpret_cast<cal_connection_info*>(get_fe_conn_info_ptr());
@ -4638,8 +4618,8 @@ int ha_mcs_impl_pushdown_init(mcs_handler_info* handler_info, TABLE* table)
return 0; return 0;
// MCOL-4023 We need to test this code path. // MCOL-4023 We need to test this code path.
//Update and delete code // Update and delete code
if ( ((thd->lex)->sql_command == SQLCOM_UPDATE) || ((thd->lex)->sql_command == SQLCOM_DELETE) || ((thd->lex)->sql_command == SQLCOM_DELETE_MULTI) || ((thd->lex)->sql_command == SQLCOM_UPDATE_MULTI)) if (isUpdateOrDeleteStatement(thd->lex->sql_command))
return doUpdateDelete(thd, gwi, std::vector<COND*>()); return doUpdateDelete(thd, gwi, std::vector<COND*>());
uint32_t sessionID = tid2sid(thd->thread_id); uint32_t sessionID = tid2sid(thd->thread_id);
@ -5059,8 +5039,7 @@ int ha_mcs_impl_select_next(uchar* buf, TABLE* table)
thd->lex->sql_command == SQLCOM_LOAD)) thd->lex->sql_command == SQLCOM_LOAD))
return HA_ERR_END_OF_FILE; return HA_ERR_END_OF_FILE;
if (((thd->lex)->sql_command == SQLCOM_UPDATE) || ((thd->lex)->sql_command == SQLCOM_DELETE) || if (isUpdateOrDeleteStatement(thd->lex->sql_command))
((thd->lex)->sql_command == SQLCOM_DELETE_MULTI) || ((thd->lex)->sql_command == SQLCOM_UPDATE_MULTI))
return rc; return rc;
// @bug 2547 // @bug 2547

View File

@ -366,6 +366,8 @@ void gp_walk(const Item* item, void* arg);
void parse_item (Item* item, std::vector<Item_field*>& field_vec, bool& hasNonSupportItem, uint16& parseInfo, gp_walk_info* gwip = NULL); void parse_item (Item* item, std::vector<Item_field*>& field_vec, bool& hasNonSupportItem, uint16& parseInfo, gp_walk_info* gwip = NULL);
const std::string bestTableName(const Item_field* ifp); const std::string bestTableName(const Item_field* ifp);
bool isMCSTable(TABLE* table_ptr); bool isMCSTable(TABLE* table_ptr);
bool isForeignTableUpdate(THD* thd);
bool isUpdateHasForeignTable(THD* thd);
// execution plan util functions prototypes // execution plan util functions prototypes
execplan::ReturnedColumn* buildReturnedColumn(Item* item, gp_walk_info& gwi, bool& nonSupport, bool isRefItem = false); execplan::ReturnedColumn* buildReturnedColumn(Item* item, gp_walk_info& gwi, bool& nonSupport, bool isRefItem = false);
@ -409,6 +411,24 @@ bool buildEqualityPredicate(execplan::ReturnedColumn* lhs,
const std::vector<Item*>& itemList, const std::vector<Item*>& itemList,
bool isInSubs = false); bool isInSubs = false);
inline bool isUpdateStatement(const enum_sql_command& command)
{
return (command == SQLCOM_UPDATE) ||
(command == SQLCOM_UPDATE_MULTI);
}
inline bool isDeleteStatement(const enum_sql_command& command)
{
return (command == SQLCOM_DELETE) ||
(command == SQLCOM_DELETE_MULTI);
}
inline bool isUpdateOrDeleteStatement(const enum_sql_command& command)
{
return isUpdateStatement(command) ||
isDeleteStatement(command);
}
#ifdef DEBUG_WALK_COND #ifdef DEBUG_WALK_COND
void debug_walk(const Item* item, void* arg); void debug_walk(const Item* item, void* arg);
#endif #endif

View File

@ -498,6 +498,13 @@ create_columnstore_derived_handler(THD* thd, TABLE_LIST *table_ptr)
if (thd->stmt_arena && thd->stmt_arena->is_stmt_execute()) if (thd->stmt_arena && thd->stmt_arena->is_stmt_execute())
return handler; return handler;
// MCOL-1482 Disable derived_handler if the multi-table update
// statement contains a non-columnstore table.
if (cal_impl_if::isUpdateHasForeignTable(thd))
{
return handler;
}
SELECT_LEX_UNIT *unit = table_ptr->derived; SELECT_LEX_UNIT *unit = table_ptr->derived;
SELECT_LEX *sl = unit->first_select(); SELECT_LEX *sl = unit->first_select();
@ -754,6 +761,14 @@ create_columnstore_select_handler(THD* thd, SELECT_LEX* select_lex)
return nullptr; return nullptr;
} }
// MCOL-1482 Disable select_handler for a multi-table update
// with a non-columnstore table as the target table of the update
// operation.
if (cal_impl_if::isForeignTableUpdate(thd))
{
return nullptr;
}
// Flag to indicate if this is a prepared statement // Flag to indicate if this is a prepared statement
bool isPS = thd->stmt_arena && thd->stmt_arena->is_stmt_execute(); bool isPS = thd->stmt_arena && thd->stmt_arena->is_stmt_execute();

View File

@ -6,6 +6,7 @@ GRANT ALL PRIVILEGES ON *.* TO 'cejuser'@'localhost';
FLUSH PRIVILEGES; FLUSH PRIVILEGES;
CREATE TABLE t1 (t1_int INT, t1_char CHAR(5))ENGINE=Innodb; CREATE TABLE t1 (t1_int INT, t1_char CHAR(5))ENGINE=Innodb;
CREATE TABLE t2 (t2_int INT, t2_char CHAR(5))ENGINE=Columnstore; CREATE TABLE t2 (t2_int INT, t2_char CHAR(5))ENGINE=Columnstore;
CREATE VIEW v1 AS SELECT * FROM t1;
INSERT INTO t1 VALUES (NULL,''),(1,'aaa'),(2,'bbb'),(3,'ccc'),(4,'ddd'),(5,'eee'),(6,'ffff'),(7,'ggggg'); INSERT INTO t1 VALUES (NULL,''),(1,'aaa'),(2,'bbb'),(3,'ccc'),(4,'ddd'),(5,'eee'),(6,'ffff'),(7,'ggggg');
INSERT INTO t2 VALUES (NULL,''),(1,'hhhh'),(3,'iii'),(5,'jjj'),(7,'kkkk'),(9,'lll'),(11,'m'),(13,'nnn'); INSERT INTO t2 VALUES (NULL,''),(1,'hhhh'),(3,'iii'),(5,'jjj'),(7,'kkkk'),(9,'lll'),(11,'m'),(13,'nnn');
SELECT * FROM t1, t2 WHERE t1.t1_int = t2.t2_int AND t1.t1_int = 3; SELECT * FROM t1, t2 WHERE t1.t1_int = t2.t2_int AND t1.t1_int = 3;
@ -23,17 +24,38 @@ t1_int t1_char t2_int t2_char
5 eee 5 sssss 5 eee 5 sssss
7 ggggg 7 sssss 7 ggggg 7 sssss
UPDATE t1 JOIN t2 on t1.t1_int=t2.t2_int SET t1.t1_char='s'; UPDATE t1 JOIN t2 on t1.t1_int=t2.t2_int SET t1.t1_char='s';
ERROR HY000: Internal error: IDB-2006: 'mcs45_db.t1' does not exist in Columnstore.
SELECT * FROM t1; SELECT * FROM t1;
t1_int t1_char t1_int t1_char
NULL NULL
1 aaa 1 s
2 bbb 2 bbb
3 ccc 3 s
4 ddd 4 ddd
5 eee 5 s
6 ffff 6 ffff
7 ggggg 7 s
UPDATE v1 JOIN t2 on v1.t1_int=t2.t2_int SET v1.t1_char='t';
SELECT * FROM t1;
t1_int t1_char
NULL
1 t
2 bbb
3 t
4 ddd
5 t
6 ffff
7 t
UPDATE t1 JOIN (SELECT * FROM t2 WHERE t2_int < 5) d_t2 on t1.t1_int=d_t2.t2_int SET t1.t1_char='u';
SELECT * FROM t1;
t1_int t1_char
NULL
1 u
2 bbb
3 u
4 ddd
5 t
6 ffff
7 t
SELECT * FROM t2; SELECT * FROM t2;
t2_int t2_char t2_int t2_char
NULL NULL NULL NULL
@ -48,13 +70,13 @@ DELETE t2 FROM t2 JOIN t1 ON t1.t1_int = t2.t2_int AND t1.t1_int = 7;
SELECT * FROM t1; SELECT * FROM t1;
t1_int t1_char t1_int t1_char
NULL NULL
1 aaa 1 u
2 bbb 2 bbb
3 ccc 3 u
4 ddd 4 ddd
5 eee 5 t
6 ffff 6 ffff
7 ggggg 7 t
SELECT * FROM t2; SELECT * FROM t2;
t2_int t2_char t2_int t2_char
NULL NULL NULL NULL
@ -66,5 +88,21 @@ NULL NULL
13 nnn 13 nnn
DELETE t1 FROM t1 JOIN t2 ON t1.t1_int = t2.t2_int AND t1.t1_int = 5; DELETE t1 FROM t1 JOIN t2 ON t1.t1_int = t2.t2_int AND t1.t1_int = 5;
ERROR HY000: Internal error: IDB-2006: 'mcs45_db.t1' does not exist in Columnstore. ERROR HY000: Internal error: IDB-2006: 'mcs45_db.t1' does not exist in Columnstore.
CREATE TABLE mcs(a INT, b INT)ENGINE=Columnstore;
CREATE TABLE idb(a INT, b INT)ENGINE=Innodb;
INSERT INTO mcs(a,b) VALUES (1,2),(2,3),(4,5);
INSERT INTO idb(a,b) VALUES (1,2),(2,3),(4,5);
UPDATE idb dest JOIN mcs src ON dest.a=src.a SET dest.b = dest.b + src.b + 100;
SELECT * FROM idb;
a b
1 104
2 106
4 110
UPDATE idb dest JOIN mcs src ON dest.a=src.a SET dest.b = dest.b + src.b + 100;
SELECT * FROM idb;
a b
1 206
2 209
4 215
DROP USER 'cejuser'@'localhost'; DROP USER 'cejuser'@'localhost';
DROP DATABASE mcs45_db; DROP DATABASE mcs45_db;

View File

@ -38,6 +38,7 @@ FLUSH PRIVILEGES;
# Create tables with Innodb and Columnstore engines # Create tables with Innodb and Columnstore engines
CREATE TABLE t1 (t1_int INT, t1_char CHAR(5))ENGINE=Innodb; CREATE TABLE t1 (t1_int INT, t1_char CHAR(5))ENGINE=Innodb;
CREATE TABLE t2 (t2_int INT, t2_char CHAR(5))ENGINE=Columnstore; CREATE TABLE t2 (t2_int INT, t2_char CHAR(5))ENGINE=Columnstore;
CREATE VIEW v1 AS SELECT * FROM t1;
INSERT INTO t1 VALUES (NULL,''),(1,'aaa'),(2,'bbb'),(3,'ccc'),(4,'ddd'),(5,'eee'),(6,'ffff'),(7,'ggggg'); INSERT INTO t1 VALUES (NULL,''),(1,'aaa'),(2,'bbb'),(3,'ccc'),(4,'ddd'),(5,'eee'),(6,'ffff'),(7,'ggggg');
INSERT INTO t2 VALUES (NULL,''),(1,'hhhh'),(3,'iii'),(5,'jjj'),(7,'kkkk'),(9,'lll'),(11,'m'),(13,'nnn'); INSERT INTO t2 VALUES (NULL,''),(1,'hhhh'),(3,'iii'),(5,'jjj'),(7,'kkkk'),(9,'lll'),(11,'m'),(13,'nnn');
@ -46,19 +47,36 @@ UPDATE t2, t1 SET t2.t2_char = 'zzz' WHERE t1.t1_int = t2.t2_int AND t1.t1_int =
SELECT * FROM t1, t2 WHERE t1.t1_int = t2.t2_int AND t1.t1_int = 3; SELECT * FROM t1, t2 WHERE t1.t1_int = t2.t2_int AND t1.t1_int = 3;
UPDATE t1 JOIN t2 on t1.t1_int=t2.t2_int SET t2.t2_char='sssss'; UPDATE t1 JOIN t2 on t1.t1_int=t2.t2_int SET t2.t2_char='sssss';
SELECT * FROM t1, t2 WHERE t1.t1_int = t2.t2_int; SELECT * FROM t1, t2 WHERE t1.t1_int = t2.t2_int;
# MCOL-1482 # MCOL-1482
--error ER_INTERNAL_ERROR
UPDATE t1 JOIN t2 on t1.t1_int=t2.t2_int SET t1.t1_char='s'; UPDATE t1 JOIN t2 on t1.t1_int=t2.t2_int SET t1.t1_char='s';
SELECT * FROM t1;
# Update foreign view
UPDATE v1 JOIN t2 on v1.t1_int=t2.t2_int SET v1.t1_char='t';
SELECT * FROM t1;
# Update foreign table with a subquery as the source
UPDATE t1 JOIN (SELECT * FROM t2 WHERE t2_int < 5) d_t2 on t1.t1_int=d_t2.t2_int SET t1.t1_char='u';
SELECT * FROM t1; SELECT * FROM t1;
SELECT * FROM t2; SELECT * FROM t2;
DELETE t2 FROM t2 JOIN t1 ON t1.t1_int = t2.t2_int AND t1.t1_int = 7; DELETE t2 FROM t2 JOIN t1 ON t1.t1_int = t2.t2_int AND t1.t1_int = 7;
SELECT * FROM t1; SELECT * FROM t1;
SELECT * FROM t2; SELECT * FROM t2;
# Similar error as MCOL-1482 # Similar error as MCOL-1482, but for a foreign engine DELETE.
--error ER_INTERNAL_ERROR --error ER_INTERNAL_ERROR
DELETE t1 FROM t1 JOIN t2 ON t1.t1_int = t2.t2_int AND t1.t1_int = 5; DELETE t1 FROM t1 JOIN t2 ON t1.t1_int = t2.t2_int AND t1.t1_int = 5;
# Test case from MCOL-1482 issue description
CREATE TABLE mcs(a INT, b INT)ENGINE=Columnstore;
CREATE TABLE idb(a INT, b INT)ENGINE=Innodb;
INSERT INTO mcs(a,b) VALUES (1,2),(2,3),(4,5);
INSERT INTO idb(a,b) VALUES (1,2),(2,3),(4,5);
UPDATE idb dest JOIN mcs src ON dest.a=src.a SET dest.b = dest.b + src.b + 100;
SELECT * FROM idb;
UPDATE idb dest JOIN mcs src ON dest.a=src.a SET dest.b = dest.b + src.b + 100;
SELECT * FROM idb;
# Clean UP # Clean UP
DROP USER 'cejuser'@'localhost'; DROP USER 'cejuser'@'localhost';
DROP DATABASE mcs45_db; DROP DATABASE mcs45_db;