From 4324519868ec94df1804fd1a0288b45f2ae90ad8 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 19 Oct 2004 11:54:33 +0300 Subject: [PATCH 1/5] fixed retsult code --- sql/sql_parse.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 9e0853a370b..1fe9e5093eb 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1765,7 +1765,7 @@ mysql_execute_command(void) if (lex->name && (!lex->name[0] || strlen(lex->name) > NAME_LEN)) { net_printf(&thd->net,ER_WRONG_TABLE_NAME,lex->name); - res=0; + res= 1; break; } if (!select_lex->db) From e6e1600ec93ae95dbb17a9e462ca1abd7e715396 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 21 Oct 2004 23:56:12 +0300 Subject: [PATCH 2/5] Check of temporary tables hiding for query fetched from QC (BUG#6084) mysql-test/r/query_cache.result: hiding real table stored in query cache by temporary table mysql-test/t/query_cache.test: hiding real table stored in query cache by temporary table sql/sql_cache.cc: Check of temporary tables hiding for query fetched from QC sql/sql_cache.h: Key length now stored in table record of query cache --- mysql-test/r/query_cache.result | 13 +++++++++++++ mysql-test/t/query_cache.test | 12 ++++++++++++ sql/sql_cache.cc | 32 +++++++++++++++++++++++++++++++- sql/sql_cache.h | 3 +++ 4 files changed, 59 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/query_cache.result b/mysql-test/r/query_cache.result index a9e9f167e5f..85fe77b1f10 100644 --- a/mysql-test/r/query_cache.result +++ b/mysql-test/r/query_cache.result @@ -704,4 +704,17 @@ Qcache_queries_in_cache 1 unlock table; drop table t1,t2; set query_cache_wlock_invalidate=default; +CREATE TABLE t1 (id INT PRIMARY KEY); +insert into t1 values (1),(2),(3); +select * from t1; +id +1 +2 +3 +create temporary table t1 (a int not null auto_increment +primary key); +select * from t1; +a +drop table t1; +drop table t1; set GLOBAL query_cache_size=0; diff --git a/mysql-test/t/query_cache.test b/mysql-test/t/query_cache.test index 8a07c0a53a0..61fbadde1e1 100644 --- a/mysql-test/t/query_cache.test +++ b/mysql-test/t/query_cache.test @@ -521,4 +521,16 @@ unlock table; drop table t1,t2; set query_cache_wlock_invalidate=default; +# +# hiding real table stored in query cache by temporary table +# +CREATE TABLE t1 (id INT PRIMARY KEY); +insert into t1 values (1),(2),(3); +select * from t1; +create temporary table t1 (a int not null auto_increment +primary key); +select * from t1; +drop table t1; +drop table t1; + set GLOBAL query_cache_size=0; diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index 60f0cfadc8e..8e953e223a9 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -971,9 +971,38 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) for (; block_table != block_table_end; block_table++) { TABLE_LIST table_list; - bzero((char*) &table_list,sizeof(table_list)); + TABLE *tmptable; Query_cache_table *table = block_table->parent; + + /* + Check that we have not temporary tables with same names of tables + of this query. If we have such tables, we will not send data from + query cache, because temporary tables hide real tables by which + query in query cache was made. + */ + for (tmptable= thd->temporary_tables; tmptable ; tmptable= tmptable->next) + { + if (tmptable->key_length - 8 == table->key_len() && + !memcmp(tmptable->table_cache_key, table->data(), + table->key_len())) + { + DBUG_PRINT("qcache", + ("Temporary table detected: '%s.%s'", + table_list.db, table_list.alias)); + STRUCT_UNLOCK(&structure_guard_mutex); + /* + We should not store result of this query because it contain + temporary tables => assign following wariable to make check + faster. + */ + thd->safe_to_cache_query=0; + BLOCK_UNLOCK_RD(query_block); + DBUG_RETURN(-1); + } + } + + bzero((char*) &table_list,sizeof(table_list)); table_list.db = table->db(); table_list.alias= table_list.real_name= table->table(); if (check_table_access(thd,SELECT_ACL,&table_list,1)) @@ -2066,6 +2095,7 @@ Query_cache::insert_table(uint key_len, char *key, } char *db = header->db(); header->table(db + db_length + 1); + header->key_len(key_len); } Query_cache_block_table *list_root = table_block->table(0); diff --git a/sql/sql_cache.h b/sql/sql_cache.h index 0c6579250ab..454f0318c12 100644 --- a/sql/sql_cache.h +++ b/sql/sql_cache.h @@ -144,10 +144,13 @@ struct Query_cache_query struct Query_cache_table { char *tbl; + uint32 key_length; inline char *db() { return (char *) data(); } inline char *table() { return tbl; } inline void table(char *table) { tbl = table; } + inline uint32 key_len() { return key_length; } + inline void key_len(uint32 len) { key_length= len; } inline gptr data() { return (gptr)(((byte*)this)+ From 5ab6c5e532f682631761f02bec6f894df3930002 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 22 Oct 2004 22:51:04 +0300 Subject: [PATCH 3/5] postreview fixes sql/mysql_priv.h: constant definition sql/sql_base.cc: difine used instead of constant sql/sql_cache.cc: difine used instead of constant typo fixed --- sql/mysql_priv.h | 1 + sql/sql_base.cc | 5 +++-- sql/sql_cache.cc | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index b5e571e74d0..b123927d09e 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -551,6 +551,7 @@ int mysql_ha_flush(THD *thd, TABLE_LIST *tables, uint mode_flags); #define MYSQL_HA_FLUSH_ALL 0x02 /* sql_base.cc */ +#define TMP_TABLE_KEY_EXTRA 8 void set_item_name(Item *item,char *pos,uint length); bool add_field_to_list(char *field_name, enum enum_field_types type, char *length, char *decimal, diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 72400bf0abb..ddc81053357 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -816,8 +816,9 @@ TABLE *open_table(THD *thd,const char *db,const char *table_name, for (table=thd->temporary_tables; table ; table=table->next) { - if (table->key_length == key_length+8 && - !memcmp(table->table_cache_key,key,key_length+8)) + if (table->key_length == key_length + TMP_TABLE_KEY_EXTRA && + !memcmp(table->table_cache_key, key, + key_length + TMP_TABLE_KEY_EXTRA)) { if (table->query_id == thd->query_id) { diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index 8e953e223a9..f503a63e752 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -983,7 +983,7 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) */ for (tmptable= thd->temporary_tables; tmptable ; tmptable= tmptable->next) { - if (tmptable->key_length - 8 == table->key_len() && + if (tmptable->key_length - TMP_TABLE_KEY_EXTRA == table->key_len() && !memcmp(tmptable->table_cache_key, table->data(), table->key_len())) { @@ -993,7 +993,7 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) STRUCT_UNLOCK(&structure_guard_mutex); /* We should not store result of this query because it contain - temporary tables => assign following wariable to make check + temporary tables => assign following variable to make check faster. */ thd->safe_to_cache_query=0; From 743597ea96cbf227c2c5dd34b9837802d62ad834 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 27 Oct 2004 13:33:11 +0300 Subject: [PATCH 4/5] Backport innodb_max_purge_lag from 4.1 innobase/include/srv0srv.h: Add configuration parameter srv_max_purge_lag. Add global variable srv_dml_needed_delay. innobase/include/trx0sys.h: Add trx_sys->rseg_history_len innobase/row/row0mysql.c: Add row_mysql_delay_if_needed() for delaying INSERTs, UPDATEs and DELETEs for srv_dml_needed_delay microseconds. innobase/srv/srv0srv.c: Define global variable srv_dml_needed_delay. Define configuration parameter srv_max_purge_lag. innobase/trx/trx0purge.c: Update trx_sys->rseg_history_len. trx_purge(): Compute srv_dml_needed_delay from srv_max_purge_lag and trx_sys->rseg_history_len. innobase/trx/trx0rseg.c: Initialize trx_sys->rseg_history_len at InnoDB start-up. sql/ha_innodb.h: Add configuration parameter srv_max_purge_lag. sql/mysqld.cc: Add startup option innodb_max_purge_lag, with default value 0 (meaning infinite, disabling the feature). sql/set_var.cc: Add global variable innodb_max_purge_lag. --- innobase/include/srv0srv.h | 2 ++ innobase/include/trx0sys.h | 4 ++++ innobase/row/row0mysql.c | 17 +++++++++++++++++ innobase/srv/srv0srv.c | 6 ++++++ innobase/trx/trx0purge.c | 38 ++++++++++++++++++++++++++++++++++++++ innobase/trx/trx0rseg.c | 7 ++++++- sql/ha_innodb.h | 1 + sql/mysqld.cc | 6 ++++++ sql/set_var.cc | 4 ++++ 9 files changed, 84 insertions(+), 1 deletion(-) diff --git a/innobase/include/srv0srv.h b/innobase/include/srv0srv.h index 57ca1f84f26..c76a1917615 100644 --- a/innobase/include/srv0srv.h +++ b/innobase/include/srv0srv.h @@ -99,6 +99,7 @@ extern ibool srv_use_doublewrite_buf; extern ibool srv_set_thread_priorities; extern int srv_query_thread_priority; +extern ulint srv_max_purge_lag; /*-------------------------------------------*/ extern ulint srv_n_rows_inserted; @@ -152,6 +153,7 @@ extern ulint srv_test_array_size; extern ulint srv_activity_count; extern ulint srv_fatal_semaphore_wait_threshold; +extern ulint srv_dml_needed_delay; extern mutex_t* kernel_mutex_temp;/* mutex protecting the server, trx structs, query threads, and lock table: we allocate diff --git a/innobase/include/trx0sys.h b/innobase/include/trx0sys.h index c7ef4d1929d..4d83c5eeaae 100644 --- a/innobase/include/trx0sys.h +++ b/innobase/include/trx0sys.h @@ -419,6 +419,10 @@ struct trx_sys_struct{ trx_rseg_t* rseg_array[TRX_SYS_N_RSEGS]; /* Pointer array to rollback segments; NULL if slot not in use */ + ulint rseg_history_len;/* Length of the TRX_RSEG_HISTORY + list (update undo logs for committed + transactions), protected by + rseg->mutex */ UT_LIST_BASE_NODE_T(read_view_t) view_list; /* List of read views sorted on trx no, biggest first */ diff --git a/innobase/row/row0mysql.c b/innobase/row/row0mysql.c index 3eb4f0de60e..78b2aa8e28f 100644 --- a/innobase/row/row0mysql.c +++ b/innobase/row/row0mysql.c @@ -89,6 +89,19 @@ row_mysql_is_system_table( || 0 == strcmp(name + 6, "user") || 0 == strcmp(name + 6, "db")); } + +/*********************************************************************** +Delays an INSERT, DELETE or UPDATE operation if the purge is lagging. */ +static +void +row_mysql_delay_if_needed(void) +/*===========================*/ +{ + if (srv_dml_needed_delay) { + os_thread_sleep(srv_dml_needed_delay); + } +} + /*********************************************************************** Reads a MySQL format variable-length field (like VARCHAR) length and returns pointer to the field data. */ @@ -856,6 +869,8 @@ row_insert_for_mysql( trx->op_info = (char *) "inserting"; + row_mysql_delay_if_needed(); + trx_start_if_not_started(trx); if (node == NULL) { @@ -1071,6 +1086,8 @@ row_update_for_mysql( trx->op_info = (char *) "updating or deleting"; + row_mysql_delay_if_needed(); + trx_start_if_not_started(trx); node = prebuilt->upd_node; diff --git a/innobase/srv/srv0srv.c b/innobase/srv/srv0srv.c index 0643ab96c1d..0a814268a36 100644 --- a/innobase/srv/srv0srv.c +++ b/innobase/srv/srv0srv.c @@ -58,6 +58,10 @@ ulint srv_activity_count = 0; /* The following is the maximum allowed duration of a lock wait. */ ulint srv_fatal_semaphore_wait_threshold = 600; +/* How much data manipulation language (DML) statements need to be delayed, +in microseconds, in order to reduce the lagging of the purge thread. */ +ulint srv_dml_needed_delay = 0; + ibool srv_lock_timeout_and_monitor_active = FALSE; ibool srv_error_monitor_active = FALSE; @@ -841,6 +845,8 @@ srv_general_init(void) /*======================= InnoDB Server FIFO queue =======================*/ +/* Maximum allowable purge history length. <=0 means 'infinite'. */ +ulint srv_max_purge_lag = 0; /************************************************************************* Puts an OS thread to wait if there are too many concurrent threads diff --git a/innobase/trx/trx0purge.c b/innobase/trx/trx0purge.c index a8b6b9fcc21..d772af47b29 100644 --- a/innobase/trx/trx0purge.c +++ b/innobase/trx/trx0purge.c @@ -295,6 +295,9 @@ trx_purge_add_update_undo_to_history( /* Add the log as the first in the history list */ flst_add_first(rseg_header + TRX_RSEG_HISTORY, undo_header + TRX_UNDO_HISTORY_NODE, mtr); + mutex_enter(&kernel_mutex); + trx_sys->rseg_history_len++; + mutex_exit(&kernel_mutex); /* Write the trx number to the undo log header */ mlog_write_dulint(undo_header + TRX_UNDO_TRX_NO, trx->no, mtr); @@ -386,6 +389,12 @@ loop: flst_cut_end(rseg_hdr + TRX_RSEG_HISTORY, log_hdr + TRX_UNDO_HISTORY_NODE, n_removed_logs, &mtr); + + mutex_enter(&kernel_mutex); + ut_ad(trx_sys->rseg_history_len >= n_removed_logs); + trx_sys->rseg_history_len -= n_removed_logs; + mutex_exit(&kernel_mutex); + freed = FALSE; while (!freed) { @@ -470,6 +479,11 @@ loop: } if (cmp >= 0) { + mutex_enter(&kernel_mutex); + ut_a(trx_sys->rseg_history_len >= n_removed_logs); + trx_sys->rseg_history_len -= n_removed_logs; + mutex_exit(&kernel_mutex); + flst_truncate_end(rseg_hdr + TRX_RSEG_HISTORY, log_hdr + TRX_UNDO_HISTORY_NODE, n_removed_logs, &mtr); @@ -1031,6 +1045,30 @@ trx_purge(void) purge_sys->view = NULL; mem_heap_empty(purge_sys->heap); + /* Determine how much data manipulation language (DML) statements + need to be delayed in order to reduce the lagging of the purge + thread. */ + srv_dml_needed_delay = 0; /* in microseconds; default: no delay */ + + /* If we cannot advance the 'purge view' because of an old + 'consistent read view', then the DML statements cannot be delayed. + Also, srv_max_purge_lag <= 0 means 'infinity'. */ + if (srv_max_purge_lag > 0 + && !UT_LIST_GET_LAST(trx_sys->view_list)) { + float ratio = (float) trx_sys->rseg_history_len + / srv_max_purge_lag; + if (ratio > ULINT_MAX / 10000) { + /* Avoid overflow: maximum delay is 4295 seconds */ + srv_dml_needed_delay = ULINT_MAX; + } else if (ratio > 1) { + /* If the history list length exceeds the + innodb_max_purge_lag, the + data manipulation statements are delayed + by at least 5000 microseconds. */ + srv_dml_needed_delay = (ulint) ((ratio - .5) * 10000); + } + } + purge_sys->view = read_view_oldest_copy_or_open_new(NULL, purge_sys->heap); mutex_exit(&kernel_mutex); diff --git a/innobase/trx/trx0rseg.c b/innobase/trx/trx0rseg.c index e3885c86def..a01d4bb835d 100644 --- a/innobase/trx/trx0rseg.c +++ b/innobase/trx/trx0rseg.c @@ -135,6 +135,7 @@ trx_rseg_mem_create( trx_ulogf_t* undo_log_hdr; fil_addr_t node_addr; ulint sum_of_undo_sizes; + ulint len; #ifdef UNIV_SYNC_DEBUG ut_ad(mutex_own(&kernel_mutex)); @@ -166,7 +167,9 @@ trx_rseg_mem_create( MLOG_4BYTES, mtr) + 1 + sum_of_undo_sizes; - if (flst_get_len(rseg_header + TRX_RSEG_HISTORY, mtr) > 0) { + len = flst_get_len(rseg_header + TRX_RSEG_HISTORY, mtr); + if (len > 0) { + trx_sys->rseg_history_len += len; node_addr = trx_purge_get_log_from_hist( flst_get_last(rseg_header + TRX_RSEG_HISTORY, @@ -206,6 +209,8 @@ trx_rseg_list_and_array_init( UT_LIST_INIT(trx_sys->rseg_list); + trx_sys->rseg_history_len = 0; + for (i = 0; i < TRX_SYS_N_RSEGS; i++) { page_no = trx_sysf_rseg_get_page_no(sys_header, i, mtr); diff --git a/sql/ha_innodb.h b/sql/ha_innodb.h index 5736f70c65c..74acc0640c9 100644 --- a/sql/ha_innodb.h +++ b/sql/ha_innodb.h @@ -207,6 +207,7 @@ extern my_bool innobase_log_archive, innobase_create_status_file; extern "C" { extern ulong srv_max_buf_pool_modified_pct; +extern ulong srv_max_purge_lag; } extern TYPELIB innobase_lock_typelib; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 5838bd909dd..d36b441ac9a 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -3520,6 +3520,7 @@ enum options_mysqld { OPT_INNODB_LOG_BUFFER_SIZE, OPT_INNODB_BUFFER_POOL_SIZE, OPT_INNODB_ADDITIONAL_MEM_POOL_SIZE, + OPT_INNODB_MAX_PURGE_LAG, OPT_INNODB_FILE_IO_THREADS, OPT_INNODB_LOCK_WAIT_TIMEOUT, OPT_INNODB_THREAD_CONCURRENCY, @@ -3700,6 +3701,11 @@ struct my_option my_long_options[] = {"innodb_max_dirty_pages_pct", OPT_INNODB_MAX_DIRTY_PAGES_PCT, "Percentage of dirty pages allowed in bufferpool", (gptr*) &srv_max_buf_pool_modified_pct, (gptr*) &srv_max_buf_pool_modified_pct, 0, GET_ULONG, REQUIRED_ARG, 90, 0, 100, 0, 0, 0}, + {"innodb_max_purge_lag", OPT_INNODB_MAX_PURGE_LAG, + "", + (gptr*) &srv_max_purge_lag, + (gptr*) &srv_max_purge_lag, 0, GET_LONG, REQUIRED_ARG, 0, 0, ~0L, + 0, 1L, 0}, {"innodb_table_locks", OPT_INNODB_TABLE_LOCKS, "If Innodb should enforce LOCK TABLE", (gptr*) &global_system_variables.innodb_table_locks, diff --git a/sql/set_var.cc b/sql/set_var.cc index a6a5ea72c71..d5aadbfbdab 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -263,6 +263,8 @@ sys_var_thd_ulong sys_net_wait_timeout("wait_timeout", #ifdef HAVE_INNOBASE_DB sys_var_long_ptr sys_innodb_max_dirty_pages_pct("innodb_max_dirty_pages_pct", &srv_max_buf_pool_modified_pct); +sys_var_long_ptr sys_innodb_max_purge_lag("innodb_max_purge_lag", + &srv_max_purge_lag); sys_var_thd_bool sys_innodb_table_locks("innodb_table_locks", &SV::innodb_table_locks); #endif @@ -451,6 +453,7 @@ sys_var *sys_variables[]= &sys_os, #ifdef HAVE_INNOBASE_DB &sys_innodb_max_dirty_pages_pct, + &sys_innodb_max_purge_lag, &sys_innodb_table_locks, #endif &sys_unique_checks @@ -523,6 +526,7 @@ struct show_var_st init_vars[]= { {"innodb_log_group_home_dir", (char*) &innobase_log_group_home_dir, SHOW_CHAR_PTR}, {"innodb_mirrored_log_groups", (char*) &innobase_mirrored_log_groups, SHOW_LONG}, {sys_innodb_max_dirty_pages_pct.name, (char*) &sys_innodb_max_dirty_pages_pct, SHOW_SYS}, + {sys_innodb_max_purge_lag.name, (char*) &sys_innodb_max_purge_lag, SHOW_SYS}, {sys_innodb_table_locks.name, (char*) &sys_innodb_table_locks, SHOW_SYS}, #endif {sys_interactive_timeout.name,(char*) &sys_interactive_timeout, SHOW_SYS}, From 3bdd7e77fdb66acb4d051dec2b1c4fd136a49f9c Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 27 Oct 2004 18:57:49 +0300 Subject: [PATCH 5/5] Fix when compiling without InnoDB BitKeeper/deleted/.del-innodb-lock-master.opt~f76a4a1999728f87: Delete: mysql-test/t/innodb-lock-master.opt --- mysql-test/r/innodb-lock.result | 1 + mysql-test/t/innodb-lock-master.opt | 1 - mysql-test/t/innodb-lock.test | 5 +++++ 3 files changed, 6 insertions(+), 1 deletion(-) delete mode 100644 mysql-test/t/innodb-lock-master.opt diff --git a/mysql-test/r/innodb-lock.result b/mysql-test/r/innodb-lock.result index 407a85ed038..4ace4065c34 100644 --- a/mysql-test/r/innodb-lock.result +++ b/mysql-test/r/innodb-lock.result @@ -1,3 +1,4 @@ +set global innodb_table_locks=1; select @@innodb_table_locks; @@innodb_table_locks 1 diff --git a/mysql-test/t/innodb-lock-master.opt b/mysql-test/t/innodb-lock-master.opt deleted file mode 100644 index 403fcde87ed..00000000000 --- a/mysql-test/t/innodb-lock-master.opt +++ /dev/null @@ -1 +0,0 @@ ---innodb-table-lock=1 diff --git a/mysql-test/t/innodb-lock.test b/mysql-test/t/innodb-lock.test index 430369f4fda..a3b6f8993f2 100644 --- a/mysql-test/t/innodb-lock.test +++ b/mysql-test/t/innodb-lock.test @@ -4,6 +4,8 @@ # Check and select innodb lock type # +set global innodb_table_locks=1; + select @@innodb_table_locks; # @@ -12,7 +14,10 @@ select @@innodb_table_locks; connect (con1,localhost,root,,); connect (con2,localhost,root,,); + +--disable_warnings drop table if exists t1; +--enable_warnings # # Testing of explicit table locks with enforced table locks