From a699e4fce85a055e3ee55db12e9891e3b26ef720 Mon Sep 17 00:00:00 2001 From: Sunny Bains Date: Tue, 30 Nov 2010 20:11:26 +1100 Subject: Fix Bug# 56228 - Dropping tables from within an active statement crashes server InnoDB AUTOINC code expects the locks to be released in strict reverse order at the end of the statement. However, nested stored proedures and partition tables break this rule. We now allow the locks to be deleted from the trx->autoinc_locks vector in any order but optimise for the common (old) case. rb://441 Approved by Marko Makela --- storage/innodb_plugin/include/ut0vec.h | 19 ++++++++ storage/innodb_plugin/include/ut0vec.ic | 29 ++++++++++++ storage/innodb_plugin/lock/lock0lock.c | 78 +++++++++++++++++++++++++++++++-- 3 files changed, 123 insertions(+), 3 deletions(-) (limited to 'storage/innodb_plugin') diff --git a/storage/innodb_plugin/include/ut0vec.h b/storage/innodb_plugin/include/ut0vec.h index a770f671cfc..0f8b955b098 100644 --- a/storage/innodb_plugin/include/ut0vec.h +++ b/storage/innodb_plugin/include/ut0vec.h @@ -93,6 +93,25 @@ ib_vector_get( ib_vector_t* vec, /*!< in: vector */ ulint n); /*!< in: element index to get */ +/****************************************************************//** +Get last element. The vector must not be empty. +@return last element */ +UNIV_INLINE +void* +ib_vector_get_last( +/*===============*/ + ib_vector_t* vec); /*!< in: vector */ + +/****************************************************************//** +Set the n'th element. */ +UNIV_INLINE +void +ib_vector_set( +/*==========*/ + ib_vector_t* vec, /*!< in/out: vector */ + ulint n, /*!< in: element index to set */ + void* elem); /*!< in: data element */ + /****************************************************************//** Remove the last element from the vector. */ UNIV_INLINE diff --git a/storage/innodb_plugin/include/ut0vec.ic b/storage/innodb_plugin/include/ut0vec.ic index 02e881f9bca..34c858868ce 100644 --- a/storage/innodb_plugin/include/ut0vec.ic +++ b/storage/innodb_plugin/include/ut0vec.ic @@ -50,6 +50,35 @@ ib_vector_get( return(vec->data[n]); } +/****************************************************************//** +Get last element. The vector must not be empty. +@return last element */ +UNIV_INLINE +void* +ib_vector_get_last( +/*===============*/ + ib_vector_t* vec) /*!< in: vector */ +{ + ut_a(vec->used > 0); + + return(vec->data[vec->used - 1]); +} + +/****************************************************************//** +Set the n'th element. */ +UNIV_INLINE +void +ib_vector_set( +/*==========*/ + ib_vector_t* vec, /*!< in/out: vector */ + ulint n, /*!< in: element index to set */ + void* elem) /*!< in: data element */ +{ + ut_a(n < vec->used); + + vec->data[n] = elem; +} + /****************************************************************//** Remove the last element from the vector. @return last vector element */ diff --git a/storage/innodb_plugin/lock/lock0lock.c b/storage/innodb_plugin/lock/lock0lock.c index 77d69d11a2d..c8bbc5c02bd 100644 --- a/storage/innodb_plugin/lock/lock0lock.c +++ b/storage/innodb_plugin/lock/lock0lock.c @@ -3624,6 +3624,80 @@ lock_table_create( return(lock); } +/*************************************************************//** +Pops autoinc lock requests from the transaction's autoinc_locks. We +handle the case where there are gaps in the array and they need to +be popped off the stack. */ +UNIV_INLINE +void +lock_table_pop_autoinc_locks( +/*=========================*/ + trx_t* trx) /*!< in/out: transaction that owns the AUTOINC locks */ +{ + ut_ad(mutex_own(&kernel_mutex)); + ut_ad(!ib_vector_is_empty(trx->autoinc_locks)); + + /* Skip any gaps, gaps are NULL lock entries in the + trx->autoinc_locks vector. */ + + do { + ib_vector_pop(trx->autoinc_locks); + + if (ib_vector_is_empty(trx->autoinc_locks)) { + return; + } + + } while (ib_vector_get_last(trx->autoinc_locks) == NULL); +} + +/*************************************************************//** +Removes an autoinc lock request from the transaction's autoinc_locks. */ +UNIV_INLINE +void +lock_table_remove_autoinc_lock( +/*===========================*/ + lock_t* lock, /*!< in: table lock */ + trx_t* trx) /*!< in/out: transaction that owns the lock */ +{ + lock_t* autoinc_lock; + lint i = ib_vector_size(trx->autoinc_locks) - 1; + + ut_ad(mutex_own(&kernel_mutex)); + ut_ad(lock_get_mode(lock) == LOCK_AUTO_INC); + ut_ad(lock_get_type_low(lock) & LOCK_TABLE); + ut_ad(!ib_vector_is_empty(trx->autoinc_locks)); + + /* With stored functions and procedures the user may drop + a table within the same "statement". This special case has + to be handled by deleting only those AUTOINC locks that were + held by the table being dropped. */ + + autoinc_lock = ib_vector_get(trx->autoinc_locks, i); + + /* This is the default fast case. */ + + if (autoinc_lock == lock) { + lock_table_pop_autoinc_locks(trx); + } else { + /* The last element should never be NULL */ + ut_a(autoinc_lock != NULL); + + /* Handle freeing the locks from within the stack. */ + + while (--i >= 0) { + autoinc_lock = ib_vector_get(trx->autoinc_locks, i); + + if (UNIV_LIKELY(autoinc_lock == lock)) { + ib_vector_set(trx->autoinc_locks, i, NULL); + return; + } + } + + /* Must find the autoinc lock. */ + ut_error; + } +} + /*************************************************************//** Removes a table lock request from the queue and the trx list of locks; this is a low-level function which does NOT check if waiting requests @@ -3663,10 +3737,8 @@ lock_table_remove_low( if (!lock_get_wait(lock) && !ib_vector_is_empty(trx->autoinc_locks)) { - lock_t* autoinc_lock; - autoinc_lock = ib_vector_pop(trx->autoinc_locks); - ut_a(autoinc_lock == lock); + lock_table_remove_autoinc_lock(lock, trx); } ut_a(table->n_waiting_or_granted_auto_inc_locks > 0); -- cgit v1.2.1 From 95cc85dbf515991ea1b19307cacce5c4ba24f21d Mon Sep 17 00:00:00 2001 From: Jimmy Yang Date: Thu, 6 Jan 2011 23:45:59 -0800 Subject: Backport Bug #58643 InnoDB: too long table name. Also fix Bug #59312 examine MAX_FULL_NAME_LEN in InnoDB to address possible insufficient name buffer Bug #59312 Approved by Sunny Bains --- storage/innodb_plugin/ChangeLog | 10 ++++++++++ storage/innodb_plugin/dict/dict0dict.c | 10 +++++----- storage/innodb_plugin/handler/ha_innodb.cc | 10 ++++++++++ storage/innodb_plugin/handler/i_s.cc | 11 +---------- storage/innodb_plugin/include/univ.i | 12 ++++++++++++ storage/innodb_plugin/row/row0merge.c | 4 ++-- 6 files changed, 40 insertions(+), 17 deletions(-) (limited to 'storage/innodb_plugin') diff --git a/storage/innodb_plugin/ChangeLog b/storage/innodb_plugin/ChangeLog index 5ca60eb73d5..34d0fe7f6ef 100644 --- a/storage/innodb_plugin/ChangeLog +++ b/storage/innodb_plugin/ChangeLog @@ -1,3 +1,13 @@ +2011-01-06 The InnoDB Team + * row/row0merge.c: + Fix Bug#59312 Examine MAX_FULL_NAME_LEN in InnoDB to address + possible insufficient name buffer + +2011-01-06 The InnoDB Team + * dict/dict0dict.c, handler/ha_innodb.cc, handler/i_s.cc, + include/univ.i: + Fix Bug#58643 InnoDB: too long table name + 2010-11-11 The InnoDB Team * thr/thr0loc.c, trx/trx0i_s.c: Fix Bug#57802 Empty ASSERTION parameter passed to the HASH_SEARCH macro diff --git a/storage/innodb_plugin/dict/dict0dict.c b/storage/innodb_plugin/dict/dict0dict.c index eb3169bd176..2e936e27065 100644 --- a/storage/innodb_plugin/dict/dict0dict.c +++ b/storage/innodb_plugin/dict/dict0dict.c @@ -932,7 +932,7 @@ dict_table_rename_in_cache( dict_foreign_t* foreign; dict_index_t* index; ulint fold; - char old_name[MAX_TABLE_NAME_LEN + 1]; + char old_name[MAX_FULL_NAME_LEN + 1]; ut_ad(table); ut_ad(mutex_own(&(dict_sys->mutex))); @@ -944,7 +944,7 @@ dict_table_rename_in_cache( ut_print_timestamp(stderr); fprintf(stderr, "InnoDB: too long table name: '%s', " "max length is %d\n", table->name, - MAX_TABLE_NAME_LEN); + MAX_FULL_NAME_LEN); ut_error; } @@ -994,11 +994,11 @@ dict_table_rename_in_cache( ut_fold_string(old_name), table); if (strlen(new_name) > strlen(table->name)) { - /* We allocate MAX_TABLE_NAME_LEN+1 bytes here to avoid + /* We allocate MAX_FULL_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); + ut_a(strlen(new_name) <= MAX_FULL_NAME_LEN); + table->name = ut_realloc(table->name, MAX_FULL_NAME_LEN + 1); } memcpy(table->name, new_name, strlen(new_name) + 1); diff --git a/storage/innodb_plugin/handler/ha_innodb.cc b/storage/innodb_plugin/handler/ha_innodb.cc index 5965bd0e59e..c0e3163d717 100644 --- a/storage/innodb_plugin/handler/ha_innodb.cc +++ b/storage/innodb_plugin/handler/ha_innodb.cc @@ -6009,6 +6009,16 @@ create_table_def( DBUG_RETURN(HA_ERR_GENERIC); } + /* MySQL does the name length check. But we do additional check + on the name length here */ + if (strlen(table_name) > MAX_FULL_NAME_LEN) { + push_warning_printf( + (THD*) trx->mysql_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_TABLE_NAME, + "InnoDB: Table Name or Database Name is too long"); + DBUG_RETURN(ER_TABLE_NAME); + } + n_cols = form->s->fields; /* We pass 0 as the space id, and determine at a lower level the space diff --git a/storage/innodb_plugin/handler/i_s.cc b/storage/innodb_plugin/handler/i_s.cc index 9ad2d656365..0ef4f77ed3e 100644 --- a/storage/innodb_plugin/handler/i_s.cc +++ b/storage/innodb_plugin/handler/i_s.cc @@ -579,16 +579,7 @@ fill_innodb_locks_from_cache( for (i = 0; i < rows_num; i++) { i_s_locks_row_t* row; - - /* note that the decoded database or table name is - never expected to be longer than NAME_LEN; - NAME_LEN for database name - 2 for surrounding quotes around database name - NAME_LEN for table name - 2 for surrounding quotes around table name - 1 for the separating dot (.) - 9 for the #mysql50# prefix */ - char buf[2 * NAME_LEN + 14]; + char buf[MAX_FULL_NAME_LEN + 1]; const char* bufend; char lock_trx_id[TRX_ID_MAX_LEN + 1]; diff --git a/storage/innodb_plugin/include/univ.i b/storage/innodb_plugin/include/univ.i index cab3af5297e..4e74411628e 100644 --- a/storage/innodb_plugin/include/univ.i +++ b/storage/innodb_plugin/include/univ.i @@ -296,6 +296,18 @@ number does not include a terminating '\0'. InnoDB probably can handle longer names internally */ #define MAX_TABLE_NAME_LEN 192 +/* The maximum length of a database name. Like MAX_TABLE_NAME_LEN this is +the MySQL's NAME_LEN, see check_and_convert_db_name(). */ +#define MAX_DATABASE_NAME_LEN MAX_TABLE_NAME_LEN + +/* MAX_FULL_NAME_LEN defines the full name path including the +database name and table name. In addition, 14 bytes is added for: + 2 for surrounding quotes around table name + 1 for the separating dot (.) + 9 for the #mysql50# prefix */ +#define MAX_FULL_NAME_LEN \ + (MAX_TABLE_NAME_LEN + MAX_DATABASE_NAME_LEN + 14) + /* UNIVERSAL TYPE DEFINITIONS ========================== diff --git a/storage/innodb_plugin/row/row0merge.c b/storage/innodb_plugin/row/row0merge.c index 160edd32fbf..647d0031635 100644 --- a/storage/innodb_plugin/row/row0merge.c +++ b/storage/innodb_plugin/row/row0merge.c @@ -2341,7 +2341,7 @@ row_merge_rename_tables( { ulint err = DB_ERROR; pars_info_t* info; - char old_name[MAX_TABLE_NAME_LEN + 1]; + char old_name[MAX_FULL_NAME_LEN + 1]; ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); ut_ad(old_table != new_table); @@ -2356,7 +2356,7 @@ row_merge_rename_tables( ut_print_timestamp(stderr); fprintf(stderr, "InnoDB: too long table name: '%s', " "max length is %d\n", old_table->name, - MAX_TABLE_NAME_LEN); + MAX_FULL_NAME_LEN); ut_error; } -- cgit v1.2.1