Commit 20c82f5c authored by unknown's avatar unknown

Fixed a bug in UPDATE statement with no index column in where condition

locks all rows (BUG #3300). When using innobase_locks_unsafe_for_binlog
option InnoDB does not take locks for those rows which do not
belong to the result set or werent changed by the query. This fix removes
unnecessary locks also from SELECT and DELETE queries.


innobase/include/row0mysql.h:
  Added prototype for row_unlock_for_mysql() function which does an unlock of 
  a row for MySQL.
innobase/include/trx0trx.h:
  Added a field trx_create_lock to a transaction structure. This field is
  TRUE if we have created a new lock for a record accessed.
innobase/lock/lock0lock.c:
  Set lock create flag if lock is created and reset this flag before 
  transaction requests a lock.
innobase/row/row0mysql.c:
  Add support for unlocking a row in InnoDB. If we are using 
  innobase_locks_unsafe_for_binlog option then all those record 
  locks obtained by SQL-query which do not belong to result set 
  or were not modified are unlocked i.e. we remove the lock from 
  those records.
sql/ha_innodb.cc:
  Added support for a unlock_row interface in InnoDB.
sql/ha_innodb.h:
  Added prototype for a function unlock_row().
parent bff1d475
...@@ -233,6 +233,17 @@ row_update_for_mysql( ...@@ -233,6 +233,17 @@ row_update_for_mysql(
the MySQL format */ the MySQL format */
row_prebuilt_t* prebuilt); /* in: prebuilt struct in MySQL row_prebuilt_t* prebuilt); /* in: prebuilt struct in MySQL
handle */ handle */
/*************************************************************************
Does an unlock of a row for MySQL. */
int
row_unlock_for_mysql(
/*=================*/
/* out: error code or DB_SUCCESS */
row_prebuilt_t* prebuilt); /* in: prebuilt struct in MySQL
handle */
/************************************************************************* /*************************************************************************
Creates an query graph node of 'update' type to be used in the MySQL Creates an query graph node of 'update' type to be used in the MySQL
interface. */ interface. */
......
...@@ -423,6 +423,8 @@ struct trx_struct{ ...@@ -423,6 +423,8 @@ struct trx_struct{
lock_t* auto_inc_lock; /* possible auto-inc lock reserved by lock_t* auto_inc_lock; /* possible auto-inc lock reserved by
the transaction; note that it is also the transaction; note that it is also
in the lock list trx_locks */ in the lock list trx_locks */
ibool trx_create_lock;/* this is TRUE if we have created a
new lock for a record accessed */
ulint n_lock_table_exp;/* number of explicit table locks ulint n_lock_table_exp;/* number of explicit table locks
(LOCK TABLES) reserved by the (LOCK TABLES) reserved by the
transaction, stored in trx_locks */ transaction, stored in trx_locks */
......
...@@ -1617,6 +1617,9 @@ lock_rec_create( ...@@ -1617,6 +1617,9 @@ lock_rec_create(
HASH_INSERT(lock_t, hash, lock_sys->rec_hash, HASH_INSERT(lock_t, hash, lock_sys->rec_hash,
lock_rec_fold(space, page_no), lock); lock_rec_fold(space, page_no), lock);
/* Note that we have create a new lock */
trx->trx_create_lock = TRUE;
if (type_mode & LOCK_WAIT) { if (type_mode & LOCK_WAIT) {
lock_set_lock_and_trx_wait(lock, trx); lock_set_lock_and_trx_wait(lock, trx);
...@@ -1791,6 +1794,15 @@ lock_rec_add_to_queue( ...@@ -1791,6 +1794,15 @@ lock_rec_add_to_queue(
if (similar_lock && !somebody_waits && !(type_mode & LOCK_WAIT)) { if (similar_lock && !somebody_waits && !(type_mode & LOCK_WAIT)) {
/* If the nth bit of a record lock is already set then we
do not set a new lock bit, otherwice we set */
if (lock_rec_get_nth_bit(similar_lock, heap_no)) {
trx->trx_create_lock = FALSE;
} else {
trx->trx_create_lock = TRUE;
}
lock_rec_set_nth_bit(similar_lock, heap_no); lock_rec_set_nth_bit(similar_lock, heap_no);
return(similar_lock); return(similar_lock);
...@@ -1822,6 +1834,7 @@ lock_rec_lock_fast( ...@@ -1822,6 +1834,7 @@ lock_rec_lock_fast(
{ {
lock_t* lock; lock_t* lock;
ulint heap_no; ulint heap_no;
trx_t* trx;
#ifdef UNIV_SYNC_DEBUG #ifdef UNIV_SYNC_DEBUG
ut_ad(mutex_own(&kernel_mutex)); ut_ad(mutex_own(&kernel_mutex));
...@@ -1840,9 +1853,12 @@ lock_rec_lock_fast( ...@@ -1840,9 +1853,12 @@ lock_rec_lock_fast(
lock = lock_rec_get_first_on_page(rec); lock = lock_rec_get_first_on_page(rec);
trx = thr_get_trx(thr);
trx->trx_create_lock = FALSE;
if (lock == NULL) { if (lock == NULL) {
if (!impl) { if (!impl) {
lock_rec_create(mode, rec, index, thr_get_trx(thr)); lock_rec_create(mode, rec, index, trx);
} }
return(TRUE); return(TRUE);
...@@ -1853,13 +1869,23 @@ lock_rec_lock_fast( ...@@ -1853,13 +1869,23 @@ lock_rec_lock_fast(
return(FALSE); return(FALSE);
} }
if (lock->trx != thr_get_trx(thr) if (lock->trx != trx
|| lock->type_mode != (mode | LOCK_REC) || lock->type_mode != (mode | LOCK_REC)
|| lock_rec_get_n_bits(lock) <= heap_no) { || lock_rec_get_n_bits(lock) <= heap_no) {
return(FALSE); return(FALSE);
} }
if (!impl) { if (!impl) {
/* If the nth bit of a record lock is already set then we
do not set a new lock bit, otherwice we set */
if (lock_rec_get_nth_bit(lock, heap_no)) {
trx->trx_create_lock = FALSE;
} else {
trx->trx_create_lock = TRUE;
}
lock_rec_set_nth_bit(lock, heap_no); lock_rec_set_nth_bit(lock, heap_no);
} }
......
...@@ -1186,6 +1186,57 @@ row_update_for_mysql( ...@@ -1186,6 +1186,57 @@ row_update_for_mysql(
return((int) err); return((int) err);
} }
/*************************************************************************
Does an unlock of a row for MySQL. */
int
row_unlock_for_mysql(
/*=================*/
/* out: error code or DB_SUCCESS */
row_prebuilt_t* prebuilt) /* in: prebuilt struct in MySQL
handle */
{
rec_t* rec;
btr_pcur_t* cur = prebuilt->pcur;
trx_t* trx = prebuilt->trx;
mtr_t mtr;
ut_ad(prebuilt && trx);
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
trx->op_info = "unlock_row";
if (srv_locks_unsafe_for_binlog) {
if (trx->trx_create_lock == TRUE) {
mtr_start(&mtr);
/* Restore a cursor position and find a record */
btr_pcur_restore_position(BTR_SEARCH_LEAF, cur, &mtr);
rec = btr_pcur_get_rec(cur);
if (rec) {
lock_rec_reset_and_release_wait(rec);
} else {
fputs("InnoDB: Error: "
"Record for the lock not found\n",
stderr);
mem_analyze_corruption((byte*) trx);
ut_error;
}
trx->trx_create_lock = FALSE;
mtr_commit(&mtr);
}
}
trx->op_info = "";
return(DB_SUCCESS);
}
/************************************************************************** /**************************************************************************
Does a cascaded delete or set null in a foreign key operation. */ Does a cascaded delete or set null in a foreign key operation. */
......
...@@ -2690,6 +2690,32 @@ ha_innobase::delete_row( ...@@ -2690,6 +2690,32 @@ ha_innobase::delete_row(
DBUG_RETURN(error); DBUG_RETURN(error);
} }
/**************************************************************************
Deletes a lock set to a row */
void
ha_innobase::unlock_row(void)
/*=========================*/
{
row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
DBUG_ENTER("ha_innobase::unlock_row");
ut_ad(prebuilt->trx ==
(trx_t*) current_thd->transaction.all.innobase_tid);
if (last_query_id != user_thd->query_id) {
ut_print_timestamp(stderr);
fprintf(stderr,
" InnoDB: Error: last_query_id is %lu != user_thd_query_id is %lu\n",
(ulong)last_query_id, (ulong)user_thd->query_id);
mem_analyze_corruption((byte *) prebuilt->trx);
ut_error;
}
row_unlock_for_mysql(prebuilt);
}
/********************************************************************** /**********************************************************************
Initializes a handle to use an index. */ Initializes a handle to use an index. */
......
...@@ -120,6 +120,7 @@ class ha_innobase: public handler ...@@ -120,6 +120,7 @@ class ha_innobase: public handler
int write_row(byte * buf); int write_row(byte * buf);
int update_row(const byte * old_data, byte * new_data); int update_row(const byte * old_data, byte * new_data);
int delete_row(const byte * buf); int delete_row(const byte * buf);
void unlock_row();
int index_init(uint index); int index_init(uint index);
int index_end(); int index_end();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment