Commit de21d09d authored by unknown's avatar unknown
Browse files

Merge heikki@bk-internal.mysql.com:/home/bk/mysql-5.0

into hundin.mysql.fi:/home/heikki/mysql-5.0

parents 8d6c0896 c2323bae
Loading
Loading
Loading
Loading
+14 −4
Original line number Diff line number Diff line
@@ -243,17 +243,27 @@ row_update_for_mysql(
					the MySQL format */
	row_prebuilt_t*	prebuilt);	/* in: prebuilt struct in MySQL
					handle */

/*************************************************************************
Does an unlock of a row for MySQL. */
This can only be used when srv_locks_unsafe_for_binlog is TRUE. Before
calling this function we must use trx_reset_new_rec_lock_info() and
trx_register_new_rec_lock() to store the information which new record locks
really were set. This function removes a newly set lock under prebuilt->pcur,
and also under prebuilt->clust_pcur. Currently, this is only used and tested
in the case of an UPDATE or a DELETE statement, where the row lock is of the
LOCK_X type.
Thus, this implements a 'mini-rollback' that releases the latest record
locks we set. */

int
row_unlock_for_mysql(
/*=================*/
					/* out: error code or DB_SUCCESS */
	row_prebuilt_t*	prebuilt);	/* in: prebuilt struct in MySQL
	row_prebuilt_t*	prebuilt,	/* in: prebuilt struct in MySQL
					handle */

	ibool		has_latches_on_recs);/* TRUE if called so that we have
					the latches on the records under pcur
					and clust_pcur, and we do not need to
					reposition the cursors. */
/*************************************************************************
Creates an query graph node of 'update' type to be used in the MySQL
interface. */
+41 −2
Original line number Diff line number Diff line
@@ -16,10 +16,39 @@ Created 3/26/1996 Heikki Tuuri
#include "que0types.h"
#include "mem0mem.h"
#include "read0types.h"
#include "dict0types.h"
#include "trx0xa.h"

extern ulint	trx_n_mysql_transactions;

/*****************************************************************
Resets the new record lock info in a transaction struct. */
UNIV_INLINE
void
trx_reset_new_rec_lock_info(
/*========================*/
	trx_t*	trx);	/* in: transaction struct */
/*****************************************************************
Registers that we have set a new record lock on an index. We only have space
to store 2 indexes! If this is called to store more than 2 indexes after
trx_reset_new_rec_lock_info(), then this function does nothing. */
UNIV_INLINE
void
trx_register_new_rec_lock(
/*======================*/
	trx_t*		trx,	/* in: transaction struct */
	dict_index_t*	index);	/* in: trx sets a new record lock on this
				index */
/*****************************************************************
Checks if trx has set a new record lock on an index. */
UNIV_INLINE
ibool
trx_new_rec_locks_contain(
/*======================*/
				/* out: TRUE if trx has set a new record lock
				on index */
	trx_t*		trx,	/* in: transaction struct */
	dict_index_t*	index);	/* in: index */
/************************************************************************
Releases the search latch if trx has reserved it. */

@@ -495,8 +524,18 @@ struct trx_struct{
	lock_t*		auto_inc_lock;	/* possible auto-inc lock reserved by
					the transaction; note that it is also
					in the lock list trx_locks */
	ibool		trx_create_lock;/* this is TRUE if we have created a
					new lock for a record accessed */
	dict_index_t*	new_rec_locks[2];/* these are normally NULL; if
					srv_locks_unsafe_for_binlog is TRUE,
					in a cursor search, if we set a new
					record lock on an index, this is set
					to point to the index; this is
					used in releasing the locks under the
					cursors if we are performing an UPDATE
					and we determine after retrieving
					the row that it does not need to be
					locked; thus, these can be used to
					implement a 'mini-rollback' that
					releases the latest record locks */
	UT_LIST_NODE_T(trx_t)
			trx_list;	/* list of transactions */
	UT_LIST_NODE_T(trx_t)
+56 −0
Original line number Diff line number Diff line
@@ -39,4 +39,60 @@ trx_start_if_not_started_low(
	}
}

/*****************************************************************
Resets the new record lock info in a transaction struct. */
UNIV_INLINE
void
trx_reset_new_rec_lock_info(
/*========================*/
	trx_t*	trx)	/* in: transaction struct */
{
	trx->new_rec_locks[0] = NULL;
	trx->new_rec_locks[1] = NULL;
}

/*****************************************************************
Registers that we have set a new record lock on an index. We only have space
to store 2 indexes! If this is called to store more than 2 indexes after
trx_reset_new_rec_lock_info(), then this function does nothing. */
UNIV_INLINE
void
trx_register_new_rec_lock(
/*======================*/
	trx_t*		trx,	/* in: transaction struct */
	dict_index_t*	index)	/* in: trx sets a new record lock on this
				index */
{
	if (trx->new_rec_locks[0] == NULL) {
		trx->new_rec_locks[0] = index;

		return;
	}

	if (trx->new_rec_locks[0] == index) {

		return;
	}

	if (trx->new_rec_locks[1] != NULL) {

		return;
	}

	trx->new_rec_locks[1] = index;
}

/*****************************************************************
Checks if trx has set a new record lock on an index. */
UNIV_INLINE
ibool
trx_new_rec_locks_contain(
/*======================*/
				/* out: TRUE if trx has set a new record lock
				on index */
	trx_t*		trx,	/* in: transaction struct */
	dict_index_t*	index)	/* in: index */
{
	return(trx->new_rec_locks[0] == index
	       || trx->new_rec_locks[1] == index);
}
+32 −28
Original line number Diff line number Diff line
@@ -1766,9 +1766,6 @@ lock_rec_create(

	HASH_INSERT(lock_t, hash, lock_sys->rec_hash,
					lock_rec_fold(space, page_no), lock);
	/* Note that we have create a new lock */
	trx->trx_create_lock = TRUE;

	if (type_mode & LOCK_WAIT) {

		lock_set_lock_and_trx_wait(lock, trx);
@@ -1945,15 +1942,6 @@ lock_rec_add_to_queue(

	if (similar_lock && !somebody_waits && !(type_mode & LOCK_WAIT)) {

		/* If the nth bit of a record lock is already set then we
		do not set a new lock bit, otherwice we set */

		if (lock_rec_get_nth_bit(similar_lock, heap_no)) {
			trx->trx_create_lock = FALSE;
		} else {
			trx->trx_create_lock = TRUE;
		}

		lock_rec_set_nth_bit(similar_lock, heap_no);

		return(similar_lock);
@@ -2005,11 +1993,14 @@ lock_rec_lock_fast(
	lock = lock_rec_get_first_on_page(rec);

	trx = thr_get_trx(thr);
	trx->trx_create_lock = FALSE;

	if (lock == NULL) {
		if (!impl) {
			lock_rec_create(mode, rec, index, trx);
			
			if (srv_locks_unsafe_for_binlog) {
				trx_register_new_rec_lock(trx, index);
			}
		}
		
		return(TRUE);
@@ -2023,21 +2014,20 @@ lock_rec_lock_fast(
	if (lock->trx != trx
	    || lock->type_mode != (mode | LOCK_REC)
	    || lock_rec_get_n_bits(lock) <= heap_no) {

	    	return(FALSE);
	}

	if (!impl) {
		/* If the nth bit of the record lock is already set then we
		do not set a new lock bit, otherwise we do set */

		/* If the nth bit of a record lock is already set then we
		do not set a new lock bit, otherwice we set */

		if (lock_rec_get_nth_bit(lock, heap_no)) {
			trx->trx_create_lock = FALSE;
		} else {
			trx->trx_create_lock = TRUE;
		}

		if (!lock_rec_get_nth_bit(lock, heap_no)) {
			lock_rec_set_nth_bit(lock, heap_no);
			if (srv_locks_unsafe_for_binlog) {
				trx_register_new_rec_lock(trx, index);
			}
		}
	}

	return(TRUE);
@@ -2093,12 +2083,19 @@ lock_rec_lock_slow(
		enough already granted on the record, we have to wait. */
    				
		err = lock_rec_enqueue_waiting(mode, rec, index, thr);

		if (srv_locks_unsafe_for_binlog) {
			trx_register_new_rec_lock(trx, index);
		}
	} else {
		if (!impl) {
			/* Set the requested lock on the record */

			lock_rec_add_to_queue(LOCK_REC | mode, rec, index,
									trx);
			if (srv_locks_unsafe_for_binlog) {
				trx_register_new_rec_lock(trx, index);
			}
		}

		err = DB_SUCCESS;
@@ -2436,8 +2433,15 @@ lock_rec_inherit_to_gap(
	
	lock = lock_rec_get_first(rec);

	/* If srv_locks_unsafe_for_binlog is TRUE, we do not want locks set
	by an UPDATE or a DELETE to be inherited as gap type locks. But we
	DO want S-locks set by a consistency constraint to be inherited also
	then. */

	while (lock != NULL) {
		if (!lock_rec_get_insert_intention(lock)) {
		if (!lock_rec_get_insert_intention(lock)
		    && !(srv_locks_unsafe_for_binlog
			 && lock_get_mode(lock) == LOCK_X)) {
			
			lock_rec_add_to_queue(LOCK_REC | lock_get_mode(lock)
						| LOCK_GAP,
+79 −24
Original line number Diff line number Diff line
@@ -1429,51 +1429,106 @@ row_update_for_mysql(
}

/*************************************************************************
Does an unlock of a row for MySQL. */
This can only be used when srv_locks_unsafe_for_binlog is TRUE. Before
calling this function we must use trx_reset_new_rec_lock_info() and
trx_register_new_rec_lock() to store the information which new record locks
really were set. This function removes a newly set lock under prebuilt->pcur,
and also under prebuilt->clust_pcur. Currently, this is only used and tested
in the case of an UPDATE or a DELETE statement, where the row lock is of the
LOCK_X type.
Thus, this implements a 'mini-rollback' that releases the latest record
locks we set. */

int
row_unlock_for_mysql(
/*=================*/
					/* out: error code or DB_SUCCESS */
	row_prebuilt_t*	prebuilt)	/* in: prebuilt struct in MySQL
	row_prebuilt_t*	prebuilt,	/* in: prebuilt struct in MySQL
					handle */
	ibool		has_latches_on_recs)/* TRUE if called so that we have
					the latches on the records under pcur
					and clust_pcur, and we do not need to
					reposition the cursors. */
{
	rec_t*		rec;
	btr_pcur_t*	cur		= prebuilt->pcur;
	dict_index_t*	index;
	btr_pcur_t*	pcur		= prebuilt->pcur;
	btr_pcur_t*	clust_pcur	= prebuilt->clust_pcur;
	trx_t*		trx		= prebuilt->trx;
	rec_t*		rec;
	mtr_t           mtr;
	
	ut_ad(prebuilt && trx);
	ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
			
	if (!srv_locks_unsafe_for_binlog) {

		fprintf(stderr,
"InnoDB: Error: calling row_unlock_for_mysql though\n"
"InnoDB: srv_locks_unsafe_for_binlog is FALSE.\n");

		return(DB_SUCCESS);
	}

	trx->op_info = "unlock_row";

	if (srv_locks_unsafe_for_binlog) {
		if (trx->trx_create_lock == TRUE) {
	index = btr_pcur_get_btr_cur(pcur)->index;

	if (index != NULL && trx_new_rec_locks_contain(trx, index)) {

		mtr_start(&mtr);
			
			/* Restore a cursor position and find a record */
			btr_pcur_restore_position(BTR_SEARCH_LEAF, cur, &mtr);
			rec = btr_pcur_get_rec(cur);
		/* Restore the cursor position and find the record */
		
		if (!has_latches_on_recs) {
			btr_pcur_restore_position(BTR_SEARCH_LEAF, pcur, &mtr);
		}

		rec = btr_pcur_get_rec(pcur);

			if (rec) {
		mutex_enter(&kernel_mutex);

		lock_rec_reset_and_release_wait(rec);
			} else {
				fputs("InnoDB: Error: "
				      "Record for the lock not found\n",
				      stderr);
				mem_analyze_corruption((byte*) trx);
				ut_error;
			}

			trx->trx_create_lock = FALSE;
		mutex_exit(&kernel_mutex);

		mtr_commit(&mtr);

		/* If the search was done through the clustered index, then
		we have not used clust_pcur at all, and we must NOT try to
		reset locks on clust_pcur. The values in clust_pcur may be
		garbage! */

		if (index->type & DICT_CLUSTERED) {
			
			goto func_exit;
		}
	}

	index = btr_pcur_get_btr_cur(clust_pcur)->index;

	if (index != NULL && trx_new_rec_locks_contain(trx, index)) {

		mtr_start(&mtr);
			
		/* Restore the cursor position and find the record */

		if (!has_latches_on_recs) {
			btr_pcur_restore_position(BTR_SEARCH_LEAF, clust_pcur,
									&mtr);
		}

		rec = btr_pcur_get_rec(clust_pcur);

		mutex_enter(&kernel_mutex);

		lock_rec_reset_and_release_wait(rec);

		mutex_exit(&kernel_mutex);

		mtr_commit(&mtr);
	}
			
func_exit:
	trx->op_info = "";
	
	return(DB_SUCCESS);
Loading