summaryrefslogtreecommitdiff
path: root/sql/sql_class.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sql_class.cc')
-rw-r--r--sql/sql_class.cc954
1 files changed, 675 insertions, 279 deletions
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 92736eacee2..224bded2026 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -28,7 +28,7 @@
#pragma implementation // gcc: Class implementation
#endif
-#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */
+#include "mariadb.h"
#include "sql_priv.h"
#include "sql_class.h"
#include "sql_cache.h" // query_cache_abort
@@ -49,14 +49,16 @@
#include <m_ctype.h>
#include <sys/stat.h>
#include <thr_alarm.h>
-#ifdef __WIN__
+#ifdef __WIN__0
#include <io.h>
#endif
#include <mysys_err.h>
#include <limits.h>
+#include "sp_head.h"
#include "sp_rcontext.h"
#include "sp_cache.h"
+#include "sql_show.h" // append_identifier
#include "transaction.h"
#include "sql_select.h" /* declares create_tmp_table() */
#include "debug_sync.h"
@@ -101,11 +103,28 @@ extern "C" void free_user_var(user_var_entry *entry)
my_free(entry);
}
+/* Functions for last-value-from-sequence hash */
+
+extern "C" uchar *get_sequence_last_key(SEQUENCE_LAST_VALUE *entry,
+ size_t *length,
+ my_bool not_used
+ __attribute__((unused)))
+{
+ *length= entry->length;
+ return (uchar*) entry->key;
+}
+
+extern "C" void free_sequence_last(SEQUENCE_LAST_VALUE *entry)
+{
+ delete entry;
+}
+
+
bool Key_part_spec::operator==(const Key_part_spec& other) const
{
return length == other.length &&
- !my_strcasecmp(system_charset_info, field_name.str,
- other.field_name.str);
+ !lex_string_cmp(system_charset_info, &field_name,
+ &other.field_name);
}
/**
@@ -121,7 +140,7 @@ Key::Key(const Key &rhs, MEM_ROOT *mem_root)
columns(rhs.columns, mem_root),
name(rhs.name),
option_list(rhs.option_list),
- generated(rhs.generated)
+ generated(rhs.generated), invisible(false)
{
list_copy_and_replace_each_value(columns, mem_root);
}
@@ -232,9 +251,9 @@ bool Foreign_key::validate(List<Create_field> &table_fields)
{
it.rewind();
while ((sql_field= it++) &&
- my_strcasecmp(system_charset_info,
- column->field_name.str,
- sql_field->field_name)) {}
+ lex_string_cmp(system_charset_info,
+ &column->field_name,
+ &sql_field->field_name)) {}
if (!sql_field)
{
my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), column->field_name.str);
@@ -318,24 +337,9 @@ ulong get_max_connections(void)
extern "C" int mysql_tmpfile(const char *prefix)
{
char filename[FN_REFLEN];
- File fd = create_temp_file(filename, mysql_tmpdir, prefix,
-#ifdef __WIN__
- O_BINARY | O_TRUNC | O_SEQUENTIAL |
- O_SHORT_LIVED |
-#endif /* __WIN__ */
- O_CREAT | O_EXCL | O_RDWR | O_TEMPORARY,
- MYF(MY_WME));
- if (fd >= 0) {
-#ifndef __WIN__
- /*
- This can be removed once the following bug is fixed:
- Bug #28903 create_temp_file() doesn't honor O_TEMPORARY option
- (file not removed) (Unix)
- */
- unlink(filename);
-#endif /* !__WIN__ */
- }
-
+ File fd= create_temp_file(filename, mysql_tmpdir, prefix,
+ O_BINARY | O_SEQUENTIAL,
+ MYF(MY_WME | MY_TEMPORARY));
return fd;
}
@@ -593,13 +597,10 @@ handle_condition(THD *thd,
extern "C" void thd_kill_timeout(THD* thd)
{
thd->status_var.max_statement_time_exceeded++;
- mysql_mutex_lock(&thd->LOCK_thd_data);
/* Kill queries that can't cause data corruptions */
thd->awake(KILL_TIMEOUT);
- mysql_mutex_unlock(&thd->LOCK_thd_data);
}
-
THD::THD(my_thread_id id, bool is_wsrep_applier)
:Statement(&main_lex, &main_mem_root, STMT_CONVENTIONAL_EXECUTION,
/* statement id */ 0),
@@ -668,6 +669,7 @@ THD::THD(my_thread_id id, bool is_wsrep_applier)
THD *old_THR_THD= current_thd;
set_current_thd(this);
status_var.local_memory_used= sizeof(THD);
+ status_var.max_local_memory_used= status_var.local_memory_used;
status_var.global_memory_used= 0;
variables.pseudo_thread_id= thread_id;
variables.max_mem_used= global_system_variables.max_mem_used;
@@ -680,8 +682,8 @@ THD::THD(my_thread_id id, bool is_wsrep_applier)
the destructor works OK in case of an error. The main_mem_root
will be re-initialized in init_for_queries().
*/
- init_sql_alloc(&main_mem_root, ALLOC_ROOT_MIN_BLOCK_SIZE, 0,
- MYF(MY_THREAD_SPECIFIC));
+ init_sql_alloc(&main_mem_root, "THD::main_mem_root",
+ ALLOC_ROOT_MIN_BLOCK_SIZE, 0, MYF(MY_THREAD_SPECIFIC));
/*
Allocation of user variables for binary logging is always done with main
@@ -701,7 +703,7 @@ THD::THD(my_thread_id id, bool is_wsrep_applier)
security_ctx= &main_security_ctx;
no_errors= 0;
password= 0;
- query_start_used= query_start_sec_part_used= 0;
+ query_start_sec_part_used= 0;
count_cuted_fields= CHECK_FIELD_IGNORE;
killed= NOT_KILLED;
killed_err= 0;
@@ -718,6 +720,7 @@ THD::THD(my_thread_id id, bool is_wsrep_applier)
// Must be reset to handle error with THD's created for init of mysqld
lex->current_select= 0;
start_utime= utime_after_query= 0;
+ system_time.start.val= system_time.sec= system_time.sec_part= 0;
utime_after_lock= 0L;
progress.arena= 0;
progress.report_to_client= 0;
@@ -738,7 +741,7 @@ THD::THD(my_thread_id id, bool is_wsrep_applier)
enable_slow_log= 0;
durability_property= HA_REGULAR_DURABILITY;
-#ifndef DBUG_OFF
+#ifdef DBUG_ASSERT_EXISTS
dbug_sentry=THD_SENTRY_MAGIC;
#endif
mysql_audit_init_thd(this);
@@ -805,9 +808,14 @@ THD::THD(my_thread_id id, bool is_wsrep_applier)
my_hash_init(&user_vars, system_charset_info, USER_VARS_HASH_SIZE, 0, 0,
(my_hash_get_key) get_var_key,
(my_hash_free_key) free_user_var, HASH_THREAD_SPECIFIC);
+ my_hash_init(&sequences, system_charset_info, SEQUENCES_HASH_SIZE, 0, 0,
+ (my_hash_get_key) get_sequence_last_key,
+ (my_hash_free_key) free_sequence_last, HASH_THREAD_SPECIFIC);
sp_proc_cache= NULL;
sp_func_cache= NULL;
+ sp_package_spec_cache= NULL;
+ sp_package_body_cache= NULL;
/* For user vars replication*/
if (opt_bin_log)
@@ -844,8 +852,7 @@ THD::THD(my_thread_id id, bool is_wsrep_applier)
}
m_binlog_invoker= INVOKER_NONE;
- memset(&invoker_user, 0, sizeof(invoker_user));
- memset(&invoker_host, 0, sizeof(invoker_host));
+ invoker.init();
prepare_derived_at_open= FALSE;
create_tmp_table_for_derived= FALSE;
save_prep_leaf_list= FALSE;
@@ -993,9 +1000,10 @@ void THD::raise_note_printf(uint sql_errno, ...)
}
Sql_condition* THD::raise_condition(uint sql_errno,
- const char* sqlstate,
- Sql_condition::enum_warning_level level,
- const char* msg)
+ const char* sqlstate,
+ Sql_condition::enum_warning_level level,
+ const Sql_user_condition_identity &ucid,
+ const char* msg)
{
Diagnostics_area *da= get_stmt_da();
Sql_condition *cond= NULL;
@@ -1054,7 +1062,7 @@ Sql_condition* THD::raise_condition(uint sql_errno,
if (!da->is_error())
{
set_row_count_func(-1);
- da->set_error_status(sql_errno, msg, sqlstate, cond);
+ da->set_error_status(sql_errno, msg, sqlstate, ucid, cond);
}
}
@@ -1065,10 +1073,10 @@ Sql_condition* THD::raise_condition(uint sql_errno,
require memory allocation and therefore might fail. Non fatal out of
memory errors can occur if raised by SIGNAL/RESIGNAL statement.
*/
- if (!(is_fatal_error && (sql_errno == EE_OUTOFMEMORY ||
- sql_errno == ER_OUTOFMEMORY)))
+ if (likely(!(is_fatal_error && (sql_errno == EE_OUTOFMEMORY ||
+ sql_errno == ER_OUTOFMEMORY))))
{
- cond= da->push_warning(this, sql_errno, sqlstate, level, msg);
+ cond= da->push_warning(this, sql_errno, sqlstate, level, ucid, msg);
}
DBUG_RETURN(cond);
}
@@ -1098,11 +1106,11 @@ char *thd_strmake(MYSQL_THD thd, const char *str, size_t size)
}
extern "C"
-LEX_STRING *thd_make_lex_string(THD *thd, LEX_STRING *lex_str,
+LEX_CSTRING *thd_make_lex_string(THD *thd, LEX_CSTRING *lex_str,
const char *str, size_t size,
int allocate_lex_string)
{
- return allocate_lex_string ? thd->make_lex_string(str, size)
+ return allocate_lex_string ? thd->make_clex_string(str, size)
: thd->make_lex_string(lex_str, str, size);
}
@@ -1151,11 +1159,20 @@ extern "C" my_thread_id next_thread_id_noinline()
}
#endif
+
+const Type_handler *THD::type_handler_for_datetime() const
+{
+ if (opt_mysql56_temporal_format)
+ return &type_handler_datetime2;
+ return &type_handler_datetime;
+}
+
+
/*
Init common variables that has to be reset on start and on change_user
*/
-void THD::init(void)
+void THD::init()
{
DBUG_ENTER("thd::init");
mysql_mutex_lock(&LOCK_global_system_variables);
@@ -1168,10 +1185,9 @@ void THD::init(void)
variables.pseudo_thread_id= thread_id;
variables.default_master_connection.str= default_master_connection_buff;
- ::strmake(variables.default_master_connection.str,
+ ::strmake(default_master_connection_buff,
global_system_variables.default_master_connection.str,
variables.default_master_connection.length);
-
mysql_mutex_unlock(&LOCK_global_system_variables);
user_time.val= start_time= start_time_sec_part= 0;
@@ -1197,6 +1213,7 @@ void THD::init(void)
reset_current_stmt_binlog_format_row();
reset_binlog_local_stmt_filter();
set_status_var_init();
+ status_var.max_local_memory_used= status_var.local_memory_used;
bzero((char *) &org_status_var, sizeof(org_status_var));
status_in_global= 0;
start_bytes_received= 0;
@@ -1250,11 +1267,21 @@ void THD::init(void)
session_tracker.enable(this);
#endif //EMBEDDED_LIBRARY
- apc_target.init(&LOCK_thd_data);
+ apc_target.init(&LOCK_thd_kill);
DBUG_VOID_RETURN;
}
-
+
+bool THD::restore_from_local_lex_to_old_lex(LEX *oldlex)
+{
+ DBUG_ASSERT(lex->sphead);
+ if (lex->sphead->merge_lex(this, oldlex, lex))
+ return true;
+ lex= oldlex;
+ return false;
+}
+
+
/* Updates some status variables to be used by update_global_user_stats */
void THD::update_stats(void)
@@ -1353,8 +1380,82 @@ void THD::change_user(void)
my_hash_init(&user_vars, system_charset_info, USER_VARS_HASH_SIZE, 0, 0,
(my_hash_get_key) get_var_key,
(my_hash_free_key) free_user_var, 0);
+ my_hash_init(&sequences, system_charset_info, SEQUENCES_HASH_SIZE, 0, 0,
+ (my_hash_get_key) get_sequence_last_key,
+ (my_hash_free_key) free_sequence_last, HASH_THREAD_SPECIFIC);
sp_cache_clear(&sp_proc_cache);
sp_cache_clear(&sp_func_cache);
+ sp_cache_clear(&sp_package_spec_cache);
+ sp_cache_clear(&sp_package_body_cache);
+}
+
+/**
+ Change default database
+
+ @note This is coded to have as few instructions as possible under
+ LOCK_thd_data
+*/
+
+bool THD::set_db(const LEX_CSTRING *new_db)
+{
+ bool result= 0;
+ /*
+ Acquiring mutex LOCK_thd_data as we either free the memory allocated
+ for the database and reallocating the memory for the new db or memcpy
+ the new_db to the db.
+ */
+ /* Do not reallocate memory if current chunk is big enough. */
+ if (db.str && new_db->str && db.length >= new_db->length)
+ {
+ mysql_mutex_lock(&LOCK_thd_data);
+ db.length= new_db->length;
+ memcpy((char*) db.str, new_db->str, new_db->length+1);
+ mysql_mutex_unlock(&LOCK_thd_data);
+ }
+ else
+ {
+ const char *org_db= db.str;
+ const char *tmp= NULL;
+ if (new_db->str)
+ {
+ if (!(tmp= my_strndup(new_db->str, new_db->length, MYF(MY_WME | ME_FATALERROR))))
+ result= 1;
+ }
+
+ mysql_mutex_lock(&LOCK_thd_data);
+ db.str= tmp;
+ db.length= tmp ? new_db->length : 0;
+ mysql_mutex_unlock(&LOCK_thd_data);
+ my_free((char*) org_db);
+ }
+ PSI_CALL_set_thread_db(db.str, (int) db.length);
+ return result;
+}
+
+
+/**
+ Set the current database
+
+ @param new_db a pointer to the new database name.
+ @param new_db_len length of the new database name.
+
+ @note This operation just sets {db, db_length}. Switching the current
+ database usually involves other actions, like switching other database
+ attributes including security context. In the future, this operation
+ will be made private and more convenient interface will be provided.
+*/
+
+void THD::reset_db(const LEX_CSTRING *new_db)
+{
+ if (new_db->str != db.str || new_db->length != db.length)
+ {
+ if (db.str != 0)
+ DBUG_PRINT("QQ", ("Overwriting: %p", db.str));
+ mysql_mutex_lock(&LOCK_thd_data);
+ db= *new_db;
+ mysql_mutex_unlock(&LOCK_thd_data);
+ PSI_CALL_set_thread_db(db.str, (int) db.length);
+ }
}
@@ -1410,8 +1511,11 @@ void THD::cleanup(void)
#endif /* defined(ENABLED_DEBUG_SYNC) */
my_hash_free(&user_vars);
+ my_hash_free(&sequences);
sp_cache_clear(&sp_proc_cache);
sp_cache_clear(&sp_func_cache);
+ sp_cache_clear(&sp_package_spec_cache);
+ sp_cache_clear(&sp_package_body_cache);
auto_inc_intervals_forced.empty();
auto_inc_intervals_in_cur_stmt_for_binlog.empty();
@@ -1440,8 +1544,8 @@ void THD::cleanup(void)
void THD::free_connection()
{
DBUG_ASSERT(free_connection_done == 0);
- my_free(db);
- db= NULL;
+ my_free(const_cast<char*>(db.str));
+ db= null_clex_str;
#ifndef EMBEDDED_LIBRARY
if (net.vio)
vio_delete(net.vio);
@@ -1511,9 +1615,13 @@ THD::~THD()
if (!status_in_global)
add_status_to_global();
- /* Ensure that no one is using THD */
- mysql_mutex_lock(&LOCK_thd_data);
- mysql_mutex_unlock(&LOCK_thd_data);
+ /*
+ Other threads may have a lock on LOCK_thd_kill to ensure that this
+ THD is not deleted while they access it. The following mutex_lock
+ ensures that no one else is using this THD and it's now safe to delete
+ */
+ mysql_mutex_lock(&LOCK_thd_kill);
+ mysql_mutex_unlock(&LOCK_thd_kill);
#ifdef WITH_WSREP
delete wsrep_rgi;
@@ -1528,7 +1636,7 @@ THD::~THD()
mysql_mutex_destroy(&LOCK_wakeup_ready);
mysql_mutex_destroy(&LOCK_thd_data);
mysql_mutex_destroy(&LOCK_thd_kill);
-#ifndef DBUG_OFF
+#ifdef DBUG_ASSERT_EXISTS
dbug_sentry= THD_SENTRY_GONE;
#endif
#ifndef EMBEDDED_LIBRARY
@@ -1560,7 +1668,7 @@ THD::~THD()
/* trick to make happy memory accounting system */
#ifndef EMBEDDED_LIBRARY
- session_tracker.deinit();
+ session_tracker.sysvars.deinit();
#endif //EMBEDDED_LIBRARY
if (status_var.local_memory_used != 0)
@@ -1609,6 +1717,9 @@ void add_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var)
to_var->binlog_bytes_written+= from_var->binlog_bytes_written;
to_var->cpu_time+= from_var->cpu_time;
to_var->busy_time+= from_var->busy_time;
+ to_var->table_open_cache_hits+= from_var->table_open_cache_hits;
+ to_var->table_open_cache_misses+= from_var->table_open_cache_misses;
+ to_var->table_open_cache_overflows+= from_var->table_open_cache_overflows;
/*
Update global_memory_used. We have to do this with atomic_add as the
@@ -1660,6 +1771,12 @@ void add_diff_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var,
dec_var->binlog_bytes_written;
to_var->cpu_time+= from_var->cpu_time - dec_var->cpu_time;
to_var->busy_time+= from_var->busy_time - dec_var->busy_time;
+ to_var->table_open_cache_hits+= from_var->table_open_cache_hits -
+ dec_var->table_open_cache_hits;
+ to_var->table_open_cache_misses+= from_var->table_open_cache_misses -
+ dec_var->table_open_cache_misses;
+ to_var->table_open_cache_overflows+= from_var->table_open_cache_overflows -
+ dec_var->table_open_cache_overflows;
/*
We don't need to accumulate memory_used as these are not reset or used by
@@ -1683,17 +1800,17 @@ void add_diff_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var,
This is normally called from another thread's THD object.
- @note Do always call this while holding LOCK_thd_data.
+ @note Do always call this while holding LOCK_thd_kill.
NOT_KILLED is used to awake a thread for a slave
*/
-void THD::awake(killed_state state_to_set)
+void THD::awake_no_mutex(killed_state state_to_set)
{
DBUG_ENTER("THD::awake");
DBUG_PRINT("enter", ("this: %p current_thd: %p state: %d",
this, current_thd, (int) state_to_set));
THD_CHECK_SENTRY(this);
- mysql_mutex_assert_owner(&LOCK_thd_data);
+ mysql_mutex_assert_owner(&LOCK_thd_kill);
print_aborted_warning(3, "KILLED");
@@ -1704,8 +1821,6 @@ void THD::awake(killed_state state_to_set)
if (killed >= KILL_CONNECTION)
state_to_set= killed;
- /* Set the 'killed' flag of 'this', which is the target THD object. */
- mysql_mutex_lock(&LOCK_thd_kill);
set_killed_no_mutex(state_to_set);
if (state_to_set >= KILL_CONNECTION || state_to_set == NOT_KILLED)
@@ -1792,7 +1907,6 @@ void THD::awake(killed_state state_to_set)
}
mysql_mutex_unlock(&mysys_var->mutex);
}
- mysql_mutex_unlock(&LOCK_thd_kill);
DBUG_VOID_RETURN;
}
@@ -1808,10 +1922,10 @@ void THD::disconnect()
{
Vio *vio= NULL;
- mysql_mutex_lock(&LOCK_thd_data);
-
set_killed(KILL_CONNECTION);
+ mysql_mutex_lock(&LOCK_thd_data);
+
#ifdef SIGNAL_WITH_VIO_CLOSE
/*
Since a active vio might might have not been set yet, in
@@ -1844,9 +1958,9 @@ bool THD::notify_shared_lock(MDL_context_owner *ctx_in_use,
{
/* This code is similar to kill_delayed_threads() */
DBUG_PRINT("info", ("kill delayed thread"));
- mysql_mutex_lock(&in_use->LOCK_thd_data);
+ mysql_mutex_lock(&in_use->LOCK_thd_kill);
if (in_use->killed < KILL_CONNECTION)
- in_use->set_killed(KILL_CONNECTION);
+ in_use->set_killed_no_mutex(KILL_CONNECTION);
if (in_use->mysys_var)
{
mysql_mutex_lock(&in_use->mysys_var->mutex);
@@ -1857,7 +1971,7 @@ bool THD::notify_shared_lock(MDL_context_owner *ctx_in_use,
in_use->mysys_var->abort= 1;
mysql_mutex_unlock(&in_use->mysys_var->mutex);
}
- mysql_mutex_unlock(&in_use->LOCK_thd_data);
+ mysql_mutex_unlock(&in_use->LOCK_thd_kill);
signalled= TRUE;
}
@@ -1948,6 +2062,23 @@ int THD::killed_errno()
}
+void THD::reset_killed()
+{
+ /*
+ Resetting killed has to be done under a mutex to ensure
+ its not done during an awake() call.
+ */
+ DBUG_ENTER("reset_killed");
+ if (killed != NOT_KILLED)
+ {
+ mysql_mutex_lock(&LOCK_thd_kill);
+ killed= NOT_KILLED;
+ killed_err= 0;
+ mysql_mutex_unlock(&LOCK_thd_kill);
+ }
+ DBUG_VOID_RETURN;
+}
+
/*
Remember the location of thread info, the structure needed for
the structure for the net buffer
@@ -1965,7 +2096,7 @@ bool THD::store_globals()
return 1;
/*
mysys_var is concurrently readable by a killer thread.
- It is protected by LOCK_thd_data, it is not needed to lock while the
+ It is protected by LOCK_thd_kill, it is not needed to lock while the
pointer is changing from NULL not non-NULL. If the kill thread reads
NULL it doesn't refer to anything, but if it is non-NULL we need to
ensure that the thread doesn't proceed to assign another thread to
@@ -2016,9 +2147,9 @@ bool THD::store_globals()
void THD::reset_globals()
{
- mysql_mutex_lock(&LOCK_thd_data);
+ mysql_mutex_lock(&LOCK_thd_kill);
mysys_var= 0;
- mysql_mutex_unlock(&LOCK_thd_data);
+ mysql_mutex_unlock(&LOCK_thd_kill);
/* Undocking the thread specific data. */
set_current_thd(0);
@@ -2144,18 +2275,18 @@ void THD::cleanup_after_query()
*/
bool THD::convert_string(LEX_STRING *to, CHARSET_INFO *to_cs,
- const char *from, uint from_length,
+ const char *from, size_t from_length,
CHARSET_INFO *from_cs)
{
DBUG_ENTER("THD::convert_string");
size_t new_length= to_cs->mbmaxlen * from_length;
uint errors;
- if (alloc_lex_string(to, new_length + 1))
+ if (unlikely(alloc_lex_string(to, new_length + 1)))
DBUG_RETURN(true); // EOM
to->length= copy_and_convert((char*) to->str, new_length, to_cs,
from, from_length, from_cs, &errors);
to->str[to->length]= 0; // Safety
- if (errors && lex->parse_vcol_expr)
+ if (unlikely(errors) && lex->parse_vcol_expr)
{
my_error(ER_BAD_DATA, MYF(0),
ErrConvString(from, from_length, from_cs).ptr(),
@@ -2171,7 +2302,7 @@ bool THD::convert_string(LEX_STRING *to, CHARSET_INFO *to_cs,
dstcs and srccs cannot be &my_charset_bin.
*/
bool THD::convert_fix(CHARSET_INFO *dstcs, LEX_STRING *dst,
- CHARSET_INFO *srccs, const char *src, uint src_length,
+ CHARSET_INFO *srccs, const char *src, size_t src_length,
String_copier *status)
{
DBUG_ENTER("THD::convert_fix");
@@ -2189,7 +2320,7 @@ bool THD::convert_fix(CHARSET_INFO *dstcs, LEX_STRING *dst,
Copy or convert a string.
*/
bool THD::copy_fix(CHARSET_INFO *dstcs, LEX_STRING *dst,
- CHARSET_INFO *srccs, const char *src, uint src_length,
+ CHARSET_INFO *srccs, const char *src, size_t src_length,
String_copier *status)
{
DBUG_ENTER("THD::copy_fix");
@@ -2206,7 +2337,7 @@ bool THD::copy_fix(CHARSET_INFO *dstcs, LEX_STRING *dst,
class String_copier_with_error: public String_copier
{
public:
- bool check_errors(CHARSET_INFO *srccs, const char *src, uint src_length)
+ bool check_errors(CHARSET_INFO *srccs, const char *src, size_t src_length)
{
if (most_important_error_pos())
{
@@ -2221,7 +2352,7 @@ public:
bool THD::convert_with_error(CHARSET_INFO *dstcs, LEX_STRING *dst,
CHARSET_INFO *srccs,
- const char *src, uint src_length)
+ const char *src, size_t src_length)
{
String_copier_with_error status;
return convert_fix(dstcs, dst, srccs, src, src_length, &status) ||
@@ -2231,7 +2362,7 @@ bool THD::convert_with_error(CHARSET_INFO *dstcs, LEX_STRING *dst,
bool THD::copy_with_error(CHARSET_INFO *dstcs, LEX_STRING *dst,
CHARSET_INFO *srccs,
- const char *src, uint src_length)
+ const char *src, size_t src_length)
{
String_copier_with_error status;
return copy_fix(dstcs, dst, srccs, src, src_length, &status) ||
@@ -2257,7 +2388,8 @@ bool THD::copy_with_error(CHARSET_INFO *dstcs, LEX_STRING *dst,
bool THD::convert_string(String *s, CHARSET_INFO *from_cs, CHARSET_INFO *to_cs)
{
uint dummy_errors;
- if (convert_buffer.copy(s->ptr(), s->length(), from_cs, to_cs, &dummy_errors))
+ if (unlikely(convert_buffer.copy(s->ptr(), s->length(), from_cs, to_cs,
+ &dummy_errors)))
return TRUE;
/* If convert_buffer >> s copying is more efficient long term */
if (convert_buffer.alloced_length() >= convert_buffer.length() * 2 ||
@@ -2270,6 +2402,85 @@ bool THD::convert_string(String *s, CHARSET_INFO *from_cs, CHARSET_INFO *to_cs)
}
+bool THD::check_string_for_wellformedness(const char *str,
+ size_t length,
+ CHARSET_INFO *cs) const
+{
+ size_t wlen= Well_formed_prefix(cs, str, length).length();
+ if (wlen < length)
+ {
+ ErrConvString err(str, length, &my_charset_bin);
+ my_error(ER_INVALID_CHARACTER_STRING, MYF(0), cs->csname, err.ptr());
+ return true;
+ }
+ return false;
+}
+
+
+bool THD::to_ident_sys_alloc(Lex_ident_sys_st *to, const Lex_ident_cli_st *ident)
+{
+ if (ident->is_quoted())
+ {
+ LEX_CSTRING unquoted;
+ if (quote_unescape(&unquoted, ident, ident->quote()))
+ return true;
+ return charset_is_system_charset ?
+ to->copy_sys(this, &unquoted) :
+ to->convert(this, &unquoted, charset());
+ }
+ return charset_is_system_charset ?
+ to->copy_sys(this, ident) :
+ to->copy_or_convert(this, ident, charset());
+}
+
+
+Item_basic_constant *
+THD::make_string_literal(const char *str, size_t length, uint repertoire)
+{
+ if (!length && (variables.sql_mode & MODE_EMPTY_STRING_IS_NULL))
+ return new (mem_root) Item_null(this, 0, variables.collation_connection);
+ if (!charset_is_collation_connection &&
+ (repertoire != MY_REPERTOIRE_ASCII ||
+ !my_charset_is_ascii_based(variables.collation_connection)))
+ {
+ LEX_STRING to;
+ if (convert_string(&to, variables.collation_connection,
+ str, length, variables.character_set_client))
+ return NULL;
+ str= to.str;
+ length= to.length;
+ }
+ return new (mem_root) Item_string(this, str, (uint)length,
+ variables.collation_connection,
+ DERIVATION_COERCIBLE, repertoire);
+}
+
+
+Item_basic_constant *
+THD::make_string_literal_nchar(const Lex_string_with_metadata_st &str)
+{
+ DBUG_ASSERT(my_charset_is_ascii_based(national_charset_info));
+ if (!str.length && (variables.sql_mode & MODE_EMPTY_STRING_IS_NULL))
+ return new (mem_root) Item_null(this, 0, national_charset_info);
+
+ return new (mem_root) Item_string(this, str.str, (uint)str.length,
+ national_charset_info,
+ DERIVATION_COERCIBLE,
+ str.repertoire());
+}
+
+
+Item_basic_constant *
+THD::make_string_literal_charset(const Lex_string_with_metadata_st &str,
+ CHARSET_INFO *cs)
+{
+ if (!str.length && (variables.sql_mode & MODE_EMPTY_STRING_IS_NULL))
+ return new (mem_root) Item_null(this, 0, cs);
+ return new (mem_root) Item_string_with_introducer(this,
+ str.str, (uint)str.length, cs);
+}
+
+
/*
Update some cache variables when character set changes
*/
@@ -2321,7 +2532,7 @@ void THD::add_changed_table(TABLE *table)
}
-void THD::add_changed_table(const char *key, long key_length)
+void THD::add_changed_table(const char *key, size_t key_length)
{
DBUG_ENTER("THD::add_changed_table(key)");
CHANGED_TABLE_LIST **prev_changed = &transaction.changed_tables;
@@ -2334,7 +2545,7 @@ void THD::add_changed_table(const char *key, long key_length)
{
list_include(prev_changed, curr, changed_table_dup(key, key_length));
DBUG_PRINT("info",
- ("key_length: %ld %u", key_length,
+ ("key_length: %zu %zu", key_length,
(*prev_changed)->key_length));
DBUG_VOID_RETURN;
}
@@ -2345,7 +2556,7 @@ void THD::add_changed_table(const char *key, long key_length)
{
list_include(prev_changed, curr, changed_table_dup(key, key_length));
DBUG_PRINT("info",
- ("key_length: %ld %u", key_length,
+ ("key_length: %zu %zu", key_length,
(*prev_changed)->key_length));
DBUG_VOID_RETURN;
}
@@ -2357,13 +2568,13 @@ void THD::add_changed_table(const char *key, long key_length)
}
}
*prev_changed = changed_table_dup(key, key_length);
- DBUG_PRINT("info", ("key_length: %ld %u", key_length,
+ DBUG_PRINT("info", ("key_length: %zu %zu", key_length,
(*prev_changed)->key_length));
DBUG_VOID_RETURN;
}
-CHANGED_TABLE_LIST* THD::changed_table_dup(const char *key, long key_length)
+CHANGED_TABLE_LIST* THD::changed_table_dup(const char *key, size_t key_length)
{
CHANGED_TABLE_LIST* new_table =
(CHANGED_TABLE_LIST*) trans_alloc(ALIGN_SIZE(sizeof(CHANGED_TABLE_LIST))+
@@ -2648,7 +2859,7 @@ static String default_field_term("\t",default_charset_info);
static String default_enclosed_and_line_start("", default_charset_info);
static String default_xml_row_term("<row>", default_charset_info);
-sql_exchange::sql_exchange(char *name, bool flag,
+sql_exchange::sql_exchange(const char *name, bool flag,
enum enum_filetype filetype_arg)
:file_name(name), opt_enclosed(0), dumpfile(flag), skip_lines(0)
{
@@ -2661,7 +2872,7 @@ sql_exchange::sql_exchange(char *name, bool flag,
cs= NULL;
}
-bool sql_exchange::escaped_given(void)
+bool sql_exchange::escaped_given(void) const
{
return escaped != &default_escaped;
}
@@ -2752,7 +2963,7 @@ bool select_send::send_eof()
Don't send EOF if we're in error condition (which implies we've already
sent or are sending an error)
*/
- if (thd->is_error())
+ if (unlikely(thd->is_error()))
return TRUE;
::my_eof(thd);
is_result_set_started= 0;
@@ -2767,10 +2978,11 @@ bool select_send::send_eof()
bool select_to_file::send_eof()
{
int error= MY_TEST(end_io_cache(&cache));
- if (mysql_file_close(file, MYF(MY_WME)) || thd->is_error())
+ if (unlikely(mysql_file_close(file, MYF(MY_WME))) ||
+ unlikely(thd->is_error()))
error= true;
- if (!error && !suppress_my_ok)
+ if (likely(!error) && !suppress_my_ok)
{
::my_ok(thd,row_count);
}
@@ -2841,8 +3053,7 @@ static File create_file(THD *thd, char *path, sql_exchange *exchange,
if (!dirname_length(exchange->file_name))
{
- strxnmov(path, FN_REFLEN-1, mysql_real_data_home, thd->db ? thd->db : "",
- NullS);
+ strxnmov(path, FN_REFLEN-1, mysql_real_data_home, thd->get_db(), NullS);
(void) fn_format(path, exchange->file_name, path, "", option);
}
else
@@ -3033,7 +3244,7 @@ int select_export::send_data(List<Item> &items)
res->charset(),
res->ptr(), res->length());
error_pos= copier.most_important_error_pos();
- if (error_pos)
+ if (unlikely(error_pos))
{
/*
TODO:
@@ -3047,12 +3258,12 @@ int select_export::send_data(List<Item> &items)
ER_TRUNCATED_WRONG_VALUE_FOR_FIELD,
ER_THD(thd, ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
"string", printable_buff,
- item->name, static_cast<long>(row_count));
+ item->name.str, static_cast<long>(row_count));
*/
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_TRUNCATED_WRONG_VALUE_FOR_FIELD,
ER_THD(thd, WARN_DATA_TRUNCATED),
- item->name, static_cast<long>(row_count));
+ item->name.str, static_cast<long>(row_count));
}
else if (copier.source_end_pos() < res->ptr() + res->length())
{
@@ -3335,7 +3546,7 @@ int select_max_min_finder_subselect::send_data(List<Item> &items)
{
if (!cache)
{
- cache= Item_cache::get_cache(thd, val_item);
+ cache= val_item->get_cache(thd);
switch (val_item->result_type()) {
case REAL_RESULT:
op= &select_max_min_finder_subselect::cmp_real;
@@ -3457,15 +3668,30 @@ int select_exists_subselect::send_data(List<Item> &items)
int select_dumpvar::prepare(List<Item> &list, SELECT_LEX_UNIT *u)
{
+ my_var_sp *mvsp;
unit= u;
-
- if (var_list.elements != list.elements)
+ m_var_sp_row= NULL;
+
+ if (var_list.elements == 1 &&
+ (mvsp= var_list.head()->get_my_var_sp()) &&
+ mvsp->type_handler() == &type_handler_row)
{
- my_message(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT,
- ER_THD(thd, ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT), MYF(0));
- return 1;
- }
- return 0;
+ // SELECT INTO row_type_sp_variable
+ if (mvsp->get_rcontext(thd->spcont)->get_variable(mvsp->offset)->cols() !=
+ list.elements)
+ goto error;
+ m_var_sp_row= mvsp;
+ return 0;
+ }
+
+ // SELECT INTO variable list
+ if (var_list.elements == list.elements)
+ return 0;
+
+error:
+ my_message(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT,
+ ER_THD(thd, ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT), MYF(0));
+ return 1;
}
@@ -3526,12 +3752,11 @@ Statement::Statement(LEX *lex_arg, MEM_ROOT *mem_root_arg,
enum enum_state state_arg, ulong id_arg)
:Query_arena(mem_root_arg, state_arg),
id(id_arg),
- mark_used_columns(MARK_COLUMNS_READ),
+ column_usage(MARK_COLUMNS_READ),
lex(lex_arg),
- db(NULL),
- db_length(0)
+ db(null_clex_str)
{
- name.str= NULL;
+ name= null_clex_str;
}
@@ -3544,7 +3769,7 @@ Query_arena::Type Statement::type() const
void Statement::set_statement(Statement *stmt)
{
id= stmt->id;
- mark_used_columns= stmt->mark_used_columns;
+ column_usage= stmt->column_usage;
lex= stmt->lex;
query_string= stmt->query_string;
}
@@ -3597,7 +3822,7 @@ void THD::set_n_backup_active_arena(Query_arena *set, Query_arena *backup)
backup->set_query_arena(this);
set_query_arena(set);
-#ifndef DBUG_OFF
+#ifdef DBUG_ASSERT_EXISTS
backup->is_backup_arena= TRUE;
#endif
DBUG_VOID_RETURN;
@@ -3616,7 +3841,7 @@ void THD::restore_active_arena(Query_arena *set, Query_arena *backup)
DBUG_ASSERT(backup->is_backup_arena);
set->set_query_arena(this);
set_query_arena(backup);
-#ifndef DBUG_OFF
+#ifdef DBUG_ASSERT_EXISTS
backup->is_backup_arena= FALSE;
#endif
DBUG_VOID_RETURN;
@@ -3788,22 +4013,48 @@ Statement_map::~Statement_map()
bool my_var_user::set(THD *thd, Item *item)
{
- Item_func_set_user_var *suv= new (thd->mem_root) Item_func_set_user_var(thd, name, item);
+ Item_func_set_user_var *suv= new (thd->mem_root) Item_func_set_user_var(thd, &name, item);
suv->save_item_result(item);
return suv->fix_fields(thd, 0) || suv->update();
}
+
+sp_rcontext *my_var_sp::get_rcontext(sp_rcontext *local_ctx) const
+{
+ return m_rcontext_handler->get_rcontext(local_ctx);
+}
+
+
bool my_var_sp::set(THD *thd, Item *item)
{
- return thd->spcont->set_variable(thd, offset, &item);
+ return get_rcontext(thd->spcont)->set_variable(thd, offset, &item);
}
-int select_dumpvar::send_data(List<Item> &items)
+bool my_var_sp_row_field::set(THD *thd, Item *item)
{
+ return get_rcontext(thd->spcont)->
+ set_variable_row_field(thd, offset, m_field_offset, &item);
+}
+
+
+bool select_dumpvar::send_data_to_var_list(List<Item> &items)
+{
+ DBUG_ENTER("select_dumpvar::send_data_to_var_list");
List_iterator_fast<my_var> var_li(var_list);
List_iterator<Item> it(items);
Item *item;
my_var *mv;
+ while ((mv= var_li++) && (item= it++))
+ {
+ if (mv->set(thd, item))
+ DBUG_RETURN(true);
+ }
+ DBUG_RETURN(false);
+}
+
+
+int select_dumpvar::send_data(List<Item> &items)
+{
DBUG_ENTER("select_dumpvar::send_data");
if (unit->offset_limit_cnt)
@@ -3816,11 +4067,12 @@ int select_dumpvar::send_data(List<Item> &items)
my_message(ER_TOO_MANY_ROWS, ER_THD(thd, ER_TOO_MANY_ROWS), MYF(0));
DBUG_RETURN(1);
}
- while ((mv= var_li++) && (item= it++))
- {
- if (mv->set(thd, item))
- DBUG_RETURN(1);
- }
+ if (m_var_sp_row ?
+ m_var_sp_row->get_rcontext(thd->spcont)->
+ set_variable_row(thd, m_var_sp_row->offset, items) :
+ send_data_to_var_list(items))
+ DBUG_RETURN(1);
+
DBUG_RETURN(thd->is_error());
}
@@ -3833,7 +4085,7 @@ bool select_dumpvar::send_eof()
Don't send EOF if we're in error condition (which implies we've already
sent or are sending an error)
*/
- if (thd->is_error())
+ if (unlikely(thd->is_error()))
return true;
if (!suppress_my_ok)
@@ -3848,9 +4100,10 @@ bool
select_materialize_with_stats::
create_result_table(THD *thd_arg, List<Item> *column_types,
bool is_union_distinct, ulonglong options,
- const char *table_alias, bool bit_fields_as_long,
+ const LEX_CSTRING *table_alias, bool bit_fields_as_long,
bool create_table,
- bool keep_row_order)
+ bool keep_row_order,
+ uint hidden)
{
DBUG_ASSERT(table == 0);
tmp_table_param.field_count= column_types->elements;
@@ -3858,7 +4111,7 @@ create_result_table(THD *thd_arg, List<Item> *column_types,
if (! (table= create_tmp_table(thd_arg, &tmp_table_param, *column_types,
(ORDER*) 0, is_union_distinct, 1,
- options, HA_POS_ERROR, (char*) table_alias,
+ options, HA_POS_ERROR, table_alias,
!create_table, keep_row_order)))
return TRUE;
@@ -3885,12 +4138,12 @@ void select_materialize_with_stats::reset()
void select_materialize_with_stats::cleanup()
{
reset();
- select_union::cleanup();
+ select_unit::cleanup();
}
/**
- Override select_union::send_data to analyze each row for NULLs and to
+ Override select_unit::send_data to analyze each row for NULLs and to
update null_statistics before sending data to the client.
@return TRUE if fatal error when sending data to the client
@@ -3905,7 +4158,7 @@ int select_materialize_with_stats::send_data(List<Item> &items)
uint nulls_in_row= 0;
int res;
- if ((res= select_union::send_data(items)))
+ if ((res= select_unit::send_data(items)))
return res;
if (table->null_catch_flags & REJECT_ROW_DUE_TO_NULL_FIELDS)
{
@@ -3959,7 +4212,7 @@ void TMP_TABLE_PARAM::init()
}
-void thd_increment_bytes_sent(void *thd, ulong length)
+void thd_increment_bytes_sent(void *thd, size_t length)
{
/* thd == 0 when close_connection() calls net_send_error() */
if (likely(thd != 0))
@@ -3968,22 +4221,16 @@ void thd_increment_bytes_sent(void *thd, ulong length)
}
}
-my_bool thd_net_is_killed()
+my_bool thd_net_is_killed(THD *thd)
{
- THD *thd= current_thd;
return thd && thd->killed ? 1 : 0;
}
-void thd_increment_bytes_received(void *thd, ulong length)
+void thd_increment_bytes_received(void *thd, size_t length)
{
- ((THD*) thd)->status_var.bytes_received+= length;
-}
-
-
-void thd_increment_net_big_packet_count(void *thd, ulong length)
-{
- ((THD*) thd)->status_var.net_big_packet_count+= length;
+ if (thd != NULL) // MDEV-13073 Ack collector having NULL
+ ((THD*) thd)->status_var.bytes_received+= length;
}
@@ -3991,6 +4238,12 @@ void THD::set_status_var_init()
{
bzero((char*) &status_var, offsetof(STATUS_VAR,
last_cleared_system_status_var));
+ /*
+ Session status for Threads_running is always 1. It can only be queried
+ by thread itself via INFORMATION_SCHEMA.SESSION_STATUS or SHOW [SESSION]
+ STATUS. And at this point thread is guaranteed to be running.
+ */
+ status_var.threads_running= 1;
}
@@ -4017,7 +4270,7 @@ void Security_context::destroy()
}
if (user != delayed_user)
{
- my_free(user);
+ my_free((char*) user);
user= NULL;
}
@@ -4027,7 +4280,7 @@ void Security_context::destroy()
external_user= NULL;
}
- my_free(ip);
+ my_free((char*) ip);
ip= NULL;
}
@@ -4043,7 +4296,7 @@ void Security_context::skip_grants()
bool Security_context::set_user(char *user_arg)
{
- my_free(user);
+ my_free((char*) user);
user= my_strdup(user_arg, MYF(0));
return user == 0;
}
@@ -4101,9 +4354,9 @@ bool Security_context::set_user(char *user_arg)
bool
Security_context::
change_security_context(THD *thd,
- LEX_STRING *definer_user,
- LEX_STRING *definer_host,
- LEX_STRING *db,
+ LEX_CSTRING *definer_user,
+ LEX_CSTRING *definer_host,
+ LEX_CSTRING *db,
Security_context **backup)
{
bool needs_change;
@@ -4426,8 +4679,10 @@ TABLE *open_purge_table(THD *thd, const char *db, size_t dblen,
Open_table_context ot_ctx(thd, 0);
TABLE_LIST *tl= (TABLE_LIST*)thd->alloc(sizeof(TABLE_LIST));
+ LEX_CSTRING db_name= {db, dblen };
+ LEX_CSTRING table_name= { tb, tblen };
- tl->init_one_table(db, dblen, tb, tblen, tb, TL_READ);
+ tl->init_one_table(&db_name, &table_name, 0, TL_READ);
tl->i_s_requested_object= OPEN_TABLE_ONLY;
bool error= open_table(thd, tl, &ot_ctx);
@@ -4435,7 +4690,7 @@ TABLE *open_purge_table(THD *thd, const char *db, size_t dblen,
/* we don't recover here */
DBUG_ASSERT(!error || !ot_ctx.can_recover_from_failed_open());
- if (error)
+ if (unlikely(error))
close_thread_tables(thd);
DBUG_RETURN(error ? NULL : tl->table);
@@ -4457,7 +4712,7 @@ TABLE *find_fk_open_table(THD *thd, const char *db, size_t db_len,
{
if (t->s->db.length == db_len && t->s->table_name.length == table_len &&
!strcmp(t->s->db.str, db) && !strcmp(t->s->table_name.str, table) &&
- t->pos_in_table_list->prelocking_placeholder == TABLE_LIST::FK)
+ t->pos_in_table_list->prelocking_placeholder == TABLE_LIST::PRELOCK_FK)
return t;
}
return NULL;
@@ -4482,7 +4737,6 @@ void destroy_thd(MYSQL_THD thd)
thd->add_status_to_global();
unlink_not_visible_thd(thd);
delete thd;
- dec_thread_running();
}
void reset_thd(MYSQL_THD thd)
@@ -4565,18 +4819,14 @@ extern "C" int thd_slave_thread(const MYSQL_THD thd)
return(thd->slave_thread);
}
-/* Returns true for a worker thread in parallel replication. */
-extern "C" int thd_rpl_is_parallel(const MYSQL_THD thd)
-{
- return thd->rgi_slave && thd->rgi_slave->is_parallel_exec;
-}
+
/* Returns high resolution timestamp for the start
of the current query. */
extern "C" unsigned long long thd_start_utime(const MYSQL_THD thd)
{
- return thd->start_utime;
+ return thd->start_time * 1000000 + thd->start_time_sec_part;
}
@@ -4609,7 +4859,7 @@ thd_need_wait_reports(const MYSQL_THD thd)
}
/*
- Used by storage engines (currently TokuDB and InnoDB/XtraDB) to report that
+ Used by storage engines (currently TokuDB and InnoDB) to report that
one transaction THD is about to go to wait for a transactional lock held by
another transactions OTHER_THD.
@@ -4631,7 +4881,7 @@ thd_need_wait_reports(const MYSQL_THD thd)
transaction, and later re-try it, to resolve the deadlock.
This call need only receive reports about waits for locks that will remain
- until the holding transaction commits. InnoDB/XtraDB auto-increment locks,
+ until the holding transaction commits. InnoDB auto-increment locks,
for example, are released earlier, and so need not be reported. (Such false
positives are not harmful, but could lead to unnecessary kill and retry, so
best avoided).
@@ -4684,7 +4934,7 @@ thd_rpl_deadlock_check(MYSQL_THD thd, MYSQL_THD other_thd)
}
/*
- This function is called from InnoDB/XtraDB to check if the commit order of
+ This function is called from InnoDB to check if the commit order of
two transactions has already been decided by the upper layer. This happens
in parallel replication, where the commit order is forced to be the same on
the slave as it was originally on the master.
@@ -4714,7 +4964,7 @@ thd_rpl_deadlock_check(MYSQL_THD thd, MYSQL_THD other_thd)
If this function returns true, normal locking should be done as required by
the binlogging and transaction isolation level in effect. But if it returns
- false, the correct order will be enforced anyway, and InnoDB/XtraDB can
+ false, the correct order will be enforced anyway, and InnoDB can
avoid taking the gap lock, preventing the lock conflict.
Calling this function is just an optimisation to avoid unnecessary
@@ -4762,66 +5012,6 @@ thd_need_ordering_with(const MYSQL_THD thd, const MYSQL_THD other_thd)
return 0;
}
-
-/*
- If the storage engine detects a deadlock, and needs to choose a victim
- transaction to roll back, it can call this function to ask the upper
- server layer for which of two possible transactions is prefered to be
- aborted and rolled back.
-
- In parallel replication, if two transactions are running in parallel and
- one is fixed to commit before the other, then the one that commits later
- will be prefered as the victim - chosing the early transaction as a victim
- will not resolve the deadlock anyway, as the later transaction still needs
- to wait for the earlier to commit.
-
- Otherwise, a transaction that uses only transactional tables, and can thus
- be safely rolled back, will be prefered as a deadlock victim over a
- transaction that also modified non-transactional (eg. MyISAM) tables.
-
- The return value is -1 if the first transaction is prefered as a deadlock
- victim, 1 if the second transaction is prefered, or 0 for no preference (in
- which case the storage engine can make the choice as it prefers).
-*/
-extern "C" int
-thd_deadlock_victim_preference(const MYSQL_THD thd1, const MYSQL_THD thd2)
-{
- rpl_group_info *rgi1, *rgi2;
- bool nontrans1, nontrans2;
-
- if (!thd1 || !thd2)
- return 0;
-
- /*
- If the transactions are participating in the same replication domain in
- parallel replication, then request to select the one that will commit
- later (in the fixed commit order from the master) as the deadlock victim.
- */
- rgi1= thd1->rgi_slave;
- rgi2= thd2->rgi_slave;
- if (rgi1 && rgi2 &&
- rgi1->is_parallel_exec &&
- rgi1->rli == rgi2->rli &&
- rgi1->current_gtid.domain_id == rgi2->current_gtid.domain_id)
- return rgi1->gtid_sub_id < rgi2->gtid_sub_id ? 1 : -1;
-
- /*
- If one transaction has modified non-transactional tables (so that it
- cannot be safely rolled back), and the other has not, then prefer to
- select the purely transactional one as the victim.
- */
- nontrans1= thd1->transaction.all.modified_non_trans_table;
- nontrans2= thd2->transaction.all.modified_non_trans_table;
- if (nontrans1 && !nontrans2)
- return 1;
- else if (!nontrans1 && nontrans2)
- return -1;
-
- /* No preferences, let the storage engine decide. */
- return 0;
-}
-
-
extern "C" int thd_non_transactional_update(const MYSQL_THD thd)
{
return(thd->transaction.all.modified_non_trans_table);
@@ -4847,7 +5037,7 @@ extern "C" void thd_mark_transaction_to_rollback(MYSQL_THD thd, bool all)
extern "C" bool thd_binlog_filter_ok(const MYSQL_THD thd)
{
- return binlog_filter->db_ok(thd->db);
+ return binlog_filter->db_ok(thd->db.str);
}
/*
@@ -4902,6 +5092,20 @@ extern "C" bool thd_is_strict_mode(const MYSQL_THD thd)
}
+/**
+ Get query start time as SQL field data.
+ Needed by InnoDB.
+ @param thd Thread object
+ @param buf Buffer to hold start time data
+*/
+void thd_get_query_start_data(THD *thd, char *buf)
+{
+ LEX_CSTRING field_name;
+ Field_timestampf f((uchar *)buf, NULL, 0, Field::NONE, &field_name, NULL, 6);
+ f.store_TIME(thd->query_start(), thd->query_start_sec_part());
+}
+
+
/*
Interface for MySQL Server, plugins and storage engines to report
when they are going to sleep/stall.
@@ -5003,10 +5207,7 @@ void THD::reset_sub_statement_state(Sub_statement_state *backup,
backup->count_cuted_fields= count_cuted_fields;
backup->in_sub_stmt= in_sub_stmt;
backup->enable_slow_log= enable_slow_log;
- backup->query_plan_flags= query_plan_flags;
backup->limit_found_rows= limit_found_rows;
- backup->examined_row_count= m_examined_row_count;
- backup->sent_row_count= m_sent_row_count;
backup->cuted_fields= cuted_fields;
backup->client_capabilities= client_capabilities;
backup->savepoints= transaction.savepoints;
@@ -5014,6 +5215,7 @@ void THD::reset_sub_statement_state(Sub_statement_state *backup,
first_successful_insert_id_in_prev_stmt;
backup->first_successful_insert_id_in_cur_stmt=
first_successful_insert_id_in_cur_stmt;
+ store_slow_query_state(backup);
if ((!lex->requires_prelocking() || is_update_query(lex->sql_command)) &&
!is_current_stmt_binlog_format_row())
@@ -5029,14 +5231,12 @@ void THD::reset_sub_statement_state(Sub_statement_state *backup,
/* Disable result sets */
client_capabilities &= ~CLIENT_MULTI_RESULTS;
in_sub_stmt|= new_state;
- m_examined_row_count= 0;
- m_sent_row_count= 0;
cuted_fields= 0;
transaction.savepoints= 0;
first_successful_insert_id_in_cur_stmt= 0;
+ reset_slow_query_state();
}
-
void THD::restore_sub_statement_state(Sub_statement_state *backup)
{
DBUG_ENTER("THD::restore_sub_statement_state");
@@ -5071,7 +5271,6 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup)
variables.option_bits= backup->option_bits;
in_sub_stmt= backup->in_sub_stmt;
enable_slow_log= backup->enable_slow_log;
- query_plan_flags= backup->query_plan_flags;
first_successful_insert_id_in_prev_stmt=
backup->first_successful_insert_id_in_prev_stmt;
first_successful_insert_id_in_cur_stmt=
@@ -5079,6 +5278,10 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup)
limit_found_rows= backup->limit_found_rows;
set_sent_row_count(backup->sent_row_count);
client_capabilities= backup->client_capabilities;
+
+ /* Restore statistic needed for slow log */
+ add_slow_query_state(backup);
+
/*
If we've left sub-statement mode, reset the fatal error flag.
Otherwise keep the current value, to propagate it up the sub-statement
@@ -5103,6 +5306,56 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup)
DBUG_VOID_RETURN;
}
+/*
+ Store slow query state at start of a stored procedure statment
+*/
+
+void THD::store_slow_query_state(Sub_statement_state *backup)
+{
+ backup->affected_rows= affected_rows;
+ backup->bytes_sent_old= bytes_sent_old;
+ backup->examined_row_count= m_examined_row_count;
+ backup->query_plan_flags= query_plan_flags;
+ backup->query_plan_fsort_passes= query_plan_fsort_passes;
+ backup->sent_row_count= m_sent_row_count;
+ backup->tmp_tables_disk_used= tmp_tables_disk_used;
+ backup->tmp_tables_size= tmp_tables_size;
+ backup->tmp_tables_used= tmp_tables_used;
+}
+
+/* Reset variables related to slow query log */
+
+void THD::reset_slow_query_state()
+{
+ affected_rows= 0;
+ bytes_sent_old= status_var.bytes_sent;
+ m_examined_row_count= 0;
+ m_sent_row_count= 0;
+ query_plan_flags= QPLAN_INIT;
+ query_plan_fsort_passes= 0;
+ tmp_tables_disk_used= 0;
+ tmp_tables_size= 0;
+ tmp_tables_used= 0;
+}
+
+/*
+ Add back the stored values to the current counters to be able to get
+ right status for 'call procedure_name'
+*/
+
+void THD::add_slow_query_state(Sub_statement_state *backup)
+{
+ affected_rows+= backup->affected_rows;
+ bytes_sent_old= backup->bytes_sent_old;
+ m_examined_row_count+= backup->examined_row_count;
+ m_sent_row_count+= backup->sent_row_count;
+ query_plan_flags|= backup->query_plan_flags;
+ query_plan_fsort_passes+= backup->query_plan_fsort_passes;
+ tmp_tables_disk_used+= backup->tmp_tables_disk_used;
+ tmp_tables_size+= backup->tmp_tables_size;
+ tmp_tables_used+= backup->tmp_tables_used;
+}
+
void THD::set_statement(Statement *stmt)
{
@@ -5137,6 +5390,8 @@ void THD::inc_examined_row_count(ha_rows count)
void THD::inc_status_created_tmp_disk_tables()
{
+ tmp_tables_disk_used++;
+ query_plan_flags|= QPLAN_TMP_DISK;
status_var_increment(status_var.created_tmp_disk_tables_);
#ifdef HAVE_PSI_STATEMENT_INTERFACE
PSI_STATEMENT_CALL(inc_statement_created_tmp_disk_tables)(m_statement_psi, 1);
@@ -5145,6 +5400,8 @@ void THD::inc_status_created_tmp_disk_tables()
void THD::inc_status_created_tmp_tables()
{
+ tmp_tables_used++;
+ query_plan_flags|= QPLAN_TMP_TABLE;
status_var_increment(status_var.created_tmp_tables_);
#ifdef HAVE_PSI_STATEMENT_INTERFACE
PSI_STATEMENT_CALL(inc_statement_created_tmp_tables)(m_statement_psi, 1);
@@ -5254,9 +5511,9 @@ void THD::set_query_and_id(char *query_arg, uint32 query_length_arg,
/** Assign a new value to thd->mysys_var. */
void THD::set_mysys_var(struct st_my_thread_var *new_mysys_var)
{
- mysql_mutex_lock(&LOCK_thd_data);
+ mysql_mutex_lock(&LOCK_thd_kill);
mysys_var= new_mysys_var;
- mysql_mutex_unlock(&LOCK_thd_data);
+ mysql_mutex_unlock(&LOCK_thd_kill);
}
/**
@@ -5298,8 +5555,8 @@ void THD::get_definer(LEX_USER *definer, bool role)
if (slave_thread && has_invoker())
#endif
{
- definer->user = invoker_user;
- definer->host= invoker_host;
+ definer->user= invoker.user;
+ definer->host= invoker.host;
definer->reset_auth();
}
else
@@ -5389,7 +5646,7 @@ public:
MY_MEMORY_ORDER_RELAXED))
{
old&= ACQUIRED | RECOVERED;
- (void) LF_BACKOFF;
+ (void) LF_BACKOFF();
}
}
bool acquire_recovered()
@@ -5402,7 +5659,7 @@ public:
if (!(old & RECOVERED) || (old & ACQUIRED))
return false;
old= RECOVERED;
- (void) LF_BACKOFF;
+ (void) LF_BACKOFF();
}
return true;
}
@@ -5681,7 +5938,7 @@ int xid_cache_iterate(THD *thd, my_hash_walk_action action, void *arg)
int THD::decide_logging_format(TABLE_LIST *tables)
{
DBUG_ENTER("THD::decide_logging_format");
- DBUG_PRINT("info", ("Query: %s", query()));
+ DBUG_PRINT("info", ("Query: %.*s", (uint) query_length(), query()));
DBUG_PRINT("info", ("variables.binlog_format: %lu",
variables.binlog_format));
DBUG_PRINT("info", ("lex->get_stmt_unsafe_flags(): 0x%x",
@@ -5696,7 +5953,7 @@ int THD::decide_logging_format(TABLE_LIST *tables)
*/
if (mysql_bin_log.is_open() && (variables.option_bits & OPTION_BIN_LOG) &&
!(wsrep_binlog_format() == BINLOG_FORMAT_STMT &&
- !binlog_filter->db_ok(db)))
+ !binlog_filter->db_ok(db.str)))
{
if (is_bulk_op())
@@ -5791,17 +6048,33 @@ int THD::decide_logging_format(TABLE_LIST *tables)
Get the capabilities vector for all involved storage engines and
mask out the flags for the binary log.
*/
- for (TABLE_LIST *table= tables; table; table= table->next_global)
+ for (TABLE_LIST *tbl= tables; tbl; tbl= tbl->next_global)
{
- if (table->placeholder())
+ TABLE *table;
+ TABLE_SHARE *share;
+ handler::Table_flags flags;
+ if (tbl->placeholder())
continue;
- handler::Table_flags const flags= table->table->file->ha_table_flags();
+ table= tbl->table;
+ share= table->s;
+ flags= table->file->ha_table_flags();
+ if (!share->table_creation_was_logged)
+ {
+ /*
+ This is a temporary table which was not logged in the binary log.
+ Disable statement logging to enforce row level logging.
+ */
+ DBUG_ASSERT(share->tmp_table);
+ flags&= ~HA_BINLOG_STMT_CAPABLE;
+ /* We can only use row logging */
+ set_current_stmt_binlog_format_row();
+ }
DBUG_PRINT("info", ("table: %s; ha_table_flags: 0x%llx",
- table->table_name, flags));
+ tbl->table_name.str, flags));
- if (table->table->no_replicate)
+ if (share->no_replicate)
{
/*
The statement uses a table that is not replicated.
@@ -5819,43 +6092,44 @@ int THD::decide_logging_format(TABLE_LIST *tables)
*/
lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_TABLE);
- if (table->lock_type >= TL_WRITE_ALLOW_WRITE)
+ if (tbl->lock_type >= TL_WRITE_ALLOW_WRITE)
{
non_replicated_tables_count++;
continue;
}
}
- if (table == lex->first_not_own_table())
+ if (tbl == lex->first_not_own_table())
found_first_not_own_table= true;
replicated_tables_count++;
- if (table->prelocking_placeholder != TABLE_LIST::FK)
+ if (tbl->prelocking_placeholder != TABLE_LIST::PRELOCK_FK)
{
- if (table->lock_type <= TL_READ_NO_INSERT)
+ if (tbl->lock_type <= TL_READ_NO_INSERT)
has_read_tables= true;
- else if (table->table->found_next_number_field &&
- (table->lock_type >= TL_WRITE_ALLOW_WRITE))
+ else if (table->found_next_number_field &&
+ (tbl->lock_type >= TL_WRITE_ALLOW_WRITE))
{
has_auto_increment_write_tables= true;
has_auto_increment_write_tables_not_first= found_first_not_own_table;
- if (table->table->s->next_number_keypart != 0)
+ if (share->next_number_keypart != 0)
has_write_table_auto_increment_not_first_in_pk= true;
}
}
- if (table->lock_type >= TL_WRITE_ALLOW_WRITE)
+ if (tbl->lock_type >= TL_WRITE_ALLOW_WRITE)
{
bool trans;
if (prev_write_table && prev_write_table->file->ht !=
- table->table->file->ht)
+ table->file->ht)
multi_write_engine= TRUE;
- if (table->table->s->non_determinstic_insert)
+ if (share->non_determinstic_insert &&
+ !(sql_command_flags[lex->sql_command] & CF_SCHEMA_CHANGE))
has_write_tables_with_unsafe_statements= true;
- trans= table->table->file->has_transactions();
+ trans= table->file->has_transactions();
- if (table->table->s->tmp_table)
+ if (share->tmp_table)
lex->set_stmt_accessed_table(trans ? LEX::STMT_WRITES_TEMP_TRANS_TABLE :
LEX::STMT_WRITES_TEMP_NON_TRANS_TABLE);
else
@@ -5866,17 +6140,16 @@ int THD::decide_logging_format(TABLE_LIST *tables)
flags_write_some_set |= flags;
is_write= TRUE;
- prev_write_table= table->table;
+ prev_write_table= table;
}
flags_access_some_set |= flags;
- if (lex->sql_command != SQLCOM_CREATE_TABLE ||
- (lex->sql_command == SQLCOM_CREATE_TABLE && lex->tmp_table()))
+ if (lex->sql_command != SQLCOM_CREATE_TABLE || lex->tmp_table())
{
- my_bool trans= table->table->file->has_transactions();
+ my_bool trans= table->file->has_transactions();
- if (table->table->s->tmp_table)
+ if (share->tmp_table)
lex->set_stmt_accessed_table(trans ? LEX::STMT_READS_TEMP_TRANS_TABLE :
LEX::STMT_READS_TEMP_NON_TRANS_TABLE);
else
@@ -5885,10 +6158,10 @@ int THD::decide_logging_format(TABLE_LIST *tables)
}
if (prev_access_table && prev_access_table->file->ht !=
- table->table->file->ht)
+ table->file->ht)
multi_access_engine= TRUE;
- prev_access_table= table->table;
+ prev_access_table= table;
}
if (wsrep_binlog_format() != BINLOG_FORMAT_ROW)
@@ -6017,9 +6290,16 @@ int THD::decide_logging_format(TABLE_LIST *tables)
{
/*
5. Error: Cannot modify table that uses a storage engine
- limited to row-logging when binlog_format = STATEMENT
+ limited to row-logging when binlog_format = STATEMENT, except
+ if all tables that are updated are temporary tables
*/
- if (IF_WSREP((!WSREP(this) || wsrep_exec_mode == LOCAL_STATE),1))
+ if (!lex->stmt_writes_to_non_temp_table())
+ {
+ /* As all updated tables are temporary, nothing will be logged */
+ set_current_stmt_binlog_format_row();
+ }
+ else if (IF_WSREP((!WSREP(this) ||
+ wsrep_exec_mode == LOCAL_STATE),1))
{
my_error((error= ER_BINLOG_STMT_MODE_AND_ROW_ENGINE), MYF(0), "");
}
@@ -6078,7 +6358,8 @@ int THD::decide_logging_format(TABLE_LIST *tables)
clear_binlog_local_stmt_filter();
}
- if (error) {
+ if (unlikely(error))
+ {
DBUG_PRINT("info", ("decision: no logging since an error was generated"));
DBUG_RETURN(-1);
}
@@ -6087,10 +6368,8 @@ int THD::decide_logging_format(TABLE_LIST *tables)
"ROW" : "STATEMENT"));
if (variables.binlog_format == BINLOG_FORMAT_ROW &&
- (lex->sql_command == SQLCOM_UPDATE ||
- lex->sql_command == SQLCOM_UPDATE_MULTI ||
- lex->sql_command == SQLCOM_DELETE ||
- lex->sql_command == SQLCOM_DELETE_MULTI))
+ (sql_command_flags[lex->sql_command] &
+ (CF_UPDATES_DATA | CF_DELETES_DATA)))
{
String table_names;
/*
@@ -6104,14 +6383,14 @@ int THD::decide_logging_format(TABLE_LIST *tables)
if (table->table->file->ht->db_type == DB_TYPE_BLACKHOLE_DB &&
table->lock_type >= TL_WRITE_ALLOW_WRITE)
{
- table_names.append(table->table_name);
+ table_names.append(&table->table_name);
table_names.append(",");
}
}
if (!table_names.is_empty())
{
- bool is_update= (lex->sql_command == SQLCOM_UPDATE ||
- lex->sql_command == SQLCOM_UPDATE_MULTI);
+ bool is_update= MY_TEST(sql_command_flags[lex->sql_command] &
+ CF_UPDATES_DATA);
/*
Replace the last ',' with '.' for table_names
*/
@@ -6136,7 +6415,7 @@ int THD::decide_logging_format(TABLE_LIST *tables)
mysql_bin_log.is_open(),
(variables.option_bits & OPTION_BIN_LOG),
(uint) wsrep_binlog_format(),
- binlog_filter->db_ok(db)));
+ binlog_filter->db_ok(db.str)));
#endif
DBUG_RETURN(0);
@@ -6358,7 +6637,7 @@ CPP_UNNAMED_NS_START
{
DBUG_ASSERT(s < sizeof(m_ptr)/sizeof(*m_ptr));
DBUG_ASSERT(m_ptr[s] != 0);
- DBUG_ASSERT(m_alloc_checked == TRUE);
+ DBUG_SLOW_ASSERT(m_alloc_checked == TRUE);
return m_ptr[s];
}
@@ -6873,6 +7152,15 @@ static bool protect_against_unsafe_warning_flood(int unsafe_type)
DBUG_RETURN(unsafe_warning_suppression_active[unsafe_type]);
}
+MYSQL_TIME THD::query_start_TIME()
+{
+ MYSQL_TIME res;
+ variables.time_zone->gmt_sec_to_TIME(&res, query_start());
+ res.second_part= query_start_sec_part();
+ time_zone_used= 1;
+ return res;
+}
+
/**
Auxiliary method used by @c binlog_query() to raise warnings.
@@ -6936,11 +7224,12 @@ void THD::issue_unsafe_warnings()
@see decide_logging_format
+ @retval < 0 No logging of query (ok)
@retval 0 Success
-
- @retval nonzero If there is a failure when writing the query (e.g.,
- write failure), then the error code is returned.
+ @retval > 0 If there is a failure when writing the query (e.g.,
+ write failure), then the error code is returned.
*/
+
int THD::binlog_query(THD::enum_binlog_query_type qtype, char const *query_arg,
ulong query_len, bool is_trans, bool direct,
bool suppress_use, int errcode)
@@ -6966,7 +7255,7 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, char const *query_arg,
The current statement is to be ignored, and not written to
the binlog. Do not call issue_unsafe_warnings().
*/
- DBUG_RETURN(0);
+ DBUG_RETURN(-1);
}
/*
@@ -6979,8 +7268,14 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, char const *query_arg,
top-most close_thread_tables().
*/
if (this->locked_tables_mode <= LTM_LOCK_TABLES)
- if (int error= binlog_flush_pending_rows_event(TRUE, is_trans))
+ {
+ int error;
+ if (unlikely(error= binlog_flush_pending_rows_event(TRUE, is_trans)))
+ {
+ DBUG_ASSERT(error > 0);
DBUG_RETURN(error);
+ }
+ }
/*
Warnings for unsafe statements logged in statement format are
@@ -7022,7 +7317,7 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, char const *query_arg,
("is_current_stmt_binlog_format_row: %d",
is_current_stmt_binlog_format_row()));
if (is_current_stmt_binlog_format_row())
- DBUG_RETURN(0);
+ DBUG_RETURN(-1);
/* Fall through */
/*
@@ -7063,7 +7358,7 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, char const *query_arg,
}
binlog_table_maps= 0;
- DBUG_RETURN(error);
+ DBUG_RETURN(error >= 0 ? error : 1);
}
case THD::QUERY_TYPE_COUNT:
@@ -7098,12 +7393,12 @@ void THD::set_last_commit_gtid(rpl_gtid &gtid)
#endif
m_last_commit_gtid= gtid;
#ifndef EMBEDDED_LIBRARY
- if (changed_gtid &&
- session_tracker.get_tracker(SESSION_SYSVARS_TRACKER)->is_enabled())
+ if (changed_gtid && session_tracker.sysvars.is_enabled())
{
- session_tracker.get_tracker(SESSION_SYSVARS_TRACKER)->
+ DBUG_ASSERT(current_thd == this);
+ session_tracker.sysvars.
mark_as_changed(this, (LEX_CSTRING*)Sys_last_gtid_ptr);
- }
+ }
#endif
}
@@ -7263,7 +7558,7 @@ wait_for_commit::wait_for_prior_commit2(THD *thd)
thd->ENTER_COND(&COND_wait_commit, &LOCK_wait_commit,
&stage_waiting_for_prior_transaction_to_commit,
&old_stage);
- while ((loc_waitee= this->waitee) && !thd->check_killed())
+ while ((loc_waitee= this->waitee) && likely(!thd->check_killed(1)))
mysql_cond_wait(&COND_wait_commit, &LOCK_wait_commit);
if (!loc_waitee)
{
@@ -7453,4 +7748,105 @@ bool Discrete_intervals_list::append(Discrete_interval *new_interval)
DBUG_RETURN(0);
}
+
+void AUTHID::copy(MEM_ROOT *mem_root, const LEX_CSTRING *user_name,
+ const LEX_CSTRING *host_name)
+{
+ user.str= strmake_root(mem_root, user_name->str, user_name->length);
+ user.length= user_name->length;
+
+ host.str= strmake_root(mem_root, host_name->str, host_name->length);
+ host.length= host_name->length;
+}
+
+
+/*
+ Set from a string in 'user@host' format.
+ This method resebmles parse_user(),
+ but does not need temporary buffers.
+*/
+void AUTHID::parse(const char *str, size_t length)
+{
+ const char *p= strrchr(str, '@');
+ if (!p)
+ {
+ user.str= str;
+ user.length= length;
+ host= null_clex_str;
+ }
+ else
+ {
+ user.str= str;
+ user.length= (size_t) (p - str);
+ host.str= p + 1;
+ host.length= (size_t) (length - user.length - 1);
+ if (user.length && !host.length)
+ host= host_not_specified; // 'user@' -> 'user@%'
+ }
+ if (user.length > USERNAME_LENGTH)
+ user.length= USERNAME_LENGTH;
+ if (host.length > HOSTNAME_LENGTH)
+ host.length= HOSTNAME_LENGTH;
+}
+
+
+void Database_qualified_name::copy(MEM_ROOT *mem_root,
+ const LEX_CSTRING &db,
+ const LEX_CSTRING &name)
+{
+ m_db.length= db.length;
+ m_db.str= strmake_root(mem_root, db.str, db.length);
+ m_name.length= name.length;
+ m_name.str= strmake_root(mem_root, name.str, name.length);
+}
+
+
+bool Table_ident::append_to(THD *thd, String *str) const
+{
+ return (db.length &&
+ (append_identifier(thd, str, db.str, db.length) ||
+ str->append('.'))) ||
+ append_identifier(thd, str, table.str, table.length);
+}
+
+
+bool Qualified_column_ident::append_to(THD *thd, String *str) const
+{
+ return Table_ident::append_to(thd, str) || str->append('.') ||
+ append_identifier(thd, str, m_column.str, m_column.length);
+}
+
+
#endif /* !defined(MYSQL_CLIENT) */
+
+
+Query_arena_stmt::Query_arena_stmt(THD *_thd) :
+ thd(_thd)
+{
+ arena= thd->activate_stmt_arena_if_needed(&backup);
+}
+
+Query_arena_stmt::~Query_arena_stmt()
+{
+ if (arena)
+ thd->restore_active_arena(arena, &backup);
+}
+
+
+bool THD::timestamp_to_TIME(MYSQL_TIME *ltime, my_time_t ts,
+ ulong sec_part, ulonglong fuzzydate)
+{
+ time_zone_used= 1;
+ if (ts == 0 && sec_part == 0)
+ {
+ if (fuzzydate & TIME_NO_ZERO_DATE)
+ return 1;
+ set_zero_time(ltime, MYSQL_TIMESTAMP_DATETIME);
+ }
+ else
+ {
+ variables.time_zone->gmt_sec_to_TIME(ltime, ts);
+ ltime->second_part= sec_part;
+ }
+ return 0;
+}