summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorJan Lindström <jplindst@mariadb.org>2014-01-25 11:02:49 +0200
committerJan Lindström <jplindst@mariadb.org>2014-01-25 11:02:49 +0200
commitd43afb8828e358f9c3bb690d0fdcd88b0637f155 (patch)
treef977f3b5fa3c938183510750aecbea31bccc67ef /sql
parentd0f77b83611077344ff29db02ea5593c9da62537 (diff)
parent02765f4c614069ece1f30976848b6299ba6f24bd (diff)
downloadmariadb-git-d43afb8828e358f9c3bb690d0fdcd88b0637f155.tar.gz
Merge MariaDB-10.0.7 revision 3961.
Diffstat (limited to 'sql')
-rw-r--r--sql/client_settings.h1
-rw-r--r--sql/event_scheduler.cc66
-rw-r--r--sql/event_scheduler.h2
-rw-r--r--sql/events.cc7
-rw-r--r--sql/events.h2
-rw-r--r--sql/filesort.cc4
-rw-r--r--sql/ha_ndbcluster_binlog.cc8
-rw-r--r--sql/ha_partition.cc656
-rw-r--r--sql/ha_partition.h12
-rw-r--r--sql/handler.cc18
-rw-r--r--sql/handler.h2
-rw-r--r--sql/hostname.cc2
-rw-r--r--sql/innodb_priv.h2
-rw-r--r--sql/item.cc53
-rw-r--r--sql/item.h11
-rw-r--r--sql/item_cmpfunc.cc49
-rw-r--r--sql/item_cmpfunc.h7
-rw-r--r--sql/item_func.cc4
-rw-r--r--sql/item_func.h5
-rw-r--r--sql/item_strfunc.cc121
-rw-r--r--sql/item_strfunc.h17
-rw-r--r--sql/item_subselect.cc2
-rw-r--r--sql/item_sum.cc2
-rw-r--r--sql/item_timefunc.cc29
-rw-r--r--sql/item_timefunc.h9
-rw-r--r--sql/item_xmlfunc.h2
-rw-r--r--sql/key.cc32
-rw-r--r--sql/key.h4
-rw-r--r--sql/log.cc311
-rw-r--r--sql/log.h7
-rw-r--r--sql/log_event.cc207
-rw-r--r--sql/log_event.h48
-rw-r--r--sql/log_event_old.cc9
-rw-r--r--sql/log_event_old.h3
-rw-r--r--sql/mdl.cc42
-rw-r--r--sql/mdl.h6
-rw-r--r--sql/my_apc.h2
-rw-r--r--sql/mysqld.cc197
-rw-r--r--sql/mysqld.h17
-rw-r--r--sql/opt_range.cc9
-rw-r--r--sql/opt_subselect.cc20
-rw-r--r--sql/opt_table_elimination.cc15
-rw-r--r--sql/partition_element.h8
-rw-r--r--sql/partition_info.cc310
-rw-r--r--sql/partition_info.h44
-rw-r--r--sql/password.c55
-rw-r--r--sql/rpl_constants.h40
-rw-r--r--sql/rpl_gtid.cc193
-rw-r--r--sql/rpl_gtid.h5
-rw-r--r--sql/rpl_rli.cc16
-rw-r--r--sql/rpl_utility.cc81
-rw-r--r--sql/share/errmsg-utf8.txt14
-rw-r--r--sql/slave.cc19
-rw-r--r--sql/sp_head.cc35
-rw-r--r--sql/sql_acl.cc287
-rw-r--r--sql/sql_admin.cc16
-rw-r--r--sql/sql_base.cc85
-rw-r--r--sql/sql_base.h6
-rw-r--r--sql/sql_cache.cc11
-rw-r--r--sql/sql_class.cc55
-rw-r--r--sql/sql_class.h12
-rw-r--r--sql/sql_db.cc5
-rw-r--r--sql/sql_handler.cc3
-rw-r--r--sql/sql_lex.cc25
-rw-r--r--sql/sql_lex.h2
-rw-r--r--sql/sql_manager.cc9
-rw-r--r--sql/sql_parse.cc47
-rw-r--r--sql/sql_partition.cc98
-rw-r--r--sql/sql_partition.h3
-rw-r--r--sql/sql_plugin.cc204
-rw-r--r--sql/sql_plugin.h14
-rw-r--r--sql/sql_plugin_services.h14
-rw-r--r--sql/sql_prepare.cc21
-rw-r--r--sql/sql_repl.cc4
-rw-r--r--sql/sql_repl.h2
-rw-r--r--sql/sql_select.cc118
-rw-r--r--sql/sql_show.cc20
-rw-r--r--sql/sql_table.cc66
-rw-r--r--sql/sql_test.cc41
-rw-r--r--sql/sql_time.cc19
-rw-r--r--sql/sql_time.h1
-rw-r--r--sql/sql_union.cc18
-rw-r--r--sql/sql_view.cc4
-rw-r--r--sql/sql_yacc.yy59
-rw-r--r--sql/sys_vars.cc30
-rw-r--r--sql/table.cc15
-rw-r--r--sql/table.h30
-rw-r--r--sql/table_cache.cc275
-rw-r--r--sql/table_cache.h1
-rw-r--r--sql/transaction.cc50
-rw-r--r--sql/transaction.h1
-rw-r--r--sql/tztime.cc44
92 files changed, 3209 insertions, 1348 deletions
diff --git a/sql/client_settings.h b/sql/client_settings.h
index 5707413f69f..d6a157f71fd 100644
--- a/sql/client_settings.h
+++ b/sql/client_settings.h
@@ -34,6 +34,7 @@
CLIENT_PROTOCOL_41 | \
CLIENT_SECURE_CONNECTION | \
CLIENT_PLUGIN_AUTH | \
+ CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA | \
CLIENT_CONNECT_ATTRS)
#define read_user_name(A) {}
diff --git a/sql/event_scheduler.cc b/sql/event_scheduler.cc
index f75a8abc835..92093e34b81 100644
--- a/sql/event_scheduler.cc
+++ b/sql/event_scheduler.cc
@@ -239,6 +239,12 @@ event_scheduler_thread(void *arg)
my_free(arg);
if (!res)
scheduler->run(thd);
+ else
+ {
+ thd->proc_info= "Clearing";
+ net_end(&thd->net);
+ delete thd;
+ }
DBUG_LEAVE; // Against gcc warnings
my_thread_end();
@@ -366,26 +372,26 @@ Event_scheduler::~Event_scheduler()
}
-/*
+/**
Starts the scheduler (again). Creates a new THD and passes it to
a forked thread. Does not wait for acknowledgement from the new
thread that it has started. Asynchronous starting. Most of the
needed initializations are done in the current thread to minimize
the chance of failure in the spawned thread.
- SYNOPSIS
- Event_scheduler::start()
+ @param[out] err_no - errno indicating type of error which caused
+ failure to start scheduler thread.
- RETURN VALUE
- FALSE OK
- TRUE Error (not reported)
+ @return
+ @retval false Success.
+ @retval true Error.
*/
bool
-Event_scheduler::start()
+Event_scheduler::start(int *err_no)
{
THD *new_thd= NULL;
- bool ret= FALSE;
+ bool ret= false;
pthread_t th;
struct scheduler_param *scheduler_param_value;
DBUG_ENTER("Event_scheduler::start");
@@ -398,7 +404,7 @@ Event_scheduler::start()
if (!(new_thd= new THD))
{
sql_print_error("Event Scheduler: Cannot initialize the scheduler thread");
- ret= TRUE;
+ ret= true;
goto end;
}
@@ -427,21 +433,30 @@ Event_scheduler::start()
DBUG_PRINT("info", ("Setting state go RUNNING"));
state= RUNNING;
DBUG_PRINT("info", ("Forking new thread for scheduler. THD: 0x%lx", (long) new_thd));
- if (mysql_thread_create(key_thread_event_scheduler,
- &th, &connection_attrib, event_scheduler_thread,
- (void*)scheduler_param_value))
+ if ((*err_no= mysql_thread_create(key_thread_event_scheduler,
+ &th, &connection_attrib,
+ event_scheduler_thread,
+ (void*)scheduler_param_value)))
{
DBUG_PRINT("error", ("cannot create a new thread"));
+ sql_print_error("Event scheduler: Failed to start scheduler,"
+ " Can not create thread for event scheduler (errno=%d)",
+ *err_no);
+
+ new_thd->proc_info= "Clearing";
+ DBUG_ASSERT(new_thd->net.buff != 0);
+ net_end(&new_thd->net);
+
state= INITIALIZED;
scheduler_thd= NULL;
- ret= TRUE;
+ delete new_thd;
- new_thd->proc_info= "Clearing";
- delete_running_thd(new_thd);
+ delete scheduler_param_value;
+ ret= true;
}
+
end:
UNLOCK_DATA();
-
DBUG_RETURN(ret);
}
@@ -553,7 +568,20 @@ Event_scheduler::execute_top(Event_queue_element_for_exec *event_name)
if ((res= mysql_thread_create(key_thread_event_worker,
&th, &connection_attrib, event_worker_thread,
event_name)))
+ {
+ mysql_mutex_lock(&LOCK_global_system_variables);
+ Events::opt_event_scheduler= Events::EVENTS_OFF;
+ mysql_mutex_unlock(&LOCK_global_system_variables);
+
+ sql_print_error("Event_scheduler::execute_top: Can not create event worker"
+ " thread (errno=%d). Stopping event scheduler", res);
+
+ new_thd->proc_info= "Clearing";
+ DBUG_ASSERT(new_thd->net.buff != 0);
+ net_end(&new_thd->net);
+
goto error;
+ }
started_events++;
executed_events++; // For SHOW STATUS
@@ -564,10 +592,8 @@ Event_scheduler::execute_top(Event_queue_element_for_exec *event_name)
error:
DBUG_PRINT("error", ("Event_scheduler::execute_top() res: %d", res));
if (new_thd)
- {
- new_thd->proc_info= "Clearing";
- delete_running_thd(new_thd);
- }
+ delete new_thd;
+
delete event_name;
DBUG_RETURN(TRUE);
}
diff --git a/sql/event_scheduler.h b/sql/event_scheduler.h
index 0b160ff49d5..4f6b9349162 100644
--- a/sql/event_scheduler.h
+++ b/sql/event_scheduler.h
@@ -78,7 +78,7 @@ public:
/* State changing methods follow */
bool
- start();
+ start(int *err_no);
bool
stop();
diff --git a/sql/events.cc b/sql/events.cc
index ea62ec4129d..082bd2ec485 100644
--- a/sql/events.cc
+++ b/sql/events.cc
@@ -791,6 +791,7 @@ Events::init(bool opt_noacl_or_bootstrap)
{
THD *thd;
+ int err_no;
bool res= FALSE;
DBUG_ENTER("Events::init");
@@ -871,7 +872,7 @@ Events::init(bool opt_noacl_or_bootstrap)
}
if (event_queue->init_queue(thd) || load_events_from_db(thd) ||
- (opt_event_scheduler == EVENTS_ON && scheduler->start()))
+ (opt_event_scheduler == EVENTS_ON && scheduler->start(&err_no)))
{
sql_print_error("Event Scheduler: Error while loading from disk.");
res= TRUE; /* fatal error: request unireg_abort */
@@ -1033,9 +1034,9 @@ Events::dump_internal_status()
DBUG_VOID_RETURN;
}
-bool Events::start()
+bool Events::start(int *err_no)
{
- return scheduler->start();
+ return scheduler->start(err_no);
}
bool Events::stop()
diff --git a/sql/events.h b/sql/events.h
index 4720c301052..a6480e7241d 100644
--- a/sql/events.h
+++ b/sql/events.h
@@ -83,7 +83,7 @@ public:
/* Protected using LOCK_global_system_variables only. */
static ulong opt_event_scheduler;
static bool check_if_system_tables_error();
- static bool start();
+ static bool start(int *err_no);
static bool stop();
public:
diff --git a/sql/filesort.cc b/sql/filesort.cc
index 8783aa96739..2b0a91bf5d0 100644
--- a/sql/filesort.cc
+++ b/sql/filesort.cc
@@ -1215,13 +1215,13 @@ bool check_if_pq_applicable(Sort_param *param,
if (param->max_rows == HA_POS_ERROR)
{
DBUG_PRINT("info", ("No LIMIT"));
- DBUG_RETURN(NULL);
+ DBUG_RETURN(false);
}
if (param->max_rows + 2 >= UINT_MAX)
{
DBUG_PRINT("info", ("Too large LIMIT"));
- DBUG_RETURN(NULL);
+ DBUG_RETURN(false);
}
ulong num_available_keys=
diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc
index 813c750053a..fc53183ca7a 100644
--- a/sql/ha_ndbcluster_binlog.cc
+++ b/sql/ha_ndbcluster_binlog.cc
@@ -1,4 +1,4 @@
-/* Copyright (c) 2006, 2011, Oracle and/or its affiliates.
+/* Copyright (c) 2006, 2013, Oracle and/or its affiliates.
Copyright (c) 2012, 2013, Monty Proram Ab.
This program is free software; you can redistribute it and/or modify
@@ -2408,6 +2408,12 @@ add_ndb_binlog_index_err:
thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd);
thd->get_stmt_da()->set_overwrite_status(false);
close_thread_tables(thd);
+ /*
+ There should be no need for rolling back transaction due to deadlock
+ (since ndb_binlog_index is non transactional).
+ */
+ DBUG_ASSERT(! thd->transaction_rollback_request);
+
thd->mdl_context.release_transactional_locks();
ndb_binlog_index= 0;
thd->variables.option_bits= saved_options;
diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc
index c5466532c6a..603e0bf59dc 100644
--- a/sql/ha_partition.cc
+++ b/sql/ha_partition.cc
@@ -55,6 +55,8 @@
#include "sql_table.h" // tablename_to_filename
#include "key.h"
#include "sql_plugin.h"
+#include "sql_show.h" // append_identifier
+#include "sql_admin.h" // SQL_ADMIN_MSG_TEXT_SIZE
#include "debug_sync.h"
@@ -66,12 +68,15 @@
#define PAR_NUM_PARTS_OFFSET 8
/* offset to the engines array */
#define PAR_ENGINES_OFFSET 12
-#define PARTITION_ENABLED_TABLE_FLAGS (HA_FILE_BASED | HA_REC_NOT_IN_SEQ)
+#define PARTITION_ENABLED_TABLE_FLAGS (HA_FILE_BASED | \
+ HA_REC_NOT_IN_SEQ | \
+ HA_CAN_REPAIR)
#define PARTITION_DISABLED_TABLE_FLAGS (HA_CAN_GEOMETRY | \
HA_CAN_FULLTEXT | \
HA_DUPLICATE_POS | \
HA_CAN_SQL_HANDLER | \
- HA_CAN_INSERT_DELAYED)
+ HA_CAN_INSERT_DELAYED | \
+ HA_READ_BEFORE_WRITE_REMOVAL)
static const char *ha_par_ext= ".par";
/****************************************************************************
@@ -348,6 +353,7 @@ void ha_partition::init_handler_variables()
m_rec_length= 0;
m_last_part= 0;
m_rec0= 0;
+ m_err_rec= NULL;
m_curr_key_info[0]= NULL;
m_curr_key_info[1]= NULL;
m_part_func_monotonicity_info= NON_MONOTONIC;
@@ -1181,10 +1187,11 @@ int ha_partition::preload_keys(THD *thd, HA_CHECK_OPT *check_opt)
0 Success
*/
-static int handle_opt_part(THD *thd, HA_CHECK_OPT *check_opt,
- handler *file, uint flag)
+int ha_partition::handle_opt_part(THD *thd, HA_CHECK_OPT *check_opt,
+ uint part_id, uint flag)
{
int error;
+ handler *file= m_file[part_id];
DBUG_ENTER("handle_opt_part");
DBUG_PRINT("enter", ("flag = %u", flag));
@@ -1193,9 +1200,27 @@ static int handle_opt_part(THD *thd, HA_CHECK_OPT *check_opt,
else if (flag == ANALYZE_PARTS)
error= file->ha_analyze(thd, check_opt);
else if (flag == CHECK_PARTS)
+ {
error= file->ha_check(thd, check_opt);
+ if (!error ||
+ error == HA_ADMIN_ALREADY_DONE ||
+ error == HA_ADMIN_NOT_IMPLEMENTED)
+ {
+ if (check_opt->flags & (T_MEDIUM | T_EXTEND))
+ error= check_misplaced_rows(part_id, false);
+ }
+ }
else if (flag == REPAIR_PARTS)
+ {
error= file->ha_repair(thd, check_opt);
+ if (!error ||
+ error == HA_ADMIN_ALREADY_DONE ||
+ error == HA_ADMIN_NOT_IMPLEMENTED)
+ {
+ if (check_opt->flags & (T_MEDIUM | T_EXTEND))
+ error= check_misplaced_rows(part_id, true);
+ }
+ }
else if (flag == ASSIGN_KEYCACHE_PARTS)
error= file->assign_to_keycache(thd, check_opt);
else if (flag == PRELOAD_KEYS_PARTS)
@@ -1216,30 +1241,38 @@ static int handle_opt_part(THD *thd, HA_CHECK_OPT *check_opt,
(modelled after mi_check_print_msg)
TODO: move this into the handler, or rewrite mysql_admin_table.
*/
-static bool print_admin_msg(THD* thd, const char* msg_type,
+static bool print_admin_msg(THD* thd, uint len,
+ const char* msg_type,
const char* db_name, String &table_name,
const char* op_name, const char *fmt, ...)
- ATTRIBUTE_FORMAT(printf, 6, 7);
-static bool print_admin_msg(THD* thd, const char* msg_type,
+ ATTRIBUTE_FORMAT(printf, 7, 8);
+static bool print_admin_msg(THD* thd, uint len,
+ const char* msg_type,
const char* db_name, String &table_name,
const char* op_name, const char *fmt, ...)
{
va_list args;
Protocol *protocol= thd->protocol;
- uint length, msg_length;
- char msgbuf[MYSQL_ERRMSG_SIZE];
- char name[SAFE_NAME_LEN*2+2];
-
+ uint length;
+ uint msg_length;
+ char name[NAME_LEN*2+2];
+ char *msgbuf;
+ bool error= true;
+
+ if (!(msgbuf= (char*) my_malloc(len, MYF(0))))
+ return true;
va_start(args, fmt);
- msg_length= my_vsnprintf(msgbuf, sizeof(msgbuf), fmt, args);
+ msg_length= my_vsnprintf(msgbuf, len, fmt, args);
va_end(args);
- msgbuf[sizeof(msgbuf) - 1] = 0; // healthy paranoia
+ if (msg_length >= (len - 1))
+ goto err;
+ msgbuf[len - 1] = 0; // healthy paranoia
if (!thd->vio_ok())
{
- sql_print_error(fmt, args);
- return TRUE;
+ sql_print_error("%s", msgbuf);
+ goto err;
}
length=(uint) (strxmov(name, db_name, ".", table_name.c_ptr_safe(), NullS) - name);
@@ -1262,9 +1295,12 @@ static bool print_admin_msg(THD* thd, const char* msg_type,
{
sql_print_error("Failed on my_net_write, writing to stderr instead: %s\n",
msgbuf);
- return TRUE;
+ goto err;
}
- return FALSE;
+ error= false;
+err:
+ my_free(msgbuf);
+ return error;
}
@@ -1314,14 +1350,15 @@ int ha_partition::handle_opt_partitions(THD *thd, HA_CHECK_OPT *check_opt,
part= i * num_subparts + j;
DBUG_PRINT("info", ("Optimize subpartition %u (%s)",
part, sub_elem->partition_name));
- if ((error= handle_opt_part(thd, check_opt, m_file[part], flag)))
+ if ((error= handle_opt_part(thd, check_opt, part, flag)))
{
/* print a line which partition the error belongs to */
if (error != HA_ADMIN_NOT_IMPLEMENTED &&
error != HA_ADMIN_ALREADY_DONE &&
error != HA_ADMIN_TRY_ALTER)
{
- print_admin_msg(thd, "error", table_share->db.str, table->alias,
+ print_admin_msg(thd, MYSQL_ERRMSG_SIZE, "error",
+ table_share->db.str, table->alias,
opt_op_name[flag],
"Subpartition %s returned error",
sub_elem->partition_name);
@@ -1340,14 +1377,15 @@ int ha_partition::handle_opt_partitions(THD *thd, HA_CHECK_OPT *check_opt,
{
DBUG_PRINT("info", ("Optimize partition %u (%s)", i,
part_elem->partition_name));
- if ((error= handle_opt_part(thd, check_opt, m_file[i], flag)))
+ if ((error= handle_opt_part(thd, check_opt, i, flag)))
{
/* print a line which partition the error belongs to */
if (error != HA_ADMIN_NOT_IMPLEMENTED &&
error != HA_ADMIN_ALREADY_DONE &&
error != HA_ADMIN_TRY_ALTER)
{
- print_admin_msg(thd, "error", table_share->db.str, table->alias,
+ print_admin_msg(thd, MYSQL_ERRMSG_SIZE, "error",
+ table_share->db.str, table->alias,
opt_op_name[flag], "Partition %s returned error",
part_elem->partition_name);
}
@@ -2025,8 +2063,7 @@ void ha_partition::update_create_info(HA_CREATE_INFO *create_info)
my_bool from_alter = (create_info->data_file_name == (const char*) -1);
create_info->data_file_name= create_info->index_file_name = NULL;
- create_info->connect_string.str= NULL;
- create_info->connect_string.length= 0;
+ create_info->connect_string= null_lex_str;
/*
We do not need to update the individual partition DATA DIRECTORY settings
@@ -2984,7 +3021,7 @@ bool ha_partition::setup_engine_array(MEM_ROOT *mem_root)
}
}
- my_afree((void*) engine_array);
+ my_afree(engine_array);
if (create_handlers(mem_root))
{
@@ -2995,7 +3032,7 @@ bool ha_partition::setup_engine_array(MEM_ROOT *mem_root)
DBUG_RETURN(false);
err:
- my_afree((void*) engine_array);
+ my_afree(engine_array);
DBUG_RETURN(true);
}
@@ -4137,7 +4174,7 @@ exit:
Called from sql_select.cc, sql_acl.cc, sql_update.cc, and sql_insert.cc.
new_data is always record[0]
- old_data is normally record[1] but may be anything
+ old_data is always record[1]
*/
int ha_partition::update_row(const uchar *old_data, uchar *new_data)
@@ -4147,6 +4184,7 @@ int ha_partition::update_row(const uchar *old_data, uchar *new_data)
int error= 0;
longlong func_value;
DBUG_ENTER("ha_partition::update_row");
+ m_err_rec= NULL;
// Need to read partition-related columns, to locate the row's partition:
DBUG_ASSERT(bitmap_is_subset(&m_part_info->full_part_field_set,
@@ -4164,6 +4202,30 @@ int ha_partition::update_row(const uchar *old_data, uchar *new_data)
error= HA_ERR_NOT_IN_LOCK_PARTITIONS;
goto exit;
}
+
+ /*
+ The protocol for updating a row is:
+ 1) position the handler (cursor) on the row to be updated,
+ either through the last read row (rnd or index) or by rnd_pos.
+ 2) call update_row with both old and new full records as arguments.
+
+ This means that m_last_part should already be set to actual partition
+ where the row was read from. And if that is not the same as the
+ calculated part_id we found a misplaced row, we return an error to
+ notify the user that something is broken in the row distribution
+ between partitions! Since we don't check all rows on read, we return an
+ error instead of correcting m_last_part, to make the user aware of the
+ problem!
+
+ Notice that HA_READ_BEFORE_WRITE_REMOVAL does not require this protocol,
+ so this is not supported for this engine.
+ */
+ if (old_part_id != m_last_part)
+ {
+ m_err_rec= old_data;
+ DBUG_RETURN(HA_ERR_ROW_IN_WRONG_PARTITION);
+ }
+
m_last_part= new_part_id;
start_part_bulk_insert(thd, new_part_id);
if (new_part_id == old_part_id)
@@ -4267,6 +4329,7 @@ int ha_partition::delete_row(const uchar *buf)
int error;
THD *thd= ha_thd();
DBUG_ENTER("ha_partition::delete_row");
+ m_err_rec= NULL;
DBUG_ASSERT(bitmap_is_subset(&m_part_info->full_part_field_set,
table->read_set));
@@ -4274,12 +4337,39 @@ int ha_partition::delete_row(const uchar *buf)
{
DBUG_RETURN(error);
}
- m_last_part= part_id;
/* Should never call delete_row on a partition which is not read */
DBUG_ASSERT(bitmap_is_set(&(m_part_info->read_partitions), part_id));
DBUG_ASSERT(bitmap_is_set(&(m_part_info->lock_partitions), part_id));
if (!bitmap_is_set(&(m_part_info->lock_partitions), part_id))
DBUG_RETURN(HA_ERR_NOT_IN_LOCK_PARTITIONS);
+
+ /*
+ The protocol for deleting a row is:
+ 1) position the handler (cursor) on the row to be deleted,
+ either through the last read row (rnd or index) or by rnd_pos.
+ 2) call delete_row with the full record as argument.
+
+ This means that m_last_part should already be set to actual partition
+ where the row was read from. And if that is not the same as the
+ calculated part_id we found a misplaced row, we return an error to
+ notify the user that something is broken in the row distribution
+ between partitions! Since we don't check all rows on read, we return an
+ error instead of forwarding the delete to the correct (m_last_part)
+ partition!
+
+ Notice that HA_READ_BEFORE_WRITE_REMOVAL does not require this protocol,
+ so this is not supported for this engine.
+
+ TODO: change the assert in InnoDB into an error instead and make this one
+ an assert instead and remove the get_part_for_delete()!
+ */
+ if (part_id != m_last_part)
+ {
+ m_err_rec= buf;
+ DBUG_RETURN(HA_ERR_ROW_IN_WRONG_PARTITION);
+ }
+
+ m_last_part= part_id;
tmp_disable_binlog(thd);
error= m_file[part_id]->ha_delete_row(buf);
reenable_binlog(thd);
@@ -4402,7 +4492,7 @@ int ha_partition::truncate_partition(Alter_info *alter_info, bool *binlog_stmt)
{
List_iterator<partition_element>
subpart_it(part_elem->subpartitions);
- partition_element *sub_elem __attribute__((unused));
+ partition_element *sub_elem;
uint j= 0, part;
do
{
@@ -4916,7 +5006,7 @@ int ha_partition::rnd_pos(uchar * buf, uchar *pos)
file= m_file[part_id];
DBUG_ASSERT(bitmap_is_set(&(m_part_info->read_partitions), part_id));
m_last_part= part_id;
- DBUG_RETURN(file->rnd_pos(buf, (pos + PARTITION_BYTES_IN_POS)));
+ DBUG_RETURN(file->ha_rnd_pos(buf, (pos + PARTITION_BYTES_IN_POS)));
}
@@ -6558,7 +6648,7 @@ void ha_partition::get_dynamic_partition_info(PARTITION_STATS *stat_info,
{
handler *file= m_file[part_id];
DBUG_ASSERT(bitmap_is_set(&(m_part_info->read_partitions), part_id));
- file->info(HA_STATUS_CONST | HA_STATUS_TIME | HA_STATUS_VARIABLE |
+ file->info(HA_STATUS_TIME | HA_STATUS_VARIABLE |
HA_STATUS_VARIABLE_EXTRA | HA_STATUS_NO_LOCK);
stat_info->records= file->stats.records;
@@ -7498,7 +7588,6 @@ double ha_partition::read_time(uint index, uint ranges, ha_rows rows)
/**
Number of rows in table. see handler.h
-
@return Number of records in the table (after pruning!)
*/
@@ -7582,10 +7671,89 @@ uint32 ha_partition::calculate_key_hash_value(Field **field_array)
{
ulong nr1= 1;
ulong nr2= 4;
+ bool use_51_hash;
+ use_51_hash= test((*field_array)->table->part_info->key_algorithm ==
+ partition_info::KEY_ALGORITHM_51);
do
{
Field *field= *field_array;
+ if (use_51_hash)
+ {
+ switch (field->real_type()) {
+ case MYSQL_TYPE_TINY:
+ case MYSQL_TYPE_SHORT:
+ case MYSQL_TYPE_LONG:
+ case MYSQL_TYPE_FLOAT:
+ case MYSQL_TYPE_DOUBLE:
+ case MYSQL_TYPE_NEWDECIMAL:
+ case MYSQL_TYPE_TIMESTAMP:
+ case MYSQL_TYPE_LONGLONG:
+ case MYSQL_TYPE_INT24:
+ case MYSQL_TYPE_TIME:
+ case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_YEAR:
+ case MYSQL_TYPE_NEWDATE:
+ {
+ if (field->is_null())
+ {
+ nr1^= (nr1 << 1) | 1;
+ continue;
+ }
+ /* Force this to my_hash_sort_bin, which was used in 5.1! */
+ uint len= field->pack_length();
+ my_charset_bin.coll->hash_sort(&my_charset_bin, field->ptr, len,
+ &nr1, &nr2);
+ /* Done with this field, continue with next one. */
+ continue;
+ }
+ case MYSQL_TYPE_STRING:
+ case MYSQL_TYPE_VARCHAR:
+ case MYSQL_TYPE_BIT:
+ /* Not affected, same in 5.1 and 5.5 */
+ break;
+ /*
+ ENUM/SET uses my_hash_sort_simple in 5.1 (i.e. my_charset_latin1)
+ and my_hash_sort_bin in 5.5!
+ */
+ case MYSQL_TYPE_ENUM:
+ case MYSQL_TYPE_SET:
+ {
+ if (field->is_null())
+ {
+ nr1^= (nr1 << 1) | 1;
+ continue;
+ }
+ /* Force this to my_hash_sort_bin, which was used in 5.1! */
+ uint len= field->pack_length();
+ my_charset_latin1.coll->hash_sort(&my_charset_latin1, field->ptr,
+ len, &nr1, &nr2);
+ continue;
+ }
+ /* New types in mysql-5.6. */
+ case MYSQL_TYPE_DATETIME2:
+ case MYSQL_TYPE_TIME2:
+ case MYSQL_TYPE_TIMESTAMP2:
+ /* Not affected, 5.6+ only! */
+ break;
+
+ /* These types should not be allowed for partitioning! */
+ case MYSQL_TYPE_NULL:
+ case MYSQL_TYPE_DECIMAL:
+ case MYSQL_TYPE_DATE:
+ case MYSQL_TYPE_TINY_BLOB:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_LONG_BLOB:
+ case MYSQL_TYPE_BLOB:
+ case MYSQL_TYPE_VAR_STRING:
+ case MYSQL_TYPE_GEOMETRY:
+ /* fall through. */
+ default:
+ DBUG_ASSERT(0); // New type?
+ /* Fall through for default hashing (5.5). */
+ }
+ /* fall through, use collation based hashing. */
+ }
field->hash(&nr1, &nr2);
} while (*(++field_array));
return (uint32) nr1;
@@ -7641,6 +7809,57 @@ enum row_type ha_partition::get_row_type() const
}
+void ha_partition::append_row_to_str(String &str)
+{
+ const uchar *rec;
+ bool is_rec0= !m_err_rec || m_err_rec == table->record[0];
+ if (is_rec0)
+ rec= table->record[0];
+ else
+ rec= m_err_rec;
+ // If PK, use full PK instead of full part field array!
+ if (table->s->primary_key != MAX_KEY)
+ {
+ KEY *key= table->key_info + table->s->primary_key;
+ KEY_PART_INFO *key_part= key->key_part;
+ KEY_PART_INFO *key_part_end= key_part + key->user_defined_key_parts;
+ if (!is_rec0)
+ set_key_field_ptr(key, rec, table->record[0]);
+ for (; key_part != key_part_end; key_part++)
+ {
+ Field *field= key_part->field;
+ str.append(" ");
+ str.append(field->field_name);
+ str.append(":");
+ field_unpack(&str, field, rec, 0, false);
+ }
+ if (!is_rec0)
+ set_key_field_ptr(key, table->record[0], rec);
+ }
+ else
+ {
+ Field **field_ptr;
+ if (!is_rec0)
+ set_field_ptr(m_part_info->full_part_field_array, rec,
+ table->record[0]);
+ /* No primary key, use full partition field array. */
+ for (field_ptr= m_part_info->full_part_field_array;
+ *field_ptr;
+ field_ptr++)
+ {
+ Field *field= *field_ptr;
+ str.append(" ");
+ str.append(field->field_name);
+ str.append(":");
+ field_unpack(&str, field, rec, 0, false);
+ }
+ if (!is_rec0)
+ set_field_ptr(m_part_info->full_part_field_array, table->record[0],
+ rec);
+ }
+}
+
+
void ha_partition::print_error(int error, myf errflag)
{
THD *thd= ha_thd();
@@ -7652,21 +7871,62 @@ void ha_partition::print_error(int error, myf errflag)
if ((error == HA_ERR_NO_PARTITION_FOUND) &&
! (thd->lex->alter_info.flags & Alter_info::ALTER_TRUNCATE_PARTITION))
m_part_info->print_no_partition_found(table);
- else
- {
- /* In case m_file has not been initialized, like in bug#42438 */
- if (m_file)
+ else if (error == HA_ERR_ROW_IN_WRONG_PARTITION)
+ {
+ /* Should only happen on DELETE or UPDATE! */
+ DBUG_ASSERT(thd_sql_command(thd) == SQLCOM_DELETE ||
+ thd_sql_command(thd) == SQLCOM_DELETE_MULTI ||
+ thd_sql_command(thd) == SQLCOM_UPDATE ||
+ thd_sql_command(thd) == SQLCOM_UPDATE_MULTI);
+ DBUG_ASSERT(m_err_rec);
+ if (m_err_rec)
{
- if (m_last_part >= m_tot_parts)
+ uint max_length;
+ char buf[MAX_KEY_LENGTH];
+ String str(buf,sizeof(buf),system_charset_info);
+ uint32 part_id;
+ str.length(0);
+ str.append("(");
+ str.append_ulonglong(m_last_part);
+ str.append(" != ");
+ if (get_part_for_delete(m_err_rec, m_rec0, m_part_info, &part_id))
+ str.append("?");
+ else
+ str.append_ulonglong(part_id);
+ str.append(")");
+ append_row_to_str(str);
+
+ /* Log this error, so the DBA can notice it and fix it! */
+ sql_print_error("Table '%-192s' corrupted: row in wrong partition: %s\n"
+ "Please REPAIR the table!",
+ table->s->table_name.str,
+ str.c_ptr_safe());
+
+ max_length= (MYSQL_ERRMSG_SIZE - (uint) strlen(ER(ER_ROW_IN_WRONG_PARTITION)));
+ if (str.length() >= max_length)
{
- DBUG_ASSERT(0);
- m_last_part= 0;
+ str.length(max_length-4);
+ str.append(STRING_WITH_LEN("..."));
}
- m_file[m_last_part]->print_error(error, errflag);
+ my_error(ER_ROW_IN_WRONG_PARTITION, MYF(0), str.c_ptr_safe());
+ m_err_rec= NULL;
+ DBUG_VOID_RETURN;
}
- else
- handler::print_error(error, errflag);
+ /* fall through to generic error handling. */
+ }
+
+ /* In case m_file has not been initialized, like in bug#42438 */
+ if (m_file)
+ {
+ if (m_last_part >= m_tot_parts)
+ {
+ DBUG_ASSERT(0);
+ m_last_part= 0;
+ }
+ m_file[m_last_part]->print_error(error, errflag);
}
+ else
+ handler::print_error(error, errflag);
DBUG_VOID_RETURN;
}
@@ -7804,6 +8064,13 @@ ha_partition::check_if_supported_inplace_alter(TABLE *altered_table,
#endif
DBUG_ENTER("ha_partition::check_if_supported_inplace_alter");
+ /*
+ Support inplace change of KEY () -> KEY ALGORITHM = N ().
+ Any other change would set partition_changed in
+ prep_alter_part_table() in mysql_alter_table().
+ */
+ if (ha_alter_info->alter_info->flags == Alter_info::ALTER_PARTITION)
+ DBUG_RETURN(HA_ALTER_INPLACE_NO_LOCK);
#ifndef PARTITION_SUPPORTS_INPLACE_ALTER
/*
@@ -7861,6 +8128,13 @@ bool ha_partition::prepare_inplace_alter_table(TABLE *altered_table,
DBUG_ENTER("ha_partition::prepare_inplace_alter_table");
+ /*
+ Changing to similar partitioning, only update metadata.
+ Non allowed changes would be catched in prep_alter_part_table().
+ */
+ if (ha_alter_info->alter_info->flags == Alter_info::ALTER_PARTITION)
+ DBUG_RETURN(false);
+
part_inplace_ctx=
static_cast<class ha_partition_inplace_ctx*>(ha_alter_info->handler_ctx);
@@ -7887,6 +8161,13 @@ bool ha_partition::inplace_alter_table(TABLE *altered_table,
DBUG_ENTER("ha_partition::inplace_alter_table");
+ /*
+ Changing to similar partitioning, only update metadata.
+ Non allowed changes would be catched in prep_alter_part_table().
+ */
+ if (ha_alter_info->alter_info->flags == Alter_info::ALTER_PARTITION)
+ DBUG_RETURN(false);
+
part_inplace_ctx=
static_cast<class ha_partition_inplace_ctx*>(ha_alter_info->handler_ctx);
@@ -7920,6 +8201,13 @@ bool ha_partition::commit_inplace_alter_table(TABLE *altered_table,
DBUG_ENTER("ha_partition::commit_inplace_alter_table");
+ /*
+ Changing to similar partitioning, only update metadata.
+ Non allowed changes would be catched in prep_alter_part_table().
+ */
+ if (ha_alter_info->alter_info->flags == Alter_info::ALTER_PARTITION)
+ DBUG_RETURN(false);
+
part_inplace_ctx=
static_cast<class ha_partition_inplace_ctx*>(ha_alter_info->handler_ctx);
@@ -8236,7 +8524,7 @@ void ha_partition::get_auto_increment(ulonglong offset, ulonglong increment,
/* Only nb_desired_values = 1 makes sense */
(*file)->get_auto_increment(offset, increment, 1,
&first_value_part, &nb_reserved_values_part);
- if (first_value_part == ~(ulonglong)(0)) // error in one partition
+ if (first_value_part == ULONGLONG_MAX) // error in one partition
{
*first_value= first_value_part;
/* log that the error was between table/partition handler */
@@ -8456,6 +8744,290 @@ int ha_partition::indexes_are_disabled(void)
}
+/**
+ Check/fix misplaced rows.
+
+ @param read_part_id Partition to check/fix.
+ @param repair If true, move misplaced rows to correct partition.
+
+ @return Operation status.
+ @retval 0 Success
+ @retval != 0 Error
+*/
+
+int ha_partition::check_misplaced_rows(uint read_part_id, bool repair)
+{
+ int result= 0;
+ uint32 correct_part_id;
+ longlong func_value;
+ longlong num_misplaced_rows= 0;
+
+ DBUG_ENTER("ha_partition::check_misplaced_rows");
+
+ DBUG_ASSERT(m_file);
+
+ if (repair)
+ {
+ /* We must read the full row, if we need to move it! */
+ bitmap_set_all(table->read_set);
+ bitmap_set_all(table->write_set);
+ }
+ else
+ {
+ /* Only need to read the partitioning fields. */
+ bitmap_union(table->read_set, &m_part_info->full_part_field_set);
+ }
+
+ if ((result= m_file[read_part_id]->ha_rnd_init(1)))
+ DBUG_RETURN(result);
+
+ while (true)
+ {
+ if ((result= m_file[read_part_id]->ha_rnd_next(m_rec0)))
+ {
+ if (result == HA_ERR_RECORD_DELETED)
+ continue;
+ if (result != HA_ERR_END_OF_FILE)
+ break;
+
+ if (num_misplaced_rows > 0)
+ {
+ print_admin_msg(ha_thd(), MYSQL_ERRMSG_SIZE, "warning",
+ table_share->db.str, table->alias,
+ opt_op_name[REPAIR_PARTS],
+ "Moved %lld misplaced rows",
+ num_misplaced_rows);
+ }
+ /* End-of-file reached, all rows are now OK, reset result and break. */
+ result= 0;
+ break;
+ }
+
+ result= m_part_info->get_partition_id(m_part_info, &correct_part_id,
+ &func_value);
+ if (result)
+ break;
+
+ if (correct_part_id != read_part_id)
+ {
+ num_misplaced_rows++;
+ if (!repair)
+ {
+ /* Check. */
+ print_admin_msg(ha_thd(), MYSQL_ERRMSG_SIZE, "error",
+ table_share->db.str, table->alias,
+ opt_op_name[CHECK_PARTS],
+ "Found a misplaced row");
+ /* Break on first misplaced row! */
+ result= HA_ADMIN_NEEDS_UPGRADE;
+ break;
+ }
+ else
+ {
+ DBUG_PRINT("info", ("Moving row from partition %d to %d",
+ read_part_id, correct_part_id));
+
+ /*
+ Insert row into correct partition. Notice that there are no commit
+ for every N row, so the repair will be one large transaction!
+ */
+ if ((result= m_file[correct_part_id]->ha_write_row(m_rec0)))
+ {
+ /*
+ We have failed to insert a row, it might have been a duplicate!
+ */
+ char buf[MAX_KEY_LENGTH];
+ String str(buf,sizeof(buf),system_charset_info);
+ str.length(0);
+ if (result == HA_ERR_FOUND_DUPP_KEY)
+ {
+ str.append("Duplicate key found, "
+ "please update or delete the record:\n");
+ result= HA_ADMIN_CORRUPT;
+ }
+ m_err_rec= NULL;
+ append_row_to_str(str);
+
+ /*
+ If the engine supports transactions, the failure will be
+ rollbacked.
+ */
+ if (!m_file[correct_part_id]->has_transactions())
+ {
+ /* Log this error, so the DBA can notice it and fix it! */
+ sql_print_error("Table '%-192s' failed to move/insert a row"
+ " from part %d into part %d:\n%s",
+ table->s->table_name.str,
+ read_part_id,
+ correct_part_id,
+ str.c_ptr_safe());
+ }
+ print_admin_msg(ha_thd(), MYSQL_ERRMSG_SIZE, "error",
+ table_share->db.str, table->alias,
+ opt_op_name[REPAIR_PARTS],
+ "Failed to move/insert a row"
+ " from part %d into part %d:\n%s",
+ read_part_id,
+ correct_part_id,
+ str.c_ptr_safe());
+ break;
+ }
+
+ /* Delete row from wrong partition. */
+ if ((result= m_file[read_part_id]->ha_delete_row(m_rec0)))
+ {
+ if (m_file[correct_part_id]->has_transactions())
+ break;
+ /*
+ We have introduced a duplicate, since we failed to remove it
+ from the wrong partition.
+ */
+ char buf[MAX_KEY_LENGTH];
+ String str(buf,sizeof(buf),system_charset_info);
+ str.length(0);
+ m_err_rec= NULL;
+ append_row_to_str(str);
+
+ /* Log this error, so the DBA can notice it and fix it! */
+ sql_print_error("Table '%-192s': Delete from part %d failed with"
+ " error %d. But it was already inserted into"
+ " part %d, when moving the misplaced row!"
+ "\nPlease manually fix the duplicate row:\n%s",
+ table->s->table_name.str,
+ read_part_id,
+ result,
+ correct_part_id,
+ str.c_ptr_safe());
+ break;
+ }
+ }
+ }
+ }
+
+ int tmp_result= m_file[read_part_id]->ha_rnd_end();
+ DBUG_RETURN(result ? result : tmp_result);
+}
+
+
+#define KEY_PARTITIONING_CHANGED_STR \
+ "KEY () partitioning changed, please run:\n" \
+ "ALTER TABLE %s.%s ALGORITHM = INPLACE %s"
+
+int ha_partition::check_for_upgrade(HA_CHECK_OPT *check_opt)
+{
+ int error= HA_ADMIN_NEEDS_CHECK;
+ DBUG_ENTER("ha_partition::check_for_upgrade");
+
+ /*
+ This is called even without FOR UPGRADE,
+ if the .frm version is lower than the current version.
+ In that case return that it needs checking!
+ */
+ if (!(check_opt->sql_flags & TT_FOR_UPGRADE))
+ DBUG_RETURN(error);
+
+ /*
+ Partitions will be checked for during their ha_check!
+
+ Check if KEY (sub)partitioning was used and any field's hash calculation
+ differs from 5.1, see bug#14521864.
+ */
+ if (table->s->mysql_version < 50503 && // 5.1 table (<5.5.3)
+ ((m_part_info->part_type == HASH_PARTITION && // KEY partitioned
+ m_part_info->list_of_part_fields) ||
+ (m_is_sub_partitioned && // KEY subpartitioned
+ m_part_info->list_of_subpart_fields)))
+ {
+ Field **field;
+ if (m_is_sub_partitioned)
+ {
+ field= m_part_info->subpart_field_array;
+ }
+ else
+ {
+ field= m_part_info->part_field_array;
+ }
+ for (; *field; field++)
+ {
+ switch ((*field)->real_type()) {
+ case MYSQL_TYPE_TINY:
+ case MYSQL_TYPE_SHORT:
+ case MYSQL_TYPE_LONG:
+ case MYSQL_TYPE_FLOAT:
+ case MYSQL_TYPE_DOUBLE:
+ case MYSQL_TYPE_NEWDECIMAL:
+ case MYSQL_TYPE_TIMESTAMP:
+ case MYSQL_TYPE_LONGLONG:
+ case MYSQL_TYPE_INT24:
+ case MYSQL_TYPE_TIME:
+ case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_YEAR:
+ case MYSQL_TYPE_NEWDATE:
+ case MYSQL_TYPE_ENUM:
+ case MYSQL_TYPE_SET:
+ {
+ THD *thd= ha_thd();
+ char *part_buf;
+ String db_name, table_name;
+ uint part_buf_len;
+ bool skip_generation= false;
+ partition_info::enum_key_algorithm old_algorithm;
+ old_algorithm= m_part_info->key_algorithm;
+ error= HA_ADMIN_FAILED;
+ append_identifier(ha_thd(), &db_name, table_share->db.str,
+ table_share->db.length);
+ append_identifier(ha_thd(), &table_name, table_share->table_name.str,
+ table_share->table_name.length);
+ if (m_part_info->key_algorithm != partition_info::KEY_ALGORITHM_NONE)
+ {
+ /*
+ Only possible when someone tampered with .frm files,
+ like during tests :)
+ */
+ skip_generation= true;
+ }
+ m_part_info->key_algorithm= partition_info::KEY_ALGORITHM_51;
+ if (skip_generation ||
+ !(part_buf= generate_partition_syntax(m_part_info,
+ &part_buf_len,
+ true,
+ true,
+ NULL,
+ NULL,
+ NULL)) ||
+ print_admin_msg(thd, SQL_ADMIN_MSG_TEXT_SIZE + 1, "error",
+ table_share->db.str,
+ table->alias,
+ opt_op_name[CHECK_PARTS],
+ KEY_PARTITIONING_CHANGED_STR,
+ db_name.c_ptr_safe(),
+ table_name.c_ptr_safe(),
+ part_buf))
+ {
+ /* Error creating admin message (too long string?). */
+ print_admin_msg(thd, MYSQL_ERRMSG_SIZE, "error",
+ table_share->db.str, table->alias,
+ opt_op_name[CHECK_PARTS],
+ KEY_PARTITIONING_CHANGED_STR,
+ db_name.c_ptr_safe(), table_name.c_ptr_safe(),
+ "<old partition clause>, but add ALGORITHM = 1"
+ " between 'KEY' and '(' to change the metadata"
+ " without the need of a full table rebuild.");
+ }
+ m_part_info->key_algorithm= old_algorithm;
+ DBUG_RETURN(error);
+ }
+ default:
+ /* Not affected! */
+ ;
+ }
+ }
+ }
+
+ DBUG_RETURN(error);
+}
+
+
struct st_mysql_storage_engine partition_storage_engine=
{ MYSQL_HANDLERTON_INTERFACE_VERSION };
diff --git a/sql/ha_partition.h b/sql/ha_partition.h
index b19f2f2ad2b..7407388b7b2 100644
--- a/sql/ha_partition.h
+++ b/sql/ha_partition.h
@@ -24,7 +24,7 @@
enum partition_keywords
{
PKW_HASH= 0, PKW_RANGE, PKW_LIST, PKW_KEY, PKW_MAXVALUE, PKW_LINEAR,
- PKW_COLUMNS
+ PKW_COLUMNS, PKW_ALGORITHM
};
@@ -155,6 +155,7 @@ private:
*/
KEY *m_curr_key_info[3]; // Current index
uchar *m_rec0; // table->record[0]
+ const uchar *m_err_rec; // record which gave error
QUEUE m_queue; // Prio queue used by sorted read
/*
Since the partition handler is a handler on top of other handlers, it
@@ -1188,9 +1189,18 @@ public:
virtual bool check_and_repair(THD *thd);
virtual bool auto_repair(int error) const;
virtual bool is_crashed() const;
+ virtual int check_for_upgrade(HA_CHECK_OPT *check_opt);
private:
int handle_opt_partitions(THD *thd, HA_CHECK_OPT *check_opt, uint flags);
+ int handle_opt_part(THD *thd, HA_CHECK_OPT *check_opt, uint part_id,
+ uint flag);
+ /**
+ Check if the rows are placed in the correct partition. If the given
+ argument is true, then move the rows to the correct partition.
+ */
+ int check_misplaced_rows(uint read_part_id, bool repair);
+ void append_row_to_str(String &str);
public:
/*
diff --git a/sql/handler.cc b/sql/handler.cc
index 4816f5f6ae0..4c797259e2c 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -1620,10 +1620,16 @@ int ha_rollback_trans(THD *thd, bool all)
}
trans->ha_list= 0;
trans->no_2pc=0;
- if (is_real_trans && thd->transaction_rollback_request &&
- thd->transaction.xid_state.xa_state != XA_NOTR)
- thd->transaction.xid_state.rm_error= thd->get_stmt_da()->sql_errno();
}
+
+ /*
+ Thanks to possibility of MDL deadlock rollback request can come even if
+ transaction hasn't been started in any transactional storage engine.
+ */
+ if (is_real_trans && thd->transaction_rollback_request &&
+ thd->transaction.xid_state.xa_state != XA_NOTR)
+ thd->transaction.xid_state.rm_error= thd->get_stmt_da()->sql_errno();
+
/* Always cleanup. Even if nht==0. There may be savepoints. */
if (is_real_trans)
{
@@ -5931,6 +5937,7 @@ int handler::ha_update_row(const uchar *old_data, uchar *new_data)
(and the old record is in record[1]).
*/
DBUG_ASSERT(new_data == table->record[0]);
+ DBUG_ASSERT(old_data == table->record[1]);
MYSQL_UPDATE_ROW_START(table_share->db.str, table_share->table_name.str);
mark_trx_read_write();
@@ -5954,6 +5961,11 @@ int handler::ha_delete_row(const uchar *buf)
Log_func *log_func= Delete_rows_log_event::binlog_row_logging_function;
DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
m_lock_type == F_WRLCK);
+ /*
+ Normally table->record[0] is used, but sometimes table->record[1] is used.
+ */
+ DBUG_ASSERT(buf == table->record[0] ||
+ buf == table->record[1]);
MYSQL_DELETE_ROW_START(table_share->db.str, table_share->table_name.str);
mark_trx_read_write();
diff --git a/sql/handler.h b/sql/handler.h
index 9d351a66f2d..eadee821303 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -1377,7 +1377,7 @@ static inline sys_var *find_hton_sysvar(handlerton *hton, st_mysql_sys_var *var)
Schema which have no meaning for replication.
*/
#define HTON_NO_BINLOG_ROW_OPT (1 << 9)
-#define HTON_EXTENDED_KEYS (1 <<10) //supports extended keys
+#define HTON_SUPPORTS_EXTENDED_KEYS (1 <<10) //supports extended keys
class Ha_trx_info;
diff --git a/sql/hostname.cc b/sql/hostname.cc
index 6c3c70aa7ea..1200dd2c185 100644
--- a/sql/hostname.cc
+++ b/sql/hostname.cc
@@ -415,7 +415,7 @@ int ip_to_hostname(struct sockaddr_storage *ip_storage,
{
const struct sockaddr *ip= (const sockaddr *) ip_storage;
int err_code;
- bool err_status;
+ bool err_status __attribute__((unused));
Host_errors errors;
DBUG_ENTER("ip_to_hostname");
diff --git a/sql/innodb_priv.h b/sql/innodb_priv.h
index 82d74236ff9..b9e471b3b13 100644
--- a/sql/innodb_priv.h
+++ b/sql/innodb_priv.h
@@ -31,4 +31,6 @@ uint strconvert(CHARSET_INFO *from_cs, const char *from, uint from_length,
uint *errors);
void sql_print_error(const char *format, ...);
+#define thd_binlog_pos(X, Y, Z) mysql_bin_log_commit_pos(X, Z, Y)
+
#endif /* INNODB_PRIV_INCLUDED */
diff --git a/sql/item.cc b/sql/item.cc
index 248b0899026..5bc9d5816eb 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -585,12 +585,11 @@ uint Item::temporal_precision(enum_field_types type)
MYSQL_TIME_STATUS status;
DBUG_ASSERT(fixed);
if ((tmp= val_str(&buf)) &&
- (type == MYSQL_TYPE_TIME ?
+ !(type == MYSQL_TYPE_TIME ?
str_to_time(tmp->charset(), tmp->ptr(), tmp->length(),
&ltime, TIME_TIME_ONLY, &status) :
str_to_datetime(tmp->charset(), tmp->ptr(), tmp->length(),
- &ltime, TIME_FUZZY_DATES, &status)) >
- MYSQL_TIMESTAMP_ERROR)
+ &ltime, TIME_FUZZY_DATES, &status)))
return MY_MIN(status.precision, TIME_SECOND_PART_DIGITS);
}
return MY_MIN(decimals, TIME_SECOND_PART_DIGITS);
@@ -8922,6 +8921,18 @@ Item_cache_temporal::Item_cache_temporal(enum_field_types field_type_arg):
}
+longlong Item_cache_temporal::val_temporal_packed()
+{
+ DBUG_ASSERT(fixed == 1);
+ if ((!value_cached && !cache_value()) || null_value)
+ {
+ null_value= TRUE;
+ return 0;
+ }
+ return value;
+}
+
+
String *Item_cache_temporal::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
@@ -8934,6 +8945,42 @@ String *Item_cache_temporal::val_str(String *str)
}
+my_decimal *Item_cache_temporal::val_decimal(my_decimal *decimal_value)
+{
+ DBUG_ASSERT(fixed == 1);
+ if ((!value_cached && !cache_value()) || null_value)
+ {
+ null_value= true;
+ return NULL;
+ }
+ return val_decimal_from_date(decimal_value);
+}
+
+
+longlong Item_cache_temporal::val_int()
+{
+ DBUG_ASSERT(fixed == 1);
+ if ((!value_cached && !cache_value()) || null_value)
+ {
+ null_value= true;
+ return 0;
+ }
+ return val_int_from_date();
+}
+
+
+double Item_cache_temporal::val_real()
+{
+ DBUG_ASSERT(fixed == 1);
+ if ((!value_cached && !cache_value()) || null_value)
+ {
+ null_value= true;
+ return 0;
+ }
+ return val_real_from_date();
+}
+
+
bool Item_cache_temporal::cache_value()
{
if (!example)
diff --git a/sql/item.h b/sql/item.h
index f5917f7d9c3..6afeca66f0a 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -3410,13 +3410,16 @@ class Item_direct_view_ref :public Item_direct_ref
TABLE_LIST *view;
TABLE *null_ref_table;
+#define NO_NULL_TABLE (reinterpret_cast<TABLE *>(0x1))
+
bool check_null_ref()
{
if (null_ref_table == NULL)
{
- null_ref_table= view->get_real_join_table();
+ if (!(null_ref_table= view->get_real_join_table()))
+ null_ref_table= NO_NULL_TABLE;
}
- if (null_ref_table->null_row)
+ if (null_ref_table != NO_NULL_TABLE && null_ref_table->null_row)
{
null_value= 1;
return TRUE;
@@ -4271,6 +4274,10 @@ class Item_cache_temporal: public Item_cache_int
public:
Item_cache_temporal(enum_field_types field_type_arg);
String* val_str(String *str);
+ my_decimal *val_decimal(my_decimal *);
+ longlong val_int();
+ longlong val_temporal_packed();
+ double val_real();
bool cache_value();
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
int save_in_field(Field *field, bool no_conversions);
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index e6688a5daa9..5852108dc43 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -549,8 +549,8 @@ void Item_bool_func2::fix_length_and_dec()
*/
DTCollation coll;
- if (args[0]->result_type() == STRING_RESULT &&
- args[1]->result_type() == STRING_RESULT &&
+ if (args[0]->cmp_type() == STRING_RESULT &&
+ args[1]->cmp_type() == STRING_RESULT &&
agg_arg_charsets_for_comparison(coll, args, 2))
return;
@@ -888,10 +888,13 @@ get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg,
enum_field_types f_type= item->cmp_type() == TIME_RESULT ?
item->field_type() : warn_item->field_type();
- if (item->result_type() == INT_RESULT && item->cmp_type() == TIME_RESULT)
+ if (item->result_type() == INT_RESULT &&
+ item->cmp_type() == TIME_RESULT &&
+ item->type() == Item::CACHE_ITEM)
{
/* it's our Item_cache_temporal, as created below */
- value= item->val_int();
+ DBUG_ASSERT(is_temporal_type(((Item_cache *) item)->field_type()));
+ value= ((Item_cache_temporal*) item)->val_temporal_packed();
}
else
{
@@ -5017,7 +5020,7 @@ bool Item_func_like::fix_fields(THD *thd, Item **ref)
turboBM_compute_bad_character_shifts();
DBUG_PRINT("info",("done"));
}
- use_sampling= ((*first == wild_many || *first == wild_one) && len > 2);
+ use_sampling= (len > 2 && (*first == wild_many || *first == wild_one));
}
}
return FALSE;
@@ -5706,8 +5709,8 @@ Item *Item_bool_rowready_func2::negated_item()
*/
Item_equal::Item_equal(Item *f1, Item *f2, bool with_const_item)
- : Item_bool_func(), eval_item(0), cond_false(0), context_field(NULL),
- link_equal_fields(FALSE)
+ : Item_bool_func(), eval_item(0), cond_false(0), cond_true(0),
+ context_field(NULL), link_equal_fields(FALSE)
{
const_item_cache= 0;
with_const= with_const_item;
@@ -5732,8 +5735,8 @@ Item_equal::Item_equal(Item *f1, Item *f2, bool with_const_item)
*/
Item_equal::Item_equal(Item_equal *item_equal)
- : Item_bool_func(), eval_item(0), cond_false(0), context_field(NULL),
- link_equal_fields(FALSE)
+ : Item_bool_func(), eval_item(0), cond_false(0), cond_true(0),
+ context_field(NULL), link_equal_fields(FALSE)
{
const_item_cache= 0;
List_iterator_fast<Item> li(item_equal->equal_items);
@@ -5800,13 +5803,9 @@ void Item_equal::add_const(Item *c, Item *f)
func->quick_fix_field();
cond_false= !func->val_int();
}
- /*
- TODO: also support the case where Item_equal becomes singular with
- this->is_cond_true()=1. When I attempted to mark the item as constant,
- the optimizer attempted to remove it, however it is still referenced from
- COND_EQUAL and I got a crash.
- */
- if (cond_false)
+ if (with_const && equal_items.elements == 1)
+ cond_true= TRUE;
+ if (cond_false || cond_true)
const_item_cache= 1;
}
@@ -6010,8 +6009,7 @@ void Item_equal::merge_into_list(List<Item_equal> *list,
void Item_equal::sort(Item_field_cmpfunc compare, void *arg)
{
- if (equal_items.elements > 1)
- bubble_sort<Item>(&equal_items, compare, arg);
+ bubble_sort<Item>(&equal_items, compare, arg);
}
@@ -6139,13 +6137,7 @@ bool Item_equal::fix_fields(THD *thd, Item **ref)
void Item_equal::update_used_tables()
{
not_null_tables_cache= used_tables_cache= 0;
- /*
- TODO: also support the case where Item_equal becomes singular with
- this->is_cond_true()=1. When I attempted to mark the item as constant,
- the optimizer attempted to remove it, however it is still referenced from
- COND_EQUAL and I got a crash.
- */
- if ((const_item_cache= cond_false))
+ if ((const_item_cache= cond_false || cond_true))
return;
Item_equal_fields_iterator it(*this);
Item *item;
@@ -6194,7 +6186,7 @@ longlong Item_equal::val_int()
{
if (cond_false)
return 0;
- if (is_cond_true())
+ if (cond_true)
return 1;
Item *item= get_const();
Item_equal_fields_iterator it(*this);
@@ -6220,11 +6212,6 @@ longlong Item_equal::val_int()
void Item_equal::fix_length_and_dec()
{
Item *item= get_first(NO_PARTICULAR_TAB, NULL);
- if (!item)
- {
- DBUG_ASSERT(is_cond_true()); // it should be the only constant
- item= equal_items.head();
- }
eval_item= cmp_item::get_comparator(item->cmp_type(), item,
item->collation.collation);
}
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index a438139bcfe..a0d9b7c20fa 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -1794,6 +1794,12 @@ class Item_equal: public Item_bool_func
the equal_items should be ignored.
*/
bool cond_false;
+ /*
+ This initially is set to FALSE. It becomes TRUE when this item is evaluated
+ as being always true. If the flag is TRUE the contents of the list
+ the equal_items should be ignored.
+ */
+ bool cond_true;
/*
compare_as_dates=TRUE <-> constants equal to fields from equal_items
must be compared as datetimes and not as strings.
@@ -1826,7 +1832,6 @@ public:
Item_equal(Item_equal *item_equal);
/* Currently the const item is always the first in the list of equal items */
inline Item* get_const() { return with_const ? equal_items.head() : NULL; }
- inline bool is_cond_true() { return equal_items.elements == 1; }
void add_const(Item *c, Item *f = NULL);
/** Add a non-constant item to the multiple equality */
void add(Item *f) { equal_items.push_back(f); }
diff --git a/sql/item_func.cc b/sql/item_func.cc
index a49dbb867d6..02aae8085d6 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -824,6 +824,8 @@ void Item_num_op::find_num_type(void)
cached_result_type= DECIMAL_RESULT;
result_precision();
fix_decimals();
+ if ((r0 == TIME_RESULT || r1 == TIME_RESULT) && decimals == 0)
+ cached_result_type= INT_RESULT;
}
else
{
@@ -1135,7 +1137,7 @@ bool Item_func_hybrid_result_type::get_date(MYSQL_TIME *ltime,
String tmp(buff,sizeof(buff), &my_charset_bin),*res;
if (!(res= str_op(&tmp)) ||
str_to_datetime_with_warn(res->charset(), res->ptr(), res->length(),
- ltime, fuzzydate) <= MYSQL_TIMESTAMP_ERROR)
+ ltime, fuzzydate))
goto err;
break;
break;
diff --git a/sql/item_func.h b/sql/item_func.h
index 03e67ddf11a..4b1516dcc4d 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -167,7 +167,10 @@ public:
Item **item, uint nitems);
inline bool get_arg0_time(MYSQL_TIME *ltime)
{
- return (null_value=args[0]->get_time(ltime));
+ null_value= args[0]->get_time(ltime);
+ DBUG_ASSERT(null_value ||
+ ltime->time_type != MYSQL_TIMESTAMP_TIME || ltime->day == 0);
+ return null_value;
}
bool is_null() {
update_null_value();
diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc
index ef189763d88..fc5dfe0994e 100644
--- a/sql/item_strfunc.cc
+++ b/sql/item_strfunc.cc
@@ -2171,129 +2171,28 @@ void Item_func_trim::print(String *str, enum_query_type query_type)
/* Item_func_password */
-/**
- Helper function for calculating a new password. Used in
- Item_func_password::fix_length_and_dec() for const parameters and in
- Item_func_password::val_str_ascii() for non-const parameters.
- @param str The plain text password which should be digested
- @param buffer a pointer to the buffer where the digest will be stored.
-
- @note The buffer must be of at least CRYPT_MAX_PASSWORD_SIZE size.
-
- @return Size of the password.
-*/
-
-static int calculate_password(String *str, char *buffer)
-{
- DBUG_ASSERT(str);
- if (str->length() == 0) // PASSWORD('') returns ''
- return 0;
-
- int buffer_len= 0;
- THD *thd= current_thd;
- int old_passwords= 0;
- if (thd)
- old_passwords= thd->variables.old_passwords;
-
-#if defined(HAVE_OPENSSL)
- if (old_passwords == 2)
- {
- my_make_scrambled_password(buffer, str->ptr(),
- str->length());
- buffer_len= (int) strlen(buffer) + 1;
- }
- else
-#endif
- if (old_passwords == 0)
- {
- my_make_scrambled_password_sha1(buffer, str->ptr(),
- str->length());
- buffer_len= SCRAMBLED_PASSWORD_CHAR_LENGTH;
- }
- else
- if (old_passwords == 1)
- {
- my_make_scrambled_password_323(buffer, str->ptr(),
- str->length());
- buffer_len= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
- }
- return buffer_len;
-}
-
-/* Item_func_password */
-void Item_func_password::fix_length_and_dec()
-{
- maybe_null= false; // PASSWORD() never returns NULL
-
- if (args[0]->const_item())
- {
- String str;
- String *res= args[0]->val_str(&str);
- if (!args[0]->null_value)
- {
- m_hashed_password_buffer_len=
- calculate_password(res, m_hashed_password_buffer);
- fix_length_and_charset(m_hashed_password_buffer_len, default_charset());
- m_recalculate_password= false;
- return;
- }
- }
-
- m_recalculate_password= true;
- fix_length_and_charset(CRYPT_MAX_PASSWORD_SIZE, default_charset());
-}
-
String *Item_func_password::val_str_ascii(String *str)
{
DBUG_ASSERT(fixed == 1);
-
- String *res= args[0]->val_str(str);
-
- if (args[0]->null_value)
- res= make_empty_result();
-
- /* we treat NULLs as equal to empty string when calling the plugin */
+ String *res= args[0]->val_str(str);
check_password_policy(res);
-
- null_value= 0;
- if (args[0]->null_value) // PASSWORD(NULL) returns ''
- return res;
-
- if (m_recalculate_password)
- m_hashed_password_buffer_len= calculate_password(res,
- m_hashed_password_buffer);
-
- if (m_hashed_password_buffer_len == 0)
+ if (args[0]->null_value || res->length() == 0)
return make_empty_result();
-
- str->set(m_hashed_password_buffer, m_hashed_password_buffer_len,
- default_charset());
-
+ my_make_scrambled_password(tmp_value, res->ptr(), res->length());
+ str->set(tmp_value, SCRAMBLED_PASSWORD_CHAR_LENGTH, &my_charset_latin1);
return str;
}
-char *Item_func_password::
- create_password_hash_buffer(THD *thd, const char *password, size_t pass_len)
+char *Item_func_password::alloc(THD *thd, const char *password, size_t pass_len)
{
- String *password_str= new (thd->mem_root)String(password, thd->variables.
- character_set_client);
- check_password_policy(password_str);
-
- char *buff= NULL;
- if (thd->variables.old_passwords == 0)
- {
- /* Allocate memory for the password scramble and one extra byte for \0 */
- buff= (char *) thd->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH + 1);
- my_make_scrambled_password_sha1(buff, password, pass_len);
- }
-#if defined(HAVE_OPENSSL)
- else
+ char *buff= (char *) thd->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH+1);
+ if (buff)
{
- /* Allocate memory for the password scramble and one extra byte for \0 */
- buff= (char *) thd->alloc(CRYPT_MAX_PASSWORD_SIZE + 1);
+ String *password_str= new (thd->mem_root)String(password, thd->variables.
+ character_set_client);
+ check_password_policy(password_str);
my_make_scrambled_password(buff, password, pass_len);
}
-#endif
return buff;
}
diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h
index 6709b4b64c6..4b9ec50c164 100644
--- a/sql/item_strfunc.h
+++ b/sql/item_strfunc.h
@@ -21,8 +21,6 @@
/* This file defines all string functions */
-#include "crypt_genhash_impl.h"
-
#ifdef USE_PRAGMA_INTERFACE
#pragma interface /* gcc class implementation */
#endif
@@ -394,21 +392,16 @@ public:
class Item_func_password :public Item_str_ascii_func
{
- char m_hashed_password_buffer[CRYPT_MAX_PASSWORD_SIZE + 1];
- unsigned int m_hashed_password_buffer_len;
- bool m_recalculate_password;
+ char tmp_value[SCRAMBLED_PASSWORD_CHAR_LENGTH+1];
public:
- Item_func_password(Item *a) :Item_str_ascii_func(a)
+ Item_func_password(Item *a) :Item_str_ascii_func(a) {}
+ String *val_str_ascii(String *str);
+ void fix_length_and_dec()
{
- m_hashed_password_buffer_len= 0;
- m_recalculate_password= false;
+ fix_length_and_charset(SCRAMBLED_PASSWORD_CHAR_LENGTH, default_charset());
}
- String *val_str_ascii(String *str);
- void fix_length_and_dec();
const char *func_name() const { return "password"; }
static char *alloc(THD *thd, const char *password, size_t pass_len);
- static char *create_password_hash_buffer(THD *thd, const char *password,
- size_t pass_len);
};
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index 35e040cad3a..81c4f0f51e5 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -1333,8 +1333,6 @@ Item_exists_subselect::Item_exists_subselect(st_select_lex *select_lex):
emb_on_expr_nest(NULL), optimizer(0), exists_transformed(0)
{
DBUG_ENTER("Item_exists_subselect::Item_exists_subselect");
- bool val_bool();
-
init(select_lex, new select_exists_subselect(this));
max_columns= UINT_MAX;
null_value= FALSE; //can't be NULL
diff --git a/sql/item_sum.cc b/sql/item_sum.cc
index b3be7339849..367ff39eab6 100644
--- a/sql/item_sum.cc
+++ b/sql/item_sum.cc
@@ -1266,7 +1266,7 @@ Item_sum_hybrid::fix_fields(THD *thd, Item **ref)
void Item_sum_hybrid::setup_hybrid(Item *item, Item *value_arg)
{
- if (!(value= Item_cache::get_cache(item)))
+ if (!(value= Item_cache::get_cache(item, item->cmp_type())))
return;
value->setup(item);
value->store(value_arg);
diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc
index a50443ad348..6ffa8b2af46 100644
--- a/sql/item_timefunc.cc
+++ b/sql/item_timefunc.cc
@@ -1467,7 +1467,9 @@ void Item_temporal_func::fix_length_and_dec()
(MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE);
collation.set(field_type() == MYSQL_TYPE_STRING ?
default_charset() : &my_charset_numeric,
- DERIVATION_NUMERIC, MY_REPERTOIRE_ASCII);
+ field_type() == MYSQL_TYPE_STRING ?
+ DERIVATION_COERCIBLE : DERIVATION_NUMERIC,
+ MY_REPERTOIRE_ASCII);
fix_char_length(char_length);
}
@@ -1478,27 +1480,6 @@ String *Item_temporal_func::val_str(String *str)
}
-longlong Item_temporal_func::val_int()
-{
- DBUG_ASSERT(fixed == 1);
- MYSQL_TIME ltime;
- if (get_date(&ltime, sql_mode))
- return 0;
- longlong v= TIME_to_ulonglong(&ltime);
- return ltime.neg ? -v : v;
-}
-
-
-double Item_temporal_func::val_real()
-{
- DBUG_ASSERT(fixed == 1);
- MYSQL_TIME ltime;
- if (get_date(&ltime, sql_mode))
- return 0;
- return TIME_to_double(&ltime);
-}
-
-
String *Item_temporal_hybrid_func::val_str_ascii(String *str)
{
DBUG_ASSERT(fixed == 1);
@@ -2183,6 +2164,10 @@ longlong Item_extract::val_int()
return 0;
neg= ltime.neg ? -1 : 1;
+ DBUG_ASSERT(ltime.time_type != MYSQL_TIMESTAMP_TIME || ltime.day == 0);
+ if (ltime.time_type == MYSQL_TIMESTAMP_TIME)
+ time_to_daytime_interval(&ltime);
+
switch (int_type) {
case INTERVAL_YEAR: return ltime.year;
case INTERVAL_YEAR_MONTH: return ltime.year*100L+ltime.month;
diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h
index 99c41961aba..cdf03199a81 100644
--- a/sql/item_timefunc.h
+++ b/sql/item_timefunc.h
@@ -498,8 +498,8 @@ public:
enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; }
Item_result cmp_type() const { return TIME_RESULT; }
String *val_str(String *str);
- longlong val_int();
- double val_real();
+ longlong val_int() { return val_int_from_date(); }
+ double val_real() { return val_real_from_date(); }
bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date) { DBUG_ASSERT(0); return 1; }
my_decimal *val_decimal(my_decimal *decimal_value)
{ return val_decimal_from_date(decimal_value); }
@@ -524,6 +524,11 @@ public:
Item_temporal_hybrid_func(Item *a,Item *b)
:Item_temporal_func(a,b) {}
enum_field_types field_type() const { return cached_field_type; }
+ Item_result cmp_type() const
+ {
+ return cached_field_type == MYSQL_TYPE_STRING ?
+ STRING_RESULT : TIME_RESULT;
+ }
const CHARSET_INFO *charset_for_protocol() const
{
/*
diff --git a/sql/item_xmlfunc.h b/sql/item_xmlfunc.h
index 800cf6ed760..e818a6da408 100644
--- a/sql/item_xmlfunc.h
+++ b/sql/item_xmlfunc.h
@@ -21,7 +21,7 @@
/* This file defines all XML functions */
-#ifdef __GNUC__
+#ifdef USE_PRAGMA_INTERFACE
#pragma interface /* gcc class implementation */
#endif
diff --git a/sql/key.cc b/sql/key.cc
index edc3e6c0057..8ba3d48e8dc 100644
--- a/sql/key.cc
+++ b/sql/key.cc
@@ -21,6 +21,9 @@
#include "key.h" // key_rec_cmp
#include "field.h" // Field
+using std::min;
+using std::max;
+
/*
Search after a key that starts with 'field'
@@ -132,7 +135,7 @@ void key_copy(uchar *to_key, uchar *from_record, KEY *key_info,
Don't copy data for null values
The -1 below is to subtract the null byte which is already handled
*/
- length= MY_MIN(key_length, (uint) key_part->store_length-1);
+ length= min<uint>(key_length, key_part->store_length-1);
if (with_zerofill)
bzero((char*) to_key, length);
continue;
@@ -142,7 +145,7 @@ void key_copy(uchar *to_key, uchar *from_record, KEY *key_info,
key_part->key_part_flag & HA_VAR_LENGTH_PART)
{
key_length-= HA_KEY_BLOB_LENGTH;
- length= MY_MIN(key_length, key_part->length);
+ length= min<uint>(key_length, key_part->length);
uint bytes= key_part->field->get_key_image(to_key, length, Field::itRAW);
if (with_zerofill && bytes < length)
bzero((char*) to_key + bytes, length - bytes);
@@ -150,7 +153,7 @@ void key_copy(uchar *to_key, uchar *from_record, KEY *key_info,
}
else
{
- length= MY_MIN(key_length, key_part->length);
+ length= min<uint>(key_length, key_part->length);
Field *field= key_part->field;
CHARSET_INFO *cs= field->charset();
uint bytes= field->get_key_image(to_key, length, Field::itRAW);
@@ -202,7 +205,7 @@ void key_restore(uchar *to_record, uchar *from_key, KEY *key_info,
Don't copy data for null bytes
The -1 below is to subtract the null byte which is already handled
*/
- length= MY_MIN(key_length, (uint) key_part->store_length-1);
+ length= min<uint>(key_length, key_part->store_length-1);
continue;
}
}
@@ -244,7 +247,7 @@ void key_restore(uchar *to_record, uchar *from_key, KEY *key_info,
my_ptrdiff_t ptrdiff= to_record - field->table->record[0];
field->move_field_offset(ptrdiff);
key_length-= HA_KEY_BLOB_LENGTH;
- length= MY_MIN(key_length, key_part->length);
+ length= min<uint>(key_length, key_part->length);
old_map= dbug_tmp_use_all_columns(field->table, field->table->write_set);
field->set_key_image(from_key, length);
dbug_tmp_restore_column_map(field->table->write_set, old_map);
@@ -253,7 +256,7 @@ void key_restore(uchar *to_record, uchar *from_key, KEY *key_info,
}
else
{
- length= MY_MIN(key_length, key_part->length);
+ length= min<uint>(key_length, key_part->length);
/* skip the byte with 'uneven' bits, if used */
memcpy(to_record + key_part->offset, from_key + used_uneven_bits
, (size_t) length - used_uneven_bits);
@@ -311,7 +314,7 @@ bool key_cmp_if_same(TABLE *table,const uchar *key,uint idx,uint key_length)
return 1;
continue;
}
- length= MY_MIN((uint) (key_end-key), store_length);
+ length= min((uint) (key_end-key), store_length);
if (!(key_part->key_type & (FIELDFLAG_NUMBER+FIELDFLAG_BINARY+
FIELDFLAG_PACK)))
{
@@ -347,8 +350,8 @@ bool key_cmp_if_same(TABLE *table,const uchar *key,uint idx,uint key_length)
@param prefix_key The field is used as a prefix key.
*/
-static void field_unpack(String *to, Field *field, const uchar *rec,
- uint max_length, bool prefix_key)
+void field_unpack(String *to, Field *field, const uchar *rec, uint max_length,
+ bool prefix_key)
{
String tmp;
DBUG_ENTER("field_unpack");
@@ -389,7 +392,7 @@ static void field_unpack(String *to, Field *field, const uchar *rec,
tmp.length(charpos);
}
if (max_length < field->pack_length())
- tmp.length(MY_MIN(tmp.length(),max_length));
+ tmp.length(min(tmp.length(),max_length));
ErrConvString err(&tmp);
to->append(err.ptr());
}
@@ -410,18 +413,17 @@ static void field_unpack(String *to, Field *field, const uchar *rec,
@param
table Table to use
@param
- idx Key number
+ key Key
*/
-void key_unpack(String *to,TABLE *table, KEY *key)
+void key_unpack(String *to, TABLE *table, KEY *key)
{
- KEY_PART_INFO *key_part,*key_part_end;
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
DBUG_ENTER("key_unpack");
to->length(0);
- for (key_part=key->key_part,key_part_end=key_part+
- key->user_defined_key_parts ;
+ KEY_PART_INFO *key_part_end= key->key_part + key->user_defined_key_parts;
+ for (KEY_PART_INFO *key_part= key->key_part;
key_part < key_part_end;
key_part++)
{
diff --git a/sql/key.h b/sql/key.h
index c8b3ad997fb..ba3102947b3 100644
--- a/sql/key.h
+++ b/sql/key.h
@@ -32,7 +32,9 @@ void key_copy(uchar *to_key, uchar *from_record, KEY *key_info, uint key_length,
void key_restore(uchar *to_record, uchar *from_key, KEY *key_info,
uint key_length);
bool key_cmp_if_same(TABLE *form,const uchar *key,uint index,uint key_length);
-void key_unpack(String *to, TABLE *form, KEY *key);
+void key_unpack(String *to, TABLE *table, KEY *key);
+void field_unpack(String *to, Field *field, const uchar *rec, uint max_length,
+ bool prefix_key);
bool is_key_used(TABLE *table, uint idx, const MY_BITMAP *fields);
int key_cmp(KEY_PART_INFO *key_part, const uchar *key, uint key_length);
ulong key_hashnr(KEY *key_info, uint used_key_parts, const uchar *key);
diff --git a/sql/log.cc b/sql/log.cc
index c09e2b416c8..dda3e2ee8c1 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -3120,7 +3120,8 @@ MYSQL_BIN_LOG::MYSQL_BIN_LOG(uint *sync_period)
bytes_written(0), file_id(1), open_count(1),
group_commit_queue(0), group_commit_queue_busy(FALSE),
num_commits(0), num_group_commits(0),
- sync_period_ptr(sync_period), sync_counter(0), state_read(false),
+ sync_period_ptr(sync_period), sync_counter(0),
+ state_file_deleted(false), binlog_state_recover_done(false),
is_relay_log(0), signal_cnt(0),
checksum_alg_reset(BINLOG_CHECKSUM_ALG_UNDEF),
relay_log_checksum_alg(BINLOG_CHECKSUM_ALG_UNDEF),
@@ -3322,12 +3323,19 @@ bool MYSQL_BIN_LOG::open(const char *log_name,
DBUG_ENTER("MYSQL_BIN_LOG::open");
DBUG_PRINT("enter",("log_type: %d",(int) log_type_arg));
- if (!is_relay_log && read_state_from_file())
- DBUG_RETURN(1);
+ if (!is_relay_log)
+ {
+ if (!binlog_state_recover_done)
+ {
+ binlog_state_recover_done= true;
+ if (do_binlog_recovery(opt_bin_logname, false))
+ DBUG_RETURN(1);
+ }
- if (!is_relay_log && !binlog_background_thread_started &&
- start_binlog_background_thread())
- DBUG_RETURN(1);
+ if (!binlog_background_thread_started &&
+ start_binlog_background_thread())
+ DBUG_RETURN(1);
+ }
if (init_and_set_log_file_name(log_name, new_name, log_type_arg,
io_cache_type_arg))
@@ -3614,6 +3622,25 @@ bool MYSQL_BIN_LOG::open(const char *log_name,
my_free(binlog_xid_count_list.get());
binlog_xid_count_list.push_back(new_xid_list_entry);
mysql_mutex_unlock(&LOCK_xid_list);
+
+ /*
+ Now that we have synced a new binlog file with an initial Gtid_list
+ event, it is safe to delete the binlog state file. We will write out
+ a new, updated file at shutdown, and if we crash before we can recover
+ the state from the newly written binlog file.
+
+ Since the state file will contain out-of-date data as soon as the first
+ new GTID is binlogged, it is better to remove it, to avoid any risk of
+ accidentally reading incorrect data later.
+ */
+ if (!state_file_deleted)
+ {
+ char buf[FN_REFLEN];
+ fn_format(buf, opt_bin_logname, mysql_data_home, ".state",
+ MY_UNPACK_FILENAME);
+ my_delete(buf, MY_SYNC_DIR);
+ state_file_deleted= true;
+ }
}
log_state= LOG_OPENED;
@@ -5599,19 +5626,15 @@ MYSQL_BIN_LOG::write_gtid_event(THD *thd, bool standalone,
gtid.domain_id= domain_id;
gtid.server_id= server_id;
gtid.seq_no= seq_no;
- mysql_mutex_lock(&LOCK_rpl_gtid_state);
err= rpl_global_gtid_binlog_state.update(&gtid, opt_gtid_strict_mode);
- mysql_mutex_unlock(&LOCK_rpl_gtid_state);
if (err && thd->get_stmt_da()->sql_errno()==ER_GTID_STRICT_OUT_OF_ORDER)
errno= ER_GTID_STRICT_OUT_OF_ORDER;
}
else
{
/* Allocate the next sequence number for the GTID. */
- mysql_mutex_lock(&LOCK_rpl_gtid_state);
err= rpl_global_gtid_binlog_state.update_with_next_gtid(domain_id,
server_id, &gtid);
- mysql_mutex_unlock(&LOCK_rpl_gtid_state);
seq_no= gtid.seq_no;
}
if (err)
@@ -5685,10 +5708,6 @@ MYSQL_BIN_LOG::read_state_from_file()
bool opened= false;
bool inited= false;
- if (state_read)
- return 0;
- state_read= true;
-
fn_format(buf, opt_bin_logname, mysql_data_home, ".state",
MY_UNPACK_FILENAME);
if ((file_no= mysql_file_open(key_file_binlog_state, buf,
@@ -5741,36 +5760,21 @@ MYSQL_BIN_LOG::get_most_recent_gtid_list(rpl_gtid **list, uint32 *size)
bool
MYSQL_BIN_LOG::append_state_pos(String *str)
{
- bool err;
-
- mysql_mutex_lock(&rpl_global_gtid_binlog_state.LOCK_binlog_state);
- err= rpl_global_gtid_binlog_state.append_pos(str);
- mysql_mutex_unlock(&rpl_global_gtid_binlog_state.LOCK_binlog_state);
- return err;
+ return rpl_global_gtid_binlog_state.append_pos(str);
}
bool
MYSQL_BIN_LOG::append_state(String *str)
{
- bool err;
-
- mysql_mutex_lock(&rpl_global_gtid_binlog_state.LOCK_binlog_state);
- err= rpl_global_gtid_binlog_state.append_state(str);
- mysql_mutex_unlock(&rpl_global_gtid_binlog_state.LOCK_binlog_state);
- return err;
+ return rpl_global_gtid_binlog_state.append_state(str);
}
bool
MYSQL_BIN_LOG::is_empty_state()
{
- bool res;
-
- mysql_mutex_lock(&rpl_global_gtid_binlog_state.LOCK_binlog_state);
- res= (rpl_global_gtid_binlog_state.count() == 0);
- mysql_mutex_unlock(&rpl_global_gtid_binlog_state.LOCK_binlog_state);
- return res;
+ return (rpl_global_gtid_binlog_state.count() == 0);
}
@@ -5779,10 +5783,8 @@ MYSQL_BIN_LOG::find_in_binlog_state(uint32 domain_id, uint32 server_id,
rpl_gtid *out_gtid)
{
rpl_gtid *gtid;
- mysql_mutex_lock(&rpl_global_gtid_binlog_state.LOCK_binlog_state);
if ((gtid= rpl_global_gtid_binlog_state.find(domain_id, server_id)))
*out_gtid= *gtid;
- mysql_mutex_unlock(&rpl_global_gtid_binlog_state.LOCK_binlog_state);
return gtid != NULL;
}
@@ -5792,29 +5794,21 @@ MYSQL_BIN_LOG::lookup_domain_in_binlog_state(uint32 domain_id,
rpl_gtid *out_gtid)
{
rpl_gtid *found_gtid;
- bool res= false;
- mysql_mutex_lock(&rpl_global_gtid_binlog_state.LOCK_binlog_state);
if ((found_gtid= rpl_global_gtid_binlog_state.find_most_recent(domain_id)))
{
*out_gtid= *found_gtid;
- res= true;
+ return true;
}
- mysql_mutex_unlock(&rpl_global_gtid_binlog_state.LOCK_binlog_state);
- return res;
+ return false;
}
int
MYSQL_BIN_LOG::bump_seq_no_counter_if_needed(uint32 domain_id, uint64 seq_no)
{
- int err;
-
- mysql_mutex_lock(&rpl_global_gtid_binlog_state.LOCK_binlog_state);
- err= rpl_global_gtid_binlog_state.bump_seq_no_if_needed(domain_id, seq_no);
- mysql_mutex_unlock(&rpl_global_gtid_binlog_state.LOCK_binlog_state);
- return err;
+ return rpl_global_gtid_binlog_state.bump_seq_no_if_needed(domain_id, seq_no);
}
@@ -5822,13 +5816,8 @@ bool
MYSQL_BIN_LOG::check_strict_gtid_sequence(uint32 domain_id, uint32 server_id,
uint64 seq_no)
{
- bool err;
-
- mysql_mutex_lock(&rpl_global_gtid_binlog_state.LOCK_binlog_state);
- err= rpl_global_gtid_binlog_state.check_strict_sequence(domain_id, server_id,
- seq_no);
- mysql_mutex_unlock(&rpl_global_gtid_binlog_state.LOCK_binlog_state);
- return err;
+ return rpl_global_gtid_binlog_state.check_strict_sequence(domain_id,
+ server_id, seq_no);
}
@@ -8804,7 +8793,6 @@ int TC_LOG::using_heuristic_recover()
int TC_LOG_BINLOG::open(const char *opt_name)
{
- LOG_INFO log_info;
int error= 1;
DBUG_ASSERT(total_ha_2pc > 1);
@@ -8825,65 +8813,8 @@ int TC_LOG_BINLOG::open(const char *opt_name)
return 1;
}
- if ((error= find_log_pos(&log_info, NullS, 1)))
- {
- if (error != LOG_INFO_EOF)
- sql_print_error("find_log_pos() failed (error: %d)", error);
- else
- error= 0;
- goto err;
- }
-
- {
- const char *errmsg;
- IO_CACHE log;
- File file;
- Log_event *ev=0;
- Format_description_log_event fdle(BINLOG_VERSION);
- char log_name[FN_REFLEN];
-
- if (! fdle.is_valid())
- goto err;
-
- do
- {
- strmake_buf(log_name, log_info.log_file_name);
- } while (!(error= find_next_log(&log_info, 1)));
-
- if (error != LOG_INFO_EOF)
- {
- sql_print_error("find_log_pos() failed (error: %d)", error);
- goto err;
- }
-
- if ((file= open_binlog(&log, log_name, &errmsg)) < 0)
- {
- sql_print_error("%s", errmsg);
- goto err;
- }
-
- if ((ev= Log_event::read_log_event(&log, 0, &fdle,
- opt_master_verify_checksum)) &&
- ev->get_type_code() == FORMAT_DESCRIPTION_EVENT &&
- ev->flags & LOG_EVENT_BINLOG_IN_USE_F)
- {
- sql_print_information("Recovering after a crash using %s", opt_name);
- error= recover(&log_info, log_name, &log,
- (Format_description_log_event *)ev);
- state_read= true;
- }
- else
- error= read_state_from_file();
-
- delete ev;
- end_io_cache(&log);
- mysql_file_close(file, MYF(MY_WME));
-
- if (error)
- goto err;
- }
-
-err:
+ error= do_binlog_recovery(opt_name, true);
+ binlog_state_recover_done= true;
return error;
}
@@ -9267,9 +9198,9 @@ start_binlog_background_thread()
int TC_LOG_BINLOG::recover(LOG_INFO *linfo, const char *last_log_name,
IO_CACHE *first_log,
- Format_description_log_event *fdle)
+ Format_description_log_event *fdle, bool do_xa)
{
- Log_event *ev;
+ Log_event *ev= NULL;
HASH xids;
MEM_ROOT mem_root;
char binlog_checkpoint_name[FN_REFLEN];
@@ -9278,13 +9209,19 @@ int TC_LOG_BINLOG::recover(LOG_INFO *linfo, const char *last_log_name,
IO_CACHE log;
File file= -1;
const char *errmsg;
+#ifdef HAVE_REPLICATION
+ rpl_gtid last_gtid;
+ bool last_gtid_standalone= false;
+ bool last_gtid_valid= false;
+#endif
if (! fdle->is_valid() ||
- my_hash_init(&xids, &my_charset_bin, TC_LOG_PAGE_SIZE/3, 0,
- sizeof(my_xid), 0, 0, MYF(0)))
+ (do_xa && my_hash_init(&xids, &my_charset_bin, TC_LOG_PAGE_SIZE/3, 0,
+ sizeof(my_xid), 0, 0, MYF(0))))
goto err1;
- init_alloc_root(&mem_root, TC_LOG_PAGE_SIZE, TC_LOG_PAGE_SIZE, MYF(0));
+ if (do_xa)
+ init_alloc_root(&mem_root, TC_LOG_PAGE_SIZE, TC_LOG_PAGE_SIZE, MYF(0));
fdle->flags&= ~LOG_EVENT_BINLOG_IN_USE_F; // abort on the first error
@@ -9304,22 +9241,23 @@ int TC_LOG_BINLOG::recover(LOG_INFO *linfo, const char *last_log_name,
0, fdle, opt_master_verify_checksum))
&& ev->is_valid())
{
- switch (ev->get_type_code())
+ enum Log_event_type typ= ev->get_type_code();
+ switch (typ)
{
case XID_EVENT:
{
- Xid_log_event *xev=(Xid_log_event *)ev;
- uchar *x= (uchar *) memdup_root(&mem_root, (uchar*) &xev->xid,
- sizeof(xev->xid));
- if (!x || my_hash_insert(&xids, x))
+ if (do_xa)
{
- delete ev;
- goto err2;
+ Xid_log_event *xev=(Xid_log_event *)ev;
+ uchar *x= (uchar *) memdup_root(&mem_root, (uchar*) &xev->xid,
+ sizeof(xev->xid));
+ if (!x || my_hash_insert(&xids, x))
+ goto err2;
+ break;
}
- break;
}
case BINLOG_CHECKPOINT_EVENT:
- if (first_round)
+ if (first_round && do_xa)
{
uint dir_len;
Binlog_checkpoint_log_event *cev= (Binlog_checkpoint_log_event *)ev;
@@ -9337,8 +9275,8 @@ int TC_LOG_BINLOG::recover(LOG_INFO *linfo, const char *last_log_name,
cev->binlog_file_name, FN_REFLEN - 1 - dir_len);
binlog_checkpoint_found= true;
}
- break;
}
+ break;
case GTID_LIST_EVENT:
if (first_round)
{
@@ -9350,28 +9288,49 @@ int TC_LOG_BINLOG::recover(LOG_INFO *linfo, const char *last_log_name,
}
break;
+#ifdef HAVE_REPLICATION
case GTID_EVENT:
if (first_round)
{
Gtid_log_event *gev= (Gtid_log_event *)ev;
- rpl_gtid gtid;
/* Update the binlog state with any GTID logged after Gtid_list. */
- gtid.domain_id= gev->domain_id;
- gtid.server_id= gev->server_id;
- gtid.seq_no= gev->seq_no;
- if (rpl_global_gtid_binlog_state.update(&gtid, false))
- goto err2;
+ last_gtid.domain_id= gev->domain_id;
+ last_gtid.server_id= gev->server_id;
+ last_gtid.seq_no= gev->seq_no;
+ last_gtid_standalone=
+ ((gev->flags2 & Gtid_log_event::FL_STANDALONE) ? true : false);
+ last_gtid_valid= true;
}
break;
+#endif
default:
/* Nothing. */
break;
}
+
+#ifdef HAVE_REPLICATION
+ if (last_gtid_valid &&
+ ((last_gtid_standalone && !ev->is_part_of_group(typ)) ||
+ (!last_gtid_standalone &&
+ (typ == XID_EVENT ||
+ (typ == QUERY_EVENT &&
+ (((Query_log_event *)ev)->is_commit() ||
+ ((Query_log_event *)ev)->is_rollback()))))))
+ {
+ if (rpl_global_gtid_binlog_state.update_nolock(&last_gtid, false))
+ goto err2;
+ last_gtid_valid= false;
+ }
+#endif
+
delete ev;
+ ev= NULL;
}
+ if (!do_xa)
+ break;
/*
If the last binlog checkpoint event points to an older log, we have to
scan all logs from there also, to get all possible XIDs to recover.
@@ -9424,21 +9383,28 @@ int TC_LOG_BINLOG::recover(LOG_INFO *linfo, const char *last_log_name,
}
}
- if (ha_recover(&xids))
- goto err2;
+ if (do_xa)
+ {
+ if (ha_recover(&xids))
+ goto err2;
- free_root(&mem_root, MYF(0));
- my_hash_free(&xids);
+ free_root(&mem_root, MYF(0));
+ my_hash_free(&xids);
+ }
return 0;
err2:
+ delete ev;
if (file >= 0)
{
end_io_cache(&log);
mysql_file_close(file, MYF(MY_WME));
}
- free_root(&mem_root, MYF(0));
- my_hash_free(&xids);
+ if (do_xa)
+ {
+ free_root(&mem_root, MYF(0));
+ my_hash_free(&xids);
+ }
err1:
sql_print_error("Crash recovery failed. Either correct the problem "
"(if it's, for example, out of memory error) and restart, "
@@ -9448,6 +9414,73 @@ err1:
}
+int
+MYSQL_BIN_LOG::do_binlog_recovery(const char *opt_name, bool do_xa_recovery)
+{
+ LOG_INFO log_info;
+ const char *errmsg;
+ IO_CACHE log;
+ File file;
+ Log_event *ev= 0;
+ Format_description_log_event fdle(BINLOG_VERSION);
+ char log_name[FN_REFLEN];
+ int error;
+
+ if ((error= find_log_pos(&log_info, NullS, 1)))
+ {
+ /*
+ If there are no binlog files (LOG_INFO_EOF), then we still try to read
+ the .state file to restore the binlog state. This allows to copy a server
+ to provision a new one without copying the binlog files (except the
+ master-bin.state file) and still preserve the correct binlog state.
+ */
+ if (error != LOG_INFO_EOF)
+ sql_print_error("find_log_pos() failed (error: %d)", error);
+ else
+ error= read_state_from_file();
+ return error;
+ }
+
+ if (! fdle.is_valid())
+ return 1;
+
+ do
+ {
+ strmake_buf(log_name, log_info.log_file_name);
+ } while (!(error= find_next_log(&log_info, 1)));
+
+ if (error != LOG_INFO_EOF)
+ {
+ sql_print_error("find_log_pos() failed (error: %d)", error);
+ return error;
+ }
+
+ if ((file= open_binlog(&log, log_name, &errmsg)) < 0)
+ {
+ sql_print_error("%s", errmsg);
+ return 1;
+ }
+
+ if ((ev= Log_event::read_log_event(&log, 0, &fdle,
+ opt_master_verify_checksum)) &&
+ ev->get_type_code() == FORMAT_DESCRIPTION_EVENT &&
+ ev->flags & LOG_EVENT_BINLOG_IN_USE_F)
+ {
+ sql_print_information("Recovering after a crash using %s", opt_name);
+ error= recover(&log_info, log_name, &log,
+ (Format_description_log_event *)ev, do_xa_recovery);
+ }
+ else
+ error= read_state_from_file();
+
+ delete ev;
+ end_io_cache(&log);
+ mysql_file_close(file, MYF(MY_WME));
+
+ return error;
+}
+
+
#ifdef INNODB_COMPATIBILITY_HOOKS
/**
Get the file name of the MySQL binlog.
diff --git a/sql/log.h b/sql/log.h
index feb79a796f1..928c1058b7f 100644
--- a/sql/log.h
+++ b/sql/log.h
@@ -521,8 +521,8 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
*/
uint *sync_period_ptr;
uint sync_counter;
- /* Protect against reading the binlog state file twice. */
- bool state_read;
+ bool state_file_deleted;
+ bool binlog_state_recover_done;
inline uint get_sync_period()
{
@@ -661,7 +661,8 @@ public:
int unlog(ulong cookie, my_xid xid);
void commit_checkpoint_notify(void *cookie);
int recover(LOG_INFO *linfo, const char *last_log_name, IO_CACHE *first_log,
- Format_description_log_event *fdle);
+ Format_description_log_event *fdle, bool do_xa);
+ int do_binlog_recovery(const char *opt_name, bool do_xa_recovery);
#if !defined(MYSQL_CLIENT)
int flush_and_set_pending_rows_event(THD *thd, Rows_log_event* event,
diff --git a/sql/log_event.cc b/sql/log_event.cc
index 272f0da6735..de53a26dd81 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -82,7 +82,6 @@ TYPELIB binlog_checksum_typelib=
#define FLAGSTR(V,F) ((V)&(F)?#F" ":"")
-
/*
Size of buffer for printing a double in format %.<PREC>g
@@ -790,6 +789,9 @@ const char* Log_event::get_type_str(Log_event_type type)
case PRE_GA_WRITE_ROWS_EVENT: return "Write_rows_event_old";
case PRE_GA_UPDATE_ROWS_EVENT: return "Update_rows_event_old";
case PRE_GA_DELETE_ROWS_EVENT: return "Delete_rows_event_old";
+ case WRITE_ROWS_EVENT_V1: return "Write_rows_v1";
+ case UPDATE_ROWS_EVENT_V1: return "Update_rows_v1";
+ case DELETE_ROWS_EVENT_V1: return "Delete_rows_v1";
case WRITE_ROWS_EVENT: return "Write_rows";
case UPDATE_ROWS_EVENT: return "Update_rows";
case DELETE_ROWS_EVENT: return "Delete_rows";
@@ -1684,12 +1686,15 @@ Log_event* Log_event::read_log_event(const char* buf, uint event_len,
case PRE_GA_DELETE_ROWS_EVENT:
ev = new Delete_rows_log_event_old(buf, event_len, description_event);
break;
+ case WRITE_ROWS_EVENT_V1:
case WRITE_ROWS_EVENT:
ev = new Write_rows_log_event(buf, event_len, description_event);
break;
+ case UPDATE_ROWS_EVENT_V1:
case UPDATE_ROWS_EVENT:
ev = new Update_rows_log_event(buf, event_len, description_event);
break;
+ case DELETE_ROWS_EVENT_V1:
case DELETE_ROWS_EVENT:
ev = new Delete_rows_log_event(buf, event_len, description_event);
break;
@@ -2507,9 +2512,33 @@ void Rows_log_event::print_verbose(IO_CACHE *file,
Table_map_log_event *map;
table_def *td;
const char *sql_command, *sql_clause1, *sql_clause2;
- Log_event_type type_code= get_type_code();
+ Log_event_type general_type_code= get_general_type_code();
- switch (type_code) {
+ if (m_extra_row_data)
+ {
+ uint8 extra_data_len= m_extra_row_data[EXTRA_ROW_INFO_LEN_OFFSET];
+ uint8 extra_payload_len= extra_data_len - EXTRA_ROW_INFO_HDR_BYTES;
+ assert(extra_data_len >= EXTRA_ROW_INFO_HDR_BYTES);
+
+ my_b_printf(file, "### Extra row data format: %u, len: %u :",
+ m_extra_row_data[EXTRA_ROW_INFO_FORMAT_OFFSET],
+ extra_payload_len);
+ if (extra_payload_len)
+ {
+ /*
+ Buffer for hex view of string, including '0x' prefix,
+ 2 hex chars / byte and trailing 0
+ */
+ const int buff_len= 2 + (256 * 2) + 1;
+ char buff[buff_len];
+ str_to_hex(buff, (const char*) &m_extra_row_data[EXTRA_ROW_INFO_HDR_BYTES],
+ extra_payload_len);
+ my_b_printf(file, "%s", buff);
+ }
+ my_b_printf(file, "\n");
+ }
+
+ switch (general_type_code) {
case WRITE_ROWS_EVENT:
sql_command= "INSERT INTO";
sql_clause1= "### SET\n";
@@ -2539,7 +2568,7 @@ void Rows_log_event::print_verbose(IO_CACHE *file,
}
/* If the write rows event contained no values for the AI */
- if (((type_code == WRITE_ROWS_EVENT) && (m_rows_buf==m_rows_end)))
+ if (((general_type_code == WRITE_ROWS_EVENT) && (m_rows_buf==m_rows_end)))
{
my_b_printf(file, "### INSERT INTO %`s.%`s VALUES ()\n",
map->get_db_name(), map->get_table_name());
@@ -2614,31 +2643,45 @@ void Log_event::print_base64(IO_CACHE* file,
if (print_event_info->verbose)
{
Rows_log_event *ev= NULL;
+ Log_event_type et= (Log_event_type) ptr[EVENT_TYPE_OFFSET];
+
if (checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF &&
checksum_alg != BINLOG_CHECKSUM_ALG_OFF)
size-= BINLOG_CHECKSUM_LEN; // checksum is displayed through the header
- if (ptr[4] == TABLE_MAP_EVENT)
+ switch (et)
+ {
+ case TABLE_MAP_EVENT:
{
Table_map_log_event *map;
map= new Table_map_log_event((const char*) ptr, size,
glob_description_event);
print_event_info->m_table_map.set_table(map->get_table_id(), map);
+ break;
}
- else if (ptr[4] == WRITE_ROWS_EVENT)
+ case WRITE_ROWS_EVENT:
+ case WRITE_ROWS_EVENT_V1:
{
ev= new Write_rows_log_event((const char*) ptr, size,
glob_description_event);
+ break;
}
- else if (ptr[4] == DELETE_ROWS_EVENT)
+ case DELETE_ROWS_EVENT:
+ case DELETE_ROWS_EVENT_V1:
{
ev= new Delete_rows_log_event((const char*) ptr, size,
glob_description_event);
+ break;
}
- else if (ptr[4] == UPDATE_ROWS_EVENT)
+ case UPDATE_ROWS_EVENT:
+ case UPDATE_ROWS_EVENT_V1:
{
ev= new Update_rows_log_event((const char*) ptr, size,
glob_description_event);
+ break;
+ }
+ default:
+ break;
}
if (ev)
@@ -4769,10 +4812,10 @@ Format_description_log_event(uint8 binlog_ver, const char* server_ver)
post_header_len[PRE_GA_UPDATE_ROWS_EVENT-1] = 0;
post_header_len[PRE_GA_DELETE_ROWS_EVENT-1] = 0;
- post_header_len[TABLE_MAP_EVENT-1]= TABLE_MAP_HEADER_LEN;
- post_header_len[WRITE_ROWS_EVENT-1]= ROWS_HEADER_LEN;
- post_header_len[UPDATE_ROWS_EVENT-1]= ROWS_HEADER_LEN;
- post_header_len[DELETE_ROWS_EVENT-1]= ROWS_HEADER_LEN;
+ post_header_len[TABLE_MAP_EVENT-1]= TABLE_MAP_HEADER_LEN;
+ post_header_len[WRITE_ROWS_EVENT_V1-1]= ROWS_HEADER_LEN_V1;
+ post_header_len[UPDATE_ROWS_EVENT_V1-1]= ROWS_HEADER_LEN_V1;
+ post_header_len[DELETE_ROWS_EVENT_V1-1]= ROWS_HEADER_LEN_V1;
/*
We here have the possibility to simulate a master of before we changed
the table map id to be stored in 6 bytes: when it was stored in 4
@@ -4785,11 +4828,16 @@ Format_description_log_event(uint8 binlog_ver, const char* server_ver)
*/
DBUG_EXECUTE_IF("old_row_based_repl_4_byte_map_id_master",
post_header_len[TABLE_MAP_EVENT-1]=
- post_header_len[WRITE_ROWS_EVENT-1]=
- post_header_len[UPDATE_ROWS_EVENT-1]=
- post_header_len[DELETE_ROWS_EVENT-1]= 6;);
+ post_header_len[WRITE_ROWS_EVENT_V1-1]=
+ post_header_len[UPDATE_ROWS_EVENT_V1-1]=
+ post_header_len[DELETE_ROWS_EVENT_V1-1]= 6;);
post_header_len[INCIDENT_EVENT-1]= INCIDENT_HEADER_LEN;
post_header_len[HEARTBEAT_LOG_EVENT-1]= 0;
+ post_header_len[IGNORABLE_LOG_EVENT-1]= 0;
+ post_header_len[ROWS_QUERY_LOG_EVENT-1]= 0;
+ post_header_len[WRITE_ROWS_EVENT-1]= ROWS_HEADER_LEN_V2;
+ post_header_len[UPDATE_ROWS_EVENT-1]= ROWS_HEADER_LEN_V2;
+ post_header_len[DELETE_ROWS_EVENT-1]= ROWS_HEADER_LEN_V2;
// Set header length of the reserved events to 0
memset(post_header_len + MYSQL_EVENTS_END - 1, 0,
@@ -5885,6 +5933,8 @@ error:
thd->get_stmt_da()->set_overwrite_status(false);
close_thread_tables(thd);
/*
+ - If transaction rollback was requested due to deadlock
+ perform it and release metadata locks.
- If inside a multi-statement transaction,
defer the release of metadata locks until the current
transaction is either committed or rolled back. This prevents
@@ -5894,7 +5944,12 @@ error:
- If in autocommit mode, or outside a transactional context,
automatically release metadata locks of the current statement.
*/
- if (! thd->in_multi_stmt_transaction_mode())
+ if (thd->transaction_rollback_request)
+ {
+ trans_rollback_implicit(thd);
+ thd->mdl_context.release_transactional_locks();
+ }
+ else if (! thd->in_multi_stmt_transaction_mode())
thd->mdl_context.release_transactional_locks();
else
thd->mdl_context.release_statement_locks();
@@ -9086,13 +9141,15 @@ const char *sql_ex_info::init(const char *buf, const char *buf_end,
#ifndef MYSQL_CLIENT
Rows_log_event::Rows_log_event(THD *thd_arg, TABLE *tbl_arg, ulong tid,
- MY_BITMAP const *cols, bool is_transactional)
+ MY_BITMAP const *cols, bool is_transactional,
+ Log_event_type event_type)
: Log_event(thd_arg, 0, is_transactional),
m_row_count(0),
m_table(tbl_arg),
m_table_id(tid),
m_width(tbl_arg ? tbl_arg->s->fields : 1),
- m_rows_buf(0), m_rows_cur(0), m_rows_end(0), m_flags(0)
+ m_rows_buf(0), m_rows_cur(0), m_rows_end(0), m_flags(0),
+ m_type(event_type), m_extra_row_data(0)
#ifdef HAVE_REPLICATION
, m_curr_row(NULL), m_curr_row_end(NULL),
m_key(NULL), m_key_info(NULL), m_key_nr(0)
@@ -9133,7 +9190,6 @@ Rows_log_event::Rows_log_event(THD *thd_arg, TABLE *tbl_arg, ulong tid,
#endif
Rows_log_event::Rows_log_event(const char *buf, uint event_len,
- Log_event_type event_type,
const Format_description_log_event
*description_event)
: Log_event(buf, description_event),
@@ -9141,7 +9197,8 @@ Rows_log_event::Rows_log_event(const char *buf, uint event_len,
#ifndef MYSQL_CLIENT
m_table(NULL),
#endif
- m_table_id(0), m_rows_buf(0), m_rows_cur(0), m_rows_end(0)
+ m_table_id(0), m_rows_buf(0), m_rows_cur(0), m_rows_end(0),
+ m_extra_row_data(0)
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
, m_curr_row(NULL), m_curr_row_end(NULL),
m_key(NULL), m_key_info(NULL), m_key_nr(0)
@@ -9149,6 +9206,9 @@ Rows_log_event::Rows_log_event(const char *buf, uint event_len,
{
DBUG_ENTER("Rows_log_event::Rows_log_event(const char*,...)");
uint8 const common_header_len= description_event->common_header_len;
+ Log_event_type event_type= (Log_event_type) buf[EVENT_TYPE_OFFSET];
+ m_type= event_type;
+
uint8 const post_header_len= description_event->post_header_len[event_type-1];
DBUG_PRINT("enter",("event_len: %u common_header_len: %d "
@@ -9171,9 +9231,54 @@ Rows_log_event::Rows_log_event(const char *buf, uint event_len,
}
m_flags= uint2korr(post_start);
+ post_start+= 2;
+
+ uint16 var_header_len= 0;
+ if (post_header_len == ROWS_HEADER_LEN_V2)
+ {
+ /*
+ Have variable length header, check length,
+ which includes length bytes
+ */
+ var_header_len= uint2korr(post_start);
+ assert(var_header_len >= 2);
+ var_header_len-= 2;
+
+ /* Iterate over var-len header, extracting 'chunks' */
+ const char* start= post_start + 2;
+ const char* end= start + var_header_len;
+ for (const char* pos= start; pos < end;)
+ {
+ switch(*pos++)
+ {
+ case RW_V_EXTRAINFO_TAG:
+ {
+ /* Have an 'extra info' section, read it in */
+ assert((end - pos) >= EXTRA_ROW_INFO_HDR_BYTES);
+ uint8 infoLen= pos[EXTRA_ROW_INFO_LEN_OFFSET];
+ assert((end - pos) >= infoLen);
+ /* Just store/use the first tag of this type, skip others */
+ if (likely(!m_extra_row_data))
+ {
+ m_extra_row_data= (uchar*) my_malloc(infoLen,
+ MYF(MY_WME));
+ if (likely(m_extra_row_data != NULL))
+ {
+ memcpy(m_extra_row_data, pos, infoLen);
+ }
+ }
+ pos+= infoLen;
+ break;
+ }
+ default:
+ /* Unknown code, we will not understand anything further here */
+ pos= end; /* Break loop */
+ }
+ }
+ }
uchar const *const var_start=
- (const uchar *)buf + common_header_len + post_header_len;
+ (const uchar *)buf + common_header_len + post_header_len + var_header_len;
uchar const *const ptr_width= var_start;
uchar *ptr_after_width= (uchar*) ptr_width;
DBUG_PRINT("debug", ("Reading from %p", ptr_after_width));
@@ -9200,7 +9305,8 @@ Rows_log_event::Rows_log_event(const char *buf, uint event_len,
m_cols_ai.bitmap= m_cols.bitmap; /* See explanation in is_valid() */
- if (event_type == UPDATE_ROWS_EVENT)
+ if ((event_type == UPDATE_ROWS_EVENT) ||
+ (event_type == UPDATE_ROWS_EVENT_V1))
{
DBUG_PRINT("debug", ("Reading from %p", ptr_after_width));
@@ -9253,24 +9359,38 @@ Rows_log_event::~Rows_log_event()
m_cols.bitmap= 0; // so no my_free in bitmap_free
bitmap_free(&m_cols); // To pair with bitmap_init().
my_free(m_rows_buf);
+ my_free(m_extra_row_data);
}
int Rows_log_event::get_data_size()
{
- int const type_code= get_type_code();
+ int const general_type_code= get_general_type_code();
- uchar buf[sizeof(m_width) + 1];
+ uchar buf[MAX_INT_WIDTH];
uchar *end= net_store_length(buf, m_width);
DBUG_EXECUTE_IF("old_row_based_repl_4_byte_map_id_master",
return 6 + no_bytes_in_map(&m_cols) + (end - buf) +
- (type_code == UPDATE_ROWS_EVENT ? no_bytes_in_map(&m_cols_ai) : 0) +
+ (general_type_code == UPDATE_ROWS_EVENT ? no_bytes_in_map(&m_cols_ai) : 0) +
(m_rows_cur - m_rows_buf););
- int data_size= ROWS_HEADER_LEN;
+
+ int data_size= 0;
+ bool is_v2_event= get_type_code() > DELETE_ROWS_EVENT_V1;
+ if (is_v2_event)
+ {
+ data_size= ROWS_HEADER_LEN_V2 +
+ (m_extra_row_data ?
+ RW_V_TAG_LEN + m_extra_row_data[EXTRA_ROW_INFO_LEN_OFFSET]:
+ 0);
+ }
+ else
+ {
+ data_size= ROWS_HEADER_LEN_V1;
+ }
data_size+= no_bytes_in_map(&m_cols);
data_size+= (uint) (end - buf);
- if (type_code == UPDATE_ROWS_EVENT)
+ if (general_type_code == UPDATE_ROWS_EVENT)
data_size+= no_bytes_in_map(&m_cols_ai);
data_size+= (uint) (m_rows_cur - m_rows_buf);
@@ -9552,7 +9672,7 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi)
*/
thd->set_time(when, when_sec_part);
- if ( m_width == table->s->fields && bitmap_is_set_all(&m_cols))
+ if (m_width == table->s->fields && bitmap_is_set_all(&m_cols))
set_flags(COMPLETE_ROWS_F);
/*
@@ -9766,7 +9886,10 @@ static int rows_event_stmt_cleanup(rpl_group_info *rgi, THD * thd)
Xid_log_event will come next which will, if some transactional engines
are involved, commit the transaction and flush the pending event to the
binlog.
+ If there was a deadlock the transaction should have been rolled back
+ already. So there should be no need to rollback the transaction.
*/
+ DBUG_ASSERT(! thd->transaction_rollback_request);
error|= (error ? trans_rollback_stmt(thd) : trans_commit_stmt(thd));
/*
@@ -9852,7 +9975,7 @@ Rows_log_event::do_update_pos(rpl_group_info *rgi)
#ifndef MYSQL_CLIENT
bool Rows_log_event::write_data_header(IO_CACHE *file)
{
- uchar buf[ROWS_HEADER_LEN]; // No need to init the buffer
+ uchar buf[ROWS_HEADER_LEN_V2]; // No need to init the buffer
DBUG_ASSERT(m_table_id != ~0UL);
DBUG_EXECUTE_IF("old_row_based_repl_4_byte_map_id_master",
{
@@ -9871,7 +9994,7 @@ bool Rows_log_event::write_data_body(IO_CACHE*file)
Note that this should be the number of *bits*, not the number of
bytes.
*/
- uchar sbuf[sizeof(m_width) + 1];
+ uchar sbuf[MAX_INT_WIDTH];
my_ptrdiff_t const data_size= m_rows_cur - m_rows_buf;
bool res= false;
uchar *const sbuf_end= net_store_length(sbuf, (size_t) m_width);
@@ -9886,7 +10009,7 @@ bool Rows_log_event::write_data_body(IO_CACHE*file)
/*
TODO[refactor write]: Remove the "down cast" here (and elsewhere).
*/
- if (get_type_code() == UPDATE_ROWS_EVENT)
+ if (get_general_type_code() == UPDATE_ROWS_EVENT)
{
DBUG_DUMP("m_cols_ai", (uchar*) m_cols_ai.bitmap,
no_bytes_in_map(&m_cols_ai));
@@ -10176,7 +10299,7 @@ Table_map_log_event::Table_map_log_event(THD *thd, TABLE *tbl, ulong tid,
m_null_bits(0),
m_meta_memory(NULL)
{
- uchar cbuf[sizeof(m_colcnt) + 1];
+ uchar cbuf[MAX_INT_WIDTH];
uchar *cbuf_end;
DBUG_ASSERT(m_table_id != ~0UL);
/*
@@ -10730,14 +10853,14 @@ bool Table_map_log_event::write_data_body(IO_CACHE *file)
uchar const dbuf[]= { (uchar) m_dblen };
uchar const tbuf[]= { (uchar) m_tbllen };
- uchar cbuf[sizeof(m_colcnt) + 1];
+ uchar cbuf[MAX_INT_WIDTH];
uchar *const cbuf_end= net_store_length(cbuf, (size_t) m_colcnt);
DBUG_ASSERT(static_cast<size_t>(cbuf_end - cbuf) <= sizeof(cbuf));
/*
Store the size of the field metadata.
*/
- uchar mbuf[sizeof(m_field_metadata_size)];
+ uchar mbuf[MAX_INT_WIDTH];
uchar *const mbuf_end= net_store_length(mbuf, m_field_metadata_size);
return (wrapper_my_b_safe_write(file, dbuf, sizeof(dbuf)) ||
@@ -10800,7 +10923,7 @@ Write_rows_log_event::Write_rows_log_event(THD *thd_arg, TABLE *tbl_arg,
ulong tid_arg,
MY_BITMAP const *cols,
bool is_transactional)
- : Rows_log_event(thd_arg, tbl_arg, tid_arg, cols, is_transactional)
+ : Rows_log_event(thd_arg, tbl_arg, tid_arg, cols, is_transactional, WRITE_ROWS_EVENT_V1)
{
}
#endif
@@ -10812,7 +10935,7 @@ Write_rows_log_event::Write_rows_log_event(THD *thd_arg, TABLE *tbl_arg,
Write_rows_log_event::Write_rows_log_event(const char *buf, uint event_len,
const Format_description_log_event
*description_event)
-: Rows_log_event(buf, event_len, WRITE_ROWS_EVENT, description_event)
+: Rows_log_event(buf, event_len, description_event)
{
}
#endif
@@ -11800,7 +11923,7 @@ int Rows_log_event::find_row(rpl_group_info *rgi)
end:
if (is_table_scan || is_index_scan)
- issue_long_find_row_warning(get_type_code(), m_table->alias.c_ptr(),
+ issue_long_find_row_warning(get_general_type_code(), m_table->alias.c_ptr(),
is_index_scan, rgi);
table->default_column_bitmaps();
DBUG_RETURN(error);
@@ -11816,7 +11939,7 @@ end:
Delete_rows_log_event::Delete_rows_log_event(THD *thd_arg, TABLE *tbl_arg,
ulong tid, MY_BITMAP const *cols,
bool is_transactional)
- : Rows_log_event(thd_arg, tbl_arg, tid, cols, is_transactional)
+ : Rows_log_event(thd_arg, tbl_arg, tid, cols, is_transactional, DELETE_ROWS_EVENT_V1)
{
}
#endif /* #if !defined(MYSQL_CLIENT) */
@@ -11828,7 +11951,7 @@ Delete_rows_log_event::Delete_rows_log_event(THD *thd_arg, TABLE *tbl_arg,
Delete_rows_log_event::Delete_rows_log_event(const char *buf, uint event_len,
const Format_description_log_event
*description_event)
- : Rows_log_event(buf, event_len, DELETE_ROWS_EVENT, description_event)
+ : Rows_log_event(buf, event_len, description_event)
{
}
#endif
@@ -11934,7 +12057,7 @@ Update_rows_log_event::Update_rows_log_event(THD *thd_arg, TABLE *tbl_arg,
MY_BITMAP const *cols_bi,
MY_BITMAP const *cols_ai,
bool is_transactional)
-: Rows_log_event(thd_arg, tbl_arg, tid, cols_bi, is_transactional)
+: Rows_log_event(thd_arg, tbl_arg, tid, cols_bi, is_transactional, UPDATE_ROWS_EVENT_V1)
{
init(cols_ai);
}
@@ -11943,7 +12066,7 @@ Update_rows_log_event::Update_rows_log_event(THD *thd_arg, TABLE *tbl_arg,
ulong tid,
MY_BITMAP const *cols,
bool is_transactional)
-: Rows_log_event(thd_arg, tbl_arg, tid, cols, is_transactional)
+: Rows_log_event(thd_arg, tbl_arg, tid, cols, is_transactional, UPDATE_ROWS_EVENT_V1)
{
init(cols);
}
@@ -11983,7 +12106,7 @@ Update_rows_log_event::Update_rows_log_event(const char *buf, uint event_len,
const
Format_description_log_event
*description_event)
- : Rows_log_event(buf, event_len, UPDATE_ROWS_EVENT, description_event)
+ : Rows_log_event(buf, event_len, description_event)
{
}
#endif
diff --git a/sql/log_event.h b/sql/log_event.h
index 138ed2c6926..cd00b40f080 100644
--- a/sql/log_event.h
+++ b/sql/log_event.h
@@ -254,12 +254,13 @@ struct sql_ex_info
#define FORMAT_DESCRIPTION_HEADER_LEN (START_V3_HEADER_LEN+1+LOG_EVENT_TYPES)
#define XID_HEADER_LEN 0
#define BEGIN_LOAD_QUERY_HEADER_LEN APPEND_BLOCK_HEADER_LEN
-#define ROWS_HEADER_LEN 8
+#define ROWS_HEADER_LEN_V1 8
#define TABLE_MAP_HEADER_LEN 8
#define EXECUTE_LOAD_QUERY_EXTRA_HEADER_LEN (4 + 4 + 4 + 1)
#define EXECUTE_LOAD_QUERY_HEADER_LEN (QUERY_HEADER_LEN + EXECUTE_LOAD_QUERY_EXTRA_HEADER_LEN)
#define INCIDENT_HEADER_LEN 2
#define HEARTBEAT_HEADER_LEN 0
+#define ROWS_HEADER_LEN_V2 10
#define ANNOTATE_ROWS_HEADER_LEN 0
#define BINLOG_CHECKPOINT_HEADER_LEN 4
#define GTID_HEADER_LEN 19
@@ -415,6 +416,9 @@ struct sql_ex_info
/* RW = "RoWs" */
#define RW_MAPID_OFFSET 0
#define RW_FLAGS_OFFSET 6
+#define RW_VHLEN_OFFSET 8
+#define RW_V_TAG_LEN 1
+#define RW_V_EXTRAINFO_TAG 0
/* ELQ = "Execute Load Query" */
#define ELQ_FILE_ID_OFFSET QUERY_HEADER_LEN
@@ -659,11 +663,11 @@ enum Log_event_type
PRE_GA_DELETE_ROWS_EVENT = 22,
/*
- These event numbers are used from 5.1.16 and forward
+ These event numbers are used from 5.1.16 until mysql-trunk-xx
*/
- WRITE_ROWS_EVENT = 23,
- UPDATE_ROWS_EVENT = 24,
- DELETE_ROWS_EVENT = 25,
+ WRITE_ROWS_EVENT_V1 = 23,
+ UPDATE_ROWS_EVENT_V1 = 24,
+ DELETE_ROWS_EVENT_V1 = 25,
/*
Something out of the ordinary happened on the master
@@ -677,6 +681,20 @@ enum Log_event_type
HEARTBEAT_LOG_EVENT= 27,
/*
+ In some situations, it is necessary to send over ignorable
+ data to the slave: data that a slave can handle in case there
+ is code for handling it, but which can be ignored if it is not
+ recognized.
+ */
+ IGNORABLE_LOG_EVENT= 28,
+ ROWS_QUERY_LOG_EVENT= 29,
+
+ /* Version 2 of the Row events */
+ WRITE_ROWS_EVENT = 30,
+ UPDATE_ROWS_EVENT = 31,
+ DELETE_ROWS_EVENT = 32,
+
+ /*
Add new events here - right above this comment!
Existing events (except ENUM_END_EVENT) should never change their numbers
*/
@@ -4178,6 +4196,9 @@ public:
void clear_flags(flag_set flags_arg) { m_flags &= ~flags_arg; }
flag_set get_flags(flag_set flags_arg) const { return m_flags & flags_arg; }
+ Log_event_type get_type_code() { return m_type; } /* Specific type (_V1 etc) */
+ virtual Log_event_type get_general_type_code() = 0; /* General rows op type, no version */
+
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
virtual void pack_info(THD *thd, Protocol *protocol);
#endif
@@ -4226,6 +4247,8 @@ public:
uint m_row_count; /* The number of rows added to the event */
+ const uchar* get_extra_row_data() const { return m_extra_row_data; }
+
protected:
/*
The constructors are protected since you're supposed to inherit
@@ -4233,10 +4256,10 @@ protected:
*/
#ifdef MYSQL_SERVER
Rows_log_event(THD*, TABLE*, ulong table_id,
- MY_BITMAP const *cols, bool is_transactional);
+ MY_BITMAP const *cols, bool is_transactional,
+ Log_event_type event_type);
#endif
Rows_log_event(const char *row_data, uint event_len,
- Log_event_type event_type,
const Format_description_log_event *description_event);
#ifdef MYSQL_CLIENT
@@ -4274,6 +4297,11 @@ protected:
flag_set m_flags; /* Flags for row-level events */
+ Log_event_type m_type; /* Actual event type */
+
+ uchar *m_extra_row_data; /* Pointer to extra row data if any */
+ /* If non null, first byte is length */
+
/* helper functions */
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
@@ -4406,7 +4434,7 @@ public:
#endif
private:
- virtual Log_event_type get_type_code() { return (Log_event_type)TYPE_CODE; }
+ virtual Log_event_type get_general_type_code() { return (Log_event_type)TYPE_CODE; }
#ifdef MYSQL_CLIENT
void print(FILE *file, PRINT_EVENT_INFO *print_event_info);
@@ -4480,7 +4508,7 @@ public:
}
protected:
- virtual Log_event_type get_type_code() { return (Log_event_type)TYPE_CODE; }
+ virtual Log_event_type get_general_type_code() { return (Log_event_type)TYPE_CODE; }
#ifdef MYSQL_CLIENT
void print(FILE *file, PRINT_EVENT_INFO *print_event_info);
@@ -4545,7 +4573,7 @@ public:
#endif
protected:
- virtual Log_event_type get_type_code() { return (Log_event_type)TYPE_CODE; }
+ virtual Log_event_type get_general_type_code() { return (Log_event_type)TYPE_CODE; }
#ifdef MYSQL_CLIENT
void print(FILE *file, PRINT_EVENT_INFO *print_event_info);
diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc
index 211768150af..7b89d5bdf08 100644
--- a/sql/log_event_old.cc
+++ b/sql/log_event_old.cc
@@ -1,4 +1,4 @@
-/* Copyright (c) 2007, 2012, Oracle and/or its affiliates.
+/* Copyright (c) 2007, 2013, Oracle and/or its affiliates.
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
@@ -1366,7 +1366,7 @@ Old_rows_log_event::~Old_rows_log_event()
int Old_rows_log_event::get_data_size()
{
- uchar buf[sizeof(m_width)+1];
+ uchar buf[MAX_INT_WIDTH];
uchar *end= net_store_length(buf, (m_width + 7) / 8);
DBUG_EXECUTE_IF("old_row_based_repl_4_byte_map_id_master",
@@ -1771,7 +1771,10 @@ int Old_rows_log_event::do_apply_event(rpl_group_info *rgi)
Xid_log_event will come next which will, if some transactional engines
are involved, commit the transaction and flush the pending event to the
binlog.
+ If there was a deadlock the transaction should have been rolled back
+ already. So there should be no need to rollback the transaction.
*/
+ DBUG_ASSERT(! thd->transaction_rollback_request);
if ((error= (binlog_error ? trans_rollback_stmt(thd) : trans_commit_stmt(thd))))
rli->report(ERROR_LEVEL, error,
"Error in %s event: commit of row events failed, "
@@ -1876,7 +1879,7 @@ bool Old_rows_log_event::write_data_body(IO_CACHE*file)
Note that this should be the number of *bits*, not the number of
bytes.
*/
- uchar sbuf[sizeof(m_width)];
+ uchar sbuf[MAX_INT_WIDTH];
my_ptrdiff_t const data_size= m_rows_cur - m_rows_buf;
// This method should not be reached.
diff --git a/sql/log_event_old.h b/sql/log_event_old.h
index ef81739a543..97fc24a1bef 100644
--- a/sql/log_event_old.h
+++ b/sql/log_event_old.h
@@ -41,6 +41,9 @@
but we keep them this way for now. /Sven
*/
+/* These classes are based on the v1 RowsHeaderLen */
+#undef ROWS_HEADER_LEN
+#define ROWS_HEADER_LEN ROWS_HEADER_LEN_V1
/**
@class Old_rows_log_event
diff --git a/sql/mdl.cc b/sql/mdl.cc
index c74a3e964cb..2f00388a14b 100644
--- a/sql/mdl.cc
+++ b/sql/mdl.cc
@@ -170,6 +170,7 @@ private:
I_P_List_counter>
Lock_cache;
Lock_cache m_unused_locks_cache;
+ friend int mdl_iterate(int (*)(MDL_ticket *, void *), void *);
};
@@ -199,6 +200,7 @@ private:
MDL_lock *m_global_lock;
/** Pre-allocated MDL_lock object for COMMIT namespace. */
MDL_lock *m_commit_lock;
+ friend int mdl_iterate(int (*)(MDL_ticket *, void *), void *);
};
@@ -718,6 +720,46 @@ void mdl_destroy()
}
+static inline int mdl_iterate_lock(MDL_lock *lock,
+ int (*callback)(MDL_ticket *ticket, void *arg),
+ void *arg)
+{
+ MDL_lock::Ticket_iterator ticket_it(lock->m_granted);
+ MDL_ticket *ticket;
+ int res= 0;
+ mysql_prlock_rdlock(&lock->m_rwlock);
+ while ((ticket= ticket_it++) && !(res= callback(ticket, arg))) /* no-op */;
+ mysql_prlock_unlock(&lock->m_rwlock);
+ return res;
+}
+
+
+int mdl_iterate(int (*callback)(MDL_ticket *ticket, void *arg), void *arg)
+{
+ uint i, j;
+ int res;
+ DBUG_ENTER("mdl_iterate");
+
+ if ((res= mdl_iterate_lock(mdl_locks.m_global_lock, callback, arg)) ||
+ (res= mdl_iterate_lock(mdl_locks.m_commit_lock, callback, arg)))
+ DBUG_RETURN(res);
+
+ for (i= 0; i < mdl_locks.m_partitions.elements(); i++)
+ {
+ MDL_map_partition *part= mdl_locks.m_partitions.at(i);
+ mysql_mutex_lock(&part->m_mutex);
+ for (j= 0; j < part->m_locks.records; j++)
+ {
+ if ((res= mdl_iterate_lock((MDL_lock*) my_hash_element(&part->m_locks, j),
+ callback, arg)))
+ break;
+ }
+ mysql_mutex_unlock(&part->m_mutex);
+ }
+ DBUG_RETURN(res);
+}
+
+
/** Initialize the container for all MDL locks. */
void MDL_map::init()
diff --git a/sql/mdl.h b/sql/mdl.h
index 75132962f8f..b1e6bfa428a 100644
--- a/sql/mdl.h
+++ b/sql/mdl.h
@@ -960,6 +960,9 @@ public:
private:
MDL_context(const MDL_context &rhs); /* not implemented */
MDL_context &operator=(MDL_context &rhs); /* not implemented */
+
+ /* metadata_lock_info plugin */
+ friend int i_s_metadata_lock_info_fill_row(MDL_ticket*, void*);
};
@@ -1004,4 +1007,7 @@ static const ulong MDL_LOCKS_HASH_PARTITIONS_DEFAULT = 8;
to avoid starving out weak, low-prio locks.
*/
extern "C" ulong max_write_lock_count;
+
+extern MYSQL_PLUGIN_IMPORT
+int mdl_iterate(int (*callback)(MDL_ticket *ticket, void *arg), void *arg);
#endif
diff --git a/sql/my_apc.h b/sql/my_apc.h
index a12db5093a4..4643e641ff1 100644
--- a/sql/my_apc.h
+++ b/sql/my_apc.h
@@ -134,6 +134,8 @@ private:
#ifdef HAVE_PSI_INTERFACE
void init_show_explain_psi_keys(void);
+#else
+#define init_show_explain_psi_keys() /* no-op */
#endif
#endif //SQL_MY_APC_INCLUDED
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 517e2a6d110..cef30d1ecc4 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -715,8 +715,6 @@ mysql_mutex_t
mysql_mutex_t LOCK_stats, LOCK_global_user_client_stats,
LOCK_global_table_stats, LOCK_global_index_stats;
-mysql_mutex_t LOCK_rpl_gtid_state;
-
/**
The below lock protects access to two global server variables:
max_prepared_stmt_count and prepared_stmt_count. These variables
@@ -793,8 +791,9 @@ static char **remaining_argv;
int orig_argc;
char **orig_argv;
-static struct my_option pfs_early_options[] __attribute__((unused)) =
+static struct my_option pfs_early_options[]=
{
+#ifdef WITH_PERFSCHEMA_STORAGE_ENGINE
{"performance_schema_instrument", OPT_PFS_INSTRUMENT,
"Default startup value for a performance schema instrument.",
&pfs_param.m_pfs_instrument, &pfs_param.m_pfs_instrument, 0, GET_STR,
@@ -859,6 +858,7 @@ static struct my_option pfs_early_options[] __attribute__((unused)) =
&pfs_param.m_consumer_statement_digest_enabled,
&pfs_param.m_consumer_statement_digest_enabled, 0,
GET_BOOL, OPT_ARG, TRUE, 0, 0, 0, 0, 0}
+#endif /* WITH_PERFSCHEMA_STORAGE_ENGINE */
};
#ifdef HAVE_PSI_INTERFACE
@@ -906,8 +906,6 @@ PSI_mutex_key key_LOCK_stats,
key_LOCK_global_index_stats,
key_LOCK_wakeup_ready, key_LOCK_wait_commit;
-PSI_mutex_key key_LOCK_rpl_gtid_state;
-
PSI_mutex_key key_LOCK_prepare_ordered, key_LOCK_commit_ordered;
PSI_mutex_key key_TABLE_SHARE_LOCK_share;
@@ -951,7 +949,6 @@ static PSI_mutex_info all_server_mutexes[]=
{ &key_LOCK_global_table_stats, "LOCK_global_table_stats", PSI_FLAG_GLOBAL},
{ &key_LOCK_global_index_stats, "LOCK_global_index_stats", PSI_FLAG_GLOBAL},
{ &key_LOCK_wakeup_ready, "THD::LOCK_wakeup_ready", 0},
- { &key_LOCK_rpl_gtid_state, "LOCK_rpl_gtid_state", PSI_FLAG_GLOBAL},
{ &key_LOCK_wait_commit, "wait_for_commit::LOCK_wait_commit", 0},
{ &key_LOCK_thd_data, "THD::LOCK_thd_data", 0},
{ &key_LOCK_user_conn, "LOCK_user_conn", PSI_FLAG_GLOBAL},
@@ -1213,7 +1210,6 @@ void net_after_header_psi(struct st_net *net, void *user_data, size_t /* unused:
void init_net_server_extension(THD *thd)
{
-#ifdef HAVE_PSI_INTERFACE
/* Start with a clean state for connection events. */
thd->m_idle_psi= NULL;
thd->m_statement_psi= NULL;
@@ -1224,9 +1220,6 @@ void init_net_server_extension(THD *thd)
thd->m_net_server_extension.m_after_header= net_after_header_psi;
/* Activate this private extension for the mysqld server. */
thd->net.extension= & thd->m_net_server_extension;
-#else
- thd->net.extension= NULL;
-#endif
}
#endif /* EMBEDDED_LIBRARY */
@@ -1874,11 +1867,12 @@ void kill_mysql(void)
if (!kill_in_progress)
{
pthread_t tmp;
+ int error;
abort_loop=1;
- if (mysql_thread_create(0, /* Not instrumented */
- &tmp, &connection_attrib, kill_server_thread,
- (void*) 0))
- sql_print_error("Can't create thread to kill server");
+ if ((error= mysql_thread_create(0, /* Not instrumented */
+ &tmp, &connection_attrib,
+ kill_server_thread, (void*) 0)))
+ sql_print_error("Can't create thread to kill server (errno= %d).", error);
}
#endif
DBUG_VOID_RETURN;
@@ -2158,7 +2152,9 @@ void clean_up(bool print_message)
delete binlog_filter;
delete global_rpl_filter;
end_ssl();
+#ifndef EMBEDDED_LIBRARY
vio_end();
+#endif /*!EMBEDDED_LIBRARY*/
#if defined(ENABLED_DEBUG_SYNC)
/* End the debug sync facility. See debug_sync.cc. */
debug_sync_end();
@@ -2238,7 +2234,6 @@ static void clean_up_mutexes()
mysql_mutex_destroy(&LOCK_global_user_client_stats);
mysql_mutex_destroy(&LOCK_global_table_stats);
mysql_mutex_destroy(&LOCK_global_index_stats);
- mysql_mutex_destroy(&LOCK_rpl_gtid_state);
#ifdef HAVE_OPENSSL
mysql_mutex_destroy(&LOCK_des_key_file);
#ifndef HAVE_YASSL
@@ -3463,10 +3458,12 @@ pthread_handler_t signal_hand(void *arg __attribute__((unused)))
#endif
#ifdef USE_ONE_SIGNAL_HAND
pthread_t tmp;
- if (mysql_thread_create(0, /* Not instrumented */
- &tmp, &connection_attrib, kill_server_thread,
- (void*) &sig))
- sql_print_error("Can't create thread to kill server");
+ if ((error= mysql_thread_create(0, /* Not instrumented */
+ &tmp, &connection_attrib,
+ kill_server_thread,
+ (void*) &sig)))
+ sql_print_error("Can't create thread to kill server (errno= %d)",
+ error);
#else
kill_server((void*) sig); // MIT THREAD has a alarm thread
#endif
@@ -3962,17 +3959,13 @@ static void my_malloc_size_cb_func(long long size, my_bool is_thread_specific)
However, this should never happen, so better to assert and
fix this.
*/
-#ifdef ENABLE_BEFORE_END_OF_MERGE_QQ
DBUG_ASSERT(thd);
-#endif
if (thd)
{
DBUG_PRINT("info", ("memory_used: %lld size: %lld",
(longlong) thd->status_var.memory_used, size));
thd->status_var.memory_used+= size;
-#ifdef ENABLE_BEFORE_END_OF_MERGE_QQ
DBUG_ASSERT((longlong) thd->status_var.memory_used >= 0);
-#endif
}
}
}
@@ -4513,8 +4506,6 @@ static int init_thread_environment()
&LOCK_global_table_stats, MY_MUTEX_INIT_FAST);
mysql_mutex_init(key_LOCK_global_index_stats,
&LOCK_global_index_stats, MY_MUTEX_INIT_FAST);
- mysql_mutex_init(key_LOCK_rpl_gtid_state,
- &LOCK_rpl_gtid_state, MY_MUTEX_INIT_SLOW);
mysql_mutex_init(key_LOCK_prepare_ordered, &LOCK_prepare_ordered,
MY_MUTEX_INIT_SLOW);
mysql_cond_init(key_COND_prepare_ordered, &COND_prepare_ordered, NULL);
@@ -5196,9 +5187,12 @@ static void create_shutdown_thread()
#ifdef __WIN__
hEventShutdown=CreateEvent(0, FALSE, FALSE, shutdown_event_name);
pthread_t hThread;
- if (mysql_thread_create(key_thread_handle_shutdown,
- &hThread, &connection_attrib, handle_shutdown, 0))
- sql_print_warning("Can't create thread to handle shutdown requests");
+ int error;
+ if ((error= mysql_thread_create(key_thread_handle_shutdown,
+ &hThread, &connection_attrib,
+ handle_shutdown, 0)))
+ sql_print_warning("Can't create thread to handle shutdown requests"
+ " (errno= %d)", error);
// On "Stop Service" we have to do regular shutdown
Service.SetShutdownEvent(hEventShutdown);
@@ -5674,6 +5668,7 @@ void wsrep_kill_mysql(THD *thd)
static void handle_connections_methods()
{
pthread_t hThread;
+ int error;
DBUG_ENTER("handle_connections_methods");
if (hPipe == INVALID_HANDLE_VALUE &&
(!have_tcpip || opt_disable_networking) &&
@@ -5689,22 +5684,24 @@ static void handle_connections_methods()
if (hPipe != INVALID_HANDLE_VALUE)
{
handler_count++;
- if (mysql_thread_create(key_thread_handle_con_namedpipes,
- &hThread, &connection_attrib,
- handle_connections_namedpipes, 0))
+ if ((error= mysql_thread_create(key_thread_handle_con_namedpipes,
+ &hThread, &connection_attrib,
+ handle_connections_namedpipes, 0)))
{
- sql_print_warning("Can't create thread to handle named pipes");
+ sql_print_warning("Can't create thread to handle named pipes"
+ " (errno= %d)", error);
handler_count--;
}
}
if (have_tcpip && !opt_disable_networking)
{
handler_count++;
- if (mysql_thread_create(key_thread_handle_con_sockets,
- &hThread, &connection_attrib,
- handle_connections_sockets_thread, 0))
+ if ((error= mysql_thread_create(key_thread_handle_con_sockets,
+ &hThread, &connection_attrib,
+ handle_connections_sockets_thread, 0)))
{
- sql_print_warning("Can't create thread to handle TCP/IP");
+ sql_print_warning("Can't create thread to handle TCP/IP",
+ " (errno= %d)", error);
handler_count--;
}
}
@@ -5712,11 +5709,12 @@ static void handle_connections_methods()
if (opt_enable_shared_memory)
{
handler_count++;
- if (mysql_thread_create(key_thread_handle_con_sharedmem,
- &hThread, &connection_attrib,
- handle_connections_shared_memory, 0))
+ if ((error= mysql_thread_create(key_thread_handle_con_sharedmem,
+ &hThread, &connection_attrib,
+ handle_connections_shared_memory, 0)))
{
- sql_print_warning("Can't create thread to handle shared memory");
+ sql_print_warning("Can't create thread to handle shared memory",
+ " (errno= %d)", error);
handler_count--;
}
}
@@ -5742,6 +5740,42 @@ void decrement_handler_count()
#ifndef EMBEDDED_LIBRARY
+
+LEX_STRING sql_statement_names[(uint) SQLCOM_END + 1];
+
+static void init_sql_statement_names()
+{
+ char *first_com= (char*) offsetof(STATUS_VAR, com_stat[0]);
+ char *last_com= (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_END]);
+ int record_size= (char*) offsetof(STATUS_VAR, com_stat[1])
+ - (char*) offsetof(STATUS_VAR, com_stat[0]);
+ char *ptr;
+ uint i;
+ uint com_index;
+
+ for (i= 0; i < ((uint) SQLCOM_END + 1); i++)
+ sql_statement_names[i]= empty_lex_str;
+
+ SHOW_VAR *var= &com_status_vars[0];
+ while (var->name != NULL)
+ {
+ ptr= var->value;
+ if ((first_com <= ptr) && (ptr <= last_com))
+ {
+ com_index= ((int)(ptr - first_com))/record_size;
+ DBUG_ASSERT(com_index < (uint) SQLCOM_END);
+ sql_statement_names[com_index].str= const_cast<char *>(var->name);
+ sql_statement_names[com_index].length= strlen(var->name);
+ }
+ var++;
+ }
+
+ DBUG_ASSERT(strcmp(sql_statement_names[(uint) SQLCOM_SELECT].str, "select") == 0);
+ DBUG_ASSERT(strcmp(sql_statement_names[(uint) SQLCOM_SIGNAL].str, "signal") == 0);
+
+ sql_statement_names[(uint) SQLCOM_END].str= const_cast<char*>("error");
+}
+
#ifndef DBUG_OFF
/*
Debugging helper function to keep the locale database
@@ -5823,6 +5857,7 @@ int mysqld_main(int argc, char **argv)
/* Must be initialized early for comparison of options name */
system_charset_info= &my_charset_utf8_general_ci;
+ init_sql_statement_names();
sys_var_init();
#ifdef WITH_PERFSCHEMA_STORAGE_ENGINE
@@ -5838,9 +5873,11 @@ int mysqld_main(int argc, char **argv)
buffered_logs.init();
my_getopt_error_reporter= buffered_option_error_reporter;
my_charset_error_reporter= buffered_option_error_reporter;
+#ifdef WITH_PERFSCHEMA_STORAGE_ENGINE
pfs_param.m_pfs_instrument= const_cast<char*>("");
+#endif /* WITH_PERFSCHEMA_STORAGE_ENGINE */
- int ho_error= handle_early_options();
+ int ho_error __attribute__((unused))= handle_early_options();
#ifdef WITH_PERFSCHEMA_STORAGE_ENGINE
if (ho_error == 0)
@@ -6460,11 +6497,14 @@ static void bootstrap(MYSQL_FILE *file)
bootstrap_file=file;
#ifndef EMBEDDED_LIBRARY // TODO: Enable this
- if (mysql_thread_create(key_thread_bootstrap,
- &thd->real_id, &connection_attrib, handle_bootstrap,
- (void*) thd))
+ int error;
+ if ((error= mysql_thread_create(key_thread_bootstrap,
+ &thd->real_id, &connection_attrib,
+ handle_bootstrap,
+ (void*) thd)))
{
- sql_print_warning("Can't create thread to handle bootstrap");
+ sql_print_warning("Can't create thread to handle bootstrap (errno= %d)",
+ error);
bootstrap_error=-1;
DBUG_VOID_RETURN;
}
@@ -7369,6 +7409,19 @@ int handle_early_options()
}
+#define MYSQL_COMPATIBILITY_OPTION(option) \
+ { option, OPT_MYSQL_COMPATIBILITY, \
+ 0, 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0 }
+
+#define MYSQL_TO_BE_IMPLEMENTED_OPTION(option) \
+ { option, OPT_MYSQL_TO_BE_IMPLEMENTED, \
+ 0, 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0 }
+
+#define MYSQL_SUGGEST_ANALOG_OPTION(option, str) \
+ { option, OPT_MYSQL_COMPATIBILITY, \
+ 0, 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0 }
+
+
/**
System variables are automatically command-line options (few
exceptions are documented in sys_var.h), so don't need
@@ -7786,7 +7839,51 @@ struct my_option my_long_options[]=
GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"table_cache", 0, "Deprecated; use --table-open-cache instead.",
&tc_size, &tc_size, 0, GET_ULONG,
- REQUIRED_ARG, TABLE_OPEN_CACHE_DEFAULT, 1, 512*1024L, 0, 1, 0}
+ REQUIRED_ARG, TABLE_OPEN_CACHE_DEFAULT, 1, 512*1024L, 0, 1, 0},
+
+ /* The following options exist in 5.6 but not in 10.0 */
+ MYSQL_TO_BE_IMPLEMENTED_OPTION("default-tmp-storage-engine"),
+ MYSQL_COMPATIBILITY_OPTION("log-raw"),
+ MYSQL_COMPATIBILITY_OPTION("log-bin-use-v1-row-events"),
+ MYSQL_TO_BE_IMPLEMENTED_OPTION("default-authentication-plugin"),
+ MYSQL_COMPATIBILITY_OPTION("binlog-max-flush-queue-time"),
+ MYSQL_TO_BE_IMPLEMENTED_OPTION("binlog-row-image"),
+ MYSQL_TO_BE_IMPLEMENTED_OPTION("explicit-defaults-for-timestamp"),
+ MYSQL_COMPATIBILITY_OPTION("master-info-repository"),
+ MYSQL_COMPATIBILITY_OPTION("relay-log-info-repository"),
+ MYSQL_SUGGEST_ANALOG_OPTION("binlog-rows-query-log-events", "--binlog-annotate-row-events"),
+ MYSQL_COMPATIBILITY_OPTION("binlog-order-commits"),
+ MYSQL_TO_BE_IMPLEMENTED_OPTION("log-throttle-queries-not-using-indexes"),
+ MYSQL_TO_BE_IMPLEMENTED_OPTION("end-markers-in-json"),
+ MYSQL_TO_BE_IMPLEMENTED_OPTION("optimizer-trace"), // OPTIMIZER_TRACE
+ MYSQL_TO_BE_IMPLEMENTED_OPTION("optimizer-trace-features"), // OPTIMIZER_TRACE
+ MYSQL_TO_BE_IMPLEMENTED_OPTION("optimizer-trace-offset"), // OPTIMIZER_TRACE
+ MYSQL_TO_BE_IMPLEMENTED_OPTION("optimizer-trace-limit"), // OPTIMIZER_TRACE
+ MYSQL_TO_BE_IMPLEMENTED_OPTION("optimizer-trace-max-mem-size"), // OPTIMIZER_TRACE
+ MYSQL_TO_BE_IMPLEMENTED_OPTION("eq-range-index-dive-limit"),
+ MYSQL_COMPATIBILITY_OPTION("server-id-bits"),
+ MYSQL_TO_BE_IMPLEMENTED_OPTION("slave-rows-search-algorithms"), // HAVE_REPLICATION
+ MYSQL_COMPATIBILITY_OPTION("table-open-cache-instances"),
+ MYSQL_TO_BE_IMPLEMENTED_OPTION("slave-allow-batching"), // HAVE_REPLICATION
+ MYSQL_COMPATIBILITY_OPTION("slave-checkpoint-period"), // HAVE_REPLICATION
+ MYSQL_COMPATIBILITY_OPTION("slave-checkpoint-group"), // HAVE_REPLICATION
+ MYSQL_SUGGEST_ANALOG_OPTION("slave-parallel-workers", "--slave-parallel-threads"), // HAVE_REPLICATION
+ MYSQL_SUGGEST_ANALOG_OPTION("slave-pending-jobs-size-max", "--slave-parallel-max-queued"), // HAVE_REPLICATION
+ MYSQL_TO_BE_IMPLEMENTED_OPTION("disconnect-on-expired-password"),
+ MYSQL_TO_BE_IMPLEMENTED_OPTION("sha256-password-private-key-path"), // HAVE_OPENSSL && !HAVE_YASSL
+ MYSQL_TO_BE_IMPLEMENTED_OPTION("sha256-password-public-key-path"), // HAVE_OPENSSL && !HAVE_YASSL
+
+ /* The following options exist in 5.5 and 5.6 but not in 10.0 */
+ MYSQL_SUGGEST_ANALOG_OPTION("abort-slave-event-count", "--debug-abort-slave-event-count"),
+ MYSQL_SUGGEST_ANALOG_OPTION("disconnect-slave-event-count", "--debug-disconnect-slave-event-count"),
+ MYSQL_SUGGEST_ANALOG_OPTION("exit-info", "--debug-exit-info"),
+ MYSQL_SUGGEST_ANALOG_OPTION("max-binlog-dump-events", "--debug-max-binlog-dump-events"),
+ MYSQL_SUGGEST_ANALOG_OPTION("sporadic-binlog-dump-fail", "--debug-sporadic-binlog-dump-fail"),
+ MYSQL_COMPATIBILITY_OPTION("new"),
+
+ /* The following options were added after 5.6.10 */
+ MYSQL_TO_BE_IMPLEMENTED_OPTION("rpl-stop-slave-timeout"),
+ MYSQL_TO_BE_IMPLEMENTED_OPTION("validate-user-plugins") // NO_EMBEDDED_ACCESS_CHECKS
};
static int show_queries(THD *thd, SHOW_VAR *var, char *buff)
@@ -8966,6 +9063,14 @@ mysqld_get_one_option(int optid,
"for compatiblity with old my.cnf files.",
opt->name);
break;
+ case OPT_MYSQL_COMPATIBILITY:
+ sql_print_warning("'%s' is MySQL 5.6 compatible option. Not used or needed "
+ "in MariaDB.", opt->name);
+ break;
+ case OPT_MYSQL_TO_BE_IMPLEMENTED:
+ sql_print_warning("'%s' is MySQL 5.6 compatible option. To be implemented "
+ "in later versions.", opt->name);
+ break;
case 'a':
global_system_variables.sql_mode= MODE_ANSI;
global_system_variables.tx_isolation= ISO_SERIALIZABLE;
diff --git a/sql/mysqld.h b/sql/mysqld.h
index 0fa51ff08d9..3cd810f7040 100644
--- a/sql/mysqld.h
+++ b/sql/mysqld.h
@@ -59,7 +59,7 @@ void kill_mysql(void);
void close_connection(THD *thd, uint sql_errno= 0, bool lock=1);
#else
void close_connection(THD *thd, uint sql_errno= 0);
-#endif
+#endif /* WITH_WSREP */
void handle_connection_in_main_thread(THD *thd);
void create_thread_to_handle_connection(THD *thd);
void delete_running_thd(THD *thd);
@@ -144,7 +144,8 @@ extern char *opt_backup_history_logname, *opt_backup_progress_logname,
extern const char *log_output_str;
extern const char *log_backup_output_str;
extern char *mysql_home_ptr, *pidfile_name_ptr;
-extern char glob_hostname[FN_REFLEN], mysql_home[FN_REFLEN];
+extern MYSQL_PLUGIN_IMPORT char glob_hostname[FN_REFLEN];
+extern char mysql_home[FN_REFLEN];
extern char pidfile_name[FN_REFLEN], system_time_zone[30], *opt_init_file;
extern char default_logfile_name[FN_REFLEN];
extern char log_error_file[FN_REFLEN], *opt_tc_log_file;
@@ -221,7 +222,6 @@ extern LEX_STRING opt_init_connect, opt_init_slave;
extern int bootstrap_error;
extern I_List<THD> threads;
extern char err_shared_dir[];
-extern TYPELIB thread_handling_typelib;
extern ulong connection_errors_select;
extern ulong connection_errors_accept;
extern ulong connection_errors_tcpwrap;
@@ -241,10 +241,11 @@ extern pthread_key(MEM_ROOT**,THR_MALLOC);
extern PSI_mutex_key key_PAGE_lock, key_LOCK_sync, key_LOCK_active,
key_LOCK_pool, key_LOCK_pending_checkpoint;
#endif /* HAVE_MMAP */
+
#ifdef WITH_WSREP
extern PSI_mutex_key key_LOCK_wsrep_thd;
extern PSI_cond_key key_COND_wsrep_thd;
-#endif /* HAVE_WSREP */
+#endif /* WITH_WSREP */
#ifdef HAVE_OPENSSL
extern PSI_mutex_key key_LOCK_des_key_file;
@@ -276,8 +277,6 @@ extern PSI_mutex_key key_TABLE_SHARE_LOCK_share, key_LOCK_stats,
key_LOCK_global_user_client_stats, key_LOCK_global_table_stats,
key_LOCK_global_index_stats, key_LOCK_wakeup_ready, key_LOCK_wait_commit;
-extern PSI_mutex_key key_LOCK_rpl_gtid_state;
-
extern PSI_rwlock_key key_rwlock_LOCK_grant, key_rwlock_LOCK_logger,
key_rwlock_LOCK_sys_init_connect, key_rwlock_LOCK_sys_init_slave,
key_rwlock_LOCK_system_variables_hash, key_rwlock_query_cache_query_lock;
@@ -504,7 +503,6 @@ extern mysql_mutex_t
LOCK_slave_list, LOCK_active_mi, LOCK_manager,
LOCK_global_system_variables, LOCK_user_conn,
LOCK_prepared_stmt_count, LOCK_error_messages, LOCK_connection_count;
-extern mysql_mutex_t LOCK_rpl_gtid_state;
extern MYSQL_PLUGIN_IMPORT mysql_mutex_t LOCK_thread_count;
#ifdef HAVE_OPENSSL
extern mysql_mutex_t LOCK_des_key_file;
@@ -583,6 +581,8 @@ enum options_mysqld
OPT_SSL_KEY,
OPT_UPDATE_LOG,
OPT_WANT_CORE,
+ OPT_MYSQL_COMPATIBILITY,
+ OPT_MYSQL_TO_BE_IMPLEMENTED,
#ifdef WITH_WSREP
OPT_WSREP_PROVIDER,
OPT_WSREP_PROVIDER_OPTIONS,
@@ -590,8 +590,9 @@ enum options_mysqld
OPT_WSREP_START_POSITION,
OPT_WSREP_SST_AUTH,
OPT_WSREP_RECOVER,
- OPT_which_is_always_the_last
#endif /* WITH_WSREP */
+
+ OPT_which_is_always_the_last
};
#endif
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index 384c99cff54..6982db898fb 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -3410,6 +3410,9 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item *cond)
if (table_records == 0)
DBUG_RETURN(FALSE);
+
+ if (table->pos_in_table_list->schema_table)
+ DBUG_RETURN(FALSE);
if (thd->variables.optimizer_use_condition_selectivity > 2 &&
!bitmap_is_clear_all(used_fields))
@@ -5360,8 +5363,10 @@ TABLE_READ_PLAN *merge_same_index_scans(PARAM *param, SEL_IMERGE *imerge,
bzero((*changed_tree)->keys,
sizeof((*changed_tree)->keys[0])*param->keys);
(*changed_tree)->keys_map.clear_all();
- key->incr_refs();
- (*tree)->keys[key_idx]->incr_refs();
+ if (key)
+ key->incr_refs();
+ if ((*tree)->keys[key_idx])
+ (*tree)->keys[key_idx]->incr_refs();
if (((*changed_tree)->keys[key_idx]=
key_or(param, key, (*tree)->keys[key_idx])))
(*changed_tree)->keys_map.set_bit(key_idx);
diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc
index c156c1642e7..a9bff4b37bf 100644
--- a/sql/opt_subselect.cc
+++ b/sql/opt_subselect.cc
@@ -1311,7 +1311,6 @@ static bool replace_where_subcondition(JOIN *join, Item **expr,
}
else if (item->type() == Item::COND_ITEM)
{
- DBUG_ASSERT(!do_fix_fields || !(*expr)->fixed);
replace_where_subcondition(join, li.ref(),
old_cond, new_cond,
do_fix_fields);
@@ -1929,11 +1928,25 @@ int pull_out_semijoin_tables(JOIN *join)
}
}
-
table_map pulled_tables= 0;
+ table_map dep_tables= 0;
if (have_join_nest_children)
goto skip;
+ /*
+ Calculate set of tables within this semi-join nest that have
+ other dependent tables
+ */
+ child_li.rewind();
+ while ((tbl= child_li++))
+ {
+ TABLE *const table= tbl->table;
+ if (table &&
+ (table->reginfo.join_tab->dependent &
+ sj_nest->nested_join->used_tables))
+ dep_tables|= table->reginfo.join_tab->dependent;
+ }
+
/* Action #1: Mark the constant tables to be pulled out */
child_li.rewind();
while ((tbl= child_li++))
@@ -1984,7 +1997,8 @@ int pull_out_semijoin_tables(JOIN *join)
child_li.rewind();
while ((tbl= child_li++))
{
- if (tbl->table && !(pulled_tables & tbl->table->map))
+ if (tbl->table && !(pulled_tables & tbl->table->map) &&
+ !(dep_tables & tbl->table->map))
{
if (find_eq_ref_candidate(tbl->table,
sj_nest->nested_join->used_tables &
diff --git a/sql/opt_table_elimination.cc b/sql/opt_table_elimination.cc
index e50a9b0b42d..46bed0e60e7 100644
--- a/sql/opt_table_elimination.cc
+++ b/sql/opt_table_elimination.cc
@@ -609,6 +609,21 @@ void eliminate_tables(JOIN *join)
/* Find the tables that are referred to from WHERE/HAVING */
used_tables= (join->conds? join->conds->used_tables() : 0) |
(join->having? join->having->used_tables() : 0);
+
+ /*
+ For "INSERT ... SELECT ... ON DUPLICATE KEY UPDATE column = val"
+ we should also take into account tables mentioned in "val".
+ */
+ if (join->thd->lex->sql_command == SQLCOM_INSERT_SELECT &&
+ join->select_lex == &thd->lex->select_lex)
+ {
+ List_iterator<Item> val_it(thd->lex->value_list);
+ while ((item= val_it++))
+ {
+ DBUG_ASSERT(item->fixed);
+ used_tables |= item->used_tables();
+ }
+ }
/* Add tables referred to from the select list */
List_iterator<Item> it(join->fields_list);
diff --git a/sql/partition_element.h b/sql/partition_element.h
index 4f03d91035a..d0a67c6af3e 100644
--- a/sql/partition_element.h
+++ b/sql/partition_element.h
@@ -115,12 +115,10 @@ public:
partition_name(NULL), tablespace_name(NULL),
log_entry(NULL), part_comment(NULL),
data_file_name(NULL), index_file_name(NULL),
- engine_type(NULL), part_state(PART_NORMAL),
+ engine_type(NULL), connect_string(null_lex_str), part_state(PART_NORMAL),
nodegroup_id(UNDEF_NODEGROUP), has_null_value(FALSE),
signed_flag(FALSE), max_value(FALSE)
{
- connect_string.str= 0;
- connect_string.length= 0;
}
partition_element(partition_element *part_elem)
: part_max_rows(part_elem->part_max_rows),
@@ -131,13 +129,11 @@ public:
data_file_name(part_elem->data_file_name),
index_file_name(part_elem->index_file_name),
engine_type(part_elem->engine_type),
- connect_string(part_elem->connect_string),
+ connect_string(null_lex_str),
part_state(part_elem->part_state),
nodegroup_id(part_elem->nodegroup_id),
has_null_value(FALSE)
{
- connect_string.str= 0;
- connect_string.length= 0;
}
~partition_element() {}
};
diff --git a/sql/partition_info.cc b/sql/partition_info.cc
index cbd7dedec32..44407bd737f 100644
--- a/sql/partition_info.cc
+++ b/sql/partition_info.cc
@@ -2705,9 +2705,11 @@ end:
DBUG_RETURN(result);
}
-/*
- The parser generates generic data structures, we need to set them up
- as the rest of the code expects to find them. This is in reality part
+/**
+ Fix partition data from parser.
+
+ @details The parser generates generic data structures, we need to set them
+ up as the rest of the code expects to find them. This is in reality part
of the syntax check of the parser code.
It is necessary to call this function in the case of a CREATE TABLE
@@ -2739,16 +2741,14 @@ end:
and number of elements are in synch with each other. So only partitioning
using functions need to be set-up to their data structures.
- SYNOPSIS
- fix_parser_data()
- thd Thread object
+ @param thd Thread object
- RETURN VALUES
- TRUE Failure
- FALSE Success
+ @return Operation status
+ @retval TRUE Failure
+ @retval FALSE Success
*/
-int partition_info::fix_parser_data(THD *thd)
+bool partition_info::fix_parser_data(THD *thd)
{
List_iterator<partition_element> it(partitions);
partition_element *part_elem;
@@ -2759,18 +2759,44 @@ int partition_info::fix_parser_data(THD *thd)
if (!(part_type == RANGE_PARTITION ||
part_type == LIST_PARTITION))
{
- /* Nothing to do for HASH/KEY partitioning */
+ if (part_type == HASH_PARTITION && list_of_part_fields)
+ {
+ /* KEY partitioning, check ALGORITHM = N. Should not pass the parser! */
+ if (key_algorithm > KEY_ALGORITHM_55)
+ {
+ my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0));
+ DBUG_RETURN(true);
+ }
+ /* If not set, use DEFAULT = 2 for CREATE and ALTER! */
+ if ((thd_sql_command(thd) == SQLCOM_CREATE_TABLE ||
+ thd_sql_command(thd) == SQLCOM_ALTER_TABLE) &&
+ key_algorithm == KEY_ALGORITHM_NONE)
+ key_algorithm= KEY_ALGORITHM_55;
+ }
DBUG_RETURN(FALSE);
}
+ if (is_sub_partitioned() && list_of_subpart_fields)
+ {
+ /* KEY subpartitioning, check ALGORITHM = N. Should not pass the parser! */
+ if (key_algorithm > KEY_ALGORITHM_55)
+ {
+ my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0));
+ DBUG_RETURN(true);
+ }
+ /* If not set, use DEFAULT = 2 for CREATE and ALTER! */
+ if ((thd_sql_command(thd) == SQLCOM_CREATE_TABLE ||
+ thd_sql_command(thd) == SQLCOM_ALTER_TABLE) &&
+ key_algorithm == KEY_ALGORITHM_NONE)
+ key_algorithm= KEY_ALGORITHM_55;
+ }
do
{
part_elem= it++;
List_iterator<part_elem_value> list_val_it(part_elem->list_val_list);
- j= 0;
num_elements= part_elem->list_val_list.elements;
DBUG_ASSERT(part_type == RANGE_PARTITION ?
num_elements == 1U : TRUE);
- do
+ for (j= 0; j < num_elements; j++)
{
part_elem_value *val= list_val_it++;
if (column_list)
@@ -2805,12 +2831,268 @@ int partition_info::fix_parser_data(THD *thd)
list_val_it.remove();
}
}
- } while (++j < num_elements);
+ }
} while (++i < num_parts);
DBUG_RETURN(FALSE);
}
+/**
+ helper function to compare strings that can also be
+ a NULL pointer.
+
+ @param a char pointer (can be NULL).
+ @param b char pointer (can be NULL).
+
+ @return false if equal
+ @retval true strings differs
+ @retval false strings is equal
+*/
+
+static bool strcmp_null(const char *a, const char *b)
+{
+ if (!a && !b)
+ return false;
+ if (a && b && !strcmp(a, b))
+ return false;
+ return true;
+}
+
+
+/**
+ Check if the new part_info has the same partitioning.
+
+ @param new_part_info New partition definition to compare with.
+
+ @return True if not considered to have changed the partitioning.
+ @retval true Allowed change (only .frm change, compatible distribution).
+ @retval false Different partitioning, will need redistribution of rows.
+
+ @note Currently only used to allow changing from non-set key_algorithm
+ to a specified key_algorithm, to avoid rebuild when upgrading from 5.1 of
+ such partitioned tables using numeric colums in the partitioning expression.
+ For more info see bug#14521864.
+ Does not check if columns etc has changed, i.e. only for
+ alter_info->flags == ALTER_PARTITION.
+*/
+
+bool partition_info::has_same_partitioning(partition_info *new_part_info)
+{
+ DBUG_ENTER("partition_info::has_same_partitioning");
+
+ DBUG_ASSERT(part_field_array && part_field_array[0]);
+
+ /*
+ Only consider pre 5.5.3 .frm's to have same partitioning as
+ a new one with KEY ALGORITHM = 1 ().
+ */
+
+ if (part_field_array[0]->table->s->mysql_version >= 50503)
+ DBUG_RETURN(false);
+
+ if (!new_part_info ||
+ part_type != new_part_info->part_type ||
+ num_parts != new_part_info->num_parts ||
+ use_default_partitions != new_part_info->use_default_partitions ||
+ new_part_info->is_sub_partitioned() != is_sub_partitioned())
+ DBUG_RETURN(false);
+
+ if (part_type != HASH_PARTITION)
+ {
+ /*
+ RANGE or LIST partitioning, check if KEY subpartitioned.
+ Also COLUMNS partitioning was added in 5.5, so treat that as different.
+ */
+ if (!is_sub_partitioned() ||
+ !new_part_info->is_sub_partitioned() ||
+ column_list ||
+ new_part_info->column_list ||
+ !list_of_subpart_fields ||
+ !new_part_info->list_of_subpart_fields ||
+ new_part_info->num_subparts != num_subparts ||
+ new_part_info->subpart_field_list.elements !=
+ subpart_field_list.elements ||
+ new_part_info->use_default_subpartitions !=
+ use_default_subpartitions)
+ DBUG_RETURN(false);
+ }
+ else
+ {
+ /* Check if KEY partitioned. */
+ if (!new_part_info->list_of_part_fields ||
+ !list_of_part_fields ||
+ new_part_info->part_field_list.elements != part_field_list.elements)
+ DBUG_RETURN(false);
+ }
+
+ /* Check that it will use the same fields in KEY (fields) list. */
+ List_iterator<char> old_field_name_it(part_field_list);
+ List_iterator<char> new_field_name_it(new_part_info->part_field_list);
+ char *old_name, *new_name;
+ while ((old_name= old_field_name_it++))
+ {
+ new_name= new_field_name_it++;
+ if (!new_name || my_strcasecmp(system_charset_info,
+ new_name,
+ old_name))
+ DBUG_RETURN(false);
+ }
+
+ if (is_sub_partitioned())
+ {
+ /* Check that it will use the same fields in KEY subpart fields list. */
+ List_iterator<char> old_field_name_it(subpart_field_list);
+ List_iterator<char> new_field_name_it(new_part_info->subpart_field_list);
+ char *old_name, *new_name;
+ while ((old_name= old_field_name_it++))
+ {
+ new_name= new_field_name_it++;
+ if (!new_name || my_strcasecmp(system_charset_info,
+ new_name,
+ old_name))
+ DBUG_RETURN(false);
+ }
+ }
+
+ if (!use_default_partitions)
+ {
+ /*
+ Loop over partitions/subpartition to verify that they are
+ the same, including state and name.
+ */
+ List_iterator<partition_element> part_it(partitions);
+ List_iterator<partition_element> new_part_it(new_part_info->partitions);
+ uint i= 0;
+ do
+ {
+ partition_element *part_elem= part_it++;
+ partition_element *new_part_elem= new_part_it++;
+ /*
+ The following must match:
+ partition_name, tablespace_name, data_file_name, index_file_name,
+ engine_type, part_max_rows, part_min_rows, nodegroup_id.
+ (max_value, signed_flag, has_null_value only on partition level,
+ RANGE/LIST)
+ The following can differ:
+ - part_comment
+ part_state must be PART_NORMAL!
+ */
+ if (!part_elem || !new_part_elem ||
+ strcmp(part_elem->partition_name,
+ new_part_elem->partition_name) ||
+ part_elem->part_state != PART_NORMAL ||
+ new_part_elem->part_state != PART_NORMAL ||
+ part_elem->max_value != new_part_elem->max_value ||
+ part_elem->signed_flag != new_part_elem->signed_flag ||
+ part_elem->has_null_value != new_part_elem->has_null_value)
+ DBUG_RETURN(false);
+
+ /* new_part_elem may not have engine_type set! */
+ if (new_part_elem->engine_type &&
+ part_elem->engine_type != new_part_elem->engine_type)
+ DBUG_RETURN(false);
+
+ if (is_sub_partitioned())
+ {
+ /*
+ Check that both old and new partition has the same definition
+ (VALUES IN/VALUES LESS THAN) (No COLUMNS partitioning, see above)
+ */
+ if (part_type == LIST_PARTITION)
+ {
+ List_iterator<part_elem_value> list_vals(part_elem->list_val_list);
+ List_iterator<part_elem_value>
+ new_list_vals(new_part_elem->list_val_list);
+ part_elem_value *val;
+ part_elem_value *new_val;
+ while ((val= list_vals++))
+ {
+ new_val= new_list_vals++;
+ if (!new_val)
+ DBUG_RETURN(false);
+ if ((!val->null_value && !new_val->null_value) &&
+ val->value != new_val->value)
+ DBUG_RETURN(false);
+ }
+ if (new_list_vals++)
+ DBUG_RETURN(false);
+ }
+ else
+ {
+ DBUG_ASSERT(part_type == RANGE_PARTITION);
+ if (new_part_elem->range_value != part_elem->range_value)
+ DBUG_RETURN(false);
+ }
+
+ if (!use_default_subpartitions)
+ {
+ List_iterator<partition_element>
+ sub_part_it(part_elem->subpartitions);
+ List_iterator<partition_element>
+ new_sub_part_it(new_part_elem->subpartitions);
+ uint j= 0;
+ do
+ {
+ partition_element *sub_part_elem= sub_part_it++;
+ partition_element *new_sub_part_elem= new_sub_part_it++;
+ /* new_part_elem may not have engine_type set! */
+ if (new_sub_part_elem->engine_type &&
+ sub_part_elem->engine_type != new_sub_part_elem->engine_type)
+ DBUG_RETURN(false);
+
+ if (strcmp(sub_part_elem->partition_name,
+ new_sub_part_elem->partition_name) ||
+ sub_part_elem->part_state != PART_NORMAL ||
+ new_sub_part_elem->part_state != PART_NORMAL ||
+ sub_part_elem->part_min_rows !=
+ new_sub_part_elem->part_min_rows ||
+ sub_part_elem->part_max_rows !=
+ new_sub_part_elem->part_max_rows ||
+ sub_part_elem->nodegroup_id !=
+ new_sub_part_elem->nodegroup_id)
+ DBUG_RETURN(false);
+
+ if (strcmp_null(sub_part_elem->data_file_name,
+ new_sub_part_elem->data_file_name) ||
+ strcmp_null(sub_part_elem->index_file_name,
+ new_sub_part_elem->index_file_name) ||
+ strcmp_null(sub_part_elem->tablespace_name,
+ new_sub_part_elem->tablespace_name))
+ DBUG_RETURN(false);
+
+ } while (++j < num_subparts);
+ }
+ }
+ else
+ {
+ if (part_elem->part_min_rows != new_part_elem->part_min_rows ||
+ part_elem->part_max_rows != new_part_elem->part_max_rows ||
+ part_elem->nodegroup_id != new_part_elem->nodegroup_id)
+ DBUG_RETURN(false);
+
+ if (strcmp_null(part_elem->data_file_name,
+ new_part_elem->data_file_name) ||
+ strcmp_null(part_elem->index_file_name,
+ new_part_elem->index_file_name) ||
+ strcmp_null(part_elem->tablespace_name,
+ new_part_elem->tablespace_name))
+ DBUG_RETURN(false);
+ }
+ } while (++i < num_parts);
+ }
+
+ /*
+ Only if key_algorithm was not specified before and it is now set,
+ consider this as nothing was changed, and allow change without rebuild!
+ */
+ if (key_algorithm != partition_info::KEY_ALGORITHM_NONE ||
+ new_part_info->key_algorithm == partition_info::KEY_ALGORITHM_NONE)
+ DBUG_RETURN(false);
+
+ DBUG_RETURN(true);
+}
+
+
void partition_info::print_debug(const char *str, uint *value)
{
DBUG_ENTER("print_debug");
diff --git a/sql/partition_info.h b/sql/partition_info.h
index 59b40d7ce88..777cd6065eb 100644
--- a/sql/partition_info.h
+++ b/sql/partition_info.h
@@ -168,8 +168,8 @@ public:
char *part_func_string;
char *subpart_func_string;
- partition_element *curr_part_elem;
- partition_element *current_partition;
+ partition_element *curr_part_elem; // part or sub part
+ partition_element *current_partition; // partition
part_elem_value *curr_list_val;
uint curr_list_object;
uint num_columns;
@@ -193,9 +193,7 @@ public:
uint num_parts;
uint num_subparts;
- uint count_curr_subparts;
-
- uint part_error_code;
+ uint count_curr_subparts; // used during parsing
uint num_list_values;
@@ -210,21 +208,35 @@ public:
but mainly of use to handlers supporting partitioning.
*/
uint16 linear_hash_mask;
-
+ /*
+ PARTITION BY KEY ALGORITHM=N
+ Which algorithm to use for hashing the fields.
+ N = 1 - Use 5.1 hashing (numeric fields are hashed as binary)
+ N = 2 - Use 5.5 hashing (numeric fields are hashed like latin1 bytes)
+ */
+ enum enum_key_algorithm
+ {
+ KEY_ALGORITHM_NONE= 0,
+ KEY_ALGORITHM_51= 1,
+ KEY_ALGORITHM_55= 2
+ };
+ enum_key_algorithm key_algorithm;
+
+ /* Only the number of partitions defined (uses default names and options). */
bool use_default_partitions;
bool use_default_num_partitions;
+ /* Only the number of subpartitions defined (uses default names etc.). */
bool use_default_subpartitions;
bool use_default_num_subpartitions;
bool default_partitions_setup;
bool defined_max_value;
- bool list_of_part_fields;
- bool list_of_subpart_fields;
- bool linear_hash_ind;
+ bool list_of_part_fields; // KEY or COLUMNS PARTITIONING
+ bool list_of_subpart_fields; // KEY SUBPARTITIONING
+ bool linear_hash_ind; // LINEAR HASH/KEY
bool fixed;
bool is_auto_partitioned;
- bool from_openfrm;
bool has_null_value;
- bool column_list;
+ bool column_list; // COLUMNS PARTITIONING, 5.5+
/**
True if pruning has been completed and can not be pruned any further,
even if there are subqueries or stored programs in the condition.
@@ -251,21 +263,22 @@ public:
part_info_string(NULL),
part_func_string(NULL), subpart_func_string(NULL),
curr_part_elem(NULL), current_partition(NULL),
- curr_list_object(0), num_columns(0),
+ curr_list_object(0), num_columns(0), table(NULL),
default_engine_type(NULL),
part_type(NOT_A_PARTITION), subpart_type(NOT_A_PARTITION),
part_info_len(0),
part_func_len(0), subpart_func_len(0),
num_parts(0), num_subparts(0),
- count_curr_subparts(0), part_error_code(0),
+ count_curr_subparts(0),
num_list_values(0), num_part_fields(0), num_subpart_fields(0),
num_full_part_fields(0), has_null_part_id(0), linear_hash_mask(0),
+ key_algorithm(KEY_ALGORITHM_NONE),
use_default_partitions(TRUE), use_default_num_partitions(TRUE),
use_default_subpartitions(TRUE), use_default_num_subpartitions(TRUE),
default_partitions_setup(FALSE), defined_max_value(FALSE),
list_of_part_fields(FALSE), list_of_subpart_fields(FALSE),
linear_hash_ind(FALSE), fixed(FALSE),
- is_auto_partitioned(FALSE), from_openfrm(FALSE),
+ is_auto_partitioned(FALSE),
has_null_value(FALSE), column_list(FALSE), is_pruning_completed(false)
{
all_fields_in_PF.clear_all();
@@ -314,7 +327,7 @@ public:
bool fix_column_value_functions(THD *thd,
part_elem_value *val,
uint part_id);
- int fix_parser_data(THD *thd);
+ bool fix_parser_data(THD *thd);
int add_max_value();
void init_col_val(part_column_list_val *col_val, Item *item);
int reorganize_into_single_field_col_val();
@@ -355,6 +368,7 @@ public:
enum_can_prune *can_prune_partitions,
bool *prune_needs_default_values,
MY_BITMAP *used_partitions);
+ bool has_same_partitioning(partition_info *new_part_info);
private:
static int list_part_cmp(const void* a, const void* b);
bool set_up_default_partitions(handler *file, HA_CREATE_INFO *info,
diff --git a/sql/password.c b/sql/password.c
index 954daf2d8d1..22e0060abd2 100644
--- a/sql/password.c
+++ b/sql/password.c
@@ -67,7 +67,6 @@
#include <mysql.h>
#include <my_rnd.h>
#include <sha1.h>
-#include <crypt_genhash_impl.h>
/************ MySQL 3.23-4.0 authentication routines: untouched ***********/
@@ -280,14 +279,13 @@ void make_password_from_salt_323(char *to, const ulong *salt)
**************** MySQL 4.1.1 authentication routines *************
*/
-/*
- Generate string of printable random characters of requested length
- SYNOPSIS
- create_random_string()
- to OUT buffer for generation; must be at least length+1 bytes
- long; result string is always null-terminated
- length IN how many random characters to put in buffer
- rand_st INOUT structure used for number generation
+/**
+ Generate string of printable random characters of requested length.
+
+ @param to[out] Buffer for generation; must be at least length+1 bytes
+ long; result string is always null-terminated
+ length[in] How many random characters to put in buffer
+ rand_st Structure used for number generation
*/
void create_random_string(char *to, uint length,
@@ -374,23 +372,6 @@ my_crypt(char *to, const uchar *s1, const uchar *s2, uint len)
}
-#if defined(HAVE_OPENSSL)
-void my_make_scrambled_password(char *to, const char *password,
- size_t pass_len)
-{
-
- char salt[CRYPT_SALT_LENGTH + 1];
-
- generate_user_salt(salt, CRYPT_SALT_LENGTH + 1);
- my_crypt_genhash(to,
- CRYPT_MAX_PASSWORD_SIZE,
- password,
- pass_len,
- salt,
- 0);
-
-}
-#endif
/**
Compute two stage SHA1 hash of the password :
@@ -422,14 +403,14 @@ void compute_two_stage_sha1_hash(const char *password, size_t pass_len,
The result of this function is used as return value from PASSWORD() and
is stored in the database.
SYNOPSIS
- my_make_scrambled_password_sha1()
+ my_make_scrambled_password()
buf OUT buffer of size 2*SHA1_HASH_SIZE + 2 to store hex string
password IN password string
pass_len IN length of password string
*/
-void my_make_scrambled_password_sha1(char *to, const char *password,
- size_t pass_len)
+void my_make_scrambled_password(char *to, const char *password,
+ size_t pass_len)
{
uint8 hash_stage2[SHA1_HASH_SIZE];
@@ -455,7 +436,7 @@ void my_make_scrambled_password_sha1(char *to, const char *password,
void make_scrambled_password(char *to, const char *password)
{
- my_make_scrambled_password_sha1(to, password, strlen(password));
+ my_make_scrambled_password(to, password, strlen(password));
}
@@ -500,7 +481,7 @@ scramble(char *to, const char *message, const char *password)
null-terminated, reply and hash_stage2 must be at least SHA1_HASH_SIZE
long (if not, something fishy is going on).
SYNOPSIS
- check_scramble_sha1()
+ check_scramble()
scramble clients' reply, presumably produced by scramble()
message original random string, previously sent to client
(presumably second argument of scramble()), must be
@@ -514,8 +495,8 @@ scramble(char *to, const char *message, const char *password)
*/
my_bool
-check_scramble_sha1(const uchar *scramble_arg, const char *message,
- const uint8 *hash_stage2)
+check_scramble(const uchar *scramble_arg, const char *message,
+ const uint8 *hash_stage2)
{
uint8 buf[SHA1_HASH_SIZE];
uint8 hash_stage2_reassured[SHA1_HASH_SIZE];
@@ -532,13 +513,6 @@ check_scramble_sha1(const uchar *scramble_arg, const char *message,
return test(memcmp(hash_stage2, hash_stage2_reassured, SHA1_HASH_SIZE));
}
-my_bool
-check_scramble(const uchar *scramble_arg, const char *message,
- const uint8 *hash_stage2)
-{
- return check_scramble_sha1(scramble_arg, message, hash_stage2);
-}
-
/*
Convert scrambled password from asciiz hex string to binary form.
@@ -567,3 +541,4 @@ void make_password_from_salt(char *to, const uint8 *hash_stage2)
*to++= PVERSION41_CHAR;
octet2hex(to, (const char*) hash_stage2, SHA1_HASH_SIZE);
}
+
diff --git a/sql/rpl_constants.h b/sql/rpl_constants.h
index 3c605d24563..f83588ce321 100644
--- a/sql/rpl_constants.h
+++ b/sql/rpl_constants.h
@@ -31,4 +31,44 @@ enum Incident {
INCIDENT_COUNT
};
+
+/**
+ Enumeration of the reserved formats of Binlog extra row information
+*/
+enum ExtraRowInfoFormat {
+ /** Ndb format */
+ ERIF_NDB = 0,
+
+ /** Reserved formats 0 -> 63 inclusive */
+ ERIF_LASTRESERVED = 63,
+
+ /**
+ Available / uncontrolled formats
+ 64 -> 254 inclusive
+ */
+ ERIF_OPEN1 = 64,
+ ERIF_OPEN2 = 65,
+
+ ERIF_LASTOPEN = 254,
+
+ /**
+ Multi-payload format 255
+
+ Length is total length, payload is sequence of
+ sub-payloads with their own headers containing
+ length + format.
+ */
+ ERIF_MULTI = 255
+};
+
+/*
+ 1 byte length, 1 byte format
+ Length is total length in bytes, including 2 byte header
+ Length values 0 and 1 are currently invalid and reserved.
+*/
+#define EXTRA_ROW_INFO_LEN_OFFSET 0
+#define EXTRA_ROW_INFO_FORMAT_OFFSET 1
+#define EXTRA_ROW_INFO_HDR_BYTES 2
+#define EXTRA_ROW_INFO_MAX_PAYLOAD (255 - EXTRA_ROW_INFO_HDR_BYTES)
+
#endif /* RPL_CONSTANTS_H */
diff --git a/sql/rpl_gtid.cc b/sql/rpl_gtid.cc
index 8429f1c7f58..3f79a0cb528 100644
--- a/sql/rpl_gtid.cc
+++ b/sql/rpl_gtid.cc
@@ -203,18 +203,15 @@ rpl_slave_state::truncate_state_table(THD *thd)
{
TABLE_LIST tlist;
int err= 0;
- TABLE *table;
+ tmp_disable_binlog(thd);
tlist.init_one_table(STRING_WITH_LEN("mysql"),
rpl_gtid_slave_state_table_name.str,
rpl_gtid_slave_state_table_name.length,
NULL, TL_WRITE);
if (!(err= open_and_lock_tables(thd, &tlist, FALSE, 0)))
{
- table= tlist.table;
- table->no_replicate= 1;
- table->s->is_gtid_slave_pos= TRUE; // TEMPORARY CODE
- err= table->file->ha_truncate();
+ err= tlist.table->file->ha_truncate();
if (err)
{
@@ -231,6 +228,7 @@ rpl_slave_state::truncate_state_table(THD *thd)
thd->mdl_context.release_transactional_locks();
}
+ reenable_binlog(thd);
return err;
}
@@ -350,14 +348,14 @@ rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id,
if ((err= gtid_check_rpl_slave_state_table(table)))
goto end;
- table->no_replicate= 1;
- table->s->is_gtid_slave_pos= TRUE; // TEMPORARY CODE
if (!in_transaction)
{
DBUG_PRINT("info", ("resetting OPTION_BEGIN"));
thd->variables.option_bits&=
- ~(ulonglong)(OPTION_NOT_AUTOCOMMIT|OPTION_BEGIN);
+ ~(ulonglong)(OPTION_NOT_AUTOCOMMIT|OPTION_BEGIN|OPTION_BIN_LOG);
}
+ else
+ thd->variables.option_bits&= ~(ulonglong)OPTION_BIN_LOG;
bitmap_set_all(table->write_set);
@@ -840,7 +838,7 @@ rpl_binlog_state::rpl_binlog_state()
void
-rpl_binlog_state::reset()
+rpl_binlog_state::reset_nolock()
{
uint32 i;
@@ -849,12 +847,22 @@ rpl_binlog_state::reset()
my_hash_reset(&hash);
}
+
+void
+rpl_binlog_state::reset()
+{
+ mysql_mutex_lock(&LOCK_binlog_state);
+ reset_nolock();
+ mysql_mutex_unlock(&LOCK_binlog_state);
+}
+
+
void rpl_binlog_state::free()
{
if (initialized)
{
initialized= 0;
- reset();
+ reset_nolock();
my_hash_free(&hash);
mysql_mutex_destroy(&LOCK_binlog_state);
}
@@ -865,14 +873,20 @@ bool
rpl_binlog_state::load(struct rpl_gtid *list, uint32 count)
{
uint32 i;
+ bool res= false;
- reset();
+ mysql_mutex_lock(&LOCK_binlog_state);
+ reset_nolock();
for (i= 0; i < count; ++i)
{
- if (update(&(list[i]), false))
- return true;
+ if (update_nolock(&(list[i]), false))
+ {
+ res= true;
+ break;
+ }
}
- return false;
+ mysql_mutex_unlock(&LOCK_binlog_state);
+ return res;
}
@@ -891,7 +905,7 @@ rpl_binlog_state::~rpl_binlog_state()
Returns 0 for ok, 1 for error.
*/
int
-rpl_binlog_state::update(const struct rpl_gtid *gtid, bool strict)
+rpl_binlog_state::update_nolock(const struct rpl_gtid *gtid, bool strict)
{
element *elem;
@@ -910,7 +924,7 @@ rpl_binlog_state::update(const struct rpl_gtid *gtid, bool strict)
if (!elem->update_element(gtid))
return 0;
}
- else if (!alloc_element(gtid))
+ else if (!alloc_element_nolock(gtid))
return 0;
my_error(ER_OUT_OF_RESOURCES, MYF(0));
@@ -918,6 +932,17 @@ rpl_binlog_state::update(const struct rpl_gtid *gtid, bool strict)
}
+int
+rpl_binlog_state::update(const struct rpl_gtid *gtid, bool strict)
+{
+ int res;
+ mysql_mutex_lock(&LOCK_binlog_state);
+ res= update_nolock(gtid, strict);
+ mysql_mutex_unlock(&LOCK_binlog_state);
+ return res;
+}
+
+
/*
Fill in a new GTID, allocating next sequence number, and update state
accordingly.
@@ -927,25 +952,30 @@ rpl_binlog_state::update_with_next_gtid(uint32 domain_id, uint32 server_id,
rpl_gtid *gtid)
{
element *elem;
+ int res= 0;
gtid->domain_id= domain_id;
gtid->server_id= server_id;
+ mysql_mutex_lock(&LOCK_binlog_state);
if ((elem= (element *)my_hash_search(&hash, (const uchar *)(&domain_id), 0)))
{
gtid->seq_no= ++elem->seq_no_counter;
if (!elem->update_element(gtid))
- return 0;
+ goto end;
}
else
{
gtid->seq_no= 1;
- if (!alloc_element(gtid))
- return 0;
+ if (!alloc_element_nolock(gtid))
+ goto end;
}
my_error(ER_OUT_OF_RESOURCES, MYF(0));
- return 1;
+ res= 1;
+end:
+ mysql_mutex_unlock(&LOCK_binlog_state);
+ return res;
}
@@ -991,7 +1021,7 @@ rpl_binlog_state::element::update_element(const rpl_gtid *gtid)
int
-rpl_binlog_state::alloc_element(const rpl_gtid *gtid)
+rpl_binlog_state::alloc_element_nolock(const rpl_gtid *gtid)
{
element *elem;
rpl_gtid *lookup_gtid;
@@ -1035,7 +1065,9 @@ rpl_binlog_state::check_strict_sequence(uint32 domain_id, uint32 server_id,
uint64 seq_no)
{
element *elem;
+ bool res= 0;
+ mysql_mutex_lock(&LOCK_binlog_state);
if ((elem= (element *)my_hash_search(&hash,
(const uchar *)(&domain_id), 0)) &&
elem->last_gtid && elem->last_gtid->seq_no >= seq_no)
@@ -1043,9 +1075,10 @@ rpl_binlog_state::check_strict_sequence(uint32 domain_id, uint32 server_id,
my_error(ER_GTID_STRICT_OUT_OF_ORDER, MYF(0), domain_id, server_id, seq_no,
elem->last_gtid->domain_id, elem->last_gtid->server_id,
elem->last_gtid->seq_no);
- return 1;
+ res= 1;
}
- return 0;
+ mysql_mutex_unlock(&LOCK_binlog_state);
+ return res;
}
@@ -1060,17 +1093,23 @@ int
rpl_binlog_state::bump_seq_no_if_needed(uint32 domain_id, uint64 seq_no)
{
element *elem;
+ int res;
+ mysql_mutex_lock(&LOCK_binlog_state);
if ((elem= (element *)my_hash_search(&hash, (const uchar *)(&domain_id), 0)))
{
if (elem->seq_no_counter < seq_no)
elem->seq_no_counter= seq_no;
- return 0;
+ res= 0;
+ goto end;
}
/* We need to allocate a new, empty element to remember the next seq_no. */
if (!(elem= (element *)my_malloc(sizeof(*elem), MYF(MY_WME))))
- return 1;
+ {
+ res= 1;
+ goto end;
+ }
elem->domain_id= domain_id;
my_hash_init(&elem->hash, &my_charset_bin, 32,
@@ -1079,11 +1118,18 @@ rpl_binlog_state::bump_seq_no_if_needed(uint32 domain_id, uint64 seq_no)
elem->last_gtid= NULL;
elem->seq_no_counter= seq_no;
if (0 == my_hash_insert(&hash, (const uchar *)elem))
- return 0;
+ {
+ res= 0;
+ goto end;
+ }
my_hash_free(&elem->hash);
my_free(elem);
- return 1;
+ res= 1;
+
+end:
+ mysql_mutex_unlock(&LOCK_binlog_state);
+ return res;
}
@@ -1099,7 +1145,9 @@ rpl_binlog_state::write_to_iocache(IO_CACHE *dest)
{
ulong i, j;
char buf[21];
+ int res= 0;
+ mysql_mutex_lock(&LOCK_binlog_state);
for (i= 0; i < hash.records; ++i)
{
size_t res;
@@ -1124,11 +1172,16 @@ rpl_binlog_state::write_to_iocache(IO_CACHE *dest)
longlong10_to_str(gtid->seq_no, buf, 10);
res= my_b_printf(dest, "%u-%u-%s\n", gtid->domain_id, gtid->server_id, buf);
if (res == (size_t) -1)
- return 1;
+ {
+ res= 1;
+ goto end;
+ }
}
}
- return 0;
+end:
+ mysql_mutex_unlock(&LOCK_binlog_state);
+ return res;
}
@@ -1139,26 +1192,31 @@ rpl_binlog_state::read_from_iocache(IO_CACHE *src)
char buf[10+1+10+1+20+1+1];
char *p, *end;
rpl_gtid gtid;
+ int res= 0;
- reset();
+ mysql_mutex_lock(&LOCK_binlog_state);
+ reset_nolock();
for (;;)
{
- size_t res= my_b_gets(src, buf, sizeof(buf));
- if (!res)
+ size_t len= my_b_gets(src, buf, sizeof(buf));
+ if (!len)
break;
p= buf;
- end= buf + res;
- if (gtid_parser_helper(&p, end, &gtid))
- return 1;
- if (update(&gtid, false))
- return 1;
+ end= buf + len;
+ if (gtid_parser_helper(&p, end, &gtid) ||
+ update_nolock(&gtid, false))
+ {
+ res= 1;
+ break;
+ }
}
- return 0;
+ mysql_mutex_unlock(&LOCK_binlog_state);
+ return res;
}
rpl_gtid *
-rpl_binlog_state::find(uint32 domain_id, uint32 server_id)
+rpl_binlog_state::find_nolock(uint32 domain_id, uint32 server_id)
{
element *elem;
if (!(elem= (element *)my_hash_search(&hash, (const uchar *)&domain_id, 0)))
@@ -1167,14 +1225,28 @@ rpl_binlog_state::find(uint32 domain_id, uint32 server_id)
}
rpl_gtid *
+rpl_binlog_state::find(uint32 domain_id, uint32 server_id)
+{
+ rpl_gtid *p;
+ mysql_mutex_lock(&LOCK_binlog_state);
+ p= find_nolock(domain_id, server_id);
+ mysql_mutex_unlock(&LOCK_binlog_state);
+ return p;
+}
+
+rpl_gtid *
rpl_binlog_state::find_most_recent(uint32 domain_id)
{
element *elem;
+ rpl_gtid *gtid= NULL;
+ mysql_mutex_lock(&LOCK_binlog_state);
elem= (element *)my_hash_search(&hash, (const uchar *)&domain_id, 0);
if (elem && elem->last_gtid)
- return elem->last_gtid;
- return NULL;
+ gtid= elem->last_gtid;
+ mysql_mutex_unlock(&LOCK_binlog_state);
+
+ return gtid;
}
@@ -1184,8 +1256,10 @@ rpl_binlog_state::count()
uint32 c= 0;
uint32 i;
+ mysql_mutex_lock(&LOCK_binlog_state);
for (i= 0; i < hash.records; ++i)
c+= ((element *)my_hash_element(&hash, i))->hash.records;
+ mysql_mutex_unlock(&LOCK_binlog_state);
return c;
}
@@ -1195,7 +1269,9 @@ int
rpl_binlog_state::get_gtid_list(rpl_gtid *gtid_list, uint32 list_size)
{
uint32 i, j, pos;
+ int res= 0;
+ mysql_mutex_lock(&LOCK_binlog_state);
pos= 0;
for (i= 0; i < hash.records; ++i)
{
@@ -1218,12 +1294,17 @@ rpl_binlog_state::get_gtid_list(rpl_gtid *gtid_list, uint32 list_size)
gtid= e->last_gtid;
if (pos >= list_size)
- return 1;
+ {
+ res= 1;
+ goto end;
+ }
memcpy(&gtid_list[pos++], gtid, sizeof(*gtid));
}
}
- return 0;
+end:
+ mysql_mutex_unlock(&LOCK_binlog_state);
+ return res;
}
@@ -1242,12 +1323,17 @@ rpl_binlog_state::get_most_recent_gtid_list(rpl_gtid **list, uint32 *size)
{
uint32 i;
uint32 alloc_size, out_size;
+ int res= 0;
+ out_size= 0;
+ mysql_mutex_lock(&LOCK_binlog_state);
alloc_size= hash.records;
if (!(*list= (rpl_gtid *)my_malloc(alloc_size * sizeof(rpl_gtid),
MYF(MY_WME))))
- return 1;
- out_size= 0;
+ {
+ res= 1;
+ goto end;
+ }
for (i= 0; i < alloc_size; ++i)
{
element *e= (element *)my_hash_element(&hash, i);
@@ -1256,8 +1342,10 @@ rpl_binlog_state::get_most_recent_gtid_list(rpl_gtid **list, uint32 *size)
memcpy(&((*list)[out_size++]), e->last_gtid, sizeof(rpl_gtid));
}
+end:
+ mysql_mutex_unlock(&LOCK_binlog_state);
*size= out_size;
- return 0;
+ return res;
}
@@ -1267,6 +1355,7 @@ rpl_binlog_state::append_pos(String *str)
uint32 i;
bool first= true;
+ mysql_mutex_lock(&LOCK_binlog_state);
for (i= 0; i < hash.records; ++i)
{
element *e= (element *)my_hash_element(&hash, i);
@@ -1274,6 +1363,7 @@ rpl_binlog_state::append_pos(String *str)
rpl_slave_state_tostring_helper(str, e->last_gtid, &first))
return true;
}
+ mysql_mutex_unlock(&LOCK_binlog_state);
return false;
}
@@ -1284,7 +1374,9 @@ rpl_binlog_state::append_state(String *str)
{
uint32 i, j;
bool first= true;
+ bool res= false;
+ mysql_mutex_lock(&LOCK_binlog_state);
for (i= 0; i < hash.records; ++i)
{
element *e= (element *)my_hash_element(&hash, i);
@@ -1306,11 +1398,16 @@ rpl_binlog_state::append_state(String *str)
gtid= e->last_gtid;
if (rpl_slave_state_tostring_helper(str, gtid, &first))
- return true;
+ {
+ res= true;
+ goto end;
+ }
}
}
- return false;
+end:
+ mysql_mutex_unlock(&LOCK_binlog_state);
+ return res;
}
diff --git a/sql/rpl_gtid.h b/sql/rpl_gtid.h
index a503184cee6..b0bc54900e7 100644
--- a/sql/rpl_gtid.h
+++ b/sql/rpl_gtid.h
@@ -146,13 +146,15 @@ struct rpl_binlog_state
rpl_binlog_state();
~rpl_binlog_state();
+ void reset_nolock();
void reset();
void free();
bool load(struct rpl_gtid *list, uint32 count);
+ int update_nolock(const struct rpl_gtid *gtid, bool strict);
int update(const struct rpl_gtid *gtid, bool strict);
int update_with_next_gtid(uint32 domain_id, uint32 server_id,
rpl_gtid *gtid);
- int alloc_element(const rpl_gtid *gtid);
+ int alloc_element_nolock(const rpl_gtid *gtid);
bool check_strict_sequence(uint32 domain_id, uint32 server_id, uint64 seq_no);
int bump_seq_no_if_needed(uint32 domain_id, uint64 seq_no);
int write_to_iocache(IO_CACHE *dest);
@@ -162,6 +164,7 @@ struct rpl_binlog_state
int get_most_recent_gtid_list(rpl_gtid **list, uint32 *size);
bool append_pos(String *str);
bool append_state(String *str);
+ rpl_gtid *find_nolock(uint32 domain_id, uint32 server_id);
rpl_gtid *find(uint32 domain_id, uint32 server_id);
rpl_gtid *find_most_recent(uint32 domain_id);
};
diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc
index ec6a16ddefa..9036f810020 100644
--- a/sql/rpl_rli.cc
+++ b/sql/rpl_rli.cc
@@ -1,5 +1,5 @@
-/* Copyright (c) 2006, 2012, Oracle and/or its affiliates.
- Copyright (c) 2010, 2012, Monty Program Ab.
+/* Copyright (c) 2006, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2010, 2013, Monty Program Ab
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
@@ -1540,6 +1540,9 @@ delete_or_keep_event_post_apply(rpl_group_info *rgi,
*/
rgi->set_annotate_event((Annotate_rows_log_event*) ev);
break;
+ case DELETE_ROWS_EVENT_V1:
+ case UPDATE_ROWS_EVENT_V1:
+ case WRITE_ROWS_EVENT_V1:
case DELETE_ROWS_EVENT:
case UPDATE_ROWS_EVENT:
case WRITE_ROWS_EVENT:
@@ -1673,6 +1676,8 @@ void rpl_group_info::slave_close_thread_tables(THD *thd)
close_thread_tables(thd);
/*
+ - If transaction rollback was requested due to deadlock
+ perform it and release metadata locks.
- If inside a multi-statement transaction,
defer the release of metadata locks until the current
transaction is either committed or rolled back. This prevents
@@ -1682,7 +1687,12 @@ void rpl_group_info::slave_close_thread_tables(THD *thd)
- If in autocommit mode, or outside a transactional context,
automatically release metadata locks of the current statement.
*/
- if (! thd->in_multi_stmt_transaction_mode())
+ if (thd->transaction_rollback_request)
+ {
+ trans_rollback_implicit(thd);
+ thd->mdl_context.release_transactional_locks();
+ }
+ else if (! thd->in_multi_stmt_transaction_mode())
thd->mdl_context.release_transactional_locks();
else
thd->mdl_context.release_statement_locks();
diff --git a/sql/rpl_utility.cc b/sql/rpl_utility.cc
index 590a5ae06ac..e8bc042e565 100644
--- a/sql/rpl_utility.cc
+++ b/sql/rpl_utility.cc
@@ -1,6 +1,5 @@
-/*
- Copyright (c) 2006, 2010, Oracle and/or its affiliates.
- Copyright (c) 2011, 2013, Monty Program Ab.
+/* Copyright (c) 2006, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2011, 2013, Monty Program Ab
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
@@ -47,6 +46,19 @@ uint32 uint_max(int bits) {
/**
+ Calculate display length for MySQL56 temporal data types from their metadata.
+ It contains fractional precision in the low 16-bit word.
+*/
+static uint32
+max_display_length_for_temporal2_field(uint32 int_display_length,
+ unsigned int metadata)
+{
+ metadata&= 0x00ff;
+ return int_display_length + metadata + (metadata ? 1 : 0);
+}
+
+
+/**
Compute the maximum display length of a field.
@param sql_type Type of the field
@@ -109,17 +121,23 @@ max_display_length_for_field(enum_field_types sql_type, unsigned int metadata)
case MYSQL_TYPE_DATE:
case MYSQL_TYPE_TIME:
- case MYSQL_TYPE_TIME2:
return 3;
+ case MYSQL_TYPE_TIME2:
+ return max_display_length_for_temporal2_field(MIN_TIME_WIDTH, metadata);
+
case MYSQL_TYPE_TIMESTAMP:
- case MYSQL_TYPE_TIMESTAMP2:
return 4;
+ case MYSQL_TYPE_TIMESTAMP2:
+ return max_display_length_for_temporal2_field(MAX_DATETIME_WIDTH, metadata);
+
case MYSQL_TYPE_DATETIME:
- case MYSQL_TYPE_DATETIME2:
return 8;
+ case MYSQL_TYPE_DATETIME2:
+ return max_display_length_for_temporal2_field(MAX_DATETIME_WIDTH, metadata);
+
case MYSQL_TYPE_BIT:
/*
Decode the size of the bit field from the master.
@@ -630,19 +648,32 @@ can_convert_field_to(Field *field,
else
DBUG_RETURN(false);
}
- else if (metadata == 0 &&
- ((field->real_type() == MYSQL_TYPE_TIMESTAMP2 &&
- source_type == MYSQL_TYPE_TIMESTAMP) ||
- (field->real_type() == MYSQL_TYPE_TIME2 &&
- source_type == MYSQL_TYPE_TIME) ||
- (field->real_type() == MYSQL_TYPE_DATETIME2 &&
- source_type == MYSQL_TYPE_DATETIME)))
+ else if (
+ /*
+ Conversion from MariaDB TIMESTAMP(0), TIME(0), DATETIME(0)
+ to the corresponding MySQL56 types is non-lossy.
+ */
+ (metadata == 0 &&
+ ((field->real_type() == MYSQL_TYPE_TIMESTAMP2 &&
+ source_type == MYSQL_TYPE_TIMESTAMP) ||
+ (field->real_type() == MYSQL_TYPE_TIME2 &&
+ source_type == MYSQL_TYPE_TIME) ||
+ (field->real_type() == MYSQL_TYPE_DATETIME2 &&
+ source_type == MYSQL_TYPE_DATETIME))) ||
+ /*
+ Conversion from MySQL56 TIMESTAMP(N), TIME(N), DATETIME(N)
+ to the corresponding MariaDB or MySQL55 types is non-lossy.
+ */
+ (metadata == field->decimals() &&
+ ((field->real_type() == MYSQL_TYPE_TIMESTAMP &&
+ source_type == MYSQL_TYPE_TIMESTAMP2) ||
+ (field->real_type() == MYSQL_TYPE_TIME &&
+ source_type == MYSQL_TYPE_TIME2) ||
+ (field->real_type() == MYSQL_TYPE_DATETIME &&
+ source_type == MYSQL_TYPE_DATETIME2))))
{
/*
TS-TODO: conversion from FSP1>FSP2.
- Can do non-lossy conversion
- from old TIME, TIMESTAMP, DATETIME
- to MySQL56 TIME(0), TIMESTAMP(0), DATETIME(0).
*/
*order_var= -1;
DBUG_RETURN(true);
@@ -914,6 +945,7 @@ TABLE *table_def::create_conversion_table(THD *thd, Relay_log_info *rli, TABLE *
DBUG_ENTER("table_def::create_conversion_table");
List<Create_field> field_list;
+ TABLE *conv_table= NULL;
/*
At slave, columns may differ. So we should create
MY_MIN(columns@master, columns@slave) columns in the
@@ -955,10 +987,15 @@ TABLE *table_def::create_conversion_table(THD *thd, Relay_log_info *rli, TABLE *
break;
case MYSQL_TYPE_DECIMAL:
- precision= field_metadata(col);
- decimals= static_cast<Field_num*>(target_table->field[col])->dec;
- max_length= field_metadata(col);
- break;
+ sql_print_error("In RBR mode, Slave received incompatible DECIMAL field "
+ "(old-style decimal field) from Master while creating "
+ "conversion table. Please consider changing datatype on "
+ "Master to new style decimal by executing ALTER command for"
+ " column Name: %s.%s.%s.",
+ target_table->s->db.str,
+ target_table->s->table_name.str,
+ target_table->field[col]->field_name);
+ goto err;
case MYSQL_TYPE_TINY_BLOB:
case MYSQL_TYPE_MEDIUM_BLOB:
@@ -986,7 +1023,9 @@ TABLE *table_def::create_conversion_table(THD *thd, Relay_log_info *rli, TABLE *
field_def->interval= interval;
}
- TABLE *conv_table= create_virtual_tmp_table(thd, field_list);
+ conv_table= create_virtual_tmp_table(thd, field_list);
+
+err:
if (conv_table == NULL)
rli->report(ERROR_LEVEL, ER_SLAVE_CANT_CREATE_CONVERSION,
ER(ER_SLAVE_CANT_CREATE_CONVERSION),
diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt
index 3ddeb7d82bb..2d061fc314c 100644
--- a/sql/share/errmsg-utf8.txt
+++ b/sql/share/errmsg-utf8.txt
@@ -6924,6 +6924,20 @@ ER_IDENT_CAUSES_TOO_LONG_PATH
ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_NOT_NULL
eng "cannot silently convert NULL values, as required in this SQL_MODE"
+ER_MUST_CHANGE_PASSWORD_LOGIN
+ eng "Your password has expired. To log in you must change it using a client that supports expired passwords."
+ bgn "Паролата ви е изтекла. За да влезете трябва да я смените използвайки клиент който поддрържа такива пароли."
+
+ER_ROW_IN_WRONG_PARTITION
+ eng "Found a row in wrong partition %s"
+ swe "Hittade en rad i fel partition %s"
+
+ER_MTS_EVENT_BIGGER_PENDING_JOBS_SIZE_MAX
+ eng "Cannot schedule event %s, relay-log name %s, position %s to Worker thread because its size %lu exceeds %lu of slave_pending_jobs_size_max."
+
+ER_INNODB_NO_FT_USES_PARSER
+ eng "Cannot CREATE FULLTEXT INDEX WITH PARSER on InnoDB table"
+
#
# MariaDB error messages section starts here
#
diff --git a/sql/slave.cc b/sql/slave.cc
index f3ac16db110..e29387a4b7f 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -795,6 +795,7 @@ int start_slave_thread(
{
pthread_t th;
ulong start_id;
+ int error;
DBUG_ENTER("start_slave_thread");
DBUG_ASSERT(mi->inited);
@@ -821,9 +822,10 @@ int start_slave_thread(
}
start_id= *slave_run_id;
DBUG_PRINT("info",("Creating new slave thread"));
- if (mysql_thread_create(thread_key,
- &th, &connection_attrib, h_func, (void*)mi))
+ if ((error = mysql_thread_create(thread_key,
+ &th, &connection_attrib, h_func, (void*)mi)))
{
+ sql_print_error("Can't create slave thread (errno= %d).", error);
if (start_lock)
mysql_mutex_unlock(start_lock);
DBUG_RETURN(ER_SLAVE_THREAD);
@@ -2500,6 +2502,7 @@ static bool send_show_master_info_header(THD *thd, bool full,
sizeof(mi->ssl_crlpath)));
field_list.push_back(new Item_empty_string("Using_Gtid",
sizeof("Current_Pos")-1));
+ field_list.push_back(new Item_empty_string("Gtid_IO_Pos", 30));
if (full)
{
field_list.push_back(new Item_return_int("Retried_transactions",
@@ -2693,6 +2696,12 @@ static bool send_show_master_info_data(THD *thd, Master_info *mi, bool full,
// Master_Ssl_Crlpath
protocol->store(mi->ssl_capath, &my_charset_bin);
protocol->store(mi->using_gtid_astext(mi->using_gtid), &my_charset_bin);
+ {
+ char buff[30];
+ String tmp(buff, sizeof(buff), system_charset_info);
+ mi->gtid_current_pos.to_string(&tmp);
+ protocol->store(tmp.ptr(), tmp.length(), &my_charset_bin);
+ }
if (full)
{
protocol->store((uint32) mi->rli.retried_trans);
@@ -3467,6 +3476,7 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli,
if (exec_res && (temp_err= has_temporary_error(thd)))
{
const char *errmsg;
+ rli->clear_error();
/*
We were in a transaction which has been rolled back because of a
temporary error;
@@ -3979,7 +3989,7 @@ Stopping slave I/O thread due to out-of-memory error from master");
goto err;
}
- if (mi->using_gtid != Master_info::USE_GTID_NO &&
+ if (mi->using_gtid == Master_info::USE_GTID_NO &&
flush_master_info(mi, TRUE, TRUE))
{
sql_print_error("Failed to flush master info file");
@@ -6013,10 +6023,9 @@ static Log_event* next_event(rpl_group_info *rgi, ulonglong *event_size)
IO_CACHE* cur_log = rli->cur_log;
mysql_mutex_t *log_lock = rli->relay_log.get_log_lock();
const char* errmsg=0;
- THD *thd = rgi->thd;
DBUG_ENTER("next_event");
- DBUG_ASSERT(thd != 0 && thd == rli->sql_driver_thd);
+ DBUG_ASSERT(rgi->thd != 0 && rgi->thd == rli->sql_driver_thd);
*event_size= 0;
#ifndef DBUG_OFF
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 4bf6c13a7d8..84e2c3069c3 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -1,5 +1,6 @@
/*
- Copyright (c) 2002, 2011, Oracle and/or its affiliates.
+ Copyright (c) 2002, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2011, 2013, Monty Program Ab
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
@@ -2102,10 +2103,18 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
close_thread_tables(thd);
thd_proc_info(thd, 0);
- if (! thd->in_sub_stmt && ! thd->in_multi_stmt_transaction_mode())
- thd->mdl_context.release_transactional_locks();
- else if (! thd->in_sub_stmt)
- thd->mdl_context.release_statement_locks();
+ if (! thd->in_sub_stmt)
+ {
+ if (thd->transaction_rollback_request)
+ {
+ trans_rollback_implicit(thd);
+ thd->mdl_context.release_transactional_locks();
+ }
+ else if (! thd->in_multi_stmt_transaction_mode())
+ thd->mdl_context.release_transactional_locks();
+ else
+ thd->mdl_context.release_statement_locks();
+ }
thd->rollback_item_tree_changes();
@@ -2964,10 +2973,18 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
close_thread_tables(thd);
thd_proc_info(thd, 0);
- if (! thd->in_sub_stmt && ! thd->in_multi_stmt_transaction_mode())
- thd->mdl_context.release_transactional_locks();
- else if (! thd->in_sub_stmt)
- thd->mdl_context.release_statement_locks();
+ if (! thd->in_sub_stmt)
+ {
+ if (thd->transaction_rollback_request)
+ {
+ trans_rollback_implicit(thd);
+ thd->mdl_context.release_transactional_locks();
+ }
+ else if (! thd->in_multi_stmt_transaction_mode())
+ thd->mdl_context.release_transactional_locks();
+ else
+ thd->mdl_context.release_statement_locks();
+ }
}
//TODO: why is this here if log_slow_query is in sp_instr_stmt_execute?
delete_explain_query(m_lex);
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index 7c2846c827c..f2e762980b9 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -722,7 +722,7 @@ static HASH acl_roles;
The hashkey used represents all the entries combined
*/
static HASH acl_roles_mappings;
-static MEM_ROOT mem, memex;
+static MEM_ROOT acl_memroot, grant_memroot;
static bool initialized=0;
static bool allow_all_hosts=1;
static HASH acl_check_hosts, column_priv_hash, proc_priv_hash, func_priv_hash;
@@ -819,9 +819,16 @@ static void free_acl_role(ACL_ROLE *role)
delete_dynamic(&(role->parent_grantee));
}
-/*
+/**
Convert scrambled password to binary form, according to scramble type,
Binary form is stored in user.salt.
+
+ @param acl_user The object where to store the salt
+ @param password The password hash containing the salt
+ @param password_len The length of the password hash
+
+ Despite the name of the function it is used when loading ACLs from disk
+ to store the password hash in the ACL_USER object.
*/
static void
@@ -1013,7 +1020,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
grant_version++; /* Privileges updated */
- init_sql_alloc(&mem, ACL_ALLOC_BLOCK_SIZE, 0, MYF(0));
+ init_sql_alloc(&acl_memroot, ACL_ALLOC_BLOCK_SIZE, 0, MYF(0));
(void) my_init_dynamic_array(&acl_hosts,sizeof(ACL_HOST), 20, 50, MYF(0));
if (tables[0].table) // "host" table may not exist (e.g. in MySQL 5.6.7+)
{
@@ -1024,8 +1031,8 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
while (!(read_record_info.read_record(&read_record_info)))
{
ACL_HOST host;
- update_hostname(&host.host,get_field(&mem, table->field[0]));
- host.db= get_field(&mem, table->field[1]);
+ update_hostname(&host.host,get_field(&acl_memroot, table->field[0]));
+ host.db= get_field(&acl_memroot, table->field[1]);
if (lower_case_table_names && host.db)
{
/*
@@ -1130,8 +1137,8 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
ACL_USER user;
bool is_role= FALSE;
bzero(&user, sizeof(user));
- update_hostname(&user.host, get_field(&mem, table->field[0]));
- char *username= get_field(&mem, table->field[1]);
+ update_hostname(&user.host, get_field(&acl_memroot, table->field[0]));
+ char *username= get_field(&acl_memroot, table->field[1]);
user.user.str= username;
user.user.length= username? strlen(username) : 0;
@@ -1157,7 +1164,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
continue;
}
- char *password= get_field(&mem, table->field[2]);
+ char *password= get_field(&acl_memroot, table->field[2]);
uint password_len= password ? strlen(password) : 0;
user.auth_string.str= safe_str(password);
user.auth_string.length= password_len;
@@ -1222,9 +1229,9 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
else /* !strcmp(ssl_type, "SPECIFIED") */
user.ssl_type=SSL_TYPE_SPECIFIED;
- user.ssl_cipher= get_field(&mem, table->field[next_field++]);
- user.x509_issuer= get_field(&mem, table->field[next_field++]);
- user.x509_subject= get_field(&mem, table->field[next_field++]);
+ user.ssl_cipher= get_field(&acl_memroot, table->field[next_field++]);
+ user.x509_issuer= get_field(&acl_memroot, table->field[next_field++]);
+ user.x509_subject= get_field(&acl_memroot, table->field[next_field++]);
char *ptr = get_field(thd->mem_root, table->field[next_field++]);
user.user_resource.questions=ptr ? atoi(ptr) : 0;
@@ -1246,7 +1253,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
if (!is_role && table->s->fields >= 41)
{
/* We may have plugin & auth_String fields */
- char *tmpstr= get_field(&mem, table->field[next_field++]);
+ char *tmpstr= get_field(&acl_memroot, table->field[next_field++]);
if (tmpstr)
{
user.plugin.str= tmpstr;
@@ -1260,7 +1267,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
safe_str(user.host.hostname));
}
user.auth_string.str=
- safe_str(get_field(&mem, table->field[next_field++]));
+ safe_str(get_field(&acl_memroot, table->field[next_field++]));
user.auth_string.length= strlen(user.auth_string.str);
fix_user_plugin_ptr(&user);
@@ -1291,7 +1298,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
if (is_role)
{
DBUG_PRINT("info", ("Found role %s", user.user.str));
- ACL_ROLE *entry= new (&mem) ACL_ROLE(&user, &mem);
+ ACL_ROLE *entry= new (&acl_memroot) ACL_ROLE(&user, &acl_memroot);
entry->role_grants = user.role_grants;
(void) my_init_dynamic_array(&entry->parent_grantee,
sizeof(ACL_USER_BASE *), 8, 8, MYF(0));
@@ -1322,12 +1329,12 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
while (!(read_record_info.read_record(&read_record_info)))
{
ACL_DB db;
- db.user=get_field(&mem, table->field[MYSQL_DB_FIELD_USER]);
- const char *hostname= get_field(&mem, table->field[MYSQL_DB_FIELD_HOST]);
+ db.user=get_field(&acl_memroot, table->field[MYSQL_DB_FIELD_USER]);
+ const char *hostname= get_field(&acl_memroot, table->field[MYSQL_DB_FIELD_HOST]);
if (!hostname && find_acl_role(db.user))
hostname= "";
update_hostname(&db.host, hostname);
- db.db=get_field(&mem, table->field[MYSQL_DB_FIELD_DB]);
+ db.db=get_field(&acl_memroot, table->field[MYSQL_DB_FIELD_DB]);
if (!db.db)
{
sql_print_warning("Found an entry in the 'db' table with empty database name; Skipped");
@@ -1391,7 +1398,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
while (!(read_record_info.read_record(&read_record_info)))
{
ACL_PROXY_USER proxy;
- proxy.init(table, &mem);
+ proxy.init(table, &acl_memroot);
if (proxy.check_validity(check_no_resolve))
continue;
if (push_dynamic(&acl_proxy_users, (uchar*) &proxy))
@@ -1437,9 +1444,9 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
continue;
}
- ROLE_GRANT_PAIR *mapping= new (&mem) ROLE_GRANT_PAIR;
+ ROLE_GRANT_PAIR *mapping= new (&acl_memroot) ROLE_GRANT_PAIR;
- if (mapping->init(&mem, username, hostname, rolename, with_grant_option))
+ if (mapping->init(&acl_memroot, username, hostname, rolename, with_grant_option))
continue;
my_hash_insert(&acl_roles_mappings, (uchar*) mapping);
@@ -1468,7 +1475,7 @@ end:
void acl_free(bool end)
{
my_hash_free(&acl_roles);
- free_root(&mem,MYF(0));
+ free_root(&acl_memroot,MYF(0));
delete_dynamic(&acl_hosts);
delete_dynamic_with_callback(&acl_users, (FREE_FUNC) free_acl_user);
delete_dynamic(&acl_dbs);
@@ -1562,7 +1569,7 @@ my_bool acl_reload(THD *thd)
old_acl_roles_mappings= acl_roles_mappings;
old_acl_proxy_users= acl_proxy_users;
old_acl_dbs= acl_dbs;
- old_mem= mem;
+ old_mem= acl_memroot;
delete_dynamic(&acl_wild_hosts);
my_hash_free(&acl_check_hosts);
@@ -1576,7 +1583,7 @@ my_bool acl_reload(THD *thd)
acl_roles_mappings= old_acl_roles_mappings;
acl_proxy_users= old_acl_proxy_users;
acl_dbs= old_acl_dbs;
- mem= old_mem;
+ acl_memroot= old_mem;
init_check_host();
}
else
@@ -1960,15 +1967,15 @@ static void acl_update_user(const char *user, const char *host,
{
acl_user->plugin= *plugin;
acl_user->auth_string.str= auth->str ?
- strmake_root(&mem, auth->str, auth->length) : const_cast<char*>("");
+ strmake_root(&acl_memroot, auth->str, auth->length) : const_cast<char*>("");
acl_user->auth_string.length= auth->length;
if (fix_user_plugin_ptr(acl_user))
- acl_user->plugin.str= strmake_root(&mem, plugin->str, plugin->length);
+ acl_user->plugin.str= strmake_root(&acl_memroot, plugin->str, plugin->length);
}
else
if (password[0])
{
- acl_user->auth_string.str= strmake_root(&mem, password, password_len);
+ acl_user->auth_string.str= strmake_root(&acl_memroot, password, password_len);
acl_user->auth_string.length= password_len;
set_user_salt(acl_user, password, password_len);
set_user_plugin(acl_user, password_len);
@@ -1985,12 +1992,12 @@ static void acl_update_user(const char *user, const char *host,
if (ssl_type != SSL_TYPE_NOT_SPECIFIED)
{
acl_user->ssl_type= ssl_type;
- acl_user->ssl_cipher= (ssl_cipher ? strdup_root(&mem,ssl_cipher) :
+ acl_user->ssl_cipher= (ssl_cipher ? strdup_root(&acl_memroot,ssl_cipher) :
0);
- acl_user->x509_issuer= (x509_issuer ? strdup_root(&mem,x509_issuer) :
+ acl_user->x509_issuer= (x509_issuer ? strdup_root(&acl_memroot,x509_issuer) :
0);
acl_user->x509_subject= (x509_subject ?
- strdup_root(&mem,x509_subject) : 0);
+ strdup_root(&acl_memroot,x509_subject) : 0);
}
/* search complete: */
break;
@@ -2004,7 +2011,7 @@ static void acl_insert_role(const char *rolename, ulong privileges)
ACL_ROLE *entry;
mysql_mutex_assert_owner(&acl_cache->lock);
- entry= new (&mem) ACL_ROLE(rolename, privileges, &mem);
+ entry= new (&acl_memroot) ACL_ROLE(rolename, privileges, &acl_memroot);
(void) my_init_dynamic_array(&entry->parent_grantee,
sizeof(ACL_USER_BASE *), 8, 8, MYF(0));
(void) my_init_dynamic_array(&entry->role_grants,sizeof(ACL_ROLE *),
@@ -2029,21 +2036,21 @@ static void acl_insert_user(const char *user, const char *host,
mysql_mutex_assert_owner(&acl_cache->lock);
- acl_user.user.str=*user ? strdup_root(&mem,user) : 0;
+ acl_user.user.str=*user ? strdup_root(&acl_memroot,user) : 0;
acl_user.user.length= strlen(user);
- update_hostname(&acl_user.host, safe_strdup_root(&mem, host));
+ update_hostname(&acl_user.host, safe_strdup_root(&acl_memroot, host));
if (plugin->str[0])
{
acl_user.plugin= *plugin;
acl_user.auth_string.str= auth->str ?
- strmake_root(&mem, auth->str, auth->length) : const_cast<char*>("");
+ strmake_root(&acl_memroot, auth->str, auth->length) : const_cast<char*>("");
acl_user.auth_string.length= auth->length;
if (fix_user_plugin_ptr(&acl_user))
- acl_user.plugin.str= strmake_root(&mem, plugin->str, plugin->length);
+ acl_user.plugin.str= strmake_root(&acl_memroot, plugin->str, plugin->length);
}
else
{
- acl_user.auth_string.str= strmake_root(&mem, password, password_len);
+ acl_user.auth_string.str= strmake_root(&acl_memroot, password, password_len);
acl_user.auth_string.length= password_len;
set_user_salt(&acl_user, password, password_len);
set_user_plugin(&acl_user, password_len);
@@ -2056,9 +2063,9 @@ static void acl_insert_user(const char *user, const char *host,
acl_user.hostname_length=(uint) strlen(host);
acl_user.ssl_type= (ssl_type != SSL_TYPE_NOT_SPECIFIED ?
ssl_type : SSL_TYPE_NONE);
- acl_user.ssl_cipher= ssl_cipher ? strdup_root(&mem,ssl_cipher) : 0;
- acl_user.x509_issuer= x509_issuer ? strdup_root(&mem,x509_issuer) : 0;
- acl_user.x509_subject=x509_subject ? strdup_root(&mem,x509_subject) : 0;
+ acl_user.ssl_cipher= ssl_cipher ? strdup_root(&acl_memroot,ssl_cipher) : 0;
+ acl_user.x509_issuer= x509_issuer ? strdup_root(&acl_memroot,x509_issuer) : 0;
+ acl_user.x509_subject=x509_subject ? strdup_root(&acl_memroot,x509_subject) : 0;
(void) my_init_dynamic_array(&acl_user.role_grants, sizeof(ACL_USER *),
8, 8, MYF(0));
@@ -2133,9 +2140,9 @@ static void acl_insert_db(const char *user, const char *host, const char *db,
{
ACL_DB acl_db;
mysql_mutex_assert_owner(&acl_cache->lock);
- acl_db.user=strdup_root(&mem,user);
- update_hostname(&acl_db.host, safe_strdup_root(&mem, host));
- acl_db.db=strdup_root(&mem,db);
+ acl_db.user=strdup_root(&acl_memroot,user);
+ update_hostname(&acl_db.host, safe_strdup_root(&acl_memroot, host));
+ acl_db.db=strdup_root(&acl_memroot,db);
acl_db.initial_access= acl_db.access= privileges;
acl_db.sort=get_sort(3,acl_db.host.hostname,acl_db.db,acl_db.user);
(void) push_dynamic(&acl_dbs,(uchar*) &acl_db);
@@ -2408,7 +2415,7 @@ static void remove_role_user_mapping(ACL_USER_BASE *grantee, ACL_ROLE *role,
static my_bool add_role_user_mapping_action(void *ptr, void *unused __attribute__((unused)))
{
ROLE_GRANT_PAIR *pair= (ROLE_GRANT_PAIR*)ptr;
- my_bool status __attribute__((unused));
+ bool status __attribute__((unused));
status= add_role_user_mapping(pair->u_uname, pair->u_hname, pair->r_uname);
/*
The invariant chosen is that acl_roles_mappings should _always_
@@ -2416,7 +2423,7 @@ static my_bool add_role_user_mapping_action(void *ptr, void *unused __attribute_
If add_role_user_mapping detects an invalid entry, it will not add
the mapping into the ACL_USER::role_grants array.
*/
- DBUG_ASSERT(status >= 0);
+ DBUG_ASSERT(status == 0);
return 0;
}
@@ -2481,22 +2488,20 @@ bool acl_check_host(const char *host, const char *ip)
}
-/*
+/**
Check if the user is allowed to change password
- SYNOPSIS:
- check_change_password()
- thd THD
- host hostname for the user
- user user name
- new_password new password
+ @param thd THD
+ @param host Hostname for the user
+ @param user User name
+ @param new_password New password
+ @param new_password_len The length of the new password
- NOTE:
- new_password cannot be NULL
+ new_password cannot be NULL
- RETURN VALUE
- 0 OK
- 1 ERROR ; In this case the error is sent to the client.
+ @return Error status
+ @retval 0 OK
+ @retval 1 ERROR; In this case the error is sent to the client.
*/
int check_change_password(THD *thd, const char *host, const char *user,
@@ -2540,21 +2545,18 @@ int check_change_password(THD *thd, const char *host, const char *user,
}
-/*
- Change a password for a user
-
- SYNOPSIS
- change_password()
- thd Thread handle
- host Hostname
- user User name
- new_password New password for host@user
+/**
+ Change a password for a user.
- RETURN VALUES
- 0 ok
- 1 ERROR; In this case the error is sent to the client.
+ @param thd THD
+ @param host Hostname
+ @param user User name
+ @param new_password New password hash for host@user
+
+ @return Error code
+ @retval 0 ok
+ @retval 1 ERROR; In this case the error is sent to the client.
*/
-
bool change_password(THD *thd, const char *host, const char *user,
char *new_password)
{
@@ -2634,7 +2636,7 @@ bool change_password(THD *thd, const char *host, const char *user,
if (acl_user->plugin.str == native_password_plugin_name.str ||
acl_user->plugin.str == old_password_plugin_name.str)
{
- acl_user->auth_string.str= strmake_root(&mem, new_password, new_password_len);
+ acl_user->auth_string.str= strmake_root(&acl_memroot, new_password, new_password_len);
acl_user->auth_string.length= new_password_len;
set_user_salt(acl_user, new_password, new_password_len);
set_user_plugin(acl_user, new_password_len);
@@ -2941,17 +2943,17 @@ bool hostname_requires_resolving(const char *hostname)
}
-/*
+/**
Update record for user in mysql.user privilege table with new password.
- SYNOPSIS
- update_user_table()
- thd Thread handle
- table Pointer to TABLE object for open mysql.user table
- host/user Hostname/username pair identifying user for which
- new password should be set
- new_password New password
- new_password_len Length of new password
+ @param thd THD
+ @param table Pointer to TABLE object for open mysql.user table
+ @param host Hostname
+ @param user Username
+ @param new_password New password hash
+ @param new_password_len Length of new password hash
+
+ @see change_password
*/
static bool update_user_table(THD *thd, TABLE *table,
@@ -3502,8 +3504,8 @@ replace_roles_mapping_table(TABLE *table, LEX_STRING *user, LEX_STRING *host,
else
{
/* allocate a new entry that will go in the hash */
- ROLE_GRANT_PAIR *hash_entry= new (&mem) ROLE_GRANT_PAIR;
- if (hash_entry->init(&mem, user->str, host->str, role->str, with_admin))
+ ROLE_GRANT_PAIR *hash_entry= new (&acl_memroot) ROLE_GRANT_PAIR;
+ if (hash_entry->init(&acl_memroot, user->str, host->str, role->str, with_admin))
DBUG_RETURN(1);
my_hash_insert(&acl_roles_mappings, (uchar*) hash_entry);
}
@@ -3660,7 +3662,7 @@ replace_proxies_priv_table(THD *thd, TABLE *table, const LEX_USER *user,
}
else
{
- new_grant.init(&mem, user->host.str, user->user.str,
+ new_grant.init(&acl_memroot, user->host.str, user->user.str,
proxied_user->host.str, proxied_user->user.str,
with_grant_arg);
acl_insert_proxy_user(&new_grant);
@@ -3690,10 +3692,10 @@ public:
uint key_length;
GRANT_COLUMN(String &c, ulong y) :rights (y), init_rights(y)
{
- column= (char*) memdup_root(&memex,c.ptr(), key_length=c.length());
+ column= (char*) memdup_root(&grant_memroot,c.ptr(), key_length=c.length());
}
- /* this constructor assumes thas source->column is allocated in memex */
+ /* this constructor assumes thas source->column is allocated in grant_memroot */
GRANT_COLUMN(GRANT_COLUMN *source) : column(source->column),
rights (source->rights), init_rights(0), key_length(source->key_length) { }
};
@@ -3751,23 +3753,23 @@ void GRANT_NAME::set_user_details(const char *h, const char *d,
bool is_routine)
{
/* Host given by user */
- update_hostname(&host, strdup_root(&memex, h));
+ update_hostname(&host, strdup_root(&grant_memroot, h));
if (db != d)
{
- db= strdup_root(&memex, d);
+ db= strdup_root(&grant_memroot, d);
if (lower_case_table_names)
my_casedn_str(files_charset_info, db);
}
- user = strdup_root(&memex,u);
+ user = strdup_root(&grant_memroot,u);
sort= get_sort(3,host.hostname,db,user);
if (tname != t)
{
- tname= strdup_root(&memex, t);
+ tname= strdup_root(&grant_memroot, t);
if (lower_case_table_names || is_routine)
my_casedn_str(files_charset_info, tname);
}
key_length= strlen(d) + strlen(u)+ strlen(t)+3;
- hash_key= (char*) alloc_root(&memex,key_length);
+ hash_key= (char*) alloc_root(&grant_memroot,key_length);
strmov(strmov(strmov(hash_key,user)+1,db)+1,tname);
}
@@ -3791,18 +3793,18 @@ GRANT_TABLE::GRANT_TABLE(const char *h, const char *d,const char *u,
*/
GRANT_NAME::GRANT_NAME(TABLE *form, bool is_routine)
{
- user= safe_str(get_field(&memex,form->field[2]));
+ user= safe_str(get_field(&grant_memroot,form->field[2]));
- const char *hostname= get_field(&memex, form->field[0]);
+ const char *hostname= get_field(&grant_memroot, form->field[0]);
mysql_mutex_lock(&acl_cache->lock);
if (!hostname && find_acl_role(user))
hostname= "";
mysql_mutex_unlock(&acl_cache->lock);
update_hostname(&host, hostname);
- db= get_field(&memex,form->field[1]);
+ db= get_field(&grant_memroot,form->field[1]);
sort= get_sort(3, host.hostname, db, user);
- tname= get_field(&memex,form->field[3]);
+ tname= get_field(&grant_memroot,form->field[3]);
if (!db || !tname)
{
/* Wrong table row; Ignore it */
@@ -3818,7 +3820,7 @@ GRANT_NAME::GRANT_NAME(TABLE *form, bool is_routine)
my_casedn_str(files_charset_info, tname);
}
key_length= (strlen(db) + strlen(user) + strlen(tname) + 3);
- hash_key= (char*) alloc_root(&memex, key_length);
+ hash_key= (char*) alloc_root(&grant_memroot, key_length);
strmov(strmov(strmov(hash_key,user)+1,db)+1,tname);
privs = (ulong) form->field[6]->val_int();
privs = fix_rights_for_table(privs);
@@ -5024,7 +5026,7 @@ static int update_role_columns(GRANT_TABLE *merged,
else
{
changed= 1;
- my_hash_insert(mh, (uchar*)new (&memex) GRANT_COLUMN(ccol));
+ my_hash_insert(mh, (uchar*)new (&grant_memroot) GRANT_COLUMN(ccol));
}
}
}
@@ -5075,7 +5077,7 @@ static int update_role_table_columns(GRANT_TABLE *merged,
roles) we need to create it
*/
DBUG_ASSERT(privs | cols);
- merged= new (&memex) GRANT_TABLE("", first[0]->db, role, first[0]->tname,
+ merged= new (&grant_memroot) GRANT_TABLE("", first[0]->db, role, first[0]->tname,
privs, cols);
merged->init_privs= merged->init_cols= 0;
update_role_columns(merged, first, last);
@@ -5201,7 +5203,7 @@ static int update_role_routines(GRANT_NAME *merged, GRANT_NAME **first,
roles) we need to create it
*/
DBUG_ASSERT(privs);
- merged= new (&memex) GRANT_NAME("", first[0]->db, role, first[0]->tname,
+ merged= new (&grant_memroot) GRANT_NAME("", first[0]->db, role, first[0]->tname,
privs, true);
merged->init_privs= 0; // all privs are inherited
my_hash_insert(hash, (uchar *)merged);
@@ -5443,12 +5445,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
{
if (!(rights & CREATE_ACL))
{
- char buf[FN_REFLEN + 1];
- build_table_filename(buf, sizeof(buf) - 1, table_list->db,
- table_list->table_name, reg_ext, 0);
- fn_format(buf, buf, "", "", MY_UNPACK_FILENAME | MY_RESOLVE_SYMLINKS |
- MY_RETURN_REAL_PATH | MY_APPEND_EXT);
- if (access(buf,F_OK))
+ if (!ha_table_exists(thd, table_list->db, table_list->table_name, 0))
{
my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, table_list->alias);
DBUG_RETURN(TRUE);
@@ -5523,7 +5520,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
mysql_rwlock_wrlock(&LOCK_grant);
mysql_mutex_lock(&acl_cache->lock);
MEM_ROOT *old_root= thd->mem_root;
- thd->mem_root= &memex;
+ thd->mem_root= &grant_memroot;
grant_version++;
while ((tmp_Str = str_list++))
@@ -5733,7 +5730,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
mysql_rwlock_wrlock(&LOCK_grant);
mysql_mutex_lock(&acl_cache->lock);
MEM_ROOT *old_root= thd->mem_root;
- thd->mem_root= &memex;
+ thd->mem_root= &grant_memroot;
DBUG_PRINT("info",("now time to iterate and add users"));
@@ -6238,7 +6235,7 @@ void grant_free(void)
my_hash_free(&column_priv_hash);
my_hash_free(&proc_priv_hash);
my_hash_free(&func_priv_hash);
- free_root(&memex,MYF(0));
+ free_root(&grant_memroot,MYF(0));
DBUG_VOID_RETURN;
}
@@ -6305,7 +6302,7 @@ static my_bool grant_load(THD *thd, TABLE_LIST *tables)
0,0,0, (my_hash_get_key) get_grant_table, 0,0);
(void) my_hash_init(&func_priv_hash, &my_charset_utf8_bin,
0,0,0, (my_hash_get_key) get_grant_table, 0,0);
- init_sql_alloc(&memex, ACL_ALLOC_BLOCK_SIZE, 0, MYF(0));
+ init_sql_alloc(&grant_memroot, ACL_ALLOC_BLOCK_SIZE, 0, MYF(0));
t_table= tables[0].table;
c_table= tables[1].table;
@@ -6317,7 +6314,7 @@ static my_bool grant_load(THD *thd, TABLE_LIST *tables)
t_table->use_all_columns();
c_table->use_all_columns();
- memex_ptr= &memex;
+ memex_ptr= &grant_memroot;
my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr);
if (!t_table->file->ha_index_first(t_table->record[0]))
@@ -6501,7 +6498,7 @@ my_bool grant_reload(THD *thd)
Create a new memory pool but save the current memory pool to make an undo
opertion possible in case of failure.
*/
- old_mem= memex;
+ old_mem= grant_memroot;
if ((return_val= grant_load(thd, tables)))
{ // Error. Revert to old hash
@@ -6510,7 +6507,7 @@ my_bool grant_reload(THD *thd)
column_priv_hash= old_column_priv_hash; /* purecov: deadcode */
proc_priv_hash= old_proc_priv_hash;
func_priv_hash= old_func_priv_hash;
- memex= old_mem; /* purecov: deadcode */
+ grant_memroot= old_mem; /* purecov: deadcode */
}
else
{
@@ -8639,7 +8636,7 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
my_hash_delete(&acl_roles, (uchar*) acl_role);
DBUG_RETURN(1);
}
- acl_role->user.str= strdup_root(&mem, user_to->user.str);
+ acl_role->user.str= strdup_root(&acl_memroot, user_to->user.str);
acl_role->user.length= user_to->user.length;
my_hash_update(&acl_roles, (uchar*) acl_role, (uchar*) old_key,
@@ -8789,6 +8786,8 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
case ROLES_MAPPINGS_HASH:
my_hash_delete(roles_mappings_hash, (uchar*) role_grant_pair);
+ if (idx != elements)
+ idx++;
break;
default:
@@ -8800,14 +8799,14 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
{
switch ( struct_no ) {
case USER_ACL:
- acl_user->user.str= strdup_root(&mem, user_to->user.str);
+ acl_user->user.str= strdup_root(&acl_memroot, user_to->user.str);
acl_user->user.length= user_to->user.length;
- acl_user->host.hostname= strdup_root(&mem, user_to->host.str);
+ acl_user->host.hostname= strdup_root(&acl_memroot, user_to->host.str);
break;
case DB_ACL:
- acl_db->user= strdup_root(&mem, user_to->user.str);
- acl_db->host.hostname= strdup_root(&mem, user_to->host.str);
+ acl_db->user= strdup_root(&acl_memroot, user_to->user.str);
+ acl_db->host.hostname= strdup_root(&acl_memroot, user_to->host.str);
break;
case COLUMN_PRIVILEGES_HASH:
@@ -8849,8 +8848,8 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
}
case PROXY_USERS_ACL:
- acl_proxy_user->set_user (&mem, user_to->user.str);
- acl_proxy_user->set_host (&mem, user_to->host.str);
+ acl_proxy_user->set_user (&acl_memroot, user_to->user.str);
+ acl_proxy_user->set_host (&acl_memroot, user_to->host.str);
break;
case ROLES_MAPPINGS_HASH:
@@ -8864,11 +8863,11 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
bool oom;
if (role_not_matched)
- oom= role_grant_pair->init(&mem, user_to->user.str,
+ oom= role_grant_pair->init(&acl_memroot, user_to->user.str,
user_to->host.str,
role_grant_pair->r_uname, false);
else
- oom= role_grant_pair->init(&mem, role_grant_pair->u_uname,
+ oom= role_grant_pair->init(&acl_memroot, role_grant_pair->u_uname,
role_grant_pair->u_hname,
user_to->user.str, false);
if (oom)
@@ -11076,7 +11075,7 @@ static bool find_mpvio_user(MPVIO_EXT *mpvio)
ACL_USER *user= find_user_or_anon(sctx->host, sctx->user, sctx->ip);
if (user)
- mpvio->acl_user= user->copy(&mem);
+ mpvio->acl_user= user->copy(mpvio->thd->mem_root);
mysql_mutex_unlock(&acl_cache->lock);
@@ -11144,36 +11143,21 @@ static bool
read_client_connect_attrs(char **ptr, char *end,
const CHARSET_INFO *from_cs)
{
- size_t length, length_length;
- size_t max_bytes_available= end - *ptr;
+ size_t length;
+ char *ptr_save= *ptr;
+
/* not enough bytes to hold the length */
- if ((*ptr) >= (end - 1))
+ if (ptr_save >= end)
return true;
- /* read the length */
- if (max_bytes_available >= 9)
- {
- char *ptr_save= *ptr;
- length= net_field_length_ll((uchar **) ptr);
- length_length= *ptr - ptr_save;
- DBUG_ASSERT(length_length <= 9);
- }
- else
- {
- /* to avoid reading unallocated and uninitialized memory */
- char buf[10]={'\0','\0','\0','\0','\0','\0','\0','\0','\0','\0',},
- *len_ptr= buf;
- memcpy(buf, *ptr, max_bytes_available);
- length= net_field_length_ll((uchar **) &len_ptr);
- length_length= len_ptr - buf;
- *ptr+= length_length;
- if (max_bytes_available < length_length)
- return true;
- }
- max_bytes_available-= length_length;
+ length= safe_net_field_length_ll((uchar **) ptr, end - ptr_save);
+
+ /* cannot even read the length */
+ if (*ptr == NULL)
+ return true;
/* length says there're more data than can fit into the packet */
- if (length > max_bytes_available)
+ if (*ptr + length > end)
return true;
/* impose an artificial length limit of 64k */
@@ -11451,13 +11435,20 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
Cast *passwd to an unsigned char, so that it doesn't extend the sign for
*passwd > 127 and become 2**32-127+ after casting to uint.
*/
- uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ?
- (uchar)(*passwd++) : strlen(passwd);
-
+ uint passwd_len;
+ if (!(thd->client_capabilities & CLIENT_SECURE_CONNECTION))
+ passwd_len= strlen(passwd);
+ else if (!(thd->client_capabilities & CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA))
+ passwd_len= (uchar)(*passwd++);
+ else
+ passwd_len= safe_net_field_length_ll((uchar**)&passwd,
+ net->read_pos + pkt_len - (uchar*)passwd);
+
db= thd->client_capabilities & CLIENT_CONNECT_WITH_DB ?
db + passwd_len + 1 : 0;
- if (passwd + passwd_len + test(db) > (char *)net->read_pos + pkt_len)
+ if (passwd == NULL ||
+ passwd + passwd_len + test(db) > (char *)net->read_pos + pkt_len)
return packet_error;
/* strlen() can't be easily deleted without changing protocol */
diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc
index 34200d46ea2..3f5ae79299d 100644
--- a/sql/sql_admin.cc
+++ b/sql/sql_admin.cc
@@ -993,8 +993,20 @@ send_result_message:
}
}
/* Error path, a admin command failed. */
- trans_commit_stmt(thd);
- trans_commit_implicit(thd);
+ if (thd->transaction_rollback_request)
+ {
+ /*
+ Unlikely, but transaction rollback was requested by one of storage
+ engines (e.g. due to deadlock). Perform it.
+ */
+ if (trans_rollback_stmt(thd) || trans_rollback_implicit(thd))
+ goto err;
+ }
+ else
+ {
+ if (trans_commit_stmt(thd) || trans_commit_implicit(thd))
+ goto err;
+ }
close_thread_tables(thd);
thd->mdl_context.release_transactional_locks();
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 733c79f3e06..5c70c4501f0 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -1,5 +1,5 @@
-/* Copyright (c) 2000, 2012, Oracle and/or its affiliates.
- Copyright (c) 2010, 2011 Monty Program Ab
+/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2010, 2013 Monty Program Ab
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
@@ -313,9 +313,11 @@ OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild)
share->table_name.str);
(*start_list)->in_use= 0;
mysql_mutex_lock(&LOCK_open);
- TABLE_SHARE::TABLE_list::Iterator it(share->tdc.used_tables);
- while (it++)
- ++(*start_list)->in_use;
+ TABLE_SHARE::All_share_tables_list::Iterator it(share->tdc.all_tables);
+ TABLE *table;
+ while ((table= it++))
+ if (table->in_use)
+ ++(*start_list)->in_use;
mysql_mutex_unlock(&LOCK_open);
(*start_list)->locked= 0; /* Obsolete. */
start_list= &(*start_list)->next;
@@ -375,16 +377,19 @@ void free_io_cache(TABLE *table)
void kill_delayed_threads_for_table(TABLE_SHARE *share)
{
- TABLE_SHARE::TABLE_list::Iterator it(share->tdc.used_tables);
+ TABLE_SHARE::All_share_tables_list::Iterator it(share->tdc.all_tables);
TABLE *tab;
mysql_mutex_assert_owner(&LOCK_open);
+ if (!delayed_insert_threads)
+ return;
+
while ((tab= it++))
{
THD *in_use= tab->in_use;
- if ((in_use->system_thread & SYSTEM_THREAD_DELAYED_INSERT) &&
+ if (in_use && (in_use->system_thread & SYSTEM_THREAD_DELAYED_INSERT) &&
! in_use->killed)
{
in_use->killed= KILL_SYSTEM_THREAD;
@@ -1025,7 +1030,7 @@ void close_thread_table(THD *thd, TABLE **table_ptr)
if (! table->needs_reopen())
{
- /* Avoid having MERGE tables with attached children in unused_tables. */
+ /* Avoid having MERGE tables with attached children in table cache. */
table->file->extra(HA_EXTRA_DETACH_CHILDREN);
/* Free memory and reset for next loop. */
free_field_buffers_larger_than(table, MAX_TDC_BLOB_SIZE);
@@ -2273,12 +2278,12 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
{
if (!ha_table_exists(thd, table_list->db, table_list->table_name))
DBUG_RETURN(FALSE);
-
- /* Table exists. Let us try to open it. */
}
else if (table_list->open_strategy == TABLE_LIST::OPEN_STUB)
DBUG_RETURN(FALSE);
+ /* Table exists. Let us try to open it. */
+
if (table_list->i_s_requested_object & OPEN_TABLE_ONLY)
gts_flags= GTS_TABLE;
else if (table_list->i_s_requested_object & OPEN_VIEW_ONLY)
@@ -3201,7 +3206,8 @@ end_free:
/** Open_table_context */
Open_table_context::Open_table_context(THD *thd, uint flags)
- :m_failed_table(NULL),
+ :m_thd(thd),
+ m_failed_table(NULL),
m_start_of_statement_svp(thd->mdl_context.mdl_savepoint()),
m_timeout(flags & MYSQL_LOCK_IGNORE_TIMEOUT ?
LONG_TIMEOUT : thd->variables.lock_wait_timeout),
@@ -3278,6 +3284,7 @@ request_backoff_action(enum_open_table_action action_arg,
if (action_arg != OT_REOPEN_TABLES && m_has_locks)
{
my_error(ER_LOCK_DEADLOCK, MYF(0));
+ mark_transaction_to_rollback(m_thd, true);
return TRUE;
}
/*
@@ -3287,13 +3294,14 @@ request_backoff_action(enum_open_table_action action_arg,
if (table)
{
DBUG_ASSERT(action_arg == OT_DISCOVER || action_arg == OT_REPAIR);
- m_failed_table= (TABLE_LIST*) current_thd->alloc(sizeof(TABLE_LIST));
+ m_failed_table= (TABLE_LIST*) m_thd->alloc(sizeof(TABLE_LIST));
if (m_failed_table == NULL)
return TRUE;
m_failed_table->init_one_table(table->db, table->db_length,
table->table_name,
table->table_name_length,
table->alias, TL_WRITE);
+ m_failed_table->open_strategy= table->open_strategy;
m_failed_table->mdl_request.set_type(MDL_EXCLUSIVE);
}
m_action= action_arg;
@@ -3304,8 +3312,6 @@ request_backoff_action(enum_open_table_action action_arg,
/**
Recover from failed attempt of open table by performing requested action.
- @param thd Thread context
-
@pre This function should be called only with "action" != OT_NO_ACTION
and after having called @sa close_tables_for_reopen().
@@ -3314,8 +3320,7 @@ request_backoff_action(enum_open_table_action action_arg,
*/
bool
-Open_table_context::
-recover_from_failed_open(THD *thd)
+Open_table_context::recover_from_failed_open()
{
bool result= FALSE;
/* Execute the action. */
@@ -3327,36 +3332,46 @@ recover_from_failed_open(THD *thd)
break;
case OT_DISCOVER:
{
- if ((result= lock_table_names(thd, m_failed_table, NULL,
+ if ((result= lock_table_names(m_thd, m_failed_table, NULL,
get_timeout(), 0)))
break;
- tdc_remove_table(thd, TDC_RT_REMOVE_ALL, m_failed_table->db,
+ tdc_remove_table(m_thd, TDC_RT_REMOVE_ALL, m_failed_table->db,
m_failed_table->table_name, FALSE);
- thd->get_stmt_da()->clear_warning_info(thd->query_id);
- thd->clear_error(); // Clear error message
+ m_thd->get_stmt_da()->clear_warning_info(m_thd->query_id);
+ m_thd->clear_error(); // Clear error message
- if ((result=
- !tdc_acquire_share(thd, m_failed_table->db,
- m_failed_table->table_name,
- GTS_TABLE | GTS_FORCE_DISCOVERY | GTS_NOLOCK)))
- break;
+ No_such_table_error_handler no_such_table_handler;
+ bool open_if_exists= m_failed_table->open_strategy == TABLE_LIST::OPEN_IF_EXISTS;
+
+ if (open_if_exists)
+ m_thd->push_internal_handler(&no_such_table_handler);
+
+ result= !tdc_acquire_share(m_thd, m_failed_table->db,
+ m_failed_table->table_name,
+ GTS_TABLE | GTS_FORCE_DISCOVERY | GTS_NOLOCK);
+ if (open_if_exists)
+ {
+ m_thd->pop_internal_handler();
+ if (result && no_such_table_handler.safely_trapped_errors())
+ result= FALSE;
+ }
- thd->mdl_context.release_transactional_locks();
+ m_thd->mdl_context.release_transactional_locks();
break;
}
case OT_REPAIR:
{
- if ((result= lock_table_names(thd, m_failed_table, NULL,
+ if ((result= lock_table_names(m_thd, m_failed_table, NULL,
get_timeout(), 0)))
break;
- tdc_remove_table(thd, TDC_RT_REMOVE_ALL, m_failed_table->db,
+ tdc_remove_table(m_thd, TDC_RT_REMOVE_ALL, m_failed_table->db,
m_failed_table->table_name, FALSE);
- result= auto_repair_table(thd, m_failed_table);
- thd->mdl_context.release_transactional_locks();
+ result= auto_repair_table(m_thd, m_failed_table);
+ m_thd->mdl_context.release_transactional_locks();
break;
}
default:
@@ -4353,7 +4368,7 @@ restart:
TABLE_LIST element. Altough currently this assumption is valid
it may change in future.
*/
- if (ot_ctx.recover_from_failed_open(thd))
+ if (ot_ctx.recover_from_failed_open())
goto err;
/* Re-open temporary tables after close_tables_for_reopen(). */
@@ -4410,7 +4425,7 @@ restart:
{
close_tables_for_reopen(thd, start,
ot_ctx.start_of_statement_svp());
- if (ot_ctx.recover_from_failed_open(thd))
+ if (ot_ctx.recover_from_failed_open())
goto err;
/* Re-open temporary tables after close_tables_for_reopen(). */
@@ -4881,7 +4896,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type,
*/
thd->mdl_context.rollback_to_savepoint(ot_ctx.start_of_statement_svp());
table_list->mdl_request.ticket= 0;
- if (ot_ctx.recover_from_failed_open(thd))
+ if (ot_ctx.recover_from_failed_open())
break;
}
@@ -6208,9 +6223,9 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list,
else
{
if (thd->mark_used_columns == MARK_COLUMNS_READ)
- it->walk(&Item::register_field_in_read_map, 1, (uchar *) 0);
+ it->walk(&Item::register_field_in_read_map, 0, (uchar *) 0);
else
- it->walk(&Item::register_field_in_write_map, 1, (uchar *) 0);
+ it->walk(&Item::register_field_in_write_map, 0, (uchar *) 0);
}
}
else
diff --git a/sql/sql_base.h b/sql/sql_base.h
index 09da848e77d..3e633fad084 100644
--- a/sql/sql_base.h
+++ b/sql/sql_base.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
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
@@ -504,7 +504,7 @@ public:
};
Open_table_context(THD *thd, uint flags);
- bool recover_from_failed_open(THD *thd);
+ bool recover_from_failed_open();
bool request_backoff_action(enum_open_table_action action_arg,
TABLE_LIST *table);
@@ -544,6 +544,8 @@ public:
}
private:
+ /* THD for which tables are opened. */
+ THD *m_thd;
/**
For OT_DISCOVER and OT_REPAIR actions, the table list element for
the table which definition should be re-discovered or which
diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc
index 97034878eef..5307f4f01f8 100644
--- a/sql/sql_cache.cc
+++ b/sql/sql_cache.cc
@@ -1,5 +1,5 @@
-/* Copyright (c) 2000, 2011, Oracle and/or its affiliates.
- Copyright (c) 2010, 2012, Monty Program Ab.
+/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2010, 2013, Monty Program Ab
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
@@ -2090,7 +2090,12 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
*/
thd->query_cache_is_applicable= 0; // Query can't be cached
}
- /* End the statement transaction potentially started by engine. */
+ /*
+ End the statement transaction potentially started by engine.
+ Currently our engines do not request rollback from callbacks.
+ If this is going to change code needs to be reworked.
+ */
+ DBUG_ASSERT(! thd->transaction_rollback_request);
trans_rollback_stmt(thd);
goto err_unlock; // Parse query
}
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 40455a0f474..b1b8a017992 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -76,8 +76,6 @@
char internal_table_name[2]= "*";
char empty_c_string[1]= {0}; /* used for not defined db */
-LEX_STRING EMPTY_STR= { (char *) "", 0 };
-
const char * const THD::DEFAULT_WHERE= "field list";
/****************************************************************************
@@ -697,10 +695,29 @@ int thd_tx_is_read_only(const THD *thd)
return (int) thd->tx_read_only;
}
+
extern "C"
-void thd_inc_row_count(THD *thd)
-{
- thd->get_stmt_da()->inc_current_row_for_warning();
+{ /* Functions for thd_error_context_service */
+
+ const char *thd_get_error_message(const THD *thd)
+ {
+ return thd->get_stmt_da()->message();
+ }
+
+ uint thd_get_error_number(const THD *thd)
+ {
+ return thd->get_stmt_da()->sql_errno();
+ }
+
+ ulong thd_get_error_row(const THD *thd)
+ {
+ return thd->get_stmt_da()->current_row_for_warning();
+ }
+
+ void thd_inc_error_row(THD *thd)
+ {
+ thd->get_stmt_da()->inc_current_row_for_warning();
+ }
}
@@ -723,8 +740,9 @@ void thd_inc_row_count(THD *thd)
*/
extern "C"
-char *thd_security_context(THD *thd, char *buffer, unsigned int length,
- unsigned int max_query_len)
+char *thd_get_error_context_description(THD *thd, char *buffer,
+ unsigned int length,
+ unsigned int max_query_len)
{
String str(buffer, length, &my_charset_latin1);
const Security_context *sctx= &thd->main_security_ctx;
@@ -968,6 +986,21 @@ wsrep_trx_is_aborting(void *thd_ptr)
}
#endif
+#if MARIA_PLUGIN_INTERFACE_VERSION < 0x0200
+/**
+ TODO: This function is for API compatibility, remove it eventually.
+ All engines should switch to use thd_get_error_context_description()
+ plugin service function.
+*/
+extern "C"
+char *thd_security_context(THD *thd,
+ char *buffer, unsigned int length,
+ unsigned int max_query_len)
+{
+ return thd_get_error_context_description(thd, buffer, length, max_query_len);
+}
+#endif
+
/**
Implementation of Drop_table_error_handler::handle_condition().
The reason in having this implementation is to silence technical low-level
@@ -1829,9 +1862,7 @@ THD::~THD()
{
DBUG_PRINT("error", ("memory_used: %lld", status_var.memory_used));
SAFEMALLOC_REPORT_MEMORY(my_thread_dbug_id());
-#ifdef ENABLE_BEFORE_END_OF_MERGE_QQ
DBUG_ASSERT(status_var.memory_used == 0); // Ensure everything is freed
-#endif
}
set_current_thd(orig_thd);
@@ -4408,7 +4439,6 @@ extern "C" unsigned long thd_get_thread_id(const MYSQL_THD thd)
return((unsigned long)thd->thread_id);
}
-
/**
Check if THD socket is still connected.
*/
@@ -4494,6 +4524,7 @@ extern "C" enum durability_properties thd_get_durability_property(const MYSQL_TH
}
/** Get the auto_increment_offset auto_increment_increment.
+Exposed by thd_autoinc_service.
Needed by InnoDB.
@param thd Thread object
@param off auto_increment_offset
@@ -5616,7 +5647,7 @@ THD::binlog_prepare_pending_rows_event(TABLE* table, uint32 serv_id,
DBUG_ASSERT(table->s->table_map_id != ~0UL);
/* Fetch the type code for the RowsEventT template parameter */
- int const type_code= RowsEventT::TYPE_CODE;
+ int const general_type_code= RowsEventT::TYPE_CODE;
/*
There is no good place to set up the transactional data, so we
@@ -5643,7 +5674,7 @@ THD::binlog_prepare_pending_rows_event(TABLE* table, uint32 serv_id,
if (!pending ||
pending->server_id != serv_id ||
pending->get_table_id() != table->s->table_map_id ||
- pending->get_type_code() != type_code ||
+ pending->get_general_type_code() != general_type_code ||
pending->get_data_size() + needed > opt_binlog_rows_event_max_size ||
pending->get_width() != colcnt ||
!bitmap_cmp(pending->get_cols(), cols))
diff --git a/sql/sql_class.h b/sql/sql_class.h
index cfce0f81621..6144dd42a0a 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -1,5 +1,4 @@
-/*
- Copyright (c) 2000, 2012, Oracle and/or its affiliates.
+/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
Copyright (c) 2009, 2013, Monty Program Ab.
This program is free software; you can redistribute it and/or modify
@@ -475,6 +474,7 @@ enum killed_type
#include "sql_lex.h" /* Must be here */
+extern LEX_STRING sql_statement_names[(uint) SQLCOM_END + 1];
class Delayed_insert;
class select_result;
class Time_zone;
@@ -3353,7 +3353,11 @@ public:
*/
bool set_db(const char *new_db, size_t new_db_len)
{
- bool result;
+ /*
+ Acquiring mutex LOCK_thd_data as we either free the memory allocated
+ for the database and reallocating the memory for the new db or memcpy
+ the new_db to the db.
+ */
mysql_mutex_lock(&LOCK_thd_data);
/* Do not reallocate memory if current chunk is big enough. */
if (db && new_db && db_length >= new_db_len)
@@ -3367,7 +3371,7 @@ public:
db= NULL;
}
db_length= db ? new_db_len : 0;
- result= new_db && !db;
+ bool result= new_db && !db;
mysql_mutex_unlock(&LOCK_thd_data);
#ifdef HAVE_PSI_THREAD_INTERFACE
if (result)
diff --git a/sql/sql_db.cc b/sql/sql_db.cc
index 8db305e45c5..8068901ebec 100644
--- a/sql/sql_db.cc
+++ b/sql/sql_db.cc
@@ -1,5 +1,5 @@
/*
- Copyright (c) 2000, 2011, Oracle and/or its affiliates.
+ Copyright (c) 2000, 2013, Oracle and/or its affiliates.
Copyright (c) 2009, 2013, Monty Program Ab.
This program is free software; you can redistribute it and/or modify
@@ -1254,8 +1254,7 @@ static void mysql_change_db_impl(THD *thd,
we just call THD::reset_db(). Since THD::reset_db() does not releases
the previous database name, we should do it explicitly.
*/
- my_free(thd->db);
-
+ thd->set_db(NULL, 0);
thd->reset_db(new_db_name->str, new_db_name->length);
}
diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc
index 4187327d622..812a5dc1461 100644
--- a/sql/sql_handler.cc
+++ b/sql/sql_handler.cc
@@ -791,7 +791,10 @@ retry:
/*
Always close statement transaction explicitly,
so that the engine doesn't have to count locks.
+ There should be no need to perform transaction
+ rollback due to deadlock.
*/
+ DBUG_ASSERT(! thd->transaction_rollback_request);
trans_rollback_stmt(thd);
mysql_ha_close_table(handler);
if (thd->stmt_arena->is_stmt_execute())
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 74a58bc7d60..e5b8090dfbc 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -197,6 +197,7 @@ init_lex_with_single_table(THD *thd, TABLE *table, LEX *lex)
table->map= 1; //To ensure correct calculation of const item
table->get_fields_in_item_tree= TRUE;
table_list->table= table;
+ table_list->cacheable_table= false;
return FALSE;
}
@@ -1522,19 +1523,14 @@ int lex_one_token(void *arg, THD *thd)
lip->save_in_comment_state();
- if (lip->yyPeekn(2) == 'M' && lip->yyPeekn(3) == '!')
- {
- /* Skip MariaDB unique marker */
- lip->set_echo(FALSE);
- lip->yySkip();
- /* The following if will be true */
- }
- if (lip->yyPeekn(2) == '!')
+ if (lip->yyPeekn(2) == '!' ||
+ (lip->yyPeekn(2) == 'M' && lip->yyPeekn(3) == '!'))
{
+ bool maria_comment_syntax= lip->yyPeekn(2) == 'M';
lip->in_comment= DISCARD_COMMENT;
/* Accept '/' '*' '!', but do not keep this marker. */
lip->set_echo(FALSE);
- lip->yySkipn(3);
+ lip->yySkipn(maria_comment_syntax ? 4 : 3);
/*
The special comment format is very strict:
@@ -1564,7 +1560,14 @@ int lex_one_token(void *arg, THD *thd)
version= (ulong) my_strtoll10(lip->get_ptr(), &end_ptr, &error);
- if (version <= MYSQL_VERSION_ID)
+ /*
+ MySQL-5.7 has new features and might have new SQL syntax that
+ MariaDB-10.0 does not understand. Ignore all versioned comments
+ with MySQL versions in the range 50700-999999, but
+ do not ignore MariaDB specific comments for the same versions.
+ */
+ if (version <= MYSQL_VERSION_ID &&
+ (version < 50700 || version > 99999 || maria_comment_syntax))
{
/* Accept 'M' 'm' 'm' 'd' 'd' */
lip->yySkipn(length);
@@ -3875,7 +3878,7 @@ void SELECT_LEX::update_used_tables()
}
for (ORDER *order= group_list.first; order; order= order->next)
(*order->item)->update_used_tables();
- if (!master_unit()->is_union())
+ if (!master_unit()->is_union() || master_unit()->global_parameters != this)
{
for (ORDER *order= order_list.first; order; order= order->next)
(*order->item)->update_used_tables();
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index f892f1bf3c7..479fa81edd7 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -636,7 +636,7 @@ public:
void print(String *str, enum_query_type query_type);
bool add_fake_select_lex(THD *thd);
- void init_prepare_fake_select_lex(THD *thd);
+ void init_prepare_fake_select_lex(THD *thd, bool first_execution);
inline bool is_prepared() { return prepared; }
bool change_result(select_result_interceptor *result,
select_result_interceptor *old_result);
diff --git a/sql/sql_manager.cc b/sql/sql_manager.cc
index 7ef320b30d2..0771f569f35 100644
--- a/sql/sql_manager.cc
+++ b/sql/sql_manager.cc
@@ -140,9 +140,12 @@ void start_handle_manager()
if (flush_time && flush_time != ~(ulong) 0L)
{
pthread_t hThread;
- if (mysql_thread_create(key_thread_handle_manager,
- &hThread, &connection_attrib, handle_manager, 0))
- sql_print_warning("Can't create handle_manager thread");
+ int error;
+ if ((error= mysql_thread_create(key_thread_handle_manager,
+ &hThread, &connection_attrib,
+ handle_manager, 0)))
+ sql_print_warning("Can't create handle_manager thread (errno= %d)",
+ error);
}
DBUG_VOID_RETURN;
}
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 62a12241c5a..d6ea0c2368a 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -1,4 +1,4 @@
-/* Copyright (c) 2000, 2012, Oracle and/or its affiliates.
+/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
Copyright (c) 2008, 2013, Monty Program Ab
This program is free software; you can redistribute it and/or modify
@@ -1646,6 +1646,18 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
close_thread_tables(thd);
thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
+ if (thd->transaction_rollback_request)
+ {
+ /*
+ Transaction rollback was requested since MDL deadlock was
+ discovered while trying to open tables. Rollback transaction
+ in all storage engines including binary log and release all
+ locks.
+ */
+ trans_rollback_implicit(thd);
+ thd->mdl_context.release_transactional_locks();
+ }
+
thd->cleanup_after_query();
break;
}
@@ -1964,7 +1976,7 @@ void log_slow_statement(THD *thd)
statement in a trigger or stored function
*/
if (unlikely(thd->in_sub_stmt))
- DBUG_VOID_RETURN; // Don't set time for sub stmt
+ goto end; // Don't set time for sub stmt
/* Follow the slow log filter configuration. */
@@ -1972,8 +1984,7 @@ void log_slow_statement(THD *thd)
(thd->variables.log_slow_filter
&& !(thd->variables.log_slow_filter & thd->query_plan_flags)))
{
- delete_explain_query(thd->lex);
- DBUG_VOID_RETURN;
+ goto end;
}
if (((thd->server_status & SERVER_QUERY_WAS_SLOW) ||
@@ -1990,13 +2001,14 @@ void log_slow_statement(THD *thd)
*/
if (thd->variables.log_slow_rate_limit > 1 &&
(global_query_id % thd->variables.log_slow_rate_limit) != 0)
- DBUG_VOID_RETURN;
+ goto end;
THD_STAGE_INFO(thd, stage_logging_slow_query);
slow_log_print(thd, thd->query(), thd->query_length(),
thd->utime_after_query);
}
+end:
delete_explain_query(thd->lex);
DBUG_VOID_RETURN;
}
@@ -2362,7 +2374,7 @@ err:
can free its locks if LOCK TABLES locked some tables before finding
that it can't lock a table in its list
*/
- trans_commit_implicit(thd);
+ trans_rollback(thd);
/* Close tables and release metadata locks. */
close_thread_tables(thd);
DBUG_ASSERT(!thd->locked_tables_mode);
@@ -2423,6 +2435,13 @@ mysql_execute_command(THD *thd)
DBUG_ASSERT(thd->transaction.stmt.is_empty() || thd->in_sub_stmt);
/*
+ Each statement or replication event which might produce deadlock
+ should handle transaction rollback on its own. So by the start of
+ the next statement transaction rollback request should be fulfilled
+ already.
+ */
+ DBUG_ASSERT(! thd->transaction_rollback_request || thd->in_sub_stmt);
+ /*
In many cases first table of main SELECT_LEX have special meaning =>
check that it is first table in global list and relink it first in
queries_tables list if it is necessary (we need such relinking only
@@ -2685,8 +2704,8 @@ mysql_execute_command(THD *thd)
or triggers as all such statements prohibited there.
*/
DBUG_ASSERT(! thd->in_sub_stmt);
- /* Commit or rollback the statement transaction. */
- thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd);
+ /* Statement transaction still should not be started. */
+ DBUG_ASSERT(thd->transaction.stmt.is_empty());
/* Commit the normal transaction if one is active. */
if (trans_commit_implicit(thd))
{
@@ -5518,7 +5537,17 @@ finish:
thd->set_binlog_format(orig_binlog_format,
orig_current_stmt_binlog_format);
- if (stmt_causes_implicit_commit(thd, CF_IMPLICIT_COMMIT_END))
+ if (! thd->in_sub_stmt && thd->transaction_rollback_request)
+ {
+ /*
+ We are not in sub-statement and transaction rollback was requested by
+ one of storage engines (e.g. due to deadlock). Rollback transaction in
+ all storage engines including binary log.
+ */
+ trans_rollback_implicit(thd);
+ thd->mdl_context.release_transactional_locks();
+ }
+ else if (stmt_causes_implicit_commit(thd, CF_IMPLICIT_COMMIT_END))
{
/* No transaction control allowed in sub-statements. */
DBUG_ASSERT(! thd->in_sub_stmt);
diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc
index 87bfcfcdfc2..3e19a33ee32 100644
--- a/sql/sql_partition.cc
+++ b/sql/sql_partition.cc
@@ -93,7 +93,9 @@ const LEX_STRING partition_keywords[]=
{ C_STRING_WITH_LEN("KEY") },
{ C_STRING_WITH_LEN("MAXVALUE") },
{ C_STRING_WITH_LEN("LINEAR ") },
- { C_STRING_WITH_LEN(" COLUMNS") }
+ { C_STRING_WITH_LEN(" COLUMNS") },
+ { C_STRING_WITH_LEN("ALGORITHM") }
+
};
static const char *part_str= "PARTITION";
static const char *sub_str= "SUB";
@@ -284,7 +286,7 @@ bool partition_default_handling(TABLE *table, partition_info *part_info,
}
}
part_info->set_up_defaults_for_partitioning(table->file,
- (ulonglong)0, (uint)0);
+ NULL, 0U);
DBUG_RETURN(FALSE);
}
@@ -317,7 +319,7 @@ int get_parts_for_update(const uchar *old_data, uchar *new_data,
longlong old_func_value;
DBUG_ENTER("get_parts_for_update");
- DBUG_ASSERT(new_data == rec0);
+ DBUG_ASSERT(new_data == rec0); // table->record[0]
set_field_ptr(part_field_array, old_data, rec0);
error= part_info->get_partition_id(part_info, old_part_id,
&old_func_value);
@@ -475,12 +477,12 @@ static bool set_up_field_array(TABLE *table,
}
if (num_fields > MAX_REF_PARTS)
{
- char *ptr;
+ char *err_str;
if (is_sub_part)
- ptr= (char*)"subpartition function";
+ err_str= (char*)"subpartition function";
else
- ptr= (char*)"partition function";
- my_error(ER_TOO_MANY_PARTITION_FUNC_FIELDS_ERROR, MYF(0), ptr);
+ err_str= (char*)"partition function";
+ my_error(ER_TOO_MANY_PARTITION_FUNC_FIELDS_ERROR, MYF(0), err_str);
DBUG_RETURN(TRUE);
}
if (num_fields == 0)
@@ -2378,6 +2380,58 @@ end:
return err;
}
+
+/**
+ Add 'KEY' word, with optional 'ALGORTIHM = N'.
+
+ @param fptr File to write to.
+ @param part_info partition_info holding the used key_algorithm
+ @param current_comment_start NULL, or comment string encapsulating the
+ PARTITION BY clause.
+
+ @return Operation status.
+ @retval 0 Success
+ @retval != 0 Failure
+*/
+
+static int add_key_with_algorithm(File fptr, partition_info *part_info,
+ const char *current_comment_start)
+{
+ int err= 0;
+ err+= add_part_key_word(fptr, partition_keywords[PKW_KEY].str);
+
+ /*
+ current_comment_start is given when called from SHOW CREATE TABLE,
+ Then only add ALGORITHM = 1, not the default 2 or non-set 0!
+ For .frm current_comment_start is NULL, then add ALGORITHM if != 0.
+ */
+ if (part_info->key_algorithm == partition_info::KEY_ALGORITHM_51 || // SHOW
+ (!current_comment_start && // .frm
+ (part_info->key_algorithm != partition_info::KEY_ALGORITHM_NONE)))
+ {
+ /* If we already are within a comment, end that comment first. */
+ if (current_comment_start)
+ err+= add_string(fptr, "*/ ");
+ err+= add_string(fptr, "/*!50611 ");
+ err+= add_part_key_word(fptr, partition_keywords[PKW_ALGORITHM].str);
+ err+= add_equal(fptr);
+ err+= add_space(fptr);
+ err+= add_int(fptr, part_info->key_algorithm);
+ err+= add_space(fptr);
+ err+= add_string(fptr, "*/ ");
+ if (current_comment_start)
+ {
+ /* Skip new line. */
+ if (current_comment_start[0] == '\n')
+ current_comment_start++;
+ err+= add_string(fptr, current_comment_start);
+ err+= add_space(fptr);
+ }
+ }
+ return err;
+}
+
+
/*
Generate the partition syntax from the partition data structure.
Useful for support of generating defaults, SHOW CREATE TABLES
@@ -2422,7 +2476,8 @@ char *generate_partition_syntax(partition_info *part_info,
bool use_sql_alloc,
bool show_partition_options,
HA_CREATE_INFO *create_info,
- Alter_info *alter_info)
+ Alter_info *alter_info,
+ const char *current_comment_start)
{
uint i,j, tot_num_parts, num_subparts;
partition_element *part_elem;
@@ -2456,7 +2511,8 @@ char *generate_partition_syntax(partition_info *part_info,
err+= add_string(fptr, partition_keywords[PKW_LINEAR].str);
if (part_info->list_of_part_fields)
{
- err+= add_part_key_word(fptr, partition_keywords[PKW_KEY].str);
+ err+= add_key_with_algorithm(fptr, part_info,
+ current_comment_start);
err+= add_part_field_list(fptr, part_info->part_field_list);
}
else
@@ -2496,8 +2552,9 @@ char *generate_partition_syntax(partition_info *part_info,
err+= add_string(fptr, partition_keywords[PKW_LINEAR].str);
if (part_info->list_of_subpart_fields)
{
- add_part_key_word(fptr, partition_keywords[PKW_KEY].str);
- add_part_field_list(fptr, part_info->subpart_field_list);
+ err+= add_key_with_algorithm(fptr, part_info,
+ current_comment_start);
+ err+= add_part_field_list(fptr, part_info->subpart_field_list);
}
else
err+= add_part_key_word(fptr, partition_keywords[PKW_HASH].str);
@@ -5619,12 +5676,25 @@ the generated partition syntax in a correct manner.
*/
if (part_info != tab_part_info)
{
- DBUG_PRINT("info", ("partition changed"));
- *partition_changed= TRUE;
- if (thd->work_part_info->fix_parser_data(thd))
+ if (part_info->fix_parser_data(thd))
{
goto err;
}
+ /*
+ Compare the old and new part_info. If only key_algorithm
+ change is done, don't consider it as changed partitioning (to avoid
+ rebuild). This is to handle KEY (numeric_cols) partitioned tables
+ created in 5.1. For more info, see bug#14521864.
+ */
+ if (alter_info->flags != Alter_info::ALTER_PARTITION ||
+ !table->part_info ||
+ alter_info->requested_algorithm !=
+ Alter_info::ALTER_TABLE_ALGORITHM_INPLACE ||
+ !table->part_info->has_same_partitioning(part_info))
+ {
+ DBUG_PRINT("info", ("partition changed"));
+ *partition_changed= true;
+ }
}
/*
Set up partition default_engine_type either from the create_info
diff --git a/sql/sql_partition.h b/sql/sql_partition.h
index 7f39ddd7a3f..5da132661c9 100644
--- a/sql/sql_partition.h
+++ b/sql/sql_partition.h
@@ -263,7 +263,8 @@ char *generate_partition_syntax(partition_info *part_info,
uint *buf_length, bool use_sql_alloc,
bool show_partition_options,
HA_CREATE_INFO *create_info,
- Alter_info *alter_info);
+ Alter_info *alter_info,
+ const char *current_comment_start);
bool verify_data_with_partition(TABLE *table, TABLE *part_table,
uint32 part_id);
bool compare_partition_options(HA_CREATE_INFO *table_create_info,
diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc
index 43abcd13fce..8508a0d7c60 100644
--- a/sql/sql_plugin.cc
+++ b/sql/sql_plugin.cc
@@ -257,15 +257,6 @@ class sys_var_pluginvar: public sys_var
public:
struct st_plugin_int *plugin;
struct st_mysql_sys_var *plugin_var;
- /**
- variable name from whatever is hard-coded in the plugin source
- and doesn't have pluginname- prefix is replaced by an allocated name
- with a plugin prefix. When plugin is uninstalled we need to restore the
- pointer to point to the hard-coded value, because plugin may be
- installed/uninstalled many times without reloading the shared object.
- */
- const char *orig_pluginvar_name;
-
static void *operator new(size_t size, MEM_ROOT *mem_root)
{ return (void*) alloc_root(mem_root, size); }
static void operator delete(void *ptr_arg,size_t size)
@@ -278,7 +269,7 @@ public:
(plugin_var_arg->flags & PLUGIN_VAR_READONLY ? READONLY : 0),
0, -1, NO_ARG, pluginvar_show_type(plugin_var_arg), 0, 0,
VARIABLE_NOT_IN_BINLOG, NULL, NULL, NULL),
- plugin_var(plugin_var_arg), orig_pluginvar_name(plugin_var_arg->name)
+ plugin_var(plugin_var_arg)
{ plugin_var->name= name_arg; }
sys_var_pluginvar *cast_pluginvar() { return this; }
bool check_update_type(Item_result type);
@@ -308,7 +299,7 @@ static bool register_builtin(struct st_maria_plugin *, struct st_plugin_int *,
static void unlock_variables(THD *thd, struct system_variables *vars);
static void cleanup_variables(THD *thd, struct system_variables *vars);
static void plugin_vars_free_values(sys_var *vars);
-static void restore_pluginvar_names(sys_var *first);
+static void restore_ptr_backup(uint n, st_ptr_backup *backup);
static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref plugin);
static void intern_plugin_unlock(LEX *lex, plugin_ref plugin);
static void reap_plugins(void);
@@ -473,9 +464,16 @@ static st_plugin_dl *plugin_dl_insert_or_reuse(struct st_plugin_dl *plugin_dl)
#endif /* HAVE_DLOPEN */
-static inline void free_plugin_mem(struct st_plugin_dl *p)
+static void free_plugin_mem(struct st_plugin_dl *p)
{
#ifdef HAVE_DLOPEN
+ if (p->ptr_backup)
+ {
+ DBUG_ASSERT(p->nbackups);
+ DBUG_ASSERT(p->handle);
+ restore_ptr_backup(p->nbackups, p->ptr_backup);
+ my_free(p->ptr_backup);
+ }
if (p->handle)
dlclose(p->handle);
#endif
@@ -706,6 +704,7 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report)
uint plugin_dir_len, dummy_errors, dlpathlen, i;
struct st_plugin_dl *tmp= 0, plugin_dl;
void *sym;
+ st_ptr_backup tmp_backup[array_elements(list_of_services)];
DBUG_ENTER("plugin_dl_add");
DBUG_PRINT("enter", ("dl->str: '%s', dl->length: %d",
dl->str, (int) dl->length));
@@ -772,7 +771,8 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report)
{
if ((sym= dlsym(plugin_dl.handle, list_of_services[i].name)))
{
- uint ver= (uint)(intptr)*(void**)sym;
+ void **ptr= (void **)sym;
+ uint ver= (uint)(intptr)*ptr;
if (ver > list_of_services[i].version ||
(ver >> 8) < (list_of_services[i].version >> 8))
{
@@ -783,8 +783,22 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report)
report_error(report, ER_CANT_OPEN_LIBRARY, dlpath, ENOEXEC, buf);
goto ret;
}
- *(void**)sym= list_of_services[i].service;
+ tmp_backup[plugin_dl.nbackups++].save(ptr);
+ *ptr= list_of_services[i].service;
+ }
+ }
+
+ if (plugin_dl.nbackups)
+ {
+ size_t bytes= plugin_dl.nbackups * sizeof(plugin_dl.ptr_backup[0]);
+ plugin_dl.ptr_backup= (st_ptr_backup *)my_malloc(bytes, MYF(0));
+ if (!plugin_dl.ptr_backup)
+ {
+ restore_ptr_backup(plugin_dl.nbackups, tmp_backup);
+ report_error(report, ER_OUTOFMEMORY, bytes);
+ goto ret;
}
+ memcpy(plugin_dl.ptr_backup, tmp_backup, bytes);
}
/* Duplicate and convert dll name */
@@ -1098,7 +1112,6 @@ static bool plugin_add(MEM_ROOT *tmp_root,
if (!(tmp_plugin_ptr= plugin_insert_or_reuse(&tmp)))
{
mysql_del_sys_var_chain(tmp.system_vars);
- restore_pluginvar_names(tmp.system_vars);
goto err;
}
plugin_array_version++;
@@ -1115,6 +1128,8 @@ static bool plugin_add(MEM_ROOT *tmp_root,
err:
errs++;
+ if (tmp.nbackups)
+ restore_ptr_backup(tmp.nbackups, tmp.ptr_backup);
if (name->str)
break;
}
@@ -1193,7 +1208,7 @@ static void plugin_del(struct st_plugin_int *plugin)
mysql_rwlock_wrlock(&LOCK_system_variables_hash);
mysql_del_sys_var_chain(plugin->system_vars);
mysql_rwlock_unlock(&LOCK_system_variables_hash);
- restore_pluginvar_names(plugin->system_vars);
+ restore_ptr_backup(plugin->nbackups, plugin->ptr_backup);
plugin_vars_free_values(plugin->system_vars);
my_hash_delete(&plugin_hash[plugin->plugin->type], (uchar*)plugin);
plugin_dl_del(plugin->plugin_dl);
@@ -2914,16 +2929,6 @@ static st_bookmark *register_var(const char *plugin, const char *name,
return result;
}
-static void restore_pluginvar_names(sys_var *first)
-{
- for (sys_var *var= first; var; var= var->next)
- {
- sys_var_pluginvar *pv= var->cast_pluginvar();
- pv->plugin_var->name= pv->orig_pluginvar_name;
- }
-}
-
-
/*
returns a pointer to the memory which holds the thd-local variable or
a pointer to the global variable if thd==null.
@@ -3805,7 +3810,7 @@ static my_option *construct_help_options(MEM_ROOT *mem_root,
to get the correct (not double-prefixed) help text.
We won't need @@sysvars anymore and don't care about their proper names.
*/
- restore_pluginvar_names(p->system_vars);
+ restore_ptr_backup(p->nbackups, p->ptr_backup);
if (construct_options(mem_root, p, opts))
DBUG_RETURN(NULL);
@@ -3850,6 +3855,7 @@ static int test_plugin_options(MEM_ROOT *tmp_root, struct st_plugin_int *tmp,
sys_var *v __attribute__((unused));
struct st_bookmark *var;
uint len, count= EXTRA_OPTIONS;
+ st_ptr_backup *tmp_backup= 0;
DBUG_ENTER("test_plugin_options");
DBUG_ASSERT(tmp->plugin && tmp->name.str);
@@ -3922,59 +3928,86 @@ static int test_plugin_options(MEM_ROOT *tmp_root, struct st_plugin_int *tmp,
plugin_name= tmp->name;
error= 1;
- for (opt= tmp->plugin->system_vars; opt && *opt; opt++)
+
+ if (tmp->plugin->system_vars)
{
- st_mysql_sys_var *o= *opt;
+ for (len=0, opt= tmp->plugin->system_vars; *opt; len++, opt++) /* no-op */;
+ tmp_backup= (st_ptr_backup *)my_alloca(len * sizeof(tmp_backup[0]));
+ DBUG_ASSERT(tmp->nbackups == 0);
+ DBUG_ASSERT(tmp->ptr_backup == 0);
- /*
- PLUGIN_VAR_STR command-line options without PLUGIN_VAR_MEMALLOC, point
- directly to values in the argv[] array. For plugins started at the
- server startup, argv[] array is allocated with load_defaults(), and
- freed when the server is shut down. But for plugins loaded with
- INSTALL PLUGIN, the memory allocated with load_defaults() is freed with
- freed() at the end of mysql_install_plugin(). Which means we cannot
- allow any pointers into that area.
- Thus, for all plugins loaded after the server was started,
- we copy string values to a plugin's memroot.
- */
- if (mysqld_server_started &&
- ((o->flags & (PLUGIN_VAR_STR | PLUGIN_VAR_NOCMDOPT |
- PLUGIN_VAR_MEMALLOC)) == PLUGIN_VAR_STR))
+ for (opt= tmp->plugin->system_vars; *opt; opt++)
{
- sysvar_str_t* str= (sysvar_str_t *)o;
- if (*str->value)
- *str->value= strdup_root(mem_root, *str->value);
- }
+ st_mysql_sys_var *o= *opt;
- if (o->flags & PLUGIN_VAR_NOSYSVAR)
- continue;
- if ((var= find_bookmark(plugin_name.str, o->name, o->flags)))
- v= new (mem_root) sys_var_pluginvar(&chain, var->key + 1, o);
- else
+ /*
+ PLUGIN_VAR_STR command-line options without PLUGIN_VAR_MEMALLOC, point
+ directly to values in the argv[] array. For plugins started at the
+ server startup, argv[] array is allocated with load_defaults(), and
+ freed when the server is shut down. But for plugins loaded with
+ INSTALL PLUGIN, the memory allocated with load_defaults() is freed with
+ freed() at the end of mysql_install_plugin(). Which means we cannot
+ allow any pointers into that area.
+ Thus, for all plugins loaded after the server was started,
+ we copy string values to a plugin's memroot.
+ */
+ if (mysqld_server_started &&
+ ((o->flags & (PLUGIN_VAR_STR | PLUGIN_VAR_NOCMDOPT |
+ PLUGIN_VAR_MEMALLOC)) == PLUGIN_VAR_STR))
+ {
+ sysvar_str_t* str= (sysvar_str_t *)o;
+ if (*str->value)
+ *str->value= strdup_root(mem_root, *str->value);
+ }
+
+ if (o->flags & PLUGIN_VAR_NOSYSVAR)
+ continue;
+ tmp_backup[tmp->nbackups++].save(&o->name);
+ if ((var= find_bookmark(plugin_name.str, o->name, o->flags)))
+ v= new (mem_root) sys_var_pluginvar(&chain, var->key + 1, o);
+ else
+ {
+ len= plugin_name.length + strlen(o->name) + 2;
+ varname= (char*) alloc_root(mem_root, len);
+ strxmov(varname, plugin_name.str, "-", o->name, NullS);
+ my_casedn_str(&my_charset_latin1, varname);
+ convert_dash_to_underscore(varname, len-1);
+ v= new (mem_root) sys_var_pluginvar(&chain, varname, o);
+ }
+ DBUG_ASSERT(v); /* check that an object was actually constructed */
+ } /* end for */
+
+ if (tmp->nbackups)
{
- len= plugin_name.length + strlen(o->name) + 2;
- varname= (char*) alloc_root(mem_root, len);
- strxmov(varname, plugin_name.str, "-", o->name, NullS);
- my_casedn_str(&my_charset_latin1, varname);
- convert_dash_to_underscore(varname, len-1);
- v= new (mem_root) sys_var_pluginvar(&chain, varname, o);
+ size_t bytes= tmp->nbackups * sizeof(tmp->ptr_backup[0]);
+ tmp->ptr_backup= (st_ptr_backup *)alloc_root(mem_root, bytes);
+ if (!tmp->ptr_backup)
+ {
+ restore_ptr_backup(tmp->nbackups, tmp_backup);
+ goto err;
+ }
+ memcpy(tmp->ptr_backup, tmp_backup, bytes);
}
- DBUG_ASSERT(v); /* check that an object was actually constructed */
- } /* end for */
- if (chain.first)
- {
- chain.last->next = NULL;
- if (mysql_add_sys_var_chain(chain.first))
+
+ if (chain.first)
{
- sql_print_error("Plugin '%s' has conflicting system variables",
- tmp->name.str);
- goto err;
+ chain.last->next = NULL;
+ if (mysql_add_sys_var_chain(chain.first))
+ {
+ sql_print_error("Plugin '%s' has conflicting system variables",
+ tmp->name.str);
+ goto err;
+ }
+ tmp->system_vars= chain.first;
}
- tmp->system_vars= chain.first;
+ my_afree(tmp_backup);
}
+
DBUG_RETURN(0);
err:
+ if (tmp_backup)
+ my_afree(tmp_backup);
if (opts)
my_cleanup_options(opts);
DBUG_RETURN(error);
@@ -4023,3 +4056,38 @@ sys_var *find_plugin_sysvar(st_plugin_int *plugin, st_mysql_sys_var *plugin_var)
return 0;
}
+/*
+ On dlclose() we need to restore values of all symbols that we've modified in
+ the DSO. The reason is - the DSO might not actually be unloaded, so on the
+ next dlopen() these symbols will have old values, they won't be
+ reinitialized.
+
+ Perhaps, there can be many reason, why a DSO won't be unloaded. Strictly
+ speaking, it's implementation defined whether to unload an unused DSO or to
+ keep it in memory.
+
+ In particular, this happens for some plugins: In 2009 a new ELF stub was
+ introduced, see Ulrich Drepper's email "Unique symbols for C++"
+ http://www.redhat.com/archives/posix-c++-wg/2009-August/msg00002.html
+
+ DSO that has objects with this stub (STB_GNU_UNIQUE) cannot be unloaded
+ (this is mentioned in the email, see the url above).
+
+ These "unique" objects are, for example, static variables in templates,
+ in inline functions, in classes. So any DSO that uses them can
+ only be loaded once. And because Boost has them, any DSO that uses Boost
+ almost certainly cannot be unloaded.
+
+ To know whether a particular DSO has these objects, one can use
+
+ readelf -s /path/to/plugin.so|grep UNIQUE
+
+ There's nothing we can do about it, but to reset the DSO to its initial
+ state before dlclose().
+*/
+static void restore_ptr_backup(uint n, st_ptr_backup *backup)
+{
+ while (n--)
+ (backup++)->restore();
+}
+
diff --git a/sql/sql_plugin.h b/sql/sql_plugin.h
index 5327b27e97c..a0225f4a071 100644
--- a/sql/sql_plugin.h
+++ b/sql/sql_plugin.h
@@ -81,15 +81,25 @@ typedef struct st_mysql_show_var SHOW_VAR;
/* A handle for the dynamic library containing a plugin or plugins. */
+struct st_ptr_backup {
+ void **ptr;
+ void *value;
+ void save(void **p) { ptr= p; value= *p; }
+ void save(const char **p) { save((void**)p); }
+ void restore() { *ptr= value; }
+};
+
struct st_plugin_dl
{
LEX_STRING dl;
void *handle;
struct st_maria_plugin *plugins;
+ st_ptr_backup *ptr_backup;
+ uint nbackups;
+ uint ref_count; /* number of plugins loaded from the library */
int mysqlversion;
int mariaversion;
bool allocated;
- uint ref_count; /* number of plugins loaded from the library */
};
/* A handle of a plugin */
@@ -99,6 +109,8 @@ struct st_plugin_int
LEX_STRING name;
struct st_maria_plugin *plugin;
struct st_plugin_dl *plugin_dl;
+ st_ptr_backup *ptr_backup;
+ uint nbackups;
uint state;
uint ref_count; /* number of threads using the plugin */
uint locks_total; /* how many times the plugin was locked */
diff --git a/sql/sql_plugin_services.h b/sql/sql_plugin_services.h
index 362252eee8a..38b4c4074be 100644
--- a/sql/sql_plugin_services.h
+++ b/sql/sql_plugin_services.h
@@ -74,6 +74,18 @@ static struct logger_service_st logger_service_handler= {
logger_rotate
};
+static struct thd_autoinc_service_st thd_autoinc_handler= {
+ thd_get_autoinc
+};
+
+static struct thd_error_context_service_st thd_error_conext_handler= {
+ thd_get_error_message,
+ thd_get_error_number,
+ thd_get_error_row,
+ thd_inc_error_row,
+ thd_get_error_context_description
+};
+
static struct st_service_ref list_of_services[]=
{
{ "my_snprintf_service", VERSION_my_snprintf, &my_snprintf_handler },
@@ -85,5 +97,7 @@ static struct st_service_ref list_of_services[]=
{ "thd_timezone_service", VERSION_thd_timezone, &thd_timezone_handler },
{ "my_sha1_service", VERSION_my_sha1, &my_sha1_handler},
{ "logger_service", VERSION_logger, &logger_service_handler },
+ { "thd_autoinc_service", VERSION_thd_autoinc, &thd_autoinc_handler },
+ { "thd_error_context_service", VERSION_thd_error_context, &thd_error_conext_handler },
};
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index f31438f75c2..a9cc26cfb61 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -1,5 +1,5 @@
-/* Copyright (c) 2002, 2012, Oracle and/or its affiliates.
- Copyright (c) 2008, 2011, Monty Program Ab
+/* Copyright (c) 2002, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2008, 2013, Monty Program Ab
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
@@ -116,6 +116,7 @@ When one supplies long data for a placeholder:
#endif
#include "lock.h" // MYSQL_OPEN_FORCE_SHARED_MDL
#include "sql_handler.h"
+#include "transaction.h" // trans_rollback_implicit
/**
A result class used to send cursor rows using the binary protocol.
@@ -3439,6 +3440,22 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
close_thread_tables(thd);
thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
+
+ /*
+ Transaction rollback was requested since MDL deadlock was discovered
+ while trying to open tables. Rollback transaction in all storage
+ engines including binary log and release all locks.
+
+ Once dynamic SQL is allowed as substatements the below if-statement
+ has to be adjusted to not do rollback in substatement.
+ */
+ DBUG_ASSERT(! thd->in_sub_stmt);
+ if (thd->transaction_rollback_request)
+ {
+ trans_rollback_implicit(thd);
+ thd->mdl_context.release_transactional_locks();
+ }
+
lex_end(lex);
cleanup_stmt();
thd->restore_backup_statement(this, &stmt_backup);
diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc
index 28d1c72edf4..e4116759c13 100644
--- a/sql/sql_repl.cc
+++ b/sql/sql_repl.cc
@@ -1575,7 +1575,7 @@ send_event_to_slave(THD *thd, NET *net, String* const packet, ushort flags,
DBUG_EXECUTE_IF("gtid_force_reconnect_at_10_1_100",
{
rpl_gtid *dbug_gtid;
- if ((dbug_gtid= until_binlog_state->find(10,1)) &&
+ if ((dbug_gtid= until_binlog_state->find_nolock(10,1)) &&
dbug_gtid->seq_no == 100)
{
DBUG_SET("-d,gtid_force_reconnect_at_10_1_100");
@@ -1585,7 +1585,7 @@ send_event_to_slave(THD *thd, NET *net, String* const packet, ushort flags,
}
});
- if (until_binlog_state->update(&event_gtid, false))
+ if (until_binlog_state->update_nolock(&event_gtid, false))
{
my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG;
return "Failed in internal GTID book-keeping: Out of memory";
diff --git a/sql/sql_repl.h b/sql/sql_repl.h
index da55e3e863f..018b23673ee 100644
--- a/sql/sql_repl.h
+++ b/sql/sql_repl.h
@@ -67,7 +67,9 @@ int log_loaded_block(IO_CACHE* file);
int init_replication_sys_vars();
void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos, ushort flags);
+#ifdef HAVE_PSI_INTERFACE
extern PSI_mutex_key key_LOCK_slave_state, key_LOCK_binlog_state;
+#endif
void rpl_init_gtid_slave_state();
void rpl_deinit_gtid_slave_state();
int gtid_state_from_binlog_pos(const char *name, uint32 pos, String *out_str);
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index f5cfbf8ffd1..c109ee10877 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -241,7 +241,8 @@ static ORDER *create_distinct_group(THD *thd, Item **ref_pointer_array,
List<Item> &all_fields,
bool *all_order_by_fields_used);
static bool test_if_subpart(ORDER *a,ORDER *b);
-static TABLE *get_sort_by_table(ORDER *a,ORDER *b,List<TABLE_LIST> &tables);
+static TABLE *get_sort_by_table(ORDER *a,ORDER *b,List<TABLE_LIST> &tables,
+ table_map const_tables);
static void calc_group_buffer(JOIN *join,ORDER *group);
static bool make_group_fields(JOIN *main_join, JOIN *curr_join);
static bool alloc_group_fields(JOIN *join,ORDER *group);
@@ -1319,7 +1320,8 @@ TODO: make view to decide if it is possible to write to WHERE directly or make S
goto setup_subq_exit;
}
error= -1; // Error is sent to client
- sort_by_table= get_sort_by_table(order, group_list, select_lex->leaf_tables);
+ /* get_sort_by_table() call used to be here: */
+ MEM_UNDEFINED(&sort_by_table, sizeof(sort_by_table));
/* Calculate how to do the join */
THD_STAGE_INFO(thd, stage_statistics);
@@ -3686,7 +3688,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
// All dep. must be constants
if (s->dependent & ~(found_const_table_map))
continue;
- if (table->stat_records() <= 1L &&
+ if (table->file->stats.records <= 1L &&
(table->file->ha_table_flags() & HA_STATS_RECORDS_IS_EXACT) &&
!table->pos_in_table_list->embedding &&
!((outer_join & table->map) &&
@@ -3798,6 +3800,9 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
}
} while (join->const_table_map & found_ref && ref_changed);
+ join->sort_by_table= get_sort_by_table(join->order, join->group_list,
+ join->select_lex->leaf_tables,
+ join->const_table_map);
/*
Update info on indexes that can be used for search lookups as
reading const tables may has added new sargable predicates.
@@ -3828,6 +3833,21 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
conds=new Item_int((longlong) 0,1);
}
join->conds= conds;
+ join->cond_equal= NULL;
+ if (conds)
+ {
+ if (conds->type() == Item::COND_ITEM &&
+ ((Item_cond*) conds)->functype() == Item_func::COND_AND_FUNC)
+ join->cond_equal= (&((Item_cond_and *) conds)->cond_equal);
+ else if (conds->type() == Item::FUNC_ITEM &&
+ ((Item_func*) conds)->functype() == Item_func::MULT_EQUAL_FUNC)
+ {
+ if (!join->cond_equal)
+ join->cond_equal= new COND_EQUAL;
+ join->cond_equal->current_level.empty();
+ join->cond_equal->current_level.push_back((Item_equal*) conds);
+ }
+ }
}
/* Calc how many (possible) matched records in each table */
@@ -3964,6 +3984,19 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
if (join->const_tables != join->table_count)
optimize_keyuse(join, keyuse_array);
+ DBUG_ASSERT(!join->conds || !join->cond_equal ||
+ !join->cond_equal->current_level.elements ||
+ (join->conds->type() == Item::COND_ITEM &&
+ ((Item_cond*) (join->conds))->functype() ==
+ Item_func::COND_AND_FUNC &&
+ join->cond_equal ==
+ &((Item_cond_and *) (join->conds))->cond_equal) ||
+ (join->conds->type() == Item::FUNC_ITEM &&
+ ((Item_func*) (join->conds))->functype() ==
+ Item_func::MULT_EQUAL_FUNC &&
+ join->cond_equal->current_level.elements == 1 &&
+ join->cond_equal->current_level.head() == join->conds));
+
if (optimize_semijoin_nests(join, all_table_map))
DBUG_RETURN(TRUE); /* purecov: inspected */
@@ -10908,7 +10941,23 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after)
join->group_list ?
join->join_tab+join->const_tables :
join->get_sort_by_join_tab();
- if (sort_by_tab)
+ /*
+ It could be that sort_by_tab==NULL, and the plan is to use filesort()
+ on the first table.
+ */
+ if (join->order)
+ {
+ join->simple_order= 0;
+ join->need_tmp= 1;
+ }
+
+ if (join->group && !join->group_optimized_away)
+ {
+ join->need_tmp= 1;
+ join->simple_group= 0;
+ }
+
+ if (sort_by_tab)
{
join->need_tmp= 1;
join->simple_order= join->simple_group= 0;
@@ -11399,17 +11448,18 @@ void JOIN::cleanup(bool full)
tabs_kind= WALK_EXECUTION_TABS;
if (table_count)
{
- for (tab= first_breadth_first_tab(this, tabs_kind); tab;
+ for (tab= first_breadth_first_tab(this, tabs_kind); tab;
tab= next_breadth_first_tab(this, tabs_kind, tab))
{
tab->cleanup();
}
}
cleaned= true;
+
}
else
{
- for (tab= first_linear_tab(this, WITH_CONST_TABLES); tab;
+ for (tab= first_linear_tab(this, WITH_CONST_TABLES); tab;
tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS))
{
if (tab->table)
@@ -14401,16 +14451,44 @@ internal_remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
{
if (cond->type() == Item::COND_ITEM)
{
- List<Item_equal> new_equalities;
bool and_level= ((Item_cond*) cond)->functype()
== Item_func::COND_AND_FUNC;
List<Item> *cond_arg_list= ((Item_cond*) cond)->argument_list();
+
+ if (and_level)
+ {
+ /*
+ Remove multiple equalities that became always true (e.g. after
+ constant row substitution).
+ They would be removed later in the function anyway, but the list of
+ them cond_equal.current_level also must be adjusted correspondingly.
+ So it's easier to do it at one pass through the list of the equalities.
+ */
+ List<Item_equal> *cond_equalities=
+ &((Item_cond_and *) cond)->cond_equal.current_level;
+ cond_arg_list->disjoin((List<Item> *) cond_equalities);
+ List_iterator<Item_equal> it(*cond_equalities);
+ Item_equal *eq_item;
+ while ((eq_item= it++))
+ {
+ if (eq_item->const_item() && eq_item->val_int())
+ it.remove();
+ }
+ cond_arg_list->concat((List<Item> *) cond_equalities);
+ }
+
+ List<Item_equal> new_equalities;
List_iterator<Item> li(*cond_arg_list);
+ bool should_fix_fields= 0;
Item::cond_result tmp_cond_value;
- bool should_fix_fields=0;
-
- *cond_value=Item::COND_UNDEF;
Item *item;
+
+ /*
+ If the list cond_arg_list became empty then it consisted only
+ of always true multiple equalities.
+ */
+ *cond_value= cond_arg_list->elements ? Item::COND_UNDEF : Item::COND_TRUE;
+
while ((item=li++))
{
Item *new_item=internal_remove_eq_conds(thd, item, &tmp_cond_value);
@@ -21284,7 +21362,8 @@ test_if_subpart(ORDER *a,ORDER *b)
*/
static TABLE *
-get_sort_by_table(ORDER *a,ORDER *b, List<TABLE_LIST> &tables)
+get_sort_by_table(ORDER *a,ORDER *b, List<TABLE_LIST> &tables,
+ table_map const_tables)
{
TABLE_LIST *table;
List_iterator<TABLE_LIST> ti(tables);
@@ -21298,6 +21377,23 @@ get_sort_by_table(ORDER *a,ORDER *b, List<TABLE_LIST> &tables)
for (; a && b; a=a->next,b=b->next)
{
+ /* Skip elements of a that are constant */
+ while (!((*a->item)->used_tables() & ~const_tables))
+ {
+ if (!(a= a->next))
+ break;
+ }
+
+ /* Skip elements of b that are constant */
+ while (!((*b->item)->used_tables() & ~const_tables))
+ {
+ if (!(b= b->next))
+ break;
+ }
+
+ if (!a || !b)
+ break;
+
if (!(*a->item)->eq(*b->item,1))
DBUG_RETURN(0);
map|=a->item[0]->used_tables();
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index bcdffce71f1..eb986f16d73 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -1758,13 +1758,8 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet,
table->field[key_part->fieldnr-1]->key_length() &&
!(key_info->flags & (HA_FULLTEXT | HA_SPATIAL))))
{
- char *end;
- buff[0] = '(';
- end= int10_to_str((long) key_part->length /
- key_part->field->charset()->mbmaxlen,
- buff + 1,10);
- *end++ = ')';
- packet->append(buff,(uint) (end-buff));
+ packet->append_parenthesized((long) key_part->length /
+ key_part->field->charset()->mbmaxlen);
}
}
packet->append(')');
@@ -1962,7 +1957,8 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet,
&part_syntax_len,
FALSE,
show_table_options,
- NULL, NULL)))
+ NULL, NULL,
+ comment_start.c_ptr())))
{
packet->append(comment_start);
if (packet->append(part_syntax, part_syntax_len) ||
@@ -2620,7 +2616,7 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond)
{
Security_context *tmp_sctx= tmp->security_ctx;
struct st_my_thread_var *mysys_var;
- const char *val;
+ const char *val, *db;
ulonglong max_counter;
if ((!tmp->vio_ok() && !tmp->system_thread) ||
@@ -2647,13 +2643,13 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond)
table->field[2]->store(tmp_sctx->host_or_ip,
strlen(tmp_sctx->host_or_ip), cs);
/* DB */
- if (tmp->db)
+ mysql_mutex_lock(&tmp->LOCK_thd_data);
+ if ((db= tmp->db))
{
- table->field[3]->store(tmp->db, strlen(tmp->db), cs);
+ table->field[3]->store(db, strlen(db), cs);
table->field[3]->set_notnull();
}
- mysql_mutex_lock(&tmp->LOCK_thd_data);
if ((mysys_var= tmp->mysys_var))
mysql_mutex_lock(&mysys_var->mutex);
/* COMMAND */
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index df499797797..ff389a96a8d 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -1818,7 +1818,8 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags)
&syntax_len,
TRUE, TRUE,
lpt->create_info,
- lpt->alter_info)))
+ lpt->alter_info,
+ NULL)))
{
DBUG_RETURN(TRUE);
}
@@ -1921,7 +1922,8 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags)
&syntax_len,
TRUE, TRUE,
lpt->create_info,
- lpt->alter_info)))
+ lpt->alter_info,
+ NULL)))
{
error= 1;
goto err;
@@ -2205,6 +2207,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
bool non_tmp_error= 0;
bool trans_tmp_table_deleted= 0, non_trans_tmp_table_deleted= 0;
bool non_tmp_table_deleted= 0;
+ bool is_drop_tmp_if_exists_added= 0;
String built_query;
String built_trans_tmp_query, built_non_trans_tmp_query;
DBUG_ENTER("mysql_rm_table_no_locks");
@@ -2234,6 +2237,15 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
table stems from the fact that such drop does not commit an ongoing
transaction and changes to non-transactional tables must be written
ahead of the transaction in some circumstances.
+
+ 6- Slave SQL thread ignores all replicate-* filter rules
+ for temporary tables with 'IF EXISTS' clause. (See sql/sql_parse.cc:
+ mysql_execute_command() for details). These commands will be binlogged
+ as they are, even if the default database (from USE `db`) is not present
+ on the Slave. This can cause point in time recovery failures later
+ when user uses the slave's binlog to re-apply. Hence at the time of binary
+ logging, these commands will be written with fully qualified table names
+ and use `db` will be suppressed.
*/
if (!dont_log_query)
{
@@ -2257,6 +2269,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
if (thd->is_current_stmt_binlog_format_row() || if_exists)
{
+ is_drop_tmp_if_exists_added= true;
built_trans_tmp_query.set_charset(system_charset_info);
built_trans_tmp_query.append("DROP TEMPORARY TABLE IF EXISTS ");
built_non_trans_tmp_query.set_charset(system_charset_info);
@@ -2333,10 +2346,12 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
String *built_ptr_query=
(is_trans ? &built_trans_tmp_query : &built_non_trans_tmp_query);
/*
- Don't write the database name if it is the current one (or if
- thd->db is NULL).
+ Write the database name if it is not the current one or if
+ thd->db is NULL or 'IF EXISTS' clause is present in 'DROP TEMPORARY'
+ query.
*/
- if (thd->db == NULL || strcmp(db,thd->db) != 0)
+ if (thd->db == NULL || strcmp(db,thd->db) != 0
+ || is_drop_tmp_if_exists_added )
{
append_identifier(thd, built_ptr_query, db, db_length);
built_ptr_query->append(".");
@@ -2551,7 +2566,9 @@ err:
error |= thd->binlog_query(THD::STMT_QUERY_TYPE,
built_non_trans_tmp_query.ptr(),
built_non_trans_tmp_query.length(),
- FALSE, FALSE, FALSE, 0);
+ FALSE, FALSE,
+ is_drop_tmp_if_exists_added,
+ 0);
}
if (trans_tmp_table_deleted)
{
@@ -2561,7 +2578,9 @@ err:
error |= thd->binlog_query(THD::STMT_QUERY_TYPE,
built_trans_tmp_query.ptr(),
built_trans_tmp_query.length(),
- TRUE, FALSE, FALSE, 0);
+ TRUE, FALSE,
+ is_drop_tmp_if_exists_added,
+ 0);
}
if (non_tmp_table_deleted)
{
@@ -4367,7 +4386,8 @@ handler *mysql_create_frm_image(THD *thd,
&syntax_len,
TRUE, TRUE,
create_info,
- alter_info)))
+ alter_info,
+ NULL)))
goto err;
part_info->part_info_string= part_syntax_buf;
part_info->part_info_len= syntax_len;
@@ -5260,7 +5280,6 @@ int mysql_discard_or_import_tablespace(THD *thd,
error= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
err:
- trans_rollback_stmt(thd);
thd->tablespace_op=FALSE;
if (error == 0)
@@ -7909,7 +7928,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
Table maybe does not exist, but we got an exclusive lock
on the name, now we can safely try to find out for sure.
*/
- if (!access(alter_ctx.get_new_filename(), F_OK))
+ if (ha_table_exists(thd, alter_ctx.new_db, alter_ctx.new_name, 0))
{
/* Table will be closed in do_command() */
my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alter_ctx.new_alias);
@@ -9128,6 +9147,12 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
Protocol *protocol= thd->protocol;
DBUG_ENTER("mysql_checksum_table");
+ /*
+ CHECKSUM TABLE returns results and rollbacks statement transaction,
+ so it should not be used in stored function or trigger.
+ */
+ DBUG_ASSERT(! thd->in_sub_stmt);
+
field_list.push_back(item = new Item_empty_string("Table", NAME_LEN*2));
item->maybe_null= 1;
field_list.push_back(item= new Item_int("Checksum",
@@ -9166,7 +9191,6 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
open_and_lock_tables(thd, table, FALSE, 0))
{
t= NULL;
- thd->clear_error(); // these errors shouldn't get client
}
else
t= table->table;
@@ -9180,7 +9204,6 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
{
/* Table didn't exist */
protocol->store_null();
- thd->clear_error();
}
else
{
@@ -9268,11 +9291,24 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
t->file->ha_rnd_end();
}
}
- thd->clear_error();
- if (! thd->in_sub_stmt)
- trans_rollback_stmt(thd);
+ trans_rollback_stmt(thd);
close_thread_tables(thd);
}
+
+ if (thd->transaction_rollback_request)
+ {
+ /*
+ If transaction rollback was requested we honor it. To do this we
+ abort statement and return error as not only CHECKSUM TABLE is
+ rolled back but the whole transaction in which it was used.
+ */
+ thd->protocol->remove_last_row();
+ goto err;
+ }
+
+ /* Hide errors from client. Return NULL for problematic tables instead. */
+ thd->clear_error();
+
if (protocol->write())
goto err;
}
diff --git a/sql/sql_test.cc b/sql/sql_test.cc
index 1a241521fd7..922423bef26 100644
--- a/sql/sql_test.cc
+++ b/sql/sql_test.cc
@@ -20,7 +20,7 @@
#include "sql_priv.h"
#include "unireg.h"
#include "sql_test.h"
-#include "sql_base.h" // unused_tables
+#include "sql_base.h"
#include "sql_show.h" // calc_sum_of_all_status
#include "sql_select.h"
#include "keycaches.h"
@@ -78,9 +78,8 @@ print_where(COND *cond,const char *info, enum_query_type query_type)
static void print_cached_tables(void)
{
- uint count= 0, unused= 0;
TABLE_SHARE *share;
- TABLE *start_link, *lnk, *entry;
+ TABLE *entry;
TDC_iterator tdc_it;
compile_time_assert(TL_WRITE_ONLY+1 == array_elements(lock_descriptions));
@@ -92,43 +91,19 @@ static void print_cached_tables(void)
mysql_mutex_lock(&LOCK_open);
while ((share= tdc_it.next()))
{
- TABLE_SHARE::TABLE_list::Iterator it(share->tdc.used_tables);
+ TABLE_SHARE::All_share_tables_list::Iterator it(share->tdc.all_tables);
while ((entry= it++))
{
printf("%-14.14s %-32s%6ld%8ld%6d %s\n",
entry->s->db.str, entry->s->table_name.str, entry->s->version,
- entry->in_use->thread_id, entry->db_stat ? 1 : 0,
- lock_descriptions[(int)entry->reginfo.lock_type]);
- }
- it.init(share->tdc.free_tables);
- while ((entry= it++))
- {
- unused++;
- printf("%-14.14s %-32s%6ld%8ld%6d %s\n",
- entry->s->db.str, entry->s->table_name.str, entry->s->version,
- 0L, entry->db_stat ? 1 : 0, "Not in use");
- }
- }
- tdc_it.deinit();
- if ((start_link=lnk=unused_tables))
- {
- do
- {
- if (lnk != lnk->next->prev || lnk != lnk->prev->next)
- {
- printf("unused_links isn't linked properly\n");
- return;
- }
- } while (count++ < tc_records() && (lnk=lnk->next) != start_link);
- if (lnk != start_link)
- {
- printf("Unused_links aren't connected\n");
+ entry->in_use ? entry->in_use->thread_id : 0,
+ entry->db_stat ? 1 : 0,
+ entry->in_use ? lock_descriptions[(int)entry->reginfo.lock_type] :
+ "Not in use");
}
}
mysql_mutex_unlock(&LOCK_open);
- if (count != unused)
- printf("Unused_links (%d) doesn't match table_def_cache: %d\n", count,
- unused);
+ tdc_it.deinit();
printf("\nCurrent refresh version: %ld\n", tdc_refresh_version());
fflush(stdout);
/* purecov: end */
diff --git a/sql/sql_time.cc b/sql/sql_time.cc
index 831a7fa2a20..b194d46643e 100644
--- a/sql/sql_time.cc
+++ b/sql/sql_time.cc
@@ -1100,3 +1100,22 @@ int my_time_compare(const MYSQL_TIME *a, const MYSQL_TIME *b)
return 0;
}
+
+/*
+ Convert a TIME value to DAY-TIME interval, e.g. for extraction:
+ EXTRACT(DAY FROM x), EXTRACT(HOUR FROM x), etc.
+ Moves full days from ltime->hour to ltime->day.
+ Note, time_type is set to MYSQL_TIMESTAMP_NONE, to make sure that
+ the structure is not used for anything else other than extraction:
+ non-extraction TIME functions expect zero day value!
+*/
+void time_to_daytime_interval(MYSQL_TIME *ltime)
+{
+ DBUG_ASSERT(ltime->time_type == MYSQL_TIMESTAMP_TIME);
+ DBUG_ASSERT(ltime->year == 0);
+ DBUG_ASSERT(ltime->month == 0);
+ DBUG_ASSERT(ltime->day == 0);
+ ltime->day= ltime->hour / 24;
+ ltime->hour%= 24;
+ ltime->time_type= MYSQL_TIMESTAMP_NONE;
+}
diff --git a/sql/sql_time.h b/sql/sql_time.h
index 928e257ab54..6a8c78ecd5e 100644
--- a/sql/sql_time.h
+++ b/sql/sql_time.h
@@ -33,6 +33,7 @@ typedef struct st_known_date_time_format KNOWN_DATE_TIME_FORMAT;
ulong convert_period_to_month(ulong period);
ulong convert_month_to_period(ulong month);
+void time_to_daytime_interval(MYSQL_TIME *l_time);
bool get_date_from_daynr(long daynr,uint *year, uint *month, uint *day);
my_time_t TIME_to_timestamp(THD *thd, const MYSQL_TIME *t, uint *error_code);
bool str_to_datetime_with_warn(CHARSET_INFO *cs, const char *str,
diff --git a/sql/sql_union.cc b/sql/sql_union.cc
index b046b3a4de8..d143930908d 100644
--- a/sql/sql_union.cc
+++ b/sql/sql_union.cc
@@ -193,13 +193,15 @@ void select_union::cleanup()
SYNOPSIS
st_select_lex_unit::init_prepare_fake_select_lex()
thd - thread handler
+ first_execution - TRUE at the first execution of the union
RETURN
options of SELECT
*/
void
-st_select_lex_unit::init_prepare_fake_select_lex(THD *thd_arg)
+st_select_lex_unit::init_prepare_fake_select_lex(THD *thd_arg,
+ bool first_execution)
{
thd_arg->lex->current_select= fake_select_lex;
fake_select_lex->table_list.link_in_list(&result_table_list,
@@ -207,7 +209,13 @@ st_select_lex_unit::init_prepare_fake_select_lex(THD *thd_arg)
fake_select_lex->context.table_list=
fake_select_lex->context.first_name_resolution_table=
fake_select_lex->get_table_list();
- if (!fake_select_lex->first_execution)
+ /*
+ The flag fake_select_lex->first_execution indicates whether this is
+ called at the first execution of the statement, while first_execution
+ shows whether this is called at the first execution of the union that
+ may form just a subselect.
+ */
+ if (!fake_select_lex->first_execution && first_execution)
{
for (ORDER *order= global_parameters->order_list.first;
order;
@@ -481,7 +489,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
{
/* Validate the global parameters of this union */
- init_prepare_fake_select_lex(thd);
+ init_prepare_fake_select_lex(thd, TRUE);
/* Should be done only once (the only item_list per statement) */
DBUG_ASSERT(fake_select_lex->join == 0);
if (!(fake_select_lex->join= new JOIN(thd, item_list, thd->variables.option_bits,
@@ -622,6 +630,7 @@ bool st_select_lex_unit::exec()
SELECT_LEX *select_cursor=first_select();
ulonglong add_rows=0;
ha_rows examined_rows= 0;
+ bool first_execution= !executed;
DBUG_ENTER("st_select_lex_unit::exec");
bool was_executed= executed;
@@ -644,6 +653,7 @@ bool st_select_lex_unit::exec()
{
ha_rows records_at_start= 0;
thd->lex->current_select= sl;
+ fake_select_lex->uncacheable|= sl->uncacheable;
{
set_limit(sl);
@@ -750,7 +760,7 @@ bool st_select_lex_unit::exec()
if (!thd->is_fatal_error) // Check if EOM
{
set_limit(global_parameters);
- init_prepare_fake_select_lex(thd);
+ init_prepare_fake_select_lex(thd, first_execution);
JOIN *join= fake_select_lex->join;
if (!join)
{
diff --git a/sql/sql_view.cc b/sql/sql_view.cc
index 505b8b25f89..e1c92a81604 100644
--- a/sql/sql_view.cc
+++ b/sql/sql_view.cc
@@ -908,7 +908,7 @@ loop_out:
fn_format(path_buff, file.str, dir.str, "", MY_UNPACK_FILENAME);
path.length= strlen(path_buff);
- if (!access(path.str, F_OK))
+ if (ha_table_exists(thd, view->db, view->table_name, NULL))
{
if (mode == VIEW_CREATE_NEW)
{
@@ -1443,7 +1443,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
objects of the view.
*/
if (!(table->view_sctx= (Security_context *)
- thd->stmt_arena->alloc(sizeof(Security_context))))
+ thd->stmt_arena->calloc(sizeof(Security_context))))
goto err;
security_ctx= table->view_sctx;
}
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 7f39720ab56..7514b7bec63 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -276,7 +276,7 @@ void case_stmt_action_case(LEX *lex)
(Instruction 12 in the example)
*/
- lex->spcont->push_label(current_thd, EMPTY_STR, lex->sphead->instructions());
+ lex->spcont->push_label(current_thd, empty_lex_str, lex->sphead->instructions());
}
/**
@@ -345,7 +345,7 @@ int case_stmt_action_when(LEX *lex, Item *when, bool simple)
*/
return !test(i) ||
- sp->push_backpatch(i, ctx->push_label(current_thd, EMPTY_STR, 0)) ||
+ sp->push_backpatch(i, ctx->push_label(current_thd, empty_lex_str, 0)) ||
sp->add_cont_backpatch(i) ||
sp->add_instr(i);
}
@@ -3074,7 +3074,7 @@ sp_decl:
sp->push_backpatch(i, ctx->last_label()))
MYSQL_YYABORT;
- if (sp->push_backpatch(i, ctx->push_label(thd, EMPTY_STR, 0)))
+ if (sp->push_backpatch(i, ctx->push_label(thd, empty_lex_str, 0)))
MYSQL_YYABORT;
}
sp_hcond_list sp_proc_stmt
@@ -3719,8 +3719,7 @@ sp_proc_stmt_unlabeled:
{ /* Unlabeled controls get a secret label. */
LEX *lex= Lex;
- lex->spcont->push_label(thd,
- EMPTY_STR,
+ lex->spcont->push_label(thd, empty_lex_str,
lex->sphead->instructions());
}
sp_unlabeled_control
@@ -3949,7 +3948,7 @@ sp_if:
sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, ctx,
$2, lex);
if (i == NULL ||
- sp->push_backpatch(i, ctx->push_label(thd, EMPTY_STR, 0)) ||
+ sp->push_backpatch(i, ctx->push_label(thd, empty_lex_str, 0)) ||
sp->add_cont_backpatch(i) ||
sp->add_instr(i))
MYSQL_YYABORT;
@@ -3966,7 +3965,7 @@ sp_if:
sp->add_instr(i))
MYSQL_YYABORT;
sp->backpatch(ctx->pop_label());
- sp->push_backpatch(i, ctx->push_label(thd, EMPTY_STR, 0));
+ sp->push_backpatch(i, ctx->push_label(thd, empty_lex_str, 0));
}
sp_elseifs
{
@@ -4179,7 +4178,7 @@ sp_unlabeled_block:
{ /* Unlabeled blocks get a secret label. */
LEX *lex= Lex;
uint ip= lex->sphead->instructions();
- sp_label *lab= lex->spcont->push_label(thd, EMPTY_STR, ip);
+ sp_label *lab= lex->spcont->push_label(thd, empty_lex_str, ip);
lab->type= sp_label::BEGIN;
}
sp_block_content
@@ -4836,7 +4835,7 @@ partition:
;
part_type_def:
- opt_linear KEY_SYM '(' part_field_list ')'
+ opt_linear KEY_SYM opt_key_algo '(' part_field_list ')'
{
partition_info *part_info= Lex->part_info;
part_info->list_of_part_fields= TRUE;
@@ -4862,6 +4861,25 @@ opt_linear:
{ Lex->part_info->linear_hash_ind= TRUE;}
;
+opt_key_algo:
+ /* empty */
+ { Lex->part_info->key_algorithm= partition_info::KEY_ALGORITHM_NONE;}
+ | ALGORITHM_SYM EQ real_ulong_num
+ {
+ switch ($3) {
+ case 1:
+ Lex->part_info->key_algorithm= partition_info::KEY_ALGORITHM_51;
+ break;
+ case 2:
+ Lex->part_info->key_algorithm= partition_info::KEY_ALGORITHM_55;
+ break;
+ default:
+ my_parse_error(ER(ER_SYNTAX_ERROR));
+ MYSQL_YYABORT;
+ }
+ }
+ ;
+
part_field_list:
/* empty */ {}
| part_field_item_list {}
@@ -4943,7 +4961,7 @@ opt_sub_part:
| SUBPARTITION_SYM BY opt_linear HASH_SYM sub_part_func
{ Lex->part_info->subpart_type= HASH_PARTITION; }
opt_num_subparts {}
- | SUBPARTITION_SYM BY opt_linear KEY_SYM
+ | SUBPARTITION_SYM BY opt_linear KEY_SYM opt_key_algo
'(' sub_part_field_list ')'
{
partition_info *part_info= Lex->part_info;
@@ -9734,7 +9752,7 @@ function_call_conflict:
| PASSWORD '(' expr ')'
{
Item* i1;
- if (thd->variables.old_passwords == 1)
+ if (thd->variables.old_passwords)
i1= new (thd->mem_root) Item_func_old_password($3);
else
i1= new (thd->mem_root) Item_func_password($3);
@@ -13984,6 +14002,7 @@ keyword:
| EXAMINED_SYM {}
| EXECUTE_SYM {}
| FLUSH_SYM {}
+ | GET_SYM {}
| HANDLER_SYM {}
| HELP_SYM {}
| HOST_SYM {}
@@ -14769,18 +14788,10 @@ text_or_password:
TEXT_STRING { $$=$1.str;}
| PASSWORD '(' TEXT_STRING ')'
{
- if ($3.length == 0)
- $$= $3.str;
- else
- switch (thd->variables.old_passwords) {
- case 1: $$= Item_func_old_password::
- alloc(thd, $3.str, $3.length);
- break;
- case 0:
- case 2: $$= Item_func_password::
- create_password_hash_buffer(thd, $3.str, $3.length);
- break;
- }
+ $$= $3.length ? thd->variables.old_passwords ?
+ Item_func_old_password::alloc(thd, $3.str, $3.length) :
+ Item_func_password::alloc(thd, $3.str, $3.length) :
+ $3.str;
if ($$ == NULL)
MYSQL_YYABORT;
}
@@ -15380,7 +15391,7 @@ grant_user:
(char *) thd->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH+1);
if (buff == NULL)
MYSQL_YYABORT;
- my_make_scrambled_password_sha1(buff, $4.str, $4.length);
+ my_make_scrambled_password(buff, $4.str, $4.length);
$1->password.str= buff;
$1->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH;
}
diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc
index 2407b57d26f..98615b8b902 100644
--- a/sql/sys_vars.cc
+++ b/sql/sys_vars.cc
@@ -829,6 +829,7 @@ static bool event_scheduler_check(sys_var *self, THD *thd, set_var *var)
}
static bool event_scheduler_update(sys_var *self, THD *thd, enum_var_type type)
{
+ int err_no= 0;
uint opt_event_scheduler_value= Events::opt_event_scheduler;
mysql_mutex_unlock(&LOCK_global_system_variables);
/*
@@ -848,11 +849,14 @@ static bool event_scheduler_update(sys_var *self, THD *thd, enum_var_type type)
for deadlocks. See bug#51160.
*/
bool ret= opt_event_scheduler_value == Events::EVENTS_ON
- ? Events::start()
+ ? Events::start(&err_no)
: Events::stop();
mysql_mutex_lock(&LOCK_global_system_variables);
if (ret)
- my_error(ER_EVENT_SET_VAR_ERROR, MYF(0), my_errno);
+ {
+ Events::opt_event_scheduler= Events::EVENTS_OFF;
+ my_error(ER_EVENT_SET_VAR_ERROR, MYF(0), err_no);
+ }
return ret;
}
@@ -2859,20 +2863,22 @@ static Sys_var_ulong Sys_table_def_size(
VALID_RANGE(TABLE_DEF_CACHE_MIN, 512*1024),
DEFAULT(TABLE_DEF_CACHE_DEFAULT), BLOCK_SIZE(1));
+
+static bool fix_table_open_cache(sys_var *, THD *, enum_var_type)
+{
+ mysql_mutex_unlock(&LOCK_global_system_variables);
+ tc_purge();
+ mysql_mutex_lock(&LOCK_global_system_variables);
+ return false;
+}
+
+
static Sys_var_ulong Sys_table_cache_size(
"table_open_cache", "The number of cached open tables",
GLOBAL_VAR(tc_size), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(1, 512*1024), DEFAULT(TABLE_OPEN_CACHE_DEFAULT),
- BLOCK_SIZE(1));
-
-static ulong table_cache_instances;
-static Sys_var_ulong Sys_table_cache_instances(
- "table_open_cache_instances",
- "MySQL 5.6 compatible option. Not used or needed in MariaDB",
- READ_ONLY GLOBAL_VAR(table_cache_instances), CMD_LINE(REQUIRED_ARG),
- VALID_RANGE(1, 64), DEFAULT(1),
- BLOCK_SIZE(1), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(NULL),
- ON_UPDATE(NULL), NULL);
+ BLOCK_SIZE(1), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
+ ON_UPDATE(fix_table_open_cache));
static Sys_var_ulong Sys_thread_cache_size(
"thread_cache_size",
diff --git a/sql/table.cc b/sql/table.cc
index d0ad3e147f3..385de6547db 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -1194,7 +1194,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
next_chunk+= str_db_type_length + 2;
}
- share->set_use_ext_keys_flag(plugin_hton(se_plugin)->flags & HTON_EXTENDED_KEYS);
+ share->set_use_ext_keys_flag(plugin_hton(se_plugin)->flags & HTON_SUPPORTS_EXTENDED_KEYS);
if (create_key_infos(disk_buff + 6, frm_image_end, keys, keyinfo,
new_frm_ver, ext_key_parts,
@@ -2139,6 +2139,7 @@ int TABLE_SHARE::init_from_sql_statement_string(THD *thd, bool write,
if (tabledef_version.str)
thd->lex->create_info.tabledef_version= tabledef_version;
+ promote_first_timestamp_column(&thd->lex->alter_info.create_list);
file= mysql_create_frm_image(thd, db.str, table_name.str,
&thd->lex->create_info, &thd->lex->alter_info,
C_ORDINARY_CREATE, &unused1, &unused2, &frm);
@@ -3819,7 +3820,7 @@ bool TABLE_SHARE::visit_subgraph(Wait_for_flush *wait_for_flush,
if (gvisitor->m_lock_open_count++ == 0)
mysql_mutex_lock(&LOCK_open);
- TABLE_list::Iterator tables_it(tdc.used_tables);
+ All_share_tables_list::Iterator tables_it(tdc.all_tables);
/*
In case of multiple searches running in parallel, avoid going
@@ -3837,7 +3838,7 @@ bool TABLE_SHARE::visit_subgraph(Wait_for_flush *wait_for_flush,
while ((table= tables_it++))
{
- if (gvisitor->inspect_edge(&table->in_use->mdl_context))
+ if (table->in_use && gvisitor->inspect_edge(&table->in_use->mdl_context))
{
goto end_leave_node;
}
@@ -3846,7 +3847,7 @@ bool TABLE_SHARE::visit_subgraph(Wait_for_flush *wait_for_flush,
tables_it.rewind();
while ((table= tables_it++))
{
- if (table->in_use->mdl_context.visit_subgraph(gvisitor))
+ if (table->in_use && table->in_use->mdl_context.visit_subgraph(gvisitor))
{
goto end_leave_node;
}
@@ -5074,10 +5075,8 @@ TABLE *TABLE_LIST::get_real_join_table()
*/
for (TABLE_LIST *t= ti++; t; t= ti++)
tbl= t;
- /*
- It is impossible that the list is empty
- so tbl can't be NULL after above loop.
- */
+ if (!tbl)
+ return NULL; // view/derived with no tables
if (!tbl->nested_join)
break;
/* go deeper if we've found nested join */
diff --git a/sql/table.h b/sql/table.h
index 301b98a5ec2..c25e2c8a83a 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -479,6 +479,7 @@ TABLE_CATEGORY get_table_category(const LEX_STRING *db,
struct TABLE_share;
+struct All_share_tables;
extern ulong tdc_refresh_version(void);
@@ -603,6 +604,7 @@ struct TABLE_SHARE
mysql_mutex_t LOCK_share; /* To protect TABLE_SHARE */
typedef I_P_List <TABLE, TABLE_share> TABLE_list;
+ typedef I_P_List <TABLE, All_share_tables> All_share_tables_list;
struct
{
/**
@@ -619,7 +621,7 @@ struct TABLE_SHARE
Doubly-linked (back-linked) lists of used and unused TABLE objects
for this share.
*/
- TABLE_list used_tables;
+ All_share_tables_list all_tables;
TABLE_list free_tables;
} tdc;
@@ -1009,17 +1011,18 @@ struct TABLE
private:
/**
- Links for the lists of used/unused TABLE objects for this share.
+ Links for the list of all TABLE objects for this share.
Declared as private to avoid direct manipulation with those objects.
One should use methods of I_P_List template instead.
*/
- TABLE *share_next, **share_prev;
-
- friend struct TABLE_share;
+ TABLE *share_all_next, **share_all_prev;
+ friend struct All_share_tables;
public:
THD *in_use; /* Which thread uses this */
+ /* Time when table was released to table cache. Valid for unused tables. */
+ ulonglong tc_time;
Field **field; /* Pointer to fields */
uchar *record[2]; /* Pointer to records */
@@ -1370,11 +1373,24 @@ struct TABLE_share
{
static inline TABLE **next_ptr(TABLE *l)
{
- return &l->share_next;
+ return &l->next;
+ }
+ static inline TABLE ***prev_ptr(TABLE *l)
+ {
+ return (TABLE ***) &l->prev;
+ }
+};
+
+
+struct All_share_tables
+{
+ static inline TABLE **next_ptr(TABLE *l)
+ {
+ return &l->share_all_next;
}
static inline TABLE ***prev_ptr(TABLE *l)
{
- return &l->share_prev;
+ return &l->share_all_prev;
}
};
diff --git a/sql/table_cache.cc b/sql/table_cache.cc
index b3c08400d56..6f51ac8276c 100644
--- a/sql/table_cache.cc
+++ b/sql/table_cache.cc
@@ -43,11 +43,7 @@
- free_table_share()
Table cache invariants:
- - TABLE_SHARE::used_tables shall not contain objects with TABLE::in_use == 0
- TABLE_SHARE::free_tables shall not contain objects with TABLE::in_use != 0
- - unused_tables shall not contain objects with TABLE::in_use != 0
- - cached TABLE object must be either in TABLE_SHARE::used_tables or in
- TABLE_SHARE::free_tables
*/
#include "my_global.h"
@@ -63,9 +59,8 @@ ulong tc_size; /**< Table cache threshold for LRU eviction. */
static HASH tdc_hash; /**< Collection of TABLE_SHARE objects. */
/** Collection of unused TABLE_SHARE objects. */
static TABLE_SHARE *oldest_unused_share, end_of_unused_share;
-TABLE *unused_tables; /**< Collection of unused TABLE objects. */
-static int64 tdc_version; /* Increments on each reload */
+static int64 tdc_version; /* Increments on each reload */
static int64 last_table_id;
static bool tdc_inited;
@@ -73,15 +68,8 @@ static uint tc_count; /**< Number of TABLE objects in table cache. */
/**
- Protects used and unused lists in the TABLE_SHARE object,
- LRU lists of used TABLEs.
-
- tc_count
- unused_tables
- TABLE::next
- TABLE::prev
- TABLE_SHARE::tdc.free_tables
- TABLE_SHARE::tdc.used_tables
+ Protects tc_count, TABLE_SHARE::tdc.free_tables, TABLE_SHARE::tdc.all_tables,
+ TABLE::in_use.
*/
mysql_mutex_t LOCK_open;
@@ -159,8 +147,8 @@ uint tc_records(void)
Free all unused TABLE objects.
While locked:
- - remove unused objects from TABLE_SHARE::tdc.free_tables lists
- - reset unused_tables
+ - remove unused objects from TABLE_SHARE::tdc.free_tables and
+ TABLE_SHARE::tdc.all_tables
- decrement tc_count
While unlocked:
@@ -172,28 +160,29 @@ uint tc_records(void)
void tc_purge(void)
{
+ TABLE_SHARE *share;
+ TABLE *table;
+ TDC_iterator tdc_it;
+ TABLE_SHARE::TABLE_list purge_tables;
+
+ tdc_it.init();
mysql_mutex_lock(&LOCK_open);
- if (unused_tables)
+ while ((share= tdc_it.next()))
{
- TABLE *table= unused_tables, *next;
- unused_tables->prev->next= 0;
- do
+ while ((table= share->tdc.free_tables.pop_front()))
{
- unused_tables->s->tdc.free_tables.remove(unused_tables);
+ share->tdc.all_tables.remove(table);
+ purge_tables.push_front(table);
tc_count--;
- } while ((unused_tables= unused_tables->next));
- mysql_rwlock_rdlock(&LOCK_flush);
- mysql_mutex_unlock(&LOCK_open);
-
- do
- {
- next= table->next;
- intern_close_table(table);
- } while ((table= next));
- mysql_rwlock_unlock(&LOCK_flush);
+ }
}
- else
- mysql_mutex_unlock(&LOCK_open);
+ tdc_it.deinit();
+ mysql_rwlock_rdlock(&LOCK_flush);
+ mysql_mutex_unlock(&LOCK_open);
+
+ while ((table= purge_tables.pop_front()))
+ intern_close_table(table);
+ mysql_rwlock_unlock(&LOCK_flush);
}
@@ -204,29 +193,12 @@ void tc_purge(void)
#ifdef EXTRA_DEBUG
static void check_unused(THD *thd)
{
- uint count= 0, open_files= 0;
- TABLE *cur_link, *start_link, *entry;
+ TABLE *entry;
TABLE_SHARE *share;
TDC_iterator tdc_it;
tdc_it.init();
mysql_mutex_lock(&LOCK_open);
- if ((start_link=cur_link=unused_tables))
- {
- do
- {
- if (cur_link != cur_link->next->prev || cur_link != cur_link->prev->next)
- {
- DBUG_PRINT("error",("Unused_links aren't linked properly")); /* purecov: inspected */
- return; /* purecov: inspected */
- }
- } while (count++ < tc_count &&
- (cur_link=cur_link->next) != start_link);
- if (cur_link != start_link)
- {
- DBUG_PRINT("error",("Unused_links aren't connected")); /* purecov: inspected */
- }
- }
while ((share= tdc_it.next()))
{
TABLE_SHARE::TABLE_list::Iterator it(share->tdc.free_tables);
@@ -245,27 +217,10 @@ static void check_unused(THD *thd)
entry->in_use= thd;
DBUG_ASSERT(!thd || !entry->file->extra(HA_EXTRA_IS_ATTACHED_CHILDREN));
entry->in_use= 0;
-
- count--;
- open_files++;
- }
- it.init(share->tdc.used_tables);
- while ((entry= it++))
- {
- if (!entry->in_use)
- {
- DBUG_PRINT("error",("Unused table is in share's list of used tables")); /* purecov: inspected */
- }
- open_files++;
}
}
mysql_mutex_unlock(&LOCK_open);
tdc_it.deinit();
- if (count != 0)
- {
- DBUG_PRINT("error",("Unused_links doesn't match open_cache: diff: %d", /* purecov: inspected */
- count)); /* purecov: inspected */
- }
}
#else
#define check_unused(A)
@@ -273,39 +228,6 @@ static void check_unused(THD *thd)
/**
- Remove unused TABLE object from table cache.
-
- @pre LOCK_open is locked, table is not used.
-
- While locked:
- - remove object from TABLE_SHARE::tdc.free_tables
- - remove object from unused_tables
-
- @note This is helper routine, supposed to be used by table cache
- methods only.
-*/
-
-static void tc_remove_table(TABLE *table)
-{
- mysql_mutex_assert_owner(&LOCK_open);
- DBUG_ASSERT(!table->in_use);
- /* Remove from per-share chain of unused TABLE objects. */
- table->s->tdc.free_tables.remove(table);
-
- /* And global unused chain. */
- table->next->prev= table->prev;
- table->prev->next= table->next;
- if (table == unused_tables)
- {
- unused_tables= unused_tables->next;
- if (table == unused_tables)
- unused_tables= 0;
- }
- tc_count--;
-}
-
-
-/**
Add new TABLE object to table cache.
@pre TABLE object is used by caller.
@@ -313,11 +235,11 @@ static void tc_remove_table(TABLE *table)
Added object cannot be evicted or acquired.
While locked:
- - add object to TABLE_SHARE::tdc.used_tables
+ - add object to TABLE_SHARE::tdc.all_tables
- increment tc_count
- evict LRU object from table cache if we reached threshold
- While unlocked:
+ While unlocked:
- free evicted object
*/
@@ -325,21 +247,45 @@ void tc_add_table(THD *thd, TABLE *table)
{
DBUG_ASSERT(table->in_use == thd);
mysql_mutex_lock(&LOCK_open);
- table->s->tdc.used_tables.push_front(table);
- tc_count++;
+ table->s->tdc.all_tables.push_front(table);
/* If we have too many TABLE instances around, try to get rid of them */
- if (tc_count > tc_size && unused_tables)
+ if (tc_count == tc_size)
{
- TABLE *purge_table= unused_tables;
- tc_remove_table(purge_table);
- mysql_rwlock_rdlock(&LOCK_flush);
+ TDC_iterator tdc_it;
mysql_mutex_unlock(&LOCK_open);
- intern_close_table(purge_table);
- mysql_rwlock_unlock(&LOCK_flush);
- check_unused(thd);
+
+ tdc_it.init();
+ mysql_mutex_lock(&LOCK_open);
+ if (tc_count == tc_size)
+ {
+ TABLE *purge_table= 0;
+ TABLE_SHARE *share;
+ while ((share= tdc_it.next()))
+ {
+ TABLE_SHARE::TABLE_list::Iterator it(share->tdc.free_tables);
+ TABLE *entry;
+ while ((entry= it++))
+ if (!purge_table || entry->tc_time < purge_table->tc_time)
+ purge_table= entry;
+ }
+ if (purge_table)
+ {
+ tdc_it.deinit();
+ purge_table->s->tdc.free_tables.remove(purge_table);
+ purge_table->s->tdc.all_tables.remove(purge_table);
+ mysql_rwlock_rdlock(&LOCK_flush);
+ mysql_mutex_unlock(&LOCK_open);
+ intern_close_table(purge_table);
+ mysql_rwlock_unlock(&LOCK_flush);
+ check_unused(thd);
+ return;
+ }
+ }
+ tdc_it.deinit();
}
- else
- mysql_mutex_unlock(&LOCK_open);
+ /* Nothing to evict, increment tc_count. */
+ tc_count++;
+ mysql_mutex_unlock(&LOCK_open);
}
@@ -352,13 +298,8 @@ void tc_add_table(THD *thd, TABLE *table)
While locked:
- pop object from TABLE_SHARE::tdc.free_tables()
- - remove share protection
- - remove object from unused_tables
- - add object to TABLE_SHARE::tdc.used_tables()
- mark object used by thd
- @note share protection is kept if there are no unused objects.
-
@return TABLE object, or NULL if no unused objects.
*/
@@ -372,28 +313,14 @@ static TABLE *tc_acquire_table(THD *thd, TABLE_SHARE *share)
mysql_mutex_unlock(&LOCK_open);
return 0;
}
- mysql_rwlock_unlock(&LOCK_tdc);
DBUG_ASSERT(!table->in_use);
-
- /* Unlink table from global unused tables list. */
- if (table == unused_tables)
- { // First unused
- unused_tables=unused_tables->next; // Remove from link
- if (table == unused_tables)
- unused_tables=0;
- }
- table->prev->next=table->next; /* Remove from unused list */
- table->next->prev=table->prev;
table->in_use= thd;
- /* Add table to list of used tables for this share. */
- table->s->tdc.used_tables.push_front(table);
mysql_mutex_unlock(&LOCK_open);
/* The ex-unused table must be fully functional. */
DBUG_ASSERT(table->db_stat && table->file);
/* The children must be detached from the table. */
DBUG_ASSERT(! table->file->extra(HA_EXTRA_IS_ATTACHED_CHILDREN));
- check_unused(thd);
return table;
}
@@ -407,13 +334,11 @@ static TABLE *tc_acquire_table(THD *thd, TABLE_SHARE *share)
While locked:
- mark object not in use by any thread
- - remove object from TABLE_SHARE::tdc.used_tables
- if object is marked for purge, decrement tc_count
- add object to TABLE_SHARE::tdc.free_tables
- - add object to unused_tables
- evict LRU object from table cache if we reached threshold
- While unlocked:
+ While unlocked:
- free evicted/purged object
@note Another thread may mark share for purge any moment (even
@@ -432,13 +357,14 @@ bool tc_release_table(TABLE *table)
DBUG_ASSERT(table->in_use);
DBUG_ASSERT(table->file);
+ table->tc_time= my_interval_timer();
+
mysql_mutex_lock(&LOCK_open);
- /* Remove table from the list of tables used in this share. */
- table->s->tdc.used_tables.remove(table);
table->in_use= 0;
- if (table->s->has_old_version() || table->needs_reopen() || !tdc_size)
+ if (table->s->has_old_version() || table->needs_reopen() || tc_count > tc_size)
{
tc_count--;
+ table->s->tdc.all_tables.remove(table);
mysql_rwlock_rdlock(&LOCK_flush);
mysql_mutex_unlock(&LOCK_open);
intern_close_table(table);
@@ -447,31 +373,7 @@ bool tc_release_table(TABLE *table)
}
/* Add table to the list of unused TABLE objects for this share. */
table->s->tdc.free_tables.push_front(table);
- /* Also link it last in the global list of unused TABLE objects. */
- if (unused_tables)
- {
- table->next=unused_tables;
- table->prev=unused_tables->prev;
- unused_tables->prev=table;
- table->prev->next=table;
- }
- else
- unused_tables=table->next=table->prev=table;
- /*
- We free the least used table, not the subject table,
- to keep the LRU order.
- */
- if (tc_count > tc_size)
- {
- TABLE *purge_table= unused_tables;
- tc_remove_table(purge_table);
- mysql_rwlock_rdlock(&LOCK_flush);
- mysql_mutex_unlock(&LOCK_open);
- intern_close_table(purge_table);
- mysql_rwlock_unlock(&LOCK_flush);
- }
- else
- mysql_mutex_unlock(&LOCK_open);
+ mysql_mutex_unlock(&LOCK_open);
check_unused(thd);
return false;
}
@@ -588,6 +490,7 @@ void tdc_start_shutdown(void)
plugins minimal and allows shutdown to proceed smoothly.
*/
tdc_size= 0;
+ tc_size= 0;
/* Free all cached but unused TABLEs and TABLE_SHAREs. */
close_cached_tables(NULL, NULL, FALSE, LONG_TIMEOUT);
}
@@ -636,19 +539,9 @@ ulong tdc_records(void)
void tdc_purge(bool all)
{
DBUG_ENTER("tdc_purge");
- for (;;)
+ while (all || tdc_records() > tdc_size)
{
TABLE_SHARE *share;
- if (!all)
- {
- mysql_rwlock_rdlock(&LOCK_tdc);
- if (tdc_hash.records <= tdc_size)
- {
- mysql_rwlock_unlock(&LOCK_tdc);
- break;
- }
- mysql_rwlock_unlock(&LOCK_tdc);
- }
mysql_mutex_lock(&LOCK_unused_shares);
if (!oldest_unused_share->tdc.next)
@@ -684,7 +577,7 @@ void tdc_init_share(TABLE_SHARE *share)
mysql_mutex_init(key_TABLE_SHARE_LOCK_table_share,
&share->tdc.LOCK_table_share, MY_MUTEX_INIT_FAST);
share->tdc.m_flush_tickets.empty();
- share->tdc.used_tables.empty();
+ share->tdc.all_tables.empty();
share->tdc.free_tables.empty();
tdc_assign_new_table_id(share);
share->version= tdc_refresh_version();
@@ -701,7 +594,7 @@ void tdc_deinit_share(TABLE_SHARE *share)
DBUG_ENTER("tdc_deinit_share");
DBUG_ASSERT(share->tdc.ref_count == 0);
DBUG_ASSERT(share->tdc.m_flush_tickets.is_empty());
- DBUG_ASSERT(share->tdc.used_tables.is_empty());
+ DBUG_ASSERT(share->tdc.all_tables.is_empty());
DBUG_ASSERT(share->tdc.free_tables.is_empty());
mysql_mutex_destroy(&share->tdc.LOCK_table_share);
DBUG_VOID_RETURN;
@@ -721,7 +614,7 @@ void tdc_deinit_share(TABLE_SHARE *share)
Caller is expected to unlock table share with tdc_unlock_share().
@retval 0 Share not found
- @retval !0 Pointer to locked table share
+ @retval !0 Pointer to locked table share
*/
TABLE_SHARE *tdc_lock_share(const char *db, const char *table_name)
@@ -846,6 +739,8 @@ TABLE_SHARE *tdc_acquire_share(THD *thd, const char *db, const char *table_name,
{
if ((*out_table= tc_acquire_table(thd, share)))
{
+ mysql_rwlock_unlock(&LOCK_tdc);
+ check_unused(thd);
DBUG_ASSERT(!(flags & GTS_NOLOCK));
DBUG_ASSERT(!share->error);
DBUG_ASSERT(!share->is_view);
@@ -894,7 +789,7 @@ TABLE_SHARE *tdc_acquire_share(THD *thd, const char *db, const char *table_name,
share->tdc.next->tdc.prev= share->tdc.prev;
share->tdc.next= 0;
share->tdc.prev= 0;
- }
+ }
mysql_mutex_unlock(&LOCK_unused_shares);
}
@@ -928,7 +823,7 @@ err:
void tdc_release_share(TABLE_SHARE *share)
{
DBUG_ENTER("tdc_release_share");
-
+
mysql_mutex_lock(&share->tdc.LOCK_table_share);
DBUG_PRINT("enter",
("share: 0x%lx table: %s.%s ref_count: %u version: %lu",
@@ -998,7 +893,7 @@ static TABLE_SHARE *tdc_delete_share(const char *db, const char *table_name)
/* Concurrent thread may start using share again, reset prev and next. */
share->tdc.prev= 0;
share->tdc.next= 0;
- }
+ }
mysql_mutex_unlock(&LOCK_unused_shares);
if (!tdc_delete_share_from_hash(share))
@@ -1064,21 +959,17 @@ bool tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type,
if ((share= tdc_delete_share(db, table_name)))
{
I_P_List <TABLE, TABLE_share> purge_tables;
- purge_tables.empty();
mysql_mutex_lock(&LOCK_open);
if (kill_delayed_threads)
kill_delayed_threads_for_table(share);
- TABLE_SHARE::TABLE_list::Iterator it(share->tdc.free_tables);
#ifndef DBUG_OFF
- if (remove_type == TDC_RT_REMOVE_ALL)
- DBUG_ASSERT(share->tdc.used_tables.is_empty());
- else if (remove_type == TDC_RT_REMOVE_NOT_OWN)
+ if (remove_type == TDC_RT_REMOVE_NOT_OWN)
{
- TABLE_SHARE::TABLE_list::Iterator it2(share->tdc.used_tables);
+ TABLE_SHARE::All_share_tables_list::Iterator it2(share->tdc.all_tables);
while ((table= it2++))
- DBUG_ASSERT(table->in_use == thd);
+ DBUG_ASSERT(!table->in_use || table->in_use == thd);
}
#endif
/*
@@ -1092,9 +983,10 @@ bool tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type,
if (remove_type != TDC_RT_REMOVE_NOT_OWN_KEEP_SHARE)
share->version= 0;
- while ((table= it++))
+ while ((table= share->tdc.free_tables.pop_front()))
{
- tc_remove_table(table);
+ share->tdc.all_tables.remove(table);
+ tc_count--;
purge_tables.push_front(table);
}
mysql_rwlock_rdlock(&LOCK_flush);
@@ -1105,6 +997,7 @@ bool tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type,
mysql_rwlock_unlock(&LOCK_flush);
check_unused(thd);
+ DBUG_ASSERT(share->tdc.all_tables.is_empty() || remove_type != TDC_RT_REMOVE_ALL);
tdc_release_share(share);
/* Wait for concurrent threads to free unused objects. */
diff --git a/sql/table_cache.h b/sql/table_cache.h
index 91fa650d3b9..7b7fb239131 100644
--- a/sql/table_cache.h
+++ b/sql/table_cache.h
@@ -26,7 +26,6 @@ enum enum_tdc_remove_table_type
extern ulong tdc_size;
extern ulong tc_size;
-extern TABLE *unused_tables; /* FIXME: make private */
extern mysql_mutex_t LOCK_open; /* FIXME: make private */
extern int tdc_init(void);
diff --git a/sql/transaction.cc b/sql/transaction.cc
index a214d1ed071..60f1e8ad3c3 100644
--- a/sql/transaction.cc
+++ b/sql/transaction.cc
@@ -1,4 +1,4 @@
-/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
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
@@ -343,6 +343,52 @@ bool trans_rollback(THD *thd)
/**
+ Implicitly rollback the current transaction, typically
+ after deadlock was discovered.
+
+ @param thd Current thread
+
+ @retval False Success
+ @retval True Failure
+
+ @note ha_rollback_low() which is indirectly called by this
+ function will mark XA transaction for rollback by
+ setting appropriate RM error status if there was
+ transaction rollback request.
+*/
+
+bool trans_rollback_implicit(THD *thd)
+{
+ int res;
+ DBUG_ENTER("trans_rollback_implict");
+
+ /*
+ Always commit/rollback statement transaction before manipulating
+ with the normal one.
+ Don't perform rollback in the middle of sub-statement, wait till
+ its end.
+ */
+ DBUG_ASSERT(thd->transaction.stmt.is_empty() && !thd->in_sub_stmt);
+
+ thd->server_status&= ~SERVER_STATUS_IN_TRANS;
+ DBUG_PRINT("info", ("clearing SERVER_STATUS_IN_TRANS"));
+ res= ha_rollback_trans(thd, true);
+ /*
+ We don't reset OPTION_BEGIN flag below to simulate implicit start
+ of new transacton in @@autocommit=1 mode. This is necessary to
+ preserve backward compatibility.
+ */
+ thd->variables.option_bits&= ~(OPTION_KEEP_LOG);
+ thd->transaction.all.modified_non_trans_table= false;
+
+ /* Rollback should clear transaction_rollback_request flag. */
+ DBUG_ASSERT(! thd->transaction_rollback_request);
+
+ DBUG_RETURN(test(res));
+}
+
+
+/**
Commit the single statement transaction.
@note Note that if the autocommit is on, then the following call
@@ -427,8 +473,6 @@ bool trans_rollback_stmt(THD *thd)
wsrep_register_hton(thd, FALSE);
#endif /* WITH_WSREP */
ha_rollback_trans(thd, FALSE);
- if (thd->transaction_rollback_request && !thd->in_sub_stmt)
- ha_rollback_trans(thd, TRUE);
if (! thd->in_active_multi_stmt_transaction())
{
thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation;
diff --git a/sql/transaction.h b/sql/transaction.h
index e002cd4a9dc..abe7823cf9b 100644
--- a/sql/transaction.h
+++ b/sql/transaction.h
@@ -30,6 +30,7 @@ bool trans_begin(THD *thd, uint flags= 0);
bool trans_commit(THD *thd);
bool trans_commit_implicit(THD *thd);
bool trans_rollback(THD *thd);
+bool trans_rollback_implicit(THD *thd);
bool trans_commit_stmt(THD *thd);
bool trans_rollback_stmt(THD *thd);
diff --git a/sql/tztime.cc b/sql/tztime.cc
index 272dfb6381b..2026ed425f2 100644
--- a/sql/tztime.cc
+++ b/sql/tztime.cc
@@ -2495,13 +2495,14 @@ char *root_name_end;
*/
my_bool
-scan_tz_dir(char * name_end)
+scan_tz_dir(char * name_end, uint symlink_recursion_level)
{
MY_DIR *cur_dir;
char *name_end_tmp;
uint i;
- if (!(cur_dir= my_dir(fullname, MYF(MY_WANT_STAT))))
+ /* Sort directory data, to pass mtr tests on different platforms. */
+ if (!(cur_dir= my_dir(fullname, MYF(MY_WANT_STAT|MY_WANT_SORT))))
return 1;
name_end= strmake(name_end, "/", FN_REFLEN - (name_end - fullname));
@@ -2515,7 +2516,32 @@ scan_tz_dir(char * name_end)
if (MY_S_ISDIR(cur_dir->dir_entry[i].mystat->st_mode))
{
- if (scan_tz_dir(name_end_tmp))
+ my_bool is_symlink;
+ if ((is_symlink= my_is_symlink(fullname)) &&
+ symlink_recursion_level > 0)
+ {
+ /*
+ The timezone definition data in some Linux distributions
+ (e.g. the "timezone-data-2013f" package in Gentoo)
+ may have synlimks like:
+ /usr/share/zoneinfo/posix/ -> /usr/share/zoneinfo/,
+ so the same timezone files are available under two names
+ (e.g. "CET" and "posix/CET").
+
+ We allow one level of symlink recursion for backward
+ compatibility with earlier timezone data packages that have
+ duplicate copies of the same timezone files inside the root
+ directory and the "posix" subdirectory (instead of symlinking).
+ This makes "posix/CET" still available, but helps to avoid
+ following such symlinks infinitely:
+ /usr/share/zoneinfo/posix/posix/posix/.../posix/
+ */
+ fflush(stdout);
+ fprintf(stderr, "Warning: Skipping directory '%s': "
+ "to avoid infinite symlink recursion.\n", fullname);
+ continue;
+ }
+ if (scan_tz_dir(name_end_tmp, symlink_recursion_level + is_symlink))
{
my_dirend(cur_dir);
return 1;
@@ -2527,14 +2553,20 @@ scan_tz_dir(char * name_end)
if (!tz_load(fullname, &tz_info, &tz_storage))
print_tz_as_sql(root_name_end + 1, &tz_info);
else
+ {
+ fflush(stdout);
fprintf(stderr,
"Warning: Unable to load '%s' as time zone. Skipping it.\n",
fullname);
+ }
free_root(&tz_storage, MYF(0));
}
else
+ {
+ fflush(stdout);
fprintf(stderr, "Warning: '%s' is not regular file or directory\n",
fullname);
+ }
}
}
@@ -2567,8 +2599,9 @@ main(int argc, char **argv)
printf("TRUNCATE TABLE time_zone_transition;\n");
printf("TRUNCATE TABLE time_zone_transition_type;\n");
- if (scan_tz_dir(root_name_end))
+ if (scan_tz_dir(root_name_end, 0))
{
+ fflush(stdout);
fprintf(stderr, "There were fatal errors during processing "
"of zoneinfo directory\n");
return 1;
@@ -2587,6 +2620,7 @@ main(int argc, char **argv)
{
if (tz_load(argv[2], &tz_info, &tz_storage))
{
+ fflush(stdout);
fprintf(stderr, "Problems with zoneinfo file '%s'\n", argv[2]);
return 1;
}
@@ -2596,6 +2630,7 @@ main(int argc, char **argv)
{
if (tz_load(argv[1], &tz_info, &tz_storage))
{
+ fflush(stdout);
fprintf(stderr, "Problems with zoneinfo file '%s'\n", argv[2]);
return 1;
}
@@ -2605,6 +2640,7 @@ main(int argc, char **argv)
free_root(&tz_storage, MYF(0));
}
+ my_end(0);
return 0;
}