diff options
author | unknown <heikki@hundin.mysql.fi> | 2002-09-20 05:11:08 +0300 |
---|---|---|
committer | unknown <heikki@hundin.mysql.fi> | 2002-09-20 05:11:08 +0300 |
commit | b8cfcf768ef493218b3917caf179c90c00632452 (patch) | |
tree | 83fa0b9921ca4d827b9a72cf561764aaaa2e4296 | |
parent | ebf518bc45b19cc0118422773c99d16b84420518 (diff) | |
download | mariadb-git-b8cfcf768ef493218b3917caf179c90c00632452.tar.gz |
Many files:
Modifications for query cache + trxs, fix of q.c.+ foreign keys
os0file.c:
Use unbuffered i/o in Windows
innobase/dict/dict0mem.c:
Modifications for query cache + trxs, fix of q.c.+ foreign keys
innobase/os/os0file.c:
Use unbuffered i/o in Windows
innobase/lock/lock0lock.c:
Modifications for query cache + trxs, fix of q.c.+ foreign keys
innobase/row/row0ins.c:
Modifications for query cache + trxs, fix of q.c.+ foreign keys
innobase/row/row0mysql.c:
Modifications for query cache + trxs, fix of q.c.+ foreign keys
innobase/row/row0sel.c:
Modifications for query cache + trxs, fix of q.c.+ foreign keys
innobase/srv/srv0srv.c:
Modifications for query cache + trxs, fix of q.c.+ foreign keys
sql/ha_innodb.h:
Modifications for query cache + trxs, fix of q.c.+ foreign keys
sql/ha_innodb.cc:
Modifications for query cache + trxs, fix of q.c.+ foreign keys
-rw-r--r-- | innobase/dict/dict0mem.c | 2 | ||||
-rw-r--r-- | innobase/lock/lock0lock.c | 20 | ||||
-rw-r--r-- | innobase/os/os0file.c | 33 | ||||
-rw-r--r-- | innobase/row/row0ins.c | 37 | ||||
-rw-r--r-- | innobase/row/row0mysql.c | 15 | ||||
-rw-r--r-- | innobase/row/row0sel.c | 54 | ||||
-rw-r--r-- | innobase/srv/srv0srv.c | 2 | ||||
-rw-r--r-- | sql/ha_innodb.cc | 186 | ||||
-rw-r--r-- | sql/ha_innodb.h | 2 |
9 files changed, 309 insertions, 42 deletions
diff --git a/innobase/dict/dict0mem.c b/innobase/dict/dict0mem.c index 9a4c94de885..e5918c6aeb6 100644 --- a/innobase/dict/dict0mem.c +++ b/innobase/dict/dict0mem.c @@ -74,6 +74,8 @@ dict_mem_table_create( table->auto_inc_lock = mem_heap_alloc(heap, lock_get_size()); + table->query_cache_inv_trx_id = ut_dulint_zero; + UT_LIST_INIT(table->locks); UT_LIST_INIT(table->foreign_list); UT_LIST_INIT(table->referenced_list); diff --git a/innobase/lock/lock0lock.c b/innobase/lock/lock0lock.c index 8c48180cf63..ebd063b6ca5 100644 --- a/innobase/lock/lock0lock.c +++ b/innobase/lock/lock0lock.c @@ -14,6 +14,8 @@ Created 5/7/1996 Heikki Tuuri #include "usr0sess.h" #include "trx0purge.h" +#include "dict0mem.h" +#include "trx0sys.h" /* Restricts the length of search we will do in the waits-for graph of transactions */ @@ -3416,8 +3418,9 @@ lock_release_off_kernel( /*====================*/ trx_t* trx) /* in: transaction */ { - ulint count; - lock_t* lock; + dict_table_t* table; + ulint count; + lock_t* lock; ut_ad(mutex_own(&kernel_mutex)); @@ -3435,6 +3438,19 @@ lock_release_off_kernel( } else { ut_ad(lock_get_type(lock) == LOCK_TABLE); + if (lock_get_mode(lock) != LOCK_IS + && (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); } diff --git a/innobase/os/os0file.c b/innobase/os/os0file.c index 1da66fdc512..b2881581fc7 100644 --- a/innobase/os/os0file.c +++ b/innobase/os/os0file.c @@ -669,13 +669,14 @@ os_file_get_size( return(FALSE); } -#if SIZEOF_OFF_T > 4 - *size = (ulint)(offs & 0xFFFFFFFF); - *size_high = (ulint)(offs >> 32); -#else - *size = (ulint) offs; - *size_high = 0; -#endif + if (sizeof(off_t) > 4) { + *size = (ulint)(offs & 0xFFFFFFFF); + *size_high = (ulint)(offs >> 32); + } else { + *size = (ulint) offs; + *size_high = 0; + } + return(TRUE); #endif } @@ -838,16 +839,18 @@ os_file_pread( /* If off_t is > 4 bytes in size, then we assume we can pass a 64-bit address */ -#if SIZEOF_OFF_T > 4 - offs = (off_t)offset + (((off_t)offset_high) << 32); -#else - offs = (off_t)offset; + if (sizeof(off_t) > 4) { + offs = (off_t)offset + (((off_t)offset_high) << 32); + + } else { + offs = (off_t)offset; - if (offset_high > 0) { - fprintf(stderr, - "InnoDB: Error: file read at offset > 4 GB\n"); + if (offset_high > 0) { + fprintf(stderr, + "InnoDB: Error: file read at offset > 4 GB\n"); + } } -#endif + os_n_file_reads++; #ifdef HAVE_PREAD diff --git a/innobase/row/row0ins.c b/innobase/row/row0ins.c index 2e6dde6db65..7c20afbe467 100644 --- a/innobase/row/row0ins.c +++ b/innobase/row/row0ins.c @@ -32,6 +32,23 @@ Created 4/20/1996 Heikki Tuuri #define ROW_INS_PREV 1 #define ROW_INS_NEXT 2 + +/********************************************************************* +This prototype is copied from /mysql/sql/ha_innodb.cc. +Invalidates the MySQL query cache for the table. +NOTE that the exact prototype of this function has to be in +/innobase/row/row0ins.c! */ + +void +innobase_invalidate_query_cache( +/*============================*/ + trx_t* trx, /* in: transaction which modifies the table */ + char* full_name, /* in: concatenation of database name, null + char '\0', table name; NOTE that in + Windows this is always in LOWER CASE! */ + ulint full_name_len); /* in: full name length */ + + /************************************************************************* Creates an insert node struct. */ @@ -386,10 +403,30 @@ row_ins_foreign_delete_or_set_null( upd_t* update; ulint err; ulint i; + char* ptr; + char table_name_buf[1000]; char err_buf[1000]; ut_a(thr && foreign && pcur && mtr); + /* Since we are going to delete or update a row, we have to invalidate + the MySQL query cache for table */ + + ut_a(ut_strlen(table->name) < 998); + + ut_memcpy(table_name_buf, table->name, ut_strlen(table->name) + 1); + + ptr = table_name_buf; + + while (*ptr != '/') { + ptr++; + } + + *ptr = '\0'; + + /* We call a function in ha_innodb.cc */ + innobase_invalidate_query_cache(thr_get_trx(thr), table_name_buf, + ut_strlen(table->name)); node = thr->run_node; ut_a(que_node_get_type(node) == QUE_NODE_UPDATE); diff --git a/innobase/row/row0mysql.c b/innobase/row/row0mysql.c index dc028b1c67b..43eef8c5092 100644 --- a/innobase/row/row0mysql.c +++ b/innobase/row/row0mysql.c @@ -1186,12 +1186,7 @@ row_create_table_for_mysql( ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); ut_ad(mutex_own(&(dict_sys->mutex))); - /* We allow a create table also if innodb_force_recovery is used. This - enables the user to stop a runaway rollback or a crash caused by - a temporary table #sql... He can use the trick explained in the - manual to rename the temporary table to rsql..., and then drop it. */ - - if (srv_created_new_raw) { + if (srv_created_new_raw || srv_force_recovery) { fprintf(stderr, "InnoDB: A new raw disk partition was initialized or\n" "InnoDB: innodb_force_recovery is on: we do not allow\n" @@ -1712,13 +1707,7 @@ row_drop_table_for_mysql( ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); ut_a(name != NULL); - /* Note that we allow dropping of a table even if innodb_force_recovery - is used. If a rollback or purge would crash because of a corrupt - table, the user can try dropping it to avoid the crash. This is also - a nice way to stop a runaway rollback caused by a failing big - table import in a single transaction. */ - - if (srv_created_new_raw) { + if (srv_created_new_raw || srv_force_recovery) { fprintf(stderr, "InnoDB: A new raw disk partition was initialized or\n" "InnoDB: innodb_force_recovery is on: we do not allow\n" diff --git a/innobase/row/row0sel.c b/innobase/row/row0sel.c index f3dced15fdf..4af04251996 100644 --- a/innobase/row/row0sel.c +++ b/innobase/row/row0sel.c @@ -30,6 +30,7 @@ Created 12/19/1997 Heikki Tuuri #include "pars0sym.h" #include "pars0pars.h" #include "row0mysql.h" +#include "read0read.h" /* Maximum number of rows to prefetch; MySQL interface has another parameter */ #define SEL_MAX_N_PREFETCH 16 @@ -3115,3 +3116,56 @@ normal_return: return(ret); } + +/*********************************************************************** +Checks if MySQL at the moment is allowed for this table to retrieve a +consistent read result, or store it to the query cache. */ + +ibool +row_search_check_if_query_cache_permitted( +/*======================================*/ + /* out: TRUE if storing or retrieving from + the query cache is permitted */ + trx_t* trx, /* in: transaction object */ + char* norm_name) /* in: concatenation of database name, '/' + char, table name */ +{ + dict_table_t* table; + ibool ret = FALSE; + + table = dict_table_get(norm_name, trx); + + if (table == NULL) { + + return(FALSE); + } + + mutex_enter(&kernel_mutex); + + /* Start the transaction if it is not started yet */ + + trx_start_if_not_started_low(trx); + + /* If there are locks on the table or some trx has invalidated the + cache up to our trx id, then ret = FALSE. + We do not check what type locks there are on the table, though only + IX type locks actually would require ret = FALSE. */ + + if (UT_LIST_GET_LEN(table->locks) == 0 + && ut_dulint_cmp(trx->id, table->query_cache_inv_trx_id) >= 0) { + + ret = TRUE; + + /* Assign a read view for the transaction if it does not yet + have one */ + + if (!trx->read_view) { + trx->read_view = read_view_open_now(trx, + trx->read_view_heap); + } + } + + mutex_exit(&kernel_mutex); + + return(ret); +} diff --git a/innobase/srv/srv0srv.c b/innobase/srv/srv0srv.c index d754f603efc..95bcd2351cd 100644 --- a/innobase/srv/srv0srv.c +++ b/innobase/srv/srv0srv.c @@ -96,7 +96,7 @@ ulint srv_n_log_files = ULINT_MAX; ulint srv_log_file_size = ULINT_MAX; /* size in database pages */ ibool srv_log_archive_on = TRUE; ulint srv_log_buffer_size = ULINT_MAX; /* size in database pages */ -ulint srv_flush_log_at_trx_commit = 1; +ibool srv_flush_log_at_trx_commit = TRUE; byte srv_latin1_ordering[256] /* The sort order table of the latin1 character set. The following table is diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index e1ffb06d5fc..6599033704d 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -14,13 +14,12 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -/* - This file defines the InnoDB handler: the interface between MySQL and InnoDB */ +/* This file defines the InnoDB handler: the interface between MySQL and +InnoDB */ /* TODO list for the InnoDB handler: - Ask Monty if strings of different languages can exist in the same database. Answer: in 4.1 yes. - */ #ifdef __GNUC__ @@ -29,6 +28,8 @@ #include "mysql_priv.h" #include "slave.h" +#include "sql_cache.h" + #ifdef HAVE_INNOBASE_DB #include <m_ctype.h> #include <assert.h> @@ -101,18 +102,11 @@ char* innobase_unix_file_flush_method = NULL; /* Below we have boolean-valued start-up parameters, and their default values */ +uint innobase_flush_log_at_trx_commit = 0; my_bool innobase_log_archive = FALSE; my_bool innobase_use_native_aio = FALSE; my_bool innobase_fast_shutdown = TRUE; -/* innodb_flush_log_at_trx_commit can now have 3 values: -0 : write to the log file once per second and flush it to disk; -1 : write to the log file at each commit and flush it to disk; -2 : write to the log file at each commit, but flush to disk only once per -second */ - -uint innobase_flush_log_at_trx_commit = 0; - /* Set default InnoDB data file size to 10 MB and let it be auto-extending. Thus users can use InnoDB without having to @@ -412,6 +406,176 @@ ha_innobase::update_thd( return(0); } + +/* BACKGROUND INFO: HOW THE MYSQL QUERY CACHE WORKS WITH INNODB + ------------------------------------------------------------ + +1) The use of the query cache for TBL is disabled when there is an +uncommitted change to TBL. + +2) When a change to TBL commits, InnoDB stores the current value of +its global trx id counter, let us denote it by INV_TRX_ID, to the table object +in the InnoDB data dictionary, and does only allow such transactions whose +id >= INV_TRX_ID to use the query cache. + +3) When InnoDB does an INSERT/DELETE/UPDATE to a table TBL, or an implicit +modification because an ON DELETE CASCADE, we invalidate the MySQL query cache +of TBL immediately. + +How this is implemented inside InnoDB: + +1) Since every modification always sets an IX type table lock on the InnoDB +table, it is easy to check if there can be uncommitted modifications for a +table: just check if there are locks in the lock list of the table. + +2) When a transaction inside InnoDB commits, it reads the global trx id +counter and stores the value INV_TRX_ID to the tables on which it had a lock. + +3) If there is an implicit table change from ON DELETE CASCADE or SET NULL, +InnoDB calls an invalidate method for the MySQL query cache for that table. + +How this is implemented inside sql_cache.cc: + +1) The query cache for an InnoDB table TBL is invalidated immediately at an +INSERT/UPDATE/DELETE, just like in the case of MyISAM. No need to delay +invalidation to the transaction commit. + +2) To store or retrieve a value from the query cache of an InnoDB table TBL, +any query must first ask InnoDB's permission. We must pass the thd as a +parameter because InnoDB will look at the trx id, if any, associated with +that thd. + +3) Use of the query cache for InnoDB tables is now allowed also when +AUTOCOMMIT==0 or we are inside BEGIN ... COMMIT. Thus transactions no longer +put restrictions on the use of the query cache. +*/ + +/********************************************************************** +The MySQL query cache uses this to check from InnoDB if the query cache at +the moment is allowed to operate on an InnoDB table. The SQL query must +be a non-locking SELECT. + +The query cache is allowed to operate on certain query only if this function +returns TRUE for all tables in the query. + +If thd is not in the autocommit state, this function also starts a new +transaction for thd if there is no active trx yet, and assigns a consistent +read view to it if there is no read view yet. */ + +my_bool +innobase_query_caching_of_table_permitted( +/*======================================*/ + /* out: TRUE if permitted, FALSE if not; + note that the value FALSE does not mean + we should invalidate the query cache: + invalidation is called explicitly */ + THD* thd, /* in: thd of the user who is trying to + store a result to the query cache or + retrieve it */ + char* full_name, /* in: concatenation of database name, + the null character '\0', and the table + name */ + uint full_name_len) /* in: length of the full name, i.e. + len(dbname) + len(tablename) + 1 */ +{ + ibool is_autocommit; + trx_t* trx; + char* ptr; + char norm_name[1000]; + + ut_a(full_name_len < 999); + + if (thd->variables.tx_isolation == ISO_SERIALIZABLE) { + /* In the SERIALIZABLE mode we add LOCK IN SHARE MODE to every + plain SELECT */ + + return((my_bool)FALSE); + } + + trx = (trx_t*) thd->transaction.all.innobase_tid; + + if (trx == NULL) { + trx = check_trx_exists(thd); + } + + innobase_release_stat_resources(trx); + + if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) { + + is_autocommit = TRUE; + } else { + is_autocommit = FALSE; + + } + + if (is_autocommit && trx->conc_state == TRX_NOT_STARTED) { + /* We are going to retrieve the query result from the + query cache. This cannot be a store operation because then + we would have started the trx already. + + We can imagine we instantaneously serialize + this consistent read trx to the current trx id counter. + If trx2 would have changed the tables of a query + result stored in the cache, and trx2 would have already + committed, making the result obsolete, then trx2 would have + already invalidated the cache. Thus we can trust the result + in the cache is ok for this query. */ + + return((my_bool)TRUE); + } + + /* Normalize the table name to InnoDB format */ + + memcpy(norm_name, full_name, full_name_len); + + norm_name[strlen(norm_name)] = '/'; /* InnoDB uses '/' as the + separator between db and table */ + norm_name[full_name_len] = '\0'; +#ifdef __WIN__ + /* Put to lower case */ + + ptr = norm_name; + + while (*ptr != '\0') { + *ptr = tolower(*ptr); + ptr++; + } +#endif + if (row_search_check_if_query_cache_permitted(trx, norm_name)) { + + printf("Query cache for %s permitted\n", norm_name); + + return((my_bool)TRUE); + } + + printf("Query cache for %s NOT permitted\n", norm_name); + + return((my_bool)FALSE); +} + +extern "C" { +/********************************************************************* +Invalidates the MySQL query cache for the table. +NOTE that the exact prototype of this function has to be in +/innobase/row/row0ins.c! */ + +void +innobase_invalidate_query_cache( +/*============================*/ + trx_t* trx, /* in: transaction which modifies the table */ + char* full_name, /* in: concatenation of database name, null + char '\0', table name; NOTE that in + Windows this is always in LOWER CASE! */ + ulint full_name_len) /* in: full name length */ +{ + /* Argument TRUE below means we are using transactions */ + query_cache.invalidate((THD*)(trx->mysql_thd), + (const char*)full_name, + (uint32)full_name_len, + TRUE); +} +} + /********************************************************************* Call this when you have opened a new table handle in HANDLER, before you call index_read_idx() etc. Actually, we can let the cursor stay open even diff --git a/sql/ha_innodb.h b/sql/ha_innodb.h index 8994359c2a7..7686e308cc2 100644 --- a/sql/ha_innodb.h +++ b/sql/ha_innodb.h @@ -204,3 +204,5 @@ int innobase_close_connection(THD *thd); int innobase_drop_database(char *path); int innodb_show_status(THD* thd); +my_bool innobase_query_caching_of_table_permitted(THD* thd, char* full_name, + uint full_name_len); |