Пример #1
0
/***********************************************************//**
Undoes a fresh insert of a row to a table. A fresh insert means that
the same clustered index unique key did not have any record, even delete
marked, at the time of the insert.  InnoDB is eager in a rollback:
if it figures out that an index record will be removed in the purge
anyway, it will remove it in the rollback.
@return	DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
UNIV_INTERN
ulint
row_undo_ins(
/*=========*/
	undo_node_t*	node)	/*!< in: row undo node */
{
	ut_ad(node);
	ut_ad(node->state == UNDO_NODE_INSERT);

	row_undo_ins_parse_undo_rec(node);

	if (!node->table || !row_undo_search_clust_to_pcur(node)) {
		trx_undo_rec_release(node->trx, node->undo_no);

		return(DB_SUCCESS);
	}

	/* Iterate over all the indexes and undo the insert.*/

	/* Skip the clustered index (the first index) */
	node->index = dict_table_get_next_index(
		dict_table_get_first_index(node->table));

	dict_table_skip_corrupt_index(node->index);

	while (node->index != NULL) {
		dtuple_t*	entry;
		ulint		err;

		entry = row_build_index_entry(node->row, node->ext,
					      node->index, node->heap);
		if (UNIV_UNLIKELY(!entry)) {
			/* The database must have crashed after
			inserting a clustered index record but before
			writing all the externally stored columns of
			that record.  Because secondary index entries
			are inserted after the clustered index record,
			we may assume that the secondary index record
			does not exist.  However, this situation may
			only occur during the rollback of incomplete
			transactions. */
			ut_a(trx_is_recv(node->trx));
		} else {
			log_free_check();
			err = row_undo_ins_remove_sec(node->index, entry);

			if (err != DB_SUCCESS) {

				return(err);
			}
		}

		dict_table_next_uncorrupted_index(node->index);
	}

	log_free_check();
	return(row_undo_ins_remove_clust_rec(node));
}
Пример #2
0
/***********************************************************//**
Undoes a fresh insert of a row to a table. A fresh insert means that
the same clustered index unique key did not have any record, even delete
marked, at the time of the insert.  InnoDB is eager in a rollback:
if it figures out that an index record will be removed in the purge
anyway, it will remove it in the rollback.
@return	DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
UNIV_INTERN
ulint
row_undo_ins(
/*=========*/
	undo_node_t*	node)	/*!< in: row undo node */
{
	ut_ad(node);
	ut_ad(node->state == UNDO_NODE_INSERT);

	row_undo_ins_parse_undo_rec(node);

	if (!node->table || !row_undo_search_clust_to_pcur(node)) {
		trx_undo_rec_release(node->trx, node->undo_no);

		return(DB_SUCCESS);
	}

	/* Iterate over all the indexes and undo the insert.*/

	/* Skip the clustered index (the first index) */
	node->index = dict_table_get_next_index(
		dict_table_get_first_index(node->table));

	while (node->index != NULL) {
		dtuple_t*	entry;
		ulint		err;

		entry = row_build_index_entry(node->row, node->ext,
					      node->index, node->heap);
		if (UNIV_UNLIKELY(!entry)) {
			/* The database must have crashed after
			inserting a clustered index record but before
			writing all the externally stored columns of
			that record, or a statement is being rolled
			back because an error occurred while storing
			off-page columns.

			Because secondary index entries are inserted
			after the clustered index record, we may
			assume that the secondary index record does
			not exist. */
		} else {
			log_free_check();
			err = row_undo_ins_remove_sec(node->index, entry);

			if (err != DB_SUCCESS) {

				return(err);
			}
		}

		node->index = dict_table_get_next_index(node->index);
	}

	log_free_check();
	return(row_undo_ins_remove_clust_rec(node));
}
Пример #3
0
/***************************************************************//**
Removes a secondary index entry if found.
@return	DB_SUCCESS, DB_FAIL, or DB_OUT_OF_FILE_SPACE */
static
ulint
row_undo_ins_remove_sec_low(
    /*========================*/
    ulint		mode,	/*!< in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE,
				depending on whether we wish optimistic or
				pessimistic descent down the index tree */
    dict_index_t*	index,	/*!< in: index */
    dtuple_t*	entry)	/*!< in: index entry to remove */
{
    btr_pcur_t	pcur;
    btr_cur_t*	btr_cur;
    ibool		found;
    ibool		success;
    ulint		err;
    mtr_t		mtr;

    log_free_check();
    mtr_start(&mtr);

    found = row_search_index_entry(index, entry, mode, &pcur, &mtr);

    btr_cur = btr_pcur_get_btr_cur(&pcur);

    if (!found) {
        /* Not found */

        btr_pcur_close(&pcur);
        mtr_commit(&mtr);

        return(DB_SUCCESS);
    }

    if (mode == BTR_MODIFY_LEAF) {
        success = btr_cur_optimistic_delete(btr_cur, &mtr);

        if (success) {
            err = DB_SUCCESS;
        } else {
            err = DB_FAIL;
        }
    } else {
        ut_ad(mode == BTR_MODIFY_TREE);

        /* No need to distinguish RB_RECOVERY here, because we
        are deleting a secondary index record: the distinction
        between RB_NORMAL and RB_RECOVERY only matters when
        deleting a record that contains externally stored
        columns. */
        ut_ad(!dict_index_is_clust(index));
        btr_cur_pessimistic_delete(&err, FALSE, btr_cur,
                                   RB_NORMAL, &mtr);
    }

    btr_pcur_close(&pcur);
    mtr_commit(&mtr);

    return(err);
}
Пример #4
0
/*******************************************************************
Removes a secondary index entry if found. */
static
ulint
row_undo_ins_remove_sec_low(
/*========================*/
				/* out: DB_SUCCESS, DB_FAIL, or
				DB_OUT_OF_FILE_SPACE */
	ulint		mode,	/* in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE,
				depending on whether we wish optimistic or
				pessimistic descent down the index tree */
	dict_index_t*	index,	/* in: index */
	dtuple_t*	entry)	/* in: index entry to remove */
{
	btr_pcur_t	pcur;
	btr_cur_t*	btr_cur;
	ibool		found;
	ibool		success;
	ulint		err;
	mtr_t		mtr;

	log_free_check();
	mtr_start(&mtr);

	found = row_search_index_entry(index, entry, mode, &pcur, &mtr);

	btr_cur = btr_pcur_get_btr_cur(&pcur);

	if (!found) {
		/* Not found */

		btr_pcur_close(&pcur);
		mtr_commit(&mtr);

		return(DB_SUCCESS);
	}

	if (mode == BTR_MODIFY_LEAF) {
		success = btr_cur_optimistic_delete(btr_cur, &mtr);

		if (success) {
			err = DB_SUCCESS;
		} else {
			err = DB_FAIL;
		}
	} else {
		ut_ad(mode == BTR_MODIFY_TREE);

		btr_cur_pessimistic_delete(&err, FALSE, btr_cur, TRUE, &mtr);
	}

	btr_pcur_close(&pcur);
	mtr_commit(&mtr);

	return(err);
}
Пример #5
0
/*******************************************************************
Removes a secondary index entry if found. */
static
ulint
row_undo_ins_remove_sec_low(
/*========================*/
				/* out: DB_SUCCESS, DB_FAIL, or
				DB_OUT_OF_FILE_SPACE */
	ulint		mode,	/* in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE,
				depending on whether we wish optimistic or
				pessimistic descent down the index tree */
	dict_index_t*	index,	/* in: index */
	dtuple_t*	entry,	/* in: index entry to remove */
	que_thr_t*	thr)	/* in: query thread */
{
	btr_pcur_t	pcur;		
	btr_cur_t*	btr_cur;
	ibool		found;
	ibool		success;
	ulint		err;
	mtr_t		mtr;
	
	UT_NOT_USED(thr);

	log_free_check();
	mtr_start(&mtr);

	found = row_search_index_entry(index, entry, mode, &pcur, &mtr);

	btr_cur = btr_pcur_get_btr_cur(&pcur);

	if (!found) {
		/* Not found */

		/* FIXME: remove printfs in the final version */

		/* printf(
		"--UNDO INS: Record not found from page %lu index %s\n",
			buf_frame_get_page_no(btr_cur_get_rec(btr_cur)),
			index->name); */

		/* ibuf_print(); */

		btr_pcur_close(&pcur);
		mtr_commit(&mtr);

		return(DB_SUCCESS);
	}

	if (mode == BTR_MODIFY_LEAF) {
		success = btr_cur_optimistic_delete(btr_cur, &mtr);

		if (success) {
			err = DB_SUCCESS;
		} else {
			err = DB_FAIL;
		}
	} else {
		ut_ad(mode == BTR_MODIFY_TREE);

		btr_cur_pessimistic_delete(&err, FALSE, btr_cur, TRUE, &mtr);
	}

	btr_pcur_close(&pcur);
	mtr_commit(&mtr);

	return(err);
}
Пример #6
0
/***************************************************************
Updates a secondary index entry of a row. */
static
ulint
row_upd_sec_index_entry(
/*====================*/
				/* out: DB_SUCCESS if operation successfully
				completed, else error code or DB_LOCK_WAIT */
	upd_node_t*	node,	/* in: row update node */
	que_thr_t*	thr)	/* in: query thread */
{
	ibool		check_ref;
	ibool		found;
	dict_index_t*	index;
	dtuple_t*	entry;
	btr_pcur_t	pcur;
	btr_cur_t*	btr_cur;
	mem_heap_t*	heap;
	rec_t*		rec;
	ulint		err	= DB_SUCCESS;
	mtr_t		mtr;
	char           	err_buf[1000];
	
	index = node->index;
	
	check_ref = row_upd_index_is_referenced(index);

	heap = mem_heap_create(1024);

	/* Build old index entry */
	entry = row_build_index_entry(node->row, index, heap);

	log_free_check();
	mtr_start(&mtr);
	
	found = row_search_index_entry(index, entry, BTR_MODIFY_LEAF, &pcur,
									&mtr);
	btr_cur = btr_pcur_get_btr_cur(&pcur);

	rec = btr_cur_get_rec(btr_cur);

	if (!found) {
	  	fprintf(stderr, "InnoDB: error in sec index entry update in\n"
		  	"InnoDB: index %s table %s\n", index->name,
		  	index->table->name);
	  	dtuple_sprintf(err_buf, 900, entry);
	  	fprintf(stderr, "InnoDB: tuple %s\n", err_buf);

	  	rec_sprintf(err_buf, 900, rec);
	  	fprintf(stderr, "InnoDB: record %s\n", err_buf);

	  	fprintf(stderr,
			"InnoDB: Make a detailed bug report and send it\n");
	  	fprintf(stderr, "InnoDB: to [email protected]\n");

		trx_print(thr_get_trx(thr));
	} else {
 	  	/* Delete mark the old index record; it can already be
          	delete marked if we return after a lock wait in
          	row_ins_index_entry below */

	  	if (!rec_get_deleted_flag(rec)) {
			err = btr_cur_del_mark_set_sec_rec(0, btr_cur, TRUE,
								thr, &mtr);
			if (err == DB_SUCCESS && check_ref) {
				/* NOTE that the following call loses
				the position of pcur ! */
				err = row_upd_check_references_constraints(
							&pcur, index->table,
							index, thr, &mtr);
				if (err != DB_SUCCESS) {

					goto close_cur;
				}
			}

	  	}
	}
close_cur:
	btr_pcur_close(&pcur);
	mtr_commit(&mtr);

	if (node->is_delete || err != DB_SUCCESS) {

		mem_heap_free(heap);	

        	return(err);
	}

	/* Build a new index entry */
	row_upd_index_replace_new_col_vals(entry, index, node->update);

	/* Insert new index entry */
	err = row_ins_index_entry(index, entry, NULL, 0, thr);

	mem_heap_free(heap);	

        return(err);
}
Пример #7
0
/***************************************************************
Removes a secondary index entry without modifying the index tree,
if possible.
@return	TRUE if success or if not found */
static
ibool
row_purge_remove_sec_if_poss_leaf(
    /*==============================*/
    purge_node_t*	node,	/*!< in: row purge node */
    dict_index_t*	index,	/*!< in: index */
    const dtuple_t*	entry)	/*!< in: index entry */
{
    mtr_t			mtr;
    btr_pcur_t		pcur;
    enum row_search_result	search_result;

    log_free_check();

    mtr_start(&mtr);

    /* Set the purge node for the call to row_purge_poss_sec(). */
    pcur.btr_cur.purge_node = node;
    /* Set the query thread, so that ibuf_insert_low() will be
    able to invoke thd_get_trx(). */
    pcur.btr_cur.thr = que_node_get_parent(node);

    search_result = row_search_index_entry(
                        index, entry, BTR_MODIFY_LEAF | BTR_DELETE, &pcur, &mtr);

    switch (search_result) {
        ibool	success;
    case ROW_FOUND:
        /* Before attempting to purge a record, check
        if it is safe to do so. */
        if (row_purge_poss_sec(node, index, entry)) {
            btr_cur_t* btr_cur = btr_pcur_get_btr_cur(&pcur);

            /* Only delete-marked records should be purged. */
            ut_ad(REC_INFO_DELETED_FLAG
                  & rec_get_info_bits(
                      btr_cur_get_rec(btr_cur),
                      dict_table_is_comp(index->table)));

            if (!btr_cur_optimistic_delete(btr_cur, &mtr)) {

                /* The index entry could not be deleted. */
                success = FALSE;
                goto func_exit;
            }
        }
    /* fall through (the index entry is still needed,
    or the deletion succeeded) */
    case ROW_NOT_DELETED_REF:
    /* The index entry is still needed. */
    case ROW_BUFFERED:
    /* The deletion was buffered. */
    case ROW_NOT_FOUND:
        /* The index entry does not exist, nothing to do. */
        success = TRUE;
func_exit:
        btr_pcur_close(&pcur);
        mtr_commit(&mtr);
        return(success);
    }

    ut_error;
    return(FALSE);
}
Пример #8
0
/***************************************************************
Removes a secondary index entry if possible, by modifying the
index tree.  Does not try to buffer the delete.
@return	TRUE if success or if not found */
static
ibool
row_purge_remove_sec_if_poss_tree(
    /*==============================*/
    purge_node_t*	node,	/*!< in: row purge node */
    dict_index_t*	index,	/*!< in: index */
    const dtuple_t*	entry)	/*!< in: index entry */
{
    btr_pcur_t		pcur;
    btr_cur_t*		btr_cur;
    ibool			success	= TRUE;
    ulint			err;
    mtr_t			mtr;
    enum row_search_result	search_result;

    log_free_check();
    mtr_start(&mtr);

    search_result = row_search_index_entry(index, entry, BTR_MODIFY_TREE,
                                           &pcur, &mtr);

    switch (search_result) {
    case ROW_NOT_FOUND:
        /* Not found.  This is a legitimate condition.  In a
        rollback, InnoDB will remove secondary recs that would
        be purged anyway.  Then the actual purge will not find
        the secondary index record.  Also, the purge itself is
        eager: if it comes to consider a secondary index
        record, and notices it does not need to exist in the
        index, it will remove it.  Then if/when the purge
        comes to consider the secondary index record a second
        time, it will not exist any more in the index. */

        /* fputs("PURGE:........sec entry not found\n", stderr); */
        /* dtuple_print(stderr, entry); */
        goto func_exit;
    case ROW_FOUND:
        break;
    case ROW_BUFFERED:
    case ROW_NOT_DELETED_REF:
        /* These are invalid outcomes, because the mode passed
        to row_search_index_entry() did not include any of the
        flags BTR_INSERT, BTR_DELETE, or BTR_DELETE_MARK. */
        ut_error;
    }

    btr_cur = btr_pcur_get_btr_cur(&pcur);

    /* We should remove the index record if no later version of the row,
    which cannot be purged yet, requires its existence. If some requires,
    we should do nothing. */

    if (row_purge_poss_sec(node, index, entry)) {
        /* Remove the index record, which should have been
        marked for deletion. */
        ut_ad(REC_INFO_DELETED_FLAG
              & rec_get_info_bits(btr_cur_get_rec(btr_cur),
                                  dict_table_is_comp(index->table)));

        btr_cur_pessimistic_delete(&err, FALSE, btr_cur,
                                   RB_NONE, &mtr);
        switch (UNIV_EXPECT(err, DB_SUCCESS)) {
        case DB_SUCCESS:
            break;
        case DB_OUT_OF_FILE_SPACE:
            success = FALSE;
            break;
        default:
            ut_error;
        }
    }

func_exit:
    btr_pcur_close(&pcur);
    mtr_commit(&mtr);

    return(success);
}
Пример #9
0
/***********************************************************//**
Removes a delete marked clustered index record if possible.
@return TRUE if success, or if not found, or if modified after the
delete marking */
static
ibool
row_purge_remove_clust_if_poss_low(
    /*===============================*/
    purge_node_t*	node,	/*!< in: row purge node */
    ulint		mode)	/*!< in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE */
{
    dict_index_t*	index;
    btr_pcur_t*	pcur;
    btr_cur_t*	btr_cur;
    ibool		success;
    ulint		err;
    mtr_t		mtr;
    rec_t*		rec;
    mem_heap_t*	heap		= NULL;
    ulint		offsets_[REC_OFFS_NORMAL_SIZE];
    rec_offs_init(offsets_);

    index = dict_table_get_first_index(node->table);

    pcur = &(node->pcur);
    btr_cur = btr_pcur_get_btr_cur(pcur);

    log_free_check();
    mtr_start(&mtr);

    success = row_purge_reposition_pcur(mode, node, &mtr);

    if (!success) {
        /* The record is already removed */

        btr_pcur_commit_specify_mtr(pcur, &mtr);

        return(TRUE);
    }

    rec = btr_pcur_get_rec(pcur);

    if (node->roll_ptr != row_get_rec_roll_ptr(
                rec, index, rec_get_offsets(rec, index, offsets_,
                                            ULINT_UNDEFINED, &heap))) {
        if (UNIV_LIKELY_NULL(heap)) {
            mem_heap_free(heap);
        }
        /* Someone else has modified the record later: do not remove */
        btr_pcur_commit_specify_mtr(pcur, &mtr);

        return(TRUE);
    }

    if (UNIV_LIKELY_NULL(heap)) {
        mem_heap_free(heap);
    }

    if (mode == BTR_MODIFY_LEAF) {
        success = btr_cur_optimistic_delete(btr_cur, &mtr);
    } else {
        ut_ad(mode == BTR_MODIFY_TREE);
        btr_cur_pessimistic_delete(&err, FALSE, btr_cur,
                                   RB_NONE, &mtr);

        if (err == DB_SUCCESS) {
            success = TRUE;
        } else if (err == DB_OUT_OF_FILE_SPACE) {
            success = FALSE;
        } else {
            ut_error;
        }
    }

    btr_pcur_commit_specify_mtr(pcur, &mtr);

    return(success);
}
Пример #10
0
/***************************************************************
Removes a secondary index entry if possible. */
static
ibool
row_purge_remove_sec_if_poss_low(
/*=============================*/
				/* out: TRUE if success or if not found */
	purge_node_t*	node,	/* in: row purge node */
	que_thr_t*	thr,	/* in: query thread */
	dict_index_t*	index,	/* in: index */
	dtuple_t*	entry,	/* in: index entry */
	ulint		mode)	/* in: latch mode BTR_MODIFY_LEAF or
				BTR_MODIFY_TREE */	
{
	btr_pcur_t	pcur;
	btr_cur_t*	btr_cur;
	ibool		success;
	ibool		old_has;
	ibool		found;
	ulint		err;
	mtr_t		mtr;
	mtr_t*		mtr_vers;
	
	UT_NOT_USED(thr);

	log_free_check();
	mtr_start(&mtr);
	
	found = row_search_index_entry(index, entry, mode, &pcur, &mtr);

	if (!found) {
		/* Not found */

		/* printf("PURGE:........sec entry not found\n"); */
		/* dtuple_print(entry); */

		btr_pcur_close(&pcur);
		mtr_commit(&mtr);

		return(TRUE);
	}

	btr_cur = btr_pcur_get_btr_cur(&pcur);
	
	/* We should remove the index record if no later version of the row,
	which cannot be purged yet, requires its existence. If some requires,
	we should do nothing. */

	mtr_vers = mem_alloc(sizeof(mtr_t));
	
	mtr_start(mtr_vers);

	success = row_purge_reposition_pcur(BTR_SEARCH_LEAF, node, mtr_vers);

	if (success) {		
		old_has = row_vers_old_has_index_entry(TRUE,
					btr_pcur_get_rec(&(node->pcur)),
					mtr_vers, index, entry);
	}

	btr_pcur_commit_specify_mtr(&(node->pcur), mtr_vers);

	mem_free(mtr_vers);
	
	if (!success || !old_has) {
		/* Remove the index record */

		if (mode == BTR_MODIFY_LEAF) {		
			success = btr_cur_optimistic_delete(btr_cur, &mtr);
		} else {
			ut_ad(mode == BTR_MODIFY_TREE);
			btr_cur_pessimistic_delete(&err, FALSE, btr_cur,
							FALSE, &mtr);
			if (err == DB_SUCCESS) {
				success = TRUE;
			} else if (err == DB_OUT_OF_FILE_SPACE) {
				success = FALSE;
			} else {
				ut_a(0);
			}
		}
	}

	btr_pcur_close(&pcur);
	mtr_commit(&mtr);

	return(success);
}
Пример #11
0
/***********************************************************//**
Delete unmarks a secondary index entry which must be found. It might not be
delete-marked at the moment, but it does not harm to unmark it anyway. We also
need to update the fields of the secondary index record if we updated its
fields but alphabetically they stayed the same, e.g., 'abc' -> 'aBc'.
@return	DB_FAIL or DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
static
ulint
row_undo_mod_del_unmark_sec_and_undo_update(
/*========================================*/
	ulint		mode,	/*!< in: search mode: BTR_MODIFY_LEAF or
				BTR_MODIFY_TREE */
	que_thr_t*	thr,	/*!< in: query thread */
	dict_index_t*	index,	/*!< in: index */
	const dtuple_t*	entry)	/*!< in: index entry */
{
	mem_heap_t*		heap;
	btr_pcur_t		pcur;
	btr_cur_t*		btr_cur;
	upd_t*			update;
	ulint			err		= DB_SUCCESS;
	big_rec_t*		dummy_big_rec;
	mtr_t			mtr;
	trx_t*			trx		= thr_get_trx(thr);
	enum row_search_result	search_result;

	/* Ignore indexes that are being created. */
	if (UNIV_UNLIKELY(*index->name == TEMP_INDEX_PREFIX)) {

		return(DB_SUCCESS);
	}

	log_free_check();
	mtr_start(&mtr);

	ut_ad(mode == BTR_MODIFY_TREE || mode == BTR_MODIFY_LEAF);

	search_result = row_search_index_entry(index, entry, mode,
					       &pcur, &mtr);

	switch (search_result) {
	case ROW_BUFFERED:
	case ROW_NOT_DELETED_REF:
		/* These are invalid outcomes, because the mode passed
		to row_search_index_entry() did not include any of the
		flags BTR_INSERT, BTR_DELETE, or BTR_DELETE_MARK. */
		ut_error;
	case ROW_NOT_FOUND:
		fputs("InnoDB: error in sec index entry del undo in\n"
		      "InnoDB: ", stderr);
		dict_index_name_print(stderr, trx, index);
		fputs("\n"
		      "InnoDB: tuple ", stderr);
		dtuple_print(stderr, entry);
		fputs("\n"
		      "InnoDB: record ", stderr);
		rec_print(stderr, btr_pcur_get_rec(&pcur), index);
		putc('\n', stderr);
		trx_print(stderr, trx, 0);
		fputs("\n"
		      "InnoDB: Submit a detailed bug report"
		      " to http://bugs.mysql.com\n", stderr);
		break;
	case ROW_FOUND:
		btr_cur = btr_pcur_get_btr_cur(&pcur);
		err = btr_cur_del_mark_set_sec_rec(BTR_NO_LOCKING_FLAG,
						   btr_cur, FALSE, thr, &mtr);
		ut_a(err == DB_SUCCESS);
		heap = mem_heap_create(100);

		update = row_upd_build_sec_rec_difference_binary(
			index, entry, btr_cur_get_rec(btr_cur), trx, heap);
		if (upd_get_n_fields(update) == 0) {

			/* Do nothing */

		} else if (mode == BTR_MODIFY_LEAF) {
			/* Try an optimistic updating of the record, keeping
			changes within the page */

			err = btr_cur_optimistic_update(
				BTR_KEEP_SYS_FLAG | BTR_NO_LOCKING_FLAG,
				btr_cur, update, 0, thr, &mtr);
			switch (err) {
			case DB_OVERFLOW:
			case DB_UNDERFLOW:
			case DB_ZIP_OVERFLOW:
				err = DB_FAIL;
			}
		} else {
			ut_a(mode == BTR_MODIFY_TREE);
			err = btr_cur_pessimistic_update(
				BTR_KEEP_SYS_FLAG | BTR_NO_LOCKING_FLAG,
				btr_cur, &heap, &dummy_big_rec,
				update, 0, thr, &mtr);
			ut_a(!dummy_big_rec);
		}

		mem_heap_free(heap);
	}

	btr_pcur_close(&pcur);
	mtr_commit(&mtr);

	return(err);
}
Пример #12
0
/***********************************************************//**
Delete marks or removes a secondary index entry if found.
@return	DB_SUCCESS, DB_FAIL, or DB_OUT_OF_FILE_SPACE */
static
ulint
row_undo_mod_del_mark_or_remove_sec_low(
/*====================================*/
	undo_node_t*	node,	/*!< in: row undo node */
	que_thr_t*	thr,	/*!< in: query thread */
	dict_index_t*	index,	/*!< in: index */
	dtuple_t*	entry,	/*!< in: index entry */
	ulint		mode)	/*!< in: latch mode BTR_MODIFY_LEAF or
				BTR_MODIFY_TREE */
{
	btr_pcur_t		pcur;
	btr_cur_t*		btr_cur;
	ibool			success;
	ibool			old_has;
	ulint			err;
	mtr_t			mtr;
	mtr_t			mtr_vers;
	enum row_search_result	search_result;

	log_free_check();
	mtr_start(&mtr);

	btr_cur = btr_pcur_get_btr_cur(&pcur);

	ut_ad(mode == BTR_MODIFY_TREE || mode == BTR_MODIFY_LEAF);

	search_result = row_search_index_entry(index, entry, mode,
					       &pcur, &mtr);

	switch (UNIV_EXPECT(search_result, ROW_FOUND)) {
	case ROW_NOT_FOUND:
		/* In crash recovery, the secondary index record may
		be missing if the UPDATE did not have time to insert
		the secondary index records before the crash.  When we
		are undoing that UPDATE in crash recovery, the record
		may be missing.

		In normal processing, if an update ends in a deadlock
		before it has inserted all updated secondary index
		records, then the undo will not find those records. */

		err = DB_SUCCESS;
		goto func_exit;
	case ROW_FOUND:
		break;
	case ROW_BUFFERED:
	case ROW_NOT_DELETED_REF:
		/* These are invalid outcomes, because the mode passed
		to row_search_index_entry() did not include any of the
		flags BTR_INSERT, BTR_DELETE, or BTR_DELETE_MARK. */
		ut_error;
	}

	/* We should remove the index record if no prior version of the row,
	which cannot be purged yet, requires its existence. If some requires,
	we should delete mark the record. */

	mtr_start(&mtr_vers);

	success = btr_pcur_restore_position(BTR_SEARCH_LEAF, &(node->pcur),
					    &mtr_vers);
	ut_a(success);

	old_has = row_vers_old_has_index_entry(FALSE,
					       btr_pcur_get_rec(&(node->pcur)),
					       &mtr_vers, index, entry);
	if (old_has) {
		err = btr_cur_del_mark_set_sec_rec(BTR_NO_LOCKING_FLAG,
						   btr_cur, TRUE, thr, &mtr);
		ut_ad(err == DB_SUCCESS);
	} else {
		/* Remove the index record */

		if (mode == BTR_MODIFY_LEAF) {
			success = btr_cur_optimistic_delete(btr_cur, &mtr);
			if (success) {
				err = DB_SUCCESS;
			} else {
				err = DB_FAIL;
			}
		} else {
			ut_ad(mode == BTR_MODIFY_TREE);

			/* No need to distinguish RB_RECOVERY_PURGE here,
			because we are deleting a secondary index record:
			the distinction between RB_NORMAL and
			RB_RECOVERY_PURGE only matters when deleting a
			record that contains externally stored
			columns. */
			ut_ad(!dict_index_is_clust(index));
			btr_cur_pessimistic_delete(&err, FALSE, btr_cur,
						   RB_NORMAL, &mtr);

			/* The delete operation may fail if we have little
			file space left: TODO: easiest to crash the database
			and restart with more file space */
		}
	}

	btr_pcur_commit_specify_mtr(&(node->pcur), &mtr_vers);

func_exit:
	btr_pcur_close(&pcur);
	mtr_commit(&mtr);

	return(err);
}
Пример #13
0
/***********************************************************//**
Undoes a modify in a clustered index record. Sets also the node state for the
next round of undo.
@return	DB_SUCCESS or error code: we may run out of file space */
static
ulint
row_undo_mod_clust(
/*===============*/
	undo_node_t*	node,	/*!< in: row undo node */
	que_thr_t*	thr)	/*!< in: query thread */
{
	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);

	/* Try optimistic processing of the record, keeping changes within
	the index page */

	err = row_undo_mod_clust_low(node, thr, &mtr, BTR_MODIFY_LEAF);

	if (err != DB_SUCCESS) {
		btr_pcur_commit_specify_mtr(pcur, &mtr);

		/* We may have to modify tree structure: do a pessimistic
		descent down the index tree */

		mtr_start(&mtr);

		err = row_undo_mod_clust_low(node, thr, &mtr, BTR_MODIFY_TREE);
	}

	btr_pcur_commit_specify_mtr(pcur, &mtr);

	if (err == DB_SUCCESS && node->rec_type == TRX_UNDO_UPD_DEL_REC) {

		mtr_start(&mtr);

		err = row_undo_mod_remove_clust_low(node, thr, &mtr,
						    BTR_MODIFY_LEAF);
		if (err != DB_SUCCESS) {
			btr_pcur_commit_specify_mtr(pcur, &mtr);

			/* We may have to modify tree structure: do a
			pessimistic descent down the index tree */

			mtr_start(&mtr);

			err = row_undo_mod_remove_clust_low(node, thr, &mtr,
							    BTR_MODIFY_TREE);
		}

		btr_pcur_commit_specify_mtr(pcur, &mtr);
	}

	node->state = UNDO_NODE_FETCH_NEXT;

	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);
}