summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorunknown <marko@hundin.mysql.fi>2004-05-03 10:43:05 +0300
committerunknown <marko@hundin.mysql.fi>2004-05-03 10:43:05 +0300
commitc98791c7dad7184c9d9af0a68e6be2f26987fc06 (patch)
tree4fbc7683d5d8affb5e873e9147389d82d9153be3
parentd3f1288044e609cdc1275b9357eed348bef48819 (diff)
parent3594adb165279605c0cf6519f5c3a5b5378f3d91 (diff)
downloadmariadb-git-c98791c7dad7184c9d9af0a68e6be2f26987fc06.tar.gz
Merge marko@build.mysql.com:/home/bk/mysql-4.0
into hundin.mysql.fi:/home/marko/l/mysql-4.0
-rw-r--r--innobase/include/lock0lock.h21
-rw-r--r--innobase/include/row0mysql.h16
-rw-r--r--innobase/include/trx0trx.h2
-rw-r--r--innobase/lock/lock0lock.c130
-rw-r--r--innobase/row/row0mysql.c88
-rw-r--r--innobase/trx/trx0trx.c2
-rw-r--r--mysql-test/r/innodb-lock.result22
-rw-r--r--mysql-test/r/innodb.result2
-rw-r--r--mysql-test/t/innodb-lock.test40
-rw-r--r--sql/ha_innodb.cc20
10 files changed, 323 insertions, 20 deletions
diff --git a/innobase/include/lock0lock.h b/innobase/include/lock0lock.h
index 9ccea5ad7a4..b1f9a10ee05 100644
--- a/innobase/include/lock0lock.h
+++ b/innobase/include/lock0lock.h
@@ -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 */
diff --git a/innobase/include/row0mysql.h b/innobase/include/row0mysql.h
index f28e1b6f048..a74c5bf4c60 100644
--- a/innobase/include/row0mysql.h
+++ b/innobase/include/row0mysql.h
@@ -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
diff --git a/innobase/include/trx0trx.h b/innobase/include/trx0trx.h
index bb2d7e8f64d..71269cb1e4e 100644
--- a/innobase/include/trx0trx.h
+++ b/innobase/include/trx0trx.h
@@ -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)
diff --git a/innobase/lock/lock0lock.c b/innobase/lock/lock0lock.c
index e141e30ae97..6534387c22c 100644
--- a/innobase/lock/lock0lock.c
+++ b/innobase/lock/lock0lock.c
@@ -2001,7 +2001,11 @@ 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
if (lock_print_waits) {
@@ -2939,7 +2943,7 @@ retry:
}
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);
@@ -3496,6 +3510,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 @@ loop:
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);
diff --git a/innobase/row/row0mysql.c b/innobase/row/row0mysql.c
index 07c3f8c4867..f53a8de2080 100644
--- a/innobase/row/row0mysql.c
+++ b/innobase/row/row0mysql.c
@@ -693,10 +693,94 @@ run_again:
/* 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(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(0, prebuilt->table, LOCK_AUTO_INC, thr);
+ err = lock_table(LOCK_TABLE_EXP, prebuilt->table,
+ prebuilt->select_lock_type, thr);
trx->error_state = err;
diff --git a/innobase/trx/trx0trx.c b/innobase/trx/trx0trx.c
index 7a028c92060..69cd6c7b22d 100644
--- a/innobase/trx/trx0trx.c
+++ b/innobase/trx/trx0trx.c
@@ -151,6 +151,7 @@ trx_create(
trx->n_tickets_to_enter_innodb = 0;
trx->auto_inc_lock = NULL;
+ trx->n_tables_locked = 0;
trx->read_view_heap = mem_heap_create(256);
trx->read_view = NULL;
@@ -278,6 +279,7 @@ trx_free(
ut_a(!trx->has_search_latch);
ut_a(!trx->auto_inc_lock);
+ ut_a(!trx->n_tables_locked);
ut_a(trx->dict_operation_lock_mode == 0);
diff --git a/mysql-test/r/innodb-lock.result b/mysql-test/r/innodb-lock.result
new file mode 100644
index 00000000000..cf00adb30ae
--- /dev/null
+++ b/mysql-test/r/innodb-lock.result
@@ -0,0 +1,22 @@
+drop table if exists t1;
+create table t1 (id integer, x integer) engine=INNODB;
+insert into t1 values(0, 0);
+set autocommit=0;
+SELECT * from t1 where id = 0 FOR UPDATE;
+id x
+0 0
+set autocommit=0;
+lock table t1 write;
+update t1 set x=1 where id = 0;
+select * from t1;
+id x
+0 1
+commit;
+update t1 set x=2 where id = 0;
+commit;
+unlock tables;
+select * from t1;
+id x
+0 2
+commit;
+drop table t1;
diff --git a/mysql-test/r/innodb.result b/mysql-test/r/innodb.result
index 6a67bbc6f8b..1a92946bfcd 100644
--- a/mysql-test/r/innodb.result
+++ b/mysql-test/r/innodb.result
@@ -431,7 +431,7 @@ Duplicate entry 'test2' for key 2
select * from t1;
id ggid email passwd
1 this will work
-3 test2 this will work
+4 test2 this will work
select * from t1 where id=1;
id ggid email passwd
1 this will work
diff --git a/mysql-test/t/innodb-lock.test b/mysql-test/t/innodb-lock.test
new file mode 100644
index 00000000000..43a175508b4
--- /dev/null
+++ b/mysql-test/t/innodb-lock.test
@@ -0,0 +1,40 @@
+-- source include/have_innodb.inc
+
+connect (con1,localhost,root,,);
+connect (con2,localhost,root,,);
+drop table if exists t1;
+
+#
+# Testing of explicit table locks
+#
+
+connection con1;
+create table t1 (id integer, x integer) engine=INNODB;
+insert into t1 values(0, 0);
+set autocommit=0;
+SELECT * from t1 where id = 0 FOR UPDATE;
+
+connection con2;
+set autocommit=0;
+
+# The following statement should hang because con1 is locking the page
+--send
+lock table t1 write;
+--sleep 2;
+
+connection con1;
+update t1 set x=1 where id = 0;
+select * from t1;
+commit;
+
+connection con2;
+reap;
+update t1 set x=2 where id = 0;
+commit;
+unlock tables;
+
+connection con1;
+select * from t1;
+commit;
+
+drop table t1;
diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc
index 7a651fc12d9..de2a817a6cb 100644
--- a/sql/ha_innodb.cc
+++ b/sql/ha_innodb.cc
@@ -4494,12 +4494,11 @@ the SQL statement in case of an error. */
int
ha_innobase::external_lock(
/*=======================*/
- /* out: 0 or error code */
+ /* out: 0 */
THD* thd, /* in: handle to the user thread */
int lock_type) /* in: lock type */
{
row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
- int error = 0;
trx_t* trx;
DBUG_ENTER("ha_innobase::external_lock");
@@ -4554,11 +4553,21 @@ ha_innobase::external_lock(
}
if (prebuilt->select_lock_type != LOCK_NONE) {
+ if (thd->in_lock_tables) {
+ ulint error;
+ error = row_lock_table_for_mysql(prebuilt);
+
+ if (error != DB_SUCCESS) {
+ error = convert_error_code_to_mysql(
+ error, user_thd);
+ DBUG_RETURN(error);
+ }
+ }
trx->mysql_n_tables_locked++;
}
- DBUG_RETURN(error);
+ DBUG_RETURN(0);
}
/* MySQL is releasing a table lock */
@@ -4566,6 +4575,9 @@ ha_innobase::external_lock(
trx->n_mysql_tables_in_use--;
prebuilt->mysql_has_locked = FALSE;
auto_inc_counter_for_this_stat = 0;
+ if (trx->n_tables_locked) {
+ row_unlock_table_for_mysql(trx);
+ }
/* If the MySQL lock count drops to zero we know that the current SQL
statement has ended */
@@ -4598,7 +4610,7 @@ ha_innobase::external_lock(
}
}
- DBUG_RETURN(error);
+ DBUG_RETURN(0);
}
/****************************************************************************