summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVasil Dimov <vasil.dimov@oracle.com>2010-06-02 15:40:49 +0300
committerVasil Dimov <vasil.dimov@oracle.com>2010-06-02 15:40:49 +0300
commit1a5cf2618b7c0b71280b981afdab79d7eaf33c91 (patch)
tree1e5ecb2e90082889f6a03476932607b3dbb0d901
parent2400a20c9b6efb909180e8386368da7acc128f84 (diff)
parentacf187a2760ed68b2bab55fd2b661842e53831c2 (diff)
downloadmariadb-git-1a5cf2618b7c0b71280b981afdab79d7eaf33c91.tar.gz
Merge mysql-trunk-innodb -> mysql-trunk-bugfixing
-rw-r--r--mysql-test/suite/innodb/r/innodb.result2
-rw-r--r--mysql-test/suite/innodb/r/innodb_bug53592.result43
-rw-r--r--mysql-test/suite/innodb/r/innodb_bug53674.result11
-rw-r--r--mysql-test/suite/innodb/t/innodb_bug53592.test82
-rw-r--r--mysql-test/suite/innodb/t/innodb_bug53674-master.opt1
-rw-r--r--mysql-test/suite/innodb/t/innodb_bug53674.test8
-rw-r--r--storage/innobase/fil/fil0fil.c33
-rw-r--r--storage/innobase/handler/ha_innodb.cc84
-rw-r--r--storage/innobase/handler/i_s.cc16
-rw-r--r--storage/innobase/include/db0err.h2
-rw-r--r--storage/innobase/include/fil0fil.h10
-rw-r--r--storage/innobase/include/lock0lock.h12
-rw-r--r--storage/innobase/include/row0mysql.h63
-rw-r--r--storage/innobase/include/sync0rw.h5
-rw-r--r--storage/innobase/lock/lock0lock.c138
-rw-r--r--storage/innobase/row/row0ins.c130
-rw-r--r--storage/innobase/row/row0mysql.c62
-rw-r--r--storage/innobase/row/row0sel.c132
-rw-r--r--storage/innobase/sync/sync0rw.c24
-rw-r--r--storage/innobase/ut/ut0ut.c2
20 files changed, 538 insertions, 322 deletions
diff --git a/mysql-test/suite/innodb/r/innodb.result b/mysql-test/suite/innodb/r/innodb.result
index 427df076eaf..2e5585ee7af 100644
--- a/mysql-test/suite/innodb/r/innodb.result
+++ b/mysql-test/suite/innodb/r/innodb.result
@@ -2648,7 +2648,7 @@ create table t3 (s1 varchar(2) binary,primary key (s1)) engine=innodb;
create table t4 (s1 char(2) binary,primary key (s1)) engine=innodb;
insert into t1 values (0x41),(0x4120),(0x4100);
insert into t2 values (0x41),(0x4120),(0x4100);
-ERROR 23000: Duplicate entry 'A\x00' for key 'PRIMARY'
+ERROR 23000: Duplicate entry 'A' for key 'PRIMARY'
insert into t2 values (0x41),(0x4120);
insert into t3 values (0x41),(0x4120),(0x4100);
ERROR 23000: Duplicate entry 'A ' for key 'PRIMARY'
diff --git a/mysql-test/suite/innodb/r/innodb_bug53592.result b/mysql-test/suite/innodb/r/innodb_bug53592.result
new file mode 100644
index 00000000000..63b30f50413
--- /dev/null
+++ b/mysql-test/suite/innodb/r/innodb_bug53592.result
@@ -0,0 +1,43 @@
+set old_alter_table=0;
+create table bug53592(a int) engine=innodb row_format=compact;
+alter table bug53592 add column b text charset utf8;
+alter table bug53592 add column c blob not null;
+create index bug53592_b on bug53592(b(81));
+create unique index bug53592_c on bug53592(c(1));
+replace into bug53592 values (),();
+Warnings:
+Warning 1364 Field 'c' doesn't have a default value
+check table bug53592;
+Table Op Msg_type Msg_text
+test.bug53592 check status OK
+drop table bug53592;
+set old_alter_table=1;
+create table bug53592(a int) engine=innodb row_format=compact;
+alter table bug53592 add column b text charset utf8;
+alter table bug53592 add column c blob not null;
+create index bug53592_b on bug53592(b(81));
+create unique index bug53592_c on bug53592(c(1));
+replace into bug53592 values (),();
+Warnings:
+Warning 1364 Field 'c' doesn't have a default value
+check table bug53592;
+Table Op Msg_type Msg_text
+test.bug53592 check status OK
+drop table bug53592;
+CREATE TABLE bug53592_1(
+col1 int, col2 int,
+PRIMARY KEY (col1, col2)
+) ENGINE=InnoDB;
+CREATE TABLE bug53592_2 (
+col int PRIMARY KEY,
+FOREIGN KEY (col) REFERENCES bug53592_1 (col1)
+ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB;
+INSERT INTO bug53592_1 VALUES (1, 2);
+INSERT INTO bug53592_1 VALUES (3, 4);
+INSERT INTO bug53592_2 VALUES (1);
+INSERT INTO bug53592_2 VALUES (3);
+UPDATE bug53592_1 SET col1 = 3 WHERE col2 = 2;
+ERROR 23000: Upholding foreign key constraints for table 'bug53592_1', entry '3-2', key 1 would lead to a duplicate entry
+drop table bug53592_2;
+drop table bug53592_1;
diff --git a/mysql-test/suite/innodb/r/innodb_bug53674.result b/mysql-test/suite/innodb/r/innodb_bug53674.result
new file mode 100644
index 00000000000..c4021c2e7cd
--- /dev/null
+++ b/mysql-test/suite/innodb/r/innodb_bug53674.result
@@ -0,0 +1,11 @@
+create table bug53674(a int)engine=innodb;
+insert into bug53674 values (1),(2);
+start transaction;
+select * from bug53674 for update;
+a
+1
+2
+select * from bug53674 where a=(select a from bug53674 where a > 1);
+a
+2
+drop table bug53674;
diff --git a/mysql-test/suite/innodb/t/innodb_bug53592.test b/mysql-test/suite/innodb/t/innodb_bug53592.test
new file mode 100644
index 00000000000..bc37743f6bf
--- /dev/null
+++ b/mysql-test/suite/innodb/t/innodb_bug53592.test
@@ -0,0 +1,82 @@
+# Testcase for Bug #53592 - "crash replacing duplicates into
+# table after fast alter table added unique key". The fix is to make
+# sure index number lookup should go through "index translation table".
+
+--source include/have_innodb.inc
+
+# Use FIC for index creation
+set old_alter_table=0;
+
+create table bug53592(a int) engine=innodb row_format=compact;
+
+alter table bug53592 add column b text charset utf8;
+
+alter table bug53592 add column c blob not null;
+
+# Create a non-unique nonclustered index
+create index bug53592_b on bug53592(b(81));
+
+# Create a unique index, this unique index should have smaller
+# index number than bug53592_b, since unique index ranks higher
+# than regular index does
+create unique index bug53592_c on bug53592(c(1));
+
+# This will trigger a dup key error and will require fetching
+# the index number through a index structure for the error reporting.
+# To get the correct index number, the code should go through index
+# translation table. Otherwise, it will get the wrong index
+# number and later trigger a server crash.
+replace into bug53592 values (),();
+
+check table bug53592;
+
+drop table bug53592;
+
+# Running the same set of test when "old_alter_table" is turned on
+set old_alter_table=1;
+
+create table bug53592(a int) engine=innodb row_format=compact;
+
+alter table bug53592 add column b text charset utf8;
+
+alter table bug53592 add column c blob not null;
+
+# Create a non-unique nonclustered index
+create index bug53592_b on bug53592(b(81));
+
+# Create a unique index
+create unique index bug53592_c on bug53592(c(1));
+
+# This will trigger a dup key error and will require fetching
+# the index number through a index structure for the error reporting.
+# To get the correct index number, the code should go through index
+# translation table. Otherwise, it will get the wrong index
+# number and later trigger a server crash.
+replace into bug53592 values (),();
+
+check table bug53592;
+drop table bug53592;
+
+# Test a dup key reported by foreign key constriant.
+CREATE TABLE bug53592_1(
+ col1 int, col2 int,
+ PRIMARY KEY (col1, col2)
+) ENGINE=InnoDB;
+
+CREATE TABLE bug53592_2 (
+ col int PRIMARY KEY,
+ FOREIGN KEY (col) REFERENCES bug53592_1 (col1)
+ ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB;
+
+INSERT INTO bug53592_1 VALUES (1, 2);
+INSERT INTO bug53592_1 VALUES (3, 4);
+
+INSERT INTO bug53592_2 VALUES (1);
+INSERT INTO bug53592_2 VALUES (3);
+
+--error ER_FOREIGN_DUPLICATE_KEY
+UPDATE bug53592_1 SET col1 = 3 WHERE col2 = 2;
+
+drop table bug53592_2;
+drop table bug53592_1;
diff --git a/mysql-test/suite/innodb/t/innodb_bug53674-master.opt b/mysql-test/suite/innodb/t/innodb_bug53674-master.opt
new file mode 100644
index 00000000000..f1cfd7ab6c7
--- /dev/null
+++ b/mysql-test/suite/innodb/t/innodb_bug53674-master.opt
@@ -0,0 +1 @@
+--log-bin --innodb-locks-unsafe-for-binlog --binlog-format=mixed
diff --git a/mysql-test/suite/innodb/t/innodb_bug53674.test b/mysql-test/suite/innodb/t/innodb_bug53674.test
new file mode 100644
index 00000000000..47f67f109c3
--- /dev/null
+++ b/mysql-test/suite/innodb/t/innodb_bug53674.test
@@ -0,0 +1,8 @@
+-- source include/have_innodb.inc
+
+create table bug53674(a int)engine=innodb;
+insert into bug53674 values (1),(2);
+start transaction;
+select * from bug53674 for update;
+select * from bug53674 where a=(select a from bug53674 where a > 1);
+drop table bug53674;
diff --git a/storage/innobase/fil/fil0fil.c b/storage/innobase/fil/fil0fil.c
index ce788ed0bfc..0d033c37879 100644
--- a/storage/innobase/fil/fil0fil.c
+++ b/storage/innobase/fil/fil0fil.c
@@ -3564,39 +3564,6 @@ next_datadir_item:
return(err);
}
-/********************************************************************//**
-If we need crash recovery, and we have called
-fil_load_single_table_tablespaces() and dict_load_single_table_tablespaces(),
-we can call this function to print an error message of orphaned .ibd files
-for which there is not a data dictionary entry with a matching table name
-and space id. */
-UNIV_INTERN
-void
-fil_print_orphaned_tablespaces(void)
-/*================================*/
-{
- fil_space_t* space;
-
- mutex_enter(&fil_system->mutex);
-
- space = UT_LIST_GET_FIRST(fil_system->space_list);
-
- while (space) {
- if (space->purpose == FIL_TABLESPACE && space->id != 0
- && !space->mark) {
- fputs("InnoDB: Warning: tablespace ", stderr);
- ut_print_filename(stderr, space->name);
- fprintf(stderr, " of id %lu has no matching table in\n"
- "InnoDB: the InnoDB data dictionary.\n",
- (ulong) space->id);
- }
-
- space = UT_LIST_GET_NEXT(space_list, space);
- }
-
- mutex_exit(&fil_system->mutex);
-}
-
/*******************************************************************//**
Returns TRUE if a single-table tablespace does not exist in the memory cache,
or is being deleted there.
diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc
index 0311aa60116..86246e62eee 100644
--- a/storage/innobase/handler/ha_innodb.cc
+++ b/storage/innobase/handler/ha_innodb.cc
@@ -6866,7 +6866,7 @@ ha_innobase::create(
(int) form->s->primary_key :
-1);
- /* Our function row_get_mysql_key_number_for_index assumes
+ /* Our function innobase_get_mysql_key_number_for_index assumes
the primary key is always number 0, if it exists */
ut_a(primary_key_no == -1 || primary_key_no == 0);
@@ -7583,6 +7583,84 @@ ha_innobase::read_time(
}
/*********************************************************************//**
+Calculates the key number used inside MySQL for an Innobase index. We will
+first check the "index translation table" for a match of the index to get
+the index number. If there does not exist an "index translation table",
+or not able to find the index in the translation table, then we will fall back
+to the traditional way of looping through dict_index_t list to find a
+match. In this case, we have to take into account if we generated a
+default clustered index for the table
+@return the key number used inside MySQL */
+static
+unsigned int
+innobase_get_mysql_key_number_for_index(
+/*====================================*/
+ INNOBASE_SHARE* share, /*!< in: share structure for index
+ translation table. */
+ const TABLE* table, /*!< in: table in MySQL data
+ dictionary */
+ dict_table_t* ib_table,/*!< in: table in Innodb data
+ dictionary */
+ const dict_index_t* index) /*!< in: index */
+{
+ const dict_index_t* ind;
+ unsigned int i;
+
+ ut_a(index);
+
+ /* If index does not belong to the table of share structure. Search
+ index->table instead */
+ if (index->table != ib_table
+ && innobase_strcasecmp(index->table->name, share->table_name)) {
+ i = 0;
+ ind = dict_table_get_first_index(index->table);
+
+ while (index != ind) {
+ ind = dict_table_get_next_index(ind);
+ i++;
+ }
+
+ if (row_table_got_default_clust_index(index->table)) {
+ ut_a(i > 0);
+ i--;
+ }
+
+ return(i);
+ }
+
+ /* If index translation table exists, we will first check
+ the index through index translation table for a match. */
+ if (share->idx_trans_tbl.index_mapping) {
+ for (i = 0; i < share->idx_trans_tbl.index_count; i++) {
+ if (share->idx_trans_tbl.index_mapping[i] == index) {
+ return(i);
+ }
+ }
+
+ /* Print an error message if we cannot find the index
+ ** in the "index translation table". */
+ sql_print_error("Cannot find index %s in InnoDB index "
+ "translation table.", index->name);
+ }
+
+ /* If we do not have an "index translation table", or not able
+ to find the index in the translation table, we'll directly find
+ matching index with information from mysql TABLE structure and
+ InnoDB dict_index_t list */
+ for (i = 0; i < table->s->keys; i++) {
+ ind = dict_table_get_index_on_name(
+ ib_table, table->key_info[i].name);
+
+ if (index == ind) {
+ return(i);
+ }
+ }
+
+ ut_error;
+
+ return(0);
+}
+/*********************************************************************//**
Returns statistics information of the table to the MySQL interpreter,
in various fields of the handle object. */
UNIV_INTERN
@@ -7851,8 +7929,8 @@ ha_innobase::info(
err_index = trx_get_error_info(prebuilt->trx);
if (err_index) {
- errkey = (unsigned int)
- row_get_mysql_key_number_for_index(err_index);
+ errkey = innobase_get_mysql_key_number_for_index(
+ share, table, ib_table, err_index);
} else {
errkey = (unsigned int) prebuilt->trx->error_key_num;
}
diff --git a/storage/innobase/handler/i_s.cc b/storage/innobase/handler/i_s.cc
index 97c1eb4dbf2..2c15a3b87db 100644
--- a/storage/innobase/handler/i_s.cc
+++ b/storage/innobase/handler/i_s.cc
@@ -1982,7 +1982,7 @@ i_s_dict_fill_sys_tables(
table_id = ut_conv_dulint_to_longlong(table->id);
- OK(fields[SYS_TABLE_ID]->store(table_id));
+ OK(fields[SYS_TABLE_ID]->store(table_id, TRUE));
OK(field_store_string(fields[SYS_TABLE_NAME], table->name));
@@ -2247,7 +2247,7 @@ i_s_dict_fill_sys_tablestats(
table_id = ut_conv_dulint_to_longlong(table->id);
- OK(fields[SYS_TABLESTATS_ID]->store(table_id));
+ OK(fields[SYS_TABLESTATS_ID]->store(table_id, TRUE));
OK(field_store_string(fields[SYS_TABLESTATS_NAME], table->name));
@@ -2259,7 +2259,7 @@ i_s_dict_fill_sys_tablestats(
"Uninitialized"));
}
- OK(fields[SYS_TABLESTATS_NROW]->store(table->stat_n_rows));
+ OK(fields[SYS_TABLESTATS_NROW]->store(table->stat_n_rows, TRUE));
OK(fields[SYS_TABLESTATS_CLUST_SIZE]->store(
table->stat_clustered_index_size));
@@ -2270,7 +2270,7 @@ i_s_dict_fill_sys_tablestats(
OK(fields[SYS_TABLESTATS_MODIFIED]->store(
table->stat_modified_counter));
- OK(fields[SYS_TABLESTATS_AUTONINC]->store(table->autoinc));
+ OK(fields[SYS_TABLESTATS_AUTONINC]->store(table->autoinc, TRUE));
OK(fields[SYS_TABLESTATS_MYSQL_OPEN_HANDLE]->store(
table->n_mysql_handles_opened));
@@ -2511,11 +2511,11 @@ i_s_dict_fill_sys_indexes(
table_id = ut_conv_dulint_to_longlong(tableid);
index_id = ut_conv_dulint_to_longlong(index->id);
- OK(fields[SYS_INDEX_ID]->store(index_id));
+ OK(fields[SYS_INDEX_ID]->store(index_id, TRUE));
OK(field_store_string(fields[SYS_INDEX_NAME], index->name));
- OK(fields[SYS_INDEX_TABLE_ID]->store(table_id));
+ OK(fields[SYS_INDEX_TABLE_ID]->store(table_id, TRUE));
OK(fields[SYS_INDEX_TYPE]->store(index->type));
@@ -2752,7 +2752,7 @@ i_s_dict_fill_sys_columns(
table_id = ut_conv_dulint_to_longlong(tableid);
- OK(fields[SYS_COLUMN_TABLE_ID]->store(table_id));
+ OK(fields[SYS_COLUMN_TABLE_ID]->store(table_id, TRUE));
OK(field_store_string(fields[SYS_COLUMN_NAME], col_name));
@@ -2962,7 +2962,7 @@ i_s_dict_fill_sys_fields(
index_id = ut_conv_dulint_to_longlong(indexid);
- OK(fields[SYS_FIELD_INDEX_ID]->store(index_id));
+ OK(fields[SYS_FIELD_INDEX_ID]->store(index_id, TRUE));
OK(field_store_string(fields[SYS_FIELD_NAME], field->name));
diff --git a/storage/innobase/include/db0err.h b/storage/innobase/include/db0err.h
index d339eb73fb9..f84ce2d15aa 100644
--- a/storage/innobase/include/db0err.h
+++ b/storage/innobase/include/db0err.h
@@ -28,6 +28,8 @@ Created 5/24/1996 Heikki Tuuri
enum db_err {
+ DB_SUCCESS_LOCKED_REC = 9, /*!< like DB_SUCCESS, but a new
+ explicit record lock was created */
DB_SUCCESS = 10,
/* The following are error codes */
diff --git a/storage/innobase/include/fil0fil.h b/storage/innobase/include/fil0fil.h
index 067b8898599..c746915844b 100644
--- a/storage/innobase/include/fil0fil.h
+++ b/storage/innobase/include/fil0fil.h
@@ -506,16 +506,6 @@ UNIV_INTERN
ulint
fil_load_single_table_tablespaces(void);
/*===================================*/
-/********************************************************************//**
-If we need crash recovery, and we have called
-fil_load_single_table_tablespaces() and dict_load_single_table_tablespaces(),
-we can call this function to print an error message of orphaned .ibd files
-for which there is not a data dictionary entry with a matching table name
-and space id. */
-UNIV_INTERN
-void
-fil_print_orphaned_tablespaces(void);
-/*================================*/
/*******************************************************************//**
Returns TRUE if a single-table tablespace does not exist in the memory cache,
or is being deleted there.
diff --git a/storage/innobase/include/lock0lock.h b/storage/innobase/include/lock0lock.h
index bdc11282dac..0319c0e9261 100644
--- a/storage/innobase/include/lock0lock.h
+++ b/storage/innobase/include/lock0lock.h
@@ -340,11 +340,12 @@ lock_sec_rec_modify_check_and_lock(
que_thr_t* thr, /*!< in: query thread */
mtr_t* mtr); /*!< in/out: mini-transaction */
/*********************************************************************//**
-Like the counterpart for a clustered index below, but now we read a
+Like lock_clust_rec_read_check_and_lock(), but reads a
secondary index record.
-@return DB_SUCCESS, DB_LOCK_WAIT, DB_DEADLOCK, or DB_QUE_THR_SUSPENDED */
+@return DB_SUCCESS, DB_SUCCESS_LOCKED_REC, DB_LOCK_WAIT, DB_DEADLOCK,
+or DB_QUE_THR_SUSPENDED */
UNIV_INTERN
-ulint
+enum db_err
lock_sec_rec_read_check_and_lock(
/*=============================*/
ulint flags, /*!< in: if BTR_NO_LOCKING_FLAG
@@ -371,9 +372,10 @@ if the query thread should anyway be suspended for some reason; if not, then
puts the transaction and the query thread to the lock wait state and inserts a
waiting request for a record lock to the lock queue. Sets the requested mode
lock on the record.
-@return DB_SUCCESS, DB_LOCK_WAIT, DB_DEADLOCK, or DB_QUE_THR_SUSPENDED */
+@return DB_SUCCESS, DB_SUCCESS_LOCKED_REC, DB_LOCK_WAIT, DB_DEADLOCK,
+or DB_QUE_THR_SUSPENDED */
UNIV_INTERN
-ulint
+enum db_err
lock_clust_rec_read_check_and_lock(
/*===============================*/
ulint flags, /*!< in: if BTR_NO_LOCKING_FLAG
diff --git a/storage/innobase/include/row0mysql.h b/storage/innobase/include/row0mysql.h
index bf9cda1ba80..39ea240772c 100644
--- a/storage/innobase/include/row0mysql.h
+++ b/storage/innobase/include/row0mysql.h
@@ -253,15 +253,6 @@ row_table_got_default_clust_index(
/*==============================*/
const dict_table_t* table); /*!< in: table */
/*********************************************************************//**
-Calculates the key number used inside MySQL for an Innobase index. We have
-to take into account if we generated a default clustered index for the table
-@return the key number used inside MySQL */
-UNIV_INTERN
-ulint
-row_get_mysql_key_number_for_index(
-/*===============================*/
- const dict_index_t* index); /*!< in: index */
-/*********************************************************************//**
Does an update or delete of a row for MySQL.
@return error code or DB_SUCCESS */
UNIV_INTERN
@@ -273,27 +264,26 @@ row_update_for_mysql(
row_prebuilt_t* prebuilt); /*!< in: prebuilt struct in MySQL
handle */
/*********************************************************************//**
-This can only be used when srv_locks_unsafe_for_binlog is TRUE or
-session is using a READ COMMITTED isolation level. Before
-calling this function we must use trx_reset_new_rec_lock_info() and
-trx_register_new_rec_lock() to store the information which new record locks
-really were set. This function removes a newly set lock under prebuilt->pcur,
-and also under prebuilt->clust_pcur. Currently, this is only used and tested
-in the case of an UPDATE or a DELETE statement, where the row lock is of the
-LOCK_X type.
-Thus, this implements a 'mini-rollback' that releases the latest record
-locks we set.
-@return error code or DB_SUCCESS */
+This can only be used when srv_locks_unsafe_for_binlog is TRUE or this
+session is using a READ COMMITTED or READ UNCOMMITTED isolation level.
+Before calling this function row_search_for_mysql() must have
+initialized prebuilt->new_rec_locks to store the information which new
+record locks really were set. This function removes a newly set
+clustered index record lock under prebuilt->pcur or
+prebuilt->clust_pcur. Thus, this implements a 'mini-rollback' that
+releases the latest clustered index record lock we set.
+@return error code or DB_SUCCESS */
UNIV_INTERN
int
row_unlock_for_mysql(
/*=================*/
- row_prebuilt_t* prebuilt, /*!< in: prebuilt struct in MySQL
+ row_prebuilt_t* prebuilt, /*!< in/out: prebuilt struct in MySQL
handle */
- ibool has_latches_on_recs);/*!< TRUE if called so that we have
- the latches on the records under pcur
- and clust_pcur, and we do not need to
- reposition the cursors. */
+ ibool has_latches_on_recs);/*!< in: TRUE if called
+ so that we have the latches on
+ the records under pcur and
+ clust_pcur, and we do not need
+ to reposition the cursors. */
/*********************************************************************//**
Creates an query graph node of 'update' type to be used in the MySQL
interface.
@@ -711,18 +701,17 @@ struct row_prebuilt_struct {
ulint new_rec_locks; /*!< normally 0; if
srv_locks_unsafe_for_binlog is
TRUE or session is using READ
- COMMITTED isolation level, in a
- cursor search, if we set a new
- record lock on an index, this is
- incremented; this is used in
- releasing the locks under the
- cursors if we are performing an
- UPDATE and we determine after
- retrieving the row that it does
- not need to be locked; thus,
- these can be used to implement a
- 'mini-rollback' that releases
- the latest record locks */
+ COMMITTED or READ UNCOMMITTED
+ isolation level, set in
+ row_search_for_mysql() if we set a new
+ record lock on the secondary
+ or clustered index; this is
+ used in row_unlock_for_mysql()
+ when releasing the lock under
+ the cursor if we determine
+ after retrieving the row that
+ it does not need to be locked
+ ('mini-rollback') */
ulint mysql_prefix_len;/*!< byte offset of the end of
the last requested column */
ulint mysql_row_len; /*!< length in bytes of a row in the
diff --git a/storage/innobase/include/sync0rw.h b/storage/innobase/include/sync0rw.h
index 6233ceef748..70471186f6d 100644
--- a/storage/innobase/include/sync0rw.h
+++ b/storage/innobase/include/sync0rw.h
@@ -622,11 +622,12 @@ struct rw_lock_struct {
unsigned cline:14; /*!< Line where created */
unsigned last_s_line:14; /*!< Line number where last time s-locked */
unsigned last_x_line:14; /*!< Line number where last time x-locked */
+#ifdef UNIV_DEBUG
ulint magic_n; /*!< RW_LOCK_MAGIC_N */
-};
-
/** Value of rw_lock_struct::magic_n */
#define RW_LOCK_MAGIC_N 22643
+#endif /* UNIV_DEBUG */
+};
#ifdef UNIV_SYNC_DEBUG
/** The structure for storing debug info of an rw-lock */
diff --git a/storage/innobase/lock/lock0lock.c b/storage/innobase/lock/lock0lock.c
index bde08ff59cd..0e57a52666e 100644
--- a/storage/innobase/lock/lock0lock.c
+++ b/storage/innobase/lock/lock0lock.c
@@ -1733,11 +1733,11 @@ lock_rec_create(
Enqueues a waiting request for a lock which cannot be granted immediately.
Checks for deadlocks.
@return DB_LOCK_WAIT, DB_DEADLOCK, or DB_QUE_THR_SUSPENDED, or
-DB_SUCCESS; DB_SUCCESS means that there was a deadlock, but another
-transaction was chosen as a victim, and we got the lock immediately:
-no need to wait then */
+DB_SUCCESS_LOCKED_REC; DB_SUCCESS_LOCKED_REC means that
+there was a deadlock, but another transaction was chosen as a victim,
+and we got the lock immediately: no need to wait then */
static
-ulint
+enum db_err
lock_rec_enqueue_waiting(
/*=====================*/
ulint type_mode,/*!< in: lock mode this
@@ -1809,7 +1809,7 @@ lock_rec_enqueue_waiting(
if (trx->wait_lock == NULL) {
- return(DB_SUCCESS);
+ return(DB_SUCCESS_LOCKED_REC);
}
trx->que_state = TRX_QUE_LOCK_WAIT;
@@ -1925,6 +1925,16 @@ somebody_waits:
return(lock_rec_create(type_mode, block, heap_no, index, trx));
}
+/** Record locking request status */
+enum lock_rec_req_status {
+ /** Failed to acquire a lock */
+ LOCK_REC_FAIL,
+ /** Succeeded in acquiring a lock (implicit or already acquired) */
+ LOCK_REC_SUCCESS,
+ /** Explicitly created a new lock */
+ LOCK_REC_SUCCESS_CREATED
+};
+
/*********************************************************************//**
This is a fast routine for locking a record in the most common cases:
there are no explicit locks on the page, or there is just one lock, owned
@@ -1932,9 +1942,9 @@ by this transaction, and of the right type_mode. This is a low-level function
which does NOT look at implicit locks! Checks lock compatibility within
explicit locks. This function sets a normal next-key lock, or in the case of
a page supremum record, a gap type lock.
-@return TRUE if locking succeeded */
+@return whether the locking succeeded */
UNIV_INLINE
-ibool
+enum lock_rec_req_status
lock_rec_lock_fast(
/*===============*/
ibool impl, /*!< in: if TRUE, no lock is set
@@ -1973,19 +1983,19 @@ lock_rec_lock_fast(
lock_rec_create(mode, block, heap_no, index, trx);
}
- return(TRUE);
+ return(LOCK_REC_SUCCESS_CREATED);
}
if (lock_rec_get_next_on_page(lock)) {
- return(FALSE);
+ return(LOCK_REC_FAIL);
}
if (lock->trx != trx
|| lock->type_mode != (mode | LOCK_REC)
|| lock_rec_get_n_bits(lock) <= heap_no) {
- return(FALSE);
+ return(LOCK_REC_FAIL);
}
if (!impl) {
@@ -1994,10 +2004,11 @@ lock_rec_lock_fast(
if (!lock_rec_get_nth_bit(lock, heap_no)) {
lock_rec_set_nth_bit(lock, heap_no);
+ return(LOCK_REC_SUCCESS_CREATED);
}
}
- return(TRUE);
+ return(LOCK_REC_SUCCESS);
}
/*********************************************************************//**
@@ -2005,9 +2016,10 @@ This is the general, and slower, routine for locking a record. This is a
low-level function which does NOT look at implicit locks! Checks lock
compatibility within explicit locks. This function sets a normal next-key
lock, or in the case of a page supremum record, a gap type lock.
-@return DB_SUCCESS, DB_LOCK_WAIT, or error code */
+@return DB_SUCCESS, DB_SUCCESS_LOCKED_REC, DB_LOCK_WAIT, DB_DEADLOCK,
+or DB_QUE_THR_SUSPENDED */
static
-ulint
+enum db_err
lock_rec_lock_slow(
/*===============*/
ibool impl, /*!< in: if TRUE, no lock is set
@@ -2024,7 +2036,6 @@ lock_rec_lock_slow(
que_thr_t* thr) /*!< in: query thread */
{
trx_t* trx;
- ulint err;
ut_ad(mutex_own(&kernel_mutex));
ut_ad((LOCK_MODE_MASK & mode) != LOCK_S
@@ -2043,27 +2054,23 @@ lock_rec_lock_slow(
/* The trx already has a strong enough lock on rec: do
nothing */
- err = DB_SUCCESS;
} else if (lock_rec_other_has_conflicting(mode, block, heap_no, trx)) {
/* If another transaction has a non-gap conflicting request in
the queue, as this transaction does not have a lock strong
enough already granted on the record, we have to wait. */
- err = lock_rec_enqueue_waiting(mode, block, heap_no,
- index, thr);
- } else {
- if (!impl) {
- /* Set the requested lock on the record */
+ return(lock_rec_enqueue_waiting(mode, block, heap_no,
+ index, thr));
+ } else if (!impl) {
+ /* Set the requested lock on the record */
- lock_rec_add_to_queue(LOCK_REC | mode, block,
- heap_no, index, trx);
- }
-
- err = DB_SUCCESS;
+ lock_rec_add_to_queue(LOCK_REC | mode, block,
+ heap_no, index, trx);
+ return(DB_SUCCESS_LOCKED_REC);
}
- return(err);
+ return(DB_SUCCESS);
}
/*********************************************************************//**
@@ -2072,9 +2079,10 @@ possible, enqueues a waiting lock request. This is a low-level function
which does NOT look at implicit locks! Checks lock compatibility within
explicit locks. This function sets a normal next-key lock, or in the case
of a page supremum record, a gap type lock.
-@return DB_SUCCESS, DB_LOCK_WAIT, or error code */
+@return DB_SUCCESS, DB_SUCCESS_LOCKED_REC, DB_LOCK_WAIT, DB_DEADLOCK,
+or DB_QUE_THR_SUSPENDED */
static
-ulint
+enum db_err
lock_rec_lock(
/*==========*/
ibool impl, /*!< in: if TRUE, no lock is set
@@ -2090,8 +2098,6 @@ lock_rec_lock(
dict_index_t* index, /*!< in: index of record */
que_thr_t* thr) /*!< in: query thread */
{
- ulint err;
-
ut_ad(mutex_own(&kernel_mutex));
ut_ad((LOCK_MODE_MASK & mode) != LOCK_S
|| lock_table_has(thr_get_trx(thr), index->table, LOCK_IS));
@@ -2103,18 +2109,20 @@ lock_rec_lock(
|| mode - (LOCK_MODE_MASK & mode) == LOCK_REC_NOT_GAP
|| mode - (LOCK_MODE_MASK & mode) == 0);
- if (lock_rec_lock_fast(impl, mode, block, heap_no, index, thr)) {
-
- /* We try a simplified and faster subroutine for the most
- common cases */
-
- err = DB_SUCCESS;
- } else {
- err = lock_rec_lock_slow(impl, mode, block,
- heap_no, index, thr);
+ /* We try a simplified and faster subroutine for the most
+ common cases */
+ switch (lock_rec_lock_fast(impl, mode, block, heap_no, index, thr)) {
+ case LOCK_REC_SUCCESS:
+ return(DB_SUCCESS);
+ case LOCK_REC_SUCCESS_CREATED:
+ return(DB_SUCCESS_LOCKED_REC);
+ case LOCK_REC_FAIL:
+ return(lock_rec_lock_slow(impl, mode, block,
+ heap_no, index, thr));
}
- return(err);
+ ut_error;
+ return(DB_ERROR);
}
/*********************************************************************//**
@@ -5072,7 +5080,14 @@ lock_rec_insert_check_and_lock(
lock_mutex_exit_kernel();
- if ((err == DB_SUCCESS) && !dict_index_is_clust(index)) {
+ switch (err) {
+ case DB_SUCCESS_LOCKED_REC:
+ err = DB_SUCCESS;
+ /* fall through */
+ case DB_SUCCESS:
+ if (dict_index_is_clust(index)) {
+ break;
+ }
/* Update the page max trx id field */
page_update_max_trx_id(block,
buf_block_get_page_zip(block),
@@ -5195,6 +5210,10 @@ lock_clust_rec_modify_check_and_lock(
ut_ad(lock_rec_queue_validate(block, rec, index, offsets));
+ if (UNIV_UNLIKELY(err == DB_SUCCESS_LOCKED_REC)) {
+ err = DB_SUCCESS;
+ }
+
return(err);
}
@@ -5261,22 +5280,27 @@ lock_sec_rec_modify_check_and_lock(
}
#endif /* UNIV_DEBUG */
- if (err == DB_SUCCESS) {
+ if (err == DB_SUCCESS || err == DB_SUCCESS_LOCKED_REC) {
/* Update the page max trx id field */
+ /* It might not be necessary to do this if
+ err == DB_SUCCESS (no new lock created),
+ but it should not cost too much performance. */
page_update_max_trx_id(block,
buf_block_get_page_zip(block),
thr_get_trx(thr)->id, mtr);
+ err = DB_SUCCESS;
}
return(err);
}
/*********************************************************************//**
-Like the counterpart for a clustered index below, but now we read a
+Like lock_clust_rec_read_check_and_lock(), but reads a
secondary index record.
-@return DB_SUCCESS, DB_LOCK_WAIT, DB_DEADLOCK, or DB_QUE_THR_SUSPENDED */
+@return DB_SUCCESS, DB_SUCCESS_LOCKED_REC, DB_LOCK_WAIT, DB_DEADLOCK,
+or DB_QUE_THR_SUSPENDED */
UNIV_INTERN
-ulint
+enum db_err
lock_sec_rec_read_check_and_lock(
/*=============================*/
ulint flags, /*!< in: if BTR_NO_LOCKING_FLAG
@@ -5297,8 +5321,8 @@ lock_sec_rec_read_check_and_lock(
LOCK_REC_NOT_GAP */
que_thr_t* thr) /*!< in: query thread */
{
- ulint err;
- ulint heap_no;
+ enum db_err err;
+ ulint heap_no;
ut_ad(!dict_index_is_clust(index));
ut_ad(block->frame == page_align(rec));
@@ -5349,9 +5373,10 @@ if the query thread should anyway be suspended for some reason; if not, then
puts the transaction and the query thread to the lock wait state and inserts a
waiting request for a record lock to the lock queue. Sets the requested mode
lock on the record.
-@return DB_SUCCESS, DB_LOCK_WAIT, DB_DEADLOCK, or DB_QUE_THR_SUSPENDED */
+@return DB_SUCCESS, DB_SUCCESS_LOCKED_REC, DB_LOCK_WAIT, DB_DEADLOCK,
+or DB_QUE_THR_SUSPENDED */
UNIV_INTERN
-ulint
+enum db_err
lock_clust_rec_read_check_and_lock(
/*===============================*/
ulint flags, /*!< in: if BTR_NO_LOCKING_FLAG
@@ -5372,8 +5397,8 @@ lock_clust_rec_read_check_and_lock(
LOCK_REC_NOT_GAP */
que_thr_t* thr) /*!< in: query thread */
{
- ulint err;
- ulint heap_no;
+ enum db_err err;
+ ulint heap_no;
ut_ad(dict_index_is_clust(index));
ut_ad(block->frame == page_align(rec));
@@ -5444,17 +5469,22 @@ lock_clust_rec_read_check_and_lock_alt(
mem_heap_t* tmp_heap = NULL;
ulint offsets_[REC_OFFS_NORMAL_SIZE];
ulint* offsets = offsets_;
- ulint ret;
+ ulint err;
rec_offs_init(offsets_);
offsets = rec_get_offsets(rec, index, offsets,
ULINT_UNDEFINED, &tmp_heap);
- ret = lock_clust_rec_read_check_and_lock(flags, block, rec, index,
+ err = lock_clust_rec_read_check_and_lock(flags, block, rec, index,
offsets, mode, gap_mode, thr);
if (tmp_heap) {
mem_heap_free(tmp_heap);
}
- return(ret);
+
+ if (UNIV_UNLIKELY(err == DB_SUCCESS_LOCKED_REC)) {
+ err = DB_SUCCESS;
+ }
+
+ return(err);
}
/*******************************************************************//**
diff --git a/storage/innobase/row/row0ins.c b/storage/innobase/row/row0ins.c
index 906aaae2412..c882a065cd1 100644
--- a/storage/innobase/row/row0ins.c
+++ b/storage/innobase/row/row0ins.c
@@ -1121,9 +1121,9 @@ nonstandard_exit_func:
/*********************************************************************//**
Sets a shared lock on a record. Used in locking possible duplicate key
records and also in checking foreign key constraints.
-@return DB_SUCCESS or error code */
+@return DB_SUCCESS, DB_SUCCESS_LOCKED_REC, or error code */
static
-ulint
+enum db_err
row_ins_set_shared_rec_lock(
/*========================*/
ulint type, /*!< in: LOCK_ORDINARY, LOCK_GAP, or
@@ -1134,7 +1134,7 @@ row_ins_set_shared_rec_lock(
const ulint* offsets,/*!< in: rec_get_offsets(rec, index) */
que_thr_t* thr) /*!< in: query thread */
{
- ulint err;
+ enum db_err err;
ut_ad(rec_offs_validate(rec, index, offsets));
@@ -1152,9 +1152,9 @@ row_ins_set_shared_rec_lock(
/*********************************************************************//**
Sets a exclusive lock on a record. Used in locking possible duplicate key
records
-@return DB_SUCCESS or error code */
+@return DB_SUCCESS, DB_SUCCESS_LOCKED_REC, or error code */
static
-ulint
+enum db_err
row_ins_set_exclusive_rec_lock(
/*===========================*/
ulint type, /*!< in: LOCK_ORDINARY, LOCK_GAP, or
@@ -1165,7 +1165,7 @@ row_ins_set_exclusive_rec_lock(
const ulint* offsets,/*!< in: rec_get_offsets(rec, index) */
que_thr_t* thr) /*!< in: query thread */
{
- ulint err;
+ enum db_err err;
ut_ad(rec_offs_validate(rec, index, offsets));
@@ -1205,7 +1205,6 @@ row_ins_check_foreign_constraint(
dict_index_t* check_index;
ulint n_fields_cmp;
btr_pcur_t pcur;
- ibool moved;
int cmp;
ulint err;
ulint i;
@@ -1336,13 +1335,13 @@ run_again:
/* Scan index records and check if there is a matching record */
- for (;;) {
+ do {
const rec_t* rec = btr_pcur_get_rec(&pcur);
const buf_block_t* block = btr_pcur_get_block(&pcur);
if (page_rec_is_infimum(rec)) {
- goto next_rec;
+ continue;
}
offsets = rec_get_offsets(rec, check_index,
@@ -1353,12 +1352,13 @@ run_again:
err = row_ins_set_shared_rec_lock(LOCK_ORDINARY, block,
rec, check_index,
offsets, thr);
- if (err != DB_SUCCESS) {
-
- break;
+ switch (err) {
+ case DB_SUCCESS_LOCKED_REC:
+ case DB_SUCCESS:
+ continue;
+ default:
+ goto end_scan;
}
-
- goto next_rec;
}
cmp = cmp_dtuple_rec(entry, rec, offsets);
@@ -1369,9 +1369,12 @@ run_again:
err = row_ins_set_shared_rec_lock(
LOCK_ORDINARY, block,
rec, check_index, offsets, thr);
- if (err != DB_SUCCESS) {
-
+ switch (err) {
+ case DB_SUCCESS_LOCKED_REC:
+ case DB_SUCCESS:
break;
+ default:
+ goto end_scan;
}
} else {
/* Found a matching record. Lock only
@@ -1382,15 +1385,18 @@ run_again:
LOCK_REC_NOT_GAP, block,
rec, check_index, offsets, thr);
- if (err != DB_SUCCESS) {
-
+ switch (err) {
+ case DB_SUCCESS_LOCKED_REC:
+ case DB_SUCCESS:
break;
+ default:
+ goto end_scan;
}
if (check_ref) {
err = DB_SUCCESS;
- break;
+ goto end_scan;
} else if (foreign->type != 0) {
/* There is an ON UPDATE or ON DELETE
condition: check them in a separate
@@ -1416,7 +1422,7 @@ run_again:
err = DB_FOREIGN_DUPLICATE_KEY;
}
- break;
+ goto end_scan;
}
/* row_ins_foreign_check_on_constraint
@@ -1429,49 +1435,41 @@ run_again:
thr, foreign, rec, entry);
err = DB_ROW_IS_REFERENCED;
- break;
+ goto end_scan;
}
}
- }
+ } else {
+ ut_a(cmp < 0);
- if (cmp < 0) {
err = row_ins_set_shared_rec_lock(
LOCK_GAP, block,
rec, check_index, offsets, thr);
- if (err != DB_SUCCESS) {
- break;
- }
-
- if (check_ref) {
- err = DB_NO_REFERENCED_ROW;
- row_ins_foreign_report_add_err(
- trx, foreign, rec, entry);
- } else {
- err = DB_SUCCESS;
+ switch (err) {
+ case DB_SUCCESS_LOCKED_REC:
+ case DB_SUCCESS:
+ if (check_ref) {
+ err = DB_NO_REFERENCED_ROW;
+ row_ins_foreign_report_add_err(
+ trx, foreign, rec, entry);
+ } else {
+ err = DB_SUCCESS;
+ }
}
- break;
+ goto end_scan;
}
+ } while (btr_pcur_move_to_next(&pcur, &mtr));
- ut_a(cmp == 0);
-next_rec:
- moved = btr_pcur_move_to_next(&pcur, &mtr);
-
- if (!moved) {
- if (check_ref) {
- rec = btr_pcur_get_rec(&pcur);
- row_ins_foreign_report_add_err(
- trx, foreign, rec, entry);
- err = DB_NO_REFERENCED_ROW;
- } else {
- err = DB_SUCCESS;
- }
-
- break;
- }
+ if (check_ref) {
+ row_ins_foreign_report_add_err(
+ trx, foreign, btr_pcur_get_rec(&pcur), entry);
+ err = DB_NO_REFERENCED_ROW;
+ } else {
+ err = DB_SUCCESS;
}
+end_scan:
btr_pcur_close(&pcur);
mtr_commit(&mtr);
@@ -1719,9 +1717,13 @@ row_ins_scan_sec_index_for_duplicate(
rec, index, offsets, thr);
}
- if (err != DB_SUCCESS) {
-
+ switch (err) {
+ case DB_SUCCESS_LOCKED_REC:
+ err = DB_SUCCESS;
+ case DB_SUCCESS:
break;
+ default:
+ goto end_scan;
}
if (page_rec_is_supremum(rec)) {
@@ -1738,17 +1740,15 @@ row_ins_scan_sec_index_for_duplicate(
thr_get_trx(thr)->error_info = index;
- break;
+ goto end_scan;
}
+ } else {
+ ut_a(cmp < 0);
+ goto end_scan;
}
-
- if (cmp < 0) {
- break;
- }
-
- ut_a(cmp == 0);
} while (btr_pcur_move_to_next(&pcur, &mtr));
+end_scan:
if (UNIV_LIKELY_NULL(heap)) {
mem_heap_free(heap);
}
@@ -1837,7 +1837,11 @@ row_ins_duplicate_error_in_clust(
cursor->index, offsets, thr);
}
- if (err != DB_SUCCESS) {
+ switch (err) {
+ case DB_SUCCESS_LOCKED_REC:
+ case DB_SUCCESS:
+ break;
+ default:
goto func_exit;
}
@@ -1877,7 +1881,11 @@ row_ins_duplicate_error_in_clust(
rec, cursor->index, offsets, thr);
}
- if (err != DB_SUCCESS) {
+ switch (err) {
+ case DB_SUCCESS_LOCKED_REC:
+ case DB_SUCCESS:
+ break;
+ default:
goto func_exit;
}
diff --git a/storage/innobase/row/row0mysql.c b/storage/innobase/row/row0mysql.c
index c9b9a982bef..1f7f98a59a2 100644
--- a/storage/innobase/row/row0mysql.c
+++ b/storage/innobase/row/row0mysql.c
@@ -1430,27 +1430,26 @@ run_again:
}
/*********************************************************************//**
-This can only be used when srv_locks_unsafe_for_binlog is TRUE or
-this session is using a READ COMMITTED isolation level. Before
-calling this function we must use trx_reset_new_rec_lock_info() and
-trx_register_new_rec_lock() to store the information which new record locks
-really were set. This function removes a newly set lock under prebuilt->pcur,
-and also under prebuilt->clust_pcur. Currently, this is only used and tested
-in the case of an UPDATE or a DELETE statement, where the row lock is of the
-LOCK_X type.
-Thus, this implements a 'mini-rollback' that releases the latest record
-locks we set.
-@return error code or DB_SUCCESS */
+This can only be used when srv_locks_unsafe_for_binlog is TRUE or this
+session is using a READ COMMITTED or READ UNCOMMITTED isolation level.
+Before calling this function row_search_for_mysql() must have
+initialized prebuilt->new_rec_locks to store the information which new
+record locks really were set. This function removes a newly set
+clustered index record lock under prebuilt->pcur or
+prebuilt->clust_pcur. Thus, this implements a 'mini-rollback' that
+releases the latest clustered index record lock we set.
+@return error code or DB_SUCCESS */
UNIV_INTERN
int
row_unlock_for_mysql(
/*=================*/
- row_prebuilt_t* prebuilt, /*!< in: prebuilt struct in MySQL
+ row_prebuilt_t* prebuilt, /*!< in/out: prebuilt struct in MySQL
handle */
- ibool has_latches_on_recs)/*!< TRUE if called so that we have
- the latches on the records under pcur
- and clust_pcur, and we do not need to
- reposition the cursors. */
+ ibool has_latches_on_recs)/*!< in: TRUE if called so
+ that we have the latches on
+ the records under pcur and
+ clust_pcur, and we do not need
+ to reposition the cursors. */
{
btr_pcur_t* pcur = prebuilt->pcur;
btr_pcur_t* clust_pcur = prebuilt->clust_pcur;
@@ -1648,37 +1647,6 @@ row_table_got_default_clust_index(
}
/*********************************************************************//**
-Calculates the key number used inside MySQL for an Innobase index. We have
-to take into account if we generated a default clustered index for the table
-@return the key number used inside MySQL */
-UNIV_INTERN
-ulint
-row_get_mysql_key_number_for_index(
-/*===============================*/
- const dict_index_t* index) /*!< in: index */
-{
- const dict_index_t* ind;
- ulint i;
-
- ut_a(index);
-
- i = 0;
- ind = dict_table_get_first_index(index->table);
-
- while (index != ind) {
- ind = dict_table_get_next_index(ind);
- i++;
- }
-
- if (row_table_got_default_clust_index(index->table)) {
- ut_a(i > 0);
- i--;
- }
-
- return(i);
-}
-
-/*********************************************************************//**
Locks the data dictionary in shared mode from modifications, for performing
foreign key check, rollback, or other operation invisible to MySQL. */
UNIV_INTERN
diff --git a/storage/innobase/row/row0sel.c b/storage/innobase/row/row0sel.c
index 4d19ed93a49..a5bf361661b 100644
--- a/storage/innobase/row/row0sel.c
+++ b/storage/innobase/row/row0sel.c
@@ -863,8 +863,14 @@ row_sel_get_clust_rec(
clust_rec, index, offsets,
node->row_lock_mode, lock_type, thr);
- if (err != DB_SUCCESS) {
-
+ switch (err) {
+ case DB_SUCCESS:
+ case DB_SUCCESS_LOCKED_REC:
+ /* Declare the variable uninitialized in Valgrind.
+ It should be set to DB_SUCCESS at func_exit. */
+ UNIV_MEM_INVALID(&err, sizeof err);
+ break;
+ default:
goto err_exit;
}
} else {
@@ -934,9 +940,9 @@ err_exit:
/*********************************************************************//**
Sets a lock on a record.
-@return DB_SUCCESS or error code */
+@return DB_SUCCESS, DB_SUCCESS_LOCKED_REC, or error code */
UNIV_INLINE
-ulint
+enum db_err
sel_set_rec_lock(
/*=============*/
const buf_block_t* block, /*!< in: buffer block of rec */
@@ -948,8 +954,8 @@ sel_set_rec_lock(
LOC_REC_NOT_GAP */
que_thr_t* thr) /*!< in: query thread */
{
- trx_t* trx;
- ulint err;
+ trx_t* trx;
+ enum db_err err;
trx = thr_get_trx(thr);
@@ -1482,11 +1488,15 @@ rec_loop:
node->row_lock_mode,
lock_type, thr);
- if (err != DB_SUCCESS) {
+ switch (err) {
+ case DB_SUCCESS_LOCKED_REC:
+ err = DB_SUCCESS;
+ case DB_SUCCESS:
+ break;
+ default:
/* Note that in this case we will store in pcur
the PREDECESSOR of the record we are waiting
the lock for */
-
goto lock_wait_or_error;
}
}
@@ -1538,8 +1548,12 @@ skip_lock:
rec, index, offsets,
node->row_lock_mode, lock_type, thr);
- if (err != DB_SUCCESS) {
-
+ switch (err) {
+ case DB_SUCCESS_LOCKED_REC:
+ err = DB_SUCCESS;
+ case DB_SUCCESS:
+ break;
+ default:
goto lock_wait_or_error;
}
}
@@ -2801,9 +2815,9 @@ row_sel_build_prev_vers_for_mysql(
Retrieves the clustered index record corresponding to a record in a
non-clustered index. Does the necessary locking. Used in the MySQL
interface.
-@return DB_SUCCESS or error code */
+@return DB_SUCCESS, DB_SUCCESS_LOCKED_REC, or error code */
static
-ulint
+enum db_err
row_sel_get_clust_rec_for_mysql(
/*============================*/
row_prebuilt_t* prebuilt,/*!< in: prebuilt struct in the handle */
@@ -2830,7 +2844,7 @@ row_sel_get_clust_rec_for_mysql(
dict_index_t* clust_index;
const rec_t* clust_rec;
rec_t* old_vers;
- ulint err;
+ enum db_err err;
trx_t* trx;
*out_rec = NULL;
@@ -2889,6 +2903,7 @@ row_sel_get_clust_rec_for_mysql(
clust_rec = NULL;
+ err = DB_SUCCESS;
goto func_exit;
}
@@ -2904,8 +2919,11 @@ row_sel_get_clust_rec_for_mysql(
0, btr_pcur_get_block(prebuilt->clust_pcur),
clust_rec, clust_index, *offsets,
prebuilt->select_lock_type, LOCK_REC_NOT_GAP, thr);
- if (err != DB_SUCCESS) {
-
+ switch (err) {
+ case DB_SUCCESS:
+ case DB_SUCCESS_LOCKED_REC:
+ break;
+ default:
goto err_exit;
}
} else {
@@ -2965,6 +2983,8 @@ row_sel_get_clust_rec_for_mysql(
rec, sec_index, clust_rec, clust_index));
#endif
}
+
+ err = DB_SUCCESS;
}
func_exit:
@@ -2977,7 +2997,6 @@ func_exit:
btr_pcur_store_position(prebuilt->clust_pcur, mtr);
}
- err = DB_SUCCESS;
err_exit:
return(err);
}
@@ -3702,8 +3721,12 @@ shortcut_fails_too_big_rec:
prebuilt->select_lock_type,
LOCK_GAP, thr);
- if (err != DB_SUCCESS) {
-
+ switch (err) {
+ case DB_SUCCESS_LOCKED_REC:
+ err = DB_SUCCESS;
+ case DB_SUCCESS:
+ break;
+ default:
goto lock_wait_or_error;
}
}
@@ -3801,8 +3824,12 @@ rec_loop:
prebuilt->select_lock_type,
LOCK_ORDINARY, thr);
- if (err != DB_SUCCESS) {
-
+ switch (err) {
+ case DB_SUCCESS_LOCKED_REC:
+ err = DB_SUCCESS;
+ case DB_SUCCESS:
+ break;
+ default:
goto lock_wait_or_error;
}
}
@@ -3932,8 +3959,11 @@ wrong_offs:
prebuilt->select_lock_type, LOCK_GAP,
thr);
- if (err != DB_SUCCESS) {
-
+ switch (err) {
+ case DB_SUCCESS_LOCKED_REC:
+ case DB_SUCCESS:
+ break;
+ default:
goto lock_wait_or_error;
}
}
@@ -3968,8 +3998,11 @@ wrong_offs:
prebuilt->select_lock_type, LOCK_GAP,
thr);
- if (err != DB_SUCCESS) {
-
+ switch (err) {
+ case DB_SUCCESS_LOCKED_REC:
+ case DB_SUCCESS:
+ break;
+ default:
goto lock_wait_or_error;
}
}
@@ -4039,15 +4072,21 @@ no_gap_lock:
switch (err) {
const rec_t* old_vers;
- case DB_SUCCESS:
+ case DB_SUCCESS_LOCKED_REC:
if (srv_locks_unsafe_for_binlog
- || trx->isolation_level <= TRX_ISO_READ_COMMITTED) {
+ || trx->isolation_level
+ <= TRX_ISO_READ_COMMITTED) {
/* Note that a record of
prebuilt->index was locked. */
prebuilt->new_rec_locks = 1;
}
+ err = DB_SUCCESS;
+ case DB_SUCCESS:
break;
case DB_LOCK_WAIT:
+ /* Never unlock rows that were part of a conflict. */
+ prebuilt->new_rec_locks = 0;
+
if (UNIV_LIKELY(prebuilt->row_read_type
!= ROW_READ_TRY_SEMI_CONSISTENT)
|| unique_search
@@ -4077,7 +4116,6 @@ no_gap_lock:
if (UNIV_LIKELY(trx->wait_lock != NULL)) {
lock_cancel_waiting_and_release(
trx->wait_lock);
- prebuilt->new_rec_locks = 0;
} else {
mutex_exit(&kernel_mutex);
@@ -4089,9 +4127,6 @@ no_gap_lock:
ULINT_UNDEFINED,
&heap);
err = DB_SUCCESS;
- /* Note that a record of
- prebuilt->index was locked. */
- prebuilt->new_rec_locks = 1;
break;
}
mutex_exit(&kernel_mutex);
@@ -4228,27 +4263,30 @@ requires_clust_rec:
err = row_sel_get_clust_rec_for_mysql(prebuilt, index, rec,
thr, &clust_rec,
&offsets, &heap, &mtr);
- if (err != DB_SUCCESS) {
+ switch (err) {
+ case DB_SUCCESS:
+ if (clust_rec == NULL) {
+ /* The record did not exist in the read view */
+ ut_ad(prebuilt->select_lock_type == LOCK_NONE);
+ goto next_rec;
+ }
+ break;
+ case DB_SUCCESS_LOCKED_REC:
+ ut_a(clust_rec != NULL);
+ if (srv_locks_unsafe_for_binlog
+ || trx->isolation_level
+ <= TRX_ISO_READ_COMMITTED) {
+ /* Note that the clustered index record
+ was locked. */
+ prebuilt->new_rec_locks = 2;
+ }
+ err = DB_SUCCESS;
+ break;
+ default:
goto lock_wait_or_error;
}
- if (clust_rec == NULL) {
- /* The record did not exist in the read view */
- ut_ad(prebuilt->select_lock_type == LOCK_NONE);
-
- goto next_rec;
- }
-
- if ((srv_locks_unsafe_for_binlog
- || trx->isolation_level <= TRX_ISO_READ_COMMITTED)
- && prebuilt->select_lock_type != LOCK_NONE) {
- /* Note that both the secondary index record
- and the clustered index record were locked. */
- ut_ad(prebuilt->new_rec_locks == 1);
- prebuilt->new_rec_locks = 2;
- }
-
if (UNIV_UNLIKELY(rec_get_deleted_flag(clust_rec, comp))) {
/* The record is delete marked: we can skip it */
diff --git a/storage/innobase/sync/sync0rw.c b/storage/innobase/sync/sync0rw.c
index 0ff2920f4f8..4dbaaa97bd9 100644
--- a/storage/innobase/sync/sync0rw.c
+++ b/storage/innobase/sync/sync0rw.c
@@ -278,7 +278,7 @@ rw_lock_create_func(
lock->level = level;
#endif /* UNIV_SYNC_DEBUG */
- lock->magic_n = RW_LOCK_MAGIC_N;
+ ut_d(lock->magic_n = RW_LOCK_MAGIC_N);
lock->cfile_name = cfile_name;
lock->cline = (unsigned int) cline;
@@ -293,10 +293,8 @@ rw_lock_create_func(
mutex_enter(&rw_lock_list_mutex);
- if (UT_LIST_GET_LEN(rw_lock_list) > 0) {
- ut_a(UT_LIST_GET_FIRST(rw_lock_list)->magic_n
- == RW_LOCK_MAGIC_N);
- }
+ ut_ad(UT_LIST_GET_FIRST(rw_lock_list) == NULL
+ || UT_LIST_GET_FIRST(rw_lock_list)->magic_n == RW_LOCK_MAGIC_N);
UT_LIST_ADD_FIRST(list, rw_lock_list, lock);
@@ -316,8 +314,6 @@ rw_lock_free_func(
ut_ad(rw_lock_validate(lock));
ut_a(lock->lock_word == X_LOCK_DECR);
- lock->magic_n = 0;
-
#ifndef INNODB_RW_LOCKS_USE_ATOMICS
mutex_free(rw_lock_get_mutex(lock));
#endif /* INNODB_RW_LOCKS_USE_ATOMICS */
@@ -327,16 +323,16 @@ rw_lock_free_func(
os_event_free(lock->wait_ex_event);
- if (UT_LIST_GET_PREV(list, lock)) {
- ut_a(UT_LIST_GET_PREV(list, lock)->magic_n == RW_LOCK_MAGIC_N);
- }
- if (UT_LIST_GET_NEXT(list, lock)) {
- ut_a(UT_LIST_GET_NEXT(list, lock)->magic_n == RW_LOCK_MAGIC_N);
- }
+ ut_ad(UT_LIST_GET_PREV(list, lock) == NULL
+ || UT_LIST_GET_PREV(list, lock)->magic_n == RW_LOCK_MAGIC_N);
+ ut_ad(UT_LIST_GET_NEXT(list, lock) == NULL
+ || UT_LIST_GET_NEXT(list, lock)->magic_n == RW_LOCK_MAGIC_N);
UT_LIST_REMOVE(list, rw_lock_list, lock);
mutex_exit(&rw_lock_list_mutex);
+
+ ut_d(lock->magic_n = 0);
}
#ifdef UNIV_DEBUG
@@ -358,7 +354,7 @@ rw_lock_validate(
waiters = rw_lock_get_waiters(lock);
lock_word = lock->lock_word;
- ut_a(lock->magic_n == RW_LOCK_MAGIC_N);
+ ut_ad(lock->magic_n == RW_LOCK_MAGIC_N);
ut_a(waiters == 0 || waiters == 1);
ut_a(lock_word > -X_LOCK_DECR ||(-lock_word) % X_LOCK_DECR == 0);
diff --git a/storage/innobase/ut/ut0ut.c b/storage/innobase/ut/ut0ut.c
index 6b65067aa54..39978304696 100644
--- a/storage/innobase/ut/ut0ut.c
+++ b/storage/innobase/ut/ut0ut.c
@@ -637,6 +637,8 @@ ut_strerr(
switch (num) {
case DB_SUCCESS:
return("Success");
+ case DB_SUCCESS_LOCKED_REC:
+ return("Success, record lock created");
case DB_ERROR:
return("Generic error");
case DB_INTERRUPTED: