summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--VERSION2
-rw-r--r--client/mysqlslap.c49
-rw-r--r--mysql-test/r/myisam_recover.result (renamed from mysql-test/r/merge_recover.result)57
-rw-r--r--mysql-test/suite/innodb/r/add_constraint.result13
-rw-r--r--mysql-test/suite/innodb/t/add_constraint.test21
-rw-r--r--mysql-test/suite/ndb/r/ndb_restore_discover.result33
-rw-r--r--mysql-test/suite/ndb/t/ndb_restore_discover.test70
-rw-r--r--mysql-test/t/myisam_recover-master.opt (renamed from mysql-test/t/merge_recover-master.opt)0
-rw-r--r--mysql-test/t/myisam_recover.test (renamed from mysql-test/t/merge_recover.test)77
-rw-r--r--mysys/ptr_cmp.c3
-rw-r--r--regex/regcomp.c24
-rw-r--r--sql/sql_base.cc58
-rw-r--r--storage/innobase/dict/dict0dict.c10
-rw-r--r--storage/innobase/include/row0purge.h13
-rw-r--r--storage/innobase/os/os0file.c13
-rw-r--r--storage/innobase/row/row0purge.c93
16 files changed, 486 insertions, 50 deletions
diff --git a/VERSION b/VERSION
index fa5486099ce..904aeda33ab 100644
--- a/VERSION
+++ b/VERSION
@@ -1,4 +1,4 @@
MYSQL_VERSION_MAJOR=5
MYSQL_VERSION_MINOR=5
-MYSQL_VERSION_PATCH=44
+MYSQL_VERSION_PATCH=45
MYSQL_VERSION_EXTRA=
diff --git a/client/mysqlslap.c b/client/mysqlslap.c
index 536535be294..03301b01efb 100644
--- a/client/mysqlslap.c
+++ b/client/mysqlslap.c
@@ -1,5 +1,6 @@
/*
- Copyright (c) 2005, 2012, Oracle and/or its affiliates.
+ Copyright (c) 2005, 2015, Oracle and/or its affiliates.
+ Copyright (c) 2010, 2015, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -244,7 +245,7 @@ void print_conclusions_csv(conclusions *con);
void generate_stats(conclusions *con, option_string *eng, stats *sptr);
uint parse_comma(const char *string, uint **range);
uint parse_delimiter(const char *script, statement **stmt, char delm);
-uint parse_option(const char *origin, option_string **stmt, char delm);
+int parse_option(const char *origin, option_string **stmt, char delm);
static int drop_schema(MYSQL *mysql, const char *db);
uint get_random_string(char *buf);
static statement *build_table_string(void);
@@ -1259,7 +1260,13 @@ get_options(int *argc,char ***argv)
if (num_int_cols_opt)
{
option_string *str;
- parse_option(num_int_cols_opt, &str, ',');
+ if(parse_option(num_int_cols_opt, &str, ',') == -1)
+ {
+ fprintf(stderr, "Invalid value specified for the option "
+ "'number-int-cols'\n");
+ option_cleanup(str);
+ return 1;
+ }
num_int_cols= atoi(str->string);
if (str->option)
num_int_cols_index= atoi(str->option);
@@ -1270,7 +1277,13 @@ get_options(int *argc,char ***argv)
if (num_char_cols_opt)
{
option_string *str;
- parse_option(num_char_cols_opt, &str, ',');
+ if(parse_option(num_char_cols_opt, &str, ',') == -1)
+ {
+ fprintf(stderr, "Invalid value specified for the option "
+ "'number-char-cols'\n");
+ option_cleanup(str);
+ return 1;
+ }
num_char_cols= atoi(str->string);
if (str->option)
num_char_cols_index= atoi(str->option);
@@ -1507,7 +1520,13 @@ get_options(int *argc,char ***argv)
printf("Parsing engines to use.\n");
if (default_engine)
- parse_option(default_engine, &engine_options, ',');
+ {
+ if(parse_option(default_engine, &engine_options, ',') == -1)
+ {
+ fprintf(stderr, "Invalid value specified for the option 'engine'\n");
+ return 1;
+ }
+ }
if (tty_password)
opt_password= get_tty_password(NullS);
@@ -1984,7 +2003,7 @@ end:
DBUG_RETURN(0);
}
-uint
+int
parse_option(const char *origin, option_string **stmt, char delm)
{
char *retstr;
@@ -2009,6 +2028,13 @@ parse_option(const char *origin, option_string **stmt, char delm)
char buffer[HUGE_STRING_LENGTH]= "";
char *buffer_ptr;
+ /*
+ Return an error if the length of the any of the comma seprated value
+ exceeds HUGE_STRING_LENGTH.
+ */
+ if ((size_t)(retstr - ptr) > HUGE_STRING_LENGTH)
+ return -1;
+
count++;
strncpy(buffer, ptr, (size_t)(retstr - ptr));
/*
@@ -2048,6 +2074,13 @@ parse_option(const char *origin, option_string **stmt, char delm)
{
char *origin_ptr;
+ /*
+ Return an error if the length of the any of the comma seprated value
+ exceeds HUGE_STRING_LENGTH.
+ */
+ if (strlen(ptr) > HUGE_STRING_LENGTH)
+ return -1;
+
if ((origin_ptr= strchr(ptr, ':')))
{
char *option_ptr;
@@ -2058,13 +2091,13 @@ parse_option(const char *origin, option_string **stmt, char delm)
option_ptr= (char *)ptr + 1 + tmp->length;
/* Move past the : and the first string */
- tmp->option_length= (size_t)((ptr + length) - option_ptr);
+ tmp->option_length= strlen(option_ptr);
tmp->option= my_strndup(option_ptr, tmp->option_length,
MYF(MY_FAE));
}
else
{
- tmp->length= (size_t)((ptr + length) - ptr);
+ tmp->length= strlen(ptr);
tmp->string= my_strndup(ptr, tmp->length, MYF(MY_FAE));
}
diff --git a/mysql-test/r/merge_recover.result b/mysql-test/r/myisam_recover.result
index 871c12ca4c0..0829c1e8b82 100644
--- a/mysql-test/r/merge_recover.result
+++ b/mysql-test/r/myisam_recover.result
@@ -1,5 +1,7 @@
#
-# Test of MyISAM MRG tables with corrupted children.
+# Tests for corrupted MyISAM tables and MyISAMMRG tables with corrupted
+# children..
+#
# Run with --myisam-recover=force option.
#
# Preparation: we need to make sure that the merge parent
@@ -44,20 +46,20 @@ drop procedure p_create;
# Switching to connection 'default'
#
#
-# We have to disable the ps-protocol, to avoid
+# We have to disable the ps-protocol, to avoid
# "Prepared statement needs to be re-prepared" errors
# -- table def versions change all the time with full table cache.
-#
+#
drop table if exists t1, t1_mrg, t1_copy;
#
# Prepare a MERGE engine table, that refers to a corrupted
# child.
-#
+#
create table t1 (a int, key(a)) engine=myisam;
create table t1_mrg (a int) union (t1) engine=merge;
#
# Create a table with a corrupted index file:
-# save an old index file, insert more rows,
+# save an old index file, insert more rows,
# overwrite the new index file with the old one.
#
insert into t1 (a) values (1), (2), (3);
@@ -101,3 +103,48 @@ execute stmt;
deallocate prepare stmt;
set @@global.table_definition_cache=default;
set @@global.table_open_cache=default;
+#
+# 18075170 - sql node restart required to avoid deadlock after
+# restore
+#
+# Check that auto-repair for MyISAM tables can now happen in the
+# middle of transaction, without aborting it.
+create table t1 (a int, key(a)) engine=myisam;
+create table t2 (a int);
+insert into t2 values (1);
+# Create a table with a corrupted index file:
+# save an old index file, insert more rows,
+# overwrite the new index file with the old one.
+insert into t1 (a) values (1);
+flush table t1;
+insert into t1 (a) values (4);
+flush table t1;
+# Check table is needed to mark the table as crashed.
+check table t1;
+Table Op Msg_type Msg_text
+test.t1 check warning Size of datafile is: 14 Should be: 7
+test.t1 check error Record-count is not ok; is 2 Should be: 1
+test.t1 check warning Found 2 key parts. Should be: 1
+test.t1 check error Corrupt
+# At this point we have a corrupt t1
+set autocommit = 0;
+select * from t2;
+a
+1
+# Without fix select from t1 will break the transaction. After the fix
+# transaction should be active and should hold lock on table t2. Alter
+# table from con2 will wait only if the transaction is not broken.
+select * from t1;
+a
+1
+4
+Warnings:
+Error 145 Table 't1' is marked as crashed and should be repaired
+Error 1194 Table 't1' is marked as crashed and should be repaired
+Error 1034 Number of rows changed from 1 to 2
+ALTER TABLE t2 ADD val INT;
+# With fix we should have alter table waiting for t2 lock here.
+ROLLBACK;
+SET autocommit = 1;
+# Cleanup
+drop table t1, t2;
diff --git a/mysql-test/suite/innodb/r/add_constraint.result b/mysql-test/suite/innodb/r/add_constraint.result
new file mode 100644
index 00000000000..cbbcb4e8fa8
--- /dev/null
+++ b/mysql-test/suite/innodb/r/add_constraint.result
@@ -0,0 +1,13 @@
+#
+# Bug #20762798 FK DDL: CRASH IN DICT_FOREIGN_REMOVE_FROM_CACHE
+#
+create table t1(a int, b int, key(a),key(b))engine=innodb;
+create table t2(a int, b int, key(a),key(b))engine=innodb;
+alter table t2 add constraint b foreign key (b) references t1(a);
+alter table t1 add constraint b1 foreign key (b) references t2(a);
+alter table t2 add constraint b1 foreign key (b) references t1(a);
+ERROR HY000: Can't create table '#sql-temporary' (errno: 121)
+alter table t2 drop foreign key b;
+alter table t1 drop foreign key b1;
+drop table t2;
+drop table t1;
diff --git a/mysql-test/suite/innodb/t/add_constraint.test b/mysql-test/suite/innodb/t/add_constraint.test
new file mode 100644
index 00000000000..eabf06434f4
--- /dev/null
+++ b/mysql-test/suite/innodb/t/add_constraint.test
@@ -0,0 +1,21 @@
+--source include/have_innodb.inc
+
+--echo #
+--echo # Bug #20762798 FK DDL: CRASH IN DICT_FOREIGN_REMOVE_FROM_CACHE
+--echo #
+
+create table t1(a int, b int, key(a),key(b))engine=innodb;
+create table t2(a int, b int, key(a),key(b))engine=innodb;
+
+alter table t2 add constraint b foreign key (b) references t1(a);
+alter table t1 add constraint b1 foreign key (b) references t2(a);
+
+--replace_regex /'[^']*test\.#sql-[0-9a-f_]*'/'#sql-temporary'/
+--error ER_CANT_CREATE_TABLE
+alter table t2 add constraint b1 foreign key (b) references t1(a);
+
+alter table t2 drop foreign key b;
+alter table t1 drop foreign key b1;
+
+drop table t2;
+drop table t1;
diff --git a/mysql-test/suite/ndb/r/ndb_restore_discover.result b/mysql-test/suite/ndb/r/ndb_restore_discover.result
new file mode 100644
index 00000000000..de10af87047
--- /dev/null
+++ b/mysql-test/suite/ndb/r/ndb_restore_discover.result
@@ -0,0 +1,33 @@
+#
+# 18075170 - sql node restart required to avoid deadlock after
+# restore
+#
+CREATE TABLE t1 (id INT) ENGINE=NDBCluster;
+CREATE TABLE t2 (id INT) ENGINE=NDBCluster;
+INSERT INTO t1 VALUES (1);
+INSERT INTO t2 VALUES (1);
+DROP TABLE t1;
+DROP TABLE t2;
+SET autocommit = 0;
+SELECT * FROM t1;
+id
+1
+SELECT * FROM t2;
+id
+1
+ROLLBACK;
+SET autocommit = 1;
+drop table t1;
+drop table t2;
+SET autocommit = 0;
+SELECT * FROM t1;
+id
+1
+SELECT * FROM t2;
+id
+1
+ALTER TABLE t1 ADD val INT;
+ROLLBACK;
+SET autocommit = 1;
+drop table t1;
+drop table t2;
diff --git a/mysql-test/suite/ndb/t/ndb_restore_discover.test b/mysql-test/suite/ndb/t/ndb_restore_discover.test
new file mode 100644
index 00000000000..6631c74d5c8
--- /dev/null
+++ b/mysql-test/suite/ndb/t/ndb_restore_discover.test
@@ -0,0 +1,70 @@
+-- source include/have_ndb.inc
+-- source include/count_sessions.inc
+
+--echo #
+--echo # 18075170 - sql node restart required to avoid deadlock after
+--echo # restore
+--echo #
+# Test Auto Discover option within a transaction
+# and make sure the transaction is not broken.
+CREATE TABLE t1 (id INT) ENGINE=NDBCluster;
+CREATE TABLE t2 (id INT) ENGINE=NDBCluster;
+
+INSERT INTO t1 VALUES (1);
+INSERT INTO t2 VALUES (1);
+
+-- source include/ndb_backup.inc
+
+DROP TABLE t1;
+DROP TABLE t2;
+
+-- source include/ndb_restore_master.inc
+
+SET autocommit = 0;
+SELECT * FROM t1;
+
+# Without fix below select was resulting in DEADLOCK error. With fix select
+# should succeed.
+SELECT * FROM t2;
+ROLLBACK;
+SET autocommit = 1;
+
+drop table t1;
+drop table t2;
+
+#
+# Checking lock preservation in transaction
+#
+# Using existing backup to create the scenario. Tables are deleted as part of
+# above test cleanup. Thus restoring the backup will bring the system to
+# required state.
+-- source include/ndb_restore_master.inc
+
+SET autocommit = 0;
+SELECT * FROM t1;
+SELECT * FROM t2;
+
+connect(con2, localhost, root);
+--SEND ALTER TABLE t1 ADD val INT
+
+connection default;
+# Alter from con2 will be in waiting state as there is a lock on t1 from
+# default connection due to active transaction. We check for this condition
+# then releasing the lock by rollbacking active transaction.
+let $wait_condition=
+ SELECT count(*) = 1 FROM information_schema.processlist WHERE state
+ LIKE "Waiting%" AND info = "ALTER TABLE t1 ADD val INT";
+--source include/wait_condition.inc
+ROLLBACK;
+SET autocommit = 1;
+
+connection con2;
+--REAP
+
+disconnect con2;
+connection default;
+drop table t1;
+drop table t2;
+
+# Wait till all disconnects are completed
+-- source include/wait_until_count_sessions.inc
diff --git a/mysql-test/t/merge_recover-master.opt b/mysql-test/t/myisam_recover-master.opt
index 875a25ad513..875a25ad513 100644
--- a/mysql-test/t/merge_recover-master.opt
+++ b/mysql-test/t/myisam_recover-master.opt
diff --git a/mysql-test/t/merge_recover.test b/mysql-test/t/myisam_recover.test
index f2cb204eeb6..49fe9c33460 100644
--- a/mysql-test/t/merge_recover.test
+++ b/mysql-test/t/myisam_recover.test
@@ -1,5 +1,9 @@
+--source include/count_sessions.inc
+
+--echo #
+--echo # Tests for corrupted MyISAM tables and MyISAMMRG tables with corrupted
+--echo # children..
--echo #
---echo # Test of MyISAM MRG tables with corrupted children.
--echo # Run with --myisam-recover=force option.
--echo #
--echo # Preparation: we need to make sure that the merge parent
@@ -57,10 +61,10 @@ eval $lock;
--echo #
connection default;
--echo #
---echo # We have to disable the ps-protocol, to avoid
+--echo # We have to disable the ps-protocol, to avoid
--echo # "Prepared statement needs to be re-prepared" errors
--echo # -- table def versions change all the time with full table cache.
---echo #
+--echo #
--disable_ps_protocol
--disable_warnings
drop table if exists t1, t1_mrg, t1_copy;
@@ -69,12 +73,12 @@ let $MYSQLD_DATADIR=`select @@datadir`;
--echo #
--echo # Prepare a MERGE engine table, that refers to a corrupted
--echo # child.
---echo #
+--echo #
create table t1 (a int, key(a)) engine=myisam;
create table t1_mrg (a int) union (t1) engine=merge;
--echo #
--echo # Create a table with a corrupted index file:
---echo # save an old index file, insert more rows,
+--echo # save an old index file, insert more rows,
--echo # overwrite the new index file with the old one.
--echo #
insert into t1 (a) values (1), (2), (3);
@@ -111,3 +115,66 @@ set @@global.table_open_cache=default;
disconnect con1;
connection default;
--enable_ps_protocol
+
+--echo #
+--echo # 18075170 - sql node restart required to avoid deadlock after
+--echo # restore
+--echo #
+--echo # Check that auto-repair for MyISAM tables can now happen in the
+--echo # middle of transaction, without aborting it.
+--enable_prepare_warnings
+
+connection default;
+
+create table t1 (a int, key(a)) engine=myisam;
+create table t2 (a int);
+insert into t2 values (1);
+
+--echo # Create a table with a corrupted index file:
+--echo # save an old index file, insert more rows,
+--echo # overwrite the new index file with the old one.
+insert into t1 (a) values (1);
+flush table t1;
+--copy_file $MYSQLD_DATADIR/test/t1.MYI $MYSQLD_DATADIR/test/t1_copy.MYI
+insert into t1 (a) values (4);
+flush table t1;
+--remove_file $MYSQLD_DATADIR/test/t1.MYI
+--copy_file $MYSQLD_DATADIR/test/t1_copy.MYI $MYSQLD_DATADIR/test/t1.MYI
+--remove_file $MYSQLD_DATADIR/test/t1_copy.MYI
+
+--echo # Check table is needed to mark the table as crashed.
+check table t1;
+
+--echo # At this point we have a corrupt t1
+set autocommit = 0;
+select * from t2;
+--echo # Without fix select from t1 will break the transaction. After the fix
+--echo # transaction should be active and should hold lock on table t2. Alter
+--echo # table from con2 will wait only if the transaction is not broken.
+--replace_regex /'.*[\/\\]/'/
+select * from t1;
+
+connect(con2, localhost, root);
+--SEND ALTER TABLE t2 ADD val INT
+
+connection default;
+--echo # With fix we should have alter table waiting for t2 lock here.
+let $wait_condition=
+ SELECT count(*) = 1 FROM information_schema.processlist WHERE state
+ LIKE "Waiting%" AND info = "ALTER TABLE t2 ADD val INT";
+
+--source include/wait_condition.inc
+ROLLBACK;
+SET autocommit = 1;
+
+connection con2;
+--REAP
+
+connection default;
+disconnect con2;
+
+--echo # Cleanup
+drop table t1, t2;
+
+# Wait till all disconnects are completed
+-- source include/wait_until_count_sessions.inc
diff --git a/mysys/ptr_cmp.c b/mysys/ptr_cmp.c
index a481b4d961c..6e373e98972 100644
--- a/mysys/ptr_cmp.c
+++ b/mysys/ptr_cmp.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -91,6 +91,7 @@ static int ptr_compare(size_t *compare_length, uchar **a, uchar **b)
reg3 int length= *compare_length;
reg1 uchar *first,*last;
+ DBUG_ASSERT(length > 0);
first= *a; last= *b;
while (--length)
{
diff --git a/regex/regcomp.c b/regex/regcomp.c
index abc18174f56..b1074a1d79c 100644
--- a/regex/regcomp.c
+++ b/regex/regcomp.c
@@ -1,3 +1,11 @@
+/* Copyright 1992, 1993, 1994 Henry Spencer. All rights reserved.
+ See file COPYRIGHT for details.
+
+ This file was modified by Oracle on 2015-05-18 for 32-bit compatibility.
+
+ Modifications copyright (c) 2015, Oracle and/or its affiliates. All rights
+ reserved. */
+
#include <my_global.h>
#include <m_string.h>
#include <m_ctype.h>
@@ -133,12 +141,26 @@ CHARSET_INFO *charset;
} else
len = strlen((char *)pattern);
+ /*
+ Find the maximum len we can safely process
+ without a rollover and a mis-malloc.
+ p->ssize is a sopno is a long (32+ bit signed);
+ size_t is 16+ bit unsigned.
+ */
+ {
+ size_t new_ssize = len / (size_t)2 * (size_t)3 + (size_t)1; /* ugh */
+ if ((new_ssize < len) || /* size_t rolled over */
+ ((SIZE_T_MAX / sizeof(sop)) < new_ssize) || /* malloc arg */
+ (new_ssize > LONG_MAX)) /* won't fit in ssize */
+ return(REG_ESPACE); /* MY_REG_ESPACE or MY_REG_INVARG */
+ p->ssize = new_ssize;
+ }
+
/* do the mallocs early so failure handling is easy */
g = (struct re_guts *)malloc(sizeof(struct re_guts) +
(NC-1)*sizeof(cat_t));
if (g == NULL)
return(REG_ESPACE);
- p->ssize = (long) (len/(size_t)2*(size_t)3 + (size_t)1); /* ugh */
p->strip = (sop *)malloc(p->ssize * sizeof(sop));
p->slen = 0;
if (p->strip == NULL) {
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 384ab7a7322..4b088952551 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -4064,10 +4064,11 @@ request_backoff_action(enum_open_table_action action_arg,
* We met a broken table that needs repair, or a table that
is not present on this MySQL server and needs re-discovery.
To perform the action, we need an exclusive metadata lock on
- the table. Acquiring an X lock while holding other shared
- locks is very deadlock-prone. If this is a multi- statement
- transaction that holds metadata locks for completed
- statements, we don't do it, and report an error instead.
+ the table. Acquiring X lock while holding other shared
+ locks can easily lead to deadlocks. We rely on MDL deadlock
+ detector to discover them. If this is a multi-statement
+ transaction that holds metadata locks for completed statements,
+ we should keep these locks after discovery/repair.
The action type in this case is OT_DISCOVER or OT_REPAIR.
* Our attempt to acquire an MDL lock lead to a deadlock,
detected by the MDL deadlock detector. The current
@@ -4108,7 +4109,7 @@ request_backoff_action(enum_open_table_action action_arg,
keep tables open between statements and a livelock
is not possible.
*/
- if (action_arg != OT_REOPEN_TABLES && m_has_locks)
+ if (action_arg == OT_BACKOFF_AND_RETRY && m_has_locks)
{
my_error(ER_LOCK_DEADLOCK, MYF(0));
m_thd->mark_transaction_to_rollback(true);
@@ -4136,6 +4137,32 @@ request_backoff_action(enum_open_table_action action_arg,
/**
+ An error handler to mark transaction to rollback on DEADLOCK error
+ during DISCOVER / REPAIR.
+*/
+class MDL_deadlock_discovery_repair_handler : public Internal_error_handler
+{
+public:
+ virtual bool handle_condition(THD *thd,
+ uint sql_errno,
+ const char* sqlstate,
+ MYSQL_ERROR::enum_warning_level level,
+ const char* msg,
+ MYSQL_ERROR ** cond_hdl)
+ {
+ if (sql_errno == ER_LOCK_DEADLOCK)
+ {
+ thd->mark_transaction_to_rollback(true);
+ }
+ /*
+ We have marked this transaction to rollback. Return false to allow
+ error to be reported or handled by other handlers.
+ */
+ return false;
+ }
+};
+
+/**
Recover from failed attempt of open table by performing requested action.
@pre This function should be called only with "action" != OT_NO_ACTION
@@ -4150,6 +4177,12 @@ Open_table_context::
recover_from_failed_open()
{
bool result= FALSE;
+ MDL_deadlock_discovery_repair_handler handler;
+ /*
+ Install error handler to mark transaction to rollback on DEADLOCK error.
+ */
+ m_thd->push_internal_handler(&handler);
+
/* Execute the action. */
switch (m_action)
{
@@ -4171,7 +4204,12 @@ recover_from_failed_open()
m_thd->warning_info->clear_warning_info(m_thd->query_id);
m_thd->clear_error(); // Clear error message
- m_thd->mdl_context.release_transactional_locks();
+ /*
+ Rollback to start of the current statement to release exclusive lock
+ on table which was discovered but preserve locks from previous statements
+ in current transaction.
+ */
+ m_thd->mdl_context.rollback_to_savepoint(start_of_statement_svp());
break;
}
case OT_REPAIR:
@@ -4185,12 +4223,18 @@ recover_from_failed_open()
m_failed_table->table_name, FALSE);
result= auto_repair_table(m_thd, m_failed_table);
- m_thd->mdl_context.release_transactional_locks();
+ /*
+ Rollback to start of the current statement to release exclusive lock
+ on table which was discovered but preserve locks from previous statements
+ in current transaction.
+ */
+ m_thd->mdl_context.rollback_to_savepoint(start_of_statement_svp());
break;
}
default:
DBUG_ASSERT(0);
}
+ m_thd->pop_internal_handler();
/*
Reset the pointers to conflicting MDL request and the
TABLE_LIST element, set when we need auto-discovery or repair,
diff --git a/storage/innobase/dict/dict0dict.c b/storage/innobase/dict/dict0dict.c
index c803169dbd7..6693c731b6c 100644
--- a/storage/innobase/dict/dict0dict.c
+++ b/storage/innobase/dict/dict0dict.c
@@ -2534,8 +2534,9 @@ dict_foreign_remove_from_cache(
const ib_rbt_node_t* node
= rbt_lookup(rbt, foreign->id);
- if (node) {
- dict_foreign_t* val = *(dict_foreign_t**) node->value;
+ if (node != NULL) {
+ dict_foreign_t* val
+ = *(dict_foreign_t**) node->value;
if (val == foreign) {
rbt_delete(rbt, foreign->id);
@@ -2555,9 +2556,10 @@ dict_foreign_remove_from_cache(
if (rbt != NULL && foreign->id != NULL) {
const ib_rbt_node_t* node
= rbt_lookup(rbt, foreign->id);
- if (node) {
- dict_foreign_t* val = *(dict_foreign_t**) node->value;
+ if (node != NULL) {
+ dict_foreign_t* val
+ = *(dict_foreign_t**) node->value;
if (val == foreign) {
rbt_delete(rbt, foreign->id);
diff --git a/storage/innobase/include/row0purge.h b/storage/innobase/include/row0purge.h
index fa9c9291d5d..9a638c80493 100644
--- a/storage/innobase/include/row0purge.h
+++ b/storage/innobase/include/row0purge.h
@@ -1,6 +1,6 @@
/*****************************************************************************
-Copyright (c) 1997, 2009, Innobase Oy. All Rights Reserved.
+Copyright (c) 1997, 2015, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@@ -111,6 +111,17 @@ struct purge_node_struct{
purge of a row */
};
+#ifdef UNIV_DEBUG
+/***********************************************************//**
+Validate the persisent cursor in the purge node. The purge node has two
+references to the clustered index record - one via the ref member, and the
+other via the persistent cursor. These two references must match each
+other if the found_clust flag is set.
+@return true if the persistent cursor is consistent with the ref member.*/
+ibool
+row_purge_validate_pcur(purge_node_t* node);
+#endif /* UNIV_DEBUG */
+
#ifndef UNIV_NONINL
#include "row0purge.ic"
#endif
diff --git a/storage/innobase/os/os0file.c b/storage/innobase/os/os0file.c
index 8fc22fbd119..d792e7a61d8 100644
--- a/storage/innobase/os/os0file.c
+++ b/storage/innobase/os/os0file.c
@@ -1,6 +1,6 @@
/***********************************************************************
-Copyright (c) 1995, 2010, Innobase Oy. All Rights Reserved.
+Copyright (c) 1995, 2015, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2009, Percona Inc.
Portions of this file contain modifications contributed and copyrighted
@@ -1276,16 +1276,19 @@ os_file_create_simple_no_error_handling_func(
#else /* __WIN__ */
os_file_t file;
int create_flag;
+ const char* mode_str = NULL;
ut_a(name);
if (create_mode == OS_FILE_OPEN) {
+ mode_str = "OPEN";
if (access_type == OS_FILE_READ_ONLY) {
create_flag = O_RDONLY;
} else {
create_flag = O_RDWR;
}
} else if (create_mode == OS_FILE_CREATE) {
+ mode_str = "CREATE";
create_flag = O_RDWR | O_CREAT | O_EXCL;
} else {
create_flag = 0;
@@ -1310,6 +1313,14 @@ os_file_create_simple_no_error_handling_func(
#endif
} else {
*success = TRUE;
+
+ /* This function is always called for data files, we should
+ disable OS caching (O_DIRECT) here as we do in
+ os_file_create_func(), so we open the same file in the same
+ mode, see man page of open(2). */
+ if (srv_unix_file_flush_method == SRV_UNIX_O_DIRECT) {
+ os_file_set_nocache(file, name, mode_str);
+ }
}
return(file);
diff --git a/storage/innobase/row/row0purge.c b/storage/innobase/row/row0purge.c
index efcfdc3bac5..9018582f5d6 100644
--- a/storage/innobase/row/row0purge.c
+++ b/storage/innobase/row/row0purge.c
@@ -1,6 +1,6 @@
/*****************************************************************************
-Copyright (c) 1997, 2011, Oracle and/or its affiliates. All Rights Reserved.
+Copyright (c) 1997, 2015, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@@ -43,6 +43,7 @@ Created 3/14/1997 Heikki Tuuri
#include "row0vers.h"
#include "row0mysql.h"
#include "log0log.h"
+#include "rem0cmp.h"
/*************************************************************************
IMPORTANT NOTE: Any operation that generates redo MUST check that there
@@ -80,7 +81,7 @@ row_purge_node_create(
/***********************************************************//**
Repositions the pcur in the purge node on the clustered index record,
-if found.
+if found. If the record is not found, close pcur.
@return TRUE if the record was found */
static
ibool
@@ -90,23 +91,28 @@ row_purge_reposition_pcur(
purge_node_t* node, /*!< in: row purge node */
mtr_t* mtr) /*!< in: mtr */
{
- ibool found;
-
if (node->found_clust) {
- found = btr_pcur_restore_position(mode, &(node->pcur), mtr);
+ ut_ad(row_purge_validate_pcur(node));
- return(found);
- }
+ node->found_clust = btr_pcur_restore_position(
+ mode, &(node->pcur), mtr);
+
+ } else {
+
+ node->found_clust = row_search_on_row_ref(
+ &(node->pcur), mode, node->table, node->ref, mtr);
- found = row_search_on_row_ref(&(node->pcur), mode, node->table,
- node->ref, mtr);
- node->found_clust = found;
+ if (node->found_clust) {
+ btr_pcur_store_position(&(node->pcur), mtr);
+ }
+ }
- if (found) {
- btr_pcur_store_position(&(node->pcur), mtr);
+ /* Close the current cursor if we fail to position it correctly. */
+ if (!node->found_clust) {
+ btr_pcur_close(&node->pcur);
}
- return(found);
+ return(node->found_clust);
}
/***********************************************************//**
@@ -143,8 +149,8 @@ row_purge_remove_clust_if_poss_low(
if (!success) {
/* The record is already removed */
-
- btr_pcur_commit_specify_mtr(pcur, &mtr);
+ /* Persistent cursor is closed if reposition fails. */
+ mtr_commit(&mtr);
return(TRUE);
}
@@ -258,7 +264,12 @@ row_purge_poss_sec(
btr_pcur_get_rec(&node->pcur),
&mtr, index, entry);
- btr_pcur_commit_specify_mtr(&node->pcur, &mtr);
+ /* Persistent cursor is closed if reposition fails. */
+ if (node->found_clust) {
+ btr_pcur_commit_specify_mtr(&node->pcur, &mtr);
+ } else {
+ mtr_commit(&mtr);
+ }
return(can_delete);
}
@@ -806,3 +817,53 @@ row_purge_step(
return(thr);
}
+
+#ifdef UNIV_DEBUG
+/***********************************************************//**
+Validate the persisent cursor in the purge node. The purge node has two
+references to the clustered index record - one via the ref member, and the
+other via the persistent cursor. These two references must match each
+other if the found_clust flag is set.
+@return true if the stored copy of persistent cursor is consistent
+with the ref member.*/
+ibool
+row_purge_validate_pcur(
+ purge_node_t* node)
+{
+ dict_index_t* clust_index;
+ ulint* offsets;
+ int st;
+
+ if (!node->found_clust) {
+ return(TRUE);
+ }
+
+ if (node->index == NULL) {
+ return(TRUE);
+ }
+
+ if (node->pcur.old_stored != BTR_PCUR_OLD_STORED) {
+ return(TRUE);
+ }
+
+ clust_index = node->pcur.btr_cur.index;
+
+ offsets = rec_get_offsets(node->pcur.old_rec, clust_index, NULL,
+ node->pcur.old_n_fields, &node->heap);
+
+ /* Here we are comparing the purge ref record and the stored initial
+ part in persistent cursor. Both cases we store n_uniq fields of the
+ cluster index and so it is fine to do the comparison. We note this
+ dependency here as pcur and ref belong to different modules. */
+ st = cmp_dtuple_rec(node->ref, node->pcur.old_rec, offsets);
+
+ if (st != 0) {
+ fprintf(stderr, "Purge node pcur validation failed\n");
+ dtuple_print(stderr, node->ref);
+ rec_print(stderr, node->pcur.old_rec, clust_index);
+ return(FALSE);
+ }
+
+ return(TRUE);
+}
+#endif /* UNIV_DEBUG */