Merge mysql-5.1-innodb -> mysql-5.1
This commit is contained in:
commit
0bbaf6e8a7
8
mysql-test/suite/innodb/r/innodb_bug60049.result
Normal file
8
mysql-test/suite/innodb/r/innodb_bug60049.result
Normal file
@ -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
|
1
mysql-test/suite/innodb/t/innodb_bug60049-master.opt
Normal file
1
mysql-test/suite/innodb/t/innodb_bug60049-master.opt
Normal file
@ -0,0 +1 @@
|
||||
--innodb_fast_shutdown=0
|
39
mysql-test/suite/innodb/t/innodb_bug60049.test
Normal file
39
mysql-test/suite/innodb/t/innodb_bug60049.test
Normal file
@ -0,0 +1,39 @@
|
||||
# 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/not_embedded.inc
|
||||
-- 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
|
28
mysql-test/suite/innodb_plugin/r/innodb_bug59307.result
Normal file
28
mysql-test/suite/innodb_plugin/r/innodb_bug59307.result
Normal file
@ -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;
|
8
mysql-test/suite/innodb_plugin/r/innodb_bug60049.result
Normal file
8
mysql-test/suite/innodb_plugin/r/innodb_bug60049.result
Normal file
@ -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
|
32
mysql-test/suite/innodb_plugin/t/innodb_bug59307.test
Normal file
32
mysql-test/suite/innodb_plugin/t/innodb_bug59307.test
Normal file
@ -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;
|
@ -0,0 +1 @@
|
||||
--innodb_fast_shutdown=0
|
39
mysql-test/suite/innodb_plugin/t/innodb_bug60049.test
Normal file
39
mysql-test/suite/innodb_plugin/t/innodb_bug60049.test
Normal file
@ -0,0 +1,39 @@
|
||||
# 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/not_embedded.inc
|
||||
-- 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
|
@ -1,3 +1,15 @@
|
||||
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:
|
||||
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,
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
@ -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. */
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
||||
/**********************************************************************//**
|
||||
|
@ -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;
|
||||
@ -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 */
|
||||
|
@ -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.
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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)) {
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user