diff options
Diffstat (limited to 'storage/xtradb/handler/ha_innodb.cc')
-rw-r--r-- | storage/xtradb/handler/ha_innodb.cc | 269 |
1 files changed, 237 insertions, 32 deletions
diff --git a/storage/xtradb/handler/ha_innodb.cc b/storage/xtradb/handler/ha_innodb.cc index 55b9aaff109..f3ba44f1139 100644 --- a/storage/xtradb/handler/ha_innodb.cc +++ b/storage/xtradb/handler/ha_innodb.cc @@ -26,8 +26,8 @@ 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 +this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA *****************************************************************************/ @@ -778,6 +778,12 @@ thd_to_trx( return(*(trx_t**) thd_ha_data(thd, innodb_hton_ptr)); } +my_bool +ha_innobase::is_fake_change_enabled(THD* thd) +{ + trx_t* trx = thd_to_trx(thd); + return (trx && trx->fake_changes); +} /********************************************************************//** Call this function when mysqld passes control to the client. That is to avoid deadlocks on the adaptive hash S-latch possibly held by thd. For more @@ -924,6 +930,9 @@ convert_error_code_to_mysql( case DB_OUT_OF_FILE_SPACE: return(HA_ERR_RECORD_FILE_FULL); + case DB_TABLE_IN_FK_CHECK: + return(HA_ERR_TABLE_IN_FK_CHECK); + case DB_TABLE_IS_BEING_USED: return(HA_ERR_WRONG_COMMAND); @@ -1095,6 +1104,20 @@ innobase_get_charset( } /**********************************************************************//** +Get the current setting of the lower_case_table_names global parameter from +mysqld.cc. We do a dirty read because for one there is no synchronization +object and secondly there is little harm in doing so even if we get a torn +read. +@return value of lower_case_table_names */ +ulint +innobase_get_lower_case_table_names(void) +/*=====================================*/ +{ + return(lower_case_table_names); +} + + +/**********************************************************************//** Determines the current SQL statement. @return SQL statement string */ extern "C" UNIV_INTERN @@ -2634,7 +2657,6 @@ skip_overwrite: /* 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); @@ -3366,51 +3388,139 @@ ha_innobase::primary_key_is_clustered() return(true); } +/** Always normalize table name to lower case on Windows */ +#ifdef __WIN__ +#define normalize_table_name(norm_name, name) \ + normalize_table_name_low(norm_name, name, TRUE) +#else +#define normalize_table_name(norm_name, name) \ + normalize_table_name_low(norm_name, name, FALSE) +#endif /* __WIN__ */ + /*****************************************************************//** Normalizes a table name string. A normalized name consists of the database name catenated to '/' and table name. An example: test/mytable. On Windows normalization puts both the database name and the -table name always to lower case. */ +table name always to lower case if "set_lower_case" is set to TRUE. */ static void -normalize_table_name( -/*=================*/ +normalize_table_name_low( +/*=====================*/ char* norm_name, /*!< out: normalized name as a null-terminated string */ - const char* name) /*!< in: table name string */ + const char* name, /*!< in: table name string */ + ibool set_lower_case) /*!< in: TRUE if we want to set + name to lower case */ { char* name_ptr; char* db_ptr; + ulint db_len; char* ptr; /* Scan name from the end */ - ptr = strend(name)-1; + ptr = strend(name) - 1; + /* seek to the last path separator */ while (ptr >= name && *ptr != '\\' && *ptr != '/') { ptr--; } name_ptr = ptr + 1; - DBUG_ASSERT(ptr > name); + /* skip any number of path separators */ + while (ptr >= name && (*ptr == '\\' || *ptr == '/')) { + ptr--; + } - ptr--; + DBUG_ASSERT(ptr >= name); + /* seek to the last but one path separator or one char before + the beginning of name */ + db_len = 0; while (ptr >= name && *ptr != '\\' && *ptr != '/') { ptr--; + db_len++; } db_ptr = ptr + 1; - memcpy(norm_name, db_ptr, strlen(name) + 1 - (db_ptr - name)); + memcpy(norm_name, db_ptr, db_len); - norm_name[name_ptr - db_ptr - 1] = '/'; + norm_name[db_len] = '/'; -#ifdef __WIN__ - innobase_casedn_str(norm_name); -#endif + memcpy(norm_name + db_len + 1, name_ptr, strlen(name_ptr) + 1); + + if (set_lower_case) { + innobase_casedn_str(norm_name); + } +} + +#if !defined(DBUG_OFF) +/********************************************************************* +Test normalize_table_name_low(). */ +static +void +test_normalize_table_name_low() +/*===========================*/ +{ + char norm_name[128]; + const char* test_data[][2] = { + /* input, expected result */ + {"./mysqltest/t1", "mysqltest/t1"}, + {"./test/#sql-842b_2", "test/#sql-842b_2"}, + {"./test/#sql-85a3_10", "test/#sql-85a3_10"}, + {"./test/#sql2-842b-2", "test/#sql2-842b-2"}, + {"./test/bug29807", "test/bug29807"}, + {"./test/foo", "test/foo"}, + {"./test/innodb_bug52663", "test/innodb_bug52663"}, + {"./test/t", "test/t"}, + {"./test/t1", "test/t1"}, + {"./test/t10", "test/t10"}, + {"/a/b/db/table", "db/table"}, + {"/a/b/db///////table", "db/table"}, + {"/a/b////db///////table", "db/table"}, + {"/var/tmp/mysqld.1/#sql842b_2_10", "mysqld.1/#sql842b_2_10"}, + {"db/table", "db/table"}, + {"ddd/t", "ddd/t"}, + {"d/ttt", "d/ttt"}, + {"d/t", "d/t"}, + {".\\mysqltest\\t1", "mysqltest/t1"}, + {".\\test\\#sql-842b_2", "test/#sql-842b_2"}, + {".\\test\\#sql-85a3_10", "test/#sql-85a3_10"}, + {".\\test\\#sql2-842b-2", "test/#sql2-842b-2"}, + {".\\test\\bug29807", "test/bug29807"}, + {".\\test\\foo", "test/foo"}, + {".\\test\\innodb_bug52663", "test/innodb_bug52663"}, + {".\\test\\t", "test/t"}, + {".\\test\\t1", "test/t1"}, + {".\\test\\t10", "test/t10"}, + {"C:\\a\\b\\db\\table", "db/table"}, + {"C:\\a\\b\\db\\\\\\\\\\\\\\table", "db/table"}, + {"C:\\a\\b\\\\\\\\db\\\\\\\\\\\\\\table", "db/table"}, + {"C:\\var\\tmp\\mysqld.1\\#sql842b_2_10", "mysqld.1/#sql842b_2_10"}, + {"db\\table", "db/table"}, + {"ddd\\t", "ddd/t"}, + {"d\\ttt", "d/ttt"}, + {"d\\t", "d/t"}, + }; + + for (size_t i = 0; i < UT_ARR_SIZE(test_data); i++) { + printf("test_normalize_table_name_low(): " + "testing \"%s\", expected \"%s\"... ", + test_data[i][0], test_data[i][1]); + + normalize_table_name_low(norm_name, test_data[i][0], FALSE); + + if (strcmp(norm_name, test_data[i][1]) == 0) { + printf("ok\n"); + } else { + printf("got \"%s\"\n", norm_name); + ut_error; + } + } } +#endif /* !DBUG_OFF */ /********************************************************************//** Get the upper limit of the MySQL integral and floating-point type. @@ -3803,6 +3913,8 @@ ha_innobase::open( THD* thd; ulint retries = 0; char* is_part = NULL; + ibool par_case_name_set = FALSE; + char par_case_name[MAX_FULL_NAME_LEN + 1]; DBUG_ENTER("ha_innobase::open"); @@ -3855,11 +3967,16 @@ ha_innobase::open( workaround for http://bugs.mysql.com/bug.php?id=33349. Look at support issue https://support.mysql.com/view.php?id=21080 for more details. */ +#ifdef __WIN__ + is_part = strstr(norm_name, "#p#"); +#else is_part = strstr(norm_name, "#P#"); +#endif /* __WIN__ */ + retry: /* Get pointer to a table object in InnoDB dictionary cache */ ib_table = dict_table_get(norm_name, TRUE); - + if (srv_pass_corrupt_table <= 1 && ib_table && ib_table->is_corrupt) { free_share(share); my_free(upd_buff, MYF(0)); @@ -3869,15 +3986,77 @@ retry: share->ib_table = ib_table; - - - - if (NULL == ib_table) { if (is_part && retries < 10) { - ++retries; - os_thread_sleep(100000); - goto retry; + /* MySQL partition engine hard codes the file name + separator as "#P#". The text case is fixed even if + lower_case_table_names is set to 1 or 2. This is true + for sub-partition names as well. InnoDB always + normalises file names to lower case on Windows, this + can potentially cause problems when copying/moving + tables between platforms. + + 1) If boot against an installation from Windows + platform, then its partition table name could + be all be in lower case in system tables. So we + will need to check lower case name when load table. + + 2) If we boot an installation from other case + sensitive platform in Windows, we might need to + check the existence of table name without lowering + case them in the system table. */ + if (innobase_get_lower_case_table_names() == 1) { + + if (!par_case_name_set) { +#ifndef __WIN__ + /* Check for the table using lower + case name, including the partition + separator "P" */ + memcpy(par_case_name, norm_name, + strlen(norm_name)); + par_case_name[strlen(norm_name)] = 0; + innobase_casedn_str(par_case_name); +#else + /* On Windows platfrom, check + whether there exists table name in + system table whose name is + not being normalized to lower case */ + normalize_table_name_low( + par_case_name, name, FALSE); +#endif + par_case_name_set = TRUE; + } + + ib_table = dict_table_get( + par_case_name, FALSE); + } + if (!ib_table) { + ++retries; + os_thread_sleep(100000); + goto retry; + } else { +#ifndef __WIN__ + sql_print_warning("Partition table %s opened " + "after converting to lower " + "case. The table may have " + "been moved from a case " + "in-sensitive file system. " + "Please recreate table in " + "the current file system\n", + norm_name); +#else + sql_print_warning("Partition table %s opened " + "after skipping the step to " + "lower case the table name. " + "The table may have been " + "moved from a case sensitive " + "file system. Please " + "recreate table in the " + "current file system\n", + norm_name); +#endif + goto table_opened; + } } if (is_part) { @@ -3908,6 +4087,8 @@ retry: DBUG_RETURN(HA_ERR_NO_SUCH_TABLE); } +table_opened: + if (ib_table->ibd_file_missing && !thd_tablespace_op(thd)) { sql_print_error("MySQL is trying to open a table handle but " "the .ibd file for\ntable %s does not exist.\n" @@ -5183,8 +5364,7 @@ no_commit: switch (sql_command) { case SQLCOM_LOAD: - if ((trx->duplicates - & (TRX_DUP_IGNORE | TRX_DUP_REPLACE))) { + if (trx->duplicates) { goto set_max_autoinc; } @@ -5472,8 +5652,7 @@ ha_innobase::update_row( && table->next_number_field && new_row == table->record[0] && thd_sql_command(user_thd) == SQLCOM_INSERT - && (trx->duplicates & (TRX_DUP_IGNORE | TRX_DUP_REPLACE)) - == TRX_DUP_IGNORE) { + && trx->duplicates) { ulonglong auto_inc; ulonglong col_max_value; @@ -5819,6 +5998,7 @@ ha_innobase::index_read( DBUG_ENTER("index_read"); ut_a(prebuilt->trx == thd_to_trx(user_thd)); + ut_ad(key_len != 0 || find_flag != HA_READ_KEY_EXACT); ha_statistic_increment(&SSV::ha_read_key_count); @@ -5855,6 +6035,7 @@ ha_innobase::index_read( (byte*) key_ptr, (ulint) key_len, prebuilt->trx); + DBUG_ASSERT(prebuilt->search_tuple->n_fields > 0); } else { /* We position the cursor to the last or the first entry in the index */ @@ -6997,6 +7178,8 @@ ha_innobase::create( DBUG_RETURN(HA_ERR_TO_BIG_ROW); } + ut_a(strlen(name) < sizeof(name2)); + strcpy(name2, name); normalize_table_name(norm_name, name2); @@ -7429,6 +7612,11 @@ ha_innobase::delete_table( DBUG_ENTER("ha_innobase::delete_table"); + DBUG_EXECUTE_IF( + "test_normalize_table_name_low", + test_normalize_table_name_low(); + ); + /* Strangely, MySQL passes the table name without the '.frm' extension, in contrast to ::create */ normalize_table_name(norm_name, name); @@ -7726,6 +7914,7 @@ ha_innobase::records_in_range( mem_heap_t* heap; DBUG_ENTER("records_in_range"); + DBUG_ASSERT(min_key || max_key); ut_a(prebuilt->trx == thd_to_trx(ha_thd())); @@ -7771,6 +7960,9 @@ ha_innobase::records_in_range( (const uchar*) 0), (ulint) (min_key ? min_key->length : 0), prebuilt->trx); + DBUG_ASSERT(min_key + ? range_start->n_fields > 0 + : range_start->n_fields == 0); row_sel_convert_mysql_key_to_innobase( range_end, (byte*) key_val_buff2, @@ -7779,6 +7971,9 @@ ha_innobase::records_in_range( (const uchar*) 0), (ulint) (max_key ? max_key->length : 0), prebuilt->trx); + DBUG_ASSERT(max_key + ? range_end->n_fields > 0 + : range_end->n_fields == 0); mode1 = convert_search_mode_to_innobase(min_key ? min_key->flag : HA_READ_KEY_EXACT); @@ -8961,6 +9156,7 @@ ha_innobase::extra( break; case HA_EXTRA_RESET_STATE: reset_template(prebuilt); + thd_to_trx(ha_thd())->duplicates = 0; break; case HA_EXTRA_NO_KEYREAD: prebuilt->read_just_key = 0; @@ -8978,19 +9174,18 @@ ha_innobase::extra( parameters below. We must not invoke update_thd() either, because the calling threads may change. CAREFUL HERE, OR MEMORY CORRUPTION MAY OCCUR! */ - case HA_EXTRA_IGNORE_DUP_KEY: + case HA_EXTRA_INSERT_WITH_UPDATE: thd_to_trx(ha_thd())->duplicates |= TRX_DUP_IGNORE; break; + case HA_EXTRA_NO_IGNORE_DUP_KEY: + thd_to_trx(ha_thd())->duplicates &= ~TRX_DUP_IGNORE; + break; case HA_EXTRA_WRITE_CAN_REPLACE: thd_to_trx(ha_thd())->duplicates |= TRX_DUP_REPLACE; break; case HA_EXTRA_WRITE_CANNOT_REPLACE: thd_to_trx(ha_thd())->duplicates &= ~TRX_DUP_REPLACE; break; - case HA_EXTRA_NO_IGNORE_DUP_KEY: - thd_to_trx(ha_thd())->duplicates &= - ~(TRX_DUP_IGNORE | TRX_DUP_REPLACE); - break; default:/* Do nothing */ ; } @@ -11905,6 +12100,13 @@ static MYSQL_SYSVAR_ULINT(lazy_drop_table, srv_lazy_drop_table, "e.g. for http://bugs.mysql.com/51325", NULL, NULL, 0, 0, 1, 0); +#ifdef UNIV_DEBUG +static MYSQL_SYSVAR_UINT(trx_rseg_n_slots_debug, trx_rseg_n_slots_debug, + PLUGIN_VAR_RQCMDARG, + "Debug flags for InnoDB to limit TRX_RSEG_N_SLOTS for trx_rsegf_undo_find_free()", + NULL, NULL, 0, 0, 1024, 0); +#endif /* UNIV_DEBUG */ + static struct st_mysql_sys_var* innobase_system_variables[]= { MYSQL_SYSVAR(page_size), MYSQL_SYSVAR(log_block_size), @@ -12001,6 +12203,9 @@ static struct st_mysql_sys_var* innobase_system_variables[]= { MYSQL_SYSVAR(pass_corrupt_table), MYSQL_SYSVAR(lazy_drop_table), MYSQL_SYSVAR(fake_changes), +#ifdef UNIV_DEBUG + MYSQL_SYSVAR(trx_rseg_n_slots_debug), +#endif /* UNIV_DEBUG */ NULL }; |