summaryrefslogtreecommitdiff
path: root/sql/sql_class.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sql_class.cc')
-rw-r--r--sql/sql_class.cc633
1 files changed, 313 insertions, 320 deletions
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index a47c6046bac..d097f7b32a8 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -29,7 +29,6 @@
#include "sql_priv.h"
#include "unireg.h" // REQUIRED: for other includes
#include "sql_class.h"
-#include "lock.h" // unlock_global_read_lock, mysql_unlock_tables
#include "sql_cache.h" // query_cache_abort
#include "sql_base.h" // close_thread_tables
#include "sql_time.h" // date_time_format_copy
@@ -101,8 +100,8 @@ extern "C" void free_user_var(user_var_entry *entry)
{
char *pos= (char*) entry+ALIGN_SIZE(sizeof(*entry));
if (entry->value && entry->value != pos)
- my_free(entry->value, MYF(0));
- my_free((char*) entry,MYF(0));
+ my_free(entry->value);
+ my_free(entry);
}
bool Key_part_spec::operator==(const Key_part_spec& other) const
@@ -262,11 +261,13 @@ int thd_tablespace_op(const THD *thd)
extern "C"
-const char *set_thd_proc_info(THD *thd, const char *info,
+const char *set_thd_proc_info(void *thd_arg, const char *info,
const char *calling_function,
const char *calling_file,
const unsigned int calling_line)
{
+ THD *thd= (THD *) thd_arg;
+
if (!thd)
thd= current_thd;
@@ -306,6 +307,37 @@ void **thd_ha_data(const THD *thd, const struct handlerton *hton)
return (void **) &thd->ha_data[hton->slot].ha_ptr;
}
+
+/**
+ Provide a handler data getter to simplify coding
+*/
+extern "C"
+void *thd_get_ha_data(const THD *thd, const struct handlerton *hton)
+{
+ return *thd_ha_data(thd, hton);
+}
+
+
+/**
+ Provide a handler data setter to simplify coding
+ @see thd_set_ha_data() definition in plugin.h
+*/
+extern "C"
+void thd_set_ha_data(THD *thd, const struct handlerton *hton,
+ const void *ha_data)
+{
+ plugin_ref *lock= &thd->ha_data[hton->slot].lock;
+ if (ha_data && !*lock)
+ *lock= ha_lock_engine(NULL, (handlerton*) hton);
+ else if (!ha_data && *lock)
+ {
+ plugin_unlock(NULL, *lock);
+ *lock= NULL;
+ }
+ *thd_ha_data(thd, hton)= (void*) ha_data;
+}
+
+
extern "C"
long long thd_test_options(const THD *thd, long long test_options)
{
@@ -321,7 +353,7 @@ int thd_sql_command(const THD *thd)
extern "C"
int thd_tx_isolation(const THD *thd)
{
- return (int) thd->variables.tx_isolation;
+ return (int) thd->tx_isolation;
}
extern "C"
@@ -459,10 +491,9 @@ THD::THD()
:Statement(&main_lex, &main_mem_root, CONVENTIONAL_EXECUTION,
/* statement id */ 0),
rli_fake(0),
- lock_id(&main_lock_id),
user_time(0), in_sub_stmt(0),
- sql_log_bin_toplevel(false),
- binlog_unsafe_warning_flags(0), binlog_table_maps(0),
+ binlog_unsafe_warning_flags(0),
+ binlog_table_maps(0),
table_map_for_update(0),
arg_of_last_insert_id_function(FALSE),
first_successful_insert_id_in_prev_stmt(0),
@@ -513,7 +544,7 @@ THD::THD()
cuted_fields= 0L;
sent_row_count= 0L;
limit_found_rows= 0;
- row_count_func= -1;
+ m_row_count_func= -1;
statement_id_counter= 0UL;
// Must be reset to handle error with THD's created for init of mysqld
lex->current_select= 0;
@@ -561,7 +592,7 @@ THD::THD()
*scramble= '\0';
/* Call to init() below requires fully initialized Open_tables_state. */
- init_open_tables_state(this, refresh_version);
+ reset_open_tables_state(this);
init();
#if defined(ENABLED_PROFILING)
@@ -592,9 +623,11 @@ THD::THD()
randominit(&rand, tmp + (ulong) &rand, tmp + (ulong) ::global_query_id);
substitute_null_with_insert_id = FALSE;
thr_lock_info_init(&lock_info); /* safety: will be reset after start */
- thr_lock_owner_init(&main_lock_id, &lock_info);
m_internal_handler= NULL;
+ current_user_used= FALSE;
+ memset(&invoker_user, 0, sizeof(invoker_user));
+ memset(&invoker_host, 0, sizeof(invoker_host));
}
@@ -805,48 +838,26 @@ MYSQL_ERROR* THD::raise_condition(uint sql_errno,
else
{
if (! stmt_da->is_error())
+ {
+ set_row_count_func(-1);
stmt_da->set_error_status(this, sql_errno, msg, sqlstate);
+ }
}
}
- /*
- If a continue handler is found, the error message will be cleared
- by the stored procedures code.
- */
- if (!is_fatal_error && spcont &&
- spcont->handle_condition(this, sql_errno, sqlstate, level, msg, &cond))
- {
- /*
- Do not push any warnings, a handled error must be completely
- silenced.
- */
- DBUG_RETURN(cond);
- }
-
- /* Un-handled conditions */
-
- cond= raise_condition_no_handler(sql_errno, sqlstate, level, msg);
- DBUG_RETURN(cond);
-}
-
-MYSQL_ERROR*
-THD::raise_condition_no_handler(uint sql_errno,
- const char* sqlstate,
- MYSQL_ERROR::enum_warning_level level,
- const char* msg)
-{
- MYSQL_ERROR *cond= NULL;
- DBUG_ENTER("THD::raise_condition_no_handler");
-
query_cache_abort(&query_cache_tls);
/* FIXME: broken special case */
if (no_warnings_for_error && (level == MYSQL_ERROR::WARN_LEVEL_ERROR))
DBUG_RETURN(NULL);
+ /* When simulating OOM, skip writing to error log to avoid mtr errors */
+ DBUG_EXECUTE_IF("simulate_out_of_memory", DBUG_RETURN(NULL););
+
cond= warning_info->push_warning(this, sql_errno, sqlstate, level, msg);
DBUG_RETURN(cond);
}
+
extern "C"
void *thd_alloc(MYSQL_THD thd, unsigned int size)
{
@@ -923,11 +934,15 @@ void THD::init(void)
update_lock_default= (variables.low_priority_updates ?
TL_WRITE_LOW_PRIORITY :
TL_WRITE);
- session_tx_isolation= (enum_tx_isolation) variables.tx_isolation;
+ tx_isolation= (enum_tx_isolation) variables.tx_isolation;
update_charset();
reset_current_stmt_binlog_format_row();
bzero((char *) &status_var, sizeof(status_var));
- sql_log_bin_toplevel= variables.option_bits & OPTION_BIN_LOG;
+
+ if (variables.sql_log_bin)
+ variables.option_bits|= OPTION_BIN_LOG;
+ else
+ variables.option_bits&= ~OPTION_BIN_LOG;
#if defined(ENABLED_DEBUG_SYNC)
/* Initialize the Debug Sync Facility. See debug_sync.cc. */
@@ -1069,7 +1084,6 @@ THD::~THD()
}
#endif
stmt_map.reset(); /* close all prepared statements */
- DBUG_ASSERT(lock_info.n_cursors == 0);
if (!cleanup_done)
cleanup();
@@ -1080,7 +1094,8 @@ THD::~THD()
DBUG_PRINT("info", ("freeing security context"));
main_security_ctx.destroy();
- safeFree(db);
+ my_free(db);
+ db= NULL;
free_root(&transaction.mem_root,MYF(0));
mysql_mutex_destroy(&LOCK_thd_data);
#ifndef DBUG_OFF
@@ -1124,6 +1139,9 @@ void add_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var)
while (to != end)
*(to++)+= *(from++);
+
+ to_var->bytes_received+= from_var->bytes_received;
+ to_var->bytes_sent+= from_var->bytes_sent;
}
/*
@@ -1149,6 +1167,9 @@ void add_diff_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var,
while (to != end)
*(to++)+= *(from++) - *(dec++);
+
+ to_var->bytes_received+= from_var->bytes_received - dec_var->bytes_received;;
+ to_var->bytes_sent+= from_var->bytes_sent - dec_var->bytes_sent;
}
@@ -1309,6 +1330,7 @@ void THD::cleanup_after_query()
where= THD::DEFAULT_WHERE;
/* reset table map for multi-table update */
table_map_for_update= 0;
+ clean_current_user_used();
}
@@ -1450,7 +1472,7 @@ void THD::add_changed_table(TABLE *table)
{
DBUG_ENTER("THD::add_changed_table(table)");
- DBUG_ASSERT(in_multi_stmt_transaction() && table->file->has_transactions());
+ DBUG_ASSERT(in_multi_stmt_transaction_mode() && table->file->has_transactions());
add_changed_table(table->s->table_cache_key.str,
(long) table->s->table_cache_key.length);
DBUG_VOID_RETURN;
@@ -1696,9 +1718,9 @@ bool select_send::send_result_set_metadata(List<Item> &list, uint flags)
return res;
}
-void select_send::abort()
+void select_send::abort_result_set()
{
- DBUG_ENTER("select_send::abort");
+ DBUG_ENTER("select_send::abort_result_set");
if (is_result_set_started && thd->spcont)
{
@@ -1772,12 +1794,6 @@ bool select_send::send_eof()
*/
ha_release_temporary_latches(thd);
- /* Unlock tables before sending packet to gain some speed */
- if (thd->lock && ! thd->locked_tables_mode)
- {
- mysql_unlock_tables(thd, thd->lock);
- thd->lock=0;
- }
/*
Don't send EOF if we're in error condition (which implies we've already
sent or are sending an error)
@@ -1815,11 +1831,6 @@ bool select_to_file::send_eof()
error= 1;
if (!error)
{
- /*
- In order to remember the value of affected rows for ROW_COUNT()
- function, SELECT INTO has to have an own SQLCOM.
- TODO: split from SQLCOM_SELECT
- */
::my_ok(thd,row_count);
}
file= -1;
@@ -1896,8 +1907,7 @@ static File create_file(THD *thd, char *path, sql_exchange *exchange,
else
(void) fn_format(path, exchange->file_name, mysql_real_data_home, "", option);
- if (opt_secure_file_priv &&
- strncmp(opt_secure_file_priv, path, strlen(opt_secure_file_priv)))
+ if (!is_secure_file_path(path))
{
/* Write only allowed to dir or subdir specified by secure_file_priv */
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--secure-file-priv");
@@ -2066,9 +2076,21 @@ bool select_export::send_data(List<Item> &items)
const char *from_end_pos;
const char *error_pos;
uint32 bytes;
- bytes= well_formed_copy_nchars(write_cs, cvt_buff, sizeof(cvt_buff),
+ uint64 estimated_bytes=
+ ((uint64) res->length() / res->charset()->mbminlen + 1) *
+ write_cs->mbmaxlen + 1;
+ set_if_smaller(estimated_bytes, UINT_MAX32);
+ if (cvt_str.realloc((uint32) estimated_bytes))
+ {
+ my_error(ER_OUTOFMEMORY, MYF(0), (uint32) estimated_bytes);
+ goto err;
+ }
+
+ bytes= well_formed_copy_nchars(write_cs, (char *) cvt_str.ptr(),
+ cvt_str.alloced_length(),
res->charset(), res->ptr(), res->length(),
- sizeof(cvt_buff),
+ UINT_MAX32, // copy all input chars,
+ // i.e. ignore nchars parameter
&well_formed_error_pos,
&cannot_convert_error_pos,
&from_end_pos);
@@ -2086,6 +2108,15 @@ bool select_export::send_data(List<Item> &items)
"string", printable_buff,
item->name, row_count);
}
+ else if (from_end_pos < res->ptr() + res->length())
+ {
+ /*
+ result is longer than UINT_MAX32 and doesn't fit into String
+ */
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ WARN_DATA_TRUNCATED, ER(WARN_DATA_TRUNCATED),
+ item->full_name(), row_count);
+ }
cvt_str.length(bytes);
res= &cvt_str;
}
@@ -2536,7 +2567,6 @@ Statement::Statement(LEX *lex_arg, MEM_ROOT *mem_root_arg,
id(id_arg),
mark_used_columns(MARK_COLUMNS_READ),
lex(lex_arg),
- cursor(0),
db(NULL),
db_length(0)
{
@@ -2558,7 +2588,6 @@ void Statement::set_statement(Statement *stmt)
mark_used_columns= stmt->mark_used_columns;
lex= stmt->lex;
query_string= stmt->query_string;
- cursor= stmt->cursor;
}
@@ -2840,11 +2869,6 @@ bool select_dumpvar::send_eof()
if (! row_count)
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_SP_FETCH_NO_DATA, ER(ER_SP_FETCH_NO_DATA));
- /*
- In order to remember the value of affected rows for ROW_COUNT()
- function, SELECT INTO has to have an own SQLCOM.
- TODO: split from SQLCOM_SELECT
- */
::my_ok(thd,row_count);
return 0;
}
@@ -2910,10 +2934,18 @@ void Security_context::destroy()
{
// If not pointer to constant
if (host != my_localhost)
- safeFree(host);
+ {
+ my_free(host);
+ host= NULL;
+ }
if (user != delayed_user)
- safeFree(user);
- safeFree(ip);
+ {
+ my_free(user);
+ user= NULL;
+ }
+
+ my_free(ip);
+ ip= NULL;
}
@@ -2929,7 +2961,7 @@ void Security_context::skip_grants()
bool Security_context::set_user(char *user_arg)
{
- safeFree(user);
+ my_free(user);
user= my_strdup(user_arg, MYF(0));
return user == 0;
}
@@ -3156,6 +3188,11 @@ extern "C" bool thd_binlog_filter_ok(const MYSQL_THD thd)
return binlog_filter->db_ok(thd->db);
}
+extern "C" bool thd_sqlcom_can_generate_row_events(const MYSQL_THD thd)
+{
+ return sqlcom_can_generate_row_events(thd);
+}
+
#ifndef EMBEDDED_LIBRARY
extern "C" void thd_pool_wait_begin(MYSQL_THD thd, int wait_type);
extern "C" void thd_pool_wait_end(MYSQL_THD thd);
@@ -3256,6 +3293,7 @@ void THD::reset_sub_statement_state(Sub_statement_state *backup,
#endif
backup->option_bits= variables.option_bits;
+ backup->count_cuted_fields= count_cuted_fields;
backup->in_sub_stmt= in_sub_stmt;
backup->enable_slow_log= enable_slow_log;
backup->limit_found_rows= limit_found_rows;
@@ -3293,6 +3331,7 @@ void THD::reset_sub_statement_state(Sub_statement_state *backup,
void THD::restore_sub_statement_state(Sub_statement_state *backup)
{
+ DBUG_ENTER("THD::restore_sub_statement_state");
#ifndef EMBEDDED_LIBRARY
/* BUG#33029, if we are replicating from a buggy master, restore
auto_inc_intervals_forced so that the top statement can use the
@@ -3319,6 +3358,7 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup)
/* ha_release_savepoint() never returns error. */
(void)ha_release_savepoint(this, sv);
}
+ count_cuted_fields= backup->count_cuted_fields;
transaction.savepoints= backup->savepoints;
variables.option_bits= backup->option_bits;
in_sub_stmt= backup->in_sub_stmt;
@@ -3348,6 +3388,7 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup)
*/
examined_row_count+= backup->examined_row_count;
cuted_fields+= backup->cuted_fields;
+ DBUG_VOID_RETURN;
}
@@ -3411,6 +3452,22 @@ void THD::leave_locked_tables_mode()
mysql_ha_move_tickets_after_trans_sentinel(this);
}
+void THD::get_definer(LEX_USER *definer)
+{
+ set_current_user_used();
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+ if (slave_thread && has_invoker())
+ {
+ definer->user = invoker_user;
+ definer->host= invoker_host;
+ definer->password.str= NULL;
+ definer->password.length= 0;
+ }
+ else
+#endif
+ get_default_definer(this, definer);
+}
+
/**
Mark transaction to rollback and mark error as fatal to a sub-statement.
@@ -3457,7 +3514,7 @@ uchar *xid_get_hash_key(const uchar *ptr, size_t *length,
void xid_free_hash(void *ptr)
{
if (!((XID_STATE*)ptr)->in_thd)
- my_free((uchar*)ptr, MYF(0));
+ my_free(ptr);
}
#ifdef HAVE_PSI_INTERFACE
@@ -3673,7 +3730,7 @@ int THD::decide_logging_format(TABLE_LIST *tables)
capabilities.
*/
handler::Table_flags flags_write_some_set= 0;
- handler::Table_flags flags_some_set= 0;
+ handler::Table_flags flags_access_some_set= 0;
handler::Table_flags flags_write_all_set=
HA_BINLOG_ROW_CAPABLE | HA_BINLOG_STMT_CAPABLE;
@@ -3689,17 +3746,16 @@ int THD::decide_logging_format(TABLE_LIST *tables)
*/
my_bool multi_access_engine= FALSE;
/*
- If non-transactional and transactional engines are about
- to be accessed and any of them is about to be updated.
- For example: Innodb and MyIsam.
+ Identifies if a table is changed.
*/
- my_bool trans_non_trans_access_engines= FALSE;
+ my_bool is_write= FALSE;
/*
- If all engines that are about to be updated are
- transactional.
+ A pointer to a previous table that was changed.
*/
- my_bool all_trans_write_engines= TRUE;
TABLE* prev_write_table= NULL;
+ /*
+ A pointer to a previous table that was accessed.
+ */
TABLE* prev_access_table= NULL;
#ifndef DBUG_OFF
@@ -3722,9 +3778,13 @@ int THD::decide_logging_format(TABLE_LIST *tables)
{
if (table->placeholder())
continue;
- if (table->table->s->table_category == TABLE_CATEGORY_PERFORMANCE)
+
+ if (table->table->s->table_category == TABLE_CATEGORY_PERFORMANCE ||
+ table->table->s->table_category == TABLE_CATEGORY_LOG)
lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_TABLE);
+
handler::Table_flags const flags= table->table->file->ha_table_flags();
+
DBUG_PRINT("info", ("table: %s; ha_table_flags: 0x%llx",
table->table_name, flags));
if (table->lock_type >= TL_WRITE_ALLOW_WRITE)
@@ -3732,177 +3792,173 @@ int THD::decide_logging_format(TABLE_LIST *tables)
if (prev_write_table && prev_write_table->file->ht !=
table->table->file->ht)
multi_write_engine= TRUE;
- /*
- Every temporary table must be always written to the binary
- log in transaction boundaries and as such we artificially
- classify them as transactional.
- Indirectly, this avoids classifying a temporary table created
- on a non-transactional engine as unsafe when it is modified
- after any transactional table:
+ my_bool trans= table->table->file->has_transactions();
- BEGIN;
- INSERT INTO innodb_t VALUES (1);
- INSERT INTO myisam_t_temp VALUES (1);
- COMMIT;
-
- BINARY LOG:
+ if (table->table->s->tmp_table)
+ lex->set_stmt_accessed_table(trans ? LEX::STMT_WRITES_TEMP_TRANS_TABLE :
+ LEX::STMT_WRITES_TEMP_NON_TRANS_TABLE);
+ else
+ lex->set_stmt_accessed_table(trans ? LEX::STMT_WRITES_TRANS_TABLE :
+ LEX::STMT_WRITES_NON_TRANS_TABLE);
- BEGIN;
- INSERT INTO innodb_t VALUES (1);
- INSERT INTO myisam_t_temp VALUES (1);
- COMMIT;
- */
- all_trans_write_engines= all_trans_write_engines &&
- (table->table->file->has_transactions() ||
- table->table->s->tmp_table);
- prev_write_table= table->table;
flags_write_all_set &= flags;
flags_write_some_set |= flags;
- }
- flags_some_set |= flags;
- /*
- The mixture of non-transactional and transactional tables must
- identified and classified as unsafe. However, a temporary table
- must be always handled as a transactional table. Based on that,
- we have the following statements classified as mixed and by
- consequence as unsafe:
-
- 1: INSERT INTO myisam_t SELECT * FROM innodb_t;
-
- 2: INSERT INTO innodb_t SELECT * FROM myisam_t;
-
- 3: INSERT INTO myisam_t SELECT * FROM myisam_t_temp;
-
- 4: INSERT INTO myisam_t_temp SELECT * FROM myisam_t;
+ is_write= TRUE;
- 5: CREATE TEMPORARY TABLE myisam_t_temp SELECT * FROM mysiam_t;
-
- The following statements are not considered mixed and as such
- are safe:
+ prev_write_table= table->table;
- 1: INSERT INTO innodb_t SELECT * FROM myisam_t_temp;
+ }
+ flags_access_some_set |= flags;
- 2: INSERT INTO myisam_t_temp SELECT * FROM innodb_t_temp;
- */
- if (!trans_non_trans_access_engines && prev_access_table &&
- (lex->sql_command != SQLCOM_CREATE_TABLE ||
+ if (lex->sql_command != SQLCOM_CREATE_TABLE ||
(lex->sql_command == SQLCOM_CREATE_TABLE &&
- (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE))))
+ (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)))
{
- my_bool prev_trans;
- my_bool act_trans;
- if (prev_access_table->s->tmp_table || table->table->s->tmp_table)
- {
- prev_trans= prev_access_table->s->tmp_table ? TRUE :
- prev_access_table->file->has_transactions();
- act_trans= table->table->s->tmp_table ? TRUE :
- table->table->file->has_transactions();
- }
+ my_bool trans= table->table->file->has_transactions();
+
+ if (table->table->s->tmp_table)
+ lex->set_stmt_accessed_table(trans ? LEX::STMT_READS_TEMP_TRANS_TABLE :
+ LEX::STMT_READS_TEMP_NON_TRANS_TABLE);
else
- {
- prev_trans= prev_access_table->file->has_transactions();
- act_trans= table->table->file->has_transactions();
- }
- trans_non_trans_access_engines= (prev_trans != act_trans);
- multi_access_engine= TRUE;
+ lex->set_stmt_accessed_table(trans ? LEX::STMT_READS_TRANS_TABLE :
+ LEX::STMT_READS_NON_TRANS_TABLE);
}
- thread_temporary_used |= table->table->s->tmp_table;
+
+ if (prev_access_table && prev_access_table->file->ht !=
+ table->table->file->ht)
+ multi_access_engine= TRUE;
+
prev_access_table= table->table;
}
DBUG_PRINT("info", ("flags_write_all_set: 0x%llx", flags_write_all_set));
DBUG_PRINT("info", ("flags_write_some_set: 0x%llx", flags_write_some_set));
- DBUG_PRINT("info", ("flags_some_set: 0x%llx", flags_some_set));
+ DBUG_PRINT("info", ("flags_access_some_set: 0x%llx", flags_access_some_set));
DBUG_PRINT("info", ("multi_write_engine: %d", multi_write_engine));
DBUG_PRINT("info", ("multi_access_engine: %d", multi_access_engine));
- DBUG_PRINT("info", ("trans_non_trans_access_engines: %d",
- trans_non_trans_access_engines));
int error= 0;
int unsafe_flags;
/*
- Set the statement as unsafe if:
-
- . it is a mixed statement, i.e. access transactional and non-transactional
- tables, and update any of them;
-
- or:
-
- . an early statement updated a transactional table;
- . and, the current statement updates a non-transactional table.
-
- Any mixed statement is classified as unsafe to ensure that mixed mode is
- completely safe. Consider the following example to understand why we
- decided to do this:
-
- Note that mixed statements such as
-
- 1: INSERT INTO myisam_t SELECT * FROM innodb_t;
-
- 2: INSERT INTO innodb_t SELECT * FROM myisam_t;
-
- 3: INSERT INTO myisam_t SELECT * FROM myisam_t_temp;
-
- 4: INSERT INTO myisam_t_temp SELECT * FROM myisam_t;
+ Classify a statement as unsafe when there is a mixed statement and an
+ on-going transaction at any point of the execution if:
- 5: CREATE TEMPORARY TABLE myisam_t_temp SELECT * FROM mysiam_t;
+ 1. The mixed statement is about to update a transactional table and
+ a non-transactional table.
- are classified as unsafe to ensure that in mixed mode the execution is
- completely safe and equivalent to the row mode. Consider the following
- statements and sessions (connections) to understand the reason:
+ 2. The mixed statement is about to update a temporary transactional
+ table and a non-transactional table.
+
+ 3. The mixed statement is about to update a transactional table and
+ read from a non-transactional table.
- con1: INSERT INTO innodb_t VALUES (1);
- con1: INSERT INTO innodb_t VALUES (100);
+ 4. The mixed statement is about to update a temporary transactional
+ table and read from a non-transactional table.
- con1: BEGIN
- con2: INSERT INTO myisam_t SELECT * FROM innodb_t;
- con1: INSERT INTO innodb_t VALUES (200);
- con1: COMMIT;
+ 5. The mixed statement is about to update a non-transactional table
+ and read from a transactional table when the isolation level is
+ lower than repeatable read.
- The point is that the concurrent statements may be written into the binary log
- in a way different from the execution. For example,
+ After updating a transactional table if:
- BINARY LOG:
-
- con2: BEGIN;
- con2: INSERT INTO myisam_t SELECT * FROM innodb_t;
- con2: COMMIT;
- con1: BEGIN
- con1: INSERT INTO innodb_t VALUES (200);
- con1: COMMIT;
-
- ....
-
- or
-
- BINARY LOG:
-
- con1: BEGIN
- con1: INSERT INTO innodb_t VALUES (200);
- con1: COMMIT;
- con2: BEGIN;
- con2: INSERT INTO myisam_t SELECT * FROM innodb_t;
- con2: COMMIT;
-
- Clearly, this may become a problem in STMT mode and setting the statement
- as unsafe will make rows to be written into the binary log in MIXED mode
- and as such the problem will not stand.
-
- In STMT mode, although such statement is classified as unsafe, i.e.
+ 6. The mixed statement is about to update a non-transactional table
+ and read from a temporary transactional table.
+
+ 7. The mixed statement is about to update a non-transactional table
+ and read from a temporary transactional table.
+
+ 8. The mixed statement is about to update a non-transactionala table
+ and read from a temporary non-transactional table.
+
+ 9. The mixed statement is about to update a temporary non-transactional
+ table and update a non-transactional table.
+
+ 10. The mixed statement is about to update a temporary non-transactional
+ table and read from a non-transactional table.
+
+ 11. A statement is about to update a non-transactional table and the
+ option variables.binlog_direct_non_trans_update is OFF.
+
+ The reason for this is that locks acquired may not protected a concurrent
+ transaction of interfering in the current execution and by consequence in
+ the result. In particular, if there is an on-going transaction and a
+ transactional table was already updated, a temporary table must be written
+ to the binary log in the boundaries of the on-going transaction and as
+ such we artificially classify them as transactional.
+ */
+ if (in_multi_stmt_transaction_mode())
+ {
+ my_bool mixed_unsafe= FALSE;
+ my_bool non_trans_unsafe= FALSE;
+
+ /* Case 1. */
+ if (lex->stmt_accessed_table(LEX::STMT_WRITES_TRANS_TABLE) &&
+ lex->stmt_accessed_table(LEX::STMT_WRITES_NON_TRANS_TABLE))
+ mixed_unsafe= TRUE;
+ /* Case 2. */
+ else if (lex->stmt_accessed_table(LEX::STMT_WRITES_TEMP_TRANS_TABLE) &&
+ lex->stmt_accessed_table(LEX::STMT_WRITES_NON_TRANS_TABLE))
+ mixed_unsafe= TRUE;
+ /* Case 3. */
+ else if (lex->stmt_accessed_table(LEX::STMT_WRITES_TRANS_TABLE) &&
+ lex->stmt_accessed_table(LEX::STMT_READS_NON_TRANS_TABLE))
+ mixed_unsafe= TRUE;
+ /* Case 4. */
+ else if (lex->stmt_accessed_table(LEX::STMT_WRITES_TEMP_TRANS_TABLE) &&
+ lex->stmt_accessed_table(LEX::STMT_READS_NON_TRANS_TABLE))
+ mixed_unsafe= TRUE;
+ /* Case 5. */
+ else if (lex->stmt_accessed_table(LEX::STMT_WRITES_NON_TRANS_TABLE) &&
+ lex->stmt_accessed_table(LEX::STMT_READS_TRANS_TABLE) &&
+ tx_isolation < ISO_REPEATABLE_READ)
+ /*
+ By default, InnoDB operates in REPEATABLE READ and with the option
+ --innodb-locks-unsafe-for-binlog disabled. In this case, InnoDB uses
+ next-key locks for searches and index scans, which prevents phantom
+ rows.
+
+ This is scenario is safe for Innodb. However, there are no means to
+ transparently get this information. Therefore, we need to improve this
+ and change the storage engines to report somehow when an execution is
+ safe under an isolation level & binary logging format.
+ */
+ mixed_unsafe= TRUE;
- INSERT INTO myisam_t SELECT * FROM innodb_t;
+ if (trans_has_updated_trans_table(this))
+ {
+ /* Case 6. */
+ if (lex->stmt_accessed_table(LEX::STMT_WRITES_NON_TRANS_TABLE) &&
+ lex->stmt_accessed_table(LEX::STMT_READS_TRANS_TABLE))
+ mixed_unsafe= TRUE;
+ /* Case 7. */
+ else if (lex->stmt_accessed_table(LEX::STMT_WRITES_NON_TRANS_TABLE) &&
+ lex->stmt_accessed_table(LEX::STMT_READS_TEMP_TRANS_TABLE))
+ mixed_unsafe= TRUE;
+ /* Case 8. */
+ else if (lex->stmt_accessed_table(LEX::STMT_WRITES_NON_TRANS_TABLE) &&
+ lex->stmt_accessed_table(LEX::STMT_READS_TEMP_NON_TRANS_TABLE))
+ mixed_unsafe= TRUE;
+ /* Case 9. */
+ else if (lex->stmt_accessed_table(LEX::STMT_WRITES_TEMP_NON_TRANS_TABLE) &&
+ lex->stmt_accessed_table(LEX::STMT_WRITES_NON_TRANS_TABLE))
+ mixed_unsafe= TRUE;
+ /* Case 10. */
+ else if (lex->stmt_accessed_table(LEX::STMT_WRITES_TEMP_NON_TRANS_TABLE) &&
+ lex->stmt_accessed_table(LEX::STMT_READS_NON_TRANS_TABLE))
+ mixed_unsafe= TRUE;
+ /* Case 11. */
+ else if (!variables.binlog_direct_non_trans_update &&
+ lex->stmt_accessed_table(LEX::STMT_WRITES_NON_TRANS_TABLE))
+ non_trans_unsafe= TRUE;
+ }
- there is no enough information to avoid writing it outside the boundaries
- of a transaction. This is not a problem if we are considering snapshot
- isolation level but if we have pure repeatable read or serializable the
- lock history on the slave will be different from the master.
- */
- if (trans_non_trans_access_engines)
- lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_MIXED_STATEMENT);
- else if (trans_has_updated_trans_table(this) && !all_trans_write_engines)
- lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_NONTRANS_AFTER_TRANS);
+ if (mixed_unsafe)
+ lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_MIXED_STATEMENT);
+ else if (non_trans_unsafe)
+ lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_NONTRANS_AFTER_TRANS);
+ }
/*
If more than one engine is involved in the statement and at
@@ -3914,7 +3970,7 @@ int THD::decide_logging_format(TABLE_LIST *tables)
(flags_write_some_set & HA_HAS_OWN_BINLOGGING))
my_error((error= ER_BINLOG_MULTIPLE_ENGINES_AND_SELF_LOGGING_ENGINE),
MYF(0));
- else if (multi_access_engine && flags_some_set & HA_HAS_OWN_BINLOGGING)
+ else if (multi_access_engine && flags_access_some_set & HA_HAS_OWN_BINLOGGING)
lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_MULTIPLE_ENGINES_AND_SELF_LOGGING_ENGINE);
/* both statement-only and row-only engines involved */
@@ -3937,7 +3993,8 @@ int THD::decide_logging_format(TABLE_LIST *tables)
*/
my_error((error= ER_BINLOG_ROW_INJECTION_AND_STMT_ENGINE), MYF(0));
}
- else if (variables.binlog_format == BINLOG_FORMAT_ROW)
+ else if (variables.binlog_format == BINLOG_FORMAT_ROW &&
+ sqlcom_can_generate_row_events(this))
{
/*
2. Error: Cannot modify table that uses a storage engine
@@ -3975,7 +4032,8 @@ int THD::decide_logging_format(TABLE_LIST *tables)
*/
my_error((error= ER_BINLOG_ROW_INJECTION_AND_STMT_MODE), MYF(0));
}
- else if ((flags_write_all_set & HA_BINLOG_STMT_CAPABLE) == 0)
+ else if ((flags_write_all_set & HA_BINLOG_STMT_CAPABLE) == 0 &&
+ sqlcom_can_generate_row_events(this))
{
/*
5. Error: Cannot modify table that uses a storage engine
@@ -3983,13 +4041,14 @@ int THD::decide_logging_format(TABLE_LIST *tables)
*/
my_error((error= ER_BINLOG_STMT_MODE_AND_ROW_ENGINE), MYF(0), "");
}
- else if ((unsafe_flags= lex->get_stmt_unsafe_flags()) != 0)
+ else if (is_write && (unsafe_flags= lex->get_stmt_unsafe_flags()) != 0)
{
/*
7. Warning: Unsafe statement logged as statement due to
binlog_format = STATEMENT
*/
binlog_unsafe_warning_flags|= unsafe_flags;
+
DBUG_PRINT("info", ("Scheduling warning to be issued by "
"binlog_query: '%s'",
ER(ER_BINLOG_UNSAFE_STATEMENT)));
@@ -4155,72 +4214,9 @@ THD::binlog_prepare_pending_rows_event(TABLE*, uint32, MY_BITMAP const*,
Update_rows_log_event *);
#endif
-#ifdef NOT_USED
-static char const*
-field_type_name(enum_field_types type)
-{
- switch (type) {
- case MYSQL_TYPE_DECIMAL:
- return "MYSQL_TYPE_DECIMAL";
- case MYSQL_TYPE_TINY:
- return "MYSQL_TYPE_TINY";
- case MYSQL_TYPE_SHORT:
- return "MYSQL_TYPE_SHORT";
- case MYSQL_TYPE_LONG:
- return "MYSQL_TYPE_LONG";
- case MYSQL_TYPE_FLOAT:
- return "MYSQL_TYPE_FLOAT";
- case MYSQL_TYPE_DOUBLE:
- return "MYSQL_TYPE_DOUBLE";
- case MYSQL_TYPE_NULL:
- return "MYSQL_TYPE_NULL";
- case MYSQL_TYPE_TIMESTAMP:
- return "MYSQL_TYPE_TIMESTAMP";
- case MYSQL_TYPE_LONGLONG:
- return "MYSQL_TYPE_LONGLONG";
- case MYSQL_TYPE_INT24:
- return "MYSQL_TYPE_INT24";
- case MYSQL_TYPE_DATE:
- return "MYSQL_TYPE_DATE";
- case MYSQL_TYPE_TIME:
- return "MYSQL_TYPE_TIME";
- case MYSQL_TYPE_DATETIME:
- return "MYSQL_TYPE_DATETIME";
- case MYSQL_TYPE_YEAR:
- return "MYSQL_TYPE_YEAR";
- case MYSQL_TYPE_NEWDATE:
- return "MYSQL_TYPE_NEWDATE";
- case MYSQL_TYPE_VARCHAR:
- return "MYSQL_TYPE_VARCHAR";
- case MYSQL_TYPE_BIT:
- return "MYSQL_TYPE_BIT";
- case MYSQL_TYPE_NEWDECIMAL:
- return "MYSQL_TYPE_NEWDECIMAL";
- case MYSQL_TYPE_ENUM:
- return "MYSQL_TYPE_ENUM";
- case MYSQL_TYPE_SET:
- return "MYSQL_TYPE_SET";
- case MYSQL_TYPE_TINY_BLOB:
- return "MYSQL_TYPE_TINY_BLOB";
- case MYSQL_TYPE_MEDIUM_BLOB:
- return "MYSQL_TYPE_MEDIUM_BLOB";
- case MYSQL_TYPE_LONG_BLOB:
- return "MYSQL_TYPE_LONG_BLOB";
- case MYSQL_TYPE_BLOB:
- return "MYSQL_TYPE_BLOB";
- case MYSQL_TYPE_VAR_STRING:
- return "MYSQL_TYPE_VAR_STRING";
- case MYSQL_TYPE_STRING:
- return "MYSQL_TYPE_STRING";
- case MYSQL_TYPE_GEOMETRY:
- return "MYSQL_TYPE_GEOMETRY";
- }
- return "Unknown";
-}
-#endif
-
+/* Declare in unnamed namespace. */
+CPP_UNNAMED_NS_START
-namespace {
/**
Class to handle temporary allocation of memory for row data.
@@ -4273,7 +4269,7 @@ namespace {
~Row_data_memory()
{
if (m_memory != 0 && m_release_memory_on_destruction)
- my_free((uchar*) m_memory, MYF(MY_WME));
+ my_free(m_memory);
}
/**
@@ -4339,8 +4335,8 @@ namespace {
uchar *m_memory;
uchar *m_ptr[2];
};
-}
+CPP_UNNAMED_NS_END
int THD::binlog_write_row(TABLE* table, bool is_trans,
MY_BITMAP const* cols, size_t colcnt,
@@ -4484,7 +4480,6 @@ int THD::binlog_flush_pending_rows_event(bool stmt_end, bool is_transactional)
if (stmt_end)
{
pending->set_flags(Rows_log_event::STMT_END_F);
- pending->flags|= LOG_EVENT_UPDATE_TABLE_MAP_VERSION_F;
binlog_table_maps= 0;
}
@@ -4529,23 +4524,10 @@ void THD::issue_unsafe_warnings()
Ensure that binlog_unsafe_warning_flags is big enough to hold all
bits. This is actually a constant expression.
*/
- DBUG_ASSERT(2 * LEX::BINLOG_STMT_UNSAFE_COUNT <=
+ DBUG_ASSERT(LEX::BINLOG_STMT_UNSAFE_COUNT <=
sizeof(binlog_unsafe_warning_flags) * CHAR_BIT);
uint32 unsafe_type_flags= binlog_unsafe_warning_flags;
-
- /*
- Clear: (1) bits above BINLOG_STMT_UNSAFE_COUNT; (2) bits for
- warnings that have been printed already.
- */
- unsafe_type_flags &= (LEX::BINLOG_STMT_UNSAFE_ALL_FLAGS ^
- (unsafe_type_flags >> LEX::BINLOG_STMT_UNSAFE_COUNT));
- /* If all warnings have been printed already, return. */
- if (unsafe_type_flags == 0)
- DBUG_VOID_RETURN;
-
- DBUG_PRINT("info", ("unsafe_type_flags: 0x%x", unsafe_type_flags));
-
/*
For each unsafe_type, check if the statement is unsafe in this way
and issue a warning.
@@ -4569,12 +4551,6 @@ void THD::issue_unsafe_warnings()
}
}
}
- /*
- Mark these unsafe types as already printed, to avoid printing
- warnings for them again.
- */
- binlog_unsafe_warning_flags|=
- unsafe_type_flags << LEX::BINLOG_STMT_UNSAFE_COUNT;
DBUG_VOID_RETURN;
}
@@ -4628,14 +4604,32 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, char const *query_arg,
/*
Warnings for unsafe statements logged in statement format are
- printed here instead of in decide_logging_format(). This is
- because the warnings should be printed only if the statement is
- actually logged. When executing decide_logging_format(), we cannot
- know for sure if the statement will be logged.
+ printed in three places instead of in decide_logging_format().
+ This is because the warnings should be printed only if the statement
+ is actually logged. When executing decide_logging_format(), we cannot
+ know for sure if the statement will be logged:
+
+ 1 - sp_head::execute_procedure which prints out warnings for calls to
+ stored procedures.
+
+ 2 - sp_head::execute_function which prints out warnings for calls
+ involving functions.
+
+ 3 - THD::binlog_query (here) which prints warning for top level
+ statements not covered by the two cases above: i.e., if not insided a
+ procedure and a function.
+
+ Besides, we should not try to print these warnings if it is not
+ possible to write statements to the binary log as it happens when
+ the execution is inside a function, or generaly speaking, when
+ the variables.option_bits & OPTION_BIN_LOG is false.
+
*/
- if (sql_log_bin_toplevel)
+ if ((variables.option_bits & OPTION_BIN_LOG) &&
+ spcont == NULL && !binlog_evt_union.do_union)
issue_unsafe_warnings();
+
switch (qtype) {
/*
ROW_QUERY_TYPE means that the statement may be logged either in
@@ -4670,7 +4664,6 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, char const *query_arg,
{
Query_log_event qinfo(this, query_arg, query_len, is_trans, direct,
suppress_use, errcode);
- qinfo.flags|= LOG_EVENT_UPDATE_TABLE_MAP_VERSION_F;
/*
Binlog table maps will be irrelevant after a Query_log_event
(they are just removed on the slave side) so after the query