summaryrefslogtreecommitdiff
path: root/sql/sql_parse.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sql_parse.cc')
-rw-r--r--sql/sql_parse.cc2131
1 files changed, 1510 insertions, 621 deletions
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 82dbe809c7d..a27cbdf5962 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -15,16 +15,15 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
#define MYSQL_LEX 1
-#include "my_global.h"
+#include <my_global.h>
#include "sql_priv.h"
-#include "unireg.h" // REQUIRED: for other includes
#include "sql_parse.h" // sql_kill, *_precheck, *_prepare
#include "lock.h" // try_transactional_lock,
// check_transactional_lock,
// set_handler_table_locks,
// lock_global_read_lock,
// make_global_read_lock_block_commit
-#include "sql_base.h" // find_temporary_tablesx
+#include "sql_base.h" // find_temporary_table
#include "sql_cache.h" // QUERY_CACHE_FLAGS_SIZE, query_cache_*
#include "sql_show.h" // mysqld_list_*, mysqld_show_*,
// calc_sum_of_all_status
@@ -44,7 +43,6 @@
#include "sql_table.h" // mysql_create_like_table,
// mysql_create_table,
// mysql_alter_table,
- // mysql_recreate_table,
// mysql_backup_table,
// mysql_restore_table
#include "sql_reload.h" // reload_acl_and_cache
@@ -82,6 +80,9 @@
#include <myisam.h>
#include <my_dir.h>
#include "rpl_handler.h"
+#include "rpl_mi.h"
+
+#include "sql_digest.h"
#include "sp_head.h"
#include "sp.h"
@@ -95,6 +96,7 @@
#include "probes_mysql.h"
#include "set_var.h"
#include "log_slow.h"
+#include "sql_bootstrap.h"
#define FLAGSTR(V,F) ((V)&(F)?#F" ":"")
@@ -118,8 +120,9 @@
"FUNCTION" : "PROCEDURE")
static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables);
-static void sql_kill(THD *thd, ulong id, killed_state state);
+static void sql_kill(THD *thd, longlong id, killed_state state, killed_type type);
static void sql_kill_user(THD *thd, LEX_USER *user, killed_state state);
+static bool lock_tables_precheck(THD *thd, TABLE_LIST *tables);
static bool execute_show_status(THD *, TABLE_LIST *);
static bool execute_rename_table(THD *, TABLE_LIST *, TABLE_LIST *);
@@ -169,6 +172,7 @@ const char *xa_state_names[]={
*/
inline bool all_tables_not_ok(THD *thd, TABLE_LIST *tables)
{
+ Rpl_filter *rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter;
return rpl_filter->is_on() && tables && !thd->spcont &&
!rpl_filter->tables_ok(thd->db, tables);
}
@@ -188,13 +192,15 @@ static bool some_non_temp_table_to_be_updated(THD *thd, TABLE_LIST *tables)
/*
- Implicitly commit a active transaction if statement requires so.
+ Check whether the statement implicitly commits an active transaction.
@param thd Thread handle.
@param mask Bitmask used for the SQL command match.
+ @return 0 No implicit commit
+ @return 1 Do a commit
*/
-static bool stmt_causes_implicit_commit(THD *thd, uint mask)
+bool stmt_causes_implicit_commit(THD *thd, uint mask)
{
LEX *lex= thd->lex;
bool skip= FALSE;
@@ -205,12 +211,22 @@ static bool stmt_causes_implicit_commit(THD *thd, uint mask)
switch (lex->sql_command) {
case SQLCOM_DROP_TABLE:
- skip= lex->drop_temporary;
+ skip= (lex->drop_temporary ||
+ (thd->variables.option_bits & OPTION_GTID_BEGIN));
break;
case SQLCOM_ALTER_TABLE:
+ /* If ALTER TABLE of non-temporary table, do implicit commit */
+ skip= (lex->create_info.tmp_table());
+ break;
case SQLCOM_CREATE_TABLE:
- /* If CREATE TABLE of non-temporary table, do implicit commit */
- skip= (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE);
+ /*
+ If CREATE TABLE of non-temporary table and the table is not part
+ if a BEGIN GTID ... COMMIT group, do a implicit commit.
+ This ensures that CREATE ... SELECT will in the same GTID group on the
+ master and slave.
+ */
+ skip= (lex->create_info.tmp_table() ||
+ (thd->variables.option_bits & OPTION_GTID_BEGIN));
break;
case SQLCOM_SET_OPTION:
skip= lex->autocommit ? FALSE : TRUE;
@@ -266,14 +282,16 @@ void init_update_queries(void)
sql_command_flags[SQLCOM_CREATE_TABLE]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
CF_AUTO_COMMIT_TRANS | CF_REPORT_PROGRESS |
CF_CAN_GENERATE_ROW_EVENTS;
- sql_command_flags[SQLCOM_CREATE_INDEX]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_CREATE_INDEX]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS | CF_REPORT_PROGRESS;
sql_command_flags[SQLCOM_ALTER_TABLE]= CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND |
- CF_AUTO_COMMIT_TRANS | CF_REPORT_PROGRESS ;
+ CF_AUTO_COMMIT_TRANS | CF_REPORT_PROGRESS |
+ CF_INSERTS_DATA;
sql_command_flags[SQLCOM_TRUNCATE]= CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND |
CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_DROP_TABLE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_LOAD]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
- CF_CAN_GENERATE_ROW_EVENTS | CF_REPORT_PROGRESS;
+ CF_CAN_GENERATE_ROW_EVENTS | CF_REPORT_PROGRESS |
+ CF_INSERTS_DATA;
sql_command_flags[SQLCOM_CREATE_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_DROP_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_ALTER_DB_UPGRADE]= CF_AUTO_COMMIT_TRANS;
@@ -290,26 +308,61 @@ void init_update_queries(void)
sql_command_flags[SQLCOM_DROP_EVENT]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_UPDATE]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
- CF_CAN_GENERATE_ROW_EVENTS;
+ CF_CAN_GENERATE_ROW_EVENTS |
+ CF_OPTIMIZER_TRACE |
+ CF_CAN_BE_EXPLAINED |
+ CF_UPDATES_DATA;
sql_command_flags[SQLCOM_UPDATE_MULTI]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
- CF_CAN_GENERATE_ROW_EVENTS;
+ CF_CAN_GENERATE_ROW_EVENTS |
+ CF_OPTIMIZER_TRACE |
+ CF_CAN_BE_EXPLAINED |
+ CF_UPDATES_DATA;
sql_command_flags[SQLCOM_INSERT]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
- CF_CAN_GENERATE_ROW_EVENTS;
+ CF_CAN_GENERATE_ROW_EVENTS |
+ CF_OPTIMIZER_TRACE |
+ CF_CAN_BE_EXPLAINED |
+ CF_INSERTS_DATA;
sql_command_flags[SQLCOM_INSERT_SELECT]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
- CF_CAN_GENERATE_ROW_EVENTS;
+ CF_CAN_GENERATE_ROW_EVENTS |
+ CF_OPTIMIZER_TRACE |
+ CF_CAN_BE_EXPLAINED |
+ CF_INSERTS_DATA;
sql_command_flags[SQLCOM_DELETE]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
- CF_CAN_GENERATE_ROW_EVENTS;
+ CF_CAN_GENERATE_ROW_EVENTS |
+ CF_OPTIMIZER_TRACE |
+ CF_CAN_BE_EXPLAINED;
sql_command_flags[SQLCOM_DELETE_MULTI]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
- CF_CAN_GENERATE_ROW_EVENTS;
+ CF_CAN_GENERATE_ROW_EVENTS |
+ CF_OPTIMIZER_TRACE |
+ CF_CAN_BE_EXPLAINED;;
sql_command_flags[SQLCOM_REPLACE]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
- CF_CAN_GENERATE_ROW_EVENTS;
+ CF_CAN_GENERATE_ROW_EVENTS |
+ CF_OPTIMIZER_TRACE |
+ CF_CAN_BE_EXPLAINED |
+ CF_INSERTS_DATA;;
sql_command_flags[SQLCOM_REPLACE_SELECT]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
- CF_CAN_GENERATE_ROW_EVENTS;
+ CF_CAN_GENERATE_ROW_EVENTS |
+ CF_OPTIMIZER_TRACE |
+ CF_CAN_BE_EXPLAINED |
+ CF_INSERTS_DATA;
sql_command_flags[SQLCOM_SELECT]= CF_REEXECUTION_FRAGILE |
- CF_CAN_GENERATE_ROW_EVENTS;
- sql_command_flags[SQLCOM_SET_OPTION]= CF_REEXECUTION_FRAGILE | CF_AUTO_COMMIT_TRANS;
+ CF_CAN_GENERATE_ROW_EVENTS |
+ CF_OPTIMIZER_TRACE |
+ CF_CAN_BE_EXPLAINED;
+ // (1) so that subquery is traced when doing "SET @var = (subquery)"
+ /*
+ @todo SQLCOM_SET_OPTION should have CF_CAN_GENERATE_ROW_EVENTS
+ set, because it may invoke a stored function that generates row
+ events. /Sven
+ */
+ sql_command_flags[SQLCOM_SET_OPTION]= CF_REEXECUTION_FRAGILE |
+ CF_AUTO_COMMIT_TRANS |
+ CF_CAN_GENERATE_ROW_EVENTS |
+ CF_OPTIMIZER_TRACE; // (1)
+ // (1) so that subquery is traced when doing "DO @var := (subquery)"
sql_command_flags[SQLCOM_DO]= CF_REEXECUTION_FRAGILE |
- CF_CAN_GENERATE_ROW_EVENTS;
+ CF_CAN_GENERATE_ROW_EVENTS |
+ CF_OPTIMIZER_TRACE; // (1)
sql_command_flags[SQLCOM_SHOW_STATUS_PROC]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
sql_command_flags[SQLCOM_SHOW_STATUS]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
@@ -335,6 +388,7 @@ void init_update_queries(void)
sql_command_flags[SQLCOM_SHOW_ENGINE_STATUS]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_ENGINE_MUTEX]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_ENGINE_LOGS]= CF_STATUS_COMMAND;
+ sql_command_flags[SQLCOM_SHOW_EXPLAIN]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_PROCESSLIST]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_GRANTS]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_CREATE_DB]= CF_STATUS_COMMAND;
@@ -350,7 +404,7 @@ void init_update_queries(void)
sql_command_flags[SQLCOM_SHOW_CREATE_EVENT]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_PROFILES]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_PROFILE]= CF_STATUS_COMMAND;
- sql_command_flags[SQLCOM_BINLOG_BASE64_EVENT]= CF_STATUS_COMMAND;
+ sql_command_flags[SQLCOM_BINLOG_BASE64_EVENT]= CF_STATUS_COMMAND | CF_CAN_GENERATE_ROW_EVENTS;
sql_command_flags[SQLCOM_SHOW_CLIENT_STATS]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_USER_STATS]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_TABLE_STATS]= CF_STATUS_COMMAND;
@@ -362,18 +416,21 @@ void init_update_queries(void)
sql_command_flags[SQLCOM_CREATE_USER]= CF_CHANGES_DATA;
sql_command_flags[SQLCOM_RENAME_USER]= CF_CHANGES_DATA;
sql_command_flags[SQLCOM_DROP_USER]= CF_CHANGES_DATA;
+ sql_command_flags[SQLCOM_CREATE_ROLE]= CF_CHANGES_DATA;
sql_command_flags[SQLCOM_GRANT]= CF_CHANGES_DATA;
+ sql_command_flags[SQLCOM_GRANT_ROLE]= CF_CHANGES_DATA;
sql_command_flags[SQLCOM_REVOKE]= CF_CHANGES_DATA;
+ sql_command_flags[SQLCOM_REVOKE_ROLE]= CF_CHANGES_DATA;
sql_command_flags[SQLCOM_OPTIMIZE]= CF_CHANGES_DATA;
- sql_command_flags[SQLCOM_CREATE_FUNCTION]= CF_CHANGES_DATA;
+ sql_command_flags[SQLCOM_CREATE_FUNCTION]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_CREATE_PROCEDURE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_CREATE_SPFUNCTION]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_DROP_PROCEDURE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_DROP_FUNCTION]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_ALTER_PROCEDURE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_ALTER_FUNCTION]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
- sql_command_flags[SQLCOM_INSTALL_PLUGIN]= CF_CHANGES_DATA;
- sql_command_flags[SQLCOM_UNINSTALL_PLUGIN]= CF_CHANGES_DATA;
+ sql_command_flags[SQLCOM_INSTALL_PLUGIN]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_UNINSTALL_PLUGIN]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
/*
The following is used to preserver CF_ROW_COUNT during the
@@ -381,11 +438,32 @@ void init_update_queries(void)
last called (or executed) statement is preserved.
See mysql_execute_command() for how CF_ROW_COUNT is used.
*/
+ /*
+ (1): without it, in "CALL some_proc((subq))", subquery would not be
+ traced.
+ */
sql_command_flags[SQLCOM_CALL]= CF_REEXECUTION_FRAGILE |
- CF_CAN_GENERATE_ROW_EVENTS;
+ CF_CAN_GENERATE_ROW_EVENTS |
+ CF_OPTIMIZER_TRACE; // (1)
sql_command_flags[SQLCOM_EXECUTE]= CF_CAN_GENERATE_ROW_EVENTS;
/*
+ We don't want to change to statement based replication for these commands
+ */
+ sql_command_flags[SQLCOM_ROLLBACK]|= CF_FORCE_ORIGINAL_BINLOG_FORMAT;
+ /* We don't want to replicate ALTER TABLE for temp tables in row format */
+ sql_command_flags[SQLCOM_ALTER_TABLE]|= CF_FORCE_ORIGINAL_BINLOG_FORMAT;
+ /* We don't want to replicate TRUNCATE for temp tables in row format */
+ sql_command_flags[SQLCOM_TRUNCATE]|= CF_FORCE_ORIGINAL_BINLOG_FORMAT;
+ /* We don't want to replicate DROP for temp tables in row format */
+ sql_command_flags[SQLCOM_DROP_TABLE]|= CF_FORCE_ORIGINAL_BINLOG_FORMAT;
+ /* We don't want to replicate CREATE/DROP INDEX for temp tables in row format */
+ sql_command_flags[SQLCOM_CREATE_INDEX]|= CF_FORCE_ORIGINAL_BINLOG_FORMAT;
+ sql_command_flags[SQLCOM_DROP_INDEX]|= CF_FORCE_ORIGINAL_BINLOG_FORMAT;
+ /* One can change replication mode with SET */
+ sql_command_flags[SQLCOM_SET_OPTION]|= CF_FORCE_ORIGINAL_BINLOG_FORMAT;
+
+ /*
The following admin table operations are allowed
on log tables.
*/
@@ -398,18 +476,120 @@ void init_update_queries(void)
sql_command_flags[SQLCOM_CREATE_USER]|= CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_DROP_USER]|= CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_RENAME_USER]|= CF_AUTO_COMMIT_TRANS;
- sql_command_flags[SQLCOM_REVOKE_ALL]= CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_CREATE_ROLE]|= CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_DROP_ROLE]|= CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_REVOKE]|= CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_REVOKE_ALL]= CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_REVOKE_ROLE]|= CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_GRANT]|= CF_AUTO_COMMIT_TRANS;
-
- sql_command_flags[SQLCOM_ASSIGN_TO_KEYCACHE]= CF_AUTO_COMMIT_TRANS;
- sql_command_flags[SQLCOM_PRELOAD_KEYS]= CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_GRANT_ROLE]|= CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_FLUSH]= CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_RESET]= CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_CREATE_SERVER]= CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_ALTER_SERVER]= CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_DROP_SERVER]= CF_AUTO_COMMIT_TRANS;
+
+ /*
+ The following statements can deal with temporary tables,
+ so temporary tables should be pre-opened for those statements to
+ simplify privilege checking.
+
+ There are other statements that deal with temporary tables and open
+ them, but which are not listed here. The thing is that the order of
+ pre-opening temporary tables for those statements is somewhat custom.
+
+ Note that SQLCOM_RENAME_TABLE should not be in this list!
+ */
+ sql_command_flags[SQLCOM_CREATE_TABLE]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_DROP_TABLE]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_CREATE_INDEX]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_ALTER_TABLE]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_TRUNCATE]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_LOAD]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_DROP_INDEX]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_UPDATE]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_UPDATE_MULTI]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_INSERT_SELECT]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_DELETE]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_DELETE_MULTI]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_REPLACE_SELECT]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_SELECT]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_SET_OPTION]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_DO]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_HA_OPEN]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_CALL]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_CHECKSUM]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_ANALYZE]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_CHECK]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_OPTIMIZE]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_REPAIR]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_PRELOAD_KEYS]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_ASSIGN_TO_KEYCACHE]|= CF_PREOPEN_TMP_TABLES;
+
+ /*
+ DDL statements that should start with closing opened handlers.
+
+ We use this flag only for statements for which open HANDLERs
+ have to be closed before temporary tables are pre-opened.
+ */
+ sql_command_flags[SQLCOM_CREATE_TABLE]|= CF_HA_CLOSE;
+ sql_command_flags[SQLCOM_DROP_TABLE]|= CF_HA_CLOSE;
+ sql_command_flags[SQLCOM_ALTER_TABLE]|= CF_HA_CLOSE;
+ sql_command_flags[SQLCOM_TRUNCATE]|= CF_HA_CLOSE;
+ sql_command_flags[SQLCOM_REPAIR]|= CF_HA_CLOSE;
+ sql_command_flags[SQLCOM_OPTIMIZE]|= CF_HA_CLOSE;
+ sql_command_flags[SQLCOM_ANALYZE]|= CF_HA_CLOSE;
+ sql_command_flags[SQLCOM_CHECK]|= CF_HA_CLOSE;
+ sql_command_flags[SQLCOM_CREATE_INDEX]|= CF_HA_CLOSE;
+ sql_command_flags[SQLCOM_DROP_INDEX]|= CF_HA_CLOSE;
+ sql_command_flags[SQLCOM_PRELOAD_KEYS]|= CF_HA_CLOSE;
+ sql_command_flags[SQLCOM_ASSIGN_TO_KEYCACHE]|= CF_HA_CLOSE;
+
+ /*
+ Mark statements that always are disallowed in read-only
+ transactions. Note that according to the SQL standard,
+ even temporary table DDL should be disallowed.
+ */
+ sql_command_flags[SQLCOM_CREATE_TABLE]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_ALTER_TABLE]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_DROP_TABLE]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_RENAME_TABLE]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_CREATE_INDEX]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_DROP_INDEX]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_CREATE_DB]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_DROP_DB]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_ALTER_DB_UPGRADE]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_ALTER_DB]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_CREATE_VIEW]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_DROP_VIEW]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_CREATE_TRIGGER]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_DROP_TRIGGER]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_CREATE_EVENT]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_ALTER_EVENT]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_DROP_EVENT]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_CREATE_USER]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_RENAME_USER]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_DROP_USER]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_CREATE_SERVER]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_ALTER_SERVER]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_DROP_SERVER]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_CREATE_FUNCTION]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_CREATE_PROCEDURE]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_CREATE_SPFUNCTION]|=CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_DROP_PROCEDURE]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_DROP_FUNCTION]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_ALTER_PROCEDURE]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_ALTER_FUNCTION]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_TRUNCATE]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_ALTER_TABLESPACE]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_REPAIR]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_OPTIMIZE]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_GRANT]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_REVOKE]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_REVOKE_ALL]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_INSTALL_PLUGIN]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_UNINSTALL_PLUGIN]|= CF_DISALLOW_IN_RO_TRANS;
}
bool sqlcom_can_generate_row_events(const THD *thd)
@@ -462,7 +642,7 @@ void execute_init_command(THD *thd, LEX_STRING *init_command,
thd->profiling.set_query_source(buf, len);
#endif
- thd_proc_info(thd, "Execution of init_command");
+ THD_STAGE_INFO(thd, stage_execution_of_init_command);
save_client_capabilities= thd->client_capabilities;
thd->client_capabilities|= CLIENT_MULTI_QUERIES;
/*
@@ -481,21 +661,29 @@ void execute_init_command(THD *thd, LEX_STRING *init_command,
}
+static char *fgets_fn(char *buffer, size_t size, fgets_input_t input, int *error)
+{
+ MYSQL_FILE *in= static_cast<MYSQL_FILE*> (input);
+ char *line= mysql_file_fgets(buffer, size, in);
+ if (error)
+ *error= (line == NULL) ? ferror(in->m_file) : 0;
+ return line;
+}
+
+
static void handle_bootstrap_impl(THD *thd)
{
MYSQL_FILE *file= bootstrap_file;
- char *buff, *res;
-
- DBUG_ENTER("handle_bootstrap");
+ DBUG_ENTER("handle_bootstrap_impl");
#ifndef EMBEDDED_LIBRARY
pthread_detach_this_thread();
thd->thread_stack= (char*) &thd;
#endif /* EMBEDDED_LIBRARY */
- thd_proc_info(thd, 0);
thd->security_ctx->user= (char*) my_strdup("boot", MYF(MY_WME));
- thd->security_ctx->priv_user[0]= thd->security_ctx->priv_host[0]=0;
+ thd->security_ctx->priv_user[0]= thd->security_ctx->priv_host[0]=
+ thd->security_ctx->priv_role[0]= 0;
/*
Make the "client" handle multiple results. This is necessary
to enable stored procedures with SELECTs and Dynamic SQL
@@ -503,50 +691,58 @@ static void handle_bootstrap_impl(THD *thd)
*/
thd->client_capabilities|= CLIENT_MULTI_RESULTS;
- buff= (char*) thd->net.buff;
thd->init_for_queries();
- while (mysql_file_fgets(buff, thd->net.max_packet, file))
+
+ for ( ; ; )
{
+ char buffer[MAX_BOOTSTRAP_QUERY_SIZE] = "";
+ int rc, length;
char *query;
- /* strlen() can't be deleted because mysql_file_fgets() doesn't return length */
- ulong length= (ulong) strlen(buff);
- while (buff[length-1] != '\n' && !mysql_file_feof(file))
+ int error= 0;
+
+ rc= read_bootstrap_query(buffer, &length, file, fgets_fn, &error);
+
+ if (rc == READ_BOOTSTRAP_EOF)
+ break;
+ /*
+ Check for bootstrap file errors. SQL syntax errors will be
+ caught below.
+ */
+ if (rc != READ_BOOTSTRAP_SUCCESS)
{
/*
- We got only a part of the current string. Will try to increase
- net buffer then read the rest of the current string.
+ mysql_parse() may have set a successful error status for the previous
+ query. We must clear the error status to report the bootstrap error.
*/
- /* purecov: begin tested */
- if (net_realloc(&(thd->net), 2 * thd->net.max_packet))
+ thd->get_stmt_da()->reset_diagnostics_area();
+
+ /* Get the nearest query text for reference. */
+ char *err_ptr= buffer + (length <= MAX_BOOTSTRAP_ERROR_LEN ?
+ 0 : (length - MAX_BOOTSTRAP_ERROR_LEN));
+ switch (rc)
{
- thd->protocol->end_statement();
- bootstrap_error= 1;
+ case READ_BOOTSTRAP_ERROR:
+ my_printf_error(ER_UNKNOWN_ERROR, "Bootstrap file error, return code (%d). "
+ "Nearest query: '%s'", MYF(0), error, err_ptr);
break;
- }
- buff= (char*) thd->net.buff;
- res= mysql_file_fgets(buff + length, thd->net.max_packet - length, file);
- if (!res && !mysql_file_feof(file))
- {
- thd->protocol->end_statement();
- bootstrap_error= 1;
+
+ case READ_BOOTSTRAP_QUERY_SIZE:
+ my_printf_error(ER_UNKNOWN_ERROR, "Boostrap file error. Query size "
+ "exceeded %d bytes near '%s'.", MYF(0),
+ MAX_BOOTSTRAP_LINE_SIZE, err_ptr);
break;
- }
- length+= (ulong) strlen(buff + length);
- /* purecov: end */
- }
- if (bootstrap_error)
- break; /* purecov: inspected */
- while (length && (my_isspace(thd->charset(), buff[length-1]) ||
- buff[length-1] == ';'))
- length--;
- buff[length]=0;
+ default:
+ DBUG_ASSERT(false);
+ break;
+ }
- /* Skip lines starting with delimiter */
- if (strncmp(buff, STRING_WITH_LEN("delimiter")) == 0)
- continue;
+ thd->protocol->end_statement();
+ bootstrap_error= 1;
+ break;
+ }
- query= (char *) thd->memdup_w_gap(buff, length + 1,
+ query= (char *) thd->memdup_w_gap(buffer, length + 1,
thd->db_length + 1 +
QUERY_CACHE_DB_LENGTH_SIZE +
QUERY_CACHE_FLAGS_SIZE);
@@ -581,6 +777,7 @@ static void handle_bootstrap_impl(THD *thd)
#if defined(ENABLED_PROFILING)
thd->profiling.finish_current_query();
#endif
+ delete_explain_query(thd->lex);
if (bootstrap_error)
break;
@@ -625,14 +822,13 @@ void do_handle_bootstrap(THD *thd)
handle_bootstrap_impl(thd);
end:
- net_end(&thd->net);
- thd->cleanup();
delete thd;
#ifndef EMBEDDED_LIBRARY
- mysql_mutex_lock(&LOCK_thread_count);
- thread_count--;
+ thread_safe_decrement32(&thread_count, &thread_count_lock);
in_bootstrap= FALSE;
+
+ mysql_mutex_lock(&LOCK_thread_count);
mysql_cond_broadcast(&COND_thread_count);
mysql_mutex_unlock(&LOCK_thread_count);
my_thread_end();
@@ -713,7 +909,7 @@ bool do_command(THD *thd)
Consider moving to init_connect() instead.
*/
thd->clear_error(); // Clear error message
- thd->stmt_da->reset_diagnostics_area();
+ thd->get_stmt_da()->reset_diagnostics_area();
net_new_transaction(net);
@@ -736,18 +932,31 @@ bool do_command(THD *thd)
*/
DEBUG_SYNC(thd, "before_do_command_net_read");
- if ((packet_length= my_net_read(net)) == packet_error)
+ packet_length= my_net_read_packet(net, 1);
+
+ if (packet_length == packet_error)
{
DBUG_PRINT("info",("Got error %d reading command from socket %s",
net->error,
vio_description(net->vio)));
+ /* Instrument this broken statement as "statement/com/error" */
+ thd->m_statement_psi= MYSQL_REFINE_STATEMENT(thd->m_statement_psi,
+ com_statement_info[COM_END].
+ m_key);
+
+
/* Check if we can continue without closing the connection */
/* The error must be set. */
DBUG_ASSERT(thd->is_error());
thd->protocol->end_statement();
+ /* Mark the statement completed. */
+ MYSQL_END_STATEMENT(thd->m_statement_psi, thd->get_stmt_da());
+ thd->m_statement_psi= NULL;
+ thd->m_digest= NULL;
+
if (net->error != 3)
{
return_value= TRUE; // We have to close it.
@@ -790,9 +999,14 @@ bool do_command(THD *thd)
my_net_set_read_timeout(net, thd->variables.net_read_timeout);
DBUG_ASSERT(packet_length);
+ DBUG_ASSERT(!thd->apc_target.is_enabled());
return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
+ DBUG_ASSERT(!thd->apc_target.is_enabled());
out:
+ /* The statement instrumentation must be closed in all cases. */
+ DBUG_ASSERT(thd->m_digest == NULL);
+ DBUG_ASSERT(thd->m_statement_psi == NULL);
DBUG_RETURN(return_value);
}
#endif /* EMBEDDED_LIBRARY */
@@ -893,7 +1107,12 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
{ DBUG_PRINT("crash_dispatch_command_before", ("now"));
DBUG_ABORT(); });
- thd->command=command;
+ /* Performance Schema Interface instrumentation, begin */
+ thd->m_statement_psi= MYSQL_REFINE_STATEMENT(thd->m_statement_psi,
+ com_statement_info[command].
+ m_key);
+ thd->set_command(command);
+
/*
Commands which always take a long time are logged into
the slow log only if opt_log_slow_admin_statements is set.
@@ -901,14 +1120,21 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
thd->enable_slow_log= TRUE;
thd->query_plan_flags= QPLAN_INIT;
thd->lex->sql_command= SQLCOM_END; /* to avoid confusing VIEW detectors */
+ thd->reset_kill_query();
DEBUG_SYNC(thd,"dispatch_command_before_set_time");
thd->set_time();
- if (server_command_flags[command] & CF_SKIP_QUERY_ID)
- thd->set_query_id(get_query_id());
- else
+ if (!(server_command_flags[command] & CF_SKIP_QUERY_ID))
thd->set_query_id(next_query_id());
+ else
+ {
+ /*
+ ping, get statistics or similar stateless command.
+ No reason to increase query id here.
+ */
+ thd->set_query_id(get_query_id());
+ }
inc_thread_running();
if (!(server_command_flags[command] & CF_SKIP_QUESTIONS))
@@ -944,6 +1170,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
#ifdef HAVE_REPLICATION
case COM_REGISTER_SLAVE:
{
+ status_var_increment(thd->status_var.com_register_slave);
if (!register_slave(thd, (uchar*)packet, packet_length))
my_ok(thd);
break;
@@ -951,7 +1178,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
#endif
case COM_CHANGE_USER:
{
- bool rc;
+ int auth_rc;
status_var_increment(thd->status_var.com_other);
thd->change_user();
@@ -982,13 +1209,13 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
if (thd->failed_com_change_user >= 3)
{
my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
- rc= 1;
+ auth_rc= 1;
}
else
- rc= acl_authenticate(thd, 0, packet_length);
+ auth_rc= acl_authenticate(thd, packet_length);
- MYSQL_AUDIT_NOTIFY_CONNECTION_CHANGE_USER(thd);
- if (rc)
+ mysql_audit_notify_connection_change_user(thd);
+ if (auth_rc)
{
/* Free user if allocated by acl_authenticate */
my_free(thd->security_ctx->user);
@@ -1048,6 +1275,10 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
}
case COM_QUERY:
{
+ DBUG_ASSERT(thd->m_digest == NULL);
+ thd->m_digest= & thd->m_digest_state;
+ thd->m_digest->reset(thd->m_token_array, max_digest_length);
+
if (alloc_query(thd, packet, packet_length))
break; // fatal error is set
MYSQL_QUERY_START(thd->query(), thd->thread_id,
@@ -1060,6 +1291,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
#if defined(ENABLED_PROFILING)
thd->profiling.set_query_source(thd->query(), thd->query_length());
#endif
+ MYSQL_SET_STATEMENT_TEXT(thd->m_statement_psi, thd->query(),
+ thd->query_length());
+
Parser_state parser_state;
if (parser_state.init(thd, thd->query(), thd->query_length()))
break;
@@ -1084,12 +1318,15 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
query_cache_end_of_result(thd);
mysql_audit_general(thd, MYSQL_AUDIT_GENERAL_STATUS,
- thd->stmt_da->is_error() ? thd->stmt_da->sql_errno()
- : 0, command_name[command].str);
+ thd->get_stmt_da()->is_error()
+ ? thd->get_stmt_da()->sql_errno()
+ : 0,
+ command_name[command].str);
ulong length= (ulong)(packet_end - beginning_of_next_stmt);
log_slow_statement(thd);
+ DBUG_ASSERT(!thd->apc_target.is_enabled());
/* Remove garbage at start of query */
while (length > 0 && my_isspace(thd->charset(), *beginning_of_next_stmt))
@@ -1098,6 +1335,12 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
length--;
}
+ /* PSI end */
+ MYSQL_END_STATEMENT(thd->m_statement_psi, thd->get_stmt_da());
+ thd->m_statement_psi= NULL;
+ thd->m_digest= NULL;
+
+ /* DTRACE end */
if (MYSQL_QUERY_DONE_ENABLED())
{
MYSQL_QUERY_DONE(thd->is_error());
@@ -1109,11 +1352,23 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
thd->profiling.set_query_source(beginning_of_next_stmt, length);
#endif
+ /* DTRACE begin */
MYSQL_QUERY_START(beginning_of_next_stmt, thd->thread_id,
(char *) (thd->db ? thd->db : ""),
&thd->security_ctx->priv_user[0],
(char *) thd->security_ctx->host_or_ip);
+ /* PSI begin */
+ thd->m_digest= & thd->m_digest_state;
+
+ thd->m_statement_psi= MYSQL_START_STATEMENT(&thd->m_statement_state,
+ com_statement_info[command].m_key,
+ thd->db, thd->db_length,
+ thd->charset());
+ THD_STAGE_INFO(thd, stage_init);
+ MYSQL_SET_STATEMENT_TEXT(thd->m_statement_psi, beginning_of_next_stmt,
+ length);
+
thd->set_query_and_id(beginning_of_next_stmt, length,
thd->charset(), next_query_id());
/*
@@ -1176,7 +1431,10 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
lex_start(thd);
/* Must be before we init the table list. */
if (lower_case_table_names)
+ {
table_name.length= my_casedn_str(files_charset_info, table_name.str);
+ db.length= my_casedn_str(files_charset_info, db.str);
+ }
table_list.init_one_table(db.str, db.length, table_name.str,
table_name.length, table_name.str, TL_READ);
/*
@@ -1202,6 +1460,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
thd->set_query(fields, query_length);
general_log_print(thd, command, "%s %s", table_list.table_name, fields);
+ if (open_temporary_tables(thd, &table_list))
+ break;
+
if (check_table_access(thd, SELECT_ACL, &table_list,
TRUE, UINT_MAX, FALSE))
break;
@@ -1238,7 +1499,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
/* We don't calculate statistics for this command */
general_log_print(thd, command, NullS);
net->error=0; // Don't give 'abort' message
- thd->stmt_da->disable_status(); // Don't send anything back
+ thd->get_stmt_da()->disable_status(); // Don't send anything back
error=TRUE; // End server
break;
#ifndef EMBEDDED_LIBRARY
@@ -1258,10 +1519,10 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
/* TODO: The following has to be changed to an 8 byte integer */
pos = uint4korr(packet);
flags = uint2korr(packet + 4);
- thd->server_id=0; /* avoid suicide */
+ thd->variables.server_id=0; /* avoid suicide */
if ((slave_server_id= uint4korr(packet+6))) // mysqlbinlog.server_id==0
kill_zombie_dump_threads(slave_server_id);
- thd->server_id = slave_server_id;
+ thd->variables.server_id = slave_server_id;
const char *name= packet + 10;
size_t nlen= strlen(name);
@@ -1286,7 +1547,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
lex_start(thd);
status_var_increment(thd->status_var.com_stat[SQLCOM_FLUSH]);
- ulong options= (ulong) (uchar) packet[0];
+ ulonglong options= (ulonglong) (uchar) packet[0];
if (trans_commit_implicit(thd))
break;
thd->mdl_context.release_transactional_locks();
@@ -1305,17 +1566,21 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
and flushes tables.
*/
bool res;
- my_pthread_setspecific_ptr(THR_THD, NULL);
+ set_current_thd(0);
res= reload_acl_and_cache(NULL, options | REFRESH_FAST,
NULL, &not_used);
- my_pthread_setspecific_ptr(THR_THD, thd);
+ set_current_thd(thd);
if (res)
break;
}
else
#endif
- if (reload_acl_and_cache(thd, options, (TABLE_LIST*) 0, &not_used))
- break;
+ {
+ thd->lex->relay_log_connection_name.str= (char*) "";
+ thd->lex->relay_log_connection_name.length= 0;
+ if (reload_acl_and_cache(thd, options, (TABLE_LIST*) 0, &not_used))
+ break;
+ }
if (trans_commit_implicit(thd))
break;
close_thread_tables(thd);
@@ -1370,7 +1635,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
if (!(uptime= (ulong) (thd->start_time - server_start_time)))
queries_per_second1000= 0;
else
- queries_per_second1000= thd->query_id * LL(1000) / uptime;
+ queries_per_second1000= thd->query_id * 1000 / uptime;
length= my_snprintf(buff, buff_len - 1,
"Uptime: %lu Threads: %d Questions: %lu "
@@ -1380,8 +1645,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
(int) thread_count, (ulong) thd->query_id,
current_global_status_var->long_query_count,
current_global_status_var->opened_tables,
- refresh_version,
- cached_open_tables(),
+ tdc_refresh_version(),
+ tc_records(),
(uint) (queries_per_second1000 / 1000),
(uint) (queries_per_second1000 % 1000));
#ifdef EMBEDDED_LIBRARY
@@ -1390,7 +1655,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
#else
(void) my_net_write(net, (uchar*) buff, length);
(void) net_flush(net);
- thd->stmt_da->disable_status();
+ thd->get_stmt_da()->disable_status();
#endif
break;
}
@@ -1412,7 +1677,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
{
status_var_increment(thd->status_var.com_stat[SQLCOM_KILL]);
ulong id=(ulong) uint4korr(packet);
- sql_kill(thd,id, KILL_CONNECTION_HARD);
+ sql_kill(thd, id, KILL_CONNECTION_HARD, KILL_TYPE_ID);
break;
}
case COM_SET_OPTION:
@@ -1466,19 +1731,26 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
mysql_audit_general(thd, MYSQL_AUDIT_GENERAL_RESULT, 0, 0);
mysql_audit_general(thd, MYSQL_AUDIT_GENERAL_STATUS,
- thd->stmt_da->is_error() ? thd->stmt_da->sql_errno() : 0,
+ thd->get_stmt_da()->is_error() ?
+ thd->get_stmt_da()->sql_errno() : 0,
command_name[command].str);
thd->update_all_stats();
log_slow_statement(thd);
- thd_proc_info(thd, "cleaning up");
+ THD_STAGE_INFO(thd, stage_cleaning_up);
thd->reset_query();
- thd->command=COM_SLEEP;
+ thd->set_examined_row_count(0); // For processlist
+ thd->set_command(COM_SLEEP);
+
+ /* Performance Schema Interface instrumentation, end */
+ MYSQL_END_STATEMENT(thd->m_statement_psi, thd->get_stmt_da());
+ thd->m_statement_psi= NULL;
+ thd->m_digest= NULL;
+
thd->set_time();
dec_thread_running();
- thd_proc_info(thd, 0);
thd->packet.shrink(thd->variables.net_buffer_length); // Reclaim some memory
free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
@@ -1503,30 +1775,38 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
}
+/*
+ @note
+ This function must call delete_explain_query().
+*/
void log_slow_statement(THD *thd)
{
DBUG_ENTER("log_slow_statement");
+
/*
The following should never be true with our current code base,
but better to keep this here so we don't accidently try to log a
statement in a trigger or stored function
*/
if (unlikely(thd->in_sub_stmt))
- DBUG_VOID_RETURN; // Don't set time for sub stmt
+ goto end; // Don't set time for sub stmt
+
/* Follow the slow log filter configuration. */
if (!thd->enable_slow_log ||
(thd->variables.log_slow_filter
&& !(thd->variables.log_slow_filter & thd->query_plan_flags)))
- DBUG_VOID_RETURN;
+ {
+ goto end;
+ }
if (((thd->server_status & SERVER_QUERY_WAS_SLOW) ||
((thd->server_status &
(SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED)) &&
opt_log_queries_not_using_indexes &&
!(sql_command_flags[thd->lex->sql_command] & CF_STATUS_COMMAND))) &&
- thd->examined_row_count >= thd->variables.min_examined_row_limit)
+ thd->get_examined_row_count() >= thd->variables.min_examined_row_limit)
{
thd->status_var.long_query_count++;
/*
@@ -1535,13 +1815,15 @@ void log_slow_statement(THD *thd)
*/
if (thd->variables.log_slow_rate_limit > 1 &&
(global_query_id % thd->variables.log_slow_rate_limit) != 0)
- DBUG_VOID_RETURN;
+ goto end;
- thd_proc_info(thd, "logging slow query");
+ THD_STAGE_INFO(thd, stage_logging_slow_query);
slow_log_print(thd, thd->query(), thd->query_length(),
thd->utime_after_query);
- thd_proc_info(thd, 0);
}
+
+end:
+ delete_explain_query(thd->lex);
DBUG_VOID_RETURN;
}
@@ -1638,8 +1920,8 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
DBUG_RETURN(1);
lex->query_tables_last= query_tables_last;
break;
- }
#endif
+ }
case SCH_PROFILES:
/*
Mark this current profiling record to be discarded. We don't
@@ -1748,25 +2030,7 @@ bool alloc_query(THD *thd, const char *packet, uint packet_length)
return FALSE;
}
-static void reset_one_shot_variables(THD *thd)
-{
- thd->variables.character_set_client=
- global_system_variables.character_set_client;
- thd->variables.collation_connection=
- global_system_variables.collation_connection;
- thd->variables.collation_database=
- global_system_variables.collation_database;
- thd->variables.collation_server=
- global_system_variables.collation_server;
- thd->update_charset();
- thd->variables.time_zone=
- global_system_variables.time_zone;
- thd->variables.lc_time_names= &my_locale_en_US;
- thd->one_shot_set= 0;
-}
-
-static
bool sp_process_definer(THD *thd)
{
DBUG_ENTER("sp_process_definer");
@@ -1803,7 +2067,7 @@ bool sp_process_definer(THD *thd)
Query_arena original_arena;
Query_arena *ps_arena= thd->activate_stmt_arena_if_needed(&original_arena);
- lex->definer= create_default_definer(thd);
+ lex->definer= create_default_definer(thd, false);
if (ps_arena)
thd->restore_active_arena(ps_arena, &original_arena);
@@ -1817,20 +2081,24 @@ bool sp_process_definer(THD *thd)
}
else
{
+ LEX_USER *d= lex->definer= get_current_user(thd, lex->definer);
+ if (!d)
+ DBUG_RETURN(TRUE);
+
/*
- If the specified definer differs from the current user, we
+ If the specified definer differs from the current user or role, we
should check that the current user has SUPER privilege (in order
to create a stored routine under another user one must have
SUPER privilege).
*/
- if ((strcmp(lex->definer->user.str, thd->security_ctx->priv_user) ||
- my_strcasecmp(system_charset_info, lex->definer->host.str,
- thd->security_ctx->priv_host)) &&
- check_global_access(thd, SUPER_ACL, true))
- {
- my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER");
+ bool curuser= !strcmp(d->user.str, thd->security_ctx->priv_user);
+ bool currole= !curuser && !strcmp(d->user.str, thd->security_ctx->priv_role);
+ bool curuserhost= curuser && d->host.str &&
+ !my_strcasecmp(system_charset_info, d->host.str,
+ thd->security_ctx->priv_host);
+ if (!curuserhost && !currole &&
+ check_global_access(thd, SUPER_ACL, false))
DBUG_RETURN(TRUE);
- }
}
/* Check that the specified definer exists. Emit a warning if not. */
@@ -1839,7 +2107,7 @@ bool sp_process_definer(THD *thd)
if (!is_acl_user(lex->definer->host.str, lex->definer->user.str))
{
push_warning_printf(thd,
- MYSQL_ERROR::WARN_LEVEL_NOTE,
+ Sql_condition::WARN_LEVEL_NOTE,
ER_NO_SUCH_USER,
ER(ER_NO_SUCH_USER),
lex->definer->user.str,
@@ -1933,7 +2201,7 @@ err:
int
mysql_execute_command(THD *thd)
{
- int res= FALSE;
+ int res= 0;
int up_result= 0;
LEX *lex= thd->lex;
/* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */
@@ -1947,6 +2215,8 @@ mysql_execute_command(THD *thd)
#ifdef HAVE_REPLICATION
/* have table map for update for multi-update statement (BUG#37051) */
bool have_table_map_for_update= FALSE;
+ /* */
+ Rpl_filter *rpl_filter;
#endif
DBUG_ENTER("mysql_execute_command");
@@ -1992,12 +2262,12 @@ mysql_execute_command(THD *thd)
variables, but for now this is probably good enough.
*/
if ((sql_command_flags[lex->sql_command] & CF_DIAGNOSTIC_STMT) != 0)
- thd->warning_info->set_read_only(TRUE);
+ thd->get_stmt_da()->set_warning_info_read_only(TRUE);
else
{
- thd->warning_info->set_read_only(FALSE);
+ thd->get_stmt_da()->set_warning_info_read_only(FALSE);
if (all_tables)
- thd->warning_info->opt_clear_warning_info(thd->query_id);
+ thd->get_stmt_da()->opt_clear_warning_info(thd->query_id);
}
#ifdef HAVE_REPLICATION
@@ -2020,11 +2290,15 @@ mysql_execute_command(THD *thd)
according to slave filtering rules.
Returning success without producing any errors in this case.
*/
- DBUG_RETURN(0);
+ if (!thd->lex->check_exists)
+ DBUG_RETURN(0);
+ /*
+ DROP TRIGGER IF NOT EXISTS will return without an error later
+ after possibly writing the query to a binlog
+ */
}
-
- // force searching in slave.cc:tables_ok()
- all_tables->updating= 1;
+ else // force searching in slave.cc:tables_ok()
+ all_tables->updating= 1;
}
/*
@@ -2060,9 +2334,6 @@ mysql_execute_command(THD *thd)
{
/* we warn the slave SQL thread */
my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
- if (thd->one_shot_set)
- reset_one_shot_variables(thd);
- DBUG_RETURN(0);
}
}
@@ -2082,28 +2353,11 @@ mysql_execute_command(THD *thd)
if (!(lex->sql_command == SQLCOM_UPDATE_MULTI) &&
!(lex->sql_command == SQLCOM_SET_OPTION) &&
!(lex->sql_command == SQLCOM_DROP_TABLE &&
- lex->drop_temporary && lex->drop_if_exists) &&
+ lex->drop_temporary && lex->check_exists) &&
all_tables_not_ok(thd, all_tables))
{
/* we warn the slave SQL thread */
my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
- if (thd->one_shot_set)
- {
- /*
- It's ok to check thd->one_shot_set here:
-
- The charsets in a MySQL 5.0 slave can change by both a binlogged
- SET ONE_SHOT statement and the event-internal charset setting,
- and these two ways to change charsets do not seems to work
- together.
-
- At least there seems to be problems in the rli cache for
- charsets if we are using ONE_SHOT. Note that this is normally no
- problem because either the >= 5.0 slave reads a 4.1 binlog (with
- ONE_SHOT) *or* or 5.0 binlog (without ONE_SHOT) but never both."
- */
- reset_one_shot_variables(thd);
- }
DBUG_RETURN(0);
}
/*
@@ -2129,11 +2383,27 @@ mysql_execute_command(THD *thd)
#endif
status_var_increment(thd->status_var.com_stat[lex->sql_command]);
- thd->progress.report_to_client= test(sql_command_flags[lex->sql_command] &
- CF_REPORT_PROGRESS);
+ thd->progress.report_to_client= MY_TEST(sql_command_flags[lex->sql_command] &
+ CF_REPORT_PROGRESS);
DBUG_ASSERT(thd->transaction.stmt.modified_non_trans_table == FALSE);
+ /* store old value of binlog format */
+ enum_binlog_format orig_binlog_format,orig_current_stmt_binlog_format;
+
+ thd->get_binlog_format(&orig_binlog_format,
+ &orig_current_stmt_binlog_format);
+
+ /*
+ Force statement logging for DDL commands to allow us to update
+ privilege, system or statistic tables directly without the updates
+ getting logged.
+ */
+ if (!(sql_command_flags[lex->sql_command] &
+ (CF_CAN_GENERATE_ROW_EVENTS | CF_FORCE_ORIGINAL_BINLOG_FORMAT |
+ CF_STATUS_COMMAND)))
+ thd->set_binlog_format_stmt();
+
/*
End a active transaction so that this command will have it's
own transaction and will also sync the binary log. If a DDL is
@@ -2149,18 +2419,59 @@ mysql_execute_command(THD *thd)
DBUG_ASSERT(! thd->in_sub_stmt);
/* Statement transaction still should not be started. */
DBUG_ASSERT(thd->transaction.stmt.is_empty());
- /* Commit the normal transaction if one is active. */
- if (trans_commit_implicit(thd))
- goto error;
- /* Release metadata locks acquired in this transaction. */
- thd->mdl_context.release_transactional_locks();
+ if (!(thd->variables.option_bits & OPTION_GTID_BEGIN))
+ {
+ /* Commit the normal transaction if one is active. */
+ if (trans_commit_implicit(thd))
+ goto error;
+ /* Release metadata locks acquired in this transaction. */
+ thd->mdl_context.release_transactional_locks();
+ }
}
-
+
#ifndef DBUG_OFF
if (lex->sql_command != SQLCOM_SET_OPTION)
DEBUG_SYNC(thd,"before_execute_sql_command");
#endif
+ /*
+ Check if we are in a read-only transaction and we're trying to
+ execute a statement which should always be disallowed in such cases.
+
+ Note that this check is done after any implicit commits.
+ */
+ if (thd->tx_read_only &&
+ (sql_command_flags[lex->sql_command] & CF_DISALLOW_IN_RO_TRANS))
+ {
+ my_error(ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION, MYF(0));
+ goto error;
+ }
+
+ /*
+ Close tables open by HANDLERs before executing DDL statement
+ which is going to affect those tables.
+
+ This should happen before temporary tables are pre-opened as
+ otherwise we will get errors about attempt to re-open tables
+ if table to be changed is open through HANDLER.
+
+ Note that even although this is done before any privilege
+ checks there is no security problem here as closing open
+ HANDLER doesn't require any privileges anyway.
+ */
+ if (sql_command_flags[lex->sql_command] & CF_HA_CLOSE)
+ mysql_ha_rm_tables(thd, all_tables);
+
+ /*
+ Pre-open temporary tables to simplify privilege checking
+ for statements which need this.
+ */
+ if (sql_command_flags[lex->sql_command] & CF_PREOPEN_TMP_TABLES)
+ {
+ if (open_temporary_tables(thd, all_tables))
+ goto error;
+ }
+
switch (lex->sql_command) {
case SQLCOM_SHOW_EVENTS:
@@ -2168,18 +2479,40 @@ mysql_execute_command(THD *thd)
my_error(ER_NOT_SUPPORTED_YET, MYF(0), "embedded server");
break;
#endif
- case SQLCOM_SHOW_STATUS_PROC:
- case SQLCOM_SHOW_STATUS_FUNC:
- if ((res= check_table_access(thd, SELECT_ACL, all_tables, FALSE,
- UINT_MAX, FALSE)))
- goto error;
- res= execute_sqlcom_select(thd, all_tables);
- break;
case SQLCOM_SHOW_STATUS:
{
execute_show_status(thd, all_tables);
break;
}
+ case SQLCOM_SHOW_EXPLAIN:
+ {
+ if (!thd->security_ctx->priv_user[0] &&
+ check_global_access(thd,PROCESS_ACL))
+ break;
+
+ /*
+ The select should use only one table, it's the SHOW EXPLAIN pseudo-table
+ */
+ if (lex->sroutines.records || lex->query_tables->next_global)
+ {
+ my_message(ER_SET_CONSTANTS_ONLY, ER(ER_SET_CONSTANTS_ONLY),
+ MYF(0));
+ goto error;
+ }
+
+ Item **it= lex->value_list.head_ref();
+ if (!(*it)->basic_const_item() ||
+ (!(*it)->fixed && (*it)->fix_fields(lex->thd, it)) ||
+ (*it)->check_cols(1))
+ {
+ my_message(ER_SET_CONSTANTS_ONLY, ER(ER_SET_CONSTANTS_ONLY),
+ MYF(0));
+ goto error;
+ }
+ }
+ /* fall through */
+ case SQLCOM_SHOW_STATUS_PROC:
+ case SQLCOM_SHOW_STATUS_FUNC:
case SQLCOM_SHOW_DATABASES:
case SQLCOM_SHOW_TABLES:
case SQLCOM_SHOW_TRIGGERS:
@@ -2284,16 +2617,16 @@ case SQLCOM_PREPARE:
case SQLCOM_SHOW_WARNS:
{
res= mysqld_show_warnings(thd, (ulong)
- ((1L << (uint) MYSQL_ERROR::WARN_LEVEL_NOTE) |
- (1L << (uint) MYSQL_ERROR::WARN_LEVEL_WARN) |
- (1L << (uint) MYSQL_ERROR::WARN_LEVEL_ERROR)
+ ((1L << (uint) Sql_condition::WARN_LEVEL_NOTE) |
+ (1L << (uint) Sql_condition::WARN_LEVEL_WARN) |
+ (1L << (uint) Sql_condition::WARN_LEVEL_ERROR)
));
break;
}
case SQLCOM_SHOW_ERRORS:
{
res= mysqld_show_warnings(thd, (ulong)
- (1L << (uint) MYSQL_ERROR::WARN_LEVEL_ERROR));
+ (1L << (uint) Sql_condition::WARN_LEVEL_ERROR));
break;
}
case SQLCOM_SHOW_PROFILES:
@@ -2353,10 +2686,62 @@ case SQLCOM_PREPARE:
#ifdef HAVE_REPLICATION
case SQLCOM_CHANGE_MASTER:
{
+ LEX_MASTER_INFO *lex_mi= &thd->lex->mi;
+ Master_info *mi;
+ bool new_master= 0;
+ bool master_info_added;
+
if (check_global_access(thd, SUPER_ACL))
goto error;
+ /*
+ In this code it's ok to use LOCK_active_mi as we are adding new things
+ into master_info_index
+ */
mysql_mutex_lock(&LOCK_active_mi);
- res = change_master(thd,active_mi);
+ if (!master_info_index)
+ {
+ mysql_mutex_unlock(&LOCK_active_mi);
+ my_error(ER_SERVER_SHUTDOWN, MYF(0));
+ goto error;
+ }
+
+ mi= master_info_index->get_master_info(&lex_mi->connection_name,
+ Sql_condition::WARN_LEVEL_NOTE);
+
+ if (mi == NULL)
+ {
+ /* New replication created */
+ mi= new Master_info(&lex_mi->connection_name, relay_log_recovery);
+ if (!mi || mi->error())
+ {
+ delete mi;
+ res= 1;
+ mysql_mutex_unlock(&LOCK_active_mi);
+ break;
+ }
+ new_master= 1;
+ }
+
+ res= change_master(thd, mi, &master_info_added);
+ if (res && new_master)
+ {
+ /*
+ If the new master was added by change_master(), remove it as it didn't
+ work (this will free mi as well).
+
+ If new master was not added, we still need to free mi.
+ */
+ if (master_info_added)
+ master_info_index->remove_master_info(mi);
+ else
+ delete mi;
+ }
+ else
+ {
+ mi->rpl_filter= get_or_create_rpl_filter(lex_mi->connection_name.str,
+ lex_mi->connection_name.length);
+ }
+
mysql_mutex_unlock(&LOCK_active_mi);
break;
}
@@ -2365,18 +2750,24 @@ case SQLCOM_PREPARE:
/* Accept one of two privileges */
if (check_global_access(thd, SUPER_ACL | REPL_CLIENT_ACL))
goto error;
- mysql_mutex_lock(&LOCK_active_mi);
- if (active_mi != NULL)
+
+ if (lex->verbose)
{
- res = show_master_info(thd, active_mi);
+ mysql_mutex_lock(&LOCK_active_mi);
+ res= show_all_master_info(thd);
+ mysql_mutex_unlock(&LOCK_active_mi);
}
else
{
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- WARN_NO_MASTER_INFO, ER(WARN_NO_MASTER_INFO));
- my_ok(thd);
+ LEX_MASTER_INFO *lex_mi= &thd->lex->mi;
+ Master_info *mi;
+ if ((mi= get_master_info(&lex_mi->connection_name,
+ Sql_condition::WARN_LEVEL_ERROR)))
+ {
+ res= show_master_info(thd, mi, 0);
+ mi->release();
+ }
}
- mysql_mutex_unlock(&LOCK_active_mi);
break;
}
case SQLCOM_SHOW_MASTER_STAT:
@@ -2432,20 +2823,20 @@ case SQLCOM_PREPARE:
goto end_with_restore_list;
}
+ /* Check privileges */
if ((res= create_table_precheck(thd, select_tables, create_table)))
goto end_with_restore_list;
/* Might have been updated in create_table_precheck */
create_info.alias= create_table->alias;
-#ifdef HAVE_READLINK
- /* Fix names if symlinked tables */
+ /* Fix names if symlinked or relocated tables */
if (append_file_to_dir(thd, &create_info.data_file_name,
create_table->table_name) ||
append_file_to_dir(thd, &create_info.index_file_name,
create_table->table_name))
goto end_with_restore_list;
-#endif
+
/*
If no engine type was given, work out the default now
rather than at parse-time.
@@ -2466,6 +2857,18 @@ case SQLCOM_PREPARE:
create_info.table_charset= 0;
}
+ /*
+ If we are a slave, we should add OR REPLACE if we don't have
+ IF EXISTS. This will help a slave to recover from
+ CREATE TABLE OR EXISTS failures by dropping the table and
+ retrying the create.
+ */
+ create_info.org_options= create_info.options;
+ if (thd->slave_thread &&
+ slave_ddl_exec_mode_options == SLAVE_EXEC_MODE_IDEMPOTENT &&
+ !(lex->create_info.options & HA_LEX_CREATE_IF_NOT_EXISTS))
+ create_info.options|= HA_LEX_CREATE_REPLACE;
+
#ifdef WITH_PARTITION_STORAGE_ENGINE
{
partition_info *part_info= thd->lex->part_info;
@@ -2478,9 +2881,6 @@ case SQLCOM_PREPARE:
}
#endif
- /* Close any open handlers for the table. */
- mysql_ha_rm_tables(thd, create_table);
-
if (select_lex->item_list.elements) // With select
{
select_result *result;
@@ -2528,7 +2928,7 @@ case SQLCOM_PREPARE:
*/
if (splocal_refs != thd->query_name_consts)
push_warning(thd,
- MYSQL_ERROR::WARN_LEVEL_WARN,
+ Sql_condition::WARN_LEVEL_WARN,
ER_UNKNOWN_ERROR,
"Invoked routine ran a statement that may cause problems with "
"binary log, see 'NAME_CONST issues' in 'Binary Logging of Stored Programs' "
@@ -2551,34 +2951,35 @@ case SQLCOM_PREPARE:
goto end_with_restore_list;
}
+ /* Copy temporarily the statement flags to thd for lock_table_names() */
+ uint save_thd_create_info_options= thd->lex->create_info.options;
+ thd->lex->create_info.options|= create_info.options;
res= open_and_lock_tables(thd, lex->query_tables, TRUE, 0);
+ thd->lex->create_info.options= save_thd_create_info_options;
if (res)
{
/* Got error or warning. Set res to 1 if error */
if (!(res= thd->is_error()))
my_ok(thd); // CREATE ... IF NOT EXISTS
+ goto end_with_restore_list;
}
- else
+
+ /* Ensure we don't try to create something from which we select from */
+ if ((create_info.options & HA_LEX_CREATE_REPLACE) &&
+ !create_info.tmp_table())
{
- /* The table already exists */
- if (create_table->table)
+ TABLE_LIST *duplicate;
+ if ((duplicate= unique_table(thd, lex->query_tables,
+ lex->query_tables->next_global,
+ CHECK_DUP_FOR_CREATE)))
{
- if (create_info.options & HA_LEX_CREATE_IF_NOT_EXISTS)
- {
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
- ER_TABLE_EXISTS_ERROR,
- ER(ER_TABLE_EXISTS_ERROR),
- create_info.alias);
- my_ok(thd);
- }
- else
- {
- my_error(ER_TABLE_EXISTS_ERROR, MYF(0), create_info.alias);
- res= 1;
- }
+ update_non_unique_table_error(lex->query_tables, "CREATE",
+ duplicate);
+ res= TRUE;
goto end_with_restore_list;
}
-
+ }
+ {
/*
Remove target table from main select and name resolution
context. This can't be done earlier as it will break view merging in
@@ -2586,9 +2987,8 @@ case SQLCOM_PREPARE:
*/
lex->unlink_first_table(&link_to_local);
- /* So that CREATE TEMPORARY TABLE gets to binlog at commit/rollback */
- if (create_info.options & HA_LEX_CREATE_TMP_TABLE)
- thd->variables.option_bits|= OPTION_KEEP_LOG;
+ /* Store reference to table in case of LOCK TABLES */
+ create_info.table= create_table->table;
/*
select_create is currently not re-execution friendly and
@@ -2606,18 +3006,18 @@ case SQLCOM_PREPARE:
CREATE from SELECT give its SELECT_LEX for SELECT,
and item_list belong to SELECT
*/
- res= handle_select(thd, lex, result, 0);
+ if (!(res= handle_select(thd, lex, result, 0)))
+ {
+ if (create_info.tmp_table())
+ thd->variables.option_bits|= OPTION_KEEP_LOG;
+ }
delete result;
}
-
lex->link_first_table_back(create_table, link_to_local);
}
}
else
{
- /* So that CREATE TEMPORARY TABLE gets to binlog at commit/rollback */
- if (create_info.options & HA_LEX_CREATE_TMP_TABLE)
- thd->variables.option_bits|= OPTION_KEEP_LOG;
/* regular create */
if (create_info.options & HA_LEX_CREATE_TABLE_LIKE)
{
@@ -2632,14 +3032,18 @@ case SQLCOM_PREPARE:
&create_info, &alter_info);
}
if (!res)
+ {
+ /* So that CREATE TEMPORARY TABLE gets to binlog at commit/rollback */
+ if (create_info.tmp_table())
+ thd->variables.option_bits|= OPTION_KEEP_LOG;
my_ok(thd);
+ }
}
end_with_restore_list:
break;
}
case SQLCOM_CREATE_INDEX:
- /* Fall through */
case SQLCOM_DROP_INDEX:
/*
CREATE INDEX and DROP INDEX are implemented by calling ALTER
@@ -2675,46 +3079,111 @@ end_with_restore_list:
res= mysql_alter_table(thd, first_table->db, first_table->table_name,
&create_info, first_table, &alter_info,
- 0, (ORDER*) 0, 0, 0);
+ 0, (ORDER*) 0, 0);
break;
}
#ifdef HAVE_REPLICATION
case SQLCOM_SLAVE_START:
{
- mysql_mutex_lock(&LOCK_active_mi);
- start_slave(thd,active_mi,1 /* net report*/);
- mysql_mutex_unlock(&LOCK_active_mi);
+ LEX_MASTER_INFO* lex_mi= &thd->lex->mi;
+ Master_info *mi;
+ int load_error;
+
+ load_error= rpl_load_gtid_slave_state(thd);
+
+ /*
+ We don't need to ensure that only one user is using master_info
+ as start_slave is protected against simultaneous usage
+ */
+ if ((mi= get_master_info(&lex_mi->connection_name,
+ Sql_condition::WARN_LEVEL_ERROR)))
+ {
+ if (load_error)
+ {
+ /*
+ We cannot start a slave using GTID if we cannot load the
+ GTID position from the mysql.gtid_slave_pos table. But we
+ can allow non-GTID replication (useful eg. during upgrade).
+ */
+ if (mi->using_gtid != Master_info::USE_GTID_NO)
+ {
+ mi->release();
+ break;
+ }
+ else
+ thd->clear_error();
+ }
+ if (!start_slave(thd, mi, 1 /* net report*/))
+ my_ok(thd);
+ mi->release();
+ }
break;
}
case SQLCOM_SLAVE_STOP:
- /*
- If the client thread has locked tables, a deadlock is possible.
- Assume that
- - the client thread does LOCK TABLE t READ.
- - then the master updates t.
- - then the SQL slave thread wants to update t,
- so it waits for the client thread because t is locked by it.
+ {
+ LEX_MASTER_INFO *lex_mi;
+ Master_info *mi;
+ /*
+ If the client thread has locked tables, a deadlock is possible.
+ Assume that
+ - the client thread does LOCK TABLE t READ.
+ - then the master updates t.
+ - then the SQL slave thread wants to update t,
+ so it waits for the client thread because t is locked by it.
- then the client thread does SLAVE STOP.
SLAVE STOP waits for the SQL slave thread to terminate its
update t, which waits for the client thread because t is locked by it.
- To prevent that, refuse SLAVE STOP if the
- client thread has locked tables
- */
- if (thd->locked_tables_mode ||
- thd->in_active_multi_stmt_transaction() || thd->global_read_lock.is_acquired())
+ To prevent that, refuse SLAVE STOP if the
+ client thread has locked tables
+ */
+ if (thd->locked_tables_mode ||
+ thd->in_active_multi_stmt_transaction() ||
+ thd->global_read_lock.is_acquired())
+ {
+ my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
+ ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
+ goto error;
+ }
+
+ lex_mi= &thd->lex->mi;
+ if ((mi= get_master_info(&lex_mi->connection_name,
+ Sql_condition::WARN_LEVEL_ERROR)))
+ {
+ if (stop_slave(thd, mi, 1/* net report*/))
+ res= 1;
+ mi->release();
+ if (rpl_parallel_resize_pool_if_no_slaves())
+ res= 1;
+ if (!res)
+ my_ok(thd);
+ }
+ break;
+ }
+ case SQLCOM_SLAVE_ALL_START:
{
- my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
- ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
- goto error;
+ mysql_mutex_lock(&LOCK_active_mi);
+ if (master_info_index && !master_info_index->start_all_slaves(thd))
+ my_ok(thd);
+ mysql_mutex_unlock(&LOCK_active_mi);
+ break;
}
+ case SQLCOM_SLAVE_ALL_STOP:
{
+ if (thd->locked_tables_mode ||
+ thd->in_active_multi_stmt_transaction() ||
+ thd->global_read_lock.is_acquired())
+ {
+ my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
+ ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
+ goto error;
+ }
mysql_mutex_lock(&LOCK_active_mi);
- stop_slave(thd,active_mi,1/* net report*/);
+ if (master_info_index && !master_info_index->stop_all_slaves(thd))
+ my_ok(thd);
mysql_mutex_unlock(&LOCK_active_mi);
break;
}
#endif /* HAVE_REPLICATION */
-
case SQLCOM_RENAME_TABLE:
{
if (execute_rename_table(thd, first_table, all_tables))
@@ -2775,6 +3244,13 @@ end_with_restore_list:
else
{
/*
+ Temporary tables should be opened for SHOW CREATE TABLE, but not
+ for SHOW CREATE VIEW.
+ */
+ if (open_temporary_tables(thd, all_tables))
+ goto error;
+
+ /*
The fact that check_some_access() returned FALSE does not mean that
access is granted. We need to check if first_table->grant.privilege
contains any table-specific privilege.
@@ -2953,6 +3429,18 @@ end_with_restore_list:
case SQLCOM_INSERT:
{
DBUG_ASSERT(first_table == all_tables && first_table != 0);
+
+ /*
+ Since INSERT DELAYED doesn't support temporary tables, we could
+ not pre-open temporary tables for SQLCOM_INSERT / SQLCOM_REPLACE.
+ Open them here instead.
+ */
+ if (first_table->lock_type != TL_WRITE_DELAYED)
+ {
+ if ((res= open_temporary_tables(thd, all_tables)))
+ break;
+ }
+
if ((res= insert_precheck(thd, all_tables)))
break;
@@ -2994,6 +3482,7 @@ end_with_restore_list:
case SQLCOM_INSERT_SELECT:
{
select_result *sel_result;
+ bool explain= MY_TEST(lex->describe);
DBUG_ASSERT(first_table == all_tables && first_table != 0);
if ((res= insert_precheck(thd, all_tables)))
break;
@@ -3052,7 +3541,10 @@ end_with_restore_list:
lex->duplicates,
lex->ignore)))
{
- res= handle_select(thd, lex, sel_result, OPTION_SETUP_TABLES_DONE);
+ if (explain)
+ res= mysql_explain_union(thd, &thd->lex->unit, sel_result);
+ else
+ res= handle_select(thd, lex, sel_result, OPTION_SETUP_TABLES_DONE);
/*
Invalidate the table in the query cache if something changed
after unlocking when changes become visible.
@@ -3068,8 +3560,22 @@ end_with_restore_list:
query_cache_invalidate3(thd, first_table, 1);
first_table->next_local= save_table;
}
+ if (explain)
+ {
+ /*
+ sel_result needs to be cleaned up properly.
+ INSERT... SELECT statement will call either send_eof() or
+ abort_result_set(). EXPLAIN doesn't call either, so we need
+ to cleanup manually.
+ */
+ sel_result->abort_result_set();
+ }
delete sel_result;
}
+
+ if (!res && explain)
+ res= thd->lex->explain->send_explain(thd);
+
/* revert changes for SP */
MYSQL_INSERT_SELECT_DONE(res, (ulong) thd->get_row_count_func());
select_lex->table_list.first= first_table;
@@ -3088,6 +3594,7 @@ end_with_restore_list:
}
case SQLCOM_DELETE:
{
+ select_result *sel_result=lex->result;
DBUG_ASSERT(first_table == all_tables && first_table != 0);
if ((res= delete_precheck(thd, all_tables)))
break;
@@ -3095,9 +3602,13 @@ end_with_restore_list:
unit->set_limit(select_lex);
MYSQL_DELETE_START(thd->query());
- res = mysql_delete(thd, all_tables, select_lex->where,
- &select_lex->order_list,
- unit->select_limit_cnt, select_lex->options);
+ if (!(sel_result= lex->result) && !(sel_result= new select_send()))
+ return 1;
+ res = mysql_delete(thd, all_tables,
+ select_lex->where, &select_lex->order_list,
+ unit->select_limit_cnt, select_lex->options,
+ sel_result);
+ delete sel_result;
MYSQL_DELETE_DONE(res, (ulong) thd->get_row_count_func());
break;
}
@@ -3105,7 +3616,8 @@ end_with_restore_list:
{
DBUG_ASSERT(first_table == all_tables && first_table != 0);
TABLE_LIST *aux_tables= thd->lex->auxiliary_table_list.first;
- multi_delete *del_result;
+ bool explain= MY_TEST(lex->describe);
+ multi_delete *result;
if ((res= multi_delete_precheck(thd, all_tables)))
break;
@@ -3116,7 +3628,7 @@ end_with_restore_list:
if (add_item_to_list(thd, new Item_null()))
goto error;
- thd_proc_info(thd, "init");
+ THD_STAGE_INFO(thd, stage_init);
if ((res= open_and_lock_tables(thd, all_tables, TRUE, 0)))
break;
@@ -3127,25 +3639,34 @@ end_with_restore_list:
goto error;
}
- if (!thd->is_fatal_error &&
- (del_result= new multi_delete(aux_tables, lex->table_count)))
- {
- res= mysql_select(thd, &select_lex->ref_pointer_array,
- select_lex->get_table_list(),
- select_lex->with_wild,
- select_lex->item_list,
- select_lex->where,
- 0, (ORDER *)NULL, (ORDER *)NULL, (Item *)NULL,
- (ORDER *)NULL,
- (select_lex->options | thd->variables.option_bits |
- SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK |
- OPTION_SETUP_TABLES_DONE) & ~OPTION_BUFFER_RESULT,
- del_result, unit, select_lex);
- res|= thd->is_error();
- MYSQL_MULTI_DELETE_DONE(res, del_result->num_deleted());
- if (res)
- del_result->abort_result_set();
- delete del_result;
+ if (!thd->is_fatal_error)
+ {
+ result= new multi_delete(aux_tables, lex->table_count);
+ if (result)
+ {
+ res= mysql_select(thd, &select_lex->ref_pointer_array,
+ select_lex->get_table_list(),
+ select_lex->with_wild,
+ select_lex->item_list,
+ select_lex->where,
+ 0, (ORDER *)NULL, (ORDER *)NULL, (Item *)NULL,
+ (ORDER *)NULL,
+ (select_lex->options | thd->variables.option_bits |
+ SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK |
+ OPTION_SETUP_TABLES_DONE) & ~OPTION_BUFFER_RESULT,
+ result, unit, select_lex);
+ res|= thd->is_error();
+
+ MYSQL_MULTI_DELETE_DONE(res, result->num_deleted());
+ if (res)
+ result->abort_result_set(); /* for both DELETE and EXPLAIN DELETE */
+ else
+ {
+ if (explain)
+ res= thd->lex->explain->send_explain(thd);
+ }
+ delete result;
+ }
}
else
{
@@ -3167,8 +3688,18 @@ end_with_restore_list:
/* So that DROP TEMPORARY TABLE gets to binlog at commit/rollback */
thd->variables.option_bits|= OPTION_KEEP_LOG;
}
+ /*
+ If we are a slave, we should add IF EXISTS if the query executed
+ on the master without an error. This will help a slave to
+ recover from multi-table DROP TABLE that was aborted in the
+ middle.
+ */
+ if (thd->slave_thread && !thd->slave_expected_error &&
+ slave_ddl_exec_mode_options == SLAVE_EXEC_MODE_IDEMPOTENT)
+ lex->check_exists= 1;
+
/* DDL and binlog write order are protected by metadata locks. */
- res= mysql_rm_table(thd, first_table, lex->drop_if_exists,
+ res= mysql_rm_table(thd, first_table, lex->check_exists,
lex->drop_temporary);
}
break;
@@ -3249,11 +3780,6 @@ end_with_restore_list:
goto error;
if (!(res= sql_set_variables(thd, lex_var_list)))
{
- /*
- If the previous command was a SET ONE_SHOT, we don't want to forget
- about the ONE_SHOT property of that SET. So we use a |= instead of = .
- */
- thd->one_shot_set|= lex->one_shot_set;
my_ok(thd);
}
else
@@ -3299,8 +3825,20 @@ end_with_restore_list:
thd->mdl_context.release_transactional_locks();
if (res)
goto error;
- if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables,
- FALSE, UINT_MAX, FALSE))
+
+ /*
+ Here we have to pre-open temporary tables for LOCK TABLES.
+
+ CF_PREOPEN_TMP_TABLES is not set for this SQL statement simply
+ because LOCK TABLES calls close_thread_tables() as a first thing
+ (it's called from unlock_locked_tables() above). So even if
+ CF_PREOPEN_TMP_TABLES was set and the tables would be pre-opened
+ in a usual way, they would have been closed.
+ */
+ if (open_temporary_tables(thd, all_tables))
+ goto error;
+
+ if (lock_tables_precheck(thd, all_tables))
goto error;
thd->variables.option_bits|= OPTION_TABLE_LOCK;
@@ -3328,9 +3866,7 @@ end_with_restore_list:
prepared statement- safe.
*/
HA_CREATE_INFO create_info(lex->create_info);
- char *alias;
- if (!(alias=thd->strmake(lex->name.str, lex->name.length)) ||
- check_db_name(&lex->name))
+ if (check_db_name(&lex->name))
{
my_error(ER_WRONG_DB_NAME, MYF(0), lex->name.str);
break;
@@ -3343,18 +3879,23 @@ end_with_restore_list:
above was not called. So we have to check rules again here.
*/
#ifdef HAVE_REPLICATION
- if (thd->slave_thread &&
- (!rpl_filter->db_ok(lex->name.str) ||
- !rpl_filter->db_ok_with_wild_table(lex->name.str)))
+ if (thd->slave_thread)
{
- my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
- break;
+ rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter;
+ if (!rpl_filter->db_ok(lex->name.str) ||
+ !rpl_filter->db_ok_with_wild_table(lex->name.str))
+ {
+ my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
+ break;
+ }
+ if (slave_ddl_exec_mode_options == SLAVE_EXEC_MODE_IDEMPOTENT &&
+ !(lex->create_info.options & HA_LEX_CREATE_IF_NOT_EXISTS))
+ create_info.options|= HA_LEX_CREATE_IF_NOT_EXISTS;
}
#endif
if (check_access(thd, CREATE_ACL, lex->name.str, NULL, NULL, 1, 0))
break;
- res= mysql_create_db(thd,(lower_case_table_names == 2 ? alias :
- lex->name.str), &create_info, 0);
+ res= mysql_create_db(thd, lex->name.str, &create_info, 0);
break;
}
case SQLCOM_DROP_DB:
@@ -3372,30 +3913,39 @@ end_with_restore_list:
above was not called. So we have to check rules again here.
*/
#ifdef HAVE_REPLICATION
- if (thd->slave_thread &&
- (!rpl_filter->db_ok(lex->name.str) ||
- !rpl_filter->db_ok_with_wild_table(lex->name.str)))
+ if (thd->slave_thread)
{
- my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
- break;
+ rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter;
+ if (!rpl_filter->db_ok(lex->name.str) ||
+ !rpl_filter->db_ok_with_wild_table(lex->name.str))
+ {
+ my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
+ break;
+ }
+ if (!thd->slave_expected_error &&
+ slave_ddl_exec_mode_options == SLAVE_EXEC_MODE_IDEMPOTENT)
+ lex->check_exists= 1;
}
#endif
if (check_access(thd, DROP_ACL, lex->name.str, NULL, NULL, 1, 0))
break;
- res= mysql_rm_db(thd, lex->name.str, lex->drop_if_exists, 0);
+ res= mysql_rm_db(thd, lex->name.str, lex->check_exists, 0);
break;
}
case SQLCOM_ALTER_DB_UPGRADE:
{
LEX_STRING *db= & lex->name;
#ifdef HAVE_REPLICATION
- if (thd->slave_thread &&
- (!rpl_filter->db_ok(db->str) ||
- !rpl_filter->db_ok_with_wild_table(db->str)))
+ if (thd->slave_thread)
{
- res= 1;
- my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
- break;
+ rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter;
+ if (!rpl_filter->db_ok(db->str) ||
+ !rpl_filter->db_ok_with_wild_table(db->str))
+ {
+ res= 1;
+ my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
+ break;
+ }
}
#endif
if (check_db_name(db))
@@ -3432,12 +3982,15 @@ end_with_restore_list:
above was not called. So we have to check rules again here.
*/
#ifdef HAVE_REPLICATION
- if (thd->slave_thread &&
- (!rpl_filter->db_ok(db->str) ||
- !rpl_filter->db_ok_with_wild_table(db->str)))
+ if (thd->slave_thread)
{
- my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
- break;
+ rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter;
+ if (!rpl_filter->db_ok(db->str) ||
+ !rpl_filter->db_ok_with_wild_table(db->str))
+ {
+ my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
+ break;
+ }
}
#endif
if (check_access(thd, ALTER_ACL, db->str, NULL, NULL, 1, 0))
@@ -3447,14 +4000,20 @@ end_with_restore_list:
}
case SQLCOM_SHOW_CREATE_DB:
{
+ char db_name_buff[NAME_LEN+1];
+ LEX_STRING db_name;
DBUG_EXECUTE_IF("4x_server_emul",
my_error(ER_UNKNOWN_ERROR, MYF(0)); goto error;);
- if (check_db_name(&lex->name))
+
+ db_name.str= db_name_buff;
+ db_name.length= lex->name.length;
+ strmov(db_name.str, lex->name.str);
+ if (check_db_name(&db_name))
{
- my_error(ER_WRONG_DB_NAME, MYF(0), lex->name.str);
+ my_error(ER_WRONG_DB_NAME, MYF(0), db_name.str);
break;
}
- res= mysqld_show_create_db(thd, lex->name.str, &lex->create_info);
+ res= mysqld_show_create_db(thd, &db_name, &lex->name, &lex->create_info);
break;
}
case SQLCOM_CREATE_EVENT:
@@ -3510,7 +4069,7 @@ end_with_restore_list:
case SQLCOM_DROP_EVENT:
if (!(res= Events::drop_event(thd,
lex->spname->m_db, lex->spname->m_name,
- lex->drop_if_exists)))
+ lex->check_exists)))
my_ok(thd);
break;
#else
@@ -3532,22 +4091,26 @@ end_with_restore_list:
}
#ifndef NO_EMBEDDED_ACCESS_CHECKS
case SQLCOM_CREATE_USER:
+ case SQLCOM_CREATE_ROLE:
{
if (check_access(thd, INSERT_ACL, "mysql", NULL, NULL, 1, 1) &&
check_global_access(thd,CREATE_USER_ACL))
break;
/* Conditionally writes to binlog */
- if (!(res= mysql_create_user(thd, lex->users_list)))
+ if (!(res= mysql_create_user(thd, lex->users_list,
+ lex->sql_command == SQLCOM_CREATE_ROLE)))
my_ok(thd);
break;
}
case SQLCOM_DROP_USER:
+ case SQLCOM_DROP_ROLE:
{
if (check_access(thd, DELETE_ACL, "mysql", NULL, NULL, 1, 1) &&
check_global_access(thd,CREATE_USER_ACL))
break;
/* Conditionally writes to binlog */
- if (!(res= mysql_drop_user(thd, lex->users_list)))
+ if (!(res= mysql_drop_user(thd, lex->users_list,
+ lex->sql_command == SQLCOM_DROP_ROLE)))
my_ok(thd);
break;
}
@@ -3567,9 +4130,6 @@ end_with_restore_list:
check_global_access(thd,CREATE_USER_ACL))
break;
- /* Replicate current user as grantor */
- thd->binlog_invoker();
-
/* Conditionally writes to binlog */
if (!(res = mysql_revoke_all(thd, lex->users_list)))
my_ok(thd);
@@ -3587,42 +4147,58 @@ end_with_restore_list:
goto error;
/* Replicate current user as grantor */
- thd->binlog_invoker();
+ thd->binlog_invoker(false);
if (thd->security_ctx->user) // If not replication
{
- LEX_USER *user, *tmp_user;
+ LEX_USER *user;
bool first_user= TRUE;
List_iterator <LEX_USER> user_list(lex->users_list);
- while ((tmp_user= user_list++))
+ while ((user= user_list++))
{
- if (!(user= get_current_user(thd, tmp_user)))
- goto error;
if (specialflag & SPECIAL_NO_RESOLVE &&
hostname_requires_resolving(user->host.str))
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARN_HOSTNAME_WONT_WORK,
ER(ER_WARN_HOSTNAME_WONT_WORK));
- // Are we trying to change a password of another user
- DBUG_ASSERT(user->host.str != 0);
/*
GRANT/REVOKE PROXY has the target user as a first entry in the list.
*/
if (lex->type == TYPE_ENUM_PROXY && first_user)
{
+ if (!(user= get_current_user(thd, user)) || !user->host.str)
+ goto error;
+
first_user= FALSE;
if (acl_check_proxy_grant_access (thd, user->host.str, user->user.str,
lex->grant & GRANT_ACL))
goto error;
}
- else if (is_acl_user(user->host.str, user->user.str) &&
- user->password.str &&
- check_change_password (thd, user->host.str, user->user.str,
- user->password.str,
- user->password.length))
- goto error;
+ else if (user->password.str)
+ {
+ // Are we trying to change a password of another user?
+ const char *hostname= user->host.str, *username=user->user.str;
+ bool userok;
+ if (username == current_user.str)
+ {
+ username= thd->security_ctx->priv_user;
+ hostname= thd->security_ctx->priv_host;
+ userok= true;
+ }
+ else
+ {
+ if (!hostname)
+ hostname= host_not_specified.str;
+ userok= is_acl_user(hostname, username);
+ }
+
+ if (userok && check_change_password (thd, hostname, username,
+ user->password.str,
+ user->password.length))
+ goto error;
+ }
}
}
if (first_table)
@@ -3666,9 +4242,9 @@ end_with_restore_list:
else
{
/* Conditionally writes to binlog */
- res = mysql_grant(thd, select_lex->db, lex->users_list, lex->grant,
- lex->sql_command == SQLCOM_REVOKE,
- lex->type == TYPE_ENUM_PROXY);
+ res= mysql_grant(thd, select_lex->db, lex->users_list, lex->grant,
+ lex->sql_command == SQLCOM_REVOKE,
+ lex->type == TYPE_ENUM_PROXY);
}
if (!res)
{
@@ -3687,6 +4263,14 @@ end_with_restore_list:
}
break;
}
+ case SQLCOM_REVOKE_ROLE:
+ case SQLCOM_GRANT_ROLE:
+ {
+ if (!(res= mysql_grant_role(thd, lex->users_list,
+ lex->sql_command != SQLCOM_GRANT_ROLE)))
+ my_ok(thd);
+ break;
+ }
#endif /*!NO_EMBEDDED_ACCESS_CHECKS*/
case SQLCOM_RESET:
/*
@@ -3701,18 +4285,33 @@ end_with_restore_list:
if (check_global_access(thd,RELOAD_ACL))
goto error;
- if (first_table && lex->type & REFRESH_READ_LOCK)
+ if (first_table && lex->type & (REFRESH_READ_LOCK|REFRESH_FOR_EXPORT))
{
/* 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);
break;
}
+#ifdef HAVE_REPLICATION
+ if (lex->type & REFRESH_READ_LOCK)
+ {
+ /*
+ We need to pause any parallel replication slave workers during FLUSH
+ TABLES WITH READ LOCK. Otherwise we might cause a deadlock, as
+ worker threads eun run in arbitrary order but need to commit in a
+ specific given order.
+ */
+ if (rpl_pause_for_ftwrl(thd))
+ goto error;
+ }
+#endif
/*
reload_acl_and_cache() will tell us if we are allowed to write to the
binlog or not.
@@ -3738,11 +4337,17 @@ end_with_restore_list:
reload_acl_and_cache binlog interactions failed
*/
res= 1;
- }
+ }
if (!res)
my_ok(thd);
}
+ else
+ res= 1; // reload_acl_and_cache failed
+#ifdef HAVE_REPLICATION
+ if (lex->type & REFRESH_READ_LOCK)
+ rpl_unpause_after_ftwrl(thd);
+#endif
break;
}
@@ -3755,7 +4360,7 @@ end_with_restore_list:
break;
}
- if (lex->kill_type == KILL_TYPE_ID)
+ if (lex->kill_type == KILL_TYPE_ID || lex->kill_type == KILL_TYPE_QUERY)
{
Item *it= (Item *)lex->value_list.head();
if ((!it->fixed && it->fix_fields(lex->thd, &it)) || it->check_cols(1))
@@ -3764,25 +4369,32 @@ end_with_restore_list:
MYF(0));
goto error;
}
- sql_kill(thd, (ulong) it->val_int(), lex->kill_signal);
+ sql_kill(thd, it->val_int(), lex->kill_signal, lex->kill_type);
}
else
sql_kill_user(thd, get_current_user(thd, lex->users_list.head()),
lex->kill_signal);
break;
}
+ case SQLCOM_SHUTDOWN:
+#ifndef EMBEDDED_LIBRARY
+ if (check_global_access(thd,SHUTDOWN_ACL))
+ goto error;
+ kill_mysql();
+ my_ok(thd);
+#else
+ my_error(ER_NOT_SUPPORTED_YET, MYF(0), "embedded server");
+#endif
+ break;
+
#ifndef NO_EMBEDDED_ACCESS_CHECKS
case SQLCOM_SHOW_GRANTS:
{
- LEX_USER *grant_user= get_current_user(thd, lex->grant_user);
+ LEX_USER *grant_user= lex->grant_user;
if (!grant_user)
goto error;
- if ((thd->security_ctx->priv_user &&
- !strcmp(thd->security_ctx->priv_user, grant_user->user.str)) ||
- !check_access(thd, SELECT_ACL, "mysql", NULL, NULL, 1, 0))
- {
- res = mysql_show_grants(thd, grant_user);
- }
+
+ res = mysql_show_grants(thd, grant_user);
break;
}
#endif
@@ -3790,6 +4402,9 @@ end_with_restore_list:
DBUG_ASSERT(first_table == all_tables && first_table != 0);
if (check_table_access(thd, SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE))
goto error;
+ /* Close temporary tables which were pre-opened for privilege checking. */
+ close_thread_tables(thd);
+ all_tables->table= NULL;
res= mysql_ha_open(thd, first_table, 0);
break;
case SQLCOM_HA_CLOSE:
@@ -3810,6 +4425,7 @@ end_with_restore_list:
break;
case SQLCOM_BEGIN:
+ DBUG_PRINT("info", ("Executing SQLCOM_BEGIN thd: %p", thd));
if (trans_begin(thd, lex->start_transaction_opt))
goto error;
my_ok(thd);
@@ -3831,12 +4447,13 @@ end_with_restore_list:
if (tx_chain)
{
if (trans_begin(thd))
- goto error;
+ goto error;
}
else
{
- /* Reset the isolation level if no chaining transaction. */
+ /* Reset the isolation level and access mode if no chaining transaction.*/
thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation;
+ thd->tx_read_only= thd->variables.tx_read_only;
}
/* Disconnect the current client connection. */
if (tx_release)
@@ -3857,6 +4474,7 @@ end_with_restore_list:
bool tx_release= (lex->tx_release == TVL_YES ||
(thd->variables.completion_type == 2 &&
lex->tx_release != TVL_NO));
+
if (trans_rollback(thd))
goto error;
thd->mdl_context.release_transactional_locks();
@@ -3868,8 +4486,9 @@ end_with_restore_list:
}
else
{
- /* Reset the isolation level if no chaining transaction. */
+ /* Reset the isolation level and access mode if no chaining transaction.*/
thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation;
+ thd->tx_read_only= thd->variables.tx_read_only;
}
/* Disconnect the current client connection. */
if (tx_release)
@@ -3911,6 +4530,10 @@ end_with_restore_list:
goto create_sp_error;
}
+ if (check_access(thd, CREATE_PROC_ACL, lex->sphead->m_db.str,
+ NULL, NULL, 0, 0))
+ goto create_sp_error;
+
/*
Check that a database directory with this name
exists. Design note: This won't work on virtual databases
@@ -3922,10 +4545,6 @@ end_with_restore_list:
goto create_sp_error;
}
- if (check_access(thd, CREATE_PROC_ACL, lex->sphead->m_db.str,
- NULL, NULL, 0, 0))
- goto create_sp_error;
-
name= lex->sphead->name(&namelen);
#ifdef HAVE_DLOPEN
if (lex->sphead->m_type == TYPE_ENUM_FUNCTION)
@@ -3995,7 +4614,7 @@ end_with_restore_list:
{
if (sp_grant_privileges(thd, lex->sphead->m_db.str, name,
lex->sql_command == SQLCOM_CREATE_PROCEDURE))
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
ER_PROC_AUTO_GRANT_FAIL, ER(ER_PROC_AUTO_GRANT_FAIL));
thd->clear_error();
}
@@ -4051,6 +4670,10 @@ create_sp_error:
open_and_lock_tables(thd, all_tables, TRUE, 0))
goto error;
+ if (check_routine_access(thd, EXECUTE_ACL, lex->spname->m_db.str,
+ lex->spname->m_name.str, TRUE, FALSE))
+ goto error;
+
/*
By this moment all needed SPs should be in cache so no need to look
into DB.
@@ -4101,11 +4724,6 @@ create_sp_error:
SERVER_MORE_RESULTS_EXISTS);
thd->server_status|= SERVER_MORE_RESULTS_EXISTS;
- if (check_routine_access(thd, EXECUTE_ACL,
- sp->m_db.str, sp->m_name.str, TRUE, FALSE))
- {
- goto error;
- }
select_limit= thd->variables.select_limit;
thd->variables.select_limit= HA_POS_ERROR;
@@ -4199,9 +4817,9 @@ create_sp_error:
if (lex->spname->m_db.str == NULL)
{
- if (lex->drop_if_exists)
+ if (lex->check_exists)
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST),
"FUNCTION (UDF)", lex->spname->m_name.str);
res= FALSE;
@@ -4254,7 +4872,7 @@ create_sp_error:
sp_revoke_privileges(thd, db, name,
lex->sql_command == SQLCOM_DROP_PROCEDURE))
{
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
ER_PROC_AUTO_REVOKE_FAIL,
ER(ER_PROC_AUTO_REVOKE_FAIL));
/* If this happens, an error should have been reported. */
@@ -4268,10 +4886,10 @@ create_sp_error:
my_ok(thd);
break;
case SP_KEY_NOT_FOUND:
- if (lex->drop_if_exists)
+ if (lex->check_exists)
{
res= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST),
SP_COM_STRING(lex), lex->spname->m_qname.str);
if (!res)
@@ -4385,9 +5003,10 @@ create_sp_error:
thd->mdl_context.release_transactional_locks();
/*
We've just done a commit, reset transaction
- isolation level to the session default.
+ isolation level and access mode to the session default.
*/
thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation;
+ thd->tx_read_only= thd->variables.tx_read_only;
my_ok(thd);
break;
case SQLCOM_XA_ROLLBACK:
@@ -4396,9 +5015,10 @@ create_sp_error:
thd->mdl_context.release_transactional_locks();
/*
We've just done a rollback, reset transaction
- isolation level to the session default.
+ isolation level and access mode to the session default.
*/
thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation;
+ thd->tx_read_only= thd->variables.tx_read_only;
my_ok(thd);
break;
case SQLCOM_XA_RECOVER:
@@ -4478,7 +5098,7 @@ create_sp_error:
if ((err_code= drop_server(thd, &lex->server_options)))
{
- if (! lex->drop_if_exists && err_code == ER_FOREIGN_SERVER_DOESNT_EXIST)
+ if (! lex->check_exists && err_code == ER_FOREIGN_SERVER_DOESNT_EXIST)
{
DBUG_PRINT("info", ("problem dropping server %s",
lex->server_options.server_name));
@@ -4504,32 +5124,21 @@ create_sp_error:
/* fall through */
case SQLCOM_SIGNAL:
case SQLCOM_RESIGNAL:
- DBUG_ASSERT(lex->m_stmt != NULL);
- res= lex->m_stmt->execute(thd);
+ case SQLCOM_GET_DIAGNOSTICS:
+ DBUG_ASSERT(lex->m_sql_cmd != NULL);
+ res= lex->m_sql_cmd->execute(thd);
break;
default:
+
#ifndef EMBEDDED_LIBRARY
DBUG_ASSERT(0); /* Impossible */
#endif
my_ok(thd);
break;
}
- thd_proc_info(thd, "query end");
+ THD_STAGE_INFO(thd, stage_query_end);
thd->update_stats();
- /*
- Binlog-related cleanup:
- Reset system variables temporarily modified by SET ONE SHOT.
-
- Exception: If this is a SET, do nothing. This is to allow
- mysqlbinlog to print many SET commands (in this case we want the
- charset temp setting to live until the real query). This is also
- needed so that SET CHARACTER_SET_CLIENT... does not cancel itself
- immediately.
- */
- if (thd->one_shot_set && lex->sql_command != SQLCOM_SET_OPTION)
- reset_one_shot_variables(thd);
-
goto finish;
error:
@@ -4542,6 +5151,10 @@ finish:
lex->unit.cleanup();
+ /* close/reopen tables that were marked to need reopen under LOCK TABLES */
+ if (! thd->lex->requires_prelocking())
+ thd->locked_tables_list.reopen_tables(thd, true);
+
if (! thd->in_sub_stmt)
{
if (thd->killed != NOT_KILLED)
@@ -4550,23 +5163,19 @@ finish:
if (thd->killed_errno())
{
/* If we already sent 'ok', we can ignore any kill query statements */
- if (! thd->stmt_da->is_set())
+ if (! thd->get_stmt_da()->is_set())
thd->send_kill_message();
}
- if (thd->killed < KILL_CONNECTION)
- {
- thd->reset_killed();
- thd->mysys_var->abort= 0;
- }
+ thd->reset_kill_query();
}
if (thd->is_error() || (thd->variables.option_bits & OPTION_MASTER_SQL_ERROR))
trans_rollback_stmt(thd);
else
{
/* If commit fails, we should be able to reset the OK status. */
- thd->stmt_da->can_overwrite_status= TRUE;
+ thd->get_stmt_da()->set_overwrite_status(true);
trans_commit_stmt(thd);
- thd->stmt_da->can_overwrite_status= FALSE;
+ thd->get_stmt_da()->set_overwrite_status(false);
}
#ifdef WITH_ARIA_STORAGE_ENGINE
ha_maria::implicit_commit(thd, FALSE);
@@ -4575,12 +5184,16 @@ finish:
/* Free tables */
close_thread_tables(thd);
- thd_proc_info(thd, 0);
#ifndef DBUG_OFF
if (lex->sql_command != SQLCOM_SET_OPTION && ! thd->in_sub_stmt)
DEBUG_SYNC(thd, "execute_command_after_close_tables");
#endif
+ if (!(sql_command_flags[lex->sql_command] &
+ (CF_CAN_GENERATE_ROW_EVENTS | CF_FORCE_ORIGINAL_BINLOG_FORMAT |
+ CF_STATUS_COMMAND)))
+ thd->set_binlog_format(orig_binlog_format,
+ orig_current_stmt_binlog_format);
if (! thd->in_sub_stmt && thd->transaction_rollback_request)
{
@@ -4596,12 +5209,15 @@ finish:
{
/* No transaction control allowed in sub-statements. */
DBUG_ASSERT(! thd->in_sub_stmt);
- /* If commit fails, we should be able to reset the OK status. */
- thd->stmt_da->can_overwrite_status= TRUE;
- /* Commit the normal transaction if one is active. */
- trans_commit_implicit(thd);
- thd->stmt_da->can_overwrite_status= FALSE;
- thd->mdl_context.release_transactional_locks();
+ if (!(thd->variables.option_bits & OPTION_GTID_BEGIN))
+ {
+ /* If commit fails, we should be able to reset the OK status. */
+ thd->get_stmt_da()->set_overwrite_status(true);
+ /* Commit the normal transaction if one is active. */
+ trans_commit_implicit(thd);
+ thd->get_stmt_da()->set_overwrite_status(false);
+ thd->mdl_context.release_transactional_locks();
+ }
}
else if (! thd->in_sub_stmt && ! thd->in_multi_stmt_transaction_mode())
{
@@ -4651,24 +5267,37 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables)
if (!(result= new select_send()))
return 1; /* purecov: inspected */
thd->send_explain_fields(result);
- res= mysql_explain_union(thd, &thd->lex->unit, result);
+
/*
- The code which prints the extended description is not robust
- against malformed queries, so skip it if we have an error.
+ This will call optimize() for all parts of query. The query plan is
+ printed out below.
*/
- if (!res && (lex->describe & DESCRIBE_EXTENDED))
+ res= mysql_explain_union(thd, &thd->lex->unit, result);
+
+ /* Print EXPLAIN only if we don't have an error */
+ if (!res)
{
- char buff[1024];
- String str(buff,(uint32) sizeof(buff), system_charset_info);
- str.length(0);
- /*
- The warnings system requires input in utf8, @see
- mysqld_show_warnings().
- */
- thd->lex->unit.print(&str, QT_TO_SYSTEM_CHARSET);
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
- ER_YES, str.c_ptr_safe());
+ /*
+ Do like the original select_describe did: remove OFFSET from the
+ top-level LIMIT
+ */
+ result->reset_offset_limit();
+ thd->lex->explain->print_explain(result, thd->lex->describe);
+ if (lex->describe & DESCRIBE_EXTENDED)
+ {
+ char buff[1024];
+ String str(buff,(uint32) sizeof(buff), system_charset_info);
+ str.length(0);
+ /*
+ The warnings system requires input in utf8, @see
+ mysqld_show_warnings().
+ */
+ thd->lex->unit.print(&str, QT_TO_SYSTEM_CHARSET);
+ push_warning(thd, Sql_condition::WARN_LEVEL_NOTE,
+ ER_YES, str.c_ptr_safe());
+ }
}
+
if (res)
result->abort_result_set();
else
@@ -4686,10 +5315,10 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables)
}
}
/* Count number of empty select queries */
- if (!thd->sent_row_count)
+ if (!thd->get_sent_row_count())
status_var_increment(thd->status_var.empty_queries);
else
- status_var_add(thd->status_var.rows_sent, thd->sent_row_count);
+ status_var_add(thd->status_var.rows_sent, thd->get_sent_row_count());
return res;
}
@@ -4712,12 +5341,67 @@ static bool execute_show_status(THD *thd, TABLE_LIST *all_tables)
mysql_mutex_lock(&LOCK_status);
add_diff_to_status(&global_status_var, &thd->status_var,
&old_status_var);
- thd->status_var= old_status_var;
+ memcpy(&thd->status_var, &old_status_var,
+ offsetof(STATUS_VAR, last_cleared_system_status_var));
mysql_mutex_unlock(&LOCK_status);
return res;
}
+/*
+ Find out if a table is a temporary table
+
+ A table is a temporary table if it's a temporary table or
+ there has been before a temporary table that has been renamed
+ to the current name.
+
+ Some examples:
+ A->B B is a temporary table if and only if A is a temp.
+ A->B, B->C Second B is temp if A is temp
+ A->B, A->C Second A can't be temp as if A was temp then B is temp
+ and Second A can only be a normal table. C is also not temp
+*/
+
+static TABLE *find_temporary_table_for_rename(THD *thd,
+ TABLE_LIST *first_table,
+ TABLE_LIST *cur_table)
+{
+ TABLE_LIST *table;
+ TABLE *res= 0;
+ bool found= 0;
+ DBUG_ENTER("find_temporary_table_for_rename");
+
+ /* Find last instance when cur_table is in TO part */
+ for (table= first_table;
+ table != cur_table;
+ table= table->next_local->next_local)
+ {
+ TABLE_LIST *next= table->next_local;
+
+ if (!strcmp(table->get_db_name(), cur_table->get_db_name()) &&
+ !strcmp(table->get_table_name(), cur_table->get_table_name()))
+ {
+ /* Table was moved away, can't be same as 'table' */
+ found= 1;
+ res= 0; // Table can't be a temporary table
+ }
+ if (!strcmp(next->get_db_name(), cur_table->get_db_name()) &&
+ !strcmp(next->get_table_name(), cur_table->get_table_name()))
+ {
+ /*
+ Table has matching name with new name of this table. cur_table should
+ have same temporary type as this table.
+ */
+ found= 1;
+ res= table->table;
+ }
+ }
+ if (!found)
+ res= find_temporary_table(thd, table);
+ DBUG_RETURN(res);
+}
+
+
static bool execute_rename_table(THD *thd, TABLE_LIST *first_table,
TABLE_LIST *all_tables)
{
@@ -4734,13 +5418,19 @@ static bool execute_rename_table(THD *thd, TABLE_LIST *first_table,
&table->next_local->grant.m_internal,
0, 0))
return 1;
+
+ /* check if these are referring to temporary tables */
+ table->table= find_temporary_table_for_rename(thd, first_table, table);
+ table->next_local->table= table->table;
+
TABLE_LIST old_list, new_list;
/*
we do not need initialize old_list and new_list because we will
- come table[0] and table->next[0] there
+ copy table[0] and table->next[0] there
*/
old_list= table[0];
new_list= table->next_local[0];
+
if (check_grant(thd, ALTER_ACL | DROP_ACL, &old_list, FALSE, 1, FALSE) ||
(!test_all_bits(table->next_local->grant.privilege,
INSERT_ACL | CREATE_ACL) &&
@@ -4753,100 +5443,6 @@ static bool execute_rename_table(THD *thd, TABLE_LIST *first_table,
}
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
-/**
- Check grants for commands which work only with one table.
-
- @param thd Thread handler
- @param privilege requested privilege
- @param all_tables global table list of query
- @param no_errors FALSE/TRUE - report/don't report error to
- the client (using my_error() call).
-
- @retval
- 0 OK
- @retval
- 1 access denied, error is sent to client
-*/
-
-bool check_single_table_access(THD *thd, ulong privilege,
- TABLE_LIST *all_tables, bool no_errors)
-{
- Security_context * backup_ctx= thd->security_ctx;
-
- /* we need to switch to the saved context (if any) */
- if (all_tables->security_ctx)
- thd->security_ctx= all_tables->security_ctx;
-
- const char *db_name;
- if ((all_tables->view || all_tables->field_translation) &&
- !all_tables->schema_table)
- db_name= all_tables->view_db.str;
- else
- db_name= all_tables->db;
-
- if (check_access(thd, privilege, db_name,
- &all_tables->grant.privilege,
- &all_tables->grant.m_internal,
- 0, no_errors))
- goto deny;
-
- /* Show only 1 table for check_grant */
- if (!(all_tables->belong_to_view &&
- (thd->lex->sql_command == SQLCOM_SHOW_FIELDS)) &&
- check_grant(thd, privilege, all_tables, FALSE, 1, no_errors))
- goto deny;
-
- thd->security_ctx= backup_ctx;
- return 0;
-
-deny:
- thd->security_ctx= backup_ctx;
- return 1;
-}
-
-/**
- Check grants for commands which work only with one table and all other
- tables belonging to subselects or implicitly opened tables.
-
- @param thd Thread handler
- @param privilege requested privilege
- @param all_tables global table list of query
-
- @retval
- 0 OK
- @retval
- 1 access denied, error is sent to client
-*/
-
-bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *all_tables)
-{
- if (check_single_table_access (thd,privilege,all_tables, FALSE))
- return 1;
-
- /* Check rights on tables of subselects and implictly opened tables */
- TABLE_LIST *subselects_tables, *view= all_tables->view ? all_tables : 0;
- if ((subselects_tables= all_tables->next_global))
- {
- /*
- Access rights asked for the first table of a view should be the same
- as for the view
- */
- if (view && subselects_tables->belong_to_view == view)
- {
- if (check_single_table_access (thd, privilege, subselects_tables, FALSE))
- return 1;
- subselects_tables= subselects_tables->next_global;
- }
- if (subselects_tables &&
- (check_table_access(thd, SELECT_ACL, subselects_tables, FALSE,
- UINT_MAX, FALSE)))
- return 1;
- }
- return 0;
-}
-
-
/**
@brief Compare requested privileges with the privileges acquired from the
User- and Db-tables.
@@ -4879,6 +5475,11 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
GRANT_INTERNAL_INFO *grant_internal_info,
bool dont_check_global_grants, bool no_errors)
{
+#ifdef NO_EMBEDDED_ACCESS_CHECKS
+ if (save_priv)
+ *save_priv= GLOBAL_ACLS;
+ return false;
+#else
Security_context *sctx= thd->security_ctx;
ulong db_access;
@@ -4905,7 +5506,7 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
dummy= 0;
}
- thd_proc_info(thd, "checking permissions");
+ THD_STAGE_INFO(thd, stage_checking_permissions);
if ((!db || !db[0]) && !thd->db && !dont_check_global_grants)
{
DBUG_PRINT("error",("No database"));
@@ -4917,6 +5518,10 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
if ((db != NULL) && (db != any_db))
{
+ /*
+ Check if this is reserved database, like information schema or
+ performance schema
+ */
const ACL_internal_schema_access *access;
access= get_cached_schema_access(grant_internal_info, db);
if (access)
@@ -4959,8 +5564,12 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
if (!(sctx->master_access & SELECT_ACL))
{
if (db && (!thd->db || db_is_pattern || strcmp(db, thd->db)))
+ {
db_access= acl_get(sctx->host, sctx->ip, sctx->priv_user, db,
db_is_pattern);
+ if (sctx->priv_role[0])
+ db_access|= acl_get("", "", sctx->priv_role, db, db_is_pattern);
+ }
else
{
/* get access for current db */
@@ -5004,8 +5613,14 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
}
if (db && (!thd->db || db_is_pattern || strcmp(db,thd->db)))
+ {
db_access= acl_get(sctx->host, sctx->ip, sctx->priv_user, db,
db_is_pattern);
+ if (sctx->priv_role[0])
+ {
+ db_access|= acl_get("", "", sctx->priv_role, db, db_is_pattern);
+ }
+ }
else
db_access= sctx->db_access;
DBUG_PRINT("info",("db_access: %lu want_access: %lu",
@@ -5057,6 +5672,101 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
"unknown")));
}
DBUG_RETURN(TRUE);
+#endif // NO_EMBEDDED_ACCESS_CHECKS
+}
+
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+/**
+ Check grants for commands which work only with one table.
+
+ @param thd Thread handler
+ @param privilege requested privilege
+ @param all_tables global table list of query
+ @param no_errors FALSE/TRUE - report/don't report error to
+ the client (using my_error() call).
+
+ @retval
+ 0 OK
+ @retval
+ 1 access denied, error is sent to client
+*/
+
+bool check_single_table_access(THD *thd, ulong privilege,
+ TABLE_LIST *all_tables, bool no_errors)
+{
+ Security_context * backup_ctx= thd->security_ctx;
+
+ /* we need to switch to the saved context (if any) */
+ if (all_tables->security_ctx)
+ thd->security_ctx= all_tables->security_ctx;
+
+ const char *db_name;
+ if ((all_tables->view || all_tables->field_translation) &&
+ !all_tables->schema_table)
+ db_name= all_tables->view_db.str;
+ else
+ db_name= all_tables->db;
+
+ if (check_access(thd, privilege, db_name,
+ &all_tables->grant.privilege,
+ &all_tables->grant.m_internal,
+ 0, no_errors))
+ goto deny;
+
+ /* Show only 1 table for check_grant */
+ if (!(all_tables->belong_to_view &&
+ (thd->lex->sql_command == SQLCOM_SHOW_FIELDS)) &&
+ check_grant(thd, privilege, all_tables, FALSE, 1, no_errors))
+ goto deny;
+
+ thd->security_ctx= backup_ctx;
+ return 0;
+
+deny:
+ thd->security_ctx= backup_ctx;
+ return 1;
+}
+
+/**
+ Check grants for commands which work only with one table and all other
+ tables belonging to subselects or implicitly opened tables.
+
+ @param thd Thread handler
+ @param privilege requested privilege
+ @param all_tables global table list of query
+
+ @retval
+ 0 OK
+ @retval
+ 1 access denied, error is sent to client
+*/
+
+bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *all_tables)
+{
+ if (check_single_table_access (thd,privilege,all_tables, FALSE))
+ return 1;
+
+ /* Check rights on tables of subselects and implictly opened tables */
+ TABLE_LIST *subselects_tables, *view= all_tables->view ? all_tables : 0;
+ if ((subselects_tables= all_tables->next_global))
+ {
+ /*
+ Access rights asked for the first table of a view should be the same
+ as for the view
+ */
+ if (view && subselects_tables->belong_to_view == view)
+ {
+ if (check_single_table_access (thd, privilege, subselects_tables, FALSE))
+ return 1;
+ subselects_tables= subselects_tables->next_global;
+ }
+ if (subselects_tables &&
+ (check_table_access(thd, SELECT_ACL, subselects_tables, FALSE,
+ UINT_MAX, FALSE)))
+ return 1;
+ }
+ return 0;
}
@@ -5114,6 +5824,12 @@ static bool check_show_access(THD *thd, TABLE_LIST *table)
DBUG_ASSERT(dst_table);
+ /*
+ Open temporary tables to be able to detect them during privilege check.
+ */
+ if (open_temporary_tables(thd, dst_table))
+ return TRUE;
+
if (check_access(thd, SELECT_ACL, dst_table->db,
&dst_table->grant.privilege,
&dst_table->grant.m_internal,
@@ -5127,6 +5843,9 @@ static bool check_show_access(THD *thd, TABLE_LIST *table)
if (check_grant(thd, SELECT_ACL, dst_table, TRUE, UINT_MAX, FALSE))
return TRUE; /* Access denied */
+ close_thread_tables(thd);
+ dst_table->table= NULL;
+
/* Access granted */
return FALSE;
}
@@ -5215,10 +5934,10 @@ check_table_access(THD *thd, ulong requirements,TABLE_LIST *tables,
DBUG_PRINT("info", ("derived: %d view: %d", table_ref->derived != 0,
table_ref->view != 0));
- if (table_ref->is_anonymous_derived_table() ||
- (table_ref->table && table_ref->table->s &&
- (int)table_ref->table->s->tmp_table))
+
+ if (table_ref->is_anonymous_derived_table())
continue;
+
thd->security_ctx= sctx;
if (check_access(thd, want_access, table_ref->get_db_name(),
@@ -5412,8 +6131,8 @@ bool check_fk_parent_table_access(THD *thd,
bool is_qualified_table_name;
Foreign_key *fk_key= (Foreign_key *)key;
LEX_STRING db_name;
- LEX_STRING table_name= { fk_key->ref_table->table.str,
- fk_key->ref_table->table.length };
+ LEX_STRING table_name= { fk_key->ref_table.str,
+ fk_key->ref_table.length };
const ulong privileges= (SELECT_ACL | INSERT_ACL | UPDATE_ACL |
DELETE_ACL | REFERENCES_ACL);
@@ -5425,15 +6144,15 @@ bool check_fk_parent_table_access(THD *thd,
return true;
}
- if (fk_key->ref_table->db.str)
+ if (fk_key->ref_db.str)
{
is_qualified_table_name= true;
- db_name.str= (char *) thd->memdup(fk_key->ref_table->db.str,
- fk_key->ref_table->db.length+1);
- db_name.length= fk_key->ref_table->db.length;
+ db_name.str= (char *) thd->memdup(fk_key->ref_db.str,
+ fk_key->ref_db.length+1);
+ db_name.length= fk_key->ref_db.length;
// Check if database name is valid or not.
- if (fk_key->ref_table->db.str && check_db_name(&db_name))
+ if (fk_key->ref_db.str && check_db_name(&db_name))
{
my_error(ER_WRONG_DB_NAME, MYF(0), db_name.str);
return true;
@@ -5465,9 +6184,10 @@ bool check_fk_parent_table_access(THD *thd,
// if lower_case_table_names is set then convert tablename to lower case.
if (lower_case_table_names)
{
- table_name.str= (char *) thd->memdup(fk_key->ref_table->table.str,
- fk_key->ref_table->table.length+1);
+ table_name.str= (char *) thd->memdup(fk_key->ref_table.str,
+ fk_key->ref_table.length+1);
table_name.length= my_casedn_str(files_charset_info, table_name.str);
+ db_name.length = my_casedn_str(files_charset_info, db_name.str);
}
parent_table.init_one_table(db_name.str, db_name.length,
@@ -5514,12 +6234,6 @@ bool check_fk_parent_table_access(THD *thd,
****************************************************************************/
-#if STACK_DIRECTION < 0
-#define used_stack(A,B) (long) (A - B)
-#else
-#define used_stack(A,B) (long) (B - A)
-#endif
-
#ifndef DBUG_OFF
long max_stack_used;
#endif
@@ -5536,9 +6250,10 @@ bool check_stack_overrun(THD *thd, long margin,
{
long stack_used;
DBUG_ASSERT(thd == current_thd);
- if ((stack_used=used_stack(thd->thread_stack,(char*) &stack_used)) >=
+ if ((stack_used= available_stack_size(thd->thread_stack, &stack_used)) >=
(long) (my_thread_stack_size - margin))
{
+ thd->is_fatal_error= 1;
/*
Do not use stack for the message buffer to ensure correct
behaviour in cases we have close to no stack left.
@@ -5553,7 +6268,7 @@ bool check_stack_overrun(THD *thd, long margin,
return 1;
}
#ifndef DBUG_OFF
- max_stack_used= max(max_stack_used, stack_used);
+ max_stack_used= MY_MAX(max_stack_used, stack_used);
#endif
return 0;
}
@@ -5621,7 +6336,6 @@ void THD::reset_for_next_command()
DBUG_ENTER("THD::reset_for_next_command");
DBUG_ASSERT(!thd->spcont); /* not for substatements of routines */
DBUG_ASSERT(! thd->in_sub_stmt);
- DBUG_ASSERT(thd->transaction.on);
thd->free_list= 0;
thd->select_number= 1;
/*
@@ -5634,6 +6348,8 @@ void THD::reset_for_next_command()
thd->query_start_used= 0;
thd->query_start_sec_part_used= 0;
thd->is_fatal_error= thd->time_zone_used= 0;
+ thd->log_current_statement= 0;
+
/*
Clear the status flag that are expected to be cleared at the
beginning of each SQL statement.
@@ -5658,10 +6374,10 @@ void THD::reset_for_next_command()
thd->user_var_events_alloc= thd->mem_root;
}
thd->clear_error();
- thd->stmt_da->reset_diagnostics_area();
- thd->warning_info->reset_for_next_command();
+ thd->get_stmt_da()->reset_diagnostics_area();
+ thd->get_stmt_da()->reset_for_next_command();
thd->rand_used= 0;
- thd->sent_row_count= thd->examined_row_count= 0;
+ thd->m_sent_row_count= thd->m_examined_row_count= 0;
thd->accessed_rows_and_keys= 0;
thd->query_plan_flags= QPLAN_INIT;
@@ -5880,10 +6596,14 @@ void mysql_parse(THD *thd, char *rawbuf, uint length,
{
LEX *lex= thd->lex;
- bool err= parse_sql(thd, parser_state, NULL);
+ bool err= parse_sql(thd, parser_state, NULL, true);
if (!err)
{
+ thd->m_statement_psi=
+ MYSQL_REFINE_STATEMENT(thd->m_statement_psi,
+ sql_statement_info[thd->lex->sql_command].
+ m_key);
#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (mqh_used && thd->user_connect &&
check_mqh(thd, lex->sql_command))
@@ -5932,13 +6652,17 @@ void mysql_parse(THD *thd, char *rawbuf, uint length,
}
else
{
+ /* Instrument this broken statement as "statement/sql/error" */
+ thd->m_statement_psi=
+ MYSQL_REFINE_STATEMENT(thd->m_statement_psi,
+ sql_statement_info[SQLCOM_END].m_key);
DBUG_ASSERT(thd->is_error());
DBUG_PRINT("info",("Command aborted. Fatal_error: %d",
thd->is_fatal_error));
query_cache_abort(&thd->query_cache_tls);
}
- thd_proc_info(thd, "freeing items");
+ THD_STAGE_INFO(thd, stage_freeing_items);
sp_cache_enforce_limit(thd->sp_proc_cache, stored_program_cache_size);
sp_cache_enforce_limit(thd->sp_func_cache, stored_program_cache_size);
thd->end_statement();
@@ -5949,6 +6673,9 @@ void mysql_parse(THD *thd, char *rawbuf, uint length,
{
/* Update statistics for getting the query from the cache */
thd->lex->sql_command= SQLCOM_SELECT;
+ thd->m_statement_psi=
+ MYSQL_REFINE_STATEMENT(thd->m_statement_psi,
+ sql_statement_info[SQLCOM_SELECT].m_key);
status_var_increment(thd->status_var.com_stat[SQLCOM_SELECT]);
thd->update_stats();
}
@@ -5979,7 +6706,7 @@ bool mysql_test_parse_for_slave(THD *thd, char *rawbuf, uint length)
lex_start(thd);
mysql_reset_thd_for_next_command(thd);
- if (!parse_sql(thd, & parser_state, NULL) &&
+ if (!parse_sql(thd, & parser_state, NULL, true) &&
all_tables_not_ok(thd, lex->select_lex.table_list.first))
error= 1; /* Ignore question */
thd->end_statement();
@@ -6011,6 +6738,7 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type,
{
register Create_field *new_field;
LEX *lex= thd->lex;
+ uint8 datetime_precision= length ? atoi(length) : 0;
DBUG_ENTER("add_field_to_list");
if (check_ident_length(field_name))
@@ -6022,7 +6750,7 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type,
lex->col_list.push_back(new Key_part_spec(*field_name, 0));
key= new Key(Key::PRIMARY, null_lex_str,
&default_key_create_info,
- 0, lex->col_list, NULL);
+ 0, lex->col_list, NULL, lex->check_exists);
lex->alter_info.key_list.push_back(key);
lex->col_list.empty();
}
@@ -6032,7 +6760,7 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type,
lex->col_list.push_back(new Key_part_spec(*field_name, 0));
key= new Key(Key::UNIQUE, null_lex_str,
&default_key_create_info, 0,
- lex->col_list, NULL);
+ lex->col_list, NULL, lex->check_exists);
lex->alter_info.key_list.push_back(key);
lex->col_list.empty();
}
@@ -6044,11 +6772,13 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type,
no need fix_fields()
We allow only one function as part of default value -
- NOW() as default for TIMESTAMP type.
+ NOW() as default for TIMESTAMP and DATETIME type.
*/
if (default_value->type() == Item::FUNC_ITEM &&
- !(((Item_func*)default_value)->functype() == Item_func::NOW_FUNC &&
- type == MYSQL_TYPE_TIMESTAMP))
+ (static_cast<Item_func*>(default_value)->functype() !=
+ Item_func::NOW_FUNC ||
+ (mysql_type_to_time_type(type) != MYSQL_TIMESTAMP_DATETIME) ||
+ default_value->decimals < datetime_precision))
{
my_error(ER_INVALID_DEFAULT, MYF(0), field_name->str);
DBUG_RETURN(1);
@@ -6070,7 +6800,9 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type,
}
}
- if (on_update_value && type != MYSQL_TYPE_TIMESTAMP)
+ if (on_update_value &&
+ (mysql_type_to_time_type(type) != MYSQL_TIMESTAMP_DATETIME ||
+ on_update_value->decimals < datetime_precision))
{
my_error(ER_INVALID_ON_UPDATE, MYF(0), field_name->str);
DBUG_RETURN(1);
@@ -6080,7 +6812,7 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type,
new_field->init(thd, field_name->str, type, length, decimals, type_modifier,
default_value, on_update_value, comment, change,
interval_list, cs, uint_geom_type, vcol_info,
- create_options))
+ create_options, lex->check_exists))
DBUG_RETURN(1);
lex->alter_info.create_list.push_back(new_field);
@@ -6129,6 +6861,7 @@ bool add_to_list(THD *thd, SQL_I_List<ORDER> &list, Item *item,bool asc)
order->free_me=0;
order->used=0;
order->counter_used= 0;
+ order->fast_field_copier_setup= 0;
list.link_in_list(order, &order->next);
DBUG_RETURN(0);
}
@@ -6161,6 +6894,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
thr_lock_type lock_type,
enum_mdl_type mdl_type,
List<Index_hint> *index_hints_arg,
+ List<String> *partition_names,
LEX_STRING *option)
{
register TABLE_LIST *ptr;
@@ -6173,7 +6907,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
if (!table)
DBUG_RETURN(0); // End of memory
alias_str= alias ? alias->str : table->table.str;
- if (!test(table_options & TL_OPTION_ALIAS) &&
+ if (!MY_TEST(table_options & TL_OPTION_ALIAS) &&
check_table_name(table->table.str, table->table.length, FALSE))
{
my_error(ER_WRONG_TABLE_NAME, MYF(0), table->table.str);
@@ -6213,15 +6947,21 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
ptr->alias= alias_str;
ptr->is_alias= alias ? TRUE : FALSE;
- if (lower_case_table_names && table->table.length)
- table->table.length= my_casedn_str(files_charset_info, table->table.str);
+ if (lower_case_table_names)
+ {
+ if (table->table.length)
+ table->table.length= my_casedn_str(files_charset_info, table->table.str);
+ if (ptr->db_length && ptr->db != any_db)
+ ptr->db_length= my_casedn_str(files_charset_info, ptr->db);
+ }
+
ptr->table_name=table->table.str;
ptr->table_name_length=table->table.length;
ptr->lock_type= lock_type;
- ptr->updating= test(table_options & TL_OPTION_UPDATING);
+ ptr->updating= MY_TEST(table_options & TL_OPTION_UPDATING);
/* TODO: remove TL_OPTION_FORCE_INDEX as it looks like it's not used */
- ptr->force_index= test(table_options & TL_OPTION_FORCE_INDEX);
- ptr->ignore_leaves= test(table_options & TL_OPTION_IGNORE_LEAVES);
+ ptr->force_index= MY_TEST(table_options & TL_OPTION_FORCE_INDEX);
+ ptr->ignore_leaves= MY_TEST(table_options & TL_OPTION_IGNORE_LEAVES);
ptr->derived= table->sel;
if (!ptr->derived && is_infoschema_db(ptr->db, ptr->db_length))
{
@@ -6309,11 +7049,14 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
*/
table_list.link_in_list(ptr, &ptr->next_local);
ptr->next_name_resolution_table= NULL;
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ ptr->partition_names= partition_names;
+#endif /* WITH_PARTITION_STORAGE_ENGINE */
/* Link table in global list (all used tables) */
lex->add_to_query_tables(ptr);
// Pure table aliases do not need to be locked:
- if (!test(table_options & TL_OPTION_ALIAS))
+ if (!MY_TEST(table_options & TL_OPTION_ALIAS))
{
ptr->mdl_request.init(MDL_key::TABLE, ptr->db, ptr->table_name, mdl_type,
MDL_TRANSACTION);
@@ -6924,7 +7667,7 @@ bool st_select_lex_unit::add_fake_select_lex(THD *thd_arg)
@retval
FALSE if all is OK
@retval
- TRUE if a memory allocation error occured
+ TRUE if a memory allocation error occurred
*/
bool
@@ -7042,37 +7785,56 @@ void add_join_natural(TABLE_LIST *a, TABLE_LIST *b, List<String> *using_fields,
/**
- kill on thread.
+ Find a thread by id and return it, locking it LOCK_thd_data
- @param thd Thread class
- @param id Thread id
- @param only_kill_query Should it kill the query or the connection
+ @param id Identifier of the thread we're looking for
+ @param query_id If true, search by query_id instead of thread_id
- @note
- This is written such that we have a short lock on LOCK_thread_count
+ @return NULL - not found
+ pointer - thread found, and its LOCK_thd_data is locked.
*/
-uint kill_one_thread(THD *thd, ulong id, killed_state kill_signal)
+THD *find_thread_by_id(longlong id, bool query_id)
{
THD *tmp;
- uint error=ER_NO_SUCH_THREAD;
- DBUG_ENTER("kill_one_thread");
- DBUG_PRINT("enter", ("id: %lu signal: %u", id, (uint) kill_signal));
-
mysql_mutex_lock(&LOCK_thread_count); // For unlink from list
I_List_iterator<THD> it(threads);
while ((tmp=it++))
{
- if (tmp->command == COM_DAEMON)
+ if (tmp->get_command() == COM_DAEMON)
continue;
- if (tmp->thread_id == id)
+ if (id == (query_id ? tmp->query_id : (longlong) tmp->thread_id))
{
mysql_mutex_lock(&tmp->LOCK_thd_data); // Lock from delete
break;
}
}
mysql_mutex_unlock(&LOCK_thread_count);
- if (tmp)
+ return tmp;
+}
+
+
+/**
+ kill one thread.
+
+ @param thd Thread class
+ @param id Thread id or query id
+ @param kill_signal Should it kill the query or the connection
+ @param type Type of id: thread id or query id
+
+ @note
+ This is written such that we have a short lock on LOCK_thread_count
+*/
+
+uint
+kill_one_thread(THD *thd, longlong id, killed_state kill_signal, killed_type type)
+{
+ THD *tmp;
+ uint error= (type == KILL_TYPE_QUERY ? ER_NO_SUCH_QUERY : ER_NO_SUCH_THREAD);
+ DBUG_ENTER("kill_one_thread");
+ DBUG_PRINT("enter", ("id: %lld signal: %u", id, (uint) kill_signal));
+
+ if (id && (tmp= find_thread_by_id(id, type == KILL_TYPE_QUERY)))
{
/*
If we're SUPER, we can KILL anything, including system-threads.
@@ -7160,7 +7922,7 @@ static uint kill_threads_for_user(THD *thd, LEX_USER *user,
mysql_mutex_unlock(&LOCK_thread_count);
DBUG_RETURN(ER_KILL_DENIED_ERROR);
}
- if (!threads_to_kill.push_back(tmp, tmp->mem_root))
+ if (!threads_to_kill.push_back(tmp, thd->mem_root))
mysql_mutex_lock(&tmp->LOCK_thd_data); // Lock from delete
}
}
@@ -7190,21 +7952,20 @@ static uint kill_threads_for_user(THD *thd, LEX_USER *user,
}
-/*
- kills a thread and sends response
+/**
+ kills a thread and sends response.
- SYNOPSIS
- sql_kill()
- thd Thread class
- id Thread id
- only_kill_query Should it kill the query or the connection
+ @param thd Thread class
+ @param id Thread id or query id
+ @param state Should it kill the query or the connection
+ @param type Type of id: thread id or query id
*/
static
-void sql_kill(THD *thd, ulong id, killed_state state)
+void sql_kill(THD *thd, longlong id, killed_state state, killed_type type)
{
uint error;
- if (!(error= kill_one_thread(thd, id, state)))
+ if (!(error= kill_one_thread(thd, id, state, type)))
{
if ((!thd->killed))
my_ok(thd);
@@ -7279,7 +8040,7 @@ bool check_simple_select()
char command[80];
Lex_input_stream *lip= & thd->m_parser_state->m_lip;
strmake(command, lip->yylval->symbol.str,
- min(lip->yylval->symbol.length, sizeof(command)-1));
+ MY_MIN(lip->yylval->symbol.length, sizeof(command)-1));
my_error(ER_CANT_USE_OPTION_HERE, MYF(0), command);
return 1;
}
@@ -7385,6 +8146,8 @@ bool multi_update_precheck(THD *thd, TABLE_LIST *tables)
*/
for (table= tables; table; table= table->next_local)
{
+ if (table->is_jtbm())
+ continue;
if (table->derived)
table->grant.privilege= SELECT_ACL;
else if ((check_access(thd, UPDATE_ACL, table->db,
@@ -7399,6 +8162,7 @@ bool multi_update_precheck(THD *thd, TABLE_LIST *tables)
check_grant(thd, SELECT_ACL, table, FALSE, 1, FALSE)))
DBUG_RETURN(TRUE);
+ table->grant.orig_want_privilege= 0;
table->table_in_first_from_clause= 1;
}
/*
@@ -7452,6 +8216,19 @@ bool multi_delete_precheck(THD *thd, TABLE_LIST *tables)
TABLE_LIST **save_query_tables_own_last= thd->lex->query_tables_own_last;
DBUG_ENTER("multi_delete_precheck");
+ /*
+ Temporary tables are pre-opened in 'tables' list only. Here we need to
+ initialize TABLE instances in 'aux_tables' list.
+ */
+ for (TABLE_LIST *tl= aux_tables; tl; tl= tl->next_global)
+ {
+ if (tl->table)
+ continue;
+
+ if (tl->correspondent_table)
+ tl->table= tl->correspondent_table->table;
+ }
+
/* sql_yacc guarantees that tables and aux_tables are not zero */
DBUG_ASSERT(aux_tables != 0);
if (check_table_access(thd, SELECT_ACL, tables, FALSE, UINT_MAX, FALSE))
@@ -7674,7 +8451,7 @@ void create_table_set_open_action_and_adjust_tables(LEX *lex)
{
TABLE_LIST *create_table= lex->query_tables;
- if (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)
+ if (lex->create_info.tmp_table())
create_table->open_type= OT_TEMPORARY_ONLY;
else
create_table->open_type= OT_BASE_ONLY;
@@ -7720,10 +8497,14 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables,
CREATE TABLE ... SELECT, also require INSERT.
*/
- want_priv= ((lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) ?
- CREATE_TMP_ACL : CREATE_ACL) |
- (select_lex->item_list.elements ? INSERT_ACL : 0);
+ want_priv= lex->create_info.tmp_table() ? CREATE_TMP_ACL :
+ (CREATE_ACL | (select_lex->item_list.elements ? INSERT_ACL : 0));
+ /* CREATE OR REPLACE on not temporary tables require DROP_ACL */
+ if ((lex->create_info.options & HA_LEX_CREATE_REPLACE) &&
+ !lex->create_info.tmp_table())
+ want_priv|= DROP_ACL;
+
if (check_access(thd, want_priv, create_table->db,
&create_table->grant.privilege,
&create_table->grant.m_internal,
@@ -7731,11 +8512,48 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables,
goto err;
/* If it is a merge table, check privileges for merge children. */
- if (lex->create_info.merge_list.first &&
- check_table_access(thd, SELECT_ACL | UPDATE_ACL | DELETE_ACL,
- lex->create_info.merge_list.first,
- FALSE, UINT_MAX, FALSE))
- goto err;
+ if (lex->create_info.merge_list.first)
+ {
+ /*
+ The user must have (SELECT_ACL | UPDATE_ACL | DELETE_ACL) on the
+ underlying base tables, even if there are temporary tables with the same
+ names.
+
+ From user's point of view, it might look as if the user must have these
+ privileges on temporary tables to create a merge table over them. This is
+ one of two cases when a set of privileges is required for operations on
+ temporary tables (see also CREATE TABLE).
+
+ The reason for this behavior stems from the following facts:
+
+ - For merge tables, the underlying table privileges are checked only
+ at CREATE TABLE / ALTER TABLE time.
+
+ In other words, once a merge table is created, the privileges of
+ the underlying tables can be revoked, but the user will still have
+ access to the merge table (provided that the user has privileges on
+ the merge table itself).
+
+ - Temporary tables shadow base tables.
+
+ I.e. there might be temporary and base tables with the same name, and
+ the temporary table takes the precedence in all operations.
+
+ - For temporary MERGE tables we do not track if their child tables are
+ base or temporary. As result we can't guarantee that privilege check
+ which was done in presence of temporary child will stay relevant
+ later as this temporary table might be removed.
+
+ If SELECT_ACL | UPDATE_ACL | DELETE_ACL privileges were not checked for
+ the underlying *base* tables, it would create a security breach as in
+ Bug#12771903.
+ */
+
+ if (check_table_access(thd, SELECT_ACL | UPDATE_ACL | DELETE_ACL,
+ lex->create_info.merge_list.first,
+ FALSE, UINT_MAX, FALSE))
+ goto err;
+ }
if (want_priv != CREATE_TMP_ACL &&
check_grant(thd, want_priv, create_table, FALSE, 1, FALSE))
@@ -7758,12 +8576,42 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables,
goto err;
error= FALSE;
+
err:
DBUG_RETURN(error);
}
/**
+ Check privileges for LOCK TABLES statement.
+
+ @param thd Thread context.
+ @param tables List of tables to be locked.
+
+ @retval FALSE - Success.
+ @retval TRUE - Failure.
+*/
+
+static bool lock_tables_precheck(THD *thd, TABLE_LIST *tables)
+{
+ TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table();
+
+ for (TABLE_LIST *table= tables; table != first_not_own_table && table;
+ table= table->next_global)
+ {
+ if (is_temporary_table(table))
+ continue;
+
+ if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, table,
+ FALSE, 1, FALSE))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/**
negate given expression.
@param thd thread handler
@@ -7804,16 +8652,23 @@ Item *negate_expression(THD *thd, Item *expr)
@param[out] definer definer
*/
-void get_default_definer(THD *thd, LEX_USER *definer)
+void get_default_definer(THD *thd, LEX_USER *definer, bool role)
{
const Security_context *sctx= thd->security_ctx;
- definer->user.str= (char *) sctx->priv_user;
+ if (role)
+ {
+ definer->user.str= const_cast<char*>(sctx->priv_role);
+ definer->host= empty_lex_str;
+ }
+ else
+ {
+ definer->user.str= const_cast<char*>(sctx->priv_user);
+ definer->host.str= const_cast<char*>(sctx->priv_host);
+ definer->host.length= strlen(definer->host.str);
+ }
definer->user.length= strlen(definer->user.str);
- definer->host.str= (char *) sctx->priv_host;
- definer->host.length= strlen(definer->host.str);
-
definer->password= null_lex_str;
definer->plugin= empty_lex_str;
definer->auth= empty_lex_str;
@@ -7831,16 +8686,22 @@ void get_default_definer(THD *thd, LEX_USER *definer)
- On error, return 0.
*/
-LEX_USER *create_default_definer(THD *thd)
+LEX_USER *create_default_definer(THD *thd, bool role)
{
LEX_USER *definer;
if (! (definer= (LEX_USER*) thd->alloc(sizeof(LEX_USER))))
return 0;
- thd->get_definer(definer);
+ thd->get_definer(definer, role);
- return definer;
+ if (role && definer->user.length == 0)
+ {
+ my_error(ER_MALFORMED_DEFINER, MYF(0));
+ return 0;
+ }
+ else
+ return definer;
}
@@ -7876,27 +8737,6 @@ LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name)
/**
- Retuns information about user or current user.
-
- @param[in] thd thread handler
- @param[in] user user
-
- @return
- - On success, return a valid pointer to initialized
- LEX_USER, which contains user information.
- - On error, return 0.
-*/
-
-LEX_USER *get_current_user(THD *thd, LEX_USER *user)
-{
- if (!user->user.str) // current_user
- return create_default_definer(thd);
-
- return user;
-}
-
-
-/**
Check that byte length of a string does not exceed some limit.
@param str string to be checked
@@ -8043,6 +8883,22 @@ int test_if_data_home_dir(const char *dir)
}
+int error_if_data_home_dir(const char *path, const char *what)
+{
+ size_t dirlen;
+ char dirpath[FN_REFLEN];
+ if (path)
+ {
+ dirname_part(dirpath, path, &dirlen);
+ if (test_if_data_home_dir(dirpath))
+ {
+ my_error(ER_WRONG_ARGUMENTS, MYF(0), what);
+ return 1;
+ }
+ }
+ return 0;
+}
+
/**
Check that host name string is valid.
@@ -8092,14 +8948,13 @@ extern int MYSQLparse(THD *thd); // from sql_yacc.cc
@retval TRUE on parsing error.
*/
-bool parse_sql(THD *thd,
- Parser_state *parser_state,
- Object_creation_ctx *creation_ctx)
+bool parse_sql(THD *thd, Parser_state *parser_state,
+ Object_creation_ctx *creation_ctx, bool do_pfs_digest)
{
bool ret_value;
DBUG_ENTER("parse_sql");
DBUG_ASSERT(thd->m_parser_state == NULL);
- DBUG_ASSERT(thd->lex->m_stmt == NULL);
+ DBUG_ASSERT(thd->lex->m_sql_cmd == NULL);
MYSQL_QUERY_PARSE_START(thd->query());
/* Backup creation context. */
@@ -8113,6 +8968,28 @@ bool parse_sql(THD *thd,
thd->m_parser_state= parser_state;
+ parser_state->m_digest_psi= NULL;
+ parser_state->m_lip.m_digest= NULL;
+
+ if (do_pfs_digest)
+ {
+ /* Start Digest */
+ parser_state->m_digest_psi= MYSQL_DIGEST_START(thd->m_statement_psi);
+
+ if (parser_state->m_input.m_compute_digest ||
+ (parser_state->m_digest_psi != NULL))
+ {
+ /*
+ If either:
+ - the caller wants to compute a digest
+ - the performance schema wants to compute a digest
+ set the digest listener in the lexer.
+ */
+ parser_state->m_lip.m_digest= thd->m_digest;
+ parser_state->m_lip.m_digest->m_digest_storage.m_charset_number= thd->charset()->number;
+ }
+ }
+
/* Parse the query. */
bool mysql_parse_status= MYSQLparse(thd) != 0;
@@ -8144,6 +9021,18 @@ bool parse_sql(THD *thd,
/* That's it. */
ret_value= mysql_parse_status || thd->is_fatal_error;
+
+ if ((ret_value == 0) && (parser_state->m_digest_psi != NULL))
+ {
+ /*
+ On parsing success, record the digest in the performance schema.
+ */
+ DBUG_ASSERT(do_pfs_digest);
+ DBUG_ASSERT(thd->m_digest != NULL);
+ MYSQL_DIGEST_END(parser_state->m_digest_psi,
+ & thd->m_digest->m_digest_storage);
+ }
+
MYSQL_QUERY_PARSE_DONE(ret_value);
DBUG_RETURN(ret_value);
}