Commit 3594adb1 authored by unknown's avatar unknown
Browse files

InnoDB: implement LOCK TABLE (Bug #3299)


innobase/include/lock0lock.h:
  Add lock_table_unlock() and lock_release_tables_off_kernel()
  Add LOCK_TABLE_EXP
innobase/include/row0mysql.h:
  Add row_unlock_table_for_mysql() and row_lock_table_for_mysql()
innobase/include/trx0trx.h:
  Add n_tables_locked
innobase/lock/lock0lock.c:
  Add LOCK_TABLE_EXP for explicit LOCK TABLE commands
  Add lock_table_unlock()
  Add lock_release_tables_off_kernel()
innobase/row/row0mysql.c:
  Add row_unlock_table_for_mysql() and row_lock_table_for_mysql()
innobase/trx/trx0trx.c:
  Add n_tables_locked
mysql-test/r/innodb.result:
  Updated handling of auto_inc columns
sql/ha_innodb.cc:
  Call row_lock_table_for_mysql() and row_unlock_table_for_mysql()
parent b7468a97
Loading
Loading
Loading
Loading
+20 −1
Original line number Diff line number Diff line
@@ -381,7 +381,9 @@ lock_table(
				/* out: DB_SUCCESS, DB_LOCK_WAIT,
				DB_DEADLOCK, or DB_QUE_THR_SUSPENDED */
	ulint		flags,	/* in: if BTR_NO_LOCKING_FLAG bit is set,
				does nothing */
				does nothing;
				if LOCK_TABLE_EXP bits are set,
				creates an explicit table lock */
	dict_table_t*	table,	/* in: database table in dictionary cache */
	ulint		mode,	/* in: lock mode */
	que_thr_t*	thr);	/* in: query thread */
@@ -394,6 +396,14 @@ lock_is_on_table(
				/* out: TRUE if there are lock(s) */
	dict_table_t*	table);	/* in: database table in dictionary cache */
/*************************************************************************
Releases a table lock.
Releases possible other transactions waiting for this lock. */

void
lock_table_unlock(
/*==============*/
	lock_t*	lock);	/* in: lock */
/*************************************************************************
Releases an auto-inc lock a transaction possibly has on a table.
Releases possible other transactions waiting for this lock. */

@@ -410,6 +420,14 @@ lock_release_off_kernel(
/*====================*/
	trx_t*	trx);	/* in: transaction */
/*************************************************************************
Releases table locks, and releases possible other transactions waiting
because of these locks. */

void
lock_release_tables_off_kernel(
/*===========================*/
	trx_t*	trx);	/* in: transaction */
/*************************************************************************
Cancels a waiting lock request and releases possible other transactions
waiting behind it. */

@@ -536,6 +554,7 @@ extern lock_sys_t* lock_sys;
/* Lock types */
#define LOCK_TABLE	16	/* these type values should be so high that */
#define	LOCK_REC	32	/* they can be ORed to the lock mode */
#define LOCK_TABLE_EXP	80	/* explicit table lock */
#define LOCK_TYPE_MASK	0xF0UL	/* mask used to extract lock type from the
				type_mode field in a lock */
/* Waiting lock flag */
+16 −0
Original line number Diff line number Diff line
@@ -153,6 +153,22 @@ row_lock_table_autoinc_for_mysql(
	row_prebuilt_t*	prebuilt);	/* in: prebuilt struct in the MySQL
					table handle */
/*************************************************************************
Unlocks a table lock possibly reserved by trx. */

void		  	
row_unlock_table_for_mysql(
/*=======================*/
	trx_t*	trx);	/* in: transaction */
/*************************************************************************
Sets a table lock on the table mentioned in prebuilt. */

int
row_lock_table_for_mysql(
/*=====================*/
					/* out: error code or DB_SUCCESS */
	row_prebuilt_t*	prebuilt);	/* in: prebuilt struct in the MySQL
					table handle */
/*************************************************************************
Does an insert for MySQL. */

int
+2 −0
Original line number Diff line number Diff line
@@ -421,6 +421,8 @@ 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 */
	ulint		n_tables_locked;/* number of table locks reserved by
					the transaction, stored in trx_locks */
	UT_LIST_NODE_T(trx_t)
			trx_list;	/* list of transactions */
	UT_LIST_NODE_T(trx_t)
+118 −12
Original line number Diff line number Diff line
@@ -2001,6 +2001,10 @@ lock_grant(
                release it at the end of the SQL statement */

                lock->trx->auto_inc_lock = lock;
        } else if (lock_get_type(lock) == LOCK_TABLE_EXP) {
		ut_ad(lock_get_mode(lock) == LOCK_S
			|| lock_get_mode(lock) == LOCK_X);
		lock->trx->n_tables_locked++;
	}

#ifdef UNIV_DEBUG
@@ -2939,7 +2943,7 @@ lock_deadlock_occurs(
	}

	if (ret == LOCK_VICTIM_IS_START) {
		if (lock_get_type(lock) == LOCK_TABLE) {
		if (lock_get_type(lock) & LOCK_TABLE) {
			table = lock->un_member.tab_lock.table;
			index = NULL;
		} else {
@@ -3015,7 +3019,7 @@ lock_deadlock_recursive(
	/* Look at the locks ahead of wait_lock in the lock queue */

	for (;;) {
		if (lock_get_type(lock) == LOCK_TABLE) {
		if (lock_get_type(lock) & LOCK_TABLE) {

			lock = UT_LIST_GET_PREV(un_member.tab_lock.locks, lock);
		} else {
@@ -3347,7 +3351,9 @@ lock_table(
				/* out: DB_SUCCESS, DB_LOCK_WAIT,
				DB_DEADLOCK, or DB_QUE_THR_SUSPENDED */
	ulint		flags,	/* in: if BTR_NO_LOCKING_FLAG bit is set,
				does nothing */
				does nothing;
				if LOCK_TABLE_EXP bits are set,
				creates an explicit table lock */
	dict_table_t*	table,	/* in: database table in dictionary cache */
	ulint		mode,	/* in: lock mode */
	que_thr_t*	thr)	/* in: query thread */
@@ -3362,6 +3368,8 @@ lock_table(
		return(DB_SUCCESS);
	}

	ut_ad(flags == 0 || flags == LOCK_TABLE_EXP);

	trx = thr_get_trx(thr);

	lock_mutex_enter_kernel();
@@ -3390,7 +3398,12 @@ lock_table(
		return(err);
	}

	lock_table_create(table, mode, trx);
	lock_table_create(table, mode | flags, trx);

	if (flags) {
		ut_ad(mode == LOCK_S || mode == LOCK_X);
		trx->n_tables_locked++;
	}

	lock_mutex_exit_kernel();

@@ -3471,7 +3484,8 @@ lock_table_dequeue(
#ifdef UNIV_SYNC_DEBUG
	ut_ad(mutex_own(&kernel_mutex));
#endif /* UNIV_SYNC_DEBUG */
	ut_ad(lock_get_type(in_lock) == LOCK_TABLE);
	ut_ad(lock_get_type(in_lock) == LOCK_TABLE ||
		lock_get_type(in_lock) == LOCK_TABLE_EXP);

	lock = UT_LIST_GET_NEXT(un_member.tab_lock.locks, in_lock);

@@ -3495,6 +3509,22 @@ lock_table_dequeue(

/*=========================== LOCK RELEASE ==============================*/

/*************************************************************************
Releases a table lock.
Releases possible other transactions waiting for this lock. */

void
lock_table_unlock(
/*==============*/
	lock_t*	lock)	/* in: lock */
{
	mutex_enter(&kernel_mutex);

	lock_table_dequeue(lock);

	mutex_exit(&kernel_mutex);
}

/*************************************************************************
Releases an auto-inc lock a transaction possibly has on a table.
Releases possible other transactions waiting for this lock. */
@@ -3542,7 +3572,7 @@ lock_release_off_kernel(
			
			lock_rec_dequeue_from_page(lock);
		} else {
			ut_ad(lock_get_type(lock) == LOCK_TABLE);
			ut_ad(lock_get_type(lock) & LOCK_TABLE);

			if (lock_get_mode(lock) != LOCK_IS
			    && (trx->insert_undo || trx->update_undo)) {
@@ -3558,6 +3588,11 @@ lock_release_off_kernel(
			}

			lock_table_dequeue(lock);
			if (lock_get_type(lock) == LOCK_TABLE_EXP) {
				ut_ad(lock_get_mode(lock) == LOCK_S
					|| lock_get_mode(lock) == LOCK_X);
				trx->n_tables_locked--;
			}
		}

		if (count == LOCK_RELEASE_KERNEL_INTERVAL) {
@@ -3577,6 +3612,73 @@ lock_release_off_kernel(
	mem_heap_empty(trx->lock_heap);

	ut_a(trx->auto_inc_lock == NULL);
	ut_a(trx->n_tables_locked == 0);
}

/*************************************************************************
Releases table locks, and releases possible other transactions waiting
because of these locks. */

void
lock_release_tables_off_kernel(
/*===========================*/
	trx_t*	trx)	/* in: transaction */
{
	dict_table_t*	table;
	ulint		count;
	lock_t*		lock;

#ifdef UNIV_SYNC_DEBUG
	ut_ad(mutex_own(&kernel_mutex));
#endif /* UNIV_SYNC_DEBUG */

	lock = UT_LIST_GET_LAST(trx->trx_locks);

	count = 0;

	while (lock != NULL) {

		count++;

		if (lock_get_type(lock) == LOCK_TABLE_EXP) {
			ut_ad(lock_get_mode(lock) == LOCK_S
				|| lock_get_mode(lock) == LOCK_X);
			if (trx->insert_undo || trx->update_undo) {

				/* The trx may have modified the table.
				We block the use of the MySQL query
				cache for all currently active
				transactions. */

				table = lock->un_member.tab_lock.table;

				table->query_cache_inv_trx_id =
							trx_sys->max_trx_id;
			}

			lock_table_dequeue(lock);
			trx->n_tables_locked--;
			lock = UT_LIST_GET_LAST(trx->trx_locks);
			continue;
		}

		if (count == LOCK_RELEASE_KERNEL_INTERVAL) {
			/* Release the kernel mutex for a while, so that we
			do not monopolize it */

			lock_mutex_exit_kernel();

			lock_mutex_enter_kernel();

			count = 0;
		}

		lock = UT_LIST_GET_PREV(trx_locks, lock);
	}

	mem_heap_empty(trx->lock_heap);

	ut_a(trx->n_tables_locked == 0);
}

/*************************************************************************
@@ -3596,7 +3698,7 @@ lock_cancel_waiting_and_release(
			
		lock_rec_dequeue_from_page(lock);
	} else {
		ut_ad(lock_get_type(lock) == LOCK_TABLE);
		ut_ad(lock_get_type(lock) & LOCK_TABLE);

		lock_table_dequeue(lock);
	}
@@ -3637,7 +3739,7 @@ lock_reset_all_on_table_for_trx(
			ut_a(!lock_get_wait(lock));
			
			lock_rec_discard(lock);
		} else if (lock_get_type(lock) == LOCK_TABLE
		} else if (lock_get_type(lock) & LOCK_TABLE
				&& lock->un_member.tab_lock.table == table) {

			ut_a(!lock_get_wait(lock));
@@ -3689,8 +3791,12 @@ lock_table_print(
#ifdef UNIV_SYNC_DEBUG
	ut_ad(mutex_own(&kernel_mutex));
#endif /* UNIV_SYNC_DEBUG */
	ut_a(lock_get_type(lock) == LOCK_TABLE);
	ut_a(lock_get_type(lock) == LOCK_TABLE ||
		lock_get_type(lock) == LOCK_TABLE_EXP);

	if (lock_get_type(lock) == LOCK_TABLE_EXP) {
		fputs("EXPLICIT ", file);
	}
	fputs("TABLE LOCK table ", file);
	ut_print_name(file, lock->un_member.tab_lock.table->name);
	fprintf(file, " trx id %lu %lu",
@@ -4009,7 +4115,7 @@ lock_print_info(
		
		lock_rec_print(file, lock);
	} else {
		ut_ad(lock_get_type(lock) == LOCK_TABLE);
		ut_ad(lock_get_type(lock) & LOCK_TABLE);
	
		lock_table_print(file, lock);
	}
@@ -4290,7 +4396,7 @@ lock_validate(void)
		lock = UT_LIST_GET_FIRST(trx->trx_locks);
		
		while (lock) {
			if (lock_get_type(lock) == LOCK_TABLE) {
			if (lock_get_type(lock) & LOCK_TABLE) {
	
				lock_table_queue_validate(
					lock->un_member.tab_lock.table);
+86 −2
Original line number Diff line number Diff line
@@ -696,7 +696,91 @@ row_lock_table_autoinc_for_mysql(

	trx_start_if_not_started(trx);

	err = lock_table(0, prebuilt->table, LOCK_AUTO_INC, thr);
	err = lock_table(0, prebuilt->table, prebuilt->select_lock_type, thr);

	trx->error_state = err;

	if (err != DB_SUCCESS) {
		que_thr_stop_for_mysql(thr);

		was_lock_wait = row_mysql_handle_errors(&err, trx, thr, NULL);

		if (was_lock_wait) {
			goto run_again;
		}

		trx->op_info = (char *) "";

		return(err);
	}

	que_thr_stop_for_mysql_no_error(thr, trx);

	trx->op_info = (char *) "";

	return((int) err);
}

/*************************************************************************
Unlocks a table lock possibly reserved by trx. */

void		  	
row_unlock_table_for_mysql(
/*=======================*/
	trx_t*	trx)	/* in: transaction */
{
	if (!trx->n_tables_locked) {

		return;
	}

	mutex_enter(&kernel_mutex);
	lock_release_tables_off_kernel(trx);
	mutex_exit(&kernel_mutex);
}
/*************************************************************************
Sets a table lock on the table mentioned in prebuilt. */

int
row_lock_table_for_mysql(
/*=====================*/
					/* out: error code or DB_SUCCESS */
	row_prebuilt_t*	prebuilt)	/* in: prebuilt struct in the MySQL
					table handle */
{
	trx_t*		trx 		= prebuilt->trx;
	que_thr_t*	thr;
	ulint		err;
	ibool		was_lock_wait;
	
	ut_ad(trx);
	ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());

	trx->op_info = (char *) "setting table lock";

	if (prebuilt->sel_graph == NULL) {
		/* Build a dummy select query graph */
		row_prebuild_sel_graph(prebuilt);
	}

	/* We use the select query graph as the dummy graph needed
	in the lock module call */

	thr = que_fork_get_first_thr(prebuilt->sel_graph);

	que_thr_move_to_run_state_for_mysql(thr, trx);

run_again:
	thr->run_node = thr;
	thr->prev_node = thr->common.parent;

	/* It may be that the current session has not yet started
	its transaction, or it has been committed: */

	trx_start_if_not_started(trx);

	err = lock_table(LOCK_TABLE_EXP, prebuilt->table,
		prebuilt->select_lock_type, thr);

	trx->error_state = err;

Loading