3666 Vasil Dimov 2011-02-17 [merge]
Merge mysql-trunk from bk-internal into my local repo
modified:
mysql-test/r/ctype_cp1250_ch.result
mysql-test/r/ctype_cp1251.result
mysql-test/r/ctype_eucjpms.result
mysql-test/t/ctype_cp1250_ch.test
mysql-test/t/ctype_cp1251.test
mysql-test/t/ctype_eucjpms.test
sql/sql_string.h
sql/sys_vars.cc
=== added file 'mysql-test/suite/innodb/r/innodb_bug53756.result'
--- a/mysql-test/suite/innodb/r/innodb_bug53756.result 1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/innodb/r/innodb_bug53756.result revid:vasil.dimov@stripped
@@ -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;
=== added file 'mysql-test/suite/innodb/r/innodb_bug59307.result'
--- a/mysql-test/suite/innodb/r/innodb_bug59307.result 1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/innodb/r/innodb_bug59307.result revid:vasil.dimov@stripped
@@ -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;
=== added file 'mysql-test/suite/innodb/r/innodb_bug60049.result'
--- a/mysql-test/suite/innodb/r/innodb_bug60049.result 1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/innodb/r/innodb_bug60049.result revid:vasil.dimov@stripped
@@ -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
=== added file 'mysql-test/suite/innodb/t/innodb_bug53756-master.opt'
--- a/mysql-test/suite/innodb/t/innodb_bug53756-master.opt 1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/innodb/t/innodb_bug53756-master.opt revid:vasil.dimov@stripped
@@ -0,0 +1 @@
+--skip-stack-trace --skip-core-file
=== added file 'mysql-test/suite/innodb/t/innodb_bug53756.test'
--- a/mysql-test/suite/innodb/t/innodb_bug53756.test 1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/innodb/t/innodb_bug53756.test revid:vasil.dimov@stripped
@@ -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
+
=== added file 'mysql-test/suite/innodb/t/innodb_bug59307.test'
--- a/mysql-test/suite/innodb/t/innodb_bug59307.test 1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/innodb/t/innodb_bug59307.test revid:vasil.dimov@stripped
@@ -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;
=== added file 'mysql-test/suite/innodb/t/innodb_bug60049-master.opt'
--- a/mysql-test/suite/innodb/t/innodb_bug60049-master.opt 1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/innodb/t/innodb_bug60049-master.opt revid:vasil.dimov@stripped
@@ -0,0 +1 @@
+--innodb_fast_shutdown=0
=== added file 'mysql-test/suite/innodb/t/innodb_bug60049.test'
--- a/mysql-test/suite/innodb/t/innodb_bug60049.test 1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/innodb/t/innodb_bug60049.test revid:vasil.dimov@stripped
@@ -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
=== added file 'mysql-test/suite/sys_vars/r/innodb_change_buffer_max_size_basic.result'
--- a/mysql-test/suite/sys_vars/r/innodb_change_buffer_max_size_basic.result 1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/sys_vars/r/innodb_change_buffer_max_size_basic.result revid:vasil.dimov@stripped
@@ -0,0 +1,77 @@
+SET @start_global_value = @@global.innodb_change_buffer_max_size;
+SELECT @start_global_value;
+@start_global_value
+25
+Valid values are between 0 and 50
+select @@global.innodb_change_buffer_max_size between 0 and 50;
+@@global.innodb_change_buffer_max_size between 0 and 50
+1
+select @@global.innodb_change_buffer_max_size;
+@@global.innodb_change_buffer_max_size
+25
+select @@session.innodb_change_buffer_max_size;
+ERROR HY000: Variable 'innodb_change_buffer_max_size' is a GLOBAL variable
+show global variables like 'innodb_change_buffer_max_size';
+Variable_name Value
+innodb_change_buffer_max_size 25
+show session variables like 'innodb_change_buffer_max_size';
+Variable_name Value
+innodb_change_buffer_max_size 25
+select * from information_schema.global_variables where variable_name='innodb_change_buffer_max_size';
+VARIABLE_NAME VARIABLE_VALUE
+INNODB_CHANGE_BUFFER_MAX_SIZE 25
+select * from information_schema.session_variables where variable_name='innodb_change_buffer_max_size';
+VARIABLE_NAME VARIABLE_VALUE
+INNODB_CHANGE_BUFFER_MAX_SIZE 25
+set global innodb_change_buffer_max_size=10;
+select @@global.innodb_change_buffer_max_size;
+@@global.innodb_change_buffer_max_size
+10
+select * from information_schema.global_variables where variable_name='innodb_change_buffer_max_size';
+VARIABLE_NAME VARIABLE_VALUE
+INNODB_CHANGE_BUFFER_MAX_SIZE 10
+select * from information_schema.session_variables where variable_name='innodb_change_buffer_max_size';
+VARIABLE_NAME VARIABLE_VALUE
+INNODB_CHANGE_BUFFER_MAX_SIZE 10
+set session innodb_change_buffer_max_size=1;
+ERROR HY000: Variable 'innodb_change_buffer_max_size' is a GLOBAL variable and should be set with SET GLOBAL
+set global innodb_change_buffer_max_size=1.1;
+ERROR 42000: Incorrect argument type to variable 'innodb_change_buffer_max_size'
+set global innodb_change_buffer_max_size=1e1;
+ERROR 42000: Incorrect argument type to variable 'innodb_change_buffer_max_size'
+set global innodb_change_buffer_max_size="foo";
+ERROR 42000: Incorrect argument type to variable 'innodb_change_buffer_max_size'
+set global innodb_change_buffer_max_size=-7;
+Warnings:
+Warning 1292 Truncated incorrect innodb_change_buffer_max_size value: '-7'
+select @@global.innodb_change_buffer_max_size;
+@@global.innodb_change_buffer_max_size
+0
+select * from information_schema.global_variables where variable_name='innodb_change_buffer_max_size';
+VARIABLE_NAME VARIABLE_VALUE
+INNODB_CHANGE_BUFFER_MAX_SIZE 0
+set global innodb_change_buffer_max_size=56;
+Warnings:
+Warning 1292 Truncated incorrect innodb_change_buffer_max_size value: '56'
+select @@global.innodb_change_buffer_max_size;
+@@global.innodb_change_buffer_max_size
+50
+select * from information_schema.global_variables where variable_name='innodb_change_buffer_max_size';
+VARIABLE_NAME VARIABLE_VALUE
+INNODB_CHANGE_BUFFER_MAX_SIZE 50
+set global innodb_change_buffer_max_size=0;
+select @@global.innodb_change_buffer_max_size;
+@@global.innodb_change_buffer_max_size
+0
+set global innodb_change_buffer_max_size=50;
+select @@global.innodb_change_buffer_max_size;
+@@global.innodb_change_buffer_max_size
+50
+set global innodb_change_buffer_max_size=DEFAULT;
+select @@global.innodb_change_buffer_max_size;
+@@global.innodb_change_buffer_max_size
+25
+SET @@global.innodb_change_buffer_max_size = @start_global_value;
+SELECT @@global.innodb_change_buffer_max_size;
+@@global.innodb_change_buffer_max_size
+25
=== added file 'mysql-test/suite/sys_vars/t/innodb_change_buffer_max_size_basic.test'
--- a/mysql-test/suite/sys_vars/t/innodb_change_buffer_max_size_basic.test 1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/sys_vars/t/innodb_change_buffer_max_size_basic.test revid:vasil.dimov@stripped
@@ -0,0 +1,63 @@
+
+
+# 2011-02-09 - Added
+#
+
+--source include/have_innodb.inc
+
+SET @start_global_value = @@global.innodb_change_buffer_max_size;
+SELECT @start_global_value;
+
+#
+# exists as global only
+#
+--echo Valid values are between 0 and 50
+select @@global.innodb_change_buffer_max_size between 0 and 50;
+select @@global.innodb_change_buffer_max_size;
+--error ER_INCORRECT_GLOBAL_LOCAL_VAR
+select @@session.innodb_change_buffer_max_size;
+show global variables like 'innodb_change_buffer_max_size';
+show session variables like 'innodb_change_buffer_max_size';
+select * from information_schema.global_variables where variable_name='innodb_change_buffer_max_size';
+select * from information_schema.session_variables where variable_name='innodb_change_buffer_max_size';
+
+#
+# show that it's writable
+#
+set global innodb_change_buffer_max_size=10;
+select @@global.innodb_change_buffer_max_size;
+select * from information_schema.global_variables where variable_name='innodb_change_buffer_max_size';
+select * from information_schema.session_variables where variable_name='innodb_change_buffer_max_size';
+--error ER_GLOBAL_VARIABLE
+set session innodb_change_buffer_max_size=1;
+
+#
+# incorrect types
+#
+--error ER_WRONG_TYPE_FOR_VAR
+set global innodb_change_buffer_max_size=1.1;
+--error ER_WRONG_TYPE_FOR_VAR
+set global innodb_change_buffer_max_size=1e1;
+--error ER_WRONG_TYPE_FOR_VAR
+set global innodb_change_buffer_max_size="foo";
+
+set global innodb_change_buffer_max_size=-7;
+select @@global.innodb_change_buffer_max_size;
+select * from information_schema.global_variables where variable_name='innodb_change_buffer_max_size';
+set global innodb_change_buffer_max_size=56;
+select @@global.innodb_change_buffer_max_size;
+select * from information_schema.global_variables where variable_name='innodb_change_buffer_max_size';
+
+#
+# min/max/DEFAULT values
+#
+set global innodb_change_buffer_max_size=0;
+select @@global.innodb_change_buffer_max_size;
+set global innodb_change_buffer_max_size=50;
+select @@global.innodb_change_buffer_max_size;
+set global innodb_change_buffer_max_size=DEFAULT;
+select @@global.innodb_change_buffer_max_size;
+
+
+SET @@global.innodb_change_buffer_max_size = @start_global_value;
+SELECT @@global.innodb_change_buffer_max_size;
=== modified file 'mysql-test/valgrind.supp'
--- a/mysql-test/valgrind.supp revid:magne.mahre@stripped
+++ b/mysql-test/valgrind.supp revid:vasil.dimov@stripped
@@ -956,5 +956,4 @@
fun:buf_buddy_relocate
fun:buf_buddy_free_low
fun:buf_buddy_free
- fun:buf_LRU_block_remove_hashed_page
}
=== modified file 'storage/innobase/btr/btr0btr.c'
--- a/storage/innobase/btr/btr0btr.c revid:magne.mahre@stripped
+++ b/storage/innobase/btr/btr0btr.c revid:vasil.dimov@stripped
@@ -43,6 +43,560 @@ Created 6/2/1994 Heikki Tuuri
#include "trx0trx.h"
#include "srv0mon.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
--------------------------------------
@@ -297,6 +851,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);
@@ -490,6 +1045,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)) {
@@ -775,6 +1331,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);
}
@@ -999,6 +1563,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 */
@@ -1027,6 +1592,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. */
@@ -1160,6 +1727,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 */
@@ -2501,6 +3069,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. */
@@ -2762,6 +3331,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)) {
@@ -2992,6 +3562,8 @@ btr_discard_page(
block);
}
+ btr_blob_dbg_remove(page, index, "btr_discard_page");
+
/* Free the file page */
btr_page_free(index, block, mtr);
=== modified file 'storage/innobase/btr/btr0cur.c'
--- a/storage/innobase/btr/btr0cur.c revid:magne.mahre@stripped
+++ b/storage/innobase/btr/btr0cur.c revid:vasil.dimov@stripped
@@ -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);
@@ -3876,6 +3877,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);
}
/*******************************************************************//**
@@ -4374,6 +4377,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);
@@ -4449,6 +4457,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,
@@ -4617,6 +4630,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;
=== modified file 'storage/innobase/buf/buf0rea.c'
--- a/storage/innobase/buf/buf0rea.c revid:magne.mahre@stripped
+++ b/storage/innobase/buf/buf0rea.c revid:vasil.dimov@stripped
@@ -535,7 +535,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) {
=== modified file 'storage/innobase/dict/dict0dict.c'
--- a/storage/innobase/dict/dict0dict.c revid:magne.mahre@stripped
+++ b/storage/innobase/dict/dict0dict.c revid:vasil.dimov@stripped
@@ -812,7 +812,10 @@ dict_table_t*
dict_table_open_on_name_low(
/*========================*/
const char* table_name, /*!< in: table name */
- ibool dict_locked) /*!< in: TRUE=data dictionary locked */
+ ibool dict_locked, /*!< in: TRUE=data dictionary locked */
+ dict_err_ignore_t
+ ignore_err) /*!< in: error to be ignored when
+ loading a table definition */
{
dict_table_t* table;
@@ -820,12 +823,22 @@ dict_table_open_on_name_low(
mutex_enter(&(dict_sys->mutex));
}
+ ut_ad(table_name);
ut_ad(mutex_own(&dict_sys->mutex));
- table = dict_table_get_low(table_name);
+ 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);
if (table != NULL) {
+ ut_ad(ignore_err != DICT_ERR_IGNORE_NONE
+ || table->corrupted == FALSE);
+
if (table->can_be_evicted) {
dict_move_to_mru(table);
}
@@ -857,7 +870,8 @@ dict_table_open_on_name(
{
dict_table_t* table;
- table = dict_table_open_on_name_low(table_name, dict_locked);
+ table = dict_table_open_on_name_low(table_name, dict_locked,
+ DICT_ERR_IGNORE_NONE);
if (table != NULL) {
/* If table->ibd_file_missing == TRUE, this will
@@ -881,9 +895,13 @@ dict_table_t*
dict_table_open_on_name_no_stats(
/*=============================*/
const char* table_name, /*!< in: table name */
- ibool dict_locked) /*!< in: TRUE=data dictionary locked */
+ ibool dict_locked, /*!< in: TRUE=data dictionary locked */
+ dict_err_ignore_t
+ ignore_err) /*!< in: error to be ignored during
+ table open */
{
- return(dict_table_open_on_name_low(table_name, dict_locked));
+ return(dict_table_open_on_name_low(table_name, dict_locked,
+ ignore_err));
}
#endif /* !UNIV_HOTBACKUP */
=== modified file 'storage/innobase/dict/dict0load.c'
--- a/storage/innobase/dict/dict0load.c revid:magne.mahre@stripped
+++ b/storage/innobase/dict/dict0load.c revid:vasil.dimov@stripped
@@ -1320,7 +1320,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;
@@ -1404,10 +1407,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)) {
@@ -1616,7 +1631,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;
@@ -1731,7 +1749,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 */
@@ -1781,6 +1799,8 @@ err_exit:
#endif /* 0 */
mem_heap_free(heap);
+ ut_ad(ignore_err != DICT_ERR_IGNORE_NONE || table->corrupted == FALSE);
+
return(table);
}
@@ -1808,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. */
@@ -1832,43 +1854,42 @@ dict_load_table_on_id(
btr_pcur_open_on_user_rec(sys_table_ids, tuple, PAGE_CUR_GE,
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)) {
- /* Not found */
-
- btr_pcur_close(&pcur);
- mtr_commit(&mtr);
- mem_heap_free(heap);
-
- return(NULL);
- }
-
- /*---------------------------------------------------*/
- /* Now we have the record in the secondary index containing the
- table ID and NAME */
+check_rec:
rec = btr_pcur_get_rec(&pcur);
- field = rec_get_nth_field_old(rec, 0, &len);
- ut_ad(len == 8);
- /* Check if the table id in record is the one searched for */
- if (table_id != mach_read_from_8(field)) {
+ if (page_rec_is_user_rec(rec)) {
+ /*---------------------------------------------------*/
+ /* Now we have the record in the secondary index
+ containing the table ID and NAME */
- btr_pcur_close(&pcur);
- mtr_commit(&mtr);
- mem_heap_free(heap);
+ field = rec_get_nth_field_old(rec, 0, &len);
+ ut_ad(len == 8);
- return(NULL);
+ /* Check if the table id in record is the one searched for */
+ if (table_id == mach_read_from_8(field)) {
+ if (rec_get_deleted_flag(rec, 0)) {
+ /* Until purge has completed, there
+ may be delete-marked duplicate records
+ for the same SYS_TABLES.ID.
+ Due to Bug #60049, some delete-marked
+ records may survive the purge forever. */
+ if (btr_pcur_move_to_next(&pcur, &mtr)) {
+
+ goto check_rec;
+ }
+ } else {
+ /* 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, DICT_ERR_IGNORE_NONE);
+ }
+ }
}
- /* 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);
-
btr_pcur_close(&pcur);
mtr_commit(&mtr);
mem_heap_free(heap);
@@ -1892,7 +1913,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);
}
=== modified file 'storage/innobase/dict/dict0mem.c'
--- a/storage/innobase/dict/dict0mem.c revid:magne.mahre@stripped
+++ b/storage/innobase/dict/dict0mem.c revid:vasil.dimov@stripped
@@ -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);
}
=== modified file 'storage/innobase/dict/dict0stats.c'
--- a/storage/innobase/dict/dict0stats.c revid:magne.mahre@stripped
+++ b/storage/innobase/dict/dict0stats.c revid:vasil.dimov@stripped
@@ -2326,10 +2326,10 @@ dict_stats_open(void)
dict_stats = mem_zalloc(sizeof(*dict_stats));
dict_stats->table_stats = dict_table_open_on_name_no_stats(
- TABLE_STATS_NAME, FALSE);
+ TABLE_STATS_NAME, FALSE, DICT_ERR_IGNORE_NONE);
dict_stats->index_stats = dict_table_open_on_name_no_stats(
- INDEX_STATS_NAME, FALSE);
+ INDEX_STATS_NAME, FALSE, DICT_ERR_IGNORE_NONE);
/* Check if the tables have the correct structure, if yes then
after this function we can safely DELETE from them without worrying
=== modified file 'storage/innobase/handler/ha_innodb.cc'
--- a/storage/innobase/handler/ha_innodb.cc revid:magne.mahre@stripped
+++ b/storage/innobase/handler/ha_innodb.cc revid:vasil.dimov@stripped
@@ -135,6 +135,10 @@ static long long innobase_buffer_pool_si
Connected to buf_LRU_old_ratio. */
static uint innobase_old_blocks_pct;
+/** Maximum on-disk size of change buffer in terms of percentage
+of the buffer pool. */
+static uint innobase_change_buffer_max_size = CHANGE_BUFFER_DEFAULT_SIZE;
+
/* The default values for the following char* start-up parameters
are determined in innobase_init below: */
@@ -292,7 +296,15 @@ static PSI_mutex_info all_innodb_mutexes
{&lock_sys_wait_mutex_key, "lock_wait_mutex", 0},
{&trx_mutex_key, "trx_mutex", 0},
{&srv_sys_tasks_mutex_key, "srv_threads_mutex", 0},
- {&read_view_mutex_key, "read_view_mutex", 0}
+ {&read_view_mutex_key, "read_view_mutex", 0},
+
+ /* mutex with os_fast_mutex_ interfaces */
+# ifndef PFS_SKIP_EVENT_MUTEX
+ {&event_os_mutex_key, "event_os_mutex", 0},
+# endif /* PFS_SKIP_EVENT_MUTEX */
+ {&os_mutex_key, "os_mutex", 0},
+ {&srv_conc_mutex_key, "srv_conc_mutex", 0},
+ {&ut_list_mutex_key, "ut_list_mutex", 0}
};
# endif /* UNIV_PFS_MUTEX */
@@ -2702,6 +2714,8 @@ innobase_change_buffering_inited_ok:
innobase_old_blocks_pct = buf_LRU_old_ratio_update(
innobase_old_blocks_pct, TRUE);
+ ibuf_max_size_update(innobase_change_buffer_max_size);
+
innobase_open_tables = hash_create(200);
mysql_mutex_init(innobase_share_mutex_key,
&innobase_share_mutex,
@@ -11316,6 +11330,26 @@ innodb_old_blocks_pct_update(
*static_cast<const uint*>(save), TRUE);
}
+/****************************************************************//**
+Update the system variable innodb_old_blocks_pct using the "saved"
+value. This function is registered as a callback with MySQL. */
+static
+void
+innodb_change_buffer_max_size_update(
+/*=================================*/
+ THD* thd, /*!< in: thread handle */
+ struct st_mysql_sys_var* var, /*!< in: pointer to
+ system variable */
+ void* var_ptr,/*!< out: where the
+ formal string goes */
+ const void* save) /*!< in: immediate result
+ from check function */
+{
+ innobase_change_buffer_max_size =
+ (*static_cast<const uint*>(save));
+ ibuf_max_size_update(innobase_change_buffer_max_size);
+}
+
/*************************************************************//**
Find the corresponding ibuf_use_t value that indexes into
innobase_change_buffering_values[] array for the input
@@ -12286,6 +12320,14 @@ static MYSQL_SYSVAR_STR(change_buffering
innodb_change_buffering_validate,
innodb_change_buffering_update, "all");
+static MYSQL_SYSVAR_UINT(change_buffer_max_size,
+ innobase_change_buffer_max_size,
+ PLUGIN_VAR_RQCMDARG,
+ "Maximum on-disk size of change buffer in terms of percentage"
+ " of the buffer pool.",
+ NULL, innodb_change_buffer_max_size_update,
+ CHANGE_BUFFER_DEFAULT_SIZE, 0, 50, 0);
+
static MYSQL_SYSVAR_ENUM(stats_method, srv_innodb_stats_method,
PLUGIN_VAR_RQCMDARG,
"Specifies how InnoDB index statistics collection code should "
@@ -12396,6 +12438,7 @@ static struct st_mysql_sys_var* innobase
MYSQL_SYSVAR(use_sys_malloc),
MYSQL_SYSVAR(use_native_aio),
MYSQL_SYSVAR(change_buffering),
+ MYSQL_SYSVAR(change_buffer_max_size),
#if defined UNIV_DEBUG || defined UNIV_IBUF_DEBUG
MYSQL_SYSVAR(change_buffering_debug),
#endif /* UNIV_DEBUG || UNIV_IBUF_DEBUG */
=== modified file 'storage/innobase/handler/handler0alter.cc'
--- a/storage/innobase/handler/handler0alter.cc revid:magne.mahre@stripped
+++ b/storage/innobase/handler/handler0alter.cc revid:vasil.dimov@stripped
@@ -857,10 +857,6 @@ ha_innobase::add_index(
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
@@ -1004,6 +1000,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);
=== modified file 'storage/innobase/handler/i_s.cc'
--- a/storage/innobase/handler/i_s.cc revid:magne.mahre@stripped
+++ b/storage/innobase/handler/i_s.cc revid:vasil.dimov@stripped
@@ -4629,7 +4629,12 @@ i_s_dict_fill_sys_indexes(
OK(fields[SYS_INDEX_NUM_FIELDS]->store(index->n_fields));
- OK(fields[SYS_INDEX_PAGE_NO]->store(index->page));
+ /* FIL_NULL is ULINT32_UNDEFINED */
+ if (index->page == FIL_NULL) {
+ OK(fields[SYS_INDEX_PAGE_NO]->store(-1));
+ } else {
+ OK(fields[SYS_INDEX_PAGE_NO]->store(index->page));
+ }
OK(fields[SYS_INDEX_SPACE]->store(index->space));
=== modified file 'storage/innobase/ibuf/ibuf0ibuf.c'
--- a/storage/innobase/ibuf/ibuf0ibuf.c revid:magne.mahre@stripped
+++ b/storage/innobase/ibuf/ibuf0ibuf.c revid:vasil.dimov@stripped
@@ -184,9 +184,6 @@ level 2 i/o. However, if an OS thread do
it uses synchronous aio, it can access any pages, as long as it obeys the
access order rules. */
-/** Buffer pool size per the maximum insert buffer size */
-#define IBUF_POOL_SIZE_PER_MAX_SIZE 2
-
/** Table name for the insert buffer. */
#define IBUF_TABLE_NAME "SYS_IBUF_TABLE"
@@ -520,12 +517,13 @@ ibuf_init_at_db_start(void)
memset(ibuf, 0, sizeof(*ibuf));
- /* Note that also a pessimistic delete can sometimes make a B-tree
- grow in size, as the references on the upper levels of the tree can
- change */
-
- ibuf->max_size = buf_pool_get_curr_size() / UNIV_PAGE_SIZE
- / IBUF_POOL_SIZE_PER_MAX_SIZE;
+ /* At startup we intialize ibuf to have a maximum of
+ CHANGE_BUFFER_DEFAULT_SIZE in terms of percentage of the
+ buffer pool size. Once ibuf struct is initialized this
+ value is updated with the user supplied size by calling
+ ibuf_max_size_update(). */
+ ibuf->max_size = ((buf_pool_get_curr_size() / UNIV_PAGE_SIZE)
+ * CHANGE_BUFFER_DEFAULT_SIZE) / 100;
mutex_create(ibuf_pessimistic_insert_mutex_key,
&ibuf_pessimistic_insert_mutex,
@@ -598,6 +596,26 @@ ibuf_init_at_db_start(void)
ibuf->index = dict_table_get_first_index(table);
}
+
+/*********************************************************************//**
+Updates the max_size value for ibuf. */
+UNIV_INTERN
+void
+ibuf_max_size_update(
+/*=================*/
+ ulint new_val) /*!< in: new value in terms of
+ percentage of the buffer pool size */
+{
+ ulint new_size = ((buf_pool_get_curr_size() / UNIV_PAGE_SIZE)
+ * new_val) / 100;
+ ibuf_enter();
+ mutex_enter(&ibuf_mutex);
+ ibuf->max_size = new_size;
+ mutex_exit(&ibuf_mutex);
+ ibuf_exit();
+}
+
+
#endif /* !UNIV_HOTBACKUP */
/*********************************************************************//**
Initializes an ibuf bitmap page. */
@@ -2660,22 +2678,45 @@ will be merged from ibuf trees to the pa
empty */
UNIV_INTERN
ulint
-ibuf_contract_for_n_pages(
-/*======================*/
- ibool sync, /*!< in: TRUE if the caller wants to wait for the
- issued read with the highest tablespace address
- to complete */
- ulint n_pages)/*!< in: try to read at least this many pages to
- the buffer pool and merge the ibuf contents to
- them */
+ibuf_contract_in_background(
+/*========================*/
+ ibool full) /*!< in: TRUE if the caller wants to do a full
+ contract based on PCT_IO(100). If FALSE then
+ the size of contract batch is determined based
+ on the current size of the ibuf tree. */
{
ulint sum_bytes = 0;
ulint sum_pages = 0;
ulint n_bytes;
ulint n_pag2;
+ ulint n_pages;
+
+ if (full) {
+ /* Caller has requested a full batch */
+ n_pages = PCT_IO(100);
+ } else {
+
+ ibuf_enter();
+ mutex_enter(&ibuf_mutex);
+
+ /* By default we do a batch of 5% of the io_capacity */
+ n_pages = PCT_IO(5);
+
+ /* If the ibuf->size is more than half the max_size
+ then we make more agreesive contraction.
+ +1 is to avoid division by zero. */
+ if (ibuf->size > ibuf->max_size / 2) {
+ ulint diff = ibuf->size - ibuf->max_size / 2;
+ n_pages += PCT_IO((diff * 100)
+ / (ibuf->max_size + 1));
+ }
+
+ mutex_exit(&ibuf_mutex);
+ ibuf_exit();
+ }
while (sum_pages < n_pages) {
- n_bytes = ibuf_contract_ext(&n_pag2, sync);
+ n_bytes = ibuf_contract_ext(&n_pag2, FALSE);
if (n_bytes == 0) {
return(sum_bytes);
@@ -3395,12 +3436,14 @@ ibuf_insert_low(
do_merge = FALSE;
/* Perform dirty reads of ibuf->size and ibuf->max_size, to
- reduce ibuf_mutex contention. ibuf->max_size remains constant
- after ibuf_init_at_db_start(), but ibuf->size should be
- protected by ibuf_mutex. Given that ibuf->size fits in a
- machine word, this should be OK; at worst we are doing some
- excessive ibuf_contract() or occasionally skipping a
- ibuf_contract(). */
+ reduce ibuf_mutex contention. Given that ibuf->max_size and
+ ibuf->size fit in a machine word, this should be OK; at worst
+ we are doing some excessive ibuf_contract() or occasionally
+ skipping an ibuf_contract(). */
+ if (ibuf->max_size == 0) {
+ return(DB_STRONG_FAIL);
+ }
+
if (ibuf->size >= ibuf->max_size + IBUF_CONTRACT_DO_NOT_INSERT) {
/* Insert buffer is now too big, contract it but do not try
to insert */
=== modified file 'storage/innobase/include/btr0btr.h'
--- a/storage/innobase/include/btr0btr.h revid:magne.mahre@stripped
+++ b/storage/innobase/include/btr0btr.h revid:vasil.dimov@stripped
@@ -92,6 +92,91 @@ insert/delete buffer when the record is
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 */
=== modified file 'storage/innobase/include/btr0cur.h'
--- a/storage/innobase/include/btr0cur.h revid:magne.mahre@stripped
+++ b/storage/innobase/include/btr0cur.h revid:vasil.dimov@stripped
@@ -54,9 +54,6 @@ page_cur_t*
btr_cur_get_page_cur(
/*=================*/
const btr_cur_t* cursor);/*!< in: tree cursor */
-#else /* UNIV_DEBUG */
-# define btr_cur_get_page_cur(cursor) (&(cursor)->page_cur)
-#endif /* UNIV_DEBUG */
/*********************************************************//**
Returns the buffer block on which the tree cursor is positioned.
@return pointer to buffer block */
@@ -64,7 +61,7 @@ UNIV_INLINE
buf_block_t*
btr_cur_get_block(
/*==============*/
- btr_cur_t* cursor);/*!< in: tree cursor */
+ const btr_cur_t* cursor);/*!< in: tree cursor */
/*********************************************************//**
Returns the record pointer of a tree cursor.
@return pointer to record */
@@ -72,7 +69,12 @@ UNIV_INLINE
rec_t*
btr_cur_get_rec(
/*============*/
- btr_cur_t* cursor);/*!< in: tree cursor */
+ const btr_cur_t* cursor);/*!< in: tree cursor */
+#else /* UNIV_DEBUG */
+# define btr_cur_get_page_cur(cursor) (&(cursor)->page_cur)
+# define btr_cur_get_block(cursor) ((cursor)->page_cur.block)
+# define btr_cur_get_rec(cursor) ((cursor)->page_cur.rec)
+#endif /* UNIV_DEBUG */
/*********************************************************//**
Returns the compressed page on which the tree cursor is positioned.
@return pointer to compressed page, or NULL if the page is not compressed */
=== modified file 'storage/innobase/include/btr0cur.ic'
--- a/storage/innobase/include/btr0cur.ic revid:magne.mahre@stripped
+++ b/storage/innobase/include/btr0cur.ic revid:vasil.dimov@stripped
@@ -38,7 +38,7 @@ btr_cur_get_page_cur(
{
return(&((btr_cur_t*) cursor)->page_cur);
}
-#endif /* UNIV_DEBUG */
+
/*********************************************************//**
Returns the buffer block on which the tree cursor is positioned.
@return pointer to buffer block */
@@ -46,7 +46,7 @@ UNIV_INLINE
buf_block_t*
btr_cur_get_block(
/*==============*/
- btr_cur_t* cursor) /*!< in: tree cursor */
+ const btr_cur_t* cursor) /*!< in: tree cursor */
{
return(page_cur_get_block(btr_cur_get_page_cur(cursor)));
}
@@ -58,10 +58,11 @@ UNIV_INLINE
rec_t*
btr_cur_get_rec(
/*============*/
- btr_cur_t* cursor) /*!< in: tree cursor */
+ const btr_cur_t* cursor) /*!< in: tree cursor */
{
- return(page_cur_get_rec(&(cursor->page_cur)));
+ return(page_cur_get_rec(btr_cur_get_page_cur(cursor)));
}
+#endif /* UNIV_DEBUG */
/*********************************************************//**
Returns the compressed page on which the tree cursor is positioned.
=== modified file 'storage/innobase/include/btr0types.h'
--- a/storage/innobase/include/btr0types.h revid:magne.mahre@stripped
+++ b/storage/innobase/include/btr0types.h revid:vasil.dimov@stripped
@@ -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. */
=== modified file 'storage/innobase/include/dict0dict.h'
--- a/storage/innobase/include/dict0dict.h revid:magne.mahre@stripped
+++ b/storage/innobase/include/dict0dict.h revid:vasil.dimov@stripped
@@ -427,7 +427,10 @@ dict_table_t*
dict_table_open_on_name_no_stats(
/*=============================*/
const char* table_name, /*!< in: table name */
- ibool dict_locked); /*!< in: TRUE=data dictionary locked */
+ ibool dict_locked, /*!< in: TRUE=data dictionary locked */
+ dict_err_ignore_t
+ ignore_err); /*!< in: error to be ignored when
+ loading the table */
/**********************************************************************//**
Find an index that is equivalent to the one passed in and is not marked
for deletion.
=== modified file 'storage/innobase/include/dict0load.h'
--- a/storage/innobase/include/dict0load.h revid:magne.mahre@stripped
+++ b/storage/innobase/include/dict0load.h revid:vasil.dimov@stripped
@@ -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 */
=== modified file 'storage/innobase/include/dict0mem.h'
--- a/storage/innobase/include/dict0mem.h revid:magne.mahre@stripped
+++ b/storage/innobase/include/dict0mem.h revid:vasil.dimov@stripped
@@ -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)
@@ -400,6 +402,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 */
@@ -495,6 +504,8 @@ struct dict_table_struct{
unsigned can_be_evicted:1;
/*!< TRUE if it's not an InnoDB system table
or a table that has no FK relationships */
+ 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
=== modified file 'storage/innobase/include/dict0priv.ic'
--- a/storage/innobase/include/dict0priv.ic revid:magne.mahre@stripped
+++ b/storage/innobase/include/dict0priv.ic revid:vasil.dimov@stripped
@@ -45,7 +45,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);
=== modified file 'storage/innobase/include/dict0types.h'
--- a/storage/innobase/include/dict0types.h revid:magne.mahre@stripped
+++ b/storage/innobase/include/dict0types.h revid:vasil.dimov@stripped
@@ -47,4 +47,18 @@ DICT_IBUF_ID_MIN plus the space id */
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
=== modified file 'storage/innobase/include/ibuf0ibuf.h'
--- a/storage/innobase/include/ibuf0ibuf.h revid:magne.mahre@stripped
+++ b/storage/innobase/include/ibuf0ibuf.h revid:vasil.dimov@stripped
@@ -35,6 +35,10 @@ Created 7/19/1997 Heikki Tuuri
#ifndef UNIV_HOTBACKUP
# include "ibuf0types.h"
+/** Default value for maximum on-disk size of change buffer in terms
+of percentage of the buffer pool. */
+#define CHANGE_BUFFER_DEFAULT_SIZE (25)
+
/* Possible operations buffered in the insert/whatever buffer. See
ibuf_insert(). DO NOT CHANGE THE VALUES OF THESE, THEY ARE STORED ON DISK. */
typedef enum {
@@ -98,6 +102,14 @@ void
ibuf_init_at_db_start(void);
/*=======================*/
/*********************************************************************//**
+Updates the max_size value for ibuf. */
+UNIV_INTERN
+void
+ibuf_max_size_update(
+/*=================*/
+ ulint new_val); /*!< in: new value in terms of
+ percentage of the buffer pool size */
+/*********************************************************************//**
Reads the biggest tablespace id from the high end of the insert buffer
tree and updates the counter in fil_system. */
UNIV_INTERN
@@ -358,14 +370,12 @@ will be merged from ibuf trees to the pa
empty */
UNIV_INTERN
ulint
-ibuf_contract_for_n_pages(
-/*======================*/
- ibool sync, /*!< in: TRUE if the caller wants to wait for the
- issued read with the highest tablespace address
- to complete */
- ulint n_pages);/*!< in: try to read at least this many pages to
- the buffer pool and merge the ibuf contents to
- them */
+ibuf_contract_in_background(
+/*========================*/
+ ibool full); /*!< in: TRUE if the caller wants to do a full
+ contract based on PCT_IO(100). If FALSE then
+ the size of contract batch is determined based
+ on the current size of the ibuf tree. */
#endif /* !UNIV_HOTBACKUP */
/*********************************************************************//**
Parses a redo log record of an ibuf bitmap page init.
=== modified file 'storage/innobase/include/ibuf0ibuf.ic'
--- a/storage/innobase/include/ibuf0ibuf.ic revid:magne.mahre@stripped
+++ b/storage/innobase/include/ibuf0ibuf.ic revid:vasil.dimov@stripped
@@ -104,6 +104,7 @@ ibuf_should_try(
decide */
{
if (ibuf_use != IBUF_USE_NONE
+ && ibuf->max_size != 0
&& !dict_index_is_clust(index)
&& (ignore_sec_unique || !dict_index_is_unique(index))) {
=== modified file 'storage/innobase/include/os0sync.h'
--- a/storage/innobase/include/os0sync.h revid:magne.mahre@stripped
+++ b/storage/innobase/include/os0sync.h revid:vasil.dimov@stripped
@@ -42,16 +42,27 @@ Created 9/6/1995 Heikki Tuuri
/** Native event (slow)*/
typedef HANDLE os_native_event_t;
/** Native mutex */
-typedef CRITICAL_SECTION os_fast_mutex_t;
+typedef CRITICAL_SECTION fast_mutex_t;
/** Native condition variable. */
typedef CONDITION_VARIABLE os_cond_t;
#else
/** Native mutex */
-typedef pthread_mutex_t os_fast_mutex_t;
+typedef pthread_mutex_t fast_mutex_t;
/** Native condition variable */
typedef pthread_cond_t os_cond_t;
#endif
+/** Structure that includes Performance Schema Probe pfs_psi
+in the os_fast_mutex structure if UNIV_PFS_MUTEX is defined */
+typedef struct os_fast_mutex_struct {
+ fast_mutex_t mutex; /*!< os_fast_mutex */
+#ifdef UNIV_PFS_MUTEX
+ struct PSI_mutex* pfs_psi;/*!< The performance schema
+ instrumentation hook */
+#endif
+} os_fast_mutex_t;
+
+
/** Operating system event */
typedef struct os_event_struct os_event_struct_t;
/** Operating system event handle */
@@ -235,34 +246,119 @@ ulint
os_fast_mutex_trylock(
/*==================*/
os_fast_mutex_t* fast_mutex); /*!< in: mutex to acquire */
+
+/**********************************************************************
+Following os_fast_ mutex APIs would be performance schema instrumented:
+
+os_fast_mutex_init
+os_fast_mutex_lock
+os_fast_mutex_unlock
+os_fast_mutex_free
+
+These mutex APIs will point to corresponding wrapper functions that contain
+the performance schema instrumentation.
+
+NOTE! The following macro should be used in mutex operation, not the
+corresponding function. */
+
+#ifdef UNIV_PFS_MUTEX
+# define os_fast_mutex_init(K, M) \
+ pfs_os_fast_mutex_init(K, M)
+
+# define os_fast_mutex_lock(M) \
+ pfs_os_fast_mutex_lock(M, __FILE__, __LINE__)
+
+# define os_fast_mutex_unlock(M) pfs_os_fast_mutex_unlock(M)
+
+# define os_fast_mutex_free(M) pfs_os_fast_mutex_free(M)
+
+/*********************************************************//**
+NOTE! Please use the corresponding macro os_fast_mutex_init(), not directly
+this function!
+A wrapper function for os_fast_mutex_init_func(). Initializes an operating
+system fast mutex semaphore. */
+UNIV_INLINE
+void
+pfs_os_fast_mutex_init(
+/*===================*/
+ PSI_mutex_key key, /*!< in: Performance Schema
+ key */
+ os_fast_mutex_t* fast_mutex); /*!< out: fast mutex */
+/**********************************************************//**
+NOTE! Please use the corresponding macro os_fast_mutex_free(), not directly
+this function!
+Wrapper function for pfs_os_fast_mutex_free(). Also destroys the performance
+schema probes when freeing the mutex */
+UNIV_INLINE
+void
+pfs_os_fast_mutex_free(
+/*===================*/
+ os_fast_mutex_t* fast_mutex); /*!< in/out: mutex to free */
+/**********************************************************//**
+NOTE! Please use the corresponding macro os_fast_mutex_lock, not directly
+this function!
+Wrapper function of os_fast_mutex_lock. Acquires ownership of a fast mutex. */
+UNIV_INLINE
+void
+pfs_os_fast_mutex_lock(
+/*===================*/
+ os_fast_mutex_t* fast_mutex, /*!< in/out: mutex to acquire */
+ const char* file_name, /*!< in: file name where
+ locked */
+ ulint line); /*!< in: line where locked */
+/**********************************************************//**
+NOTE! Please use the corresponding macro os_fast_mutex_unlock, not directly
+this function!
+Wrapper function of os_fast_mutex_unlock. Releases ownership of a fast mutex. */
+UNIV_INLINE
+void
+pfs_os_fast_mutex_unlock(
+/*=====================*/
+ os_fast_mutex_t* fast_mutex); /*!< in/out: mutex to release */
+
+#else /* UNIV_PFS_MUTEX */
+
+# define os_fast_mutex_init(K, M) \
+ os_fast_mutex_init_func(&((os_fast_mutex_t*)(M))->mutex)
+
+# define os_fast_mutex_lock(M) \
+ os_fast_mutex_lock_func(&((os_fast_mutex_t*)(M))->mutex)
+
+# define os_fast_mutex_unlock(M) \
+ os_fast_mutex_unlock_func(&((os_fast_mutex_t*)(M))->mutex)
+
+# define os_fast_mutex_free(M) \
+ os_fast_mutex_free_func(&((os_fast_mutex_t*)(M))->mutex)
+#endif /* UNIV_PFS_MUTEX */
+
/**********************************************************//**
Releases ownership of a fast mutex. */
UNIV_INTERN
void
-os_fast_mutex_unlock(
-/*=================*/
- os_fast_mutex_t* fast_mutex); /*!< in: mutex to release */
+os_fast_mutex_unlock_func(
+/*======================*/
+ fast_mutex_t* fast_mutex); /*!< in: mutex to release */
/*********************************************************//**
Initializes an operating system fast mutex semaphore. */
UNIV_INTERN
void
-os_fast_mutex_init(
-/*===============*/
- os_fast_mutex_t* fast_mutex); /*!< in: fast mutex */
+os_fast_mutex_init_func(
+/*====================*/
+ fast_mutex_t* fast_mutex); /*!< in: fast mutex */
/**********************************************************//**
Acquires ownership of a fast mutex. */
UNIV_INTERN
void
-os_fast_mutex_lock(
-/*===============*/
- os_fast_mutex_t* fast_mutex); /*!< in: mutex to acquire */
+os_fast_mutex_lock_func(
+/*====================*/
+ fast_mutex_t* fast_mutex); /*!< in: mutex to acquire */
/**********************************************************//**
Frees an mutex object. */
UNIV_INTERN
void
-os_fast_mutex_free(
-/*===============*/
- os_fast_mutex_t* fast_mutex); /*!< in: mutex to free */
+os_fast_mutex_free_func(
+/*====================*/
+ fast_mutex_t* fast_mutex); /*!< in: mutex to free */
/**********************************************************//**
Atomic compare-and-swap and increment for InnoDB. */
=== modified file 'storage/innobase/include/os0sync.ic'
--- a/storage/innobase/include/os0sync.ic revid:magne.mahre@stripped
+++ b/storage/innobase/include/os0sync.ic revid:vasil.dimov@stripped
@@ -36,14 +36,10 @@ os_fast_mutex_trylock(
/*==================*/
os_fast_mutex_t* fast_mutex) /*!< in: mutex to acquire */
{
-#ifdef __WIN__
- if (TryEnterCriticalSection(fast_mutex)) {
-
- return(0);
- } else {
+ fast_mutex_t* mutex = &fast_mutex->mutex;
- return(1);
- }
+#ifdef __WIN__
+ return(!TryEnterCriticalSection(mutex));
#else
/* NOTE that the MySQL my_pthread.h redefines pthread_mutex_trylock
so that it returns 0 on success. In the operating system
@@ -51,10 +47,100 @@ os_fast_mutex_trylock(
returns 1 on success (but MySQL remaps that to 0), while Linux,
FreeBSD, Solaris, AIX, Tru64 Unix, HP-UX-11.0 return 0 on success. */
- return((ulint) pthread_mutex_trylock(fast_mutex));
+ return((ulint) pthread_mutex_trylock(mutex));
#endif
}
+#ifdef UNIV_PFS_MUTEX
+/*********************************************************//**
+NOTE! Please use the corresponding macro os_fast_mutex_init(), not directly
+this function!
+A wrapper function for os_fast_mutex_init_func(). Initializes an operating
+system fast mutex semaphore. */
+UNIV_INLINE
+void
+pfs_os_fast_mutex_init(
+/*===================*/
+ PSI_mutex_key key, /*!< in: Performance Schema
+ key */
+ os_fast_mutex_t* fast_mutex) /*!< out: fast mutex */
+{
+ fast_mutex->pfs_psi = (PSI_server && PFS_IS_INSTRUMENTED(key))
+ ? PSI_server->init_mutex(key,
+ &fast_mutex->mutex)
+ : NULL;
+
+ os_fast_mutex_init_func(&fast_mutex->mutex);
+}
+/******************************************************************//**
+NOTE! Please use the corresponding macro os_fast_mutex_free(), not directly
+this function!
+Wrapper function for pfs_os_fast_mutex_free(). Also destroys the performance
+schema probes when freeing the mutex */
+UNIV_INLINE
+void
+pfs_os_fast_mutex_free(
+/*===================*/
+ os_fast_mutex_t* fast_mutex) /*!< in/out: mutex */
+{
+ if (UNIV_LIKELY(PSI_server && fast_mutex->pfs_psi)) {
+ PSI_server->destroy_mutex(fast_mutex->pfs_psi);
+ fast_mutex->pfs_psi = NULL;
+ }
+
+ os_fast_mutex_free_func(&fast_mutex->mutex);
+}
+/**********************************************************//**
+NOTE! Please use the corresponding macro os_fast_mutex_lock, not directly
+this function!
+Wrapper function of os_fast_mutex_lock_func. Acquires ownership of a fast
+mutex. */
+UNIV_INLINE
+void
+pfs_os_fast_mutex_lock(
+/*===================*/
+ os_fast_mutex_t* fast_mutex, /*!< in/out: mutex to acquire */
+ const char* file_name, /*!< in: file name where
+ locked */
+ ulint line) /*!< in: line where locked */
+{
+ struct PSI_mutex_locker* locker = NULL;
+ PSI_mutex_locker_state state;
+ int result = 0;
+
+ if (UNIV_LIKELY(PSI_server && fast_mutex->pfs_psi)) {
+ locker = PSI_server->get_thread_mutex_locker(
+ &state, fast_mutex->pfs_psi, PSI_MUTEX_LOCK);
+ if (locker) {
+ PSI_server->start_mutex_wait(locker, file_name, line);
+ }
+ }
+
+ os_fast_mutex_lock_func(&fast_mutex->mutex);
+
+ if (locker) {
+ PSI_server->end_mutex_wait(locker, result);
+ }
+}
+/**********************************************************//**
+NOTE! Please use the corresponding macro os_fast_mutex_unlock, not directly
+this function!
+Wrapper function of os_fast_mutex_unlock_func. Releases ownership of a
+fast mutex. */
+UNIV_INLINE
+void
+pfs_os_fast_mutex_unlock(
+/*=====================*/
+ os_fast_mutex_t* fast_mutex) /*!< in/out: mutex to release */
+{
+ if (UNIV_LIKELY(PSI_server && fast_mutex->pfs_psi)) {
+ PSI_server->unlock_mutex(fast_mutex->pfs_psi);
+ }
+
+ os_fast_mutex_unlock_func(&fast_mutex->mutex);
+}
+#endif /* UNIV_PFS_MUTEX */
+
#ifdef HAVE_WINDOWS_ATOMICS
/* Use inline functions to make 64 and 32 bit versions of windows atomic
=== modified file 'storage/innobase/include/page0zip.h'
--- a/storage/innobase/include/page0zip.h revid:magne.mahre@stripped
+++ b/storage/innobase/include/page0zip.h revid:vasil.dimov@stripped
@@ -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 */
/**********************************************************************//**
=== modified file 'storage/innobase/include/srv0srv.h'
--- a/storage/innobase/include/srv0srv.h revid:magne.mahre@stripped
+++ b/storage/innobase/include/srv0srv.h revid:vasil.dimov@stripped
@@ -172,7 +172,7 @@ extern ulong srv_io_capacity;
/* Returns the number of IO operations that is X percent of the
capacity. PCT_IO(5) -> returns the number of IO operations that
is 5% of the max where max is srv_io_capacity. */
-#define PCT_IO(p) ((ulong) (srv_io_capacity * ((double) p / 100.0)))
+#define PCT_IO(p) ((ulong) (srv_io_capacity * ((double) (p) / 100.0)))
/* The "innodb_stats_method" setting, decides how InnoDB is going
to treat NULL value when collecting statistics. It is not defined
=== modified file 'storage/innobase/include/sync0sync.h'
--- a/storage/innobase/include/sync0sync.h revid:magne.mahre@stripped
+++ b/storage/innobase/include/sync0sync.h revid:vasil.dimov@stripped
@@ -53,19 +53,14 @@ typedef byte lock_word_t;
#endif
#if defined UNIV_PFS_MUTEX || defined UNIV_PFS_RWLOCK
-/* There are mutexes/rwlocks that we want to exclude from
-instrumentation even if their corresponding performance schema
-define is set. And this PFS_NOT_INSTRUMENTED is used
-as the key value to dentify those objects that would
-be excluded from instrumentation. */
-# define PFS_NOT_INSTRUMENTED ULINT32_UNDEFINED
-
-# define PFS_IS_INSTRUMENTED(key) ((key) != PFS_NOT_INSTRUMENTED)
/* By default, buffer mutexes and rwlocks will be excluded from
instrumentation due to their large number of instances. */
# define PFS_SKIP_BUFFER_MUTEX_RWLOCK
+/* By default, event->mutex will also be excluded from instrumentation */
+# define PFS_SKIP_EVENT_MUTEX
+
#endif /* UNIV_PFS_MUTEX || UNIV_PFS_RWLOCK */
#ifdef UNIV_PFS_MUTEX
@@ -122,6 +117,11 @@ extern mysql_pfs_key_t trx_sys_rw_lock_k
extern mysql_pfs_key_t read_view_mutex_key;
extern mysql_pfs_key_t srv_sys_mutex_key;
extern mysql_pfs_key_t srv_sys_tasks_mutex_key;
+extern mysql_pfs_key_t srv_conc_mutex_key;
+extern mysql_pfs_key_t event_os_mutex_key;
+extern mysql_pfs_key_t ut_list_mutex_key;
+extern mysql_pfs_key_t os_mutex_key;
+
#endif /* UNIV_PFS_MUTEX */
/******************************************************************//**
=== modified file 'storage/innobase/include/trx0sys.h'
--- a/storage/innobase/include/trx0sys.h revid:magne.mahre@stripped
+++ b/storage/innobase/include/trx0sys.h revid:vasil.dimov@stripped
@@ -685,9 +685,6 @@ struct trx_sys_struct{
UT_LIST_BASE_NODE_T(trx_t) mysql_trx_list;
/*!< List of transactions created
for MySQL */
- ulint latest_rseg; /*!< Latest rollback segment in the
- round-robin assignment of rollback
- segments to transactions */
trx_rseg_t* rseg_array[TRX_SYS_N_RSEGS];
/*!< Pointer array to rollback
segments; NULL if slot not in use;
=== modified file 'storage/innobase/include/univ.i'
--- a/storage/innobase/include/univ.i revid:magne.mahre@stripped
+++ b/storage/innobase/include/univ.i revid:vasil.dimov@stripped
@@ -144,6 +144,16 @@ resolved */
# define UNIV_PFS_IO
# endif
# define UNIV_PFS_THREAD
+
+/* There are mutexes/rwlocks that we want to exclude from
+instrumentation even if their corresponding performance schema
+define is set. And this PFS_NOT_INSTRUMENTED is used
+as the key value to identify those objects that would
+be excluded from instrumentation. */
+# define PFS_NOT_INSTRUMENTED ULINT32_UNDEFINED
+
+# define PFS_IS_INSTRUMENTED(key) ((key) != PFS_NOT_INSTRUMENTED)
+
#endif /* HAVE_PSI_INTERFACE */
/* DEBUG VERSION CONTROL
@@ -196,6 +206,8 @@ this will break redo log file compatibil
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 */
=== modified file 'storage/innobase/os/os0sync.c'
--- a/storage/innobase/os/os0sync.c revid:magne.mahre@stripped
+++ b/storage/innobase/os/os0sync.c revid:vasil.dimov@stripped
@@ -75,6 +75,11 @@ UNIV_INTERN ulint os_fast_mutex_count =
/* The number of microsecnds in a second. */
static const ulint MICROSECS_IN_A_SECOND = 1000000;
+#ifdef UNIV_PFS_MUTEX
+UNIV_INTERN mysql_pfs_key_t event_os_mutex_key;
+UNIV_INTERN mysql_pfs_key_t os_mutex_key;
+#endif
+
/* Because a mutex is embedded inside an event and there is an
event embedded inside a mutex, on free, this generates a recursive call.
This version of the free event function doesn't acquire the global lock */
@@ -132,7 +137,7 @@ ibool
os_cond_wait_timed(
/*===============*/
os_cond_t* cond, /*!< in: condition variable. */
- os_fast_mutex_t* mutex, /*!< in: fast mutex */
+ os_fast_mutex_t* fast_mutex, /*!< in: fast mutex */
#ifndef __WIN__
const struct timespec* abstime /*!< in: timeout */
#else
@@ -141,6 +146,7 @@ os_cond_wait_timed(
#endif /* !__WIN__ */
)
{
+ fast_mutex_t* mutex = &fast_mutex->mutex;
#ifdef __WIN__
BOOL ret;
DWORD err;
@@ -195,8 +201,9 @@ void
os_cond_wait(
/*=========*/
os_cond_t* cond, /*!< in: condition variable. */
- os_fast_mutex_t* mutex) /*!< in: fast mutex */
+ os_fast_mutex_t* fast_mutex)/*!< in: fast mutex */
{
+ fast_mutex_t* mutex = &fast_mutex->mutex;
ut_a(cond);
ut_a(mutex);
@@ -388,7 +395,12 @@ os_event_create(
event = ut_malloc(sizeof(struct os_event_struct));
- os_fast_mutex_init(&(event->os_mutex));
+#ifndef PFS_SKIP_EVENT_MUTEX
+ os_fast_mutex_init(event_os_mutex_key, &event->os_mutex);
+#else
+ os_fast_mutex_init(PFS_NOT_INSTRUMENTED, &event->os_mutex);
+#endif
+
os_cond_init(&(event->cond_var));
@@ -775,7 +787,7 @@ os_mutex_create(void)
mutex = ut_malloc(sizeof(os_fast_mutex_t));
- os_fast_mutex_init(mutex);
+ os_fast_mutex_init(os_mutex_key, mutex);
mutex_str = ut_malloc(sizeof(os_mutex_str_t));
mutex_str->handle = mutex;
@@ -864,9 +876,9 @@ os_mutex_free(
Initializes an operating system fast mutex semaphore. */
UNIV_INTERN
void
-os_fast_mutex_init(
-/*===============*/
- os_fast_mutex_t* fast_mutex) /*!< in: fast mutex */
+os_fast_mutex_init_func(
+/*====================*/
+ fast_mutex_t* fast_mutex) /*!< in: fast mutex */
{
#ifdef __WIN__
ut_a(fast_mutex);
@@ -893,9 +905,9 @@ os_fast_mutex_init(
Acquires ownership of a fast mutex. */
UNIV_INTERN
void
-os_fast_mutex_lock(
-/*===============*/
- os_fast_mutex_t* fast_mutex) /*!< in: mutex to acquire */
+os_fast_mutex_lock_func(
+/*====================*/
+ fast_mutex_t* fast_mutex) /*!< in: mutex to acquire */
{
#ifdef __WIN__
EnterCriticalSection((LPCRITICAL_SECTION) fast_mutex);
@@ -908,9 +920,9 @@ os_fast_mutex_lock(
Releases ownership of a fast mutex. */
UNIV_INTERN
void
-os_fast_mutex_unlock(
-/*=================*/
- os_fast_mutex_t* fast_mutex) /*!< in: mutex to release */
+os_fast_mutex_unlock_func(
+/*======================*/
+ fast_mutex_t* fast_mutex) /*!< in: mutex to release */
{
#ifdef __WIN__
LeaveCriticalSection(fast_mutex);
@@ -923,9 +935,9 @@ os_fast_mutex_unlock(
Frees a mutex object. */
UNIV_INTERN
void
-os_fast_mutex_free(
-/*===============*/
- os_fast_mutex_t* fast_mutex) /*!< in: mutex to free */
+os_fast_mutex_free_func(
+/*====================*/
+ fast_mutex_t* fast_mutex) /*!< in: mutex to free */
{
#ifdef __WIN__
ut_a(fast_mutex);
=== modified file 'storage/innobase/page/page0cur.c'
--- a/storage/innobase/page/page0cur.c revid:magne.mahre@stripped
+++ b/storage/innobase/page/page0cur.c revid:vasil.dimov@stripped
@@ -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.
=== modified file 'storage/innobase/page/page0page.c'
--- a/storage/innobase/page/page0page.c revid:magne.mahre@stripped
+++ b/storage/innobase/page/page0page.c revid:vasil.dimov@stripped
@@ -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));
=== modified file 'storage/innobase/page/page0zip.c'
--- a/storage/innobase/page/page0zip.c revid:magne.mahre@stripped
+++ b/storage/innobase/page/page0zip.c revid:vasil.dimov@stripped
@@ -4455,6 +4455,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 */
@@ -4513,7 +4515,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
@@ -4583,6 +4585,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);
}
=== modified file 'storage/innobase/row/row0ins.c'
--- a/storage/innobase/row/row0ins.c revid:magne.mahre@stripped
+++ b/storage/innobase/row/row0ins.c revid:vasil.dimov@stripped
@@ -48,9 +48,6 @@ Created 4/20/1996 Heikki Tuuri
#include "usr0sess.h"
#include "buf0lru.h"
-#define ROW_INS_PREV 1
-#define ROW_INS_NEXT 2
-
/*************************************************************************
IMPORTANT NOTE: Any operation that generates redo MUST check that there
is enough space in the redo log before for that operation. This is
@@ -1930,23 +1927,22 @@ func_exit:
}
/***************************************************************//**
-Checks if an index entry has long enough common prefix with an existing
-record so that the intended insert of the entry must be changed to a modify of
-the existing record. In the case of a clustered index, the prefix must be
-n_unique fields long, and in the case of a secondary index, all fields must be
-equal.
-@return 0 if no update, ROW_INS_PREV if previous should be updated;
-currently we do the search so that only the low_match record can match
-enough to the search tuple, not the next record */
+Checks if an index entry has long enough common prefix with an
+existing record so that the intended insert of the entry must be
+changed to a modify of the existing record. In the case of a clustered
+index, the prefix must be n_unique fields long. In the case of a
+secondary index, all fields must be equal. InnoDB never updates
+secondary index records in place, other than clearing or setting the
+delete-mark flag. We could be able to update the non-unique fields
+of a unique secondary index record by checking the cursor->up_match,
+but we do not do so, because it could have some locking implications.
+@return TRUE if the existing record should be updated; FALSE if not */
UNIV_INLINE
-ulint
-row_ins_must_modify(
-/*================*/
- btr_cur_t* cursor) /*!< in: B-tree cursor */
+ibool
+row_ins_must_modify_rec(
+/*====================*/
+ const btr_cur_t* cursor) /*!< in: B-tree cursor */
{
- ulint enough_match;
- rec_t* rec;
-
/* NOTE: (compare to the note in row_ins_duplicate_error) Because node
pointers on upper levels of the B-tree may match more to entry than
to actual user records on the leaf level, we have to check if the
@@ -1954,19 +1950,9 @@ row_ins_must_modify(
node pointers contain index->n_unique first fields, and in the case
of a secondary index, all fields of the index. */
- enough_match = dict_index_get_n_unique_in_tree(cursor->index);
-
- if (cursor->low_match >= enough_match) {
-
- rec = btr_cur_get_rec(cursor);
-
- if (!page_rec_is_infimum(rec)) {
-
- return(ROW_INS_PREV);
- }
- }
-
- return(0);
+ return(cursor->low_match
+ >= dict_index_get_n_unique_in_tree(cursor->index)
+ && !page_rec_is_infimum(btr_cur_get_rec(cursor)));
}
/***************************************************************//**
@@ -1994,9 +1980,8 @@ row_ins_index_entry_low(
{
btr_cur_t cursor;
ulint search_mode;
- ulint modify = 0; /* remove warning */
+ ibool modify = FALSE;
rec_t* insert_rec;
- rec_t* rec;
ulint err;
ulint n_unique;
big_rec_t* big_rec = NULL;
@@ -2087,20 +2072,13 @@ row_ins_index_entry_low(
}
}
- modify = row_ins_must_modify(&cursor);
+ modify = row_ins_must_modify_rec(&cursor);
- if (modify != 0) {
+ if (modify) {
/* There is already an index entry with a long enough common
prefix, we must convert the insert into a modify of an
existing record */
- if (modify == ROW_INS_NEXT) {
- rec = page_rec_get_next(btr_cur_get_rec(&cursor));
-
- btr_cur_position(index, rec,
- btr_cur_get_block(&cursor),&cursor);
- }
-
if (dict_index_is_clust(index)) {
err = row_ins_clust_index_entry_by_modify(
mode, &cursor, &heap, &big_rec, entry,
=== modified file 'storage/innobase/row/row0merge.c'
--- a/storage/innobase/row/row0merge.c revid:magne.mahre@stripped
+++ b/storage/innobase/row/row0merge.c revid:vasil.dimov@stripped
@@ -2323,7 +2323,7 @@ row_merge_create_temporary_table(
use it we need to open the table. */
temp_table = dict_table_open_on_name_no_stats(
- new_table->name, TRUE);
+ new_table->name, TRUE, DICT_ERR_IGNORE_NONE);
ut_a(new_table == temp_table);
}
=== modified file 'storage/innobase/row/row0mysql.c'
--- a/storage/innobase/row/row0mysql.c revid:magne.mahre@stripped
+++ b/storage/innobase/row/row0mysql.c revid:vasil.dimov@stripped
@@ -1951,7 +1951,8 @@ err_exit:
ut_print_name(stderr, trx, TRUE, table->name);
fputs(" because tablespace full\n", stderr);
- if (dict_table_open_on_name_no_stats(table->name, FALSE)) {
+ if (dict_table_open_on_name_no_stats(
+ table->name, FALSE, DICT_ERR_IGNORE_NONE)) {
/* Make things easy for the drop table code. */
@@ -2254,7 +2255,8 @@ loop:
return(n_tables + n_tables_dropped);
}
- table = dict_table_open_on_name_no_stats(drop->table_name, FALSE);
+ table = dict_table_open_on_name_no_stats(drop->table_name, FALSE,
+ DICT_ERR_IGNORE_NONE);
if (table == NULL) {
/* If for some reason the table has already been dropped
@@ -2425,7 +2427,8 @@ row_discard_tablespace_for_mysql(
row_mysql_lock_data_dictionary(trx);
- table = dict_table_open_on_name_no_stats(name, TRUE);
+ table = dict_table_open_on_name_no_stats(name, TRUE,
+ DICT_ERR_IGNORE_NONE);
if (!table) {
err = DB_TABLE_NOT_FOUND;
@@ -2624,7 +2627,8 @@ row_import_tablespace_for_mysql(
row_mysql_lock_data_dictionary(trx);
- table = dict_table_open_on_name_no_stats(name, TRUE);
+ table = dict_table_open_on_name_no_stats(name, TRUE,
+ DICT_ERR_IGNORE_NONE);
if (!table) {
ut_print_timestamp(stderr);
@@ -3152,7 +3156,8 @@ row_drop_table_for_mysql(
ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX));
#endif /* UNIV_SYNC_DEBUG */
- table = dict_table_open_on_name_no_stats(name, TRUE);
+ table = dict_table_open_on_name_no_stats(name, TRUE,
+ DICT_ERR_IGNORE_INDEX_ROOT);
if (!table) {
err = DB_TABLE_NOT_FOUND;
@@ -3415,7 +3420,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);
@@ -3572,7 +3577,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);
@@ -3680,7 +3685,8 @@ loop:
while ((table_name = dict_get_first_table_name_in_db(name))) {
ut_a(memcmp(table_name, name, namelen) == 0);
- table = dict_table_open_on_name_no_stats(table_name, TRUE);
+ table = dict_table_open_on_name_no_stats(table_name, TRUE,
+ DICT_ERR_IGNORE_NONE);
ut_a(table);
ut_a(!table->can_be_evicted);
@@ -3874,7 +3880,8 @@ row_rename_table_for_mysql(
dict_locked = trx->dict_operation_lock_mode == RW_X_LATCH;
- table = dict_table_open_on_name_no_stats(old_name, dict_locked);
+ table = dict_table_open_on_name_no_stats(old_name, dict_locked,
+ DICT_ERR_IGNORE_NONE);
if (!table) {
err = DB_TABLE_NOT_FOUND;
=== modified file 'storage/innobase/row/row0upd.c'
--- a/storage/innobase/row/row0upd.c revid:magne.mahre@stripped
+++ b/storage/innobase/row/row0upd.c revid:vasil.dimov@stripped
@@ -496,14 +496,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)) {
=== modified file 'storage/innobase/srv/srv0srv.c'
--- a/storage/innobase/srv/srv0srv.c revid:magne.mahre@stripped
+++ b/storage/innobase/srv/srv0srv.c revid:vasil.dimov@stripped
@@ -455,6 +455,8 @@ UNIV_INTERN mysql_pfs_key_t srv_misc_tmp
UNIV_INTERN mysql_pfs_key_t srv_sys_mutex_key;
/* Key to register srv_sys_t::tasks_mutex with performance schema */
UNIV_INTERN mysql_pfs_key_t srv_sys_tasks_mutex_key;
+/* Key to register srv_conc_mutex_key with performance schema */
+UNIV_INTERN mysql_pfs_key_t srv_conc_mutex_key;
#endif /* UNIV_PFS_MUTEX */
/* Temporary file for innodb monitor output */
@@ -957,7 +959,7 @@ srv_init(void)
/* Init the server concurrency restriction data structures */
- os_fast_mutex_init(&srv_conc_mutex);
+ os_fast_mutex_init(srv_conc_mutex_key, &srv_conc_mutex);
UT_LIST_INIT(srv_conc_queue);
@@ -2306,7 +2308,7 @@ srv_master_do_active_tasks(void)
/* Do an ibuf merge */
srv_main_thread_op_info = "doing insert buffer merge";
- ibuf_contract_for_n_pages(FALSE, PCT_IO(5));
+ ibuf_contract_in_background(FALSE);
/* Flush logs if needed */
srv_main_thread_op_info = "flushing log";
@@ -2384,7 +2386,7 @@ srv_master_do_idle_tasks(void)
/* Do an ibuf merge */
srv_main_thread_op_info = "doing insert buffer merge";
- ibuf_contract_for_n_pages(FALSE, PCT_IO(100));
+ ibuf_contract_in_background(TRUE);
if (srv_shutdown_state > 0) {
return;
@@ -2456,7 +2458,7 @@ srv_master_do_shutdown_tasks(
/* Do an ibuf merge */
srv_main_thread_op_info = "doing insert buffer merge";
- n_bytes_merged = ibuf_contract_for_n_pages(FALSE, PCT_IO(100));
+ n_bytes_merged = ibuf_contract_in_background(TRUE);
/* Flush logs if needed */
srv_sync_log_buffer_in_background();
=== modified file 'storage/innobase/srv/srv0start.c'
--- a/storage/innobase/srv/srv0start.c revid:magne.mahre@stripped
+++ b/storage/innobase/srv/srv0start.c revid:vasil.dimov@stripped
@@ -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,
@@ -1984,7 +1990,7 @@ innobase_start_or_create_for_mysql(void)
}
/* Check that os_fast_mutexes work as expected */
- os_fast_mutex_init(&srv_os_test_mutex);
+ os_fast_mutex_init(PFS_NOT_INSTRUMENTED, &srv_os_test_mutex);
if (0 != os_fast_mutex_trylock(&srv_os_test_mutex)) {
ut_print_timestamp(stderr);
=== modified file 'storage/innobase/sync/sync0rw.c'
--- a/storage/innobase/sync/sync0rw.c revid:magne.mahre@stripped
+++ b/storage/innobase/sync/sync0rw.c revid:vasil.dimov@stripped
@@ -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);
=== modified file 'storage/innobase/sync/sync0sync.c'
--- a/storage/innobase/sync/sync0sync.c revid:magne.mahre@stripped
+++ b/storage/innobase/sync/sync0sync.c revid:vasil.dimov@stripped
@@ -280,7 +280,7 @@ mutex_create_func(
#if defined(HAVE_ATOMIC_BUILTINS)
mutex_reset_lock_word(mutex);
#else
- os_fast_mutex_init(&(mutex->os_fast_mutex));
+ os_fast_mutex_init(PFS_NOT_INSTRUMENTED, &mutex->os_fast_mutex);
mutex->lock_word = 0;
#endif
mutex->event = os_event_create(NULL);
=== modified file 'storage/innobase/trx/trx0purge.c'
--- a/storage/innobase/trx/trx0purge.c revid:magne.mahre@stripped
+++ b/storage/innobase/trx/trx0purge.c revid:vasil.dimov@stripped
@@ -166,6 +166,7 @@ trx_purge_sys_create(
purge_sys->trx->id = 0;
purge_sys->trx->start_time = ut_time();
purge_sys->trx->state = TRX_STATE_ACTIVE;
+ purge_sys->trx->op_info = "purge trx";
purge_sys->query = trx_purge_graph_build(
purge_sys->trx, n_purge_threads);
=== modified file 'storage/innobase/trx/trx0trx.c'
--- a/storage/innobase/trx/trx0trx.c revid:magne.mahre@stripped
+++ b/storage/innobase/trx/trx0trx.c revid:vasil.dimov@stripped
@@ -595,7 +595,6 @@ trx_lists_init_at_db_start(void)
/******************************************************************//**
Assigns a rollback segment to a transaction in a round-robin fashion.
-Skips the SYSTEM rollback segment if another is available.
@return assigned rollback segment */
UNIV_INLINE
trx_rseg_t*
@@ -604,11 +603,12 @@ trx_assign_rseg(void)
{
ulint i;
trx_rseg_t* rseg;
+ static ulint latest_rseg = 0;
- /* This breaks true round robin but that should be OK.
- Our aim is to reduce the contention of the trx_sys_t::lock. */
- i = trx_sys->latest_rseg;
- i %= TRX_SYS_N_RSEGS;
+ /* This breaks true round robin but that should be OK. */
+
+ i = latest_rseg++;
+ i %= TRX_SYS_N_RSEGS;
do {
rseg = trx_sys->rseg_array[i];
@@ -618,13 +618,13 @@ trx_assign_rseg(void)
} while (rseg == NULL);
+#ifdef UNIV_DEBUG
rw_lock_x_lock(&trx_sys->lock);
- trx_sys->latest_rseg = i % TRX_SYS_N_RSEGS;
-
ut_ad(trx_sys_validate_trx_list());
rw_lock_x_unlock(&trx_sys->lock);
+#endif /* UNIV_DEBUG */
return(rseg);
}
@@ -1242,10 +1242,6 @@ state_ok:
fputs(" recovered trx", f);
}
- if (trx->id == 0) {
- fputs(" purge trx", f);
- }
-
if (trx->declared_to_be_inside_innodb) {
fprintf(f, ", thread declared inside InnoDB %lu",
(ulong) trx->n_tickets_to_enter_innodb);
=== modified file 'storage/innobase/ut/ut0mem.c'
--- a/storage/innobase/ut/ut0mem.c revid:magne.mahre@stripped
+++ b/storage/innobase/ut/ut0mem.c revid:vasil.dimov@stripped
@@ -46,6 +46,11 @@ UNIV_INTERN ulint ut_total_allocated_me
/** Mutex protecting ut_total_allocated_memory and ut_mem_block_list */
UNIV_INTERN os_fast_mutex_t ut_list_mutex;
+#ifdef UNIV_PFS_MUTEX
+/* Key to register server_mutex with performance schema */
+UNIV_INTERN mysql_pfs_key_t ut_list_mutex_key;
+#endif
+
/** Dynamically allocated memory block */
struct ut_mem_block_struct{
UT_LIST_NODE_T(ut_mem_block_t) mem_block_list;
@@ -77,7 +82,7 @@ ut_mem_init(void)
/*=============*/
{
ut_a(!ut_mem_block_list_inited);
- os_fast_mutex_init(&ut_list_mutex);
+ os_fast_mutex_init(ut_list_mutex_key, &ut_list_mutex);
UT_LIST_INIT(ut_mem_block_list);
ut_mem_block_list_inited = TRUE;
}
No bundle (reason: useless for push emails).
| Thread |
|---|
| • bzr push into mysql-trunk branch (vasil.dimov:3666) | vasil.dimov | 17 Feb |