Skip to content

Commit a3f4bd2

Browse files
author
jan@hundin.mysql.fi
committed
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.
1 parent 7c80937 commit a3f4bd2

File tree

6 files changed

+119
-2
lines changed

6 files changed

+119
-2
lines changed

innobase/include/row0mysql.h

+11
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,17 @@ row_update_for_mysql(
233233
the MySQL format */
234234
row_prebuilt_t* prebuilt); /* in: prebuilt struct in MySQL
235235
handle */
236+
237+
/*************************************************************************
238+
Does an unlock of a row for MySQL. */
239+
240+
int
241+
row_unlock_for_mysql(
242+
/*=================*/
243+
/* out: error code or DB_SUCCESS */
244+
row_prebuilt_t* prebuilt); /* in: prebuilt struct in MySQL
245+
handle */
246+
236247
/*************************************************************************
237248
Creates an query graph node of 'update' type to be used in the MySQL
238249
interface. */

innobase/include/trx0trx.h

+2
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,8 @@ struct trx_struct{
423423
lock_t* auto_inc_lock; /* possible auto-inc lock reserved by
424424
the transaction; note that it is also
425425
in the lock list trx_locks */
426+
ibool trx_create_lock;/* this is TRUE if we have created a
427+
new lock for a record accessed */
426428
ulint n_lock_table_exp;/* number of explicit table locks
427429
(LOCK TABLES) reserved by the
428430
transaction, stored in trx_locks */

innobase/lock/lock0lock.c

+28-2
Original file line numberDiff line numberDiff line change
@@ -1617,6 +1617,9 @@ lock_rec_create(
16171617

16181618
HASH_INSERT(lock_t, hash, lock_sys->rec_hash,
16191619
lock_rec_fold(space, page_no), lock);
1620+
/* Note that we have create a new lock */
1621+
trx->trx_create_lock = TRUE;
1622+
16201623
if (type_mode & LOCK_WAIT) {
16211624

16221625
lock_set_lock_and_trx_wait(lock, trx);
@@ -1791,6 +1794,15 @@ lock_rec_add_to_queue(
17911794

17921795
if (similar_lock && !somebody_waits && !(type_mode & LOCK_WAIT)) {
17931796

1797+
/* If the nth bit of a record lock is already set then we
1798+
do not set a new lock bit, otherwice we set */
1799+
1800+
if (lock_rec_get_nth_bit(similar_lock, heap_no)) {
1801+
trx->trx_create_lock = FALSE;
1802+
} else {
1803+
trx->trx_create_lock = TRUE;
1804+
}
1805+
17941806
lock_rec_set_nth_bit(similar_lock, heap_no);
17951807

17961808
return(similar_lock);
@@ -1822,6 +1834,7 @@ lock_rec_lock_fast(
18221834
{
18231835
lock_t* lock;
18241836
ulint heap_no;
1837+
trx_t* trx;
18251838

18261839
#ifdef UNIV_SYNC_DEBUG
18271840
ut_ad(mutex_own(&kernel_mutex));
@@ -1840,9 +1853,12 @@ lock_rec_lock_fast(
18401853

18411854
lock = lock_rec_get_first_on_page(rec);
18421855

1856+
trx = thr_get_trx(thr);
1857+
trx->trx_create_lock = FALSE;
1858+
18431859
if (lock == NULL) {
18441860
if (!impl) {
1845-
lock_rec_create(mode, rec, index, thr_get_trx(thr));
1861+
lock_rec_create(mode, rec, index, trx);
18461862
}
18471863

18481864
return(TRUE);
@@ -1853,13 +1869,23 @@ lock_rec_lock_fast(
18531869
return(FALSE);
18541870
}
18551871

1856-
if (lock->trx != thr_get_trx(thr)
1872+
if (lock->trx != trx
18571873
|| lock->type_mode != (mode | LOCK_REC)
18581874
|| lock_rec_get_n_bits(lock) <= heap_no) {
18591875
return(FALSE);
18601876
}
18611877

18621878
if (!impl) {
1879+
1880+
/* If the nth bit of a record lock is already set then we
1881+
do not set a new lock bit, otherwice we set */
1882+
1883+
if (lock_rec_get_nth_bit(lock, heap_no)) {
1884+
trx->trx_create_lock = FALSE;
1885+
} else {
1886+
trx->trx_create_lock = TRUE;
1887+
}
1888+
18631889
lock_rec_set_nth_bit(lock, heap_no);
18641890
}
18651891

innobase/row/row0mysql.c

+51
Original file line numberDiff line numberDiff line change
@@ -1186,6 +1186,57 @@ row_update_for_mysql(
11861186
return((int) err);
11871187
}
11881188

1189+
/*************************************************************************
1190+
Does an unlock of a row for MySQL. */
1191+
1192+
int
1193+
row_unlock_for_mysql(
1194+
/*=================*/
1195+
/* out: error code or DB_SUCCESS */
1196+
row_prebuilt_t* prebuilt) /* in: prebuilt struct in MySQL
1197+
handle */
1198+
{
1199+
rec_t* rec;
1200+
btr_pcur_t* cur = prebuilt->pcur;
1201+
trx_t* trx = prebuilt->trx;
1202+
mtr_t mtr;
1203+
1204+
ut_ad(prebuilt && trx);
1205+
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
1206+
1207+
trx->op_info = "unlock_row";
1208+
1209+
if (srv_locks_unsafe_for_binlog) {
1210+
if (trx->trx_create_lock == TRUE) {
1211+
1212+
mtr_start(&mtr);
1213+
1214+
/* Restore a cursor position and find a record */
1215+
btr_pcur_restore_position(BTR_SEARCH_LEAF, cur, &mtr);
1216+
rec = btr_pcur_get_rec(cur);
1217+
1218+
if (rec) {
1219+
1220+
lock_rec_reset_and_release_wait(rec);
1221+
} else {
1222+
fputs("InnoDB: Error: "
1223+
"Record for the lock not found\n",
1224+
stderr);
1225+
mem_analyze_corruption((byte*) trx);
1226+
ut_error;
1227+
}
1228+
1229+
trx->trx_create_lock = FALSE;
1230+
mtr_commit(&mtr);
1231+
}
1232+
1233+
}
1234+
1235+
trx->op_info = "";
1236+
1237+
return(DB_SUCCESS);
1238+
}
1239+
11891240
/**************************************************************************
11901241
Does a cascaded delete or set null in a foreign key operation. */
11911242

sql/ha_innodb.cc

+26
Original file line numberDiff line numberDiff line change
@@ -2690,6 +2690,32 @@ ha_innobase::delete_row(
26902690
DBUG_RETURN(error);
26912691
}
26922692

2693+
/**************************************************************************
2694+
Deletes a lock set to a row */
2695+
2696+
void
2697+
ha_innobase::unlock_row(void)
2698+
/*=========================*/
2699+
{
2700+
row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
2701+
2702+
DBUG_ENTER("ha_innobase::unlock_row");
2703+
2704+
ut_ad(prebuilt->trx ==
2705+
(trx_t*) current_thd->transaction.all.innobase_tid);
2706+
2707+
if (last_query_id != user_thd->query_id) {
2708+
ut_print_timestamp(stderr);
2709+
fprintf(stderr,
2710+
" InnoDB: Error: last_query_id is %lu != user_thd_query_id is %lu\n",
2711+
(ulong)last_query_id, (ulong)user_thd->query_id);
2712+
mem_analyze_corruption((byte *) prebuilt->trx);
2713+
ut_error;
2714+
}
2715+
2716+
row_unlock_for_mysql(prebuilt);
2717+
}
2718+
26932719
/**********************************************************************
26942720
Initializes a handle to use an index. */
26952721

sql/ha_innodb.h

+1
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ class ha_innobase: public handler
120120
int write_row(byte * buf);
121121
int update_row(const byte * old_data, byte * new_data);
122122
int delete_row(const byte * buf);
123+
void unlock_row();
123124

124125
int index_init(uint index);
125126
int index_end();

0 commit comments

Comments
 (0)