From 89621ad7387fb206023e8767b164ad6750d8e43b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 8 Feb 2011 12:56:23 +0200 Subject: [PATCH 01/13] Implement UNIV_BLOB_DEBUG. An early version of this caught Bug #55284. This option is known to be broken when tablespaces contain off-page columns after crash recovery. It has only been tested when creating the data files from the scratch. btr_blob_dbg_t: A map from page_no:heap_no:field_no to first_blob_page_no. This map is instantiated for every clustered index in index->blobs. It is protected by index->blobs_mutex. btr_blob_dbg_msg_issue(): Issue a diagnostic message. Invoked when btr_blob_dbg_msg is set. btr_blob_dbg_rbt_insert(): Insert a btr_blob_dbg_t into index->blobs. btr_blob_dbg_rbt_delete(): Remove a btr_blob_dbg_t from index->blobs. btr_blob_dbg_cmp(): Comparator for btr_blob_dbg_t. btr_blob_dbg_add_blob(): Add a BLOB reference to the map. btr_blob_dbg_add_rec(): Add all BLOB references from a record to the map. btr_blob_dbg_print(): Display the map of BLOB references in an index. btr_blob_dbg_remove_rec(): Remove all BLOB references of a record from the map. btr_blob_dbg_is_empty(): Check that no BLOB references exist to or from a page. Disowned references from delete-marked records are tolerated. btr_blob_dbg_op(): Perform an operation on all BLOB references on a B-tree page. btr_blob_dbg_add(): Add all BLOB references from a B-tree page to the map. btr_blob_dbg_remove(): Remove all BLOB references from a B-tree page from the map. btr_blob_dbg_restore(): Restore the BLOB references after a failed page reorganize. btr_blob_dbg_set_deleted_flag(): Modify the 'deleted' flag in the BLOB references of a record. btr_blob_dbg_owner(): Own or disown a BLOB reference. btr_page_create(), btr_page_free_low(): Assert that no BLOB references exist. btr_create(): Create index->blobs for clustered indexes. btr_page_reorganize_low(): Invoke btr_blob_dbg_remove() before copying the records. Invoke btr_blob_dbg_restore() if the operation fails. btr_page_empty(), btr_lift_page_up(), btr_compress(), btr_discard_page(): Invoke btr_blob_dbg_remove(). btr_cur_del_mark_set_clust_rec(): Invoke btr_blob_dbg_set_deleted_flag(). Other cases of modifying the delete mark are either in the secondary index or during crash recovery, which we do not promise to support. btr_cur_set_ownership_of_extern_field(): Invoke btr_blob_dbg_owner(). btr_store_big_rec_extern_fields(): Invoke btr_blob_dbg_add_blob(). btr_free_externally_stored_field(): Invoke btr_blob_dbg_assert_empty() on the first BLOB page. page_cur_insert_rec_low(), page_cur_insert_rec_zip(), page_copy_rec_list_end_to_created_page(): Invoke btr_blob_dbg_add_rec(). page_cur_insert_rec_zip_reorg(), page_copy_rec_list_end(), page_copy_rec_list_start(): After failure, invoke btr_blob_dbg_remove() and btr_blob_dbg_add(). page_cur_delete_rec(): Invoke btr_blob_dbg_remove_rec(). page_delete_rec_list_end(): Invoke btr_blob_dbg_op(btr_blob_dbg_remove_rec). page_zip_reorganize(): Invoke btr_blob_dbg_remove() before copying the records. page_zip_copy_recs(): Invoke btr_blob_dbg_add(). row_upd_rec_in_place(): Invoke btr_blob_dbg_rbt_delete() and btr_blob_dbg_rbt_insert(). innobase_start_or_create_for_mysql(): Warn when UNIV_BLOB_DEBUG is enabled. rb://550 approved by Jimmy Yang --- storage/innodb_plugin/btr/btr0btr.c | 571 ++++++++++++++++++++++ storage/innodb_plugin/btr/btr0cur.c | 44 ++ storage/innodb_plugin/dict/dict0mem.c | 9 + storage/innodb_plugin/include/btr0btr.h | 85 ++++ storage/innodb_plugin/include/btr0types.h | 125 +++++ storage/innodb_plugin/include/dict0mem.h | 7 + storage/innodb_plugin/include/page0zip.h | 2 +- storage/innodb_plugin/include/univ.i | 2 + storage/innodb_plugin/page/page0cur.c | 10 + storage/innodb_plugin/page/page0page.c | 11 + storage/innodb_plugin/page/page0zip.c | 5 +- storage/innodb_plugin/row/row0upd.c | 35 ++ storage/innodb_plugin/srv/srv0start.c | 6 + 13 files changed, 910 insertions(+), 2 deletions(-) diff --git a/storage/innodb_plugin/btr/btr0btr.c b/storage/innodb_plugin/btr/btr0btr.c index 3d8d6048603..46810c011c4 100644 --- a/storage/innodb_plugin/btr/btr0btr.c +++ b/storage/innodb_plugin/btr/btr0btr.c @@ -42,6 +42,560 @@ Created 6/2/1994 Heikki Tuuri #include "ibuf0ibuf.h" #include "trx0trx.h" +#ifdef UNIV_BLOB_DEBUG +# include "srv0srv.h" +# include "ut0rbt.h" + +/** TRUE when messages about index->blobs modification are enabled. */ +static ibool btr_blob_dbg_msg; + +/** Issue a message about an operation on index->blobs. +@param op operation +@param b the entry being subjected to the operation +@param ctx the context of the operation */ +#define btr_blob_dbg_msg_issue(op, b, ctx) \ + fprintf(stderr, op " %u:%u:%u->%u %s(%u,%u,%u)\n", \ + (b)->ref_page_no, (b)->ref_heap_no, \ + (b)->ref_field_no, (b)->blob_page_no, ctx, \ + (b)->owner, (b)->always_owner, (b)->del) + +/** Insert to index->blobs a reference to an off-page column. +@param index the index tree +@param b the reference +@param ctx context (for logging) */ +UNIV_INTERN +void +btr_blob_dbg_rbt_insert( +/*====================*/ + dict_index_t* index, /*!< in/out: index tree */ + const btr_blob_dbg_t* b, /*!< in: the reference */ + const char* ctx) /*!< in: context (for logging) */ +{ + if (btr_blob_dbg_msg) { + btr_blob_dbg_msg_issue("insert", b, ctx); + } + mutex_enter(&index->blobs_mutex); + rbt_insert(index->blobs, b, b); + mutex_exit(&index->blobs_mutex); +} + +/** Remove from index->blobs a reference to an off-page column. +@param index the index tree +@param b the reference +@param ctx context (for logging) */ +UNIV_INTERN +void +btr_blob_dbg_rbt_delete( +/*====================*/ + dict_index_t* index, /*!< in/out: index tree */ + const btr_blob_dbg_t* b, /*!< in: the reference */ + const char* ctx) /*!< in: context (for logging) */ +{ + if (btr_blob_dbg_msg) { + btr_blob_dbg_msg_issue("delete", b, ctx); + } + mutex_enter(&index->blobs_mutex); + ut_a(rbt_delete(index->blobs, b)); + mutex_exit(&index->blobs_mutex); +} + +/**************************************************************//** +Comparator for items (btr_blob_dbg_t) in index->blobs. +The key in index->blobs is (ref_page_no, ref_heap_no, ref_field_no). +@return negative, 0 or positive if *a<*b, *a=*b, *a>*b */ +static +int +btr_blob_dbg_cmp( +/*=============*/ + const void* a, /*!< in: first btr_blob_dbg_t to compare */ + const void* b) /*!< in: second btr_blob_dbg_t to compare */ +{ + const btr_blob_dbg_t* aa = a; + const btr_blob_dbg_t* bb = b; + + ut_ad(aa != NULL); + ut_ad(bb != NULL); + + if (aa->ref_page_no != bb->ref_page_no) { + return(aa->ref_page_no < bb->ref_page_no ? -1 : 1); + } + if (aa->ref_heap_no != bb->ref_heap_no) { + return(aa->ref_heap_no < bb->ref_heap_no ? -1 : 1); + } + if (aa->ref_field_no != bb->ref_field_no) { + return(aa->ref_field_no < bb->ref_field_no ? -1 : 1); + } + return(0); +} + +/**************************************************************//** +Add a reference to an off-page column to the index->blobs map. */ +UNIV_INTERN +void +btr_blob_dbg_add_blob( +/*==================*/ + const rec_t* rec, /*!< in: clustered index record */ + ulint field_no, /*!< in: off-page column number */ + ulint page_no, /*!< in: start page of the column */ + dict_index_t* index, /*!< in/out: index tree */ + const char* ctx) /*!< in: context (for logging) */ +{ + btr_blob_dbg_t b; + const page_t* page = page_align(rec); + + ut_a(index->blobs); + + b.blob_page_no = page_no; + b.ref_page_no = page_get_page_no(page); + b.ref_heap_no = page_rec_get_heap_no(rec); + b.ref_field_no = field_no; + ut_a(b.ref_field_no >= index->n_uniq); + b.always_owner = b.owner = TRUE; + b.del = FALSE; + ut_a(!rec_get_deleted_flag(rec, page_is_comp(page))); + btr_blob_dbg_rbt_insert(index, &b, ctx); +} + +/**************************************************************//** +Add to index->blobs any references to off-page columns from a record. +@return number of references added */ +UNIV_INTERN +ulint +btr_blob_dbg_add_rec( +/*=================*/ + const rec_t* rec, /*!< in: record */ + dict_index_t* index, /*!< in/out: index */ + const ulint* offsets,/*!< in: offsets */ + const char* ctx) /*!< in: context (for logging) */ +{ + ulint count = 0; + ulint i; + btr_blob_dbg_t b; + ibool del; + + ut_ad(rec_offs_validate(rec, index, offsets)); + + if (!rec_offs_any_extern(offsets)) { + return(0); + } + + b.ref_page_no = page_get_page_no(page_align(rec)); + b.ref_heap_no = page_rec_get_heap_no(rec); + del = (rec_get_deleted_flag(rec, rec_offs_comp(offsets)) != 0); + + for (i = 0; i < rec_offs_n_fields(offsets); i++) { + if (rec_offs_nth_extern(offsets, i)) { + ulint len; + const byte* field_ref = rec_get_nth_field( + rec, offsets, i, &len); + + ut_a(len != UNIV_SQL_NULL); + ut_a(len >= BTR_EXTERN_FIELD_REF_SIZE); + field_ref += len - BTR_EXTERN_FIELD_REF_SIZE; + + if (!memcmp(field_ref, field_ref_zero, + BTR_EXTERN_FIELD_REF_SIZE)) { + /* the column has not been stored yet */ + continue; + } + + b.ref_field_no = i; + b.blob_page_no = mach_read_from_4( + field_ref + BTR_EXTERN_PAGE_NO); + ut_a(b.ref_field_no >= index->n_uniq); + b.always_owner = b.owner + = !(field_ref[BTR_EXTERN_LEN] + & BTR_EXTERN_OWNER_FLAG); + b.del = del; + + btr_blob_dbg_rbt_insert(index, &b, ctx); + count++; + } + } + + return(count); +} + +/**************************************************************//** +Display the references to off-page columns. +This function is to be called from a debugger, +for example when a breakpoint on ut_dbg_assertion_failed is hit. */ +UNIV_INTERN +void +btr_blob_dbg_print( +/*===============*/ + const dict_index_t* index) /*!< in: index tree */ +{ + const ib_rbt_node_t* node; + + if (!index->blobs) { + return; + } + + /* We intentionally do not acquire index->blobs_mutex here. + This function is to be called from a debugger, and the caller + should make sure that the index->blobs_mutex is held. */ + + for (node = rbt_first(index->blobs); + node != NULL; node = rbt_next(index->blobs, node)) { + const btr_blob_dbg_t* b + = rbt_value(btr_blob_dbg_t, node); + fprintf(stderr, "%u:%u:%u->%u%s%s%s\n", + b->ref_page_no, b->ref_heap_no, b->ref_field_no, + b->blob_page_no, + b->owner ? "" : "(disowned)", + b->always_owner ? "" : "(has disowned)", + b->del ? "(deleted)" : ""); + } +} + +/**************************************************************//** +Remove from index->blobs any references to off-page columns from a record. +@return number of references removed */ +UNIV_INTERN +ulint +btr_blob_dbg_remove_rec( +/*====================*/ + const rec_t* rec, /*!< in: record */ + dict_index_t* index, /*!< in/out: index */ + const ulint* offsets,/*!< in: offsets */ + const char* ctx) /*!< in: context (for logging) */ +{ + ulint i; + ulint count = 0; + btr_blob_dbg_t b; + + ut_ad(rec_offs_validate(rec, index, offsets)); + + if (!rec_offs_any_extern(offsets)) { + return(0); + } + + b.ref_page_no = page_get_page_no(page_align(rec)); + b.ref_heap_no = page_rec_get_heap_no(rec); + + for (i = 0; i < rec_offs_n_fields(offsets); i++) { + if (rec_offs_nth_extern(offsets, i)) { + ulint len; + const byte* field_ref = rec_get_nth_field( + rec, offsets, i, &len); + + ut_a(len != UNIV_SQL_NULL); + ut_a(len >= BTR_EXTERN_FIELD_REF_SIZE); + field_ref += len - BTR_EXTERN_FIELD_REF_SIZE; + + b.ref_field_no = i; + b.blob_page_no = mach_read_from_4( + field_ref + BTR_EXTERN_PAGE_NO); + + switch (b.blob_page_no) { + case 0: + /* The column has not been stored yet. + The BLOB pointer must be all zero. + There cannot be a BLOB starting at + page 0, because page 0 is reserved for + the tablespace header. */ + ut_a(!memcmp(field_ref, field_ref_zero, + BTR_EXTERN_FIELD_REF_SIZE)); + /* fall through */ + case FIL_NULL: + /* the column has been freed already */ + continue; + } + + btr_blob_dbg_rbt_delete(index, &b, ctx); + count++; + } + } + + return(count); +} + +/**************************************************************//** +Check that there are no references to off-page columns from or to +the given page. Invoked when freeing or clearing a page. +@return TRUE when no orphan references exist */ +UNIV_INTERN +ibool +btr_blob_dbg_is_empty( +/*==================*/ + dict_index_t* index, /*!< in: index */ + ulint page_no) /*!< in: page number */ +{ + const ib_rbt_node_t* node; + ibool success = TRUE; + + if (!index->blobs) { + return(success); + } + + mutex_enter(&index->blobs_mutex); + + for (node = rbt_first(index->blobs); + node != NULL; node = rbt_next(index->blobs, node)) { + const btr_blob_dbg_t* b + = rbt_value(btr_blob_dbg_t, node); + + if (b->ref_page_no != page_no && b->blob_page_no != page_no) { + continue; + } + + fprintf(stderr, + "InnoDB: orphan BLOB ref%s%s%s %u:%u:%u->%u\n", + b->owner ? "" : "(disowned)", + b->always_owner ? "" : "(has disowned)", + b->del ? "(deleted)" : "", + b->ref_page_no, b->ref_heap_no, b->ref_field_no, + b->blob_page_no); + + if (b->blob_page_no != page_no || b->owner || !b->del) { + success = FALSE; + } + } + + mutex_exit(&index->blobs_mutex); + return(success); +} + +/**************************************************************//** +Count and process all references to off-page columns on a page. +@return number of references processed */ +UNIV_INTERN +ulint +btr_blob_dbg_op( +/*============*/ + const page_t* page, /*!< in: B-tree leaf page */ + const rec_t* rec, /*!< in: record to start from + (NULL to process the whole page) */ + dict_index_t* index, /*!< in/out: index */ + const char* ctx, /*!< in: context (for logging) */ + const btr_blob_dbg_op_f op) /*!< in: operation on records */ +{ + ulint count = 0; + mem_heap_t* heap = NULL; + ulint offsets_[REC_OFFS_NORMAL_SIZE]; + ulint* offsets = offsets_; + rec_offs_init(offsets_); + + ut_a(fil_page_get_type(page) == FIL_PAGE_INDEX); + ut_a(!rec || page_align(rec) == page); + + if (!index->blobs || !page_is_leaf(page) + || !dict_index_is_clust(index)) { + return(0); + } + + if (rec == NULL) { + rec = page_get_infimum_rec(page); + } + + do { + offsets = rec_get_offsets(rec, index, offsets, + ULINT_UNDEFINED, &heap); + count += op(rec, index, offsets, ctx); + rec = page_rec_get_next_const(rec); + } while (!page_rec_is_supremum(rec)); + + if (UNIV_LIKELY_NULL(heap)) { + mem_heap_free(heap); + } + + return(count); +} + +/**************************************************************//** +Count and add to index->blobs any references to off-page columns +from records on a page. +@return number of references added */ +UNIV_INTERN +ulint +btr_blob_dbg_add( +/*=============*/ + const page_t* page, /*!< in: rewritten page */ + dict_index_t* index, /*!< in/out: index */ + const char* ctx) /*!< in: context (for logging) */ +{ + btr_blob_dbg_assert_empty(index, page_get_page_no(page)); + + return(btr_blob_dbg_op(page, NULL, index, ctx, btr_blob_dbg_add_rec)); +} + +/**************************************************************//** +Count and remove from index->blobs any references to off-page columns +from records on a page. +Used when reorganizing a page, before copying the records. +@return number of references removed */ +UNIV_INTERN +ulint +btr_blob_dbg_remove( +/*================*/ + const page_t* page, /*!< in: b-tree page */ + dict_index_t* index, /*!< in/out: index */ + const char* ctx) /*!< in: context (for logging) */ +{ + ulint count; + + count = btr_blob_dbg_op(page, NULL, index, ctx, + btr_blob_dbg_remove_rec); + + /* Check that no references exist. */ + btr_blob_dbg_assert_empty(index, page_get_page_no(page)); + + return(count); +} + +/**************************************************************//** +Restore in index->blobs any references to off-page columns +Used when page reorganize fails due to compressed page overflow. */ +UNIV_INTERN +void +btr_blob_dbg_restore( +/*=================*/ + const page_t* npage, /*!< in: page that failed to compress */ + const page_t* page, /*!< in: copy of original page */ + dict_index_t* index, /*!< in/out: index */ + const char* ctx) /*!< in: context (for logging) */ +{ + ulint removed; + ulint added; + + ut_a(page_get_page_no(npage) == page_get_page_no(page)); + ut_a(page_get_space_id(npage) == page_get_space_id(page)); + + removed = btr_blob_dbg_remove(npage, index, ctx); + added = btr_blob_dbg_add(page, index, ctx); + ut_a(added == removed); +} + +/**************************************************************//** +Modify the 'deleted' flag of a record. */ +UNIV_INTERN +void +btr_blob_dbg_set_deleted_flag( +/*==========================*/ + const rec_t* rec, /*!< in: record */ + dict_index_t* index, /*!< in/out: index */ + const ulint* offsets,/*!< in: rec_get_offs(rec, index) */ + ibool del) /*!< in: TRUE=deleted, FALSE=exists */ +{ + const ib_rbt_node_t* node; + btr_blob_dbg_t b; + btr_blob_dbg_t* c; + ulint i; + + ut_ad(rec_offs_validate(rec, index, offsets)); + ut_a(dict_index_is_clust(index)); + ut_a(del == !!del);/* must be FALSE==0 or TRUE==1 */ + + if (!rec_offs_any_extern(offsets) || !index->blobs) { + + return; + } + + b.ref_page_no = page_get_page_no(page_align(rec)); + b.ref_heap_no = page_rec_get_heap_no(rec); + + for (i = 0; i < rec_offs_n_fields(offsets); i++) { + if (rec_offs_nth_extern(offsets, i)) { + ulint len; + const byte* field_ref = rec_get_nth_field( + rec, offsets, i, &len); + + ut_a(len != UNIV_SQL_NULL); + ut_a(len >= BTR_EXTERN_FIELD_REF_SIZE); + field_ref += len - BTR_EXTERN_FIELD_REF_SIZE; + + b.ref_field_no = i; + b.blob_page_no = mach_read_from_4( + field_ref + BTR_EXTERN_PAGE_NO); + + switch (b.blob_page_no) { + case 0: + ut_a(memcmp(field_ref, field_ref_zero, + BTR_EXTERN_FIELD_REF_SIZE)); + /* page number 0 is for the + page allocation bitmap */ + case FIL_NULL: + /* the column has been freed already */ + ut_error; + } + + mutex_enter(&index->blobs_mutex); + node = rbt_lookup(index->blobs, &b); + ut_a(node); + + c = rbt_value(btr_blob_dbg_t, node); + /* The flag should be modified. */ + c->del = del; + if (btr_blob_dbg_msg) { + b = *c; + mutex_exit(&index->blobs_mutex); + btr_blob_dbg_msg_issue("del_mk", &b, ""); + } else { + mutex_exit(&index->blobs_mutex); + } + } + } +} + +/**************************************************************//** +Change the ownership of an off-page column. */ +UNIV_INTERN +void +btr_blob_dbg_owner( +/*===============*/ + const rec_t* rec, /*!< in: record */ + dict_index_t* index, /*!< in/out: index */ + const ulint* offsets,/*!< in: rec_get_offs(rec, index) */ + ulint i, /*!< in: ith field in rec */ + ibool own) /*!< in: TRUE=owned, FALSE=disowned */ +{ + const ib_rbt_node_t* node; + btr_blob_dbg_t b; + const byte* field_ref; + ulint len; + + ut_ad(rec_offs_validate(rec, index, offsets)); + ut_a(rec_offs_nth_extern(offsets, i)); + + field_ref = rec_get_nth_field(rec, offsets, i, &len); + ut_a(len != UNIV_SQL_NULL); + ut_a(len >= BTR_EXTERN_FIELD_REF_SIZE); + field_ref += len - BTR_EXTERN_FIELD_REF_SIZE; + + b.ref_page_no = page_get_page_no(page_align(rec)); + b.ref_heap_no = page_rec_get_heap_no(rec); + b.ref_field_no = i; + b.owner = !(field_ref[BTR_EXTERN_LEN] & BTR_EXTERN_OWNER_FLAG); + b.blob_page_no = mach_read_from_4(field_ref + BTR_EXTERN_PAGE_NO); + + ut_a(b.owner == own); + + mutex_enter(&index->blobs_mutex); + node = rbt_lookup(index->blobs, &b); + /* row_ins_clust_index_entry_by_modify() invokes + btr_cur_unmark_extern_fields() also for the newly inserted + references, which are all zero bytes until the columns are stored. + The node lookup must fail if and only if that is the case. */ + ut_a(!memcmp(field_ref, field_ref_zero, BTR_EXTERN_FIELD_REF_SIZE) + == !node); + + if (node) { + btr_blob_dbg_t* c = rbt_value(btr_blob_dbg_t, node); + /* Some code sets ownership from TRUE to TRUE. + We do not allow changing ownership from FALSE to FALSE. */ + ut_a(own || c->owner); + + c->owner = own; + if (!own) { + c->always_owner = FALSE; + } + } + + mutex_exit(&index->blobs_mutex); +} +#endif /* UNIV_BLOB_DEBUG */ + /* Latching strategy of the InnoDB B-tree -------------------------------------- @@ -296,6 +850,7 @@ btr_page_create( page_t* page = buf_block_get_frame(block); ut_ad(mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX)); + btr_blob_dbg_assert_empty(index, buf_block_get_page_no(block)); if (UNIV_LIKELY_NULL(page_zip)) { page_create_zip(block, index, level, mtr); @@ -489,6 +1044,7 @@ btr_page_free_low( modify clock */ buf_block_modify_clock_inc(block); + btr_blob_dbg_assert_empty(index, buf_block_get_page_no(block)); if (dict_index_is_ibuf(index)) { @@ -773,6 +1329,13 @@ btr_create( block = buf_page_get(space, zip_size, page_no, RW_X_LATCH, mtr); } else { +#ifdef UNIV_BLOB_DEBUG + if ((type & DICT_CLUSTERED) && !index->blobs) { + mutex_create(&index->blobs_mutex, SYNC_ANY_LATCH); + index->blobs = rbt_create(sizeof(btr_blob_dbg_t), + btr_blob_dbg_cmp); + } +#endif /* UNIV_BLOB_DEBUG */ block = fseg_create(space, 0, PAGE_HEADER + PAGE_BTR_SEG_TOP, mtr); } @@ -996,6 +1559,7 @@ btr_page_reorganize_low( block->check_index_page_at_flush = TRUE; #endif /* !UNIV_HOTBACKUP */ + btr_blob_dbg_remove(page, index, "btr_page_reorganize"); /* Recreate the page: note that global data on page (possible segment headers, next page-field, etc.) is preserved intact */ @@ -1024,6 +1588,8 @@ btr_page_reorganize_low( (!page_zip_compress(page_zip, page, index, NULL))) { /* Restore the old page and exit. */ + btr_blob_dbg_restore(page, temp_page, index, + "btr_page_reorganize_compress_fail"); #if defined UNIV_DEBUG || defined UNIV_ZIP_DEBUG /* Check that the bytes that we skip are identical. */ @@ -1157,6 +1723,7 @@ btr_page_empty( #endif /* UNIV_ZIP_DEBUG */ btr_search_drop_page_hash_index(block); + btr_blob_dbg_remove(page, index, "btr_page_empty"); /* Recreate the page: note that global data on page (possible segment headers, next page-field, etc.) is preserved intact */ @@ -2497,6 +3064,7 @@ btr_lift_page_up( index); } + btr_blob_dbg_remove(page, index, "btr_lift_page_up"); lock_update_copy_and_discard(father_block, block); /* Go upward to root page, decrementing levels by one. */ @@ -2758,6 +3326,7 @@ err_exit: lock_update_merge_right(merge_block, orig_succ, block); } + btr_blob_dbg_remove(page, index, "btr_compress"); mem_heap_free(heap); if (!dict_index_is_clust(index) && page_is_leaf(merge_page)) { @@ -2988,6 +3557,8 @@ btr_discard_page( block); } + btr_blob_dbg_remove(page, index, "btr_discard_page"); + /* Free the file page */ btr_page_free(index, block, mtr); diff --git a/storage/innodb_plugin/btr/btr0cur.c b/storage/innodb_plugin/btr/btr0cur.c index 704cc606a5f..86d77c79e7b 100644 --- a/storage/innodb_plugin/btr/btr0cur.c +++ b/storage/innodb_plugin/btr/btr0cur.c @@ -2572,6 +2572,7 @@ btr_cur_del_mark_set_clust_rec( page_zip = buf_block_get_page_zip(block); + btr_blob_dbg_set_deleted_flag(rec, index, offsets, val); btr_rec_set_deleted_flag(rec, page_zip, val); trx = thr_get_trx(thr); @@ -3595,6 +3596,8 @@ btr_cur_set_ownership_of_extern_field( } else { mach_write_to_1(data + local_len + BTR_EXTERN_LEN, byte_val); } + + btr_blob_dbg_owner(rec, index, offsets, i, val); } /*******************************************************************//** @@ -4094,6 +4097,11 @@ btr_store_big_rec_extern_fields_func( } if (prev_page_no == FIL_NULL) { + btr_blob_dbg_add_blob( + rec, big_rec_vec->fields[i] + .field_no, page_no, index, + "store"); + mach_write_to_4(field_ref + BTR_EXTERN_SPACE_ID, space_id); @@ -4169,6 +4177,11 @@ next_zip_page: MLOG_4BYTES, &mtr); if (prev_page_no == FIL_NULL) { + btr_blob_dbg_add_blob( + rec, big_rec_vec->fields[i] + .field_no, page_no, index, + "store"); + mlog_write_ulint(field_ref + BTR_EXTERN_SPACE_ID, space_id, @@ -4337,6 +4350,37 @@ btr_free_externally_stored_field( rec_zip_size = 0; } +#ifdef UNIV_BLOB_DEBUG + if (!(field_ref[BTR_EXTERN_LEN] & BTR_EXTERN_OWNER_FLAG) + && !((field_ref[BTR_EXTERN_LEN] & BTR_EXTERN_INHERITED_FLAG) + && (rb_ctx == RB_NORMAL || rb_ctx == RB_RECOVERY))) { + /* This off-page column will be freed. + Check that no references remain. */ + + btr_blob_dbg_t b; + + b.blob_page_no = mach_read_from_4( + field_ref + BTR_EXTERN_PAGE_NO); + + if (rec) { + /* Remove the reference from the record to the + BLOB. If the BLOB were not freed, the + reference would be removed when the record is + removed. Freeing the BLOB will overwrite the + BTR_EXTERN_PAGE_NO in the field_ref of the + record with FIL_NULL, which would make the + btr_blob_dbg information inconsistent with the + record. */ + b.ref_page_no = page_get_page_no(page_align(rec)); + b.ref_heap_no = page_rec_get_heap_no(rec); + b.ref_field_no = i; + btr_blob_dbg_rbt_delete(index, &b, "free"); + } + + btr_blob_dbg_assert_empty(index, b.blob_page_no); + } +#endif /* UNIV_BLOB_DEBUG */ + for (;;) { #ifdef UNIV_SYNC_DEBUG buf_block_t* rec_block; diff --git a/storage/innodb_plugin/dict/dict0mem.c b/storage/innodb_plugin/dict/dict0mem.c index 3287247029f..aef815dd2f6 100644 --- a/storage/innodb_plugin/dict/dict0mem.c +++ b/storage/innodb_plugin/dict/dict0mem.c @@ -36,6 +36,9 @@ Created 1/8/1996 Heikki Tuuri #ifndef UNIV_HOTBACKUP # include "lock0lock.h" #endif /* !UNIV_HOTBACKUP */ +#ifdef UNIV_BLOB_DEBUG +# include "ut0rbt.h" +#endif /* UNIV_BLOB_DEBUG */ #define DICT_HEAP_SIZE 100 /*!< initial memory heap size when creating a table or index object */ @@ -316,6 +319,12 @@ dict_mem_index_free( { ut_ad(index); ut_ad(index->magic_n == DICT_INDEX_MAGIC_N); +#ifdef UNIV_BLOB_DEBUG + if (index->blobs) { + mutex_free(&index->blobs_mutex); + rbt_free(index->blobs); + } +#endif /* UNIV_BLOB_DEBUG */ mem_heap_free(index->heap); } diff --git a/storage/innodb_plugin/include/btr0btr.h b/storage/innodb_plugin/include/btr0btr.h index dde3a0bab69..5aa02694e0e 100644 --- a/storage/innodb_plugin/include/btr0btr.h +++ b/storage/innodb_plugin/include/btr0btr.h @@ -81,6 +81,91 @@ UNIQUE definition on secondary indexes when we decide if we can use the insert buffer to speed up inserts */ #define BTR_IGNORE_SEC_UNIQUE 2048 +#ifdef UNIV_BLOB_DEBUG +# include "ut0rbt.h" +/** An index->blobs entry for keeping track of off-page column references */ +struct btr_blob_dbg_struct +{ + unsigned blob_page_no:32; /*!< first BLOB page number */ + unsigned ref_page_no:32; /*!< referring page number */ + unsigned ref_heap_no:16; /*!< referring heap number */ + unsigned ref_field_no:10; /*!< referring field number */ + unsigned owner:1; /*!< TRUE if BLOB owner */ + unsigned always_owner:1; /*!< TRUE if always + has been the BLOB owner; + reset to TRUE on B-tree + page splits and merges */ + unsigned del:1; /*!< TRUE if currently + delete-marked */ +}; + +/**************************************************************//** +Add a reference to an off-page column to the index->blobs map. */ +UNIV_INTERN +void +btr_blob_dbg_add_blob( +/*==================*/ + const rec_t* rec, /*!< in: clustered index record */ + ulint field_no, /*!< in: number of off-page column */ + ulint page_no, /*!< in: start page of the column */ + dict_index_t* index, /*!< in/out: index tree */ + const char* ctx) /*!< in: context (for logging) */ + __attribute__((nonnull)); +/**************************************************************//** +Display the references to off-page columns. +This function is to be called from a debugger, +for example when a breakpoint on ut_dbg_assertion_failed is hit. */ +UNIV_INTERN +void +btr_blob_dbg_print( +/*===============*/ + const dict_index_t* index) /*!< in: index tree */ + __attribute__((nonnull)); +/**************************************************************//** +Check that there are no references to off-page columns from or to +the given page. Invoked when freeing or clearing a page. +@return TRUE when no orphan references exist */ +UNIV_INTERN +ibool +btr_blob_dbg_is_empty( +/*==================*/ + dict_index_t* index, /*!< in: index */ + ulint page_no) /*!< in: page number */ + __attribute__((nonnull, warn_unused_result)); + +/**************************************************************//** +Modify the 'deleted' flag of a record. */ +UNIV_INTERN +void +btr_blob_dbg_set_deleted_flag( +/*==========================*/ + const rec_t* rec, /*!< in: record */ + dict_index_t* index, /*!< in/out: index */ + const ulint* offsets,/*!< in: rec_get_offs(rec, index) */ + ibool del) /*!< in: TRUE=deleted, FALSE=exists */ + __attribute__((nonnull)); +/**************************************************************//** +Change the ownership of an off-page column. */ +UNIV_INTERN +void +btr_blob_dbg_owner( +/*===============*/ + const rec_t* rec, /*!< in: record */ + dict_index_t* index, /*!< in/out: index */ + const ulint* offsets,/*!< in: rec_get_offs(rec, index) */ + ulint i, /*!< in: ith field in rec */ + ibool own) /*!< in: TRUE=owned, FALSE=disowned */ + __attribute__((nonnull)); +/** Assert that there are no BLOB references to or from the given page. */ +# define btr_blob_dbg_assert_empty(index, page_no) \ + ut_a(btr_blob_dbg_is_empty(index, page_no)) +#else /* UNIV_BLOB_DEBUG */ +# define btr_blob_dbg_add_blob(rec, field_no, page, index, ctx) ((void) 0) +# define btr_blob_dbg_set_deleted_flag(rec, index, offsets, del)((void) 0) +# define btr_blob_dbg_owner(rec, index, offsets, i, val) ((void) 0) +# define btr_blob_dbg_assert_empty(index, page_no) ((void) 0) +#endif /* UNIV_BLOB_DEBUG */ + /**************************************************************//** Gets the root node of a tree and x-latches it. @return root page, x-latched */ diff --git a/storage/innodb_plugin/include/btr0types.h b/storage/innodb_plugin/include/btr0types.h index ef4a6b04b34..07c06fb18d7 100644 --- a/storage/innodb_plugin/include/btr0types.h +++ b/storage/innodb_plugin/include/btr0types.h @@ -38,6 +38,131 @@ typedef struct btr_cur_struct btr_cur_t; /** B-tree search information for the adaptive hash index */ typedef struct btr_search_struct btr_search_t; +#ifdef UNIV_BLOB_DEBUG +# include "buf0types.h" +/** An index->blobs entry for keeping track of off-page column references */ +typedef struct btr_blob_dbg_struct btr_blob_dbg_t; + +/** Insert to index->blobs a reference to an off-page column. +@param index the index tree +@param b the reference +@param ctx context (for logging) */ +UNIV_INTERN +void +btr_blob_dbg_rbt_insert( +/*====================*/ + dict_index_t* index, /*!< in/out: index tree */ + const btr_blob_dbg_t* b, /*!< in: the reference */ + const char* ctx) /*!< in: context (for logging) */ + __attribute__((nonnull)); + +/** Remove from index->blobs a reference to an off-page column. +@param index the index tree +@param b the reference +@param ctx context (for logging) */ +UNIV_INTERN +void +btr_blob_dbg_rbt_delete( +/*====================*/ + dict_index_t* index, /*!< in/out: index tree */ + const btr_blob_dbg_t* b, /*!< in: the reference */ + const char* ctx) /*!< in: context (for logging) */ + __attribute__((nonnull)); + +/**************************************************************//** +Add to index->blobs any references to off-page columns from a record. +@return number of references added */ +UNIV_INTERN +ulint +btr_blob_dbg_add_rec( +/*=================*/ + const rec_t* rec, /*!< in: record */ + dict_index_t* index, /*!< in/out: index */ + const ulint* offsets,/*!< in: offsets */ + const char* ctx) /*!< in: context (for logging) */ + __attribute__((nonnull)); +/**************************************************************//** +Remove from index->blobs any references to off-page columns from a record. +@return number of references removed */ +UNIV_INTERN +ulint +btr_blob_dbg_remove_rec( +/*====================*/ + const rec_t* rec, /*!< in: record */ + dict_index_t* index, /*!< in/out: index */ + const ulint* offsets,/*!< in: offsets */ + const char* ctx) /*!< in: context (for logging) */ + __attribute__((nonnull)); +/**************************************************************//** +Count and add to index->blobs any references to off-page columns +from records on a page. +@return number of references added */ +UNIV_INTERN +ulint +btr_blob_dbg_add( +/*=============*/ + const page_t* page, /*!< in: rewritten page */ + dict_index_t* index, /*!< in/out: index */ + const char* ctx) /*!< in: context (for logging) */ + __attribute__((nonnull)); +/**************************************************************//** +Count and remove from index->blobs any references to off-page columns +from records on a page. +Used when reorganizing a page, before copying the records. +@return number of references removed */ +UNIV_INTERN +ulint +btr_blob_dbg_remove( +/*================*/ + const page_t* page, /*!< in: b-tree page */ + dict_index_t* index, /*!< in/out: index */ + const char* ctx) /*!< in: context (for logging) */ + __attribute__((nonnull)); +/**************************************************************//** +Restore in index->blobs any references to off-page columns +Used when page reorganize fails due to compressed page overflow. */ +UNIV_INTERN +void +btr_blob_dbg_restore( +/*=================*/ + const page_t* npage, /*!< in: page that failed to compress */ + const page_t* page, /*!< in: copy of original page */ + dict_index_t* index, /*!< in/out: index */ + const char* ctx) /*!< in: context (for logging) */ + __attribute__((nonnull)); + +/** Operation that processes the BLOB references of an index record +@param[in] rec record on index page +@param[in/out] index the index tree of the record +@param[in] offsets rec_get_offsets(rec,index) +@param[in] ctx context (for logging) +@return number of BLOB references processed */ +typedef ulint (*btr_blob_dbg_op_f) +(const rec_t* rec,dict_index_t* index,const ulint* offsets,const char* ctx); + +/**************************************************************//** +Count and process all references to off-page columns on a page. +@return number of references processed */ +UNIV_INTERN +ulint +btr_blob_dbg_op( +/*============*/ + const page_t* page, /*!< in: B-tree leaf page */ + const rec_t* rec, /*!< in: record to start from + (NULL to process the whole page) */ + dict_index_t* index, /*!< in/out: index */ + const char* ctx, /*!< in: context (for logging) */ + const btr_blob_dbg_op_f op) /*!< in: operation on records */ + __attribute__((nonnull(1,3,4,5))); +#else /* UNIV_BLOB_DEBUG */ +# define btr_blob_dbg_add_rec(rec, index, offsets, ctx) ((void) 0) +# define btr_blob_dbg_add(page, index, ctx) ((void) 0) +# define btr_blob_dbg_remove_rec(rec, index, offsets, ctx) ((void) 0) +# define btr_blob_dbg_remove(page, index, ctx) ((void) 0) +# define btr_blob_dbg_restore(npage, page, index, ctx) ((void) 0) +# define btr_blob_dbg_op(page, rec, index, ctx, op) ((void) 0) +#endif /* UNIV_BLOB_DEBUG */ + /** The size of a reference to data stored on a different page. The reference is stored at the end of the prefix of the field in the index record. */ diff --git a/storage/innodb_plugin/include/dict0mem.h b/storage/innodb_plugin/include/dict0mem.h index 09a068ccb93..bd32a239cfd 100644 --- a/storage/innodb_plugin/include/dict0mem.h +++ b/storage/innodb_plugin/include/dict0mem.h @@ -340,6 +340,13 @@ struct dict_index_struct{ index, or 0 if the index existed when InnoDB was started up */ #endif /* !UNIV_HOTBACKUP */ +#ifdef UNIV_BLOB_DEBUG + mutex_t blobs_mutex; + /*!< mutex protecting blobs */ + void* blobs; /*!< map of (page_no,heap_no,field_no) + to first_blob_page_no; protected by + blobs_mutex; @see btr_blob_dbg_t */ +#endif /* UNIV_BLOB_DEBUG */ #ifdef UNIV_DEBUG ulint magic_n;/*!< magic number */ /** Value of dict_index_struct::magic_n */ diff --git a/storage/innodb_plugin/include/page0zip.h b/storage/innodb_plugin/include/page0zip.h index 574809e5227..00c1d0516e6 100644 --- a/storage/innodb_plugin/include/page0zip.h +++ b/storage/innodb_plugin/include/page0zip.h @@ -420,7 +420,7 @@ page_zip_copy_recs( const page_t* src, /*!< in: page */ dict_index_t* index, /*!< in: index of the B-tree */ mtr_t* mtr) /*!< in: mini-transaction */ - __attribute__((nonnull(1,2,3,4))); + __attribute__((nonnull)); #endif /* !UNIV_HOTBACKUP */ /**********************************************************************//** diff --git a/storage/innodb_plugin/include/univ.i b/storage/innodb_plugin/include/univ.i index 690bfd5d6a9..6dd38df3782 100644 --- a/storage/innodb_plugin/include/univ.i +++ b/storage/innodb_plugin/include/univ.i @@ -194,6 +194,8 @@ this will break redo log file compatibility, but it may be useful when debugging redo log application problems. */ #define UNIV_MEM_DEBUG /* detect memory leaks etc */ #define UNIV_IBUF_DEBUG /* debug the insert buffer */ +#define UNIV_BLOB_DEBUG /* track BLOB ownership; +assumes that no BLOBs survive server restart */ #define UNIV_IBUF_COUNT_DEBUG /* debug the insert buffer; this limits the database to IBUF_COUNT_N_SPACES and IBUF_COUNT_N_PAGES, and the insert buffer must be empty when the database is started */ diff --git a/storage/innodb_plugin/page/page0cur.c b/storage/innodb_plugin/page/page0cur.c index f10f16a7dd9..936762b986a 100644 --- a/storage/innodb_plugin/page/page0cur.c +++ b/storage/innodb_plugin/page/page0cur.c @@ -1149,6 +1149,8 @@ use_heap: current_rec, index, mtr); } + btr_blob_dbg_add_rec(insert_rec, index, offsets, "insert"); + return(insert_rec); } @@ -1195,10 +1197,12 @@ page_cur_insert_rec_zip_reorg( } /* Out of space: restore the page */ + btr_blob_dbg_remove(page, index, "insert_zip_fail"); if (!page_zip_decompress(page_zip, page, FALSE)) { ut_error; /* Memory corrupted? */ } ut_ad(page_validate(page, index)); + btr_blob_dbg_add(page, index, "insert_zip_fail"); return(NULL); } @@ -1490,6 +1494,8 @@ use_heap: page_zip_write_rec(page_zip, insert_rec, index, offsets, 1); + btr_blob_dbg_add_rec(insert_rec, index, offsets, "insert_zip_ok"); + /* 9. Write log record of the insert */ if (UNIV_LIKELY(mtr != NULL)) { page_cur_insert_rec_write_log(insert_rec, rec_size, @@ -1697,6 +1703,9 @@ page_copy_rec_list_end_to_created_page( heap_top += rec_size; + rec_offs_make_valid(insert_rec, index, offsets); + btr_blob_dbg_add_rec(insert_rec, index, offsets, "copy_end"); + page_cur_insert_rec_write_log(insert_rec, rec_size, prev_rec, index, mtr); prev_rec = insert_rec; @@ -1944,6 +1953,7 @@ page_cur_delete_rec( page_dir_slot_set_n_owned(cur_dir_slot, page_zip, cur_n_owned - 1); /* 6. Free the memory occupied by the record */ + btr_blob_dbg_remove_rec(current_rec, index, offsets, "delete"); page_mem_free(page, page_zip, current_rec, index, offsets); /* 7. Now we have decremented the number of owned records of the slot. diff --git a/storage/innodb_plugin/page/page0page.c b/storage/innodb_plugin/page/page0page.c index 10008f9ac25..6cae03e8829 100644 --- a/storage/innodb_plugin/page/page0page.c +++ b/storage/innodb_plugin/page/page0page.c @@ -685,12 +685,16 @@ page_copy_rec_list_end( if (UNIV_UNLIKELY (!page_zip_reorganize(new_block, index, mtr))) { + btr_blob_dbg_remove(new_page, index, + "copy_end_reorg_fail"); if (UNIV_UNLIKELY (!page_zip_decompress(new_page_zip, new_page, FALSE))) { ut_error; } ut_ad(page_validate(new_page, index)); + btr_blob_dbg_add(new_page, index, + "copy_end_reorg_fail"); return(NULL); } else { /* The page was reorganized: @@ -803,12 +807,16 @@ page_copy_rec_list_start( if (UNIV_UNLIKELY (!page_zip_reorganize(new_block, index, mtr))) { + btr_blob_dbg_remove(new_page, index, + "copy_start_reorg_fail"); if (UNIV_UNLIKELY (!page_zip_decompress(new_page_zip, new_page, FALSE))) { ut_error; } ut_ad(page_validate(new_page, index)); + btr_blob_dbg_add(new_page, index, + "copy_start_reorg_fail"); return(NULL); } else { /* The page was reorganized: @@ -1080,6 +1088,9 @@ page_delete_rec_list_end( /* Remove the record chain segment from the record chain */ page_rec_set_next(prev_rec, page_get_supremum_rec(page)); + btr_blob_dbg_op(page, rec, index, "delete_end", + btr_blob_dbg_remove_rec); + /* Catenate the deleted chain segment to the page free list */ page_rec_set_next(last_rec, page_header_get_ptr(page, PAGE_FREE)); diff --git a/storage/innodb_plugin/page/page0zip.c b/storage/innodb_plugin/page/page0zip.c index bb9b0995c72..a1dd4177ba8 100644 --- a/storage/innodb_plugin/page/page0zip.c +++ b/storage/innodb_plugin/page/page0zip.c @@ -4451,6 +4451,8 @@ page_zip_reorganize( /* Copy the old page to temporary space */ buf_frame_copy(temp_page, page); + btr_blob_dbg_remove(page, index, "zip_reorg"); + /* Recreate the page: note that global data on page (possible segment headers, next page-field, etc.) is preserved intact */ @@ -4509,7 +4511,7 @@ page_zip_copy_recs( mtr_t* mtr) /*!< in: mini-transaction */ { ut_ad(mtr_memo_contains_page(mtr, page, MTR_MEMO_PAGE_X_FIX)); - ut_ad(mtr_memo_contains_page(mtr, (page_t*) src, MTR_MEMO_PAGE_X_FIX)); + ut_ad(mtr_memo_contains_page(mtr, src, MTR_MEMO_PAGE_X_FIX)); ut_ad(!dict_index_is_ibuf(index)); #ifdef UNIV_ZIP_DEBUG /* The B-tree operations that call this function may set @@ -4579,6 +4581,7 @@ page_zip_copy_recs( #ifdef UNIV_ZIP_DEBUG ut_a(page_zip_validate(page_zip, page)); #endif /* UNIV_ZIP_DEBUG */ + btr_blob_dbg_add(page, index, "page_zip_copy_recs"); page_zip_compress_write_log(page_zip, page, index, mtr); } diff --git a/storage/innodb_plugin/row/row0upd.c b/storage/innodb_plugin/row/row0upd.c index 9ded3d68018..3a6de4b94a7 100644 --- a/storage/innodb_plugin/row/row0upd.c +++ b/storage/innodb_plugin/row/row0upd.c @@ -498,14 +498,49 @@ row_upd_rec_in_place( n_fields = upd_get_n_fields(update); for (i = 0; i < n_fields; i++) { +#ifdef UNIV_BLOB_DEBUG + btr_blob_dbg_t b; + const byte* field_ref = NULL; +#endif /* UNIV_BLOB_DEBUG */ + upd_field = upd_get_nth_field(update, i); new_val = &(upd_field->new_val); ut_ad(!dfield_is_ext(new_val) == !rec_offs_nth_extern(offsets, upd_field->field_no)); +#ifdef UNIV_BLOB_DEBUG + if (dfield_is_ext(new_val)) { + ulint len; + field_ref = rec_get_nth_field(rec, offsets, i, &len); + ut_a(len != UNIV_SQL_NULL); + ut_a(len >= BTR_EXTERN_FIELD_REF_SIZE); + field_ref += len - BTR_EXTERN_FIELD_REF_SIZE; + + b.ref_page_no = page_get_page_no(page_align(rec)); + b.ref_heap_no = page_rec_get_heap_no(rec); + b.ref_field_no = i; + b.blob_page_no = mach_read_from_4( + field_ref + BTR_EXTERN_PAGE_NO); + ut_a(b.ref_field_no >= index->n_uniq); + btr_blob_dbg_rbt_delete(index, &b, "upd_in_place"); + } +#endif /* UNIV_BLOB_DEBUG */ rec_set_nth_field(rec, offsets, upd_field->field_no, dfield_get_data(new_val), dfield_get_len(new_val)); + +#ifdef UNIV_BLOB_DEBUG + if (dfield_is_ext(new_val)) { + b.blob_page_no = mach_read_from_4( + field_ref + BTR_EXTERN_PAGE_NO); + b.always_owner = b.owner = !(field_ref[BTR_EXTERN_LEN] + & BTR_EXTERN_OWNER_FLAG); + b.del = rec_get_deleted_flag( + rec, rec_offs_comp(offsets)); + + btr_blob_dbg_rbt_insert(index, &b, "upd_in_place"); + } +#endif /* UNIV_BLOB_DEBUG */ } if (UNIV_LIKELY_NULL(page_zip)) { diff --git a/storage/innodb_plugin/srv/srv0start.c b/storage/innodb_plugin/srv/srv0start.c index 73f8f319704..f8b5049ca65 100644 --- a/storage/innodb_plugin/srv/srv0start.c +++ b/storage/innodb_plugin/srv/srv0start.c @@ -1061,6 +1061,12 @@ innobase_start_or_create_for_mysql(void) ); #endif +#ifdef UNIV_BLOB_DEBUG + fprintf(stderr, + "InnoDB: !!!!!!!! UNIV_BLOB_DEBUG switched on !!!!!!!!!\n" + "InnoDB: Server restart may fail with UNIV_BLOB_DEBUG\n"); +#endif /* UNIV_BLOB_DEBUG */ + #ifdef UNIV_SYNC_DEBUG fprintf(stderr, "InnoDB: !!!!!!!! UNIV_SYNC_DEBUG switched on !!!!!!!!!\n"); From 82d8ca01c8a807addd5dbc2923d276b53977a6f8 Mon Sep 17 00:00:00 2001 From: Jimmy Yang Date: Wed, 9 Feb 2011 01:15:06 -0800 Subject: [PATCH 02/13] Fix Bug #59048 truncate table or create index could leave index->page to be FIL_NULL rb://545 approved by Sunny Bains --- storage/innobase/dict/dict0load.c | 34 ++++++++++++++++++++------- storage/innobase/include/dict0dict.h | 12 ++++++++++ storage/innobase/include/dict0dict.ic | 30 ++++++++++++++++++++++- storage/innobase/include/dict0load.h | 5 +++- storage/innobase/include/dict0mem.h | 4 ++++ storage/innobase/include/dict0types.h | 14 +++++++++++ storage/innobase/row/row0mysql.c | 6 ++--- 7 files changed, 92 insertions(+), 13 deletions(-) diff --git a/storage/innobase/dict/dict0load.c b/storage/innobase/dict/dict0load.c index 3561e7220ab..ca22132a360 100644 --- a/storage/innobase/dict/dict0load.c +++ b/storage/innobase/dict/dict0load.c @@ -1323,7 +1323,10 @@ ulint dict_load_indexes( /*==============*/ dict_table_t* table, /*!< in/out: table */ - mem_heap_t* heap) /*!< in: memory heap for temporary storage */ + mem_heap_t* heap, /*!< in: memory heap for temporary storage */ + dict_err_ignore_t ignore_err) + /*!< in: error to be ignored when + loading the index definition */ { dict_table_t* sys_indexes; dict_index_t* sys_index; @@ -1406,10 +1409,22 @@ dict_load_indexes( "InnoDB: but the index tree has been freed!\n", index->name, table->name); + if (ignore_err & DICT_ERR_IGNORE_INDEX_ROOT) { + /* If caller can tolerate this error, + we will continue to load the index and + let caller deal with this error. However + mark the index and table corrupted */ + index->corrupted = TRUE; + table->corrupted = TRUE; + fprintf(stderr, + "InnoDB: Index is corrupt but forcing" + " load into data dictionary\n"); + } else { corrupted: - dict_mem_index_free(index); - error = DB_CORRUPTION; - goto func_exit; + dict_mem_index_free(index); + error = DB_CORRUPTION; + goto func_exit; + } } else if (!dict_index_is_clust(index) && NULL == dict_table_get_first_index(table)) { @@ -1618,7 +1633,10 @@ dict_load_table( /*============*/ const char* name, /*!< in: table name in the databasename/tablename format */ - ibool cached) /*!< in: TRUE=add to cache, FALSE=do not */ + ibool cached, /*!< in: TRUE=add to cache, FALSE=do not */ + dict_err_ignore_t ignore_err) + /*!< in: error to be ignored when loading + table and its indexes' definition */ { dict_table_t* table; dict_table_t* sys_tables; @@ -1733,7 +1751,7 @@ err_exit: mem_heap_empty(heap); - err = dict_load_indexes(table, heap); + err = dict_load_indexes(table, heap, ignore_err); /* Initialize table foreign_child value. Its value could be changed when dict_load_foreigns() is called below */ @@ -1869,7 +1887,7 @@ dict_load_table_on_id( 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), - TRUE); + TRUE, DICT_ERR_IGNORE_NONE); btr_pcur_close(&pcur); mtr_commit(&mtr); @@ -1894,7 +1912,7 @@ dict_load_sys_table( heap = mem_heap_create(1000); - dict_load_indexes(table, heap); + dict_load_indexes(table, heap, DICT_ERR_IGNORE_NONE); mem_heap_free(heap); } diff --git a/storage/innobase/include/dict0dict.h b/storage/innobase/include/dict0dict.h index 033c435bf16..d6f2bebae3a 100644 --- a/storage/innobase/include/dict0dict.h +++ b/storage/innobase/include/dict0dict.h @@ -441,6 +441,18 @@ function. @return table, NULL if not found */ UNIV_INLINE dict_table_t* +dict_table_get_low_ignore_err( +/*===========================*/ + const char* table_name, /*!< in: table name */ + dict_err_ignore_t + ignore_err); /*!< in: error to be ignored when + loading a table definition */ +/**********************************************************************//** +Gets a table; loads it to the dictionary cache if necessary. A low-level +function. +@return table, NULL if not found */ +UNIV_INLINE +dict_table_t* dict_table_get_low( /*===============*/ const char* table_name); /*!< in: table name */ diff --git a/storage/innobase/include/dict0dict.ic b/storage/innobase/include/dict0dict.ic index 42f124dedfc..59606af7056 100644 --- a/storage/innobase/include/dict0dict.ic +++ b/storage/innobase/include/dict0dict.ic @@ -827,6 +827,34 @@ dict_table_check_if_in_cache_low( return(table); } +/**********************************************************************//** +load a table into dictionary cache, ignore any error specified during load; +@return table, NULL if not found */ +UNIV_INLINE +dict_table_t* +dict_table_get_low_ignore_err( +/*==========================*/ + const char* table_name, /*!< in: table name */ + dict_err_ignore_t + ignore_err) /*!< in: error to be ignored when + loading a table definition */ +{ + dict_table_t* table; + + ut_ad(table_name); + ut_ad(mutex_own(&(dict_sys->mutex))); + + table = dict_table_check_if_in_cache_low(table_name); + + if (table == NULL) { + table = dict_load_table(table_name, TRUE, ignore_err); + } + + ut_ad(!table || table->cached); + + return(table); +} + /**********************************************************************//** Gets a table; loads it to the dictionary cache if necessary. A low-level function. @@ -845,7 +873,7 @@ dict_table_get_low( table = dict_table_check_if_in_cache_low(table_name); if (table == NULL) { - table = dict_load_table(table_name, TRUE); + table = dict_load_table(table_name, TRUE, DICT_ERR_IGNORE_NONE); } ut_ad(!table || table->cached); diff --git a/storage/innobase/include/dict0load.h b/storage/innobase/include/dict0load.h index f009f221f32..51d07f43446 100644 --- a/storage/innobase/include/dict0load.h +++ b/storage/innobase/include/dict0load.h @@ -170,7 +170,10 @@ dict_load_table( /*============*/ const char* name, /*!< in: table name in the databasename/tablename format */ - ibool cached);/*!< in: TRUE=add to cache, FALSE=do not */ + ibool cached, /*!< in: TRUE=add to cache, FALSE=do not */ + dict_err_ignore_t ignore_err); + /*!< in: error to be ignored when loading + table and its indexes' definition */ /***********************************************************************//** Loads a table object based on the table id. @return table; NULL if table does not exist */ diff --git a/storage/innobase/include/dict0mem.h b/storage/innobase/include/dict0mem.h index 95bcb0cab2d..75d3b2c3302 100644 --- a/storage/innobase/include/dict0mem.h +++ b/storage/innobase/include/dict0mem.h @@ -361,6 +361,8 @@ struct dict_index_struct{ /*!< TRUE if this index is marked to be dropped in ha_innobase::prepare_drop_index(), otherwise FALSE */ + unsigned corrupted:1; + /*!< TRUE if the index object is corrupted */ dict_field_t* fields; /*!< array of field descriptions */ #ifndef UNIV_HOTBACKUP UT_LIST_NODE_T(dict_index_t) @@ -494,6 +496,8 @@ struct dict_table_struct{ to the dictionary cache */ unsigned n_def:10;/*!< number of columns defined so far */ unsigned n_cols:10;/*!< number of columns */ + unsigned corrupted:1; + /*!< TRUE if table is corrupted */ dict_col_t* cols; /*!< array of column descriptions */ const char* col_names; /*!< Column names packed in a character string diff --git a/storage/innobase/include/dict0types.h b/storage/innobase/include/dict0types.h index 687209575c9..8cbd7cd5783 100644 --- a/storage/innobase/include/dict0types.h +++ b/storage/innobase/include/dict0types.h @@ -43,4 +43,18 @@ typedef struct tab_node_struct tab_node_t; typedef ib_id_t table_id_t; typedef ib_id_t index_id_t; +/** Error to ignore when we load table dictionary into memory. However, +the table and index will be marked as "corrupted", and caller will +be responsible to deal with corrupted table or index. +Note: please define the IGNORE_ERR_* as bits, so their value can +be or-ed together */ +enum dict_err_ignore { + DICT_ERR_IGNORE_NONE = 0, /*!< no error to ignore */ + DICT_ERR_IGNORE_INDEX_ROOT = 1, /*!< ignore error if index root + page is FIL_NUL or incorrect value */ + DICT_ERR_IGNORE_ALL = 0xFFFF /*!< ignore all errors */ +}; + +typedef enum dict_err_ignore dict_err_ignore_t; + #endif diff --git a/storage/innobase/row/row0mysql.c b/storage/innobase/row/row0mysql.c index a7b0cabfa4b..5fb4b4ac8c3 100644 --- a/storage/innobase/row/row0mysql.c +++ b/storage/innobase/row/row0mysql.c @@ -3132,7 +3132,7 @@ row_drop_table_for_mysql( ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX)); #endif /* UNIV_SYNC_DEBUG */ - table = dict_table_get_low(name); + table = dict_table_get_low_ignore_err(name, DICT_ERR_IGNORE_INDEX_ROOT); if (!table) { err = DB_TABLE_NOT_FOUND; @@ -3367,7 +3367,7 @@ check_next_foreign: dict_table_remove_from_cache(table); - if (dict_load_table(name, TRUE) != NULL) { + if (dict_load_table(name, TRUE, DICT_ERR_IGNORE_NONE) != NULL) { ut_print_timestamp(stderr); fputs(" InnoDB: Error: not able to remove table ", stderr); @@ -3513,7 +3513,7 @@ row_mysql_drop_temp_tables(void) btr_pcur_store_position(&pcur, &mtr); btr_pcur_commit_specify_mtr(&pcur, &mtr); - table = dict_load_table(table_name, TRUE); + table = dict_load_table(table_name, TRUE, DICT_ERR_IGNORE_NONE); if (table) { row_drop_table_for_mysql(table_name, trx, FALSE); From 321b238874f8305ec5650f46455d66552726fc3b Mon Sep 17 00:00:00 2001 From: Jimmy Yang Date: Thu, 10 Feb 2011 00:58:23 -0800 Subject: [PATCH 03/13] Merge a fix for Bug #53756 ALTER TABLE ADD PRIMARY KEY affects crash recovery This was already pushed to mysql-5.1-innodb some time ago (revision id jimmy.yang@oracle.com-20100907054137-tpuior7hez4f5ghl) but it was not merged to the 5.5 trees. --- .../suite/innodb/r/innodb_bug53756.result | 118 +++++++++++ .../suite/innodb/t/innodb_bug53756-master.opt | 1 + .../suite/innodb/t/innodb_bug53756.test | 184 ++++++++++++++++++ storage/innobase/dict/dict0load.c | 27 ++- 4 files changed, 316 insertions(+), 14 deletions(-) create mode 100644 mysql-test/suite/innodb/r/innodb_bug53756.result create mode 100644 mysql-test/suite/innodb/t/innodb_bug53756-master.opt 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..37453be8201 --- /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. +# 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; +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-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 new file mode 100644 index 00000000000..67a95371821 --- /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 # 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 +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 ca22132a360..14490980bb6 100644 --- a/storage/innobase/dict/dict0load.c +++ b/storage/innobase/dict/dict0load.c @@ -1828,6 +1828,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. */ @@ -1854,15 +1856,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) - || rec_get_deleted_flag(rec, 0)) { + if (!btr_pcur_is_on_user_rec(&pcur)) { /* 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); } /*---------------------------------------------------*/ @@ -1875,12 +1879,7 @@ dict_load_table_on_id( /* Check if the table id in record is the one searched for */ if (table_id != mach_read_from_8(field)) { - - 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 */ @@ -1888,7 +1887,7 @@ dict_load_table_on_id( /* Load the table definition to memory */ table = dict_load_table(mem_heap_strdupl(heap, (char*) field, len), TRUE, DICT_ERR_IGNORE_NONE); - +func_exit: btr_pcur_close(&pcur); mtr_commit(&mtr); mem_heap_free(heap); From 8aa7e213dc7127bc334087eed5dbcd17e2865a47 Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Thu, 10 Feb 2011 13:21:22 +0200 Subject: [PATCH 04/13] Increment InnoDB Plugin version from 1.0.15 to 1.0.16. InnoDB Plugin 1.0.15 has been released with MySQL 5.1.55. --- 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 6dd38df3782..22ed765e680 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 15 +#define INNODB_VERSION_BUGFIX 16 /* The following is the InnoDB version as shown in SELECT plugin_version FROM information_schema.plugins; From f7bd0a33be2513cf441da3147be06b830296f35e Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Thu, 10 Feb 2011 13:25:12 +0200 Subject: [PATCH 05/13] Increment InnoDB version from 1.1.5 to 1.1.6 InnoDB 1.1.5 was released with MySQL 5.5.9 --- 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 f18673552b6..d82249fd6a8 100644 --- a/storage/innobase/include/univ.i +++ b/storage/innobase/include/univ.i @@ -51,7 +51,7 @@ Created 1/20/1994 Heikki Tuuri #define INNODB_VERSION_MAJOR 1 #define INNODB_VERSION_MINOR 1 -#define INNODB_VERSION_BUGFIX 5 +#define INNODB_VERSION_BUGFIX 6 /* The following is the InnoDB version as shown in SELECT plugin_version FROM information_schema.plugins; From e10775a38acf10e4ea0e588c2d8ef10f1bfb6c20 Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Thu, 10 Feb 2011 13:28:58 +0200 Subject: [PATCH 06/13] Extend the valgrind suppression to cover one more code path: ==3307== Invalid read of size 1 ==3307== at 0xA8389A: mach_read_from_4 (mach0data.ic:182) ==3307== by 0xA833E3: buf_buddy_relocate (buf0buddy.c:450) ==3307== by 0xA8451E: buf_buddy_free_low (buf0buddy.c:619) ==3307== by 0x999326: buf_buddy_free (buf0buddy.ic:131) ==3307== by 0x998B66: buf_page_get_gen (buf0buf.c:2968) ==3307== by 0x9765D4: btr_cur_open_at_rnd_pos_func (btr0cur.c:985) ==3307== by 0x97F5EB: btr_estimate_number_of_different_key_vals (btr0cur.c:3621) ==3307== by 0x9C8694: dict_update_statistics (dict0dict.c:4312) ==3307== by 0x8FCF85: row_update_statistics_if_needed (row0mysql.c:933) ==3307== by 0x8FCEF8: row_insert_for_mysql (row0mysql.c:1240) ==3307== by 0x8E8134: ha_innobase::write_row(unsigned char*) (ha_innodb.cc:4988) ==3307== by 0x73CE6D: handler::ha_write_row(unsigned char*) (handler.cc:4776) ==3307== by 0x6512DC: copy_data_between_tables(TABLE*, TABLE*, List&, bool, unsigned int, st_order*, unsigned long long*, unsigned long long*, enum_enable_or_disable, bool) (sql_table.cc:7017) ==3307== by 0x65E1E1: mysql_alter_table(THD*, char*, char*, st_ha_create_information*, TABLE_LIST*, Alter_info*, unsigned int, st_order*, bool) (sql_table.cc:6416) ==3307== by 0x893920: Alter_table_statement::execute(THD*) (sql_alter.cc:106) ==3307== by 0x5DDB3B: mysql_execute_command(THD*) (sql_parse.cc:4321) --- mysql-test/valgrind.supp | 1 - 1 file changed, 1 deletion(-) diff --git a/mysql-test/valgrind.supp b/mysql-test/valgrind.supp index 6bb1308320f..923d0cf5351 100644 --- a/mysql-test/valgrind.supp +++ b/mysql-test/valgrind.supp @@ -876,5 +876,4 @@ fun:buf_buddy_relocate fun:buf_buddy_free_low fun:buf_buddy_free - fun:buf_LRU_block_remove_hashed_page } From 79d7a9bb3f8bbf845228612d52ce5384eb4e7bc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 10 Feb 2011 14:07:58 +0200 Subject: [PATCH 07/13] Bug #59877 Wrong buffer pool selected in buf_read_ibuf_merge_pages() --- storage/innobase/buf/buf0rea.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/innobase/buf/buf0rea.c b/storage/innobase/buf/buf0rea.c index 82b45172988..17704a4b65d 100644 --- a/storage/innobase/buf/buf0rea.c +++ b/storage/innobase/buf/buf0rea.c @@ -530,7 +530,7 @@ buf_read_ibuf_merge_pages( buf_pool_t* buf_pool; ulint zip_size = fil_space_get_zip_size(space_ids[i]); - buf_pool = buf_pool_get(space_ids[i], space_versions[i]); + buf_pool = buf_pool_get(space_ids[i], page_nos[i]); while (buf_pool->n_pend_reads > buf_pool->curr_size / BUF_READ_AHEAD_PEND_LIMIT) { From b5c61ed1f25001e6a967bc2e70273d61bd22bab6 Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Thu, 10 Feb 2011 17:16:32 +0200 Subject: [PATCH 08/13] Fix Bug#59307 Valgrind: uninitialized value in rw_lock_set_writer_id_and_recursion_flag() by silencing a bogus Valgrind warning: ==4392== Conditional jump or move depends on uninitialised value(s) ==4392== at 0x5A18416: rw_lock_set_writer_id_and_recursion_flag (sync0rw.ic:283) ==4392== by 0x5A1865C: rw_lock_x_lock_low (sync0rw.c:558) ==4392== by 0x5A18481: rw_lock_x_lock_func (sync0rw.c:617) ==4392== by 0x597EEE6: mtr_x_lock_func (mtr0mtr.ic:271) ==4392== by 0x597EBBD: fsp_header_init (fsp0fsp.c:970) ==4392== by 0x5A15E78: innobase_start_or_create_for_mysql (srv0start.c:1508) ==4392== by 0x598B789: innobase_init(void*) (ha_innodb.cc:2282) os_compare_and_swap_thread_id() is defined as __sync_bool_compare_and_swap(). From the GCC doc: `bool __sync_bool_compare_and_swap (TYPE *ptr, TYPE oldval TYPE newval, ...)' ... The "bool" version returns true if the comparison is successful and NEWVAL was written. So it is not possible that the return value is uninitialized, no matter what the arguments to os_compare_and_swap_thread_id() are. Probably Valgrind gets confused by the implementation of the GCC internal function __sync_bool_compare_and_swap(). --- storage/innodb_plugin/include/sync0rw.ic | 1 + 1 file changed, 1 insertion(+) diff --git a/storage/innodb_plugin/include/sync0rw.ic b/storage/innodb_plugin/include/sync0rw.ic index 7116f1b7c9b..4110a0a7e0c 100644 --- a/storage/innodb_plugin/include/sync0rw.ic +++ b/storage/innodb_plugin/include/sync0rw.ic @@ -280,6 +280,7 @@ rw_lock_set_writer_id_and_recursion_flag( local_thread = lock->writer_thread; success = os_compare_and_swap_thread_id( &lock->writer_thread, local_thread, curr_thread); + UNIV_MEM_VALID(&success, sizeof(success)); ut_a(success); lock->recursive = recursive; From 4a8c83574626f4107ca84964f4c1faa13176eff1 Mon Sep 17 00:00:00 2001 From: Jimmy Yang Date: Mon, 14 Feb 2011 02:07:59 -0800 Subject: [PATCH 09/13] Fix Bug #59749 Enabling concurrent reads while creating non-primary unique index gives failures. Approved by Marko --- storage/innodb_plugin/ChangeLog | 6 ++++++ storage/innodb_plugin/handler/handler0alter.cc | 12 ++++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/storage/innodb_plugin/ChangeLog b/storage/innodb_plugin/ChangeLog index 0cbdc8ed9d2..9fa00ac8e6f 100644 --- a/storage/innodb_plugin/ChangeLog +++ b/storage/innodb_plugin/ChangeLog @@ -1,3 +1,9 @@ +2011-02-14 The InnoDB Team + + * handler/handler0alter.cc: + Bug#59749 Enabling concurrent reads while creating non-primary + unique index gives failures + 2011-01-31 The InnoDB Team * btr/btr0cur.c, include/row0upd.h, diff --git a/storage/innodb_plugin/handler/handler0alter.cc b/storage/innodb_plugin/handler/handler0alter.cc index 517445f7e69..dc1317d5c5a 100644 --- a/storage/innodb_plugin/handler/handler0alter.cc +++ b/storage/innodb_plugin/handler/handler0alter.cc @@ -782,10 +782,6 @@ err_exit: ut_ad(error == DB_SUCCESS); - /* We will need to rebuild index translation table. Set - valid index entry count in the translation table to zero */ - share->idx_trans_tbl.index_count = 0; - /* Commit the data dictionary transaction in order to release the table locks on the system tables. This means that if MySQL crashes while creating a new primary key inside @@ -911,6 +907,14 @@ error: } convert_error: + if (error == DB_SUCCESS) { + /* Build index is successful. We will need to + rebuild index translation table. Reset the + index entry count in the translation table + to zero, so that translation table will be rebuilt */ + share->idx_trans_tbl.index_count = 0; + } + error = convert_error_code_to_mysql(error, innodb_table->flags, user_thd); From 0efaef7d469eb6decdd8cf17057154914a10fd41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 15 Feb 2011 10:51:33 +0200 Subject: [PATCH 10/13] Bug#59307 Valgrind: uninitialized value in rw_lock_set_writer_id_and_recursion_flag() rw_lock_create_func(): Initialize lock->writer_thread, so that Valgrind will not complain even when Valgrind instrumentation is not enabled. Flag lock->writer_thread uninitialized, so that Valgrind can complain when it is used uninitialized. rw_lock_set_writer_id_and_recursion_flag(): Revert the bogus Valgrind instrumentation that was pushed in the first attempt to fix this bug. --- .../innodb_plugin/r/innodb_bug59307.result | 28 ++++++++++++++++ .../innodb_plugin/t/innodb_bug59307.test | 32 +++++++++++++++++++ storage/innodb_plugin/ChangeLog | 6 ++++ storage/innodb_plugin/include/sync0rw.ic | 1 - storage/innodb_plugin/sync/sync0rw.c | 3 ++ 5 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 mysql-test/suite/innodb_plugin/r/innodb_bug59307.result create mode 100644 mysql-test/suite/innodb_plugin/t/innodb_bug59307.test diff --git a/mysql-test/suite/innodb_plugin/r/innodb_bug59307.result b/mysql-test/suite/innodb_plugin/r/innodb_bug59307.result new file mode 100644 index 00000000000..0d726e83708 --- /dev/null +++ b/mysql-test/suite/innodb_plugin/r/innodb_bug59307.result @@ -0,0 +1,28 @@ +CREATE TABLE t1 ( +t1_int INT, +t1_time TIME +) ENGINE=innodb; +CREATE TABLE t2 ( +t2_int int PRIMARY KEY, +t2_int2 INT +) ENGINE=INNODB; +INSERT INTO t2 VALUES (); +Warnings: +Warning 1364 Field 't2_int' doesn't have a default value +INSERT INTO t1 VALUES (); +SELECT * +FROM t1 AS t1a +WHERE NOT EXISTS +(SELECT * +FROM t1 AS t1b +WHERE t1b.t1_int NOT IN +(SELECT t2.t2_int +FROM t2 +WHERE t1b.t1_time LIKE t1b.t1_int +OR t1b.t1_time <> t2.t2_int2 +AND 6=7 +) +) +; +t1_int t1_time +DROP TABLE t1,t2; diff --git a/mysql-test/suite/innodb_plugin/t/innodb_bug59307.test b/mysql-test/suite/innodb_plugin/t/innodb_bug59307.test new file mode 100644 index 00000000000..9c68adf36cf --- /dev/null +++ b/mysql-test/suite/innodb_plugin/t/innodb_bug59307.test @@ -0,0 +1,32 @@ +-- source include/have_innodb_plugin.inc +# Bug #59307 uninitialized value in rw_lock_set_writer_id_and_recursion_flag() +# when Valgrind instrumentation (UNIV_DEBUG_VALGRIND) is not enabled + +CREATE TABLE t1 ( + t1_int INT, + t1_time TIME +) ENGINE=innodb; + +CREATE TABLE t2 ( + t2_int int PRIMARY KEY, + t2_int2 INT +) ENGINE=INNODB; + +INSERT INTO t2 VALUES (); +INSERT INTO t1 VALUES (); + +SELECT * +FROM t1 AS t1a +WHERE NOT EXISTS + (SELECT * + FROM t1 AS t1b + WHERE t1b.t1_int NOT IN + (SELECT t2.t2_int + FROM t2 + WHERE t1b.t1_time LIKE t1b.t1_int + OR t1b.t1_time <> t2.t2_int2 + AND 6=7 + ) +) +; +DROP TABLE t1,t2; diff --git a/storage/innodb_plugin/ChangeLog b/storage/innodb_plugin/ChangeLog index 9fa00ac8e6f..1b2747ab012 100644 --- a/storage/innodb_plugin/ChangeLog +++ b/storage/innodb_plugin/ChangeLog @@ -1,3 +1,9 @@ +2011-02-15 The InnoDB Team + + * sync/sync0rw.c, innodb_bug59307.test: + Bug#59307 Valgrind: uninitialized value in + rw_lock_set_writer_id_and_recursion_flag() + 2011-02-14 The InnoDB Team * handler/handler0alter.cc: diff --git a/storage/innodb_plugin/include/sync0rw.ic b/storage/innodb_plugin/include/sync0rw.ic index 4110a0a7e0c..7116f1b7c9b 100644 --- a/storage/innodb_plugin/include/sync0rw.ic +++ b/storage/innodb_plugin/include/sync0rw.ic @@ -280,7 +280,6 @@ rw_lock_set_writer_id_and_recursion_flag( local_thread = lock->writer_thread; success = os_compare_and_swap_thread_id( &lock->writer_thread, local_thread, curr_thread); - UNIV_MEM_VALID(&success, sizeof(success)); ut_a(success); lock->recursive = recursive; diff --git a/storage/innodb_plugin/sync/sync0rw.c b/storage/innodb_plugin/sync/sync0rw.c index 00e0324becd..a5da606ad80 100644 --- a/storage/innodb_plugin/sync/sync0rw.c +++ b/storage/innodb_plugin/sync/sync0rw.c @@ -260,6 +260,9 @@ rw_lock_create_func( contains garbage at initialization and cannot be used for recursive x-locking. */ lock->recursive = FALSE; + /* Silence Valgrind when UNIV_DEBUG_VALGRIND is not enabled. */ + memset((void*) &lock->writer_thread, 0, sizeof lock->writer_thread); + UNIV_MEM_INVALID(&lock->writer_thread, sizeof lock->writer_thread); #ifdef UNIV_SYNC_DEBUG UT_LIST_INIT(lock->debug_list); From cb884043b6ea225c0093c2ae5dba7575214fac49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 15 Feb 2011 12:12:27 +0200 Subject: [PATCH 11/13] Add a test for suspected Bug#60049. --- .../suite/innodb/r/innodb_bug60049.result | 8 ++++ .../suite/innodb/t/innodb_bug60049-master.opt | 1 + .../suite/innodb/t/innodb_bug60049.test | 38 +++++++++++++++++++ .../innodb_plugin/r/innodb_bug60049.result | 8 ++++ .../t/innodb_bug60049-master.opt | 1 + .../innodb_plugin/t/innodb_bug60049.test | 38 +++++++++++++++++++ 6 files changed, 94 insertions(+) create mode 100644 mysql-test/suite/innodb/r/innodb_bug60049.result create mode 100644 mysql-test/suite/innodb/t/innodb_bug60049-master.opt create mode 100644 mysql-test/suite/innodb/t/innodb_bug60049.test create mode 100644 mysql-test/suite/innodb_plugin/r/innodb_bug60049.result create mode 100644 mysql-test/suite/innodb_plugin/t/innodb_bug60049-master.opt create mode 100644 mysql-test/suite/innodb_plugin/t/innodb_bug60049.test diff --git a/mysql-test/suite/innodb/r/innodb_bug60049.result b/mysql-test/suite/innodb/r/innodb_bug60049.result new file mode 100644 index 00000000000..bec0e05a897 --- /dev/null +++ b/mysql-test/suite/innodb/r/innodb_bug60049.result @@ -0,0 +1,8 @@ +CREATE TABLE t(a INT)ENGINE=InnoDB; +RENAME TABLE t TO u; +DROP TABLE u; +SELECT @@innodb_fast_shutdown; +@@innodb_fast_shutdown +0 +Last record of ID_IND root page (9): +1808000018050074000000000000000c5359535f464f524549474e5f434f4c53 diff --git a/mysql-test/suite/innodb/t/innodb_bug60049-master.opt b/mysql-test/suite/innodb/t/innodb_bug60049-master.opt new file mode 100644 index 00000000000..22a5d4ed221 --- /dev/null +++ b/mysql-test/suite/innodb/t/innodb_bug60049-master.opt @@ -0,0 +1 @@ +--innodb_fast_shutdown=0 diff --git a/mysql-test/suite/innodb/t/innodb_bug60049.test b/mysql-test/suite/innodb/t/innodb_bug60049.test new file mode 100644 index 00000000000..1e0feaf0c89 --- /dev/null +++ b/mysql-test/suite/innodb/t/innodb_bug60049.test @@ -0,0 +1,38 @@ +# Bug #60049 Verify that purge leaves no garbage in unique secondary indexes +# This test requires a fresh server start-up and a slow shutdown. +# This was a suspected bug (not a bug). + +-- source include/have_innodb.inc + +CREATE TABLE t(a INT)ENGINE=InnoDB; +RENAME TABLE t TO u; +DROP TABLE u; +SELECT @@innodb_fast_shutdown; +let $MYSQLD_DATADIR=`select @@datadir`; + +# Shut down the server +-- exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +-- shutdown_server 10 +-- source include/wait_until_disconnected.inc + +# Check the tail of ID_IND (SYS_TABLES.ID) +let IBDATA1=$MYSQLD_DATADIR/ibdata1; +perl; +my $file = $ENV{'IBDATA1'}; +open(FILE, "<$file") || die "Unable to open $file"; +# Read DICT_HDR_TABLE_IDS, the root page number of ID_IND (SYS_TABLES.ID). +seek(FILE, 7*16384+38+36, 0) || die "Unable to seek $file"; +die unless read(FILE, $_, 4) == 4; +my $sys_tables_id_root = unpack "N"; +print "Last record of ID_IND root page ($sys_tables_id_root):\n"; +# This should be the last record in ID_IND. Dump it in hexadecimal. +seek(FILE, $sys_tables_id_root*16384 + 152, 0) || die "Unable to seek $file"; +read(FILE, $_, 32) || die "Unable to read $file"; +close(FILE); +print unpack("H*"),"\n"; +EOF + +# Restart the server. +-- exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +-- enable_reconnect +-- source include/wait_until_connected_again.inc diff --git a/mysql-test/suite/innodb_plugin/r/innodb_bug60049.result b/mysql-test/suite/innodb_plugin/r/innodb_bug60049.result new file mode 100644 index 00000000000..bec0e05a897 --- /dev/null +++ b/mysql-test/suite/innodb_plugin/r/innodb_bug60049.result @@ -0,0 +1,8 @@ +CREATE TABLE t(a INT)ENGINE=InnoDB; +RENAME TABLE t TO u; +DROP TABLE u; +SELECT @@innodb_fast_shutdown; +@@innodb_fast_shutdown +0 +Last record of ID_IND root page (9): +1808000018050074000000000000000c5359535f464f524549474e5f434f4c53 diff --git a/mysql-test/suite/innodb_plugin/t/innodb_bug60049-master.opt b/mysql-test/suite/innodb_plugin/t/innodb_bug60049-master.opt new file mode 100644 index 00000000000..22a5d4ed221 --- /dev/null +++ b/mysql-test/suite/innodb_plugin/t/innodb_bug60049-master.opt @@ -0,0 +1 @@ +--innodb_fast_shutdown=0 diff --git a/mysql-test/suite/innodb_plugin/t/innodb_bug60049.test b/mysql-test/suite/innodb_plugin/t/innodb_bug60049.test new file mode 100644 index 00000000000..0c093ac94b1 --- /dev/null +++ b/mysql-test/suite/innodb_plugin/t/innodb_bug60049.test @@ -0,0 +1,38 @@ +# Bug #60049 Verify that purge leaves no garbage in unique secondary indexes +# This test requires a fresh server start-up and a slow shutdown. +# This was a suspected bug (not a bug). + +-- source include/have_innodb_plugin.inc + +CREATE TABLE t(a INT)ENGINE=InnoDB; +RENAME TABLE t TO u; +DROP TABLE u; +SELECT @@innodb_fast_shutdown; +let $MYSQLD_DATADIR=`select @@datadir`; + +# Shut down the server +-- exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +-- shutdown_server 10 +-- source include/wait_until_disconnected.inc + +# Check the tail of ID_IND (SYS_TABLES.ID) +let IBDATA1=$MYSQLD_DATADIR/ibdata1; +perl; +my $file = $ENV{'IBDATA1'}; +open(FILE, "<$file") || die "Unable to open $file"; +# Read DICT_HDR_TABLE_IDS, the root page number of ID_IND (SYS_TABLES.ID). +seek(FILE, 7*16384+38+36, 0) || die "Unable to seek $file"; +die unless read(FILE, $_, 4) == 4; +my $sys_tables_id_root = unpack "N"; +print "Last record of ID_IND root page ($sys_tables_id_root):\n"; +# This should be the last record in ID_IND. Dump it in hexadecimal. +seek(FILE, $sys_tables_id_root*16384 + 152, 0) || die "Unable to seek $file"; +read(FILE, $_, 32) || die "Unable to read $file"; +close(FILE); +print unpack("H*"),"\n"; +EOF + +# Restart the server. +-- exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +-- enable_reconnect +-- source include/wait_until_connected_again.inc From 6459e2c3e1e53c5ead7e8420dc3ccc2cf8015e88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Wed, 16 Feb 2011 15:34:16 +0200 Subject: [PATCH 12/13] Make the implicit unpack parameter explicit in the Bug #60049 test. --- mysql-test/suite/innodb/t/innodb_bug60049.test | 4 ++-- mysql-test/suite/innodb_plugin/t/innodb_bug60049.test | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mysql-test/suite/innodb/t/innodb_bug60049.test b/mysql-test/suite/innodb/t/innodb_bug60049.test index 1e0feaf0c89..cff1c3dc09d 100644 --- a/mysql-test/suite/innodb/t/innodb_bug60049.test +++ b/mysql-test/suite/innodb/t/innodb_bug60049.test @@ -23,13 +23,13 @@ open(FILE, "<$file") || die "Unable to open $file"; # Read DICT_HDR_TABLE_IDS, the root page number of ID_IND (SYS_TABLES.ID). seek(FILE, 7*16384+38+36, 0) || die "Unable to seek $file"; die unless read(FILE, $_, 4) == 4; -my $sys_tables_id_root = unpack "N"; +my $sys_tables_id_root = unpack("N", $_); print "Last record of ID_IND root page ($sys_tables_id_root):\n"; # This should be the last record in ID_IND. Dump it in hexadecimal. seek(FILE, $sys_tables_id_root*16384 + 152, 0) || die "Unable to seek $file"; read(FILE, $_, 32) || die "Unable to read $file"; close(FILE); -print unpack("H*"),"\n"; +print unpack("H*", $_), "\n"; EOF # Restart the server. diff --git a/mysql-test/suite/innodb_plugin/t/innodb_bug60049.test b/mysql-test/suite/innodb_plugin/t/innodb_bug60049.test index 0c093ac94b1..b3557243235 100644 --- a/mysql-test/suite/innodb_plugin/t/innodb_bug60049.test +++ b/mysql-test/suite/innodb_plugin/t/innodb_bug60049.test @@ -23,13 +23,13 @@ open(FILE, "<$file") || die "Unable to open $file"; # Read DICT_HDR_TABLE_IDS, the root page number of ID_IND (SYS_TABLES.ID). seek(FILE, 7*16384+38+36, 0) || die "Unable to seek $file"; die unless read(FILE, $_, 4) == 4; -my $sys_tables_id_root = unpack "N"; +my $sys_tables_id_root = unpack("N", $_); print "Last record of ID_IND root page ($sys_tables_id_root):\n"; # This should be the last record in ID_IND. Dump it in hexadecimal. seek(FILE, $sys_tables_id_root*16384 + 152, 0) || die "Unable to seek $file"; read(FILE, $_, 32) || die "Unable to read $file"; close(FILE); -print unpack("H*"),"\n"; +print unpack("H*", $_), "\n"; EOF # Restart the server. From 61449541fb8aec7f82d8ab8e64688a4c5118e923 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 17 Feb 2011 09:45:07 +0200 Subject: [PATCH 13/13] Disable the Bug #60049 test on embedded, as it requires server restart. --- mysql-test/suite/innodb/t/innodb_bug60049.test | 1 + mysql-test/suite/innodb_plugin/t/innodb_bug60049.test | 1 + 2 files changed, 2 insertions(+) diff --git a/mysql-test/suite/innodb/t/innodb_bug60049.test b/mysql-test/suite/innodb/t/innodb_bug60049.test index cff1c3dc09d..b35fb12cc5e 100644 --- a/mysql-test/suite/innodb/t/innodb_bug60049.test +++ b/mysql-test/suite/innodb/t/innodb_bug60049.test @@ -2,6 +2,7 @@ # This test requires a fresh server start-up and a slow shutdown. # This was a suspected bug (not a bug). +-- source include/not_embedded.inc -- source include/have_innodb.inc CREATE TABLE t(a INT)ENGINE=InnoDB; diff --git a/mysql-test/suite/innodb_plugin/t/innodb_bug60049.test b/mysql-test/suite/innodb_plugin/t/innodb_bug60049.test index b3557243235..8d6c38ff9ef 100644 --- a/mysql-test/suite/innodb_plugin/t/innodb_bug60049.test +++ b/mysql-test/suite/innodb_plugin/t/innodb_bug60049.test @@ -2,6 +2,7 @@ # This test requires a fresh server start-up and a slow shutdown. # This was a suspected bug (not a bug). +-- source include/not_embedded.inc -- source include/have_innodb_plugin.inc CREATE TABLE t(a INT)ENGINE=InnoDB;