summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.bzr-mysql/default.conf6
-rw-r--r--VERSION2
-rw-r--r--client/mysql_plugin.c10
-rw-r--r--client/mysql_upgrade.c2
-rw-r--r--client/mysqltest.cc80
-rw-r--r--mysql-test/extra/binlog_tests/ctype_ucs_binlog.test1
-rw-r--r--mysql-test/r/alter_table-big.result44
-rw-r--r--mysql-test/r/mysql_upgrade.result33
-rw-r--r--mysql-test/r/mysqld--help-notwin.result3
-rw-r--r--mysql-test/r/mysqld--help-win.result3
-rw-r--r--mysql-test/r/mysqltest.result6
-rw-r--r--mysql-test/r/query_cache.result52
-rw-r--r--mysql-test/suite/innodb/r/innodb-lock.result17
-rw-r--r--mysql-test/suite/innodb/r/innodb_replace.result77
-rw-r--r--mysql-test/suite/innodb/t/innodb-lock.test41
-rw-r--r--mysql-test/suite/innodb/t/innodb_replace.test186
-rw-r--r--mysql-test/suite/rpl/r/rpl_cant_read_event_incident.result2
-rw-r--r--mysql-test/suite/rpl/r/rpl_log_pos.result1
-rw-r--r--mysql-test/suite/rpl/r/rpl_manual_change_index_file.result1
-rw-r--r--mysql-test/suite/rpl/r/rpl_packet.result1
-rw-r--r--mysql-test/suite/rpl/r/rpl_rotate_purge_deadlock.result30
-rw-r--r--mysql-test/suite/rpl/r/rpl_row_find_row_debug.result18
-rw-r--r--mysql-test/suite/rpl/t/rpl_cant_read_event_incident.test5
-rw-r--r--mysql-test/suite/rpl/t/rpl_log_pos.test7
-rw-r--r--mysql-test/suite/rpl/t/rpl_manual_change_index_file.test7
-rw-r--r--mysql-test/suite/rpl/t/rpl_packet.test7
-rw-r--r--mysql-test/suite/rpl/t/rpl_rotate_purge_deadlock-master.opt1
-rw-r--r--mysql-test/suite/rpl/t/rpl_rotate_purge_deadlock.test92
-rw-r--r--mysql-test/suite/rpl/t/rpl_row_find_row_debug.test62
-rw-r--r--mysql-test/suite/sys_vars/r/metadata_locks_cache_size_basic.result21
-rw-r--r--mysql-test/suite/sys_vars/t/metadata_locks_cache_size_basic-master.opt1
-rw-r--r--mysql-test/suite/sys_vars/t/metadata_locks_cache_size_basic.test25
-rw-r--r--mysql-test/t/alter_table-big.test106
-rw-r--r--mysql-test/t/disabled.def1
-rw-r--r--mysql-test/t/mysql_plugin.test3
-rw-r--r--mysql-test/t/mysql_upgrade.test21
-rw-r--r--mysql-test/t/mysqltest.test23
-rw-r--r--mysql-test/t/query_cache.test83
-rw-r--r--sql/item_func.cc18
-rw-r--r--sql/log.cc131
-rw-r--r--sql/log.h4
-rw-r--r--sql/log_event.cc64
-rw-r--r--sql/log_event.h1
-rw-r--r--sql/mdl.cc266
-rw-r--r--sql/mdl.h8
-rw-r--r--sql/rpl_injector.cc4
-rw-r--r--sql/rpl_rli.cc12
-rw-r--r--sql/rpl_rli.h41
-rw-r--r--sql/sql_class.h8
-rw-r--r--sql/sql_insert.cc5
-rw-r--r--sql/sql_parse.cc2
-rw-r--r--sql/sql_plist.h9
-rw-r--r--sql/sql_plugin.cc207
-rw-r--r--sql/sql_reload.cc2
-rw-r--r--sql/sql_repl.cc7
-rw-r--r--sql/sql_table.cc1
-rw-r--r--sql/sys_vars.cc6
-rw-r--r--storage/innobase/btr/btr0pcur.c2
-rw-r--r--storage/innobase/buf/buf0buf.c25
-rw-r--r--storage/innobase/handler/ha_innodb.cc43
-rw-r--r--storage/innobase/ibuf/ibuf0ibuf.c315
-rw-r--r--storage/innobase/include/btr0pcur.h8
-rw-r--r--storage/innobase/include/btr0pcur.ic32
-rw-r--r--storage/innobase/row/row0ins.c8
-rw-r--r--storage/innobase/row/row0mysql.c3
65 files changed, 1789 insertions, 524 deletions
diff --git a/.bzr-mysql/default.conf b/.bzr-mysql/default.conf
index 772f1d15b92..43d479a1043 100644
--- a/.bzr-mysql/default.conf
+++ b/.bzr-mysql/default.conf
@@ -1,4 +1,4 @@
[MYSQL]
-post_commit_to = "MYSQL_COMMITS_SECURITY_WW@ORACLE.COM"
-post_push_to = "MYSQL_COMMITS_SECURITY_WW@ORACLE.COM"
-tree_name = "mysql-5.5-security"
+post_commit_to = "commits@lists.mysql.com"
+post_push_to = "commits@lists.mysql.com"
+tree_name = "mysql-5.5"
diff --git a/VERSION b/VERSION
index 7bac52ecdfb..4eec6bf9d34 100644
--- a/VERSION
+++ b/VERSION
@@ -1,4 +1,4 @@
MYSQL_VERSION_MAJOR=5
MYSQL_VERSION_MINOR=5
-MYSQL_VERSION_PATCH=18
+MYSQL_VERSION_PATCH=19
MYSQL_VERSION_EXTRA=
diff --git a/client/mysql_plugin.c b/client/mysql_plugin.c
index 55f5ea0827e..fb2a031bb8e 100644
--- a/client/mysql_plugin.c
+++ b/client/mysql_plugin.c
@@ -929,19 +929,19 @@ static int check_access()
opt_datadir);
goto exit;
}
- if ((error= my_access(opt_plugin_ini, F_OK)))
+ if (opt_plugin_ini && (error= my_access(opt_plugin_ini, F_OK)))
{
fprintf(stderr, "ERROR: Cannot access plugin config file at '%s'.\n",
opt_plugin_ini);
goto exit;
}
- if ((error= my_access(opt_mysqld, F_OK)))
+ if (opt_mysqld && (error= my_access(opt_mysqld, F_OK)))
{
fprintf(stderr, "ERROR: Cannot access mysqld path '%s'.\n",
opt_mysqld);
goto exit;
}
- if ((error= my_access(opt_my_print_defaults, F_OK)))
+ if (opt_my_print_defaults && (error= my_access(opt_my_print_defaults, F_OK)))
{
fprintf(stderr, "ERROR: Cannot access my-print-defaults path '%s'.\n",
opt_my_print_defaults);
@@ -967,7 +967,7 @@ static int find_tool(const char *tool_name, char *tool_path)
int i= 0;
const char *paths[]= {
- opt_basedir, opt_mysqld, opt_my_print_defaults, "/usr",
+ opt_mysqld, opt_basedir, opt_my_print_defaults, "/usr",
"/usr/local/mysql", "/usr/sbin", "/usr/share", "/extra", "/extra/debug",
"/extra/release", "/bin", "/usr/bin", "/mysql/bin"
};
@@ -1124,7 +1124,7 @@ static int dump_bootstrap_file(char *bootstrap_file)
error= 1;
goto exit;
}
- printf("# Query: %s", query_str);
+ printf("# Query: %s\n", query_str);
exit:
if (file)
diff --git a/client/mysql_upgrade.c b/client/mysql_upgrade.c
index fb4be716df2..684d20c29a8 100644
--- a/client/mysql_upgrade.c
+++ b/client/mysql_upgrade.c
@@ -286,6 +286,8 @@ get_one_option(int optid, const struct my_option *opt,
case 'v': /* --verbose */
case 'f': /* --force */
+ case 's': /* --upgrade-system-tables */
+ case OPT_WRITE_BINLOG: /* --write-binlog */
add_option= FALSE;
break;
diff --git a/client/mysqltest.cc b/client/mysqltest.cc
index f0184e5297b..1bd5d5941e5 100644
--- a/client/mysqltest.cc
+++ b/client/mysqltest.cc
@@ -502,6 +502,31 @@ struct st_command *curr_command= 0;
char builtin_echo[FN_REFLEN];
+struct st_replace_regex
+{
+DYNAMIC_ARRAY regex_arr; /* stores a list of st_regex subsitutions */
+
+/*
+Temporary storage areas for substitutions. To reduce unnessary copying
+and memory freeing/allocation, we pre-allocate two buffers, and alternate
+their use, one for input/one for output, the roles changing on the next
+st_regex substition. At the end of substitutions buf points to the
+one containing the final result.
+*/
+char* buf;
+char* even_buf;
+char* odd_buf;
+int even_buf_len;
+int odd_buf_len;
+};
+
+struct st_replace_regex *glob_replace_regex= 0;
+
+struct st_replace;
+struct st_replace *glob_replace= 0;
+void replace_strings_append(struct st_replace *rep, DYNAMIC_STRING* ds,
+const char *from, int len);
+
static void cleanup_and_exit(int exit_code) __attribute__((noreturn));
void die(const char *fmt, ...)
@@ -531,6 +556,7 @@ void str_to_file2(const char *fname, char *str, int size, my_bool append);
void fix_win_paths(const char *val, int len);
const char *get_errname_from_code (uint error_code);
+int multi_reg_replace(struct st_replace_regex* r,char* val);
#ifdef __WIN__
void free_tmp_sh_file();
@@ -2432,7 +2458,23 @@ void var_query_set(VAR *var, const char *query, const char** query_end)
if (row[i])
{
/* Add column to tab separated string */
- dynstr_append_mem(&result, row[i], lengths[i]);
+ char *val= row[i];
+ int len= lengths[i];
+
+ if (glob_replace_regex)
+ {
+ /* Regex replace */
+ if (!multi_reg_replace(glob_replace_regex, (char*)val))
+ {
+ val= glob_replace_regex->buf;
+ len= strlen(val);
+ }
+ }
+
+ if (glob_replace)
+ replace_strings_append(glob_replace, &result, val, len);
+ else
+ dynstr_append_mem(&result, val, len);
}
dynstr_append_mem(&result, "\t", 1);
}
@@ -3344,8 +3386,9 @@ void do_copy_file(struct st_command *command)
' ');
DBUG_PRINT("info", ("Copy %s to %s", ds_from_file.str, ds_to_file.str));
+ /* MY_HOLD_ORIGINAL_MODES prevents attempts to chown the file */
error= (my_copy(ds_from_file.str, ds_to_file.str,
- MYF(MY_DONT_OVERWRITE_FILE)) != 0);
+ MYF(MY_DONT_OVERWRITE_FILE | MY_HOLD_ORIGINAL_MODES)) != 0);
handle_command_error(command, error);
dynstr_free(&ds_from_file);
dynstr_free(&ds_to_file);
@@ -9119,16 +9162,11 @@ typedef struct st_pointer_array { /* when using array-strings */
uint array_allocs,max_count,length,max_length;
} POINTER_ARRAY;
-struct st_replace;
struct st_replace *init_replace(char * *from, char * *to, uint count,
char * word_end_chars);
int insert_pointer_name(reg1 POINTER_ARRAY *pa,char * name);
-void replace_strings_append(struct st_replace *rep, DYNAMIC_STRING* ds,
- const char *from, int len);
void free_pointer_array(POINTER_ARRAY *pa);
-struct st_replace *glob_replace;
-
/*
Get arguments for replace. The syntax is:
replace from to [from to ...]
@@ -9272,26 +9310,6 @@ struct st_regex
int icase; /* true if the match is case insensitive */
};
-struct st_replace_regex
-{
- DYNAMIC_ARRAY regex_arr; /* stores a list of st_regex subsitutions */
-
- /*
- Temporary storage areas for substitutions. To reduce unnessary copying
- and memory freeing/allocation, we pre-allocate two buffers, and alternate
- their use, one for input/one for output, the roles changing on the next
- st_regex substition. At the end of substitutions buf points to the
- one containing the final result.
- */
- char* buf;
- char* even_buf;
- char* odd_buf;
- int even_buf_len;
- int odd_buf_len;
-};
-
-struct st_replace_regex *glob_replace_regex= 0;
-
int reg_replace(char** buf_p, int* buf_len_p, char *pattern, char *replace,
char *string, int icase);
@@ -9490,7 +9508,13 @@ void do_get_replace_regex(struct st_command *command)
{
char *expr= command->first_argument;
free_replace_regex();
- if (!(glob_replace_regex=init_replace_regex(expr)))
+ /* Allow variable for the *entire* list of replacements */
+ if (*expr == '$')
+ {
+ VAR *val= var_get(expr, NULL, 0, 1);
+ expr= val ? val->str_val : NULL;
+ }
+ if (expr && *expr && !(glob_replace_regex=init_replace_regex(expr)))
die("Could not init replace_regex");
command->last_argument= command->end;
}
diff --git a/mysql-test/extra/binlog_tests/ctype_ucs_binlog.test b/mysql-test/extra/binlog_tests/ctype_ucs_binlog.test
index b240109f6e6..733ad05b0be 100644
--- a/mysql-test/extra/binlog_tests/ctype_ucs_binlog.test
+++ b/mysql-test/extra/binlog_tests/ctype_ucs_binlog.test
@@ -15,7 +15,6 @@ source include/show_binlog_events.inc;
# absolutely need variables names to be quoted and strings to be
# escaped).
flush logs;
---replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR
let $MYSQLD_DATADIR= `select @@datadir`;
--exec $MYSQL_BINLOG --short-form $MYSQLD_DATADIR/master-bin.000001
drop table t2;
diff --git a/mysql-test/r/alter_table-big.result b/mysql-test/r/alter_table-big.result
index d6b936bd5d7..33af60938a1 100644
--- a/mysql-test/r/alter_table-big.result
+++ b/mysql-test/r/alter_table-big.result
@@ -1,57 +1,77 @@
drop table if exists t1, t2;
+set debug_sync='RESET';
create table t1 (n1 int, n2 int, n3 int,
key (n1, n2, n3),
key (n2, n3, n1),
key (n3, n1, n2));
create table t2 (i int);
alter table t1 disable keys;
-insert into t1 values (RAND()*1000, RAND()*1000, RAND()*1000);
+insert into t1 values (1, 2, 3);
reset master;
-set session debug="+d,sleep_alter_enable_indexes";
+set debug_sync='alter_table_enable_indexes SIGNAL parked WAIT_FOR go';
alter table t1 enable keys;;
+set debug_sync='now WAIT_FOR parked';
insert into t2 values (1);
-insert into t1 values (1, 1, 1);
-set session debug="-d,sleep_alter_enable_indexes";
+insert into t1 values (1, 1, 1);;
+set debug_sync='now SIGNAL go';
show binlog events from <binlog_start>;
Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000001 # Query # # BEGIN
master-bin.000001 # Query # # use `test`; insert into t2 values (1)
+master-bin.000001 # Query # # COMMIT
master-bin.000001 # Query # # use `test`; alter table t1 enable keys
+master-bin.000001 # Query # # BEGIN
master-bin.000001 # Query # # use `test`; insert into t1 values (1, 1, 1)
+master-bin.000001 # Query # # COMMIT
drop tables t1, t2;
+set debug_sync='RESET';
End of 5.0 tests
drop table if exists t1, t2, t3;
create table t1 (i int);
reset master;
-set session debug="+d,sleep_alter_before_main_binlog";
+set debug_sync='alter_table_before_main_binlog SIGNAL parked WAIT_FOR go';
alter table t1 change i c char(10) default 'Test1';;
-insert into t1 values ();
+set debug_sync='now WAIT_FOR parked';
+insert into t1 values ();;
+set debug_sync='now SIGNAL go';
select * from t1;
c
Test1
+set debug_sync='alter_table_before_main_binlog SIGNAL parked WAIT_FOR go';
alter table t1 change c vc varchar(100) default 'Test2';;
-rename table t1 to t2;
+set debug_sync='now WAIT_FOR parked';
+rename table t1 to t2;;
+set debug_sync='now SIGNAL go';
drop table t2;
create table t1 (i int);
+set debug_sync='alter_table_before_main_binlog SIGNAL parked WAIT_FOR go';
alter table t1 change i c char(10) default 'Test3', rename to t2;;
-insert into t2 values ();
+set debug_sync='now WAIT_FOR parked';
+insert into t2 values();;
+set debug_sync='now SIGNAL go';
select * from t2;
c
Test3
alter table t2 change c vc varchar(100) default 'Test2', rename to t1;;
rename table t1 to t3;
drop table t3;
-set session debug="-d,sleep_alter_before_main_binlog";
+set debug_sync='alter_table_before_main_binlog SIGNAL parked WAIT_FOR go';
+set debug_sync='RESET';
show binlog events from <binlog_start>;
Log_name Pos Event_type Server_id End_log_pos Info
master-bin.000001 # Query # # use `test`; alter table t1 change i c char(10) default 'Test1'
+master-bin.000001 # Query # # BEGIN
master-bin.000001 # Query # # use `test`; insert into t1 values ()
+master-bin.000001 # Query # # COMMIT
master-bin.000001 # Query # # use `test`; alter table t1 change c vc varchar(100) default 'Test2'
master-bin.000001 # Query # # use `test`; rename table t1 to t2
-master-bin.000001 # Query # # use `test`; drop table t2
+master-bin.000001 # Query # # use `test`; DROP TABLE `t2` /* generated by server */
master-bin.000001 # Query # # use `test`; create table t1 (i int)
master-bin.000001 # Query # # use `test`; alter table t1 change i c char(10) default 'Test3', rename to t2
-master-bin.000001 # Query # # use `test`; insert into t2 values ()
+master-bin.000001 # Query # # BEGIN
+master-bin.000001 # Query # # use `test`; insert into t2 values()
+master-bin.000001 # Query # # COMMIT
master-bin.000001 # Query # # use `test`; alter table t2 change c vc varchar(100) default 'Test2', rename to t1
master-bin.000001 # Query # # use `test`; rename table t1 to t3
-master-bin.000001 # Query # # use `test`; drop table t3
+master-bin.000001 # Query # # use `test`; DROP TABLE `t3` /* generated by server */
End of 5.1 tests
diff --git a/mysql-test/r/mysql_upgrade.result b/mysql-test/r/mysql_upgrade.result
index e36b4781ddf..8ce3f426375 100644
--- a/mysql-test/r/mysql_upgrade.result
+++ b/mysql-test/r/mysql_upgrade.result
@@ -194,3 +194,36 @@ GRANT ALL PRIVILEGES ON `roelt`.`test2` TO 'user3'@'%'
DROP USER 'user3'@'%';
End of 5.1 tests
The --upgrade-system-tables option was used, databases won't be touched.
+#
+# Bug#11827359 60223: MYSQL_UPGRADE PROBLEM WITH OPTION
+# SKIP-WRITE-BINLOG
+#
+# Droping the previously created mysql_upgrade_info file..
+# Running mysql_upgrade with --skip-write-binlog..
+mtr.global_suppressions OK
+mtr.test_suppressions OK
+mysql.columns_priv OK
+mysql.db OK
+mysql.event OK
+mysql.func OK
+mysql.general_log OK
+mysql.help_category OK
+mysql.help_keyword OK
+mysql.help_relation OK
+mysql.help_topic OK
+mysql.host OK
+mysql.ndb_binlog_index OK
+mysql.plugin OK
+mysql.proc OK
+mysql.procs_priv OK
+mysql.proxies_priv OK
+mysql.servers OK
+mysql.slow_log OK
+mysql.tables_priv OK
+mysql.time_zone OK
+mysql.time_zone_leap_second OK
+mysql.time_zone_name OK
+mysql.time_zone_transition OK
+mysql.time_zone_transition_type OK
+mysql.user OK
+End of tests
diff --git a/mysql-test/r/mysqld--help-notwin.result b/mysql-test/r/mysqld--help-notwin.result
index b4faa833c1b..cb727984ec0 100644
--- a/mysql-test/r/mysqld--help-notwin.result
+++ b/mysql-test/r/mysqld--help-notwin.result
@@ -336,6 +336,8 @@ The following options may be given as the first argument:
After this many write locks, allow some read locks to run
in between
--memlock Lock mysqld in memory.
+ --metadata-locks-cache-size=#
+ Size of unused metadata locks cache
--min-examined-row-limit=#
Don't write queries to slow log that examine fewer rows
than that
@@ -844,6 +846,7 @@ max-tmp-tables 32
max-user-connections 0
max-write-lock-count 18446744073709551615
memlock FALSE
+metadata-locks-cache-size 1024
min-examined-row-limit 0
multi-range-count 256
myisam-block-size 1024
diff --git a/mysql-test/r/mysqld--help-win.result b/mysql-test/r/mysqld--help-win.result
index 361d30620f7..38235e1a093 100644
--- a/mysql-test/r/mysqld--help-win.result
+++ b/mysql-test/r/mysqld--help-win.result
@@ -335,6 +335,8 @@ The following options may be given as the first argument:
After this many write locks, allow some read locks to run
in between
--memlock Lock mysqld in memory.
+ --metadata-locks-cache-size=#
+ Size of unused metadata locks cache
--min-examined-row-limit=#
Don't write queries to slow log that examine fewer rows
than that
@@ -847,6 +849,7 @@ max-tmp-tables 32
max-user-connections 0
max-write-lock-count 18446744073709551615
memlock FALSE
+metadata-locks-cache-size 1024
min-examined-row-limit 0
multi-range-count 256
myisam-block-size 1024
diff --git a/mysql-test/r/mysqltest.result b/mysql-test/r/mysqltest.result
index a0456c65f53..82069ebedaf 100644
--- a/mysql-test/r/mysqltest.result
+++ b/mysql-test/r/mysqltest.result
@@ -549,6 +549,7 @@ mysqltest: At line 1: Wrong column number to replace_column in 'replace_column 1
select "LONG_STRING" as x;
x
LONG_STRING
+dog
mysqltest: At line 1: Invalid integer argument "10!"
mysqltest: At line 1: Invalid integer argument "a"
mysqltest: At line 1: Missing required argument 'connection name' to command 'connect'
@@ -662,6 +663,11 @@ a D
1 1
1 4
drop table t1;
+y
+txt
+b is b and more is more
+txt
+a is a and less is more
create table t2 ( a char(10));
garbage;
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'garbage' at line 1
diff --git a/mysql-test/r/query_cache.result b/mysql-test/r/query_cache.result
index f209e401764..8132f3e9e0a 100644
--- a/mysql-test/r/query_cache.result
+++ b/mysql-test/r/query_cache.result
@@ -1583,6 +1583,58 @@ show status like 'Qcache_free_blocks';
Variable_name Value
Qcache_free_blocks 0
Restore default values.
+drop database if exists db1;
+drop database if exists db2;
+set GLOBAL query_cache_size=15*1024*1024;
+create database db1;
+use db1;
+create table t1(c1 int)engine=myisam;
+insert into t1(c1) values (1);
+select * from db1.t1 f;
+c1
+1
+show status like 'Qcache_queries_in_cache';
+Variable_name Value
+Qcache_queries_in_cache 1
+create database db2;
+rename table db1.t1 to db2.t2;
+drop database db1;
+show status like 'Qcache_queries_in_cache';
+Variable_name Value
+Qcache_queries_in_cache 0
+drop database db2;
+set global query_cache_size=default;
+drop database if exists db1;
+drop database if exists db3;
+set GLOBAL query_cache_size=15*1024*1024;
+create database db1;
+create database db3;
+use db1;
+create table t1(c1 int) engine=myisam;
+use db3;
+create table t1(c1 int) engine=myisam;
+use db1;
+insert into t1(c1) values (1);
+use test;
+select * from db1.t1;
+c1
+1
+select c1+1 from db1.t1;
+c1+1
+2
+select * from db3.t1;
+c1
+show status like 'Qcache_queries_in_cache';
+Variable_name Value
+Qcache_queries_in_cache 3
+create database db2;
+rename table db1.t1 to db2.t2;
+drop database db1;
+show status like 'Qcache_queries_in_cache';
+Variable_name Value
+Qcache_queries_in_cache 1
+drop database db2;
+drop database db3;
set GLOBAL query_cache_type=default;
set GLOBAL query_cache_limit=default;
set GLOBAL query_cache_min_res_unit=default;
diff --git a/mysql-test/suite/innodb/r/innodb-lock.result b/mysql-test/suite/innodb/r/innodb-lock.result
index 41f308788a2..439a8d6513c 100644
--- a/mysql-test/suite/innodb/r/innodb-lock.result
+++ b/mysql-test/suite/innodb/r/innodb-lock.result
@@ -89,3 +89,20 @@ commit;
# Connection 'con1'.
commit;
drop table t1;
+#
+#Bug#12842206 INNODB LOCKING REGRESSION FOR INSERT IGNORE
+#fixed by re-fixing Bug#7975
+#aka Bug#11759688 52020: InnoDB can still deadlock on just INSERT...
+#
+CREATE TABLE t1 (a INT PRIMARY KEY, b INT NOT NULL) ENGINE=InnoDB;
+INSERT INTO t1 VALUES(3,1);
+BEGIN;
+INSERT IGNORE INTO t1 VALUES(3,14);
+BEGIN;
+INSERT IGNORE INTO t1 VALUES(3,23);
+SELECT * FROM t1 FOR UPDATE;
+COMMIT;
+a b
+3 1
+COMMIT;
+DROP TABLE t1;
diff --git a/mysql-test/suite/innodb/r/innodb_replace.result b/mysql-test/suite/innodb/r/innodb_replace.result
new file mode 100644
index 00000000000..30009b8ddc9
--- /dev/null
+++ b/mysql-test/suite/innodb/r/innodb_replace.result
@@ -0,0 +1,77 @@
+#
+#Bug#11759688 52020: InnoDB can still deadlock
+#on just INSERT...ON DUPLICATE KEY
+#a.k.a. Bug#7975 deadlock without any locking, simple select and update
+#
+CREATE TABLE t1 (a INT PRIMARY KEY, b INT NOT NULL) ENGINE=InnoDB;
+INSERT INTO t1 VALUES(3,1);
+BEGIN;
+SET DEBUG_SYNC='write_row_noreplace SIGNAL insert1 WAIT_FOR select1';
+INSERT INTO t1 VALUES(3,2);
+SET DEBUG_SYNC='now WAIT_FOR insert1';
+SELECT * FROM t1 LOCK IN SHARE MODE;
+a b
+3 1
+SELECT * FROM t1 FOR UPDATE;
+SET DEBUG_SYNC='now SIGNAL select1';
+ERROR 23000: Duplicate entry '3' for key 'PRIMARY'
+INSERT INTO t1 VALUES(3,3) ON DUPLICATE KEY UPDATE b=b+10;
+ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
+COMMIT;
+SET DEBUG_SYNC='write_row_replace SIGNAL insert2 WAIT_FOR select2';
+REPLACE INTO t1 VALUES(3,4);
+SET DEBUG_SYNC='now WAIT_FOR insert2';
+SELECT * FROM t1;
+a b
+3 11
+SELECT * FROM t1 LOCK IN SHARE MODE;
+SET DEBUG_SYNC='now SIGNAL select2';
+SET DEBUG_SYNC='write_row_replace SIGNAL insert3 WAIT_FOR select3';
+INSERT INTO t1 VALUES(3,5) ON DUPLICATE KEY UPDATE b=b+20;
+a b
+3 4
+SET DEBUG_SYNC='now WAIT_FOR insert3';
+SELECT b FROM t1 LOCK IN SHARE MODE;
+SET DEBUG_SYNC='now SIGNAL select3';
+b
+24
+SET DEBUG_SYNC='write_row_noreplace SIGNAL insert4 WAIT_FOR select4';
+LOAD DATA INFILE '../../std_data/loaddata5.dat' INTO TABLE t1 FIELDS TERMINATED BY '' ENCLOSED BY '' (a, b);
+SET DEBUG_SYNC='now WAIT_FOR insert4';
+SELECT b FROM t1 WHERE a=3 LOCK IN SHARE MODE;
+b
+24
+SELECT b FROM t1 WHERE a=3 FOR UPDATE;
+SET DEBUG_SYNC='now SIGNAL select4';
+b
+24
+ERROR 23000: Duplicate entry '3' for key 'PRIMARY'
+SET DEBUG_SYNC='write_row_noreplace SIGNAL insert5 WAIT_FOR select5';
+LOAD DATA INFILE '../../std_data/loaddata5.dat' IGNORE INTO TABLE t1 FIELDS TERMINATED BY '' ENCLOSED BY '' (a, b);
+SET DEBUG_SYNC='now WAIT_FOR insert5';
+SELECT * FROM t1;
+a b
+3 24
+SELECT * FROM t1 WHERE a=3 LOCK IN SHARE MODE;
+a b
+3 24
+SELECT * FROM t1 WHERE a=3 FOR UPDATE;
+SET DEBUG_SYNC='now SIGNAL select5';
+a b
+3 24
+SET DEBUG_SYNC='write_row_replace SIGNAL insert6 WAIT_FOR select6';
+LOAD DATA INFILE '../../std_data/loaddata5.dat' REPLACE INTO TABLE t1 FIELDS TERMINATED BY '' ENCLOSED BY '' (a, b);
+SET DEBUG_SYNC='now WAIT_FOR insert6';
+SELECT * FROM t1;
+a b
+1 2
+3 24
+5 6
+SELECT a,b FROM t1 LOCK IN SHARE MODE;
+SET DEBUG_SYNC='now SIGNAL select6';
+a b
+1 2
+3 4
+5 6
+SET DEBUG_SYNC='RESET';
+DROP TABLE t1;
diff --git a/mysql-test/suite/innodb/t/innodb-lock.test b/mysql-test/suite/innodb/t/innodb-lock.test
index 96f555e3ecc..4d8d3d542d5 100644
--- a/mysql-test/suite/innodb/t/innodb-lock.test
+++ b/mysql-test/suite/innodb/t/innodb-lock.test
@@ -127,3 +127,44 @@ commit;
drop table t1;
# End of 4.1 tests
+
+--echo #
+--echo #Bug#12842206 INNODB LOCKING REGRESSION FOR INSERT IGNORE
+--echo #fixed by re-fixing Bug#7975
+--echo #aka Bug#11759688 52020: InnoDB can still deadlock on just INSERT...
+--echo #
+
+connection default;
+CREATE TABLE t1 (a INT PRIMARY KEY, b INT NOT NULL) ENGINE=InnoDB;
+
+INSERT INTO t1 VALUES(3,1);
+
+BEGIN;
+# this used to wrongly acquire an X lock; now it takes an S lock
+INSERT IGNORE INTO t1 VALUES(3,14);
+
+connection con1;
+BEGIN;
+# this used to wrongly acquire an X lock; now it takes an S lock
+INSERT IGNORE INTO t1 VALUES(3,23);
+--send
+SELECT * FROM t1 FOR UPDATE;
+
+connection con2;
+# Check that the above SELECT is blocked
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = 'Sending data' and
+ info = 'SELECT * FROM t1 FOR UPDATE';
+--source include/wait_condition.inc
+
+connection default;
+COMMIT;
+connection con1;
+reap;
+COMMIT;
+disconnect con1;
+disconnect con2;
+
+connection default;
+DROP TABLE t1;
diff --git a/mysql-test/suite/innodb/t/innodb_replace.test b/mysql-test/suite/innodb/t/innodb_replace.test
new file mode 100644
index 00000000000..a35f423c85e
--- /dev/null
+++ b/mysql-test/suite/innodb/t/innodb_replace.test
@@ -0,0 +1,186 @@
+--source include/have_innodb.inc
+--source include/have_debug_sync.inc
+
+--echo #
+--echo #Bug#11759688 52020: InnoDB can still deadlock
+--echo #on just INSERT...ON DUPLICATE KEY
+--echo #a.k.a. Bug#7975 deadlock without any locking, simple select and update
+--echo #
+
+CREATE TABLE t1 (a INT PRIMARY KEY, b INT NOT NULL) ENGINE=InnoDB;
+
+INSERT INTO t1 VALUES(3,1);
+
+connect (con1,localhost,root,,);
+connect (con2,localhost,root,,);
+connection con1;
+
+BEGIN;
+# normal INSERT of a duplicate should only S-lock the existing record (3,1)
+SET DEBUG_SYNC='write_row_noreplace SIGNAL insert1 WAIT_FOR select1';
+--send
+INSERT INTO t1 VALUES(3,2);
+
+connection default;
+SET DEBUG_SYNC='now WAIT_FOR insert1';
+# this should S-lock (3,1); no conflict
+SELECT * FROM t1 LOCK IN SHARE MODE;
+# this should X-lock (3,1), conflicting with con1
+--send
+SELECT * FROM t1 FOR UPDATE;
+
+connection con2;
+# Check that the above SELECT is blocked
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = 'Sending data' and
+ info = 'SELECT * FROM t1 FOR UPDATE';
+--source include/wait_condition.inc
+SET DEBUG_SYNC='now SIGNAL select1';
+
+connection con1;
+--error ER_DUP_ENTRY
+reap;
+# We are still holding an S-lock on (3,1) after the failed INSERT.
+# The following will upgrade it to an X-lock, causing a deadlock.
+# InnoDB should resolve the deadlock by aborting the blocked SELECT.
+INSERT INTO t1 VALUES(3,3) ON DUPLICATE KEY UPDATE b=b+10;
+
+connection default;
+--error ER_LOCK_DEADLOCK
+reap;
+connection con1;
+COMMIT;
+
+SET DEBUG_SYNC='write_row_replace SIGNAL insert2 WAIT_FOR select2';
+--send
+REPLACE INTO t1 VALUES(3,4);
+
+connection default;
+SET DEBUG_SYNC='now WAIT_FOR insert2';
+SELECT * FROM t1;
+--send
+SELECT * FROM t1 LOCK IN SHARE MODE;
+
+connection con2;
+# Check that the above SELECT is blocked because of X lock.
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = 'Sending data' and
+ info = 'SELECT * FROM t1 LOCK IN SHARE MODE';
+--source include/wait_condition.inc
+SET DEBUG_SYNC='now SIGNAL select2';
+
+connection con1;
+reap;
+
+SET DEBUG_SYNC='write_row_replace SIGNAL insert3 WAIT_FOR select3';
+--send
+INSERT INTO t1 VALUES(3,5) ON DUPLICATE KEY UPDATE b=b+20;
+
+connection default;
+reap;
+SET DEBUG_SYNC='now WAIT_FOR insert3';
+--send
+SELECT b FROM t1 LOCK IN SHARE MODE;
+
+connection con2;
+# Check that the above SELECT is blocked because of X lock.
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = 'Sending data' and
+ info = 'SELECT b FROM t1 LOCK IN SHARE MODE';
+--source include/wait_condition.inc
+SET DEBUG_SYNC='now SIGNAL select3';
+
+connection default;
+reap;
+
+connection con1;
+reap;
+SET DEBUG_SYNC='write_row_noreplace SIGNAL insert4 WAIT_FOR select4';
+--send
+LOAD DATA INFILE '../../std_data/loaddata5.dat' INTO TABLE t1 FIELDS TERMINATED BY '' ENCLOSED BY '' (a, b);
+
+connection default;
+SET DEBUG_SYNC='now WAIT_FOR insert4';
+# this should S-lock (3,1); no conflict
+SELECT b FROM t1 WHERE a=3 LOCK IN SHARE MODE;
+# this should X-lock (3,1), conflicting with con1
+--send
+SELECT b FROM t1 WHERE a=3 FOR UPDATE;
+
+connection con2;
+# Check that the above SELECT is blocked
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = 'statistics' and
+ info = 'SELECT b FROM t1 WHERE a=3 FOR UPDATE';
+--source include/wait_condition.inc
+SET DEBUG_SYNC='now SIGNAL select4';
+
+connection default;
+reap;
+
+connection con1;
+--error ER_DUP_ENTRY
+reap;
+SET DEBUG_SYNC='write_row_noreplace SIGNAL insert5 WAIT_FOR select5';
+--send
+LOAD DATA INFILE '../../std_data/loaddata5.dat' IGNORE INTO TABLE t1 FIELDS TERMINATED BY '' ENCLOSED BY '' (a, b);
+
+connection default;
+SET DEBUG_SYNC='now WAIT_FOR insert5';
+SELECT * FROM t1;
+# this should S-lock; no conflict
+SELECT * FROM t1 WHERE a=3 LOCK IN SHARE MODE;
+# this should X-lock, conflicting with the S-lock of the IGNORE in con1
+--send
+SELECT * FROM t1 WHERE a=3 FOR UPDATE;
+
+connection con2;
+# Check that the above SELECT is blocked
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = 'statistics' and
+ info = 'SELECT * FROM t1 WHERE a=3 FOR UPDATE';
+--source include/wait_condition.inc
+SET DEBUG_SYNC='now SIGNAL select5';
+
+connection con1;
+reap;
+connection default;
+reap;
+
+connection con1;
+SET DEBUG_SYNC='write_row_replace SIGNAL insert6 WAIT_FOR select6';
+--send
+LOAD DATA INFILE '../../std_data/loaddata5.dat' REPLACE INTO TABLE t1 FIELDS TERMINATED BY '' ENCLOSED BY '' (a, b);
+
+connection default;
+SET DEBUG_SYNC='now WAIT_FOR insert6';
+SELECT * FROM t1;
+# this should conflict with the X-lock acquired by the REPLACE
+--send
+SELECT a,b FROM t1 LOCK IN SHARE MODE;
+
+connection con2;
+# Check that the above SELECT is blocked
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = 'Sending data' and
+ info = 'SELECT a,b FROM t1 LOCK IN SHARE MODE';
+--source include/wait_condition.inc
+SET DEBUG_SYNC='now SIGNAL select6';
+
+connection con1;
+reap;
+connection default;
+reap;
+
+disconnect con1;
+disconnect con2;
+
+connection default;
+SET DEBUG_SYNC='RESET';
+DROP TABLE t1;
diff --git a/mysql-test/suite/rpl/r/rpl_cant_read_event_incident.result b/mysql-test/suite/rpl/r/rpl_cant_read_event_incident.result
index 1bee6f2ec1a..c1b2c6e3195 100644
--- a/mysql-test/suite/rpl/r/rpl_cant_read_event_incident.result
+++ b/mysql-test/suite/rpl/r/rpl_cant_read_event_incident.result
@@ -11,7 +11,7 @@ reset slave;
start slave;
include/wait_for_slave_param.inc [Last_IO_Errno]
Last_IO_Errno = '1236'
-Last_IO_Error = 'Got fatal error 1236 from master when reading data from binary log: 'binlog truncated in the middle of event; consider out of disk space on master; the last event was read from 'master-bin.000001' at 316, the last byte read was read from 'master-bin.000001' at 335.''
+Last_IO_Error = 'Got fatal error 1236 from master when reading data from binary log: 'binlog truncated in the middle of event; consider out of disk space on master; the last event was read from './master-bin.000001' at 316, the last byte read was read from './master-bin.000001' at 335.''
reset master;
stop slave;
reset slave;
diff --git a/mysql-test/suite/rpl/r/rpl_log_pos.result b/mysql-test/suite/rpl/r/rpl_log_pos.result
index 5e9c096d773..42b88711f03 100644
--- a/mysql-test/suite/rpl/r/rpl_log_pos.result
+++ b/mysql-test/suite/rpl/r/rpl_log_pos.result
@@ -9,7 +9,6 @@ change master to master_log_pos=MASTER_LOG_POS;
Read_Master_Log_Pos = '75'
start slave;
include/wait_for_slave_io_error.inc [errno=1236]
-Last_IO_Error = 'Got fatal error 1236 from master when reading data from binary log: 'log event entry exceeded max_allowed_packet; Increase max_allowed_packet on master; the last event was read from 'master-bin.000001' at 75, the last byte read was read from 'master-bin.000001' at 94.''
include/stop_slave_sql.inc
show master status;
File Position Binlog_Do_DB Binlog_Ignore_DB
diff --git a/mysql-test/suite/rpl/r/rpl_manual_change_index_file.result b/mysql-test/suite/rpl/r/rpl_manual_change_index_file.result
index 41101da98f8..a2dc5d402a7 100644
--- a/mysql-test/suite/rpl/r/rpl_manual_change_index_file.result
+++ b/mysql-test/suite/rpl/r/rpl_manual_change_index_file.result
@@ -5,7 +5,6 @@ CREATE TABLE t1(c1 INT);
FLUSH LOGS;
call mtr.add_suppression('Got fatal error 1236 from master when reading data from binary log: .*could not find next log');
include/wait_for_slave_io_error.inc [errno=1236]
-Last_IO_Error = 'Got fatal error 1236 from master when reading data from binary log: 'could not find next log; the last event was read from 'master-bin.000002' at 237, the last byte read was read from 'master-bin.000002' at 237.''
CREATE TABLE t2(c1 INT);
FLUSH LOGS;
CREATE TABLE t3(c1 INT);
diff --git a/mysql-test/suite/rpl/r/rpl_packet.result b/mysql-test/suite/rpl/r/rpl_packet.result
index a11fc16123d..27f85cac7de 100644
--- a/mysql-test/suite/rpl/r/rpl_packet.result
+++ b/mysql-test/suite/rpl/r/rpl_packet.result
@@ -37,7 +37,6 @@ DROP TABLE t1;
CREATE TABLE t1 (f1 int PRIMARY KEY, f2 LONGTEXT, f3 LONGTEXT) ENGINE=MyISAM;
INSERT INTO t1(f1, f2, f3) VALUES(1, REPEAT('a', @@global.max_allowed_packet), REPEAT('b', @@global.max_allowed_packet));
include/wait_for_slave_io_error.inc [errno=1236]
-Last_IO_Error = 'Got fatal error 1236 from master when reading data from binary log: 'log event entry exceeded max_allowed_packet; Increase max_allowed_packet on master; the last event was read from 'master-bin.000001' at 463, the last byte read was read from 'master-bin.000001' at 482.''
STOP SLAVE;
RESET SLAVE;
RESET MASTER;
diff --git a/mysql-test/suite/rpl/r/rpl_rotate_purge_deadlock.result b/mysql-test/suite/rpl/r/rpl_rotate_purge_deadlock.result
new file mode 100644
index 00000000000..34024f89617
--- /dev/null
+++ b/mysql-test/suite/rpl/r/rpl_rotate_purge_deadlock.result
@@ -0,0 +1,30 @@
+include/master-slave.inc
+[connection master]
+show binary logs;
+Log_name File_size
+master-bin.000001 #
+create table t1 (f text) engine=innodb;
+SET DEBUG_SYNC = 'at_purge_logs_before_date WAIT_FOR rotated';
+insert into t1 set f=repeat('a', 4096);
+*** there must be two logs in the list ***
+show binary logs;
+Log_name File_size
+master-bin.000001 #
+master-bin.000002 #
+insert into t1 set f=repeat('b', 4096);
+*** there must be three logs in the list ***
+show binary logs;
+Log_name File_size
+master-bin.000001 #
+master-bin.000002 #
+master-bin.000003 #
+SET DEBUG_SYNC = 'now SIGNAL rotated';
+SET DEBUG_SYNC = 'RESET';
+SET DEBUG_SYNC = 'RESET';
+SET DEBUG_SYNC = 'at_purge_logs_before_date WAIT_FOR rotated';
+insert into t1 set f=repeat('b', 4096);
+SET DEBUG_SYNC = 'now SIGNAL rotated';
+SET DEBUG_SYNC = 'RESET';
+SET DEBUG_SYNC = 'RESET';
+drop table t1;
+include/rpl_end.inc
diff --git a/mysql-test/suite/rpl/r/rpl_row_find_row_debug.result b/mysql-test/suite/rpl/r/rpl_row_find_row_debug.result
new file mode 100644
index 00000000000..295c053563d
--- /dev/null
+++ b/mysql-test/suite/rpl/r/rpl_row_find_row_debug.result
@@ -0,0 +1,18 @@
+include/master-slave.inc
+[connection master]
+include/stop_slave.inc
+SET GLOBAL log_warnings = 2;
+SET GLOBAL debug="d,inject_long_find_row_note";
+include/start_slave.inc
+CREATE TABLE t1 (c1 INT);
+INSERT INTO t1 VALUES (1), (2);
+UPDATE t1 SET c1= 1000 WHERE c1=2;
+DELETE FROM t1;
+DROP TABLE t1;
+# Check if any note related to long DELETE_ROWS and UPDATE_ROWS appears in the error log
+Occurrences: update=1, delete=1
+include/stop_slave.inc
+SET GLOBAL debug = '';
+SET GLOBAL log_warnings = 1;
+include/start_slave.inc
+include/rpl_end.inc
diff --git a/mysql-test/suite/rpl/t/rpl_cant_read_event_incident.test b/mysql-test/suite/rpl/t/rpl_cant_read_event_incident.test
index 71445be55e6..5e88b163d99 100644
--- a/mysql-test/suite/rpl/t/rpl_cant_read_event_incident.test
+++ b/mysql-test/suite/rpl/t/rpl_cant_read_event_incident.test
@@ -14,6 +14,11 @@
--source include/master-slave.inc
--source include/have_binlog_format_mixed.inc
+#
+# Bug#13050593 swallows `\' from Last_IO_Error
+# todo: uncomment the filter once the bug is fixed.
+#
+--source include/not_windows.inc
call mtr.add_suppression("Error in Log_event::read_log_event()");
diff --git a/mysql-test/suite/rpl/t/rpl_log_pos.test b/mysql-test/suite/rpl/t/rpl_log_pos.test
index 484ffa52a44..882a05712f2 100644
--- a/mysql-test/suite/rpl/t/rpl_log_pos.test
+++ b/mysql-test/suite/rpl/t/rpl_log_pos.test
@@ -22,7 +22,12 @@ let $status_items= Read_Master_Log_Pos;
source include/show_slave_status.inc;
start slave;
let $slave_io_errno= 1236;
-let $show_slave_io_error= 1;
+#
+# Win and Unix path is printed differently: BUG#13055685. So
+# show_slave_io_error is made 0 until the bug fixes provide necessary
+# facilities
+#
+let $show_slave_io_error= 0;
source include/wait_for_slave_io_error.inc;
source include/stop_slave_sql.inc;
diff --git a/mysql-test/suite/rpl/t/rpl_manual_change_index_file.test b/mysql-test/suite/rpl/t/rpl_manual_change_index_file.test
index b0d3b23b4e1..9b648de8486 100644
--- a/mysql-test/suite/rpl/t/rpl_manual_change_index_file.test
+++ b/mysql-test/suite/rpl/t/rpl_manual_change_index_file.test
@@ -60,7 +60,12 @@ call mtr.add_suppression('Got fatal error 1236 from master when reading data fro
connection slave;
# 1236 = ER_MASTER_FATAL_ERROR_READING_BINLOG
--let $slave_io_errno= 1236
---let $show_slave_io_error= 1
+#
+# Win and Unix path is printed differently: BUG#13055685. So
+# show_slave_io_error is made 0 until the bug fixes provide necessary
+# facilities
+#
+--let $show_slave_io_error= 0
--source include/wait_for_slave_io_error.inc
connection master;
diff --git a/mysql-test/suite/rpl/t/rpl_packet.test b/mysql-test/suite/rpl/t/rpl_packet.test
index 7e9a35883a3..2b8a1fd8310 100644
--- a/mysql-test/suite/rpl/t/rpl_packet.test
+++ b/mysql-test/suite/rpl/t/rpl_packet.test
@@ -125,7 +125,12 @@ connection slave;
# The slave I/O thread must stop after receiving
# 1236=ER_MASTER_FATAL_ERROR_READING_BINLOG error message from master.
--let $slave_io_errno= 1236
---let $show_slave_io_error= 1
+#
+# Win and Unix path is printed differently: BUG#13055685. So
+# show_slave_io_error is made 0 until the bug fixes provide necessary
+# facilities
+#
+--let $show_slave_io_error= 0
--source include/wait_for_slave_io_error.inc
# Remove the bad binlog and clear error status on slave.
diff --git a/mysql-test/suite/rpl/t/rpl_rotate_purge_deadlock-master.opt b/mysql-test/suite/rpl/t/rpl_rotate_purge_deadlock-master.opt
new file mode 100644
index 00000000000..f71317fc399
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_rotate_purge_deadlock-master.opt
@@ -0,0 +1 @@
+--max-binlog-size=4k --expire-logs-days=1
diff --git a/mysql-test/suite/rpl/t/rpl_rotate_purge_deadlock.test b/mysql-test/suite/rpl/t/rpl_rotate_purge_deadlock.test
new file mode 100644
index 00000000000..429612c405f
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_rotate_purge_deadlock.test
@@ -0,0 +1,92 @@
+#
+# Bug#11763573 - 56299: MUTEX DEADLOCK WITH COM_BINLOG_DUMP, BINLOG PURGE, AND PROCESSLIST/KILL
+#
+source include/master-slave.inc;
+source include/have_debug_sync.inc;
+source include/have_binlog_format_row.inc;
+source include/have_innodb.inc;
+
+#
+# Testing that execution of two concurrent INSERTing connections both
+# triggering the binlog rotation is correct even though their execution
+# is interleaved.
+# The test makes the first connection to complete the rotation part
+# and yields control to the second connection that rotates as well and
+# gets first on purging. And the fact of interleaving does not create
+# any issue.
+#
+
+connection master;
+source include/show_binary_logs.inc;
+create table t1 (f text) engine=innodb;
+SET DEBUG_SYNC = 'at_purge_logs_before_date WAIT_FOR rotated';
+send insert into t1 set f=repeat('a', 4096);
+
+connection master1;
+
+let $wait_condition=
+ SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.PROCESSLIST
+ WHERE STATE like "debug sync point: at_purge_logs_before_date%";
+--source include/wait_condition.inc
+
+--echo *** there must be two logs in the list ***
+source include/show_binary_logs.inc;
+
+insert into t1 set f=repeat('b', 4096);
+
+--echo *** there must be three logs in the list ***
+source include/show_binary_logs.inc;
+
+SET DEBUG_SYNC = 'now SIGNAL rotated';
+SET DEBUG_SYNC = 'RESET';
+
+# the first connection finally completes its INSERT
+connection master;
+reap;
+SET DEBUG_SYNC = 'RESET';
+
+sync_slave_with_master;
+
+
+#
+# Testing the reported deadlock involving DUMP, KILL and INSERT threads
+#
+
+connection master;
+SET DEBUG_SYNC = 'at_purge_logs_before_date WAIT_FOR rotated';
+send insert into t1 set f=repeat('b', 4096);
+
+connection master1;
+
+# make sure INSERT reaches waiting point
+let $wait_condition=
+ SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.PROCESSLIST
+ WHERE STATE like "debug sync point: at_purge_logs_before_date%";
+--source include/wait_condition.inc
+
+# find and kill DUMP thread
+let $_tid= `select id from information_schema.processlist where command = 'Binlog Dump' limit 1`;
+--disable_query_log
+eval kill query $_tid;
+--enable_query_log
+
+#
+# Now the proof is that the new DUMP thread has executed
+# a critical section of the deadlock without any regression and is UP
+#
+let $wait_condition=
+ SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.PROCESSLIST
+ WHERE command = 'Binlog Dump' and STATE like "Master has sent all binlog to slave%";
+--source include/wait_condition.inc
+
+SET DEBUG_SYNC = 'now SIGNAL rotated';
+SET DEBUG_SYNC = 'RESET';
+
+connection master;
+reap;
+SET DEBUG_SYNC = 'RESET';
+drop table t1;
+
+sync_slave_with_master;
+
+--source include/rpl_end.inc
diff --git a/mysql-test/suite/rpl/t/rpl_row_find_row_debug.test b/mysql-test/suite/rpl/t/rpl_row_find_row_debug.test
new file mode 100644
index 00000000000..a8d9e878ba2
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_row_find_row_debug.test
@@ -0,0 +1,62 @@
+#
+# Bug#11760927: 53375: RBR + NO PK => HIGH LOAD ON SLAVE (TABLE SCAN/CPU) => SLAVE FAILURE
+#
+--source include/master-slave.inc
+--source include/have_binlog_format_row.inc
+--source include/have_debug.inc
+
+# SETUP
+# - setup log_warnings and debug
+--connection slave
+--source include/stop_slave.inc
+--let $debug_save= `SELECT @@GLOBAL.debug`
+--let $log_warnings_save= `SELECT @@GLOBAL.log_warnings`
+
+SET GLOBAL log_warnings = 2;
+
+let $log_error_= `SELECT @@GLOBAL.log_error`;
+if(!$log_error_)
+{
+ # MySQL Server on windows is started with --console and thus
+ # does not know the location of its .err log, use default location
+ let $log_error_ = $MYSQLTEST_VARDIR/log/mysqld.2.err;
+}
+
+# Assign env variable LOG_ERROR
+let LOG_ERROR=$log_error_;
+
+# force printing the notes to the error log
+SET GLOBAL debug="d,inject_long_find_row_note";
+--source include/start_slave.inc
+
+# test
+--connection master
+CREATE TABLE t1 (c1 INT);
+--sync_slave_with_master
+--connection master
+
+INSERT INTO t1 VALUES (1), (2);
+UPDATE t1 SET c1= 1000 WHERE c1=2;
+DELETE FROM t1;
+DROP TABLE t1;
+--sync_slave_with_master
+
+--echo # Check if any note related to long DELETE_ROWS and UPDATE_ROWS appears in the error log
+perl;
+ use strict;
+ my $log_error= $ENV{'LOG_ERROR'} or die "LOG_ERROR not set";
+ open(FILE, "$log_error") or die("Unable to open $log_error: $!\n");
+ my $upd_count = () = grep(/The slave is applying a ROW event on behalf of an UPDATE statement on table t1 and is currently taking a considerable amount/g,<FILE>);
+ seek(FILE, 0, 0) or die "Can't seek to beginning of file: $!";
+ my $del_count = () = grep(/The slave is applying a ROW event on behalf of a DELETE statement on table t1 and is currently taking a considerable amount/g,<FILE>);
+ print "Occurrences: update=$upd_count, delete=$del_count\n";
+ close(FILE);
+EOF
+
+# cleanup
+--source include/stop_slave.inc
+--eval SET GLOBAL debug = '$debug_save'
+--eval SET GLOBAL log_warnings = $log_warnings_save
+--source include/start_slave.inc
+
+--source include/rpl_end.inc
diff --git a/mysql-test/suite/sys_vars/r/metadata_locks_cache_size_basic.result b/mysql-test/suite/sys_vars/r/metadata_locks_cache_size_basic.result
new file mode 100644
index 00000000000..a62b6011739
--- /dev/null
+++ b/mysql-test/suite/sys_vars/r/metadata_locks_cache_size_basic.result
@@ -0,0 +1,21 @@
+#
+# Check that the paremeter is correctly set by start-up
+# option (.opt file sets it to 256 while default is 1024).
+select @@global.metadata_locks_cache_size = 256;
+@@global.metadata_locks_cache_size = 256
+1
+#
+# Check that variable is read only
+#
+set @@global.metadata_locks_cache_size= 1024;
+ERROR HY000: Variable 'metadata_locks_cache_size' is a read only variable
+select @@global.metadata_locks_cache_size = 256;
+@@global.metadata_locks_cache_size = 256
+1
+#
+# And only GLOBAL
+#
+select @@session.metadata_locks_cache_size;
+ERROR HY000: Variable 'metadata_locks_cache_size' is a GLOBAL variable
+set @@session.metadata_locks_cache_size= 1024;
+ERROR HY000: Variable 'metadata_locks_cache_size' is a read only variable
diff --git a/mysql-test/suite/sys_vars/t/metadata_locks_cache_size_basic-master.opt b/mysql-test/suite/sys_vars/t/metadata_locks_cache_size_basic-master.opt
new file mode 100644
index 00000000000..77e019cd3d4
--- /dev/null
+++ b/mysql-test/suite/sys_vars/t/metadata_locks_cache_size_basic-master.opt
@@ -0,0 +1 @@
+--metadata-locks-cache-size=256
diff --git a/mysql-test/suite/sys_vars/t/metadata_locks_cache_size_basic.test b/mysql-test/suite/sys_vars/t/metadata_locks_cache_size_basic.test
new file mode 100644
index 00000000000..31b033579c6
--- /dev/null
+++ b/mysql-test/suite/sys_vars/t/metadata_locks_cache_size_basic.test
@@ -0,0 +1,25 @@
+#
+# Basic test coverage for --metadata-locks-cache-size startup
+# parameter and corresponding read-only global @@metadata_locks_cache_size
+# variable.
+#
+
+--echo #
+--echo # Check that the paremeter is correctly set by start-up
+--echo # option (.opt file sets it to 256 while default is 1024).
+select @@global.metadata_locks_cache_size = 256;
+
+--echo #
+--echo # Check that variable is read only
+--echo #
+--error ER_INCORRECT_GLOBAL_LOCAL_VAR
+set @@global.metadata_locks_cache_size= 1024;
+select @@global.metadata_locks_cache_size = 256;
+
+--echo #
+--echo # And only GLOBAL
+--echo #
+--error ER_INCORRECT_GLOBAL_LOCAL_VAR
+select @@session.metadata_locks_cache_size;
+--error ER_INCORRECT_GLOBAL_LOCAL_VAR
+set @@session.metadata_locks_cache_size= 1024;
diff --git a/mysql-test/t/alter_table-big.test b/mysql-test/t/alter_table-big.test
index e007a3a55e0..b010815955f 100644
--- a/mysql-test/t/alter_table-big.test
+++ b/mysql-test/t/alter_table-big.test
@@ -18,7 +18,10 @@
--disable_warnings
drop table if exists t1, t2;
--enable_warnings
+set debug_sync='RESET';
+
connect (addconroot, localhost, root,,);
+connect (addconroot2, localhost, root,,);
connection default;
create table t1 (n1 int, n2 int, n3 int,
key (n1, n2, n3),
@@ -26,38 +29,45 @@ create table t1 (n1 int, n2 int, n3 int,
key (n3, n1, n2));
create table t2 (i int);
-# Starting from 5.1 we have runtime settable @@debug variable,
-# which can be used for introducing delays at certain points of
-# statement execution, so we don't need many rows in 't1' to make
-# this test repeatable.
alter table t1 disable keys;
---disable_warnings
-insert into t1 values (RAND()*1000, RAND()*1000, RAND()*1000);
---enable_warnings
+insert into t1 values (1, 2, 3);
# Later we use binlog to check the order in which statements are
# executed so let us reset it first.
reset master;
-set session debug="+d,sleep_alter_enable_indexes";
+set debug_sync='alter_table_enable_indexes SIGNAL parked WAIT_FOR go';
--send alter table t1 enable keys;
connection addconroot;
---sleep 2
+# Wait until ALTER TABLE acquires metadata lock.
+set debug_sync='now WAIT_FOR parked';
# This statement should not be blocked by in-flight ALTER and therefore
# should be executed and written to binlog before ALTER TABLE ... ENABLE KEYS
# finishes.
insert into t2 values (1);
# And this should wait until the end of ALTER TABLE ... ENABLE KEYS.
-insert into t1 values (1, 1, 1);
+--send insert into t1 values (1, 1, 1);
+connection addconroot2;
+# Wait until the above INSERT INTO t1 is blocked due to ALTER
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = "Waiting for table metadata lock" and
+ info = "insert into t1 values (1, 1, 1)";
+--source include/wait_condition.inc
+# Resume ALTER execution.
+set debug_sync='now SIGNAL go';
connection default;
--reap
-set session debug="-d,sleep_alter_enable_indexes";
+connection addconroot;
+--reap
+connection default;
# Check that statements were executed/binlogged in correct order.
source include/show_binlog_events.inc;
# Clean up
drop tables t1, t2;
disconnect addconroot;
-
+disconnect addconroot2;
+set debug_sync='RESET';
--echo End of 5.0 tests
@@ -72,48 +82,92 @@ disconnect addconroot;
--disable_warnings
drop table if exists t1, t2, t3;
--enable_warnings
+connect (addconroot, localhost, root,,);
+connect (addconroot2, localhost, root,,);
+connection default;
create table t1 (i int);
# We are going to check that statements are logged in correct order
reset master;
-set session debug="+d,sleep_alter_before_main_binlog";
+set debug_sync='alter_table_before_main_binlog SIGNAL parked WAIT_FOR go';
--send alter table t1 change i c char(10) default 'Test1';
-connect (addconroot, localhost, root,,);
connection addconroot;
---sleep 2
-insert into t1 values ();
-select * from t1;
+# Wait until ALTER TABLE acquires metadata lock.
+set debug_sync='now WAIT_FOR parked';
+--send insert into t1 values ();
+connection addconroot2;
+# Wait until the above INSERT INTO t1 is blocked due to ALTER
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = "Waiting for table metadata lock" and
+ info = "insert into t1 values ()";
+--source include/wait_condition.inc
+# Resume ALTER execution.
+set debug_sync='now SIGNAL go';
connection default;
--reap
+connection addconroot;
+--reap
+connection default;
+select * from t1;
+set debug_sync='alter_table_before_main_binlog SIGNAL parked WAIT_FOR go';
--send alter table t1 change c vc varchar(100) default 'Test2';
connection addconroot;
---sleep 2
-rename table t1 to t2;
+# Wait until ALTER TABLE acquires metadata lock.
+set debug_sync='now WAIT_FOR parked';
+--send rename table t1 to t2;
+connection addconroot2;
+# Wait until the above RENAME TABLE is blocked due to ALTER
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = "Waiting for table metadata lock" and
+ info = "rename table t1 to t2";
+--source include/wait_condition.inc
+# Resume ALTER execution.
+set debug_sync='now SIGNAL go';
connection default;
--reap
+connection addconroot;
+--reap
+connection default;
drop table t2;
# And now tests for ALTER TABLE with RENAME clause. In this
# case target table name should be properly locked as well.
create table t1 (i int);
+set debug_sync='alter_table_before_main_binlog SIGNAL parked WAIT_FOR go';
--send alter table t1 change i c char(10) default 'Test3', rename to t2;
connection addconroot;
---sleep 2
-insert into t2 values ();
-select * from t2;
+# Wait until ALTER TABLE acquires metadata lock.
+set debug_sync='now WAIT_FOR parked';
+--send insert into t2 values();
+connection addconroot2;
+# Wait until the above INSERT INTO t2 is blocked due to ALTER
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = "Waiting for table metadata lock" and
+ info = "insert into t2 values()";
+--source include/wait_condition.inc
+# Resume ALTER execution.
+set debug_sync='now SIGNAL go';
connection default;
--reap
+connection addconroot;
+--reap
+connection default;
+select * from t2;
--send alter table t2 change c vc varchar(100) default 'Test2', rename to t1;
connection addconroot;
---sleep 2
-rename table t1 to t3;
connection default;
--reap
+rename table t1 to t3;
+
disconnect addconroot;
+disconnect addconroot2;
drop table t3;
-set session debug="-d,sleep_alter_before_main_binlog";
+set debug_sync='alter_table_before_main_binlog SIGNAL parked WAIT_FOR go';
+set debug_sync='RESET';
# Check that all statements were logged in correct order
source include/show_binlog_events.inc;
--echo End of 5.1 tests
-
diff --git a/mysql-test/t/disabled.def b/mysql-test/t/disabled.def
index 08db85a3f45..cb1de2cb0ff 100644
--- a/mysql-test/t/disabled.def
+++ b/mysql-test/t/disabled.def
@@ -12,7 +12,6 @@
lowercase_table3 : Bug#11762269 2010-06-30 alik main.lowercase_table3 on Mac OSX
read_many_rows_innodb : Bug#11748886 2010-11-15 mattiasj report already exists
sum_distinct-big : Bug#11764126 2010-11-15 mattiasj was not tested
-alter_table-big : Bug#11748731 2010-11-15 mattiasj was not tested
create-big : Bug#11748731 2010-11-15 mattiasj was not tested
archive-big : Bug#11817185 2011-03-10 Anitha Disabled since this leads to timeout on Solaris Sparc
log_tables-big : Bug#11756699 2010-11-15 mattiasj report already exists
diff --git a/mysql-test/t/mysql_plugin.test b/mysql-test/t/mysql_plugin.test
index 897e22ddd62..c5968df85f8 100644
--- a/mysql-test/t/mysql_plugin.test
+++ b/mysql-test/t/mysql_plugin.test
@@ -142,8 +142,11 @@ EOF
--echo # Simulate loading a plugin libary with multiple entry points.
--echo # This will test the DISABLE to ensure all rows are removed.
--echo #
+--replace_regex /\.dll/.so/
eval INSERT INTO mysql.plugin VALUES ('wicky', '$DAEMONEXAMPLE');
+--replace_regex /\.dll/.so/
eval INSERT INTO mysql.plugin VALUES ('wacky', '$DAEMONEXAMPLE');
+--replace_regex /\.dll/.so/
eval INSERT INTO mysql.plugin VALUES ('wonky', '$DAEMONEXAMPLE');
--echo #
diff --git a/mysql-test/t/mysql_upgrade.test b/mysql-test/t/mysql_upgrade.test
index 3c6cc45da38..05b8c81a797 100644
--- a/mysql-test/t/mysql_upgrade.test
+++ b/mysql-test/t/mysql_upgrade.test
@@ -102,5 +102,24 @@ DROP USER 'user3'@'%';
# Test the --upgrade-system-tables option
#
--replace_result $MYSQLTEST_VARDIR var
---exec $MYSQL_UPGRADE --skip-verbose --upgrade-system-tables
+--exec $MYSQL_UPGRADE --skip-verbose --force --upgrade-system-tables
+--echo #
+--echo # Bug#11827359 60223: MYSQL_UPGRADE PROBLEM WITH OPTION
+--echo # SKIP-WRITE-BINLOG
+--echo #
+
+let $MYSQLD_DATADIR= `select @@datadir`;
+
+--echo # Droping the previously created mysql_upgrade_info file..
+--remove_file $MYSQLD_DATADIR/mysql_upgrade_info
+
+--echo # Running mysql_upgrade with --skip-write-binlog..
+--replace_result $MYSQLTEST_VARDIR var
+--exec $MYSQL_UPGRADE --skip-verbose --skip-write-binlog
+
+# mysql_upgrade must have created mysql_upgrade_info file,
+# so the following command should never fail.
+--remove_file $MYSQLD_DATADIR/mysql_upgrade_info
+
+--echo End of tests
diff --git a/mysql-test/t/mysqltest.test b/mysql-test/t/mysqltest.test
index 11ab79458d7..86ee7ccc1fa 100644
--- a/mysql-test/t/mysqltest.test
+++ b/mysql-test/t/mysqltest.test
@@ -1745,6 +1745,12 @@ let $long_rep= $long_rep,$long_rep;
--replace_result $long_rep LONG_STRING
eval select "$long_rep" as x;
+# Test replace within ``
+
+--replace_result cat dog
+--let $animal= `select "cat" as pet`
+--echo $animal
+
# ----------------------------------------------------------------------------
# Test sync_with_master
# ----------------------------------------------------------------------------
@@ -2065,6 +2071,23 @@ insert into t1 values (2,4);
select * from t1;
drop table t1;
+# Test usage with ``
+
+--replace_regex /x/y/
+--let $result= `select "x" as col`
+--echo $result
+
+# Test usage with a variable as pattern list
+
+--disable_query_log
+--let $patt= /a /b / /less/more/
+--replace_regex $patt
+select "a is a and less is more" as txt;
+--let $patt=
+--replace_regex $patt
+select "a is a and less is more" as txt;
+--enable_query_log
+
#-------------------------------------------------------------------------
# BUG #11754855 : Passing variable to --error
#-------------------------------------------------------------------------
diff --git a/mysql-test/t/query_cache.test b/mysql-test/t/query_cache.test
index 51e362d4916..49a8817fe6b 100644
--- a/mysql-test/t/query_cache.test
+++ b/mysql-test/t/query_cache.test
@@ -1193,47 +1193,48 @@ set global query_cache_type=0;
show status like 'Qcache_free_blocks';
--echo Restore default values.
-# Bug#28211 RENAME DATABASE and query cache don't play nicely together
-#
-# TODO: enable these tests when RENAME DATABASE is implemented.
-# --disable_warnings
-# drop database if exists db1;
-# drop database if exists db2;
-# --enable_warnings
-# set GLOBAL query_cache_size=15*1024*1024;
-# create database db1;
-# use db1;
-# create table t1(c1 int)engine=myisam;
-# insert into t1(c1) values (1);
-# select * from db1.t1 f;
-# show status like 'Qcache_queries_in_cache';
-# rename schema db1 to db2;
-# show status like 'Qcache_queries_in_cache';
-# drop database db2;
-# set global query_cache_size=default;
-#
-# --disable_warnings
-# drop database if exists db1;
-# drop database if exists db3;
-# --enable_warnings
-# set GLOBAL query_cache_size=15*1024*1024;
-# create database db1;
-# create database db3;
-# use db1;
-# create table t1(c1 int) engine=myisam;
-# use db3;
-# create table t1(c1 int) engine=myisam;
-# use db1;
-# insert into t1(c1) values (1);
-# use mysql;
-# select * from db1.t1;
-# select c1+1 from db1.t1;
-# select * from db3.t1;
-# show status like 'Qcache_queries_in_cache';
-# rename schema db1 to db2;
-# show status like 'Qcache_queries_in_cache';
-# drop database db2;
-# drop database db3;
+ --disable_warnings
+ drop database if exists db1;
+ drop database if exists db2;
+ --enable_warnings
+ set GLOBAL query_cache_size=15*1024*1024;
+ create database db1;
+ use db1;
+ create table t1(c1 int)engine=myisam;
+ insert into t1(c1) values (1);
+ select * from db1.t1 f;
+ show status like 'Qcache_queries_in_cache';
+ create database db2;
+ rename table db1.t1 to db2.t2;
+ drop database db1;
+ show status like 'Qcache_queries_in_cache';
+ drop database db2;
+ set global query_cache_size=default;
+
+ --disable_warnings
+ drop database if exists db1;
+ drop database if exists db3;
+ --enable_warnings
+ set GLOBAL query_cache_size=15*1024*1024;
+ create database db1;
+ create database db3;
+ use db1;
+ create table t1(c1 int) engine=myisam;
+ use db3;
+ create table t1(c1 int) engine=myisam;
+ use db1;
+ insert into t1(c1) values (1);
+ use test;
+ select * from db1.t1;
+ select c1+1 from db1.t1;
+ select * from db3.t1;
+ show status like 'Qcache_queries_in_cache';
+ create database db2;
+ rename table db1.t1 to db2.t2;
+ drop database db1;
+ show status like 'Qcache_queries_in_cache';
+ drop database db2;
+ drop database db3;
set GLOBAL query_cache_type=default;
set GLOBAL query_cache_limit=default;
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 7257b411ec4..0cd6ff6357e 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -2328,25 +2328,31 @@ double my_double_round(double value, longlong dec, bool dec_unsigned,
/*
tmp2 is here to avoid return the value with 80 bit precision
This will fix that the test round(0.1,1) = round(0.1,1) is true
+ Tagging with volatile is no guarantee, it may still be optimized away...
*/
volatile double tmp2;
tmp=(abs_dec < array_elements(log_10) ?
log_10[abs_dec] : pow(10.0,(double) abs_dec));
+ // Pre-compute these, to avoid optimizing away e.g. 'floor(v/tmp) * tmp'.
+ volatile double value_div_tmp= value / tmp;
+ volatile double value_mul_tmp= value * tmp;
+
if (dec_negative && my_isinf(tmp))
- tmp2= 0;
- else if (!dec_negative && my_isinf(value * tmp))
+ tmp2= 0.0;
+ else if (!dec_negative && my_isinf(value_mul_tmp))
tmp2= value;
else if (truncate)
{
- if (value >= 0)
- tmp2= dec < 0 ? floor(value/tmp)*tmp : floor(value*tmp)/tmp;
+ if (value >= 0.0)
+ tmp2= dec < 0 ? floor(value_div_tmp) * tmp : floor(value_mul_tmp) / tmp;
else
- tmp2= dec < 0 ? ceil(value/tmp)*tmp : ceil(value*tmp)/tmp;
+ tmp2= dec < 0 ? ceil(value_div_tmp) * tmp : ceil(value_mul_tmp) / tmp;
}
else
- tmp2=dec < 0 ? rint(value/tmp)*tmp : rint(value*tmp)/tmp;
+ tmp2=dec < 0 ? rint(value_div_tmp) * tmp : rint(value_mul_tmp) / tmp;
+
return tmp2;
}
diff --git a/sql/log.cc b/sql/log.cc
index c6b41447d6a..77ba7aa6283 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -49,7 +49,7 @@
#include "sql_plugin.h"
#include "rpl_handler.h"
-
+#include "debug_sync.h"
/* max size of the log message */
#define MAX_LOG_BUFFER_SIZE 1024
#define MAX_TIME_SIZE 32
@@ -5002,19 +5002,29 @@ err:
{
bool synced;
if ((error= flush_and_sync(&synced)))
- goto unlock;
-
- if ((error= RUN_HOOK(binlog_storage, after_flush,
+ {
+ mysql_mutex_unlock(&LOCK_log);
+ }
+ else if ((error= RUN_HOOK(binlog_storage, after_flush,
(thd, log_file_name, file->pos_in_file, synced))))
{
sql_print_error("Failed to run 'after_flush' hooks");
- goto unlock;
+ mysql_mutex_unlock(&LOCK_log);
+ }
+ else
+ {
+ bool check_purge;
+ signal_update();
+ error= rotate(false, &check_purge);
+ mysql_mutex_unlock(&LOCK_log);
+ if (!error && check_purge)
+ purge();
}
- signal_update();
- rotate_and_purge(RP_LOCK_LOG_IS_ALREADY_LOCKED);
}
-unlock:
- mysql_mutex_unlock(&LOCK_log);
+ else
+ {
+ mysql_mutex_unlock(&LOCK_log);
+ }
}
if (error)
@@ -5100,25 +5110,29 @@ bool general_log_write(THD *thd, enum enum_server_command command,
}
/**
+ The method executes rotation when LOCK_log is already acquired
+ by the caller.
+
+ @param force_rotate caller can request the log rotation
+ @param check_purge is set to true if rotation took place
+
@note
If rotation fails, for instance the server was unable
to create a new log file, we still try to write an
incident event to the current log.
@retval
- nonzero - error
+ nonzero - error in rotating routine.
*/
-int MYSQL_BIN_LOG::rotate_and_purge(uint flags)
+int MYSQL_BIN_LOG::rotate(bool force_rotate, bool* check_purge)
{
int error= 0;
- DBUG_ENTER("MYSQL_BIN_LOG::rotate_and_purge");
-#ifdef HAVE_REPLICATION
- bool check_purge= false;
-#endif
- if (!(flags & RP_LOCK_LOG_IS_ALREADY_LOCKED))
- mysql_mutex_lock(&LOCK_log);
- if ((flags & RP_FORCE_ROTATE) ||
- (my_b_tell(&log_file) >= (my_off_t) max_size))
+ DBUG_ENTER("MYSQL_BIN_LOG::rotate");
+
+ //todo: fix the macro def and restore safe_mutex_assert_owner(&LOCK_log);
+ *check_purge= false;
+
+ if (force_rotate || (my_b_tell(&log_file) >= (my_off_t) max_size))
{
if ((error= new_file_without_locking()))
/**
@@ -5133,24 +5147,59 @@ int MYSQL_BIN_LOG::rotate_and_purge(uint flags)
if (!write_incident(current_thd, FALSE))
flush_and_sync(0);
-#ifdef HAVE_REPLICATION
- check_purge= true;
-#endif
+ *check_purge= true;
}
- if (!(flags & RP_LOCK_LOG_IS_ALREADY_LOCKED))
- mysql_mutex_unlock(&LOCK_log);
+ DBUG_RETURN(error);
+}
+
+/**
+ The method executes logs purging routine.
+
+ @retval
+ nonzero - error in rotating routine.
+*/
+void MYSQL_BIN_LOG::purge()
+{
#ifdef HAVE_REPLICATION
- /*
- NOTE: Run purge_logs wo/ holding LOCK_log
- as it otherwise will deadlock in ndbcluster_binlog_index_purge_file
- */
- if (!error && check_purge && expire_logs_days)
+ if (expire_logs_days)
{
+ DEBUG_SYNC(current_thd, "at_purge_logs_before_date");
time_t purge_time= my_time(0) - expire_logs_days*24*60*60;
if (purge_time >= 0)
+ {
purge_logs_before_date(purge_time);
+ }
}
#endif
+}
+
+/**
+ The method is a shortcut of @c rotate() and @c purge().
+ LOCK_log is acquired prior to rotate and is released after it.
+
+ @param force_rotate caller can request the log rotation
+
+ @retval
+ nonzero - error in rotating routine.
+*/
+int MYSQL_BIN_LOG::rotate_and_purge(bool force_rotate)
+{
+ int error= 0;
+ DBUG_ENTER("MYSQL_BIN_LOG::rotate_and_purge");
+ bool check_purge= false;
+
+ //todo: fix the macro def and restore safe_mutex_assert_not_owner(&LOCK_log);
+ mysql_mutex_lock(&LOCK_log);
+ error= rotate(force_rotate, &check_purge);
+ /*
+ NOTE: Run purge_logs wo/ holding LOCK_log because it does not need
+ the mutex. Otherwise causes various deadlocks.
+ */
+ mysql_mutex_unlock(&LOCK_log);
+
+ if (!error && check_purge)
+ purge();
+
DBUG_RETURN(error);
}
@@ -5352,10 +5401,17 @@ bool MYSQL_BIN_LOG::write_incident(THD *thd, bool lock)
{
if (!error && !(error= flush_and_sync(0)))
{
+ bool check_purge= false;
signal_update();
- error= rotate_and_purge(RP_LOCK_LOG_IS_ALREADY_LOCKED);
+ error= rotate(false, &check_purge);
+ mysql_mutex_unlock(&LOCK_log);
+ if (!error && check_purge)
+ purge();
+ }
+ else
+ {
+ mysql_mutex_unlock(&LOCK_log);
}
- mysql_mutex_unlock(&LOCK_log);
}
DBUG_RETURN(error);
}
@@ -5388,11 +5444,13 @@ bool MYSQL_BIN_LOG::write(THD *thd, IO_CACHE *cache, Log_event *commit_event,
bool incident)
{
DBUG_ENTER("MYSQL_BIN_LOG::write(THD *, IO_CACHE *, Log_event *)");
- mysql_mutex_lock(&LOCK_log);
DBUG_ASSERT(is_open());
if (likely(is_open())) // Should always be true
{
+ bool check_purge;
+
+ mysql_mutex_lock(&LOCK_log);
/*
We only bother to write to the binary log if there is anything
to write.
@@ -5460,12 +5518,17 @@ bool MYSQL_BIN_LOG::write(THD *thd, IO_CACHE *cache, Log_event *commit_event,
mysql_mutex_lock(&LOCK_prep_xids);
prepared_xids++;
mysql_mutex_unlock(&LOCK_prep_xids);
+ mysql_mutex_unlock(&LOCK_log);
}
else
- if (rotate_and_purge(RP_LOCK_LOG_IS_ALREADY_LOCKED))
+ {
+ if (rotate(false, &check_purge))
goto err;
+ mysql_mutex_unlock(&LOCK_log);
+ if (check_purge)
+ purge();
+ }
}
- mysql_mutex_unlock(&LOCK_log);
DBUG_RETURN(0);
diff --git a/sql/log.h b/sql/log.h
index 0f0f81dd402..481bd545960 100644
--- a/sql/log.h
+++ b/sql/log.h
@@ -455,7 +455,9 @@ public:
void make_log_name(char* buf, const char* log_ident);
bool is_active(const char* log_file_name);
int update_log_index(LOG_INFO* linfo, bool need_update_threads);
- int rotate_and_purge(uint flags);
+ int rotate(bool force_rotate, bool* check_purge);
+ void purge();
+ int rotate_and_purge(bool force_rotate);
/**
Flush binlog cache and synchronize to disk.
diff --git a/sql/log_event.cc b/sql/log_event.cc
index b29bcfdb536..0003a621cbb 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -7761,6 +7761,12 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli)
// row processing loop
+ /*
+ set the initial time of this ROWS statement if it was not done
+ before in some other ROWS event.
+ */
+ const_cast<Relay_log_info*>(rli)->set_row_stmt_start_timestamp();
+
while (error == 0 && m_curr_row < m_rows_end)
{
/* in_use can have been set to NULL in close_tables_for_reopen */
@@ -9252,6 +9258,51 @@ record_compare_exit:
return result;
}
+/*
+ Check if we are already spending too much time on this statement.
+ if we are, warn user that it might be because table does not have
+ a PK, but only if the warning was not printed before for this STMT.
+
+ @param type The event type code.
+ @param table_name The name of the table that the slave is
+ operating.
+ @param is_index_scan States whether the slave is doing an index scan
+ or not.
+ @param rli The relay metadata info.
+*/
+static inline
+void issue_long_find_row_warning(Log_event_type type,
+ const char *table_name,
+ bool is_index_scan,
+ const Relay_log_info *rli)
+{
+ if ((global_system_variables.log_warnings > 1 &&
+ !const_cast<Relay_log_info*>(rli)->is_long_find_row_note_printed()))
+ {
+ time_t now= my_time(0);
+ time_t stmt_ts= const_cast<Relay_log_info*>(rli)->get_row_stmt_start_timestamp();
+
+ DBUG_EXECUTE_IF("inject_long_find_row_note",
+ stmt_ts-=(LONG_FIND_ROW_THRESHOLD*2););
+
+ long delta= (long) (now - stmt_ts);
+
+ if (delta > LONG_FIND_ROW_THRESHOLD)
+ {
+ const_cast<Relay_log_info*>(rli)->set_long_find_row_note_printed();
+ const char* evt_type= type == DELETE_ROWS_EVENT ? " DELETE" : "n UPDATE";
+ const char* scan_type= is_index_scan ? "scanning an index" : "scanning the table";
+
+ sql_print_information("The slave is applying a ROW event on behalf of a%s statement "
+ "on table %s and is currently taking a considerable amount "
+ "of time (%ld seconds). This is due to the fact that it is %s "
+ "while looking up records to be processed. Consider adding a "
+ "primary key (or unique key) to the table to improve "
+ "performance.", evt_type, table_name, delta, scan_type);
+ }
+ }
+}
+
/**
Locate the current row in event's table.
@@ -9287,6 +9338,7 @@ int Rows_log_event::find_row(const Relay_log_info *rli)
TABLE *table= m_table;
int error= 0;
+ bool is_table_scan= false, is_index_scan= false;
/*
rpl_row_tabledefs.test specifies that
@@ -9452,6 +9504,8 @@ int Rows_log_event::find_row(const Relay_log_info *rli)
}
}
+ is_index_scan=true;
+
/*
In case key is not unique, we still have to iterate over records found
and find the one which is identical to the row given. A copy of the
@@ -9508,6 +9562,8 @@ int Rows_log_event::find_row(const Relay_log_info *rli)
goto err;
}
+ is_table_scan= true;
+
/* Continue until we find the right record or have made a full loop */
do
{
@@ -9561,10 +9617,18 @@ int Rows_log_event::find_row(const Relay_log_info *rli)
goto err;
}
ok:
+ if (is_table_scan || is_index_scan)
+ issue_long_find_row_warning(get_type_code(), m_table->alias,
+ is_index_scan, rli);
+
table->default_column_bitmaps();
DBUG_RETURN(0);
err:
+ if (is_table_scan || is_index_scan)
+ issue_long_find_row_warning(get_type_code(), m_table->alias,
+ is_index_scan, rli);
+
table->default_column_bitmaps();
DBUG_RETURN(error);
}
diff --git a/sql/log_event.h b/sql/log_event.h
index 07656ebaa72..d65f2d2c85d 100644
--- a/sql/log_event.h
+++ b/sql/log_event.h
@@ -53,6 +53,7 @@
class String;
#define PREFIX_SQL_LOAD "SQL_LOAD-"
+#define LONG_FIND_ROW_THRESHOLD 60 /* seconds */
/**
Either assert or return an error.
diff --git a/sql/mdl.cc b/sql/mdl.cc
index 046ac25703a..81d6c69dca4 100644
--- a/sql/mdl.cc
+++ b/sql/mdl.cc
@@ -91,6 +91,10 @@ const char *MDL_key::m_namespace_to_wait_state_name[NAMESPACE_END]=
static bool mdl_initialized= 0;
+class MDL_object_lock;
+class MDL_object_lock_cache_adapter;
+
+
/**
A collection of all MDL locks. A singleton,
there is only one instance of the map in the server.
@@ -111,6 +115,25 @@ private:
HASH m_locks;
/* Protects access to m_locks hash. */
mysql_mutex_t m_mutex;
+ /**
+ Cache of (unused) MDL_lock objects available for re-use.
+
+ On some systems (e.g. Windows XP) constructing/destructing
+ MDL_lock objects can be fairly expensive. We use this cache
+ to avoid these costs in scenarios in which they can have
+ significant negative effect on performance. For example, when
+ there is only one thread constantly executing statements in
+ auto-commit mode and thus constantly causing creation/
+ destruction of MDL_lock objects for the tables it uses.
+
+ Note that this cache contains only MDL_object_lock objects.
+
+ Protected by m_mutex mutex.
+ */
+ typedef I_P_List<MDL_object_lock, MDL_object_lock_cache_adapter,
+ I_P_List_counter>
+ Lock_cache;
+ Lock_cache m_unused_locks_cache;
/** Pre-allocated MDL_lock object for GLOBAL namespace. */
MDL_lock *m_global_lock;
/** Pre-allocated MDL_lock object for COMMIT namespace. */
@@ -379,7 +402,8 @@ public:
: key(key_arg),
m_ref_usage(0),
m_ref_release(0),
- m_is_destroyed(FALSE)
+ m_is_destroyed(FALSE),
+ m_version(0)
{
mysql_prlock_init(key_MDL_lock_rwlock, &m_rwlock);
}
@@ -414,6 +438,22 @@ public:
uint m_ref_usage;
uint m_ref_release;
bool m_is_destroyed;
+ /**
+ We use the same idea and an additional version counter to support
+ caching of unused MDL_lock object for further re-use.
+ This counter is incremented while holding both MDL_map::m_mutex and
+ MDL_lock::m_rwlock locks each time when a MDL_lock is moved from
+ the hash to the unused objects list (or destroyed).
+ A thread, which has found a MDL_lock object for the key in the hash
+ and then released the MDL_map::m_mutex before acquiring the
+ MDL_lock::m_rwlock, can determine that this object was moved to the
+ unused objects list (or destroyed) while it held no locks by comparing
+ the version value which it read while holding the MDL_map::m_mutex
+ with the value read after acquiring the MDL_lock::m_rwlock.
+ Note that since it takes several years to overflow this counter such
+ theoretically possible overflows should not have any practical effects.
+ */
+ ulonglong m_version;
};
@@ -462,6 +502,26 @@ public:
: MDL_lock(key_arg)
{ }
+ /**
+ Reset unused MDL_object_lock object to represent the lock context for a
+ different object.
+ */
+ void reset(const MDL_key *new_key)
+ {
+ /* We need to change only object's key. */
+ key.mdl_key_init(new_key);
+ /* m_granted and m_waiting should be already in the empty/initial state. */
+ DBUG_ASSERT(is_empty());
+ /* Object should not be marked as destroyed. */
+ DBUG_ASSERT(! m_is_destroyed);
+ /*
+ Values of the rest of the fields should be preserved between old and
+ new versions of the object. E.g., m_version and m_ref_usage/release
+ should be kept intact to properly handle possible remaining references
+ to the old version of the object.
+ */
+ }
+
virtual const bitmap_t *incompatible_granted_types_bitmap() const
{
return m_granted_incompatible;
@@ -479,10 +539,29 @@ public:
private:
static const bitmap_t m_granted_incompatible[MDL_TYPE_END];
static const bitmap_t m_waiting_incompatible[MDL_TYPE_END];
+
+public:
+ /** Members for linking the object into the list of unused objects. */
+ MDL_object_lock *next_in_cache, **prev_in_cache;
+};
+
+
+/**
+ Helper class for linking MDL_object_lock objects into the unused objects list.
+*/
+class MDL_object_lock_cache_adapter :
+ public I_P_List_adapter<MDL_object_lock, &MDL_object_lock::next_in_cache,
+ &MDL_object_lock::prev_in_cache>
+{
};
static MDL_map mdl_locks;
+/**
+ Start-up parameter for the maximum size of the unused MDL_lock objects cache.
+*/
+ulong mdl_locks_cache_size;
+
extern "C"
{
@@ -565,6 +644,10 @@ void MDL_map::destroy()
my_hash_free(&m_locks);
MDL_lock::destroy(m_global_lock);
MDL_lock::destroy(m_commit_lock);
+
+ MDL_object_lock *lock;
+ while ((lock= m_unused_locks_cache.pop_front()))
+ MDL_lock::destroy(lock);
}
@@ -614,11 +697,49 @@ retry:
mdl_key->ptr(),
mdl_key->length())))
{
- lock= MDL_lock::create(mdl_key);
+ MDL_object_lock *unused_lock= NULL;
+
+ /*
+ No lock object found so we need to create a new one
+ or reuse an existing unused object.
+ */
+ if (mdl_key->mdl_namespace() != MDL_key::SCHEMA &&
+ m_unused_locks_cache.elements())
+ {
+ /*
+ We need a MDL_object_lock type of object and the unused objects
+ cache has some. Get the first object from the cache and set a new
+ key for it.
+ */
+ DBUG_ASSERT(mdl_key->mdl_namespace() != MDL_key::GLOBAL &&
+ mdl_key->mdl_namespace() != MDL_key::COMMIT);
+
+ unused_lock= m_unused_locks_cache.pop_front();
+ unused_lock->reset(mdl_key);
+
+ lock= unused_lock;
+ }
+ else
+ {
+ lock= MDL_lock::create(mdl_key);
+ }
+
if (!lock || my_hash_insert(&m_locks, (uchar*)lock))
{
+ if (unused_lock)
+ {
+ /*
+ Note that we can't easily destroy an object from cache here as it
+ still might be referenced by other threads. So we simply put it
+ back into the cache.
+ */
+ m_unused_locks_cache.push_front(unused_lock);
+ }
+ else
+ {
+ MDL_lock::destroy(lock);
+ }
mysql_mutex_unlock(&m_mutex);
- MDL_lock::destroy(lock);
return NULL;
}
}
@@ -633,7 +754,7 @@ retry:
/**
Release mdl_locks.m_mutex mutex and lock MDL_lock::m_rwlock for lock
object from the hash. Handle situation when object was released
- while the held no mutex.
+ while we held no locks.
@retval FALSE - Success.
@retval TRUE - Object was released while we held no mutex, caller
@@ -642,6 +763,8 @@ retry:
bool MDL_map::move_from_hash_to_lock_mutex(MDL_lock *lock)
{
+ ulonglong version;
+
DBUG_ASSERT(! lock->m_is_destroyed);
mysql_mutex_assert_owner(&m_mutex);
@@ -651,26 +774,50 @@ bool MDL_map::move_from_hash_to_lock_mutex(MDL_lock *lock)
m_is_destroyed is FALSE.
*/
lock->m_ref_usage++;
+ /* Read value of the version counter under protection of m_mutex lock. */
+ version= lock->m_version;
mysql_mutex_unlock(&m_mutex);
mysql_prlock_wrlock(&lock->m_rwlock);
lock->m_ref_release++;
- if (unlikely(lock->m_is_destroyed))
+
+ if (unlikely(lock->m_version != version))
{
/*
- Object was released while we held no mutex, we need to
- release it if no others hold references to it, while our own
- reference count ensured that the object as such haven't got
- its memory released yet. We can also safely compare
- m_ref_usage and m_ref_release since the object is no longer
- present in the hash so no one will be able to find it and
- increment m_ref_usage anymore.
+ If the current value of version differs from one that was read while
+ we held m_mutex mutex, this MDL_lock object was moved to the unused
+ objects list or destroyed while we held no locks.
+ We should retry our search. But first we should destroy the MDL_lock
+ object if necessary.
*/
- uint ref_usage= lock->m_ref_usage;
- uint ref_release= lock->m_ref_release;
- mysql_prlock_unlock(&lock->m_rwlock);
- if (ref_usage == ref_release)
- MDL_lock::destroy(lock);
+ if (unlikely(lock->m_is_destroyed))
+ {
+ /*
+ Object was released while we held no locks, we need to
+ release it if no others hold references to it, while our own
+ reference count ensured that the object as such haven't got
+ its memory released yet. We can also safely compare
+ m_ref_usage and m_ref_release since the object is no longer
+ present in the hash (or unused objects list) so no one will
+ be able to find it and increment m_ref_usage anymore.
+ */
+ uint ref_usage= lock->m_ref_usage;
+ uint ref_release= lock->m_ref_release;
+ mysql_prlock_unlock(&lock->m_rwlock);
+ if (ref_usage == ref_release)
+ MDL_lock::destroy(lock);
+ }
+ else
+ {
+ /*
+ Object was not destroyed but its version has changed.
+ This means that it was moved to the unused objects list
+ (and even might be already re-used). So now it might
+ correspond to a different key, therefore we should simply
+ retry our search.
+ */
+ mysql_prlock_unlock(&lock->m_rwlock);
+ }
return TRUE;
}
return FALSE;
@@ -685,8 +832,6 @@ bool MDL_map::move_from_hash_to_lock_mutex(MDL_lock *lock)
void MDL_map::remove(MDL_lock *lock)
{
- uint ref_usage, ref_release;
-
if (lock->key.mdl_namespace() == MDL_key::GLOBAL ||
lock->key.mdl_namespace() == MDL_key::COMMIT)
{
@@ -698,31 +843,65 @@ void MDL_map::remove(MDL_lock *lock)
return;
}
- /*
- Destroy the MDL_lock object, but ensure that anyone that is
- holding a reference to the object is not remaining, if so he
- has the responsibility to release it.
-
- Setting of m_is_destroyed to TRUE while holding _both_
- mdl_locks.m_mutex and MDL_lock::m_rwlock mutexes transfers the
- protection of m_ref_usage from mdl_locks.m_mutex to
- MDL_lock::m_rwlock while removal of object from the hash makes
- it read-only. Therefore whoever acquires MDL_lock::m_rwlock next
- will see most up to date version of m_ref_usage.
-
- This means that when m_is_destroyed is TRUE and we hold the
- MDL_lock::m_rwlock we can safely read the m_ref_usage
- member.
- */
mysql_mutex_lock(&m_mutex);
my_hash_delete(&m_locks, (uchar*) lock);
- lock->m_is_destroyed= TRUE;
- ref_usage= lock->m_ref_usage;
- ref_release= lock->m_ref_release;
- mysql_prlock_unlock(&lock->m_rwlock);
- mysql_mutex_unlock(&m_mutex);
- if (ref_usage == ref_release)
- MDL_lock::destroy(lock);
+ /*
+ To let threads holding references to the MDL_lock object know that it was
+ moved to the list of unused objects or destroyed, we increment the version
+ counter under protection of both MDL_map::m_mutex and MDL_lock::m_rwlock
+ locks. This allows us to read the version value while having either one
+ of those locks.
+ */
+ lock->m_version++;
+
+ if ((lock->key.mdl_namespace() != MDL_key::SCHEMA) &&
+ (m_unused_locks_cache.elements() < mdl_locks_cache_size))
+ {
+ /*
+ This is an object of MDL_object_lock type and the cache of unused
+ objects has not reached its maximum size yet. So instead of destroying
+ object we move it to the list of unused objects to allow its later
+ re-use with possibly different key. Any threads holding references to
+ this object (owning MDL_map::m_mutex or MDL_lock::m_rwlock) will notice
+ this thanks to the fact that we have changed the MDL_lock::m_version
+ counter.
+ */
+ DBUG_ASSERT(lock->key.mdl_namespace() != MDL_key::GLOBAL &&
+ lock->key.mdl_namespace() != MDL_key::COMMIT);
+
+ m_unused_locks_cache.push_front((MDL_object_lock*)lock);
+ mysql_mutex_unlock(&m_mutex);
+ mysql_prlock_unlock(&lock->m_rwlock);
+ }
+ else
+ {
+ /*
+ Destroy the MDL_lock object, but ensure that anyone that is
+ holding a reference to the object is not remaining, if so he
+ has the responsibility to release it.
+
+ Setting of m_is_destroyed to TRUE while holding _both_
+ mdl_locks.m_mutex and MDL_lock::m_rwlock mutexes transfers the
+ protection of m_ref_usage from mdl_locks.m_mutex to
+ MDL_lock::m_rwlock while removal of the object from the hash
+ (and cache of unused objects) makes it read-only. Therefore
+ whoever acquires MDL_lock::m_rwlock next will see the most up
+ to date version of m_ref_usage.
+
+ This means that when m_is_destroyed is TRUE and we hold the
+ MDL_lock::m_rwlock we can safely read the m_ref_usage
+ member.
+ */
+ uint ref_usage, ref_release;
+
+ lock->m_is_destroyed= TRUE;
+ ref_usage= lock->m_ref_usage;
+ ref_release= lock->m_ref_release;
+ mysql_mutex_unlock(&m_mutex);
+ mysql_prlock_unlock(&lock->m_rwlock);
+ if (ref_usage == ref_release)
+ MDL_lock::destroy(lock);
+ }
}
@@ -820,9 +999,6 @@ void MDL_request::init(const MDL_key *key_arg,
Auxiliary functions needed for creation/destruction of MDL_lock objects.
@note Also chooses an MDL_lock descendant appropriate for object namespace.
-
- @todo This naive implementation should be replaced with one that saves
- on memory allocation by reusing released objects.
*/
inline MDL_lock *MDL_lock::create(const MDL_key *mdl_key)
diff --git a/sql/mdl.h b/sql/mdl.h
index fb2de45b831..5c1af23f5e2 100644
--- a/sql/mdl.h
+++ b/sql/mdl.h
@@ -851,4 +851,12 @@ extern "C" void thd_exit_cond(MYSQL_THD thd, const char *old_msg);
extern mysql_mutex_t LOCK_open;
#endif
+
+/*
+ Start-up parameter for the maximum size of the unused MDL_lock objects cache
+ and a constant for its default value.
+*/
+extern ulong mdl_locks_cache_size;
+static const ulong MDL_LOCKS_CACHE_SIZE_DEFAULT = 1024;
+
#endif
diff --git a/sql/rpl_injector.cc b/sql/rpl_injector.cc
index d4e3e0fd7b4..c5fdc521f11 100644
--- a/sql/rpl_injector.cc
+++ b/sql/rpl_injector.cc
@@ -237,7 +237,7 @@ int injector::record_incident(THD *thd, Incident incident)
Incident_log_event ev(thd, incident);
if (int error= mysql_bin_log.write(&ev))
return error;
- return mysql_bin_log.rotate_and_purge(RP_FORCE_ROTATE);
+ return mysql_bin_log.rotate_and_purge(true);
}
int injector::record_incident(THD *thd, Incident incident, LEX_STRING const message)
@@ -245,5 +245,5 @@ int injector::record_incident(THD *thd, Incident incident, LEX_STRING const mess
Incident_log_event ev(thd, incident, message);
if (int error= mysql_bin_log.write(&ev))
return error;
- return mysql_bin_log.rotate_and_purge(RP_FORCE_ROTATE);
+ return mysql_bin_log.rotate_and_purge(true);
}
diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc
index e1c764a4248..3371406ba65 100644
--- a/sql/rpl_rli.cc
+++ b/sql/rpl_rli.cc
@@ -52,7 +52,8 @@ Relay_log_info::Relay_log_info(bool is_slave_recovery)
inited(0), abort_slave(0), slave_running(0), until_condition(UNTIL_NONE),
until_log_pos(0), retried_trans(0),
tables_to_lock(0), tables_to_lock_count(0),
- last_event_start_time(0), m_flags(0)
+ last_event_start_time(0), m_flags(0), row_stmt_start_timestamp(0),
+ long_find_row_note_printed(false)
{
DBUG_ENTER("Relay_log_info::Relay_log_info");
@@ -1245,6 +1246,15 @@ void Relay_log_info::cleanup_context(THD *thd, bool error)
*/
thd->variables.option_bits&= ~OPTION_NO_FOREIGN_KEY_CHECKS;
thd->variables.option_bits&= ~OPTION_RELAXED_UNIQUE_CHECKS;
+
+ /*
+ Reset state related to long_find_row notes in the error log:
+ - timestamp
+ - flag that decides whether the slave prints or not
+ */
+ reset_row_stmt_start_timestamp();
+ unset_long_find_row_note_printed();
+
DBUG_VOID_RETURN;
}
diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h
index fb62279c4c1..c8a0f549e0f 100644
--- a/sql/rpl_rli.h
+++ b/sql/rpl_rli.h
@@ -452,8 +452,49 @@ public:
(m_flags & (1UL << IN_STMT));
}
+ time_t get_row_stmt_start_timestamp()
+ {
+ return row_stmt_start_timestamp;
+ }
+
+ time_t set_row_stmt_start_timestamp()
+ {
+ if (row_stmt_start_timestamp == 0)
+ row_stmt_start_timestamp= my_time(0);
+
+ return row_stmt_start_timestamp;
+ }
+
+ void reset_row_stmt_start_timestamp()
+ {
+ row_stmt_start_timestamp= 0;
+ }
+
+ void set_long_find_row_note_printed()
+ {
+ long_find_row_note_printed= true;
+ }
+
+ void unset_long_find_row_note_printed()
+ {
+ long_find_row_note_printed= false;
+ }
+
+ bool is_long_find_row_note_printed()
+ {
+ return long_find_row_note_printed;
+ }
+
private:
+
uint32 m_flags;
+
+ /*
+ Runtime state for printing a note when slave is taking
+ too long while processing a row event.
+ */
+ time_t row_stmt_start_timestamp;
+ bool long_find_row_note_printed;
};
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 9f13908d31c..36091546ff4 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -161,9 +161,6 @@ typedef struct st_user_var_events
bool unsigned_flag;
} BINLOG_USER_VAR_EVENT;
-#define RP_LOCK_LOG_IS_ALREADY_LOCKED 1
-#define RP_FORCE_ROTATE 2
-
/*
The COPY_INFO structure is used by INSERT/REPLACE code.
The schema of the row counting by the INSERT/INSERT ... ON DUPLICATE KEY
@@ -413,8 +410,9 @@ typedef struct system_variables
*/
ulong dynamic_variables_version;
char* dynamic_variables_ptr;
- uint dynamic_variables_head; /* largest valid variable offset */
- uint dynamic_variables_size; /* how many bytes are in use */
+ uint dynamic_variables_head; /* largest valid variable offset */
+ uint dynamic_variables_size; /* how many bytes are in use */
+ LIST *dynamic_variables_allocs; /* memory hunks for PLUGIN_VAR_MEMALLOC */
ulonglong max_heap_table_size;
ulonglong tmp_table_size;
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index eaf7f4c895b..c061a3f472c 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -77,6 +77,8 @@
#include "transaction.h"
#include "sql_audit.h"
+#include "debug_sync.h"
+
#ifndef EMBEDDED_LIBRARY
static bool delayed_get_table(THD *thd, MDL_request *grl_protection_request,
TABLE_LIST *table_list);
@@ -1541,6 +1543,8 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
error= HA_ERR_FOUND_DUPP_KEY; /* Database can't find key */
goto err;
}
+ DEBUG_SYNC(thd, "write_row_replace");
+
/* Read all columns for the row we are going to replace */
table->use_all_columns();
/*
@@ -1729,6 +1733,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
}
else if ((error=table->file->ha_write_row(table->record[0])))
{
+ DEBUG_SYNC(thd, "write_row_noreplace");
if (!info->ignore ||
table->file->is_fatal_error(error, HA_CHECK_DUP))
goto err;
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 3b5b5df788e..32ccb8f2c5f 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -2888,7 +2888,7 @@ end_with_restore_list:
{
Incident_log_event ev(thd, incident);
(void) mysql_bin_log.write(&ev); /* error is ignored */
- if (mysql_bin_log.rotate_and_purge(RP_FORCE_ROTATE))
+ if (mysql_bin_log.rotate_and_purge(true))
{
res= 1;
break;
diff --git a/sql/sql_plist.h b/sql/sql_plist.h
index ca1d15f3015..2b6f1067321 100644
--- a/sql/sql_plist.h
+++ b/sql/sql_plist.h
@@ -128,6 +128,15 @@ public:
}
inline T* front() { return m_first; }
inline const T *front() const { return m_first; }
+ inline T* pop_front()
+ {
+ T *result= front();
+
+ if (result)
+ remove(result);
+
+ return result;
+ }
void swap(I_P_List<T, B, C> &rhs)
{
swap_variables(T *, m_first, rhs.m_first);
diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc
index 5f5e73091ff..2b801b65a09 100644
--- a/sql/sql_plugin.cc
+++ b/sql/sql_plugin.cc
@@ -255,6 +255,13 @@ static bool register_builtin(struct st_mysql_plugin *, struct st_plugin_int *,
static void unlock_variables(THD *thd, struct system_variables *vars);
static void cleanup_variables(THD *thd, struct system_variables *vars);
static void plugin_vars_free_values(sys_var *vars);
+static bool plugin_var_memalloc_session_update(THD *thd,
+ struct st_mysql_sys_var *var,
+ char **dest, const char *value);
+static bool plugin_var_memalloc_global_update(THD *thd,
+ struct st_mysql_sys_var *var,
+ char **dest, const char *value);
+static void plugin_var_memalloc_free(struct system_variables *vars);
static void restore_pluginvar_names(sys_var *first);
static void plugin_opt_set_limits(struct my_option *,
const struct st_mysql_sys_var *);
@@ -2300,13 +2307,7 @@ static void update_func_longlong(THD *thd, struct st_mysql_sys_var *var,
static void update_func_str(THD *thd, struct st_mysql_sys_var *var,
void *tgt, const void *save)
{
- char *old= *(char **) tgt;
- *(char **)tgt= *(char **) save;
- if (var->flags & PLUGIN_VAR_MEMALLOC)
- {
- *(char **)tgt= my_strdup(*(char **) save, MYF(0));
- my_free(old);
- }
+ *(char **) tgt= *(char **) save;
}
@@ -2565,11 +2566,13 @@ static uchar *intern_sys_var_ptr(THD* thd, int offset, bool global_lock)
if ((pi->plugin_var->flags & PLUGIN_VAR_TYPEMASK) == PLUGIN_VAR_STR &&
pi->plugin_var->flags & PLUGIN_VAR_MEMALLOC)
{
- char **pp= (char**) (thd->variables.dynamic_variables_ptr +
- *(int*)(pi->plugin_var + 1));
- if ((*pp= *(char**) (global_system_variables.dynamic_variables_ptr +
- *(int*)(pi->plugin_var + 1))))
- *pp= my_strdup(*pp, MYF(MY_WME|MY_FAE));
+ int varoff= *(int *) (pi->plugin_var + 1);
+ char **thdvar= (char **) (thd->variables.
+ dynamic_variables_ptr + varoff);
+ char **sysvar= (char **) (global_system_variables.
+ dynamic_variables_ptr + varoff);
+ *thdvar= NULL;
+ plugin_var_memalloc_session_update(thd, NULL, thdvar, *sysvar);
}
}
@@ -2675,33 +2678,8 @@ static void unlock_variables(THD *thd, struct system_variables *vars)
*/
static void cleanup_variables(THD *thd, struct system_variables *vars)
{
- st_bookmark *v;
- sys_var_pluginvar *pivar;
- sys_var *var;
- int flags;
- uint idx;
-
- mysql_rwlock_rdlock(&LOCK_system_variables_hash);
- for (idx= 0; idx < bookmark_hash.records; idx++)
- {
- v= (st_bookmark*) my_hash_element(&bookmark_hash, idx);
- if (v->version > vars->dynamic_variables_version ||
- !(var= intern_find_sys_var(v->key + 1, v->name_len)) ||
- !(pivar= var->cast_pluginvar()) ||
- v->key[0] != (pivar->plugin_var->flags & PLUGIN_VAR_TYPEMASK))
- continue;
-
- flags= pivar->plugin_var->flags;
-
- if ((flags & PLUGIN_VAR_TYPEMASK) == PLUGIN_VAR_STR &&
- flags & PLUGIN_VAR_THDLOCAL && flags & PLUGIN_VAR_MEMALLOC)
- {
- char **ptr= (char**) pivar->real_value_ptr(thd, OPT_SESSION);
- my_free(*ptr);
- *ptr= NULL;
- }
- }
- mysql_rwlock_unlock(&LOCK_system_variables_hash);
+ if (thd)
+ plugin_var_memalloc_free(&thd->variables);
DBUG_ASSERT(vars->table_plugin == NULL);
@@ -2796,6 +2774,136 @@ static SHOW_TYPE pluginvar_show_type(st_mysql_sys_var *plugin_var)
}
+/**
+ Set value for thread local variable with PLUGIN_VAR_MEMALLOC flag.
+
+ @param[in] thd Thread context.
+ @param[in] var Plugin variable.
+ @param[in,out] dest Destination memory pointer.
+ @param[in] value '\0'-terminated new value.
+
+ Most plugin variable values are stored on dynamic_variables_ptr.
+ Releasing memory occupied by these values is as simple as freeing
+ dynamic_variables_ptr.
+
+ An exception to the rule are PLUGIN_VAR_MEMALLOC variables, which
+ are stored on individual memory hunks. All of these hunks has to
+ be freed when it comes to cleanup.
+
+ It may happen that a plugin was uninstalled and descriptors of
+ it's variables are lost. In this case it is impossible to locate
+ corresponding values.
+
+ In addition to allocating and setting variable value, new element
+ is added to dynamic_variables_allocs list. When thread is done, it
+ has to call plugin_var_memalloc_free() to release memory used by
+ PLUGIN_VAR_MEMALLOC variables.
+
+ If var is NULL, variable update function is not called. This is
+ needed when we take snapshot of system variables during thread
+ initialization.
+
+ @note List element and variable value are stored on the same memory
+ hunk. List element is followed by variable value.
+
+ @return Completion status
+ @retval false Success
+ @retval true Failure
+*/
+
+static bool plugin_var_memalloc_session_update(THD *thd,
+ struct st_mysql_sys_var *var,
+ char **dest, const char *value)
+
+{
+ LIST *old_element= NULL;
+ struct system_variables *vars= &thd->variables;
+ DBUG_ENTER("plugin_var_memalloc_session_update");
+
+ if (value)
+ {
+ size_t length= strlen(value) + 1;
+ LIST *element;
+ if (!(element= (LIST *) my_malloc(sizeof(LIST) + length, MYF(MY_WME))))
+ DBUG_RETURN(true);
+ memcpy(element + 1, value, length);
+ value= (const char *) (element + 1);
+ vars->dynamic_variables_allocs= list_add(vars->dynamic_variables_allocs,
+ element);
+ }
+
+ if (*dest)
+ old_element= (LIST *) (*dest - sizeof(LIST));
+
+ if (var)
+ var->update(thd, var, (void **) dest, (const void *) &value);
+ else
+ *dest= (char *) value;
+
+ if (old_element)
+ {
+ vars->dynamic_variables_allocs= list_delete(vars->dynamic_variables_allocs,
+ old_element);
+ my_free(old_element);
+ }
+ DBUG_RETURN(false);
+}
+
+
+/**
+ Free all elements allocated by plugin_var_memalloc_session_update().
+
+ @param[in] vars system variables structure
+
+ @see plugin_var_memalloc_session_update
+*/
+
+static void plugin_var_memalloc_free(struct system_variables *vars)
+{
+ LIST *next, *root;
+ DBUG_ENTER("plugin_var_memalloc_free");
+ for (root= vars->dynamic_variables_allocs; root; root= next)
+ {
+ next= root->next;
+ my_free(root);
+ }
+ vars->dynamic_variables_allocs= NULL;
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Set value for global variable with PLUGIN_VAR_MEMALLOC flag.
+
+ @param[in] thd Thread context.
+ @param[in] var Plugin variable.
+ @param[in,out] dest Destination memory pointer.
+ @param[in] value '\0'-terminated new value.
+
+ @return Completion status
+ @retval false Success
+ @retval true Failure
+*/
+
+static bool plugin_var_memalloc_global_update(THD *thd,
+ struct st_mysql_sys_var *var,
+ char **dest, const char *value)
+{
+ char *old_value= *dest;
+ DBUG_ENTER("plugin_var_memalloc_global_update");
+
+ if (value && !(value= my_strdup(value, MYF(MY_WME))))
+ DBUG_RETURN(true);
+
+ var->update(thd, var, (void **) dest, (const void *) &value);
+
+ if (old_value)
+ my_free(old_value);
+
+ DBUG_RETURN(false);
+}
+
+
bool sys_var_pluginvar::check_update_type(Item_result type)
{
switch (plugin_var->flags & PLUGIN_VAR_TYPEMASK) {
@@ -2880,6 +2988,7 @@ bool sys_var_pluginvar::do_check(THD *thd, set_var *var)
bool sys_var_pluginvar::session_update(THD *thd, set_var *var)
{
+ bool rc= false;
DBUG_ASSERT(!is_readonly());
DBUG_ASSERT(plugin_var->flags & PLUGIN_VAR_THDLOCAL);
DBUG_ASSERT(thd == current_thd);
@@ -2889,13 +2998,20 @@ bool sys_var_pluginvar::session_update(THD *thd, set_var *var)
const void *src= var->value ? (void*)&var->save_result
: (void*)real_value_ptr(thd, OPT_GLOBAL);
mysql_mutex_unlock(&LOCK_global_system_variables);
- plugin_var->update(thd, plugin_var, tgt, src);
- return false;
+ if ((plugin_var->flags & PLUGIN_VAR_TYPEMASK) == PLUGIN_VAR_STR &&
+ plugin_var->flags & PLUGIN_VAR_MEMALLOC)
+ rc= plugin_var_memalloc_session_update(thd, plugin_var, (char **) tgt,
+ *(const char **) src);
+ else
+ plugin_var->update(thd, plugin_var, tgt, src);
+
+ return rc;
}
bool sys_var_pluginvar::global_update(THD *thd, set_var *var)
{
+ bool rc= false;
DBUG_ASSERT(!is_readonly());
mysql_mutex_assert_owner(&LOCK_global_system_variables);
@@ -2952,9 +3068,14 @@ bool sys_var_pluginvar::global_update(THD *thd, set_var *var)
}
}
- plugin_var->update(thd, plugin_var, tgt, src);
+ if ((plugin_var->flags & PLUGIN_VAR_TYPEMASK) == PLUGIN_VAR_STR &&
+ plugin_var->flags & PLUGIN_VAR_MEMALLOC)
+ rc= plugin_var_memalloc_global_update(thd, plugin_var, (char **) tgt,
+ *(const char **) src);
+ else
+ plugin_var->update(thd, plugin_var, tgt, src);
- return false;
+ return rc;
}
diff --git a/sql/sql_reload.cc b/sql/sql_reload.cc
index ae4c3999838..b0665a9ea6b 100644
--- a/sql/sql_reload.cc
+++ b/sql/sql_reload.cc
@@ -148,7 +148,7 @@ bool reload_acl_and_cache(THD *thd, unsigned long options,
tmp_write_to_binlog= 0;
if (mysql_bin_log.is_open())
{
- if (mysql_bin_log.rotate_and_purge(RP_FORCE_ROTATE))
+ if (mysql_bin_log.rotate_and_purge(true))
*write_to_binlog= -1;
}
}
diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc
index 5d6d8bb13e3..00c85d8eb43 100644
--- a/sql/sql_repl.cc
+++ b/sql/sql_repl.cc
@@ -1038,12 +1038,9 @@ err:
detailing the fatal error message with coordinates
of the last position read.
*/
- char b_start[FN_REFLEN], b_end[FN_REFLEN];
- fn_format(b_start, coord->file_name, "", "", MY_REPLACE_DIR);
- fn_format(b_end, log_file_name, "", "", MY_REPLACE_DIR);
my_snprintf(error_text, sizeof(error_text), fmt, errmsg,
- b_start, (llstr(coord->pos, llbuff1), llbuff1),
- b_end, (llstr(my_b_tell(&log), llbuff2), llbuff2));
+ coord->file_name, (llstr(coord->pos, llbuff1), llbuff1),
+ log_file_name, (llstr(my_b_tell(&log), llbuff2), llbuff2));
}
else
strcpy(error_text, errmsg);
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 9a68420e5fc..5363737998b 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -5958,6 +5958,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
case ENABLE:
if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN))
goto err;
+ DEBUG_SYNC(thd,"alter_table_enable_indexes");
DBUG_EXECUTE_IF("sleep_alter_enable_indexes", my_sleep(6000000););
error= table->file->ha_enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
break;
diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc
index da8e61da52e..7a04015dc93 100644
--- a/sql/sys_vars.cc
+++ b/sql/sys_vars.cc
@@ -1156,6 +1156,12 @@ static Sys_var_ulonglong Sys_max_heap_table_size(
VALID_RANGE(16384, (ulonglong)~(intptr)0), DEFAULT(16*1024*1024),
BLOCK_SIZE(1024));
+static Sys_var_ulong Sys_metadata_locks_cache_size(
+ "metadata_locks_cache_size", "Size of unused metadata locks cache",
+ READ_ONLY GLOBAL_VAR(mdl_locks_cache_size), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(1, 1024*1024), DEFAULT(MDL_LOCKS_CACHE_SIZE_DEFAULT),
+ BLOCK_SIZE(1));
+
static Sys_var_ulong Sys_pseudo_thread_id(
"pseudo_thread_id",
"This variable is for internal server use",
diff --git a/storage/innobase/btr/btr0pcur.c b/storage/innobase/btr/btr0pcur.c
index cbb0d21a7ed..57d9752649f 100644
--- a/storage/innobase/btr/btr0pcur.c
+++ b/storage/innobase/btr/btr0pcur.c
@@ -247,6 +247,8 @@ btr_pcur_restore_position_func(
cursor->rel_pos == BTR_PCUR_BEFORE_FIRST_IN_TREE,
index, latch_mode, btr_pcur_get_btr_cur(cursor), mtr);
+ cursor->latch_mode = latch_mode;
+ cursor->pos_state = BTR_PCUR_IS_POSITIONED;
cursor->block_when_stored = btr_pcur_get_block(cursor);
return(FALSE);
diff --git a/storage/innobase/buf/buf0buf.c b/storage/innobase/buf/buf0buf.c
index 30f37a2f89a..1fcc2107f18 100644
--- a/storage/innobase/buf/buf0buf.c
+++ b/storage/innobase/buf/buf0buf.c
@@ -839,6 +839,16 @@ pfs_register_buffer_block(
rwlock->pfs_psi = (PSI_server)
? PSI_server->init_rwlock(buf_block_lock_key, rwlock)
: NULL;
+
+# ifdef UNIV_SYNC_DEBUG
+ rwlock = &block->debug_latch;
+ ut_a(!rwlock->pfs_psi);
+ rwlock->pfs_psi = (PSI_server)
+ ? PSI_server->init_rwlock(buf_block_debug_latch_key,
+ rwlock)
+ : NULL;
+# endif /* UNIV_SYNC_DEBUG */
+
# endif /* UNIV_PFS_RWLOCK */
block++;
}
@@ -895,17 +905,24 @@ buf_block_init(
mutex_create(PFS_NOT_INSTRUMENTED, &block->mutex, SYNC_BUF_BLOCK);
rw_lock_create(PFS_NOT_INSTRUMENTED, &block->lock, SYNC_LEVEL_VARYING);
+
+# ifdef UNIV_SYNC_DEBUG
+ rw_lock_create(PFS_NOT_INSTRUMENTED,
+ &block->debug_latch, SYNC_NO_ORDER_CHECK);
+# endif /* UNIV_SYNC_DEBUG */
+
#else /* PFS_SKIP_BUFFER_MUTEX_RWLOCK || PFS_GROUP_BUFFER_SYNC */
mutex_create(buffer_block_mutex_key, &block->mutex, SYNC_BUF_BLOCK);
rw_lock_create(buf_block_lock_key, &block->lock, SYNC_LEVEL_VARYING);
+
+# ifdef UNIV_SYNC_DEBUG
+ rw_lock_create(buf_block_debug_latch_key,
+ &block->debug_latch, SYNC_NO_ORDER_CHECK);
+# endif /* UNIV_SYNC_DEBUG */
#endif /* PFS_SKIP_BUFFER_MUTEX_RWLOCK || PFS_GROUP_BUFFER_SYNC */
ut_ad(rw_lock_validate(&(block->lock)));
-#ifdef UNIV_SYNC_DEBUG
- rw_lock_create(buf_block_debug_latch_key,
- &block->debug_latch, SYNC_NO_ORDER_CHECK);
-#endif /* UNIV_SYNC_DEBUG */
}
/********************************************************************//**
diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc
index f6156471380..4e01f7e1cf4 100644
--- a/storage/innobase/handler/ha_innodb.cc
+++ b/storage/innobase/handler/ha_innodb.cc
@@ -5063,8 +5063,7 @@ no_commit:
switch (sql_command) {
case SQLCOM_LOAD:
- if ((trx->duplicates
- & (TRX_DUP_IGNORE | TRX_DUP_REPLACE))) {
+ if (trx->duplicates) {
goto set_max_autoinc;
}
@@ -5340,8 +5339,7 @@ ha_innobase::update_row(
&& table->next_number_field
&& new_row == table->record[0]
&& thd_sql_command(user_thd) == SQLCOM_INSERT
- && (trx->duplicates & (TRX_DUP_IGNORE | TRX_DUP_REPLACE))
- == TRX_DUP_IGNORE) {
+ && trx->duplicates) {
ulonglong auto_inc;
ulonglong col_max_value;
@@ -5701,6 +5699,7 @@ ha_innobase::index_read(
(byte*) key_ptr,
(ulint) key_len,
prebuilt->trx);
+ DBUG_ASSERT(prebuilt->search_tuple->n_fields > 0);
} else {
/* We position the cursor to the last or the first entry
in the index */
@@ -5807,13 +5806,13 @@ ha_innobase::innobase_get_index(
table. Only print message if the index translation
table exists */
if (share->idx_trans_tbl.index_mapping) {
- sql_print_error("InnoDB could not find "
- "index %s key no %u for "
- "table %s through its "
- "index translation table",
- key ? key->name : "NULL",
- keynr,
- prebuilt->table->name);
+ sql_print_warning("InnoDB could not find "
+ "index %s key no %u for "
+ "table %s through its "
+ "index translation table",
+ key ? key->name : "NULL",
+ keynr,
+ prebuilt->table->name);
}
index = dict_table_get_index_on_name(prebuilt->table,
@@ -7528,6 +7527,7 @@ ha_innobase::records_in_range(
mem_heap_t* heap;
DBUG_ENTER("records_in_range");
+ DBUG_ASSERT(min_key || max_key);
ut_a(prebuilt->trx == thd_to_trx(ha_thd()));
@@ -7577,6 +7577,9 @@ ha_innobase::records_in_range(
(const uchar*) 0),
(ulint) (min_key ? min_key->length : 0),
prebuilt->trx);
+ DBUG_ASSERT(min_key
+ ? range_start->n_fields > 0
+ : range_start->n_fields == 0);
row_sel_convert_mysql_key_to_innobase(
range_end, (byte*) key_val_buff2,
@@ -7585,6 +7588,9 @@ ha_innobase::records_in_range(
(const uchar*) 0),
(ulint) (max_key ? max_key->length : 0),
prebuilt->trx);
+ DBUG_ASSERT(max_key
+ ? range_end->n_fields > 0
+ : range_end->n_fields == 0);
mode1 = convert_search_mode_to_innobase(min_key ? min_key->flag :
HA_READ_KEY_EXACT);
@@ -8286,7 +8292,10 @@ ha_innobase::check(
putc('\n', stderr);
#endif
- if (!btr_validate_index(index, prebuilt->trx)) {
+ /* If this is an index being created, break */
+ if (*index->name == TEMP_INDEX_PREFIX) {
+ break;
+ } else if (!btr_validate_index(index, prebuilt->trx)) {
is_ok = FALSE;
innobase_format_name(
@@ -8835,6 +8844,7 @@ ha_innobase::extra(
break;
case HA_EXTRA_RESET_STATE:
reset_template(prebuilt);
+ thd_to_trx(ha_thd())->duplicates = 0;
break;
case HA_EXTRA_NO_KEYREAD:
prebuilt->read_just_key = 0;
@@ -8852,19 +8862,18 @@ ha_innobase::extra(
parameters below. We must not invoke update_thd()
either, because the calling threads may change.
CAREFUL HERE, OR MEMORY CORRUPTION MAY OCCUR! */
- case HA_EXTRA_IGNORE_DUP_KEY:
+ case HA_EXTRA_INSERT_WITH_UPDATE:
thd_to_trx(ha_thd())->duplicates |= TRX_DUP_IGNORE;
break;
+ case HA_EXTRA_NO_IGNORE_DUP_KEY:
+ thd_to_trx(ha_thd())->duplicates &= ~TRX_DUP_IGNORE;
+ break;
case HA_EXTRA_WRITE_CAN_REPLACE:
thd_to_trx(ha_thd())->duplicates |= TRX_DUP_REPLACE;
break;
case HA_EXTRA_WRITE_CANNOT_REPLACE:
thd_to_trx(ha_thd())->duplicates &= ~TRX_DUP_REPLACE;
break;
- case HA_EXTRA_NO_IGNORE_DUP_KEY:
- thd_to_trx(ha_thd())->duplicates &=
- ~(TRX_DUP_IGNORE | TRX_DUP_REPLACE);
- break;
default:/* Do nothing */
;
}
diff --git a/storage/innobase/ibuf/ibuf0ibuf.c b/storage/innobase/ibuf/ibuf0ibuf.c
index 0676a7be0f7..47ec1365cb8 100644
--- a/storage/innobase/ibuf/ibuf0ibuf.c
+++ b/storage/innobase/ibuf/ibuf0ibuf.c
@@ -254,11 +254,20 @@ ibuf_count_check(
list of the ibuf */
/* @} */
+#define IBUF_REC_FIELD_SPACE 0 /*!< in the pre-4.1 format,
+ the page number. later, the space_id */
+#define IBUF_REC_FIELD_MARKER 1 /*!< starting with 4.1, a marker
+ consisting of 1 byte that is 0 */
+#define IBUF_REC_FIELD_PAGE 2 /*!< starting with 4.1, the
+ page number */
+#define IBUF_REC_FIELD_METADATA 3 /* the metadata field */
+#define IBUF_REC_FIELD_USER 4 /* first user field */
+
/* Various constants for checking the type of an ibuf record and extracting
data from it. For details, see the description of the record format at the
top of this file. */
-/** @name Format of the fourth column of an insert buffer record
+/** @name Format of the IBUF_REC_FIELD_METADATA of an insert buffer record
The fourth column in the MySQL 5.5 format contains an operation
type, counter, and some flags. */
/* @{ */
@@ -1233,13 +1242,13 @@ ibuf_rec_get_page_no_func(
ut_ad(ibuf_inside(mtr));
ut_ad(rec_get_n_fields_old(rec) > 2);
- field = rec_get_nth_field_old(rec, 1, &len);
+ field = rec_get_nth_field_old(rec, IBUF_REC_FIELD_MARKER, &len);
if (len == 1) {
/* This is of the >= 4.1.x record format */
ut_a(trx_sys_multiple_tablespace_format);
- field = rec_get_nth_field_old(rec, 2, &len);
+ field = rec_get_nth_field_old(rec, IBUF_REC_FIELD_PAGE, &len);
} else {
ut_a(trx_doublewrite_must_reset_space_ids);
ut_a(!trx_sys_multiple_tablespace_format);
@@ -1279,13 +1288,13 @@ ibuf_rec_get_space_func(
ut_ad(ibuf_inside(mtr));
ut_ad(rec_get_n_fields_old(rec) > 2);
- field = rec_get_nth_field_old(rec, 1, &len);
+ field = rec_get_nth_field_old(rec, IBUF_REC_FIELD_MARKER, &len);
if (len == 1) {
/* This is of the >= 4.1.x record format */
ut_a(trx_sys_multiple_tablespace_format);
- field = rec_get_nth_field_old(rec, 0, &len);
+ field = rec_get_nth_field_old(rec, IBUF_REC_FIELD_SPACE, &len);
ut_a(len == 4);
return(mach_read_from_4(field));
@@ -1335,9 +1344,9 @@ ibuf_rec_get_info_func(
|| mtr_memo_contains_page(mtr, rec, MTR_MEMO_PAGE_S_FIX));
ut_ad(ibuf_inside(mtr));
fields = rec_get_n_fields_old(rec);
- ut_a(fields > 4);
+ ut_a(fields > IBUF_REC_FIELD_USER);
- types = rec_get_nth_field_old(rec, 3, &len);
+ types = rec_get_nth_field_old(rec, IBUF_REC_FIELD_METADATA, &len);
info_len_local = len % DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE;
@@ -1363,7 +1372,8 @@ ibuf_rec_get_info_func(
ut_a(op_local < IBUF_OP_COUNT);
ut_a((len - info_len_local) ==
- (fields - 4) * DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE);
+ (fields - IBUF_REC_FIELD_USER)
+ * DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE);
if (op) {
*op = op_local;
@@ -1407,7 +1417,7 @@ ibuf_rec_get_op_type_func(
ut_ad(ibuf_inside(mtr));
ut_ad(rec_get_n_fields_old(rec) > 2);
- (void) rec_get_nth_field_old(rec, 1, &len);
+ (void) rec_get_nth_field_old(rec, IBUF_REC_FIELD_MARKER, &len);
if (len > 1) {
/* This is a < 4.1.x format record */
@@ -1436,12 +1446,12 @@ ibuf_rec_get_counter(
const byte* ptr;
ulint len;
- if (rec_get_n_fields_old(rec) < 4) {
+ if (rec_get_n_fields_old(rec) <= IBUF_REC_FIELD_METADATA) {
return(ULINT_UNDEFINED);
}
- ptr = rec_get_nth_field_old(rec, 3, &len);
+ ptr = rec_get_nth_field_old(rec, IBUF_REC_FIELD_METADATA, &len);
if (len >= 2) {
@@ -1666,7 +1676,7 @@ ibuf_build_entry_from_ibuf_rec_func(
|| mtr_memo_contains_page(mtr, ibuf_rec, MTR_MEMO_PAGE_S_FIX));
ut_ad(ibuf_inside(mtr));
- data = rec_get_nth_field_old(ibuf_rec, 1, &len);
+ data = rec_get_nth_field_old(ibuf_rec, IBUF_REC_FIELD_MARKER, &len);
if (len > 1) {
/* This a < 4.1.x format record */
@@ -1678,13 +1688,13 @@ ibuf_build_entry_from_ibuf_rec_func(
ut_a(trx_sys_multiple_tablespace_format);
ut_a(*data == 0);
- ut_a(rec_get_n_fields_old(ibuf_rec) > 4);
+ ut_a(rec_get_n_fields_old(ibuf_rec) > IBUF_REC_FIELD_USER);
- n_fields = rec_get_n_fields_old(ibuf_rec) - 4;
+ n_fields = rec_get_n_fields_old(ibuf_rec) - IBUF_REC_FIELD_USER;
tuple = dtuple_create(heap, n_fields);
- types = rec_get_nth_field_old(ibuf_rec, 3, &len);
+ types = rec_get_nth_field_old(ibuf_rec, IBUF_REC_FIELD_METADATA, &len);
ibuf_rec_get_info(mtr, ibuf_rec, NULL, &comp, &info_len, NULL);
@@ -1698,7 +1708,8 @@ ibuf_build_entry_from_ibuf_rec_func(
for (i = 0; i < n_fields; i++) {
field = dtuple_get_nth_field(tuple, i);
- data = rec_get_nth_field_old(ibuf_rec, i + 4, &len);
+ data = rec_get_nth_field_old(
+ ibuf_rec, i + IBUF_REC_FIELD_USER, &len);
dfield_set_data(field, data, len);
@@ -1745,7 +1756,7 @@ ibuf_rec_get_size(
field_offset = 2;
types_offset = DATA_ORDER_NULL_TYPE_BUF_SIZE;
} else {
- field_offset = 4;
+ field_offset = IBUF_REC_FIELD_USER;
types_offset = DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE;
}
@@ -1806,7 +1817,7 @@ ibuf_rec_get_volume_func(
ut_ad(ibuf_inside(mtr));
ut_ad(rec_get_n_fields_old(ibuf_rec) > 2);
- data = rec_get_nth_field_old(ibuf_rec, 1, &len);
+ data = rec_get_nth_field_old(ibuf_rec, IBUF_REC_FIELD_MARKER, &len);
pre_4_1 = (len > 1);
if (pre_4_1) {
@@ -1829,7 +1840,8 @@ ibuf_rec_get_volume_func(
ut_a(trx_sys_multiple_tablespace_format);
ut_a(*data == 0);
- types = rec_get_nth_field_old(ibuf_rec, 3, &len);
+ types = rec_get_nth_field_old(
+ ibuf_rec, IBUF_REC_FIELD_METADATA, &len);
ibuf_rec_get_info(mtr, ibuf_rec, &op, &comp, &info_len, NULL);
@@ -1859,7 +1871,8 @@ ibuf_rec_get_volume_func(
}
types += info_len;
- n_fields = rec_get_n_fields_old(ibuf_rec) - 4;
+ n_fields = rec_get_n_fields_old(ibuf_rec)
+ - IBUF_REC_FIELD_USER;
}
data_size = ibuf_rec_get_size(ibuf_rec, types, n_fields, pre_4_1, comp);
@@ -1914,11 +1927,11 @@ ibuf_entry_build(
n_fields = dtuple_get_n_fields(entry);
- tuple = dtuple_create(heap, n_fields + 4);
+ tuple = dtuple_create(heap, n_fields + IBUF_REC_FIELD_USER);
/* 1) Space Id */
- field = dtuple_get_nth_field(tuple, 0);
+ field = dtuple_get_nth_field(tuple, IBUF_REC_FIELD_SPACE);
buf = mem_heap_alloc(heap, 4);
@@ -1928,7 +1941,7 @@ ibuf_entry_build(
/* 2) Marker byte */
- field = dtuple_get_nth_field(tuple, 1);
+ field = dtuple_get_nth_field(tuple, IBUF_REC_FIELD_MARKER);
buf = mem_heap_alloc(heap, 1);
@@ -1940,7 +1953,7 @@ ibuf_entry_build(
/* 3) Page number */
- field = dtuple_get_nth_field(tuple, 2);
+ field = dtuple_get_nth_field(tuple, IBUF_REC_FIELD_PAGE);
buf = mem_heap_alloc(heap, 4);
@@ -1988,10 +2001,7 @@ ibuf_entry_build(
ulint fixed_len;
const dict_field_t* ifield;
- /* We add 4 below because we have the 4 extra fields at the
- start of an ibuf record */
-
- field = dtuple_get_nth_field(tuple, i + 4);
+ field = dtuple_get_nth_field(tuple, i + IBUF_REC_FIELD_USER);
entry_field = dtuple_get_nth_field(entry, i);
dfield_copy(field, entry_field);
@@ -2024,13 +2034,13 @@ ibuf_entry_build(
/* 4) Type info, part #2 */
- field = dtuple_get_nth_field(tuple, 3);
+ field = dtuple_get_nth_field(tuple, IBUF_REC_FIELD_METADATA);
dfield_set_data(field, type_info, ti - type_info);
/* Set all the types in the new tuple binary */
- dtuple_set_types_binary(tuple, n_fields + 4);
+ dtuple_set_types_binary(tuple, n_fields + IBUF_REC_FIELD_USER);
return(tuple);
}
@@ -2090,11 +2100,11 @@ ibuf_new_search_tuple_build(
ut_a(trx_sys_multiple_tablespace_format);
- tuple = dtuple_create(heap, 3);
+ tuple = dtuple_create(heap, IBUF_REC_FIELD_METADATA);
/* Store the space id in tuple */
- field = dtuple_get_nth_field(tuple, 0);
+ field = dtuple_get_nth_field(tuple, IBUF_REC_FIELD_SPACE);
buf = mem_heap_alloc(heap, 4);
@@ -2104,7 +2114,7 @@ ibuf_new_search_tuple_build(
/* Store the new format record marker byte */
- field = dtuple_get_nth_field(tuple, 1);
+ field = dtuple_get_nth_field(tuple, IBUF_REC_FIELD_MARKER);
buf = mem_heap_alloc(heap, 1);
@@ -2114,7 +2124,7 @@ ibuf_new_search_tuple_build(
/* Store the page number in tuple */
- field = dtuple_get_nth_field(tuple, 2);
+ field = dtuple_get_nth_field(tuple, IBUF_REC_FIELD_PAGE);
buf = mem_heap_alloc(heap, 4);
@@ -2122,7 +2132,7 @@ ibuf_new_search_tuple_build(
dfield_set_data(field, buf, 4);
- dtuple_set_types_binary(tuple, 3);
+ dtuple_set_types_binary(tuple, IBUF_REC_FIELD_METADATA);
return(tuple);
}
@@ -2789,8 +2799,10 @@ ibuf_get_volume_buffered_hash(
ulint fold;
ulint bitmask;
- len = ibuf_rec_get_size(rec, types, rec_get_n_fields_old(rec) - 4,
- FALSE, comp);
+ len = ibuf_rec_get_size(
+ rec, types,
+ rec_get_n_fields_old(rec) - IBUF_REC_FIELD_USER,
+ FALSE, comp);
fold = ut_fold_binary(data, len);
hash += (fold / (CHAR_BIT * sizeof *hash)) % size;
@@ -2842,8 +2854,8 @@ ibuf_get_volume_buffered_count_func(
ut_ad(ibuf_inside(mtr));
n_fields = rec_get_n_fields_old(rec);
- ut_ad(n_fields > 4);
- n_fields -= 4;
+ ut_ad(n_fields > IBUF_REC_FIELD_USER);
+ n_fields -= IBUF_REC_FIELD_USER;
rec_get_nth_field_offs_old(rec, 1, &len);
/* This function is only invoked when buffering new
@@ -2852,7 +2864,7 @@ ibuf_get_volume_buffered_count_func(
ut_a(len == 1);
ut_ad(trx_sys_multiple_tablespace_format);
- types = rec_get_nth_field_old(rec, 3, &len);
+ types = rec_get_nth_field_old(rec, IBUF_REC_FIELD_METADATA, &len);
switch (UNIV_EXPECT(len % DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE,
IBUF_REC_INFO_SIZE)) {
@@ -3164,7 +3176,7 @@ ibuf_update_max_tablespace_id(void)
} else {
rec = btr_pcur_get_rec(&pcur);
- field = rec_get_nth_field_old(rec, 0, &len);
+ field = rec_get_nth_field_old(rec, IBUF_REC_FIELD_SPACE, &len);
ut_a(len == 4);
@@ -3186,10 +3198,12 @@ ibuf_update_max_tablespace_id(void)
ibuf_get_entry_counter_low_func(rec,space,page_no)
#endif
/****************************************************************//**
-Helper function for ibuf_set_entry_counter. Checks if rec is for (space,
-page_no), and if so, reads counter value from it and returns that + 1.
-Otherwise, returns 0.
-@return new counter value, or 0 */
+Helper function for ibuf_get_entry_counter_func. Checks if rec is for
+(space, page_no), and if so, reads counter value from it and returns
+that + 1.
+@retval ULINT_UNDEFINED if the record does not contain any counter
+@retval 0 if the record is not for (space, page_no)
+@retval 1 + previous counter value, otherwise */
static
ulint
ibuf_get_entry_counter_low_func(
@@ -3210,7 +3224,7 @@ ibuf_get_entry_counter_low_func(
|| mtr_memo_contains_page(mtr, rec, MTR_MEMO_PAGE_S_FIX));
ut_ad(rec_get_n_fields_old(rec) > 2);
- field = rec_get_nth_field_old(rec, 1, &len);
+ field = rec_get_nth_field_old(rec, IBUF_REC_FIELD_MARKER, &len);
if (UNIV_UNLIKELY(len != 1)) {
/* pre-4.1 format */
@@ -3223,7 +3237,7 @@ ibuf_get_entry_counter_low_func(
ut_a(trx_sys_multiple_tablespace_format);
/* Check the tablespace identifier. */
- field = rec_get_nth_field_old(rec, 0, &len);
+ field = rec_get_nth_field_old(rec, IBUF_REC_FIELD_SPACE, &len);
ut_a(len == 4);
if (mach_read_from_4(field) != space) {
@@ -3232,7 +3246,7 @@ ibuf_get_entry_counter_low_func(
}
/* Check the page offset. */
- field = rec_get_nth_field_old(rec, 2, &len);
+ field = rec_get_nth_field_old(rec, IBUF_REC_FIELD_PAGE, &len);
ut_a(len == 4);
if (mach_read_from_4(field) != page_no) {
@@ -3241,7 +3255,7 @@ ibuf_get_entry_counter_low_func(
}
/* Check if the record contains a counter field. */
- field = rec_get_nth_field_old(rec, 3, &len);
+ field = rec_get_nth_field_old(rec, IBUF_REC_FIELD_METADATA, &len);
switch (len % DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE) {
default:
@@ -3257,147 +3271,61 @@ ibuf_get_entry_counter_low_func(
}
}
+#ifdef UNIV_DEBUG
+# define ibuf_get_entry_counter(space,page_no,rec,mtr,exact_leaf) \
+ ibuf_get_entry_counter_func(space,page_no,rec,mtr,exact_leaf)
+#else /* UNIV_DEBUG */
+# define ibuf_get_entry_counter(space,page_no,rec,mtr,exact_leaf) \
+ ibuf_get_entry_counter_func(space,page_no,rec,exact_leaf)
+#endif
+
/****************************************************************//**
-Set the counter field in entry to the correct value based on the current
+Calculate the counter field for an entry based on the current
last record in ibuf for (space, page_no).
-@return FALSE if we should abort this insertion to ibuf */
+@return the counter field, or ULINT_UNDEFINED
+if we should abort this insertion to ibuf */
static
-ibool
-ibuf_set_entry_counter(
-/*===================*/
- dtuple_t* entry, /*!< in/out: entry to patch */
+ulint
+ibuf_get_entry_counter_func(
+/*========================*/
ulint space, /*!< in: space id of entry */
ulint page_no, /*!< in: page number of entry */
- btr_pcur_t* pcur, /*!< in: pcur positioned on the record
- found by btr_pcur_open(.., entry,
- PAGE_CUR_LE, ..., pcur, ...) */
- ibool is_optimistic, /*!< in: is this an optimistic insert */
- mtr_t* mtr) /*!< in: mtr */
+ const rec_t* rec, /*!< in: the record preceding the
+ insertion point */
+#ifdef UNIV_DEBUG
+ mtr_t* mtr, /*!< in: mini-transaction */
+#endif /* UNIV_DEBUG */
+ ibool only_leaf) /*!< in: TRUE if this is the only
+ leaf page that can contain entries
+ for (space,page_no), that is, there
+ was no exact match for (space,page_no)
+ in the node pointer */
{
- dfield_t* field;
- byte* data;
- ulint counter = 0;
-
- /* pcur points to either a user rec or to a page's infimum record. */
ut_ad(ibuf_inside(mtr));
- ut_ad(mtr_memo_contains(mtr, btr_pcur_get_block(pcur),
- MTR_MEMO_PAGE_X_FIX));
- ut_ad(page_validate(btr_pcur_get_page(pcur), ibuf->index));
-
- if (btr_pcur_is_on_user_rec(pcur)) {
-
- counter = ibuf_get_entry_counter_low(
- mtr, btr_pcur_get_rec(pcur), space, page_no);
-
- if (UNIV_UNLIKELY(counter == ULINT_UNDEFINED)) {
- /* The record lacks a counter field.
- Such old records must be merged before
- new records can be buffered. */
-
- return(FALSE);
- }
- } else if (btr_pcur_is_before_first_in_tree(pcur, mtr)) {
- /* Ibuf tree is either completely empty, or the insert
- position is at the very first record of a non-empty tree. In
- either case we have no previous records for (space,
- page_no). */
-
- counter = 0;
- } else if (btr_pcur_is_before_first_on_page(pcur)) {
- btr_cur_t* cursor = btr_pcur_get_btr_cur(pcur);
-
- if (cursor->low_match < 3) {
- /* If low_match < 3, we know that the father node
- pointer did not contain the searched for (space,
- page_no), which means that the search ended on the
- right page regardless of the counter value, and
- since we're at the infimum record, there are no
- existing records. */
-
- counter = 0;
- } else {
- rec_t* rec;
- const page_t* page;
- buf_block_t* block;
- page_t* prev_page;
- ulint prev_page_no;
-
- ut_a(cursor->ibuf_cnt != ULINT_UNDEFINED);
-
- page = btr_pcur_get_page(pcur);
- prev_page_no = btr_page_get_prev(page, mtr);
-
- ut_a(prev_page_no != FIL_NULL);
-
- block = buf_page_get(
- IBUF_SPACE_ID, 0, prev_page_no,
- RW_X_LATCH, mtr);
+ ut_ad(mtr_memo_contains_page(mtr, rec, MTR_MEMO_PAGE_X_FIX));
+ ut_ad(page_validate(page_align(rec), ibuf->index));
- buf_block_dbg_add_level(block, SYNC_IBUF_TREE_NODE);
-
- prev_page = buf_block_get_frame(block);
-
- rec = page_rec_get_prev(
- page_get_supremum_rec(prev_page));
-
- ut_ad(page_rec_is_user_rec(rec));
-
- counter = ibuf_get_entry_counter_low(
- mtr, rec, space, page_no);
-
- if (UNIV_UNLIKELY(counter == ULINT_UNDEFINED)) {
- /* The record lacks a counter field.
- Such old records must be merged before
- new records can be buffered. */
-
- return(FALSE);
- }
-
- if (counter < cursor->ibuf_cnt) {
- /* Search ended on the wrong page. */
-
- if (is_optimistic) {
- /* In an optimistic insert, we can
- shift the insert position to the left
- page, since it only needs an X-latch
- on the page itself, which the
- original search acquired for us. */
-
- btr_cur_position(
- ibuf->index, rec, block,
- btr_pcur_get_btr_cur(pcur));
- } else {
- /* We can't shift the insert
- position to the left page in a
- pessimistic insert since it would
- require an X-latch on the left
- page's left page, so we have to
- abort. */
-
- return(FALSE);
- }
- } else {
- /* The counter field in the father node is
- the same as we would insert; we don't know
- whether the insert should go to this page or
- the left page (the later fields can differ),
- so refuse the insert. */
-
- return(FALSE);
- }
- }
+ if (page_rec_is_supremum(rec)) {
+ /* This is just for safety. The record should be a
+ page infimum or a user record. */
+ ut_ad(0);
+ return(ULINT_UNDEFINED);
+ } else if (!page_rec_is_infimum(rec)) {
+ return(ibuf_get_entry_counter_low(mtr, rec, space, page_no));
+ } else if (only_leaf
+ || fil_page_get_prev(page_align(rec)) == FIL_NULL) {
+ /* The parent node pointer did not contain the
+ searched for (space, page_no), which means that the
+ search ended on the correct page regardless of the
+ counter value, and since we're at the infimum record,
+ there are no existing records. */
+ return(0);
} else {
- /* The cursor is not positioned at or before a user record. */
- return(FALSE);
+ /* We used to read the previous page here. It would
+ break the latching order, because the caller has
+ buffer-fixed an insert buffer bitmap page. */
+ return(ULINT_UNDEFINED);
}
-
- /* Patch counter value in already built entry. */
- field = dtuple_get_nth_field(entry, 3);
- data = dfield_get_data(field);
-
- mach_write_to_2(data + IBUF_REC_OFFSET_COUNTER, counter);
-
- return(TRUE);
}
/*********************************************************************//**
@@ -3604,16 +3532,27 @@ fail_exit:
}
}
- /* Patch correct counter value to the entry to insert. This can
- change the insert position, which can result in the need to abort in
- some cases. */
- if (!no_counter
- && !ibuf_set_entry_counter(ibuf_entry, space, page_no, &pcur,
- mode == BTR_MODIFY_PREV, &mtr)) {
+ if (!no_counter) {
+ /* Patch correct counter value to the entry to
+ insert. This can change the insert position, which can
+ result in the need to abort in some cases. */
+ ulint counter = ibuf_get_entry_counter(
+ space, page_no, btr_pcur_get_rec(&pcur), &mtr,
+ btr_pcur_get_btr_cur(&pcur)->low_match
+ < IBUF_REC_FIELD_METADATA);
+ dfield_t* field;
+
+ if (counter == ULINT_UNDEFINED) {
bitmap_fail:
- ibuf_mtr_commit(&bitmap_mtr);
+ ibuf_mtr_commit(&bitmap_mtr);
+ goto fail_exit;
+ }
- goto fail_exit;
+ field = dtuple_get_nth_field(
+ ibuf_entry, IBUF_REC_FIELD_METADATA);
+ mach_write_to_2(
+ (byte*) dfield_get_data(field)
+ + IBUF_REC_OFFSET_COUNTER, counter);
}
/* Set the bitmap bit denoting that the insert buffer contains
diff --git a/storage/innobase/include/btr0pcur.h b/storage/innobase/include/btr0pcur.h
index f605c476844..140f94466db 100644
--- a/storage/innobase/include/btr0pcur.h
+++ b/storage/innobase/include/btr0pcur.h
@@ -263,14 +263,6 @@ btr_pcur_commit_specify_mtr(
/*========================*/
btr_pcur_t* pcur, /*!< in: persistent cursor */
mtr_t* mtr); /*!< in: mtr to commit */
-/**************************************************************//**
-Tests if a cursor is detached: that is the latch mode is BTR_NO_LATCHES.
-@return TRUE if detached */
-UNIV_INLINE
-ibool
-btr_pcur_is_detached(
-/*=================*/
- btr_pcur_t* pcur); /*!< in: persistent cursor */
/*********************************************************//**
Moves the persistent cursor to the next record in the tree. If no records are
left, the cursor stays 'after last in tree'.
diff --git a/storage/innobase/include/btr0pcur.ic b/storage/innobase/include/btr0pcur.ic
index d86601e5a32..054ce753c7d 100644
--- a/storage/innobase/include/btr0pcur.ic
+++ b/storage/innobase/include/btr0pcur.ic
@@ -389,38 +389,6 @@ btr_pcur_commit_specify_mtr(
}
/**************************************************************//**
-Sets the pcur latch mode to BTR_NO_LATCHES. */
-UNIV_INLINE
-void
-btr_pcur_detach(
-/*============*/
- btr_pcur_t* pcur) /*!< in: persistent cursor */
-{
- ut_a(pcur->pos_state == BTR_PCUR_IS_POSITIONED);
-
- pcur->latch_mode = BTR_NO_LATCHES;
-
- pcur->pos_state = BTR_PCUR_WAS_POSITIONED;
-}
-
-/**************************************************************//**
-Tests if a cursor is detached: that is the latch mode is BTR_NO_LATCHES.
-@return TRUE if detached */
-UNIV_INLINE
-ibool
-btr_pcur_is_detached(
-/*=================*/
- btr_pcur_t* pcur) /*!< in: persistent cursor */
-{
- if (pcur->latch_mode == BTR_NO_LATCHES) {
-
- return(TRUE);
- }
-
- return(FALSE);
-}
-
-/**************************************************************//**
Sets the old_rec_buf field to NULL. */
UNIV_INLINE
void
diff --git a/storage/innobase/row/row0ins.c b/storage/innobase/row/row0ins.c
index 6d34e3217af..6e311ce2e80 100644
--- a/storage/innobase/row/row0ins.c
+++ b/storage/innobase/row/row0ins.c
@@ -1655,7 +1655,7 @@ row_ins_scan_sec_index_for_duplicate(
ulint n_fields_cmp;
btr_pcur_t pcur;
ulint err = DB_SUCCESS;
- unsigned allow_duplicates;
+ ulint allow_duplicates;
mtr_t mtr;
mem_heap_t* heap = NULL;
ulint offsets_[REC_OFFS_NORMAL_SIZE];
@@ -1686,7 +1686,7 @@ row_ins_scan_sec_index_for_duplicate(
btr_pcur_open(index, entry, PAGE_CUR_GE, BTR_SEARCH_LEAF, &pcur, &mtr);
- allow_duplicates = thr_get_trx(thr)->duplicates & TRX_DUP_IGNORE;
+ allow_duplicates = thr_get_trx(thr)->duplicates;
/* Scan index records and check if there is a duplicate */
@@ -1820,7 +1820,7 @@ row_ins_duplicate_error_in_clust(
sure that in roll-forward we get the same duplicate
errors as in original execution */
- if (trx->duplicates & TRX_DUP_IGNORE) {
+ if (trx->duplicates) {
/* If the SQL-query will update or replace
duplicate key we will take X-lock for
@@ -1864,7 +1864,7 @@ row_ins_duplicate_error_in_clust(
offsets = rec_get_offsets(rec, cursor->index, offsets,
ULINT_UNDEFINED, &heap);
- if (trx->duplicates & TRX_DUP_IGNORE) {
+ if (trx->duplicates) {
/* If the SQL-query will update or replace
duplicate key we will take X-lock for
diff --git a/storage/innobase/row/row0mysql.c b/storage/innobase/row/row0mysql.c
index d0bedd69842..d06411e09f0 100644
--- a/storage/innobase/row/row0mysql.c
+++ b/storage/innobase/row/row0mysql.c
@@ -1,6 +1,6 @@
/*****************************************************************************
-Copyright (c) 2000, 2010, Innobase Oy. All Rights Reserved.
+Copyright (c) 2000, 2011, 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
@@ -3995,6 +3995,7 @@ end:
trx->error_state = DB_SUCCESS;
trx_general_rollback_for_mysql(trx, NULL);
trx->error_state = DB_SUCCESS;
+ err = DB_ERROR;
goto funct_exit;
}