diff options
author | unknown <marko@hundin.mysql.fi> | 2004-05-03 10:43:05 +0300 |
---|---|---|
committer | unknown <marko@hundin.mysql.fi> | 2004-05-03 10:43:05 +0300 |
commit | c98791c7dad7184c9d9af0a68e6be2f26987fc06 (patch) | |
tree | 4fbc7683d5d8affb5e873e9147389d82d9153be3 | |
parent | d3f1288044e609cdc1275b9357eed348bef48819 (diff) | |
parent | 3594adb165279605c0cf6519f5c3a5b5378f3d91 (diff) | |
download | mariadb-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.h | 21 | ||||
-rw-r--r-- | innobase/include/row0mysql.h | 16 | ||||
-rw-r--r-- | innobase/include/trx0trx.h | 2 | ||||
-rw-r--r-- | innobase/lock/lock0lock.c | 130 | ||||
-rw-r--r-- | innobase/row/row0mysql.c | 88 | ||||
-rw-r--r-- | innobase/trx/trx0trx.c | 2 | ||||
-rw-r--r-- | mysql-test/r/innodb-lock.result | 22 | ||||
-rw-r--r-- | mysql-test/r/innodb.result | 2 | ||||
-rw-r--r-- | mysql-test/t/innodb-lock.test | 40 | ||||
-rw-r--r-- | sql/ha_innodb.cc | 20 |
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); } /**************************************************************************** |