diff options
Diffstat (limited to 'sql/sql_class.h')
-rw-r--r-- | sql/sql_class.h | 2136 |
1 files changed, 1672 insertions, 464 deletions
diff --git a/sql/sql_class.h b/sql/sql_class.h index bff7492ffec..2517f5cc06f 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -15,105 +15,136 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ - #ifndef SQL_CLASS_INCLUDED #define SQL_CLASS_INCLUDED /* Classes in mysql */ -#ifdef USE_PRAGMA_INTERFACE -#pragma interface /* gcc class implementation */ -#endif - #include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */ -#ifdef MYSQL_SERVER -#include "unireg.h" // REQUIRED: for other includes -#endif #include <waiting_threads.h> #include "sql_const.h" #include <mysql/plugin_audit.h> #include "log.h" #include "rpl_tblmap.h" #include "mdl.h" +#include "field.h" // Create_field #include "probes_mysql.h" -#include "sql_locale.h" /* my_locale_st */ -#include "sql_profile.h" /* PROFILING */ -#include "scheduler.h" /* thd_scheduler */ -#include "protocol.h" /* Protocol_text, Protocol_binary */ -#include "violite.h" /* vio_is_connected */ -#include "thr_lock.h" /* thr_lock_type, THR_LOCK_DATA, - THR_LOCK_INFO */ - +#include "sql_locale.h" /* my_locale_st */ +#include "sql_profile.h" /* PROFILING */ +#include "scheduler.h" /* thd_scheduler */ +#include "protocol.h" /* Protocol_text, Protocol_binary */ +#include "violite.h" /* vio_is_connected */ +#include "thr_lock.h" /* thr_lock_type, THR_LOCK_DATA, THR_LOCK_INFO */ +#include "thr_timer.h" + +#include "sql_digest_stream.h" // sql_digest_state + +#include <mysql/psi/mysql_stage.h> +#include <mysql/psi/mysql_statement.h> +#include <mysql/psi/mysql_idle.h> +#include <mysql/psi/mysql_table.h> +#include <mysql_com_server.h> + +extern "C" +void set_thd_stage_info(void *thd, + const PSI_stage_info *new_stage, + PSI_stage_info *old_stage, + const char *calling_func, + const char *calling_file, + const unsigned int calling_line); + +#define THD_STAGE_INFO(thd, stage) \ + (thd)->enter_stage(&stage, __func__, __FILE__, __LINE__) + +#include "my_apc.h" +#include "rpl_gtid.h" +#include "wsrep_mysqld.h" class Reprepare_observer; class Relay_log_info; +struct rpl_group_info; +class Rpl_filter; class Query_log_event; class Load_log_event; -class Slave_log_event; class sp_rcontext; class sp_cache; class Lex_input_stream; class Parser_state; class Rows_log_event; class Sroutine_hash_entry; -class User_level_lock; class user_var_entry; struct Trans_binlog_info; +class rpl_io_thread_info; +class rpl_sql_thread_info; -enum enum_enable_or_disable { LEAVE_AS_IS, ENABLE, DISABLE }; enum enum_ha_read_modes { RFIRST, RNEXT, RPREV, RLAST, RKEY, RNEXT_SAME }; enum enum_duplicates { DUP_ERROR, DUP_REPLACE, DUP_UPDATE }; enum enum_delay_key_write { DELAY_KEY_WRITE_NONE, DELAY_KEY_WRITE_ON, DELAY_KEY_WRITE_ALL }; enum enum_slave_exec_mode { SLAVE_EXEC_MODE_STRICT, SLAVE_EXEC_MODE_IDEMPOTENT, - SLAVE_EXEC_MODE_LAST_BIT}; + SLAVE_EXEC_MODE_LAST_BIT }; +enum enum_slave_run_triggers_for_rbr { SLAVE_RUN_TRIGGERS_FOR_RBR_NO, + SLAVE_RUN_TRIGGERS_FOR_RBR_YES, + SLAVE_RUN_TRIGGERS_FOR_RBR_LOGGING}; enum enum_slave_type_conversions { SLAVE_TYPE_CONVERSIONS_ALL_LOSSY, SLAVE_TYPE_CONVERSIONS_ALL_NON_LOSSY}; enum enum_mark_columns { MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE}; enum enum_filetype { FILETYPE_CSV, FILETYPE_XML }; +enum enum_binlog_row_image { + /** PKE in the before image and changed columns in the after image */ + BINLOG_ROW_IMAGE_MINIMAL= 0, + /** Whenever possible, before and after image contain all columns except blobs. */ + BINLOG_ROW_IMAGE_NOBLOB= 1, + /** All columns in both before and after image. */ + BINLOG_ROW_IMAGE_FULL= 2 +}; + + /* Bits for different SQL modes modes (including ANSI mode) */ -#define MODE_REAL_AS_FLOAT 1 -#define MODE_PIPES_AS_CONCAT 2 -#define MODE_ANSI_QUOTES 4 -#define MODE_IGNORE_SPACE 8 -#define MODE_IGNORE_BAD_TABLE_OPTIONS 16 -#define MODE_ONLY_FULL_GROUP_BY 32 -#define MODE_NO_UNSIGNED_SUBTRACTION 64 -#define MODE_NO_DIR_IN_CREATE 128 -#define MODE_POSTGRESQL 256 -#define MODE_ORACLE 512 -#define MODE_MSSQL 1024 -#define MODE_DB2 2048 -#define MODE_MAXDB 4096 -#define MODE_NO_KEY_OPTIONS 8192 -#define MODE_NO_TABLE_OPTIONS 16384 -#define MODE_NO_FIELD_OPTIONS 32768 -#define MODE_MYSQL323 65536L -#define MODE_MYSQL40 (MODE_MYSQL323*2) -#define MODE_ANSI (MODE_MYSQL40*2) -#define MODE_NO_AUTO_VALUE_ON_ZERO (MODE_ANSI*2) -#define MODE_NO_BACKSLASH_ESCAPES (MODE_NO_AUTO_VALUE_ON_ZERO*2) -#define MODE_STRICT_TRANS_TABLES (MODE_NO_BACKSLASH_ESCAPES*2) -#define MODE_STRICT_ALL_TABLES (MODE_STRICT_TRANS_TABLES*2) -#define MODE_NO_ZERO_IN_DATE (MODE_STRICT_ALL_TABLES*2) -#define MODE_NO_ZERO_DATE (MODE_NO_ZERO_IN_DATE*2) -#define MODE_INVALID_DATES (MODE_NO_ZERO_DATE*2) -#define MODE_ERROR_FOR_DIVISION_BY_ZERO (MODE_INVALID_DATES*2) -#define MODE_TRADITIONAL (MODE_ERROR_FOR_DIVISION_BY_ZERO*2) -#define MODE_NO_AUTO_CREATE_USER (MODE_TRADITIONAL*2) -#define MODE_HIGH_NOT_PRECEDENCE (MODE_NO_AUTO_CREATE_USER*2) -#define MODE_NO_ENGINE_SUBSTITUTION (MODE_HIGH_NOT_PRECEDENCE*2) -#define MODE_PAD_CHAR_TO_FULL_LENGTH (ULL(1) << 31) +#define MODE_REAL_AS_FLOAT (1ULL << 0) +#define MODE_PIPES_AS_CONCAT (1ULL << 1) +#define MODE_ANSI_QUOTES (1ULL << 2) +#define MODE_IGNORE_SPACE (1ULL << 3) +#define MODE_IGNORE_BAD_TABLE_OPTIONS (1ULL << 4) +#define MODE_ONLY_FULL_GROUP_BY (1ULL << 5) +#define MODE_NO_UNSIGNED_SUBTRACTION (1ULL << 6) +#define MODE_NO_DIR_IN_CREATE (1ULL << 7) +#define MODE_POSTGRESQL (1ULL << 8) +#define MODE_ORACLE (1ULL << 9) +#define MODE_MSSQL (1ULL << 10) +#define MODE_DB2 (1ULL << 11) +#define MODE_MAXDB (1ULL << 12) +#define MODE_NO_KEY_OPTIONS (1ULL << 13) +#define MODE_NO_TABLE_OPTIONS (1ULL << 14) +#define MODE_NO_FIELD_OPTIONS (1ULL << 15) +#define MODE_MYSQL323 (1ULL << 16) +#define MODE_MYSQL40 (1ULL << 17) +#define MODE_ANSI (1ULL << 18) +#define MODE_NO_AUTO_VALUE_ON_ZERO (1ULL << 19) +#define MODE_NO_BACKSLASH_ESCAPES (1ULL << 20) +#define MODE_STRICT_TRANS_TABLES (1ULL << 21) +#define MODE_STRICT_ALL_TABLES (1ULL << 22) +#define MODE_NO_ZERO_IN_DATE (1ULL << 23) +#define MODE_NO_ZERO_DATE (1ULL << 24) +#define MODE_INVALID_DATES (1ULL << 25) +#define MODE_ERROR_FOR_DIVISION_BY_ZERO (1ULL << 26) +#define MODE_TRADITIONAL (1ULL << 27) +#define MODE_NO_AUTO_CREATE_USER (1ULL << 28) +#define MODE_HIGH_NOT_PRECEDENCE (1ULL << 29) +#define MODE_NO_ENGINE_SUBSTITUTION (1ULL << 30) +#define MODE_PAD_CHAR_TO_FULL_LENGTH (1ULL << 31) /* Bits for different old style modes */ -#define OLD_MODE_NO_DUP_KEY_WARNINGS_WITH_IGNORE 1 -#define OLD_MODE_NO_PROGRESS_INFO 2 +#define OLD_MODE_NO_DUP_KEY_WARNINGS_WITH_IGNORE (1 << 0) +#define OLD_MODE_NO_PROGRESS_INFO (1 << 1) +#define OLD_MODE_ZERO_DATE_TIME_CAST (1 << 2) extern char internal_table_name[2]; extern char empty_c_string[1]; +extern LEX_STRING EMPTY_STR; extern MYSQL_PLUGIN_IMPORT const char **errmesg; extern bool volatile shutdown_in_progress; @@ -153,9 +184,6 @@ public: }; -#define TC_LOG_PAGE_SIZE 8192 -#define TC_LOG_MIN_SIZE (3*TC_LOG_PAGE_SIZE) - #define TC_HEURISTIC_RECOVER_COMMIT 1 #define TC_HEURISTIC_RECOVER_ROLLBACK 2 extern ulong tc_heuristic_recover; @@ -227,11 +255,15 @@ public: class Alter_drop :public Sql_alloc { public: - enum drop_type {KEY, COLUMN }; + enum drop_type {KEY, COLUMN, FOREIGN_KEY }; const char *name; enum drop_type type; - Alter_drop(enum drop_type par_type,const char *par_name) - :name(par_name), type(par_type) {} + bool drop_if_exists; + Alter_drop(enum drop_type par_type,const char *par_name, bool par_exists) + :name(par_name), type(par_type), drop_if_exists(par_exists) + { + DBUG_ASSERT(par_name != NULL); + } /** Used to make a clone of this object for ALTER/CREATE TABLE @sa comment for Key_part_spec::clone @@ -256,7 +288,7 @@ public: }; -class Key :public Sql_alloc { +class Key :public Sql_alloc, public DDL_options { public: enum Keytype { PRIMARY, UNIQUE, MULTIPLE, FULLTEXT, SPATIAL, FOREIGN_KEY}; enum Keytype type; @@ -267,17 +299,27 @@ public: bool generated; Key(enum Keytype type_par, const LEX_STRING &name_arg, + ha_key_alg algorithm_arg, bool generated_arg, DDL_options_st ddl_options) + :DDL_options(ddl_options), + type(type_par), key_create_info(default_key_create_info), + name(name_arg), option_list(NULL), generated(generated_arg) + { + key_create_info.algorithm= algorithm_arg; + } + Key(enum Keytype type_par, const LEX_STRING &name_arg, KEY_CREATE_INFO *key_info_arg, bool generated_arg, List<Key_part_spec> &cols, - engine_option_value *create_opt) - :type(type_par), key_create_info(*key_info_arg), columns(cols), + engine_option_value *create_opt, DDL_options_st ddl_options) + :DDL_options(ddl_options), + type(type_par), key_create_info(*key_info_arg), columns(cols), name(name_arg), option_list(create_opt), generated(generated_arg) {} Key(enum Keytype type_par, const char *name_arg, size_t name_len_arg, KEY_CREATE_INFO *key_info_arg, bool generated_arg, List<Key_part_spec> &cols, - engine_option_value *create_opt) - :type(type_par), key_create_info(*key_info_arg), columns(cols), + engine_option_value *create_opt, DDL_options_st ddl_options) + :DDL_options(ddl_options), + type(type_par), key_create_info(*key_info_arg), columns(cols), option_list(create_opt), generated(generated_arg) { name.str= (char *)name_arg; @@ -295,27 +337,31 @@ public: { return new (mem_root) Key(*this, mem_root); } }; -class Table_ident; class Foreign_key: public Key { public: enum fk_match_opt { FK_MATCH_UNDEF, FK_MATCH_FULL, FK_MATCH_PARTIAL, FK_MATCH_SIMPLE}; - enum fk_option { FK_OPTION_UNDEF, FK_OPTION_RESTRICT, FK_OPTION_CASCADE, - FK_OPTION_SET_NULL, FK_OPTION_NO_ACTION, FK_OPTION_DEFAULT}; - Table_ident *ref_table; + LEX_STRING ref_db; + LEX_STRING ref_table; List<Key_part_spec> ref_columns; uint delete_opt, update_opt, match_opt; Foreign_key(const LEX_STRING &name_arg, List<Key_part_spec> &cols, - Table_ident *table, List<Key_part_spec> &ref_cols, - uint delete_opt_arg, uint update_opt_arg, uint match_opt_arg) - :Key(FOREIGN_KEY, name_arg, &default_key_create_info, 0, cols, NULL), - ref_table(table), ref_columns(ref_cols), + const LEX_STRING &ref_db_arg, const LEX_STRING &ref_table_arg, + List<Key_part_spec> &ref_cols, + uint delete_opt_arg, uint update_opt_arg, uint match_opt_arg, + DDL_options ddl_options) + :Key(FOREIGN_KEY, name_arg, &default_key_create_info, 0, cols, NULL, + ddl_options), + ref_db(ref_db_arg), ref_table(ref_table_arg), ref_columns(ref_cols), delete_opt(delete_opt_arg), update_opt(update_opt_arg), match_opt(match_opt_arg) - {} - Foreign_key(const Foreign_key &rhs, MEM_ROOT *mem_root); + { + // We don't check for duplicate FKs. + key_create_info.check_for_duplicate_indexes= false; + } + Foreign_key(const Foreign_key &rhs, MEM_ROOT *mem_root); /** Used to make a clone of this object for ALTER/CREATE TABLE @sa comment for Key_part_spec::clone @@ -419,31 +465,32 @@ enum killed_state */ ABORT_QUERY= 6, ABORT_QUERY_HARD= 7, + KILL_TIMEOUT= 8, + KILL_TIMEOUT_HARD= 9, /* All of the following killed states will kill the connection KILL_CONNECTION must be the first of these and it must start with an even number (becasue of HARD bit)! */ - KILL_CONNECTION= 8, - KILL_CONNECTION_HARD= 9, - KILL_SYSTEM_THREAD= 10, - KILL_SYSTEM_THREAD_HARD= 11, - KILL_SERVER= 12, - KILL_SERVER_HARD= 13 + KILL_CONNECTION= 10, + KILL_CONNECTION_HARD= 11, + KILL_SYSTEM_THREAD= 12, + KILL_SYSTEM_THREAD_HARD= 13, + KILL_SERVER= 14, + KILL_SERVER_HARD= 15 }; -extern int killed_errno(killed_state killed); #define killed_mask_hard(killed) ((killed_state) ((killed) & ~KILL_HARD_BIT)) enum killed_type { KILL_TYPE_ID, - KILL_TYPE_USER + KILL_TYPE_USER, + KILL_TYPE_QUERY }; #include "sql_lex.h" /* Must be here */ -extern LEX_STRING sql_statement_names[(uint) SQLCOM_END + 1]; class Delayed_insert; class select_result; class Time_zone; @@ -453,6 +500,8 @@ class Time_zone; #define THD_CHECK_SENTRY(thd) DBUG_ASSERT(thd->dbug_sentry == THD_SENTRY_MAGIC) +typedef ulonglong sql_mode_t; + typedef struct system_variables { /* @@ -475,9 +524,10 @@ typedef struct system_variables ulonglong max_heap_table_size; ulonglong tmp_table_size; ulonglong long_query_time; + ulonglong max_statement_time; ulonglong optimizer_switch; - ulonglong sql_mode; ///< which non-standard SQL behaviour should be enabled - ulonglong old_behavior; ///< which old SQL behaviour should be enabled + sql_mode_t sql_mode; ///< which non-standard SQL behaviour should be enabled + sql_mode_t old_behavior; ///< which old SQL behaviour should be enabled ulonglong option_bits; ///< OPTION_xxx constants, e.g. OPTION_PROFILING ulonglong join_buff_space_limit; ulonglong log_slow_filter; @@ -486,10 +536,30 @@ typedef struct system_variables ulonglong join_buff_size; ulonglong sortbuff_size; ulonglong group_concat_max_len; + ulonglong default_regex_flags; + ulonglong max_mem_used; + + /** + Place holders to store Multi-source variables in sys_var.cc during + update and show of variables. + */ + ulonglong slave_skip_counter; + ulonglong max_relay_log_size; + ha_rows select_limit; ha_rows max_join_size; ha_rows expensive_subquery_limit; ulong auto_increment_increment, auto_increment_offset; +#ifdef WITH_WSREP + /* + Stored values of the auto_increment_increment and auto_increment_offset + that are will be restored when wsrep_auto_increment_control will be set + to 'OFF', because the setting it to 'ON' leads to overwriting of the + original values (which are set by the user) by calculated ones (which + are based on the cluster size): + */ + ulong saved_auto_increment_increment, saved_auto_increment_offset; +#endif /* WITH_WSREP */ ulong lock_wait_timeout; ulong join_cache_level; ulong max_allowed_packet; @@ -508,6 +578,11 @@ typedef struct system_variables ulong net_write_timeout; ulong optimizer_prune_level; ulong optimizer_search_depth; + ulong optimizer_selectivity_sampling_limit; + ulong optimizer_use_condition_selectivity; + ulong use_stat_tables; + ulong histogram_size; + ulong histogram_type; ulong preload_buff_size; ulong profiling_history_size; ulong read_buff_size; @@ -528,24 +603,32 @@ typedef struct system_variables /* Flags for slow log filtering */ ulong log_slow_rate_limit; ulong binlog_format; ///< binlog format for this thd (see enum_binlog_format) + ulong binlog_row_image; ulong progress_report_time; - my_bool binlog_annotate_row_events; - my_bool binlog_direct_non_trans_update; - my_bool sql_log_bin; ulong completion_type; ulong query_cache_type; ulong tx_isolation; ulong updatable_views_with_limit; int max_user_connections; + ulong server_id; /** In slave thread we need to know in behalf of which thread the query is being run to replicate temp tables properly */ my_thread_id pseudo_thread_id; + /** + When replicating an event group with GTID, keep these values around so + slave binlog can receive the same GTID as the original. + */ + uint32 gtid_domain_id; + uint64 gtid_seq_no; + /** + Default transaction access mode. READ ONLY (true) or READ WRITE (false). + */ + my_bool tx_read_only; my_bool low_priority_updates; my_bool query_cache_wlock_invalidate; - my_bool engine_condition_pushdown; my_bool keep_files_on_create; my_bool old_mode; @@ -553,8 +636,19 @@ typedef struct system_variables my_bool old_passwords; my_bool big_tables; my_bool query_cache_strip_comments; + my_bool sql_log_slow; + my_bool sql_log_bin; + /* + A flag to help detect whether binary logging was temporarily disabled + (see tmp_disable_binlog(A) macro). + */ + my_bool sql_log_bin_off; + my_bool binlog_annotate_row_events; + my_bool binlog_direct_non_trans_update; plugin_ref table_plugin; + plugin_ref tmp_table_plugin; + plugin_ref enforced_table_plugin; /* Only charset part of these variables is sensible */ CHARSET_INFO *character_set_filesystem; @@ -566,8 +660,13 @@ typedef struct system_variables CHARSET_INFO *collation_database; CHARSET_INFO *collation_connection; + /* Names. These will be allocated in buffers in thd */ + LEX_STRING default_master_connection; + /* Error messages */ MY_LOCALE *lc_messages; + const char **errmsgs; /* lc_messages->errmsg->errmsgs */ + /* Locale Support */ MY_LOCALE *lc_time_names; @@ -579,7 +678,13 @@ typedef struct system_variables ulong wt_timeout_short, wt_deadlock_search_depth_short; ulong wt_timeout_long, wt_deadlock_search_depth_long; - double long_query_time_double; + my_bool wsrep_on; + my_bool wsrep_causal_reads; + my_bool wsrep_dirty_reads; + uint wsrep_sync_wait; + ulong wsrep_retry_autocommit; + ulong wsrep_OSU_method; + double long_query_time_double, max_statement_time_double; my_bool pseudo_slave_mode; @@ -593,10 +698,22 @@ typedef struct system_variables typedef struct system_status_var { - ulong com_other; ulong com_stat[(uint) SQLCOM_END]; - ulong created_tmp_disk_tables; - ulong created_tmp_tables; + ulong com_create_tmp_table; + ulong com_drop_tmp_table; + ulong com_other; + + ulong com_stmt_prepare; + ulong com_stmt_reprepare; + ulong com_stmt_execute; + ulong com_stmt_send_long_data; + ulong com_stmt_fetch; + ulong com_stmt_reset; + ulong com_stmt_close; + + ulong com_register_slave; + ulong created_tmp_disk_tables_; + ulong created_tmp_tables_; ulong ha_commit_count; ulong ha_delete_count; ulong ha_read_first_count; @@ -604,9 +721,11 @@ typedef struct system_status_var ulong ha_read_key_count; ulong ha_read_next_count; ulong ha_read_prev_count; + ulong ha_read_retry_count; ulong ha_read_rnd_count; ulong ha_read_rnd_next_count; ulong ha_read_rnd_deleted_count; + /* This number doesn't include calls to the default implementation and calls made by range access. The intent is to count only calls made by @@ -628,31 +747,27 @@ typedef struct system_status_var ulong ha_discover_count; ulong ha_savepoint_count; ulong ha_savepoint_rollback_count; + ulong ha_external_lock_count; ulong net_big_packet_count; ulong opened_tables; ulong opened_shares; ulong opened_views; /* +1 opening a view */ - ulong select_full_join_count; - ulong select_full_range_join_count; - ulong select_range_count; - ulong select_range_check_count; - ulong select_scan_count; + ulong select_full_join_count_; + ulong select_full_range_join_count_; + ulong select_range_count_; + ulong select_range_check_count_; + ulong select_scan_count_; + ulong update_scan_count; + ulong delete_scan_count; ulong executed_triggers; ulong long_query_count; - ulong filesort_merge_passes; - ulong filesort_range_count; - ulong filesort_rows; - ulong filesort_scan_count; - /* Prepared statements and binary protocol */ - ulong com_stmt_prepare; - ulong com_stmt_reprepare; - ulong com_stmt_execute; - ulong com_stmt_send_long_data; - ulong com_stmt_fetch; - ulong com_stmt_reset; - ulong com_stmt_close; + ulong filesort_merge_passes_; + ulong filesort_range_count_; + ulong filesort_rows_; + ulong filesort_scan_count_; + ulong filesort_pq_sorts_; /* Features used */ ulong feature_dynamic_columns; /* +1 when creating a dynamic column */ @@ -664,9 +779,15 @@ typedef struct system_status_var ulong feature_trigger; /* +1 opening a table with triggers */ ulong feature_xml; /* +1 when XPATH is used */ + /* From MASTER_GTID_WAIT usage */ + ulonglong master_gtid_wait_timeouts; /* Number of timeouts */ + ulonglong master_gtid_wait_time; /* Time in microseconds */ + ulonglong master_gtid_wait_count; + ulong empty_queries; ulong access_denied_errors; ulong lost_connections; + ulong max_statement_time_exceeded; /* Number of statements sent from the client */ @@ -685,6 +806,11 @@ typedef struct system_status_var ulonglong binlog_bytes_written; double last_query_cost; double cpu_time, busy_time; + /* Don't initialize */ + /* Memory used for thread local storage */ + volatile int64 local_memory_used; + /* Memory allocated for global usage */ + volatile int64 global_memory_used; } STATUS_VAR; /* @@ -694,12 +820,66 @@ typedef struct system_status_var */ #define last_system_status_var questions +#define last_cleared_system_status_var local_memory_used + +/* + Global status variables +*/ + +extern ulong feature_files_opened_with_delayed_keys; + void add_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var); void add_diff_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var, STATUS_VAR *dec_var); +/* + Update global_memory_used. We have to do this with atomic_add as the + global value can change outside of LOCK_status. +*/ +inline void update_global_memory_status(int64 size) +{ + DBUG_PRINT("info", ("global memory_used: %lld size: %lld", + (longlong) global_status_var.global_memory_used, + size)); + // workaround for gcc 4.2.4-1ubuntu4 -fPIE (from DEB_BUILD_HARDENING=1) + int64 volatile * volatile ptr= &global_status_var.global_memory_used; + my_atomic_add64_explicit(ptr, size, MY_MEMORY_ORDER_RELAXED); +} + +/** + Get collation by name, send error to client on failure. + @param name Collation name + @param name_cs Character set of the name string + @return + @retval NULL on error + @retval Pointter to CHARSET_INFO with the given name on success +*/ +inline CHARSET_INFO * +mysqld_collation_get_by_name(const char *name, + CHARSET_INFO *name_cs= system_charset_info) +{ + CHARSET_INFO *cs; + MY_CHARSET_LOADER loader; + my_charset_loader_init_mysys(&loader); + if (!(cs= my_collation_get_by_name(&loader, name, MYF(0)))) + { + ErrConvString err(name, name_cs); + my_error(ER_UNKNOWN_COLLATION, MYF(0), err.ptr()); + if (loader.error[0]) + push_warning_printf(current_thd, + Sql_condition::WARN_LEVEL_WARN, + ER_UNKNOWN_COLLATION, "%s", loader.error); + } + return cs; +} + +inline bool is_supported_parser_charset(CHARSET_INFO *cs) +{ + return MY_TEST(cs->mbminlen == 1); +} + #ifdef MYSQL_SERVER void free_tmp_table(THD *thd, TABLE *entry); @@ -798,6 +978,19 @@ public: }; +class Query_arena_memroot: public Query_arena, public Sql_alloc +{ +public: + Query_arena_memroot(MEM_ROOT *mem_root_arg, enum enum_state state_arg) : + Query_arena(mem_root_arg, state_arg) + {} + Query_arena_memroot() : Query_arena() + {} + + virtual ~Query_arena_memroot() {} +}; + + class Server_side_cursor; /** @@ -983,14 +1176,15 @@ struct st_savepoint { enum xa_states {XA_NOTR=0, XA_ACTIVE, XA_IDLE, XA_PREPARED, XA_ROLLBACK_ONLY}; extern const char *xa_state_names[]; +class XID_cache_element; typedef struct st_xid_state { /* For now, this is only used to catch duplicated external xids */ XID xid; // transaction identifier enum xa_states xa_state; // used by external XA only - bool in_thd; /* Error reported by the Resource Manager (RM) to the Transaction Manager. */ uint rm_error; + XID_cache_element *xid_cache_element; /** Check that XA transaction has an uncommitted work. Report an error @@ -1014,17 +1208,15 @@ typedef struct st_xid_state { } return false; } - } XID_STATE; -extern mysql_mutex_t LOCK_xid_cache; -extern HASH xid_cache; -bool xid_cache_init(void); +void xid_cache_init(void); void xid_cache_free(void); -XID_STATE *xid_cache_search(XID *xid); +XID_STATE *xid_cache_search(THD *thd, XID *xid); bool xid_cache_insert(XID *xid, enum xa_states xa_state); -bool xid_cache_insert(XID_STATE *xid_state); -void xid_cache_delete(XID_STATE *xid_state); +bool xid_cache_insert(THD *thd, XID_STATE *xid_state); +void xid_cache_delete(THD *thd, XID_STATE *xid_state); +int xid_cache_iterate(THD *thd, my_hash_walk_action action, void *argument); /** @class Security_context @@ -1046,6 +1238,8 @@ public: char proxy_user[USERNAME_LENGTH + MAX_HOSTNAME + 5]; /* The host privilege we are using */ char priv_host[MAX_HOSTNAME]; + /* The role privilege we are using */ + char priv_role[USERNAME_LENGTH]; /* The external user (if available) */ char *external_user; /* points to host if host is available, otherwise points to ip */ @@ -1284,9 +1478,11 @@ enum enum_thread_type SYSTEM_THREAD_DELAYED_INSERT= 1, SYSTEM_THREAD_SLAVE_IO= 2, SYSTEM_THREAD_SLAVE_SQL= 4, - SYSTEM_THREAD_NDBCLUSTER_BINLOG= 8, - SYSTEM_THREAD_EVENT_SCHEDULER= 16, - SYSTEM_THREAD_EVENT_WORKER= 32 + SYSTEM_THREAD_EVENT_SCHEDULER= 8, + SYSTEM_THREAD_EVENT_WORKER= 16, + SYSTEM_THREAD_BINLOG_BACKGROUND= 32, + SYSTEM_THREAD_SLAVE_INIT= 64, + SYSTEM_THREAD_SLAVE_BACKGROUND= 128 }; inline char const * @@ -1299,9 +1495,9 @@ show_system_thread(enum_thread_type thread) RETURN_NAME_AS_STRING(SYSTEM_THREAD_DELAYED_INSERT); RETURN_NAME_AS_STRING(SYSTEM_THREAD_SLAVE_IO); RETURN_NAME_AS_STRING(SYSTEM_THREAD_SLAVE_SQL); - RETURN_NAME_AS_STRING(SYSTEM_THREAD_NDBCLUSTER_BINLOG); RETURN_NAME_AS_STRING(SYSTEM_THREAD_EVENT_SCHEDULER); RETURN_NAME_AS_STRING(SYSTEM_THREAD_EVENT_WORKER); + RETURN_NAME_AS_STRING(SYSTEM_THREAD_SLAVE_INIT); default: sprintf(buf, "<UNKNOWN SYSTEM THREAD: %d>", thread); return buf; @@ -1351,9 +1547,9 @@ public: virtual bool handle_condition(THD *thd, uint sql_errno, const char* sqlstate, - MYSQL_ERROR::enum_warning_level level, + Sql_condition::enum_warning_level level, const char* msg, - MYSQL_ERROR ** cond_hdl) = 0; + Sql_condition ** cond_hdl) = 0; private: Internal_error_handler *m_prev_internal_handler; @@ -1372,9 +1568,9 @@ public: bool handle_condition(THD *thd, uint sql_errno, const char* sqlstate, - MYSQL_ERROR::enum_warning_level level, + Sql_condition::enum_warning_level level, const char* msg, - MYSQL_ERROR ** cond_hdl) + Sql_condition ** cond_hdl) { /* Ignore error */ return TRUE; @@ -1384,6 +1580,29 @@ public: /** + Implements the trivial error handler which counts errors as they happen. +*/ + +class Counting_error_handler : public Internal_error_handler +{ +public: + int errors; + bool handle_condition(THD *thd, + uint sql_errno, + const char* sqlstate, + Sql_condition::enum_warning_level level, + const char* msg, + Sql_condition ** cond_hdl) + { + if (level == Sql_condition::WARN_LEVEL_ERROR) + errors++; + return false; + } + Counting_error_handler() : errors(0) {} +}; + + +/** This class is an internal error handler implementation for DROP TABLE statements. The thing is that there may be warnings during execution of these statements, which should not be exposed to the user. @@ -1399,9 +1618,9 @@ public: bool handle_condition(THD *thd, uint sql_errno, const char* sqlstate, - MYSQL_ERROR::enum_warning_level level, + Sql_condition::enum_warning_level level, const char* msg, - MYSQL_ERROR ** cond_hdl); + Sql_condition ** cond_hdl); private: }; @@ -1454,13 +1673,16 @@ public: m_reopen_array(NULL), m_locked_tables_count(0) { - init_sql_alloc(&m_locked_tables_root, MEM_ROOT_BLOCK_SIZE, 0); + init_sql_alloc(&m_locked_tables_root, MEM_ROOT_BLOCK_SIZE, 0, + MYF(MY_THREAD_SPECIFIC)); } void unlock_locked_tables(THD *thd); + void unlock_locked_table(THD *thd, MDL_ticket *mdl_ticket); ~Locked_tables_list() { - unlock_locked_tables(0); + reset(); } + void reset(); bool init_locked_tables(THD *thd); TABLE_LIST *locked_tables() { return m_locked_tables; } void unlink_from_list(THD *thd, TABLE_LIST *table_list, @@ -1468,7 +1690,10 @@ public: void unlink_all_closed_tables(THD *thd, MYSQL_LOCK *lock, size_t reopen_count); - bool reopen_tables(THD *thd); + bool reopen_tables(THD *thd, bool need_reopen); + bool restore_lock(THD *thd, TABLE_LIST *dst_table_list, TABLE *table, + MYSQL_LOCK *lock); + void add_back_last_deleted_lock(TABLE_LIST *dst_table_list); }; @@ -1558,8 +1783,163 @@ private: }; +/* + Class to facilitate the commit of one transactions waiting for the commit of + another transaction to complete first. + + This is used during (parallel) replication, to allow different transactions + to be applied in parallel, but still commit in order. + + The transaction that wants to wait for a prior commit must first register + to wait with register_wait_for_prior_commit(waitee). Such registration + must be done holding the waitee->LOCK_wait_commit, to prevent the other + THD from disappearing during the registration. + + Then during commit, if a THD is registered to wait, it will call + wait_for_prior_commit() as part of ha_commit_trans(). If no wait is + registered, or if the waitee for has already completed commit, then + wait_for_prior_commit() returns immediately. + + And when a THD that may be waited for has completed commit (more precisely + commit_ordered()), then it must call wakeup_subsequent_commits() to wake + up any waiters. Note that this must be done at a point that is guaranteed + to be later than any waiters registering themselves. It is safe to call + wakeup_subsequent_commits() multiple times, as waiters are removed from + registration as part of the wakeup. + + The reason for separate register and wait calls is that this allows to + register the wait early, at a point where the waited-for THD is known to + exist. And then the actual wait can be done much later, where the + waited-for THD may have been long gone. By registering early, the waitee + can signal before disappearing. +*/ +struct wait_for_commit +{ + /* + The LOCK_wait_commit protects the fields subsequent_commits_list and + wakeup_subsequent_commits_running (for a waitee), and the pointer + waiterr and associated COND_wait_commit (for a waiter). + */ + mysql_mutex_t LOCK_wait_commit; + mysql_cond_t COND_wait_commit; + /* List of threads that did register_wait_for_prior_commit() on us. */ + wait_for_commit *subsequent_commits_list; + /* Link field for entries in subsequent_commits_list. */ + wait_for_commit *next_subsequent_commit; + /* + Our waitee, if we did register_wait_for_prior_commit(), and were not + yet woken up. Else NULL. + + When this is cleared for wakeup, the COND_wait_commit condition is + signalled. + */ + wait_for_commit *waitee; + /* + Generic pointer for use by the transaction coordinator to optimise the + waiting for improved group commit. + + Currently used by binlog TC to signal that a waiter is ready to commit, so + that the waitee can grab it and group commit it directly. It is free to be + used by another transaction coordinator for similar purposes. + */ + void *opaque_pointer; + /* The wakeup error code from the waitee. 0 means no error. */ + int wakeup_error; + /* + Flag set when wakeup_subsequent_commits_running() is active, see comments + on that function for details. + */ + bool wakeup_subsequent_commits_running; + /* + This flag can be set when a commit starts, but has not completed yet. + It is used by binlog group commit to allow a waiting transaction T2 to + join the group commit of an earlier transaction T1. When T1 has queued + itself for group commit, it will set the commit_started flag. Then when + T2 becomes ready to commit and needs to wait for T1 to commit first, T2 + can queue itself before waiting, and thereby participate in the same + group commit as T1. + */ + bool commit_started; + + void register_wait_for_prior_commit(wait_for_commit *waitee); + int wait_for_prior_commit(THD *thd) + { + /* + Quick inline check, to avoid function call and locking in the common case + where no wakeup is registered, or a registered wait was already signalled. + */ + if (waitee) + return wait_for_prior_commit2(thd); + else + { + if (wakeup_error) + my_error(ER_PRIOR_COMMIT_FAILED, MYF(0)); + return wakeup_error; + } + } + void wakeup_subsequent_commits(int wakeup_error_arg) + { + /* + Do the check inline, so only the wakeup case takes the cost of a function + call for every commmit. + + Note that the check is done without locking. It is the responsibility of + the user of the wakeup facility to ensure that no waiters can register + themselves after the last call to wakeup_subsequent_commits(). + + This avoids having to take another lock for every commit, which would be + pointless anyway - even if we check under lock, there is nothing to + prevent a waiter from arriving just after releasing the lock. + */ + if (subsequent_commits_list) + wakeup_subsequent_commits2(wakeup_error_arg); + } + void unregister_wait_for_prior_commit() + { + if (waitee) + unregister_wait_for_prior_commit2(); + else + wakeup_error= 0; + } + /* + Remove a waiter from the list in the waitee. Used to unregister a wait. + The caller must be holding the locks of both waiter and waitee. + */ + void remove_from_list(wait_for_commit **next_ptr_ptr) + { + wait_for_commit *cur; + + while ((cur= *next_ptr_ptr) != NULL) + { + if (cur == this) + { + *next_ptr_ptr= this->next_subsequent_commit; + break; + } + next_ptr_ptr= &cur->next_subsequent_commit; + } + waitee= NULL; + } + + void wakeup(int wakeup_error); + + int wait_for_prior_commit2(THD *thd); + void wakeup_subsequent_commits2(int wakeup_error); + void unregister_wait_for_prior_commit2(); + + wait_for_commit(); + ~wait_for_commit(); + void reinit(); +}; + + extern "C" void my_message_sql(uint error, const char *str, myf MyFlags); +class THD; +#ifndef DBUG_OFF +void dbug_serve_apcs(THD *thd, int n_calls); +#endif + /** @class THD For each client connection we create a separate thread with THD serving as @@ -1567,6 +1947,7 @@ extern "C" void my_message_sql(uint error, const char *str, myf MyFlags); */ class THD :public Statement, + public MDL_context_owner, public Open_tables_state { private: @@ -1587,10 +1968,16 @@ public: /* Used to execute base64 coded binlog events in MySQL server */ Relay_log_info* rli_fake; + rpl_group_info* rgi_fake; /* Slave applier execution context */ - Relay_log_info* rli_slave; + rpl_group_info* rgi_slave; + + union { + rpl_io_thread_info *rpl_io_info; + rpl_sql_thread_info *rpl_sql_info; + } system_thread_info; - void reset_for_next_command(); + void reset_for_next_command(bool do_clear_errors= 1); /* Constant for THD::where initialization in the beginning of every query. @@ -1622,6 +2009,8 @@ public: Query_cache_tls query_cache_tls; #endif NET net; // client connection descriptor + /** Aditional network instrumentation for the server only. */ + NET_SERVER m_net_server_extension; scheduler_functions *scheduler; // Scheduler for this connection Protocol *protocol; // Current protocol Protocol_text protocol_text; // Normal protocol @@ -1644,6 +2033,8 @@ public: Is locked when THD is deleted. */ mysql_mutex_t LOCK_thd_data; + /* Protect kill information */ + mysql_mutex_t LOCK_thd_kill; /* all prepared statements and cursors of this connection */ Statement_map stmt_map; @@ -1688,6 +2079,37 @@ public: */ const char *proc_info; +private: + unsigned int m_current_stage_key; + +public: + void enter_stage(const PSI_stage_info *stage, + const char *calling_func, + const char *calling_file, + const unsigned int calling_line) + { + DBUG_PRINT("THD::enter_stage", ("%s:%d", calling_file, calling_line)); + DBUG_ASSERT(stage); + m_current_stage_key= stage->m_key; + proc_info= stage->m_name; +#if defined(ENABLED_PROFILING) + profiling.status_change(stage->m_name, calling_func, calling_file, + calling_line); +#endif +#ifdef HAVE_PSI_THREAD_INTERFACE + MYSQL_SET_STAGE(m_current_stage_key, calling_file, calling_line); +#endif + } + + void backup_stage(PSI_stage_info *stage) + { + stage->m_key= m_current_stage_key; + stage->m_name= proc_info; + } + + const char *get_proc_info() const + { return proc_info; } + /* Used in error messages to tell user in what part of MySQL we found an error. E. g. when where= "having clause", if fix_fields() fails, user @@ -1703,21 +2125,23 @@ public: HASH handler_tables_hash; /* - One thread can hold up to one named user-level lock. This variable - points to a lock object if the lock is present. See item_func.cc and + A thread can hold named user-level locks. This variable + contains granted tickets if a lock is present. See item_func.cc and chapter 'Miscellaneous functions', for functions GET_LOCK, RELEASE_LOCK. */ - User_level_lock *ull; + HASH ull_hash; #ifndef DBUG_OFF uint dbug_sentry; // watch out for memory corruption #endif struct st_my_thread_var *mysys_var; +private: /* Type of current query: COM_STMT_PREPARE, COM_QUERY, etc. Set from first byte of the packet in do_command() */ - enum enum_server_command command; - uint32 server_id; + enum enum_server_command m_command; + +public: uint32 file_id; // for LOAD DATA INFILE /* remote (peer) port */ uint16 peer_port; @@ -1755,7 +2179,10 @@ public: uint in_sub_stmt; /* True when opt_userstat_running is set at start of query */ bool userstat_running; - /* True if we want to log all errors */ + /* + True if we have to log all errors. Are set by some engines to temporary + force errors to the error log. + */ bool log_all_errors; /* Do not set socket timeouts for wait_timeout (used with threadpool) */ @@ -1772,6 +2199,9 @@ public: */ bool create_tmp_table_for_derived; + /* The flag to force reading statistics from EITS tables */ + bool force_read_stats; + bool save_prep_leaf_list; /* container for handler's private per-connection data */ @@ -1788,27 +2218,23 @@ public: int binlog_write_table_map(TABLE *table, bool is_transactional, my_bool *with_annotate= 0); int binlog_write_row(TABLE* table, bool is_transactional, - MY_BITMAP const* cols, size_t colcnt, const uchar *buf); int binlog_delete_row(TABLE* table, bool is_transactional, - MY_BITMAP const* cols, size_t colcnt, const uchar *buf); int binlog_update_row(TABLE* table, bool is_transactional, - MY_BITMAP const* cols, size_t colcnt, const uchar *old_data, const uchar *new_data); + static void binlog_prepare_row_images(TABLE* table); - void set_server_id(uint32 sid) { server_id = sid; } + void set_server_id(uint32 sid) { variables.server_id = sid; } /* Member functions to handle pending event for row-level logging. */ template <class RowsEventT> Rows_log_event* binlog_prepare_pending_rows_event(TABLE* table, uint32 serv_id, - MY_BITMAP const* cols, - size_t colcnt, size_t needed, bool is_transactional, - RowsEventT* hint); + RowsEventT* hint); Rows_log_event* binlog_get_pending_rows_event(bool is_transactional) const; void binlog_set_pending_rows_event(Rows_log_event* ev, bool is_transactional); inline int binlog_flush_pending_rows_event(bool stmt_end) @@ -1845,8 +2271,46 @@ public: !mysql_bin_log.is_open()); } + enum binlog_filter_state + { + BINLOG_FILTER_UNKNOWN, + BINLOG_FILTER_CLEAR, + BINLOG_FILTER_SET + }; + + inline void reset_binlog_local_stmt_filter() + { + m_binlog_filter_state= BINLOG_FILTER_UNKNOWN; + } + + inline void clear_binlog_local_stmt_filter() + { + DBUG_ASSERT(m_binlog_filter_state == BINLOG_FILTER_UNKNOWN); + m_binlog_filter_state= BINLOG_FILTER_CLEAR; + } + + inline void set_binlog_local_stmt_filter() + { + DBUG_ASSERT(m_binlog_filter_state == BINLOG_FILTER_UNKNOWN); + m_binlog_filter_state= BINLOG_FILTER_SET; + } + + inline binlog_filter_state get_binlog_local_stmt_filter() + { + return m_binlog_filter_state; + } + private: /** + Indicate if the current statement should be discarded + instead of written to the binlog. + This is used to discard special statements, such as + DML or DDL that affects only 'local' (non replicated) + tables, such as performance_schema.* + */ + binlog_filter_state m_binlog_filter_state; + + /** Indicates the format in which the current statement will be logged. This can only be set from @c decide_logging_format(). */ @@ -1924,7 +2388,8 @@ public: { bzero((char*)this, sizeof(*this)); xid_state.xid.null(); - init_sql_alloc(&mem_root, ALLOC_ROOT_MIN_BLOCK_SIZE, 0); + init_sql_alloc(&mem_root, ALLOC_ROOT_MIN_BLOCK_SIZE, 0, + MYF(MY_THREAD_SPECIFIC)); } } transaction; Global_read_lock global_read_lock; @@ -2148,11 +2613,12 @@ public: ha_rows cuted_fields; +private: /* number of rows we actually sent to the client, including "synthetic" rows in ROLLUP etc. */ - ha_rows sent_row_count; + ha_rows m_sent_row_count; /** Number of rows read and/or evaluated for a statement. Used for @@ -2164,12 +2630,42 @@ public: statement including ORDER BY could possibly evaluate the row in filesort() before reading it for e.g. update. */ - ha_rows examined_row_count; + ha_rows m_examined_row_count; + +public: + ha_rows get_sent_row_count() const + { return m_sent_row_count; } + + ha_rows get_examined_row_count() const + { return m_examined_row_count; } + + void set_sent_row_count(ha_rows count); + void set_examined_row_count(ha_rows count); + + void inc_sent_row_count(ha_rows count); + void inc_examined_row_count(ha_rows count); + + void inc_status_created_tmp_disk_tables(); + void inc_status_created_tmp_files(); + void inc_status_created_tmp_tables(); + void inc_status_select_full_join(); + void inc_status_select_full_range_join(); + void inc_status_select_range(); + void inc_status_select_range_check(); + void inc_status_select_scan(); + void inc_status_sort_merge_passes(); + void inc_status_sort_range(); + void inc_status_sort_rows(ha_rows count); + void inc_status_sort_scan(); + void set_status_no_index_used(); + void set_status_no_good_index_used(); + /** The number of rows and/or keys examined by the query, both read, changed or written. */ ulonglong accessed_rows_and_keys; + /** Check if the number of rows accessed by a statement exceeded LIMIT ROWS EXAMINED. If so, signal the query engine to stop execution. @@ -2177,17 +2673,35 @@ public: void check_limit_rows_examined() { if (++accessed_rows_and_keys > lex->limit_rows_examined_cnt) - killed= ABORT_QUERY; + set_killed(ABORT_QUERY); } USER_CONN *user_connect; CHARSET_INFO *db_charset; - Warning_info *warning_info; - Diagnostics_area *stmt_da; #if defined(ENABLED_PROFILING) PROFILING profiling; #endif + /** Current statement digest. */ + sql_digest_state *m_digest; + /** Current statement digest token array. */ + unsigned char *m_token_array; + /** Top level statement digest. */ + sql_digest_state m_digest_state; + + /** Current statement instrumentation. */ + PSI_statement_locker *m_statement_psi; +#ifdef HAVE_PSI_STATEMENT_INTERFACE + /** Current statement instrumentation state. */ + PSI_statement_locker_state m_statement_state; +#endif /* HAVE_PSI_STATEMENT_INTERFACE */ + /** Idle instrumentation. */ + PSI_idle_locker *m_idle_psi; +#ifdef HAVE_PSI_IDLE_INTERFACE + /** Idle instrumentation state. */ + PSI_idle_locker_state m_idle_state; +#endif /* HAVE_PSI_IDLE_INTERFACE */ + /* Id of current query. Statement can be reused to execute several queries query_id is global in context of the whole MySQL server. @@ -2206,10 +2720,10 @@ public: ulong query_plan_fsort_passes; pthread_t real_id; /* For debugging */ my_thread_id thread_id; + uint32 os_thread_id; uint tmp_table, global_disable_checkpoint; uint server_status,open_options; enum enum_thread_type system_thread; - uint select_number; //number of select (used for EXPLAIN) /* Current or next transaction isolation level. When a connection is established, the value is taken from @@ -2235,12 +2749,23 @@ public: above. */ enum_tx_isolation tx_isolation; + /* + Current or next transaction access mode. + See comment above regarding tx_isolation. + */ + bool tx_read_only; enum_check_fields count_cuted_fields; DYNAMIC_ARRAY user_var_events; /* For user variables replication */ MEM_ROOT *user_var_events_alloc; /* Allocate above array elements here */ /* + Define durability properties that engines may check to + improve performance. Not yet used in MariaDB + */ + enum durability_properties durability_property; + + /* If checking this in conjunction with a wait condition, please include a check after enter_cond() if you want to avoid a race condition. For details see the implementation of awake(), @@ -2248,22 +2773,48 @@ public: */ killed_state volatile killed; + /* + The following is used if one wants to have a specific error number and + text for the kill + */ + struct err_info + { + int no; + const char msg[256]; + } *killed_err; + + /* See also thd_killed() */ + inline bool check_killed() + { + if (killed) + return TRUE; + if (apc_target.have_apc_requests()) + apc_target.process_apc_requests(); + return FALSE; + } + /* scramble - random string sent to client on handshake */ char scramble[SCRAMBLE_LENGTH+1]; - bool slave_thread, one_shot_set; + /* + If this is a slave, the name of the connection stored here. + This is used for taging error messages in the log files. + */ + LEX_STRING connection_name; + char default_master_connection_buff[MAX_CONNECTION_NAME+1]; + uint8 password; /* 0, 1 or 2 */ + uint8 failed_com_change_user; + bool slave_thread; bool extra_port; /* If extra connection */ bool no_errors; - uint8 password; - uint8 failed_com_change_user; /** Set to TRUE if execution of the current compound statement can not continue. In particular, disables activation of CONTINUE or EXIT handlers of stored routines. Reset in the end of processing of the current user request, in - @see mysql_reset_thd_for_next_command(). + @see THD::reset_for_next_command(). */ bool is_fatal_error; /** @@ -2289,13 +2840,6 @@ public: /* for IS NULL => = last_insert_id() fix in remove_eq_conds() */ bool substitute_null_with_insert_id; bool in_lock_tables; - /** - True if a slave error. Causes the slave to stop. Not the same - as the statement execution error (is_error()), since - a statement may be expected to return an error, e.g. because - it returned an error on master, and this is OK on the slave. - */ - bool is_slave_error; bool bootstrap, cleanup_done; /** is set if some thread specific value(s) used in a statement. */ @@ -2312,6 +2856,32 @@ public: /* set during loop of derived table processing */ bool derived_tables_processing; bool tablespace_op; /* This is TRUE in DISCARD/IMPORT TABLESPACE */ + /* True if we have to log the current statement */ + bool log_current_statement; + /** + True if a slave error. Causes the slave to stop. Not the same + as the statement execution error (is_error()), since + a statement may be expected to return an error, e.g. because + it returned an error on master, and this is OK on the slave. + */ + bool is_slave_error; + /* + True when a transaction is queued up for binlog group commit. + Used so that if another transaction needs to wait for a row lock held by + this transaction, it can signal to trigger the group commit immediately, + skipping the normal --binlog-commit-wait-count wait. + */ + bool waiting_on_group_commit; + /* + Set true when another transaction goes to wait on a row lock held by this + transaction. Used together with waiting_on_group_commit. + */ + bool has_waiter; + /* + In case of a slave, set to the error code the master got when executing + the query. 0 if no error on the master. + */ + int slave_expected_error; sp_rcontext *spcont; // SP runtime context sp_cache *sp_proc_cache; @@ -2346,10 +2916,13 @@ public: union { my_bool my_bool_value; + int int_value; + uint uint_value; long long_value; ulong ulong_value; ulonglong ulonglong_value; double double_value; + void *ptr_value; } sys_var_tmp; struct { @@ -2378,6 +2951,7 @@ public: query_id_t first_query_id; } binlog_evt_union; + mysql_cond_t COND_wsrep_thd; /** Internal parser state. Note that since the parser is not re-entrant, we keep only one parser @@ -2409,7 +2983,8 @@ public: /* Debug Sync facility. See debug_sync.cc. */ struct st_debug_sync_control *debug_sync_control; #endif /* defined(ENABLED_DEBUG_SYNC) */ - THD(); + THD(bool is_wsrep_applier= false); + ~THD(); void init(void); @@ -2447,10 +3022,21 @@ public: void close_active_vio(); #endif void awake(killed_state state_to_set); - + /** Disconnect the associated communication endpoint. */ void disconnect(); + + /* + Allows this thread to serve as a target for others to schedule Async + Procedure Calls on. + + It's possible to schedule any code to be executed this way, by + inheriting from the Apc_call object. Currently, only + Show_explain_request uses this. + */ + Apc_target apc_target; + #ifndef MYSQL_CLIENT enum enum_binlog_query_type { /* The query can be logged in row format or in statement format. */ @@ -2468,22 +3054,23 @@ public: int errcode); #endif - /* - For enter_cond() / exit_cond() to work the mutex must be got before - enter_cond(); this mutex is then released by exit_cond(). - Usage must be: lock mutex; enter_cond(); your code; exit_cond(). - */ - inline const char* enter_cond(mysql_cond_t *cond, mysql_mutex_t* mutex, - const char* msg) + inline void + enter_cond(mysql_cond_t *cond, mysql_mutex_t* mutex, + const PSI_stage_info *stage, PSI_stage_info *old_stage, + const char *src_function, const char *src_file, + int src_line) { - const char* old_msg = proc_info; mysql_mutex_assert_owner(mutex); mysys_var->current_mutex = mutex; mysys_var->current_cond = cond; - proc_info = msg; - return old_msg; + if (old_stage) + backup_stage(old_stage); + if (stage) + enter_stage(stage, src_function, src_file, src_line); } - inline void exit_cond(const char* old_msg) + inline void exit_cond(const PSI_stage_info *stage, + const char *src_function, const char *src_file, + int src_line) { /* Putting the mutex unlock in thd->exit_cond() ensures that @@ -2495,10 +3082,51 @@ public: mysql_mutex_lock(&mysys_var->mutex); mysys_var->current_mutex = 0; mysys_var->current_cond = 0; - proc_info = old_msg; + if (stage) + enter_stage(stage, src_function, src_file, src_line); mysql_mutex_unlock(&mysys_var->mutex); return; } + virtual int is_killed() { return killed; } + virtual THD* get_thd() { return this; } + + /** + A callback to the server internals that is used to address + special cases of the locking protocol. + Invoked when acquiring an exclusive lock, for each thread that + has a conflicting shared metadata lock. + + This function: + - aborts waiting of the thread on a data lock, to make it notice + the pending exclusive lock and back off. + - if the thread is an INSERT DELAYED thread, sends it a KILL + signal to terminate it. + + @note This function does not wait for the thread to give away its + locks. Waiting is done outside for all threads at once. + + @param ctx_in_use The MDL context owner (thread) to wake up. + @param needs_thr_lock_abort Indicates that to wake up thread + this call needs to abort its waiting + on table-level lock. + + @retval TRUE if the thread was woken up + @retval FALSE otherwise. + */ + virtual bool notify_shared_lock(MDL_context_owner *ctx_in_use, + bool needs_thr_lock_abort); + + // End implementation of MDL_context_owner interface. + + inline bool is_strict_mode() const + { + return (bool) (variables.sql_mode & (MODE_STRICT_TRANS_TABLES | + MODE_STRICT_ALL_TABLES)); + } + inline bool backslash_escapes() const + { + return !MY_TEST(variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES); + } inline my_time_t query_start() { query_start_used=1; return start_time; } inline ulong query_start_sec_part() { query_start_sec_part_used=1; return start_time_sec_part; } @@ -2507,6 +3135,9 @@ public: my_hrtime_t hrtime= my_hrtime(); start_time= hrtime_to_my_time(hrtime); start_time_sec_part= hrtime_sec_part(hrtime); +#ifdef HAVE_PSI_THREAD_INTERFACE + PSI_THREAD_CALL(set_thread_start_time)(start_time); +#endif } inline void set_start_time() { @@ -2514,6 +3145,9 @@ public: { start_time= hrtime_to_my_time(user_time); start_time_sec_part= hrtime_sec_part(user_time); +#ifdef HAVE_PSI_THREAD_INTERFACE + PSI_THREAD_CALL(set_thread_start_time)(start_time); +#endif } else set_current_time(); @@ -2523,17 +3157,22 @@ public: set_start_time(); start_utime= utime_after_lock= microsecond_interval_timer(); } - inline void set_time(my_hrtime_t t) + inline void set_time(my_hrtime_t t) { user_time= t; set_time(); } - inline void set_time(my_time_t t, ulong sec_part) + inline void set_time(my_time_t t, ulong sec_part) { my_hrtime_t hrtime= { hrtime_from_time(t) + sec_part }; set_time(hrtime); } - void set_time_after_lock() { utime_after_lock= microsecond_interval_timer(); } + void set_time_after_lock() + { + utime_after_lock= microsecond_interval_timer(); + MYSQL_SET_STATEMENT_LOCK_TIME(m_statement_psi, + (utime_after_lock - start_utime)); + } ulonglong current_utime() { return microsecond_interval_timer(); } /** @@ -2630,20 +3269,78 @@ public: return alloc_root(&transaction.mem_root,size); } - LEX_STRING *make_lex_string(LEX_STRING *lex_str, - const char* str, uint length, - bool allocate_lex_string); + LEX_STRING *make_lex_string(LEX_STRING *lex_str, const char* str, uint length) + { + if (!(lex_str->str= strmake_root(mem_root, str, length))) + return 0; + lex_str->length= length; + return lex_str; + } + + LEX_STRING *make_lex_string(const char* str, uint length) + { + LEX_STRING *lex_str; + if (!(lex_str= (LEX_STRING *)alloc_root(mem_root, sizeof(LEX_STRING)))) + return 0; + return make_lex_string(lex_str, str, length); + } + // Allocate LEX_STRING for character set conversion + bool alloc_lex_string(LEX_STRING *dst, uint length) + { + if ((dst->str= (char*) alloc(length))) + return false; + dst->length= 0; // Safety + return true; // EOM + } bool convert_string(LEX_STRING *to, CHARSET_INFO *to_cs, const char *from, uint from_length, CHARSET_INFO *from_cs); + /* + Convert a strings between character sets. + Uses my_convert_fix(), which uses an mb_wc .. mc_mb loop internally. + dstcs and srccs cannot be &my_charset_bin. + */ + bool convert_fix(CHARSET_INFO *dstcs, LEX_STRING *dst, + CHARSET_INFO *srccs, const char *src, uint src_length, + String_copier *status); + + /* + Same as above, but additionally sends ER_INVALID_CHARACTER_STRING + in case of bad byte sequences or Unicode conversion problems. + */ + bool convert_with_error(CHARSET_INFO *dstcs, LEX_STRING *dst, + CHARSET_INFO *srccs, + const char *src, uint src_length); + + /* + If either "dstcs" or "srccs" is &my_charset_bin, + then performs native copying using cs->cset->copy_fix(). + Otherwise, performs Unicode conversion using convert_fix(). + */ + bool copy_fix(CHARSET_INFO *dstcs, LEX_STRING *dst, + CHARSET_INFO *srccs, const char *src, uint src_length, + String_copier *status); + + /* + Same as above, but additionally sends ER_INVALID_CHARACTER_STRING + in case of bad byte sequences or Unicode conversion problems. + */ + bool copy_with_error(CHARSET_INFO *dstcs, LEX_STRING *dst, + CHARSET_INFO *srccs, const char *src, uint src_length); bool convert_string(String *s, CHARSET_INFO *from_cs, CHARSET_INFO *to_cs); void add_changed_table(TABLE *table); void add_changed_table(const char *key, long key_length); CHANGED_TABLE_LIST * changed_table_dup(const char *key, long key_length); - int send_explain_fields(select_result *result); + int prepare_explain_fields(select_result *result, List<Item> *field_list, + uint8 explain_flags, bool is_analyze); + int send_explain_fields(select_result *result, uint8 explain_flags, + bool is_analyze); + void make_explain_field_list(List<Item> &field_list, uint8 explain_flags, + bool is_analyze); + void make_explain_json_field_list(List<Item> &field_list, bool is_analyze); /** Clear the current error, if any. @@ -2653,18 +3350,18 @@ public: @todo: To silence an error, one should use Internal_error_handler mechanism. Issuing an error that can be possibly later "cleared" is not compatible with other installed error handlers and audit plugins. - In future this function will be removed. */ - inline void clear_error() + inline void clear_error(bool clear_diagnostics= 0) { DBUG_ENTER("clear_error"); - if (stmt_da->is_error()) - stmt_da->reset_diagnostics_area(); + if (get_stmt_da()->is_error() || clear_diagnostics) + get_stmt_da()->reset_diagnostics_area(); is_slave_error= 0; if (killed == KILL_BAD_DATA) - killed= NOT_KILLED; // KILL_BAD_DATA can be reset w/o a mutex + reset_killed(); DBUG_VOID_RETURN; } + #ifndef EMBEDDED_LIBRARY inline bool vio_ok() const { return net.vio != 0; } /** Return FALSE if connection to client is broken. */ @@ -2688,7 +3385,7 @@ public: */ inline void fatal_error() { - DBUG_ASSERT(stmt_da->is_error() || killed); + DBUG_ASSERT(get_stmt_da()->is_error() || killed); is_fatal_error= 1; DBUG_PRINT("error",("Fatal error set")); } @@ -2705,7 +3402,20 @@ public: To raise this flag, use my_error(). */ - inline bool is_error() const { return stmt_da->is_error(); } + inline bool is_error() const { return m_stmt_da->is_error(); } + + /// Returns Diagnostics-area for the current statement. + Diagnostics_area *get_stmt_da() + { return m_stmt_da; } + + /// Returns Diagnostics-area for the current statement. + const Diagnostics_area *get_stmt_da() const + { return m_stmt_da; } + + /// Sets Diagnostics-area for the current statement. + void set_stmt_da(Diagnostics_area *da) + { m_stmt_da= da; } + inline CHARSET_INFO *charset() { return variables.character_set_client; } void update_charset(); @@ -2765,10 +3475,54 @@ public: state after execution of a non-prepared SQL statement. */ void end_statement(); - inline int killed_errno() const + + /* + Mark thread to be killed, with optional error number and string. + string is not released, so it has to be allocted on thd mem_root + or be a global string + + Ensure that we don't replace a kill with a lesser one. For example + if user has done 'kill_connection' we shouldn't replace it with + KILL_QUERY. + */ + inline void set_killed(killed_state killed_arg, + int killed_errno_arg= 0, + const char *killed_err_msg_arg= 0) { - return ::killed_errno(killed); + mysql_mutex_lock(&LOCK_thd_kill); + set_killed_no_mutex(killed_arg, killed_errno_arg, killed_err_msg_arg); + mysql_mutex_unlock(&LOCK_thd_kill); } + /* + This is only used by THD::awake where we need to keep the lock mutex + locked over some time. + It's ok to have this inline, as in most cases killed_errno_arg will + be a constant 0 and most of the function will disappear. + */ + inline void set_killed_no_mutex(killed_state killed_arg, + int killed_errno_arg= 0, + const char *killed_err_msg_arg= 0) + { + if (killed <= killed_arg) + { + killed= killed_arg; + if (killed_errno_arg) + { + /* + If alloc fails, we only remember the killed flag. + The worst things that can happen is that we get + a suboptimal error message. + */ + if ((killed_err= (err_info*) alloc(sizeof(*killed_err)))) + { + killed_err->no= killed_errno_arg; + ::strmake((char*) killed_err->msg, killed_err_msg_arg, + sizeof(killed_err->msg)-1); + } + } + } + } + int killed_errno(); inline void reset_killed() { /* @@ -2777,16 +3531,28 @@ public: */ if (killed != NOT_KILLED) { - mysql_mutex_lock(&LOCK_thd_data); + mysql_mutex_lock(&LOCK_thd_kill); killed= NOT_KILLED; - mysql_mutex_unlock(&LOCK_thd_data); + killed_err= 0; + mysql_mutex_unlock(&LOCK_thd_kill); + } + } + inline void reset_kill_query() + { + if (killed < KILL_CONNECTION) + { + reset_killed(); + mysys_var->abort= 0; } } - inline void send_kill_message() const + inline void send_kill_message() { + mysql_mutex_lock(&LOCK_thd_kill); int err= killed_errno(); if (err) - my_message(err, ER(err), MYF(0)); + my_message(err, killed_err ? killed_err->msg : ER_THD(this, err), + MYF(0)); + mysql_mutex_unlock(&LOCK_thd_kill); } /* return TRUE if we will abort query if we make a warning now */ inline bool really_abort_on_warning() @@ -2803,6 +3569,31 @@ public: void set_n_backup_active_arena(Query_arena *set, Query_arena *backup); void restore_active_arena(Query_arena *set, Query_arena *backup); + inline void get_binlog_format(enum_binlog_format *format, + enum_binlog_format *current_format) + { + *format= (enum_binlog_format) variables.binlog_format; + *current_format= current_stmt_binlog_format; + } + inline enum_binlog_format get_current_stmt_binlog_format() + { + return current_stmt_binlog_format; + } + inline void set_binlog_format(enum_binlog_format format, + enum_binlog_format current_format) + { + DBUG_ENTER("set_binlog_format"); + variables.binlog_format= format; + current_stmt_binlog_format= current_format; + DBUG_VOID_RETURN; + } + inline void set_binlog_format_stmt() + { + DBUG_ENTER("set_binlog_format_stmt"); + variables.binlog_format= BINLOG_FORMAT_STMT; + current_stmt_binlog_format= BINLOG_FORMAT_STMT; + DBUG_VOID_RETURN; + } /* @todo Make these methods private or remove them completely. Only decide_logging_format should call them. /Sven @@ -2827,22 +3618,31 @@ public: tests fail and so force them to propagate the lex->binlog_row_based_if_mixed upwards to the caller. */ - if ((variables.binlog_format == BINLOG_FORMAT_MIXED) && - (in_sub_stmt == 0)) + if ((wsrep_binlog_format() == BINLOG_FORMAT_MIXED) && (in_sub_stmt == 0)) set_current_stmt_binlog_format_row(); DBUG_VOID_RETURN; } + inline void set_current_stmt_binlog_format_row() { DBUG_ENTER("set_current_stmt_binlog_format_row"); current_stmt_binlog_format= BINLOG_FORMAT_ROW; DBUG_VOID_RETURN; } - inline void clear_current_stmt_binlog_format_row() + /* Set binlog format temporarily to statement. Returns old format */ + inline enum_binlog_format set_current_stmt_binlog_format_stmt() { - DBUG_ENTER("clear_current_stmt_binlog_format_row"); + enum_binlog_format orig_format= current_stmt_binlog_format; + DBUG_ENTER("set_current_stmt_binlog_format_stmt"); current_stmt_binlog_format= BINLOG_FORMAT_STMT; + DBUG_RETURN(orig_format); + } + inline void restore_stmt_binlog_format(enum_binlog_format format) + { + DBUG_ENTER("restore_stmt_binlog_format"); + DBUG_ASSERT(!is_current_stmt_binlog_format_row()); + current_stmt_binlog_format= format; DBUG_VOID_RETURN; } inline void reset_current_stmt_binlog_format_row() @@ -2869,10 +3669,10 @@ public: show_system_thread(system_thread))); if (in_sub_stmt == 0) { - if (variables.binlog_format == BINLOG_FORMAT_ROW) + if (wsrep_binlog_format() == BINLOG_FORMAT_ROW) set_current_stmt_binlog_format_row(); else if (temporary_tables == NULL) - clear_current_stmt_binlog_format_row(); + set_current_stmt_binlog_format_stmt(); } DBUG_VOID_RETURN; } @@ -2918,8 +3718,13 @@ public: db= NULL; } db_length= db ? new_db_len : 0; + bool result= new_db && !db; mysql_mutex_unlock(&LOCK_thd_data); - return new_db && !db; +#ifdef HAVE_PSI_THREAD_INTERFACE + if (result) + PSI_THREAD_CALL(set_thread_db)(new_db, new_db_len); +#endif + return result; } /** @@ -2941,6 +3746,9 @@ public: db= new_db; db_length= new_db_len; mysql_mutex_unlock(&LOCK_thd_data); +#ifdef HAVE_PSI_THREAD_INTERFACE + PSI_THREAD_CALL(set_thread_db)(new_db, new_db_len); +#endif } } /* @@ -2971,6 +3779,7 @@ public: */ void push_internal_handler(Internal_error_handler *handler); +private: /** Handle a sql condition. @param sql_errno the condition error number @@ -2980,12 +3789,13 @@ public: @param[out] cond_hdl the sql condition raised, if any @return true if the condition is handled */ - virtual bool handle_condition(uint sql_errno, - const char* sqlstate, - MYSQL_ERROR::enum_warning_level level, - const char* msg, - MYSQL_ERROR ** cond_hdl); + bool handle_condition(uint sql_errno, + const char* sqlstate, + Sql_condition::enum_warning_level level, + const char* msg, + Sql_condition ** cond_hdl); +public: /** Remove the error handler last pushed. */ @@ -3035,10 +3845,10 @@ private: To raise a SQL condition, the code should use the public raise_error() or raise_warning() methods provided by class THD. */ - friend class Signal_common; - friend class Signal_statement; - friend class Resignal_statement; - friend void push_warning(THD*, MYSQL_ERROR::enum_warning_level, uint, const char*); + friend class Sql_cmd_common_signal; + friend class Sql_cmd_signal; + friend class Sql_cmd_resignal; + friend void push_warning(THD*, Sql_condition::enum_warning_level, uint, const char*); friend void my_message_sql(uint, const char *, myf); /** @@ -3049,15 +3859,24 @@ private: @param msg the condition message text @return The condition raised, or NULL */ - MYSQL_ERROR* + Sql_condition* raise_condition(uint sql_errno, const char* sqlstate, - MYSQL_ERROR::enum_warning_level level, + Sql_condition::enum_warning_level level, const char* msg); public: /** Overloaded to guard query/query_length fields */ virtual void set_statement(Statement *stmt); + void set_command(enum enum_server_command command) + { + m_command= command; +#ifdef HAVE_PSI_THREAD_INTERFACE + PSI_STATEMENT_CALL(set_thread_command)(m_command); +#endif + } + inline enum enum_server_command get_command() const + { return m_command; } /** Assign a new value to thd->query and thd->query_id and mysys_var. @@ -3072,12 +3891,24 @@ public: { set_query(CSET_STRING(query_arg, query_length_arg, charset())); } - void set_query(const CSET_STRING &str); /* Mutex protected */ + void set_query(const CSET_STRING &string_arg) + { + mysql_mutex_lock(&LOCK_thd_data); + set_query_inner(string_arg); + mysql_mutex_unlock(&LOCK_thd_data); + +#ifdef HAVE_PSI_THREAD_INTERFACE + PSI_THREAD_CALL(set_thread_info)(query(), query_length()); +#endif + } void reset_query() /* Mutex protected */ { set_query(CSET_STRING()); } void set_query_and_id(char *query_arg, uint32 query_length_arg, CHARSET_INFO *cs, query_id_t new_query_id); - void set_query_id(query_id_t new_query_id); + void set_query_id(query_id_t new_query_id) + { + query_id= new_query_id; + } void set_open_tables(TABLE *open_tables_arg) { mysql_mutex_lock(&LOCK_thd_data); @@ -3106,9 +3937,11 @@ public: } void leave_locked_tables_mode(); int decide_logging_format(TABLE_LIST *tables); - void binlog_invoker() { m_binlog_invoker= TRUE; } - bool need_binlog_invoker() { return m_binlog_invoker; } - void get_definer(LEX_USER *definer); + + enum need_invoker { INVOKER_NONE=0, INVOKER_USER, INVOKER_ROLE}; + void binlog_invoker(bool role) { m_binlog_invoker= role ? INVOKER_ROLE : INVOKER_USER; } + enum need_invoker need_binlog_invoker() { return m_binlog_invoker; } + void get_definer(LEX_USER *definer, bool role); void set_invoker(const LEX_STRING *user, const LEX_STRING *host) { invoker_user= *user; @@ -3142,13 +3975,37 @@ public: void add_status_to_global() { + DBUG_ASSERT(status_in_global == 0); mysql_mutex_lock(&LOCK_status); add_to_status(&global_status_var, &status_var); /* Mark that this THD status has already been added in global status */ + status_var.global_memory_used= 0; status_in_global= 1; mysql_mutex_unlock(&LOCK_status); } + wait_for_commit *wait_for_commit_ptr; + int wait_for_prior_commit() + { + if (wait_for_commit_ptr) + return wait_for_commit_ptr->wait_for_prior_commit(this); + return 0; + } + void wakeup_subsequent_commits(int wakeup_error) + { + if (wait_for_commit_ptr) + wait_for_commit_ptr->wakeup_subsequent_commits(wakeup_error); + } + wait_for_commit *suspend_subsequent_commits() { + wait_for_commit *suspended= wait_for_commit_ptr; + wait_for_commit_ptr= NULL; + return suspended; + } + void resume_subsequent_commits(wait_for_commit *suspended) { + DBUG_ASSERT(!wait_for_commit_ptr); + wait_for_commit_ptr= suspended; + } + void mark_transaction_to_rollback(bool all); private: @@ -3171,18 +4028,19 @@ private: tree itself is reused between executions and thus is stored elsewhere. */ MEM_ROOT main_mem_root; - Warning_info main_warning_info; Diagnostics_area main_da; + Diagnostics_area *m_stmt_da; /** - It will be set TURE if CURRENT_USER() is called in account management - statements or default definer is set in CREATE/ALTER SP, SF, Event, - TRIGGER or VIEW statements. + It will be set if CURRENT_USER() or CURRENT_ROLE() is called in account + management statements or default definer is set in CREATE/ALTER SP, SF, + Event, TRIGGER or VIEW statements. - Current user will be binlogged into Query_log_event if m_binlog_invoker - is TRUE; It will be stored into invoker_host and invoker_user by SQL thread. + Current user or role will be binlogged into Query_log_event if + m_binlog_invoker is not NONE; It will be stored into invoker_host and + invoker_user by SQL thread. */ - bool m_binlog_invoker; + enum need_invoker m_binlog_invoker; /** It points to the invoker in the Query_log_event. @@ -3192,6 +4050,12 @@ private: */ LEX_STRING invoker_user; LEX_STRING invoker_host; + + /* Protect against add/delete of temporary tables in parallel replication */ + void rgi_lock_temporary_tables(); + void rgi_unlock_temporary_tables(bool clear); + bool rgi_have_temporary_tables(); +public: /* Flag, mutex and condition for a thread to wait for a signal from another thread. @@ -3202,55 +4066,162 @@ private: bool wakeup_ready; mysql_mutex_t LOCK_wakeup_ready; mysql_cond_t COND_wakeup_ready; + /* + The GTID assigned to the last commit. If no GTID was assigned to any commit + so far, this is indicated by last_commit_gtid.seq_no == 0. + */ + rpl_gtid last_commit_gtid; + + inline void lock_temporary_tables() + { + if (rgi_slave) + rgi_lock_temporary_tables(); + } + inline void unlock_temporary_tables(bool clear) + { + if (rgi_slave) + rgi_unlock_temporary_tables(clear); + } + inline bool have_temporary_tables() + { + return (temporary_tables || + (rgi_slave && unlikely(rgi_have_temporary_tables()))); + } + + LF_PINS *tdc_hash_pins; + LF_PINS *xid_hash_pins; + bool fix_xid_hash_pins(); + + inline ulong wsrep_binlog_format() const + { + return WSREP_FORMAT(variables.binlog_format); + } + +#ifdef WITH_WSREP + const bool wsrep_applier; /* dedicated slave applier thread */ + bool wsrep_applier_closing; /* applier marked to close */ + bool wsrep_client_thread; /* to identify client threads*/ + bool wsrep_PA_safe; + bool wsrep_converted_lock_session; + bool wsrep_apply_toi; /* applier processing in TOI */ + enum wsrep_exec_mode wsrep_exec_mode; + query_id_t wsrep_last_query_id; + enum wsrep_query_state wsrep_query_state; + enum wsrep_conflict_state wsrep_conflict_state; + wsrep_trx_meta_t wsrep_trx_meta; + uint32 wsrep_rand; + Relay_log_info *wsrep_rli; + rpl_group_info *wsrep_rgi; + wsrep_ws_handle_t wsrep_ws_handle; + ulong wsrep_retry_counter; // of autocommit + char *wsrep_retry_query; + size_t wsrep_retry_query_len; + enum enum_server_command wsrep_retry_command; + enum wsrep_consistency_check_mode + wsrep_consistency_check; + int wsrep_mysql_replicated; + const char *wsrep_TOI_pre_query; /* a query to apply before + the actual TOI query */ + size_t wsrep_TOI_pre_query_len; + wsrep_po_handle_t wsrep_po_handle; + size_t wsrep_po_cnt; +#ifdef GTID_SUPPORT + rpl_sid wsrep_po_sid; +#endif /* GTID_SUPPORT */ + void *wsrep_apply_format; + char wsrep_info[128]; /* string for dynamic proc info */ + /* + When enabled, do not replicate/binlog updates from the current table that's + being processed. At the moment, it is used to keep mysql.gtid_slave_pos + table updates from being replicated to other nodes via galera replication. + */ + bool wsrep_ignore_table; + wsrep_gtid_t wsrep_sync_wait_gtid; + ulong wsrep_affected_rows; + bool wsrep_replicate_GTID; + bool wsrep_skip_wsrep_GTID; +#endif /* WITH_WSREP */ + + /* Handling of timeouts for commands */ + thr_timer_t query_timer; +public: + void set_query_timer() + { +#ifndef EMBEDDED_LIBRARY + /* + Don't start a query timer if + - If timeouts are not set + - if we are in a stored procedure or sub statement + - If this is a slave thread + - If we already have set a timeout (happens when running prepared + statements that calls mysql_execute_command()) + */ + if (!variables.max_statement_time || spcont || in_sub_stmt || + slave_thread || query_timer.expired == 0) + return; + thr_timer_settime(&query_timer, variables.max_statement_time); +#endif + } + void reset_query_timer() + { +#ifndef EMBEDDED_LIBRARY + if (spcont || in_sub_stmt || slave_thread) + return; + if (!query_timer.expired) + thr_timer_end(&query_timer); +#endif + } + void restore_set_statement_var() + { + main_lex.restore_set_statement_var(); + } + /* Copy relevant `stmt` transaction flags to `all` transaction. */ + void merge_unsafe_rollback_flags() + { + if (transaction.stmt.modified_non_trans_table) + transaction.all.modified_non_trans_table= TRUE; + transaction.all.m_unsafe_rollback_flags|= + (transaction.stmt.m_unsafe_rollback_flags & + (THD_TRANS::DID_WAIT | THD_TRANS::CREATED_TEMP_TABLE | + THD_TRANS::DROPPED_TEMP_TABLE | THD_TRANS::DID_DDL)); + } }; -/** A short cut for thd->stmt_da->set_ok_status(). */ +/** A short cut for thd->get_stmt_da()->set_ok_status(). */ inline void my_ok(THD *thd, ulonglong affected_rows= 0, ulonglong id= 0, const char *message= NULL) { thd->set_row_count_func(affected_rows); - thd->stmt_da->set_ok_status(thd, affected_rows, id, message); + thd->get_stmt_da()->set_ok_status(affected_rows, id, message); } -/** A short cut for thd->stmt_da->set_eof_status(). */ +/** A short cut for thd->get_stmt_da()->set_eof_status(). */ inline void my_eof(THD *thd) { thd->set_row_count_func(-1); - thd->stmt_da->set_eof_status(thd); + thd->get_stmt_da()->set_eof_status(thd); } -#define tmp_disable_binlog(A) \ +#define tmp_disable_binlog(A) \ {ulonglong tmp_disable_binlog__save_options= (A)->variables.option_bits; \ - (A)->variables.option_bits&= ~OPTION_BIN_LOG - -#define reenable_binlog(A) (A)->variables.option_bits= tmp_disable_binlog__save_options;} - - -/* - These functions are for making it later easy to add strict - checking for all date handling. -*/ + (A)->variables.option_bits&= ~OPTION_BIN_LOG; \ + (A)->variables.sql_log_bin_off= 1; -const my_bool strict_date_checking= 0; +#define reenable_binlog(A) \ + (A)->variables.option_bits= tmp_disable_binlog__save_options; \ + (A)->variables.sql_log_bin_off= 0;} -inline ulong sql_mode_for_dates(THD *thd) -{ - if (strict_date_checking) - return (thd->variables.sql_mode & - (MODE_NO_ZERO_DATE | MODE_NO_ZERO_IN_DATE | - MODE_INVALID_DATES)); - return (thd->variables.sql_mode & MODE_INVALID_DATES); -} -inline ulong sql_mode_for_dates() +inline sql_mode_t sql_mode_for_dates(THD *thd) { - return sql_mode_for_dates(current_thd); + return thd->variables.sql_mode & + (MODE_NO_ZERO_DATE | MODE_NO_ZERO_IN_DATE | MODE_INVALID_DATES); } /* @@ -3280,13 +4251,64 @@ public: class JOIN; -class select_result :public Sql_alloc { -protected: +/* Pure interface for sending tabular data */ +class select_result_sink: public Sql_alloc +{ +public: THD *thd; + select_result_sink(THD *thd_arg): thd(thd_arg) {} + /* + send_data returns 0 on ok, 1 on error and -1 if data was ignored, for + example for a duplicate row entry written to a temp table. + */ + virtual int send_data(List<Item> &items)=0; + virtual ~select_result_sink() {}; +}; + +class select_result_interceptor; + +/* + Interface for sending tabular data, together with some other stuff: + + - Primary purpose seems to be seding typed tabular data: + = the DDL is sent with send_fields() + = the rows are sent with send_data() + Besides that, + - there seems to be an assumption that the sent data is a result of + SELECT_LEX_UNIT *unit, + - nest_level is used by SQL parser +*/ + +class select_result :public select_result_sink +{ +protected: + /* + All descendant classes have their send_data() skip the first + unit->offset_limit_cnt rows sent. Select_materialize + also uses unit->get_unit_column_types(). + */ SELECT_LEX_UNIT *unit; + /* Something used only by the parser: */ public: - select_result(); + select_result(THD *thd_arg): select_result_sink(thd_arg) {} virtual ~select_result() {}; + /** + Change wrapped select_result. + + Replace the wrapped result object with new_result and call + prepare() and prepare2() on new_result. + + This base class implementation doesn't wrap other select_results. + + @param new_result The new result object to wrap around + + @retval false Success + @retval true Error + */ + virtual bool change_result(select_result *new_result) + { + return false; + } virtual int prepare(List<Item> &list, SELECT_LEX_UNIT *u) { unit= u; @@ -3301,13 +4323,7 @@ public: virtual uint field_count(List<Item> &fields) const { return fields.elements; } virtual bool send_result_set_metadata(List<Item> &list, uint flags)=0; - /* - send_data returns 0 on ok, 1 on error and -1 if data was ignored, for - example for a duplicate row entry written to a temp table. - */ - virtual int send_data(List<Item> &items)=0; virtual bool initialize_tables (JOIN *join=0) { return 0; } - virtual void send_error(uint errcode,const char *err); virtual bool send_eof()=0; /** Check if this query returns a result set and therefore is allowed in @@ -3330,6 +4346,64 @@ public: void begin_dataset() {} #endif virtual void update_used_tables() {} + + void reset_offset_limit() + { + unit->offset_limit_cnt= 0; + } + + /* + This returns + - NULL if the class sends output row to the client + - this if the output is set elsewhere (a file, @variable, or table). + */ + virtual select_result_interceptor *result_interceptor()=0; +}; + + +/* + This is a select_result_sink which simply writes all data into a (temporary) + table. Creation/deletion of the table is outside of the scope of the class + + It is aimed at capturing SHOW EXPLAIN output, so: + - Unlike select_result class, we don't assume that the sent data is an + output of a SELECT_LEX_UNIT (and so we dont apply "LIMIT x,y" from the + unit) + - We don't try to convert the target table to MyISAM +*/ + +class select_result_explain_buffer : public select_result_sink +{ +public: + select_result_explain_buffer(THD *thd_arg, TABLE *table_arg) : + select_result_sink(thd_arg), dst_table(table_arg) {}; + + TABLE *dst_table; /* table to write into */ + + /* The following is called in the child thread: */ + int send_data(List<Item> &items); +}; + + +/* + This is a select_result_sink which stores the data in text form. + + It is only used to save EXPLAIN output. +*/ + +class select_result_text_buffer : public select_result_sink +{ +public: + select_result_text_buffer(THD *thd_arg): select_result_sink(thd_arg) {} + int send_data(List<Item> &items); + bool send_result_set_metadata(List<Item> &fields, uint flag); + + void save_to(String *res); +private: + int append_row(List<Item> &items, bool send_names); + + List<char*> rows; + int n_columns; }; @@ -3342,14 +4416,24 @@ public: class select_result_interceptor: public select_result { public: - select_result_interceptor() + select_result_interceptor(THD *thd_arg): + select_result(thd_arg), suppress_my_ok(false) { DBUG_ENTER("select_result_interceptor::select_result_interceptor"); - DBUG_PRINT("enter", ("this 0x%lx", (ulong) this)); + DBUG_PRINT("enter", ("this %p", this)); DBUG_VOID_RETURN; } /* Remove gcc warning */ uint field_count(List<Item> &fields) const { return 0; } bool send_result_set_metadata(List<Item> &fields, uint flag) { return FALSE; } + select_result_interceptor *result_interceptor() { return this; } + + /* + Instruct the object to not call my_ok(). Client output will be handled + elsewhere. (this is used by ANALYZE $stmt feature). + */ + void disable_my_ok_calls() { suppress_my_ok= true; } +protected: + bool suppress_my_ok; }; @@ -3361,13 +4445,31 @@ class select_send :public select_result { */ bool is_result_set_started; public: - select_send() :is_result_set_started(FALSE) {} + select_send(THD *thd_arg): + select_result(thd_arg), is_result_set_started(FALSE) {} bool send_result_set_metadata(List<Item> &list, uint flags); int send_data(List<Item> &items); bool send_eof(); virtual bool check_simple_select() const { return FALSE; } void abort_result_set(); virtual void cleanup(); + select_result_interceptor *result_interceptor() { return NULL; } +}; + + +/* + We need this class, because select_send::send_eof() will call ::my_eof. + + See also class Protocol_discard. +*/ + +class select_send_analyze : public select_send +{ + bool send_result_set_metadata(List<Item> &list, uint flags) { return 0; } + bool send_eof() { return 0; } + void abort_result_set() {} +public: + select_send_analyze(THD *thd_arg): select_send(thd_arg) {} }; @@ -3380,10 +4482,10 @@ protected: char path[FN_REFLEN]; public: - select_to_file(sql_exchange *ex) :exchange(ex), file(-1),row_count(0L) + select_to_file(THD *thd_arg, sql_exchange *ex): + select_result_interceptor(thd_arg), exchange(ex), file(-1),row_count(0L) { path[0]=0; } ~select_to_file(); - void send_error(uint errcode,const char *err); bool send_eof(); void cleanup(); }; @@ -3423,7 +4525,7 @@ class select_export :public select_to_file { bool fixed_row_size; CHARSET_INFO *write_cs; // output charset public: - select_export(sql_exchange *ex) :select_to_file(ex) {} + select_export(THD *thd_arg, sql_exchange *ex): select_to_file(thd_arg, ex) {} ~select_export(); int prepare(List<Item> &list, SELECT_LEX_UNIT *u); int send_data(List<Item> &items); @@ -3432,7 +4534,7 @@ public: class select_dump :public select_to_file { public: - select_dump(sql_exchange *ex) :select_to_file(ex) {} + select_dump(THD *thd_arg, sql_exchange *ex): select_to_file(thd_arg, ex) {} int prepare(List<Item> &list, SELECT_LEX_UNIT *u); int send_data(List<Item> &items); }; @@ -3446,7 +4548,7 @@ class select_insert :public select_result_interceptor { ulonglong autoinc_value_of_last_inserted_row; // autogenerated or not COPY_INFO info; bool insert_into_view; - select_insert(TABLE_LIST *table_list_par, + select_insert(THD *thd_arg, TABLE_LIST *table_list_par, TABLE *table_par, List<Item> *fields_par, List<Item> *update_fields, List<Item> *update_values, enum_duplicates duplic, bool ignore); @@ -3456,7 +4558,8 @@ class select_insert :public select_result_interceptor { virtual int send_data(List<Item> &items); virtual void store_values(List<Item> &values); virtual bool can_rollback_data() { return 0; } - void send_error(uint errcode,const char *err); + bool prepare_eof(); + bool send_ok_packet(); bool send_eof(); virtual void abort_result_set(); /* not implemented: select_insert is never re-used in prepared statements */ @@ -3465,9 +4568,8 @@ class select_insert :public select_result_interceptor { class select_create: public select_insert { - ORDER *group; TABLE_LIST *create_table; - HA_CREATE_INFO *create_info; + Table_specification_st *create_info; TABLE_LIST *select_tables; Alter_info *alter_info; Field **field; @@ -3475,24 +4577,25 @@ class select_create: public select_insert { MYSQL_LOCK *m_lock; /* m_lock or thd->extra_lock */ MYSQL_LOCK **m_plock; + bool exit_done; + public: - select_create (TABLE_LIST *table_arg, - HA_CREATE_INFO *create_info_par, - Alter_info *alter_info_arg, - List<Item> &select_fields,enum_duplicates duplic, bool ignore, - TABLE_LIST *select_tables_arg) - :select_insert (NULL, NULL, &select_fields, 0, 0, duplic, ignore), + select_create(THD *thd_arg, TABLE_LIST *table_arg, + Table_specification_st *create_info_par, + Alter_info *alter_info_arg, + List<Item> &select_fields,enum_duplicates duplic, bool ignore, + TABLE_LIST *select_tables_arg): + select_insert(thd_arg, NULL, NULL, &select_fields, 0, 0, duplic, ignore), create_table(table_arg), create_info(create_info_par), select_tables(select_tables_arg), alter_info(alter_info_arg), - m_plock(NULL) + m_plock(NULL), exit_done(0) {} int prepare(List<Item> &list, SELECT_LEX_UNIT *u); int binlog_show_create_table(TABLE **tables, uint count); void store_values(List<Item> &values); - void send_error(uint errcode,const char *err); bool send_eof(); virtual void abort_result_set(); virtual bool can_rollback_data() { return 1; } @@ -3507,12 +4610,22 @@ public: #ifdef WITH_ARIA_STORAGE_ENGINE #include <maria.h> +#else +#undef USE_ARIA_FOR_TMP_TABLES #endif #ifdef USE_ARIA_FOR_TMP_TABLES -#define ENGINE_COLUMNDEF MARIA_COLUMNDEF +#define TMP_ENGINE_COLUMNDEF MARIA_COLUMNDEF +#define TMP_ENGINE_HTON maria_hton +#define TMP_ENGINE_NAME "Aria" +inline uint tmp_table_max_key_length() { return maria_max_key_length(); } +inline uint tmp_table_max_key_parts() { return maria_max_key_segments(); } #else -#define ENGINE_COLUMNDEF MI_COLUMNDEF +#define TMP_ENGINE_COLUMNDEF MI_COLUMNDEF +#define TMP_ENGINE_HTON myisam_hton +#define TMP_ENGINE_NAME "MyISAM" +inline uint tmp_table_max_key_length() { return MI_MAX_KEY_LENGTH; } +inline uint tmp_table_max_key_parts() { return MI_MAX_KEY_SEG; } #endif /* @@ -3535,7 +4648,7 @@ public: Copy_field *save_copy_field, *save_copy_field_end; uchar *group_buff; Item **items_to_copy; /* Fields in tmp table */ - ENGINE_COLUMNDEF *recinfo, *start_recinfo; + TMP_ENGINE_COLUMNDEF *recinfo, *start_recinfo; KEY *keyinfo; ha_rows end_write_records; /** @@ -3568,8 +4681,6 @@ public: uint group_parts,group_length,group_null_parts; uint quick_group; bool using_indirect_summary_function; - /* If >0 convert all blob fields to varchar(convert_blob_length) */ - uint convert_blob_length; CHARSET_INFO *table_charset; bool schema_table; /* TRUE if the temp table is created for subquery materialization. */ @@ -3598,7 +4709,7 @@ public: TMP_TABLE_PARAM() :copy_field(0), group_parts(0), - group_length(0), group_null_parts(0), convert_blob_length(0), + group_length(0), group_null_parts(0), schema_table(0), materialized_subquery(0), force_not_null_cols(0), precomputed_group_by(0), force_copy_fields(0), bit_fields_as_long(0), skip_create_table(0) @@ -3633,11 +4744,23 @@ public: TABLE *table; ha_rows records; - select_union() :write_err(0), table(0), records(0) { tmp_table_param.init(); } + select_union(THD *thd_arg): + select_result_interceptor(thd_arg), write_err(0), table(0), records(0) + { tmp_table_param.init(); } int prepare(List<Item> &list, SELECT_LEX_UNIT *u); + /** + Do prepare() and prepare2() if they have been postponed until + column type information is computed (used by select_union_direct). + + @param types Column types + + @return false on success, true on failure + */ + virtual bool postponed_prepare(List<Item> &types) + { return false; } int send_data(List<Item> &items); bool send_eof(); - bool flush(); + virtual bool flush(); void cleanup(); virtual bool create_result_table(THD *thd, List<Item> *column_types, bool is_distinct, ulonglong options, @@ -3648,13 +4771,108 @@ public: TMP_TABLE_PARAM *get_tmp_table_param() { return &tmp_table_param; } }; + +/** + UNION result that is passed directly to the receiving select_result + without filling a temporary table. + + Function calls are forwarded to the wrapped select_result, but some + functions are expected to be called only once for each query, so + they are only executed for the first SELECT in the union (execept + for send_eof(), which is executed only for the last SELECT). + + This select_result is used when a UNION is not DISTINCT and doesn't + have a global ORDER BY clause. @see st_select_lex_unit::prepare(). +*/ + +class select_union_direct :public select_union +{ +private: + /* Result object that receives all rows */ + select_result *result; + /* The last SELECT_LEX of the union */ + SELECT_LEX *last_select_lex; + + /* Wrapped result has received metadata */ + bool done_send_result_set_metadata; + /* Wrapped result has initialized tables */ + bool done_initialize_tables; + + /* Accumulated limit_found_rows */ + ulonglong limit_found_rows; + + /* Number of rows offset */ + ha_rows offset; + /* Number of rows limit + offset, @see select_union_direct::send_data() */ + ha_rows limit; + +public: + /* Number of rows in the union */ + ha_rows send_records; + select_union_direct(THD *thd_arg, select_result *result_arg, + SELECT_LEX *last_select_lex_arg): + select_union(thd_arg), result(result_arg), + last_select_lex(last_select_lex_arg), + done_send_result_set_metadata(false), done_initialize_tables(false), + limit_found_rows(0) + { send_records= 0; } + bool change_result(select_result *new_result); + uint field_count(List<Item> &fields) const + { + // Only called for top-level select_results, usually select_send + DBUG_ASSERT(false); /* purecov: inspected */ + return 0; /* purecov: inspected */ + } + bool postponed_prepare(List<Item> &types); + bool send_result_set_metadata(List<Item> &list, uint flags); + int send_data(List<Item> &items); + bool initialize_tables (JOIN *join= NULL); + bool send_eof(); + bool flush() { return false; } + bool check_simple_select() const + { + /* Only called for top-level select_results, usually select_send */ + DBUG_ASSERT(false); /* purecov: inspected */ + return false; /* purecov: inspected */ + } + void abort_result_set() + { + result->abort_result_set(); /* purecov: inspected */ + } + void cleanup() + { + send_records= 0; + } + void set_thd(THD *thd_arg) + { + /* + Only called for top-level select_results, usually select_send, + and for the results of subquery engines + (select_<something>_subselect). + */ + DBUG_ASSERT(false); /* purecov: inspected */ + } + void reset_offset_limit_cnt() + { + // EXPLAIN should never output to a select_union_direct + DBUG_ASSERT(false); /* purecov: inspected */ + } + void begin_dataset() + { + // Only called for sp_cursor::Select_fetch_into_spvars + DBUG_ASSERT(false); /* purecov: inspected */ + } +}; + + /* Base subselect interface class */ class select_subselect :public select_result_interceptor { protected: Item_subselect *item; public: - select_subselect(Item_subselect *item); + select_subselect(THD *thd_arg, Item_subselect *item_arg): + select_result_interceptor(thd_arg), item(item_arg) {} int send_data(List<Item> &items)=0; bool send_eof() { return 0; }; }; @@ -3663,8 +4881,8 @@ public: class select_singlerow_subselect :public select_subselect { public: - select_singlerow_subselect(Item_subselect *item_arg) - :select_subselect(item_arg) + select_singlerow_subselect(THD *thd_arg, Item_subselect *item_arg): + select_subselect(thd_arg, item_arg) {} int send_data(List<Item> &items); }; @@ -3709,7 +4927,8 @@ protected: void reset(); public: - select_materialize_with_stats() { tmp_table_param.init(); } + select_materialize_with_stats(THD *thd_arg): select_union(thd_arg) + { tmp_table_param.init(); } bool create_result_table(THD *thd, List<Item> *column_types, bool is_distinct, ulonglong options, const char *alias, @@ -3746,9 +4965,9 @@ class select_max_min_finder_subselect :public select_subselect bool fmax; bool is_all; public: - select_max_min_finder_subselect(Item_subselect *item_arg, bool mx, - bool all) - :select_subselect(item_arg), cache(0), fmax(mx), is_all(all) + select_max_min_finder_subselect(THD *thd_arg, Item_subselect *item_arg, + bool mx, bool all): + select_subselect(thd_arg, item_arg), cache(0), fmax(mx), is_all(all) {} void cleanup(); int send_data(List<Item> &items); @@ -3762,8 +4981,8 @@ public: class select_exists_subselect :public select_subselect { public: - select_exists_subselect(Item_subselect *item_arg) - :select_subselect(item_arg){} + select_exists_subselect(THD *thd_arg, Item_subselect *item_arg): + select_subselect(thd_arg, item_arg) {} int send_data(List<Item> &items); }; @@ -3790,13 +5009,13 @@ public: /* Cost to materialize - execute the sub-join and write rows into temp.table */ - COST_VECT materialization_cost; + Cost_estimate materialization_cost; /* Cost to make one lookup in the temptable */ - COST_VECT lookup_cost; + Cost_estimate lookup_cost; /* Cost of scanning the materialized table */ - COST_VECT scan_cost; + Cost_estimate scan_cost; /* --- Execution structures ---------- */ @@ -3885,7 +5104,7 @@ public: table.str= internal_table_name; table.length=1; } - bool is_derived_table() const { return test(sel); } + bool is_derived_table() const { return MY_TEST(sel); } inline void change_db(char *db_name) { db.str= db_name; db.length= (uint) strlen(db_name); @@ -3895,6 +5114,7 @@ public: // this is needed for user_vars hash class user_var_entry { + CHARSET_INFO *m_charset; public: user_var_entry() {} /* Remove gcc warning */ LEX_STRING name; @@ -3908,9 +5128,12 @@ class user_var_entry longlong val_int(bool *null_value) const; String *val_str(bool *null_value, String *str, uint decimals); my_decimal *val_decimal(bool *null_value, my_decimal *result); - DTCollation collation; + CHARSET_INFO *charset() const { return m_charset; } + void set_charset(CHARSET_INFO *cs) { m_charset= cs; } }; +user_var_entry *get_variable(HASH *hash, LEX_STRING &name, + bool create_if_not_exists); /* Unique -- class for unique (removing of duplicates). @@ -3933,6 +5156,7 @@ class Unique :public Sql_alloc uint size; uint full_size; uint min_dupl_count; /* always 0 for unions, > 0 for intersections */ + bool with_counters; bool merge(TABLE *table, uchar *buff, bool without_last_merge); @@ -4006,17 +5230,16 @@ class multi_delete :public select_result_interceptor bool delete_while_scanning; /* error handling (rollback and binlogging) can happen in send_eof() - so that afterward send_error() needs to find out that. + so that afterward abort_result_set() needs to find out that. */ bool error_handled; public: - multi_delete(TABLE_LIST *dt, uint num_of_tables); + multi_delete(THD *thd_arg, TABLE_LIST *dt, uint num_of_tables); ~multi_delete(); int prepare(List<Item> &list, SELECT_LEX_UNIT *u); int send_data(List<Item> &items); bool initialize_tables (JOIN *join); - void send_error(uint errcode,const char *err); int do_deletes(); int do_table_deletes(TABLE *table, bool ignore); bool send_eof(); @@ -4052,19 +5275,20 @@ class multi_update :public select_result_interceptor bool ignore; /* error handling (rollback and binlogging) can happen in send_eof() - so that afterward send_error() needs to find out that. + so that afterward abort_result_set() needs to find out that. */ bool error_handled; - + + /* Need this to protect against multiple prepare() calls */ + bool prepared; public: - multi_update(TABLE_LIST *ut, List<TABLE_LIST> *leaves_list, + multi_update(THD *thd_arg, TABLE_LIST *ut, List<TABLE_LIST> *leaves_list, List<Item> *fields, List<Item> *values, enum_duplicates handle_duplicates, bool ignore); ~multi_update(); int prepare(List<Item> &list, SELECT_LEX_UNIT *u); int send_data(List<Item> &items); bool initialize_tables (JOIN *join); - void send_error(uint errcode,const char *err); int do_updates(); bool send_eof(); inline ha_rows num_found() @@ -4081,28 +5305,43 @@ public: class my_var : public Sql_alloc { public: - LEX_STRING s; -#ifndef DBUG_OFF + const LEX_STRING name; + enum type { SESSION_VAR, LOCAL_VAR, PARAM_VAR }; + type scope; + my_var(const LEX_STRING& j, enum type s) : name(j), scope(s) { } + virtual ~my_var() {} + virtual bool set(THD *thd, Item *val) = 0; +}; + +class my_var_sp: public my_var { +public: + uint offset; + enum_field_types type; /* Routine to which this Item_splocal belongs. Used for checking if correct runtime context is used for variable handling. */ sp_head *sp; -#endif - bool local; - uint offset; - enum_field_types type; - my_var (LEX_STRING& j, bool i, uint o, enum_field_types t) - :s(j), local(i), offset(o), type(t) - {} - ~my_var() {} + my_var_sp(const LEX_STRING& j, uint o, enum_field_types t, sp_head *s) + : my_var(j, LOCAL_VAR), offset(o), type(t), sp(s) { } + ~my_var_sp() { } + bool set(THD *thd, Item *val); +}; + +class my_var_user: public my_var { +public: + my_var_user(const LEX_STRING& j) + : my_var(j, SESSION_VAR) { } + ~my_var_user() { } + bool set(THD *thd, Item *val); }; class select_dumpvar :public select_result_interceptor { ha_rows row_count; public: List<my_var> var_list; - select_dumpvar() { var_list.empty(); row_count= 0;} + select_dumpvar(THD *thd_arg): select_result_interceptor(thd_arg) + { var_list.empty(); row_count= 0; } ~select_dumpvar() {} int prepare(List<Item> &list, SELECT_LEX_UNIT *u); int send_data(List<Item> &items); @@ -4180,6 +5419,52 @@ public: */ #define CF_CAN_GENERATE_ROW_EVENTS (1U << 9) +/** + Identifies statements which may deal with temporary tables and for which + temporary tables should be pre-opened to simplify privilege checks. +*/ +#define CF_PREOPEN_TMP_TABLES (1U << 10) + +/** + Identifies statements for which open handlers should be closed in the + beginning of the statement. +*/ +#define CF_HA_CLOSE (1U << 11) + +/** + Identifies statements that can be explained with EXPLAIN. +*/ +#define CF_CAN_BE_EXPLAINED (1U << 12) + +/** Identifies statements which may generate an optimizer trace */ +#define CF_OPTIMIZER_TRACE (1U << 14) + +/** + Identifies statements that should always be disallowed in + read only transactions. +*/ +#define CF_DISALLOW_IN_RO_TRANS (1U << 15) + +/** + Statement that need the binlog format to be unchanged. +*/ +#define CF_FORCE_ORIGINAL_BINLOG_FORMAT (1U << 16) + +/** + Statement that inserts new rows (INSERT, REPLACE, LOAD, ALTER TABLE) +*/ +#define CF_INSERTS_DATA (1U << 17) + +/** + Statement that updates existing rows (UPDATE, multi-update) +*/ +#define CF_UPDATES_DATA (1U << 18) + +/** + Not logged into slow log as "admin commands" +*/ +#define CF_ADMIN_COMMAND (1U << 19) + /* Bits in server_command_flags */ /** @@ -4197,6 +5482,11 @@ public: */ #define CF_SKIP_QUESTIONS (1U << 1) +/** + Do not check that wsrep snapshot is ready before allowing this command +*/ +#define CF_SKIP_WSREP_CHECK (1U << 2) + /* Inline functions */ inline bool add_item_to_list(THD *thd, Item *item) @@ -4206,7 +5496,7 @@ inline bool add_item_to_list(THD *thd, Item *item) inline bool add_value_to_list(THD *thd, Item *value) { - return thd->lex->value_list.push_back(value); + return thd->lex->value_list.push_back(value, thd->mem_root); } inline bool add_order_to_list(THD *thd, Item *item, bool asc) @@ -4224,6 +5514,13 @@ inline bool add_group_to_list(THD *thd, Item *item, bool asc) return thd->lex->current_select->add_group_to_list(thd, item, asc); } +inline Item *and_conds(THD *thd, Item *a, Item *b) +{ + if (!b) return a; + if (!a) return b; + return new (thd->mem_root) Item_cond_and(thd, a, b); +} + /* inline handler methods that need to know TABLE and THD structures */ inline void handler::increment_statistics(ulong SSV::*offset) const { @@ -4236,112 +5533,6 @@ inline void handler::decrement_statistics(ulong SSV::*offset) const status_var_decrement(table->in_use->status_var.*offset); } -inline int handler::ha_index_read_map(uchar * buf, const uchar * key, - key_part_map keypart_map, - enum ha_rkey_function find_flag) -{ - DBUG_ASSERT(inited==INDEX); - MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str); - increment_statistics(&SSV::ha_read_key_count); - int error= index_read_map(buf, key, keypart_map, find_flag); - if (!error) - update_index_statistics(); - table->status=error ? STATUS_NOT_FOUND: 0; - MYSQL_INDEX_READ_ROW_DONE(error); - return error; -} - - -/* - @note: Other index lookup/navigation functions require prior - handler->index_init() call. This function is different, it requires - that the scan is not initialized, and accepts "uint index" as an argument. -*/ - -inline int handler::ha_index_read_idx_map(uchar * buf, uint index, - const uchar * key, - key_part_map keypart_map, - enum ha_rkey_function find_flag) -{ - DBUG_ASSERT(inited==NONE); - MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str); - increment_statistics(&SSV::ha_read_key_count); - int error= index_read_idx_map(buf, index, key, keypart_map, find_flag); - if (!error) - { - update_rows_read(); - index_rows_read[index]++; - } - table->status=error ? STATUS_NOT_FOUND: 0; - MYSQL_INDEX_READ_ROW_DONE(error); - return error; -} - -inline int handler::ha_index_next(uchar * buf) -{ - DBUG_ASSERT(inited==INDEX); - MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str); - increment_statistics(&SSV::ha_read_next_count); - int error= index_next(buf); - if (!error) - update_index_statistics(); - table->status=error ? STATUS_NOT_FOUND: 0; - MYSQL_INDEX_READ_ROW_DONE(error); - return error; -} - -inline int handler::ha_index_prev(uchar * buf) -{ - DBUG_ASSERT(inited==INDEX); - MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str); - increment_statistics(&SSV::ha_read_prev_count); - int error= index_prev(buf); - if (!error) - update_index_statistics(); - table->status=error ? STATUS_NOT_FOUND: 0; - MYSQL_INDEX_READ_ROW_DONE(error); - return error; -} - -inline int handler::ha_index_first(uchar * buf) -{ - DBUG_ASSERT(inited==INDEX); - MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str); - increment_statistics(&SSV::ha_read_first_count); - int error= index_first(buf); - if (!error) - update_index_statistics(); - table->status=error ? STATUS_NOT_FOUND: 0; - MYSQL_INDEX_READ_ROW_DONE(error); - return error; -} - -inline int handler::ha_index_last(uchar * buf) -{ - DBUG_ASSERT(inited==INDEX); - MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str); - increment_statistics(&SSV::ha_read_last_count); - int error= index_last(buf); - if (!error) - update_index_statistics(); - table->status=error ? STATUS_NOT_FOUND: 0; - MYSQL_INDEX_READ_ROW_DONE(error); - return error; -} - -inline int handler::ha_index_next_same(uchar *buf, const uchar *key, - uint keylen) -{ - DBUG_ASSERT(inited==INDEX); - MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str); - increment_statistics(&SSV::ha_read_next_count); - int error= index_next_same(buf, key, keylen); - if (!error) - update_index_statistics(); - table->status=error ? STATUS_NOT_FOUND: 0; - MYSQL_INDEX_READ_ROW_DONE(error); - return error; -} inline int handler::ha_ft_read(uchar *buf) { @@ -4353,42 +5544,9 @@ inline int handler::ha_ft_read(uchar *buf) return error; } -inline int handler::ha_rnd_next(uchar *buf) -{ - MYSQL_READ_ROW_START(table_share->db.str, table_share->table_name.str, TRUE); - int error= rnd_next(buf); - if (!error) - { - update_rows_read(); - increment_statistics(&SSV::ha_read_rnd_next_count); - } - else if (error == HA_ERR_RECORD_DELETED) - increment_statistics(&SSV::ha_read_rnd_deleted_count); - else - increment_statistics(&SSV::ha_read_rnd_next_count); - - table->status=error ? STATUS_NOT_FOUND: 0; - MYSQL_READ_ROW_DONE(error); - return error; -} - -inline int handler::ha_rnd_pos(uchar *buf, uchar *pos) -{ - MYSQL_READ_ROW_START(table_share->db.str, table_share->table_name.str, FALSE); - increment_statistics(&SSV::ha_read_rnd_count); - int error= rnd_pos(buf, pos); - if (!error) - update_rows_read(); - table->status=error ? STATUS_NOT_FOUND: 0; - MYSQL_READ_ROW_DONE(error); - return error; -} - inline int handler::ha_rnd_pos_by_record(uchar *buf) { int error= rnd_pos_by_record(buf); - if (!error) - update_rows_read(); table->status=error ? STATUS_NOT_FOUND: 0; return error; } @@ -4404,24 +5562,74 @@ inline int handler::ha_read_first_row(uchar *buf, uint primary_key) inline int handler::ha_write_tmp_row(uchar *buf) { + int error; MYSQL_INSERT_ROW_START(table_share->db.str, table_share->table_name.str); increment_statistics(&SSV::ha_tmp_write_count); - int error= write_row(buf); + TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0, + { error= write_row(buf); }) MYSQL_INSERT_ROW_DONE(error); return error; } inline int handler::ha_update_tmp_row(const uchar *old_data, uchar *new_data) { + int error; MYSQL_UPDATE_ROW_START(table_share->db.str, table_share->table_name.str); increment_statistics(&SSV::ha_tmp_update_count); - int error= update_row(old_data, new_data); + TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_UPDATE_ROW, active_index, 0, + { error= update_row(old_data, new_data);}) MYSQL_UPDATE_ROW_DONE(error); return error; } + extern pthread_attr_t *get_connection_attrib(void); +/** + Set thread entering a condition + + This function should be called before putting a thread to wait for + a condition. @a mutex should be held before calling this + function. After being waken up, @f thd_exit_cond should be called. + + @param thd The thread entering the condition, NULL means current thread + @param cond The condition the thread is going to wait for + @param mutex The mutex associated with the condition, this must be + held before call this function + @param stage The new process message for the thread + @param old_stage The old process message for the thread + @param src_function The caller source function name + @param src_file The caller source file name + @param src_line The caller source line number +*/ +void thd_enter_cond(MYSQL_THD thd, mysql_cond_t *cond, mysql_mutex_t *mutex, + const PSI_stage_info *stage, PSI_stage_info *old_stage, + const char *src_function, const char *src_file, + int src_line); + +#define THD_ENTER_COND(P1, P2, P3, P4, P5) \ + thd_enter_cond(P1, P2, P3, P4, P5, __func__, __FILE__, __LINE__) + +/** + Set thread leaving a condition + + This function should be called after a thread being waken up for a + condition. + + @param thd The thread entering the condition, NULL means current thread + @param stage The process message, ususally this should be the old process + message before calling @f thd_enter_cond + @param src_function The caller source function name + @param src_file The caller source file name + @param src_line The caller source line number +*/ +void thd_exit_cond(MYSQL_THD thd, const PSI_stage_info *stage, + const char *src_function, const char *src_file, + int src_line); + +#define THD_EXIT_COND(P1, P2) \ + thd_exit_cond(P1, P2, __func__, __FILE__, __LINE__) + #endif /* MYSQL_SERVER */ #endif /* SQL_CLASS_INCLUDED */ |