summaryrefslogtreecommitdiff
path: root/storage
diff options
context:
space:
mode:
authorunknown <lars@mysql.com>2005-12-22 06:39:02 +0100
committerunknown <lars@mysql.com>2005-12-22 06:39:02 +0100
commit09346e6e2de57fadc94f70beb29eabe5186dbe62 (patch)
tree192a6c65973c50f6a436a8c6e6bb19efa2ed2419 /storage
parent65c560e9b836953bba66307268430f0faf50ca3c (diff)
downloadmariadb-git-09346e6e2de57fadc94f70beb29eabe5186dbe62.tar.gz
WL#1012: All changes as one single changeset.
This includes both code and test cases. BitKeeper/deleted/.del-ctype_ucs_binlog.result~280d136b1a0bcf17: Delete: mysql-test/r/ctype_ucs_binlog.result BitKeeper/deleted/.del-rpl_delete_all.result~7c050d592614b3f: Delete: mysql-test/r/rpl_delete_all.result BitKeeper/deleted/.del-rpl000013-slave.opt~18266ad8a2403e8d: Delete: mysql-test/t/rpl000013-slave.opt BitKeeper/deleted/.del-rpl_delete_all.test~700a1490277780e0: Delete: mysql-test/t/rpl_delete_all.test mysql-test/extra/binlog_tests/binlog.test: Import patch wl1012.patch mysql-test/extra/binlog_tests/blackhole.test: Import patch wl1012.patch mysql-test/extra/binlog_tests/ctype_cp932.test: Import patch wl1012.patch mysql-test/extra/binlog_tests/ctype_cp932_binlog.test: Import patch wl1012.patch mysql-test/extra/binlog_tests/ctype_ucs_binlog.test: Import patch wl1012.patch mysql-test/extra/binlog_tests/drop_temp_table.test: Import patch wl1012.patch mysql-test/extra/binlog_tests/insert_select-binlog.test: Import patch wl1012.patch mysql-test/extra/binlog_tests/mix_innodb_myisam_binlog.test: Import patch wl1012.patch mysql-test/extra/rpl_tests/rpl_ddl.test: Import patch wl1012.patch mysql-test/extra/rpl_tests/rpl_deadlock.test: Import patch wl1012.patch mysql-test/extra/rpl_tests/rpl_err_ignoredtable.test: Import patch wl1012.patch mysql-test/extra/rpl_tests/rpl_flsh_tbls.test: Import patch wl1012.patch mysql-test/extra/rpl_tests/rpl_loaddata_m.test: Import patch wl1012.patch mysql-test/extra/rpl_tests/rpl_log.test: Import patch wl1012.patch mysql-test/extra/rpl_tests/rpl_max_relay_size.test: Import patch wl1012.patch mysql-test/extra/rpl_tests/rpl_multi_query.test: Import patch wl1012.patch mysql-test/extra/rpl_tests/rpl_reset_slave.test: Import patch wl1012.patch mysql-test/extra/rpl_tests/rpl_stm_000001.test: Import patch wl1012.patch mysql-test/extra/rpl_tests/rpl_stm_EE_err.test: Import patch wl1012.patch mysql-test/extra/rpl_tests/rpl_stm_charset.test: Import patch wl1012.patch mysql-test/extra/rpl_tests/rpl_user_variables.test: Import patch wl1012.patch mysql-test/r/binlog_stm_binlog.result: Import patch wl1012.patch mysql-test/r/binlog_stm_blackhole.result: Import patch wl1012.patch mysql-test/r/binlog_stm_ctype_cp932.result: Import patch wl1012.patch mysql-test/r/binlog_stm_ctype_ucs.result: Import patch wl1012.patch mysql-test/r/binlog_stm_drop_tmp_tbl.result: Import patch wl1012.patch mysql-test/r/binlog_stm_insert_select.result: Import patch wl1012.patch mysql-test/r/binlog_stm_mix_innodb_myisam.result: Import patch wl1012.patch mysql-test/r/rpl_000012.result: Import patch wl1012.patch mysql-test/r/rpl_000015.result: Import patch wl1012.patch mysql-test/r/rpl_deadlock_innodb.result: Import patch wl1012.patch mysql-test/r/rpl_flushlog_loop.result: Import patch wl1012.patch mysql-test/r/rpl_loaddata_s.result: Import patch wl1012.patch mysql-test/r/rpl_stm_000001.result: Import patch wl1012.patch mysql-test/r/rpl_stm_EE_err.result: Import patch wl1012.patch mysql-test/r/rpl_stm_charset.result: Import patch wl1012.patch mysql-test/r/rpl_stm_ddl.result: Import patch wl1012.patch mysql-test/r/rpl_stm_err_ignoredtable.result: Import patch wl1012.patch mysql-test/r/rpl_stm_flsh_tbls.result: Import patch wl1012.patch mysql-test/r/rpl_stm_loaddata_m.result: Import patch wl1012.patch mysql-test/r/rpl_stm_log.result: Import patch wl1012.patch mysql-test/r/rpl_stm_max_relay_size.result: Import patch wl1012.patch mysql-test/r/rpl_stm_multi_query.result: Import patch wl1012.patch mysql-test/r/rpl_stm_mystery22.result: Import patch wl1012.patch mysql-test/r/rpl_stm_reset_slave.result: Import patch wl1012.patch mysql-test/r/rpl_stm_rewrt_db.result: Import patch wl1012.patch mysql-test/r/rpl_stm_sp.result: Import patch wl1012.patch mysql-test/r/rpl_stm_timezone.result: Import patch wl1012.patch mysql-test/r/rpl_stm_until.result: Import patch wl1012.patch mysql-test/r/rpl_stm_user_variables.result: Import patch wl1012.patch mysql-test/r/rpl_stm_view.result: Import patch wl1012.patch mysql-test/t/binlog_row_binlog-master.opt: Import patch wl1012.patch mysql-test/t/rpl_000012.test: Import patch wl1012.patch mysql-test/t/rpl_000015-slave.sh: Import patch wl1012.patch mysql-test/t/rpl_000015.slave-mi: Import patch wl1012.patch mysql-test/t/rpl_000015.test: Import patch wl1012.patch mysql-test/t/rpl_deadlock_innodb-slave.opt: Import patch wl1012.patch mysql-test/t/rpl_flushlog_loop-master.opt: Import patch wl1012.patch mysql-test/t/rpl_flushlog_loop-master.sh: Import patch wl1012.patch mysql-test/t/rpl_flushlog_loop-slave.opt: Import patch wl1012.patch mysql-test/t/rpl_flushlog_loop-slave.sh: Import patch wl1012.patch mysql-test/t/rpl_flushlog_loop.test: Import patch wl1012.patch mysql-test/t/rpl_loaddata_s-slave.opt: Import patch wl1012.patch mysql-test/t/rpl_loaddata_s.test: Import patch wl1012.patch mysql-test/t/rpl_stm_000001-slave.opt: Import patch wl1012.patch mysql-test/t/rpl_stm_err_ignoredtable-slave.opt: Import patch wl1012.patch mysql-test/t/rpl_stm_loaddata_m-master.opt: Import patch wl1012.patch mysql-test/t/rpl_stm_log-master.opt: Import patch wl1012.patch mysql-test/t/rpl_stm_log-slave.opt: Import patch wl1012.patch mysql-test/t/rpl_stm_mystery22.test: Import patch wl1012.patch mysql-test/t/rpl_stm_rewrt_db-slave.opt: Import patch wl1012.patch mysql-test/t/rpl_stm_rewrt_db.test: Import patch wl1012.patch mysql-test/t/rpl_stm_sp-master.opt: Import patch wl1012.patch mysql-test/t/rpl_stm_sp-slave.opt: Import patch wl1012.patch mysql-test/t/rpl_stm_sp.test: Import patch wl1012.patch mysql-test/t/rpl_stm_timezone-master.opt: Import patch wl1012.patch mysql-test/t/rpl_stm_timezone-slave.opt: Import patch wl1012.patch BUILD/SETUP.sh: Import patch wl1012.patch Makefile.am: Import patch wl1012.patch mysql-test/t/rpl_stm_timezone.test: Import patch wl1012.patch mysql-test/t/rpl_stm_until.test: Import patch wl1012.patch mysql-test/t/rpl_stm_view.test: Import patch wl1012.patch client/Makefile.am: Import patch wl1012.patch client/client_priv.h: Import patch wl1012.patch client/mysqlbinlog.cc: Import patch wl1012.patch configure.in: Import patch wl1012.patch include/Makefile.am: Import patch wl1012.patch include/base64.h: Import patch wl1012.patch include/config-win.h: Import patch wl1012.patch include/my_base.h: Import patch wl1012.patch include/my_global.h: Import patch wl1012.patch mysql-test/Makefile.am: Import patch wl1012.patch mysql-test/mysql-test-run.pl: Import patch wl1012.patch mysql-test/mysql-test-run.sh: Import patch wl1012.patch mysql-test/r/date_formats.result: Import patch wl1012.patch mysql-test/r/flush_block_commit.result: Import patch wl1012.patch mysql-test/r/innodb.result: Import patch wl1012.patch mysql-test/r/rpl000017.result: Import patch wl1012.patch mysql-test/r/rpl_change_master.result: Import patch wl1012.patch mysql-test/r/rpl_commit_after_flush.result: Import patch wl1012.patch mysql-test/r/rpl_create_database.result: Import patch wl1012.patch mysql-test/r/rpl_do_grant.result: Import patch wl1012.patch mysql-test/r/rpl_loaddata.result: Import patch wl1012.patch mysql-test/r/rpl_log_pos.result: Import patch wl1012.patch mysql-test/r/rpl_multi_delete.result: Import patch wl1012.patch mysql-test/r/rpl_multi_update.result: Import patch wl1012.patch mysql-test/r/rpl_openssl.result: Import patch wl1012.patch mysql-test/r/rpl_replicate_do.result: Import patch wl1012.patch mysql-test/r/rpl_rotate_logs.result: Import patch wl1012.patch mysql-test/r/rpl_server_id1.result: Import patch wl1012.patch mysql-test/r/rpl_server_id2.result: Import patch wl1012.patch mysql-test/r/rpl_temporary.result: Import patch wl1012.patch mysql-test/r/user_var-binlog.result: Import patch wl1012.patch mysql-test/t/create_select_tmp.test: Import patch wl1012.patch mysql-test/t/date_formats.test: Import patch wl1012.patch mysql-test/t/disabled.def: Import patch wl1012.patch mysql-test/t/innodb.test: Import patch wl1012.patch mysql-test/t/mysqlbinlog.test: Import patch wl1012.patch mysql-test/t/mysqlbinlog2.test: Import patch wl1012.patch mysql-test/t/rpl000002.test: Import patch wl1012.patch mysql-test/t/rpl000006.test: Import patch wl1012.patch mysql-test/t/rpl000013.test: Import patch wl1012.patch mysql-test/t/rpl000017.test: Import patch wl1012.patch mysql-test/t/rpl_auto_increment.test: Import patch wl1012.patch mysql-test/t/rpl_change_master.test: Import patch wl1012.patch mysql-test/t/rpl_commit_after_flush.test: Import patch wl1012.patch mysql-test/t/rpl_create_database.test: Import patch wl1012.patch mysql-test/t/rpl_do_grant.test: Import patch wl1012.patch mysql-test/t/rpl_drop.test: Import patch wl1012.patch mysql-test/t/rpl_empty_master_crash.test: Import patch wl1012.patch mysql-test/t/rpl_failed_optimize.test: Import patch wl1012.patch mysql-test/t/rpl_heap.test: Import patch wl1012.patch mysql-test/t/rpl_insert_id.test: Import patch wl1012.patch mysql-test/t/rpl_insert_ignore.test: Import patch wl1012.patch mysql-test/t/rpl_loaddata.test: Import patch wl1012.patch mysql-test/t/rpl_log_pos.test: Import patch wl1012.patch mysql-test/t/rpl_multi_delete.test: Import patch wl1012.patch mysql-test/t/rpl_multi_update.test: Import patch wl1012.patch mysql-test/t/rpl_multi_update2.test: Import patch wl1012.patch mysql-test/t/rpl_multi_update3.test: Import patch wl1012.patch mysql-test/t/rpl_openssl.test: Import patch wl1012.patch mysql-test/t/rpl_redirect.test: Import patch wl1012.patch mysql-test/t/rpl_relayrotate.test: Import patch wl1012.patch mysql-test/t/rpl_replicate_do.test: Import patch wl1012.patch mysql-test/t/rpl_rotate_logs.test: Import patch wl1012.patch mysql-test/t/rpl_server_id1.test: Import patch wl1012.patch mysql-test/t/rpl_sp_effects.test: Import patch wl1012.patch mysql-test/t/rpl_temporary.test: Import patch wl1012.patch mysql-test/t/rpl_trigger.test: Import patch wl1012.patch mysql-test/t/sp.test: Import patch wl1012.patch mysql-test/t/user_var-binlog.test: Import patch wl1012.patch mysys/Makefile.am: Import patch wl1012.patch mysys/base64.c: Import patch wl1012.patch sql/Makefile.am: Import patch wl1012.patch sql/ha_innodb.cc: Import patch wl1012.patch sql/ha_innodb.h: Import patch wl1012.patch sql/ha_partition.cc: Import patch wl1012.patch sql/handler.cc: Import patch wl1012.patch sql/handler.h: Import patch wl1012.patch sql/item_sum.cc: Import patch wl1012.patch sql/log.cc: Import patch wl1012.patch sql/log_event.cc: Import patch wl1012.patch sql/log_event.h: Import patch wl1012.patch sql/mysql_priv.h: Import patch wl1012.patch sql/mysqld.cc: Import patch wl1012.patch sql/rpl_filter.h: Import patch wl1012.patch sql/set_var.cc: Import patch wl1012.patch sql/share/errmsg.txt: Import patch wl1012.patch sql/slave.cc: Import patch wl1012.patch sql/slave.h: Import patch wl1012.patch sql/sp.cc: Import patch wl1012.patch sql/sp_head.cc: Import patch wl1012.patch sql/sql_acl.cc: Import patch wl1012.patch sql/sql_base.cc: Import patch wl1012.patch sql/sql_class.cc: Import patch wl1012.patch sql/sql_class.h: Import patch wl1012.patch sql/sql_delete.cc: Import patch wl1012.patch sql/sql_insert.cc: Import patch wl1012.patch sql/sql_lex.h: Import patch wl1012.patch sql/sql_list.h: Import patch wl1012.patch sql/sql_load.cc: Import patch wl1012.patch sql/sql_parse.cc: Import patch wl1012.patch sql/sql_plugin.cc: Import patch wl1012.patch sql/sql_rename.cc: Import patch wl1012.patch sql/sql_repl.h: Import patch wl1012.patch sql/sql_select.cc: Import patch wl1012.patch sql/sql_show.cc: Import patch wl1012.patch sql/sql_table.cc: Import patch wl1012.patch sql/sql_udf.cc: Import patch wl1012.patch sql/sql_union.cc: Import patch wl1012.patch sql/sql_update.cc: Import patch wl1012.patch sql/sql_yacc.yy: Import patch wl1012.patch sql/table.cc: Import patch wl1012.patch sql/table.h: Import patch wl1012.patch storage/innobase/include/lock0lock.h: Import patch wl1012.patch storage/innobase/include/row0mysql.h: Import patch wl1012.patch storage/innobase/include/row0vers.h: Import patch wl1012.patch storage/innobase/lock/lock0lock.c: Import patch wl1012.patch storage/innobase/row/row0mysql.c: Import patch wl1012.patch storage/innobase/row/row0sel.c: Import patch wl1012.patch storage/innobase/row/row0vers.c: Import patch wl1012.patch
Diffstat (limited to 'storage')
-rw-r--r--storage/innobase/include/lock0lock.h20
-rw-r--r--storage/innobase/include/row0mysql.h30
-rw-r--r--storage/innobase/include/row0vers.h26
-rw-r--r--storage/innobase/lock/lock0lock.c68
-rw-r--r--storage/innobase/row/row0mysql.c14
-rw-r--r--storage/innobase/row/row0sel.c172
-rw-r--r--storage/innobase/row/row0vers.c138
7 files changed, 436 insertions, 32 deletions
diff --git a/storage/innobase/include/lock0lock.h b/storage/innobase/include/lock0lock.h
index 20b1f1d7145..86e579bc007 100644
--- a/storage/innobase/include/lock0lock.h
+++ b/storage/innobase/include/lock0lock.h
@@ -64,14 +64,6 @@ lock_clust_rec_some_has_impl(
dict_index_t* index, /* in: clustered index */
const ulint* offsets);/* in: rec_get_offsets(rec, index) */
/*****************************************************************
-Resets the lock bits for a single record. Releases transactions
-waiting for lock requests here. */
-
-void
-lock_rec_reset_and_release_wait(
-/*============================*/
- rec_t* rec); /* in: record whose locks bits should be reset */
-/*****************************************************************
Makes a record to inherit the locks of another record as gap type
locks, but does not reset the lock bits of the other record. Also
waiting lock requests on rec are inherited as GRANTED gap locks. */
@@ -427,6 +419,18 @@ lock_is_on_table(
/*=============*/
/* out: TRUE if there are lock(s) */
dict_table_t* table); /* in: database table in dictionary cache */
+/*****************************************************************
+Removes a granted record lock of a transaction from the queue and grants
+locks to other transactions waiting in the queue if they now are entitled
+to a lock. */
+
+void
+lock_rec_unlock(
+/*============*/
+ trx_t* trx, /* in: transaction that has set a record
+ lock */
+ rec_t* rec, /* in: record */
+ ulint lock_mode); /* in: LOCK_S or LOCK_X */
/*************************************************************************
Releases a table lock.
Releases possible other transactions waiting for this lock. */
diff --git a/storage/innobase/include/row0mysql.h b/storage/innobase/include/row0mysql.h
index b5da4634d98..4062179483d 100644
--- a/storage/innobase/include/row0mysql.h
+++ b/storage/innobase/include/row0mysql.h
@@ -612,6 +612,31 @@ struct row_prebuilt_struct {
that was decided in ha_innodb.cc,
::store_lock(), ::external_lock(),
etc. */
+ ulint row_read_type; /* ROW_READ_WITH_LOCKS if row locks
+ should be the obtained for records
+ under an UPDATE or DELETE cursor.
+ If innodb_locks_unsafe_for_binlog
+ is TRUE, this can be set to
+ ROW_READ_TRY_SEMI_CONSISTENT, so that
+ if the row under an UPDATE or DELETE
+ cursor was locked by another
+ transaction, InnoDB will resort
+ to reading the last committed value
+ ('semi-consistent read'). Then,
+ this field will be set to
+ ROW_READ_DID_SEMI_CONSISTENT to
+ indicate that. If the row does not
+ match the WHERE condition, MySQL will
+ invoke handler::unlock_row() to
+ clear the flag back to
+ ROW_READ_TRY_SEMI_CONSISTENT and
+ to simply skip the row. If
+ the row matches, the next call to
+ row_search_for_mysql() will lock
+ the row.
+ This eliminates lock waits in some
+ cases; note that this breaks
+ serializability. */
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
@@ -657,6 +682,11 @@ struct row_prebuilt_struct {
#define ROW_RETRIEVE_PRIMARY_KEY 1
#define ROW_RETRIEVE_ALL_COLS 2
+/* Values for row_read_type */
+#define ROW_READ_WITH_LOCKS 0
+#define ROW_READ_TRY_SEMI_CONSISTENT 1
+#define ROW_READ_DID_SEMI_CONSISTENT 2
+
#ifndef UNIV_NONINL
#include "row0mysql.ic"
diff --git a/storage/innobase/include/row0vers.h b/storage/innobase/include/row0vers.h
index 079d841f7f3..fafbe9a2402 100644
--- a/storage/innobase/include/row0vers.h
+++ b/storage/innobase/include/row0vers.h
@@ -92,6 +92,32 @@ row_vers_build_for_consistent_read(
record does not exist in the view, that is,
it was freshly inserted afterwards */
+/*********************************************************************
+Constructs the last committed version of a clustered index record,
+which should be seen by a semi-consistent read. */
+
+ulint
+row_vers_build_for_semi_consistent_read(
+/*====================================*/
+ /* out: DB_SUCCESS or DB_MISSING_HISTORY */
+ rec_t* rec, /* in: record in a clustered index; the
+ caller must have a latch on the page; this
+ latch locks the top of the stack of versions
+ of this records */
+ mtr_t* mtr, /* in: mtr holding the latch on rec */
+ dict_index_t* index, /* in: the clustered index */
+ ulint** offsets,/* in/out: offsets returned by
+ rec_get_offsets(rec, index) */
+ mem_heap_t** offset_heap,/* in/out: memory heap from which
+ the offsets are allocated */
+ mem_heap_t* in_heap,/* in: memory heap from which the memory for
+ old_vers is allocated; memory for possible
+ intermediate versions is allocated and freed
+ locally within the function */
+ rec_t** old_vers);/* out, own: rec, old version, or NULL if the
+ record does not exist in the view, that is,
+ it was freshly inserted afterwards */
+
#ifndef UNIV_NONINL
#include "row0vers.ic"
diff --git a/storage/innobase/lock/lock0lock.c b/storage/innobase/lock/lock0lock.c
index ae42dc31f82..a3d388f981d 100644
--- a/storage/innobase/lock/lock0lock.c
+++ b/storage/innobase/lock/lock0lock.c
@@ -2386,7 +2386,7 @@ lock_rec_free_all_from_discard_page(
/*****************************************************************
Resets the lock bits for a single record. Releases transactions waiting for
lock requests here. */
-
+static
void
lock_rec_reset_and_release_wait(
/*============================*/
@@ -3748,6 +3748,72 @@ lock_table_dequeue(
/*=========================== LOCK RELEASE ==============================*/
+/*****************************************************************
+Removes a granted record lock of a transaction from the queue and grants
+locks to other transactions waiting in the queue if they now are entitled
+to a lock. */
+
+void
+lock_rec_unlock(
+/*============*/
+ trx_t* trx, /* in: transaction that has set a record
+ lock */
+ rec_t* rec, /* in: record */
+ ulint lock_mode) /* in: LOCK_S or LOCK_X */
+{
+ lock_t* lock;
+ ulint heap_no;
+
+ ut_ad(trx && rec);
+
+ mutex_enter(&kernel_mutex);
+
+ heap_no = rec_get_heap_no(rec, page_rec_is_comp(rec));
+
+ lock = lock_rec_get_first(rec);
+
+ /* Remove the record lock */
+
+ while (lock != NULL) {
+ if (lock->trx == trx && lock_get_mode(lock) == lock_mode) {
+ ut_a(!lock_get_wait(lock));
+
+ lock_rec_reset_nth_bit(lock, heap_no);
+
+ break;
+ }
+
+ lock = lock_rec_get_next(rec, lock);
+ }
+
+ if (UNIV_UNLIKELY(lock == NULL)) {
+ mutex_exit(&kernel_mutex);
+ ut_print_timestamp(stderr);
+ fprintf(stderr,
+" InnoDB: Error: unlock row could not find a %lu mode lock on the record\n",
+ (ulong)lock_mode);
+
+ return;
+ }
+
+ /* Check if we can now grant waiting lock requests */
+
+ lock = lock_rec_get_first(rec);
+
+ while (lock != NULL) {
+ if (lock_get_wait(lock)
+ && !lock_rec_has_to_wait_in_queue(lock)) {
+
+ /* Grant the lock */
+ lock_grant(lock);
+ }
+
+ lock = lock_rec_get_next(rec, lock);
+ }
+
+ mutex_exit(&kernel_mutex);
+}
+
/*************************************************************************
Releases a table lock.
Releases possible other transactions waiting for this lock. */
diff --git a/storage/innobase/row/row0mysql.c b/storage/innobase/row/row0mysql.c
index 82f7daf2ed8..1a67ea26f5a 100644
--- a/storage/innobase/row/row0mysql.c
+++ b/storage/innobase/row/row0mysql.c
@@ -626,6 +626,8 @@ row_create_prebuilt(
prebuilt->select_lock_type = LOCK_NONE;
prebuilt->stored_select_lock_type = 99999999;
+ prebuilt->row_read_type = ROW_READ_WITH_LOCKS;
+
prebuilt->sel_graph = NULL;
prebuilt->search_tuple = dtuple_create(heap,
@@ -1486,11 +1488,7 @@ row_unlock_for_mysql(
rec = btr_pcur_get_rec(pcur);
- mutex_enter(&kernel_mutex);
-
- lock_rec_reset_and_release_wait(rec);
-
- mutex_exit(&kernel_mutex);
+ lock_rec_unlock(trx, rec, prebuilt->select_lock_type);
mtr_commit(&mtr);
@@ -1520,11 +1518,7 @@ row_unlock_for_mysql(
rec = btr_pcur_get_rec(clust_pcur);
- mutex_enter(&kernel_mutex);
-
- lock_rec_reset_and_release_wait(rec);
-
- mutex_exit(&kernel_mutex);
+ lock_rec_unlock(trx, rec, prebuilt->select_lock_type);
mtr_commit(&mtr);
}
diff --git a/storage/innobase/row/row0sel.c b/storage/innobase/row/row0sel.c
index 1b66f14f5d7..b08d27d2916 100644
--- a/storage/innobase/row/row0sel.c
+++ b/storage/innobase/row/row0sel.c
@@ -536,6 +536,41 @@ row_sel_build_prev_vers(
}
/*************************************************************************
+Builds the last committed version of a clustered index record for a
+semi-consistent read. */
+static
+ulint
+row_sel_build_committed_vers_for_mysql(
+/*===================================*/
+ /* out: DB_SUCCESS or error code */
+ dict_index_t* clust_index, /* in: clustered index */
+ row_prebuilt_t* prebuilt, /* in: prebuilt struct */
+ rec_t* rec, /* in: record in a clustered index */
+ ulint** offsets, /* in/out: offsets returned by
+ rec_get_offsets(rec, clust_index) */
+ mem_heap_t** offset_heap, /* in/out: memory heap from which
+ the offsets are allocated */
+ rec_t** old_vers, /* out: old version, or NULL if the
+ record does not exist in the view:
+ i.e., it was freshly inserted
+ afterwards */
+ mtr_t* mtr) /* in: mtr */
+{
+ ulint err;
+
+ if (prebuilt->old_vers_heap) {
+ mem_heap_empty(prebuilt->old_vers_heap);
+ } else {
+ prebuilt->old_vers_heap = mem_heap_create(200);
+ }
+
+ err = row_vers_build_for_semi_consistent_read(rec, mtr, clust_index,
+ offsets, offset_heap,
+ prebuilt->old_vers_heap, old_vers);
+ return(err);
+}
+
+/*************************************************************************
Tests the conditions which determine when the index segment we are searching
through has been exhausted. */
UNIV_INLINE
@@ -3066,7 +3101,6 @@ row_search_for_mysql(
rec_t* rec;
rec_t* result_rec;
rec_t* clust_rec;
- rec_t* old_vers;
ulint err = DB_SUCCESS;
ibool unique_search = FALSE;
ibool unique_search_from_clust_index = FALSE;
@@ -3077,6 +3111,11 @@ row_search_for_mysql(
locking SELECT, and the isolation
level is <= TRX_ISO_READ_COMMITTED,
then this is set to FALSE */
+ ibool did_semi_consistent_read = FALSE;
+ /* if the returned record was locked
+ and we did a semi-consistent read
+ (fetch the newest committed version),
+ then this is set to TRUE */
#ifdef UNIV_SEARCH_DEBUG
ulint cnt = 0;
#endif /* UNIV_SEARCH_DEBUG */
@@ -3163,7 +3202,7 @@ cursor lock count is done correctly. See bugs #12263 and #12456!
trx->search_latch_timeout = BTR_SEA_TIMEOUT;
}
- /* Reset the new record lock info if we srv_locks_unsafe_for_binlog
+ /* Reset the new record lock info if srv_locks_unsafe_for_binlog
is set. Then we are able to remove the record locks set here on an
individual row. */
@@ -3431,9 +3470,28 @@ shortcut_fails_too_big_rec:
clust_index = dict_table_get_first_index(index->table);
if (UNIV_LIKELY(direction != 0)) {
- if (!sel_restore_position_for_mysql(&same_user_rec,
- BTR_SEARCH_LEAF,
- pcur, moves_up, &mtr)) {
+ ibool need_to_process = sel_restore_position_for_mysql(
+ &same_user_rec, BTR_SEARCH_LEAF,
+ pcur, moves_up, &mtr);
+
+ if (UNIV_UNLIKELY(need_to_process)) {
+ if (UNIV_UNLIKELY(prebuilt->row_read_type
+ == ROW_READ_DID_SEMI_CONSISTENT)) {
+ /* We did a semi-consistent read,
+ but the record was removed in
+ the meantime. */
+ prebuilt->row_read_type
+ = ROW_READ_TRY_SEMI_CONSISTENT;
+ }
+ } else if (UNIV_LIKELY(prebuilt->row_read_type
+ != ROW_READ_DID_SEMI_CONSISTENT)) {
+
+ /* The cursor was positioned on the record
+ that we returned previously. If we need
+ to repeat a semi-consistent read as a
+ pessimistic locking read, the record
+ cannot be skipped. */
+
goto next_rec;
}
@@ -3751,7 +3809,64 @@ no_gap_lock:
prebuilt->select_lock_type,
lock_type, thr);
- if (err != DB_SUCCESS) {
+ switch (err) {
+ rec_t* old_vers;
+ case DB_SUCCESS:
+ break;
+ case DB_LOCK_WAIT:
+ if (UNIV_LIKELY(prebuilt->row_read_type
+ != ROW_READ_TRY_SEMI_CONSISTENT)
+ || index != clust_index) {
+
+ goto lock_wait_or_error;
+ }
+
+ /* The following call returns 'offsets'
+ associated with 'old_vers' */
+ err = row_sel_build_committed_vers_for_mysql(
+ clust_index, prebuilt, rec,
+ &offsets, &heap,
+ &old_vers, &mtr);
+
+ if (err != DB_SUCCESS) {
+
+ goto lock_wait_or_error;
+ }
+
+ mutex_enter(&kernel_mutex);
+ if (trx->was_chosen_as_deadlock_victim) {
+ mutex_exit(&kernel_mutex);
+
+ goto lock_wait_or_error;
+ }
+ if (UNIV_LIKELY(trx->wait_lock != NULL)) {
+ lock_cancel_waiting_and_release(
+ trx->wait_lock);
+ trx_reset_new_rec_lock_info(trx);
+ } else {
+ mutex_exit(&kernel_mutex);
+
+ /* The lock was granted while we were
+ searching for the last committed version.
+ Do a normal locking read. */
+
+ offsets = rec_get_offsets(rec, index, offsets,
+ ULINT_UNDEFINED, &heap);
+ err = DB_SUCCESS;
+ break;
+ }
+ mutex_exit(&kernel_mutex);
+
+ if (old_vers == NULL) {
+ /* The row was not yet committed */
+
+ goto next_rec;
+ }
+
+ did_semi_consistent_read = TRUE;
+ rec = old_vers;
+ break;
+ default:
goto lock_wait_or_error;
}
@@ -3775,6 +3890,7 @@ no_gap_lock:
&& !lock_clust_rec_cons_read_sees(rec, index,
offsets, trx->read_view)) {
+ rec_t* old_vers;
/* The following call returns 'offsets'
associated with 'old_vers' */
err = row_sel_build_prev_vers_for_mysql(
@@ -3821,14 +3937,13 @@ no_gap_lock:
/* The record is delete-marked: we can skip it */
if (srv_locks_unsafe_for_binlog
- && prebuilt->select_lock_type != LOCK_NONE) {
+ && prebuilt->select_lock_type != LOCK_NONE
+ && !did_semi_consistent_read) {
/* No need to keep a lock on a delete-marked record
if we do not want to use next-key locking. */
row_unlock_for_mysql(prebuilt, TRUE);
-
- trx_reset_new_rec_lock_info(trx);
}
goto next_rec;
@@ -3882,8 +3997,6 @@ requires_clust_rec:
locking. */
row_unlock_for_mysql(prebuilt, TRUE);
-
- trx_reset_new_rec_lock_info(trx);
}
goto next_rec;
@@ -3977,7 +4090,7 @@ got_row:
a unique search. */
if (!unique_search_from_clust_index
- || prebuilt->select_lock_type == LOCK_X
+ || prebuilt->select_lock_type != LOCK_NONE
|| prebuilt->used_in_HANDLER) {
/* Inside an update always store the cursor position */
@@ -3990,6 +4103,19 @@ got_row:
goto normal_return;
next_rec:
+ /* Reset the old and new "did semi-consistent read" flags. */
+ if (UNIV_UNLIKELY(prebuilt->row_read_type
+ == ROW_READ_DID_SEMI_CONSISTENT)) {
+ prebuilt->row_read_type = ROW_READ_TRY_SEMI_CONSISTENT;
+ }
+ did_semi_consistent_read = FALSE;
+
+ if (UNIV_UNLIKELY(srv_locks_unsafe_for_binlog)
+ && prebuilt->select_lock_type != LOCK_NONE) {
+
+ trx_reset_new_rec_lock_info(trx);
+ }
+
/*-------------------------------------------------------------*/
/* PHASE 5: Move the cursor to the next index record */
@@ -4042,8 +4168,14 @@ not_moved:
goto rec_loop;
lock_wait_or_error:
- /*-------------------------------------------------------------*/
+ /* Reset the old and new "did semi-consistent read" flags. */
+ if (UNIV_UNLIKELY(prebuilt->row_read_type
+ == ROW_READ_DID_SEMI_CONSISTENT)) {
+ prebuilt->row_read_type = ROW_READ_TRY_SEMI_CONSISTENT;
+ }
+ did_semi_consistent_read = FALSE;
+ /*-------------------------------------------------------------*/
btr_pcur_store_position(pcur, &mtr);
mtr_commit(&mtr);
@@ -4126,6 +4258,20 @@ func_exit:
if (UNIV_LIKELY_NULL(heap)) {
mem_heap_free(heap);
}
+
+ /* Set or reset the "did semi-consistent read" flag on return.
+ The flag did_semi_consistent_read is set if and only if
+ the record being returned was fetched with a semi-consistent read. */
+ ut_ad(prebuilt->row_read_type != ROW_READ_WITH_LOCKS
+ || !did_semi_consistent_read);
+
+ if (UNIV_UNLIKELY(prebuilt->row_read_type != ROW_READ_WITH_LOCKS)) {
+ if (UNIV_UNLIKELY(did_semi_consistent_read)) {
+ prebuilt->row_read_type = ROW_READ_DID_SEMI_CONSISTENT;
+ } else {
+ prebuilt->row_read_type = ROW_READ_TRY_SEMI_CONSISTENT;
+ }
+ }
return(err);
}
diff --git a/storage/innobase/row/row0vers.c b/storage/innobase/row/row0vers.c
index 8e747423047..b32ab8822f4 100644
--- a/storage/innobase/row/row0vers.c
+++ b/storage/innobase/row/row0vers.c
@@ -490,3 +490,141 @@ row_vers_build_for_consistent_read(
return(err);
}
+
+/*********************************************************************
+Constructs the last committed version of a clustered index record,
+which should be seen by a semi-consistent read. */
+
+ulint
+row_vers_build_for_semi_consistent_read(
+/*====================================*/
+ /* out: DB_SUCCESS or DB_MISSING_HISTORY */
+ rec_t* rec, /* in: record in a clustered index; the
+ caller must have a latch on the page; this
+ latch locks the top of the stack of versions
+ of this records */
+ mtr_t* mtr, /* in: mtr holding the latch on rec */
+ dict_index_t* index, /* in: the clustered index */
+ ulint** offsets,/* in/out: offsets returned by
+ rec_get_offsets(rec, index) */
+ mem_heap_t** offset_heap,/* in/out: memory heap from which
+ the offsets are allocated */
+ mem_heap_t* in_heap,/* in: memory heap from which the memory for
+ old_vers is allocated; memory for possible
+ intermediate versions is allocated and freed
+ locally within the function */
+ rec_t** old_vers)/* out, own: rec, old version, or NULL if the
+ record does not exist in the view, that is,
+ it was freshly inserted afterwards */
+{
+ rec_t* version;
+ mem_heap_t* heap = NULL;
+ byte* buf;
+ ulint err;
+ dulint rec_trx_id;
+
+ ut_ad(index->type & DICT_CLUSTERED);
+ ut_ad(mtr_memo_contains(mtr, buf_block_align(rec), MTR_MEMO_PAGE_X_FIX)
+ || mtr_memo_contains(mtr, buf_block_align(rec),
+ MTR_MEMO_PAGE_S_FIX));
+#ifdef UNIV_SYNC_DEBUG
+ ut_ad(!rw_lock_own(&(purge_sys->latch), RW_LOCK_SHARED));
+#endif /* UNIV_SYNC_DEBUG */
+
+ ut_ad(rec_offs_validate(rec, index, *offsets));
+
+ rw_lock_s_lock(&(purge_sys->latch));
+ /* The S-latch on purge_sys prevents the purge view from
+ changing. Thus, if we have an uncommitted transaction at
+ this point, then purge cannot remove its undo log even if
+ the transaction could commit now. */
+
+ version = rec;
+
+ for (;;) {
+ trx_t* version_trx;
+ mem_heap_t* heap2;
+ rec_t* prev_version;
+ dulint version_trx_id;
+
+ version_trx_id = row_get_rec_trx_id(
+ version, index, *offsets);
+ if (rec == version) {
+ rec_trx_id = version_trx_id;
+ }
+
+ mutex_enter(&kernel_mutex);
+ version_trx = trx_get_on_id(version_trx_id);
+ mutex_exit(&kernel_mutex);
+
+ if (!version_trx
+ || version_trx->conc_state == TRX_NOT_STARTED
+ || version_trx->conc_state == TRX_COMMITTED_IN_MEMORY) {
+
+ /* We found a version that belongs to a
+ committed transaction: return it. */
+
+ if (rec == version) {
+ *old_vers = rec;
+ err = DB_SUCCESS;
+ break;
+ }
+
+ /* We assume that a rolled-back transaction stays in
+ TRX_ACTIVE state until all the changes have been
+ rolled back and the transaction is removed from
+ the global list of transactions. */
+
+ if (!ut_dulint_cmp(rec_trx_id, version_trx_id)) {
+ /* The transaction was committed while
+ we searched for earlier versions.
+ Return the current version as a
+ semi-consistent read. */
+
+ version = rec;
+ *offsets = rec_get_offsets(version,
+ index, *offsets,
+ ULINT_UNDEFINED, offset_heap);
+ }
+
+ buf = mem_heap_alloc(in_heap, rec_offs_size(*offsets));
+ *old_vers = rec_copy(buf, version, *offsets);
+ rec_offs_make_valid(*old_vers, index, *offsets);
+ err = DB_SUCCESS;
+
+ break;
+ }
+
+ heap2 = heap;
+ heap = mem_heap_create(1024);
+
+ err = trx_undo_prev_version_build(rec, mtr, version, index,
+ *offsets, heap, &prev_version);
+ if (heap2) {
+ mem_heap_free(heap2); /* free version */
+ }
+
+ if (UNIV_UNLIKELY(err != DB_SUCCESS)) {
+ break;
+ }
+
+ if (prev_version == NULL) {
+ /* It was a freshly inserted version */
+ *old_vers = NULL;
+ err = DB_SUCCESS;
+
+ break;
+ }
+
+ version = prev_version;
+ *offsets = rec_get_offsets(version, index, *offsets,
+ ULINT_UNDEFINED, offset_heap);
+ }/* for (;;) */
+
+ if (heap) {
+ mem_heap_free(heap);
+ }
+ rw_lock_s_unlock(&(purge_sys->latch));
+
+ return(err);
+}