summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.bzrignore3
-rw-r--r--libmysqld/CMakeLists.txt2
-rw-r--r--libmysqld/Makefile.am1
-rw-r--r--sql/CMakeLists.txt1
-rw-r--r--sql/Makefile.am6
-rw-r--r--sql/lock.h42
-rw-r--r--sql/mysqld.cc1
-rw-r--r--sql/sql_base.h43
-rw-r--r--sql/sql_parse.cc407
-rw-r--r--sql/sql_parse.h2
-rw-r--r--sql/sql_reload.cc427
-rw-r--r--sql/sql_reload.h26
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