Merge mysql-5.5-innodb -> mysql-5.5
This commit is contained in:
commit
0047afa3b2
118
mysql-test/suite/innodb/r/innodb_bug53756.result
Normal file
118
mysql-test/suite/innodb/r/innodb_bug53756.result
Normal file
@ -0,0 +1,118 @@
|
||||
DROP TABLE IF EXISTS bug_53756 ;
|
||||
CREATE TABLE bug_53756 (pk INT, c1 INT) ENGINE=InnoDB;
|
||||
ALTER TABLE bug_53756 ADD PRIMARY KEY (pk);
|
||||
INSERT INTO bug_53756 VALUES(1, 11), (2, 22), (3, 33), (4, 44);
|
||||
|
||||
# Select a less restrictive isolation level.
|
||||
SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
|
||||
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
|
||||
COMMIT;
|
||||
|
||||
# Start a transaction in the default connection for isolation.
|
||||
START TRANSACTION;
|
||||
SELECT @@tx_isolation;
|
||||
@@tx_isolation
|
||||
READ-COMMITTED
|
||||
SELECT * FROM bug_53756;
|
||||
pk c1
|
||||
1 11
|
||||
2 22
|
||||
3 33
|
||||
4 44
|
||||
|
||||
# connection con1 deletes row 1
|
||||
START TRANSACTION;
|
||||
SELECT @@tx_isolation;
|
||||
@@tx_isolation
|
||||
READ-COMMITTED
|
||||
DELETE FROM bug_53756 WHERE pk=1;
|
||||
|
||||
# connection con2 deletes row 2
|
||||
START TRANSACTION;
|
||||
SELECT @@tx_isolation;
|
||||
@@tx_isolation
|
||||
READ-COMMITTED
|
||||
DELETE FROM bug_53756 WHERE pk=2;
|
||||
|
||||
# connection con3 updates row 3
|
||||
START TRANSACTION;
|
||||
SELECT @@tx_isolation;
|
||||
@@tx_isolation
|
||||
READ-COMMITTED
|
||||
UPDATE bug_53756 SET c1=77 WHERE pk=3;
|
||||
|
||||
# connection con4 updates row 4
|
||||
START TRANSACTION;
|
||||
SELECT @@tx_isolation;
|
||||
@@tx_isolation
|
||||
READ-COMMITTED
|
||||
UPDATE bug_53756 SET c1=88 WHERE pk=4;
|
||||
|
||||
# connection con5 inserts row 5
|
||||
START TRANSACTION;
|
||||
SELECT @@tx_isolation;
|
||||
@@tx_isolation
|
||||
READ-COMMITTED
|
||||
INSERT INTO bug_53756 VALUES(5, 55);
|
||||
|
||||
# connection con6 inserts row 6
|
||||
START TRANSACTION;
|
||||
SELECT @@tx_isolation;
|
||||
@@tx_isolation
|
||||
READ-COMMITTED
|
||||
INSERT INTO bug_53756 VALUES(6, 66);
|
||||
|
||||
# connection con1 commits.
|
||||
COMMIT;
|
||||
|
||||
# connection con3 commits.
|
||||
COMMIT;
|
||||
|
||||
# connection con4 rolls back.
|
||||
ROLLBACK;
|
||||
|
||||
# connection con6 rolls back.
|
||||
ROLLBACK;
|
||||
|
||||
# The connections 2 and 5 stay open.
|
||||
|
||||
# connection default selects resulting data.
|
||||
# Delete of row 1 was committed.
|
||||
# Update of row 3 was committed.
|
||||
# Due to isolation level read committed, these should be included.
|
||||
# All other changes should not be included.
|
||||
SELECT * FROM bug_53756;
|
||||
pk c1
|
||||
2 22
|
||||
3 77
|
||||
4 44
|
||||
|
||||
# connection default
|
||||
#
|
||||
# Crash server.
|
||||
START TRANSACTION;
|
||||
INSERT INTO bug_53756 VALUES (666,666);
|
||||
SET SESSION debug="+d,crash_commit_before";
|
||||
COMMIT;
|
||||
ERROR HY000: Lost connection to MySQL server during query
|
||||
|
||||
#
|
||||
# disconnect con1, con2, con3, con4, con5, con6.
|
||||
#
|
||||
# Restart server.
|
||||
|
||||
#
|
||||
# Select recovered data.
|
||||
# Delete of row 1 was committed.
|
||||
# Update of row 3 was committed.
|
||||
# These should be included.
|
||||
# All other changes should not be included.
|
||||
# Delete of row 2 and insert of row 5 should be rolled back
|
||||
SELECT * FROM bug_53756;
|
||||
pk c1
|
||||
2 22
|
||||
3 77
|
||||
4 44
|
||||
|
||||
# Clean up.
|
||||
DROP TABLE bug_53756;
|
28
mysql-test/suite/innodb/r/innodb_bug59307.result
Normal file
28
mysql-test/suite/innodb/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/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_bug53756-master.opt
Normal file
1
mysql-test/suite/innodb/t/innodb_bug53756-master.opt
Normal file
@ -0,0 +1 @@
|
||||
--skip-stack-trace --skip-core-file
|
184
mysql-test/suite/innodb/t/innodb_bug53756.test
Normal file
184
mysql-test/suite/innodb/t/innodb_bug53756.test
Normal file
@ -0,0 +1,184 @@
|
||||
# This is the test case for bug #53756. Alter table operation could
|
||||
# leave a deleted record for the temp table (later renamed to the altered
|
||||
# table) in the SYS_TABLES secondary index, we should ignore this row and
|
||||
# find the first non-deleted row for the specified table_id when load table
|
||||
# metadata in the function dict_load_table_on_id() during crash recovery.
|
||||
|
||||
#
|
||||
# innobackup needs to connect to the server. Not supported in embedded.
|
||||
--source include/not_embedded.inc
|
||||
#
|
||||
# This test case needs to crash the server. Needs a debug server.
|
||||
--source include/have_debug.inc
|
||||
#
|
||||
# Don't test this under valgrind, memory leaks will occur.
|
||||
--source include/not_valgrind.inc
|
||||
#
|
||||
# This test case needs InnoDB.
|
||||
-- source include/have_innodb.inc
|
||||
|
||||
#
|
||||
# Precautionary clean up.
|
||||
#
|
||||
--disable_warnings
|
||||
DROP TABLE IF EXISTS bug_53756 ;
|
||||
--enable_warnings
|
||||
|
||||
#
|
||||
# Create test data.
|
||||
#
|
||||
CREATE TABLE bug_53756 (pk INT, c1 INT) ENGINE=InnoDB;
|
||||
ALTER TABLE bug_53756 ADD PRIMARY KEY (pk);
|
||||
INSERT INTO bug_53756 VALUES(1, 11), (2, 22), (3, 33), (4, 44);
|
||||
|
||||
--echo
|
||||
--echo # Select a less restrictive isolation level.
|
||||
# Don't use user variables. They won't survive server crash.
|
||||
--let $global_isolation= `SELECT @@global.tx_isolation`
|
||||
--let $session_isolation= `SELECT @@session.tx_isolation`
|
||||
SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
|
||||
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
|
||||
COMMIT;
|
||||
|
||||
--echo
|
||||
--echo # Start a transaction in the default connection for isolation.
|
||||
START TRANSACTION;
|
||||
SELECT @@tx_isolation;
|
||||
SELECT * FROM bug_53756;
|
||||
|
||||
--echo
|
||||
--echo # connection con1 deletes row 1
|
||||
--connect (con1,localhost,root,,)
|
||||
START TRANSACTION;
|
||||
SELECT @@tx_isolation;
|
||||
DELETE FROM bug_53756 WHERE pk=1;
|
||||
|
||||
--echo
|
||||
--echo # connection con2 deletes row 2
|
||||
--connect (con2,localhost,root,,)
|
||||
START TRANSACTION;
|
||||
SELECT @@tx_isolation;
|
||||
DELETE FROM bug_53756 WHERE pk=2;
|
||||
|
||||
--echo
|
||||
--echo # connection con3 updates row 3
|
||||
--connect (con3,localhost,root,,)
|
||||
START TRANSACTION;
|
||||
SELECT @@tx_isolation;
|
||||
UPDATE bug_53756 SET c1=77 WHERE pk=3;
|
||||
|
||||
--echo
|
||||
--echo # connection con4 updates row 4
|
||||
--connect (con4,localhost,root,,)
|
||||
START TRANSACTION;
|
||||
SELECT @@tx_isolation;
|
||||
UPDATE bug_53756 SET c1=88 WHERE pk=4;
|
||||
|
||||
--echo
|
||||
--echo # connection con5 inserts row 5
|
||||
--connect (con5,localhost,root,,)
|
||||
START TRANSACTION;
|
||||
SELECT @@tx_isolation;
|
||||
INSERT INTO bug_53756 VALUES(5, 55);
|
||||
|
||||
--echo
|
||||
--echo # connection con6 inserts row 6
|
||||
--connect (con6,localhost,root,,)
|
||||
START TRANSACTION;
|
||||
SELECT @@tx_isolation;
|
||||
INSERT INTO bug_53756 VALUES(6, 66);
|
||||
|
||||
--echo
|
||||
--echo # connection con1 commits.
|
||||
--connection con1
|
||||
COMMIT;
|
||||
|
||||
--echo
|
||||
--echo # connection con3 commits.
|
||||
--connection con3
|
||||
COMMIT;
|
||||
|
||||
--echo
|
||||
--echo # connection con4 rolls back.
|
||||
--connection con4
|
||||
ROLLBACK;
|
||||
|
||||
--echo
|
||||
--echo # connection con6 rolls back.
|
||||
--connection con6
|
||||
ROLLBACK;
|
||||
|
||||
--echo
|
||||
--echo # The connections 2 and 5 stay open.
|
||||
|
||||
--echo
|
||||
--echo # connection default selects resulting data.
|
||||
--echo # Delete of row 1 was committed.
|
||||
--echo # Update of row 3 was committed.
|
||||
--echo # Due to isolation level read committed, these should be included.
|
||||
--echo # All other changes should not be included.
|
||||
--connection default
|
||||
SELECT * FROM bug_53756;
|
||||
|
||||
--echo
|
||||
--echo # connection default
|
||||
--connection default
|
||||
--echo #
|
||||
--echo # Crash server.
|
||||
#
|
||||
# Write file to make mysql-test-run.pl expect the "crash", but don't start
|
||||
# it until it's told to
|
||||
--exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
|
||||
#
|
||||
START TRANSACTION;
|
||||
INSERT INTO bug_53756 VALUES (666,666);
|
||||
#
|
||||
# Request a crash on next execution of commit.
|
||||
SET SESSION debug="+d,crash_commit_before";
|
||||
#
|
||||
# Execute the statement that causes the crash.
|
||||
--error 2013
|
||||
COMMIT;
|
||||
--echo
|
||||
--echo #
|
||||
--echo # disconnect con1, con2, con3, con4, con5, con6.
|
||||
--disconnect con1
|
||||
--disconnect con2
|
||||
--disconnect con3
|
||||
--disconnect con4
|
||||
--disconnect con5
|
||||
--disconnect con6
|
||||
--echo #
|
||||
--echo # Restart server.
|
||||
#
|
||||
# Write file to make mysql-test-run.pl start up the server again
|
||||
--exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
|
||||
#
|
||||
# Turn on reconnect
|
||||
--enable_reconnect
|
||||
#
|
||||
# Call script that will poll the server waiting for it to be back online again
|
||||
--source include/wait_until_connected_again.inc
|
||||
#
|
||||
# Turn off reconnect again
|
||||
--disable_reconnect
|
||||
--echo
|
||||
|
||||
--echo #
|
||||
--echo # Select recovered data.
|
||||
--echo # Delete of row 1 was committed.
|
||||
--echo # Update of row 3 was committed.
|
||||
--echo # These should be included.
|
||||
--echo # All other changes should not be included.
|
||||
--echo # Delete of row 2 and insert of row 5 should be rolled back
|
||||
SELECT * FROM bug_53756;
|
||||
|
||||
--echo
|
||||
--echo # Clean up.
|
||||
DROP TABLE bug_53756;
|
||||
|
||||
--disable_query_log
|
||||
eval SET GLOBAL tx_isolation= '$global_isolation';
|
||||
eval SET SESSION tx_isolation= '$session_isolation';
|
||||
--enable_query_log
|
||||
|
32
mysql-test/suite/innodb/t/innodb_bug59307.test
Normal file
32
mysql-test/suite/innodb/t/innodb_bug59307.test
Normal file
@ -0,0 +1,32 @@
|
||||
-- source include/have_innodb.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;
|
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
|
@ -875,5 +875,4 @@
|
||||
fun:buf_buddy_relocate
|
||||
fun:buf_buddy_free_low
|
||||
fun:buf_buddy_free
|
||||
fun:buf_LRU_block_remove_hashed_page
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
@ -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. */
|
||||
|
@ -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 */
|
||||
|
@ -827,6 +827,34 @@ dict_table_check_if_in_cache_low(
|
||||
return(table);
|
||||
}
|
||||
|
||||
/**********************************************************************//**
|
||||
load a table into dictionary cache, ignore any error specified during load;
|
||||
@return table, NULL if not found */
|
||||
UNIV_INLINE
|
||||
dict_table_t*
|
||||
dict_table_get_low_ignore_err(
|
||||
/*==========================*/
|
||||
const char* table_name, /*!< in: table name */
|
||||
dict_err_ignore_t
|
||||
ignore_err) /*!< in: error to be ignored when
|
||||
loading a table definition */
|
||||
{
|
||||
dict_table_t* table;
|
||||
|
||||
ut_ad(table_name);
|
||||
ut_ad(mutex_own(&(dict_sys->mutex)));
|
||||
|
||||
table = dict_table_check_if_in_cache_low(table_name);
|
||||
|
||||
if (table == NULL) {
|
||||
table = dict_load_table(table_name, TRUE, ignore_err);
|
||||
}
|
||||
|
||||
ut_ad(!table || table->cached);
|
||||
|
||||
return(table);
|
||||
}
|
||||
|
||||
/**********************************************************************//**
|
||||
Gets a table; loads it to the dictionary cache if necessary. A low-level
|
||||
function.
|
||||
@ -845,7 +873,7 @@ dict_table_get_low(
|
||||
table = dict_table_check_if_in_cache_low(table_name);
|
||||
|
||||
if (table == NULL) {
|
||||
table = dict_load_table(table_name, TRUE);
|
||||
table = dict_load_table(table_name, TRUE, DICT_ERR_IGNORE_NONE);
|
||||
}
|
||||
|
||||
ut_ad(!table || table->cached);
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
||||
/**********************************************************************//**
|
||||
|
@ -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 */
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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)) {
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user