Commit 91c8a65a authored by Annamalai Gurusami's avatar Annamalai Gurusami

Bug #13249921 ASSERT !BPAGE->FILE_PAGE_WAS_FREED, USUALLY IN

TRANSACTION ROLLBACK

Description:  During the rollback operation, a blob page 
is removed earlier than desired.  Consider following scenario:

1. create table t1(a int primary key,b blob) engine=innodb;
2. insert into t1 values (1,repeat('b',9000));
3. begin;
4. update t1 set b=concat(b,'b');
5. update t1 set a=a+1;
6. insert into t1 values (1,repeat('b',9000));
7. rollback;

The update operation in line 5 produces 2 undo log record. The first
undo record (TRX_UNDO_DEL_MARK_REC) goes to trx->update_undo and the
second undo record (TRX_UNDO_INSERT_REC) goes to trx->insert_undo.
During rollback, they are executed out of order.

When the undo record TRX_UNDO_DEL_MARK_REC is applied/executed,
the blob ownership is also reset.  Because of this the blob page
is released earlier than desired.  This blob page must have been
freed only as part of applying/executing the undo record
TRX_UNDO_INSERT_REC.

This problem can be avoided by executing the undo records in
order.  This patch will make innodb to execute the undo records
in order.

rb://1125 approved by Marko.
parent f5daa878
...@@ -78,8 +78,6 @@ struct undo_node_struct{ ...@@ -78,8 +78,6 @@ struct undo_node_struct{
dulint undo_no;/* undo number of the record */ dulint undo_no;/* undo number of the record */
ulint rec_type;/* undo log record type: TRX_UNDO_INSERT_REC, ulint rec_type;/* undo log record type: TRX_UNDO_INSERT_REC,
... */ ... */
dulint new_roll_ptr; /* roll ptr to restore to clustered index
record */
dulint new_trx_id; /* trx id to restore to clustered index dulint new_trx_id; /* trx id to restore to clustered index
record */ record */
btr_pcur_t pcur; /* persistent cursor used in searching the btr_pcur_t pcur; /* persistent cursor used in searching the
...@@ -101,11 +99,8 @@ struct undo_node_struct{ ...@@ -101,11 +99,8 @@ struct undo_node_struct{
/* Execution states for an undo node */ /* Execution states for an undo node */
#define UNDO_NODE_FETCH_NEXT 1 /* we should fetch the next undo log #define UNDO_NODE_FETCH_NEXT 1 /* we should fetch the next undo log
record */ record */
#define UNDO_NODE_PREV_VERS 2 /* the roll ptr to previous version of #define UNDO_NODE_INSERT 2
a row is stored in node, and undo #define UNDO_NODE_MODIFY 3
should be done based on it */
#define UNDO_NODE_INSERT 3
#define UNDO_NODE_MODIFY 4
#ifndef UNIV_NONINL #ifndef UNIV_NONINL
......
...@@ -41,37 +41,6 @@ delete marked clustered index record was delete unmarked and possibly also ...@@ -41,37 +41,6 @@ delete marked clustered index record was delete unmarked and possibly also
some of its fields were changed. Now, it is possible that the delete marked some of its fields were changed. Now, it is possible that the delete marked
version has become obsolete at the time the undo is started. */ version has become obsolete at the time the undo is started. */
/***************************************************************
Checks if also the previous version of the clustered index record was
modified or inserted by the same transaction, and its undo number is such
that it should be undone in the same rollback. */
UNIV_INLINE
ibool
row_undo_mod_undo_also_prev_vers(
/*=============================*/
/* out: TRUE if also previous modify or
insert of this row should be undone */
undo_node_t* node, /* in: row undo node */
dulint* undo_no)/* out: the undo number */
{
trx_undo_rec_t* undo_rec;
trx_t* trx;
trx = node->trx;
if (0 != ut_dulint_cmp(node->new_trx_id, trx->id)) {
*undo_no = ut_dulint_zero;
return(FALSE);
}
undo_rec = trx_undo_get_undo_rec_low(node->new_roll_ptr, node->heap);
*undo_no = trx_undo_rec_get_undo_no(undo_rec);
return(ut_dulint_cmp(trx->roll_limit, *undo_no) <= 0);
}
/*************************************************************** /***************************************************************
Undoes a modify in a clustered index record. */ Undoes a modify in a clustered index record. */
static static
...@@ -202,17 +171,9 @@ row_undo_mod_clust( ...@@ -202,17 +171,9 @@ row_undo_mod_clust(
btr_pcur_t* pcur; btr_pcur_t* pcur;
mtr_t mtr; mtr_t mtr;
ulint err; ulint err;
ibool success;
ibool more_vers;
dulint new_undo_no;
ut_ad(node && thr); ut_ad(node && thr);
/* Check if also the previous version of the clustered index record
should be undone in this same rollback operation */
more_vers = row_undo_mod_undo_also_prev_vers(node, &new_undo_no);
pcur = &(node->pcur); pcur = &(node->pcur);
mtr_start(&mtr); mtr_start(&mtr);
...@@ -260,20 +221,6 @@ row_undo_mod_clust( ...@@ -260,20 +221,6 @@ row_undo_mod_clust(
trx_undo_rec_release(node->trx, node->undo_no); trx_undo_rec_release(node->trx, node->undo_no);
if (more_vers && err == DB_SUCCESS) {
/* Reserve the undo log record to the prior version after
committing &mtr: this is necessary to comply with the latching
order, as &mtr may contain the fsp latch which is lower in
the latch hierarchy than trx->undo_mutex. */
success = trx_undo_rec_reserve(node->trx, new_undo_no);
if (success) {
node->state = UNDO_NODE_PREV_VERS;
}
}
return(err); return(err);
} }
...@@ -702,7 +649,6 @@ row_undo_mod_parse_undo_rec( ...@@ -702,7 +649,6 @@ row_undo_mod_parse_undo_rec(
trx_undo_update_rec_get_update(ptr, clust_index, type, trx_id, trx_undo_update_rec_get_update(ptr, clust_index, type, trx_id,
roll_ptr, info_bits, trx, roll_ptr, info_bits, trx,
node->heap, &(node->update)); node->heap, &(node->update));
node->new_roll_ptr = roll_ptr;
node->new_trx_id = trx_id; node->new_trx_id = trx_id;
node->cmpl_info = cmpl_info; node->cmpl_info = cmpl_info;
} }
......
...@@ -236,25 +236,6 @@ row_undo( ...@@ -236,25 +236,6 @@ row_undo(
node->roll_ptr = roll_ptr; node->roll_ptr = roll_ptr;
node->undo_no = trx_undo_rec_get_undo_no(node->undo_rec); node->undo_no = trx_undo_rec_get_undo_no(node->undo_rec);
if (trx_undo_roll_ptr_is_insert(roll_ptr)) {
node->state = UNDO_NODE_INSERT;
} else {
node->state = UNDO_NODE_MODIFY;
}
} else if (node->state == UNDO_NODE_PREV_VERS) {
/* Undo should be done to the same clustered index record
again in this same rollback, restoring the previous version */
roll_ptr = node->new_roll_ptr;
node->undo_rec = trx_undo_get_undo_rec_low(roll_ptr,
node->heap);
node->roll_ptr = roll_ptr;
node->undo_no = trx_undo_rec_get_undo_no(node->undo_rec);
if (trx_undo_roll_ptr_is_insert(roll_ptr)) { if (trx_undo_roll_ptr_is_insert(roll_ptr)) {
node->state = UNDO_NODE_INSERT; node->state = UNDO_NODE_INSERT;
......
2012-09-28 The InnoDB Team
* include/row0undo.h, row/row0umod.c, row/row0undo.c:
Fix Bug#13249921 ASSERT !BPAGE->FILE_PAGE_WAS_FREED, USUALLY
IN TRANSACTION ROLLBACK. This patch will ensure that the
undo logs will be applied in proper reverse order.
2012-09-18 The InnoDB Team 2012-09-18 The InnoDB Team
* btr/btr0cur.c, handler/ha_innodb.cc, ibuf/ibuf0ibuf.c, * btr/btr0cur.c, handler/ha_innodb.cc, ibuf/ibuf0ibuf.c,
......
...@@ -87,10 +87,6 @@ that index record. */ ...@@ -87,10 +87,6 @@ that index record. */
enum undo_exec { enum undo_exec {
UNDO_NODE_FETCH_NEXT = 1, /*!< we should fetch the next UNDO_NODE_FETCH_NEXT = 1, /*!< we should fetch the next
undo log record */ undo log record */
UNDO_NODE_PREV_VERS, /*!< the roll ptr to previous
version of a row is stored in
node, and undo should be done
based on it */
UNDO_NODE_INSERT, /*!< undo a fresh insert of a UNDO_NODE_INSERT, /*!< undo a fresh insert of a
row to a table */ row to a table */
UNDO_NODE_MODIFY /*!< undo a modify operation UNDO_NODE_MODIFY /*!< undo a modify operation
...@@ -108,9 +104,6 @@ struct undo_node_struct{ ...@@ -108,9 +104,6 @@ struct undo_node_struct{
undo_no_t undo_no;/*!< undo number of the record */ undo_no_t undo_no;/*!< undo number of the record */
ulint rec_type;/*!< undo log record type: TRX_UNDO_INSERT_REC, ulint rec_type;/*!< undo log record type: TRX_UNDO_INSERT_REC,
... */ ... */
roll_ptr_t new_roll_ptr;
/*!< roll ptr to restore to clustered index
record */
trx_id_t new_trx_id; /*!< trx id to restore to clustered index trx_id_t new_trx_id; /*!< trx id to restore to clustered index
record */ record */
btr_pcur_t pcur; /*!< persistent cursor used in searching the btr_pcur_t pcur; /*!< persistent cursor used in searching the
......
...@@ -68,36 +68,6 @@ check. ...@@ -68,36 +68,6 @@ check.
If you make a change in this module make sure that no codepath is If you make a change in this module make sure that no codepath is
introduced where a call to log_free_check() is bypassed. */ introduced where a call to log_free_check() is bypassed. */
/***********************************************************//**
Checks if also the previous version of the clustered index record was
modified or inserted by the same transaction, and its undo number is such
that it should be undone in the same rollback.
@return TRUE if also previous modify or insert of this row should be undone */
static
ibool
row_undo_mod_undo_also_prev_vers(
/*=============================*/
undo_node_t* node, /*!< in: row undo node */
undo_no_t* undo_no)/*!< out: the undo number */
{
trx_undo_rec_t* undo_rec;
trx_t* trx;
trx = node->trx;
if (0 != ut_dulint_cmp(node->new_trx_id, trx->id)) {
*undo_no = ut_dulint_zero;
return(FALSE);
}
undo_rec = trx_undo_get_undo_rec_low(node->new_roll_ptr, node->heap);
*undo_no = trx_undo_rec_get_undo_no(undo_rec);
return(ut_dulint_cmp(trx->roll_limit, *undo_no) <= 0);
}
/***********************************************************//** /***********************************************************//**
Undoes a modify in a clustered index record. Undoes a modify in a clustered index record.
@return DB_SUCCESS, DB_FAIL, or error code: we may run out of file space */ @return DB_SUCCESS, DB_FAIL, or error code: we may run out of file space */
...@@ -226,19 +196,11 @@ row_undo_mod_clust( ...@@ -226,19 +196,11 @@ row_undo_mod_clust(
btr_pcur_t* pcur; btr_pcur_t* pcur;
mtr_t mtr; mtr_t mtr;
ulint err; ulint err;
ibool success;
ibool more_vers;
undo_no_t new_undo_no;
ut_ad(node && thr); ut_ad(node && thr);
log_free_check(); log_free_check();
/* Check if also the previous version of the clustered index record
should be undone in this same rollback operation */
more_vers = row_undo_mod_undo_also_prev_vers(node, &new_undo_no);
pcur = &(node->pcur); pcur = &(node->pcur);
mtr_start(&mtr); mtr_start(&mtr);
...@@ -286,20 +248,6 @@ row_undo_mod_clust( ...@@ -286,20 +248,6 @@ row_undo_mod_clust(
trx_undo_rec_release(node->trx, node->undo_no); trx_undo_rec_release(node->trx, node->undo_no);
if (more_vers && err == DB_SUCCESS) {
/* Reserve the undo log record to the prior version after
committing &mtr: this is necessary to comply with the latching
order, as &mtr may contain the fsp latch which is lower in
the latch hierarchy than trx->undo_mutex. */
success = trx_undo_rec_reserve(node->trx, new_undo_no);
if (success) {
node->state = UNDO_NODE_PREV_VERS;
}
}
return(err); return(err);
} }
...@@ -799,7 +747,6 @@ row_undo_mod_parse_undo_rec( ...@@ -799,7 +747,6 @@ row_undo_mod_parse_undo_rec(
trx_undo_update_rec_get_update(ptr, clust_index, type, trx_id, trx_undo_update_rec_get_update(ptr, clust_index, type, trx_id,
roll_ptr, info_bits, trx, roll_ptr, info_bits, trx,
node->heap, &(node->update)); node->heap, &(node->update));
node->new_roll_ptr = roll_ptr;
node->new_trx_id = trx_id; node->new_trx_id = trx_id;
node->cmpl_info = cmpl_info; node->cmpl_info = cmpl_info;
} }
......
...@@ -277,25 +277,6 @@ row_undo( ...@@ -277,25 +277,6 @@ row_undo(
node->roll_ptr = roll_ptr; node->roll_ptr = roll_ptr;
node->undo_no = trx_undo_rec_get_undo_no(node->undo_rec); node->undo_no = trx_undo_rec_get_undo_no(node->undo_rec);
if (trx_undo_roll_ptr_is_insert(roll_ptr)) {
node->state = UNDO_NODE_INSERT;
} else {
node->state = UNDO_NODE_MODIFY;
}
} else if (node->state == UNDO_NODE_PREV_VERS) {
/* Undo should be done to the same clustered index record
again in this same rollback, restoring the previous version */
roll_ptr = node->new_roll_ptr;
node->undo_rec = trx_undo_get_undo_rec_low(roll_ptr,
node->heap);
node->roll_ptr = roll_ptr;
node->undo_no = trx_undo_rec_get_undo_no(node->undo_rec);
if (trx_undo_roll_ptr_is_insert(roll_ptr)) { if (trx_undo_roll_ptr_is_insert(roll_ptr)) {
node->state = UNDO_NODE_INSERT; node->state = UNDO_NODE_INSERT;
......
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