Exemplo n.º 1
0
/***************************************************************
Updates a clustered index record of a row when the ordering fields do
not change. */
static
ulint
row_upd_clust_rec(
/*==============*/
				/* out: DB_SUCCESS if operation successfully
				completed, else error code or DB_LOCK_WAIT */
	upd_node_t*	node,	/* in: row update node */
	dict_index_t*	index,	/* in: clustered index */
	que_thr_t*	thr,	/* in: query thread */
	mtr_t*		mtr)	/* in: mtr; gets committed here */
{
	big_rec_t*	big_rec	= NULL;
	btr_pcur_t*	pcur;
	btr_cur_t*	btr_cur;
	ulint		err;
	
	ut_ad(node);
	ut_ad(index->type & DICT_CLUSTERED);

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

	ut_ad(FALSE == rec_get_deleted_flag(btr_pcur_get_rec(pcur)));
	
	/* Try optimistic updating of the record, keeping changes within
	the page; we do not check locks because we assume the x-lock on the
	record to update */

	if (node->cmpl_info & UPD_NODE_NO_SIZE_CHANGE) {
		err = btr_cur_update_in_place(BTR_NO_LOCKING_FLAG,
						btr_cur, node->update,
						node->cmpl_info, thr, mtr);
	} else {
		err = btr_cur_optimistic_update(BTR_NO_LOCKING_FLAG,
						btr_cur, node->update,
						node->cmpl_info, thr, mtr);
	}

	mtr_commit(mtr);
	
	if (err == DB_SUCCESS) {

		return(err);
	}

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

	mtr_start(mtr);
	
	/* NOTE: this transaction has an s-lock or x-lock on the record and
	therefore other transactions cannot modify the record when we have no
	latch on the page. In addition, we assume that other query threads of
	the same transaction do not modify the record in the meantime.
	Therefore we can assert that the restoration of the cursor succeeds. */

	ut_a(btr_pcur_restore_position(BTR_MODIFY_TREE, pcur, mtr));

	ut_ad(FALSE == rec_get_deleted_flag(btr_pcur_get_rec(pcur)));
	
	err = btr_cur_pessimistic_update(BTR_NO_LOCKING_FLAG, btr_cur,
					&big_rec, node->update,
					node->cmpl_info, thr, mtr);
	mtr_commit(mtr);

	if (err == DB_SUCCESS && big_rec) {
		mtr_start(mtr);
		ut_a(btr_pcur_restore_position(BTR_MODIFY_TREE, pcur, mtr));
	
		err = btr_store_big_rec_extern_fields(index,
						btr_cur_get_rec(btr_cur),
						big_rec, mtr);		
		mtr_commit(mtr);
	}

	if (big_rec) {
		dtuple_big_rec_free(big_rec);
	}
		
	return(err);
}
Exemplo n.º 2
0
/***************************************************************
Marks the clustered index record deleted and inserts the updated version
of the record to the index. This function should be used when the ordering
fields of the clustered index record change. This should be quite rare in
database applications. */
static
ulint
row_upd_clust_rec_by_insert(
/*========================*/
				/* out: DB_SUCCESS if operation successfully
				completed, else error code or DB_LOCK_WAIT */
	upd_node_t*	node,	/* in: row update node */
	dict_index_t*	index,	/* in: clustered index of the record */
	que_thr_t*	thr,	/* in: query thread */
	ibool		check_ref,/* in: TRUE if index may be referenced in
				a foreign key constraint */
	mtr_t*		mtr)	/* in: mtr; gets committed here */
{
	mem_heap_t*	heap;
	btr_pcur_t*	pcur;
	btr_cur_t*	btr_cur;
	trx_t*		trx;
	dict_table_t*	table;
	dtuple_t*	entry;
	ulint		err;
	
	ut_ad(node);
	ut_ad(index->type & DICT_CLUSTERED);

	trx = thr_get_trx(thr);
	table = node->table;
	pcur = node->pcur;
	btr_cur	= btr_pcur_get_btr_cur(pcur);
	
	if (node->state != UPD_NODE_INSERT_CLUSTERED) {

		err = btr_cur_del_mark_set_clust_rec(BTR_NO_LOCKING_FLAG,
						btr_cur, TRUE, thr, mtr);
		if (err != DB_SUCCESS) {
			mtr_commit(mtr);

			return(err);
		}

		/* Mark as not-owned the externally stored fields which the new
		row inherits from the delete marked record: purge should not
		free those externally stored fields even if the delete marked
		record is removed from the index tree, or updated. */

		btr_cur_mark_extern_inherited_fields(btr_cur_get_rec(btr_cur),
							node->update, mtr);
		if (check_ref) {
			/* NOTE that the following call loses
			the position of pcur ! */
			err = row_upd_check_references_constraints(
							pcur, table,
							index, thr, mtr);
			if (err != DB_SUCCESS) {
				mtr_commit(mtr);

				return(err);
			}
		}

	} 

	mtr_commit(mtr);

	node->state = UPD_NODE_INSERT_CLUSTERED;

	heap = mem_heap_create(500);
	
	entry = row_build_index_entry(node->row, index, heap);

	row_upd_clust_index_replace_new_col_vals(entry, node->update);
	
	row_upd_index_entry_sys_field(entry, index, DATA_TRX_ID, trx->id);
	
	/* If we return from a lock wait, for example, we may have
	extern fields marked as not-owned in entry (marked if the
	if-branch above). We must unmark them. */
	
	btr_cur_unmark_dtuple_extern_fields(entry, node->ext_vec,
							node->n_ext_vec);
	/* We must mark non-updated extern fields in entry as inherited,
	so that a possible rollback will not free them */
	
	btr_cur_mark_dtuple_inherited_extern(entry, node->ext_vec,
						node->n_ext_vec,
						node->update);
	
	err = row_ins_index_entry(index, entry, node->ext_vec,
						node->n_ext_vec, thr);
	mem_heap_free(heap);
	
	return(err);
}
Exemplo n.º 3
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);
}
Exemplo n.º 4
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);
}
Exemplo n.º 5
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);
}
Exemplo n.º 6
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);
}