Commit af27c38f authored by heikki@donna.mysql.fi's avatar heikki@donna.mysql.fi

row0sel.c Fix a bug in multiversioned reads through a secondary index

row0uins.c	Partial fix to the DROP TABLE + another user rolls back in that table problem
row0umod.c	Partial fix to the DROP TABLE + another user rolls back in that table problem
os0file.c	Reduce probability of deadlock bugs in connection with ibuf: do not let the ibuf i/o handler sleep
parent 0f9a30ab
......@@ -1348,6 +1348,10 @@ os_aio(
}
} else if (mode == OS_AIO_IBUF) {
ut_ad(type == OS_FILE_READ);
/* Reduce probability of deadlock bugs in connection with ibuf:
do not let the ibuf i/o handler sleep */
wake_later = FALSE;
array = os_aio_ibuf_array;
} else if (mode == OS_AIO_LOG) {
......
......@@ -48,6 +48,52 @@ to que_run_threads: this is to allow canceling runaway queries */
#define SEL_EXHAUSTED 1
#define SEL_RETRY 2
/************************************************************************
Returns TRUE if the user-defined column values in a secondary index record
are the same as the corresponding columns in the clustered index record. */
static
ibool
row_sel_sec_rec_is_for_clust_rec(
/*=============================*/
rec_t* sec_rec,
dict_index_t* sec_index,
rec_t* clust_rec,
dict_index_t* clust_index)
{
dict_col_t* col;
byte* sec_field;
ulint sec_len;
byte* clust_field;
ulint clust_len;
ulint n;
ulint i;
n = dict_index_get_n_ordering_defined_by_user(sec_index);
for (i = 0; i++; i < n) {
col = dict_field_get_col(
dict_index_get_nth_field(sec_index, i));
clust_field = rec_get_nth_field(clust_rec,
dict_col_get_clust_pos(col),
&clust_len);
sec_field = rec_get_nth_field(sec_rec, i, &sec_len);
if (sec_len != clust_len) {
return(FALSE);
}
if (sec_len != UNIV_SQL_NULL
&& ut_memcmp(sec_field, clust_field, sec_len) != 0) {
return(FALSE);
}
}
return(TRUE);
}
/*************************************************************************
Creates a select node struct. */
......@@ -561,6 +607,8 @@ row_sel_get_clust_rec(
/* This is a non-locking consistent read: if necessary, fetch
a previous version of the record */
old_vers = NULL;
if (!lock_clust_rec_cons_read_sees(clust_rec, index,
node->read_view)) {
......@@ -579,6 +627,28 @@ row_sel_get_clust_rec(
return(DB_SUCCESS);
}
}
/* If we had to go to an earlier version of row or the
secondary index record is delete marked, then it may be that
the secondary index record corresponding to clust_rec
(or old_vers) is not rec; in that case we must ignore
such row because in our snapshot rec would not have existed.
Remember that from rec we cannot see directly which transaction
id corresponds to it: we have to go to the clustered index
record. A query where we want to fetch all rows where
the secondary index value is in some interval would return
a wrong result if we would not drop rows which we come to
visit through secondary index records that would not really
exist in our snapshot. */
if ((old_vers || rec_get_deleted_flag(rec))
&& !row_sel_sec_rec_is_for_clust_rec(rec, plan->index,
clust_rec, index)) {
clust_rec = NULL;
*out_rec = clust_rec;
return(DB_SUCCESS);
}
}
/* Fetch the columns needed in test conditions */
......@@ -2106,6 +2176,8 @@ row_sel_get_clust_rec_for_mysql(
trx = thr_get_trx(thr);
old_vers = NULL;
if (!lock_clust_rec_cons_read_sees(clust_rec, clust_index,
trx->read_view)) {
......@@ -2121,6 +2193,25 @@ row_sel_get_clust_rec_for_mysql(
clust_rec = old_vers;
}
/* If we had to go to an earlier version of row or the
secondary index record is delete marked, then it may be that
the secondary index record corresponding to clust_rec
(or old_vers) is not rec; in that case we must ignore
such row because in our snapshot rec would not have existed.
Remember that from rec we cannot see directly which transaction
id corrsponds to it: we have to go to the clustered index
record. A query where we want to fetch all rows where
the secondary index value is in some interval would return
a wrong result if we would not drop rows which we come to
visit through secondary index records that would not really
exist in our snapshot. */
if ((old_vers || rec_get_deleted_flag(rec))
&& !row_sel_sec_rec_is_for_clust_rec(rec, sec_index,
clust_rec, clust_index)) {
clust_rec = NULL;
}
}
*out_rec = clust_rec;
......
......@@ -250,9 +250,12 @@ row_undo_ins_parse_undo_rec(
ut_ad(type == TRX_UNDO_INSERT_REC);
node->rec_type = type;
/* NOTE that the table has to be explicitly released later */
node->table = dict_table_get_on_id(table_id, node->trx);
if (node->table == NULL) {
return;
}
clust_index = dict_table_get_first_index(node->table);
ptr = trx_undo_rec_get_row_ref(ptr, clust_index, &(node->ref),
......@@ -280,7 +283,11 @@ row_undo_ins(
row_undo_ins_parse_undo_rec(node, thr);
if (node->table == NULL) {
found = FALSE;
} else {
found = row_undo_search_clust_to_pcur(node, thr);
}
if (!found) {
return(DB_SUCCESS);
......
......@@ -534,9 +534,16 @@ row_undo_mod_parse_undo_rec(
&undo_no, &table_id);
node->rec_type = type;
/* NOTE that the table has to be explicitly released later */
node->table = dict_table_get_on_id(table_id, thr_get_trx(thr));
/* TODO: other fixes associated with DROP TABLE + rollback in the
same table by another user */
if (node->table == NULL) {
/* Table was dropped */
return;
}
clust_index = dict_table_get_first_index(node->table);
ptr = trx_undo_update_rec_get_sys_cols(ptr, &trx_id, &roll_ptr,
......@@ -571,11 +578,16 @@ row_undo_mod(
row_undo_mod_parse_undo_rec(node, thr);
if (node->table == NULL) {
found = FALSE;
} else {
found = row_undo_search_clust_to_pcur(node, thr);
}
if (!found) {
/* It is already undone, or will be undone by another query
thread */
thread, or table was dropped */
node->state = UNDO_NODE_FETCH_NEXT;
......
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