diff options
Diffstat (limited to 'sql/sql_class.h')
-rw-r--r-- | sql/sql_class.h | 783 |
1 files changed, 561 insertions, 222 deletions
diff --git a/sql/sql_class.h b/sql/sql_class.h index 4d14b42b065..1e4f0225397 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1,6 +1,6 @@ /* Copyright (c) 2000, 2016, Oracle and/or its affiliates. - Copyright (c) 2009, 2016, MariaDB + Copyright (c) 2009, 2017, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -21,6 +21,7 @@ /* Classes in mysql */ #include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */ +#include "dur_prop.h" #include <waiting_threads.h> #include "sql_const.h" #include <mysql/plugin_audit.h> @@ -36,6 +37,7 @@ #include "violite.h" /* vio_is_connected */ #include "thr_lock.h" /* thr_lock_type, THR_LOCK_DATA, THR_LOCK_INFO */ #include "thr_timer.h" +#include "thr_malloc.h" #include <my_tree.h> #include "sql_digest_stream.h" // sql_digest_state @@ -45,6 +47,7 @@ #include <mysql/psi/mysql_idle.h> #include <mysql/psi/mysql_table.h> #include <mysql_com_server.h> +#include "session_tracker.h" extern "C" void set_thd_stage_info(void *thd, @@ -151,7 +154,7 @@ extern MYSQL_PLUGIN_IMPORT const char **errmesg; extern bool volatile shutdown_in_progress; extern "C" LEX_STRING * thd_query_string (MYSQL_THD thd); -extern "C" char **thd_query(MYSQL_THD thd); +extern "C" size_t thd_query_safe(MYSQL_THD thd, char *buf, size_t buflen); /** @class CSET_STRING @@ -177,11 +180,10 @@ public: } inline char *str() const { return string.str; } - inline uint32 length() const { return string.length; } + inline size_t length() const { return string.length; } CHARSET_INFO *charset() const { return cs; } friend LEX_STRING * thd_query_string (MYSQL_THD thd); - friend char **thd_query(MYSQL_THD thd); }; @@ -226,6 +228,7 @@ typedef struct st_copy_info { List<Item> *update_values; /* for VIEW ... WITH CHECK OPTION */ TABLE_LIST *view; + TABLE_LIST *table_list; /* Normal table */ } COPY_INFO; @@ -256,7 +259,7 @@ public: class Alter_drop :public Sql_alloc { public: - enum drop_type {KEY, COLUMN, FOREIGN_KEY }; + enum drop_type {KEY, COLUMN, FOREIGN_KEY, CHECK_CONSTRAINT }; const char *name; enum drop_type type; bool drop_if_exists; @@ -271,15 +274,21 @@ public: */ Alter_drop *clone(MEM_ROOT *mem_root) const { return new (mem_root) Alter_drop(*this); } + const char *type_name() + { + return type == COLUMN ? "COLUMN" : + type == CHECK_CONSTRAINT ? "CONSTRAINT" : + type == KEY ? "INDEX" : "FOREIGN KEY"; + } }; class Alter_column :public Sql_alloc { public: const char *name; - Item *def; - Alter_column(const char *par_name,Item *literal) - :name(par_name), def(literal) {} + Virtual_column_info *default_value; + Alter_column(const char *par_name, Virtual_column_info *expr) + :name(par_name), default_value(expr) {} /** Used to make a clone of this object for ALTER/CREATE TABLE @sa comment for Key_part_spec::clone @@ -469,16 +478,27 @@ enum killed_state KILL_TIMEOUT= 8, KILL_TIMEOUT_HARD= 9, /* + When binlog reading thread connects to the server it kills + all the binlog threads with the same ID. + */ + KILL_SLAVE_SAME_ID= 10, + /* 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= 10, - KILL_CONNECTION_HARD= 11, - KILL_SYSTEM_THREAD= 12, - KILL_SYSTEM_THREAD_HARD= 13, - KILL_SERVER= 14, - KILL_SERVER_HARD= 15 + KILL_CONNECTION= 12, + KILL_CONNECTION_HARD= 13, + KILL_SYSTEM_THREAD= 14, + KILL_SYSTEM_THREAD_HARD= 15, + KILL_SERVER= 16, + KILL_SERVER_HARD= 17, + /* + Used in threadpool to signal wait timeout. + */ + KILL_WAIT_TIMEOUT= 18, + KILL_WAIT_TIMEOUT_HARD= 19 + }; #define killed_mask_hard(killed) ((killed_state) ((killed) & ~KILL_HARD_BIT)) @@ -501,8 +521,6 @@ 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 { /* @@ -523,7 +541,8 @@ typedef struct system_variables uint dynamic_variables_size; /* how many bytes are in use */ ulonglong max_heap_table_size; - ulonglong tmp_table_size; + ulonglong tmp_memory_table_size; + ulonglong tmp_disk_table_size; ulonglong long_query_time; ulonglong max_statement_time; ulonglong optimizer_switch; @@ -561,11 +580,13 @@ typedef struct system_variables */ ulong saved_auto_increment_increment, saved_auto_increment_offset; #endif /* WITH_WSREP */ + uint eq_range_index_dive_limit; ulong lock_wait_timeout; ulong join_cache_level; ulong max_allowed_packet; ulong max_error_count; ulong max_length_for_sort_data; + ulong max_recursive_iterations; ulong max_sort_length; ulong max_tmp_tables; ulong max_insert_delayed_threads; @@ -636,6 +657,7 @@ typedef struct system_variables my_bool old_alter_table; my_bool old_passwords; my_bool big_tables; + my_bool only_standard_compliant_cte; my_bool query_cache_strip_comments; my_bool sql_log_slow; my_bool sql_log_bin; @@ -666,7 +688,7 @@ typedef struct system_variables /* Error messages */ MY_LOCALE *lc_messages; - const char **errmsgs; /* lc_messages->errmsg->errmsgs */ + const char ***errmsgs; /* lc_messages->errmsg->errmsgs */ /* Locale Support */ MY_LOCALE *lc_time_names; @@ -689,6 +711,12 @@ typedef struct system_variables my_bool pseudo_slave_mode; + char *session_track_system_variables; + ulong session_track_transaction_info; + my_bool session_track_schema; + my_bool session_track_state_change; + + ulong threadpool_priority; } SV; /** @@ -703,6 +731,7 @@ typedef struct system_status_var ulong com_create_tmp_table; ulong com_drop_tmp_table; ulong com_other; + ulong com_multi; ulong com_stmt_prepare; ulong com_stmt_reprepare; @@ -779,6 +808,7 @@ typedef struct system_status_var ulong feature_timezone; /* +1 when XPATH is used */ ulong feature_trigger; /* +1 opening a table with triggers */ ulong feature_xml; /* +1 when XPATH is used */ + ulong feature_window_functions; /* +1 when window functions are used */ /* From MASTER_GTID_WAIT usage */ ulong master_gtid_wait_timeouts; /* Number of timeouts */ @@ -827,8 +857,7 @@ typedef struct system_status_var Global status variables */ -extern ulong feature_files_opened_with_delayed_keys; - +extern ulong feature_files_opened_with_delayed_keys, feature_check_constraint; void add_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var); @@ -1061,7 +1090,10 @@ public: inline char *query() const { return query_string.str(); } - inline uint32 query_length() const { return query_string.length(); } + inline uint32 query_length() const + { + return static_cast<uint32>(query_string.length()); + } CHARSET_INFO *query_charset() const { return query_string.charset(); } void set_query_inner(const CSET_STRING &string_arg) { @@ -1234,7 +1266,8 @@ public: priv_user - The user privilege we are using. May be "" for anonymous user. ip - client IP */ - char *host, *user, *ip; + const char *host; + char *user, *ip; char priv_user[USERNAME_LENGTH]; char proxy_user[USERNAME_LENGTH + MAX_HOSTNAME + 5]; /* The host privilege we are using */ @@ -1281,7 +1314,40 @@ public: */ struct Item_change_record; -typedef I_List<Item_change_record> Item_change_list; +class Item_change_list +{ + I_List<Item_change_record> change_list; +public: + void nocheck_register_item_tree_change(Item **place, Item *old_value, + MEM_ROOT *runtime_memroot); + void check_and_register_item_tree_change(Item **place, Item **new_value, + MEM_ROOT *runtime_memroot); + void rollback_item_tree_changes(); + void move_elements_to(Item_change_list *to) + { + change_list.move_elements_to(&to->change_list); + } + bool is_empty() { return change_list.is_empty(); } +}; + + +class Item_change_list_savepoint: public Item_change_list +{ +public: + Item_change_list_savepoint(Item_change_list *list) + { + list->move_elements_to(this); + } + void rollback(Item_change_list *list) + { + list->rollback_item_tree_changes(); + move_elements_to(list); + } + ~Item_change_list_savepoint() + { + DBUG_ASSERT(is_empty()); + } +}; /** @@ -1298,6 +1364,61 @@ enum enum_locked_tables_mode LTM_always_last }; +/** + The following structure is an extension to TABLE_SHARE and is + exclusively for temporary tables. + + @note: + Although, TDC_element has data members (like next, prev & + all_tables) to store the list of TABLE_SHARE & TABLE objects + related to a particular TABLE_SHARE, they cannot be moved to + TABLE_SHARE in order to be reused for temporary tables. This + is because, as concurrent threads iterating through hash of + TDC_element's may need access to all_tables, but if all_tables + is made part of TABLE_SHARE, then TDC_element->share->all_tables + is not always guaranteed to be valid, as TDC_element can live + longer than TABLE_SHARE. +*/ +struct TMP_TABLE_SHARE : public TABLE_SHARE +{ +private: + /* + Link to all temporary table shares. Declared as private to + avoid direct manipulation with those objects. One should + use methods of I_P_List template instead. + */ + TMP_TABLE_SHARE *tmp_next; + TMP_TABLE_SHARE **tmp_prev; + + friend struct All_tmp_table_shares; + +public: + /* + Doubly-linked (back-linked) lists of used and unused TABLE objects + for this share. + */ + All_share_tables_list all_tmp_tables; +}; + +/** + Helper class which specifies which members of TMP_TABLE_SHARE are + used for participation in the list of temporary tables. +*/ + +struct All_tmp_table_shares +{ + static inline TMP_TABLE_SHARE **next_ptr(TMP_TABLE_SHARE *l) + { + return &l->tmp_next; + } + static inline TMP_TABLE_SHARE ***prev_ptr(TMP_TABLE_SHARE *l) + { + return &l->tmp_prev; + } +}; + +/* Also used in rpl_rli.h. */ +typedef I_P_List <TMP_TABLE_SHARE, All_tmp_table_shares> All_tmp_tables_list; /** Class that holds information about tables which were opened and locked @@ -1327,15 +1448,25 @@ public: base tables that were opened with @see open_tables(). */ TABLE *open_tables; + /** - List of temporary tables used by this thread. Contains user-level - temporary tables, created with CREATE TEMPORARY TABLE, and - internal temporary tables, created, e.g., to resolve a SELECT, + A list of temporary tables used by this thread. This includes + user-level temporary tables, created with CREATE TEMPORARY TABLE, + and internal temporary tables, created, e.g., to resolve a SELECT, or for an intermediate table used in ALTER. - XXX Why are internal temporary tables added to this list? */ - TABLE *temporary_tables; + All_tmp_tables_list *temporary_tables; + + /* + Derived tables. + */ TABLE *derived_tables; + + /* + Temporary tables created for recursive table references. + */ + TABLE *rec_tables; + /* During a MySQL session, one can lock tables in two modes: automatic or manual. In automatic mode all necessary tables are locked just before @@ -1413,8 +1544,12 @@ public: void reset_open_tables_state(THD *thd) { - open_tables= temporary_tables= derived_tables= 0; - extra_lock= lock= 0; + open_tables= 0; + temporary_tables= 0; + derived_tables= 0; + rec_tables= 0; + extra_lock= 0; + lock= 0; locked_tables_mode= LTM_NONE; state_flags= 0U; m_reprepare_observer= NULL; @@ -1466,9 +1601,9 @@ public: Discrete_intervals_list auto_inc_intervals_forced; ulonglong limit_found_rows; ha_rows cuted_fields, sent_row_count, examined_row_count; - ulong client_capabilities; + ulonglong client_capabilities; ulong query_plan_flags; - uint in_sub_stmt; + uint in_sub_stmt; /* 0, SUB_STMT_TRIGGER or SUB_STMT_FUNCTION */ bool enable_slow_log; bool last_insert_id_used; SAVEPOINT *savepoints; @@ -1486,8 +1621,8 @@ enum enum_thread_type 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 + SYSTEM_THREAD_SLAVE_BACKGROUND= 64, + SYSTEM_THREAD_GENERIC= 128 }; inline char const * @@ -1502,7 +1637,7 @@ show_system_thread(enum_thread_type thread) RETURN_NAME_AS_STRING(SYSTEM_THREAD_SLAVE_SQL); RETURN_NAME_AS_STRING(SYSTEM_THREAD_EVENT_SCHEDULER); RETURN_NAME_AS_STRING(SYSTEM_THREAD_EVENT_WORKER); - RETURN_NAME_AS_STRING(SYSTEM_THREAD_SLAVE_INIT); + RETURN_NAME_AS_STRING(SYSTEM_THREAD_SLAVE_BACKGROUND); default: sprintf(buf, "<UNKNOWN SYSTEM THREAD: %d>", thread); return buf; @@ -1515,6 +1650,7 @@ show_system_thread(enum_thread_type thread) Internal error handlers are exception handlers used by the server implementation. */ + class Internal_error_handler { protected: @@ -1552,7 +1688,7 @@ public: virtual bool handle_condition(THD *thd, uint sql_errno, const char* sqlstate, - Sql_condition::enum_warning_level level, + Sql_condition::enum_warning_level *level, const char* msg, Sql_condition ** cond_hdl) = 0; @@ -1573,7 +1709,7 @@ public: bool handle_condition(THD *thd, uint sql_errno, const char* sqlstate, - Sql_condition::enum_warning_level level, + Sql_condition::enum_warning_level *level, const char* msg, Sql_condition ** cond_hdl) { @@ -1595,11 +1731,11 @@ public: bool handle_condition(THD *thd, uint sql_errno, const char* sqlstate, - Sql_condition::enum_warning_level level, + Sql_condition::enum_warning_level *level, const char* msg, Sql_condition ** cond_hdl) { - if (level == Sql_condition::WARN_LEVEL_ERROR) + if (*level == Sql_condition::WARN_LEVEL_ERROR) errors++; return false; } @@ -1623,7 +1759,7 @@ public: bool handle_condition(THD *thd, uint sql_errno, const char* sqlstate, - Sql_condition::enum_warning_level level, + Sql_condition::enum_warning_level *level, const char* msg, Sql_condition ** cond_hdl); @@ -1632,6 +1768,30 @@ private: /** + Internal error handler to process an error from MDL_context::upgrade_lock() + and mysql_lock_tables(). Used by implementations of HANDLER READ and + LOCK TABLES LOCAL. +*/ + +class MDL_deadlock_and_lock_abort_error_handler: public Internal_error_handler +{ +public: + virtual + bool handle_condition(THD *thd, + uint sql_errno, + const char *sqlstate, + Sql_condition::enum_warning_level *level, + const char* msg, + Sql_condition **cond_hdl); + + bool need_reopen() const { return m_need_reopen; }; + void init() { m_need_reopen= FALSE; }; +private: + bool m_need_reopen; +}; + + +/** Tables that were locked with LOCK TABLES statement. Encapsulates a list of TABLE_LIST instances for tables @@ -1663,20 +1823,23 @@ private: TABLE_LIST *m_locked_tables; TABLE_LIST **m_locked_tables_last; /** An auxiliary array used only in reopen_tables(). */ - TABLE **m_reopen_array; + TABLE_LIST **m_reopen_array; /** Count the number of tables in m_locked_tables list. We can't rely on thd->lock->table_count because it excludes non-transactional temporary tables. We need to know an exact number of TABLE objects. */ - size_t m_locked_tables_count; + uint m_locked_tables_count; public: + bool some_table_marked_for_reopen; + Locked_tables_list() :m_locked_tables(NULL), m_locked_tables_last(&m_locked_tables), m_reopen_array(NULL), - m_locked_tables_count(0) + m_locked_tables_count(0), + some_table_marked_for_reopen(0) { init_sql_alloc(&m_locked_tables_root, MEM_ROOT_BLOCK_SIZE, 0, MYF(MY_THREAD_SPECIFIC)); @@ -1699,6 +1862,7 @@ public: 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); + void mark_table_for_reopen(THD *thd, TABLE *table); }; @@ -1952,6 +2116,14 @@ void dbug_serve_apcs(THD *thd, int n_calls); */ class THD :public Statement, + /* + This is to track items changed during execution of a prepared + statement/stored procedure. It's created by + nocheck_register_item_tree_change() in memory root of THD, + and freed in rollback_item_tree_changes(). + For conventional execution it's always empty. + */ + public Item_change_list, public MDL_context_owner, public Open_tables_state { @@ -1968,6 +2140,19 @@ private: inline bool is_conventional() const { DBUG_ASSERT(0); return Statement::is_conventional(); } + void dec_thread_count(void) + { + DBUG_ASSERT(thread_count > 0); + thread_safe_decrement32(&thread_count); + signal_thd_deleted(); + } + + + void inc_thread_count(void) + { + thread_safe_increment32(&thread_count); + } + public: MDL_context mdl_context; @@ -2043,6 +2228,13 @@ public: /* all prepared statements and cursors of this connection */ Statement_map stmt_map; + + /* Last created prepared statement */ + Statement *last_stmt; + inline void set_last_stmt(Statement *stmt) + { last_stmt= (is_error() ? NULL : stmt); } + inline void clear_last_stmt() { last_stmt= NULL; } + /* A pointer to the stack frame of handle_one_connection(), which is called first in the thread for handling a client @@ -2125,7 +2317,7 @@ public: /* Needed by MariaDB semi sync replication */ Trans_binlog_info *semisync_info; - ulong client_capabilities; /* What the client supports */ + ulonglong client_capabilities; /* What the client supports */ ulong max_client_packet_length; HASH handler_tables_hash; @@ -2139,6 +2331,9 @@ public: uint dbug_sentry; // watch out for memory corruption #endif struct st_my_thread_var *mysys_var; + + /* Original charset number from the first client packet, or COM_CHANGE_USER*/ + CHARSET_INFO *org_charset; private: /* Type of current query: COM_STMT_PREPARE, COM_QUERY, etc. Set from @@ -2167,7 +2362,7 @@ public: bool report_to_client; /* true, if we will send progress report packets to a client - (client has requested them, see CLIENT_PROGRESS; report_to_client + (client has requested them, see MARIADB_CLIENT_PROGRESS; report_to_client is true; not in sub-statement) */ bool report; @@ -2402,14 +2597,6 @@ public: #ifdef SIGNAL_WITH_VIO_CLOSE Vio* active_vio; #endif - /* - This is to track items changed during execution of a prepared - statement/stored procedure. It's created by - nocheck_register_item_tree_change() in memory root of THD, and freed in - rollback_item_tree_changes(). For conventional execution it's always - empty. - */ - Item_change_list change_list; /* A permanent memory area of the statement. For conventional @@ -2425,6 +2612,8 @@ public: */ Query_arena *stmt_arena; + void *bulk_param; + /* map for tables that will be updated for a multi-table update query statement, for other query statements, this will be zero. @@ -2721,7 +2910,7 @@ public: ulong query_plan_flags; ulong query_plan_fsort_passes; pthread_t real_id; /* For debugging */ - my_thread_id thread_id; + my_thread_id thread_id, thread_dbug_id; uint32 os_thread_id; uint tmp_table, global_disable_checkpoint; uint server_status,open_options; @@ -2842,7 +3031,7 @@ public: /* for IS NULL => = last_insert_id() fix in remove_eq_conds() */ bool substitute_null_with_insert_id; bool in_lock_tables; - bool bootstrap, cleanup_done; + bool bootstrap, cleanup_done, free_connection_done; /** is set if some thread specific value(s) used in a statement. */ bool thread_specific_used; @@ -2986,7 +3175,7 @@ public: /* Debug Sync facility. See debug_sync.cc. */ struct st_debug_sync_control *debug_sync_control; #endif /* defined(ENABLED_DEBUG_SYNC) */ - THD(bool is_wsrep_applier= false); + THD(my_thread_id id, bool is_wsrep_applier= false); ~THD(); @@ -3006,6 +3195,8 @@ public: void change_user(void); void cleanup(void); void cleanup_after_query(); + void free_connection(); + void reset_for_reuse(); bool store_globals(); void reset_globals(); #ifdef SIGNAL_WITH_VIO_CLOSE @@ -3013,7 +3204,6 @@ public: { mysql_mutex_lock(&LOCK_thd_data); active_vio = vio; - vio_set_thread_id(vio, pthread_self()); mysql_mutex_unlock(&LOCK_thd_data); } inline void clear_active_vio() @@ -3178,6 +3368,12 @@ public: } ulonglong current_utime() { return microsecond_interval_timer(); } + /* Tell SHOW PROCESSLIST to show time from this point */ + inline void set_time_for_next_stage() + { + utime_after_query= current_utime(); + } + /** Update server status after execution of a top level statement. Currently only checks if a query was slow, and assigns @@ -3187,7 +3383,7 @@ public: */ void update_server_status() { - utime_after_query= current_utime(); + set_time_for_next_stage(); if (utime_after_query > utime_after_lock + variables.long_query_time) server_status|= SERVER_QUERY_WAS_SLOW; } @@ -3406,6 +3602,12 @@ public: To raise this flag, use my_error(). */ inline bool is_error() const { return m_stmt_da->is_error(); } + void set_bulk_execution(void *bulk) + { + bulk_param= bulk; + m_stmt_da->set_bulk_execution(MY_TEST(bulk)); + } + bool is_bulk_op() const { return MY_TEST(bulk_param); } /// Returns Diagnostics-area for the current statement. Diagnostics_area *get_stmt_da() const @@ -3417,6 +3619,22 @@ public: inline CHARSET_INFO *charset() { return variables.character_set_client; } void update_charset(); + void update_charset(CHARSET_INFO *character_set_client, + CHARSET_INFO *collation_connection) + { + variables.character_set_client= character_set_client; + variables.collation_connection= collation_connection; + update_charset(); + } + void update_charset(CHARSET_INFO *character_set_client, + CHARSET_INFO *collation_connection, + CHARSET_INFO *character_set_results) + { + variables.character_set_client= character_set_client; + variables.collation_connection= collation_connection; + variables.character_set_results= character_set_results; + update_charset(); + } inline Query_arena *activate_stmt_arena_if_needed(Query_arena *backup) { @@ -3463,11 +3681,6 @@ public: */ memcpy((char*) place, new_value, sizeof(*new_value)); } - void nocheck_register_item_tree_change(Item **place, Item *old_value, - MEM_ROOT *runtime_memroot); - void check_and_register_item_tree_change(Item **place, Item **new_value, - MEM_ROOT *runtime_memroot); - void rollback_item_tree_changes(); /* Cleanup statement parse state (parse tree, lex) and execution @@ -3512,7 +3725,8 @@ public: 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= (err_info*) alloc_root(&main_mem_root, sizeof(*killed_err)); + if (killed_err) { killed_err->no= killed_errno_arg; ::strmake((char*) killed_err->msg, killed_err_msg_arg, @@ -3664,13 +3878,13 @@ public: */ DBUG_PRINT("debug", ("temporary_tables: %s, in_sub_stmt: %s, system_thread: %s", - YESNO(temporary_tables), YESNO(in_sub_stmt), + YESNO(has_thd_temporary_tables()), YESNO(in_sub_stmt), show_system_thread(system_thread))); if (in_sub_stmt == 0) { if (wsrep_binlog_format() == BINLOG_FORMAT_ROW) set_current_stmt_binlog_format_row(); - else if (temporary_tables == NULL) + else if (!has_thd_temporary_tables()) set_current_stmt_binlog_format_stmt(); } DBUG_VOID_RETURN; @@ -3721,7 +3935,7 @@ public: mysql_mutex_unlock(&LOCK_thd_data); #ifdef HAVE_PSI_THREAD_INTERFACE if (result) - PSI_THREAD_CALL(set_thread_db)(new_db, new_db_len); + PSI_THREAD_CALL(set_thread_db)(new_db, (int) new_db_len); #endif return result; } @@ -3746,7 +3960,7 @@ public: 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); + PSI_THREAD_CALL(set_thread_db)(new_db, (int) new_db_len); #endif } } @@ -3759,11 +3973,28 @@ public: { if (db == NULL) { - my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0)); - return TRUE; + /* + No default database is set. In this case if it's guaranteed that + no CTE can be used in the statement then we can throw an error right + now at the parser stage. Otherwise the decision about throwing such + a message must be postponed until a post-parser stage when we are able + to resolve all CTE names as we don't need this message to be thrown + for any CTE references. + */ + if (!lex->with_clauses_list) + { + my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0)); + return TRUE; + } + /* This will allow to throw an error later for non-CTE references */ + *p_db= NULL; + *p_db_length= 0; + } + else + { + *p_db= strmake(db, db_length); + *p_db_length= db_length; } - *p_db= strmake(db, db_length); - *p_db_length= db_length; return FALSE; } thd_scheduler event_scheduler; @@ -3790,7 +4021,7 @@ private: */ bool handle_condition(uint sql_errno, const char* sqlstate, - Sql_condition::enum_warning_level level, + Sql_condition::enum_warning_level *level, const char* msg, Sql_condition ** cond_hdl); @@ -4062,11 +4293,10 @@ 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: +#ifndef EMBEDDED_LIBRARY + Session_tracker session_tracker; +#endif //EMBEDDED_LIBRARY /* Flag, mutex and condition for a thread to wait for a signal from another thread. @@ -4081,28 +4311,98 @@ public: 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; +private: + rpl_gtid m_last_commit_gtid; + +public: + rpl_gtid get_last_commit_gtid() { return m_last_commit_gtid; } + void set_last_commit_gtid(rpl_gtid >id); - 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(); +/* Members related to temporary tables. */ +public: + /* Opened table states. */ + enum Temporary_table_state { + TMP_TABLE_IN_USE, + TMP_TABLE_NOT_IN_USE, + TMP_TABLE_ANY + }; + bool has_thd_temporary_tables(); + + TABLE *create_and_open_tmp_table(handlerton *hton, + LEX_CUSTRING *frm, + const char *path, + const char *db, + const char *table_name, + bool open_in_engine); + + TABLE *find_temporary_table(const char *db, const char *table_name, + Temporary_table_state state= TMP_TABLE_IN_USE); + TABLE *find_temporary_table(const TABLE_LIST *tl, + Temporary_table_state state= TMP_TABLE_IN_USE); + + TMP_TABLE_SHARE *find_tmp_table_share_w_base_key(const char *key, + uint key_length); + TMP_TABLE_SHARE *find_tmp_table_share(const char *db, + const char *table_name); + TMP_TABLE_SHARE *find_tmp_table_share(const TABLE_LIST *tl); + TMP_TABLE_SHARE *find_tmp_table_share(const char *key, uint key_length); + + bool open_temporary_table(TABLE_LIST *tl); + bool open_temporary_tables(TABLE_LIST *tl); + + bool close_temporary_tables(); + bool rename_temporary_table(TABLE *table, const char *db, + const char *table_name); + bool drop_temporary_table(TABLE *table, bool *is_trans, bool delete_table); + bool rm_temporary_table(handlerton *hton, const char *path); + void mark_tmp_tables_as_free_for_reuse(); + void mark_tmp_table_as_free_for_reuse(TABLE *table); + + TMP_TABLE_SHARE* save_tmp_table_share(TABLE *table); + void restore_tmp_table_share(TMP_TABLE_SHARE *share); + void close_unused_temporary_table_instances(const TABLE_LIST *tl); + +private: + /* Whether a lock has been acquired? */ + bool m_tmp_tables_locked; + + bool has_temporary_tables(); + uint create_tmp_table_def_key(char *key, const char *db, + const char *table_name); + TMP_TABLE_SHARE *create_temporary_table(handlerton *hton, LEX_CUSTRING *frm, + const char *path, const char *db, + const char *table_name); + TABLE *find_temporary_table(const char *key, uint key_length, + Temporary_table_state state); + TABLE *open_temporary_table(TMP_TABLE_SHARE *share, const char *alias, + bool open_in_engine); + bool find_and_use_tmp_table(const TABLE_LIST *tl, TABLE **out_table); + bool use_temporary_table(TABLE *table, TABLE **out_table); + void close_temporary_table(TABLE *table); + bool log_events_and_free_tmp_shares(); + void free_tmp_table_share(TMP_TABLE_SHARE *share, bool delete_table); + void free_temporary_table(TABLE *table); + bool lock_temporary_tables(); + void unlock_temporary_tables(); + + inline uint tmpkeyval(TMP_TABLE_SHARE *share) + { + return uint4korr(share->table_cache_key.str + + share->table_cache_key.length - 4); + } + + inline TMP_TABLE_SHARE *tmp_table_share(TABLE *table) + { + DBUG_ASSERT(table->s->tmp_table); + return static_cast<TMP_TABLE_SHARE *>(table->s); + } + +public: inline ulong wsrep_binlog_format() const { return WSREP_FORMAT(variables.binlog_format); @@ -4151,6 +4451,14 @@ public: ulong wsrep_affected_rows; bool wsrep_replicate_GTID; bool wsrep_skip_wsrep_GTID; + /* This flag is set when innodb do an intermediate commit to + processing the LOAD DATA INFILE statement by splitting it into 10K + rows chunks. If flag is set, then binlog rotation is not performed + while intermediate transaction try to commit, because in this case + rotation causes unregistration of innodb handler. Later innodb handler + registered again, but replication of last chunk of rows is skipped + by the innodb engine: */ + bool wsrep_split_flag; #endif /* WITH_WSREP */ /* Handling of timeouts for commands */ @@ -4196,8 +4504,40 @@ public: (THD_TRANS::DID_WAIT | THD_TRANS::CREATED_TEMP_TABLE | THD_TRANS::DROPPED_TEMP_TABLE | THD_TRANS::DID_DDL)); } + /* + Reset current_linfo + Setting current_linfo to 0 needs to be done with LOCK_thread_count to + ensure that adjust_linfo_offsets doesn't use a structure that may + be deleted. + */ + inline void reset_current_linfo() + { + mysql_mutex_lock(&LOCK_thread_count); + current_linfo= 0; + mysql_mutex_unlock(&LOCK_thread_count); + } }; +inline void add_to_active_threads(THD *thd) +{ + mysql_mutex_lock(&LOCK_thread_count); + threads.append(thd); + mysql_mutex_unlock(&LOCK_thread_count); +} + +/* + This should be called when you want to delete a thd that was not + running any queries. + This function will assert that the THD is linked. +*/ + +inline void unlink_not_visible_thd(THD *thd) +{ + thd->assert_linked(); + mysql_mutex_lock(&LOCK_thread_count); + thd->unlink(); + mysql_mutex_unlock(&LOCK_thread_count); +} /** A short cut for thd->get_stmt_da()->set_ok_status(). */ @@ -4217,6 +4557,8 @@ my_eof(THD *thd) { thd->set_row_count_func(-1); thd->get_stmt_da()->set_eof_status(thd); + + TRANSACT_TRACKER(add_trx_state(thd, TX_RESULT_SET)); } #define tmp_disable_binlog(A) \ @@ -4296,12 +4638,13 @@ 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(). + also uses unit->get_column_types(). */ SELECT_LEX_UNIT *unit; /* Something used only by the parser: */ public: select_result(THD *thd_arg): select_result_sink(thd_arg) {} + void set_unit(SELECT_LEX_UNIT *unit_arg) { unit= unit_arg; } virtual ~select_result() {}; /** Change wrapped select_result. @@ -4358,6 +4701,9 @@ public: #endif virtual void update_used_tables() {} + /* this method is called just before the first row of the table can be read */ + virtual void prepare_to_read_rows() {} + void reset_offset_limit() { unit->offset_limit_cnt= 0; @@ -4589,6 +4935,7 @@ class select_create: public select_insert { /* m_lock or thd->extra_lock */ MYSQL_LOCK **m_plock; bool exit_done; + TMP_TABLE_SHARE *saved_tmp_table_share; public: select_create(THD *thd_arg, TABLE_LIST *table_arg, @@ -4596,12 +4943,14 @@ public: 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), + select_insert(thd_arg, table_arg, 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), exit_done(0) + m_plock(NULL), exit_done(0), + saved_tmp_table_share(0) {} int prepare(List<Item> &list, SELECT_LEX_UNIT *u); @@ -4647,16 +4996,9 @@ inline uint tmp_table_max_key_parts() { return MI_MAX_KEY_SEG; } class TMP_TABLE_PARAM :public Sql_alloc { -private: - /* Prevent use of these (not safe because of lists and copy_field) */ - TMP_TABLE_PARAM(const TMP_TABLE_PARAM &); - void operator=(TMP_TABLE_PARAM &); - public: List<Item> copy_funcs; - List<Item> save_copy_funcs; Copy_field *copy_field, *copy_field_end; - Copy_field *save_copy_field, *save_copy_field_end; uchar *group_buff; Item **items_to_copy; /* Fields in tmp table */ TMP_ENGINE_COLUMNDEF *recinfo, *start_recinfo; @@ -4691,7 +5033,13 @@ public: uint hidden_field_count; uint group_parts,group_length,group_null_parts; uint quick_group; - bool using_indirect_summary_function; + /** + Enabled when we have atleast one outer_sum_func. Needed when used + along with distinct. + + @see create_tmp_table + */ + bool using_outer_summary_function; CHARSET_INFO *table_charset; bool schema_table; /* TRUE if the temp table is created for subquery materialization. */ @@ -4721,9 +5069,10 @@ public: TMP_TABLE_PARAM() :copy_field(0), group_parts(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) + using_outer_summary_function(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) {} ~TMP_TABLE_PARAM() { @@ -4735,17 +5084,13 @@ public: if (copy_field) /* Fix for Intel compiler */ { delete [] copy_field; - save_copy_field= copy_field= NULL; - save_copy_field_end= copy_field_end= NULL; + copy_field= NULL; + copy_field_end= NULL; } } - void free_copy_field_data() - { - for (Copy_field *ptr= copy_field ; ptr != copy_field_end ; ptr++) - ptr->tmp.free(); - } }; + class select_union :public select_result_interceptor { public: @@ -4783,6 +5128,36 @@ public: }; +class select_union_recursive :public select_union +{ + public: + /* The temporary table with the new records generated by one iterative step */ + TABLE *incr_table; + /* One of tables from the list rec_tables (determined dynamically) */ + TABLE *first_rec_table_to_update; + /* The temporary tables used for recursive table references */ + List<TABLE> rec_tables; + /* + The count of how many times cleanup() was called with cleaned==false + for the unit specifying the recursive CTE for which this object was created + or for the unit specifying a CTE that mutually recursive with this CTE. + */ + uint cleanup_count; + + select_union_recursive(THD *thd_arg): + select_union(thd_arg), + incr_table(0), first_rec_table_to_update(0), cleanup_count(0) {}; + + int send_data(List<Item> &items); + bool create_result_table(THD *thd, List<Item> *column_types, + bool is_distinct, ulonglong options, + const char *alias, + bool bit_fields_as_long, + bool create_table, + bool keep_row_order= FALSE); + void cleanup(); +}; + /** UNION result that is passed directly to the receiving select_result without filling a temporary table. @@ -5059,16 +5434,19 @@ public: /* Structs used when sorting */ +struct SORT_FIELD_ATTR +{ + uint length; /* Length of sort field */ + uint suffix_length; /* Length suffix (0-4) */ +}; + -typedef struct st_sort_field { +struct SORT_FIELD: public SORT_FIELD_ATTR +{ Field *field; /* Field to sort */ Item *item; /* Item if not sorting fields */ - uint length; /* Length of sort field */ - uint suffix_length; /* Length suffix (0-4) */ - Item_result result_type; /* Type of item */ bool reverse; /* if descending sort */ - bool need_strxnfrm; /* If we have to use strxnfrm() */ -} SORT_FIELD; +}; typedef struct st_sort_buffer { @@ -5146,94 +5524,7 @@ class user_var_entry user_var_entry *get_variable(HASH *hash, LEX_STRING &name, bool create_if_not_exists); -/* - Unique -- class for unique (removing of duplicates). - Puts all values to the TREE. If the tree becomes too big, - it's dumped to the file. User can request sorted values, or - just iterate through them. In the last case tree merging is performed in - memory simultaneously with iteration, so it should be ~2-3x faster. - */ - -class Unique :public Sql_alloc -{ - DYNAMIC_ARRAY file_ptrs; - ulong max_elements; /* Total number of elements that will be stored in-memory */ - ulonglong max_in_memory_size; - IO_CACHE file; - TREE tree; - uchar *record_pointers; - /* Number of elements filtered out due to min_dupl_count when storing results - to table. See Unique::get */ - ulong filtered_out_elems; - bool flush(); - uint size; - uint full_size; /* Size of element + space needed to store the number of - duplicates found for the element. */ - uint min_dupl_count; /* Minimum number of occurences of element required for - it to be written to record_pointers. - always 0 for unions, > 0 for intersections */ - bool with_counters; - - bool merge(TABLE *table, uchar *buff, bool without_last_merge); - -public: - ulong elements; - Unique(qsort_cmp2 comp_func, void *comp_func_fixed_arg, - uint size_arg, ulonglong max_in_memory_size_arg, - uint min_dupl_count_arg= 0); - ~Unique(); - ulong elements_in_tree() { return tree.elements_in_tree; } - inline bool unique_add(void *ptr) - { - DBUG_ENTER("unique_add"); - DBUG_PRINT("info", ("tree %u - %lu", tree.elements_in_tree, max_elements)); - if (!(tree.flag & TREE_ONLY_DUPS) && - tree.elements_in_tree >= max_elements && flush()) - DBUG_RETURN(1); - DBUG_RETURN(!tree_insert(&tree, ptr, 0, tree.custom_arg)); - } - - bool is_in_memory() { return (my_b_tell(&file) == 0); } - void close_for_expansion() { tree.flag= TREE_ONLY_DUPS; } - - bool get(TABLE *table); - - /* Cost of searching for an element in the tree */ - inline static double get_search_cost(ulonglong tree_elems, uint compare_factor) - { - return log((double) tree_elems) / (compare_factor * M_LN2); - } - - static double get_use_cost(uint *buffer, size_t nkeys, uint key_size, - ulonglong max_in_memory_size, uint compare_factor, - bool intersect_fl, bool *in_memory); - inline static int get_cost_calc_buff_size(size_t nkeys, uint key_size, - ulonglong max_in_memory_size) - { - ulonglong max_elems_in_tree= - max_in_memory_size / ALIGN_SIZE(sizeof(TREE_ELEMENT)+key_size); - - if (max_elems_in_tree == 0) - max_elems_in_tree= 1; - return (int) (sizeof(uint)*(1 + nkeys/max_elems_in_tree)); - } - - void reset(); - bool walk(TABLE *table, tree_walk_action action, void *walk_action_arg); - - uint get_size() const { return size; } - ulonglong get_max_in_memory_size() const { return max_in_memory_size; } - - friend int unique_write_to_file(uchar* key, element_count count, Unique *unique); - friend int unique_write_to_ptrs(uchar* key, element_count count, Unique *unique); - - friend int unique_write_to_file_with_count(uchar* key, element_count count, - Unique *unique); - friend int unique_intersect_write_to_ptrs(uchar* key, element_count count, - Unique *unique); -}; - - +class SORT_INFO; class multi_delete :public select_result_interceptor { TABLE_LIST *delete_tables, *table_being_deleted; @@ -5260,13 +5551,11 @@ public: int send_data(List<Item> &items); bool initialize_tables (JOIN *join); int do_deletes(); - int do_table_deletes(TABLE *table, bool ignore); + int do_table_deletes(TABLE *table, SORT_INFO *sort_info, bool ignore); bool send_eof(); - inline ha_rows num_deleted() - { - return deleted; - } + inline ha_rows num_deleted() const { return deleted; } virtual void abort_result_set(); + void prepare_to_read_rows(); }; @@ -5310,16 +5599,11 @@ public: bool initialize_tables (JOIN *join); int do_updates(); bool send_eof(); - inline ha_rows num_found() - { - return found; - } - inline ha_rows num_updated() - { - return updated; - } + inline ha_rows num_found() const { return found; } + inline ha_rows num_updated() const { return updated; } virtual void abort_result_set(); void update_used_tables(); + void prepare_to_read_rows(); }; class my_var : public Sql_alloc { @@ -5484,6 +5768,15 @@ public: */ #define CF_ADMIN_COMMAND (1U << 19) +/** + SP Bulk execution safe +*/ +#define CF_SP_BULK_SAFE (1U << 20) +/** + SP Bulk execution optimized +*/ +#define CF_SP_BULK_OPTIMIZED (1U << 21) + /* Bits in server_command_flags */ /** @@ -5500,11 +5793,19 @@ public: sent by the user (ie: stored procedure). */ #define CF_SKIP_QUESTIONS (1U << 1) - +#ifdef WITH_WSREP /** Do not check that wsrep snapshot is ready before allowing this command */ #define CF_SKIP_WSREP_CHECK (1U << 2) +#else +#define CF_SKIP_WSREP_CHECK 0 +#endif /* WITH_WSREP */ + +/** + Do not allow it for COM_MULTI batch +*/ +#define CF_NO_COM_MULTI (1U << 3) /* Inline functions */ @@ -5649,6 +5950,44 @@ void thd_exit_cond(MYSQL_THD thd, const PSI_stage_info *stage, #define THD_EXIT_COND(P1, P2) \ thd_exit_cond(P1, P2, __func__, __FILE__, __LINE__) +inline bool binlog_should_compress(ulong len) +{ + return opt_bin_log_compress && + len >= opt_bin_log_compress_min_len; +} + + +/** + Save thd sql_mode on instantiation. + On destruction it resets the mode to the previously stored value. +*/ +class Sql_mode_save +{ + public: + Sql_mode_save(THD *thd) : thd(thd), old_mode(thd->variables.sql_mode) {} + ~Sql_mode_save() { thd->variables.sql_mode = old_mode; } + + private: + THD *thd; + sql_mode_t old_mode; // SQL mode saved at construction time. +}; + +class Switch_to_definer_security_ctx +{ + public: + Switch_to_definer_security_ctx(THD *thd, TABLE_LIST *table) : + m_thd(thd), m_sctx(thd->security_ctx) + { + if (table->security_ctx) + thd->security_ctx= table->security_ctx; + } + ~Switch_to_definer_security_ctx() { m_thd->security_ctx = m_sctx; } + + private: + THD *m_thd; + Security_context *m_sctx; +}; + #endif /* MYSQL_SERVER */ #endif /* SQL_CLASS_INCLUDED */ |