summaryrefslogtreecommitdiff
path: root/storage
diff options
context:
space:
mode:
authorVasil Dimov <vasil.dimov@oracle.com>2011-02-17 13:57:26 +0200
committerVasil Dimov <vasil.dimov@oracle.com>2011-02-17 13:57:26 +0200
commit60a84072312c89dde45b1a3d937c032fa9be9925 (patch)
tree26f6100c7a683ba51523b61be449a5e70a0ac362 /storage
parent26077d8dcb44af9e0512a684200c0350d47764fc (diff)
parent778e700d1ccec4d302e9cdf143232bd65972dc83 (diff)
downloadmariadb-git-60a84072312c89dde45b1a3d937c032fa9be9925.tar.gz
Merge mysql-5.5-innodb -> mysql-5.5
Diffstat (limited to 'storage')
-rw-r--r--storage/innobase/btr/btr0btr.c572
-rw-r--r--storage/innobase/btr/btr0cur.c44
-rw-r--r--storage/innobase/buf/buf0rea.c2
-rw-r--r--storage/innobase/dict/dict0load.c61
-rw-r--r--storage/innobase/dict/dict0mem.c9
-rw-r--r--storage/innobase/handler/handler0alter.cc12
-rw-r--r--storage/innobase/include/btr0btr.h85
-rw-r--r--storage/innobase/include/btr0types.h125
-rw-r--r--storage/innobase/include/dict0dict.h12
-rw-r--r--storage/innobase/include/dict0dict.ic30
-rw-r--r--storage/innobase/include/dict0load.h5
-rw-r--r--storage/innobase/include/dict0mem.h11
-rw-r--r--storage/innobase/include/dict0types.h14
-rw-r--r--storage/innobase/include/page0zip.h2
-rw-r--r--storage/innobase/include/univ.i4
-rw-r--r--storage/innobase/page/page0cur.c10
-rw-r--r--storage/innobase/page/page0page.c11
-rw-r--r--storage/innobase/page/page0zip.c5
-rw-r--r--storage/innobase/row/row0mysql.c6
-rw-r--r--storage/innobase/row/row0upd.c35
-rw-r--r--storage/innobase/srv/srv0start.c6
-rw-r--r--storage/innobase/sync/sync0rw.c3
22 files changed, 1029 insertions, 35 deletions
diff --git a/storage/innobase/btr/btr0btr.c b/storage/innobase/btr/btr0btr.c
index 2e544c0552a..fb2509a62ff 100644
--- a/storage/innobase/btr/btr0btr.c
+++ b/storage/innobase/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)) {
@@ -774,6 +1330,14 @@ 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(PFS_NOT_INSTRUMENTED,
+ &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);
}
@@ -998,6 +1562,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 */
@@ -1026,6 +1591,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. */
@@ -1159,6 +1726,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 */
@@ -2499,6 +3067,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. */
@@ -2760,6 +3329,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)) {
@@ -2990,6 +3560,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/innobase/btr/btr0cur.c b/storage/innobase/btr/btr0cur.c
index ff9b1835a53..d1d5d2020ec 100644
--- a/storage/innobase/btr/btr0cur.c
+++ b/storage/innobase/btr/btr0cur.c
@@ -2690,6 +2690,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);
@@ -3873,6 +3874,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);
}
/*******************************************************************//**
@@ -4373,6 +4376,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);
@@ -4448,6 +4456,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,
@@ -4616,6 +4629,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/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) {
diff --git a/storage/innobase/dict/dict0load.c b/storage/innobase/dict/dict0load.c
index 3561e7220ab..14490980bb6 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 */
@@ -1810,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. */
@@ -1836,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);
}
/*---------------------------------------------------*/
@@ -1857,20 +1879,15 @@ 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 */
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);
+func_exit:
btr_pcur_close(&pcur);
mtr_commit(&mtr);
mem_heap_free(heap);
@@ -1894,7 +1911,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/dict/dict0mem.c b/storage/innobase/dict/dict0mem.c
index 8d3d78f3900..a442a3811d8 100644
--- a/storage/innobase/dict/dict0mem.c
+++ b/storage/innobase/dict/dict0mem.c
@@ -38,6 +38,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 */
@@ -380,6 +383,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/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc
index c0bd3121c9c..e0279c13de2 100644
--- a/storage/innobase/handler/handler0alter.cc
+++ b/storage/innobase/handler/handler0alter.cc
@@ -786,10 +786,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
@@ -915,6 +911,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);
diff --git a/storage/innobase/include/btr0btr.h b/storage/innobase/include/btr0btr.h
index e213c22937f..5b3e166371d 100644
--- a/storage/innobase/include/btr0btr.h
+++ b/storage/innobase/include/btr0btr.h
@@ -92,6 +92,91 @@ insert/delete buffer when the record is not in the buffer pool. */
buffer when the record is not in the buffer pool. */
#define BTR_DELETE 8192
+#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/innobase/include/btr0types.h b/storage/innobase/include/btr0types.h
index ef4a6b04b34..07c06fb18d7 100644
--- a/storage/innobase/include/btr0types.h
+++ b/storage/innobase/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/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
@@ -828,6 +828,34 @@ dict_table_check_if_in_cache_low(
}
/**********************************************************************//**
+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.
@return table, NULL if not found */
@@ -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 5757b79d3ed..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)
@@ -395,6 +397,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 */
@@ -487,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/include/page0zip.h b/storage/innobase/include/page0zip.h
index 574809e5227..00c1d0516e6 100644
--- a/storage/innobase/include/page0zip.h
+++ b/storage/innobase/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/innobase/include/univ.i b/storage/innobase/include/univ.i
index a99b8306eb8..f561226a2de 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;
@@ -201,6 +201,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/innobase/page/page0cur.c b/storage/innobase/page/page0cur.c
index f10f16a7dd9..936762b986a 100644
--- a/storage/innobase/page/page0cur.c
+++ b/storage/innobase/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/innobase/page/page0page.c b/storage/innobase/page/page0page.c
index 2e785412ac9..99182e85849 100644
--- a/storage/innobase/page/page0page.c
+++ b/storage/innobase/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/innobase/page/page0zip.c b/storage/innobase/page/page0zip.c
index 4c5371370da..704b0c88e02 100644
--- a/storage/innobase/page/page0zip.c
+++ b/storage/innobase/page/page0zip.c
@@ -4452,6 +4452,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 */
@@ -4510,7 +4512,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
@@ -4580,6 +4582,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/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);
diff --git a/storage/innobase/row/row0upd.c b/storage/innobase/row/row0upd.c
index c6bff0fccd6..ea71a16bed2 100644
--- a/storage/innobase/row/row0upd.c
+++ b/storage/innobase/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/innobase/srv/srv0start.c b/storage/innobase/srv/srv0start.c
index 2a9ad0e7541..050fe460652 100644
--- a/storage/innobase/srv/srv0start.c
+++ b/storage/innobase/srv/srv0start.c
@@ -1083,6 +1083,12 @@ innobase_start_or_create_for_mysql(void)
# endif
#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
ut_print_timestamp(stderr);
fprintf(stderr,
diff --git a/storage/innobase/sync/sync0rw.c b/storage/innobase/sync/sync0rw.c
index 295a010f9d7..73d1e3aa46e 100644
--- a/storage/innobase/sync/sync0rw.c
+++ b/storage/innobase/sync/sync0rw.c
@@ -271,6 +271,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);