From 3eaca005ff48e84924601ecb92580a7adbaa6cfe Mon Sep 17 00:00:00 2001 From: Monty Date: Tue, 5 Dec 2017 17:05:05 +0200 Subject: [PATCH 01/32] Ensure that mysqladmin also works with MariaDB 10.3 + more Increase the number of status variabels that can be handled --- client/mysqladmin.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/mysqladmin.cc b/client/mysqladmin.cc index 45b332a6cd6..7e824cadf40 100644 --- a/client/mysqladmin.cc +++ b/client/mysqladmin.cc @@ -34,9 +34,9 @@ char *host= NULL, *user= 0, *opt_password= 0, *default_charset= (char*) MYSQL_AUTODETECT_CHARSET_NAME; -char truncated_var_names[MAX_MYSQL_VAR][MAX_TRUNC_LENGTH]; -char ex_var_names[MAX_MYSQL_VAR][FN_REFLEN]; -ulonglong last_values[MAX_MYSQL_VAR]; +char truncated_var_names[MAX_MYSQL_VAR+100][MAX_TRUNC_LENGTH]; +char ex_var_names[MAX_MYSQL_VAR+100][FN_REFLEN]; +ulonglong last_values[MAX_MYSQL_VAR+100]; static int interval=0; static my_bool option_force=0,interrupted=0,new_line=0, opt_compress= 0, opt_local= 0, opt_relative= 0, opt_verbose= 0, @@ -885,7 +885,7 @@ static int execute_commands(MYSQL *mysql,int argc, char **argv) return -1; } - DBUG_ASSERT(mysql_num_rows(res) < MAX_MYSQL_VAR); + DBUG_ASSERT(mysql_num_rows(res) < MAX_MYSQL_VAR+100); if (!opt_vertical) print_header(res); From 6d4b0958dc5a6a2d44dcccf083ec9e136bb4a38e Mon Sep 17 00:00:00 2001 From: Monty Date: Thu, 7 Dec 2017 11:41:52 +0200 Subject: [PATCH 02/32] Fix failing test mysql_client_test Backported patch from 10.3 that allows one to remove short .frm files --- sql/datadict.cc | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/sql/datadict.cc b/sql/datadict.cc index 5b70639174c..5a4d8bbb8bf 100644 --- a/sql/datadict.cc +++ b/sql/datadict.cc @@ -72,33 +72,36 @@ Table_type dd_frm_type(THD *thd, char *path, LEX_CSTRING *engine_name, if ((file= mysql_file_open(key_file_frm, path, O_RDONLY | O_SHARE, MYF(0))) < 0) DBUG_RETURN(TABLE_TYPE_UNKNOWN); - error= mysql_file_read(file, (uchar*) header, sizeof(header), MYF(MY_NABP)); - if (error) + /* + We return TABLE_TYPE_NORMAL if we can open the .frm file. This allows us + to drop a bad .frm file with DROP TABLE + */ + type= TABLE_TYPE_NORMAL; + + /* + Initialize engine name in case we are not able to find it out + The cast is safe, as engine_name->str points to a usable buffer. + */ + if (engine_name) + { + engine_name->length= 0; + ((char*) (engine_name->str))[0]= 0; + } + + if ((error= mysql_file_read(file, (uchar*) header, sizeof(header), MYF(MY_NABP)))) goto err; + if (!strncmp((char*) header, "TYPE=VIEW\n", 10)) { type= TABLE_TYPE_VIEW; goto err; } - /* - We return TABLE_TYPE_NORMAL if we can read the .frm file. This allows us - to drop a bad .frm file with DROP TABLE - */ - type= TABLE_TYPE_NORMAL; - /* engine_name is 0 if we only want to know if table is view or not */ if (!engine_name) goto err; - /* - Initialize engine name in case we are not able to find it out - The cast is safe, as engine_name->str points to a usable buffer. - */ - engine_name->length= 0; - ((char*) (engine_name->str))[0]= 0; - if (!is_binary_frm_header(header)) goto err; From da3a3a68df34c7fef387ce890d3925166edeef2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Lindstr=C3=B6m?= Date: Thu, 7 Dec 2017 12:26:29 +0200 Subject: [PATCH 03/32] MDEV-12837: WSREP: BF lock wait long Problem was a merge error from MySQL wsrep i.e. Galera. wsrep_on_check New check function. Galera can't be enabled if innodb-lock-schedule-algorithm=VATS. innobase_kill_query In Galera async kill we could own lock mutex. innobase_init If Variance-Aware-Transaction-Sheduling Algorithm (VATS) is used on Galera we fall back to First-Come-First-Served (FCFS) with notice to user. Changed innodb-lock-schedule-algorithm as read-only parameter as it was designed to be. lock_reset_lock_and_trx_wait Use ib::hex() to print out transaction ID. lock_rec_other_has_expl_req, lock_rec_other_has_conflicting, RecLock::add_to_waitq lock_rec_lock_slow lock_table_other_has_incompatible lock_rec_insert_check_and_lock lock_prdt_other_has_conflicting Change pointer to conflicting lock to normal pointer as this pointer contents could be changed later. RecLock::create Conclicting lock pointer is moved to last parameter with default value NULL. This conflicting transaction could be selected as victim in Galera if requesting transaction is BF (brute force) transaction. In this case contents of conflicting lock pointer will be changed. Use ib::hex() to print transaction ids. --- sql/sys_vars.cc | 3 +- sql/wsrep_var.cc | 16 +++ sql/wsrep_var.h | 3 +- storage/innobase/handler/ha_innodb.cc | 14 ++- storage/innobase/include/lock0priv.h | 19 ++-- storage/innobase/lock/lock0lock.cc | 140 +++++++++++++++----------- storage/innobase/lock/lock0prdt.cc | 10 +- storage/innobase/lock/lock0wait.cc | 34 +++++-- 8 files changed, 153 insertions(+), 86 deletions(-) diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index a32bc3fce8e..d53276d7c24 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -5005,7 +5005,8 @@ static Sys_var_mybool Sys_wsrep_on ( "wsrep_on", "To enable wsrep replication ", SESSION_VAR(wsrep_on), CMD_LINE(OPT_ARG), DEFAULT(FALSE), - NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), + NO_MUTEX_GUARD, NOT_IN_BINLOG, + ON_CHECK(wsrep_on_check), ON_UPDATE(wsrep_on_update)); static Sys_var_charptr Sys_wsrep_start_position ( diff --git a/sql/wsrep_var.cc b/sql/wsrep_var.cc index 5b001ca2922..3c90f03a5fb 100644 --- a/sql/wsrep_var.cc +++ b/sql/wsrep_var.cc @@ -42,12 +42,28 @@ int wsrep_init_vars() return 0; } +extern ulong innodb_lock_schedule_algorithm; + bool wsrep_on_update (sys_var *self, THD* thd, enum_var_type var_type) { if (var_type == OPT_GLOBAL) { // FIXME: this variable probably should be changed only per session thd->variables.wsrep_on = global_system_variables.wsrep_on; } + + return false; +} + +bool wsrep_on_check(sys_var *self, THD* thd, set_var* var) +{ + bool new_wsrep_on= (bool)var->save_result.ulonglong_value; + + if (new_wsrep_on && innodb_lock_schedule_algorithm != 0) { + my_message(ER_WRONG_ARGUMENTS, " WSREP (galera) can't be enabled " + "if innodb_lock_schedule_algorithm=VATS. Please configure" + " innodb_lock_schedule_algorithm=FCFS and restart.", MYF(0)); + return true; + } return false; } diff --git a/sql/wsrep_var.h b/sql/wsrep_var.h index dde59d1503f..b9051b29843 100644 --- a/sql/wsrep_var.h +++ b/sql/wsrep_var.h @@ -41,7 +41,8 @@ int wsrep_init_vars(); #define DEFAULT_ARGS (THD* thd, enum_var_type var_type) #define INIT_ARGS (const char* opt) -extern bool wsrep_causal_reads_update UPDATE_ARGS; +extern bool wsrep_causal_reads_update UPDATE_ARGS; +extern bool wsrep_on_check CHECK_ARGS; extern bool wsrep_on_update UPDATE_ARGS; extern bool wsrep_sync_wait_update UPDATE_ARGS; extern bool wsrep_start_position_check CHECK_ARGS; diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index e02fb953a48..a87ab677d04 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -3862,6 +3862,16 @@ innobase_init( goto error; } +#ifdef WITH_WSREP + /* Currently, Galera does not support VATS lock schedule algorithm. */ + if (innodb_lock_schedule_algorithm == INNODB_LOCK_SCHEDULE_ALGORITHM_VATS + && global_system_variables.wsrep_on) { + ib::info() << "In Galera environment Variance-Aware-Transaction-Sheduling Algorithm" + " is not supported. Falling back to First-Come-First-Served order. "; + innodb_lock_schedule_algorithm = INNODB_LOCK_SCHEDULE_ALGORITHM_FCFS; + } +#endif /* WITH_WSREP */ + #ifndef HAVE_LZ4 if (innodb_compression_algorithm == PAGE_LZ4_ALGORITHM) { sql_print_error("InnoDB: innodb_compression_algorithm = %lu unsupported.\n" @@ -5351,7 +5361,7 @@ innobase_kill_query( wsrep_thd_is_BF(current_thd, FALSE)); } - if (!wsrep_thd_is_BF(trx->mysql_thd, FALSE)) { + if (!wsrep_thd_is_BF(trx->mysql_thd, FALSE) && trx->abort_type != TRX_WSREP_ABORT) { lock_mutex_enter(); lock_mutex_taken = true; } @@ -20863,7 +20873,7 @@ static MYSQL_SYSVAR_ULONG(doublewrite_batch_size, srv_doublewrite_batch_size, #endif /* defined UNIV_DEBUG || defined UNIV_PERF_DEBUG */ static MYSQL_SYSVAR_ENUM(lock_schedule_algorithm, innodb_lock_schedule_algorithm, - PLUGIN_VAR_RQCMDARG, + PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY, "The algorithm Innodb uses for deciding which locks to grant next when" " a lock is released. Possible values are" " FCFS" diff --git a/storage/innobase/include/lock0priv.h b/storage/innobase/include/lock0priv.h index 6bb75817ad6..185779e476f 100644 --- a/storage/innobase/include/lock0priv.h +++ b/storage/innobase/include/lock0priv.h @@ -721,7 +721,7 @@ public: as a victim, and we got the lock immediately: no need to wait then */ dberr_t add_to_waitq( - const lock_t* wait_for, + lock_t* wait_for, const lock_prdt_t* prdt = NULL); @@ -731,21 +731,22 @@ public: @param[in] owns_trx_mutex true if caller owns the trx_t::mutex @param[in] add_to_hash add the lock to hash table @param[in] prdt Predicate lock (optional) + @param[in,out] c_lock Conflicting lock request or NULL + in Galera conflicting lock is selected + as deadlock victim if requester + is BF transaction. @return new lock instance */ lock_t* create( trx_t* trx, bool owns_trx_mutex, bool add_to_hash, const lock_prdt_t* - prdt = NULL); + prdt = NULL +#ifdef WITH_WSREP + ,lock_t* c_lock = NULL +#endif /* WITH_WSREP */ + ); - lock_t* create( - lock_t* const c_lock, - trx_t* trx, - bool owns_trx_mutex, - bool add_to_hash, - const lock_prdt_t* - prdt = NULL); /** Check of the lock is on m_rec_id. @param[in] lock Lock to compare with diff --git a/storage/innobase/lock/lock0lock.cc b/storage/innobase/lock/lock0lock.cc index d9ac953d2e8..7a1afec820c 100644 --- a/storage/innobase/lock/lock0lock.cc +++ b/storage/innobase/lock/lock0lock.cc @@ -811,13 +811,18 @@ lock_reset_lock_and_trx_wait( } ib::error() << - "Trx id " << lock->trx->id - << " is waiting a lock in statement " - << (stmt ? stmt : "NULL") - << " for this trx id " << trx_id - << " and statement " - << (stmt2 ? stmt2 : "NULL") - << "wait_lock " << lock->trx->lock.wait_lock; + "Trx id " << ib::hex(lock->trx->id) + << " is waiting a lock " + << " for this trx id " << ib::hex(trx_id) + << " wait_lock " << lock->trx->lock.wait_lock; + if (stmt) { + ib::info() << " SQL1: " << stmt; + } + + if (stmt2) { + ib::info() << " SQL2: " << stmt2; + } + ut_ad(0); } @@ -1316,7 +1321,7 @@ lock_rec_has_expl( Checks if some other transaction has a lock request in the queue. @return lock or NULL */ static -const lock_t* +lock_t* lock_rec_other_has_expl_req( /*========================*/ lock_mode mode, /*!< in: LOCK_S or LOCK_X */ @@ -1339,10 +1344,10 @@ lock_rec_other_has_expl_req( return(NULL); } - for (const lock_t* lock = lock_rec_get_first(lock_sys->rec_hash, + for (lock_t* lock = lock_rec_get_first(lock_sys->rec_hash, block, heap_no); lock != NULL; - lock = lock_rec_get_next_const(heap_no, lock)) { + lock = lock_rec_get_next(heap_no, lock)) { if (lock->trx != trx && !lock_rec_get_gap(lock) @@ -1431,7 +1436,7 @@ Checks if some other transaction has a conflicting explicit lock request in the queue, so that we have to wait. @return lock or NULL */ static -const lock_t* +lock_t* lock_rec_other_has_conflicting( /*===========================*/ ulint mode, /*!< in: LOCK_S or LOCK_X, @@ -1443,7 +1448,7 @@ lock_rec_other_has_conflicting( ulint heap_no,/*!< in: heap number of the record */ const trx_t* trx) /*!< in: our transaction */ { - const lock_t* lock; + lock_t* lock; ut_ad(lock_mutex_own()); @@ -1451,13 +1456,16 @@ lock_rec_other_has_conflicting( for (lock = lock_rec_get_first(lock_sys->rec_hash, block, heap_no); lock != NULL; - lock = lock_rec_get_next_const(heap_no, lock)) { + lock = lock_rec_get_next(heap_no, lock)) { if (lock_rec_has_to_wait(true, trx, mode, lock, is_supremum)) { #ifdef WITH_WSREP if (wsrep_on_trx(trx)) { trx_mutex_enter(lock->trx); - wsrep_kill_victim((trx_t *)trx, (lock_t *)lock); + /* Below function will roll back either trx + or lock->trx depending on priority of the + transaction. */ + wsrep_kill_victim(const_cast(trx), lock); trx_mutex_exit(lock->trx); } #endif /* WITH_WSREP */ @@ -1947,19 +1955,21 @@ Create a new lock. @param[in] owns_trx_mutex true if caller owns the trx_t::mutex @param[in] add_to_hash add the lock to hash table @param[in] prdt Predicate lock (optional) +@param[in,out] c_lock Conflicting lock request or NULL + in Galera conflicting lock is selected + as deadlock victim if requester + is BF transaction. @return a new lock instance */ lock_t* -RecLock::create(trx_t* trx, bool owns_trx_mutex, bool add_to_hash, const lock_prdt_t* prdt) -{ - return create(NULL, trx, owns_trx_mutex, add_to_hash, prdt); -} -lock_t* RecLock::create( - lock_t* const c_lock, trx_t* trx, bool owns_trx_mutex, bool add_to_hash, - const lock_prdt_t* prdt) + const lock_prdt_t* prdt +#ifdef WITH_WSREP + ,lock_t* c_lock +#endif /* WITH_WSREP */ +) { ut_ad(lock_mutex_own()); ut_ad(owns_trx_mutex == trx_mutex_own(trx)); @@ -2045,7 +2055,7 @@ RecLock::create( trx_mutex_exit(c_lock->trx); if (wsrep_debug) { - ib::info() << "WSREP: c_lock canceled " << c_lock->trx->id; + ib::info() << "WSREP: c_lock canceled " << ib::hex(c_lock->trx->id); ib::info() << " SQL1: " << wsrep_thd_query(c_lock->trx->mysql_thd); ib::info() << " SQL2: " @@ -2182,8 +2192,8 @@ RecLock::mark_trx_for_rollback(trx_t* trx) if (thd != NULL) { char buffer[1024]; - ib::info() << "Blocking transaction: ID: " << trx->id << " - " - << " Blocked transaction ID: "<< m_trx->id << " - " + ib::info() << "Blocking transaction: ID: " << ib::hex(trx->id) << " - " + << " Blocked transaction ID: "<< ib::hex(m_trx->id) << " - " << thd_get_error_context_description(thd, buffer, sizeof(buffer), 512); } @@ -2224,7 +2234,7 @@ queue is itself waiting roll it back, also do a deadlock check and resolve. as a victim, and we got the lock immediately: no need to wait then */ dberr_t -RecLock::add_to_waitq(const lock_t* wait_for, const lock_prdt_t* prdt) +RecLock::add_to_waitq(lock_t* wait_for, const lock_prdt_t* prdt) { ut_ad(lock_mutex_own()); ut_ad(m_trx == thr_get_trx(m_thr)); @@ -2259,7 +2269,12 @@ RecLock::add_to_waitq(const lock_t* wait_for, const lock_prdt_t* prdt) bool high_priority = trx_is_high_priority(m_trx); /* Don't queue the lock to hash table, if high priority transaction. */ - lock_t* lock = create(m_trx, true, !high_priority, prdt); + lock_t* lock = create( + m_trx, true, !high_priority, prdt +#ifdef WITH_WSREP + ,wait_for +#endif /* WITH_WSREP */ + ); /* Attempt to jump over the low priority waiting locks. */ if (high_priority && jump_queue(lock, wait_for)) { @@ -2268,10 +2283,18 @@ RecLock::add_to_waitq(const lock_t* wait_for, const lock_prdt_t* prdt) return(DB_SUCCESS); } +#ifdef WITH_WSREP + if (!lock_get_wait(lock) && wsrep_thd_is_BF(m_trx->mysql_thd, FALSE)) { + if (wsrep_debug) { + ib::info() << "WSREP: BF thread got lock granted early, ID " << ib::hex(lock->trx->id) + << " query: " << wsrep_thd_query(m_trx->mysql_thd); + } + return(DB_SUCCESS); + } +#endif /* WITH_WSREP */ ut_ad(lock_get_wait(lock)); - dberr_t err = deadlock_check(lock); - + dberr_t err = deadlock_check(lock); ut_ad(trx_mutex_own(m_trx)); // Move it only when it does not cause a deadlock. @@ -2553,8 +2576,7 @@ lock_rec_lock_slow( err = DB_SUCCESS; } else { - - const lock_t* wait_for = lock_rec_other_has_conflicting( + lock_t* wait_for = lock_rec_other_has_conflicting( mode, block, heap_no, trx); if (wait_for != NULL) { @@ -2684,6 +2706,14 @@ lock_rec_has_to_wait_in_queue( #ifdef WITH_WSREP if (wsrep_thd_is_BF(wait_lock->trx->mysql_thd, FALSE) && wsrep_thd_is_BF(lock->trx->mysql_thd, TRUE)) { + if (wsrep_debug) { + ib::info() << "WSREP: waiting BF trx: " << ib::hex(wait_lock->trx->id) + << " query: " << wsrep_thd_query(wait_lock->trx->mysql_thd); + lock_rec_print(stderr, wait_lock); + ib::info() << "WSREP: do not wait another BF trx: " << ib::hex(lock->trx->id) + << " query: " << wsrep_thd_query(lock->trx->mysql_thd); + lock_rec_print(stderr, lock); + } /* don't wait for another BF lock */ continue; } @@ -2786,8 +2816,8 @@ RecLock::jump_queue( DBUG_LOG("trx", "Granting High Priority Transaction " - << lock->trx->id << " a lock jumping over" - << " waiting Transaction " << conflict_lock->trx->id); + << ib::hex(lock->trx->id) << " a lock jumping over" + << " waiting Transaction " << ib::hex(conflict_lock->trx->id)); lock_reset_lock_and_trx_wait(lock); return(true); @@ -2958,9 +2988,9 @@ RecLock::make_trx_hit_list( ut_ad(trx->lock.wait_lock != next); DBUG_LOG("trx", "High Priority Transaction " - << lock->trx->id + << ib::hex(lock->trx->id) << " waking up blocking transaction " - << trx->id); + << ib::hex(trx->id)); trx->lock.was_chosen_as_deadlock_victim = true; lock_cancel_waiting_and_release(trx->lock.wait_lock); @@ -3050,22 +3080,21 @@ lock_grant_and_move_on_page( && lock_get_wait(lock) && !lock_rec_has_to_wait_in_queue(lock)) { - bool exit_trx_mutex = false; - + if (lock->trx->abort_type != TRX_SERVER_ABORT) { ut_ad(trx_mutex_own(lock->trx)); trx_mutex_exit(lock->trx); exit_trx_mutex = true; } - + lock_grant(lock, false); - + if (exit_trx_mutex) { ut_ad(!trx_mutex_own(lock->trx)); trx_mutex_enter(lock->trx); } - + if (previous != NULL) { /* Move the lock to the head of the list. */ HASH_GET_NEXT(hash, previous) = HASH_GET_NEXT(hash, lock); @@ -3161,9 +3190,9 @@ lock_rec_dequeue_from_page( } } } - } else { - lock_grant_and_move_on_page(lock_hash, space, page_no); - } + } else { + lock_grant_and_move_on_page(lock_hash, space, page_no); + } } /*************************************************************//** @@ -4389,7 +4418,7 @@ lock_table_create( ut_list_insert(table->locks, c_lock, lock, TableLockGetNode()); if (wsrep_debug) { ib::info() << "table lock BF conflict for " << - c_lock->trx->id; + ib::hex(c_lock->trx->id); ib::info() << " SQL: " << wsrep_thd_query(c_lock->trx->mysql_thd); } @@ -4425,7 +4454,7 @@ lock_table_create( } if (wsrep_debug) { - ib::info() << "WSREP: c_lock canceled " << c_lock->trx->id; + ib::info() << "WSREP: c_lock canceled " << ib::hex(c_lock->trx->id); ib::info() << " SQL: " << wsrep_thd_query(c_lock->trx->mysql_thd); } @@ -4697,7 +4726,7 @@ Checks if other transactions have an incompatible mode lock request in the lock queue. @return lock or NULL */ UNIV_INLINE -const lock_t* +lock_t* lock_table_other_has_incompatible( /*==============================*/ const trx_t* trx, /*!< in: transaction, or NULL if all @@ -4708,7 +4737,7 @@ lock_table_other_has_incompatible( const dict_table_t* table, /*!< in: table */ lock_mode mode) /*!< in: lock mode */ { - const lock_t* lock; + lock_t* lock; ut_ad(lock_mutex_own()); @@ -4757,7 +4786,7 @@ lock_table( { trx_t* trx; dberr_t err; - const lock_t* wait_for; + lock_t* wait_for; ut_ad(table && thr); @@ -4813,9 +4842,9 @@ lock_table( mode: this trx may have to wait */ if (wait_for != NULL) { - err = lock_table_enqueue_waiting((lock_t*)wait_for, mode | flags, table, thr); + err = lock_table_enqueue_waiting(wait_for, mode | flags, table, thr); } else { - lock_table_create(table, mode | flags, trx); + lock_table_create(wait_for, table, mode | flags, trx); ut_a(!flags || mode == LOCK_S || mode == LOCK_X); @@ -6744,7 +6773,7 @@ lock_rec_insert_check_and_lock( const ulint type_mode = LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION; - const lock_t* wait_for = lock_rec_other_has_conflicting( + lock_t* wait_for = lock_rec_other_has_conflicting( type_mode, block, heap_no, trx); if (wait_for != NULL) { @@ -8486,16 +8515,7 @@ DeadlockChecker::check_and_resolve(const lock_t* lock, trx_t* trx) ut_ad(trx == checker.m_start); ut_ad(trx == victim_trx); -#ifdef WITH_WSREP - if (!wsrep_thd_is_BF(victim_trx->mysql_thd, TRUE)) - { -#endif /* WITH_WSREP */ - rollback_print(victim_trx, lock); -#ifdef WITH_WSREP - } else { - /* BF processor */; - } -#endif /* WITH_WSREP */ + rollback_print(victim_trx, lock); MONITOR_INC(MONITOR_DEADLOCK); diff --git a/storage/innobase/lock/lock0prdt.cc b/storage/innobase/lock/lock0prdt.cc index 88573b8a71a..dc2c6e2d15e 100644 --- a/storage/innobase/lock/lock0prdt.cc +++ b/storage/innobase/lock/lock0prdt.cc @@ -290,7 +290,7 @@ Checks if some other transaction has a conflicting predicate lock request in the queue, so that we have to wait. @return lock or NULL */ static -const lock_t* +lock_t* lock_prdt_other_has_conflicting( /*============================*/ ulint mode, /*!< in: LOCK_S or LOCK_X, @@ -305,10 +305,10 @@ lock_prdt_other_has_conflicting( { ut_ad(lock_mutex_own()); - for (const lock_t* lock = lock_rec_get_first( + for (lock_t* lock = lock_rec_get_first( lock_hash_get(mode), block, PRDT_HEAPNO); lock != NULL; - lock = lock_rec_get_next_const(PRDT_HEAPNO, lock)) { + lock = lock_rec_get_next(PRDT_HEAPNO, lock)) { if (lock->trx == trx) { continue; @@ -565,7 +565,7 @@ lock_prdt_insert_check_and_lock( const ulint mode = LOCK_X | LOCK_PREDICATE | LOCK_INSERT_INTENTION; - const lock_t* wait_for = lock_prdt_other_has_conflicting( + lock_t* wait_for = lock_prdt_other_has_conflicting( mode, block, prdt, trx); if (wait_for != NULL) { @@ -854,7 +854,7 @@ lock_prdt_lock( if (lock == NULL) { - const lock_t* wait_for; + lock_t* wait_for; wait_for = lock_prdt_other_has_conflicting( prdt_mode, block, prdt, trx); diff --git a/storage/innobase/lock/lock0wait.cc b/storage/innobase/lock/lock0wait.cc index 0ed55558dc4..c41821412af 100644 --- a/storage/innobase/lock/lock0wait.cc +++ b/storage/innobase/lock/lock0wait.cc @@ -183,20 +183,38 @@ lock_wait_table_reserve_slot( /*********************************************************************//** check if lock timeout was for priority thread, as a side effect trigger lock monitor +@param[in] trx transaction owning the lock +@param[in] locked true if trx and lock_sys_mutex is ownd @return false for regular lock timeout */ -static ibool +static +bool wsrep_is_BF_lock_timeout( -/*====================*/ - trx_t* trx) /* in: trx to check for lock priority */ + const trx_t* trx, + bool locked = true) { - if (wsrep_on_trx(trx) && wsrep_thd_is_BF(trx->mysql_thd, FALSE)) { - fprintf(stderr, "WSREP: BF lock wait long\n"); + if (wsrep_on_trx(trx) + && wsrep_thd_is_BF(trx->mysql_thd, FALSE) + && trx->error_state != DB_DEADLOCK) { + ib::info() << "WSREP: BF lock wait long for trx:" << ib::hex(trx->id) + << " query: " << wsrep_thd_query(trx->mysql_thd); + if (!locked) { + lock_mutex_enter(); + } + + ut_ad(lock_mutex_own()); + + wsrep_trx_print_locking(stderr, trx, 3000); + + if (!locked) { + lock_mutex_exit(); + } + srv_print_innodb_monitor = TRUE; srv_print_innodb_lock_monitor = TRUE; os_event_set(srv_monitor_event); - return TRUE; + return true; } - return FALSE; + return false; } #endif /* WITH_WSREP */ @@ -399,7 +417,7 @@ lock_wait_suspend_thread( && wait_time > (double) lock_wait_timeout #ifdef WITH_WSREP && (!wsrep_on_trx(trx) || - (!wsrep_is_BF_lock_timeout(trx) && trx->error_state != DB_DEADLOCK)) + (!wsrep_is_BF_lock_timeout(trx, false) && trx->error_state != DB_DEADLOCK)) #endif /* WITH_WSREP */ && !trx_is_high_priority(trx)) { From ba576c5b787de0b2218843d83f4524279ae7848b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Lindstr=C3=B6m?= Date: Thu, 7 Dec 2017 12:43:26 +0200 Subject: [PATCH 04/32] MDEV-14401: Stored procedure that declares a handler that catches ER_LOCK_DEADLOCK error causes thd->is_error() assertion This was missing bug fix from MySQL wsrep i.e. Galera. Problem was that if stored procedure declares a handler that catches deadlock error, then the error may have been cleared in method sp_rcontext::handle_sql_condition(). Use wsrep_conflict_state correctly to determine is the error already sent to client. Add test case for both this bug and MDEV-12837: WSREP: BF lock wait long. Test requires both fixes to pass. --- .../suite/galera/r/galera_bf_lock_wait.result | 23 ++++++++ .../suite/galera/t/galera_bf_lock_wait.test | 52 +++++++++++++++++++ sql/sql_parse.cc | 37 ++++++++----- 3 files changed, 98 insertions(+), 14 deletions(-) create mode 100644 mysql-test/suite/galera/r/galera_bf_lock_wait.result create mode 100644 mysql-test/suite/galera/t/galera_bf_lock_wait.test diff --git a/mysql-test/suite/galera/r/galera_bf_lock_wait.result b/mysql-test/suite/galera/r/galera_bf_lock_wait.result new file mode 100644 index 00000000000..7ec524da888 --- /dev/null +++ b/mysql-test/suite/galera/r/galera_bf_lock_wait.result @@ -0,0 +1,23 @@ +CREATE TABLE t1 ENGINE=InnoDB select 1 as a, 1 as b union select 2, 2; +ALTER TABLE t1 add primary key(a); +CREATE PROCEDURE p1() +BEGIN +DECLARE CONTINUE HANDLER FOR SQLEXCEPTION rollback; +WHILE 1 DO +start transaction; +update t1 set b=connection_id() where a=1; +commit; +END WHILE; +END| +connect node_1_p1, 127.0.0.1, root, , test, $NODE_MYPORT_1; +call p1; +connect node_1_p2, 127.0.0.1, root, , test, $NODE_MYPORT_1; +call p1; +connect node_2_p1, 127.0.0.1, root, , test, $NODE_MYPORT_2; +call p1; +connect node_2_p2, 127.0.0.1, root, , test, $NODE_MYPORT_2; +call p1; +connection default; +checking error log for 'BF lock wait long' message for 10 times every 10 seconds ... +drop table t1; +drop procedure p1; diff --git a/mysql-test/suite/galera/t/galera_bf_lock_wait.test b/mysql-test/suite/galera/t/galera_bf_lock_wait.test new file mode 100644 index 00000000000..e3a9077a888 --- /dev/null +++ b/mysql-test/suite/galera/t/galera_bf_lock_wait.test @@ -0,0 +1,52 @@ +--source include/galera_cluster.inc +--source include/big_test.inc + +CREATE TABLE t1 ENGINE=InnoDB select 1 as a, 1 as b union select 2, 2; +ALTER TABLE t1 add primary key(a); + +DELIMITER |; + +CREATE PROCEDURE p1() +BEGIN + DECLARE CONTINUE HANDLER FOR SQLEXCEPTION rollback; + WHILE 1 DO + start transaction; + update t1 set b=connection_id() where a=1; + commit; + END WHILE; +END| + + +DELIMITER ;| + +--connect node_1_p1, 127.0.0.1, root, , test, $NODE_MYPORT_1 +send call p1; +--connect node_1_p2, 127.0.0.1, root, , test, $NODE_MYPORT_1 +send call p1; +--connect node_2_p1, 127.0.0.1, root, , test, $NODE_MYPORT_2 +send call p1; +--connect node_2_p2, 127.0.0.1, root, , test, $NODE_MYPORT_2 +send call p1; + +connection default; +let $counter=10; +let $sleep_period=10; + +echo checking error log for 'BF lock wait long' message for $counter times every $sleep_period seconds ...; +while($counter > 0) +{ +--disable_query_log +--disable_result_log + eval do sleep($sleep_period); +--enable_query_log +--enable_result_log + +# use error 0,1 instead if want test to continue + --error 1 + exec grep 'BF lock wait long' $MYSQLTEST_VARDIR/log/mysqld.*.err; + dec $counter; +} + +drop table t1; +drop procedure p1; + diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 12e7d4700de..d964831a098 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -5563,14 +5563,19 @@ end_with_restore_list: thd->print_aborted_warning(3, "RELEASE"); } #ifdef WITH_WSREP - if (WSREP(thd) && (thd->wsrep_conflict_state != NO_CONFLICT && - thd->wsrep_conflict_state != REPLAYING)) - { - DBUG_ASSERT(thd->is_error()); // the error is already issued - } - else + if (WSREP(thd)) { + + if (thd->wsrep_conflict_state == NO_CONFLICT || + thd->wsrep_conflict_state == REPLAYING) + { + my_ok(thd); + } + } else { +#endif /* WITH_WSREP */ + my_ok(thd); +#ifdef WITH_WSREP + } #endif /* WITH_WSREP */ - my_ok(thd); break; } case SQLCOM_ROLLBACK: @@ -5607,13 +5612,16 @@ end_with_restore_list: if (tx_release) thd->set_killed(KILL_CONNECTION); #ifdef WITH_WSREP - if (WSREP(thd) && thd->wsrep_conflict_state != NO_CONFLICT) - { - DBUG_ASSERT(thd->is_error()); // the error is already issued - } - else + if (WSREP(thd)) { + if (thd->wsrep_conflict_state == NO_CONFLICT) { + my_ok(thd); + } + } else { +#endif /* WITH_WSREP */ + my_ok(thd); +#ifdef WITH_WSREP + } #endif /* WITH_WSREP */ - my_ok(thd); break; } case SQLCOM_RELEASE_SAVEPOINT: @@ -6237,8 +6245,9 @@ finish: if (thd->is_error() || (thd->variables.option_bits & OPTION_MASTER_SQL_ERROR)) trans_rollback_stmt(thd); #ifdef WITH_WSREP - else if (thd->spcont && + if (thd->spcont && (thd->wsrep_conflict_state == MUST_ABORT || + thd->wsrep_conflict_state == ABORTED || thd->wsrep_conflict_state == CERT_FAILURE)) { /* From 4d016e6ed2d32298977a66e125cbfcae39e23847 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Lindstr=C3=B6m?= Date: Thu, 7 Dec 2017 12:59:32 +0200 Subject: [PATCH 05/32] Add Galera test cases that fail to disabled. --- mysql-test/suite/galera/disabled.def | 5 +++++ mysql-test/suite/galera/r/MW-388.result | 12 +++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/mysql-test/suite/galera/disabled.def b/mysql-test/suite/galera/disabled.def index 423c83208f8..801b12a1f84 100644 --- a/mysql-test/suite/galera/disabled.def +++ b/mysql-test/suite/galera/disabled.def @@ -56,3 +56,8 @@ MW-284 : MDEV-13549 Galera test failures 10.1 galera_as_slave : MDEV-13549 Galera test failures 10.1 galera_var_innodb_disallow_writes : MDEV-10949 galera_kill_applier : race condition at the start of the test +MW-328C: MDEV-13549 Galera test failures 10.1 +MW-328A: MDEV-13549 Galera test failures 10.1 +MW-328B: MDEV-13549 Galera test failures 10.1 +MW-328: MDEV-13549 Galera test failures 10.1 +galera_suspend_slave: MDEV-13549 Galera test failures 10.1 \ No newline at end of file diff --git a/mysql-test/suite/galera/r/MW-388.result b/mysql-test/suite/galera/r/MW-388.result index 17d347a11fb..7a8c2adafc1 100644 --- a/mysql-test/suite/galera/r/MW-388.result +++ b/mysql-test/suite/galera/r/MW-388.result @@ -1,3 +1,4 @@ +connection node_1; CREATE TABLE t1 (f1 INTEGER PRIMARY KEY, f2 CHAR(255)) Engine=InnoDB; CREATE PROCEDURE insert_proc () BEGIN @@ -12,28 +13,37 @@ SET GLOBAL wsrep_slave_threads = 2; SET GLOBAL DEBUG = "d,sync.wsrep_apply_cb"; Warnings: Warning 1287 '@@debug' is deprecated and will be removed in a future release. Please use '@@debug_dbug' instead +connection node_2; INSERT INTO t1 VALUES (1, 'node 2');; +connect node_1a, 127.0.0.1, root, , test, $NODE_MYPORT_1; +connection node_1a; SET SESSION DEBUG_SYNC = "now WAIT_FOR sync.wsrep_apply_cb_reached"; +connection node_1; SET SESSION wsrep_sync_wait = 0; SET SESSION DEBUG_SYNC = 'wsrep_after_replication SIGNAL wsrep_after_replication_reached WAIT_FOR wsrep_after_replication_continue'; CALL insert_proc ();; +connection node_1a; SET SESSION DEBUG_SYNC = "now WAIT_FOR wsrep_after_replication_reached"; SET GLOBAL DEBUG = ""; Warnings: Warning 1287 '@@debug' is deprecated and will be removed in a future release. Please use '@@debug_dbug' instead SET DEBUG_SYNC = "now SIGNAL wsrep_after_replication_continue"; SET DEBUG_SYNC = "now SIGNAL signal.wsrep_apply_cb"; +connection node_2; +connection node_1; SELECT @errno = 1213; @errno = 1213 -1 +0 SELECT * FROM t1; f1 f2 1 node 2 3 node 1 +connection node_2; SELECT * FROM t1; f1 f2 1 node 2 3 node 1 +connection node_1; SET GLOBAL wsrep_slave_threads = DEFAULT; DROP TABLE t1; DROP PROCEDURE insert_proc; From 08dae447118b49b99499ccc456c9a72afa651f24 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Thu, 7 Dec 2017 15:54:27 +0400 Subject: [PATCH 06/32] MDEV-14228 MariaDB crashes with function --- mysql-test/r/sp-row.result | 4 +- mysql-test/r/sp.result | 57 ++++++++++++ mysql-test/r/subselect.result | 12 +-- mysql-test/r/subselect_no_exists_to_in.result | 12 +-- mysql-test/r/subselect_no_mat.result | 12 +-- mysql-test/r/subselect_no_opts.result | 12 +-- mysql-test/r/subselect_no_scache.result | 12 +-- mysql-test/r/subselect_no_semijoin.result | 12 +-- .../suite/compat/oracle/r/sp-row.result | 4 +- mysql-test/suite/compat/oracle/r/sp.result | 63 ++++++++++++++ mysql-test/suite/compat/oracle/t/sp-row.test | 4 +- mysql-test/suite/compat/oracle/t/sp.test | 87 +++++++++++++++++++ mysql-test/t/sp-row.test | 4 +- mysql-test/t/sp.test | 80 +++++++++++++++++ mysql-test/t/subselect.test | 12 +-- sql/item.cc | 10 +++ sql/item_func.cc | 13 ++- 17 files changed, 357 insertions(+), 53 deletions(-) diff --git a/mysql-test/r/sp-row.result b/mysql-test/r/sp-row.result index adb67030feb..d3be7c2a9b9 100644 --- a/mysql-test/r/sp-row.result +++ b/mysql-test/r/sp-row.result @@ -210,7 +210,7 @@ SELECT a=1; END; $$ CALL p1(); -ERROR 21000: Operand should contain 2 column(s) +ERROR HY000: Illegal parameter data types row and int for operation '=' DROP PROCEDURE p1; CREATE PROCEDURE p1() BEGIN @@ -219,7 +219,7 @@ SELECT 1=a; END; $$ CALL p1(); -ERROR 21000: Operand should contain 1 column(s) +ERROR HY000: Illegal parameter data types int and row for operation '=' DROP PROCEDURE p1; # # Passing the entire ROW to a stored function diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index 9fad95421cf..f40e6033b9f 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -8291,3 +8291,60 @@ rec=(10) c rec=(20) DROP PROCEDURE p1; +# +# MDEV-14228 MariaDB crashes with function +# +CREATE TABLE t1 (c VARCHAR(16), KEY(c)); +INSERT INTO t1 VALUES ('foo'); +CREATE FUNCTION f1() RETURNS VARCHAR(16) +BEGIN +DECLARE v VARCHAR(16); +FOR v IN (SELECT DISTINCT c FROM t1) +DO +IF (v = 'bar') THEN +SELECT 1 INTO @a; +END IF; +END FOR; +RETURN 'qux'; +END $$ +SELECT f1(); +ERROR HY000: Illegal parameter data types row and varchar for operation '=' +DROP FUNCTION f1; +CREATE FUNCTION f1() RETURNS VARCHAR(16) +BEGIN +DECLARE v ROW TYPE OF t1; +IF v = 'bar' THEN +RETURN 'eq'; +END IF; +RETURN 'ne'; +END $$ +SELECT f1(); +ERROR HY000: Illegal parameter data types row and varchar for operation '=' +DROP FUNCTION f1; +CREATE FUNCTION f1() RETURNS VARCHAR(16) +BEGIN +DECLARE v ROW(a INT); +IF v = 'bar' THEN +RETURN 'eq'; +END IF; +RETURN 'ne'; +END $$ +SELECT f1(); +ERROR HY000: Illegal parameter data types row and varchar for operation '=' +DROP FUNCTION f1; +DROP TABLE t1; +BEGIN NOT ATOMIC +DECLARE v ROW(a INT); +SELECT v IN ('a','b'); +END $$ +ERROR HY000: Illegal parameter data types row and varchar for operation 'in' +BEGIN NOT ATOMIC +DECLARE v ROW(a INT); +SELECT 'a' IN (v,'b'); +END $$ +ERROR HY000: Illegal parameter data types varchar and row for operation 'in' +BEGIN NOT ATOMIC +DECLARE v ROW(a INT); +SELECT 'a' IN ('b',v); +END $$ +ERROR HY000: Illegal parameter data types varchar and row for operation 'in' diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index 7ba8b545e6a..1e0d5f31a7a 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -384,10 +384,10 @@ Warnings: Note 1003 /* select#1 */ select 'joce' AS `pseudo`,(/* select#2 */ select 'test' from `test`.`t8` where 1) AS `(SELECT email FROM t8 WHERE pseudo=(SELECT pseudo FROM t8 WHERE pseudo='joce'))` from `test`.`t8` where 1 SELECT pseudo FROM t8 WHERE pseudo=(SELECT pseudo,email FROM t8 WHERE pseudo='joce'); -ERROR 21000: Operand should contain 1 column(s) +ERROR HY000: Illegal parameter data types varchar and row for operation '=' SELECT pseudo FROM t8 WHERE pseudo=(SELECT * FROM t8 WHERE pseudo='joce'); -ERROR 21000: Operand should contain 1 column(s) +ERROR HY000: Illegal parameter data types varchar and row for operation '=' SELECT pseudo FROM t8 WHERE pseudo=(SELECT pseudo FROM t8 WHERE pseudo='joce'); pseudo joce @@ -2865,13 +2865,13 @@ drop table t1, t2; create table t1 (a int, b int); insert into t1 values (1,2); select 1 = (select * from t1); -ERROR 21000: Operand should contain 1 column(s) +ERROR HY000: Illegal parameter data types int and row for operation '=' select (select * from t1) = 1; -ERROR 21000: Operand should contain 2 column(s) +ERROR HY000: Illegal parameter data types row and int for operation '=' select (1,2) = (select a from t1); -ERROR 21000: Operand should contain 2 column(s) +ERROR HY000: Illegal parameter data types row and int for operation '=' select (select a from t1) = (1,2); -ERROR 21000: Operand should contain 1 column(s) +ERROR HY000: Illegal parameter data types int and row for operation '=' select (1,2,3) = (select * from t1); ERROR 21000: Operand should contain 3 column(s) select (select * from t1) = (1,2,3); diff --git a/mysql-test/r/subselect_no_exists_to_in.result b/mysql-test/r/subselect_no_exists_to_in.result index c09f3c94710..ac02ec71920 100644 --- a/mysql-test/r/subselect_no_exists_to_in.result +++ b/mysql-test/r/subselect_no_exists_to_in.result @@ -388,10 +388,10 @@ Warnings: Note 1003 /* select#1 */ select 'joce' AS `pseudo`,(/* select#2 */ select 'test' from `test`.`t8` where 1) AS `(SELECT email FROM t8 WHERE pseudo=(SELECT pseudo FROM t8 WHERE pseudo='joce'))` from `test`.`t8` where 1 SELECT pseudo FROM t8 WHERE pseudo=(SELECT pseudo,email FROM t8 WHERE pseudo='joce'); -ERROR 21000: Operand should contain 1 column(s) +ERROR HY000: Illegal parameter data types varchar and row for operation '=' SELECT pseudo FROM t8 WHERE pseudo=(SELECT * FROM t8 WHERE pseudo='joce'); -ERROR 21000: Operand should contain 1 column(s) +ERROR HY000: Illegal parameter data types varchar and row for operation '=' SELECT pseudo FROM t8 WHERE pseudo=(SELECT pseudo FROM t8 WHERE pseudo='joce'); pseudo joce @@ -2868,13 +2868,13 @@ drop table t1, t2; create table t1 (a int, b int); insert into t1 values (1,2); select 1 = (select * from t1); -ERROR 21000: Operand should contain 1 column(s) +ERROR HY000: Illegal parameter data types int and row for operation '=' select (select * from t1) = 1; -ERROR 21000: Operand should contain 2 column(s) +ERROR HY000: Illegal parameter data types row and int for operation '=' select (1,2) = (select a from t1); -ERROR 21000: Operand should contain 2 column(s) +ERROR HY000: Illegal parameter data types row and int for operation '=' select (select a from t1) = (1,2); -ERROR 21000: Operand should contain 1 column(s) +ERROR HY000: Illegal parameter data types int and row for operation '=' select (1,2,3) = (select * from t1); ERROR 21000: Operand should contain 3 column(s) select (select * from t1) = (1,2,3); diff --git a/mysql-test/r/subselect_no_mat.result b/mysql-test/r/subselect_no_mat.result index 0aefeaf44d9..dd89f2f2776 100644 --- a/mysql-test/r/subselect_no_mat.result +++ b/mysql-test/r/subselect_no_mat.result @@ -391,10 +391,10 @@ Warnings: Note 1003 /* select#1 */ select 'joce' AS `pseudo`,(/* select#2 */ select 'test' from `test`.`t8` where 1) AS `(SELECT email FROM t8 WHERE pseudo=(SELECT pseudo FROM t8 WHERE pseudo='joce'))` from `test`.`t8` where 1 SELECT pseudo FROM t8 WHERE pseudo=(SELECT pseudo,email FROM t8 WHERE pseudo='joce'); -ERROR 21000: Operand should contain 1 column(s) +ERROR HY000: Illegal parameter data types varchar and row for operation '=' SELECT pseudo FROM t8 WHERE pseudo=(SELECT * FROM t8 WHERE pseudo='joce'); -ERROR 21000: Operand should contain 1 column(s) +ERROR HY000: Illegal parameter data types varchar and row for operation '=' SELECT pseudo FROM t8 WHERE pseudo=(SELECT pseudo FROM t8 WHERE pseudo='joce'); pseudo joce @@ -2871,13 +2871,13 @@ drop table t1, t2; create table t1 (a int, b int); insert into t1 values (1,2); select 1 = (select * from t1); -ERROR 21000: Operand should contain 1 column(s) +ERROR HY000: Illegal parameter data types int and row for operation '=' select (select * from t1) = 1; -ERROR 21000: Operand should contain 2 column(s) +ERROR HY000: Illegal parameter data types row and int for operation '=' select (1,2) = (select a from t1); -ERROR 21000: Operand should contain 2 column(s) +ERROR HY000: Illegal parameter data types row and int for operation '=' select (select a from t1) = (1,2); -ERROR 21000: Operand should contain 1 column(s) +ERROR HY000: Illegal parameter data types int and row for operation '=' select (1,2,3) = (select * from t1); ERROR 21000: Operand should contain 3 column(s) select (select * from t1) = (1,2,3); diff --git a/mysql-test/r/subselect_no_opts.result b/mysql-test/r/subselect_no_opts.result index 92defb3c36d..c5c4019768c 100644 --- a/mysql-test/r/subselect_no_opts.result +++ b/mysql-test/r/subselect_no_opts.result @@ -387,10 +387,10 @@ Warnings: Note 1003 /* select#1 */ select 'joce' AS `pseudo`,(/* select#2 */ select 'test' from `test`.`t8` where 1) AS `(SELECT email FROM t8 WHERE pseudo=(SELECT pseudo FROM t8 WHERE pseudo='joce'))` from `test`.`t8` where 1 SELECT pseudo FROM t8 WHERE pseudo=(SELECT pseudo,email FROM t8 WHERE pseudo='joce'); -ERROR 21000: Operand should contain 1 column(s) +ERROR HY000: Illegal parameter data types varchar and row for operation '=' SELECT pseudo FROM t8 WHERE pseudo=(SELECT * FROM t8 WHERE pseudo='joce'); -ERROR 21000: Operand should contain 1 column(s) +ERROR HY000: Illegal parameter data types varchar and row for operation '=' SELECT pseudo FROM t8 WHERE pseudo=(SELECT pseudo FROM t8 WHERE pseudo='joce'); pseudo joce @@ -2867,13 +2867,13 @@ drop table t1, t2; create table t1 (a int, b int); insert into t1 values (1,2); select 1 = (select * from t1); -ERROR 21000: Operand should contain 1 column(s) +ERROR HY000: Illegal parameter data types int and row for operation '=' select (select * from t1) = 1; -ERROR 21000: Operand should contain 2 column(s) +ERROR HY000: Illegal parameter data types row and int for operation '=' select (1,2) = (select a from t1); -ERROR 21000: Operand should contain 2 column(s) +ERROR HY000: Illegal parameter data types row and int for operation '=' select (select a from t1) = (1,2); -ERROR 21000: Operand should contain 1 column(s) +ERROR HY000: Illegal parameter data types int and row for operation '=' select (1,2,3) = (select * from t1); ERROR 21000: Operand should contain 3 column(s) select (select * from t1) = (1,2,3); diff --git a/mysql-test/r/subselect_no_scache.result b/mysql-test/r/subselect_no_scache.result index b47dab2e79e..1af56e5b683 100644 --- a/mysql-test/r/subselect_no_scache.result +++ b/mysql-test/r/subselect_no_scache.result @@ -390,10 +390,10 @@ Warnings: Note 1003 /* select#1 */ select 'joce' AS `pseudo`,(/* select#2 */ select 'test' from `test`.`t8` where 1) AS `(SELECT email FROM t8 WHERE pseudo=(SELECT pseudo FROM t8 WHERE pseudo='joce'))` from `test`.`t8` where 1 SELECT pseudo FROM t8 WHERE pseudo=(SELECT pseudo,email FROM t8 WHERE pseudo='joce'); -ERROR 21000: Operand should contain 1 column(s) +ERROR HY000: Illegal parameter data types varchar and row for operation '=' SELECT pseudo FROM t8 WHERE pseudo=(SELECT * FROM t8 WHERE pseudo='joce'); -ERROR 21000: Operand should contain 1 column(s) +ERROR HY000: Illegal parameter data types varchar and row for operation '=' SELECT pseudo FROM t8 WHERE pseudo=(SELECT pseudo FROM t8 WHERE pseudo='joce'); pseudo joce @@ -2871,13 +2871,13 @@ drop table t1, t2; create table t1 (a int, b int); insert into t1 values (1,2); select 1 = (select * from t1); -ERROR 21000: Operand should contain 1 column(s) +ERROR HY000: Illegal parameter data types int and row for operation '=' select (select * from t1) = 1; -ERROR 21000: Operand should contain 2 column(s) +ERROR HY000: Illegal parameter data types row and int for operation '=' select (1,2) = (select a from t1); -ERROR 21000: Operand should contain 2 column(s) +ERROR HY000: Illegal parameter data types row and int for operation '=' select (select a from t1) = (1,2); -ERROR 21000: Operand should contain 1 column(s) +ERROR HY000: Illegal parameter data types int and row for operation '=' select (1,2,3) = (select * from t1); ERROR 21000: Operand should contain 3 column(s) select (select * from t1) = (1,2,3); diff --git a/mysql-test/r/subselect_no_semijoin.result b/mysql-test/r/subselect_no_semijoin.result index 9d04ddd9829..4276e5ef638 100644 --- a/mysql-test/r/subselect_no_semijoin.result +++ b/mysql-test/r/subselect_no_semijoin.result @@ -387,10 +387,10 @@ Warnings: Note 1003 /* select#1 */ select 'joce' AS `pseudo`,(/* select#2 */ select 'test' from `test`.`t8` where 1) AS `(SELECT email FROM t8 WHERE pseudo=(SELECT pseudo FROM t8 WHERE pseudo='joce'))` from `test`.`t8` where 1 SELECT pseudo FROM t8 WHERE pseudo=(SELECT pseudo,email FROM t8 WHERE pseudo='joce'); -ERROR 21000: Operand should contain 1 column(s) +ERROR HY000: Illegal parameter data types varchar and row for operation '=' SELECT pseudo FROM t8 WHERE pseudo=(SELECT * FROM t8 WHERE pseudo='joce'); -ERROR 21000: Operand should contain 1 column(s) +ERROR HY000: Illegal parameter data types varchar and row for operation '=' SELECT pseudo FROM t8 WHERE pseudo=(SELECT pseudo FROM t8 WHERE pseudo='joce'); pseudo joce @@ -2867,13 +2867,13 @@ drop table t1, t2; create table t1 (a int, b int); insert into t1 values (1,2); select 1 = (select * from t1); -ERROR 21000: Operand should contain 1 column(s) +ERROR HY000: Illegal parameter data types int and row for operation '=' select (select * from t1) = 1; -ERROR 21000: Operand should contain 2 column(s) +ERROR HY000: Illegal parameter data types row and int for operation '=' select (1,2) = (select a from t1); -ERROR 21000: Operand should contain 2 column(s) +ERROR HY000: Illegal parameter data types row and int for operation '=' select (select a from t1) = (1,2); -ERROR 21000: Operand should contain 1 column(s) +ERROR HY000: Illegal parameter data types int and row for operation '=' select (1,2,3) = (select * from t1); ERROR 21000: Operand should contain 3 column(s) select (select * from t1) = (1,2,3); diff --git a/mysql-test/suite/compat/oracle/r/sp-row.result b/mysql-test/suite/compat/oracle/r/sp-row.result index f95817420f1..9557a24a1da 100644 --- a/mysql-test/suite/compat/oracle/r/sp-row.result +++ b/mysql-test/suite/compat/oracle/r/sp-row.result @@ -232,7 +232,7 @@ SELECT a=1; END; $$ CALL p1(); -ERROR 21000: Operand should contain 2 column(s) +ERROR HY000: Illegal parameter data types row and int for operation '=' DROP PROCEDURE p1; CREATE PROCEDURE p1() AS @@ -242,7 +242,7 @@ SELECT 1=a; END; $$ CALL p1(); -ERROR 21000: Operand should contain 1 column(s) +ERROR HY000: Illegal parameter data types int and row for operation '=' DROP PROCEDURE p1; # # Passing the entire ROW to a stored function diff --git a/mysql-test/suite/compat/oracle/r/sp.result b/mysql-test/suite/compat/oracle/r/sp.result index 2bf4f50e6d5..2f565b0a183 100644 --- a/mysql-test/suite/compat/oracle/r/sp.result +++ b/mysql-test/suite/compat/oracle/r/sp.result @@ -2432,3 +2432,66 @@ a b DROP PROCEDURE p1; DROP VIEW v1; DROP TABLE t1; +# +# MDEV-14228 MariaDB crashes with function +# +CREATE TABLE t1 (c VARCHAR(16), KEY(c)); +INSERT INTO t1 VALUES ('foo'); +CREATE FUNCTION f1() RETURN VARCHAR(16) +IS +v VARCHAR2(16); +BEGIN +FOR v IN (SELECT DISTINCT c FROM t1) +LOOP +IF (v = 'bar') THEN +SELECT 1 INTO @a; +END IF; +END LOOP; +RETURN 'qux'; +END $$ +SELECT f1(); +ERROR HY000: Illegal parameter data types row and varchar for operation '=' +DROP FUNCTION f1; +CREATE FUNCTION f1() RETURN VARCHAR(16) +IS +v t1%ROWTYPE; +BEGIN +IF v = 'bar' THEN +NULL; +END IF; +RETURN 'qux'; +END $$ +SELECT f1(); +ERROR HY000: Illegal parameter data types row and varchar for operation '=' +DROP FUNCTION f1; +CREATE FUNCTION f1() RETURN VARCHAR(16) +IS +v ROW(a INT); +BEGIN +IF v = 'bar' THEN +NULL; +END IF; +RETURN 'qux'; +END $$ +SELECT f1(); +ERROR HY000: Illegal parameter data types row and varchar for operation '=' +DROP FUNCTION f1; +DROP TABLE t1; +DECLARE +v ROW(a INT); +BEGIN +SELECT v IN ('a','b'); +END $$ +ERROR HY000: Illegal parameter data types row and varchar for operation 'in' +DECLARE +v ROW(a INT); +BEGIN +SELECT 'a' IN (v,'b'); +END $$ +ERROR HY000: Illegal parameter data types varchar and row for operation 'in' +DECLARE +v ROW(a INT); +BEGIN +SELECT 'a' IN ('b',v); +END $$ +ERROR HY000: Illegal parameter data types varchar and row for operation 'in' diff --git a/mysql-test/suite/compat/oracle/t/sp-row.test b/mysql-test/suite/compat/oracle/t/sp-row.test index b26cae5a5c6..469494ad228 100644 --- a/mysql-test/suite/compat/oracle/t/sp-row.test +++ b/mysql-test/suite/compat/oracle/t/sp-row.test @@ -295,7 +295,7 @@ BEGIN END; $$ DELIMITER ;$$ ---error ER_OPERAND_COLUMNS +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION CALL p1(); DROP PROCEDURE p1; @@ -309,7 +309,7 @@ BEGIN END; $$ DELIMITER ;$$ ---error ER_OPERAND_COLUMNS +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION CALL p1(); DROP PROCEDURE p1; diff --git a/mysql-test/suite/compat/oracle/t/sp.test b/mysql-test/suite/compat/oracle/t/sp.test index e7adbb5403a..693569d184c 100644 --- a/mysql-test/suite/compat/oracle/t/sp.test +++ b/mysql-test/suite/compat/oracle/t/sp.test @@ -2260,3 +2260,90 @@ CALL p1(); DROP PROCEDURE p1; DROP VIEW v1; DROP TABLE t1; + + +--echo # +--echo # MDEV-14228 MariaDB crashes with function +--echo # + +CREATE TABLE t1 (c VARCHAR(16), KEY(c)); +INSERT INTO t1 VALUES ('foo'); + +DELIMITER $$; +CREATE FUNCTION f1() RETURN VARCHAR(16) +IS + v VARCHAR2(16); +BEGIN + FOR v IN (SELECT DISTINCT c FROM t1) + LOOP + IF (v = 'bar') THEN + SELECT 1 INTO @a; + END IF; + END LOOP; + RETURN 'qux'; +END $$ +DELIMITER ;$$ +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +SELECT f1(); +DROP FUNCTION f1; + +DELIMITER $$; +CREATE FUNCTION f1() RETURN VARCHAR(16) +IS + v t1%ROWTYPE; +BEGIN + IF v = 'bar' THEN + NULL; + END IF; + RETURN 'qux'; +END $$ +DELIMITER ;$$ +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +SELECT f1(); +DROP FUNCTION f1; + +DELIMITER $$; +CREATE FUNCTION f1() RETURN VARCHAR(16) +IS + v ROW(a INT); +BEGIN + IF v = 'bar' THEN + NULL; + END IF; + RETURN 'qux'; +END $$ +DELIMITER ;$$ +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +SELECT f1(); +DROP FUNCTION f1; + +DROP TABLE t1; + + +DELIMITER $$; +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +DECLARE + v ROW(a INT); +BEGIN + SELECT v IN ('a','b'); +END $$ +DELIMITER ;$$ + +DELIMITER $$; +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +DECLARE + v ROW(a INT); +BEGIN + SELECT 'a' IN (v,'b'); +END $$ +DELIMITER ;$$ + +DELIMITER $$; +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +DECLARE + v ROW(a INT); +BEGIN + SELECT 'a' IN ('b',v); +END $$ +DELIMITER ;$$ + diff --git a/mysql-test/t/sp-row.test b/mysql-test/t/sp-row.test index 5928c8cb76d..837e24c89c0 100644 --- a/mysql-test/t/sp-row.test +++ b/mysql-test/t/sp-row.test @@ -274,7 +274,7 @@ BEGIN END; $$ DELIMITER ;$$ ---error ER_OPERAND_COLUMNS +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION CALL p1(); DROP PROCEDURE p1; @@ -287,7 +287,7 @@ BEGIN END; $$ DELIMITER ;$$ ---error ER_OPERAND_COLUMNS +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION CALL p1(); DROP PROCEDURE p1; diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index 08aa70df03e..82ad655593a 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -9771,3 +9771,83 @@ $$ DELIMITER ;$$ CALL p1(); DROP PROCEDURE p1; + + +--echo # +--echo # MDEV-14228 MariaDB crashes with function +--echo # + +CREATE TABLE t1 (c VARCHAR(16), KEY(c)); +INSERT INTO t1 VALUES ('foo'); + +DELIMITER $$; +CREATE FUNCTION f1() RETURNS VARCHAR(16) +BEGIN + DECLARE v VARCHAR(16); + FOR v IN (SELECT DISTINCT c FROM t1) + DO + IF (v = 'bar') THEN + SELECT 1 INTO @a; + END IF; + END FOR; + RETURN 'qux'; +END $$ +DELIMITER ;$$ +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +SELECT f1(); +DROP FUNCTION f1; + +DELIMITER $$; +CREATE FUNCTION f1() RETURNS VARCHAR(16) +BEGIN + DECLARE v ROW TYPE OF t1; + IF v = 'bar' THEN + RETURN 'eq'; + END IF; + RETURN 'ne'; +END $$ +DELIMITER ;$$ +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +SELECT f1(); +DROP FUNCTION f1; + +DELIMITER $$; +CREATE FUNCTION f1() RETURNS VARCHAR(16) +BEGIN + DECLARE v ROW(a INT); + IF v = 'bar' THEN + RETURN 'eq'; + END IF; + RETURN 'ne'; +END $$ +DELIMITER ;$$ +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +SELECT f1(); +DROP FUNCTION f1; + +DROP TABLE t1; + + +DELIMITER $$; +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +BEGIN NOT ATOMIC + DECLARE v ROW(a INT); + SELECT v IN ('a','b'); +END $$ +DELIMITER ;$$ + +DELIMITER $$; +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +BEGIN NOT ATOMIC +DECLARE v ROW(a INT); + SELECT 'a' IN (v,'b'); +END $$ +DELIMITER ;$$ + +DELIMITER $$; +--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION +BEGIN NOT ATOMIC + DECLARE v ROW(a INT); + SELECT 'a' IN ('b',v); +END $$ +DELIMITER ;$$ diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test index 5e1e1494fee..ce0b949fcac 100644 --- a/mysql-test/t/subselect.test +++ b/mysql-test/t/subselect.test @@ -188,10 +188,10 @@ INSERT INTO t8 (pseudo,email) VALUES ('joce','test'); INSERT INTO t8 (pseudo,email) VALUES ('joce1','test1'); INSERT INTO t8 (pseudo,email) VALUES ('2joce1','2test1'); EXPLAIN EXTENDED SELECT pseudo,(SELECT email FROM t8 WHERE pseudo=(SELECT pseudo FROM t8 WHERE pseudo='joce')) FROM t8 WHERE pseudo=(SELECT pseudo FROM t8 WHERE pseudo='joce'); --- error ER_OPERAND_COLUMNS +-- error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION SELECT pseudo FROM t8 WHERE pseudo=(SELECT pseudo,email FROM t8 WHERE pseudo='joce'); --- error ER_OPERAND_COLUMNS +-- error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION SELECT pseudo FROM t8 WHERE pseudo=(SELECT * FROM t8 WHERE pseudo='joce'); SELECT pseudo FROM t8 WHERE pseudo=(SELECT pseudo FROM t8 WHERE pseudo='joce'); @@ -1834,13 +1834,13 @@ drop table t1, t2; # create table t1 (a int, b int); insert into t1 values (1,2); --- error ER_OPERAND_COLUMNS +-- error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION select 1 = (select * from t1); --- error ER_OPERAND_COLUMNS +-- error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION select (select * from t1) = 1; --- error ER_OPERAND_COLUMNS +-- error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION select (1,2) = (select a from t1); --- error ER_OPERAND_COLUMNS +-- error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION select (select a from t1) = (1,2); -- error ER_OPERAND_COLUMNS select (1,2,3) = (select * from t1); diff --git a/sql/item.cc b/sql/item.cc index c9f5462cca9..6fdcd29af29 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -1148,6 +1148,16 @@ bool Item::check_type_can_return_text(const char *opname) const bool Item::check_type_scalar(const char *opname) const { + /* + fixed==true usually means than the Item has an initialized + and reliable data type handler and attributes. + Item_outer_ref is an exception. It copies the data type and the attributes + from the referenced Item in the constructor, but then sets "fixed" to false, + and re-fixes itself again in fix_inner_refs(). + This hack in Item_outer_ref should probably be refactored eventually. + Discuss with Sanja. + */ + DBUG_ASSERT(fixed || type() == REF_ITEM); const Type_handler *handler= type_handler(); if (handler->is_scalar_type()) return false; diff --git a/sql/item_func.cc b/sql/item_func.cc index be5e5064d60..a6134c009d5 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -153,12 +153,19 @@ void Item_func::sync_with_sum_func_and_with_field(List &list) bool Item_func::check_argument_types_like_args0() const { - uint cols; - if (arg_count == 0) + if (arg_count < 2) return false; - cols= args[0]->cols(); + uint cols= args[0]->cols(); + bool is_scalar= args[0]->type_handler()->is_scalar_type(); for (uint i= 1; i < arg_count; i++) { + if (is_scalar != args[i]->type_handler()->is_scalar_type()) + { + my_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION, MYF(0), + args[0]->type_handler()->name().ptr(), + args[i]->type_handler()->name().ptr(), func_name()); + return true; + } if (args[i]->check_cols(cols)) return true; } From 578b26598abf5ee9aa8238943d43b62eb8dadb16 Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Fri, 8 Dec 2017 01:15:10 +0300 Subject: [PATCH 07/32] MDEV-14607: storage_engine-rocksdb.type_bit_indexes fails after latest pushes Update the .result file. Old EXPLAIN output was incorrect due to hitting MDEV-14563. --- mysql-test/suite/storage_engine/type_bit_indexes.result | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mysql-test/suite/storage_engine/type_bit_indexes.result b/mysql-test/suite/storage_engine/type_bit_indexes.result index af8ddf7d6c9..e7c0cf656c5 100644 --- a/mysql-test/suite/storage_engine/type_bit_indexes.result +++ b/mysql-test/suite/storage_engine/type_bit_indexes.result @@ -69,7 +69,7 @@ INSERT INTO t1 (a,b,c,d) VALUES (1,0xFFFF,0xFFFFFFFF,0xFFFFFFFFFFFFFFFF); EXPLAIN SELECT HEX(b+c) FROM t1 WHERE c > 1 OR HEX(b) < 0xFFFFFF; id select_type table type possible_keys key key_len ref rows Extra -# # # # # b_c # # # # +# # # # # NULL # # # # SELECT HEX(b+c) FROM t1 WHERE c > 1 OR HEX(b) < 0xFFFFFF; HEX(b+c) 10 @@ -98,7 +98,7 @@ INSERT INTO t1 (a,b,c,d) VALUES (1,0xFFFF,0xFFFFFFFF,0xFFFFFFFFFFFFFFFF); EXPLAIN SELECT DISTINCT a+0 FROM t1 ORDER BY a; id select_type table type possible_keys key key_len ref rows Extra -# # # # # a # # # # +# # # # # NULL # # # # SELECT DISTINCT a+0 FROM t1 ORDER BY a; a+0 0 From a61fbf87ed9f6bb68332b81bc1024d1ded944d3d Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Fri, 8 Dec 2017 09:44:53 +0400 Subject: [PATCH 08/32] Adding the -Wnon-virtual-dtor GCC/CLang flag in maintainer mode --- cmake/maintainer.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/maintainer.cmake b/cmake/maintainer.cmake index bd2cfd7097c..77f55837fdb 100644 --- a/cmake/maintainer.cmake +++ b/cmake/maintainer.cmake @@ -51,7 +51,7 @@ IF(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang") ENDIF() # Set warning flags for G++/Clang++ IF(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") - SET(MY_MAINTAINER_CXX_WARNINGS "${MY_CXX_WARNING_FLAGS}") + SET(MY_MAINTAINER_CXX_WARNINGS "${MY_CXX_WARNING_FLAGS} -Wnon-virtual-dtor") ENDIF() IF(MYSQL_MAINTAINER_MODE MATCHES "ON") From dfafe15abbd552ed487b2b0f7188db9303b1fa1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Fri, 8 Dec 2017 09:53:11 +0200 Subject: [PATCH 09/32] MDEV-14606 Assertion failure on IMPORT TABLESPACE fseg_alloc_free_page_low(): Remove a bogus and redundant assertion about fil_space_t::purpose. The debug function fsp_space_modify_check() is asserting something similar, but more accurately. --- storage/innobase/fsp/fsp0fsp.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/storage/innobase/fsp/fsp0fsp.cc b/storage/innobase/fsp/fsp0fsp.cc index 80303314d9f..2531a9fb5aa 100644 --- a/storage/innobase/fsp/fsp0fsp.cc +++ b/storage/innobase/fsp/fsp0fsp.cc @@ -2446,8 +2446,6 @@ fseg_alloc_free_page_low( ut_ad(mach_read_from_4(seg_inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE); ut_ad(!((page_offset(seg_inode) - FSEG_ARR_OFFSET) % FSEG_INODE_SIZE)); - ut_ad(space->purpose == FIL_TYPE_TEMPORARY - || space->purpose == FIL_TYPE_TABLESPACE); seg_id = mach_read_from_8(seg_inode + FSEG_ID); ut_ad(seg_id); From 07e9ff1fe18999e1acd640ee3b2169c3f506fb35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Sat, 11 Nov 2017 22:27:03 +0200 Subject: [PATCH 10/32] MDEV-14378 In ALGORITHM=INPLACE, use a common name for the intermediate tables or partitions Allow DROP TABLE `#mysql50##sql-...._.` to drop tables that were being rebuilt by ALGORITHM=INPLACE NOTE: If the server is killed after the table-rebuilding ALGORITHM=INPLACE commits inside InnoDB but before the .frm file has been replaced, then the recovery will involve something else than DROP TABLE. NOTE: If the server is killed in a true inplace ALTER TABLE commits inside InnoDB but before the .frm file has been replaced, then we are really out of luck. To properly handle that situation, we would need a transactional mysql.ddl_fixup table that directs recovery to rename or remove files. prepare_inplace_alter_table_dict(): Use the altered_table->s->table_name for generating the new_table_name. table_name_t::part_suffix: The start of the partition name suffix. table_name_t::dbend(): Return the end of the schema name. table_name_t::dblen(): Return the length of the schema name, in bytes. table_name_t::basename(): Return the name without the schema name. table_name_t::part(): Return the partition name, or NULL if none. row_drop_table_for_mysql(): Assert for #sql, not #sql-ib. --- .../innodb/r/innodb-alter-tempfile.result | 16 +------ mysql-test/suite/innodb/t/alter_crash.test | 16 ++++--- .../suite/innodb/t/innodb-alter-tempfile.test | 47 ++++++------------- storage/innobase/dict/dict0mem.cc | 8 ++++ storage/innobase/handler/ha_innodb.cc | 6 +-- storage/innobase/handler/handler0alter.cc | 17 +++++-- storage/innobase/include/dict0mem.h | 23 +++++++++ storage/innobase/row/row0mysql.cc | 4 +- 8 files changed, 70 insertions(+), 67 deletions(-) diff --git a/mysql-test/suite/innodb/r/innodb-alter-tempfile.result b/mysql-test/suite/innodb/r/innodb-alter-tempfile.result index ce13ad0978b..b164c3c26b0 100644 --- a/mysql-test/suite/innodb/r/innodb-alter-tempfile.result +++ b/mysql-test/suite/innodb/r/innodb-alter-tempfile.result @@ -4,17 +4,10 @@ # Temporary tablename will be unique. This makes sure that future # in-place ALTERs of the same table will not be blocked due to # temporary tablename. -# Crash the server in ha_innobase::commit_inplace_alter_table() CREATE TABLE t1 (f1 INT NOT NULL, f2 INT NOT NULL) ENGINE=innodb; -SET debug='d,innodb_alter_commit_crash_before_commit'; -Warnings: -Warning 1287 '@@debug' is deprecated and will be removed in a future release. Please use '@@debug_dbug' instead -# Write file to make mysql-test-run.pl expect crash -# Execute the statement that causes the crash +SET debug_dbug='+d,innodb_alter_commit_crash_before_commit'; ALTER TABLE t1 ADD PRIMARY KEY (f2, f1); ERROR HY000: Lost connection to MySQL server during query -# Startup the server after the crash -# Read and remember the temporary table name show create table t1; Table Create Table t1 CREATE TABLE `t1` ( @@ -23,13 +16,6 @@ t1 CREATE TABLE `t1` ( ) ENGINE=InnoDB DEFAULT CHARSET=latin1 # Consecutive Alter table does not create same temporary file name ALTER TABLE t1 ADD PRIMARY KEY (f2, f1); -# Shutdown the server to allow manual recovery -# Manual recovery begin. The dictionary was not updated -# and the files were not renamed. The rebuilt table -# was left behind on purpose, to faciliate data recovery. -# Manual recovery end -# Startup the server after manual recovery -# Drop the orphaned rebuilt table. show create table t1; Table Create Table t1 CREATE TABLE `t1` ( diff --git a/mysql-test/suite/innodb/t/alter_crash.test b/mysql-test/suite/innodb/t/alter_crash.test index 54cc51aecf4..b4fdfd2f2d5 100644 --- a/mysql-test/suite/innodb/t/alter_crash.test +++ b/mysql-test/suite/innodb/t/alter_crash.test @@ -138,24 +138,27 @@ ALTER TABLE t2 ADD PRIMARY KEY (f2, f1); let $temp_table_name = `SELECT SUBSTRING(name,6) FROM information_schema.innodb_sys_tables WHERE name LIKE "test/#sql-ib$orig_table_id%"`; -# This second copy is an environment variable for the perl script below. -let temp_table_name = $temp_table_name; --echo # Manual *.frm recovery begin. The dictionary was not updated --echo # and the files were not renamed. The rebuilt table --echo # was left behind on purpose, to faciliate data recovery. +let TABLENAME_INC= $MYSQLTEST_VARDIR/tmp/tablename.inc; perl; -my @frm_file = glob "$ENV{'datadir'}/test/#sql-*.frm"; -my $target_frm = "$ENV{'datadir'}/test/$ENV{'temp_table_name'}.frm"; -rename($frm_file[0], $target_frm); +die unless open OUT, ">$ENV{TABLENAME_INC}"; +chdir "$ENV{'datadir'}/test"; +my @frm_file = map { substr($_, 0, -4) } glob "#sql-*.frm"; +print OUT 'let $tablename=', $frm_file[0], ';'; +close OUT or die; EOF +source $TABLENAME_INC; +remove_file $TABLENAME_INC; --echo # Manual recovery end --echo # Drop the orphaned rebuilt table. --disable_query_log -eval DROP TABLE `#mysql50#$temp_table_name`; +eval DROP TABLE `#mysql50#$tablename`; --enable_query_log SHOW TABLES; @@ -186,7 +189,6 @@ SET DEBUG_DBUG='+d,innodb_alter_commit_crash_after_commit'; let $orig_table_id = `select table_id from information_schema.innodb_sys_tables where name = 'test/t1'`; -# FIXME: MDEV-9469 'Incorrect key file' on ALTER TABLE # Write file to make mysql-test-run.pl expect crash --exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect # diff --git a/mysql-test/suite/innodb/t/innodb-alter-tempfile.test b/mysql-test/suite/innodb/t/innodb-alter-tempfile.test index d3f34b12ea6..f2038da8c4c 100644 --- a/mysql-test/suite/innodb/t/innodb-alter-tempfile.test +++ b/mysql-test/suite/innodb/t/innodb-alter-tempfile.test @@ -24,49 +24,30 @@ let datadir= `select @@datadir`; --let $_server_id= `SELECT @@server_id` --let $_expect_file_name=$MYSQLTEST_VARDIR/tmp/mysqld.$_server_id.expect ---echo # Crash the server in ha_innobase::commit_inplace_alter_table() CREATE TABLE t1 (f1 INT NOT NULL, f2 INT NOT NULL) ENGINE=innodb; -SET debug='d,innodb_alter_commit_crash_before_commit'; +SET debug_dbug='+d,innodb_alter_commit_crash_before_commit'; -let $orig_table_id = `SELECT table_id - FROM information_schema.innodb_sys_tables - WHERE name = 'test/t1'`; +--exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect ---echo # Write file to make mysql-test-run.pl expect crash ---exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect - ---echo # Execute the statement that causes the crash --error 2013 ALTER TABLE t1 ADD PRIMARY KEY (f2, f1); ---echo # Startup the server after the crash + +let TABLENAME_INC= $MYSQLTEST_VARDIR/tmp/tablename.inc; +perl; +die unless open OUT, ">$ENV{TABLENAME_INC}"; +chdir "$ENV{'datadir'}/test"; +my @frm_file = map { substr($_, 0, -4) } glob "#sql-*.frm"; +print OUT 'let $temp_table_name=', $frm_file[0], ';'; +close OUT or die; +EOF +source $TABLENAME_INC; +remove_file $TABLENAME_INC; + --source include/start_mysqld.inc ---echo # Read and remember the temporary table name -let $temp_table_name = `SELECT SUBSTRING(name,6) - FROM information_schema.innodb_sys_tables - WHERE name LIKE "test/#sql-ib$orig_table_id%"`; -# This second copy is an environment variable for the perl script below. -let temp_table_name = $temp_table_name; show create table t1; --echo # Consecutive Alter table does not create same temporary file name ALTER TABLE t1 ADD PRIMARY KEY (f2, f1); ---echo # Shutdown the server to allow manual recovery ---source include/shutdown_mysqld.inc - ---echo # Manual recovery begin. The dictionary was not updated ---echo # and the files were not renamed. The rebuilt table ---echo # was left behind on purpose, to faciliate data recovery. - -perl; -my @frm_file = glob "$ENV{'datadir'}/test/#sql-*.frm"; -my $target_frm = "$ENV{'datadir'}/test/$ENV{'temp_table_name'}.frm"; -rename($frm_file[0], $target_frm); -EOF ---echo # Manual recovery end ---echo # Startup the server after manual recovery ---source include/start_mysqld.inc - ---echo # Drop the orphaned rebuilt table. --disable_query_log eval DROP TABLE `#mysql50#$temp_table_name`; --enable_query_log diff --git a/storage/innobase/dict/dict0mem.cc b/storage/innobase/dict/dict0mem.cc index 0808cc61f28..86a2e025c8f 100644 --- a/storage/innobase/dict/dict0mem.cc +++ b/storage/innobase/dict/dict0mem.cc @@ -50,6 +50,14 @@ static const char* innobase_system_databases[] = { NullS }; +/** The start of the table basename suffix for partitioned tables */ +const char table_name_t::part_suffix[4] +#ifdef _WIN32 += "#p#"; +#else += "#P#"; +#endif + /** An interger randomly initialized at startup used to make a temporary table name as unuique as possible. */ static ib_uint32_t dict_temp_file_num; diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 1fade781612..1c7489f3392 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -299,11 +299,7 @@ is_partition( { /* We look for pattern #P# to see if the table is partitioned MariaDB table. */ -#ifdef _WIN32 - return strstr(file_name, "#p#"); -#else - return strstr(file_name, "#P#"); -#endif /* _WIN32 */ + return strstr(file_name, table_name_t::part_suffix); } /** Signal to shut down InnoDB (NULL if shutdown was signaled, or if diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc index 0ff82a530c4..4d630f9f731 100644 --- a/storage/innobase/handler/handler0alter.cc +++ b/storage/innobase/handler/handler0alter.cc @@ -4514,11 +4514,18 @@ prepare_inplace_alter_table_dict( to rebuild the table with a temporary name. */ if (new_clustered) { - const char* new_table_name - = dict_mem_create_temporary_tablename( - ctx->heap, - ctx->new_table->name.m_name, - ctx->new_table->id); + size_t dblen = ctx->old_table->name.dblen() + 1; + size_t tablen = altered_table->s->table_name.length; + const char* part = ctx->old_table->name.part(); + size_t partlen = part ? strlen(part) : 0; + char* new_table_name = static_cast( + mem_heap_alloc(ctx->heap, + dblen + tablen + partlen + 1)); + memcpy(new_table_name, ctx->old_table->name.m_name, dblen); + memcpy(new_table_name + dblen, + altered_table->s->table_name.str, tablen); + memcpy(new_table_name + dblen + tablen, + part ? part : "", partlen + 1); ulint n_cols = 0; ulint n_v_cols = 0; dtuple_t* add_cols; diff --git a/storage/innobase/include/dict0mem.h b/storage/innobase/include/dict0mem.h index fd3e46b638c..b3bf55f7a74 100644 --- a/storage/innobase/include/dict0mem.h +++ b/storage/innobase/include/dict0mem.h @@ -565,6 +565,29 @@ struct table_name_t { /** The name in internal representation */ char* m_name; + + /** @return the end of the schema name */ + const char* dbend() const + { + const char* sep = strchr(m_name, '/'); + ut_ad(sep); + return sep; + } + + /** @return the length of the schema name, in bytes */ + size_t dblen() const { return dbend() - m_name; } + + /** Determine the filename-safe encoded table name. + @return the filename-safe encoded table name */ + const char* basename() const { return dbend() + 1; } + + /** The start of the table basename suffix for partitioned tables */ + static const char part_suffix[4]; + + /** Determine the partition or subpartition name suffix. + @return the partition name + @retval NULL if the table is not partitioned */ + const char* part() const { return strstr(basename(), part_suffix); } }; /** Data structure for a column in a table */ diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc index b5e4fa9b8a3..d9bc6dac34b 100644 --- a/storage/innobase/row/row0mysql.cc +++ b/storage/innobase/row/row0mysql.cc @@ -3814,8 +3814,8 @@ row_drop_table_for_mysql( TRX_DICT_OP_INDEX, we should be dropping auxiliary tables for full-text indexes or temp tables. */ ut_ad(strstr(table->name.m_name, "/FTS_") != NULL - || strstr(table->name.m_name, TEMP_FILE_PREFIX_INNODB) - != NULL); + || strstr(table->name.m_name, + TEMP_TABLE_PATH_PREFIX) != NULL); } /* Mark all indexes unavailable in the data dictionary cache From 0e5eef886ac281599da2caa24e0a560b4f889c7d Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Fri, 8 Dec 2017 13:19:19 +0400 Subject: [PATCH 11/32] MDEV-14350 Index use with collation utf8mb4_unicode_nopad_ci on LIKE pattern with wrong results --- .../include/ctype_like_range_mdev14350.inc | 14 ++++++ mysql-test/r/ctype_latin1.result | 25 +++++++++++ mysql-test/r/ctype_like_range.result | 45 +++++++++++++++++++ mysql-test/r/ctype_ucs2_uca.result | 26 +++++++++++ mysql-test/r/ctype_utf16_uca.result | 26 +++++++++++ mysql-test/r/ctype_utf32_uca.result | 26 +++++++++++ mysql-test/r/ctype_utf8_uca.result | 25 +++++++++++ mysql-test/r/ctype_utf8mb4_uca.result | 26 +++++++++++ mysql-test/t/ctype_latin1.test | 3 ++ mysql-test/t/ctype_like_range.test | 40 +++++++++++++++++ mysql-test/t/ctype_ucs2_uca.test | 4 ++ mysql-test/t/ctype_utf16_uca.test | 5 +++ mysql-test/t/ctype_utf32_uca.test | 5 +++ mysql-test/t/ctype_utf8_uca.test | 3 ++ mysql-test/t/ctype_utf8mb4_uca.test | 5 +++ strings/ctype-mb.c | 10 ++--- strings/ctype-simple.c | 4 +- 17 files changed, 285 insertions(+), 7 deletions(-) create mode 100644 mysql-test/include/ctype_like_range_mdev14350.inc diff --git a/mysql-test/include/ctype_like_range_mdev14350.inc b/mysql-test/include/ctype_like_range_mdev14350.inc new file mode 100644 index 00000000000..89ecc0201cd --- /dev/null +++ b/mysql-test/include/ctype_like_range_mdev14350.inc @@ -0,0 +1,14 @@ +--echo # +--echo # MDEV-14350 Index use with collation utf8mb4_unicode_nopad_ci on LIKE pattern with wrong results +--echo # + +CREATE OR REPLACE TABLE t1 AS SELECT SPACE(50) AS a, SPACE (50) AS b; +ALTER TABLE t1 ADD KEY(a), ADD KEY(b); +SHOW CREATE TABLE t1; +INSERT INTO t1 VALUES ('111', '111'); +INSERT INTO t1 VALUES ('222', '222'); +INSERT INTO t1 VALUES ('333', '333'); +INSERT INTO t1 VALUES ('444', '444'); +SELECT * FROM t1 WHERE a LIKE '111%'; +SELECT * FROM t1 IGNORE INDEX (a) WHERE a LIKE '111%'; +DROP TABLE t1; diff --git a/mysql-test/r/ctype_latin1.result b/mysql-test/r/ctype_latin1.result index 0ca643f25fb..b60d711568c 100644 --- a/mysql-test/r/ctype_latin1.result +++ b/mysql-test/r/ctype_latin1.result @@ -8804,6 +8804,31 @@ DROP TABLE t1; # End of ctype_pad.inc # SET STORAGE_ENGINE=Default; +SET NAMES latin1; +# +# MDEV-14350 Index use with collation utf8mb4_unicode_nopad_ci on LIKE pattern with wrong results +# +CREATE OR REPLACE TABLE t1 AS SELECT SPACE(50) AS a, SPACE (50) AS b; +ALTER TABLE t1 ADD KEY(a), ADD KEY(b); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` varchar(50) DEFAULT NULL, + `b` varchar(50) DEFAULT NULL, + KEY `a` (`a`), + KEY `b` (`b`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +INSERT INTO t1 VALUES ('111', '111'); +INSERT INTO t1 VALUES ('222', '222'); +INSERT INTO t1 VALUES ('333', '333'); +INSERT INTO t1 VALUES ('444', '444'); +SELECT * FROM t1 WHERE a LIKE '111%'; +a b +111 111 +SELECT * FROM t1 IGNORE INDEX (a) WHERE a LIKE '111%'; +a b +111 111 +DROP TABLE t1; # # End of 10.2 tests # diff --git a/mysql-test/r/ctype_like_range.result b/mysql-test/r/ctype_like_range.result index f8032d0ae86..d8e621fd056 100644 --- a/mysql-test/r/ctype_like_range.result +++ b/mysql-test/r/ctype_like_range.result @@ -4430,5 +4430,50 @@ a_ 6100 61FF a% 61000000000000000000 61FFFFFFFFFFFFFFFFFF DROP TABLE t1; # +# MDEV-14350 Index use with collation utf8mb4_unicode_nopad_ci on LIKE pattern with wrong results +# +CREATE TABLE t1 (a VARCHAR(10) CHARACTER SET latin1 COLLATE latin1_swedish_nopad_ci); +INSERT INTO t1 VALUES ('111%'); +SELECT a, HEX(LIKE_RANGE_MIN(a,200)) FROM t1; +a HEX(LIKE_RANGE_MIN(a,200)) +111% 313131 +DROP TABLE t1; +CREATE TABLE t1 (a VARCHAR(10) CHARACTER SET utf8 COLLATE utf8_general_nopad_ci); +INSERT INTO t1 VALUES ('111%'); +SELECT a, HEX(LIKE_RANGE_MIN(a,200)) FROM t1; +a HEX(LIKE_RANGE_MIN(a,200)) +111% 313131 +DROP TABLE t1; +CREATE TABLE t1 (a VARCHAR(10) CHARACTER SET utf8 COLLATE utf8_unicode_nopad_ci); +INSERT INTO t1 VALUES ('111%'); +SELECT a, HEX(LIKE_RANGE_MIN(a,200)) FROM t1; +a HEX(LIKE_RANGE_MIN(a,200)) +111% 313131 +DROP TABLE t1; +CREATE TABLE t1 (a VARCHAR(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_nopad_ci); +INSERT INTO t1 VALUES ('111%'); +SELECT a, HEX(LIKE_RANGE_MIN(a,200)) FROM t1; +a HEX(LIKE_RANGE_MIN(a,200)) +111% 313131 +DROP TABLE t1; +CREATE TABLE t1 (a VARCHAR(10) CHARACTER SET ucs2 COLLATE ucs2_unicode_nopad_ci); +INSERT INTO t1 VALUES ('111%'); +SELECT a, HEX(LIKE_RANGE_MIN(a,200)) FROM t1; +a HEX(LIKE_RANGE_MIN(a,200)) +111% 003100310031 +DROP TABLE t1; +CREATE TABLE t1 (a VARCHAR(10) CHARACTER SET utf16 COLLATE utf16_unicode_nopad_ci); +INSERT INTO t1 VALUES ('111%'); +SELECT a, HEX(LIKE_RANGE_MIN(a,200)) FROM t1; +a HEX(LIKE_RANGE_MIN(a,200)) +111% 003100310031 +DROP TABLE t1; +CREATE TABLE t1 (a VARCHAR(10) CHARACTER SET utf32 COLLATE utf32_unicode_nopad_ci); +INSERT INTO t1 VALUES ('111%'); +SELECT a, HEX(LIKE_RANGE_MIN(a,200)) FROM t1; +a HEX(LIKE_RANGE_MIN(a,200)) +111% 000000310000003100000031 +DROP TABLE t1; +# # End of 10.2 tests # diff --git a/mysql-test/r/ctype_ucs2_uca.result b/mysql-test/r/ctype_ucs2_uca.result index 48ec25b32e3..44a623842c6 100644 --- a/mysql-test/r/ctype_ucs2_uca.result +++ b/mysql-test/r/ctype_ucs2_uca.result @@ -559,6 +559,32 @@ DROP TABLE t1; # End of ctype_pad.inc # SET STORAGE_ENGINE=Default; +SET NAMES utf8, collation_connection=ucs2_unicode_520_nopad_ci; +# +# MDEV-14350 Index use with collation utf8mb4_unicode_nopad_ci on LIKE pattern with wrong results +# +CREATE OR REPLACE TABLE t1 AS SELECT SPACE(50) AS a, SPACE (50) AS b; +ALTER TABLE t1 ADD KEY(a), ADD KEY(b); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` varchar(50) CHARACTER SET ucs2 COLLATE ucs2_unicode_520_nopad_ci DEFAULT NULL, + `b` varchar(50) CHARACTER SET ucs2 COLLATE ucs2_unicode_520_nopad_ci DEFAULT NULL, + KEY `a` (`a`), + KEY `b` (`b`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +INSERT INTO t1 VALUES ('111', '111'); +INSERT INTO t1 VALUES ('222', '222'); +INSERT INTO t1 VALUES ('333', '333'); +INSERT INTO t1 VALUES ('444', '444'); +SELECT * FROM t1 WHERE a LIKE '111%'; +a b +111 111 +SELECT * FROM t1 IGNORE INDEX (a) WHERE a LIKE '111%'; +a b +111 111 +DROP TABLE t1; +SET NAMES utf8; # # End of 10.2 tests # diff --git a/mysql-test/r/ctype_utf16_uca.result b/mysql-test/r/ctype_utf16_uca.result index 1105225ed87..0cb9c4c74c1 100644 --- a/mysql-test/r/ctype_utf16_uca.result +++ b/mysql-test/r/ctype_utf16_uca.result @@ -7866,6 +7866,32 @@ DROP TABLE t1; # End of ctype_pad.inc # SET STORAGE_ENGINE=Default; +SET NAMES utf8, collation_connection=utf16_unicode_520_nopad_ci; +# +# MDEV-14350 Index use with collation utf8mb4_unicode_nopad_ci on LIKE pattern with wrong results +# +CREATE OR REPLACE TABLE t1 AS SELECT SPACE(50) AS a, SPACE (50) AS b; +ALTER TABLE t1 ADD KEY(a), ADD KEY(b); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` varchar(50) CHARACTER SET utf16 COLLATE utf16_unicode_520_nopad_ci DEFAULT NULL, + `b` varchar(50) CHARACTER SET utf16 COLLATE utf16_unicode_520_nopad_ci DEFAULT NULL, + KEY `a` (`a`), + KEY `b` (`b`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +INSERT INTO t1 VALUES ('111', '111'); +INSERT INTO t1 VALUES ('222', '222'); +INSERT INTO t1 VALUES ('333', '333'); +INSERT INTO t1 VALUES ('444', '444'); +SELECT * FROM t1 WHERE a LIKE '111%'; +a b +111 111 +SELECT * FROM t1 IGNORE INDEX (a) WHERE a LIKE '111%'; +a b +111 111 +DROP TABLE t1; +SET NAMES utf8; # # End of 10.2 tests # diff --git a/mysql-test/r/ctype_utf32_uca.result b/mysql-test/r/ctype_utf32_uca.result index 097da3d7c16..a112918c0c3 100644 --- a/mysql-test/r/ctype_utf32_uca.result +++ b/mysql-test/r/ctype_utf32_uca.result @@ -7886,6 +7886,32 @@ DROP TABLE t1; # End of ctype_pad.inc # SET STORAGE_ENGINE=Default; +SET NAMES utf8, collation_connection=utf32_unicode_520_nopad_ci; +# +# MDEV-14350 Index use with collation utf8mb4_unicode_nopad_ci on LIKE pattern with wrong results +# +CREATE OR REPLACE TABLE t1 AS SELECT SPACE(50) AS a, SPACE (50) AS b; +ALTER TABLE t1 ADD KEY(a), ADD KEY(b); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` varchar(50) CHARACTER SET utf32 COLLATE utf32_unicode_520_nopad_ci DEFAULT NULL, + `b` varchar(50) CHARACTER SET utf32 COLLATE utf32_unicode_520_nopad_ci DEFAULT NULL, + KEY `a` (`a`), + KEY `b` (`b`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +INSERT INTO t1 VALUES ('111', '111'); +INSERT INTO t1 VALUES ('222', '222'); +INSERT INTO t1 VALUES ('333', '333'); +INSERT INTO t1 VALUES ('444', '444'); +SELECT * FROM t1 WHERE a LIKE '111%'; +a b +111 111 +SELECT * FROM t1 IGNORE INDEX (a) WHERE a LIKE '111%'; +a b +111 111 +DROP TABLE t1; +SET NAMES utf8; # # End of 10.2 tests # diff --git a/mysql-test/r/ctype_utf8_uca.result b/mysql-test/r/ctype_utf8_uca.result index c8107da3b6f..23e9802b61a 100644 --- a/mysql-test/r/ctype_utf8_uca.result +++ b/mysql-test/r/ctype_utf8_uca.result @@ -559,6 +559,31 @@ DROP TABLE t1; # End of ctype_pad.inc # SET STORAGE_ENGINE=Default; +SET NAMES utf8 COLLATE utf8_unicode_nopad_ci; +# +# MDEV-14350 Index use with collation utf8mb4_unicode_nopad_ci on LIKE pattern with wrong results +# +CREATE OR REPLACE TABLE t1 AS SELECT SPACE(50) AS a, SPACE (50) AS b; +ALTER TABLE t1 ADD KEY(a), ADD KEY(b); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_nopad_ci DEFAULT NULL, + `b` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_nopad_ci DEFAULT NULL, + KEY `a` (`a`), + KEY `b` (`b`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +INSERT INTO t1 VALUES ('111', '111'); +INSERT INTO t1 VALUES ('222', '222'); +INSERT INTO t1 VALUES ('333', '333'); +INSERT INTO t1 VALUES ('444', '444'); +SELECT * FROM t1 WHERE a LIKE '111%'; +a b +111 111 +SELECT * FROM t1 IGNORE INDEX (a) WHERE a LIKE '111%'; +a b +111 111 +DROP TABLE t1; # # End of 10.2 tests # diff --git a/mysql-test/r/ctype_utf8mb4_uca.result b/mysql-test/r/ctype_utf8mb4_uca.result index ca7f1a3d7d4..0712ca63abd 100644 --- a/mysql-test/r/ctype_utf8mb4_uca.result +++ b/mysql-test/r/ctype_utf8mb4_uca.result @@ -6576,6 +6576,32 @@ DROP TABLE t1; # End of ctype_pad.inc # SET STORAGE_ENGINE=Default; +SET NAMES utf8mb4 COLLATE utf8mb4_unicode_520_nopad_ci; +# +# MDEV-14350 Index use with collation utf8mb4_unicode_nopad_ci on LIKE pattern with wrong results +# +CREATE OR REPLACE TABLE t1 AS SELECT SPACE(50) AS a, SPACE (50) AS b; +ALTER TABLE t1 ADD KEY(a), ADD KEY(b); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_nopad_ci DEFAULT NULL, + `b` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_nopad_ci DEFAULT NULL, + KEY `a` (`a`), + KEY `b` (`b`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +INSERT INTO t1 VALUES ('111', '111'); +INSERT INTO t1 VALUES ('222', '222'); +INSERT INTO t1 VALUES ('333', '333'); +INSERT INTO t1 VALUES ('444', '444'); +SELECT * FROM t1 WHERE a LIKE '111%'; +a b +111 111 +SELECT * FROM t1 IGNORE INDEX (a) WHERE a LIKE '111%'; +a b +111 111 +DROP TABLE t1; +SET NAMES utf8mb4; # # End of 10.2 tests # diff --git a/mysql-test/t/ctype_latin1.test b/mysql-test/t/ctype_latin1.test index 8c51bfef2d8..31d4ff7f802 100644 --- a/mysql-test/t/ctype_latin1.test +++ b/mysql-test/t/ctype_latin1.test @@ -411,6 +411,9 @@ let $coll='latin1_nopad_bin'; let $coll_pad='latin1_bin'; --source include/ctype_pad_all_engines.inc +SET NAMES latin1; +--source include/ctype_like_range_mdev14350.inc + --echo # --echo # End of 10.2 tests --echo # diff --git a/mysql-test/t/ctype_like_range.test b/mysql-test/t/ctype_like_range.test index e8784990d36..3055abe5f59 100644 --- a/mysql-test/t/ctype_like_range.test +++ b/mysql-test/t/ctype_like_range.test @@ -154,6 +154,46 @@ INSERT INTO t1 (a) VALUES ('a'),('a_'),('a%'); SELECT a, HEX(mn), HEX(mx) FROM t1; DROP TABLE t1; +--echo # +--echo # MDEV-14350 Index use with collation utf8mb4_unicode_nopad_ci on LIKE pattern with wrong results +--echo # + +CREATE TABLE t1 (a VARCHAR(10) CHARACTER SET latin1 COLLATE latin1_swedish_nopad_ci); +INSERT INTO t1 VALUES ('111%'); +SELECT a, HEX(LIKE_RANGE_MIN(a,200)) FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 (a VARCHAR(10) CHARACTER SET utf8 COLLATE utf8_general_nopad_ci); +INSERT INTO t1 VALUES ('111%'); +SELECT a, HEX(LIKE_RANGE_MIN(a,200)) FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 (a VARCHAR(10) CHARACTER SET utf8 COLLATE utf8_unicode_nopad_ci); +INSERT INTO t1 VALUES ('111%'); +SELECT a, HEX(LIKE_RANGE_MIN(a,200)) FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 (a VARCHAR(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_nopad_ci); +INSERT INTO t1 VALUES ('111%'); +SELECT a, HEX(LIKE_RANGE_MIN(a,200)) FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 (a VARCHAR(10) CHARACTER SET ucs2 COLLATE ucs2_unicode_nopad_ci); +INSERT INTO t1 VALUES ('111%'); +SELECT a, HEX(LIKE_RANGE_MIN(a,200)) FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 (a VARCHAR(10) CHARACTER SET utf16 COLLATE utf16_unicode_nopad_ci); +INSERT INTO t1 VALUES ('111%'); +SELECT a, HEX(LIKE_RANGE_MIN(a,200)) FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 (a VARCHAR(10) CHARACTER SET utf32 COLLATE utf32_unicode_nopad_ci); +INSERT INTO t1 VALUES ('111%'); +SELECT a, HEX(LIKE_RANGE_MIN(a,200)) FROM t1; +DROP TABLE t1; + + --echo # --echo # End of 10.2 tests --echo # diff --git a/mysql-test/t/ctype_ucs2_uca.test b/mysql-test/t/ctype_ucs2_uca.test index bc6d6150ee6..0aed0956f6c 100644 --- a/mysql-test/t/ctype_ucs2_uca.test +++ b/mysql-test/t/ctype_ucs2_uca.test @@ -16,6 +16,10 @@ let $coll='ucs2_unicode_520_nopad_ci'; let $coll_pad='ucs2_unicode_520_ci'; --source include/ctype_pad_all_engines.inc +SET NAMES utf8, collation_connection=ucs2_unicode_520_nopad_ci; +--source include/ctype_like_range_mdev14350.inc +SET NAMES utf8; + --echo # --echo # End of 10.2 tests --echo # diff --git a/mysql-test/t/ctype_utf16_uca.test b/mysql-test/t/ctype_utf16_uca.test index 95ce74076d0..46d572fbe81 100644 --- a/mysql-test/t/ctype_utf16_uca.test +++ b/mysql-test/t/ctype_utf16_uca.test @@ -238,6 +238,11 @@ let $coll='utf16_unicode_520_nopad_ci'; let $coll_pad='utf16_unicode_520_ci'; --source include/ctype_pad_all_engines.inc +SET NAMES utf8, collation_connection=utf16_unicode_520_nopad_ci; +--source include/ctype_like_range_mdev14350.inc +SET NAMES utf8; + + --echo # --echo # End of 10.2 tests --echo # diff --git a/mysql-test/t/ctype_utf32_uca.test b/mysql-test/t/ctype_utf32_uca.test index e5eb3b6d881..334d8fd1d48 100644 --- a/mysql-test/t/ctype_utf32_uca.test +++ b/mysql-test/t/ctype_utf32_uca.test @@ -260,6 +260,11 @@ let $coll='utf32_unicode_520_nopad_ci'; let $coll_pad='utf32_unicode_520_ci'; --source include/ctype_pad_all_engines.inc +SET NAMES utf8, collation_connection=utf32_unicode_520_nopad_ci; +--source include/ctype_like_range_mdev14350.inc +SET NAMES utf8; + + --echo # --echo # End of 10.2 tests --echo # diff --git a/mysql-test/t/ctype_utf8_uca.test b/mysql-test/t/ctype_utf8_uca.test index 670998a367b..0879b4d2810 100644 --- a/mysql-test/t/ctype_utf8_uca.test +++ b/mysql-test/t/ctype_utf8_uca.test @@ -14,6 +14,9 @@ let $coll='utf8_unicode_520_nopad_ci'; let $coll_pad='utf8_unicode_520_ci'; --source include/ctype_pad_all_engines.inc +SET NAMES utf8 COLLATE utf8_unicode_nopad_ci; +--source include/ctype_like_range_mdev14350.inc + --echo # --echo # End of 10.2 tests diff --git a/mysql-test/t/ctype_utf8mb4_uca.test b/mysql-test/t/ctype_utf8mb4_uca.test index fe76ed45e3f..160cb48bad6 100644 --- a/mysql-test/t/ctype_utf8mb4_uca.test +++ b/mysql-test/t/ctype_utf8mb4_uca.test @@ -100,6 +100,11 @@ let $coll='utf8mb4_unicode_520_nopad_ci'; let $coll_pad='utf8mb4_unicode_520_ci'; --source include/ctype_pad_all_engines.inc +SET NAMES utf8mb4 COLLATE utf8mb4_unicode_520_nopad_ci; +--source include/ctype_like_range_mdev14350.inc +SET NAMES utf8mb4; + + --echo # --echo # End of 10.2 tests --echo # diff --git a/strings/ctype-mb.c b/strings/ctype-mb.c index 9e476c23202..bfd533e6f98 100644 --- a/strings/ctype-mb.c +++ b/strings/ctype-mb.c @@ -799,9 +799,9 @@ fill_max_and_min: 'a\0\0... is the smallest possible string when we have space expand a\ff\ff... is the biggest possible string */ - *min_length= ((cs->state & MY_CS_BINSORT) ? (size_t) (min_str - min_org) : - res_length); - *max_length= res_length; + *min_length= (cs->state & (MY_CS_BINSORT | MY_CS_NOPAD)) ? + (size_t) (min_str - min_org) : + res_length; /* Create min key */ do { @@ -1002,9 +1002,9 @@ my_like_range_generic(CHARSET_INFO *cs, a\min\min... is the smallest possible string a\max\max... is the biggest possible string */ - *min_length= ((cs->state & MY_CS_BINSORT) ? + *min_length= (cs->state & (MY_CS_BINSORT | MY_CS_NOPAD)) ? (size_t) (min_str - min_org) : - res_length); + res_length; *max_length= res_length; goto pad_min_max; } diff --git a/strings/ctype-simple.c b/strings/ctype-simple.c index cf73f117f0f..fc5218caba1 100644 --- a/strings/ctype-simple.c +++ b/strings/ctype-simple.c @@ -1059,9 +1059,9 @@ my_bool my_like_range_simple(CHARSET_INFO *cs, if (*ptr == w_many) /* '%' in SQL */ { /* Calculate length of keys */ - *min_length= ((cs->state & MY_CS_BINSORT) ? + *min_length= (cs->state & (MY_CS_BINSORT | MY_CS_NOPAD)) ? (size_t) (min_str - min_org) : - res_length); + res_length; *max_length= res_length; do { From 2662228d1836c8af36b915b1658924b272033150 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Lindstr=C3=B6m?= Date: Fri, 8 Dec 2017 13:33:11 +0200 Subject: [PATCH 12/32] Fix test failures. --- mysql-test/suite/sys_vars/r/sysvars_innodb.result | 2 +- mysql-test/suite/sys_vars/t/wsrep_on_basic.opt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 mysql-test/suite/sys_vars/t/wsrep_on_basic.opt diff --git a/mysql-test/suite/sys_vars/r/sysvars_innodb.result b/mysql-test/suite/sys_vars/r/sysvars_innodb.result index 9d1fe2786ba..c79b5e4de34 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_innodb.result +++ b/mysql-test/suite/sys_vars/r/sysvars_innodb.result @@ -1614,7 +1614,7 @@ NUMERIC_MIN_VALUE NULL NUMERIC_MAX_VALUE NULL NUMERIC_BLOCK_SIZE NULL ENUM_VALUE_LIST fcfs,vats -READ_ONLY NO +READ_ONLY YES COMMAND_LINE_ARGUMENT REQUIRED VARIABLE_NAME INNODB_LOCK_WAIT_TIMEOUT SESSION_VALUE 50 diff --git a/mysql-test/suite/sys_vars/t/wsrep_on_basic.opt b/mysql-test/suite/sys_vars/t/wsrep_on_basic.opt new file mode 100644 index 00000000000..aa1fb6cb155 --- /dev/null +++ b/mysql-test/suite/sys_vars/t/wsrep_on_basic.opt @@ -0,0 +1 @@ +--innodb-lock-schedule-algorithm=FCFS From b3346c2f41d0f7bd0d919826592b37ae09fa41c8 Mon Sep 17 00:00:00 2001 From: Sergey Vojtovich Date: Thu, 7 Dec 2017 15:03:59 +0200 Subject: [PATCH 13/32] Restore LF_BACKOFF Moved InnoDB UT_RELAX_CPU() to server. Restored cross-platform LF_BACKOFF implementation basing on UT_RELAX_CPU(). --- include/atomic/generic-msvc.h | 30 ---------------- include/lf.h | 1 + include/my_atomic.h | 9 ----- include/my_cpu.h | 57 ++++++++++++++++++++++++++++++ mysys/lf_alloc-pin.c | 4 +-- mysys/lf_hash.c | 6 ++-- mysys/waiting_threads.c | 2 +- storage/innobase/include/os0once.h | 3 +- storage/innobase/include/ut0ut.h | 29 --------------- storage/innobase/ut/ut0ut.cc | 2 +- 10 files changed, 67 insertions(+), 76 deletions(-) diff --git a/include/atomic/generic-msvc.h b/include/atomic/generic-msvc.h index 6c5272c98f4..8daa497036f 100644 --- a/include/atomic/generic-msvc.h +++ b/include/atomic/generic-msvc.h @@ -90,37 +90,7 @@ C_MODE_END ret= 0; /* avoid compiler warning */ \ ret= IL_COMP_EXCHG ## S (a, ret, ret); #endif -/* - my_yield_processor (equivalent of x86 PAUSE instruction) should be used - to improve performance on hyperthreaded CPUs. Intel recommends to use it in - spin loops also on non-HT machines to reduce power consumption (see e.g - http://softwarecommunity.intel.com/articles/eng/2004.htm) - Running benchmarks for spinlocks implemented with InterlockedCompareExchange - and YieldProcessor shows that much better performance is achieved by calling - YieldProcessor in a loop - that is, yielding longer. On Intel boxes setting - loop count in the range 200-300 brought best results. - */ -#ifndef YIELD_LOOPS -#define YIELD_LOOPS 200 -#endif - -static __inline int my_yield_processor() -{ - int i; - for(i=0; i +#include C_MODE_START diff --git a/include/my_atomic.h b/include/my_atomic.h index 8f13a0ab89b..c6abcda2d62 100644 --- a/include/my_atomic.h +++ b/include/my_atomic.h @@ -346,15 +346,6 @@ make_atomic_store(ptr) #undef make_atomic_fas_body #undef intptr -/* - the macro below defines (as an expression) the code that - will be run in spin-loops. Intel manuals recummend to have PAUSE there. - It is expected to be defined in include/atomic/ *.h files -*/ -#ifndef LF_BACKOFF -#define LF_BACKOFF (1) -#endif - #define MY_ATOMIC_OK 0 #define MY_ATOMIC_NOT_1CPU 1 extern int my_atomic_initialize(); diff --git a/include/my_cpu.h b/include/my_cpu.h index 026b92c1b74..856a8e9b04a 100644 --- a/include/my_cpu.h +++ b/include/my_cpu.h @@ -1,3 +1,5 @@ +#ifndef MY_CPU_INCLUDED +#define MY_CPU_INCLUDED /* Copyright (c) 2013, MariaDB foundation Ab and SkySQL This program is free software; you can redistribute it and/or modify @@ -42,3 +44,58 @@ #define HMT_medium_high() #define HMT_high() #endif + + +static inline void MY_RELAX_CPU(void) +{ +#ifdef HAVE_PAUSE_INSTRUCTION + /* + According to the gcc info page, asm volatile means that the + instruction has important side-effects and must not be removed. + Also asm volatile may trigger a memory barrier (spilling all registers + to memory). + */ +#ifdef __SUNPRO_CC + asm ("pause" ); +#else + __asm__ __volatile__ ("pause"); +#endif + +#elif defined(HAVE_FAKE_PAUSE_INSTRUCTION) + __asm__ __volatile__ ("rep; nop"); +#elif defined _WIN32 + /* + In the Win32 API, the x86 PAUSE instruction is executed by calling + the YieldProcessor macro defined in WinNT.h. It is a CPU architecture- + independent way by using YieldProcessor. + */ + YieldProcessor(); +#elif defined(_ARCH_PWR8) + __ppc_get_timebase(); +#else + int32 var, oldval = 0; + my_atomic_cas32_strong_explicit(&var, &oldval, 1, MY_MEMORY_ORDER_RELAXED, + MY_MEMORY_ORDER_RELAXED); +#endif +} + + +/* + LF_BACKOFF should be used to improve performance on hyperthreaded CPUs. Intel + recommends to use it in spin loops also on non-HT machines to reduce power + consumption (see e.g http://softwarecommunity.intel.com/articles/eng/2004.htm) + + Running benchmarks for spinlocks implemented with InterlockedCompareExchange + and YieldProcessor shows that much better performance is achieved by calling + YieldProcessor in a loop - that is, yielding longer. On Intel boxes setting + loop count in the range 200-300 brought best results. +*/ + +static inline int LF_BACKOFF(void) +{ + int i; + for (i= 0; i < 200; i++) + MY_RELAX_CPU(); + return 1; +} +#endif diff --git a/mysys/lf_alloc-pin.c b/mysys/lf_alloc-pin.c index bf2b8a12846..8a96fccf16a 100644 --- a/mysys/lf_alloc-pin.c +++ b/mysys/lf_alloc-pin.c @@ -430,7 +430,7 @@ static void alloc_free(uchar *first, { anext_node(last)= tmp.node; } while (!my_atomic_casptr((void **)(char *)&allocator->top, - (void **)&tmp.ptr, first) && LF_BACKOFF); + (void **)&tmp.ptr, first) && LF_BACKOFF()); } /* @@ -501,7 +501,7 @@ void *lf_alloc_new(LF_PINS *pins) { node= allocator->top; lf_pin(pins, 0, node); - } while (node != allocator->top && LF_BACKOFF); + } while (node != allocator->top && LF_BACKOFF()); if (!node) { node= (void *)my_malloc(allocator->element_size, MYF(MY_WME)); diff --git a/mysys/lf_hash.c b/mysys/lf_hash.c index 430f1007f30..6b3fa78475d 100644 --- a/mysys/lf_hash.c +++ b/mysys/lf_hash.c @@ -102,7 +102,7 @@ retry: do { /* PTR() isn't necessary below, head is a dummy node */ cursor->curr= (LF_SLIST *)(*cursor->prev); lf_pin(pins, 1, cursor->curr); - } while (*cursor->prev != (intptr)cursor->curr && LF_BACKOFF); + } while (*cursor->prev != (intptr)cursor->curr && LF_BACKOFF()); for (;;) { @@ -117,7 +117,7 @@ retry: link= cursor->curr->link; cursor->next= PTR(link); lf_pin(pins, 0, cursor->next); - } while (link != cursor->curr->link && LF_BACKOFF); + } while (link != cursor->curr->link && LF_BACKOFF()); if (!DELETED(link)) { @@ -145,7 +145,7 @@ retry: and remove this deleted node */ if (my_atomic_casptr((void **) cursor->prev, - (void **) &cursor->curr, cursor->next) && LF_BACKOFF) + (void **) &cursor->curr, cursor->next) && LF_BACKOFF()) lf_alloc_free(pins, cursor->curr); else goto retry; diff --git a/mysys/waiting_threads.c b/mysys/waiting_threads.c index 2549bd8a587..6a4139844db 100644 --- a/mysys/waiting_threads.c +++ b/mysys/waiting_threads.c @@ -617,7 +617,7 @@ retry: { rc= *shared_ptr; lf_pin(arg->thd->pins, 0, rc); - } while (rc != *shared_ptr && LF_BACKOFF); + } while (rc != *shared_ptr && LF_BACKOFF()); if (rc == 0) { diff --git a/storage/innobase/include/os0once.h b/storage/innobase/include/os0once.h index 05a45a69f33..551e78d24ba 100644 --- a/storage/innobase/include/os0once.h +++ b/storage/innobase/include/os0once.h @@ -30,6 +30,7 @@ Created Feb 20, 2014 Vasil Dimov #include "univ.i" #include "ut0ut.h" +#include "my_cpu.h" /** Execute a given function exactly once in a multi-threaded environment or wait for the function to be executed by another thread. @@ -110,7 +111,7 @@ public: ut_error; } - UT_RELAX_CPU(); + MY_RELAX_CPU(); } } } diff --git a/storage/innobase/include/ut0ut.h b/storage/innobase/include/ut0ut.h index 4e9c2599933..352f5cce83d 100644 --- a/storage/innobase/include/ut0ut.h +++ b/storage/innobase/include/ut0ut.h @@ -52,35 +52,6 @@ Created 1/20/1994 Heikki Tuuri /** Time stamp */ typedef time_t ib_time_t; -#ifdef HAVE_PAUSE_INSTRUCTION - /* According to the gcc info page, asm volatile means that the - instruction has important side-effects and must not be removed. - Also asm volatile may trigger a memory barrier (spilling all registers - to memory). */ -# ifdef __SUNPRO_CC -# define UT_RELAX_CPU() asm ("pause" ) -# else -# define UT_RELAX_CPU() __asm__ __volatile__ ("pause") -# endif /* __SUNPRO_CC */ - -#elif defined(HAVE_FAKE_PAUSE_INSTRUCTION) -# define UT_RELAX_CPU() __asm__ __volatile__ ("rep; nop") -#elif defined _WIN32 - /* In the Win32 API, the x86 PAUSE instruction is executed by calling - the YieldProcessor macro defined in WinNT.h. It is a CPU architecture- - independent way by using YieldProcessor. */ -# define UT_RELAX_CPU() YieldProcessor() -#elif defined(__powerpc__) && defined __GLIBC__ -# include -# define UT_RELAX_CPU() __ppc_get_timebase() -#else -# define UT_RELAX_CPU() do { \ - volatile int32 volatile_var; \ - int32 oldval= 0; \ - my_atomic_cas32(&volatile_var, &oldval, 1); \ - } while (0) -#endif - #if defined (__GNUC__) # define UT_COMPILER_BARRIER() __asm__ __volatile__ ("":::"memory") #elif defined (_MSC_VER) diff --git a/storage/innobase/ut/ut0ut.cc b/storage/innobase/ut/ut0ut.cc index 28e327a2a77..88c5c889c8d 100644 --- a/storage/innobase/ut/ut0ut.cc +++ b/storage/innobase/ut/ut0ut.cc @@ -293,7 +293,7 @@ ut_delay( UT_LOW_PRIORITY_CPU(); for (i = 0; i < delay * 50; i++) { - UT_RELAX_CPU(); + MY_RELAX_CPU(); UT_COMPILER_BARRIER(); } From 7891a713daf52b5afd7e423d83c6d80d788ab733 Mon Sep 17 00:00:00 2001 From: Monty Date: Thu, 7 Dec 2017 15:15:22 +0200 Subject: [PATCH 14/32] Don't wait too long in SHOW PROCESSLIST This will allow show processlist to continue, without blocking all new connections, if some threads gets stuck while holding LOCK_thd_data or mysys_var->mutex Connections that has mutex 'stuck' are marked as 'Busy' in 'Command' Todo: Make F_BACKOFF to do 'pause' instead of just (1) --- sql/sql_show.cc | 206 +++++++++++++++++++++++++++++------------------- 1 file changed, 127 insertions(+), 79 deletions(-) diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 30cf490c10c..bb6a03fff96 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -137,6 +137,29 @@ static const LEX_CSTRING *view_algorithm(TABLE_LIST *table); bool get_lookup_field_values(THD *, COND *, TABLE_LIST *, LOOKUP_FIELD_VALUES *); +/** + Try to lock a mutex, but give up after a short while to not cause deadlocks + + The loop is short, as the mutex we are trying to lock are mutex the should + never be locked a long time, just over a few instructions. + + @return 0 ok + @return 1 error +*/ + +static bool trylock_short(mysql_mutex_t *mutex) +{ + uint i; + for (i= 0 ; i < 100 ; i++) + { + if (!mysql_mutex_trylock(mutex)) + return 0; + LF_BACKOFF(); + } + return 1; +} + + /*************************************************************************** ** List all table types supported ***************************************************************************/ @@ -2634,7 +2657,8 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose) while ((tmp=it++)) { Security_context *tmp_sctx= tmp->security_ctx; - struct st_my_thread_var *mysys_var; + struct st_my_thread_var *mysys_var= 0; + bool got_thd_data, got_mysys_lock= 0; if ((tmp->vio_ok() || tmp->system_thread) && (!user || (!tmp->system_thread && tmp_sctx->user && !strcmp(tmp_sctx->user, user)))) @@ -2658,48 +2682,66 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose) tmp_sctx->host_or_ip : tmp_sctx->host ? tmp_sctx->host : ""); thd_info->command=(int) tmp->get_command(); - mysql_mutex_lock(&tmp->LOCK_thd_data); - if ((thd_info->db= tmp->db)) // Safe test - thd_info->db= thd->strdup(thd_info->db); - if ((mysys_var= tmp->mysys_var)) - mysql_mutex_lock(&mysys_var->mutex); - thd_info->proc_info= (char*) (tmp->killed >= KILL_QUERY ? - "Killed" : 0); - thd_info->state_info= thread_state_info(tmp); - if (mysys_var) - mysql_mutex_unlock(&mysys_var->mutex); - /* Lock THD mutex that protects its data when looking at it. */ - if (tmp->query()) - { - uint length= MY_MIN(max_query_length, tmp->query_length()); - char *q= thd->strmake(tmp->query(),length); - /* Safety: in case strmake failed, we set length to 0. */ - thd_info->query_string= - CSET_STRING(q, q ? length : 0, tmp->query_charset()); - } + if ((got_thd_data= !trylock_short(&tmp->LOCK_thd_data))) + if ((mysys_var= tmp->mysys_var)) + got_mysys_lock= !trylock_short(&mysys_var->mutex); - /* - Progress report. We need to do this under a lock to ensure that all - is from the same stage. - */ - if (tmp->progress.max_counter) + if (got_thd_data) { - uint max_stage= MY_MAX(tmp->progress.max_stage, 1); - thd_info->progress= (((tmp->progress.stage / (double) max_stage) + - ((tmp->progress.counter / - (double) tmp->progress.max_counter) / - (double) max_stage)) * - 100.0); - set_if_smaller(thd_info->progress, 100); + /* This is correct under mysys_lock, otherwise an approximation */ + thd_info->proc_info= (char*) (tmp->killed >= KILL_QUERY ? + "Killed" : 0); + if (got_mysys_lock) + mysql_mutex_unlock(&mysys_var->mutex); + + /* + The following variables are only safe to access under a lock + */ + + if ((thd_info->db= tmp->db)) // Safe test + thd_info->db= thd->strdup(thd_info->db); + + if (tmp->query()) + { + uint length= MY_MIN(max_query_length, tmp->query_length()); + char *q= thd->strmake(tmp->query(),length); + /* Safety: in case strmake failed, we set length to 0. */ + thd_info->query_string= + CSET_STRING(q, q ? length : 0, tmp->query_charset()); + } + + /* + Progress report. We need to do this under a lock to ensure that all + is from the same stage. + */ + if (tmp->progress.max_counter) + { + uint max_stage= MY_MAX(tmp->progress.max_stage, 1); + thd_info->progress= (((tmp->progress.stage / (double) max_stage) + + ((tmp->progress.counter / + (double) tmp->progress.max_counter) / + (double) max_stage)) * + 100.0); + set_if_smaller(thd_info->progress, 100); + } + else + thd_info->progress= 0.0; } else + { + thd_info->proc_info= "Busy"; thd_info->progress= 0.0; + } + + thd_info->state_info= thread_state_info(tmp); thd_info->start_time= tmp->start_utime; ulonglong utime_after_query_snapshot= tmp->utime_after_query; if (thd_info->start_time < utime_after_query_snapshot) thd_info->start_time= utime_after_query_snapshot; // COM_SLEEP - mysql_mutex_unlock(&tmp->LOCK_thd_data); + + if (got_thd_data) + mysql_mutex_unlock(&tmp->LOCK_thd_data); thread_infos.append(thd_info); } } @@ -2938,7 +2980,7 @@ int fill_show_explain(THD *thd, TABLE_LIST *table, COND *cond) explain_req.request_thd= thd; explain_req.failed_to_produce= FALSE; - /* Ok, we have a lock on target->LOCK_thd_data, can call: */ + /* Ok, we have a lock on target->LOCK_thd_kill, can call: */ bres= tmp->apc_target.make_apc_call(thd, &explain_req, timeout_sec, &timed_out); if (bres || explain_req.failed_to_produce) @@ -3017,9 +3059,10 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond) while ((tmp= it++)) { Security_context *tmp_sctx= tmp->security_ctx; - struct st_my_thread_var *mysys_var; + struct st_my_thread_var *mysys_var= 0; const char *val, *db; ulonglong max_counter; + bool got_thd_data, got_mysys_lock= 0; if ((!tmp->vio_ok() && !tmp->system_thread) || (user && (tmp->system_thread || !tmp_sctx->user || @@ -3045,23 +3088,33 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond) else table->field[2]->store(tmp_sctx->host_or_ip, strlen(tmp_sctx->host_or_ip), cs); - /* DB */ - mysql_mutex_lock(&tmp->LOCK_thd_data); - if ((db= tmp->db)) + + if ((got_thd_data= !trylock_short(&tmp->LOCK_thd_data))) + if ((mysys_var= tmp->mysys_var)) + got_mysys_lock= !trylock_short(&mysys_var->mutex); + + if (got_thd_data) { - table->field[3]->store(db, strlen(db), cs); - table->field[3]->set_notnull(); + /* DB */ + if ((db= tmp->db)) + { + table->field[3]->store(db, strlen(db), cs); + table->field[3]->set_notnull(); + } } - if ((mysys_var= tmp->mysys_var)) - mysql_mutex_lock(&mysys_var->mutex); /* COMMAND */ - if ((val= (char *) ((tmp->killed >= KILL_QUERY ? + if ((val= (char *) (!got_thd_data ? "Busy" : + (tmp->killed >= KILL_QUERY ? "Killed" : 0)))) table->field[4]->store(val, strlen(val), cs); else table->field[4]->store(command_name[tmp->get_command()].str, command_name[tmp->get_command()].length, cs); + + if (got_mysys_lock) + mysql_mutex_unlock(&mysys_var->mutex); + /* MYSQL_TIME */ ulonglong utime= tmp->start_utime; ulonglong utime_after_query_snapshot= tmp->utime_after_query; @@ -3070,6 +3123,7 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond) utime= utime && utime < unow ? unow - utime : 0; table->field[5]->store(utime / HRTIME_RESOLUTION, TRUE); + /* STATE */ if ((val= thread_state_info(tmp))) { @@ -3077,46 +3131,40 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond) table->field[6]->set_notnull(); } - if (mysys_var) - mysql_mutex_unlock(&mysys_var->mutex); - mysql_mutex_unlock(&tmp->LOCK_thd_data); + if (got_thd_data) + { + if (tmp->query()) + { + table->field[7]->store(tmp->query(), + MY_MIN(PROCESS_LIST_INFO_WIDTH, + tmp->query_length()), cs); + table->field[7]->set_notnull(); + + /* INFO_BINARY */ + table->field[16]->store(tmp->query(), + MY_MIN(PROCESS_LIST_INFO_WIDTH, + tmp->query_length()), + &my_charset_bin); + table->field[16]->set_notnull(); + } + + /* + Progress report. We need to do this under a lock to ensure that all + is from the same stage. + */ + if ((max_counter= tmp->progress.max_counter)) + { + table->field[9]->store((longlong) tmp->progress.stage + 1, 1); + table->field[10]->store((longlong) tmp->progress.max_stage, 1); + table->field[11]->store((double) tmp->progress.counter / + (double) max_counter*100.0); + } + mysql_mutex_unlock(&tmp->LOCK_thd_data); + } /* TIME_MS */ table->field[8]->store((double)(utime / (HRTIME_RESOLUTION / 1000.0))); - /* INFO */ - /* Lock THD mutex that protects its data when looking at it. */ - mysql_mutex_lock(&tmp->LOCK_thd_data); - if (tmp->query()) - { - table->field[7]->store(tmp->query(), - MY_MIN(PROCESS_LIST_INFO_WIDTH, - tmp->query_length()), cs); - table->field[7]->set_notnull(); - } - - /* INFO_BINARY */ - if (tmp->query()) - { - table->field[16]->store(tmp->query(), - MY_MIN(PROCESS_LIST_INFO_WIDTH, - tmp->query_length()), &my_charset_bin); - table->field[16]->set_notnull(); - } - - /* - Progress report. We need to do this under a lock to ensure that all - is from the same stage. - */ - if ((max_counter= tmp->progress.max_counter)) - { - table->field[9]->store((longlong) tmp->progress.stage + 1, 1); - table->field[10]->store((longlong) tmp->progress.max_stage, 1); - table->field[11]->store((double) tmp->progress.counter / - (double) max_counter*100.0); - } - mysql_mutex_unlock(&tmp->LOCK_thd_data); - /* This may become negative if we free a memory allocated by another thread in this thread. However it's better that we notice it eventually From 3574f9c7bc74ae1dd87b442b70929f30e6f95c8e Mon Sep 17 00:00:00 2001 From: Monty Date: Thu, 7 Dec 2017 21:10:51 +0200 Subject: [PATCH 15/32] Updated MW-388.result file --- mysql-test/suite/galera/r/MW-388.result | 12 +++--------- mysql-test/suite/galera/t/MW-388.test | 6 +++--- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/mysql-test/suite/galera/r/MW-388.result b/mysql-test/suite/galera/r/MW-388.result index 7a8c2adafc1..a2cf02712bb 100644 --- a/mysql-test/suite/galera/r/MW-388.result +++ b/mysql-test/suite/galera/r/MW-388.result @@ -10,9 +10,7 @@ INSERT INTO t1 VALUES (1, 'node 1'),(2, 'node 1'); INSERT INTO t1 VALUES (3, 'node 1'); END| SET GLOBAL wsrep_slave_threads = 2; -SET GLOBAL DEBUG = "d,sync.wsrep_apply_cb"; -Warnings: -Warning 1287 '@@debug' is deprecated and will be removed in a future release. Please use '@@debug_dbug' instead +SET GLOBAL DEBUG_DBUG = "d,sync.wsrep_apply_cb"; connection node_2; INSERT INTO t1 VALUES (1, 'node 2');; connect node_1a, 127.0.0.1, root, , test, $NODE_MYPORT_1; @@ -24,9 +22,7 @@ SET SESSION DEBUG_SYNC = 'wsrep_after_replication SIGNAL wsrep_after_replication CALL insert_proc ();; connection node_1a; SET SESSION DEBUG_SYNC = "now WAIT_FOR wsrep_after_replication_reached"; -SET GLOBAL DEBUG = ""; -Warnings: -Warning 1287 '@@debug' is deprecated and will be removed in a future release. Please use '@@debug_dbug' instead +SET GLOBAL DEBUG_DBUG = ""; SET DEBUG_SYNC = "now SIGNAL wsrep_after_replication_continue"; SET DEBUG_SYNC = "now SIGNAL signal.wsrep_apply_cb"; connection node_2; @@ -47,9 +43,7 @@ connection node_1; SET GLOBAL wsrep_slave_threads = DEFAULT; DROP TABLE t1; DROP PROCEDURE insert_proc; -SET GLOBAL debug = NULL; -Warnings: -Warning 1287 '@@debug' is deprecated and will be removed in a future release. Please use '@@debug_dbug' instead +SET GLOBAL debug_dbug = NULL; SET debug_sync='RESET'; SELECT @@debug_sync; @@debug_sync diff --git a/mysql-test/suite/galera/t/MW-388.test b/mysql-test/suite/galera/t/MW-388.test index 209695dca80..de1ac52bf3e 100644 --- a/mysql-test/suite/galera/t/MW-388.test +++ b/mysql-test/suite/galera/t/MW-388.test @@ -30,7 +30,7 @@ DELIMITER ;| # commit cut is not processed and therefore does not advance # local monitor, and our INSERT remains stuck there. SET GLOBAL wsrep_slave_threads = 2; -SET GLOBAL DEBUG = "d,sync.wsrep_apply_cb"; +SET GLOBAL DEBUG_DBUG = "d,sync.wsrep_apply_cb"; --connection node_2 --send INSERT INTO t1 VALUES (1, 'node 2'); @@ -48,7 +48,7 @@ SET SESSION DEBUG_SYNC = 'wsrep_after_replication SIGNAL wsrep_after_replication SET SESSION DEBUG_SYNC = "now WAIT_FOR wsrep_after_replication_reached"; -SET GLOBAL DEBUG = ""; +SET GLOBAL DEBUG_DBUG = ""; SET DEBUG_SYNC = "now SIGNAL wsrep_after_replication_continue"; SET DEBUG_SYNC = "now SIGNAL signal.wsrep_apply_cb"; @@ -69,7 +69,7 @@ SET GLOBAL wsrep_slave_threads = DEFAULT; DROP TABLE t1; DROP PROCEDURE insert_proc; -SET GLOBAL debug = NULL; +SET GLOBAL debug_dbug = NULL; SET debug_sync='RESET'; # Make sure no pending signals are leftover to surprise subsequent tests. From c2118a08b144c95cd4d88a080eaa70abd49f3740 Mon Sep 17 00:00:00 2001 From: Monty Date: Thu, 7 Dec 2017 21:28:00 +0200 Subject: [PATCH 16/32] Move all kill mutex protection to LOCK_thd_kill LOCK_thd_data was used to protect both THD data and ensure that the THD is not deleted while it was in use This patch moves the THD delete protection to LOCK_thd_kill, which already protects the THD for kill. The benefits are: - More well defined what LOCK_thd_data protects - LOCK_thd_data usage is now much simpler and easier to verify - Less chance of deadlocks in SHOW PROCESS LIST as there is less chance of interactions between mutexes - Remove not needed LOCK_thread_count from thd_get_error_context_description() - Fewer mutex taken for thd->awake() Other things: - Don't take mysys->var mutex in show processlist to check if thread is kill marked - thd->awake() now automatically takes the LOCK_thd_kill mutex (Simplifies code) - Apc uses LOCK_thd_kill instead of LOCK_thd_data --- mysql-test/suite/perfschema/r/nesting.result | 14 ++--- mysql-test/suite/perfschema/t/nesting.test | 1 + sql/event_scheduler.cc | 3 - sql/my_apc.cc | 24 ++++---- sql/my_apc.h | 6 +- sql/mysqld.cc | 4 +- sql/slave.cc | 8 +-- sql/sql_class.cc | 46 +++++++------- sql/sql_class.h | 16 ++++- sql/sql_insert.cc | 8 +-- sql/sql_parse.cc | 20 +++--- sql/sql_repl.cc | 6 +- sql/sql_show.cc | 65 ++++++++------------ sql/threadpool_common.cc | 6 +- sql/wsrep_mysqld.cc | 2 - 15 files changed, 109 insertions(+), 120 deletions(-) diff --git a/mysql-test/suite/perfschema/r/nesting.result b/mysql-test/suite/perfschema/r/nesting.result index 78126d93cc7..37681757973 100644 --- a/mysql-test/suite/perfschema/r/nesting.result +++ b/mysql-test/suite/perfschema/r/nesting.result @@ -125,7 +125,7 @@ relative_event_id relative_end_event_id event_name comment nesting_event_type re 15 15 stage/sql/Starting cleanup (stage) STATEMENT 0 16 16 stage/sql/Freeing items (stage) STATEMENT 0 17 17 wait/io/socket/sql/client_connection send STATEMENT 0 -18 18 wait/synch/mutex/sql/THD::LOCK_thd_data lock STATEMENT 0 +18 18 wait/synch/mutex/sql/THD::LOCK_thd_kill lock STATEMENT 0 19 20 stage/sql/Reset for next command (stage) STATEMENT 0 20 20 wait/synch/mutex/sql/THD::LOCK_thd_data lock STAGE 19 21 21 idle idle NULL NULL @@ -147,7 +147,7 @@ relative_event_id relative_end_event_id event_name comment nesting_event_type re 37 37 stage/sql/Starting cleanup (stage) STATEMENT 22 38 38 stage/sql/Freeing items (stage) STATEMENT 22 39 39 wait/io/socket/sql/client_connection send STATEMENT 22 -40 40 wait/synch/mutex/sql/THD::LOCK_thd_data lock STATEMENT 22 +40 40 wait/synch/mutex/sql/THD::LOCK_thd_kill lock STATEMENT 22 41 42 stage/sql/Reset for next command (stage) STATEMENT 22 42 42 wait/synch/mutex/sql/THD::LOCK_thd_data lock STAGE 41 43 43 idle idle NULL NULL @@ -169,7 +169,7 @@ relative_event_id relative_end_event_id event_name comment nesting_event_type re 59 59 stage/sql/Starting cleanup (stage) STATEMENT 44 60 60 stage/sql/Freeing items (stage) STATEMENT 44 61 61 wait/io/socket/sql/client_connection send STATEMENT 44 -62 62 wait/synch/mutex/sql/THD::LOCK_thd_data lock STATEMENT 44 +62 62 wait/synch/mutex/sql/THD::LOCK_thd_kill lock STATEMENT 44 63 64 stage/sql/Reset for next command (stage) STATEMENT 44 64 64 wait/synch/mutex/sql/THD::LOCK_thd_data lock STAGE 63 65 65 idle idle NULL NULL @@ -194,7 +194,7 @@ select "With a third part to make things complete" as payload NULL NULL 82 82 stage/sql/Starting cleanup (stage) STATEMENT 66 83 85 stage/sql/Freeing items (stage) STATEMENT 66 84 84 wait/io/socket/sql/client_connection send STAGE 83 -85 85 wait/synch/mutex/sql/THD::LOCK_thd_data lock STAGE 83 +85 85 wait/synch/mutex/sql/THD::LOCK_thd_kill lock STAGE 83 86 103 statement/sql/select select "And this is the second part of a multi query" as payload; select "With a third part to make things complete" as payload NULL NULL 87 89 stage/sql/Init (stage) STATEMENT 86 @@ -213,7 +213,7 @@ select "With a third part to make things complete" as payload NULL NULL 100 100 stage/sql/Starting cleanup (stage) STATEMENT 86 101 103 stage/sql/Freeing items (stage) STATEMENT 86 102 102 wait/io/socket/sql/client_connection send STAGE 101 -103 103 wait/synch/mutex/sql/THD::LOCK_thd_data lock STAGE 101 +103 103 wait/synch/mutex/sql/THD::LOCK_thd_kill lock STAGE 101 104 122 statement/sql/select select "With a third part to make things complete" as payload NULL NULL 105 106 stage/sql/Init (stage) STATEMENT 104 106 106 wait/synch/mutex/sql/THD::LOCK_thd_data lock STAGE 105 @@ -230,7 +230,7 @@ select "With a third part to make things complete" as payload NULL NULL 117 117 stage/sql/Starting cleanup (stage) STATEMENT 104 118 118 stage/sql/Freeing items (stage) STATEMENT 104 119 119 wait/io/socket/sql/client_connection send STATEMENT 104 -120 120 wait/synch/mutex/sql/THD::LOCK_thd_data lock STATEMENT 104 +120 120 wait/synch/mutex/sql/THD::LOCK_thd_kill lock STATEMENT 104 121 122 stage/sql/Reset for next command (stage) STATEMENT 104 122 122 wait/synch/mutex/sql/THD::LOCK_thd_data lock STAGE 121 123 123 idle idle NULL NULL @@ -252,7 +252,7 @@ select "With a third part to make things complete" as payload NULL NULL 139 139 stage/sql/Starting cleanup (stage) STATEMENT 124 140 140 stage/sql/Freeing items (stage) STATEMENT 124 141 141 wait/io/socket/sql/client_connection send STATEMENT 124 -142 142 wait/synch/mutex/sql/THD::LOCK_thd_data lock STATEMENT 124 +142 142 wait/synch/mutex/sql/THD::LOCK_thd_kill lock STATEMENT 124 143 144 stage/sql/Reset for next command (stage) STATEMENT 124 144 144 wait/synch/mutex/sql/THD::LOCK_thd_data lock STAGE 143 disconnect con1; diff --git a/mysql-test/suite/perfschema/t/nesting.test b/mysql-test/suite/perfschema/t/nesting.test index 9ab59157f50..d0547d8a932 100644 --- a/mysql-test/suite/perfschema/t/nesting.test +++ b/mysql-test/suite/perfschema/t/nesting.test @@ -39,6 +39,7 @@ update performance_schema.setup_instruments set enabled='YES', timed='YES' 'wait/io/socket/sql/client_connection', 'wait/synch/rwlock/sql/LOCK_grant', 'wait/synch/mutex/sql/THD::LOCK_thd_data', + 'wait/synch/mutex/sql/THD::LOCK_thd_kill', 'wait/io/file/sql/query_log'); update performance_schema.setup_instruments set enabled='YES', timed='YES' diff --git a/sql/event_scheduler.cc b/sql/event_scheduler.cc index 1e808a17604..225a3172dc1 100644 --- a/sql/event_scheduler.cc +++ b/sql/event_scheduler.cc @@ -648,14 +648,11 @@ Event_scheduler::stop() state= STOPPING; DBUG_PRINT("info", ("Scheduler thread has id %lu", (ulong) scheduler_thd->thread_id)); - /* Lock from delete */ - mysql_mutex_lock(&scheduler_thd->LOCK_thd_data); /* This will wake up the thread if it waits on Queue's conditional */ sql_print_information("Event Scheduler: Killing the scheduler thread, " "thread id %lu", (ulong) scheduler_thd->thread_id); scheduler_thd->awake(KILL_CONNECTION); - mysql_mutex_unlock(&scheduler_thd->LOCK_thd_data); /* thd could be 0x0, when shutting down */ sql_print_information("Event Scheduler: " diff --git a/sql/my_apc.cc b/sql/my_apc.cc index b165a801ce5..2699ac9c60b 100644 --- a/sql/my_apc.cc +++ b/sql/my_apc.cc @@ -34,7 +34,7 @@ void Apc_target::init(mysql_mutex_t *target_mutex) { DBUG_ASSERT(!enabled); - LOCK_thd_data_ptr= target_mutex; + LOCK_thd_kill_ptr= target_mutex; #ifndef DBUG_OFF n_calls_processed= 0; #endif @@ -45,7 +45,7 @@ void Apc_target::init(mysql_mutex_t *target_mutex) void Apc_target::enqueue_request(Call_request *qe) { - mysql_mutex_assert_owner(LOCK_thd_data_ptr); + mysql_mutex_assert_owner(LOCK_thd_kill_ptr); if (apc_calls) { Call_request *after= apc_calls->prev; @@ -71,7 +71,7 @@ void Apc_target::enqueue_request(Call_request *qe) void Apc_target::dequeue_request(Call_request *qe) { - mysql_mutex_assert_owner(LOCK_thd_data_ptr); + mysql_mutex_assert_owner(LOCK_thd_kill_ptr); if (apc_calls == qe) { if ((apc_calls= apc_calls->next) == qe) @@ -145,14 +145,14 @@ bool Apc_target::make_apc_call(THD *caller_thd, Apc_call *call, int wait_res= 0; PSI_stage_info old_stage; - caller_thd->ENTER_COND(&apc_request.COND_request, LOCK_thd_data_ptr, + caller_thd->ENTER_COND(&apc_request.COND_request, LOCK_thd_kill_ptr, &stage_show_explain, &old_stage); /* todo: how about processing other errors here? */ while (!apc_request.processed && (wait_res != ETIMEDOUT)) { - /* We own LOCK_thd_data_ptr */ + /* We own LOCK_thd_kill_ptr */ wait_res= mysql_cond_timedwait(&apc_request.COND_request, - LOCK_thd_data_ptr, &abstime); + LOCK_thd_kill_ptr, &abstime); // &apc_request.LOCK_request, &abstime); if (caller_thd->killed) break; @@ -163,7 +163,7 @@ bool Apc_target::make_apc_call(THD *caller_thd, Apc_call *call, /* The wait has timed out, or this thread was KILLed. Remove the request from the queue (ok to do because we own - LOCK_thd_data_ptr) + LOCK_thd_kill_ptr) */ apc_request.processed= TRUE; dequeue_request(&apc_request); @@ -176,7 +176,7 @@ bool Apc_target::make_apc_call(THD *caller_thd, Apc_call *call, res= FALSE; } /* - exit_cond() will call mysql_mutex_unlock(LOCK_thd_data_ptr) for us: + exit_cond() will call mysql_mutex_unlock(LOCK_thd_kill_ptr) for us: */ caller_thd->EXIT_COND(&old_stage); @@ -185,7 +185,7 @@ bool Apc_target::make_apc_call(THD *caller_thd, Apc_call *call, } else { - mysql_mutex_unlock(LOCK_thd_data_ptr); + mysql_mutex_unlock(LOCK_thd_kill_ptr); } return res; } @@ -202,11 +202,11 @@ void Apc_target::process_apc_requests() { Call_request *request; - mysql_mutex_lock(LOCK_thd_data_ptr); + mysql_mutex_lock(LOCK_thd_kill_ptr); if (!(request= get_first_in_queue())) { /* No requests in the queue */ - mysql_mutex_unlock(LOCK_thd_data_ptr); + mysql_mutex_unlock(LOCK_thd_kill_ptr); break; } @@ -225,7 +225,7 @@ void Apc_target::process_apc_requests() n_calls_processed++; #endif mysql_cond_signal(&request->COND_request); - mysql_mutex_unlock(LOCK_thd_data_ptr); + mysql_mutex_unlock(LOCK_thd_kill_ptr); } } diff --git a/sql/my_apc.h b/sql/my_apc.h index 46c6fbd549d..a04e09257b9 100644 --- a/sql/my_apc.h +++ b/sql/my_apc.h @@ -44,7 +44,7 @@ class THD; */ class Apc_target { - mysql_mutex_t *LOCK_thd_data_ptr; + mysql_mutex_t *LOCK_thd_kill_ptr; public: Apc_target() : enabled(0), apc_calls(NULL) {} ~Apc_target() { DBUG_ASSERT(!enabled && !apc_calls);} @@ -66,9 +66,9 @@ public: void disable() { DBUG_ASSERT(enabled); - mysql_mutex_lock(LOCK_thd_data_ptr); + mysql_mutex_lock(LOCK_thd_kill_ptr); bool process= !--enabled && have_apc_requests(); - mysql_mutex_unlock(LOCK_thd_data_ptr); + mysql_mutex_unlock(LOCK_thd_kill_ptr); if (unlikely(process)) process_apc_requests(); } diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 374a988537f..c1e14974c35 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -1704,7 +1704,7 @@ static void close_connections(void) #endif tmp->set_killed(KILL_SERVER_HARD); MYSQL_CALLBACK(thread_scheduler, post_kill_notification, (tmp)); - mysql_mutex_lock(&tmp->LOCK_thd_data); + mysql_mutex_lock(&tmp->LOCK_thd_kill); if (tmp->mysys_var) { tmp->mysys_var->abort=1; @@ -1727,7 +1727,7 @@ static void close_connections(void) } mysql_mutex_unlock(&tmp->mysys_var->mutex); } - mysql_mutex_unlock(&tmp->LOCK_thd_data); + mysql_mutex_unlock(&tmp->LOCK_thd_kill); } mysql_mutex_unlock(&LOCK_thread_count); // For unlink from list diff --git a/sql/slave.cc b/sql/slave.cc index c6ac31677cf..c7acaec9e21 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -343,9 +343,7 @@ handle_slave_background(void *arg __attribute__((unused))) THD *to_kill= p->to_kill; kill_list= p->next; - mysql_mutex_lock(&to_kill->LOCK_thd_data); to_kill->awake(KILL_CONNECTION); - mysql_mutex_unlock(&to_kill->LOCK_thd_data); mysql_mutex_lock(&to_kill->LOCK_wakeup_ready); to_kill->rgi_slave->killed_for_retry= rpl_group_info::RETRY_KILL_KILLED; @@ -856,7 +854,7 @@ terminate_slave_thread(THD *thd, int error __attribute__((unused)); DBUG_PRINT("loop", ("killing slave thread")); - mysql_mutex_lock(&thd->LOCK_thd_data); + mysql_mutex_lock(&thd->LOCK_thd_kill); #ifndef DONT_USE_THR_ALARM /* Error codes from pthread_kill are: @@ -866,9 +864,9 @@ terminate_slave_thread(THD *thd, int err __attribute__((unused))= pthread_kill(thd->real_id, thr_client_alarm); DBUG_ASSERT(err != EINVAL); #endif - thd->awake(NOT_KILLED); + thd->awake_no_mutex(NOT_KILLED); - mysql_mutex_unlock(&thd->LOCK_thd_data); + mysql_mutex_unlock(&thd->LOCK_thd_kill); /* There is a small chance that slave thread might miss the first diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 7af1170d107..c4ac5705c08 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -555,8 +555,6 @@ char *thd_get_error_context_description(THD *thd, char *buffer, char header[256]; int len; - mysql_mutex_lock(&LOCK_thread_count); - /* The pointers thd->query and thd->proc_info might change since they are being modified concurrently. This is acceptable for proc_info since its @@ -612,7 +610,6 @@ char *thd_get_error_context_description(THD *thd, char *buffer, } mysql_mutex_unlock(&thd->LOCK_thd_data); } - mysql_mutex_unlock(&LOCK_thread_count); if (str.c_ptr_safe() == buffer) return buffer; @@ -704,10 +701,8 @@ handle_condition(THD *thd, extern "C" void thd_kill_timeout(THD* thd) { thd->status_var.max_statement_time_exceeded++; - mysql_mutex_lock(&thd->LOCK_thd_data); /* Kill queries that can't cause data corruptions */ thd->awake(KILL_TIMEOUT); - mysql_mutex_unlock(&thd->LOCK_thd_data); } @@ -1363,7 +1358,7 @@ void THD::init(void) session_tracker.enable(this); #endif //EMBEDDED_LIBRARY - apc_target.init(&LOCK_thd_data); + apc_target.init(&LOCK_thd_kill); DBUG_VOID_RETURN; } @@ -1627,9 +1622,13 @@ THD::~THD() if (!status_in_global) add_status_to_global(); - /* Ensure that no one is using THD */ - mysql_mutex_lock(&LOCK_thd_data); - mysql_mutex_unlock(&LOCK_thd_data); + /* + Other threads may have a lock on LOCK_thd_kill to ensure that this + THD is not deleted while they access it. The following mutex_lock + ensures that no one else is using this THD and it's now safe to delete + */ + mysql_mutex_lock(&LOCK_thd_kill); + mysql_mutex_unlock(&LOCK_thd_kill); #ifdef WITH_WSREP mysql_mutex_lock(&LOCK_wsrep_thd); @@ -1802,17 +1801,17 @@ void add_diff_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var, This is normally called from another thread's THD object. - @note Do always call this while holding LOCK_thd_data. + @note Do always call this while holding LOCK_thd_kill. NOT_KILLED is used to awake a thread for a slave */ -void THD::awake(killed_state state_to_set) +void THD::awake_no_mutex(killed_state state_to_set) { DBUG_ENTER("THD::awake"); DBUG_PRINT("enter", ("this: %p current_thd: %p state: %d", this, current_thd, (int) state_to_set)); THD_CHECK_SENTRY(this); - mysql_mutex_assert_owner(&LOCK_thd_data); + mysql_mutex_assert_owner(&LOCK_thd_kill); print_aborted_warning(3, "KILLED"); @@ -1823,8 +1822,6 @@ void THD::awake(killed_state state_to_set) if (killed >= KILL_CONNECTION) state_to_set= killed; - /* Set the 'killed' flag of 'this', which is the target THD object. */ - mysql_mutex_lock(&LOCK_thd_kill); set_killed_no_mutex(state_to_set); if (state_to_set >= KILL_CONNECTION || state_to_set == NOT_KILLED) @@ -1911,7 +1908,6 @@ void THD::awake(killed_state state_to_set) } mysql_mutex_unlock(&mysys_var->mutex); } - mysql_mutex_unlock(&LOCK_thd_kill); DBUG_VOID_RETURN; } @@ -1927,10 +1923,10 @@ void THD::disconnect() { Vio *vio= NULL; - mysql_mutex_lock(&LOCK_thd_data); - set_killed(KILL_CONNECTION); + mysql_mutex_lock(&LOCK_thd_data); + #ifdef SIGNAL_WITH_VIO_CLOSE /* Since a active vio might might have not been set yet, in @@ -1963,9 +1959,9 @@ bool THD::notify_shared_lock(MDL_context_owner *ctx_in_use, { /* This code is similar to kill_delayed_threads() */ DBUG_PRINT("info", ("kill delayed thread")); - mysql_mutex_lock(&in_use->LOCK_thd_data); + mysql_mutex_lock(&in_use->LOCK_thd_kill); if (in_use->killed < KILL_CONNECTION) - in_use->set_killed(KILL_CONNECTION); + in_use->set_killed_no_mutex(KILL_CONNECTION); if (in_use->mysys_var) { mysql_mutex_lock(&in_use->mysys_var->mutex); @@ -1976,7 +1972,7 @@ bool THD::notify_shared_lock(MDL_context_owner *ctx_in_use, in_use->mysys_var->abort= 1; mysql_mutex_unlock(&in_use->mysys_var->mutex); } - mysql_mutex_unlock(&in_use->LOCK_thd_data); + mysql_mutex_unlock(&in_use->LOCK_thd_kill); signalled= TRUE; } @@ -2084,7 +2080,7 @@ bool THD::store_globals() return 1; /* mysys_var is concurrently readable by a killer thread. - It is protected by LOCK_thd_data, it is not needed to lock while the + It is protected by LOCK_thd_kill, it is not needed to lock while the pointer is changing from NULL not non-NULL. If the kill thread reads NULL it doesn't refer to anything, but if it is non-NULL we need to ensure that the thread doesn't proceed to assign another thread to @@ -2135,9 +2131,9 @@ bool THD::store_globals() void THD::reset_globals() { - mysql_mutex_lock(&LOCK_thd_data); + mysql_mutex_lock(&LOCK_thd_kill); mysys_var= 0; - mysql_mutex_unlock(&LOCK_thd_data); + mysql_mutex_unlock(&LOCK_thd_kill); /* Undocking the thread specific data. */ set_current_thd(0); @@ -5429,9 +5425,9 @@ void THD::set_query_and_id(char *query_arg, uint32 query_length_arg, /** Assign a new value to thd->mysys_var. */ void THD::set_mysys_var(struct st_my_thread_var *new_mysys_var) { - mysql_mutex_lock(&LOCK_thd_data); + mysql_mutex_lock(&LOCK_thd_kill); mysys_var= new_mysys_var; - mysql_mutex_unlock(&LOCK_thd_data); + mysql_mutex_unlock(&LOCK_thd_kill); } /** diff --git a/sql/sql_class.h b/sql/sql_class.h index 8e99d57d0b4..d733aea1756 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -2127,11 +2127,15 @@ public: - thd->query and thd->query_length (used by SHOW ENGINE INNODB STATUS and SHOW PROCESSLIST - thd->db and thd->db_length (used in SHOW PROCESSLIST) - - thd->mysys_var (used by KILL statement and shutdown). Is locked when THD is deleted. */ mysql_mutex_t LOCK_thd_data; - /* Protect kill information */ + /* + Protects: + - kill information + - mysys_var (used by KILL statement and shutdown). + - Also ensures that THD is not deleted while mutex is hold + */ mysql_mutex_t LOCK_thd_kill; /* all prepared statements and cursors of this connection */ @@ -3131,7 +3135,13 @@ public: } void close_active_vio(); #endif - void awake(killed_state state_to_set); + void awake_no_mutex(killed_state state_to_set); + void awake(killed_state state_to_set) + { + mysql_mutex_lock(&LOCK_thd_kill); + awake_no_mutex(state_to_set); + mysql_mutex_unlock(&LOCK_thd_kill); + } /** Disconnect the associated communication endpoint. */ void disconnect(); diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 698ff8246ed..07b03baaf27 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -2702,9 +2702,9 @@ void kill_delayed_threads(void) Delayed_insert *di; while ((di= it++)) { - mysql_mutex_lock(&di->thd.LOCK_thd_data); + mysql_mutex_lock(&di->thd.LOCK_thd_kill); if (di->thd.killed < KILL_CONNECTION) - di->thd.set_killed(KILL_CONNECTION); + di->thd.set_killed_no_mutex(KILL_CONNECTION); if (di->thd.mysys_var) { mysql_mutex_lock(&di->thd.mysys_var->mutex); @@ -2722,7 +2722,7 @@ void kill_delayed_threads(void) } mysql_mutex_unlock(&di->thd.mysys_var->mutex); } - mysql_mutex_unlock(&di->thd.LOCK_thd_data); + mysql_mutex_unlock(&di->thd.LOCK_thd_kill); } mysql_mutex_unlock(&LOCK_delayed_insert); // For unlink from list DBUG_VOID_RETURN; @@ -3076,9 +3076,9 @@ pthread_handler_t handle_delayed_insert(void *arg) this. */ mysql_mutex_lock(&thd->LOCK_thd_data); - thd->set_killed(KILL_CONNECTION_HARD); // If error thd->mdl_context.set_needs_thr_lock_abort(0); mysql_mutex_unlock(&thd->LOCK_thd_data); + thd->set_killed(KILL_CONNECTION_HARD); // If error close_thread_tables(thd); // Free the table thd->mdl_context.release_transactional_locks(); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 4246c9cdae9..2030c34d977 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -8758,13 +8758,13 @@ void add_join_natural(TABLE_LIST *a, TABLE_LIST *b, List *using_fields, /** - Find a thread by id and return it, locking it LOCK_thd_data + Find a thread by id and return it, locking it LOCK_thd_kill @param id Identifier of the thread we're looking for @param query_id If true, search by query_id instead of thread_id @return NULL - not found - pointer - thread found, and its LOCK_thd_data is locked. + pointer - thread found, and its LOCK_thd_kill is locked. */ THD *find_thread_by_id(longlong id, bool query_id) @@ -8778,7 +8778,7 @@ THD *find_thread_by_id(longlong id, bool query_id) continue; if (id == (query_id ? tmp->query_id : (longlong) tmp->thread_id)) { - mysql_mutex_lock(&tmp->LOCK_thd_data); // Lock from delete + mysql_mutex_lock(&tmp->LOCK_thd_kill); // Lock from delete break; } } @@ -8834,13 +8834,13 @@ kill_one_thread(THD *thd, longlong id, killed_state kill_signal, killed_type typ thd->security_ctx->user_matches(tmp->security_ctx)) && !wsrep_thd_is_BF(tmp, true)) { - tmp->awake(kill_signal); + tmp->awake_no_mutex(kill_signal); error=0; } else error= (type == KILL_TYPE_QUERY ? ER_KILL_QUERY_DENIED_ERROR : ER_KILL_DENIED_ERROR); - mysql_mutex_unlock(&tmp->LOCK_thd_data); + mysql_mutex_unlock(&tmp->LOCK_thd_kill); } DBUG_PRINT("exit", ("%d", error)); DBUG_RETURN(error); @@ -8898,7 +8898,7 @@ static uint kill_threads_for_user(THD *thd, LEX_USER *user, DBUG_RETURN(ER_KILL_DENIED_ERROR); } if (!threads_to_kill.push_back(tmp, thd->mem_root)) - mysql_mutex_lock(&tmp->LOCK_thd_data); // Lock from delete + mysql_mutex_lock(&tmp->LOCK_thd_kill); // Lock from delete } } mysql_mutex_unlock(&LOCK_thread_count); @@ -8909,17 +8909,17 @@ static uint kill_threads_for_user(THD *thd, LEX_USER *user, THD *ptr= it2++; do { - ptr->awake(kill_signal); + ptr->awake_no_mutex(kill_signal); /* Careful here: The list nodes are allocated on the memroots of the THDs to be awakened. But those THDs may be terminated and deleted as soon as we release - LOCK_thd_data, which will make the list nodes invalid. + LOCK_thd_kill, which will make the list nodes invalid. Since the operation "it++" dereferences the "next" pointer of the - previous list node, we need to do this while holding LOCK_thd_data. + previous list node, we need to do this while holding LOCK_thd_kill. */ next_ptr= it2++; - mysql_mutex_unlock(&ptr->LOCK_thd_data); + mysql_mutex_unlock(&ptr->LOCK_thd_kill); (*rows)++; } while ((ptr= next_ptr)); } diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 7586e8837d0..36808e9e986 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -3344,7 +3344,7 @@ void kill_zombie_dump_threads(uint32 slave_server_id) if (tmp->get_command() == COM_BINLOG_DUMP && tmp->variables.server_id == slave_server_id) { - mysql_mutex_lock(&tmp->LOCK_thd_data); // Lock from delete + mysql_mutex_lock(&tmp->LOCK_thd_kill); // Lock from delete break; } } @@ -3356,8 +3356,8 @@ void kill_zombie_dump_threads(uint32 slave_server_id) it will be slow because it will iterate through the list again. We just to do kill the thread ourselves. */ - tmp->awake(KILL_SLAVE_SAME_ID); - mysql_mutex_unlock(&tmp->LOCK_thd_data); + tmp->awake_no_mutex(KILL_SLAVE_SAME_ID); + mysql_mutex_unlock(&tmp->LOCK_thd_kill); } } diff --git a/sql/sql_show.cc b/sql/sql_show.cc index bb6a03fff96..f5ce91c6612 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2578,23 +2578,28 @@ static const char *thread_state_info(THD *tmp) { if (tmp->net.reading_or_writing == 2) return "Writing to net"; - else if (tmp->get_command() == COM_SLEEP) + if (tmp->get_command() == COM_SLEEP) return ""; - else - return "Reading from net"; + return "Reading from net"; } - else #endif + + if (tmp->proc_info) + return tmp->proc_info; + + /* Check if we are waiting on a condition */ + if (!trylock_short(&tmp->LOCK_thd_kill)) { - if (tmp->proc_info) - return tmp->proc_info; - else if (tmp->mysys_var && tmp->mysys_var->current_cond) + /* mysys_var is protected by above mutex */ + bool cond= tmp->mysys_var && tmp->mysys_var->current_cond; + mysql_mutex_unlock(&tmp->LOCK_thd_kill); + if (cond) return "Waiting on cond"; - else - return NULL; } + return NULL; } + void mysqld_list_processes(THD *thd,const char *user, bool verbose) { Item *field; @@ -2657,8 +2662,7 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose) while ((tmp=it++)) { Security_context *tmp_sctx= tmp->security_ctx; - struct st_my_thread_var *mysys_var= 0; - bool got_thd_data, got_mysys_lock= 0; + bool got_thd_data; if ((tmp->vio_ok() || tmp->system_thread) && (!user || (!tmp->system_thread && tmp_sctx->user && !strcmp(tmp_sctx->user, user)))) @@ -2684,17 +2688,10 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose) thd_info->command=(int) tmp->get_command(); if ((got_thd_data= !trylock_short(&tmp->LOCK_thd_data))) - if ((mysys_var= tmp->mysys_var)) - got_mysys_lock= !trylock_short(&mysys_var->mutex); - - if (got_thd_data) { - /* This is correct under mysys_lock, otherwise an approximation */ + /* This is an approximation */ thd_info->proc_info= (char*) (tmp->killed >= KILL_QUERY ? "Killed" : 0); - if (got_mysys_lock) - mysql_mutex_unlock(&mysys_var->mutex); - /* The following variables are only safe to access under a lock */ @@ -2952,13 +2949,13 @@ int fill_show_explain(THD *thd, TABLE_LIST *table, COND *cond) tmp_sctx->user))) { my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "PROCESS"); - mysql_mutex_unlock(&tmp->LOCK_thd_data); + mysql_mutex_unlock(&tmp->LOCK_thd_kill); DBUG_RETURN(1); } if (tmp == thd) { - mysql_mutex_unlock(&tmp->LOCK_thd_data); + mysql_mutex_unlock(&tmp->LOCK_thd_kill); my_error(ER_TARGET_NOT_EXPLAINABLE, MYF(0)); DBUG_RETURN(1); } @@ -2966,7 +2963,7 @@ int fill_show_explain(THD *thd, TABLE_LIST *table, COND *cond) bool bres; /* Ok we've found the thread of interest and it won't go away because - we're holding its LOCK_thd data. Post it a SHOW EXPLAIN request. + we're holding its LOCK_thd_kill. Post it a SHOW EXPLAIN request. */ bool timed_out; int timeout_sec= 30; @@ -3059,10 +3056,9 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond) while ((tmp= it++)) { Security_context *tmp_sctx= tmp->security_ctx; - struct st_my_thread_var *mysys_var= 0; const char *val, *db; ulonglong max_counter; - bool got_thd_data, got_mysys_lock= 0; + bool got_thd_data; if ((!tmp->vio_ok() && !tmp->system_thread) || (user && (tmp->system_thread || !tmp_sctx->user || @@ -3090,10 +3086,6 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond) strlen(tmp_sctx->host_or_ip), cs); if ((got_thd_data= !trylock_short(&tmp->LOCK_thd_data))) - if ((mysys_var= tmp->mysys_var)) - got_mysys_lock= !trylock_short(&mysys_var->mutex); - - if (got_thd_data) { /* DB */ if ((db= tmp->db)) @@ -3112,9 +3104,6 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond) table->field[4]->store(command_name[tmp->get_command()].str, command_name[tmp->get_command()].length, cs); - if (got_mysys_lock) - mysql_mutex_unlock(&mysys_var->mutex); - /* MYSQL_TIME */ ulonglong utime= tmp->start_utime; ulonglong utime_after_query_snapshot= tmp->utime_after_query; @@ -3124,13 +3113,6 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond) table->field[5]->store(utime / HRTIME_RESOLUTION, TRUE); - /* STATE */ - if ((val= thread_state_info(tmp))) - { - table->field[6]->store(val, strlen(val), cs); - table->field[6]->set_notnull(); - } - if (got_thd_data) { if (tmp->query()) @@ -3162,6 +3144,13 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond) mysql_mutex_unlock(&tmp->LOCK_thd_data); } + /* STATE */ + if ((val= thread_state_info(tmp))) + { + table->field[6]->store(val, strlen(val), cs); + table->field[6]->set_notnull(); + } + /* TIME_MS */ table->field[8]->store((double)(utime / (HRTIME_RESOLUTION / 1000.0))); diff --git a/sql/threadpool_common.cc b/sql/threadpool_common.cc index 598951da406..98b2dcd1fcd 100644 --- a/sql/threadpool_common.cc +++ b/sql/threadpool_common.cc @@ -250,7 +250,7 @@ static THD* threadpool_add_connection(CONNECT *connect, void *scheduler_data) } delete connect; add_to_active_threads(thd); - thd->mysys_var= mysys_var; + thd->set_mysys_var(mysys_var); thd->event_scheduler.data= scheduler_data; /* Create new PSI thread for use with the THD. */ @@ -477,11 +477,11 @@ void tp_timeout_handler(TP_connection *c) if (c->state != TP_STATE_IDLE) return; THD *thd=c->thd; - mysql_mutex_lock(&thd->LOCK_thd_data); + mysql_mutex_lock(&thd->LOCK_thd_kill); thd->set_killed(KILL_WAIT_TIMEOUT); c->priority= TP_PRIORITY_HIGH; post_kill_notification(thd); - mysql_mutex_unlock(&thd->LOCK_thd_data); + mysql_mutex_unlock(&thd->LOCK_thd_kill); } diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc index 7800ec5e627..00f1b777f17 100644 --- a/sql/wsrep_mysqld.cc +++ b/sql/wsrep_mysqld.cc @@ -2492,9 +2492,7 @@ extern "C" void wsrep_thd_awake(THD *thd, my_bool signal) { if (signal) { - mysql_mutex_lock(&thd->LOCK_thd_data); thd->awake(KILL_QUERY); - mysql_mutex_unlock(&thd->LOCK_thd_data); } else { From 38908aaf81412f82ef11c8a7e654bb4aab50e855 Mon Sep 17 00:00:00 2001 From: Monty Date: Fri, 8 Dec 2017 10:07:11 +0200 Subject: [PATCH 17/32] Fix failing mtr tests - Changed rocksdb.2pcgroup_commit.test to print information on error - Updated myisam_views-big.result --- .../suite/funcs_1/r/myisam_views-big.result | 6 ++-- .../rocksdb/r/2pc_group_commit.result | 30 +++++++++---------- .../rocksdb/t/2pc_group_commit.test | 24 +++++++-------- 3 files changed, 30 insertions(+), 30 deletions(-) diff --git a/mysql-test/suite/funcs_1/r/myisam_views-big.result b/mysql-test/suite/funcs_1/r/myisam_views-big.result index 3290b3dd36a..ff7d0a595ab 100644 --- a/mysql-test/suite/funcs_1/r/myisam_views-big.result +++ b/mysql-test/suite/funcs_1/r/myisam_views-big.result @@ -4784,7 +4784,7 @@ CREATE VIEW v2 AS Select * from test.v1; ERROR 42S02: Table 'test.v1' doesn't exist DROP VIEW IF EXISTS v2; Warnings: -Note 4090 Unknown VIEW: 'test.v2' +Note 4091 Unknown VIEW: 'test.v2' Testcase 3.3.1.25 -------------------------------------------------------------------------------- @@ -8387,7 +8387,7 @@ Call sp1() ; ERROR 42000: PROCEDURE test.sp1 does not exist Drop view if exists test.v1 ; Warnings: -Note 4090 Unknown VIEW: 'test.v1' +Note 4091 Unknown VIEW: 'test.v1' Drop procedure sp1 ; ERROR 42000: PROCEDURE test.sp1 does not exist @@ -22989,7 +22989,7 @@ CREATE VIEW v1 AS SELECT f1 FROM t1; DROP VIEW IF EXISTS v1; DROP VIEW IF EXISTS v1; Warnings: -Note 4090 Unknown VIEW: 'test.v1' +Note 4091 Unknown VIEW: 'test.v1' Testcase 3.3.1.68 -------------------------------------------------------------------------------- diff --git a/storage/rocksdb/mysql-test/rocksdb/r/2pc_group_commit.result b/storage/rocksdb/mysql-test/rocksdb/r/2pc_group_commit.result index a9e80f1562e..ac546a284fc 100644 --- a/storage/rocksdb/mysql-test/rocksdb/r/2pc_group_commit.result +++ b/storage/rocksdb/mysql-test/rocksdb/r/2pc_group_commit.result @@ -14,16 +14,16 @@ SET GLOBAL rocksdb_flush_log_at_trx_commit=1; select variable_value into @b1 from information_schema.global_status where variable_name='Binlog_commits'; select variable_value into @b2 from information_schema.global_status where variable_name='Binlog_group_commits'; select variable_value into @b3 from information_schema.global_status where variable_name='Rocksdb_wal_synced'; -select IF(variable_value - @b1 = 1000, 'OK', 'FAIL') as Binlog_commits +select IF(variable_value - @b1 = 1000, 'OK', variable_value - @b1 = 1000) as Binlog_commits from information_schema.global_status where variable_name='Binlog_commits'; Binlog_commits OK -select IF(variable_value - @b2 = 1000, 'OK', 'FAIL') as Binlog_group_commits +select IF(variable_value - @b2 = 1000, 'OK', variable_value - @b2 = 1000) as Binlog_group_commits from information_schema.global_status where variable_name='Binlog_group_commits'; Binlog_group_commits OK # Prepare operations sync, commits don't. We expect slightly more than 1K syncs: -select IF(variable_value - @b3 between 1000 and 1500, 'OK', 'FAIL') as Rocksdb_wal_synced +select IF(variable_value - @b3 between 1000 and 1500, 'OK', variable_value - @b3 between 1000 and 1500) as Rocksdb_wal_synced from information_schema.global_status where variable_name='Rocksdb_wal_synced'; Rocksdb_wal_synced OK @@ -33,17 +33,17 @@ OK select variable_value into @b1 from information_schema.global_status where variable_name='Binlog_commits'; select variable_value into @b2 from information_schema.global_status where variable_name='Binlog_group_commits'; select variable_value into @b3 from information_schema.global_status where variable_name='Rocksdb_wal_synced'; -select IF(variable_value - @b1 = 10000, 'OK', 'FAIL') as Binlog_commits +select IF(variable_value - @b1 = 10000, 'OK', variable_value - @b1 = 10000) as Binlog_commits from information_schema.global_status where variable_name='Binlog_commits'; Binlog_commits OK -select IF(variable_value - @b2 between 100 and 5000, 'OK', 'FAIL') as Binlog_group_commits +select IF(variable_value - @b2 between 100 and 5000, 'OK', variable_value - @b2 between 100 and 5000) as Binlog_group_commits from information_schema.global_status where variable_name='Binlog_group_commits'; Binlog_group_commits OK -select IF(variable_value - @b3 between 1 and 9000, 'OK', 'FAIL') +select IF(variable_value - @b3 between 1 and 9000, 'OK', variable_value - @b3 between 1 and 9000) from information_schema.global_status where variable_name='Rocksdb_wal_synced'; -IF(variable_value - @b3 between 1 and 9000, 'OK', 'FAIL') +IF(variable_value - @b3 between 1 and 9000, 'OK', variable_value - @b3 between 1 and 9000) OK ## # 2PC enabled, MyRocks durability disabled, single thread @@ -53,17 +53,17 @@ SET GLOBAL rocksdb_flush_log_at_trx_commit=0; select variable_value into @b1 from information_schema.global_status where variable_name='Binlog_commits'; select variable_value into @b2 from information_schema.global_status where variable_name='Binlog_group_commits'; select variable_value into @b3 from information_schema.global_status where variable_name='Rocksdb_wal_synced'; -select IF(variable_value - @b1 = 1000, 'OK', 'FAIL') as Binlog_commits +select IF(variable_value - @b1 = 1000, 'OK', variable_value - @b1 = 1000) as Binlog_commits from information_schema.global_status where variable_name='Binlog_commits'; Binlog_commits OK -select IF(variable_value - @b2 = 1000, 'OK', 'FAIL') as Binlog_group_commits +select IF(variable_value - @b2 = 1000, 'OK', variable_value - @b2 = 1000) as Binlog_group_commits from information_schema.global_status where variable_name='Binlog_group_commits'; Binlog_group_commits OK -select IF(variable_value - @b3 < 10, 'OK', 'FAIL') +select IF(variable_value - @b3 < 10, 'OK', variable_value - @b3 < 10) from information_schema.global_status where variable_name='Rocksdb_wal_synced'; -IF(variable_value - @b3 < 10, 'OK', 'FAIL') +IF(variable_value - @b3 < 10, 'OK', variable_value - @b3 < 10) OK ## # 2PC enabled, MyRocks durability disabled, concurrent workload @@ -71,17 +71,17 @@ OK select variable_value into @b1 from information_schema.global_status where variable_name='Binlog_commits'; select variable_value into @b2 from information_schema.global_status where variable_name='Binlog_group_commits'; select variable_value into @b3 from information_schema.global_status where variable_name='Rocksdb_wal_synced'; -select IF(variable_value - @b1 = 10000, 'OK', 'FAIL') as Binlog_commits +select IF(variable_value - @b1 = 10000, 'OK', variable_value - @b1 = 10000) as Binlog_commits from information_schema.global_status where variable_name='Binlog_commits'; Binlog_commits OK -select IF(variable_value - @b2 < 8000, 'OK', 'FAIL') as Binlog_group_commits +select IF(variable_value - @b2 < 8000, 'OK', variable_value - @b2 < 8000) as Binlog_group_commits from information_schema.global_status where variable_name='Binlog_group_commits'; Binlog_group_commits OK -select IF(variable_value - @b3 < 10, 'OK', 'FAIL') +select IF(variable_value - @b3 < 10, 'OK', variable_value - @b3 < 10) from information_schema.global_status where variable_name='Rocksdb_wal_synced'; -IF(variable_value - @b3 < 10, 'OK', 'FAIL') +IF(variable_value - @b3 < 10, 'OK', variable_value - @b3 < 10) OK SET GLOBAL rocksdb_enable_2pc= @save_rocksdb_enable_2pc; SET GLOBAL rocksdb_flush_log_at_trx_commit= @save_rocksdb_flush_log_at_trx_commit; diff --git a/storage/rocksdb/mysql-test/rocksdb/t/2pc_group_commit.test b/storage/rocksdb/mysql-test/rocksdb/t/2pc_group_commit.test index 1a77424de39..d91d54a3543 100644 --- a/storage/rocksdb/mysql-test/rocksdb/t/2pc_group_commit.test +++ b/storage/rocksdb/mysql-test/rocksdb/t/2pc_group_commit.test @@ -31,12 +31,12 @@ select variable_value into @b1 from information_schema.global_status where varia select variable_value into @b2 from information_schema.global_status where variable_name='Binlog_group_commits'; select variable_value into @b3 from information_schema.global_status where variable_name='Rocksdb_wal_synced'; --exec $MYSQL_SLAP --silent --concurrency=1 --number-of-queries=1000 --query="INSERT INTO t1 (id, value) VALUES(NULL, 1)" -select IF(variable_value - @b1 = 1000, 'OK', 'FAIL') as Binlog_commits +select IF(variable_value - @b1 = 1000, 'OK', variable_value - @b1 = 1000) as Binlog_commits from information_schema.global_status where variable_name='Binlog_commits'; -select IF(variable_value - @b2 = 1000, 'OK', 'FAIL') as Binlog_group_commits +select IF(variable_value - @b2 = 1000, 'OK', variable_value - @b2 = 1000) as Binlog_group_commits from information_schema.global_status where variable_name='Binlog_group_commits'; --echo # Prepare operations sync, commits don't. We expect slightly more than 1K syncs: -select IF(variable_value - @b3 between 1000 and 1500, 'OK', 'FAIL') as Rocksdb_wal_synced +select IF(variable_value - @b3 between 1000 and 1500, 'OK', variable_value - @b3 between 1000 and 1500) as Rocksdb_wal_synced from information_schema.global_status where variable_name='Rocksdb_wal_synced'; --echo ## @@ -48,11 +48,11 @@ select variable_value into @b3 from information_schema.global_status where varia --exec $MYSQL_SLAP --silent --concurrency=50 --number-of-queries=10000 --query="INSERT INTO t1 (id, value) VALUES(NULL, 1)" -select IF(variable_value - @b1 = 10000, 'OK', 'FAIL') as Binlog_commits +select IF(variable_value - @b1 = 10000, 'OK', variable_value - @b1 = 10000) as Binlog_commits from information_schema.global_status where variable_name='Binlog_commits'; -select IF(variable_value - @b2 between 100 and 5000, 'OK', 'FAIL') as Binlog_group_commits +select IF(variable_value - @b2 between 100 and 5000, 'OK', variable_value - @b2 between 100 and 5000) as Binlog_group_commits from information_schema.global_status where variable_name='Binlog_group_commits'; -select IF(variable_value - @b3 between 1 and 9000, 'OK', 'FAIL') +select IF(variable_value - @b3 between 1 and 9000, 'OK', variable_value - @b3 between 1 and 9000) from information_schema.global_status where variable_name='Rocksdb_wal_synced'; --echo ## @@ -66,11 +66,11 @@ select variable_value into @b2 from information_schema.global_status where varia select variable_value into @b3 from information_schema.global_status where variable_name='Rocksdb_wal_synced'; --exec $MYSQL_SLAP --silent --concurrency=1 --number-of-queries=1000 --query="INSERT INTO t1 (id, value) VALUES(NULL, 1)" -select IF(variable_value - @b1 = 1000, 'OK', 'FAIL') as Binlog_commits +select IF(variable_value - @b1 = 1000, 'OK', variable_value - @b1 = 1000) as Binlog_commits from information_schema.global_status where variable_name='Binlog_commits'; -select IF(variable_value - @b2 = 1000, 'OK', 'FAIL') as Binlog_group_commits +select IF(variable_value - @b2 = 1000, 'OK', variable_value - @b2 = 1000) as Binlog_group_commits from information_schema.global_status where variable_name='Binlog_group_commits'; -select IF(variable_value - @b3 < 10, 'OK', 'FAIL') +select IF(variable_value - @b3 < 10, 'OK', variable_value - @b3 < 10) from information_schema.global_status where variable_name='Rocksdb_wal_synced'; --echo ## @@ -83,11 +83,11 @@ select variable_value into @b3 from information_schema.global_status where varia --exec $MYSQL_SLAP --silent --concurrency=50 --number-of-queries=10000 --query="INSERT INTO t1 (id, value) VALUES(NULL, 1)" -select IF(variable_value - @b1 = 10000, 'OK', 'FAIL') as Binlog_commits +select IF(variable_value - @b1 = 10000, 'OK', variable_value - @b1 = 10000) as Binlog_commits from information_schema.global_status where variable_name='Binlog_commits'; -select IF(variable_value - @b2 < 8000, 'OK', 'FAIL') as Binlog_group_commits +select IF(variable_value - @b2 < 8000, 'OK', variable_value - @b2 < 8000) as Binlog_group_commits from information_schema.global_status where variable_name='Binlog_group_commits'; -select IF(variable_value - @b3 < 10, 'OK', 'FAIL') +select IF(variable_value - @b3 < 10, 'OK', variable_value - @b3 < 10) from information_schema.global_status where variable_name='Rocksdb_wal_synced'; ## From bf96310657116bccc3ef0be66084db5a446d4ba4 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Fri, 8 Dec 2017 13:10:41 +0000 Subject: [PATCH 18/32] Fix warnings --- storage/innobase/dict/dict0defrag_bg.cc | 2 +- storage/innobase/dict/dict0stats.cc | 14 ++++++-------- storage/innobase/include/dict0stats.h | 2 +- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/storage/innobase/dict/dict0defrag_bg.cc b/storage/innobase/dict/dict0defrag_bg.cc index 8f2c3231e1c..e43a75b2cf7 100644 --- a/storage/innobase/dict/dict0defrag_bg.cc +++ b/storage/innobase/dict/dict0defrag_bg.cc @@ -318,7 +318,7 @@ dict_stats_save_defrag_stats(dict_index_t* index, trx_t* trx) return DB_SUCCESS; } - lint now = ut_time(); + ib_time_t now = ut_time(); dberr_t err = dict_stats_save_index_stat( index, now, "n_page_split", index->stat_defrag_n_page_split, diff --git a/storage/innobase/dict/dict0stats.cc b/storage/innobase/dict/dict0stats.cc index 9afa228661d..e4aca96da4b 100644 --- a/storage/innobase/dict/dict0stats.cc +++ b/storage/innobase/dict/dict0stats.cc @@ -2256,7 +2256,7 @@ storage. dberr_t dict_stats_save_index_stat( dict_index_t* index, - lint last_update, + ib_time_t last_update, const char* stat_name, ib_uint64_t stat_value, ib_uint64_t* sample_size, @@ -2278,7 +2278,7 @@ dict_stats_save_index_stat( pars_info_add_str_literal(pinfo, "table_name", table_utf8); pars_info_add_str_literal(pinfo, "index_name", index->name); UNIV_MEM_ASSERT_RW_ABORT(&last_update, 4); - pars_info_add_int4_literal(pinfo, "last_update", last_update); + pars_info_add_int4_literal(pinfo, "last_update", (lint)last_update); UNIV_MEM_ASSERT_RW_ABORT(stat_name, strlen(stat_name)); pars_info_add_str_literal(pinfo, "stat_name", stat_name); UNIV_MEM_ASSERT_RW_ABORT(&stat_value, 8); @@ -2389,7 +2389,7 @@ dict_stats_save( const index_id_t* only_for_index = NULL) { pars_info_t* pinfo; - lint now; + ib_time_t now; dberr_t ret; dict_table_t* table; char db_utf8[MAX_DB_UTF8_LEN]; @@ -2407,16 +2407,14 @@ dict_stats_save( dict_fs2utf8(table->name.m_name, db_utf8, sizeof(db_utf8), table_utf8, sizeof(table_utf8)); - /* MySQL's timestamp is 4 byte, so we use - pars_info_add_int4_literal() which takes a lint arg, so "now" is - lint */ - now = (lint) ut_time(); + + now = ut_time(); pinfo = pars_info_create(); pars_info_add_str_literal(pinfo, "database_name", db_utf8); pars_info_add_str_literal(pinfo, "table_name", table_utf8); - pars_info_add_int4_literal(pinfo, "last_update", now); + pars_info_add_int4_literal(pinfo, "last_update", (lint)now); pars_info_add_ull_literal(pinfo, "n_rows", table->stat_n_rows); pars_info_add_ull_literal(pinfo, "clustered_index_size", table->stat_clustered_index_size); diff --git a/storage/innobase/include/dict0stats.h b/storage/innobase/include/dict0stats.h index 4a34b90195a..357d26b5557 100644 --- a/storage/innobase/include/dict0stats.h +++ b/storage/innobase/include/dict0stats.h @@ -212,7 +212,7 @@ storage. dberr_t dict_stats_save_index_stat( dict_index_t* index, - lint last_update, + ib_time_t last_update, const char* stat_name, ib_uint64_t stat_value, ib_uint64_t* sample_size, From 39450498096a450a98e79bd1bc59d50a0903e1cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Fri, 8 Dec 2017 15:18:39 +0200 Subject: [PATCH 19/32] Remove space_name_list_t fil_get_space_names(): Remove. fts_drop_orphaned_tables(): Iterate fil_system->space_list directly. --- storage/innobase/fil/fil0fil.cc | 45 --------------------- storage/innobase/fts/fts0fts.cc | 64 ++++++++++-------------------- storage/innobase/include/fil0fil.h | 17 -------- 3 files changed, 22 insertions(+), 104 deletions(-) diff --git a/storage/innobase/fil/fil0fil.cc b/storage/innobase/fil/fil0fil.cc index 3b496e3959f..24bdaf2c263 100644 --- a/storage/innobase/fil/fil0fil.cc +++ b/storage/innobase/fil/fil0fil.cc @@ -6160,51 +6160,6 @@ fil_delete_file( } } -/** -Iterate over all the spaces in the space list and fetch the -tablespace names. It will return a copy of the name that must be -freed by the caller using: delete[]. -@return DB_SUCCESS if all OK. */ -dberr_t -fil_get_space_names( -/*================*/ - space_name_list_t& space_name_list) - /*!< in/out: List to append to */ -{ - fil_space_t* space; - dberr_t err = DB_SUCCESS; - - mutex_enter(&fil_system->mutex); - - for (space = UT_LIST_GET_FIRST(fil_system->space_list); - space != NULL; - space = UT_LIST_GET_NEXT(space_list, space)) { - - if (space->purpose == FIL_TYPE_TABLESPACE) { - ulint len; - char* name; - - len = ::strlen(space->name); - name = UT_NEW_ARRAY_NOKEY(char, len + 1); - - if (name == 0) { - /* Caller to free elements allocated so far. */ - err = DB_OUT_OF_MEMORY; - break; - } - - memcpy(name, space->name, len); - name[len] = 0; - - space_name_list.push_back(name); - } - } - - mutex_exit(&fil_system->mutex); - - return(err); -} - /** Generate redo log for swapping two .ibd files @param[in] old_table old table @param[in] new_table new table diff --git a/storage/innobase/fts/fts0fts.cc b/storage/innobase/fts/fts0fts.cc index 056eee5187a..cfc4b61c441 100644 --- a/storage/innobase/fts/fts0fts.cc +++ b/storage/innobase/fts/fts0fts.cc @@ -6991,15 +6991,6 @@ fts_drop_orphaned_tables(void) que_t* graph; ib_vector_t* tables; ib_alloc_t* heap_alloc; - space_name_list_t space_name_list; - dberr_t error = DB_SUCCESS; - - /* Note: We have to free the memory after we are done with the list. */ - error = fil_get_space_names(space_name_list); - - if (error == DB_OUT_OF_MEMORY) { - ib::fatal() << "Out of memory"; - } heap = mem_heap_create(1024); heap_alloc = ib_heap_allocator_create(heap); @@ -7012,35 +7003,32 @@ fts_drop_orphaned_tables(void) users can't map them back to table names and this will create unnecessary clutter. */ - for (space_name_list_t::iterator it = space_name_list.begin(); - it != space_name_list.end(); - ++it) { + mutex_enter(&fil_system->mutex); - fts_aux_table_t* fts_aux_table; + for (fil_space_t* space = UT_LIST_GET_FIRST(fil_system->space_list); + space != NULL; + space = UT_LIST_GET_NEXT(space_list, space)) { - fts_aux_table = static_cast( - ib_vector_push(tables, NULL)); - - memset(fts_aux_table, 0x0, sizeof(*fts_aux_table)); - - if (!fts_is_aux_table_name(fts_aux_table, *it, strlen(*it))) { - ib_vector_pop(tables); - } else { - ulint len = strlen(*it); - - fts_aux_table->id = fil_space_get_id_by_name(*it); - - /* We got this list from fil0fil.cc. The tablespace - with this name must exist. */ - ut_a(fts_aux_table->id != ULINT_UNDEFINED); - - fts_aux_table->name = static_cast( - mem_heap_dup(heap, *it, len + 1)); - - fts_aux_table->name[len] = 0; + if (space->purpose != FIL_TYPE_TABLESPACE) { + continue; } + + fts_aux_table_t fts_aux_table; + memset(&fts_aux_table, 0x0, sizeof fts_aux_table); + + size_t len = strlen(space->name); + + if (!fts_is_aux_table_name(&fts_aux_table, space->name, len)) { + continue; + } + + fts_aux_table.id = space->id; + fts_aux_table.name = mem_heap_strdupl(heap, space->name, len); + ib_vector_push(tables, &fts_aux_table); } + mutex_exit(&fil_system->mutex); + trx = trx_allocate_for_background(); trx->op_info = "dropping orphaned FTS tables"; row_mysql_lock_data_dictionary(trx); @@ -7068,7 +7056,7 @@ fts_drop_orphaned_tables(void) "CLOSE c;"); for (;;) { - error = fts_eval_sql(trx, graph); + dberr_t error = fts_eval_sql(trx, graph); if (error == DB_SUCCESS) { fts_check_and_drop_orphaned_tables(trx, tables); @@ -7101,14 +7089,6 @@ fts_drop_orphaned_tables(void) if (heap != NULL) { mem_heap_free(heap); } - - /** Free the memory allocated to store the .ibd names. */ - for (space_name_list_t::iterator it = space_name_list.begin(); - it != space_name_list.end(); - ++it) { - - UT_DELETE_ARRAY(*it); - } } /**********************************************************************//** diff --git a/storage/innobase/include/fil0fil.h b/storage/innobase/include/fil0fil.h index 9fa507c2114..1027cf5cbb7 100644 --- a/storage/innobase/include/fil0fil.h +++ b/storage/innobase/include/fil0fil.h @@ -35,16 +35,11 @@ Created 10/25/1995 Heikki Tuuri #include "page0size.h" #include "ibuf0types.h" -#include -#include - // Forward declaration struct trx_t; class page_id_t; class truncate_t; -typedef std::list > space_name_list_t; - /** Structure containing encryption specification */ struct fil_space_crypt_t; @@ -1492,18 +1487,6 @@ ulint fil_space_get_id_by_name( const char* tablespace); -/** -Iterate over all the spaces in the space list and fetch the -tablespace names. It will return a copy of the name that must be -freed by the caller using: delete[]. -@return DB_SUCCESS if all OK. */ -dberr_t -fil_get_space_names( -/*================*/ - space_name_list_t& space_name_list) - /*!< in/out: Vector for collecting the names. */ - MY_ATTRIBUTE((warn_unused_result)); - /** Generate redo log for swapping two .ibd files @param[in] old_table old table @param[in] new_table new table From 51bc407403bb7da1b616cf88d024a18ab9e36937 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Fri, 8 Dec 2017 16:31:54 +0200 Subject: [PATCH 20/32] Remove dead code for "InnoDB table(space) monitor" Starting with MySQL 5.7 (or MariaDB 10.2.2) InnoDB no longer contains the "table monitor" or "tablespace monitor". The conditions on srv_print_innodb_tablespace_monitor, srv_print_innodb_table_monitor never hold. So, the code was dead. Also, remove a bogus reference to dict_print(), which used to implement the InnoDB table monitor. --- storage/innobase/dict/dict0load.cc | 3 +- storage/innobase/include/dict0load.h | 3 +- storage/innobase/srv/srv0srv.cc | 64 +--------------------------- 3 files changed, 3 insertions(+), 67 deletions(-) diff --git a/storage/innobase/dict/dict0load.cc b/storage/innobase/dict/dict0load.cc index 2f1efd3c9ed..15e14b9d9f7 100644 --- a/storage/innobase/dict/dict0load.cc +++ b/storage/innobase/dict/dict0load.cc @@ -377,8 +377,7 @@ dict_getnext_system( /********************************************************************//** This function processes one SYS_TABLES record and populate the dict_table_t -struct for the table. Extracted out of dict_print() to be used by -both monitor table output and information schema innodb_sys_tables output. +struct for the table. @return error message, or NULL on success */ const char* dict_process_sys_tables_rec_and_mtr_commit( diff --git a/storage/innobase/include/dict0load.h b/storage/innobase/include/dict0load.h index 22fd27c2484..9ba42007568 100644 --- a/storage/innobase/include/dict0load.h +++ b/storage/innobase/include/dict0load.h @@ -193,8 +193,7 @@ dict_getnext_system( mtr_t* mtr); /*!< in: the mini-transaction */ /********************************************************************//** This function processes one SYS_TABLES record and populate the dict_table_t -struct for the table. Extracted out of dict_print() to be used by -both monitor table output and information schema innodb_sys_tables output. +struct for the table. @return error message, or NULL on success */ const char* dict_process_sys_tables_rec_and_mtr_commit( diff --git a/storage/innobase/srv/srv0srv.cc b/storage/innobase/srv/srv0srv.cc index 9ac4daf5901..b6d9fbe635d 100644 --- a/storage/innobase/srv/srv0srv.cc +++ b/storage/innobase/srv/srv0srv.cc @@ -454,8 +454,6 @@ stderr on startup/shutdown. Not enabled on the embedded server. */ ibool srv_print_verbose_log; my_bool srv_print_innodb_monitor; my_bool srv_print_innodb_lock_monitor; -my_bool srv_print_innodb_tablespace_monitor; -my_bool srv_print_innodb_table_monitor; /** innodb_force_primary_key; whether to disallow CREATE TABLE without PRIMARY KEY */ my_bool srv_force_primary_key; @@ -1713,8 +1711,6 @@ DECLARE_THREAD(srv_monitor_thread)(void*) double time_elapsed; time_t current_time; time_t last_monitor_time; - time_t last_table_monitor_time; - time_t last_tablespace_monitor_time; ulint mutex_skipped; ibool last_srv_print_monitor; @@ -1730,8 +1726,6 @@ DECLARE_THREAD(srv_monitor_thread)(void*) #endif /* UNIV_PFS_THREAD */ srv_last_monitor_time = ut_time(); - last_table_monitor_time = ut_time(); - last_tablespace_monitor_time = ut_time(); last_monitor_time = ut_time(); mutex_skipped = 0; last_srv_print_monitor = srv_print_innodb_monitor; @@ -1791,60 +1785,6 @@ loop: os_file_set_eof(srv_monitor_file); mutex_exit(&srv_monitor_file_mutex); } - - if (srv_print_innodb_tablespace_monitor - && difftime(current_time, - last_tablespace_monitor_time) > 60) { - last_tablespace_monitor_time = ut_time(); - - fputs("========================" - "========================\n", - stderr); - - ut_print_timestamp(stderr); - - fputs(" INNODB TABLESPACE MONITOR OUTPUT\n" - "========================" - "========================\n", - stderr); - - // JAN: TODO: MySQL 5.7 - //fsp_print(0); - //fputs("Validating tablespace\n", stderr); - //fsp_validate(0); - fputs("Validation ok\n" - "---------------------------------------\n" - "END OF INNODB TABLESPACE MONITOR OUTPUT\n" - "=======================================\n", - stderr); - } - - if (srv_print_innodb_table_monitor - && difftime(current_time, last_table_monitor_time) > 60) { - - last_table_monitor_time = ut_time(); - - // fprintf(stderr, "Warning: %s\n", - // DEPRECATED_MSG_INNODB_TABLE_MONITOR); - - fputs("===========================================\n", - stderr); - - ut_print_timestamp(stderr); - - fputs(" INNODB TABLE MONITOR OUTPUT\n" - "===========================================\n", - stderr); - // dict_print(); - - fputs("-----------------------------------\n" - "END OF INNODB TABLE MONITOR OUTPUT\n" - "==================================\n", - stderr); - - //fprintf(stderr, "Warning: %s\n", - // DEPRECATED_MSG_INNODB_TABLE_MONITOR); - } } if (srv_shutdown_state != SRV_SHUTDOWN_NONE) { @@ -1852,9 +1792,7 @@ loop: } if (srv_print_innodb_monitor - || srv_print_innodb_lock_monitor - || srv_print_innodb_tablespace_monitor - || srv_print_innodb_table_monitor) { + || srv_print_innodb_lock_monitor) { goto loop; } From 0af52734a790b61690ada63492974b3670116e3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Fri, 8 Dec 2017 16:36:19 +0200 Subject: [PATCH 21/32] Remove the unused function row_is_magic_monitor_table() Before MySQL 5.7 or MariaDB 10.2.2, there used to be some magic InnoDB table names that would assign some InnoDB flags on CREATE TABLE or DROP TABLE. --- storage/innobase/row/row0mysql.cc | 46 ------------------------------- 1 file changed, 46 deletions(-) diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc index 9a6d65f39c9..5b2e80bc50e 100644 --- a/storage/innobase/row/row0mysql.cc +++ b/storage/innobase/row/row0mysql.cc @@ -93,27 +93,6 @@ static ib_mutex_t row_drop_list_mutex; /** Flag: has row_mysql_drop_list been initialized? */ static ibool row_mysql_drop_list_inited = FALSE; -/** Magic table names for invoking various monitor threads */ -/* @{ */ -static const char S_innodb_monitor[] = "innodb_monitor"; -static const char S_innodb_lock_monitor[] = "innodb_lock_monitor"; -static const char S_innodb_tablespace_monitor[] = "innodb_tablespace_monitor"; -static const char S_innodb_table_monitor[] = "innodb_table_monitor"; -#ifdef UNIV_MEM_DEBUG -static const char S_innodb_mem_validate[] = "innodb_mem_validate"; -#endif /* UNIV_MEM_DEBUG */ -/* @} */ - -/** Evaluates to true if str1 equals str2_onstack, used for comparing -the magic table names. -@param str1 in: string to compare -@param str1_len in: length of str1, in bytes, including terminating NUL -@param str2_onstack in: char[] array containing a NUL terminated string -@return TRUE if str1 equals str2_onstack */ -#define STR_EQ(str1, str1_len, str2_onstack) \ - ((str1_len) == sizeof(str2_onstack) \ - && memcmp(str1, str2_onstack, sizeof(str2_onstack)) == 0) - /*******************************************************************//** Determine if the given name is a name reserved for MySQL system tables. @return TRUE if name is a MySQL system table name */ @@ -5140,31 +5119,6 @@ not_ok: goto loop; } -/*********************************************************************//** -Determines if a table is a magic monitor table. -@return true if monitor table */ -UNIV_INTERN -bool -row_is_magic_monitor_table( -/*=======================*/ - const char* table_name) /*!< in: name of the table, in the - form database/table_name */ -{ - const char* name; /* table_name without database/ */ - ulint len; - - name = dict_remove_db_name(table_name); - len = strlen(name) + 1; - - return(STR_EQ(name, len, S_innodb_monitor) - || STR_EQ(name, len, S_innodb_lock_monitor) - || STR_EQ(name, len, S_innodb_tablespace_monitor) - || STR_EQ(name, len, S_innodb_table_monitor) -#ifdef UNIV_MEM_DEBUG - || STR_EQ(name, len, S_innodb_mem_validate) -#endif /* UNIV_MEM_DEBUG */ - ); -} /*********************************************************************//** Initialize this module */ From 927dd9f355c742ce896983969b82cb3198c250ff Mon Sep 17 00:00:00 2001 From: Sergey Vojtovich Date: Fri, 8 Dec 2017 21:35:29 +0400 Subject: [PATCH 22/32] Fixed LF_BACKOFF calls --- sql/sql_class.cc | 4 ++-- storage/maria/lockman.c | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/sql/sql_class.cc b/sql/sql_class.cc index c4ac5705c08..185b43dd807 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -5560,7 +5560,7 @@ public: MY_MEMORY_ORDER_RELAXED)) { old&= ACQUIRED | RECOVERED; - (void) LF_BACKOFF; + (void) LF_BACKOFF(); } } bool acquire_recovered() @@ -5573,7 +5573,7 @@ public: if (!(old & RECOVERED) || (old & ACQUIRED)) return false; old= RECOVERED; - (void) LF_BACKOFF; + (void) LF_BACKOFF(); } return true; } diff --git a/storage/maria/lockman.c b/storage/maria/lockman.c index efdf7e1c4b8..fa0a3289106 100644 --- a/storage/maria/lockman.c +++ b/storage/maria/lockman.c @@ -268,7 +268,7 @@ retry: do { cursor->curr= PTR(*cursor->prev); lf_pin(pins, 1, cursor->curr); - } while(*cursor->prev != (intptr)cursor->curr && LF_BACKOFF); + } while(*cursor->prev != (intptr)cursor->curr && LF_BACKOFF()); for (;;) { if (!cursor->curr) @@ -277,7 +277,7 @@ retry: cur_link= cursor->curr->link; cursor->next= PTR(cur_link); lf_pin(pins, 0, cursor->next); - } while (cur_link != cursor->curr->link && LF_BACKOFF); + } while (cur_link != cursor->curr->link && LF_BACKOFF()); cur_hashnr= cursor->curr->hashnr; cur_resource= cursor->curr->resource; cur_lock= cursor->curr->lock; @@ -285,7 +285,7 @@ retry: cur_flags= cursor->curr->flags; if (*cursor->prev != (intptr)cursor->curr) { - (void)LF_BACKOFF; + (void)LF_BACKOFF(); goto retry; } if (!DELETED(cur_link)) @@ -362,7 +362,7 @@ retry: lf_alloc_free(pins, cursor->curr); else { - (void)LF_BACKOFF; + (void)LF_BACKOFF(); goto retry; } } From 86beb08774178d50453f5163c01abaf2505f2778 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 11 Dec 2017 09:31:28 +0200 Subject: [PATCH 23/32] Remove an unnecessary dependency on persistent statistics --- .../innodb/r/innodb_max_recordsize_32k.result | 38 +++++------------ .../innodb/r/innodb_max_recordsize_64k.result | 38 +++++------------ .../innodb/t/innodb_max_recordsize_32k.opt | 3 +- .../innodb/t/innodb_max_recordsize_32k.test | 41 ++++--------------- .../innodb/t/innodb_max_recordsize_64k.opt | 3 +- .../innodb/t/innodb_max_recordsize_64k.test | 41 ++++--------------- 6 files changed, 38 insertions(+), 126 deletions(-) diff --git a/mysql-test/suite/innodb/r/innodb_max_recordsize_32k.result b/mysql-test/suite/innodb/r/innodb_max_recordsize_32k.result index 0c06ad24a49..117b7e4418e 100644 --- a/mysql-test/suite/innodb/r/innodb_max_recordsize_32k.result +++ b/mysql-test/suite/innodb/r/innodb_max_recordsize_32k.result @@ -2,18 +2,7 @@ call mtr.add_suppression('InnoDB: Cannot add field.*because after adding it, the SELECT @@innodb_page_size; @@innodb_page_size 32768 -SET GLOBAL innodb_file_format='Barracuda'; -SET GLOBAL innodb_file_per_table=ON; -SET @@innodb_strict_mode=ON; -SELECT @@innodb_file_format; -@@innodb_file_format -Barracuda -SELECT @@innodb_file_per_table; -@@innodb_file_per_table -1 -SELECT @@innodb_strict_mode; -@@innodb_strict_mode -1 +SET innodb_strict_mode=ON; CREATE TABLE tab5(col1 CHAR (255), col2 CHAR (255), col3 CHAR(255),col4 CHAR(255), col5 CHAR(255), col6 CHAR(255), col7 CHAR(255), col8 CHAR(255), col9 CHAR(255),col10 CHAR(255), col11 CHAR(255), col12 CHAR(255), col13 CHAR(255),col14 CHAR(255),col15 CHAR(255),col16 CHAR(255), col17 CHAR(255), @@ -337,11 +326,9 @@ FLUSH TABLE t; ANALYZE TABLE t; Table Op Msg_type Msg_text test.t analyze status OK -SELECT stat_value FROM mysql.innodb_index_stats where database_name = 'test' and table_name= 't' and stat_name='n_leaf_pages'; -stat_value -6 -SELECT clustered_index_size from mysql.innodb_table_stats where database_name = 'test' and table_name= 't'; -clustered_index_size +SELECT clust_index_size FROM INFORMATION_SCHEMA.INNODB_SYS_TABLESTATS +WHERE name = 'test/t'; +clust_index_size 7 DROP TABLE t; CREATE TABLE t(col BLOB) ENGINE=InnoDB ROW_FORMAT=REDUNDANT; @@ -359,11 +346,9 @@ FLUSH TABLE t; ANALYZE TABLE t; Table Op Msg_type Msg_text test.t analyze status OK -SELECT stat_value FROM mysql.innodb_index_stats where database_name = 'test' and table_name= 't' and stat_name='n_leaf_pages'; -stat_value -4 -SELECT clustered_index_size from mysql.innodb_table_stats where database_name = 'test' and table_name= 't'; -clustered_index_size +SELECT clust_index_size FROM INFORMATION_SCHEMA.INNODB_SYS_TABLESTATS +WHERE name = 'test/t'; +clust_index_size 5 DROP TABLE t; CREATE TABLE t(col BLOB) ENGINE=InnoDB ROW_FORMAT=COMPACT; @@ -381,11 +366,8 @@ FLUSH TABLE t; ANALYZE TABLE t; Table Op Msg_type Msg_text test.t analyze status OK -SELECT stat_value FROM mysql.innodb_index_stats where database_name = 'test' and table_name= 't' and stat_name='n_leaf_pages'; -stat_value -4 -SELECT clustered_index_size from mysql.innodb_table_stats where database_name = 'test' and table_name= 't'; -clustered_index_size +SELECT clust_index_size FROM INFORMATION_SCHEMA.INNODB_SYS_TABLESTATS +WHERE name = 'test/t'; +clust_index_size 5 DROP TABLE t; -# Success diff --git a/mysql-test/suite/innodb/r/innodb_max_recordsize_64k.result b/mysql-test/suite/innodb/r/innodb_max_recordsize_64k.result index f9751f02d43..de957023ff8 100644 --- a/mysql-test/suite/innodb/r/innodb_max_recordsize_64k.result +++ b/mysql-test/suite/innodb/r/innodb_max_recordsize_64k.result @@ -2,18 +2,7 @@ call mtr.add_suppression('InnoDB: Cannot add field.*because after adding it, the SELECT @@innodb_page_size; @@innodb_page_size 65536 -SET GLOBAL innodb_file_format='Barracuda'; -SET GLOBAL innodb_file_per_table=ON; -SET @@innodb_strict_mode=ON; -SELECT @@innodb_file_format; -@@innodb_file_format -Barracuda -SELECT @@innodb_file_per_table; -@@innodb_file_per_table -1 -SELECT @@innodb_strict_mode; -@@innodb_strict_mode -1 +SET innodb_strict_mode=ON; CREATE TABLE tab5(col1 CHAR (255), col2 CHAR (255), col3 CHAR(255),col4 CHAR(255), col5 CHAR(255), col6 CHAR(255), col7 CHAR(255), col8 CHAR(255), col9 CHAR(255),col10 CHAR(255), col11 CHAR(255), col12 CHAR(255), col13 CHAR(255),col14 CHAR(255),col15 CHAR(255),col16 CHAR(255), col17 CHAR(255), @@ -540,11 +529,9 @@ FLUSH TABLE t; ANALYZE TABLE t; Table Op Msg_type Msg_text test.t analyze status OK -SELECT stat_value FROM mysql.innodb_index_stats where database_name = 'test' and table_name= 't' and stat_name='n_leaf_pages'; -stat_value -4 -SELECT clustered_index_size from mysql.innodb_table_stats where database_name = 'test' and table_name= 't'; -clustered_index_size +SELECT clust_index_size FROM INFORMATION_SCHEMA.INNODB_SYS_TABLESTATS +WHERE name = 'test/t'; +clust_index_size 5 DROP TABLE t; CREATE TABLE t(col BLOB) ENGINE=InnoDB ROW_FORMAT=REDUNDANT; @@ -562,11 +549,9 @@ FLUSH TABLE t; ANALYZE TABLE t; Table Op Msg_type Msg_text test.t analyze status OK -SELECT stat_value FROM mysql.innodb_index_stats where database_name = 'test' and table_name= 't' and stat_name='n_leaf_pages'; -stat_value -3 -SELECT clustered_index_size from mysql.innodb_table_stats where database_name = 'test' and table_name= 't'; -clustered_index_size +SELECT clust_index_size FROM INFORMATION_SCHEMA.INNODB_SYS_TABLESTATS +WHERE name = 'test/t'; +clust_index_size 4 DROP TABLE t; CREATE TABLE t(col BLOB) ENGINE=InnoDB ROW_FORMAT=COMPACT; @@ -584,11 +569,8 @@ FLUSH TABLE t; ANALYZE TABLE t; Table Op Msg_type Msg_text test.t analyze status OK -SELECT stat_value FROM mysql.innodb_index_stats where database_name = 'test' and table_name= 't' and stat_name='n_leaf_pages'; -stat_value -3 -SELECT clustered_index_size from mysql.innodb_table_stats where database_name = 'test' and table_name= 't'; -clustered_index_size +SELECT clust_index_size FROM INFORMATION_SCHEMA.INNODB_SYS_TABLESTATS +WHERE name = 'test/t'; +clust_index_size 4 DROP TABLE t; -# Success diff --git a/mysql-test/suite/innodb/t/innodb_max_recordsize_32k.opt b/mysql-test/suite/innodb/t/innodb_max_recordsize_32k.opt index f543bd0da39..587a2d7e6c1 100644 --- a/mysql-test/suite/innodb/t/innodb_max_recordsize_32k.opt +++ b/mysql-test/suite/innodb/t/innodb_max_recordsize_32k.opt @@ -1,3 +1,4 @@ --innodb-page-size=32K --innodb_buffer_pool_size=32M ---innodb-stats-persistent=ON +--skip-innodb-stats-persistent +--innodb-sys-tablestats diff --git a/mysql-test/suite/innodb/t/innodb_max_recordsize_32k.test b/mysql-test/suite/innodb/t/innodb_max_recordsize_32k.test index 8549bbfdbef..aa816a9b0e8 100644 --- a/mysql-test/suite/innodb/t/innodb_max_recordsize_32k.test +++ b/mysql-test/suite/innodb/t/innodb_max_recordsize_32k.test @@ -6,19 +6,7 @@ call mtr.add_suppression('InnoDB: Cannot add field.*because after adding it, the # Check page size 32k SELECT @@innodb_page_size; -let $innodb_file_per_table = `SELECT @@innodb_file_per_table`; -let $innodb_file_format = `SELECT @@innodb_file_format`; -let $innodb_strict_mode = `SELECT @@innodb_strict_mode`; - ---disable_warnings -SET GLOBAL innodb_file_format='Barracuda'; -SET GLOBAL innodb_file_per_table=ON; -SET @@innodb_strict_mode=ON; ---enable_warnings - -SELECT @@innodb_file_format; -SELECT @@innodb_file_per_table; -SELECT @@innodb_strict_mode; +SET innodb_strict_mode=ON; # Check the error when the max record length > 16K for innodb_page_size=32k --error ER_TOO_BIG_ROWSIZE @@ -359,9 +347,8 @@ INSERT INTO t VALUES (REPEAT('a',65535)); SELECT LENGTH(col) FROM t; FLUSH TABLE t; ANALYZE TABLE t; -# retrieve the number of leaf pages -SELECT stat_value FROM mysql.innodb_index_stats where database_name = 'test' and table_name= 't' and stat_name='n_leaf_pages'; -SELECT clustered_index_size from mysql.innodb_table_stats where database_name = 'test' and table_name= 't'; +SELECT clust_index_size FROM INFORMATION_SCHEMA.INNODB_SYS_TABLESTATS +WHERE name = 'test/t'; DROP TABLE t; CREATE TABLE t(col BLOB) ENGINE=InnoDB ROW_FORMAT=REDUNDANT; @@ -372,9 +359,8 @@ INSERT INTO t VALUES (REPEAT('a',65535)); SELECT LENGTH(col) FROM t; FLUSH TABLE t; ANALYZE TABLE t; -# retrieve the number of leaf pages -SELECT stat_value FROM mysql.innodb_index_stats where database_name = 'test' and table_name= 't' and stat_name='n_leaf_pages'; -SELECT clustered_index_size from mysql.innodb_table_stats where database_name = 'test' and table_name= 't'; +SELECT clust_index_size FROM INFORMATION_SCHEMA.INNODB_SYS_TABLESTATS +WHERE name = 'test/t'; DROP TABLE t; CREATE TABLE t(col BLOB) ENGINE=InnoDB ROW_FORMAT=COMPACT; @@ -385,19 +371,6 @@ INSERT INTO t VALUES (REPEAT('a',65535)); SELECT LENGTH(col) FROM t; FLUSH TABLE t; ANALYZE TABLE t; -# retrieve the number of leaf pages -SELECT stat_value FROM mysql.innodb_index_stats where database_name = 'test' and table_name= 't' and stat_name='n_leaf_pages'; -SELECT clustered_index_size from mysql.innodb_table_stats where database_name = 'test' and table_name= 't'; +SELECT clust_index_size FROM INFORMATION_SCHEMA.INNODB_SYS_TABLESTATS +WHERE name = 'test/t'; DROP TABLE t; - -# cleanup ---disable_query_log ---disable_warnings -eval SET GLOBAL INNODB_FILE_FORMAT=$innodb_file_format; -eval SET GLOBAL INNODB_FILE_PER_TABLE=$innodb_file_per_table; -eval SET GLOBAL INNODB_STRICT_MODE=$innodb_strict_mode; ---enable_warnings ---enable_query_log - ---echo # Success - diff --git a/mysql-test/suite/innodb/t/innodb_max_recordsize_64k.opt b/mysql-test/suite/innodb/t/innodb_max_recordsize_64k.opt index 8c9b8ae1e48..1a8442b841f 100644 --- a/mysql-test/suite/innodb/t/innodb_max_recordsize_64k.opt +++ b/mysql-test/suite/innodb/t/innodb_max_recordsize_64k.opt @@ -1,3 +1,4 @@ --innodb-page-size=64K --innodb_buffer_pool_size=32M ---innodb-stats-persistent=ON +--skip-innodb-stats-persistent +--innodb-sys-tablestats diff --git a/mysql-test/suite/innodb/t/innodb_max_recordsize_64k.test b/mysql-test/suite/innodb/t/innodb_max_recordsize_64k.test index 0e6bc3bb04c..598cc3d77bf 100644 --- a/mysql-test/suite/innodb/t/innodb_max_recordsize_64k.test +++ b/mysql-test/suite/innodb/t/innodb_max_recordsize_64k.test @@ -6,19 +6,7 @@ call mtr.add_suppression('InnoDB: Cannot add field.*because after adding it, the # Check page size 64k SELECT @@innodb_page_size; -let $innodb_file_per_table = `SELECT @@innodb_file_per_table`; -let $innodb_file_format = `SELECT @@innodb_file_format`; -let $innodb_strict_mode = `SELECT @@innodb_strict_mode`; - ---disable_warnings -SET GLOBAL innodb_file_format='Barracuda'; -SET GLOBAL innodb_file_per_table=ON; -SET @@innodb_strict_mode=ON; ---enable_warnings - -SELECT @@innodb_file_format; -SELECT @@innodb_file_per_table; -SELECT @@innodb_strict_mode; +SET innodb_strict_mode=ON; # Check the error when the max record length > 32K for innodb_page_size=64k --error ER_TOO_BIG_ROWSIZE @@ -563,9 +551,8 @@ INSERT INTO t VALUES (REPEAT('a',65535)); SELECT LENGTH(col) FROM t; FLUSH TABLE t; ANALYZE TABLE t; -# retrieve the number of leaf pages -SELECT stat_value FROM mysql.innodb_index_stats where database_name = 'test' and table_name= 't' and stat_name='n_leaf_pages'; -SELECT clustered_index_size from mysql.innodb_table_stats where database_name = 'test' and table_name= 't'; +SELECT clust_index_size FROM INFORMATION_SCHEMA.INNODB_SYS_TABLESTATS +WHERE name = 'test/t'; DROP TABLE t; CREATE TABLE t(col BLOB) ENGINE=InnoDB ROW_FORMAT=REDUNDANT; @@ -576,9 +563,8 @@ INSERT INTO t VALUES (REPEAT('a',65535)); SELECT LENGTH(col) FROM t; FLUSH TABLE t; ANALYZE TABLE t; -# retrieve the number of leaf pages -SELECT stat_value FROM mysql.innodb_index_stats where database_name = 'test' and table_name= 't' and stat_name='n_leaf_pages'; -SELECT clustered_index_size from mysql.innodb_table_stats where database_name = 'test' and table_name= 't'; +SELECT clust_index_size FROM INFORMATION_SCHEMA.INNODB_SYS_TABLESTATS +WHERE name = 'test/t'; DROP TABLE t; CREATE TABLE t(col BLOB) ENGINE=InnoDB ROW_FORMAT=COMPACT; @@ -589,19 +575,6 @@ INSERT INTO t VALUES (REPEAT('a',65535)); SELECT LENGTH(col) FROM t; FLUSH TABLE t; ANALYZE TABLE t; -# retrieve the number of leaf pages -SELECT stat_value FROM mysql.innodb_index_stats where database_name = 'test' and table_name= 't' and stat_name='n_leaf_pages'; -SELECT clustered_index_size from mysql.innodb_table_stats where database_name = 'test' and table_name= 't'; +SELECT clust_index_size FROM INFORMATION_SCHEMA.INNODB_SYS_TABLESTATS +WHERE name = 'test/t'; DROP TABLE t; - -# cleanup ---disable_query_log ---disable_warnings -eval SET GLOBAL INNODB_FILE_FORMAT=$innodb_file_format; -eval SET GLOBAL INNODB_FILE_PER_TABLE=$innodb_file_per_table; -eval SET GLOBAL INNODB_STRICT_MODE=$innodb_strict_mode; ---enable_warnings ---enable_query_log - ---echo # Success - From 40eee1da17d529e35b554298e6bc400be8ed0e9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 11 Dec 2017 10:06:32 +0200 Subject: [PATCH 24/32] MDEV-14614 InnoDB: Failing assertion: trx->error_state == DB_SUCCESS or lock wait timeout upon saving statistics The assertion failure was caused by MDEV-14511 Use fewer transactions for updating InnoDB persistent statistics We are reusing a transaction object after commit, and sometimes, even after a successful operation, the trx_t::error_state may be something else than DB_SUCCESS. Reset the field when needed. --- storage/innobase/btr/btr0defragment.cc | 1 + storage/innobase/dict/dict0defrag_bg.cc | 7 +++++++ storage/innobase/dict/dict0stats_bg.cc | 1 + storage/innobase/handler/ha_innodb.cc | 3 +++ storage/innobase/handler/handler0alter.cc | 1 + 5 files changed, 13 insertions(+) diff --git a/storage/innobase/btr/btr0defragment.cc b/storage/innobase/btr/btr0defragment.cc index cdbe47d48f8..86df5a077a8 100644 --- a/storage/innobase/btr/btr0defragment.cc +++ b/storage/innobase/btr/btr0defragment.cc @@ -831,6 +831,7 @@ DECLARE_THREAD(btr_defragment_thread)(void*) mtr_commit(&mtr); /* Reaching the end of the index. */ dict_stats_empty_defrag_stats(index); + trx->error_state = DB_SUCCESS; ut_d(trx->persistent_stats = true); ++trx->will_lock; dberr_t err = dict_stats_save_defrag_stats(index, trx); diff --git a/storage/innobase/dict/dict0defrag_bg.cc b/storage/innobase/dict/dict0defrag_bg.cc index e43a75b2cf7..34120f0bdc9 100644 --- a/storage/innobase/dict/dict0defrag_bg.cc +++ b/storage/innobase/dict/dict0defrag_bg.cc @@ -213,6 +213,7 @@ dict_stats_process_entry_from_defrag_pool(trx_t* trx) index_id_t index_id; ut_ad(!srv_read_only_mode); + ut_ad(trx->persistent_stats); /* pop the first index from the auto defrag pool */ if (!dict_stats_defrag_pool_get(&table_id, &index_id)) { @@ -242,6 +243,7 @@ dict_stats_process_entry_from_defrag_pool(trx_t* trx) } mutex_exit(&dict_sys->mutex); + trx->error_state = DB_SUCCESS; ++trx->will_lock; dberr_t err = dict_stats_save_defrag_stats(index, trx); @@ -275,6 +277,8 @@ dict_defrag_process_entries_from_defrag_pool(trx_t* trx) dberr_t dict_stats_save_defrag_summary(dict_index_t* index, trx_t* trx) { + ut_ad(trx->persistent_stats); + if (dict_index_is_ibuf(index)) { return DB_SUCCESS; } @@ -294,6 +298,9 @@ dict_stats_save_defrag_summary(dict_index_t* index, trx_t* trx) dberr_t dict_stats_save_defrag_stats(dict_index_t* index, trx_t* trx) { + ut_ad(trx->error_state == DB_SUCCESS); + ut_ad(trx->persistent_stats); + if (dict_index_is_ibuf(index)) { return DB_SUCCESS; } diff --git a/storage/innobase/dict/dict0stats_bg.cc b/storage/innobase/dict/dict0stats_bg.cc index 0f76c73bc96..3f5419a0751 100644 --- a/storage/innobase/dict/dict0stats_bg.cc +++ b/storage/innobase/dict/dict0stats_bg.cc @@ -378,6 +378,7 @@ dict_stats_process_entry_from_recalc_pool(trx_t* trx) dict_stats_recalc_pool_add(table); } else { + trx->error_state = DB_SUCCESS; ++trx->will_lock; dict_stats_update(table, DICT_STATS_RECALC_PERSISTENT, trx); if (trx->state != TRX_STATE_NOT_STARTED) { diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index a87ab677d04..e7a4532a604 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -6480,6 +6480,7 @@ no_such_table: } ut_ad(!trx->persistent_stats); ut_d(trx->persistent_stats = true); + trx->error_state = DB_SUCCESS; ++trx->will_lock; dict_stats_init(ib_table, trx); innobase_commit_low(trx); @@ -13281,6 +13282,7 @@ create_table_info_t::create_table_update_dict() innobase_copy_frm_flags_from_create_info(innobase_table, m_create_info); ++m_trx->will_lock; + m_trx->error_state = DB_SUCCESS; dict_stats_update(innobase_table, DICT_STATS_EMPTY_TABLE, m_trx); innobase_commit_low(m_trx); @@ -14045,6 +14047,7 @@ ha_innobase::rename_table( normalize_table_name(norm_from, from); normalize_table_name(norm_to, to); + trx->error_state = DB_SUCCESS; ++trx->will_lock; ret = dict_stats_rename_table(norm_from, norm_to, errstr, sizeof errstr, trx); diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc index d2401bd4e2d..44c40d05b4c 100644 --- a/storage/innobase/handler/handler0alter.cc +++ b/storage/innobase/handler/handler0alter.cc @@ -9073,6 +9073,7 @@ foreign_fail: } row_mysql_unlock_data_dictionary(trx); + trx->error_state = DB_SUCCESS; ++trx->will_lock; /* TODO: The following code could be executed From b8a0373ed2e4339d74aa7628f55dedf2303e57c6 Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Fri, 8 Dec 2017 16:13:03 +0300 Subject: [PATCH 25/32] MDEV-14123: ".rocksdb folder may break workflow", and other MDEVs Rename Use '#rocksdb' instead of '.rocksdb' as subdirectory for RocksDB datadir. --- storage/rocksdb/ha_rocksdb.cc | 2 +- storage/rocksdb/mysql-test/rocksdb/suite.opt | 2 +- storage/rocksdb/mysql-test/rocksdb_sys_vars/suite.opt | 2 +- storage/rocksdb/mysql-test/storage_engine/parts/suite.opt | 2 +- storage/rocksdb/mysql-test/storage_engine/suite.opt | 2 +- storage/rocksdb/mysql-test/storage_engine/trx/suite.opt | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/storage/rocksdb/ha_rocksdb.cc b/storage/rocksdb/ha_rocksdb.cc index 5c7b185efdc..3613634f344 100644 --- a/storage/rocksdb/ha_rocksdb.cc +++ b/storage/rocksdb/ha_rocksdb.cc @@ -1446,7 +1446,7 @@ static MYSQL_SYSVAR_UINT( static MYSQL_SYSVAR_STR(datadir, rocksdb_datadir, PLUGIN_VAR_OPCMDARG | PLUGIN_VAR_READONLY, "RocksDB data directory", nullptr, nullptr, - "./.rocksdb"); + "./#rocksdb"); static MYSQL_SYSVAR_STR(supported_compression_types, compression_types_val, diff --git a/storage/rocksdb/mysql-test/rocksdb/suite.opt b/storage/rocksdb/mysql-test/rocksdb/suite.opt index f5dc0ce891c..3829658ab1c 100644 --- a/storage/rocksdb/mysql-test/rocksdb/suite.opt +++ b/storage/rocksdb/mysql-test/rocksdb/suite.opt @@ -1,2 +1,2 @@ ---ignore-db-dirs=.rocksdb --plugin-load=$HA_ROCKSDB_SO --default-storage-engine=rocksdb +--ignore-db-dirs=#rocksdb --plugin-load=$HA_ROCKSDB_SO --default-storage-engine=rocksdb diff --git a/storage/rocksdb/mysql-test/rocksdb_sys_vars/suite.opt b/storage/rocksdb/mysql-test/rocksdb_sys_vars/suite.opt index 431fc331458..b503913d2c8 100644 --- a/storage/rocksdb/mysql-test/rocksdb_sys_vars/suite.opt +++ b/storage/rocksdb/mysql-test/rocksdb_sys_vars/suite.opt @@ -1,2 +1,2 @@ ---ignore-db-dirs=.rocksdb --plugin-load=$HA_ROCKSDB_SO +--ignore-db-dirs=#rocksdb --plugin-load=$HA_ROCKSDB_SO diff --git a/storage/rocksdb/mysql-test/storage_engine/parts/suite.opt b/storage/rocksdb/mysql-test/storage_engine/parts/suite.opt index 1e464761753..d77a822766f 100644 --- a/storage/rocksdb/mysql-test/storage_engine/parts/suite.opt +++ b/storage/rocksdb/mysql-test/storage_engine/parts/suite.opt @@ -1 +1 @@ ---ignore-db-dirs=.rocksdb --plugin-load=$HA_ROCKSDB_SO --binlog_format=ROW --loose-rocksdb_flush_log_at_trx_commit=0 +--ignore-db-dirs=#rocksdb --plugin-load=$HA_ROCKSDB_SO --binlog_format=ROW --loose-rocksdb_flush_log_at_trx_commit=0 diff --git a/storage/rocksdb/mysql-test/storage_engine/suite.opt b/storage/rocksdb/mysql-test/storage_engine/suite.opt index 8eb0e87db98..e6122c7ed3e 100644 --- a/storage/rocksdb/mysql-test/storage_engine/suite.opt +++ b/storage/rocksdb/mysql-test/storage_engine/suite.opt @@ -1 +1 @@ ---ignore-db-dirs=.rocksdb --plugin-load=$HA_ROCKSDB_SO --binlog_format=ROW --collation-server=latin1_bin --loose-rocksdb_flush_log_at_trx_commit=0 +--ignore-db-dirs=#rocksdb --plugin-load=$HA_ROCKSDB_SO --binlog_format=ROW --collation-server=latin1_bin --loose-rocksdb_flush_log_at_trx_commit=0 diff --git a/storage/rocksdb/mysql-test/storage_engine/trx/suite.opt b/storage/rocksdb/mysql-test/storage_engine/trx/suite.opt index 1e464761753..d77a822766f 100644 --- a/storage/rocksdb/mysql-test/storage_engine/trx/suite.opt +++ b/storage/rocksdb/mysql-test/storage_engine/trx/suite.opt @@ -1 +1 @@ ---ignore-db-dirs=.rocksdb --plugin-load=$HA_ROCKSDB_SO --binlog_format=ROW --loose-rocksdb_flush_log_at_trx_commit=0 +--ignore-db-dirs=#rocksdb --plugin-load=$HA_ROCKSDB_SO --binlog_format=ROW --loose-rocksdb_flush_log_at_trx_commit=0 From ddc1d6904a8852d6584a8fb7721de78d56bd454e Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Fri, 8 Dec 2017 16:41:51 +0300 Subject: [PATCH 26/32] MDEV-14123: .rocksdb folder may break workflow which re-create data directory Part2: make MyRocks add its directory into @@ignore_db_dirs when starting. This is necessary because apparently not everybody are using plugin's my.cnf So load ha_rocksdb.{so,dll} manually and then hit MDEV-12451, MDEV-14461 etc. --- sql/sql_show.cc | 51 +++++++++++++++++++ storage/rocksdb/ha_rocksdb.cc | 10 ++++ .../rocksdb/r/mariadb_ignore_dirs.result | 9 ++++ storage/rocksdb/mysql-test/rocksdb/suite.opt | 2 +- .../rocksdb/t/mariadb_ignore_dirs.test | 17 +++++++ 5 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 storage/rocksdb/mysql-test/rocksdb/r/mariadb_ignore_dirs.result create mode 100644 storage/rocksdb/mysql-test/rocksdb/t/mariadb_ignore_dirs.test diff --git a/sql/sql_show.cc b/sql/sql_show.cc index de9d7d77dc8..f63d4a2d4f4 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -787,6 +787,57 @@ static void dispose_db_dir(void *ptr) } +/* + Append an element into @@ignore_db_dirs + + This is a function to be called after regular option processing has been + finalized. +*/ + +void ignore_db_dirs_append(const char *dirname_arg) +{ + char *new_entry_buf; + LEX_STRING *new_entry; + size_t len= strlen(dirname_arg); + + if (!my_multi_malloc(0, + &new_entry, sizeof(LEX_STRING), + &new_entry_buf, len + 1, + NullS)) + return; + + memcpy(new_entry_buf, dirname_arg, len+1); + new_entry->str = new_entry_buf; + new_entry->length= len; + + if (my_hash_insert(&ignore_db_dirs_hash, (uchar *)new_entry)) + { + // Either the name is already there or out-of-memory. + my_free(new_entry); + return; + } + + // Append the name to the option string. + size_t curlen= strlen(opt_ignore_db_dirs); + // Add one for comma and one for \0. + size_t newlen= curlen + len + 1 + 1; + char *new_db_dirs; + if (!(new_db_dirs= (char*)my_malloc(newlen ,MYF(0)))) + { + // This is not a critical condition + return; + } + + memcpy(new_db_dirs, opt_ignore_db_dirs, curlen); + if (curlen != 0) + new_db_dirs[curlen]=','; + memcpy(new_db_dirs + (curlen + ((curlen!=0)?1:0)), dirname_arg, len+1); + + if (opt_ignore_db_dirs) + my_free(opt_ignore_db_dirs); + opt_ignore_db_dirs= new_db_dirs; +} + bool ignore_db_dirs_process_additions() { diff --git a/storage/rocksdb/ha_rocksdb.cc b/storage/rocksdb/ha_rocksdb.cc index 3613634f344..e08329ab61e 100644 --- a/storage/rocksdb/ha_rocksdb.cc +++ b/storage/rocksdb/ha_rocksdb.cc @@ -111,6 +111,10 @@ bool thd_binlog_filter_ok(const MYSQL_THD thd); MYSQL_PLUGIN_IMPORT bool my_disable_leak_check; +// Needed in rocksdb_init_func +void ignore_db_dirs_append(const char *dirname_arg); + + namespace myrocks { static st_global_stats global_stats; @@ -3934,6 +3938,7 @@ static rocksdb::Status check_rocksdb_options_compatibility( return status; } + /* Storage Engine initialization function, invoked when plugin is loaded. */ @@ -3962,6 +3967,11 @@ static int rocksdb_init_func(void *const p) { mysql_mutex_init(rdb_mem_cmp_space_mutex_key, &rdb_mem_cmp_space_mutex, MY_MUTEX_INIT_FAST); + const char* initial_rocksdb_datadir_for_ignore_dirs= rocksdb_datadir; + if (!strncmp(rocksdb_datadir, "./", 2)) + initial_rocksdb_datadir_for_ignore_dirs += 2; + ignore_db_dirs_append(initial_rocksdb_datadir_for_ignore_dirs); + #if defined(HAVE_PSI_INTERFACE) rdb_collation_exceptions = new Regex_list_handler(key_rwlock_collation_exception_list); diff --git a/storage/rocksdb/mysql-test/rocksdb/r/mariadb_ignore_dirs.result b/storage/rocksdb/mysql-test/rocksdb/r/mariadb_ignore_dirs.result new file mode 100644 index 00000000000..9b91cdb5551 --- /dev/null +++ b/storage/rocksdb/mysql-test/rocksdb/r/mariadb_ignore_dirs.result @@ -0,0 +1,9 @@ +# +# RocksDB plugin adds #rocksdb to ignore_db_dirs +# +select @@ignore_db_dirs; +@@ignore_db_dirs +#rocksdb +select @@ignore_db_dirs; +@@ignore_db_dirs +aa,bbb,#rocksdb diff --git a/storage/rocksdb/mysql-test/rocksdb/suite.opt b/storage/rocksdb/mysql-test/rocksdb/suite.opt index 3829658ab1c..22c9d7a300e 100644 --- a/storage/rocksdb/mysql-test/rocksdb/suite.opt +++ b/storage/rocksdb/mysql-test/rocksdb/suite.opt @@ -1,2 +1,2 @@ ---ignore-db-dirs=#rocksdb --plugin-load=$HA_ROCKSDB_SO --default-storage-engine=rocksdb +--plugin-load=$HA_ROCKSDB_SO --default-storage-engine=rocksdb diff --git a/storage/rocksdb/mysql-test/rocksdb/t/mariadb_ignore_dirs.test b/storage/rocksdb/mysql-test/rocksdb/t/mariadb_ignore_dirs.test new file mode 100644 index 00000000000..49591dd612a --- /dev/null +++ b/storage/rocksdb/mysql-test/rocksdb/t/mariadb_ignore_dirs.test @@ -0,0 +1,17 @@ +--source include/have_rocksdb.inc + + +--echo # +--echo # RocksDB plugin adds #rocksdb to ignore_db_dirs +--echo # + +select @@ignore_db_dirs; + +--let $_mysqld_option=--ignore-db-dirs=aa --ignore-db-dirs=bbb +--source include/restart_mysqld_with_option.inc + +select @@ignore_db_dirs; + +--let $_mysqld_option=--ignore-db-dirs=#rocksdb +--source include/restart_mysqld_with_option.inc + From bdeb27a0005c7dfcd09a05ed50f4ee54786916e7 Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Sun, 10 Dec 2017 19:22:48 +0300 Subject: [PATCH 27/32] MDEV-14123: .rocksdb folder may break workflow which re-create data directory Part #2: Adjust other parts of testsuite to take into account that RocksDB files are in "#rocksdb" now. --- storage/rocksdb/mysql-test/rocksdb/r/rocksdb.result | 2 +- storage/rocksdb/mysql-test/rocksdb/t/bulk_load.inc | 2 +- storage/rocksdb/mysql-test/rocksdb/t/compact_deletes_test.inc | 2 +- storage/rocksdb/mysql-test/rocksdb/t/drop_table2.test | 4 ++-- storage/rocksdb/mysql-test/rocksdb/t/optimize_table.inc | 2 +- .../mysql-test/rocksdb_hotbackup/include/stream_run.sh | 4 ++-- .../rocksdb_rpl/t/rpl_gtid_crash_safe_wal_corrupt.inc | 4 ++-- .../rocksdb_sys_vars/r/rocksdb_datadir_basic.result | 2 +- storage/rocksdb/mysql-test/storage_engine/cleanup_engine.inc | 4 ++-- 9 files changed, 13 insertions(+), 13 deletions(-) diff --git a/storage/rocksdb/mysql-test/rocksdb/r/rocksdb.result b/storage/rocksdb/mysql-test/rocksdb/r/rocksdb.result index ed0688c96ad..d791cf98bb0 100644 --- a/storage/rocksdb/mysql-test/rocksdb/r/rocksdb.result +++ b/storage/rocksdb/mysql-test/rocksdb/r/rocksdb.result @@ -898,7 +898,7 @@ rocksdb_concurrent_prepare ON rocksdb_create_checkpoint rocksdb_create_if_missing ON rocksdb_create_missing_column_families OFF -rocksdb_datadir ./.rocksdb +rocksdb_datadir ./#rocksdb rocksdb_db_write_buffer_size 0 rocksdb_deadlock_detect OFF rocksdb_deadlock_detect_depth 50 diff --git a/storage/rocksdb/mysql-test/rocksdb/t/bulk_load.inc b/storage/rocksdb/mysql-test/rocksdb/t/bulk_load.inc index 42cab5ad8c1..87cb1f70f32 100644 --- a/storage/rocksdb/mysql-test/rocksdb/t/bulk_load.inc +++ b/storage/rocksdb/mysql-test/rocksdb/t/bulk_load.inc @@ -134,7 +134,7 @@ select count(b) from t3; # the server starts --let $tmpext = .bulk_load.tmp --let $MYSQLD_DATADIR= `SELECT @@datadir` ---let $datadir = $MYSQLD_DATADIR/.rocksdb +--let $datadir = $MYSQLD_DATADIR/#rocksdb --write_file $datadir/test$tmpext dummy data EOF diff --git a/storage/rocksdb/mysql-test/rocksdb/t/compact_deletes_test.inc b/storage/rocksdb/mysql-test/rocksdb/t/compact_deletes_test.inc index 19a16fbe3a7..0a0d670505f 100644 --- a/storage/rocksdb/mysql-test/rocksdb/t/compact_deletes_test.inc +++ b/storage/rocksdb/mysql-test/rocksdb/t/compact_deletes_test.inc @@ -44,7 +44,7 @@ perl; print "wait_for_delete: $ENV{no_more_deletes}\n"; while ($retry++ < $num_retries) { $total_d=$total_e=0; - for $f (<$ENV{MYSQLTEST_VARDIR}/mysqld.1/data/.rocksdb/*.sst>) { + for $f (<$ENV{MYSQLTEST_VARDIR}/mysqld.1/data/#rocksdb/*.sst>) { # excluding system cf $filename= "$ENV{MARIAROCKS_SST_DUMP} --command=scan --output_hex --file=$f"; open(D, '-|', $filename) || die("Can't open file $filename: $!"); diff --git a/storage/rocksdb/mysql-test/rocksdb/t/drop_table2.test b/storage/rocksdb/mysql-test/rocksdb/t/drop_table2.test index 14d856cc0c5..a9012aea80a 100644 --- a/storage/rocksdb/mysql-test/rocksdb/t/drop_table2.test +++ b/storage/rocksdb/mysql-test/rocksdb/t/drop_table2.test @@ -97,7 +97,7 @@ set @@global.rocksdb_compact_cf = 'rev:cf2'; set @@global.rocksdb_compact_cf = 'default'; perl; -$size+=-s $_ for (<$ENV{MYSQLTEST_VARDIR}/mysqld.1/data/.rocksdb/*.sst>); +$size+=-s $_ for (<$ENV{MYSQLTEST_VARDIR}/mysqld.1/data/\#rocksdb/*.sst>); $filename= "$ENV{MYSQLTEST_VARDIR}/tmp/size_output"; open(F, '>', $filename) || die("Can't open file $filename: $!"); print F $size; @@ -121,7 +121,7 @@ let $wait_condition = select count(*) = 0 # Check that space is reclaimed perl; -$size+=-s $_ for (<$ENV{MYSQLTEST_VARDIR}/mysqld.1/data/.rocksdb/*.sst>); +$size+=-s $_ for (<$ENV{MYSQLTEST_VARDIR}/mysqld.1/data/\#rocksdb/*.sst>); $filename= "$ENV{MYSQLTEST_VARDIR}/tmp/size_output"; open(F, '<', $filename) || die("Can't open file $filename: $!"); $old=; diff --git a/storage/rocksdb/mysql-test/rocksdb/t/optimize_table.inc b/storage/rocksdb/mysql-test/rocksdb/t/optimize_table.inc index 9d03aae5c0c..5f66937cef1 100644 --- a/storage/rocksdb/mysql-test/rocksdb/t/optimize_table.inc +++ b/storage/rocksdb/mysql-test/rocksdb/t/optimize_table.inc @@ -1,7 +1,7 @@ # run a check script to verify sst files reduced enough during each optimize table perl; -$size += -s $_ for (<$ENV{datadir}/.rocksdb/*.sst>); +$size += -s $_ for (<$ENV{datadir}/#rocksdb/*.sst>); $file= "$ENV{MYSQL_TMP_DIR}/sst_size.dat"; if (-f $file) { diff --git a/storage/rocksdb/mysql-test/rocksdb_hotbackup/include/stream_run.sh b/storage/rocksdb/mysql-test/rocksdb_hotbackup/include/stream_run.sh index ecf8a851267..b6735abb0a9 100755 --- a/storage/rocksdb/mysql-test/rocksdb_hotbackup/include/stream_run.sh +++ b/storage/rocksdb/mysql-test/rocksdb_hotbackup/include/stream_run.sh @@ -77,7 +77,7 @@ MOVEBACK_LOG="${MYSQL_TMP_DIR}/myrocks_hotbackup_moveback_log" echo "myrocks_hotbackup move-back phase" $MYSQL_MYROCKS_HOTBACKUP --move_back --datadir=$dest_data_dir \ - --rocksdb_datadir=$dest_data_dir/.rocksdb \ - --rocksdb_waldir=$dest_data_dir/.rocksdb \ + --rocksdb_datadir=$dest_data_dir/\#rocksdb \ + --rocksdb_waldir=$dest_data_dir/\#rocksdb \ --backup_dir=$backup_dir > $MOVEBACK_LOG 2>&1 diff --git a/storage/rocksdb/mysql-test/rocksdb_rpl/t/rpl_gtid_crash_safe_wal_corrupt.inc b/storage/rocksdb/mysql-test/rocksdb_rpl/t/rpl_gtid_crash_safe_wal_corrupt.inc index a52bfc9186d..5e5961f3aef 100644 --- a/storage/rocksdb/mysql-test/rocksdb_rpl/t/rpl_gtid_crash_safe_wal_corrupt.inc +++ b/storage/rocksdb/mysql-test/rocksdb_rpl/t/rpl_gtid_crash_safe_wal_corrupt.inc @@ -40,7 +40,7 @@ select * from mysql.slave_gtid_info; --write_file $MYSQL_TMP_DIR/truncate_tail_wal.sh #!/bin/bash -F=`ls -t $slave_data_dir/.rocksdb/*.log | head -n 1` +F=`ls -t $slave_data_dir/\#rocksdb/*.log | head -n 1` SIZE=`stat -c %s $F` NEW_SIZE=`expr $SIZE - 10` truncate -s $NEW_SIZE $F @@ -116,7 +116,7 @@ connection slave; #!/bin/bash # expected to be around 950 bytes -F=`ls -t $slave_data_dir/.rocksdb/*.log | head -n 1` +F=`ls -t $slave_data_dir/\#rocksdb/*.log | head -n 1` SIZE=`stat -c %s $F` OFFSET=$(( $SIZE-500 )) dd bs=1 if=/dev/zero of=$F count=100 seek=$OFFSET conv=notrunc diff --git a/storage/rocksdb/mysql-test/rocksdb_sys_vars/r/rocksdb_datadir_basic.result b/storage/rocksdb/mysql-test/rocksdb_sys_vars/r/rocksdb_datadir_basic.result index a3f9eff6c1f..3215624bad8 100644 --- a/storage/rocksdb/mysql-test/rocksdb_sys_vars/r/rocksdb_datadir_basic.result +++ b/storage/rocksdb/mysql-test/rocksdb_sys_vars/r/rocksdb_datadir_basic.result @@ -1,7 +1,7 @@ SET @start_global_value = @@global.ROCKSDB_DATADIR; SELECT @start_global_value; @start_global_value -./.rocksdb +./#rocksdb "Trying to set variable @@global.ROCKSDB_DATADIR to 444. It should fail because it is readonly." SET @@global.ROCKSDB_DATADIR = 444; ERROR HY000: Variable 'rocksdb_datadir' is a read only variable diff --git a/storage/rocksdb/mysql-test/storage_engine/cleanup_engine.inc b/storage/rocksdb/mysql-test/storage_engine/cleanup_engine.inc index 4f6c586172d..e6fe915ed38 100644 --- a/storage/rocksdb/mysql-test/storage_engine/cleanup_engine.inc +++ b/storage/rocksdb/mysql-test/storage_engine/cleanup_engine.inc @@ -12,14 +12,14 @@ --let $datadir= `SELECT @@datadir` --error 0,1 ---file_exists $datadir/.rocksdb/* +--file_exists $datadir/\#rocksdb/* if (!$mysql_errno) { --enable_reconnect --exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect --shutdown_server --source include/wait_until_disconnected.inc - --rmdir $datadir/.rocksdb + --rmdir $datadir/\#rocksdb --exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect --source include/wait_until_connected_again.inc } From 1e6ac94451ff11c0a96843c26b55fecb9e30a57d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 11 Dec 2017 12:37:19 +0200 Subject: [PATCH 28/32] Correct the comment of row_vers_impl_x_locked() --- storage/innobase/include/row0vers.h | 20 ++++++------ storage/innobase/row/row0vers.cc | 49 +++++++++++++++-------------- 2 files changed, 35 insertions(+), 34 deletions(-) diff --git a/storage/innobase/include/row0vers.h b/storage/innobase/include/row0vers.h index b28533578e1..576b53358f8 100644 --- a/storage/innobase/include/row0vers.h +++ b/storage/innobase/include/row0vers.h @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1997, 2016, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2017, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -38,19 +39,18 @@ Created 2/6/1997 Heikki Tuuri // Forward declaration class ReadView; -/*****************************************************************//** -Finds out if an active transaction has inserted or modified a secondary +/** Determine if an active transaction has inserted or modified a secondary index record. -@return 0 if committed, else the active transaction id; -NOTE that this function can return false positives but never false -negatives. The caller must confirm all positive results by calling -trx_is_active() while holding lock_sys->mutex. */ +@param[in] rec secondary index record +@param[in] index secondary index +@param[in] offsets rec_get_offsets(rec, index) +@return the active transaction; trx_release_reference() must be invoked +@retval NULL if the record was committed */ trx_t* row_vers_impl_x_locked( -/*===================*/ - const rec_t* rec, /*!< in: record in a secondary index */ - dict_index_t* index, /*!< in: the secondary index */ - const ulint* offsets);/*!< in: rec_get_offsets(rec, index) */ + const rec_t* rec, + dict_index_t* index, + const ulint* offsets); /*****************************************************************//** Finds out if we must preserve a delete marked earlier version of a clustered diff --git a/storage/innobase/row/row0vers.cc b/storage/innobase/row/row0vers.cc index c5d08e1ece6..23ba751ea67 100644 --- a/storage/innobase/row/row0vers.cc +++ b/storage/innobase/row/row0vers.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1997, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, MariaDB Corporation +Copyright (c) 2017, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -63,23 +63,25 @@ row_vers_non_vc_match( const dtuple_t* ientry, mem_heap_t* heap, ulint* n_non_v_col); -/*****************************************************************//** -Finds out if an active transaction has inserted or modified a secondary +/** Determine if an active transaction has inserted or modified a secondary index record. -@return 0 if committed, else the active transaction id; -NOTE that this function can return false positives but never false -negatives. The caller must confirm all positive results by calling -trx_is_active() while holding lock_sys->mutex. */ +@param[in] clust_rec clustered index record +@param[in] clust_index clustered index +@param[in] rec secondary index record +@param[in] index secondary index +@param[in] offsets rec_get_offsets(rec, index) +@param[in,out] mtr mini-transaction +@return the active transaction; trx_release_reference() must be invoked +@retval NULL if the record was committed */ UNIV_INLINE trx_t* row_vers_impl_x_locked_low( -/*=======================*/ - const rec_t* clust_rec, /*!< in: clustered index record */ - dict_index_t* clust_index, /*!< in: the clustered index */ - const rec_t* rec, /*!< in: secondary index record */ - dict_index_t* index, /*!< in: the secondary index */ - const ulint* offsets, /*!< in: rec_get_offsets(rec, index) */ - mtr_t* mtr) /*!< in/out: mini-transaction */ + const rec_t* clust_rec, + dict_index_t* clust_index, + const rec_t* rec, + dict_index_t* index, + const ulint* offsets, + mtr_t* mtr) { trx_id_t trx_id; ibool corrupt; @@ -325,19 +327,18 @@ result_check: DBUG_RETURN(trx); } -/*****************************************************************//** -Finds out if an active transaction has inserted or modified a secondary +/** Determine if an active transaction has inserted or modified a secondary index record. -@return 0 if committed, else the active transaction id; -NOTE that this function can return false positives but never false -negatives. The caller must confirm all positive results by calling -trx_is_active() while holding lock_sys->mutex. */ +@param[in] rec secondary index record +@param[in] index secondary index +@param[in] offsets rec_get_offsets(rec, index) +@return the active transaction; trx_release_reference() must be invoked +@retval NULL if the record was committed */ trx_t* row_vers_impl_x_locked( -/*===================*/ - const rec_t* rec, /*!< in: record in a secondary index */ - dict_index_t* index, /*!< in: the secondary index */ - const ulint* offsets)/*!< in: rec_get_offsets(rec, index) */ + const rec_t* rec, + dict_index_t* index, + const ulint* offsets) { mtr_t mtr; trx_t* trx; From 434c9e6f0e6c6aa09953f2367ab4db42a1f26f53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 11 Dec 2017 13:55:05 +0200 Subject: [PATCH 29/32] MDEV-14614 InnoDB: Failing assertion in dict_stats_rename_table() dict_stats_rename_table(): After DB_LOCK_WAIT_TIMEOUT or DB_DUPLICATE_KEY, reset the trx->error_state before retrying. Also, properly treat DB_DEADLOCK as a hard error. --- storage/innobase/dict/dict0stats.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/storage/innobase/dict/dict0stats.cc b/storage/innobase/dict/dict0stats.cc index e4aca96da4b..662ea959b9e 100644 --- a/storage/innobase/dict/dict0stats.cc +++ b/storage/innobase/dict/dict0stats.cc @@ -3659,8 +3659,8 @@ dict_stats_rename_table( new_db_utf8, new_table_utf8, trx); mutex_exit(&dict_sys->mutex); /* fall through */ - case DB_DEADLOCK: case DB_LOCK_WAIT_TIMEOUT: + trx->error_state = DB_SUCCESS; os_thread_sleep(200000 /* 0.2 sec */); continue; case DB_STATS_DO_NOT_EXIST: @@ -3701,8 +3701,8 @@ dict_stats_rename_table( new_db_utf8, new_table_utf8, trx); mutex_exit(&dict_sys->mutex); /* fall through */ - case DB_DEADLOCK: case DB_LOCK_WAIT_TIMEOUT: + trx->error_state = DB_SUCCESS; os_thread_sleep(200000 /* 0.2 sec */); continue; case DB_STATS_DO_NOT_EXIST: From 13b9ec651a807be59139fc295431d7ad14d3c8db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 11 Dec 2017 14:39:53 +0200 Subject: [PATCH 30/32] MDEV-14589 InnoDB should not lock a delete-marked record When the transaction isolation level is SERIALIZABLE, or when a locking read is performed in the REPEATABLE READ isolation level, InnoDB must lock delete-marked records in order to prevent another transaction from inserting something. However, at READ UNCOMMITTED or READ COMMITTED isolation level or when the parameter innodb_locks_unsafe_for_binlog is set, the repeatability of the reads does not matter, and there is no need to lock any records. row_search_mvcc(): Skip locks on delete-marked committed records upfront, instead of invoking row_unlock_for_mysql() afterwards. The unlocking never worked for secondary index records. --- mysql-test/suite/innodb/r/lock_deleted.result | 57 +++++++++++++++ mysql-test/suite/innodb/t/lock_deleted.test | 72 +++++++++++++++++++ storage/innobase/row/row0sel.cc | 51 +++++++++---- 3 files changed, 167 insertions(+), 13 deletions(-) create mode 100644 mysql-test/suite/innodb/r/lock_deleted.result create mode 100644 mysql-test/suite/innodb/t/lock_deleted.test diff --git a/mysql-test/suite/innodb/r/lock_deleted.result b/mysql-test/suite/innodb/r/lock_deleted.result new file mode 100644 index 00000000000..0fcb8bd5aa8 --- /dev/null +++ b/mysql-test/suite/innodb/r/lock_deleted.result @@ -0,0 +1,57 @@ +connect stop_purge, localhost, root,,; +START TRANSACTION WITH CONSISTENT SNAPSHOT; +connect delete, localhost, root,,; +connection default; +CREATE TABLE t1(a INT PRIMARY KEY, b INT UNIQUE) ENGINE=InnoDB; +INSERT INTO t1 VALUES(1,1); +DELETE FROM t1; +SET DEBUG_SYNC='row_ins_sec_index_unique SIGNAL inserted WAIT_FOR locked'; +BEGIN; +INSERT INTO t1 VALUES(1,1); +connection delete; +SET DEBUG_SYNC='now WAIT_FOR inserted'; +SET DEBUG_SYNC='innodb_row_search_for_mysql_exit SIGNAL locked'; +SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; +BEGIN; +DELETE FROM t1 WHERE b=1; +connection default; +connection delete; +COMMIT; +connection default; +SET DEBUG_SYNC='RESET'; +ROLLBACK; +SET DEBUG_SYNC='row_ins_sec_index_unique SIGNAL inserted WAIT_FOR locked'; +BEGIN; +INSERT INTO t1 VALUES(1,1); +connection delete; +SET DEBUG_SYNC='now WAIT_FOR inserted'; +SET DEBUG_SYNC='innodb_row_search_for_mysql_exit SIGNAL locked'; +SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; +BEGIN; +DELETE FROM t1 WHERE b=1; +connection default; +connection delete; +COMMIT; +connection default; +SET DEBUG_SYNC='RESET'; +ROLLBACK; +SET DEBUG_SYNC='row_ins_sec_index_unique SIGNAL inserted WAIT_FOR locked'; +BEGIN; +SET innodb_lock_wait_timeout=1; +INSERT INTO t1 VALUES(1,1); +connection delete; +SET DEBUG_SYNC='now WAIT_FOR inserted'; +SET DEBUG_SYNC='innodb_row_search_for_mysql_exit SIGNAL locked'; +SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ; +BEGIN; +DELETE FROM t1 WHERE b=1; +connection default; +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +COMMIT; +SET DEBUG_SYNC='RESET'; +connection delete; +COMMIT; +disconnect delete; +disconnect stop_purge; +connection default; +DROP TABLE t1; diff --git a/mysql-test/suite/innodb/t/lock_deleted.test b/mysql-test/suite/innodb/t/lock_deleted.test new file mode 100644 index 00000000000..8dbad90d354 --- /dev/null +++ b/mysql-test/suite/innodb/t/lock_deleted.test @@ -0,0 +1,72 @@ +--source include/have_innodb.inc +--source include/have_debug.inc +--source include/have_debug_sync.inc + +--source include/count_sessions.inc + +connect(stop_purge, localhost, root,,); +START TRANSACTION WITH CONSISTENT SNAPSHOT; +connect(delete, localhost, root,,); +connection default; + +CREATE TABLE t1(a INT PRIMARY KEY, b INT UNIQUE) ENGINE=InnoDB; +INSERT INTO t1 VALUES(1,1); +DELETE FROM t1; + +let $i=2; +while ($i) { +let $iso= `SELECT CASE $i WHEN 1 THEN 'UNCOMMITTED' ELSE 'COMMITTED' END`; + +SET DEBUG_SYNC='row_ins_sec_index_unique SIGNAL inserted WAIT_FOR locked'; +BEGIN; +send INSERT INTO t1 VALUES(1,1); + +connection delete; +SET DEBUG_SYNC='now WAIT_FOR inserted'; +SET DEBUG_SYNC='innodb_row_search_for_mysql_exit SIGNAL locked'; +eval SET SESSION TRANSACTION ISOLATION LEVEL READ $iso; +BEGIN; +send DELETE FROM t1 WHERE b=1; + +connection default; +reap; +connection delete; +reap; +COMMIT; + +connection default; +SET DEBUG_SYNC='RESET'; +ROLLBACK; + +dec $i; +} + +SET DEBUG_SYNC='row_ins_sec_index_unique SIGNAL inserted WAIT_FOR locked'; +BEGIN; +SET innodb_lock_wait_timeout=1; +send INSERT INTO t1 VALUES(1,1); + +connection delete; +SET DEBUG_SYNC='now WAIT_FOR inserted'; +SET DEBUG_SYNC='innodb_row_search_for_mysql_exit SIGNAL locked'; +SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ; +BEGIN; +send DELETE FROM t1 WHERE b=1; + +connection default; +--error ER_LOCK_WAIT_TIMEOUT +reap; +COMMIT; +SET DEBUG_SYNC='RESET'; + +connection delete; +reap; +COMMIT; + +disconnect delete; +disconnect stop_purge; + +connection default; +DROP TABLE t1; + +--source include/wait_until_count_sessions.inc diff --git a/storage/innobase/row/row0sel.cc b/storage/innobase/row/row0sel.cc index 9bf71592f54..9cad3ab7de6 100644 --- a/storage/innobase/row/row0sel.cc +++ b/storage/innobase/row/row0sel.cc @@ -4890,9 +4890,44 @@ wrong_offs: ulint lock_type; + if (srv_locks_unsafe_for_binlog + || trx->isolation_level <= TRX_ISO_READ_COMMITTED) { + /* At READ COMMITTED or READ UNCOMMITTED + isolation levels, do not lock committed + delete-marked records. */ + if (!rec_get_deleted_flag(rec, comp)) { + goto no_gap_lock; + } + if (index == clust_index) { + trx_id_t trx_id = row_get_rec_trx_id( + rec, index, offsets); + /* In delete-marked records, DB_TRX_ID must + always refer to an existing undo log record. */ + ut_ad(trx_id); + if (!trx_rw_is_active(trx_id, NULL, false)) { + /* The clustered index record + was delete-marked in a committed + transaction. Ignore the record. */ + goto locks_ok_del_marked; + } + } else if (trx_t* trx = row_vers_impl_x_locked( + rec, index, offsets)) { + /* The record belongs to an active + transaction. We must acquire a lock. */ + trx_release_reference(trx); + } else { + /* The secondary index record does not + point to a delete-marked clustered index + record that belongs to an active transaction. + Ignore the secondary index record, because + it is not locked. */ + goto next_rec; + } + + goto no_gap_lock; + } + if (!set_also_gap_locks - || srv_locks_unsafe_for_binlog - || trx->isolation_level <= TRX_ISO_READ_COMMITTED || (unique_search && !rec_get_deleted_flag(rec, comp)) || dict_index_is_spatial(index)) { @@ -5096,6 +5131,7 @@ locks_ok: page_rec_is_comp() cannot be used! */ if (rec_get_deleted_flag(rec, comp)) { +locks_ok_del_marked: /* In delete-marked records, DB_TRX_ID must always refer to an existing undo log record. */ ut_ad(index != clust_index @@ -5103,17 +5139,6 @@ locks_ok: /* The record is delete-marked: we can skip it */ - if ((srv_locks_unsafe_for_binlog - || trx->isolation_level <= TRX_ISO_READ_COMMITTED) - && prebuilt->select_lock_type != LOCK_NONE - && !did_semi_consistent_read) { - - /* No need to keep a lock on a delete-marked record - if we do not want to use next-key locking. */ - - row_unlock_for_mysql(prebuilt, TRUE); - } - /* This is an optimization to skip setting the next key lock on the record that follows this delete-marked record. This optimization works because of the unique search criteria From 8695c816e7a0ebc6f2922ce1e1da4f2c50933509 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 11 Dec 2017 15:57:21 +0200 Subject: [PATCH 31/32] Wrap __atomic_compare_exchange_n() as valid C --- include/my_atomic.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/my_atomic.h b/include/my_atomic.h index c6abcda2d62..237efdd952b 100644 --- a/include/my_atomic.h +++ b/include/my_atomic.h @@ -374,18 +374,18 @@ extern int my_atomic_initialize(); #define my_atomic_add64_explicit(P, A, O) __atomic_fetch_add((P), (A), (O)) #define my_atomic_cas32_weak_explicit(P, E, D, S, F) \ - __atomic_compare_exchange_n((P), (E), (D), true, (S), (F)) + __atomic_compare_exchange_n((P), (E), (D), 1, (S), (F)) #define my_atomic_cas64_weak_explicit(P, E, D, S, F) \ - __atomic_compare_exchange_n((P), (E), (D), true, (S), (F)) + __atomic_compare_exchange_n((P), (E), (D), 1, (S), (F)) #define my_atomic_casptr_weak_explicit(P, E, D, S, F) \ - __atomic_compare_exchange_n((P), (E), (D), true, (S), (F)) + __atomic_compare_exchange_n((P), (E), (D), 1, (S), (F)) #define my_atomic_cas32_strong_explicit(P, E, D, S, F) \ - __atomic_compare_exchange_n((P), (E), (D), false, (S), (F)) + __atomic_compare_exchange_n((P), (E), (D), 0, (S), (F)) #define my_atomic_cas64_strong_explicit(P, E, D, S, F) \ - __atomic_compare_exchange_n((P), (E), (D), false, (S), (F)) + __atomic_compare_exchange_n((P), (E), (D), 0, (S), (F)) #define my_atomic_casptr_strong_explicit(P, E, D, S, F) \ - __atomic_compare_exchange_n((P), (E), (D), false, (S), (F)) + __atomic_compare_exchange_n((P), (E), (D), 0, (S), (F)) #else #define MY_MEMORY_ORDER_RELAXED #define MY_MEMORY_ORDER_CONSUME From e12f77a7e3c29a1c1418e2b87194d0f06e2711e2 Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Tue, 12 Dec 2017 01:33:03 +0300 Subject: [PATCH 32/32] MDEV-14389: MyRocks and NOPAD collations Disallow use of NOPAD collations in indexed columns. --- sql/share/errmsg-utf8.txt | 4 +++ storage/rocksdb/ha_rocksdb.cc | 27 ++++++++++++++++--- .../rocksdb/r/mariadb_port_fixes.result | 9 +++++++ .../rocksdb/t/mariadb_port_fixes.test | 13 +++++++++ 4 files changed, 50 insertions(+), 3 deletions(-) diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 32f1a25e126..528dc7107e6 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -7749,3 +7749,7 @@ ER_NET_OK_PACKET_TOO_LARGE ER_GEOJSON_EMPTY_COORDINATES eng "Incorrect GeoJSON format - empty 'coordinates' array." + +ER_MYROCKS_CANT_NOPAD_COLLATION + eng "MyRocks doesn't currently support collations with \"No pad\" attribute." + diff --git a/storage/rocksdb/ha_rocksdb.cc b/storage/rocksdb/ha_rocksdb.cc index e08329ab61e..564832993ce 100644 --- a/storage/rocksdb/ha_rocksdb.cc +++ b/storage/rocksdb/ha_rocksdb.cc @@ -5964,6 +5964,19 @@ rdb_is_index_collation_supported(const my_core::Field *const field) { return true; } + +static bool +rdb_field_uses_nopad_collation(const my_core::Field *const field) { + const my_core::enum_field_types type = field->real_type(); + /* Handle [VAR](CHAR|BINARY) or TEXT|BLOB */ + if (type == MYSQL_TYPE_VARCHAR || type == MYSQL_TYPE_STRING || + type == MYSQL_TYPE_BLOB) { + return (field->charset()->state & MY_CS_NOPAD); + } + return false; +} + + /* Create structures needed for storing data in rocksdb. This is called when the table is created. The structures will be shared by all TABLE* objects. @@ -6072,8 +6085,7 @@ int ha_rocksdb::create_cfs( for (uint i = 0; i < tbl_def_arg->m_key_count; i++) { rocksdb::ColumnFamilyHandle *cf_handle; - if (rocksdb_strict_collation_check && - !is_hidden_pk(i, table_arg, tbl_def_arg) && + if (!is_hidden_pk(i, table_arg, tbl_def_arg) && tbl_def_arg->base_tablename().find(tmp_file_prefix) != 0) { if (!tsys_set) { @@ -6085,7 +6097,16 @@ int ha_rocksdb::create_cfs( for (uint part = 0; part < table_arg->key_info[i].ext_key_parts; part++) { - if (!rdb_is_index_collation_supported( + /* MariaDB: disallow NOPAD collations */ + if (rdb_field_uses_nopad_collation( + table_arg->key_info[i].key_part[part].field)) + { + my_error(ER_MYROCKS_CANT_NOPAD_COLLATION, MYF(0)); + DBUG_RETURN(HA_EXIT_FAILURE); + } + + if (rocksdb_strict_collation_check && + !rdb_is_index_collation_supported( table_arg->key_info[i].key_part[part].field) && !rdb_collation_exceptions->matches(tablename_sys)) { std::string collation_err; diff --git a/storage/rocksdb/mysql-test/rocksdb/r/mariadb_port_fixes.result b/storage/rocksdb/mysql-test/rocksdb/r/mariadb_port_fixes.result index 686b5637f7d..f590fd22dff 100644 --- a/storage/rocksdb/mysql-test/rocksdb/r/mariadb_port_fixes.result +++ b/storage/rocksdb/mysql-test/rocksdb/r/mariadb_port_fixes.result @@ -55,3 +55,12 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t2 range a a 32 NULL # Using where drop table t1,t2; set global rocksdb_strict_collation_check=@tmp_rscc; +# +# MDEV-14389: MyRocks and NOPAD collations +# +create table t1 (pk varchar(10) collate latin1_nopad_bin, primary key(pk)) engine=rocksdb; +ERROR HY000: MyRocks doesn't currently support collations with "No pad" attribute. +set global rocksdb_strict_collation_check=off; +create table t1 (pk varchar(10) collate latin1_nopad_bin, primary key(pk)) engine=rocksdb; +ERROR HY000: MyRocks doesn't currently support collations with "No pad" attribute. +set global rocksdb_strict_collation_check=@tmp_rscc; diff --git a/storage/rocksdb/mysql-test/rocksdb/t/mariadb_port_fixes.test b/storage/rocksdb/mysql-test/rocksdb/t/mariadb_port_fixes.test index f003aaf2032..70a4f5b05cb 100644 --- a/storage/rocksdb/mysql-test/rocksdb/t/mariadb_port_fixes.test +++ b/storage/rocksdb/mysql-test/rocksdb/t/mariadb_port_fixes.test @@ -54,3 +54,16 @@ explain select a from t2 where a <'zzz'; drop table t1,t2; set global rocksdb_strict_collation_check=@tmp_rscc; + +--echo # +--echo # MDEV-14389: MyRocks and NOPAD collations +--echo # + +--error ER_MYROCKS_CANT_NOPAD_COLLATION +create table t1 (pk varchar(10) collate latin1_nopad_bin, primary key(pk)) engine=rocksdb; + +set global rocksdb_strict_collation_check=off; +--error ER_MYROCKS_CANT_NOPAD_COLLATION +create table t1 (pk varchar(10) collate latin1_nopad_bin, primary key(pk)) engine=rocksdb; + +set global rocksdb_strict_collation_check=@tmp_rscc;