From 94174db16b8d20ee0d719b23877b1835e88a8838 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Fri, 18 Jun 2010 20:14:10 +0400 Subject: [PATCH 001/115] A new implementation for the TABLE_SHARE cache in MDL subsystem. Fix a number of caveates that the previous implementation suffered from, including unprotected access to shared data and lax resource accounting (share->ref_count) that could lead to deadlocks. The new implementation still suffers from a number of potential deadlocks in some edge cases, and this is still not enabled by default. Especially since performance testing has shown that it gives only marginable (not even exceeding measuring accuracy) improvements. @todo: - Remove calls to close_cached_tables() with REFRESH_FAST, and have_lock, because they break the MDL cache. - rework FLUSH TABLES to not use close_cached_tables() - make sure that whenever we set TABLE_SHARE::version to 0 we free MDL cache references to it. --- sql/mdl.cc | 82 ++++++++++++------- sql/mdl.h | 5 +- sql/sql_base.cc | 209 +++++++++++++++++++++++++++++++++++++----------- sql/sql_yacc.yy | 12 ++- 4 files changed, 228 insertions(+), 80 deletions(-) diff --git a/sql/mdl.cc b/sql/mdl.cc index 184b3c6051d..22ad15d2360 100644 --- a/sql/mdl.cc +++ b/sql/mdl.cc @@ -284,8 +284,8 @@ public: public: /** The key of the object (data) being protected. */ MDL_key key; - void *cached_object; - mdl_cached_object_release_hook cached_object_release_hook; + /** A cached reference to the TABLE_SHARE. Protected by LOCK_open. */ + void *m_cached_object; /** Read-write lock protecting this lock context. @@ -362,8 +362,7 @@ public: MDL_lock(const MDL_key *key_arg) : key(key_arg), - cached_object(NULL), - cached_object_release_hook(NULL), + m_cached_object(NULL), m_ref_usage(0), m_ref_release(0), m_is_destroyed(FALSE) @@ -371,6 +370,8 @@ public: mysql_prlock_init(key_MDL_lock_rwlock, &m_rwlock); } + /* Overridden for TABLE objects, to support TABLE_SHARE cache in MDL. */ + virtual void release_cached_object() {} virtual ~MDL_lock() { mysql_prlock_destroy(&m_rwlock); @@ -458,6 +459,25 @@ private: }; +/** + A lock implementation for MDL_key::TABLE. +*/ + +class MDL_table_lock: public MDL_object_lock +{ +public: + MDL_table_lock(const MDL_key *key_arg) + : MDL_object_lock(key_arg) + { } +#ifdef DISABLED_UNTIL_GRL_IS_MADE_PART_OF_MDL + virtual void release_cached_object() + { + tdc_release_cached_share(&m_cached_object); + } +#endif +}; + + static MDL_map mdl_locks; extern "C" @@ -674,8 +694,7 @@ void MDL_map::remove(MDL_lock *lock) { uint ref_usage, ref_release; - if (lock->cached_object) - (*lock->cached_object_release_hook)(lock->cached_object); + lock->release_cached_object(); /* Destroy the MDL_lock object, but ensure that anyone that is @@ -839,6 +858,8 @@ inline MDL_lock *MDL_lock::create(const MDL_key *mdl_key) { case MDL_key::GLOBAL: return new MDL_global_lock(mdl_key); + case MDL_key::TABLE: + return new MDL_table_lock(mdl_key); default: return new MDL_object_lock(mdl_key); } @@ -1181,11 +1202,6 @@ void MDL_lock::reschedule_waiters() */ m_waiting.remove_ticket(ticket); m_granted.add_ticket(ticket); - - /* If we are granting an X lock, release the cached object. */ - if (ticket->get_type() == MDL_EXCLUSIVE && cached_object) - (*cached_object_release_hook)(cached_object); - cached_object= NULL; } /* If we could not update the wait slot of the waiter, @@ -1655,14 +1671,13 @@ MDL_context::try_acquire_lock_impl(MDL_request *mdl_request, { lock->m_granted.add_ticket(ticket); - if (mdl_request->type == MDL_EXCLUSIVE && lock->cached_object) - (*lock->cached_object_release_hook)(lock->cached_object); - lock->cached_object= NULL; - mysql_prlock_unlock(&lock->m_rwlock); m_tickets.push_front(ticket); + if (ticket->get_type() == MDL_EXCLUSIVE) + ticket->clear_cached_object(); + mdl_request->ticket= ticket; } else @@ -1864,6 +1879,9 @@ MDL_context::acquire_lock(MDL_request *mdl_request, ulong lock_wait_timeout) */ DBUG_ASSERT(wait_status == MDL_wait::GRANTED); + if (ticket->get_type() == MDL_EXCLUSIVE) + ticket->clear_cached_object(); + m_tickets.push_front(ticket); mdl_request->ticket= ticket; @@ -2450,7 +2468,7 @@ bool MDL_ticket::has_pending_conflicting_lock() const This function has the following usage pattern: - try to acquire an MDL lock - - when done, call for mdl_get_cached_object(). If it returns NULL, our + - when done, call for get_cached_object(). If it returns NULL, our thread has the only lock on this table. - look up TABLE_SHARE in the table definition cache - call mdl_set_cache_object() to assign the share to the opaque pointer. @@ -2460,28 +2478,33 @@ bool MDL_ticket::has_pending_conflicting_lock() const */ void -MDL_ticket::set_cached_object(void *cached_object, - mdl_cached_object_release_hook release_hook) +MDL_ticket::set_cached_object(void *cached_object) { - DBUG_ENTER("mdl_set_cached_object"); + DBUG_ENTER("MDL_ticket::set_cached_object"); DBUG_PRINT("enter", ("db=%s name=%s cached_object=%p", m_lock->key.db_name(), m_lock->key.name(), cached_object)); - /* - TODO: This assumption works now since we do get_cached_object() - and set_cached_object() in the same critical section. Once - this becomes false we will have to call release_hook here and - use additional mutex protecting 'cached_object' member. - */ - DBUG_ASSERT(!m_lock->cached_object); + mysql_mutex_assert_owner(&LOCK_open); + DBUG_ASSERT(m_lock->key.mdl_namespace() == MDL_key::TABLE); + DBUG_ASSERT(!m_lock->m_cached_object); - m_lock->cached_object= cached_object; - m_lock->cached_object_release_hook= release_hook; + m_lock->m_cached_object= cached_object; DBUG_VOID_RETURN; } +/** + A helper function to flush the table share cached in MDL. + @pre The ticket is acquired. +*/ + +void MDL_ticket::clear_cached_object() +{ + m_lock->release_cached_object(); +} + + /** Get a pointer to an opaque object that associated with the lock. @@ -2492,7 +2515,8 @@ MDL_ticket::set_cached_object(void *cached_object, void *MDL_ticket::get_cached_object() { - return m_lock->cached_object; + mysql_mutex_assert_owner(&LOCK_open); + return m_lock->m_cached_object; } diff --git a/sql/mdl.h b/sql/mdl.h index 43d88c143c0..ad3945f524c 100644 --- a/sql/mdl.h +++ b/sql/mdl.h @@ -397,8 +397,8 @@ public: bool has_pending_conflicting_lock() const; void *get_cached_object(); - void set_cached_object(void *cached_object, - mdl_cached_object_release_hook release_hook); + void set_cached_object(void *cached_object); + void clear_cached_object(); MDL_context *get_ctx() const { return m_ctx; } bool is_upgradable_or_exclusive() const { @@ -724,6 +724,7 @@ extern "C" const char *set_thd_proc_info(void *thd_arg, const char *info, const char *calling_function, const char *calling_file, const unsigned int calling_line); +extern void tdc_release_cached_share(void *ptr); #ifndef DBUG_OFF extern mysql_mutex_t LOCK_open; #endif diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 78862985e97..bd73dd57367 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -755,27 +755,6 @@ TABLE_SHARE *get_cached_table_share(const char *db, const char *table_name) } -#ifdef DISABLED_UNTIL_GRL_IS_MADE_PART_OF_MDL -/** - @brief Mark table share as having one more user (increase its reference - count). - - @param share Table share for which reference count should be increased. -*/ - -static void reference_table_share(TABLE_SHARE *share) -{ - DBUG_ENTER("reference_table_share"); - DBUG_ASSERT(share->ref_count); - mysql_mutex_assert_owner(&LOCK_open); - share->ref_count++; - DBUG_PRINT("exit", ("share: 0x%lx ref_count: %u", - (ulong) share, share->ref_count)); - DBUG_VOID_RETURN; -} -#endif - - /* Create a list for all open tables matching SQL expression @@ -941,6 +920,141 @@ static void kill_delayed_threads_for_table(TABLE_SHARE *share) } +#ifdef DISABLED_UNTIL_GRL_IS_MADE_PART_OF_MDL +/** + Flush MDL cached objects. + + How MDL table share cache works + ------------------------------- + Since we take a table share from the table definition + cache only after taking an MDL lock, the MDL lock + object is a convenient place to cache a pointer + to the table share. However, not all SQL in MySQL + takes an MDL lock prior to working with the TDC, + various forms of FLUSH TABLES (including SET GLOBAL + read_only) being the one and only exception. + + To make FLUSH TABLES work, and avoid having dangling + references to TABLE_SHARE objects in MDL subsystem + after a flush, we make sure that all references + to table shares are released whenever a flush comes. + This is done in this function. + + To sum up, the following invariants are held: + - no statement can work with a TABLE_SHARE without + a metadata lock. The only exception is FLUSH TABLES. + - a metadata lock object can be used to store + a cached reference (pointer) to the corresponding + TABLE_SHARE, if and only if this TABLE_SHARE is + not stale (version == refresh_version). In other words, + checking TABLE_SHARE version and setting the reference + must happen only in the same critical section protected + by LOCK_open. + - FLUSH will mark all subject TABLE_SHARE objects + as stale, and then will manually release all TABLE_SHARE + references in MDL cache. Since marking TABLE_SHARE + objects is done inside a critical section protected + by LOCK_open and prior to calling flush_mdl_cache(), + it's guaranteed that a flush will take place before + a new reference to the table share is established + in some other connection. +*/ + +bool flush_mdl_cache(THD *thd, TABLE_LIST *table_list) +{ + MDL_request_list mdl_requests; + MDL_request *mdl_request; + + DBUG_ENTER("flush_mdl_cache"); + + if (table_list == NULL) + { + mysql_mutex_lock(&LOCK_open); + for (uint idx= 0 ; idx < table_def_cache.records; idx++) + { + TABLE_SHARE *share=(TABLE_SHARE*) my_hash_element(&table_def_cache, + idx); + if (share->needs_reopen()) + { + mdl_request= MDL_request::create(MDL_key::TABLE, + share->db.str, + share->table_name.str, + MDL_SHARED_HIGH_PRIO, + thd->mem_root); + if (! mdl_request) + { + mysql_mutex_unlock(&LOCK_open); + DBUG_RETURN(TRUE); + } + mdl_requests.push_front(mdl_request); + } + } + mysql_mutex_unlock(&LOCK_open); + } + else + { + for (TABLE_LIST *tables= table_list; tables; tables= tables->next_global) + { + DBUG_ASSERT(tables->mdl_request.type == MDL_SHARED_HIGH_PRIO); + mdl_requests.push_front(&tables->mdl_request); + } + } + + for (MDL_request_list::Iterator it(mdl_requests); + (mdl_request= it++); ) + { + if (thd->mdl_context.try_acquire_lock(mdl_request)) + DBUG_RETURN(TRUE); + if (mdl_request->ticket) + { + mdl_request->ticket->clear_cached_object(); + thd->mdl_context.release_lock(mdl_request->ticket); + } + } + DBUG_RETURN(FALSE); +} + + +/** + @brief Helper function used by MDL subsystem for releasing TABLE_SHARE + objects in cases when it no longer wants to cache reference to it. +*/ + +void tdc_release_cached_share(void *ptr) +{ + TABLE_SHARE **share= (TABLE_SHARE **) ptr; + mysql_mutex_lock(&LOCK_open); + if (*share) + { + release_table_share(*share); + *share= NULL; + broadcast_refresh(); + } + mysql_mutex_unlock(&LOCK_open); +} + + +/** + @brief Mark table share as having one more user (increase its reference + count). + + @param share Table share for which reference count should be increased. +*/ + +static void tdc_reference_table_share(TABLE_SHARE *share) +{ + DBUG_ENTER("tdc_reference_table_share"); + DBUG_ASSERT(share->ref_count); + mysql_mutex_assert_owner(&LOCK_open); + share->ref_count++; + DBUG_PRINT("exit", ("share: 0x%lx ref_count: %u", + (ulong) share, share->ref_count)); + DBUG_VOID_RETURN; +} + + +#endif // DISABLED_UNTIL_GRL_IS_MADE_PART_OF + /* Close all tables which aren't in use by any thread @@ -1052,6 +1166,15 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables, bool have_lock, /* Wait until all threads have closed all the tables we are flushing. */ DBUG_PRINT("info", ("Waiting for other threads to close their open tables")); +#ifdef DISABLED_UNTIL_GRL_IS_MADE_PART_OF + /* + @todo We need to do this for fast refresh as well, otherwise + deadlocks are possible. + */ + if (flush_mdl_cache(thd, tables)) + goto err_with_reopen; +#endif // DISABLED_UNTIL_GRL_IS_MADE_PART_OF_MDL + while (found && ! thd->killed) { found= FALSE; @@ -2361,20 +2484,6 @@ end: } -/** - @brief Helper function used by MDL subsystem for releasing TABLE_SHARE - objects in cases when it no longer wants to cache reference to it. -*/ - -void table_share_release_hook(void *share) -{ - mysql_mutex_lock(&LOCK_open); - release_table_share((TABLE_SHARE*) share); - broadcast_refresh(); - mysql_mutex_unlock(&LOCK_open); -} - - /** An error handler which converts, if possible, ER_LOCK_DEADLOCK error that can occur when we are trying to acquire a metadata lock to @@ -2869,8 +2978,8 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, mysql_mutex_lock(&LOCK_open); #ifdef DISABLED_UNTIL_GRL_IS_MADE_PART_OF_MDL if (!(share= (TABLE_SHARE *) mdl_ticket->get_cached_object())) -#endif { +#endif // DISABLED_UNTIL_GRL_IS_MADE_PART_OF_MDL if (!(share= get_table_share_with_create(thd, table_list, key, key_length, OPEN_VIEW, &error, @@ -2927,14 +3036,15 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, #ifdef DISABLED_UNTIL_GRL_IS_MADE_PART_OF_MDL /* - We are going to to store extra reference to the share in MDL-subsystem - so we need to increase reference counter; + We are going to to store extra reference to the share + in MDL-subsystem so we need to increase reference counter. */ - reference_table_share(share); - mdl_ticket->set_cached_object(share, table_share_release_hook); -#endif + if (! share->needs_reopen()) + { + mdl_ticket->set_cached_object(share); + tdc_reference_table_share(share); + } } -#ifdef DISABLED_UNTIL_GRL_IS_MADE_PART_OF_MDL else { if (table_list->view) @@ -2943,19 +3053,19 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, check_and_update_table_version(thd, table_list, share); /* Always an error. */ DBUG_ASSERT(thd->is_error()); - goto err_unlock; + goto err_unlock2; } /* When we have cached TABLE_SHARE we know that is not a view. */ if (table_list->i_s_requested_object & OPEN_VIEW_ONLY) - goto err_unlock; + goto err_unlock2; /* We are going to use this share for construction of new TABLE object so reference counter should be increased. */ - reference_table_share(share); + tdc_reference_table_share(share); } -#endif +#endif // DISABLED_UNTIL_GRL_IS_MADE_PART_OF_MDL /* @@ -8755,6 +8865,13 @@ void tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type, automatically deleted once it is no longer referenced. */ share->version= 0; +#ifdef DISABLED_UNTIL_GRL_IS_MADE_PART_OF_MDL + /* + If lock type is not EXCLUSIVE, we must call + MDL_ticket::release_cached_object() here to make sure there + is no self-reference left on the share in MDL_lock. + */ +#endif while ((table= it++)) free_cache_entry(table); } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 1c673f4ca42..73935d2a0b1 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -11186,10 +11186,10 @@ flush_options: Lex->type|= REFRESH_TABLES; /* Set type of metadata and table locks for - FLUSH TABLES table_list WITH READ LOCK. + FLUSH TABLES table_list [WITH READ LOCK]. */ YYPS->m_lock_type= TL_READ_NO_INSERT; - YYPS->m_mdl_type= MDL_EXCLUSIVE; + YYPS->m_mdl_type= MDL_SHARED_HIGH_PRIO; } opt_table_list {} opt_with_read_lock {} @@ -11199,7 +11199,13 @@ flush_options: opt_with_read_lock: /* empty */ {} | WITH READ_SYM LOCK_SYM - { Lex->type|= REFRESH_READ_LOCK; } + { + TABLE_LIST *tables= Lex->query_tables; + Lex->type|= REFRESH_READ_LOCK; + /* We acquire an X lock currently and then downgrade. */ + for (; tables; tables= tables->next_global) + tables->mdl_request.set_type(MDL_EXCLUSIVE); + } ; flush_options_list: From cda93dfc8fdf88abdee25ff582db02bda0fbeed6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 21 Jun 2010 13:32:39 +0300 Subject: [PATCH 002/115] =?UTF-8?q?Merge=20Bug=20#54658=20fix=20from=20mys?= =?UTF-8?q?ql-5.1-innodb:=20----------------------------------------------?= =?UTF-8?q?--------------=20revno:=203515=20revision-id:=20marko.makela@or?= =?UTF-8?q?acle.com-20100621094008-o9fa153s3f09merw=20parent:=20vasil.dimo?= =?UTF-8?q?v@oracle.com-20100618085155-xnm469cbhedqea96=20committer:=20Mar?= =?UTF-8?q?ko=20M=C3=A4kel=C3=A4=20=20branch=20ni?= =?UTF-8?q?ck:=205.1-innodb=20timestamp:=20Mon=202010-06-21=2012:40:08=20+?= =?UTF-8?q?0300=20message:=20=20=20Bug=20#54658:=20InnoDB:=20Warning:=20al?= =?UTF-8?q?located=20tablespace=20%lu,=20old=20maximum=20was=200?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dict_check_tablespaces_and_store_max_id(): Initialize max_space_id and fil_system->max_assigned_id from DICT_HDR_MAX_SPACE_ID. fil_space_create(): Suppress the warning unless !recv_recovery_on (do not complain while applying the redo log). --- storage/innobase/dict/dict0load.c | 7 ++++++- storage/innobase/fil/fil0fil.c | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/storage/innobase/dict/dict0load.c b/storage/innobase/dict/dict0load.c index 6bf2c1d9d81..949c10264d9 100644 --- a/storage/innobase/dict/dict0load.c +++ b/storage/innobase/dict/dict0load.c @@ -644,7 +644,7 @@ dict_check_tablespaces_and_store_max_id( dict_index_t* sys_index; btr_pcur_t pcur; const rec_t* rec; - ulint max_space_id = 0; + ulint max_space_id; mtr_t mtr; mutex_enter(&(dict_sys->mutex)); @@ -655,6 +655,11 @@ dict_check_tablespaces_and_store_max_id( sys_index = UT_LIST_GET_FIRST(sys_tables->indexes); ut_a(!dict_table_is_comp(sys_tables)); + max_space_id = mtr_read_ulint(dict_hdr_get(&mtr) + + DICT_HDR_MAX_SPACE_ID, + MLOG_4BYTES, &mtr); + fil_set_max_space_id_if_bigger(max_space_id); + btr_pcur_open_at_index_side(TRUE, sys_index, BTR_SEARCH_LEAF, &pcur, TRUE, &mtr); loop: diff --git a/storage/innobase/fil/fil0fil.c b/storage/innobase/fil/fil0fil.c index 0d033c37879..1fbbebb748f 100644 --- a/storage/innobase/fil/fil0fil.c +++ b/storage/innobase/fil/fil0fil.c @@ -1214,7 +1214,7 @@ try_again: space->tablespace_version = fil_system->tablespace_version; space->mark = FALSE; - if (UNIV_LIKELY(purpose == FIL_TABLESPACE) + if (UNIV_LIKELY(purpose == FIL_TABLESPACE && !recv_recovery_on) && UNIV_UNLIKELY(id > fil_system->max_assigned_id)) { if (!fil_system->space_id_reuse_warned) { fil_system->space_id_reuse_warned = TRUE; From 23d05c1c6e2fb2a017838a21756f772a5df30ec7 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Mon, 21 Jun 2010 13:39:30 +0200 Subject: [PATCH 003/115] Bug#53593: Add some instrumentation to improve Valgrind sensitivity. Implement WITH_VALGRIND for the CMake build. --- CMakeLists.txt | 3 +++ config.h.cmake | 2 +- configure.cmake | 10 +++++++++- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 536fae9f4b1..f4c7f552e93 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -115,6 +115,9 @@ OPTION(ENABLED_PROFILING "Enable profiling" ON) OPTION(CYBOZU "" OFF) OPTION(BACKUP_TEST "" OFF) OPTION(WITHOUT_SERVER OFF) +IF(UNIX) + OPTION(WITH_VALGRIND "Valgrind instrumentation" OFF) +ENDIF() OPTION (WITH_UNIT_TESTS "Compile MySQL with unit tests" ON) MARK_AS_ADVANCED(CYBOZU BACKUP_TEST WITHOUT_SERVER DISABLE_SHARED) diff --git a/config.h.cmake b/config.h.cmake index c6c049814ba..3fa15a0706a 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -292,7 +292,7 @@ #define USE_MB 1 #define USE_MB_IDENT 1 - +#cmakedefine HAVE_VALGRIND /* Types we may use */ #cmakedefine SIZEOF_CHAR @SIZEOF_CHAR@ diff --git a/configure.cmake b/configure.cmake index 1727e2b2c10..246133f1fbe 100644 --- a/configure.cmake +++ b/configure.cmake @@ -777,7 +777,7 @@ ENDIF(NOT HAVE_POSIX_SIGNALS) # Assume regular sprintf SET(SPRINTFS_RETURNS_INT 1) -IF(CMAKE_COMPILER_IS_GNUXX) +IF(CMAKE_COMPILER_IS_GNUCXX) CHECK_CXX_SOURCE_COMPILES(" #include int main(int argc, char **argv) @@ -969,6 +969,14 @@ configuration. By default gcc built-in sync functions are used, if available and 'smp' configuration otherwise.") MARK_AS_ADVANCED(WITH_ATOMIC_LOCKS MY_ATOMIC_MODE_RWLOCK MY_ATOMIC_MODE_DUMMY) +IF(WITH_VALGRIND) + CHECK_INCLUDE_FILES("valgrind/memcheck.h;valgrind/valgrind.h" + HAVE_VALGRIND_HEADERS) + IF(HAVE_VALGRIND_HEADERS) + SET(HAVE_VALGRIND 1) + ENDIF() +ENDIF() + #-------------------------------------------------------------------- # Check for IPv6 support #-------------------------------------------------------------------- From 72e5d2ae119d23b6a750bb0943e7094bb00e56e4 Mon Sep 17 00:00:00 2001 From: Inaam Rana Date: Mon, 21 Jun 2010 11:52:09 -0400 Subject: [PATCH 004/115] Fixes bug#39168. Make a call to log_free_check() on all DML paths. --- storage/innobase/include/log0log.ic | 5 ++++- storage/innobase/row/row0ins.c | 9 +++++++++ storage/innobase/row/row0purge.c | 11 +++++++++++ storage/innobase/row/row0uins.c | 13 ++++++++++++- storage/innobase/row/row0umod.c | 14 +++++++++++++- storage/innobase/row/row0upd.c | 16 ++++++++++++++-- 6 files changed, 63 insertions(+), 5 deletions(-) diff --git a/storage/innobase/include/log0log.ic b/storage/innobase/include/log0log.ic index 139f4041a36..1ce00fd7313 100644 --- a/storage/innobase/include/log0log.ic +++ b/storage/innobase/include/log0log.ic @@ -433,7 +433,10 @@ void log_free_check(void) /*================*/ { - /* ut_ad(sync_thread_levels_empty()); */ + +#ifdef UNIV_SYNC_DEBUG + ut_ad(sync_thread_levels_empty_gen(TRUE)); +#endif /* UNIV_SYNC_DEBUG */ if (log_sys->check_flush_or_checkpoint) { diff --git a/storage/innobase/row/row0ins.c b/storage/innobase/row/row0ins.c index c882a065cd1..1880747e21a 100644 --- a/storage/innobase/row/row0ins.c +++ b/storage/innobase/row/row0ins.c @@ -51,6 +51,15 @@ Created 4/20/1996 Heikki Tuuri #define ROW_INS_PREV 1 #define ROW_INS_NEXT 2 +/************************************************************************* +IMPORTANT NOTE: Any operation that generates redo MUST check that there +is enough space in the redo log before for that operation. This is +done by calling log_free_check(). The reason for checking the +availability of the redo log space before the start of the operation is +that we MUST not hold any synchonization objects when performing the +check. +If you make a change in this module make sure that no codepath is +introduced where a call to log_free_check() is bypassed. */ /*********************************************************************//** Creates an insert node struct. diff --git a/storage/innobase/row/row0purge.c b/storage/innobase/row/row0purge.c index da9d31f333f..4b1bc258cea 100644 --- a/storage/innobase/row/row0purge.c +++ b/storage/innobase/row/row0purge.c @@ -44,6 +44,16 @@ Created 3/14/1997 Heikki Tuuri #include "row0mysql.h" #include "log0log.h" +/************************************************************************* +IMPORTANT NOTE: Any operation that generates redo MUST check that there +is enough space in the redo log before for that operation. This is +done by calling log_free_check(). The reason for checking the +availability of the redo log space before the start of the operation is +that we MUST not hold any synchonization objects when performing the +check. +If you make a change in this module make sure that no codepath is +introduced where a call to log_free_check() is bypassed. */ + /********************************************************************//** Creates a purge node to a query graph. @return own: purge node */ @@ -126,6 +136,7 @@ row_purge_remove_clust_if_poss_low( pcur = &(node->pcur); btr_cur = btr_pcur_get_btr_cur(pcur); + log_free_check(); mtr_start(&mtr); success = row_purge_reposition_pcur(mode, node, &mtr); diff --git a/storage/innobase/row/row0uins.c b/storage/innobase/row/row0uins.c index c35f1ef7a44..4d644e9de50 100644 --- a/storage/innobase/row/row0uins.c +++ b/storage/innobase/row/row0uins.c @@ -46,6 +46,16 @@ Created 2/25/1997 Heikki Tuuri #include "ibuf0ibuf.h" #include "log0log.h" +/************************************************************************* +IMPORTANT NOTE: Any operation that generates redo MUST check that there +is enough space in the redo log before for that operation. This is +done by calling log_free_check(). The reason for checking the +availability of the redo log space before the start of the operation is +that we MUST not hold any synchonization objects when performing the +check. +If you make a change in this module make sure that no codepath is +introduced where a call to log_free_check() is bypassed. */ + /***************************************************************//** Removes a clustered index record. The pcur in node was positioned on the record, now it is detached. @@ -151,7 +161,6 @@ row_undo_ins_remove_sec_low( mtr_t mtr; enum row_search_result search_result; - log_free_check(); mtr_start(&mtr); btr_cur = btr_pcur_get_btr_cur(&pcur); @@ -337,6 +346,7 @@ row_undo_ins( transactions. */ ut_a(trx_is_recv(node->trx)); } else { + log_free_check(); err = row_undo_ins_remove_sec(node->index, entry); if (err != DB_SUCCESS) { @@ -348,5 +358,6 @@ row_undo_ins( node->index = dict_table_get_next_index(node->index); } + log_free_check(); return(row_undo_ins_remove_clust_rec(node)); } diff --git a/storage/innobase/row/row0umod.c b/storage/innobase/row/row0umod.c index 75de18a0b7d..a49e7a68595 100644 --- a/storage/innobase/row/row0umod.c +++ b/storage/innobase/row/row0umod.c @@ -58,12 +58,22 @@ delete marked clustered index record was delete unmarked and possibly also some of its fields were changed. Now, it is possible that the delete marked version has become obsolete at the time the undo is started. */ +/************************************************************************* +IMPORTANT NOTE: Any operation that generates redo MUST check that there +is enough space in the redo log before for that operation. This is +done by calling log_free_check(). The reason for checking the +availability of the redo log space before the start of the operation is +that we MUST not hold any synchonization objects when performing the +check. +If you make a change in this module make sure that no codepath is +introduced where a call to log_free_check() is bypassed. */ + /***********************************************************//** Checks if also the previous version of the clustered index record was modified or inserted by the same transaction, and its undo number is such that it should be undone in the same rollback. @return TRUE if also previous modify or insert of this row should be undone */ -UNIV_INLINE +static ibool row_undo_mod_undo_also_prev_vers( /*=============================*/ @@ -231,6 +241,8 @@ row_undo_mod_clust( ut_ad(node && thr); + log_free_check(); + /* Check if also the previous version of the clustered index record should be undone in this same rollback operation */ diff --git a/storage/innobase/row/row0upd.c b/storage/innobase/row/row0upd.c index f1a90a3bf1c..39d5036da0d 100644 --- a/storage/innobase/row/row0upd.c +++ b/storage/innobase/row/row0upd.c @@ -92,6 +92,16 @@ the x-latch freed? The most efficient way for performing a searched delete is obviously to keep the x-latch for several steps of query graph execution. */ +/************************************************************************* +IMPORTANT NOTE: Any operation that generates redo MUST check that there +is enough space in the redo log before for that operation. This is +done by calling log_free_check(). The reason for checking the +availability of the redo log space before the start of the operation is +that we MUST not hold any synchonization objects when performing the +check. +If you make a change in this module make sure that no codepath is +introduced where a call to log_free_check() is bypassed. */ + /***********************************************************//** Checks if an update vector changes some of the first ordering fields of an index record. This is only used in foreign key checks and we can assume @@ -1454,7 +1464,6 @@ row_upd_sec_index_entry( entry = row_build_index_entry(node->row, node->ext, index, heap); ut_a(entry); - log_free_check(); mtr_start(&mtr); /* Set the query thread, so that ibuf_insert_low() will be @@ -1557,7 +1566,7 @@ Updates the secondary index record if it is changed in the row update or deletes it if this is a delete. @return DB_SUCCESS if operation successfully completed, else error code or DB_LOCK_WAIT */ -UNIV_INLINE +static ulint row_upd_sec_step( /*=============*/ @@ -2049,6 +2058,7 @@ row_upd( if (node->state == UPD_NODE_UPDATE_CLUSTERED || node->state == UPD_NODE_INSERT_CLUSTERED) { + log_free_check(); err = row_upd_clust_step(node, thr); if (err != DB_SUCCESS) { @@ -2063,6 +2073,8 @@ row_upd( } while (node->index != NULL) { + + log_free_check(); err = row_upd_sec_step(node, thr); if (err != DB_SUCCESS) { From 84b51676bfbb87aefa61267621e0e6349e254bee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 22 Jun 2010 13:42:39 +0300 Subject: [PATCH 005/115] dict_load_column_low(): Initialize pos. Improve documentation. Approved by Jimmy Yang. --- storage/innobase/dict/dict0load.c | 13 ++++++++----- storage/innobase/include/dict0load.h | 3 ++- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/storage/innobase/dict/dict0load.c b/storage/innobase/dict/dict0load.c index 949c10264d9..b998b157d19 100644 --- a/storage/innobase/dict/dict0load.c +++ b/storage/innobase/dict/dict0load.c @@ -782,12 +782,13 @@ const char* dict_load_column_low( /*=================*/ dict_table_t* table, /*!< in/out: table, could be NULL - if we just polulate a dict_column_t + if we just populate a dict_column_t struct with information from a SYS_COLUMNS record */ mem_heap_t* heap, /*!< in/out: memory heap for temporary storage */ - dict_col_t* column, /*!< out: dict_column_t to fill */ + dict_col_t* column, /*!< out: dict_column_t to fill, + or NULL if table != NULL */ dulint* table_id, /*!< out: table id */ const char** col_name, /*!< out: column name */ const rec_t* rec) /*!< in: SYS_COLUMNS record */ @@ -800,6 +801,8 @@ dict_load_column_low( ulint col_len; ulint pos; + ut_ad(table || column); + if (UNIV_UNLIKELY(rec_get_deleted_flag(rec, 0))) { return("delete-marked record in SYS_COLUMNS"); } @@ -827,9 +830,9 @@ err_len: goto err_len; } - if (!table) { - pos = mach_read_from_4(field); - } else if (UNIV_UNLIKELY(table->n_def != mach_read_from_4(field))) { + pos = mach_read_from_4(field); + + if (UNIV_UNLIKELY(table && table->n_def != pos)) { return("SYS_COLUMNS.POS mismatch"); } diff --git a/storage/innobase/include/dict0load.h b/storage/innobase/include/dict0load.h index d85f8f7fc3e..461099efeba 100644 --- a/storage/innobase/include/dict0load.h +++ b/storage/innobase/include/dict0load.h @@ -109,7 +109,8 @@ dict_load_column_low( a SYS_COLUMNS record */ mem_heap_t* heap, /*!< in/out: memory heap for temporary storage */ - dict_col_t* column, /*!< out: dict_column_t to fill */ + dict_col_t* column, /*!< out: dict_column_t to fill, + or NULL if table != NULL */ dulint* table_id, /*!< out: table id */ const char** col_name, /*!< out: column name */ const rec_t* rec); /*!< in: SYS_COLUMNS record */ From 3ba73a2173fb59e3bcc2b1e231d68e53545b3a2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 22 Jun 2010 13:43:17 +0300 Subject: [PATCH 006/115] buf_flush_insert_in_flush_rbt(), buf_flush_validate_low(): Silence GCC warnings about dereferencing a type-punned pointer. Approved by Jimmy Yang. --- storage/innobase/buf/buf0flu.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/storage/innobase/buf/buf0flu.c b/storage/innobase/buf/buf0flu.c index 046f1ed51e8..6c2a67e8a2d 100644 --- a/storage/innobase/buf/buf0flu.c +++ b/storage/innobase/buf/buf0flu.c @@ -114,7 +114,9 @@ buf_flush_insert_in_flush_rbt( p_node = rbt_prev(buf_pool->flush_rbt, c_node); if (p_node != NULL) { - prev = *rbt_value(buf_page_t*, p_node); + buf_page_t** value; + value = rbt_value(buf_page_t*, p_node); + prev = *value; ut_a(prev != NULL); } @@ -2088,13 +2090,13 @@ buf_flush_validate_low( ut_a(om > 0); if (UNIV_LIKELY_NULL(buf_pool->flush_rbt)) { - buf_page_t* rpage; + buf_page_t** prpage; ut_a(rnode); - rpage = *rbt_value(buf_page_t*, rnode); + prpage = rbt_value(buf_page_t*, rnode); - ut_a(rpage); - ut_a(rpage == bpage); + ut_a(*prpage); + ut_a(*prpage == bpage); rnode = rbt_next(buf_pool->flush_rbt, rnode); } From f776568e32c6180fe9f895b68029eb6ab2e33c10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 22 Jun 2010 15:05:33 +0300 Subject: [PATCH 007/115] =?UTF-8?q?Merge=20Bug#54686=20fix=20from=20mysql-?= =?UTF-8?q?5.1-innodb:=20-------------------------------------------------?= =?UTF-8?q?-----------=20revno:=203517=20revision-id:=20marko.makela@oracl?= =?UTF-8?q?e.com-20100622115215-kxtzx7xuugcxd375=20parent:=20marko.makela@?= =?UTF-8?q?oracle.com-20100621095148-8g73k8k68dpj080u=20committer:=20Marko?= =?UTF-8?q?=20M=C3=A4kel=C3=A4=20=20branch=20nick?= =?UTF-8?q?:=205.1-innodb=20timestamp:=20Tue=202010-06-22=2014:52:15=20+03?= =?UTF-8?q?00=20message:=20=20=20Bug#54686=20"field->col->mtype=20=3D=3D?= =?UTF-8?q?=20type"=20assertion=20error=20at=20row/row0sel.c=20=20=20ha=5F?= =?UTF-8?q?innobase::index=5Fread(),=20ha=5Finnobase::records=5Fin=5Frange?= =?UTF-8?q?():=20Check=20that=20=20=20the=20index=20is=20useable=20before?= =?UTF-8?q?=20invoking=20row=5Fsel=5Fconvert=5Fmysql=5Fkey=5Fto=5Finnobase?= =?UTF-8?q?().?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fix is based on a suggestion by Yasufumi Kinoshita. --- storage/innobase/handler/ha_innodb.cc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 675edc61ac7..bc8ab206711 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -5568,6 +5568,9 @@ ha_innobase::index_read( prebuilt->index_usable = FALSE; DBUG_RETURN(HA_ERR_CRASHED); } + if (UNIV_UNLIKELY(!prebuilt->index_usable)) { + DBUG_RETURN(HA_ERR_TABLE_DEF_CHANGED); + } /* Note that if the index for which the search template is built is not necessarily prebuilt->index, but can also be the clustered index */ @@ -7410,6 +7413,10 @@ ha_innobase::records_in_range( n_rows = HA_POS_ERROR; goto func_exit; } + if (UNIV_UNLIKELY(!row_merge_is_index_usable(prebuilt->trx, index))) { + n_rows = HA_ERR_TABLE_DEF_CHANGED; + goto func_exit; + } heap = mem_heap_create(2 * (key->key_parts * sizeof(dfield_t) + sizeof(dtuple_t))); From f5f9c922c864556044c606e804bec3780236b885 Mon Sep 17 00:00:00 2001 From: "karen.langford@oracle.com" <> Date: Tue, 22 Jun 2010 19:21:25 +0200 Subject: [PATCH 008/115] Set version number for mysql-5.1.46sp1 release --- configure.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.in b/configure.in index 904c54abb5f..9098ed1f3cb 100644 --- a/configure.in +++ b/configure.in @@ -12,7 +12,7 @@ dnl dnl When changing the major version number please also check the switch dnl statement in mysqlbinlog::check_master_version(). You may also need dnl to update version.c in ndb. -AC_INIT([MySQL Server], [5.1.46], [], [mysql]) +AC_INIT([MySQL Server], [5.1.46sp1], [], [mysql]) AC_CONFIG_SRCDIR([sql/mysqld.cc]) AC_CANONICAL_SYSTEM From d4858b96d3ffdd162f0273f106e99f76a334ed2f Mon Sep 17 00:00:00 2001 From: MySQL Build Team Date: Tue, 22 Jun 2010 21:42:14 +0200 Subject: [PATCH 009/115] Backport into build-201006221614-5.1.46sp1 > ------------------------------------------------------------ > revno: 3362 > revision-id: davi.arnaut@sun.com-20100401131522-895y8uzvv8ag44gs > parent: ramil@mysql.com-20100429045409-r7r5lcyiruis15v7 > committer: Davi Arnaut > branch nick: 50755-5.1 > timestamp: Thu 2010-04-01 10:15:22 -0300 > message: > Bug#50755: Crash if stored routine def contains version comments > > The problem was that a syntactically invalid trigger could cause > the server to crash when trying to list triggers. The crash would > happen due to a mishap in the backup/restore procedure that should > protect parser items which are not associated with the trigger. The > backup/restore is used to isolate the parse tree (and context) of > a statement from the load (and parsing) of a trigger. In this case, > a error during the parsing of a trigger could cause the improper > backup/restore sequence. > > The solution is to properly restore the original statement context > before the parser is exited due to syntax errors in the trigger body. --- mysql-test/r/trigger.result | 23 +++++++++++++++++++++++ mysql-test/t/trigger.test | 31 +++++++++++++++++++++++++++++++ sql/sp_head.cc | 36 ++++++++++++++---------------------- sql/sp_head.h | 4 ---- sql/sql_lex.cc | 1 + 5 files changed, 69 insertions(+), 26 deletions(-) diff --git a/mysql-test/r/trigger.result b/mysql-test/r/trigger.result index 3446babbb52..e3c0b0e1dd9 100644 --- a/mysql-test/r/trigger.result +++ b/mysql-test/r/trigger.result @@ -2128,4 +2128,27 @@ Warning 1048 Column 'id' cannot be null Warning 1048 Column 'id' cannot be null DROP TRIGGER t1_bu; DROP TABLE t1,t2; +# +# Bug#50755: Crash if stored routine def contains version comments +# +DROP DATABASE IF EXISTS db1; +DROP TRIGGER IF EXISTS trg1; +DROP TABLE IF EXISTS t1, t2; +CREATE DATABASE db1; +USE db1; +CREATE TABLE t1 (b INT); +CREATE TABLE t2 (a INT); +CREATE TRIGGER trg1 BEFORE INSERT ON t2 FOR EACH ROW INSERT/*!INTO*/t1 VALUES (1); +# Used to crash +SHOW TRIGGERS IN db1; +Trigger Event Table Statement Timing Created sql_mode Definer character_set_client collation_connection Database Collation +Warnings: +Warning 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'VALUES (1)' at line 1 +INSERT INTO t2 VALUES (1); +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'VALUES (1)' at line 1 +SELECT * FROM t1; +b +# Work around Bug#45235 +DROP DATABASE db1; +USE test; End of 5.1 tests. diff --git a/mysql-test/t/trigger.test b/mysql-test/t/trigger.test index 368271f1fb2..bcbca4d2139 100644 --- a/mysql-test/t/trigger.test +++ b/mysql-test/t/trigger.test @@ -2439,4 +2439,35 @@ UPDATE t1 SET id=NULL; DROP TRIGGER t1_bu; DROP TABLE t1,t2; +--echo # +--echo # Bug#50755: Crash if stored routine def contains version comments +--echo # + +--disable_warnings +DROP DATABASE IF EXISTS db1; +DROP TRIGGER IF EXISTS trg1; +DROP TABLE IF EXISTS t1, t2; +--enable_warnings + +CREATE DATABASE db1; +USE db1; + +CREATE TABLE t1 (b INT); +CREATE TABLE t2 (a INT); + +CREATE TRIGGER trg1 BEFORE INSERT ON t2 FOR EACH ROW INSERT/*!INTO*/t1 VALUES (1); +--echo # Used to crash +SHOW TRIGGERS IN db1; +--error ER_PARSE_ERROR +INSERT INTO t2 VALUES (1); +SELECT * FROM t1; + +--echo # Work around Bug#45235 +let $MYSQLD_DATADIR = `select @@datadir`; +--remove_file $MYSQLD_DATADIR/db1/t2.TRG +--remove_file $MYSQLD_DATADIR/db1/trg1.TRN + +DROP DATABASE db1; +USE test; + --echo End of 5.1 tests. diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 11d5e5f830b..cadda38053c 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -745,21 +745,12 @@ sp_head::create(THD *thd) sp_head::~sp_head() { - DBUG_ENTER("sp_head::~sp_head"); - destroy(); - delete m_next_cached_sp; - if (m_thd) - restore_thd_mem_root(m_thd); - DBUG_VOID_RETURN; -} - -void -sp_head::destroy() -{ - sp_instr *i; LEX *lex; - DBUG_ENTER("sp_head::destroy"); - DBUG_PRINT("info", ("name: %s", m_name.str)); + sp_instr *i; + DBUG_ENTER("sp_head::~sp_head"); + + /* sp_head::restore_thd_mem_root() must already have been called. */ + DBUG_ASSERT(m_thd == NULL); for (uint ip = 0 ; (i = get_instr(ip)) ; ip++) delete i; @@ -770,21 +761,22 @@ sp_head::destroy() /* If we have non-empty LEX stack then we just came out of parser with error. Now we should delete all auxilary LEXes and restore original - THD::lex (In this case sp_head::restore_thd_mem_root() was not called - too, so m_thd points to the current thread context). - It is safe to not update LEX::ptr because further query string parsing - and execution will be stopped anyway. + THD::lex. It is safe to not update LEX::ptr because further query + string parsing and execution will be stopped anyway. */ - DBUG_ASSERT(m_lex.is_empty() || m_thd); while ((lex= (LEX *)m_lex.pop())) { - lex_end(m_thd->lex); - delete m_thd->lex; - m_thd->lex= lex; + THD *thd= lex->thd; + lex_end(thd->lex); + delete thd->lex; + thd->lex= lex; } hash_free(&m_sptabs); hash_free(&m_sroutines); + + delete m_next_cached_sp; + DBUG_VOID_RETURN; } diff --git a/sql/sp_head.h b/sql/sp_head.h index 00c96d44f70..d422adc8927 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -289,10 +289,6 @@ public: virtual ~sp_head(); - /// Free memory - void - destroy(); - bool execute_trigger(THD *thd, const LEX_STRING *db_name, diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 5097ca2ad5b..a3776f59241 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -2106,6 +2106,7 @@ void st_lex::cleanup_lex_after_parse_error(THD *thd) */ if (thd->lex->sphead) { + thd->lex->sphead->restore_thd_mem_root(thd); delete thd->lex->sphead; thd->lex->sphead= NULL; } From 98cfc9e61e9d061ff6803c3e371febde282a97a1 Mon Sep 17 00:00:00 2001 From: MySQL Build Team Date: Tue, 22 Jun 2010 21:50:47 +0200 Subject: [PATCH 010/115] Backport into build-201006221614-5.1.46sp1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit > ------------------------------------------------------------ > revno: 3351.14.50 > revision-id: marko.makela@oracle.com-20100421185359-8qaxoa2yyrpzwdd7 > parent: marko.makela@oracle.com-20100421102723-0i80uezbyu0ekj5d > committer: Marko Mäkelä > branch nick: 5.1-innodb > timestamp: Wed 2010-04-21 21:53:59 +0300 > message: > btr_page_split_and_insert(): Avoid an infinite loop. (Bug #52964) > > btr_page_tuple_smaller(): New function, refactored from > btr_page_split_and_insert(). > > btr_page_get_split_rec(): Renamed from btr_page_get_sure_split_rec(). > Note that a NULL return may mean that the tuple is to be inserted into > either the lower or upper page, to be determined by btr_page_tuple_smaller(). > > btr_page_split_and_insert(): When btr_page_get_split_rec() returns NULL, > invoke btr_page_tuple_smaller() to determine which half-page the tuple > belongs to. > > Reviewed by Sunny Bains --- storage/innodb_plugin/btr/btr0btr.c | 91 +++++++++++++++++++---------- 1 file changed, 59 insertions(+), 32 deletions(-) diff --git a/storage/innodb_plugin/btr/btr0btr.c b/storage/innodb_plugin/btr/btr0btr.c index fa4fc05d96b..cd8f42b3818 100644 --- a/storage/innodb_plugin/btr/btr0btr.c +++ b/storage/innodb_plugin/btr/btr0btr.c @@ -1451,11 +1451,11 @@ Calculates a split record such that the tuple will certainly fit on its half-page when the split is performed. We assume in this function only that the cursor page has at least one user record. @return split record, or NULL if tuple will be the first record on -upper half-page */ +the lower or upper half-page (determined by btr_page_tuple_smaller()) */ static rec_t* -btr_page_get_sure_split_rec( -/*========================*/ +btr_page_get_split_rec( +/*===================*/ btr_cur_t* cursor, /*!< in: cursor at which insert should be made */ const dtuple_t* tuple, /*!< in: tuple to insert */ ulint n_ext) /*!< in: number of externally stored columns */ @@ -1831,6 +1831,37 @@ btr_attach_half_pages( btr_page_set_next(upper_page, upper_page_zip, next_page_no, mtr); } +/*************************************************************//** +Determine if a tuple is smaller than any record on the page. +@return TRUE if smaller */ +static +ibool +btr_page_tuple_smaller( +/*===================*/ + btr_cur_t* cursor, /*!< in: b-tree cursor */ + const dtuple_t* tuple, /*!< in: tuple to consider */ + ulint* offsets,/*!< in/out: temporary storage */ + ulint n_uniq, /*!< in: number of unique fields + in the index page records */ + mem_heap_t** heap) /*!< in/out: heap for offsets */ +{ + buf_block_t* block; + const rec_t* first_rec; + page_cur_t pcur; + + /* Read the first user record in the page. */ + block = btr_cur_get_block(cursor); + page_cur_set_before_first(block, &pcur); + page_cur_move_to_next(&pcur); + first_rec = page_cur_get_rec(&pcur); + + offsets = rec_get_offsets( + first_rec, cursor->index, offsets, + n_uniq, heap); + + return(cmp_dtuple_rec(tuple, first_rec, offsets) < 0); +} + /*************************************************************//** Splits an index page to halves and inserts the tuple. It is assumed that mtr holds an x-latch to the index tree. NOTE: the tree x-latch is @@ -1905,49 +1936,45 @@ func_start: if (n_iterations > 0) { direction = FSP_UP; hint_page_no = page_no + 1; - split_rec = btr_page_get_sure_split_rec(cursor, tuple, n_ext); + split_rec = btr_page_get_split_rec(cursor, tuple, n_ext); + if (UNIV_UNLIKELY(split_rec == NULL)) { + insert_left = btr_page_tuple_smaller( + cursor, tuple, offsets, n_uniq, &heap); + } } else if (btr_page_get_split_rec_to_right(cursor, &split_rec)) { direction = FSP_UP; hint_page_no = page_no + 1; + insert_left = FALSE; } else if (btr_page_get_split_rec_to_left(cursor, &split_rec)) { direction = FSP_DOWN; hint_page_no = page_no - 1; + ut_ad(split_rec); } else { direction = FSP_UP; hint_page_no = page_no + 1; - if (page_get_n_recs(page) == 1) { - page_cur_t pcur; + /* If there is only one record in the index page, we + can't split the node in the middle by default. We need + to determine whether the new record will be inserted + to the left or right. */ - /* There is only one record in the index page - therefore we can't split the node in the middle - by default. We need to determine whether the - new record will be inserted to the left or right. */ - - /* Read the first (and only) record in the page. */ - page_cur_set_before_first(block, &pcur); - page_cur_move_to_next(&pcur); - first_rec = page_cur_get_rec(&pcur); - - offsets = rec_get_offsets( - first_rec, cursor->index, offsets, - n_uniq, &heap); - - /* If the new record is less than the existing record - the split in the middle will copy the existing - record to the new node. */ - if (cmp_dtuple_rec(tuple, first_rec, offsets) < 0) { - split_rec = page_get_middle_rec(page); - } else { - split_rec = NULL; - } - } else { + if (page_get_n_recs(page) > 1) { split_rec = page_get_middle_rec(page); + } else if (btr_page_tuple_smaller(cursor, tuple, + offsets, n_uniq, &heap)) { + split_rec = page_rec_get_next( + page_get_infimum_rec(page)); + } else { + split_rec = NULL; + insert_left = FALSE; } } + /* At this point, insert_left is initialized if split_rec == NULL + and may be uninitialized otherwise. */ + /* 2. Allocate a new page to the index */ new_block = btr_page_alloc(cursor->index, hint_page_no, direction, btr_page_get_level(page, mtr), mtr); @@ -1974,11 +2001,11 @@ func_start: avoid further splits by inserting the record to an empty page. */ split_rec = NULL; - goto insert_right; + goto insert_empty; } } else { -insert_right: - insert_left = FALSE; +insert_empty: + ut_ad(!split_rec); buf = mem_alloc(rec_get_converted_size(cursor->index, tuple, n_ext)); From 01ca7cff6fd255a630f26f9112774d324aa8a4f7 Mon Sep 17 00:00:00 2001 From: MySQL Build Team Date: Tue, 22 Jun 2010 21:52:43 +0200 Subject: [PATCH 011/115] Backport into build-201006221614-5.1.46sp1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit > ------------------------------------------------------------ > revno: 3351.14.74 > revision-id: marko.makela@oracle.com-20100504093128-44v6glupe1dsh0ug > parent: marko.makela@oracle.com-20100503122859-k73bl51re93o0mt4 > committer: Marko Mäkelä > branch nick: 5.1-innodb > timestamp: Tue 2010-05-04 12:31:28 +0300 > message: > btr_page_split_and_insert(): Correct the fix of Bug #52964. > When split_rec==NULL, choose the correct node pointer key (first_rec). --- storage/innodb_plugin/btr/btr0btr.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/storage/innodb_plugin/btr/btr0btr.c b/storage/innodb_plugin/btr/btr0btr.c index cd8f42b3818..1d6b4f5a0e6 100644 --- a/storage/innodb_plugin/btr/btr0btr.c +++ b/storage/innodb_plugin/btr/btr0btr.c @@ -2003,9 +2003,13 @@ func_start: split_rec = NULL; goto insert_empty; } + } else if (UNIV_UNLIKELY(insert_left)) { + first_rec = page_rec_get_next(page_get_infimum_rec(page)); + move_limit = page_rec_get_next(btr_cur_get_rec(cursor)); } else { insert_empty: ut_ad(!split_rec); + ut_ad(!insert_left); buf = mem_alloc(rec_get_converted_size(cursor->index, tuple, n_ext)); @@ -2029,7 +2033,11 @@ insert_empty: && btr_page_insert_fits(cursor, split_rec, offsets, tuple, n_ext, heap); } else { - mem_free(buf); + if (!insert_left) { + mem_free(buf); + buf = NULL; + } + insert_will_fit = !new_page_zip && btr_page_insert_fits(cursor, NULL, NULL, tuple, n_ext, heap); From 5121205d5753efb69a5dfeb45a1f3c47fd2ada04 Mon Sep 17 00:00:00 2001 From: MySQL Build Team Date: Tue, 22 Jun 2010 21:54:41 +0200 Subject: [PATCH 012/115] Backport into build-201006221614-5.1.46sp1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit > ------------------------------------------------------------ > revno: 3351.47.1 > revision-id: marko.makela@oracle.com-20100511104500-c6kzd0bg5s42p8e9 > parent: vasil.dimov@oracle.com-20100510132852-cz457uqvj8iiy9mm > committer: Marko Mäkelä > branch nick: mysql-5.1-innodb2 > timestamp: Tue 2010-05-11 13:45:00 +0300 > message: > Remove a stray expression. Spotted by Sunny Bains. --- storage/innodb_plugin/page/page0zip.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/innodb_plugin/page/page0zip.c b/storage/innodb_plugin/page/page0zip.c index aa5e39ff04a..a64a41ea518 100644 --- a/storage/innodb_plugin/page/page0zip.c +++ b/storage/innodb_plugin/page/page0zip.c @@ -571,7 +571,7 @@ page_zip_dir_encode( /* Traverse the list of stored records in the collation order, starting from the first user record. */ - rec = page + PAGE_NEW_INFIMUM, TRUE; + rec = page + PAGE_NEW_INFIMUM; i = 0; From 0e40579c13aefe908cd3ac249ae17c1d7c13b4b2 Mon Sep 17 00:00:00 2001 From: MySQL Build Team Date: Tue, 22 Jun 2010 21:56:18 +0200 Subject: [PATCH 013/115] Backport into build-201006221614-5.1.46sp1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit > ------------------------------------------------------------ > revno: 3351.47.2 > revision-id: marko.makela@oracle.com-20100511104910-nim8kgguawpis7zo > parent: marko.makela@oracle.com-20100511104500-c6kzd0bg5s42p8e9 > committer: Marko Mäkelä > branch nick: mysql-5.1-innodb2 > timestamp: Tue 2010-05-11 13:49:10 +0300 > message: > btr_page_split_and_insert(): Add an assertion > suggested by Sunny Bains when reviewing Bug #52964. --- storage/innodb_plugin/btr/btr0btr.c | 1 + 1 file changed, 1 insertion(+) diff --git a/storage/innodb_plugin/btr/btr0btr.c b/storage/innodb_plugin/btr/btr0btr.c index 1d6b4f5a0e6..0fd209e0216 100644 --- a/storage/innodb_plugin/btr/btr0btr.c +++ b/storage/innodb_plugin/btr/btr0btr.c @@ -2004,6 +2004,7 @@ func_start: goto insert_empty; } } else if (UNIV_UNLIKELY(insert_left)) { + ut_a(n_iterations > 0); first_rec = page_rec_get_next(page_get_infimum_rec(page)); move_limit = page_rec_get_next(btr_cur_get_rec(cursor)); } else { From 8763eab5097231b32a387be03383ae5685d91162 Mon Sep 17 00:00:00 2001 From: MySQL Build Team Date: Tue, 22 Jun 2010 21:59:35 +0200 Subject: [PATCH 014/115] Backport into build-201006221614-5.1.46sp1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit > ------------------------------------------------------------ > revno: 3351.14.47 > revision-id: marko.makela@oracle.com-20100421095033-0acvzxb8um8cms0a > parent: marko.makela@oracle.com-20100421094032-ir4glqk46qvg2ywn > committer: Marko Mäkelä > branch nick: 5.1-innodb > timestamp: Wed 2010-04-21 12:50:33 +0300 > message: > dtuple_convert_big_rec(): Store locally any fields whose maximum length > is less than 256 bytes. (Bug #52745) > Add related comments and debug assertions to the "offsets" > functions in rem0rec.c. > Approved by Sunny Bains --- storage/innodb_plugin/data/data0data.c | 15 +++++++ storage/innodb_plugin/rem/rem0rec.c | 59 +++++++++++++++++++++++++- 2 files changed, 72 insertions(+), 2 deletions(-) diff --git a/storage/innodb_plugin/data/data0data.c b/storage/innodb_plugin/data/data0data.c index e3c1f1b4f23..0715b49bf9c 100644 --- a/storage/innodb_plugin/data/data0data.c +++ b/storage/innodb_plugin/data/data0data.c @@ -666,6 +666,21 @@ dtuple_convert_big_rec( goto skip_field; } + /* In DYNAMIC and COMPRESSED format, store + locally any non-BLOB columns whose maximum + length does not exceed 256 bytes. This is + because there is no room for the "external + storage" flag when the maximum length is 255 + bytes or less. This restriction trivially + holds in REDUNDANT and COMPACT format, because + there we always store locally columns whose + length is up to local_len == 788 bytes. + @see rec_init_offsets_comp_ordinary */ + if (ifield->col->mtype != DATA_BLOB + && ifield->col->len < 256) { + goto skip_field; + } + longest_i = i; longest = savings; diff --git a/storage/innodb_plugin/rem/rem0rec.c b/storage/innodb_plugin/rem/rem0rec.c index 27c11dacc8c..59cf6e887d1 100644 --- a/storage/innodb_plugin/rem/rem0rec.c +++ b/storage/innodb_plugin/rem/rem0rec.c @@ -212,6 +212,13 @@ rec_get_n_extern_new( const dict_col_t* col = dict_field_get_col(field); len = *lens--; + /* If the maximum length of the field is up + to 255 bytes, the actual length is always + stored in one byte. If the maximum length is + more than 255 bytes, the actual length is + stored in one byte for 0..127. The length + will be encoded in two bytes when it is 128 or + more, or when the field is stored externally. */ if (UNIV_UNLIKELY(col->len > 255) || UNIV_UNLIKELY(col->mtype == DATA_BLOB)) { if (len & 0x80) { @@ -294,6 +301,13 @@ rec_init_offsets_comp_ordinary( const dict_col_t* col = dict_field_get_col(field); len = *lens--; + /* If the maximum length of the field is up + to 255 bytes, the actual length is always + stored in one byte. If the maximum length is + more than 255 bytes, the actual length is + stored in one byte for 0..127. The length + will be encoded in two bytes when it is 128 or + more, or when the field is stored externally. */ if (UNIV_UNLIKELY(col->len > 255) || UNIV_UNLIKELY(col->mtype == DATA_BLOB)) { @@ -425,6 +439,15 @@ rec_init_offsets( const dict_col_t* col = dict_field_get_col(field); len = *lens--; + /* If the maximum length of the field + is up to 255 bytes, the actual length + is always stored in one byte. If the + maximum length is more than 255 bytes, + the actual length is stored in one + byte for 0..127. The length will be + encoded in two bytes when it is 128 or + more, or when the field is stored + externally. */ if (UNIV_UNLIKELY(col->len > 255) || UNIV_UNLIKELY(col->mtype == DATA_BLOB)) { @@ -647,6 +670,13 @@ rec_get_offsets_reverse( const dict_col_t* col = dict_field_get_col(field); len = *lens++; + /* If the maximum length of the field is up + to 255 bytes, the actual length is always + stored in one byte. If the maximum length is + more than 255 bytes, the actual length is + stored in one byte for 0..127. The length + will be encoded in two bytes when it is 128 or + more, or when the field is stored externally. */ if (UNIV_UNLIKELY(col->len > 255) || UNIV_UNLIKELY(col->mtype == DATA_BLOB)) { if (len & 0x80) { @@ -781,12 +811,20 @@ rec_get_converted_size_comp_prefix( ut_ad(len <= col->len || col->mtype == DATA_BLOB); + /* If the maximum length of a variable-length field + is up to 255 bytes, the actual length is always stored + in one byte. If the maximum length is more than 255 + bytes, the actual length is stored in one byte for + 0..127. The length will be encoded in two bytes when + it is 128 or more, or when the field is stored externally. */ + if (field->fixed_len) { ut_ad(len == field->fixed_len); /* dict_index_add_col() should guarantee this */ ut_ad(!field->prefix_len || field->fixed_len == field->prefix_len); } else if (dfield_is_ext(&fields[i])) { + ut_ad(col->len >= 256 || col->mtype == DATA_BLOB); extra_size += 2; } else if (len < 128 || (col->len < 256 && col->mtype != DATA_BLOB)) { @@ -1086,6 +1124,8 @@ rec_convert_dtuple_to_rec_comp( /* Store the data and the offsets */ for (i = 0, field = fields; i < n_fields; i++, field++) { + const dict_field_t* ifield; + type = dfield_get_type(field); len = dfield_get_len(field); @@ -1120,12 +1160,20 @@ rec_convert_dtuple_to_rec_comp( /* only nullable fields can be null */ ut_ad(!dfield_is_null(field)); - fixed_len = dict_index_get_nth_field(index, i)->fixed_len; - + ifield = dict_index_get_nth_field(index, i); + fixed_len = ifield->fixed_len; + /* If the maximum length of a variable-length field + is up to 255 bytes, the actual length is always stored + in one byte. If the maximum length is more than 255 + bytes, the actual length is stored in one byte for + 0..127. The length will be encoded in two bytes when + it is 128 or more, or when the field is stored externally. */ if (fixed_len) { ut_ad(len == fixed_len); ut_ad(!dfield_is_ext(field)); } else if (dfield_is_ext(field)) { + ut_ad(ifield->col->len >= 256 + || ifield->col->mtype == DATA_BLOB); ut_ad(len <= REC_MAX_INDEX_COL_LEN + BTR_EXTERN_FIELD_REF_SIZE); *lens-- = (byte) (len >> 8) | 0xc0; @@ -1402,6 +1450,13 @@ rec_copy_prefix_to_buf( prefix_len += field->fixed_len; } else { ulint len = *lens--; + /* If the maximum length of the column is up + to 255 bytes, the actual length is always + stored in one byte. If the maximum length is + more than 255 bytes, the actual length is + stored in one byte for 0..127. The length + will be encoded in two bytes when it is 128 or + more, or when the column is stored externally. */ if (col->len > 255 || col->mtype == DATA_BLOB) { if (len & 0x80) { /* 1exxxxxx */ From 25d938b691c0d6b8261985b2ac4d9e5e2aad4fa9 Mon Sep 17 00:00:00 2001 From: MySQL Build Team Date: Tue, 22 Jun 2010 22:09:31 +0200 Subject: [PATCH 015/115] Backport into 5.1.46sp1: > revno: 3351.14.56 > committer: Marko Mdkeld > branch nick: 5.1-innodb > timestamp: Mon 2010-04-26 14:08:56 +0300 > message: > Add a test case for Bug #52745. --- .../innodb_plugin/r/innodb_bug52745.result | 130 ++++++++++++++++++ .../innodb_plugin/t/innodb_bug52745.test | 109 +++++++++++++++ 2 files changed, 239 insertions(+) create mode 100644 mysql-test/suite/innodb_plugin/r/innodb_bug52745.result create mode 100644 mysql-test/suite/innodb_plugin/t/innodb_bug52745.test diff --git a/mysql-test/suite/innodb_plugin/r/innodb_bug52745.result b/mysql-test/suite/innodb_plugin/r/innodb_bug52745.result new file mode 100644 index 00000000000..254c6525257 --- /dev/null +++ b/mysql-test/suite/innodb_plugin/r/innodb_bug52745.result @@ -0,0 +1,130 @@ +SET GLOBAL innodb_file_format='Barracuda'; +SET GLOBAL innodb_file_per_table=on; +CREATE TABLE bug52745 ( +a2 int(10) unsigned DEFAULT NULL, +col37 time DEFAULT NULL, +col38 char(229) CHARACTER SET utf8 DEFAULT NULL, +col39 text, +col40 timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, +col41 int(10) unsigned DEFAULT NULL, +col42 varchar(248) CHARACTER SET utf8 DEFAULT NULL, +col43 smallint(5) unsigned zerofill DEFAULT NULL, +col44 varchar(150) CHARACTER SET utf8 DEFAULT NULL, +col45 float unsigned zerofill DEFAULT NULL, +col46 binary(1) DEFAULT NULL, +col47 tinyint(4) DEFAULT NULL, +col48 tinyint(1) DEFAULT NULL, +col49 timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', +col50 binary(1) DEFAULT NULL, +col51 double unsigned zerofill DEFAULT NULL, +col52 int(10) unsigned DEFAULT NULL, +col53 time DEFAULT NULL, +col54 double unsigned DEFAULT NULL, +col55 time DEFAULT NULL, +col56 mediumtext CHARACTER SET latin2, +col57 blob, +col58 decimal(52,16) unsigned zerofill NOT NULL DEFAULT '000000000000000000000000000000000000.0000000000000000', +col59 binary(1) DEFAULT NULL, +col60 longblob, +col61 time DEFAULT NULL, +col62 longtext CHARACTER SET utf8 COLLATE utf8_persian_ci, +col63 timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', +col64 int(10) unsigned DEFAULT NULL, +col65 date DEFAULT NULL, +col66 timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', +col67 binary(1) DEFAULT NULL, +col68 tinyblob, +col69 date DEFAULT NULL, +col70 tinyint(3) unsigned zerofill DEFAULT NULL, +col71 varchar(44) CHARACTER SET utf8 DEFAULT NULL, +col72 datetime DEFAULT NULL, +col73 smallint(5) unsigned zerofill DEFAULT NULL, +col74 longblob, +col75 bit(34) DEFAULT NULL, +col76 float unsigned zerofill DEFAULT NULL, +col77 year(2) DEFAULT NULL, +col78 tinyint(3) unsigned DEFAULT NULL, +col79 set('msfheowh','tbpxbgf','by','wahnrjw','myqfasxz','rsokyumrt') CHARACTER SET latin2 DEFAULT NULL, +col80 datetime DEFAULT NULL, +col81 smallint(6) DEFAULT NULL, +col82 enum('xtaurnqfqz','rifrse','kuzwpbvb','niisabk','zxavro','rbvasv','','uulrfaove','','') DEFAULT NULL, +col83 bigint(20) unsigned zerofill DEFAULT NULL, +col84 float unsigned zerofill DEFAULT NULL, +col85 double DEFAULT NULL, +col86 enum('ylannv','','vlkhycqc','snke','cxifustp','xiaxaswzp','oxl') CHARACTER SET latin1 COLLATE latin1_german2_ci DEFAULT NULL, +col87 varbinary(221) DEFAULT NULL, +col88 double unsigned DEFAULT NULL, +col89 float unsigned zerofill DEFAULT NULL, +col90 tinyblob +) ENGINE=InnoDB DEFAULT CHARSET=latin1 ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=1; +Warnings: +Note 1291 Column 'col82' has duplicated value '' in ENUM +Note 1291 Column 'col82' has duplicated value '' in ENUM +INSERT INTO bug52745 SET +col40='0000-00-00 00:00:00', +col51=16547, +col53='7711484', +col54=-28604, +col55='7112612', +col56='wakefulness\'', +col57=repeat('absorbefacient\'',106), +col58=11027, +col59='AM09gW7', +col60=repeat('Noelani\'',16), +col61='2520576', +col62='substitutiv', +col63='19950106155112', +col64=-12038, +col65='86238806', +col66='19600719080256', +col68=repeat('Sagittarius\'',54), +col69='38943902', +col70=1232, +col71='Elora\'', +col74=repeat('zipp',11), +col75='0', +col76=23254, +col78=13247, +col79='56219', +col80='20500609035724', +col81=11632, +col82=7, +col84=-23863, +col85=6341, +col87='HZdkf.4 s7t,5Rmq 8so fmr,ruGLUG25TrtI.yQ 2SuHq0ML7rw7.4 b2yf2E5TJxOtBBZImezDnzpj,uPYfznnEUDN1e9aQoO 2DsplB7TFWy oQJ br HLF :F,eQ p4i1oWsr lL3PG,hjCz6hYqN h1QTjLCjrv:QCdSzpYBibJAtZCxLOk3l6Blsh.W', +col88=16894, +col89=6161, +col90=repeat('gale',48); +Warnings: +Warning 1265 Data truncated for column 'col53' at row 1 +Warning 1264 Out of range value for column 'col54' at row 1 +Warning 1265 Data truncated for column 'col59' at row 1 +Warning 1265 Data truncated for column 'col61' at row 1 +Warning 1264 Out of range value for column 'col64' at row 1 +Warning 1265 Data truncated for column 'col65' at row 1 +Warning 1264 Out of range value for column 'col66' at row 1 +Warning 1265 Data truncated for column 'col68' at row 1 +Warning 1265 Data truncated for column 'col69' at row 1 +Warning 1264 Out of range value for column 'col70' at row 1 +Warning 1264 Out of range value for column 'col78' at row 1 +Warning 1265 Data truncated for column 'col79' at row 1 +Warning 1264 Out of range value for column 'col84' at row 1 +SHOW WARNINGS; +Level Code Message +Warning 1265 Data truncated for column 'col53' at row 1 +Warning 1264 Out of range value for column 'col54' at row 1 +Warning 1265 Data truncated for column 'col59' at row 1 +Warning 1265 Data truncated for column 'col61' at row 1 +Warning 1264 Out of range value for column 'col64' at row 1 +Warning 1265 Data truncated for column 'col65' at row 1 +Warning 1264 Out of range value for column 'col66' at row 1 +Warning 1265 Data truncated for column 'col68' at row 1 +Warning 1265 Data truncated for column 'col69' at row 1 +Warning 1264 Out of range value for column 'col70' at row 1 +Warning 1264 Out of range value for column 'col78' at row 1 +Warning 1265 Data truncated for column 'col79' at row 1 +Warning 1264 Out of range value for column 'col84' at row 1 +DROP TABLE bug52745; +SET GLOBAL innodb_file_format=Antelope; +SET GLOBAL innodb_file_format_check=Antelope; +SET GLOBAL innodb_file_per_table=0; diff --git a/mysql-test/suite/innodb_plugin/t/innodb_bug52745.test b/mysql-test/suite/innodb_plugin/t/innodb_bug52745.test new file mode 100644 index 00000000000..b20a993a2d1 --- /dev/null +++ b/mysql-test/suite/innodb_plugin/t/innodb_bug52745.test @@ -0,0 +1,109 @@ +-- source include/have_innodb_plugin.inc + +let $file_format=`select @@innodb_file_format`; +let $file_format_check=`select @@innodb_file_format_check`; +let $file_per_table=`select @@innodb_file_per_table`; +SET GLOBAL innodb_file_format='Barracuda'; +SET GLOBAL innodb_file_per_table=on; + +CREATE TABLE bug52745 ( + a2 int(10) unsigned DEFAULT NULL, + col37 time DEFAULT NULL, + col38 char(229) CHARACTER SET utf8 DEFAULT NULL, + col39 text, + col40 timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + col41 int(10) unsigned DEFAULT NULL, + col42 varchar(248) CHARACTER SET utf8 DEFAULT NULL, + col43 smallint(5) unsigned zerofill DEFAULT NULL, + col44 varchar(150) CHARACTER SET utf8 DEFAULT NULL, + col45 float unsigned zerofill DEFAULT NULL, + col46 binary(1) DEFAULT NULL, + col47 tinyint(4) DEFAULT NULL, + col48 tinyint(1) DEFAULT NULL, + col49 timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', + col50 binary(1) DEFAULT NULL, + col51 double unsigned zerofill DEFAULT NULL, + col52 int(10) unsigned DEFAULT NULL, + col53 time DEFAULT NULL, + col54 double unsigned DEFAULT NULL, + col55 time DEFAULT NULL, + col56 mediumtext CHARACTER SET latin2, + col57 blob, + col58 decimal(52,16) unsigned zerofill NOT NULL DEFAULT '000000000000000000000000000000000000.0000000000000000', + col59 binary(1) DEFAULT NULL, + col60 longblob, + col61 time DEFAULT NULL, + col62 longtext CHARACTER SET utf8 COLLATE utf8_persian_ci, + col63 timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', + col64 int(10) unsigned DEFAULT NULL, + col65 date DEFAULT NULL, + col66 timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', + col67 binary(1) DEFAULT NULL, + col68 tinyblob, + col69 date DEFAULT NULL, + col70 tinyint(3) unsigned zerofill DEFAULT NULL, + col71 varchar(44) CHARACTER SET utf8 DEFAULT NULL, + col72 datetime DEFAULT NULL, + col73 smallint(5) unsigned zerofill DEFAULT NULL, + col74 longblob, + col75 bit(34) DEFAULT NULL, + col76 float unsigned zerofill DEFAULT NULL, + col77 year(2) DEFAULT NULL, + col78 tinyint(3) unsigned DEFAULT NULL, + col79 set('msfheowh','tbpxbgf','by','wahnrjw','myqfasxz','rsokyumrt') CHARACTER SET latin2 DEFAULT NULL, + col80 datetime DEFAULT NULL, + col81 smallint(6) DEFAULT NULL, + col82 enum('xtaurnqfqz','rifrse','kuzwpbvb','niisabk','zxavro','rbvasv','','uulrfaove','','') DEFAULT NULL, + col83 bigint(20) unsigned zerofill DEFAULT NULL, + col84 float unsigned zerofill DEFAULT NULL, + col85 double DEFAULT NULL, + col86 enum('ylannv','','vlkhycqc','snke','cxifustp','xiaxaswzp','oxl') CHARACTER SET latin1 COLLATE latin1_german2_ci DEFAULT NULL, + col87 varbinary(221) DEFAULT NULL, + col88 double unsigned DEFAULT NULL, + col89 float unsigned zerofill DEFAULT NULL, + col90 tinyblob +) ENGINE=InnoDB DEFAULT CHARSET=latin1 ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=1; + +INSERT INTO bug52745 SET +col40='0000-00-00 00:00:00', +col51=16547, +col53='7711484', +col54=-28604, +col55='7112612', +col56='wakefulness\'', +col57=repeat('absorbefacient\'',106), +col58=11027, +col59='AM09gW7', +col60=repeat('Noelani\'',16), +col61='2520576', +col62='substitutiv', +col63='19950106155112', +col64=-12038, +col65='86238806', +col66='19600719080256', +col68=repeat('Sagittarius\'',54), +col69='38943902', +col70=1232, +col71='Elora\'', +col74=repeat('zipp',11), +col75='0', +col76=23254, +col78=13247, +col79='56219', +col80='20500609035724', +col81=11632, +col82=7, +col84=-23863, +col85=6341, +col87='HZdkf.4 s7t,5Rmq 8so fmr,ruGLUG25TrtI.yQ 2SuHq0ML7rw7.4 b2yf2E5TJxOtBBZImezDnzpj,uPYfznnEUDN1e9aQoO 2DsplB7TFWy oQJ br HLF :F,eQ p4i1oWsr lL3PG,hjCz6hYqN h1QTjLCjrv:QCdSzpYBibJAtZCxLOk3l6Blsh.W', +col88=16894, +col89=6161, +col90=repeat('gale',48); + +SHOW WARNINGS; + +DROP TABLE bug52745; + +EVAL SET GLOBAL innodb_file_format=$file_format; +EVAL SET GLOBAL innodb_file_format_check=$file_format_check; +EVAL SET GLOBAL innodb_file_per_table=$file_per_table; From 33042e6db5dd86ebb9af6d502ed7b6c069d60a27 Mon Sep 17 00:00:00 2001 From: MySQL Build Team Date: Tue, 22 Jun 2010 22:34:48 +0200 Subject: [PATCH 016/115] Backport into build-201006221614-5.1.46sp1 > ------------------------------------------------------------ > revno: 1810.3987.13 > revision-id: ramil@mysql.com-20100429044232-f0pkyx8fnpszf142 > parent: alexey.kopytov@sun.com-20100426200600-op06qy98llzpzgl1 > committer: Ramil Kalimullin > branch nick: b53237-5.0-bugteam > timestamp: Thu 2010-04-29 08:42:32 +0400 > message: > Fix for bug #53237: mysql_list_fields/COM_FIELD_LIST stack smashing > > Problem: "COM_FIELD_LIST is an old command of the MySQL server, before there was real move to only > SQL. Seems that the data sent to COM_FIELD_LIST( mysql_list_fields() function) is not > checked for sanity. By sending long data for the table a buffer is overflown, which can > be used deliberately to include code that harms". > > Fix: check incoming data length. The patch did not apply cleanly: - Line numbers are completely off, roughly it is 2030 -> 1313 - What is called "pend" in the patch, is "arg_end" in the source. --- sql/sql_parse.cc | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 5228a37f490..11481933c8a 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1300,8 +1300,16 @@ bool dispatch_command(enum enum_server_command command, THD *thd, We have name + wildcard in packet, separated by endzero */ arg_end= strend(packet); + uint arg_length= arg_end - packet; + + /* Check given table name length. */ + if (arg_length >= packet_length || arg_length > NAME_LEN) + { + my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0)); + break; + } thd->convert_string(&conv_name, system_charset_info, - packet, (uint) (arg_end - packet), thd->charset()); + packet, arg_length, thd->charset()); table_list.alias= table_list.table_name= conv_name.str; packet= arg_end + 1; From 74a077b0b67dbf9dd449972e9a3c954de12e8399 Mon Sep 17 00:00:00 2001 From: MySQL Build Team Date: Tue, 22 Jun 2010 22:51:35 +0200 Subject: [PATCH 017/115] Backport into build-201006221614-5.1.46sp1 > ------------------------------------------------------------ > revno: 1810.3987.14 > revision-id: davi.arnaut@sun.com-20100429132816-ictyul6d75itek22 > parent: ramil@mysql.com-20100429044232-f0pkyx8fnpszf142 > committer: Davi Arnaut > branch nick: 50974-5.0 > timestamp: Thu 2010-04-29 10:28:16 -0300 > message: > Bug#50974: Server keeps receiving big (> max_allowed_packet) packets indefinitely. > > The server could be tricked to read packets indefinitely if it > received a packet larger than the maximum size of one packet. > This problem is aggravated by the fact that it can be triggered > before authentication. > > The solution is to no skip big packets for non-authenticated > sessions. If a big packet is sent before a session is authen- > ticated, a error is returned and the connection is closed. > ------------------------------------------------------------ > revno: 3363 [merge] > revision-id: davi.arnaut@sun.com-20100429231819-i3anwzrdasjmezvt > parent: davi.arnaut@sun.com-20100401131522-895y8uzvv8ag44gs > parent: davi.arnaut@sun.com-20100429132816-ictyul6d75itek22 > committer: Davi Arnaut > branch nick: mysql-5.1-bugteam > timestamp: Thu 2010-04-29 20:18:19 -0300 > message: > Manual merge. > ------------------------------------------------------------ > Use --include-merges or -n0 to see merged revisions. --- include/mysql_com.h | 10 ++++++++++ sql/net_serv.cc | 4 ++++ sql/sql_connect.cc | 7 +++++++ 3 files changed, 21 insertions(+) diff --git a/include/mysql_com.h b/include/mysql_com.h index db5a5eb8741..7d3dd3d4f34 100644 --- a/include/mysql_com.h +++ b/include/mysql_com.h @@ -277,6 +277,16 @@ typedef struct st_net { /** Client library sqlstate buffer. Set along with the error message. */ char sqlstate[SQLSTATE_LENGTH+1]; void *extension; +#if defined(MYSQL_SERVER) && !defined(EMBEDDED_LIBRARY) + /* + Controls whether a big packet should be skipped. + + Initially set to FALSE by default. Unauthenticated sessions must have + this set to FALSE so that the server can't be tricked to read packets + indefinitely. + */ + my_bool skip_big_packet; +#endif } NET; diff --git a/sql/net_serv.cc b/sql/net_serv.cc index 73892f31ccf..15c0c581108 100644 --- a/sql/net_serv.cc +++ b/sql/net_serv.cc @@ -136,6 +136,9 @@ my_bool my_net_init(NET *net, Vio* vio) #else net->query_cache_query= 0; #endif +#if defined(MYSQL_SERVER) && !defined(EMBEDDED_LIBRARY) + net->skip_big_packet= FALSE; +#endif if (vio != 0) /* If real connection */ { @@ -949,6 +952,7 @@ my_real_read(NET *net, size_t *complen) { #if defined(MYSQL_SERVER) && !defined(NO_ALARM) if (!net->compress && + net->skip_big_packet && !my_net_skip_rest(net, (uint32) len, &alarmed, &alarm_buff)) net->error= 3; /* Successfully skiped packet */ #endif diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc index 16f11fe22c4..2039c7f7449 100644 --- a/sql/sql_connect.cc +++ b/sql/sql_connect.cc @@ -471,6 +471,13 @@ check_user(THD *thd, enum enum_server_command command, } my_ok(thd); thd->password= test(passwd_len); // remember for error messages + /* + Allow the network layer to skip big packets. Although a malicious + authenticated session might use this to trick the server to read + big packets indefinitely, this is a previously established behavior + that needs to be preserved as to not break backwards compatibility. + */ + thd->net.skip_big_packet= TRUE; /* Ready to handle queries */ DBUG_RETURN(0); } From dacd3e4a77bb4015ca0e2fb55088dbb852f6bd15 Mon Sep 17 00:00:00 2001 From: MySQL Build Team Date: Tue, 22 Jun 2010 22:53:01 +0200 Subject: [PATCH 018/115] Backport into build-201006221614-5.1.46sp1 > ------------------------------------------------------------ > revno: 3351.41.1 > revision-id: alexey.kopytov@sun.com-20100430111048-jdls6ofn4kkmpt09 > parent: sergey.glukhov@sun.com-20100329134249-03wyhzp5k92dzhcb > committer: Alexey Kopytov > branch nick: my51-bug48419 > timestamp: Fri 2010-04-30 15:10:48 +0400 > message: > Bug #48419: another explain crash.. > > WHERE predicates containing references to empty tables in a > subquery were handled incorrectly by the optimizer when > executing EXPLAIN. As a result, the optimizer could try to > evaluate such predicates rather than just stop with > "Impossible WHERE noticed after reading const tables" as > it would do in a non-subquery case. This led to valgrind > errors and crashes. > > Fixed the code checking the above condition so that subqueries > are not excluded and hence are handled in the same way as top > level SELECTs. --- mysql-test/r/explain.result | 12 ++++++++++++ mysql-test/r/ps.result | 12 ++++++------ mysql-test/t/explain.test | 15 +++++++++++++++ sql/sql_select.cc | 3 +-- 4 files changed, 34 insertions(+), 8 deletions(-) diff --git a/mysql-test/r/explain.result b/mysql-test/r/explain.result index b8db8b53e06..8f2d704b312 100644 --- a/mysql-test/r/explain.result +++ b/mysql-test/r/explain.result @@ -226,4 +226,16 @@ Warnings: Note 1276 Field or reference 'test.t1.c' of SELECT #2 was resolved in SELECT #1 Note 1003 select (select 1 from `test`.`t2` where (`test`.`t2`.`d` = NULL)) AS `(SELECT 1 FROM t2 WHERE d = c)` from `test`.`t1` DROP TABLE t1, t2; +# +# Bug #48419: another explain crash.. +# +CREATE TABLE t1 (a INT); +CREATE TABLE t2 (b BLOB, KEY b(b(100))); +INSERT INTO t2 VALUES ('1'), ('2'), ('3'); +FLUSH TABLES; +EXPLAIN SELECT 1 FROM t1 WHERE a = (SELECT 1 FROM t1 t JOIN t2 WHERE b <= 1 AND t.a); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables +2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables +DROP TABLE t1, t2; End of 5.1 tests. diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result index cf08d763e5c..84b9cdf930c 100644 --- a/mysql-test/r/ps.result +++ b/mysql-test/r/ps.result @@ -155,24 +155,24 @@ execute stmt1 ; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables 6 DERIVED NULL NULL NULL NULL NULL NULL NULL no matching row in const table -5 DEPENDENT SUBQUERY t2 system NULL NULL NULL NULL 0 const row not found -4 DEPENDENT SUBQUERY t2 system NULL NULL NULL NULL 0 const row not found +5 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables +4 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables 3 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables 2 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables execute stmt1 ; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables 6 DERIVED NULL NULL NULL NULL NULL NULL NULL no matching row in const table -5 DEPENDENT SUBQUERY t2 system NULL NULL NULL NULL 0 const row not found -4 DEPENDENT SUBQUERY t2 system NULL NULL NULL NULL 0 const row not found +5 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables +4 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables 3 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables 2 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables explain SELECT (SELECT SUM(c1 + c12 + 0.0) FROM t2 where (t1.c2 - 0e-3) = t2.c2 GROUP BY t1.c15 LIMIT 1) as scalar_s, exists (select 1.0e+0 from t2 where t2.c3 * 9.0000000000 = t1.c4) as exists_s, c5 * 4 in (select c6 + 0.3e+1 from t2) as in_s, (c7 - 4, c8 - 4) in (select c9 + 4.0, c10 + 40e-1 from t2) as in_row_s FROM t1, (select c25 x, c32 y from t2) tt WHERE x * 1 = c25; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables 6 DERIVED NULL NULL NULL NULL NULL NULL NULL no matching row in const table -5 DEPENDENT SUBQUERY t2 system NULL NULL NULL NULL 0 const row not found -4 DEPENDENT SUBQUERY t2 system NULL NULL NULL NULL 0 const row not found +5 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables +4 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables 3 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables 2 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables deallocate prepare stmt1; diff --git a/mysql-test/t/explain.test b/mysql-test/t/explain.test index 3c2f7bbbe96..ba6be72dbdc 100644 --- a/mysql-test/t/explain.test +++ b/mysql-test/t/explain.test @@ -198,4 +198,19 @@ INSERT INTO t2 VALUES (NULL), (0); EXPLAIN EXTENDED SELECT (SELECT 1 FROM t2 WHERE d = c) FROM t1; DROP TABLE t1, t2; + +--echo # +--echo # Bug #48419: another explain crash.. +--echo # + +CREATE TABLE t1 (a INT); +CREATE TABLE t2 (b BLOB, KEY b(b(100))); +INSERT INTO t2 VALUES ('1'), ('2'), ('3'); + +FLUSH TABLES; + +EXPLAIN SELECT 1 FROM t1 WHERE a = (SELECT 1 FROM t1 t JOIN t2 WHERE b <= 1 AND t.a); + +DROP TABLE t1, t2; + --echo End of 5.1 tests. diff --git a/sql/sql_select.cc b/sql/sql_select.cc index a426f4b68a1..291432c2bb6 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -1107,8 +1107,7 @@ JOIN::optimize() } if (conds && const_table_map != found_const_table_map && - (select_options & SELECT_DESCRIBE) && - select_lex->master_unit() == &thd->lex->unit) // upper level SELECT + (select_options & SELECT_DESCRIBE)) { conds=new Item_int((longlong) 0,1); // Always false } From e3d842c60d0fe723b77e92dc63e7dced93639e7c Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Wed, 23 Jun 2010 10:17:21 +0300 Subject: [PATCH 019/115] Merge Bug#47991 fix from mysql-5.1-innodb ------------------------------------------------------------ revno: 3517 revision-id: vasil.dimov@oracle.com-20100622163043-dc0lxy0byg74viet parent: marko.makela@oracle.com-20100621095148-8g73k8k68dpj080u committer: Vasil Dimov branch nick: mysql-5.1-innodb timestamp: Tue 2010-06-22 19:30:43 +0300 message: Fix Bug#47991 InnoDB Dictionary Cache memory usage increases indefinitely when renaming tables Allocate the table name using ut_malloc() instead of table->heap because the latter cannot be freed. Adjust dict_sys->size calculations all over the code. Change dict_table_t::name from const char* to char* because we need to ut_malloc()/ut_free() it. Reviewed by: Inaam, Marko, Heikki (rb://384) Approved by: Heikki (rb://384) ------------------------------------------------------------ --- storage/innobase/dict/dict0dict.c | 34 ++++++++++++++++++++++------- storage/innobase/dict/dict0mem.c | 4 +++- storage/innobase/include/dict0mem.h | 2 +- storage/innobase/include/univ.i | 6 +++++ storage/innobase/page/page0zip.c | 1 + storage/innobase/row/row0merge.c | 13 ++++++++++- 6 files changed, 49 insertions(+), 11 deletions(-) diff --git a/storage/innobase/dict/dict0dict.c b/storage/innobase/dict/dict0dict.c index a298d785449..734c104183d 100644 --- a/storage/innobase/dict/dict0dict.c +++ b/storage/innobase/dict/dict0dict.c @@ -864,7 +864,8 @@ dict_table_add_to_cache( /* Add table to LRU list of tables */ UT_LIST_ADD_FIRST(table_LRU, dict_sys->table_LRU, table); - dict_sys->size += mem_heap_get_size(table->heap); + dict_sys->size += mem_heap_get_size(table->heap) + + strlen(table->name) + 1; } /**********************************************************************//** @@ -918,14 +919,21 @@ dict_table_rename_in_cache( dict_foreign_t* foreign; dict_index_t* index; ulint fold; - ulint old_size; - const char* old_name; + char old_name[MAX_TABLE_NAME_LEN + 1]; ut_ad(table); ut_ad(mutex_own(&(dict_sys->mutex))); - old_size = mem_heap_get_size(table->heap); - old_name = table->name; + /* store the old/current name to an automatic variable */ + if (strlen(table->name) + 1 <= sizeof(old_name)) { + memcpy(old_name, table->name, strlen(table->name) + 1); + } else { + ut_print_timestamp(stderr); + fprintf(stderr, "InnoDB: too long table name: '%s', " + "max length is %d\n", table->name, + MAX_TABLE_NAME_LEN); + ut_error; + } fold = ut_fold_string(new_name); @@ -971,12 +979,22 @@ dict_table_rename_in_cache( /* Remove table from the hash tables of tables */ HASH_DELETE(dict_table_t, name_hash, dict_sys->table_hash, ut_fold_string(old_name), table); - table->name = mem_heap_strdup(table->heap, new_name); + + if (strlen(new_name) > strlen(table->name)) { + /* We allocate MAX_TABLE_NAME_LEN+1 bytes here to avoid + memory fragmentation, we assume a repeated calls of + ut_realloc() with the same size do not cause fragmentation */ + ut_a(strlen(new_name) <= MAX_TABLE_NAME_LEN); + table->name = ut_realloc(table->name, MAX_TABLE_NAME_LEN + 1); + } + memcpy(table->name, new_name, strlen(new_name) + 1); /* Add table to hash table of tables */ HASH_INSERT(dict_table_t, name_hash, dict_sys->table_hash, fold, table); - dict_sys->size += (mem_heap_get_size(table->heap) - old_size); + + dict_sys->size += strlen(new_name) - strlen(old_name); + ut_a(dict_sys->size > 0); /* Update the table_name field in indexes */ index = dict_table_get_first_index(table); @@ -1201,7 +1219,7 @@ dict_table_remove_from_cache( /* Remove table from LRU list of tables */ UT_LIST_REMOVE(table_LRU, dict_sys->table_LRU, table); - size = mem_heap_get_size(table->heap); + size = mem_heap_get_size(table->heap) + strlen(table->name) + 1; ut_ad(dict_sys->size >= size); diff --git a/storage/innobase/dict/dict0mem.c b/storage/innobase/dict/dict0mem.c index b6e516783c7..c1c1c063c3e 100644 --- a/storage/innobase/dict/dict0mem.c +++ b/storage/innobase/dict/dict0mem.c @@ -73,7 +73,8 @@ dict_mem_table_create( table->heap = heap; table->flags = (unsigned int) flags; - table->name = mem_heap_strdup(heap, name); + table->name = ut_malloc(strlen(name) + 1); + memcpy(table->name, name, strlen(name) + 1); table->space = (unsigned int) space; table->n_cols = (unsigned int) (n_cols + DATA_N_SYS_COLS); @@ -112,6 +113,7 @@ dict_mem_table_free( #ifndef UNIV_HOTBACKUP mutex_free(&(table->autoinc_mutex)); #endif /* UNIV_HOTBACKUP */ + ut_free(table->name); mem_heap_free(table->heap); } diff --git a/storage/innobase/include/dict0mem.h b/storage/innobase/include/dict0mem.h index 57e5b5394ee..c9a95343e69 100644 --- a/storage/innobase/include/dict0mem.h +++ b/storage/innobase/include/dict0mem.h @@ -416,7 +416,7 @@ initialized to 0, NULL or FALSE in dict_mem_table_create(). */ struct dict_table_struct{ dulint id; /*!< id of the table */ mem_heap_t* heap; /*!< memory heap */ - const char* name; /*!< table name */ + char* name; /*!< table name */ const char* dir_path_of_temp_table;/*!< NULL or the directory path where a TEMPORARY table that was explicitly created by a user should be placed if diff --git a/storage/innobase/include/univ.i b/storage/innobase/include/univ.i index 11cec113fc8..7fb201995e9 100644 --- a/storage/innobase/include/univ.i +++ b/storage/innobase/include/univ.i @@ -310,6 +310,12 @@ management to ensure correct alignment for doubles etc. */ /* Maximum number of parallel threads in a parallelized operation */ #define UNIV_MAX_PARALLELISM 32 +/* The maximum length of a table name. This is the MySQL limit and is +defined in mysql_com.h like NAME_CHAR_LEN*SYSTEM_CHARSET_MBMAXLEN, the +number does not include a terminating '\0'. InnoDB probably can handle +longer names internally */ +#define MAX_TABLE_NAME_LEN 192 + /* UNIVERSAL TYPE DEFINITIONS ========================== diff --git a/storage/innobase/page/page0zip.c b/storage/innobase/page/page0zip.c index 14ec3e7a94f..1f094334589 100644 --- a/storage/innobase/page/page0zip.c +++ b/storage/innobase/page/page0zip.c @@ -1464,6 +1464,7 @@ page_zip_fields_free( dict_table_t* table = index->table; mem_heap_free(index->heap); mutex_free(&(table->autoinc_mutex)); + ut_free(table->name); mem_heap_free(table->heap); } } diff --git a/storage/innobase/row/row0merge.c b/storage/innobase/row/row0merge.c index d9084bb4ffd..aab13742532 100644 --- a/storage/innobase/row/row0merge.c +++ b/storage/innobase/row/row0merge.c @@ -2357,7 +2357,7 @@ row_merge_rename_tables( { ulint err = DB_ERROR; pars_info_t* info; - const char* old_name= old_table->name; + char old_name[MAX_TABLE_NAME_LEN + 1]; ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); ut_ad(old_table != new_table); @@ -2365,6 +2365,17 @@ row_merge_rename_tables( ut_a(trx->dict_operation_lock_mode == RW_X_LATCH); + /* store the old/current name to an automatic variable */ + if (strlen(old_table->name) + 1 <= sizeof(old_name)) { + memcpy(old_name, old_table->name, strlen(old_table->name) + 1); + } else { + ut_print_timestamp(stderr); + fprintf(stderr, "InnoDB: too long table name: '%s', " + "max length is %d\n", old_table->name, + MAX_TABLE_NAME_LEN); + ut_error; + } + trx->op_info = "renaming tables"; /* We use the private SQL parser of Innobase to generate the query From 0a7d57999237fae1d6208c58448ec3fd3a3d698b Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Wed, 23 Jun 2010 13:02:49 +0300 Subject: [PATCH 020/115] Enable InnoDB's UNIV_DEBUG if MySQL's WITH_DEBUG[_FULL] is defined This will make PB2 test InnoDB with UNIV_DEBUG on its *_debug platforms Discussed with: Marko (on IRC) --- storage/innobase/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/storage/innobase/CMakeLists.txt b/storage/innobase/CMakeLists.txt index 6aec52032f3..23b7fb2d479 100644 --- a/storage/innobase/CMakeLists.txt +++ b/storage/innobase/CMakeLists.txt @@ -40,6 +40,10 @@ IF(UNIX) ENDIF() ENDIF() +# Enable InnoDB's UNIV_DEBUG if MySQL's WITH_DEBUG[_FULL] is defined +IF(WITH_DEBUG OR WITH_DEBUG_FULL) + ADD_DEFINITIONS("-DUNIV_DEBUG") +ENDIF() IF(NOT MSVC) # either define HAVE_IB_GCC_ATOMIC_BUILTINS or not From d72f61396b0883cd0cc624a33cdfdb9079a070fd Mon Sep 17 00:00:00 2001 From: sunanda Date: Wed, 23 Jun 2010 12:03:22 +0200 Subject: [PATCH 021/115] Backport into build-201006221614-5.1.46sp1 > ------------------------------------------------------------ > revno: 3367 [merge] > revision-id: joro@sun.com-20100504140328-srxf3c088j2twnq6 > parent: kristofer.pettersson@sun.com-20100503172109-f9hracq5pqsaomb1 > parent: joro@sun.com-20100503151651-nakknn8amrapmdp7 > committer: Georgi Kodinov > branch nick: B53371-5.1-bugteam > timestamp: Tue 2010-05-04 17:03:28 +0300 > message: > Bug #53371: COM_FIELD_LIST can be abused to bypass table level grants. > > This is the 5.1 merge and extension of the fix. > The server was happily accepting paths in table name in all places a table > name is accepted (e.g. a SELECT). This allowed all users that have some > privilege over some database to read all tables in all databases in all > mysql server instances that the server file system has access to. > Fixed by : > 1. making sure no path elements are allowed in quoted table name when > constructing the path (note that the path symbols are still valid in table names > when they're properly escaped by the server). > 2. checking the #mysql50# prefixed names the same way they're checked for > path elements in mysql-5.0. > ------------------------------------------------------------ > Use --include-merges or -n0 to see merged revisions. --- mysql-test/r/grant.result | 16 ++++++++++++++ mysql-test/t/grant.test | 25 ++++++++++++++++++++++ sql/mysql_priv.h | 2 +- sql/partition_info.cc | 4 ++-- sql/sql_parse.cc | 9 +++++++- sql/sql_table.cc | 14 ++++++++++++ sql/sql_yacc.yy | 2 +- sql/table.cc | 29 ++++++++++++++++++++++--- tests/mysql_client_test.c | 45 +++++++++++++++++++++++++++++++++++++++ 9 files changed, 138 insertions(+), 8 deletions(-) diff --git a/mysql-test/r/grant.result b/mysql-test/r/grant.result index 92beccd2a9e..6831ef6183d 100644 --- a/mysql-test/r/grant.result +++ b/mysql-test/r/grant.result @@ -1413,3 +1413,19 @@ DROP USER 'user1'; DROP USER 'user1'@'localhost'; DROP USER 'user2'; DROP DATABASE db1; +CREATE DATABASE db1; +CREATE DATABASE db2; +GRANT SELECT ON db1.* to 'testbug'@localhost; +USE db2; +CREATE TABLE t1 (a INT); +USE test; +SELECT * FROM `../db2/tb2`; +ERROR 42S02: Table 'db1.../db2/tb2' doesn't exist +SELECT * FROM `../db2`.tb2; +ERROR 42000: SELECT command denied to user 'testbug'@'localhost' for table 'tb2' +SELECT * FROM `#mysql50#/../db2/tb2`; +ERROR 42S02: Table 'db1.#mysql50#/../db2/tb2' doesn't exist +DROP USER 'testbug'@localhost; +DROP TABLE db2.t1; +DROP DATABASE db1; +DROP DATABASE db2; diff --git a/mysql-test/t/grant.test b/mysql-test/t/grant.test index bcd393bd6ab..cb8d3c63be8 100644 --- a/mysql-test/t/grant.test +++ b/mysql-test/t/grant.test @@ -1525,5 +1525,30 @@ DROP USER 'user1'@'localhost'; DROP USER 'user2'; DROP DATABASE db1; + +# +# Bug #53371: COM_FIELD_LIST can be abused to bypass table level grants. +# + +CREATE DATABASE db1; +CREATE DATABASE db2; +GRANT SELECT ON db1.* to 'testbug'@localhost; +USE db2; +CREATE TABLE t1 (a INT); +USE test; +connect (con1,localhost,testbug,,db1); +--error ER_NO_SUCH_TABLE +SELECT * FROM `../db2/tb2`; +--error ER_TABLEACCESS_DENIED_ERROR +SELECT * FROM `../db2`.tb2; +--error ER_NO_SUCH_TABLE +SELECT * FROM `#mysql50#/../db2/tb2`; +connection default; +disconnect con1; +DROP USER 'testbug'@localhost; +DROP TABLE db2.t1; +DROP DATABASE db1; +DROP DATABASE db2; + # Wait till we reached the initial number of concurrent sessions --source include/wait_until_count_sessions.inc diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 56175d069c5..3ecbef4d456 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -2263,7 +2263,7 @@ void update_create_info_from_table(HA_CREATE_INFO *info, TABLE *form); int rename_file_ext(const char * from,const char * to,const char * ext); bool check_db_name(LEX_STRING *db); bool check_column_name(const char *name); -bool check_table_name(const char *name, uint length); +bool check_table_name(const char *name, uint length, bool check_for_path_chars); char *get_field(MEM_ROOT *mem, Field *field); bool get_field(MEM_ROOT *mem, Field *field, class String *res); int wild_case_compare(CHARSET_INFO *cs, const char *str,const char *wildstr); diff --git a/sql/partition_info.cc b/sql/partition_info.cc index ba9ea0e876e..6e2f7dfad26 100644 --- a/sql/partition_info.cc +++ b/sql/partition_info.cc @@ -972,7 +972,7 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type, part_elem->engine_type= default_engine_type; } if (check_table_name(part_elem->partition_name, - strlen(part_elem->partition_name))) + strlen(part_elem->partition_name), FALSE)) { my_error(ER_WRONG_PARTITION_NAME, MYF(0)); goto end; @@ -990,7 +990,7 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type, { sub_elem= sub_it++; if (check_table_name(sub_elem->partition_name, - strlen(sub_elem->partition_name))) + strlen(sub_elem->partition_name), FALSE)) { my_error(ER_WRONG_PARTITION_NAME, MYF(0)); goto end; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 11481933c8a..93d80164ffb 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1310,6 +1310,13 @@ bool dispatch_command(enum enum_server_command command, THD *thd, } thd->convert_string(&conv_name, system_charset_info, packet, arg_length, thd->charset()); + if (check_table_name(conv_name.str, conv_name.length, FALSE)) + { + /* this is OK due to convert_string() null-terminating the string */ + my_error(ER_WRONG_TABLE_NAME, MYF(0), conv_name.str); + break; + } + table_list.alias= table_list.table_name= conv_name.str; packet= arg_end + 1; @@ -6233,7 +6240,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, DBUG_RETURN(0); // End of memory alias_str= alias ? alias->str : table->table.str; if (!test(table_options & TL_OPTION_ALIAS) && - check_table_name(table->table.str, table->table.length)) + check_table_name(table->table.str, table->table.length, FALSE)) { my_error(ER_WRONG_TABLE_NAME, MYF(0), table->table.str); DBUG_RETURN(0); diff --git a/sql/sql_table.cc b/sql/sql_table.cc index ad72cab664e..84e6c721d72 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -435,7 +435,21 @@ uint tablename_to_filename(const char *from, char *to, uint to_length) DBUG_PRINT("enter", ("from '%s'", from)); if ((length= check_n_cut_mysql50_prefix(from, to, to_length))) + { + /* + Check if the name supplied is a valid mysql 5.0 name and + make the name a zero length string if it's not. + Note that just returning zero length is not enough : + a lot of places don't check the return value and expect + a zero terminated string. + */ + if (check_table_name(to, length, TRUE)) + { + to[0]= 0; + length= 0; + } DBUG_RETURN(length); + } length= strconvert(system_charset_info, from, &my_charset_filename, to, to_length, &errors); if (check_if_legal_tablename(to) && diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 4f43ab8bebd..f815da006b1 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -6133,7 +6133,7 @@ alter_list_item: { MYSQL_YYABORT; } - if (check_table_name($3->table.str,$3->table.length) || + if (check_table_name($3->table.str,$3->table.length, FALSE) || ($3->db.str && check_db_name(&$3->db))) { my_error(ER_WRONG_TABLE_NAME, MYF(0), $3->table.str); diff --git a/sql/table.cc b/sql/table.cc index a4e2c59fb87..04d7b3a8d0a 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -494,6 +494,26 @@ inline bool is_system_table_name(const char *name, uint length) } +/** + Check if a string contains path elements +*/ + +static inline bool has_disabled_path_chars(const char *str) +{ + for (; *str; str++) + switch (*str) + { + case FN_EXTCHAR: + case '/': + case '\\': + case '~': + case '@': + return TRUE; + } + return FALSE; +} + + /* Read table definition from a binary / text based .frm file @@ -549,7 +569,8 @@ int open_table_def(THD *thd, TABLE_SHARE *share, uint db_flags) This kind of tables must have been opened only by the my_open() above. */ - if (strchr(share->table_name.str, '@') || + if (has_disabled_path_chars(share->table_name.str) || + has_disabled_path_chars(share->db.str) || !strncmp(share->db.str, MYSQL50_TABLE_NAME_PREFIX, MYSQL50_TABLE_NAME_PREFIX_LENGTH) || !strncmp(share->table_name.str, MYSQL50_TABLE_NAME_PREFIX, @@ -2711,7 +2732,6 @@ bool check_db_name(LEX_STRING *org_name) (name_length > NAME_CHAR_LEN)); /* purecov: inspected */ } - /* Allow anything as a table name, as long as it doesn't contain an ' ' at the end @@ -2719,7 +2739,7 @@ bool check_db_name(LEX_STRING *org_name) */ -bool check_table_name(const char *name, uint length) +bool check_table_name(const char *name, uint length, bool check_for_path_chars) { uint name_length= 0; // name length in symbols const char *end= name+length; @@ -2746,6 +2766,9 @@ bool check_table_name(const char *name, uint length) continue; } } + if (check_for_path_chars && + (*name == '/' || *name == '\\' || *name == '~' || *name == FN_EXTCHAR)) + return 1; #endif name++; name_length++; diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index f65e549fd96..b99461ecd06 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -18049,6 +18049,50 @@ static void test_bug44495() DBUG_VOID_RETURN; } +static void test_bug53371() +{ + int rc; + MYSQL_RES *result; + + myheader("test_bug53371"); + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + myquery(rc); + rc= mysql_query(mysql, "DROP DATABASE IF EXISTS bug53371"); + myquery(rc); + rc= mysql_query(mysql, "DROP USER 'testbug'@localhost"); + + rc= mysql_query(mysql, "CREATE TABLE t1 (a INT)"); + myquery(rc); + rc= mysql_query(mysql, "CREATE DATABASE bug53371"); + myquery(rc); + rc= mysql_query(mysql, "GRANT SELECT ON bug53371.* to 'testbug'@localhost"); + myquery(rc); + + rc= mysql_change_user(mysql, "testbug", NULL, "bug53371"); + myquery(rc); + + rc= mysql_query(mysql, "SHOW COLUMNS FROM client_test_db.t1"); + DIE_UNLESS(rc); + DIE_UNLESS(mysql_errno(mysql) == 1142); + + result= mysql_list_fields(mysql, "../client_test_db/t1", NULL); + DIE_IF(result); + + result= mysql_list_fields(mysql, "#mysql50#/../client_test_db/t1", NULL); + DIE_IF(result); + + rc= mysql_change_user(mysql, opt_user, opt_password, current_db); + myquery(rc); + rc= mysql_query(mysql, "DROP TABLE t1"); + myquery(rc); + rc= mysql_query(mysql, "DROP DATABASE bug53371"); + myquery(rc); + rc= mysql_query(mysql, "DROP USER 'testbug'@localhost"); + myquery(rc); +} + + /* Read and parse arguments and MySQL options from my.cnf */ @@ -18358,6 +18402,7 @@ static struct my_tests_st my_tests[]= { { "test_bug30472", test_bug30472 }, { "test_bug20023", test_bug20023 }, { "test_bug45010", test_bug45010 }, + { "test_bug53371", test_bug53371 }, { "test_bug31418", test_bug31418 }, { "test_bug31669", test_bug31669 }, { "test_bug28386", test_bug28386 }, From b722f546b6e4f9f4819910535aae37cb48194823 Mon Sep 17 00:00:00 2001 From: sunanda Date: Wed, 23 Jun 2010 12:14:23 +0200 Subject: [PATCH 022/115] Backport into build-201006221614-5.1.46sp1 > ------------------------------------------------------------ > revno: 3386 > revision-id: sergey.glukhov@sun.com-20100518082821-yajhvbv1ghmlpu1n > parent: aelkin@mysql.com-20100516170332-x8priwrdjwolc065 > committer: Sergey Glukhov > branch nick: mysql-5.1-bugteam > timestamp: Tue 2010-05-18 13:28:21 +0500 > message: > Bug#48729 SELECT ... FROM INFORMATION_SCHEMA.ROUTINES causes memory to grow > Analysis showed that in case of accessing I_S table > ROUTINES we perform unnecessary allocations > with get_field() function for every processed row that > in their turn causes significant memory growth. > the fix is to avoid use of get_field(). --- sql/sql_show.cc | 74 ++++++++++++++++++++++++------------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/sql/sql_show.cc b/sql/sql_show.cc index cb60027842d..b4881125b14 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -4180,24 +4180,37 @@ int fill_schema_coll_charset_app(THD *thd, TABLE_LIST *tables, COND *cond) } +static inline void copy_field_as_string(Field *to_field, Field *from_field) +{ + char buff[MAX_FIELD_WIDTH]; + String tmp_str(buff, sizeof(buff), system_charset_info); + from_field->val_str(&tmp_str); + to_field->store(tmp_str.ptr(), tmp_str.length(), system_charset_info); +} + + bool store_schema_proc(THD *thd, TABLE *table, TABLE *proc_table, const char *wild, bool full_access, const char *sp_user) { - String tmp_string; - String sp_db, sp_name, definer; MYSQL_TIME time; LEX *lex= thd->lex; CHARSET_INFO *cs= system_charset_info; - get_field(thd->mem_root, proc_table->field[0], &sp_db); - get_field(thd->mem_root, proc_table->field[1], &sp_name); - get_field(thd->mem_root, proc_table->field[11], &definer); + char sp_db_buff[NAME_LEN + 1], sp_name_buff[NAME_LEN + 1], + definer_buff[USERNAME_LENGTH + HOSTNAME_LENGTH + 2]; + String sp_db(sp_db_buff, sizeof(sp_db_buff), cs); + String sp_name(sp_name_buff, sizeof(sp_name_buff), cs); + String definer(definer_buff, sizeof(definer_buff), cs); + + proc_table->field[0]->val_str(&sp_db); + proc_table->field[1]->val_str(&sp_name); + proc_table->field[11]->val_str(&definer); + if (!full_access) - full_access= !strcmp(sp_user, definer.ptr()); - if (!full_access && check_some_routine_access(thd, sp_db.ptr(), - sp_name.ptr(), - proc_table->field[2]-> - val_int() == - TYPE_ENUM_PROCEDURE)) + full_access= !strcmp(sp_user, definer.c_ptr_safe()); + if (!full_access && + check_some_routine_access(thd, sp_db.c_ptr_safe(), sp_name.c_ptr_safe(), + proc_table->field[2]->val_int() == + TYPE_ENUM_PROCEDURE)) return 0; if ((lex->sql_command == SQLCOM_SHOW_STATUS_PROC && @@ -4207,55 +4220,42 @@ bool store_schema_proc(THD *thd, TABLE *table, TABLE *proc_table, (sql_command_flags[lex->sql_command] & CF_STATUS_COMMAND) == 0) { restore_record(table, s->default_values); - if (!wild || !wild[0] || !wild_compare(sp_name.ptr(), wild, 0)) + if (!wild || !wild[0] || !wild_compare(sp_name.c_ptr_safe(), wild, 0)) { int enum_idx= (int) proc_table->field[5]->val_int(); table->field[3]->store(sp_name.ptr(), sp_name.length(), cs); - get_field(thd->mem_root, proc_table->field[3], &tmp_string); - table->field[0]->store(tmp_string.ptr(), tmp_string.length(), cs); + copy_field_as_string(table->field[0], proc_table->field[3]); table->field[2]->store(sp_db.ptr(), sp_db.length(), cs); - get_field(thd->mem_root, proc_table->field[2], &tmp_string); - table->field[4]->store(tmp_string.ptr(), tmp_string.length(), cs); + copy_field_as_string(table->field[4], proc_table->field[2]); if (proc_table->field[2]->val_int() == TYPE_ENUM_FUNCTION) { - get_field(thd->mem_root, proc_table->field[9], &tmp_string); - table->field[5]->store(tmp_string.ptr(), tmp_string.length(), cs); + copy_field_as_string(table->field[5], proc_table->field[9]); table->field[5]->set_notnull(); } if (full_access) { - get_field(thd->mem_root, proc_table->field[19], &tmp_string); - table->field[7]->store(tmp_string.ptr(), tmp_string.length(), cs); + copy_field_as_string(table->field[7], proc_table->field[19]); table->field[7]->set_notnull(); } table->field[6]->store(STRING_WITH_LEN("SQL"), cs); table->field[10]->store(STRING_WITH_LEN("SQL"), cs); - get_field(thd->mem_root, proc_table->field[6], &tmp_string); - table->field[11]->store(tmp_string.ptr(), tmp_string.length(), cs); + copy_field_as_string(table->field[11], proc_table->field[6]); table->field[12]->store(sp_data_access_name[enum_idx].str, sp_data_access_name[enum_idx].length , cs); - get_field(thd->mem_root, proc_table->field[7], &tmp_string); - table->field[14]->store(tmp_string.ptr(), tmp_string.length(), cs); + copy_field_as_string(table->field[14], proc_table->field[7]); + bzero((char *)&time, sizeof(time)); ((Field_timestamp *) proc_table->field[12])->get_time(&time); table->field[15]->store_time(&time, MYSQL_TIMESTAMP_DATETIME); bzero((char *)&time, sizeof(time)); ((Field_timestamp *) proc_table->field[13])->get_time(&time); table->field[16]->store_time(&time, MYSQL_TIMESTAMP_DATETIME); - get_field(thd->mem_root, proc_table->field[14], &tmp_string); - table->field[17]->store(tmp_string.ptr(), tmp_string.length(), cs); - get_field(thd->mem_root, proc_table->field[15], &tmp_string); - table->field[18]->store(tmp_string.ptr(), tmp_string.length(), cs); + copy_field_as_string(table->field[17], proc_table->field[14]); + copy_field_as_string(table->field[18], proc_table->field[15]); table->field[19]->store(definer.ptr(), definer.length(), cs); - - get_field(thd->mem_root, proc_table->field[16], &tmp_string); - table->field[20]->store(tmp_string.ptr(), tmp_string.length(), cs); - - get_field(thd->mem_root, proc_table->field[17], &tmp_string); - table->field[21]->store(tmp_string.ptr(), tmp_string.length(), cs); - - get_field(thd->mem_root, proc_table->field[18], &tmp_string); - table->field[22]->store(tmp_string.ptr(), tmp_string.length(), cs); + copy_field_as_string(table->field[20], proc_table->field[16]); + copy_field_as_string(table->field[21], proc_table->field[17]); + copy_field_as_string(table->field[22], proc_table->field[18]); return schema_table_store_record(thd, table); } From 120717da681f027fb9c55597fa2753645d90631a Mon Sep 17 00:00:00 2001 From: sunanda Date: Wed, 23 Jun 2010 12:22:05 +0200 Subject: [PATCH 023/115] Backport into build-201006221614-5.1.46sp1 > ------------------------------------------------------------ > revno: 3392.1.1 > revision-id: gshchepa@mysql.com-20100521184732-0jvpzinv0uwyvr2d > parent: sven.sandberg@sun.com-20100520153801-yyhujm1qqa4eyfn0 > committer: Gleb Shchepa > branch nick: 53804-5.1 > timestamp: Fri 2010-05-21 22:47:32 +0400 > message: > Bug #53804: serious flaws in the alter database .. upgrade > data directory name command > > The check_db_name function has been modified to validate tails of > #mysql50#-prefixed database names for compliance with MySQL 5.0 > database name encoding rules (the check_table_name function call > has been reused). --- mysql-test/r/renamedb.result | 2 +- mysql-test/r/upgrade.result | 28 ++++++++++++++++++++++++++++ mysql-test/t/renamedb.test | 2 +- mysql-test/t/upgrade.test | 34 ++++++++++++++++++++++++++++++++++ sql/mysql_priv.h | 1 + sql/sql_table.cc | 23 ++++++++++++++++++++--- sql/table.cc | 34 ++++++++++------------------------ 7 files changed, 95 insertions(+), 29 deletions(-) diff --git a/mysql-test/r/renamedb.result b/mysql-test/r/renamedb.result index ff8f89592fc..e77aca0d0b7 100644 --- a/mysql-test/r/renamedb.result +++ b/mysql-test/r/renamedb.result @@ -7,6 +7,6 @@ ERROR HY000: Incorrect usage of ALTER DATABASE UPGRADE DATA DIRECTORY NAME and n ALTER DATABASE `#mysql51#not-yet` UPGRADE DATA DIRECTORY NAME; ERROR HY000: Incorrect usage of ALTER DATABASE UPGRADE DATA DIRECTORY NAME and name ALTER DATABASE `#mysql50#` UPGRADE DATA DIRECTORY NAME; -ERROR HY000: Incorrect usage of ALTER DATABASE UPGRADE DATA DIRECTORY NAME and name +ERROR 42000: Incorrect database name '#mysql50#' ALTER DATABASE `#mysql50#upgrade-me` UPGRADE DATA DIRECTORY NAME; ERROR 42000: Unknown database '#mysql50#upgrade-me' diff --git a/mysql-test/r/upgrade.result b/mysql-test/r/upgrade.result index 034242079b1..da2f55b5bb1 100644 --- a/mysql-test/r/upgrade.result +++ b/mysql-test/r/upgrade.result @@ -112,3 +112,31 @@ select * from `a-b-c`.v1; f1 drop database `a-b-c`; use test; +# End of 5.0 tests +# +# Bug #53804: serious flaws in the alter database .. upgrade data +# directory name command +# +ALTER DATABASE `#mysql50#:` UPGRADE DATA DIRECTORY NAME; +ERROR 42000: Unknown database '#mysql50#:' +ALTER DATABASE `#mysql50#.` UPGRADE DATA DIRECTORY NAME; +ERROR 42000: Incorrect database name '#mysql50#.' +ALTER DATABASE `#mysql50#../` UPGRADE DATA DIRECTORY NAME; +ERROR 42000: Incorrect database name '#mysql50#../' +ALTER DATABASE `#mysql50#../..` UPGRADE DATA DIRECTORY NAME; +ERROR 42000: Incorrect database name '#mysql50#../..' +ALTER DATABASE `#mysql50#../../` UPGRADE DATA DIRECTORY NAME; +ERROR 42000: Incorrect database name '#mysql50#../../' +ALTER DATABASE `#mysql50#./blablabla` UPGRADE DATA DIRECTORY NAME; +ERROR 42000: Incorrect database name '#mysql50#./blablabla' +ALTER DATABASE `#mysql50#../blablabla` UPGRADE DATA DIRECTORY NAME; +ERROR 42000: Incorrect database name '#mysql50#../blablabla' +ALTER DATABASE `#mysql50#/` UPGRADE DATA DIRECTORY NAME; +ERROR 42000: Incorrect database name '#mysql50#/' +ALTER DATABASE `#mysql50#/.` UPGRADE DATA DIRECTORY NAME; +ERROR 42000: Incorrect database name '#mysql50#/.' +USE `#mysql50#.`; +ERROR 42000: Incorrect database name '#mysql50#.' +USE `#mysql50#../blablabla`; +ERROR 42000: Incorrect database name '#mysql50#../blablabla' +# End of 5.1 tests diff --git a/mysql-test/t/renamedb.test b/mysql-test/t/renamedb.test index 84315090b7a..71d0c127058 100644 --- a/mysql-test/t/renamedb.test +++ b/mysql-test/t/renamedb.test @@ -44,7 +44,7 @@ ALTER DATABASE `#mysql41#not-supported` UPGRADE DATA DIRECTORY NAME; --error ER_WRONG_USAGE ALTER DATABASE `#mysql51#not-yet` UPGRADE DATA DIRECTORY NAME; ---error ER_WRONG_USAGE +--error ER_WRONG_DB_NAME ALTER DATABASE `#mysql50#` UPGRADE DATA DIRECTORY NAME; --error ER_BAD_DB_ERROR diff --git a/mysql-test/t/upgrade.test b/mysql-test/t/upgrade.test index e390e8a1253..a7b9a1531ff 100644 --- a/mysql-test/t/upgrade.test +++ b/mysql-test/t/upgrade.test @@ -137,3 +137,37 @@ select * from `a-b-c`.v1; --enable_ps_protocol drop database `a-b-c`; use test; + +--echo # End of 5.0 tests + +--echo # +--echo # Bug #53804: serious flaws in the alter database .. upgrade data +--echo # directory name command +--echo # + +--error ER_BAD_DB_ERROR +ALTER DATABASE `#mysql50#:` UPGRADE DATA DIRECTORY NAME; +--error ER_WRONG_DB_NAME +ALTER DATABASE `#mysql50#.` UPGRADE DATA DIRECTORY NAME; +--error ER_WRONG_DB_NAME +ALTER DATABASE `#mysql50#../` UPGRADE DATA DIRECTORY NAME; +--error ER_WRONG_DB_NAME +ALTER DATABASE `#mysql50#../..` UPGRADE DATA DIRECTORY NAME; +--error ER_WRONG_DB_NAME +ALTER DATABASE `#mysql50#../../` UPGRADE DATA DIRECTORY NAME; +--error ER_WRONG_DB_NAME +ALTER DATABASE `#mysql50#./blablabla` UPGRADE DATA DIRECTORY NAME; +--error ER_WRONG_DB_NAME +ALTER DATABASE `#mysql50#../blablabla` UPGRADE DATA DIRECTORY NAME; +--error ER_WRONG_DB_NAME +ALTER DATABASE `#mysql50#/` UPGRADE DATA DIRECTORY NAME; +--error ER_WRONG_DB_NAME +ALTER DATABASE `#mysql50#/.` UPGRADE DATA DIRECTORY NAME; + +--error ER_WRONG_DB_NAME +USE `#mysql50#.`; +--error ER_WRONG_DB_NAME +USE `#mysql50#../blablabla`; + +--echo # End of 5.1 tests + diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 3ecbef4d456..30f3a1af437 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -2287,6 +2287,7 @@ uint explain_filename(THD* thd, const char *from, char *to, uint to_length, uint filename_to_tablename(const char *from, char *to, uint to_length); uint tablename_to_filename(const char *from, char *to, uint to_length); uint check_n_cut_mysql50_prefix(const char *from, char *to, uint to_length); +bool check_mysql50_prefix(const char *name); #endif /* MYSQL_SERVER || INNODB_COMPATIBILITY_HOOKS */ #ifdef MYSQL_SERVER uint build_table_filename(char *buff, size_t bufflen, const char *db, diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 84e6c721d72..babc025db87 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -391,6 +391,25 @@ uint filename_to_tablename(const char *from, char *to, uint to_length) } +/** + Check if given string begins with "#mysql50#" prefix + + @param name string to check cut + + @retval + FALSE no prefix found + @retval + TRUE prefix found +*/ + +bool check_mysql50_prefix(const char *name) +{ + return (name[0] == '#' && + !strncmp(name, MYSQL50_TABLE_NAME_PREFIX, + MYSQL50_TABLE_NAME_PREFIX_LENGTH)); +} + + /** Check if given string begins with "#mysql50#" prefix, cut it if so. @@ -406,9 +425,7 @@ uint filename_to_tablename(const char *from, char *to, uint to_length) uint check_n_cut_mysql50_prefix(const char *from, char *to, uint to_length) { - if (from[0] == '#' && - !strncmp(from, MYSQL50_TABLE_NAME_PREFIX, - MYSQL50_TABLE_NAME_PREFIX_LENGTH)) + if (check_mysql50_prefix(from)) return (uint) (strmake(to, from + MYSQL50_TABLE_NAME_PREFIX_LENGTH, to_length - 1) - to); return 0; diff --git a/sql/table.cc b/sql/table.cc index 04d7b3a8d0a..23d41760495 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -2701,44 +2701,30 @@ bool check_db_name(LEX_STRING *org_name) { char *name= org_name->str; uint name_length= org_name->length; + bool check_for_path_chars; if (!name_length || name_length > NAME_LEN) return 1; + if ((check_for_path_chars= check_mysql50_prefix(name))) + { + name+= MYSQL50_TABLE_NAME_PREFIX_LENGTH; + name_length-= MYSQL50_TABLE_NAME_PREFIX_LENGTH; + } + if (lower_case_table_names && name != any_db) my_casedn_str(files_charset_info, name); -#if defined(USE_MB) && defined(USE_MB_IDENT) - if (use_mb(system_charset_info)) - { - name_length= 0; - bool last_char_is_space= TRUE; - char *end= name + org_name->length; - while (name < end) - { - int len; - last_char_is_space= my_isspace(system_charset_info, *name); - len= my_ismbchar(system_charset_info, name, end); - if (!len) - len= 1; - name+= len; - name_length++; - } - return (last_char_is_space || name_length > NAME_CHAR_LEN); - } - else -#endif - return ((org_name->str[org_name->length - 1] != ' ') || - (name_length > NAME_CHAR_LEN)); /* purecov: inspected */ + return check_table_name(name, name_length, check_for_path_chars); } + /* Allow anything as a table name, as long as it doesn't contain an ' ' at the end returns 1 on error */ - bool check_table_name(const char *name, uint length, bool check_for_path_chars) { uint name_length= 0; // name length in symbols @@ -2766,10 +2752,10 @@ bool check_table_name(const char *name, uint length, bool check_for_path_chars) continue; } } +#endif if (check_for_path_chars && (*name == '/' || *name == '\\' || *name == '~' || *name == FN_EXTCHAR)) return 1; -#endif name++; name_length++; } From e9c542b05ebfe1df7cb33100ef9e6eab2297c274 Mon Sep 17 00:00:00 2001 From: Jimmy Yang Date: Wed, 23 Jun 2010 03:46:57 -0700 Subject: [PATCH 024/115] Merge Bug#54044 fix from mysql-5.1-innodb: ------------------------------------------------------------ revno: 3520 committer: Jimmy Yang branch nick: mysql-5.1-innodb timestamp: Tue 2010-06-22 19:04:31 -0700 message: Fix bug #54044, Create temporary tables and using innodb crashes. Screen out NULL type columns, and return without creating the table. rb://378 approved by Marko ------------------------------------------------------------ --- .../suite/innodb/r/innodb_bug54044.result | 3 +++ .../suite/innodb/t/innodb_bug54044.test | 11 +++++++++ storage/innobase/handler/ha_innodb.cc | 24 +++++++++++++++++-- 3 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 mysql-test/suite/innodb/r/innodb_bug54044.result create mode 100644 mysql-test/suite/innodb/t/innodb_bug54044.test diff --git a/mysql-test/suite/innodb/r/innodb_bug54044.result b/mysql-test/suite/innodb/r/innodb_bug54044.result new file mode 100644 index 00000000000..9574381d8e1 --- /dev/null +++ b/mysql-test/suite/innodb/r/innodb_bug54044.result @@ -0,0 +1,3 @@ +CREATE TEMPORARY TABLE TABLE_54044 ENGINE = INNODB +AS SELECT IF(NULL IS NOT NULL, NULL, NULL); +ERROR HY000: Can't create table 'test.TABLE_54044' (errno: -1) diff --git a/mysql-test/suite/innodb/t/innodb_bug54044.test b/mysql-test/suite/innodb/t/innodb_bug54044.test new file mode 100644 index 00000000000..824450ae1a6 --- /dev/null +++ b/mysql-test/suite/innodb/t/innodb_bug54044.test @@ -0,0 +1,11 @@ +# This is the test for bug #54044. Special handle MYSQL_TYPE_NULL type +# during create table, so it will not trigger assertion failure. + +--source include/have_innodb.inc + +# This 'create table' operation should fail because of +# using NULL datatype +--error ER_CANT_CREATE_TABLE +CREATE TEMPORARY TABLE TABLE_54044 ENGINE = INNODB + AS SELECT IF(NULL IS NOT NULL, NULL, NULL); + diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index bc8ab206711..43f53bae310 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -4139,6 +4139,11 @@ get_innobase_type_from_mysql_type( case MYSQL_TYPE_BLOB: case MYSQL_TYPE_LONG_BLOB: return(DATA_BLOB); + case MYSQL_TYPE_NULL: + /* MySQL currently accepts "NULL" datatype, but will + reject such datatype in the next release. We will cope + with it and not trigger assertion failure in 5.1 */ + break; default: ut_error; } @@ -6189,7 +6194,22 @@ create_table_def( field = form->field[i]; col_type = get_innobase_type_from_mysql_type(&unsigned_type, - field); + field); + + if (!col_type) { + push_warning_printf( + (THD*) trx->mysql_thd, + MYSQL_ERROR::WARN_LEVEL_WARN, + ER_CANT_CREATE_TABLE, + "Error creating table '%s' with " + "column '%s'. Please check its " + "column type and try to re-create " + "the table with an appropriate " + "column type.", + table->name, (char*) field->field_name); + goto err_col; + } + if (field->null_ptr) { nulls_allowed = 0; } else { @@ -6247,7 +6267,7 @@ create_table_def( if (dict_col_name_is_reserved(field->field_name)){ my_error(ER_WRONG_COLUMN_NAME, MYF(0), field->field_name); - +err_col: dict_mem_table_free(table); trx_commit_for_mysql(trx); From d26a9ed0d3ed19bda86bed565130b391f13587b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Wed, 23 Jun 2010 14:06:59 +0300 Subject: [PATCH 025/115] Bug#54728: Replace the dulint struct with a 64-bit integer. --- storage/innobase/btr/btr0btr.c | 6 +- storage/innobase/btr/btr0cur.c | 12 +- storage/innobase/btr/btr0sea.c | 47 +++-- storage/innobase/buf/buf0buf.c | 31 ++-- storage/innobase/buf/buf0flu.c | 14 +- storage/innobase/buf/buf0lru.c | 10 +- storage/innobase/data/data0data.c | 28 +-- storage/innobase/dict/dict0boot.c | 47 ++--- storage/innobase/dict/dict0crea.c | 13 +- storage/innobase/dict/dict0dict.c | 50 +++-- storage/innobase/dict/dict0load.c | 29 +-- storage/innobase/fil/fil0fil.c | 10 +- storage/innobase/fsp/fsp0fsp.c | 141 ++++++-------- storage/innobase/handler/ha_innodb.cc | 4 +- storage/innobase/handler/i_s.cc | 45 ++--- storage/innobase/ibuf/ibuf0ibuf.c | 4 +- storage/innobase/include/btr0btr.h | 4 +- storage/innobase/include/btr0btr.ic | 7 +- storage/innobase/include/data0data.h | 2 +- storage/innobase/include/data0data.ic | 4 +- storage/innobase/include/data0type.h | 2 +- storage/innobase/include/dict0boot.h | 31 ++-- storage/innobase/include/dict0boot.ic | 16 +- storage/innobase/include/dict0dict.h | 18 +- storage/innobase/include/dict0dict.ic | 6 +- storage/innobase/include/dict0load.h | 12 +- storage/innobase/include/dict0mem.h | 6 +- storage/innobase/include/dict0types.h | 3 + storage/innobase/include/fil0fil.h | 1 + storage/innobase/include/lock0lock.h | 4 +- storage/innobase/include/mach0data.h | 111 +++++------ storage/innobase/include/mach0data.ic | 189 +++++++------------ storage/innobase/include/mtr0log.h | 22 +-- storage/innobase/include/mtr0log.ic | 12 +- storage/innobase/include/mtr0mtr.h | 9 - storage/innobase/include/page0page.h | 2 +- storage/innobase/include/page0page.ic | 5 +- storage/innobase/include/pars0pars.h | 25 +-- storage/innobase/include/que0que.h | 3 - storage/innobase/include/read0read.h | 10 +- storage/innobase/include/read0read.ic | 14 +- storage/innobase/include/rem0rec.h | 2 +- storage/innobase/include/rem0rec.ic | 4 +- storage/innobase/include/row0upd.h | 2 +- storage/innobase/include/srv0srv.h | 6 +- storage/innobase/include/trx0i_s.h | 13 +- storage/innobase/include/trx0rec.h | 4 +- storage/innobase/include/trx0rec.ic | 4 +- storage/innobase/include/trx0sys.h | 5 + storage/innobase/include/trx0sys.ic | 13 +- storage/innobase/include/trx0trx.h | 24 +-- storage/innobase/include/trx0trx.ic | 12 -- storage/innobase/include/trx0types.h | 13 +- storage/innobase/include/trx0undo.h | 2 +- storage/innobase/include/trx0undo.ic | 39 ++-- storage/innobase/include/univ.i | 7 +- storage/innobase/include/ut0byte.h | 169 +---------------- storage/innobase/include/ut0byte.ic | 254 +------------------------- storage/innobase/include/ut0rnd.h | 8 +- storage/innobase/include/ut0rnd.ic | 12 +- storage/innobase/lock/lock0lock.c | 77 ++++---- storage/innobase/log/log0log.c | 22 +-- storage/innobase/log/log0recv.c | 40 ++-- storage/innobase/mach/mach0data.c | 32 ++-- storage/innobase/mtr/mtr0log.c | 22 +-- storage/innobase/mtr/mtr0mtr.c | 17 -- storage/innobase/page/page0page.c | 6 +- storage/innobase/page/page0zip.c | 4 +- storage/innobase/pars/pars0pars.c | 29 +-- storage/innobase/read/read0read.c | 42 ++--- storage/innobase/row/row0ins.c | 8 +- storage/innobase/row/row0merge.c | 15 +- storage/innobase/row/row0mysql.c | 12 +- storage/innobase/row/row0purge.c | 9 +- storage/innobase/row/row0sel.c | 3 +- storage/innobase/row/row0uins.c | 4 +- storage/innobase/row/row0umod.c | 8 +- storage/innobase/row/row0undo.c | 5 +- storage/innobase/row/row0upd.c | 9 +- storage/innobase/row/row0vers.c | 12 +- storage/innobase/trx/trx0i_s.c | 6 +- storage/innobase/trx/trx0purge.c | 106 +++++------ storage/innobase/trx/trx0rec.c | 64 +++---- storage/innobase/trx/trx0roll.c | 77 ++++---- storage/innobase/trx/trx0rseg.c | 4 +- storage/innobase/trx/trx0sys.c | 67 +++---- storage/innobase/trx/trx0trx.c | 96 +++++----- storage/innobase/trx/trx0undo.c | 23 ++- storage/innobase/ut/ut0byte.c | 25 --- 89 files changed, 874 insertions(+), 1572 deletions(-) diff --git a/storage/innobase/btr/btr0btr.c b/storage/innobase/btr/btr0btr.c index 05dd094b6df..f8638af2e71 100644 --- a/storage/innobase/btr/btr0btr.c +++ b/storage/innobase/btr/btr0btr.c @@ -737,7 +737,7 @@ btr_create( ulint space, /*!< in: space where created */ ulint zip_size,/*!< in: compressed page size in bytes or 0 for uncompressed pages */ - dulint index_id,/*!< in: index id */ + index_id_t index_id,/*!< in: index id */ dict_index_t* index, /*!< in: index */ mtr_t* mtr) /*!< in: mini-transaction handle */ { @@ -1020,7 +1020,7 @@ btr_page_reorganize_low( /* In crash recovery, dict_index_is_sec_or_ibuf() always returns TRUE, even for clustered indexes. max_trx_id is unused in clustered index pages. */ - ut_ad(!ut_dulint_is_zero(max_trx_id) || recovery); + ut_ad(max_trx_id != 0 || recovery); } if (UNIV_LIKELY_NULL(page_zip) @@ -2883,7 +2883,7 @@ btr_discard_only_page_on_level( ibuf_reset_free_bits(block); if (page_is_leaf(buf_block_get_frame(block))) { - ut_a(!ut_dulint_is_zero(max_trx_id)); + ut_a(max_trx_id); page_set_max_trx_id(block, buf_block_get_page_zip(block), max_trx_id, mtr); diff --git a/storage/innobase/btr/btr0cur.c b/storage/innobase/btr/btr0cur.c index 31e1a2d4b12..f1d8bb52825 100644 --- a/storage/innobase/btr/btr0cur.c +++ b/storage/innobase/btr/btr0cur.c @@ -660,7 +660,7 @@ retry_page_get: buf_block_dbg_add_level(block, SYNC_TREE_NODE); } - ut_ad(0 == ut_dulint_cmp(index->id, btr_page_get_index_id(page))); + ut_ad(index->id == btr_page_get_index_id(page)); if (UNIV_UNLIKELY(height == ULINT_UNDEFINED)) { /* We are in the root node */ @@ -854,8 +854,7 @@ btr_cur_open_at_index_side_func( RW_NO_LATCH, NULL, BUF_GET, file, line, mtr); page = buf_block_get_frame(block); - ut_ad(0 == ut_dulint_cmp(index->id, - btr_page_get_index_id(page))); + ut_ad(index->id == btr_page_get_index_id(page)); block->check_index_page_at_flush = TRUE; @@ -975,8 +974,7 @@ btr_cur_open_at_rnd_pos_func( RW_NO_LATCH, NULL, BUF_GET, file, line, mtr); page = buf_block_get_frame(block); - ut_ad(0 == ut_dulint_cmp(index->id, - btr_page_get_index_id(page))); + ut_ad(index->id == btr_page_get_index_id(page)); if (height == ULINT_UNDEFINED) { /* We are in the root node */ @@ -1135,7 +1133,7 @@ btr_cur_trx_report( const char* op) /*!< in: operation */ { fprintf(stderr, "Trx with id " TRX_ID_FMT " going to ", - TRX_ID_PREP_PRINTF(trx->id)); + (ullint) trx->id); fputs(op, stderr); dict_index_name_print(stderr, trx, index); putc('\n', stderr); @@ -1826,7 +1824,7 @@ btr_cur_update_in_place( page_zip_des_t* page_zip; ulint err; rec_t* rec; - roll_ptr_t roll_ptr = ut_dulint_zero; + roll_ptr_t roll_ptr = 0; trx_t* trx; ulint was_delete_marked; mem_heap_t* heap = NULL; diff --git a/storage/innobase/btr/btr0sea.c b/storage/innobase/btr/btr0sea.c index 98a321bdb80..75005dddb87 100644 --- a/storage/innobase/btr/btr0sea.c +++ b/storage/innobase/btr/btr0sea.c @@ -523,9 +523,9 @@ btr_search_update_hash_ref( buf_block_t* block, /*!< in: buffer block where cursor positioned */ btr_cur_t* cursor) /*!< in: cursor */ { - ulint fold; - rec_t* rec; - dulint index_id; + ulint fold; + rec_t* rec; + index_id_t index_id; ut_ad(cursor->flag == BTR_CUR_HASH_FAIL); #ifdef UNIV_SYNC_DEBUG @@ -830,7 +830,7 @@ btr_search_guess_on_hash( buf_block_t* block; rec_t* rec; ulint fold; - dulint index_id; + index_id_t index_id; #ifdef notdefined btr_cur_t cursor2; btr_pcur_t pcur; @@ -922,8 +922,7 @@ btr_search_guess_on_hash( is positioned on. We cannot look at the next of the previous record to determine if our guess for the cursor position is right. */ - if (UNIV_EXPECT - (ut_dulint_cmp(index_id, btr_page_get_index_id(block->frame)), 0) + if (UNIV_UNLIKELY(index_id != btr_page_get_index_id(block->frame)) || !btr_search_check_guess(cursor, has_search_latch, tuple, mode, mtr)) { @@ -1028,7 +1027,7 @@ btr_search_drop_page_hash_index( const rec_t* rec; ulint fold; ulint prev_fold; - dulint index_id; + index_id_t index_id; ulint n_cached; ulint n_recs; ulint* folds; @@ -1088,7 +1087,7 @@ retry: index_id = btr_page_get_index_id(page); - ut_a(0 == ut_dulint_cmp(index_id, index->id)); + ut_a(index_id == index->id); prev_fold = 0; @@ -1245,7 +1244,7 @@ btr_search_build_page_hash_index( rec_t* next_rec; ulint fold; ulint next_fold; - dulint index_id; + index_id_t index_id; ulint n_cached; ulint n_recs; ulint* folds; @@ -1498,7 +1497,7 @@ btr_search_update_hash_on_delete( buf_block_t* block; rec_t* rec; ulint fold; - dulint index_id; + index_id_t index_id; ibool found; ulint offsets_[REC_OFFS_NORMAL_SIZE]; mem_heap_t* heap = NULL; @@ -1604,7 +1603,7 @@ btr_search_update_hash_on_insert( rec_t* rec; rec_t* ins_rec; rec_t* next_rec; - dulint index_id; + index_id_t index_id; ulint fold; ulint ins_fold; ulint next_fold = 0; /* remove warning (??? bug ???) */ @@ -1784,6 +1783,7 @@ btr_search_validate(void) = buf_block_align(node->data); const buf_block_t* hash_block; buf_pool_t* buf_pool; + index_id_t page_index_id; buf_pool = buf_pool_from_bpage((buf_page_t*) block); @@ -1828,12 +1828,15 @@ btr_search_validate(void) + (block->curr_n_bytes > 0), &heap); - if (!block->is_hashed || node->fold - != rec_fold((rec_t*)(node->data), - offsets, - block->curr_n_fields, - block->curr_n_bytes, - btr_page_get_index_id(block->frame))) { + page_index_id = btr_page_get_index_id(block->frame); + + if (UNIV_UNLIKELY + (!block->is_hashed || node->fold + != rec_fold((rec_t*)(node->data), + offsets, + block->curr_n_fields, + block->curr_n_bytes, + page_index_id))) { const page_t* page = block->frame; ok = FALSE; @@ -1843,21 +1846,17 @@ btr_search_validate(void) " InnoDB: Error in an adaptive hash" " index pointer to page %lu\n" "InnoDB: ptr mem address %p" - " index id %lu %lu," + " index id %llu," " node fold %lu, rec fold %lu\n", (ulong) page_get_page_no(page), node->data, - (ulong) ut_dulint_get_high( - btr_page_get_index_id(page)), - (ulong) ut_dulint_get_low( - btr_page_get_index_id(page)), + (ullint) page_index_id, (ulong) node->fold, (ulong) rec_fold((rec_t*)(node->data), offsets, block->curr_n_fields, block->curr_n_bytes, - btr_page_get_index_id( - page))); + page_index_id)); fputs("InnoDB: Record ", stderr); rec_print_new(stderr, (rec_t*)node->data, diff --git a/storage/innobase/buf/buf0buf.c b/storage/innobase/buf/buf0buf.c index 4b6b0a82486..ab3c1abf490 100644 --- a/storage/innobase/buf/buf0buf.c +++ b/storage/innobase/buf/buf0buf.c @@ -522,7 +522,9 @@ buf_page_is_corrupted( ib_uint64_t current_lsn; if (log_peek_lsn(¤t_lsn) - && current_lsn < mach_read_ull(read_buf + FIL_PAGE_LSN)) { + && UNIV_UNLIKELY + (current_lsn + < mach_read_from_8(read_buf + FIL_PAGE_LSN))) { ut_print_timestamp(stderr); fprintf(stderr, @@ -538,7 +540,7 @@ buf_page_is_corrupted( "InnoDB: for more information.\n", (ulong) mach_read_from_4(read_buf + FIL_PAGE_OFFSET), - mach_read_ull(read_buf + FIL_PAGE_LSN), + mach_read_from_8(read_buf + FIL_PAGE_LSN), current_lsn); } } @@ -735,17 +737,15 @@ buf_page_print( #endif /* !UNIV_HOTBACKUP */ switch (fil_page_get_type(read_buf)) { + index_id_t index_id; case FIL_PAGE_INDEX: + index_id = btr_page_get_index_id(read_buf); fprintf(stderr, "InnoDB: Page may be an index page where" - " index id is %lu %lu\n", - (ulong) ut_dulint_get_high( - btr_page_get_index_id(read_buf)), - (ulong) ut_dulint_get_low( - btr_page_get_index_id(read_buf))); + " index id is %llu\n", + (ullint) index_id); #ifndef UNIV_HOTBACKUP - index = dict_index_find_on_id_low( - btr_page_get_index_id(read_buf)); + index = dict_index_find_on_id_low(index_id); if (index) { fputs("InnoDB: (", stderr); dict_index_name_print(stderr, NULL, index); @@ -4461,12 +4461,12 @@ buf_print_instance( /*===============*/ buf_pool_t* buf_pool) { - dulint* index_ids; + index_id_t* index_ids; ulint* counts; ulint size; ulint i; ulint j; - dulint id; + index_id_t id; ulint n_found; buf_chunk_t* chunk; dict_index_t* index; @@ -4475,7 +4475,7 @@ buf_print_instance( size = buf_pool->curr_size; - index_ids = mem_alloc(sizeof(dulint) * size); + index_ids = mem_alloc(size * sizeof *index_ids); counts = mem_alloc(sizeof(ulint) * size); buf_pool_mutex_enter(buf_pool); @@ -4530,8 +4530,7 @@ buf_print_instance( while (j < n_found) { - if (ut_dulint_cmp(index_ids[j], - id) == 0) { + if (index_ids[j] == id) { counts[j]++; break; @@ -4554,8 +4553,8 @@ buf_print_instance( index = dict_index_get_if_in_cache(index_ids[i]); fprintf(stderr, - "Block count for index %lu in buffer is about %lu", - (ulong) ut_dulint_get_low(index_ids[i]), + "Block count for index %llu in buffer is about %lu", + (ullint) index_ids[i], (ulong) counts[i]); if (index) { diff --git a/storage/innobase/buf/buf0flu.c b/storage/innobase/buf/buf0flu.c index 6c2a67e8a2d..3737627301f 100644 --- a/storage/innobase/buf/buf0flu.c +++ b/storage/innobase/buf/buf0flu.c @@ -982,8 +982,8 @@ buf_flush_init_for_writing( case FIL_PAGE_TYPE_ZBLOB: case FIL_PAGE_TYPE_ZBLOB2: case FIL_PAGE_INDEX: - mach_write_ull(page_zip->data - + FIL_PAGE_LSN, newest_lsn); + mach_write_to_8(page_zip->data + + FIL_PAGE_LSN, newest_lsn); memset(page_zip->data + FIL_PAGE_FILE_FLUSH_LSN, 0, 8); mach_write_to_4(page_zip->data + FIL_PAGE_SPACE_OR_CHKSUM, @@ -1005,10 +1005,10 @@ buf_flush_init_for_writing( } /* Write the newest modification lsn to the page header and trailer */ - mach_write_ull(page + FIL_PAGE_LSN, newest_lsn); + mach_write_to_8(page + FIL_PAGE_LSN, newest_lsn); - mach_write_ull(page + UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM, - newest_lsn); + mach_write_to_8(page + UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM, + newest_lsn); /* Store the new formula checksum */ @@ -1096,8 +1096,8 @@ buf_flush_write_block_low( ut_a(mach_read_from_4(frame + FIL_PAGE_SPACE_OR_CHKSUM) == page_zip_calc_checksum(frame, zip_size)); } - mach_write_ull(frame + FIL_PAGE_LSN, - bpage->newest_modification); + mach_write_to_8(frame + FIL_PAGE_LSN, + bpage->newest_modification); memset(frame + FIL_PAGE_FILE_FLUSH_LSN, 0, 8); break; case BUF_BLOCK_FILE_PAGE: diff --git a/storage/innobase/buf/buf0lru.c b/storage/innobase/buf/buf0lru.c index a539c4e894b..e1d4b5081b8 100644 --- a/storage/innobase/buf/buf0lru.c +++ b/storage/innobase/buf/buf0lru.c @@ -2278,19 +2278,17 @@ buf_LRU_print_instance( case BUF_BLOCK_FILE_PAGE: frame = buf_block_get_frame((buf_block_t*) bpage); fprintf(stderr, "\ntype %lu" - " index id %lu\n", + " index id %llu\n", (ulong) fil_page_get_type(frame), - (ulong) ut_dulint_get_low( - btr_page_get_index_id(frame))); + (ullint) btr_page_get_index_id(frame)); break; case BUF_BLOCK_ZIP_PAGE: frame = bpage->zip.data; fprintf(stderr, "\ntype %lu size %lu" - " index id %lu\n", + " index id %llu\n", (ulong) fil_page_get_type(frame), (ulong) buf_page_get_zip_size(bpage), - (ulong) ut_dulint_get_low( - btr_page_get_index_id(frame))); + (ullint) btr_page_get_index_id(frame)); break; default: diff --git a/storage/innobase/data/data0data.c b/storage/innobase/data/data0data.c index 0715b49bf9c..0ef0cfa554a 100644 --- a/storage/innobase/data/data0data.c +++ b/storage/innobase/data/data0data.c @@ -367,7 +367,7 @@ dfield_print_also_hex( prtype = dtype_get_prtype(dfield_get_type(dfield)); switch (dtype_get_mtype(dfield_get_type(dfield))) { - dulint id; + ib_id_t id; case DATA_INT: switch (len) { ulint val; @@ -417,22 +417,16 @@ dfield_print_also_hex( case 6: id = mach_read_from_6(data); - fprintf(stderr, "{%lu %lu}", - ut_dulint_get_high(id), - ut_dulint_get_low(id)); + fprintf(stderr, "%llu", (ullint) id); break; case 7: id = mach_read_from_7(data); - fprintf(stderr, "{%lu %lu}", - ut_dulint_get_high(id), - ut_dulint_get_low(id)); + fprintf(stderr, "%llu", (ullint) id); break; case 8: id = mach_read_from_8(data); - fprintf(stderr, "{%lu %lu}", - ut_dulint_get_high(id), - ut_dulint_get_low(id)); + fprintf(stderr, "%llu", (ullint) id); break; default: goto print_hex; @@ -444,29 +438,25 @@ dfield_print_also_hex( case DATA_TRX_ID: id = mach_read_from_6(data); - fprintf(stderr, "trx_id " TRX_ID_FMT, - TRX_ID_PREP_PRINTF(id)); + fprintf(stderr, "trx_id " TRX_ID_FMT, (ullint) id); break; case DATA_ROLL_PTR: id = mach_read_from_7(data); - fprintf(stderr, "roll_ptr {%lu %lu}", - ut_dulint_get_high(id), ut_dulint_get_low(id)); + fprintf(stderr, "roll_ptr " TRX_ID_FMT, (ullint) id); break; case DATA_ROW_ID: id = mach_read_from_6(data); - fprintf(stderr, "row_id {%lu %lu}", - ut_dulint_get_high(id), ut_dulint_get_low(id)); + fprintf(stderr, "row_id " TRX_ID_FMT, (ullint) id); break; default: - id = mach_dulint_read_compressed(data); + id = mach_ull_read_compressed(data); - fprintf(stderr, "mix_id {%lu %lu}", - ut_dulint_get_high(id), ut_dulint_get_low(id)); + fprintf(stderr, "mix_id " TRX_ID_FMT, (ullint) id); } break; diff --git a/storage/innobase/dict/dict0boot.c b/storage/innobase/dict/dict0boot.c index e63c1dc94b9..20d676e6129 100644 --- a/storage/innobase/dict/dict0boot.c +++ b/storage/innobase/dict/dict0boot.c @@ -67,12 +67,15 @@ UNIV_INTERN void dict_hdr_get_new_id( /*================*/ - dulint* table_id, /*!< out: table id (not assigned if NULL) */ - dulint* index_id, /*!< out: index id (not assigned if NULL) */ - ulint* space_id) /*!< out: space id (not assigned if NULL) */ + table_id_t* table_id, /*!< out: table id + (not assigned if NULL) */ + index_id_t* index_id, /*!< out: index id + (not assigned if NULL) */ + ulint* space_id) /*!< out: space id + (not assigned if NULL) */ { dict_hdr_t* dict_hdr; - dulint id; + ib_id_t id; mtr_t mtr; mtr_start(&mtr); @@ -80,16 +83,16 @@ dict_hdr_get_new_id( dict_hdr = dict_hdr_get(&mtr); if (table_id) { - id = mtr_read_dulint(dict_hdr + DICT_HDR_TABLE_ID, &mtr); - id = ut_dulint_add(id, 1); - mlog_write_dulint(dict_hdr + DICT_HDR_TABLE_ID, id, &mtr); + id = mach_read_from_8(dict_hdr + DICT_HDR_TABLE_ID); + id++; + mlog_write_ull(dict_hdr + DICT_HDR_TABLE_ID, id, &mtr); *table_id = id; } if (index_id) { - id = mtr_read_dulint(dict_hdr + DICT_HDR_INDEX_ID, &mtr); - id = ut_dulint_add(id, 1); - mlog_write_dulint(dict_hdr + DICT_HDR_INDEX_ID, id, &mtr); + id = mach_read_from_8(dict_hdr + DICT_HDR_INDEX_ID); + id++; + mlog_write_ull(dict_hdr + DICT_HDR_INDEX_ID, id, &mtr); *index_id = id; } @@ -114,7 +117,7 @@ dict_hdr_flush_row_id(void) /*=======================*/ { dict_hdr_t* dict_hdr; - dulint id; + row_id_t id; mtr_t mtr; ut_ad(mutex_own(&(dict_sys->mutex))); @@ -125,7 +128,7 @@ dict_hdr_flush_row_id(void) dict_hdr = dict_hdr_get(&mtr); - mlog_write_dulint(dict_hdr + DICT_HDR_ROW_ID, id, &mtr); + mlog_write_ull(dict_hdr + DICT_HDR_ROW_ID, id, &mtr); mtr_commit(&mtr); } @@ -157,14 +160,14 @@ dict_hdr_create( /* Start counting row, table, index, and tree ids from DICT_HDR_FIRST_ID */ - mlog_write_dulint(dict_header + DICT_HDR_ROW_ID, - ut_dulint_create(0, DICT_HDR_FIRST_ID), mtr); + mlog_write_ull(dict_header + DICT_HDR_ROW_ID, + DICT_HDR_FIRST_ID, mtr); - mlog_write_dulint(dict_header + DICT_HDR_TABLE_ID, - ut_dulint_create(0, DICT_HDR_FIRST_ID), mtr); + mlog_write_ull(dict_header + DICT_HDR_TABLE_ID, + DICT_HDR_FIRST_ID, mtr); - mlog_write_dulint(dict_header + DICT_HDR_INDEX_ID, - ut_dulint_create(0, DICT_HDR_FIRST_ID), mtr); + mlog_write_ull(dict_header + DICT_HDR_INDEX_ID, + DICT_HDR_FIRST_ID, mtr); mlog_write_ulint(dict_header + DICT_HDR_MAX_SPACE_ID, 0, MLOG_4BYTES, mtr); @@ -273,11 +276,9 @@ dict_boot(void) ..._MARGIN, it will immediately be updated to the disk-based header. */ - dict_sys->row_id = ut_dulint_add( - ut_dulint_align_up(mtr_read_dulint(dict_hdr + DICT_HDR_ROW_ID, - &mtr), - DICT_HDR_ROW_ID_WRITE_MARGIN), - DICT_HDR_ROW_ID_WRITE_MARGIN); + dict_sys->row_id = DICT_HDR_ROW_ID_WRITE_MARGIN + + ut_uint64_align_up(mach_read_from_8(dict_hdr + DICT_HDR_ROW_ID), + DICT_HDR_ROW_ID_WRITE_MARGIN); /* Insert into the dictionary cache the descriptions of the basic system tables */ diff --git a/storage/innobase/dict/dict0crea.c b/storage/innobase/dict/dict0crea.c index f185371bfca..5c4c8507a39 100644 --- a/storage/innobase/dict/dict0crea.c +++ b/storage/innobase/dict/dict0crea.c @@ -578,7 +578,7 @@ dict_build_index_def_step( ins_node_set_new_row(node->ind_def, row); /* Note that the index was created by this transaction. */ - index->trx_id = (ib_uint64_t) ut_conv_dulint_to_longlong(trx->id); + index->trx_id = trx->id; return(DB_SUCCESS); } @@ -749,7 +749,7 @@ dict_truncate_index_tree( ibool drop = !space; ulint zip_size; ulint type; - dulint index_id; + index_id_t index_id; rec_t* rec; const byte* ptr; ulint len; @@ -842,7 +842,7 @@ create: for (index = UT_LIST_GET_FIRST(table->indexes); index; index = UT_LIST_GET_NEXT(indexes, index)) { - if (!ut_dulint_cmp(index->id, index_id)) { + if (index->id == index_id) { root_page_no = btr_create(type, space, zip_size, index_id, index, mtr); index->page = (unsigned int) root_page_no; @@ -852,10 +852,9 @@ create: ut_print_timestamp(stderr); fprintf(stderr, - " InnoDB: Index %lu %lu of table %s is missing\n" + " InnoDB: Index %llu of table %s is missing\n" "InnoDB: from the data dictionary during TRUNCATE!\n", - ut_dulint_get_high(index_id), - ut_dulint_get_low(index_id), + (ullint) index_id, table->name); return(FIL_NULL); @@ -1107,7 +1106,7 @@ dict_create_index_step( if (node->state == INDEX_ADD_TO_CACHE) { - dulint index_id = node->index->id; + index_id_t index_id = node->index->id; err = dict_index_add_to_cache( node->table, node->index, FIL_NULL, diff --git a/storage/innobase/dict/dict0dict.c b/storage/innobase/dict/dict0dict.c index 734c104183d..064a5a34960 100644 --- a/storage/innobase/dict/dict0dict.c +++ b/storage/innobase/dict/dict0dict.c @@ -256,8 +256,8 @@ dict_mutex_exit_for_mysql(void) /** Get the mutex that protects index->stat_n_diff_key_vals[] */ #define GET_INDEX_STAT_MUTEX(index) \ - (&dict_index_stat_mutex[ut_fold_dulint(index->id) \ - % DICT_INDEX_STAT_MUTEX_SIZE]) + (&dict_index_stat_mutex[ut_fold_ull(index->id) \ + % DICT_INDEX_STAT_MUTEX_SIZE]) /**********************************************************************//** Lock the appropriate mutex to protect index->stat_n_diff_key_vals[]. @@ -425,14 +425,14 @@ dict_index_t* dict_index_get_on_id_low( /*=====================*/ dict_table_t* table, /*!< in: table */ - dulint id) /*!< in: index id */ + index_id_t id) /*!< in: index id */ { dict_index_t* index; index = dict_table_get_first_index(table); while (index) { - if (0 == ut_dulint_cmp(id, index->id)) { + if (id == index->id) { /* Found */ return(index); @@ -574,12 +574,12 @@ UNIV_INTERN dict_table_t* dict_table_get_on_id( /*=================*/ - dulint table_id, /*!< in: table id */ - trx_t* trx) /*!< in: transaction handle */ + table_id_t table_id, /*!< in: table id */ + trx_t* trx) /*!< in: transaction handle */ { dict_table_t* table; - if (ut_dulint_cmp(table_id, DICT_FIELDS_ID) <= 0 + if (table_id <= DICT_FIELDS_ID || trx->dict_operation_lock_mode == RW_X_LATCH) { /* It is a system table which will always exist in the table cache: we avoid acquiring the dictionary mutex, because @@ -800,7 +800,7 @@ dict_table_add_to_cache( table->cached = TRUE; fold = ut_fold_string(table->name); - id_fold = ut_fold_dulint(table->id); + id_fold = ut_fold_ull(table->id); row_len = 0; for (i = 0; i < table->n_def; i++) { @@ -842,7 +842,7 @@ dict_table_add_to_cache( dict_table_t* table2; HASH_SEARCH(id_hash, dict_sys->table_id_hash, id_fold, dict_table_t*, table2, ut_ad(table2->cached), - ut_dulint_cmp(table2->id, table->id) == 0); + table2->id == table->id); ut_a(table2 == NULL); #ifdef UNIV_DEBUG @@ -877,7 +877,7 @@ UNIV_INTERN dict_index_t* dict_index_find_on_id_low( /*======================*/ - dulint id) /*!< in: index id */ + index_id_t id) /*!< in: index id */ { dict_table_t* table; dict_index_t* index; @@ -888,7 +888,7 @@ dict_index_find_on_id_low( index = dict_table_get_first_index(table); while (index) { - if (0 == ut_dulint_cmp(id, index->id)) { + if (id == index->id) { /* Found */ return(index); @@ -1144,7 +1144,7 @@ void dict_table_change_id_in_cache( /*==========================*/ dict_table_t* table, /*!< in/out: table object already in cache */ - dulint new_id) /*!< in: new id to set */ + table_id_t new_id) /*!< in: new id to set */ { ut_ad(table); ut_ad(mutex_own(&(dict_sys->mutex))); @@ -1153,12 +1153,12 @@ dict_table_change_id_in_cache( /* Remove the table from the hash table of id's */ HASH_DELETE(dict_table_t, id_hash, dict_sys->table_id_hash, - ut_fold_dulint(table->id), table); + ut_fold_ull(table->id), table); table->id = new_id; /* Add the table back to the hash table */ HASH_INSERT(dict_table_t, id_hash, dict_sys->table_id_hash, - ut_fold_dulint(table->id), table); + ut_fold_ull(table->id), table); } /**********************************************************************//** @@ -1214,7 +1214,7 @@ dict_table_remove_from_cache( HASH_DELETE(dict_table_t, name_hash, dict_sys->table_hash, ut_fold_string(table->name), table); HASH_DELETE(dict_table_t, id_hash, dict_sys->table_id_hash, - ut_fold_dulint(table->id), table); + ut_fold_ull(table->id), table); /* Remove table from LRU list of tables */ UT_LIST_REMOVE(table_LRU, dict_sys->table_LRU, table); @@ -2476,8 +2476,7 @@ dict_table_get_index_by_max_id( /* We found a matching index, select the index with the higher id*/ - if (!found - || ut_dulint_cmp(index->id, found->id) > 0) { + if (!found || index->id > found->id) { found = index; } @@ -3965,7 +3964,7 @@ UNIV_INTERN dict_index_t* dict_index_get_if_in_cache_low( /*===========================*/ - dulint index_id) /*!< in: index id */ + index_id_t index_id) /*!< in: index id */ { ut_ad(mutex_own(&(dict_sys->mutex))); @@ -3980,7 +3979,7 @@ UNIV_INTERN dict_index_t* dict_index_get_if_in_cache( /*=======================*/ - dulint index_id) /*!< in: index id */ + index_id_t index_id) /*!< in: index id */ { dict_index_t* index; @@ -4376,12 +4375,11 @@ dict_table_print_low( fprintf(stderr, "--------------------------------------\n" - "TABLE: name %s, id %lu %lu, flags %lx, columns %lu," + "TABLE: name %s, id %llu, flags %lx, columns %lu," " indexes %lu, appr.rows %lu\n" " COLUMNS: ", table->name, - (ulong) ut_dulint_get_high(table->id), - (ulong) ut_dulint_get_low(table->id), + (ullint) table->id, (ulong) table->flags, (ulong) table->n_cols, (ulong) UT_LIST_GET_LEN(table->indexes), @@ -4470,14 +4468,13 @@ dict_index_print_low( } fprintf(stderr, - " INDEX: name %s, id %lu %lu, fields %lu/%lu," + " INDEX: name %s, id %llu, fields %lu/%lu," " uniq %lu, type %lu\n" " root page %lu, appr.key vals %lu," " leaf pages %lu, size pages %lu\n" " FIELDS: ", index->name, - (ulong) ut_dulint_get_high(index->id), - (ulong) ut_dulint_get_low(index->id), + (ullint) index->id, (ulong) index->n_user_defined_cols, (ulong) index->n_fields, (ulong) index->n_uniq, @@ -4849,8 +4846,7 @@ dict_table_get_index_on_name_and_min_id( while (index != NULL) { if (ut_strcmp(index->name, name) == 0) { - if (!min_index - || ut_dulint_cmp(index->id, min_index->id) < 0) { + if (!min_index || index->id < min_index->id) { min_index = index; } diff --git a/storage/innobase/dict/dict0load.c b/storage/innobase/dict/dict0load.c index b998b157d19..20a18c72a39 100644 --- a/storage/innobase/dict/dict0load.c +++ b/storage/innobase/dict/dict0load.c @@ -364,7 +364,7 @@ dict_process_sys_indexes_rec( mem_heap_t* heap, /*!< in/out: heap memory */ const rec_t* rec, /*!< in: current SYS_INDEXES rec */ dict_index_t* index, /*!< out: index to be filled */ - dulint* table_id) /*!< out: index table id */ + table_id_t* table_id) /*!< out: index table id */ { const char* err_msg; byte* buf; @@ -390,7 +390,7 @@ dict_process_sys_columns_rec( mem_heap_t* heap, /*!< in/out: heap memory */ const rec_t* rec, /*!< in: current SYS_COLUMNS rec */ dict_col_t* column, /*!< out: dict_col_t to be filled */ - dulint* table_id, /*!< out: table id */ + table_id_t* table_id, /*!< out: table id */ const char** col_name) /*!< out: column name */ { const char* err_msg; @@ -414,8 +414,8 @@ dict_process_sys_fields_rec( dict_field_t* sys_field, /*!< out: dict_field_t to be filled */ ulint* pos, /*!< out: Field position */ - dulint* index_id, /*!< out: current index id */ - dulint last_id) /*!< in: previous index id */ + index_id_t* index_id, /*!< out: current index id */ + index_id_t last_id) /*!< in: previous index id */ { byte* buf; byte* last_index_id; @@ -789,7 +789,7 @@ dict_load_column_low( for temporary storage */ dict_col_t* column, /*!< out: dict_column_t to fill, or NULL if table != NULL */ - dulint* table_id, /*!< out: table id */ + table_id_t* table_id, /*!< out: table id */ const char** col_name, /*!< out: column name */ const rec_t* rec) /*!< in: SYS_COLUMNS record */ { @@ -819,8 +819,7 @@ err_len: if (table_id) { *table_id = mach_read_from_8(field); - } else if (UNIV_UNLIKELY(ut_dulint_cmp(table->id, - mach_read_from_8(field)))) { + } else if (UNIV_UNLIKELY(table->id != mach_read_from_8(field))) { return("SYS_COLUMNS.TABLE_ID mismatch"); } @@ -1199,7 +1198,7 @@ dict_load_index_low( ulint len; ulint name_len; char* name_buf; - dulint id; + index_id_t id; ulint n_fields; ulint type; ulint space; @@ -1316,19 +1315,11 @@ dict_load_indexes( dfield_t* dfield; const rec_t* rec; byte* buf; - ibool is_sys_table; mtr_t mtr; ulint error = DB_SUCCESS; ut_ad(mutex_own(&(dict_sys->mutex))); - if ((ut_dulint_get_high(table->id) == 0) - && (ut_dulint_get_low(table->id) < DICT_HDR_FIRST_ID)) { - is_sys_table = TRUE; - } else { - is_sys_table = FALSE; - } - mtr_start(&mtr); sys_indexes = dict_table_get_low("SYS_INDEXES"); @@ -1414,7 +1405,7 @@ corrupted: " is not clustered!\n", stderr); goto corrupted; - } else if (is_sys_table + } else if (table->id < DICT_HDR_FIRST_ID && (dict_index_is_clust(index) || ((table == dict_sys->sys_tables) && !strcmp("ID_IND", index->name)))) { @@ -1774,7 +1765,7 @@ UNIV_INTERN dict_table_t* dict_load_table_on_id( /*==================*/ - dulint table_id) /*!< in: table id */ + table_id_t table_id) /*!< in: table id */ { byte id_buf[8]; btr_pcur_t pcur; @@ -1837,7 +1828,7 @@ dict_load_table_on_id( ut_ad(len == 8); /* Check if the table id in record is the one searched for */ - if (ut_dulint_cmp(table_id, mach_read_from_8(field)) != 0) { + if (table_id != mach_read_from_8(field)) { btr_pcur_close(&pcur); mtr_commit(&mtr); diff --git a/storage/innobase/fil/fil0fil.c b/storage/innobase/fil/fil0fil.c index 1fbbebb748f..710f0ac8603 100644 --- a/storage/innobase/fil/fil0fil.c +++ b/storage/innobase/fil/fil0fil.c @@ -1705,7 +1705,7 @@ fil_write_lsn_and_arch_no_to_file( fil_read(TRUE, 0, 0, sum_of_sizes, 0, UNIV_PAGE_SIZE, buf, NULL); - mach_write_ull(buf + FIL_PAGE_FILE_FLUSH_LSN, lsn); + mach_write_to_8(buf + FIL_PAGE_FILE_FLUSH_LSN, lsn); fil_write(TRUE, 0, 0, sum_of_sizes, 0, UNIV_PAGE_SIZE, buf, NULL); @@ -1799,7 +1799,7 @@ fil_read_flushed_lsn_and_arch_log_no( os_file_read(data_file, buf, 0, 0, UNIV_PAGE_SIZE); - flushed_lsn = mach_read_ull(buf + FIL_PAGE_FILE_FLUSH_LSN); + flushed_lsn = mach_read_from_8(buf + FIL_PAGE_FILE_FLUSH_LSN); ut_free(buf2); @@ -2850,7 +2850,7 @@ fil_reset_too_high_lsns( /* We have to read the file flush lsn from the header of the file */ - flush_lsn = mach_read_ull(page + FIL_PAGE_FILE_FLUSH_LSN); + flush_lsn = mach_read_from_8(page + FIL_PAGE_FILE_FLUSH_LSN); if (current_lsn >= flush_lsn) { /* Ok */ @@ -2898,7 +2898,7 @@ fil_reset_too_high_lsns( goto func_exit; } - if (mach_read_ull(page + FIL_PAGE_LSN) > current_lsn) { + if (mach_read_from_8(page + FIL_PAGE_LSN) > current_lsn) { /* We have to reset the lsn */ if (zip_size) { @@ -2940,7 +2940,7 @@ fil_reset_too_high_lsns( goto func_exit; } - mach_write_ull(page + FIL_PAGE_FILE_FLUSH_LSN, current_lsn); + mach_write_to_8(page + FIL_PAGE_FILE_FLUSH_LSN, current_lsn); success = os_file_write(filepath, file, page, 0, 0, zip_size ? zip_size : UNIV_PAGE_SIZE); diff --git a/storage/innobase/fsp/fsp0fsp.c b/storage/innobase/fsp/fsp0fsp.c index 2bae8481d20..3efe147b998 100644 --- a/storage/innobase/fsp/fsp0fsp.c +++ b/storage/innobase/fsp/fsp0fsp.c @@ -127,9 +127,8 @@ typedef byte fseg_inode_t; #define FSEG_ARR_OFFSET (FSEG_PAGE_DATA + FLST_NODE_SIZE) /*-------------------------------------*/ -#define FSEG_ID 0 /* 8 bytes of segment id: if this is - ut_dulint_zero, it means that the - header is unused */ +#define FSEG_ID 0 /* 8 bytes of segment id: if this is 0, + it means that the header is unused */ #define FSEG_NOT_FULL_N_USED 8 /* number of used segment pages in the FSEG_NOT_FULL list */ @@ -999,11 +998,11 @@ fsp_header_init( flst_init(header + FSP_SEG_INODES_FULL, mtr); flst_init(header + FSP_SEG_INODES_FREE, mtr); - mlog_write_dulint(header + FSP_SEG_ID, ut_dulint_create(0, 1), mtr); + mlog_write_ull(header + FSP_SEG_ID, 1, mtr); if (space == 0) { fsp_fill_free_list(FALSE, space, header, mtr); btr_create(DICT_CLUSTERED | DICT_UNIVERSAL | DICT_IBUF, - 0, 0, ut_dulint_add(DICT_IBUF_ID_MIN, space), + 0, 0, DICT_IBUF_ID_MIN + space, dict_ind_redundant, mtr); } else { fsp_fill_free_list(TRUE, space, header, mtr); @@ -1841,7 +1840,7 @@ fsp_seg_inode_page_find_used( inode = fsp_seg_inode_page_get_nth_inode( page, i, zip_size, mtr); - if (!ut_dulint_is_zero(mach_read_from_8(inode + FSEG_ID))) { + if (mach_read_from_8(inode + FSEG_ID)) { /* This is used */ ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N) @@ -1872,7 +1871,7 @@ fsp_seg_inode_page_find_free( inode = fsp_seg_inode_page_get_nth_inode( page, i, zip_size, mtr); - if (ut_dulint_is_zero(mach_read_from_8(inode + FSEG_ID))) { + if (!mach_read_from_8(inode + FSEG_ID)) { /* This is unused */ return(i); @@ -1931,7 +1930,7 @@ fsp_alloc_seg_inode_page( inode = fsp_seg_inode_page_get_nth_inode(page, i, zip_size, mtr); - mlog_write_dulint(inode + FSEG_ID, ut_dulint_zero, mtr); + mlog_write_ull(inode + FSEG_ID, 0, mtr); } flst_add_last(space_header + FSP_SEG_INODES_FREE, @@ -1998,7 +1997,7 @@ fsp_alloc_seg_inode( page + FSEG_INODE_PAGE_NODE, mtr); } - ut_ad(ut_dulint_is_zero(mach_read_from_8(inode + FSEG_ID)) + ut_ad(!mach_read_from_8(inode + FSEG_ID) || mach_read_from_4(inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE); return(inode); } @@ -2036,7 +2035,7 @@ fsp_free_seg_inode( page + FSEG_INODE_PAGE_NODE, mtr); } - mlog_write_dulint(inode + FSEG_ID, ut_dulint_zero, mtr); + mlog_write_ull(inode + FSEG_ID, 0, mtr); mlog_write_ulint(inode + FSEG_MAGIC_N, 0xfa051ce3, MLOG_4BYTES, mtr); if (ULINT_UNDEFINED @@ -2073,8 +2072,7 @@ fseg_inode_try_get( inode = fut_get_ptr(space, zip_size, inode_addr, RW_X_LATCH, mtr); - if (UNIV_UNLIKELY - (ut_dulint_is_zero(mach_read_from_8(inode + FSEG_ID)))) { + if (UNIV_UNLIKELY(!mach_read_from_8(inode + FSEG_ID))) { inode = NULL; } else { @@ -2249,7 +2247,7 @@ fseg_create_general( ulint zip_size; fsp_header_t* space_header; fseg_inode_t* inode; - dulint seg_id; + ib_id_t seg_id; buf_block_t* block = 0; /* remove warning */ fseg_header_t* header = 0; /* remove warning */ rw_lock_t* latch; @@ -2303,12 +2301,11 @@ fseg_create_general( /* Read the next segment id from space header and increment the value in space header */ - seg_id = mtr_read_dulint(space_header + FSP_SEG_ID, mtr); + seg_id = mach_read_from_8(space_header + FSP_SEG_ID); - mlog_write_dulint(space_header + FSP_SEG_ID, ut_dulint_add(seg_id, 1), - mtr); + mlog_write_ull(space_header + FSP_SEG_ID, seg_id + 1, mtr); - mlog_write_dulint(inode + FSEG_ID, seg_id, mtr); + mlog_write_ull(inode + FSEG_ID, seg_id, mtr); mlog_write_ulint(inode + FSEG_NOT_FULL_N_USED, 0, MLOG_4BYTES, mtr); flst_init(inode + FSEG_FREE, mtr); @@ -2460,7 +2457,7 @@ fseg_fill_free_list( { xdes_t* descr; ulint i; - dulint seg_id; + ib_id_t seg_id; ulint reserved; ulint used; @@ -2497,10 +2494,10 @@ fseg_fill_free_list( xdes_set_state(descr, XDES_FSEG, mtr); - seg_id = mtr_read_dulint(inode + FSEG_ID, mtr); + seg_id = mach_read_from_8(inode + FSEG_ID); ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE); - mlog_write_dulint(descr + XDES_ID, seg_id, mtr); + mlog_write_ull(descr + XDES_ID, seg_id, mtr); flst_add_last(inode + FSEG_FREE, descr + XDES_FLST_NODE, mtr); hint += FSP_EXTENT_SIZE; @@ -2524,7 +2521,7 @@ fseg_alloc_free_extent( mtr_t* mtr) /*!< in: mtr */ { xdes_t* descr; - dulint seg_id; + ib_id_t seg_id; fil_addr_t first; ut_ad(!((page_offset(inode) - FSEG_ARR_OFFSET) % FSEG_INODE_SIZE)); @@ -2545,10 +2542,10 @@ fseg_alloc_free_extent( return(NULL); } - seg_id = mtr_read_dulint(inode + FSEG_ID, mtr); + seg_id = mach_read_from_8(inode + FSEG_ID); xdes_set_state(descr, XDES_FSEG, mtr); - mlog_write_dulint(descr + XDES_ID, seg_id, mtr); + mlog_write_ull(descr + XDES_ID, seg_id, mtr); flst_add_last(inode + FSEG_FREE, descr + XDES_FLST_NODE, mtr); /* Try to fill the segment free list */ @@ -2583,7 +2580,7 @@ fseg_alloc_free_page_low( { fsp_header_t* space_header; ulint space_size; - dulint seg_id; + ib_id_t seg_id; ulint used; ulint reserved; xdes_t* descr; /*!< extent of the hinted page */ @@ -2599,9 +2596,9 @@ 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)); - seg_id = mtr_read_dulint(seg_inode + FSEG_ID, mtr); + seg_id = mach_read_from_8(seg_inode + FSEG_ID); - ut_ad(!ut_dulint_is_zero(seg_id)); + ut_ad(seg_id); reserved = fseg_n_reserved_pages_low(seg_inode, &used, mtr); @@ -2619,8 +2616,7 @@ fseg_alloc_free_page_low( /* In the big if-else below we look for ret_page and ret_descr */ /*-------------------------------------------------------------*/ if ((xdes_get_state(descr, mtr) == XDES_FSEG) - && (0 == ut_dulint_cmp(mtr_read_dulint(descr + XDES_ID, - mtr), seg_id)) + && mach_read_from_8(descr + XDES_ID) == seg_id && (xdes_get_bit(descr, XDES_FREE_BIT, hint % FSP_EXTENT_SIZE, mtr) == TRUE)) { @@ -2642,7 +2638,7 @@ fseg_alloc_free_page_low( ut_a(ret_descr == descr); xdes_set_state(ret_descr, XDES_FSEG, mtr); - mlog_write_dulint(ret_descr + XDES_ID, seg_id, mtr); + mlog_write_ull(ret_descr + XDES_ID, seg_id, mtr); flst_add_last(seg_inode + FSEG_FREE, ret_descr + XDES_FLST_NODE, mtr); @@ -2671,8 +2667,7 @@ fseg_alloc_free_page_low( } /*-----------------------------------------------------------*/ } else if ((xdes_get_state(descr, mtr) == XDES_FSEG) - && (0 == ut_dulint_cmp(mtr_read_dulint(descr + XDES_ID, - mtr), seg_id)) + && mach_read_from_8(descr + XDES_ID) == seg_id && (!xdes_is_full(descr, mtr))) { /* 4. We can take the page from the same extent as the @@ -3243,8 +3238,8 @@ fseg_free_page_low( xdes_t* descr; ulint not_full_n_used; ulint state; - dulint descr_id; - dulint seg_id; + ib_id_t descr_id; + ib_id_t seg_id; ulint i; ut_ad(seg_inode && mtr); @@ -3303,20 +3298,18 @@ crash: /* If we get here, the page is in some extent of the segment */ - descr_id = mtr_read_dulint(descr + XDES_ID, mtr); - seg_id = mtr_read_dulint(seg_inode + FSEG_ID, mtr); + descr_id = mach_read_from_8(descr + XDES_ID); + seg_id = mach_read_from_8(seg_inode + FSEG_ID); #if 0 fprintf(stderr, "InnoDB: InnoDB is freeing space %lu page %lu,\n" - "InnoDB: which belongs to descr seg %lu %lu\n" - "InnoDB: segment %lu %lu.\n", + "InnoDB: which belongs to descr seg %llu\n" + "InnoDB: segment %llu.\n", (ulong) space, (ulong) page, - (ulong) ut_dulint_get_high(descr_id), - (ulong) ut_dulint_get_low(descr_id), - (ulong) ut_dulint_get_high(seg_id), - (ulong) ut_dulint_get_low(seg_id)); + (ullint) descr_id, + (ullint) seg_id); #endif /* 0 */ - if (0 != ut_dulint_cmp(descr_id, seg_id)) { + if (UNIV_UNLIKELY(descr_id != seg_id)) { fputs("InnoDB: Dump of the tablespace extent descriptor: ", stderr); ut_print_buf(stderr, descr, 40); @@ -3328,13 +3321,11 @@ crash: "InnoDB: Serious error: InnoDB is trying to" " free space %lu page %lu,\n" "InnoDB: which does not belong to" - " segment %lu %lu but belongs\n" - "InnoDB: to segment %lu %lu.\n", + " segment %llu but belongs\n" + "InnoDB: to segment %llu.\n", (ulong) space, (ulong) page, - (ulong) ut_dulint_get_high(descr_id), - (ulong) ut_dulint_get_low(descr_id), - (ulong) ut_dulint_get_high(seg_id), - (ulong) ut_dulint_get_low(seg_id)); + (ullint) descr_id, + (ullint) seg_id); goto crash; } @@ -3423,8 +3414,7 @@ fseg_free_extent( descr = xdes_get_descriptor(space, zip_size, page, mtr); ut_a(xdes_get_state(descr, mtr) == XDES_FSEG); - ut_a(0 == ut_dulint_cmp(mtr_read_dulint(descr + XDES_ID, mtr), - mtr_read_dulint(seg_inode + FSEG_ID, mtr))); + ut_a(!memcmp(descr + XDES_ID, seg_inode + FSEG_ID, 8)); ut_ad(mach_read_from_4(seg_inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE); @@ -3684,7 +3674,7 @@ fseg_validate_low( mtr_t* mtr2) /*!< in: mtr */ { ulint space; - dulint seg_id; + ib_id_t seg_id; mtr_t mtr; xdes_t* descr; fil_addr_t node_addr; @@ -3696,7 +3686,7 @@ fseg_validate_low( space = page_get_space_id(page_align(inode)); - seg_id = mtr_read_dulint(inode + FSEG_ID, mtr2); + seg_id = mach_read_from_8(inode + FSEG_ID); n_used = mtr_read_ulint(inode + FSEG_NOT_FULL_N_USED, MLOG_4BYTES, mtr2); flst_validate(inode + FSEG_FREE, mtr2); @@ -3719,8 +3709,7 @@ fseg_validate_low( ut_a(xdes_get_n_used(descr, &mtr) == 0); ut_a(xdes_get_state(descr, &mtr) == XDES_FSEG); - ut_a(!ut_dulint_cmp(mtr_read_dulint(descr + XDES_ID, &mtr), - seg_id)); + ut_a(mach_read_from_8(descr + XDES_ID) == seg_id); node_addr = flst_get_next_addr(descr + XDES_FLST_NODE, &mtr); mtr_commit(&mtr); @@ -3744,8 +3733,7 @@ fseg_validate_low( ut_a(xdes_get_n_used(descr, &mtr) > 0); ut_a(xdes_get_n_used(descr, &mtr) < FSP_EXTENT_SIZE); ut_a(xdes_get_state(descr, &mtr) == XDES_FSEG); - ut_a(!ut_dulint_cmp(mtr_read_dulint(descr + XDES_ID, &mtr), - seg_id)); + ut_a(mach_read_from_8(descr + XDES_ID) == seg_id); n_used2 += xdes_get_n_used(descr, &mtr); @@ -3770,8 +3758,7 @@ fseg_validate_low( ut_a(xdes_get_n_used(descr, &mtr) == FSP_EXTENT_SIZE); ut_a(xdes_get_state(descr, &mtr) == XDES_FSEG); - ut_a(!ut_dulint_cmp(mtr_read_dulint(descr + XDES_ID, &mtr), - seg_id)); + ut_a(mach_read_from_8(descr + XDES_ID) == seg_id); node_addr = flst_get_next_addr(descr + XDES_FLST_NODE, &mtr); mtr_commit(&mtr); @@ -3822,8 +3809,6 @@ fseg_print_low( mtr_t* mtr) /*!< in: mtr */ { ulint space; - ulint seg_id_low; - ulint seg_id_high; ulint n_used; ulint n_frag; ulint n_free; @@ -3832,7 +3817,7 @@ fseg_print_low( ulint reserved; ulint used; ulint page_no; - dulint d_var; + ib_id_t seg_id; ut_ad(mtr_memo_contains_page(mtr, inode, MTR_MEMO_PAGE_X_FIX)); space = page_get_space_id(page_align(inode)); @@ -3840,10 +3825,7 @@ fseg_print_low( reserved = fseg_n_reserved_pages_low(inode, &used, mtr); - d_var = mtr_read_dulint(inode + FSEG_ID, mtr); - - seg_id_low = ut_dulint_get_low(d_var); - seg_id_high = ut_dulint_get_high(d_var); + seg_id = mach_read_from_8(inode + FSEG_ID); n_used = mtr_read_ulint(inode + FSEG_NOT_FULL_N_USED, MLOG_4BYTES, mtr); @@ -3853,11 +3835,11 @@ fseg_print_low( n_full = flst_get_len(inode + FSEG_FULL, mtr); fprintf(stderr, - "SEGMENT id %lu %lu space %lu; page %lu;" + "SEGMENT id %llu space %lu; page %lu;" " res %lu used %lu; full ext %lu\n" "fragm pages %lu; free extents %lu;" " not full extents %lu: pages %lu\n", - (ulong) seg_id_high, (ulong) seg_id_low, + (ullint) seg_id, (ulong) space, (ulong) page_no, (ulong) reserved, (ulong) used, (ulong) n_full, (ulong) n_frag, (ulong) n_free, (ulong) n_not_full, @@ -4059,8 +4041,7 @@ fsp_validate( seg_inode = fsp_seg_inode_page_get_nth_inode( seg_inode_page, n, zip_size, &mtr); - ut_a(!ut_dulint_is_zero( - mach_read_from_8(seg_inode + FSEG_ID))); + ut_a(mach_read_from_8(seg_inode + FSEG_ID) != 0); fseg_validate_low(seg_inode, &mtr); descr_count += flst_get_len(seg_inode + FSEG_FREE, @@ -4105,8 +4086,7 @@ fsp_validate( seg_inode = fsp_seg_inode_page_get_nth_inode( seg_inode_page, n, zip_size, &mtr); - if (!ut_dulint_is_zero( - mach_read_from_8(seg_inode + FSEG_ID))) { + if (mach_read_from_8(seg_inode + FSEG_ID)) { fseg_validate_low(seg_inode, &mtr); descr_count += flst_get_len( @@ -4168,11 +4148,9 @@ fsp_print( ulint n_free; ulint n_free_frag; ulint n_full_frag; - ulint seg_id_low; - ulint seg_id_high; + ib_id_t seg_id; ulint n; ulint n_segs = 0; - dulint d_var; mtr_t mtr; mtr_t mtr2; @@ -4202,21 +4180,18 @@ fsp_print( n_free_frag = flst_get_len(header + FSP_FREE_FRAG, &mtr); n_full_frag = flst_get_len(header + FSP_FULL_FRAG, &mtr); - d_var = mtr_read_dulint(header + FSP_SEG_ID, &mtr); - - seg_id_low = ut_dulint_get_low(d_var); - seg_id_high = ut_dulint_get_high(d_var); + seg_id = mach_read_from_8(header + FSP_SEG_ID); fprintf(stderr, "FILE SPACE INFO: id %lu\n" "size %lu, free limit %lu, free extents %lu\n" "not full frag extents %lu: used pages %lu," " full frag extents %lu\n" - "first seg id not used %lu %lu\n", + "first seg id not used %llu\n", (ulong) space, (ulong) size, (ulong) free_limit, (ulong) n_free, (ulong) n_free_frag, (ulong) frag_n_used, (ulong) n_full_frag, - (ulong) seg_id_high, (ulong) seg_id_low); + (ullint) seg_id); mtr_commit(&mtr); @@ -4246,8 +4221,7 @@ fsp_print( seg_inode = fsp_seg_inode_page_get_nth_inode( seg_inode_page, n, zip_size, &mtr); - ut_a(!ut_dulint_is_zero( - mach_read_from_8(seg_inode + FSEG_ID))); + ut_a(mach_read_from_8(seg_inode + FSEG_ID) != 0); fseg_print_low(seg_inode, &mtr); n_segs++; @@ -4284,8 +4258,7 @@ fsp_print( seg_inode = fsp_seg_inode_page_get_nth_inode( seg_inode_page, n, zip_size, &mtr); - if (!ut_dulint_is_zero( - mach_read_from_8(seg_inode + FSEG_ID))) { + if (mach_read_from_8(seg_inode + FSEG_ID)) { fseg_print_low(seg_inode, &mtr); n_segs++; diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 43f53bae310..9868889cbde 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -2967,9 +2967,9 @@ innobase_close_connection( global_system_variables.log_warnings) { sql_print_warning( "MySQL is closing a connection that has an active " - "InnoDB transaction. %lu row modifications will " + "InnoDB transaction. %llu row modifications will " "roll back.", - (ulong) trx->undo_no.low); + (ullint) trx->undo_no); } innobase_rollback_trx(trx); diff --git a/storage/innobase/handler/i_s.cc b/storage/innobase/handler/i_s.cc index 2c15a3b87db..06860c21b37 100644 --- a/storage/innobase/handler/i_s.cc +++ b/storage/innobase/handler/i_s.cc @@ -1973,16 +1973,13 @@ i_s_dict_fill_sys_tables( dict_table_t* table, /*!< in: table */ TABLE* table_to_fill) /*!< in/out: fill this table */ { - longlong table_id; Field** fields; DBUG_ENTER("i_s_dict_fill_sys_tables"); fields = table_to_fill->field; - table_id = ut_conv_dulint_to_longlong(table->id); - - OK(fields[SYS_TABLE_ID]->store(table_id, TRUE)); + OK(fields[SYS_TABLE_ID]->store(longlong(table->id), TRUE)); OK(field_store_string(fields[SYS_TABLE_NAME], table->name)); @@ -2238,16 +2235,13 @@ i_s_dict_fill_sys_tablestats( dict_table_t* table, /*!< in: table */ TABLE* table_to_fill) /*!< in/out: fill this table */ { - longlong table_id; Field** fields; DBUG_ENTER("i_s_dict_fill_sys_tablestats"); fields = table_to_fill->field; - table_id = ut_conv_dulint_to_longlong(table->id); - - OK(fields[SYS_TABLESTATS_ID]->store(table_id, TRUE)); + OK(fields[SYS_TABLESTATS_ID]->store(longlong(table->id), TRUE)); OK(field_store_string(fields[SYS_TABLESTATS_NAME], table->name)); @@ -2495,27 +2489,22 @@ int i_s_dict_fill_sys_indexes( /*======================*/ THD* thd, /*!< in: thread */ - dulint tableid, /*!< in: table id */ + table_id_t table_id, /*!< in: table id */ dict_index_t* index, /*!< in: populated dict_index_t struct with index info */ TABLE* table_to_fill) /*!< in/out: fill this table */ { - longlong table_id; - longlong index_id; Field** fields; DBUG_ENTER("i_s_dict_fill_sys_indexes"); fields = table_to_fill->field; - table_id = ut_conv_dulint_to_longlong(tableid); - index_id = ut_conv_dulint_to_longlong(index->id); - - OK(fields[SYS_INDEX_ID]->store(index_id, TRUE)); + OK(fields[SYS_INDEX_ID]->store(longlong(index->id), TRUE)); OK(field_store_string(fields[SYS_INDEX_NAME], index->name)); - OK(fields[SYS_INDEX_TABLE_ID]->store(table_id, TRUE)); + OK(fields[SYS_INDEX_TABLE_ID]->store(longlong(table_id), TRUE)); OK(fields[SYS_INDEX_TYPE]->store(index->type)); @@ -2564,7 +2553,7 @@ i_s_sys_indexes_fill_table( /* Process each record in the table */ while (rec) { const char* err_msg;; - dulint table_id; + table_id_t table_id; dict_index_t index_rec; /* Populate a dict_index_t structure with information from @@ -2737,22 +2726,19 @@ int i_s_dict_fill_sys_columns( /*======================*/ THD* thd, /*!< in: thread */ - dulint tableid, /*!< in: table ID */ + table_id_t table_id, /*!< in: table ID */ const char* col_name, /*!< in: column name */ dict_col_t* column, /*!< in: dict_col_t struct holding more column information */ TABLE* table_to_fill) /*!< in/out: fill this table */ { - longlong table_id; Field** fields; DBUG_ENTER("i_s_dict_fill_sys_columns"); fields = table_to_fill->field; - table_id = ut_conv_dulint_to_longlong(tableid); - - OK(fields[SYS_COLUMN_TABLE_ID]->store(table_id, TRUE)); + OK(fields[SYS_COLUMN_TABLE_ID]->store(longlong(table_id), TRUE)); OK(field_store_string(fields[SYS_COLUMN_NAME], col_name)); @@ -2803,7 +2789,7 @@ i_s_sys_columns_fill_table( while (rec) { const char* err_msg; dict_col_t column_rec; - dulint table_id; + table_id_t table_id; /* populate a dict_col_t structure with information from a SYS_COLUMNS row */ @@ -2948,21 +2934,18 @@ int i_s_dict_fill_sys_fields( /*=====================*/ THD* thd, /*!< in: thread */ - dulint indexid, /*!< in: index id for the field */ + index_id_t index_id, /*!< in: index id for the field */ dict_field_t* field, /*!< in: table */ ulint pos, /*!< in: Field position */ TABLE* table_to_fill) /*!< in/out: fill this table */ { - longlong index_id; Field** fields; DBUG_ENTER("i_s_dict_fill_sys_fields"); fields = table_to_fill->field; - index_id = ut_conv_dulint_to_longlong(indexid); - - OK(fields[SYS_FIELD_INDEX_ID]->store(index_id, TRUE)); + OK(fields[SYS_FIELD_INDEX_ID]->store(longlong(index_id), TRUE)); OK(field_store_string(fields[SYS_FIELD_NAME], field->name)); @@ -2988,7 +2971,7 @@ i_s_sys_fields_fill_table( btr_pcur_t pcur; const rec_t* rec; mem_heap_t* heap; - dulint last_id; + index_id_t last_id; mtr_t mtr; DBUG_ENTER("i_s_sys_fields_fill_table"); @@ -3005,14 +2988,14 @@ i_s_sys_fields_fill_table( /* will save last index id so that we know whether we move to the next index. This is used to calculate prefix length */ - last_id = ut_dulint_create(0, 0); + last_id = 0; rec = dict_startscan_system(&pcur, &mtr, SYS_FIELDS); while (rec) { ulint pos; const char* err_msg; - dulint index_id; + index_id_t index_id; dict_field_t field_rec; /* Populate a dict_field_t structure with information from diff --git a/storage/innobase/ibuf/ibuf0ibuf.c b/storage/innobase/ibuf/ibuf0ibuf.c index 0397af88ff4..dc8e61e5070 100644 --- a/storage/innobase/ibuf/ibuf0ibuf.c +++ b/storage/innobase/ibuf/ibuf0ibuf.c @@ -565,7 +565,7 @@ ibuf_init_at_db_start(void) dict_mem_table_add_col(table, heap, "DUMMY_COLUMN", DATA_BINARY, 0, 0); - table->id = ut_dulint_add(DICT_IBUF_ID_MIN, IBUF_SPACE_ID); + table->id = DICT_IBUF_ID_MIN + IBUF_SPACE_ID; dict_table_add_to_cache(table, heap); mem_heap_free(heap); @@ -576,7 +576,7 @@ ibuf_init_at_db_start(void) dict_mem_index_add_field(index, "DUMMY_COLUMN", 0); - index->id = ut_dulint_add(DICT_IBUF_ID_MIN, IBUF_SPACE_ID); + index->id = DICT_IBUF_ID_MIN + IBUF_SPACE_ID; error = dict_index_add_to_cache(table, index, FSP_IBUF_TREE_ROOT_PAGE_NO, FALSE); diff --git a/storage/innobase/include/btr0btr.h b/storage/innobase/include/btr0btr.h index cc08cc620c5..3912f315f2a 100644 --- a/storage/innobase/include/btr0btr.h +++ b/storage/innobase/include/btr0btr.h @@ -130,7 +130,7 @@ btr_page_get( Gets the index id field of a page. @return index id */ UNIV_INLINE -dulint +index_id_t btr_page_get_index_id( /*==================*/ const page_t* page); /*!< in: index page */ @@ -226,7 +226,7 @@ btr_create( ulint space, /*!< in: space where created */ ulint zip_size,/*!< in: compressed page size in bytes or 0 for uncompressed pages */ - dulint index_id,/*!< in: index id */ + index_id_t index_id,/*!< in: index id */ dict_index_t* index, /*!< in: index */ mtr_t* mtr); /*!< in: mini-transaction handle */ /************************************************************//** diff --git a/storage/innobase/include/btr0btr.ic b/storage/innobase/include/btr0btr.ic index 97944cc2e26..6c580a0bd6e 100644 --- a/storage/innobase/include/btr0btr.ic +++ b/storage/innobase/include/btr0btr.ic @@ -86,7 +86,7 @@ btr_page_set_index_id( page_t* page, /*!< in: page to be created */ page_zip_des_t* page_zip,/*!< in: compressed page whose uncompressed part will be updated, or NULL */ - dulint id, /*!< in: index id */ + index_id_t id, /*!< in: index id */ mtr_t* mtr) /*!< in: mtr */ { if (UNIV_LIKELY_NULL(page_zip)) { @@ -95,8 +95,7 @@ btr_page_set_index_id( page + (PAGE_HEADER + PAGE_INDEX_ID), 8, mtr); } else { - mlog_write_dulint(page + (PAGE_HEADER + PAGE_INDEX_ID), - id, mtr); + mlog_write_ull(page + (PAGE_HEADER + PAGE_INDEX_ID), id, mtr); } } #endif /* !UNIV_HOTBACKUP */ @@ -105,7 +104,7 @@ btr_page_set_index_id( Gets the index id field of a page. @return index id */ UNIV_INLINE -dulint +index_id_t btr_page_get_index_id( /*==================*/ const page_t* page) /*!< in: index page */ diff --git a/storage/innobase/include/data0data.h b/storage/innobase/include/data0data.h index f9fce3f3657..11e4027777a 100644 --- a/storage/innobase/include/data0data.h +++ b/storage/innobase/include/data0data.h @@ -309,7 +309,7 @@ dtuple_fold( ulint n_fields,/*!< in: number of complete fields to fold */ ulint n_bytes,/*!< in: number of bytes to fold in an incomplete last field */ - dulint tree_id)/*!< in: index tree id */ + index_id_t tree_id)/*!< in: index tree id */ __attribute__((pure)); /*******************************************************************//** Sets types of fields binary in a tuple. */ diff --git a/storage/innobase/include/data0data.ic b/storage/innobase/include/data0data.ic index da79aa33702..2e3adf4b707 100644 --- a/storage/innobase/include/data0data.ic +++ b/storage/innobase/include/data0data.ic @@ -518,7 +518,7 @@ dtuple_fold( ulint n_fields,/*!< in: number of complete fields to fold */ ulint n_bytes,/*!< in: number of bytes to fold in an incomplete last field */ - dulint tree_id)/*!< in: index tree id */ + index_id_t tree_id)/*!< in: index tree id */ { const dfield_t* field; ulint i; @@ -530,7 +530,7 @@ dtuple_fold( ut_ad(tuple->magic_n == DATA_TUPLE_MAGIC_N); ut_ad(dtuple_check_typed(tuple)); - fold = ut_fold_dulint(tree_id); + fold = ut_fold_ull(tree_id); for (i = 0; i < n_fields; i++) { field = dtuple_get_nth_field(tuple, i); diff --git a/storage/innobase/include/data0type.h b/storage/innobase/include/data0type.h index a73bed3a9f5..1a9c7bbd7ba 100644 --- a/storage/innobase/include/data0type.h +++ b/storage/innobase/include/data0type.h @@ -128,7 +128,7 @@ columns, and for them the precise type is usually not used at all. /* Precise data types for system columns and the length of those columns; NOTE: the values must run from 0 up in the order given! All codes must be less than 256 */ -#define DATA_ROW_ID 0 /* row id: a dulint */ +#define DATA_ROW_ID 0 /* row id: a 48-bit integer */ #define DATA_ROW_ID_LEN 6 /* stored length for row id */ #define DATA_TRX_ID 1 /* transaction id: 6 bytes */ diff --git a/storage/innobase/include/dict0boot.h b/storage/innobase/include/dict0boot.h index 148b5cbe250..22df826da65 100644 --- a/storage/innobase/include/dict0boot.h +++ b/storage/innobase/include/dict0boot.h @@ -51,32 +51,35 @@ UNIV_INTERN void dict_hdr_get_new_id( /*================*/ - dulint* table_id, /*!< out: table id (not assigned if NULL) */ - dulint* index_id, /*!< out: index id (not assigned if NULL) */ - ulint* space_id); /*!< out: space id (not assigned if NULL) */ + table_id_t* table_id, /*!< out: table id + (not assigned if NULL) */ + index_id_t* index_id, /*!< out: index id + (not assigned if NULL) */ + ulint* space_id); /*!< out: space id + (not assigned if NULL) */ /**********************************************************************//** Returns a new row id. @return the new id */ UNIV_INLINE -dulint +row_id_t dict_sys_get_new_row_id(void); /*=========================*/ /**********************************************************************//** Reads a row id from a record or other 6-byte stored form. @return row id */ UNIV_INLINE -dulint +row_id_t dict_sys_read_row_id( /*=================*/ - byte* field); /*!< in: record field */ + const byte* field); /*!< in: record field */ /**********************************************************************//** Writes a row id to a record or other 6-byte stored form. */ UNIV_INLINE void dict_sys_write_row_id( /*==================*/ - byte* field, /*!< in: record field */ - dulint row_id);/*!< in: row id */ + byte* field, /*!< in: record field */ + row_id_t row_id);/*!< in: row id */ /*****************************************************************//** Initializes the data dictionary memory structures when the database is started. This function is also called when the data dictionary is created. */ @@ -97,12 +100,12 @@ dict_create(void); #define DICT_HDR_PAGE_NO FSP_DICT_HDR_PAGE_NO /* The ids for the basic system tables and their indexes */ -#define DICT_TABLES_ID ut_dulint_create(0, 1) -#define DICT_COLUMNS_ID ut_dulint_create(0, 2) -#define DICT_INDEXES_ID ut_dulint_create(0, 3) -#define DICT_FIELDS_ID ut_dulint_create(0, 4) +#define DICT_TABLES_ID 1 +#define DICT_COLUMNS_ID 2 +#define DICT_INDEXES_ID 3 +#define DICT_FIELDS_ID 4 /* The following is a secondary index on SYS_TABLES */ -#define DICT_TABLE_IDS_ID ut_dulint_create(0, 5) +#define DICT_TABLE_IDS_ID 5 #define DICT_HDR_FIRST_ID 10 /* the ids for tables etc. start from this number, except for basic @@ -110,7 +113,7 @@ dict_create(void); indexes; ibuf tables and indexes are assigned as the id the number DICT_IBUF_ID_MIN plus the space id */ -#define DICT_IBUF_ID_MIN ut_dulint_create(0xFFFFFFFFUL, 0) +#define DICT_IBUF_ID_MIN 0xFFFFFFFF00000000ULL /* The offset of the dictionary header on the page */ #define DICT_HDR FSEG_PAGE_DATA diff --git a/storage/innobase/include/dict0boot.ic b/storage/innobase/include/dict0boot.ic index d5f372e38c4..d3ba9eee78f 100644 --- a/storage/innobase/include/dict0boot.ic +++ b/storage/innobase/include/dict0boot.ic @@ -36,22 +36,22 @@ dict_hdr_flush_row_id(void); Returns a new row id. @return the new id */ UNIV_INLINE -dulint +row_id_t dict_sys_get_new_row_id(void) /*=========================*/ { - dulint id; + row_id_t id; mutex_enter(&(dict_sys->mutex)); id = dict_sys->row_id; - if (0 == (ut_dulint_get_low(id) % DICT_HDR_ROW_ID_WRITE_MARGIN)) { + if (0 == (id % DICT_HDR_ROW_ID_WRITE_MARGIN)) { dict_hdr_flush_row_id(); } - UT_DULINT_INC(dict_sys->row_id); + dict_sys->row_id++; mutex_exit(&(dict_sys->mutex)); @@ -62,10 +62,10 @@ dict_sys_get_new_row_id(void) Reads a row id from a record or other 6-byte stored form. @return row id */ UNIV_INLINE -dulint +row_id_t dict_sys_read_row_id( /*=================*/ - byte* field) /*!< in: record field */ + const byte* field) /*!< in: record field */ { #if DATA_ROW_ID_LEN != 6 # error "DATA_ROW_ID_LEN != 6" @@ -80,8 +80,8 @@ UNIV_INLINE void dict_sys_write_row_id( /*==================*/ - byte* field, /*!< in: record field */ - dulint row_id) /*!< in: row id */ + byte* field, /*!< in: record field */ + row_id_t row_id) /*!< in: row id */ { #if DATA_ROW_ID_LEN != 6 # error "DATA_ROW_ID_LEN != 6" diff --git a/storage/innobase/include/dict0dict.h b/storage/innobase/include/dict0dict.h index 3a1bee4cd89..28fa12e4678 100644 --- a/storage/innobase/include/dict0dict.h +++ b/storage/innobase/include/dict0dict.h @@ -75,8 +75,8 @@ UNIV_INTERN dict_table_t* dict_table_get_on_id( /*=================*/ - dulint table_id, /*!< in: table id */ - trx_t* trx); /*!< in: transaction handle */ + table_id_t table_id, /*!< in: table id */ + trx_t* trx); /*!< in: transaction handle */ /********************************************************************//** Decrements the count of open MySQL handles to a table. */ UNIV_INTERN @@ -277,7 +277,7 @@ void dict_table_change_id_in_cache( /*==========================*/ dict_table_t* table, /*!< in/out: table object already in cache */ - dulint new_id);/*!< in: new id to set */ + table_id_t new_id);/*!< in: new id to set */ /**********************************************************************//** Adds a foreign key constraint object to the dictionary cache. May free the object if there already is an object with the same identifier in. @@ -397,7 +397,7 @@ dict_index_t* dict_index_get_on_id_low( /*=====================*/ dict_table_t* table, /*!< in: table */ - dulint index_id); /*!< in: index id */ + index_id_t index_id); /*!< in: index id */ /**********************************************************************//** Checks if a table is in the dictionary cache. @return table, NULL if not found */ @@ -423,7 +423,7 @@ UNIV_INLINE dict_table_t* dict_table_get_on_id_low( /*=====================*/ - dulint table_id); /*!< in: table id */ + table_id_t table_id); /*!< in: table id */ /**********************************************************************//** Find an index that is equivalent to the one passed in and is not marked for deletion. @@ -710,7 +710,7 @@ UNIV_INTERN dict_index_t* dict_index_find_on_id_low( /*======================*/ - dulint id); /*!< in: index id */ + index_id_t id); /*!< in: index id */ /**********************************************************************//** Adds an index to the dictionary cache. @return DB_SUCCESS, DB_TOO_BIG_RECORD, or DB_CORRUPTION */ @@ -901,7 +901,7 @@ UNIV_INTERN dict_index_t* dict_index_get_if_in_cache_low( /*===========================*/ - dulint index_id); /*!< in: index id */ + index_id_t index_id); /*!< in: index id */ #if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG /**********************************************************************//** Returns an index object if it is found in the dictionary cache. @@ -910,7 +910,7 @@ UNIV_INTERN dict_index_t* dict_index_get_if_in_cache( /*=======================*/ - dulint index_id); /*!< in: index id */ + index_id_t index_id); /*!< in: index id */ #endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */ #ifdef UNIV_DEBUG /**********************************************************************//** @@ -1135,7 +1135,7 @@ struct dict_sys_struct{ and DROP TABLE, as well as reading the dictionary data for a table from system tables */ - dulint row_id; /*!< the next row id to assign; + row_id_t row_id; /*!< the next row id to assign; NOTE that at a checkpoint this must be written to the dict system header and flushed to a file; in diff --git a/storage/innobase/include/dict0dict.ic b/storage/innobase/include/dict0dict.ic index 93c3f8d4733..b118df87f8b 100644 --- a/storage/innobase/include/dict0dict.ic +++ b/storage/innobase/include/dict0dict.ic @@ -780,7 +780,7 @@ UNIV_INLINE dict_table_t* dict_table_get_on_id_low( /*=====================*/ - dulint table_id) /*!< in: table id */ + table_id_t table_id) /*!< in: table id */ { dict_table_t* table; ulint fold; @@ -788,11 +788,11 @@ dict_table_get_on_id_low( ut_ad(mutex_own(&(dict_sys->mutex))); /* Look for the table name in the hash table */ - fold = ut_fold_dulint(table_id); + fold = ut_fold_ull(table_id); HASH_SEARCH(id_hash, dict_sys->table_id_hash, fold, dict_table_t*, table, ut_ad(table->cached), - !ut_dulint_cmp(table->id, table_id)); + table->id == table_id); if (table == NULL) { table = dict_load_table_on_id(table_id); } diff --git a/storage/innobase/include/dict0load.h b/storage/innobase/include/dict0load.h index 461099efeba..6a718a464ab 100644 --- a/storage/innobase/include/dict0load.h +++ b/storage/innobase/include/dict0load.h @@ -111,7 +111,7 @@ dict_load_column_low( for temporary storage */ dict_col_t* column, /*!< out: dict_column_t to fill, or NULL if table != NULL */ - dulint* table_id, /*!< out: table id */ + table_id_t* table_id, /*!< out: table id */ const char** col_name, /*!< out: column name */ const rec_t* rec); /*!< in: SYS_COLUMNS record */ /********************************************************************//** @@ -174,7 +174,7 @@ UNIV_INTERN dict_table_t* dict_load_table_on_id( /*==================*/ - dulint table_id); /*!< in: table id */ + table_id_t table_id); /*!< in: table id */ /********************************************************************//** This function is called when the database is booted. Loads system table index definitions except for the clustered index which @@ -256,7 +256,7 @@ dict_process_sys_indexes_rec( const rec_t* rec, /*!< in: current SYS_INDEXES rec */ dict_index_t* index, /*!< out: dict_index_t to be filled */ - dulint* table_id); /*!< out: table id */ + table_id_t* table_id); /*!< out: table id */ /********************************************************************//** This function parses a SYS_COLUMNS record and populate a dict_column_t structure with the information from the record. @@ -268,7 +268,7 @@ dict_process_sys_columns_rec( mem_heap_t* heap, /*!< in/out: heap memory */ const rec_t* rec, /*!< in: current SYS_COLUMNS rec */ dict_col_t* column, /*!< out: dict_col_t to be filled */ - dulint* table_id, /*!< out: table id */ + table_id_t* table_id, /*!< out: table id */ const char** col_name); /*!< out: column name */ /********************************************************************//** This function parses a SYS_FIELDS record and populate a dict_field_t @@ -283,8 +283,8 @@ dict_process_sys_fields_rec( dict_field_t* sys_field, /*!< out: dict_field_t to be filled */ ulint* pos, /*!< out: Field position */ - dulint* index_id, /*!< out: current index id */ - dulint last_id); /*!< in: previous index id */ + index_id_t* index_id, /*!< out: current index id */ + index_id_t last_id); /*!< in: previous index id */ /********************************************************************//** This function parses a SYS_FOREIGN record and populate a dict_foreign_t structure with the information from the record. For detail information diff --git a/storage/innobase/include/dict0mem.h b/storage/innobase/include/dict0mem.h index c9a95343e69..286eb408a3b 100644 --- a/storage/innobase/include/dict0mem.h +++ b/storage/innobase/include/dict0mem.h @@ -293,7 +293,7 @@ struct dict_field_struct{ /** Data structure for an index. Most fields will be initialized to 0, NULL or FALSE in dict_mem_index_create(). */ struct dict_index_struct{ - dulint id; /*!< id of the index */ + index_id_t id; /*!< id of the index */ mem_heap_t* heap; /*!< memory heap */ const char* name; /*!< index name */ const char* table_name;/*!< table name */ @@ -349,7 +349,7 @@ struct dict_index_struct{ /* @} */ rw_lock_t lock; /*!< read-write lock protecting the upper levels of the index tree */ - ib_uint64_t trx_id; /*!< id of the transaction that created this + trx_id_t trx_id; /*!< id of the transaction that created this index, or 0 if the index existed when InnoDB was started up */ #endif /* !UNIV_HOTBACKUP */ @@ -414,7 +414,7 @@ a foreign key constraint is enforced, therefore RESTRICT just means no flag */ /** Data structure for a database table. Most fields will be initialized to 0, NULL or FALSE in dict_mem_table_create(). */ struct dict_table_struct{ - dulint id; /*!< id of the table */ + table_id_t id; /*!< id of the table */ mem_heap_t* heap; /*!< memory heap */ char* name; /*!< table name */ const char* dir_path_of_temp_table;/*!< NULL or the directory path diff --git a/storage/innobase/include/dict0types.h b/storage/innobase/include/dict0types.h index 7ad69193cc9..0a9edfbfe70 100644 --- a/storage/innobase/include/dict0types.h +++ b/storage/innobase/include/dict0types.h @@ -45,4 +45,7 @@ typedef struct tab_node_struct tab_node_t; #define DICT_HDR_SPACE 0 /* the SYSTEM tablespace */ #define DICT_HDR_PAGE_NO FSP_DICT_HDR_PAGE_NO +typedef ib_id_t table_id_t; +typedef ib_id_t index_id_t; + #endif diff --git a/storage/innobase/include/fil0fil.h b/storage/innobase/include/fil0fil.h index c746915844b..d7d98787bcf 100644 --- a/storage/innobase/include/fil0fil.h +++ b/storage/innobase/include/fil0fil.h @@ -26,6 +26,7 @@ Created 10/25/1995 Heikki Tuuri #ifndef fil0fil_h #define fil0fil_h +#include "univ.i" #include "dict0types.h" #include "ut0byte.h" #include "os0file.h" diff --git a/storage/innobase/include/lock0lock.h b/storage/innobase/include/lock0lock.h index 0319c0e9261..3eca80beda4 100644 --- a/storage/innobase/include/lock0lock.h +++ b/storage/innobase/include/lock0lock.h @@ -670,7 +670,7 @@ lock_get_type( Gets the id of the transaction owning a lock. @return transaction id */ UNIV_INTERN -ullint +trx_id_t lock_get_trx_id( /*============*/ const lock_t* lock); /*!< in: lock */ @@ -699,7 +699,7 @@ lock_get_type_str( Gets the id of the table on which the lock is. @return id of the table */ UNIV_INTERN -ullint +table_id_t lock_get_table_id( /*==============*/ const lock_t* lock); /*!< in: lock */ diff --git a/storage/innobase/include/mach0data.h b/storage/innobase/include/mach0data.h index 44ee3df22ce..7d80bcd5b8c 100644 --- a/storage/innobase/include/mach0data.h +++ b/storage/innobase/include/mach0data.h @@ -166,14 +166,14 @@ UNIV_INLINE void mach_write_to_6( /*============*/ - byte* b, /*!< in: pointer to 6 bytes where to store */ - dulint n); /*!< in: dulint integer to be stored */ + byte* b, /*!< in: pointer to 6 bytes where to store */ + ib_uint64_t id); /*!< in: 48-bit integer */ /********************************************************//** The following function is used to fetch data from 6 consecutive bytes. The most significant byte is at the lowest address. -@return dulint integer */ +@return 48-bit integer */ UNIV_INLINE -dulint +ib_uint64_t mach_read_from_6( /*=============*/ const byte* b) /*!< in: pointer to 6 bytes */ @@ -185,14 +185,14 @@ UNIV_INLINE void mach_write_to_7( /*============*/ - byte* b, /*!< in: pointer to 7 bytes where to store */ - dulint n); /*!< in: dulint integer to be stored */ + byte* b, /*!< in: pointer to 7 bytes where to store */ + ib_uint64_t n); /*!< in: 56-bit integer */ /********************************************************//** The following function is used to fetch data from 7 consecutive bytes. The most significant byte is at the lowest address. -@return dulint integer */ +@return 56-bit integer */ UNIV_INLINE -dulint +ib_uint64_t mach_read_from_7( /*=============*/ const byte* b) /*!< in: pointer to 7 bytes */ @@ -204,88 +204,69 @@ UNIV_INLINE void mach_write_to_8( /*============*/ - byte* b, /*!< in: pointer to 8 bytes where to store */ - dulint n); /*!< in: dulint integer to be stored */ -/*******************************************************//** -The following function is used to store data in 8 consecutive -bytes. We store the most significant byte to the lowest address. */ -UNIV_INLINE -void -mach_write_ull( -/*===========*/ byte* b, /*!< in: pointer to 8 bytes where to store */ ib_uint64_t n); /*!< in: 64-bit integer to be stored */ /********************************************************//** The following function is used to fetch data from 8 consecutive bytes. The most significant byte is at the lowest address. -@return dulint integer */ -UNIV_INLINE -dulint -mach_read_from_8( -/*=============*/ - const byte* b) /*!< in: pointer to 8 bytes */ - __attribute__((nonnull, pure)); -/********************************************************//** -The following function is used to fetch data from 8 consecutive -bytes. The most significant byte is at the lowest address. @return 64-bit integer */ UNIV_INLINE ib_uint64_t -mach_read_ull( -/*==========*/ +mach_read_from_8( +/*=============*/ const byte* b) /*!< in: pointer to 8 bytes */ __attribute__((nonnull, pure)); /*********************************************************//** -Writes a dulint in a compressed form (5..9 bytes). +Writes a 64-bit integer in a compressed form (5..9 bytes). @return size in bytes */ UNIV_INLINE ulint -mach_dulint_write_compressed( -/*=========================*/ - byte* b, /*!< in: pointer to memory where to store */ - dulint n); /*!< in: dulint integer to be stored */ +mach_ull_write_compressed( +/*======================*/ + byte* b, /*!< in: pointer to memory where to store */ + ib_uint64_t n); /*!< in: 64-bit integer to be stored */ /*********************************************************//** -Returns the size of a dulint when written in the compressed form. +Returns the size of a 64-bit integer when written in the compressed form. @return compressed size in bytes */ UNIV_INLINE ulint -mach_dulint_get_compressed_size( -/*============================*/ - dulint n); /*!< in: dulint integer to be stored */ +mach_ull_get_compressed_size( +/*=========================*/ + ib_uint64_t n); /*!< in: 64-bit integer to be stored */ /*********************************************************//** -Reads a dulint in a compressed form. -@return read dulint */ +Reads a 64-bit integer in a compressed form. +@return the value read */ UNIV_INLINE -dulint -mach_dulint_read_compressed( -/*========================*/ +ib_uint64_t +mach_ull_read_compressed( +/*=====================*/ const byte* b) /*!< in: pointer to memory from where to read */ __attribute__((nonnull, pure)); /*********************************************************//** -Writes a dulint in a compressed form (1..11 bytes). +Writes a 64-bit integer in a compressed form (1..11 bytes). @return size in bytes */ UNIV_INLINE ulint -mach_dulint_write_much_compressed( -/*==============================*/ - byte* b, /*!< in: pointer to memory where to store */ - dulint n); /*!< in: dulint integer to be stored */ +mach_ull_write_much_compressed( +/*===========================*/ + byte* b, /*!< in: pointer to memory where to store */ + ib_uint64_t n); /*!< in: 64-bit integer to be stored */ /*********************************************************//** -Returns the size of a dulint when written in the compressed form. +Returns the size of a 64-bit integer when written in the compressed form. @return compressed size in bytes */ UNIV_INLINE ulint -mach_dulint_get_much_compressed_size( -/*=================================*/ - dulint n) /*!< in: dulint integer to be stored */ +mach_ull_get_much_compressed_size( +/*==============================*/ + ib_uint64_t n) /*!< in: 64-bit integer to be stored */ __attribute__((const)); /*********************************************************//** -Reads a dulint in a compressed form. -@return read dulint */ +Reads a 64-bit integer in a compressed form. +@return the value read */ UNIV_INLINE -dulint -mach_dulint_read_much_compressed( -/*=============================*/ +ib_uint64_t +mach_ull_read_much_compressed( +/*==========================*/ const byte* b) /*!< in: pointer to memory from where to read */ __attribute__((nonnull, pure)); /*********************************************************//** @@ -299,15 +280,17 @@ mach_parse_compressed( byte* end_ptr,/*!< in: pointer to end of the buffer */ ulint* val); /*!< out: read value */ /*********************************************************//** -Reads a dulint in a compressed form if the log record fully contains it. -@return pointer to end of the stored field, NULL if not complete */ +Reads a 64-bit integer in a compressed form +if the log record fully contains it. +@return pointer to end of the stored field, NULL if not complete */ UNIV_INTERN byte* -mach_dulint_parse_compressed( -/*=========================*/ - byte* ptr, /*!< in: pointer to buffer from where to read */ - byte* end_ptr,/*!< in: pointer to end of the buffer */ - dulint* val); /*!< out: read value */ +mach_ull_parse_compressed( +/*======================*/ + byte* ptr, /*!< in: pointer to buffer from where to read */ + byte* end_ptr,/*!< in: pointer to end of the buffer */ + ib_uint64_t* val) /*!< out: read value */ + __attribute__((nonnull, pure)); #ifndef UNIV_HOTBACKUP /*********************************************************//** Reads a double. It is stored in a little-endian format. diff --git a/storage/innobase/include/mach0data.ic b/storage/innobase/include/mach0data.ic index 96d2417ac81..e27aecfa577 100644 --- a/storage/innobase/include/mach0data.ic +++ b/storage/innobase/include/mach0data.ic @@ -280,22 +280,6 @@ UNIV_INLINE void mach_write_to_8( /*============*/ - byte* b, /*!< in: pointer to 8 bytes where to store */ - dulint n) /*!< in: dulint integer to be stored */ -{ - ut_ad(b); - - mach_write_to_4(b, ut_dulint_get_high(n)); - mach_write_to_4(b + 4, ut_dulint_get_low(n)); -} - -/*******************************************************//** -The following function is used to store data in 8 consecutive -bytes. We store the most significant byte to the lowest address. */ -UNIV_INLINE -void -mach_write_ull( -/*===========*/ byte* b, /*!< in: pointer to 8 bytes where to store */ ib_uint64_t n) /*!< in: 64-bit integer to be stored */ { @@ -305,35 +289,14 @@ mach_write_ull( mach_write_to_4(b + 4, (ulint) n); } -/********************************************************//** -The following function is used to fetch data from 8 consecutive -bytes. The most significant byte is at the lowest address. -@return dulint integer */ -UNIV_INLINE -dulint -mach_read_from_8( -/*=============*/ - const byte* b) /*!< in: pointer to 8 bytes */ -{ - ulint high; - ulint low; - - ut_ad(b); - - high = mach_read_from_4(b); - low = mach_read_from_4(b + 4); - - return(ut_dulint_create(high, low)); -} - /********************************************************//** The following function is used to fetch data from 8 consecutive bytes. The most significant byte is at the lowest address. @return 64-bit integer */ UNIV_INLINE ib_uint64_t -mach_read_ull( -/*==========*/ +mach_read_from_8( +/*=============*/ const byte* b) /*!< in: pointer to 8 bytes */ { ib_uint64_t ull; @@ -351,34 +314,28 @@ UNIV_INLINE void mach_write_to_7( /*============*/ - byte* b, /*!< in: pointer to 7 bytes where to store */ - dulint n) /*!< in: dulint integer to be stored */ + byte* b, /*!< in: pointer to 7 bytes where to store */ + ib_uint64_t n) /*!< in: 56-bit integer */ { ut_ad(b); - mach_write_to_3(b, ut_dulint_get_high(n)); - mach_write_to_4(b + 3, ut_dulint_get_low(n)); + mach_write_to_3(b, (ulint) (n >> 32)); + mach_write_to_4(b + 3, (ulint) n); } /********************************************************//** The following function is used to fetch data from 7 consecutive bytes. The most significant byte is at the lowest address. -@return dulint integer */ +@return 56-bit integer */ UNIV_INLINE -dulint +ib_uint64_t mach_read_from_7( /*=============*/ const byte* b) /*!< in: pointer to 7 bytes */ { - ulint high; - ulint low; - ut_ad(b); - high = mach_read_from_3(b); - low = mach_read_from_4(b + 3); - - return(ut_dulint_create(high, low)); + return(ut_ull_create(mach_read_from_3(b), mach_read_from_4(b + 3))); } /*******************************************************//** @@ -388,162 +345,156 @@ UNIV_INLINE void mach_write_to_6( /*============*/ - byte* b, /*!< in: pointer to 6 bytes where to store */ - dulint n) /*!< in: dulint integer to be stored */ + byte* b, /*!< in: pointer to 6 bytes where to store */ + ib_uint64_t n) /*!< in: 48-bit integer */ { ut_ad(b); - mach_write_to_2(b, ut_dulint_get_high(n)); - mach_write_to_4(b + 2, ut_dulint_get_low(n)); + mach_write_to_2(b, (ulint) (n >> 32)); + mach_write_to_4(b + 2, (ulint) n); } /********************************************************//** The following function is used to fetch data from 6 consecutive bytes. The most significant byte is at the lowest address. -@return dulint integer */ +@return 48-bit integer */ UNIV_INLINE -dulint +ib_uint64_t mach_read_from_6( /*=============*/ const byte* b) /*!< in: pointer to 6 bytes */ { - ulint high; - ulint low; - ut_ad(b); - high = mach_read_from_2(b); - low = mach_read_from_4(b + 2); - - return(ut_dulint_create(high, low)); + return(ut_ull_create(mach_read_from_2(b), mach_read_from_4(b + 2))); } /*********************************************************//** -Writes a dulint in a compressed form (5..9 bytes). +Writes a 64-bit integer in a compressed form (5..9 bytes). @return size in bytes */ UNIV_INLINE ulint -mach_dulint_write_compressed( -/*=========================*/ - byte* b, /*!< in: pointer to memory where to store */ - dulint n) /*!< in: dulint integer to be stored */ +mach_ull_write_compressed( +/*======================*/ + byte* b, /*!< in: pointer to memory where to store */ + ib_uint64_t n) /*!< in: 64-bit integer to be stored */ { ulint size; ut_ad(b); - size = mach_write_compressed(b, ut_dulint_get_high(n)); - mach_write_to_4(b + size, ut_dulint_get_low(n)); + size = mach_write_compressed(b, (ulint) (n >> 32)); + mach_write_to_4(b + size, (ulint) n); return(size + 4); } /*********************************************************//** -Returns the size of a dulint when written in the compressed form. +Returns the size of a 64-bit integer when written in the compressed form. @return compressed size in bytes */ UNIV_INLINE ulint -mach_dulint_get_compressed_size( -/*============================*/ - dulint n) /*!< in: dulint integer to be stored */ +mach_ull_get_compressed_size( +/*=========================*/ + ib_uint64_t n) /*!< in: 64-bit integer to be stored */ { - return(4 + mach_get_compressed_size(ut_dulint_get_high(n))); + return(4 + mach_get_compressed_size((ulint) (n >> 32))); } /*********************************************************//** -Reads a dulint in a compressed form. -@return read dulint */ +Reads a 64-bit integer in a compressed form. +@return the value read */ UNIV_INLINE -dulint -mach_dulint_read_compressed( -/*========================*/ +ib_uint64_t +mach_ull_read_compressed( +/*=====================*/ const byte* b) /*!< in: pointer to memory from where to read */ { - ulint high; - ulint low; - ulint size; + ib_uint64_t n; + ulint size; ut_ad(b); - high = mach_read_compressed(b); + n = (ib_uint64_t) mach_read_compressed(b); - size = mach_get_compressed_size(high); + size = mach_get_compressed_size((ulint) n); - low = mach_read_from_4(b + size); + n <<= 32; + n |= (ib_uint64_t) mach_read_from_4(b + size); - return(ut_dulint_create(high, low)); + return(n); } /*********************************************************//** -Writes a dulint in a compressed form (1..11 bytes). +Writes a 64-bit integer in a compressed form (1..11 bytes). @return size in bytes */ UNIV_INLINE ulint -mach_dulint_write_much_compressed( -/*==============================*/ - byte* b, /*!< in: pointer to memory where to store */ - dulint n) /*!< in: dulint integer to be stored */ +mach_ull_write_much_compressed( +/*===========================*/ + byte* b, /*!< in: pointer to memory where to store */ + ib_uint64_t n) /*!< in: 64-bit integer to be stored */ { ulint size; ut_ad(b); - if (ut_dulint_get_high(n) == 0) { - return(mach_write_compressed(b, ut_dulint_get_low(n))); + if (!(n >> 32)) { + return(mach_write_compressed(b, (ulint) n)); } *b = (byte)0xFF; - size = 1 + mach_write_compressed(b + 1, ut_dulint_get_high(n)); + size = 1 + mach_write_compressed(b + 1, (ulint) (n >> 32)); - size += mach_write_compressed(b + size, ut_dulint_get_low(n)); + size += mach_write_compressed(b + size, (ulint) n & 0xFFFFFFFF); return(size); } /*********************************************************//** -Returns the size of a dulint when written in the compressed form. +Returns the size of a 64-bit integer when written in the compressed form. @return compressed size in bytes */ UNIV_INLINE ulint -mach_dulint_get_much_compressed_size( -/*=================================*/ - dulint n) /*!< in: dulint integer to be stored */ +mach_ull_get_much_compressed_size( +/*==============================*/ + ib_uint64_t n) /*!< in: 64-bit integer to be stored */ { - if (0 == ut_dulint_get_high(n)) { - return(mach_get_compressed_size(ut_dulint_get_low(n))); + if (!(n >> 32)) { + return(mach_get_compressed_size((ulint) n)); } - return(1 + mach_get_compressed_size(ut_dulint_get_high(n)) - + mach_get_compressed_size(ut_dulint_get_low(n))); + return(1 + mach_get_compressed_size((ulint) (n >> 32)) + + mach_get_compressed_size((ulint) n & ULINT32_MASK)); } /*********************************************************//** -Reads a dulint in a compressed form. -@return read dulint */ +Reads a 64-bit integer in a compressed form. +@return the value read */ UNIV_INLINE -dulint -mach_dulint_read_much_compressed( -/*=============================*/ +ib_uint64_t +mach_ull_read_much_compressed( +/*==========================*/ const byte* b) /*!< in: pointer to memory from where to read */ { - ulint high; - ulint low; - ulint size; + ib_uint64_t n; + ulint size; ut_ad(b); if (*b != (byte)0xFF) { - high = 0; + n = 0; size = 0; } else { - high = mach_read_compressed(b + 1); + n = (ib_uint64_t) mach_read_compressed(b + 1); - size = 1 + mach_get_compressed_size(high); + size = 1 + mach_get_compressed_size((ulint) n); + n <<= 32; } - low = mach_read_compressed(b + size); + n |= mach_read_compressed(b + size); - return(ut_dulint_create(high, low)); + return(n); } #ifndef UNIV_HOTBACKUP /*********************************************************//** diff --git a/storage/innobase/include/mtr0log.h b/storage/innobase/include/mtr0log.h index 6322af2a569..d271002a5fe 100644 --- a/storage/innobase/include/mtr0log.h +++ b/storage/innobase/include/mtr0log.h @@ -47,11 +47,11 @@ Writes 8 bytes to a file page buffered in the buffer pool. Writes the corresponding log record to the mini-transaction log. */ UNIV_INTERN void -mlog_write_dulint( -/*==============*/ - byte* ptr, /*!< in: pointer where to write */ - dulint val, /*!< in: value to write */ - mtr_t* mtr); /*!< in: mini-transaction handle */ +mlog_write_ull( +/*===========*/ + byte* ptr, /*!< in: pointer where to write */ + ib_uint64_t val, /*!< in: value to write */ + mtr_t* mtr); /*!< in: mini-transaction handle */ /********************************************************//** Writes a string to a file page buffered in the buffer pool. Writes the corresponding log record to the mini-transaction log. */ @@ -125,13 +125,13 @@ mlog_catenate_ulint_compressed( mtr_t* mtr, /*!< in: mtr */ ulint val); /*!< in: value to write */ /********************************************************//** -Catenates a compressed dulint to mlog. */ +Catenates a compressed 64-bit integer to mlog. */ UNIV_INLINE void -mlog_catenate_dulint_compressed( -/*============================*/ - mtr_t* mtr, /*!< in: mtr */ - dulint val); /*!< in: value to write */ +mlog_catenate_ull_compressed( +/*=========================*/ + mtr_t* mtr, /*!< in: mtr */ + ib_uint64_t val); /*!< in: value to write */ /********************************************************//** Opens a buffer to mlog. It must be closed with mlog_close. @return buffer, NULL if log mode MTR_LOG_NONE */ @@ -183,7 +183,7 @@ mlog_parse_initial_log_record( ulint* space, /*!< out: space id */ ulint* page_no);/*!< out: page number */ /********************************************************//** -Parses a log record written by mlog_write_ulint or mlog_write_dulint. +Parses a log record written by mlog_write_ulint or mlog_write_ull. @return parsed record end, NULL if not a complete record */ UNIV_INTERN byte* diff --git a/storage/innobase/include/mtr0log.ic b/storage/innobase/include/mtr0log.ic index 5c24c38b337..c670a0a8c82 100644 --- a/storage/innobase/include/mtr0log.ic +++ b/storage/innobase/include/mtr0log.ic @@ -142,13 +142,13 @@ mlog_catenate_ulint_compressed( } /********************************************************//** -Catenates a compressed dulint to mlog. */ +Catenates a compressed 64-bit integer to mlog. */ UNIV_INLINE void -mlog_catenate_dulint_compressed( -/*============================*/ - mtr_t* mtr, /*!< in: mtr */ - dulint val) /*!< in: value to write */ +mlog_catenate_ull_compressed( +/*=========================*/ + mtr_t* mtr, /*!< in: mtr */ + ib_uint64_t val) /*!< in: value to write */ { byte* log_ptr; @@ -160,7 +160,7 @@ mlog_catenate_dulint_compressed( return; } - log_ptr += mach_dulint_write_compressed(log_ptr, val); + log_ptr += mach_ull_write_compressed(log_ptr, val); mlog_close(mtr, log_ptr); } diff --git a/storage/innobase/include/mtr0mtr.h b/storage/innobase/include/mtr0mtr.h index bc3f1951be9..8abca093548 100644 --- a/storage/innobase/include/mtr0mtr.h +++ b/storage/innobase/include/mtr0mtr.h @@ -264,15 +264,6 @@ mtr_read_ulint( const byte* ptr, /*!< in: pointer from where to read */ ulint type, /*!< in: MLOG_1BYTE, MLOG_2BYTES, MLOG_4BYTES */ mtr_t* mtr); /*!< in: mini-transaction handle */ -/********************************************************//** -Reads 8 bytes from a file page buffered in the buffer pool. -@return value read */ -UNIV_INTERN -dulint -mtr_read_dulint( -/*============*/ - const byte* ptr, /*!< in: pointer from where to read */ - mtr_t* mtr); /*!< in: mini-transaction handle */ #ifndef UNIV_HOTBACKUP /*********************************************************************//** This macro locks an rw-lock in s-mode. */ diff --git a/storage/innobase/include/page0page.h b/storage/innobase/include/page0page.h index 3899499fb6a..826fd13125d 100644 --- a/storage/innobase/include/page0page.h +++ b/storage/innobase/include/page0page.h @@ -66,7 +66,7 @@ typedef byte page_header_t; direction */ #define PAGE_N_RECS 16 /* number of user records on the page */ #define PAGE_MAX_TRX_ID 18 /* highest id of a trx which may have modified - a record on the page; a dulint; defined only + a record on the page; trx_id_t; defined only in secondary indexes and in the insert buffer tree; NOTE: this may be modified only when the thread has an x-latch to the page, diff --git a/storage/innobase/include/page0page.ic b/storage/innobase/include/page0page.ic index 8f794410f20..e9624c2360f 100644 --- a/storage/innobase/include/page0page.ic +++ b/storage/innobase/include/page0page.ic @@ -94,11 +94,10 @@ page_update_max_trx_id( TRUE for the dummy indexes constructed during redo log application). In that case, PAGE_MAX_TRX_ID is unused, and trx_id is usually zero. */ - ut_ad(!ut_dulint_is_zero(trx_id) || recv_recovery_is_on()); + ut_ad(trx_id || recv_recovery_is_on()); ut_ad(page_is_leaf(buf_block_get_frame(block))); - if (ut_dulint_cmp(page_get_max_trx_id(buf_block_get_frame(block)), - trx_id) < 0) { + if (page_get_max_trx_id(buf_block_get_frame(block)) < trx_id) { page_set_max_trx_id(block, page_zip, trx_id, mtr); } diff --git a/storage/innobase/include/pars0pars.h b/storage/innobase/include/pars0pars.h index 524fe4ac3e7..141b2706d7d 100644 --- a/storage/innobase/include/pars0pars.h +++ b/storage/innobase/include/pars0pars.h @@ -520,35 +520,18 @@ pars_info_add_int4_literal( Equivalent to: char buf[8]; -mach_write_ull(buf, val); -pars_info_add_literal(info, name, buf, 8, DATA_INT, 0); +mach_write_to_8(buf, val); +pars_info_add_literal(info, name, buf, 8, DATA_FIXBINARY, 0); except that the buffer is dynamically allocated from the info struct's heap. */ UNIV_INTERN void -pars_info_add_uint64_literal( -/*=========================*/ +pars_info_add_ull_literal( +/*======================*/ pars_info_t* info, /*!< in: info struct */ const char* name, /*!< in: name */ ib_uint64_t val); /*!< in: value */ - -/****************************************************************//** -Equivalent to: - -char buf[8]; -mach_write_to_8(buf, val); -pars_info_add_literal(info, name, buf, 8, DATA_BINARY, 0); - -except that the buffer is dynamically allocated from the info struct's -heap. */ -UNIV_INTERN -void -pars_info_add_dulint_literal( -/*=========================*/ - pars_info_t* info, /*!< in: info struct */ - const char* name, /*!< in: name */ - dulint val); /*!< in: value */ /****************************************************************//** Add user function. */ UNIV_INTERN diff --git a/storage/innobase/include/que0que.h b/storage/innobase/include/que0que.h index 39f8d07af89..09734bbb197 100644 --- a/storage/innobase/include/que0que.h +++ b/storage/innobase/include/que0que.h @@ -421,9 +421,6 @@ struct que_fork_struct{ ibool cur_on_row; /*!< TRUE if cursor is on a row, i.e., it is not before the first row or after the last row */ - dulint n_inserts; /*!< number of rows inserted */ - dulint n_updates; /*!< number of rows updated */ - dulint n_deletes; /*!< number of rows deleted */ sel_node_t* last_sel_node; /*!< last executed select node, or NULL if none */ UT_LIST_NODE_T(que_fork_t) diff --git a/storage/innobase/include/read0read.h b/storage/innobase/include/read0read.h index 4d9a9fade36..73ea66f4da2 100644 --- a/storage/innobase/include/read0read.h +++ b/storage/innobase/include/read0read.h @@ -43,8 +43,7 @@ read_view_t* read_view_open_now( /*===============*/ trx_id_t cr_trx_id, /*!< in: trx_id of creating - transaction, or ut_dulint_zero - used in purge */ + transaction, or 0 used in purge */ mem_heap_t* heap); /*!< in: memory heap from which allocated */ /*********************************************************************//** @@ -56,8 +55,7 @@ read_view_t* read_view_oldest_copy_or_open_new( /*==============================*/ trx_id_t cr_trx_id, /*!< in: trx_id of creating - transaction, or ut_dulint_zero - used in purge */ + transaction, or 0 used in purge */ mem_heap_t* heap); /*!< in: memory heap from which allocated */ /*********************************************************************//** @@ -125,7 +123,7 @@ read should not see the modifications to the database. */ struct read_view_struct{ ulint type; /*!< VIEW_NORMAL, VIEW_HIGH_GRANULARITY */ - undo_no_t undo_no;/*!< ut_dulint_zero or if type is + undo_no_t undo_no;/*!< 0 or if type is VIEW_HIGH_GRANULARITY transaction undo_no when this high-granularity consistent read view was created */ @@ -156,7 +154,7 @@ struct read_view_struct{ that is, up_limit_id and low_limit_id. */ trx_id_t creator_trx_id; /*!< trx id of creating transaction, or - ut_dulint_zero used in purge */ + 0 used in purge */ UT_LIST_NODE_T(read_view_t) view_list; /*!< List of read views in trx_sys */ }; diff --git a/storage/innobase/include/read0read.ic b/storage/innobase/include/read0read.ic index 9924967cc2d..5bb5249b591 100644 --- a/storage/innobase/include/read0read.ic +++ b/storage/innobase/include/read0read.ic @@ -64,15 +64,14 @@ read_view_sees_trx_id( trx_id_t trx_id) /*!< in: trx id */ { ulint n_ids; - int cmp; ulint i; - if (ut_dulint_cmp(trx_id, view->up_limit_id) < 0) { + if (trx_id < view->up_limit_id) { return(TRUE); } - if (ut_dulint_cmp(trx_id, view->low_limit_id) >= 0) { + if (trx_id >= view->low_limit_id) { return(FALSE); } @@ -85,12 +84,11 @@ read_view_sees_trx_id( n_ids = view->n_trx_ids; for (i = 0; i < n_ids; i++) { + trx_id_t view_trx_id + = read_view_get_nth_trx_id(view, n_ids - i - 1); - cmp = ut_dulint_cmp( - trx_id, - read_view_get_nth_trx_id(view, n_ids - i - 1)); - if (cmp <= 0) { - return(cmp < 0); + if (trx_id <= view_trx_id) { + return(trx_id != view_trx_id); } } diff --git a/storage/innobase/include/rem0rec.h b/storage/innobase/include/rem0rec.h index 17d08afabb9..53402e8d3a9 100644 --- a/storage/innobase/include/rem0rec.h +++ b/storage/innobase/include/rem0rec.h @@ -659,7 +659,7 @@ rec_fold( fields to fold */ ulint n_bytes, /*!< in: number of bytes to fold in an incomplete last field */ - dulint tree_id) /*!< in: index tree id */ + index_id_t tree_id) /*!< in: index tree id */ __attribute__((pure)); #endif /* !UNIV_HOTBACKUP */ /*********************************************************//** diff --git a/storage/innobase/include/rem0rec.ic b/storage/innobase/include/rem0rec.ic index 8e5bd9a7fcd..ba306eaf27f 100644 --- a/storage/innobase/include/rem0rec.ic +++ b/storage/innobase/include/rem0rec.ic @@ -1594,7 +1594,7 @@ rec_fold( fields to fold */ ulint n_bytes, /*!< in: number of bytes to fold in an incomplete last field */ - dulint tree_id) /*!< in: index tree id */ + index_id_t tree_id) /*!< in: index tree id */ { ulint i; const byte* data; @@ -1618,7 +1618,7 @@ rec_fold( n_bytes = 0; } - fold = ut_fold_dulint(tree_id); + fold = ut_fold_ull(tree_id); for (i = 0; i < n_fields; i++) { data = rec_get_nth_field(rec, offsets, i, &len); diff --git a/storage/innobase/include/row0upd.h b/storage/innobase/include/row0upd.h index 635d746d5a1..f7bec6f7561 100644 --- a/storage/innobase/include/row0upd.h +++ b/storage/innobase/include/row0upd.h @@ -132,7 +132,7 @@ row_upd_index_entry_sys_field( them */ dict_index_t* index, /*!< in: clustered index */ ulint type, /*!< in: DATA_TRX_ID or DATA_ROLL_PTR */ - dulint val); /*!< in: value to write */ + ib_uint64_t val); /*!< in: value to write */ /*********************************************************************//** Creates an update node for a query graph. @return own: update node */ diff --git a/storage/innobase/include/srv0srv.h b/storage/innobase/include/srv0srv.h index 18f7c07c3c6..5fbb59b14ff 100644 --- a/storage/innobase/include/srv0srv.h +++ b/storage/innobase/include/srv0srv.h @@ -161,9 +161,9 @@ is 5% of the max where max is srv_io_capacity. */ #define PCT_IO(p) ((ulong) (srv_io_capacity * ((double) p / 100.0))) #ifdef UNIV_LOG_ARCHIVE -extern ibool srv_log_archive_on; -extern ibool srv_archive_recovery; -extern dulint srv_archive_recovery_limit_lsn; +extern ibool srv_log_archive_on; +extern ibool srv_archive_recovery; +extern ib_uint64_t srv_archive_recovery_limit_lsn; #endif /* UNIV_LOG_ARCHIVE */ extern char* srv_file_flush_method_str; diff --git a/storage/innobase/include/trx0i_s.h b/storage/innobase/include/trx0i_s.h index c610782c229..8f8b7367fb0 100644 --- a/storage/innobase/include/trx0i_s.h +++ b/storage/innobase/include/trx0i_s.h @@ -30,6 +30,7 @@ Created July 17, 2007 Vasil Dimov #include "univ.i" #include "trx0types.h" +#include "dict0types.h" #include "ut0ut.h" /** The maximum amount of memory that can be consumed by innodb_trx, @@ -95,7 +96,7 @@ struct i_s_hash_chain_struct { /** This structure represents INFORMATION_SCHEMA.innodb_locks row */ struct i_s_locks_row_struct { - ullint lock_trx_id; /*!< transaction identifier */ + trx_id_t lock_trx_id; /*!< transaction identifier */ const char* lock_mode; /*!< lock mode from lock_get_mode_str() */ const char* lock_type; /*!< lock type from @@ -116,7 +117,7 @@ struct i_s_locks_row_struct { /** The following are auxiliary and not included in the table */ /* @{ */ - ullint lock_table_id; + table_id_t lock_table_id; /*!< table identifier from lock_get_table_id */ i_s_hash_chain_t hash_chain; /*!< hash table chain node for @@ -126,10 +127,10 @@ struct i_s_locks_row_struct { /** This structure represents INFORMATION_SCHEMA.innodb_trx row */ struct i_s_trx_row_struct { - ullint trx_id; /*!< transaction identifier */ - const char* trx_state; /*!< transaction state from - trx_get_que_state_str() */ - ib_time_t trx_started; /*!< trx_struct::start_time */ + trx_id_t trx_id; /*!< transaction identifier */ + const char* trx_state; /*!< transaction state from + trx_get_que_state_str() */ + ib_time_t trx_started; /*!< trx_struct::start_time */ const i_s_locks_row_t* requested_lock_row; /*!< pointer to a row in innodb_locks if trx diff --git a/storage/innobase/include/trx0rec.h b/storage/innobase/include/trx0rec.h index a6e56e963c6..477748f6f89 100644 --- a/storage/innobase/include/trx0rec.h +++ b/storage/innobase/include/trx0rec.h @@ -108,7 +108,7 @@ trx_undo_rec_get_pars( ibool* updated_extern, /*!< out: TRUE if we updated an externally stored fild */ undo_no_t* undo_no, /*!< out: undo log record number */ - dulint* table_id); /*!< out: table id */ + table_id_t* table_id); /*!< out: table id */ /*******************************************************************//** Builds a row reference from an undo log record. @return pointer to remaining part of undo record */ @@ -227,7 +227,7 @@ trx_undo_report_row_operation( index, otherwise NULL */ roll_ptr_t* roll_ptr); /*!< out: rollback pointer to the inserted undo log record, - ut_dulint_zero if BTR_NO_UNDO_LOG + 0 if BTR_NO_UNDO_LOG flag was specified */ /******************************************************************//** Copies an undo record to heap. This function can be called if we know that diff --git a/storage/innobase/include/trx0rec.ic b/storage/innobase/include/trx0rec.ic index e7e41d6d9f6..f0b3276ed44 100644 --- a/storage/innobase/include/trx0rec.ic +++ b/storage/innobase/include/trx0rec.ic @@ -78,7 +78,7 @@ trx_undo_rec_get_undo_no( ptr = undo_rec + 3; - return(mach_dulint_read_much_compressed(ptr)); + return(mach_ull_read_much_compressed(ptr)); } /**********************************************************************//** @@ -90,7 +90,7 @@ trx_undo_rec_get_offset( /*====================*/ undo_no_t undo_no) /*!< in: undo no read from node */ { - return (3 + mach_dulint_get_much_compressed_size(undo_no)); + return (3 + mach_ull_get_much_compressed_size(undo_no)); } /***********************************************************************//** diff --git a/storage/innobase/include/trx0sys.h b/storage/innobase/include/trx0sys.h index fc92b4317d5..d6965377165 100644 --- a/storage/innobase/include/trx0sys.h +++ b/storage/innobase/include/trx0sys.h @@ -573,6 +573,11 @@ identifier is added to this constant. */ #define TRX_SYS_FILE_FORMAT_TAG_MAGIC_N_LOW 3645922177UL /** Contents of TRX_SYS_FILE_FORMAT_TAG+4 when valid */ #define TRX_SYS_FILE_FORMAT_TAG_MAGIC_N_HIGH 2745987765UL +/** Contents of TRX_SYS_FILE_FORMAT_TAG when valid. The file format +identifier is added to this 64-bit constant. */ +#define TRX_SYS_FILE_FORMAT_TAG_MAGIC_N \ + ((ib_uint64_t) TRX_SYS_FILE_FORMAT_TAG_MAGIC_N_HIGH << 32 \ + | TRX_SYS_FILE_FORMAT_TAG_MAGIC_N_LOW) /* @} */ /** Doublewrite control struct */ diff --git a/storage/innobase/include/trx0sys.ic b/storage/innobase/include/trx0sys.ic index 820d31d0692..385c7f4f0cc 100644 --- a/storage/innobase/include/trx0sys.ic +++ b/storage/innobase/include/trx0sys.ic @@ -266,7 +266,7 @@ trx_get_on_id( trx = UT_LIST_GET_FIRST(trx_sys->trx_list); while (trx != NULL) { - if (0 == ut_dulint_cmp(trx_id, trx->id)) { + if (trx_id == trx->id) { return(trx); } @@ -315,12 +315,12 @@ trx_is_active( ut_ad(mutex_own(&(kernel_mutex))); - if (ut_dulint_cmp(trx_id, trx_list_get_min_trx_id()) < 0) { + if (trx_id < trx_list_get_min_trx_id()) { return(FALSE); } - if (ut_dulint_cmp(trx_id, trx_sys->max_trx_id) >= 0) { + if (UNIV_UNLIKELY(trx_id >= trx_sys->max_trx_id)) { /* There must be corruption: we return TRUE because this function is only called by lock_clust_rec_some_has_impl() @@ -359,15 +359,12 @@ trx_sys_get_new_trx_id(void) Thus trx id values will not overlap when the database is repeatedly started! */ - if (ut_dulint_get_low(trx_sys->max_trx_id) - % TRX_SYS_TRX_ID_WRITE_MARGIN == 0) { + if ((ulint) trx_sys->max_trx_id % TRX_SYS_TRX_ID_WRITE_MARGIN == 0) { trx_sys_flush_max_trx_id(); } - id = trx_sys->max_trx_id; - - UT_DULINT_INC(trx_sys->max_trx_id); + id = trx_sys->max_trx_id++; return(id); } diff --git a/storage/innobase/include/trx0trx.h b/storage/innobase/include/trx0trx.h index abd175d365b..6a817ccdc8e 100644 --- a/storage/innobase/include/trx0trx.h +++ b/storage/innobase/include/trx0trx.h @@ -408,30 +408,20 @@ Calculates the "weight" of a transaction. The weight of one transaction is estimated as the number of altered rows + the number of locked rows. @param t transaction @return transaction weight */ -#define TRX_WEIGHT(t) \ - ut_dulint_add((t)->undo_no, UT_LIST_GET_LEN((t)->trx_locks)) +#define TRX_WEIGHT(t) ((t)->undo_no + UT_LIST_GET_LEN((t)->trx_locks)) /*******************************************************************//** Compares the "weight" (or size) of two transactions. Transactions that have edited non-transactional tables are considered heavier than ones that have not. -@return <0, 0 or >0; similar to strcmp(3) */ +@return TRUE if weight(a) >= weight(b) */ UNIV_INTERN -int -trx_weight_cmp( -/*===========*/ +ibool +trx_weight_ge( +/*==========*/ const trx_t* a, /*!< in: the first transaction to be compared */ const trx_t* b); /*!< in: the second transaction to be compared */ -/*******************************************************************//** -Retrieves transacion's id, represented as unsigned long long. -@return transaction's id */ -UNIV_INLINE -ullint -trx_get_id( -/*=======*/ - const trx_t* trx); /*!< in: transaction */ - /* Maximum length of a string that can be returned by trx_get_que_state_str(). */ #define TRX_QUE_STATE_STR_MAX_LEN 12 /* "ROLLING BACK" */ @@ -555,8 +545,8 @@ struct trx_struct{ max trx id when the transaction is moved to COMMITTED_IN_MEMORY state */ ib_uint64_t commit_lsn; /*!< lsn at the time of the commit */ - trx_id_t table_id; /*!< Table to drop iff dict_operation - is TRUE, or ut_dulint_zero. */ + table_id_t table_id; /*!< Table to drop iff dict_operation + is TRUE, or 0. */ /*------------------------------*/ void* mysql_thd; /*!< MySQL thread handle corresponding to this trx, or NULL */ diff --git a/storage/innobase/include/trx0trx.ic b/storage/innobase/include/trx0trx.ic index 7332eeece85..4a1d3bcde0b 100644 --- a/storage/innobase/include/trx0trx.ic +++ b/storage/innobase/include/trx0trx.ic @@ -68,18 +68,6 @@ trx_get_error_info( return(trx->error_info); } -/*******************************************************************//** -Retrieves transacion's id, represented as unsigned long long. -@return transaction's id */ -UNIV_INLINE -ullint -trx_get_id( -/*=======*/ - const trx_t* trx) /*!< in: transaction */ -{ - return((ullint)ut_conv_dulint_to_longlong(trx->id)); -} - /*******************************************************************//** Retrieves transaction's que state in a human readable string. The string should not be free()'d or modified. diff --git a/storage/innobase/include/trx0types.h b/storage/innobase/include/trx0types.h index 40a7256cbfd..a4115b5aca7 100644 --- a/storage/innobase/include/trx0types.h +++ b/storage/innobase/include/trx0types.h @@ -28,10 +28,7 @@ Created 3/26/1996 Heikki Tuuri #include "ut0byte.h" -/** prepare trx_t::id for being printed via printf(3) */ -#define TRX_ID_PREP_PRINTF(id) (ullint) ut_conv_dulint_to_longlong(id) - -/** printf(3) format used for printing TRX_ID_PRINTF_PREP() */ +/** printf(3) format used for printing DB_TRX_ID and other system fields */ #define TRX_ID_FMT "%llX" /** maximum length that a formatted trx_t::id could take, not including @@ -81,12 +78,14 @@ enum trx_rb_ctx { in crash recovery */ }; +/** Row identifier (DB_ROW_ID, DATA_ROW_ID) */ +typedef ib_id_t row_id_t; /** Transaction identifier (DB_TRX_ID, DATA_TRX_ID) */ -typedef dulint trx_id_t; +typedef ib_id_t trx_id_t; /** Rollback pointer (DB_ROLL_PTR, DATA_ROLL_PTR) */ -typedef dulint roll_ptr_t; +typedef ib_id_t roll_ptr_t; /** Undo number */ -typedef dulint undo_no_t; +typedef ib_id_t undo_no_t; /** Transaction savepoint */ typedef struct trx_savept_struct trx_savept_t; diff --git a/storage/innobase/include/trx0undo.h b/storage/innobase/include/trx0undo.h index a084f2394b5..54809f9c2d5 100644 --- a/storage/innobase/include/trx0undo.h +++ b/storage/innobase/include/trx0undo.h @@ -383,7 +383,7 @@ struct trx_undo_struct{ XID xid; /*!< X/Open XA transaction identification */ ibool dict_operation; /*!< TRUE if a dict operation trx */ - dulint table_id; /*!< if a dict operation, then the table + table_id_t table_id; /*!< if a dict operation, then the table id */ trx_rseg_t* rseg; /*!< rseg where the undo log belongs */ /*-----------------------------*/ diff --git a/storage/innobase/include/trx0undo.ic b/storage/innobase/include/trx0undo.ic index 6502ee826e5..cc7d76dffe5 100644 --- a/storage/innobase/include/trx0undo.ic +++ b/storage/innobase/include/trx0undo.ic @@ -39,16 +39,19 @@ trx_undo_build_roll_ptr( ulint page_no, /*!< in: page number */ ulint offset) /*!< in: offset of the undo entry within page */ { + roll_ptr_t roll_ptr; #if DATA_ROLL_PTR_LEN != 7 # error "DATA_ROLL_PTR_LEN != 7" #endif + ut_ad(is_insert <= 1); ut_ad(rseg_id < TRX_SYS_N_RSEGS); + ut_ad(offset < 65536); - return(ut_dulint_create(is_insert * 128 * 256 * 256 - + rseg_id * 256 * 256 - + (page_no / 256) / 256, - (page_no % (256 * 256)) * 256 * 256 - + offset)); + roll_ptr = (roll_ptr_t) is_insert << 55 + | (roll_ptr_t) rseg_id << 48 + | (roll_ptr_t) page_no << 16 + | offset; + return(roll_ptr); } /***********************************************************************//** @@ -64,24 +67,20 @@ trx_undo_decode_roll_ptr( ulint* offset) /*!< out: offset of the undo entry within page */ { - ulint low; - ulint high; #if DATA_ROLL_PTR_LEN != 7 # error "DATA_ROLL_PTR_LEN != 7" #endif #if TRUE != 1 # error "TRUE != 1" #endif - high = ut_dulint_get_high(roll_ptr); - low = ut_dulint_get_low(roll_ptr); - - *offset = low % (256 * 256); - - *is_insert = high / (256 * 256 * 128); /* TRUE == 1 */ - *rseg_id = (high / (256 * 256)) % 128; - - *page_no = (high % (256 * 256)) * 256 * 256 - + (low / 256) / 256; + ut_ad(roll_ptr < (1ULL << 56)); + *offset = (ulint) roll_ptr & 0xFFFF; + roll_ptr >>= 16; + *page_no = (ulint) roll_ptr & 0xFFFFFFFF; + roll_ptr >>= 32; + *rseg_id = (ulint) roll_ptr & 0x7F; + roll_ptr >>= 7; + *is_insert = (ibool) roll_ptr; /* TRUE==1 */ } /***********************************************************************//** @@ -93,16 +92,14 @@ trx_undo_roll_ptr_is_insert( /*========================*/ roll_ptr_t roll_ptr) /*!< in: roll pointer */ { - ulint high; #if DATA_ROLL_PTR_LEN != 7 # error "DATA_ROLL_PTR_LEN != 7" #endif #if TRUE != 1 # error "TRUE != 1" #endif - high = ut_dulint_get_high(roll_ptr); - - return(high / (256 * 256 * 128)); + ut_ad(roll_ptr < (1ULL << 56)); + return((ibool) (roll_ptr >> 55)); } #endif /* !UNIV_HOTBACKUP */ diff --git a/storage/innobase/include/univ.i b/storage/innobase/include/univ.i index 7fb201995e9..8ca1c272b5d 100644 --- a/storage/innobase/include/univ.i +++ b/storage/innobase/include/univ.i @@ -371,8 +371,10 @@ typedef unsigned long long int ullint; /* The 'undefined' value for a ulint */ #define ULINT_UNDEFINED ((ulint)(-1)) +/** The bitmask of 32-bit unsigned integer */ +#define ULINT32_MASK 0xFFFFFFFF /* The undefined 32-bit unsigned integer */ -#define ULINT32_UNDEFINED 0xFFFFFFFF +#define ULINT32_UNDEFINED ULINT32_MASK /* Maximum value for a ulint */ #define ULINT_MAX ((ulint)(-2)) @@ -380,6 +382,9 @@ typedef unsigned long long int ullint; /* Maximum value for ib_uint64_t */ #define IB_ULONGLONG_MAX ((ib_uint64_t) (~0ULL)) +/** The generic InnoDB system object identifier data type */ +typedef ib_uint64_t ib_id_t; + /* This 'ibool' type is used within Innobase. Remember that different included headers may define 'bool' differently. Do not assume that 'bool' is a ulint! */ #define ibool ulint diff --git a/storage/innobase/include/ut0byte.h b/storage/innobase/include/ut0byte.h index f55e2888c60..b99d7175b94 100644 --- a/storage/innobase/include/ut0byte.h +++ b/storage/innobase/include/ut0byte.h @@ -27,145 +27,22 @@ Created 1/20/1994 Heikki Tuuri #define ut0byte_h + #include "univ.i" -/** Pair of ulint integers. */ -typedef struct dulint_struct dulint; -/** Type definition for a 64-bit unsigned integer, which works also -in 32-bit machines. NOTE! Access the fields only with the accessor -functions. This definition appears here only for the compiler to -know the size of a dulint. */ -struct dulint_struct{ - ulint high; /*!< most significant 32 bits */ - ulint low; /*!< least significant 32 bits */ -}; - -/** Zero value for a dulint */ -extern const dulint ut_dulint_zero; - -/** Maximum value for a dulint */ -extern const dulint ut_dulint_max; - /*******************************************************//** -Creates a 64-bit dulint out of two ulints. +Creates a 64-bit integer out of two 32-bit integers. @return created dulint */ UNIV_INLINE -dulint -ut_dulint_create( -/*=============*/ +ib_uint64_t +ut_ull_create( +/*==========*/ ulint high, /*!< in: high-order 32 bits */ - ulint low); /*!< in: low-order 32 bits */ -/*******************************************************//** -Gets the high-order 32 bits of a dulint. -@return 32 bits in ulint */ -UNIV_INLINE -ulint -ut_dulint_get_high( -/*===============*/ - dulint d); /*!< in: dulint */ -/*******************************************************//** -Gets the low-order 32 bits of a dulint. -@return 32 bits in ulint */ -UNIV_INLINE -ulint -ut_dulint_get_low( -/*==============*/ - dulint d); /*!< in: dulint */ -/*******************************************************//** -Converts a dulint (a struct of 2 ulints) to ib_int64_t, which is a 64-bit -integer type. -@return value in ib_int64_t type */ -UNIV_INLINE -ib_int64_t -ut_conv_dulint_to_longlong( -/*=======================*/ - dulint d); /*!< in: dulint */ -/*******************************************************//** -Tests if a dulint is zero. -@return TRUE if zero */ -UNIV_INLINE -ibool -ut_dulint_is_zero( -/*==============*/ - dulint a); /*!< in: dulint */ -/*******************************************************//** -Compares two dulints. -@return -1 if a < b, 0 if a == b, 1 if a > b */ -UNIV_INLINE -int -ut_dulint_cmp( -/*==========*/ - dulint a, /*!< in: dulint */ - dulint b); /*!< in: dulint */ -/*******************************************************//** -Calculates the max of two dulints. -@return max(a, b) */ -UNIV_INLINE -dulint -ut_dulint_get_max( -/*==============*/ - dulint a, /*!< in: dulint */ - dulint b); /*!< in: dulint */ -/*******************************************************//** -Calculates the min of two dulints. -@return min(a, b) */ -UNIV_INLINE -dulint -ut_dulint_get_min( -/*==============*/ - dulint a, /*!< in: dulint */ - dulint b); /*!< in: dulint */ -/*******************************************************//** -Adds a ulint to a dulint. -@return sum a + b */ -UNIV_INLINE -dulint -ut_dulint_add( -/*==========*/ - dulint a, /*!< in: dulint */ - ulint b); /*!< in: ulint */ -/*******************************************************//** -Subtracts a ulint from a dulint. -@return a - b */ -UNIV_INLINE -dulint -ut_dulint_subtract( -/*===============*/ - dulint a, /*!< in: dulint */ - ulint b); /*!< in: ulint, b <= a */ -/*******************************************************//** -Subtracts a dulint from another. NOTE that the difference must be positive -and smaller that 4G. -@return a - b */ -UNIV_INLINE -ulint -ut_dulint_minus( -/*============*/ - dulint a, /*!< in: dulint; NOTE a must be >= b and at most - 2 to power 32 - 1 greater */ - dulint b); /*!< in: dulint */ + ulint low) /*!< in: low-order 32 bits */ + __attribute__((const)); + /********************************************************//** -Rounds a dulint downward to a multiple of a power of 2. -@return rounded value */ -UNIV_INLINE -dulint -ut_dulint_align_down( -/*=================*/ - dulint n, /*!< in: number to be rounded */ - ulint align_no); /*!< in: align by this number which must be a - power of 2 */ -/********************************************************//** -Rounds a dulint upward to a multiple of a power of 2. -@return rounded value */ -UNIV_INLINE -dulint -ut_dulint_align_up( -/*===============*/ - dulint n, /*!< in: number to be rounded */ - ulint align_no); /*!< in: align by this number which must be a - power of 2 */ -/********************************************************//** -Rounds a dulint downward to a multiple of a power of 2. +Rounds a 64-bit integer downward to a multiple of a power of 2. @return rounded value */ UNIV_INLINE ib_uint64_t @@ -184,34 +61,6 @@ ut_uint64_align_up( ib_uint64_t n, /*!< in: number to be rounded */ ulint align_no); /*!< in: align by this number which must be a power of 2 */ -/*******************************************************//** -Increments a dulint variable by 1. */ -#define UT_DULINT_INC(D)\ -{\ - if ((D).low == 0xFFFFFFFFUL) {\ - (D).high = (D).high + 1;\ - (D).low = 0;\ - } else {\ - (D).low = (D).low + 1;\ - }\ -} -/*******************************************************//** -Tests if two dulints are equal. */ -#define UT_DULINT_EQ(D1, D2) (((D1).low == (D2).low)\ - && ((D1).high == (D2).high)) -#ifdef notdefined -/************************************************************//** -Sort function for dulint arrays. */ -UNIV_INTERN -void -ut_dulint_sort( -/*===========*/ - dulint* arr, /*!< in/out: array to be sorted */ - dulint* aux_arr,/*!< in/out: auxiliary array (same size as arr) */ - ulint low, /*!< in: low bound of sort interval, inclusive */ - ulint high); /*!< in: high bound of sort interval, noninclusive */ -#endif /* notdefined */ - /*********************************************************//** The following function rounds up a pointer to the nearest aligned address. @return aligned pointer */ diff --git a/storage/innobase/include/ut0byte.ic b/storage/innobase/include/ut0byte.ic index 3dd51890cb4..73af41bfefa 100644 --- a/storage/innobase/include/ut0byte.ic +++ b/storage/innobase/include/ut0byte.ic @@ -24,260 +24,22 @@ Created 5/30/1994 Heikki Tuuri *******************************************************************/ /*******************************************************//** -Creates a 64-bit dulint out of two ulints. +Creates a 64-bit integer out of two 32-bit integers. @return created dulint */ UNIV_INLINE -dulint -ut_dulint_create( -/*=============*/ +ib_uint64_t +ut_ull_create( +/*==========*/ ulint high, /*!< in: high-order 32 bits */ ulint low) /*!< in: low-order 32 bits */ { - dulint res; - - ut_ad(high <= 0xFFFFFFFF); - ut_ad(low <= 0xFFFFFFFF); - - res.high = high; - res.low = low; - - return(res); -} - -/*******************************************************//** -Gets the high-order 32 bits of a dulint. -@return 32 bits in ulint */ -UNIV_INLINE -ulint -ut_dulint_get_high( -/*===============*/ - dulint d) /*!< in: dulint */ -{ - return(d.high); -} - -/*******************************************************//** -Gets the low-order 32 bits of a dulint. -@return 32 bits in ulint */ -UNIV_INLINE -ulint -ut_dulint_get_low( -/*==============*/ - dulint d) /*!< in: dulint */ -{ - return(d.low); -} - -/*******************************************************//** -Converts a dulint (a struct of 2 ulints) to ib_int64_t, which is a 64-bit -integer type. -@return value in ib_int64_t type */ -UNIV_INLINE -ib_int64_t -ut_conv_dulint_to_longlong( -/*=======================*/ - dulint d) /*!< in: dulint */ -{ - return((ib_int64_t)d.low - + (((ib_int64_t)d.high) << 32)); -} - -/*******************************************************//** -Tests if a dulint is zero. -@return TRUE if zero */ -UNIV_INLINE -ibool -ut_dulint_is_zero( -/*==============*/ - dulint a) /*!< in: dulint */ -{ - if ((a.low == 0) && (a.high == 0)) { - - return(TRUE); - } - - return(FALSE); -} - -/*******************************************************//** -Compares two dulints. -@return -1 if a < b, 0 if a == b, 1 if a > b */ -UNIV_INLINE -int -ut_dulint_cmp( -/*==========*/ - dulint a, /*!< in: dulint */ - dulint b) /*!< in: dulint */ -{ - if (a.high > b.high) { - return(1); - } else if (a.high < b.high) { - return(-1); - } else if (a.low > b.low) { - return(1); - } else if (a.low < b.low) { - return(-1); - } else { - return(0); - } -} - -/*******************************************************//** -Calculates the max of two dulints. -@return max(a, b) */ -UNIV_INLINE -dulint -ut_dulint_get_max( -/*==============*/ - dulint a, /*!< in: dulint */ - dulint b) /*!< in: dulint */ -{ - if (ut_dulint_cmp(a, b) > 0) { - - return(a); - } - - return(b); -} - -/*******************************************************//** -Calculates the min of two dulints. -@return min(a, b) */ -UNIV_INLINE -dulint -ut_dulint_get_min( -/*==============*/ - dulint a, /*!< in: dulint */ - dulint b) /*!< in: dulint */ -{ - if (ut_dulint_cmp(a, b) > 0) { - - return(b); - } - - return(a); -} - -/*******************************************************//** -Adds a ulint to a dulint. -@return sum a + b */ -UNIV_INLINE -dulint -ut_dulint_add( -/*==========*/ - dulint a, /*!< in: dulint */ - ulint b) /*!< in: ulint */ -{ - if (0xFFFFFFFFUL - b >= a.low) { - a.low += b; - - return(a); - } - - a.low = a.low - (0xFFFFFFFFUL - b) - 1; - - a.high++; - - return(a); -} - -/*******************************************************//** -Subtracts a ulint from a dulint. -@return a - b */ -UNIV_INLINE -dulint -ut_dulint_subtract( -/*===============*/ - dulint a, /*!< in: dulint */ - ulint b) /*!< in: ulint, b <= a */ -{ - if (a.low >= b) { - a.low -= b; - - return(a); - } - - b -= a.low + 1; - - a.low = 0xFFFFFFFFUL - b; - - ut_ad(a.high > 0); - - a.high--; - - return(a); -} - -/*******************************************************//** -Subtracts a dulint from another. NOTE that the difference must be positive -and smaller that 4G. -@return a - b */ -UNIV_INLINE -ulint -ut_dulint_minus( -/*============*/ - dulint a, /*!< in: dulint; NOTE a must be >= b and at most - 2 to power 32 - 1 greater */ - dulint b) /*!< in: dulint */ -{ - ulint diff; - - if (a.high == b.high) { - ut_ad(a.low >= b.low); - - return(a.low - b.low); - } - - ut_ad(a.high == b.high + 1); - - diff = (ulint)(0xFFFFFFFFUL - b.low); - diff += 1 + a.low; - - ut_ad(diff > a.low); - - return(diff); + ut_ad(high <= ULINT32_MAX); + ut_ad(low <= ULINT32_MAX); + return(((ib_uint64_t) high) << 32 | low); } /********************************************************//** -Rounds a dulint downward to a multiple of a power of 2. -@return rounded value */ -UNIV_INLINE -dulint -ut_dulint_align_down( -/*=================*/ - dulint n, /*!< in: number to be rounded */ - ulint align_no) /*!< in: align by this number which must be a - power of 2 */ -{ - ulint low, high; - - ut_ad(align_no > 0); - ut_ad(((align_no - 1) & align_no) == 0); - - low = ut_dulint_get_low(n); - high = ut_dulint_get_high(n); - - low = low & ~(align_no - 1); - - return(ut_dulint_create(high, low)); -} - -/********************************************************//** -Rounds a dulint upward to a multiple of a power of 2. -@return rounded value */ -UNIV_INLINE -dulint -ut_dulint_align_up( -/*===============*/ - dulint n, /*!< in: number to be rounded */ - ulint align_no) /*!< in: align by this number which must be a - power of 2 */ -{ - return(ut_dulint_align_down(ut_dulint_add(n, align_no - 1), align_no)); -} - -/********************************************************//** -Rounds ib_uint64_t downward to a multiple of a power of 2. +Rounds a 64-bit integer downward to a multiple of a power of 2. @return rounded value */ UNIV_INLINE ib_uint64_t diff --git a/storage/innobase/include/ut0rnd.h b/storage/innobase/include/ut0rnd.h index ce5152e942f..946b1117af7 100644 --- a/storage/innobase/include/ut0rnd.h +++ b/storage/innobase/include/ut0rnd.h @@ -97,13 +97,13 @@ ut_fold_ulint_pair( ulint n2) /*!< in: ulint */ __attribute__((const)); /*************************************************************//** -Folds a dulint. +Folds a 64-bit integer. @return folded value */ UNIV_INLINE ulint -ut_fold_dulint( -/*===========*/ - dulint d) /*!< in: dulint */ +ut_fold_ull( +/*========*/ + ib_uint64_t d) /*!< in: 64-bit integer */ __attribute__((const)); /*************************************************************//** Folds a character string ending in the null character. diff --git a/storage/innobase/include/ut0rnd.ic b/storage/innobase/include/ut0rnd.ic index c3dbd86923c..a6ba4ec2ba2 100644 --- a/storage/innobase/include/ut0rnd.ic +++ b/storage/innobase/include/ut0rnd.ic @@ -173,16 +173,16 @@ ut_fold_ulint_pair( } /*************************************************************//** -Folds a dulint. +Folds a 64-bit integer. @return folded value */ UNIV_INLINE ulint -ut_fold_dulint( -/*===========*/ - dulint d) /*!< in: dulint */ +ut_fold_ull( +/*========*/ + ib_uint64_t d) /*!< in: 64-bit integer */ { - return(ut_fold_ulint_pair(ut_dulint_get_low(d), - ut_dulint_get_high(d))); + return(ut_fold_ulint_pair((ulint) d & ULINT32_MASK, + (ulint) (d >> 32))); } /*************************************************************//** diff --git a/storage/innobase/lock/lock0lock.c b/storage/innobase/lock/lock0lock.c index 0e57a52666e..dcfca1b6315 100644 --- a/storage/innobase/lock/lock0lock.c +++ b/storage/innobase/lock/lock0lock.c @@ -468,7 +468,7 @@ lock_check_trx_id_sanity( /* A sanity check: the trx_id in rec must be smaller than the global trx id counter */ - if (ut_dulint_cmp(trx_id, trx_sys->max_trx_id) >= 0) { + if (UNIV_UNLIKELY(trx_id >= trx_sys->max_trx_id)) { ut_print_timestamp(stderr); fputs(" InnoDB: Error: transaction id associated" " with record\n", @@ -481,8 +481,7 @@ lock_check_trx_id_sanity( " global trx id counter " TRX_ID_FMT "!\n" "InnoDB: The table is corrupt. You have to do" " dump + drop + reimport.\n", - TRX_ID_PREP_PRINTF(trx_id), - TRX_ID_PREP_PRINTF(trx_sys->max_trx_id)); + (ullint) trx_id, (ullint) trx_sys->max_trx_id); is_ok = FALSE; } @@ -556,9 +555,9 @@ lock_sec_rec_cons_read_sees( } max_trx_id = page_get_max_trx_id(page_align(rec)); - ut_ad(!ut_dulint_is_zero(max_trx_id)); + ut_ad(max_trx_id); - return(ut_dulint_cmp(max_trx_id, view->up_limit_id) < 0); + return(max_trx_id < view->up_limit_id); } /*********************************************************************//** @@ -1594,8 +1593,7 @@ lock_sec_rec_some_has_impl_off_kernel( max trx id to the log, and therefore during recovery, this value for a page may be incorrect. */ - if (!(ut_dulint_cmp(page_get_max_trx_id(page), - trx_list_get_min_trx_id()) >= 0) + if (page_get_max_trx_id(page) < trx_list_get_min_trx_id() && !recv_recovery_is_on()) { return(NULL); @@ -1820,8 +1818,8 @@ lock_rec_enqueue_waiting( #ifdef UNIV_DEBUG if (lock_print_waits) { - fprintf(stderr, "Lock wait for trx %lu in index ", - (ulong) ut_dulint_get_low(trx->id)); + fprintf(stderr, "Lock wait for trx " TRX_ID_FMT " in index ", + (ullint) trx->id); ut_print_name(stderr, trx, FALSE, index->name); } #endif /* UNIV_DEBUG */ @@ -2193,8 +2191,8 @@ lock_grant( #ifdef UNIV_DEBUG if (lock_print_waits) { - fprintf(stderr, "Lock wait for trx %lu ends\n", - (ulong) ut_dulint_get_low(lock->trx->id)); + fprintf(stderr, "Lock wait for trx " TRX_ID_FMT " ends\n", + (ullint) lock->trx->id); } #endif /* UNIV_DEBUG */ @@ -3486,8 +3484,7 @@ lock_deadlock_recursive( } #endif /* UNIV_DEBUG */ - if (trx_weight_cmp(wait_lock->trx, - start) >= 0) { + if (trx_weight_ge(wait_lock->trx, start)) { /* Our recursion starting point transaction is 'smaller', let us choose 'start' as the victim and roll @@ -4023,7 +4020,7 @@ lock_release_off_kernel( ut_ad(lock_get_type_low(lock) & LOCK_TABLE); if (lock_get_mode(lock) != LOCK_IS - && !ut_dulint_is_zero(trx->undo_no)) { + && trx->undo_no != 0) { /* The trx may have modified the table. We block the use of the MySQL query cache for @@ -4222,8 +4219,7 @@ lock_table_print( fputs("TABLE LOCK table ", file); ut_print_name(file, lock->trx, TRUE, lock->un_member.tab_lock.table->name); - fprintf(file, " trx id " TRX_ID_FMT, - TRX_ID_PREP_PRINTF(lock->trx->id)); + fprintf(file, " trx id " TRX_ID_FMT, (ullint) lock->trx->id); if (lock_get_mode(lock) == LOCK_S) { fputs(" lock mode S", file); @@ -4276,8 +4272,7 @@ lock_rec_print( (ulong) space, (ulong) page_no, (ulong) lock_rec_get_n_bits(lock)); dict_index_name_print(file, lock->trx, lock->index); - fprintf(file, " trx id " TRX_ID_FMT, - TRX_ID_PREP_PRINTF(lock->trx->id)); + fprintf(file, " trx id " TRX_ID_FMT, (ullint) lock->trx->id); if (lock_get_mode(lock) == LOCK_S) { fputs(" lock mode S", file); @@ -4412,13 +4407,13 @@ lock_print_info_summary( "------------\n", file); fprintf(file, "Trx id counter " TRX_ID_FMT "\n", - TRX_ID_PREP_PRINTF(trx_sys->max_trx_id)); + (ullint) trx_sys->max_trx_id); fprintf(file, "Purge done for trx's n:o < " TRX_ID_FMT " undo n:o < " TRX_ID_FMT "\n", - TRX_ID_PREP_PRINTF(purge_sys->purge_trx_no), - TRX_ID_PREP_PRINTF(purge_sys->purge_undo_no)); + (ullint) purge_sys->purge_trx_no, + (ullint) purge_sys->purge_undo_no); fprintf(file, "History list length %lu\n", @@ -4495,10 +4490,8 @@ loop: "Trx read view will not see trx with" " id >= " TRX_ID_FMT ", sees < " TRX_ID_FMT "\n", - TRX_ID_PREP_PRINTF( - trx->read_view->low_limit_id), - TRX_ID_PREP_PRINTF( - trx->read_view->up_limit_id)); + (ullint) trx->read_view->low_limit_id, + (ullint) trx->read_view->up_limit_id); } if (trx->que_state == TRX_QUE_LOCK_WAIT) { @@ -4912,12 +4905,12 @@ ibool lock_validate(void) /*===============*/ { - lock_t* lock; - trx_t* trx; - dulint limit; - ulint space; - ulint page_no; - ulint i; + lock_t* lock; + trx_t* trx; + ib_uint64_t limit; + ulint space; + ulint page_no; + ulint i; lock_mutex_enter_kernel(); @@ -4941,20 +4934,21 @@ lock_validate(void) for (i = 0; i < hash_get_n_cells(lock_sys->rec_hash); i++) { - limit = ut_dulint_zero; + limit = 0; for (;;) { lock = HASH_GET_FIRST(lock_sys->rec_hash, i); while (lock) { + ib_uint64_t space_page; ut_a(trx_in_trx_list(lock->trx)); space = lock->un_member.rec_lock.space; page_no = lock->un_member.rec_lock.page_no; - if (ut_dulint_cmp( - ut_dulint_create(space, page_no), - limit) >= 0) { + space_page = ut_ull_create(space, page_no); + + if (space_page >= limit) { break; } @@ -4974,7 +4968,7 @@ lock_validate(void) lock_mutex_enter_kernel(); - limit = ut_dulint_create(space, page_no + 1); + limit = ut_ull_create(space, page_no + 1); } } @@ -5348,8 +5342,7 @@ lock_sec_rec_read_check_and_lock( if the max trx id for the page >= min trx id for the trx list or a database recovery is running. */ - if (((ut_dulint_cmp(page_get_max_trx_id(block->frame), - trx_list_get_min_trx_id()) >= 0) + if ((page_get_max_trx_id(block->frame) >= trx_list_get_min_trx_id() || recv_recovery_is_on()) && !page_rec_is_supremum(rec)) { @@ -5572,12 +5565,12 @@ lock_get_type( Gets the id of the transaction owning a lock. @return transaction id */ UNIV_INTERN -ullint +trx_id_t lock_get_trx_id( /*============*/ const lock_t* lock) /*!< in: lock */ { - return(trx_get_id(lock->trx)); + return(lock->trx->id); } /*******************************************************************//** @@ -5671,7 +5664,7 @@ lock_get_table( Gets the id of the table on which the lock is. @return id of the table */ UNIV_INTERN -ullint +table_id_t lock_get_table_id( /*==============*/ const lock_t* lock) /*!< in: lock */ @@ -5680,7 +5673,7 @@ lock_get_table_id( table = lock_get_table(lock); - return((ullint)ut_conv_dulint_to_longlong(table->id)); + return(table->id); } /*******************************************************************//** diff --git a/storage/innobase/log/log0log.c b/storage/innobase/log/log0log.c index 386f9630baa..401cede1d8f 100644 --- a/storage/innobase/log/log0log.c +++ b/storage/innobase/log/log0log.c @@ -1166,7 +1166,7 @@ log_group_file_header_flush( buf = *(group->file_header_bufs + nth_file); mach_write_to_4(buf + LOG_GROUP_ID, group->id); - mach_write_ull(buf + LOG_FILE_START_LSN, start_lsn); + mach_write_to_8(buf + LOG_FILE_START_LSN, start_lsn); /* Wipe over possible label of ibbackup --restore */ memcpy(buf + LOG_FILE_WAS_CREATED_BY_HOT_BACKUP, " ", 4); @@ -1769,8 +1769,8 @@ log_group_checkpoint( buf = group->checkpoint_buf; - mach_write_ull(buf + LOG_CHECKPOINT_NO, log_sys->next_checkpoint_no); - mach_write_ull(buf + LOG_CHECKPOINT_LSN, log_sys->next_checkpoint_lsn); + mach_write_to_8(buf + LOG_CHECKPOINT_NO, log_sys->next_checkpoint_no); + mach_write_to_8(buf + LOG_CHECKPOINT_LSN, log_sys->next_checkpoint_lsn); mach_write_to_4(buf + LOG_CHECKPOINT_OFFSET, log_group_calc_lsn_offset( @@ -1790,9 +1790,9 @@ log_group_checkpoint( } } - mach_write_ull(buf + LOG_CHECKPOINT_ARCHIVED_LSN, archived_lsn); + mach_write_to_8(buf + LOG_CHECKPOINT_ARCHIVED_LSN, archived_lsn); #else /* UNIV_LOG_ARCHIVE */ - mach_write_ull(buf + LOG_CHECKPOINT_ARCHIVED_LSN, IB_ULONGLONG_MAX); + mach_write_to_8(buf + LOG_CHECKPOINT_ARCHIVED_LSN, IB_ULONGLONG_MAX); #endif /* UNIV_LOG_ARCHIVE */ for (i = 0; i < LOG_MAX_N_GROUPS; i++) { @@ -1884,7 +1884,7 @@ log_reset_first_header_and_checkpoint( ib_uint64_t lsn; mach_write_to_4(hdr_buf + LOG_GROUP_ID, 0); - mach_write_ull(hdr_buf + LOG_FILE_START_LSN, start); + mach_write_to_8(hdr_buf + LOG_FILE_START_LSN, start); lsn = start + LOG_BLOCK_HDR_SIZE; @@ -1896,15 +1896,15 @@ log_reset_first_header_and_checkpoint( + (sizeof "ibbackup ") - 1)); buf = hdr_buf + LOG_CHECKPOINT_1; - mach_write_ull(buf + LOG_CHECKPOINT_NO, 0); - mach_write_ull(buf + LOG_CHECKPOINT_LSN, lsn); + mach_write_to_8(buf + LOG_CHECKPOINT_NO, 0); + mach_write_to_8(buf + LOG_CHECKPOINT_LSN, lsn); mach_write_to_4(buf + LOG_CHECKPOINT_OFFSET, LOG_FILE_HDR_SIZE + LOG_BLOCK_HDR_SIZE); mach_write_to_4(buf + LOG_CHECKPOINT_LOG_BUF_SIZE, 2 * 1024 * 1024); - mach_write_ull(buf + LOG_CHECKPOINT_ARCHIVED_LSN, IB_ULONGLONG_MAX); + mach_write_to_8(buf + LOG_CHECKPOINT_ARCHIVED_LSN, IB_ULONGLONG_MAX); fold = ut_fold_binary(buf, LOG_CHECKPOINT_CHECKSUM_1); mach_write_to_4(buf + LOG_CHECKPOINT_CHECKSUM_1, fold); @@ -2272,7 +2272,7 @@ log_group_archive_file_header_write( buf = *(group->archive_file_header_bufs + nth_file); mach_write_to_4(buf + LOG_GROUP_ID, group->id); - mach_write_ull(buf + LOG_FILE_START_LSN, start_lsn); + mach_write_to_8(buf + LOG_FILE_START_LSN, start_lsn); mach_write_to_4(buf + LOG_FILE_NO, file_no); mach_write_to_4(buf + LOG_FILE_ARCH_COMPLETED, FALSE); @@ -2308,7 +2308,7 @@ log_group_archive_completed_header_write( buf = *(group->archive_file_header_bufs + nth_file); mach_write_to_4(buf + LOG_FILE_ARCH_COMPLETED, TRUE); - mach_write_ull(buf + LOG_FILE_END_LSN, end_lsn); + mach_write_to_8(buf + LOG_FILE_END_LSN, end_lsn); dest_offset = nth_file * group->file_size + LOG_FILE_ARCH_COMPLETED; diff --git a/storage/innobase/log/log0recv.c b/storage/innobase/log/log0recv.c index f47f47e6a68..f983c4fb974 100644 --- a/storage/innobase/log/log0recv.c +++ b/storage/innobase/log/log0recv.c @@ -704,11 +704,11 @@ recv_find_max_checkpoint( group->state = LOG_GROUP_OK; - group->lsn = mach_read_ull( + group->lsn = mach_read_from_8( buf + LOG_CHECKPOINT_LSN); group->lsn_offset = mach_read_from_4( buf + LOG_CHECKPOINT_OFFSET); - checkpoint_no = mach_read_ull( + checkpoint_no = mach_read_from_8( buf + LOG_CHECKPOINT_NO); #ifdef UNIV_DEBUG @@ -778,14 +778,14 @@ recv_read_cp_info_for_backup( cp_buf = hdr + LOG_CHECKPOINT_1; if (recv_check_cp_is_consistent(cp_buf)) { - max_cp_no = mach_read_ull(cp_buf + LOG_CHECKPOINT_NO); + max_cp_no = mach_read_from_8(cp_buf + LOG_CHECKPOINT_NO); max_cp = LOG_CHECKPOINT_1; } cp_buf = hdr + LOG_CHECKPOINT_2; if (recv_check_cp_is_consistent(cp_buf)) { - if (mach_read_ull(cp_buf + LOG_CHECKPOINT_NO) > max_cp_no) { + if (mach_read_from_8(cp_buf + LOG_CHECKPOINT_NO) > max_cp_no) { max_cp = LOG_CHECKPOINT_2; } } @@ -796,7 +796,7 @@ recv_read_cp_info_for_backup( cp_buf = hdr + max_cp; - *lsn = mach_read_ull(cp_buf + LOG_CHECKPOINT_LSN); + *lsn = mach_read_from_8(cp_buf + LOG_CHECKPOINT_LSN); *offset = mach_read_from_4(cp_buf + LOG_CHECKPOINT_OFFSET); /* If the user is running a pre-3.23.50 version of InnoDB, its @@ -816,9 +816,9 @@ recv_read_cp_info_for_backup( /* fprintf(stderr, "fsp limit %lu MB\n", *fsp_limit); */ - *cp_no = mach_read_ull(cp_buf + LOG_CHECKPOINT_NO); + *cp_no = mach_read_from_8(cp_buf + LOG_CHECKPOINT_NO); - *first_header_lsn = mach_read_ull(hdr + LOG_FILE_START_LSN); + *first_header_lsn = mach_read_from_8(hdr + LOG_FILE_START_LSN); return(TRUE); } @@ -1541,7 +1541,7 @@ recv_recover_page_func( #endif /* !UNIV_HOTBACKUP */ /* Read the newest modification lsn from the page */ - page_lsn = mach_read_ull(page + FIL_PAGE_LSN); + page_lsn = mach_read_from_8(page + FIL_PAGE_LSN); #ifndef UNIV_HOTBACKUP /* It may be that the page has been modified in the buffer @@ -1616,14 +1616,14 @@ recv_recover_page_func( block, &mtr); end_lsn = recv->start_lsn + recv->len; - mach_write_ull(FIL_PAGE_LSN + page, end_lsn); - mach_write_ull(UNIV_PAGE_SIZE - - FIL_PAGE_END_LSN_OLD_CHKSUM - + page, end_lsn); + mach_write_to_8(FIL_PAGE_LSN + page, end_lsn); + mach_write_to_8(UNIV_PAGE_SIZE + - FIL_PAGE_END_LSN_OLD_CHKSUM + + page, end_lsn); if (page_zip) { - mach_write_ull(FIL_PAGE_LSN - + page_zip->data, end_lsn); + mach_write_to_8(FIL_PAGE_LSN + + page_zip->data, end_lsn); } } @@ -1995,7 +1995,7 @@ recv_apply_log_recs_for_backup(void) buf_flush_init_for_writing( block->frame, buf_block_get_page_zip(block), - mach_read_ull(block->frame + FIL_PAGE_LSN)); + mach_read_from_8(block->frame + FIL_PAGE_LSN)); if (zip_size) { error = fil_io(OS_FILE_WRITE, TRUE, @@ -2961,9 +2961,9 @@ recv_recovery_from_checkpoint_start_func( buf = log_sys->checkpoint_buf; - checkpoint_lsn = mach_read_ull(buf + LOG_CHECKPOINT_LSN); - checkpoint_no = mach_read_ull(buf + LOG_CHECKPOINT_NO); - archived_lsn = mach_read_ull(buf + LOG_CHECKPOINT_ARCHIVED_LSN); + checkpoint_lsn = mach_read_from_8(buf + LOG_CHECKPOINT_LSN); + checkpoint_no = mach_read_from_8(buf + LOG_CHECKPOINT_NO); + archived_lsn = mach_read_from_8(buf + LOG_CHECKPOINT_ARCHIVED_LSN); /* Read the first log file header to print a note if this is a recovery from a restored InnoDB Hot Backup */ @@ -3613,8 +3613,8 @@ ask_again: return(TRUE); } - start_lsn = mach_read_ull(buf + LOG_FILE_START_LSN); - file_end_lsn = mach_read_ull(buf + LOG_FILE_END_LSN); + start_lsn = mach_read_from_8(buf + LOG_FILE_START_LSN); + file_end_lsn = mach_read_from_8(buf + LOG_FILE_END_LSN); if (!recv_sys->scanned_lsn) { diff --git a/storage/innobase/mach/mach0data.c b/storage/innobase/mach/mach0data.c index e030ce9aadf..7e7a939961d 100644 --- a/storage/innobase/mach/mach0data.c +++ b/storage/innobase/mach/mach0data.c @@ -94,30 +94,31 @@ mach_parse_compressed( } /*********************************************************//** -Reads a dulint in a compressed form if the log record fully contains it. -@return pointer to end of the stored field, NULL if not complete */ +Reads a 64-bit integer in a compressed form +if the log record fully contains it. +@return pointer to end of the stored field, NULL if not complete */ UNIV_INTERN byte* -mach_dulint_parse_compressed( -/*=========================*/ - byte* ptr, /*!< in: pointer to buffer from where to read */ - byte* end_ptr,/*!< in: pointer to end of the buffer */ - dulint* val) /*!< out: read value */ +mach_ull_parse_compressed( +/*======================*/ + byte* ptr, /* in: pointer to buffer from where to read */ + byte* end_ptr,/* in: pointer to end of the buffer */ + ib_uint64_t* val) /* out: read value */ { - ulint high; - ulint low; - ulint size; + ulint size; - ut_ad(ptr && end_ptr && val); + ut_ad(ptr); + ut_ad(end_ptr); + ut_ad(val); if (end_ptr < ptr + 5) { return(NULL); } - high = mach_read_compressed(ptr); + *val = mach_read_compressed(ptr); - size = mach_get_compressed_size(high); + size = mach_get_compressed_size((ulint) *val); ptr += size; @@ -126,9 +127,8 @@ mach_dulint_parse_compressed( return(NULL); } - low = mach_read_from_4(ptr); - - *val = ut_dulint_create(high, low); + *val <<= 32; + *val |= mach_read_from_4(ptr); return(ptr + 4); } diff --git a/storage/innobase/mtr/mtr0log.c b/storage/innobase/mtr/mtr0log.c index 3f3dab36b76..04eeb4391cd 100644 --- a/storage/innobase/mtr/mtr0log.c +++ b/storage/innobase/mtr/mtr0log.c @@ -133,7 +133,7 @@ mlog_parse_initial_log_record( } /********************************************************//** -Parses a log record written by mlog_write_ulint or mlog_write_dulint. +Parses a log record written by mlog_write_ulint or mlog_write_ull. @return parsed record end, NULL if not a complete record or a corrupt record */ UNIV_INTERN byte* @@ -145,9 +145,9 @@ mlog_parse_nbytes( byte* page, /*!< in: page where to apply the log record, or NULL */ void* page_zip)/*!< in/out: compressed page, or NULL */ { - ulint offset; - ulint val; - dulint dval; + ulint offset; + ulint val; + ib_uint64_t dval; ut_a(type <= MLOG_8BYTES); ut_a(!page || !page_zip || fil_page_get_type(page) != FIL_PAGE_INDEX); @@ -167,7 +167,7 @@ mlog_parse_nbytes( } if (type == MLOG_8BYTES) { - ptr = mach_dulint_parse_compressed(ptr, end_ptr, &dval); + ptr = mach_ull_parse_compressed(ptr, end_ptr, &dval); if (ptr == NULL) { @@ -290,11 +290,11 @@ Writes 8 bytes to a file page buffered in the buffer pool. Writes the corresponding log record to the mini-transaction log. */ UNIV_INTERN void -mlog_write_dulint( -/*==============*/ - byte* ptr, /*!< in: pointer where to write */ - dulint val, /*!< in: value to write */ - mtr_t* mtr) /*!< in: mini-transaction handle */ +mlog_write_ull( +/*===========*/ + byte* ptr, /*!< in: pointer where to write */ + ib_uint64_t val, /*!< in: value to write */ + mtr_t* mtr) /*!< in: mini-transaction handle */ { byte* log_ptr; @@ -316,7 +316,7 @@ mlog_write_dulint( mach_write_to_2(log_ptr, page_offset(ptr)); log_ptr += 2; - log_ptr += mach_dulint_write_compressed(log_ptr, val); + log_ptr += mach_ull_write_compressed(log_ptr, val); mlog_close(mtr, log_ptr); } diff --git a/storage/innobase/mtr/mtr0mtr.c b/storage/innobase/mtr/mtr0mtr.c index b01462f6b9b..b38c5b0d63c 100644 --- a/storage/innobase/mtr/mtr0mtr.c +++ b/storage/innobase/mtr/mtr0mtr.c @@ -375,23 +375,6 @@ mtr_read_ulint( } } -/********************************************************//** -Reads 8 bytes from a file page buffered in the buffer pool. -@return value read */ -UNIV_INTERN -dulint -mtr_read_dulint( -/*============*/ - const byte* ptr, /*!< in: pointer from where to read */ - mtr_t* mtr __attribute__((unused))) - /*!< in: mini-transaction handle */ -{ - ut_ad(mtr->state == MTR_ACTIVE); - ut_ad(mtr_memo_contains_page(mtr, ptr, MTR_MEMO_PAGE_S_FIX) - || mtr_memo_contains_page(mtr, ptr, MTR_MEMO_PAGE_X_FIX)); - return(mach_read_from_8(ptr)); -} - #ifdef UNIV_DEBUG # ifndef UNIV_HOTBACKUP /**********************************************************//** diff --git a/storage/innobase/page/page0page.c b/storage/innobase/page/page0page.c index 10008f9ac25..2e785412ac9 100644 --- a/storage/innobase/page/page0page.c +++ b/storage/innobase/page/page0page.c @@ -235,8 +235,8 @@ page_set_max_trx_id( 8, mtr); #ifndef UNIV_HOTBACKUP } else if (mtr) { - mlog_write_dulint(page + (PAGE_HEADER + PAGE_MAX_TRX_ID), - trx_id, mtr); + mlog_write_ull(page + (PAGE_HEADER + PAGE_MAX_TRX_ID), + trx_id, mtr); #endif /* !UNIV_HOTBACKUP */ } else { mach_write_to_8(page + (PAGE_HEADER + PAGE_MAX_TRX_ID), trx_id); @@ -457,7 +457,7 @@ page_create_low( page_header_set_field(page, NULL, PAGE_DIRECTION, PAGE_NO_DIRECTION); page_header_set_field(page, NULL, PAGE_N_DIRECTION, 0); page_header_set_field(page, NULL, PAGE_N_RECS, 0); - page_set_max_trx_id(block, NULL, ut_dulint_zero, NULL); + page_set_max_trx_id(block, NULL, 0, NULL); memset(heap_top, 0, UNIV_PAGE_SIZE - PAGE_EMPTY_DIR_START - page_offset(heap_top)); diff --git a/storage/innobase/page/page0zip.c b/storage/innobase/page/page0zip.c index 1f094334589..98640a8e6fb 100644 --- a/storage/innobase/page/page0zip.c +++ b/storage/innobase/page/page0zip.c @@ -4468,7 +4468,7 @@ page_zip_reorganize( /* Copy max trx id to recreated page */ trx_id_t max_trx_id = page_get_max_trx_id(temp_page); page_set_max_trx_id(block, NULL, max_trx_id, NULL); - ut_ad(!ut_dulint_is_zero(max_trx_id)); + ut_ad(max_trx_id != 0); } /* Restore logging. */ @@ -4528,7 +4528,7 @@ page_zip_copy_recs( /* The PAGE_MAX_TRX_ID must be set on leaf pages of secondary indexes. It does not matter on other pages. */ ut_a(dict_index_is_clust(index) || !page_is_leaf(src) - || !ut_dulint_is_zero(page_get_max_trx_id(src))); + || page_get_max_trx_id(src)); UNIV_MEM_ASSERT_W(page, UNIV_PAGE_SIZE); UNIV_MEM_ASSERT_W(page_zip->data, page_zip_get_size(page_zip)); diff --git a/storage/innobase/pars/pars0pars.c b/storage/innobase/pars/pars0pars.c index 613e7962f0e..ef107f2896f 100644 --- a/storage/innobase/pars/pars0pars.c +++ b/storage/innobase/pars/pars0pars.c @@ -2030,29 +2030,6 @@ pars_info_add_int4_literal( /****************************************************************//** Equivalent to: -char buf[8]; -mach_write_ull(buf, val); -pars_info_add_literal(info, name, buf, 8, DATA_INT, 0); - -except that the buffer is dynamically allocated from the info struct's -heap. */ -UNIV_INTERN -void -pars_info_add_uint64_literal( -/*=========================*/ - pars_info_t* info, /*!< in: info struct */ - const char* name, /*!< in: name */ - ib_uint64_t val) /*!< in: value */ -{ - byte* buf = mem_heap_alloc(info->heap, 8); - - mach_write_ull(buf, val); - pars_info_add_literal(info, name, buf, 8, DATA_INT, 0); -} - -/****************************************************************//** -Equivalent to: - char buf[8]; mach_write_to_8(buf, val); pars_info_add_literal(info, name, buf, 8, DATA_FIXBINARY, 0); @@ -2061,11 +2038,11 @@ except that the buffer is dynamically allocated from the info struct's heap. */ UNIV_INTERN void -pars_info_add_dulint_literal( -/*=========================*/ +pars_info_add_ull_literal( +/*======================*/ pars_info_t* info, /*!< in: info struct */ const char* name, /*!< in: name */ - dulint val) /*!< in: value */ + ib_uint64_t val) /*!< in: value */ { byte* buf = mem_heap_alloc(info->heap, 8); diff --git a/storage/innobase/read/read0read.c b/storage/innobase/read/read0read.c index 85adae4ddff..9975b8c2c57 100644 --- a/storage/innobase/read/read0read.c +++ b/storage/innobase/read/read0read.c @@ -168,8 +168,7 @@ read_view_t* read_view_oldest_copy_or_open_new( /*==============================*/ trx_id_t cr_trx_id, /*!< in: trx_id of creating - transaction, or ut_dulint_zero - used in purge */ + transaction, or 0 used in purge */ mem_heap_t* heap) /*!< in: memory heap from which allocated */ { @@ -191,7 +190,7 @@ read_view_oldest_copy_or_open_new( n = old_view->n_trx_ids; - if (!ut_dulint_is_zero(old_view->creator_trx_id)) { + if (old_view->creator_trx_id) { n++; } else { needs_insert = FALSE; @@ -206,9 +205,8 @@ read_view_oldest_copy_or_open_new( while (i < n) { if (needs_insert && (i >= old_view->n_trx_ids - || ut_dulint_cmp(old_view->creator_trx_id, - read_view_get_nth_trx_id(old_view, i)) - > 0)) { + || old_view->creator_trx_id + > read_view_get_nth_trx_id(old_view, i))) { read_view_set_nth_trx_id(view_copy, i, old_view->creator_trx_id); @@ -252,8 +250,7 @@ read_view_t* read_view_open_now( /*===============*/ trx_id_t cr_trx_id, /*!< in: trx_id of creating - transaction, or ut_dulint_zero - used in purge */ + transaction, or 0 used in purge */ mem_heap_t* heap) /*!< in: memory heap from which allocated */ { @@ -267,7 +264,7 @@ read_view_open_now( view->creator_trx_id = cr_trx_id; view->type = VIEW_NORMAL; - view->undo_no = ut_dulint_zero; + view->undo_no = 0; /* No future transactions should be visible in the view */ @@ -280,7 +277,7 @@ read_view_open_now( /* No active transaction should be visible, except cr_trx */ while (trx) { - if (ut_dulint_cmp(trx->id, cr_trx_id) != 0 + if (trx->id != cr_trx_id && (trx->conc_state == TRX_ACTIVE || trx->conc_state == TRX_PREPARED)) { @@ -292,9 +289,9 @@ read_view_open_now( trx_sys->max_trx_id can still be active, if it is in the middle of its commit! Note that when a transaction starts, we initialize trx->no to - ut_dulint_max. */ + IB_ULONGLONG_MAX. */ - if (ut_dulint_cmp(view->low_limit_no, trx->no) > 0) { + if (view->low_limit_no > trx->no) { view->low_limit_no = trx->no; } @@ -367,22 +364,20 @@ read_view_print( if (view->type == VIEW_HIGH_GRANULARITY) { fprintf(stderr, - "High-granularity read view undo_n:o %lu %lu\n", - (ulong) ut_dulint_get_high(view->undo_no), - (ulong) ut_dulint_get_low(view->undo_no)); + "High-granularity read view undo_n:o %llu\n", + (ullint) view->undo_no); } else { fprintf(stderr, "Normal read view\n"); } - fprintf(stderr, "Read view low limit trx n:o %lu %lu\n", - (ulong) ut_dulint_get_high(view->low_limit_no), - (ulong) ut_dulint_get_low(view->low_limit_no)); + fprintf(stderr, "Read view low limit trx n:o " TRX_ID_FMT "\n", + (ullint) view->low_limit_no); fprintf(stderr, "Read view up limit trx id " TRX_ID_FMT "\n", - TRX_ID_PREP_PRINTF(view->up_limit_id)); + (ullint) view->up_limit_id); fprintf(stderr, "Read view low limit trx id " TRX_ID_FMT "\n", - TRX_ID_PREP_PRINTF(view->low_limit_id)); + (ullint) view->low_limit_id); fprintf(stderr, "Read view individually stored trx ids:\n"); @@ -390,8 +385,7 @@ read_view_print( for (i = 0; i < n_ids; i++) { fprintf(stderr, "Read view trx id " TRX_ID_FMT "\n", - TRX_ID_PREP_PRINTF( - read_view_get_nth_trx_id(view, i))); + (ullint) read_view_get_nth_trx_id(view, i)); } } @@ -460,9 +454,9 @@ read_cursor_view_create_for_mysql( trx_sys->max_trx_id can still be active, if it is in the middle of its commit! Note that when a transaction starts, we initialize trx->no to - ut_dulint_max. */ + IB_ULONGLONG_MAX. */ - if (ut_dulint_cmp(view->low_limit_no, trx->no) > 0) { + if (view->low_limit_no > trx->no) { view->low_limit_no = trx->no; } diff --git a/storage/innobase/row/row0ins.c b/storage/innobase/row/row0ins.c index 1880747e21a..241f652d0bf 100644 --- a/storage/innobase/row/row0ins.c +++ b/storage/innobase/row/row0ins.c @@ -87,7 +87,7 @@ ins_node_create( node->select = NULL; - node->trx_id = ut_dulint_zero; + node->trx_id = 0; node->entry_sys_heap = mem_heap_create(128); @@ -207,7 +207,7 @@ ins_node_set_new_row( /* As we allocated a new trx id buf, the trx id should be written there again: */ - node->trx_id = ut_dulint_zero; + node->trx_id = 0; } /*******************************************************************//** @@ -2279,7 +2279,7 @@ row_ins_alloc_row_id_step( /*======================*/ ins_node_t* node) /*!< in: row insert node */ { - dulint row_id; + row_id_t row_id; ut_ad(node->state == INS_NODE_ALLOC_ROW_ID); @@ -2466,7 +2466,7 @@ row_ins_step( /* It may be that the current session has not yet started its transaction, or it has been committed: */ - if (UT_DULINT_EQ(trx->id, node->trx_id)) { + if (trx->id == node->trx_id) { /* No need to do IX-locking */ goto same_trx; diff --git a/storage/innobase/row/row0merge.c b/storage/innobase/row/row0merge.c index aab13742532..8aa64eb82c0 100644 --- a/storage/innobase/row/row0merge.c +++ b/storage/innobase/row/row0merge.c @@ -2023,7 +2023,7 @@ row_merge_drop_index( ut_ad(index && table && trx); - pars_info_add_dulint_literal(info, "indexid", index->id); + pars_info_add_ull_literal(info, "indexid", index->id); trx_start_if_not_started(trx); trx->op_info = "dropping index"; @@ -2093,7 +2093,7 @@ row_merge_drop_temp_indexes(void) const rec_t* rec; const byte* field; ulint len; - dulint table_id; + table_id_t table_id; dict_table_t* table; btr_pcur_move_to_next_user_rec(&pcur, &mtr); @@ -2320,7 +2320,7 @@ row_merge_rename_indexes( trx->op_info = "renaming indexes"; - pars_info_add_dulint_literal(info, "tableid", table->id); + pars_info_add_ull_literal(info, "tableid", table->id); err = que_eval_sql(info, rename_indexes, FALSE, trx); @@ -2508,8 +2508,7 @@ row_merge_create_index( /* Note the id of the transaction that created this index, we use it to restrict readers from accessing this index, to ensure read consistency. */ - index->trx_id = (ib_uint64_t) - ut_conv_dulint_to_longlong(trx->id); + index->trx_id = trx->id; } else { index = NULL; } @@ -2526,10 +2525,8 @@ row_merge_is_index_usable( const trx_t* trx, /*!< in: transaction */ const dict_index_t* index) /*!< in: index to check */ { - return(!trx->read_view || read_view_sees_trx_id( - trx->read_view, - ut_dulint_create((ulint) (index->trx_id >> 32), - (ulint) index->trx_id & 0xFFFFFFFF))); + return(!trx->read_view + || read_view_sees_trx_id(trx->read_view, index->trx_id)); } /*********************************************************************//** diff --git a/storage/innobase/row/row0mysql.c b/storage/innobase/row/row0mysql.c index 1f7f98a59a2..1b11987a7f9 100644 --- a/storage/innobase/row/row0mysql.c +++ b/storage/innobase/row/row0mysql.c @@ -1532,7 +1532,7 @@ row_unlock_for_mysql( } } - if (ut_dulint_cmp(rec_trx_id, trx->id) != 0) { + if (rec_trx_id != trx->id) { /* We did not update the record: unlock it */ rec = btr_pcur_get_rec(pcur); @@ -2283,7 +2283,7 @@ row_discard_tablespace_for_mysql( trx_t* trx) /*!< in: transaction handle */ { dict_foreign_t* foreign; - dulint new_id; + table_id_t new_id; dict_table_t* table; ibool success; ulint err; @@ -2405,7 +2405,7 @@ row_discard_tablespace_for_mysql( info = pars_info_create(); pars_info_add_str_literal(info, "table_name", name); - pars_info_add_dulint_literal(info, "new_id", new_id); + pars_info_add_ull_literal(info, "new_id", new_id); err = que_eval_sql(info, "PROCEDURE DISCARD_TABLESPACE_PROC () IS\n" @@ -2619,7 +2619,7 @@ row_truncate_table_for_mysql( dict_index_t* sys_index; btr_pcur_t pcur; mtr_t mtr; - dulint new_id; + table_id_t new_id; ulint recreate_space = 0; pars_info_t* info = NULL; @@ -2873,8 +2873,8 @@ next_rec: info = pars_info_create(); pars_info_add_int4_literal(info, "space", (lint) table->space); - pars_info_add_dulint_literal(info, "old_id", table->id); - pars_info_add_dulint_literal(info, "new_id", new_id); + pars_info_add_ull_literal(info, "old_id", table->id); + pars_info_add_ull_literal(info, "new_id", new_id); err = que_eval_sql(info, "PROCEDURE RENUMBER_TABLESPACE_PROC () IS\n" diff --git a/storage/innobase/row/row0purge.c b/storage/innobase/row/row0purge.c index 4b1bc258cea..fe7a3e0236e 100644 --- a/storage/innobase/row/row0purge.c +++ b/storage/innobase/row/row0purge.c @@ -151,10 +151,9 @@ row_purge_remove_clust_if_poss_low( rec = btr_pcur_get_rec(pcur); - if (0 != ut_dulint_cmp(node->roll_ptr, row_get_rec_roll_ptr( - rec, index, rec_get_offsets( - rec, index, offsets_, - ULINT_UNDEFINED, &heap)))) { + if (node->roll_ptr != row_get_rec_roll_ptr( + rec, index, rec_get_offsets(rec, index, offsets_, + ULINT_UNDEFINED, &heap))) { if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } @@ -617,7 +616,7 @@ row_purge_parse_undo_rec( byte* ptr; trx_t* trx; undo_no_t undo_no; - dulint table_id; + table_id_t table_id; trx_id_t trx_id; roll_ptr_t roll_ptr; ulint info_bits; diff --git a/storage/innobase/row/row0sel.c b/storage/innobase/row/row0sel.c index 2861235a995..01742265ae2 100644 --- a/storage/innobase/row/row0sel.c +++ b/storage/innobase/row/row0sel.c @@ -4622,8 +4622,7 @@ row_search_check_if_query_cache_permitted( IX type locks actually would require ret = FALSE. */ if (UT_LIST_GET_LEN(table->locks) == 0 - && ut_dulint_cmp(trx->id, - table->query_cache_inv_trx_id) >= 0) { + && trx->id >= table->query_cache_inv_trx_id) { ret = TRUE; diff --git a/storage/innobase/row/row0uins.c b/storage/innobase/row/row0uins.c index 4d644e9de50..d25afed3840 100644 --- a/storage/innobase/row/row0uins.c +++ b/storage/innobase/row/row0uins.c @@ -78,7 +78,7 @@ row_undo_ins_remove_clust_rec( &mtr); ut_a(success); - if (ut_dulint_cmp(node->table->id, DICT_INDEXES_ID) == 0) { + if (node->table->id == DICT_INDEXES_ID) { ut_ad(node->trx->dict_operation_lock_mode == RW_X_LATCH); /* Drop the index tree associated with the row in @@ -260,7 +260,7 @@ row_undo_ins_parse_undo_rec( dict_index_t* clust_index; byte* ptr; undo_no_t undo_no; - dulint table_id; + table_id_t table_id; ulint type; ulint dummy; ibool dummy_extern; diff --git a/storage/innobase/row/row0umod.c b/storage/innobase/row/row0umod.c index a49e7a68595..aebff0764c8 100644 --- a/storage/innobase/row/row0umod.c +++ b/storage/innobase/row/row0umod.c @@ -85,9 +85,9 @@ row_undo_mod_undo_also_prev_vers( trx = node->trx; - if (0 != ut_dulint_cmp(node->new_trx_id, trx->id)) { + if (node->new_trx_id != trx->id) { - *undo_no = ut_dulint_zero; + *undo_no = 0; return(FALSE); } @@ -95,7 +95,7 @@ row_undo_mod_undo_also_prev_vers( *undo_no = trx_undo_rec_get_undo_no(undo_rec); - return(ut_dulint_cmp(trx->roll_limit, *undo_no) <= 0); + return(trx->roll_limit <= *undo_no); } /***********************************************************//** @@ -790,7 +790,7 @@ row_undo_mod_parse_undo_rec( dict_index_t* clust_index; byte* ptr; undo_no_t undo_no; - dulint table_id; + table_id_t table_id; trx_id_t trx_id; roll_ptr_t roll_ptr; ulint info_bits; diff --git a/storage/innobase/row/row0undo.c b/storage/innobase/row/row0undo.c index 3d739c9689a..ca321c269c7 100644 --- a/storage/innobase/row/row0undo.c +++ b/storage/innobase/row/row0undo.c @@ -185,9 +185,8 @@ row_undo_search_clust_to_pcur( offsets = rec_get_offsets(rec, clust_index, offsets, ULINT_UNDEFINED, &heap); - if (!found || 0 != ut_dulint_cmp(node->roll_ptr, - row_get_rec_roll_ptr(rec, clust_index, - offsets))) { + if (!found || node->roll_ptr + != row_get_rec_roll_ptr(rec, clust_index, offsets)) { /* We must remove the reservation on the undo log record BEFORE releasing the latch on the clustered index page: this diff --git a/storage/innobase/row/row0upd.c b/storage/innobase/row/row0upd.c index 39d5036da0d..fd689c58b7c 100644 --- a/storage/innobase/row/row0upd.c +++ b/storage/innobase/row/row0upd.c @@ -377,7 +377,7 @@ row_upd_index_entry_sys_field( them */ dict_index_t* index, /*!< in: clustered index */ ulint type, /*!< in: DATA_TRX_ID or DATA_ROLL_PTR */ - dulint val) /*!< in: value to write */ + ib_uint64_t val) /*!< in: value to write */ { dfield_t* dfield; byte* field; @@ -536,7 +536,7 @@ row_upd_write_sys_vals_to_log( trx_write_roll_ptr(log_ptr, roll_ptr); log_ptr += DATA_ROLL_PTR_LEN; - log_ptr += mach_dulint_write_compressed(log_ptr, trx->id); + log_ptr += mach_ull_write_compressed(log_ptr, trx->id); return(log_ptr); } @@ -570,7 +570,7 @@ row_upd_parse_sys_vals( *roll_ptr = trx_read_roll_ptr(ptr); ptr += DATA_ROLL_PTR_LEN; - ptr = mach_dulint_parse_compressed(ptr, end_ptr, trx_id); + ptr = mach_ull_parse_compressed(ptr, end_ptr, trx_id); return(ptr); } @@ -1912,8 +1912,7 @@ row_upd_clust_step( then we have to free the file segments of the index tree associated with the index */ - if (node->is_delete - && ut_dulint_cmp(node->table->id, DICT_INDEXES_ID) == 0) { + if (node->is_delete && node->table->id == DICT_INDEXES_ID) { dict_drop_index_tree(btr_pcur_get_rec(pcur), mtr); diff --git a/storage/innobase/row/row0vers.c b/storage/innobase/row/row0vers.c index a4fbb5289aa..3e6fc3f18b6 100644 --- a/storage/innobase/row/row0vers.c +++ b/storage/innobase/row/row0vers.c @@ -209,7 +209,7 @@ row_vers_impl_x_locked_off_kernel( prev_trx_id must have already committed for the trx_id to be able to modify the row. Therefore, prev_trx_id cannot hold any implicit lock. */ - if (vers_del && 0 != ut_dulint_cmp(trx_id, prev_trx_id)) { + if (vers_del && trx_id != prev_trx_id) { mutex_enter(&kernel_mutex); break; @@ -280,7 +280,7 @@ row_vers_impl_x_locked_off_kernel( break; } - if (0 != ut_dulint_cmp(trx_id, prev_trx_id)) { + if (trx_id != prev_trx_id) { /* The versions modified by the trx_id transaction end to prev_version: no implicit x-lock */ @@ -533,7 +533,7 @@ row_vers_build_for_consistent_read( undo_no of the record is < undo_no in the view. */ if (view->type == VIEW_HIGH_GRANULARITY - && ut_dulint_cmp(view->creator_trx_id, trx_id) == 0) { + && view->creator_trx_id == trx_id) { roll_ptr = row_get_rec_roll_ptr(version, index, *offsets); @@ -541,7 +541,7 @@ row_vers_build_for_consistent_read( undo_no = trx_undo_rec_get_undo_no(undo_rec); mem_heap_empty(heap); - if (ut_dulint_cmp(view->undo_no, undo_no) > 0) { + if (view->undo_no > undo_no) { /* The view already sees this version: we can copy it to in_heap and return */ @@ -632,7 +632,7 @@ row_vers_build_for_semi_consistent_read( mem_heap_t* heap = NULL; byte* buf; ulint err; - trx_id_t rec_trx_id = ut_dulint_zero; + trx_id_t rec_trx_id = 0; ut_ad(dict_index_is_clust(index)); ut_ad(mtr_memo_contains_page(mtr, rec, MTR_MEMO_PAGE_X_FIX) @@ -684,7 +684,7 @@ row_vers_build_for_semi_consistent_read( rolled back and the transaction is removed from the global list of transactions. */ - if (!ut_dulint_cmp(rec_trx_id, version_trx_id)) { + if (rec_trx_id == version_trx_id) { /* The transaction was committed while we searched for earlier versions. Return the current version as a diff --git a/storage/innobase/trx/trx0i_s.c b/storage/innobase/trx/trx0i_s.c index 8b719646023..1ad074769c7 100644 --- a/storage/innobase/trx/trx0i_s.c +++ b/storage/innobase/trx/trx0i_s.c @@ -444,7 +444,7 @@ fill_trx_row( ut_ad(mutex_own(&kernel_mutex)); - row->trx_id = trx_get_id(trx); + row->trx_id = trx->id; row->trx_started = (ib_time_t) trx->start_time; row->trx_state = trx_get_que_state_str(trx); @@ -462,7 +462,7 @@ fill_trx_row( row->trx_wait_started = 0; } - row->trx_weight = (ullint) ut_conv_dulint_to_longlong(TRX_WEIGHT(trx)); + row->trx_weight = (ullint) TRX_WEIGHT(trx); if (trx->mysql_thd == NULL) { /* For internal transactions e.g., purge and transactions @@ -527,7 +527,7 @@ thd_done: row->trx_rows_locked = lock_number_of_rows_locked(trx); - row->trx_rows_modified = ut_conv_dulint_to_longlong(trx->undo_no); + row->trx_rows_modified = trx->undo_no; row->trx_concurrency_tickets = trx->n_tickets_to_enter_innodb; diff --git a/storage/innobase/trx/trx0purge.c b/storage/innobase/trx/trx0purge.c index 550a8c9c4b3..e17ed547050 100644 --- a/storage/innobase/trx/trx0purge.c +++ b/storage/innobase/trx/trx0purge.c @@ -145,47 +145,44 @@ void trx_purge_arr_get_biggest( /*======================*/ trx_undo_arr_t* arr, /*!< in: purge array */ - trx_id_t* trx_no, /*!< out: transaction number: ut_dulint_zero + trx_id_t* trx_no, /*!< out: transaction number: 0 if array is empty */ undo_no_t* undo_no)/*!< out: undo number */ { trx_undo_inf_t* cell; trx_id_t pair_trx_no; undo_no_t pair_undo_no; - int trx_cmp; - ulint n_used; ulint i; ulint n; - n = 0; - n_used = arr->n_used; - pair_trx_no = ut_dulint_zero; - pair_undo_no = ut_dulint_zero; + n = arr->n_used; + pair_trx_no = 0; + pair_undo_no = 0; - for (i = 0;; i++) { - cell = trx_undo_arr_get_nth_info(arr, i); + if (n) { + for (i = 0;; i++) { + cell = trx_undo_arr_get_nth_info(arr, i); - if (cell->in_use) { - n++; - trx_cmp = ut_dulint_cmp(cell->trx_no, pair_trx_no); + if (!cell->in_use) { + continue; + } - if ((trx_cmp > 0) - || ((trx_cmp == 0) - && (ut_dulint_cmp(cell->undo_no, - pair_undo_no) >= 0))) { + if ((cell->trx_no > pair_trx_no) + || ((cell->trx_no == pair_trx_no) + && cell->undo_no >= pair_undo_no)) { pair_trx_no = cell->trx_no; pair_undo_no = cell->undo_no; } - } - if (n == n_used) { - *trx_no = pair_trx_no; - *undo_no = pair_undo_no; - - return; + if (!--n) { + break; + } } } + + *trx_no = pair_trx_no; + *undo_no = pair_undo_no; } /****************************************************************//** @@ -233,8 +230,8 @@ trx_purge_sys_create(void) purge_sys->n_pages_handled = 0; - purge_sys->purge_trx_no = ut_dulint_zero; - purge_sys->purge_undo_no = ut_dulint_zero; + purge_sys->purge_trx_no = 0; + purge_sys->purge_undo_no = 0; purge_sys->next_stored = FALSE; rw_lock_create(trx_purge_latch_key, @@ -257,7 +254,7 @@ trx_purge_sys_create(void) purge_sys->query = trx_purge_graph_build(); - purge_sys->view = read_view_oldest_copy_or_open_new(ut_dulint_zero, + purge_sys->view = read_view_oldest_copy_or_open_new(0, purge_sys->heap); } @@ -370,7 +367,7 @@ trx_purge_add_update_undo_to_history( } /* Write the trx number to the undo log header */ - mlog_write_dulint(undo_header + TRX_UNDO_TRX_NO, trx->no, mtr); + mlog_write_ull(undo_header + TRX_UNDO_TRX_NO, trx->no, mtr); /* Write information about delete markings to the undo log header */ if (!undo->del_marks) { @@ -512,9 +509,9 @@ trx_purge_truncate_rseg_history( page_t* undo_page; trx_ulogf_t* log_hdr; trx_usegf_t* seg_hdr; - int cmp; ulint n_removed_logs = 0; mtr_t mtr; + trx_id_t undo_trx_no; ut_ad(mutex_own(&(purge_sys->mutex))); @@ -540,15 +537,16 @@ loop: hdr_addr.page, &mtr); log_hdr = undo_page + hdr_addr.boffset; + undo_trx_no = mach_read_from_8(log_hdr + TRX_UNDO_TRX_NO); - cmp = ut_dulint_cmp(mach_read_from_8(log_hdr + TRX_UNDO_TRX_NO), - limit_trx_no); - if (cmp == 0) { - trx_undo_truncate_start(rseg, rseg->space, hdr_addr.page, - hdr_addr.boffset, limit_undo_no); - } + if (undo_trx_no >= limit_trx_no) { + if (undo_trx_no == limit_trx_no) { + trx_undo_truncate_start(rseg, rseg->space, + hdr_addr.page, + hdr_addr.boffset, + limit_undo_no); + } - if (cmp >= 0) { mutex_enter(&kernel_mutex); ut_a(trx_sys->rseg_history_len >= n_removed_logs); trx_sys->rseg_history_len -= n_removed_logs; @@ -614,7 +612,7 @@ trx_purge_truncate_history(void) trx_purge_arr_get_biggest(purge_sys->arr, &limit_trx_no, &limit_undo_no); - if (ut_dulint_is_zero(limit_trx_no)) { + if (limit_trx_no == 0) { limit_trx_no = purge_sys->purge_trx_no; limit_undo_no = purge_sys->purge_undo_no; @@ -623,13 +621,12 @@ trx_purge_truncate_history(void) /* We play safe and set the truncate limit at most to the purge view low_limit number, though this is not necessary */ - if (ut_dulint_cmp(limit_trx_no, purge_sys->view->low_limit_no) >= 0) { + if (limit_trx_no >= purge_sys->view->low_limit_no) { limit_trx_no = purge_sys->view->low_limit_no; - limit_undo_no = ut_dulint_zero; + limit_undo_no = 0; } - ut_ad((ut_dulint_cmp(limit_trx_no, - purge_sys->view->low_limit_no) <= 0)); + ut_ad(limit_trx_no <= purge_sys->view->low_limit_no); rseg = UT_LIST_GET_FIRST(trx_sys->rseg_list); @@ -684,8 +681,8 @@ trx_purge_rseg_get_next_history_log( ut_a(rseg->last_page_no != FIL_NULL); - purge_sys->purge_trx_no = ut_dulint_add(rseg->last_trx_no, 1); - purge_sys->purge_undo_no = ut_dulint_zero; + purge_sys->purge_trx_no = rseg->last_trx_no + 1; + purge_sys->purge_undo_no = 0; purge_sys->next_stored = FALSE; mtr_start(&mtr); @@ -787,7 +784,7 @@ trx_purge_choose_next_log(void) rseg = UT_LIST_GET_FIRST(trx_sys->rseg_list); - min_trx_no = ut_dulint_max; + min_trx_no = IB_ULONGLONG_MAX; min_rseg = NULL; @@ -796,9 +793,8 @@ trx_purge_choose_next_log(void) if (rseg->last_page_no != FIL_NULL) { - if ((min_rseg == NULL) - || (ut_dulint_cmp(min_trx_no, - rseg->last_trx_no) > 0)) { + if (min_rseg == NULL + || min_trx_no > rseg->last_trx_no) { min_rseg = rseg; min_trx_no = rseg->last_trx_no; @@ -848,7 +844,7 @@ trx_purge_choose_next_log(void) if (rec == &trx_purge_dummy_rec) { - purge_sys->purge_undo_no = ut_dulint_zero; + purge_sys->purge_undo_no = 0; purge_sys->page_no = page_no; purge_sys->offset = 0; } else { @@ -1041,8 +1037,7 @@ trx_purge_fetch_next_rec( return(NULL); } - if (ut_dulint_cmp(purge_sys->purge_trx_no, - purge_sys->view->low_limit_no) >= 0) { + if (purge_sys->purge_trx_no >= purge_sys->view->low_limit_no) { purge_sys->state = TRX_STOP_PURGE; trx_purge_truncate_if_arr_empty(); @@ -1052,10 +1047,10 @@ trx_purge_fetch_next_rec( return(NULL); } - /* fprintf(stderr, "Thread %lu purging trx %lu undo record %lu\n", + /* fprintf(stderr, "Thread %lu purging trx %llu undo record %llu\n", os_thread_get_curr_id(), - ut_dulint_get_low(purge_sys->purge_trx_no), - ut_dulint_get_low(purge_sys->purge_undo_no)); */ + (ullint) purge_sys->purge_trx_no, + (ullint) purge_sys->purge_undo_no); */ *roll_ptr = trx_undo_build_roll_ptr(FALSE, (purge_sys->rseg)->id, purge_sys->page_no, @@ -1064,8 +1059,7 @@ trx_purge_fetch_next_rec( *cell = trx_purge_arr_store_info(purge_sys->purge_trx_no, purge_sys->purge_undo_no); - ut_ad(ut_dulint_cmp(purge_sys->purge_trx_no, - (purge_sys->view)->low_limit_no) < 0); + ut_ad(purge_sys->purge_trx_no < purge_sys->view->low_limit_no); /* The following call will advance the stored values of purge_trx_no and purge_undo_no, therefore we had to store them first */ @@ -1157,7 +1151,7 @@ trx_purge( } } - purge_sys->view = read_view_oldest_copy_or_open_new(ut_dulint_zero, + purge_sys->view = read_view_oldest_copy_or_open_new(0, purge_sys->heap); mutex_exit(&kernel_mutex); @@ -1215,8 +1209,8 @@ trx_purge_sys_print(void) fprintf(stderr, "InnoDB: Purge trx n:o " TRX_ID_FMT ", undo n:o " TRX_ID_FMT "\n", - TRX_ID_PREP_PRINTF(purge_sys->purge_trx_no), - TRX_ID_PREP_PRINTF(purge_sys->purge_undo_no)); + (ullint) purge_sys->purge_trx_no, + (ullint) purge_sys->purge_undo_no); fprintf(stderr, "InnoDB: Purge next stored %lu, page_no %lu, offset %lu,\n" "InnoDB: Purge hdr_page_no %lu, hdr_offset %lu\n", diff --git a/storage/innobase/trx/trx0rec.c b/storage/innobase/trx/trx0rec.c index f50e10ed756..e7e9a008db4 100644 --- a/storage/innobase/trx/trx0rec.c +++ b/storage/innobase/trx/trx0rec.c @@ -242,8 +242,8 @@ trx_undo_page_report_insert( /* Store first some general parameters to the undo log */ *ptr++ = TRX_UNDO_INSERT_REC; - ptr += mach_dulint_write_much_compressed(ptr, trx->undo_no); - ptr += mach_dulint_write_much_compressed(ptr, index->table->id); + ptr += mach_ull_write_much_compressed(ptr, trx->undo_no); + ptr += mach_ull_write_much_compressed(ptr, index->table->id); /*----------------------------------------*/ /* Store then the fields required to uniquely determine the record to be inserted in the clustered index */ @@ -289,7 +289,7 @@ trx_undo_rec_get_pars( ibool* updated_extern, /*!< out: TRUE if we updated an externally stored fild */ undo_no_t* undo_no, /*!< out: undo log record number */ - dulint* table_id) /*!< out: table id */ + table_id_t* table_id) /*!< out: table id */ { byte* ptr; ulint type_cmpl; @@ -309,11 +309,11 @@ trx_undo_rec_get_pars( *type = type_cmpl & (TRX_UNDO_CMPL_INFO_MULT - 1); *cmpl_info = type_cmpl / TRX_UNDO_CMPL_INFO_MULT; - *undo_no = mach_dulint_read_much_compressed(ptr); - ptr += mach_dulint_get_much_compressed_size(*undo_no); + *undo_no = mach_ull_read_much_compressed(ptr); + ptr += mach_ull_get_much_compressed_size(*undo_no); - *table_id = mach_dulint_read_much_compressed(ptr); - ptr += mach_dulint_get_much_compressed_size(*table_id); + *table_id = mach_ull_read_much_compressed(ptr); + ptr += mach_ull_get_much_compressed_size(*table_id); return(ptr); } @@ -598,9 +598,9 @@ trx_undo_page_report_modify( type_cmpl_ptr = ptr; *ptr++ = (byte) type_cmpl; - ptr += mach_dulint_write_much_compressed(ptr, trx->undo_no); + ptr += mach_ull_write_much_compressed(ptr, trx->undo_no); - ptr += mach_dulint_write_much_compressed(ptr, table->id); + ptr += mach_ull_write_much_compressed(ptr, table->id); /*----------------------------------------*/ /* Store the state of the info bits */ @@ -620,16 +620,16 @@ trx_undo_page_report_modify( by some other trx as it must have committed by now for us to allow an over-write. */ if (ignore_prefix) { - ignore_prefix = ut_dulint_cmp(trx_id, trx->id) != 0; + ignore_prefix = (trx_id != trx->id); } - ptr += mach_dulint_write_compressed(ptr, trx_id); + ptr += mach_ull_write_compressed(ptr, trx_id); field = rec_get_nth_field(rec, offsets, dict_index_get_sys_col_pos( index, DATA_ROLL_PTR), &flen); ut_ad(flen == DATA_ROLL_PTR_LEN); - ptr += mach_dulint_write_compressed(ptr, trx_read_roll_ptr(field)); + ptr += mach_ull_write_compressed(ptr, trx_read_roll_ptr(field)); /*----------------------------------------*/ /* Store then the fields required to uniquely determine the @@ -848,11 +848,11 @@ trx_undo_update_rec_get_sys_cols( /* Read the values of the system columns */ - *trx_id = mach_dulint_read_compressed(ptr); - ptr += mach_dulint_get_compressed_size(*trx_id); + *trx_id = mach_ull_read_compressed(ptr); + ptr += mach_ull_get_compressed_size(*trx_id); - *roll_ptr = mach_dulint_read_compressed(ptr); - ptr += mach_dulint_get_compressed_size(*roll_ptr); + *roll_ptr = mach_ull_read_compressed(ptr); + ptr += mach_ull_get_compressed_size(*roll_ptr); return(ptr); } @@ -1168,7 +1168,7 @@ trx_undo_report_row_operation( index, otherwise NULL */ roll_ptr_t* roll_ptr) /*!< out: rollback pointer to the inserted undo log record, - ut_dulint_zero if BTR_NO_UNDO_LOG + 0 if BTR_NO_UNDO_LOG flag was specified */ { trx_t* trx; @@ -1186,7 +1186,7 @@ trx_undo_report_row_operation( if (flags & BTR_NO_UNDO_LOG_FLAG) { - *roll_ptr = ut_dulint_zero; + *roll_ptr = 0; return(DB_SUCCESS); } @@ -1284,7 +1284,7 @@ trx_undo_report_row_operation( undo->top_undo_no = trx->undo_no; undo->guess_block = undo_block; - UT_DULINT_INC(trx->undo_no); + trx->undo_no++; mutex_exit(&trx->undo_mutex); @@ -1433,7 +1433,7 @@ trx_undo_prev_version_build( trx_id_t rec_trx_id; ulint type; undo_no_t undo_no; - dulint table_id; + table_id_t table_id; trx_id_t trx_id; roll_ptr_t roll_ptr; roll_ptr_t old_roll_ptr; @@ -1523,7 +1523,7 @@ trx_undo_prev_version_build( roll_ptr, info_bits, NULL, heap, &update); - if (ut_dulint_cmp(table_id, index->table->id) != 0) { + if (UNIV_UNLIKELY(table_id != index->table->id)) { ptr = NULL; fprintf(stderr, @@ -1544,16 +1544,14 @@ trx_undo_prev_version_build( fprintf(stderr, "InnoDB: table %s, index %s, n_uniq %lu\n" "InnoDB: undo rec address %p, type %lu cmpl_info %lu\n" - "InnoDB: undo rec table id %lu %lu," - " index table id %lu %lu\n" + "InnoDB: undo rec table id %llu," + " index table id %llu\n" "InnoDB: dump of 150 bytes in undo rec: ", index->table_name, index->name, (ulong) dict_index_get_n_unique(index), undo_rec, (ulong) type, (ulong) cmpl_info, - (ulong) ut_dulint_get_high(table_id), - (ulong) ut_dulint_get_low(table_id), - (ulong) ut_dulint_get_high(index->table->id), - (ulong) ut_dulint_get_low(index->table->id)); + (ullint) table_id, + (ullint) index->table->id); ut_print_buf(stderr, undo_rec, 150); fputs("\n" "InnoDB: index record ", stderr); @@ -1564,14 +1562,10 @@ trx_undo_prev_version_build( fprintf(stderr, "\n" "InnoDB: Record trx id " TRX_ID_FMT ", update rec trx id " TRX_ID_FMT "\n" - "InnoDB: Roll ptr in rec %lu %lu, in update rec" - " %lu %lu\n", - TRX_ID_PREP_PRINTF(rec_trx_id), - TRX_ID_PREP_PRINTF(trx_id), - (ulong) ut_dulint_get_high(old_roll_ptr), - (ulong) ut_dulint_get_low(old_roll_ptr), - (ulong) ut_dulint_get_high(roll_ptr), - (ulong) ut_dulint_get_low(roll_ptr)); + "InnoDB: Roll ptr in rec " TRX_ID_FMT + ", in update rec" TRX_ID_FMT "\n", + (ullint) rec_trx_id, (ullint) trx_id, + (ullint) old_roll_ptr, (ullint) roll_ptr); trx_purge_sys_print(); return(DB_ERROR); diff --git a/storage/innobase/trx/trx0roll.c b/storage/innobase/trx/trx0roll.c index 4f1a71a5531..42b8a8685ad 100644 --- a/storage/innobase/trx/trx0roll.c +++ b/storage/innobase/trx/trx0roll.c @@ -52,7 +52,7 @@ static trx_t* trx_roll_crash_recv_trx = NULL; /** In crash recovery we set this to the undo n:o of the current trx to be rolled back. Then we can print how many % the rollback has progressed. */ -static ib_int64_t trx_roll_max_undo_no; +static undo_no_t trx_roll_max_undo_no; /** Auxiliary variable which tells the previous progress % we printed */ static ulint trx_roll_progress_printed_pct; @@ -443,7 +443,7 @@ trx_rollback_active( ut_a(thr == que_fork_start_command(fork)); trx_roll_crash_recv_trx = trx; - trx_roll_max_undo_no = ut_conv_dulint_to_longlong(trx->undo_no); + trx_roll_max_undo_no = trx->undo_no; trx_roll_progress_printed_pct = 0; rows_to_undo = trx_roll_max_undo_no; @@ -456,7 +456,7 @@ trx_rollback_active( fprintf(stderr, " InnoDB: Rolling back trx with id " TRX_ID_FMT ", %lu%s" " rows to undo\n", - TRX_ID_PREP_PRINTF(trx->id), + (ullint) trx->id, (ulong) rows_to_undo, unit); mutex_exit(&kernel_mutex); @@ -478,8 +478,9 @@ trx_rollback_active( mutex_exit(&kernel_mutex); fprintf(stderr, - "InnoDB: Waiting for rollback of trx id %lu to end\n", - (ulong) ut_dulint_get_low(trx->id)); + "InnoDB: Waiting for rollback of trx id " + TRX_ID_FMT " to end\n", + (ullint) trx->id); os_thread_sleep(100000); mutex_enter(&kernel_mutex); @@ -488,16 +489,15 @@ trx_rollback_active( mutex_exit(&kernel_mutex); if (trx_get_dict_operation(trx) != TRX_DICT_OP_NONE - && !ut_dulint_is_zero(trx->table_id)) { + && trx->table_id != 0) { /* If the transaction was for a dictionary operation, we drop the relevant table, if it still exists */ fprintf(stderr, - "InnoDB: Dropping table with id %lu %lu" + "InnoDB: Dropping table with id %llu" " in recovery if it exists\n", - (ulong) ut_dulint_get_high(trx->table_id), - (ulong) ut_dulint_get_low(trx->table_id)); + (ullint) trx->table_id); table = dict_table_get_on_id_low(trx->table_id); @@ -521,7 +521,7 @@ trx_rollback_active( fprintf(stderr, "\nInnoDB: Rolling back of trx id " TRX_ID_FMT " completed\n", - TRX_ID_PREP_PRINTF(trx->id)); + (ullint) trx->id); mem_heap_free(heap); trx_roll_crash_recv_trx = NULL; @@ -574,7 +574,7 @@ loop: fprintf(stderr, "InnoDB: Cleaning up trx with id " TRX_ID_FMT "\n", - TRX_ID_PREP_PRINTF(trx->id)); + (ullint) trx->id); trx_cleanup_at_db_startup(trx); goto loop; @@ -710,7 +710,7 @@ trx_undo_arr_store_info( } else { n++; - if (0 == ut_dulint_cmp(cell->undo_no, undo_no)) { + if (cell->undo_no == undo_no) { if (stored_here) { stored_here->in_use = FALSE; @@ -754,7 +754,7 @@ trx_undo_arr_remove_info( cell = trx_undo_arr_get_nth_info(arr, i); if (cell->in_use - && 0 == ut_dulint_cmp(cell->undo_no, undo_no)) { + && cell->undo_no == undo_no) { cell->in_use = FALSE; @@ -769,7 +769,7 @@ trx_undo_arr_remove_info( /*******************************************************************//** Gets the biggest undo number in an array. -@return biggest value, ut_dulint_zero if the array is empty */ +@return biggest value, 0 if the array is empty */ static undo_no_t trx_undo_arr_get_biggest( @@ -784,14 +784,14 @@ trx_undo_arr_get_biggest( n = 0; n_used = arr->n_used; - biggest = ut_dulint_zero; + biggest = 0; for (i = 0;; i++) { cell = trx_undo_arr_get_nth_info(arr, i); if (cell->in_use) { n++; - if (ut_dulint_cmp(cell->undo_no, biggest) > 0) { + if (cell->undo_no > biggest) { biggest = cell->undo_no; } @@ -827,9 +827,9 @@ trx_roll_try_truncate( if (arr->n_used > 0) { biggest = trx_undo_arr_get_biggest(arr); - if (ut_dulint_cmp(biggest, limit) >= 0) { + if (biggest >= limit) { - limit = ut_dulint_add(biggest, 1); + limit = biggest + 1; } } @@ -865,9 +865,9 @@ trx_roll_pop_top_rec( undo->top_page_no, mtr); offset = undo->top_offset; - /* fprintf(stderr, "Thread %lu undoing trx %lu undo record %lu\n", - os_thread_get_curr_id(), ut_dulint_get_low(trx->id), - ut_dulint_get_low(undo->top_undo_no)); */ + /* fprintf(stderr, "Thread %lu undoing trx " TRX_ID_FMT + " undo record " TRX_ID_FMT "\n", + os_thread_get_curr_id(), trx->id, undo->top_undo_no); */ prev_rec = trx_undo_get_prev_rec(undo_page + offset, undo->hdr_page_no, undo->hdr_offset, @@ -938,15 +938,14 @@ try_again: undo = upd_undo; } else if (!upd_undo || upd_undo->empty) { undo = ins_undo; - } else if (ut_dulint_cmp(upd_undo->top_undo_no, - ins_undo->top_undo_no) > 0) { + } else if (upd_undo->top_undo_no > ins_undo->top_undo_no) { undo = upd_undo; } else { undo = ins_undo; } if (!undo || undo->empty - || (ut_dulint_cmp(limit, undo->top_undo_no) > 0)) { + || limit > undo->top_undo_no) { if ((trx->undo_no_arr)->n_used == 0) { /* Rollback is ending */ @@ -978,7 +977,7 @@ try_again: undo_no = trx_undo_rec_get_undo_no(undo_rec); - ut_ad(ut_dulint_cmp(ut_dulint_add(undo_no, 1), trx->undo_no) == 0); + ut_ad(undo_no + 1 == trx->undo_no); /* We print rollback progress info if we are in a crash recovery and the transaction has at least 1000 row operations to undo. */ @@ -986,8 +985,7 @@ try_again: if (trx == trx_roll_crash_recv_trx && trx_roll_max_undo_no > 1000) { progress_pct = 100 - (ulint) - ((ut_conv_dulint_to_longlong(undo_no) * 100) - / trx_roll_max_undo_no); + ((undo_no * 100) / trx_roll_max_undo_no); if (progress_pct != trx_roll_progress_printed_pct) { if (trx_roll_progress_printed_pct == 0) { fprintf(stderr, @@ -1090,22 +1088,21 @@ trx_rollback( /* Initialize the rollback field in the transaction */ - if (sig->type == TRX_SIG_TOTAL_ROLLBACK) { - - trx->roll_limit = ut_dulint_zero; - - } else if (sig->type == TRX_SIG_ROLLBACK_TO_SAVEPT) { - + switch (sig->type) { + case TRX_SIG_TOTAL_ROLLBACK: + trx->roll_limit = 0; + break; + case TRX_SIG_ROLLBACK_TO_SAVEPT: trx->roll_limit = (sig->savept).least_undo_no; - - } else if (sig->type == TRX_SIG_ERROR_OCCURRED) { - + break; + case TRX_SIG_ERROR_OCCURRED: trx->roll_limit = trx->last_sql_stat_start.least_undo_no; - } else { + break; + default: ut_error; } - ut_a(ut_dulint_cmp(trx->roll_limit, trx->undo_no) <= 0); + ut_a(trx->roll_limit <= trx->undo_no); trx->pages_undone = 0; @@ -1269,8 +1266,8 @@ trx_finish_rollback_off_kernel( #ifdef UNIV_DEBUG if (lock_print_waits) { - fprintf(stderr, "Trx %lu rollback finished\n", - (ulong) ut_dulint_get_low(trx->id)); + fprintf(stderr, "Trx " TRX_ID_FMT " rollback finished\n", + (ullint) trx->id); } #endif /* UNIV_DEBUG */ diff --git a/storage/innobase/trx/trx0rseg.c b/storage/innobase/trx/trx0rseg.c index b458364b05d..740320f68c1 100644 --- a/storage/innobase/trx/trx0rseg.c +++ b/storage/innobase/trx/trx0rseg.c @@ -236,8 +236,8 @@ trx_rseg_mem_create( node_addr.page, mtr) + node_addr.boffset; - rseg->last_trx_no = mtr_read_dulint( - undo_log_hdr + TRX_UNDO_TRX_NO, mtr); + rseg->last_trx_no = mach_read_from_8( + undo_log_hdr + TRX_UNDO_TRX_NO); rseg->last_del_marks = mtr_read_ulint( undo_log_hdr + TRX_UNDO_DEL_MARKS, MLOG_2BYTES, mtr); } else { diff --git a/storage/innobase/trx/trx0sys.c b/storage/innobase/trx/trx0sys.c index e2f0ff6d532..640412c4572 100644 --- a/storage/innobase/trx/trx0sys.c +++ b/storage/innobase/trx/trx0sys.c @@ -664,8 +664,8 @@ trx_sys_flush_max_trx_id(void) sys_header = trx_sysf_get(&mtr); - mlog_write_dulint(sys_header + TRX_SYS_TRX_ID_STORE, - trx_sys->max_trx_id, &mtr); + mlog_write_ull(sys_header + TRX_SYS_TRX_ID_STORE, + trx_sys->max_trx_id, &mtr); mtr_commit(&mtr); } @@ -912,8 +912,7 @@ trx_sysf_create( sys_header = trx_sysf_get(mtr); /* Start counting transaction ids from number 1 up */ - mach_write_to_8(sys_header + TRX_SYS_TRX_ID_STORE, - ut_dulint_create(0, 1)); + mach_write_to_8(sys_header + TRX_SYS_TRX_ID_STORE, 1); /* Reset the rollback segment slots. Old versions of InnoDB define TRX_SYS_N_RSEGS as 256 (TRX_SYS_OLD_N_RSEGS) and expect @@ -950,7 +949,7 @@ trx_sys_init_at_db_start(void) /*==========================*/ { trx_sysf_t* sys_header; - ib_int64_t rows_to_undo = 0; + ib_uint64_t rows_to_undo = 0; const char* unit = ""; trx_t* trx; mtr_t mtr; @@ -976,12 +975,10 @@ trx_sys_init_at_db_start(void) to the disk-based header! Thus trx id values will not overlap when the database is repeatedly started! */ - trx_sys->max_trx_id = ut_dulint_add( - ut_dulint_align_up(mtr_read_dulint( - sys_header - + TRX_SYS_TRX_ID_STORE, &mtr), - TRX_SYS_TRX_ID_WRITE_MARGIN), - 2 * TRX_SYS_TRX_ID_WRITE_MARGIN); + trx_sys->max_trx_id = 2 * TRX_SYS_TRX_ID_WRITE_MARGIN + + ut_uint64_align_up(mach_read_from_8(sys_header + + TRX_SYS_TRX_ID_STORE), + TRX_SYS_TRX_ID_WRITE_MARGIN); UT_LIST_INIT(trx_sys->mysql_trx_list); trx_dummy_sess = sess_open(); @@ -992,9 +989,8 @@ trx_sys_init_at_db_start(void) for (;;) { - if ( trx->conc_state != TRX_PREPARED) { - rows_to_undo += ut_conv_dulint_to_longlong( - trx->undo_no); + if (trx->conc_state != TRX_PREPARED) { + rows_to_undo += trx->undo_no; } trx = UT_LIST_GET_NEXT(trx_list, trx); @@ -1017,7 +1013,7 @@ trx_sys_init_at_db_start(void) (ulong) rows_to_undo, unit); fprintf(stderr, "InnoDB: Trx id counter is " TRX_ID_FMT "\n", - TRX_ID_PREP_PRINTF(trx_sys->max_trx_id)); + (ullint) trx_sys->max_trx_id); } UT_LIST_INIT(trx_sys->view_list); @@ -1061,7 +1057,7 @@ trx_sys_file_format_max_write( mtr_t mtr; byte* ptr; buf_block_t* block; - ulint tag_value_low; + ib_uint64_t tag_value; mtr_start(&mtr); @@ -1072,17 +1068,13 @@ trx_sys_file_format_max_write( file_format_max.name = trx_sys_file_format_id_to_name(format_id); ptr = buf_block_get_frame(block) + TRX_SYS_FILE_FORMAT_TAG; - tag_value_low = format_id + TRX_SYS_FILE_FORMAT_TAG_MAGIC_N_LOW; + tag_value = format_id + TRX_SYS_FILE_FORMAT_TAG_MAGIC_N; if (name) { *name = file_format_max.name; } - mlog_write_dulint( - ptr, - ut_dulint_create(TRX_SYS_FILE_FORMAT_TAG_MAGIC_N_HIGH, - tag_value_low), - &mtr); + mlog_write_ull(ptr, tag_value, &mtr); mtr_commit(&mtr); @@ -1100,8 +1092,7 @@ trx_sys_file_format_max_read(void) mtr_t mtr; const byte* ptr; const buf_block_t* block; - ulint format_id; - dulint file_format_id; + ib_id_t file_format_id; /* Since this is called during the startup phase it's safe to read the value without a covering mutex. */ @@ -1115,16 +1106,15 @@ trx_sys_file_format_max_read(void) mtr_commit(&mtr); - format_id = file_format_id.low - TRX_SYS_FILE_FORMAT_TAG_MAGIC_N_LOW; + file_format_id -= TRX_SYS_FILE_FORMAT_TAG_MAGIC_N; - if (file_format_id.high != TRX_SYS_FILE_FORMAT_TAG_MAGIC_N_HIGH - || format_id >= FILE_FORMAT_NAME_N) { + if (file_format_id >= FILE_FORMAT_NAME_N) { /* Either it has never been tagged, or garbage in it. */ return(ULINT_UNDEFINED); } - return(format_id); + return((ulint) file_format_id); } /*****************************************************************//** @@ -1416,7 +1406,7 @@ trx_sys_read_file_format_id( byte buf[UNIV_PAGE_SIZE * 2]; page_t* page = ut_align(buf, UNIV_PAGE_SIZE); const byte* ptr; - dulint file_format_id; + ib_id_t file_format_id; *format_id = ULINT_UNDEFINED; @@ -1430,9 +1420,9 @@ trx_sys_read_file_format_id( if (!success) { /* The following call prints an error message */ os_file_get_last_error(TRUE); - + ut_print_timestamp(stderr); - + fprintf(stderr, " ibbackup: Error: trying to read system tablespace file format,\n" " ibbackup: but could not open the tablespace file %s!\n", @@ -1449,9 +1439,9 @@ trx_sys_read_file_format_id( if (!success) { /* The following call prints an error message */ os_file_get_last_error(TRUE); - + ut_print_timestamp(stderr); - + fprintf(stderr, " ibbackup: Error: trying to read system table space file format,\n" " ibbackup: but failed to read the tablespace file %s!\n", @@ -1465,17 +1455,16 @@ trx_sys_read_file_format_id( /* get the file format from the page */ ptr = page + TRX_SYS_FILE_FORMAT_TAG; file_format_id = mach_read_from_8(ptr); + file_format_id -= TRX_SYS_FILE_FORMAT_TAG_MAGIC_N; - *format_id = file_format_id.low - TRX_SYS_FILE_FORMAT_TAG_MAGIC_N_LOW; - - if (file_format_id.high != TRX_SYS_FILE_FORMAT_TAG_MAGIC_N_HIGH - || *format_id >= FILE_FORMAT_NAME_N) { + if (file_format_id >= FILE_FORMAT_NAME_N) { /* Either it has never been tagged, or garbage in it. */ - *format_id = ULINT_UNDEFINED; return(TRUE); } - + + *format_id = (ulint) file_format_id; + return(TRUE); } diff --git a/storage/innobase/trx/trx0trx.c b/storage/innobase/trx/trx0trx.c index c794671f7be..0ca6c2d7f35 100644 --- a/storage/innobase/trx/trx0trx.c +++ b/storage/innobase/trx/trx0trx.c @@ -109,8 +109,8 @@ trx_create( trx->isolation_level = TRX_ISO_REPEATABLE_READ; - trx->id = ut_dulint_zero; - trx->no = ut_dulint_max; + trx->id = 0; + trx->no = IB_ULONGLONG_MAX; trx->support_xa = TRUE; @@ -121,7 +121,7 @@ trx_create( trx->must_flush_log_later = FALSE; trx->dict_operation = TRX_DICT_OP_NONE; - trx->table_id = ut_dulint_zero; + trx->table_id = 0; trx->mysql_thd = NULL; trx->active_trans = 0; @@ -137,8 +137,8 @@ trx_create( trx->rseg = NULL; - trx->undo_no = ut_dulint_zero; - trx->last_sql_stat_start.least_undo_no = ut_dulint_zero; + trx->undo_no = 0; + trx->last_sql_stat_start.least_undo_no = 0; trx->insert_undo = NULL; trx->update_undo = NULL; trx->undo_no_arr = NULL; @@ -392,9 +392,9 @@ trx_list_insert_ordered( trx2 = UT_LIST_GET_FIRST(trx_sys->trx_list); while (trx2 != NULL) { - if (ut_dulint_cmp(trx->id, trx2->id) >= 0) { + if (trx->id >= trx2->id) { - ut_ad(ut_dulint_cmp(trx->id, trx2->id) == 1); + ut_ad(trx->id > trx2->id); break; } trx2 = UT_LIST_GET_NEXT(trx_list, trx2); @@ -463,7 +463,7 @@ trx_lists_init_at_db_start(void) TRX_ID_FMT " was in the" " XA prepared state.\n", - TRX_ID_PREP_PRINTF(trx->id)); + (ullint) trx->id); if (srv_force_recovery == 0) { @@ -495,9 +495,9 @@ trx_lists_init_at_db_start(void) trx->conc_state = TRX_ACTIVE; /* A running transaction always has the number - field inited to ut_dulint_max */ + field inited to IB_ULONGLONG_MAX */ - trx->no = ut_dulint_max; + trx->no = IB_ULONGLONG_MAX; } if (undo->dict_operation) { @@ -507,8 +507,7 @@ trx_lists_init_at_db_start(void) } if (!undo->empty) { - trx->undo_no = ut_dulint_add(undo->top_undo_no, - 1); + trx->undo_no = undo->top_undo_no + 1; } trx_list_insert_ordered(trx); @@ -539,8 +538,7 @@ trx_lists_init_at_db_start(void) "InnoDB: Transaction " TRX_ID_FMT " was in the" " XA prepared state.\n", - TRX_ID_PREP_PRINTF( - trx->id)); + (ullint) trx->id); if (srv_force_recovery == 0) { @@ -571,9 +569,9 @@ trx_lists_init_at_db_start(void) /* A running transaction always has the number field inited to - ut_dulint_max */ + IB_ULONGLONG_MAX */ - trx->no = ut_dulint_max; + trx->no = IB_ULONGLONG_MAX; } trx->rseg = rseg; @@ -589,11 +587,9 @@ trx_lists_init_at_db_start(void) trx->update_undo = undo; if ((!undo->empty) - && (ut_dulint_cmp(undo->top_undo_no, - trx->undo_no) >= 0)) { + && undo->top_undo_no >= trx->undo_no) { - trx->undo_no = ut_dulint_add(undo->top_undo_no, - 1); + trx->undo_no = undo->top_undo_no + 1; } undo = UT_LIST_GET_NEXT(undo_list, undo); @@ -655,7 +651,7 @@ trx_start_low( ut_ad(trx->rseg == NULL); if (trx->is_purge) { - trx->id = ut_dulint_zero; + trx->id = 0; trx->conc_state = TRX_ACTIVE; trx->start_time = time(NULL); @@ -673,10 +669,10 @@ trx_start_low( trx->id = trx_sys_get_new_trx_id(); - /* The initial value for trx->no: ut_dulint_max is used in + /* The initial value for trx->no: IB_ULONGLONG_MAX is used in read_view_open_now: */ - trx->no = ut_dulint_max; + trx->no = IB_ULONGLONG_MAX; trx->rseg = rseg; @@ -941,8 +937,8 @@ trx_commit_off_kernel( trx->conc_state = TRX_NOT_STARTED; trx->rseg = NULL; - trx->undo_no = ut_dulint_zero; - trx->last_sql_stat_start.least_undo_no = ut_dulint_zero; + trx->undo_no = 0; + trx->last_sql_stat_start.least_undo_no = 0; ut_ad(UT_LIST_GET_LEN(trx->wait_thrs) == 0); ut_ad(UT_LIST_GET_LEN(trx->trx_locks) == 0); @@ -967,8 +963,8 @@ trx_cleanup_at_db_startup( trx->conc_state = TRX_NOT_STARTED; trx->rseg = NULL; - trx->undo_no = ut_dulint_zero; - trx->last_sql_stat_start.least_undo_no = ut_dulint_zero; + trx->undo_no = 0; + trx->last_sql_stat_start.least_undo_no = 0; UT_LIST_REMOVE(trx_list, trx_sys->trx_list, trx); } @@ -1631,7 +1627,7 @@ trx_mark_sql_stat_end( ut_a(trx); if (trx->conc_state == TRX_NOT_STARTED) { - trx->undo_no = ut_dulint_zero; + trx->undo_no = 0; } trx->last_sql_stat_start.least_undo_no = trx->undo_no; @@ -1651,7 +1647,7 @@ trx_print( { ibool newline; - fprintf(f, "TRANSACTION " TRX_ID_FMT, TRX_ID_PREP_PRINTF(trx->id)); + fprintf(f, "TRANSACTION " TRX_ID_FMT, (ullint) trx->id); switch (trx->conc_state) { case TRX_NOT_STARTED: @@ -1735,10 +1731,10 @@ trx_print( fputs(", holds adaptive hash latch", f); } - if (!ut_dulint_is_zero(trx->undo_no)) { + if (trx->undo_no != 0) { newline = TRUE; - fprintf(f, ", undo log entries %lu", - (ulong) ut_dulint_get_low(trx->undo_no)); + fprintf(f, ", undo log entries %llu", + (ullint) trx->undo_no); } if (newline) { @@ -1754,11 +1750,11 @@ trx_print( Compares the "weight" (or size) of two transactions. Transactions that have edited non-transactional tables are considered heavier than ones that have not. -@return <0, 0 or >0; similar to strcmp(3) */ +@return TRUE if weight(a) >= weight(b) */ UNIV_INTERN -int -trx_weight_cmp( -/*===========*/ +ibool +trx_weight_ge( +/*==========*/ const trx_t* a, /*!< in: the first transaction to be compared */ const trx_t* b) /*!< in: the second transaction to be compared */ { @@ -1769,19 +1765,14 @@ trx_weight_cmp( not edited non-transactional tables. */ a_notrans_edit = a->mysql_thd != NULL - && thd_has_edited_nontrans_tables(a->mysql_thd); + && thd_has_edited_nontrans_tables(a->mysql_thd); b_notrans_edit = b->mysql_thd != NULL - && thd_has_edited_nontrans_tables(b->mysql_thd); + && thd_has_edited_nontrans_tables(b->mysql_thd); - if (a_notrans_edit && !b_notrans_edit) { + if (a_notrans_edit != b_notrans_edit) { - return(1); - } - - if (!a_notrans_edit && b_notrans_edit) { - - return(-1); + return(a_notrans_edit); } /* Either both had edited non-transactional tables or both had @@ -1792,13 +1783,11 @@ trx_weight_cmp( fprintf(stderr, "%s TRX_WEIGHT(a): %lld+%lu, TRX_WEIGHT(b): %lld+%lu\n", __func__, - ut_conv_dulint_to_longlong(a->undo_no), - UT_LIST_GET_LEN(a->trx_locks), - ut_conv_dulint_to_longlong(b->undo_no), - UT_LIST_GET_LEN(b->trx_locks)); + a->undo_no, UT_LIST_GET_LEN(a->trx_locks), + b->undo_no, UT_LIST_GET_LEN(b->trx_locks)); #endif - return(ut_dulint_cmp(TRX_WEIGHT(a), TRX_WEIGHT(b))); + return(TRX_WEIGHT(a) >= TRX_WEIGHT(b)); } /****************************************************************//** @@ -1980,14 +1969,13 @@ trx_recover_for_mysql( fprintf(stderr, " InnoDB: Transaction " TRX_ID_FMT " in" " prepared state after recovery\n", - TRX_ID_PREP_PRINTF(trx->id)); + (ullint) trx->id); ut_print_timestamp(stderr); fprintf(stderr, " InnoDB: Transaction contains changes" - " to %lu rows\n", - (ulong) ut_conv_dulint_to_longlong( - trx->undo_no)); + " to %llu rows\n", + (ullint) trx->undo_no); count++; diff --git a/storage/innobase/trx/trx0undo.c b/storage/innobase/trx/trx0undo.c index eb5112c4d31..3689f5ebdad 100644 --- a/storage/innobase/trx/trx0undo.c +++ b/storage/innobase/trx/trx0undo.c @@ -515,7 +515,7 @@ trx_undo_header_create_log( { mlog_write_initial_log_record(undo_page, MLOG_UNDO_HDR_CREATE, mtr); - mlog_catenate_dulint_compressed(mtr, trx_id); + mlog_catenate_ull_compressed(mtr, trx_id); } #else /* !UNIV_HOTBACKUP */ # define trx_undo_header_create_log(undo_page,trx_id,mtr) ((void) 0) @@ -687,7 +687,7 @@ trx_undo_insert_header_reuse_log( { mlog_write_initial_log_record(undo_page, MLOG_UNDO_HDR_REUSE, mtr); - mlog_catenate_dulint_compressed(mtr, trx_id); + mlog_catenate_ull_compressed(mtr, trx_id); } #else /* !UNIV_HOTBACKUP */ # define trx_undo_insert_header_reuse_log(undo_page,trx_id,mtr) ((void) 0) @@ -708,7 +708,7 @@ trx_undo_parse_page_header( { trx_id_t trx_id; - ptr = mach_dulint_parse_compressed(ptr, end_ptr, &trx_id); + ptr = mach_ull_parse_compressed(ptr, end_ptr, &trx_id); if (ptr == NULL) { @@ -1098,8 +1098,7 @@ trx_undo_truncate_end( break; } - if (ut_dulint_cmp(trx_undo_rec_get_undo_no(rec), limit) - >= 0) { + if (trx_undo_rec_get_undo_no(rec) >= limit) { /* Truncate at least this record off, maybe more */ trunc_here = rec; @@ -1152,7 +1151,7 @@ trx_undo_truncate_start( ut_ad(mutex_own(&(rseg->mutex))); - if (ut_dulint_is_zero(limit)) { + if (!limit) { return; } @@ -1174,7 +1173,7 @@ loop: last_rec = trx_undo_page_get_last_rec(undo_page, hdr_page_no, hdr_offset); - if (ut_dulint_cmp(trx_undo_rec_get_undo_no(last_rec), limit) >= 0) { + if (trx_undo_rec_get_undo_no(last_rec) >= limit) { mtr_commit(&mtr); @@ -1296,7 +1295,7 @@ trx_undo_mem_create_at_db_start( undo_header = undo_page + offset; - trx_id = mtr_read_dulint(undo_header + TRX_UNDO_TRX_ID, mtr); + trx_id = mach_read_from_8(undo_header + TRX_UNDO_TRX_ID); xid_exists = mtr_read_ulint(undo_header + TRX_UNDO_XID_EXISTS, MLOG_1BYTE, mtr); @@ -1320,7 +1319,7 @@ trx_undo_mem_create_at_db_start( undo->dict_operation = mtr_read_ulint( undo_header + TRX_UNDO_DICT_TRANS, MLOG_1BYTE, mtr); - undo->table_id = mtr_read_dulint(undo_header + TRX_UNDO_TABLE_ID, mtr); + undo->table_id = mach_read_from_8(undo_header + TRX_UNDO_TABLE_ID); undo->state = state; undo->size = flst_get_len(seg_header + TRX_UNDO_PAGE_LIST, mtr); @@ -1709,7 +1708,7 @@ trx_undo_mark_as_dict_operation( ut_error; case TRX_DICT_OP_INDEX: /* Do not discard the table on recovery. */ - undo->table_id = ut_dulint_zero; + undo->table_id = 0; break; case TRX_DICT_OP_TABLE: undo->table_id = trx->table_id; @@ -1720,8 +1719,8 @@ trx_undo_mark_as_dict_operation( + TRX_UNDO_DICT_TRANS, TRUE, MLOG_1BYTE, mtr); - mlog_write_dulint(hdr_page + undo->hdr_offset + TRX_UNDO_TABLE_ID, - undo->table_id, mtr); + mlog_write_ull(hdr_page + undo->hdr_offset + TRX_UNDO_TABLE_ID, + undo->table_id, mtr); undo->dict_operation = TRUE; } diff --git a/storage/innobase/ut/ut0byte.c b/storage/innobase/ut/ut0byte.c index 4e093f72ce2..535f74b8907 100644 --- a/storage/innobase/ut/ut0byte.c +++ b/storage/innobase/ut/ut0byte.c @@ -28,28 +28,3 @@ Created 5/11/1994 Heikki Tuuri #ifdef UNIV_NONINL #include "ut0byte.ic" #endif - -/** Zero value for a dulint */ -UNIV_INTERN const dulint ut_dulint_zero = {0, 0}; - -/** Maximum value for a dulint */ -UNIV_INTERN const dulint ut_dulint_max = {0xFFFFFFFFUL, 0xFFFFFFFFUL}; - -#ifdef notdefined /* unused code */ -#include "ut0sort.h" - -/************************************************************//** -Sort function for dulint arrays. */ -UNIV_INTERN -void -ut_dulint_sort( -/*===========*/ - dulint* arr, /*!< in/out: array to be sorted */ - dulint* aux_arr,/*!< in/out: auxiliary array (same size as arr) */ - ulint low, /*!< in: low bound of sort interval, inclusive */ - ulint high) /*!< in: high bound of sort interval, noninclusive */ -{ - UT_SORT_FUNCTION_BODY(ut_dulint_sort, arr, aux_arr, low, high, - ut_dulint_cmp); -} -#endif /* notdefined */ From be3005d9e71e853076b9ee35097ffd976d7207f1 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Wed, 23 Jun 2010 13:34:40 +0200 Subject: [PATCH 026/115] Backport from mysql-6.0-codebase of: ------------------------------------------------------------ revno: 3672 committer: lars-erik.bjork@sun.com branch nick: 48067-mysql-6.0-codebase-bugfixing timestamp: Mon 2009-10-26 13:51:43 +0100 message: This is a patch for bug#48067 "A temp table with the same name as an existing table, makes drop database fail" When dropping the database, mysql_rm_known_files() reads the contents of the database directory, and creates a TABLE_LIST object, for each .frm file encountered. Temporary tables, however, are not associated with any .frm file. The list of tables to drop are passed to mysql_rm_table_part2(). This method prefers temporary tables over regular tables, so if there is a temporary table with the same name as a regular, the temporary is removed, leaving the regular table intact. Regular tables are only deleted if there are no temporary tables with the same name. This fix ensures, that for all TABLE_LIST objects that are created by mysql_rm_known_files(), 'open_type' is set to 'OT_BASE_ONLY', to indicate that this is a regular table. In all cases in mysql_rm_table_part2() where we prefer a temporary table to a non-temporary table, we chek if 'open_type' equals 'OT_BASE_ONLY'. --- mysql-test/r/temp_table.result | 12 ++++++++++++ mysql-test/t/temp_table.test | 15 +++++++++++++++ sql/sql_db.cc | 1 + sql/sql_table.cc | 10 +++++++--- 4 files changed, 35 insertions(+), 3 deletions(-) diff --git a/mysql-test/r/temp_table.result b/mysql-test/r/temp_table.result index ba6b9f81a2d..33f5c6b5165 100644 --- a/mysql-test/r/temp_table.result +++ b/mysql-test/r/temp_table.result @@ -210,4 +210,16 @@ UPDATE t1,t2 SET t1.a = t2.a; INSERT INTO t2 SELECT f1(); DROP TABLE t1,t2,t3; DROP FUNCTION f1; +# +# Bug #48067: A temp table with the same name as an existing table, +# makes drop database fail. +# +DROP TEMPORARY TABLE IF EXISTS bug48067.t1; +DROP DATABASE IF EXISTS bug48067; +CREATE DATABASE bug48067; +CREATE TABLE bug48067.t1 (c1 int); +INSERT INTO bug48067.t1 values (1); +CREATE TEMPORARY TABLE bug48067.t1 (c1 int); +DROP DATABASE bug48067; +DROP TEMPORARY table bug48067.t1; End of 5.1 tests diff --git a/mysql-test/t/temp_table.test b/mysql-test/t/temp_table.test index 2bfa4936c91..92c22242cdb 100644 --- a/mysql-test/t/temp_table.test +++ b/mysql-test/t/temp_table.test @@ -235,4 +235,19 @@ INSERT INTO t2 SELECT f1(); DROP TABLE t1,t2,t3; DROP FUNCTION f1; +--echo # +--echo # Bug #48067: A temp table with the same name as an existing table, +--echo # makes drop database fail. +--echo # +--disable_warnings +DROP TEMPORARY TABLE IF EXISTS bug48067.t1; +DROP DATABASE IF EXISTS bug48067; +--enable_warnings +CREATE DATABASE bug48067; +CREATE TABLE bug48067.t1 (c1 int); +INSERT INTO bug48067.t1 values (1); +CREATE TEMPORARY TABLE bug48067.t1 (c1 int); +DROP DATABASE bug48067; +DROP TEMPORARY table bug48067.t1; + --echo End of 5.1 tests diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 2e48475f298..5e992d2391c 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -1196,6 +1196,7 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db, (void) filename_to_tablename(file->name, table_list->table_name, MYSQL50_TABLE_NAME_PREFIX_LENGTH + strlen(file->name) + 1); + table_list->open_type= OT_BASE_ONLY; /* To be able to correctly look up the table in the table cache. */ if (lower_case_table_names) diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 902e7fa7b5f..e0e32b81e03 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -1964,7 +1964,8 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, else { for (table= tables; table; table= table->next_local) - if (find_temporary_table(thd, table->db, table->table_name)) + if (table->open_type != OT_BASE_ONLY && + find_temporary_table(thd, table->db, table->table_name)) { /* A temporary table. @@ -2009,8 +2010,11 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, table->db, table->table_name, (long) table->table, table->table ? (long) table->table->s : (long) -1)); - error= drop_temporary_table(thd, table); - + if (table->open_type == OT_BASE_ONLY) + error= 1; + else + error= drop_temporary_table(thd, table); + switch (error) { case 0: // removed temporary table From bc3deeb269c85ae07fa39e101b7ba2c7c99e94e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 24 Jun 2010 09:01:46 +0300 Subject: [PATCH 027/115] ut_ull_create(): Fix a typo in ut_ad(): ULINT32_MASK instead of ULINT32_MAX. --- storage/innobase/include/ut0byte.ic | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/storage/innobase/include/ut0byte.ic b/storage/innobase/include/ut0byte.ic index 73af41bfefa..e7908efa41a 100644 --- a/storage/innobase/include/ut0byte.ic +++ b/storage/innobase/include/ut0byte.ic @@ -33,8 +33,8 @@ ut_ull_create( ulint high, /*!< in: high-order 32 bits */ ulint low) /*!< in: low-order 32 bits */ { - ut_ad(high <= ULINT32_MAX); - ut_ad(low <= ULINT32_MAX); + ut_ad(high <= ULINT32_MASK); + ut_ad(low <= ULINT32_MASK); return(((ib_uint64_t) high) << 32 | low); } From 2f7fd13135c266408709e3fc34ad2cf98a2cc95b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 24 Jun 2010 09:08:42 +0300 Subject: [PATCH 028/115] mach_ull_parse_compressed(): Move to .ic file to silence a GCC warning about trx_id being possibly uninitialized in trx_undo_parse_page_header(). The warning remains when UNIV_DEBUG or UNIV_MUST_NOT_INLINE is enabled. --- storage/innobase/include/mach0data.h | 2 +- storage/innobase/include/mach0data.ic | 40 +++++++++++++++++++++++++++ storage/innobase/mach/mach0data.c | 40 --------------------------- 3 files changed, 41 insertions(+), 41 deletions(-) diff --git a/storage/innobase/include/mach0data.h b/storage/innobase/include/mach0data.h index 7d80bcd5b8c..3157e74224d 100644 --- a/storage/innobase/include/mach0data.h +++ b/storage/innobase/include/mach0data.h @@ -283,7 +283,7 @@ mach_parse_compressed( Reads a 64-bit integer in a compressed form if the log record fully contains it. @return pointer to end of the stored field, NULL if not complete */ -UNIV_INTERN +UNIV_INLINE byte* mach_ull_parse_compressed( /*======================*/ diff --git a/storage/innobase/include/mach0data.ic b/storage/innobase/include/mach0data.ic index e27aecfa577..b1e5991d39e 100644 --- a/storage/innobase/include/mach0data.ic +++ b/storage/innobase/include/mach0data.ic @@ -496,6 +496,46 @@ mach_ull_read_much_compressed( return(n); } + +/*********************************************************//** +Reads a 64-bit integer in a compressed form +if the log record fully contains it. +@return pointer to end of the stored field, NULL if not complete */ +UNIV_INLINE +byte* +mach_ull_parse_compressed( +/*======================*/ + byte* ptr, /* in: pointer to buffer from where to read */ + byte* end_ptr,/* in: pointer to end of the buffer */ + ib_uint64_t* val) /* out: read value */ +{ + ulint size; + + ut_ad(ptr); + ut_ad(end_ptr); + ut_ad(val); + + if (end_ptr < ptr + 5) { + + return(NULL); + } + + *val = mach_read_compressed(ptr); + + size = mach_get_compressed_size((ulint) *val); + + ptr += size; + + if (end_ptr < ptr + 4) { + + return(NULL); + } + + *val <<= 32; + *val |= mach_read_from_4(ptr); + + return(ptr + 4); +} #ifndef UNIV_HOTBACKUP /*********************************************************//** Reads a double. It is stored in a little-endian format. diff --git a/storage/innobase/mach/mach0data.c b/storage/innobase/mach/mach0data.c index 7e7a939961d..647d9e57384 100644 --- a/storage/innobase/mach/mach0data.c +++ b/storage/innobase/mach/mach0data.c @@ -92,43 +92,3 @@ mach_parse_compressed( return(ptr + 5); } } - -/*********************************************************//** -Reads a 64-bit integer in a compressed form -if the log record fully contains it. -@return pointer to end of the stored field, NULL if not complete */ -UNIV_INTERN -byte* -mach_ull_parse_compressed( -/*======================*/ - byte* ptr, /* in: pointer to buffer from where to read */ - byte* end_ptr,/* in: pointer to end of the buffer */ - ib_uint64_t* val) /* out: read value */ -{ - ulint size; - - ut_ad(ptr); - ut_ad(end_ptr); - ut_ad(val); - - if (end_ptr < ptr + 5) { - - return(NULL); - } - - *val = mach_read_compressed(ptr); - - size = mach_get_compressed_size((ulint) *val); - - ptr += size; - - if (end_ptr < ptr + 4) { - - return(NULL); - } - - *val <<= 32; - *val |= mach_read_from_4(ptr); - - return(ptr + 4); -} From ae029e13c996148ac6395b1ce50d260f37982854 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 24 Jun 2010 09:09:29 +0300 Subject: [PATCH 029/115] After-review cleanup of Bug#57428: replace the dulint struct with a 64-bit int trx_undo_build_roll_ptr(): Clarify the assertion on is_insert being 0 or 1. TRX_SYS_FILE_FORMAT_TAG: Remove "extra" space after period in the comment. See also http://desktoppub.about.com/cs/typespacing/a/onetwospaces.htm --- storage/innobase/include/trx0sys.h | 4 ++-- storage/innobase/include/trx0undo.ic | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/storage/innobase/include/trx0sys.h b/storage/innobase/include/trx0sys.h index d6965377165..63e3f6be934 100644 --- a/storage/innobase/include/trx0sys.h +++ b/storage/innobase/include/trx0sys.h @@ -568,12 +568,12 @@ FIL_PAGE_ARCH_LOG_NO_OR_SPACE_NO. */ (TRX_SYS_PAGE_NO of TRX_SYS_SPACE) */ #define TRX_SYS_FILE_FORMAT_TAG (UNIV_PAGE_SIZE - 16) -/** Contents of TRX_SYS_FILE_FORMAT_TAG when valid. The file format +/** Contents of TRX_SYS_FILE_FORMAT_TAG when valid. The file format identifier is added to this constant. */ #define TRX_SYS_FILE_FORMAT_TAG_MAGIC_N_LOW 3645922177UL /** Contents of TRX_SYS_FILE_FORMAT_TAG+4 when valid */ #define TRX_SYS_FILE_FORMAT_TAG_MAGIC_N_HIGH 2745987765UL -/** Contents of TRX_SYS_FILE_FORMAT_TAG when valid. The file format +/** Contents of TRX_SYS_FILE_FORMAT_TAG when valid. The file format identifier is added to this 64-bit constant. */ #define TRX_SYS_FILE_FORMAT_TAG_MAGIC_N \ ((ib_uint64_t) TRX_SYS_FILE_FORMAT_TAG_MAGIC_N_HIGH << 32 \ diff --git a/storage/innobase/include/trx0undo.ic b/storage/innobase/include/trx0undo.ic index cc7d76dffe5..b81330f7f8b 100644 --- a/storage/innobase/include/trx0undo.ic +++ b/storage/innobase/include/trx0undo.ic @@ -43,7 +43,7 @@ trx_undo_build_roll_ptr( #if DATA_ROLL_PTR_LEN != 7 # error "DATA_ROLL_PTR_LEN != 7" #endif - ut_ad(is_insert <= 1); + ut_ad(is_insert == 0 || is_insert == 1); ut_ad(rseg_id < TRX_SYS_N_RSEGS); ut_ad(offset < 65536); From 3f81c310dc9e926d49a79b034da45eb9d5ef9ee4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 24 Jun 2010 10:21:40 +0300 Subject: [PATCH 030/115] trx_undo_parse_page_header(): Silence a bogus warning. --- storage/innobase/trx/trx0undo.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/storage/innobase/trx/trx0undo.c b/storage/innobase/trx/trx0undo.c index 3689f5ebdad..90fc98f419d 100644 --- a/storage/innobase/trx/trx0undo.c +++ b/storage/innobase/trx/trx0undo.c @@ -707,6 +707,12 @@ trx_undo_parse_page_header( mtr_t* mtr) /*!< in: mtr or NULL */ { trx_id_t trx_id; + /* Silence a GCC warning about possibly uninitialized variable + when mach_ull_parse_compressed() is not inlined. */ + ut_d(trx_id = 0); + /* Declare the variable uninitialized in Valgrind, so that the + above initialization will not mask any bugs. */ + UNIV_MEM_INVALID(&trx_id, sizeof trx_id); ptr = mach_ull_parse_compressed(ptr, end_ptr, &trx_id); From da8daa3679a075ec88c320df569528ccf671a4b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 24 Jun 2010 14:06:52 +0300 Subject: [PATCH 031/115] =?UTF-8?q?Merge=20Bug=20#54679=20fix=20from=20mys?= =?UTF-8?q?ql-5.1-innodb:=20----------------------------------------------?= =?UTF-8?q?--------------=20revno:=203523=20revision-id:=20marko.makela@or?= =?UTF-8?q?acle.com-20100624104620-pklunowaigv7quu9=20parent:=20jimmy.yang?= =?UTF-8?q?@oracle.com-20100624021010-oh2hnp8e1xbaax6u=20committer:=20Mark?= =?UTF-8?q?o=20M=C3=A4kel=C3=A4=20=20branch=20nic?= =?UTF-8?q?k:=205.1-innodb=20timestamp:=20Thu=202010-06-24=2013:46:20=20+0?= =?UTF-8?q?300=20message:=20=20=20Bug#54679:=20alter=20table=20causes=20co?= =?UTF-8?q?mpressed=20row=5Fformat=20to=20revert=20to=20compact?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ha_innobase::create(): Add the local variable row_type = form->s->row_type. Adjust it to ROW_TYPE_COMPRESSED when ROW_FORMAT is not specified or inherited but KEY_BLOCK_SIZE is. Observe the inherited ROW_FORMAT even when it is not explicitly specified. innodb_bug54679.test: New test, to test the bug and to ensure that there are no regressions. (The only difference in the test result without the patch applied is that the first ALTER TABLE changes ROW_FORMAT to Compact.) --- .../suite/innodb/r/innodb_bug54679.result | 91 ++++++++++ .../suite/innodb/t/innodb_bug54679.test | 97 +++++++++++ storage/innobase/handler/ha_innodb.cc | 159 +++++++++--------- 3 files changed, 268 insertions(+), 79 deletions(-) create mode 100644 mysql-test/suite/innodb/r/innodb_bug54679.result create mode 100644 mysql-test/suite/innodb/t/innodb_bug54679.test diff --git a/mysql-test/suite/innodb/r/innodb_bug54679.result b/mysql-test/suite/innodb/r/innodb_bug54679.result new file mode 100644 index 00000000000..3190bbb5c7f --- /dev/null +++ b/mysql-test/suite/innodb/r/innodb_bug54679.result @@ -0,0 +1,91 @@ +SET GLOBAL innodb_file_format='Barracuda'; +SET GLOBAL innodb_file_per_table=ON; +SET innodb_strict_mode=ON; +CREATE TABLE bug54679 (a INT) ENGINE=InnoDB ROW_FORMAT=COMPRESSED; +SELECT TABLE_NAME,ROW_FORMAT,CREATE_OPTIONS FROM information_schema.tables +WHERE TABLE_NAME='bug54679'; +TABLE_NAME ROW_FORMAT CREATE_OPTIONS +bug54679 Compressed row_format=COMPRESSED +ALTER TABLE bug54679 ADD COLUMN b INT; +SELECT TABLE_NAME,ROW_FORMAT,CREATE_OPTIONS FROM information_schema.tables +WHERE TABLE_NAME='bug54679'; +TABLE_NAME ROW_FORMAT CREATE_OPTIONS +bug54679 Compressed row_format=COMPRESSED +DROP TABLE bug54679; +CREATE TABLE bug54679 (a INT) ENGINE=InnoDB; +SELECT TABLE_NAME,ROW_FORMAT,CREATE_OPTIONS FROM information_schema.tables +WHERE TABLE_NAME='bug54679'; +TABLE_NAME ROW_FORMAT CREATE_OPTIONS +bug54679 Compact +ALTER TABLE bug54679 KEY_BLOCK_SIZE=1; +SELECT TABLE_NAME,ROW_FORMAT,CREATE_OPTIONS FROM information_schema.tables +WHERE TABLE_NAME='bug54679'; +TABLE_NAME ROW_FORMAT CREATE_OPTIONS +bug54679 Compressed KEY_BLOCK_SIZE=1 +ALTER TABLE bug54679 ROW_FORMAT=REDUNDANT; +ERROR HY000: Can't create table '#sql-temporary' (errno: 1478) +SHOW WARNINGS; +Level Code Message +Warning 1478 InnoDB: cannot specify ROW_FORMAT = REDUNDANT with KEY_BLOCK_SIZE. +Error 1005 Can't create table '#sql-temporary' (errno: 1478) +DROP TABLE bug54679; +CREATE TABLE bug54679 (a INT) ENGINE=InnoDB ROW_FORMAT=REDUNDANT; +SELECT TABLE_NAME,ROW_FORMAT,CREATE_OPTIONS FROM information_schema.tables +WHERE TABLE_NAME='bug54679'; +TABLE_NAME ROW_FORMAT CREATE_OPTIONS +bug54679 Redundant row_format=REDUNDANT +ALTER TABLE bug54679 KEY_BLOCK_SIZE=2; +SELECT TABLE_NAME,ROW_FORMAT,CREATE_OPTIONS FROM information_schema.tables +WHERE TABLE_NAME='bug54679'; +TABLE_NAME ROW_FORMAT CREATE_OPTIONS +bug54679 Compressed row_format=REDUNDANT KEY_BLOCK_SIZE=2 +SET GLOBAL innodb_file_format=Antelope; +ALTER TABLE bug54679 KEY_BLOCK_SIZE=4; +ERROR HY000: Can't create table '#sql-temporary' (errno: 1478) +SHOW WARNINGS; +Level Code Message +Warning 1478 InnoDB: KEY_BLOCK_SIZE requires innodb_file_format > Antelope. +Error 1005 Can't create table '#sql-temporary' (errno: 1478) +ALTER TABLE bug54679 ROW_FORMAT=DYNAMIC; +ERROR HY000: Can't create table '#sql-temporary' (errno: 1478) +SHOW WARNINGS; +Level Code Message +Warning 1478 InnoDB: KEY_BLOCK_SIZE requires innodb_file_format > Antelope. +Warning 1478 InnoDB: ROW_FORMAT=DYNAMIC requires innodb_file_format > Antelope. +Warning 1478 InnoDB: cannot specify ROW_FORMAT = DYNAMIC with KEY_BLOCK_SIZE. +Error 1005 Can't create table '#sql-temporary' (errno: 1478) +DROP TABLE bug54679; +CREATE TABLE bug54679 (a INT) ENGINE=InnoDB ROW_FORMAT=DYNAMIC; +ERROR HY000: Can't create table 'test.bug54679' (errno: 1478) +SHOW WARNINGS; +Level Code Message +Warning 1478 InnoDB: ROW_FORMAT=DYNAMIC requires innodb_file_format > Antelope. +Error 1005 Can't create table 'test.bug54679' (errno: 1478) +CREATE TABLE bug54679 (a INT) ENGINE=InnoDB; +SET GLOBAL innodb_file_format=Barracuda; +SET GLOBAL innodb_file_per_table=OFF; +ALTER TABLE bug54679 KEY_BLOCK_SIZE=4; +ERROR HY000: Can't create table '#sql-temporary' (errno: 1478) +SHOW WARNINGS; +Level Code Message +Warning 1478 InnoDB: KEY_BLOCK_SIZE requires innodb_file_per_table. +Error 1005 Can't create table '#sql-temporary' (errno: 1478) +ALTER TABLE bug54679 ROW_FORMAT=DYNAMIC; +ERROR HY000: Can't create table '#sql-temporary' (errno: 1478) +SHOW WARNINGS; +Level Code Message +Warning 1478 InnoDB: ROW_FORMAT=DYNAMIC requires innodb_file_per_table. +Error 1005 Can't create table '#sql-temporary' (errno: 1478) +DROP TABLE bug54679; +CREATE TABLE bug54679 (a INT) ENGINE=InnoDB ROW_FORMAT=DYNAMIC; +ERROR HY000: Can't create table 'test.bug54679' (errno: 1478) +SHOW WARNINGS; +Level Code Message +Warning 1478 InnoDB: ROW_FORMAT=DYNAMIC requires innodb_file_per_table. +Error 1005 Can't create table 'test.bug54679' (errno: 1478) +SET GLOBAL innodb_file_per_table=ON; +CREATE TABLE bug54679 (a INT) ENGINE=InnoDB ROW_FORMAT=DYNAMIC; +DROP TABLE bug54679; +SET GLOBAL innodb_file_format=Antelope; +SET GLOBAL innodb_file_format_max=Antelope; +SET GLOBAL innodb_file_per_table=0; diff --git a/mysql-test/suite/innodb/t/innodb_bug54679.test b/mysql-test/suite/innodb/t/innodb_bug54679.test new file mode 100644 index 00000000000..a102a916705 --- /dev/null +++ b/mysql-test/suite/innodb/t/innodb_bug54679.test @@ -0,0 +1,97 @@ +# Test Bug #54679 alter table causes compressed row_format to revert to compact + +--source include/have_innodb.inc + +let $file_format=`select @@innodb_file_format`; +let $file_format_max=`select @@innodb_file_format_max`; +let $file_per_table=`select @@innodb_file_per_table`; +SET GLOBAL innodb_file_format='Barracuda'; +SET GLOBAL innodb_file_per_table=ON; +SET innodb_strict_mode=ON; + +CREATE TABLE bug54679 (a INT) ENGINE=InnoDB ROW_FORMAT=COMPRESSED; +SELECT TABLE_NAME,ROW_FORMAT,CREATE_OPTIONS FROM information_schema.tables +WHERE TABLE_NAME='bug54679'; + +# The ROW_FORMAT of the table should be preserved when it is not specified +# in ALTER TABLE. +ALTER TABLE bug54679 ADD COLUMN b INT; +SELECT TABLE_NAME,ROW_FORMAT,CREATE_OPTIONS FROM information_schema.tables +WHERE TABLE_NAME='bug54679'; + +DROP TABLE bug54679; + +# Check that the ROW_FORMAT conversion to/from COMPRESSED works. + +CREATE TABLE bug54679 (a INT) ENGINE=InnoDB; +SELECT TABLE_NAME,ROW_FORMAT,CREATE_OPTIONS FROM information_schema.tables +WHERE TABLE_NAME='bug54679'; + +# KEY_BLOCK_SIZE implies COMPRESSED. +ALTER TABLE bug54679 KEY_BLOCK_SIZE=1; +SELECT TABLE_NAME,ROW_FORMAT,CREATE_OPTIONS FROM information_schema.tables +WHERE TABLE_NAME='bug54679'; + +--replace_regex /'[^']*test\.#sql-[0-9a-f_]*'/'#sql-temporary'/ +--error ER_CANT_CREATE_TABLE +ALTER TABLE bug54679 ROW_FORMAT=REDUNDANT; +--replace_regex /'[^']*test\.#sql-[0-9a-f_]*'/'#sql-temporary'/ +SHOW WARNINGS; +DROP TABLE bug54679; +CREATE TABLE bug54679 (a INT) ENGINE=InnoDB ROW_FORMAT=REDUNDANT; +SELECT TABLE_NAME,ROW_FORMAT,CREATE_OPTIONS FROM information_schema.tables +WHERE TABLE_NAME='bug54679'; + +ALTER TABLE bug54679 KEY_BLOCK_SIZE=2; +SELECT TABLE_NAME,ROW_FORMAT,CREATE_OPTIONS FROM information_schema.tables +WHERE TABLE_NAME='bug54679'; + +# This prevents other than REDUNDANT or COMPACT ROW_FORMAT for new tables. +SET GLOBAL innodb_file_format=Antelope; + +--replace_regex /'[^']*test\.#sql-[0-9a-f_]*'/'#sql-temporary'/ +--error ER_CANT_CREATE_TABLE +ALTER TABLE bug54679 KEY_BLOCK_SIZE=4; +--replace_regex /'[^']*test\.#sql-[0-9a-f_]*'/'#sql-temporary'/ +SHOW WARNINGS; +--replace_regex /'[^']*test\.#sql-[0-9a-f_]*'/'#sql-temporary'/ +--error ER_CANT_CREATE_TABLE +ALTER TABLE bug54679 ROW_FORMAT=DYNAMIC; +--replace_regex /'[^']*test\.#sql-[0-9a-f_]*'/'#sql-temporary'/ +SHOW WARNINGS; +DROP TABLE bug54679; +--replace_regex /'[^']*test\.#sql-[0-9a-f_]*'/'#sql-temporary'/ +--error ER_CANT_CREATE_TABLE +CREATE TABLE bug54679 (a INT) ENGINE=InnoDB ROW_FORMAT=DYNAMIC; +--replace_regex /'[^']*test\.#sql-[0-9a-f_]*'/'#sql-temporary'/ +SHOW WARNINGS; +CREATE TABLE bug54679 (a INT) ENGINE=InnoDB; + +SET GLOBAL innodb_file_format=Barracuda; +# This will prevent ROW_FORMAT=COMPRESSED, because the system tablespace +# cannot be compressed. +SET GLOBAL innodb_file_per_table=OFF; + +--replace_regex /'[^']*test\.#sql-[0-9a-f_]*'/'#sql-temporary'/ +--error ER_CANT_CREATE_TABLE +ALTER TABLE bug54679 KEY_BLOCK_SIZE=4; +--replace_regex /'[^']*test\.#sql-[0-9a-f_]*'/'#sql-temporary'/ +SHOW WARNINGS; +--replace_regex /'[^']*test\.#sql-[0-9a-f_]*'/'#sql-temporary'/ +--error ER_CANT_CREATE_TABLE +ALTER TABLE bug54679 ROW_FORMAT=DYNAMIC; +--replace_regex /'[^']*test\.#sql-[0-9a-f_]*'/'#sql-temporary'/ +SHOW WARNINGS; +DROP TABLE bug54679; +--replace_regex /'[^']*test\.#sql-[0-9a-f_]*'/'#sql-temporary'/ +--error ER_CANT_CREATE_TABLE +CREATE TABLE bug54679 (a INT) ENGINE=InnoDB ROW_FORMAT=DYNAMIC; +--replace_regex /'[^']*test\.#sql-[0-9a-f_]*'/'#sql-temporary'/ +SHOW WARNINGS; +SET GLOBAL innodb_file_per_table=ON; +CREATE TABLE bug54679 (a INT) ENGINE=InnoDB ROW_FORMAT=DYNAMIC; +DROP TABLE bug54679; + +EVAL SET GLOBAL innodb_file_format=$file_format; +EVAL SET GLOBAL innodb_file_format_max=$file_format_max; +EVAL SET GLOBAL innodb_file_per_table=$file_per_table; diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 9868889cbde..97737fd6204 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -6669,6 +6669,7 @@ ha_innobase::create( const ulint file_format = srv_file_format; const char* stmt; size_t stmt_len; + enum row_type row_type; DBUG_ENTER("ha_innobase::create"); @@ -6789,94 +6790,94 @@ ha_innobase::create( } } - if (create_info->used_fields & HA_CREATE_USED_ROW_FORMAT) { - if (flags) { - /* KEY_BLOCK_SIZE was specified. */ - if (form->s->row_type != ROW_TYPE_COMPRESSED) { - /* ROW_FORMAT other than COMPRESSED - ignores KEY_BLOCK_SIZE. It does not - make sense to reject conflicting - KEY_BLOCK_SIZE and ROW_FORMAT, because - such combinations can be obtained - with ALTER TABLE anyway. */ - push_warning_printf( - thd, - MYSQL_ERROR::WARN_LEVEL_WARN, - ER_ILLEGAL_HA_CREATE_OPTION, - "InnoDB: ignoring KEY_BLOCK_SIZE=%lu" - " unless ROW_FORMAT=COMPRESSED.", - create_info->key_block_size); - flags = 0; - } - } else { - /* No KEY_BLOCK_SIZE */ - if (form->s->row_type == ROW_TYPE_COMPRESSED) { - /* ROW_FORMAT=COMPRESSED without - KEY_BLOCK_SIZE implies half the - maximum KEY_BLOCK_SIZE. */ - flags = (DICT_TF_ZSSIZE_MAX - 1) - << DICT_TF_ZSSIZE_SHIFT - | DICT_TF_COMPACT - | DICT_TF_FORMAT_ZIP - << DICT_TF_FORMAT_SHIFT; + row_type = form->s->row_type; + + if (flags) { + /* KEY_BLOCK_SIZE was specified. */ + if (!(create_info->used_fields & HA_CREATE_USED_ROW_FORMAT)) { + /* ROW_FORMAT was not specified; + default to ROW_FORMAT=COMPRESSED */ + row_type = ROW_TYPE_COMPRESSED; + } else if (row_type != ROW_TYPE_COMPRESSED) { + /* ROW_FORMAT other than COMPRESSED + ignores KEY_BLOCK_SIZE. It does not + make sense to reject conflicting + KEY_BLOCK_SIZE and ROW_FORMAT, because + such combinations can be obtained + with ALTER TABLE anyway. */ + push_warning_printf( + thd, + MYSQL_ERROR::WARN_LEVEL_WARN, + ER_ILLEGAL_HA_CREATE_OPTION, + "InnoDB: ignoring KEY_BLOCK_SIZE=%lu" + " unless ROW_FORMAT=COMPRESSED.", + create_info->key_block_size); + flags = 0; + } + } else { + /* No KEY_BLOCK_SIZE */ + if (row_type == ROW_TYPE_COMPRESSED) { + /* ROW_FORMAT=COMPRESSED without + KEY_BLOCK_SIZE implies half the + maximum KEY_BLOCK_SIZE. */ + flags = (DICT_TF_ZSSIZE_MAX - 1) + << DICT_TF_ZSSIZE_SHIFT + | DICT_TF_COMPACT + | DICT_TF_FORMAT_ZIP + << DICT_TF_FORMAT_SHIFT; #if DICT_TF_ZSSIZE_MAX < 1 # error "DICT_TF_ZSSIZE_MAX < 1" #endif - } } + } - switch (form->s->row_type) { - const char* row_format_name; - case ROW_TYPE_REDUNDANT: - break; - case ROW_TYPE_COMPRESSED: - case ROW_TYPE_DYNAMIC: - row_format_name - = form->s->row_type == ROW_TYPE_COMPRESSED - ? "COMPRESSED" - : "DYNAMIC"; + switch (row_type) { + const char* row_format_name; + case ROW_TYPE_REDUNDANT: + break; + case ROW_TYPE_COMPRESSED: + case ROW_TYPE_DYNAMIC: + row_format_name + = row_type == ROW_TYPE_COMPRESSED + ? "COMPRESSED" + : "DYNAMIC"; - if (!srv_file_per_table) { - push_warning_printf( - thd, - MYSQL_ERROR::WARN_LEVEL_WARN, - ER_ILLEGAL_HA_CREATE_OPTION, - "InnoDB: ROW_FORMAT=%s" - " requires innodb_file_per_table.", - row_format_name); - } else if (file_format < DICT_TF_FORMAT_ZIP) { - push_warning_printf( - thd, - MYSQL_ERROR::WARN_LEVEL_WARN, - ER_ILLEGAL_HA_CREATE_OPTION, - "InnoDB: ROW_FORMAT=%s" - " requires innodb_file_format >" - " Antelope.", - row_format_name); - } else { - flags |= DICT_TF_COMPACT - | (DICT_TF_FORMAT_ZIP - << DICT_TF_FORMAT_SHIFT); - break; - } - - /* fall through */ - case ROW_TYPE_NOT_USED: - case ROW_TYPE_FIXED: - default: - push_warning(thd, - MYSQL_ERROR::WARN_LEVEL_WARN, - ER_ILLEGAL_HA_CREATE_OPTION, - "InnoDB: assuming ROW_FORMAT=COMPACT."); - case ROW_TYPE_DEFAULT: - case ROW_TYPE_COMPACT: - flags = DICT_TF_COMPACT; + if (!srv_file_per_table) { + push_warning_printf( + thd, + MYSQL_ERROR::WARN_LEVEL_WARN, + ER_ILLEGAL_HA_CREATE_OPTION, + "InnoDB: ROW_FORMAT=%s" + " requires innodb_file_per_table.", + row_format_name); + } else if (file_format < DICT_TF_FORMAT_ZIP) { + push_warning_printf( + thd, + MYSQL_ERROR::WARN_LEVEL_WARN, + ER_ILLEGAL_HA_CREATE_OPTION, + "InnoDB: ROW_FORMAT=%s" + " requires innodb_file_format >" + " Antelope.", + row_format_name); + } else { + flags |= DICT_TF_COMPACT + | (DICT_TF_FORMAT_ZIP + << DICT_TF_FORMAT_SHIFT); break; } - } else if (!flags) { - /* No KEY_BLOCK_SIZE or ROW_FORMAT specified: - use ROW_FORMAT=COMPACT by default. */ + + /* fall through */ + case ROW_TYPE_NOT_USED: + case ROW_TYPE_FIXED: + default: + push_warning(thd, + MYSQL_ERROR::WARN_LEVEL_WARN, + ER_ILLEGAL_HA_CREATE_OPTION, + "InnoDB: assuming ROW_FORMAT=COMPACT."); + case ROW_TYPE_DEFAULT: + case ROW_TYPE_COMPACT: flags = DICT_TF_COMPACT; + break; } /* Look for a primary key */ From fce131cceef0ae818b41451f89c45aa0b1fbf3a2 Mon Sep 17 00:00:00 2001 From: Sunny Bains Date: Fri, 25 Jun 2010 12:59:37 +1000 Subject: [PATCH 032/115] Fix bug#54583. This change reverses r1530 by getting rid of a bogus assertion and clarifies the invariant in dict_table_get_on_id(). In Mar 2007 Marko observed a crash during recovery, the crash resulted from an UNDO operation on a system table. His solution was to acquire an X lock on the data dictionary, this in hindsight was an overkill. It is unclear what caused the crash, current hypothesis is that it was a memory corruption. The X lock results in performance issues by when undoing changes due to rollback during normal operation on regular tables. Why the change is safe: ====================== The InnoDB code has changed since the original X lock change was made. In the new code we always lock the data dictionary in X mode during startup when UNDOing operations on the system tables (this is a given). This ensures that the crash Marko observed cannot happen as long as all transactions that update the system tables follow the standard rules by setting the appropriate DICT_OP flag when writing the log records when they make the changes. If transactions violate the above mentioned rule then during recovery (at startup) the rollback code (see trx0roll.c) will not acquire the X lock and we will see the crash again. This will however be a different bug. --- storage/innobase/dict/dict0dict.c | 10 ++++------ storage/innobase/include/sync0sync.h | 2 +- storage/innobase/row/row0undo.c | 4 ++-- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/storage/innobase/dict/dict0dict.c b/storage/innobase/dict/dict0dict.c index 064a5a34960..802f0bd8b6f 100644 --- a/storage/innobase/dict/dict0dict.c +++ b/storage/innobase/dict/dict0dict.c @@ -581,13 +581,11 @@ dict_table_get_on_id( if (table_id <= DICT_FIELDS_ID || trx->dict_operation_lock_mode == RW_X_LATCH) { - /* It is a system table which will always exist in the table - cache: we avoid acquiring the dictionary mutex, because - if we are doing a rollback to handle an error in TABLE - CREATE, for example, we already have the mutex! */ - ut_ad(mutex_own(&(dict_sys->mutex)) - || trx->dict_operation_lock_mode == RW_X_LATCH); + /* Note: An X latch implies that the transaction + already owns the dictionary mutex. */ + + ut_ad(mutex_own(&dict_sys->mutex)); return(dict_table_get_on_id_low(table_id)); } diff --git a/storage/innobase/include/sync0sync.h b/storage/innobase/include/sync0sync.h index 4e73bee9108..940e583350a 100644 --- a/storage/innobase/include/sync0sync.h +++ b/storage/innobase/include/sync0sync.h @@ -621,7 +621,7 @@ or row lock! */ #define SYNC_FILE_FORMAT_TAG 1200 /* Used to serialize access to the file format tag */ #define SYNC_DICT_OPERATION 1001 /* table create, drop, etc. reserve - this in X-mode, implicit or backround + this in X-mode; implicit or backround operations purge, rollback, foreign key checks reserve this in S-mode */ #define SYNC_DICT 1000 diff --git a/storage/innobase/row/row0undo.c b/storage/innobase/row/row0undo.c index ca321c269c7..ef97fa50cd1 100644 --- a/storage/innobase/row/row0undo.c +++ b/storage/innobase/row/row0undo.c @@ -296,7 +296,7 @@ row_undo( if (locked_data_dict) { - row_mysql_lock_data_dictionary(trx); + row_mysql_freeze_data_dictionary(trx); } if (node->state == UNDO_NODE_INSERT) { @@ -311,7 +311,7 @@ row_undo( if (locked_data_dict) { - row_mysql_unlock_data_dictionary(trx); + row_mysql_unfreeze_data_dictionary(trx); } /* Do some cleanup */ From 7f1e5a37a0eaed2867be78e43d2984a31284af84 Mon Sep 17 00:00:00 2001 From: Jimmy Yang Date: Thu, 24 Jun 2010 22:14:20 -0700 Subject: [PATCH 033/115] Moved fix for Bug#54044 to security branch. Undo revno:3116 --- .../suite/innodb/r/innodb_bug54044.result | 3 --- .../suite/innodb/t/innodb_bug54044.test | 11 --------- storage/innobase/handler/ha_innodb.cc | 24 ++----------------- 3 files changed, 2 insertions(+), 36 deletions(-) delete mode 100644 mysql-test/suite/innodb/r/innodb_bug54044.result delete mode 100644 mysql-test/suite/innodb/t/innodb_bug54044.test diff --git a/mysql-test/suite/innodb/r/innodb_bug54044.result b/mysql-test/suite/innodb/r/innodb_bug54044.result deleted file mode 100644 index 9574381d8e1..00000000000 --- a/mysql-test/suite/innodb/r/innodb_bug54044.result +++ /dev/null @@ -1,3 +0,0 @@ -CREATE TEMPORARY TABLE TABLE_54044 ENGINE = INNODB -AS SELECT IF(NULL IS NOT NULL, NULL, NULL); -ERROR HY000: Can't create table 'test.TABLE_54044' (errno: -1) diff --git a/mysql-test/suite/innodb/t/innodb_bug54044.test b/mysql-test/suite/innodb/t/innodb_bug54044.test deleted file mode 100644 index 824450ae1a6..00000000000 --- a/mysql-test/suite/innodb/t/innodb_bug54044.test +++ /dev/null @@ -1,11 +0,0 @@ -# This is the test for bug #54044. Special handle MYSQL_TYPE_NULL type -# during create table, so it will not trigger assertion failure. - ---source include/have_innodb.inc - -# This 'create table' operation should fail because of -# using NULL datatype ---error ER_CANT_CREATE_TABLE -CREATE TEMPORARY TABLE TABLE_54044 ENGINE = INNODB - AS SELECT IF(NULL IS NOT NULL, NULL, NULL); - diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 97737fd6204..f3d9bcd8d59 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -4139,11 +4139,6 @@ get_innobase_type_from_mysql_type( case MYSQL_TYPE_BLOB: case MYSQL_TYPE_LONG_BLOB: return(DATA_BLOB); - case MYSQL_TYPE_NULL: - /* MySQL currently accepts "NULL" datatype, but will - reject such datatype in the next release. We will cope - with it and not trigger assertion failure in 5.1 */ - break; default: ut_error; } @@ -6194,22 +6189,7 @@ create_table_def( field = form->field[i]; col_type = get_innobase_type_from_mysql_type(&unsigned_type, - field); - - if (!col_type) { - push_warning_printf( - (THD*) trx->mysql_thd, - MYSQL_ERROR::WARN_LEVEL_WARN, - ER_CANT_CREATE_TABLE, - "Error creating table '%s' with " - "column '%s'. Please check its " - "column type and try to re-create " - "the table with an appropriate " - "column type.", - table->name, (char*) field->field_name); - goto err_col; - } - + field); if (field->null_ptr) { nulls_allowed = 0; } else { @@ -6267,7 +6247,7 @@ create_table_def( if (dict_col_name_is_reserved(field->field_name)){ my_error(ER_WRONG_COLUMN_NAME, MYF(0), field->field_name); -err_col: + dict_mem_table_free(table); trx_commit_for_mysql(trx); From 9702d53ff8c340e9b9563e7f5298b336c2af7f3a Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Fri, 25 Jun 2010 09:07:18 +0200 Subject: [PATCH 034/115] Bug #53757 assert in mysql_truncate_by_delete The assert was triggered if a connection executing TRUNCATE on a InnoDB table was killed during open_tables. This bug was fixed in the scope of Bug #45643 "InnoDB does not support replication of TRUNCATE TABLE". This patch adds test coverage to innodb_mysql_sync.test. --- mysql-test/r/innodb_mysql_sync.result | 18 ++++++++++++++++++ mysql-test/t/innodb_mysql_sync.test | 26 ++++++++++++++++++++++++++ sql/sql_base.cc | 1 + 3 files changed, 45 insertions(+) diff --git a/mysql-test/r/innodb_mysql_sync.result b/mysql-test/r/innodb_mysql_sync.result index 0e75e62b13a..43a98829d4e 100644 --- a/mysql-test/r/innodb_mysql_sync.result +++ b/mysql-test/r/innodb_mysql_sync.result @@ -48,3 +48,21 @@ Warnings: Error 1146 Table 'test.t1' doesn't exist # Connection default SET DEBUG_SYNC= "RESET"; +# +# Bug#53757 assert in mysql_truncate_by_delete +# +DROP TABLE IF EXISTS t1, t2; +CREATE TABLE t1(a INT) Engine=InnoDB; +CREATE TABLE t2(id INT); +INSERT INTO t1 VALUES (1), (2); +INSERT INTO t2 VALUES(connection_id()); +SET DEBUG_SYNC= "open_and_process_table SIGNAL opening WAIT_FOR killed"; +# Sending: (not reaped since connection is killed later) +TRUNCATE t1; +SET DEBUG_SYNC= "now WAIT_FOR opening"; +SELECT ((@id := id) - id) FROM t2; +((@id := id) - id) +0 +KILL @id; +SET DEBUG_SYNC= "now SIGNAL killed"; +DROP TABLE t1, t2; diff --git a/mysql-test/t/innodb_mysql_sync.test b/mysql-test/t/innodb_mysql_sync.test index ee92ee9f52e..07f75afec40 100644 --- a/mysql-test/t/innodb_mysql_sync.test +++ b/mysql-test/t/innodb_mysql_sync.test @@ -80,6 +80,32 @@ disconnect con1; SET DEBUG_SYNC= "RESET"; +--echo # +--echo # Bug#53757 assert in mysql_truncate_by_delete +--echo # + +--disable_warnings +DROP TABLE IF EXISTS t1, t2; +--enable_warnings + +CREATE TABLE t1(a INT) Engine=InnoDB; +CREATE TABLE t2(id INT); +INSERT INTO t1 VALUES (1), (2); + +connect (con1, localhost, root); +INSERT INTO t2 VALUES(connection_id()); +SET DEBUG_SYNC= "open_and_process_table SIGNAL opening WAIT_FOR killed"; +--echo # Sending: (not reaped since connection is killed later) +--send TRUNCATE t1 + +connection default; +SET DEBUG_SYNC= "now WAIT_FOR opening"; +SELECT ((@id := id) - id) FROM t2; +KILL @id; +SET DEBUG_SYNC= "now SIGNAL killed"; +DROP TABLE t1, t2; + + # Check that all connections opened by test cases in this file are really # gone so execution of other tests won't be affected by their presence. --source include/wait_until_count_sessions.inc diff --git a/sql/sql_base.cc b/sql/sql_base.cc index bd73dd57367..0d4d949d701 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -4429,6 +4429,7 @@ open_and_process_table(THD *thd, LEX *lex, TABLE_LIST *tables, bool error= FALSE; bool safe_to_ignore_table= FALSE; DBUG_ENTER("open_and_process_table"); + DEBUG_SYNC(thd, "open_and_process_table"); /* Ignore placeholders for derived tables. After derived tables From e79d41893ab713253ebe6a55fc238a4e50e6501b Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Sat, 26 Jun 2010 22:23:28 +0200 Subject: [PATCH 035/115] Bug #49891 View DDL breaks REPEATABLE READ The problem was that if a query accessing a view was blocked due to conflicting locks on tables in the view definition, it would be possible for a different connection to alter the view definition before the view query completed. When the view query later resumed, it used the old view definition. This meant that if the view query was later repeated inside the same transaction, the two executions of the query would give different results, thus breaking repeatable read. (The first query used the old view definition, the second used the new view definition). This bug is no longer repeatable with the recent changes to the metadata locking subsystem (revno: 3040). The view query will no longer back-off and release the lock on the view definiton. Instead it will wait for the conflicting lock(s) to go away while keeping the view definition lock. This means that it is no longer possible for a concurrent connection to alter the view definition. Instead, any such attempt will be blocked. In the case from the bug report where the same view query was executed twice inside the same transaction, any ALTER VIEW from other connections will now be blocked until the transaction has completed (or aborted). The view queries will therefore use the same view definition and we will have repeatable read. Test case added to innodb_mysql_lock.test. This patch contains no code changes. --- mysql-test/r/innodb_mysql_lock.result | 32 +++++++++++++ mysql-test/t/innodb_mysql_lock.test | 68 +++++++++++++++++++++++++++ 2 files changed, 100 insertions(+) diff --git a/mysql-test/r/innodb_mysql_lock.result b/mysql-test/r/innodb_mysql_lock.result index 95adf712cb4..bf1c3a89f40 100644 --- a/mysql-test/r/innodb_mysql_lock.result +++ b/mysql-test/r/innodb_mysql_lock.result @@ -116,3 +116,35 @@ Table Op Msg_type Msg_text test.t1 optimize note Table does not support optimize, doing recreate + analyze instead test.t1 optimize status OK DROP TABLE t1; +# +# Bug#49891 View DDL breaks REPEATABLE READ +# +DROP TABLE IF EXISTS t1, t2; +DROP VIEW IF EXISTS v2; +CREATE TABLE t1 ( f1 INTEGER ) ENGINE = innodb; +CREATE TABLE t2 ( f1 INTEGER ); +CREATE VIEW v1 AS SELECT 1 FROM t1; +# Connection con3 +LOCK TABLE t1 WRITE; +# Connection default +START TRANSACTION; +# Sending: +SELECT * FROM v1; +# Connection con2 +# Waiting for 'SELECT * FROM v1' to sync in. +# Sending: +ALTER VIEW v1 AS SELECT 2 FROM t2; +# Connection con3 +# Waiting for 'ALTER VIEW v1 AS SELECT 2 FROM t2' to sync in. +UNLOCK TABLES; +# Connection default; +# Reaping: SELECT * FROM v1 +1 +SELECT * FROM v1; +1 +COMMIT; +# Connection con2 +# Reaping: ALTER VIEW v1 AS SELECT 2 FROM t2 +# Connection default +DROP TABLE t1, t2; +DROP VIEW v1; diff --git a/mysql-test/t/innodb_mysql_lock.test b/mysql-test/t/innodb_mysql_lock.test index 36d09b4c411..c8ece729b19 100644 --- a/mysql-test/t/innodb_mysql_lock.test +++ b/mysql-test/t/innodb_mysql_lock.test @@ -209,6 +209,74 @@ disconnect con1; DROP TABLE t1; +--echo # +--echo # Bug#49891 View DDL breaks REPEATABLE READ +--echo # + +--disable_warnings +DROP TABLE IF EXISTS t1, t2; +DROP VIEW IF EXISTS v2; +--enable_warnings + +CREATE TABLE t1 ( f1 INTEGER ) ENGINE = innodb; +CREATE TABLE t2 ( f1 INTEGER ); +CREATE VIEW v1 AS SELECT 1 FROM t1; + +connect (con2, localhost, root); +connect (con3, localhost, root); + +--echo # Connection con3 +connection con3; +LOCK TABLE t1 WRITE; + +--echo # Connection default +connection default; +START TRANSACTION; +# This should block due to t1 being locked. +--echo # Sending: +--send SELECT * FROM v1 + +--echo # Connection con2 +connection con2; +--echo # Waiting for 'SELECT * FROM v1' to sync in. +let $wait_condition= + SELECT COUNT(*) = 1 FROM information_schema.processlist + WHERE state = "Waiting for table" AND info = "SELECT * FROM v1"; +--source include/wait_condition.inc +# This should block due to v1 being locked. +--echo # Sending: +--send ALTER VIEW v1 AS SELECT 2 FROM t2 + +--echo # Connection con3 +connection con3; +--echo # Waiting for 'ALTER VIEW v1 AS SELECT 2 FROM t2' to sync in. +let $wait_condition= + SELECT COUNT(*) = 1 FROM information_schema.processlist + WHERE state = "Waiting for table" AND info = "ALTER VIEW v1 AS SELECT 2 FROM t2"; +--source include/wait_condition.inc +# Unlock t1 allowing SELECT * FROM v1 to proceed. +UNLOCK TABLES; + +--echo # Connection default; +connection default; +--echo # Reaping: SELECT * FROM v1 +--reap +SELECT * FROM v1; +COMMIT; + +--echo # Connection con2 +connection con2; +--echo # Reaping: ALTER VIEW v1 AS SELECT 2 FROM t2 +--reap + +--echo # Connection default +connection default; +DROP TABLE t1, t2; +DROP VIEW v1; +disconnect con2; +disconnect con3; + + # Check that all connections opened by test cases in this file are really # gone so execution of other tests won't be affected by their presence. --source include/wait_until_count_sessions.inc From 3d2c88ad563a994e6f7d4579a479d553795c4a5f Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Mon, 28 Jun 2010 15:52:24 +0300 Subject: [PATCH 036/115] Disable UNIV_DEBUG when WITH_DEBUG is enabled It causes lots of failures due to Bug#54861 Additional connections not handled properly in mtr --embedded should be re-enabled when that bug is resolved --- storage/innobase/CMakeLists.txt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/storage/innobase/CMakeLists.txt b/storage/innobase/CMakeLists.txt index 23b7fb2d479..8e3e4efbb0e 100644 --- a/storage/innobase/CMakeLists.txt +++ b/storage/innobase/CMakeLists.txt @@ -41,9 +41,11 @@ IF(UNIX) ENDIF() # Enable InnoDB's UNIV_DEBUG if MySQL's WITH_DEBUG[_FULL] is defined -IF(WITH_DEBUG OR WITH_DEBUG_FULL) - ADD_DEFINITIONS("-DUNIV_DEBUG") -ENDIF() +# enable when this bug is resolved: +# Bug#54861 Additional connections not handled properly in mtr --embedded +#IF(WITH_DEBUG OR WITH_DEBUG_FULL) +# ADD_DEFINITIONS("-DUNIV_DEBUG") +#ENDIF() IF(NOT MSVC) # either define HAVE_IB_GCC_ATOMIC_BUILTINS or not From dec24a7dd8b6ed6b2d8e92649ffb3b8df3edffdc Mon Sep 17 00:00:00 2001 From: Jimmy Yang Date: Mon, 28 Jun 2010 19:41:37 -0700 Subject: [PATCH 037/115] Check in fix for bug #53756: "ALTER TABLE ADD PRIMARY KEY affects crash recovery" rb://369 approved by Marko --- .../suite/innodb/r/innodb_bug53756.result | 118 +++++++++++ .../suite/innodb/t/innodb_bug53756.test | 184 ++++++++++++++++++ storage/innobase/dict/dict0load.c | 27 ++- 3 files changed, 315 insertions(+), 14 deletions(-) create mode 100644 mysql-test/suite/innodb/r/innodb_bug53756.result create mode 100644 mysql-test/suite/innodb/t/innodb_bug53756.test diff --git a/mysql-test/suite/innodb/r/innodb_bug53756.result b/mysql-test/suite/innodb/r/innodb_bug53756.result new file mode 100644 index 00000000000..67797f9c90f --- /dev/null +++ b/mysql-test/suite/innodb/r/innodb_bug53756.result @@ -0,0 +1,118 @@ +DROP TABLE IF EXISTS bug_53756 ; +CREATE TABLE bug_53756 (pk INT, c1 INT) ENGINE=InnoDB; +ALTER TABLE bug_53756 ADD PRIMARY KEY (pk); +INSERT INTO bug_53756 VALUES(1, 11), (2, 22), (3, 33), (4, 44); + +# Select a less restrictive isolation level. +SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED; +SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; +COMMIT; + +# Start a transaction in the default connection for isolation. +START TRANSACTION; +SELECT @@tx_isolation; +@@tx_isolation +READ-COMMITTED +SELECT * FROM bug_53756; +pk c1 +1 11 +2 22 +3 33 +4 44 + +# connection con1 deletes row 1 +START TRANSACTION; +SELECT @@tx_isolation; +@@tx_isolation +READ-COMMITTED +DELETE FROM bug_53756 WHERE pk=1; + +# connection con2 deletes row 2 +START TRANSACTION; +SELECT @@tx_isolation; +@@tx_isolation +READ-COMMITTED +DELETE FROM bug_53756 WHERE pk=2; + +# connection con3 updates row 3 +START TRANSACTION; +SELECT @@tx_isolation; +@@tx_isolation +READ-COMMITTED +UPDATE bug_53756 SET c1=77 WHERE pk=3; + +# connection con4 updates row 4 +START TRANSACTION; +SELECT @@tx_isolation; +@@tx_isolation +READ-COMMITTED +UPDATE bug_53756 SET c1=88 WHERE pk=4; + +# connection con5 inserts row 5 +START TRANSACTION; +SELECT @@tx_isolation; +@@tx_isolation +READ-COMMITTED +INSERT INTO bug_53756 VALUES(5, 55); + +# connection con6 inserts row 6 +START TRANSACTION; +SELECT @@tx_isolation; +@@tx_isolation +READ-COMMITTED +INSERT INTO bug_53756 VALUES(6, 66); + +# connection con1 commits. +COMMIT; + +# connection con3 commits. +COMMIT; + +# connection con4 rolls back. +ROLLBACK; + +# connection con6 rolls back. +ROLLBACK; + +# The connections 2 and 5 stay open. + +# connection default selects resulting data. +# Delete of row 1 was committed. +# Dpdate of row 3 was committed. +# Due to isolation level read committed, these should be included. +# All other changes should not be included. +SELECT * FROM bug_53756; +pk c1 +2 22 +3 77 +4 44 + +# connection default +# +# Crash server. +START TRANSACTION; +INSERT INTO bug_53756 VALUES (666,666); +SET SESSION debug="+d,crash_commit_before"; +COMMIT; +ERROR HY000: Lost connection to MySQL server during query + +# +# disconnect con1, con2, con3, con4, con5, con6. +# +# Restart server. + +# +# Select recovered data. +# Delete of row 1 was committed. +# Update of row 3 was committed. +# These should be included. +# All other changes should not be included. +# Delete of row 2 and insert of row 5 should be rolled back +SELECT * FROM bug_53756; +pk c1 +2 22 +3 77 +4 44 + +# Clean up. +DROP TABLE bug_53756; diff --git a/mysql-test/suite/innodb/t/innodb_bug53756.test b/mysql-test/suite/innodb/t/innodb_bug53756.test new file mode 100644 index 00000000000..85a09478486 --- /dev/null +++ b/mysql-test/suite/innodb/t/innodb_bug53756.test @@ -0,0 +1,184 @@ +# This is the test case for bug #53756. Alter table operation could +# leave a deleted record for the temp table (later renamed to the altered +# table) in the SYS_TABLES secondary index, we should ignore this row and +# find the first non-deleted row for the specified table_id when load table +# metadata in the function dict_load_table_on_id() during crash recovery. + +# +# innobackup needs to connect to the server. Not supported in embedded. +--source include/not_embedded.inc +# +# This test case needs to crash the server. Needs a debug server. +--source include/have_debug.inc +# +# Don't test this under valgrind, memory leaks will occur. +--source include/not_valgrind.inc +# +# This test case needs InnoDB. +--source include/have_innodb.inc + +# +# Precautionary clean up. +# +--disable_warnings +DROP TABLE IF EXISTS bug_53756 ; +--enable_warnings + +# +# Create test data. +# +CREATE TABLE bug_53756 (pk INT, c1 INT) ENGINE=InnoDB; +ALTER TABLE bug_53756 ADD PRIMARY KEY (pk); +INSERT INTO bug_53756 VALUES(1, 11), (2, 22), (3, 33), (4, 44); + +--echo +--echo # Select a less restrictive isolation level. +# Don't use user variables. They won't survive server crash. +--let $global_isolation= `SELECT @@global.tx_isolation`; +--let $session_isolation= `SELECT @@session.tx_isolation`; +SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED; +SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; +COMMIT; + +--echo +--echo # Start a transaction in the default connection for isolation. +START TRANSACTION; +SELECT @@tx_isolation; +SELECT * FROM bug_53756; + +--echo +--echo # connection con1 deletes row 1 +--connect (con1,localhost,root,,) +START TRANSACTION; +SELECT @@tx_isolation; +DELETE FROM bug_53756 WHERE pk=1; + +--echo +--echo # connection con2 deletes row 2 +--connect (con2,localhost,root,,) +START TRANSACTION; +SELECT @@tx_isolation; +DELETE FROM bug_53756 WHERE pk=2; + +--echo +--echo # connection con3 updates row 3 +--connect (con3,localhost,root,,) +START TRANSACTION; +SELECT @@tx_isolation; +UPDATE bug_53756 SET c1=77 WHERE pk=3; + +--echo +--echo # connection con4 updates row 4 +--connect (con4,localhost,root,,) +START TRANSACTION; +SELECT @@tx_isolation; +UPDATE bug_53756 SET c1=88 WHERE pk=4; + +--echo +--echo # connection con5 inserts row 5 +--connect (con5,localhost,root,,) +START TRANSACTION; +SELECT @@tx_isolation; +INSERT INTO bug_53756 VALUES(5, 55); + +--echo +--echo # connection con6 inserts row 6 +--connect (con6,localhost,root,,) +START TRANSACTION; +SELECT @@tx_isolation; +INSERT INTO bug_53756 VALUES(6, 66); + +--echo +--echo # connection con1 commits. +--connection con1 +COMMIT; + +--echo +--echo # connection con3 commits. +--connection con3 +COMMIT; + +--echo +--echo # connection con4 rolls back. +--connection con4 +ROLLBACK; + +--echo +--echo # connection con6 rolls back. +--connection con6 +ROLLBACK; + +--echo +--echo # The connections 2 and 5 stay open. + +--echo +--echo # connection default selects resulting data. +--echo # Delete of row 1 was committed. +--echo # Dpdate of row 3 was committed. +--echo # Due to isolation level read committed, these should be included. +--echo # All other changes should not be included. +--connection default +SELECT * FROM bug_53756; + +--echo +--echo # connection default +--connection default +--echo # +--echo # Crash server. +# +# Write file to make mysql-test-run.pl expect the "crash", but don't start +# it until it's told to +--exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +# +START TRANSACTION; +INSERT INTO bug_53756 VALUES (666,666); +# +# Request a crash on next execution of commit. +SET SESSION debug="+d,crash_commit_before"; +# +# Execute the statement that causes the crash. +--error 2013 +COMMIT; +--echo +--echo # +--echo # disconnect con1, con2, con3, con4, con5, con6. +--disconnect con1 +--disconnect con2 +--disconnect con3 +--disconnect con4 +--disconnect con5 +--disconnect con6 +--echo # +--echo # Restart server. +# +# Write file to make mysql-test-run.pl start up the server again +--exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +# +# Turn on reconnect +--enable_reconnect +# +# Call script that will poll the server waiting for it to be back online again +--source include/wait_until_connected_again.inc +# +# Turn off reconnect again +--disable_reconnect +--echo + +--echo # +--echo # Select recovered data. +--echo # Delete of row 1 was committed. +--echo # Update of row 3 was committed. +--echo # These should be included. +--echo # All other changes should not be included. +--echo # Delete of row 2 and insert of row 5 should be rolled back +SELECT * FROM bug_53756; + +--echo +--echo # Clean up. +DROP TABLE bug_53756; + +--disable_query_log +eval SET GLOBAL tx_isolation= '$global_isolation'; +eval SET SESSION tx_isolation= '$session_isolation'; +--enable_query_log + diff --git a/storage/innobase/dict/dict0load.c b/storage/innobase/dict/dict0load.c index 65f1c9536bd..d5e7600f4d0 100644 --- a/storage/innobase/dict/dict0load.c +++ b/storage/innobase/dict/dict0load.c @@ -927,6 +927,8 @@ dict_load_table_on_id( ut_ad(mutex_own(&(dict_sys->mutex))); + table = NULL; + /* NOTE that the operation of this function is protected by the dictionary mutex, and therefore no deadlocks can occur with other dictionary operations. */ @@ -953,15 +955,17 @@ dict_load_table_on_id( BTR_SEARCH_LEAF, &pcur, &mtr); rec = btr_pcur_get_rec(&pcur); - if (!btr_pcur_is_on_user_rec(&pcur, &mtr) - || rec_get_deleted_flag(rec, 0)) { + if (!btr_pcur_is_on_user_rec(&pcur, &mtr)) { /* Not found */ + goto func_exit; + } - btr_pcur_close(&pcur); - mtr_commit(&mtr); - mem_heap_free(heap); - - return(NULL); + /* Find the first record that is not delete marked */ + while (rec_get_deleted_flag(rec, 0)) { + if (!btr_pcur_move_to_next_user_rec(&pcur, &mtr)) { + goto func_exit; + } + rec = btr_pcur_get_rec(&pcur); } /*---------------------------------------------------*/ @@ -974,19 +978,14 @@ dict_load_table_on_id( /* Check if the table id in record is the one searched for */ if (ut_dulint_cmp(table_id, mach_read_from_8(field)) != 0) { - - btr_pcur_close(&pcur); - mtr_commit(&mtr); - mem_heap_free(heap); - - return(NULL); + goto func_exit; } /* Now we get the table name from the record */ field = rec_get_nth_field_old(rec, 1, &len); /* Load the table definition to memory */ table = dict_load_table(mem_heap_strdupl(heap, (char*) field, len)); - +func_exit: btr_pcur_close(&pcur); mtr_commit(&mtr); mem_heap_free(heap); From 48a74d7472d92915c12671d2b0563df5d44614b4 Mon Sep 17 00:00:00 2001 From: Dmitry Shulga Date: Tue, 29 Jun 2010 16:32:03 +0700 Subject: [PATCH 038/115] Fixed bug #51855. Race condition in XA START. If several threads concurrently execute the statement XA START 'x', then mysqld server could crash. --- sql/sql_class.cc | 10 +++++++--- sql/sql_parse.cc | 26 ++++++++++++++++++-------- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 93aa6a8268c..99792f2b262 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -3365,9 +3365,13 @@ bool xid_cache_insert(XID *xid, enum xa_states xa_state) bool xid_cache_insert(XID_STATE *xid_state) { pthread_mutex_lock(&LOCK_xid_cache); - DBUG_ASSERT(hash_search(&xid_cache, xid_state->xid.key(), - xid_state->xid.key_length())==0); - my_bool res=my_hash_insert(&xid_cache, (uchar*)xid_state); + if (hash_search(&xid_cache, xid_state->xid.key(), xid_state->xid.key_length())) + { + pthread_mutex_unlock(&LOCK_xid_cache); + my_error(ER_XAER_DUPID, MYF(0)); + return TRUE; + } + my_bool res= my_hash_insert(&xid_cache, (uchar*)xid_state); pthread_mutex_unlock(&LOCK_xid_cache); return res; } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index ed2c76fdcb8..a8dede3e7f5 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -4730,7 +4730,7 @@ create_sp_error: my_error(ER_XAER_NOTA, MYF(0)); break; } - thd->transaction.xid_state.xa_state=XA_ACTIVE; + thd->transaction.xid_state.xa_state= XA_ACTIVE; my_ok(thd); break; } @@ -4750,16 +4750,16 @@ create_sp_error: my_error(ER_XAER_OUTSIDE, MYF(0)); break; } - if (xid_cache_search(thd->lex->xid)) - { - my_error(ER_XAER_DUPID, MYF(0)); - break; - } DBUG_ASSERT(thd->transaction.xid_state.xid.is_null()); - thd->transaction.xid_state.xa_state=XA_ACTIVE; + thd->transaction.xid_state.xa_state= XA_ACTIVE; thd->transaction.xid_state.rm_error= 0; thd->transaction.xid_state.xid.set(thd->lex->xid); - xid_cache_insert(&thd->transaction.xid_state); + if (xid_cache_insert(&thd->transaction.xid_state)) + { + thd->transaction.xid_state.xa_state= XA_NOTR; + thd->transaction.xid_state.xid.null(); + break; + } thd->transaction.all.modified_non_trans_table= FALSE; thd->options= ((thd->options & ~(OPTION_KEEP_LOG)) | OPTION_BEGIN); thd->server_status|= SERVER_STATUS_IN_TRANS; @@ -4813,6 +4813,16 @@ create_sp_error: case SQLCOM_XA_COMMIT: if (!thd->transaction.xid_state.xid.eq(thd->lex->xid)) { + /* + xid_state.in_thd is always true beside of xa recovery + procedure. Note, that there is no race condition here + between xid_cache_search and xid_cache_delete, since we're always + deleting our own XID (thd->lex->xid == thd->transaction.xid_state.xid). + The only case when thd->lex->xid != thd->transaction.xid_state.xid + and xid_state->in_thd == 0 is in ha_recover() functionality, + which is called before starting client connections, and thus is + always single-threaded. + */ XID_STATE *xs=xid_cache_search(thd->lex->xid); if (!xs || xs->in_thd) my_error(ER_XAER_NOTA, MYF(0)); From deaba603e4ac60d1c15c13b773a116ee4c49e717 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 29 Jun 2010 14:32:48 +0300 Subject: [PATCH 039/115] Bug#52199 utf32: mbminlen=4, mbmaxlen=4, type->mbminlen=0, type->mbmaxlen=4 Merge and adjust a forgotten change to fix this bug. rb://393 approved by Jimmy Yang ------------------------------------------------------------------------ r3794 | marko | 2009-01-07 14:14:53 +0000 (Wed, 07 Jan 2009) | 18 lines branches/6.0: Allow the minimum length of a multi-byte character to be up to 4 bytes. (Bug #35391) dtype_t, dict_col_t: Replace mbminlen:2, mbmaxlen:3 with mbminmaxlen:5. In this way, the 5 bits can hold two values of 0..4, and the storage size of the fields will not cross the 64-bit boundary. Encode the values as DATA_MBMAX * mbmaxlen + mbminlen. Define the auxiliary macros DB_MBMINLEN(mbminmaxlen), DB_MBMAXLEN(mbminmaxlen), and DB_MINMAXLEN(mbminlen, mbmaxlen). Try to trim and pad UTF-16 and UTF-32 with spaces as appropriate. Alexander Barkov suggested the use of cs->cset->fill(cs, buff, len, 0x20). ha_innobase::store_key_val_for_row() now does that, but the added function row_mysql_pad_col() does not, because it doesn't have the MySQL TABLE object. rb://49 approved by Heikki Tuuri ------------------------------------------------------------------------ --- .../suite/innodb/r/innodb_bug52199.result | 5 ++ .../suite/innodb/t/innodb_bug52199.test | 7 ++ storage/innobase/data/data0type.c | 11 +-- storage/innobase/dict/dict0mem.c | 31 ++++++++ storage/innobase/handler/ha_innodb.cc | 30 ++++---- storage/innobase/handler/handler0alter.cc | 6 +- storage/innobase/include/data0type.h | 47 +++++++++--- storage/innobase/include/data0type.ic | 75 +++++++++++++------ storage/innobase/include/dict0dict.h | 27 +++++++ storage/innobase/include/dict0dict.ic | 50 +++++++++++-- storage/innobase/include/dict0mem.h | 15 ++-- storage/innobase/include/dict0mem.ic | 32 -------- storage/innobase/include/row0mysql.h | 11 +++ storage/innobase/row/row0ins.c | 63 +++++++--------- storage/innobase/row/row0merge.c | 2 +- storage/innobase/row/row0mysql.c | 67 ++++++++++++++++- storage/innobase/row/row0row.c | 8 +- storage/innobase/row/row0sel.c | 58 +++++++------- storage/innobase/row/row0upd.c | 2 +- storage/innobase/trx/trx0trx.c | 2 +- 20 files changed, 364 insertions(+), 185 deletions(-) create mode 100644 mysql-test/suite/innodb/r/innodb_bug52199.result create mode 100644 mysql-test/suite/innodb/t/innodb_bug52199.test diff --git a/mysql-test/suite/innodb/r/innodb_bug52199.result b/mysql-test/suite/innodb/r/innodb_bug52199.result new file mode 100644 index 00000000000..7e8c1ee46e0 --- /dev/null +++ b/mysql-test/suite/innodb/r/innodb_bug52199.result @@ -0,0 +1,5 @@ +CREATE TABLE bug52199 (a INT NOT NULL, +b CHAR(125) CHARACTER SET utf32 COLLATE utf32_bin NOT NULL +)ENGINE=InnoDB; +CREATE UNIQUE INDEX idx ON bug52199(a); +DROP TABLE bug52199; diff --git a/mysql-test/suite/innodb/t/innodb_bug52199.test b/mysql-test/suite/innodb/t/innodb_bug52199.test new file mode 100644 index 00000000000..0fec64ba243 --- /dev/null +++ b/mysql-test/suite/innodb/t/innodb_bug52199.test @@ -0,0 +1,7 @@ +-- source include/have_innodb.inc + +CREATE TABLE bug52199 (a INT NOT NULL, +b CHAR(125) CHARACTER SET utf32 COLLATE utf32_bin NOT NULL +)ENGINE=InnoDB; +CREATE UNIQUE INDEX idx ON bug52199(a); +DROP TABLE bug52199; diff --git a/storage/innobase/data/data0type.c b/storage/innobase/data/data0type.c index e834fd2ec55..20d1f5db8d3 100644 --- a/storage/innobase/data/data0type.c +++ b/storage/innobase/data/data0type.c @@ -49,10 +49,8 @@ ulint dtype_get_at_most_n_mbchars( /*========================*/ ulint prtype, /*!< in: precise type */ - ulint mbminlen, /*!< in: minimum length of a - multi-byte character */ - ulint mbmaxlen, /*!< in: maximum length of a - multi-byte character */ + ulint mbminmaxlen, /*!< in: minimum and maximum length of + a multi-byte character */ ulint prefix_len, /*!< in: length of the requested prefix, in characters, multiplied by dtype_get_mbmaxlen(dtype) */ @@ -60,6 +58,9 @@ dtype_get_at_most_n_mbchars( const char* str) /*!< in: the string whose prefix length is being determined */ { + ulint mbminlen = DATA_MBMINLEN(mbminmaxlen); + ulint mbmaxlen = DATA_MBMAXLEN(mbminmaxlen); + ut_a(data_len != UNIV_SQL_NULL); ut_ad(!mbmaxlen || !(prefix_len % mbmaxlen)); @@ -180,7 +181,7 @@ dtype_validate( } #ifndef UNIV_HOTBACKUP - ut_a(type->mbminlen <= type->mbmaxlen); + ut_a(dtype_get_mbminlen(type) <= dtype_get_mbmaxlen(type)); #endif /* !UNIV_HOTBACKUP */ return(TRUE); diff --git a/storage/innobase/dict/dict0mem.c b/storage/innobase/dict/dict0mem.c index c1c1c063c3e..bbb8f810f44 100644 --- a/storage/innobase/dict/dict0mem.c +++ b/storage/innobase/dict/dict0mem.c @@ -206,6 +206,37 @@ dict_mem_table_add_col( dict_mem_fill_column_struct(col, i, mtype, prtype, len); } + +/**********************************************************************//** +This function populates a dict_col_t memory structure with +supplied information. */ +UNIV_INTERN +void +dict_mem_fill_column_struct( +/*========================*/ + dict_col_t* column, /*!< out: column struct to be + filled */ + ulint col_pos, /*!< in: column position */ + ulint mtype, /*!< in: main data type */ + ulint prtype, /*!< in: precise type */ + ulint col_len) /*!< in: column length */ +{ +#ifndef UNIV_HOTBACKUP + ulint mbminlen; + ulint mbmaxlen; +#endif /* !UNIV_HOTBACKUP */ + + column->ind = (unsigned int) col_pos; + column->ord_part = 0; + column->mtype = (unsigned int) mtype; + column->prtype = (unsigned int) prtype; + column->len = (unsigned int) col_len; +#ifndef UNIV_HOTBACKUP + dtype_get_mblen(mtype, prtype, &mbminlen, &mbmaxlen); + dict_col_set_mbminmaxlen(column, mbminlen, mbmaxlen); +#endif /* !UNIV_HOTBACKUP */ +} + /**********************************************************************//** Creates an index memory object. @return own: index object */ diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index f3d9bcd8d59..7cead57be62 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -1036,6 +1036,8 @@ innobase_get_cset_width( if (cs) { *mbminlen = cs->mbminlen; *mbmaxlen = cs->mbmaxlen; + ut_ad(*mbminlen < DATA_MBMAX); + ut_ad(*mbmaxlen < DATA_MBMAX); } else { THD* thd = current_thd; @@ -4433,15 +4435,14 @@ ha_innobase::store_key_val_for_row( memcpy(buff, src_start, true_len); buff += true_len; - /* Pad the unused space with spaces. Note that no - padding is ever needed for UCS-2 because in MySQL, - all UCS2 characters are 2 bytes, as MySQL does not - support surrogate pairs, which are needed to represent - characters in the range U+10000 to U+10FFFF. */ + /* Pad the unused space with spaces. */ if (true_len < key_len) { - ulint pad_len = key_len - true_len; - memset(buff, ' ', pad_len); + ulint pad_len = key_len - true_len; + ut_a(!(pad_len % cs->mbminlen)); + + cs->cset->fill(cs, buff, pad_len, + 0x20 /* space */); buff += pad_len; } } @@ -4550,6 +4551,7 @@ build_template( /* Note that in InnoDB, i is the column number. MySQL calls columns 'fields'. */ for (i = 0; i < n_fields; i++) { + const dict_col_t* col = &index->table->cols[i]; templ = prebuilt->mysql_template + n_requested_fields; field = table->field[i]; @@ -4598,7 +4600,7 @@ include_field: if (index == clust_index) { templ->rec_field_no = dict_col_get_clust_pos( - &index->table->cols[i], index); + col, index); } else { templ->rec_field_no = dict_index_get_nth_col_pos( index, i); @@ -4627,7 +4629,7 @@ include_field: mysql_prefix_len = templ->mysql_col_offset + templ->mysql_col_len; } - templ->type = index->table->cols[i].mtype; + templ->type = col->mtype; templ->mysql_type = (ulint)field->type(); if (templ->mysql_type == DATA_MYSQL_TRUE_VARCHAR) { @@ -4635,12 +4637,10 @@ include_field: (((Field_varstring*)field)->length_bytes); } - templ->charset = dtype_get_charset_coll( - index->table->cols[i].prtype); - templ->mbminlen = index->table->cols[i].mbminlen; - templ->mbmaxlen = index->table->cols[i].mbmaxlen; - templ->is_unsigned = index->table->cols[i].prtype - & DATA_UNSIGNED; + templ->charset = dtype_get_charset_coll(col->prtype); + templ->mbminlen = dict_col_get_mbminlen(col); + templ->mbmaxlen = dict_col_get_mbmaxlen(col); + templ->is_unsigned = col->prtype & DATA_UNSIGNED; if (templ->type == DATA_BLOB) { prebuilt->templ_contains_blob = TRUE; } diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc index ec17882590c..3744d16570c 100644 --- a/storage/innobase/handler/handler0alter.cc +++ b/storage/innobase/handler/handler0alter.cc @@ -99,8 +99,10 @@ innobase_col_to_mysql( #ifdef UNIV_DEBUG case DATA_MYSQL: ut_ad(flen >= len); - ut_ad(col->mbmaxlen >= col->mbminlen); - ut_ad(col->mbmaxlen > col->mbminlen || flen == len); + ut_ad(DATA_MBMAXLEN(col->mbminmaxlen) + >= DATA_MBMINLEN(col->mbminmaxlen)); + ut_ad(DATA_MBMAXLEN(col->mbminmaxlen) + > DATA_MBMINLEN(col->mbminmaxlen) || flen == len); memcpy(dest, data, len); break; diff --git a/storage/innobase/include/data0type.h b/storage/innobase/include/data0type.h index 1a9c7bbd7ba..850cf2e2975 100644 --- a/storage/innobase/include/data0type.h +++ b/storage/innobase/include/data0type.h @@ -168,6 +168,17 @@ SQL null*/ store the charset-collation number; one byte is left unused, though */ #define DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE 6 +/* Maximum multi-byte character length in bytes, plus 1 */ +#define DATA_MBMAX 5 + +/* Pack mbminlen, mbmaxlen to mbminmaxlen. */ +#define DATA_MBMINMAXLEN(mbminlen, mbmaxlen) \ + ((mbmaxlen) * DATA_MBMAX + (mbminlen)) +/* Get mbminlen from mbminmaxlen. */ +#define DATA_MBMINLEN(mbminmaxlen) UNIV_EXPECT(((mbminmaxlen) % DATA_MBMAX), 1) +/* Get mbmaxlen from mbminmaxlen. */ +#define DATA_MBMAXLEN(mbminmaxlen) ((mbminmaxlen) / DATA_MBMAX) + #ifndef UNIV_HOTBACKUP /*********************************************************************//** Gets the MySQL type code from a dtype. @@ -187,10 +198,8 @@ ulint dtype_get_at_most_n_mbchars( /*========================*/ ulint prtype, /*!< in: precise type */ - ulint mbminlen, /*!< in: minimum length of a - multi-byte character */ - ulint mbmaxlen, /*!< in: maximum length of a - multi-byte character */ + ulint mbminmaxlen, /*!< in: minimum and maximum length of + a multi-byte character */ ulint prefix_len, /*!< in: length of the requested prefix, in characters, multiplied by dtype_get_mbmaxlen(dtype) */ @@ -335,6 +344,19 @@ dtype_get_mbmaxlen( /*===============*/ const dtype_t* type); /*!< in: type */ /*********************************************************************//** +Sets the minimum and maximum length of a character, in bytes. */ +UNIV_INLINE +void +dtype_set_mbminmaxlen( +/*==================*/ + dtype_t* type, /*!< in/out: type */ + ulint mbminlen, /*!< in: minimum length of a char, + in bytes, or 0 if this is not + a character type */ + ulint mbmaxlen); /*!< in: maximum length of a char, + in bytes, or 0 if this is not + a character type */ +/*********************************************************************//** Gets the padding character code for the type. @return padding character code, or ULINT_UNDEFINED if no padding specified */ UNIV_INLINE @@ -354,8 +376,8 @@ dtype_get_fixed_size_low( ulint mtype, /*!< in: main type */ ulint prtype, /*!< in: precise type */ ulint len, /*!< in: length */ - ulint mbminlen, /*!< in: minimum length of a multibyte char */ - ulint mbmaxlen, /*!< in: maximum length of a multibyte char */ + ulint mbminmaxlen, /*!< in: minimum and maximum length of a + multibyte character, in bytes */ ulint comp); /*!< in: nonzero=ROW_FORMAT=COMPACT */ #ifndef UNIV_HOTBACKUP /***********************************************************************//** @@ -368,8 +390,8 @@ dtype_get_min_size_low( ulint mtype, /*!< in: main type */ ulint prtype, /*!< in: precise type */ ulint len, /*!< in: length */ - ulint mbminlen, /*!< in: minimum length of a multibyte char */ - ulint mbmaxlen); /*!< in: maximum length of a multibyte char */ + ulint mbminmaxlen); /*!< in: minimum and maximum length of a + multibyte character */ /***********************************************************************//** Returns the maximum size of a data type. Note: types in system tables may be incomplete and return incorrect information. @@ -472,10 +494,11 @@ struct dtype_struct{ the string, MySQL uses 1 or 2 bytes to store the string length) */ #ifndef UNIV_HOTBACKUP - unsigned mbminlen:2; /*!< minimum length of a - character, in bytes */ - unsigned mbmaxlen:3; /*!< maximum length of a - character, in bytes */ + unsigned mbminmaxlen:5; /*!< minimum and maximum length of a + character, in bytes; + DATA_MBMINMAXLEN(mbminlen,mbmaxlen); + mbminlen=DATA_MBMINLEN(mbminmaxlen); + mbmaxlen=DATA_MBMINLEN(mbminmaxlen) */ #endif /* !UNIV_HOTBACKUP */ }; diff --git a/storage/innobase/include/data0type.ic b/storage/innobase/include/data0type.ic index 2bf67a941bd..757dd815c5e 100644 --- a/storage/innobase/include/data0type.ic +++ b/storage/innobase/include/data0type.ic @@ -93,13 +93,34 @@ dtype_get_mblen( innobase_get_cset_width(dtype_get_charset_coll(prtype), mbminlen, mbmaxlen); ut_ad(*mbminlen <= *mbmaxlen); - ut_ad(*mbminlen <= 2); /* mbminlen in dtype_t is 0..3 */ - ut_ad(*mbmaxlen < 1 << 3); /* mbmaxlen in dtype_t is 0..7 */ + ut_ad(*mbminlen < DATA_MBMAX); + ut_ad(*mbmaxlen < DATA_MBMAX); } else { *mbminlen = *mbmaxlen = 0; } } +/*********************************************************************//** +Sets the minimum and maximum length of a character, in bytes. */ +UNIV_INLINE +void +dtype_set_mbminmaxlen( +/*==================*/ + dtype_t* type, /*!< in/out: type */ + ulint mbminlen, /*!< in: minimum length of a char, + in bytes, or 0 if this is not + a character type */ + ulint mbmaxlen) /*!< in: maximum length of a char, + in bytes, or 0 if this is not + a character type */ +{ + ut_ad(mbminlen < DATA_MBMAX); + ut_ad(mbmaxlen < DATA_MBMAX); + ut_ad(mbminlen <= mbmaxlen); + + type->mbminmaxlen = DATA_MBMINMAXLEN(mbminlen, mbmaxlen); +} + /*********************************************************************//** Compute the mbminlen and mbmaxlen members of a data type structure. */ UNIV_INLINE @@ -112,8 +133,7 @@ dtype_set_mblen( ulint mbmaxlen; dtype_get_mblen(type->mtype, type->prtype, &mbminlen, &mbmaxlen); - type->mbminlen = mbminlen; - type->mbmaxlen = mbmaxlen; + dtype_set_mbminmaxlen(type, mbminlen, mbmaxlen); ut_ad(dtype_validate(type)); } @@ -210,7 +230,7 @@ dtype_get_mbminlen( const dtype_t* type) /*!< in: type */ { ut_ad(type); - return(type->mbminlen); + return(DATA_MBMINLEN(type->mbminmaxlen)); } /*********************************************************************//** Gets the maximum length of a character, in bytes. @@ -223,7 +243,7 @@ dtype_get_mbmaxlen( const dtype_t* type) /*!< in: type */ { ut_ad(type); - return(type->mbmaxlen); + return(DATA_MBMAXLEN(type->mbminmaxlen)); } /*********************************************************************//** @@ -404,8 +424,8 @@ dtype_get_fixed_size_low( ulint mtype, /*!< in: main type */ ulint prtype, /*!< in: precise type */ ulint len, /*!< in: length */ - ulint mbminlen, /*!< in: minimum length of a multibyte char */ - ulint mbmaxlen, /*!< in: maximum length of a multibyte char */ + ulint mbminmaxlen, /*!< in: minimum and maximum length of + a multibyte character, in bytes */ ulint comp) /*!< in: nonzero=ROW_FORMAT=COMPACT */ { switch (mtype) { @@ -453,8 +473,9 @@ dtype_get_fixed_size_low( dtype_get_charset_coll(prtype), &i_mbminlen, &i_mbmaxlen); - if (UNIV_UNLIKELY(mbminlen != i_mbminlen) - || UNIV_UNLIKELY(mbmaxlen != i_mbmaxlen)) { + if (UNIV_UNLIKELY + (DATA_MBMINMAXLEN(i_mbminlen, i_mbmaxlen) + != mbminmaxlen)) { ut_print_timestamp(stderr); fprintf(stderr, " InnoDB: " @@ -464,10 +485,10 @@ dtype_get_fixed_size_low( "type->mbmaxlen=%lu\n", (ulong) i_mbminlen, (ulong) i_mbmaxlen, - (ulong) mbminlen, - (ulong) mbmaxlen); + (ulong) DATA_MBMINLEN(mbminmaxlen), + (ulong) DATA_MBMAXLEN(mbminmaxlen)); } - if (mbminlen == mbmaxlen) { + if (i_mbminlen == i_mbmaxlen) { return(len); } } @@ -499,8 +520,8 @@ dtype_get_min_size_low( ulint mtype, /*!< in: main type */ ulint prtype, /*!< in: precise type */ ulint len, /*!< in: length */ - ulint mbminlen, /*!< in: minimum length of a multibyte char */ - ulint mbmaxlen) /*!< in: maximum length of a multibyte char */ + ulint mbminmaxlen) /*!< in: minimum and maximum length of a + multi-byte character */ { switch (mtype) { case DATA_SYS: @@ -527,14 +548,22 @@ dtype_get_min_size_low( case DATA_DOUBLE: return(len); case DATA_MYSQL: - if ((prtype & DATA_BINARY_TYPE) || mbminlen == mbmaxlen) { + if (prtype & DATA_BINARY_TYPE) { return(len); + } else { + ulint mbminlen = DATA_MBMINLEN(mbminmaxlen); + ulint mbmaxlen = DATA_MBMAXLEN(mbminmaxlen); + + if (mbminlen == mbmaxlen) { + return(len); + } + + /* this is a variable-length character set */ + ut_a(mbminlen > 0); + ut_a(mbmaxlen > mbminlen); + ut_a(len % mbmaxlen == 0); + return(len * mbminlen / mbmaxlen); } - /* this is a variable-length character set */ - ut_a(mbminlen > 0); - ut_a(mbmaxlen > mbminlen); - ut_a(len % mbmaxlen == 0); - return(len * mbminlen / mbmaxlen); case DATA_VARCHAR: case DATA_BINARY: case DATA_DECIMAL: @@ -595,9 +624,9 @@ dtype_get_sql_null_size( { #ifndef UNIV_HOTBACKUP return(dtype_get_fixed_size_low(type->mtype, type->prtype, type->len, - type->mbminlen, type->mbmaxlen, comp)); + type->mbminmaxlen, comp)); #else /* !UNIV_HOTBACKUP */ return(dtype_get_fixed_size_low(type->mtype, type->prtype, type->len, - 0, 0, 0)); + 0, 0)); #endif /* !UNIV_HOTBACKUP */ } diff --git a/storage/innobase/include/dict0dict.h b/storage/innobase/include/dict0dict.h index 28fa12e4678..971173a65a5 100644 --- a/storage/innobase/include/dict0dict.h +++ b/storage/innobase/include/dict0dict.h @@ -102,6 +102,33 @@ void dict_load_space_id_list(void); /*=========================*/ /*********************************************************************//** +Gets the minimum number of bytes per character. +@return minimum multi-byte char size, in bytes */ +UNIV_INLINE +ulint +dict_col_get_mbminlen( +/*==================*/ + const dict_col_t* col); /*!< in: column */ +/*********************************************************************//** +Gets the maximum number of bytes per character. +@return maximum multi-byte char size, in bytes */ +UNIV_INLINE +ulint +dict_col_get_mbmaxlen( +/*==================*/ + const dict_col_t* col); /*!< in: column */ +/*********************************************************************//** +Sets the minimum and maximum number of bytes per character. */ +UNIV_INLINE +void +dict_col_set_mbminmaxlen( +/*=====================*/ + dict_col_t* col, /*!< in/out: column */ + ulint mbminlen, /*!< in: minimum multi-byte + character size, in bytes */ + ulint mbmaxlen); /*!< in: minimum multi-byte + character size, in bytes */ +/*********************************************************************//** Gets the column data type. */ UNIV_INLINE void diff --git a/storage/innobase/include/dict0dict.ic b/storage/innobase/include/dict0dict.ic index b118df87f8b..09f967aa74a 100644 --- a/storage/innobase/include/dict0dict.ic +++ b/storage/innobase/include/dict0dict.ic @@ -28,6 +28,46 @@ Created 1/8/1996 Heikki Tuuri #include "dict0load.h" #include "rem0types.h" +/*********************************************************************//** +Gets the minimum number of bytes per character. +@return minimum multi-byte char size, in bytes */ +UNIV_INLINE +ulint +dict_col_get_mbminlen( +/*==================*/ + const dict_col_t* col) /*!< in: column */ +{ + return(DATA_MBMINLEN(col->mbminmaxlen)); +} +/*********************************************************************//** +Gets the maximum number of bytes per character. +@return maximum multi-byte char size, in bytes */ +UNIV_INLINE +ulint +dict_col_get_mbmaxlen( +/*==================*/ + const dict_col_t* col) /*!< in: column */ +{ + return(DATA_MBMAXLEN(col->mbminmaxlen)); +} +/*********************************************************************//** +Sets the minimum and maximum number of bytes per character. */ +UNIV_INLINE +void +dict_col_set_mbminmaxlen( +/*=====================*/ + dict_col_t* col, /*!< in/out: column */ + ulint mbminlen, /*!< in: minimum multi-byte + character size, in bytes */ + ulint mbmaxlen) /*!< in: minimum multi-byte + character size, in bytes */ +{ + ut_ad(mbminlen < DATA_MBMAX); + ut_ad(mbmaxlen < DATA_MBMAX); + ut_ad(mbminlen <= mbmaxlen); + + col->mbminmaxlen = DATA_MBMINMAXLEN(mbminlen, mbmaxlen); +} /*********************************************************************//** Gets the column data type. */ UNIV_INLINE @@ -42,8 +82,7 @@ dict_col_copy_type( type->mtype = col->mtype; type->prtype = col->prtype; type->len = col->len; - type->mbminlen = col->mbminlen; - type->mbmaxlen = col->mbmaxlen; + type->mbminmaxlen = col->mbminmaxlen; } #endif /* !UNIV_HOTBACKUP */ @@ -65,8 +104,7 @@ dict_col_type_assert_equal( ut_ad(col->prtype == type->prtype); ut_ad(col->len == type->len); # ifndef UNIV_HOTBACKUP - ut_ad(col->mbminlen == type->mbminlen); - ut_ad(col->mbmaxlen == type->mbmaxlen); + ut_ad(col->mbminmaxlen == type->mbminmaxlen); # endif /* !UNIV_HOTBACKUP */ return(TRUE); @@ -84,7 +122,7 @@ dict_col_get_min_size( const dict_col_t* col) /*!< in: column */ { return(dtype_get_min_size_low(col->mtype, col->prtype, col->len, - col->mbminlen, col->mbmaxlen)); + col->mbminmaxlen)); } /***********************************************************************//** Returns the maximum size of the column. @@ -109,7 +147,7 @@ dict_col_get_fixed_size( ulint comp) /*!< in: nonzero=ROW_FORMAT=COMPACT */ { return(dtype_get_fixed_size_low(col->mtype, col->prtype, col->len, - col->mbminlen, col->mbmaxlen, comp)); + col->mbminmaxlen, comp)); } /***********************************************************************//** Returns the ROW_FORMAT=REDUNDANT stored SQL NULL size of a column. diff --git a/storage/innobase/include/dict0mem.h b/storage/innobase/include/dict0mem.h index 286eb408a3b..75f9acd6b26 100644 --- a/storage/innobase/include/dict0mem.h +++ b/storage/innobase/include/dict0mem.h @@ -151,9 +151,9 @@ dict_mem_table_add_col( ulint prtype, /*!< in: precise type */ ulint len); /*!< in: precision */ /**********************************************************************//** -This function poplulates a dict_col_t memory structure with +This function populates a dict_col_t memory structure with supplied information. */ -UNIV_INLINE +UNIV_INTERN void dict_mem_fill_column_struct( /*========================*/ @@ -162,7 +162,7 @@ dict_mem_fill_column_struct( ulint col_pos, /*!< in: column position */ ulint mtype, /*!< in: main data type */ ulint prtype, /*!< in: precise type */ - ulint col_len); /*!< in: column lenght */ + ulint col_len); /*!< in: column length */ /**********************************************************************//** This function poplulates a dict_index_t index memory structure with supplied information. */ @@ -249,10 +249,11 @@ struct dict_col_struct{ the string, MySQL uses 1 or 2 bytes to store the string length) */ - unsigned mbminlen:2; /*!< minimum length of a - character, in bytes */ - unsigned mbmaxlen:3; /*!< maximum length of a - character, in bytes */ + unsigned mbminmaxlen:5; /*!< minimum and maximum length of a + character, in bytes; + DATA_MBMINMAXLEN(mbminlen,mbmaxlen); + mbminlen=DATA_MBMINLEN(mbminmaxlen); + mbmaxlen=DATA_MBMINLEN(mbminmaxlen) */ /*----------------------*/ /* End of definitions copied from dtype_t */ /* @} */ diff --git a/storage/innobase/include/dict0mem.ic b/storage/innobase/include/dict0mem.ic index 5a851da5640..1d80ffc9b94 100644 --- a/storage/innobase/include/dict0mem.ic +++ b/storage/innobase/include/dict0mem.ic @@ -70,35 +70,3 @@ dict_mem_fill_index_struct( index->magic_n = DICT_INDEX_MAGIC_N; #endif /* UNIV_DEBUG */ } - -/**********************************************************************//** -This function poplulates a dict_col_t memory structure with -supplied information. */ -UNIV_INLINE -void -dict_mem_fill_column_struct( -/*========================*/ - dict_col_t* column, /*!< out: column struct to be - filled */ - ulint col_pos, /*!< in: column position */ - ulint mtype, /*!< in: main data type */ - ulint prtype, /*!< in: precise type */ - ulint col_len) /*!< in: column lenght */ -{ -#ifndef UNIV_HOTBACKUP - ulint mbminlen; - ulint mbmaxlen; -#endif /* !UNIV_HOTBACKUP */ - - column->ind = (unsigned int) col_pos; - column->ord_part = 0; - column->mtype = (unsigned int) mtype; - column->prtype = (unsigned int) prtype; - column->len = (unsigned int) col_len; -#ifndef UNIV_HOTBACKUP - dtype_get_mblen(mtype, prtype, &mbminlen, &mbmaxlen); - - column->mbminlen = (unsigned int) mbminlen; - column->mbmaxlen = (unsigned int) mbmaxlen; -#endif /* !UNIV_HOTBACKUP */ -} diff --git a/storage/innobase/include/row0mysql.h b/storage/innobase/include/row0mysql.h index 39ea240772c..f97d90adb2b 100644 --- a/storage/innobase/include/row0mysql.h +++ b/storage/innobase/include/row0mysql.h @@ -103,6 +103,17 @@ row_mysql_read_blob_ref( ulint col_len); /*!< in: BLOB reference length (not BLOB length) */ /**************************************************************//** +Pad a column with spaces. */ +UNIV_INTERN +void +row_mysql_pad_col( +/*==============*/ + ulint mbminlen, /*!< in: minimum size of a character, + in bytes */ + byte* pad, /*!< out: padded buffer */ + ulint len); /*!< in: number of bytes to pad */ + +/**************************************************************//** Stores a non-SQL-NULL field given in the MySQL format in the InnoDB format. The counterpart of this function is row_sel_field_store_in_mysql_format() in row0sel.c. diff --git a/storage/innobase/row/row0ins.c b/storage/innobase/row/row0ins.c index 241f652d0bf..8fd8c4f8532 100644 --- a/storage/innobase/row/row0ins.c +++ b/storage/innobase/row/row0ins.c @@ -515,8 +515,7 @@ row_ins_cascade_calc_update_vec( if (!dfield_is_null(&ufield->new_val) && dtype_get_at_most_n_mbchars( - col->prtype, - col->mbminlen, col->mbmaxlen, + col->prtype, col->mbminmaxlen, col->len, ufield_len, dfield_get_data(&ufield->new_val)) @@ -539,49 +538,37 @@ row_ins_cascade_calc_update_vec( if (min_size > ufield_len) { - char* pad_start; - const char* pad_end; - char* padded_data - = mem_heap_alloc( - heap, min_size); - pad_start = padded_data + ufield_len; - pad_end = padded_data + min_size; + byte* pad; + ulint pad_len; + byte* padded_data; + ulint mbminlen; + + padded_data = mem_heap_alloc( + heap, min_size); + + pad = padded_data + ufield_len; + pad_len = min_size - ufield_len; memcpy(padded_data, dfield_get_data(&ufield ->new_val), - dfield_get_len(&ufield - ->new_val)); + ufield_len); - switch (UNIV_EXPECT(col->mbminlen,1)) { - default: - ut_error; + mbminlen = dict_col_get_mbminlen(col); + + ut_ad(!(ufield_len % mbminlen)); + ut_ad(!(min_size % mbminlen)); + + if (mbminlen == 1 + && dtype_get_charset_coll( + col->prtype) + == DATA_MYSQL_BINARY_CHARSET_COLL) { + /* Do not pad BINARY columns */ return(ULINT_UNDEFINED); - case 1: - if (UNIV_UNLIKELY - (dtype_get_charset_coll( - col->prtype) - == DATA_MYSQL_BINARY_CHARSET_COLL)) { - /* Do not pad BINARY - columns. */ - return(ULINT_UNDEFINED); - } - - /* space=0x20 */ - memset(pad_start, 0x20, - pad_end - pad_start); - break; - case 2: - /* space=0x0020 */ - ut_a(!(ufield_len % 2)); - ut_a(!(min_size % 2)); - do { - *pad_start++ = 0x00; - *pad_start++ = 0x20; - } while (pad_start < pad_end); - break; } + row_mysql_pad_col(mbminlen, + pad, pad_len); dfield_set_data(&ufield->new_val, padded_data, min_size); } @@ -2232,7 +2219,7 @@ row_ins_index_entry_set_vals( = dict_field_get_col(ind_field); len = dtype_get_at_most_n_mbchars( - col->prtype, col->mbminlen, col->mbmaxlen, + col->prtype, col->mbminmaxlen, ind_field->prefix_len, len, dfield_get_data(row_field)); diff --git a/storage/innobase/row/row0merge.c b/storage/innobase/row/row0merge.c index 8aa64eb82c0..3fdad6d423b 100644 --- a/storage/innobase/row/row0merge.c +++ b/storage/innobase/row/row0merge.c @@ -338,7 +338,7 @@ row_merge_buf_add( if (ifield->prefix_len) { len = dtype_get_at_most_n_mbchars( col->prtype, - col->mbminlen, col->mbmaxlen, + col->mbminmaxlen, ifield->prefix_len, len, dfield_get_data(field)); dfield_set_len(field, len); diff --git a/storage/innobase/row/row0mysql.c b/storage/innobase/row/row0mysql.c index 1b11987a7f9..1262ac71e98 100644 --- a/storage/innobase/row/row0mysql.c +++ b/storage/innobase/row/row0mysql.c @@ -265,6 +265,49 @@ row_mysql_read_blob_ref( return(data); } +/**************************************************************//** +Pad a column with spaces. */ +UNIV_INTERN +void +row_mysql_pad_col( +/*==============*/ + ulint mbminlen, /*!< in: minimum size of a character, + in bytes */ + byte* pad, /*!< out: padded buffer */ + ulint len) /*!< in: number of bytes to pad */ +{ + const byte* pad_end; + + switch (UNIV_EXPECT(mbminlen, 1)) { + default: + ut_error; + case 1: + /* space=0x20 */ + memset(pad, 0x20, len); + break; + case 2: + /* space=0x0020 */ + pad_end = pad + len; + ut_a(!(len % 2)); + do { + *pad++ = 0x00; + *pad++ = 0x20; + } while (pad < pad_end); + break; + case 4: + /* space=0x00000020 */ + pad_end = pad + len; + ut_a(!(len % 4)); + do { + *pad++ = 0x00; + *pad++ = 0x00; + *pad++ = 0x00; + *pad++ = 0x20; + } while (pad < pad_end); + break; + } +} + /**************************************************************//** Stores a non-SQL-NULL field given in the MySQL format in the InnoDB format. The counterpart of this function is row_sel_field_store_in_mysql_format() in @@ -357,12 +400,28 @@ row_mysql_store_col_in_innobase_format( /* Remove trailing spaces from old style VARCHAR columns. */ - /* Handle UCS2 strings differently. */ + /* Handle Unicode strings differently. */ ulint mbminlen = dtype_get_mbminlen(dtype); ptr = mysql_data; - if (mbminlen == 2) { + switch (mbminlen) { + default: + ut_error; + case 4: + /* space=0x00000020 */ + /* Trim "half-chars", just in case. */ + col_len &= ~3; + + while (col_len >= 4 + && ptr[col_len - 4] == 0x00 + && ptr[col_len - 3] == 0x00 + && ptr[col_len - 2] == 0x00 + && ptr[col_len - 1] == 0x20) { + col_len -= 4; + } + break; + case 2: /* space=0x0020 */ /* Trim "half-chars", just in case. */ col_len &= ~1; @@ -371,8 +430,8 @@ row_mysql_store_col_in_innobase_format( && ptr[col_len - 1] == 0x20) { col_len -= 2; } - } else { - ut_a(mbminlen == 1); + break; + case 1: /* space=0x20 */ while (col_len > 0 && ptr[col_len - 1] == 0x20) { diff --git a/storage/innobase/row/row0row.c b/storage/innobase/row/row0row.c index 6cdfa410c15..951148977f9 100644 --- a/storage/innobase/row/row0row.c +++ b/storage/innobase/row/row0row.c @@ -156,7 +156,7 @@ row_build_index_entry( } len = dtype_get_at_most_n_mbchars( - col->prtype, col->mbminlen, col->mbmaxlen, + col->prtype, col->mbminmaxlen, ind_field->prefix_len, len, dfield_get_data(dfield)); dfield_set_len(dfield, len); } @@ -514,8 +514,7 @@ row_build_row_ref( dfield_set_len(dfield, dtype_get_at_most_n_mbchars( dtype->prtype, - dtype->mbminlen, - dtype->mbmaxlen, + dtype->mbminmaxlen, clust_col_prefix_len, len, (char*) field)); } @@ -629,8 +628,7 @@ notfound: dfield_set_len(dfield, dtype_get_at_most_n_mbchars( dtype->prtype, - dtype->mbminlen, - dtype->mbmaxlen, + dtype->mbminmaxlen, clust_col_prefix_len, len, (char*) field)); } diff --git a/storage/innobase/row/row0sel.c b/storage/innobase/row/row0sel.c index 01742265ae2..4bd61016b21 100644 --- a/storage/innobase/row/row0sel.c +++ b/storage/innobase/row/row0sel.c @@ -88,10 +88,8 @@ row_sel_sec_rec_is_for_blob( /*========================*/ ulint mtype, /*!< in: main type */ ulint prtype, /*!< in: precise type */ - ulint mbminlen, /*!< in: minimum length of a - multi-byte character */ - ulint mbmaxlen, /*!< in: maximum length of a - multi-byte character */ + ulint mbminmaxlen, /*!< in: minimum and maximum length of + a multi-byte character */ const byte* clust_field, /*!< in: the locally stored part of the clustered index column, including the BLOB pointer; the clustered @@ -119,7 +117,7 @@ row_sel_sec_rec_is_for_blob( return(FALSE); } - len = dtype_get_at_most_n_mbchars(prtype, mbminlen, mbmaxlen, + len = dtype_get_at_most_n_mbchars(prtype, mbminmaxlen, sec_len, len, (const char*) buf); return(!cmp_data_data(mtype, prtype, buf, len, sec_field, sec_len)); @@ -202,14 +200,14 @@ row_sel_sec_rec_is_for_clust_rec( } len = dtype_get_at_most_n_mbchars( - col->prtype, col->mbminlen, col->mbmaxlen, + col->prtype, col->mbminmaxlen, ifield->prefix_len, len, (char*) clust_field); if (rec_offs_nth_extern(clust_offs, clust_pos) && len < sec_len) { if (!row_sel_sec_rec_is_for_blob( col->mtype, col->prtype, - col->mbminlen, col->mbmaxlen, + col->mbminmaxlen, clust_field, clust_len, sec_field, sec_len, dict_table_zip_size( @@ -2508,13 +2506,13 @@ row_sel_field_store_in_mysql_format( ulint len) /*!< in: length of the data */ { byte* ptr; - byte* field_end; - byte* pad_ptr; ut_ad(len != UNIV_SQL_NULL); UNIV_MEM_ASSERT_RW(data, len); switch (templ->type) { + const byte* field_end; + byte* pad; case DATA_INT: /* Convert integer data from Innobase to a little-endian format, sign bit restored to normal */ @@ -2558,38 +2556,32 @@ row_sel_field_store_in_mysql_format( unused end of a >= 5.0.3 true VARCHAR column, just in case MySQL expects its contents to be deterministic. */ - pad_ptr = dest + len; + pad = dest + len; ut_ad(templ->mbminlen <= templ->mbmaxlen); - /* We handle UCS2 charset strings differently. */ - if (templ->mbminlen == 2) { - /* A space char is two bytes, 0x0020 in UCS2 */ + /* We treat some Unicode charset strings specially. */ + switch (templ->mbminlen) { + case 4: + /* InnoDB should never have stripped partial + UTF-32 characters. */ + ut_a(!(len & 3)); + break; + case 2: + /* A space char is two bytes, + 0x0020 in UCS2 and UTF-16 */ - if (len & 1) { + if (UNIV_UNLIKELY(len & 1)) { /* A 0x20 has been stripped from the column. Pad it back. */ - if (pad_ptr < field_end) { - *pad_ptr = 0x20; - pad_ptr++; + if (pad < field_end) { + *pad++ = 0x20; } } - - /* Pad the rest of the string with 0x0020 */ - - while (pad_ptr < field_end) { - *pad_ptr = 0x00; - pad_ptr++; - *pad_ptr = 0x20; - pad_ptr++; - } - } else { - ut_ad(templ->mbminlen == 1); - /* space=0x20 */ - - memset(pad_ptr, 0x20, field_end - pad_ptr); } + + row_mysql_pad_col(templ->mbminlen, pad, field_end - pad); break; case DATA_BLOB: @@ -2614,9 +2606,9 @@ row_sel_field_store_in_mysql_format( || !(templ->mysql_col_len % templ->mbmaxlen)); ut_ad(len * templ->mbmaxlen >= templ->mysql_col_len); - if (templ->mbminlen != templ->mbmaxlen) { + if (templ->mbminlen == 1 && templ->mbmaxlen != 1) { /* Pad with spaces. This undoes the stripping - done in row0mysql.ic, function + done in row0mysql.c, function row_mysql_store_col_in_innobase_format(). */ memset(dest + len, 0x20, templ->mysql_col_len - len); diff --git a/storage/innobase/row/row0upd.c b/storage/innobase/row/row0upd.c index fd689c58b7c..8b151ec34cc 100644 --- a/storage/innobase/row/row0upd.c +++ b/storage/innobase/row/row0upd.c @@ -949,7 +949,7 @@ row_upd_index_replace_new_col_val( } len = dtype_get_at_most_n_mbchars(col->prtype, - col->mbminlen, col->mbmaxlen, + col->mbminmaxlen, field->prefix_len, len, (const char*) data); diff --git a/storage/innobase/trx/trx0trx.c b/storage/innobase/trx/trx0trx.c index 0ca6c2d7f35..19e3eb26421 100644 --- a/storage/innobase/trx/trx0trx.c +++ b/storage/innobase/trx/trx0trx.c @@ -2024,7 +2024,7 @@ trx_get_trx_by_xid( while (trx) { /* Compare two X/Open XA transaction id's: their length should be the same and binary comparison - of gtrid_lenght+bqual_length bytes should be + of gtrid_length+bqual_length bytes should be the same */ if (xid->gtrid_length == trx->xid.gtrid_length From 7271bcb4ec9efbfffd9c963f22c1779ba797cf6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 29 Jun 2010 15:55:18 +0300 Subject: [PATCH 040/115] Bug#54358: READ UNCOMMITTED access failure of off-page DYNAMIC or COMPRESSED columns When the server crashes after a record stub has been inserted and before all its off-page columns have been written, the record will contain incomplete off-page columns after crash recovery. Such records may only be accessed at the READ UNCOMMITTED isolation level or when rolling back a recovered transaction in recv_recovery_rollback_active(). Skip these records at the READ UNCOMMITTED isolation level. TODO: Add assertions for checking the above assumptions hold when an incomplete BLOB is encountered. btr_rec_copy_externally_stored_field(): Return NULL if the field is incomplete. row_prebuilt_t::templ_contains_blob: Clarify what "BLOB" means in this context. Hint: MySQL BLOBs are not the same as InnoDB BLOBs. row_sel_store_mysql_rec(): Return FALSE if not all columns could be retrieved. Previously this function always returned TRUE. Assert that the record is not delete-marked. row_sel_push_cache_row_for_mysql(): Return FALSE if not all columns could be retrieved. row_search_for_mysql(): Skip records containing incomplete off-page columns. Assert that the transaction isolation level is READ UNCOMMITTED. rb://380 approved by Jimmy Yang --- storage/innodb_plugin/btr/btr0cur.c | 24 ++++- storage/innodb_plugin/include/btr0cur.h | 2 +- storage/innodb_plugin/include/row0mysql.h | 6 +- storage/innodb_plugin/row/row0merge.c | 5 + storage/innodb_plugin/row/row0sel.c | 113 +++++++++++++++++++--- 5 files changed, 133 insertions(+), 17 deletions(-) diff --git a/storage/innodb_plugin/btr/btr0cur.c b/storage/innodb_plugin/btr/btr0cur.c index 50531ad3bd7..9b28f26f054 100644 --- a/storage/innodb_plugin/btr/btr0cur.c +++ b/storage/innodb_plugin/btr/btr0cur.c @@ -4814,7 +4814,7 @@ btr_copy_externally_stored_field( /*******************************************************************//** Copies an externally stored field of a record to mem heap. -@return the field copied to heap */ +@return the field copied to heap, or NULL if the field is incomplete */ UNIV_INTERN byte* btr_rec_copy_externally_stored_field( @@ -4844,6 +4844,28 @@ btr_rec_copy_externally_stored_field( data = rec_get_nth_field(rec, offsets, no, &local_len); + ut_a(local_len >= BTR_EXTERN_FIELD_REF_SIZE); + + if (UNIV_UNLIKELY + (!memcmp(data + local_len - BTR_EXTERN_FIELD_REF_SIZE, + field_ref_zero, BTR_EXTERN_FIELD_REF_SIZE))) { + /* The externally stored field was not written + yet. This is only a valid condition when the server + crashed after the time a record stub was freshly + inserted but before all its columns were written. This + record should only be seen by + recv_recovery_rollback_active() or any + TRX_ISO_READ_UNCOMMITTED transactions. */ + + /* TODO: assert that there is an owner_trx with + owner_trx->id == DB_TRX_ID and owner_trx->is_recovered */ + + /* TODO: assert that for the current transaction trx, + either (trx == owner_trx && trx_is_recv(trx)) or + trx->isolation_level == TRX_ISO_READ_UNCOMMITTED. */ + return(NULL); + } + return(btr_copy_externally_stored_field(len, data, zip_size, local_len, heap)); } diff --git a/storage/innodb_plugin/include/btr0cur.h b/storage/innodb_plugin/include/btr0cur.h index 716f15c4267..7dc2eb63cf5 100644 --- a/storage/innodb_plugin/include/btr0cur.h +++ b/storage/innodb_plugin/include/btr0cur.h @@ -570,7 +570,7 @@ btr_copy_externally_stored_field_prefix( ulint local_len);/*!< in: length of data, in bytes */ /*******************************************************************//** Copies an externally stored field of a record to mem heap. -@return the field copied to heap */ +@return the field copied to heap, or NULL if the field is incomplete */ UNIV_INTERN byte* btr_rec_copy_externally_stored_field( diff --git a/storage/innodb_plugin/include/row0mysql.h b/storage/innodb_plugin/include/row0mysql.h index 39ea240772c..b69e657361b 100644 --- a/storage/innodb_plugin/include/row0mysql.h +++ b/storage/innodb_plugin/include/row0mysql.h @@ -622,7 +622,11 @@ struct row_prebuilt_struct { the secondary index, then this is set to TRUE */ unsigned templ_contains_blob:1;/*!< TRUE if the template contains - BLOB column(s) */ + a column with DATA_BLOB == + get_innobase_type_from_mysql_type(); + not to be confused with InnoDB + externally stored columns + (VARCHAR can be off-page too) */ mysql_row_templ_t* mysql_template;/*!< template used to transform rows fast between MySQL and Innobase formats; memory for this template diff --git a/storage/innodb_plugin/row/row0merge.c b/storage/innodb_plugin/row/row0merge.c index 70cc7912fad..56a68b58225 100644 --- a/storage/innodb_plugin/row/row0merge.c +++ b/storage/innodb_plugin/row/row0merge.c @@ -1780,6 +1780,11 @@ row_merge_copy_blobs( (below). */ data = btr_rec_copy_externally_stored_field( mrec, offsets, zip_size, i, &len, heap); + /* Because we have locked the table, any records + written by incomplete transactions must have been + rolled back already. There must not be any incomplete + BLOB columns. */ + ut_a(data); dfield_set_data(field, data, len); } diff --git a/storage/innodb_plugin/row/row0sel.c b/storage/innodb_plugin/row/row0sel.c index 2861235a995..ab8c78e4f4a 100644 --- a/storage/innodb_plugin/row/row0sel.c +++ b/storage/innodb_plugin/row/row0sel.c @@ -416,7 +416,7 @@ row_sel_fetch_columns( field_no))) { /* Copy an externally stored field to the - temporary heap */ + temporary heap, if possible. */ heap = mem_heap_create(1); @@ -425,6 +425,21 @@ row_sel_fetch_columns( dict_table_zip_size(index->table), field_no, &len, heap); + /* data == NULL means that the + externally stored field was not + written yet. This is only a valid + condition when the server crashed + after the time a record stub was + freshly inserted but before all its + columns were written. This record + should only be seen by + recv_recovery_rollback_active() or any + TRX_ISO_READ_UNCOMMITTED + transactions. The InnoDB SQL parser + (the sole caller of this function) + does not implement READ UNCOMMITTED, + and it is not involved during rollback. */ + ut_a(data); ut_a(len != UNIV_SQL_NULL); needs_copy = TRUE; @@ -926,6 +941,7 @@ row_sel_get_clust_rec( when plan->clust_pcur was positioned. The latch will not be released until mtr_commit(mtr). */ + ut_ad(!rec_get_deleted_flag(clust_rec, rec_offs_comp(offsets))); row_sel_fetch_columns(index, clust_rec, offsets, UT_LIST_GET_FIRST(plan->columns)); *out_rec = clust_rec; @@ -1628,6 +1644,13 @@ skip_lock: } if (old_vers == NULL) { + /* The record does not exist + in our read view. Skip it, but + first attempt to determine + whether the index segment we + are searching through has been + exhausted. */ + offsets = rec_get_offsets( rec, index, offsets, ULINT_UNDEFINED, &heap); @@ -2647,9 +2670,8 @@ Convert a row in the Innobase format to a row in the MySQL format. Note that the template in prebuilt may advise us to copy only a few columns to mysql_rec, other columns are left blank. All columns may not be needed in the query. -@return TRUE if success, FALSE if could not allocate memory for a BLOB -(though we may also assert in that case) */ -static +@return TRUE on success, FALSE if not all columns could be retrieved */ +static __attribute__((warn_unused_result)) ibool row_sel_store_mysql_rec( /*====================*/ @@ -2672,6 +2694,7 @@ row_sel_store_mysql_rec( ut_ad(prebuilt->mysql_template); ut_ad(prebuilt->default_rec); ut_ad(rec_offs_validate(rec, NULL, offsets)); + ut_ad(!rec_get_deleted_flag(rec, rec_offs_comp(offsets))); if (UNIV_LIKELY_NULL(prebuilt->blob_heap)) { mem_heap_free(prebuilt->blob_heap); @@ -2719,6 +2742,26 @@ row_sel_store_mysql_rec( dict_table_zip_size(prebuilt->table), templ->rec_field_no, &len, heap); + if (UNIV_UNLIKELY(!data)) { + /* The externally stored field + was not written yet. This is + only a valid condition when + the server crashed after the + time a record stub was freshly + inserted but before all its + columns were written. This + record should only be seen by + recv_recovery_rollback_active() + or any TRX_ISO_READ_UNCOMMITTED + transactions. */ + + if (extern_field_heap) { + mem_heap_free(extern_field_heap); + } + + return(FALSE); + } + ut_a(len != UNIV_SQL_NULL); } else { /* Field is stored in the row. */ @@ -3136,9 +3179,10 @@ row_sel_pop_cached_row_for_mysql( } /********************************************************************//** -Pushes a row for MySQL to the fetch cache. */ -UNIV_INLINE -void +Pushes a row for MySQL to the fetch cache. +@return TRUE on success, FALSE if the record contains incomplete BLOBs */ +UNIV_INLINE __attribute__((warn_unused_result)) +ibool row_sel_push_cache_row_for_mysql( /*=============================*/ row_prebuilt_t* prebuilt, /*!< in: prebuilt struct */ @@ -3180,10 +3224,11 @@ row_sel_push_cache_row_for_mysql( prebuilt->fetch_cache[ prebuilt->n_fetch_cached], prebuilt, rec, offsets))) { - ut_error; + return(FALSE); } prebuilt->n_fetch_cached++; + return(TRUE); } /*********************************************************************//** @@ -3578,6 +3623,24 @@ row_search_for_mysql( if (!row_sel_store_mysql_rec(buf, prebuilt, rec, offsets)) { + /* Only fresh inserts at + server crash time may contain + incomplete externally stored + columns. Pretend that such + records do not exist. Such + records may only be accessed + at the READ UNCOMMITTED + isolation level or when + rolling back a recovered + transaction. Rollback happens + at a lower level, not here. */ + ut_a(trx->isolation_level + == TRX_ISO_READ_UNCOMMITTED); + /* TODO: assert that there is + an owner_trx with + owner_trx->id == DB_TRX_ID and + owner_trx->is_recovered */ + err = DB_TOO_BIG_RECORD; /* We let the main loop to do the @@ -4357,9 +4420,20 @@ requires_clust_rec: not cache rows because there the cursor is a scrollable cursor. */ - row_sel_push_cache_row_for_mysql(prebuilt, result_rec, - offsets); - if (prebuilt->n_fetch_cached == MYSQL_FETCH_CACHE_SIZE) { + if (!row_sel_push_cache_row_for_mysql(prebuilt, result_rec, + offsets)) { + /* Only fresh inserts at server crash time may contain + incomplete externally stored columns. Pretend that + such records do not exist. Such records may only be + accessed at the READ UNCOMMITTED isolation level or + when rolling back a recovered transaction. Rollback + happens at a lower level, not here. */ + ut_a(trx->isolation_level == TRX_ISO_READ_UNCOMMITTED); + /* TODO: assert that there is an owner_trx + with owner_trx->id == DB_TRX_ID and + owner_trx->is_recovered */ + } else if (prebuilt->n_fetch_cached + == MYSQL_FETCH_CACHE_SIZE) { goto got_row; } @@ -4375,9 +4449,20 @@ requires_clust_rec: } else { if (!row_sel_store_mysql_rec(buf, prebuilt, result_rec, offsets)) { - err = DB_TOO_BIG_RECORD; - - goto lock_wait_or_error; + /* Only fresh inserts at server crash + time may contain incomplete externally + stored columns. Pretend that such + records do not exist. Such records may + only be accessed at the READ UNCOMMITTED + isolation level or when rolling back a + recovered transaction. Rollback happens + at a lower level, not here. */ + ut_a(trx->isolation_level + == TRX_ISO_READ_UNCOMMITTED); + /* TODO: assert that there is an owner_trx + with owner_trx->id == DB_TRX_ID and + owner_trx->is_recovered */ + goto next_rec; } } From c43070dabefc472b88b6d8092747e1cdc592f76e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 29 Jun 2010 15:56:53 +0300 Subject: [PATCH 041/115] ChangeLog entry for Bug #54358 --- storage/innodb_plugin/ChangeLog | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/storage/innodb_plugin/ChangeLog b/storage/innodb_plugin/ChangeLog index 328fcf8e0bb..5e41c5f180a 100644 --- a/storage/innodb_plugin/ChangeLog +++ b/storage/innodb_plugin/ChangeLog @@ -1,3 +1,10 @@ +2010-06-29 The InnoDB Team + + * btr/btr0cur.c, include/btr0cur.h, + include/row0mysql.h, row/row0merge.c, row/row0sel.c: + Fix Bug#54358 READ UNCOMMITTED access failure of off-page DYNAMIC + or COMPRESSED columns + 2010-06-24 The InnoDB Team * handler/ha_innodb.cc: From c8e48de2610cf65b65b1ea01e22c612aafce7fcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 29 Jun 2010 16:00:58 +0300 Subject: [PATCH 042/115] Bug#54408: txn rollback after recovery: row0umod.c:673 dict_table_get_format(index->table) The REDUNDANT and COMPACT formats store a local 768-byte prefix of each externally stored column. No row_ext cache is needed, but we initialized one nevertheless. When the BLOB pointer was zero, we would ignore the locally stored prefix as well. This triggered an assertion failure in row_undo_mod_upd_exist_sec(). row_build(): Allow ext==NULL when a REDUNDANT or COMPACT table contains externally stored columns. row_undo_search_clust_to_pcur(), row_upd_store_row(): Invoke row_build() with ext==NULL on REDUNDANT and COMPACT tables. rb://382 approved by Jimmy Yang --- storage/innodb_plugin/row/row0row.c | 8 +++++++- storage/innodb_plugin/row/row0undo.c | 18 +++++++++++++++++- storage/innodb_plugin/row/row0upd.c | 17 ++++++++++++++++- 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/storage/innodb_plugin/row/row0row.c b/storage/innodb_plugin/row/row0row.c index cb7dfa2b7c9..8e806a14a98 100644 --- a/storage/innodb_plugin/row/row0row.c +++ b/storage/innodb_plugin/row/row0row.c @@ -294,7 +294,13 @@ row_build( ut_ad(dtuple_check_typed(row)); - if (j) { + if (!ext) { + /* REDUNDANT and COMPACT formats store a local + 768-byte prefix of each externally stored + column. No cache is needed. */ + ut_ad(dict_table_get_format(index->table) + < DICT_TF_FORMAT_ZIP); + } else if (j) { *ext = row_ext_create(j, ext_cols, row, dict_table_zip_size(index->table), heap); diff --git a/storage/innodb_plugin/row/row0undo.c b/storage/innodb_plugin/row/row0undo.c index 9ef842b5114..fd28a4f6520 100644 --- a/storage/innodb_plugin/row/row0undo.c +++ b/storage/innodb_plugin/row/row0undo.c @@ -199,8 +199,24 @@ row_undo_search_clust_to_pcur( ret = FALSE; } else { + row_ext_t** ext; + + if (dict_table_get_format(node->table) >= DICT_TF_FORMAT_ZIP) { + /* In DYNAMIC or COMPRESSED format, there is + no prefix of externally stored columns in the + clustered index record. Build a cache of + column prefixes. */ + ext = &node->ext; + } else { + /* REDUNDANT and COMPACT formats store a local + 768-byte prefix of each externally stored + column. No cache is needed. */ + ext = NULL; + node->ext = NULL; + } + node->row = row_build(ROW_COPY_DATA, clust_index, rec, - offsets, NULL, &node->ext, node->heap); + offsets, NULL, ext, node->heap); if (node->update) { node->undo_row = dtuple_copy(node->row, node->heap); row_upd_replace(node->undo_row, &node->undo_ext, diff --git a/storage/innodb_plugin/row/row0upd.c b/storage/innodb_plugin/row/row0upd.c index d0aaecd3dae..397b117c067 100644 --- a/storage/innodb_plugin/row/row0upd.c +++ b/storage/innodb_plugin/row/row0upd.c @@ -1398,6 +1398,7 @@ row_upd_store_row( dict_index_t* clust_index; rec_t* rec; mem_heap_t* heap = NULL; + row_ext_t** ext; ulint offsets_[REC_OFFS_NORMAL_SIZE]; const ulint* offsets; rec_offs_init(offsets_); @@ -1414,8 +1415,22 @@ row_upd_store_row( offsets = rec_get_offsets(rec, clust_index, offsets_, ULINT_UNDEFINED, &heap); + + if (dict_table_get_format(node->table) >= DICT_TF_FORMAT_ZIP) { + /* In DYNAMIC or COMPRESSED format, there is no prefix + of externally stored columns in the clustered index + record. Build a cache of column prefixes. */ + ext = &node->ext; + } else { + /* REDUNDANT and COMPACT formats store a local + 768-byte prefix of each externally stored column. + No cache is needed. */ + ext = NULL; + node->ext = NULL; + } + node->row = row_build(ROW_COPY_DATA, clust_index, rec, offsets, - NULL, &node->ext, node->heap); + NULL, ext, node->heap); if (node->is_delete) { node->upd_row = NULL; node->upd_ext = NULL; From 5a25f4081570b539a20a845320e92b1018aef3b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 29 Jun 2010 16:12:19 +0300 Subject: [PATCH 043/115] ChangeLog entry for Bug #54408 --- storage/innodb_plugin/ChangeLog | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/storage/innodb_plugin/ChangeLog b/storage/innodb_plugin/ChangeLog index 5e41c5f180a..f7e9a3df943 100644 --- a/storage/innodb_plugin/ChangeLog +++ b/storage/innodb_plugin/ChangeLog @@ -1,3 +1,8 @@ +2010-06-29 The InnoDB Team + * row/row0row.c, row/row0undo.c, row/row0upd.c: + Fix Bug#54408 txn rollback after recovery: row0umod.c:673 + dict_table_get_format(index->table) + 2010-06-29 The InnoDB Team * btr/btr0cur.c, include/btr0cur.h, From 6e6d119a25ddda3599b9491823b4c32e4ee17ede Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 29 Jun 2010 16:19:07 +0300 Subject: [PATCH 044/115] =?UTF-8?q?Merge=20Bug#54358=20fix=20from=20mysql-?= =?UTF-8?q?5.1-innodb:=20-------------------------------------------------?= =?UTF-8?q?-----------=20revno:=203529=20revision-id:=20marko.makela@oracl?= =?UTF-8?q?e.com-20100629125518-m3am4ia1ffjr0d0j=20parent:=20jimmy.yang@or?= =?UTF-8?q?acle.com-20100629024137-690sacm5sogruzvb=20committer:=20Marko?= =?UTF-8?q?=20M=C3=A4kel=C3=A4=20=20branch=20nick?= =?UTF-8?q?:=205.1-innodb=20timestamp:=20Tue=202010-06-29=2015:55:18=20+03?= =?UTF-8?q?00=20message:=20=20=20Bug#54358:=20READ=20UNCOMMITTED=20access?= =?UTF-8?q?=20failure=20of=20off-page=20DYNAMIC=20or=20COMPRESSED=20=20=20?= =?UTF-8?q?columns?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the server crashes after a record stub has been inserted and before all its off-page columns have been written, the record will contain incomplete off-page columns after crash recovery. Such records may only be accessed at the READ UNCOMMITTED isolation level or when rolling back a recovered transaction in recv_recovery_rollback_active(). Skip these records at the READ UNCOMMITTED isolation level. TODO: Add assertions for checking the above assumptions hold when an incomplete BLOB is encountered. btr_rec_copy_externally_stored_field(): Return NULL if the field is incomplete. row_prebuilt_t::templ_contains_blob: Clarify what "BLOB" means in this context. Hint: MySQL BLOBs are not the same as InnoDB BLOBs. row_sel_store_mysql_rec(): Return FALSE if not all columns could be retrieved. Previously this function always returned TRUE. Assert that the record is not delete-marked. row_sel_push_cache_row_for_mysql(): Return FALSE if not all columns could be retrieved. row_search_for_mysql(): Skip records containing incomplete off-page columns. Assert that the transaction isolation level is READ UNCOMMITTED. rb://380 approved by Jimmy Yang --- storage/innobase/btr/btr0cur.c | 24 +++++- storage/innobase/include/btr0cur.h | 2 +- storage/innobase/include/row0mysql.h | 6 +- storage/innobase/row/row0merge.c | 5 ++ storage/innobase/row/row0sel.c | 113 +++++++++++++++++++++++---- 5 files changed, 133 insertions(+), 17 deletions(-) diff --git a/storage/innobase/btr/btr0cur.c b/storage/innobase/btr/btr0cur.c index f1d8bb52825..90dfba7e151 100644 --- a/storage/innobase/btr/btr0cur.c +++ b/storage/innobase/btr/btr0cur.c @@ -4934,7 +4934,7 @@ btr_copy_externally_stored_field( /*******************************************************************//** Copies an externally stored field of a record to mem heap. -@return the field copied to heap */ +@return the field copied to heap, or NULL if the field is incomplete */ UNIV_INTERN byte* btr_rec_copy_externally_stored_field( @@ -4964,6 +4964,28 @@ btr_rec_copy_externally_stored_field( data = rec_get_nth_field(rec, offsets, no, &local_len); + ut_a(local_len >= BTR_EXTERN_FIELD_REF_SIZE); + + if (UNIV_UNLIKELY + (!memcmp(data + local_len - BTR_EXTERN_FIELD_REF_SIZE, + field_ref_zero, BTR_EXTERN_FIELD_REF_SIZE))) { + /* The externally stored field was not written + yet. This is only a valid condition when the server + crashed after the time a record stub was freshly + inserted but before all its columns were written. This + record should only be seen by + recv_recovery_rollback_active() or any + TRX_ISO_READ_UNCOMMITTED transactions. */ + + /* TODO: assert that there is an owner_trx with + owner_trx->id == DB_TRX_ID and owner_trx->is_recovered */ + + /* TODO: assert that for the current transaction trx, + either (trx == owner_trx && trx_is_recv(trx)) or + trx->isolation_level == TRX_ISO_READ_UNCOMMITTED. */ + return(NULL); + } + return(btr_copy_externally_stored_field(len, data, zip_size, local_len, heap)); } diff --git a/storage/innobase/include/btr0cur.h b/storage/innobase/include/btr0cur.h index 136d2d068a1..757477838ee 100644 --- a/storage/innobase/include/btr0cur.h +++ b/storage/innobase/include/btr0cur.h @@ -558,7 +558,7 @@ btr_copy_externally_stored_field_prefix( ulint local_len);/*!< in: length of data, in bytes */ /*******************************************************************//** Copies an externally stored field of a record to mem heap. -@return the field copied to heap */ +@return the field copied to heap, or NULL if the field is incomplete */ UNIV_INTERN byte* btr_rec_copy_externally_stored_field( diff --git a/storage/innobase/include/row0mysql.h b/storage/innobase/include/row0mysql.h index f97d90adb2b..d9c26a2ee3b 100644 --- a/storage/innobase/include/row0mysql.h +++ b/storage/innobase/include/row0mysql.h @@ -633,7 +633,11 @@ struct row_prebuilt_struct { the secondary index, then this is set to TRUE */ unsigned templ_contains_blob:1;/*!< TRUE if the template contains - BLOB column(s) */ + a column with DATA_BLOB == + get_innobase_type_from_mysql_type(); + not to be confused with InnoDB + externally stored columns + (VARCHAR can be off-page too) */ mysql_row_templ_t* mysql_template;/*!< template used to transform rows fast between MySQL and Innobase formats; memory for this template diff --git a/storage/innobase/row/row0merge.c b/storage/innobase/row/row0merge.c index 3fdad6d423b..417e125375a 100644 --- a/storage/innobase/row/row0merge.c +++ b/storage/innobase/row/row0merge.c @@ -1779,6 +1779,11 @@ row_merge_copy_blobs( (below). */ data = btr_rec_copy_externally_stored_field( mrec, offsets, zip_size, i, &len, heap); + /* Because we have locked the table, any records + written by incomplete transactions must have been + rolled back already. There must not be any incomplete + BLOB columns. */ + ut_a(data); dfield_set_data(field, data, len); } diff --git a/storage/innobase/row/row0sel.c b/storage/innobase/row/row0sel.c index 4bd61016b21..13c867dc4c3 100644 --- a/storage/innobase/row/row0sel.c +++ b/storage/innobase/row/row0sel.c @@ -414,7 +414,7 @@ row_sel_fetch_columns( field_no))) { /* Copy an externally stored field to the - temporary heap */ + temporary heap, if possible. */ heap = mem_heap_create(1); @@ -423,6 +423,21 @@ row_sel_fetch_columns( dict_table_zip_size(index->table), field_no, &len, heap); + /* data == NULL means that the + externally stored field was not + written yet. This is only a valid + condition when the server crashed + after the time a record stub was + freshly inserted but before all its + columns were written. This record + should only be seen by + recv_recovery_rollback_active() or any + TRX_ISO_READ_UNCOMMITTED + transactions. The InnoDB SQL parser + (the sole caller of this function) + does not implement READ UNCOMMITTED, + and it is not involved during rollback. */ + ut_a(data); ut_a(len != UNIV_SQL_NULL); needs_copy = TRUE; @@ -924,6 +939,7 @@ row_sel_get_clust_rec( when plan->clust_pcur was positioned. The latch will not be released until mtr_commit(mtr). */ + ut_ad(!rec_get_deleted_flag(clust_rec, rec_offs_comp(offsets))); row_sel_fetch_columns(index, clust_rec, offsets, UT_LIST_GET_FIRST(plan->columns)); *out_rec = clust_rec; @@ -1626,6 +1642,13 @@ skip_lock: } if (old_vers == NULL) { + /* The record does not exist + in our read view. Skip it, but + first attempt to determine + whether the index segment we + are searching through has been + exhausted. */ + offsets = rec_get_offsets( rec, index, offsets, ULINT_UNDEFINED, &heap); @@ -2639,9 +2662,8 @@ Convert a row in the Innobase format to a row in the MySQL format. Note that the template in prebuilt may advise us to copy only a few columns to mysql_rec, other columns are left blank. All columns may not be needed in the query. -@return TRUE if success, FALSE if could not allocate memory for a BLOB -(though we may also assert in that case) */ -static +@return TRUE on success, FALSE if not all columns could be retrieved */ +static __attribute__((warn_unused_result)) ibool row_sel_store_mysql_rec( /*====================*/ @@ -2664,6 +2686,7 @@ row_sel_store_mysql_rec( ut_ad(prebuilt->mysql_template); ut_ad(prebuilt->default_rec); ut_ad(rec_offs_validate(rec, NULL, offsets)); + ut_ad(!rec_get_deleted_flag(rec, rec_offs_comp(offsets))); if (UNIV_LIKELY_NULL(prebuilt->blob_heap)) { mem_heap_free(prebuilt->blob_heap); @@ -2711,6 +2734,26 @@ row_sel_store_mysql_rec( dict_table_zip_size(prebuilt->table), templ->rec_field_no, &len, heap); + if (UNIV_UNLIKELY(!data)) { + /* The externally stored field + was not written yet. This is + only a valid condition when + the server crashed after the + time a record stub was freshly + inserted but before all its + columns were written. This + record should only be seen by + recv_recovery_rollback_active() + or any TRX_ISO_READ_UNCOMMITTED + transactions. */ + + if (extern_field_heap) { + mem_heap_free(extern_field_heap); + } + + return(FALSE); + } + ut_a(len != UNIV_SQL_NULL); } else { /* Field is stored in the row. */ @@ -3128,9 +3171,10 @@ row_sel_pop_cached_row_for_mysql( } /********************************************************************//** -Pushes a row for MySQL to the fetch cache. */ -UNIV_INLINE -void +Pushes a row for MySQL to the fetch cache. +@return TRUE on success, FALSE if the record contains incomplete BLOBs */ +UNIV_INLINE __attribute__((warn_unused_result)) +ibool row_sel_push_cache_row_for_mysql( /*=============================*/ row_prebuilt_t* prebuilt, /*!< in: prebuilt struct */ @@ -3172,10 +3216,11 @@ row_sel_push_cache_row_for_mysql( prebuilt->fetch_cache[ prebuilt->n_fetch_cached], prebuilt, rec, offsets))) { - ut_error; + return(FALSE); } prebuilt->n_fetch_cached++; + return(TRUE); } /*********************************************************************//** @@ -3570,6 +3615,24 @@ row_search_for_mysql( if (!row_sel_store_mysql_rec(buf, prebuilt, rec, offsets)) { + /* Only fresh inserts at + server crash time may contain + incomplete externally stored + columns. Pretend that such + records do not exist. Such + records may only be accessed + at the READ UNCOMMITTED + isolation level or when + rolling back a recovered + transaction. Rollback happens + at a lower level, not here. */ + ut_a(trx->isolation_level + == TRX_ISO_READ_UNCOMMITTED); + /* TODO: assert that there is + an owner_trx with + owner_trx->id == DB_TRX_ID and + owner_trx->is_recovered */ + err = DB_TOO_BIG_RECORD; /* We let the main loop to do the @@ -4349,9 +4412,20 @@ requires_clust_rec: not cache rows because there the cursor is a scrollable cursor. */ - row_sel_push_cache_row_for_mysql(prebuilt, result_rec, - offsets); - if (prebuilt->n_fetch_cached == MYSQL_FETCH_CACHE_SIZE) { + if (!row_sel_push_cache_row_for_mysql(prebuilt, result_rec, + offsets)) { + /* Only fresh inserts at server crash time may contain + incomplete externally stored columns. Pretend that + such records do not exist. Such records may only be + accessed at the READ UNCOMMITTED isolation level or + when rolling back a recovered transaction. Rollback + happens at a lower level, not here. */ + ut_a(trx->isolation_level == TRX_ISO_READ_UNCOMMITTED); + /* TODO: assert that there is an owner_trx + with owner_trx->id == DB_TRX_ID and + owner_trx->is_recovered */ + } else if (prebuilt->n_fetch_cached + == MYSQL_FETCH_CACHE_SIZE) { goto got_row; } @@ -4367,9 +4441,20 @@ requires_clust_rec: } else { if (!row_sel_store_mysql_rec(buf, prebuilt, result_rec, offsets)) { - err = DB_TOO_BIG_RECORD; - - goto lock_wait_or_error; + /* Only fresh inserts at server crash + time may contain incomplete externally + stored columns. Pretend that such + records do not exist. Such records may + only be accessed at the READ UNCOMMITTED + isolation level or when rolling back a + recovered transaction. Rollback happens + at a lower level, not here. */ + ut_a(trx->isolation_level + == TRX_ISO_READ_UNCOMMITTED); + /* TODO: assert that there is an owner_trx + with owner_trx->id == DB_TRX_ID and + owner_trx->is_recovered */ + goto next_rec; } } From 1cc7629e368770448c316152b463a9a9e5dd2c51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 29 Jun 2010 16:21:05 +0300 Subject: [PATCH 045/115] =?UTF-8?q?Merge=20Bug#54408=20fix=20from=20mysql-?= =?UTF-8?q?5.1-innodb:=20-------------------------------------------------?= =?UTF-8?q?-----------=20revno:=203531=20revision-id:=20marko.makela@oracl?= =?UTF-8?q?e.com-20100629130058-1ilqaj51u9sj9vqe=20parent:=20marko.makela@?= =?UTF-8?q?oracle.com-20100629125653-t799e5x30h31cvrd=20committer:=20Marko?= =?UTF-8?q?=20M=C3=A4kel=C3=A4=20=20branch=20nick?= =?UTF-8?q?:=205.1-innodb=20timestamp:=20Tue=202010-06-29=2016:00:58=20+03?= =?UTF-8?q?00=20message:=20=20=20Bug#54408:=20txn=20rollback=20after=20rec?= =?UTF-8?q?overy:=20row0umod.c:673=20=20=20dict=5Ftable=5Fget=5Fformat(ind?= =?UTF-8?q?ex->table)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The REDUNDANT and COMPACT formats store a local 768-byte prefix of each externally stored column. No row_ext cache is needed, but we initialized one nevertheless. When the BLOB pointer was zero, we would ignore the locally stored prefix as well. This triggered an assertion failure in row_undo_mod_upd_exist_sec(). row_build(): Allow ext==NULL when a REDUNDANT or COMPACT table contains externally stored columns. row_undo_search_clust_to_pcur(), row_upd_store_row(): Invoke row_build() with ext==NULL on REDUNDANT and COMPACT tables. rb://382 approved by Jimmy Yang --- storage/innobase/row/row0row.c | 8 +++++++- storage/innobase/row/row0undo.c | 18 +++++++++++++++++- storage/innobase/row/row0upd.c | 17 ++++++++++++++++- 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/storage/innobase/row/row0row.c b/storage/innobase/row/row0row.c index 951148977f9..050b8522fa3 100644 --- a/storage/innobase/row/row0row.c +++ b/storage/innobase/row/row0row.c @@ -294,7 +294,13 @@ row_build( ut_ad(dtuple_check_typed(row)); - if (j) { + if (!ext) { + /* REDUNDANT and COMPACT formats store a local + 768-byte prefix of each externally stored + column. No cache is needed. */ + ut_ad(dict_table_get_format(index->table) + < DICT_TF_FORMAT_ZIP); + } else if (j) { *ext = row_ext_create(j, ext_cols, row, dict_table_zip_size(index->table), heap); diff --git a/storage/innobase/row/row0undo.c b/storage/innobase/row/row0undo.c index ef97fa50cd1..09970b7fe21 100644 --- a/storage/innobase/row/row0undo.c +++ b/storage/innobase/row/row0undo.c @@ -198,8 +198,24 @@ row_undo_search_clust_to_pcur( ret = FALSE; } else { + row_ext_t** ext; + + if (dict_table_get_format(node->table) >= DICT_TF_FORMAT_ZIP) { + /* In DYNAMIC or COMPRESSED format, there is + no prefix of externally stored columns in the + clustered index record. Build a cache of + column prefixes. */ + ext = &node->ext; + } else { + /* REDUNDANT and COMPACT formats store a local + 768-byte prefix of each externally stored + column. No cache is needed. */ + ext = NULL; + node->ext = NULL; + } + node->row = row_build(ROW_COPY_DATA, clust_index, rec, - offsets, NULL, &node->ext, node->heap); + offsets, NULL, ext, node->heap); if (node->update) { node->undo_row = dtuple_copy(node->row, node->heap); row_upd_replace(node->undo_row, &node->undo_ext, diff --git a/storage/innobase/row/row0upd.c b/storage/innobase/row/row0upd.c index 8b151ec34cc..588ee352ba0 100644 --- a/storage/innobase/row/row0upd.c +++ b/storage/innobase/row/row0upd.c @@ -1398,6 +1398,7 @@ row_upd_store_row( dict_index_t* clust_index; rec_t* rec; mem_heap_t* heap = NULL; + row_ext_t** ext; ulint offsets_[REC_OFFS_NORMAL_SIZE]; const ulint* offsets; rec_offs_init(offsets_); @@ -1414,8 +1415,22 @@ row_upd_store_row( offsets = rec_get_offsets(rec, clust_index, offsets_, ULINT_UNDEFINED, &heap); + + if (dict_table_get_format(node->table) >= DICT_TF_FORMAT_ZIP) { + /* In DYNAMIC or COMPRESSED format, there is no prefix + of externally stored columns in the clustered index + record. Build a cache of column prefixes. */ + ext = &node->ext; + } else { + /* REDUNDANT and COMPACT formats store a local + 768-byte prefix of each externally stored column. + No cache is needed. */ + ext = NULL; + node->ext = NULL; + } + node->row = row_build(ROW_COPY_DATA, clust_index, rec, offsets, - NULL, &node->ext, node->heap); + NULL, ext, node->heap); if (node->is_delete) { node->upd_row = NULL; node->upd_ext = NULL; From 02599d659a3b52b1a4715b1913816a54e2b324e9 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Tue, 29 Jun 2010 18:01:33 +0400 Subject: [PATCH 046/115] A fix for Bug#54811 "Assert in mysql_lock_have_duplicate()". Remove mysql_lock_have_duplicate(), since now we always have TABLE_LIST objects for MyISAMMRG children in lex->query_tables and keep it till the end of the statement (sub-statement). --- mysql-test/r/merge.result | 102 +++++++++++++++++++++++++++++++ mysql-test/t/merge-big.test | 4 +- mysql-test/t/merge.test | 119 ++++++++++++++++++++++++++++++++++++ sql/lock.cc | 104 ------------------------------- sql/lock.h | 2 - sql/sql_base.cc | 85 ++++++++++++++++++++------ 6 files changed, 288 insertions(+), 128 deletions(-) diff --git a/mysql-test/r/merge.result b/mysql-test/r/merge.result index 8f7ebb06c06..63b957e7a3f 100644 --- a/mysql-test/r/merge.result +++ b/mysql-test/r/merge.result @@ -2718,4 +2718,106 @@ m2 CREATE TABLE `m2` ( `i` int(11) DEFAULT NULL ) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 INSERT_METHOD=FIRST UNION=(`t1`) drop tables m1, m2, t1; +# +# Test case for Bug#54811 "Assert in mysql_lock_have_duplicate()" +# Check that unique_table() works correctly for merge tables. +# +drop table if exists t1, t2, t3, m1, m2; +create table t1 (a int); +create table t2 (a int); +create table t3 (b int); +create view v1 as select * from t3,t1; +create table m1 (a int) engine=merge union (t1, t2) insert_method=last; +create table m2 (a int) engine=merge union (t1, t2) insert_method=first; +create temporary table tmp (b int); +insert into tmp (b) values (1); +insert into t1 (a) values (1); +insert into t3 (b) values (1); +insert into m1 (a) values ((select max(a) from m1)); +ERROR HY000: You can't specify target table 'm1' for update in FROM clause +insert into m1 (a) values ((select max(a) from m2)); +ERROR HY000: You can't specify target table 'm1' for update in FROM clause +insert into m1 (a) values ((select max(a) from t1)); +ERROR HY000: You can't specify target table 'm1' for update in FROM clause +insert into m1 (a) values ((select max(a) from t2)); +ERROR HY000: You can't specify target table 'm1' for update in FROM clause +insert into m1 (a) values ((select max(a) from t3, m1)); +ERROR HY000: You can't specify target table 'm1' for update in FROM clause +insert into m1 (a) values ((select max(a) from t3, m2)); +ERROR HY000: You can't specify target table 'm1' for update in FROM clause +insert into m1 (a) values ((select max(a) from t3, t1)); +ERROR HY000: You can't specify target table 'm1' for update in FROM clause +insert into m1 (a) values ((select max(a) from t3, t2)); +ERROR HY000: You can't specify target table 'm1' for update in FROM clause +insert into m1 (a) values ((select max(a) from tmp, m1)); +ERROR HY000: You can't specify target table 'm1' for update in FROM clause +insert into m1 (a) values ((select max(a) from tmp, m2)); +ERROR HY000: You can't specify target table 'm1' for update in FROM clause +insert into m1 (a) values ((select max(a) from tmp, t1)); +ERROR HY000: You can't specify target table 'm1' for update in FROM clause +insert into m1 (a) values ((select max(a) from tmp, t2)); +ERROR HY000: You can't specify target table 'm1' for update in FROM clause +insert into m1 (a) values ((select max(a) from v1)); +ERROR HY000: The definition of table 'v1' prevents operation INSERT on table 'm1'. +insert into m1 (a) values ((select max(a) from tmp, v1)); +ERROR HY000: The definition of table 'v1' prevents operation INSERT on table 'm1'. +update m1 set a = ((select max(a) from m1)); +ERROR HY000: You can't specify target table 'm1' for update in FROM clause +update m1 set a = ((select max(a) from m2)); +ERROR HY000: You can't specify target table 'm1' for update in FROM clause +update m1 set a = ((select max(a) from t1)); +ERROR HY000: You can't specify target table 'm1' for update in FROM clause +update m1 set a = ((select max(a) from t2)); +ERROR HY000: You can't specify target table 'm1' for update in FROM clause +update m1 set a = ((select max(a) from t3, m1)); +ERROR HY000: You can't specify target table 'm1' for update in FROM clause +update m1 set a = ((select max(a) from t3, m2)); +ERROR HY000: You can't specify target table 'm1' for update in FROM clause +update m1 set a = ((select max(a) from t3, t1)); +ERROR HY000: You can't specify target table 'm1' for update in FROM clause +update m1 set a = ((select max(a) from t3, t2)); +ERROR HY000: You can't specify target table 'm1' for update in FROM clause +update m1 set a = ((select max(a) from tmp, m1)); +ERROR HY000: You can't specify target table 'm1' for update in FROM clause +update m1 set a = ((select max(a) from tmp, m2)); +ERROR HY000: You can't specify target table 'm1' for update in FROM clause +update m1 set a = ((select max(a) from tmp, t1)); +ERROR HY000: You can't specify target table 'm1' for update in FROM clause +update m1 set a = ((select max(a) from tmp, t2)); +ERROR HY000: You can't specify target table 'm1' for update in FROM clause +update m1 set a = ((select max(a) from v1)); +ERROR HY000: The definition of table 'v1' prevents operation UPDATE on table 'm1'. +update m1 set a = ((select max(a) from tmp, v1)); +ERROR HY000: The definition of table 'v1' prevents operation UPDATE on table 'm1'. +delete from m1 where a = (select max(a) from m1); +ERROR HY000: You can't specify target table 'm1' for update in FROM clause +delete from m1 where a = (select max(a) from m2); +ERROR HY000: You can't specify target table 'm1' for update in FROM clause +delete from m1 where a = (select max(a) from t1); +ERROR HY000: You can't specify target table 'm1' for update in FROM clause +delete from m1 where a = (select max(a) from t2); +ERROR HY000: You can't specify target table 'm1' for update in FROM clause +delete from m1 where a = (select max(a) from t3, m1); +ERROR HY000: You can't specify target table 'm1' for update in FROM clause +delete from m1 where a = (select max(a) from t3, m2); +ERROR HY000: You can't specify target table 'm1' for update in FROM clause +delete from m1 where a = (select max(a) from t3, t1); +ERROR HY000: You can't specify target table 'm1' for update in FROM clause +delete from m1 where a = (select max(a) from t3, t2); +ERROR HY000: You can't specify target table 'm1' for update in FROM clause +delete from m1 where a = (select max(a) from tmp, m1); +ERROR HY000: You can't specify target table 'm1' for update in FROM clause +delete from m1 where a = (select max(a) from tmp, m2); +ERROR HY000: You can't specify target table 'm1' for update in FROM clause +delete from m1 where a = (select max(a) from tmp, t1); +ERROR HY000: You can't specify target table 'm1' for update in FROM clause +delete from m1 where a = (select max(a) from tmp, t2); +ERROR HY000: You can't specify target table 'm1' for update in FROM clause +delete from m1 where a = (select max(a) from v1); +ERROR HY000: The definition of table 'v1' prevents operation DELETE on table 'm1'. +delete from m1 where a = (select max(a) from tmp, v1); +ERROR HY000: The definition of table 'v1' prevents operation DELETE on table 'm1'. +drop view v1; +drop temporary table tmp; +drop table t1, t2, t3, m1, m2; End of 6.0 tests diff --git a/mysql-test/t/merge-big.test b/mysql-test/t/merge-big.test index 33bd93791f1..509c7742dac 100644 --- a/mysql-test/t/merge-big.test +++ b/mysql-test/t/merge-big.test @@ -51,7 +51,7 @@ connection default; #--sleep 8 #SELECT ID,STATE,INFO FROM INFORMATION_SCHEMA.PROCESSLIST; let $wait_condition= SELECT 1 FROM INFORMATION_SCHEMA.PROCESSLIST - WHERE ID = $con1_id AND STATE = 'Table lock'; + WHERE ID = $con1_id AND STATE = 'Waiting for table'; --source include/wait_condition.inc #SELECT NOW(); --echo # Kick INSERT out of thr_multi_lock(). @@ -61,7 +61,7 @@ FLUSH TABLES; #--sleep 8 #SELECT ID,STATE,INFO FROM INFORMATION_SCHEMA.PROCESSLIST; let $wait_condition= SELECT 1 FROM INFORMATION_SCHEMA.PROCESSLIST - WHERE ID = $con1_id AND STATE = 'Table lock'; + WHERE ID = $con1_id AND STATE = 'Waiting for table'; --source include/wait_condition.inc #SELECT NOW(); --echo # Unlock and close table and wait for con1 to close too. diff --git a/mysql-test/t/merge.test b/mysql-test/t/merge.test index 29c0eae1df6..d7026011055 100644 --- a/mysql-test/t/merge.test +++ b/mysql-test/t/merge.test @@ -2201,6 +2201,125 @@ show create table m1; show create table m2; drop tables m1, m2, t1; +--echo # +--echo # Test case for Bug#54811 "Assert in mysql_lock_have_duplicate()" +--echo # Check that unique_table() works correctly for merge tables. +--echo # +--disable_warnings +drop table if exists t1, t2, t3, m1, m2; +--enable_warnings +create table t1 (a int); +create table t2 (a int); +create table t3 (b int); +create view v1 as select * from t3,t1; +create table m1 (a int) engine=merge union (t1, t2) insert_method=last; +create table m2 (a int) engine=merge union (t1, t2) insert_method=first; +create temporary table tmp (b int); +insert into tmp (b) values (1); + +insert into t1 (a) values (1); +insert into t3 (b) values (1); +--error ER_UPDATE_TABLE_USED +insert into m1 (a) values ((select max(a) from m1)); +--error ER_UPDATE_TABLE_USED +insert into m1 (a) values ((select max(a) from m2)); +--error ER_UPDATE_TABLE_USED +insert into m1 (a) values ((select max(a) from t1)); +--error ER_UPDATE_TABLE_USED +insert into m1 (a) values ((select max(a) from t2)); + +--error ER_UPDATE_TABLE_USED +insert into m1 (a) values ((select max(a) from t3, m1)); +--error ER_UPDATE_TABLE_USED +insert into m1 (a) values ((select max(a) from t3, m2)); +--error ER_UPDATE_TABLE_USED +insert into m1 (a) values ((select max(a) from t3, t1)); +--error ER_UPDATE_TABLE_USED +insert into m1 (a) values ((select max(a) from t3, t2)); + +--error ER_UPDATE_TABLE_USED +insert into m1 (a) values ((select max(a) from tmp, m1)); +--error ER_UPDATE_TABLE_USED +insert into m1 (a) values ((select max(a) from tmp, m2)); +--error ER_UPDATE_TABLE_USED +insert into m1 (a) values ((select max(a) from tmp, t1)); +--error ER_UPDATE_TABLE_USED +insert into m1 (a) values ((select max(a) from tmp, t2)); + +--error ER_VIEW_PREVENT_UPDATE +insert into m1 (a) values ((select max(a) from v1)); +--error ER_VIEW_PREVENT_UPDATE +insert into m1 (a) values ((select max(a) from tmp, v1)); + + +--error ER_UPDATE_TABLE_USED +update m1 set a = ((select max(a) from m1)); +--error ER_UPDATE_TABLE_USED +update m1 set a = ((select max(a) from m2)); +--error ER_UPDATE_TABLE_USED +update m1 set a = ((select max(a) from t1)); +--error ER_UPDATE_TABLE_USED +update m1 set a = ((select max(a) from t2)); + +--error ER_UPDATE_TABLE_USED +update m1 set a = ((select max(a) from t3, m1)); +--error ER_UPDATE_TABLE_USED +update m1 set a = ((select max(a) from t3, m2)); +--error ER_UPDATE_TABLE_USED +update m1 set a = ((select max(a) from t3, t1)); +--error ER_UPDATE_TABLE_USED +update m1 set a = ((select max(a) from t3, t2)); + +--error ER_UPDATE_TABLE_USED +update m1 set a = ((select max(a) from tmp, m1)); +--error ER_UPDATE_TABLE_USED +update m1 set a = ((select max(a) from tmp, m2)); +--error ER_UPDATE_TABLE_USED +update m1 set a = ((select max(a) from tmp, t1)); +--error ER_UPDATE_TABLE_USED +update m1 set a = ((select max(a) from tmp, t2)); + +--error ER_VIEW_PREVENT_UPDATE +update m1 set a = ((select max(a) from v1)); +--error ER_VIEW_PREVENT_UPDATE +update m1 set a = ((select max(a) from tmp, v1)); + + +--error ER_UPDATE_TABLE_USED +delete from m1 where a = (select max(a) from m1); +--error ER_UPDATE_TABLE_USED +delete from m1 where a = (select max(a) from m2); +--error ER_UPDATE_TABLE_USED +delete from m1 where a = (select max(a) from t1); +--error ER_UPDATE_TABLE_USED +delete from m1 where a = (select max(a) from t2); + +--error ER_UPDATE_TABLE_USED +delete from m1 where a = (select max(a) from t3, m1); +--error ER_UPDATE_TABLE_USED +delete from m1 where a = (select max(a) from t3, m2); +--error ER_UPDATE_TABLE_USED +delete from m1 where a = (select max(a) from t3, t1); +--error ER_UPDATE_TABLE_USED +delete from m1 where a = (select max(a) from t3, t2); + +--error ER_UPDATE_TABLE_USED +delete from m1 where a = (select max(a) from tmp, m1); +--error ER_UPDATE_TABLE_USED +delete from m1 where a = (select max(a) from tmp, m2); +--error ER_UPDATE_TABLE_USED +delete from m1 where a = (select max(a) from tmp, t1); +--error ER_UPDATE_TABLE_USED +delete from m1 where a = (select max(a) from tmp, t2); + +--error ER_VIEW_PREVENT_UPDATE +delete from m1 where a = (select max(a) from v1); +--error ER_VIEW_PREVENT_UPDATE +delete from m1 where a = (select max(a) from tmp, v1); + +drop view v1; +drop temporary table tmp; +drop table t1, t2, t3, m1, m2; --echo End of 6.0 tests diff --git a/sql/lock.cc b/sql/lock.cc index 52d97a2422b..de0f39018f7 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -627,110 +627,6 @@ MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b) } -/** - Find duplicate lock in tables. - - Temporary tables are ignored here like they are ignored in - get_lock_data(). If we allow two opens on temporary tables later, - both functions should be checked. - - @param thd The current thread. - @param needle The table to check for duplicate lock. - @param haystack The list of tables to search for the dup lock. - - @note - This is mainly meant for MERGE tables in INSERT ... SELECT - situations. The 'real', underlying tables can be found only after - the MERGE tables are opened. This function assumes that the tables are - already locked. - - @retval - NULL No duplicate lock found. - @retval - !NULL First table from 'haystack' that matches a lock on 'needle'. -*/ - -TABLE_LIST *mysql_lock_have_duplicate(THD *thd, TABLE_LIST *needle, - TABLE_LIST *haystack) -{ - MYSQL_LOCK *mylock; - TABLE **lock_tables; - TABLE *table; - TABLE *table2; - THR_LOCK_DATA **lock_locks; - THR_LOCK_DATA **table_lock_data; - THR_LOCK_DATA **end_data; - THR_LOCK_DATA **lock_data2; - THR_LOCK_DATA **end_data2; - DBUG_ENTER("mysql_lock_have_duplicate"); - - /* - Table may not be defined for derived or view tables. - Table may not be part of a lock for delayed operations. - */ - if (! (table= needle->table) || ! table->lock_count) - goto end; - - /* A temporary table does not have locks. */ - if (table->s->tmp_table == NON_TRANSACTIONAL_TMP_TABLE) - goto end; - - /* Get command lock or LOCK TABLES lock. Maybe empty for INSERT DELAYED. */ - if (! (mylock= thd->lock)) - goto end; - - /* If we have less than two tables, we cannot have duplicates. */ - if (mylock->table_count < 2) - goto end; - - lock_locks= mylock->locks; - lock_tables= mylock->table; - - /* Prepare table related variables that don't change in loop. */ - DBUG_ASSERT((table->lock_position < mylock->table_count) && - (table == lock_tables[table->lock_position])); - table_lock_data= lock_locks + table->lock_data_start; - end_data= table_lock_data + table->lock_count; - - for (; haystack; haystack= haystack->next_global) - { - if (haystack->placeholder()) - continue; - table2= haystack->table; - if (table2->s->tmp_table == NON_TRANSACTIONAL_TMP_TABLE) - continue; - - /* All tables in list must be in lock. */ - DBUG_ASSERT((table2->lock_position < mylock->table_count) && - (table2 == lock_tables[table2->lock_position])); - - for (lock_data2= lock_locks + table2->lock_data_start, - end_data2= lock_data2 + table2->lock_count; - lock_data2 < end_data2; - lock_data2++) - { - THR_LOCK_DATA **lock_data; - THR_LOCK *lock2= (*lock_data2)->lock; - - for (lock_data= table_lock_data; - lock_data < end_data; - lock_data++) - { - if ((*lock_data)->lock == lock2) - { - DBUG_PRINT("info", ("haystack match: '%s'", haystack->table_name)); - DBUG_RETURN(haystack); - } - } - } - } - - end: - DBUG_PRINT("info", ("no duplicate found")); - DBUG_RETURN(NULL); -} - - /** Unlock a set of external. */ static int unlock_external(THD *thd, TABLE **table,uint count) diff --git a/sql/lock.h b/sql/lock.h index 84c7bce0679..4bdf0085d07 100644 --- a/sql/lock.h +++ b/sql/lock.h @@ -60,8 +60,6 @@ void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table); void mysql_lock_abort(THD *thd, TABLE *table, bool upgrade_lock); bool mysql_lock_abort_for_thread(THD *thd, TABLE *table); MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b); -TABLE_LIST *mysql_lock_have_duplicate(THD *thd, TABLE_LIST *needle, - TABLE_LIST *haystack); void broadcast_refresh(void); /* Lock based on name */ bool lock_table_names(THD *thd, TABLE_LIST *table_list); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 0d4d949d701..3758ec7253d 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1931,15 +1931,14 @@ TABLE_LIST *find_table_in_list(TABLE_LIST *table, } -/* + +/** Test that table is unique (It's only exists once in the table list) - SYNOPSIS - unique_table() - thd thread handle - table table which should be checked - table_list list of tables - check_alias whether to check tables' aliases + @param thd thread handle + @param table table which should be checked + @param table_list list of tables + @param check_alias whether to check tables' aliases NOTE: to exclude derived tables from check we use following mechanism: a) during derived table processing set THD::derived_tables_processing @@ -1950,7 +1949,7 @@ TABLE_LIST *find_table_in_list(TABLE_LIST *table, processing loop, because multi-update call fix_fields() for some its items (which mean JOIN::prepare for subqueries) before unique_table call to detect which tables should be locked for write). - c) unique_table skip all tables which belong to SELECT with + c) find_dup_table skip all tables which belong to SELECT with SELECT::exclude_from_table_unique_test set. Also SELECT::exclude_from_table_unique_test used to exclude from check tables of main SELECT of multi-delete and multi-update @@ -1962,17 +1961,17 @@ TABLE_LIST *find_table_in_list(TABLE_LIST *table, TODO: when we will have table/view change detection we can do this check only once for PS/SP - RETURN - found duplicate - 0 if table is unique + @retval !=0 found duplicate + @retval 0 if table is unique */ -TABLE_LIST* unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list, - bool check_alias) +static +TABLE_LIST* find_dup_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list, + bool check_alias) { TABLE_LIST *res; const char *d_name, *t_name, *t_alias; - DBUG_ENTER("unique_table"); + DBUG_ENTER("find_dup_table"); DBUG_PRINT("enter", ("table alias: %s", table->alias)); /* @@ -1987,6 +1986,9 @@ TABLE_LIST* unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list, */ if (table->table) { + /* All MyISAMMRG children are plain MyISAM tables. */ + DBUG_ASSERT(table->table->file->ht->db_type != DB_TYPE_MRG_MYISAM); + /* temporary table is always unique */ if (table->table && table->table->s->tmp_table != NO_TMP_TABLE) DBUG_RETURN(0); @@ -2008,8 +2010,7 @@ TABLE_LIST* unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list, Table is unique if it is present only once in the global list of tables and once in the list of table locks. */ - if (! (res= find_table_in_global_list(table_list, d_name, t_name)) && - ! (res= mysql_lock_have_duplicate(thd, table, table_list))) + if (! (res= find_table_in_global_list(table_list, d_name, t_name))) break; /* Skip if same underlying table. */ @@ -2048,6 +2049,37 @@ next: } +/** + Test that the subject table of INSERT/UPDATE/DELETE/CREATE + or (in case of MyISAMMRG) one of its children are not used later + in the query. + + @retval non-NULL The table list element for the table that + represents the duplicate. + @retval NULL No duplicates found. +*/ + +TABLE_LIST* +unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list, + bool check_alias) +{ + TABLE_LIST *dup; + if (table->table && table->table->file->ht->db_type == DB_TYPE_MRG_MYISAM) + { + TABLE_LIST *child; + dup= NULL; + /* Check duplicates of all merge children. */ + for (child= table->next_global; child && child->parent_l == table; + child= child->next_global) + { + if ((dup= find_dup_table(thd, child, child->next_global, check_alias))) + break; + } + } + else + dup= find_dup_table(thd, table, table_list, check_alias); + return dup; +} /* Issue correct error message in case we found 2 duplicate tables which prevent some update operation @@ -3204,8 +3236,7 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, table_list->table= table; DBUG_ASSERT(table->key_read == 0); /* Tables may be reused in a sub statement. */ - if (table->file->extra(HA_EXTRA_IS_ATTACHED_CHILDREN)) - table->file->extra(HA_EXTRA_DETACH_CHILDREN); + DBUG_ASSERT(! table->file->extra(HA_EXTRA_IS_ATTACHED_CHILDREN)); DBUG_RETURN(FALSE); err_lock: @@ -5563,11 +5594,25 @@ bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags) or schema tables) as free for reuse. */ -static void mark_real_tables_as_free_for_reuse(TABLE_LIST *table) +static void mark_real_tables_as_free_for_reuse(TABLE_LIST *table_list) { - for (; table; table= table->next_global) + TABLE_LIST *table; + for (table= table_list; table; table= table->next_global) if (!table->placeholder()) + { table->table->query_id= 0; + } + for (table= table_list; table; table= table->next_global) + if (!table->placeholder()) + { + /* + Detach children of MyISAMMRG tables used in + sub-statements, they will be reattached at open. + This has to be done in a separate loop to make sure + that children have had their query_id cleared. + */ + table->table->file->extra(HA_EXTRA_DETACH_CHILDREN); + } } From cace7d3038fdd8f97c290a3ee60aa273bfa0f4b7 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Tue, 29 Jun 2010 18:28:36 +0400 Subject: [PATCH 047/115] Salvage comments added by Ingo while working on Bug#52114 and Bug#50788. The bugs themselves are regressions that are introduced by an incomplete fix for Bug#36171 and will not be pushed. --- mysys/mf_iocache.c | 52 ++++++++++++++++++++++++++++++++++--- sql/sql_base.cc | 6 ++++- storage/myisam/mi_statrec.c | 7 ++++- 3 files changed, 59 insertions(+), 6 deletions(-) diff --git a/mysys/mf_iocache.c b/mysys/mf_iocache.c index 620ac667a8b..b7fe36ac049 100644 --- a/mysys/mf_iocache.c +++ b/mysys/mf_iocache.c @@ -455,7 +455,9 @@ my_bool reinit_io_cache(IO_CACHE *info, enum cache_type type, RETURN 0 we succeeded in reading all data - 1 Error: can't read requested characters + 1 Error: couldn't read requested characters. In this case: + If info->error == -1, we got a read error. + Otherwise info->error contains the number of bytes in Buffer. */ int _my_b_read(register IO_CACHE *info, uchar *Buffer, size_t Count) @@ -464,6 +466,7 @@ int _my_b_read(register IO_CACHE *info, uchar *Buffer, size_t Count) my_off_t pos_in_file; DBUG_ENTER("_my_b_read"); + /* If the buffer is not empty yet, copy what is available. */ if ((left_length= (size_t) (info->read_end-info->read_pos))) { DBUG_ASSERT(Count >= left_length); /* User is not using my_b_read() */ @@ -475,7 +478,7 @@ int _my_b_read(register IO_CACHE *info, uchar *Buffer, size_t Count) /* pos_in_file always point on where info->buffer was read */ pos_in_file=info->pos_in_file+ (size_t) (info->read_end - info->buffer); - /* + /* Whenever a function which operates on IO_CACHE flushes/writes some part of the IO_CACHE to disk it will set the property "seek_not_done" to indicate this to other functions operating @@ -502,19 +505,38 @@ int _my_b_read(register IO_CACHE *info, uchar *Buffer, size_t Count) } } + /* + Calculate, how much we are within a IO_SIZE block. Ideally this + should be zero. + */ diff_length= (size_t) (pos_in_file & (IO_SIZE-1)); + + /* + If more than a block plus the rest of the current block is wanted, + we do read directly, without filling the buffer. + */ if (Count >= (size_t) (IO_SIZE+(IO_SIZE-diff_length))) { /* Fill first intern buffer */ size_t read_length; if (info->end_of_file <= pos_in_file) - { /* End of file */ + { + /* End of file. Return, what we did copy from the buffer. */ info->error= (int) left_length; DBUG_RETURN(1); } + /* + Crop the wanted count to a multiple of IO_SIZE and subtract, + what we did already read from a block. That way, the read will + end aligned with a block. + */ length=(Count & (size_t) ~(IO_SIZE-1))-diff_length; if ((read_length= my_read(info->file,Buffer, length, info->myflags)) != length) { + /* + If we didn't get, what we wanted, we either return -1 for a read + error, or (it's end of file), how much we got in total. + */ info->error= (read_length == (size_t) -1 ? -1 : (int) (read_length+left_length)); DBUG_RETURN(1); @@ -526,15 +548,27 @@ int _my_b_read(register IO_CACHE *info, uchar *Buffer, size_t Count) diff_length=0; } + /* + At this point, we want less than one and a partial block. + We will read a full cache, minus the number of bytes, we are + within a block already. So we will reach new alignment. + */ max_length= info->read_length-diff_length; + /* We will not read past end of file. */ if (info->type != READ_FIFO && max_length > (info->end_of_file - pos_in_file)) max_length= (size_t) (info->end_of_file - pos_in_file); + /* + If there is nothing left to read, + we either are done, or we failed to fulfill the request. + Otherwise, we read max_length into the cache. + */ if (!max_length) { if (Count) { - info->error= left_length; /* We only got this many char */ + /* We couldn't fulfil the request. Return, how much we got. */ + info->error= left_length; DBUG_RETURN(1); } length=0; /* Didn't read any chars */ @@ -543,13 +577,23 @@ int _my_b_read(register IO_CACHE *info, uchar *Buffer, size_t Count) info->myflags)) < Count || length == (size_t) -1) { + /* + We got an read error, or less than requested (end of file). + If not a read error, copy, what we got. + */ if (length != (size_t) -1) memcpy(Buffer, info->buffer, length); info->pos_in_file= pos_in_file; + /* For a read error, return -1, otherwise, what we got in total. */ info->error= length == (size_t) -1 ? -1 : (int) (length+left_length); info->read_pos=info->read_end=info->buffer; DBUG_RETURN(1); } + /* + Count is the remaining number of bytes requested. + length is the amount of data in the cache. + Read Count bytes from the cache. + */ info->read_pos=info->buffer+Count; info->read_end=info->buffer+length; info->pos_in_file=pos_in_file; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 3758ec7253d..d69a5b1aa77 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1931,7 +1931,6 @@ TABLE_LIST *find_table_in_list(TABLE_LIST *table, } - /** Test that table is unique (It's only exists once in the table list) @@ -2054,6 +2053,11 @@ next: or (in case of MyISAMMRG) one of its children are not used later in the query. + For MyISAMMRG tables, it is assumed that all the underlying + tables of @c table (if any) are listed right after it and that + their @c parent_l field points at the main table. + + @retval non-NULL The table list element for the table that represents the duplicate. @retval NULL No duplicates found. diff --git a/storage/myisam/mi_statrec.c b/storage/myisam/mi_statrec.c index 74fca5902f5..7d595916d78 100644 --- a/storage/myisam/mi_statrec.c +++ b/storage/myisam/mi_statrec.c @@ -273,7 +273,12 @@ int _mi_read_rnd_static_record(MI_INFO *info, uchar *buf, DBUG_RETURN(error); } - /* Read record with cacheing */ + /* + Read record with caching. If my_b_read() returns TRUE, less than the + requested bytes have been read. In this case rec_cache.error is + either -1 for a read error, or contains the number of bytes copied + into the buffer. + */ error=my_b_read(&info->rec_cache,(uchar*) buf,share->base.reclength); if (info->s->base.pack_reclength != info->s->base.reclength && !error) { From aab20dd89b8700610e2b7a5a0f3e543dc7bb27c8 Mon Sep 17 00:00:00 2001 From: Mark Leith Date: Wed, 30 Jun 2010 10:11:04 +0100 Subject: [PATCH 048/115] Bug#54726 - Inconsistent reporting between mutex and RW lock spin rounds - Add round counts to the output for RW s/x locks --- storage/innobase/sync/sync0sync.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/storage/innobase/sync/sync0sync.c b/storage/innobase/sync/sync0sync.c index 235f733382d..0bc96aae9de 100644 --- a/storage/innobase/sync/sync0sync.c +++ b/storage/innobase/sync/sync0sync.c @@ -1492,14 +1492,16 @@ sync_print_wait_info( fprintf(file, "Mutex spin waits %llu, rounds %llu, OS waits %llu\n" - "RW-shared spins %llu, OS waits %llu;" - " RW-excl spins %llu, OS waits %llu\n", + "RW-shared spins %llu, rounds %llu, OS waits %llu\n" + "RW-excl spins %llu, rounds %llu, OS waits %llu\n", mutex_spin_wait_count, mutex_spin_round_count, mutex_os_wait_count, rw_s_spin_wait_count, + rw_s_spin_round_count, rw_s_os_wait_count, rw_x_spin_wait_count, + rw_x_spin_round_count, rw_x_os_wait_count); fprintf(file, From 5754382c31b85ab4bbcded9691efd7f3891daa17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Wed, 30 Jun 2010 12:31:49 +0300 Subject: [PATCH 049/115] Correct some comments that were added in the fix of Bug #54358 (READ UNCOMMITTED access failure of off-page DYNAMIC or COMPRESSED columns). Records that lack incompletely written externally stored columns may be accessed by READ UNCOMMITTED transaction even without involving a crash during an INSERT or UPDATE operation. I verified this as follows. (1) added a delay after the mini-transaction for writing the clustered index 'stub' record was committed (patch attached) (2) started mysqld in gdb, setting breakpoints to the where the assertions about READ UNCOMMITTED were added in the bug fix (3) invoked ibtest3 --create-options=key_block_size=2 to create BLOBs in a COMPRESSED table (4) invoked the following: yes 'set transaction isolation level read uncommitted; checksum table blobt3;select sleep(1);'|mysql -uroot test (5) noted that one of the breakpoints was triggered (return(NULL) in btr_rec_copy_externally_stored_field()) === modified file 'storage/innodb_plugin/row/row0ins.c' --- storage/innodb_plugin/row/row0ins.c 2010-06-30 08:17:25 +0000 +++ storage/innodb_plugin/row/row0ins.c 2010-06-30 08:17:25 +0000 @@ -2120,6 +2120,7 @@ function_exit: rec_t* rec; ulint* offsets; mtr_start(&mtr); + os_thread_sleep(5000000); btr_cur_search_to_nth_level(index, 0, entry, PAGE_CUR_LE, BTR_MODIFY_TREE, &cursor, 0, === modified file 'storage/innodb_plugin/row/row0upd.c' --- storage/innodb_plugin/row/row0upd.c 2010-06-30 08:11:55 +0000 +++ storage/innodb_plugin/row/row0upd.c 2010-06-30 08:11:55 +0000 @@ -1763,6 +1763,7 @@ row_upd_clust_rec( rec_offs_init(offsets_); mtr_start(mtr); + os_thread_sleep(5000000); ut_a(btr_pcur_restore_position(BTR_MODIFY_TREE, pcur, mtr)); rec = btr_cur_get_rec(btr_cur); --- storage/innodb_plugin/btr/btr0cur.c | 14 ++------ storage/innodb_plugin/row/row0sel.c | 53 +++++++++-------------------- 2 files changed, 19 insertions(+), 48 deletions(-) diff --git a/storage/innodb_plugin/btr/btr0cur.c b/storage/innodb_plugin/btr/btr0cur.c index 9b28f26f054..7fa7d42320a 100644 --- a/storage/innodb_plugin/btr/btr0cur.c +++ b/storage/innodb_plugin/btr/btr0cur.c @@ -4849,20 +4849,10 @@ btr_rec_copy_externally_stored_field( if (UNIV_UNLIKELY (!memcmp(data + local_len - BTR_EXTERN_FIELD_REF_SIZE, field_ref_zero, BTR_EXTERN_FIELD_REF_SIZE))) { - /* The externally stored field was not written - yet. This is only a valid condition when the server - crashed after the time a record stub was freshly - inserted but before all its columns were written. This - record should only be seen by + /* The externally stored field was not written yet. + This record should only be seen by recv_recovery_rollback_active() or any TRX_ISO_READ_UNCOMMITTED transactions. */ - - /* TODO: assert that there is an owner_trx with - owner_trx->id == DB_TRX_ID and owner_trx->is_recovered */ - - /* TODO: assert that for the current transaction trx, - either (trx == owner_trx && trx_is_recv(trx)) or - trx->isolation_level == TRX_ISO_READ_UNCOMMITTED. */ return(NULL); } diff --git a/storage/innodb_plugin/row/row0sel.c b/storage/innodb_plugin/row/row0sel.c index ab8c78e4f4a..d0c59862fce 100644 --- a/storage/innodb_plugin/row/row0sel.c +++ b/storage/innodb_plugin/row/row0sel.c @@ -427,11 +427,7 @@ row_sel_fetch_columns( /* data == NULL means that the externally stored field was not - written yet. This is only a valid - condition when the server crashed - after the time a record stub was - freshly inserted but before all its - columns were written. This record + written yet. This record should only be seen by recv_recovery_rollback_active() or any TRX_ISO_READ_UNCOMMITTED @@ -2744,12 +2740,7 @@ row_sel_store_mysql_rec( if (UNIV_UNLIKELY(!data)) { /* The externally stored field - was not written yet. This is - only a valid condition when - the server crashed after the - time a record stub was freshly - inserted but before all its - columns were written. This + was not written yet. This record should only be seen by recv_recovery_rollback_active() or any TRX_ISO_READ_UNCOMMITTED @@ -3623,8 +3614,7 @@ row_search_for_mysql( if (!row_sel_store_mysql_rec(buf, prebuilt, rec, offsets)) { - /* Only fresh inserts at - server crash time may contain + /* Only fresh inserts may contain incomplete externally stored columns. Pretend that such records do not exist. Such @@ -3636,10 +3626,6 @@ row_search_for_mysql( at a lower level, not here. */ ut_a(trx->isolation_level == TRX_ISO_READ_UNCOMMITTED); - /* TODO: assert that there is - an owner_trx with - owner_trx->id == DB_TRX_ID and - owner_trx->is_recovered */ err = DB_TOO_BIG_RECORD; @@ -4422,16 +4408,14 @@ requires_clust_rec: if (!row_sel_push_cache_row_for_mysql(prebuilt, result_rec, offsets)) { - /* Only fresh inserts at server crash time may contain - incomplete externally stored columns. Pretend that - such records do not exist. Such records may only be - accessed at the READ UNCOMMITTED isolation level or - when rolling back a recovered transaction. Rollback - happens at a lower level, not here. */ + /* Only fresh inserts may contain incomplete + externally stored columns. Pretend that such + records do not exist. Such records may only be + accessed at the READ UNCOMMITTED isolation + level or when rolling back a recovered + transaction. Rollback happens at a lower + level, not here. */ ut_a(trx->isolation_level == TRX_ISO_READ_UNCOMMITTED); - /* TODO: assert that there is an owner_trx - with owner_trx->id == DB_TRX_ID and - owner_trx->is_recovered */ } else if (prebuilt->n_fetch_cached == MYSQL_FETCH_CACHE_SIZE) { @@ -4449,19 +4433,16 @@ requires_clust_rec: } else { if (!row_sel_store_mysql_rec(buf, prebuilt, result_rec, offsets)) { - /* Only fresh inserts at server crash - time may contain incomplete externally - stored columns. Pretend that such - records do not exist. Such records may - only be accessed at the READ UNCOMMITTED + /* Only fresh inserts may contain + incomplete externally stored + columns. Pretend that such records do + not exist. Such records may only be + accessed at the READ UNCOMMITTED isolation level or when rolling back a - recovered transaction. Rollback happens - at a lower level, not here. */ + recovered transaction. Rollback + happens at a lower level, not here. */ ut_a(trx->isolation_level == TRX_ISO_READ_UNCOMMITTED); - /* TODO: assert that there is an owner_trx - with owner_trx->id == DB_TRX_ID and - owner_trx->is_recovered */ goto next_rec; } } From 81d3dadb540321a7d88f3bbce0b3a456f90328cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Wed, 30 Jun 2010 12:38:47 +0300 Subject: [PATCH 050/115] Bug#54358 follow-up: Correct some error handling. --- storage/innodb_plugin/row/row0sel.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/storage/innodb_plugin/row/row0sel.c b/storage/innodb_plugin/row/row0sel.c index d0c59862fce..76c144e5a8c 100644 --- a/storage/innodb_plugin/row/row0sel.c +++ b/storage/innodb_plugin/row/row0sel.c @@ -3627,11 +3627,8 @@ row_search_for_mysql( ut_a(trx->isolation_level == TRX_ISO_READ_UNCOMMITTED); - err = DB_TOO_BIG_RECORD; - - /* We let the main loop to do the - error handling */ - goto shortcut_fails_too_big_rec; + /* Proceed as in case SEL_RETRY. */ + break; } mtr_commit(&mtr); @@ -3671,7 +3668,7 @@ release_search_latch_if_needed: default: ut_ad(0); } -shortcut_fails_too_big_rec: + mtr_commit(&mtr); mtr_start(&mtr); } From fd7a1118cfe1d7c5b589a8638747b65bd31b823b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Wed, 30 Jun 2010 12:52:41 +0300 Subject: [PATCH 051/115] =?UTF-8?q?Merge=20Bug=20#54358=20comment=20correc?= =?UTF-8?q?tions=20from=20mysql-5.1-innodb:=20----------------------------?= =?UTF-8?q?--------------------------------=20revno:=203533=20revision-id:?= =?UTF-8?q?=20marko.makela@oracle.com-20100630093149-wmc37t128gic933v=20pa?= =?UTF-8?q?rent:=20marko.makela@oracle.com-20100629131219-pjbkpk5rsqztmw27?= =?UTF-8?q?=20committer:=20Marko=20M=C3=A4kel=C3=A4=20=20branch=20nick:=205.1-innodb=20timestamp:=20Wed=202010-0?= =?UTF-8?q?6-30=2012:31:49=20+0300=20message:=20=20=20Correct=20some=20com?= =?UTF-8?q?ments=20that=20were=20added=20in=20the=20fix=20of=20Bug=20#5435?= =?UTF-8?q?8=20=20=20(READ=20UNCOMMITTED=20access=20failure=20of=20off-pag?= =?UTF-8?q?e=20DYNAMIC=20or=20COMPRESSED=20columns).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Records that lack incompletely written externally stored columns may be accessed by READ UNCOMMITTED transaction even without involving a crash during an INSERT or UPDATE operation. I verified this as follows. (1) added a delay after the mini-transaction for writing the clustered index 'stub' record was committed (patch attached) (2) started mysqld in gdb, setting breakpoints to the where the assertions about READ UNCOMMITTED were added in the bug fix (3) invoked ibtest3 --create-options=key_block_size=2 to create BLOBs in a COMPRESSED table (4) invoked the following: yes 'set transaction isolation level read uncommitted; checksum table blobt3;select sleep(1);'|mysql -uroot test (5) noted that one of the breakpoints was triggered (return(NULL) in btr_rec_copy_externally_stored_field()) === modified file 'storage/innodb_plugin/row/row0ins.c' --- storage/innodb_plugin/row/row0ins.c 2010-06-30 08:17:25 +0000 +++ storage/innodb_plugin/row/row0ins.c 2010-06-30 08:17:25 +0000 @@ -2120,6 +2120,7 @@ function_exit: rec_t* rec; ulint* offsets; mtr_start(&mtr); + os_thread_sleep(5000000); btr_cur_search_to_nth_level(index, 0, entry, PAGE_CUR_LE, BTR_MODIFY_TREE, &cursor, 0, === modified file 'storage/innodb_plugin/row/row0upd.c' --- storage/innodb_plugin/row/row0upd.c 2010-06-30 08:11:55 +0000 +++ storage/innodb_plugin/row/row0upd.c 2010-06-30 08:11:55 +0000 @@ -1763,6 +1763,7 @@ row_upd_clust_rec( rec_offs_init(offsets_); mtr_start(mtr); + os_thread_sleep(5000000); ut_a(btr_pcur_restore_position(BTR_MODIFY_TREE, pcur, mtr)); rec = btr_cur_get_rec(btr_cur); --- storage/innobase/btr/btr0cur.c | 14 ++------- storage/innobase/row/row0sel.c | 53 +++++++++++----------------------- 2 files changed, 19 insertions(+), 48 deletions(-) diff --git a/storage/innobase/btr/btr0cur.c b/storage/innobase/btr/btr0cur.c index 90dfba7e151..537d5f51184 100644 --- a/storage/innobase/btr/btr0cur.c +++ b/storage/innobase/btr/btr0cur.c @@ -4969,20 +4969,10 @@ btr_rec_copy_externally_stored_field( if (UNIV_UNLIKELY (!memcmp(data + local_len - BTR_EXTERN_FIELD_REF_SIZE, field_ref_zero, BTR_EXTERN_FIELD_REF_SIZE))) { - /* The externally stored field was not written - yet. This is only a valid condition when the server - crashed after the time a record stub was freshly - inserted but before all its columns were written. This - record should only be seen by + /* The externally stored field was not written yet. + This record should only be seen by recv_recovery_rollback_active() or any TRX_ISO_READ_UNCOMMITTED transactions. */ - - /* TODO: assert that there is an owner_trx with - owner_trx->id == DB_TRX_ID and owner_trx->is_recovered */ - - /* TODO: assert that for the current transaction trx, - either (trx == owner_trx && trx_is_recv(trx)) or - trx->isolation_level == TRX_ISO_READ_UNCOMMITTED. */ return(NULL); } diff --git a/storage/innobase/row/row0sel.c b/storage/innobase/row/row0sel.c index 13c867dc4c3..3d435965949 100644 --- a/storage/innobase/row/row0sel.c +++ b/storage/innobase/row/row0sel.c @@ -425,11 +425,7 @@ row_sel_fetch_columns( /* data == NULL means that the externally stored field was not - written yet. This is only a valid - condition when the server crashed - after the time a record stub was - freshly inserted but before all its - columns were written. This record + written yet. This record should only be seen by recv_recovery_rollback_active() or any TRX_ISO_READ_UNCOMMITTED @@ -2736,12 +2732,7 @@ row_sel_store_mysql_rec( if (UNIV_UNLIKELY(!data)) { /* The externally stored field - was not written yet. This is - only a valid condition when - the server crashed after the - time a record stub was freshly - inserted but before all its - columns were written. This + was not written yet. This record should only be seen by recv_recovery_rollback_active() or any TRX_ISO_READ_UNCOMMITTED @@ -3615,8 +3606,7 @@ row_search_for_mysql( if (!row_sel_store_mysql_rec(buf, prebuilt, rec, offsets)) { - /* Only fresh inserts at - server crash time may contain + /* Only fresh inserts may contain incomplete externally stored columns. Pretend that such records do not exist. Such @@ -3628,10 +3618,6 @@ row_search_for_mysql( at a lower level, not here. */ ut_a(trx->isolation_level == TRX_ISO_READ_UNCOMMITTED); - /* TODO: assert that there is - an owner_trx with - owner_trx->id == DB_TRX_ID and - owner_trx->is_recovered */ err = DB_TOO_BIG_RECORD; @@ -4414,16 +4400,14 @@ requires_clust_rec: if (!row_sel_push_cache_row_for_mysql(prebuilt, result_rec, offsets)) { - /* Only fresh inserts at server crash time may contain - incomplete externally stored columns. Pretend that - such records do not exist. Such records may only be - accessed at the READ UNCOMMITTED isolation level or - when rolling back a recovered transaction. Rollback - happens at a lower level, not here. */ + /* Only fresh inserts may contain incomplete + externally stored columns. Pretend that such + records do not exist. Such records may only be + accessed at the READ UNCOMMITTED isolation + level or when rolling back a recovered + transaction. Rollback happens at a lower + level, not here. */ ut_a(trx->isolation_level == TRX_ISO_READ_UNCOMMITTED); - /* TODO: assert that there is an owner_trx - with owner_trx->id == DB_TRX_ID and - owner_trx->is_recovered */ } else if (prebuilt->n_fetch_cached == MYSQL_FETCH_CACHE_SIZE) { @@ -4441,19 +4425,16 @@ requires_clust_rec: } else { if (!row_sel_store_mysql_rec(buf, prebuilt, result_rec, offsets)) { - /* Only fresh inserts at server crash - time may contain incomplete externally - stored columns. Pretend that such - records do not exist. Such records may - only be accessed at the READ UNCOMMITTED + /* Only fresh inserts may contain + incomplete externally stored + columns. Pretend that such records do + not exist. Such records may only be + accessed at the READ UNCOMMITTED isolation level or when rolling back a - recovered transaction. Rollback happens - at a lower level, not here. */ + recovered transaction. Rollback + happens at a lower level, not here. */ ut_a(trx->isolation_level == TRX_ISO_READ_UNCOMMITTED); - /* TODO: assert that there is an owner_trx - with owner_trx->id == DB_TRX_ID and - owner_trx->is_recovered */ goto next_rec; } } From 44e4ead2d07c9038929ceb8660b358a07c08f16d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Wed, 30 Jun 2010 12:55:10 +0300 Subject: [PATCH 052/115] =?UTF-8?q?Merge=20a=20Bug=20#54358=20error=20hand?= =?UTF-8?q?ling=20correction=20from=20mysql-5.1-innodb:=20----------------?= =?UTF-8?q?--------------------------------------------=20revno:=203534=20?= =?UTF-8?q?revision-id:=20marko.makela@oracle.com-20100630093847-7gkr1lh3b?= =?UTF-8?q?h2xksy0=20parent:=20marko.makela@oracle.com-20100630093149-wmc3?= =?UTF-8?q?7t128gic933v=20committer:=20Marko=20M=C3=A4kel=C3=A4=20=20branch=20nick:=205.1-innodb=20timestamp:=20?= =?UTF-8?q?Wed=202010-06-30=2012:38:47=20+0300=20message:=20=20=20Bug#5435?= =?UTF-8?q?8=20follow-up:=20Correct=20some=20error=20handling.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- storage/innobase/row/row0sel.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/storage/innobase/row/row0sel.c b/storage/innobase/row/row0sel.c index 3d435965949..39ab2179740 100644 --- a/storage/innobase/row/row0sel.c +++ b/storage/innobase/row/row0sel.c @@ -3619,11 +3619,8 @@ row_search_for_mysql( ut_a(trx->isolation_level == TRX_ISO_READ_UNCOMMITTED); - err = DB_TOO_BIG_RECORD; - - /* We let the main loop to do the - error handling */ - goto shortcut_fails_too_big_rec; + /* Proceed as in case SEL_RETRY. */ + break; } mtr_commit(&mtr); @@ -3663,7 +3660,7 @@ release_search_latch_if_needed: default: ut_ad(0); } -shortcut_fails_too_big_rec: + mtr_commit(&mtr); mtr_start(&mtr); } From 34e409fafa8e5a413f9acbb30ca72a0480e83ed8 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Wed, 30 Jun 2010 13:37:03 +0200 Subject: [PATCH 053/115] Temporary fix for bug#54835 MTR version 1 cannot start server - bootstrap reports unknown InnoDB engine Removes --loose-skip-innodb from the list of server startup options. --- mysql-test/lib/v1/mysql-test-run.pl | 1 - 1 file changed, 1 deletion(-) diff --git a/mysql-test/lib/v1/mysql-test-run.pl b/mysql-test/lib/v1/mysql-test-run.pl index 64d7376605e..36acea3ab6d 100755 --- a/mysql-test/lib/v1/mysql-test-run.pl +++ b/mysql-test/lib/v1/mysql-test-run.pl @@ -3114,7 +3114,6 @@ sub install_db ($$) { mtr_add_arg($args, "--bootstrap"); mtr_add_arg($args, "--basedir=%s", $path_my_basedir); mtr_add_arg($args, "--datadir=%s", $data_dir); - mtr_add_arg($args, "--loose-skip-innodb"); mtr_add_arg($args, "--loose-skip-ndbcluster"); mtr_add_arg($args, "--tmpdir=."); mtr_add_arg($args, "--core-file"); From 3ff4ccd14b44120be231a213855f780f4bfd491a Mon Sep 17 00:00:00 2001 From: Jimmy Yang Date: Wed, 30 Jun 2010 21:52:47 -0700 Subject: [PATCH 054/115] Fix bug #54311 Crash on CHECK PARTITION after concurrent LOAD DATA and adaptive_hash_index=OFF rb://389 approved by Marko --- storage/innobase/btr/btr0sea.c | 11 ++++++++++- storage/innobase/ha/ha0ha.c | 15 +++++++++++---- storage/innobase/handler/ha_innodb.cc | 1 + storage/innobase/include/btr0sea.h | 8 +++++++- 4 files changed, 29 insertions(+), 6 deletions(-) diff --git a/storage/innobase/btr/btr0sea.c b/storage/innobase/btr/btr0sea.c index 75005dddb87..06cc48c7c60 100644 --- a/storage/innobase/btr/btr0sea.c +++ b/storage/innobase/btr/btr0sea.c @@ -46,6 +46,7 @@ Created 2/17/1996 Heikki Tuuri /** Flag: has the search system been enabled? Protected by btr_search_latch and btr_search_enabled_mutex. */ UNIV_INTERN char btr_search_enabled = TRUE; +UNIV_INTERN ibool btr_search_fully_disabled = FALSE; /** Mutex protecting btr_search_enabled */ static mutex_t btr_search_enabled_mutex; @@ -213,12 +214,19 @@ btr_search_disable(void) mutex_enter(&btr_search_enabled_mutex); rw_lock_x_lock(&btr_search_latch); + /* Disable access to hash index, also tell ha_insert_for_fold() + stop adding new nodes to hash index, but still allow updating + existing nodes */ btr_search_enabled = FALSE; /* Clear all block->is_hashed flags and remove all entries from btr_search_sys->hash_index. */ buf_pool_drop_hash_index(); + /* hash index has been cleaned up, disallow any operation to + the hash index */ + btr_search_fully_disabled = TRUE; + /* btr_search_enabled_mutex should guarantee this. */ ut_ad(!btr_search_enabled); @@ -237,6 +245,7 @@ btr_search_enable(void) rw_lock_x_lock(&btr_search_latch); btr_search_enabled = TRUE; + btr_search_fully_disabled = FALSE; rw_lock_x_unlock(&btr_search_latch); mutex_exit(&btr_search_enabled_mutex); @@ -1375,7 +1384,7 @@ btr_search_build_page_hash_index( rw_lock_x_lock(&btr_search_latch); - if (UNIV_UNLIKELY(!btr_search_enabled)) { + if (UNIV_UNLIKELY(btr_search_fully_disabled)) { goto exit_func; } diff --git a/storage/innobase/ha/ha0ha.c b/storage/innobase/ha/ha0ha.c index 9d9d341ad39..f9e798012f8 100644 --- a/storage/innobase/ha/ha0ha.c +++ b/storage/innobase/ha/ha0ha.c @@ -31,9 +31,7 @@ Created 8/22/1994 Heikki Tuuri #ifdef UNIV_DEBUG # include "buf0buf.h" #endif /* UNIV_DEBUG */ -#ifdef UNIV_SYNC_DEBUG -# include "btr0sea.h" -#endif /* UNIV_SYNC_DEBUG */ +#include "btr0sea.h" #include "page0page.h" /*************************************************************//** @@ -127,7 +125,8 @@ ha_clear( /*************************************************************//** Inserts an entry into a hash table. If an entry with the same fold number is found, its node is updated to point to the new data, and no new node -is inserted. +is inserted. If btr_search_enabled is set to FALSE, we will only allow +updating existing nodes, but no new node is allowed to be added. @return TRUE if succeed, FALSE if no more memory could be allocated */ UNIV_INTERN ibool @@ -174,6 +173,7 @@ ha_insert_for_fold_func( prev_block->n_pointers--; block->n_pointers++; } + ut_ad(!btr_search_fully_disabled); # endif /* !UNIV_HOTBACKUP */ prev_node->block = block; @@ -186,6 +186,13 @@ ha_insert_for_fold_func( prev_node = prev_node->next; } + /* We are in the process of disabling hash index, do not add + new chain node */ + if (!btr_search_enabled) { + ut_ad(!btr_search_fully_disabled); + return(TRUE); + } + /* We have to allocate a new chain node */ node = mem_heap_alloc(hash_get_heap(table, fold), sizeof(ha_node_t)); diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 7cead57be62..3bb2fc778cc 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -2451,6 +2451,7 @@ innobase_change_buffering_inited_ok: /* Get the current high water mark format. */ innobase_file_format_max = (char*) trx_sys_file_format_max_get(); + btr_search_fully_disabled = (!btr_search_enabled); DBUG_RETURN(FALSE); error: DBUG_RETURN(TRUE); diff --git a/storage/innobase/include/btr0sea.h b/storage/innobase/include/btr0sea.h index f98ba386f9c..20a2be7f877 100644 --- a/storage/innobase/include/btr0sea.h +++ b/storage/innobase/include/btr0sea.h @@ -190,7 +190,13 @@ btr_search_validate(void); /** Flag: has the search system been enabled? Protected by btr_search_latch and btr_search_enabled_mutex. */ -extern char btr_search_enabled; +extern char btr_search_enabled; + +/** Flag: whether the search system has completed its disabling process, +It is set to TRUE right after buf_pool_drop_hash_index() in +btr_search_disable(), indicating hash index entries are cleaned up. +Protected by btr_search_latch and btr_search_enabled_mutex. */ +extern ibool btr_search_fully_disabled; /** The search info struct in an index */ struct btr_search_struct{ From 0d328677636fecd29af032c59309a36c98981381 Mon Sep 17 00:00:00 2001 From: Jimmy Yang Date: Wed, 30 Jun 2010 22:06:01 -0700 Subject: [PATCH 055/115] Port fix for bug #54311 from mysql-trunk-innodb to mysql-5.1-innodb codeline. Bug #54311: Crash on CHECK PARTITION after concurrent LOAD DATA and adaptive_hash_index=OFF --- storage/innodb_plugin/ChangeLog | 6 ++++++ storage/innodb_plugin/btr/btr0sea.c | 11 ++++++++++- storage/innodb_plugin/ha/ha0ha.c | 15 +++++++++++---- storage/innodb_plugin/handler/ha_innodb.cc | 1 + storage/innodb_plugin/include/btr0sea.h | 8 +++++++- 5 files changed, 35 insertions(+), 6 deletions(-) diff --git a/storage/innodb_plugin/ChangeLog b/storage/innodb_plugin/ChangeLog index f7e9a3df943..a49a6b54a39 100644 --- a/storage/innodb_plugin/ChangeLog +++ b/storage/innodb_plugin/ChangeLog @@ -1,3 +1,9 @@ +2010-06-30 The InnoDB Team + + * btr/btr0sea.c, ha/ha0ha.c, handler/ha_innodb.cc, include/btr0sea.h: + Fix Bug#54311 Crash on CHECK PARTITION after concurrent LOAD DATA + and adaptive_hash_index=OFF + 2010-06-29 The InnoDB Team * row/row0row.c, row/row0undo.c, row/row0upd.c: Fix Bug#54408 txn rollback after recovery: row0umod.c:673 diff --git a/storage/innodb_plugin/btr/btr0sea.c b/storage/innodb_plugin/btr/btr0sea.c index c91afa31bf0..ac7248fef20 100644 --- a/storage/innodb_plugin/btr/btr0sea.c +++ b/storage/innodb_plugin/btr/btr0sea.c @@ -46,6 +46,7 @@ Created 2/17/1996 Heikki Tuuri /** Flag: has the search system been enabled? Protected by btr_search_latch and btr_search_enabled_mutex. */ UNIV_INTERN char btr_search_enabled = TRUE; +UNIV_INTERN ibool btr_search_fully_disabled = FALSE; /** Mutex protecting btr_search_enabled */ static mutex_t btr_search_enabled_mutex; @@ -201,12 +202,19 @@ btr_search_disable(void) mutex_enter(&btr_search_enabled_mutex); rw_lock_x_lock(&btr_search_latch); + /* Disable access to hash index, also tell ha_insert_for_fold() + stop adding new nodes to hash index, but still allow updating + existing nodes */ btr_search_enabled = FALSE; /* Clear all block->is_hashed flags and remove all entries from btr_search_sys->hash_index. */ buf_pool_drop_hash_index(); + /* hash index has been cleaned up, disallow any operation to + the hash index */ + btr_search_fully_disabled = TRUE; + /* btr_search_enabled_mutex should guarantee this. */ ut_ad(!btr_search_enabled); @@ -225,6 +233,7 @@ btr_search_enable(void) rw_lock_x_lock(&btr_search_latch); btr_search_enabled = TRUE; + btr_search_fully_disabled = FALSE; rw_lock_x_unlock(&btr_search_latch); mutex_exit(&btr_search_enabled_mutex); @@ -1363,7 +1372,7 @@ btr_search_build_page_hash_index( rw_lock_x_lock(&btr_search_latch); - if (UNIV_UNLIKELY(!btr_search_enabled)) { + if (UNIV_UNLIKELY(btr_search_fully_disabled)) { goto exit_func; } diff --git a/storage/innodb_plugin/ha/ha0ha.c b/storage/innodb_plugin/ha/ha0ha.c index 9d9d341ad39..f9e798012f8 100644 --- a/storage/innodb_plugin/ha/ha0ha.c +++ b/storage/innodb_plugin/ha/ha0ha.c @@ -31,9 +31,7 @@ Created 8/22/1994 Heikki Tuuri #ifdef UNIV_DEBUG # include "buf0buf.h" #endif /* UNIV_DEBUG */ -#ifdef UNIV_SYNC_DEBUG -# include "btr0sea.h" -#endif /* UNIV_SYNC_DEBUG */ +#include "btr0sea.h" #include "page0page.h" /*************************************************************//** @@ -127,7 +125,8 @@ ha_clear( /*************************************************************//** Inserts an entry into a hash table. If an entry with the same fold number is found, its node is updated to point to the new data, and no new node -is inserted. +is inserted. If btr_search_enabled is set to FALSE, we will only allow +updating existing nodes, but no new node is allowed to be added. @return TRUE if succeed, FALSE if no more memory could be allocated */ UNIV_INTERN ibool @@ -174,6 +173,7 @@ ha_insert_for_fold_func( prev_block->n_pointers--; block->n_pointers++; } + ut_ad(!btr_search_fully_disabled); # endif /* !UNIV_HOTBACKUP */ prev_node->block = block; @@ -186,6 +186,13 @@ ha_insert_for_fold_func( prev_node = prev_node->next; } + /* We are in the process of disabling hash index, do not add + new chain node */ + if (!btr_search_enabled) { + ut_ad(!btr_search_fully_disabled); + return(TRUE); + } + /* We have to allocate a new chain node */ node = mem_heap_alloc(hash_get_heap(table, fold), sizeof(ha_node_t)); diff --git a/storage/innodb_plugin/handler/ha_innodb.cc b/storage/innodb_plugin/handler/ha_innodb.cc index aa80814dbe0..c6042815e6c 100644 --- a/storage/innodb_plugin/handler/ha_innodb.cc +++ b/storage/innodb_plugin/handler/ha_innodb.cc @@ -2270,6 +2270,7 @@ innobase_change_buffering_inited_ok: /* Get the current high water mark format. */ innobase_file_format_check = (char*) trx_sys_file_format_max_get(); + btr_search_fully_disabled = (!btr_search_enabled); DBUG_RETURN(FALSE); error: DBUG_RETURN(TRUE); diff --git a/storage/innodb_plugin/include/btr0sea.h b/storage/innodb_plugin/include/btr0sea.h index f98ba386f9c..20a2be7f877 100644 --- a/storage/innodb_plugin/include/btr0sea.h +++ b/storage/innodb_plugin/include/btr0sea.h @@ -190,7 +190,13 @@ btr_search_validate(void); /** Flag: has the search system been enabled? Protected by btr_search_latch and btr_search_enabled_mutex. */ -extern char btr_search_enabled; +extern char btr_search_enabled; + +/** Flag: whether the search system has completed its disabling process, +It is set to TRUE right after buf_pool_drop_hash_index() in +btr_search_disable(), indicating hash index entries are cleaned up. +Protected by btr_search_latch and btr_search_enabled_mutex. */ +extern ibool btr_search_fully_disabled; /** The search info struct in an index */ struct btr_search_struct{ From 41a3dfe49093e41806a0f3b36dbb654ef1b8f29a Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Thu, 1 Jul 2010 15:53:46 +0200 Subject: [PATCH 056/115] A 5.5 version of the fix for Bug #54360 "Deadlock DROP/ALTER/CREATE DATABASE with open HANDLER" Remove LOCK_create_db, database name locks, and use metadata locks instead. This exposes CREATE/DROP/ALTER DATABASE statements to the graph-based deadlock detector in MDL, and paves the way for a safe, deadlock-free implementation of RENAME DATABASE. Database DDL statements will now take exclusive metadata locks on the database name, while table/view/routine DDL statements take intention exclusive locks on the database name. This prevents race conditions between database DDL and table/view/routine DDL. (e.g. DROP DATABASE with concurrent CREATE/ALTER/DROP TABLE) By adding database name locks, this patch implements WL#4450 "DDL locking: CREATE/DROP DATABASE must use database locks" and WL#4985 "DDL locking: namespace/hierarchical locks". The patch also changes code to use init_one_table() where appropriate. The new lock_table_names() function requires TABLE_LIST::db_length to be set correctly, and this is taken care of by init_one_table(). This patch also adds a simple template to help work with the mysys HASH data structure. Most of the patch was written by Konstantin Osipov. --- mysql-test/r/mdl_sync.result | 237 +++++++++ mysql-test/r/partition_debug_sync.result | 2 +- mysql-test/r/schema.result | 50 +- .../suite/perfschema/r/server_init.result | 8 - .../suite/perfschema/t/server_init.test | 6 - mysql-test/t/mdl_sync.test | 475 ++++++++++++++++++ mysql-test/t/partition_debug_sync.test | 2 +- mysql-test/t/schema.test | 98 +++- sql/lock.cc | 67 ++- sql/lock.h | 3 +- sql/mdl.cc | 76 +-- sql/mdl.h | 10 +- sql/mysqld.cc | 21 +- sql/mysqld.h | 6 +- sql/sql_acl.cc | 97 ++-- sql/sql_base.cc | 167 +++--- sql/sql_base.h | 10 +- sql/sql_db.cc | 320 ++---------- sql/sql_db.h | 7 +- sql/sql_help.cc | 23 +- sql/sql_hset.h | 117 +++++ sql/sql_parse.cc | 37 +- sql/sql_rename.cc | 10 +- sql/sql_table.cc | 49 +- sql/sql_table.h | 1 - sql/sql_truncate.cc | 42 +- sql/sql_view.cc | 7 +- sql/sql_yacc.yy | 4 +- 28 files changed, 1300 insertions(+), 652 deletions(-) create mode 100644 sql/sql_hset.h diff --git a/mysql-test/r/mdl_sync.result b/mysql-test/r/mdl_sync.result index 67d778211dd..1b0ffc588e1 100644 --- a/mysql-test/r/mdl_sync.result +++ b/mysql-test/r/mdl_sync.result @@ -2527,3 +2527,240 @@ SET DEBUG_SYNC= "now SIGNAL completed"; Field Type Collation Null Key Default Extra Privileges Comment a char(255) latin1_swedish_ci YES NULL # DROP TABLE t1; +# +# Tests for schema-scope locks +# +DROP DATABASE IF EXISTS db1; +DROP DATABASE IF EXISTS db2; +# Test 1: +# CREATE DATABASE blocks database DDL on the same database, but +# not database DDL on different databases. Tests X vs X lock. +# +# Connection default +SET DEBUG_SYNC= 'after_wait_locked_schema_name SIGNAL locked WAIT_FOR blocked'; +# Sending: +CREATE DATABASE db1; +# Connection con2 +SET DEBUG_SYNC= 'now WAIT_FOR locked'; +# Sending: +CREATE DATABASE db1; +# Connection con3 +CREATE DATABASE db2; +ALTER DATABASE db2 DEFAULT CHARACTER SET utf8; +DROP DATABASE db2; +SET DEBUG_SYNC= 'now SIGNAL blocked'; +# Connection default +# Reaping: CREATE DATABASE db1 +# Connection con2 +# Reaping: CREATE DATABASE db1 +ERROR HY000: Can't create database 'db1'; database exists +# Test 2: +# ALTER DATABASE blocks database DDL on the same database, but +# not database DDL on different databases. Tests X vs X lock. +# +# Connection default +SET DEBUG_SYNC= 'after_wait_locked_schema_name SIGNAL locked WAIT_FOR blocked'; +# Sending: +ALTER DATABASE db1 DEFAULT CHARACTER SET utf8; +# Connection con2 +SET DEBUG_SYNC= 'now WAIT_FOR locked'; +# Sending: +ALTER DATABASE db1 DEFAULT CHARACTER SET utf8; +# Connection con3 +CREATE DATABASE db2; +ALTER DATABASE db2 DEFAULT CHARACTER SET utf8; +DROP DATABASE db2; +SET DEBUG_SYNC= 'now SIGNAL blocked'; +# Connection default +# Reaping: ALTER DATABASE db1 DEFAULT CHARACTER SET utf8 +# Connection con2 +# Reaping: ALTER DATABASE db1 DEFAULT CHARACTER SET utf8 +# Connection default +SET DEBUG_SYNC= 'after_wait_locked_schema_name SIGNAL locked WAIT_FOR blocked'; +# Sending: +ALTER DATABASE db1 DEFAULT CHARACTER SET utf8; +# Connection con2 +SET DEBUG_SYNC= 'now WAIT_FOR locked'; +# Sending: +DROP DATABASE db1; +# Connection con3 +SET DEBUG_SYNC= 'now SIGNAL blocked'; +# Connection default +# Reaping: ALTER DATABASE db1 DEFAULT CHARACTER SET utf8 +# Connection con2 +# Reaping: DROP DATABASE db1 +CREATE DATABASE db1; +# Test 3: +# Two ALTER..UPGRADE of the same database are mutually exclusive, but +# two ALTER..UPGRADE of different databases are not. Tests X vs X lock. +# +# Connection default +SET DEBUG_SYNC= 'after_wait_locked_schema_name SIGNAL locked WAIT_FOR blocked'; +# Sending: +ALTER DATABASE `#mysql50#a-b-c` UPGRADE DATA DIRECTORY NAME; +# Connection con2 +SET DEBUG_SYNC= 'now WAIT_FOR locked'; +# Sending: +ALTER DATABASE `#mysql50#a-b-c` UPGRADE DATA DIRECTORY NAME; +# Connection con3 +ALTER DATABASE `#mysql50#a-b-c-d` UPGRADE DATA DIRECTORY NAME; +SET DEBUG_SYNC= 'now SIGNAL blocked'; +# Connection default +# Reaping: ALTER DATABASE '#mysql50#a-b-c' UPGRADE DATA DIRECTORY NAME +# Connection con2 +# Reaping: ALTER DATABASE '#mysql50#a-b-c' UPGRADE DATA DIRECTORY NAME +ERROR 42000: Unknown database '#mysql50#a-b-c' +DROP DATABASE `a-b-c`; +DROP DATABASE `a-b-c-d`; +# Test 4: +# DROP DATABASE blocks database DDL on the same database, but +# not database DDL on different databases. Tests X vs X lock. +# +# Connection default +SET DEBUG_SYNC= 'after_wait_locked_schema_name SIGNAL locked WAIT_FOR blocked'; +# Sending: +DROP DATABASE db1; +# Connection con2 +SET DEBUG_SYNC= 'now WAIT_FOR locked'; +# Sending: +DROP DATABASE db1; +# Connection con3 +CREATE DATABASE db2; +ALTER DATABASE db2 DEFAULT CHARACTER SET utf8; +DROP DATABASE db2; +SET DEBUG_SYNC= 'now SIGNAL blocked'; +# Connection default +# Reaping: DROP DATABASE db1 +# Connection con2 +# Reaping: DROP DATABASE db1 +ERROR HY000: Can't drop database 'db1'; database doesn't exist +# Connection default +CREATE DATABASE db1; +SET DEBUG_SYNC= 'after_wait_locked_schema_name SIGNAL locked WAIT_FOR blocked'; +# Sending: +DROP DATABASE db1; +# Connection con2 +SET DEBUG_SYNC= 'now WAIT_FOR locked'; +# Sending: +ALTER DATABASE db1 DEFAULT CHARACTER SET utf8; +# Connection con3 +SET DEBUG_SYNC= 'now SIGNAL blocked'; +# Connection default +# Reaping: DROP DATABASE db1 +# Connection con2 +# Reaping: ALTER DATABASE db1 DEFAULT CHARACTER SET utf8 +ERROR HY000: Can't create/write to file './db1/db.opt' (Errcode: 2) +# Test 5: +# Locked database name prevents CREATE of tables in that database. +# Tests X vs IX lock. +# +# Connection default +CREATE DATABASE db1; +SET DEBUG_SYNC= 'after_wait_locked_schema_name SIGNAL locked WAIT_FOR blocked'; +# Sending: +DROP DATABASE db1; +# Connection con2 +SET DEBUG_SYNC= 'now WAIT_FOR locked'; +# Sending: +CREATE TABLE db1.t1 (a INT); +# Connection con3 +SET DEBUG_SYNC= 'now SIGNAL blocked'; +# Connection default +# Reaping: DROP DATABASE db1 +# Connection con2 +# Reaping: CREATE TABLE db1.t1 (a INT) +ERROR 42000: Unknown database 'db1' +# Test 6: +# Locked database name prevents RENAME of tables to/from that database. +# Tests X vs IX lock. +# +# Connection default +CREATE DATABASE db1; +CREATE TABLE db1.t1 (a INT); +SET DEBUG_SYNC= 'after_wait_locked_schema_name SIGNAL locked WAIT_FOR blocked'; +# Sending: +DROP DATABASE db1; +# Connection con2 +SET DEBUG_SYNC= 'now WAIT_FOR locked'; +# Sending: +RENAME TABLE db1.t1 TO test.t1; +# Connection con3 +SET DEBUG_SYNC= 'now SIGNAL blocked'; +# Connection default +# Reaping: DROP DATABASE db1 +# Connection con2 +# Reaping: RENAME TABLE db1.t1 TO test.t1 +ERROR HY000: Can't find file: './db1/t1.frm' (errno: 2) +# Connection default +CREATE DATABASE db1; +CREATE TABLE test.t2 (a INT); +SET DEBUG_SYNC= 'after_wait_locked_schema_name SIGNAL locked WAIT_FOR blocked'; +# Sending: +DROP DATABASE db1; +# Connection con2 +SET DEBUG_SYNC= 'now WAIT_FOR locked'; +# Sending: +RENAME TABLE test.t2 TO db1.t2; +# Connection con3 +SET DEBUG_SYNC= 'now SIGNAL blocked'; +# Connection default +# Reaping: DROP DATABASE db1 +# Connection con2 +# Reaping: RENAME TABLE test.t2 TO db1.t2 +ERROR HY000: Error on rename of './test/t2.MYI' to './db1/t2.MYI' (Errcode: 2) +DROP TABLE test.t2; +# Test 7: +# Locked database name prevents DROP of tables in that database. +# Tests X vs IX lock. +# +# Connection default +CREATE DATABASE db1; +CREATE TABLE db1.t1 (a INT); +SET DEBUG_SYNC= 'after_wait_locked_schema_name SIGNAL locked WAIT_FOR blocked'; +# Sending: +DROP DATABASE db1; +# Connection con2 +SET DEBUG_SYNC= 'now WAIT_FOR locked'; +# Sending: +DROP TABLE db1.t1; +# Connection con3 +SET DEBUG_SYNC= 'now SIGNAL blocked'; +# Connection default +# Reaping: DROP DATABASE db1 +# Connection con2 +# Reaping: DROP TABLE db1.t1 +ERROR 42S02: Unknown table 't1' +# Connection default +SET DEBUG_SYNC= 'RESET'; +# +# End of tests for schema-scope locks +# +# +# Tests of granted global S lock (FLUSH TABLE WITH READ LOCK) +# +CREATE DATABASE db1; +CREATE TABLE db1.t1(a INT); +# Connection default +FLUSH TABLE WITH READ LOCK; +# Connection con2 +CREATE TABLE db1.t2(a INT); +# Connection default +UNLOCK TABLES; +# Connection con2 +# Reaping CREATE TABLE db1.t2(a INT) +# Connection default +FLUSH TABLE WITH READ LOCK; +# Connection con2 +ALTER DATABASE db1 DEFAULT CHARACTER SET utf8; +# Connection default +UNLOCK TABLES; +# Connection con2 +# Reaping ALTER DATABASE db1 DEFAULT CHARACTER SET utf8 +# Connection default +FLUSH TABLE WITH READ LOCK; +# Connection con2 +FLUSH TABLE WITH READ LOCK; +UNLOCK TABLES; +# Connection default +UNLOCK TABLES; +DROP DATABASE db1; diff --git a/mysql-test/r/partition_debug_sync.result b/mysql-test/r/partition_debug_sync.result index 5ab1044934b..0e3241cf88e 100644 --- a/mysql-test/r/partition_debug_sync.result +++ b/mysql-test/r/partition_debug_sync.result @@ -47,7 +47,7 @@ ENGINE = MYISAM PARTITION p1 VALUES LESS THAN (20), PARTITION p2 VALUES LESS THAN (100), PARTITION p3 VALUES LESS THAN MAXVALUE ) */; -SET DEBUG_SYNC= 'open_tables_acquire_upgradable_mdl SIGNAL removing_partitions WAIT_FOR waiting_for_alter'; +SET DEBUG_SYNC= 'alter_table_before_open_tables SIGNAL removing_partitions WAIT_FOR waiting_for_alter'; SET DEBUG_SYNC= 'alter_table_before_rename_result_table WAIT_FOR delete_done'; ALTER TABLE t2 REMOVE PARTITIONING; # Con default diff --git a/mysql-test/r/schema.result b/mysql-test/r/schema.result index 2919606d74a..890f53669b5 100644 --- a/mysql-test/r/schema.result +++ b/mysql-test/r/schema.result @@ -16,19 +16,21 @@ drop schema foo; # Bug #48940 MDL deadlocks against mysql_rm_db # DROP SCHEMA IF EXISTS schema1; +DROP SCHEMA IF EXISTS schema2; # Connection default CREATE SCHEMA schema1; +CREATE SCHEMA schema2; CREATE TABLE schema1.t1 (a INT); SET autocommit= FALSE; INSERT INTO schema1.t1 VALUES (1); # Connection 2 DROP SCHEMA schema1; # Connection default -ALTER SCHEMA schema1 DEFAULT CHARACTER SET utf8; -Got one of the listed errors +ALTER SCHEMA schema2 DEFAULT CHARACTER SET utf8; SET autocommit= TRUE; # Connection 2 # Connection default +DROP SCHEMA schema2; # # Bug #49988 MDL deadlocks with mysql_create_db, reload_acl_and_cache # @@ -48,3 +50,47 @@ ERROR HY000: Can't execute the given command because you have active locked tabl UNLOCK TABLES; # Connection con2 # Connection default +# +# Bug#54360 Deadlock DROP/ALTER/CREATE DATABASE with open HANDLER +# +CREATE DATABASE db1; +CREATE TABLE db1.t1 (a INT); +INSERT INTO db1.t1 VALUES (1), (2); +# Connection con1 +HANDLER db1.t1 OPEN; +# Connection default +# Sending: +DROP DATABASE db1; +# Connection con2 +# Connection con1 +CREATE DATABASE db2; +ALTER DATABASE db2 DEFAULT CHARACTER SET utf8; +DROP DATABASE db2; +# Connection default +# Reaping: DROP DATABASE db1 +# +# Tests for increased CREATE/ALTER/DROP DATABASE concurrency with +# database name locks. +# +DROP DATABASE IF EXISTS db1; +DROP DATABASE IF EXISTS db2; +# Connection default +CREATE DATABASE db1; +CREATE TABLE db1.t1 (id INT); +START TRANSACTION; +INSERT INTO db1.t1 VALUES (1); +# Connection 2 +# DROP DATABASE should block due to the active transaction +# Sending: +DROP DATABASE db1; +# Connection 3 +# But it should still be possible to CREATE/ALTER/DROP other databases. +CREATE DATABASE db2; +ALTER DATABASE db2 DEFAULT CHARACTER SET utf8; +DROP DATABASE db2; +# Connection default +# End the transaction so DROP DATABASE db1 can continue +COMMIT; +# Connection 2 +# Reaping: DROP DATABASE db1 +# Connection default; diff --git a/mysql-test/suite/perfschema/r/server_init.result b/mysql-test/suite/perfschema/r/server_init.result index ac340f8eb67..a37a5805dd7 100644 --- a/mysql-test/suite/perfschema/r/server_init.result +++ b/mysql-test/suite/perfschema/r/server_init.result @@ -40,18 +40,10 @@ where name like "wait/synch/cond/mysys/THR_COND_threads"; count(name) 1 select count(name) from MUTEX_INSTANCES -where name like "wait/synch/mutex/sql/LOCK_mysql_create_db"; -count(name) -1 -select count(name) from MUTEX_INSTANCES where name like "wait/synch/mutex/sql/LOCK_open"; count(name) 1 select count(name) from MUTEX_INSTANCES -where name like "wait/synch/mutex/sql/LOCK_lock_db"; -count(name) -1 -select count(name) from MUTEX_INSTANCES where name like "wait/synch/mutex/sql/LOCK_thread_count"; count(name) 1 diff --git a/mysql-test/suite/perfschema/t/server_init.test b/mysql-test/suite/perfschema/t/server_init.test index cd9357cce67..0bb3dd84ac4 100644 --- a/mysql-test/suite/perfschema/t/server_init.test +++ b/mysql-test/suite/perfschema/t/server_init.test @@ -68,15 +68,9 @@ select count(name) from COND_INSTANCES # Verify that these global mutexes have been properly initilized in sql -select count(name) from MUTEX_INSTANCES - where name like "wait/synch/mutex/sql/LOCK_mysql_create_db"; - select count(name) from MUTEX_INSTANCES where name like "wait/synch/mutex/sql/LOCK_open"; -select count(name) from MUTEX_INSTANCES - where name like "wait/synch/mutex/sql/LOCK_lock_db"; - select count(name) from MUTEX_INSTANCES where name like "wait/synch/mutex/sql/LOCK_thread_count"; diff --git a/mysql-test/t/mdl_sync.test b/mysql-test/t/mdl_sync.test index 19f9b396087..441cabb10e2 100644 --- a/mysql-test/t/mdl_sync.test +++ b/mysql-test/t/mdl_sync.test @@ -3705,6 +3705,481 @@ DROP TABLE t1; disconnect con1; +--echo # +--echo # Tests for schema-scope locks +--echo # + +--disable_warnings +DROP DATABASE IF EXISTS db1; +DROP DATABASE IF EXISTS db2; +--enable_warnings + +connect (con2, localhost, root); +connect (con3, localhost, root); + +--echo # Test 1: +--echo # CREATE DATABASE blocks database DDL on the same database, but +--echo # not database DDL on different databases. Tests X vs X lock. +--echo # + +--echo # Connection default +connection default; +SET DEBUG_SYNC= 'after_wait_locked_schema_name SIGNAL locked WAIT_FOR blocked'; +--echo # Sending: +--send CREATE DATABASE db1 + +--echo # Connection con2 +connection con2; +SET DEBUG_SYNC= 'now WAIT_FOR locked'; +--echo # Sending: +# This should block. +--send CREATE DATABASE db1 + +--echo # Connection con3 +connection con3; +let $wait_condition=SELECT COUNT(*)=1 FROM information_schema.processlist + WHERE state='Waiting for table' AND info='CREATE DATABASE db1'; +--source include/wait_condition.inc +# This should not block. +CREATE DATABASE db2; +ALTER DATABASE db2 DEFAULT CHARACTER SET utf8; +DROP DATABASE db2; +SET DEBUG_SYNC= 'now SIGNAL blocked'; + +--echo # Connection default +connection default; +--echo # Reaping: CREATE DATABASE db1 +--reap + +--echo # Connection con2 +connection con2; +--echo # Reaping: CREATE DATABASE db1 +--error ER_DB_CREATE_EXISTS +--reap + +--echo # Test 2: +--echo # ALTER DATABASE blocks database DDL on the same database, but +--echo # not database DDL on different databases. Tests X vs X lock. +--echo # + +--echo # Connection default +connection default; +SET DEBUG_SYNC= 'after_wait_locked_schema_name SIGNAL locked WAIT_FOR blocked'; +--echo # Sending: +--send ALTER DATABASE db1 DEFAULT CHARACTER SET utf8 + +--echo # Connection con2 +connection con2; +SET DEBUG_SYNC= 'now WAIT_FOR locked'; +--echo # Sending: +# This should block. +--send ALTER DATABASE db1 DEFAULT CHARACTER SET utf8 + +--echo # Connection con3 +connection con3; +let $wait_condition=SELECT COUNT(*)=1 FROM information_schema.processlist + WHERE state='Waiting for table' + AND info='ALTER DATABASE db1 DEFAULT CHARACTER SET utf8'; +--source include/wait_condition.inc +# This should not block. +CREATE DATABASE db2; +ALTER DATABASE db2 DEFAULT CHARACTER SET utf8; +DROP DATABASE db2; +SET DEBUG_SYNC= 'now SIGNAL blocked'; + +--echo # Connection default +connection default; +--echo # Reaping: ALTER DATABASE db1 DEFAULT CHARACTER SET utf8 +--reap + +--echo # Connection con2 +connection con2; +--echo # Reaping: ALTER DATABASE db1 DEFAULT CHARACTER SET utf8 +--reap + +--echo # Connection default +connection default; +SET DEBUG_SYNC= 'after_wait_locked_schema_name SIGNAL locked WAIT_FOR blocked'; +--echo # Sending: +--send ALTER DATABASE db1 DEFAULT CHARACTER SET utf8 + +--echo # Connection con2 +connection con2; +SET DEBUG_SYNC= 'now WAIT_FOR locked'; +--echo # Sending: +# This should also block. +--send DROP DATABASE db1 + +--echo # Connection con3 +connection con3; +let $wait_condition=SELECT COUNT(*)=1 FROM information_schema.processlist + WHERE state='Waiting for table' AND info='DROP DATABASE db1'; +--source include/wait_condition.inc +SET DEBUG_SYNC= 'now SIGNAL blocked'; + +--echo # Connection default +connection default; +--echo # Reaping: ALTER DATABASE db1 DEFAULT CHARACTER SET utf8 +--reap + +--echo # Connection con2 +connection con2; +--echo # Reaping: DROP DATABASE db1 +--reap +# Recreate the database +CREATE DATABASE db1; + +--echo # Test 3: +--echo # Two ALTER..UPGRADE of the same database are mutually exclusive, but +--echo # two ALTER..UPGRADE of different databases are not. Tests X vs X lock. +--echo # + +let $MYSQLD_DATADIR= `select @@datadir`; +# Manually make a 5.0 database from the template +--mkdir $MYSQLD_DATADIR/a-b-c +--copy_file $MYSQLD_DATADIR/db1/db.opt $MYSQLD_DATADIR/a-b-c/db.opt +--mkdir $MYSQLD_DATADIR/a-b-c-d +--copy_file $MYSQLD_DATADIR/db1/db.opt $MYSQLD_DATADIR/a-b-c-d/db.opt + +--echo # Connection default +connection default; +SET DEBUG_SYNC= 'after_wait_locked_schema_name SIGNAL locked WAIT_FOR blocked'; +--echo # Sending: +--send ALTER DATABASE `#mysql50#a-b-c` UPGRADE DATA DIRECTORY NAME + +--echo # Connection con2 +connection con2; +SET DEBUG_SYNC= 'now WAIT_FOR locked'; +--echo # Sending: +# This should block. +--send ALTER DATABASE `#mysql50#a-b-c` UPGRADE DATA DIRECTORY NAME + +--echo # Connection con3 +connection con3; +let $wait_condition=SELECT COUNT(*)=1 FROM information_schema.processlist + WHERE state='Waiting for table' + AND info='ALTER DATABASE `#mysql50#a-b-c` UPGRADE DATA DIRECTORY NAME'; +--source include/wait_condition.inc +# This should not block. +ALTER DATABASE `#mysql50#a-b-c-d` UPGRADE DATA DIRECTORY NAME; +SET DEBUG_SYNC= 'now SIGNAL blocked'; + +--echo # Connection default +connection default; +--echo # Reaping: ALTER DATABASE '#mysql50#a-b-c' UPGRADE DATA DIRECTORY NAME +--reap + +--echo # Connection con2 +connection con2; +--echo # Reaping: ALTER DATABASE '#mysql50#a-b-c' UPGRADE DATA DIRECTORY NAME +--error ER_BAD_DB_ERROR +--reap +DROP DATABASE `a-b-c`; +DROP DATABASE `a-b-c-d`; + +--echo # Test 4: +--echo # DROP DATABASE blocks database DDL on the same database, but +--echo # not database DDL on different databases. Tests X vs X lock. +--echo # + +--echo # Connection default +connection default; +SET DEBUG_SYNC= 'after_wait_locked_schema_name SIGNAL locked WAIT_FOR blocked'; +--echo # Sending: +--send DROP DATABASE db1 + +--echo # Connection con2 +connection con2; +SET DEBUG_SYNC= 'now WAIT_FOR locked'; +--echo # Sending: +# This should block. +--send DROP DATABASE db1 + +--echo # Connection con3 +connection con3; +let $wait_condition=SELECT COUNT(*)=1 FROM information_schema.processlist + WHERE state='Waiting for table' AND info='DROP DATABASE db1'; +--source include/wait_condition.inc +# This should not block. +CREATE DATABASE db2; +ALTER DATABASE db2 DEFAULT CHARACTER SET utf8; +DROP DATABASE db2; +SET DEBUG_SYNC= 'now SIGNAL blocked'; + +--echo # Connection default +connection default; +--echo # Reaping: DROP DATABASE db1 +--reap + +--echo # Connection con2 +connection con2; +--echo # Reaping: DROP DATABASE db1 +--error ER_DB_DROP_EXISTS +--reap + +--echo # Connection default +connection default; +CREATE DATABASE db1; +SET DEBUG_SYNC= 'after_wait_locked_schema_name SIGNAL locked WAIT_FOR blocked'; +--echo # Sending: +--send DROP DATABASE db1 + +--echo # Connection con2 +connection con2; +SET DEBUG_SYNC= 'now WAIT_FOR locked'; +--echo # Sending: +# This should also block. +--send ALTER DATABASE db1 DEFAULT CHARACTER SET utf8 + +--echo # Connection con3 +connection con3; +let $wait_condition=SELECT COUNT(*)=1 FROM information_schema.processlist + WHERE state='Waiting for table' + AND info='ALTER DATABASE db1 DEFAULT CHARACTER SET utf8'; +--source include/wait_condition.inc +SET DEBUG_SYNC= 'now SIGNAL blocked'; + +--echo # Connection default +connection default; +--echo # Reaping: DROP DATABASE db1 +--reap + +--echo # Connection con2 +connection con2; +--echo # Reaping: ALTER DATABASE db1 DEFAULT CHARACTER SET utf8 +--error 1 # Wrong error pending followup patch for bug#54360 +--reap + + +--echo # Test 5: +--echo # Locked database name prevents CREATE of tables in that database. +--echo # Tests X vs IX lock. +--echo # + +--echo # Connection default +connection default; +CREATE DATABASE db1; +SET DEBUG_SYNC= 'after_wait_locked_schema_name SIGNAL locked WAIT_FOR blocked'; +--echo # Sending: +--send DROP DATABASE db1 + +--echo # Connection con2 +connection con2; +SET DEBUG_SYNC= 'now WAIT_FOR locked'; +--echo # Sending: +# This should block. +--send CREATE TABLE db1.t1 (a INT) + +--echo # Connection con3 +connection con3; +let $wait_condition=SELECT COUNT(*)=1 FROM information_schema.processlist + WHERE state='Waiting for table' AND info='CREATE TABLE db1.t1 (a INT)'; +--source include/wait_condition.inc +SET DEBUG_SYNC= 'now SIGNAL blocked'; + +--echo # Connection default +connection default; +--echo # Reaping: DROP DATABASE db1 +--reap + +--echo # Connection con2 +connection con2; +--echo # Reaping: CREATE TABLE db1.t1 (a INT) +--error ER_BAD_DB_ERROR +--reap + +--echo # Test 6: +--echo # Locked database name prevents RENAME of tables to/from that database. +--echo # Tests X vs IX lock. +--echo # + +--echo # Connection default +connection default; +CREATE DATABASE db1; +CREATE TABLE db1.t1 (a INT); +SET DEBUG_SYNC= 'after_wait_locked_schema_name SIGNAL locked WAIT_FOR blocked'; +--echo # Sending: +--send DROP DATABASE db1 + +--echo # Connection con2 +connection con2; +SET DEBUG_SYNC= 'now WAIT_FOR locked'; +--echo # Sending: +# This should block. +--send RENAME TABLE db1.t1 TO test.t1 + +--echo # Connection con3 +connection con3; +let $wait_condition=SELECT COUNT(*)=1 FROM information_schema.processlist + WHERE state='Waiting for table' AND info='RENAME TABLE db1.t1 TO test.t1'; +--source include/wait_condition.inc +SET DEBUG_SYNC= 'now SIGNAL blocked'; + +--echo # Connection default +connection default; +--echo # Reaping: DROP DATABASE db1 +--reap + +--echo # Connection con2 +connection con2; +--echo # Reaping: RENAME TABLE db1.t1 TO test.t1 +--error ER_FILE_NOT_FOUND +--reap + +--echo # Connection default +connection default; +CREATE DATABASE db1; +CREATE TABLE test.t2 (a INT); +SET DEBUG_SYNC= 'after_wait_locked_schema_name SIGNAL locked WAIT_FOR blocked'; +--echo # Sending: +--send DROP DATABASE db1 + +--echo # Connection con2 +connection con2; +SET DEBUG_SYNC= 'now WAIT_FOR locked'; +--echo # Sending: +# This should block. +--send RENAME TABLE test.t2 TO db1.t2 + +--echo # Connection con3 +connection con3; +let $wait_condition=SELECT COUNT(*)=1 FROM information_schema.processlist + WHERE state='Waiting for table' AND info='RENAME TABLE test.t2 TO db1.t2'; +--source include/wait_condition.inc +SET DEBUG_SYNC= 'now SIGNAL blocked'; + +--echo # Connection default +connection default; +--echo # Reaping: DROP DATABASE db1 +--reap + +--echo # Connection con2 +connection con2; +--echo # Reaping: RENAME TABLE test.t2 TO db1.t2 +--error 7 # Wrong error pending followup patch for bug#54360 +--reap +DROP TABLE test.t2; + + +--echo # Test 7: +--echo # Locked database name prevents DROP of tables in that database. +--echo # Tests X vs IX lock. +--echo # + +--echo # Connection default +connection default; +CREATE DATABASE db1; +CREATE TABLE db1.t1 (a INT); +SET DEBUG_SYNC= 'after_wait_locked_schema_name SIGNAL locked WAIT_FOR blocked'; +--echo # Sending: +--send DROP DATABASE db1 + +--echo # Connection con2 +connection con2; +SET DEBUG_SYNC= 'now WAIT_FOR locked'; +--echo # Sending: +# This should block. +--send DROP TABLE db1.t1 + +--echo # Connection con3 +connection con3; +let $wait_condition=SELECT COUNT(*)=1 FROM information_schema.processlist + WHERE state='Waiting for table' AND info='DROP TABLE db1.t1'; +--source include/wait_condition.inc +SET DEBUG_SYNC= 'now SIGNAL blocked'; + +--echo # Connection default +connection default; +--echo # Reaping: DROP DATABASE db1 +--reap + +--echo # Connection con2 +connection con2; +--echo # Reaping: DROP TABLE db1.t1 +--error ER_BAD_TABLE_ERROR +--reap + +--echo # Connection default +connection default; +disconnect con2; +disconnect con3; +SET DEBUG_SYNC= 'RESET'; + +--echo # +--echo # End of tests for schema-scope locks +--echo # + +--echo # +--echo # Tests of granted global S lock (FLUSH TABLE WITH READ LOCK) +--echo # + +CREATE DATABASE db1; +CREATE TABLE db1.t1(a INT); +connect(con2, localhost, root); +connect(con3, localhost, root); + +--echo # Connection default +connection default; +FLUSH TABLE WITH READ LOCK; + +--echo # Connection con2 +connection con2; +# IX global lock should block +--send CREATE TABLE db1.t2(a INT) + +--echo # Connection default +connection default; +let $wait_condition=SELECT COUNT(*)=1 FROM information_schema.processlist + WHERE state='Waiting for release of readlock' + AND info='CREATE TABLE db1.t2(a INT)'; +--source include/wait_condition.inc +UNLOCK TABLES; + +--echo # Connection con2 +connection con2; +--echo # Reaping CREATE TABLE db1.t2(a INT) +--reap + +--echo # Connection default +connection default; +FLUSH TABLE WITH READ LOCK; + +--echo # Connection con2 +connection con2; +# X global lock should block +--send ALTER DATABASE db1 DEFAULT CHARACTER SET utf8 + +--echo # Connection default +connection default; +let $wait_condition=SELECT COUNT(*)=1 FROM information_schema.processlist + WHERE state='Waiting for release of readlock' + AND info='ALTER DATABASE db1 DEFAULT CHARACTER SET utf8'; +--source include/wait_condition.inc +UNLOCK TABLES; + +--echo # Connection con2 +connection con2; +--echo # Reaping ALTER DATABASE db1 DEFAULT CHARACTER SET utf8 +--reap + +--echo # Connection default +connection default; +FLUSH TABLE WITH READ LOCK; + +--echo # Connection con2 +connection con2; +# S global lock should not block +FLUSH TABLE WITH READ LOCK; +UNLOCK TABLES; + +--echo # Connection default +connection default; +UNLOCK TABLES; +DROP DATABASE db1; +disconnect con2; +disconnect con3; + + # Check that all connections opened by test cases in this file are really # gone so execution of other tests won't be affected by their presence. --source include/wait_until_count_sessions.inc diff --git a/mysql-test/t/partition_debug_sync.test b/mysql-test/t/partition_debug_sync.test index 9165006f537..cde94856ae6 100644 --- a/mysql-test/t/partition_debug_sync.test +++ b/mysql-test/t/partition_debug_sync.test @@ -65,7 +65,7 @@ ENGINE = MYISAM PARTITION p1 VALUES LESS THAN (20), PARTITION p2 VALUES LESS THAN (100), PARTITION p3 VALUES LESS THAN MAXVALUE ) */; -SET DEBUG_SYNC= 'open_tables_acquire_upgradable_mdl SIGNAL removing_partitions WAIT_FOR waiting_for_alter'; +SET DEBUG_SYNC= 'alter_table_before_open_tables SIGNAL removing_partitions WAIT_FOR waiting_for_alter'; SET DEBUG_SYNC= 'alter_table_before_rename_result_table WAIT_FOR delete_done'; --send ALTER TABLE t2 REMOVE PARTITIONING connection default; diff --git a/mysql-test/t/schema.test b/mysql-test/t/schema.test index f106b9e4865..e3592f39780 100644 --- a/mysql-test/t/schema.test +++ b/mysql-test/t/schema.test @@ -23,6 +23,7 @@ drop schema foo; --disable_warnings DROP SCHEMA IF EXISTS schema1; +DROP SCHEMA IF EXISTS schema2; --enable_warnings connect(con2, localhost, root); @@ -31,6 +32,7 @@ connect(con2, localhost, root); connection default; CREATE SCHEMA schema1; +CREATE SCHEMA schema2; CREATE TABLE schema1.t1 (a INT); SET autocommit= FALSE; @@ -46,9 +48,7 @@ let $wait_condition= SELECT COUNT(*)= 1 FROM information_schema.processlist WHERE state= 'Waiting for table' AND info='DROP SCHEMA schema1'; --source include/wait_condition.inc -# Listing the error twice to prevent result diffences based on filename ---error 1,1 -ALTER SCHEMA schema1 DEFAULT CHARACTER SET utf8; +ALTER SCHEMA schema2 DEFAULT CHARACTER SET utf8; SET autocommit= TRUE; --echo # Connection 2 @@ -57,6 +57,7 @@ connection con2; --echo # Connection default connection default; +DROP SCHEMA schema2; disconnect con2; @@ -102,6 +103,97 @@ connection con2; connection default; disconnect con2; + +--echo # +--echo # Bug#54360 Deadlock DROP/ALTER/CREATE DATABASE with open HANDLER +--echo # + +CREATE DATABASE db1; +CREATE TABLE db1.t1 (a INT); +INSERT INTO db1.t1 VALUES (1), (2); + +--echo # Connection con1 +connect (con1, localhost, root); +HANDLER db1.t1 OPEN; + +--echo # Connection default +connection default; +--echo # Sending: +--send DROP DATABASE db1 + +--echo # Connection con2 +connect (con2, localhost, root); +let $wait_condition=SELECT COUNT(*)=1 FROM information_schema.processlist + WHERE state='Waiting for table' AND info='DROP DATABASE db1'; +--source include/wait_condition.inc + +--echo # Connection con1 +connection con1; +# All these statements before resulted in deadlock. +CREATE DATABASE db2; +ALTER DATABASE db2 DEFAULT CHARACTER SET utf8; +DROP DATABASE db2; + +--echo # Connection default +connection default; +--echo # Reaping: DROP DATABASE db1 +--reap +disconnect con1; +disconnect con2; + + +--echo # +--echo # Tests for increased CREATE/ALTER/DROP DATABASE concurrency with +--echo # database name locks. +--echo # + +--disable_warnings +DROP DATABASE IF EXISTS db1; +DROP DATABASE IF EXISTS db2; +--enable_warnings + +connect (con2, localhost, root); +connect (con3, localhost, root); + +--echo # Connection default +connection default; +CREATE DATABASE db1; +CREATE TABLE db1.t1 (id INT); +START TRANSACTION; +INSERT INTO db1.t1 VALUES (1); + +--echo # Connection 2 +connection con2; +--echo # DROP DATABASE should block due to the active transaction +--echo # Sending: +--send DROP DATABASE db1 + +--echo # Connection 3 +connection con3; +let $wait_condition=SELECT COUNT(*)=1 FROM information_schema.processlist + WHERE state='Waiting for table' and info='DROP DATABASE db1'; +--source include/wait_condition.inc +--echo # But it should still be possible to CREATE/ALTER/DROP other databases. +CREATE DATABASE db2; +ALTER DATABASE db2 DEFAULT CHARACTER SET utf8; +DROP DATABASE db2; + +--echo # Connection default +connection default; +--echo # End the transaction so DROP DATABASE db1 can continue +COMMIT; + +--echo # Connection 2 +connection con2; +--echo # Reaping: DROP DATABASE db1 +--reap + +--echo # Connection default; +connection default; +disconnect con2; +disconnect con3; + + # Check that all connections opened by test cases in this file are really # gone so execution of other tests won't be affected by their presence. --source include/wait_until_count_sessions.inc diff --git a/sql/lock.cc b/sql/lock.cc index de0f39018f7..dcee018276f 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -747,62 +747,48 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, } -/***************************************************************************** - Lock table based on the name. - This is used when we need total access to a closed, not open table -*****************************************************************************/ - /** - Obtain exclusive metadata locks on the list of tables. + Obtain an exclusive metadata lock on a schema name. - @param thd Thread handle - @param table_list List of tables to lock + @param thd Thread handle. + @param db The database name. - @note This function assumes that no metadata locks were acquired - before calling it. Also it cannot be called while holding - LOCK_open mutex. Both these invariants are enforced by asserts - in MDL_context::acquire_locks(). - @note Initialization of MDL_request members of TABLE_LIST elements - is a responsibility of the caller. + This function cannot be called while holding LOCK_open mutex. + To avoid deadlocks, we do not try to obtain exclusive metadata + locks in LOCK TABLES mode, since in this mode there may be + other metadata locks already taken by the current connection, + and we must not wait for MDL locks while holding locks. - @retval FALSE Success. - @retval TRUE Failure (OOM or thread was killed). + @retval FALSE Success. + @retval TRUE Failure: we're in LOCK TABLES mode, or out of memory, + or this connection was killed. */ -bool lock_table_names(THD *thd, TABLE_LIST *table_list) +bool lock_schema_name(THD *thd, const char *db) { MDL_request_list mdl_requests; MDL_request global_request; - TABLE_LIST *lock_table; + MDL_request mdl_request; + + if (thd->locked_tables_mode) + { + my_message(ER_LOCK_OR_ACTIVE_TRANSACTION, + ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0)); + return TRUE; + } global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE); + mdl_request.init(MDL_key::SCHEMA, db, "", MDL_EXCLUSIVE); - for (lock_table= table_list; lock_table; lock_table= lock_table->next_local) - mdl_requests.push_front(&lock_table->mdl_request); - + mdl_requests.push_front(&mdl_request); mdl_requests.push_front(&global_request); if (thd->mdl_context.acquire_locks(&mdl_requests, thd->variables.lock_wait_timeout)) - return 1; + return TRUE; - return 0; -} - - -/** - Release all metadata locks previously obtained by lock_table_names(). - - @param thd Thread handle. - - @note Cannot be called while holding LOCK_open mutex. -*/ - -void unlock_table_names(THD *thd) -{ - DBUG_ENTER("unlock_table_names"); - thd->mdl_context.release_transactional_locks(); - DBUG_VOID_RETURN; + DEBUG_SYNC(thd, "after_wait_locked_schema_name"); + return FALSE; } @@ -837,6 +823,7 @@ bool lock_routine_name(THD *thd, bool is_function, MDL_key::PROCEDURE); MDL_request_list mdl_requests; MDL_request global_request; + MDL_request schema_request; MDL_request mdl_request; if (thd->locked_tables_mode) @@ -850,9 +837,11 @@ bool lock_routine_name(THD *thd, bool is_function, DEBUG_SYNC(thd, "before_wait_locked_pname"); global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE); + schema_request.init(MDL_key::SCHEMA, db, "", MDL_INTENTION_EXCLUSIVE); mdl_request.init(mdl_type, db, name, MDL_EXCLUSIVE); mdl_requests.push_front(&mdl_request); + mdl_requests.push_front(&schema_request); mdl_requests.push_front(&global_request); if (thd->mdl_context.acquire_locks(&mdl_requests, diff --git a/sql/lock.h b/sql/lock.h index 4bdf0085d07..0083dd3ba18 100644 --- a/sql/lock.h +++ b/sql/lock.h @@ -62,8 +62,7 @@ bool mysql_lock_abort_for_thread(THD *thd, TABLE *table); MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b); void broadcast_refresh(void); /* Lock based on name */ -bool lock_table_names(THD *thd, TABLE_LIST *table_list); -void unlock_table_names(THD *thd); +bool lock_schema_name(THD *thd, const char *db); /* Lock based on stored routine name */ bool lock_routine_name(THD *thd, bool is_function, const char *db, const char *name); diff --git a/sql/mdl.cc b/sql/mdl.cc index 22ad15d2360..631d8f52b5f 100644 --- a/sql/mdl.cc +++ b/sql/mdl.cc @@ -406,14 +406,15 @@ public: /** - An implementation of the global metadata lock. The only locking modes - which are supported at the moment are SHARED and INTENTION EXCLUSIVE. + An implementation of the scoped metadata lock. The only locking modes + which are supported at the moment are SHARED and INTENTION EXCLUSIVE + and EXCLUSIVE */ -class MDL_global_lock : public MDL_lock +class MDL_scoped_lock : public MDL_lock { public: - MDL_global_lock(const MDL_key *key_arg) + MDL_scoped_lock(const MDL_key *key_arg) : MDL_lock(key_arg) { } @@ -857,7 +858,8 @@ inline MDL_lock *MDL_lock::create(const MDL_key *mdl_key) switch (mdl_key->mdl_namespace()) { case MDL_key::GLOBAL: - return new MDL_global_lock(mdl_key); + case MDL_key::SCHEMA: + return new MDL_scoped_lock(mdl_key); case MDL_key::TABLE: return new MDL_table_lock(mdl_key); default: @@ -1217,61 +1219,66 @@ void MDL_lock::reschedule_waiters() /** - Compatibility (or rather "incompatibility") matrices for global metadata + Compatibility (or rather "incompatibility") matrices for scoped metadata lock. Arrays of bitmaps which elements specify which granted/waiting locks are incompatible with type of lock being requested. - Here is how types of individual locks are translated to type of global lock: + Here is how types of individual locks are translated to type of scoped lock: ----------------+-------------+ Type of request | Correspond. | - for indiv. lock | global lock | + for indiv. lock | scoped lock | ----------------+-------------+ S, SH, SR, SW | IS | SNW, SNRW, X | IX | SNW, SNRW -> X | IX (*) | The first array specifies if particular type of request can be satisfied - if there is granted global lock of certain type. + if there is granted scoped lock of certain type. - | Type of active | - Request | global lock | - type | IS(**) IX S | - ---------+----------------+ - IS | + + + | - IX | + + - | - S | + - + | + | Type of active | + Request | scoped lock | + type | IS(**) IX S X | + ---------+------------------+ + IS | + + + + | + IX | + + - - | + S | + - + - | + X | + - - - | The second array specifies if particular type of request can be satisfied - if there is already waiting request for the global lock of certain type. + if there is already waiting request for the scoped lock of certain type. I.e. it specifies what is the priority of different lock types. - | Pending | - Request | global lock | - type | IS(**) IX S | - ---------+--------------+ - IS | + + + | - IX | + + - | - S | + + + | + | Pending | + Request | scoped lock | + type | IS(**) IX S X | + ---------+-----------------+ + IS | + + + + | + IX | + + - - | + S | + + + - | + X | + + + + | Here: "+" -- means that request can be satisfied "-" -- means that request can't be satisfied and should wait - (*) Since for upgradable locks we always take intention exclusive global + (*) Since for upgradable locks we always take intention exclusive scoped lock at the same time when obtaining the shared lock, there is no need to obtain such lock during the upgrade itself. - (**) Since intention shared global locks are compatible with all other + (**) Since intention shared scoped locks are compatible with all other type of locks we don't even have any accounting for them. */ -const MDL_lock::bitmap_t MDL_global_lock::m_granted_incompatible[MDL_TYPE_END] = +const MDL_lock::bitmap_t MDL_scoped_lock::m_granted_incompatible[MDL_TYPE_END] = { - MDL_BIT(MDL_SHARED), MDL_BIT(MDL_INTENTION_EXCLUSIVE), 0, 0, 0, 0, 0, 0 + MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED), + MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_INTENTION_EXCLUSIVE), 0, 0, 0, 0, 0, + MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED) | MDL_BIT(MDL_INTENTION_EXCLUSIVE) }; -const MDL_lock::bitmap_t MDL_global_lock::m_waiting_incompatible[MDL_TYPE_END] = +const MDL_lock::bitmap_t MDL_scoped_lock::m_waiting_incompatible[MDL_TYPE_END] = { - MDL_BIT(MDL_SHARED), 0, 0, 0, 0, 0, 0, 0 + MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED), + MDL_BIT(MDL_EXCLUSIVE), 0, 0, 0, 0, 0, 0 }; @@ -1912,7 +1919,7 @@ extern "C" int mdl_request_ptr_cmp(const void* ptr1, const void* ptr2) @note The list of requests should not contain non-exclusive lock requests. There should not be any acquired locks in the context. - @note Assumes that one already owns global intention exclusive lock. + @note Assumes that one already owns scoped intention exclusive lock. @retval FALSE Success @retval TRUE Failure @@ -1929,13 +1936,6 @@ bool MDL_context::acquire_locks(MDL_request_list *mdl_requests, if (req_count == 0) return FALSE; - /* - To reduce deadlocks, the server acquires all exclusive - locks at once. For shared locks, try_acquire_lock() is - used instead. - */ - DBUG_ASSERT(m_tickets.is_empty() || m_tickets.front() == m_trans_sentinel); - /* Sort requests according to MDL_key. */ if (! (sort_buf= (MDL_request **)my_malloc(req_count * sizeof(MDL_request*), diff --git a/sql/mdl.h b/sql/mdl.h index ad3945f524c..5c58289aea2 100644 --- a/sql/mdl.h +++ b/sql/mdl.h @@ -40,15 +40,16 @@ class Deadlock_detection_visitor; Type of metadata lock request. @sa Comments for MDL_object_lock::can_grant_lock() and - MDL_global_lock::can_grant_lock() for details. + MDL_scoped_lock::can_grant_lock() for details. */ enum enum_mdl_type { /* - An intention exclusive metadata lock. Used only for global locks. + An intention exclusive metadata lock. Used only for scoped locks. Owner of this type of lock can acquire upgradable exclusive locks on individual objects. - Compatible with other IX locks, but is incompatible with global S lock. + Compatible with other IX locks, but is incompatible with scoped S and + X locks. */ MDL_INTENTION_EXCLUSIVE= 0, /* @@ -179,6 +180,7 @@ public: MDL_key is also used outside of the MDL subsystem. */ enum enum_mdl_namespace { GLOBAL=0, + SCHEMA, TABLE, FUNCTION, PROCEDURE, @@ -646,6 +648,8 @@ private: closes all open HANDLERs. However, one can open a few HANDLERs after entering the read only mode. + * LOCK TABLES locks include intention exclusive locks on + involved schemas. */ Ticket_list m_tickets; /** diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 28cad51aa41..0ba796a5eb8 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -27,8 +27,8 @@ // reset_status_vars #include "strfunc.h" // find_set_from_flags #include "parse_file.h" // File_parser_dummy_hook -#include "sql_db.h" // my_database_names_free, - // my_database_names_init +#include "sql_db.h" // my_dboptions_cache_free + // my_dboptions_cache_init #include "sql_table.h" // release_ddl_log, execute_ddl_log_recovery #include "sql_connect.h" // free_max_user_conn, init_max_user_conn, // handle_one_connection @@ -653,7 +653,7 @@ SHOW_COMP_OPTION have_profiling; pthread_key(MEM_ROOT**,THR_MALLOC); pthread_key(THD*, THR_THD); mysql_mutex_t LOCK_thread_count; -mysql_mutex_t LOCK_mysql_create_db, LOCK_open, +mysql_mutex_t LOCK_open, LOCK_mapped_file, LOCK_status, LOCK_global_read_lock, LOCK_error_log, LOCK_uuid_generator, LOCK_delayed_insert, LOCK_delayed_status, LOCK_delayed_create, @@ -1488,7 +1488,7 @@ void clean_up(bool print_message) bitmap_free(&slave_error_mask); #endif my_tz_free(); - my_database_names_free(); + my_dboptions_cache_free(); #ifndef NO_EMBEDDED_ACCESS_CHECKS servers_free(1); acl_free(1); @@ -1597,8 +1597,6 @@ static void wait_for_signal_thread_to_end() static void clean_up_mutexes() { - mysql_mutex_destroy(&LOCK_mysql_create_db); - mysql_mutex_destroy(&LOCK_lock_db); mysql_rwlock_destroy(&LOCK_grant); mysql_mutex_destroy(&LOCK_open); mysql_mutex_destroy(&LOCK_thread_count); @@ -3730,7 +3728,7 @@ static int init_common_variables() use_temp_pool= 0; #endif - if (my_database_names_init()) + if (my_dboptions_cache_init()) return 1; /* @@ -3787,9 +3785,6 @@ You should consider changing lower_case_table_names to 1 or 2", static int init_thread_environment() { - mysql_mutex_init(key_LOCK_mysql_create_db, - &LOCK_mysql_create_db, MY_MUTEX_INIT_SLOW); - mysql_mutex_init(key_LOCK_lock_db, &LOCK_lock_db, MY_MUTEX_INIT_SLOW); mysql_mutex_init(key_LOCK_open, &LOCK_open, MY_MUTEX_INIT_FAST); mysql_mutex_init(key_LOCK_thread_count, &LOCK_thread_count, MY_MUTEX_INIT_FAST); mysql_mutex_init(key_LOCK_mapped_file, &LOCK_mapped_file, MY_MUTEX_INIT_SLOW); @@ -8007,8 +8002,8 @@ PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_prep_xids, key_LOCK_connection_count, key_LOCK_crypt, key_LOCK_delayed_create, key_LOCK_delayed_insert, key_LOCK_delayed_status, key_LOCK_error_log, key_LOCK_gdl, key_LOCK_global_read_lock, key_LOCK_global_system_variables, - key_LOCK_lock_db, key_LOCK_manager, key_LOCK_mapped_file, - key_LOCK_mysql_create_db, key_LOCK_open, key_LOCK_prepared_stmt_count, + key_LOCK_manager, key_LOCK_mapped_file, + key_LOCK_open, key_LOCK_prepared_stmt_count, key_LOCK_rpl_status, key_LOCK_server_started, key_LOCK_status, key_LOCK_system_variables_hash, key_LOCK_table_share, key_LOCK_thd_data, key_LOCK_user_conn, key_LOCK_uuid_generator, key_LOG_LOCK_log, @@ -8046,10 +8041,8 @@ static PSI_mutex_info all_server_mutexes[]= { &key_LOCK_gdl, "LOCK_gdl", PSI_FLAG_GLOBAL}, { &key_LOCK_global_read_lock, "LOCK_global_read_lock", PSI_FLAG_GLOBAL}, { &key_LOCK_global_system_variables, "LOCK_global_system_variables", PSI_FLAG_GLOBAL}, - { &key_LOCK_lock_db, "LOCK_lock_db", PSI_FLAG_GLOBAL}, { &key_LOCK_manager, "LOCK_manager", PSI_FLAG_GLOBAL}, { &key_LOCK_mapped_file, "LOCK_mapped_file", PSI_FLAG_GLOBAL}, - { &key_LOCK_mysql_create_db, "LOCK_mysql_create_db", PSI_FLAG_GLOBAL}, { &key_LOCK_open, "LOCK_open", PSI_FLAG_GLOBAL}, { &key_LOCK_prepared_stmt_count, "LOCK_prepared_stmt_count", PSI_FLAG_GLOBAL}, { &key_LOCK_rpl_status, "LOCK_rpl_status", PSI_FLAG_GLOBAL}, diff --git a/sql/mysqld.h b/sql/mysqld.h index e14cd15ceb8..b07d148f507 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -234,8 +234,8 @@ extern PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_prep_xids, key_LOCK_connection_count, key_LOCK_crypt, key_LOCK_delayed_create, key_LOCK_delayed_insert, key_LOCK_delayed_status, key_LOCK_error_log, key_LOCK_gdl, key_LOCK_global_read_lock, key_LOCK_global_system_variables, - key_LOCK_lock_db, key_LOCK_logger, key_LOCK_manager, key_LOCK_mapped_file, - key_LOCK_mysql_create_db, key_LOCK_open, key_LOCK_prepared_stmt_count, + key_LOCK_logger, key_LOCK_manager, key_LOCK_mapped_file, + key_LOCK_open, key_LOCK_prepared_stmt_count, key_LOCK_rpl_status, key_LOCK_server_started, key_LOCK_status, key_LOCK_table_share, key_LOCK_thd_data, key_LOCK_user_conn, key_LOCK_uuid_generator, key_LOG_LOCK_log, @@ -323,7 +323,7 @@ extern MYSQL_PLUGIN_IMPORT key_map key_map_full; /* Should be threaded /* Server mutex locks and condition variables. */ -extern mysql_mutex_t LOCK_mysql_create_db, LOCK_open, LOCK_lock_db, +extern mysql_mutex_t LOCK_open, LOCK_mapped_file, LOCK_user_locks, LOCK_status, LOCK_error_log, LOCK_delayed_insert, LOCK_uuid_generator, LOCK_delayed_status, LOCK_delayed_create, LOCK_crypt, LOCK_timezone, diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index ec25e4cb68b..99226bad03f 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -677,16 +677,15 @@ my_bool acl_reload(THD *thd) To avoid deadlocks we should obtain table locks before obtaining acl_cache->lock mutex. */ - bzero((char*) tables, sizeof(tables)); - tables[0].alias= tables[0].table_name= (char*) "host"; - tables[1].alias= tables[1].table_name= (char*) "user"; - tables[2].alias= tables[2].table_name= (char*) "db"; - tables[0].db=tables[1].db=tables[2].db=(char*) "mysql"; + tables[0].init_one_table(C_STRING_WITH_LEN("mysql"), + C_STRING_WITH_LEN("host"), "host", TL_READ); + tables[1].init_one_table(C_STRING_WITH_LEN("mysql"), + C_STRING_WITH_LEN("user"), "user", TL_READ); + tables[2].init_one_table(C_STRING_WITH_LEN("mysql"), + C_STRING_WITH_LEN("db"), "db", TL_READ); tables[0].next_local= tables[0].next_global= tables+1; tables[1].next_local= tables[1].next_global= tables+2; - tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_READ; tables[0].open_type= tables[1].open_type= tables[2].open_type= OT_BASE_ONLY; - init_mdl_requests(tables); if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT)) { @@ -1925,9 +1924,8 @@ static bool test_if_create_new_users(THD *thd) { TABLE_LIST tl; ulong db_access; - bzero((char*) &tl,sizeof(tl)); - tl.db= (char*) "mysql"; - tl.table_name= (char*) "user"; + tl.init_one_table(C_STRING_WITH_LEN("mysql"), + C_STRING_WITH_LEN("user"), "user", TL_WRITE); create_new_users= 1; db_access=acl_get(sctx->host, sctx->ip, @@ -3107,20 +3105,18 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, /* open the mysql.tables_priv and mysql.columns_priv tables */ - bzero((char*) &tables,sizeof(tables)); - tables[0].alias=tables[0].table_name= (char*) "user"; - tables[1].alias=tables[1].table_name= (char*) "tables_priv"; - tables[2].alias=tables[2].table_name= (char*) "columns_priv"; + tables[0].init_one_table(C_STRING_WITH_LEN("mysql"), + C_STRING_WITH_LEN("user"), "user", TL_WRITE); + tables[1].init_one_table(C_STRING_WITH_LEN("mysql"), + C_STRING_WITH_LEN("tables_priv"), + "tables_priv", TL_WRITE); + tables[2].init_one_table(C_STRING_WITH_LEN("mysql"), + C_STRING_WITH_LEN("columns_priv"), + "columns_priv", TL_WRITE); tables[0].next_local= tables[0].next_global= tables+1; /* Don't open column table if we don't need it ! */ - tables[1].next_local= - tables[1].next_global= ((column_priv || - (revoke_grant && - ((rights & COL_ACLS) || columns.elements))) - ? tables+2 : 0); - tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_WRITE; - tables[0].db=tables[1].db=tables[2].db=(char*) "mysql"; - init_mdl_requests(tables); + if (column_priv || (revoke_grant && ((rights & COL_ACLS) || columns.elements))) + tables[1].next_local= tables[1].next_global= tables+2; /* This statement will be replicated as a statement, even when using @@ -3354,13 +3350,11 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, /* open the mysql.user and mysql.procs_priv tables */ - bzero((char*) &tables,sizeof(tables)); - tables[0].alias=tables[0].table_name= (char*) "user"; - tables[1].alias=tables[1].table_name= (char*) "procs_priv"; + tables[0].init_one_table(C_STRING_WITH_LEN("mysql"), + C_STRING_WITH_LEN("user"), "user", TL_WRITE); + tables[1].init_one_table(C_STRING_WITH_LEN("mysql"), + C_STRING_WITH_LEN("procs_priv"), "procs_priv", TL_WRITE); tables[0].next_local= tables[0].next_global= tables+1; - tables[0].lock_type=tables[1].lock_type=TL_WRITE; - tables[0].db=tables[1].db=(char*) "mysql"; - init_mdl_requests(tables); /* This statement will be replicated as a statement, even when using @@ -3511,13 +3505,11 @@ bool mysql_grant(THD *thd, const char *db, List &list, } /* open the mysql.user and mysql.db tables */ - bzero((char*) &tables,sizeof(tables)); - tables[0].alias=tables[0].table_name=(char*) "user"; - tables[1].alias=tables[1].table_name=(char*) "db"; + tables[0].init_one_table(C_STRING_WITH_LEN("mysql"), + C_STRING_WITH_LEN("user"), "user", TL_WRITE); + tables[1].init_one_table(C_STRING_WITH_LEN("mysql"), + C_STRING_WITH_LEN("db"), "db", TL_WRITE); tables[0].next_local= tables[0].next_global= tables+1; - tables[0].lock_type=tables[1].lock_type=TL_WRITE; - tables[0].db=tables[1].db=(char*) "mysql"; - init_mdl_requests(tables); /* This statement will be replicated as a statement, even when using @@ -3930,14 +3922,14 @@ my_bool grant_reload(THD *thd) if (!initialized) DBUG_RETURN(0); - bzero((char*) tables, sizeof(tables)); - tables[0].alias= tables[0].table_name= (char*) "tables_priv"; - tables[1].alias= tables[1].table_name= (char*) "columns_priv"; - tables[0].db= tables[1].db= (char *) "mysql"; + tables[0].init_one_table(C_STRING_WITH_LEN("mysql"), + C_STRING_WITH_LEN("tables_priv"), + "tables_priv", TL_READ); + tables[1].init_one_table(C_STRING_WITH_LEN("mysql"), + C_STRING_WITH_LEN("columns_priv"), + "columns_priv", TL_READ); tables[0].next_local= tables[0].next_global= tables+1; - tables[0].lock_type= tables[1].lock_type= TL_READ; tables[0].open_type= tables[1].open_type= OT_BASE_ONLY; - init_mdl_requests(tables); /* To avoid deadlocks we should obtain table locks before @@ -5209,22 +5201,23 @@ int open_grant_tables(THD *thd, TABLE_LIST *tables) DBUG_RETURN(-1); } - bzero((char*) tables, GRANT_TABLES*sizeof(*tables)); - tables->alias= tables->table_name= (char*) "user"; - (tables+1)->alias= (tables+1)->table_name= (char*) "db"; - (tables+2)->alias= (tables+2)->table_name= (char*) "tables_priv"; - (tables+3)->alias= (tables+3)->table_name= (char*) "columns_priv"; - (tables+4)->alias= (tables+4)->table_name= (char*) "procs_priv"; + tables->init_one_table(C_STRING_WITH_LEN("mysql"), + C_STRING_WITH_LEN("user"), "user", TL_WRITE); + (tables+1)->init_one_table(C_STRING_WITH_LEN("mysql"), + C_STRING_WITH_LEN("db"), "db", TL_WRITE); + (tables+2)->init_one_table(C_STRING_WITH_LEN("mysql"), + C_STRING_WITH_LEN("tables_priv"), + "tables_priv", TL_WRITE); + (tables+3)->init_one_table(C_STRING_WITH_LEN("mysql"), + C_STRING_WITH_LEN("columns_priv"), + "columns_priv", TL_WRITE); + (tables+4)->init_one_table(C_STRING_WITH_LEN("mysql"), + C_STRING_WITH_LEN("procs_priv"), + "procs_priv", TL_WRITE); tables->next_local= tables->next_global= tables+1; (tables+1)->next_local= (tables+1)->next_global= tables+2; (tables+2)->next_local= (tables+2)->next_global= tables+3; (tables+3)->next_local= (tables+3)->next_global= tables+4; - tables->lock_type= (tables+1)->lock_type= - (tables+2)->lock_type= (tables+3)->lock_type= - (tables+4)->lock_type= TL_WRITE; - tables->db= (tables+1)->db= (tables+2)->db= - (tables+3)->db= (tables+4)->db= (char*) "mysql"; - init_mdl_requests(tables); #ifdef HAVE_REPLICATION /* diff --git a/sql/sql_base.cc b/sql/sql_base.cc index d69a5b1aa77..426c9db2717 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -53,6 +53,7 @@ #include "rpl_filter.h" #include "sql_table.h" // build_table_filename #include "datadict.h" // dd_frm_type() +#include "sql_hset.h" // Hash_set #ifdef __WIN__ #include #endif @@ -4029,7 +4030,6 @@ end_unlock: Open_table_context::Open_table_context(THD *thd, uint flags) :m_failed_table(NULL), m_start_of_statement_svp(thd->mdl_context.mdl_savepoint()), - m_global_mdl_request(NULL), m_timeout(flags & MYSQL_LOCK_IGNORE_TIMEOUT ? LONG_TIMEOUT : thd->variables.lock_wait_timeout), m_flags(flags), @@ -4040,26 +4040,6 @@ Open_table_context::Open_table_context(THD *thd, uint flags) {} -/** - Get MDL_request object for global intention exclusive lock which - is acquired during opening tables for statements which take - upgradable shared metadata locks. -*/ - -MDL_request *Open_table_context::get_global_mdl_request(THD *thd) -{ - if (! m_global_mdl_request) - { - if ((m_global_mdl_request= new (thd->mem_root) MDL_request())) - { - m_global_mdl_request->init(MDL_key::GLOBAL, "", "", - MDL_INTENTION_EXCLUSIVE); - } - } - return m_global_mdl_request; -} - - /** Check if we can back-off and set back off action if we can. Otherwise report and return error. @@ -4108,13 +4088,23 @@ request_backoff_action(enum_open_table_action action_arg, my_error(ER_LOCK_DEADLOCK, MYF(0)); return TRUE; } - m_action= action_arg; /* If auto-repair or discovery are requested, a pointer to table list element must be provided. */ - DBUG_ASSERT((m_action != OT_DISCOVER && m_action != OT_REPAIR) || table); - m_failed_table= table; + if (table) + { + DBUG_ASSERT(action_arg == OT_DISCOVER || action_arg == OT_REPAIR); + m_failed_table= (TABLE_LIST*) current_thd->alloc(sizeof(TABLE_LIST)); + if (m_failed_table == NULL) + return TRUE; + m_failed_table->init_one_table(table->db, table->db_length, + table->table_name, + table->table_name_length, + table->alias, TL_WRITE); + m_failed_table->mdl_request.set_type(MDL_EXCLUSIVE); + } + m_action= action_arg; return FALSE; } @@ -4136,11 +4126,6 @@ Open_table_context:: recover_from_failed_open(THD *thd) { bool result= FALSE; - /* - Remove reference to released ticket from MDL_request. - */ - if (m_global_mdl_request) - m_global_mdl_request->ticket= NULL; /* Execute the action. */ switch (m_action) { @@ -4152,19 +4137,9 @@ recover_from_failed_open(THD *thd) break; case OT_DISCOVER: { - MDL_request mdl_global_request; - MDL_request mdl_xlock_request(&m_failed_table->mdl_request); - MDL_request_list mdl_requests; - - mdl_global_request.init(MDL_key::GLOBAL, "", "", - MDL_INTENTION_EXCLUSIVE); - mdl_xlock_request.set_type(MDL_EXCLUSIVE); - - mdl_requests.push_front(&mdl_xlock_request); - mdl_requests.push_front(&mdl_global_request); - - if ((result= - thd->mdl_context.acquire_locks(&mdl_requests, get_timeout()))) + if ((result= lock_table_names(thd, m_failed_table, NULL, + get_timeout(), + MYSQL_OPEN_SKIP_TEMPORARY))) break; mysql_mutex_lock(&LOCK_open); @@ -4181,19 +4156,9 @@ recover_from_failed_open(THD *thd) } case OT_REPAIR: { - MDL_request mdl_global_request; - MDL_request mdl_xlock_request(&m_failed_table->mdl_request); - MDL_request_list mdl_requests; - - mdl_global_request.init(MDL_key::GLOBAL, "", "", - MDL_INTENTION_EXCLUSIVE); - mdl_xlock_request.set_type(MDL_EXCLUSIVE); - - mdl_requests.push_front(&mdl_xlock_request); - mdl_requests.push_front(&mdl_global_request); - - if ((result= - thd->mdl_context.acquire_locks(&mdl_requests, get_timeout()))) + if ((result= lock_table_names(thd, m_failed_table, NULL, + get_timeout(), + MYSQL_OPEN_SKIP_TEMPORARY))) break; mysql_mutex_lock(&LOCK_open); @@ -4688,32 +4653,40 @@ end: DBUG_RETURN(error); } +extern "C" uchar *schema_set_get_key(const uchar *record, size_t *length, + my_bool not_used __attribute__((unused))) +{ + TABLE_LIST *table=(TABLE_LIST*) record; + *length= table->db_length; + return (uchar*) table->db; +} /** - Acquire upgradable (SNW, SNRW) metadata locks on tables to be opened - for LOCK TABLES or a DDL statement. Under LOCK TABLES, we can't take + Acquire upgradable (SNW, SNRW) metadata locks on tables used by + LOCK TABLES or by a DDL statement. Under LOCK TABLES, we can't take new locks, so use open_tables_check_upgradable_mdl() instead. - @param thd Thread context. - @param tables_start Start of list of tables on which upgradable locks - should be acquired. - @param tables_end End of list of tables. - @param ot_ctx Context of open_tables() operation. - @param flags Bitmap of flags to modify how the tables will be - open, see open_table() description for details. + @param thd Thread context. + @param tables_start Start of list of tables on which upgradable locks + should be acquired. + @param tables_end End of list of tables. + @param lock_wait_timeout Seconds to wait before timeout. + @param flags Bitmap of flags to modify how the tables will be + open, see open_table() description for details. @retval FALSE Success. @retval TRUE Failure (e.g. connection was killed) */ -static bool -open_tables_acquire_upgradable_mdl(THD *thd, TABLE_LIST *tables_start, - TABLE_LIST *tables_end, - Open_table_context *ot_ctx, - uint flags) +bool +lock_table_names(THD *thd, + TABLE_LIST *tables_start, TABLE_LIST *tables_end, + ulong lock_wait_timeout, uint flags) { MDL_request_list mdl_requests; TABLE_LIST *table; + MDL_request global_request; + Hash_set schema_set; DBUG_ASSERT(!thd->locked_tables_mode); @@ -4726,30 +4699,37 @@ open_tables_acquire_upgradable_mdl(THD *thd, TABLE_LIST *tables_start, (table->open_type != OT_BASE_ONLY && ! (flags & MYSQL_OPEN_SKIP_TEMPORARY) && find_temporary_table(thd, table)))) + { + if (schema_set.insert(table)) + return TRUE; mdl_requests.push_front(&table->mdl_request); + } } if (! mdl_requests.is_empty()) { - DEBUG_SYNC(thd, "open_tables_acquire_upgradable_mdl"); - - MDL_request *global_request= ot_ctx->get_global_mdl_request(thd); - - if (global_request == NULL) - return TRUE; - mdl_requests.push_front(global_request); + /* + Scoped locks: Take intention exclusive locks on all involved + schemas. + */ + Hash_set::Iterator it(schema_set); + while ((table= it++)) + { + MDL_request *schema_request= new (thd->mem_root) MDL_request; + if (schema_request == NULL) + return TRUE; + schema_request->init(MDL_key::SCHEMA, table->db, "", + MDL_INTENTION_EXCLUSIVE); + mdl_requests.push_front(schema_request); + } + /* Take the global intention exclusive lock. */ + global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE); + mdl_requests.push_front(&global_request); } - if (thd->mdl_context.acquire_locks(&mdl_requests, ot_ctx->get_timeout())) + if (thd->mdl_context.acquire_locks(&mdl_requests, lock_wait_timeout)) return TRUE; - for (table= tables_start; table && table != tables_end; - table= table->next_global) - { - if (table->mdl_request.type >= MDL_SHARED_NO_WRITE) - table->mdl_request.ticket= NULL; - } - return FALSE; } @@ -4921,12 +4901,21 @@ restart: goto err; } } - else if (open_tables_acquire_upgradable_mdl(thd, *start, - thd->lex->first_not_own_table(), - &ot_ctx, flags)) + else { - error= TRUE; - goto err; + TABLE_LIST *table; + if (lock_table_names(thd, *start, thd->lex->first_not_own_table(), + ot_ctx.get_timeout(), flags)) + { + error= TRUE; + goto err; + } + for (table= *start; table && table != thd->lex->first_not_own_table(); + table= table->next_global) + { + if (table->mdl_request.type >= MDL_SHARED_NO_WRITE) + table->mdl_request.ticket= NULL; + } } } diff --git a/sql/sql_base.h b/sql/sql_base.h index 20a068e27d7..67e5601663a 100644 --- a/sql/sql_base.h +++ b/sql/sql_base.h @@ -205,6 +205,9 @@ int setup_ftfuncs(SELECT_LEX* select); int init_ftfuncs(THD *thd, SELECT_LEX* select, bool no_order); void wait_for_condition(THD *thd, mysql_mutex_t *mutex, mysql_cond_t *cond); +bool lock_table_names(THD *thd, TABLE_LIST *table_list, + TABLE_LIST *table_list_end, ulong lock_wait_timeout, + uint flags); bool open_tables(THD *thd, TABLE_LIST **tables, uint *counter, uint flags, Prelocking_strategy *prelocking_strategy); /* open_and_lock_tables with optional derived handling */ @@ -480,8 +483,6 @@ public: return m_start_of_statement_svp; } - MDL_request *get_global_mdl_request(THD *thd); - inline ulong get_timeout() const { return m_timeout; @@ -498,11 +499,6 @@ private: */ TABLE_LIST *m_failed_table; MDL_ticket *m_start_of_statement_svp; - /** - Request object for global intention exclusive lock which is acquired during - opening tables for statements which take upgradable shared metadata locks. - */ - MDL_request *m_global_mdl_request; /** Lock timeout in seconds. Initialized to LONG_TIMEOUT when opening system tables or to the "lock_wait_timeout" system variable for regular tables. diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 5e992d2391c..c4a7dc53972 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -58,106 +58,6 @@ static void mysql_change_db_impl(THD *thd, CHARSET_INFO *new_db_charset); -/* Database lock hash */ -HASH lock_db_cache; -mysql_mutex_t LOCK_lock_db; -int creating_database= 0; // how many database locks are made - - -/* Structure for database lock */ -typedef struct my_dblock_st -{ - char *name; /* Database name */ - uint name_length; /* Database length name */ -} my_dblock_t; - - -/* - lock_db key. -*/ - -extern "C" uchar* lock_db_get_key(my_dblock_t *, size_t *, my_bool not_used); - -uchar* lock_db_get_key(my_dblock_t *ptr, size_t *length, - my_bool not_used __attribute__((unused))) -{ - *length= ptr->name_length; - return (uchar*) ptr->name; -} - - -/* - Free lock_db hash element. -*/ - -extern "C" void lock_db_free_element(void *ptr); - -void lock_db_free_element(void *ptr) -{ - my_free(ptr, MYF(0)); -} - - -/* - Put a database lock entry into the hash. - - DESCRIPTION - Insert a database lock entry into hash. - LOCK_db_lock must be previously locked. - - RETURN VALUES - 0 on success. - 1 on error. -*/ - -static my_bool lock_db_insert(const char *dbname, uint length) -{ - my_dblock_t *opt; - my_bool error= 0; - DBUG_ENTER("lock_db_insert"); - - mysql_mutex_assert_owner(&LOCK_lock_db); - - if (!(opt= (my_dblock_t*) my_hash_search(&lock_db_cache, - (uchar*) dbname, length))) - { - /* Db is not in the hash, insert it */ - char *tmp_name; - if (!my_multi_malloc(MYF(MY_WME | MY_ZEROFILL), - &opt, (uint) sizeof(*opt), &tmp_name, (uint) length+1, - NullS)) - { - error= 1; - goto end; - } - - opt->name= tmp_name; - strmov(opt->name, dbname); - opt->name_length= length; - - if ((error= my_hash_insert(&lock_db_cache, (uchar*) opt))) - my_free(opt, MYF(0)); - } - -end: - DBUG_RETURN(error); -} - - -/* - Delete a database lock entry from hash. -*/ - -void lock_db_delete(const char *name, uint length) -{ - my_dblock_t *opt; - mysql_mutex_assert_owner(&LOCK_lock_db); - if ((opt= (my_dblock_t *)my_hash_search(&lock_db_cache, - (const uchar*) name, length))) - my_hash_delete(&lock_db_cache, (uchar*) opt); -} - - /* Database options hash */ static HASH dboptions; static my_bool dboptions_init= 0; @@ -233,21 +133,16 @@ static void init_database_names_psi_keys(void) } #endif -/* - Initialize database option hash and locked database hash. +/** + Initialize database option cache. - SYNOPSIS - my_database_names() + @note Must be called before any other database function is called. - NOTES - Must be called before any other database function is called. - - RETURN - 0 ok - 1 Fatal error + @retval 0 ok + @retval 1 Fatal error */ -bool my_database_names_init(void) +bool my_dboptions_cache_init(void) { #ifdef HAVE_PSI_INTERFACE init_database_names_psi_keys(); @@ -261,36 +156,30 @@ bool my_database_names_init(void) error= my_hash_init(&dboptions, lower_case_table_names ? &my_charset_bin : system_charset_info, 32, 0, 0, (my_hash_get_key) dboptions_get_key, - free_dbopt,0) || - my_hash_init(&lock_db_cache, lower_case_table_names ? - &my_charset_bin : system_charset_info, - 32, 0, 0, (my_hash_get_key) lock_db_get_key, - lock_db_free_element,0); - + free_dbopt,0); } return error; } -/* +/** Free database option hash and locked databases hash. */ -void my_database_names_free(void) +void my_dboptions_cache_free(void) { if (dboptions_init) { dboptions_init= 0; my_hash_free(&dboptions); mysql_rwlock_destroy(&LOCK_dboptions); - my_hash_free(&lock_db_cache); } } -/* - Cleanup cached options +/** + Cleanup cached options. */ void my_dbopt_cleanup(void) @@ -395,7 +284,7 @@ end: Deletes database options from the hash. */ -void del_dbopt(const char *path) +static void del_dbopt(const char *path) { my_dbopt_t *opt; mysql_rwlock_wrlock(&LOCK_dboptions); @@ -664,25 +553,8 @@ int mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create_info, DBUG_RETURN(-1); } - /* - Do not create database if another thread is holding read lock. - Wait for global read lock before acquiring LOCK_mysql_create_db. - After wait_if_global_read_lock() we have protection against another - global read lock. If we would acquire LOCK_mysql_create_db first, - another thread could step in and get the global read lock before we - reach wait_if_global_read_lock(). If this thread tries the same as we - (admin a db), it would then go and wait on LOCK_mysql_create_db... - Furthermore wait_if_global_read_lock() checks if the current thread - has the global read lock and refuses the operation with - ER_CANT_UPDATE_WITH_READLOCK if applicable. - */ - if (thd->global_read_lock.wait_if_global_read_lock(thd, FALSE, TRUE)) - { - error= -1; - goto exit2; - } - - mysql_mutex_lock(&LOCK_mysql_create_db); + if (lock_schema_name(thd, db)) + DBUG_RETURN(-1); /* Check directory */ path_len= build_table_filename(path, sizeof(path) - 1, db, "", "", 0); @@ -786,7 +658,10 @@ not_silent: qinfo.db = db; qinfo.db_len = strlen(db); - /* These DDL methods and logging protected with LOCK_mysql_create_db */ + /* + These DDL methods and logging are protected with the exclusive + metadata lock on the schema + */ if (mysql_bin_log.write(&qinfo)) { error= -1; @@ -797,9 +672,6 @@ not_silent: } exit: - mysql_mutex_unlock(&LOCK_mysql_create_db); - thd->global_read_lock.start_waiting_global_read_lock(thd); -exit2: DBUG_RETURN(error); } @@ -813,22 +685,8 @@ bool mysql_alter_db(THD *thd, const char *db, HA_CREATE_INFO *create_info) int error= 0; DBUG_ENTER("mysql_alter_db"); - /* - Do not alter database if another thread is holding read lock. - Wait for global read lock before acquiring LOCK_mysql_create_db. - After wait_if_global_read_lock() we have protection against another - global read lock. If we would acquire LOCK_mysql_create_db first, - another thread could step in and get the global read lock before we - reach wait_if_global_read_lock(). If this thread tries the same as we - (admin a db), it would then go and wait on LOCK_mysql_create_db... - Furthermore wait_if_global_read_lock() checks if the current thread - has the global read lock and refuses the operation with - ER_CANT_UPDATE_WITH_READLOCK if applicable. - */ - if ((error= thd->global_read_lock.wait_if_global_read_lock(thd, FALSE, TRUE))) - goto exit2; - - mysql_mutex_lock(&LOCK_mysql_create_db); + if (lock_schema_name(thd, db)) + DBUG_RETURN(TRUE); /* Recreate db options file: /dbpath/.db.opt @@ -866,16 +724,16 @@ bool mysql_alter_db(THD *thd, const char *db, HA_CREATE_INFO *create_info) qinfo.db = db; qinfo.db_len = strlen(db); - /* These DDL methods and logging protected with LOCK_mysql_create_db */ + /* + These DDL methods and logging are protected with the exclusive + metadata lock on the schema. + */ if ((error= mysql_bin_log.write(&qinfo))) goto exit; } my_ok(thd, result); exit: - mysql_mutex_unlock(&LOCK_mysql_create_db); - thd->global_read_lock.start_waiting_global_read_lock(thd); -exit2: DBUG_RETURN(error); } @@ -907,25 +765,9 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) TABLE_LIST* dropped_tables= 0; DBUG_ENTER("mysql_rm_db"); - /* - Do not drop database if another thread is holding read lock. - Wait for global read lock before acquiring LOCK_mysql_create_db. - After wait_if_global_read_lock() we have protection against another - global read lock. If we would acquire LOCK_mysql_create_db first, - another thread could step in and get the global read lock before we - reach wait_if_global_read_lock(). If this thread tries the same as we - (admin a db), it would then go and wait on LOCK_mysql_create_db... - Furthermore wait_if_global_read_lock() checks if the current thread - has the global read lock and refuses the operation with - ER_CANT_UPDATE_WITH_READLOCK if applicable. - */ - if (thd->global_read_lock.wait_if_global_read_lock(thd, FALSE, TRUE)) - { - error= -1; - goto exit2; - } - mysql_mutex_lock(&LOCK_mysql_create_db); + if (lock_schema_name(thd, db)) + DBUG_RETURN(TRUE); length= build_table_filename(path, sizeof(path) - 1, db, "", "", 0); strmov(path+length, MY_DB_OPT_FILE); // Append db option file name @@ -1013,7 +855,10 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) qinfo.db = db; qinfo.db_len = strlen(db); - /* These DDL methods and logging protected with LOCK_mysql_create_db */ + /* + These DDL methods and logging are protected with the exclusive + metadata lock on the schema. + */ if (mysql_bin_log.write(&qinfo)) { error= -1; @@ -1045,7 +890,10 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) tbl_name_len= strlen(tbl->table_name) + 3; if (query_pos + tbl_name_len + 1 >= query_end) { - /* These DDL methods and logging protected with LOCK_mysql_create_db */ + /* + These DDL methods and logging are protected with the exclusive + metadata lock on the schema. + */ if (write_to_binlog(thd, query, query_pos -1 - query, db, db_len)) { error= -1; @@ -1062,7 +910,10 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) if (query_pos != query_data_start) { - /* These DDL methods and logging protected with LOCK_mysql_create_db */ + /* + These DDL methods and logging are protected with the exclusive + metadata lock on the schema. + */ if (write_to_binlog(thd, query, query_pos -1 - query, db, db_len)) { error= -1; @@ -1080,9 +931,6 @@ exit: */ if (thd->db && !strcmp(thd->db, db) && error == 0) mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server); - mysql_mutex_unlock(&LOCK_mysql_create_db); - thd->global_read_lock.start_waiting_global_read_lock(thd); -exit2: DBUG_RETURN(error); } @@ -1099,12 +947,12 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db, long deleted=0; ulong found_other_files=0; char filePath[FN_REFLEN]; - TABLE_LIST *tot_list=0, **tot_list_next; + TABLE_LIST *tot_list=0, **tot_list_next_local, **tot_list_next_global; List raid_dirs; DBUG_ENTER("mysql_rm_known_files"); DBUG_PRINT("enter",("path: %s", org_path)); - tot_list_next= &tot_list; + tot_list_next_local= tot_list_next_global= &tot_list; for (uint idx=0 ; idx < (uint) dirp->number_off_files && !thd->killed ; @@ -1192,23 +1040,28 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db, if (!table_list) goto err; table_list->db= (char*) (table_list+1); - table_list->table_name= strmov(table_list->db, db) + 1; - (void) filename_to_tablename(file->name, table_list->table_name, - MYSQL50_TABLE_NAME_PREFIX_LENGTH + - strlen(file->name) + 1); + table_list->db_length= strmov(table_list->db, db) - table_list->db; + table_list->table_name= table_list->db + table_list->db_length + 1; + table_list->table_name_length= filename_to_tablename(file->name, + table_list->table_name, + MYSQL50_TABLE_NAME_PREFIX_LENGTH + + strlen(file->name) + 1); table_list->open_type= OT_BASE_ONLY; /* To be able to correctly look up the table in the table cache. */ if (lower_case_table_names) - my_casedn_str(files_charset_info, table_list->table_name); + table_list->table_name_length= my_casedn_str(files_charset_info, + table_list->table_name); table_list->alias= table_list->table_name; // If lower_case_table_names=2 table_list->internal_tmp_table= is_prefix(file->name, tmp_file_prefix); table_list->mdl_request.init(MDL_key::TABLE, table_list->db, table_list->table_name, MDL_EXCLUSIVE); /* Link into list */ - (*tot_list_next)= table_list; - tot_list_next= &table_list->next_local; + (*tot_list_next_local)= table_list; + (*tot_list_next_global)= table_list; + tot_list_next_local= &table_list->next_local; + tot_list_next_global= &table_list->next_global; deleted++; } else @@ -1771,60 +1624,6 @@ bool mysql_opt_change_db(THD *thd, } -static int -lock_databases(THD *thd, const char *db1, uint length1, - const char *db2, uint length2) -{ - mysql_mutex_lock(&LOCK_lock_db); - while (!thd->killed && - (my_hash_search(&lock_db_cache,(uchar*) db1, length1) || - my_hash_search(&lock_db_cache,(uchar*) db2, length2))) - { - wait_for_condition(thd, &LOCK_lock_db, &COND_refresh); - mysql_mutex_lock(&LOCK_lock_db); - } - - if (thd->killed) - { - mysql_mutex_unlock(&LOCK_lock_db); - return 1; - } - - lock_db_insert(db1, length1); - lock_db_insert(db2, length2); - creating_database++; - - /* - Wait if a concurent thread is creating a table at the same time. - The assumption here is that it will not take too long until - there is a point in time when a table is not created. - */ - - while (!thd->killed && creating_table) - { - wait_for_condition(thd, &LOCK_lock_db, &COND_refresh); - mysql_mutex_lock(&LOCK_lock_db); - } - - if (thd->killed) - { - lock_db_delete(db1, length1); - lock_db_delete(db2, length2); - creating_database--; - mysql_mutex_unlock(&LOCK_lock_db); - mysql_cond_signal(&COND_refresh); - return(1); - } - - /* - We can unlock now as the hash will protect against anyone creating a table - in the databases we are using - */ - mysql_mutex_unlock(&LOCK_lock_db); - return 0; -} - - /** Upgrade a 5.0 database. This function is invoked whenever an ALTER DATABASE UPGRADE query is executed: @@ -1866,9 +1665,9 @@ bool mysql_upgrade_db(THD *thd, LEX_STRING *old_db) new_db.str= old_db->str + MYSQL50_TABLE_NAME_PREFIX_LENGTH; new_db.length= old_db->length - MYSQL50_TABLE_NAME_PREFIX_LENGTH; - if (lock_databases(thd, old_db->str, old_db->length, - new_db.str, new_db.length)) - DBUG_RETURN(1); + /* Lock the old name, the new name will be locked by mysql_create_db().*/ + if (lock_schema_name(thd, old_db->str)) + DBUG_RETURN(-1); /* Let's remember if we should do "USE newdb" afterwards. @@ -2035,15 +1834,6 @@ bool mysql_upgrade_db(THD *thd, LEX_STRING *old_db) error|= mysql_change_db(thd, & new_db, FALSE); exit: - mysql_mutex_lock(&LOCK_lock_db); - /* Remove the databases from db lock cache */ - lock_db_delete(old_db->str, old_db->length); - lock_db_delete(new_db.str, new_db.length); - creating_database--; - /* Signal waiting CREATE TABLE's to continue */ - mysql_cond_signal(&COND_refresh); - mysql_mutex_unlock(&LOCK_lock_db); - DBUG_RETURN(error); } diff --git a/sql/sql_db.h b/sql/sql_db.h index 96b3de80d3a..ecb8deaa397 100644 --- a/sql/sql_db.h +++ b/sql/sql_db.h @@ -35,8 +35,8 @@ bool mysql_opt_change_db(THD *thd, LEX_STRING *saved_db_name, bool force_switch, bool *cur_db_changed); -bool my_database_names_init(void); -void my_database_names_free(void); +bool my_dboptions_cache_init(void); +void my_dboptions_cache_free(void); bool check_db_dir_existence(const char *db_name); bool load_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create); bool load_db_opt_by_name(THD *thd, const char *db_name, @@ -45,9 +45,6 @@ CHARSET_INFO *get_default_db_collation(THD *thd, const char *db_name); bool my_dbopt_init(void); void my_dbopt_cleanup(void); -extern int creating_database; // How many database locks are made -extern HASH lock_db_cache; - #define MY_DB_OPT_FILE "db.opt" #endif /* SQL_DB_INCLUDED */ diff --git a/sql/sql_help.cc b/sql/sql_help.cc index 7bea236269a..4e3df950134 100644 --- a/sql/sql_help.cc +++ b/sql/sql_help.cc @@ -643,23 +643,24 @@ bool mysqld_help(THD *thd, const char *mask) MEM_ROOT *mem_root= thd->mem_root; DBUG_ENTER("mysqld_help"); - bzero((uchar*)tables,sizeof(tables)); - tables[0].alias= tables[0].table_name= (char*) "help_topic"; - tables[0].lock_type= TL_READ; + tables[0].init_one_table(C_STRING_WITH_LEN("mysql"), + C_STRING_WITH_LEN("help_topic"), + "help_topic", TL_READ); + tables[1].init_one_table(C_STRING_WITH_LEN("mysql"), + C_STRING_WITH_LEN("help_category"), + "help_category", TL_READ); + tables[2].init_one_table(C_STRING_WITH_LEN("mysql"), + C_STRING_WITH_LEN("help_relation"), + "help_relation", TL_READ); + tables[3].init_one_table(C_STRING_WITH_LEN("mysql"), + C_STRING_WITH_LEN("help_keyword"), + "help_keyword", TL_READ); tables[0].next_global= tables[0].next_local= tables[0].next_name_resolution_table= &tables[1]; - tables[1].alias= tables[1].table_name= (char*) "help_category"; - tables[1].lock_type= TL_READ; tables[1].next_global= tables[1].next_local= tables[1].next_name_resolution_table= &tables[2]; - tables[2].alias= tables[2].table_name= (char*) "help_relation"; - tables[2].lock_type= TL_READ; tables[2].next_global= tables[2].next_local= tables[2].next_name_resolution_table= &tables[3]; - tables[3].alias= tables[3].table_name= (char*) "help_keyword"; - tables[3].lock_type= TL_READ; - tables[0].db= tables[1].db= tables[2].db= tables[3].db= (char*) "mysql"; - init_mdl_requests(tables); /* HELP must be available under LOCK TABLES. diff --git a/sql/sql_hset.h b/sql/sql_hset.h new file mode 100644 index 00000000000..c3b98b881f5 --- /dev/null +++ b/sql/sql_hset.h @@ -0,0 +1,117 @@ +#ifndef SQL_HSET_INCLUDED +#define SQL_HSET_INCLUDED +/* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + + 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 Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "my_global.h" +#include "hash.h" + + +/** + A type-safe wrapper around mysys HASH. +*/ + +template +class Hash_set +{ +public: + typedef T Value_type; + enum { START_SIZE= 8 }; + /** + Constructs an empty hash. Does not allocate memory, it is done upon + the first insert. Thus does not cause or return errors. + */ + Hash_set(); + /** + Destroy the hash by freeing the buckets table. Does + not call destructors for the elements. + */ + ~Hash_set(); + /** + Insert a single value into a hash. Does not tell whether + the value was inserted -- if an identical value existed, + it is not replaced. + + @retval TRUE Out of memory. + @retval FALSE OK. The value either was inserted or existed + in the hash. + */ + bool insert(T *value); + /** Is this hash set empty? */ + bool is_empty() const { return m_hash.records == 0; } + /** Returns the number of unique elements. */ + size_t size() const { return static_cast(m_hash.records); } + /** An iterator over hash elements. Is not insert-stable. */ + class Iterator + { + public: + Iterator(Hash_set &hash_set); + /** + Return the current element and reposition the iterator to the next + element. + */ + inline T *operator++(int); + void rewind() { m_idx= 0; } + private: + HASH *m_hash; + uint m_idx; + }; +private: + HASH m_hash; +}; + + +template +Hash_set::Hash_set() +{ + my_hash_clear(&m_hash); +} + + +template +Hash_set::~Hash_set() +{ + my_hash_free(&m_hash); +} + + +template +bool Hash_set::insert(T *value) +{ + my_hash_init_opt(&m_hash, &my_charset_bin, START_SIZE, 0, 0, K, 0, MYF(0)); + size_t key_len; + const uchar *key= K(reinterpret_cast(value), &key_len, FALSE); + if (my_hash_search(&m_hash, key, key_len) == NULL) + return my_hash_insert(&m_hash, reinterpret_cast(value)); + return FALSE; +} + + +template +Hash_set::Iterator::Iterator(Hash_set &set_arg) + :m_hash(&set_arg.m_hash), + m_idx(0) +{} + + +template +inline T *Hash_set::Iterator::operator++(int) +{ + if (m_idx < m_hash->records) + return reinterpret_cast(my_hash_element(m_hash, m_idx++)); + return NULL; +} + +#endif // SQL_HSET_INCLUDED diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 273eadf4205..31eaf4111ce 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -270,10 +270,10 @@ void init_update_queries(void) sql_command_flags[SQLCOM_LOAD]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE | CF_PROTECT_AGAINST_GRL | CF_CAN_GENERATE_ROW_EVENTS; - sql_command_flags[SQLCOM_CREATE_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; - sql_command_flags[SQLCOM_DROP_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; - sql_command_flags[SQLCOM_ALTER_DB_UPGRADE]= CF_AUTO_COMMIT_TRANS; - sql_command_flags[SQLCOM_ALTER_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_CREATE_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS | CF_PROTECT_AGAINST_GRL; + sql_command_flags[SQLCOM_DROP_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS | CF_PROTECT_AGAINST_GRL; + sql_command_flags[SQLCOM_ALTER_DB_UPGRADE]= CF_AUTO_COMMIT_TRANS | CF_PROTECT_AGAINST_GRL; + sql_command_flags[SQLCOM_ALTER_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS | CF_PROTECT_AGAINST_GRL; sql_command_flags[SQLCOM_RENAME_TABLE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_DROP_INDEX]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_CREATE_VIEW]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE | @@ -1803,7 +1803,8 @@ static bool flush_tables_with_read_lock(THD *thd, TABLE_LIST *all_tables) current internal MDL asserts, fix after discussing with Dmitry. */ - if (lock_table_names(thd, all_tables)) + if (lock_table_names(thd, all_tables, 0, thd->variables.lock_wait_timeout, + MYSQL_OPEN_SKIP_TEMPORARY)) goto error; for (table_list= all_tables; table_list; @@ -3644,12 +3645,6 @@ end_with_restore_list: #endif if (check_access(thd, CREATE_ACL, lex->name.str, NULL, NULL, 1, 0)) break; - if (thd->locked_tables_mode) - { - my_message(ER_LOCK_OR_ACTIVE_TRANSACTION, - ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0)); - goto error; - } res= mysql_create_db(thd,(lower_case_table_names == 2 ? alias : lex->name.str), &create_info, 0); break; @@ -3679,12 +3674,6 @@ end_with_restore_list: #endif if (check_access(thd, DROP_ACL, lex->name.str, NULL, NULL, 1, 0)) break; - if (thd->locked_tables_mode) - { - my_message(ER_LOCK_OR_ACTIVE_TRANSACTION, - ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0)); - goto error; - } res= mysql_rm_db(thd, lex->name.str, lex->drop_if_exists, 0); break; } @@ -3713,14 +3702,6 @@ end_with_restore_list: res= 1; break; } - if (thd->locked_tables_mode) - { - res= 1; - my_message(ER_LOCK_OR_ACTIVE_TRANSACTION, - ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0)); - goto error; - } - res= mysql_upgrade_db(thd, db); if (!res) my_ok(thd); @@ -3753,12 +3734,6 @@ end_with_restore_list: #endif if (check_access(thd, ALTER_ACL, db->str, NULL, NULL, 1, 0)) break; - if (thd->locked_tables_mode) - { - my_message(ER_LOCK_OR_ACTIVE_TRANSACTION, - ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0)); - goto error; - } res= mysql_alter_db(thd, db->str, &create_info); break; } diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc index 130a99a374f..301b22bd70e 100644 --- a/sql/sql_rename.cc +++ b/sql/sql_rename.cc @@ -24,10 +24,9 @@ #include "sql_table.h" // build_table_filename #include "sql_view.h" // mysql_frm_type, mysql_rename_view #include "sql_trigger.h" -#include "lock.h" // wait_if_global_read_lock, lock_table_names, - // unlock_table_names, +#include "lock.h" // wait_if_global_read_lock // start_waiting_global_read_lock -#include "sql_base.h" // tdc_remove_table +#include "sql_base.h" // tdc_remove_table, lock_table_names, #include "sql_handler.h" // mysql_ha_rm_tables #include "datadict.h" @@ -144,7 +143,8 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent) } } - if (lock_table_names(thd, table_list)) + if (lock_table_names(thd, table_list, 0, thd->variables.lock_wait_timeout, + MYSQL_OPEN_SKIP_TEMPORARY)) goto err; mysql_mutex_lock(&LOCK_open); @@ -197,7 +197,7 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent) if (!error) query_cache_invalidate3(thd, table_list, 0); - unlock_table_names(thd); + thd->mdl_context.release_transactional_locks(); err: thd->global_read_lock.start_waiting_global_read_lock(thd); diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 97c8f59d7df..74acd134910 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -22,17 +22,17 @@ #include "sql_rename.h" // do_rename #include "sql_parse.h" // test_if_data_home_dir #include "sql_cache.h" // query_cache_* -#include "sql_base.h" // open_temporary_table -#include "lock.h" // wait_if_global_read_lock, lock_table_names, +#include "sql_base.h" // open_temporary_table, lock_table_names +#include "lock.h" // wait_if_global_read_lock // start_waiting_global_read_lock, - // unlock_table_names, mysql_unlock_tables + // mysql_unlock_tables #include "strfunc.h" // find_type2, find_set #include "sql_view.h" // view_checksum #include "sql_truncate.h" // regenerate_locked_table #include "sql_partition.h" // mem_alloc_error, // generate_partition_syntax, // partition_info -#include "sql_db.h" // load_db_opt_by_name, lock_db_cache, creating_database +#include "sql_db.h" // load_db_opt_by_name #include "sql_time.h" // make_truncated_value_warning #include "records.h" // init_read_record, end_read_record #include "filesort.h" // filesort_free_buffers @@ -58,8 +58,6 @@ #include #endif -int creating_table= 0; // How many mysql_create_table are running - const char *primary_key_name="PRIMARY"; static bool check_if_keyname_exists(const char *name,KEY *start, KEY *end); @@ -1954,7 +1952,8 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, { if (!thd->locked_tables_mode) { - if (lock_table_names(thd, tables)) + if (lock_table_names(thd, tables, NULL, thd->variables.lock_wait_timeout, + MYSQL_OPEN_SKIP_TEMPORARY)) DBUG_RETURN(1); mysql_mutex_lock(&LOCK_open); for (table= tables; table; table= table->next_local) @@ -2296,7 +2295,7 @@ err: leaving LOCK TABLES mode if we have dropped only temporary tables. */ if (! thd->locked_tables_mode) - unlock_table_names(thd); + thd->mdl_context.release_transactional_locks(); else { if (thd->lock && thd->lock->table_count == 0 && non_temp_tables_count > 0) @@ -4226,24 +4225,6 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table, bool result; DBUG_ENTER("mysql_create_table"); - /* Wait for any database locks */ - mysql_mutex_lock(&LOCK_lock_db); - while (!thd->killed && - my_hash_search(&lock_db_cache, (uchar*)create_table->db, - create_table->db_length)) - { - wait_for_condition(thd, &LOCK_lock_db, &COND_refresh); - mysql_mutex_lock(&LOCK_lock_db); - } - - if (thd->killed) - { - mysql_mutex_unlock(&LOCK_lock_db); - DBUG_RETURN(TRUE); - } - creating_table++; - mysql_mutex_unlock(&LOCK_lock_db); - /* Open or obtain an exclusive metadata lock on table being created. */ @@ -4282,10 +4263,6 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table, } unlock: - mysql_mutex_lock(&LOCK_lock_db); - if (!--creating_table && creating_database) - mysql_cond_signal(&COND_refresh); - mysql_mutex_unlock(&LOCK_lock_db); DBUG_RETURN(result); } @@ -4458,8 +4435,6 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, { char key[MAX_DBKEY_LENGTH]; uint key_length; - MDL_request mdl_global_request; - MDL_request_list mdl_requests; /* If the table didn't exist, we have a shared metadata lock on it that is left from mysql_admin_table()'s attempt to @@ -4479,12 +4454,9 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, table_list->db, table_list->table_name, MDL_EXCLUSIVE); - mdl_global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE); - mdl_requests.push_front(&table_list->mdl_request); - mdl_requests.push_front(&mdl_global_request); - - if (thd->mdl_context.acquire_locks(&mdl_requests, - thd->variables.lock_wait_timeout)) + if (lock_table_names(thd, table_list, table_list->next_global, + thd->variables.lock_wait_timeout, + MYSQL_OPEN_SKIP_TEMPORARY)) DBUG_RETURN(0); has_mdl_lock= TRUE; @@ -6577,6 +6549,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, Alter_table_prelocking_strategy alter_prelocking_strategy(alter_info); + DEBUG_SYNC(thd, "alter_table_before_open_tables"); error= open_and_lock_tables(thd, table_list, FALSE, 0, &alter_prelocking_strategy); diff --git a/sql/sql_table.h b/sql/sql_table.h index 40b24605bd6..dca4b706605 100644 --- a/sql/sql_table.h +++ b/sql/sql_table.h @@ -210,7 +210,6 @@ uint explain_filename(THD* thd, const char *from, char *to, uint to_length, extern MYSQL_PLUGIN_IMPORT const char *primary_key_name; -extern int creating_table; // How many mysql_create_table() are running extern mysql_mutex_t LOCK_gdl; #endif /* SQL_TABLE_INCLUDED */ diff --git a/sql/sql_truncate.cc b/sql/sql_truncate.cc index 901ab8e987d..6bbf86cbe55 100644 --- a/sql/sql_truncate.cc +++ b/sql/sql_truncate.cc @@ -242,9 +242,10 @@ static bool open_and_lock_table_for_truncate(THD *thd, TABLE_LIST *table_ref, MDL_ticket **ticket_downgrade) { TABLE *table= NULL; - MDL_ticket *mdl_ticket= NULL; DBUG_ENTER("open_and_lock_table_for_truncate"); + DBUG_ASSERT(table_ref->lock_type == TL_WRITE); + DBUG_ASSERT(table_ref->mdl_request.type == MDL_SHARED_NO_READ_WRITE); /* Before doing anything else, acquire a metadata lock on the table, or ensure we have one. We don't use open_and_lock_tables() @@ -266,6 +267,7 @@ static bool open_and_lock_table_for_truncate(THD *thd, TABLE_LIST *table_ref, *hton_can_recreate= ha_check_storage_engine_flag(table->s->db_type(), HTON_CAN_RECREATE); + table_ref->mdl_request.ticket= table->mdl_ticket; } else { @@ -273,21 +275,12 @@ static bool open_and_lock_table_for_truncate(THD *thd, TABLE_LIST *table_ref, Even though we could use the previous execution branch here just as well, we must not try to open the table: */ - MDL_request mdl_global_request, mdl_request; - MDL_request_list mdl_requests; - - mdl_global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE); - mdl_request.init(MDL_key::TABLE, table_ref->db, table_ref->table_name, - MDL_SHARED_NO_READ_WRITE); - mdl_requests.push_front(&mdl_request); - mdl_requests.push_front(&mdl_global_request); - - if (thd->mdl_context.acquire_locks(&mdl_requests, - thd->variables.lock_wait_timeout)) + DBUG_ASSERT(table_ref->next_global == NULL); + if (lock_table_names(thd, table_ref, NULL, + thd->variables.lock_wait_timeout, + MYSQL_OPEN_SKIP_TEMPORARY)) DBUG_RETURN(TRUE); - mdl_ticket= mdl_request.ticket; - if (dd_check_storage_engine_flag(thd, table_ref->db, table_ref->table_name, HTON_CAN_RECREATE, hton_can_recreate)) DBUG_RETURN(TRUE); @@ -313,7 +306,9 @@ static bool open_and_lock_table_for_truncate(THD *thd, TABLE_LIST *table_ref, else { ulong timeout= thd->variables.lock_wait_timeout; - if (thd->mdl_context.upgrade_shared_lock_to_exclusive(mdl_ticket, timeout)) + if (thd->mdl_context. + upgrade_shared_lock_to_exclusive(table_ref->mdl_request.ticket, + timeout)) DBUG_RETURN(TRUE); mysql_mutex_lock(&LOCK_open); tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table_ref->db, @@ -335,15 +330,14 @@ static bool open_and_lock_table_for_truncate(THD *thd, TABLE_LIST *table_ref, table_ref->required_type= FRMTYPE_TABLE; /* We don't need to load triggers. */ DBUG_ASSERT(table_ref->trg_event_map == 0); - /* Work around partition parser rules using alter table's. */ - if (thd->lex->alter_info.flags & ALTER_ADMIN_PARTITION) - { - table_ref->lock_type= TL_WRITE; - table_ref->mdl_request.set_type(MDL_SHARED_WRITE); - } - /* Ensure proper lock types (e.g. from the parser). */ - DBUG_ASSERT(table_ref->lock_type == TL_WRITE); - DBUG_ASSERT(table_ref->mdl_request.type == MDL_SHARED_WRITE); + /* + Even though we have an MDL lock on the table here, we don't + pass MYSQL_OPEN_HAS_MDL_LOCK to open_and_lock_tables + since to truncate a MERGE table, we must open and lock + merge children, and on those we don't have an MDL lock. + Thus clear the ticket to satisfy MDL asserts. + */ + table_ref->mdl_request.ticket= NULL; /* Open the table as it will handle some required preparations. diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 69abe70e863..be13349b5a1 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -19,10 +19,10 @@ #include "sql_priv.h" #include "unireg.h" #include "sql_view.h" -#include "sql_base.h" // find_table_in_global_list +#include "sql_base.h" // find_table_in_global_list, lock_table_names #include "sql_parse.h" // sql_parse #include "sql_cache.h" // query_cache_* -#include "lock.h" // wait_if_global_read_lock, lock_table_names +#include "lock.h" // wait_if_global_read_lock #include "sql_show.h" // append_identifier #include "sql_table.h" // build_table_filename #include "sql_db.h" // mysql_opt_change_db, mysql_change_db @@ -1652,7 +1652,8 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode) DBUG_RETURN(TRUE); } - if (lock_table_names(thd, views)) + if (lock_table_names(thd, views, 0, thd->variables.lock_wait_timeout, + MYSQL_OPEN_SKIP_TEMPORARY)) DBUG_RETURN(TRUE); mysql_mutex_lock(&LOCK_open); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 73935d2a0b1..934ec9c35ab 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -6442,6 +6442,8 @@ alter_commands: lex->sql_command= SQLCOM_TRUNCATE; lex->alter_info.flags|= ALTER_ADMIN_PARTITION; lex->check_opt.init(); + lex->query_tables->mdl_request.set_type(MDL_SHARED_NO_READ_WRITE); + lex->query_tables->lock_type= TL_WRITE; } | reorg_partition_rule ; @@ -10695,7 +10697,7 @@ truncate: lex->select_lex.sql_cache= SELECT_LEX::SQL_CACHE_UNSPECIFIED; lex->select_lex.init_order(); YYPS->m_lock_type= TL_WRITE; - YYPS->m_mdl_type= MDL_SHARED_WRITE; + YYPS->m_mdl_type= MDL_SHARED_NO_READ_WRITE; } table_name {} From 8ad1aa562f70374e49a3550f4d0f9a16cddfd4d2 Mon Sep 17 00:00:00 2001 From: Dmitry Lenev Date: Thu, 1 Jul 2010 18:58:47 +0400 Subject: [PATCH 057/115] A follow-up for 5.5 version of fix for bug#54360 "Deadlock DROP/ALTER/CREATE DATABASE with open HANDLER". Remove wait_for_condition() which became unused after database locks were replaced with MDL scoped locks. If one needs functionality provided by this call one can always use THD::enter_cond()/exit_cond() methods. Also removed an unused include from sql_db.cc and updated comment describing one of used includes to reflect current situation. --- sql/sql_base.cc | 45 --------------------------------------------- sql/sql_base.h | 2 -- sql/sql_db.cc | 4 +--- 3 files changed, 1 insertion(+), 50 deletions(-) diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 426c9db2717..ea688ad6e0f 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -2412,51 +2412,6 @@ void drop_open_table(THD *thd, TABLE *table, const char *db_name, } -/* - Wait for condition but allow the user to send a kill to mysqld - - SYNOPSIS - wait_for_condition() - thd Thread handler - mutex mutex that is currently hold that is associated with condition - Will be unlocked on return - cond Condition to wait for -*/ - -void wait_for_condition(THD *thd, mysql_mutex_t *mutex, mysql_cond_t *cond) -{ - /* Wait until the current table is up to date */ - const char *proc_info; - thd->mysys_var->current_mutex= mutex; - thd->mysys_var->current_cond= cond; - proc_info=thd->proc_info; - thd_proc_info(thd, "Waiting for table"); - DBUG_ENTER("wait_for_condition"); - DEBUG_SYNC(thd, "waiting_for_table"); - if (!thd->killed) - mysql_cond_wait(cond, mutex); - - /* - We must unlock mutex first to avoid deadlock becasue conditions are - sent to this thread by doing locks in the following order: - lock(mysys_var->mutex) - lock(mysys_var->current_mutex) - - One by effect of this that one can only use wait_for_condition with - condition variables that are guranteed to not disapper (freed) even if this - mutex is unlocked - */ - - mysql_mutex_unlock(mutex); - mysql_mutex_lock(&thd->mysys_var->mutex); - thd->mysys_var->current_mutex= 0; - thd->mysys_var->current_cond= 0; - thd_proc_info(thd, proc_info); - mysql_mutex_unlock(&thd->mysys_var->mutex); - DBUG_VOID_RETURN; -} - - /** Check that table exists in table definition cache, on disk or in some storage engine. diff --git a/sql/sql_base.h b/sql/sql_base.h index 67e5601663a..24669bd2597 100644 --- a/sql/sql_base.h +++ b/sql/sql_base.h @@ -203,8 +203,6 @@ int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves, COND **conds); int setup_ftfuncs(SELECT_LEX* select); int init_ftfuncs(THD *thd, SELECT_LEX* select, bool no_order); -void wait_for_condition(THD *thd, mysql_mutex_t *mutex, - mysql_cond_t *cond); bool lock_table_names(THD *thd, TABLE_LIST *table_list, TABLE_LIST *table_list_end, ulong lock_wait_timeout, uint flags); diff --git a/sql/sql_db.cc b/sql/sql_db.cc index c4a7dc53972..fe4b112196c 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -21,14 +21,12 @@ #include "unireg.h" #include "sql_db.h" #include "sql_cache.h" // query_cache_* -#include "lock.h" // wait_if_global_read_lock, - // start_waiting_global_read_lock +#include "lock.h" // lock_schema_name #include "sql_table.h" // build_table_filename, // filename_to_tablename #include "sql_rename.h" // mysql_rename_tables #include "sql_acl.h" // SELECT_ACL, DB_ACLS, // acl_get, check_grant_db -#include "sql_base.h" // wait_for_condition #include "log_event.h" // Query_log_event #include #include "sp.h" From f84ec55e0873d8d61e63b5edd1484b935060efda Mon Sep 17 00:00:00 2001 From: Dmitry Lenev Date: Thu, 1 Jul 2010 19:48:56 +0400 Subject: [PATCH 058/115] Another follow-up for 5.5 version of fix for bug#54360 "Deadlock DROP/ALTER/CREATE DATABASE with open HANDLER". Fixes production build which was broken by the fix for bug#54360 due to missing instantiation of some Hash_set template's methods. Circumvent requirement of explicit instantiation of non-inline methods by making all Hash_set methods inline. --- sql/sql_hset.h | 74 ++++++++++++++++++-------------------------------- 1 file changed, 27 insertions(+), 47 deletions(-) diff --git a/sql/sql_hset.h b/sql/sql_hset.h index c3b98b881f5..2ea70b91da8 100644 --- a/sql/sql_hset.h +++ b/sql/sql_hset.h @@ -33,12 +33,18 @@ public: Constructs an empty hash. Does not allocate memory, it is done upon the first insert. Thus does not cause or return errors. */ - Hash_set(); + Hash_set() + { + my_hash_clear(&m_hash); + } /** Destroy the hash by freeing the buckets table. Does not call destructors for the elements. */ - ~Hash_set(); + ~Hash_set() + { + my_hash_free(&m_hash); + } /** Insert a single value into a hash. Does not tell whether the value was inserted -- if an identical value existed, @@ -48,7 +54,15 @@ public: @retval FALSE OK. The value either was inserted or existed in the hash. */ - bool insert(T *value); + bool insert(T *value) + { + my_hash_init_opt(&m_hash, &my_charset_bin, START_SIZE, 0, 0, K, 0, MYF(0)); + size_t key_len; + const uchar *key= K(reinterpret_cast(value), &key_len, FALSE); + if (my_hash_search(&m_hash, key, key_len) == NULL) + return my_hash_insert(&m_hash, reinterpret_cast(value)); + return FALSE; + } /** Is this hash set empty? */ bool is_empty() const { return m_hash.records == 0; } /** Returns the number of unique elements. */ @@ -57,12 +71,20 @@ public: class Iterator { public: - Iterator(Hash_set &hash_set); + Iterator(Hash_set &hash_set) + : m_hash(&hash_set.m_hash), + m_idx(0) + {} /** Return the current element and reposition the iterator to the next element. */ - inline T *operator++(int); + inline T *operator++(int) + { + if (m_idx < m_hash->records) + return reinterpret_cast(my_hash_element(m_hash, m_idx++)); + return NULL; + } void rewind() { m_idx= 0; } private: HASH *m_hash; @@ -72,46 +94,4 @@ private: HASH m_hash; }; - -template -Hash_set::Hash_set() -{ - my_hash_clear(&m_hash); -} - - -template -Hash_set::~Hash_set() -{ - my_hash_free(&m_hash); -} - - -template -bool Hash_set::insert(T *value) -{ - my_hash_init_opt(&m_hash, &my_charset_bin, START_SIZE, 0, 0, K, 0, MYF(0)); - size_t key_len; - const uchar *key= K(reinterpret_cast(value), &key_len, FALSE); - if (my_hash_search(&m_hash, key, key_len) == NULL) - return my_hash_insert(&m_hash, reinterpret_cast(value)); - return FALSE; -} - - -template -Hash_set::Iterator::Iterator(Hash_set &set_arg) - :m_hash(&set_arg.m_hash), - m_idx(0) -{} - - -template -inline T *Hash_set::Iterator::operator++(int) -{ - if (m_idx < m_hash->records) - return reinterpret_cast(my_hash_element(m_hash, m_idx++)); - return NULL; -} - #endif // SQL_HSET_INCLUDED From d93301eba10fd9425b35be841ee977183b969990 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Fri, 2 Jul 2010 11:26:27 +0200 Subject: [PATCH 059/115] Followup to Bug #54360 Deadlock DROP/ALTER/CREATE DATABASE with open HANDLER Fixes problem which caused mdl_sync.test to fail on Solaris and Windows due to path name differences in error messages in the result file. --- mysql-test/r/mdl_sync.result | 6 +++--- mysql-test/t/mdl_sync.test | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/mysql-test/r/mdl_sync.result b/mysql-test/r/mdl_sync.result index 1b0ffc588e1..0fd408b0208 100644 --- a/mysql-test/r/mdl_sync.result +++ b/mysql-test/r/mdl_sync.result @@ -2649,7 +2649,7 @@ SET DEBUG_SYNC= 'now SIGNAL blocked'; # Reaping: DROP DATABASE db1 # Connection con2 # Reaping: ALTER DATABASE db1 DEFAULT CHARACTER SET utf8 -ERROR HY000: Can't create/write to file './db1/db.opt' (Errcode: 2) +Got one of the listed errors # Test 5: # Locked database name prevents CREATE of tables in that database. # Tests X vs IX lock. @@ -2690,7 +2690,7 @@ SET DEBUG_SYNC= 'now SIGNAL blocked'; # Reaping: DROP DATABASE db1 # Connection con2 # Reaping: RENAME TABLE db1.t1 TO test.t1 -ERROR HY000: Can't find file: './db1/t1.frm' (errno: 2) +Got one of the listed errors # Connection default CREATE DATABASE db1; CREATE TABLE test.t2 (a INT); @@ -2707,7 +2707,7 @@ SET DEBUG_SYNC= 'now SIGNAL blocked'; # Reaping: DROP DATABASE db1 # Connection con2 # Reaping: RENAME TABLE test.t2 TO db1.t2 -ERROR HY000: Error on rename of './test/t2.MYI' to './db1/t2.MYI' (Errcode: 2) +Got one of the listed errors DROP TABLE test.t2; # Test 7: # Locked database name prevents DROP of tables in that database. diff --git a/mysql-test/t/mdl_sync.test b/mysql-test/t/mdl_sync.test index 441cabb10e2..6b721ace07f 100644 --- a/mysql-test/t/mdl_sync.test +++ b/mysql-test/t/mdl_sync.test @@ -3947,7 +3947,7 @@ connection default; --echo # Connection con2 connection con2; --echo # Reaping: ALTER DATABASE db1 DEFAULT CHARACTER SET utf8 ---error 1 # Wrong error pending followup patch for bug#54360 +--error 1,1 # Wrong error pending followup patch for bug#54360 --reap @@ -4023,7 +4023,7 @@ connection default; --echo # Connection con2 connection con2; --echo # Reaping: RENAME TABLE db1.t1 TO test.t1 ---error ER_FILE_NOT_FOUND +--error ER_FILE_NOT_FOUND, ER_FILE_NOT_FOUND --reap --echo # Connection default @@ -4056,7 +4056,7 @@ connection default; --echo # Connection con2 connection con2; --echo # Reaping: RENAME TABLE test.t2 TO db1.t2 ---error 7 # Wrong error pending followup patch for bug#54360 +--error 7, 7 # Wrong error pending followup patch for bug#54360 --reap DROP TABLE test.t2; From 4e2a2bc93e0da353aa1a3dcc9259f4d31a5aab54 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Fri, 2 Jul 2010 11:54:14 +0200 Subject: [PATCH 060/115] Followup for Bug #54360 Deadlock DROP/ALTER/CREATE DATABASE with open HANDLER Fixes a problem with schema.test visible using embedded server. The HANDLER was not closed which caused the test to hang. The problem was not visible if the test was run on a normal server as the the handler there was implicitly closed by DATABASE DDL statements doing Events::drop_schema_events(). --- mysql-test/r/schema.result | 1 + mysql-test/t/schema.test | 1 + 2 files changed, 2 insertions(+) diff --git a/mysql-test/r/schema.result b/mysql-test/r/schema.result index 890f53669b5..853c3bcf575 100644 --- a/mysql-test/r/schema.result +++ b/mysql-test/r/schema.result @@ -66,6 +66,7 @@ DROP DATABASE db1; CREATE DATABASE db2; ALTER DATABASE db2 DEFAULT CHARACTER SET utf8; DROP DATABASE db2; +HANDLER t1 CLOSE; # Connection default # Reaping: DROP DATABASE db1 # diff --git a/mysql-test/t/schema.test b/mysql-test/t/schema.test index e3592f39780..ed3b98ec2f7 100644 --- a/mysql-test/t/schema.test +++ b/mysql-test/t/schema.test @@ -133,6 +133,7 @@ connection con1; CREATE DATABASE db2; ALTER DATABASE db2 DEFAULT CHARACTER SET utf8; DROP DATABASE db2; +HANDLER t1 CLOSE; --echo # Connection default connection default; From 0e9b910d6b7442a42f2aea0a0043327fff44b267 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Fri, 2 Jul 2010 19:21:07 +0400 Subject: [PATCH 061/115] A test case for Bug#50788 "main.merge fails on HPUX", and a backport of relevant changes from the 6.0 version of the fix done by Ingo Struewing. The bug itself was fixed by the patch for Bug#54811. MyISAMMRG engine would try to use MMAP on its children even on platforms that don't support it and even if myisam_use_mmap option was off. This lead to an infinite hang in INSERT ... SELECT into a MyISAMMRG table when the destination MyISAM table was also selected from. A bug in duplicate detection fixed by 54811 was essential to the hang - when a duplicate is detected, the optimizer disables the use of memory mapped files, and it wasn't the case. The patch below is also to not turn on MMAP on children tables if myisam_use_mmap is off. A test case is added to cover MyISAMMRG and myisam_use_mmap option. --- mysql-test/r/merge_mmap.result | 64 ++++++++++++++++++++++++++++++ mysql-test/t/merge_mmap-master.opt | 1 + mysql-test/t/merge_mmap.test | 60 ++++++++++++++++++++++++++++ storage/myisam/mi_statrec.c | 13 +++++- storage/myisammrg/ha_myisammrg.cc | 2 + 5 files changed, 138 insertions(+), 2 deletions(-) create mode 100644 mysql-test/r/merge_mmap.result create mode 100644 mysql-test/t/merge_mmap-master.opt create mode 100644 mysql-test/t/merge_mmap.test diff --git a/mysql-test/r/merge_mmap.result b/mysql-test/r/merge_mmap.result new file mode 100644 index 00000000000..d975a1be377 --- /dev/null +++ b/mysql-test/r/merge_mmap.result @@ -0,0 +1,64 @@ +SET GLOBAL storage_engine = MyISAM; +SET SESSION storage_engine = MyISAM; +DROP TABLE IF EXISTS t1, t2, m1, m2; +CREATE TABLE t1 (c1 INT); +CREATE TABLE t2 (c1 INT); +CREATE TABLE m1 (c1 INT) ENGINE=MRG_MyISAM UNION=(t1,t2) +INSERT_METHOD=LAST; +CREATE TABLE m2 (c1 INT) ENGINE=MRG_MyISAM UNION=(t1,t2) +INSERT_METHOD=LAST; +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2), (3), (4); +INSERT INTO m2 SELECT * FROM m1; +SELECT * FROM m2; +c1 +1 +2 +3 +4 +1 +2 +3 +4 +SELECT * FROM t2; +c1 +2 +3 +4 +1 +2 +3 +4 +DROP TABLE m2, m1, t2, t1; +CREATE TABLE t1 (c1 INT); +CREATE TABLE t2 (c1 INT); +CREATE TABLE m1 (c1 INT) ENGINE=MRG_MyISAM UNION=(t1,t2) +INSERT_METHOD=LAST; +CREATE TABLE m2 (c1 INT) ENGINE=MRG_MyISAM UNION=(t1,t2) +INSERT_METHOD=LAST; +LOCK TABLE m1 WRITE, m2 WRITE; +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2), (3), (4); +INSERT INTO m2 SELECT * FROM m1; +SELECT * FROM m2; +c1 +1 +2 +3 +4 +1 +2 +3 +4 +SELECT * FROM t2; +c1 +2 +3 +4 +1 +2 +3 +4 +UNLOCK TABLES; +DROP TABLE m2, m1, t2, t1; +End of 6.0 tests diff --git a/mysql-test/t/merge_mmap-master.opt b/mysql-test/t/merge_mmap-master.opt new file mode 100644 index 00000000000..9606fb57187 --- /dev/null +++ b/mysql-test/t/merge_mmap-master.opt @@ -0,0 +1 @@ +--myisam-use-mmap diff --git a/mysql-test/t/merge_mmap.test b/mysql-test/t/merge_mmap.test new file mode 100644 index 00000000000..3468702ca45 --- /dev/null +++ b/mysql-test/t/merge_mmap.test @@ -0,0 +1,60 @@ +# +# Test of MERGE TABLES with MyISAM memory mapping enabled (--myisam-use-mmap) +# + +# MERGE tables require MyISAM tables +--let $default=`SELECT @@global.storage_engine` +SET GLOBAL storage_engine = MyISAM; +SET SESSION storage_engine = MyISAM; + +# Clean up resources used in this test case. +--disable_warnings +DROP TABLE IF EXISTS t1, t2, m1, m2; +--enable_warnings + +#################### +## No locked tables. +#################### +# +# INSERT-SELECT with no TEMPORARY table. +# +CREATE TABLE t1 (c1 INT); +CREATE TABLE t2 (c1 INT); +CREATE TABLE m1 (c1 INT) ENGINE=MRG_MyISAM UNION=(t1,t2) + INSERT_METHOD=LAST; +CREATE TABLE m2 (c1 INT) ENGINE=MRG_MyISAM UNION=(t1,t2) + INSERT_METHOD=LAST; +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2), (3), (4); +INSERT INTO m2 SELECT * FROM m1; +SELECT * FROM m2; +SELECT * FROM t2; +DROP TABLE m2, m1, t2, t1; + +#################### +## With LOCK TABLES. +#################### +# +# INSERT-SELECT with no TEMPORARY table. +# +CREATE TABLE t1 (c1 INT); +CREATE TABLE t2 (c1 INT); +CREATE TABLE m1 (c1 INT) ENGINE=MRG_MyISAM UNION=(t1,t2) + INSERT_METHOD=LAST; +CREATE TABLE m2 (c1 INT) ENGINE=MRG_MyISAM UNION=(t1,t2) + INSERT_METHOD=LAST; +LOCK TABLE m1 WRITE, m2 WRITE; +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2), (3), (4); +INSERT INTO m2 SELECT * FROM m1; +SELECT * FROM m2; +SELECT * FROM t2; +UNLOCK TABLES; +DROP TABLE m2, m1, t2, t1; +--echo End of 6.0 tests + +--disable_result_log +--disable_query_log +eval SET GLOBAL storage_engine = $default; +--enable_result_log +--enable_query_log diff --git a/storage/myisam/mi_statrec.c b/storage/myisam/mi_statrec.c index 7d595916d78..f83afa3c886 100644 --- a/storage/myisam/mi_statrec.c +++ b/storage/myisam/mi_statrec.c @@ -298,8 +298,17 @@ int _mi_read_rnd_static_record(MI_INFO *info, uchar *buf, info->update|= HA_STATE_AKTIV | HA_STATE_KEY_CHANGED; DBUG_RETURN(0); } - /* my_errno should be set if rec_cache.error == -1 */ + /* error is TRUE. my_errno should be set if rec_cache.error == -1 */ if (info->rec_cache.error != -1 || my_errno == 0) - my_errno=HA_ERR_WRONG_IN_RECORD; + { + /* + If we could not get a full record, we either have a broken record, + or are at end of file. + */ + if (info->rec_cache.error == 0) + my_errno= HA_ERR_END_OF_FILE; + else + my_errno= HA_ERR_WRONG_IN_RECORD; + } DBUG_RETURN(my_errno); /* Something wrong (EOF?) */ } diff --git a/storage/myisammrg/ha_myisammrg.cc b/storage/myisammrg/ha_myisammrg.cc index 67b81225b13..1681f062117 100644 --- a/storage/myisammrg/ha_myisammrg.cc +++ b/storage/myisammrg/ha_myisammrg.cc @@ -1322,6 +1322,8 @@ int ha_myisammrg::extra(enum ha_extra_function operation) if (operation == HA_EXTRA_FORCE_REOPEN || operation == HA_EXTRA_PREPARE_FOR_DROP) return 0; + if (operation == HA_EXTRA_MMAP && !opt_myisam_use_mmap) + return 0; return myrg_extra(file,operation,0); } From 635ccedbbc1f970e465d140cdf4cdd2cba141c8e Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Fri, 2 Jul 2010 20:07:57 +0400 Subject: [PATCH 062/115] A fix and a test case for Bug#36171 "CREATE TEMPORARY TABLE and MERGE engine". Backport the patch from 6.0 by Ingo Struewing: revid:ingo.struewing@sun.com-20091028183659-6kmv1k3gdq6cpg4d Bug#36171 - CREATE TEMPORARY TABLE and MERGE engine In former MySQL versions, up to 5.1.23/6.0.4 it was possible to create temporary MERGE tables with non-temporary MyISAM tables. This has been changed in the mentioned version due to Bug 19627 (temporary merge table locking). MERGE children were locked through the parent table. If the parent was temporary, it was not locked and so the children were not locked either. Parallel use of the MyISAM tables corrupted them. Since 6.0.6 (WL 4144 - Lock MERGE engine children), the children are locked independently from the parent. Now it is possible to allow non-temporary children with a temporary parent. Even though the temporary MERGE table itself is not locked, each non-temporary MyISAM table is locked anyway. NOTE: Behavior change: In 5.1.23/6.0.4 we prohibited non-temporary children with a temporary MERGE table. Now we re-allow it. An important side-effect is that temporary tables, which overlay non-temporary MERGE children, overlay the children in the MERGE table. --- mysql-test/r/merge.result | 738 +++++++++++++++++++++++++++++- mysql-test/r/merge_mmap.result | 126 +++++ mysql-test/t/merge.test | 335 +++++++++++++- mysql-test/t/merge_mmap.test | 91 ++++ storage/myisammrg/ha_myisammrg.cc | 23 +- 5 files changed, 1291 insertions(+), 22 deletions(-) diff --git a/mysql-test/r/merge.result b/mysql-test/r/merge.result index 43f456a6d95..f7f0cea3b19 100644 --- a/mysql-test/r/merge.result +++ b/mysql-test/r/merge.result @@ -1,3 +1,5 @@ +set global storage_engine=myisam; +set session storage_engine=myisam; drop table if exists t1,t2,t3,t4,t5,t6; drop database if exists mysqltest; create table t1 (a int not null primary key auto_increment, message char(20)); @@ -584,7 +586,9 @@ INSERT INTO t1 VALUES (1); INSERT INTO t2 VALUES (2); CREATE TEMPORARY TABLE t3 (c1 INT NOT NULL) ENGINE=MRG_MYISAM UNION=(t1,t2); SELECT * FROM t3; -ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist +c1 +1 +2 CREATE TEMPORARY TABLE t4 (c1 INT NOT NULL); CREATE TEMPORARY TABLE t5 (c1 INT NOT NULL); INSERT INTO t4 VALUES (4); @@ -613,7 +617,9 @@ ERROR HY000: Unable to open underlying table which is differently defined or of drop table t3; create temporary table t3 (a int not null) ENGINE=MERGE UNION=(t1,t2); select * from t3; -ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist +a +1 +2 drop table t3, t2, t1; # CREATE...SELECT is not implemented for MERGE tables. CREATE TEMPORARY TABLE t1 (c1 INT NOT NULL); @@ -1196,12 +1202,13 @@ ERROR HY000: Table 't4' was not locked with LOCK TABLES # it can even be used. CREATE TEMPORARY TABLE t4 LIKE t3; SHOW CREATE TABLE t4; -ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist +Table Create Table +t4 CREATE TEMPORARY TABLE `t4` ( + `c1` int(11) DEFAULT NULL +) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 INSERT_METHOD=LAST UNION=(`t1`,`t2`) INSERT INTO t4 VALUES (4); -ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist UNLOCK TABLES; INSERT INTO t4 VALUES (4); -ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist DROP TABLE t4; # # Rename child. @@ -1229,6 +1236,8 @@ c1 2 3 4 +4 +4 RENAME TABLE t2 TO t5; SELECT * FROM t3 ORDER BY c1; ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist @@ -1239,6 +1248,8 @@ c1 2 3 4 +4 +4 # # 3. Normal rename with locked tables. LOCK TABLES t1 WRITE, t2 WRITE, t3 WRITE; @@ -1248,6 +1259,8 @@ c1 2 3 4 +4 +4 RENAME TABLE t2 TO t5; ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction SELECT * FROM t3 ORDER BY c1; @@ -1256,6 +1269,8 @@ c1 2 3 4 +4 +4 RENAME TABLE t5 TO t2; ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction SELECT * FROM t3 ORDER BY c1; @@ -1264,6 +1279,8 @@ c1 2 3 4 +4 +4 UNLOCK TABLES; # # 4. Alter table rename. @@ -1277,6 +1294,8 @@ c1 2 3 4 +4 +4 # # 5. Alter table rename with locked tables. LOCK TABLES t1 WRITE, t2 WRITE, t3 WRITE; @@ -1293,6 +1312,8 @@ c1 2 3 4 +4 +4 # # Rename parent. # @@ -1304,6 +1325,8 @@ c1 2 3 4 +4 +4 RENAME TABLE t3 TO t5; ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction SELECT * FROM t3 ORDER BY c1; @@ -1312,6 +1335,8 @@ c1 2 3 4 +4 +4 RENAME TABLE t5 TO t3; ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction SELECT * FROM t3 ORDER BY c1; @@ -1320,6 +1345,8 @@ c1 2 3 4 +4 +4 # # 5. Alter table rename with locked tables. ALTER TABLE t3 RENAME TO t5; @@ -1335,6 +1362,8 @@ c1 2 3 4 +4 +4 DROP TABLE t1, t2, t3; # # Drop locked tables. @@ -2650,6 +2679,705 @@ test.t1 optimize Error Unable to open underlying table which is differently defi test.t1 optimize note The storage engine for the table doesn't support optimize DROP TABLE t1; # +# Bug#36171 - CREATE TEMPORARY TABLE and MERGE engine +# More tests with TEMPORARY MERGE table and permanent children. +# First without locked tables. +# +DROP TABLE IF EXISTS t1, t2, t3, t4, m1, m2; +# +CREATE TABLE t1 (c1 INT, c2 INT) ENGINE=MyISAM; +CREATE TABLE t2 (c1 INT, c2 INT) ENGINE=MyISAM; +CREATE TEMPORARY TABLE m1 (c1 INT, c2 INT) ENGINE=MRG_MyISAM UNION=(t1,t2) +INSERT_METHOD=LAST; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +SHOW CREATE TABLE m1; +Table Create Table +m1 CREATE TEMPORARY TABLE `m1` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL +) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 INSERT_METHOD=LAST UNION=(`t1`,`t2`) +SELECT * FROM m1; +c1 c2 +INSERT INTO t1 VALUES (111, 121); +INSERT INTO m1 VALUES (211, 221); +SELECT * FROM m1; +c1 c2 +111 121 +211 221 +SELECT * FROM t1; +c1 c2 +111 121 +SELECT * FROM t2; +c1 c2 +211 221 +# +ALTER TABLE m1 RENAME m2; +SHOW CREATE TABLE m2; +Table Create Table +m2 CREATE TEMPORARY TABLE `m2` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL +) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 INSERT_METHOD=LAST UNION=(`t1`,`t2`) +SELECT * FROM m2; +c1 c2 +111 121 +211 221 +# +CREATE TEMPORARY TABLE m1 (c1 INT, c2 INT) ENGINE=MRG_MyISAM UNION=(t1,t2) +INSERT_METHOD=LAST; +ALTER TABLE m2 RENAME m1; +ERROR 42S01: Table 'm1' already exists +DROP TABLE m1; +ALTER TABLE m2 RENAME m1; +SHOW CREATE TABLE m1; +Table Create Table +m1 CREATE TEMPORARY TABLE `m1` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL +) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 INSERT_METHOD=LAST UNION=(`t1`,`t2`) +SELECT * FROM m1; +c1 c2 +111 121 +211 221 +# +ALTER TABLE m1 ADD COLUMN c3 INT; +INSERT INTO m1 VALUES (212, 222, 232); +ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist +SELECT * FROM m1; +ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist +ALTER TABLE t1 ADD COLUMN c3 INT; +ALTER TABLE t2 ADD COLUMN c3 INT; +INSERT INTO m1 VALUES (212, 222, 232); +SELECT * FROM m1; +c1 c2 c3 +111 121 NULL +211 221 NULL +212 222 232 +# +ALTER TABLE m1 DROP COLUMN c3; +INSERT INTO m1 VALUES (213, 223); +ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist +SELECT * FROM m1; +ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist +ALTER TABLE t1 DROP COLUMN c3; +ALTER TABLE t2 DROP COLUMN c3; +INSERT INTO m1 VALUES (213, 223); +SELECT * FROM m1; +c1 c2 +111 121 +211 221 +212 222 +213 223 +# +CREATE TABLE t3 (c1 INT, c2 INT) ENGINE=MyISAM; +ALTER TABLE m1 UNION=(t1,t2,t3); +INSERT INTO m1 VALUES (311, 321); +SELECT * FROM m1; +c1 c2 +111 121 +211 221 +212 222 +213 223 +311 321 +SELECT * FROM t1; +c1 c2 +111 121 +SELECT * FROM t2; +c1 c2 +211 221 +212 222 +213 223 +SELECT * FROM t3; +c1 c2 +311 321 +# +CREATE TEMPORARY TABLE t4 (c1 INT, c2 INT) ENGINE=MyISAM; +ALTER TABLE m1 UNION=(t1,t2,t3,t4); +INSERT INTO m1 VALUES (411, 421); +SELECT * FROM m1; +c1 c2 +111 121 +211 221 +212 222 +213 223 +311 321 +411 421 +SELECT * FROM t1; +c1 c2 +111 121 +SELECT * FROM t2; +c1 c2 +211 221 +212 222 +213 223 +SELECT * FROM t3; +c1 c2 +311 321 +SELECT * FROM t4; +c1 c2 +411 421 +# +ALTER TABLE m1 ENGINE=MyISAM; +SHOW CREATE TABLE m1; +Table Create Table +m1 CREATE TEMPORARY TABLE `m1` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +INSERT INTO m1 VALUES (511, 521); +SELECT * FROM m1; +c1 c2 +111 121 +211 221 +212 222 +213 223 +311 321 +411 421 +511 521 +# +ALTER TABLE m1 ENGINE=MRG_MyISAM UNION=(t1,t2) +INSERT_METHOD=LAST; +SELECT * FROM m1; +c1 c2 +111 121 +211 221 +212 222 +213 223 +SELECT * FROM t1; +c1 c2 +111 121 +SELECT * FROM t2; +c1 c2 +211 221 +212 222 +213 223 +# +CREATE TEMPORARY TABLE t1 (c1 INT, c2 INT) ENGINE=MyISAM; +INSERT INTO t1 VALUES (611, 621); +SELECT * FROM m1; +c1 c2 +611 621 +211 221 +212 222 +213 223 +DROP TABLE t1; +SELECT * FROM m1; +c1 c2 +111 121 +211 221 +212 222 +213 223 +# +# +SHOW CREATE TABLE m1; +Table Create Table +m1 CREATE TEMPORARY TABLE `m1` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL +) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 INSERT_METHOD=LAST UNION=(`t1`,`t2`) +# +CREATE TABLE m2 SELECT * FROM m1; +SHOW CREATE TABLE m2; +Table Create Table +m2 CREATE TABLE `m2` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +SELECT * FROM m2; +c1 c2 +111 121 +211 221 +212 222 +213 223 +DROP TABLE m2; +# +CREATE TEMPORARY TABLE m2 SELECT * FROM m1; +SHOW CREATE TABLE m2; +Table Create Table +m2 CREATE TEMPORARY TABLE `m2` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +SELECT * FROM m2; +c1 c2 +111 121 +211 221 +212 222 +213 223 +DROP TABLE m2; +# +CREATE TABLE m2 (c1 INT, c2 INT) ENGINE=MRG_MyISAM UNION=(t3,t4) +INSERT_METHOD=LAST; +SELECT * FROM m2; +ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist +DROP TABLE m2; +# +CREATE TABLE m2 (c1 INT, c2 INT) ENGINE=MRG_MyISAM UNION=(t3,t4) +INSERT_METHOD=LAST SELECT * FROM m1; +ERROR HY000: 'test.m2' is not BASE TABLE +# +CREATE TEMPORARY TABLE m2 (c1 INT, c2 INT) ENGINE=MRG_MyISAM UNION=(t3,t4) +INSERT_METHOD=LAST SELECT * FROM m1; +ERROR HY000: 'test.m2' is not BASE TABLE +# +CREATE TABLE m2 LIKE m1; +SHOW CREATE TABLE m2; +Table Create Table +m2 CREATE TABLE `m2` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL +) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 INSERT_METHOD=LAST UNION=(`t1`,`t2`) +SELECT * FROM m2; +c1 c2 +111 121 +211 221 +212 222 +213 223 +INSERT INTO m2 SELECT * FROM m1; +SELECT * FROM m2; +c1 c2 +111 121 +211 221 +212 222 +213 223 +111 121 +211 221 +212 222 +213 223 +DROP TABLE m2; +# +CREATE TEMPORARY TABLE m2 LIKE m1; +SHOW CREATE TABLE m2; +Table Create Table +m2 CREATE TEMPORARY TABLE `m2` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL +) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 INSERT_METHOD=LAST UNION=(`t1`,`t2`) +SELECT * FROM m2; +c1 c2 +111 121 +211 221 +212 222 +213 223 +111 121 +211 221 +212 222 +213 223 +INSERT INTO m2 SELECT * FROM m1; +SELECT * FROM m2; +c1 c2 +111 121 +211 221 +212 222 +213 223 +111 121 +211 221 +212 222 +213 223 +111 121 +211 221 +212 222 +213 223 +111 121 +211 221 +212 222 +213 223 +DROP TABLE m2; +# +CREATE TEMPORARY TABLE m2 (c1 INT, c2 INT) ENGINE=MRG_MyISAM UNION=(t3,t4) +INSERT_METHOD=LAST; +INSERT INTO m2 SELECT * FROM m1; +SELECT * FROM m2; +c1 c2 +311 321 +411 421 +111 121 +211 221 +212 222 +213 223 +111 121 +211 221 +212 222 +213 223 +111 121 +211 221 +212 222 +213 223 +111 121 +211 221 +212 222 +213 223 +# +# +LOCK TABLE m1 WRITE, m2 WRITE; +SELECT * FROM m1,m2 WHERE m1.c1=m2.c1; +c1 c2 c1 c2 +111 121 111 121 +111 121 111 121 +111 121 111 121 +111 121 111 121 +211 221 211 221 +211 221 211 221 +211 221 211 221 +211 221 211 221 +212 222 212 222 +212 222 212 222 +212 222 212 222 +212 222 212 222 +213 223 213 223 +213 223 213 223 +213 223 213 223 +213 223 213 223 +111 121 111 121 +111 121 111 121 +111 121 111 121 +111 121 111 121 +211 221 211 221 +211 221 211 221 +211 221 211 221 +211 221 211 221 +212 222 212 222 +212 222 212 222 +212 222 212 222 +212 222 212 222 +213 223 213 223 +213 223 213 223 +213 223 213 223 +213 223 213 223 +111 121 111 121 +111 121 111 121 +111 121 111 121 +111 121 111 121 +211 221 211 221 +211 221 211 221 +211 221 211 221 +211 221 211 221 +212 222 212 222 +212 222 212 222 +212 222 212 222 +212 222 212 222 +213 223 213 223 +213 223 213 223 +213 223 213 223 +213 223 213 223 +111 121 111 121 +111 121 111 121 +111 121 111 121 +111 121 111 121 +211 221 211 221 +211 221 211 221 +211 221 211 221 +211 221 211 221 +212 222 212 222 +212 222 212 222 +212 222 212 222 +212 222 212 222 +213 223 213 223 +213 223 213 223 +213 223 213 223 +213 223 213 223 +UNLOCK TABLES; +DROP TABLE t1, t2, t3, t4, m1, m2; +# +# Bug#36171 - CREATE TEMPORARY TABLE and MERGE engine +# More tests with TEMPORARY MERGE table and permanent children. +# (continued) Now the same with locked table. +# +CREATE TABLE t1 (c1 INT, c2 INT) ENGINE=MyISAM; +CREATE TABLE t2 (c1 INT, c2 INT) ENGINE=MyISAM; +CREATE TEMPORARY TABLE m1 (c1 INT, c2 INT) ENGINE=MRG_MyISAM UNION=(t1,t2) +INSERT_METHOD=LAST; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +SHOW CREATE TABLE m1; +Table Create Table +m1 CREATE TEMPORARY TABLE `m1` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL +) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 INSERT_METHOD=LAST UNION=(`t1`,`t2`) +SELECT * FROM m1; +c1 c2 +INSERT INTO t1 VALUES (111, 121); +INSERT INTO m1 VALUES (211, 221); +SELECT * FROM m1; +c1 c2 +111 121 +211 221 +SELECT * FROM t1; +c1 c2 +111 121 +SELECT * FROM t2; +c1 c2 +211 221 +# +LOCK TABLE m1 WRITE, t1 WRITE, t2 WRITE; +# +ALTER TABLE m1 RENAME m2; +SHOW CREATE TABLE m2; +Table Create Table +m2 CREATE TEMPORARY TABLE `m2` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL +) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 INSERT_METHOD=LAST UNION=(`t1`,`t2`) +SELECT * FROM m2; +c1 c2 +111 121 +211 221 +# +CREATE TEMPORARY TABLE m1 (c1 INT, c2 INT) ENGINE=MRG_MyISAM UNION=(t1,t2) +INSERT_METHOD=LAST; +ALTER TABLE m2 RENAME m1; +ERROR 42S01: Table 'm1' already exists +DROP TABLE m1; +ALTER TABLE m2 RENAME m1; +SHOW CREATE TABLE m1; +Table Create Table +m1 CREATE TEMPORARY TABLE `m1` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL +) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 INSERT_METHOD=LAST UNION=(`t1`,`t2`) +SELECT * FROM m1; +c1 c2 +111 121 +211 221 +# +ALTER TABLE m1 ADD COLUMN c3 INT; +INSERT INTO m1 VALUES (212, 222, 232); +ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist +SELECT * FROM m1; +ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist +ALTER TABLE t1 ADD COLUMN c3 INT; +ALTER TABLE t2 ADD COLUMN c3 INT; +INSERT INTO m1 VALUES (212, 222, 232); +SELECT * FROM m1; +c1 c2 c3 +111 121 NULL +211 221 NULL +212 222 232 +# +ALTER TABLE m1 DROP COLUMN c3; +INSERT INTO m1 VALUES (213, 223); +ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist +SELECT * FROM m1; +ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist +ALTER TABLE t1 DROP COLUMN c3; +ALTER TABLE t2 DROP COLUMN c3; +INSERT INTO m1 VALUES (213, 223); +SELECT * FROM m1; +c1 c2 +111 121 +211 221 +212 222 +213 223 +# +UNLOCK TABLES; +CREATE TABLE t3 (c1 INT, c2 INT) ENGINE=MyISAM; +ALTER TABLE m1 UNION=(t1,t2,t3); +LOCK TABLE m1 WRITE; +INSERT INTO m1 VALUES (311, 321); +SELECT * FROM m1; +c1 c2 +111 121 +211 221 +212 222 +213 223 +311 321 +SELECT * FROM t1; +c1 c2 +111 121 +SELECT * FROM t2; +c1 c2 +211 221 +212 222 +213 223 +SELECT * FROM t3; +c1 c2 +311 321 +# +CREATE TEMPORARY TABLE t4 (c1 INT, c2 INT) ENGINE=MyISAM; +ALTER TABLE m1 UNION=(t1,t2,t3,t4); +INSERT INTO m1 VALUES (411, 421); +SELECT * FROM m1; +c1 c2 +111 121 +211 221 +212 222 +213 223 +311 321 +411 421 +SELECT * FROM t1; +c1 c2 +111 121 +SELECT * FROM t2; +c1 c2 +211 221 +212 222 +213 223 +SELECT * FROM t3; +c1 c2 +311 321 +SELECT * FROM t4; +c1 c2 +411 421 +# +ALTER TABLE m1 ENGINE=MyISAM; +SHOW CREATE TABLE m1; +Table Create Table +m1 CREATE TEMPORARY TABLE `m1` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +INSERT INTO m1 VALUES (511, 521); +SELECT * FROM m1; +c1 c2 +111 121 +211 221 +212 222 +213 223 +311 321 +411 421 +511 521 +# +ALTER TABLE m1 ENGINE=MRG_MyISAM UNION=(t1,t2) +INSERT_METHOD=LAST; +SELECT * FROM m1; +c1 c2 +111 121 +211 221 +212 222 +213 223 +SELECT * FROM t1; +c1 c2 +111 121 +SELECT * FROM t2; +c1 c2 +211 221 +212 222 +213 223 +# +CREATE TEMPORARY TABLE t1 (c1 INT, c2 INT) ENGINE=MyISAM; +INSERT INTO t1 VALUES (611, 621); +SELECT * FROM m1; +c1 c2 +611 621 +211 221 +212 222 +213 223 +DROP TABLE t1; +SELECT * FROM m1; +c1 c2 +111 121 +211 221 +212 222 +213 223 +# +# +SHOW CREATE TABLE m1; +Table Create Table +m1 CREATE TEMPORARY TABLE `m1` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL +) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 INSERT_METHOD=LAST UNION=(`t1`,`t2`) +CREATE TABLE m2 SELECT * FROM m1; +ERROR HY000: Table 'm2' was not locked with LOCK TABLES +# +CREATE TEMPORARY TABLE m2 SELECT * FROM m1; +SHOW CREATE TABLE m2; +Table Create Table +m2 CREATE TEMPORARY TABLE `m2` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +SELECT * FROM m2; +c1 c2 +111 121 +211 221 +212 222 +213 223 +DROP TABLE m2; +# +CREATE TEMPORARY TABLE m2 (c1 INT, c2 INT) ENGINE=MRG_MyISAM UNION=(t3,t4) +INSERT_METHOD=LAST; +SELECT * FROM m2; +c1 c2 +311 321 +411 421 +LOCK TABLE m1 WRITE, m2 WRITE; +UNLOCK TABLES; +DROP TABLE m2; +LOCK TABLE m1 WRITE; +# +# ER_TABLE_NOT_LOCKED is returned in ps-protocol +CREATE TABLE m2 (c1 INT, c2 INT) ENGINE=MRG_MyISAM UNION=(t3,t4) +INSERT_METHOD=LAST SELECT * FROM m1; +Got one of the listed errors +# +CREATE TEMPORARY TABLE m2 (c1 INT, c2 INT) ENGINE=MRG_MyISAM UNION=(t3,t4) +INSERT_METHOD=LAST SELECT * FROM m1; +ERROR HY000: 'test.m2' is not BASE TABLE +# +CREATE TEMPORARY TABLE m2 LIKE m1; +SHOW CREATE TABLE m2; +Table Create Table +m2 CREATE TEMPORARY TABLE `m2` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL +) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 INSERT_METHOD=LAST UNION=(`t1`,`t2`) +LOCK TABLE m1 WRITE, m2 WRITE; +SHOW CREATE TABLE m2; +Table Create Table +m2 CREATE TEMPORARY TABLE `m2` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL +) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 INSERT_METHOD=LAST UNION=(`t1`,`t2`) +SELECT * FROM m2; +c1 c2 +111 121 +211 221 +212 222 +213 223 +INSERT INTO m2 SELECT * FROM m1; +SELECT * FROM m2; +c1 c2 +111 121 +211 221 +212 222 +213 223 +111 121 +211 221 +212 222 +213 223 +DROP TABLE m2; +# +CREATE TEMPORARY TABLE m2 (c1 INT, c2 INT) ENGINE=MRG_MyISAM UNION=(t3,t4) +INSERT_METHOD=LAST; +LOCK TABLE m1 WRITE, m2 WRITE; +INSERT INTO m2 SELECT * FROM m1; +SELECT * FROM m2; +c1 c2 +311 321 +411 421 +111 121 +211 221 +212 222 +213 223 +111 121 +211 221 +212 222 +213 223 +# +UNLOCK TABLES; +DROP TABLE t1, t2, t3, t4, m1, m2; +# # Bug47098 assert in MDL_context::destroy on HANDLER # OPEN # diff --git a/mysql-test/r/merge_mmap.result b/mysql-test/r/merge_mmap.result index d975a1be377..e8014259a4a 100644 --- a/mysql-test/r/merge_mmap.result +++ b/mysql-test/r/merge_mmap.result @@ -32,6 +32,66 @@ c1 DROP TABLE m2, m1, t2, t1; CREATE TABLE t1 (c1 INT); CREATE TABLE t2 (c1 INT); +CREATE TEMPORARY TABLE m1 (c1 INT) ENGINE=MRG_MyISAM UNION=(t1,t2) +INSERT_METHOD=LAST; +CREATE TABLE m2 (c1 INT) ENGINE=MRG_MyISAM UNION=(t1,t2) +INSERT_METHOD=LAST; +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2), (3), (4); +INSERT INTO m2 SELECT * FROM m1; +SELECT * FROM m2; +c1 +1 +2 +3 +4 +1 +2 +3 +4 +DROP TABLE m2, m1, t2, t1; +CREATE TABLE t1 (c1 INT); +CREATE TABLE t2 (c1 INT); +CREATE TABLE m1 (c1 INT) ENGINE=MRG_MyISAM UNION=(t1,t2) +INSERT_METHOD=LAST; +CREATE TEMPORARY TABLE m2 (c1 INT) ENGINE=MRG_MyISAM UNION=(t1,t2) +INSERT_METHOD=LAST; +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2), (3), (4); +INSERT INTO m2 SELECT * FROM m1; +SELECT * FROM m2; +c1 +1 +2 +3 +4 +1 +2 +3 +4 +DROP TABLE m2, m1, t2, t1; +CREATE TABLE t1 (c1 INT); +CREATE TABLE t2 (c1 INT); +CREATE TEMPORARY TABLE m1 (c1 INT) ENGINE=MRG_MyISAM UNION=(t1,t2) +INSERT_METHOD=LAST; +CREATE TEMPORARY TABLE m2 (c1 INT) ENGINE=MRG_MyISAM UNION=(t1,t2) +INSERT_METHOD=LAST; +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2), (3), (4); +INSERT INTO m2 SELECT * FROM m1; +SELECT * FROM m2; +c1 +1 +2 +3 +4 +1 +2 +3 +4 +DROP TABLE m2, m1, t2, t1; +CREATE TABLE t1 (c1 INT); +CREATE TABLE t2 (c1 INT); CREATE TABLE m1 (c1 INT) ENGINE=MRG_MyISAM UNION=(t1,t2) INSERT_METHOD=LAST; CREATE TABLE m2 (c1 INT) ENGINE=MRG_MyISAM UNION=(t1,t2) @@ -61,4 +121,70 @@ c1 4 UNLOCK TABLES; DROP TABLE m2, m1, t2, t1; +CREATE TABLE t1 (c1 INT); +CREATE TABLE t2 (c1 INT); +CREATE TEMPORARY TABLE m1 (c1 INT) ENGINE=MRG_MyISAM UNION=(t1,t2) +INSERT_METHOD=LAST; +CREATE TABLE m2 (c1 INT) ENGINE=MRG_MyISAM UNION=(t1,t2) +INSERT_METHOD=LAST; +LOCK TABLE m1 WRITE, m2 WRITE; +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2), (3), (4); +INSERT INTO m2 SELECT * FROM m1; +SELECT * FROM m2; +c1 +1 +2 +3 +4 +1 +2 +3 +4 +UNLOCK TABLES; +DROP TABLE m2, m1, t2, t1; +CREATE TABLE t1 (c1 INT); +CREATE TABLE t2 (c1 INT); +CREATE TABLE m1 (c1 INT) ENGINE=MRG_MyISAM UNION=(t1,t2) +INSERT_METHOD=LAST; +CREATE TEMPORARY TABLE m2 (c1 INT) ENGINE=MRG_MyISAM UNION=(t1,t2) +INSERT_METHOD=LAST; +LOCK TABLE m1 WRITE, m2 WRITE; +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2), (3), (4); +INSERT INTO m2 SELECT * FROM m1; +SELECT * FROM m2; +c1 +1 +2 +3 +4 +1 +2 +3 +4 +UNLOCK TABLES; +DROP TABLE m2, m1, t2, t1; +CREATE TABLE t1 (c1 INT); +CREATE TABLE t2 (c1 INT); +CREATE TEMPORARY TABLE m1 (c1 INT) ENGINE=MRG_MyISAM UNION=(t1,t2) +INSERT_METHOD=LAST; +CREATE TEMPORARY TABLE m2 (c1 INT) ENGINE=MRG_MyISAM UNION=(t1,t2) +INSERT_METHOD=LAST; +LOCK TABLE m1 WRITE, m2 WRITE; +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2), (3), (4); +INSERT INTO m2 SELECT * FROM m1; +SELECT * FROM m2; +c1 +1 +2 +3 +4 +1 +2 +3 +4 +UNLOCK TABLES; +DROP TABLE m2, m1, t2, t1; End of 6.0 tests diff --git a/mysql-test/t/merge.test b/mysql-test/t/merge.test index 252495cc1be..31bc8a5e881 100644 --- a/mysql-test/t/merge.test +++ b/mysql-test/t/merge.test @@ -2,6 +2,12 @@ # Test of MERGE TABLES # +# MERGE tables require MyISAM tables +let $default=`select @@global.storage_engine`; +set global storage_engine=myisam; +set session storage_engine=myisam; + +# Clean up resources used in this test case. --disable_warnings drop table if exists t1,t2,t3,t4,t5,t6; drop database if exists mysqltest; @@ -222,7 +228,6 @@ CREATE TABLE t2 (c1 INT NOT NULL); INSERT INTO t1 VALUES (1); INSERT INTO t2 VALUES (2); CREATE TEMPORARY TABLE t3 (c1 INT NOT NULL) ENGINE=MRG_MYISAM UNION=(t1,t2); ---error ER_WRONG_MRG_TABLE SELECT * FROM t3; CREATE TEMPORARY TABLE t4 (c1 INT NOT NULL); CREATE TEMPORARY TABLE t5 (c1 INT NOT NULL); @@ -254,7 +259,6 @@ create table t3 (a int not null) ENGINE=MERGE UNION=(t1,t2); select * from t3; drop table t3; create temporary table t3 (a int not null) ENGINE=MERGE UNION=(t1,t2); ---error ER_WRONG_MRG_TABLE select * from t3; drop table t3, t2, t1; --echo # CREATE...SELECT is not implemented for MERGE tables. @@ -891,12 +895,9 @@ INSERT INTO t4 VALUES (4); --echo # If the temporary MERGE table uses the locked children only, --echo # it can even be used. CREATE TEMPORARY TABLE t4 LIKE t3; ---error ER_WRONG_MRG_TABLE SHOW CREATE TABLE t4; ---error ER_WRONG_MRG_TABLE INSERT INTO t4 VALUES (4); UNLOCK TABLES; ---error ER_WRONG_MRG_TABLE INSERT INTO t4 VALUES (4); DROP TABLE t4; # @@ -2116,6 +2117,325 @@ ALTER TABLE t1 ENGINE=MERGE UNION(t_not_exists,t1); OPTIMIZE TABLE t1; DROP TABLE t1; +--echo # +--echo # Bug#36171 - CREATE TEMPORARY TABLE and MERGE engine +--echo # More tests with TEMPORARY MERGE table and permanent children. +--echo # First without locked tables. +--echo # +--disable_warnings +DROP TABLE IF EXISTS t1, t2, t3, t4, m1, m2; +--enable_warnings +# +--echo # +CREATE TABLE t1 (c1 INT, c2 INT) ENGINE=MyISAM; +CREATE TABLE t2 (c1 INT, c2 INT) ENGINE=MyISAM; +CREATE TEMPORARY TABLE m1 (c1 INT, c2 INT) ENGINE=MRG_MyISAM UNION=(t1,t2) + INSERT_METHOD=LAST; +SHOW CREATE TABLE t1; +SHOW CREATE TABLE m1; +SELECT * FROM m1; +INSERT INTO t1 VALUES (111, 121); +INSERT INTO m1 VALUES (211, 221); +SELECT * FROM m1; +SELECT * FROM t1; +SELECT * FROM t2; +# +--echo # +ALTER TABLE m1 RENAME m2; +SHOW CREATE TABLE m2; +SELECT * FROM m2; +# +--echo # +CREATE TEMPORARY TABLE m1 (c1 INT, c2 INT) ENGINE=MRG_MyISAM UNION=(t1,t2) + INSERT_METHOD=LAST; +--error ER_TABLE_EXISTS_ERROR +ALTER TABLE m2 RENAME m1; +DROP TABLE m1; +ALTER TABLE m2 RENAME m1; +SHOW CREATE TABLE m1; +SELECT * FROM m1; +# +--echo # +ALTER TABLE m1 ADD COLUMN c3 INT; +--error ER_WRONG_MRG_TABLE +INSERT INTO m1 VALUES (212, 222, 232); +--error ER_WRONG_MRG_TABLE +SELECT * FROM m1; +ALTER TABLE t1 ADD COLUMN c3 INT; +ALTER TABLE t2 ADD COLUMN c3 INT; +INSERT INTO m1 VALUES (212, 222, 232); +SELECT * FROM m1; +# +--echo # +ALTER TABLE m1 DROP COLUMN c3; +--error ER_WRONG_MRG_TABLE +INSERT INTO m1 VALUES (213, 223); +--error ER_WRONG_MRG_TABLE +SELECT * FROM m1; +ALTER TABLE t1 DROP COLUMN c3; +ALTER TABLE t2 DROP COLUMN c3; +INSERT INTO m1 VALUES (213, 223); +SELECT * FROM m1; +# +--echo # +CREATE TABLE t3 (c1 INT, c2 INT) ENGINE=MyISAM; +ALTER TABLE m1 UNION=(t1,t2,t3); +INSERT INTO m1 VALUES (311, 321); +SELECT * FROM m1; +SELECT * FROM t1; +SELECT * FROM t2; +SELECT * FROM t3; +# +--echo # +CREATE TEMPORARY TABLE t4 (c1 INT, c2 INT) ENGINE=MyISAM; +ALTER TABLE m1 UNION=(t1,t2,t3,t4); +INSERT INTO m1 VALUES (411, 421); +SELECT * FROM m1; +SELECT * FROM t1; +SELECT * FROM t2; +SELECT * FROM t3; +SELECT * FROM t4; +# +--echo # +ALTER TABLE m1 ENGINE=MyISAM; +SHOW CREATE TABLE m1; +INSERT INTO m1 VALUES (511, 521); +SELECT * FROM m1; +# +--echo # +ALTER TABLE m1 ENGINE=MRG_MyISAM UNION=(t1,t2) + INSERT_METHOD=LAST; +SELECT * FROM m1; +SELECT * FROM t1; +SELECT * FROM t2; +# +--echo # +CREATE TEMPORARY TABLE t1 (c1 INT, c2 INT) ENGINE=MyISAM; +INSERT INTO t1 VALUES (611, 621); +SELECT * FROM m1; +DROP TABLE t1; +SELECT * FROM m1; +# +# +--echo # +--echo # +SHOW CREATE TABLE m1; +# +--echo # +CREATE TABLE m2 SELECT * FROM m1; +SHOW CREATE TABLE m2; +SELECT * FROM m2; +DROP TABLE m2; +# +--echo # +CREATE TEMPORARY TABLE m2 SELECT * FROM m1; +SHOW CREATE TABLE m2; +SELECT * FROM m2; +DROP TABLE m2; +# +--echo # +CREATE TABLE m2 (c1 INT, c2 INT) ENGINE=MRG_MyISAM UNION=(t3,t4) + INSERT_METHOD=LAST; +--error ER_WRONG_MRG_TABLE +SELECT * FROM m2; +DROP TABLE m2; +# +--echo # +--error ER_WRONG_OBJECT +CREATE TABLE m2 (c1 INT, c2 INT) ENGINE=MRG_MyISAM UNION=(t3,t4) + INSERT_METHOD=LAST SELECT * FROM m1; +# +--echo # +--error ER_WRONG_OBJECT +CREATE TEMPORARY TABLE m2 (c1 INT, c2 INT) ENGINE=MRG_MyISAM UNION=(t3,t4) + INSERT_METHOD=LAST SELECT * FROM m1; +# +--echo # +CREATE TABLE m2 LIKE m1; +SHOW CREATE TABLE m2; +SELECT * FROM m2; +INSERT INTO m2 SELECT * FROM m1; +SELECT * FROM m2; +DROP TABLE m2; +# +--echo # +CREATE TEMPORARY TABLE m2 LIKE m1; +SHOW CREATE TABLE m2; +SELECT * FROM m2; +INSERT INTO m2 SELECT * FROM m1; +SELECT * FROM m2; +DROP TABLE m2; +# +--echo # +CREATE TEMPORARY TABLE m2 (c1 INT, c2 INT) ENGINE=MRG_MyISAM UNION=(t3,t4) + INSERT_METHOD=LAST; +INSERT INTO m2 SELECT * FROM m1; +SELECT * FROM m2; +# +# +--echo # +--echo # +LOCK TABLE m1 WRITE, m2 WRITE; +SELECT * FROM m1,m2 WHERE m1.c1=m2.c1; +UNLOCK TABLES; +# +DROP TABLE t1, t2, t3, t4, m1, m2; +# +# +# +--echo # +--echo # Bug#36171 - CREATE TEMPORARY TABLE and MERGE engine +--echo # More tests with TEMPORARY MERGE table and permanent children. +--echo # (continued) Now the same with locked table. +--echo # +CREATE TABLE t1 (c1 INT, c2 INT) ENGINE=MyISAM; +CREATE TABLE t2 (c1 INT, c2 INT) ENGINE=MyISAM; +CREATE TEMPORARY TABLE m1 (c1 INT, c2 INT) ENGINE=MRG_MyISAM UNION=(t1,t2) + INSERT_METHOD=LAST; +SHOW CREATE TABLE t1; +SHOW CREATE TABLE m1; +SELECT * FROM m1; +INSERT INTO t1 VALUES (111, 121); +INSERT INTO m1 VALUES (211, 221); +SELECT * FROM m1; +SELECT * FROM t1; +SELECT * FROM t2; +# +--echo # +LOCK TABLE m1 WRITE, t1 WRITE, t2 WRITE; +# +--echo # +ALTER TABLE m1 RENAME m2; +SHOW CREATE TABLE m2; +SELECT * FROM m2; +# +--echo # +CREATE TEMPORARY TABLE m1 (c1 INT, c2 INT) ENGINE=MRG_MyISAM UNION=(t1,t2) + INSERT_METHOD=LAST; +--error ER_TABLE_EXISTS_ERROR +ALTER TABLE m2 RENAME m1; +DROP TABLE m1; +ALTER TABLE m2 RENAME m1; +SHOW CREATE TABLE m1; +SELECT * FROM m1; +# +--echo # +ALTER TABLE m1 ADD COLUMN c3 INT; +--error ER_WRONG_MRG_TABLE +INSERT INTO m1 VALUES (212, 222, 232); +--error ER_WRONG_MRG_TABLE +SELECT * FROM m1; +ALTER TABLE t1 ADD COLUMN c3 INT; +ALTER TABLE t2 ADD COLUMN c3 INT; +INSERT INTO m1 VALUES (212, 222, 232); +SELECT * FROM m1; +# +--echo # +ALTER TABLE m1 DROP COLUMN c3; +--error ER_WRONG_MRG_TABLE +INSERT INTO m1 VALUES (213, 223); +--error ER_WRONG_MRG_TABLE +SELECT * FROM m1; +ALTER TABLE t1 DROP COLUMN c3; +ALTER TABLE t2 DROP COLUMN c3; +INSERT INTO m1 VALUES (213, 223); +SELECT * FROM m1; +# +--echo # +UNLOCK TABLES; +CREATE TABLE t3 (c1 INT, c2 INT) ENGINE=MyISAM; +ALTER TABLE m1 UNION=(t1,t2,t3); +LOCK TABLE m1 WRITE; +INSERT INTO m1 VALUES (311, 321); +SELECT * FROM m1; +SELECT * FROM t1; +SELECT * FROM t2; +SELECT * FROM t3; +# +--echo # +CREATE TEMPORARY TABLE t4 (c1 INT, c2 INT) ENGINE=MyISAM; +ALTER TABLE m1 UNION=(t1,t2,t3,t4); +INSERT INTO m1 VALUES (411, 421); +SELECT * FROM m1; +SELECT * FROM t1; +SELECT * FROM t2; +SELECT * FROM t3; +SELECT * FROM t4; +# +--echo # +ALTER TABLE m1 ENGINE=MyISAM; +SHOW CREATE TABLE m1; +INSERT INTO m1 VALUES (511, 521); +SELECT * FROM m1; +# +--echo # +ALTER TABLE m1 ENGINE=MRG_MyISAM UNION=(t1,t2) + INSERT_METHOD=LAST; +SELECT * FROM m1; +SELECT * FROM t1; +SELECT * FROM t2; +# +--echo # +CREATE TEMPORARY TABLE t1 (c1 INT, c2 INT) ENGINE=MyISAM; +INSERT INTO t1 VALUES (611, 621); +SELECT * FROM m1; +DROP TABLE t1; +SELECT * FROM m1; +# +# +--echo # +--echo # +SHOW CREATE TABLE m1; +--error ER_TABLE_NOT_LOCKED +CREATE TABLE m2 SELECT * FROM m1; +# +--echo # +CREATE TEMPORARY TABLE m2 SELECT * FROM m1; +SHOW CREATE TABLE m2; +SELECT * FROM m2; +DROP TABLE m2; +# +--echo # +CREATE TEMPORARY TABLE m2 (c1 INT, c2 INT) ENGINE=MRG_MyISAM UNION=(t3,t4) + INSERT_METHOD=LAST; +SELECT * FROM m2; +LOCK TABLE m1 WRITE, m2 WRITE; +UNLOCK TABLES; +DROP TABLE m2; +LOCK TABLE m1 WRITE; +# +--echo # +--echo # ER_TABLE_NOT_LOCKED is returned in ps-protocol +--error ER_WRONG_OBJECT, ER_TABLE_NOT_LOCKED +CREATE TABLE m2 (c1 INT, c2 INT) ENGINE=MRG_MyISAM UNION=(t3,t4) + INSERT_METHOD=LAST SELECT * FROM m1; +# +--echo # +--error ER_WRONG_OBJECT +CREATE TEMPORARY TABLE m2 (c1 INT, c2 INT) ENGINE=MRG_MyISAM UNION=(t3,t4) + INSERT_METHOD=LAST SELECT * FROM m1; +# +--echo # +CREATE TEMPORARY TABLE m2 LIKE m1; +SHOW CREATE TABLE m2; +LOCK TABLE m1 WRITE, m2 WRITE; +SHOW CREATE TABLE m2; +SELECT * FROM m2; +INSERT INTO m2 SELECT * FROM m1; +SELECT * FROM m2; +DROP TABLE m2; +# +--echo # +CREATE TEMPORARY TABLE m2 (c1 INT, c2 INT) ENGINE=MRG_MyISAM UNION=(t3,t4) + INSERT_METHOD=LAST; +LOCK TABLE m1 WRITE, m2 WRITE; +INSERT INTO m2 SELECT * FROM m1; +SELECT * FROM m2; +# +--echo # +UNLOCK TABLES; +DROP TABLE t1, t2, t3, t4, m1, m2; + --echo # --echo # Bug47098 assert in MDL_context::destroy on HANDLER --echo # OPEN @@ -2347,3 +2667,8 @@ drop table t1, t2, t3, m1, m2; --echo End of 6.0 tests +--disable_result_log +--disable_query_log +eval set global storage_engine=$default; +--enable_result_log +--enable_query_log diff --git a/mysql-test/t/merge_mmap.test b/mysql-test/t/merge_mmap.test index 3468702ca45..c97b029754d 100644 --- a/mysql-test/t/merge_mmap.test +++ b/mysql-test/t/merge_mmap.test @@ -30,6 +30,48 @@ INSERT INTO m2 SELECT * FROM m1; SELECT * FROM m2; SELECT * FROM t2; DROP TABLE m2, m1, t2, t1; +# +# INSERT-SELECT with TEMPORARY select table. +# +CREATE TABLE t1 (c1 INT); +CREATE TABLE t2 (c1 INT); +CREATE TEMPORARY TABLE m1 (c1 INT) ENGINE=MRG_MyISAM UNION=(t1,t2) + INSERT_METHOD=LAST; +CREATE TABLE m2 (c1 INT) ENGINE=MRG_MyISAM UNION=(t1,t2) + INSERT_METHOD=LAST; +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2), (3), (4); +INSERT INTO m2 SELECT * FROM m1; +SELECT * FROM m2; +DROP TABLE m2, m1, t2, t1; +# +# INSERT-SELECT with TEMPORARY insert table. +# +CREATE TABLE t1 (c1 INT); +CREATE TABLE t2 (c1 INT); +CREATE TABLE m1 (c1 INT) ENGINE=MRG_MyISAM UNION=(t1,t2) + INSERT_METHOD=LAST; +CREATE TEMPORARY TABLE m2 (c1 INT) ENGINE=MRG_MyISAM UNION=(t1,t2) + INSERT_METHOD=LAST; +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2), (3), (4); +INSERT INTO m2 SELECT * FROM m1; +SELECT * FROM m2; +DROP TABLE m2, m1, t2, t1; +# +# INSERT-SELECT with TEMPORARY both tables. +# +CREATE TABLE t1 (c1 INT); +CREATE TABLE t2 (c1 INT); +CREATE TEMPORARY TABLE m1 (c1 INT) ENGINE=MRG_MyISAM UNION=(t1,t2) + INSERT_METHOD=LAST; +CREATE TEMPORARY TABLE m2 (c1 INT) ENGINE=MRG_MyISAM UNION=(t1,t2) + INSERT_METHOD=LAST; +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2), (3), (4); +INSERT INTO m2 SELECT * FROM m1; +SELECT * FROM m2; +DROP TABLE m2, m1, t2, t1; #################### ## With LOCK TABLES. @@ -51,6 +93,55 @@ SELECT * FROM m2; SELECT * FROM t2; UNLOCK TABLES; DROP TABLE m2, m1, t2, t1; +# +# INSERT-SELECT with TEMPORARY select table. +# +CREATE TABLE t1 (c1 INT); +CREATE TABLE t2 (c1 INT); +CREATE TEMPORARY TABLE m1 (c1 INT) ENGINE=MRG_MyISAM UNION=(t1,t2) + INSERT_METHOD=LAST; +CREATE TABLE m2 (c1 INT) ENGINE=MRG_MyISAM UNION=(t1,t2) + INSERT_METHOD=LAST; +LOCK TABLE m1 WRITE, m2 WRITE; +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2), (3), (4); +INSERT INTO m2 SELECT * FROM m1; +SELECT * FROM m2; +UNLOCK TABLES; +DROP TABLE m2, m1, t2, t1; +# +# INSERT-SELECT with TEMPORARY insert table. +# +CREATE TABLE t1 (c1 INT); +CREATE TABLE t2 (c1 INT); +CREATE TABLE m1 (c1 INT) ENGINE=MRG_MyISAM UNION=(t1,t2) + INSERT_METHOD=LAST; +CREATE TEMPORARY TABLE m2 (c1 INT) ENGINE=MRG_MyISAM UNION=(t1,t2) + INSERT_METHOD=LAST; +LOCK TABLE m1 WRITE, m2 WRITE; +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2), (3), (4); +INSERT INTO m2 SELECT * FROM m1; +SELECT * FROM m2; +UNLOCK TABLES; +DROP TABLE m2, m1, t2, t1; +# +# INSERT-SELECT with TEMPORARY both tables. +# +CREATE TABLE t1 (c1 INT); +CREATE TABLE t2 (c1 INT); +CREATE TEMPORARY TABLE m1 (c1 INT) ENGINE=MRG_MyISAM UNION=(t1,t2) + INSERT_METHOD=LAST; +CREATE TEMPORARY TABLE m2 (c1 INT) ENGINE=MRG_MyISAM UNION=(t1,t2) + INSERT_METHOD=LAST; +LOCK TABLE m1 WRITE, m2 WRITE; +INSERT INTO t1 VALUES (1); +INSERT INTO t2 VALUES (2), (3), (4); +INSERT INTO m2 SELECT * FROM m1; +SELECT * FROM m2; +UNLOCK TABLES; +DROP TABLE m2, m1, t2, t1; + --echo End of 6.0 tests --disable_result_log diff --git a/storage/myisammrg/ha_myisammrg.cc b/storage/myisammrg/ha_myisammrg.cc index 1681f062117..08aa9dfd0e4 100644 --- a/storage/myisammrg/ha_myisammrg.cc +++ b/storage/myisammrg/ha_myisammrg.cc @@ -476,10 +476,7 @@ int ha_myisammrg::add_children_list(void) child_l->parent_l= parent_l; /* Copy select_lex. Used in unique_table() at least. */ child_l->select_lex= parent_l->select_lex; - /* - Set the expected table version, to not cause spurious re-prepare. - @todo: revise after the fix for Bug#36171 - */ + /* Set the expected table version, to not cause spurious re-prepare. */ child_l->set_table_ref_id(mrg_child_def->get_child_table_ref_type(), mrg_child_def->get_child_def_version()); /* Link TABLE_LIST object into the children list. */ @@ -618,15 +615,17 @@ extern "C" MI_INFO *myisammrg_attach_children_callback(void *callback_param) param->need_compat_check= TRUE; /* - If parent is temporary, children must be temporary too and vice - versa. This check must be done for every child on every open because - the table def version can overlap between temporary and - non-temporary tables. We need to detect the case where a - non-temporary table has been replaced with a temporary table of the - same version. Or vice versa. A very unlikely case, but it could - happen. + If child is temporary, parent must be temporary as well. Other + parent/child combinations are allowed. This check must be done for + every child on every open because the table def version can overlap + between temporary and non-temporary tables. We need to detect the + case where a non-temporary table has been replaced with a temporary + table of the same version. Or vice versa. A very unlikely case, but + it could happen. (Note that the condition was different from + 5.1.23/6.0.4(Bug#19627) to 5.5.6 (Bug#36171): child->s->tmp_table != + parent->s->tmp_table. Tables were required to have the same status.) */ - if (child->s->tmp_table != parent->s->tmp_table) + if (child->s->tmp_table && !parent->s->tmp_table) { DBUG_PRINT("error", ("temporary table mismatch parent: %d child: %d", parent->s->tmp_table, child->s->tmp_table)); From ecfd9958a19d76250f5cb611eaace7b4caa1ad7c Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Mon, 5 Jul 2010 13:59:34 +0200 Subject: [PATCH 063/115] Bug #54401 assert in Diagnostics_area::set_eof_status , HANDLER This assert checks that the server does not try to send EOF to the client if there has been some error during processing. This to make sure that the error is in fact sent to the client. The problem was that any errors during processing of WHERE conditions in HANDLER ... READ statements where not detected by the handler code. The handler code therefore still tried to send EOF to the client, triggering the assert. The bug was only noticeable in debug builds. This patch fixes the problem by making sure that the handler code checks for errors during condition processing and acts accordingly. --- mysql-test/include/handler.inc | 32 ++++++++++++++++++++++++++++++ mysql-test/r/handler_innodb.result | 20 +++++++++++++++++++ mysql-test/r/handler_myisam.result | 20 +++++++++++++++++++ sql/sql_handler.cc | 4 ++++ 4 files changed, 76 insertions(+) diff --git a/mysql-test/include/handler.inc b/mysql-test/include/handler.inc index 0031cb68647..98988ab55ba 100644 --- a/mysql-test/include/handler.inc +++ b/mysql-test/include/handler.inc @@ -1757,3 +1757,35 @@ disconnect con51355; --echo # Connection default connection default; + +--echo # +--echo # Bug#54401 assert in Diagnostics_area::set_eof_status , HANDLER +--echo # + +--disable_warnings +DROP TABLE IF EXISTS t1, t2; +DROP FUNCTION IF EXISTS f1; +--enable_warnings + +delimiter |; +CREATE FUNCTION f1() RETURNS INTEGER +BEGIN + SELECT 1 FROM t2 INTO @a; + RETURN 1; +END| +delimiter ;| + +# Get f1() parsed and cached +--error ER_NO_SUCH_TABLE +SELECT f1(); + +CREATE TABLE t1(a INT); +INSERT INTO t1 VALUES (1); +HANDLER t1 OPEN; +# This used to cause the assert +--error ER_NO_SUCH_TABLE +HANDLER t1 READ FIRST WHERE f1() = 1; +HANDLER t1 CLOSE; + +DROP FUNCTION f1; +DROP TABLE t1; diff --git a/mysql-test/r/handler_innodb.result b/mysql-test/r/handler_innodb.result index 08d2fc58e8a..121cfa89f1c 100644 --- a/mysql-test/r/handler_innodb.result +++ b/mysql-test/r/handler_innodb.result @@ -1710,3 +1710,23 @@ ERROR 42S02: Table 'test.t1' doesn't exist HANDLER t1 CLOSE; # Connection con51355 # Connection default +# +# Bug#54401 assert in Diagnostics_area::set_eof_status , HANDLER +# +DROP TABLE IF EXISTS t1, t2; +DROP FUNCTION IF EXISTS f1; +CREATE FUNCTION f1() RETURNS INTEGER +BEGIN +SELECT 1 FROM t2 INTO @a; +RETURN 1; +END| +SELECT f1(); +ERROR 42S02: Table 'test.t2' doesn't exist +CREATE TABLE t1(a INT); +INSERT INTO t1 VALUES (1); +HANDLER t1 OPEN; +HANDLER t1 READ FIRST WHERE f1() = 1; +ERROR 42S02: Table 'test.t2' doesn't exist +HANDLER t1 CLOSE; +DROP FUNCTION f1; +DROP TABLE t1; diff --git a/mysql-test/r/handler_myisam.result b/mysql-test/r/handler_myisam.result index 31bc828b0b9..fd08fd12f15 100644 --- a/mysql-test/r/handler_myisam.result +++ b/mysql-test/r/handler_myisam.result @@ -1707,6 +1707,26 @@ HANDLER t1 CLOSE; # Connection con51355 # Connection default # +# Bug#54401 assert in Diagnostics_area::set_eof_status , HANDLER +# +DROP TABLE IF EXISTS t1, t2; +DROP FUNCTION IF EXISTS f1; +CREATE FUNCTION f1() RETURNS INTEGER +BEGIN +SELECT 1 FROM t2 INTO @a; +RETURN 1; +END| +SELECT f1(); +ERROR 42S02: Table 'test.t2' doesn't exist +CREATE TABLE t1(a INT); +INSERT INTO t1 VALUES (1); +HANDLER t1 OPEN; +HANDLER t1 READ FIRST WHERE f1() = 1; +ERROR 42S02: Table 'test.t2' doesn't exist +HANDLER t1 CLOSE; +DROP FUNCTION f1; +DROP TABLE t1; +# # BUG #46456: HANDLER OPEN + TRUNCATE + DROP (temporary) TABLE, crash # CREATE TABLE t1 AS SELECT 1 AS f1; diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index d6f2a472e05..e49da2b0423 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -747,7 +747,11 @@ retry: goto ok; } if (cond && !cond->val_int()) + { + if (thd->is_error()) + goto err; continue; + } if (num_rows >= offset_limit_cnt) { protocol->prepare_for_resend(); From f0fe99523c6fe6bb85031415bbd6098c1c17ce97 Mon Sep 17 00:00:00 2001 From: Jimmy Yang Date: Mon, 5 Jul 2010 19:26:38 -0700 Subject: [PATCH 064/115] Add innodb_bug53756-master.opt for innodb_bug53756 test. --- mysql-test/suite/innodb/r/innodb_bug53756.result | 2 +- mysql-test/suite/innodb/t/innodb_bug53756-master.opt | 1 + mysql-test/suite/innodb/t/innodb_bug53756.test | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 mysql-test/suite/innodb/t/innodb_bug53756-master.opt diff --git a/mysql-test/suite/innodb/r/innodb_bug53756.result b/mysql-test/suite/innodb/r/innodb_bug53756.result index 67797f9c90f..37453be8201 100644 --- a/mysql-test/suite/innodb/r/innodb_bug53756.result +++ b/mysql-test/suite/innodb/r/innodb_bug53756.result @@ -78,7 +78,7 @@ ROLLBACK; # connection default selects resulting data. # Delete of row 1 was committed. -# Dpdate of row 3 was committed. +# Update of row 3 was committed. # Due to isolation level read committed, these should be included. # All other changes should not be included. SELECT * FROM bug_53756; diff --git a/mysql-test/suite/innodb/t/innodb_bug53756-master.opt b/mysql-test/suite/innodb/t/innodb_bug53756-master.opt new file mode 100644 index 00000000000..425fda95086 --- /dev/null +++ b/mysql-test/suite/innodb/t/innodb_bug53756-master.opt @@ -0,0 +1 @@ +--skip-stack-trace --skip-core-file diff --git a/mysql-test/suite/innodb/t/innodb_bug53756.test b/mysql-test/suite/innodb/t/innodb_bug53756.test index 85a09478486..8324f2640a2 100644 --- a/mysql-test/suite/innodb/t/innodb_bug53756.test +++ b/mysql-test/suite/innodb/t/innodb_bug53756.test @@ -114,7 +114,7 @@ ROLLBACK; --echo --echo # connection default selects resulting data. --echo # Delete of row 1 was committed. ---echo # Dpdate of row 3 was committed. +--echo # Update of row 3 was committed. --echo # Due to isolation level read committed, these should be included. --echo # All other changes should not be included. --connection default From 5050cd7c895fbb749b19495ef6f9dd4098fd474c Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Wed, 7 Jul 2010 15:20:07 +0200 Subject: [PATCH 065/115] Bug #37521 Row inserted through view not always visible in base table immediately after The problem was that rows inserted in a table by one connection was not immediately visible if another connection queried the table, even if the insert had committed. The reason for the problem was that the server sent a status reply to the client before it actually did the commit. Therefore it was possible to get an OK from the server before the changes were made permanent and visible to other connections. This patch fixes the problem by not sending status messages to the server until any changes made have been committed. No test case added as reproducing the error requires very specific timing betweeen the server and two or more clients. This patch also fixes the following (duplicate) bugs: Bug #29334 pseudo-finished SHOW GLOBAL STATUS Bug #36618 myisam insert not immediately visible to select from another client Bug #45864 insert on one connection, immediate query on another produces no result Bug #51329 Inserts from one connection not immediately visible in second connection Bug #41516 Assertion fails when error returned from handler::external_lock(thd, F_UNLCK) --- sql/sql_parse.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 31eaf4111ce..dd47dfb88af 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1511,13 +1511,13 @@ bool dispatch_command(enum enum_server_command command, THD *thd, thd->transaction.stmt.reset(); - thd->protocol->end_statement(); - query_cache_end_of_result(thd); - thd->proc_info= "closing tables"; /* Free tables */ close_thread_tables(thd); + thd->protocol->end_statement(); + query_cache_end_of_result(thd); + if (!thd->is_error() && !thd->killed_errno()) mysql_audit_general(thd, MYSQL_AUDIT_GENERAL_RESULT, 0, 0); From 75690d7b267fa51609e1e4a9a81878274b725801 Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Wed, 7 Jul 2010 20:51:30 +0300 Subject: [PATCH 066/115] Add the innodb_plugin tests to "make dist". --- mysql-test/Makefile.am | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mysql-test/Makefile.am b/mysql-test/Makefile.am index 06aea68b52d..13e2388858f 100644 --- a/mysql-test/Makefile.am +++ b/mysql-test/Makefile.am @@ -100,6 +100,8 @@ TEST_DIRS = t r include std_data std_data/parts collections \ suite/rpl_ndb suite/rpl_ndb/t suite/rpl_ndb/r \ suite/parts suite/parts/t suite/parts/r suite/parts/inc \ suite/innodb suite/innodb/t suite/innodb/r suite/innodb/include \ + suite/innodb_plugin suite/innodb_plugin/t suite/innodb_plugin/r \ + suite/innodb_plugin/include \ suite/engines suite/engines/funcs suite/engines/iuds suite/engines/rr_trx \ suite/engines/funcs/r suite/engines/funcs/t suite/engines/iuds/r \ suite/engines/iuds/t suite/engines/rr_trx/include suite/engines/rr_trx/r \ From aff388cd09ff30373cd02c8080911a51bc671817 Mon Sep 17 00:00:00 2001 From: sunanda Date: Wed, 7 Jul 2010 20:34:50 +0200 Subject: [PATCH 067/115] rko Mdkeld change, revision 3351.14.134 add innodb_plugin to mysql-test-run default suites was not complete. Bootstrap failed to pick up necessary files needed by test and hence all tests failed. --- mysql-test/Makefile.am | 1 + 1 file changed, 1 insertion(+) diff --git a/mysql-test/Makefile.am b/mysql-test/Makefile.am index 06aea68b52d..066c8d53c14 100644 --- a/mysql-test/Makefile.am +++ b/mysql-test/Makefile.am @@ -100,6 +100,7 @@ TEST_DIRS = t r include std_data std_data/parts collections \ suite/rpl_ndb suite/rpl_ndb/t suite/rpl_ndb/r \ suite/parts suite/parts/t suite/parts/r suite/parts/inc \ suite/innodb suite/innodb/t suite/innodb/r suite/innodb/include \ + suite/innodb_plugin suite/innodb_plugin/t suite/innodb_plugin/r suite/innodb_plugin/include \ suite/engines suite/engines/funcs suite/engines/iuds suite/engines/rr_trx \ suite/engines/funcs/r suite/engines/funcs/t suite/engines/iuds/r \ suite/engines/iuds/t suite/engines/rr_trx/include suite/engines/rr_trx/r \ From a8f71eb38138ee0f1dc23b1eb404006dccf1ec24 Mon Sep 17 00:00:00 2001 From: Jimmy Yang Date: Thu, 8 Jul 2010 00:05:59 -0700 Subject: [PATCH 068/115] Fix Bug #54764 memory allocated in os_aio_array_create is not freed at shutdown rb://395, approved by Sunny Bains --- storage/innobase/os/os0file.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/storage/innobase/os/os0file.c b/storage/innobase/os/os0file.c index c07f8f4bf95..0045b8731e4 100644 --- a/storage/innobase/os/os0file.c +++ b/storage/innobase/os/os0file.c @@ -3233,6 +3233,9 @@ os_aio_array_create( #endif #if defined(LINUX_NATIVE_AIO) + array->aio_ctx = NULL; + array->aio_events = NULL; + /* If we are not using native aio interface then skip this part of initialization. */ if (!srv_use_native_aio) { @@ -3313,6 +3316,13 @@ os_aio_array_free( os_event_free(array->not_full); os_event_free(array->is_empty); +#if defined(LINUX_NATIVE_AIO) + if (srv_use_native_aio) { + ut_free(array->aio_events); + ut_free(array->aio_ctx); + } +#endif /* LINUX_NATIVE_AIO */ + ut_free(array->slots); ut_free(array); } From 8349522bde173c1c2306b9edc4073aa9e02827d9 Mon Sep 17 00:00:00 2001 From: "karen.langford@oracle.com" <> Date: Fri, 9 Jul 2010 14:23:48 +0200 Subject: [PATCH 069/115] Fix bug #55039 Failing assertion: space_id > 0 in fil0fil.c. --- storage/innodb_plugin/dict/dict0crea.c | 18 +++++++++++++++--- storage/innodb_plugin/os/os0file.c | 12 ++++++++++-- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/storage/innodb_plugin/dict/dict0crea.c b/storage/innodb_plugin/dict/dict0crea.c index f185371bfca..09353c45c8c 100644 --- a/storage/innodb_plugin/dict/dict0crea.c +++ b/storage/innodb_plugin/dict/dict0crea.c @@ -240,17 +240,29 @@ dict_build_table_def_step( ibool is_path; mtr_t mtr; ulint space = 0; + ibool file_per_table; ut_ad(mutex_own(&(dict_sys->mutex))); table = node->table; - dict_hdr_get_new_id(&table->id, NULL, - srv_file_per_table ? &space : NULL); + /* Cache the global variable "srv_file_per_table" to + a local variable before using it. Please note + "srv_file_per_table" is not under dict_sys mutex + protection, and could be changed while executing + this function. So better to cache the current value + to a local variable, and all future reference to + "srv_file_per_table" should use this local variable. */ + file_per_table = srv_file_per_table; + + dict_hdr_get_new_id(&table->id, NULL, NULL); thr_get_trx(thr)->table_id = table->id; - if (srv_file_per_table) { + if (file_per_table) { + /* Get a new space id if srv_file_per_table is set */ + dict_hdr_get_new_id(NULL, NULL, &space); + if (UNIV_UNLIKELY(space == ULINT_UNDEFINED)) { return(DB_ERROR); } diff --git a/storage/innodb_plugin/os/os0file.c b/storage/innodb_plugin/os/os0file.c index b244e3974b3..9f937b9def2 100644 --- a/storage/innodb_plugin/os/os0file.c +++ b/storage/innodb_plugin/os/os0file.c @@ -1339,7 +1339,11 @@ try_again: /* When srv_file_per_table is on, file creation failure may not be critical to the whole instance. Do not crash the server in - case of unknown errors. */ + case of unknown errors. + Please note "srv_file_per_table" is a global variable with + no explicit synchronization protection. It could be + changed during this execution path. It might not have the + same value as the one when building the table definition */ if (srv_file_per_table) { retry = os_file_handle_error_no_exit(name, create_mode == OS_FILE_CREATE ? @@ -1426,7 +1430,11 @@ try_again: /* When srv_file_per_table is on, file creation failure may not be critical to the whole instance. Do not crash the server in - case of unknown errors. */ + case of unknown errors. + Please note "srv_file_per_table" is a global variable with + no explicit synchronization protection. It could be + changed during this execution path. It might not have the + same value as the one when building the table definition */ if (srv_file_per_table) { retry = os_file_handle_error_no_exit(name, create_mode == OS_FILE_CREATE ? From e59f416691c9e9bb58c4158158783f35f3568f98 Mon Sep 17 00:00:00 2001 From: Alexey Kopytov Date: Mon, 12 Jul 2010 18:58:55 +0400 Subject: [PATCH 070/115] Bug#55061: Build failing on sol 8 x86 - assembler code vs compiler problem GCC-style inline assembly is not supported by the Sun Studio compilers prior to version 12. Added a check for the Sun Studio version to avoid using _FPU_GETCW() / _FPU_SETCW() when inline assembly is unsupported. This can lead to some differences in floating point calculations on Solaris 8/x86 which, however, is not worth bothering with Sun-style assembly .il templates. --- sql/mysqld.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 99f16b36dfa..598db8d993c 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -190,7 +190,7 @@ typedef fp_except fp_except_t; # define fpu_control_t unsigned int # define _FPU_EXTENDED 0x300 # define _FPU_DOUBLE 0x200 -# if defined(__GNUC__) || defined(__SUNPRO_CC) +# if defined(__GNUC__) || (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x590) # define _FPU_GETCW(cw) asm volatile ("fnstcw %0" : "=m" (*&cw)) # define _FPU_SETCW(cw) asm volatile ("fldcw %0" : : "m" (*&cw)) # else From 5b775faf584562a246a6f9c36a1f8ecf0c2d5c01 Mon Sep 17 00:00:00 2001 From: Jimmy Yang Date: Tue, 13 Jul 2010 02:58:39 -0700 Subject: [PATCH 071/115] Fix Bug #55095 innodb_file_format_check: no warning for illegal value rb://397 approved by Sunny Bains --- mysql-test/suite/rpl/t/rpl_sync-slave.opt | 2 +- storage/innobase/handler/ha_innodb.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mysql-test/suite/rpl/t/rpl_sync-slave.opt b/mysql-test/suite/rpl/t/rpl_sync-slave.opt index 972f9ef8af9..9d8c2c80711 100644 --- a/mysql-test/suite/rpl/t/rpl_sync-slave.opt +++ b/mysql-test/suite/rpl/t/rpl_sync-slave.opt @@ -1 +1 @@ ---sync-relay-log-info=1 --relay-log-recovery=1 --innodb_file_format_check='ON' +--sync-relay-log-info=1 --relay-log-recovery=1 --innodb_file_format_check=1 diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 3bb2fc778cc..3fc9865ffbf 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -10833,7 +10833,7 @@ by the server. Can be set during server startup at command line or configure file, and a read only variable after server startup */ static MYSQL_SYSVAR_BOOL(file_format_check, innobase_file_format_check, - PLUGIN_VAR_OPCMDARG | PLUGIN_VAR_READONLY, + PLUGIN_VAR_NOCMDARG | PLUGIN_VAR_READONLY, "Whether to perform system file format check.", NULL, NULL, TRUE); From cf93bc718151480ec42f0d34ebc3862567ffb8ef Mon Sep 17 00:00:00 2001 From: Dmitry Lenev Date: Tue, 13 Jul 2010 22:01:54 +0400 Subject: [PATCH 072/115] A pre-requisite for patch fixing bug #52044 "FLUSH TABLES WITH READ LOCK and FLUSH TABLES WITH READ LOCK are incompatible", which adds information about waits caused by FLUSH TABLES statement to deadlock detector in MDL subsystem. Remove API supporting caching of pointers to TABLE_SHARE object in MDL subsystem and all code related to it. The problem was that locking requirements of code implementing this API conflicted with locking requirements of code which adds information about waits caused by flushes to deadlock detector in MDL subsystem (the former needed to lock LOCK_open or its future equivalent while having write-lock on MDL_lock's rwlock, and the latter needs to be able to read-lock MDL_lock rwlock while owning LOCK_open or its future equivalent). Since caching of pointers to TABLE_SHARE objects in MDL subsystem didn't bring expected performance benefits we decided to remove caching API rather than try to come up with some complex solution for this problem. --- sql/mdl.cc | 103 ------------------ sql/mdl.h | 4 - sql/sql_base.cc | 282 +++++++++--------------------------------------- 3 files changed, 48 insertions(+), 341 deletions(-) diff --git a/sql/mdl.cc b/sql/mdl.cc index c5b84902823..818b736e597 100644 --- a/sql/mdl.cc +++ b/sql/mdl.cc @@ -284,8 +284,6 @@ public: public: /** The key of the object (data) being protected. */ MDL_key key; - /** A cached reference to the TABLE_SHARE. Protected by LOCK_open. */ - void *m_cached_object; /** Read-write lock protecting this lock context. @@ -362,7 +360,6 @@ public: MDL_lock(const MDL_key *key_arg) : key(key_arg), - m_cached_object(NULL), m_ref_usage(0), m_ref_release(0), m_is_destroyed(FALSE) @@ -370,8 +367,6 @@ public: mysql_prlock_init(key_MDL_lock_rwlock, &m_rwlock); } - /* Overridden for TABLE objects, to support TABLE_SHARE cache in MDL. */ - virtual void release_cached_object() {} virtual ~MDL_lock() { mysql_prlock_destroy(&m_rwlock); @@ -460,25 +455,6 @@ private: }; -/** - A lock implementation for MDL_key::TABLE. -*/ - -class MDL_table_lock: public MDL_object_lock -{ -public: - MDL_table_lock(const MDL_key *key_arg) - : MDL_object_lock(key_arg) - { } -#ifdef DISABLED_UNTIL_GRL_IS_MADE_PART_OF_MDL - virtual void release_cached_object() - { - tdc_release_cached_share(&m_cached_object); - } -#endif -}; - - static MDL_map mdl_locks; extern "C" @@ -695,8 +671,6 @@ void MDL_map::remove(MDL_lock *lock) { uint ref_usage, ref_release; - lock->release_cached_object(); - /* Destroy the MDL_lock object, but ensure that anyone that is holding a reference to the object is not remaining, if so he @@ -860,8 +834,6 @@ inline MDL_lock *MDL_lock::create(const MDL_key *mdl_key) case MDL_key::GLOBAL: case MDL_key::SCHEMA: return new MDL_scoped_lock(mdl_key); - case MDL_key::TABLE: - return new MDL_table_lock(mdl_key); default: return new MDL_object_lock(mdl_key); } @@ -1682,9 +1654,6 @@ MDL_context::try_acquire_lock_impl(MDL_request *mdl_request, m_tickets.push_front(ticket); - if (ticket->get_type() == MDL_EXCLUSIVE) - ticket->clear_cached_object(); - mdl_request->ticket= ticket; } else @@ -1886,9 +1855,6 @@ MDL_context::acquire_lock(MDL_request *mdl_request, ulong lock_wait_timeout) */ DBUG_ASSERT(wait_status == MDL_wait::GRANTED); - if (ticket->get_type() == MDL_EXCLUSIVE) - ticket->clear_cached_object(); - m_tickets.push_front(ticket); mdl_request->ticket= ticket; @@ -2451,75 +2417,6 @@ bool MDL_ticket::has_pending_conflicting_lock() const } -/** - Associate pointer to an opaque object with a lock. - - @param cached_object Pointer to the object - @param release_hook Cleanup function to be called when MDL subsystem - decides to remove lock or associate another object. - - This is used to cache a pointer to TABLE_SHARE in the lock - structure. Such caching can save one acquisition of LOCK_open - and one table definition cache lookup for every table. - - Since the pointer may be stored only inside an acquired lock, - the caching is only effective when there is more than one lock - granted on a given table. - - This function has the following usage pattern: - - try to acquire an MDL lock - - when done, call for get_cached_object(). If it returns NULL, our - thread has the only lock on this table. - - look up TABLE_SHARE in the table definition cache - - call mdl_set_cache_object() to assign the share to the opaque pointer. - - The release hook is invoked when the last shared metadata - lock on this name is released. -*/ - -void -MDL_ticket::set_cached_object(void *cached_object) -{ - DBUG_ENTER("MDL_ticket::set_cached_object"); - DBUG_PRINT("enter", ("db=%s name=%s cached_object=%p", - m_lock->key.db_name(), m_lock->key.name(), - cached_object)); - mysql_mutex_assert_owner(&LOCK_open); - DBUG_ASSERT(m_lock->key.mdl_namespace() == MDL_key::TABLE); - DBUG_ASSERT(!m_lock->m_cached_object); - - m_lock->m_cached_object= cached_object; - - DBUG_VOID_RETURN; -} - - -/** - A helper function to flush the table share cached in MDL. - @pre The ticket is acquired. -*/ - -void MDL_ticket::clear_cached_object() -{ - m_lock->release_cached_object(); -} - - -/** - Get a pointer to an opaque object that associated with the lock. - - @param ticket Lock ticket for the lock which the object is associated to. - - @return Pointer to an opaque object associated with the lock. -*/ - -void *MDL_ticket::get_cached_object() -{ - mysql_mutex_assert_owner(&LOCK_open); - return m_lock->m_cached_object; -} - - /** Releases metadata locks that were acquired after a specific savepoint. diff --git a/sql/mdl.h b/sql/mdl.h index 5c58289aea2..319c16cf6ce 100644 --- a/sql/mdl.h +++ b/sql/mdl.h @@ -398,9 +398,6 @@ public: public: bool has_pending_conflicting_lock() const; - void *get_cached_object(); - void set_cached_object(void *cached_object); - void clear_cached_object(); MDL_context *get_ctx() const { return m_ctx; } bool is_upgradable_or_exclusive() const { @@ -728,7 +725,6 @@ extern "C" const char *set_thd_proc_info(void *thd_arg, const char *info, const char *calling_function, const char *calling_file, const unsigned int calling_line); -extern void tdc_release_cached_share(void *ptr); #ifndef DBUG_OFF extern mysql_mutex_t LOCK_open; #endif diff --git a/sql/sql_base.cc b/sql/sql_base.cc index d57554785b8..f18d5e26f01 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -921,141 +921,6 @@ static void kill_delayed_threads_for_table(TABLE_SHARE *share) } -#ifdef DISABLED_UNTIL_GRL_IS_MADE_PART_OF_MDL -/** - Flush MDL cached objects. - - How MDL table share cache works - ------------------------------- - Since we take a table share from the table definition - cache only after taking an MDL lock, the MDL lock - object is a convenient place to cache a pointer - to the table share. However, not all SQL in MySQL - takes an MDL lock prior to working with the TDC, - various forms of FLUSH TABLES (including SET GLOBAL - read_only) being the one and only exception. - - To make FLUSH TABLES work, and avoid having dangling - references to TABLE_SHARE objects in MDL subsystem - after a flush, we make sure that all references - to table shares are released whenever a flush comes. - This is done in this function. - - To sum up, the following invariants are held: - - no statement can work with a TABLE_SHARE without - a metadata lock. The only exception is FLUSH TABLES. - - a metadata lock object can be used to store - a cached reference (pointer) to the corresponding - TABLE_SHARE, if and only if this TABLE_SHARE is - not stale (version == refresh_version). In other words, - checking TABLE_SHARE version and setting the reference - must happen only in the same critical section protected - by LOCK_open. - - FLUSH will mark all subject TABLE_SHARE objects - as stale, and then will manually release all TABLE_SHARE - references in MDL cache. Since marking TABLE_SHARE - objects is done inside a critical section protected - by LOCK_open and prior to calling flush_mdl_cache(), - it's guaranteed that a flush will take place before - a new reference to the table share is established - in some other connection. -*/ - -bool flush_mdl_cache(THD *thd, TABLE_LIST *table_list) -{ - MDL_request_list mdl_requests; - MDL_request *mdl_request; - - DBUG_ENTER("flush_mdl_cache"); - - if (table_list == NULL) - { - mysql_mutex_lock(&LOCK_open); - for (uint idx= 0 ; idx < table_def_cache.records; idx++) - { - TABLE_SHARE *share=(TABLE_SHARE*) my_hash_element(&table_def_cache, - idx); - if (share->needs_reopen()) - { - mdl_request= MDL_request::create(MDL_key::TABLE, - share->db.str, - share->table_name.str, - MDL_SHARED_HIGH_PRIO, - thd->mem_root); - if (! mdl_request) - { - mysql_mutex_unlock(&LOCK_open); - DBUG_RETURN(TRUE); - } - mdl_requests.push_front(mdl_request); - } - } - mysql_mutex_unlock(&LOCK_open); - } - else - { - for (TABLE_LIST *tables= table_list; tables; tables= tables->next_global) - { - DBUG_ASSERT(tables->mdl_request.type == MDL_SHARED_HIGH_PRIO); - mdl_requests.push_front(&tables->mdl_request); - } - } - - for (MDL_request_list::Iterator it(mdl_requests); - (mdl_request= it++); ) - { - if (thd->mdl_context.try_acquire_lock(mdl_request)) - DBUG_RETURN(TRUE); - if (mdl_request->ticket) - { - mdl_request->ticket->clear_cached_object(); - thd->mdl_context.release_lock(mdl_request->ticket); - } - } - DBUG_RETURN(FALSE); -} - - -/** - @brief Helper function used by MDL subsystem for releasing TABLE_SHARE - objects in cases when it no longer wants to cache reference to it. -*/ - -void tdc_release_cached_share(void *ptr) -{ - TABLE_SHARE **share= (TABLE_SHARE **) ptr; - mysql_mutex_lock(&LOCK_open); - if (*share) - { - release_table_share(*share); - *share= NULL; - broadcast_refresh(); - } - mysql_mutex_unlock(&LOCK_open); -} - - -/** - @brief Mark table share as having one more user (increase its reference - count). - - @param share Table share for which reference count should be increased. -*/ - -static void tdc_reference_table_share(TABLE_SHARE *share) -{ - DBUG_ENTER("tdc_reference_table_share"); - DBUG_ASSERT(share->ref_count); - mysql_mutex_assert_owner(&LOCK_open); - share->ref_count++; - DBUG_PRINT("exit", ("share: 0x%lx ref_count: %u", - (ulong) share, share->ref_count)); - DBUG_VOID_RETURN; -} - - -#endif // DISABLED_UNTIL_GRL_IS_MADE_PART_OF - /* Close all tables which aren't in use by any thread @@ -1167,15 +1032,6 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables, bool have_lock, /* Wait until all threads have closed all the tables we are flushing. */ DBUG_PRINT("info", ("Waiting for other threads to close their open tables")); -#ifdef DISABLED_UNTIL_GRL_IS_MADE_PART_OF - /* - @todo We need to do this for fast refresh as well, otherwise - deadlocks are possible. - */ - if (flush_mdl_cache(thd, tables)) - goto err_with_reopen; -#endif // DISABLED_UNTIL_GRL_IS_MADE_PART_OF_MDL - while (found && ! thd->killed) { found= FALSE; @@ -2969,97 +2825,61 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, DBUG_RETURN(FALSE); mysql_mutex_lock(&LOCK_open); -#ifdef DISABLED_UNTIL_GRL_IS_MADE_PART_OF_MDL - if (!(share= (TABLE_SHARE *) mdl_ticket->get_cached_object())) + + if (!(share= get_table_share_with_create(thd, table_list, key, + key_length, OPEN_VIEW, + &error, + hash_value))) + goto err_unlock2; + + if (share->is_view) { -#endif // DISABLED_UNTIL_GRL_IS_MADE_PART_OF_MDL - if (!(share= get_table_share_with_create(thd, table_list, key, - key_length, OPEN_VIEW, - &error, - hash_value))) - goto err_unlock2; - - if (share->is_view) - { - /* - If parent_l of the table_list is non null then a merge table - has this view as child table, which is not supported. - */ - if (table_list->parent_l) - { - my_error(ER_WRONG_MRG_TABLE, MYF(0)); - goto err_unlock; - } - - /* - This table is a view. Validate its metadata version: in particular, - that it was a view when the statement was prepared. - */ - if (check_and_update_table_version(thd, table_list, share)) - goto err_unlock; - if (table_list->i_s_requested_object & OPEN_TABLE_ONLY) - goto err_unlock; - - /* Open view */ - if (open_new_frm(thd, share, alias, - (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE | - HA_GET_INDEX | HA_TRY_READ_ONLY), - READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD, - thd->open_options, - 0, table_list, mem_root)) - goto err_unlock; - - /* TODO: Don't free this */ - release_table_share(share); - - DBUG_ASSERT(table_list->view); - - mysql_mutex_unlock(&LOCK_open); - DBUG_RETURN(FALSE); - } /* - Note that situation when we are trying to open a table for what - was a view during previous execution of PS will be handled in by - the caller. Here we should simply open our table even if - TABLE_LIST::view is true. + If parent_l of the table_list is non null then a merge table + has this view as child table, which is not supported. */ + if (table_list->parent_l) + { + my_error(ER_WRONG_MRG_TABLE, MYF(0)); + goto err_unlock; + } - if (table_list->i_s_requested_object & OPEN_VIEW_ONLY) + /* + This table is a view. Validate its metadata version: in particular, + that it was a view when the statement was prepared. + */ + if (check_and_update_table_version(thd, table_list, share)) + goto err_unlock; + if (table_list->i_s_requested_object & OPEN_TABLE_ONLY) goto err_unlock; -#ifdef DISABLED_UNTIL_GRL_IS_MADE_PART_OF_MDL - /* - We are going to to store extra reference to the share - in MDL-subsystem so we need to increase reference counter. - */ - if (! share->needs_reopen()) - { - mdl_ticket->set_cached_object(share); - tdc_reference_table_share(share); - } - } - else - { - if (table_list->view) - { - DBUG_ASSERT(thd->m_reprepare_observer); - check_and_update_table_version(thd, table_list, share); - /* Always an error. */ - DBUG_ASSERT(thd->is_error()); - goto err_unlock2; - } - /* When we have cached TABLE_SHARE we know that is not a view. */ - if (table_list->i_s_requested_object & OPEN_VIEW_ONLY) - goto err_unlock2; + /* Open view */ + if (open_new_frm(thd, share, alias, + (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE | + HA_GET_INDEX | HA_TRY_READ_ONLY), + READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD, + thd->open_options, + 0, table_list, mem_root)) + goto err_unlock; - /* - We are going to use this share for construction of new TABLE object - so reference counter should be increased. - */ - tdc_reference_table_share(share); - } -#endif // DISABLED_UNTIL_GRL_IS_MADE_PART_OF_MDL + /* TODO: Don't free this */ + release_table_share(share); + DBUG_ASSERT(table_list->view); + + mysql_mutex_unlock(&LOCK_open); + DBUG_RETURN(FALSE); + } + + /* + Note that situation when we are trying to open a table for what + was a view during previous execution of PS will be handled in by + the caller. Here we should simply open our table even if + TABLE_LIST::view is true. + */ + + if (table_list->i_s_requested_object & OPEN_VIEW_ONLY) + goto err_unlock; /* If the version changes while we're opening the tables, @@ -8860,13 +8680,7 @@ void tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type, automatically deleted once it is no longer referenced. */ share->version= 0; -#ifdef DISABLED_UNTIL_GRL_IS_MADE_PART_OF_MDL - /* - If lock type is not EXCLUSIVE, we must call - MDL_ticket::release_cached_object() here to make sure there - is no self-reference left on the share in MDL_lock. - */ -#endif + while ((table= it++)) free_cache_entry(table); } From 57b985ab56f5ff75f7a476399c628a3a9e1a56b1 Mon Sep 17 00:00:00 2001 From: Sunny Bains Date: Thu, 15 Jul 2010 11:55:15 +1000 Subject: [PATCH 073/115] Fix bug# 54901 assert during recovery when binlog enabled. Remove the pure attribute from a function. The function doesn't qualify as a pure function because it has a side-effect (modifies its parameter). Add a clarifying comment to another function's declaration. --- storage/innobase/include/buf0buf.h | 3 ++- storage/innobase/include/mach0data.h | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/storage/innobase/include/buf0buf.h b/storage/innobase/include/buf0buf.h index 5326ca9c14f..f33ef65ddf2 100644 --- a/storage/innobase/include/buf0buf.h +++ b/storage/innobase/include/buf0buf.h @@ -869,7 +869,8 @@ buf_page_set_accessed( __attribute__((nonnull)); /*********************************************************************//** Gets the buf_block_t handle of a buffered file block if an uncompressed -page frame exists, or NULL. +page frame exists, or NULL. Note: even though bpage is not declared a +const we don't update its value. It is safe to make this pure. @return control block, or NULL */ UNIV_INLINE buf_block_t* diff --git a/storage/innobase/include/mach0data.h b/storage/innobase/include/mach0data.h index 3157e74224d..8434bc73586 100644 --- a/storage/innobase/include/mach0data.h +++ b/storage/innobase/include/mach0data.h @@ -289,8 +289,7 @@ mach_ull_parse_compressed( /*======================*/ byte* ptr, /*!< in: pointer to buffer from where to read */ byte* end_ptr,/*!< in: pointer to end of the buffer */ - ib_uint64_t* val) /*!< out: read value */ - __attribute__((nonnull, pure)); + ib_uint64_t* val); /*!< out: read value */ #ifndef UNIV_HOTBACKUP /*********************************************************//** Reads a double. It is stored in a little-endian format. From 32dcc26b33a8516e5957e7c052593e69ec46a4b9 Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Mon, 19 Jul 2010 17:47:17 +0300 Subject: [PATCH 074/115] fix tree names --- .bzr-mysql/default.conf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.bzr-mysql/default.conf b/.bzr-mysql/default.conf index 77df77c6021..f044f8e62da 100644 --- a/.bzr-mysql/default.conf +++ b/.bzr-mysql/default.conf @@ -1,4 +1,4 @@ [MYSQL] -post_commit_to = "dbg_mysql_security@sun.com" -post_push_to = "dbg_mysql_security@sun.com" -tree_name = "mysql-5.1-security" +post_commit_to = "commits@lists.mysql.com" +post_push_to = "commits@lists.mysql.com" +tree_name = "mysql-5.1" From cf5e69c9042614459b9098548be0b33cb4a4671d Mon Sep 17 00:00:00 2001 From: Sven Sandberg Date: Tue, 20 Jul 2010 17:27:13 +0200 Subject: [PATCH 075/115] BUG#55322: SHOW BINLOG EVENTS increases @@SESSION.MAX_ALLOWED_PACKET Problem: when SHOW BINLOG EVENTS was issued, it increased the value of @@session.max_allowed_packet. This allowed a non-root user to increase the amount of memory used by her thread arbitrarily. Thus, it removes the bound on the amount of system resources used by a client, so it presents a security risk (DoS attack). Fix: it is correct to increase the value of @@session.max_allowed_packet while executing SHOW BINLOG EVENTS (see BUG 30435). However, the increase should only be temporary. Thus, the fix is to restore the value when SHOW BINLOG EVENTS ends. The value of @@session.max_allowed_packet is also increased in mysql_binlog_send (i.e., the binlog dump thread). It is not clear if this can cause any trouble, since normally the client that issues COM_BINLOG_DUMP will not issue any other commands that would be affected by the increased value of @@session.max_allowed_packet. However, we restore the value just in case. --- mysql-test/suite/rpl/r/rpl_packet.result | 8 +++++ mysql-test/suite/rpl/t/rpl_packet.test | 39 +++++++++++++++++++++++- sql/sql_repl.cc | 5 +++ 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/mysql-test/suite/rpl/r/rpl_packet.result b/mysql-test/suite/rpl/r/rpl_packet.result index 0a9495751fe..1ec9259a1fb 100644 --- a/mysql-test/suite/rpl/r/rpl_packet.result +++ b/mysql-test/suite/rpl/r/rpl_packet.result @@ -49,6 +49,14 @@ Slave_IO_Running = No (expect No) SELECT "Got fatal error 1236 from master when reading data from binary log: 'log event entry exceeded max_allowed_packet; Increase max_allowed_packet on master'" AS Last_IO_Error; Last_IO_Error Got fatal error 1236 from master when reading data from binary log: 'log event entry exceeded max_allowed_packet; Increase max_allowed_packet on master' +STOP SLAVE; +RESET SLAVE; +RESET MASTER; +SET @max_allowed_packet_0= @@session.max_allowed_packet; +SHOW BINLOG EVENTS; +SET @max_allowed_packet_1= @@session.max_allowed_packet; +SHOW BINLOG EVENTS; +SET @max_allowed_packet_2= @@session.max_allowed_packet; ==== clean up ==== DROP TABLE t1; SET @@global.max_allowed_packet= 1024; diff --git a/mysql-test/suite/rpl/t/rpl_packet.test b/mysql-test/suite/rpl/t/rpl_packet.test index bfc144c759b..08a533e22ca 100644 --- a/mysql-test/suite/rpl/t/rpl_packet.test +++ b/mysql-test/suite/rpl/t/rpl_packet.test @@ -1,7 +1,12 @@ +# ==== Purpose ==== # # Check replication protocol packet size handling -# Bug#19402 SQL close to the size of the max_allowed_packet fails on slave # +# ==== Related bugs ==== +# Bug#19402 SQL close to the size of the max_allowed_packet fails on slave +# BUG#23755: Replicated event larger that max_allowed_packet infinitely re-transmits +# BUG#42914: No LAST_IO_ERROR for max_allowed_packet errors +# BUG#55322: SHOW BINLOG EVENTS increases @@SESSION.MAX_ALLOWED_PACKET # max-out size db name source include/master-slave.inc; @@ -119,6 +124,38 @@ let $slave_io_running= query_get_value(SHOW SLAVE STATUS, Slave_IO_Running, 1); let $last_io_error= query_get_value(SHOW SLAVE STATUS, Last_IO_Error, 1); eval SELECT "$last_io_error" AS Last_IO_Error; +# Remove the bad binlog and clear error status on slave. +STOP SLAVE; +RESET SLAVE; +--connection master +RESET MASTER; + + +# +# BUG#55322: SHOW BINLOG EVENTS increases @@SESSION.MAX_ALLOWED_PACKET +# +# In BUG#55322, @@session.max_allowed_packet increased each time SHOW +# BINLOG EVENTS was issued. To verify that this bug is fixed, we +# execute SHOW BINLOG EVENTS twice and check that max_allowed_packet +# never changes. We turn off the result log because we don't care +# about the contents of the binlog. + +--disable_result_log +SET @max_allowed_packet_0= @@session.max_allowed_packet; +SHOW BINLOG EVENTS; +SET @max_allowed_packet_1= @@session.max_allowed_packet; +SHOW BINLOG EVENTS; +SET @max_allowed_packet_2= @@session.max_allowed_packet; +--enable_result_log +if (`SELECT NOT(@max_allowed_packet_0 = @max_allowed_packet_1 AND @max_allowed_packet_1 = @max_allowed_packet_2)`) +{ + --echo ERROR: max_allowed_packet changed after executing SHOW BINLOG EVENTS + --source include/show_rpl_debug_info.inc + SELECT @max_allowed_packet_0, @max_allowed_packet_1, @max_allowed_packet_2; + --die @max_allowed_packet changed after executing SHOW BINLOG EVENTS +} + + --echo ==== clean up ==== connection master; DROP TABLE t1; diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index c220f609c09..dcbc982b4aa 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -357,6 +357,7 @@ void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos, #ifndef DBUG_OFF int left_events = max_binlog_dump_events; #endif + int old_max_allowed_packet= thd->variables.max_allowed_packet; DBUG_ENTER("mysql_binlog_send"); DBUG_PRINT("enter",("log_ident: '%s' pos: %ld", log_ident, (long) pos)); @@ -762,6 +763,7 @@ end: pthread_mutex_lock(&LOCK_thread_count); thd->current_linfo = 0; pthread_mutex_unlock(&LOCK_thread_count); + thd->variables.max_allowed_packet= old_max_allowed_packet; DBUG_VOID_RETURN; err: @@ -779,6 +781,7 @@ err: pthread_mutex_unlock(&LOCK_thread_count); if (file >= 0) (void) my_close(file, MYF(MY_WME)); + thd->variables.max_allowed_packet= old_max_allowed_packet; my_message(my_errno, errmsg, MYF(0)); DBUG_VOID_RETURN; @@ -1422,6 +1425,7 @@ bool mysql_show_binlog_events(THD* thd) bool ret = TRUE; IO_CACHE log; File file = -1; + int old_max_allowed_packet= thd->variables.max_allowed_packet; DBUG_ENTER("mysql_show_binlog_events"); Log_event::init_show_field_list(&field_list); @@ -1560,6 +1564,7 @@ err: pthread_mutex_lock(&LOCK_thread_count); thd->current_linfo = 0; pthread_mutex_unlock(&LOCK_thread_count); + thd->variables.max_allowed_packet= old_max_allowed_packet; DBUG_RETURN(ret); } From 17b9155f00f9f80c1c1f15bb98baebe21ac2a9e1 Mon Sep 17 00:00:00 2001 From: Davi Arnaut Date: Tue, 20 Jul 2010 14:36:15 -0300 Subject: [PATCH 076/115] Bug#54453: Failing assertion: trx->active_trans when renaming a table with active trx Essentially, the problem is that InnoDB does a implicit commit when a cursor (table handler) is unlocked/closed, creating a dissonance between the transaction state within the server layer and the storage engine layer. Theoretically, a statement transaction can encompass several table instances in a similar manner to a multiple statement transaction, hence it does not make sense to limit a statement transaction to the lifetime of the table instances (cursors) used within it. Since this particular instance of the problem is only triggerable on 5.1 and is masked on 5.5 due 2PC being skipped (assertion is in the prepare phase of a 2PC), the solution (which is less risky) is to explicitly end the transaction before the cached table is unlock on rename table. The patch is to be null merged into trunk. --- mysql-test/include/commit.inc | 4 ++-- mysql-test/r/commit_1innodb.result | 4 ++-- .../suite/innodb_plugin/r/innodb_bug54453.result | 9 +++++++++ .../suite/innodb_plugin/t/innodb_bug54453.test | 15 +++++++++++++++ sql/sql_table.cc | 8 ++++++++ 5 files changed, 36 insertions(+), 4 deletions(-) create mode 100644 mysql-test/suite/innodb_plugin/r/innodb_bug54453.result create mode 100644 mysql-test/suite/innodb_plugin/t/innodb_bug54453.test diff --git a/mysql-test/include/commit.inc b/mysql-test/include/commit.inc index d91ba8291fd..d412eae8364 100644 --- a/mysql-test/include/commit.inc +++ b/mysql-test/include/commit.inc @@ -725,9 +725,9 @@ call p_verify_status_increment(4, 4, 4, 4); alter table t3 add column (b int); call p_verify_status_increment(2, 0, 2, 0); alter table t3 rename t4; -call p_verify_status_increment(2, 2, 2, 2); +call p_verify_status_increment(1, 0, 1, 0); rename table t4 to t3; -call p_verify_status_increment(2, 2, 2, 2); +call p_verify_status_increment(1, 0, 1, 0); truncate table t3; call p_verify_status_increment(4, 4, 4, 4); create view v1 as select * from t2; diff --git a/mysql-test/r/commit_1innodb.result b/mysql-test/r/commit_1innodb.result index 51c4ac3002c..1f0b2c8019b 100644 --- a/mysql-test/r/commit_1innodb.result +++ b/mysql-test/r/commit_1innodb.result @@ -841,11 +841,11 @@ call p_verify_status_increment(2, 0, 2, 0); SUCCESS alter table t3 rename t4; -call p_verify_status_increment(2, 2, 2, 2); +call p_verify_status_increment(1, 0, 1, 0); SUCCESS rename table t4 to t3; -call p_verify_status_increment(2, 2, 2, 2); +call p_verify_status_increment(1, 0, 1, 0); SUCCESS truncate table t3; diff --git a/mysql-test/suite/innodb_plugin/r/innodb_bug54453.result b/mysql-test/suite/innodb_plugin/r/innodb_bug54453.result new file mode 100644 index 00000000000..e623989a9d4 --- /dev/null +++ b/mysql-test/suite/innodb_plugin/r/innodb_bug54453.result @@ -0,0 +1,9 @@ +# +# Bug#54453: Failing assertion: trx->active_trans when renaming a table with active trx +# +DROP TABLE IF EXISTS bug54453; +CREATE TABLE bug54453(a INT) ENGINE=InnoDB; +ALTER TABLE bug54453 RENAME TO bug54453_2; +SELECT * FROM bug54453_2; +a +DROP TABLE bug54453_2; diff --git a/mysql-test/suite/innodb_plugin/t/innodb_bug54453.test b/mysql-test/suite/innodb_plugin/t/innodb_bug54453.test new file mode 100644 index 00000000000..486695d326d --- /dev/null +++ b/mysql-test/suite/innodb_plugin/t/innodb_bug54453.test @@ -0,0 +1,15 @@ +--source include/have_innodb_plugin.inc +--source include/have_log_bin.inc + +--echo # +--echo # Bug#54453: Failing assertion: trx->active_trans when renaming a table with active trx +--echo # + +--disable_warnings +DROP TABLE IF EXISTS bug54453; +--enable_warnings + +CREATE TABLE bug54453(a INT) ENGINE=InnoDB; +ALTER TABLE bug54453 RENAME TO bug54453_2; +SELECT * FROM bug54453_2; +DROP TABLE bug54453_2; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 50045ec6d90..47b91fcca0e 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -6848,6 +6848,14 @@ view_err: if (!error && (new_name != table_name || new_db != db)) { thd_proc_info(thd, "rename"); + + /* + Workaround InnoDB ending the transaction when the table instance + is unlocked/closed (close_cached_table below), otherwise the trx + state will differ between the server and storage engine layers. + */ + ha_autocommit_or_rollback(thd, 0); + /* Then do a 'simple' rename of the table. First we need to close all instances of 'source' table. From d676c3ff0eef8613ac689df8ed07ecdd0a39817b Mon Sep 17 00:00:00 2001 From: Davi Arnaut Date: Tue, 20 Jul 2010 14:44:29 -0300 Subject: [PATCH 077/115] Bug#52514: mysql 5.1 do_abi_check does not compile w/ gcc4.5 due to GCC preprocessor change The problem is that newer GCC versions treats missing headers as fatal errors. The solution is to use a guard macro to prevent the inclusion of system headers when checking the ABI with the C Preprocessor. Reference: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=15638 http://gcc.gnu.org/bugzilla/show_bug.cgi?id=44836 --- Makefile.am | 2 +- configure.in | 11 +---------- include/mysql.h | 2 ++ include/mysql.h.pp | 1 - 4 files changed, 4 insertions(+), 12 deletions(-) diff --git a/Makefile.am b/Makefile.am index 7953b81fb7b..4ce753ad8aa 100644 --- a/Makefile.am +++ b/Makefile.am @@ -314,7 +314,7 @@ abi_check_all: $(TEST_PREPROCESSOR_HEADER) do_abi_check: set -ex; \ for file in $(abi_headers); do \ - @CC@ -E -nostdinc -dI \ + @CC@ -E -nostdinc -dI -DMYSQL_ABI_CHECK \ -I$(top_srcdir)/include \ -I$(top_srcdir)/include/mysql \ -I$(top_srcdir)/sql \ diff --git a/configure.in b/configure.in index 0264c351b07..8dcdecf8ea2 100644 --- a/configure.in +++ b/configure.in @@ -444,16 +444,7 @@ if test "$GCC" != "yes" || expr "$CC" : ".*icc.*" then ABI_CHECK="" else - # Workaround GCC >= 4.5 - See Bug#52514 - case `$CC -dumpversion` in - [[4-9]].[[5-9]]*) - AC_MSG_WARN([ABI check disabled (GCC >= 4.5)]) - ABI_CHECK="" - ;; - *) - ABI_CHECK="abi_check" - ;; - esac + ABI_CHECK="abi_check" fi AC_SUBST(ABI_CHECK) diff --git a/include/mysql.h b/include/mysql.h index d114afb6c93..dcf3e167e6a 100644 --- a/include/mysql.h +++ b/include/mysql.h @@ -44,7 +44,9 @@ extern "C" { #endif #ifndef _global_h /* If not standard header */ +#ifndef MYSQL_ABI_CHECK #include +#endif #ifdef __LCC__ #include /* For windows */ #endif diff --git a/include/mysql.h.pp b/include/mysql.h.pp index 633cde41130..0a397863022 100644 --- a/include/mysql.h.pp +++ b/include/mysql.h.pp @@ -1,4 +1,3 @@ -#include typedef char my_bool; typedef int my_socket; #include "mysql_version.h" From 8d407c5f704c523205d52eabd89ca7fd366cf6eb Mon Sep 17 00:00:00 2001 From: Davi Arnaut Date: Tue, 20 Jul 2010 14:55:16 -0300 Subject: [PATCH 078/115] Fix tree name. --- .bzr-mysql/default.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.bzr-mysql/default.conf b/.bzr-mysql/default.conf index 4eab3d239d0..658c8ba845b 100644 --- a/.bzr-mysql/default.conf +++ b/.bzr-mysql/default.conf @@ -1,4 +1,4 @@ [MYSQL] post_commit_to = "commits@lists.mysql.com" post_push_to = "commits@lists.mysql.com" -tree_name = "mysql-trunk-bugfixing" +tree_name = "mysql-trunk-merge" From c96b249fc3912f7a86f885cc62da1a3eeed537c6 Mon Sep 17 00:00:00 2001 From: Davi Arnaut Date: Tue, 20 Jul 2010 15:07:36 -0300 Subject: [PATCH 079/115] Bug#45288: pb2 returns a lot of compilation warnings on linux Fix warnings flagged by the new warning option -Wunused-but-set-variable that was added to GCC 4.6 and that is enabled by -Wunused and -Wall. The option causes a warning whenever a local variable is assigned to but is later unused. It also warns about meaningless pointer dereferences. --- client/mysql.cc | 4 +- client/mysql_upgrade.c | 5 +- extra/comp_err.c | 2 - extra/yassl/src/yassl_imp.cpp | 24 ++---- include/my_pthread.h | 5 +- include/mysys_err.h | 4 +- mysys/errors.c | 6 +- mysys/mf_iocache.c | 11 ++- mysys/my_copy.c | 20 ++++- mysys/my_redel.c | 24 ++++-- regex/engine.c | 17 ++--- .../instance-manager/instance_options.cc | 3 - sql/field.cc | 2 - sql/item.cc | 7 +- sql/log.cc | 73 ++++++++++--------- sql/mysqld.cc | 7 +- sql/partition_info.cc | 2 - sql/slave.cc | 7 +- sql/spatial.cc | 3 +- sql/sql_acl.cc | 2 - sql/sql_base.cc | 14 +--- sql/sql_load.cc | 13 ++-- sql/sql_parse.cc | 17 +++-- sql/sql_repl.cc | 7 +- sql/sql_show.cc | 4 +- sql/sql_table.cc | 2 - sql/table.cc | 3 +- sql/udf_example.c | 2 +- storage/csv/ha_tina.cc | 12 +-- storage/example/ha_example.cc | 2 +- storage/myisam/mi_locking.c | 14 +--- storage/myisam/rt_split.c | 3 - storage/myisammrg/myrg_open.c | 5 -- strings/decimal.c | 3 +- 34 files changed, 155 insertions(+), 174 deletions(-) diff --git a/client/mysql.cc b/client/mysql.cc index edcc72b60bf..5b90f318629 100644 --- a/client/mysql.cc +++ b/client/mysql.cc @@ -3639,7 +3639,7 @@ xmlencode_print(const char *src, uint length) tee_fputs("NULL", PAGER); else { - for (const char *p = src; length; *p++, length--) + for (const char *p = src; length; p++, length--) { const char *t; if ((t = array_value(xmlmeta, *p))) @@ -4726,7 +4726,7 @@ static const char* construct_prompt() struct tm *t = localtime(&lclock); /* parse thru the settings for the prompt */ - for (char *c = current_prompt; *c ; *c++) + for (char *c = current_prompt; *c ; c++) { if (*c != PROMPT_CHAR) processed_prompt.append(*c); diff --git a/client/mysql_upgrade.c b/client/mysql_upgrade.c index 0b8b43775ed..3122cc25731 100644 --- a/client/mysql_upgrade.c +++ b/client/mysql_upgrade.c @@ -595,7 +595,10 @@ static int upgrade_already_done(void) my_fclose(in, MYF(0)); - return (strncmp(buf, MYSQL_SERVER_VERSION, + if (!res) + return 0; /* Could not read from file => not sure */ + + return (strncmp(res, MYSQL_SERVER_VERSION, sizeof(MYSQL_SERVER_VERSION)-1)==0); } diff --git a/extra/comp_err.c b/extra/comp_err.c index c02c7ca3d2a..7e0b47a7dc7 100644 --- a/extra/comp_err.c +++ b/extra/comp_err.c @@ -831,7 +831,6 @@ static struct message *parse_message_string(struct message *new_message, static struct errors *parse_error_string(char *str, int er_count) { struct errors *new_error; - char *start; DBUG_ENTER("parse_error_string"); DBUG_PRINT("enter", ("str: %s", str)); @@ -842,7 +841,6 @@ static struct errors *parse_error_string(char *str, int er_count) DBUG_RETURN(0); /* OOM: Fatal error */ /* getting the error name */ - start= str; str= skip_delimiters(str); if (!(new_error->er_name= get_word(&str))) diff --git a/extra/yassl/src/yassl_imp.cpp b/extra/yassl/src/yassl_imp.cpp index f079df8c7ce..86799f961ae 100644 --- a/extra/yassl/src/yassl_imp.cpp +++ b/extra/yassl/src/yassl_imp.cpp @@ -884,21 +884,19 @@ void Alert::Process(input_buffer& input, SSL& ssl) else hmac(ssl, verify, data, aSz, alert, true); - // read mac and fill + // read mac and skip fill int digestSz = ssl.getCrypto().get_digest().get_digestSize(); opaque mac[SHA_LEN]; input.read(mac, digestSz); if (ssl.getSecurity().get_parms().cipher_type_ == block) { int ivExtra = 0; - opaque fill; if (ssl.isTLSv1_1()) ivExtra = ssl.getCrypto().get_cipher().get_blockSize(); int padSz = ssl.getSecurity().get_parms().encrypt_size_ - ivExtra - aSz - digestSz; - for (int i = 0; i < padSz; i++) - fill = input[AUTO]; + input.set_current(input.get_current() + padSz); } // verify @@ -981,17 +979,17 @@ output_buffer& operator<<(output_buffer& output, const Data& data) void Data::Process(input_buffer& input, SSL& ssl) { int msgSz = ssl.getSecurity().get_parms().encrypt_size_; - int pad = 0, padByte = 0; + int pad = 0, padSz = 0; int ivExtra = 0; if (ssl.getSecurity().get_parms().cipher_type_ == block) { if (ssl.isTLSv1_1()) // IV ivExtra = ssl.getCrypto().get_cipher().get_blockSize(); pad = *(input.get_buffer() + input.get_current() + msgSz -ivExtra - 1); - padByte = 1; + padSz = 1; } int digestSz = ssl.getCrypto().get_digest().get_digestSize(); - int dataSz = msgSz - ivExtra - digestSz - pad - padByte; + int dataSz = msgSz - ivExtra - digestSz - pad - padSz; opaque verify[SHA_LEN]; const byte* rawData = input.get_buffer() + input.get_current(); @@ -1020,14 +1018,10 @@ void Data::Process(input_buffer& input, SSL& ssl) hmac(ssl, verify, rawData, dataSz, application_data, true); } - // read mac and fill + // read mac and skip fill opaque mac[SHA_LEN]; - opaque fill; input.read(mac, digestSz); - for (int i = 0; i < pad; i++) - fill = input[AUTO]; - if (padByte) - fill = input[AUTO]; + input.set_current(input.get_current() + pad + padSz); // verify if (dataSz) { @@ -2073,11 +2067,9 @@ void Finished::Process(input_buffer& input, SSL& ssl) if (ssl.isTLSv1_1()) ivExtra = ssl.getCrypto().get_cipher().get_blockSize(); - opaque fill; int padSz = ssl.getSecurity().get_parms().encrypt_size_ - ivExtra - HANDSHAKE_HEADER - finishedSz - digestSz; - for (int i = 0; i < padSz; i++) - fill = input[AUTO]; + input.set_current(input.get_current() + padSz); // verify mac if (memcmp(mac, verifyMAC, digestSz)) { diff --git a/include/my_pthread.h b/include/my_pthread.h index eff6a677192..fec7c972a7b 100644 --- a/include/my_pthread.h +++ b/include/my_pthread.h @@ -269,13 +269,14 @@ int sigwait(sigset_t *setp, int *sigp); /* Use our implemention */ we want to make sure that no such flags are set. */ #if defined(HAVE_SIGACTION) && !defined(my_sigset) -#define my_sigset(A,B) do { struct sigaction l_s; sigset_t l_set; int l_rc; \ +#define my_sigset(A,B) do { struct sigaction l_s; sigset_t l_set; \ + IF_DBUG(int l_rc); \ DBUG_ASSERT((A) != 0); \ sigemptyset(&l_set); \ l_s.sa_handler = (B); \ l_s.sa_mask = l_set; \ l_s.sa_flags = 0; \ - l_rc= sigaction((A), &l_s, (struct sigaction *) NULL);\ + IF_DBUG(l_rc=) sigaction((A), &l_s, NULL); \ DBUG_ASSERT(l_rc == 0); \ } while (0) #elif defined(HAVE_SIGSET) && !defined(my_sigset) diff --git a/include/mysys_err.h b/include/mysys_err.h index 09e77248c17..6294b37f773 100644 --- a/include/mysys_err.h +++ b/include/mysys_err.h @@ -62,7 +62,9 @@ extern const char * NEAR globerrs[]; /* my_error_messages is here */ #define EE_UNKNOWN_COLLATION 28 #define EE_FILENOTFOUND 29 #define EE_FILE_NOT_CLOSED 30 -#define EE_ERROR_LAST 30 /* Copy last error nr */ +#define EE_CHANGE_OWNERSHIP 31 +#define EE_CHANGE_PERMISSIONS 32 +#define EE_ERROR_LAST 32 /* Copy last error nr */ /* Add error numbers before EE_ERROR_LAST and change it accordingly. */ /* exit codes for all MySQL programs */ diff --git a/mysys/errors.c b/mysys/errors.c index 8d3303cac9f..a5ad4a956ab 100644 --- a/mysys/errors.c +++ b/mysys/errors.c @@ -49,7 +49,9 @@ const char * NEAR globerrs[GLOBERRS]= "Can't sync file '%s' to disk (Errcode: %d)", "Collation '%s' is not a compiled collation and is not specified in the '%s' file", "File '%s' not found (Errcode: %d)", - "File '%s' (fileno: %d) was not closed" + "File '%s' (fileno: %d) was not closed", + "Can't change ownership of the file '%s' (Errcode: %d)", + "Can't change permissions of the file '%s' (Errcode: %d)", }; void init_glob_errs(void) @@ -90,6 +92,8 @@ void init_glob_errs() EE(EE_UNKNOWN_COLLATION)= "Collation '%s' is not a compiled collation and is not specified in the %s file"; EE(EE_FILENOTFOUND) = "File '%s' not found (Errcode: %d)"; EE(EE_FILE_NOT_CLOSED) = "File '%s' (fileno: %d) was not closed"; + EE(EE_CHANGE_OWNERSHIP) = "Can't change ownership of the file '%s' (Errcode: %d)"; + EE(EE_CHANGE_PERMISSIONS) = "Can't change permissions of the file '%s' (Errcode: %d)"; } #endif diff --git a/mysys/mf_iocache.c b/mysys/mf_iocache.c index 1a47982b221..e9b947b04a6 100644 --- a/mysys/mf_iocache.c +++ b/mysys/mf_iocache.c @@ -1701,16 +1701,19 @@ int my_block_write(register IO_CACHE *info, const uchar *Buffer, size_t Count, #endif -int my_b_flush_io_cache(IO_CACHE *info, int need_append_buffer_lock) +int my_b_flush_io_cache(IO_CACHE *info, + int need_append_buffer_lock __attribute__((unused))) { size_t length; - my_bool append_cache; my_off_t pos_in_file; + my_bool append_cache= (info->type == SEQ_READ_APPEND); DBUG_ENTER("my_b_flush_io_cache"); DBUG_PRINT("enter", ("cache: 0x%lx", (long) info)); - if (!(append_cache = (info->type == SEQ_READ_APPEND))) - need_append_buffer_lock=0; +#ifdef THREAD + if (!append_cache) + need_append_buffer_lock= 0; +#endif if (info->type == WRITE_CACHE || append_cache) { diff --git a/mysys/my_copy.c b/mysys/my_copy.c index 418e2b6f8a2..d0c1fc29229 100644 --- a/mysys/my_copy.c +++ b/mysys/my_copy.c @@ -16,6 +16,7 @@ #include "mysys_priv.h" #include /* for stat */ #include +#include "mysys_err.h" #if defined(HAVE_UTIME_H) #include #elif defined(HAVE_SYS_UTIME_H) @@ -56,7 +57,6 @@ int my_copy(const char *from, const char *to, myf MyFlags) File from_file,to_file; uchar buff[IO_SIZE]; MY_STAT stat_buff,new_stat_buff; - int res; DBUG_ENTER("my_copy"); DBUG_PRINT("my",("from %s to %s MyFlags %d", from, to, MyFlags)); @@ -102,9 +102,23 @@ int my_copy(const char *from, const char *to, myf MyFlags) if (MyFlags & MY_HOLD_ORIGINAL_MODES && !new_file_stat) DBUG_RETURN(0); /* File copyed but not stat */ - res= chmod(to, stat_buff.st_mode & 07777); /* Copy modes */ + /* Copy modes */ + if (chmod(to, stat_buff.st_mode & 07777)) + { + my_errno= errno; + if (MyFlags & (MY_FAE+MY_WME)) + my_error(EE_CHANGE_PERMISSIONS, MYF(ME_BELL+ME_WAITTANG), from, errno); + goto err; + } #if !defined(__WIN__) && !defined(__NETWARE__) - res= chown(to, stat_buff.st_uid,stat_buff.st_gid); /* Copy ownership */ + /* Copy ownership */ + if (chown(to, stat_buff.st_uid,stat_buff.st_gid)) + { + my_errno= errno; + if (MyFlags & (MY_FAE+MY_WME)) + my_error(EE_CHANGE_OWNERSHIP, MYF(ME_BELL+ME_WAITTANG), from, errno); + goto err; + } #endif #if !defined(VMS) && !defined(__ZTC__) if (MyFlags & MY_COPYTIME) diff --git a/mysys/my_redel.c b/mysys/my_redel.c index 6521253f949..4013c5c8323 100644 --- a/mysys/my_redel.c +++ b/mysys/my_redel.c @@ -76,11 +76,8 @@ end: int my_copystat(const char *from, const char *to, int MyFlags) { struct stat statbuf; -#if !defined(__WIN__) && !defined(__NETWARE__) - int res; -#endif - if (stat((char*) from, &statbuf)) + if (stat(from, &statbuf)) { my_errno=errno; if (MyFlags & (MY_FAE+MY_WME)) @@ -89,7 +86,15 @@ int my_copystat(const char *from, const char *to, int MyFlags) } if ((statbuf.st_mode & S_IFMT) != S_IFREG) return 1; - VOID(chmod(to, statbuf.st_mode & 07777)); /* Copy modes */ + + /* Copy modes */ + if (chmod(to, statbuf.st_mode & 07777)) + { + my_errno= errno; + if (MyFlags & (MY_FAE+MY_WME)) + my_error(EE_CHANGE_PERMISSIONS, MYF(ME_BELL+ME_WAITTANG), from, errno); + return -1; + } #if !defined(__WIN__) && !defined(__NETWARE__) if (statbuf.st_nlink > 1 && MyFlags & MY_LINK_WARNING) @@ -97,7 +102,14 @@ int my_copystat(const char *from, const char *to, int MyFlags) if (MyFlags & MY_LINK_WARNING) my_error(EE_LINK_WARNING,MYF(ME_BELL+ME_WAITTANG),from,statbuf.st_nlink); } - res= chown(to, statbuf.st_uid, statbuf.st_gid); /* Copy ownership */ + /* Copy ownership */ + if (chown(to, statbuf.st_uid, statbuf.st_gid)) + { + my_errno= errno; + if (MyFlags & (MY_FAE+MY_WME)) + my_error(EE_CHANGE_OWNERSHIP, MYF(ME_BELL+ME_WAITTANG), from, errno); + return -1; + } #endif /* !__WIN__ && !__NETWARE__ */ #ifndef VMS diff --git a/regex/engine.c b/regex/engine.c index 1968ca61a96..be08adf8601 100644 --- a/regex/engine.c +++ b/regex/engine.c @@ -256,7 +256,6 @@ sopno stopst; register char *ssp; /* start of string matched by subsubRE */ register char *sep; /* end of string matched by subsubRE */ register char *oldssp; /* previous ssp */ - register char *dp; /* used in debug mode to check asserts */ AT("diss", start, stop, startst, stopst); sp = start; @@ -314,11 +313,9 @@ sopno stopst; ssub = ss + 1; esub = es - 1; /* did innards match? */ - if (slow(charset, m, sp, rest, ssub, esub) != NULL) { - dp = dissect(charset, m, sp, rest, ssub, esub); - assert(dp == rest); - } else /* no */ - assert(sp == rest); + if (slow(charset, m, sp, rest, ssub, esub) != NULL) + sp = dissect(charset, m, sp, rest, ssub, esub); + assert(sp == rest); sp = rest; break; case OPLUS_: @@ -353,8 +350,8 @@ sopno stopst; } assert(sep == rest); /* must exhaust substring */ assert(slow(charset, m, ssp, sep, ssub, esub) == rest); - dp = dissect(charset, m, ssp, sep, ssub, esub); - assert(dp == sep); + sp = dissect(charset, m, ssp, sep, ssub, esub); + assert(sp == sep); sp = rest; break; case OCH_: @@ -388,8 +385,8 @@ sopno stopst; else assert(OP(m->g->strip[esub]) == O_CH); } - dp = dissect(charset, m, sp, rest, ssub, esub); - assert(dp == rest); + sp = dissect(charset, m, sp, rest, ssub, esub); + assert(sp == rest); sp = rest; break; case O_PLUS: diff --git a/server-tools/instance-manager/instance_options.cc b/server-tools/instance-manager/instance_options.cc index 8b96d6f0f96..725089e2f42 100644 --- a/server-tools/instance-manager/instance_options.cc +++ b/server-tools/instance-manager/instance_options.cc @@ -303,7 +303,6 @@ bool Instance_options::fill_log_options() enum { MAX_LOG_OPTION_LENGTH= 256 }; char datadir[MAX_LOG_OPTION_LENGTH]; char hostname[MAX_LOG_OPTION_LENGTH]; - uint hostname_length; struct log_files_st { const char *name; @@ -335,8 +334,6 @@ bool Instance_options::fill_log_options() strmov(hostname, "mysql"); hostname[MAX_LOG_OPTION_LENGTH - 1]= 0; /* Safety */ - hostname_length= strlen(hostname); - for (log_files= logs_st; log_files->name; log_files++) { diff --git a/sql/field.cc b/sql/field.cc index c648b53e139..c887a5f1c9b 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -5541,7 +5541,6 @@ int Field_date::store(const char *from, uint len,CHARSET_INFO *cs) int Field_date::store(double nr) { longlong tmp; - int error= 0; if (nr >= 19000000000000.0 && nr <= 99991231235959.0) nr=floor(nr/1000000.0); // Timestamp to date if (nr < 0.0 || nr > 99991231.0) @@ -5550,7 +5549,6 @@ int Field_date::store(double nr) set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, nr, MYSQL_TIMESTAMP_DATE); - error= 1; } else tmp= (longlong) rint(nr); diff --git a/sql/item.cc b/sql/item.cc index db2c4c0974b..66c5314c16e 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -255,11 +255,10 @@ my_decimal *Item::val_decimal_from_int(my_decimal *decimal_value) my_decimal *Item::val_decimal_from_string(my_decimal *decimal_value) { String *res; - char *end_ptr; - if (!(res= val_str(&str_value))) - return 0; // NULL or EOM - end_ptr= (char*) res->ptr()+ res->length(); + if (!(res= val_str(&str_value))) + return NULL; + if (str2my_decimal(E_DEC_FATAL_ERROR & ~E_DEC_BAD_NUM, res->ptr(), res->length(), res->charset(), decimal_value) & E_DEC_BAD_NUM) diff --git a/sql/log.cc b/sql/log.cc index d8d5f6fa418..614a07e6b63 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -5050,6 +5050,22 @@ void sql_perror(const char *message) } +/* + Unfortunately, there seems to be no good way + to restore the original streams upon failure. +*/ +static bool redirect_std_streams(const char *file) +{ + if (freopen(file, "a+", stdout) && freopen(file, "a+", stderr)) + { + setbuf(stderr, NULL); + return FALSE; + } + + return TRUE; +} + + bool flush_error_log() { bool result=0; @@ -5077,11 +5093,7 @@ bool flush_error_log() setbuf(stderr, NULL); (void) my_delete(err_renamed, MYF(0)); my_rename(log_error_file,err_renamed,MYF(0)); - if (freopen(log_error_file,"a+",stdout)) - { - freopen(log_error_file,"a+",stderr); - setbuf(stderr, NULL); - } + redirect_std_streams(log_error_file); if ((fd = my_open(err_temp, O_RDONLY, MYF(0))) >= 0) { @@ -5096,13 +5108,7 @@ bool flush_error_log() result= 1; #else my_rename(log_error_file,err_renamed,MYF(0)); - if (freopen(log_error_file,"a+",stdout)) - { - FILE *reopen; - reopen= freopen(log_error_file,"a+",stderr); - setbuf(stderr, NULL); - } - else + if (redirect_std_streams(log_error_file)) result= 1; #endif VOID(pthread_mutex_unlock(&LOCK_error_log)); @@ -5153,25 +5159,9 @@ static void print_buffer_to_nt_eventlog(enum loglevel level, char *buff, #endif /* __NT__ */ -/** - Prints a printf style message to the error log and, under NT, to the - Windows event log. - - This function prints the message into a buffer and then sends that buffer - to other functions to write that message to other logging sources. - - @param event_type Type of event to write (Error, Warning, or Info) - @param format Printf style format of message - @param args va_list list of arguments for the message - - @returns - The function always returns 0. The return value is present in the - signature to be compatible with other logging routines, which could - return an error (e.g. logging to the log tables) -*/ - #ifndef EMBEDDED_LIBRARY -static void print_buffer_to_file(enum loglevel level, const char *buffer) +static void print_buffer_to_file(enum loglevel level, const char *buffer, + size_t length) { time_t skr; struct tm tm_tmp; @@ -5185,7 +5175,7 @@ static void print_buffer_to_file(enum loglevel level, const char *buffer) localtime_r(&skr, &tm_tmp); start=&tm_tmp; - fprintf(stderr, "%02d%02d%02d %2d:%02d:%02d [%s] %s\n", + fprintf(stderr, "%02d%02d%02d %2d:%02d:%02d [%s] %.*s\n", start->tm_year % 100, start->tm_mon+1, start->tm_mday, @@ -5194,7 +5184,7 @@ static void print_buffer_to_file(enum loglevel level, const char *buffer) start->tm_sec, (level == ERROR_LEVEL ? "ERROR" : level == WARNING_LEVEL ? "Warning" : "Note"), - buffer); + (int) length, buffer); fflush(stderr); @@ -5202,7 +5192,22 @@ static void print_buffer_to_file(enum loglevel level, const char *buffer) DBUG_VOID_RETURN; } +/** + Prints a printf style message to the error log and, under NT, to the + Windows event log. + This function prints the message into a buffer and then sends that buffer + to other functions to write that message to other logging sources. + + @param level The level of the msg significance + @param format Printf style format of message + @param args va_list list of arguments for the message + + @returns + The function always returns 0. The return value is present in the + signature to be compatible with other logging routines, which could + return an error (e.g. logging to the log tables) +*/ int vprint_msg_to_log(enum loglevel level, const char *format, va_list args) { char buff[1024]; @@ -5210,7 +5215,7 @@ int vprint_msg_to_log(enum loglevel level, const char *format, va_list args) DBUG_ENTER("vprint_msg_to_log"); length= my_vsnprintf(buff, sizeof(buff), format, args); - print_buffer_to_file(level, buff); + print_buffer_to_file(level, buff, length); #ifdef __NT__ print_buffer_to_nt_eventlog(level, buff, length, sizeof(buff)); @@ -5218,7 +5223,7 @@ int vprint_msg_to_log(enum loglevel level, const char *format, va_list args) DBUG_RETURN(0); } -#endif /*EMBEDDED_LIBRARY*/ +#endif /* EMBEDDED_LIBRARY */ void sql_print_error(const char *format, ...) diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 99f16b36dfa..fda64e5a1ea 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -3783,7 +3783,6 @@ static void end_ssl() static int init_server_components() { - FILE* reopen; DBUG_ENTER("init_server_components"); /* We need to call each of these following functions to ensure that @@ -3826,8 +3825,8 @@ static int init_server_components() if (freopen(log_error_file, "a+", stdout)) #endif { - reopen= freopen(log_error_file, "a+", stderr); - setbuf(stderr, NULL); + if (freopen(log_error_file, "a+", stderr)) + setbuf(stderr, NULL); } } } @@ -8222,7 +8221,7 @@ mysqld_get_one_option(int optid, *val= 0; val+= 2; while (*val && my_isspace(mysqld_charset, *val)) - *val++; + val++; if (!*val) { sql_print_error("Bad syntax in replicate-rewrite-db - empty TO db!\n"); diff --git a/sql/partition_info.cc b/sql/partition_info.cc index d85888e295c..f37151ea51d 100644 --- a/sql/partition_info.cc +++ b/sql/partition_info.cc @@ -1206,13 +1206,11 @@ bool partition_info::set_up_charset_field_preps() i= 0; while ((field= *(ptr++))) { - CHARSET_INFO *cs; uchar *field_buf; LINT_INIT(field_buf); if (!field_is_partition_charset(field)) continue; - cs= ((Field_str*)field)->charset(); size= field->pack_length(); if (!(field_buf= (uchar*) sql_calloc(size))) goto error; diff --git a/sql/slave.cc b/sql/slave.cc index 795bc481071..f1e0962e7e8 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -3035,11 +3035,8 @@ log '%s' at position %s, relay log '%s' position: %s", RPL_LOG_NAME, pthread_mutex_lock(&rli->data_lock); if (rli->slave_skip_counter) { - char *pos; - pos= strmake(saved_log_name, rli->group_relay_log_name, FN_REFLEN - 1); - pos= '\0'; - pos= strmake(saved_master_log_name, rli->group_master_log_name, FN_REFLEN - 1); - pos= '\0'; + strmake(saved_log_name, rli->group_relay_log_name, FN_REFLEN - 1); + strmake(saved_master_log_name, rli->group_master_log_name, FN_REFLEN - 1); saved_log_pos= rli->group_relay_log_pos; saved_master_log_pos= rli->group_master_log_pos; saved_skip= rli->slave_skip_counter; diff --git a/sql/spatial.cc b/sql/spatial.cc index 11df6c00dc5..2305a8eb97d 100644 --- a/sql/spatial.cc +++ b/sql/spatial.cc @@ -159,11 +159,10 @@ Geometry *Geometry::construct(Geometry_buffer *buffer, { uint32 geom_type; Geometry *result; - char byte_order; if (data_len < SRID_SIZE + WKB_HEADER_SIZE) // < 4 + (1 + 4) return NULL; - byte_order= data[SRID_SIZE]; + /* + 1 to skip the byte order (stored in position SRID_SIZE). */ geom_type= uint4korr(data + SRID_SIZE + 1); if (!(result= create_by_typeid(buffer, (int) geom_type))) return NULL; diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 90eef872115..b507b70d1fb 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -5701,7 +5701,6 @@ bool mysql_create_user(THD *thd, List &list) { int result; String wrong_users; - ulong sql_mode; LEX_USER *user_name, *tmp_user_name; List_iterator user_list(list); TABLE_LIST tables[GRANT_TABLES]; @@ -5748,7 +5747,6 @@ bool mysql_create_user(THD *thd, List &list) } some_users_created= TRUE; - sql_mode= thd->variables.sql_mode; if (replace_user_table(thd, tables[0].table, *user_name, 0, 0, 1, 0)) { append_user(&wrong_users, user_name); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index d2392bdd9b1..c38526a6d0b 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -5674,7 +5674,7 @@ static void update_field_dependencies(THD *thd, Field *field, TABLE *table) DBUG_ENTER("update_field_dependencies"); if (thd->mark_used_columns != MARK_COLUMNS_NONE) { - MY_BITMAP *current_bitmap, *other_bitmap; + MY_BITMAP *bitmap; /* We always want to register the used keys, as the column bitmap may have @@ -5685,15 +5685,9 @@ static void update_field_dependencies(THD *thd, Field *field, TABLE *table) table->merge_keys.merge(field->part_of_key); if (thd->mark_used_columns == MARK_COLUMNS_READ) - { - current_bitmap= table->read_set; - other_bitmap= table->write_set; - } + bitmap= table->read_set; else - { - current_bitmap= table->write_set; - other_bitmap= table->read_set; - } + bitmap= table->write_set; /* The test-and-set mechanism in the bitmap is not reliable during @@ -5702,7 +5696,7 @@ static void update_field_dependencies(THD *thd, Field *field, TABLE *table) only those columns that are used in the SET clause. I.e they are being set here. See multi_update::prepare() */ - if (bitmap_fast_test_and_set(current_bitmap, field->field_index)) + if (bitmap_fast_test_and_set(bitmap, field->field_index)) { if (thd->mark_used_columns == MARK_COLUMNS_WRITE) { diff --git a/sql/sql_load.cc b/sql/sql_load.cc index ee7481234a4..a4cf46b35e8 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -128,6 +128,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, bool is_fifo=0; #ifndef EMBEDDED_LIBRARY LOAD_FILE_INFO lf_info; + THD::killed_state killed_status; #endif char *db = table_list->db; // This is never null /* @@ -138,7 +139,6 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, char *tdb= thd->db ? thd->db : db; // Result is never null ulong skip_lines= ex->skip_lines; bool transactional_table; - THD::killed_state killed_status= THD::NOT_KILLED; DBUG_ENTER("mysql_load"); #ifdef EMBEDDED_LIBRARY @@ -455,7 +455,11 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, error=1; thd->killed= THD::KILL_QUERY; };); - killed_status= (error == 0)? THD::NOT_KILLED : thd->killed; + +#ifndef EMBEDDED_LIBRARY + killed_status= (error == 0) ? THD::NOT_KILLED : thd->killed; +#endif + /* We must invalidate the table in query cache before binlog writing and ha_autocommit_... @@ -708,12 +712,9 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, List_iterator_fast it(fields_vars); Item_field *sql_field; TABLE *table= table_list->table; - ulonglong id; bool err; DBUG_ENTER("read_fixed_length"); - id= 0; - while (!read_info.read_fixed_length()) { if (thd->killed) @@ -839,12 +840,10 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, Item *item; TABLE *table= table_list->table; uint enclosed_length; - ulonglong id; bool err; DBUG_ENTER("read_sep_field"); enclosed_length=enclosed.length(); - id= 0; for (;;it.rewind()) { diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 1f3d29ffec0..134517e5bf7 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -465,6 +465,12 @@ static void handle_bootstrap_impl(THD *thd) } buff= (char*) thd->net.buff; res= fgets(buff + length, thd->net.max_packet - length, file); + if (!res && !feof(file)) + { + net_end_statement(thd); + bootstrap_error= 1; + break; + } length+= (ulong) strlen(buff + length); /* purecov: end */ } @@ -1535,7 +1541,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, { STATUS_VAR current_global_status_var; ulong uptime; - uint length; + uint length __attribute__((unused)); ulonglong queries_per_second1000; char buff[250]; uint buff_len= sizeof(buff); @@ -1548,7 +1554,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, else queries_per_second1000= thd->query_id * LL(1000) / uptime; - length= my_snprintf((char*) buff, buff_len - 1, + length= my_snprintf(buff, buff_len - 1, "Uptime: %lu Threads: %d Questions: %lu " "Slow queries: %lu Opens: %lu Flush tables: %lu " "Open tables: %u Queries per second avg: %u.%u", @@ -1560,10 +1566,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd, cached_open_tables(), (uint) (queries_per_second1000 / 1000), (uint) (queries_per_second1000 % 1000)); -#ifdef EMBEDDED_LIBRARY - /* Store the buffer in permanent memory */ - my_ok(thd, 0, 0, buff); -#endif #ifdef SAFEMALLOC if (sf_malloc_cur_memory) // Using SAFEMALLOC { @@ -1578,6 +1580,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd, VOID(my_net_write(net, (uchar*) buff, length)); VOID(net_flush(net)); thd->main_da.disable_status(); +#else + /* Store the buffer in permanent memory */ + my_ok(thd, 0, 0, buff); #endif break; } diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index f6045e4704e..d7dd3eb63f2 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -1168,12 +1168,9 @@ bool change_master(THD* thd, Master_info* mi) /* Before processing the command, save the previous state. */ - char *pos; - pos= strmake(saved_host, mi->host, HOSTNAME_LENGTH); - pos= '\0'; + strmake(saved_host, mi->host, HOSTNAME_LENGTH); saved_port= mi->port; - pos= strmake(saved_log_name, mi->master_log_name, FN_REFLEN - 1); - pos= '\0'; + strmake(saved_log_name, mi->master_log_name, FN_REFLEN - 1); saved_log_pos= mi->master_log_pos; /* diff --git a/sql/sql_show.cc b/sql/sql_show.cc index d0e76e501e2..eb5d3a1965d 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -3868,7 +3868,6 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables, uint flags=field->flags; char tmp[MAX_FIELD_WIDTH]; String type(tmp,sizeof(tmp), system_charset_info); - char *end; int decimals, field_length; if (wild && wild[0] && @@ -3889,7 +3888,7 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables, field->field_name) & COL_ACLS; if (!tables->schema_table && !col_access) continue; - end= tmp; + char *end= tmp; for (uint bitnr=0; col_access ; col_access>>=1,bitnr++) { if (col_access & 1) @@ -4015,7 +4014,6 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables, table->field[15]->store((const char*) pos, strlen((const char*) pos), cs); - end= tmp; if (field->unireg_check == Field::NEXT_NUMBER) table->field[16]->store(STRING_WITH_LEN("auto_increment"), cs); if (show_table->timestamp_field == field && diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 47b91fcca0e..f765e5c5cae 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -6507,7 +6507,6 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, uint index_add_count= 0; uint *index_add_buffer= NULL; uint candidate_key_count= 0; - bool committed= 0; bool no_pk; DBUG_ENTER("mysql_alter_table"); @@ -7380,7 +7379,6 @@ view_err: DBUG_PRINT("info", ("Committing before unlocking table")); if (ha_autocommit_or_rollback(thd, 0) || end_active_trans(thd)) goto err1; - committed= 1; } /*end of if (! new_table) for add/drop index*/ diff --git a/sql/table.cc b/sql/table.cc index dde3654dab1..e989ab039a0 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -534,7 +534,7 @@ int open_table_def(THD *thd, TABLE_SHARE *share, uint db_flags) int error, table_type; bool error_given; File file; - uchar head[288], *disk_buff; + uchar head[288]; char path[FN_REFLEN]; MEM_ROOT **root_ptr, *old_root; DBUG_ENTER("open_table_def"); @@ -543,7 +543,6 @@ int open_table_def(THD *thd, TABLE_SHARE *share, uint db_flags) error= 1; error_given= 0; - disk_buff= NULL; strxmov(path, share->normalized_path.str, reg_ext, NullS); if ((file= my_open(path, O_RDONLY | O_SHARE, MYF(0))) < 0) diff --git a/sql/udf_example.c b/sql/udf_example.c index 637293209e0..468118b44ef 100644 --- a/sql/udf_example.c +++ b/sql/udf_example.c @@ -1067,7 +1067,7 @@ char *myfunc_argument_name(UDF_INIT *initid __attribute__((unused)), { if (!args->attributes[0]) { - null_value= 0; + *null_value= 1; return 0; } (*length)--; /* space for ending \0 (for debugging purposes) */ diff --git a/storage/csv/ha_tina.cc b/storage/csv/ha_tina.cc index e3bc7f55dd2..5a3399b2401 100644 --- a/storage/csv/ha_tina.cc +++ b/storage/csv/ha_tina.cc @@ -468,7 +468,7 @@ int ha_tina::encode_quote(uchar *buf) const char *ptr; const char *end_ptr; const bool was_null= (*field)->is_null(); - + /* assistance for backwards compatibility in production builds. note: this will not work for ENUM columns. @@ -480,7 +480,7 @@ int ha_tina::encode_quote(uchar *buf) } (*field)->val_str(&attribute,&attribute); - + if (was_null) (*field)->set_null(); @@ -491,34 +491,30 @@ int ha_tina::encode_quote(uchar *buf) buffer.append('"'); - while (ptr < end_ptr) + for (; ptr < end_ptr; ptr++) { if (*ptr == '"') { buffer.append('\\'); buffer.append('"'); - *ptr++; } else if (*ptr == '\r') { buffer.append('\\'); buffer.append('r'); - *ptr++; } else if (*ptr == '\\') { buffer.append('\\'); buffer.append('\\'); - *ptr++; } else if (*ptr == '\n') { buffer.append('\\'); buffer.append('n'); - *ptr++; } else - buffer.append(*ptr++); + buffer.append(*ptr); } buffer.append('"'); } diff --git a/storage/example/ha_example.cc b/storage/example/ha_example.cc index 604722c3c8c..2a4fe538c85 100644 --- a/storage/example/ha_example.cc +++ b/storage/example/ha_example.cc @@ -153,7 +153,7 @@ static int example_done_func(void *p) hash_free(&example_open_tables); pthread_mutex_destroy(&example_mutex); - DBUG_RETURN(0); + DBUG_RETURN(error); } diff --git a/storage/myisam/mi_locking.c b/storage/myisam/mi_locking.c index 342efff4842..589b9cf89b7 100644 --- a/storage/myisam/mi_locking.c +++ b/storage/myisam/mi_locking.c @@ -29,7 +29,6 @@ int mi_lock_database(MI_INFO *info, int lock_type) int error; uint count; MYISAM_SHARE *share=info->s; - uint flag; DBUG_ENTER("mi_lock_database"); DBUG_PRINT("enter",("lock_type: %d old lock %d r_locks: %u w_locks: %u " "global_changed: %d open_count: %u name: '%s'", @@ -48,7 +47,7 @@ int mi_lock_database(MI_INFO *info, int lock_type) DBUG_RETURN(0); } - flag=error=0; + error= 0; pthread_mutex_lock(&share->intern_lock); if (share->kfile >= 0) /* May only be false on windows */ { @@ -120,14 +119,12 @@ int mi_lock_database(MI_INFO *info, int lock_type) { if (share->r_locks) { /* Only read locks left */ - flag=1; if (my_lock(share->kfile,F_RDLCK,0L,F_TO_EOF, MYF(MY_WME | MY_SEEK_NOT_DONE)) && !error) error=my_errno; } else if (!share->w_locks) { /* No more locks */ - flag=1; if (my_lock(share->kfile,F_UNLCK,0L,F_TO_EOF, MYF(MY_WME | MY_SEEK_NOT_DONE)) && !error) error=my_errno; @@ -148,7 +145,6 @@ int mi_lock_database(MI_INFO *info, int lock_type) */ if (share->w_locks == 1) { - flag=1; if (my_lock(share->kfile,lock_type,0L,F_TO_EOF, MYF(MY_SEEK_NOT_DONE))) { @@ -163,7 +159,6 @@ int mi_lock_database(MI_INFO *info, int lock_type) } if (!share->r_locks && !share->w_locks) { - flag=1; if (my_lock(share->kfile,lock_type,0L,F_TO_EOF, info->lock_wait | MY_SEEK_NOT_DONE)) { @@ -188,7 +183,6 @@ int mi_lock_database(MI_INFO *info, int lock_type) { /* Change READONLY to RW */ if (share->r_locks == 1) { - flag=1; if (my_lock(share->kfile,lock_type,0L,F_TO_EOF, MYF(info->lock_wait | MY_SEEK_NOT_DONE))) { @@ -205,7 +199,6 @@ int mi_lock_database(MI_INFO *info, int lock_type) { if (!share->w_locks) { - flag=1; if (my_lock(share->kfile,lock_type,0L,F_TO_EOF, info->lock_wait | MY_SEEK_NOT_DONE)) { @@ -252,11 +245,6 @@ int mi_lock_database(MI_INFO *info, int lock_type) } #endif pthread_mutex_unlock(&share->intern_lock); -#if defined(FULL_LOG) || defined(_lint) - lock_type|=(int) (flag << 8); /* Set bit to set if real lock */ - myisam_log_command(MI_LOG_LOCK,info,(uchar*) &lock_type,sizeof(lock_type), - error); -#endif DBUG_RETURN(error); } /* mi_lock_database */ diff --git a/storage/myisam/rt_split.c b/storage/myisam/rt_split.c index 88cf643faf9..03d22de68fa 100644 --- a/storage/myisam/rt_split.c +++ b/storage/myisam/rt_split.c @@ -255,7 +255,6 @@ int rtree_split_page(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *page, uchar *key, SplitStruct *stop; double *coord_buf; double *next_coord; - double *old_coord; int n_dim; uchar *source_cur, *cur1, *cur2; uchar *new_page= info->buff; @@ -293,8 +292,6 @@ int rtree_split_page(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *page, uchar *key, rtree_d_mbr(keyinfo->seg, key, key_length, cur->coords); cur->key = key; - old_coord = next_coord; - if (split_rtree_node(task, max_keys + 1, mi_getint(page) + full_length + 2, full_length, rt_PAGE_MIN_SIZE(keyinfo->block_length), diff --git a/storage/myisammrg/myrg_open.c b/storage/myisammrg/myrg_open.c index 5fcbe0c3297..18f8aa8d2c0 100644 --- a/storage/myisammrg/myrg_open.c +++ b/storage/myisammrg/myrg_open.c @@ -227,9 +227,7 @@ MYRG_INFO *myrg_parent_open(const char *parent_name, int save_errno; int insert_method; uint length; - uint dir_length; uint child_count; - size_t name_buff_length; File fd; IO_CACHE file_cache; char parent_name_buff[FN_REFLEN * 2]; @@ -299,7 +297,6 @@ MYRG_INFO *myrg_parent_open(const char *parent_name, } /* Call callback for each child. */ - dir_length= dirname_part(parent_name_buff, parent_name, &name_buff_length); my_b_seek(&file_cache, 0); while ((length= my_b_gets(&file_cache, child_name_buff, FN_REFLEN - 1))) { @@ -379,7 +376,6 @@ int myrg_attach_children(MYRG_INFO *m_info, int handle_locking, { ulonglong file_offset; MI_INFO *myisam; - int rc; int errpos; int save_errno; uint idx; @@ -398,7 +394,6 @@ int myrg_attach_children(MYRG_INFO *m_info, int handle_locking, here and in ha_myisammrg::store_lock() forces consistent data. */ pthread_mutex_lock(&m_info->mutex); - rc= 1; errpos= 0; file_offset= 0; min_keys= 0; diff --git a/strings/decimal.c b/strings/decimal.c index 4403fc9fd6b..bda296ce832 100644 --- a/strings/decimal.c +++ b/strings/decimal.c @@ -1934,8 +1934,7 @@ static int do_sub(decimal_t *from1, decimal_t *from2, decimal_t *to) int decimal_intg(decimal_t *from) { int res; - dec1 *tmp_res; - tmp_res= remove_leading_zeroes(from, &res); + remove_leading_zeroes(from, &res); return res; } From 5b19c1593f2fce7c6e912b176f3b8ff34b61c280 Mon Sep 17 00:00:00 2001 From: Davi Arnaut Date: Tue, 20 Jul 2010 16:34:20 -0300 Subject: [PATCH 080/115] Bug#52514: mysql 5.1 do_abi_check does not compile w/ gcc4.5 due to GCC preprocessor change Addendum for trunk: do not include system header when checking the ABI. --- include/mysql/plugin.h.pp | 3 --- include/mysql/service_my_snprintf.h | 3 +++ include/mysql/service_thd_alloc.h | 2 ++ 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/include/mysql/plugin.h.pp b/include/mysql/plugin.h.pp index 3a1b03742da..ce9902ee418 100644 --- a/include/mysql/plugin.h.pp +++ b/include/mysql/plugin.h.pp @@ -1,7 +1,5 @@ #include #include -#include -#include extern struct my_snprintf_service_st { size_t (*my_snprintf_type)(char*, size_t, const char*, ...); size_t (*my_vsnprintf_type)(char *, size_t, const char*, va_list); @@ -9,7 +7,6 @@ extern struct my_snprintf_service_st { size_t my_snprintf(char* to, size_t n, const char* fmt, ...); size_t my_vsnprintf(char *to, size_t n, const char* fmt, va_list ap); #include -#include struct st_mysql_lex_string { char *str; diff --git a/include/mysql/service_my_snprintf.h b/include/mysql/service_my_snprintf.h index 9e5fe7f9c9f..d7f8d07e110 100644 --- a/include/mysql/service_my_snprintf.h +++ b/include/mysql/service_my_snprintf.h @@ -70,8 +70,11 @@ extern "C" { #endif +#ifndef MYSQL_ABI_CHECK #include #include +#endif + extern struct my_snprintf_service_st { size_t (*my_snprintf_type)(char*, size_t, const char*, ...); size_t (*my_vsnprintf_type)(char *, size_t, const char*, va_list); diff --git a/include/mysql/service_thd_alloc.h b/include/mysql/service_thd_alloc.h index 86158ba1359..7061c2bd4d5 100644 --- a/include/mysql/service_thd_alloc.h +++ b/include/mysql/service_thd_alloc.h @@ -27,7 +27,9 @@ allocations - they are better served with my_malloc. */ +#ifndef MYSQL_ABI_CHECK #include +#endif #ifdef __cplusplus extern "C" { From 3ff34a4a325b675b12d3ef12c0fa907c5ad670d1 Mon Sep 17 00:00:00 2001 From: Davi Arnaut Date: Tue, 20 Jul 2010 16:53:39 -0300 Subject: [PATCH 081/115] Bug#52514: mysql 5.1 do_abi_check does not compile w/ gcc4.5 due to GCC preprocessor change Addendum for trunk: add -DMYSQL_ABI_CHECK to the cmake ABI check. --- Makefile.am | 2 +- cmake/abi_check.cmake | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile.am b/Makefile.am index 9a9a53ebd16..a736a61fb59 100644 --- a/Makefile.am +++ b/Makefile.am @@ -63,7 +63,7 @@ dist-hook: test ! -f $(top_srcdir)/configure.am || \ $(INSTALL_DATA) $(top_srcdir)/configure.am $(distdir) -all-local: @ABI_CHECK@ +all-local: @ABI_CHECK@ tags: support-files/build-tags diff --git a/cmake/abi_check.cmake b/cmake/abi_check.cmake index 0d5bf21f540..7911b7848a8 100644 --- a/cmake/abi_check.cmake +++ b/cmake/abi_check.cmake @@ -38,7 +38,7 @@ IF(CMAKE_COMPILER_IS_GNUCC AND CMAKE_SYSTEM_NAME MATCHES "Linux") -DCOMPILER=${COMPILER} -DSOURCE_DIR=${CMAKE_SOURCE_DIR} -DBINARY_DIR=${CMAKE_BINARY_DIR} - "-DABI_HEADERS=${API_PREPROCESSOR_HEADER}" + "-DDMYSQL_ABI_CHECK -DABI_HEADERS=${API_PREPROCESSOR_HEADER}" -P ${CMAKE_SOURCE_DIR}/cmake/do_abi_check.cmake VERBATIM ) @@ -48,7 +48,7 @@ IF(CMAKE_COMPILER_IS_GNUCC AND CMAKE_SYSTEM_NAME MATCHES "Linux") -DCMAKE_C_COMPILER=${COMPILER} -DCMAKE_SOURCE_DIR=${CMAKE_SOURCE_DIR} -DCMAKE_BINARY_DIR=${CMAKE_BINARY_DIR} - "-DABI_HEADERS=${API_PREPROCESSOR_HEADER}" + "-DMYSQL_ABI_CHECK -DABI_HEADERS=${API_PREPROCESSOR_HEADER}" -P ${CMAKE_SOURCE_DIR}/cmake/scripts/do_abi_check.cmake VERBATIM ) From abacbed6f4aaf3009f1d8057de6022ca0bda018a Mon Sep 17 00:00:00 2001 From: Calvin Sun Date: Tue, 20 Jul 2010 15:42:31 -0500 Subject: [PATCH 082/115] Improve InnoDB synchronization primitives on Windows This patch was originally developed by Vladislav Vaintroub. The main changes are: * Use TryEnterCriticalSection in os_fast_mutex_trylock(). * Use lightweight condition variables on Vista or later Windows; but fall back to events on older Windows, such as XP. This patch also fixes the following bugs: bug# 52102 InnoDB Plugin shows performance drop compared to InnoDB on Windows bug# 53204 os_fastmutex_trylock is implemented incorrectly on Windows rb://363 approved by Inaam Rana --- storage/innobase/CMakeLists.txt | 6 +- storage/innobase/include/os0file.h | 10 +- storage/innobase/include/os0sync.h | 73 +---- storage/innobase/include/os0sync.ic | 11 +- storage/innobase/include/srv0srv.h | 3 + storage/innobase/os/os0file.c | 62 ++-- storage/innobase/os/os0sync.c | 444 ++++++++++++++++------------ storage/innobase/os/os0thread.c | 2 +- storage/innobase/srv/srv0srv.c | 15 + storage/innobase/srv/srv0start.c | 12 +- storage/innobase/sync/sync0arr.c | 2 +- 11 files changed, 349 insertions(+), 291 deletions(-) diff --git a/storage/innobase/CMakeLists.txt b/storage/innobase/CMakeLists.txt index 8e3e4efbb0e..fbb59b07dfe 100644 --- a/storage/innobase/CMakeLists.txt +++ b/storage/innobase/CMakeLists.txt @@ -188,11 +188,7 @@ IF(SIZEOF_PTHREAD_T) ENDIF() IF(MSVC) - # Windows atomics do not perform well. Disable Windows atomics by default. - # See bug#52102 for details. - - #ADD_DEFINITIONS(-DHAVE_WINDOWS_ATOMICS -DINNODB_RW_LOCKS_USE_ATOMICS -DHAVE_IB_PAUSE_INSTRUCTION) - ADD_DEFINITIONS(-DHAVE_IB_PAUSE_INSTRUCTION) + ADD_DEFINITIONS(-DHAVE_WINDOWS_ATOMICS -DHAVE_IB_PAUSE_INSTRUCTION) ENDIF() diff --git a/storage/innobase/include/os0file.h b/storage/innobase/include/os0file.h index a112cb06697..7a9b4cffa2b 100644 --- a/storage/innobase/include/os0file.h +++ b/storage/innobase/include/os0file.h @@ -177,6 +177,13 @@ log. */ #define OS_WIN95 2 /*!< Microsoft Windows 95 */ #define OS_WINNT 3 /*!< Microsoft Windows NT 3.x */ #define OS_WIN2000 4 /*!< Microsoft Windows 2000 */ +#define OS_WINXP 5 /*!< Microsoft Windows XP + or Windows Server 2003 */ +#define OS_WINVISTA 6 /*!< Microsoft Windows Vista + or Windows Server 2008 */ +#define OS_WIN7 7 /*!< Microsoft Windows 7 + or Windows Server 2008 R2 */ + extern ulint os_n_file_reads; extern ulint os_n_file_writes; @@ -368,7 +375,8 @@ typedef DIR* os_file_dir_t; /*!< directory stream */ /***********************************************************************//** Gets the operating system version. Currently works only on Windows. -@return OS_WIN95, OS_WIN31, OS_WINNT, or OS_WIN2000 */ +@return OS_WIN95, OS_WIN31, OS_WINNT, OS_WIN2000, OS_WINXP, OS_WINVISTA, +OS_WIN7. */ UNIV_INTERN ulint os_get_os_version(void); diff --git a/storage/innobase/include/os0sync.h b/storage/innobase/include/os0sync.h index 0c22162b900..0b600c80ce3 100644 --- a/storage/innobase/include/os0sync.h +++ b/storage/innobase/include/os0sync.h @@ -38,28 +38,18 @@ Created 9/6/1995 Heikki Tuuri #include "ut0lst.h" #ifdef __WIN__ - +/** Native event (slow)*/ +typedef HANDLE os_native_event_t; /** Native mutex */ -#define os_fast_mutex_t CRITICAL_SECTION - -/** Native event */ -typedef HANDLE os_native_event_t; - -/** Operating system event */ -typedef struct os_event_struct os_event_struct_t; -/** Operating system event handle */ -typedef os_event_struct_t* os_event_t; - -/** An asynchronous signal sent between threads */ -struct os_event_struct { - os_native_event_t handle; - /*!< Windows event */ - UT_LIST_NODE_T(os_event_struct_t) os_event_list; - /*!< list of all created events */ -}; +typedef CRITICAL_SECTION os_fast_mutex_t; +/** Native condition variable. */ +typedef CONDITION_VARIABLE os_cond_t; #else /** Native mutex */ -typedef pthread_mutex_t os_fast_mutex_t; +typedef pthread_mutex_t os_fast_mutex_t; +/** Native condition variable */ +typedef pthread_cond_t os_cond_t; +#endif /** Operating system event */ typedef struct os_event_struct os_event_struct_t; @@ -68,6 +58,10 @@ typedef os_event_struct_t* os_event_t; /** An asynchronous signal sent between threads */ struct os_event_struct { +#ifdef __WIN__ + HANDLE handle; /*!< kernel event object, slow, + used on older Windows */ +#endif os_fast_mutex_t os_mutex; /*!< this mutex protects the next fields */ ibool is_set; /*!< this is TRUE when the event is @@ -76,24 +70,17 @@ struct os_event_struct { this event */ ib_int64_t signal_count; /*!< this is incremented each time the event becomes signaled */ - pthread_cond_t cond_var; /*!< condition variable is used in + os_cond_t cond_var; /*!< condition variable is used in waiting for the event */ UT_LIST_NODE_T(os_event_struct_t) os_event_list; /*!< list of all created events */ }; -#endif /** Operating system mutex */ typedef struct os_mutex_struct os_mutex_str_t; /** Operating system mutex handle */ typedef os_mutex_str_t* os_mutex_t; -/** Denotes an infinite delay for os_event_wait_time() */ -#define OS_SYNC_INFINITE_TIME ((ulint)(-1)) - -/** Return value of os_event_wait_time() when the time is exceeded */ -#define OS_SYNC_TIME_EXCEEDED 1 - /** Mutex protecting counts and the event and OS 'slow' mutex lists */ extern os_mutex_t os_sync_mutex; @@ -187,42 +174,14 @@ os_event_wait_low( #define os_event_wait(event) os_event_wait_low(event, 0) -/**********************************************************//** -Waits for an event object until it is in the signaled state or -a timeout is exceeded. In Unix the timeout is always infinite. -@return 0 if success, OS_SYNC_TIME_EXCEEDED if timeout was exceeded */ -UNIV_INTERN -ulint -os_event_wait_time( -/*===============*/ - os_event_t event, /*!< in: event to wait */ - ulint time); /*!< in: timeout in microseconds, or - OS_SYNC_INFINITE_TIME */ -#ifdef __WIN__ -/**********************************************************//** -Waits for any event in an OS native event array. Returns if even a single -one is signaled or becomes signaled. -@return index of the event which was signaled */ -UNIV_INTERN -ulint -os_event_wait_multiple( -/*===================*/ - ulint n, /*!< in: number of events in the - array */ - os_native_event_t* native_event_array); - /*!< in: pointer to an array of event - handles */ -#endif /*********************************************************//** Creates an operating system mutex semaphore. Because these are slow, the mutex semaphore of InnoDB itself (mutex_t) should be used where possible. @return the mutex handle */ UNIV_INTERN os_mutex_t -os_mutex_create( -/*============*/ - const char* name); /*!< in: the name of the mutex, if NULL - the mutex is created without a name */ +os_mutex_create(void); +/*=================*/ /**********************************************************//** Acquires ownership of a mutex semaphore. */ UNIV_INTERN diff --git a/storage/innobase/include/os0sync.ic b/storage/innobase/include/os0sync.ic index 1f3ce38fa65..c33f13aaad6 100644 --- a/storage/innobase/include/os0sync.ic +++ b/storage/innobase/include/os0sync.ic @@ -28,8 +28,7 @@ Created 9/6/1995 Heikki Tuuri #endif /**********************************************************//** -Acquires ownership of a fast mutex. Currently in Windows this is the same -as os_fast_mutex_lock! +Acquires ownership of a fast mutex. @return 0 if success, != 0 if was reserved by another thread */ UNIV_INLINE ulint @@ -38,9 +37,13 @@ os_fast_mutex_trylock( os_fast_mutex_t* fast_mutex) /*!< in: mutex to acquire */ { #ifdef __WIN__ - EnterCriticalSection(fast_mutex); + if (TryEnterCriticalSection(fast_mutex)) { - return(0); + return(0); + } else { + + return(1); + } #else /* NOTE that the MySQL my_pthread.h redefines pthread_mutex_trylock so that it returns 0 on success. In the operating system diff --git a/storage/innobase/include/srv0srv.h b/storage/innobase/include/srv0srv.h index 5fbb59b14ff..d78c8113aee 100644 --- a/storage/innobase/include/srv0srv.h +++ b/storage/innobase/include/srv0srv.h @@ -112,6 +112,9 @@ OS (provided we compiled Innobase with it in), otherwise we will use simulated aio we build below with threads. Currently we support native aio on windows and linux */ extern my_bool srv_use_native_aio; +#ifdef __WIN__ +extern ibool srv_use_native_conditions; +#endif extern ulint srv_n_data_files; extern char** srv_data_file_names; extern ulint* srv_data_file_sizes; diff --git a/storage/innobase/os/os0file.c b/storage/innobase/os/os0file.c index 0045b8731e4..6b17dccd2bf 100644 --- a/storage/innobase/os/os0file.c +++ b/storage/innobase/os/os0file.c @@ -183,7 +183,7 @@ struct os_aio_slot_struct{ which pending aio operation was completed */ #ifdef WIN_ASYNC_IO - os_event_t event; /*!< event object we need in the + HANDLE handle; /*!< handle object we need in the OVERLAPPED struct */ OVERLAPPED control; /*!< Windows control block for the aio request */ @@ -225,7 +225,7 @@ struct os_aio_array_struct{ aio array outside the ibuf segment */ os_aio_slot_t* slots; /*!< Pointer to the slots in the array */ #ifdef __WIN__ - os_native_event_t* native_events; + HANDLE* handles; /*!< Pointer to an array of OS native event handles where we copied the handles from slots, in the same @@ -304,7 +304,8 @@ UNIV_INTERN ulint os_n_pending_reads = 0; /***********************************************************************//** Gets the operating system version. Currently works only on Windows. -@return OS_WIN95, OS_WIN31, OS_WINNT, OS_WIN2000 */ +@return OS_WIN95, OS_WIN31, OS_WINNT, OS_WIN2000, OS_WINXP, OS_WINVISTA, +OS_WIN7. */ UNIV_INTERN ulint os_get_os_version(void) @@ -322,10 +323,18 @@ os_get_os_version(void) } else if (os_info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) { return(OS_WIN95); } else if (os_info.dwPlatformId == VER_PLATFORM_WIN32_NT) { - if (os_info.dwMajorVersion <= 4) { - return(OS_WINNT); - } else { - return(OS_WIN2000); + switch (os_info.dwMajorVersion) { + case 3: + case 4: + return OS_WINNT; + case 5: + return (os_info.dwMinorVersion == 0) ? OS_WIN2000 + : OS_WINXP; + case 6: + return (os_info.dwMinorVersion == 0) ? OS_WINVISTA + : OS_WIN7; + default: + return OS_WIN7; } } else { ut_error; @@ -673,10 +682,10 @@ os_io_init_simple(void) { ulint i; - os_file_count_mutex = os_mutex_create(NULL); + os_file_count_mutex = os_mutex_create(); for (i = 0; i < OS_FILE_N_SEEK_MUTEXES; i++) { - os_file_seek_mutexes[i] = os_mutex_create(NULL); + os_file_seek_mutexes[i] = os_mutex_create(); } } @@ -3217,7 +3226,7 @@ os_aio_array_create( array = ut_malloc(sizeof(os_aio_array_t)); - array->mutex = os_mutex_create(NULL); + array->mutex = os_mutex_create(); array->not_full = os_event_create(NULL); array->is_empty = os_event_create(NULL); @@ -3229,7 +3238,7 @@ os_aio_array_create( array->cur_seg = 0; array->slots = ut_malloc(n * sizeof(os_aio_slot_t)); #ifdef __WIN__ - array->native_events = ut_malloc(n * sizeof(os_native_event_t)); + array->handles = ut_malloc(n * sizeof(HANDLE)); #endif #if defined(LINUX_NATIVE_AIO) @@ -3273,13 +3282,13 @@ skip_native_aio: slot->pos = i; slot->reserved = FALSE; #ifdef WIN_ASYNC_IO - slot->event = os_event_create(NULL); + slot->handle = CreateEvent(NULL,TRUE, FALSE, NULL); over = &(slot->control); - over->hEvent = slot->event->handle; + over->hEvent = slot->handle; - *((array->native_events) + i) = over->hEvent; + *((array->handles) + i) = over->hEvent; #elif defined(LINUX_NATIVE_AIO) @@ -3305,12 +3314,12 @@ os_aio_array_free( for (i = 0; i < array->n_slots; i++) { os_aio_slot_t* slot = os_aio_array_get_nth_slot(array, i); - os_event_free(slot->event); + CloseHandle(slot->handle); } #endif /* WIN_ASYNC_IO */ #ifdef __WIN__ - ut_free(array->native_events); + ut_free(array->handles); #endif /* __WIN__ */ os_mutex_free(array->mutex); os_event_free(array->not_full); @@ -3463,7 +3472,7 @@ os_aio_array_wake_win_aio_at_shutdown( for (i = 0; i < array->n_slots; i++) { - os_event_set((array->slots + i)->event); + SetEvent((array->slots + i)->handle); } } #endif @@ -3702,7 +3711,7 @@ found: control = &(slot->control); control->Offset = (DWORD)offset; control->OffsetHigh = (DWORD)offset_high; - os_event_reset(slot->event); + ResetEvent(slot->handle); #elif defined(LINUX_NATIVE_AIO) @@ -3774,7 +3783,7 @@ os_aio_array_free_slot( #ifdef WIN_ASYNC_IO - os_event_reset(slot->event); + ResetEvent(slot->handle); #elif defined(LINUX_NATIVE_AIO) @@ -4208,13 +4217,20 @@ os_aio_windows_handle( n = array->n_slots / array->n_segments; if (array == os_aio_sync_array) { - os_event_wait(os_aio_array_get_nth_slot(array, pos)->event); + WaitForSingleObject( + os_aio_array_get_nth_slot(array, pos)->handle, + INFINITE); i = pos; } else { srv_set_io_thread_op_info(orig_seg, "wait Windows aio"); - i = os_event_wait_multiple(n, - (array->native_events) - + segment * n); + i = WaitForMultipleObjects((DWORD) n, + array->handles + segment * n, + FALSE, + INFINITE); + } + + if (srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS) { + os_thread_exit(NULL); } os_mutex_enter(array->mutex); diff --git a/storage/innobase/os/os0sync.c b/storage/innobase/os/os0sync.c index 60467242e14..3c70e93aae0 100644 --- a/storage/innobase/os/os0sync.c +++ b/storage/innobase/os/os0sync.c @@ -35,6 +35,7 @@ Created 9/6/1995 Heikki Tuuri #include "ut0mem.h" #include "srv0start.h" +#include "srv0srv.h" /* Type definition for an operating system mutex struct */ struct os_mutex_struct{ @@ -76,6 +77,155 @@ event embedded inside a mutex, on free, this generates a recursive call. This version of the free event function doesn't acquire the global lock */ static void os_event_free_internal(os_event_t event); +/* On Windows (Vista and later), load function pointers for condition +variable handling. Those functions are not available in prior versions, +so we have to use them via runtime loading, as long as we support XP. */ +static void os_cond_module_init(void); + +#ifdef __WIN__ +/* Prototypes and function pointers for condition variable functions */ +typedef VOID (WINAPI* InitializeConditionVariableProc) + (PCONDITION_VARIABLE ConditionVariable); +static InitializeConditionVariableProc initialize_condition_variable; + +typedef BOOL (WINAPI* SleepConditionVariableCSProc) + (PCONDITION_VARIABLE ConditionVariable, + PCRITICAL_SECTION CriticalSection, + DWORD dwMilliseconds); +static SleepConditionVariableCSProc sleep_condition_variable; + +typedef VOID (WINAPI* WakeAllConditionVariableProc) + (PCONDITION_VARIABLE ConditionVariable); +static WakeAllConditionVariableProc wake_all_condition_variable; + +typedef VOID (WINAPI* WakeConditionVariableProc) + (PCONDITION_VARIABLE ConditionVariable); +static WakeConditionVariableProc wake_condition_variable; +#endif + +/*********************************************************//** +Initialitze condition variable */ +UNIV_INLINE +void +os_cond_init( +/*=========*/ + os_cond_t* cond) /*!< in: condition variable. */ +{ + ut_a(cond); + +#ifdef __WIN__ + ut_a(initialize_condition_variable != NULL); + initialize_condition_variable(cond); +#else + ut_a(pthread_cond_init(cond, NULL) == 0); +#endif +} + +/*********************************************************//** +Wait on condition variable */ +UNIV_INLINE +void +os_cond_wait( +/*=========*/ + os_cond_t* cond, /*!< in: condition variable. */ + os_fast_mutex_t* mutex) /*!< in: fast mutex */ +{ + ut_a(cond); + ut_a(mutex); + +#ifdef __WIN__ + ut_a(sleep_condition_variable != NULL); + ut_a(sleep_condition_variable(cond, mutex, INFINITE)); +#else + ut_a(pthread_cond_wait(cond, mutex) == 0); +#endif +} + +/*********************************************************//** +Wakes all threads waiting for condition variable */ +UNIV_INLINE +void +os_cond_broadcast( +/*==============*/ + os_cond_t* cond) /*!< in: condition variable. */ +{ + ut_a(cond); + +#ifdef __WIN__ + ut_a(wake_all_condition_variable != NULL); + wake_all_condition_variable(cond); +#else + ut_a(pthread_cond_broadcast(cond) == 0); +#endif +} + +/*********************************************************//** +Wakes one thread waiting for condition variable */ +UNIV_INLINE +void +os_cond_signal( +/*==========*/ + os_cond_t* cond) /*!< in: condition variable. */ +{ + ut_a(cond); + +#ifdef __WIN__ + ut_a(wake_condition_variable != NULL); + wake_condition_variable(cond); +#else + ut_a(pthread_cond_signal(cond) == 0); +#endif +} + +/*********************************************************//** +Destroys condition variable */ +UNIV_INLINE +void +os_cond_destroy( +/*============*/ + os_cond_t* cond) /*!< in: condition variable. */ +{ +#ifdef __WIN__ + /* Do nothing */ +#else + ut_a(pthread_cond_destroy(cond) == 0); +#endif +} + +/*********************************************************//** +On Windows (Vista and later), load function pointers for condition variable +handling. Those functions are not available in prior versions, so we have to +use them via runtime loading, as long as we support XP. */ +static +void +os_cond_module_init(void) +/*=====================*/ +{ +#ifdef __WIN__ + HMODULE h_dll; + + if (!srv_use_native_conditions) + return; + + h_dll = GetModuleHandle("kernel32"); + + initialize_condition_variable = (InitializeConditionVariableProc) + GetProcAddress(h_dll, "InitializeConditionVariable"); + sleep_condition_variable = (SleepConditionVariableCSProc) + GetProcAddress(h_dll, "SleepConditionVariableCS"); + wake_all_condition_variable = (WakeAllConditionVariableProc) + GetProcAddress(h_dll, "WakeAllConditionVariable"); + wake_condition_variable = (WakeConditionVariableProc) + GetProcAddress(h_dll, "WakeConditionVariable"); + + /* When using native condition variables, check function pointers */ + ut_a(initialize_condition_variable); + ut_a(sleep_condition_variable); + ut_a(wake_all_condition_variable); + ut_a(wake_condition_variable); +#endif +} + /*********************************************************//** Initializes global event and OS 'slow' mutex lists. */ UNIV_INTERN @@ -89,7 +239,10 @@ os_sync_init(void) os_sync_mutex = NULL; os_sync_mutex_inited = FALSE; - os_sync_mutex = os_mutex_create(NULL); + /* Now for Windows only */ + os_cond_module_init(); + + os_sync_mutex = os_mutex_create(); os_sync_mutex_inited = TRUE; } @@ -143,42 +296,45 @@ os_event_create( const char* name) /*!< in: the name of the event, if NULL the event is created without a name */ { -#ifdef __WIN__ - os_event_t event; - - event = ut_malloc(sizeof(struct os_event_struct)); - - event->handle = CreateEvent(NULL, /* No security attributes */ - TRUE, /* Manual reset */ - FALSE, /* Initial state nonsignaled */ - (LPCTSTR) name); - if (!event->handle) { - fprintf(stderr, - "InnoDB: Could not create a Windows event semaphore;" - " Windows error %lu\n", - (ulong) GetLastError()); - } -#else /* Unix */ os_event_t event; - UT_NOT_USED(name); +#ifdef __WIN__ + if(!srv_use_native_conditions) { - event = ut_malloc(sizeof(struct os_event_struct)); + event = ut_malloc(sizeof(struct os_event_struct)); - os_fast_mutex_init(&(event->os_mutex)); + event->handle = CreateEvent(NULL, + TRUE, + FALSE, + (LPCTSTR) name); + if (!event->handle) { + fprintf(stderr, + "InnoDB: Could not create a Windows event" + " semaphore; Windows error %lu\n", + (ulong) GetLastError()); + } + } else /* Windows with condition variables */ +#endif - ut_a(0 == pthread_cond_init(&(event->cond_var), NULL)); + { + UT_NOT_USED(name); - event->is_set = FALSE; + event = ut_malloc(sizeof(struct os_event_struct)); - /* We return this value in os_event_reset(), which can then be - be used to pass to the os_event_wait_low(). The value of zero - is reserved in os_event_wait_low() for the case when the - caller does not want to pass any signal_count value. To - distinguish between the two cases we initialize signal_count - to 1 here. */ - event->signal_count = 1; -#endif /* __WIN__ */ + os_fast_mutex_init(&(event->os_mutex)); + + os_cond_init(&(event->cond_var)); + + event->is_set = FALSE; + + /* We return this value in os_event_reset(), which can then be + be used to pass to the os_event_wait_low(). The value of zero + is reserved in os_event_wait_low() for the case when the + caller does not want to pass any signal_count value. To + distinguish between the two cases we initialize signal_count + to 1 here. */ + event->signal_count = 1; + } /* The os_sync_mutex can be NULL because during startup an event can be created [ because it's embedded in the mutex/rwlock ] before @@ -208,10 +364,15 @@ os_event_set( /*=========*/ os_event_t event) /*!< in: event to set */ { -#ifdef __WIN__ ut_a(event); - ut_a(SetEvent(event->handle)); -#else + +#ifdef __WIN__ + if (!srv_use_native_conditions) { + ut_a(SetEvent(event->handle)); + return; + } +#endif + ut_a(event); os_fast_mutex_lock(&(event->os_mutex)); @@ -221,11 +382,10 @@ os_event_set( } else { event->is_set = TRUE; event->signal_count += 1; - ut_a(0 == pthread_cond_broadcast(&(event->cond_var))); + os_cond_broadcast(&(event->cond_var)); } os_fast_mutex_unlock(&(event->os_mutex)); -#endif } /**********************************************************//** @@ -244,12 +404,14 @@ os_event_reset( { ib_int64_t ret = 0; -#ifdef __WIN__ ut_a(event); - ut_a(ResetEvent(event->handle)); -#else - ut_a(event); +#ifdef __WIN__ + if(!srv_use_native_conditions) { + ut_a(ResetEvent(event->handle)); + return(0); + } +#endif os_fast_mutex_lock(&(event->os_mutex)); @@ -261,7 +423,6 @@ os_event_reset( ret = event->signal_count; os_fast_mutex_unlock(&(event->os_mutex)); -#endif return(ret); } @@ -274,19 +435,21 @@ os_event_free_internal( os_event_t event) /*!< in: event to free */ { #ifdef __WIN__ - ut_a(event); - - ut_a(CloseHandle(event->handle)); -#else - ut_a(event); - - /* This is to avoid freeing the mutex twice */ - os_fast_mutex_free(&(event->os_mutex)); - - ut_a(0 == pthread_cond_destroy(&(event->cond_var))); + if(!srv_use_native_conditions) { + ut_a(event); + ut_a(CloseHandle(event->handle)); + } else #endif - /* Remove from the list of events */ + { + ut_a(event); + /* This is to avoid freeing the mutex twice */ + os_fast_mutex_free(&(event->os_mutex)); + + os_cond_destroy(&(event->cond_var)); + } + + /* Remove from the list of events */ UT_LIST_REMOVE(os_event_list, os_event_list, event); os_event_count--; @@ -303,18 +466,19 @@ os_event_free( os_event_t event) /*!< in: event to free */ { + ut_a(event); #ifdef __WIN__ - ut_a(event); - - ut_a(CloseHandle(event->handle)); -#else - ut_a(event); - - os_fast_mutex_free(&(event->os_mutex)); - ut_a(0 == pthread_cond_destroy(&(event->cond_var))); + if(!srv_use_native_conditions){ + ut_a(CloseHandle(event->handle)); + } else /*Windows with condition variables */ #endif - /* Remove from the list of events */ + { + os_fast_mutex_free(&(event->os_mutex)); + os_cond_destroy(&(event->cond_var)); + } + + /* Remove from the list of events */ os_mutex_enter(os_sync_mutex); UT_LIST_REMOVE(os_event_list, os_event_list, event); @@ -355,24 +519,28 @@ os_event_wait_low( returned by previous call of os_event_reset(). */ { -#ifdef __WIN__ - DWORD err; - - ut_a(event); - - UT_NOT_USED(reset_sig_count); - - /* Specify an infinite time limit for waiting */ - err = WaitForSingleObject(event->handle, INFINITE); - - ut_a(err == WAIT_OBJECT_0); - - if (srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS) { - os_thread_exit(NULL); - } -#else ib_int64_t old_signal_count; +#ifdef __WIN__ + if(!srv_use_native_conditions) { + DWORD err; + + ut_a(event); + + UT_NOT_USED(reset_sig_count); + + /* Specify an infinite wait */ + err = WaitForSingleObject(event->handle, INFINITE); + + ut_a(err == WAIT_OBJECT_0); + + if (srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS) { + os_thread_exit(NULL); + } + return; + } +#endif + os_fast_mutex_lock(&(event->os_mutex)); if (reset_sig_count) { @@ -396,123 +564,29 @@ os_event_wait_low( return; } - pthread_cond_wait(&(event->cond_var), &(event->os_mutex)); + os_cond_wait(&(event->cond_var), &(event->os_mutex)); /* Solaris manual said that spurious wakeups may occur: we have to check if the event really has been signaled after we came here to wait */ } -#endif } -/**********************************************************//** -Waits for an event object until it is in the signaled state or -a timeout is exceeded. In Unix the timeout is always infinite. -@return 0 if success, OS_SYNC_TIME_EXCEEDED if timeout was exceeded */ -UNIV_INTERN -ulint -os_event_wait_time( -/*===============*/ - os_event_t event, /*!< in: event to wait */ - ulint time) /*!< in: timeout in microseconds, or - OS_SYNC_INFINITE_TIME */ -{ -#ifdef __WIN__ - DWORD err; - - ut_a(event); - - if (time != OS_SYNC_INFINITE_TIME) { - err = WaitForSingleObject(event->handle, (DWORD) time / 1000); - } else { - err = WaitForSingleObject(event->handle, INFINITE); - } - - if (err == WAIT_OBJECT_0) { - - return(0); - } else if (err == WAIT_TIMEOUT) { - - return(OS_SYNC_TIME_EXCEEDED); - } else { - ut_error; - return(1000000); /* dummy value to eliminate compiler warn. */ - } -#else - UT_NOT_USED(time); - - /* In Posix this is just an ordinary, infinite wait */ - - os_event_wait(event); - - return(0); -#endif -} - -#ifdef __WIN__ -/**********************************************************//** -Waits for any event in an OS native event array. Returns if even a single -one is signaled or becomes signaled. -@return index of the event which was signaled */ -UNIV_INTERN -ulint -os_event_wait_multiple( -/*===================*/ - ulint n, /*!< in: number of events in the - array */ - os_native_event_t* native_event_array) - /*!< in: pointer to an array of event - handles */ -{ - DWORD index; - - ut_a(native_event_array); - ut_a(n > 0); - - index = WaitForMultipleObjects((DWORD) n, native_event_array, - FALSE, /* Wait for any 1 event */ - INFINITE); /* Infinite wait time - limit */ - ut_a(index >= WAIT_OBJECT_0); /* NOTE: Pointless comparison */ - ut_a(index < WAIT_OBJECT_0 + n); - - if (srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS) { - os_thread_exit(NULL); - } - - return(index - WAIT_OBJECT_0); -} -#endif - /*********************************************************//** Creates an operating system mutex semaphore. Because these are slow, the mutex semaphore of InnoDB itself (mutex_t) should be used where possible. @return the mutex handle */ UNIV_INTERN os_mutex_t -os_mutex_create( -/*============*/ - const char* name) /*!< in: the name of the mutex, if NULL - the mutex is created without a name */ +os_mutex_create(void) +/*=================*/ { -#ifdef __WIN__ - HANDLE mutex; - os_mutex_t mutex_str; - - mutex = CreateMutex(NULL, /* No security attributes */ - FALSE, /* Initial state: no owner */ - (LPCTSTR) name); - ut_a(mutex); -#else os_fast_mutex_t* mutex; os_mutex_t mutex_str; - UT_NOT_USED(name); - mutex = ut_malloc(sizeof(os_fast_mutex_t)); os_fast_mutex_init(mutex); -#endif mutex_str = ut_malloc(sizeof(os_mutex_str_t)); mutex_str->handle = mutex; @@ -543,25 +617,11 @@ os_mutex_enter( /*===========*/ os_mutex_t mutex) /*!< in: mutex to acquire */ { -#ifdef __WIN__ - DWORD err; - - ut_a(mutex); - - /* Specify infinite time limit for waiting */ - err = WaitForSingleObject(mutex->handle, INFINITE); - - ut_a(err == WAIT_OBJECT_0); - - (mutex->count)++; - ut_a(mutex->count == 1); -#else os_fast_mutex_lock(mutex->handle); (mutex->count)++; ut_a(mutex->count == 1); -#endif } /**********************************************************//** @@ -577,11 +637,7 @@ os_mutex_exit( ut_a(mutex->count == 1); (mutex->count)--; -#ifdef __WIN__ - ut_a(ReleaseMutex(mutex->handle)); -#else os_fast_mutex_unlock(mutex->handle); -#endif } /**********************************************************//** @@ -610,15 +666,9 @@ os_mutex_free( os_mutex_exit(os_sync_mutex); } -#ifdef __WIN__ - ut_a(CloseHandle(mutex->handle)); - - ut_free(mutex); -#else os_fast_mutex_free(mutex->handle); ut_free(mutex->handle); ut_free(mutex); -#endif } /*********************************************************//** diff --git a/storage/innobase/os/os0thread.c b/storage/innobase/os/os0thread.c index 78df66d7834..b41d873a129 100644 --- a/storage/innobase/os/os0thread.c +++ b/storage/innobase/os/os0thread.c @@ -252,7 +252,7 @@ os_thread_yield(void) /*=================*/ { #if defined(__WIN__) - Sleep(0); + SwitchToThread(); #elif (defined(HAVE_SCHED_YIELD) && defined(HAVE_SCHED_H)) sched_yield(); #elif defined(HAVE_PTHREAD_YIELD_ZERO_ARG) diff --git a/storage/innobase/srv/srv0srv.c b/storage/innobase/srv/srv0srv.c index 6354689105a..7430ff73668 100644 --- a/storage/innobase/srv/srv0srv.c +++ b/storage/innobase/srv/srv0srv.c @@ -142,6 +142,21 @@ use simulated aio we build below with threads. Currently we support native aio on windows and linux */ UNIV_INTERN my_bool srv_use_native_aio = TRUE; +#ifdef __WIN__ +/* Windows native condition variables. We use runtime loading / function +pointers, because they are not available on Windows Server 2003 and +Windows XP/2000. + +We use condition for events on Windows if possible, even if os_event +resembles Windows kernel event object well API-wise. The reason is +performance, kernel objects are heavyweights and WaitForSingleObject() is a +performance killer causing calling thread to context switch. Besides, Innodb +is preallocating large number (often millions) of os_events. With kernel event +objects it takes a big chunk out of non-paged pool, which is better suited +for tasks like IO than for storing idle event objects. */ +UNIV_INTERN ibool srv_use_native_conditions = FALSE; +#endif /* __WIN__ */ + UNIV_INTERN ulint srv_n_data_files = 0; UNIV_INTERN char** srv_data_file_names = NULL; /* size in database pages */ diff --git a/storage/innobase/srv/srv0start.c b/storage/innobase/srv/srv0start.c index 4a0ecc5154f..d6ad554d41e 100644 --- a/storage/innobase/srv/srv0start.c +++ b/storage/innobase/srv/srv0start.c @@ -1160,10 +1160,18 @@ innobase_start_or_create_for_mysql(void) srv_use_native_aio = FALSE; break; - default: - /* On Win 2000 and XP use async i/o */ + + case OS_WIN2000: + case OS_WINXP: + /* On 2000 and XP, async IO is available. */ srv_use_native_aio = TRUE; break; + + default: + /* Vista and later have both async IO and condition variables */ + srv_use_native_aio = TRUE; + srv_use_native_conditions = TRUE; + break; } #elif defined(LINUX_NATIVE_AIO) diff --git a/storage/innobase/sync/sync0arr.c b/storage/innobase/sync/sync0arr.c index 248bd2cd25d..753ebd958ac 100644 --- a/storage/innobase/sync/sync0arr.c +++ b/storage/innobase/sync/sync0arr.c @@ -250,7 +250,7 @@ sync_array_create( /* Then create the mutex to protect the wait array complex */ if (protection == SYNC_ARRAY_OS_MUTEX) { - arr->os_mutex = os_mutex_create(NULL); + arr->os_mutex = os_mutex_create(); } else if (protection == SYNC_ARRAY_MUTEX) { mutex_create(syn_arr_mutex_key, &arr->mutex, SYNC_NO_ORDER_CHECK); From 49d327ebf763dca23969ac4afe814617815d1411 Mon Sep 17 00:00:00 2001 From: Dmitry Shulga Date: Wed, 21 Jul 2010 14:56:43 +0700 Subject: [PATCH 083/115] Fixed bug #42496 - the server could crash on a debug assert after a failure to write into a closed socket --- sql/protocol.cc | 15 +++++++++++---- sql/sql_cache.cc | 3 ++- sql/sql_cursor.cc | 7 ++++++- sql/sql_prepare.cc | 7 +++++-- 4 files changed, 24 insertions(+), 8 deletions(-) diff --git a/sql/protocol.cc b/sql/protocol.cc index dc53e029647..dfb78462f13 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -534,7 +534,11 @@ void Protocol::end_partial_result_set(THD *thd_arg) bool Protocol::flush() { #ifndef EMBEDDED_LIBRARY - return net_flush(&thd->net); + bool error; + thd->main_da.can_overwrite_status= TRUE; + error= net_flush(&thd->net); + thd->main_da.can_overwrite_status= FALSE; + return error; #else return 0; #endif @@ -574,7 +578,8 @@ bool Protocol::send_fields(List *list, uint flags) if (flags & SEND_NUM_ROWS) { // Packet with number of elements uchar *pos= net_store_length(buff, list->elements); - (void) my_net_write(&thd->net, buff, (size_t) (pos-buff)); + if (my_net_write(&thd->net, buff, (size_t) (pos-buff))) + DBUG_RETURN(1); } #ifndef DBUG_OFF @@ -698,7 +703,7 @@ bool Protocol::send_fields(List *list, uint flags) if (flags & SEND_DEFAULTS) item->send(&prot, &tmp); // Send default value if (prot.write()) - break; /* purecov: inspected */ + DBUG_RETURN(1); #ifndef DBUG_OFF field_types[count++]= field.type; #endif @@ -711,7 +716,9 @@ bool Protocol::send_fields(List *list, uint flags) to show that there is no cursor. Send no warning information, as it will be sent at statement end. */ - write_eof_packet(thd, &thd->net, thd->server_status, thd->total_warn_count); + if (write_eof_packet(thd, &thd->net, thd->server_status, + thd->total_warn_count)) + DBUG_RETURN(1); } DBUG_RETURN(prepare_for_send(list)); diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index f862cbed4f1..fcf4edbdc22 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -1653,7 +1653,8 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d", thd->limit_found_rows = query->found_rows(); thd->status_var.last_query_cost= 0.0; - thd->main_da.disable_status(); + if (!thd->main_da.is_set()) + thd->main_da.disable_status(); BLOCK_UNLOCK_RD(query_block); DBUG_RETURN(1); // Result sent to client diff --git a/sql/sql_cursor.cc b/sql/sql_cursor.cc index 6f61dc40f66..d7d029d28d4 100644 --- a/sql/sql_cursor.cc +++ b/sql/sql_cursor.cc @@ -658,7 +658,12 @@ void Materialized_cursor::fetch(ulong num_rows) if ((res= table->file->rnd_next(table->record[0]))) break; /* Send data only if the read was successful. */ - result->send_data(item_list); + /* + If network write failed (i.e. due to a closed socked), + the error has already been set. Just return. + */ + if (result->send_data(item_list)) + return; } switch (res) { diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 041d9f7c30b..bd152866deb 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -263,8 +263,11 @@ static bool send_prep_stmt(Prepared_statement *stmt, uint columns) &stmt->lex->param_list, Protocol::SEND_EOF); } - /* Flag that a response has already been sent */ - thd->main_da.disable_status(); + + if (!error) + /* Flag that a response has already been sent */ + thd->main_da.disable_status(); + DBUG_RETURN(error); } #else From 047d47241c6e0eff1e5b55f9f34b28049c4ed989 Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Wed, 21 Jul 2010 18:05:57 +0300 Subject: [PATCH 084/115] Addendum #4 to bug #53095 SHOW DATABASES LIKE ... was not converting to lowercase on comparison as the documentation is suggesting. Fixed it to behave similarly to SHOW TABLES LIKE ... and updated the failing on MacOSX lowercase_table2 test case. --- mysql-test/r/lowercase_table2.result | 3 ++- sql/sql_show.cc | 15 +++++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/mysql-test/r/lowercase_table2.result b/mysql-test/r/lowercase_table2.result index 51c2ac0faf5..c9a46b70fab 100644 --- a/mysql-test/r/lowercase_table2.result +++ b/mysql-test/r/lowercase_table2.result @@ -56,6 +56,7 @@ CREATE DATABASE `TEST_$1`; SHOW DATABASES LIKE "TEST%"; Database (TEST%) TEST_$1 +test DROP DATABASE `test_$1`; CREATE TABLE T1 (a int) engine=innodb; INSERT INTO T1 VALUES (1); @@ -171,6 +172,6 @@ create table myUC (i int); select TABLE_SCHEMA,TABLE_NAME FROM information_schema.TABLES where TABLE_SCHEMA ='mysqltest_LC2'; TABLE_SCHEMA TABLE_NAME -mysqltest_LC2 myUC +mysqltest_lc2 myUC use test; drop database mysqltest_LC2; diff --git a/sql/sql_show.cc b/sql/sql_show.cc index eb5d3a1965d..616bced8f4a 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -521,8 +521,19 @@ find_files(THD *thd, List *files, const char *db, continue; file_name_len= filename_to_tablename(file->name, uname, sizeof(uname)); - if (wild && wild_compare(uname, wild, 0)) - continue; + if (wild) + { + if (lower_case_table_names) + { + if (my_wildcmp(files_charset_info, + uname, uname + file_name_len, + wild, wild + wild_length, + wild_prefix, wild_one,wild_many)) + continue; + } + else if (wild_compare(uname, wild, 0)) + continue; + } if (!(file_name= thd->make_lex_string(file_name, uname, file_name_len, TRUE))) { From 6a8e6ac4c5f19023b2ac1013e966f1f09e7393fb Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Wed, 21 Jul 2010 20:19:59 +0300 Subject: [PATCH 085/115] Increment InnoDB version to 1.1.2 InnoDB 1.1.1 was released with MySQL 5.5.5-m3 --- storage/innobase/include/univ.i | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/innobase/include/univ.i b/storage/innobase/include/univ.i index 863d8300438..ac87942f255 100644 --- a/storage/innobase/include/univ.i +++ b/storage/innobase/include/univ.i @@ -46,7 +46,7 @@ Created 1/20/1994 Heikki Tuuri #define INNODB_VERSION_MAJOR 1 #define INNODB_VERSION_MINOR 1 -#define INNODB_VERSION_BUGFIX 1 +#define INNODB_VERSION_BUGFIX 2 /* The following is the InnoDB version as shown in SELECT plugin_version FROM information_schema.plugins; From 043e9e2491e1000c33d763aabb1fdc5ce04251a9 Mon Sep 17 00:00:00 2001 From: Sunny Bains Date: Thu, 22 Jul 2010 07:41:51 +1000 Subject: [PATCH 086/115] Bug 54617 - During shutdown: Failing assertion: arr->n_reserved == 0 Get rid of at least on suspicious path. Ensure that the purge thread doesn't acquire a mutex after it has signalled shutdown. --- storage/innobase/srv/srv0srv.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/storage/innobase/srv/srv0srv.c b/storage/innobase/srv/srv0srv.c index 7430ff73668..97d699dde99 100644 --- a/storage/innobase/srv/srv0srv.c +++ b/storage/innobase/srv/srv0srv.c @@ -3022,6 +3022,8 @@ srv_purge_thread( slot_no = srv_table_reserve_slot(SRV_WORKER); + slot = srv_table_get_nth_slot(slot_no); + ++srv_n_threads_active[SRV_WORKER]; mutex_exit(&kernel_mutex); @@ -3073,20 +3075,16 @@ srv_purge_thread( mutex_enter(&kernel_mutex); + ut_ad(srv_table_get_nth_slot(slot_no) == slot); + /* Decrement the active count. */ srv_suspend_thread(); - mutex_exit(&kernel_mutex); + slot->in_use = FALSE; /* Free the thread local memory. */ thr_local_free(os_thread_get_curr_id()); - mutex_enter(&kernel_mutex); - - /* Free the slot for reuse. */ - slot = srv_table_get_nth_slot(slot_no); - slot->in_use = FALSE; - mutex_exit(&kernel_mutex); #ifdef UNIV_DEBUG_THREAD_CREATION From 7c6d9a94b793bfdee8ca57537f1daabc63fe9bbd Mon Sep 17 00:00:00 2001 From: Sunny Bains Date: Thu, 22 Jul 2010 09:16:19 +1000 Subject: [PATCH 087/115] Bug#54583 InnoDB: Assertion failure sync/sync0sync.c:1226 Silence the UNIV_SYNC_DEBUG assertion failure while upgrading old data files to multiple rollback segments during server startup. Because the upgrade takes place while InnoDB is running a single thread, we can safely ignore the latching order checks without fearing deadlocks. innobase_start_or_create_for_mysql(): Set srv_is_being_started = FALSE only after trx_sys_create_rsegs() has completed. sync_thread_add_level(): If srv_is_being_started, ignore latching order violations for SYNC_TRX_SYS_HEADER and SYNC_IBUF_BITMAP. Create all the non-IO threads after creating the extra rollback segments. Patch originally from Marko with some additions by Sunny. --- storage/innobase/srv/srv0start.c | 35 ++++++++++++++++++------------- storage/innobase/sync/sync0sync.c | 19 ++++++++++++++--- 2 files changed, 37 insertions(+), 17 deletions(-) diff --git a/storage/innobase/srv/srv0start.c b/storage/innobase/srv/srv0start.c index 3d4aaa9d5d4..4da836672ec 100644 --- a/storage/innobase/srv/srv0start.c +++ b/storage/innobase/srv/srv0start.c @@ -1703,6 +1703,27 @@ innobase_start_or_create_for_mysql(void) /* fprintf(stderr, "Max allowed record size %lu\n", page_get_free_space_of_empty() / 2); */ + if (trx_doublewrite == NULL) { + /* Create the doublewrite buffer to a new tablespace */ + + trx_sys_create_doublewrite_buf(); + } + + /* Here the double write buffer has already been created and so + any new rollback segments will be allocated after the double + write buffer. The default segment should already exist. + We create the new segments only if it's a new database or + the database was shutdown cleanly. */ + + /* Note: When creating the extra rollback segments during an upgrade + we violate the latching order, even if the change buffer is empty. + We make an exception in sync0sync.c and check srv_is_being_started + for that violation. It cannot create a deadlock because we are still + running in single threaded mode essentially. Only the IO threads + should be running at this stage. */ + + trx_sys_create_rsegs(TRX_SYS_N_RSEGS - 1); + /* Create the thread which watches the timeouts for lock waits */ os_thread_create(&srv_lock_timeout_thread, NULL, thread_ids + 2 + SRV_MAX_N_IO_THREADS); @@ -1717,20 +1738,6 @@ innobase_start_or_create_for_mysql(void) srv_is_being_started = FALSE; - if (trx_doublewrite == NULL) { - /* Create the doublewrite buffer to a new tablespace */ - - trx_sys_create_doublewrite_buf(); - } - - /* Here the double write buffer has already been created and so - any new rollback segments will be allocated after the double - write buffer. The default segment should already exist. - We create the new segments only if it's a new database or - the database was shutdown cleanly. */ - - trx_sys_create_rsegs(TRX_SYS_N_RSEGS - 1); - err = dict_create_or_check_foreign_constraint_tables(); if (err != DB_SUCCESS) { diff --git a/storage/innobase/sync/sync0sync.c b/storage/innobase/sync/sync0sync.c index 0bc96aae9de..8062d9e902e 100644 --- a/storage/innobase/sync/sync0sync.c +++ b/storage/innobase/sync/sync0sync.c @@ -40,6 +40,9 @@ Created 9/5/1995 Heikki Tuuri #include "srv0srv.h" #include "buf0types.h" #include "os0sync.h" /* for HAVE_ATOMIC_BUILTINS */ +#ifdef UNIV_SYNC_DEBUG +# include "srv0start.h" /* srv_is_being_started */ +#endif /* UNIV_SYNC_DEBUG */ /* REASONS FOR IMPLEMENTING THE SPIN LOCK MUTEX @@ -1152,6 +1155,13 @@ sync_thread_add_level( case SYNC_TREE_NODE_FROM_HASH: /* Do no order checking */ break; + case SYNC_TRX_SYS_HEADER: + if (srv_is_being_started) { + /* This is violated during trx_sys_create_rsegs() + when creating additional rollback segments when + upgrading in innobase_start_or_create_for_mysql(). */ + break; + } case SYNC_MEM_POOL: case SYNC_MEM_HASH: case SYNC_RECV: @@ -1160,7 +1170,6 @@ sync_thread_add_level( case SYNC_LOG_FLUSH_ORDER: case SYNC_THR_LOCAL: case SYNC_ANY_LATCH: - case SYNC_TRX_SYS_HEADER: case SYNC_FILE_FORMAT_TAG: case SYNC_DOUBLEWRITE: case SYNC_SEARCH_SYS: @@ -1222,8 +1231,12 @@ sync_thread_add_level( ut_a(sync_thread_levels_g(array, SYNC_IBUF_BITMAP - 1, TRUE)); } else { - ut_a(sync_thread_levels_g(array, SYNC_IBUF_BITMAP, - TRUE)); + /* This is violated during trx_sys_create_rsegs() + when creating additional rollback segments when + upgrading in innobase_start_or_create_for_mysql(). */ + ut_a(srv_is_being_started + || sync_thread_levels_g(array, SYNC_IBUF_BITMAP, + TRUE)); } break; case SYNC_FSP_PAGE: From b00f1560884b5b1eeddb2ce85e825e6ef93c7032 Mon Sep 17 00:00:00 2001 From: Sunny Bains Date: Thu, 22 Jul 2010 14:46:12 +1000 Subject: [PATCH 088/115] Remove code that was added during the flush list mutex refactoring. We cannot release a dirty page in the middle of a mini-transaction. Replace the code with an assertion that checks for this condition. Original svn revision was: r6330. --- storage/innobase/mtr/mtr0mtr.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/storage/innobase/mtr/mtr0mtr.c b/storage/innobase/mtr/mtr0mtr.c index b38c5b0d63c..74d04a22b86 100644 --- a/storage/innobase/mtr/mtr0mtr.c +++ b/storage/innobase/mtr/mtr0mtr.c @@ -337,9 +337,12 @@ mtr_memo_release( slot = dyn_array_get_element(memo, offset); if (object == slot->object && type == slot->type) { - if (mtr->modifications) { - mtr_memo_slot_note_modification(mtr, slot); - } + + /* We cannot release a page that has been written + to in the middle of a mini-transaction. */ + + ut_ad(!(mtr->modifications + && slot->type == MTR_MEMO_PAGE_X_FIX)); mtr_memo_slot_release(mtr, slot); From 131095149f81aeb9a1423fb00ddd4d71a6dee011 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Thu, 22 Jul 2010 10:00:32 +0200 Subject: [PATCH 089/115] Bug #55223 assert in Protocol::end_statement during CREATE DATABASE The problem was that a statement could cause an assert if it was aborted by KILL QUERY while it waited on a metadata lock. This assert checks that a statement either sends OK or an error to the client. If the bug was triggered on release builds, it caused OK to be sent to the client instead of ER_QUERY_INTERRUPTED. The root cause of the problem was that there are two separate ways to tell if a statement is killed: thd->killed and mysys_var->abort. KILL QUERY causes both to be set, thd->killed before mysys_var->abort. Also, both values are reset at the end of statement execution. This means that it is possible for KILL QUERY to first set thd->killed, then have the killed statement reset both thd->killed and mysys_var->abort and finally have KILL QUERY set mysys_var->abort. This means that the connection with the killed statement will start executing the next statement with the two values out of sync - i.e. thd->killed not set but mysys_var->abort set. Since mysys_var->abort is used to check if a wait for a metadata lock should be aborted, the next statement would immediately abort any such waiting. When waiting is aborted, no OK message is sent and thd->killed is checked to see if ER_QUERY_INTERRUPTED should be sent to the client. But since the->killed had been reset, neither OK nor an error message was sent to the client. This then triggered the assert. This patch fixes the problem by changing the metadata lock waiting code to check thd->killed. No test case added as reproducing the assert is dependent on very exact timing of two (or more) threads. The patch has been checked using RQG and the grammar posted on the bug report. --- sql/mdl.cc | 72 ++++-------------------------------------------------- sql/mdl.h | 8 +++--- 2 files changed, 9 insertions(+), 71 deletions(-) diff --git a/sql/mdl.cc b/sql/mdl.cc index 818b736e597..ca66799baed 100644 --- a/sql/mdl.cc +++ b/sql/mdl.cc @@ -881,67 +881,6 @@ uint MDL_ticket::get_deadlock_weight() const } -/** - Helper functions and macros to be used for killable waiting in metadata - locking subsystem. - - @sa THD::enter_cond()/exit_cond()/killed. - - @note We can't use THD::enter_cond()/exit_cond()/killed directly here - since this will make metadata subsystem dependent on THD class - and thus prevent us from writing unit tests for it. And usage of - wrapper functions to access THD::killed/enter_cond()/exit_cond() - will probably introduce too much overhead. -*/ - -#define MDL_ENTER_COND(A, B, C, D) \ - mdl_enter_cond(A, B, C, D, __func__, __FILE__, __LINE__) - -static inline const char *mdl_enter_cond(THD *thd, - st_my_thread_var *mysys_var, - mysql_cond_t *cond, - mysql_mutex_t *mutex, - const char *calling_func, - const char *calling_file, - const unsigned int calling_line) -{ - mysql_mutex_assert_owner(mutex); - - mysys_var->current_mutex= mutex; - mysys_var->current_cond= cond; - - DEBUG_SYNC(thd, "mdl_enter_cond"); - - return set_thd_proc_info(thd, "Waiting for table", - calling_func, calling_file, calling_line); -} - -#define MDL_EXIT_COND(A, B, C, D) \ - mdl_exit_cond(A, B, C, D, __func__, __FILE__, __LINE__) - -static inline void mdl_exit_cond(THD *thd, - st_my_thread_var *mysys_var, - mysql_mutex_t *mutex, - const char* old_msg, - const char *calling_func, - const char *calling_file, - const unsigned int calling_line) -{ - DBUG_ASSERT(mutex == mysys_var->current_mutex); - - mysql_mutex_unlock(mutex); - mysql_mutex_lock(&mysys_var->mutex); - mysys_var->current_mutex= NULL; - mysys_var->current_cond= NULL; - mysql_mutex_unlock(&mysys_var->mutex); - - DEBUG_SYNC(thd, "mdl_exit_cond"); - - (void) set_thd_proc_info(thd, old_msg, calling_func, - calling_file, calling_line); -} - - /** Construct an empty wait slot. */ MDL_wait::MDL_wait() @@ -1021,15 +960,14 @@ MDL_wait::timed_wait(THD *thd, struct timespec *abs_timeout, { const char *old_msg; enum_wait_status result; - st_my_thread_var *mysys_var= my_thread_var; int wait_result= 0; mysql_mutex_lock(&m_LOCK_wait_status); - old_msg= MDL_ENTER_COND(thd, mysys_var, &m_COND_wait_status, - &m_LOCK_wait_status); + old_msg= thd_enter_cond(thd, &m_COND_wait_status, &m_LOCK_wait_status, + "Waiting for table"); - while (!m_wait_status && !mysys_var->abort && + while (!m_wait_status && !thd_killed(thd) && wait_result != ETIMEDOUT && wait_result != ETIME) wait_result= mysql_cond_timedwait(&m_COND_wait_status, &m_LOCK_wait_status, abs_timeout); @@ -1048,14 +986,14 @@ MDL_wait::timed_wait(THD *thd, struct timespec *abs_timeout, false, which means that the caller intends to restart the wait. */ - if (mysys_var->abort) + if (thd_killed(thd)) m_wait_status= KILLED; else if (set_status_on_timeout) m_wait_status= TIMEOUT; } result= m_wait_status; - MDL_EXIT_COND(thd, mysys_var, &m_LOCK_wait_status, old_msg); + thd_exit_cond(thd, old_msg); return result; } diff --git a/sql/mdl.h b/sql/mdl.h index 319c16cf6ce..c8acd69c0f1 100644 --- a/sql/mdl.h +++ b/sql/mdl.h @@ -721,10 +721,10 @@ void mdl_destroy(); extern bool mysql_notify_thread_having_shared_lock(THD *thd, THD *in_use, bool needs_thr_lock_abort); -extern "C" const char *set_thd_proc_info(void *thd_arg, const char *info, - const char *calling_function, - const char *calling_file, - const unsigned int calling_line); +extern "C" const char* thd_enter_cond(MYSQL_THD thd, mysql_cond_t *cond, + mysql_mutex_t *mutex, const char *msg); +extern "C" void thd_exit_cond(MYSQL_THD thd, const char *old_msg); + #ifndef DBUG_OFF extern mysql_mutex_t LOCK_open; #endif From 0dd0f30ba58ce7bcf0b896348c0514918690ef86 Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Thu, 22 Jul 2010 11:10:36 +0300 Subject: [PATCH 090/115] Fix .bzr-mysql/default.conf after the merge from mysql-trunk-bugfixing --- .bzr-mysql/default.conf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.bzr-mysql/default.conf b/.bzr-mysql/default.conf index 4eab3d239d0..df9a60f35ad 100644 --- a/.bzr-mysql/default.conf +++ b/.bzr-mysql/default.conf @@ -1,4 +1,4 @@ [MYSQL] -post_commit_to = "commits@lists.mysql.com" -post_push_to = "commits@lists.mysql.com" -tree_name = "mysql-trunk-bugfixing" +post_commit_to = commits@lists.mysql.com, innodb_dev_ww@oracle.com +post_push_to = commits@lists.mysql.com, innodb_dev_ww@oracle.com +tree_name = "mysql-trunk-innodb" From e814e9ac1e753c23438195c82ded0c5fe03d6436 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Thu, 22 Jul 2010 11:10:35 +0200 Subject: [PATCH 091/115] Bug #54905 Connection with WRITE lock cannot ALTER table due to concurrent SHOW CREATE The problem was that a SHOW CREATE TABLE statement issued inside a transaction did not release its metadata locks at the end of the statement execution. This happened even if SHOW CREATE TABLE is an information statement. The consequence was that SHOW CREATE TABLE was able to block other connections from accessing the table (e.g. using ALTER TABLE). This patch fixes the problem by explicitly releasing any metadata locks taken by SHOW CREATE TABLE after the statement completes. Test case added to show_check.test. --- mysql-test/r/show_check.result | 21 +++++++++++++++++++++ mysql-test/t/show_check.test | 33 +++++++++++++++++++++++++++++++++ sql/sql_show.cc | 32 +++++++++++++++++++++++--------- 3 files changed, 77 insertions(+), 9 deletions(-) diff --git a/mysql-test/r/show_check.result b/mysql-test/r/show_check.result index ef2277fef38..f49366cbf08 100644 --- a/mysql-test/r/show_check.result +++ b/mysql-test/r/show_check.result @@ -1466,3 +1466,24 @@ t1 CREATE TABLE `t1` ( # Switching to connection 'default'. UNLOCK TABLES; DROP TABLE t1; +# +# Bug#54905 Connection with WRITE lock cannot ALTER table due to +# concurrent SHOW CREATE +# +DROP TABLE IF EXISTS t1; +CREATE TABLE t1(a INT); +# Connection con1 +LOCK TABLE t1 WRITE; +# Connection default +START TRANSACTION; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +# Connection con1 +ALTER TABLE t1 CHARACTER SET = utf8; +UNLOCK TABLES; +# Connection default +COMMIT; +DROP TABLE t1; diff --git a/mysql-test/t/show_check.test b/mysql-test/t/show_check.test index fa003c2fe69..89a43f24296 100644 --- a/mysql-test/t/show_check.test +++ b/mysql-test/t/show_check.test @@ -1246,6 +1246,39 @@ UNLOCK TABLES; DROP TABLE t1; +--echo # +--echo # Bug#54905 Connection with WRITE lock cannot ALTER table due to +--echo # concurrent SHOW CREATE +--echo # + +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +CREATE TABLE t1(a INT); + +--echo # Connection con1 +connect (con1,localhost,root); +LOCK TABLE t1 WRITE; + +--echo # Connection default +connection default; +START TRANSACTION; +SHOW CREATE TABLE t1; + +--echo # Connection con1 +connection con1; +# Used to block +ALTER TABLE t1 CHARACTER SET = utf8; +UNLOCK TABLES; + +--echo # Connection default +connection default; +COMMIT; +disconnect con1; +DROP TABLE t1; + + # Wait till all disconnects are completed --source include/wait_until_count_sessions.inc diff --git a/sql/sql_show.cc b/sql/sql_show.cc index a8ae5832a2a..8647237ce72 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -649,22 +649,30 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) Protocol *protocol= thd->protocol; char buff[2048]; String buffer(buff, sizeof(buff), system_charset_info); + List field_list; + bool error= TRUE; DBUG_ENTER("mysqld_show_create"); DBUG_PRINT("enter",("db: %s table: %s",table_list->db, table_list->table_name)); + /* + Metadata locks taken during SHOW CREATE should be released when + the statmement completes as it is an information statement. + */ + MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint(); + /* We want to preserve the tree for views. */ thd->lex->view_prepare_mode= TRUE; { Show_create_error_handler view_error_suppressor(thd, table_list); thd->push_internal_handler(&view_error_suppressor); - bool error= + bool open_error= open_normal_and_derived_tables(thd, table_list, MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL); thd->pop_internal_handler(); - if (error && (thd->killed || thd->is_error())) - DBUG_RETURN(TRUE); + if (open_error && (thd->killed || thd->is_error())) + goto exit; } /* TODO: add environment variables show when it become possible */ @@ -672,7 +680,7 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) { my_error(ER_WRONG_OBJECT, MYF(0), table_list->db, table_list->table_name, "VIEW"); - DBUG_RETURN(TRUE); + goto exit; } buffer.length(0); @@ -684,9 +692,8 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) view_store_create_info(thd, table_list, &buffer) : store_create_info(thd, table_list, &buffer, NULL, FALSE /* show_database */))) - DBUG_RETURN(TRUE); + goto exit; - List field_list; if (table_list->view) { field_list.push_back(new Item_empty_string("View",NAME_CHAR_LEN)); @@ -707,7 +714,8 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) - DBUG_RETURN(TRUE); + goto exit; + protocol->prepare_for_resend(); if (table_list->view) protocol->store(table_list->view_name.str, system_charset_info); @@ -735,10 +743,16 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) protocol->store(buffer.ptr(), buffer.length(), buffer.charset()); if (protocol->write()) - DBUG_RETURN(TRUE); + goto exit; + error= FALSE; my_eof(thd); - DBUG_RETURN(FALSE); + +exit: + close_thread_tables(thd); + /* Release any metadata locks taken during SHOW CREATE. */ + thd->mdl_context.rollback_to_savepoint(mdl_savepoint); + DBUG_RETURN(error); } bool mysqld_show_create_db(THD *thd, char *dbname, From 92b9ca54228b750fd011ce83873a34aa187f7135 Mon Sep 17 00:00:00 2001 From: Davi Arnaut Date: Thu, 22 Jul 2010 08:30:14 -0300 Subject: [PATCH 092/115] Do not generate autotools configuration scripts when cmake is to be used. --- BUILD/FINISH.sh | 1 + BUILD/Makefile.am | 2 +- BUILD/autorun.sh | 36 +++++++++++-------- ...choose_configure.sh => cmake_configure.sh} | 0 4 files changed, 23 insertions(+), 16 deletions(-) rename BUILD/{choose_configure.sh => cmake_configure.sh} (100%) diff --git a/BUILD/FINISH.sh b/BUILD/FINISH.sh index 142ff7eb08e..b7f7a1db77e 100644 --- a/BUILD/FINISH.sh +++ b/BUILD/FINISH.sh @@ -6,6 +6,7 @@ configure="./configure $base_configs $extra_configs" commands="\ $make -k maintainer-clean || true /bin/rm -rf */.deps/*.P configure config.cache storage/*/configure storage/*/config.cache autom4te.cache storage/*/autom4te.cache; +/bin/rm -rf CMakeCache.txt CMakeFiles/ path=`dirname $0` . \"$path/autorun.sh\"" diff --git a/BUILD/Makefile.am b/BUILD/Makefile.am index c7bf813c9fe..c5732d43fbf 100644 --- a/BUILD/Makefile.am +++ b/BUILD/Makefile.am @@ -20,7 +20,7 @@ EXTRA_DIST = FINISH.sh \ SETUP.sh \ autorun.sh \ - choose_configure.sh \ + cmake_configure.sh \ build_mccge.sh \ check-cpu \ cleanup \ diff --git a/BUILD/autorun.sh b/BUILD/autorun.sh index f45b1f7d08c..9fdd0486360 100755 --- a/BUILD/autorun.sh +++ b/BUILD/autorun.sh @@ -21,18 +21,24 @@ done IFS="$save_ifs" rm -rf configure -aclocal || die "Can't execute aclocal" -autoheader || die "Can't execute autoheader" -# --force means overwrite ltmain.sh script if it already exists -$LIBTOOLIZE --automake --force --copy || die "Can't execute libtoolize" - -# --add-missing instructs automake to install missing auxiliary files -# and --force to overwrite them if they already exist -automake --add-missing --force --copy || die "Can't execute automake" -autoconf || die "Can't execute autoconf" -# Do not use autotools generated configure directly. Instead, use a script -# that will either call CMake or original configure shell script at build -# time (CMake is preferred if installed). -mv configure configure.am -cp BUILD/choose_configure.sh configure -chmod a+x configure + +# Ensure that cmake and perl are available. Required for cmake based builds. +cmake -P cmake/check_minimal_version.cmake >/dev/null 2>&1 || HAVE_CMAKE=no +perl --version >/dev/null 2>&1 || HAVE_CMAKE=no + +# Whether to use the autotools configuration script or cmake. +if test "$HAVE_CMAKE" = "no" +then + aclocal || die "Can't execute aclocal" + autoheader || die "Can't execute autoheader" + # --force means overwrite ltmain.sh script if it already exists + $LIBTOOLIZE --automake --force --copy || die "Can't execute libtoolize" + # --add-missing instructs automake to install missing auxiliary files + # and --force to overwrite them if they already exist + automake --add-missing --force --copy || die "Can't execute automake" + autoconf || die "Can't execute autoconf" +else + path=`dirname $0` + cp $path/cmake_configure.sh $path/../configure + chmod +x $path/../configure +fi diff --git a/BUILD/choose_configure.sh b/BUILD/cmake_configure.sh similarity index 100% rename from BUILD/choose_configure.sh rename to BUILD/cmake_configure.sh From 2773385dd7f2b02ae226538c818037056cd01fd2 Mon Sep 17 00:00:00 2001 From: Luis Soares Date: Thu, 22 Jul 2010 16:56:50 +0100 Subject: [PATCH 093/115] BUG#55387: binlog.binlog_tmp_table crashes the server sporadically There are two problems: 1. When closing temporary tables, during the THD clean up - and after the session connection was already closed, there is a chance we can push an error into the THD diagnostics area, if the writing of the implicit DROP event to the binary log fails for some reason. As a consequence an assertion can be triggered, because at that point the diagnostics area is already set. 2. Using push_warning with MYSQL_ERROR::WARN_LEVEL_ERROR is a bug. Given that close_temporary_tables is mostly called from THD::cleanup - ie, with the session already closed, we fix problem #1 by allowing the diagnostics area to be overwritten. There is one other place in the code that calls close_temporary_tables - while applying Start_log_event_v3. To cover that case, we make close_temporary_tables to return the error, thus, propagating upwards in the stack. To fix problem #2, we replace push_warning with sql_print_error. --- sql/log_event.cc | 7 ++++--- sql/sql_base.cc | 32 +++++++++++++++++++++++++------- sql/sql_base.h | 2 +- 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/sql/log_event.cc b/sql/log_event.cc index 5236a2794cf..fe8d13fac9c 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -3719,6 +3719,7 @@ bool Start_log_event_v3::write(IO_CACHE* file) int Start_log_event_v3::do_apply_event(Relay_log_info const *rli) { DBUG_ENTER("Start_log_event_v3::do_apply_event"); + int error= 0; switch (binlog_version) { case 3: @@ -3731,7 +3732,7 @@ int Start_log_event_v3::do_apply_event(Relay_log_info const *rli) */ if (created) { - close_temporary_tables(thd); + error= close_temporary_tables(thd); cleanup_load_tmpdir(); } else @@ -3759,7 +3760,7 @@ int Start_log_event_v3::do_apply_event(Relay_log_info const *rli) Can distinguish, based on the value of 'created': this event was generated at master startup. */ - close_temporary_tables(thd); + error= close_temporary_tables(thd); } /* Otherwise, can't distinguish a Start_log_event generated at @@ -3771,7 +3772,7 @@ int Start_log_event_v3::do_apply_event(Relay_log_info const *rli) /* this case is impossible */ DBUG_RETURN(1); } - DBUG_RETURN(0); + DBUG_RETURN(error); } #endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */ diff --git a/sql/sql_base.cc b/sql/sql_base.cc index f746edef83f..14cd8dd6354 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1634,7 +1634,7 @@ static inline uint tmpkeyval(THD *thd, TABLE *table) creates one DROP TEMPORARY TABLE binlog event for each pseudo-thread */ -void close_temporary_tables(THD *thd) +bool close_temporary_tables(THD *thd) { DBUG_ENTER("close_temporary_tables"); TABLE *table; @@ -1642,9 +1642,10 @@ void close_temporary_tables(THD *thd) TABLE *prev_table; /* Assume thd->variables.option_bits has OPTION_QUOTE_SHOW_CREATE */ bool was_quote_show= TRUE; + bool error= 0; if (!thd->temporary_tables) - DBUG_VOID_RETURN; + DBUG_RETURN(FALSE); if (!mysql_bin_log.is_open()) { @@ -1655,7 +1656,7 @@ void close_temporary_tables(THD *thd) close_temporary(table, 1, 1); } thd->temporary_tables= 0; - DBUG_VOID_RETURN; + DBUG_RETURN(FALSE); } /* Better add "if exists", in case a RESET MASTER has been done */ @@ -1754,11 +1755,27 @@ void close_temporary_tables(THD *thd) qinfo.db= db.ptr(); qinfo.db_len= db.length(); thd->variables.character_set_client= cs_save; - if (mysql_bin_log.write(&qinfo)) + + thd->stmt_da->can_overwrite_status= TRUE; + if ((error= (mysql_bin_log.write(&qinfo) || error))) { - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, MYF(0), - "Failed to write the DROP statement for temporary tables to binary log"); + /* + If we're here following THD::cleanup, thence the connection + has been closed already. So lets print a message to the + error log instead of pushing yet another error into the + stmt_da. + + Also, we keep the error flag so that we propagate the error + up in the stack. This way, if we're the SQL thread we notice + that close_temporary_tables failed. (Actually, the SQL + thread only calls close_temporary_tables while applying old + Start_log_event_v3 events.) + */ + sql_print_error("Failed to write the DROP statement for " + "temporary tables to binary log"); } + thd->stmt_da->can_overwrite_status= FALSE; + thd->variables.pseudo_thread_id= save_pseudo_thread_id; thd->thread_specific_used= save_thread_specific_used; } @@ -1771,7 +1788,8 @@ void close_temporary_tables(THD *thd) if (!was_quote_show) thd->variables.option_bits&= ~OPTION_QUOTE_SHOW_CREATE; /* restore option */ thd->temporary_tables=0; - DBUG_VOID_RETURN; + + DBUG_RETURN(error); } /* diff --git a/sql/sql_base.h b/sql/sql_base.h index 20a068e27d7..eed535f5cdc 100644 --- a/sql/sql_base.h +++ b/sql/sql_base.h @@ -222,7 +222,7 @@ int decide_logging_format(THD *thd, TABLE_LIST *tables); void free_io_cache(TABLE *entry); void intern_close_table(TABLE *entry); bool close_thread_table(THD *thd, TABLE **table_ptr); -void close_temporary_tables(THD *thd); +bool close_temporary_tables(THD *thd); TABLE_LIST *unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list, bool check_alias); int drop_temporary_table(THD *thd, TABLE_LIST *table_list); From 11b0caf1d6fe5311784e8d4129b1fef7b84caa72 Mon Sep 17 00:00:00 2001 From: "kevin.lewis@oracle.com" <> Date: Thu, 22 Jul 2010 11:15:15 -0500 Subject: [PATCH 094/115] Bug#49542 - Do as the comment suggests and downgrade directory errors from find_file() to a warning unless they happen during a SHOW command. --- sql/sql_show.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sql/sql_show.cc b/sql/sql_show.cc index eb5d3a1965d..c979d44dc1c 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1,4 +1,4 @@ -/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc. +/* Copyright 2000, 2010 Oracle and/or its affiliates. All rights reserved. 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 @@ -9,9 +9,9 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* Function with list databases, tables or fields */ @@ -2986,7 +2986,7 @@ make_table_name_list(THD *thd, List *table_names, LEX *lex, */ if (res == FIND_FILES_DIR) { - if (lex->sql_command != SQLCOM_SELECT) + if (sql_command_flags[lex->sql_command] & CF_STATUS_COMMAND) return 1; thd->clear_error(); return 2; From 3074ff5233d128be5a3539ab0dab9e213600513d Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Fri, 23 Jul 2010 10:37:44 +0300 Subject: [PATCH 095/115] Fix the failing innodb.innodb_bug54679 test A change in the default values of some config parameters caused this test to fail, adjust the test and make it more robust so it does not fail for the same reason in the future. --- mysql-test/suite/innodb/r/innodb_bug54679.result | 3 --- mysql-test/suite/innodb/t/innodb_bug54679.test | 4 ++++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/mysql-test/suite/innodb/r/innodb_bug54679.result b/mysql-test/suite/innodb/r/innodb_bug54679.result index 3190bbb5c7f..948696fb31d 100644 --- a/mysql-test/suite/innodb/r/innodb_bug54679.result +++ b/mysql-test/suite/innodb/r/innodb_bug54679.result @@ -86,6 +86,3 @@ Error 1005 Can't create table 'test.bug54679' (errno: 1478) SET GLOBAL innodb_file_per_table=ON; CREATE TABLE bug54679 (a INT) ENGINE=InnoDB ROW_FORMAT=DYNAMIC; DROP TABLE bug54679; -SET GLOBAL innodb_file_format=Antelope; -SET GLOBAL innodb_file_format_max=Antelope; -SET GLOBAL innodb_file_per_table=0; diff --git a/mysql-test/suite/innodb/t/innodb_bug54679.test b/mysql-test/suite/innodb/t/innodb_bug54679.test index a102a916705..c5e308acb5e 100644 --- a/mysql-test/suite/innodb/t/innodb_bug54679.test +++ b/mysql-test/suite/innodb/t/innodb_bug54679.test @@ -92,6 +92,10 @@ SET GLOBAL innodb_file_per_table=ON; CREATE TABLE bug54679 (a INT) ENGINE=InnoDB ROW_FORMAT=DYNAMIC; DROP TABLE bug54679; +# restore original values, quietly so the test does not fail if those +# defaults are changed +-- disable_query_log EVAL SET GLOBAL innodb_file_format=$file_format; EVAL SET GLOBAL innodb_file_format_max=$file_format_max; EVAL SET GLOBAL innodb_file_per_table=$file_per_table; +-- enable_query_log From 97bfd559f190fa9daf9886909844c3e12cc192a1 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Fri, 23 Jul 2010 10:44:55 +0200 Subject: [PATCH 096/115] Bug #55498 SHOW CREATE TRIGGER takes wrong type of metadata lock The first problem was that SHOW CREATE TRIGGER took a stronger metadata lock than required. This caused the statement to be blocked when it was not needed. For example, LOCK TABLE WRITE in one connection would block SHOW CREATE TRIGGER in another connection. Another problem was that a SHOW CREATE TRIGGER statement issued inside a transaction did not release its metadata locks at the end of the statement execution. This happened even if SHOW CREATE TRIGGER is an information statement. The consequence was that SHOW CREATE TRIGGER was able to block other connections from accessing the table (e.g. using ALTER TABLE). This patch fixes the problem by changing SHOW CREATE TRIGGER to take a MDL_SHARED_HIGH_PRIO metadata lock similar to what is already done for SHOW CREATE TABLE. The patch also changes SHOW CREATE TRIGGER to explicitly release any metadata locks taken by the statement after it completes. Test case added to show_check.test. --- mysql-test/r/show_check.result | 27 ++++++++++++++++++++ mysql-test/t/show_check.test | 46 ++++++++++++++++++++++++++++++++++ sql/sql_show.cc | 38 +++++++++++++++++----------- 3 files changed, 97 insertions(+), 14 deletions(-) diff --git a/mysql-test/r/show_check.result b/mysql-test/r/show_check.result index f49366cbf08..c1a75281e0e 100644 --- a/mysql-test/r/show_check.result +++ b/mysql-test/r/show_check.result @@ -1487,3 +1487,30 @@ UNLOCK TABLES; # Connection default COMMIT; DROP TABLE t1; +# +# Bug#55498 SHOW CREATE TRIGGER takes wrong type of metadata lock. +# +DROP TABLE IF EXISTS t1; +CREATE TABLE t1 (a INT); +CREATE TRIGGER t1_bi BEFORE INSERT ON t1 FOR EACH ROW SET new.a = 1; +# Test 1: SHOW CREATE TRIGGER with WRITE locked table. +# Connection con1 +LOCK TABLE t1 WRITE; +# Connection default +SHOW CREATE TRIGGER t1_bi; +Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation +t1_bi CREATE DEFINER=`root`@`localhost` TRIGGER t1_bi BEFORE INSERT ON t1 FOR EACH ROW SET new.a = 1 utf8 utf8_general_ci latin1_swedish_ci +# Connection con1 +UNLOCK TABLES; +# Test 2: ALTER TABLE with SHOW CREATE TRIGGER in transaction +# Connection default +START TRANSACTION; +SHOW CREATE TRIGGER t1_bi; +Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation +t1_bi CREATE DEFINER=`root`@`localhost` TRIGGER t1_bi BEFORE INSERT ON t1 FOR EACH ROW SET new.a = 1 utf8 utf8_general_ci latin1_swedish_ci +# Connection con1 +ALTER TABLE t1 CHARACTER SET = utf8; +# Connection default +COMMIT; +DROP TRIGGER t1_bi; +DROP TABLE t1; diff --git a/mysql-test/t/show_check.test b/mysql-test/t/show_check.test index 89a43f24296..0323f015e88 100644 --- a/mysql-test/t/show_check.test +++ b/mysql-test/t/show_check.test @@ -1279,6 +1279,52 @@ disconnect con1; DROP TABLE t1; +--echo # +--echo # Bug#55498 SHOW CREATE TRIGGER takes wrong type of metadata lock. +--echo # + +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +CREATE TABLE t1 (a INT); +CREATE TRIGGER t1_bi BEFORE INSERT ON t1 FOR EACH ROW SET new.a = 1; + +--echo # Test 1: SHOW CREATE TRIGGER with WRITE locked table. + +--echo # Connection con1 +connect (con1, localhost, root); +LOCK TABLE t1 WRITE; + +--echo # Connection default +connection default; +# Should not block. +SHOW CREATE TRIGGER t1_bi; + +--echo # Connection con1 +connection con1; +UNLOCK TABLES; + +--echo # Test 2: ALTER TABLE with SHOW CREATE TRIGGER in transaction + +--echo # Connection default +connection default; +START TRANSACTION; +SHOW CREATE TRIGGER t1_bi; + +--echo # Connection con1 +connection con1; +# Should not block. +ALTER TABLE t1 CHARACTER SET = utf8; + +--echo # Connection default +connection default; +COMMIT; +DROP TRIGGER t1_bi; +DROP TABLE t1; +disconnect con1; + + # Wait till all disconnects are completed --source include/wait_until_count_sessions.inc diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 8647237ce72..dc3784eb538 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -7779,6 +7779,10 @@ TABLE_LIST *get_trigger_table(THD *thd, const sp_name *trg_name) bool show_create_trigger(THD *thd, const sp_name *trg_name) { TABLE_LIST *lst= get_trigger_table(thd, trg_name); + uint num_tables; /* NOTE: unused, only to pass to open_tables(). */ + Table_triggers_list *triggers; + int trigger_idx; + bool error= TRUE; if (!lst) return TRUE; @@ -7790,35 +7794,35 @@ bool show_create_trigger(THD *thd, const sp_name *trg_name) } /* - Open the table by name in order to load Table_triggers_list object. - - NOTE: there is race condition here -- the table can be dropped after - LOCK_open is released. It will be fixed later by acquiring shared - metadata lock on trigger or table name. + Metadata locks taken during SHOW CREATE TRIGGER should be released when + the statement completes as it is an information statement. */ + MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint(); - uint num_tables; /* NOTE: unused, only to pass to open_tables(). */ - - if (open_tables(thd, &lst, &num_tables, 0)) + /* + Open the table by name in order to load Table_triggers_list object. + */ + if (open_tables(thd, &lst, &num_tables, + MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL)) { my_error(ER_TRG_CANT_OPEN_TABLE, MYF(0), (const char *) trg_name->m_db.str, (const char *) lst->table_name); - return TRUE; + goto exit; /* Perform closing actions and return error status. */ } - Table_triggers_list *triggers= lst->table->triggers; + triggers= lst->table->triggers; if (!triggers) { my_error(ER_TRG_DOES_NOT_EXIST, MYF(0)); - return TRUE; + goto exit; } - int trigger_idx= triggers->find_trigger_by_name(&trg_name->m_name); + trigger_idx= triggers->find_trigger_by_name(&trg_name->m_name); if (trigger_idx < 0) { @@ -7826,16 +7830,22 @@ bool show_create_trigger(THD *thd, const sp_name *trg_name) (const char *) trg_name->m_db.str, (const char *) lst->table_name); - return TRUE; + goto exit; } - return show_create_trigger_impl(thd, triggers, trigger_idx); + error= show_create_trigger_impl(thd, triggers, trigger_idx); /* NOTE: if show_create_trigger_impl() failed, that means we could not send data to the client. In this case we simply raise the error status and client connection will be closed. */ + +exit: + close_thread_tables(thd); + /* Release any metadata locks taken during SHOW CREATE TRIGGER. */ + thd->mdl_context.rollback_to_savepoint(mdl_savepoint); + return error; } class IS_internal_schema_access : public ACL_internal_schema_access From 6af3c0f94723c1c51c0185277966239c971b66d2 Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Fri, 23 Jul 2010 19:32:38 +0300 Subject: [PATCH 097/115] Increment InnoDB Plugin version to 1.0.11. InnoDB Plugin 1.0.10 has been released with MySQL 5.1.49. --- storage/innodb_plugin/include/univ.i | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/innodb_plugin/include/univ.i b/storage/innodb_plugin/include/univ.i index b8e595161b9..aa56c18e44e 100644 --- a/storage/innodb_plugin/include/univ.i +++ b/storage/innodb_plugin/include/univ.i @@ -46,7 +46,7 @@ Created 1/20/1994 Heikki Tuuri #define INNODB_VERSION_MAJOR 1 #define INNODB_VERSION_MINOR 0 -#define INNODB_VERSION_BUGFIX 10 +#define INNODB_VERSION_BUGFIX 11 /* The following is the InnoDB version as shown in SELECT plugin_version FROM information_schema.plugins; From f860873c0d1a16b85388f7d02c5bd2d201087513 Mon Sep 17 00:00:00 2001 From: Davi Arnaut Date: Fri, 23 Jul 2010 21:55:03 -0300 Subject: [PATCH 098/115] Bug#55501: Disable innodb plugin usage in the embedded server on certain OSes Do not attempt to test the innodb plugin with the embedded server, it's not supported for now. --- mysql-test/include/have_innodb_plugin.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/mysql-test/include/have_innodb_plugin.inc b/mysql-test/include/have_innodb_plugin.inc index 99a79465f52..df876deb2d7 100644 --- a/mysql-test/include/have_innodb_plugin.inc +++ b/mysql-test/include/have_innodb_plugin.inc @@ -1,3 +1,4 @@ +--source include/not_embedded.inc disable_query_log; --require r/true.require SELECT (plugin_library LIKE 'ha_innodb_plugin%') AS `TRUE` FROM information_schema.plugins WHERE LOWER(plugin_name) = 'innodb' AND LOWER(plugin_status) = 'active'; From e497d6e6e1a45ffdd235e965c54ba8354bb18b83 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Mon, 26 Jul 2010 09:06:18 +0400 Subject: [PATCH 099/115] Bug#45012 my_like_range_cp932 generates invalid string Problem: The functions my_like_range_xxx() returned badly formed maximum strings for Asian character sets, which made problems for storage engines. Fix: - Removed a number my_like_range_xxx() implementations, which were in fact dumplicate code pieces. - Using generic my_like_range_mb() instead. - Setting max_sort_char member properly for Asian character sets - Adding unittest/strings/strings-t.c, to test that my_like_range_xxx() return well-formed min and max strings. Notes: - No additional tests in mysql/t/ available. Old tests cover the affected code well enough. --- configure.in | 3 +- strings/ctype-big5.c | 86 +------------------------- strings/ctype-cp932.c | 76 +---------------------- strings/ctype-euc_kr.c | 4 +- strings/ctype-eucjpms.c | 4 +- strings/ctype-gb2312.c | 4 +- strings/ctype-gbk.c | 86 +------------------------- strings/ctype-mb.c | 22 +++++-- strings/ctype-sjis.c | 85 +------------------------- strings/ctype-ujis.c | 4 +- unittest/Makefile.am | 4 +- unittest/strings/Makefile.am | 27 +++++++++ unittest/strings/strings-t.c | 114 +++++++++++++++++++++++++++++++++++ 13 files changed, 181 insertions(+), 338 deletions(-) create mode 100644 unittest/strings/Makefile.am create mode 100644 unittest/strings/strings-t.c diff --git a/configure.in b/configure.in index 0af6a92fcc9..efd291f953c 100644 --- a/configure.in +++ b/configure.in @@ -2880,7 +2880,8 @@ fi AC_CONFIG_FILES(Makefile extra/Makefile mysys/Makefile dnl unittest/Makefile unittest/mytap/Makefile unittest/mytap/t/Makefile dnl - unittest/mysys/Makefile unittest/examples/Makefile dnl + unittest/mysys/Makefile unittest/strings/Makefile dnl + unittest/examples/Makefile dnl strings/Makefile regex/Makefile storage/Makefile dnl man/Makefile BUILD/Makefile vio/Makefile dnl libmysql/Makefile libmysql_r/Makefile client/Makefile dnl diff --git a/strings/ctype-big5.c b/strings/ctype-big5.c index 3da307b82fc..2cb40c266d2 100644 --- a/strings/ctype-big5.c +++ b/strings/ctype-big5.c @@ -377,86 +377,6 @@ static int my_strxfrm_big5(uchar *dest, const uchar *src, int len) #endif -/* -** Calculate min_str and max_str that ranges a LIKE string. -** Arguments: -** ptr Pointer to LIKE string. -** ptr_length Length of LIKE string. -** escape Escape character in LIKE. (Normally '\'). -** All escape characters should be removed from min_str and max_str -** res_length Length of min_str and max_str. -** min_str Smallest case sensitive string that ranges LIKE. -** Should be space padded to res_length. -** max_str Largest case sensitive string that ranges LIKE. -** Normally padded with the biggest character sort value. -** -** The function should return 0 if ok and 1 if the LIKE string can't be -** optimized ! -*/ - -#define max_sort_char ((char) 255) - -static my_bool my_like_range_big5(CHARSET_INFO *cs __attribute__((unused)), - const char *ptr,size_t ptr_length, - pbool escape, pbool w_one, pbool w_many, - size_t res_length, - char *min_str, char *max_str, - size_t *min_length, size_t *max_length) -{ - const char *end= ptr + ptr_length; - char *min_org=min_str; - char *min_end=min_str+res_length; - size_t charlen= res_length / cs->mbmaxlen; - - for (; ptr != end && min_str != min_end && charlen > 0; ptr++, charlen--) - { - if (ptr+1 != end && isbig5code(ptr[0],ptr[1])) - { - *min_str++= *max_str++ = *ptr++; - *min_str++= *max_str++ = *ptr; - continue; - } - if (*ptr == escape && ptr+1 != end) - { - ptr++; /* Skip escape */ - if (isbig5code(ptr[0], ptr[1])) - *min_str++= *max_str++ = *ptr++; - if (min_str < min_end) - *min_str++= *max_str++= *ptr; - continue; - } - if (*ptr == w_one) /* '_' in SQL */ - { - *min_str++='\0'; /* This should be min char */ - *max_str++=max_sort_char; - continue; - } - if (*ptr == w_many) /* '%' in SQL */ - { - /* - Calculate length of keys: - '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; - do { - *min_str++ = 0; - *max_str++ = max_sort_char; - } while (min_str != min_end); - return 0; - } - *min_str++= *max_str++ = *ptr; - } - - *min_length= *max_length= (size_t) (min_str-min_org); - while (min_str != min_end) - *min_str++= *max_str++= ' '; - return 0; -} - - static uint ismbchar_big5(CHARSET_INFO *cs __attribute__((unused)), const char* p, const char *e) { @@ -6338,7 +6258,7 @@ static MY_COLLATION_HANDLER my_collation_big5_chinese_ci_handler = my_strnncollsp_big5, my_strnxfrm_big5, my_strnxfrmlen_simple, - my_like_range_big5, + my_like_range_mb, my_wildcmp_mb, my_strcasecmp_mb, my_instr_mb, @@ -6402,7 +6322,7 @@ CHARSET_INFO my_charset_big5_chinese_ci= 1, /* mbminlen */ 2, /* mbmaxlen */ 0, /* min_sort_char */ - 255, /* max_sort_char */ + 0xF9D5, /* max_sort_char */ ' ', /* pad char */ 1, /* escape_with_backslash_is_dangerous */ &my_charset_big5_handler, @@ -6435,7 +6355,7 @@ CHARSET_INFO my_charset_big5_bin= 1, /* mbminlen */ 2, /* mbmaxlen */ 0, /* min_sort_char */ - 255, /* max_sort_char */ + 0xF9FE, /* max_sort_char */ ' ', /* pad char */ 1, /* escape_with_backslash_is_dangerous */ &my_charset_big5_handler, diff --git a/strings/ctype-cp932.c b/strings/ctype-cp932.c index 07191c436b7..238c6f61baa 100644 --- a/strings/ctype-cp932.c +++ b/strings/ctype-cp932.c @@ -306,76 +306,6 @@ static size_t my_strnxfrm_cp932(CHARSET_INFO *cs __attribute__((unused)), } -/* -** Calculate min_str and max_str that ranges a LIKE string. -** Arguments: -** ptr Pointer to LIKE string. -** ptr_length Length of LIKE string. -** escape Escape character in LIKE. (Normally '\'). -** All escape characters should be removed from min_str and max_str -** res_length Length of min_str and max_str. -** min_str Smallest case sensitive string that ranges LIKE. -** Should be space padded to res_length. -** max_str Largest case sensitive string that ranges LIKE. -** Normally padded with the biggest character sort value. -** -** The function should return 0 if ok and 1 if the LIKE string can't be -** optimized ! -*/ - -#define max_sort_char ((char) 255) - -static my_bool my_like_range_cp932(CHARSET_INFO *cs __attribute__((unused)), - const char *ptr,size_t ptr_length, - pbool escape, pbool w_one, pbool w_many, - size_t res_length, - char *min_str,char *max_str, - size_t *min_length, size_t *max_length) -{ - const char *end=ptr+ptr_length; - char *min_org=min_str; - char *min_end=min_str+res_length; - - while (ptr < end && min_str < min_end) { - if (ismbchar_cp932(cs, ptr, end)) { - *min_str++ = *max_str++ = *ptr++; - if (min_str < min_end) - *min_str++ = *max_str++ = *ptr++; - continue; - } - if (*ptr == escape && ptr+1 < end) { - ptr++; /* Skip escape */ - if (ismbchar_cp932(cs, ptr, end)) - *min_str++ = *max_str++ = *ptr++; - if (min_str < min_end) - *min_str++ = *max_str++ = *ptr++; - continue; - } - if (*ptr == w_one) { /* '_' in SQL */ - *min_str++ = '\0'; /* This should be min char */ - *max_str++ = max_sort_char; - ptr++; - continue; - } - if (*ptr == w_many) - { /* '%' in SQL */ - *min_length = (size_t)(min_str - min_org); - *max_length = res_length; - do - { - *min_str++= 0; - *max_str++= max_sort_char; - } while (min_str < min_end); - return 0; - } - *min_str++ = *max_str++ = *ptr++; - } - *min_length = *max_length = (size_t) (min_str - min_org); - while (min_str < min_end) - *min_str++ = *max_str++ = ' '; /* Because if key compression */ - return 0; -} - /* page 0 0x00A1-0x00DF */ static uint16 tab_cp932_uni0[]={ 0xFF61,0xFF62,0xFF63,0xFF64,0xFF65,0xFF66,0xFF67,0xFF68, @@ -5467,7 +5397,7 @@ static MY_COLLATION_HANDLER my_collation_ci_handler = my_strnncollsp_cp932, my_strnxfrm_cp932, my_strnxfrmlen_simple, - my_like_range_cp932, + my_like_range_mb, my_wildcmp_mb, /* wildcmp */ my_strcasecmp_8bit, my_instr_mb, @@ -5533,7 +5463,7 @@ CHARSET_INFO my_charset_cp932_japanese_ci= 1, /* mbminlen */ 2, /* mbmaxlen */ 0, /* min_sort_char */ - 255, /* max_sort_char */ + 0xFCFC, /* max_sort_char */ ' ', /* pad char */ 1, /* escape_with_backslash_is_dangerous */ &my_charset_handler, @@ -5565,7 +5495,7 @@ CHARSET_INFO my_charset_cp932_bin= 1, /* mbminlen */ 2, /* mbmaxlen */ 0, /* min_sort_char */ - 255, /* max_sort_char */ + 0xFCFC, /* max_sort_char */ ' ', /* pad char */ 1, /* escape_with_backslash_is_dangerous */ &my_charset_handler, diff --git a/strings/ctype-euc_kr.c b/strings/ctype-euc_kr.c index fc0af7e35d5..ee957304716 100644 --- a/strings/ctype-euc_kr.c +++ b/strings/ctype-euc_kr.c @@ -8762,7 +8762,7 @@ CHARSET_INFO my_charset_euckr_korean_ci= 1, /* mbminlen */ 2, /* mbmaxlen */ 0, /* min_sort_char */ - 255, /* max_sort_char */ + 0xFEFE, /* max_sort_char */ ' ', /* pad char */ 0, /* escape_with_backslash_is_dangerous */ &my_charset_handler, @@ -8795,7 +8795,7 @@ CHARSET_INFO my_charset_euckr_bin= 1, /* mbminlen */ 2, /* mbmaxlen */ 0, /* min_sort_char */ - 255, /* max_sort_char */ + 0xFEFE, /* max_sort_char */ ' ', /* pad char */ 0, /* escape_with_backslash_is_dangerous */ &my_charset_handler, diff --git a/strings/ctype-eucjpms.c b/strings/ctype-eucjpms.c index b8b04dfca6b..615981b4d27 100644 --- a/strings/ctype-eucjpms.c +++ b/strings/ctype-eucjpms.c @@ -8710,7 +8710,7 @@ CHARSET_INFO my_charset_eucjpms_japanese_ci= 1, /* mbminlen */ 3, /* mbmaxlen */ 0, /* min_sort_char */ - 255, /* max_sort_char */ + 0xFEFE, /* max_sort_char */ ' ', /* pad_char */ 0, /* escape_with_backslash_is_dangerous */ &my_charset_handler, @@ -8743,7 +8743,7 @@ CHARSET_INFO my_charset_eucjpms_bin= 1, /* mbminlen */ 3, /* mbmaxlen */ 0, /* min_sort_char */ - 255, /* max_sort_char */ + 0xFEFE, /* max_sort_char */ ' ', /* pad_char */ 0, /* escape_with_backslash_is_dangerous */ &my_charset_handler, diff --git a/strings/ctype-gb2312.c b/strings/ctype-gb2312.c index 0267f35ff5c..84f67dbbc2e 100644 --- a/strings/ctype-gb2312.c +++ b/strings/ctype-gb2312.c @@ -5790,7 +5790,7 @@ CHARSET_INFO my_charset_gb2312_chinese_ci= 1, /* mbminlen */ 2, /* mbmaxlen */ 0, /* min_sort_char */ - 255, /* max_sort_char */ + 0xF7FE, /* max_sort_char */ ' ', /* pad char */ 0, /* escape_with_backslash_is_dangerous */ &my_charset_handler, @@ -5822,7 +5822,7 @@ CHARSET_INFO my_charset_gb2312_bin= 1, /* mbminlen */ 2, /* mbmaxlen */ 0, /* min_sort_char */ - 255, /* max_sort_char */ + 0xF7FE, /* max_sort_char */ ' ', /* pad char */ 0, /* escape_with_backslash_is_dangerous */ &my_charset_handler, diff --git a/strings/ctype-gbk.c b/strings/ctype-gbk.c index 7b8bb85652b..89607823d34 100644 --- a/strings/ctype-gbk.c +++ b/strings/ctype-gbk.c @@ -2690,86 +2690,6 @@ static size_t my_strnxfrm_gbk(CHARSET_INFO *cs __attribute__((unused)), } -/* -** Calculate min_str and max_str that ranges a LIKE string. -** Arguments: -** ptr Pointer to LIKE string. -** ptr_length Length of LIKE string. -** escape Escape character in LIKE. (Normally '\'). -** All escape characters should be removed from min_str and max_str -** res_length Length of min_str and max_str. -** min_str Smallest case sensitive string that ranges LIKE. -** Should be space padded to res_length. -** max_str Largest case sensitive string that ranges LIKE. -** Normally padded with the biggest character sort value. -** -** The function should return 0 if ok and 1 if the LIKE string can't be -** optimized ! -*/ - -#define max_sort_char ((uchar) 255) - -static my_bool my_like_range_gbk(CHARSET_INFO *cs __attribute__((unused)), - const char *ptr,size_t ptr_length, - pbool escape, pbool w_one, pbool w_many, - size_t res_length, - char *min_str,char *max_str, - size_t *min_length,size_t *max_length) -{ - const char *end= ptr + ptr_length; - char *min_org=min_str; - char *min_end=min_str+res_length; - size_t charlen= res_length / cs->mbmaxlen; - - for (; ptr != end && min_str != min_end && charlen > 0; ptr++, charlen--) - { - if (ptr+1 != end && isgbkcode(ptr[0],ptr[1])) - { - *min_str++= *max_str++ = *ptr++; - *min_str++= *max_str++ = *ptr; - continue; - } - if (*ptr == escape && ptr+1 != end) - { - ptr++; /* Skip escape */ - if (isgbkcode(ptr[0], ptr[1])) - *min_str++= *max_str++ = *ptr; - if (min_str < min_end) - *min_str++= *max_str++= *ptr; - continue; - } - if (*ptr == w_one) /* '_' in SQL */ - { - *min_str++='\0'; /* This should be min char */ - *max_str++=max_sort_char; - continue; - } - if (*ptr == w_many) /* '%' in SQL */ - { - /* - Calculate length of keys: - '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; - do { - *min_str++= 0; - *max_str++= max_sort_char; - } while (min_str != min_end); - return 0; - } - *min_str++= *max_str++ = *ptr; - } - - *min_length= *max_length = (size_t) (min_str - min_org); - while (min_str != min_end) - *min_str++= *max_str++= ' '; /* Because if key compression */ - return 0; -} - - static uint ismbchar_gbk(CHARSET_INFO *cs __attribute__((unused)), const char* p, const char *e) { @@ -9983,7 +9903,7 @@ static MY_COLLATION_HANDLER my_collation_ci_handler = my_strnncollsp_gbk, my_strnxfrm_gbk, my_strnxfrmlen_simple, - my_like_range_gbk, + my_like_range_mb, my_wildcmp_mb, my_strcasecmp_mb, my_instr_mb, @@ -10048,7 +9968,7 @@ CHARSET_INFO my_charset_gbk_chinese_ci= 1, /* mbminlen */ 2, /* mbmaxlen */ 0, /* min_sort_char */ - 255, /* max_sort_char */ + 0xA967, /* max_sort_char */ ' ', /* pad char */ 1, /* escape_with_backslash_is_dangerous */ &my_charset_handler, @@ -10080,7 +10000,7 @@ CHARSET_INFO my_charset_gbk_bin= 1, /* mbminlen */ 2, /* mbmaxlen */ 0, /* min_sort_char */ - 255, /* max_sort_char */ + 0xFEFE, /* max_sort_char */ ' ', /* pad char */ 1, /* escape_with_backslash_is_dangerous */ &my_charset_handler, diff --git a/strings/ctype-mb.c b/strings/ctype-mb.c index 903811e2ab9..e3788fc4dff 100644 --- a/strings/ctype-mb.c +++ b/strings/ctype-mb.c @@ -498,7 +498,9 @@ static void my_hash_sort_mb_bin(CHARSET_INFO *cs __attribute__((unused)), DESCRIPTION Write max key: - for non-Unicode character sets: - just set to 255. + just bfill using max_sort_char if max_sort_char is one byte. + In case when max_sort_char is two bytes, fill with double-byte pairs + and optionally pad with a single space character. - for Unicode character set (utf-8): create a buffer with multibyte representation of the max_sort_char character, and copy it into max_str in a loop. @@ -510,12 +512,20 @@ static void pad_max_char(CHARSET_INFO *cs, char *str, char *end) if (!(cs->state & MY_CS_UNICODE)) { - bfill(str, end - str, 255); - return; + if (cs->max_sort_char <= 255) + { + bfill(str, end - str, cs->max_sort_char); + return; + } + buf[0]= cs->max_sort_char >> 8; + buf[1]= cs->max_sort_char & 0xFF; + buflen= 2; + } + else + { + buflen= cs->cset->wc_mb(cs, cs->max_sort_char, (uchar*) buf, + (uchar*) buf + sizeof(buf)); } - - buflen= cs->cset->wc_mb(cs, cs->max_sort_char, (uchar*) buf, - (uchar*) buf + sizeof(buf)); DBUG_ASSERT(buflen > 0); do diff --git a/strings/ctype-sjis.c b/strings/ctype-sjis.c index ac426e0d7b5..3f479ffc102 100644 --- a/strings/ctype-sjis.c +++ b/strings/ctype-sjis.c @@ -304,85 +304,6 @@ static size_t my_strnxfrm_sjis(CHARSET_INFO *cs __attribute__((unused)), } -/* -** Calculate min_str and max_str that ranges a LIKE string. -** Arguments: -** ptr Pointer to LIKE string. -** ptr_length Length of LIKE string. -** escape Escape character in LIKE. (Normally '\'). -** All escape characters should be removed from min_str and max_str -** res_length Length of min_str and max_str. -** min_str Smallest case sensitive string that ranges LIKE. -** Should be space padded to res_length. -** max_str Largest case sensitive string that ranges LIKE. -** Normally padded with the biggest character sort value. -** -** The function should return 0 if ok and 1 if the LIKE string can't be -** optimized ! -*/ - -#define max_sort_char ((char) 255) - -static my_bool my_like_range_sjis(CHARSET_INFO *cs __attribute__((unused)), - const char *ptr,size_t ptr_length, - pbool escape, pbool w_one, pbool w_many, - size_t res_length, - char *min_str,char *max_str, - size_t *min_length,size_t *max_length) -{ - const char *end= ptr + ptr_length; - char *min_org=min_str; - char *min_end=min_str+res_length; - size_t charlen= res_length / cs->mbmaxlen; - - for ( ; ptr < end && min_str < min_end && charlen > 0 ; charlen--) - { - if (ismbchar_sjis(cs, ptr, end)) { - *min_str++ = *max_str++ = *ptr++; - if (min_str < min_end) - *min_str++ = *max_str++ = *ptr++; - continue; - } - if (*ptr == escape && ptr+1 < end) { - ptr++; /* Skip escape */ - if (ismbchar_sjis(cs, ptr, end)) - *min_str++ = *max_str++ = *ptr++; - if (min_str < min_end) - *min_str++ = *max_str++ = *ptr++; - continue; - } - if (*ptr == w_one) { /* '_' in SQL */ - *min_str++ = '\0'; /* This should be min char */ - *max_str++ = max_sort_char; - ptr++; - continue; - } - if (*ptr == w_many) - { /* '%' in SQL */ - /* - Calculate length of keys: - '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; - do - { - *min_str++= 0; - *max_str++= max_sort_char; - } while (min_str < min_end); - return 0; - } - *min_str++ = *max_str++ = *ptr++; - } - - *min_length= *max_length= (size_t) (min_str - min_org); - while (min_str != min_end) - *min_str++= *max_str++= ' '; /* Because if key compression */ - return 0; -} - /* page 0 0x00A1-0x00DF */ static uint16 tab_sjis_uni0[]={ 0xFF61,0xFF62,0xFF63,0xFF64,0xFF65,0xFF66,0xFF67,0xFF68, @@ -4628,7 +4549,7 @@ static MY_COLLATION_HANDLER my_collation_ci_handler = my_strnncollsp_sjis, my_strnxfrm_sjis, my_strnxfrmlen_simple, - my_like_range_sjis, + my_like_range_mb, my_wildcmp_mb, /* wildcmp */ my_strcasecmp_8bit, my_instr_mb, @@ -4694,7 +4615,7 @@ CHARSET_INFO my_charset_sjis_japanese_ci= 1, /* mbminlen */ 2, /* mbmaxlen */ 0, /* min_sort_char */ - 255, /* max_sort_char */ + 0xFCFC, /* max_sort_char */ ' ', /* pad char */ 1, /* escape_with_backslash_is_dangerous */ &my_charset_handler, @@ -4726,7 +4647,7 @@ CHARSET_INFO my_charset_sjis_bin= 1, /* mbminlen */ 2, /* mbmaxlen */ 0, /* min_sort_char */ - 255, /* max_sort_char */ + 0xFCFC, /* max_sort_char */ ' ', /* pad char */ 1, /* escape_with_backslash_is_dangerous */ &my_charset_handler, diff --git a/strings/ctype-ujis.c b/strings/ctype-ujis.c index 1a872a92595..4474bd0cf96 100644 --- a/strings/ctype-ujis.c +++ b/strings/ctype-ujis.c @@ -8567,7 +8567,7 @@ CHARSET_INFO my_charset_ujis_japanese_ci= 1, /* mbminlen */ 3, /* mbmaxlen */ 0, /* min_sort_char */ - 255, /* max_sort_char */ + 0xFEFE, /* max_sort_char */ ' ', /* pad char */ 0, /* escape_with_backslash_is_dangerous */ &my_charset_handler, @@ -8600,7 +8600,7 @@ CHARSET_INFO my_charset_ujis_bin= 1, /* mbminlen */ 3, /* mbmaxlen */ 0, /* min_sort_char */ - 255, /* max_sort_char */ + 0xFEFE, /* max_sort_char */ ' ', /* pad char */ 0, /* escape_with_backslash_is_dangerous */ &my_charset_handler, diff --git a/unittest/Makefile.am b/unittest/Makefile.am index 65fa615fb98..889e029f6ef 100644 --- a/unittest/Makefile.am +++ b/unittest/Makefile.am @@ -13,12 +13,12 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -SUBDIRS = mytap . mysys examples +SUBDIRS = mytap . mysys examples strings EXTRA_DIST = unit.pl CLEANFILES = unit -unittests = mytap mysys @mysql_se_unittest_dirs@ @mysql_pg_unittest_dirs@ +unittests = mytap mysys strings @mysql_se_unittest_dirs@ @mysql_pg_unittest_dirs@ test: perl unit.pl run $(unittests) diff --git a/unittest/strings/Makefile.am b/unittest/strings/Makefile.am new file mode 100644 index 00000000000..5b18d89f58e --- /dev/null +++ b/unittest/strings/Makefile.am @@ -0,0 +1,27 @@ +# Copyright 2000, 2010, Oracle and/or its affiliates. All rights reserved. +# +# 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 Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +AM_CPPFLAGS = @ZLIB_INCLUDES@ -I$(top_builddir)/include +AM_CPPFLAGS += -I$(top_srcdir)/include -I$(top_srcdir)/unittest/mytap + +LDADD = $(top_builddir)/unittest/mytap/libmytap.a \ + $(top_builddir)/mysys/libmysys.a \ + $(top_builddir)/dbug/libdbug.a \ + $(top_builddir)/strings/libmystrings.a + +noinst_PROGRAMS = strings-t + +# Don't update the files from bitkeeper +%::SCCS/s.% diff --git a/unittest/strings/strings-t.c b/unittest/strings/strings-t.c new file mode 100644 index 00000000000..2d246cfa17f --- /dev/null +++ b/unittest/strings/strings-t.c @@ -0,0 +1,114 @@ +/* Copyright 2000, 2010, Oracle and/or its affiliates. All rights reserved. + + 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 Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include +#include +#include + + +/* + Test that like_range() returns well-formed results. +*/ +static int +test_like_range_for_charset(CHARSET_INFO *cs, const char *src, size_t src_len) +{ + char min_str[32], max_str[32]; + size_t min_len, max_len, min_well_formed_len, max_well_formed_len; + int error= 0; + + cs->coll->like_range(cs, src, src_len, '\\', '_', '%', + sizeof(min_str), min_str, max_str, &min_len, &max_len); + diag("min_len=%d\tmax_len=%d\t%s", min_len, max_len, cs->name); + min_well_formed_len= cs->cset->well_formed_len(cs, + min_str, min_str + min_len, + 10000, &error); + max_well_formed_len= cs->cset->well_formed_len(cs, + max_str, max_str + max_len, + 10000, &error); + if (min_len != min_well_formed_len) + diag("Bad min_str: min_well_formed_len=%d min_str[%d]=0x%02X", + min_well_formed_len, min_well_formed_len, + (uchar) min_str[min_well_formed_len]); + if (max_len != max_well_formed_len) + diag("Bad max_str: max_well_formed_len=%d max_str[%d]=0x%02X", + max_well_formed_len, max_well_formed_len, + (uchar) max_str[max_well_formed_len]); + return + min_len == min_well_formed_len && + max_len == max_well_formed_len ? 0 : 1; +} + + +static CHARSET_INFO *charset_list[]= +{ +#ifdef HAVE_CHARSET_big5 + &my_charset_big5_chinese_ci, + &my_charset_big5_bin, +#endif +#ifdef HAVE_CHARSET_euckr + &my_charset_euckr_korean_ci, + &my_charset_euckr_bin, +#endif +#ifdef HAVE_CHARSET_gb2312 + &my_charset_gb2312_chinese_ci, + &my_charset_gb2312_bin, +#endif +#ifdef HAVE_CHARSET_gbk + &my_charset_gbk_chinese_ci, + &my_charset_gbk_bin, +#endif +#ifdef HAVE_CHARSET_latin1 + &my_charset_latin1, + &my_charset_latin1_bin, +#endif +#ifdef HAVE_CHARSET_sjis + &my_charset_sjis_japanese_ci, + &my_charset_sjis_bin, +#endif +#ifdef HAVE_CHARSET_tis620 + &my_charset_tis620_thai_ci, + &my_charset_tis620_bin, +#endif +#ifdef HAVE_CHARSET_ujis + &my_charset_ujis_japanese_ci, + &my_charset_ujis_bin, +#endif +#ifdef HAVE_CHARSET_utf8 + &my_charset_utf8_general_ci, + &my_charset_utf8_unicode_ci, + &my_charset_utf8_bin, +#endif +}; + + +int main() +{ + size_t i, failed= 0; + + plan(1); + diag("Testing my_like_range_xxx() functions"); + + for (i= 0; i < array_elements(charset_list); i++) + { + CHARSET_INFO *cs= charset_list[i]; + if (test_like_range_for_charset(cs, "abc%", 4)) + { + ++failed; + diag("Failed for %s", cs->name); + } + } + ok(failed == 0, "Testing my_like_range_xxx() functions"); + return exit_status(); +} From c67cf159e928fe63582a4233d130dada0f866cb7 Mon Sep 17 00:00:00 2001 From: Dmitry Lenev Date: Mon, 26 Jul 2010 13:22:38 +0400 Subject: [PATCH 100/115] Test for bug #53820 "ALTER a MEDIUMINT column table causes full table copy". This patch only adds test case as the bug itself was addressed by Ramil's fix for bug 50946 "fast index creation still seems to copy the table". --- mysql-test/r/alter_table.result | 13 +++++++++++++ mysql-test/t/alter_table.test | 16 ++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/mysql-test/r/alter_table.result b/mysql-test/r/alter_table.result index 90dde034e10..304f562d47d 100644 --- a/mysql-test/r/alter_table.result +++ b/mysql-test/r/alter_table.result @@ -1370,3 +1370,16 @@ CREATE TABLE t1 (id int); INSERT INTO t1 VALUES (1), (2); ALTER TABLE t1 ADD COLUMN (f1 INT), ADD COLUMN (f2 INT), ADD KEY f2k(f2); DROP TABLE t1; +# +# Test for bug #53820 "ALTER a MEDIUMINT column table causes full +# table copy". +# +DROP TABLE IF EXISTS t1; +CREATE TABLE t1 (a INT, b MEDIUMINT); +INSERT INTO t1 VALUES (1, 1), (2, 2); +# The below ALTER should not copy table and so no rows should +# be shown as affected. +ALTER TABLE t1 CHANGE a id INT; +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +DROP TABLE t1; diff --git a/mysql-test/t/alter_table.test b/mysql-test/t/alter_table.test index d7f7a12cbf8..5b5fdf50c16 100644 --- a/mysql-test/t/alter_table.test +++ b/mysql-test/t/alter_table.test @@ -1128,3 +1128,19 @@ INSERT INTO t1 VALUES (1), (2); ALTER TABLE t1 ADD COLUMN (f1 INT), ADD COLUMN (f2 INT), ADD KEY f2k(f2); DROP TABLE t1; + +--echo # +--echo # Test for bug #53820 "ALTER a MEDIUMINT column table causes full +--echo # table copy". +--echo # +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings +CREATE TABLE t1 (a INT, b MEDIUMINT); +INSERT INTO t1 VALUES (1, 1), (2, 2); +--echo # The below ALTER should not copy table and so no rows should +--echo # be shown as affected. +--enable_info +ALTER TABLE t1 CHANGE a id INT; +--disable_info +DROP TABLE t1; From e168621d61ffe19b66c2428e60a5c64bf15804f1 Mon Sep 17 00:00:00 2001 From: Jimmy Yang Date: Mon, 26 Jul 2010 03:07:36 -0700 Subject: [PATCH 101/115] Fix Bug #55395 INNODB_TRX duplicates columns rb://408 approved by Sunny Bains --- .../innodb/r/innodb_information_schema.result | 16 +-- storage/innobase/handler/i_s.cc | 130 +----------------- 2 files changed, 3 insertions(+), 143 deletions(-) diff --git a/mysql-test/suite/innodb/r/innodb_information_schema.result b/mysql-test/suite/innodb/r/innodb_information_schema.result index 1737dab2ff0..13de084bc09 100644 --- a/mysql-test/suite/innodb/r/innodb_information_schema.result +++ b/mysql-test/suite/innodb/r/innodb_information_schema.result @@ -42,21 +42,7 @@ trx_isolation_level varchar(16) NO trx_unique_checks int(1) NO 0 trx_foreign_key_checks int(1) NO 0 trx_last_foreign_key_error varchar(256) YES NULL -trx_apative_hash_latched int(1) NO 0 -trx_adaptive_hash_timeout bigint(21) unsigned NO 0 -trx_operation_state varchar(64) YES NULL -trx_tables_in_use bigint(21) unsigned NO 0 -trx_tables_locked bigint(21) unsigned NO 0 -trx_lock_structs bigint(21) unsigned NO 0 -trx_lock_memory_bytes bigint(21) unsigned NO 0 -trx_rows_locked bigint(21) unsigned NO 0 -trx_rows_modified bigint(21) unsigned NO 0 -trx_concurrency_tickets bigint(21) unsigned NO 0 -trx_isolation_level varchar(16) NO -trx_unique_checks int(1) NO 0 -trx_foreign_key_checks int(1) NO 0 -trx_last_foreign_key_error varchar(256) YES NULL -trx_apative_hash_latched int(1) NO 0 +trx_adaptive_hash_latched int(1) NO 0 trx_adaptive_hash_timeout bigint(21) unsigned NO 0 trx_state trx_weight trx_tables_in_use trx_tables_locked trx_rows_locked trx_rows_modified trx_concurrency_tickets trx_isolation_level trx_unique_checks trx_foreign_key_checks RUNNING 4 0 0 7 1 0 REPEATABLE READ 1 1 diff --git a/storage/innobase/handler/i_s.cc b/storage/innobase/handler/i_s.cc index 06860c21b37..0733a558080 100644 --- a/storage/innobase/handler/i_s.cc +++ b/storage/innobase/handler/i_s.cc @@ -401,133 +401,7 @@ static ST_FIELD_INFO innodb_trx_fields_info[] = STRUCT_FLD(open_method, SKIP_OPEN_TABLE)}, #define IDX_TRX_ADAPTIVE_HASH_LATCHED 20 - {STRUCT_FLD(field_name, "trx_apative_hash_latched"), - STRUCT_FLD(field_length, 1), - STRUCT_FLD(field_type, MYSQL_TYPE_LONG), - STRUCT_FLD(value, 0), - STRUCT_FLD(field_flags, 0), - STRUCT_FLD(old_name, ""), - STRUCT_FLD(open_method, SKIP_OPEN_TABLE)}, - -#define IDX_TRX_ADAPTIVE_HASH_TIMEOUT 21 - {STRUCT_FLD(field_name, "trx_adaptive_hash_timeout"), - STRUCT_FLD(field_length, MY_INT64_NUM_DECIMAL_DIGITS), - STRUCT_FLD(field_type, MYSQL_TYPE_LONGLONG), - STRUCT_FLD(value, 0), - STRUCT_FLD(field_flags, MY_I_S_UNSIGNED), - STRUCT_FLD(old_name, ""), - STRUCT_FLD(open_method, SKIP_OPEN_TABLE)}, - -#define IDX_TRX_OPERATION_STATE 8 - {STRUCT_FLD(field_name, "trx_operation_state"), - STRUCT_FLD(field_length, TRX_I_S_TRX_OP_STATE_MAX_LEN), - STRUCT_FLD(field_type, MYSQL_TYPE_STRING), - STRUCT_FLD(value, 0), - STRUCT_FLD(field_flags, MY_I_S_MAYBE_NULL), - STRUCT_FLD(old_name, ""), - STRUCT_FLD(open_method, SKIP_OPEN_TABLE)}, - -#define IDX_TRX_TABLES_IN_USE 9 - {STRUCT_FLD(field_name, "trx_tables_in_use"), - STRUCT_FLD(field_length, MY_INT64_NUM_DECIMAL_DIGITS), - STRUCT_FLD(field_type, MYSQL_TYPE_LONGLONG), - STRUCT_FLD(value, 0), - STRUCT_FLD(field_flags, MY_I_S_UNSIGNED), - STRUCT_FLD(old_name, ""), - STRUCT_FLD(open_method, SKIP_OPEN_TABLE)}, - -#define IDX_TRX_TABLES_LOCKED 10 - {STRUCT_FLD(field_name, "trx_tables_locked"), - STRUCT_FLD(field_length, MY_INT64_NUM_DECIMAL_DIGITS), - STRUCT_FLD(field_type, MYSQL_TYPE_LONGLONG), - STRUCT_FLD(value, 0), - STRUCT_FLD(field_flags, MY_I_S_UNSIGNED), - STRUCT_FLD(old_name, ""), - STRUCT_FLD(open_method, SKIP_OPEN_TABLE)}, - -#define IDX_TRX_LOCK_STRUCTS 11 - {STRUCT_FLD(field_name, "trx_lock_structs"), - STRUCT_FLD(field_length, MY_INT64_NUM_DECIMAL_DIGITS), - STRUCT_FLD(field_type, MYSQL_TYPE_LONGLONG), - STRUCT_FLD(value, 0), - STRUCT_FLD(field_flags, MY_I_S_UNSIGNED), - STRUCT_FLD(old_name, ""), - STRUCT_FLD(open_method, SKIP_OPEN_TABLE)}, - -#define IDX_TRX_LOCK_MEMORY_BYTES 12 - {STRUCT_FLD(field_name, "trx_lock_memory_bytes"), - STRUCT_FLD(field_length, MY_INT64_NUM_DECIMAL_DIGITS), - STRUCT_FLD(field_type, MYSQL_TYPE_LONGLONG), - STRUCT_FLD(value, 0), - STRUCT_FLD(field_flags, MY_I_S_UNSIGNED), - STRUCT_FLD(old_name, ""), - STRUCT_FLD(open_method, SKIP_OPEN_TABLE)}, - -#define IDX_TRX_ROWS_LOCKED 13 - {STRUCT_FLD(field_name, "trx_rows_locked"), - STRUCT_FLD(field_length, MY_INT64_NUM_DECIMAL_DIGITS), - STRUCT_FLD(field_type, MYSQL_TYPE_LONGLONG), - STRUCT_FLD(value, 0), - STRUCT_FLD(field_flags, MY_I_S_UNSIGNED), - STRUCT_FLD(old_name, ""), - STRUCT_FLD(open_method, SKIP_OPEN_TABLE)}, - -#define IDX_TRX_ROWS_MODIFIED 14 - {STRUCT_FLD(field_name, "trx_rows_modified"), - STRUCT_FLD(field_length, MY_INT64_NUM_DECIMAL_DIGITS), - STRUCT_FLD(field_type, MYSQL_TYPE_LONGLONG), - STRUCT_FLD(value, 0), - STRUCT_FLD(field_flags, MY_I_S_UNSIGNED), - STRUCT_FLD(old_name, ""), - STRUCT_FLD(open_method, SKIP_OPEN_TABLE)}, - -#define IDX_TRX_CONNCURRENCY_TICKETS 15 - {STRUCT_FLD(field_name, "trx_concurrency_tickets"), - STRUCT_FLD(field_length, MY_INT64_NUM_DECIMAL_DIGITS), - STRUCT_FLD(field_type, MYSQL_TYPE_LONGLONG), - STRUCT_FLD(value, 0), - STRUCT_FLD(field_flags, MY_I_S_UNSIGNED), - STRUCT_FLD(old_name, ""), - STRUCT_FLD(open_method, SKIP_OPEN_TABLE)}, - -#define IDX_TRX_ISOLATION_LEVEL 16 - {STRUCT_FLD(field_name, "trx_isolation_level"), - STRUCT_FLD(field_length, TRX_I_S_TRX_ISOLATION_LEVEL_MAX_LEN), - STRUCT_FLD(field_type, MYSQL_TYPE_STRING), - STRUCT_FLD(value, 0), - STRUCT_FLD(field_flags, 0), - STRUCT_FLD(old_name, ""), - STRUCT_FLD(open_method, SKIP_OPEN_TABLE)}, - -#define IDX_TRX_UNIQUE_CHECKS 17 - {STRUCT_FLD(field_name, "trx_unique_checks"), - STRUCT_FLD(field_length, 1), - STRUCT_FLD(field_type, MYSQL_TYPE_LONG), - STRUCT_FLD(value, 1), - STRUCT_FLD(field_flags, 0), - STRUCT_FLD(old_name, ""), - STRUCT_FLD(open_method, SKIP_OPEN_TABLE)}, - -#define IDX_TRX_FOREIGN_KEY_CHECKS 18 - {STRUCT_FLD(field_name, "trx_foreign_key_checks"), - STRUCT_FLD(field_length, 1), - STRUCT_FLD(field_type, MYSQL_TYPE_LONG), - STRUCT_FLD(value, 1), - STRUCT_FLD(field_flags, 0), - STRUCT_FLD(old_name, ""), - STRUCT_FLD(open_method, SKIP_OPEN_TABLE)}, - -#define IDX_TRX_LAST_FOREIGN_KEY_ERROR 19 - {STRUCT_FLD(field_name, "trx_last_foreign_key_error"), - STRUCT_FLD(field_length, TRX_I_S_TRX_FK_ERROR_MAX_LEN), - STRUCT_FLD(field_type, MYSQL_TYPE_STRING), - STRUCT_FLD(value, 0), - STRUCT_FLD(field_flags, MY_I_S_MAYBE_NULL), - STRUCT_FLD(old_name, ""), - STRUCT_FLD(open_method, SKIP_OPEN_TABLE)}, - -#define IDX_TRX_ADAPTIVE_HASH_LATCHED 20 - {STRUCT_FLD(field_name, "trx_apative_hash_latched"), + {STRUCT_FLD(field_name, "trx_adaptive_hash_latched"), STRUCT_FLD(field_length, 1), STRUCT_FLD(field_type, MYSQL_TYPE_LONG), STRUCT_FLD(value, 0), @@ -674,7 +548,7 @@ fill_innodb_trx_from_cache( OK(field_store_string(fields[IDX_TRX_LAST_FOREIGN_KEY_ERROR], row->trx_foreign_key_error)); - /* trx_apative_hash_latched */ + /* trx_adaptive_hash_latched */ OK(fields[IDX_TRX_ADAPTIVE_HASH_LATCHED]->store( row->trx_has_search_latch)); From ed434ce045ac524bad4da0644c0bbff364ad757e Mon Sep 17 00:00:00 2001 From: Davi Arnaut Date: Mon, 26 Jul 2010 12:54:20 -0300 Subject: [PATCH 102/115] Bug#45377: ARCHIVE tables aren't discoverable after OPTIMIZE The problem was that the optimize method of the ARCHIVE storage engine was not preserving the FRM embedded in the ARZ file when rewriting the ARZ file for optimization. The ARCHIVE engine stores the FRM in the ARZ file so it can be transferred from machine to machine without also copying the FRM -- the engine restores the embedded FRM during discovery. The solution is to copy over the FRM when rewriting the ARZ file. In addition, some initial error checking is performed to ensure garbage is not copied over. --- mysql-test/r/archive.result | 26 ++++++++++++++++++++++++ mysql-test/t/archive.test | 21 ++++++++++++++++++++ storage/archive/azio.c | 22 ++++++++++----------- storage/archive/ha_archive.cc | 37 ++++++++++++++++++++++++++++++++++- storage/archive/ha_archive.h | 1 + 5 files changed, 95 insertions(+), 12 deletions(-) diff --git a/mysql-test/r/archive.result b/mysql-test/r/archive.result index 685e1e5ba4b..028c8b32f87 100644 --- a/mysql-test/r/archive.result +++ b/mysql-test/r/archive.result @@ -12775,3 +12775,29 @@ a 1 2 DROP TABLE t1; +# +# Bug#45377: ARCHIVE tables aren't discoverable after OPTIMIZE +# +DROP TABLE IF EXISTS t1; +CREATE TABLE t1 (a int) ENGINE=ARCHIVE; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) DEFAULT NULL +) ENGINE=ARCHIVE DEFAULT CHARSET=latin1 +INSERT INTO t1 VALUES (1); +OPTIMIZE TABLE t1; +Table Op Msg_type Msg_text +test.t1 optimize status OK +FLUSH TABLES; +INSERT INTO t1 VALUES (2); +SELECT * FROM t1 ORDER BY a; +a +1 +2 +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) DEFAULT NULL +) ENGINE=ARCHIVE DEFAULT CHARSET=latin1 +DROP TABLE t1; diff --git a/mysql-test/t/archive.test b/mysql-test/t/archive.test index a3665e5f455..c3a080612a9 100644 --- a/mysql-test/t/archive.test +++ b/mysql-test/t/archive.test @@ -1701,3 +1701,24 @@ SELECT * FROM t1; REPAIR TABLE t1 EXTENDED; SELECT * FROM t1; DROP TABLE t1; + + +--echo # +--echo # Bug#45377: ARCHIVE tables aren't discoverable after OPTIMIZE +--echo # + +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +CREATE TABLE t1 (a int) ENGINE=ARCHIVE; +SHOW CREATE TABLE t1; +INSERT INTO t1 VALUES (1); +OPTIMIZE TABLE t1; +let $MYSQLD_DATADIR= `select @@datadir`; +remove_file $MYSQLD_DATADIR/test/t1.frm; +FLUSH TABLES; +INSERT INTO t1 VALUES (2); +SELECT * FROM t1 ORDER BY a; +SHOW CREATE TABLE t1; +DROP TABLE t1; diff --git a/storage/archive/azio.c b/storage/archive/azio.c index c1dd6e6f38c..1e2753027dc 100644 --- a/storage/archive/azio.c +++ b/storage/archive/azio.c @@ -31,7 +31,7 @@ int az_open(azio_stream *s, const char *path, int Flags, File fd); int do_flush(azio_stream *file, int flush); int get_byte(azio_stream *s); void check_header(azio_stream *s); -void write_header(azio_stream *s); +int write_header(azio_stream *s); int destroy(azio_stream *s); void putLong(File file, uLong x); uLong getLong(azio_stream *s); @@ -155,7 +155,7 @@ int az_open (azio_stream *s, const char *path, int Flags, File fd) } -void write_header(azio_stream *s) +int write_header(azio_stream *s) { char buffer[AZHEADER_SIZE + AZMETA_BUFFER_SIZE]; char *ptr= buffer; @@ -191,8 +191,8 @@ void write_header(azio_stream *s) *(ptr + AZ_DIRTY_POS)= (unsigned char)s->dirty; /* Start of Data Block Index Block */ /* Always begin at the begining, and end there as well */ - my_pwrite(s->file, (uchar*) buffer, AZHEADER_SIZE + AZMETA_BUFFER_SIZE, 0, - MYF(0)); + return my_pwrite(s->file, (uchar*) buffer, AZHEADER_SIZE + AZMETA_BUFFER_SIZE, + 0, MYF(MY_NABP)) ? 1 : 0; } /* =========================================================================== @@ -838,19 +838,19 @@ int azwrite_frm(azio_stream *s, char *blob, unsigned int length) s->frm_length= length; s->start+= length; - my_pwrite(s->file, (uchar*) blob, s->frm_length, s->frm_start_pos, MYF(0)); - - write_header(s); - my_seek(s->file, 0, MY_SEEK_END, MYF(0)); + if (my_pwrite(s->file, (uchar*) blob, s->frm_length, + s->frm_start_pos, MYF(MY_NABP)) || + write_header(s) || + (my_seek(s->file, 0, MY_SEEK_END, MYF(0)) == MY_FILEPOS_ERROR)) + return 1; return 0; } int azread_frm(azio_stream *s, char *blob) { - my_pread(s->file, (uchar*) blob, s->frm_length, s->frm_start_pos, MYF(0)); - - return 0; + return my_pread(s->file, (uchar*) blob, s->frm_length, + s->frm_start_pos, MYF(MY_NABP)) ? 1 : 0; } diff --git a/storage/archive/ha_archive.cc b/storage/archive/ha_archive.cc index 63848370ff1..ef907b035b5 100644 --- a/storage/archive/ha_archive.cc +++ b/storage/archive/ha_archive.cc @@ -613,6 +613,34 @@ int ha_archive::close(void) } +/** + Copy a frm blob between streams. + + @param src The source stream. + @param dst The destination stream. + + @return Zero on success, non-zero otherwise. +*/ + +int ha_archive::frm_copy(azio_stream *src, azio_stream *dst) +{ + int rc= 0; + char *frm_ptr; + + if (!(frm_ptr= (char *) my_malloc(src->frm_length, MYF(0)))) + return HA_ERR_OUT_OF_MEM; + + /* Write file offset is set to the end of the file. */ + if (azread_frm(src, frm_ptr) || + azwrite_frm(dst, frm_ptr, src->frm_length)) + rc= my_errno ? my_errno : HA_ERR_INTERNAL_ERROR; + + my_free(frm_ptr); + + return rc; +} + + /* We create our data file here. The format is pretty simple. You can read about the format of the data file above. @@ -1345,10 +1373,10 @@ int ha_archive::repair(THD* thd, HA_CHECK_OPT* check_opt) */ int ha_archive::optimize(THD* thd, HA_CHECK_OPT* check_opt) { - DBUG_ENTER("ha_archive::optimize"); int rc= 0; azio_stream writer; char writer_filename[FN_REFLEN]; + DBUG_ENTER("ha_archive::optimize"); init_archive_reader(); @@ -1366,6 +1394,13 @@ int ha_archive::optimize(THD* thd, HA_CHECK_OPT* check_opt) if (!(azopen(&writer, writer_filename, O_CREAT|O_RDWR|O_BINARY))) DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE); + /* + Transfer the embedded FRM so that the file can be discoverable. + Write file offset is set to the end of the file. + */ + if ((rc= frm_copy(&archive, &writer))) + goto error; + /* An extended rebuild is a lot more effort. We open up each row and re-record it. Any dead rows are removed (aka rows that may have been partially recorded). diff --git a/storage/archive/ha_archive.h b/storage/archive/ha_archive.h index 94842203f16..b258b403c3c 100644 --- a/storage/archive/ha_archive.h +++ b/storage/archive/ha_archive.h @@ -75,6 +75,7 @@ class ha_archive: public handler archive_record_buffer *create_record_buffer(unsigned int length); void destroy_record_buffer(archive_record_buffer *r); + int frm_copy(azio_stream *src, azio_stream *dst); public: ha_archive(handlerton *hton, TABLE_SHARE *table_arg); From ec2c3bf2c1c27e4401c767a6cdcb3172453ff42c Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Tue, 27 Jul 2010 14:25:53 +0400 Subject: [PATCH 103/115] A pre-requisite patch for the fix for Bug#52044. This patch also fixes Bug#55452 "SET PASSWORD is replicated twice in RBR mode". The goal of this patch is to remove the release of metadata locks from close_thread_tables(). This is necessary to not mistakenly release the locks in the course of a multi-step operation that involves multiple close_thread_tables() or close_tables_for_reopen(). On the same token, move statement commit outside close_thread_tables(). Other cleanups: Cleanup COM_FIELD_LIST. Don't call close_thread_tables() in COM_SHUTDOWN -- there are no open tables there that can be closed (we leave the locked tables mode in THD destructor, and this close_thread_tables() won't leave it anyway). Make open_and_lock_tables() and open_and_lock_tables_derived() call close_thread_tables() upon failure. Remove the calls to close_thread_tables() that are now unnecessary. Simplify the back off condition in Open_table_context. Streamline metadata lock handling in LOCK TABLES implementation. Add asserts to ensure correct life cycle of statement transaction in a session. Remove a piece of dead code that has also become redundant after the fix for Bug 37521. --- mysql-test/r/variables.result | 22 ++ mysql-test/r/view.result | 6 +- .../r/rpl_row_implicit_commit_binlog.result | 4 - mysql-test/t/variables.test | 26 +++ sql/event_data_objects.cc | 18 +- sql/event_db_repository.cc | 74 +++--- sql/event_db_repository.h | 5 +- sql/events.cc | 14 +- sql/ha_ndbcluster.cc | 3 +- sql/ha_ndbcluster_binlog.cc | 19 +- sql/handler.cc | 4 +- sql/handler.h | 1 + sql/log.cc | 2 +- sql/log_event.cc | 60 ++--- sql/rpl_rli.cc | 16 ++ sql/set_var.cc | 1 - sql/slave.cc | 1 - sql/sp.cc | 16 +- sql/sp_head.cc | 49 +++- sql/sp_head.h | 2 + sql/sql_acl.cc | 46 ++-- sql/sql_base.cc | 155 +++++++------ sql/sql_base.h | 1 + sql/sql_class.cc | 7 - sql/sql_cursor.cc | 2 + sql/sql_do.cc | 5 +- sql/sql_handler.cc | 21 +- sql/sql_insert.cc | 34 +-- sql/sql_lex.cc | 3 + sql/sql_parse.cc | 214 ++++++++++-------- sql/sql_parse.h | 1 - sql/sql_partition.cc | 3 +- sql/sql_plugin.cc | 4 +- sql/sql_prepare.cc | 42 +--- sql/sql_priv.h | 10 + sql/sql_servers.cc | 17 +- sql/sql_table.cc | 65 +++--- sql/sql_trigger.cc | 27 +-- sql/sql_udf.cc | 4 +- sql/sys_vars.cc | 15 +- sql/transaction.cc | 27 +++ sql/tztime.cc | 12 +- 42 files changed, 590 insertions(+), 468 deletions(-) diff --git a/mysql-test/r/variables.result b/mysql-test/r/variables.result index b6ad1ff31bf..be81afe1a43 100644 --- a/mysql-test/r/variables.result +++ b/mysql-test/r/variables.result @@ -1677,3 +1677,25 @@ SET @@sql_quote_show_create = @sql_quote_show_create_saved; # End of Bug#34828. +# Make sure we can manipulate with autocommit in the +# along with other variables. +drop table if exists t1; +drop function if exists t1_max; +drop function if exists t1_min; +create table t1 (a int) engine=innodb; +insert into t1(a) values (0), (1); +create function t1_max() returns int return (select max(a) from t1); +create function t1_min() returns int return (select min(a) from t1); +select t1_min(); +t1_min() +0 +select t1_max(); +t1_max() +1 +set @@session.autocommit=t1_min(), @@session.autocommit=t1_max(), +@@session.autocommit=t1_min(), @@session.autocommit=t1_max(), +@@session.autocommit=t1_min(), @@session.autocommit=t1_max(); +# Cleanup. +drop table t1; +drop function t1_min; +drop function t1_max; diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index e1c1d6f4128..96b45f0d5bb 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -1955,15 +1955,15 @@ CHECK TABLE v1, v2, v3, v4, v5, v6; Table Op Msg_type Msg_text test.v1 check Error FUNCTION test.f1 does not exist test.v1 check Error View 'test.v1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them -test.v1 check status Operation failed +test.v1 check error Corrupt test.v2 check status OK test.v3 check Error FUNCTION test.f1 does not exist test.v3 check Error View 'test.v3' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them -test.v3 check status Operation failed +test.v3 check error Corrupt test.v4 check status OK test.v5 check Error FUNCTION test.f1 does not exist test.v5 check Error View 'test.v5' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them -test.v5 check status Operation failed +test.v5 check error Corrupt test.v6 check status OK create function f1 () returns int return (select max(col1) from t1); DROP TABLE t1; diff --git a/mysql-test/suite/rpl/r/rpl_row_implicit_commit_binlog.result b/mysql-test/suite/rpl/r/rpl_row_implicit_commit_binlog.result index 896ba90b865..459dc83e01d 100644 --- a/mysql-test/suite/rpl/r/rpl_row_implicit_commit_binlog.result +++ b/mysql-test/suite/rpl/r/rpl_row_implicit_commit_binlog.result @@ -165,10 +165,6 @@ master-bin.000001 # Table_map # # table_id: # (test.tt_1) master-bin.000001 # Write_rows # # table_id: # flags: STMT_END_F master-bin.000001 # Xid # # COMMIT /* XID */ master-bin.000001 # Query # # use `test`; SET PASSWORD FOR 'user'@'localhost'='*D8DECEC305209EEFEC43008E1D420E1AA06B19E0' -master-bin.000001 # Query # # BEGIN -master-bin.000001 # Table_map # # table_id: # (mysql.user) -master-bin.000001 # Update_rows # # table_id: # flags: STMT_END_F -master-bin.000001 # Query # # COMMIT -e-e-e-e-e-e-e-e-e-e-e- >> << -e-e-e-e-e-e-e-e-e-e-e- -b-b-b-b-b-b-b-b-b-b-b- >> << -b-b-b-b-b-b-b-b-b-b-b- diff --git a/mysql-test/t/variables.test b/mysql-test/t/variables.test index d865851841f..75099523062 100644 --- a/mysql-test/t/variables.test +++ b/mysql-test/t/variables.test @@ -1405,4 +1405,30 @@ SET @@sql_quote_show_create = @sql_quote_show_create_saved; --echo # End of Bug#34828. --echo +--echo # Make sure we can manipulate with autocommit in the +--echo # along with other variables. + + +--disable_warnings +drop table if exists t1; +drop function if exists t1_max; +drop function if exists t1_min; +--enable_warnings + +create table t1 (a int) engine=innodb; +insert into t1(a) values (0), (1); +create function t1_max() returns int return (select max(a) from t1); +create function t1_min() returns int return (select min(a) from t1); +select t1_min(); +select t1_max(); +set @@session.autocommit=t1_min(), @@session.autocommit=t1_max(), + @@session.autocommit=t1_min(), @@session.autocommit=t1_max(), + @@session.autocommit=t1_min(), @@session.autocommit=t1_max(); + +--echo # Cleanup. +drop table t1; +drop function t1_min; +drop function t1_max; + + ########################################################################### diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc index dd1845b29bc..52c509621ac 100644 --- a/sql/event_data_objects.cc +++ b/sql/event_data_objects.cc @@ -1402,6 +1402,8 @@ Event_job_data::execute(THD *thd, bool drop) */ thd->set_db(dbname.str, dbname.length); + lex_start(thd); + #ifndef NO_EMBEDDED_ACCESS_CHECKS if (event_sctx.change_security_context(thd, &definer_user, &definer_host, @@ -1411,7 +1413,7 @@ Event_job_data::execute(THD *thd, bool drop) "[%s].[%s.%s] execution failed, " "failed to authenticate the user.", definer.str, dbname.str, name.str); - goto end_no_lex_start; + goto end; } #endif @@ -1427,11 +1429,11 @@ Event_job_data::execute(THD *thd, bool drop) "[%s].[%s.%s] execution failed, " "user no longer has EVENT privilege.", definer.str, dbname.str, name.str); - goto end_no_lex_start; + goto end; } if (construct_sp_sql(thd, &sp_sql)) - goto end_no_lex_start; + goto end; /* Set up global thread attributes to reflect the properties of @@ -1451,8 +1453,6 @@ Event_job_data::execute(THD *thd, bool drop) if (parser_state.init(thd, thd->query(), thd->query_length())) goto end; - lex_start(thd); - if (parse_sql(thd, & parser_state, creation_ctx)) { sql_print_error("Event Scheduler: " @@ -1484,13 +1484,6 @@ Event_job_data::execute(THD *thd, bool drop) } end: - if (thd->lex->sphead) /* NULL only if a parse error */ - { - delete thd->lex->sphead; - thd->lex->sphead= NULL; - } - -end_no_lex_start: if (drop && !thd->is_fatal_error) { /* @@ -1529,7 +1522,6 @@ end_no_lex_start: if (save_sctx) event_sctx.restore_security_context(thd, save_sctx); #endif - lex_end(thd->lex); thd->lex->unit.cleanup(); thd->end_statement(); thd->cleanup_after_query(); diff --git a/sql/event_db_repository.cc b/sql/event_db_repository.cc index b7c01f10066..d47f1641bb0 100644 --- a/sql/event_db_repository.cc +++ b/sql/event_db_repository.cc @@ -518,17 +518,20 @@ Event_db_repository::table_scan_all_for_i_s(THD *thd, TABLE *schema_table, */ bool -Event_db_repository::fill_schema_events(THD *thd, TABLE_LIST *tables, +Event_db_repository::fill_schema_events(THD *thd, TABLE_LIST *i_s_table, const char *db) { - TABLE *schema_table= tables->table; - TABLE *event_table= NULL; + TABLE *schema_table= i_s_table->table; + Open_tables_backup open_tables_backup; + TABLE_LIST event_table; int ret= 0; DBUG_ENTER("Event_db_repository::fill_schema_events"); DBUG_PRINT("info",("db=%s", db? db:"(null)")); - if (open_event_table(thd, TL_READ, &event_table)) + event_table.init_one_table("mysql", 5, "event", 5, "event", TL_READ); + + if (open_system_tables_for_read(thd, &event_table, &open_tables_backup)) DBUG_RETURN(TRUE); /* @@ -541,11 +544,11 @@ Event_db_repository::fill_schema_events(THD *thd, TABLE_LIST *tables, every single row's `db` with the schema which we show. */ if (db) - ret= index_read_for_db_for_i_s(thd, schema_table, event_table, db); + ret= index_read_for_db_for_i_s(thd, schema_table, event_table.table, db); else - ret= table_scan_all_for_i_s(thd, schema_table, event_table); + ret= table_scan_all_for_i_s(thd, schema_table, event_table.table); - close_thread_tables(thd); + close_system_tables(thd, &open_tables_backup); DBUG_PRINT("info", ("Return code=%d", ret)); DBUG_RETURN(ret); @@ -584,10 +587,7 @@ Event_db_repository::open_event_table(THD *thd, enum thr_lock_type lock_type, tables.init_one_table("mysql", 5, "event", 5, "event", lock_type); if (open_and_lock_tables(thd, &tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT)) - { - close_thread_tables(thd); DBUG_RETURN(TRUE); - } *table= tables.table; tables.table->use_all_columns(); @@ -700,7 +700,8 @@ Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data, end: if (table) - close_thread_tables(thd); + close_mysql_tables(thd); + thd->variables.sql_mode= saved_mode; DBUG_RETURN(test(ret)); } @@ -811,7 +812,8 @@ Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data, end: if (table) - close_thread_tables(thd); + close_mysql_tables(thd); + thd->variables.sql_mode= saved_mode; DBUG_RETURN(test(ret)); } @@ -865,7 +867,7 @@ Event_db_repository::drop_event(THD *thd, LEX_STRING db, LEX_STRING name, end: if (table) - close_thread_tables(thd); + close_mysql_tables(thd); DBUG_RETURN(test(ret)); } @@ -933,34 +935,14 @@ Event_db_repository::find_named_event(LEX_STRING db, LEX_STRING name, void Event_db_repository::drop_schema_events(THD *thd, LEX_STRING schema) -{ - DBUG_ENTER("Event_db_repository::drop_schema_events"); - drop_events_by_field(thd, ET_FIELD_DB, schema); - DBUG_VOID_RETURN; -} - - -/** - Drops all events which have a specific value of a field. - - @pre The thread handle has no open tables. - - @param[in,out] thd Thread - @param[in,out] table mysql.event TABLE - @param[in] field Which field of the row to use for matching - @param[in] field_value The value that should match -*/ - -void -Event_db_repository::drop_events_by_field(THD *thd, - enum enum_events_table_field field, - LEX_STRING field_value) { int ret= 0; TABLE *table= NULL; READ_RECORD read_record_info; - DBUG_ENTER("Event_db_repository::drop_events_by_field"); - DBUG_PRINT("enter", ("field=%d field_value=%s", field, field_value.str)); + enum enum_events_table_field field= ET_FIELD_DB; + MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint(); + DBUG_ENTER("Event_db_repository::drop_schema_events"); + DBUG_PRINT("enter", ("field=%d schema=%s", field, schema.str)); if (open_event_table(thd, TL_WRITE, &table)) DBUG_VOID_RETURN; @@ -979,7 +961,7 @@ Event_db_repository::drop_events_by_field(THD *thd, get_field(thd->mem_root, table->field[ET_FIELD_NAME]))); - if (!sortcmp_lex_string(et_field_lex, field_value, system_charset_info)) + if (!sortcmp_lex_string(et_field_lex, schema, system_charset_info)) { DBUG_PRINT("info", ("Dropping")); if ((ret= table->file->ha_delete_row(table->record[0]))) @@ -989,6 +971,11 @@ Event_db_repository::drop_events_by_field(THD *thd, } end_read_record(&read_record_info); close_thread_tables(thd); + /* + Make sure to only release the MDL lock on mysql.event, not other + metadata locks DROP DATABASE might have acquired. + */ + thd->mdl_context.rollback_to_savepoint(mdl_savepoint); DBUG_VOID_RETURN; } @@ -1026,7 +1013,7 @@ Event_db_repository::load_named_event(THD *thd, LEX_STRING dbname, else if ((ret= etn->load_from_row(thd, table))) my_error(ER_CANNOT_LOAD_FROM_TABLE, MYF(0), "event"); - close_thread_tables(thd); + close_mysql_tables(thd); } thd->variables.sql_mode= saved_mode; @@ -1104,7 +1091,8 @@ update_timing_fields_for_event(THD *thd, end: if (table) - close_thread_tables(thd); + close_mysql_tables(thd); + /* Restore the state of binlog format */ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row()); if (save_binlog_row_based) @@ -1151,7 +1139,7 @@ Event_db_repository::check_system_tables(THD *thd) if (table_intact.check(tables.table, &mysql_db_table_def)) ret= 1; - close_thread_tables(thd); + close_mysql_tables(thd); } /* Check mysql.user */ tables.init_one_table("mysql", 5, "user", 4, "user", TL_READ); @@ -1171,7 +1159,7 @@ Event_db_repository::check_system_tables(THD *thd) event_priv_column_position); ret= 1; } - close_thread_tables(thd); + close_mysql_tables(thd); } /* Check mysql.event */ tables.init_one_table("mysql", 5, "event", 5, "event", TL_READ); @@ -1185,7 +1173,7 @@ Event_db_repository::check_system_tables(THD *thd) { if (table_intact.check(tables.table, &event_table_def)) ret= 1; - close_thread_tables(thd); + close_mysql_tables(thd); } DBUG_RETURN(test(ret)); diff --git a/sql/event_db_repository.h b/sql/event_db_repository.h index ef778407d1e..ea7f3bbac0e 100644 --- a/sql/event_db_repository.h +++ b/sql/event_db_repository.h @@ -91,7 +91,7 @@ public: bool load_named_event(THD *thd, LEX_STRING dbname, LEX_STRING name, Event_basic *et); - bool + static bool open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table); bool @@ -109,9 +109,6 @@ public: static bool check_system_tables(THD *thd); private: - void - drop_events_by_field(THD *thd, enum enum_events_table_field field, - LEX_STRING field_value); bool index_read_for_db_for_i_s(THD *thd, TABLE *schema_table, TABLE *event_table, const char *db); diff --git a/sql/events.cc b/sql/events.cc index a548bda53e2..5379ec2c9eb 100644 --- a/sql/events.cc +++ b/sql/events.cc @@ -16,7 +16,7 @@ #include "sql_priv.h" #include "unireg.h" #include "sql_parse.h" // check_access -#include "sql_base.h" // close_thread_tables +#include "sql_base.h" // close_mysql_tables #include "sql_show.h" // append_definer #include "events.h" #include "sql_db.h" // check_db_dir_existence @@ -754,7 +754,6 @@ Events::fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */) { char *db= NULL; int ret; - Open_tables_backup open_tables_backup; DBUG_ENTER("Events::fill_schema_events"); if (check_if_system_tables_error()) @@ -773,15 +772,7 @@ Events::fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */) DBUG_RETURN(1); db= thd->lex->select_lex.db; } - /* - Reset and backup of the currently open tables in this thread - is a way to allow SELECTs from INFORMATION_SCHEMA.events under - LOCK TABLES and in pre-locked mode. See also - Events::show_create_event for additional comments. - */ - thd->reset_n_backup_open_tables_state(&open_tables_backup); ret= db_repository->fill_schema_events(thd, tables, db); - thd->restore_backup_open_tables_state(&open_tables_backup); DBUG_RETURN(ret); } @@ -1161,8 +1152,7 @@ Events::load_events_from_db(THD *thd) end: end_read_record(&read_record_info); - close_thread_tables(thd); - + close_mysql_tables(thd); DBUG_RETURN(ret); } diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index ecf2984a4c0..7cac8373bc4 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -7417,7 +7417,8 @@ int ndbcluster_find_files(handlerton *hton, THD *thd, FALSE, /* drop_temporary */ FALSE, /* drop_view */ TRUE /* dont_log_query*/); - + trans_commit_implicit(thd); /* Safety, should be unnecessary. */ + thd->mdl_context.release_transactional_locks(); /* Clear error message that is returned when table is deleted */ thd->clear_error(); } diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc index 4f8bb66fcb0..b610687496e 100644 --- a/sql/ha_ndbcluster_binlog.cc +++ b/sql/ha_ndbcluster_binlog.cc @@ -298,13 +298,6 @@ static void run_query(THD *thd, char *buf, char *end, thd_ndb->m_error_code, (int) thd->is_error(), thd->is_slave_error); } - - /* - After executing statement we should unlock and close tables open - by it as well as release meta-data locks obtained by it. - */ - close_thread_tables(thd); - /* XXX: this code is broken. mysql_parse()/mysql_reset_thd_for_next_command() can not be called from within a statement, and @@ -2422,7 +2415,11 @@ int ndb_add_ndb_binlog_index(THD *thd, void *_row) } add_ndb_binlog_index_err: + thd->stmt_da->can_overwrite_status= TRUE; + thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd); + thd->stmt_da->can_overwrite_status= FALSE; close_thread_tables(thd); + thd->mdl_context.release_transactional_locks(); ndb_binlog_index= 0; thd->variables.option_bits= saved_options; return error; @@ -3969,7 +3966,9 @@ restart: { if (ndb_binlog_index->s->needs_reopen()) { + trans_commit_stmt(thd); close_thread_tables(thd); + thd->mdl_context.release_transactional_locks(); ndb_binlog_index= 0; } } @@ -4280,7 +4279,9 @@ restart: if (do_ndbcluster_binlog_close_connection == BCCC_restart) { ndb_binlog_tables_inited= FALSE; + trans_commit_stmt(thd); close_thread_tables(thd); + thd->mdl_context.release_transactional_locks(); ndb_binlog_index= 0; goto restart; } @@ -4288,7 +4289,11 @@ err: sql_print_information("Stopping Cluster Binlog"); DBUG_PRINT("info",("Shutting down cluster binlog thread")); thd->proc_info= "Shutting down"; + thd->stmt_da->can_overwrite_status= TRUE; + thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd); + thd->stmt_da->can_overwrite_status= FALSE; close_thread_tables(thd); + thd->mdl_context.release_transactional_locks(); mysql_mutex_lock(&injector_mutex); /* don't mess with the injector_ndb anymore from other threads */ injector_thd= 0; diff --git a/sql/handler.cc b/sql/handler.cc index b42840c7b1b..e67f5d6862c 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -1145,6 +1145,7 @@ int ha_commit_trans(THD *thd, bool all) if (thd->in_sub_stmt) { + DBUG_ASSERT(0); /* Since we don't support nested statement transactions in 5.0, we can't commit or rollback stmt transactions while we are inside @@ -1159,7 +1160,6 @@ int ha_commit_trans(THD *thd, bool all) bail out with error even before ha_commit_trans() call. To be 100% safe let us throw error in non-debug builds. */ - DBUG_ASSERT(0); my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0)); DBUG_RETURN(2); } @@ -1342,6 +1342,7 @@ int ha_rollback_trans(THD *thd, bool all) if (thd->in_sub_stmt) { + DBUG_ASSERT(0); /* If we are inside stored function or trigger we should not commit or rollback current statement transaction. See comment in ha_commit_trans() @@ -1349,7 +1350,6 @@ int ha_rollback_trans(THD *thd, bool all) */ if (!all) DBUG_RETURN(0); - DBUG_ASSERT(0); my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0)); DBUG_RETURN(1); } diff --git a/sql/handler.h b/sql/handler.h index 96095798d18..9f262542a4f 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -846,6 +846,7 @@ struct THD_TRANS bool modified_non_trans_table; void reset() { no_2pc= FALSE; modified_non_trans_table= FALSE; } + bool is_empty() const { return ha_list == NULL; } }; diff --git a/sql/log.cc b/sql/log.cc index b398cb1e73f..6eab16bc3a4 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -27,7 +27,7 @@ #include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_priv.h" #include "log.h" -#include "sql_base.h" // close_thread_tables +#include "sql_base.h" // open_log_table #include "sql_repl.h" #include "sql_delete.h" // mysql_truncate #include "sql_parse.h" // command_name diff --git a/sql/log_event.cc b/sql/log_event.cc index 5236a2794cf..8f70282ce79 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -3332,6 +3332,19 @@ int Query_log_event::do_apply_event(Relay_log_info const *rli, thd->table_map_for_update= (table_map)table_map_for_update; thd->set_invoker(&user, &host); + /* + Flag if we need to rollback the statement transaction on + slave if it by chance succeeds. + If we expected a non-zero error code and get nothing and, + it is a concurrency issue or ignorable issue, effects + of the statement should be rolled back. + */ + if (expected_error && + (ignored_error_code(expected_error) || + concurrency_error_code(expected_error))) + { + thd->variables.option_bits|= OPTION_MASTER_SQL_ERROR; + } /* Execute the query (note that we bypass dispatch_command()) */ Parser_state parser_state; if (!parser_state.init(thd, thd->query(), thd->query_length())) @@ -3340,6 +3353,8 @@ int Query_log_event::do_apply_event(Relay_log_info const *rli, log_slow_statement(thd); } + thd->variables.option_bits&= ~OPTION_MASTER_SQL_ERROR; + /* Resetting the enable_slow_log thd variable. @@ -3382,7 +3397,6 @@ START SLAVE; . Query: '%s'", expected_error, thd->query()); general_log_write(thd, COM_QUERY, thd->query(), thd->query_length()); compare_errors: - /* In the slave thread, we may sometimes execute some DROP / * 40005 TEMPORARY * / TABLE that come from parts of binlogs (likely if we @@ -3430,25 +3444,7 @@ Default database: '%s'. Query: '%s'", DBUG_PRINT("info",("error ignored")); clear_all_errors(thd, const_cast(rli)); thd->killed= THD::NOT_KILLED; - /* - When an error is expected and matches the actual error the - slave does not report any error and by consequence changes - on transactional tables are not rolled back in the function - close_thread_tables(). For that reason, we explicitly roll - them back here. - */ - if (expected_error && expected_error == actual_error) - trans_rollback_stmt(thd); } - /* - If we expected a non-zero error code and get nothing and, it is a concurrency - issue or should be ignored. - */ - else if (expected_error && !actual_error && - (concurrency_error_code(expected_error) || - ignored_error_code(expected_error))) - trans_rollback_stmt(thd); - /* Other cases: mostly we expected no error and get one. */ @@ -3516,7 +3512,6 @@ end: thd->set_db(NULL, 0); /* will free the current database */ thd->set_query(NULL, 0); DBUG_PRINT("info", ("end: query= 0")); - close_thread_tables(thd); /* As a disk space optimization, future masters will not log an event for LAST_INSERT_ID() if that function returned 0 (and thus they will be able @@ -4946,7 +4941,22 @@ error: thd->catalog= 0; thd->set_db(NULL, 0); /* will free the current database */ thd->set_query(NULL, 0); + thd->stmt_da->can_overwrite_status= TRUE; + thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd); + thd->stmt_da->can_overwrite_status= FALSE; close_thread_tables(thd); + /* + - If inside a multi-statement transaction, + defer the release of metadata locks until the current + transaction is either committed or rolled back. This prevents + other statements from modifying the table for the entire + duration of this transaction. This provides commit ordering + and guarantees serializability across multiple transactions. + - If in autocommit mode, or outside a transactional context, + automatically release metadata locks of the current statement. + */ + if (! thd->in_multi_stmt_transaction_mode()) + thd->mdl_context.release_transactional_locks(); DBUG_EXECUTE_IF("LOAD_DATA_INFILE_has_fatal_error", thd->is_slave_error= 0; thd->is_fatal_error= 1;); @@ -5531,11 +5541,9 @@ int Xid_log_event::do_apply_event(Relay_log_info const *rli) /* For a slave Xid_log_event is COMMIT */ general_log_print(thd, COM_QUERY, "COMMIT /* implicit, from Xid_log_event */"); - if (!(res= trans_commit(thd))) - { - close_thread_tables(thd); - thd->mdl_context.release_transactional_locks(); - } + res= trans_commit(thd); /* Automatically rolls back on error. */ + thd->mdl_context.release_transactional_locks(); + return res; } @@ -7610,8 +7618,6 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli) We should not honour --slave-skip-errors at this point as we are having severe errors which should not be skiped. */ - mysql_unlock_tables(thd, thd->lock); - thd->lock= 0; thd->is_slave_error= 1; const_cast(rli)->slave_close_thread_tables(thd); DBUG_RETURN(ERR_BAD_TABLE_DEF); diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc index 03c369394bf..af9b452acd8 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -1257,7 +1257,23 @@ void Relay_log_info::clear_tables_to_lock() void Relay_log_info::slave_close_thread_tables(THD *thd) { + thd->stmt_da->can_overwrite_status= TRUE; + thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd); + thd->stmt_da->can_overwrite_status= FALSE; + close_thread_tables(thd); + /* + - If inside a multi-statement transaction, + defer the release of metadata locks until the current + transaction is either committed or rolled back. This prevents + other statements from modifying the table for the entire + duration of this transaction. This provides commit ordering + and guarantees serializability across multiple transactions. + - If in autocommit mode, or outside a transactional context, + automatically release metadata locks of the current statement. + */ + if (! thd->in_multi_stmt_transaction_mode()) + thd->mdl_context.release_transactional_locks(); clear_tables_to_lock(); } #endif diff --git a/sql/set_var.cc b/sql/set_var.cc index ec9c09f02a3..9daaf883ea8 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -27,7 +27,6 @@ #include "mysqld.h" // lc_messages_dir #include "sys_vars_shared.h" #include "transaction.h" -#include "sql_base.h" // close_thread_tables #include "sql_locale.h" // my_locale_by_number, // my_locale_by_name #include "strfunc.h" // find_set_from_flags, find_set diff --git a/sql/slave.cc b/sql/slave.cc index d41d0479dde..a6199854e48 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -3044,7 +3044,6 @@ err: change_rpl_status(RPL_ACTIVE_SLAVE,RPL_IDLE_SLAVE); DBUG_ASSERT(thd->net.buff != 0); net_end(&thd->net); // destructor will not free it, because net.vio is 0 - close_thread_tables(thd); mysql_mutex_lock(&LOCK_thread_count); THD_CHECK_SENTRY(thd); delete thd; diff --git a/sql/sp.cc b/sql/sp.cc index 5328471f4c0..0265ef45a2a 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -450,10 +450,7 @@ static TABLE *open_proc_table_for_update(THD *thd) if (!proc_table_intact.check(table, &proc_table_def)) DBUG_RETURN(table); - close_thread_tables(thd); - DBUG_RETURN(NULL); - } @@ -856,6 +853,7 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, } end: + thd->lex->sphead= NULL; lex_end(thd->lex); thd->lex= old_lex; return ret; @@ -1159,8 +1157,6 @@ sp_create_routine(THD *thd, int type, sp_head *sp) done: thd->count_cuted_fields= saved_count_cuted_fields; thd->variables.sql_mode= saved_mode; - - close_thread_tables(thd); /* Restore the state of binlog format */ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row()); if (save_binlog_row_based) @@ -1239,8 +1235,6 @@ sp_drop_routine(THD *thd, int type, sp_name *name) sp_cache_flush_obsolete(spc, &sp); } } - - close_thread_tables(thd); /* Restore the state of binlog format */ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row()); if (save_binlog_row_based) @@ -1348,7 +1342,6 @@ sp_update_routine(THD *thd, int type, sp_name *name, st_sp_chistics *chistics) sp_cache_invalidate(); } err: - close_thread_tables(thd); /* Restore the state of binlog format */ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row()); if (save_binlog_row_based) @@ -1370,6 +1363,7 @@ sp_drop_db_routines(THD *thd, char *db) TABLE *table; int ret; uint key_len; + MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint(); DBUG_ENTER("sp_drop_db_routines"); DBUG_PRINT("enter", ("db: %s", db)); @@ -1410,6 +1404,11 @@ sp_drop_db_routines(THD *thd, char *db) table->file->ha_index_end(); close_thread_tables(thd); + /* + Make sure to only release the MDL lock on mysql.proc, not other + metadata locks DROP DATABASE might have acquired. + */ + thd->mdl_context.rollback_to_savepoint(mdl_savepoint); err: DBUG_RETURN(ret); @@ -2142,6 +2141,7 @@ sp_load_for_information_schema(THD *thd, TABLE *proc_table, String *db, newlex.current_select= NULL; sp= sp_compile(thd, &defstr, sql_mode, creation_ctx); *free_sp_head= 1; + thd->lex->sphead= NULL; lex_end(thd->lex); thd->lex= old_lex; return sp; diff --git a/sql/sp_head.cc b/sql/sp_head.cc index f75acf11984..11f138e67be 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -38,6 +38,7 @@ #include "set_var.h" #include "sql_parse.h" // cleanup_items #include "sql_base.h" // close_thread_tables +#include "transaction.h" // trans_commit_stmt /* Sufficient max length of printed destinations and frame offsets (all uints). @@ -795,6 +796,7 @@ sp_head::~sp_head() while ((lex= (LEX *)m_lex.pop())) { THD *thd= lex->thd; + thd->lex->sphead= NULL; lex_end(thd->lex); delete thd->lex; thd->lex= lex; @@ -1995,17 +1997,24 @@ sp_head::execute_procedure(THD *thd, List *args) arguments evaluation. If arguments evaluation required prelocking mode, we'll leave it here. */ + thd->lex->unit.cleanup(); + if (!thd->in_sub_stmt) { - thd->lex->unit.cleanup(); - - thd_proc_info(thd, "closing tables"); - close_thread_tables(thd); - thd_proc_info(thd, 0); - - thd->rollback_item_tree_changes(); + thd->stmt_da->can_overwrite_status= TRUE; + thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd); + thd->stmt_da->can_overwrite_status= FALSE; } + thd_proc_info(thd, "closing tables"); + close_thread_tables(thd); + thd_proc_info(thd, 0); + + if (! thd->in_sub_stmt && ! thd->in_multi_stmt_transaction_mode()) + thd->mdl_context.release_transactional_locks(); + + thd->rollback_item_tree_changes(); + DBUG_PRINT("info",(" %.*s: eval args done", (int) m_name.length, m_name.str)); } @@ -2197,6 +2206,7 @@ sp_head::restore_lex(THD *thd) merge_table_list(thd, sublex->query_tables, sublex); if (! sublex->sp_lex_in_use) { + sublex->sphead= NULL; lex_end(sublex); delete sublex; } @@ -2806,12 +2816,27 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp, DBUG_PRINT("info",("exec_core returned: %d", res)); } - m_lex->unit.cleanup(); + /* + Call after unit->cleanup() to close open table + key read. + */ + if (open_tables) + { + m_lex->unit.cleanup(); + /* Here we also commit or rollback the current statement. */ + if (! thd->in_sub_stmt) + { + thd->stmt_da->can_overwrite_status= TRUE; + thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd); + thd->stmt_da->can_overwrite_status= FALSE; + } + thd_proc_info(thd, "closing tables"); + close_thread_tables(thd); + thd_proc_info(thd, 0); - thd_proc_info(thd, "closing tables"); - /* Here we also commit or rollback the current statement. */ - close_thread_tables(thd); - thd_proc_info(thd, 0); + if (! thd->in_sub_stmt && ! thd->in_multi_stmt_transaction_mode()) + thd->mdl_context.release_transactional_locks(); + } if (m_lex->query_tables_own_last) { diff --git a/sql/sp_head.h b/sql/sp_head.h index 9796c49fdfb..b2446c8f680 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -682,6 +682,8 @@ public: { if (m_lex_resp) { + /* Prevent endless recursion. */ + m_lex->sphead= NULL; lex_end(m_lex); delete m_lex; } diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index d4c4f464884..0008968de2a 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -27,7 +27,7 @@ #include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_priv.h" #include "sql_acl.h" // MYSQL_DB_FIELD_COUNT, ACL_ACCESS -#include "sql_base.h" // close_thread_tables +#include "sql_base.h" // close_mysql_tables #include "key.h" // key_copy, key_cmp_if_same, key_restore #include "sql_show.h" // append_identifier #include "sql_table.h" // build_table_filename @@ -730,9 +730,7 @@ my_bool acl_reload(THD *thd) if (old_initialized) mysql_mutex_unlock(&acl_cache->lock); end: - trans_commit_implicit(thd); - close_thread_tables(thd); - thd->mdl_context.release_transactional_locks(); + close_mysql_tables(thd); DBUG_RETURN(return_val); } @@ -1585,6 +1583,7 @@ bool change_password(THD *thd, const char *host, const char *user, /* Buffer should be extended when password length is extended. */ char buff[512]; ulong query_length; + bool save_binlog_row_based; uint new_password_len= (uint) strlen(new_password); bool result= 1; DBUG_ENTER("change_password"); @@ -1614,10 +1613,17 @@ bool change_password(THD *thd, const char *host, const char *user, DBUG_RETURN(0); } #endif - if (!(table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT))) DBUG_RETURN(1); + /* + This statement will be replicated as a statement, even when using + row-based replication. The flag will be reset at the end of the + statement. + */ + if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row())) + thd->clear_current_stmt_binlog_format_row(); + mysql_mutex_lock(&acl_cache->lock); ACL_USER *acl_user; if (!(acl_user= find_acl_user(host, user, TRUE))) @@ -1652,7 +1658,13 @@ bool change_password(THD *thd, const char *host, const char *user, FALSE, FALSE, FALSE, 0); } end: - close_thread_tables(thd); + close_mysql_tables(thd); + + /* Restore the state of binlog format */ + DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row()); + if (save_binlog_row_based) + thd->set_current_stmt_binlog_format_row(); + DBUG_RETURN(result); } @@ -3082,7 +3094,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, DBUG_RETURN(TRUE); column_priv|= column->rights; } - close_thread_tables(thd); + close_mysql_tables(thd); } else { @@ -3172,7 +3184,6 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, thd->lex->sql_command= backup.sql_command; if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT)) { // Should never happen - close_thread_tables(thd); /* purecov: deadcode */ /* Restore the state of binlog format */ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row()); if (save_binlog_row_based) @@ -3398,7 +3409,6 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT)) { // Should never happen - close_thread_tables(thd); /* Restore the state of binlog format */ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row()); if (save_binlog_row_based) @@ -3553,7 +3563,6 @@ bool mysql_grant(THD *thd, const char *db, List &list, if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT)) { // This should never happen - close_thread_tables(thd); /* purecov: deadcode */ /* Restore the state of binlog format */ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row()); if (save_binlog_row_based) @@ -3613,7 +3622,6 @@ bool mysql_grant(THD *thd, const char *db, List &list, } mysql_rwlock_unlock(&LOCK_grant); - close_thread_tables(thd); if (!result) my_ok(thd); @@ -3874,10 +3882,7 @@ static my_bool grant_reload_procs_priv(THD *thd) table.open_type= OT_BASE_ONLY; if (open_and_lock_tables(thd, &table, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT)) - { - close_thread_tables(thd); DBUG_RETURN(TRUE); - } mysql_rwlock_wrlock(&LOCK_grant); /* Save a copy of the current hash if we need to undo the grant load */ @@ -3899,7 +3904,7 @@ static my_bool grant_reload_procs_priv(THD *thd) } mysql_rwlock_unlock(&LOCK_grant); - close_thread_tables(thd); + close_mysql_tables(thd); DBUG_RETURN(return_val); } @@ -3970,9 +3975,7 @@ my_bool grant_reload(THD *thd) free_root(&old_mem,MYF(0)); } mysql_rwlock_unlock(&LOCK_grant); - trans_commit_implicit(thd); - close_thread_tables(thd); - thd->mdl_context.release_transactional_locks(); + close_mysql_tables(thd); /* It is OK failing to load procs_priv table because we may be @@ -5250,7 +5253,6 @@ int open_grant_tables(THD *thd, TABLE_LIST *tables) if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT)) { // This should never happen - close_thread_tables(thd); DBUG_RETURN(-1); } @@ -5890,7 +5892,6 @@ bool mysql_create_user(THD *thd, List &list) result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length()); mysql_rwlock_unlock(&LOCK_grant); - close_thread_tables(thd); /* Restore the state of binlog format */ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row()); if (save_binlog_row_based) @@ -5975,7 +5976,6 @@ bool mysql_drop_user(THD *thd, List &list) result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length()); mysql_rwlock_unlock(&LOCK_grant); - close_thread_tables(thd); thd->variables.sql_mode= old_sql_mode; /* Restore the state of binlog format */ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row()); @@ -6072,7 +6072,6 @@ bool mysql_rename_user(THD *thd, List &list) result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length()); mysql_rwlock_unlock(&LOCK_grant); - close_thread_tables(thd); /* Restore the state of binlog format */ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row()); if (save_binlog_row_based) @@ -6270,8 +6269,6 @@ bool mysql_revoke_all(THD *thd, List &list) write_bin_log(thd, FALSE, thd->query(), thd->query_length()); mysql_rwlock_unlock(&LOCK_grant); - close_thread_tables(thd); - /* Restore the state of binlog format */ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row()); if (save_binlog_row_based) @@ -6418,7 +6415,6 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name, mysql_mutex_unlock(&acl_cache->lock); mysql_rwlock_unlock(&LOCK_grant); - close_thread_tables(thd); thd->pop_internal_handler(); /* Restore the state of binlog format */ diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 790898baae6..7a59fefdddd 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1402,6 +1402,9 @@ void close_thread_tables(THD *thd) DEBUG_SYNC(thd, "before_close_thread_tables"); #endif + DBUG_ASSERT(thd->transaction.stmt.is_empty() || thd->in_sub_stmt || + (thd->state_flags & Open_tables_state::BACKUPS_AVAIL)); + /* Detach MERGE children after every statement. Even under LOCK TABLES. */ for (table= thd->open_tables; table; table= table->next) { @@ -1446,28 +1449,6 @@ void close_thread_tables(THD *thd) Mark all temporary tables used by this statement as free for reuse. */ mark_temp_tables_as_free_for_reuse(thd); - /* - Let us commit transaction for statement. Since in 5.0 we only have - one statement transaction and don't allow several nested statement - transactions this call will do nothing if we are inside of stored - function or trigger (i.e. statement transaction is already active and - does not belong to statement for which we do close_thread_tables()). - TODO: This should be fixed in later releases. - */ - if (!(thd->state_flags & Open_tables_state::BACKUPS_AVAIL)) - { - thd->stmt_da->can_overwrite_status= TRUE; - thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd); - thd->stmt_da->can_overwrite_status= FALSE; - - /* - Reset transaction state, but only if we're not inside a - sub-statement of a prelocked statement. - */ - if (thd->locked_tables_mode <= LTM_LOCK_TABLES || - thd->lex->requires_prelocking()) - thd->transaction.stmt.reset(); - } if (thd->locked_tables_mode) { @@ -1528,26 +1509,6 @@ void close_thread_tables(THD *thd) if (thd->open_tables) close_open_tables(thd); - /* - - If inside a multi-statement transaction, - defer the release of metadata locks until the current - transaction is either committed or rolled back. This prevents - other statements from modifying the table for the entire - duration of this transaction. This provides commit ordering - and guarantees serializability across multiple transactions. - - If closing a system table, defer the release of metadata locks - to the caller. We have no sentinel in MDL subsystem to guard - transactional locks from system tables locks, so don't know - which locks are which here. - - If in autocommit mode, or outside a transactional context, - automatically release metadata locks of the current statement. - */ - if (! thd->in_multi_stmt_transaction_mode() && - ! (thd->state_flags & Open_tables_state::BACKUPS_AVAIL)) - { - thd->mdl_context.release_transactional_locks(); - } - DBUG_VOID_RETURN; } @@ -1562,7 +1523,14 @@ bool close_thread_table(THD *thd, TABLE **table_ptr) DBUG_ASSERT(table->key_read == 0); DBUG_ASSERT(!table->file || table->file->inited == handler::NONE); mysql_mutex_assert_not_owner(&LOCK_open); - + /* + The metadata lock must be released after giving back + the table to the table cache. + */ + DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, + table->s->db.str, + table->s->table_name.str, + MDL_SHARED)); table->mdl_ticket= NULL; mysql_mutex_lock(&thd->LOCK_thd_data); @@ -3188,6 +3156,7 @@ Locked_tables_list::init_locked_tables(THD *thd) return FALSE; } + /** Leave LTM_LOCK_TABLES mode if it's been entered. @@ -3224,7 +3193,12 @@ Locked_tables_list::unlock_locked_tables(THD *thd) } thd->leave_locked_tables_mode(); + DBUG_ASSERT(thd->transaction.stmt.is_empty()); close_thread_tables(thd); + /* + We rely on the caller to implicitly commit the + transaction and release transactional locks. + */ } /* After closing tables we can free memory used for storing lock @@ -3810,9 +3784,7 @@ Open_table_context::Open_table_context(THD *thd, uint flags) LONG_TIMEOUT : thd->variables.lock_wait_timeout), m_flags(flags), m_action(OT_NO_ACTION), - m_has_locks((thd->in_multi_stmt_transaction_mode() && - thd->mdl_context.has_locks()) || - thd->mdl_context.trans_sentinel()) + m_has_locks(thd->mdl_context.has_locks()) {} @@ -5264,6 +5236,8 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type, table= 0; end: + if (table == NULL) + close_thread_tables(thd); thd_proc_info(thd, 0); DBUG_RETURN(table); } @@ -5282,7 +5256,8 @@ end: should work for this statement. @note - The lock will automaticaly be freed by close_thread_tables() + The thr_lock locks will automatically be freed by + close_thread_tables(). @retval FALSE OK. @retval TRUE Error @@ -5293,11 +5268,12 @@ bool open_and_lock_tables(THD *thd, TABLE_LIST *tables, Prelocking_strategy *prelocking_strategy) { uint counter; + MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint(); DBUG_ENTER("open_and_lock_tables"); DBUG_PRINT("enter", ("derived handling: %d", derived)); if (open_tables(thd, &tables, &counter, flags, prelocking_strategy)) - DBUG_RETURN(TRUE); + goto err; DBUG_EXECUTE_IF("sleep_open_and_lock_after_open", { const char *old_proc_info= thd->proc_info; @@ -5306,15 +5282,22 @@ bool open_and_lock_tables(THD *thd, TABLE_LIST *tables, thd->proc_info= old_proc_info;}); if (lock_tables(thd, tables, counter, flags)) - DBUG_RETURN(TRUE); + goto err; if (derived && (mysql_handle_derived(thd->lex, &mysql_derived_prepare) || (thd->fill_derived_tables() && mysql_handle_derived(thd->lex, &mysql_derived_filling)))) - DBUG_RETURN(TRUE); /* purecov: inspected */ + goto err; DBUG_RETURN(FALSE); +err: + if (! thd->in_sub_stmt) + trans_rollback_stmt(thd); /* Necessary if derived handling failed. */ + close_thread_tables(thd); + /* Don't keep locks for a failed statement. */ + thd->mdl_context.rollback_to_savepoint(mdl_savepoint); + DBUG_RETURN(TRUE); } @@ -5340,13 +5323,24 @@ bool open_and_lock_tables(THD *thd, TABLE_LIST *tables, bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags) { + DML_prelocking_strategy prelocking_strategy; uint counter; + MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint(); DBUG_ENTER("open_normal_and_derived_tables"); DBUG_ASSERT(!thd->fill_derived_tables()); - if (open_tables(thd, &tables, &counter, flags) || + if (open_tables(thd, &tables, &counter, flags, &prelocking_strategy) || mysql_handle_derived(thd->lex, &mysql_derived_prepare)) - DBUG_RETURN(TRUE); /* purecov: inspected */ + goto end; + DBUG_RETURN(0); +end: + /* No need to rollback statement transaction, it's not started. */ + DBUG_ASSERT(thd->transaction.stmt.is_empty()); + close_thread_tables(thd); + /* Don't keep locks for a failed statement. */ + thd->mdl_context.rollback_to_savepoint(mdl_savepoint); + + DBUG_RETURN(TRUE); /* purecov: inspected */ } @@ -5607,6 +5601,14 @@ void close_tables_for_reopen(THD *thd, TABLE_LIST **tables, /* We have to cleanup translation tables of views. */ tmp->cleanup_items(); } + /* + No need to commit/rollback the statement transaction: it's + either not started or we're filling in an INFORMATION_SCHEMA + table on the fly, and thus mustn't manipulate with the + transaction of the enclosing statement. + */ + DBUG_ASSERT(thd->transaction.stmt.is_empty() || + (thd->state_flags & Open_tables_state::BACKUPS_AVAIL)); close_thread_tables(thd); thd->mdl_context.rollback_to_savepoint(start_of_statement_svp); } @@ -9034,7 +9036,8 @@ open_system_tables_for_read(THD *thd, TABLE_LIST *table_list, MYSQL_LOCK_IGNORE_TIMEOUT)) { lex->restore_backup_query_tables_list(&query_tables_list_backup); - goto error; + thd->restore_backup_open_tables_state(backup); + DBUG_RETURN(TRUE); } for (TABLE_LIST *tables= table_list; tables; tables= tables->next_global) @@ -9045,11 +9048,6 @@ open_system_tables_for_read(THD *thd, TABLE_LIST *table_list, lex->restore_backup_query_tables_list(&query_tables_list_backup); DBUG_RETURN(FALSE); - -error: - close_system_tables(thd, backup); - - DBUG_RETURN(TRUE); } @@ -9072,6 +9070,38 @@ close_system_tables(THD *thd, Open_tables_backup *backup) } +/** + A helper function to close a mysql.* table opened + in an auxiliary THD during bootstrap or in the main + connection, when we know that there are no locks + held by the connection due to a preceding implicit + commit. + + This function assumes that there is no + statement transaction started for the operation + itself, since mysql.* tables are not transactional + and when they are used the binlog is off (DDL + binlogging is always statement-based. + + We need this function since we'd like to not + just close the system table, but also release + the metadata lock on it. + + Note, that in LOCK TABLES mode this function + does not release the metadata lock. But in this + mode the table can be opened only if it is locked + explicitly with LOCK TABLES. +*/ + +void +close_mysql_tables(THD *thd) +{ + /* No need to commit/rollback statement transaction, it's not started. */ + DBUG_ASSERT(thd->transaction.stmt.is_empty()); + close_thread_tables(thd); + thd->mdl_context.release_transactional_locks(); +} + /* Open and lock one system table for update. @@ -9143,16 +9173,7 @@ open_log_table(THD *thd, TABLE_LIST *one_table, Open_tables_backup *backup) table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; } else - { - /* - If error in mysql_lock_tables(), open_ltable doesn't close the - table. Thread kill during mysql_lock_tables() is such error. But - open tables cannot be accepted when restoring the open tables - state. - */ - close_thread_tables(thd); thd->restore_backup_open_tables_state(backup); - } thd->utime_after_lock= save_utime_after_lock; DBUG_RETURN(table); diff --git a/sql/sql_base.h b/sql/sql_base.h index 24669bd2597..b912f80d44f 100644 --- a/sql/sql_base.h +++ b/sql/sql_base.h @@ -240,6 +240,7 @@ bool is_equal(const LEX_STRING *a, const LEX_STRING *b); bool open_system_tables_for_read(THD *thd, TABLE_LIST *table_list, Open_tables_backup *backup); void close_system_tables(THD *thd, Open_tables_backup *backup); +void close_mysql_tables(THD *thd); TABLE *open_system_table_for_update(THD *thd, TABLE_LIST *one_table); TABLE *open_log_table(THD *thd, TABLE_LIST *one_table, Open_tables_backup *backup); void close_log_table(THD *thd, Open_tables_backup *backup); diff --git a/sql/sql_class.cc b/sql/sql_class.cc index eb4d353db81..f3ef440c2f0 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -29,7 +29,6 @@ #include "sql_priv.h" #include "unireg.h" // REQUIRED: for other includes #include "sql_class.h" -#include "lock.h" // unlock_global_read_lock, mysql_unlock_tables #include "sql_cache.h" // query_cache_abort #include "sql_base.h" // close_thread_tables #include "sql_time.h" // date_time_format_copy @@ -1817,12 +1816,6 @@ bool select_send::send_eof() */ ha_release_temporary_latches(thd); - /* Unlock tables before sending packet to gain some speed */ - if (thd->lock && ! thd->locked_tables_mode) - { - mysql_unlock_tables(thd, thd->lock); - thd->lock=0; - } /* Don't send EOF if we're in error condition (which implies we've already sent or are sending an error) diff --git a/sql/sql_cursor.cc b/sql/sql_cursor.cc index ca724ec262f..28d2bd8b4de 100644 --- a/sql/sql_cursor.cc +++ b/sql/sql_cursor.cc @@ -22,6 +22,7 @@ #include "sql_select.h" #include "probes_mysql.h" #include "sql_parse.h" // mysql_execute_command +#include "sql_base.h" /**************************************************************************** Declarations. @@ -523,6 +524,7 @@ Sensitive_cursor::close() thd->derived_tables= derived_tables; thd->lock= lock; + close_thread_tables(thd); /* Is expected to at least close tables and empty thd->change_list */ stmt_arena->cleanup_stmt(); diff --git a/sql/sql_do.cc b/sql/sql_do.cc index 79e488ac2a5..085473c24b8 100644 --- a/sql/sql_do.cc +++ b/sql/sql_do.cc @@ -39,9 +39,10 @@ bool mysql_do(THD *thd, List &values) /* Rollback the effect of the statement, since next instruction will clear the error and the rollback in the end of - dispatch_command() won't work. + mysql_execute_command() won't work. */ - trans_rollback_stmt(thd); + if (! thd->in_sub_stmt) + trans_rollback_stmt(thd); thd->clear_error(); // DO always is OK } my_ok(thd); diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 8e38cf5eed0..f1dddbb2eb5 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -59,7 +59,7 @@ #include "key.h" // key_copy #include "sql_base.h" // insert_fields #include "sql_select.h" -#include +#include "transaction.h" #define HANDLER_TABLES_HASH_SIZE 120 @@ -309,9 +309,15 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) } if (error) { + /* + No need to rollback statement transaction, it's not started. + If called with reopen flag, no need to rollback either, + it will be done at statement end. + */ + DBUG_ASSERT(thd->transaction.stmt.is_empty()); close_thread_tables(thd); - thd->set_open_tables(backup_open_tables); thd->mdl_context.rollback_to_savepoint(mdl_savepoint); + thd->set_open_tables(backup_open_tables); if (!reopen) my_hash_delete(&thd->handler_tables_hash, (uchar*) hash_tables); else @@ -578,6 +584,11 @@ retry: if (sql_handler_lock_error.need_reopen()) { DBUG_ASSERT(!lock && !thd->is_error()); + /* + Always close statement transaction explicitly, + so that the engine doesn't have to count locks. + */ + trans_rollback_stmt(thd); mysql_ha_close_table(thd, hash_tables); goto retry; } @@ -764,12 +775,18 @@ retry: num_rows++; } ok: + /* + Always close statement transaction explicitly, + so that the engine doesn't have to count locks. + */ + trans_commit_stmt(thd); mysql_unlock_tables(thd,lock); my_eof(thd); DBUG_PRINT("exit",("OK")); DBUG_RETURN(FALSE); err: + trans_rollback_stmt(thd); mysql_unlock_tables(thd,lock); err0: DBUG_PRINT("exit",("ERROR")); diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index c783d74b363..cc0b6ba3a2d 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1862,7 +1862,10 @@ public: while ((row=rows.get())) delete row; if (table) + { close_thread_tables(&thd); + thd.mdl_context.release_transactional_locks(); + } mysql_mutex_lock(&LOCK_thread_count); mysql_mutex_destroy(&mutex); mysql_cond_destroy(&cond); @@ -2414,6 +2417,8 @@ bool Delayed_insert::open_and_lock_table() } if (!(table->file->ha_table_flags() & HA_CAN_INSERT_DELAYED)) { + /* To rollback InnoDB statement transaction. */ + trans_rollback_stmt(&thd); my_error(ER_DELAYED_NOT_SUPPORTED, MYF(ME_FATALERROR), table_list.table_name); return TRUE; @@ -2480,12 +2485,6 @@ pthread_handler_t handle_delayed_insert(void *arg) goto err; } - /* - Open table requires an initialized lex in case the table is - partitioned. The .frm file contains a partial SQL string which is - parsed using a lex, that depends on initialized thd->lex. - */ - lex_start(thd); thd->lex->sql_command= SQLCOM_INSERT; // For innodb::store_lock() /* Statement-based replication of INSERT DELAYED has problems with RAND() @@ -2619,28 +2618,11 @@ pthread_handler_t handle_delayed_insert(void *arg) } err: - /* - mysql_lock_tables() can potentially start a transaction and write - a table map. In the event of an error, that transaction has to be - rolled back. We only need to roll back a potential statement - transaction, since real transactions are rolled back in - close_thread_tables(). - - TODO: This is not true any more, table maps are generated on the - first call to ha_*_row() instead. Remove code that are used to - cover for the case outlined above. - */ - trans_rollback_stmt(thd); - DBUG_LEAVE; } - /* - di should be unlinked from the thread handler list and have no active - clients - */ - close_thread_tables(thd); // Free the table + thd->mdl_context.release_transactional_locks(); di->table=0; thd->killed= THD::KILL_CONNECTION; // If error mysql_cond_broadcast(&di->cond_client); // Safety @@ -2648,6 +2630,10 @@ pthread_handler_t handle_delayed_insert(void *arg) mysql_mutex_lock(&LOCK_delayed_create); // Because of delayed_get_table mysql_mutex_lock(&LOCK_delayed_insert); + /* + di should be unlinked from the thread handler list and have no active + clients + */ delete di; mysql_mutex_unlock(&LOCK_delayed_insert); mysql_mutex_unlock(&LOCK_delayed_create); diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index aefddc0b6a5..eef139d5698 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -450,6 +450,9 @@ void lex_end(LEX *lex) } reset_dynamic(&lex->plugins); + delete lex->sphead; + lex->sphead= NULL; + DBUG_VOID_RETURN; } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 8a94aea8aa7..53c2ca6fa39 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -115,6 +115,7 @@ "FUNCTION" : "PROCEDURE") static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables); +static void sql_kill(THD *thd, ulong id, bool only_kill_query); const char *any_db="*any*"; // Special symbol for check_access @@ -413,6 +414,9 @@ void init_update_queries(void) sql_command_flags[SQLCOM_FLUSH]= CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_RESET]= CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_CHECK]= CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_CREATE_SERVER]= CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_ALTER_SERVER]= CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_DROP_SERVER]= CF_AUTO_COMMIT_TRANS; } bool sqlcom_can_generate_row_events(const THD *thd) @@ -568,7 +572,6 @@ static void handle_bootstrap_impl(THD *thd) } mysql_parse(thd, thd->query(), length, &parser_state); - close_thread_tables(thd); // Free tables bootstrap_error= thd->is_error(); thd->protocol->end_statement(); @@ -1139,13 +1142,11 @@ bool dispatch_command(enum enum_server_command command, THD *thd, { char *beginning_of_next_stmt= (char*) parser_state.m_lip.found_semicolon; - - thd->protocol->end_statement(); - query_cache_end_of_result(thd); /* Multiple queries exits, execute them individually */ - close_thread_tables(thd); + thd->protocol->end_statement(); + query_cache_end_of_result(thd); ulong length= (ulong)(packet_end - beginning_of_next_stmt); log_slow_statement(thd); @@ -1197,38 +1198,54 @@ bool dispatch_command(enum enum_server_command command, THD *thd, char *fields, *packet_end= packet + packet_length, *arg_end; /* Locked closure of all tables */ TABLE_LIST table_list; - LEX_STRING conv_name; - - /* used as fields initializator */ - lex_start(thd); + LEX_STRING table_name; + LEX_STRING db; + /* + SHOW statements should not add the used tables to the list of tables + used in a transaction. + */ + MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint(); status_var_increment(thd->status_var.com_stat[SQLCOM_SHOW_FIELDS]); - bzero((char*) &table_list,sizeof(table_list)); - if (thd->copy_db_to(&table_list.db, &table_list.db_length)) + if (thd->copy_db_to(&db.str, &db.length)) break; /* We have name + wildcard in packet, separated by endzero */ arg_end= strend(packet); uint arg_length= arg_end - packet; - + /* Check given table name length. */ if (arg_length >= packet_length || arg_length > NAME_LEN) { my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0)); break; } - thd->convert_string(&conv_name, system_charset_info, + thd->convert_string(&table_name, system_charset_info, packet, arg_length, thd->charset()); - if (check_table_name(conv_name.str, conv_name.length, FALSE)) + if (check_table_name(table_name.str, table_name.length, FALSE)) { /* this is OK due to convert_string() null-terminating the string */ - my_error(ER_WRONG_TABLE_NAME, MYF(0), conv_name.str); + my_error(ER_WRONG_TABLE_NAME, MYF(0), table_name.str); break; } - - table_list.alias= table_list.table_name= conv_name.str; packet= arg_end + 1; + mysql_reset_thd_for_next_command(thd); + lex_start(thd); + /* Must be before we init the table list. */ + if (lower_case_table_names) + table_name.length= my_casedn_str(files_charset_info, table_name.str); + table_list.init_one_table(db.str, db.length, table_name.str, + table_name.length, table_name.str, TL_READ); + /* + Init TABLE_LIST members necessary when the undelrying + table is view. + */ + table_list.select_lex= &(thd->lex->select_lex); + thd->lex-> + select_lex.table_list.link_in_list(&table_list, + &table_list.next_local); + thd->lex->add_to_query_tables(&table_list); if (is_infoschema_db(table_list.db, table_list.db_length)) { @@ -1242,32 +1259,23 @@ bool dispatch_command(enum enum_server_command command, THD *thd, break; thd->set_query(fields, query_length); general_log_print(thd, command, "%s %s", table_list.table_name, fields); - if (lower_case_table_names) - my_casedn_str(files_charset_info, table_list.table_name); - if (check_access(thd, SELECT_ACL, table_list.db, - &table_list.grant.privilege, - &table_list.grant.m_internal, - 0, 0)) + if (check_table_access(thd, SELECT_ACL, &table_list, + TRUE, UINT_MAX, FALSE)) break; - if (check_grant(thd, SELECT_ACL, &table_list, TRUE, UINT_MAX, FALSE)) - break; - /* init structures for VIEW processing */ - table_list.select_lex= &(thd->lex->select_lex); - - lex_start(thd); - mysql_reset_thd_for_next_command(thd); - - thd->lex-> - select_lex.table_list.link_in_list(&table_list, - &table_list.next_local); - thd->lex->add_to_query_tables(&table_list); - init_mdl_requests(&table_list); - - /* switch on VIEW optimisation: do not fill temporary tables */ + /* + Turn on an optimization relevant if the underlying table + is a view: do not fill derived tables. + */ thd->lex->sql_command= SQLCOM_SHOW_FIELDS; + mysqld_list_fields(thd,&table_list,fields); thd->lex->unit.cleanup(); + /* No need to rollback statement transaction, it's not started. */ + DBUG_ASSERT(thd->transaction.stmt.is_empty()); + close_thread_tables(thd); + thd->mdl_context.rollback_to_savepoint(mdl_savepoint); + thd->cleanup_after_query(); break; } @@ -1315,7 +1323,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd, ulong options= (ulong) (uchar) packet[0]; if (trans_commit_implicit(thd)) break; - close_thread_tables(thd); thd->mdl_context.release_transactional_locks(); if (check_global_access(thd,RELOAD_ACL)) break; @@ -1377,7 +1384,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd, DBUG_PRINT("quit",("Got shutdown command for level %u", level)); general_log_print(thd, command, NullS); my_eof(thd); - close_thread_tables(thd); // Free before kill kill_mysql(); error=TRUE; break; @@ -1480,29 +1486,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd, my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0)); break; } - - /* report error issued during command execution */ - if (thd->killed_errno()) - { - if (! thd->stmt_da->is_set()) - thd->send_kill_message(); - } - if (thd->killed == THD::KILL_QUERY || thd->killed == THD::KILL_BAD_DATA) - { - thd->killed= THD::NOT_KILLED; - thd->mysys_var->abort= 0; - } - - /* If commit fails, we should be able to reset the OK status. */ - thd->stmt_da->can_overwrite_status= TRUE; - thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd); - thd->stmt_da->can_overwrite_status= FALSE; - - thd->transaction.stmt.reset(); - - thd->proc_info= "closing tables"; - /* Free tables */ - close_thread_tables(thd); + DBUG_ASSERT(thd->derived_tables == NULL && + (thd->open_tables == NULL || + (thd->locked_tables_mode == LTM_LOCK_TABLES))); thd->protocol->end_statement(); query_cache_end_of_result(thd); @@ -1715,6 +1701,9 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident, In brief: take exclusive locks, expel tables from the table cache, reopen the tables, enter the 'LOCKED TABLES' mode, downgrade the locks. + Note: the function is written to be called from + mysql_execute_command(), it is not reusable in arbitrary + execution context. Required privileges ------------------- @@ -1816,9 +1805,9 @@ static bool flush_tables_with_read_lock(THD *thd, TABLE_LIST *all_tables) &lock_tables_prelocking_strategy) || thd->locked_tables_list.init_locked_tables(thd)) { - close_thread_tables(thd); goto error; } + thd->variables.option_bits|= OPTION_TABLE_LOCK; /* Downgrade the exclusive locks. @@ -2041,6 +2030,7 @@ mysql_execute_command(THD *thd) thd->work_part_info= 0; #endif + DBUG_ASSERT(thd->transaction.stmt.is_empty() || thd->in_sub_stmt); /* In many cases first table of main SELECT_LEX have special meaning => check that it is first table in global list and relink it first in @@ -2222,8 +2212,7 @@ mysql_execute_command(THD *thd) /* Commit the normal transaction if one is active. */ if (trans_commit_implicit(thd)) goto error; - /* Close tables and release metadata locks. */ - close_thread_tables(thd); + /* Release metadata locks acquired in this transaction. */ thd->mdl_context.release_transactional_locks(); } @@ -3536,24 +3525,27 @@ end_with_restore_list: done FLUSH TABLES WITH READ LOCK + BEGIN. If this assumption becomes false, mysqldump will not work. */ - thd->locked_tables_list.unlock_locked_tables(thd); if (thd->variables.option_bits & OPTION_TABLE_LOCK) { - trans_commit_implicit(thd); + res= trans_commit_implicit(thd); + thd->locked_tables_list.unlock_locked_tables(thd); thd->mdl_context.release_transactional_locks(); thd->variables.option_bits&= ~(OPTION_TABLE_LOCK); } if (thd->global_read_lock.is_acquired()) thd->global_read_lock.unlock_global_read_lock(thd); + if (res) + goto error; my_ok(thd); break; case SQLCOM_LOCK_TABLES: + /* We must end the transaction first, regardless of anything */ + res= trans_commit_implicit(thd); thd->locked_tables_list.unlock_locked_tables(thd); - /* we must end the trasaction first, regardless of anything */ - if (trans_commit_implicit(thd)) - goto error; - /* release transactional metadata locks. */ + /* Release transactional metadata locks. */ thd->mdl_context.release_transactional_locks(); + if (res) + goto error; if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE)) goto error; @@ -3576,17 +3568,14 @@ end_with_restore_list: if (res) { + trans_rollback_stmt(thd); /* Need to end the current transaction, so the storage engine (InnoDB) can free its locks if LOCK TABLES locked some tables before finding that it can't lock a table in its list */ - trans_rollback_stmt(thd); trans_commit_implicit(thd); - /* - Close tables and release metadata locks otherwise a later call to - close_thread_tables might not release the locks if autocommit is off. - */ + /* Close tables and release metadata locks. */ close_thread_tables(thd); DBUG_ASSERT(!thd->locked_tables_mode); thd->mdl_context.release_transactional_locks(); @@ -4205,9 +4194,7 @@ end_with_restore_list: locks in the MDL context, so there is no risk to deadlock. */ - trans_commit_implicit(thd); - close_thread_tables(thd); - thd->mdl_context.release_transactional_locks(); + close_mysql_tables(thd); /* Check if the definer exists on slave, then use definer privilege to insert routine privileges to mysql.procs_priv. @@ -4484,9 +4471,7 @@ create_sp_error: locks in the MDL context, so there is no risk to deadlock. */ - trans_commit_implicit(thd); - close_thread_tables(thd); - thd->mdl_context.release_transactional_locks(); + close_mysql_tables(thd); if (sp_automatic_privileges && !opt_noacl && sp_revoke_privileges(thd, db, name, @@ -4778,17 +4763,60 @@ finish: DBUG_ASSERT(!thd->in_active_multi_stmt_transaction() || thd->in_multi_stmt_transaction_mode()); + + if (! thd->in_sub_stmt) + { + /* report error issued during command execution */ + if (thd->killed_errno()) + { + if (! thd->stmt_da->is_set()) + thd->send_kill_message(); + } + if (thd->killed == THD::KILL_QUERY || thd->killed == THD::KILL_BAD_DATA) + { + thd->killed= THD::NOT_KILLED; + thd->mysys_var->abort= 0; + } + if (thd->is_error() || (thd->variables.option_bits & OPTION_MASTER_SQL_ERROR)) + trans_rollback_stmt(thd); + else + { + /* If commit fails, we should be able to reset the OK status. */ + thd->stmt_da->can_overwrite_status= TRUE; + trans_commit_stmt(thd); + thd->stmt_da->can_overwrite_status= FALSE; + } + } + + lex->unit.cleanup(); + /* Free tables */ + thd_proc_info(thd, "closing tables"); + close_thread_tables(thd); + thd_proc_info(thd, 0); + if (stmt_causes_implicit_commit(thd, CF_IMPLICIT_COMMIT_END)) { + /* No transaction control allowed in sub-statements. */ + DBUG_ASSERT(! thd->in_sub_stmt); /* If commit fails, we should be able to reset the OK status. */ thd->stmt_da->can_overwrite_status= TRUE; - /* Commit or rollback the statement transaction. */ - thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd); /* Commit the normal transaction if one is active. */ trans_commit_implicit(thd); thd->stmt_da->can_overwrite_status= FALSE; - /* Close tables and release metadata locks. */ - close_thread_tables(thd); + thd->mdl_context.release_transactional_locks(); + } + else if (! thd->in_sub_stmt && ! thd->in_multi_stmt_transaction_mode()) + { + /* + - If inside a multi-statement transaction, + defer the release of metadata locks until the current + transaction is either committed or rolled back. This prevents + other statements from modifying the table for the entire + duration of this transaction. This provides commit ordering + and guarantees serializability across multiple transactions. + - If in autocommit mode, or outside a transactional context, + automatically release metadata locks of the current statement. + */ thd->mdl_context.release_transactional_locks(); } @@ -5886,12 +5914,6 @@ void mysql_parse(THD *thd, const char *inBuf, uint length, query_cache_abort(&thd->query_cache_tls); } - if (thd->lex->sphead) - { - delete thd->lex->sphead; - thd->lex->sphead= 0; - } - lex->unit.cleanup(); thd_proc_info(thd, "freeing items"); thd->end_statement(); thd->cleanup_after_query(); @@ -6997,11 +7019,15 @@ uint kill_one_thread(THD *thd, ulong id, bool only_kill_query) only_kill_query Should it kill the query or the connection */ +static void sql_kill(THD *thd, ulong id, bool only_kill_query) { uint error; if (!(error= kill_one_thread(thd, id, only_kill_query))) - my_ok(thd); + { + if (! thd->killed) + my_ok(thd); + } else my_error(error, MYF(0), id); } diff --git a/sql/sql_parse.h b/sql/sql_parse.h index 475811d45b7..7304836ed0f 100644 --- a/sql/sql_parse.h +++ b/sql/sql_parse.h @@ -51,7 +51,6 @@ bool parse_sql(THD *thd, Object_creation_ctx *creation_ctx); uint kill_one_thread(THD *thd, ulong id, bool only_kill_query); -void sql_kill(THD *thd, ulong id, bool only_kill_query); void free_items(Item *item); void cleanup_items(Item *item); diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index d7ff753dfd0..2c8b5d67d04 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -59,7 +59,7 @@ #include "my_md5.h" #include "transaction.h" -#include "sql_base.h" // close_thread_tables +#include "sql_base.h" // close_all_tables_for_name #include "sql_table.h" // build_table_filename, // build_table_shadow_filename, // table_to_filename @@ -6758,7 +6758,6 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, table_list, FALSE, NULL, written_bin_log)); err: - close_thread_tables(thd); DBUG_RETURN(TRUE); } #endif diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index 2b6be403fc6..27cd03d8edd 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -21,7 +21,7 @@ #include "sql_locale.h" #include "sql_plugin.h" #include "sql_parse.h" // check_table_access -#include "sql_base.h" // close_thread_tables +#include "sql_base.h" // close_mysql_tables #include "key.h" // key_copy #include "sql_show.h" // remove_status_vars, add_status_vars #include "strfunc.h" // find_set @@ -1511,8 +1511,8 @@ static void plugin_load(MEM_ROOT *tmp_root, int *argc, char **argv) sql_print_error(ER(ER_GET_ERRNO), my_errno); end_read_record(&read_record_info); table->m_needs_reopen= TRUE; // Force close to free memory + close_mysql_tables(new_thd); end: - close_thread_tables(new_thd); /* Remember that we don't have a THD */ my_pthread_setspecific_ptr(THR_THD, 0); DBUG_VOID_RETURN; diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 089e751900e..151a135125e 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -90,7 +90,7 @@ When one supplies long data for a placeholder: #include "set_var.h" #include "sql_prepare.h" #include "sql_parse.h" // insert_precheck, update_precheck, delete_precheck -#include "sql_base.h" // close_thread_tables +#include "sql_base.h" // open_normal_and_derived_tables #include "sql_cache.h" // query_cache_* #include "sql_view.h" // create_view_precheck #include "sql_delete.h" // mysql_prepare_delete @@ -2989,12 +2989,6 @@ Execute_sql_statement::execute_server_code(THD *thd) error= mysql_execute_command(thd); - if (thd->killed_errno()) - { - if (! thd->stmt_da->is_set()) - thd->send_kill_message(); - } - /* report error issued during command execution */ if (error == 0 && thd->spcont == NULL) general_log_write(thd, COM_STMT_EXECUTE, @@ -3102,13 +3096,8 @@ void Prepared_statement::cleanup_stmt() DBUG_ENTER("Prepared_statement::cleanup_stmt"); DBUG_PRINT("enter",("stmt: 0x%lx", (long) this)); - delete lex->sphead; - lex->sphead= 0; - /* The order is important */ - lex->unit.cleanup(); cleanup_items(free_list); thd->cleanup_after_query(); - close_thread_tables(thd); thd->rollback_item_tree_changes(); DBUG_VOID_RETURN; @@ -3272,21 +3261,16 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) to PREPARE stmt FROM "CREATE PROCEDURE ..." */ DBUG_ASSERT(lex->sphead == NULL || error != 0); - if (lex->sphead) - { - delete lex->sphead; - lex->sphead= NULL; - } + /* The order is important */ + lex->unit.cleanup(); + /* No need to commit statement transaction, it's not started. */ + DBUG_ASSERT(thd->transaction.stmt.is_empty()); + + close_thread_tables(thd); + thd->mdl_context.rollback_to_savepoint(mdl_savepoint); lex_end(lex); cleanup_stmt(); - /* - If not inside a multi-statement transaction, the metadata - locks have already been released and our savepoint points - to ticket which has been released as well. - */ - if (thd->in_multi_stmt_transaction_mode()) - thd->mdl_context.rollback_to_savepoint(mdl_savepoint); thd->restore_backup_statement(this, &stmt_backup); thd->stmt_arena= old_stmt_arena; @@ -3393,11 +3377,6 @@ Prepared_statement::set_parameters(String *expanded_query, and execute of a new statement. If this happens repeatedly more than MAX_REPREPARE_ATTEMPTS times, we give up. - In future we need to be able to keep the metadata locks between - prepare and execute, but right now open_and_lock_tables(), as - well as close_thread_tables() are buried deep inside - execution code (mysql_execute_command()). - @return TRUE if an error, FALSE if success @retval TRUE either MAX_REPREPARE_ATTEMPTS has been reached, or some general error @@ -3484,11 +3463,6 @@ Prepared_statement::execute_server_runnable(Server_runnable *server_runnable) error= server_runnable->execute_server_code(thd); - delete lex->sphead; - lex->sphead= 0; - /* The order is important */ - lex->unit.cleanup(); - close_thread_tables(thd); thd->cleanup_after_query(); thd->restore_active_arena(this, &stmt_backup); diff --git a/sql/sql_priv.h b/sql/sql_priv.h index 604890ffbe5..708608fc2f1 100644 --- a/sql/sql_priv.h +++ b/sql/sql_priv.h @@ -135,6 +135,16 @@ extern char err_shared_dir[]; Type of locks to be acquired is specified directly. */ #define SELECT_HIGH_PRIORITY (1ULL << 34) // SELECT, user +/** + Is set in slave SQL thread when there was an + error on master, which, when is not reproducible + on slave (i.e. the query succeeds on slave), + is not terminal to the state of repliation, + and should be ignored. The slave SQL thread, + however, needs to rollback the effects of the + succeeded statement to keep replication consistent. +*/ +#define OPTION_MASTER_SQL_ERROR (1ULL << 35) /* The rest of the file is included in the server only */ diff --git a/sql/sql_servers.cc b/sql/sql_servers.cc index c7f9cf0b132..cfbf8e96719 100644 --- a/sql/sql_servers.cc +++ b/sql/sql_servers.cc @@ -36,7 +36,7 @@ #include "sql_priv.h" #include "sql_servers.h" #include "unireg.h" -#include "sql_base.h" // close_thread_tables +#include "sql_base.h" // close_mysql_tables #include "records.h" // init_read_record, end_read_record #include "hash_filo.h" #include @@ -280,9 +280,7 @@ bool servers_reload(THD *thd) } end: - trans_commit_implicit(thd); - close_thread_tables(thd); - thd->mdl_context.release_transactional_locks(); + close_mysql_tables(thd); DBUG_PRINT("info", ("unlocking servers_cache")); mysql_rwlock_unlock(&THR_LOCK_servers); DBUG_RETURN(return_val); @@ -535,6 +533,7 @@ int insert_server_record(TABLE *table, FOREIGN_SERVER *server) { int error; DBUG_ENTER("insert_server_record"); + tmp_disable_binlog(table->in_use); table->use_all_columns(); empty_record(table); @@ -571,6 +570,8 @@ int insert_server_record(TABLE *table, FOREIGN_SERVER *server) } else error= ER_FOREIGN_SERVER_EXISTS; + + reenable_binlog(table->in_use); DBUG_RETURN(error); } @@ -625,7 +626,7 @@ int drop_server(THD *thd, LEX_SERVER_OPTIONS *server_options) error= delete_server_record(table, name.str, name.length); /* close the servers table before we call closed_cached_connection_tables */ - close_thread_tables(thd); + close_mysql_tables(thd); if (close_cached_connection_tables(thd, TRUE, &name)) { @@ -880,6 +881,7 @@ update_server_record(TABLE *table, FOREIGN_SERVER *server) { int error=0; DBUG_ENTER("update_server_record"); + tmp_disable_binlog(table->in_use); table->use_all_columns(); /* set the field that's the PK to the value we're looking for */ table->field[0]->store(server->server_name, @@ -913,6 +915,7 @@ update_server_record(TABLE *table, FOREIGN_SERVER *server) } end: + reenable_binlog(table->in_use); DBUG_RETURN(error); } @@ -938,6 +941,7 @@ delete_server_record(TABLE *table, { int error; DBUG_ENTER("delete_server_record"); + tmp_disable_binlog(table->in_use); table->use_all_columns(); /* set the field that's the PK to the value we're looking for */ @@ -959,6 +963,7 @@ delete_server_record(TABLE *table, table->file->print_error(error, MYF(0)); } + reenable_binlog(table->in_use); DBUG_RETURN(error); } @@ -1050,7 +1055,7 @@ int alter_server(THD *thd, LEX_SERVER_OPTIONS *server_options) error= update_server(thd, existing, altered); /* close the servers table before we call closed_cached_connection_tables */ - close_thread_tables(thd); + close_mysql_tables(thd); if (close_cached_connection_tables(thd, FALSE, &name)) { diff --git a/sql/sql_table.cc b/sql/sql_table.cc index cacfcedff23..42b3dfaad83 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -2285,18 +2285,13 @@ err: { /* Under LOCK TABLES we should release meta-data locks on the tables - which were dropped. Otherwise we can rely on close_thread_tables() - doing this. Unfortunately in this case we are likely to get more - false positives in try_acquire_lock() function. So - it makes sense to remove exclusive meta-data locks in all cases. + which were dropped. Leave LOCK TABLES mode if we managed to drop all tables which were locked. Additional check for 'non_temp_tables_count' is to avoid leaving LOCK TABLES mode if we have dropped only temporary tables. */ - if (! thd->locked_tables_mode) - thd->mdl_context.release_transactional_locks(); - else + if (thd->locked_tables_mode) { if (thd->lock && thd->lock->table_count == 0 && non_temp_tables_count > 0) { @@ -2305,7 +2300,8 @@ err: } for (table= tables; table; table= table->next_local) { - if (table->mdl_request.ticket) + /* Drop locks for all successfully dropped tables. */ + if (table->table == NULL && table->mdl_request.ticket) { /* Under LOCK TABLES we may have several instances of table open @@ -2316,6 +2312,10 @@ err: } } } + /* + Rely on the caller to implicitly commit the transaction + and release metadata locks. + */ } end: @@ -4214,8 +4214,14 @@ warn: } -/* - Database and name-locking aware wrapper for mysql_create_table_no_lock(), +/** + Implementation of SQLCOM_CREATE_TABLE. + + Take the metadata locks (including a shared lock on the affected + schema) and create the table. Is written to be called from + mysql_execute_command(), to which it delegates the common parts + with other commands (i.e. implicit commit before and after, + close of thread tables. */ bool mysql_create_table(THD *thd, TABLE_LIST *create_table, @@ -4231,7 +4237,7 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table, if (open_and_lock_tables(thd, thd->lex->query_tables, FALSE, 0)) { result= TRUE; - goto unlock; + goto end; } /* Got lock. */ @@ -4253,16 +4259,7 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table, !(create_info->options & HA_LEX_CREATE_TMP_TABLE)))) result= write_bin_log(thd, TRUE, thd->query(), thd->query_length()); - if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE)) - { - /* - close_thread_tables() takes care about both closing open tables (which - might be still around in case of error) and releasing metadata locks. - */ - close_thread_tables(thd); - } - -unlock: +end: DBUG_RETURN(result); } @@ -4752,6 +4749,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, trans_rollback_stmt(thd); trans_rollback(thd); close_thread_tables(thd); + thd->mdl_context.release_transactional_locks(); DBUG_PRINT("admin", ("simple error, admin next table")); continue; case -1: // error, message could be written to net @@ -5038,11 +5036,11 @@ send_result_message: trans_commit_stmt(thd); trans_commit(thd); close_thread_tables(thd); - table->table= NULL; thd->mdl_context.release_transactional_locks(); + table->table= NULL; if (!result_code) // recreation went ok { - /* Clear the ticket released in close_thread_tables(). */ + /* Clear the ticket released above. */ table->mdl_request.ticket= NULL; DEBUG_SYNC(thd, "ha_admin_open_ltable"); table->mdl_request.set_type(MDL_SHARED_WRITE); @@ -6729,13 +6727,11 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, goto err; DBUG_EXECUTE_IF("sleep_alter_enable_indexes", my_sleep(6000000);); error= table->file->ha_enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE); - /* COND_refresh will be signaled in close_thread_tables() */ break; case DISABLE: if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN)) goto err; error=table->file->ha_disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE); - /* COND_refresh will be signaled in close_thread_tables() */ break; default: DBUG_ASSERT(FALSE); @@ -6821,8 +6817,8 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, { /* Under LOCK TABLES we should adjust meta-data locks before finishing - statement. Otherwise we can rely on close_thread_tables() releasing - them. + statement. Otherwise we can rely on them being released + along with the implicit commit. */ if (new_name != table_name || new_db != db) { @@ -7360,8 +7356,8 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, 5) Write statement to the binary log. 6) If we are under LOCK TABLES and do ALTER TABLE ... RENAME we remove placeholders and release metadata locks. - 7) If we are not not under LOCK TABLES we rely on close_thread_tables() - call to remove placeholders and releasing metadata locks. + 7) If we are not not under LOCK TABLES we rely on the caller + (mysql_execute_command()) to release metadata locks. */ thd_proc_info(thd, "rename result table"); @@ -7990,7 +7986,13 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables, } } thd->clear_error(); + if (! thd->in_sub_stmt) + trans_rollback_stmt(thd); close_thread_tables(thd); + /* + Don't release metadata locks, this will be done at + statement end. + */ table->table=0; // For query cache } if (protocol->write()) @@ -8000,10 +8002,7 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables, my_eof(thd); DBUG_RETURN(FALSE); - err: - close_thread_tables(thd); // Shouldn't be needed - if (table) - table->table=0; +err: DBUG_RETURN(TRUE); } diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index 2f084c369b6..a5664b00287 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -540,9 +540,9 @@ end: } /* - If we are under LOCK TABLES we should restore original state of meta-data - locks. Otherwise call to close_thread_tables() will take care about both - TABLE instance created by open_n_lock_single_table() and metadata lock. + If we are under LOCK TABLES we should restore original state of + meta-data locks. Otherwise all locks will be released along + with the implicit commit. */ if (thd->locked_tables_mode && tables && lock_upgrade_done) mdl_ticket->downgrade_exclusive_lock(MDL_SHARED_NO_READ_WRITE); @@ -1321,6 +1321,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, thd->reset_db((char*) db, strlen(db)); while ((trg_create_str= it++)) { + sp_head *sp; trg_sql_mode= itm++; LEX_STRING *trg_definer= it_definer++; @@ -1357,13 +1358,14 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, */ lex.set_trg_event_type_for_tables(); - lex.sphead->set_info(0, 0, &lex.sp_chistics, (ulong) *trg_sql_mode); - int event= lex.trg_chistics.event; int action_time= lex.trg_chistics.action_time; - lex.sphead->set_creation_ctx(creation_ctx); - triggers->bodies[event][action_time]= lex.sphead; + sp= triggers->bodies[event][action_time]= lex.sphead; + lex.sphead= NULL; /* Prevent double cleanup. */ + + sp->set_info(0, 0, &lex.sp_chistics, (ulong) *trg_sql_mode); + sp->set_creation_ctx(creation_ctx); if (!trg_definer->length) { @@ -1376,27 +1378,26 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_TRG_NO_DEFINER, ER(ER_TRG_NO_DEFINER), (const char*) db, - (const char*) lex.sphead->m_name.str); + (const char*) sp->m_name.str); /* Set definer to the '' to correct displaying in the information schema. */ - lex.sphead->set_definer((char*) "", 0); + sp->set_definer((char*) "", 0); /* Triggers without definer information are executed under the authorization of the invoker. */ - lex.sphead->m_chistics->suid= SP_IS_NOT_SUID; + sp->m_chistics->suid= SP_IS_NOT_SUID; } else - lex.sphead->set_definer(trg_definer->str, trg_definer->length); + sp->set_definer(trg_definer->str, trg_definer->length); - if (triggers->names_list.push_back(&lex.sphead->m_name, - &table->mem_root)) + if (triggers->names_list.push_back(&sp->m_name, &table->mem_root)) goto err_with_lex_cleanup; if (!(on_table_name= alloc_lex_string(&table->mem_root))) diff --git a/sql/sql_udf.cc b/sql/sql_udf.cc index 3d197303fb1..ba1d0ceadeb 100644 --- a/sql/sql_udf.cc +++ b/sql/sql_udf.cc @@ -33,7 +33,7 @@ #include "sql_priv.h" #include "unireg.h" -#include "sql_base.h" // close_thread_tables +#include "sql_base.h" // close_mysql_tables #include "sql_parse.h" // check_identifier_name #include "sql_table.h" // write_bin_log #include "records.h" // init_read_record, end_read_record @@ -251,7 +251,7 @@ void udf_init() table->m_needs_reopen= TRUE; // Force close to free memory end: - close_thread_tables(new_thd); + close_mysql_tables(new_thd); delete new_thd; /* Remember that we don't have a THD */ my_pthread_setspecific_ptr(THR_THD, 0); diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 6e95961ebb0..9e212fb95e9 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -2203,14 +2203,21 @@ static bool fix_autocommit(sys_var *self, THD *thd, enum_var_type type) thd->variables.option_bits & OPTION_NOT_AUTOCOMMIT) { // activating autocommit - if (trans_commit(thd)) + if (trans_commit_stmt(thd) || trans_commit(thd)) { thd->variables.option_bits&= ~OPTION_AUTOCOMMIT; return true; } - close_thread_tables(thd); - thd->mdl_context.release_transactional_locks(); - + /* + Don't close thread tables or release metadata locks: if we do so, we + risk releasing locks/closing tables of expressions used to assign + other variables, as in: + set @var=my_stored_function1(), @@autocommit=1, @var2=(select max(a) + from my_table), ... + The locks will be released at statement end anyway, as SET + statement that assigns autocommit is marked to commit + transaction implicitly at the end (@sa stmt_causes_implicitcommit()). + */ thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG | OPTION_NOT_AUTOCOMMIT); thd->transaction.all.modified_non_trans_table= false; diff --git a/sql/transaction.cc b/sql/transaction.cc index f6786f20dcf..a28fba8805d 100644 --- a/sql/transaction.cc +++ b/sql/transaction.cc @@ -28,6 +28,12 @@ static bool trans_check(THD *thd) enum xa_states xa_state= thd->transaction.xid_state.xa_state; DBUG_ENTER("trans_check"); + /* + Always commit statement transaction before manipulating with + the normal one. + */ + DBUG_ASSERT(thd->transaction.stmt.is_empty()); + if (unlikely(thd->in_sub_stmt)) my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0)); if (xa_state != XA_NOTR) @@ -252,6 +258,14 @@ bool trans_commit_stmt(THD *thd) { DBUG_ENTER("trans_commit_stmt"); int res= FALSE; + /* + We currently don't invoke commit/rollback at end of + a sub-statement. In future, we perhaps should take + a savepoint for each nested statement, and release the + savepoint when statement has succeeded. + */ + DBUG_ASSERT(! thd->in_sub_stmt); + if (thd->transaction.stmt.ha_list) { res= ha_commit_trans(thd, FALSE); @@ -267,6 +281,9 @@ bool trans_commit_stmt(THD *thd) RUN_HOOK(transaction, after_rollback, (thd, FALSE)); else RUN_HOOK(transaction, after_commit, (thd, FALSE)); + + thd->transaction.stmt.reset(); + DBUG_RETURN(test(res)); } @@ -283,6 +300,14 @@ bool trans_rollback_stmt(THD *thd) { DBUG_ENTER("trans_rollback_stmt"); + /* + We currently don't invoke commit/rollback at end of + a sub-statement. In future, we perhaps should take + a savepoint for each nested statement, and release the + savepoint when statement has succeeded. + */ + DBUG_ASSERT(! thd->in_sub_stmt); + if (thd->transaction.stmt.ha_list) { ha_rollback_trans(thd, FALSE); @@ -294,6 +319,8 @@ bool trans_rollback_stmt(THD *thd) RUN_HOOK(transaction, after_rollback, (thd, FALSE)); + thd->transaction.stmt.reset(); + DBUG_RETURN(FALSE); } diff --git a/sql/tztime.cc b/sql/tztime.cc index af8574c38f1..43d43123158 100644 --- a/sql/tztime.cc +++ b/sql/tztime.cc @@ -49,13 +49,6 @@ #include "lock.h" // MYSQL_LOCK_IGNORE_FLUSH, // MYSQL_LOCK_IGNORE_TIMEOUT -/* - This forward declaration is needed because including sql_base.h - causes further includes. [TODO] Eliminate this forward declaration - and include a file with the prototype instead. -*/ -extern void close_thread_tables(THD *thd); - /* Now we don't use abbreviations in server but we will do this in future. */ @@ -1784,10 +1777,7 @@ end_with_setting_default_tz: end_with_close: if (time_zone_tables_exist) - { - close_thread_tables(thd); - thd->mdl_context.release_transactional_locks(); - } + close_mysql_tables(thd); end_with_cleanup: From 6d059673f7dc1dbff5e154b0ca6d1ef2f0fa3cc3 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Tue, 27 Jul 2010 16:42:36 +0400 Subject: [PATCH 104/115] Implement WL#5502 Remove dead 5.0 class Sensitive_cursor. Remove dead and unused code. Update to reflect the code review requests. --- include/thr_lock.h | 19 +-- mysys/thr_lock.c | 51 ++---- sql/lock.cc | 2 +- sql/sp_rcontext.cc | 3 +- sql/sql_class.cc | 5 - sql/sql_class.h | 6 +- sql/sql_cursor.cc | 380 ++------------------------------------------- sql/sql_cursor.h | 16 +- sql/sql_prepare.cc | 6 +- sql/sql_select.cc | 89 +++-------- sql/sql_select.h | 6 - sql/sql_union.cc | 3 +- 12 files changed, 64 insertions(+), 522 deletions(-) diff --git a/include/thr_lock.h b/include/thr_lock.h index 37dc37f8017..5626c067e5e 100644 --- a/include/thr_lock.h +++ b/include/thr_lock.h @@ -89,23 +89,11 @@ typedef struct st_thr_lock_info { pthread_t thread; my_thread_id thread_id; - ulong n_cursors; } THR_LOCK_INFO; -/* - Lock owner identifier. Globally identifies the lock owner within the - thread and among all the threads. The address of an instance of this - structure is used as id. -*/ - -typedef struct st_thr_lock_owner -{ - THR_LOCK_INFO *info; -} THR_LOCK_OWNER; - typedef struct st_thr_lock_data { - THR_LOCK_OWNER *owner; + THR_LOCK_INFO *owner; struct st_thr_lock_data *next,**prev; struct st_thr_lock *lock; mysql_cond_t *cond; @@ -141,19 +129,18 @@ extern LIST *thr_lock_thread_list; extern mysql_mutex_t THR_LOCK_lock; my_bool init_thr_lock(void); /* Must be called once/thread */ -#define thr_lock_owner_init(owner, info_arg) (owner)->info= (info_arg) void thr_lock_info_init(THR_LOCK_INFO *info); void thr_lock_init(THR_LOCK *lock); void thr_lock_delete(THR_LOCK *lock); void thr_lock_data_init(THR_LOCK *lock,THR_LOCK_DATA *data, void *status_param); enum enum_thr_lock_result thr_lock(THR_LOCK_DATA *data, - THR_LOCK_OWNER *owner, + THR_LOCK_INFO *owner, enum thr_lock_type lock_type, ulong lock_wait_timeout); void thr_unlock(THR_LOCK_DATA *data); enum enum_thr_lock_result thr_multi_lock(THR_LOCK_DATA **data, - uint count, THR_LOCK_OWNER *owner, + uint count, THR_LOCK_INFO *owner, ulong lock_wait_timeout); void thr_multi_unlock(THR_LOCK_DATA **data,uint count); void diff --git a/mysys/thr_lock.c b/mysys/thr_lock.c index 9d10ba1fb01..7ba0490cb29 100644 --- a/mysys/thr_lock.c +++ b/mysys/thr_lock.c @@ -107,7 +107,7 @@ my_bool init_thr_lock() } static inline my_bool -thr_lock_owner_equal(THR_LOCK_OWNER *rhs, THR_LOCK_OWNER *lhs) +thr_lock_owner_equal(THR_LOCK_INFO *rhs, THR_LOCK_INFO *lhs) { return rhs == lhs; } @@ -122,7 +122,7 @@ static int check_lock(struct st_lock_list *list, const char* lock_type, { THR_LOCK_DATA *data,**prev; uint count=0; - THR_LOCK_OWNER *UNINIT_VAR(first_owner); + THR_LOCK_INFO *UNINIT_VAR(first_owner); prev= &list->data; if (list->data) @@ -341,7 +341,6 @@ void thr_lock_info_init(THR_LOCK_INFO *info) struct st_my_thread_var *tmp= my_thread_var; info->thread= tmp->pthread_self; info->thread_id= tmp->id; - info->n_cursors= 0; } /* Initialize a lock instance */ @@ -357,7 +356,7 @@ void thr_lock_data_init(THR_LOCK *lock,THR_LOCK_DATA *data, void *param) static inline my_bool -has_old_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner) +has_old_lock(THR_LOCK_DATA *data, THR_LOCK_INFO *owner) { for ( ; data ; data=data->next) { @@ -506,13 +505,12 @@ wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data, enum enum_thr_lock_result -thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner, +thr_lock(THR_LOCK_DATA *data, THR_LOCK_INFO *owner, enum thr_lock_type lock_type, ulong lock_wait_timeout) { THR_LOCK *lock=data->lock; enum enum_thr_lock_result result= THR_LOCK_SUCCESS; struct st_lock_list *wait_queue; - THR_LOCK_DATA *lock_owner; DBUG_ENTER("thr_lock"); data->next=0; @@ -521,7 +519,7 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner, data->owner= owner; /* Must be reset ! */ mysql_mutex_lock(&lock->mutex); DBUG_PRINT("lock",("data: 0x%lx thread: 0x%lx lock: 0x%lx type: %d", - (long) data, data->owner->info->thread_id, + (long) data, data->owner->thread_id, (long) lock, (int) lock_type)); check_locks(lock,(uint) lock_type <= (uint) TL_READ_NO_INSERT ? "enter read_lock" : "enter write_lock",0); @@ -558,7 +556,7 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner, */ DBUG_PRINT("lock",("write locked 1 by thread: 0x%lx", - lock->write.data->owner->info->thread_id)); + lock->write.data->owner->thread_id)); if (thr_lock_owner_equal(data->owner, lock->write.data->owner) || (lock->write.data->type <= TL_WRITE_DELAYED && (((int) lock_type <= (int) TL_READ_HIGH_PRIORITY) || @@ -707,7 +705,7 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner, goto end; } DBUG_PRINT("lock",("write locked 2 by thread: 0x%lx", - lock->write.data->owner->info->thread_id)); + lock->write.data->owner->thread_id)); } else { @@ -743,23 +741,10 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner, } } DBUG_PRINT("lock",("write locked 3 by thread: 0x%lx type: %d", - lock->read.data->owner->info->thread_id, data->type)); + lock->read.data->owner->thread_id, data->type)); } wait_queue= &lock->write_wait; } - /* - Try to detect a trivial deadlock when using cursors: attempt to - lock a table that is already locked by an open cursor within the - same connection. lock_owner can be zero if we succumbed to a high - priority writer in the write_wait queue. - */ - lock_owner= lock->read.data ? lock->read.data : lock->write.data; - if (lock_owner && lock_owner->owner->info == owner->info) - { - DBUG_PRINT("lock",("deadlock")); - result= THR_LOCK_DEADLOCK; - goto end; - } /* Can't get lock yet; Wait for it */ DBUG_RETURN(wait_for_lock(wait_queue, data, 0, lock_wait_timeout)); end: @@ -807,7 +792,7 @@ static inline void free_all_read_locks(THR_LOCK *lock, } /* purecov: begin inspected */ DBUG_PRINT("lock",("giving read lock to thread: 0x%lx", - data->owner->info->thread_id)); + data->owner->thread_id)); /* purecov: end */ data->cond=0; /* Mark thread free */ mysql_cond_signal(cond); @@ -826,7 +811,7 @@ void thr_unlock(THR_LOCK_DATA *data) enum thr_lock_type lock_type=data->type; DBUG_ENTER("thr_unlock"); DBUG_PRINT("lock",("data: 0x%lx thread: 0x%lx lock: 0x%lx", - (long) data, data->owner->info->thread_id, (long) lock)); + (long) data, data->owner->thread_id, (long) lock)); mysql_mutex_lock(&lock->mutex); check_locks(lock,"start of release lock",0); @@ -915,7 +900,7 @@ static void wake_up_waiters(THR_LOCK *lock) data->type=TL_WRITE; /* Upgrade lock */ /* purecov: begin inspected */ DBUG_PRINT("lock",("giving write lock of type %d to thread: 0x%lx", - data->type, data->owner->info->thread_id)); + data->type, data->owner->thread_id)); /* purecov: end */ { mysql_cond_t *cond= data->cond; @@ -1020,7 +1005,7 @@ static void sort_locks(THR_LOCK_DATA **data,uint count) enum enum_thr_lock_result -thr_multi_lock(THR_LOCK_DATA **data, uint count, THR_LOCK_OWNER *owner, +thr_multi_lock(THR_LOCK_DATA **data, uint count, THR_LOCK_INFO *owner, ulong lock_wait_timeout) { THR_LOCK_DATA **pos,**end; @@ -1144,7 +1129,7 @@ void thr_multi_unlock(THR_LOCK_DATA **data,uint count) else { DBUG_PRINT("lock",("Free lock: data: 0x%lx thread: 0x%lx lock: 0x%lx", - (long) *pos, (*pos)->owner->info->thread_id, + (long) *pos, (*pos)->owner->thread_id, (long) (*pos)->lock)); } } @@ -1200,7 +1185,7 @@ my_bool thr_abort_locks_for_thread(THR_LOCK *lock, my_thread_id thread_id) mysql_mutex_lock(&lock->mutex); for (data= lock->read_wait.data; data ; data= data->next) { - if (data->owner->info->thread_id == thread_id) /* purecov: tested */ + if (data->owner->thread_id == thread_id) /* purecov: tested */ { DBUG_PRINT("info",("Aborting read-wait lock")); data->type= TL_UNLOCK; /* Mark killed */ @@ -1217,7 +1202,7 @@ my_bool thr_abort_locks_for_thread(THR_LOCK *lock, my_thread_id thread_id) } for (data= lock->write_wait.data; data ; data= data->next) { - if (data->owner->info->thread_id == thread_id) /* purecov: tested */ + if (data->owner->thread_id == thread_id) /* purecov: tested */ { DBUG_PRINT("info",("Aborting write-wait lock")); data->type= TL_UNLOCK; @@ -1387,7 +1372,7 @@ static void thr_print_lock(const char* name,struct st_lock_list *list) prev= &list->data; for (data=list->data; data && count++ < MAX_LOCKS ; data=data->next) { - printf("0x%lx (%lu:%d); ", (ulong) data, data->owner->info->thread_id, + printf("0x%lx (%lu:%d); ", (ulong) data, data->owner->thread_id, (int) data->type); if (data->prev != prev) printf("\nWarning: prev didn't point at previous lock\n"); @@ -1525,7 +1510,6 @@ static void *test_thread(void *arg) { int i,j,param=*((int*) arg); THR_LOCK_DATA data[MAX_LOCK_COUNT]; - THR_LOCK_OWNER owner; THR_LOCK_INFO lock_info; THR_LOCK_DATA *multi_locks[MAX_LOCK_COUNT]; my_thread_init(); @@ -1534,7 +1518,6 @@ static void *test_thread(void *arg) thr_lock_info_init(&lock_info); - thr_lock_owner_init(&owner, &lock_info); for (i=0; i < lock_counts[param] ; i++) thr_lock_data_init(locks+tests[param][i].lock_nr,data+i,NULL); for (j=1 ; j < 10 ; j++) /* try locking 10 times */ @@ -1544,7 +1527,7 @@ static void *test_thread(void *arg) multi_locks[i]= &data[i]; data[i].type= tests[param][i].lock_type; } - thr_multi_lock(multi_locks, lock_counts[param], &owner, TEST_TIMEOUT); + thr_multi_lock(multi_locks, lock_counts[param], &lock_info, TEST_TIMEOUT); mysql_mutex_lock(&LOCK_thread_count); { int tmp=rand() & 7; /* Do something from 0-2 sec */ diff --git a/sql/lock.cc b/sql/lock.cc index 7c0acb58e7c..1a77b576e67 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -313,7 +313,7 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, uint flags) rc= thr_lock_errno_to_mysql[(int) thr_multi_lock(sql_lock->locks + sql_lock->lock_count, sql_lock->lock_count, - thd->lock_id, timeout)]; + &thd->lock_info, timeout)]; if (rc) { if (sql_lock->table_count) diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc index 047cec76486..b08f8008b59 100644 --- a/sql/sp_rcontext.cc +++ b/sql/sp_rcontext.cc @@ -525,8 +525,7 @@ sp_cursor::open(THD *thd) MYF(0)); return -1; } - if (mysql_open_cursor(thd, (uint) ALWAYS_MATERIALIZED_CURSOR, &result, - &server_side_cursor)) + if (mysql_open_cursor(thd, &result, &server_side_cursor)) return -1; return 0; } diff --git a/sql/sql_class.cc b/sql/sql_class.cc index f3ef440c2f0..9d48c1282d5 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -490,7 +490,6 @@ THD::THD() :Statement(&main_lex, &main_mem_root, CONVENTIONAL_EXECUTION, /* statement id */ 0), rli_fake(0), - lock_id(&main_lock_id), user_time(0), in_sub_stmt(0), binlog_unsafe_warning_flags(0), stmt_accessed_table_flag(0), @@ -624,7 +623,6 @@ THD::THD() randominit(&rand, tmp + (ulong) &rand, tmp + (ulong) ::global_query_id); substitute_null_with_insert_id = FALSE; thr_lock_info_init(&lock_info); /* safety: will be reset after start */ - thr_lock_owner_init(&main_lock_id, &lock_info); m_internal_handler= NULL; current_user_used= FALSE; @@ -1113,7 +1111,6 @@ THD::~THD() } #endif stmt_map.reset(); /* close all prepared statements */ - DBUG_ASSERT(lock_info.n_cursors == 0); if (!cleanup_done) cleanup(); @@ -2589,7 +2586,6 @@ Statement::Statement(LEX *lex_arg, MEM_ROOT *mem_root_arg, id(id_arg), mark_used_columns(MARK_COLUMNS_READ), lex(lex_arg), - cursor(0), db(NULL), db_length(0) { @@ -2611,7 +2607,6 @@ void Statement::set_statement(Statement *stmt) mark_used_columns= stmt->mark_used_columns; lex= stmt->lex; query_string= stmt->query_string; - cursor= stmt->cursor; } diff --git a/sql/sql_class.h b/sql/sql_class.h index c095fee6232..8a54e7069b1 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -38,7 +38,7 @@ #include "protocol.h" /* Protocol_text, Protocol_binary */ #include "violite.h" /* vio_is_connected */ #include "thr_lock.h" /* thr_lock_type, THR_LOCK_DATA, - THR_LOCK_INFO, THR_LOCK_OWNER */ + THR_LOCK_INFO */ class Reprepare_observer; @@ -723,7 +723,6 @@ public: ENGINE INNODB STATUS. */ LEX_STRING query_string; - Server_side_cursor *cursor; inline char *query() { return query_string.str; } inline uint32 query_length() { return query_string.length; } @@ -1416,9 +1415,6 @@ public: struct system_status_var status_var; // Per thread statistic vars struct system_status_var *initial_status_var; /* used by show status */ THR_LOCK_INFO lock_info; // Locking info of this thread - THR_LOCK_OWNER main_lock_id; // To use for conventional queries - THR_LOCK_OWNER *lock_id; // If not main_lock_id, points to - // the lock_id of a cursor. /** Protects THD data accessed from other threads: - thd->query and thd->query_length (used by SHOW ENGINE diff --git a/sql/sql_cursor.cc b/sql/sql_cursor.cc index 28d2bd8b4de..9a3eb6ff526 100644 --- a/sql/sql_cursor.cc +++ b/sql/sql_cursor.cc @@ -19,60 +19,13 @@ #include "sql_priv.h" #include "unireg.h" #include "sql_cursor.h" -#include "sql_select.h" #include "probes_mysql.h" #include "sql_parse.h" // mysql_execute_command -#include "sql_base.h" /**************************************************************************** Declarations. ****************************************************************************/ -/** - Sensitive_cursor -- a sensitive non-materialized server side - cursor. An instance of this class cursor has its own runtime - state -- list of used items and memory root for runtime memory, - open and locked tables, change list for the changes of the - parsed tree. This state is freed when the cursor is closed. -*/ - -class Sensitive_cursor: public Server_side_cursor -{ - MEM_ROOT main_mem_root; - Query_arena *stmt_arena; - JOIN *join; - TABLE *open_tables; - MYSQL_LOCK *lock; - TABLE *derived_tables; - /* List of items created during execution */ - query_id_t query_id; - struct Engine_info - { - handlerton *ht; - void *read_view; - }; - Engine_info ht_info[MAX_HA]; - Item_change_list change_list; - my_bool close_at_commit; - THR_LOCK_OWNER lock_id; -private: - /* bzero cursor state in THD */ - void reset_thd(THD *thd); -public: - Sensitive_cursor(THD *thd, select_result *result_arg); - - THR_LOCK_OWNER *get_lock_id() { return &lock_id; } - /* Save THD state into cursor */ - void post_open(THD *thd); - - virtual bool is_open() const { return join != 0; } - virtual int open(JOIN *join); - virtual void fetch(ulong num_rows); - virtual void close(); - virtual ~Sensitive_cursor(); -}; - - /** Materialized_cursor -- an insensitive materialized server-side cursor. The result set of this cursor is saved in a temporary @@ -125,10 +78,9 @@ public: /**************************************************************************/ /** - Attempt to open a materialized or non-materialized cursor. + Attempt to open a materialized cursor. @param thd thread handle - @param[in] flags create a materialized cursor or not @param[in] result result class of the caller used as a destination for the rows fetched from the cursor @param[out] pcursor a pointer to store a pointer to cursor in @@ -141,37 +93,21 @@ public: non-zero an error, 'pcursor' has been left intact. */ -int mysql_open_cursor(THD *thd, uint flags, select_result *result, +int mysql_open_cursor(THD *thd, select_result *result, Server_side_cursor **pcursor) { - Sensitive_cursor *sensitive_cursor; select_result *save_result; Select_materialize *result_materialize; LEX *lex= thd->lex; int rc; - /* - The lifetime of the sensitive cursor is the same or less as the - lifetime of the runtime memory of the statement it's opened for. - */ if (! (result_materialize= new (thd->mem_root) Select_materialize(result))) return 1; - if (! (sensitive_cursor= new (thd->mem_root) Sensitive_cursor(thd, result))) - { - delete result_materialize; - result_materialize= NULL; - return 1; - } - save_result= lex->result; lex->result= result_materialize; - if (! (flags & (uint) ALWAYS_MATERIALIZED_CURSOR)) - { - thd->lock_id= sensitive_cursor->get_lock_id(); - thd->cursor= sensitive_cursor; - } + MYSQL_QUERY_EXEC_START(thd->query(), thd->thread_id, (char *) (thd->db ? thd->db : ""), @@ -182,20 +118,14 @@ int mysql_open_cursor(THD *thd, uint flags, select_result *result, MYSQL_QUERY_EXEC_DONE(rc); lex->result= save_result; - thd->lock_id= &thd->main_lock_id; - thd->cursor= 0; - /* Possible options here: - - a sensitive cursor is open. In this case rc is 0 and - result_materialize->materialized_cursor is NULL, or - a materialized cursor is open. In this case rc is 0 and result_materialize->materialized is not NULL - an error occurred during materialization. result_materialize->materialized_cursor is not NULL, but rc != 0 - successful completion of mysql_execute_command without - a cursor: rc is 0, result_materialize->materialized_cursor is NULL, - sensitive_cursor is not open. + a cursor: rc is 0, result_materialize->materialized_cursor is NULL. This is possible if some command writes directly to the network, bypassing select_result mechanism. An example of such command is SHOW VARIABLES or SHOW STATUS. @@ -204,23 +134,10 @@ int mysql_open_cursor(THD *thd, uint flags, select_result *result, { if (result_materialize->materialized_cursor) delete result_materialize->materialized_cursor; - goto err_open; - } - - if (sensitive_cursor->is_open()) - { - DBUG_ASSERT(!result_materialize->materialized_cursor); - /* - It's safer if we grab THD state after mysql_execute_command - is finished and not in Sensitive_cursor::open(), because - currently the call to Sensitive_cursor::open is buried deep - in JOIN::exec of the top level join. - */ - sensitive_cursor->post_open(thd); - *pcursor= sensitive_cursor; goto end; } - else if (result_materialize->materialized_cursor) + + if (result_materialize->materialized_cursor) { Materialized_cursor *materialized_cursor= result_materialize->materialized_cursor; @@ -228,18 +145,13 @@ int mysql_open_cursor(THD *thd, uint flags, select_result *result, if ((rc= materialized_cursor->open(0))) { delete materialized_cursor; - materialized_cursor= NULL; - goto err_open; + goto end; } *pcursor= materialized_cursor; thd->stmt_arena->cleanup_stmt(); - goto end; } -err_open: - DBUG_ASSERT(! (sensitive_cursor && sensitive_cursor->is_open())); - delete sensitive_cursor; end: delete result_materialize; return rc; @@ -271,280 +183,6 @@ void Server_side_cursor::operator delete(void *ptr, size_t size) DBUG_VOID_RETURN; } -/**************************************************************************** - Sensitive_cursor -****************************************************************************/ - -Sensitive_cursor::Sensitive_cursor(THD *thd, select_result *result_arg) - :Server_side_cursor(&main_mem_root, result_arg), - stmt_arena(0), - join(0), - close_at_commit(FALSE) -{ - /* We will overwrite it at open anyway. */ - init_sql_alloc(&main_mem_root, ALLOC_ROOT_MIN_BLOCK_SIZE, 0); - thr_lock_owner_init(&lock_id, &thd->lock_info); - bzero((void*) ht_info, sizeof(ht_info)); -} - - -/** - Save THD state into cursor. - - @todo - - What problems can we have with it if cursor is open? - - TODO: must be fixed because of the prelocked mode. -*/ -void -Sensitive_cursor::post_open(THD *thd) -{ - Engine_info *info; - /* - We need to save and reset thd->mem_root, otherwise it'll be - freed later in mysql_parse. - - We can't just change thd->mem_root here as we want to keep the - things that are already allocated in thd->mem_root for - Sensitive_cursor::fetch() - */ - *mem_root= *thd->mem_root; - stmt_arena= thd->stmt_arena; - state= stmt_arena->state; - /* Allocate a new memory root for thd */ - init_sql_alloc(thd->mem_root, - thd->variables.query_alloc_block_size, - thd->variables.query_prealloc_size); - - /* - Save tables and zero THD pointers to prevent table close in - close_thread_tables. - */ - derived_tables= thd->derived_tables; - open_tables= thd->open_tables; - lock= thd->lock; - query_id= thd->query_id; - free_list= thd->free_list; - thd->change_list.move_elements_to(&change_list); - reset_thd(thd); - /* Now we have an active cursor and can cause a deadlock */ - thd->lock_info.n_cursors++; - - close_at_commit= FALSE; /* reset in case we're reusing the cursor */ - info= &ht_info[0]; - for (Ha_trx_info *ha_trx_info= thd->transaction.stmt.ha_list; - ha_trx_info; ha_trx_info= ha_trx_info->next()) - { - handlerton *ht= ha_trx_info->ht(); - close_at_commit|= test(ht->flags & HTON_CLOSE_CURSORS_AT_COMMIT); - if (ht->create_cursor_read_view) - { - info->ht= ht; - info->read_view= (ht->create_cursor_read_view)(ht, thd); - ++info; - } - } - /* - What problems can we have with it if cursor is open? - TODO: must be fixed because of the prelocked mode. - */ -} - - -/** - bzero cursor state in THD. -*/ - -void -Sensitive_cursor::reset_thd(THD *thd) -{ - thd->derived_tables= 0; - thd->set_open_tables(NULL); - thd->lock= 0; - thd->free_list= 0; - thd->change_list.empty(); -} - - -int -Sensitive_cursor::open(JOIN *join_arg) -{ - join= join_arg; - THD *thd= join->thd; - /* First non-constant table */ - JOIN_TAB *join_tab= join->join_tab + join->const_tables; - DBUG_ENTER("Sensitive_cursor::open"); - - join->change_result(result); - /* - Send fields description to the client; server_status is sent - in 'EOF' packet, which follows send_result_set_metadata(). - We don't simply use SEND_EOF flag of send_result_set_metadata because we also - want to flush the network buffer, which is done only in a standalone - send_eof(). - */ - result->send_result_set_metadata(*join->fields, Protocol::SEND_NUM_ROWS); - thd->server_status|= SERVER_STATUS_CURSOR_EXISTS; - result->send_eof(); - thd->server_status&= ~SERVER_STATUS_CURSOR_EXISTS; - - /* Prepare JOIN for reading rows. */ - join->tmp_table= 0; - join->join_tab[join->tables-1].next_select= setup_end_select_func(join); - join->send_records= 0; - join->fetch_limit= join->unit->offset_limit_cnt; - - /* Disable JOIN CACHE as it is not working with cursors yet */ - for (JOIN_TAB *tab= join_tab; - tab != join->join_tab + join->tables - 1; - tab++) - { - if (tab->next_select == sub_select_cache) - tab->next_select= sub_select; - } - - DBUG_ASSERT(join_tab->table->reginfo.not_exists_optimize == 0); - DBUG_ASSERT(join_tab->not_used_in_distinct == 0); - /* - null_row is set only if row not found and it's outer join: should never - happen for the first table in join_tab list - */ - DBUG_ASSERT(join_tab->table->null_row == 0); - DBUG_RETURN(0); -} - - -/** - Fetch next num_rows rows from the cursor and send them to the client. - - Precondition: - - Sensitive_cursor is open - - @param num_rows fetch up to this number of rows (maybe less) -*/ - -void -Sensitive_cursor::fetch(ulong num_rows) -{ - THD *thd= join->thd; - JOIN_TAB *join_tab= join->join_tab + join->const_tables; - enum_nested_loop_state error= NESTED_LOOP_OK; - Query_arena backup_arena; - Engine_info *info; - DBUG_ENTER("Sensitive_cursor::fetch"); - DBUG_PRINT("enter",("rows: %lu", num_rows)); - - DBUG_ASSERT(thd->derived_tables == 0 && thd->open_tables == 0 && - thd->lock == 0); - - thd->derived_tables= derived_tables; - thd->set_open_tables(open_tables); - thd->lock= lock; - thd->set_query_id(query_id); - change_list.move_elements_to(&thd->change_list); - /* save references to memory allocated during fetch */ - thd->set_n_backup_active_arena(this, &backup_arena); - - for (info= ht_info; info->read_view ; info++) - (info->ht->set_cursor_read_view)(info->ht, thd, info->read_view); - - join->fetch_limit+= num_rows; - - error= sub_select(join, join_tab, 0); - if (error == NESTED_LOOP_OK || error == NESTED_LOOP_NO_MORE_ROWS) - error= sub_select(join,join_tab,1); - if (error == NESTED_LOOP_QUERY_LIMIT) - error= NESTED_LOOP_OK; /* select_limit used */ - if (error == NESTED_LOOP_CURSOR_LIMIT) - join->resume_nested_loop= TRUE; - - ha_release_temporary_latches(thd); - - /* Grab free_list here to correctly free it in close */ - thd->restore_active_arena(this, &backup_arena); - - thd->change_list.move_elements_to(&change_list); - reset_thd(thd); - - for (info= ht_info; info->read_view; info++) - (info->ht->set_cursor_read_view)(info->ht, thd, 0); - - if (error == NESTED_LOOP_CURSOR_LIMIT) - { - /* Fetch limit worked, possibly more rows are there */ - thd->server_status|= SERVER_STATUS_CURSOR_EXISTS; - result->send_eof(); - thd->server_status&= ~SERVER_STATUS_CURSOR_EXISTS; - } - else - { - close(); - if (error == NESTED_LOOP_OK) - { - thd->server_status|= SERVER_STATUS_LAST_ROW_SENT; - result->send_eof(); - thd->server_status&= ~SERVER_STATUS_LAST_ROW_SENT; - } - else if (error != NESTED_LOOP_KILLED) - my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0)); - } - DBUG_VOID_RETURN; -} - - -/** - @todo - Another hack: we need to set THD state as if in a fetch to be - able to call stmt close. -*/ -void -Sensitive_cursor::close() -{ - THD *thd= join->thd; - DBUG_ENTER("Sensitive_cursor::close"); - - for (Engine_info *info= ht_info; info->read_view; info++) - { - (info->ht->close_cursor_read_view)(info->ht, thd, info->read_view); - info->read_view= 0; - info->ht= 0; - } - - change_list.move_elements_to(&thd->change_list); - { - /* - XXX: Another hack: we need to set THD state as if in a fetch to be - able to call stmt close. - */ - DBUG_ASSERT(lock || open_tables || derived_tables); - - TABLE *tmp_derived_tables= thd->derived_tables; - MYSQL_LOCK *tmp_lock= thd->lock; - - thd->set_open_tables(open_tables); - thd->derived_tables= derived_tables; - thd->lock= lock; - - close_thread_tables(thd); - /* Is expected to at least close tables and empty thd->change_list */ - stmt_arena->cleanup_stmt(); - - thd->set_open_tables(tmp_derived_tables); - thd->derived_tables= tmp_derived_tables; - thd->lock= tmp_lock; - } - thd->lock_info.n_cursors--; /* Decrease the number of active cursors */ - join= 0; - stmt_arena= 0; - free_items(); - DBUG_VOID_RETURN; -} - - -Sensitive_cursor::~Sensitive_cursor() -{ - if (is_open()) - close(); -} /*************************************************************************** Materialized_cursor @@ -570,7 +208,8 @@ Materialized_cursor::Materialized_cursor(select_result *result_arg, @param send_result_set_metadata List of fields that would be sent. */ -int Materialized_cursor::fill_item_list(THD *thd, List &send_result_set_metadata) +int Materialized_cursor::fill_item_list(THD *thd, + List &send_result_set_metadata) { Query_arena backup_arena; int rc; @@ -608,6 +247,7 @@ end: return rc || thd->is_error(); } + int Materialized_cursor::open(JOIN *join __attribute__((unused))) { THD *thd= fake_unit.thd; diff --git a/sql/sql_cursor.h b/sql/sql_cursor.h index 2a394e281b4..ed7bfac821a 100644 --- a/sql/sql_cursor.h +++ b/sql/sql_cursor.h @@ -32,11 +32,11 @@ class JOIN; */ /** - Server_side_cursor -- an interface for materialized and - sensitive (non-materialized) implementation of cursors. All - cursors are self-contained (created in their own memory root). - For that reason they must be deleted only using a pointer to - Server_side_cursor, not to its base class. + Server_side_cursor -- an interface for materialized + implementation of cursors. All cursors are self-contained + (created in their own memory root). For that reason they must + be deleted only using a pointer to Server_side_cursor, not to + its base class. */ class Server_side_cursor: protected Query_arena, public Sql_alloc @@ -60,11 +60,7 @@ public: }; -int mysql_open_cursor(THD *thd, uint flags, - select_result *result, +int mysql_open_cursor(THD *thd, select_result *result, Server_side_cursor **res); -/** Possible values for flags */ -enum { ANY_CURSOR= 1, ALWAYS_MATERIALIZED_CURSOR= 2 }; - #endif /* _sql_cusor_h_ */ diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 151a135125e..bbdac26d985 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -152,6 +152,7 @@ public: THD *thd; Select_fetch_protocol_binary result; Item_param **param_array; + Server_side_cursor *cursor; uint param_count; uint last_errno; uint flags; @@ -2672,7 +2673,6 @@ void mysqld_stmt_fetch(THD *thd, char *packet, uint packet_length) if (!cursor->is_open()) { stmt->close_cursor(); - thd->cursor= 0; reset_stmt_params(stmt); } @@ -3010,6 +3010,7 @@ Prepared_statement::Prepared_statement(THD *thd_arg) thd(thd_arg), result(thd_arg), param_array(0), + cursor(0), param_count(0), last_errno(0), flags((uint) IS_IN_USE) @@ -3751,8 +3752,7 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor) /* Go! */ if (open_cursor) - error= mysql_open_cursor(thd, (uint) ALWAYS_MATERIALIZED_CURSOR, - &result, &cursor); + error= mysql_open_cursor(thd, &result, &cursor); else { /* diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 11378ac0d11..1d61c37ba13 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -33,7 +33,6 @@ #include "sql_select.h" #include "sql_cache.h" // query_cache_* #include "sql_table.h" // primary_key_name -#include "sql_cursor.h" #include "probes_mysql.h" #include "key.h" // key_copy, key_cmp, key_cmp_if_same #include "lock.h" // mysql_unlock_some_tables, @@ -2340,35 +2339,13 @@ JOIN::exec() curr_join->fields= curr_fields_list; curr_join->procedure= procedure; - if (is_top_level_join() && thd->cursor && tables != const_tables) - { - /* - We are here if this is JOIN::exec for the last select of the main unit - and the client requested to open a cursor. - We check that not all tables are constant because this case is not - handled by do_select() separately, and this case is not implemented - for cursors yet. - */ - DBUG_ASSERT(error == 0); - /* - curr_join is used only for reusable joins - that is, - to perform SELECT for each outer row (like in subselects). - This join is main, so we know for sure that curr_join == join. - */ - DBUG_ASSERT(curr_join == this); - /* Open cursor for the last join sweep */ - error= thd->cursor->open(this); - } - else - { - thd_proc_info(thd, "Sending data"); - DBUG_PRINT("info", ("%s", thd->proc_info)); - result->send_result_set_metadata((procedure ? curr_join->procedure_fields_list : - *curr_fields_list), - Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF); - error= do_select(curr_join, curr_fields_list, NULL, procedure); - thd->limit_found_rows= curr_join->send_records; - } + thd_proc_info(thd, "Sending data"); + DBUG_PRINT("info", ("%s", thd->proc_info)); + result->send_result_set_metadata((procedure ? curr_join->procedure_fields_list : + *curr_fields_list), + Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF); + error= do_select(curr_join, curr_fields_list, NULL, procedure); + thd->limit_found_rows= curr_join->send_records; /* Accumulate the counts from all join iterations of all join parts. */ thd->examined_row_count+= curr_join->examined_rows; @@ -2563,16 +2540,6 @@ mysql_select(THD *thd, Item ***rref_pointer_array, join->exec(); - if (thd->cursor && thd->cursor->is_open()) - { - /* - A cursor was opened for the last sweep in exec(). - We are here only if this is mysql_select for top-level SELECT_LEX_UNIT - and there were no error. - */ - free_join= 0; - } - if (thd->lex->describe & DESCRIBE_EXTENDED) { select_lex->where= join->conds_history; @@ -11642,37 +11609,23 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records) enum_nested_loop_state rc; READ_RECORD *info= &join_tab->read_record; - if (join->resume_nested_loop) + join->return_tab= join_tab; + + if (join_tab->last_inner) { - /* If not the last table, plunge down the nested loop */ - if (join_tab < join->join_tab + join->tables - 1) - rc= (*join_tab->next_select)(join, join_tab + 1, 0); - else - { - join->resume_nested_loop= FALSE; - rc= NESTED_LOOP_OK; - } + /* join_tab is the first inner table for an outer join operation. */ + + /* Set initial state of guard variables for this table.*/ + join_tab->found=0; + join_tab->not_null_compl= 1; + + /* Set first_unmatched for the last inner table of this group */ + join_tab->last_inner->first_unmatched= join_tab; } - else - { - join->return_tab= join_tab; + join->thd->warning_info->reset_current_row_for_warning(); - if (join_tab->last_inner) - { - /* join_tab is the first inner table for an outer join operation. */ - - /* Set initial state of guard variables for this table.*/ - join_tab->found=0; - join_tab->not_null_compl= 1; - - /* Set first_unmatched for the last inner table of this group */ - join_tab->last_inner->first_unmatched= join_tab; - } - join->thd->warning_info->reset_current_row_for_warning(); - - error= (*join_tab->read_first_record)(join_tab); - rc= evaluate_join_record(join, join_tab, error); - } + error= (*join_tab->read_first_record)(join_tab); + rc= evaluate_join_record(join, join_tab, error); while (rc == NESTED_LOOP_OK) { diff --git a/sql/sql_select.h b/sql/sql_select.h index 0496870bb3f..40f9e6d4054 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -309,11 +309,6 @@ public: bool sort_and_group; bool first_record,full_join,group, no_field_update; bool do_send_rows; - /** - TRUE when we want to resume nested loop iterations when - fetching data from a cursor - */ - bool resume_nested_loop; table_map const_table_map,found_const_table_map; /* Bitmap of all inner tables from outer joins @@ -479,7 +474,6 @@ public: sort_and_group= 0; first_record= 0; do_send_rows= 1; - resume_nested_loop= FALSE; send_records= 0; found_records= 0; fetch_limit= HA_POS_ERROR; diff --git a/sql/sql_union.cc b/sql/sql_union.cc index 9ca4556524f..acc0f704c44 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -36,8 +36,7 @@ bool mysql_union(THD *thd, LEX *lex, select_result *result, if (!(res= unit->prepare(thd, result, SELECT_NO_UNLOCK | setup_tables_done_option))) res= unit->exec(); - if (res || !thd->cursor || !thd->cursor->is_open()) - res|= unit->cleanup(); + res|= unit->cleanup(); DBUG_RETURN(res); } From c402eccf793223a2bb94d5754c3c6837a8920b21 Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Wed, 28 Jul 2010 08:38:28 +0300 Subject: [PATCH 105/115] Remove trailing whitespace on ha_innodb.cc:574 --- storage/innobase/handler/ha_innodb.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index bf0c5528933..8778c705d5a 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -571,7 +571,7 @@ innodb_show_status( THD* thd, /*!< in: the MySQL query thread of the caller */ stat_print_fn *stat_print); static -bool innobase_show_status(handlerton *hton, THD* thd, +bool innobase_show_status(handlerton *hton, THD* thd, stat_print_fn* stat_print, enum ha_stat_type stat_type); From 9c0a4999afae11faf9e2d4c9f5a203dfff4f604c Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Wed, 28 Jul 2010 09:15:07 +0300 Subject: [PATCH 106/115] Fix trailing whitespace on ha_innodb.cc:1259 --- storage/innobase/handler/ha_innodb.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 8778c705d5a..ab9df9a0272 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -1256,7 +1256,7 @@ innobase_mysql_tmpfile(void) my_close(). */ #ifdef _WIN32 - /* Note that on Windows, the integer returned by mysql_tmpfile + /* Note that on Windows, the integer returned by mysql_tmpfile has no relation to C runtime file descriptor. Here, we need to call my_get_osfhandle to get the HANDLE and then convert it to C runtime filedescriptor. */ From 8d28ec7c77cd7d67ea574c2d4717560f7b3c7ebb Mon Sep 17 00:00:00 2001 From: Jimmy Yang Date: Wed, 28 Jul 2010 03:20:44 -0700 Subject: [PATCH 107/115] Fix bug #55581 by backporting fix of #52546 from mysql-trunk-innodb to mysql-5.1-innodb plugin. --- storage/innodb_plugin/ChangeLog | 6 ++++ storage/innodb_plugin/include/mem0pool.h | 12 ------- storage/innodb_plugin/mem/mem0mem.c | 12 +++---- storage/innodb_plugin/mem/mem0pool.c | 41 +++++++++++++++--------- storage/innodb_plugin/srv/srv0start.c | 6 +++- 5 files changed, 43 insertions(+), 34 deletions(-) diff --git a/storage/innodb_plugin/ChangeLog b/storage/innodb_plugin/ChangeLog index 5bc4ea7c303..3e802360d23 100644 --- a/storage/innodb_plugin/ChangeLog +++ b/storage/innodb_plugin/ChangeLog @@ -1,3 +1,9 @@ +2010-07-27 The InnoDB Team + + * include/mem0pool.h, mem/mem0mem.c, mem/mem0pool.c, srv/srv0start.c: + Fix Bug#55581 shutdown with innodb-use-sys-malloc=0: assert + mutex->magic_n == MUTEX_MAGIC_N. + 2010-06-30 The InnoDB Team * btr/btr0sea.c, ha/ha0ha.c, handler/ha_innodb.cc, include/btr0sea.h: diff --git a/storage/innodb_plugin/include/mem0pool.h b/storage/innodb_plugin/include/mem0pool.h index 5e93bf88a47..fa8be296ec9 100644 --- a/storage/innodb_plugin/include/mem0pool.h +++ b/storage/innodb_plugin/include/mem0pool.h @@ -100,18 +100,6 @@ mem_pool_get_reserved( /*==================*/ mem_pool_t* pool); /*!< in: memory pool */ /********************************************************************//** -Reserves the mem pool mutex. */ -UNIV_INTERN -void -mem_pool_mutex_enter(void); -/*======================*/ -/********************************************************************//** -Releases the mem pool mutex. */ -UNIV_INTERN -void -mem_pool_mutex_exit(void); -/*=====================*/ -/********************************************************************//** Validates a memory pool. @return TRUE if ok */ UNIV_INTERN diff --git a/storage/innodb_plugin/mem/mem0mem.c b/storage/innodb_plugin/mem/mem0mem.c index c0ce8a3e1ac..1dd4db30841 100644 --- a/storage/innodb_plugin/mem/mem0mem.c +++ b/storage/innodb_plugin/mem/mem0mem.c @@ -367,7 +367,7 @@ mem_heap_create_block( block->line = line; #ifdef MEM_PERIODIC_CHECK - mem_pool_mutex_enter(); + mutex_enter(&(mem_comm_pool->mutex)); if (!mem_block_list_inited) { mem_block_list_inited = TRUE; @@ -376,7 +376,7 @@ mem_heap_create_block( UT_LIST_ADD_LAST(mem_block_list, mem_block_list, block); - mem_pool_mutex_exit(); + mutex_exit(&(mem_comm_pool->mutex)); #endif mem_block_set_len(block, len); mem_block_set_type(block, type); @@ -479,11 +479,11 @@ mem_heap_block_free( UT_LIST_REMOVE(list, heap->base, block); #ifdef MEM_PERIODIC_CHECK - mem_pool_mutex_enter(); + mutex_enter(&(mem_comm_pool->mutex)); UT_LIST_REMOVE(mem_block_list, mem_block_list, block); - mem_pool_mutex_exit(); + mutex_exit(&(mem_comm_pool->mutex)); #endif ut_ad(heap->total_size >= block->len); @@ -556,7 +556,7 @@ mem_validate_all_blocks(void) { mem_block_t* block; - mem_pool_mutex_enter(); + mutex_enter(&(mem_comm_pool->mutex)); block = UT_LIST_GET_FIRST(mem_block_list); @@ -568,6 +568,6 @@ mem_validate_all_blocks(void) block = UT_LIST_GET_NEXT(mem_block_list, block); } - mem_pool_mutex_exit(); + mutex_exit(&(mem_comm_pool->mutex)); } #endif diff --git a/storage/innodb_plugin/mem/mem0pool.c b/storage/innodb_plugin/mem/mem0pool.c index c4f8af607e0..3291453eeb5 100644 --- a/storage/innodb_plugin/mem/mem0pool.c +++ b/storage/innodb_plugin/mem/mem0pool.c @@ -34,6 +34,7 @@ Created 5/12/1997 Heikki Tuuri #include "ut0lst.h" #include "ut0byte.h" #include "mem0mem.h" +#include "srv0start.h" /* We would like to use also the buffer frames to allocate memory. This would be desirable, because then the memory consumption of the database @@ -121,23 +122,33 @@ mysql@lists.mysql.com */ UNIV_INTERN ulint mem_n_threads_inside = 0; /********************************************************************//** -Reserves the mem pool mutex. */ -UNIV_INTERN +Reserves the mem pool mutex if we are not in server shutdown. Use +this function only in memory free functions, since only memory +free functions are used during server shutdown. */ +UNIV_INLINE void -mem_pool_mutex_enter(void) -/*======================*/ +mem_pool_mutex_enter( +/*=================*/ + mem_pool_t* pool) /*!< in: memory pool */ { - mutex_enter(&(mem_comm_pool->mutex)); + if (srv_shutdown_state < SRV_SHUTDOWN_EXIT_THREADS) { + mutex_enter(&(pool->mutex)); + } } /********************************************************************//** -Releases the mem pool mutex. */ -UNIV_INTERN +Releases the mem pool mutex if we are not in server shutdown. As +its corresponding mem_pool_mutex_enter() function, use it only +in memory free functions */ +UNIV_INLINE void -mem_pool_mutex_exit(void) -/*=====================*/ +mem_pool_mutex_exit( +/*================*/ + mem_pool_t* pool) /*!< in: memory pool */ { - mutex_exit(&(mem_comm_pool->mutex)); + if (srv_shutdown_state < SRV_SHUTDOWN_EXIT_THREADS) { + mutex_exit(&(pool->mutex)); + } } /********************************************************************//** @@ -567,7 +578,7 @@ mem_area_free( n = ut_2_log(size); - mutex_enter(&(pool->mutex)); + mem_pool_mutex_enter(pool); mem_n_threads_inside++; ut_a(mem_n_threads_inside == 1); @@ -595,7 +606,7 @@ mem_area_free( pool->reserved += ut_2_exp(n); mem_n_threads_inside--; - mutex_exit(&(pool->mutex)); + mem_pool_mutex_exit(pool); mem_area_free(new_ptr, pool); @@ -611,7 +622,7 @@ mem_area_free( } mem_n_threads_inside--; - mutex_exit(&(pool->mutex)); + mem_pool_mutex_exit(pool); ut_ad(mem_pool_validate(pool)); } @@ -630,7 +641,7 @@ mem_pool_validate( ulint free; ulint i; - mutex_enter(&(pool->mutex)); + mem_pool_mutex_enter(pool); free = 0; @@ -658,7 +669,7 @@ mem_pool_validate( ut_a(free + pool->reserved == pool->size); - mutex_exit(&(pool->mutex)); + mem_pool_mutex_exit(pool); return(TRUE); } diff --git a/storage/innodb_plugin/srv/srv0start.c b/storage/innodb_plugin/srv/srv0start.c index e517b9a86b0..ba9fc831b39 100644 --- a/storage/innodb_plugin/srv/srv0start.c +++ b/storage/innodb_plugin/srv/srv0start.c @@ -2018,9 +2018,13 @@ innobase_shutdown_for_mysql(void) pars_lexer_close(); log_mem_free(); buf_pool_free(); - ut_free_all_mem(); mem_close(); + /* ut_free_all_mem() frees all allocated memory not freed yet + in shutdown, and it will also free the ut_list_mutex, so it + should be the last one for all operation */ + ut_free_all_mem(); + if (os_thread_count != 0 || os_event_count != 0 || os_mutex_count != 0 From 0b217d901536c97ee9b7faf47999930bcc4b48dd Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Wed, 28 Jul 2010 21:56:15 +0400 Subject: [PATCH 108/115] Fix a failing assert when running funcs_1.innodb_trig_03 test. The failure was introduced by a precursor patch for the fix for Bug#52044. When opening tables for GRANT statement to check that subject columns exist, mysql_table_grant() would try to lock the tables, and thus start a transaction. This was unnecessary and lead to an assert. --- sql/sql_acl.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 0008968de2a..7163702d401 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -3072,7 +3072,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, class LEX_COLUMN *column; List_iterator column_iter(columns); - if (open_and_lock_tables(thd, table_list, TRUE, 0)) + if (open_normal_and_derived_tables(thd, table_list, 0)) DBUG_RETURN(TRUE); while ((column = column_iter++)) From 95ec38c5b10117aeb8ea0c24e4ba63aa8e9f3eee Mon Sep 17 00:00:00 2001 From: Gleb Shchepa Date: Thu, 29 Jul 2010 01:02:43 +0400 Subject: [PATCH 109/115] Bug #55472: Assertion failed in heap_rfirst function of hp_rfirst.c on DELETE statement Single-table delete ordered by a field that has a hash-type index may cause an assertion failure or a crash. An optimization added by the fix for the bug 36569 forced the optimizer to use ORDER BY-compatible indices when applicable. However, the existence of unsorted indices (HASH index algorithm for some engines such as MEMORY/HEAP, NDB) was ignored. The test_if_order_by_key function has been modified to skip unsorted indices. --- mysql-test/r/heap_hash.result | 11 +++++++++++ mysql-test/t/heap_hash.test | 17 +++++++++++++++++ sql/sql_select.cc | 2 +- 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/heap_hash.result b/mysql-test/r/heap_hash.result index 7cc76611fb8..e3a3e0e9740 100644 --- a/mysql-test/r/heap_hash.result +++ b/mysql-test/r/heap_hash.result @@ -382,3 +382,14 @@ INSERT INTO t1 VALUES('A ', 'A '); ERROR 23000: Duplicate entry 'A -A ' for key 'key1' DROP TABLE t1; End of 5.0 tests +# +# Bug #55472: Assertion failed in heap_rfirst function of hp_rfirst.c +# on DELETE statement +# +CREATE TABLE t1 (col_int_nokey INT, +col_int_key INT, +INDEX(col_int_key) USING HASH) ENGINE = HEAP; +INSERT INTO t1 (col_int_nokey, col_int_key) VALUES (3, 0), (4, 0), (3, 1); +DELETE FROM t1 WHERE col_int_nokey = 5 ORDER BY col_int_key LIMIT 2; +DROP TABLE t1; +End of 5.5 tests diff --git a/mysql-test/t/heap_hash.test b/mysql-test/t/heap_hash.test index 1e3491f89a9..748347021fc 100644 --- a/mysql-test/t/heap_hash.test +++ b/mysql-test/t/heap_hash.test @@ -284,3 +284,20 @@ INSERT INTO t1 VALUES('A ', 'A '); DROP TABLE t1; --echo End of 5.0 tests + +--echo # +--echo # Bug #55472: Assertion failed in heap_rfirst function of hp_rfirst.c +--echo # on DELETE statement +--echo # + +CREATE TABLE t1 (col_int_nokey INT, + col_int_key INT, + INDEX(col_int_key) USING HASH) ENGINE = HEAP; +INSERT INTO t1 (col_int_nokey, col_int_key) VALUES (3, 0), (4, 0), (3, 1); + +DELETE FROM t1 WHERE col_int_nokey = 5 ORDER BY col_int_key LIMIT 2; + +DROP TABLE t1; + +--echo End of 5.5 tests + diff --git a/sql/sql_select.cc b/sql/sql_select.cc index e18486d718a..9c9a436cc24 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -13244,7 +13244,7 @@ static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx, DBUG_RETURN(0); } - if (key_part->field != field) + if (key_part->field != field || !field->part_of_sortkey.is_set(idx)) DBUG_RETURN(0); /* set flag to 1 if we can use read-next on key, else to -1 */ From 2ad690fdf8a1141be8439a7e2d53974a0d3ec923 Mon Sep 17 00:00:00 2001 From: Date: Thu, 29 Jul 2010 11:00:57 +0800 Subject: [PATCH 110/115] BUG#49124 Security issue with /*!-versioned */ SQL statements on Slave /*![:version:] Query Code */, where [:version:] is a sequence of 5 digits representing the mysql server version(e.g /*!50200 ... */), is a special comment that the query in it can be executed on those servers whose versions are larger than the version appearing in the comment. It leads to a security issue when slave's version is larger than master's. A malicious user can improve his privileges on slaves. Because slave SQL thread is running with SUPER privileges, so it can execute queries that he/she does not have privileges on master. This bug is fixed with the logic below: - To replace '!' with ' ' in the magic comments which are not applied on master. So they become common comments and will not be applied on slave. - Example: 'INSERT INTO t1 VALUES (1) /*!10000, (2)*/ /*!99999 ,(3)*/ will be binlogged as 'INSERT INTO t1 VALUES (1) /*!10000, (2)*/ /* 99999 ,(3)*/ --- .../rpl/r/rpl_conditional_comments.result | 57 ++++++++++++++ .../suite/rpl/t/rpl_conditional_comments.test | 74 +++++++++++++++++++ sql/mysql_priv.h | 2 +- sql/sql_lex.cc | 19 ++++- sql/sql_lex.h | 20 ++++- sql/sql_parse.cc | 12 +-- sql/sql_partition.cc | 2 +- sql/sql_partition.h | 2 +- sql/table.h | 2 +- 9 files changed, 173 insertions(+), 17 deletions(-) create mode 100644 mysql-test/suite/rpl/r/rpl_conditional_comments.result create mode 100644 mysql-test/suite/rpl/t/rpl_conditional_comments.test diff --git a/mysql-test/suite/rpl/r/rpl_conditional_comments.result b/mysql-test/suite/rpl/r/rpl_conditional_comments.result new file mode 100644 index 00000000000..105d3bc59f3 --- /dev/null +++ b/mysql-test/suite/rpl/r/rpl_conditional_comments.result @@ -0,0 +1,57 @@ +stop slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +reset master; +reset slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +start slave; +CREATE TABLE t1(c1 INT); +show binlog events from ; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # use `test`; CREATE TABLE t1(c1 INT) + +# Case 1: +# ------------------------------------------------------------------ +# In a statement, some CCs are applied while others are not. The CCs +# which are not applied on master will be binlogged as common comments. +/*!99999 --- */INSERT /*!INTO*/ /*!10000 t1 */ VALUES(10) /*!99999 ,(11)*/; +show binlog events from ; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # use `test`; /* 99999 --- */INSERT /*!INTO*/ /*!10000 t1 */ VALUES(10) /* 99999 ,(11)*/ +Comparing tables master:test.t1 and slave:test.t1 + +# Case 2: +# ----------------------------------------------------------------- +# Verify whether it can be binlogged correctly when executing prepared +# statement. +PREPARE stmt FROM 'INSERT INTO /*!99999 blabla*/ t1 VALUES(60) /*!99999 ,(61)*/'; +EXECUTE stmt; +DROP TABLE t1; +CREATE TABLE t1(c1 INT); +EXECUTE stmt; +Comparing tables master:test.t1 and slave:test.t1 + +SET @value=62; +PREPARE stmt FROM 'INSERT INTO /*!99999 blabla */ t1 VALUES(?) /*!99999 ,(63)*/'; +EXECUTE stmt USING @value; +DROP TABLE t1; +CREATE TABLE t1(c1 INT); +EXECUTE stmt USING @value; +show binlog events from ; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # use `test`; INSERT INTO /* 99999 blabla*/ t1 VALUES(60) /* 99999 ,(61)*/ +master-bin.000001 # Query # # use `test`; DROP TABLE t1 +master-bin.000001 # Query # # use `test`; CREATE TABLE t1(c1 INT) +master-bin.000001 # Query # # use `test`; INSERT INTO /* 99999 blabla*/ t1 VALUES(60) /* 99999 ,(61)*/ +master-bin.000001 # Query # # use `test`; INSERT INTO /* 99999 blabla */ t1 VALUES(62) /* 99999 ,(63)*/ +master-bin.000001 # Query # # use `test`; DROP TABLE t1 +master-bin.000001 # Query # # use `test`; CREATE TABLE t1(c1 INT) +master-bin.000001 # Query # # use `test`; INSERT INTO /* 99999 blabla */ t1 VALUES(62) /* 99999 ,(63)*/ +Comparing tables master:test.t1 and slave:test.t1 + +# Case 3: +# ----------------------------------------------------------------- +# Verify it can restore the '!', if the it is an uncomplete conditional +# comments +SELECT c1 FROM /*!99999 t1 WHEREN; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '/*!99999 t1 WHEREN' at line 1 +DROP TABLE t1; diff --git a/mysql-test/suite/rpl/t/rpl_conditional_comments.test b/mysql-test/suite/rpl/t/rpl_conditional_comments.test new file mode 100644 index 00000000000..14251d5eb37 --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_conditional_comments.test @@ -0,0 +1,74 @@ +############################################################################### +# After the patch for BUG#49124: +# - Use ' ' instead of '!' in the conditional comments which are not applied on +# master. So they become common comments and will not be applied on slave. +# +# - Example: +# 'INSERT INTO t1 VALUES (1) /*!10000, (2)*/ /*!99999 ,(3)*/ +# will be binlogged as +# 'INSERT INTO t1 VALUES (1) /*!10000, (2)*/ /* 99999 ,(3)*/'. +############################################################################### +source include/master-slave.inc; +source include/have_binlog_format_statement.inc; + +CREATE TABLE t1(c1 INT); +source include/show_binlog_events.inc; +let $binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1); + +--echo +--echo # Case 1: +--echo # ------------------------------------------------------------------ +--echo # In a statement, some CCs are applied while others are not. The CCs +--echo # which are not applied on master will be binlogged as common comments. + +/*!99999 --- */INSERT /*!INTO*/ /*!10000 t1 */ VALUES(10) /*!99999 ,(11)*/; + +source include/show_binlog_events.inc; +let $binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1); +sync_slave_with_master; +let $diff_table_1=master:test.t1; +let $diff_table_2=slave:test.t1; +source include/diff_tables.inc; + +--echo +--echo # Case 2: +--echo # ----------------------------------------------------------------- +--echo # Verify whether it can be binlogged correctly when executing prepared +--echo # statement. +PREPARE stmt FROM 'INSERT INTO /*!99999 blabla*/ t1 VALUES(60) /*!99999 ,(61)*/'; +EXECUTE stmt; +DROP TABLE t1; +CREATE TABLE t1(c1 INT); +EXECUTE stmt; + +sync_slave_with_master; +let $diff_table_1=master:test.t1; +let $diff_table_2=slave:test.t1; +source include/diff_tables.inc; + +--echo +SET @value=62; +PREPARE stmt FROM 'INSERT INTO /*!99999 blabla */ t1 VALUES(?) /*!99999 ,(63)*/'; +EXECUTE stmt USING @value; +DROP TABLE t1; +CREATE TABLE t1(c1 INT); +EXECUTE stmt USING @value; + +source include/show_binlog_events.inc; +let $binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1); + +sync_slave_with_master; +let $diff_table_1=master:test.t1; +let $diff_table_2=slave:test.t1; +source include/diff_tables.inc; + +--echo +--echo # Case 3: +--echo # ----------------------------------------------------------------- +--echo # Verify it can restore the '!', if the it is an uncomplete conditional +--echo # comments +--error 1064 +SELECT c1 FROM /*!99999 t1 WHEREN; + +DROP TABLE t1; +source include/master-slave-end.inc; diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 88f3763ef50..9f2c0b04f2c 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1024,7 +1024,7 @@ bool mysql_opt_change_db(THD *thd, bool force_switch, bool *cur_db_changed); -void mysql_parse(THD *thd, const char *inBuf, uint length, +void mysql_parse(THD *thd, char *rawbuf, uint length, const char ** semicolon); bool mysql_test_parse_for_slave(THD *thd,char *inBuf,uint length); diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 6bfd6f3906c..2bff036b1f1 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -111,7 +111,7 @@ st_parsing_options::reset() } -bool Lex_input_stream::init(THD *thd, const char *buff, unsigned int length) +bool Lex_input_stream::init(THD *thd, char *buff, unsigned int length) { DBUG_EXECUTE_IF("bug42064_simulate_oom", DBUG_SET("+d,simulate_out_of_memory");); @@ -1292,11 +1292,10 @@ int MYSQLlex(void *arg, void *yythd) ulong version; version=strtol(version_str, NULL, 10); - /* Accept 'M' 'm' 'm' 'd' 'd' */ - lip->yySkipn(5); - if (version <= MYSQL_VERSION_ID) { + /* Accept 'M' 'm' 'm' 'd' 'd' */ + lip->yySkipn(5); /* Expand the content of the special comment as real code */ lip->set_echo(TRUE); state=MY_LEX_START; @@ -1304,7 +1303,19 @@ int MYSQLlex(void *arg, void *yythd) } else { + const char* version_mark= lip->get_ptr() - 1; + DBUG_ASSERT(*version_mark == '!'); + /* + Patch and skip the conditional comment to avoid it + being propagated infinitely (eg. to a slave). + */ + char *pcom= lip->yyUnput(' '); comment_closed= ! consume_comment(lip, 1); + if (! comment_closed) + { + DBUG_ASSERT(pcom == version_mark); + *pcom= '!'; + } /* version allowed to have one level of comment inside. */ } } diff --git a/sql/sql_lex.h b/sql/sql_lex.h index cefb0cb49fb..7403bb5a1a4 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -1180,7 +1180,7 @@ public: @retval FALSE OK @retval TRUE Error */ - bool init(THD *thd, const char *buff, unsigned int length); + bool init(THD *thd, char *buff, unsigned int length); /** Set the echo mode. @@ -1294,6 +1294,20 @@ public: m_ptr += n; } + /** + Puts a character back into the stream, canceling + the effect of the last yyGet() or yySkip(). + Note that the echo mode should not change between calls + to unput, get, or skip from the stream. + */ + char *yyUnput(char ch) + { + *--m_ptr= ch; + if (m_echo) + m_cpp_ptr--; + return m_ptr; + } + /** End of file indicator for the query text to parse. @return true if there are no more characters to parse @@ -1440,7 +1454,7 @@ public: private: /** Pointer to the current position in the raw input stream. */ - const char *m_ptr; + char *m_ptr; /** Starting position of the last token parsed, in the raw buffer. */ const char *m_tok_start; @@ -1972,7 +1986,7 @@ public: @retval FALSE OK @retval TRUE Error */ - bool init(THD *thd, const char *buff, unsigned int length) + bool init(THD *thd, char *buff, unsigned int length) { return m_lip.init(thd, buff, length); } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 0a509f62849..9ec03ea1d5f 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -5946,13 +5946,13 @@ void mysql_init_multi_delete(LEX *lex) Parse a query. @param thd Current thread - @param inBuf Begining of the query text + @param rawbuf Begining of the query text @param length Length of the query text @param[out] found_semicolon For multi queries, position of the character of the next query in the query text. */ -void mysql_parse(THD *thd, const char *inBuf, uint length, +void mysql_parse(THD *thd, char *rawbuf, uint length, const char ** found_semicolon) { DBUG_ENTER("mysql_parse"); @@ -5978,7 +5978,7 @@ void mysql_parse(THD *thd, const char *inBuf, uint length, lex_start(thd); mysql_reset_thd_for_next_command(thd); - if (query_cache_send_result_to_client(thd, (char*) inBuf, length) <= 0) + if (query_cache_send_result_to_client(thd, rawbuf, length) <= 0) { LEX *lex= thd->lex; @@ -5987,7 +5987,7 @@ void mysql_parse(THD *thd, const char *inBuf, uint length, Parser_state parser_state; bool err; - if (!(err= parser_state.init(thd, inBuf, length))) + if (!(err= parser_state.init(thd, rawbuf, length))) { err= parse_sql(thd, & parser_state, NULL); *found_semicolon= parser_state.m_lip.found_semicolon; @@ -6073,14 +6073,14 @@ void mysql_parse(THD *thd, const char *inBuf, uint length, 1 can be ignored */ -bool mysql_test_parse_for_slave(THD *thd, char *inBuf, uint length) +bool mysql_test_parse_for_slave(THD *thd, char *rawbuf, uint length) { LEX *lex= thd->lex; bool error= 0; DBUG_ENTER("mysql_test_parse_for_slave"); Parser_state parser_state; - if (!(error= parser_state.init(thd, inBuf, length))) + if (!(error= parser_state.init(thd, rawbuf, length))) { lex_start(thd); mysql_reset_thd_for_next_command(thd); diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 122ae661046..76caa2b0c8d 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -3876,7 +3876,7 @@ void get_partition_set(const TABLE *table, uchar *buf, const uint index, */ bool mysql_unpack_partition(THD *thd, - const char *part_buf, uint part_info_len, + char *part_buf, uint part_info_len, const char *part_state, uint part_state_len, TABLE* table, bool is_create_table_ind, handlerton *default_db_type, diff --git a/sql/sql_partition.h b/sql/sql_partition.h index b9efbf25a00..02a5ead1117 100644 --- a/sql/sql_partition.h +++ b/sql/sql_partition.h @@ -78,7 +78,7 @@ void get_full_part_id_from_key(const TABLE *table, uchar *buf, KEY *key_info, const key_range *key_spec, part_id_range *part_spec); -bool mysql_unpack_partition(THD *thd, const char *part_buf, +bool mysql_unpack_partition(THD *thd, char *part_buf, uint part_info_len, const char *part_state, uint part_state_len, TABLE *table, bool is_create_table_ind, diff --git a/sql/table.h b/sql/table.h index 3ef3c5e0cb2..8ea7175eec4 100644 --- a/sql/table.h +++ b/sql/table.h @@ -442,7 +442,7 @@ typedef struct st_table_share #ifdef WITH_PARTITION_STORAGE_ENGINE /** @todo: Move into *ha_data for partitioning */ bool auto_partitioned; - const char *partition_info; + char *partition_info; uint partition_info_len; uint partition_info_buffer_size; const char *part_state; From c7071a7214114e224fcd63b4be77867783185c4c Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Thu, 29 Jul 2010 10:12:44 +0400 Subject: [PATCH 111/115] Postfix for BUG#45012. Problem: The original patch didn't compile on debug_werror due to wrong format in printf("%d") for size_t variables. Fix: Adding cast to (int). --- unittest/strings/strings-t.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/unittest/strings/strings-t.c b/unittest/strings/strings-t.c index 2d246cfa17f..278f42e56a5 100644 --- a/unittest/strings/strings-t.c +++ b/unittest/strings/strings-t.c @@ -30,7 +30,7 @@ test_like_range_for_charset(CHARSET_INFO *cs, const char *src, size_t src_len) cs->coll->like_range(cs, src, src_len, '\\', '_', '%', sizeof(min_str), min_str, max_str, &min_len, &max_len); - diag("min_len=%d\tmax_len=%d\t%s", min_len, max_len, cs->name); + diag("min_len=%d\tmax_len=%d\t%s", (int) min_len, (int) max_len, cs->name); min_well_formed_len= cs->cset->well_formed_len(cs, min_str, min_str + min_len, 10000, &error); @@ -39,11 +39,11 @@ test_like_range_for_charset(CHARSET_INFO *cs, const char *src, size_t src_len) 10000, &error); if (min_len != min_well_formed_len) diag("Bad min_str: min_well_formed_len=%d min_str[%d]=0x%02X", - min_well_formed_len, min_well_formed_len, + (int) min_well_formed_len, (int) min_well_formed_len, (uchar) min_str[min_well_formed_len]); if (max_len != max_well_formed_len) diag("Bad max_str: max_well_formed_len=%d max_str[%d]=0x%02X", - max_well_formed_len, max_well_formed_len, + (int) max_well_formed_len, (int) max_well_formed_len, (uchar) max_str[max_well_formed_len]); return min_len == min_well_formed_len && From 3d1af939c30355fb835010be2a8a4de620b2b9a9 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Thu, 29 Jul 2010 12:35:12 +0400 Subject: [PATCH 112/115] Fix a Windows failure of main.merge test introduced by the precursor patch for Bug#52044. When passing the TABLE instance for invalidation to the query cache, we didn't always have a valid share (in case of error). Make sure we invalidate the table using TABLE_LIST, not TABLE, object. --- sql/sql_table.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sql/sql_table.cc b/sql/sql_table.cc index c4b4a37b35a..0447e1a24af 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -5144,7 +5144,8 @@ send_result_message: May be something modified. Consequently, we have to invalidate the query cache. */ - query_cache_invalidate3(thd, table->table, 0); + table->table= 0; // For query cache + query_cache_invalidate3(thd, table, 0); } } /* Error path, a admin command failed. */ @@ -5152,7 +5153,6 @@ send_result_message: trans_commit_implicit(thd); close_thread_tables(thd); thd->mdl_context.release_transactional_locks(); - table->table=0; // For query cache /* If it is CHECK TABLE v1, v2, v3, and v1, v2, v3 are views, we will run From f7c362df8c696a598d8be2250a3352c7aabb464a Mon Sep 17 00:00:00 2001 From: Alexander Nozdrin Date: Thu, 29 Jul 2010 19:35:22 +0400 Subject: [PATCH 113/115] Fix build failure. --- sql/ha_ndbcluster.cc | 1 + sql/ha_ndbcluster_binlog.cc | 1 + 2 files changed, 2 insertions(+) diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index 7cac8373bc4..d4a98265c49 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -36,6 +36,7 @@ #include "discover.h" // readfrm #include "sql_acl.h" // wild_case_compare #include "rpl_mi.h" +#include "transaction.h" /* There is an incompatibility between GNU ar and the Solaris linker diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc index b610687496e..26fdb8e1425 100644 --- a/sql/ha_ndbcluster_binlog.cc +++ b/sql/ha_ndbcluster_binlog.cc @@ -36,6 +36,7 @@ #include "lock.h" // MYSQL_LOCK_IGNORE_FLUSH, // mysql_unlock_tables #include "sql_parse.h" // mysql_parse +#include "transaction.h" #ifdef ndb_dynamite #undef assert From cb4db7d8cba3a5f91ca6d1f1cae4a62e61945da6 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Thu, 29 Jul 2010 20:15:37 +0400 Subject: [PATCH 114/115] Fix a compilation failure of ha_ndbcluster_binlog.cc. --- sql/ha_ndbcluster_binlog.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc index b610687496e..26fdb8e1425 100644 --- a/sql/ha_ndbcluster_binlog.cc +++ b/sql/ha_ndbcluster_binlog.cc @@ -36,6 +36,7 @@ #include "lock.h" // MYSQL_LOCK_IGNORE_FLUSH, // mysql_unlock_tables #include "sql_parse.h" // mysql_parse +#include "transaction.h" #ifdef ndb_dynamite #undef assert From c9d2f7ee728e06f9487dcf9aa8a95d09960c86f9 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Thu, 29 Jul 2010 20:29:01 +0400 Subject: [PATCH 115/115] Fix the tree name. --- .bzr-mysql/default.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.bzr-mysql/default.conf b/.bzr-mysql/default.conf index be5e1c348fa..4eab3d239d0 100644 --- a/.bzr-mysql/default.conf +++ b/.bzr-mysql/default.conf @@ -1,4 +1,4 @@ [MYSQL] post_commit_to = "commits@lists.mysql.com" post_push_to = "commits@lists.mysql.com" -tree_name = "mysql-trunk-runtime" +tree_name = "mysql-trunk-bugfixing"