row0vers.c, row0sel.c, row0ins.c:

  Fix bug: InnoDB could print that it cannot find a clustered index record if an update undo, purge, and a consistent read coincided, in rare cases it might also have returned a wrong row in a query
parent 4deb135b
......@@ -791,9 +791,9 @@ row_ins_foreign_check_on_constraint(
mem_heap_free(tmp_heap);
clust_rec = btr_pcur_get_rec(cascade->pcur);
}
if (!page_rec_is_user_rec(clust_rec)) {
if (btr_pcur_get_low_match(cascade->pcur)
< dict_index_get_n_unique(clust_index)) {
fprintf(stderr,
"InnoDB: error in cascade of a foreign key op\n"
"InnoDB: index %s table %s\n", index->name,
......@@ -803,8 +803,8 @@ row_ins_foreign_check_on_constraint(
fprintf(stderr, "InnoDB: record %s\n", err_buf);
rec_sprintf(err_buf, 900, clust_rec);
fprintf(stderr, "InnoDB: clustered record %s\n", err_buf);
fprintf(stderr, "InnoDB: clustered record %s\n",
err_buf);
fprintf(stderr,
"InnoDB: Make a detailed bug report and send it\n");
fprintf(stderr, "InnoDB: to mysql@lists.mysql.com\n");
......@@ -813,6 +813,7 @@ row_ins_foreign_check_on_constraint(
goto nonstandard_exit_func;
}
}
/* Set an X-lock on the row to delete or update in the child table */
......
......@@ -609,7 +609,25 @@ row_sel_get_clust_rec(
clust_rec = btr_pcur_get_rec(&(plan->clust_pcur));
ut_ad(page_rec_is_user_rec(clust_rec));
if (btr_pcur_get_low_match(&(plan->clust_pcur))
< dict_index_get_n_unique(index)) {
ut_a(rec_get_deleted_flag(rec));
ut_a(node->read_view);
/* In a rare case it is possible that no clust rec is found
for a delete-marked secondary index record: if in row0umod.c
in row_undo_mod_remove_clust_low() we have already removed
the clust rec, while purge is still cleaning and removing
secondary index records associated with earlier versions of
the clustered index record. In that case we know that the
clustered index record did not exist in the read view of
trx. */
clust_rec = NULL;
goto func_exit;
}
if (!node->read_view) {
/* Try to place a lock on the index record */
......@@ -672,6 +690,7 @@ row_sel_get_clust_rec(
row_sel_fetch_columns(index, clust_rec,
UT_LIST_GET_FIRST(plan->columns));
func_exit:
*out_rec = clust_rec;
return(DB_SUCCESS);
......@@ -1253,6 +1272,8 @@ rec_loop:
/* PHASE 3: Get previous version in a consistent read */
cons_read_requires_clust_rec = FALSE;
if (consistent_read) {
/* This is a non-locking consistent read: if necessary, fetch
a previous version of the record */
......@@ -2269,7 +2290,10 @@ row_sel_get_clust_rec_for_mysql(
/* out: DB_SUCCESS or error code */
row_prebuilt_t* prebuilt,/* in: prebuilt struct in the handle */
dict_index_t* sec_index,/* in: secondary index where rec resides */
rec_t* rec, /* in: record in a non-clustered index */
rec_t* rec, /* in: record in a non-clustered index; if
this is a locking read, then rec is not
allowed to be delete-marked, and that would
not make sense either */
que_thr_t* thr, /* in: query thread */
rec_t** out_rec,/* out: clustered record or an old version of
it, NULL if the old version did not exist
......@@ -2298,7 +2322,21 @@ row_sel_get_clust_rec_for_mysql(
clust_rec = btr_pcur_get_rec(prebuilt->clust_pcur);
if (!page_rec_is_user_rec(clust_rec)) {
if (btr_pcur_get_low_match(prebuilt->clust_pcur)
< dict_index_get_n_unique(clust_index)) {
/* In a rare case it is possible that no clust rec is found
for a delete-marked secondary index record: if in row0umod.c
in row_undo_mod_remove_clust_low() we have already removed
the clust rec, while purge is still cleaning and removing
secondary index records associated with earlier versions of
the clustered index record. In that case we know that the
clustered index record did not exist in the read view of
trx. */
if (!rec_get_deleted_flag(rec)
|| prebuilt->select_lock_type != LOCK_NONE) {
ut_print_timestamp(stderr);
fprintf(stderr,
" InnoDB: error clustered record for sec rec not found\n"
......@@ -2306,10 +2344,12 @@ row_sel_get_clust_rec_for_mysql(
sec_index->table->name);
rec_sprintf(err_buf, 900, rec);
fprintf(stderr, "InnoDB: sec index record %s\n", err_buf);
fprintf(stderr,
"InnoDB: sec index record %s\n", err_buf);
rec_sprintf(err_buf, 900, clust_rec);
fprintf(stderr, "InnoDB: clust index record %s\n", err_buf);
fprintf(stderr,
"InnoDB: clust index record %s\n", err_buf);
trx = thr_get_trx(thr);
trx_print(err_buf, trx);
......@@ -2318,6 +2358,7 @@ row_sel_get_clust_rec_for_mysql(
"%s\nInnoDB: Make a detailed bug report and send it\n",
err_buf);
fprintf(stderr, "InnoDB: to mysql@lists.mysql.com\n");
}
clust_rec = NULL;
......@@ -2989,8 +3030,6 @@ rec_loop:
/*-------------------------------------------------------------*/
/* PHASE 4: Look for matching records in a loop */
cons_read_requires_clust_rec = FALSE;
rec = btr_pcur_get_rec(pcur);
/*
printf("Using index %s cnt %lu ", index->name, cnt);
......@@ -3145,6 +3184,8 @@ rec_loop:
/* We are ready to look at a possible new index entry in the result
set: the cursor is now placed on a user record */
cons_read_requires_clust_rec = FALSE;
if (prebuilt->select_lock_type != LOCK_NONE) {
/* Try to place a lock on the index record; note that delete
marked records are a special case in a unique search. If there
......@@ -3170,8 +3211,6 @@ rec_loop:
/* This is a non-locking consistent read: if necessary, fetch
a previous version of the record */
cons_read_requires_clust_rec = FALSE;
if (trx->isolation_level == TRX_ISO_READ_UNCOMMITTED) {
/* Do nothing: we let a non-locking SELECT read the
......@@ -3215,7 +3254,7 @@ rec_loop:
if (rec_get_deleted_flag(rec) && !cons_read_requires_clust_rec) {
/* The record is delete marked: we can skip it if this is
/* The record is delete-marked: we can skip it if this is
not a consistent read which might see an earlier version
of a non-clustered index record */
......
......@@ -59,7 +59,6 @@ row_vers_impl_x_locked_off_kernel(
ibool rec_del;
ulint err;
mtr_t mtr;
char err_buf[1000];
ut_ad(mutex_own(&kernel_mutex));
ut_ad(!rw_lock_own(&(purge_sys->latch), RW_LOCK_SHARED));
......@@ -77,22 +76,17 @@ row_vers_impl_x_locked_off_kernel(
clust_rec = row_get_clust_rec(BTR_SEARCH_LEAF, rec, index,
&clust_index, &mtr);
if (!clust_rec) {
rec_sprintf(err_buf, 900, rec);
ut_print_timestamp(stderr);
fprintf(stderr,
" InnoDB: Error: cannot find the clustered index record\n"
"InnoDB: for a secondary index record in table %s index %s.\n"
"InnoDB: Secondary index record %s.\n"
"InnoDB: The table is probably corrupt. Please run CHECK TABLE on it.\n"
"InnoDB: You can try to repair the table by dump + drop + reimport.\n"
"InnoDB: Send a detailed bug report to mysql@lists.mysql.com.\n",
index->table_name, index->name, err_buf);
mutex_enter(&kernel_mutex);
mtr_commit(&mtr);
/* We assume there is no lock on the record, though this
is not certain because the table is apparently corrupt */
/* In a rare case it is possible that no clust rec is found
for a secondary index record: if in row0umod.c
row_undo_mod_remove_clust_low() we have already removed the
clust rec, while purge is still cleaning and removing
secondary index records associated with earlier versions of
the clustered index record. In that case there cannot be
any implicit lock on the secondary index record, because
an active transaction which has modified the secondary index
record has also modified the clustered index record. And in
a rollback we always undo the modifications to secondary index
records before the clustered index record. */
return(NULL);
}
......
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