mirror of
https://github.com/MariaDB/server.git
synced 2025-01-17 20:42:30 +01:00
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.
This commit is contained in:
parent
f5daa8785b
commit
91c8a65a80
7 changed files with 9 additions and 159 deletions
|
@ -78,8 +78,6 @@ struct undo_node_struct{
|
|||
dulint undo_no;/* undo number of the record */
|
||||
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
|
||||
record */
|
||||
btr_pcur_t pcur; /* persistent cursor used in searching the
|
||||
|
@ -101,11 +99,8 @@ struct undo_node_struct{
|
|||
/* Execution states for an undo node */
|
||||
#define UNDO_NODE_FETCH_NEXT 1 /* we should fetch the next undo log
|
||||
record */
|
||||
#define UNDO_NODE_PREV_VERS 2 /* the roll ptr to previous version of
|
||||
a row is stored in node, and undo
|
||||
should be done based on it */
|
||||
#define UNDO_NODE_INSERT 3
|
||||
#define UNDO_NODE_MODIFY 4
|
||||
#define UNDO_NODE_INSERT 2
|
||||
#define UNDO_NODE_MODIFY 3
|
||||
|
||||
|
||||
#ifndef UNIV_NONINL
|
||||
|
|
|
@ -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
|
||||
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. */
|
||||
static
|
||||
|
@ -202,17 +171,9 @@ row_undo_mod_clust(
|
|||
btr_pcur_t* pcur;
|
||||
mtr_t mtr;
|
||||
ulint err;
|
||||
ibool success;
|
||||
ibool more_vers;
|
||||
dulint new_undo_no;
|
||||
|
||||
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);
|
||||
|
||||
mtr_start(&mtr);
|
||||
|
@ -260,20 +221,6 @@ row_undo_mod_clust(
|
|||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -702,7 +649,6 @@ row_undo_mod_parse_undo_rec(
|
|||
trx_undo_update_rec_get_update(ptr, clust_index, type, trx_id,
|
||||
roll_ptr, info_bits, trx,
|
||||
node->heap, &(node->update));
|
||||
node->new_roll_ptr = roll_ptr;
|
||||
node->new_trx_id = trx_id;
|
||||
node->cmpl_info = cmpl_info;
|
||||
}
|
||||
|
|
|
@ -236,25 +236,6 @@ row_undo(
|
|||
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)) {
|
||||
|
||||
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)) {
|
||||
|
||||
node->state = UNDO_NODE_INSERT;
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
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
|
||||
|
||||
* btr/btr0cur.c, handler/ha_innodb.cc, ibuf/ibuf0ibuf.c,
|
||||
|
|
|
@ -87,10 +87,6 @@ that index record. */
|
|||
enum undo_exec {
|
||||
UNDO_NODE_FETCH_NEXT = 1, /*!< we should fetch the next
|
||||
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
|
||||
row to a table */
|
||||
UNDO_NODE_MODIFY /*!< undo a modify operation
|
||||
|
@ -108,9 +104,6 @@ struct undo_node_struct{
|
|||
undo_no_t undo_no;/*!< undo number of the record */
|
||||
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
|
||||
record */
|
||||
btr_pcur_t pcur; /*!< persistent cursor used in searching the
|
||||
|
|
|
@ -68,36 +68,6 @@ check.
|
|||
If you make a change in this module make sure that no codepath is
|
||||
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.
|
||||
@return DB_SUCCESS, DB_FAIL, or error code: we may run out of file space */
|
||||
|
@ -226,19 +196,11 @@ row_undo_mod_clust(
|
|||
btr_pcur_t* pcur;
|
||||
mtr_t mtr;
|
||||
ulint err;
|
||||
ibool success;
|
||||
ibool more_vers;
|
||||
undo_no_t new_undo_no;
|
||||
|
||||
ut_ad(node && thr);
|
||||
|
||||
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);
|
||||
|
||||
mtr_start(&mtr);
|
||||
|
@ -286,20 +248,6 @@ row_undo_mod_clust(
|
|||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -799,7 +747,6 @@ row_undo_mod_parse_undo_rec(
|
|||
trx_undo_update_rec_get_update(ptr, clust_index, type, trx_id,
|
||||
roll_ptr, info_bits, trx,
|
||||
node->heap, &(node->update));
|
||||
node->new_roll_ptr = roll_ptr;
|
||||
node->new_trx_id = trx_id;
|
||||
node->cmpl_info = cmpl_info;
|
||||
}
|
||||
|
|
|
@ -277,25 +277,6 @@ row_undo(
|
|||
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)) {
|
||||
|
||||
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)) {
|
||||
|
||||
node->state = UNDO_NODE_INSERT;
|
||||
|
|
Loading…
Reference in a new issue