/*
Copyright ( c ) 2000 , 2016 , Oracle and / or its affiliates .
Copyright ( c ) 2009 , 2026 , MariaDB plc .
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; version 2 of the License .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
#ifndef SQL_CLASS_INCLUDED
#define SQL_CLASS_INCLUDED
/* Classes in mysql */
#include <atomic>
#include <thread>
#include "dur_prop.h"
#include <waiting_threads.h>
#include "sql_array.h"
#include "sql_const.h"
#include "lex_ident.h"
#include "sql_used.h"
#include <mysql/plugin_audit.h>
#include "log.h"
#include "rpl_tblmap.h"
#include "mdl.h"
#include "field.h" // Create_field
#include "opt_trace_context.h"
#include "probes_mysql.h"
#include "sql_locale.h" /* my_locale_st */
#include "sql_profile.h" /* PROFILING */
#include "scheduler.h" /* thd_scheduler */
#include "protocol.h" /* Protocol_text, Protocol_binary */
#include "violite.h" /* vio_is_connected */
#include "thr_lock.h" /* thr_lock_type, THR_LOCK_DATA, THR_LOCK_INFO */
#include "thr_timer.h"
#include "thr_malloc.h"
#include "log_slow.h" /* LOG_SLOW_DISABLE_... */
#include <my_tree.h>
#include "sql_digest_stream.h" // sql_digest_state
#include <mysql/psi/mysql_stage.h>
#include <mysql/psi/mysql_statement.h>
#include <mysql/psi/mysql_idle.h>
#include <mysql/psi/mysql_table.h>
#include <mysql_com_server.h>
#include "session_tracker.h"
#include "sql_path.h"
#include "backup.h"
#include "xa.h"
#include "scope.h"
#include "ddl_log.h" /* DDL_LOG_STATE */
#include "ha_handler_stats.h" // ha_handler_stats */
#include "sql_basic_types.h" // enum class active_dml_stmt
#include "sql_trigger.h"
#include "opt_context_store_replay.h"
extern "C"
void set_thd_stage_info(void *thd,
const PSI_stage_info *new_stage,
PSI_stage_info *old_stage,
const char *calling_func,
const char *calling_file,
const unsigned int calling_line);
#define THD_STAGE_INFO(thd, stage) \
(thd)->enter_stage(&stage, __func__, __FILE__, __LINE__)
#include "my_apc.h"
#include "rpl_gtid_base.h"
#include "wsrep.h"
#include "wsrep_on.h"
#include <inttypes.h>
#include <ilist.h>
#ifdef WITH_WSREP
/* wsrep-lib */
#include "wsrep_client_service.h"
#include "wsrep_client_state.h"
#include "wsrep_mutex.h"
#include "wsrep_condition_variable.h"
class Wsrep_applier_service;
enum wsrep_consistency_check_mode {
NO_CONSISTENCY_CHECK,
CONSISTENCY_CHECK_DECLARED,
CONSISTENCY_CHECK_RUNNING,
};
#endif /* WITH_WSREP */
class Reprepare_observer;
class Relay_log_info;
struct rpl_group_info;
struct rpl_parallel_thread;
class Rpl_filter;
class Query_log_event;
class Log_event_writer;
class sp_rcontext;
class sp_cache;
class Lex_input_stream;
class Parser_state;
class Rows_log_event;
class Sroutine_hash_entry;
class user_var_entry;
struct Trans_binlog_info;
class rpl_io_thread_info;
class rpl_sql_thread_info;
#ifdef HAVE_REPLICATION
struct Slave_info;
#endif
enum enum_ha_read_modes { RFIRST, RNEXT, RPREV, RLAST, RKEY, RNEXT_SAME };
enum enum_duplicates { DUP_ERROR, DUP_REPLACE, DUP_UPDATE };
enum enum_delay_key_write { DELAY_KEY_WRITE_NONE, DELAY_KEY_WRITE_ON,
DELAY_KEY_WRITE_ALL };
enum enum_slave_exec_mode { SLAVE_EXEC_MODE_STRICT,
SLAVE_EXEC_MODE_IDEMPOTENT,
SLAVE_EXEC_MODE_LAST_BIT };
enum enum_slave_run_triggers_for_rbr { SLAVE_RUN_TRIGGERS_FOR_RBR_NO,
SLAVE_RUN_TRIGGERS_FOR_RBR_YES,
SLAVE_RUN_TRIGGERS_FOR_RBR_LOGGING,
SLAVE_RUN_TRIGGERS_FOR_RBR_ENFORCE};
enum enum_slave_type_conversions { SLAVE_TYPE_CONVERSIONS_ALL_LOSSY,
SLAVE_TYPE_CONVERSIONS_ALL_NON_LOSSY,
SLAVE_TYPE_CONVERSIONS_ERROR_IF_MISSING_FIELD };
enum read_only_options { READONLY_OFF, READONLY_ON, READONLY_NO_LOCK,
READONLY_NO_LOCK_NO_ADMIN};
/*
COLUMNS_READ : A column is goind to be read .
COLUMNS_WRITE : A column is going to be written to .
MARK_COLUMNS_READ : A column is goind to be read .
A bit in read set is set to inform handler that the field
is to be read . If field list contains duplicates , then
thd - > dup_field is set to point to the last found
duplicate .
MARK_COLUMNS_WRITE : A column is going to be written to .
A bit is set in write set to inform handler that it needs
to update this field in write_row and update_row .
*/
enum enum_column_usage
{ COLUMNS_READ, COLUMNS_WRITE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE};
static inline bool should_mark_column(enum_column_usage column_usage)
{ return column_usage >= MARK_COLUMNS_READ; }
enum enum_filetype { FILETYPE_CSV, FILETYPE_XML };
enum enum_binlog_row_image {
/** PKE in the before image and changed columns in the after image */
BINLOG_ROW_IMAGE_MINIMAL= 0 ,
/** Whenever possible, before and after image contain all columns except blobs. */
BINLOG_ROW_IMAGE_NOBLOB= 1 ,
/** All columns in both before and after image. */
BINLOG_ROW_IMAGE_FULL= 2 ,
/** All columns in before image, but only updated columns in after image */
BINLOG_ROW_IMAGE_FULL_NODUP= 3
};
/* Bits for different SQL modes modes (including ANSI mode) */
#define MODE_REAL_AS_FLOAT (1 ULL << 0 )
#define MODE_PIPES_AS_CONCAT (1 ULL << 1 )
#define MODE_ANSI_QUOTES (1 ULL << 2 )
#define MODE_IGNORE_SPACE (1 ULL << 3 )
#define MODE_IGNORE_BAD_TABLE_OPTIONS (1 ULL << 4 )
#define MODE_ONLY_FULL_GROUP_BY (1 ULL << 5 )
#define MODE_NO_UNSIGNED_SUBTRACTION (1 ULL << 6 )
#define MODE_NO_DIR_IN_CREATE (1 ULL << 7 )
#define MODE_POSTGRESQL (1 ULL << 8 )
#define MODE_ORACLE (1 ULL << 9 )
#define MODE_MSSQL (1 ULL << 10 )
#define MODE_DB2 (1 ULL << 11 )
#define MODE_MAXDB (1 ULL << 12 )
#define MODE_NO_KEY_OPTIONS (1 ULL << 13 )
#define MODE_NO_TABLE_OPTIONS (1 ULL << 14 )
#define MODE_NO_FIELD_OPTIONS (1 ULL << 15 )
#define MODE_MYSQL323 (1 ULL << 16 )
#define MODE_MYSQL40 (1 ULL << 17 )
#define MODE_ANSI (1 ULL << 18 )
#define MODE_NO_AUTO_VALUE_ON_ZERO (1 ULL << 19 )
#define MODE_NO_BACKSLASH_ESCAPES (1 ULL << 20 )
#define MODE_STRICT_TRANS_TABLES (1 ULL << 21 )
#define MODE_STRICT_ALL_TABLES (1 ULL << 22 )
#define MODE_NO_ZERO_IN_DATE (1 ULL << 23 )
#define MODE_NO_ZERO_DATE (1 ULL << 24 )
#define MODE_INVALID_DATES (1 ULL << 25 )
#define MODE_ERROR_FOR_DIVISION_BY_ZERO (1 ULL << 26 )
#define MODE_TRADITIONAL (1 ULL << 27 )
#define MODE_NO_AUTO_CREATE_USER (1 ULL << 28 )
#define MODE_HIGH_NOT_PRECEDENCE (1 ULL << 29 )
#define MODE_NO_ENGINE_SUBSTITUTION (1 ULL << 30 )
#define MODE_PAD_CHAR_TO_FULL_LENGTH (1 ULL << 31 )
/* SQL mode bits defined above are common for MariaDB and MySQL */
#define MODE_MASK_MYSQL_COMPATIBLE 0 xFFFFFFFFULL
/* The following modes are specific to MariaDB */
#define MODE_EMPTY_STRING_IS_NULL (1 ULL << 32 )
#define MODE_SIMULTANEOUS_ASSIGNMENT (1 ULL << 33 )
#define MODE_TIME_ROUND_FRACTIONAL (1 ULL << 34 )
/* The following modes are specific to MySQL */
#define MODE_MYSQL80_TIME_TRUNCATE_FRACTIONAL (1 ULL << 32 )
#define WAS_ORACLE (1 ULL << 35 )
#define IS_OR_WAS_ORACLE (MODE_ORACLE | WAS_ORACLE)
/* Bits for different old style modes */
#define OLD_MODE_NO_DUP_KEY_WARNINGS_WITH_IGNORE (1 << 0 )
#define OLD_MODE_NO_PROGRESS_INFO (1 << 1 )
#define OLD_MODE_ZERO_DATE_TIME_CAST (1 << 2 )
#define OLD_MODE_IGNORE_INDEX_ONLY_FOR_JOIN (1 << 3 )
#define OLD_MODE_COMPAT_5_1_CHECKSUM (1 << 4 )
#define OLD_MODE_NO_NULL_COLLATION_IDS (1 << 5 )
#define OLD_MODE_LOCK_ALTER_TABLE_COPY (1 << 6 )
#define OLD_MODE_OLD_FLUSH_STATUS (1 << 7 )
#define OLD_MODE_SESSION_USER_IS_USER (1 << 8 )
#define OLD_MODE_2_DIGIT_YEAR (1 << 9 )
#define OLD_MODE_UTF8_IS_UTF8MB3 (1 << 10 )
#define OLD_MODE_DEFAULT_VALUE 0
void old_mode_deprecated_warnings(ulonglong v);
/*
Bits for @ @ new_mode - > thd - > variables . new_behaviour system variable
See sys_vars . cc / new_mode_all_names
*/
#define NEW_MODE_MAX 0
/* Definitions above that have transitioned from new behaviour to default */
#define NOW_DEFAULT -1
#define TEST_NEW_MODE_FLAG(thd, flag) \
(flag == NOW_DEFAULT ? TRUE : thd->variables.new_behavior & flag)
extern char internal_table_name[2 ];
extern char empty_c_string[1 ];
extern MYSQL_PLUGIN_IMPORT const char **errmesg;
extern "C" LEX_STRING * thd_query_string (MYSQL_THD thd);
extern "C" unsigned long long thd_query_id(const MYSQL_THD thd);
extern "C" size_t thd_query_safe(MYSQL_THD thd, char *buf, size_t buflen);
extern "C" const char *thd_priv_user(MYSQL_THD thd, size_t *length);
extern "C" const char *thd_priv_host(MYSQL_THD thd, size_t *length);
extern "C" const char *thd_user_name(MYSQL_THD thd);
extern "C" const char *thd_client_host(MYSQL_THD thd);
extern "C" const char *thd_client_ip(MYSQL_THD thd);
extern "C" LEX_CSTRING *thd_current_db(MYSQL_THD thd);
extern "C" int thd_current_status(MYSQL_THD thd);
extern "C" enum enum_server_command thd_current_command(MYSQL_THD thd);
extern "C" int thd_double_innodb_cardinality(MYSQL_THD thd);
extern void mariadb_error_read_only();
/**
@ class CSET_STRING
@ brief Character set armed LEX_STRING
*/
class CSET_STRING
{
private :
LEX_STRING string;
CHARSET_INFO *cs;
public :
CSET_STRING() : cs(&my_charset_bin)
{
string.str= NULL;
string.length= 0 ;
}
CSET_STRING(char *str_arg, size_t length_arg, CHARSET_INFO *cs_arg) :
cs(cs_arg)
{
DBUG_ASSERT(cs_arg != NULL);
string.str= str_arg;
string.length= length_arg;
}
inline char *str() const { return string.str; }
inline size_t length() const { return string.length; }
CHARSET_INFO *charset() const { return cs; }
friend LEX_STRING * thd_query_string (MYSQL_THD thd);
};
template <typename T> class Slice
{
T m_offset;
T m_count;
public :
Slice(T offset, T count)
:m_offset(offset), m_count(count)
{ }
T offset() const { return m_offset; }
T count() const { return m_count; }
};
class Recreate_info
{
public :
ha_rows copied;
ha_rows duplicate;
uchar tabledef_version[MY_UUID_SIZE];
Recreate_info()
:copied(0 ),
duplicate(0 )
{
bzero(tabledef_version, sizeof (tabledef_version));
}
ha_rows records_copied() const { return copied; }
ha_rows records_duplicate() const { return duplicate; }
ha_rows records_processed() const
{
return copied + duplicate;
}
};
#define TC_HEURISTIC_RECOVER_COMMIT 1
#define TC_HEURISTIC_RECOVER_ROLLBACK 2
extern ulong tc_heuristic_recover;
typedef struct st_user_var_events
{
user_var_entry *user_var_event;
char *value;
size_t length;
const Type_handler *th;
uint charset_number;
} BINLOG_USER_VAR_EVENT;
/*
The COPY_INFO structure is used by INSERT / REPLACE code .
The schema of the row counting by the INSERT / INSERT . . . ON DUPLICATE KEY
UPDATE code :
If a row is inserted then the copied variable is incremented .
If a row is updated by the INSERT . . . ON DUPLICATE KEY UPDATE and the
new data differs from the old one then the copied and the updated
variables are incremented .
The touched variable is incremented if a row was touched by the update part
of the INSERT . . . ON DUPLICATE KEY UPDATE no matter whether the row
was actually changed or not .
*/
typedef struct st_copy_info {
ha_rows records; /**< Number of processed records */
ha_rows deleted; /**< Number of deleted records */
ha_rows updated; /**< Number of updated records */
ha_rows copied; /**< Number of copied records */
ha_rows accepted_rows; /**< Number of accepted original rows
(same as number of rows in RETURNING) */
ha_rows error_count;
ha_rows touched; /* Number of touched records */
enum enum_duplicates handle_duplicates;
int escape_char, last_errno;
bool ignore;
/* for INSERT ... UPDATE */
List<Item> *update_fields;
List<Item> *update_values;
/* for VIEW ... WITH CHECK OPTION */
TABLE_LIST *view;
TABLE_LIST *table_list; /* Normal table */
} COPY_INFO;
class Key_part_spec :public Sql_alloc {
public :
Lex_ident_column field_name;
uint length;
bool generated, asc;
Key_part_spec(const LEX_CSTRING *name, uint len, bool gen= false )
: field_name(*name), length(len), generated(gen), asc(1 )
{}
bool operator ==(const Key_part_spec& other) const ;
/**
Construct a copy of this Key_part_spec . field_name is copied
by - pointer as it is known to never change . At the same time
' length ' may be reset in mysql_prepare_create_table , and this
is why we supply it with a copy .
@ return If out of memory , 0 is returned and an error is set in
THD .
*/
Key_part_spec *clone(MEM_ROOT *mem_root) const
{ return new (mem_root) Key_part_spec(*this ); }
bool check_key_for_blob(const class handler *file) const ;
bool check_key_length_for_blob() const ;
bool check_primary_key_for_blob(const class handler *file) const
{
return check_key_for_blob(file) || check_key_length_for_blob();
}
bool check_foreign_key_for_blob(const class handler *file) const
{
return check_key_for_blob(file) || check_key_length_for_blob();
}
bool init_multiple_key_for_blob(const class handler *file);
};
class Alter_drop :public Sql_alloc {
public :
enum drop_type { KEY, COLUMN, FOREIGN_KEY, CHECK_CONSTRAINT, PERIOD };
Lex_ident_column name;
enum drop_type type;
bool drop_if_exists;
Alter_drop(enum drop_type par_type,
const LEX_CSTRING &par_name,
bool par_exists)
:name(par_name), type(par_type), drop_if_exists(par_exists)
{
DBUG_ASSERT(par_name.str != NULL);
}
/**
Used to make a clone of this object for ALTER / CREATE TABLE
@ sa comment for Key_part_spec : : clone
*/
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 == PERIOD ? "PERIOD" :
type == KEY ? "INDEX" : "FOREIGN KEY" ;
}
};
class Alter_column :public Sql_alloc {
public :
LEX_CSTRING name;
LEX_CSTRING new_name;
Virtual_column_info *default_value;
bool alter_if_exists;
Alter_column(LEX_CSTRING par_name, Virtual_column_info *expr, bool par_exists)
:name(par_name), new_name{NULL, 0 }, default_value(expr), alter_if_exists(par_exists) {}
Alter_column(LEX_CSTRING par_name, LEX_CSTRING _new_name, bool exists)
:name(par_name), new_name(_new_name), default_value(NULL), alter_if_exists(exists) {}
/**
Used to make a clone of this object for ALTER / CREATE TABLE
@ sa comment for Key_part_spec : : clone
*/
Alter_column *clone(MEM_ROOT *mem_root) const
{ return new (mem_root) Alter_column(*this ); }
bool is_rename()
{
DBUG_ASSERT(!new_name.str || !default_value);
return new_name.str;
}
};
class Alter_rename_key : public Sql_alloc
{
public :
const Lex_ident_column old_name;
const Lex_ident_column new_name;
bool alter_if_exists;
Alter_rename_key(LEX_CSTRING old_name_arg, LEX_CSTRING new_name_arg, bool exists)
: old_name(old_name_arg), new_name(new_name_arg), alter_if_exists(exists) {}
Alter_rename_key *clone(MEM_ROOT *mem_root) const
{ return new (mem_root) Alter_rename_key(*this ); }
};
/* An ALTER INDEX operation that changes the ignorability of an index. */
class Alter_index_ignorability: public Sql_alloc
{
public :
Alter_index_ignorability(const LEX_CSTRING &name,
bool is_ignored, bool if_exists) :
m_name(name), m_is_ignored(is_ignored), m_if_exists(if_exists)
{
DBUG_ASSERT(name.str != NULL);
}
const Lex_ident_column &name() const { return m_name; }
bool if_exists() const { return m_if_exists; }
/* The ignorability after the operation is performed. */
bool is_ignored() const { return m_is_ignored; }
Alter_index_ignorability *clone(MEM_ROOT *mem_root) const
{ return new (mem_root) Alter_index_ignorability(*this ); }
private :
const Lex_ident_column m_name;
bool m_is_ignored;
bool m_if_exists;
};
class Key :public Sql_alloc, public DDL_options {
public :
enum Keytype { PRIMARY, UNIQUE, MULTIPLE, FULLTEXT, SPATIAL, VECTOR,
FOREIGN_KEY, IGNORE_KEY};
enum Keytype type;
KEY_CREATE_INFO key_create_info;
List<Key_part_spec> columns;
Lex_ident_column name;
engine_option_value *option_list;
bool generated;
bool invisible;
bool without_overlaps;
bool old;
uint length;
Lex_ident_column period;
Key(enum Keytype type_par, const LEX_CSTRING *name_arg,
ha_key_alg algorithm_arg, bool generated_arg, DDL_options_st ddl_options)
:DDL_options(ddl_options),
type(type_par), key_create_info(default_key_create_info),
name(*name_arg), option_list(NULL), generated(generated_arg),
invisible(false ), without_overlaps(false ), old(false ), length(0 )
{
key_create_info.algorithm= algorithm_arg;
}
Key(enum Keytype type_par, const LEX_CSTRING *name_arg,
KEY_CREATE_INFO *key_info_arg,
bool generated_arg, List<Key_part_spec> *cols,
engine_option_value *create_opt, DDL_options_st ddl_options)
:DDL_options(ddl_options),
type(type_par), key_create_info(*key_info_arg), columns(*cols),
name(*name_arg), option_list(create_opt), generated(generated_arg),
invisible(false ), without_overlaps(false ), old(false ), length(0 )
{}
Key(const Key &rhs, MEM_ROOT *mem_root);
virtual ~Key() = default ;
/* Equality comparison of keys (ignoring name) */
friend bool is_foreign_key_prefix(Key *a, Key *b);
/**
Used to make a clone of this object for ALTER / CREATE TABLE
@ sa comment for Key_part_spec : : clone
*/
virtual Key *clone(MEM_ROOT *mem_root) const
{ return new (mem_root) Key(*this , mem_root); }
};
class Foreign_key: public Key {
public :
enum fk_match_opt { FK_MATCH_UNDEF, FK_MATCH_FULL,
FK_MATCH_PARTIAL, FK_MATCH_SIMPLE};
LEX_CSTRING constraint_name;
LEX_CSTRING ref_db;
LEX_CSTRING ref_table;
List<Key_part_spec> ref_columns;
enum enum_fk_option delete_opt, update_opt;
enum fk_match_opt match_opt;
Foreign_key(const LEX_CSTRING *name_arg, List<Key_part_spec> *cols,
const LEX_CSTRING *constraint_name_arg,
const LEX_CSTRING *ref_db_arg, const LEX_CSTRING *ref_table_arg,
List<Key_part_spec> *ref_cols,
enum_fk_option delete_opt_arg, enum_fk_option update_opt_arg,
fk_match_opt match_opt_arg,
DDL_options ddl_options)
:Key(FOREIGN_KEY, name_arg, &default_key_create_info, 0 , cols, NULL,
ddl_options),
constraint_name(*constraint_name_arg),
ref_db(*ref_db_arg), ref_table(*ref_table_arg), ref_columns(*ref_cols),
delete_opt(delete_opt_arg), update_opt(update_opt_arg),
match_opt(match_opt_arg)
{
}
Foreign_key(const Foreign_key &rhs, MEM_ROOT *mem_root);
/**
Used to make a clone of this object for ALTER / CREATE TABLE
@ sa comment for Key_part_spec : : clone
*/
Key *clone(MEM_ROOT *mem_root) const override
{ return new (mem_root) Foreign_key(*this , mem_root); }
/* Used to validate foreign key options */
bool validate(List<Create_field> &table_fields);
};
typedef struct st_mysql_lock
{
TABLE **table;
THR_LOCK_DATA **locks;
uint table_count,lock_count;
uint flags;
} MYSQL_LOCK;
class LEX_COLUMN : public Sql_alloc
{
public :
String column;
privilege_t rights;
LEX_COLUMN (const String& x,const privilege_t & y ): column (x),rights (y) {}
};
class MY_LOCALE;
/**
Query_cache_tls - - query cache thread local data .
*/
struct Query_cache_block;
struct Query_cache_tls
{
/*
' first_query_block ' should be accessed only via query cache
functions and methods to maintain proper locking .
*/
Query_cache_block *first_query_block;
void set_first_query_block(Query_cache_block *first_query_block_arg)
{
first_query_block= first_query_block_arg;
}
Query_cache_tls() :first_query_block(NULL) {}
};
/* SIGNAL / RESIGNAL / GET DIAGNOSTICS */
/**
This enumeration list all the condition item names of a condition in the
SQL condition area .
*/
typedef enum enum_diag_condition_item_name
{
/*
Conditions that can be set by the user ( SIGNAL / RESIGNAL ) ,
and by the server implementation .
*/
DIAG_CLASS_ORIGIN= 0 ,
FIRST_DIAG_SET_PROPERTY= DIAG_CLASS_ORIGIN,
DIAG_SUBCLASS_ORIGIN= 1 ,
DIAG_CONSTRAINT_CATALOG= 2 ,
DIAG_CONSTRAINT_SCHEMA= 3 ,
DIAG_CONSTRAINT_NAME= 4 ,
DIAG_CATALOG_NAME= 5 ,
DIAG_SCHEMA_NAME= 6 ,
DIAG_TABLE_NAME= 7 ,
DIAG_COLUMN_NAME= 8 ,
DIAG_CURSOR_NAME= 9 ,
DIAG_MESSAGE_TEXT= 10 ,
DIAG_MYSQL_ERRNO= 11 ,
DIAG_ROW_NUMBER= 12 ,
LAST_DIAG_SET_PROPERTY= DIAG_ROW_NUMBER
} Diag_condition_item_name;
/**
Name of each diagnostic condition item .
This array is indexed by Diag_condition_item_name .
*/
extern const LEX_CSTRING Diag_condition_item_names[];
/**
These states are bit coded with HARD . For each state there must be a pair
< state_even_num > , and < state_odd_num > _ HARD .
*/
enum killed_state : uint32_t
{
NOT_KILLED= 0 ,
KILL_HARD_BIT= 1 , /* Bit for HARD KILL */
KILL_BAD_DATA= 2 ,
KILL_BAD_DATA_HARD= 3 ,
KILL_QUERY= 4 ,
KILL_QUERY_HARD= 5 ,
/*
ABORT_QUERY signals to the query processor to stop execution ASAP without
issuing an error . Instead a warning is issued , and when possible a partial
query result is returned to the client .
*/
ABORT_QUERY= 6 ,
ABORT_QUERY_HARD= 7 ,
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= 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))
enum killed_type
{
KILL_TYPE_ID,
KILL_TYPE_USER,
KILL_TYPE_QUERY
};
#define SECONDS_TO_WAIT_FOR_KILL 2
#define SECONDS_TO_WAIT_FOR_DUMP_THREAD_KILL 10
#if !defined (_WIN32) && defined (HAVE_SELECT)
/* my_sleep() can wait for sub second times */
#define WAIT_FOR_KILL_TRY_TIMES 20
#else
#define WAIT_FOR_KILL_TRY_TIMES 2
#endif
#include "sql_lex.h" /* Must be here */
class Delayed_insert;
class select_result;
class Time_zone;
#define THD_SENTRY_MAGIC 0 xfeedd1ff
#define THD_SENTRY_GONE 0 xdeadbeef
#define THD_CHECK_SENTRY(thd) DBUG_ASSERT(thd->dbug_sentry == THD_SENTRY_MAGIC)
typedef struct system_variables
{
/*
How dynamically allocated system variables are handled :
The global_system_variables and max_system_variables are " authoritative "
They both should have the same ' version ' and ' size ' .
When attempting to access a dynamic variable , if the session version
is out of date , then the session version is updated and realloced if
neccessary and bytes copied from global to make up for missing data .
Note that one should use my_bool instead of bool here , as the variables
are used with my_getopt . c
*/
ulong dynamic_variables_version;
char * dynamic_variables_ptr;
uint dynamic_variables_head; /* largest valid variable offset */
uint dynamic_variables_size; /* how many bytes are in use */
ulonglong max_heap_table_size;
ulonglong tmp_memory_table_size;
ulonglong tmp_disk_table_size;
ulonglong log_slow_query_time;
ulonglong log_slow_always_query_time;
ulonglong max_statement_time;
ulonglong optimizer_switch;
ulonglong optimizer_trace;
ulonglong optimizer_join_limit_pref_ratio;
sql_mode_t sql_mode; ///< which non-standard SQL behaviour should be enabled
sql_mode_t old_behavior; ///< which old SQL behaviour should be enabled
sql_mode_t new_behavior; ///< which new SQL behaviour should be enabled
ulonglong option_bits; ///< OPTION_xxx constants, e.g. OPTION_PROFILING
ulonglong join_buff_space_limit;
ulonglong log_slow_filter;
ulonglong log_slow_verbosity;
ulonglong log_slow_disabled_statements;
ulonglong log_disabled_statements;
ulonglong note_verbosity;
ulonglong bulk_insert_buff_size;
ulonglong join_buff_size;
ulonglong sortbuff_size;
ulonglong default_regex_flags;
ulonglong max_mem_used;
ulonglong max_rowid_filter_size;
ulonglong create_temporary_table_binlog_formats;
/**
Place holders to store Multi - source variables in sys_var . cc during
update and show of variables .
*/
ulonglong slave_skip_counter;
ulonglong max_relay_log_size;
ulonglong max_tmp_space_usage;
double optimizer_where_cost, optimizer_scan_setup_cost;
double log_slow_query_time_double, max_statement_time_double;
double log_slow_always_query_time_double;
double sample_percentage;
ha_rows select_limit;
ha_rows max_join_size;
ha_rows expensive_subquery_limit;
#ifdef WITH_WSREP
/*
Stored values of the auto_increment_increment and auto_increment_offset
that are will be restored when wsrep_auto_increment_control will be set
to ' OFF ' , because the setting it to ' ON ' leads to overwriting of the
original values ( which are set by the user ) by calculated ones ( which
are based on the cluster size ) :
*/
ulonglong wsrep_gtid_seq_no;
ulong saved_auto_increment_increment, saved_auto_increment_offset;
ulong saved_lock_wait_timeout;
#endif /* WITH_WSREP */
uint analyze_max_length;
ulong auto_increment_increment, auto_increment_offset;
ulong column_compression_zlib_strategy;
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_insert_delayed_threads;
ulong min_examined_row_limit;
ulong net_buffer_length;
ulong net_interactive_timeout;
ulong net_read_timeout;
ulong net_retry_count;
ulong net_wait_timeout;
ulong net_write_timeout;
ulong optimizer_extra_pruning_depth;
ulong optimizer_prune_level;
ulong optimizer_search_depth;
ulong optimizer_selectivity_sampling_limit;
ulong optimizer_use_condition_selectivity;
ulong optimizer_max_sel_arg_weight;
ulong optimizer_max_sel_args;
ulong optimizer_trace_max_mem_size;
ulong optimizer_adjust_secondary_key_costs;
ulong use_stat_tables;
ulong histogram_size;
ulong histogram_type;
ulong preload_buff_size;
ulong profiling_history_size;
ulong read_buff_size;
ulong read_rnd_buff_size;
ulong mrr_buff_size;
ulong div_precincrement;
/* Total size of all buffers used by the subselect_rowid_merge_engine. */
ulong rowid_merge_buff_size;
ulong max_sp_recursion_depth;
ulong default_week_format;
ulong max_seeks_for_key;
ulong range_alloc_block_size;
ulong query_alloc_block_size;
ulong query_prealloc_size;
ulong trans_alloc_block_size;
ulong trans_prealloc_size;
ulong log_warnings;
ulong block_encryption_mode;
ulong log_slow_max_warnings;
/* Flags for slow log filtering */
ulong log_slow_rate_limit;
ulong binlog_format; ///< binlog format for this thd (see enum_binlog_format)
ulong binlog_row_image;
ulong progress_report_time;
ulong completion_type;
ulong query_cache_type;
ulong tx_isolation;
ulong updatable_views_with_limit;
ulong alter_algorithm_unused;
ulong server_id;
ulong session_track_transaction_info;
ulong threadpool_priority;
ulong vers_alter_history;
/* deadlock detection */
ulong wt_timeout_short, wt_deadlock_search_depth_short;
ulong wt_timeout_long, wt_deadlock_search_depth_long;
/**
In slave thread we need to know in behalf of which
thread the query is being run to replicate temp tables properly
*/
my_thread_id pseudo_thread_id;
/**
When replicating an event group with GTID , keep these values around so
slave binlog can receive the same GTID as the original .
*/
uint64 gtid_seq_no;
uint32 gtid_domain_id;
uint group_concat_max_len;
uint eq_range_index_dive_limit;
uint idle_transaction_timeout;
uint idle_readonly_transaction_timeout;
uint idle_write_transaction_timeout;
uint column_compression_threshold;
uint column_compression_zlib_level;
uint in_subquery_conversion_threshold;
uint log_slow_max_query_length;
uint max_open_cursors;
int max_user_connections;
/**
Default transaction access mode . READ ONLY ( true ) or READ WRITE ( false ) .
*/
my_bool tx_read_only;
my_bool low_priority_updates;
my_bool query_cache_wlock_invalidate;
my_bool keep_files_on_create;
my_bool old_passwords;
my_bool only_standard_compliant_cte;
my_bool query_cache_strip_comments;
my_bool sql_log_slow;
my_bool sql_log_bin;
my_bool binlog_annotate_row_events;
my_bool binlog_direct_non_trans_update;
my_bool column_compression_zlib_wrap;
my_bool sysdate_is_now;
my_bool wsrep_on;
my_bool wsrep_dirty_reads;
my_bool pseudo_slave_mode;
my_bool session_track_schema;
my_bool session_track_state_change;
#ifdef USER_VAR_TRACKING
my_bool session_track_user_variables;
#endif // USER_VAR_TRACKING
my_bool tcp_nodelay;
my_bool optimizer_record_context;
/* Name of the user @variable that has the context we're replaying */
char *optimizer_replay_context;
plugin_ref table_plugin;
plugin_ref tmp_table_plugin;
plugin_ref enforced_table_plugin;
/* Only charset part of these variables is sensible */
CHARSET_INFO *character_set_filesystem;
CHARSET_INFO *character_set_client;
CHARSET_INFO *character_set_results;
/* Both charset and collation parts of these variables are important */
CHARSET_INFO *collation_server;
CHARSET_INFO *collation_database;
CHARSET_INFO *collation_connection;
/* Names. These will be allocated in buffers in thd */
LEX_CSTRING default_master_connection;
/* Error messages */
MY_LOCALE *lc_messages;
const char ***errmsgs; /* lc_messages->errmsg->errmsgs */
/* Locale Support */
MY_LOCALE *lc_time_names;
Time_zone *time_zone;
char *session_track_system_variables;
char *redirect_url;
/* Some wsrep variables */
ulonglong wsrep_trx_fragment_size;
ulong wsrep_retry_autocommit;
ulong wsrep_trx_fragment_unit;
ulong wsrep_OSU_method;
uint wsrep_sync_wait;
vers_asof_timestamp_t vers_asof_timestamp;
my_bool binlog_alter_two_phase;
Charset_collation_map_st character_set_collations;
Sql_path path;
} SV;
/**
Per thread status variables .
Must be long / ulong up to last_system_status_var so that
add_to_status / add_diff_to_status can work .
*/
typedef struct system_status_var
{
ulong column_compressions;
ulong column_decompressions;
ulong com_stat[(uint) SQLCOM_END];
ulong com_create_tmp_table;
ulong com_drop_tmp_table;
ulong com_other;
ulong com_stmt_prepare;
ulong com_stmt_reprepare;
ulong com_stmt_execute;
ulong com_stmt_send_long_data;
ulong com_stmt_fetch;
ulong com_stmt_reset;
ulong com_stmt_close;
ulong com_register_slave;
ulong created_tmp_disk_tables_;
ulong created_tmp_tables_;
ulong ha_commit_count;
ulong ha_delete_count;
ulong ha_read_first_count;
ulong ha_read_last_count;
ulong ha_read_key_count;
ulong ha_read_key_miss;
ulong ha_read_next_count;
ulong ha_read_prev_count;
ulong ha_read_retry_count;
ulong ha_read_rnd_count;
ulong ha_read_rnd_next_count;
ulong ha_read_rnd_deleted_count;
/*
This number doesn ' t include calls to the default implementation and
calls made by range access . The intent is to count only calls made by
BatchedKeyAccess .
*/
ulong ha_mrr_init_count;
ulong ha_mrr_key_refills_count;
ulong ha_mrr_rowid_refills_count;
ulong ha_rollback_count;
ulong ha_update_count;
ulong ha_write_count;
/* The following are for internal temporary tables */
ulong ha_tmp_update_count;
ulong ha_tmp_write_count;
ulong ha_tmp_delete_count;
ulong ha_prepare_count;
ulong ha_icp_attempts;
ulong ha_icp_match;
ulong ha_discover_count;
ulong ha_savepoint_count;
ulong ha_savepoint_rollback_count;
ulong ha_external_lock_count;
ulong opened_tables;
ulong opened_shares;
ulong opened_views; /* +1 opening a view */
ulong select_full_join_count_;
ulong select_full_range_join_count_;
ulong select_range_count_;
ulong select_range_check_count_;
ulong select_scan_count_;
ulong update_scan_count;
ulong delete_scan_count;
ulong executed_triggers;
ulong long_query_count;
ulong filesort_merge_passes_;
ulong filesort_range_count_;
ulong filesort_rows_;
ulong filesort_scan_count_;
ulong filesort_pq_sorts_;
ulong optimizer_join_prefixes_check_calls;
/* Features used */
ulong feature_custom_aggregate_functions; /* +1 when custom aggregate
functions are used */
ulong feature_dynamic_columns; /* +1 when creating a dynamic column */
ulong feature_fulltext; /* +1 when MATCH is used */
ulong feature_gis; /* +1 opening table with GIS features */
ulong feature_invisible_columns; /* +1 opening table with invisible column */
ulong feature_json; /* +1 when JSON function is used */
ulong feature_locale; /* +1 when LOCALE is set */
ulong feature_subquery; /* +1 when subqueries are used */
ulong feature_system_versioning; /* +1 opening table WITH SYSTEM VERSIONING */
ulong feature_application_time_periods;
/* +1 opening a table with application-time period */
ulong feature_insert_returning; /* +1 when INSERT...RETURNING is used */
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_vector_index; /* +1 when open table with vector index */
ulong feature_window_functions; /* +1 when window functions are used */
ulong feature_into_outfile; /* +1 when INTO OUTFILE is used */
ulong feature_into_variable; /* +1 when INTO VARIABLE is used */
/* From MASTER_GTID_WAIT usage */
ulong master_gtid_wait_timeouts; /* Number of timeouts */
ulong master_gtid_wait_time; /* Time in microseconds */
ulong master_gtid_wait_count;
ulong empty_queries;
ulong access_denied_errors;
ulong lost_connections;
ulong max_statement_time_exceeded;
/*
Number of times where column info was not
sent with prepared statement metadata .
*/
ulong skip_metadata_count;
/*
Number of statements sent from the client
*/
ulong questions;
/*
IMPORTANT !
SEE last_system_status_var DEFINITION BELOW .
Below ' last_system_status_var ' are all variables that cannot be handled
automatically by add_to_status ( ) / add_diff_to_status ( ) .
*/
ulonglong bytes_received;
ulonglong bytes_sent;
ulonglong rows_read;
ulonglong rows_sent;
ulonglong rows_tmp_read;
ulonglong binlog_bytes_written;
ulonglong table_open_cache_hits;
ulonglong table_open_cache_misses;
ulonglong table_open_cache_overflows;
ulonglong cpu_time, busy_time, query_time;
double last_query_cost;
uint32 threads_running;
/* Memory used by internal temporary tables and on disk transaction cache */
ulonglong max_tmp_space_used;
/* Memory used for thread local storage */
int64 max_local_memory_used;
/*
Following variables are not cleared by FLUSH STATUS
Don ' t copy them back to THD after show status
*/
ulonglong tmp_space_used;
volatile int64 local_memory_used;
/* Memory allocated for global usage */
volatile int64 global_memory_used;
time_t flush_status_time;
} STATUS_VAR;
/*
This is used for ' SHOW STATUS ' . It must be updated to the last ulong
variable in system_status_var which is makes sense to add to the global
counter
*/
#define last_system_status_var questions
/* Parameters to set_status_var_init() */
#define STATUS_OFFSET(A) offsetof(STATUS_VAR,A)
/* Clear as part of flush */
#define clear_for_flush_status STATUS_OFFSET(tmp_space_used)
/* Clear as part of a new connection and reuse connection */
#define clear_for_new_connection STATUS_OFFSET(max_tmp_space_used)
/* Full initialization. Note that global_memory_used is updated early! */
#define clear_for_server_start STATUS_OFFSET(global_memory_used)
#define last_restored_status_var clear_for_flush_status
/** Number of contiguous global status variables */
constexpr int COUNT_GLOBAL_STATUS_VARS=
int (STATUS_OFFSET(last_system_status_var) /sizeof (ulong)) + 1 ;
/*
Global status variables
*/
extern ulong feature_files_opened_with_delayed_keys, feature_check_constraint;
void add_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var);
void add_diff_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var,
STATUS_VAR *dec_var);
uint calc_sum_of_all_status(STATUS_VAR *to);
static inline void calc_sum_of_all_status_if_needed(STATUS_VAR *to)
{
if (to->local_memory_used == 0 )
{
mysql_mutex_lock(&LOCK_status);
*to= global_status_var;
mysql_mutex_unlock(&LOCK_status);
calc_sum_of_all_status(to);
DBUG_ASSERT(to->local_memory_used);
}
}
/*
Update global_memory_used . We have to do this with atomic_add as the
global value can change outside of LOCK_status .
*/
static inline void update_global_memory_status(int64 size)
{
DBUG_PRINT("info" , ("global memory_used: %lld size: %lld" ,
(longlong) global_status_var.global_memory_used,
size));
// workaround for gcc 4.2.4-1ubuntu4 -fPIE (from DEB_BUILD_HARDENING=1)
int64 volatile * volatile ptr= &global_status_var.global_memory_used;
my_atomic_add64_explicit(ptr, size, MY_MEMORY_ORDER_RELAXED);
}
static inline bool is_supported_parser_charset(CHARSET_INFO *cs)
{
return MY_TEST(cs->mbminlen == 1 && cs->number != 17 /* filename */);
}
/** THD registry */
class THD_list_iterator
{
protected :
I_List<THD> threads;
mutable mysql_rwlock_t lock;
public :
/**
Iterates registered threads .
@ param action called for every element
@ param argument opaque argument passed to action
@ return
@ retval 0 iteration completed successfully
@ retval 1 iteration was interrupted ( action returned 1 )
*/
template <typename T> int iterate(my_bool (*action)(THD *thd, T *arg), T *arg= 0 )
{
int res= 0 ;
mysql_rwlock_rdlock(&lock);
I_List_iterator<THD> it(threads);
while (auto tmp= it++)
if ((res= action(tmp, arg)))
break ;
mysql_rwlock_unlock(&lock);
return res;
}
static THD_list_iterator *iterator();
};
/**
A counter of THDs
It must be specified as a first base class of THD , so that increment is
done before any other THD constructors and decrement - after any other THD
destructors .
Destructor unblocks close_connection ( ) if there are no more THD ' s left .
*/
struct THD_count
{
static Atomic_counter<uint32_t> count;
static uint value() { return static_cast <uint>(count); }
static uint connection_thd_count();
THD_count() { count++; }
~THD_count() { count--; }
};
#ifdef MYSQL_SERVER
#include "select_result.h"
#include "statement_rcontext.h"
void free_tmp_table(THD *thd, TABLE *entry);
/* The following macro is to make init of Query_arena simpler */
#ifdef DBUG_ASSERT_EXISTS
#define INIT_ARENA_DBUG_INFO is_backup_arena= 0 ; is_reprepared= FALSE ;
#else
#define INIT_ARENA_DBUG_INFO
#endif
class Query_arena
{
public :
/*
List of items created in the parser for this query . Every item puts
itself to the list on creation ( see Item : : Item ( ) for details ) )
*/
Item *free_list;
MEM_ROOT *mem_root; // Pointer to current memroot
#ifdef DBUG_ASSERT_EXISTS
bool is_backup_arena; /* True if this arena is used for backup. */
bool is_reprepared;
#endif
/*
The state reflects three different life cycles for three
different types of statements :
Prepared statement : STMT_INITIALIZED - > STMT_PREPARED - > STMT_EXECUTED .
Stored procedure : STMT_INITIALIZED_FOR_SP - > STMT_EXECUTED .
Other statements : STMT_CONVENTIONAL_EXECUTION never changes .
Special case for stored procedure arguments : STMT_SP_QUERY_ARGUMENTS
This state never changes and used for objects
whose lifetime is whole duration of function call
( sp_rcontext , it ' s tables and items . etc ) . Such objects
should be deallocated after every execution of a stored
routine . Caller ' s arena / memroot can ' t be used for
placing such objects since memory allocated on caller ' s
arena not freed until termination of user ' s session .
*/
enum enum_state
{
STMT_INITIALIZED= 0 , STMT_INITIALIZED_FOR_SP= 1 , STMT_PREPARED= 2 ,
STMT_CONVENTIONAL_EXECUTION= 3 , STMT_EXECUTED= 4 ,
STMT_SP_QUERY_ARGUMENTS= 5 , STMT_ERROR= -1
};
enum_state state;
/*
Bit - ORed mask of Item : : with_flag for * some * items in free_list .
The goal is to have the cumulated COMPLEX_DATA_TYPE flag .
So far only only some items can can COMPLEX_DATA_TYPE :
Item_param , Item_func , Item_sp_variable , Item_row
For other Item types this flag is not collected .
*/
item_with_t with_flags_bit_or_for_complex_data_types;
bool with_complex_data_types() const
{
return (bool ) (with_flags_bit_or_for_complex_data_types &
item_with_t::COMPLEX_DATA_TYPE);
}
/*
Raise an error if free_list contains items with complex data types .
@ param op - the operation name for the error message , e . g . " CREATE VIEW "
@ return - true if the error was raised , or false otherwise
*/
bool check_free_list_no_complex_data_types(const char *op);
public :
/* We build without RTTI, so dynamic_cast can't be used. */
enum Type
{
STATEMENT, PREPARED_STATEMENT, STORED_PROCEDURE
};
Query_arena(MEM_ROOT *mem_root_arg, enum enum_state state_arg) :
free_list(0 ), mem_root(mem_root_arg), state(state_arg),
with_flags_bit_or_for_complex_data_types(item_with_t::NONE)
{ INIT_ARENA_DBUG_INFO; }
/*
This constructor is used only when Query_arena is created as
backup storage for another instance of Query_arena .
*/
Query_arena() { INIT_ARENA_DBUG_INFO; }
virtual Type type() const ;
virtual ~Query_arena() = default ;
inline bool is_stmt_prepare() const { return state == STMT_INITIALIZED; }
inline bool is_stmt_prepare_or_first_sp_execute() const
{ return (int )state < (int )STMT_PREPARED; }
inline bool is_stmt_prepare_or_first_stmt_execute() const
{ return (int )state <= (int )STMT_PREPARED; }
inline bool is_stmt_execute() const
{ return state == STMT_PREPARED || state == STMT_EXECUTED; }
inline bool is_conventional() const
{ return state == STMT_CONVENTIONAL_EXECUTION; }
template <typename T=char >
inline T* alloc(size_t size) const
{
return (T*)alloc_root(mem_root, sizeof (T)*size);
}
template <typename T=char >
inline T* calloc(size_t size) const
{
void * ptr= alloc_root(mem_root, sizeof (T)*size);
if (ptr)
bzero(ptr, sizeof (T)*size);
return (T*)ptr;
}
inline char *strdup(const char *str) const
{ return strdup_root(mem_root,str); }
inline char *strmake(const char *str, size_t size) const
{ return strmake_root(mem_root,str,size); }
inline LEX_CSTRING strcat(const LEX_CSTRING &a, const LEX_CSTRING &b) const
{
char *buf= alloc(a.length + b.length + 1 );
if (unlikely(!buf))
return null_clex_str;
memcpy(buf, a.str, a.length);
memcpy(buf + a.length, b.str, b.length);
buf[a.length + b.length] = 0 ;
return {buf, a.length + b.length};
}
inline void *memdup(const void *str, size_t size) const
{ return memdup_root(mem_root,str,size); }
inline void *memdup_w_gap(const void *str, size_t size, size_t gap) const
{
void *ptr;
if (likely((ptr= alloc_root(mem_root,size+gap))))
memcpy(ptr,str,size);
return ptr;
}
/*
Methods to copy a string to the memory root
and return the value as a LEX_CSTRING .
*/
LEX_STRING strmake_lex_string(const char *str, size_t length) const
{
char *tmp= strmake_root(mem_root, str, length);
if (!tmp)
return {0 ,0 };
return {tmp, length};
}
LEX_CSTRING strmake_lex_cstring(const char *str, size_t length) const
{
return strmake_lex_string(str, length);
}
LEX_CSTRING strmake_lex_cstring(const LEX_CSTRING &from) const
{
return strmake_lex_cstring(from.str, from.length);
}
LEX_CUSTRING strmake_lex_custring(const LEX_CUSTRING &from) const
{
const void *tmp= memdup(from.str, from.length);
if (!tmp)
return {0 ,0 };
return {(const uchar*)tmp, from.length};
}
LEX_CSTRING strmake_lex_cstring_trim_whitespace(const LEX_CSTRING &from,
CHARSET_INFO *cs)
{
return strmake_lex_cstring(Lex_cstring(from).trim_whitespace(cs));
}
/*
Methods to copy a string to memory root ,
write the result to the out parameter ,
and return as a LEX_STRING / LEX_CSTRING pointer .
*/
LEX_STRING *make_lex_string(LEX_STRING *lex_str, const char * str,
size_t length) const
{
if (!(lex_str->str= strmake_root(mem_root, str, length)))
{
lex_str->length= 0 ;
return 0 ;
}
lex_str->length= length;
return lex_str;
}
LEX_CSTRING *make_lex_string(LEX_CSTRING *lex_str, const char * str,
size_t length) const
{
if (!(lex_str->str= strmake_root(mem_root, str, length)))
{
lex_str->length= 0 ;
return 0 ;
}
lex_str->length= length;
return lex_str;
}
/*
Methods to copy a string value on memory root ,
but also allocate on memory root LEX_CSTRING itself
and return a pointer to it .
*/
LEX_CSTRING *make_clex_string(const char * str, size_t length) const
{
LEX_CSTRING *lex_str;
char *tmp;
if (unlikely(!(lex_str= (LEX_CSTRING *)alloc_root(mem_root,
sizeof (LEX_CSTRING) +
length+1 ))))
return 0 ;
tmp= (char *) (lex_str+1 );
lex_str->str= tmp;
memcpy(tmp, str, length);
tmp[length]= 0 ;
lex_str->length= length;
return lex_str;
}
LEX_CSTRING *make_clex_string(const LEX_CSTRING from) const
{
return make_clex_string(from.str, from.length);
}
// Allocate LEX_STRING for character set conversion
bool alloc_lex_string(LEX_STRING *dst, size_t length) const
{
if (likely((dst->str= alloc(length))))
return false ;
dst->length= 0 ; // Safety
return true ; // EOM
}
// Remove double quotes: aaa""bbb -> aaa"bbb
bool quote_unescape(LEX_CSTRING *dst, const LEX_CSTRING *src,
char quote) const
{
const char *tmp= src->str;
const char *tmpend= src->str + src->length;
char *to;
if (!(dst->str= to= alloc(src->length + 1 )))
{
dst->length= 0 ; // Safety
return true ;
}
for ( ; tmp < tmpend; )
{
if ((*to++= *tmp++) == quote)
tmp++; // Skip double quotes
}
*to= 0 ; // End null for safety
dst->length= to - dst->str;
return false ;
}
/*
Make a lower - cased copy of an identifier on mem_root .
@ param src - The original identifier ( usually coming from the parser )
@ return - { NULL , 0 } in case of EOM , or a non - NULL LEX_STRING
with the lower - cased identifier copy .
*/
LEX_STRING make_ident_casedn(const LEX_CSTRING &src)
{
return lex_string_casedn_root(mem_root, &my_charset_utf8mb3_general_ci,
src.str, src.length);
}
/*
Make an exact copy or a lower - cased copy of an identifier on mem_root .
@ param src - The original identifier ( usually coming from the parser )
@ param casedn - If the name should be converted to lower case
@ return - { NULL , 0 } in case of EOM ,
or a non - NULL LEX_STRING with the identifier copy .
*/
LEX_STRING make_ident_opt_casedn(const LEX_CSTRING &src, bool casedn)
{
return casedn ? make_ident_casedn(src) :
lex_string_strmake_root(mem_root, src.str, src.length);
}
template <typename Lex_ident_XXX>
Lex_ident_XXX lex_ident_copy(const Lex_ident_XXX &src)
{
return Lex_ident_XXX(strmake_lex_cstring(src));
}
template <typename Lex_ident_XXX>
Lex_ident_XXX lex_ident_casedn(const Lex_ident_XXX &src)
{
return Lex_ident_XXX(make_ident_casedn(src));
}
/*
Convert a LEX_CSTRING to a valid database name :
- validated with Lex_ident_fs : : check_db_name ( )
- optionally lower - cased
The lower - cased copy is created on Query_arena : : mem_root , when needed .
@ param name - The name to normalize . Must not be { NULL , 0 } .
@ param casedn - If the name should be lower - cased .
@ return - { NULL , 0 } on EOM or a bad database name
( with an errror is raised ,
or a good database name otherwise .
*/
Lex_ident_db to_ident_db_opt_casedn_with_error(const LEX_CSTRING &name,
bool casedn);
/*
Convert a LEX_CSTRING to a valid internal database name :
- validated with Lex_ident_db : : check_name ( )
- optionally lower - cased when lower_case_table_names = = 1
The lower - cased copy is created on Query_arena : : mem_root , when needed .
@ param name - The name to normalize . Must not be { NULL , 0 } .
@ return - { NULL , 0 } on EOM or a bad database name
( with an errror is raised ,
or a good database name otherwise .
*/
Lex_ident_db to_ident_db_internal_with_error(const LEX_CSTRING &name)
{
return to_ident_db_opt_casedn_with_error(name, lower_case_table_names == 1 );
}
/*
Convert a LEX_CSTRING to a valid normalized database name :
- validated with Lex_ident_fs : : check_db_name ( )
- optionally lower - cased when lower_case_table_names > 0
The lower - cased copy is created on Query_arena : : mem_root , when needed .
@ param name - The name to normalize . Must not be { NULL , 0 } .
@ return - { NULL , 0 } on EOM or a bad database name
( with an errror is raised ,
or a good database name otherwise .
*/
Lex_ident_db_normalized to_ident_db_normalized_with_error(
const LEX_CSTRING &name)
{
Lex_ident_db tmp= to_ident_db_opt_casedn_with_error(name,
lower_case_table_names > 0 );
return Lex_ident_db_normalized(tmp);
}
void set_query_arena(Query_arena *set);
void expr_event_handler_for_free_list(THD *thd, expr_event_t event);
void free_items();
/* Close the active state associated with execution of this statement */
virtual bool cleanup_stmt(bool /*restore_set_statement_vars*/);
};
class Query_arena_memroot: public Query_arena, public Sql_alloc
{
public :
Query_arena_memroot(MEM_ROOT *mem_root_arg, enum enum_state state_arg) :
Query_arena(mem_root_arg, state_arg)
{}
Query_arena_memroot() : Query_arena()
{}
virtual ~Query_arena_memroot() = default ;
};
class Query_arena_stmt
{
THD *thd;
Query_arena backup;
Query_arena *arena;
public :
Query_arena_stmt(THD *_thd);
~Query_arena_stmt();
bool arena_replaced()
{
return arena != NULL;
}
};
/*
Struct to catch changes in column metadata that is sent to client .
in the " result set metadata " . Used to support
MARIADB_CLIENT_CACHE_METADATA .
*/
struct send_column_info_state
{
/* Last client charset (affects metadata) */
CHARSET_INFO *last_charset= nullptr;
/* Checksum, only used to check changes if 'immutable' is false*/
uint32 checksum= 0 ;
/*
Column info can only be changed by PreparedStatement : : reprepare ( )
There is a class of " weird " prepared statements like SELECT ? or SELECT @ a
that are not immutable , and depend on input parameters or user variables
*/
bool immutable= false ;
bool initialized= false ;
/* Used by PreparedStatement::reprepare()*/
void reset()
{
initialized= false ;
checksum= 0 ;
}
};
/* Bits in sql_command_flags */
enum cf_flags_t : uint {
CF_CHANGES_DATA = 1 U << 0 ,
CF_REPORT_PROGRESS = 1 U << 1 ,
CF_STATUS_COMMAND = 1 U << 2 ,
CF_SHOW_TABLE_COMMAND = 1 U << 3 ,
CF_WRITE_LOGS_COMMAND = 1 U << 4 ,
/**
Must be set for SQL statements that may contain
Item expressions and / or use joins and tables .
Indicates that the parse tree of such statement may
contain rule - based optimizations that depend on metadata
( i . e . number of columns in a table ) , and consequently
that the statement must be re - prepared whenever
referenced metadata changes . Must not be set for
statements that themselves change metadata , e . g . RENAME ,
ALTER and other DDL , since otherwise will trigger constant
reprepare . Consequently , complex item expressions and
joins are currently prohibited in these statements .
*/
CF_REEXECUTION_FRAGILE = 1 U << 5 ,
/**
Implicitly commit before the SQL statement is executed .
Statements marked with this flag will cause any active
transaction to end ( commit ) before proceeding with the
command execution .
This flag should be set for statements that probably can ' t
be rolled back or that do not expect any previously metadata
locked tables .
*/
CF_IMPLICIT_COMMIT_BEGIN = 1 U << 6 ,
/**
Implicitly commit after the SQL statement .
Statements marked with this flag are automatically committed
at the end of the statement .
This flag should be set for statements that will implicitly
open and take metadata locks on system tables that should not
be carried for the whole duration of a active transaction .
*/
CF_IMPLICIT_COMMIT_END = 1 U << 7 ,
/**
CF_IMPLICT_COMMIT_BEGIN and CF_IMPLICIT_COMMIT_END are used
to ensure that the active transaction is implicitly committed
before and after every DDL statement and any statement that
modifies our currently non - transactional system tables .
*/
#define CF_AUTO_COMMIT_TRANS (CF_IMPLICIT_COMMIT_BEGIN | CF_IMPLICIT_COMMIT_END)
/**
Diagnostic statement .
Diagnostic statements :
- SHOW WARNING
- SHOW ERROR
- GET DIAGNOSTICS ( WL # 2111 )
do not modify the diagnostics area during execution .
*/
CF_DIAGNOSTIC_STMT = 1 U << 8 ,
/**
Identifies statements that may generate row events
and that may end up in the binary log .
*/
CF_CAN_GENERATE_ROW_EVENTS = 1 U << 9 ,
/**
Identifies statements which may deal with temporary tables and for which
temporary tables should be pre - opened to simplify privilege checks .
*/
CF_PREOPEN_TMP_TABLES = 1 U << 10 ,
/**
Identifies statements for which open handlers should be closed in the
beginning of the statement .
*/
CF_HA_CLOSE = 1 U << 11 ,
/**
Identifies statements that can be explained with EXPLAIN .
*/
CF_CAN_BE_EXPLAINED = 1 U << 12 ,
/** Identifies statements which may generate an optimizer trace */
CF_OPTIMIZER_TRACE = 1 U << 14 ,
/**
Identifies statements that should always be disallowed in
read only transactions .
*/
CF_DISALLOW_IN_RO_TRANS = 1 U << 15 ,
/**
Statement that need the binlog format to be unchanged .
*/
CF_FORCE_ORIGINAL_BINLOG_FORMAT = 1 U << 16 ,
/**
Statement that inserts new rows ( INSERT , REPLACE , LOAD , ALTER TABLE )
*/
CF_INSERTS_DATA = 1 U << 17 ,
/**
Statement that updates existing rows ( UPDATE , multi - update )
*/
CF_UPDATES_DATA = 1 U << 18 ,
/**
Not logged into slow log as " admin commands "
*/
CF_ADMIN_COMMAND = 1 U << 19 ,
/**
SP Bulk execution safe
*/
CF_PS_ARRAY_BINDING_SAFE = 1 U << 20 ,
/**
SP Bulk execution optimized
*/
CF_PS_ARRAY_BINDING_OPTIMIZED = 1 U << 21 ,
/**
If command creates or drops a table
*/
CF_SCHEMA_CHANGE = 1 U << 22 ,
/**
If command creates or drops a database
*/
CF_DB_CHANGE = 1 U << 23 ,
/**
Statement that deletes existing rows ( DELETE , DELETE_MULTI )
*/
CF_DELETES_DATA = 1 U << 24 ,
#ifdef WITH_WSREP
/**
DDL statement that may be subject to error filtering .
*/
CF_WSREP_MAY_IGNORE_ERRORS = 1 U << 25 ,
/**
Basic DML statements that create writeset .
*/
CF_WSREP_BASIC_DML = 1 U << 26 ,
#endif /* WITH_WSREP */
};
static inline constexpr cf_flags_t operator |(cf_flags_t a, cf_flags_t b)
{
return static_cast <cf_flags_t>(static_cast <ulonglong>(a) |
static_cast <ulonglong>(b));
}
static inline cf_flags_t& operator |=(cf_flags_t &a, cf_flags_t b)
{
return a= a | b;
}
extern cf_flags_t sql_command_flags[];
/**
@ class Statement
@ brief State of a single command executed against this connection .
One connection can contain a lot of simultaneously running statements ,
some of which could be :
- prepared , that is , contain placeholders ,
- opened as cursors . We maintain 1 to 1 relationship between
statement and cursor - if user wants to create another cursor for his
query , we create another statement for it .
To perform some action with statement we reset THD part to the state of
that statement , do the action , and then save back modified state from THD
to the statement . It will be changed in near future , and Statement will
be used explicitly .
*/
class Statement: public ilink, public Query_arena
{
Statement(const Statement &rhs); /* not implemented: */
Statement &operator =(const Statement &rhs); /* non-copyable */
public :
/*
Uniquely identifies each statement object in thread scope ; change during
statement lifetime . FIXME : must be const
*/
ulong id;
enum enum_column_usage column_usage;
LEX_CSTRING name; /* name for named prepared statements */
LEX *lex; // parse tree descriptor
my_hrtime_t hr_prepare_time; // time of preparation in microseconds
/*
Points to the query associated with this statement . It ' s const , but
we need to declare it char * because all table handlers are written
in C and need to point to it .
Note that if we set query = NULL , we must at the same time set
query_length = 0 , and protect the whole operation with
LOCK_thd_data mutex . To avoid crashes in races , if we do not
know that thd - > query cannot change at the moment , we should print
thd - > query like this :
( 1 ) reserve the LOCK_thd_data mutex ;
( 2 ) print or copy the value of query and query_length
( 3 ) release LOCK_thd_data mutex .
This printing is needed at least in SHOW PROCESSLIST and SHOW
ENGINE INNODB STATUS .
*/
CSET_STRING query_string;
/*
If opt_query_cache_strip_comments is set , this contains query without
comments . If not set , it contains pointer to query_string .
*/
String base_query;
inline char *query() const { return query_string.str(); }
inline uint32 query_length() const
{
return static_cast <uint32>(query_string.length());
}
inline char *query_end() const
{
return query_string.str() + query_string.length();
}
CHARSET_INFO *query_charset() const { return query_string.charset(); }
void set_query_inner(const CSET_STRING &string_arg)
{
query_string= string_arg;
}
void set_query_inner(char *query_arg, uint32 query_length_arg,
CHARSET_INFO *cs_arg)
{
set_query_inner(CSET_STRING(query_arg, query_length_arg, cs_arg));
}
void reset_query_inner()
{
set_query_inner(CSET_STRING());
}
cf_flags_t sql_command_flags() const
{
return ::sql_command_flags[lex->sql_command];
}
/**
Name of the current ( default ) database .
If there is the current ( default ) database , " db . str " contains its name . If
there is no current ( default ) database , " db . str " is NULL and " db . length " is
0 . In other words , db must either be NULL , or contain a
valid database name .
*/
LEX_CSTRING db;
send_column_info_state column_info_state;
/* This is set to 1 of last call to send_result_to_client() was ok */
my_bool query_cache_is_applicable;
/* This constructor is called for backup statements */
Statement() = default ;
Statement(LEX *lex_arg, MEM_ROOT *mem_root_arg,
enum enum_state state_arg, ulong id_arg);
virtual ~Statement();
/* Assign execution context (note: not all members) of given stmt to self */
virtual void set_statement(Statement *stmt);
void set_n_backup_statement(Statement *stmt, Statement *backup);
void restore_backup_statement(Statement *stmt, Statement *backup);
/* return class type */
Type type() const override;
private :
Dynamic_array<active_dml_stmt> m_running_stmts{PSI_INSTRUMENT_MEM};
/**
Stack of events of triggers being invoked on running a DML statement .
E . g . if there is a trigger BEFORE INSERT ON t1 that calls the statement
` DELETE FROM t2 ` and there is a BEFORE DELETE trigger for the table t2
that runs the statement ` UPDATE t3 ` and there is a BEFORE UPDATE trigger
for the table t3 then at the moment when the statement ` UPDATE t3 . . . `
be invoked , the stack m_running_trgs would contain the following events :
top - > TRG_EVENT_UPDATE
TRG_EVENT_DELETE
bottom - > TRG_EVENT_INSERT
}
*/
Dynamic_array<trg_event_type> m_running_trgs{PSI_INSTRUMENT_MEM};
public :
active_dml_stmt current_active_stmt();
bool push_active_stmt(active_dml_stmt new_active_stmt);
void pop_current_active_stmt();
trg_event_type current_trg_event();
bool push_current_trg_event(trg_event_type trg_event);
void pop_current_trg_event();
};
/**
This class is responsible for storing a kind of current DML statement
for further matching with type of statement represented by the clauses
INSERTING / UPDATING / DELETING .
On handling the statements INSERT / UPDATE / DELETE the corresponding type
of the statement specified by the enum active_dml_stmt is pushed on top of
the Statement ' s stack in constructor of the class Running_stmt_guard and
popped up on finishing execution of the statement by destructor of the class
Running_stmt_guard .
Every time when the one of the clauses INSERTING / UPDATING / DELETING
is evaluated , the last pushed type of DML statement matched with the type
representing by the clause INSERTING / UPDATING / DELETING .
@ see Item_trigger_type_of_statement : : val_bool ( )
*/
class Running_stmt_guard
{
Statement *m_stmt;
public :
Running_stmt_guard(Statement *stmt,
active_dml_stmt new_active_stmt)
: m_stmt{stmt}
{
m_stmt->push_active_stmt(new_active_stmt);
}
~Running_stmt_guard()
{
m_stmt->pop_current_active_stmt();
}
};
/**
Container for all statements created / used in a connection .
Statements in Statement_map have unique Statement : : id ( guaranteed by id
assignment in Statement : : Statement )
Non - empty statement names are unique too : attempt to insert a new statement
with duplicate name causes older statement to be deleted
Statements are auto - deleted when they are removed from the map and when the
map is deleted .
*/
class Statement_map
{
public :
Statement_map();
int insert(THD *thd, Statement *statement);
Statement *find_by_name(const LEX_CSTRING *name)
{
Statement *stmt;
stmt= (Statement*)my_hash_search(&names_hash, (uchar*)name->str,
name->length);
return stmt;
}
Statement *find(ulong id)
{
if (last_found_statement == 0 || id != last_found_statement->id)
{
Statement *stmt;
stmt= (Statement *) my_hash_search(&st_hash, (uchar *) &id, sizeof (id));
if (stmt && stmt->name.str)
return NULL;
last_found_statement= stmt;
}
return last_found_statement;
}
/*
Close all cursors of this connection that use tables of a storage
engine that has transaction - specific state and therefore can not
survive COMMIT or ROLLBACK . Currently all but MyISAM cursors are closed .
*/
void close_transient_cursors();
void erase(Statement *statement);
/* Erase all statements (calls Statement destructor) */
void reset();
~Statement_map();
private :
HASH st_hash;
HASH names_hash;
I_List<Statement> transient_cursor_list;
Statement *last_found_statement;
};
struct st_savepoint {
struct st_savepoint *prev;
char *name;
uint length;
Ha_trx_info *ha_list;
/** State of metadata locks before this savepoint was set. */
MDL_savepoint mdl_savepoint;
};
/**
@ class Security_context
@ brief A set of THD members describing the current authenticated user .
*/
class Security_context {
public :
Security_context()
:master_access(NO_ACL),
db_access(NO_ACL)
{} /* Remove gcc warning */
/*
host - host of the client
user - user of the client , set to NULL until the user has been read from
the connection
priv_user - The user privilege we are using . May be " " for anonymous user .
ip - client IP
*/
const char *host;
const char *user, *ip;
char priv_user[USERNAME_LENGTH];
char proxy_user[USERNAME_LENGTH + MAX_HOSTNAME + 5 ];
/* The host privilege we are using */
char priv_host[MAX_HOSTNAME];
/* The role privilege we are using */
char priv_role[USERNAME_LENGTH];
/* The external user (if available) */
char *external_user;
/* points to host if host is available, otherwise points to ip */
const char *host_or_ip;
access_t master_access; /* Global privileges from mysql.user */
access_t db_access; /* Privileges for current db */
bool password_expired;
void init();
void destroy();
void skip_grants();
inline char *priv_host_name()
{
return (*priv_host ? priv_host : (char *)"%" );
}
bool set_user(char *user_arg);
#ifndef NO_EMBEDDED_ACCESS_CHECKS
bool
change_security_context(THD *thd,
LEX_CSTRING *definer_user,
LEX_CSTRING *definer_host,
LEX_CSTRING *db,
Security_context **backup);
void
restore_security_context(THD *thd, Security_context *backup);
#endif
bool priv_user_matches(const Security_context *) const ;
/**
Check global access
@ param want_access The required privileges
@ param match_any if the security context must match all or any of the req .
* privileges .
@ return True if the security context fulfills the access requirements .
*/
bool check_access(const privilege_t want_access, bool match_any = false );
bool is_priv_user(const LEX_CSTRING &user, const LEX_CSTRING &host) const ;
bool is_user_defined() const
{ return user && user != delayed_user && user != slave_user && user != wsrep_user; };
};
/**
A registry for item tree transformations performed during
query optimization . We register only those changes which require
a rollback to re - execute a prepared statement or stored procedure
yet another time .
*/
struct Item_change_record;
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());
}
};
/**
Type of locked tables mode .
See comment for THD : : locked_tables_mode for complete description .
*/
enum enum_locked_tables_mode
{
LTM_NONE= 0 ,
LTM_LOCK_TABLES,
LTM_PRELOCKED,
/*
TODO : remove LTM_PRELOCKED_UNDER_LOCK_TABLES : it is never used apart from
LTM_LOCK_TABLES .
*/
LTM_PRELOCKED_UNDER_LOCK_TABLES,
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
by the thread . It is also used to save / restore this information in
push_open_tables_state ( ) / pop_open_tables_state ( ) .
*/
class Open_tables_state
{
public :
/**
As part of class THD , this member is set during execution
of a prepared statement . When it is set , it is used
by the locking subsystem to report a change in table metadata .
When Open_tables_state part of THD is reset to open
a system or INFORMATION_SCHEMA table , the member is cleared
to avoid spurious ER_NEED_REPREPARE errors - - system and
INFORMATION_SCHEMA tables are not subject to metadata version
tracking .
@ sa check_and_update_table_version ( )
*/
Reprepare_observer *m_reprepare_observer;
/**
List of regular tables in use by this thread . Contains temporary and
base tables that were opened with @ see open_tables ( ) .
*/
TABLE *open_tables;
/**
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 .
*/
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
statement execution , and all acquired locks are stored in ' lock '
member . Unlocking takes place automatically as well , when the
statement ends .
Manual mode comes into play when a user issues a ' LOCK TABLES '
statement . In this mode the user can only use the locked tables .
Trying to use any other tables will give an error .
The locked tables are also stored in this member , however ,
thd - > locked_tables_mode is turned on . Manual locking is described in
the ' LOCK_TABLES ' chapter of the MySQL manual .
See also lock_tables ( ) for details .
*/
MYSQL_LOCK *lock;
/*
CREATE - SELECT keeps an extra lock for the table being
created . This field is used to keep the extra lock available for
lower level routines , which would otherwise miss that lock .
*/
MYSQL_LOCK *extra_lock;
/*
Enum enum_locked_tables_mode and locked_tables_mode member are
used to indicate whether the so - called " locked tables mode " is on ,
and what kind of mode is active .
Locked tables mode is used when it ' s necessary to open and
lock many tables at once , for usage across multiple
( sub - ) statements .
This may be necessary either for queries that use stored functions
and triggers , in which case the statements inside functions and
triggers may be executed many times , or for implementation of
LOCK TABLES , in which case the opened tables are reused by all
subsequent statements until a call to UNLOCK TABLES .
The kind of locked tables mode employed for stored functions and
triggers is also called " prelocked mode " .
In this mode , first open_tables ( ) call to open the tables used
in a statement analyses all functions used by the statement
and adds all indirectly used tables to the list of tables to
open and lock .
It also marks the parse tree of the statement as requiring
prelocking . After that , lock_tables ( ) locks the entire list
of tables and changes THD : : locked_tables_modeto LTM_PRELOCKED .
All statements executed inside functions or triggers
use the prelocked tables , instead of opening their own ones .
Prelocked mode is turned off automatically once close_thread_tables ( )
of the main statement is called .
*/
enum enum_locked_tables_mode locked_tables_mode;
uint current_tablenr;
enum enum_flags {
BACKUPS_AVAIL = (1 U << 0 ) /* There are backups available */
};
/*
Flags with information about the open tables state .
*/
uint state_flags;
/**
This constructor initializes Open_tables_state instance which can only
be used as backup storage . To prepare Open_tables_state instance for
operations which open / lock / close tables ( e . g . open_table ( ) ) one has to
call init_open_tables_state ( ) .
*/
Open_tables_state() : state_flags(0 U) { }
void set_open_tables_state(Open_tables_state *state)
{
*this = *state;
}
void reset_open_tables_state()
{
open_tables= 0 ;
temporary_tables= 0 ;
derived_tables= 0 ;
rec_tables= 0 ;
extra_lock= 0 ;
lock= 0 ;
locked_tables_mode= LTM_NONE;
state_flags= 0 U;
m_reprepare_observer= NULL;
}
};
/**
Storage for backup of Open_tables_state . Must
be used only to open system tables ( TABLE_CATEGORY_SYSTEM
and TABLE_CATEGORY_LOG ) .
*/
class Open_tables_backup: public Open_tables_state
{
public :
/**
When we backup the open tables state to open a system
table or tables , we want to save state of metadata
locks which were acquired before the backup . It is used
to release metadata locks on system tables after they are
no longer used .
*/
MDL_savepoint mdl_system_tables_svp;
};
/**
@ class Sub_statement_state
@ brief Used to save context when executing a function or trigger
operations on stat tables aren ' t technically a sub - statement , but they are
similar in a sense that they cannot change the transaction status .
*/
/* Defines used for Sub_statement_state::in_sub_stmt */
#define SUB_STMT_TRIGGER 1
#define SUB_STMT_FUNCTION 2
#define SUB_STMT_STAT_TABLES 4
#define SUB_STMT_BEFORE_TRIGGER 8
#define SUB_STMT_PS_SAFE_CONTEXT 16 // e.g. func used in assignment right hand
class Sub_statement_state
{
public :
Discrete_interval auto_inc_interval_for_cur_row;
Discrete_intervals_list auto_inc_intervals_forced;
SAVEPOINT *savepoints;
ulonglong option_bits;
ulonglong first_successful_insert_id_in_prev_stmt;
ulonglong first_successful_insert_id_in_cur_stmt, insert_id_for_cur_row;
ulonglong limit_found_rows;
ulonglong tmp_tables_size;
ulonglong client_capabilities;
ulonglong cuted_fields, sent_row_count, examined_row_count;
ulonglong sent_row_count_for_statement, examined_row_count_for_statement;
ulonglong affected_rows;
ulonglong bytes_sent_old;
ulonglong max_tmp_space_used;
ha_handler_stats handler_stats;
ulong tmp_tables_used;
ulong tmp_tables_disk_used;
ulong query_plan_fsort_passes;
ulong query_plan_flags;
uint in_sub_stmt; /* 0, SUB_STMT_TRIGGER or SUB_STMT_FUNCTION */
bool enable_slow_log;
bool last_insert_id_used;
bool in_stored_procedure;
bool do_union;
enum enum_check_fields count_cuted_fields;
};
/* Flags for the THD::system_thread variable */
enum enum_thread_type
{
NON_SYSTEM_THREAD= 0 ,
SYSTEM_THREAD_DELAYED_INSERT= 1 ,
SYSTEM_THREAD_SLAVE_IO= 2 ,
SYSTEM_THREAD_SLAVE_SQL= 4 ,
SYSTEM_THREAD_EVENT_SCHEDULER= 8 ,
SYSTEM_THREAD_EVENT_WORKER= 16 ,
SYSTEM_THREAD_BINLOG_BACKGROUND= 32 ,
SYSTEM_THREAD_SLAVE_BACKGROUND= 64 ,
SYSTEM_THREAD_GENERIC= 128 ,
SYSTEM_THREAD_SEMISYNC_MASTER_BACKGROUND= 256
};
inline char const *
show_system_thread(enum_thread_type thread)
{
#define RETURN_NAME_AS_STRING(NAME) case (NAME): return #NAME
switch (thread) {
static char buf[64 ];
RETURN_NAME_AS_STRING(NON_SYSTEM_THREAD);
RETURN_NAME_AS_STRING(SYSTEM_THREAD_DELAYED_INSERT);
RETURN_NAME_AS_STRING(SYSTEM_THREAD_SLAVE_IO);
RETURN_NAME_AS_STRING(SYSTEM_THREAD_SLAVE_SQL);
RETURN_NAME_AS_STRING(SYSTEM_THREAD_EVENT_SCHEDULER);
RETURN_NAME_AS_STRING(SYSTEM_THREAD_EVENT_WORKER);
RETURN_NAME_AS_STRING(SYSTEM_THREAD_SLAVE_BACKGROUND);
RETURN_NAME_AS_STRING(SYSTEM_THREAD_SEMISYNC_MASTER_BACKGROUND);
default :
snprintf(buf, sizeof (buf), "<UNKNOWN SYSTEM THREAD: %d>" , thread);
return buf;
}
#undef RETURN_NAME_AS_STRING
}
/**
This class represents the interface for internal error handlers .
Internal error handlers are exception handlers used by the server
implementation .
*/
class Internal_error_handler
{
protected :
Internal_error_handler() :
m_prev_internal_handler(NULL)
{}
virtual ~Internal_error_handler() = default ;
public :
/**
Handle a sql condition .
This method can be implemented by a subclass to achieve any of the
following :
- mask a warning / error internally , prevent exposing it to the user ,
- mask a warning / error and throw another one instead .
When this method returns true , the sql condition is considered
' handled ' , and will not be propagated to upper layers .
It is the responsibility of the code installing an internal handler
to then check for trapped conditions , and implement logic to recover
from the anticipated conditions trapped during runtime .
This mechanism is similar to C + + try / throw / catch :
- ' try ' correspond to < code > THD : : push_internal_handler ( ) < / code > ,
- ' throw ' correspond to < code > my_error ( ) < / code > ,
which invokes < code > my_message_sql ( ) < / code > ,
- ' catch ' correspond to checking how / if an internal handler was invoked ,
before removing it from the exception stack with
< code > THD : : pop_internal_handler ( ) < / code > .
@ param thd the calling thread
@ param cond the condition raised .
@ return true if the condition is handled
*/
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) = 0 ;
private :
Internal_error_handler *m_prev_internal_handler;
friend class THD;
};
/**
Implements the trivial error handler which cancels all error states
and prevents an SQLSTATE to be set .
Remembers the first error
*/
class Dummy_error_handler : public Internal_error_handler
{
uint m_unhandled_errors;
uint first_error;
public :
Dummy_error_handler()
: m_unhandled_errors(0 ), first_error(0 )
{}
bool handle_condition(THD *thd,
uint sql_errno,
const char * sqlstate,
Sql_condition::enum_warning_level *level,
const char * msg,
Sql_condition ** cond_hdl) override
{
m_unhandled_errors++;
if (!first_error)
first_error= sql_errno;
return TRUE ; // Ignore error
}
bool any_error() { return m_unhandled_errors != 0 ; }
uint got_error() { return first_error; }
};
/**
Implements the trivial error handler which counts errors as they happen .
*/
class Counting_error_handler : public Internal_error_handler
{
public :
int errors;
bool handle_condition(THD *thd,
uint sql_errno,
const char * sqlstate,
Sql_condition::enum_warning_level *level,
const char * msg,
Sql_condition ** cond_hdl) override
{
if (*level == Sql_condition::WARN_LEVEL_ERROR)
errors++;
return false ;
}
Counting_error_handler() : errors(0 ) {}
};
extern "C" void my_message_sql(uint error, const char *str, myf MyFlags);
/**
Error handler that captures and postpones errors .
Warnings and notes are passed through to the next handler .
Stored errors can be re - emitted later via emit_errors ( ) .
*/
class Postponed_error_handler : public Internal_error_handler
{
struct Error_entry
{
uint sql_errno;
char message[MYSQL_ERRMSG_SIZE];
Error_entry *next;
};
Error_entry *m_first;
Error_entry *m_last;
MEM_ROOT *m_mem_root;
public :
Postponed_error_handler(MEM_ROOT *mem_root)
: m_first(nullptr), m_last(nullptr), m_mem_root(mem_root)
{}
bool handle_condition(THD *thd,
uint sql_errno,
const char *sqlstate,
Sql_condition::enum_warning_level *level,
const char *msg,
Sql_condition **cond_hdl) override
{
/* Only capture errors, let warnings and notes pass through */
if (*level != Sql_condition::WARN_LEVEL_ERROR)
return false ;
Error_entry *entry= (Error_entry*) alloc_root(m_mem_root,
sizeof (Error_entry));
if (!entry)
return false ; // Can't store, let error propagate
entry->sql_errno= sql_errno;
strmake(entry->message, msg, sizeof (entry->message) - 1 );
entry->next= nullptr;
if (m_last)
m_last->next= entry;
else
m_first= entry;
m_last= entry;
return true ;
}
bool has_errors() const { return m_first != nullptr; }
void emit_errors()
{
for (Error_entry *e= m_first; e; e= e->next)
my_message_sql(e->sql_errno, e->message, MYF(0 ));
}
void clear()
{
m_first= m_last= nullptr;
}
};
/**
This class is an internal error handler implementation for
DROP TABLE statements . The thing is that there may be warnings during
execution of these statements , which should not be exposed to the user .
This class is intended to silence such warnings .
*/
class Drop_table_error_handler : public Internal_error_handler
{
public :
Drop_table_error_handler() = default ;
public :
bool handle_condition(THD *thd,
uint sql_errno,
const char * sqlstate,
Sql_condition::enum_warning_level *level,
const char * msg,
Sql_condition ** cond_hdl) override;
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) override;
bool need_reopen() const { return m_need_reopen; };
void init() { m_need_reopen= FALSE ; };
private :
bool m_need_reopen;
};
class Turn_errors_to_warnings_handler : public Internal_error_handler
{
public :
Turn_errors_to_warnings_handler() = default ;
bool handle_condition(THD *,
uint,
const char *,
Sql_condition::enum_warning_level *level,
const char *,
Sql_condition ** cond_hdl) override
{
*cond_hdl= NULL;
if (*level == Sql_condition::WARN_LEVEL_ERROR)
*level= Sql_condition::WARN_LEVEL_WARN;
return (0 );
}
};
struct Suppress_warnings_error_handler : public Internal_error_handler
{
bool handle_condition(THD *,
uint,
const char *,
Sql_condition::enum_warning_level *level,
const char *,
Sql_condition **) override
{
return *level == Sql_condition::WARN_LEVEL_WARN;
}
};
/**
Tables that were locked with LOCK TABLES statement .
Encapsulates a list of TABLE_LIST instances for tables
locked by LOCK TABLES statement , memory root for metadata locks ,
and , generally , the context of LOCK TABLES statement .
In LOCK TABLES mode , the locked tables are kept open between
statements .
Therefore , we can ' t allocate metadata locks on execution memory
root - - as well as tables , the locks need to stay around till
UNLOCK TABLES is called .
The locks are allocated in the memory root encapsulated in this
class .
Some SQL commands , like FLUSH TABLE or ALTER TABLE , demand that
the tables they operate on are closed , at least temporarily .
This class encapsulates a list of TABLE_LIST instances , one
for each base table from LOCK TABLES list ,
which helps conveniently close the TABLEs when it ' s necessary
and later reopen them .
Implemented in sql_base . cc
*/
class Locked_tables_list
{
public :
MEM_ROOT m_locked_tables_root;
private :
TABLE_LIST *m_locked_tables;
TABLE_LIST **m_locked_tables_last;
/** An auxiliary array used only in reopen_tables(). */
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 .
*/
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 ),
some_table_marked_for_reopen(0 )
{
init_sql_alloc(key_memory_locked_table_list, &m_locked_tables_root,
MEM_ROOT_BLOCK_SIZE, 0 , MYF(MY_THREAD_SPECIFIC));
}
int unlock_locked_tables(THD *thd);
int unlock_locked_table(THD *thd, MDL_ticket *mdl_ticket);
~Locked_tables_list()
{
reset();
}
void reset();
bool init_locked_tables(THD *thd);
TABLE_LIST *locked_tables() { return m_locked_tables; }
void unlink_from_list(THD *thd, TABLE_LIST *table_list,
bool remove_from_locked_tables);
void unlink_all_closed_tables(THD *thd,
MYSQL_LOCK *lock,
size_t reopen_count);
bool reopen_tables(THD *thd, bool need_reopen);
bool restore_lock(THD *thd, TABLE_LIST *dst_table_list, TABLE *table,
MYSQL_LOCK *lock);
void add_back_last_deleted_lock(TABLE_LIST *dst_table_list);
void mark_table_for_reopen(TABLE *table);
};
/**
Storage engine specific thread local data .
*/
struct Ha_data
{
/**
Storage engine specific thread local data .
Lifetime : one user connection .
*/
void *ha_ptr;
/**
0 : Life time : one statement within a transaction . If @ @ autocommit is
on , also represents the entire transaction .
@ sa trans_register_ha ( )
1 : Life time : one transaction within a connection .
If the storage engine does not participate in a transaction ,
this should not be used .
@ sa trans_register_ha ( )
*/
Ha_trx_info ha_info[2 ];
/**
NULL : engine is not bound to this thread
non - NULL : engine is bound to this thread , engine shutdown forbidden
*/
plugin_ref lock;
Ha_data() :ha_ptr(NULL) {}
void reset()
{
ha_ptr= nullptr;
for (auto &info : ha_info)
info.reset();
lock= nullptr;
}
};
/**
An instance of the global read lock in a connection .
Implemented in lock . cc .
*/
class Global_read_lock
{
public :
enum enum_grl_state
{
GRL_NONE,
GRL_ACQUIRED,
GRL_ACQUIRED_AND_BLOCKS_COMMIT
};
Global_read_lock()
: m_state(GRL_NONE),
m_mdl_global_read_lock(NULL)
{}
bool lock_global_read_lock(THD *thd);
void unlock_global_read_lock(THD *thd);
bool make_global_read_lock_block_commit(THD *thd);
bool is_acquired() const { return m_state != GRL_NONE; }
void set_explicit_lock_duration(THD *thd);
private :
enum_grl_state m_state;
/**
Global read lock is acquired in two steps :
1 . acquire MDL_BACKUP_FTWRL1 in BACKUP namespace to prohibit DDL and DML
2 . upgrade to MDL_BACKUP_FTWRL2 to prohibit commits
*/
MDL_ticket *m_mdl_global_read_lock;
};
/*
Class to facilitate the commit of one transactions waiting for the commit of
another transaction to complete first .
This is used during ( parallel ) replication , to allow different transactions
to be applied in parallel , but still commit in order .
The transaction that wants to wait for a prior commit must first register
to wait with register_wait_for_prior_commit ( waitee ) . Such registration
must be done holding the waitee - > LOCK_wait_commit , to prevent the other
THD from disappearing during the registration .
Then during commit , if a THD is registered to wait , it will call
wait_for_prior_commit ( ) as part of ha_commit_trans ( ) . If no wait is
registered , or if the waitee for has already completed commit , then
wait_for_prior_commit ( ) returns immediately .
And when a THD that may be waited for has completed commit ( more precisely
commit_ordered ( ) ) , then it must call wakeup_subsequent_commits ( ) to wake
up any waiters . Note that this must be done at a point that is guaranteed
to be later than any waiters registering themselves . It is safe to call
wakeup_subsequent_commits ( ) multiple times , as waiters are removed from
registration as part of the wakeup .
The reason for separate register and wait calls is that this allows to
register the wait early , at a point where the waited - for THD is known to
exist . And then the actual wait can be done much later , where the
waited - for THD may have been long gone . By registering early , the waitee
can signal before disappearing .
*/
struct wait_for_commit
{
/*
The LOCK_wait_commit protects the fields subsequent_commits_list and
wakeup_subsequent_commits_running ( for a waitee ) , and the pointer
waitee and associated COND_wait_commit ( for a waiter ) .
*/
mysql_mutex_t LOCK_wait_commit;
mysql_cond_t COND_wait_commit;
/* List of threads that did register_wait_for_prior_commit() on us. */
wait_for_commit *subsequent_commits_list;
/* Link field for entries in subsequent_commits_list. */
wait_for_commit *next_subsequent_commit;
/*
Our waitee , if we did register_wait_for_prior_commit ( ) , and were not
yet woken up . Else NULL .
When this is cleared for wakeup , the COND_wait_commit condition is
signalled .
This pointer is protected by LOCK_wait_commit . But there is also a " fast
path " where the waiter compares this to NULL without holding the lock .
Such read must be done with acquire semantics ( and all corresponding
writes done with release semantics ) . This ensures that a wakeup with error
is reliably detected as ( waitee = = NULL & & wakeup_error ! = 0 ) .
*/
std::atomic<wait_for_commit *> waitee;
/*
Generic pointer for use by the transaction coordinator to optimise the
waiting for improved group commit .
Currently used by binlog TC to signal that a waiter is ready to commit , so
that the waitee can grab it and group commit it directly . It is free to be
used by another transaction coordinator for similar purposes .
*/
void *opaque_pointer;
/* The wakeup error code from the waitee. 0 means no error. */
int wakeup_error;
/*
Flag set when wakeup_subsequent_commits_running ( ) is active , see comments
on that function for details .
*/
bool wakeup_subsequent_commits_running;
/*
This flag can be set when a commit starts , but has not completed yet .
It is used by binlog group commit to allow a waiting transaction T2 to
join the group commit of an earlier transaction T1 . When T1 has queued
itself for group commit , it will set the commit_started flag . Then when
T2 becomes ready to commit and needs to wait for T1 to commit first , T2
can queue itself before waiting , and thereby participate in the same
group commit as T1 .
*/
bool commit_started;
/*
Set to temporarily ignore calls to wakeup_subsequent_commits ( ) . The
caller must arrange that another wakeup_subsequent_commits ( ) gets called
later after wakeup_blocked has been set back to false .
This is used for parallel replication with temporary tables .
Temporary tables require strict single - threaded operation . The normal
optimization , of doing wakeup_subsequent_commits early and overlapping
part of the commit with the following transaction , is not safe . Thus
when temporary tables are replicated , wakeup is blocked until the
event group is fully done .
*/
bool wakeup_blocked;
void register_wait_for_prior_commit(wait_for_commit *waitee);
int wait_for_prior_commit(THD *thd, bool allow_kill=true )
{
/*
Quick inline check , to avoid function call and locking in the common case
where no wakeup is registered , or a registered wait was already signalled .
*/
if (waitee.load(std::memory_order_acquire))
return wait_for_prior_commit2(thd, allow_kill);
else
{
if (unlikely(wakeup_error))
prior_commit_error(thd);
return wakeup_error;
}
}
void wakeup_subsequent_commits(int wakeup_error_arg)
{
/*
Do the check inline , so only the wakeup case takes the cost of a function
call for every commmit .
Note that the check is done without locking . It is the responsibility of
the user of the wakeup facility to ensure that no waiters can register
themselves after the last call to wakeup_subsequent_commits ( ) .
This avoids having to take another lock for every commit , which would be
pointless anyway - even if we check under lock , there is nothing to
prevent a waiter from arriving just after releasing the lock .
*/
if (subsequent_commits_list)
wakeup_subsequent_commits2(wakeup_error_arg);
}
void unregister_wait_for_prior_commit()
{
if (waitee.load(std::memory_order_relaxed))
unregister_wait_for_prior_commit2();
else
wakeup_error= 0 ;
}
/*
Remove a waiter from the list in the waitee . Used to unregister a wait .
The caller must be holding the locks of both waiter and waitee .
*/
void remove_from_list(wait_for_commit **next_ptr_ptr)
{
wait_for_commit *cur;
while ((cur= *next_ptr_ptr) != NULL)
{
if (cur == this )
{
*next_ptr_ptr= this ->next_subsequent_commit;
break ;
}
next_ptr_ptr= &cur->next_subsequent_commit;
}
waitee.store(NULL, std::memory_order_relaxed);
}
void wakeup(int wakeup_error);
int wait_for_prior_commit2(THD *thd, bool allow_kill);
void prior_commit_error(THD *thd);
void wakeup_subsequent_commits2(int wakeup_error);
void unregister_wait_for_prior_commit2();
wait_for_commit();
~wait_for_commit();
void reinit();
};
class Sp_caches
{
protected :
ulong m_sp_cache_version;
public :
sp_cache *sp_proc_cache;
sp_cache *sp_func_cache;
sp_cache *sp_package_spec_cache;
sp_cache *sp_package_body_cache;
Sp_caches()
:m_sp_cache_version(0 ), sp_proc_cache(NULL), sp_func_cache(NULL),
sp_package_spec_cache(NULL), sp_package_body_cache(NULL)
{ }
~Sp_caches()
{
// All caches must be freed by the caller explicitly
DBUG_ASSERT(sp_proc_cache == NULL);
DBUG_ASSERT(sp_func_cache == NULL);
DBUG_ASSERT(sp_package_spec_cache == NULL);
DBUG_ASSERT(sp_package_body_cache == NULL);
}
void sp_caches_clear();
/**
Clear content of sp related caches .
Don ' t delete cache objects itself .
*/
void sp_caches_empty();
ulong sp_cache_version() const
{
DBUG_ASSERT(m_sp_cache_version);
return m_sp_cache_version;
}
void set_sp_cache_version_if_needed(ulong version)
{
if (!m_sp_cache_version)
m_sp_cache_version= version;
}
};
extern "C" void my_message_sql(uint error, const char *str, myf MyFlags);
class Gap_time_tracker;
/*
Thread context for Gap_time_tracker class .
*/
class Gap_time_tracker_data
{
public :
Gap_time_tracker_data(): bill_to(NULL) {}
Gap_time_tracker *bill_to;
ulonglong start_time;
void init() { bill_to = NULL; }
};
/**
Support structure for asynchronous group commit , or more generally
any asynchronous operation that needs to finish before server writes
response to client .
An engine , or any other server component , can signal that there is
a pending operation by incrementing a counter , i . e inc_pending_ops ( )
and that pending operation is finished by decrementing that counter
dec_pending_ops ( ) .
NOTE : Currently , pending operations can not fail , i . e there is no
way to pass a return code in dec_pending_ops ( )
The server does not write response to the client before the counter
becomes 0 . In case of group commit it ensures that data is persistent
before success reported to client , i . e durability in ACID .
*/
struct thd_async_state
{
enum class enum_async_state
{
NONE,
SUSPENDED, /* do_command() did not finish, and needs to be resumed */
RESUMED /* do_command() is resumed*/
};
enum_async_state m_state{enum_async_state::NONE};
/* Stuff we need to resume do_command where we finished last time*/
enum enum_server_command m_command{COM_SLEEP};
LEX_STRING m_packet{0 ,0 };
mysql_mutex_t m_mtx;
mysql_cond_t m_cond;
/** Pending counter*/
Atomic_counter<int > m_pending_ops=0 ;
#ifndef DBUG_OFF
/* Checks */
pthread_t m_dbg_thread;
#endif
thd_async_state()
{
mysql_mutex_init(PSI_NOT_INSTRUMENTED, &m_mtx, 0 );
mysql_cond_init(PSI_INSTRUMENT_ME, &m_cond, 0 );
}
/*
Currently only used with threadpool , one can " suspend " and " resume " a THD .
Suspend only means leaving do_command earlier , after saving some state .
Resume is continuing suspended THD ' s do_command ( ) , from where it finished last time .
*/
bool try_suspend()
{
bool ret;
mysql_mutex_lock(&m_mtx);
DBUG_ASSERT(m_state == enum_async_state::NONE);
DBUG_ASSERT(m_pending_ops >= 0 );
if (m_pending_ops)
{
ret=true ;
m_state= enum_async_state::SUSPENDED;
}
else
{
/*
If there is no pending operations , can ' t suspend , since
nobody can resume it .
*/
ret=false ;
}
mysql_mutex_unlock(&m_mtx);
return ret;
}
~thd_async_state()
{
wait_for_pending_ops();
mysql_mutex_destroy(&m_mtx);
mysql_cond_destroy(&m_cond);
}
/*
Increment pending asynchronous operations .
The client response may not be written if
this count > 0 .
So , without threadpool query needs to wait for
the operations to finish .
With threadpool , THD can be suspended and resumed
when this counter goes to 0 .
*/
void inc_pending_ops()
{
mysql_mutex_lock(&m_mtx);
#ifndef DBUG_OFF
/*
Check that increments are always done by the same thread .
*/
if (!m_pending_ops)
m_dbg_thread= pthread_self();
else
DBUG_ASSERT(pthread_equal(pthread_self(),m_dbg_thread));
#endif
m_pending_ops++;
mysql_mutex_unlock(&m_mtx);
}
int dec_pending_ops(enum_async_state* state)
{
int ret;
mysql_mutex_lock(&m_mtx);
ret= --m_pending_ops;
if (!ret)
mysql_cond_signal(&m_cond);
*state = m_state;
mysql_mutex_unlock(&m_mtx);
return ret;
}
/*
This is used for " dirty " reading pending ops ,
when dirty read is OK .
*/
int pending_ops()
{
return m_pending_ops;
}
/* Wait for pending operations to finish.*/
void wait_for_pending_ops()
{
/*
It is fine to read m_pending_ops and compare it with 0 ,
without mutex protection .
The value is only incremented by the current thread , and will
be decremented by another one , thus " dirty " may show positive number
when it is really 0 , but this is not a problem , and the only
bad thing from that will be rechecking under mutex .
*/
if (!pending_ops())
return ;
mysql_mutex_lock(&m_mtx);
DBUG_ASSERT(m_pending_ops >= 0 );
while (m_pending_ops)
mysql_cond_wait(&m_cond, &m_mtx);
mysql_mutex_unlock(&m_mtx);
}
};
enum class THD_WHERE
{
NOWHERE = 0 ,
CHECKING_TRANSFORMED_SUBQUERY,
IN_ALL_ANY_SUBQUERY,
JSON_TABLE_ARGUMENT,
FIELD_LIST,
PARTITION_FUNCTION,
FROM_CLAUSE,
DEFAULT_WHERE,
ON_CLAUSE,
WHERE_CLAUSE,
SET_LIST,
INSERT_LIST,
VALUES_CLAUSE,
UPDATE_CLAUSE,
RETURNING,
FOR_SYSTEM_TIME,
ORDER_CLAUSE,
HAVING_CLAUSE,
GROUP_STATEMENT,
PROCEDURE_LIST,
CHECK_OPTION,
DO_STATEMENT,
HANDLER_STATEMENT,
USE_WHERE_STRING, // ugh, a compromise for vcol...
};
const char *thd_where(THD *thd);
/**
@ class THD
For each client connection we create a separate thread with THD serving as
a thread / connection descriptor
*/
class THD: public THD_count, /* this must be first */
public Sql_used,
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 Open_tables_state,
public Sp_caches,
public Statement_rcontext
{
private :
inline bool is_stmt_prepare() const
{ DBUG_ASSERT(0 ); return Statement::is_stmt_prepare(); }
inline bool is_stmt_prepare_or_first_sp_execute() const
{ DBUG_ASSERT(0 ); return Statement::is_stmt_prepare_or_first_sp_execute(); }
inline bool is_stmt_prepare_or_first_stmt_execute() const
{ DBUG_ASSERT(0 ); return Statement::is_stmt_prepare_or_first_stmt_execute(); }
inline bool is_conventional() const
{ DBUG_ASSERT(0 ); return Statement::is_conventional(); }
public :
bool is_setting_returning;
MDL_context mdl_context;
/* Used to execute base64 coded binlog events in MySQL server */
Relay_log_info* rli_fake;
rpl_group_info* rgi_fake;
/* Slave applier execution context */
rpl_group_info* rgi_slave;
union {
rpl_io_thread_info *rpl_io_info;
rpl_sql_thread_info *rpl_sql_info;
} system_thread_info;
/* Used for BACKUP LOCK */
MDL_ticket *mdl_backup_ticket, *mdl_backup_lock;
/* Used to register that thread has a MDL_BACKUP_WAIT_COMMIT lock */
MDL_ticket *backup_commit_lock;
void reset_for_next_command(bool do_clear_errors= 1 );
#ifdef EMBEDDED_LIBRARY
struct st_mysql *mysql;
unsigned long client_stmt_id;
unsigned long client_param_count;
struct st_mysql_bind *client_params;
char *extra_data;
ulong extra_length;
struct st_mysql_data *cur_data;
struct st_mysql_data *first_data;
struct st_mysql_data **data_tail;
void clear_data_list();
struct st_mysql_data *alloc_new_dataset();
/*
In embedded server it points to the statement that is processed
in the current query . We store some results directly in statement
fields then .
*/
struct st_mysql_stmt *current_stmt;
#endif
Query_cache_tls query_cache_tls;
NET net; // client connection descriptor
/** Additional network instrumentation for the server only. */
NET_SERVER m_net_server_extension;
scheduler_functions *scheduler; // Scheduler for this connection
Protocol *protocol; // Current protocol
Protocol_text protocol_text; // Normal protocol
Protocol_binary protocol_binary; // Binary protocol
HASH user_vars; // hash for user variables
String packet; // dynamic buffer for network I/O
String convert_buffer; // buffer for charset conversions
struct my_rnd_struct rand; // used for authentication
struct system_variables variables; // Changeable local variables
struct system_status_var status_var; // Per thread statistic vars
struct system_status_var org_status_var; // For user statistics
struct system_status_var *initial_status_var; /* used by show status */
ha_handler_stats handler_stats; // Handler statistics
THR_LOCK_INFO lock_info; // Locking info of this thread
/**
Protects THD data accessed from other threads :
- thd - > query and thd - > query_length ( used by SHOW ENGINE
INNODB STATUS and SHOW PROCESSLIST
- thd - > db ( used in SHOW PROCESSLIST )
Is locked when THD is deleted .
*/
mutable mysql_mutex_t LOCK_thd_data;
/*
Protects :
- kill information
- mysys_var ( used by KILL statement and shutdown ) .
- Also ensures that THD is not deleted while mutex is hold
*/
mutable mysql_mutex_t LOCK_thd_kill;
/* all prepared statements and cursors of this connection */
Statement_map stmt_map;
/* Last created prepared statement */
Statement *last_stmt;
Statement *cur_stmt= 0 ;
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
*/
void *thread_stack;
/**
Currently selected catalog .
*/
char *catalog;
/**
@ note
Some members of THD ( currently ' Statement : : db ' ,
' catalog ' and ' query ' ) are set and alloced by the slave SQL thread
( for the THD of that thread ) ; that thread is ( and must remain , for now )
the only responsible for freeing these 3 members . If you add members
here , and you add code to set them in replication , don ' t forget to
free_them_and_set_them_to_0 in replication properly . For details see
the ' err : ' label of the handle_slave_sql ( ) in sql / slave . cc .
@ see handle_slave_sql
*/
Security_context main_security_ctx;
Security_context *security_ctx;
Security_context *security_context() const { return security_ctx; }
void set_security_context(Security_context *sctx) { security_ctx = sctx; }
/*
Points to info - string that we show in SHOW PROCESSLIST
You are supposed to update thd - > proc_info only if you have coded
a time - consuming piece that MySQL can get stuck in for a long time .
Set it using the thd_proc_info ( THD * thread , const char * message )
macro / function .
This member is accessed and assigned without any synchronization .
Therefore , it may point only to constant ( statically
allocated ) strings , which memory won ' t go away over time .
*/
const char *proc_info;
void set_psi(PSI_thread *psi)
{
my_atomic_storeptr((void *volatile *)&m_psi, psi);
}
PSI_thread* get_psi()
{
return static_cast <PSI_thread*>(my_atomic_loadptr((void *volatile *)&m_psi));
}
private :
unsigned int m_current_stage_key;
/** Performance schema thread instrumentation for this session. */
PSI_thread *m_psi;
public :
void enter_stage(const PSI_stage_info *stage,
const char *calling_func,
const char *calling_file,
const unsigned int calling_line)
{
DBUG_PRINT("THD::enter_stage" , ("%s at %s:%d" , stage->m_name,
calling_file, calling_line));
DBUG_ASSERT(stage);
m_current_stage_key= stage->m_key;
proc_info= stage->m_name;
#if defined (ENABLED_PROFILING)
profiling.status_change(proc_info, calling_func, calling_file,
calling_line);
#endif
#ifdef HAVE_PSI_THREAD_INTERFACE
m_stage_progress_psi= MYSQL_SET_STAGE(m_current_stage_key, calling_file, calling_line);
#endif
}
void backup_stage(PSI_stage_info *stage)
{
stage->m_key= m_current_stage_key;
stage->m_name= proc_info;
}
const char *get_proc_info() const
{ return proc_info; }
// Used by thd_where() when where==USE_WHERE_STRING
const char *where_str;
/*
Used in error messages to tell user in what part of MySQL we found an
error . E . g . when where = " having clause " , if fix_fields ( ) fails , user
will know that the error was in having clause .
*/
THD_WHERE where;
/* Needed by MariaDB semi sync replication */
Trans_binlog_info *semisync_info;
#ifndef DBUG_OFF
/*
If Active_tranx is missing an entry for a transaction which is planning to
await an ACK , this ensures that the reason is because semi - sync was turned
off then on in - between the binlogging of the transaction , and before it had
started waiting for the ACK .
*/
ulong expected_semi_sync_offs;
#endif
/* If this is a semisync slave connection. */
bool semi_sync_slave;
/* Several threads may share this thd. Used with parallel repair */
bool shared_thd;
/*
Mark if query was logged as statement or to mark that there was no
changes in the table . Used to check if tmp table changes are
properly logged .
*/
bool tmp_table_binlog_handled;
ulonglong client_capabilities; /* What the client supports */
ulong max_client_packet_length;
HASH handler_tables_hash;
/*
A thread can hold named user - level locks . This variable
contains granted tickets if a lock is present . See item_func . cc and
chapter ' Miscellaneous functions ' , for functions GET_LOCK , RELEASE_LOCK .
*/
HASH ull_hash;
/* Hash of used sequences (for PREVIOUS value) */
HASH sequences;
#ifdef DBUG_ASSERT_EXISTS
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
first byte of the packet in do_command ( )
*/
enum enum_server_command m_command;
public :
uint32 file_id; // for LOAD DATA INFILE
/* remote (peer) port */
uint16 peer_port;
my_time_t start_time; // start_time and its sec_part
ulong start_time_sec_part; // are almost always used separately
my_hrtime_t user_time;
// track down slow pthread_create
ulonglong prior_thr_create_utime, thr_create_utime;
ulonglong start_utime, utime_after_lock, utime_after_query;
/* This can be used by handlers to send signals to the SQL level */
ulonglong replication_flags;
// Process indicator
struct {
/*
true , if the currently running command can send progress report
packets to a client . Set by mysql_execute_command ( ) for safe commands
See CF_REPORT_PROGRESS
*/
bool report_to_client;
/*
true , if we will send progress report packets to a client
( client has requested them , see MARIADB_CLIENT_PROGRESS ; report_to_client
is true ; not in sub - statement )
*/
bool report;
uint stage, max_stage;
ulonglong counter, max_counter;
ulonglong next_report_time;
Query_arena *arena;
} progress;
thr_lock_type update_lock_default;
Delayed_insert *di;
/* <> 0 if we are inside of trigger or stored function. */
uint in_sub_stmt;
/*
Check if THD : : in_sub_stmt indicates that we ' re in a " real " substatement .
A PS - safe function call is not considered as a real substatement - it
behaves more like a procedure call in terms of table locking and
transaction handling .
- Case 1 a : in_sub_stmt = = 0
The current evaluation is at the top level .
We ' re not in a substatement . Return false .
- Case 1 b : in_sub_stmt = = 0
The current evaluation is inside a stored procedure
and there are no stored functions or triggers in the call stack .
I . e . the call stack consists only of stored procedure calls .
We ' re not in a substatement . Return false .
- Case 2 : in_sub_stmt = = ( SUB_STMT_FUNCTION | SUB_STMT_PS_SAFE_CONTEXT )
The current evaluation is inside a stored function f1 ( ) and the function
f1 ( ) is used in a safe context ( e . g . in an assignment right hand ) .
We ' re not in a " real " substatement . Return false .
- Case 3 : in_sub_stmt is something else
We ' re in a " real " substatement . Return true .
*/
bool in_sub_stmt_ps_unsafe() const
{
return in_sub_stmt &&
(in_sub_stmt != (SUB_STMT_FUNCTION | SUB_STMT_PS_SAFE_CONTEXT));
}
/*
Reject a dynamic SQL statement ( PREPARE , EXECUTE , DEALLOCATE PREPARE ,
EXECUTE IMMEDIATE ) which is executed in a " real " substatement , i . e . in
a trigger , or in a stored function called outside of a safe PS context .
See in_sub_stmt_ps_unsafe ( ) above .
Note , the error message argument is hardcoded to " Dynamic SQL " , so this
is not a general purpose substatement test . Call it only from the
dynamic SQL statements ; use in_sub_stmt_ps_unsafe ( ) elsewhere .
@ retval true the statement was rejected , an error was raised
@ retval false the statement can continue
*/
bool error_if_in_sub_stmt_ps_unsafe() const
{
if (!in_sub_stmt_ps_unsafe())
return false ;
raise_error_dynamic_sql_not_allowed();
return true ;
}
/*
Check if the current statement is in the middle of something that a
nested prepared statement would destroy : a non - empty LEX : : query_tables
( the whole list , not only the own tables ) , whose tables
Prepared_statement : : prepare ( ) would close , or a statement transaction ,
which it assumes to be empty .
*/
bool stmt_state_conflicts_with_ps() const ;
/* True when opt_userstat_running is set at start of query */
bool userstat_running;
/*
True if we have to log all errors . Are set by some engines to temporary
force errors to the error log .
*/
bool log_all_errors;
/* Do not set socket timeouts for wait_timeout (used with threadpool) */
bool skip_wait_timeout;
bool prepare_derived_at_open;
/* Set to 1 if status of this THD is already in global status */
bool status_in_global;
/*
To signal that the tmp table to be created is created for materialized
derived table or a view .
*/
bool create_tmp_table_for_derived;
bool save_prep_leaf_list;
/**
The data member reset_sp_cache is to signal that content of sp_cache
must be reset ( all items be removed from it ) .
*/
bool reset_sp_cache;
bool killed_for_exceeding_limit_rows_warning_given;
/* container for handler's private per-connection data */
Ha_data ha_data[MAX_HA];
/**
Bit field for the state of binlog warnings .
The first Lex : : BINLOG_STMT_UNSAFE_COUNT bits list all types of
unsafeness that the current statement has .
This must be a member of THD and not of LEX , because warnings are
detected and issued in different places ( @ c
decide_logging_format ( ) and @ c binlog_query ( ) , respectively ) .
Between these calls , the THD - > lex object may change ; e . g . , if a
stored routine is invoked . Only THD persists between the calls .
*/
uint32 binlog_unsafe_warning_flags;
#ifndef MYSQL_CLIENT
binlog_cache_mngr * binlog_setup_trx_data();
/*
If set , tell binlog to store the value as query ' xid ' in the next
Query_log_event
*/
ulonglong binlog_xid;
/*
Public interface to write RBR events to the binlog
*/
void binlog_start_trans_and_stmt();
void binlog_set_stmt_begin();
int binlog_write_row(TABLE* table, Event_log *bin_log,
binlog_cache_data *cache_data, bool is_transactional,
const uchar *buf);
int binlog_delete_row(TABLE* table, Event_log *bin_log,
binlog_cache_data *cache_data, bool is_transactional,
enum_binlog_row_image row_image, const uchar *buf);
int binlog_update_row(TABLE* table, Event_log *bin_log,
binlog_cache_data *cache_data, bool is_transactional,
enum_binlog_row_image row_image,
const uchar *old_data, const uchar *new_data);
bool prepare_handlers_for_update(uint flag);
bool binlog_write_annotated_row(bool use_trans_cache);
void binlog_prepare_for_row_logging();
bool binlog_write_table_maps();
void set_server_id(uint32 sid) { variables.server_id = sid; }
/*
Member functions to handle pending event for row - level logging .
*/
binlog_cache_mngr *binlog_get_cache_mngr() const ;
inline int binlog_flush_pending_rows_event(bool stmt_end)
{
return (binlog_flush_pending_rows_event(stmt_end, FALSE ) ||
binlog_flush_pending_rows_event(stmt_end, TRUE ));
}
int binlog_flush_pending_rows_event(bool stmt_end, bool is_transactional);
uint has_pending_row_events();
bool binlog_need_stmt_format(bool is_transactional) const
{
if (!log_current_statement())
return false ;
binlog_cache_mngr *cache_mngr= binlog_get_cache_mngr();
if (!cache_mngr)
return true ;
return !binlog_get_pending_rows_event(cache_mngr,
use_trans_cache(this ,
is_transactional));
}
bool binlog_for_noop_dml(bool transactional_table);
void binlog_truncate_tmp_files()
{
binlog_cache_mngr *cache_mngr= binlog_get_cache_mngr();
if (cache_mngr)
::binlog_truncate_tmp_files(cache_mngr);
}
/**
Determine the binlog format of the current statement .
@ retval 0 if the current statement will be logged in statement
format .
@ retval nonzero if the current statement will be logged in row
format .
*/
int is_current_stmt_binlog_format_row() const {
DBUG_ASSERT(current_stmt_binlog_format == BINLOG_FORMAT_STMT ||
current_stmt_binlog_format == BINLOG_FORMAT_ROW);
return current_stmt_binlog_format == BINLOG_FORMAT_ROW;
}
int is_binlog_format_row() const
{
return variables.binlog_format == BINLOG_FORMAT_ROW;
}
/*
Should we binlog a CREATE TEMPORARY statement .
This should happen only if all of the following is true
- binlog format is either BINLOG_FORMAT_STMT or BINLOG_FORMAT_MIXED
and the corresponding bit is set in binlog_format_for_create_temporary .
- The server is not in readonly mode or this is a slave thread .
- Slave threads are not affected by readonly in this case .
Note that CREATE TEMPORARY is always logged in STATEMENT from as
temporary tables does not support ROW logging .
@ result 1 CREATE should be logged
@ result 0 CREATE should not be logged
*/
bool binlog_create_tmp_table()
{
return (((1 ULL << variables.binlog_format) &
variables.create_temporary_table_binlog_formats) &&
(!opt_readonly || slave_thread));
}
/**
Determine if binlogging is disabled for this session
@ retval 0 if the current statement binlogging is disabled
( could be because of binlog closed / binlog option
is set to false ) .
@ retval 1 if the current statement will be binlogged
*/
inline bool is_current_stmt_binlog_disabled() const
{
return (!(variables.option_bits & OPTION_BIN_LOG) ||
!mysql_bin_log.is_open());
}
enum binlog_filter_state
{
BINLOG_FILTER_UNKNOWN,
BINLOG_FILTER_CLEAR,
BINLOG_FILTER_SET
};
inline void reset_binlog_local_stmt_filter()
{
m_binlog_filter_state= BINLOG_FILTER_UNKNOWN;
}
inline void clear_binlog_local_stmt_filter()
{
DBUG_ASSERT(m_binlog_filter_state == BINLOG_FILTER_UNKNOWN);
m_binlog_filter_state= BINLOG_FILTER_CLEAR;
}
inline void set_binlog_local_stmt_filter()
{
DBUG_ASSERT(m_binlog_filter_state == BINLOG_FILTER_UNKNOWN);
m_binlog_filter_state= BINLOG_FILTER_SET;
}
inline binlog_filter_state get_binlog_local_stmt_filter()
{
return m_binlog_filter_state;
}
bool binlog_renamed_tmp_tables(TABLE_LIST *table_list);
/**
Checks if a user connection is read - only
*/
inline bool check_read_only_with_error()
{
if (likely(!opt_readonly) || slave_thread ||
((security_ctx->master_access & PRIV_IGNORE_READ_ONLY) &&
opt_readonly != READONLY_NO_LOCK_NO_ADMIN))
return false ;
mariadb_error_read_only();
return true ;
}
private :
/**
Indicate if the current statement should be discarded
instead of written to the binlog .
This is used to discard special statements , such as
DML or DDL that affects only ' local ' ( non replicated )
tables , such as performance_schema . *
*/
binlog_filter_state m_binlog_filter_state;
/**
Indicates the format in which the current statement will be
logged . This can only be set from @ c decide_logging_format ( ) .
*/
enum_binlog_format current_stmt_binlog_format;
public :
/* 1 if binlog table maps has been written */
bool binlog_table_maps;
void issue_unsafe_warnings();
void reset_unsafe_warnings()
{ binlog_unsafe_warning_flags= 0 ; }
void reset_binlog_for_next_statement()
{
binlog_table_maps= 0 ;
}
bool binlog_table_should_be_logged(const LEX_CSTRING *db);
// Accessors and setters of two-phase loggable ALTER binlog properties
uchar get_binlog_flags_for_alter();
void set_binlog_flags_for_alter(uchar);
uint64 get_binlog_start_alter_seq_no();
void set_binlog_start_alter_seq_no(uint64);
#endif /* MYSQL_CLIENT */
public :
struct st_transactions {
SAVEPOINT *savepoints;
THD_TRANS all; // Trans since BEGIN WORK
THD_TRANS stmt; // Trans for current statement
bool on; // see ha_enable_transaction()
XID_STATE xid_state;
XID implicit_xid;
WT_THD wt; ///< for deadlock detection
Rows_log_event *m_pending_rows_event;
struct st_trans_time : public my_timeval
{
void reset(THD *thd)
{
tv_sec= thd->query_start();
/*
The type of tv_usec depends on the system and on macOS
it is _ _ darwin_suseconds_t . Using decltype is system
agnostic because its result is whatever the underlying
type of tv_usec is . This should be more portable than
assuming that the left hand side is ( long ) ( the previous
cast value ) .
*/
tv_usec= static_cast <decltype(tv_usec)>(thd->query_start_sec_part());
}
} start_time;
/*
Tables changed in transaction ( that must be invalidated in query cache ) .
List contain only transactional tables , that not invalidated in query
cache ( instead of full list of changed in transaction tables ) .
*/
CHANGED_TABLE_LIST* changed_tables;
MEM_ROOT mem_root; // Transaction-life memory allocation pool
void cleanup()
{
DBUG_ENTER("THD::st_transactions::cleanup" );
changed_tables= 0 ;
savepoints= 0 ;
implicit_xid.null();
free_root(&mem_root,MYF(MY_KEEP_PREALLOC));
DBUG_VOID_RETURN;
}
void free()
{
free_root(&mem_root,MYF(0 ));
}
bool is_active()
{
return (all.ha_list != NULL);
}
bool is_empty()
{
return all.is_empty() && stmt.is_empty();
}
st_transactions()
{
bzero((char *)this , sizeof (*this ));
implicit_xid.null();
init_sql_alloc(key_memory_thd_transactions, &mem_root,
DEFAULT_ROOT_BLOCK_SIZE, 0 , MYF(MY_THREAD_SPECIFIC));
}
} default_transaction, *transaction;
Global_read_lock global_read_lock;
Field *dup_field;
#ifndef _WIN32
sigset_t signals;
#endif
#ifdef SIGNAL_WITH_VIO_CLOSE
Vio* active_vio;
#endif
/*
A permanent memory area of the statement . For conventional
execution , the parsed tree and execution runtime reside in the same
memory root . In this case stmt_arena points to THD . In case of
a prepared statement or a stored procedure statement , thd - > mem_root
conventionally points to runtime memory , and thd - > stmt_arena
points to the memory of the PS / SP , where the parsed tree of the
statement resides . Whenever you need to perform a permanent
transformation of a parsed tree , you should allocate new memory in
stmt_arena , to allow correct re - execution of PS / SP .
Note : in the parser , stmt_arena = = thd , even for PS / SP .
*/
Query_arena *stmt_arena;
/**
Get either call or statement arena . In case some function is called from
within a query the call arena has to be used for a memory allocation ,
else use the statement arena .
*/
Query_arena *active_stmt_arena_to_use()
{
return (state == Query_arena::STMT_SP_QUERY_ARGUMENTS) ? this :
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 .
*/
table_map table_map_for_update;
/* Tells if LAST_INSERT_ID(#) was called for the current statement */
bool arg_of_last_insert_id_function;
/*
ALL OVER THIS FILE , " insert_id " means " * automatically generated * value for
insertion into an auto_increment column " .
*/
/*
This is the first autogenerated insert id which was * successfully *
inserted by the previous statement ( exactly , if the previous statement
didn ' t successfully insert an autogenerated insert id , then it ' s the one
of the statement before , etc ) .
It can also be set by SET LAST_INSERT_ID = # or SELECT LAST_INSERT_ID ( # ) .
It is returned by LAST_INSERT_ID ( ) .
*/
ulonglong first_successful_insert_id_in_prev_stmt;
/*
Variant of the above , used for storing in statement - based binlog . The
difference is that the one above can change as the execution of a stored
function progresses , while the one below is set once and then does not
change ( which is the value which statement - based binlog needs ) .
*/
ulonglong first_successful_insert_id_in_prev_stmt_for_binlog;
/*
This is the first autogenerated insert id which was * successfully *
inserted by the current statement . It is maintained only to set
first_successful_insert_id_in_prev_stmt when statement ends .
*/
ulonglong first_successful_insert_id_in_cur_stmt;
/*
We follow this logic :
- when stmt starts , first_successful_insert_id_in_prev_stmt contains the
first insert id successfully inserted by the previous stmt .
- as stmt makes progress , handler : : insert_id_for_cur_row changes ;
every time get_auto_increment ( ) is called ,
auto_inc_intervals_in_cur_stmt_for_binlog is augmented with the
reserved interval ( if statement - based binlogging ) .
- at first successful insertion of an autogenerated value ,
first_successful_insert_id_in_cur_stmt is set to
handler : : insert_id_for_cur_row .
- when stmt goes to binlog ,
auto_inc_intervals_in_cur_stmt_for_binlog is binlogged if
non - empty .
- when stmt ends , first_successful_insert_id_in_prev_stmt is set to
first_successful_insert_id_in_cur_stmt .
*/
/*
stmt_depends_on_first_successful_insert_id_in_prev_stmt is set when
LAST_INSERT_ID ( ) is used by a statement .
If it is set , first_successful_insert_id_in_prev_stmt_for_binlog will be
stored in the statement - based binlog .
This variable is CUMULATIVE along the execution of a stored function or
trigger : if one substatement sets it to 1 it will stay 1 until the
function / trigger ends , thus making sure that
first_successful_insert_id_in_prev_stmt_for_binlog does not change anymore
and is propagated to the caller for binlogging .
*/
bool stmt_depends_on_first_successful_insert_id_in_prev_stmt;
/*
List of auto_increment intervals reserved by the thread so far , for
storage in the statement - based binlog .
Note that its minimum is not first_successful_insert_id_in_cur_stmt :
assuming a table with an autoinc column , and this happens :
INSERT INTO . . . VALUES ( 3 ) ;
SET INSERT_ID = 3 ; INSERT IGNORE . . . VALUES ( NULL ) ;
then the latter INSERT will insert no rows
( first_successful_insert_id_in_cur_stmt = = 0 ) , but storing " INSERT_ID = 3 "
in the binlog is still needed ; the list ' s minimum will contain 3 .
This variable is cumulative : if several statements are written to binlog
as one ( stored functions or triggers are used ) this list is the
concatenation of all intervals reserved by all statements .
*/
Discrete_intervals_list auto_inc_intervals_in_cur_stmt_for_binlog;
/* Used by replication and SET INSERT_ID */
Discrete_intervals_list auto_inc_intervals_forced;
/*
There is BUG # 19630 where statement - based replication of stored
functions / triggers with two auto_increment columns breaks .
We however ensure that it works when there is 0 or 1 auto_increment
column ; our rules are
a ) on master , while executing a top statement involving substatements ,
first top - or sub - statement to generate auto_increment values wins the
exclusive right to see its values be written to binlog ( the write
will be done by the statement or its caller ) , and the losers won ' t see
their values be written to binlog .
b ) on slave , while replicating a top statement involving substatements ,
first top - or sub - statement to need to read auto_increment values from
the master ' s binlog wins the exclusive right to read them ( so the losers
won ' t read their values from binlog but instead generate on their own ) .
a ) implies that we mustn ' t backup / restore
auto_inc_intervals_in_cur_stmt_for_binlog .
b ) implies that we mustn ' t backup / restore auto_inc_intervals_forced .
If there are more than 1 auto_increment columns , then intervals for
different columns may mix into the
auto_inc_intervals_in_cur_stmt_for_binlog list , which is logically wrong ,
but there is no point in preventing this mixing by preventing intervals
from the secondly inserted column to come into the list , as such
prevention would be wrong too .
What will happen in the case of
INSERT INTO t1 ( auto_inc ) VALUES ( NULL ) ;
where t1 has a trigger which inserts into an auto_inc column of t2 , is
that in binlog we ' ll store the interval of t1 and the interval of t2 ( when
we store intervals , soon ) , then in slave , t1 will use both intervals , t2
will use none ; if t1 inserts the same number of rows as on master ,
normally the 2 nd interval will not be used by t1 , which is fine . t2 ' s
values will be wrong if t2 ' s internal auto_increment counter is different
from what it was on master ( which is likely ) . In 5 . 1 , in mixed binlogging
mode , row - based binlogging is used for such cases where two
auto_increment columns are inserted .
*/
inline void record_first_successful_insert_id_in_cur_stmt(ulonglong id_arg)
{
if (first_successful_insert_id_in_cur_stmt == 0 )
first_successful_insert_id_in_cur_stmt= id_arg;
}
inline ulonglong read_first_successful_insert_id_in_prev_stmt(void )
{
if (!stmt_depends_on_first_successful_insert_id_in_prev_stmt)
{
/* It's the first time we read it */
first_successful_insert_id_in_prev_stmt_for_binlog=
first_successful_insert_id_in_prev_stmt;
stmt_depends_on_first_successful_insert_id_in_prev_stmt= 1 ;
}
return first_successful_insert_id_in_prev_stmt;
}
/*
Used by Intvar_log_event : : do_apply_event ( ) and by " SET INSERT_ID = # "
( mysqlbinlog ) . We ' ll soon add a variant which can take many intervals in
argument .
*/
inline void force_one_auto_inc_interval(ulonglong next_id)
{
auto_inc_intervals_forced.empty(); // in case of multiple SET INSERT_ID
auto_inc_intervals_forced.append(next_id, ULONGLONG_MAX, 0 );
}
inline void set_binlog_bit()
{
if (variables.sql_log_bin)
variables.option_bits |= OPTION_BIN_LOG;
else
variables.option_bits &= ~OPTION_BIN_LOG;
}
ulonglong limit_found_rows;
private :
/**
Stores the result of ROW_COUNT ( ) function .
ROW_COUNT ( ) function is a MySQL extention , but we try to keep it
similar to ROW_COUNT member of the GET DIAGNOSTICS stack of the SQL
standard ( see SQL99 , part 2 , search for ROW_COUNT ) . It ' s value is
implementation defined for anything except INSERT , DELETE , UPDATE .
ROW_COUNT is assigned according to the following rules :
- In my_ok ( ) :
- for DML statements : to the number of affected rows ;
- for DDL statements : to 0 .
- In my_eof ( ) : to - 1 to indicate that there was a result set .
We derive this semantics from the JDBC specification , where int
java . sql . Statement . getUpdateCount ( ) is defined to ( sic ) " return the
current result as an update count ; if the result is a ResultSet
object or there are no more results , - 1 is returned " .
- In my_error ( ) : to - 1 to be compatible with the MySQL C API and
MySQL ODBC driver .
- For SIGNAL statements : to 0 per WL # 2110 specification ( see also
sql_signal . cc comment ) . Zero is used since that ' s the " default "
value of ROW_COUNT in the diagnostics area .
*/
longlong m_row_count_func; /* For the ROW_COUNT() function */
public :
inline longlong get_row_count_func() const
{
return m_row_count_func;
}
inline void set_row_count_func(longlong row_count_func)
{
m_row_count_func= row_count_func;
}
/*
Free all top level statement data ( e . g . belonging to SYS_REFCURSORs )
and reinit it for a new top level statement .
It ' s called in the very end of the top level statement
( it ' s not called for individual stored routune statements ) .
*/
void statement_rcontext_reinit()
{
Statement_rcontext::reinit(this );
with_flags_bit_or_for_complex_data_types= item_with_t::NONE;
}
inline void set_affected_rows(longlong row_count_func)
{
/*
We have to add to affected_rows ( used by slow log ) , as otherwise
information for ' call ' will be wrong
*/
affected_rows+= (row_count_func >= 0 ? row_count_func : 0 );
}
ha_rows cuted_fields;
/*
number of rows we actually sent to the client , including " synthetic "
rows in ROLLUP etc .
*/
ha_rows m_sent_row_count;
/* Number of rows for the total statement */
ha_rows sent_row_count_for_statement;
/**
Number of rows read and / or evaluated for a statement . Used for
slow log reporting .
An examined row is defined as a row that is read and / or evaluated
according to a statement condition , including in
create_sort_index ( ) . Rows may be counted more than once , e . g . , a
statement including ORDER BY could possibly evaluate the row in
filesort ( ) before reading it for e . g . update .
*/
ha_rows m_examined_row_count;
/* Number of rows for the top level query */
ha_rows examined_row_count_for_statement;
ha_rows get_sent_row_count() const
{ return m_sent_row_count; }
ha_rows get_examined_row_count() const
{
DBUG_EXECUTE_IF("debug_huge_number_of_examined_rows" ,
return (ULONGLONG_MAX - 1000000 ););
return m_examined_row_count;
}
ulonglong get_affected_rows() const
{ return affected_rows; }
void set_sent_row_count(ha_rows count);
inline void inc_sent_row_count(ha_rows count)
{
m_sent_row_count+= count;
sent_row_count_for_statement+= count;
MYSQL_SET_STATEMENT_ROWS_SENT(m_statement_psi, m_sent_row_count);
}
inline void inc_examined_row_count_fast(ha_rows count= 1 )
{
m_examined_row_count+= count;
examined_row_count_for_statement+= count;
}
inline void inc_examined_row_count(ha_rows count= 1 )
{
inc_examined_row_count_fast();
MYSQL_SET_STATEMENT_ROWS_EXAMINED(m_statement_psi, m_examined_row_count);
}
void ps_report_examined_row_count();
void inc_status_created_tmp_disk_tables();
void inc_status_created_tmp_files();
void inc_status_created_tmp_tables();
void inc_status_select_full_join();
void inc_status_select_full_range_join();
void inc_status_select_range();
void inc_status_select_range_check();
void inc_status_select_scan();
void inc_status_sort_merge_passes();
void inc_status_sort_range();
void inc_status_sort_rows(ha_rows count);
void inc_status_sort_scan();
void set_status_no_index_used();
void set_status_no_good_index_used();
/**
The number of rows and / or keys examined by the query , both read ,
changed or written .
*/
ulonglong accessed_rows_and_keys;
/**
Check if the number of rows accessed by a statement exceeded
LIMIT ROWS EXAMINED . If so , signal the query engine to stop execution .
*/
void killed_for_exceeding_limit_rows();
inline void check_limit_rows_examined()
{
if (++accessed_rows_and_keys > lex->limit_rows_examined_cnt)
killed_for_exceeding_limit_rows();
}
USER_CONN *user_connect;
CHARSET_INFO *db_charset;
#if defined (ENABLED_PROFILING)
PROFILING profiling;
#endif
/** Current stage progress instrumentation. */
PSI_stage_progress *m_stage_progress_psi;
/** Current statement digest. */
sql_digest_state *m_digest;
/** Current statement digest token array. */
unsigned char *m_token_array;
/** Top level statement digest. */
sql_digest_state m_digest_state;
/** Current statement instrumentation. */
PSI_statement_locker *m_statement_psi;
#ifdef HAVE_PSI_STATEMENT_INTERFACE
/** Current statement instrumentation state. */
PSI_statement_locker_state m_statement_state;
#endif /* HAVE_PSI_STATEMENT_INTERFACE */
/** Current transaction instrumentation. */
PSI_transaction_locker *m_transaction_psi;
#ifdef HAVE_PSI_TRANSACTION_INTERFACE
/** Current transaction instrumentation state. */
PSI_transaction_locker_state m_transaction_state;
#endif /* HAVE_PSI_TRANSACTION_INTERFACE */
/** Idle instrumentation. */
PSI_idle_locker *m_idle_psi;
#ifdef HAVE_PSI_IDLE_INTERFACE
/** Idle instrumentation state. */
PSI_idle_locker_state m_idle_state;
#endif /* HAVE_PSI_IDLE_INTERFACE */
/*
Id of current query . Statement can be reused to execute several queries
query_id is global in context of the whole MySQL server .
ID is automatically generated from mutex - protected counter .
It ' s used in handler code for various purposes : to check which columns
from table are necessary for this select , to check if it ' s necessary to
update auto - updatable fields ( like auto_increment and timestamp ) .
*/
query_id_t query_id;
access_t col_access{NO_ACL};
/* Statement id is thread-wide. This counter is used to generate ids */
ulong statement_id_counter;
ulong rand_saved_seed1, rand_saved_seed2;
/* The following variables are used when printing to slow log */
ulong query_plan_flags;
ulong query_plan_fsort_passes;
ulong tmp_tables_used;
ulong tmp_tables_disk_used;
ulonglong tmp_tables_size;
ulonglong bytes_sent_old;
ulonglong affected_rows; /* Number of changed rows */
ulonglong max_tmp_space_used= 0 ;
Opt_trace_context opt_trace;
pthread_t real_id; /* For debugging */
my_thread_id thread_id, thread_dbug_id;
uint32 os_thread_id;
uint tmp_table, global_disable_checkpoint;
uint server_status,open_options;
enum enum_thread_type system_thread;
enum backup_stages current_backup_stage;
#ifdef WITH_WSREP
bool wsrep_desynced_backup_stage;
#endif /* WITH_WSREP */
/*
Current or next transaction isolation level .
When a connection is established , the value is taken from
@ @ session . transaction_isolation ( default transaction isolation
for the session ) , which is in turn taken from
@ @ global . transaction_isolation ( the global value ) .
If there is no transaction started , this variable
holds the value of the next transaction ' s isolation level .
When a transaction starts , the value stored in this variable
becomes " actual " .
At transaction commit or rollback , we assign this variable
again from @ @ session . transaction_isolation .
The only statement that can otherwise change the value
of this variable is SET TRANSACTION ISOLATION LEVEL .
Its purpose is to effect the isolation level of the next
transaction in this session . When this statement is executed ,
the value in this variable is changed . However , since
this statement is only allowed when there is no active
transaction , this assignment ( naturally ) only affects the
upcoming transaction .
At the end of the current active transaction the value is
be reset again from @ @ session . transaction_isolation , as described
above .
*/
enum_tx_isolation tx_isolation;
/*
Current or next transaction access mode .
See comment above regarding transaction_isolation .
*/
bool tx_read_only;
enum_check_fields count_cuted_fields;
DYNAMIC_ARRAY user_var_events; /* For user variables replication */
MEM_ROOT *user_var_events_alloc; /* Allocate above array elements here */
/*
Define durability properties that engines may check to
improve performance . Not yet used in MariaDB
*/
enum durability_properties durability_property;
/*
If checking this in conjunction with a wait condition , please
include a check after enter_cond ( ) if you want to avoid a race
condition . For details see the implementation of awake ( ) ,
especially the " broadcast " part .
*/
killed_state volatile killed;
/*
The following is used if one wants to have a specific error number and
text for the kill
*/
struct err_info
{
int no;
const char msg[256 ];
} *killed_err;
/* See also thd_killed() */
inline bool check_killed(bool dont_send_error_message= 0 )
{
if (unlikely(killed))
{
if (!dont_send_error_message)
send_kill_message();
return TRUE ;
}
if (apc_target.have_apc_requests())
apc_target.process_apc_requests(false );
return FALSE ;
}
/* scramble - random string sent to client on handshake */
char scramble[SCRAMBLE_LENGTH+1 ];
/*
If this is a slave , the name of the connection stored here .
This is used for tagging error messages in the log files .
*/
LEX_CSTRING connection_name;
uint8 password; /* 0, 1 or 2 */
uint8 failed_com_change_user;
bool slave_thread;
bool no_errors;
/**
Set to TRUE if execution of the current compound statement
can not continue . In particular , disables activation of
CONTINUE or EXIT handlers of stored routines .
Reset in the end of processing of the current user request , in
@ see THD : : reset_for_next_command ( ) .
*/
bool is_fatal_error;
/**
Set by a storage engine to request the entire
transaction ( that possibly spans multiple engines ) to
rollback . Reset in ha_rollback .
*/
bool transaction_rollback_request;
/**
TRUE if we are in a sub - statement and the current error can
not be safely recovered until we left the sub - statement mode .
In particular , disables activation of CONTINUE and EXIT
handlers inside sub - statements . E . g . if it is a deadlock
error and requires a transaction - wide rollback , this flag is
raised ( traditionally , MySQL first has to close all the reads
via @ see handler : : ha_index_or_rnd_end ( ) and only then perform
the rollback ) .
Reset to FALSE when we leave the sub - statement mode .
*/
bool is_fatal_sub_stmt_error;
/* 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, free_connection_done;
/**
is set if a statement accesses a temporary table created through
CREATE TEMPORARY TABLE .
*/
private :
bool charset_is_system_charset, charset_is_collation_connection;
bool charset_is_character_set_filesystem;
public :
bool enable_slow_log; /* Enable slow log for current statement */
bool abort_on_warning;
bool got_warning; /* Set on call to push_warning() */
/* set during loop of derived table processing */
bool derived_tables_processing;
bool tablespace_op; /* This is TRUE in DISCARD/IMPORT TABLESPACE */
bool log_current_statement() const
{
return variables.option_bits & OPTION_BINLOG_THIS_STMT;
}
/**
True if a slave error . Causes the slave to stop . Not the same
as the statement execution error ( is_error ( ) ) , since
a statement may be expected to return an error , e . g . because
it returned an error on master , and this is OK on the slave .
*/
bool is_slave_error;
/* True if we have printed something to the error log for this statement */
bool error_printed_to_log;
/*
True when a transaction is queued up for binlog group commit .
Used so that if another transaction needs to wait for a row lock held by
this transaction , it can signal to trigger the group commit immediately ,
skipping the normal - - binlog - commit - wait - count wait .
*/
bool waiting_on_group_commit;
/*
Set true when another transaction goes to wait on a row lock held by this
transaction . Used together with waiting_on_group_commit .
*/
bool has_waiter;
/*
In case of a slave , set to the error code the master got when executing
the query . 0 if no error on the master .
The stored into variable master error code may get reset inside
execution stack when the event turns out to be ignored .
*/
int slave_expected_error;
enum_sql_command last_sql_command; // Last sql_command executed in mysql_execute_command()
sp_rcontext *spcont; // SP runtime context
sp_rcontext *get_rcontext(const sp_rcontext_addr &addr);
Item_field *get_variable(const sp_rcontext_addr &addr);
/** number of name_const() substitutions, see sp_head.cc:subst_spvars() */
uint query_name_consts;
NET* slave_net; // network connection from slave -> m.
/*
Used to update global user stats . The global user stats are updated
occasionally with the ' diff ' variables . After the update , the ' diff '
variables are reset to 0 .
*/
/* Time when the current thread connected to MySQL. */
time_t current_connect_time;
/* Last time when THD stats were updated in global_user_stats. */
time_t last_global_update_time;
/* Number of commands not reflected in global_user_stats yet. */
uint select_commands, update_commands, other_commands;
ulonglong start_cpu_time;
ulonglong start_bytes_received;
/* Used by the sys_var class to store temporary values */
union
{
my_bool my_bool_value;
int int_value;
uint uint_value;
long long_value;
ulong ulong_value;
ulonglong ulonglong_value;
double double_value;
void *ptr_value;
} sys_var_tmp;
struct {
/*
If true , mysql_bin_log : : write ( Log_event ) call will not write events to
binlog , and maintain 2 below variables instead ( use
mysql_bin_log . start_union_events to turn this on )
*/
bool do_union;
/*
If TRUE , at least one mysql_bin_log : : write ( Log_event ) call has been
made after last mysql_bin_log . start_union_events ( ) call .
*/
bool unioned_events;
/*
If TRUE , at least one mysql_bin_log : : write ( Log_event e ) , where
e . cache_stmt = = TRUE call has been made after last
mysql_bin_log . start_union_events ( ) call .
*/
bool unioned_events_trans;
/*
' queries ' ( actually SP statements ) that run under inside this binlog
union have thd - > query_id > = first_query_id .
*/
query_id_t first_query_id;
} binlog_evt_union;
/**
Internal parser state .
Note that since the parser is not re - entrant , we keep only one parser
state here . This member is valid only when executing code during parsing .
*/
Parser_state *m_parser_state;
Locked_tables_list locked_tables_list;
/*
If non - NULL , used to record all the " context " ( i . e . environment data ) that
the optimizer sees . The idea is to save the context and then re - create it
somewhere else .
*/
Optimizer_context_recorder *opt_ctx_recorder= NULL;
/* Optimizer Context we are replaying */
Optimizer_context_replay *opt_ctx_replay= NULL;
/* Previously captured context, available through I_S.OPTIMIZER_CONTEXT */
Optimizer_context_capture *captured_opt_ctx= NULL;
#ifdef WITH_PARTITION_STORAGE_ENGINE
partition_info *work_part_info;
#endif
#ifndef EMBEDDED_LIBRARY
/**
Array of active audit plugins which have been used by this THD .
This list is later iterated to invoke release_thd ( ) on those
plugins .
*/
DYNAMIC_ARRAY audit_class_plugins;
/**
Array of bits indicating which audit classes have already been
added to the list of audit plugins which are currently in use .
*/
unsigned long audit_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE];
int audit_plugin_version;
#endif
#if defined (ENABLED_DEBUG_SYNC)
/* Debug Sync facility. See debug_sync.cc. */
struct st_debug_sync_control *debug_sync_control;
#endif /* defined(ENABLED_DEBUG_SYNC) */
/**
@ param id thread identifier
@ param is_wsrep_applier thread type
*/
THD(my_thread_id id, bool is_wsrep_applier= false );
~THD();
void init();
/*
Initialize memory roots necessary for query processing and ( ! )
pre - allocate memory for it . We can ' t do that in THD constructor because
there are use cases ( acl_init , delayed inserts , watcher threads ,
killing mysqld ) where it ' s vital to not allocate excessive and not used
memory . Note , that we still don ' t return error from init_for_queries ( ) :
if preallocation fails , we should notice that at the first call to
alloc_root .
*/
void init_for_queries();
void update_all_stats();
void update_stats(void );
void change_user(void );
void cleanup(void );
void cleanup_after_query();
void free_connection();
void reset_for_reuse();
void store_globals();
void reset_stack()
{
thread_stack= 0 ;
}
void reset_globals();
bool trace_started()
{
return opt_trace.is_started();
}
#ifdef SIGNAL_WITH_VIO_CLOSE
inline void set_active_vio(Vio* vio)
{
mysql_mutex_lock(&LOCK_thd_data);
active_vio = vio;
mysql_mutex_unlock(&LOCK_thd_data);
}
inline void clear_active_vio()
{
mysql_mutex_lock(&LOCK_thd_data);
active_vio = 0 ;
mysql_mutex_unlock(&LOCK_thd_data);
}
void close_active_vio();
#endif
void awake_no_mutex(killed_state state_to_set,
int killed_errno_arg= 0 ,
const char *killed_err_msg_arg= 0 );
void awake(killed_state state_to_set,
int killed_errno_arg= 0 ,
const char *killed_err_msg_arg= 0 )
{
mysql_mutex_lock(&LOCK_thd_kill);
mysql_mutex_lock(&LOCK_thd_data);
awake_no_mutex(state_to_set, killed_errno_arg, killed_err_msg_arg);
mysql_mutex_unlock(&LOCK_thd_data);
mysql_mutex_unlock(&LOCK_thd_kill);
}
void abort_current_cond_wait(bool force);
/** Disconnect the associated communication endpoint. */
void disconnect();
/*
Allows this thread to serve as a target for others to schedule Async
Procedure Calls on .
It ' s possible to schedule any code to be executed this way , by
inheriting from the Apc_call object . Currently , only
Show_explain_request uses this .
*/
Apc_target apc_target;
Gap_time_tracker_data gap_tracker_data;
#ifndef MYSQL_CLIENT
enum enum_binlog_query_type {
/* The query can be logged in row format or in statement format. */
ROW_QUERY_TYPE,
/* The query has to be logged in statement format. */
STMT_QUERY_TYPE,
QUERY_TYPE_COUNT
};
int binlog_query(enum_binlog_query_type qtype,
char const *query, ulong query_len, bool is_trans,
bool direct, bool suppress_use,
int errcode);
bool binlog_current_query_unfiltered();
#endif
inline void
enter_cond(mysql_cond_t *cond, mysql_mutex_t* mutex,
const PSI_stage_info *stage, PSI_stage_info *old_stage,
const char *src_function, const char *src_file,
int src_line)
{
mysql_mutex_assert_owner(mutex);
mysys_var->current_mutex = mutex;
mysys_var->current_cond = cond;
if (old_stage)
backup_stage(old_stage);
if (stage)
enter_stage(stage, src_function, src_file, src_line);
}
inline void exit_cond(const PSI_stage_info *stage,
const char *src_function, const char *src_file,
int src_line)
{
/*
Putting the mutex unlock in thd - > exit_cond ( ) ensures that
mysys_var - > current_mutex is always unlocked _ before_ mysys_var - > mutex is
locked ( if that would not be the case , you ' ll get a deadlock if someone
does a THD : : awake ( ) on you ) .
*/
mysql_mutex_unlock(mysys_var->current_mutex);
mysql_mutex_lock(&mysys_var->mutex);
mysys_var->current_mutex = 0 ;
mysys_var->current_cond = 0 ;
if (stage)
enter_stage(stage, src_function, src_file, src_line);
mysql_mutex_unlock(&mysys_var->mutex);
return ;
}
int is_killed() { return killed; }
/**
A callback to the server internals that is used to address
special cases of the locking protocol .
Invoked when acquiring an exclusive lock , for each thread that
has a conflicting shared metadata lock .
This function :
- aborts waiting of the thread on a data lock , to make it notice
the pending exclusive lock and back off .
- if the thread is an INSERT DELAYED thread , sends it a KILL
signal to terminate it .
@ note This function does not wait for the thread to give away its
locks . Waiting is done outside for all threads at once .
@ param in_use The THD to wake up .
@ param needs_thr_lock_abort Indicates that to wake up thread
this call needs to abort its waiting
on table - level lock .
@ retval TRUE if the thread was woken up
@ retval FALSE otherwise .
*/
bool notify_shared_lock(THD *in_use,
bool needs_thr_lock_abort,
bool needs_non_slave_abort);
inline bool is_strict_mode() const
{
return (bool ) (variables.sql_mode & (MODE_STRICT_TRANS_TABLES |
MODE_STRICT_ALL_TABLES));
}
inline bool backslash_escapes() const
{
return !MY_TEST(variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES);
}
const Type_handler *type_handler_for_datetime() const ;
bool timestamp_to_TIME(MYSQL_TIME *ltime, my_time_t ts,
ulong sec_part, date_mode_t fuzzydate);
inline my_time_t query_start() { return start_time; }
inline ulong query_start_sec_part()
{ used|= QUERY_START_SEC_PART_USED; return start_time_sec_part; }
MYSQL_TIME query_start_TIME();
time_round_mode_t temporal_round_mode() const
{
return variables.sql_mode & MODE_TIME_ROUND_FRACTIONAL ?
TIME_FRAC_ROUND : TIME_FRAC_TRUNCATE;
}
private :
struct {
my_hrtime_t start;
my_time_t sec;
ulong sec_part;
} system_time;
void set_system_time()
{
my_hrtime_t hrtime= my_hrtime();
my_time_t sec= hrtime_to_my_time(hrtime);
ulong sec_part= hrtime_sec_part(hrtime);
if (sec > system_time.sec ||
(sec == system_time.sec && sec_part > system_time.sec_part) ||
hrtime.val < system_time.start.val)
{
system_time.sec= sec;
system_time.sec_part= sec_part;
system_time.start= hrtime;
}
else
{
if (system_time.sec_part < TIME_MAX_SECOND_PART)
system_time.sec_part++;
else
{
system_time.sec++;
system_time.sec_part= 0 ;
}
}
}
public :
my_timeval transaction_time()
{
if (!in_multi_stmt_transaction_mode())
transaction->start_time.reset(this );
return transaction->start_time;
}
inline void set_start_time()
{
if (user_time.val)
{
start_time= hrtime_to_my_time(user_time);
start_time_sec_part= hrtime_sec_part(user_time);
}
else
{
set_system_time();
start_time= system_time.sec;
start_time_sec_part= system_time.sec_part;
}
PSI_CALL_set_thread_start_time(start_time);
}
inline void set_time()
{
set_start_time();
start_utime= utime_after_lock= microsecond_interval_timer();
}
/* only used in SET @@timestamp=... */
inline void set_time(my_hrtime_t t)
{
user_time= t;
set_time();
}
inline void force_set_time(my_time_t t, ulong sec_part)
{
start_time= system_time.sec= t;
start_time_sec_part= system_time.sec_part= sec_part;
}
/*
this is only used by replication and BINLOG command .
usecs > TIME_MAX_SECOND_PART means " was not in binlog "
*/
inline void set_time(my_time_t t, ulong sec_part)
{
if (opt_secure_timestamp > (slave_thread ? SECTIME_REPL : SECTIME_SUPER))
set_time(); // note that BINLOG itself requires SUPER
else
{
if (sec_part <= TIME_MAX_SECOND_PART)
force_set_time(t, sec_part);
else if (t != system_time.sec)
force_set_time(t, 0 );
else
{
start_time= t;
start_time_sec_part= ++system_time.sec_part;
}
user_time.val= hrtime_from_time(start_time) + start_time_sec_part;
PSI_CALL_set_thread_start_time(start_time);
start_utime= utime_after_lock= microsecond_interval_timer();
}
}
void set_time_after_lock()
{
utime_after_lock= microsecond_interval_timer();
MYSQL_SET_STATEMENT_LOCK_TIME(m_statement_psi,
(utime_after_lock - start_utime));
}
ulonglong current_utime() { return microsecond_interval_timer(); }
/* Tell SHOW PROCESSLIST to show time from this point */
inline void set_time_for_next_stage()
{
utime_after_query= current_utime();
}
Timeval_null safe_timeval_replacement_for_nonzero_datetime(const Datetime &);
/**
Update server status after execution of a top level statement .
Currently only checks if a query was slow , and assigns
the status accordingly .
Evaluate the current time , and if it exceeds the long - query - time
setting , mark the query as slow .
*/
void update_server_status()
{
set_time_for_next_stage();
if (utime_after_query >= utime_after_lock + variables.log_slow_query_time)
server_status|= SERVER_QUERY_WAS_SLOW;
}
/* True if query took longer than log_slow_always_query_time */
bool log_slow_always_query_time()
{
return (utime_after_query >= utime_after_lock + variables.log_slow_always_query_time);
}
inline ulonglong found_rows(void )
{
return limit_found_rows;
}
/**
Returns TRUE if session is in a multi - statement transaction mode .
OPTION_NOT_AUTOCOMMIT : When autocommit is off , a multi - statement
transaction is implicitly started on the first statement after a
previous transaction has been ended .
OPTION_BEGIN : Regardless of the autocommit status , a multi - statement
transaction can be explicitly started with the statements " START
TRANSACTION " , " BEGIN [ WORK ] " , " [ COMMIT | ROLLBACK ] AND CHAIN " , etc .
Note : this doesn ' t tell you whether a transaction is active .
A session can be in multi - statement transaction mode , and yet
have no active transaction , e . g . , in case of :
set @ @ autocommit = 0 ;
set @ a = 3 ; < - - these statements don ' t
set transaction isolation level serializable ; < - - start an active
flush tables ; < - - transaction
I . e . for the above scenario this function returns TRUE , even
though no active transaction has begun .
@ sa in_active_multi_stmt_transaction ( )
*/
inline bool in_multi_stmt_transaction_mode()
{
return variables.option_bits & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN);
}
/**
TRUE if the session is in a multi - statement transaction mode
( @ sa in_multi_stmt_transaction_mode ( ) ) * and * there is an
active transaction , i . e . there is an explicit start of a
transaction with BEGIN statement , or implicit with a
statement that uses a transactional engine .
For example , these scenarios don ' t start an active transaction
( even though the server is in multi - statement transaction mode ) :
set @ @ autocommit = 0 ;
select * from nontrans_table ;
set @ var = TRUE ;
flush tables ;
Note , that even for a statement that starts a multi - statement
transaction ( i . e . select * from trans_table ) , this
flag won ' t be set until we open the statement ' s tables
and the engines register themselves for the transaction
( see trans_register_ha ( ) ) ,
hence this method is reliable to use only after
open_tables ( ) has completed .
Why do we need a flag ?
- - - - - - - - - - - - - - - - - - - - - -
We need to maintain a ( at first glance redundant )
session flag , rather than looking at thd - > transaction . all . ha_list
because of explicit start of a transaction with BEGIN .
I . e . in case of
BEGIN ;
select * from nontrans_t1 ; < - - in_active_multi_stmt_transaction ( ) is true
*/
inline bool in_active_multi_stmt_transaction()
{
return server_status & SERVER_STATUS_IN_TRANS;
}
/* Commit both statement and full transaction */
int commit_whole_transaction_and_close_tables();
void give_protection_error();
/*
Give an error if any of the following is true for this connection
- BACKUP STAGE is active
- FLUSH TABLE WITH READ LOCK is active
- BACKUP LOCK table_name is active
*/
inline bool has_read_only_protection()
{
if (current_backup_stage == BACKUP_FINISHED &&
!global_read_lock.is_acquired() &&
!mdl_backup_lock)
return FALSE ;
give_protection_error();
return TRUE ;
}
inline bool fill_information_schema_tables()
{
return !stmt_arena->is_stmt_prepare();
}
inline void * trans_alloc(size_t size) const
{
return alloc_root(&transaction->mem_root,size);
}
LEX_CSTRING strmake_lex_cstring_trim_whitespace(const LEX_CSTRING &from)
{
return Query_arena::strmake_lex_cstring_trim_whitespace(from, charset());
}
bool convert_string(LEX_STRING *to, CHARSET_INFO *to_cs,
const char *from, size_t from_length,
CHARSET_INFO *from_cs) const ;
bool reinterpret_string_from_binary(LEX_CSTRING *to, CHARSET_INFO *to_cs,
const char *from, size_t from_length)
const ;
bool convert_string(LEX_CSTRING *to, CHARSET_INFO *to_cs,
const char *from, size_t from_length,
CHARSET_INFO *from_cs) const
{
LEX_STRING tmp;
bool rc= convert_string(&tmp, to_cs, from, from_length, from_cs);
to->str= tmp.str;
to->length= tmp.length;
return rc;
}
bool convert_string(LEX_CSTRING *to, CHARSET_INFO *tocs,
const LEX_CSTRING *from, CHARSET_INFO *fromcs,
bool simple_copy_is_possible) const
{
if (!simple_copy_is_possible)
return unlikely(convert_string(to, tocs, from->str, from->length, fromcs));
if (fromcs == &my_charset_bin)
return reinterpret_string_from_binary(to, tocs, from->str, from->length);
*to= *from;
return false ;
}
/*
Convert a strings between character sets .
Uses my_convert_fix ( ) , which uses an mb_wc . . mc_mb loop internally .
dstcs and srccs cannot be & my_charset_bin .
*/
bool convert_fix(CHARSET_INFO *dstcs, LEX_STRING *dst,
CHARSET_INFO *srccs, const char *src, size_t src_length,
String_copier *status) const ;
/*
Same as above , but additionally sends ER_INVALID_CHARACTER_STRING
in case of bad byte sequences or Unicode conversion problems .
*/
bool convert_with_error(CHARSET_INFO *dstcs, LEX_STRING *dst,
CHARSET_INFO *srccs,
const char *src, size_t src_length) const ;
/*
If either " dstcs " or " srccs " is & my_charset_bin ,
then performs native copying using copy_fix ( ) .
Otherwise , performs Unicode conversion using convert_fix ( ) .
*/
bool copy_fix(CHARSET_INFO *dstcs, LEX_STRING *dst,
CHARSET_INFO *srccs, const char *src, size_t src_length,
String_copier *status) const ;
/*
Same as above , but additionally sends ER_INVALID_CHARACTER_STRING
in case of bad byte sequences or Unicode conversion problems .
*/
bool copy_with_error(CHARSET_INFO *dstcs, LEX_STRING *dst,
CHARSET_INFO *srccs, const char *src, size_t src_length)
const ;
bool convert_string(String *s, CHARSET_INFO *from_cs,
CHARSET_INFO *to_cs);
/*
Check if the string is wellformed , raise an error if not wellformed .
@ param str - The string to check .
@ param length - the string length .
*/
bool check_string_for_wellformedness(const char *str,
size_t length,
CHARSET_INFO *cs) const ;
bool to_ident_sys_alloc(Lex_ident_sys_st *to,
const Lex_ident_cli_st *from) const ;
/*
Create a string literal with optional client - > connection conversion .
@ param str - the string in the client character set
@ param length - length of the string
@ param repertoire - the repertoire of the string
*/
Item_basic_constant *make_string_literal(const char *str, size_t length,
my_repertoire_t repertoire);
Item_basic_constant *
make_string_literal(const Lex_string_with_metadata_st &str)
{
my_repertoire_t repertoire= str.repertoire(variables.character_set_client);
return make_string_literal(str.str, str.length, repertoire);
}
Item_basic_constant *make_string_literal_nchar(const Lex_string_with_metadata_st &str);
Item_basic_constant *make_string_literal_charset(const Lex_string_with_metadata_st &str,
CHARSET_INFO *cs);
bool make_text_string_sys(LEX_CSTRING *to,
const Lex_string_with_metadata_st *from)
{
return convert_string(to, system_charset_info,
from, charset(), charset_is_system_charset);
}
bool make_text_string_connection(LEX_CSTRING *to,
const Lex_string_with_metadata_st *from)
{
return convert_string(to, variables.collation_connection,
from, charset(), charset_is_collation_connection);
}
bool make_text_string_filesystem(LEX_CSTRING *to,
const Lex_string_with_metadata_st *from)
{
return convert_string(to, variables.character_set_filesystem,
from, charset(), charset_is_character_set_filesystem);
}
void add_changed_table(TABLE *table);
void add_changed_table(const char *key, size_t key_length);
CHANGED_TABLE_LIST * changed_table_dup(const char *key, size_t key_length);
int prepare_explain_fields(select_result *result, List<Item> *field_list,
uint8 explain_flags, bool is_analyze);
int send_explain_fields(select_result *result, uint8 explain_flags,
bool is_analyze);
void make_explain_field_list(List<Item> &field_list, uint8 explain_flags,
bool is_analyze);
void make_explain_json_field_list(List<Item> &field_list, bool is_analyze);
/**
Clear the current error , if any .
We do not clear is_fatal_error or is_fatal_sub_stmt_error since we
assume this is never called if the fatal error is set .
@ todo : To silence an error , one should use Internal_error_handler
mechanism . Issuing an error that can be possibly later " cleared " is not
compatible with other installed error handlers and audit plugins .
*/
inline void clear_error(bool clear_diagnostics= 0 )
{
DBUG_ENTER("clear_error" );
if (get_stmt_da()->is_error() || clear_diagnostics)
get_stmt_da()->reset_diagnostics_area();
is_slave_error= 0 ;
if (killed == KILL_BAD_DATA)
reset_killed();
my_errno= 0 ;
DBUG_VOID_RETURN;
}
#ifndef EMBEDDED_LIBRARY
inline bool vio_ok() const { return net.vio != 0 ; }
/** Return FALSE if connection to client is broken. */
bool is_connected()
{
/*
All system threads ( e . g . , the slave IO thread ) are connected but
not using vio . So this function always returns true for all
system threads .
*/
return system_thread || (vio_ok() ? vio_is_connected(net.vio) : FALSE );
}
#else
inline bool vio_ok() const { return TRUE ; }
inline bool is_connected() { return TRUE ; }
#endif
void my_ok_with_recreate_info(const Recreate_info &info, ulong warn_count);
/**
Mark the current error as fatal . Warning : this does not
set any error , it sets a property of the error , so must be
followed or prefixed with my_error ( ) .
*/
inline void fatal_error()
{
DBUG_ASSERT(get_stmt_da()->is_error() || killed);
is_fatal_error= 1 ;
DBUG_PRINT("error" ,("Fatal error set" ));
}
/**
TRUE if there is an error in the error stack .
Please use this method instead of direct access to
net . report_error .
If TRUE , the current ( sub ) - statement should be aborted .
The main difference between this member and is_fatal_error
is that a fatal error can not be handled by a stored
procedure continue handler , whereas a normal error can .
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()
{ return m_stmt_da; }
/// Returns Diagnostics-area for the current statement.
const Diagnostics_area *get_stmt_da() const
{ return m_stmt_da; }
/// Sets Diagnostics-area for the current statement.
void set_stmt_da(Diagnostics_area *da)
{ m_stmt_da= da; }
inline CHARSET_INFO *charset() const { 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)
{
if (state == Query_arena::STMT_SP_QUERY_ARGUMENTS)
/*
Caller uses the arena with state STMT_SP_QUERY_ARGUMENTS for stored
routine ' s parameters . Lifetime of these objects spans a lifetime of
stored routine call and freed every time the stored routine execution
has been completed . That is the reason why switching to statement ' s
arena is not performed for arguments , else we would observe increasing
of memory usage while a stored routine be called over and over again .
*/
return NULL;
/*
Use the persistent arena if we are in a prepared statement or a stored
procedure statement and we have not already changed to use this arena .
*/
if (!stmt_arena->is_conventional() && mem_root != stmt_arena->mem_root)
{
set_n_backup_active_arena(stmt_arena, backup);
return stmt_arena;
}
return 0 ;
}
bool is_item_tree_change_register_required()
{
return !stmt_arena->is_conventional();
}
void register_item_tree_change(Item **place)
{
/* TODO: check for OOM condition here */
if (is_item_tree_change_register_required())
nocheck_register_item_tree_change(place, *place, mem_root);
}
void change_item_tree(Item **place, Item *new_value)
{
DBUG_ENTER("THD::change_item_tree" );
DBUG_PRINT("enter" , ("Register: %p (%p) <- %p" ,
*place, place, new_value));
register_item_tree_change(place);
*place= new_value;
DBUG_VOID_RETURN;
}
/**
Make change in item tree after checking whether it needs registering
@ param place place where we should assign new value
@ param new_value place of the new value
@ details
see check_and_register_item_tree_change details
*/
void check_and_register_item_tree(Item **place, Item **new_value)
{
if (!stmt_arena->is_conventional())
check_and_register_item_tree_change(place, new_value, mem_root);
/*
We have to use memcpy instead of * place = * new_value merge to
avoid problems with strict aliasing .
*/
memcpy((char *) place, new_value, sizeof (*new_value));
}
/*
Cleanup statement parse state ( parse tree , lex ) and execution
state after execution of a non - prepared SQL statement .
*/
void end_statement();
/*
Mark thread to be killed , with optional error number and string .
string is not released , so it has to be allocated on thd mem_root
or be a global string
Ensure that we don ' t replace a kill with a lesser one . For example
if user has done ' kill_connection ' we shouldn ' t replace it with
KILL_QUERY .
*/
inline void set_killed(killed_state killed_arg,
int killed_errno_arg= 0 ,
const char *killed_err_msg_arg= 0 )
{
mysql_mutex_lock(&LOCK_thd_kill);
set_killed_no_mutex(killed_arg, killed_errno_arg, killed_err_msg_arg);
mysql_mutex_unlock(&LOCK_thd_kill);
}
/*
This is only used by THD : : awake where we need to keep the lock mutex
locked over some time .
It ' s ok to have this inline , as in most cases killed_errno_arg will
be a constant 0 and most of the function will disappear .
*/
inline void set_killed_no_mutex(killed_state killed_arg,
int killed_errno_arg= 0 ,
const char *killed_err_msg_arg= 0 )
{
if (killed <= killed_arg)
{
killed= killed_arg;
if (killed_errno_arg)
{
/*
If alloc fails , we only remember the killed flag .
The worst things that can happen is that we get
a suboptimal error message .
*/
if (!killed_err)
killed_err= (err_info*) my_malloc(PSI_INSTRUMENT_ME, sizeof (*killed_err), MYF(MY_WME));
if (likely(killed_err))
{
killed_err->no= killed_errno_arg;
::strmake((char *) killed_err->msg, killed_err_msg_arg,
sizeof (killed_err->msg)-1 );
}
}
}
}
int killed_errno();
void reset_killed();
inline void reset_kill_query()
{
if (killed < KILL_CONNECTION)
{
reset_killed();
mysys_var->abort= 0 ;
}
}
inline void send_kill_message()
{
mysql_mutex_lock(&LOCK_thd_kill);
int err= killed_errno();
if (err)
my_message(err, killed_err ? killed_err->msg : ER_THD(this , err), MYF(0 ));
mysql_mutex_unlock(&LOCK_thd_kill);
}
/* return TRUE if we will abort query if we make a warning now */
inline bool really_abort_on_warning()
{
return (abort_on_warning &&
(!transaction->stmt.modified_non_trans_table ||
(variables.sql_mode & MODE_STRICT_ALL_TABLES)));
}
void set_status_var_init(ulong offset);
void reset_n_backup_open_tables_state(Open_tables_backup *backup);
void restore_backup_open_tables_state(Open_tables_backup *backup);
void reset_sub_statement_state(Sub_statement_state *backup, uint new_state);
void restore_sub_statement_state(Sub_statement_state *backup);
void store_slow_query_state(Sub_statement_state *backup);
void reset_slow_query_state(Sub_statement_state *backup);
void add_slow_query_state(Sub_statement_state *backup);
void set_n_backup_active_arena(Query_arena *set, Query_arena *backup);
void restore_active_arena(Query_arena *set, Query_arena *backup);
inline void get_binlog_format(enum_binlog_format *format,
enum_binlog_format *current_format)
{
*format= (enum_binlog_format) variables.binlog_format;
*current_format= current_stmt_binlog_format;
}
inline enum_binlog_format get_current_stmt_binlog_format()
{
return current_stmt_binlog_format;
}
inline void set_binlog_format(enum_binlog_format format,
enum_binlog_format current_format)
{
DBUG_ENTER("set_binlog_format" );
variables.binlog_format= format;
current_stmt_binlog_format= current_format;
DBUG_VOID_RETURN;
}
inline void set_binlog_format_stmt()
{
DBUG_ENTER("set_binlog_format_stmt" );
variables.binlog_format= BINLOG_FORMAT_STMT;
current_stmt_binlog_format= BINLOG_FORMAT_STMT;
DBUG_VOID_RETURN;
}
/*
@ todo Make these methods private or remove them completely . Only
decide_logging_format should call them . / Sven
*/
inline void set_current_stmt_binlog_format_row_if_mixed()
{
DBUG_ENTER("set_current_stmt_binlog_format_row_if_mixed" );
/*
This should only be called from decide_logging_format .
@ todo Once we have ensured this , uncomment the following
statement , remove the big comment below that , and remove the
in_sub_stmt = = 0 condition from the following ' if ' .
*/
/* DBUG_ASSERT(in_sub_stmt == 0); */
/*
If in a stored / function trigger , the caller should already have done the
change . We test in_sub_stmt to prevent introducing bugs where people
wouldn ' t ensure that , and would switch to row - based mode in the middle
of executing a stored function / trigger ( which is too late , see also
reset_current_stmt_binlog_format_row ( ) ) ; this condition will make their
tests fail and so force them to propagate the
lex - > binlog_row_based_if_mixed upwards to the caller .
*/
if ((wsrep_binlog_format(variables.binlog_format) == BINLOG_FORMAT_MIXED) &&
!in_sub_stmt_ps_unsafe())
set_current_stmt_binlog_format_row();
DBUG_VOID_RETURN;
}
inline void set_current_stmt_binlog_format(enum_binlog_format format)
{
current_stmt_binlog_format= format;
}
inline void set_current_stmt_binlog_format_row()
{
DBUG_ENTER("set_current_stmt_binlog_format_row" );
current_stmt_binlog_format= BINLOG_FORMAT_ROW;
DBUG_VOID_RETURN;
}
/* Set binlog format temporarily to statement. Returns old format */
inline enum_binlog_format set_current_stmt_binlog_format_stmt()
{
enum_binlog_format orig_format= current_stmt_binlog_format;
DBUG_ENTER("set_current_stmt_binlog_format_stmt" );
current_stmt_binlog_format= BINLOG_FORMAT_STMT;
DBUG_RETURN(orig_format);
}
inline void restore_stmt_binlog_format(enum_binlog_format format)
{
DBUG_ENTER("restore_stmt_binlog_format" );
DBUG_ASSERT(!is_current_stmt_binlog_format_row());
current_stmt_binlog_format= format;
DBUG_VOID_RETURN;
}
inline void reset_current_stmt_binlog_format_row()
{
DBUG_ENTER("reset_current_stmt_binlog_format_row" );
DBUG_PRINT("debug" ,
("temporary_tables: %s, in_sub_stmt: %s, system_thread: %s" ,
YESNO(has_temporary_tables()), YESNO(in_sub_stmt),
show_system_thread(system_thread)));
if (in_sub_stmt == 0 )
{
if (wsrep_binlog_format(variables.binlog_format) == BINLOG_FORMAT_ROW)
set_current_stmt_binlog_format_row();
else
set_current_stmt_binlog_format_stmt();
}
DBUG_VOID_RETURN;
}
/**
Set the current database ; use deep copy of C - string .
@ param new_db a pointer to the new database name .
@ param new_db_len length of the new database name .
Initialize the current database from a NULL - terminated string with
length . If we run out of memory , we free the current database and
return TRUE . This way the user will notice the error as there will be
no current database selected ( in addition to the error message set by
malloc ) .
@ 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 .
@ return Operation status
@ retval FALSE Success
@ retval TRUE Out - of - memory error
*/
bool set_db(const LEX_CSTRING *new_db);
/** Set the current database, without copying */
void reset_db(const LEX_CSTRING *new_db);
bool check_if_current_db_is_set_with_error() const
{
if (db.str == NULL)
{
/*
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_cte_resolution)
my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0 ));
return TRUE ;
}
return false ;
}
/*
Copy the current database to the argument . Use the current arena to
allocate memory for a deep copy : current database may be freed after
a statement is parsed but before it ' s executed .
Can only be called by owner of thd ( no mutex protection )
*/
bool copy_db_to(LEX_CSTRING *to)
{
if (check_if_current_db_is_set_with_error())
return true ;
to->str= strmake(db.str, db.length);
to->length= db.length;
return to->str == NULL; /* True on error */
}
/*
Make a normalized copy of the current database .
Raise an error if no current database is set .
Note , in case of lower_case_table_names = = 2 , thd - > db can contain the
name in arbitrary case typed by the user , so it must be lower - cased .
For other lower_case_table_names values the name is already in
its normalized case , so it ' s copied as is .
*/
Lex_ident_db_normalized copy_db_normalized()
{
if (check_if_current_db_is_set_with_error())
return Lex_ident_db_normalized();
LEX_CSTRING ident= make_ident_opt_casedn(db, lower_case_table_names == 2 );
/*
A non - empty thd - > db is always known to satisfy check_db_name ( ) .
So after optional lower - casing above it ' s safe to
make Lex_ident_db_normalized .
*/
return Lex_ident_db_normalized(ident);
}
/* Get db name or "". */
const char *get_db()
{ return safe_str(db.str); }
thd_scheduler event_scheduler;
public :
inline Internal_error_handler *get_internal_handler()
{ return m_internal_handler; }
/**
Add an internal error handler to the thread execution context .
@ param handler the exception handler to add
*/
void push_internal_handler(Internal_error_handler *handler);
private :
/**
Handle a sql condition .
@ param sql_errno the condition error number
@ param sqlstate the condition sqlstate
@ param level the condition level
@ param msg the condition message text
@ param [ out ] cond_hdl the sql condition raised , if any
@ return true if the condition is handled
*/
bool handle_condition(uint sql_errno,
const char * sqlstate,
Sql_condition::enum_warning_level *level,
const char * msg,
Sql_condition ** cond_hdl);
public :
/**
Remove the error handler last pushed .
*/
Internal_error_handler *pop_internal_handler();
/**
Raise an exception condition .
@ param code the MYSQL_ERRNO error code of the error
*/
void raise_error(uint code);
/**
Raise an exception condition , with a formatted message .
@ param code the MYSQL_ERRNO error code of the error
*/
void raise_error_printf(uint code, ...);
/**
Raise a completion condition ( warning ) .
@ param code the MYSQL_ERRNO error code of the warning
*/
void raise_warning(uint code);
/**
Raise a completion condition ( warning ) , with a formatted message .
@ param code the MYSQL_ERRNO error code of the warning
*/
void raise_warning_printf(uint code, ...);
/**
Raise a completion condition ( note ) , with a fixed message .
@ param code the MYSQL_ERRNO error code of the note
*/
void raise_note(uint code);
/**
Raise an completion condition ( note ) , with a formatted message .
@ param code the MYSQL_ERRNO error code of the note
*/
void raise_note_printf(uint code, ...);
/**
@ brief Push an error message into MySQL error stack with line
and position information .
This function provides semantic action implementers with a way
to push the famous " You have a syntax error near . . . " error
message into the error stack , which is normally produced only if
a parse error is discovered internally by the Bison generated
parser .
*/
void parse_error(const char *err_text, const char *yytext)
{
Lex_input_stream *lip= &m_parser_state->m_lip;
if (!yytext && !(yytext= lip->get_tok_start()))
yytext= "" ;
/* Push an error into the error stack */
ErrConvString err(yytext, strlen(yytext), variables.character_set_client);
my_printf_error(ER_PARSE_ERROR, ER_THD(this , ER_PARSE_ERROR), MYF(0 ),
err_text, err.ptr(), lip->yylineno);
}
void parse_error(uint err_number, const char *yytext= 0 )
{
parse_error(ER_THD(this , err_number), yytext);
}
void parse_error()
{
parse_error(ER_SYNTAX_ERROR);
}
#ifdef mysqld_error_find_printf_error_used
void parse_error(const char *t)
{
}
#endif
private :
/*
Only the implementation of the SIGNAL and RESIGNAL statements
is permitted to raise SQL conditions in a generic way ,
or to raise them by bypassing handlers ( RESIGNAL ) .
To raise a SQL condition , the code should use the public
raise_error ( ) or raise_warning ( ) methods provided by class THD .
*/
friend class Sql_cmd_common_signal;
friend class Sql_cmd_signal;
friend class Sql_cmd_resignal;
friend void push_warning(THD*, Sql_condition::enum_warning_level, uint, const char *);
friend void my_message_sql(uint, const char *, myf);
/**
Raise a generic SQL condition .
@ param sql_errno the condition error number
@ param sqlstate the condition SQLSTATE
@ param level the condition level
@ param msg the condition message text
@ return The condition raised , or NULL
*/
Sql_condition* raise_condition(uint sql_errno, const char * sqlstate,
Sql_condition::enum_warning_level level, const char * msg)
{
Sql_condition cond(NULL, // don't strdup the msg
Sql_condition_identity(sql_errno, sqlstate, level,
Sql_user_condition_identity()),
msg, get_stmt_da()->current_row_for_warning());
return raise_condition(&cond);
}
Sql_condition* raise_condition(const Sql_condition *cond);
private :
void push_warning_truncated_priv(Sql_condition::enum_warning_level level,
uint sql_errno,
const char *type_str, const char *val)
{
DBUG_ASSERT(sql_errno == ER_TRUNCATED_WRONG_VALUE ||
sql_errno == ER_WRONG_VALUE);
char buff[MYSQL_ERRMSG_SIZE];
CHARSET_INFO *cs= &my_charset_latin1;
cs->cset->snprintf(cs, buff, sizeof (buff),
ER_THD(this , sql_errno), type_str, val);
/*
Note : the format string can vary between ER_TRUNCATED_WRONG_VALUE
and ER_WRONG_VALUE , but the code passed to push_warning ( ) is
always ER_TRUNCATED_WRONG_VALUE . This is intentional .
*/
push_warning(this , level, ER_TRUNCATED_WRONG_VALUE, buff);
}
public :
void push_warning_truncated_wrong_value(Sql_condition::enum_warning_level level,
const char *type_str, const char *val)
{
return push_warning_truncated_priv(level, ER_TRUNCATED_WRONG_VALUE,
type_str, val);
}
void push_warning_wrong_value(Sql_condition::enum_warning_level level,
const char *type_str, const char *val)
{
return push_warning_truncated_priv(level, ER_WRONG_VALUE, type_str, val);
}
void push_warning_truncated_wrong_value(const char *type_str, const char *val)
{
return push_warning_truncated_wrong_value(Sql_condition::WARN_LEVEL_WARN,
type_str, val);
}
void push_warning_truncated_value_for_field(Sql_condition::enum_warning_level
level, const char *type_str,
const char *val,
const char *db_name,
const char *table_name,
const char *name)
{
DBUG_ASSERT(name);
char buff[MYSQL_ERRMSG_SIZE];
CHARSET_INFO *cs= &my_charset_latin1;
if (!db_name)
db_name= "" ;
if (!table_name)
table_name= "" ;
cs->cset->snprintf(cs, buff, sizeof (buff),
ER_THD(this , ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
type_str, val, db_name, table_name, name,
(ulong) get_stmt_da()->current_row_for_warning());
push_warning(this , level, ER_TRUNCATED_WRONG_VALUE, buff);
}
void push_warning_wrong_or_truncated_value(Sql_condition::enum_warning_level level,
bool totally_useless_value,
const char *type_str,
const char *val,
const char *db_name,
const char *table_name,
const char *field_name)
{
if (field_name)
push_warning_truncated_value_for_field(level, type_str, val,
db_name, table_name, field_name);
else if (totally_useless_value)
push_warning_wrong_value(level, type_str, val);
else
push_warning_truncated_wrong_value(level, type_str, val);
}
public :
/** Overloaded to guard query/query_length fields */
void set_statement(Statement *stmt) override;
inline void set_command(enum enum_server_command command)
{
DBUG_ASSERT(command != COM_SLEEP);
m_command= command;
#ifdef HAVE_PSI_THREAD_INTERFACE
PSI_STATEMENT_CALL(set_thread_command)(m_command);
#endif
}
/* As sleep needs a bit of special handling, we have a special case for it */
inline void mark_connection_idle()
{
proc_info= 0 ;
m_command= COM_SLEEP;
#ifdef HAVE_PSI_THREAD_INTERFACE
PSI_STATEMENT_CALL(set_thread_command)(m_command);
#endif
}
inline enum enum_server_command get_command() const
{ return m_command; }
/**
Assign a new value to thd - > query and thd - > query_id and mysys_var .
Protected with LOCK_thd_data mutex .
*/
void set_query(char *query_arg, size_t query_length_arg,
CHARSET_INFO *cs_arg)
{
set_query(CSET_STRING(query_arg, query_length_arg, cs_arg));
}
void set_query(char *query_arg, size_t query_length_arg) /*Mutex protected*/
{
set_query(CSET_STRING(query_arg, query_length_arg, charset()));
}
void set_query(const CSET_STRING &string_arg)
{
mysql_mutex_lock(&LOCK_thd_data);
set_query_inner(string_arg);
mysql_mutex_unlock(&LOCK_thd_data);
PSI_CALL_set_thread_info(query(), query_length());
}
void reset_query() /* Mutex protected */
{ set_query(CSET_STRING()); }
void set_query_and_id(char *query_arg, uint32 query_length_arg,
CHARSET_INFO *cs, query_id_t new_query_id);
void set_query_id(query_id_t new_query_id)
{
query_id= new_query_id;
#ifdef WITH_WSREP
if (WSREP_NNULL(this ))
{
set_wsrep_next_trx_id(query_id);
WSREP_DEBUG("assigned new next trx id: %" PRIu64, wsrep_next_trx_id());
}
#endif /* WITH_WSREP */
}
void set_open_tables(TABLE *open_tables_arg)
{
mysql_mutex_lock(&LOCK_thd_data);
open_tables= open_tables_arg;
mysql_mutex_unlock(&LOCK_thd_data);
}
void set_mysys_var(struct st_my_thread_var *new_mysys_var);
void enter_locked_tables_mode(enum_locked_tables_mode mode_arg)
{
DBUG_ASSERT(locked_tables_mode == LTM_NONE);
if (mode_arg == LTM_LOCK_TABLES)
{
/*
When entering LOCK TABLES mode we should set explicit duration
for all metadata locks acquired so far in order to avoid releasing
them till UNLOCK TABLES statement .
We don ' t do this when entering prelocked mode since sub - statements
don ' t release metadata locks and restoring status - quo after leaving
prelocking mode gets complicated .
*/
mdl_context.set_explicit_duration_for_all_locks();
}
locked_tables_mode= mode_arg;
}
void leave_locked_tables_mode();
/* Release transactional locks if there are no active transactions */
void release_transactional_locks()
{
if (!in_active_multi_stmt_transaction())
mdl_context.release_transactional_locks(this );
}
int decide_logging_format(TABLE_LIST *tables);
/*
In Some cases when decide_logging_format is called it does not have
all information to decide the logging format . So that cases we call
decide_logging_format_2 at later stages in execution .
One example would be binlog format for insert on duplicate key
( IODKU ) but column with unique key is not inserted . We do not have
inserted columns info when we call decide_logging_format so on
later stage we call reconsider_logging_format_for_iodup ( )
*/
void reconsider_logging_format_for_iodup(TABLE *table);
enum need_invoker { INVOKER_NONE=0 , INVOKER_USER, INVOKER_ROLE};
void binlog_invoker(bool role) { m_binlog_invoker= role ? INVOKER_ROLE : INVOKER_USER; }
enum need_invoker need_binlog_invoker() { return m_binlog_invoker; }
void get_definer(LEX_USER *definer, bool role);
void set_invoker(const LEX_CSTRING *user, const LEX_CSTRING *host)
{
invoker.user= *user;
invoker.host= *host;
}
LEX_CSTRING get_invoker_user() { return invoker.user; }
LEX_CSTRING get_invoker_host() { return invoker.host; }
bool has_invoker() { return invoker.user.length > 0 ; }
void print_aborted_warning(uint threshold, const char *reason)
{
if (global_system_variables.log_warnings > threshold)
{
char real_ip_str[64 ];
real_ip_str[0 ]= 0 ;
/* For proxied connections, add the real IP to the warning message */
if (net.using_proxy_protocol && net.vio)
{
if (net.vio->localhost)
snprintf(real_ip_str, sizeof (real_ip_str), " real ip: 'localhost'" );
else
{
char buf[INET6_ADDRSTRLEN];
if (!vio_getnameinfo((sockaddr *)&(net.vio->remote), buf,
sizeof (buf),NULL, 0 , NI_NUMERICHOST))
{
snprintf(real_ip_str, sizeof (real_ip_str), " real ip: '%s'" ,buf);
}
}
}
Security_context *sctx= &main_security_ctx;
sql_print_warning(ER_DEFAULT(ER_NEW_ABORTING_CONNECTION),
thread_id, (db.str ? db.str : "unconnected" ),
sctx->user ? sctx->user : "unauthenticated" ,
sctx->host_or_ip, real_ip_str, reason);
}
}
public :
void clear_wakeup_ready() { wakeup_ready= false ; }
/*
Sleep waiting for others to wake us up with signal_wakeup_ready ( ) .
Must call clear_wakeup_ready ( ) before waiting .
*/
void wait_for_wakeup_ready();
/* Wake this thread up from wait_for_wakeup_ready(). */
void signal_wakeup_ready();
void add_status_to_global()
{
DBUG_ASSERT(status_in_global == 0 );
mysql_mutex_lock(&LOCK_status);
add_to_status(&global_status_var, &status_var);
/* Mark that this THD status has already been added in global status */
status_var.global_memory_used= 0 ;
status_in_global= 1 ;
mysql_mutex_unlock(&LOCK_status);
}
wait_for_commit *wait_for_commit_ptr;
int wait_for_prior_commit(bool allow_kill=true )
{
if (wait_for_commit_ptr)
return wait_for_commit_ptr->wait_for_prior_commit(this , allow_kill);
return 0 ;
}
void wakeup_subsequent_commits(int wakeup_error)
{
if (wait_for_commit_ptr)
wait_for_commit_ptr->wakeup_subsequent_commits(wakeup_error);
}
wait_for_commit *suspend_subsequent_commits() {
wait_for_commit *suspended= wait_for_commit_ptr;
wait_for_commit_ptr= NULL;
return suspended;
}
void resume_subsequent_commits(wait_for_commit *suspended) {
DBUG_ASSERT(!wait_for_commit_ptr);
wait_for_commit_ptr= suspended;
}
void mark_transaction_to_rollback(bool all);
bool internal_transaction() { return transaction != &default_transaction; }
MEM_ROOT *user_vars_root() { return &user_vars_memroot; }
private :
/** The current internal error handler for this thread, or NULL. */
Internal_error_handler *m_internal_handler;
/**
The lex to hold the parsed tree of conventional ( non - prepared ) queries .
Whereas for prepared and stored procedure statements we use an own lex
instance for each new query , for conventional statements we reuse
the same lex . ( @ see mysql_parse for details ) .
*/
LEX main_lex;
/**
This memory root is used for two purposes :
- for conventional queries , to allocate structures stored in main_lex
during parsing , and allocate runtime data ( execution plan , etc . )
during execution .
- for prepared queries , only to allocate runtime data . The parsed
tree itself is reused between executions and thus is stored elsewhere .
*/
MEM_ROOT main_mem_root;
/**
Memory root the user_var_entry objects and their names are allocated on .
*/
MEM_ROOT user_vars_memroot;
Diagnostics_area main_da;
Diagnostics_area *m_stmt_da;
/**
It will be set if CURRENT_USER ( ) or CURRENT_ROLE ( ) is called in account
management statements or default definer is set in CREATE / ALTER SP , SF ,
Event , TRIGGER or VIEW statements .
Current user or role will be binlogged into Query_log_event if
m_binlog_invoker is not NONE ; It will be stored into invoker_host and
invoker_user by SQL thread .
*/
enum need_invoker m_binlog_invoker;
/**
It points to the invoker in the Query_log_event .
SQL thread use it as the default definer in CREATE / ALTER SP , SF , Event ,
TRIGGER or VIEW statements or current user in account management
statements if it is not NULL .
*/
AUTHID invoker;
public :
Session_tracker session_tracker;
/*
Flag , mutex and condition for a thread to wait for a signal from another
thread .
Currently used to wait for group commit to complete , and COND_wakeup_ready
is used for threads to wait on semi - sync ACKs ( though is protected by
Repl_semi_sync_master : : LOCK_binlog ) . Note the following relationships
between these two use - cases when using
rpl_semi_sync_master_wait_point = AFTER_SYNC during group commit :
1 ) Non - leader threads use COND_wakeup_ready to wait for the leader thread
to complete binlog commit .
2 ) The leader thread uses COND_wakeup_ready to await ACKs from the
slave before signalling the non - leader threads to wake up .
With wait_point = AFTER_COMMIT , there is no overlap as binlogging has
finished , so COND_wakeup_ready is safe to re - use .
*/
bool wakeup_ready;
mysql_mutex_t LOCK_wakeup_ready;
mysql_cond_t COND_wakeup_ready;
/*
The GTID assigned to the last commit . If no GTID was assigned to any commit
so far , this is indicated by last_commit_gtid . seq_no = = 0 .
*/
private :
rpl_gtid m_last_commit_gtid;
public :
const rpl_gtid *get_last_commit_gtid() { return &m_last_commit_gtid; }
void set_last_commit_gtid(rpl_gtid >id);
LF_PINS *tdc_hash_pins;
LF_PINS *xid_hash_pins;
bool fix_xid_hash_pins();
const XID *get_xid() const
{
#ifdef WITH_WSREP
if (!wsrep_xid.is_null())
return &wsrep_xid;
#endif /* WITH_WSREP */
return (transaction->xid_state.is_explicit_XA() ?
transaction->xid_state.get_xid() :
&transaction->implicit_xid);
}
/* 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();
bool has_temporary_tables();
bool has_not_logged_temporary_tables();
bool has_logged_temporary_tables();
TABLE *create_and_open_tmp_table(LEX_CUSTRING *frm,
const char *path,
const Lex_ident_db &db,
const Lex_ident_table &table_name,
bool open_internal_tables);
TABLE *find_temporary_table(const Lex_ident_db &db,
const Lex_ident_table &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 Lex_ident_db &db,
const Lex_ident_table &table_name);
TMP_TABLE_SHARE *find_tmp_table_share(const TABLE_LIST *tl);
TMP_TABLE_SHARE *find_tmp_table_share(const char *key, size_t key_length);
bool open_temporary_table(TABLE_LIST *tl);
bool check_and_open_tmp_table(TABLE_LIST *tl);
bool open_temporary_tables(TABLE_LIST *tl);
bool close_temporary_tables();
bool rename_temporary_table(TABLE *table, const LEX_CSTRING *db,
const LEX_CSTRING *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;
uint create_tmp_table_def_key(char *key, const Lex_ident_db &db,
const Lex_ident_table &table_name);
TMP_TABLE_SHARE *create_temporary_table(LEX_CUSTRING *frm,
const char *path,
const Lex_ident_db &db,
const Lex_ident_table &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 Lex_ident_table &alias);
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();
bool 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 :
thd_async_state async_state;
#ifdef HAVE_REPLICATION
/*
If we do a purge of binary logs , log index info of the threads
that are currently reading it needs to be adjusted . To do that
each thread that is using LOG_INFO needs to adjust the pointer to it
*/
LOG_INFO *current_linfo;
Slave_info *slave_info;
void set_current_linfo(LOG_INFO *linfo);
void reset_current_linfo() { set_current_linfo(0 ); }
int register_slave(uchar *packet, size_t packet_length);
void unregister_slave();
bool is_binlog_dump_thread();
#endif
bool check_slave_ignored_db_with_error(const Lex_ident_db &db) const ;
inline ulong wsrep_binlog_format(ulong binlog_format) const
{
#ifdef WITH_WSREP
// During CTAS we force ROW format
if (wsrep_ctas)
return BINLOG_FORMAT_ROW;
else
return ((wsrep_forced_binlog_format != BINLOG_FORMAT_UNSPEC) ?
wsrep_forced_binlog_format : binlog_format);
#else
return (binlog_format);
#endif
}
#ifdef WITH_WSREP
bool wsrep_applier; /* dedicated slave applier thread */
bool wsrep_applier_closing; /* applier marked to close */
bool wsrep_applier_in_rollback; /* applier is rolling
back a transaction */
bool wsrep_client_thread; /* to identify client threads*/
query_id_t wsrep_last_query_id;
XID wsrep_xid;
/** This flag denotes that record locking should be skipped during INSERT,
gap locking during SELECT , and write - write conflicts due to innodb
snapshot isolation during DELETE .
Only used by the streaming replication thread that only modifies the
mysql.wsrep_streaming_log table. */
my_bool wsrep_skip_locking;
mysql_cond_t COND_wsrep_thd;
// changed from wsrep_seqno_t to wsrep_trx_meta_t in wsrep API rev 75
uint32 wsrep_rand;
rpl_group_info *wsrep_rgi;
bool wsrep_converted_lock_session;
ulong wsrep_retry_counter; // of autocommit
bool wsrep_PA_safe;
char * wsrep_retry_query;
size_t wsrep_retry_query_len;
enum enum_server_command wsrep_retry_command;
enum wsrep_consistency_check_mode
wsrep_consistency_check;
std::vector<wsrep::provider::status_variable> wsrep_status_vars;
int wsrep_mysql_replicated;
const char * wsrep_TOI_pre_query; /* a query to apply before
the actual TOI query */
size_t wsrep_TOI_pre_query_len;
wsrep_po_handle_t wsrep_po_handle;
size_t wsrep_po_cnt;
void *wsrep_apply_format;
uchar* wsrep_rbr_buf;
wsrep_gtid_t wsrep_sync_wait_gtid;
uint64 wsrep_last_written_gtid_seqno;
uint64 wsrep_current_gtid_seqno;
ulong wsrep_affected_rows;
bool wsrep_has_ignored_error;
/* true if wsrep_on was ON in last wsrep_on_update */
bool wsrep_was_on;
/*
When enabled , do not replicate / binlog updates from the current table that ' s
being processed . At the moment , it is used to keep mysql . gtid_slave_pos
table updates from being replicated to other nodes via galera replication .
*/
bool wsrep_ignore_table;
/* thread who has started kill for this THD protected by LOCK_thd_data*/
my_thread_id wsrep_aborter;
/* Kill signal used, if thread was killed by manual KILL. Protected by
LOCK_thd_kill. */
std::atomic<killed_state> wsrep_abort_by_kill;
/* */
struct err_info* wsrep_abort_by_kill_err;
#ifndef DBUG_OFF
int wsrep_killed_state;
#endif /* DBUG_OFF */
/* true if BF abort is observed in do_command() right after reading
client's packet, and if the client has sent PS execute command. */
bool wsrep_delayed_BF_abort;
// true if this transaction is CREATE TABLE AS SELECT (CTAS)
bool wsrep_ctas;
/*
Transaction id :
* m_wsrep_next_trx_id is assigned on the first query after
wsrep_next_trx_id ( ) return WSREP_UNDEFINED_TRX_ID
* Each storage engine must assign value of wsrep_next_trx_id ( )
when the transaction starts .
* Effective transaction id is returned via wsrep_trx_id ( )
*/
/*
Return effective transaction id
*/
wsrep_trx_id_t wsrep_trx_id() const
{
return m_wsrep_client_state.transaction().id().get();
}
/*
Set next trx id
*/
void set_wsrep_next_trx_id(query_id_t query_id)
{
m_wsrep_next_trx_id = (wsrep_trx_id_t) query_id;
}
/*
Return next trx id
*/
wsrep_trx_id_t wsrep_next_trx_id() const
{
return m_wsrep_next_trx_id;
}
/*
If node is async slave and have parallel execution , wait for prior commits .
*/
bool wsrep_parallel_slave_wait_for_prior_commit();
private :
wsrep_trx_id_t m_wsrep_next_trx_id; /* cast from query_id_t */
/* wsrep-lib */
Wsrep_mutex m_wsrep_mutex;
Wsrep_condition_variable m_wsrep_cond;
Wsrep_client_service m_wsrep_client_service;
Wsrep_client_state m_wsrep_client_state;
public :
Wsrep_client_state& wsrep_cs() { return m_wsrep_client_state; }
const Wsrep_client_state& wsrep_cs() const { return m_wsrep_client_state; }
const wsrep::transaction& wsrep_trx() const
{ return m_wsrep_client_state.transaction(); }
const wsrep::streaming_context& wsrep_sr() const
{ return m_wsrep_client_state.transaction().streaming_context(); }
/* Pointer to applier service for streaming THDs. This is needed to
be able to delete applier service object in case of background
rollback. */
Wsrep_applier_service* wsrep_applier_service;
/* wait_for_commit struct for binlog group commit */
wait_for_commit wsrep_wfc;
bool wsrep_applier_is_in_rollback() const
{ return wsrep_applier_in_rollback; }
#endif /* WITH_WSREP */
/* Handling of timeouts for commands */
thr_timer_t query_timer;
/*
Number of strings which were involved in sorting or grouping and whose
lengths were truncated according to the max_sort_length system variable
setting
*/
ulonglong num_of_strings_sorted_on_truncated_length;
public :
void set_query_timer_if_needed()
{
#ifndef EMBEDDED_LIBRARY
/*
Slave vs user threads have timeouts configured via different variables ,
so pick the appropriate one to use .
*/
ulonglong timeout_val=
slave_thread ? slave_max_statement_time : variables.max_statement_time;
/*
Don ' t start a query timer if
- If timeouts are not set
- if we are in a stored procedure or sub statement
- If we already have set a timeout ( happens when running prepared
statements that calls mysql_execute_command ( ) )
*/
if (!timeout_val || spcont || in_sub_stmt || query_timer.expired == 0 )
return ;
set_query_timer_force(timeout_val);
#endif
}
void set_query_timer_force(ulonglong timeout_val)
{
#ifndef EMBEDDED_LIBRARY
thr_timer_settime(&query_timer, timeout_val);
#endif
}
void reset_query_timer()
{
#ifndef EMBEDDED_LIBRARY
if (spcont || in_sub_stmt)
return ;
if (!query_timer.expired)
thr_timer_end(&query_timer);
#endif
}
bool restore_set_statement_var()
{
return main_lex.restore_set_statement_var();
}
/* Copy relevant `stmt` transaction flags to `all` transaction. */
void merge_unsafe_rollback_flags()
{
if (transaction->stmt.modified_non_trans_table)
transaction->all.modified_non_trans_table= TRUE ;
transaction->all.m_unsafe_rollback_flags|=
(transaction->stmt.m_unsafe_rollback_flags &
(THD_TRANS::MODIFIED_NON_TRANS_TABLE |
THD_TRANS::DID_WAIT | THD_TRANS::CREATED_TEMP_TABLE |
THD_TRANS::DROPPED_TEMP_TABLE | THD_TRANS::DID_DDL |
THD_TRANS::EXECUTED_TABLE_ADMIN_CMD));
}
uint get_net_wait_timeout()
{
if (in_active_multi_stmt_transaction())
{
if (transaction->all.is_trx_read_write())
{
if (variables.idle_write_transaction_timeout > 0 )
return variables.idle_write_transaction_timeout;
}
else
{
if (variables.idle_readonly_transaction_timeout > 0 )
return variables.idle_readonly_transaction_timeout;
}
if (variables.idle_transaction_timeout > 0 )
return variables.idle_transaction_timeout;
}
return uint(variables.net_wait_timeout);
}
/**
Switch to a sublex , to parse a substatement or an expression .
*/
void set_local_lex(sp_lex_local *sublex)
{
DBUG_ASSERT(lex->sphead);
lex= sublex;
/* Reset part of parser state which needs this. */
m_parser_state->m_yacc.reset_before_substatement();
}
/**
Switch back from a sublex ( currently pointed by this - > lex ) to the old lex .
Sublex is merged to " oldlex " and this - > lex is set to " oldlex " .
This method is called after parsing a substatement or an expression .
set_local_lex ( ) must be previously called .
@ param oldlex - The old lex which was active before set_local_lex ( ) .
@ returns - false on success , true on error ( failed to merge LEX ' s ) .
See also sp_head : : merge_lex ( ) .
*/
bool restore_from_local_lex_to_old_lex(LEX *oldlex);
Item *sp_fix_func_item(Item **it_addr);
Item *sp_fix_func_item_for_assignment(const Field *to, Item **it_addr);
Item *sp_prepare_func_item(Item **it_addr, uint cols);
bool sp_eval_expr(Field *result_field, Item **expr_item_ptr);
bool sql_parser(LEX *old_lex, LEX *lex,
char *str, uint str_len, bool stmt_prepare_mode);
myf get_utf8_flag() const
{
return (variables.old_behavior & OLD_MODE_UTF8_IS_UTF8MB3 ?
MY_UTF8_IS_UTF8MB3 : 0 );
}
Charset_collation_context
charset_collation_context_create_db() const
{
return Charset_collation_context(variables.collation_server,
variables.collation_server);
}
Charset_collation_context
charset_collation_context_alter_db(const char *db);
Charset_collation_context
charset_collation_context_create_table_in_db(const char *db);
Charset_collation_context
charset_collation_context_alter_table(const TABLE_SHARE *s);
/**
Save current lex to the output parameter and reset it to point to
main_lex . This method is called from mysql_client_binlog_statement ( )
to temporary
@ param [ out ] backup_lex original value of current lex
*/
void backup_and_reset_current_lex(LEX **backup_lex)
{
*backup_lex= lex;
lex= &main_lex;
}
/**
Restore current lex to its original value it had before calling the method
backup_and_reset_current_lex ( ) .
@ param backup_lex original value of current lex
*/
void restore_current_lex(LEX *backup_lex)
{
lex= backup_lex;
}
bool should_collect_handler_stats()
{
/*
We update handler_stats . active to ensure that we have the same
value across the whole statement .
This function is only called from TABLE : : init ( ) so the value will
be the same for the whole statement .
*/
handler_stats.active=
((variables.log_slow_verbosity & LOG_SLOW_VERBOSITY_ENGINE) ||
userstat_running || lex->analyze_stmt);
return handler_stats.active;
}
/* Return true if we should create a note when an unusable key is found */
bool give_notes_for_unusable_keys()
{
return ((variables.note_verbosity & (NOTE_VERBOSITY_UNUSABLE_KEYS)) ||
(lex->describe && // Is EXPLAIN
(variables.note_verbosity & NOTE_VERBOSITY_EXPLAIN)));
}
uint gconcat_max_len()
{
return MY_MIN(variables.group_concat_max_len,
(uint)variables.max_allowed_packet);
}
bool vers_insert_history_fast(const TABLE *table)
{
DBUG_ASSERT(table->versioned());
return table->versioned(VERS_TIMESTAMP) &&
(variables.option_bits & OPTION_INSERT_HISTORY) &&
lex->duplicates == DUP_ERROR;
}
bool vers_insert_history(const Field *field)
{
if (!field->vers_sys_field())
return false ;
if (!vers_insert_history_fast(field->table))
return false ;
if (lex->sql_command != SQLCOM_INSERT &&
lex->sql_command != SQLCOM_INSERT_SELECT &&
lex->sql_command != SQLCOM_LOAD)
return false ;
return !is_set_timestamp_forbidden(this );
}
/**
@ brief
Return true if current statement uses cursor protocol for execution .
@ details
Cursor protocol execution is determined by checking if lex - > result is a
Select_materialize object , which is exclusively used by the server for
cursor result set materialization .
*/
bool is_cursor_execution() const ;
/*
Return true if we are in stored procedure , not in a function or
trigger .
*/
bool in_stored_procedure()
{
return (lex->sphead != 0 &&
!(in_sub_stmt & (SUB_STMT_FUNCTION | SUB_STMT_TRIGGER)));
}
bool reparsing_sp_stmt= {false };
/* Data and methods for bulk multiple unit result reporting */
DYNAMIC_ARRAY *unit_results;
void stop_collecting_unit_results();
bool collect_unit_results(ulonglong id, ulonglong affected_rows);
bool need_report_unit_results();
bool report_collected_unit_results();
bool init_collecting_unit_results();
void push_final_warnings();
};
/*
Start a new independent transaction for the THD .
The old one is stored in this object and restored when calling
restore_old_transaction ( ) or when the object is freed
*/
class start_new_trans
{
/* container for handler's private per-connection data */
Ha_data old_ha_data[MAX_HA];
struct THD::st_transactions *old_transaction, new_transaction;
Open_tables_backup open_tables_state_backup;
MDL_savepoint mdl_savepoint;
PSI_transaction_locker *m_transaction_psi;
THD *org_thd;
uint in_sub_stmt;
uint server_status;
my_bool wsrep_on;
public :
start_new_trans(THD *thd);
~start_new_trans()
{
destroy();
}
void destroy()
{
if (org_thd) // Safety
restore_old_transaction();
new_transaction.free();
}
void restore_old_transaction();
};
/** A short cut for thd->get_stmt_da()->set_ok_status(). */
inline void
my_ok(THD *thd, ulonglong affected_rows_arg= 0 , ulonglong id= 0 ,
const char *message= NULL)
{
thd->set_row_count_func(affected_rows_arg);
thd->set_affected_rows(affected_rows_arg);
thd->get_stmt_da()->set_ok_status(affected_rows_arg, id, message);
}
/** A short cut for thd->get_stmt_da()->set_eof_status(). */
inline void
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) \
{ulonglong tmp_disable_binlog__save_options= (A)->variables.option_bits; \
(A)->variables.option_bits&= ~OPTION_BIN_LOG; \
(A)->variables.option_bits|= OPTION_BIN_TMP_LOG_OFF;
#define reenable_binlog(A) \
(A)->variables.option_bits= tmp_disable_binlog__save_options; }
inline date_conv_mode_t sql_mode_for_dates(THD *thd)
{
static_assert((ulonglong(date_conv_mode_t::KNOWN_MODES) &
ulonglong(time_round_mode_t::KNOWN_MODES)) == 0 ,
"date_conv_mode_t and time_round_mode_t must use different "
"bit values" );
static_assert(MODE_NO_ZERO_DATE == date_mode_t::NO_ZERO_DATE &&
MODE_NO_ZERO_IN_DATE == date_mode_t::NO_ZERO_IN_DATE &&
MODE_INVALID_DATES == date_mode_t::INVALID_DATES,
"sql_mode_t and date_mode_t values must be equal" );
return date_conv_mode_t(thd->variables.sql_mode &
(MODE_NO_ZERO_DATE | MODE_NO_ZERO_IN_DATE | MODE_INVALID_DATES));
}
/*
Used to hold information about file and file structure in exchange
via non - DB file ( . . . INTO OUTFILE . . . , . . . LOAD DATA . . . )
XXX : We never call destructor for objects of this class .
*/
class sql_exchange :public Sql_alloc
{
public :
enum enum_filetype filetype; /* load XML, Added by Arnold & Erik */
const char *file_name;
String *field_term,*enclosed,*line_term,*line_start,*escaped;
bool opt_enclosed;
bool dumpfile;
ulong skip_lines;
CHARSET_INFO *cs;
sql_exchange(const char *name, bool dumpfile_flag,
enum_filetype filetype_arg= FILETYPE_CSV);
bool escaped_given(void ) const ;
};
/*
This is used to get result from a select
*/
class JOIN;
/*
This is a select_result_sink which simply writes all data into a ( temporary )
table . Creation / deletion of the table is outside of the scope of the class
It is aimed at capturing SHOW EXPLAIN output , so :
- Unlike select_result class , we don ' t assume that the sent data is an
output of a SELECT_LEX_UNIT ( and so we don ' t apply " LIMIT x , y " from the
unit )
- We don ' t try to convert the target table to MyISAM
*/
class select_result_explain_buffer : public select_result_sink
{
public :
select_result_explain_buffer(THD *thd_arg, TABLE *table_arg) :
select_result_sink(thd_arg), dst_table(table_arg) {};
TABLE *dst_table; /* table to write into */
/* The following is called in the child thread: */
int send_data(List<Item> &items) override;
};
/*
This is a select_result_sink which stores the data in text form .
It is only used to save EXPLAIN output .
*/
class select_result_text_buffer : public select_result_sink
{
public :
select_result_text_buffer(THD *thd_arg): select_result_sink(thd_arg) {}
int send_data(List<Item> &items) override;
bool send_result_set_metadata(List<Item> &fields, uint flag);
void save_to(String *res);
private :
int append_row(List<Item> &items, bool send_names);
List<char *> rows;
int n_columns;
};
class select_send :public select_result {
/**
True if we have sent result set metadata to the client .
In this case the client always expects us to end the result
set with an eof or error packet
*/
bool is_result_set_started;
public :
select_send(THD *thd_arg):
select_result(thd_arg), is_result_set_started(FALSE ) {}
bool send_result_set_metadata(List<Item> &list, uint flags) override;
int send_data(List<Item> &items) override;
bool send_eof() override;
bool check_simple_select() const override { return FALSE ; }
void abort_result_set() override;
void reset_for_next_ps_execution() override;
select_result_interceptor *result_interceptor() override { return NULL; }
};
/*
We need this class , because select_send : : send_eof ( ) will call : : my_eof .
See also class Protocol_discard .
*/
class select_send_analyze : public select_send
{
bool send_result_set_metadata(List<Item> &list, uint flags) override { return 0 ; }
bool send_eof() override { return 0 ; }
void abort_result_set() override {}
public :
select_send_analyze(THD *thd_arg): select_send(thd_arg) {}
};
class select_to_file :public select_result_interceptor {
protected :
sql_exchange *exchange;
File file;
IO_CACHE cache;
ha_rows row_count;
char path[FN_REFLEN];
public :
select_to_file(THD *thd_arg, sql_exchange *ex):
select_result_interceptor(thd_arg), exchange(ex), file(-1 ),row_count(0 L)
{ path[0 ]=0 ; }
~select_to_file();
bool send_eof() override;
void abort_result_set() override;
void reset_for_next_ps_execution() override;
bool free_recources();
};
#define ESCAPE_CHARS "ntrb0ZN" // keep synchronous with READ_INFO::unescape
/*
List of all possible characters of a numeric value text representation .
*/
#define NUMERIC_CHARS ".0123456789e+-"
class select_export :public select_to_file {
uint field_term_length;
int field_sep_char,escape_char,line_sep_char;
int field_term_char; // first char of FIELDS TERMINATED BY or MAX_INT
/*
The is_ambiguous_field_sep field is true if a value of the field_sep_char
field is one of the ' n ' , ' t ' , ' r ' etc characters
( see the READ_INFO : : unescape method and the ESCAPE_CHARS constant value ) .
*/
bool is_ambiguous_field_sep;
/*
The is_ambiguous_field_term is true if field_sep_char contains the first
char of the FIELDS TERMINATED BY ( ENCLOSED BY is empty ) , and items can
contain this character .
*/
bool is_ambiguous_field_term;
/*
The is_unsafe_field_sep field is true if a value of the field_sep_char
field is one of the ' 0 ' . . ' 9 ' , ' + ' , ' - ' , ' . ' and ' e ' characters
( see the NUMERIC_CHARS constant value ) .
*/
bool is_unsafe_field_sep;
bool fixed_row_size;
CHARSET_INFO *write_cs; // output charset
public :
select_export(THD *thd_arg, sql_exchange *ex): select_to_file(thd_arg, ex) {}
~select_export();
int prepare(List<Item> &list, SELECT_LEX_UNIT *u) override;
int send_data(List<Item> &items) override;
};
class select_dump :public select_to_file {
public :
select_dump(THD *thd_arg, sql_exchange *ex): select_to_file(thd_arg, ex) {}
int prepare(List<Item> &list, SELECT_LEX_UNIT *u) override;
int send_data(List<Item> &items) override;
};
class Write_record; // defined in sql_insert.h
class select_insert :public select_result_interceptor {
public :
select_result *sel_result;
TABLE_LIST *table_list;
TABLE *table;
List<Item> *fields;
Write_record *write;
ulonglong autoinc_value_of_last_inserted_row; // autogenerated or not
COPY_INFO info;
bool insert_into_view;
select_insert(THD *thd_arg, TABLE_LIST *table_list_par, TABLE *table_par,
List<Item> *fields_par, List<Item> *update_fields,
List<Item> *update_values, enum_duplicates duplic,
bool ignore, select_result *sel_ret_list, Write_record *write);
~select_insert();
int prepare(List<Item> &list, SELECT_LEX_UNIT *u) override;
int prepare2(JOIN *join) override;
int send_data(List<Item> &items) override;
virtual bool store_values(List<Item> &values, bool *trg_skip_row);
virtual bool can_rollback_data() { return 0 ; }
bool prepare_eof(bool using_create);
bool send_ok_packet();
bool send_eof() override;
void abort_result_set() override;
/* not implemented: select_insert is never re-used in prepared statements */
void reset_for_next_ps_execution() override;
};
class select_create: public select_insert {
Table_specification_st *create_info;
TABLE_LIST *select_tables;
Alter_info *alter_info;
Field **field;
/* lock data for tmp table */
MYSQL_LOCK *m_lock;
/* m_lock or thd->extra_lock */
MYSQL_LOCK **m_plock;
bool exit_done;
TMP_TABLE_SHARE *saved_tmp_table_share;
DDL_LOG_STATE ddl_log_state_create, ddl_log_state_rm;
public :
select_create(THD *thd_arg, TABLE_LIST *table_arg,
Table_specification_st *create_info_par,
Alter_info *alter_info_arg,
List<Item> &select_fields,enum_duplicates duplic, bool ignore,
TABLE_LIST *select_tables_arg, Write_record *write):
select_insert(thd_arg, table_arg, NULL, &select_fields, 0 , 0 , duplic,
ignore, NULL, write),
create_info(create_info_par),
select_tables(select_tables_arg),
alter_info(alter_info_arg),
m_plock(NULL), exit_done(0 ),
saved_tmp_table_share(0 )
{
DBUG_ASSERT(create_info->default_table_charset);
bzero(&ddl_log_state_create, sizeof (ddl_log_state_create));
bzero(&ddl_log_state_rm, sizeof (ddl_log_state_rm));
}
int prepare(List<Item> &list, SELECT_LEX_UNIT *u) override;
bool store_values(List<Item> &values, bool *trg_skip_row) override;
bool send_eof() override;
void abort_result_set() override;
bool can_rollback_data() override { return 1 ; }
// Needed for access from local class MY_HOOKS in prepare(), since thd is protected.
const THD *get_thd(void ) { return thd; }
const HA_CREATE_INFO *get_create_info() { return create_info; };
int prepare2(JOIN *join) override { return 0 ; }
private :
TABLE *create_table_from_items(THD *thd,
List<Item> *items,
MYSQL_LOCK **lock);
int postlock(THD *thd, TABLE **tables);
};
#include <myisam.h>
#ifdef WITH_ARIA_STORAGE_ENGINE
#include <maria.h>
#else
#undef USE_ARIA_FOR_TMP_TABLES
#endif
#ifdef USE_ARIA_FOR_TMP_TABLES
#define TMP_ENGINE_COLUMNDEF MARIA_COLUMNDEF
#define TMP_ENGINE_HTON maria_hton
#define TMP_ENGINE_NAME "Aria"
inline uint tmp_table_max_key_length() { return maria_max_key_length(); }
inline uint tmp_table_max_key_parts() { return maria_max_key_segments(); }
#else
#define TMP_ENGINE_COLUMNDEF MI_COLUMNDEF
#define TMP_ENGINE_HTON myisam_hton
#define TMP_ENGINE_NAME "MyISAM"
inline uint tmp_table_max_key_length() { return MI_MAX_KEY_LENGTH; }
inline uint tmp_table_max_key_parts() { return MI_MAX_KEY_SEG; }
#endif
/*
Param to create temporary tables when doing SELECT : s
NOTE
This structure is copied using memcpy as a part of JOIN .
*/
class TMP_TABLE_PARAM :public Sql_alloc
{
public :
List<Item> copy_funcs;
Copy_field *copy_field, *copy_field_end;
uchar *group_buff;
const char *tmp_name;
Item **items_to_copy; /* Fields in tmp table */
TMP_ENGINE_COLUMNDEF *recinfo, *start_recinfo;
KEY *keyinfo;
ulong *rec_per_key;
ha_rows end_write_records;
/**
Number of normal fields in the query , including those referred to
from aggregate functions . Hence , " SELECT ` field1 ` ,
SUM ( ` field2 ` ) from t1 " sets this counter to 2 .
@ see count_field_types
*/
uint field_count;
/**
Number of fields in the query that have functions . Includes both
aggregate functions ( e . g . , SUM ) and non - aggregates ( e . g . , RAND ) .
Also counts functions referred to from aggregate functions , i . e . ,
" SELECT SUM ( RAND ( ) ) " sets this counter to 2 .
@ see count_field_types
*/
uint func_count;
/**
Number of fields in the query that have aggregate functions . Note
that the optimizer may choose to optimize away these fields by
replacing them with constants , in which case sum_func_count will
need to be updated .
@ see opt_sum_query , count_field_types
*/
uint sum_func_count;
uint copy_func_count; // Allocated copy fields
uint hidden_field_count;
uint group_parts,group_length,group_null_parts;
/*
If we ' re doing a GROUP BY operation , shows which one is used :
true TemporaryTableWithPartialSums algorithm ( see end_update ( ) ) .
false OrderedGroupBy algorithm ( see end_write_group ( ) ) .
*/
uint quick_group;
/**
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. */
bool materialized_subquery;
/* TRUE if all columns of the table are guaranteed to be non-nullable */
bool force_not_null_cols;
/*
True if GROUP BY and its aggregate functions are already computed
by a table access method ( e . g . by loose index scan ) . In this case
query execution should not perform aggregation and should treat
aggregate functions as normal functions .
*/
bool precomputed_group_by;
bool group_concat;
bool force_copy_fields;
/*
If TRUE , create_tmp_field called from create_tmp_table will convert
all BIT fields to 64 - bit longs . This is a workaround the limitation
that MEMORY tables cannot index BIT columns .
*/
bool bit_fields_as_long;
/*
Whether to create or postpone actual creation of this temporary table .
TRUE < = > create_tmp_table will create only the TABLE structure .
*/
bool skip_create_table;
TMP_TABLE_PARAM()
:copy_field(0 ), group_parts(0 ),
group_length(0 ), group_null_parts(0 ),
using_outer_summary_function(0 ),
schema_table(0 ), materialized_subquery(0 ), force_not_null_cols(0 ),
precomputed_group_by(0 ), group_concat(0 ),
force_copy_fields(0 ), bit_fields_as_long(0 ), skip_create_table(0 )
{
init();
}
~TMP_TABLE_PARAM()
{
cleanup();
}
void init(void );
inline void cleanup(void )
{
if (copy_field) /* Fix for Intel compiler */
{
delete [] copy_field;
copy_field= NULL;
copy_field_end= NULL;
}
}
};
class select_unit :public select_result_interceptor
{
protected :
uint curr_step, prev_step, curr_sel;
enum sub_select_type step;
public :
TMP_TABLE_PARAM tmp_table_param;
/* Number of additional (hidden) field of the used temporary table */
int addon_cnt;
int write_err; /* Error code from the last send_data->ha_write_row call. */
TABLE *table;
select_unit(THD *thd_arg):
select_result_interceptor(thd_arg), addon_cnt(0 ), table(0 )
{
init();
tmp_table_param.init();
}
int prepare(List<Item> &list, SELECT_LEX_UNIT *u) override;
/**
Do prepare ( ) and prepare2 ( ) if they have been postponed until
column type information is computed ( used by select_union_direct ) .
@ param types Column types
@ return false on success , true on failure
*/
virtual bool postponed_prepare(List<Item> &types)
{ return false ; }
int send_data(List<Item> &items) override;
int write_record();
int update_counter(Field *counter, longlong value);
int delete_record();
bool send_eof() override;
virtual bool flush();
void reset_for_next_ps_execution() override;
virtual bool create_result_table(THD *thd, List<Item> *column_types,
bool is_distinct, ulonglong options,
const LEX_CSTRING *alias,
bool bit_fields_as_long,
bool create_table,
bool keep_row_order,
uint hidden);
TMP_TABLE_PARAM *get_tmp_table_param() { return &tmp_table_param; }
void init()
{
curr_step= prev_step= 0 ;
curr_sel= UINT_MAX;
step= UNION_TYPE;
write_err= 0 ;
}
virtual void change_select();
virtual bool force_enable_index_if_needed() { return false ; }
};
/**
@ class select_unit_ext
The class used when processing rows produced by operands of query expressions
containing INTERSECT ALL and / or EXCEPT all operations . One or two extra fields
of the temporary to store the rows of the partial and final result can be employed .
Both of them contain counters . The second additional field is used only when
the processed query expression contains INTERSECT ALL .
Consider how these extra fields are used .
Let
table t1 ( f char ( 8 ) )
table t2 ( f char ( 8 ) )
table t3 ( f char ( 8 ) )
contain the following sets :
( " b " ) , ( " a " ) , ( " d " ) , ( " c " ) , ( " b " ) , ( " a " ) , ( " c " ) , ( " a " )
( " c " ) , ( " b " ) , ( " c " ) , ( " c " ) , ( " a " ) , ( " b " ) , ( " g " )
( " c " ) , ( " a " ) , ( " b " ) , ( " d " ) , ( " b " ) , ( " e " )
- Let ' s demonstrate how the set operation INTERSECT ALL is proceesed
for the query
SELECT f FROM t1 INTERSECT ALL SELECT f FROM t2
When send_data ( ) is called for the rows of the first operand we put
the processed record into the temporary table if there was no such record
setting dup_cnt field to 1 and add_cnt field to 0 and increment the
counter in the dup_cnt field by one otherwise . We get
| add_cnt | dup_cnt | f |
| 0 | 2 | b |
| 0 | 3 | a |
| 0 | 1 | d |
| 0 | 2 | c |
The call of send_eof ( ) for the first operand swaps the values stored in
dup_cnt and add_cnt . After this , we ' ll see the following rows in the
temporary table
| add_cnt | dup_cnt | f |
| 2 | 0 | b |
| 3 | 0 | a |
| 1 | 0 | d |
| 2 | 0 | c |
When send_data ( ) is called for the rows of the second operand we increment
the counter in dup_cnt if the processed row is found in the table and do
nothing otherwise . As a result we get
| add_cnt | dup_cnt | f |
| 2 | 2 | b |
| 3 | 1 | a |
| 1 | 0 | d |
| 2 | 3 | c |
At the call of send_eof ( ) for the second operand first we disable index .
Then for each record , the minimum of counters from dup_cnt and add_cnt m is
taken . If m = = 0 then the record is deleted . Otherwise record is replaced
with m copies of it . Yet the counter in this copies are set to 1 for
dup_cnt and to 0 for add_cnt
| add_cnt | dup_cnt | f |
| 0 | 1 | b |
| 0 | 1 | b |
| 0 | 1 | a |
| 0 | 1 | c |
| 0 | 1 | c |
- Let ' s demonstrate how the set operation EXCEPT ALL is proceesed
for the query
SELECT f FROM t1 EXCEPT ALL SELECT f FROM t3
Only one additional counter field dup_cnt is used for EXCEPT ALL .
After the first operand has been processed we have in the temporary table
| dup_cnt | f |
| 2 | b |
| 3 | a |
| 1 | d |
| 2 | c |
When send_data ( ) is called for the rows of the second operand we decrement
the counter in dup_cnt if the processed row is found in the table and do
nothing otherwise . If the counter becomes 0 we delete the record
| dup_cnt | f |
| 2 | a |
| 1 | c |
Finally at the call of send_eof ( ) for the second operand we disable index
unfold rows adding duplicates
| dup_cnt | f |
| 1 | a |
| 1 | a |
| 1 | c |
*/
class select_unit_ext :public select_unit
{
public :
select_unit_ext(THD *thd_arg):
select_unit(thd_arg), increment(0 ), is_index_enabled(TRUE ),
curr_op_type(UNSPECIFIED)
{
};
int send_data(List<Item> &items) override;
void change_select() override;
int unfold_record(ha_rows cnt);
bool send_eof() override;
bool force_enable_index_if_needed() override
{
is_index_enabled= true ;
return true ;
}
bool disable_index_if_needed(SELECT_LEX *curr_sl);
/*
How to change increment / decrement the counter in duplicate_cnt field
when processing a record produced by the current operand in send_data ( ) .
The value can be 1 or - 1
*/
int increment;
/* TRUE <=> the index of the result temporary table is enabled */
bool is_index_enabled;
/* The type of the set operation currently executed */
enum set_op_type curr_op_type;
/*
Points to the extra field of the temporary table where
duplicate counters are stored
*/
Field *duplicate_cnt;
/*
Points to the extra field of the temporary table where additional
counters used only for INTERSECT ALL operations are stored
*/
Field *additional_cnt;
};
class select_union_recursive :public select_unit
{
public :
/* The temporary table with the new records generated by one iterative step */
TABLE *incr_table;
/* The TMP_TABLE_PARAM structure used to create incr_table */
TMP_TABLE_PARAM incr_table_param;
/* One of tables from the list rec_tables (determined dynamically) */
TABLE *first_rec_table_to_update;
/*
The list of all recursive table references to the CTE for whose
specification this select_union_recursive was created
*/
List<TABLE_LIST> rec_table_refs;
/*
The count of how many times reset_for_next_ps_execution ( ) 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;
long row_counter;
select_union_recursive(THD *thd_arg):
select_unit(thd_arg),
incr_table(0 ), first_rec_table_to_update(0 ), cleanup_count(0 ),
row_counter(0 )
{ incr_table_param.init(); };
int send_data(List<Item> &items) override;
bool create_result_table(THD *thd, List<Item> *column_types,
bool is_distinct, ulonglong options,
const LEX_CSTRING *alias,
bool bit_fields_as_long,
bool create_table,
bool keep_row_order,
uint hidden) override;
void reset_for_next_ps_execution() override;
};
/**
UNION result that is passed directly to the receiving select_result
without filling a temporary table .
Function calls are forwarded to the wrapped select_result , but some
functions are expected to be called only once for each query , so
they are only executed for the first SELECT in the union ( except
for send_eof ( ) , which is executed only for the last SELECT ) .
This select_result is used when a UNION is not DISTINCT and doesn ' t
have a global ORDER BY clause . @ see st_select_lex_unit : : prepare ( ) .
*/
class select_union_direct :public select_unit
{
private :
/* Result object that receives all rows */
select_result *result;
/* The last SELECT_LEX of the union */
SELECT_LEX *last_select_lex;
/* Wrapped result has received metadata */
bool done_send_result_set_metadata;
/* Wrapped result has initialized tables */
bool done_initialize_tables;
/* Accumulated limit_found_rows */
ulonglong limit_found_rows;
/* Number of rows offset */
ha_rows offset;
/* Number of rows limit + offset, @see select_union_direct::send_data() */
ha_rows limit;
public :
/* Number of rows in the union */
ha_rows send_records;
select_union_direct(THD *thd_arg, select_result *result_arg,
SELECT_LEX *last_select_lex_arg):
select_unit(thd_arg), result(result_arg),
last_select_lex(last_select_lex_arg),
done_send_result_set_metadata(false ), done_initialize_tables(false ),
limit_found_rows(0 )
{ send_records= 0 ; }
bool change_result(select_result *new_result) override;
uint field_count(List<Item> &fields) const override
{
// Only called for top-level select_results, usually select_send
DBUG_ASSERT(false ); /* purecov: inspected */
return 0 ; /* purecov: inspected */
}
bool postponed_prepare(List<Item> &types) override;
bool send_result_set_metadata(List<Item> &list, uint flags) override;
int send_data(List<Item> &items) override;
bool initialize_tables (JOIN *join) override;
bool send_eof() override;
bool flush() override { return false ; }
bool check_simple_select() const override
{
/* Only called for top-level select_results, usually select_send */
DBUG_ASSERT(false ); /* purecov: inspected */
return false ; /* purecov: inspected */
}
void abort_result_set() override
{
result->abort_result_set(); /* purecov: inspected */
}
void reset_for_next_ps_execution() override
{
send_records= 0 ;
}
void set_thd(THD *thd_arg)
{
/*
Only called for top - level select_results , usually select_send ,
and for the results of subquery engines
( select_ < something > _ subselect ) .
*/
DBUG_ASSERT(false ); /* purecov: inspected */
}
void remove_offset_limit()
{
// EXPLAIN should never output to a select_union_direct
DBUG_ASSERT(false ); /* purecov: inspected */
}
#ifdef EMBEDDED_LIBRARY
void begin_dataset() override
#else
void begin_dataset()
#endif
{
// Only called for sp_cursor::Select_fetch_into_spvars
DBUG_ASSERT(false ); /* purecov: inspected */
}
};
/* Base subselect interface class */
class select_subselect :public select_result_interceptor
{
protected :
Item_subselect *item;
public :
select_subselect(THD *thd_arg, Item_subselect *item_arg):
select_result_interceptor(thd_arg), item(item_arg) {}
int send_data(List<Item> &items) override=0 ;
bool send_eof() override { return 0 ; };
};
/* Single value subselect interface class */
class select_singlerow_subselect :public select_subselect
{
public :
select_singlerow_subselect(THD *thd_arg, Item_subselect *item_arg):
select_subselect(thd_arg, item_arg)
{}
int send_data(List<Item> &items) override;
};
/*
This class specializes select_union to collect statistics about the
data stored in the temp table . Currently the class collects statistics
about NULLs .
*/
class select_materialize_with_stats : public select_unit
{
protected :
class Column_statistics
{
public :
/* Count of NULLs per column. */
ha_rows null_count;
/* The row number that contains the first NULL in a column. */
ha_rows min_null_row;
/* The row number that contains the last NULL in a column. */
ha_rows max_null_row;
};
/* Array of statistics data per column. */
Column_statistics* col_stat;
/*
The number of columns in the biggest sub - row that consists of only
NULL values .
*/
uint max_nulls_in_row;
/*
Count of rows written to the temp table . This is redundant as it is
already stored in handler : : stats . records , however that one is relatively
expensive to compute ( given we need that for every row ) .
*/
ha_rows count_rows;
protected :
void reset();
public :
select_materialize_with_stats(THD *thd_arg): select_unit(thd_arg)
{ tmp_table_param.init(); }
bool create_result_table(THD *thd, List<Item> *column_types,
bool is_distinct, ulonglong options,
const LEX_CSTRING *alias,
bool bit_fields_as_long,
bool create_table,
bool keep_row_order,
uint hidden) override;
bool init_result_table(ulonglong select_options);
int send_data(List<Item> &items) override;
void reset_for_next_ps_execution() override;
ha_rows get_null_count_of_col(uint idx)
{
DBUG_ASSERT(idx < table->s->fields);
return col_stat[idx].null_count;
}
ha_rows get_max_null_of_col(uint idx)
{
DBUG_ASSERT(idx < table->s->fields);
return col_stat[idx].max_null_row;
}
ha_rows get_min_null_of_col(uint idx)
{
DBUG_ASSERT(idx < table->s->fields);
return col_stat[idx].min_null_row;
}
uint get_max_nulls_in_row() { return max_nulls_in_row; }
};
/* used in independent ALL/ANY optimisation */
class select_max_min_finder_subselect :public select_subselect
{
Item_cache *cache;
bool (select_max_min_finder_subselect::*op)();
bool fmax;
bool is_all;
void set_op(const Type_handler *ha);
public :
select_max_min_finder_subselect(THD *thd_arg, Item_subselect *item_arg,
bool mx, bool all):
select_subselect(thd_arg, item_arg), cache(0 ), fmax(mx), is_all(all)
{}
void reset_for_next_ps_execution() override;
int send_data(List<Item> &items) override;
bool cmp_real();
bool cmp_int();
bool cmp_decimal();
bool cmp_str();
bool cmp_time();
bool cmp_native();
};
/* EXISTS subselect interface class */
class select_exists_subselect :public select_subselect
{
public :
select_exists_subselect(THD *thd_arg, Item_subselect *item_arg):
select_subselect(thd_arg, item_arg) {}
int send_data(List<Item> &items) override;
};
/*
Optimizer and executor structure for the materialized semi - join info . This
structure contains
- The sj - materialization temporary table
- Members needed to make index lookup or a full scan of the temptable .
*/
class POSITION;
class SJ_MATERIALIZATION_INFO : public Sql_alloc
{
public :
/* Optimal join sub-order */
POSITION *positions;
uint tables; /* Number of tables in the sj-nest */
/* Number of rows in the materialized table, before the de-duplication */
double rows_with_duplicates;
/* Expected #rows in the materialized table, after de-duplication */
double rows;
/*
Cost to materialize - execute the sub - join and write rows into temp . table
*/
double materialization_cost;
/* Cost to make one lookup in the temptable */
double lookup_cost;
/* Cost of scanning the materialized table */
double scan_cost;
/* --- Execution structures ---------- */
/*
TRUE < = > This structure is used for execution . We don ' t necessarily pick
sj - materialization , so some of SJ_MATERIALIZATION_INFO structures are not
used by materialization
*/
bool is_used;
bool materialized; /* TRUE <=> materialization already performed */
/*
TRUE - the temptable is read with full scan
FALSE - we use the temptable for index lookups
*/
bool is_sj_scan;
/* The temptable and its related info */
TMP_TABLE_PARAM sjm_table_param;
List<Item> sjm_table_cols;
TABLE *table;
/* Structure used to make index lookups */
struct st_table_ref *tab_ref;
Item *in_equality; /* See create_subq_in_equalities() */
Item *join_cond; /* See comments in make_join_select() */
Copy_field *copy_field; /* Needed for SJ_Materialization scan */
};
/* Structs used when sorting */
struct SORT_FIELD_ATTR
{
/*
If using mem - comparable fixed - size keys :
length of the mem - comparable image of the field , in bytes .
If using packed keys : still the same ? Not clear what is the use of it .
*/
uint length;
/*
For most datatypes , this is 0 .
The exception are the VARBINARY columns .
For those columns , the comparison actually compares
( value_prefix ( N ) , suffix = length ( value ) )
Here value_prefix is either the whole value or its prefix if it was too
long , and the suffix is the length of the original value .
( this way , for values X and Y : if X = prefix ( Y ) then X compares as less
than Y
*/
uint suffix_length;
/*
If using packed keys , number of bytes that are used to store the length
of the packed key .
*/
uint length_bytes;
/* Max. length of the original value, in bytes */
uint original_length;
enum Type { FIXED_SIZE, VARIABLE_SIZE } type;
/*
TRUE : if the item or field is NULLABLE
FALSE : otherwise
*/
bool maybe_null;
CHARSET_INFO *cs;
uint pack_sort_string(uchar *to, const Binary_string *str,
CHARSET_INFO *cs) const ;
int compare_packed_fixed_size_vals(const uchar *a, size_t *a_len,
const uchar *b, size_t *b_len);
int compare_packed_varstrings(const uchar *a, size_t *a_len,
const uchar *b, size_t *b_len);
bool check_if_packing_possible(THD *thd) const ;
bool is_variable_sized() { return type == VARIABLE_SIZE; }
void set_length_and_original_length(THD *thd, uint length_arg);
};
struct SORT_FIELD: public SORT_FIELD_ATTR
{
Field *field; /* Field to sort */
Item *item; /* Item if not sorting fields */
bool reverse; /* if descending sort */
};
typedef struct st_sort_buffer {
uint index; /* 0 or 1 */
uint sort_orders;
uint change_pos; /* If sort-fields changed */
char **buff;
SORT_FIELD *sortorder;
} SORT_BUFFER;
/* Structure for db & table in sql_yacc */
class Table_ident :public Sql_alloc
{
public :
LEX_CSTRING db;
LEX_CSTRING table;
SELECT_LEX_UNIT *sel;
inline Table_ident(THD *thd, const LEX_CSTRING *db_arg,
const LEX_CSTRING *table_arg,
bool force)
:table(*table_arg), sel((SELECT_LEX_UNIT *)0 )
{
if (!force && (thd->client_capabilities & CLIENT_NO_SCHEMA))
db= null_clex_str;
else
db= *db_arg;
}
inline Table_ident(const LEX_CSTRING *table_arg)
:table(*table_arg), sel((SELECT_LEX_UNIT *)0 )
{
db= null_clex_str;
}
/*
This constructor is used only for the case when we create a derived
table . A derived table has no name and doesn ' t belong to any database .
Later , if there was an alias specified for the table , it will be set
by add_table_to_list .
*/
inline Table_ident(SELECT_LEX_UNIT *s) : sel(s)
{
/* We must have a table name here as this is used with add_table_to_list */
db.str= empty_c_string;
db.length= 0 ;
table.str= internal_table_name;
table.length=1 ;
}
bool is_derived_table() const { return MY_TEST(sel); }
inline void change_db(LEX_CSTRING *db_name)
{
db= *db_name;
}
bool resolve_table_rowtype_ref(THD *thd, Row_definition_list &defs);
bool append_to(THD *thd, String *to) const ;
/*
Convert Table_ident : : m_db to a valid internal database name :
- validated with Lex_ident_db : : check_name ( )
- optionally lower - cased when lower_case_table_names = = 1
@ param arena - the arena to allocate the lower - cased copy on , when needed .
@ return { NULL , 0 } in case of EOM or invalid database name ,
or a good identifier otherwise .
*/
Lex_ident_db to_ident_db_internal_with_error(Query_arena *arena) const ;
};
class Qualified_column_ident: public Table_ident
{
public :
const Lex_ident_column m_column;
public :
Qualified_column_ident(const LEX_CSTRING *column)
:Table_ident(&null_clex_str),
m_column(*column)
{ }
Qualified_column_ident(const LEX_CSTRING *table, const LEX_CSTRING *column)
:Table_ident(table),
m_column(*column)
{ }
Qualified_column_ident(THD *thd,
const LEX_CSTRING *db,
const LEX_CSTRING *table,
const LEX_CSTRING *column)
:Table_ident(thd, db, table, false ),
m_column(*column)
{ }
bool resolve_type_ref(THD *thd, Column_definition *def) const ;
bool append_to(THD *thd, String *to) const ;
};
class Qualified_ident: public Sql_alloc
{
protected :
Lex_ident_cli_st m_pos;
Lex_ident_sys m_parts[3 ];
sp_variable *m_spvar;
uint m_defined_parts;
public :
Qualified_ident(THD *thd, const Lex_ident_cli_st &a);
Qualified_ident(THD *thd,
const Lex_ident_cli_st &a,
const Lex_ident_cli_st &b);
Qualified_ident(THD *thd,
const Lex_ident_cli_st &a,
const Lex_ident_cli_st &b,
const Lex_ident_cli_st &c);
Qualified_ident(const Lex_ident_sys_st &a);
/*
Returns the client query fragment starting at the first character
of the leftmost identifier and ending at after the last character
of the rightmist identifier .
*/
const Lex_ident_cli_st &pos() const
{
return m_pos;
}
sp_variable *spvar() const
{
return m_spvar;
}
const Lex_ident_sys &part(uint i) const
{
DBUG_ASSERT(i < array_elements(m_parts));
return m_parts[i];
}
uint defined_parts() const
{
return m_defined_parts;
}
/*
When initializing m_parts [ n ] in the constructor , a EOM could happen .
This method checks that all defined parts were initialized without error .
*/
bool is_sane() const
{
for (uint i= 0 ; i < m_defined_parts; i++)
{
if (m_parts[i].is_null())
return false ; // E.g. EOM happened in the constructor
}
return true ;
}
void set_pos(const Lex_ident_cli_st &pos)
{
m_pos= pos;
}
void set_spvar(sp_variable *spvar)
{
m_spvar= spvar;
}
};
// this is needed for user_vars hash
class user_var_entry: public Type_handler_hybrid_field_type
{
CHARSET_INFO *m_charset;
public :
user_var_entry() = default ; /* Remove gcc warning */
LEX_CSTRING name;
char *value;
size_t length;
query_id_t update_query_id, used_query_id;
double val_real(bool *null_value);
longlong val_int(bool *null_value) const ;
String *val_str(bool *null_value, String *str, uint decimals) const ;
my_decimal *val_decimal(bool *null_value, my_decimal *result);
CHARSET_INFO *charset() const { return m_charset; }
void set_charset(CHARSET_INFO *cs) { m_charset= cs; }
};
user_var_entry *get_variable(HASH *hash, LEX_CSTRING *name,
bool create_if_not_exists);
class SORT_INFO;
class multi_delete :public select_result_interceptor
{
TABLE_LIST *delete_tables, *table_being_deleted;
TMP_TABLE_PARAM *tmp_table_param;
TABLE **tmp_tables, *main_table;
ha_rows deleted, found;
uint table_count;
int error;
bool do_delete;
/* True if at least one table we delete from is transactional */
bool transactional_tables;
/* True if at least one table we delete from is not transactional */
bool normal_tables;
bool delete_while_scanning;
/*
error handling ( rollback and binlogging ) can happen in send_eof ( )
so that afterward abort_result_set ( ) needs to find out that .
*/
bool error_handled;
public :
// Methods used by ColumnStore
uint get_num_of_tables() const { return table_count; }
TABLE_LIST* get_tables() const { return delete_tables; }
public :
multi_delete(THD *thd_arg, TABLE_LIST *dt, uint num_of_tables);
~multi_delete();
int prepare(List<Item> &list, SELECT_LEX_UNIT *u) override;
int prepare2(JOIN *join) override;
int send_data(List<Item> &items) override;
bool initialize_tables (JOIN *join) override;
int do_deletes();
int rowid_table_deletes(TABLE *table, bool ignore);
int do_table_deletes(TABLE *table, SORT_INFO *sort_info, bool ignore);
bool send_eof() override;
inline ha_rows num_deleted() const { return deleted; }
void abort_result_set() override;
void prepare_to_read_rows() override;
};
class multi_update :public select_result_interceptor
{
TABLE_LIST *all_tables; /* query/update command tables */
List<TABLE_LIST> *leaves; /* list of leaves of join table tree */
List<TABLE_LIST> updated_leaves; /* a superset of tables which will be updated */
List<TABLE_LIST> update_targets; /* the tables that will be UPDATE'd */
TABLE_LIST *update_tables;
TABLE **tmp_tables, *main_table, *table_to_update;
TMP_TABLE_PARAM *tmp_table_param;
ha_rows updated, found;
List <Item> *fields, *values;
List <Item> **fields_for_table, **values_for_table;
uint table_count;
/*
List of tables referenced in the CHECK OPTION condition of
the updated view excluding the updated table .
*/
List <TABLE> unupdated_check_opt_tables;
Copy_field *copy_field;
enum enum_duplicates handle_duplicates;
bool do_update, trans_safe;
/* True if the update operation has made a change in a transactional table */
bool transactional_tables;
bool ignore;
/*
error handling ( rollback and binlogging ) can happen in send_eof ( )
so that afterward abort_result_set ( ) needs to find out that .
*/
bool error_handled;
/* Need this to protect against multiple prepare() calls */
bool prepared;
// For System Versioning (may need to insert new fields to a table).
ha_rows updated_sys_ver;
bool has_vers_fields;
const table_map tables_to_update;
public :
multi_update(THD *thd_arg, TABLE_LIST *ut, List<TABLE_LIST> *leaves_list,
List<Item> *fields, List<Item> *values,
enum_duplicates handle_duplicates, bool ignore);
~multi_update();
bool init(THD *thd);
bool init_for_single_table(THD *thd);
int prepare(List<Item> &list, SELECT_LEX_UNIT *u) override;
int send_data(List<Item> &items) override;
bool initialize_tables (JOIN *join) override;
int prepare2(JOIN *join) override;
int do_updates();
bool send_eof() override;
inline ha_rows num_found() const { return found; }
inline ha_rows num_updated() const { return updated; }
inline void set_found (ha_rows n) { found= n; }
inline void set_updated (ha_rows n) { updated= n; }
virtual void abort_result_set() override;
void update_used_tables() override;
void prepare_to_read_rows() override;
};
class my_var_sp;
class my_var : public Sql_alloc {
public :
const LEX_CSTRING name;
enum type { SESSION_VAR, LOCAL_VAR, PARAM_VAR };
type scope;
my_var(const LEX_CSTRING *j, enum type s) : name(*j), scope(s) { }
virtual ~my_var() = default ;
virtual bool set(THD *thd, Item *val) = 0 ;
virtual my_var_sp *get_my_var_sp() { return NULL; }
};
class my_var_sp: public my_var,
public sp_rcontext_addr
{
const Type_handler *m_type_handler;
public :
/*
Routine to which this Item_splocal belongs . Used for checking if correct
runtime context is used for variable handling .
*/
sp_head *sp;
my_var_sp(const Lex_ident_sys_st &name, const sp_rcontext_addr &addr,
const Type_handler *type_handler, sp_head *s)
: my_var(&name, LOCAL_VAR),
sp_rcontext_addr(addr), m_type_handler(type_handler), sp(s) { }
~my_var_sp() = default ;
bool set(THD *thd, Item *val) override;
virtual bool set_row(THD *thd, List<Item> &select_list)
{
DBUG_ASSERT(0 );
return set(thd, select_list.head());
}
my_var_sp *get_my_var_sp() override { return this ; }
const Type_handler *type_handler() const
{ return m_type_handler; }
sp_rcontext *get_rcontext(sp_rcontext *local_ctx) const ;
/*
Check if the value list is compatible with the INTO variable .
This method is called if there is only one variable in the INTO list , e . g . :
SELECT < select list > INTO spvar_varchar ; - - scalar variable
*/
virtual bool check_assignability(THD *thd,
const List<Item> &select_list,
bool *assign_as_row) const
{
// The ROW data type has its own my_var_sp. See sql_type_row.cc.
DBUG_ASSERT(type_handler() != &type_handler_row);
/*
If the variable is not scalar ( and it ' s also known not to be ROW ) ,
then it ' s not compatible with the select list , because Items
in the select list can only be scalar .
*/
*assign_as_row= false ;
return select_list.elements != 1 ||
!type_handler()->is_scalar_type() /*e.g. assoc array */;
}
};
class my_var_user: public my_var {
public :
my_var_user(const LEX_CSTRING *j)
: my_var(j, SESSION_VAR) { }
~my_var_user() = default ;
bool set(THD *thd, Item *val) override;
};
class select_dumpvar :public select_result_interceptor {
ha_rows row_count;
my_var_sp *m_var_sp_row; // Not NULL if SELECT INTO row_type_sp_variable
bool send_data_to_var_list(List<Item> &items);
public :
List<my_var> var_list;
select_dumpvar(THD *thd_arg)
:select_result_interceptor(thd_arg), row_count(0 ), m_var_sp_row(NULL)
{ var_list.empty(); }
~select_dumpvar() = default ;
int prepare(List<Item> &list, SELECT_LEX_UNIT *u) override;
int send_data(List<Item> &items) override;
bool send_eof() override;
bool check_simple_select() const override;
void reset_for_next_ps_execution() override;
};
/* Bits in server_command_flags */
/**
Skip the increase of the global query id counter . Commonly set for
commands that are stateless ( won ' t cause any change on the server
internal states ) .
*/
#define CF_SKIP_QUERY_ID (1 U << 0 )
/**
Skip the increase of the number of statements that clients have
sent to the server . Commonly used for commands that will cause
a statement to be executed but the statement might have not been
sent by the user ( ie : stored procedure ) .
*/
#define CF_SKIP_QUESTIONS (1 U << 1 )
#ifdef WITH_WSREP
/**
Do not check that wsrep snapshot is ready before allowing this command
*/
#define CF_SKIP_WSREP_CHECK (1 U << 2 )
#else
#define CF_SKIP_WSREP_CHECK 0
#endif /* WITH_WSREP */
/* Inline functions */
inline bool add_item_to_list(THD *thd, Item *item)
{
bool res;
LEX *lex= thd->lex;
if (lex->current_select->parsing_place == IN_RETURNING)
res= lex->returning()->add_item_to_list(thd, item);
else
res= lex->current_select->add_item_to_list(thd, item);
return res;
}
inline bool add_value_to_list(THD *thd, Item *value)
{
return thd->lex->value_list.push_back(value, thd->mem_root);
}
inline bool add_order_to_list(THD *thd, Item *item, bool asc)
{
return thd->lex->current_select->add_order_to_list(thd, item, asc);
}
inline bool add_gorder_to_list(THD *thd, Item *item, bool asc)
{
return thd->lex->current_select->add_gorder_to_list(thd, item, asc);
}
inline bool add_group_to_list(THD *thd, Item *item, bool asc)
{
return thd->lex->current_select->add_group_to_list(thd, item, asc);
}
inline Item *and_conds(THD *thd, Item *a, Item *b)
{
if (!b) return a;
if (!a) return b;
return new (thd->mem_root) Item_cond_and(thd, a, b);
}
/* inline handler methods that need to know TABLE and THD structures */
inline void handler::increment_statistics(ulong SSV::*offset) const
{
status_var_increment(table->in_use->status_var.*offset);
table->in_use->check_limit_rows_examined();
}
inline void handler::fast_increment_statistics(ulong SSV::*offset) const
{
status_var_increment(table->in_use->status_var.*offset);
}
inline void handler::decrement_statistics(ulong SSV::*offset) const
{
status_var_decrement(table->in_use->status_var.*offset);
}
/* Update references in the handler to the table */
inline void handler::set_table(TABLE* table_arg)
{
table= table_arg;
costs= &table_arg->s->optimizer_costs;
}
inline bool handler::pk_is_clustering_key(uint index) const
{
/*
We have to check for MAX_INDEX as table - > s - > primary_key can be
MAX_KEY in the case where there is no primary key .
*/
return index != MAX_KEY && is_clustering_key(index);
}
inline bool handler::is_clustering_key(uint index) const
{
DBUG_ASSERT(index != MAX_KEY);
return table->is_clustering_key(index);
}
inline int handler::ha_ft_read(uchar *buf)
{
int error= ft_read(buf);
if (!error)
{
update_rows_read();
if (table->vfield && buf == table->record[0 ])
table->update_virtual_fields(this , VCOL_UPDATE_FOR_READ);
}
table->status=error ? STATUS_NOT_FOUND: 0 ;
return error;
}
inline int handler::ha_rnd_pos_by_record(uchar *buf)
{
int error= rnd_pos_by_record(buf);
table->status=error ? STATUS_NOT_FOUND: 0 ;
return error;
}
inline int handler::ha_read_first_row(uchar *buf, uint primary_key)
{
int error= read_first_row(buf, primary_key);
if (!error)
update_rows_read();
table->status=error ? STATUS_NOT_FOUND: 0 ;
return error;
}
inline int handler::ha_write_tmp_row(uchar *buf)
{
int error;
MYSQL_INSERT_ROW_START(table_share->db.str, table_share->table_name.str);
increment_statistics(&SSV::ha_tmp_write_count);
TABLE_IO_WAIT(tracker, PSI_TABLE_WRITE_ROW, MAX_KEY, error,
{ error= write_row(buf); })
MYSQL_INSERT_ROW_DONE(error);
return error;
}
inline int handler::ha_delete_tmp_row(uchar *buf)
{
int error;
MYSQL_DELETE_ROW_START(table_share->db.str, table_share->table_name.str);
increment_statistics(&SSV::ha_tmp_delete_count);
TABLE_IO_WAIT(tracker, PSI_TABLE_DELETE_ROW, MAX_KEY, error,
{ error= delete_row(buf); })
MYSQL_DELETE_ROW_DONE(error);
return error;
}
inline int handler::ha_update_tmp_row(const uchar *old_data, uchar *new_data)
{
int error;
MYSQL_UPDATE_ROW_START(table_share->db.str, table_share->table_name.str);
increment_statistics(&SSV::ha_tmp_update_count);
TABLE_IO_WAIT(tracker, PSI_TABLE_UPDATE_ROW, active_index, error,
{ error= update_row(old_data, new_data);})
MYSQL_UPDATE_ROW_DONE(error);
return error;
}
inline bool handler::has_long_unique()
{
return table->s->long_unique_table;
}
/**
Return whether the handler is root .
@ return false if table is maintained by different handlerton , true otherwise .
@ note The implementation supposes that the same handler can ' t be found as both
root and non - root .
There are two known cases when it ' s non - root :
1 . under partition ' s ha_write_row ( ) ( also true for copy_partitions ( ) )
2 . under ha_mroonga : : wrapper_write_row ( ) ;
same applies for ha_delete_row / ha_update_row .
*/
inline bool handler::is_root_handler() const
{
return ht == table->file->ht;
}
extern pthread_attr_t *get_connection_attrib(void );
/**
Set thread entering a condition
This function should be called before putting a thread to wait for
a condition . @ a mutex should be held before calling this
function . After being waken up , @ f thd_exit_cond should be called .
@ param thd The thread entering the condition , NULL means current thread
@ param cond The condition the thread is going to wait for
@ param mutex The mutex associated with the condition , this must be
held before call this function
@ param stage The new process message for the thread
@ param old_stage The old process message for the thread
@ param src_function The caller source function name
@ param src_file The caller source file name
@ param src_line The caller source line number
*/
void thd_enter_cond(MYSQL_THD thd, mysql_cond_t *cond, mysql_mutex_t *mutex,
const PSI_stage_info *stage, PSI_stage_info *old_stage,
const char *src_function, const char *src_file,
int src_line);
#define THD_ENTER_COND(P1, P2, P3, P4, P5) \
thd_enter_cond(P1, P2, P3, P4, P5, __func__, __FILE__, __LINE__)
/**
Set thread leaving a condition
This function should be called after a thread being waken up for a
condition .
@ param thd The thread entering the condition , NULL means current thread
@ param stage The process message , ususally this should be the old process
message before calling @ f thd_enter_cond
@ param src_function The caller source function name
@ param src_file The caller source file name
@ param src_line The caller source line number
*/
void thd_exit_cond(MYSQL_THD thd, const PSI_stage_info *stage,
const char *src_function, const char *src_file,
int src_line);
#define THD_EXIT_COND(P1, P2) \
thd_exit_cond(P1, P2, __func__, __FILE__, __LINE__)
/**
@ def ENTER_COND ( C , M , S , O )
Start a wait on a condition .
@ param C the condition to wait on
@ param M the associated mutex
@ param S the new stage to enter
@ param O the previous stage
@ sa EXIT_COND ( ) .
*/
#define ENTER_COND(C, M, S, O) enter_cond(C, M, S, O, __func__, __FILE__, __LINE__)
/**
@ def EXIT_COND ( S )
End a wait on a condition
@ param S the new stage to enter
*/
#define EXIT_COND(S) exit_cond(S, __func__, __FILE__, __LINE__)
inline bool binlog_should_compress(size_t len)
{
return opt_bin_log_compress &&
len >= opt_bin_log_compress_min_len;
}
void binlog_prepare_row_images(TABLE* table,
enum_binlog_row_image row_image);
/**
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; }
protected :
THD *thd;
sql_mode_t old_mode; // SQL mode saved at construction time.
};
/*
Save the current sql_mode . Switch off sql_mode flags which can prevent
normal parsing of VIEWs , expressions in generated columns .
Restore the old sql_mode on destructor .
*/
class Sql_mode_save_for_frm_handling: public Sql_mode_save
{
public :
Sql_mode_save_for_frm_handling(THD *thd)
:Sql_mode_save(thd)
{
if (thd->variables.sql_mode & MODE_ORACLE)
thd->variables.sql_mode|= IS_OR_WAS_ORACLE;
/*
- MODE_REAL_AS_FLOAT affect only CREATE TABLE parsing
+ MODE_PIPES_AS_CONCAT affect expression parsing
+ MODE_ANSI_QUOTES affect expression parsing
+ MODE_IGNORE_SPACE affect expression parsing
- MODE_IGNORE_BAD_TABLE_OPTIONS affect only CREATE / ALTER TABLE parsing
* MODE_ONLY_FULL_GROUP_BY affect execution
* MODE_NO_UNSIGNED_SUBTRACTION affect execution
- MODE_NO_DIR_IN_CREATE affect table creation only
- MODE_POSTGRESQL compounded from other modes
+ MODE_ORACLE affects Item creation ( e . g for CONCAT )
- MODE_MSSQL compounded from other modes
- MODE_DB2 compounded from other modes
- MODE_MAXDB affect only CREATE TABLE parsing
- MODE_NO_KEY_OPTIONS affect only SHOW
- MODE_NO_TABLE_OPTIONS affect only SHOW
- MODE_NO_FIELD_OPTIONS affect only SHOW
- MODE_MYSQL323 affect only SHOW
- MODE_MYSQL40 affect only SHOW
- MODE_ANSI compounded from other modes
( + transaction mode )
? MODE_NO_AUTO_VALUE_ON_ZERO affect UPDATEs
+ MODE_NO_BACKSLASH_ESCAPES affect expression parsing
+ MODE_EMPTY_STRING_IS_NULL affect expression parsing
*/
thd->variables.sql_mode&= ~(MODE_PIPES_AS_CONCAT | MODE_ANSI_QUOTES |
MODE_IGNORE_SPACE | MODE_NO_BACKSLASH_ESCAPES |
MODE_ORACLE | MODE_EMPTY_STRING_IS_NULL);
};
~Sql_mode_save_for_frm_handling()
{
if (thd->variables.sql_mode & IS_OR_WAS_ORACLE)
thd->variables.sql_mode&= ~IS_OR_WAS_ORACLE;
}
};
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;
};
class Sql_mode_instant_set: public Sql_mode_save
{
public :
Sql_mode_instant_set(THD *thd, sql_mode_t temporary_value)
:Sql_mode_save(thd)
{
thd->variables.sql_mode= temporary_value;
}
};
class Sql_mode_instant_remove: public Sql_mode_save
{
public :
Sql_mode_instant_remove(THD *thd, sql_mode_t temporary_remove_flags)
:Sql_mode_save(thd)
{
thd->variables.sql_mode&= ~temporary_remove_flags;
}
};
class Abort_on_warning_instant_set
{
THD *m_thd;
bool m_save_abort_on_warning;
public :
Abort_on_warning_instant_set(THD *thd, bool temporary_value)
:m_thd(thd), m_save_abort_on_warning(thd->abort_on_warning)
{
thd->abort_on_warning= temporary_value;
}
~Abort_on_warning_instant_set()
{
m_thd->abort_on_warning= m_save_abort_on_warning;
}
};
class Check_level_instant_set
{
THD *m_thd;
enum_check_fields m_check_level;
public :
Check_level_instant_set(THD *thd, enum_check_fields temporary_value)
:m_thd(thd), m_check_level(thd->count_cuted_fields)
{
thd->count_cuted_fields= temporary_value;
}
~Check_level_instant_set()
{
m_thd->count_cuted_fields= m_check_level;
}
};
class Use_relaxed_field_copy: public Sql_mode_save,
public Check_level_instant_set,
public Abort_on_warning_instant_set
{
public :
Use_relaxed_field_copy(THD *thd) :
Sql_mode_save(thd), Check_level_instant_set(thd, CHECK_FIELD_IGNORE),
Abort_on_warning_instant_set(thd, 0 )
{
thd->variables.sql_mode&= ~(MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE);
thd->variables.sql_mode|= MODE_INVALID_DATES;
}
};
/**
This class resembles the SQL Standard schema qualified object name :
< schema qualified name > : : = [ < schema name > < period > ] < qualified identifier >
*/
class Database_qualified_name
{
public :
Lex_ident_db m_db;
Lex_cstring m_name; // no comparison semantics
Database_qualified_name()
{ }
Database_qualified_name(const Lex_ident_db &db, const LEX_CSTRING &name)
:m_db(db), m_name(name)
{ }
Identifier_chain2 to_identifier_chain2() const
{
return Identifier_chain2(m_db, m_name);
}
bool eq_routine_name(const Database_qualified_name *other) const
{
return m_db.streq_safe(other->m_db) &&
Lex_ident_routine(m_name).streq(other->m_name);
}
/*
Make copies of " db " and " name " on the memory root in internal format :
- Lower - case " db " if lower - case - table - names = = 1 .
- Preserve " name " as is .
*/
bool copy_sp_name_internal(MEM_ROOT *mem_root, const Lex_ident_db &db,
const LEX_CSTRING &name);
bool make_package_routine_name(MEM_ROOT *mem_root,
const LEX_CSTRING &package,
const LEX_CSTRING &routine)
{
char *tmp;
size_t length= package.length + 1 + routine.length + 1 ;
if (unlikely(!(tmp= (char *) alloc_root(mem_root, length))))
return true ;
m_name.length= Identifier_chain2(package, routine).make_qname(tmp, length);
m_name.str= tmp;
return false ;
}
bool make_package_routine_name(MEM_ROOT *mem_root,
const LEX_CSTRING &db,
const LEX_CSTRING &package,
const LEX_CSTRING &routine)
{
if (unlikely(make_package_routine_name(mem_root, package, routine)))
return true ;
if (unlikely(!(m_db.str= strmake_root(mem_root, db.str, db.length))))
return true ;
m_db.length= db.length;
return false ;
}
};
class ErrConvDQName: public ErrConv
{
protected :
const Database_qualified_name *m_name;
public :
ErrConvDQName(const Database_qualified_name *name)
:m_name(name)
{ }
LEX_CSTRING lex_cstring() const override
{
if (m_name->m_db.length)
{
size_t length= m_name->to_identifier_chain2().make_qname(err_buffer,
sizeof (err_buffer));
return {err_buffer, length};
}
return {m_name->m_name.str, m_name->m_name.length};
}
};
class Type_holder: public Sql_alloc,
public Item_args,
public Type_handler_hybrid_field_type,
public Type_all_attributes,
public Type_extra_attributes
{
bool m_maybe_null;
public :
Type_holder()
:m_maybe_null(false )
{ }
void set_type_maybe_null(bool maybe_null_arg) override
{
m_maybe_null= maybe_null_arg;
}
bool get_maybe_null() const { return m_maybe_null; }
decimal_digits_t decimal_precision() const override
{
/*
Type_holder is not used directly to create fields , so
its virtual decimal_precision ( ) is never called .
We should eventually extend create_result_table ( ) to accept
an array of Type_holders directly , without having to allocate
Item_type_holder ' s and put them into List < Item > .
*/
DBUG_ASSERT(0 );
return 0 ;
}
Type_extra_attributes *type_extra_attributes_addr() override
{
return this ;
}
const Type_extra_attributes type_extra_attributes() const override
{
return *this ;
}
bool aggregate_attributes(THD *thd)
{
static LEX_CSTRING union_name= { STRING_WITH_LEN("UNION" ) };
for (uint i= 0 ; i < arg_count; i++)
m_maybe_null|= args[i]->maybe_null();
return
type_handler()->Item_hybrid_func_fix_attributes(thd,
union_name, this , this ,
args, arg_count);
}
};
/*
A helper class to set THD flags to emit warnings / errors in case of
overflow / type errors during assigning values into the SP variable fields .
Saves original flags values in constructor .
Restores original flags in destructor .
*/
class Sp_eval_expr_state
{
THD *m_thd;
enum_check_fields m_count_cuted_fields;
bool m_abort_on_warning;
bool m_stmt_modified_non_trans_table;
void start()
{
m_thd->count_cuted_fields= CHECK_FIELD_ERROR_FOR_NULL;
m_thd->abort_on_warning= m_thd->is_strict_mode();
m_thd->transaction->stmt.modified_non_trans_table= false ;
}
void stop()
{
m_thd->count_cuted_fields= m_count_cuted_fields;
m_thd->abort_on_warning= m_abort_on_warning;
m_thd->transaction->stmt.modified_non_trans_table=
m_stmt_modified_non_trans_table;
}
public :
Sp_eval_expr_state(THD *thd)
:m_thd(thd),
m_count_cuted_fields(thd->count_cuted_fields),
m_abort_on_warning(thd->abort_on_warning),
m_stmt_modified_non_trans_table(thd->transaction->stmt.
modified_non_trans_table)
{
start();
}
~Sp_eval_expr_state()
{
stop();
}
};
#ifndef DBUG_OFF
void dbug_serve_apcs(THD *thd, int n_calls);
#endif
class StatementBinlog
{
const enum_binlog_format saved_binlog_format;
THD *const thd;
public :
StatementBinlog(THD *thd, bool need_stmt) :
saved_binlog_format(thd->get_current_stmt_binlog_format()),
thd(thd)
{
if (need_stmt && saved_binlog_format != BINLOG_FORMAT_STMT)
{
thd->set_current_stmt_binlog_format_stmt();
}
}
~StatementBinlog()
{
thd->set_current_stmt_binlog_format(saved_binlog_format);
}
};
/** THD registry */
class THD_list: public THD_list_iterator
{
public :
/**
Constructor replacement .
Unfortunately we can ' t use fair constructor to initialize mutex
for two reasons : PFS and embedded . The former can probably be fixed ,
the latter can probably be dropped .
*/
void init()
{
mysql_rwlock_init(key_rwlock_THD_list, &lock);
}
/** Destructor replacement. */
void destroy()
{
mysql_rwlock_destroy(&lock);
}
/**
Inserts thread to registry .
@ param thd thread
Thread becomes accessible via server_threads .
*/
void insert(THD *thd)
{
mysql_rwlock_wrlock(&lock);
threads.append(thd);
mysql_rwlock_unlock(&lock);
}
/**
Removes thread from registry .
@ param thd thread
Thread becomes not accessible via server_threads .
*/
void erase(THD *thd)
{
thd->assert_linked();
mysql_rwlock_wrlock(&lock);
thd->unlink();
mysql_rwlock_unlock(&lock);
}
};
extern THD_list server_threads;
void setup_tmp_table_column_bitmaps(TABLE *table, uchar *bitmaps,
uint field_count);
C_MODE_START
void mariadb_sleep_for_space(unsigned int seconds);
C_MODE_END
#ifdef WITH_WSREP
extern void wsrep_to_isolation_end(THD*);
#endif
/*
RAII utility class to ease binlogging with temporary setting
THD etc context and restoring the original one upon logger execution .
*/
class Write_log_with_flags
{
THD* m_thd;
#ifdef WITH_WSREP
bool wsrep_to_isolation;
#endif
public :
~Write_log_with_flags()
{
m_thd->set_binlog_flags_for_alter(0 );
m_thd->set_binlog_start_alter_seq_no(0 );
#ifdef WITH_WSREP
if (wsrep_to_isolation)
wsrep_to_isolation_end(m_thd);
#endif
}
Write_log_with_flags(THD *thd, uchar flags,
bool do_wsrep_iso __attribute__((unused))= false ) :
m_thd(thd)
{
m_thd->set_binlog_flags_for_alter(flags);
#ifdef WITH_WSREP
wsrep_to_isolation= do_wsrep_iso && WSREP(m_thd);
#endif
}
};
/**
Make a new string allocated on THD ' s mem - root .
@ param thd thread handler .
@ param start_ptr start of the new string .
@ param end_ptr end of the new string .
@ return LEX_CSTRING object , containing a pointer to a newly
constructed / allocated string , and its length . The data member
LEX_CSTRING : : str has the value nullptr in case of out - of - memory error .
*/
LEX_CSTRING make_string(THD *thd, const char *start_ptr,
const char *end_ptr);
#include "deprecation.h"
#endif /* MYSQL_SERVER */
#endif /* SQL_CLASS_INCLUDED */
Messung V0.5 in Prozent C=92 H=100 G=95
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.2.101Bemerkung:
(vorverarbeitet am 2026-10-08)
¤
*Bot Zugriff