diff options
-rw-r--r-- | .bzrignore | 3 | ||||
-rw-r--r-- | libmysqld/CMakeLists.txt | 2 | ||||
-rw-r--r-- | libmysqld/Makefile.am | 1 | ||||
-rw-r--r-- | sql/CMakeLists.txt | 1 | ||||
-rw-r--r-- | sql/Makefile.am | 6 | ||||
-rw-r--r-- | sql/lock.h | 42 | ||||
-rw-r--r-- | sql/mysqld.cc | 1 | ||||
-rw-r--r-- | sql/sql_base.h | 43 | ||||
-rw-r--r-- | sql/sql_parse.cc | 407 | ||||
-rw-r--r-- | sql/sql_parse.h | 2 | ||||
-rw-r--r-- | sql/sql_reload.cc | 427 | ||||
-rw-r--r-- | sql/sql_reload.h | 26 |
12 files changed, 512 insertions, 449 deletions
diff --git a/.bzrignore b/.bzrignore index a17b1dec6cb..4c437cdd36c 100644 --- a/.bzrignore +++ b/.bzrignore @@ -997,6 +997,7 @@ libmysqld/.deps/sql_cursor.Po libmysqld/.deps/sql_db.Po libmysqld/.deps/sql_delete.Po libmysqld/.deps/sql_truncate.Po +libmysqld/.deps/sql_reload.Po libmysqld/.deps/datadict.Po libmysqld/.deps/sql_derived.Po libmysqld/.deps/sql_do.Po @@ -1175,6 +1176,7 @@ libmysqld/sql_cursor.h libmysqld/sql_db.cc libmysqld/sql_delete.cc libmysqld/sql_truncate.cc +libmysqld/sql_reload.cc libmysqld/datadict.cc libmysqld/sql_derived.cc libmysqld/sql_do.cc @@ -2067,6 +2069,7 @@ sql/.deps/sql_cursor.Po sql/.deps/sql_db.Po sql/.deps/sql_delete.Po sql/.deps/sql_truncate.Po +sql/.deps/sql_reload.Po sql/.deps/datadict.Po sql/.deps/sql_derived.Po sql/.deps/sql_do.Po diff --git a/libmysqld/CMakeLists.txt b/libmysqld/CMakeLists.txt index a7efcb024ec..c3b51ad2423 100644 --- a/libmysqld/CMakeLists.txt +++ b/libmysqld/CMakeLists.txt @@ -64,7 +64,7 @@ SET(SQL_EMBEDDED_SOURCES emb_qcache.cc libmysqld.c lib_sql.cc ../sql/sql_db.cc ../sql/sql_delete.cc ../sql/sql_derived.cc ../sql/sql_do.cc ../sql/sql_error.cc ../sql/sql_handler.cc ../sql/sql_help.cc ../sql/sql_insert.cc ../sql/datadict.cc - ../sql/sql_truncate.cc + ../sql/sql_truncate.cc ../sql/sql_reload.cc ../sql/sql_lex.cc ../sql/keycaches.cc ../sql/sql_list.cc ../sql/sql_load.cc ../sql/sql_locale.cc ../sql/sql_binlog.cc ../sql/sql_manager.cc diff --git a/libmysqld/Makefile.am b/libmysqld/Makefile.am index 1ffa349bcfe..3375806e412 100644 --- a/libmysqld/Makefile.am +++ b/libmysqld/Makefile.am @@ -64,6 +64,7 @@ sqlsources = derror.cc field.cc field_conv.cc strfunc.cc filesort.cc \ opt_sum.cc procedure.cc records.cc sql_acl.cc \ sql_load.cc discover.cc sql_locale.cc \ sql_profile.cc sql_truncate.cc datadict.cc \ + sql_reload.cc \ sql_analyse.cc sql_base.cc sql_cache.cc sql_class.cc \ sql_crypt.cc sql_db.cc sql_delete.cc sql_error.cc sql_insert.cc \ sql_lex.cc sql_list.cc sql_manager.cc \ diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 943d6b2eece..68753c092e2 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -75,6 +75,7 @@ SET (SQL_SOURCE sql_profile.cc event_parse_data.cc sql_signal.cc rpl_handler.cc mdl.cc transaction.cc sys_vars.cc sql_truncate.cc datadict.cc + sql_reload.cc ${GEN_SOURCES} ${MYSYS_LIBWRAP_SOURCE}) diff --git a/sql/Makefile.am b/sql/Makefile.am index 7fed55f3cd6..1c75ead9516 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -40,6 +40,7 @@ DTRACEFILES = filesort.o \ sql_cursor.o \ sql_delete.o \ sql_truncate.o \ + sql_reload.o \ sql_insert.o \ datadict.o \ sql_parse.o \ @@ -59,6 +60,7 @@ DTRACEFILES_DEPEND = filesort.o \ sql_cursor.o \ sql_delete.o \ sql_truncate.o \ + sql_reload.o \ sql_insert.o \ datadict.o \ sql_parse.o \ @@ -126,7 +128,7 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \ contributors.h sql_servers.h sql_signal.h records.h \ sql_prepare.h rpl_handler.h replication.h mdl.h \ sql_plist.h transaction.h sys_vars.h sql_truncate.h \ - datadict.h + sql_reload.h datadict.h mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \ item.cc item_sum.cc item_buff.cc item_func.cc \ @@ -140,7 +142,7 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \ sql_connect.cc scheduler.cc sql_parse.cc \ keycaches.cc set_var.cc sql_yacc.yy sys_vars.cc \ sql_base.cc table.cc sql_select.cc sql_insert.cc \ - datadict.cc sql_profile.cc \ + sql_reload.cc datadict.cc sql_profile.cc \ sql_prepare.cc sql_error.cc sql_locale.cc \ sql_update.cc sql_delete.cc uniques.cc sql_do.cc \ procedure.cc sql_test.cc sql_truncate.cc \ diff --git a/sql/lock.h b/sql/lock.h index 0083dd3ba18..c097c8d269e 100644 --- a/sql/lock.h +++ b/sql/lock.h @@ -9,48 +9,6 @@ struct TABLE_LIST; class THD; typedef struct st_mysql_lock MYSQL_LOCK; -/* mysql_lock_tables() and open_table() flags bits */ -#define MYSQL_OPEN_IGNORE_GLOBAL_READ_LOCK 0x0001 -#define MYSQL_OPEN_IGNORE_FLUSH 0x0002 -#define MYSQL_OPEN_TEMPORARY_ONLY 0x0004 -#define MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY 0x0008 -#define MYSQL_LOCK_LOG_TABLE 0x0010 -/** - Do not try to acquire a metadata lock on the table: we - already have one. -*/ -#define MYSQL_OPEN_HAS_MDL_LOCK 0x0020 -/** - If in locked tables mode, ignore the locked tables and get - a new instance of the table. -*/ -#define MYSQL_OPEN_GET_NEW_TABLE 0x0040 -/** Don't look up the table in the list of temporary tables. */ -#define MYSQL_OPEN_SKIP_TEMPORARY 0x0080 -/** Fail instead of waiting when conficting metadata lock is discovered. */ -#define MYSQL_OPEN_FAIL_ON_MDL_CONFLICT 0x0100 -/** Open tables using MDL_SHARED lock instead of one specified in parser. */ -#define MYSQL_OPEN_FORCE_SHARED_MDL 0x0200 -/** - Open tables using MDL_SHARED_HIGH_PRIO lock instead of one specified - in parser. -*/ -#define MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL 0x0400 -/** - When opening or locking the table, use the maximum timeout - (LONG_TIMEOUT = 1 year) rather than the user-supplied timeout value. -*/ -#define MYSQL_LOCK_IGNORE_TIMEOUT 0x0800 - -/** Please refer to the internals manual. */ -#define MYSQL_OPEN_REOPEN (MYSQL_OPEN_IGNORE_FLUSH |\ - MYSQL_OPEN_IGNORE_GLOBAL_READ_LOCK |\ - MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY |\ - MYSQL_LOCK_IGNORE_TIMEOUT |\ - MYSQL_OPEN_GET_NEW_TABLE |\ - MYSQL_OPEN_SKIP_TEMPORARY |\ - MYSQL_OPEN_HAS_MDL_LOCK) - MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **table, uint count, uint flags); void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock); diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 278d1688c21..e0dc3ef0211 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -87,6 +87,7 @@ #include <errmsg.h> #include "sp_rcontext.h" #include "sp_cache.h" +#include "sql_reload.h" // reload_acl_and_cache #ifdef HAVE_POLL_H #include <poll.h> diff --git a/sql/sql_base.h b/sql/sql_base.h index 8462ef3d2aa..ca5ca31ab6a 100644 --- a/sql/sql_base.h +++ b/sql/sql_base.h @@ -89,6 +89,49 @@ TABLE_SHARE *get_cached_table_share(const char *db, const char *table_name); TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type update, uint lock_flags); + +/* mysql_lock_tables() and open_table() flags bits */ +#define MYSQL_OPEN_IGNORE_GLOBAL_READ_LOCK 0x0001 +#define MYSQL_OPEN_IGNORE_FLUSH 0x0002 +#define MYSQL_OPEN_TEMPORARY_ONLY 0x0004 +#define MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY 0x0008 +#define MYSQL_LOCK_LOG_TABLE 0x0010 +/** + Do not try to acquire a metadata lock on the table: we + already have one. +*/ +#define MYSQL_OPEN_HAS_MDL_LOCK 0x0020 +/** + If in locked tables mode, ignore the locked tables and get + a new instance of the table. +*/ +#define MYSQL_OPEN_GET_NEW_TABLE 0x0040 +/** Don't look up the table in the list of temporary tables. */ +#define MYSQL_OPEN_SKIP_TEMPORARY 0x0080 +/** Fail instead of waiting when conficting metadata lock is discovered. */ +#define MYSQL_OPEN_FAIL_ON_MDL_CONFLICT 0x0100 +/** Open tables using MDL_SHARED lock instead of one specified in parser. */ +#define MYSQL_OPEN_FORCE_SHARED_MDL 0x0200 +/** + Open tables using MDL_SHARED_HIGH_PRIO lock instead of one specified + in parser. +*/ +#define MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL 0x0400 +/** + When opening or locking the table, use the maximum timeout + (LONG_TIMEOUT = 1 year) rather than the user-supplied timeout value. +*/ +#define MYSQL_LOCK_IGNORE_TIMEOUT 0x0800 + +/** Please refer to the internals manual. */ +#define MYSQL_OPEN_REOPEN (MYSQL_OPEN_IGNORE_FLUSH |\ + MYSQL_OPEN_IGNORE_GLOBAL_READ_LOCK |\ + MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY |\ + MYSQL_LOCK_IGNORE_TIMEOUT |\ + MYSQL_OPEN_GET_NEW_TABLE |\ + MYSQL_OPEN_SKIP_TEMPORARY |\ + MYSQL_OPEN_HAS_MDL_LOCK) + bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, Open_table_context *ot_ctx); bool open_new_frm(THD *thd, TABLE_SHARE *share, const char *alias, diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index b72bd3ac7f3..18aa4a09478 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -50,6 +50,7 @@ // mysql_backup_table, // mysql_restore_table #include "sql_truncate.h" // mysql_truncate_table +#include "sql_reload.h" // reload_acl_and_cache #include "sql_connect.h" // check_user, // decrease_user_connections, // thd_init_client_charset, check_mqh, @@ -1696,153 +1697,6 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident, /** - Implementation of FLUSH TABLES <table_list> WITH READ LOCK. - - In brief: take exclusive locks, expel tables from the table - cache, reopen the tables, enter the 'LOCKED TABLES' mode, - downgrade the locks. - Note: the function is written to be called from - mysql_execute_command(), it is not reusable in arbitrary - execution context. - - Required privileges - ------------------- - Since the statement implicitly enters LOCK TABLES mode, - it requires LOCK TABLES privilege on every table. - But since the rest of FLUSH commands require - the global RELOAD_ACL, it also requires RELOAD_ACL. - - Compatibility with the global read lock - --------------------------------------- - We don't wait for the GRL, since neither the - 5.1 combination that this new statement is intended to - replace (LOCK TABLE <list> WRITE; FLUSH TABLES;), - nor FLUSH TABLES WITH READ LOCK do. - @todo: this is not implemented, Dmitry disagrees. - Currently we wait for GRL in another connection, - but are compatible with a GRL in our own connection. - - Behaviour under LOCK TABLES - --------------------------- - Bail out: i.e. don't perform an implicit UNLOCK TABLES. - This is not consistent with LOCK TABLES statement, but is - in line with behaviour of FLUSH TABLES WITH READ LOCK, and we - try to not introduce any new statements with implicit - semantics. - - Compatibility with parallel updates - ----------------------------------- - As a result, we will wait for all open transactions - against the tables to complete. After the lock downgrade, - new transactions will be able to read the tables, but not - write to them. - - Differences from FLUSH TABLES <list> - ------------------------------------- - - you can't flush WITH READ LOCK a non-existent table - - you can't flush WITH READ LOCK under LOCK TABLES - - currently incompatible with the GRL (@todo: fix) - - Effect on views and temporary tables. - ------------------------------------ - You can only apply this command to existing base tables. - If a view with such name exists, ER_WRONG_OBJECT is returned. - If a temporary table with such name exists, it's ignored: - if there is a base table, it's used, otherwise ER_NO_SUCH_TABLE - is returned. - - Implicit commit - --------------- - This statement causes an implicit commit before and - after it. - - HANDLER SQL - ----------- - If this connection has HANDLERs open against - some of the tables being FLUSHed, these handlers - are implicitly flushed (lose their position). -*/ - -static bool flush_tables_with_read_lock(THD *thd, TABLE_LIST *all_tables) -{ - Lock_tables_prelocking_strategy lock_tables_prelocking_strategy; - TABLE_LIST *table_list; - MDL_request_list mdl_requests; - - /* - This is called from SQLCOM_FLUSH, the transaction has - been committed implicitly. - */ - - /* RELOAD_ACL is checked by the caller. Check table-level privileges. */ - if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables, - FALSE, UINT_MAX, FALSE)) - goto error; - - if (thd->locked_tables_mode) - { - my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0)); - goto error; - } - - /* - Acquire SNW locks on tables to be flushed. We can't use - lock_table_names() here as this call will also acquire global IX - and database-scope IX locks on the tables, and this will make - this statement incompatible with FLUSH TABLES WITH READ LOCK. - */ - for (table_list= all_tables; table_list; - table_list= table_list->next_global) - mdl_requests.push_front(&table_list->mdl_request); - - if (thd->mdl_context.acquire_locks(&mdl_requests, - thd->variables.lock_wait_timeout)) - goto error; - - DEBUG_SYNC(thd,"flush_tables_with_read_lock_after_acquire_locks"); - - for (table_list= all_tables; table_list; - table_list= table_list->next_global) - { - /* Request removal of table from cache. */ - tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED, - table_list->db, - table_list->table_name, FALSE); - - /* Skip views and temporary tables. */ - table_list->required_type= FRMTYPE_TABLE; /* Don't try to flush views. */ - table_list->open_type= OT_BASE_ONLY; /* Ignore temporary tables. */ - } - - /* - Before opening and locking tables the below call also waits - for old shares to go away, so the fact that we don't pass - MYSQL_LOCK_IGNORE_FLUSH flag to it is important. - */ - if (open_and_lock_tables(thd, all_tables, FALSE, - MYSQL_OPEN_HAS_MDL_LOCK, - &lock_tables_prelocking_strategy) || - thd->locked_tables_list.init_locked_tables(thd)) - { - goto error; - } - thd->variables.option_bits|= OPTION_TABLE_LOCK; - - /* - We don't downgrade MDL_SHARED_NO_WRITE here as the intended - post effect of this call is identical to LOCK TABLES <...> READ, - and we didn't use thd->in_lock_talbes and - thd->sql_command= SQLCOM_LOCK_TABLES hacks to enter the LTM. - */ - - return FALSE; - -error: - return TRUE; -} - - -/** Read query from packet and store in thd->query. Used in COM_QUERY and COM_STMT_PREPARE. @@ -3972,6 +3826,10 @@ end_with_restore_list: if (first_table && lex->type & REFRESH_READ_LOCK) { + /* Check table-level privileges. */ + if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables, + FALSE, UINT_MAX, FALSE)) + goto error; if (flush_tables_with_read_lock(thd, all_tables)) goto error; my_ok(thd); @@ -6705,261 +6563,6 @@ void add_join_natural(TABLE_LIST *a, TABLE_LIST *b, List<String> *using_fields, /** - Reload/resets privileges and the different caches. - - @param thd Thread handler (can be NULL!) - @param options What should be reset/reloaded (tables, privileges, slave...) - @param tables Tables to flush (if any) - @param write_to_binlog True if we can write to the binlog. - - @note Depending on 'options', it may be very bad to write the - query to the binlog (e.g. FLUSH SLAVE); this is a - pointer where reload_acl_and_cache() will put 0 if - it thinks we really should not write to the binlog. - Otherwise it will put 1. - - @return Error status code - @retval 0 Ok - @retval !=0 Error; thd->killed is set or thd->is_error() is true -*/ - -bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, - bool *write_to_binlog) -{ - bool result=0; - select_errors=0; /* Write if more errors */ - bool tmp_write_to_binlog= 1; - - DBUG_ASSERT(!thd || !thd->in_sub_stmt); - -#ifndef NO_EMBEDDED_ACCESS_CHECKS - if (options & REFRESH_GRANT) - { - THD *tmp_thd= 0; - /* - If reload_acl_and_cache() is called from SIGHUP handler we have to - allocate temporary THD for execution of acl_reload()/grant_reload(). - */ - if (!thd && (thd= (tmp_thd= new THD))) - { - thd->thread_stack= (char*) &tmp_thd; - thd->store_globals(); - } - - if (thd) - { - bool reload_acl_failed= acl_reload(thd); - bool reload_grants_failed= grant_reload(thd); - bool reload_servers_failed= servers_reload(thd); - - if (reload_acl_failed || reload_grants_failed || reload_servers_failed) - { - result= 1; - /* - When an error is returned, my_message may have not been called and - the client will hang waiting for a response. - */ - my_error(ER_UNKNOWN_ERROR, MYF(0), "FLUSH PRIVILEGES failed"); - } - } - - if (tmp_thd) - { - delete tmp_thd; - /* Remember that we don't have a THD */ - my_pthread_setspecific_ptr(THR_THD, 0); - thd= 0; - } - reset_mqh((LEX_USER *)NULL, TRUE); - } -#endif - if (options & REFRESH_LOG) - { - /* - Flush the normal query log, the update log, the binary log, - the slow query log, the relay log (if it exists) and the log - tables. - */ - - options|= REFRESH_BINARY_LOG; - options|= REFRESH_RELAY_LOG; - options|= REFRESH_SLOW_LOG; - options|= REFRESH_GENERAL_LOG; - options|= REFRESH_ENGINE_LOG; - options|= REFRESH_ERROR_LOG; - } - - if (options & REFRESH_ERROR_LOG) - if (flush_error_log()) - result= 1; - - if ((options & REFRESH_SLOW_LOG) && opt_slow_log) - logger.flush_slow_log(); - - if ((options & REFRESH_GENERAL_LOG) && opt_log) - logger.flush_general_log(); - - if (options & REFRESH_ENGINE_LOG) - if (ha_flush_logs(NULL)) - result= 1; - - if (options & REFRESH_BINARY_LOG) - { - /* - Writing this command to the binlog may result in infinite loops - when doing mysqlbinlog|mysql, and anyway it does not really make - sense to log it automatically (would cause more trouble to users - than it would help them) - */ - tmp_write_to_binlog= 0; - if (mysql_bin_log.is_open()) - mysql_bin_log.rotate_and_purge(RP_FORCE_ROTATE); - } - if (options & REFRESH_RELAY_LOG) - { -#ifdef HAVE_REPLICATION - mysql_mutex_lock(&LOCK_active_mi); - rotate_relay_log(active_mi); - mysql_mutex_unlock(&LOCK_active_mi); -#endif - } -#ifdef HAVE_QUERY_CACHE - if (options & REFRESH_QUERY_CACHE_FREE) - { - query_cache.pack(); // FLUSH QUERY CACHE - options &= ~REFRESH_QUERY_CACHE; // Don't flush cache, just free memory - } - if (options & (REFRESH_TABLES | REFRESH_QUERY_CACHE)) - { - query_cache.flush(); // RESET QUERY CACHE - } -#endif /*HAVE_QUERY_CACHE*/ - - DBUG_ASSERT(!thd || thd->locked_tables_mode || - !thd->mdl_context.has_locks() || - thd->handler_tables_hash.records || - thd->global_read_lock.is_acquired()); - - /* - Note that if REFRESH_READ_LOCK bit is set then REFRESH_TABLES is set too - (see sql_yacc.yy) - */ - if (options & (REFRESH_TABLES | REFRESH_READ_LOCK)) - { - if ((options & REFRESH_READ_LOCK) && thd) - { - /* - On the first hand we need write lock on the tables to be flushed, - on the other hand we must not try to aspire a global read lock - if we have a write locked table as this would lead to a deadlock - when trying to reopen (and re-lock) the table after the flush. - */ - if (thd->locked_tables_mode) - { - my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0)); - return 1; - } - /* - Writing to the binlog could cause deadlocks, as we don't log - UNLOCK TABLES - */ - tmp_write_to_binlog= 0; - if (thd->global_read_lock.lock_global_read_lock(thd)) - return 1; // Killed - if (close_cached_tables(thd, tables, - ((options & REFRESH_FAST) ? FALSE : TRUE), - thd->variables.lock_wait_timeout)) - result= 1; - - if (thd->global_read_lock.make_global_read_lock_block_commit(thd)) // Killed - { - /* Don't leave things in a half-locked state */ - thd->global_read_lock.unlock_global_read_lock(thd); - return 1; - } - } - else - { - if (thd && thd->locked_tables_mode) - { - /* - If we are under LOCK TABLES we should have a write - lock on tables which we are going to flush. - */ - if (tables) - { - for (TABLE_LIST *t= tables; t; t= t->next_local) - if (!find_table_for_mdl_upgrade(thd->open_tables, t->db, - t->table_name, FALSE)) - return 1; - } - else - { - for (TABLE *tab= thd->open_tables; tab; tab= tab->next) - { - if (! tab->mdl_ticket->is_upgradable_or_exclusive()) - { - my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0), - tab->s->table_name.str); - return 1; - } - } - } - } - - if (close_cached_tables(thd, tables, - ((options & REFRESH_FAST) ? FALSE : TRUE), - (thd ? thd->variables.lock_wait_timeout : - LONG_TIMEOUT))) - result= 1; - } - my_dbopt_cleanup(); - } - if (options & REFRESH_HOSTS) - hostname_cache_refresh(); - if (thd && (options & REFRESH_STATUS)) - refresh_status(thd); - if (options & REFRESH_THREADS) - flush_thread_cache(); -#ifdef HAVE_REPLICATION - if (options & REFRESH_MASTER) - { - DBUG_ASSERT(thd); - tmp_write_to_binlog= 0; - if (reset_master(thd)) - { - result=1; - } - } -#endif -#ifdef OPENSSL - if (options & REFRESH_DES_KEY_FILE) - { - if (des_key_file && load_des_key_file(des_key_file)) - result= 1; - } -#endif -#ifdef HAVE_REPLICATION - if (options & REFRESH_SLAVE) - { - tmp_write_to_binlog= 0; - mysql_mutex_lock(&LOCK_active_mi); - if (reset_slave(thd, active_mi)) - result=1; - mysql_mutex_unlock(&LOCK_active_mi); - } -#endif - if (options & REFRESH_USER_RESOURCES) - reset_mqh((LEX_USER *) NULL, 0); /* purecov: inspected */ - *write_to_binlog= tmp_write_to_binlog; - /* - If the query was killed then this function must fail. - */ - return result || (thd ? thd->killed : 0); -} - - -/** kill on thread. @param thd Thread class diff --git a/sql/sql_parse.h b/sql/sql_parse.h index 8b7fe8f7b83..fe7fbd9482e 100644 --- a/sql/sql_parse.h +++ b/sql/sql_parse.h @@ -93,8 +93,6 @@ void mysql_init_multi_delete(LEX *lex); bool multi_delete_set_locks_and_link_aux_tables(LEX *lex); void create_table_set_open_action_and_adjust_tables(LEX *lex); pthread_handler_t handle_bootstrap(void *arg); -bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, - bool *write_to_binlog); int mysql_execute_command(THD *thd); bool do_command(THD *thd); void do_handle_bootstrap(THD *thd); diff --git a/sql/sql_reload.cc b/sql/sql_reload.cc new file mode 100644 index 00000000000..bf38af78536 --- /dev/null +++ b/sql/sql_reload.cc @@ -0,0 +1,427 @@ +/* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "sql_reload.h" +#include "sql_priv.h" +#include "mysqld.h" // select_errors +#include "sql_class.h" // THD +#include "sql_acl.h" // acl_reload +#include "sql_servers.h" // servers_reload +#include "sql_connect.h" // reset_mqh +#include "sql_base.h" // close_cached_tables +#include "sql_db.h" // my_dbopt_cleanup +#include "hostname.h" // hostname_cache_refresh +#include "sql_repl.h" // reset_master, reset_slave +#include "debug_sync.h" + + +/** + Reload/resets privileges and the different caches. + + @param thd Thread handler (can be NULL!) + @param options What should be reset/reloaded (tables, privileges, slave...) + @param tables Tables to flush (if any) + @param write_to_binlog True if we can write to the binlog. + + @note Depending on 'options', it may be very bad to write the + query to the binlog (e.g. FLUSH SLAVE); this is a + pointer where reload_acl_and_cache() will put 0 if + it thinks we really should not write to the binlog. + Otherwise it will put 1. + + @return Error status code + @retval 0 Ok + @retval !=0 Error; thd->killed is set or thd->is_error() is true +*/ + +bool reload_acl_and_cache(THD *thd, unsigned long options, + TABLE_LIST *tables, bool *write_to_binlog) +{ + bool result=0; + select_errors=0; /* Write if more errors */ + bool tmp_write_to_binlog= 1; + + DBUG_ASSERT(!thd || !thd->in_sub_stmt); + +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (options & REFRESH_GRANT) + { + THD *tmp_thd= 0; + /* + If reload_acl_and_cache() is called from SIGHUP handler we have to + allocate temporary THD for execution of acl_reload()/grant_reload(). + */ + if (!thd && (thd= (tmp_thd= new THD))) + { + thd->thread_stack= (char*) &tmp_thd; + thd->store_globals(); + } + + if (thd) + { + bool reload_acl_failed= acl_reload(thd); + bool reload_grants_failed= grant_reload(thd); + bool reload_servers_failed= servers_reload(thd); + + if (reload_acl_failed || reload_grants_failed || reload_servers_failed) + { + result= 1; + /* + When an error is returned, my_message may have not been called and + the client will hang waiting for a response. + */ + my_error(ER_UNKNOWN_ERROR, MYF(0), "FLUSH PRIVILEGES failed"); + } + } + + if (tmp_thd) + { + delete tmp_thd; + /* Remember that we don't have a THD */ + my_pthread_setspecific_ptr(THR_THD, 0); + thd= 0; + } + reset_mqh((LEX_USER *)NULL, TRUE); + } +#endif + if (options & REFRESH_LOG) + { + /* + Flush the normal query log, the update log, the binary log, + the slow query log, the relay log (if it exists) and the log + tables. + */ + + options|= REFRESH_BINARY_LOG; + options|= REFRESH_RELAY_LOG; + options|= REFRESH_SLOW_LOG; + options|= REFRESH_GENERAL_LOG; + options|= REFRESH_ENGINE_LOG; + options|= REFRESH_ERROR_LOG; + } + + if (options & REFRESH_ERROR_LOG) + if (flush_error_log()) + result= 1; + + if ((options & REFRESH_SLOW_LOG) && opt_slow_log) + logger.flush_slow_log(); + + if ((options & REFRESH_GENERAL_LOG) && opt_log) + logger.flush_general_log(); + + if (options & REFRESH_ENGINE_LOG) + if (ha_flush_logs(NULL)) + result= 1; + + if (options & REFRESH_BINARY_LOG) + { + /* + Writing this command to the binlog may result in infinite loops + when doing mysqlbinlog|mysql, and anyway it does not really make + sense to log it automatically (would cause more trouble to users + than it would help them) + */ + tmp_write_to_binlog= 0; + if (mysql_bin_log.is_open()) + mysql_bin_log.rotate_and_purge(RP_FORCE_ROTATE); + } + if (options & REFRESH_RELAY_LOG) + { +#ifdef HAVE_REPLICATION + mysql_mutex_lock(&LOCK_active_mi); + rotate_relay_log(active_mi); + mysql_mutex_unlock(&LOCK_active_mi); +#endif + } +#ifdef HAVE_QUERY_CACHE + if (options & REFRESH_QUERY_CACHE_FREE) + { + query_cache.pack(); // FLUSH QUERY CACHE + options &= ~REFRESH_QUERY_CACHE; // Don't flush cache, just free memory + } + if (options & (REFRESH_TABLES | REFRESH_QUERY_CACHE)) + { + query_cache.flush(); // RESET QUERY CACHE + } +#endif /*HAVE_QUERY_CACHE*/ + + DBUG_ASSERT(!thd || thd->locked_tables_mode || + !thd->mdl_context.has_locks() || + thd->handler_tables_hash.records || + thd->global_read_lock.is_acquired()); + + /* + Note that if REFRESH_READ_LOCK bit is set then REFRESH_TABLES is set too + (see sql_yacc.yy) + */ + if (options & (REFRESH_TABLES | REFRESH_READ_LOCK)) + { + if ((options & REFRESH_READ_LOCK) && thd) + { + /* + On the first hand we need write lock on the tables to be flushed, + on the other hand we must not try to aspire a global read lock + if we have a write locked table as this would lead to a deadlock + when trying to reopen (and re-lock) the table after the flush. + */ + if (thd->locked_tables_mode) + { + my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0)); + return 1; + } + /* + Writing to the binlog could cause deadlocks, as we don't log + UNLOCK TABLES + */ + tmp_write_to_binlog= 0; + if (thd->global_read_lock.lock_global_read_lock(thd)) + return 1; // Killed + if (close_cached_tables(thd, tables, + ((options & REFRESH_FAST) ? FALSE : TRUE), + thd->variables.lock_wait_timeout)) + result= 1; + + if (thd->global_read_lock.make_global_read_lock_block_commit(thd)) // Killed + { + /* Don't leave things in a half-locked state */ + thd->global_read_lock.unlock_global_read_lock(thd); + return 1; + } + } + else + { + if (thd && thd->locked_tables_mode) + { + /* + If we are under LOCK TABLES we should have a write + lock on tables which we are going to flush. + */ + if (tables) + { + for (TABLE_LIST *t= tables; t; t= t->next_local) + if (!find_table_for_mdl_upgrade(thd->open_tables, t->db, + t->table_name, FALSE)) + return 1; + } + else + { + for (TABLE *tab= thd->open_tables; tab; tab= tab->next) + { + if (! tab->mdl_ticket->is_upgradable_or_exclusive()) + { + my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0), + tab->s->table_name.str); + return 1; + } + } + } + } + + if (close_cached_tables(thd, tables, + ((options & REFRESH_FAST) ? FALSE : TRUE), + (thd ? thd->variables.lock_wait_timeout : + LONG_TIMEOUT))) + result= 1; + } + my_dbopt_cleanup(); + } + if (options & REFRESH_HOSTS) + hostname_cache_refresh(); + if (thd && (options & REFRESH_STATUS)) + refresh_status(thd); + if (options & REFRESH_THREADS) + flush_thread_cache(); +#ifdef HAVE_REPLICATION + if (options & REFRESH_MASTER) + { + DBUG_ASSERT(thd); + tmp_write_to_binlog= 0; + if (reset_master(thd)) + { + result=1; + } + } +#endif +#ifdef OPENSSL + if (options & REFRESH_DES_KEY_FILE) + { + if (des_key_file && load_des_key_file(des_key_file)) + result= 1; + } +#endif +#ifdef HAVE_REPLICATION + if (options & REFRESH_SLAVE) + { + tmp_write_to_binlog= 0; + mysql_mutex_lock(&LOCK_active_mi); + if (reset_slave(thd, active_mi)) + result=1; + mysql_mutex_unlock(&LOCK_active_mi); + } +#endif + if (options & REFRESH_USER_RESOURCES) + reset_mqh((LEX_USER *) NULL, 0); /* purecov: inspected */ + *write_to_binlog= tmp_write_to_binlog; + /* + If the query was killed then this function must fail. + */ + return result || (thd ? thd->killed : 0); +} + + +/** + Implementation of FLUSH TABLES <table_list> WITH READ LOCK. + + In brief: take exclusive locks, expel tables from the table + cache, reopen the tables, enter the 'LOCKED TABLES' mode, + downgrade the locks. + Note: the function is written to be called from + mysql_execute_command(), it is not reusable in arbitrary + execution context. + + Required privileges + ------------------- + Since the statement implicitly enters LOCK TABLES mode, + it requires LOCK TABLES privilege on every table. + But since the rest of FLUSH commands require + the global RELOAD_ACL, it also requires RELOAD_ACL. + + Compatibility with the global read lock + --------------------------------------- + We don't wait for the GRL, since neither the + 5.1 combination that this new statement is intended to + replace (LOCK TABLE <list> WRITE; FLUSH TABLES;), + nor FLUSH TABLES WITH READ LOCK do. + @todo: this is not implemented, Dmitry disagrees. + Currently we wait for GRL in another connection, + but are compatible with a GRL in our own connection. + + Behaviour under LOCK TABLES + --------------------------- + Bail out: i.e. don't perform an implicit UNLOCK TABLES. + This is not consistent with LOCK TABLES statement, but is + in line with behaviour of FLUSH TABLES WITH READ LOCK, and we + try to not introduce any new statements with implicit + semantics. + + Compatibility with parallel updates + ----------------------------------- + As a result, we will wait for all open transactions + against the tables to complete. After the lock downgrade, + new transactions will be able to read the tables, but not + write to them. + + Differences from FLUSH TABLES <list> + ------------------------------------- + - you can't flush WITH READ LOCK a non-existent table + - you can't flush WITH READ LOCK under LOCK TABLES + - currently incompatible with the GRL (@todo: fix) + + Effect on views and temporary tables. + ------------------------------------ + You can only apply this command to existing base tables. + If a view with such name exists, ER_WRONG_OBJECT is returned. + If a temporary table with such name exists, it's ignored: + if there is a base table, it's used, otherwise ER_NO_SUCH_TABLE + is returned. + + Implicit commit + --------------- + This statement causes an implicit commit before and + after it. + + HANDLER SQL + ----------- + If this connection has HANDLERs open against + some of the tables being FLUSHed, these handlers + are implicitly flushed (lose their position). +*/ + +bool flush_tables_with_read_lock(THD *thd, TABLE_LIST *all_tables) +{ + Lock_tables_prelocking_strategy lock_tables_prelocking_strategy; + TABLE_LIST *table_list; + MDL_request_list mdl_requests; + + /* + This is called from SQLCOM_FLUSH, the transaction has + been committed implicitly. + */ + + if (thd->locked_tables_mode) + { + my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0)); + goto error; + } + + /* + Acquire SNW locks on tables to be flushed. We can't use + lock_table_names() here as this call will also acquire global IX + and database-scope IX locks on the tables, and this will make + this statement incompatible with FLUSH TABLES WITH READ LOCK. + */ + for (table_list= all_tables; table_list; + table_list= table_list->next_global) + mdl_requests.push_front(&table_list->mdl_request); + + if (thd->mdl_context.acquire_locks(&mdl_requests, + thd->variables.lock_wait_timeout)) + goto error; + + DEBUG_SYNC(thd,"flush_tables_with_read_lock_after_acquire_locks"); + + for (table_list= all_tables; table_list; + table_list= table_list->next_global) + { + /* Request removal of table from cache. */ + tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED, + table_list->db, + table_list->table_name, FALSE); + + /* Skip views and temporary tables. */ + table_list->required_type= FRMTYPE_TABLE; /* Don't try to flush views. */ + table_list->open_type= OT_BASE_ONLY; /* Ignore temporary tables. */ + } + + /* + Before opening and locking tables the below call also waits + for old shares to go away, so the fact that we don't pass + MYSQL_LOCK_IGNORE_FLUSH flag to it is important. + */ + if (open_and_lock_tables(thd, all_tables, FALSE, + MYSQL_OPEN_HAS_MDL_LOCK, + &lock_tables_prelocking_strategy) || + thd->locked_tables_list.init_locked_tables(thd)) + { + goto error; + } + thd->variables.option_bits|= OPTION_TABLE_LOCK; + + /* + We don't downgrade MDL_SHARED_NO_WRITE here as the intended + post effect of this call is identical to LOCK TABLES <...> READ, + and we didn't use thd->in_lock_talbes and + thd->sql_command= SQLCOM_LOCK_TABLES hacks to enter the LTM. + */ + + return FALSE; + +error: + return TRUE; +} + + + diff --git a/sql/sql_reload.h b/sql/sql_reload.h new file mode 100644 index 00000000000..0df5485c907 --- /dev/null +++ b/sql/sql_reload.h @@ -0,0 +1,26 @@ +#ifndef SQL_RELOAD_INCLUDED +#define SQL_RELOAD_INCLUDED +/* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +class THD; +struct TABLE_LIST; + +bool reload_acl_and_cache(THD *thd, unsigned long options, + TABLE_LIST *tables, bool *write_to_binlog); + +bool flush_tables_with_read_lock(THD *thd, TABLE_LIST *all_tables); + +#endif |