diff --git a/storage/xtradb/btr/btr0btr.cc b/storage/xtradb/btr/btr0btr.cc index 7e357f0a1cf..ee36302b75b 100644 --- a/storage/xtradb/btr/btr0btr.cc +++ b/storage/xtradb/btr/btr0btr.cc @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1994, 2013, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 1994, 2015, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2012, Facebook Inc. This program is free software; you can redistribute it and/or modify it under @@ -2143,7 +2143,7 @@ the tuple. It is assumed that mtr contains an x-latch on the tree. NOTE that the operation of this function must always succeed, we cannot reverse it: therefore enough free disk space must be guaranteed to be available before this function is called. -@return inserted record */ +@return inserted record or NULL if run out of space */ UNIV_INTERN rec_t* btr_root_raise_and_insert( @@ -2204,6 +2204,11 @@ btr_root_raise_and_insert( level = btr_page_get_level(root, mtr); new_block = btr_page_alloc(index, 0, FSP_NO_DIR, level, mtr, mtr); + + if (new_block == NULL && os_has_said_disk_full) { + return(NULL); + } + new_page = buf_block_get_frame(new_block); new_page_zip = buf_block_get_page_zip(new_block); ut_a(!new_page_zip == !root_page_zip); @@ -2980,7 +2985,7 @@ function must always succeed, we cannot reverse it: therefore enough free disk space (2 pages) must be guaranteed to be available before this function is called. -@return inserted record */ +@return inserted record or NULL if run out of space */ UNIV_INTERN rec_t* btr_page_split_and_insert( @@ -3094,9 +3099,18 @@ func_start: } } + DBUG_EXECUTE_IF("disk_is_full", + os_has_said_disk_full = true; + return(NULL);); + /* 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, mtr); + + if (new_block == NULL && os_has_said_disk_full) { + return(NULL); + } + new_page = buf_block_get_frame(new_block); new_page_zip = buf_block_get_page_zip(new_block); btr_page_create(new_block, new_page_zip, cursor->index, diff --git a/storage/xtradb/btr/btr0cur.cc b/storage/xtradb/btr/btr0cur.cc index 4c6fdd67c95..6e6e2e08390 100644 --- a/storage/xtradb/btr/btr0cur.cc +++ b/storage/xtradb/btr/btr0cur.cc @@ -1733,6 +1733,10 @@ btr_cur_pessimistic_insert( flags, cursor, offsets, heap, entry, n_ext, mtr); } + if (*rec == NULL && os_has_said_disk_full) { + return(DB_OUT_OF_FILE_SPACE); + } + ut_ad(page_rec_get_next(btr_cur_get_rec(cursor)) == *rec); if (!(flags & BTR_NO_LOCKING_FLAG)) { diff --git a/storage/xtradb/buf/buf0flu.cc b/storage/xtradb/buf/buf0flu.cc index d9893fc49b8..97c86ee7ed8 100644 --- a/storage/xtradb/buf/buf0flu.cc +++ b/storage/xtradb/buf/buf0flu.cc @@ -2209,9 +2209,10 @@ Clears up tail of the LRU lists: * Put replaceable pages at the tail of LRU to the free list * Flush dirty pages at the tail of LRU to the disk The depth to which we scan each buffer pool is controlled by dynamic -config parameter innodb_LRU_scan_depth. */ +config parameter innodb_LRU_scan_depth. +@return number of pages flushed */ UNIV_INTERN -void +ulint buf_flush_LRU_tail(void) /*====================*/ { @@ -2313,6 +2314,7 @@ buf_flush_LRU_tail(void) MONITOR_LRU_BATCH_PAGES, total_flushed); } + return(total_flushed); } /*********************************************************************//** @@ -2608,19 +2610,24 @@ static void page_cleaner_adapt_lru_sleep_time( /*==============================*/ - ulint* lru_sleep_time) /*!< in/out: desired page cleaner thread sleep + ulint* lru_sleep_time, /*!< in/out: desired page cleaner thread sleep time for LRU flushes */ + ulint lru_n_flushed) /*!< in: number of flushed in previous batch */ + { ulint free_len = buf_get_total_free_list_length(); ulint max_free_len = srv_LRU_scan_depth * srv_buf_pool_instances; - if (free_len < max_free_len / 100) { + if (free_len < max_free_len / 100 && lru_n_flushed) { - /* Free lists filled less than 1%, no sleep */ + /* Free lists filled less than 1% + and iteration was able to flush, no sleep */ *lru_sleep_time = 0; - } else if (free_len > max_free_len / 5) { + } else if (free_len > max_free_len / 5 + || (free_len < max_free_len / 100 && lru_n_flushed == 0)) { - /* Free lists filled more than 20%, sleep a bit more */ + /* Free lists filled more than 20% + or no pages flushed in previous batch, sleep a bit more */ *lru_sleep_time += 50; if (*lru_sleep_time > srv_cleaner_max_lru_time) *lru_sleep_time = srv_cleaner_max_lru_time; @@ -2826,6 +2833,7 @@ DECLARE_THREAD(buf_flush_lru_manager_thread)( { ulint next_loop_time = ut_time_ms() + 1000; ulint lru_sleep_time = srv_cleaner_max_lru_time; + ulint lru_n_flushed = 1; #ifdef UNIV_PFS_THREAD pfs_register_thread(buf_lru_manager_thread_key); @@ -2852,11 +2860,11 @@ DECLARE_THREAD(buf_flush_lru_manager_thread)( page_cleaner_sleep_if_needed(next_loop_time); - page_cleaner_adapt_lru_sleep_time(&lru_sleep_time); + page_cleaner_adapt_lru_sleep_time(&lru_sleep_time, lru_n_flushed); next_loop_time = ut_time_ms() + lru_sleep_time; - buf_flush_LRU_tail(); + lru_n_flushed = buf_flush_LRU_tail(); } buf_lru_manager_is_active = false; diff --git a/storage/xtradb/dict/dict0dict.cc b/storage/xtradb/dict/dict0dict.cc index 6406a784348..8b65b769444 100644 --- a/storage/xtradb/dict/dict0dict.cc +++ b/storage/xtradb/dict/dict0dict.cc @@ -1590,10 +1590,13 @@ dict_table_rename_in_cache( to preserve the original table name in constraints which reference it */ { + dberr_t err; dict_foreign_t* foreign; dict_index_t* index; ulint fold; char old_name[MAX_FULL_NAME_LEN + 1]; + os_file_type_t ftype; + ibool exists; ut_ad(mutex_own(&(dict_sys->mutex))); @@ -1631,8 +1634,6 @@ dict_table_rename_in_cache( .ibd file and rebuild the .isl file if needed. */ if (dict_table_is_discarded(table)) { - os_file_type_t type; - ibool exists; char* filepath; ut_ad(table->space != TRX_SYS_SPACE); @@ -1651,7 +1652,7 @@ dict_table_rename_in_cache( fil_delete_tablespace(table->space, BUF_REMOVE_ALL_NO_WRITE); /* Delete any temp file hanging around. */ - if (os_file_status(filepath, &exists, &type) + if (os_file_status(filepath, &exists, &ftype) && exists && !os_file_delete_if_exists(innodb_file_temp_key, filepath)) { @@ -1663,8 +1664,6 @@ dict_table_rename_in_cache( mem_free(filepath); } else if (table->space != TRX_SYS_SPACE) { - char* new_path = NULL; - if (DICT_TF2_FLAG_IS_SET(table, DICT_TF2_TEMPORARY)) { ut_print_timestamp(stderr); fputs(" InnoDB: Error: trying to rename a" @@ -1678,34 +1677,43 @@ dict_table_rename_in_cache( } return(DB_ERROR); + } - } else if (DICT_TF_HAS_DATA_DIR(table->flags)) { - char* old_path; - - old_path = fil_space_get_first_path(table->space); + char* new_path = NULL; + char* old_path = fil_space_get_first_path(table->space); + if (DICT_TF_HAS_DATA_DIR(table->flags)) { new_path = os_file_make_new_pathname( old_path, new_name); - mem_free(old_path); - - dberr_t err = fil_create_link_file( - new_name, new_path); - + err = fil_create_link_file(new_name, new_path); if (err != DB_SUCCESS) { mem_free(new_path); + mem_free(old_path); return(DB_TABLESPACE_EXISTS); } + } else { + new_path = fil_make_ibd_name(new_name, false); + } + + /* New filepath must not exist. */ + err = fil_rename_tablespace_check( + table->space, old_path, new_path, false); + if (err != DB_SUCCESS) { + mem_free(old_path); + mem_free(new_path); + return(err); } ibool success = fil_rename_tablespace( old_name, table->space, new_name, new_path); + mem_free(old_path); + mem_free(new_path); + /* If the tablespace is remote, a new .isl file was created If success, delete the old one. If not, delete the new one. */ - if (new_path) { - - mem_free(new_path); + if (DICT_TF_HAS_DATA_DIR(table->flags)) { fil_delete_link_file(success ? old_name : new_name); } diff --git a/storage/xtradb/fil/fil0fil.cc b/storage/xtradb/fil/fil0fil.cc index 8c27affc4ac..981df578acb 100644 --- a/storage/xtradb/fil/fil0fil.cc +++ b/storage/xtradb/fil/fil0fil.cc @@ -3022,6 +3022,48 @@ fil_make_isl_name( return(filename); } +/** Test if a tablespace file can be renamed to a new filepath by checking +if that the old filepath exists and the new filepath does not exist. +@param[in] space_id tablespace id +@param[in] old_path old filepath +@param[in] new_path new filepath +@param[in] is_discarded whether the tablespace is discarded +@return innodb error code */ +dberr_t +fil_rename_tablespace_check( + ulint space_id, + const char* old_path, + const char* new_path, + bool is_discarded) +{ + ulint exists = false; + os_file_type_t ftype; + + if (!is_discarded + && os_file_status(old_path, &exists, &ftype) + && !exists) { + ib_logf(IB_LOG_LEVEL_ERROR, + "Cannot rename '%s' to '%s' for space ID %lu" + " because the source file does not exist.", + old_path, new_path, space_id); + + return(DB_TABLESPACE_NOT_FOUND); + } + + exists = false; + if (!os_file_status(new_path, &exists, &ftype) || exists) { + ib_logf(IB_LOG_LEVEL_ERROR, + "Cannot rename '%s' to '%s' for space ID %lu" + " because the target file exists." + " Remove the target file and try again.", + old_path, new_path, space_id); + + return(DB_TABLESPACE_EXISTS); + } + + return(DB_SUCCESS); +} + /*******************************************************************//** Renames a single-table tablespace. The tablespace must be cached in the tablespace memory cache. @@ -6630,31 +6672,110 @@ fil_get_space_names( return(err); } -/****************************************************************//** -Generate redo logs for swapping two .ibd files */ +/** Generate redo log for swapping two .ibd files +@param[in] old_table old table +@param[in] new_table new table +@param[in] tmp_name temporary table name +@param[in,out] mtr mini-transaction +@return innodb error code */ UNIV_INTERN -void +dberr_t fil_mtr_rename_log( -/*===============*/ - ulint old_space_id, /*!< in: tablespace id of the old - table. */ - const char* old_name, /*!< in: old table name */ - ulint new_space_id, /*!< in: tablespace id of the new - table */ - const char* new_name, /*!< in: new table name */ - const char* tmp_name, /*!< in: temp table name used while - swapping */ - mtr_t* mtr) /*!< in/out: mini-transaction */ + const dict_table_t* old_table, + const dict_table_t* new_table, + const char* tmp_name, + mtr_t* mtr) { - if (old_space_id != TRX_SYS_SPACE) { - fil_op_write_log(MLOG_FILE_RENAME, old_space_id, - 0, 0, old_name, tmp_name, mtr); + dberr_t err = DB_SUCCESS; + char* old_path; + + /* If neither table is file-per-table, + there will be no renaming of files. */ + if (old_table->space == TRX_SYS_SPACE + && new_table->space == TRX_SYS_SPACE) { + return(DB_SUCCESS); } - if (new_space_id != TRX_SYS_SPACE) { - fil_op_write_log(MLOG_FILE_RENAME, new_space_id, - 0, 0, new_name, old_name, mtr); + if (DICT_TF_HAS_DATA_DIR(old_table->flags)) { + old_path = os_file_make_remote_pathname( + old_table->data_dir_path, old_table->name, "ibd"); + } else { + old_path = fil_make_ibd_name(old_table->name, false); } + if (old_path == NULL) { + return(DB_OUT_OF_MEMORY); + } + + if (old_table->space != TRX_SYS_SPACE) { + char* tmp_path; + + if (DICT_TF_HAS_DATA_DIR(old_table->flags)) { + tmp_path = os_file_make_remote_pathname( + old_table->data_dir_path, tmp_name, "ibd"); + } + else { + tmp_path = fil_make_ibd_name(tmp_name, false); + } + + if (tmp_path == NULL) { + mem_free(old_path); + return(DB_OUT_OF_MEMORY); + } + + /* Temp filepath must not exist. */ + err = fil_rename_tablespace_check( + old_table->space, old_path, tmp_path, + dict_table_is_discarded(old_table)); + mem_free(tmp_path); + if (err != DB_SUCCESS) { + mem_free(old_path); + return(err); + } + + fil_op_write_log(MLOG_FILE_RENAME, old_table->space, + 0, 0, old_table->name, tmp_name, mtr); + } + + if (new_table->space != TRX_SYS_SPACE) { + + /* Destination filepath must not exist unless this ALTER + TABLE starts and ends with a file_per-table tablespace. */ + if (old_table->space == TRX_SYS_SPACE) { + char* new_path = NULL; + + if (DICT_TF_HAS_DATA_DIR(new_table->flags)) { + new_path = os_file_make_remote_pathname( + new_table->data_dir_path, + new_table->name, "ibd"); + } + else { + new_path = fil_make_ibd_name( + new_table->name, false); + } + + if (new_path == NULL) { + mem_free(old_path); + return(DB_OUT_OF_MEMORY); + } + + err = fil_rename_tablespace_check( + new_table->space, new_path, old_path, + dict_table_is_discarded(new_table)); + mem_free(new_path); + if (err != DB_SUCCESS) { + mem_free(old_path); + return(err); + } + } + + fil_op_write_log(MLOG_FILE_RENAME, new_table->space, + 0, 0, new_table->name, old_table->name, mtr); + + } + + mem_free(old_path); + + return(err); } /************************************************************************* diff --git a/storage/xtradb/fsp/fsp0fsp.cc b/storage/xtradb/fsp/fsp0fsp.cc index b96f2c1bb7a..59328dadc38 100644 --- a/storage/xtradb/fsp/fsp0fsp.cc +++ b/storage/xtradb/fsp/fsp0fsp.cc @@ -958,10 +958,20 @@ fsp_try_extend_data_file( } } else { /* We extend single-table tablespaces first one extent - at a time, but for bigger tablespaces more. It is not - enough to extend always by one extent, because some - extents are frag page extents. */ + at a time, but 4 at a time for bigger tablespaces. It is + not enough to extend always by one extent, because we need + to add at least one extent to FSP_FREE. + A single extent descriptor page will track many extents. + And the extent that uses its extent descriptor page is + put onto the FSP_FREE_FRAG list. Extents that do not + use their extent descriptor page are added to FSP_FREE. + The physical page size is used to determine how many + extents are tracked on one extent descriptor page. */ ulint extent_size; /*!< one megabyte, in pages */ + ulint threshold; /*!< The size of the tablespace + (in number of pages) where we + start allocating more than one + extent at a time. */ if (!zip_size) { extent_size = FSP_EXTENT_SIZE; @@ -970,6 +980,14 @@ fsp_try_extend_data_file( * UNIV_PAGE_SIZE / zip_size; } + /* Threshold is set at 32mb except when the page + size is small enough that it must be done sooner. + For page size less than 4k, we may reach the + extent contains extent descriptor page before + 32 mb. */ + threshold = ut_min((32 * extent_size), + (zip_size ? zip_size : UNIV_PAGE_SIZE)); + if (size < extent_size) { /* Let us first extend the file to extent_size */ success = fsp_try_extend_data_file_with_pages( @@ -986,7 +1004,7 @@ fsp_try_extend_data_file( size = extent_size; } - if (size < 32 * extent_size) { + if (size < threshold) { size_increase = extent_size; } else { /* Below in fsp_fill_free_list() we assume diff --git a/storage/xtradb/handler/ha_innodb.cc b/storage/xtradb/handler/ha_innodb.cc index 4daf40df10c..4ce9df722c1 100644 --- a/storage/xtradb/handler/ha_innodb.cc +++ b/storage/xtradb/handler/ha_innodb.cc @@ -16273,7 +16273,6 @@ innodb_sched_priority_purge_update( return; } - ut_ad(purge_sys->state == PURGE_STATE_RUN); for (ulint i = 0; i < srv_n_purge_threads; i++) { ulint actual_priority diff --git a/storage/xtradb/handler/handler0alter.cc b/storage/xtradb/handler/handler0alter.cc index 764c6904853..49ae7c19cc5 100644 --- a/storage/xtradb/handler/handler0alter.cc +++ b/storage/xtradb/handler/handler0alter.cc @@ -156,10 +156,13 @@ my_error_innodb( /* TODO: report the row, as we do for DB_DUPLICATE_KEY */ my_error(ER_INVALID_USE_OF_NULL, MYF(0)); break; + case DB_TABLESPACE_EXISTS: + my_error(ER_TABLESPACE_EXISTS, MYF(0), table); + break; + #ifdef UNIV_DEBUG case DB_SUCCESS: case DB_DUPLICATE_KEY: - case DB_TABLESPACE_EXISTS: case DB_ONLINE_LOG_TOO_BIG: /* These codes should not be passed here. */ ut_error; @@ -5035,6 +5038,61 @@ commit_cache_rebuild( DBUG_VOID_RETURN; } +/** Store the column number of the columns in a list belonging +to indexes which are not being dropped. +@param[in] ctx In-place ALTER TABLE context +@param[out] drop_col_list list which will be set, containing columns + which is part of index being dropped */ +static +void +get_col_list_to_be_dropped( + ha_innobase_inplace_ctx* ctx, + std::set& drop_col_list) +{ + for (ulint index_count = 0; index_count < ctx->num_to_drop_index; + index_count++) { + dict_index_t* index = ctx->drop_index[index_count]; + + for (ulint col = 0; col < index->n_user_defined_cols; col++) { + ulint col_no = dict_index_get_nth_col_no(index, col); + drop_col_list.insert(col_no); + } + } +} + +/** For each column, which is part of an index which is not going to be +dropped, it checks if the column number of the column is same as col_no +argument passed. +@param[in] table table object +@param[in] col_no column number of the column which is to be checked +@retval true column exists +@retval false column does not exist. */ +static +bool +check_col_exists_in_indexes( + const dict_table_t* table, + ulint col_no) +{ + for (dict_index_t* index = dict_table_get_first_index(table); index; + index = dict_table_get_next_index(index)) { + + if (index->to_be_dropped) { + continue; + } + + for (ulint col = 0; col < index->n_user_defined_cols; col++) { + + ulint index_col_no = dict_index_get_nth_col_no( + index, col); + if (col_no == index_col_no) { + return(true); + } + } + } + + return(false); +} + /** Commit the changes made during prepare_inplace_alter_table() and inplace_alter_table() inside the data dictionary tables, when not rebuilding the table. @@ -5170,6 +5228,20 @@ commit_cache_norebuild( DBUG_ASSERT(!ctx->need_rebuild()); + std::set drop_list; + std::set::const_iterator col_it; + + /* Check if the column, part of an index to be dropped is part of any + other index which is not being dropped. If it so, then set the ord_part + of the column to 0. */ + get_col_list_to_be_dropped(ctx, drop_list); + + for(col_it = drop_list.begin(); col_it != drop_list.end(); ++col_it) { + if (!check_col_exists_in_indexes(ctx->new_table, *col_it)) { + ctx->new_table->cols[*col_it].ord_part = 0; + } + } + for (ulint i = 0; i < ctx->num_to_add_index; i++) { dict_index_t* index = ctx->add_index[i]; DBUG_ASSERT(dict_index_get_online_status(index) @@ -5370,6 +5442,7 @@ ha_innobase::commit_inplace_alter_table( Alter_inplace_info* ha_alter_info, bool commit) { + dberr_t error; ha_innobase_inplace_ctx* ctx0 = static_cast (ha_alter_info->handler_ctx); @@ -5451,7 +5524,7 @@ ha_innobase::commit_inplace_alter_table( transactions collected during crash recovery could be holding InnoDB locks only, not MySQL locks. */ - dberr_t error = row_merge_lock_table( + error = row_merge_lock_table( prebuilt->trx, ctx->old_table, LOCK_X); if (error != DB_SUCCESS) { @@ -5586,14 +5659,20 @@ ha_innobase::commit_inplace_alter_table( = static_cast(*pctx); DBUG_ASSERT(ctx->need_rebuild()); - /* Generate the redo log for the file - operations that will be performed in - commit_cache_rebuild(). */ - fil_mtr_rename_log(ctx->old_table->space, - ctx->old_table->name, - ctx->new_table->space, - ctx->new_table->name, - ctx->tmp_name, &mtr); + /* Check for any possible problems for any + file operations that will be performed in + commit_cache_rebuild(), and if none, generate + the redo log for these operations. */ + error = fil_mtr_rename_log(ctx->old_table, + ctx->new_table, + ctx->tmp_name, &mtr); + if (error != DB_SUCCESS) { + /* Out of memory or a problem will occur + when renaming files. */ + fail = true; + my_error_innodb(error, ctx->old_table->name, + ctx->old_table->flags); + } DBUG_INJECT_CRASH("ib_commit_inplace_crash", crash_inject_count++); } @@ -5606,18 +5685,25 @@ ha_innobase::commit_inplace_alter_table( DBUG_EXECUTE_IF("innodb_alter_commit_crash_before_commit", log_buffer_flush_to_disk(); DBUG_SUICIDE();); - ut_ad(trx_state_eq(trx, TRX_STATE_ACTIVE)); ut_ad(!trx->fts_trx); - ut_ad(trx->insert_undo || trx->update_undo); - /* The following call commits the - mini-transaction, making the data dictionary - transaction committed at mtr.end_lsn. The - transaction becomes 'durable' by the time when - log_buffer_flush_to_disk() returns. In the - logical sense the commit in the file-based - data structures happens here. */ - trx_commit_low(trx, &mtr); + if (fail) { + mtr_set_log_mode(&mtr, MTR_LOG_NO_REDO); + mtr_commit(&mtr); + trx_rollback_for_mysql(trx); + } else { + /* The following call commits the + mini-transaction, making the data dictionary + transaction committed at mtr.end_lsn. The + transaction becomes 'durable' by the time when + log_buffer_flush_to_disk() returns. In the + logical sense the commit in the file-based + data structures happens here. */ + ut_ad(trx_state_eq(trx, TRX_STATE_ACTIVE)); + ut_ad(trx->insert_undo || trx->update_undo); + + trx_commit_low(trx, &mtr); + } /* If server crashes here, the dictionary in InnoDB and MySQL will differ. The .ibd files @@ -5639,7 +5725,6 @@ ha_innobase::commit_inplace_alter_table( update the in-memory structures, close some handles, release temporary files, and (unless we rolled back) update persistent statistics. */ - dberr_t error = DB_SUCCESS; for (inplace_alter_handler_ctx** pctx = ctx_array; *pctx; pctx++) { diff --git a/storage/xtradb/include/buf0flu.h b/storage/xtradb/include/buf0flu.h index b73037dfab1..2cd8eefb79d 100644 --- a/storage/xtradb/include/buf0flu.h +++ b/storage/xtradb/include/buf0flu.h @@ -202,9 +202,10 @@ Clears up tail of the LRU lists: * Put replaceable pages at the tail of LRU to the free list * Flush dirty pages at the tail of LRU to the disk The depth to which we scan each buffer pool is controlled by dynamic -config parameter innodb_LRU_scan_depth. */ +config parameter innodb_LRU_scan_depth. +@return number of pages flushed */ UNIV_INTERN -void +ulint buf_flush_LRU_tail(void); /*====================*/ /*********************************************************************//** diff --git a/storage/xtradb/include/fil0fil.h b/storage/xtradb/include/fil0fil.h index c5ac9d7de83..1c78519cac5 100644 --- a/storage/xtradb/include/fil0fil.h +++ b/storage/xtradb/include/fil0fil.h @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1995, 2014, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 1995, 2015, 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 @@ -490,6 +490,21 @@ fil_discard_tablespace( ulint id) /*!< in: space id */ __attribute__((warn_unused_result)); #endif /* !UNIV_HOTBACKUP */ + +/** Test if a tablespace file can be renamed to a new filepath by checking +if that the old filepath exists and the new filepath does not exist. +@param[in] space_id tablespace id +@param[in] old_path old filepath +@param[in] new_path new filepath +@param[in] is_discarded whether the tablespace is discarded +@return innodb error code */ +dberr_t +fil_rename_tablespace_check( + ulint space_id, + const char* old_path, + const char* new_path, + bool is_discarded); + /*******************************************************************//** Renames a single-table tablespace. The tablespace must be cached in the tablespace memory cache. @@ -990,21 +1005,19 @@ fil_get_space_names( /*!< in/out: Vector for collecting the names. */ __attribute__((warn_unused_result)); -/****************************************************************//** -Generate redo logs for swapping two .ibd files */ +/** Generate redo log for swapping two .ibd files +@param[in] old_table old table +@param[in] new_table new table +@param[in] tmp_name temporary table name +@param[in,out] mtr mini-transaction +@return innodb error code */ UNIV_INTERN -void +dberr_t fil_mtr_rename_log( -/*===============*/ - ulint old_space_id, /*!< in: tablespace id of the old - table. */ - const char* old_name, /*!< in: old table name */ - ulint new_space_id, /*!< in: tablespace id of the new - table */ - const char* new_name, /*!< in: new table name */ - const char* tmp_name, /*!< in: temp table name used while - swapping */ - mtr_t* mtr) /*!< in/out: mini-transaction */ + const dict_table_t* old_table, + const dict_table_t* new_table, + const char* tmp_name, + mtr_t* mtr) __attribute__((nonnull)); /*******************************************************************//** diff --git a/storage/xtradb/include/univ.i b/storage/xtradb/include/univ.i index 11900631a45..28a100ee757 100644 --- a/storage/xtradb/include/univ.i +++ b/storage/xtradb/include/univ.i @@ -47,7 +47,7 @@ Created 1/20/1994 Heikki Tuuri #define INNODB_VERSION_BUGFIX MYSQL_VERSION_PATCH #ifndef PERCONA_INNODB_VERSION -#define PERCONA_INNODB_VERSION 76.0 +#define PERCONA_INNODB_VERSION 76.1 #endif /* Enable UNIV_LOG_ARCHIVE in XtraDB */ diff --git a/storage/xtradb/log/log0recv.cc b/storage/xtradb/log/log0recv.cc index 82b0af828b0..cc0bb515988 100644 --- a/storage/xtradb/log/log0recv.cc +++ b/storage/xtradb/log/log0recv.cc @@ -828,6 +828,10 @@ not_consistent: fprintf(stderr, "InnoDB: No valid checkpoint found.\n" + "InnoDB: If you are attempting downgrade" + " from MySQL 5.7.9 or later,\n" + "InnoDB: please refer to " REFMAN + "upgrading-downgrading.html\n" "InnoDB: If this error appears when you are" " creating an InnoDB database,\n" "InnoDB: the problem may be that during" diff --git a/storage/xtradb/row/row0import.cc b/storage/xtradb/row/row0import.cc index acf6be7d8d8..89496b4176b 100644 --- a/storage/xtradb/row/row0import.cc +++ b/storage/xtradb/row/row0import.cc @@ -2068,8 +2068,20 @@ PageConverter::validate( return(IMPORT_PAGE_STATUS_CORRUPTED); } else if (offset > 0 && page_get_page_no(page) == 0) { - const byte* b = page; - const byte* e = b + m_page_size; + ulint checksum; + + checksum = mach_read_from_4(page + FIL_PAGE_SPACE_OR_CHKSUM); + if (checksum != 0) { + /* Checksum check passed in buf_page_is_corrupted(). */ + ib_logf(IB_LOG_LEVEL_WARN, + "%s: Page %lu checksum %lu should be zero.", + m_filepath, (ulong) (offset / m_page_size), + checksum); + } + + const byte* b = page + FIL_PAGE_OFFSET; + const byte* e = page + m_page_size + - FIL_PAGE_END_LSN_OLD_CHKSUM; /* If the page number is zero and offset > 0 then the entire page MUST consist of zeroes. If not then diff --git a/storage/xtradb/row/row0log.cc b/storage/xtradb/row/row0log.cc index f88baa42d58..3989b669c28 100644 --- a/storage/xtradb/row/row0log.cc +++ b/storage/xtradb/row/row0log.cc @@ -1454,6 +1454,7 @@ row_log_table_apply_insert_low( dtuple_t* entry; const row_log_t*log = dup->index->online_log; dict_index_t* index = dict_table_get_first_index(log->table); + ulint n_index = 0; ut_ad(dtuple_validate(row)); ut_ad(trx_id); @@ -1489,6 +1490,8 @@ row_log_table_apply_insert_low( } do { + n_index++; + if (!(index = dict_table_get_next_index(index))) { break; } @@ -1501,6 +1504,12 @@ row_log_table_apply_insert_low( error = row_ins_sec_index_entry_low( flags, BTR_MODIFY_TREE, index, offsets_heap, heap, entry, trx_id, thr); + + /* Report correct index name for duplicate key error. */ + if (error == DB_DUPLICATE_KEY) { + thr_get_trx(thr)->error_key_num = n_index; + } + } while (error == DB_SUCCESS); return(error); @@ -1808,6 +1817,7 @@ row_log_table_apply_update( mtr_t mtr; btr_pcur_t pcur; dberr_t error; + ulint n_index = 0; ut_ad(dtuple_get_n_fields_cmp(old_pk) == dict_index_get_n_unique(index)); @@ -2083,6 +2093,8 @@ func_exit_committed: break; } + n_index++; + if (index->type & DICT_FTS) { continue; } @@ -2126,6 +2138,11 @@ func_exit_committed: BTR_MODIFY_TREE, index, offsets_heap, heap, entry, trx_id, thr); + /* Report correct index name for duplicate key error. */ + if (error == DB_DUPLICATE_KEY) { + thr_get_trx(thr)->error_key_num = n_index; + } + mtr_start(&mtr); } diff --git a/storage/xtradb/srv/srv0start.cc b/storage/xtradb/srv/srv0start.cc index bbd095dacf7..0d3f4691a50 100644 --- a/storage/xtradb/srv/srv0start.cc +++ b/storage/xtradb/srv/srv0start.cc @@ -159,6 +159,9 @@ static const ulint SRV_UNDO_TABLESPACE_SIZE_IN_PAGES = #define SRV_N_PENDING_IOS_PER_THREAD OS_AIO_N_PENDING_IOS_PER_THREAD #define SRV_MAX_N_PENDING_SYNC_IOS 100 +/** The round off to MB is similar as done in srv_parse_megabytes() */ +#define CALC_NUMBER_OF_PAGES(size) ((size) / (1024 * 1024)) * \ + ((1024 * 1024) / (UNIV_PAGE_SIZE)) #ifdef UNIV_PFS_THREAD /* Keys to register InnoDB threads with performance schema */ UNIV_INTERN mysql_pfs_key_t io_handler_thread_key; @@ -988,10 +991,16 @@ open_or_create_data_files( size_check: size = os_file_get_size(files[i]); ut_a(size != (os_offset_t) -1); - /* Round size downward to megabytes */ - rounded_size_pages = (ulint) - (size >> UNIV_PAGE_SIZE_SHIFT); + /* Under some error conditions like disk full + narios or file size reaching filesystem + limit the data file could contain an incomplete + extent at the end. When we extend a data file + and if some failure happens, then also the data + file could contain an incomplete extent. So we + need to round the size downward to a megabyte.*/ + + rounded_size_pages = (ulint) CALC_NUMBER_OF_PAGES(size); if (i == srv_n_data_files - 1 && srv_auto_extend_last_data_file) { diff --git a/storage/xtradb/sync/sync0sync.cc b/storage/xtradb/sync/sync0sync.cc index d6a7f569b4a..7fc992bf972 100644 --- a/storage/xtradb/sync/sync0sync.cc +++ b/storage/xtradb/sync/sync0sync.cc @@ -1690,34 +1690,49 @@ sync_print_wait_info( /*=================*/ FILE* file) /*!< in: file where to print */ { + // Sum counter values once + ib_int64_t mutex_spin_wait_count_val + = static_cast(mutex_spin_wait_count); + ib_int64_t mutex_spin_round_count_val + = static_cast(mutex_spin_round_count); + ib_int64_t mutex_os_wait_count_val + = static_cast(mutex_os_wait_count); + ib_int64_t rw_s_spin_wait_count_val + = static_cast(rw_lock_stats.rw_s_spin_wait_count); + ib_int64_t rw_s_spin_round_count_val + = static_cast(rw_lock_stats.rw_s_spin_round_count); + ib_int64_t rw_s_os_wait_count_val + = static_cast(rw_lock_stats.rw_s_os_wait_count); + ib_int64_t rw_x_spin_wait_count_val + = static_cast(rw_lock_stats.rw_x_spin_wait_count); + ib_int64_t rw_x_spin_round_count_val + = static_cast(rw_lock_stats.rw_x_spin_round_count); + ib_int64_t rw_x_os_wait_count_val + = static_cast(rw_lock_stats.rw_x_os_wait_count); + fprintf(file, - "Mutex spin waits " UINT64PF ", rounds " UINT64PF ", " - "OS waits " UINT64PF "\n" - "RW-shared spins " UINT64PF ", rounds " UINT64PF ", " - "OS waits " UINT64PF "\n" - "RW-excl spins " UINT64PF ", rounds " UINT64PF ", " - "OS waits " UINT64PF "\n", - (ib_uint64_t) mutex_spin_wait_count, - (ib_uint64_t) mutex_spin_round_count, - (ib_uint64_t) mutex_os_wait_count, - (ib_uint64_t) rw_lock_stats.rw_s_spin_wait_count, - (ib_uint64_t) rw_lock_stats.rw_s_spin_round_count, - (ib_uint64_t) rw_lock_stats.rw_s_os_wait_count, - (ib_uint64_t) rw_lock_stats.rw_x_spin_wait_count, - (ib_uint64_t) rw_lock_stats.rw_x_spin_round_count, - (ib_uint64_t) rw_lock_stats.rw_x_os_wait_count); + "Mutex spin waits " INT64PF ", rounds " INT64PF ", " + "OS waits " INT64PF "\n" + "RW-shared spins " INT64PF ", rounds " INT64PF ", " + "OS waits " INT64PF "\n" + "RW-excl spins " INT64PF ", rounds " INT64PF ", " + "OS waits " INT64PF "\n", + mutex_spin_wait_count_val, mutex_spin_round_count_val, + mutex_os_wait_count_val, + rw_s_spin_wait_count_val, rw_s_spin_round_count_val, + rw_s_os_wait_count_val, + rw_x_spin_wait_count_val, rw_x_spin_round_count_val, + rw_x_os_wait_count_val); fprintf(file, "Spin rounds per wait: %.2f mutex, %.2f RW-shared, " "%.2f RW-excl\n", - (double) mutex_spin_round_count / - (mutex_spin_wait_count ? mutex_spin_wait_count : 1), - (double) rw_lock_stats.rw_s_spin_round_count / - (rw_lock_stats.rw_s_spin_wait_count - ? rw_lock_stats.rw_s_spin_wait_count : 1), - (double) rw_lock_stats.rw_x_spin_round_count / - (rw_lock_stats.rw_x_spin_wait_count - ? rw_lock_stats.rw_x_spin_wait_count : 1)); + (double) mutex_spin_round_count_val / + (mutex_spin_wait_count_val ? mutex_spin_wait_count_val : 1LL), + (double) rw_s_spin_round_count_val / + (rw_s_spin_wait_count_val ? rw_s_spin_wait_count_val : 1LL), + (double) rw_x_spin_round_count_val / + (rw_x_spin_wait_count_val ? rw_x_spin_wait_count_val : 1LL)); } /*******************************************************************//**