summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorSergei Golubchik <sergii@pisem.net>2012-10-19 20:38:59 +0200
committerSergei Golubchik <sergii@pisem.net>2012-10-19 20:38:59 +0200
commite1f681c99b3e5462c033aaafa94ac295e626cde2 (patch)
tree2da5eff1a0d03831c2d85b32a7bc3df6ec37b522 /sql
parent52c84d144d3b07966d9b3bab8694eb012eef69ce (diff)
parent807fef40fffbbb8e92564a52b902b504ba8cfcdc (diff)
downloadmariadb-git-e1f681c99b3e5462c033aaafa94ac295e626cde2.tar.gz
10.0-base -> 10.0-monty
Diffstat (limited to 'sql')
-rw-r--r--sql/CMakeLists.txt2
-rw-r--r--sql/bounded_queue.h195
-rw-r--r--sql/create_options.cc5
-rw-r--r--sql/derror.cc4
-rw-r--r--sql/event_db_repository.cc11
-rw-r--r--sql/event_scheduler.cc6
-rw-r--r--sql/events.cc33
-rw-r--r--sql/field.cc16
-rw-r--r--sql/filesort.cc574
-rw-r--r--sql/filesort.h4
-rw-r--r--sql/filesort_utils.cc143
-rw-r--r--sql/filesort_utils.h129
-rw-r--r--sql/ha_ndbcluster_binlog.cc25
-rw-r--r--sql/ha_partition.cc141
-rw-r--r--sql/ha_partition.h26
-rw-r--r--sql/handler.cc128
-rw-r--r--sql/handler.h90
-rw-r--r--sql/item.cc32
-rw-r--r--sql/item.h107
-rw-r--r--sql/item_cmpfunc.cc21
-rw-r--r--sql/item_create.cc23
-rw-r--r--sql/item_func.cc101
-rw-r--r--sql/item_func.h26
-rw-r--r--sql/item_geofunc.cc6
-rw-r--r--sql/item_strfunc.cc1
-rw-r--r--sql/item_subselect.cc366
-rw-r--r--sql/item_subselect.h5
-rw-r--r--sql/item_timefunc.cc37
-rw-r--r--sql/item_xmlfunc.cc2
-rw-r--r--sql/lex.h2
-rw-r--r--sql/log.cc1011
-rw-r--r--sql/log.h143
-rw-r--r--sql/log_event.cc750
-rw-r--r--sql/log_event.h147
-rw-r--r--sql/log_event_old.cc2
-rw-r--r--sql/log_event_old.h2
-rw-r--r--sql/multi_range_read.cc2
-rw-r--r--sql/my_apc.cc270
-rw-r--r--sql/my_apc.h138
-rw-r--r--sql/mysqld.cc311
-rw-r--r--sql/mysqld.h14
-rw-r--r--sql/opt_range.cc27
-rw-r--r--sql/opt_subselect.cc15
-rw-r--r--sql/opt_sum.cc2
-rw-r--r--sql/protocol.cc6
-rw-r--r--sql/protocol.h5
-rw-r--r--sql/repl_failsafe.cc1
-rw-r--r--sql/rpl_mi.cc616
-rw-r--r--sql/rpl_mi.h54
-rw-r--r--sql/rpl_record.cc2
-rw-r--r--sql/rpl_rli.cc74
-rw-r--r--sql/rpl_rli.h6
-rw-r--r--sql/rpl_utility.cc7
-rw-r--r--sql/rpl_utility.h2
-rw-r--r--sql/set_var.cc2
-rw-r--r--sql/set_var.h3
-rw-r--r--sql/share/errmsg-utf8.txt59
-rw-r--r--sql/signal_handler.cc17
-rw-r--r--sql/slave.cc315
-rw-r--r--sql/slave.h9
-rw-r--r--sql/sp.cc34
-rw-r--r--sql/sp_head.cc2
-rw-r--r--sql/spatial.h2
-rw-r--r--sql/sql_acl.cc184
-rw-r--r--sql/sql_admin.cc1
-rw-r--r--sql/sql_array.h71
-rw-r--r--sql/sql_base.cc62
-rw-r--r--sql/sql_cache.cc97
-rw-r--r--sql/sql_cache.h31
-rw-r--r--sql/sql_class.cc190
-rw-r--r--sql/sql_class.h193
-rw-r--r--sql/sql_cmd.h2
-rw-r--r--sql/sql_const.h1
-rw-r--r--sql/sql_cursor.cc2
-rw-r--r--sql/sql_db.cc41
-rw-r--r--sql/sql_delete.cc16
-rw-r--r--sql/sql_insert.cc70
-rw-r--r--sql/sql_join_cache.cc13
-rw-r--r--sql/sql_lex.cc130
-rw-r--r--sql/sql_lex.h20
-rw-r--r--sql/sql_load.cc96
-rw-r--r--sql/sql_parse.cc297
-rw-r--r--sql/sql_partition.cc6
-rw-r--r--sql/sql_plugin.cc2
-rw-r--r--sql/sql_plugin.h6
-rw-r--r--sql/sql_prepare.cc3
-rw-r--r--sql/sql_priv.h1
-rw-r--r--sql/sql_profile.cc8
-rw-r--r--sql/sql_profile.h2
-rw-r--r--sql/sql_reload.cc51
-rw-r--r--sql/sql_repl.cc274
-rw-r--r--sql/sql_select.cc857
-rw-r--r--sql/sql_select.h49
-rw-r--r--sql/sql_show.cc246
-rw-r--r--sql/sql_show.h28
-rw-r--r--sql/sql_sort.h50
-rw-r--r--sql/sql_state.c2
-rw-r--r--sql/sql_string.cc28
-rw-r--r--sql/sql_string.h1
-rw-r--r--sql/sql_table.cc187
-rw-r--r--sql/sql_time.cc69
-rw-r--r--sql/sql_time.h2
-rw-r--r--sql/sql_trigger.cc3
-rw-r--r--sql/sql_truncate.cc41
-rw-r--r--sql/sql_udf.cc47
-rw-r--r--sql/sql_union.cc4
-rw-r--r--sql/sql_update.cc8
-rw-r--r--sql/sql_yacc.yy142
-rw-r--r--sql/strfunc.cc1
-rw-r--r--sql/sys_vars.cc282
-rw-r--r--sql/sys_vars.h174
-rw-r--r--sql/table.cc33
-rw-r--r--sql/table.h64
-rw-r--r--sql/tztime.cc4
-rw-r--r--sql/uniques.cc9
115 files changed, 8029 insertions, 2483 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt
index 6c53930f23d..0174ff822f3 100644
--- a/sql/CMakeLists.txt
+++ b/sql/CMakeLists.txt
@@ -39,6 +39,7 @@ ENDIF()
SET (SQL_SOURCE
../sql-common/client.c derror.cc des_key_file.cc
discover.cc ../libmysql/errmsg.c field.cc field_conv.cc
+ filesort_utils.cc
filesort.cc gstream.cc sha2.cc
signal_handler.cc
handler.cc hash_filo.h sql_plugin_services.h
@@ -86,6 +87,7 @@ SET (SQL_SOURCE
gcalc_slicescan.cc gcalc_tools.cc
threadpool_common.cc
../sql-common/mysql_async.c
+ my_apc.cc my_apc.h
${GEN_SOURCES}
${MYSYS_LIBWRAP_SOURCE}
)
diff --git a/sql/bounded_queue.h b/sql/bounded_queue.h
new file mode 100644
index 00000000000..2d4e6cff96d
--- /dev/null
+++ b/sql/bounded_queue.h
@@ -0,0 +1,195 @@
+/* Copyright (c) 2010, 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
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifndef BOUNDED_QUEUE_INCLUDED
+#define BOUNDED_QUEUE_INCLUDED
+
+#include <string.h>
+#include "my_global.h"
+#include "my_base.h"
+#include "my_sys.h"
+#include "queues.h"
+
+class Sort_param;
+
+/**
+ A priority queue with a fixed, limited size.
+
+ This is a wrapper on top of QUEUE and the queue_xxx() functions.
+ It keeps the top-N elements which are inserted.
+
+ Elements of type Element_type are pushed into the queue.
+ For each element, we call a user-supplied keymaker_function,
+ to generate a key of type Key_type for the element.
+ Instances of Key_type are compared with the user-supplied compare_function.
+
+ The underlying QUEUE implementation needs one extra element for replacing
+ the lowest/highest element when pushing into a full queue.
+ */
+template<typename Element_type, typename Key_type>
+class Bounded_queue
+{
+public:
+ Bounded_queue()
+ {
+ memset(&m_queue, 0, sizeof(m_queue));
+ }
+
+ ~Bounded_queue()
+ {
+ delete_queue(&m_queue);
+ }
+
+ /**
+ Function for making sort-key from input data.
+ @param param Sort parameters.
+ @param to Where to put the key.
+ @param from The input data.
+ */
+ typedef void (*keymaker_function)(Sort_param *param,
+ Key_type *to,
+ Element_type *from);
+
+ /**
+ Function for comparing two keys.
+ @param n Pointer to number of bytes to compare.
+ @param a First key.
+ @param b Second key.
+ @retval -1, 0, or 1 depending on whether the left argument is
+ less than, equal to, or greater than the right argument.
+ */
+ typedef int (*compare_function)(size_t *n, Key_type **a, Key_type **b);
+
+ /**
+ Initialize the queue.
+
+ @param max_elements The size of the queue.
+ @param max_at_top Set to true if you want biggest element on top.
+ false: We keep the n largest elements.
+ pop() will return the smallest key in the result set.
+ true: We keep the n smallest elements.
+ pop() will return the largest key in the result set.
+ @param compare Compare function for elements, takes 3 arguments.
+ If NULL, we use get_ptr_compare(compare_length).
+ @param compare_length Length of the data (i.e. the keys) used for sorting.
+ @param keymaker Function which generates keys for elements.
+ @param sort_param Sort parameters.
+ @param sort_keys Array of pointers to keys to sort.
+
+ @retval 0 OK, 1 Could not allocate memory.
+
+ We do *not* take ownership of any of the input pointer arguments.
+ */
+ int init(ha_rows max_elements, bool max_at_top,
+ compare_function compare, size_t compare_length,
+ keymaker_function keymaker, Sort_param *sort_param,
+ Key_type **sort_keys);
+
+ /**
+ Pushes an element on the queue.
+ If the queue is already full, we discard one element.
+ Calls keymaker_function to generate a key for the element.
+
+ @param element The element to be pushed.
+ */
+ void push(Element_type *element);
+
+ /**
+ Removes the top element from the queue.
+
+ @retval Pointer to the (key of the) removed element.
+
+ @note This function is for unit testing, where we push elements into to the
+ queue, and test that the appropriate keys are retained.
+ Interleaving of push() and pop() operations has not been tested.
+ */
+ Key_type **pop()
+ {
+ // Don't return the extra element to the client code.
+ if (queue_is_full((&m_queue)))
+ queue_remove(&m_queue, 0);
+ DBUG_ASSERT(m_queue.elements > 0);
+ if (m_queue.elements == 0)
+ return NULL;
+ return reinterpret_cast<Key_type**>(queue_remove(&m_queue, 0));
+ }
+
+ /**
+ The number of elements in the queue.
+ */
+ uint num_elements() const { return m_queue.elements; }
+
+ /**
+ Is the queue initialized?
+ */
+ bool is_initialized() const { return m_queue.max_elements > 0; }
+
+private:
+ Key_type **m_sort_keys;
+ size_t m_compare_length;
+ keymaker_function m_keymaker;
+ Sort_param *m_sort_param;
+ st_queue m_queue;
+};
+
+
+template<typename Element_type, typename Key_type>
+int Bounded_queue<Element_type, Key_type>::init(ha_rows max_elements,
+ bool max_at_top,
+ compare_function compare,
+ size_t compare_length,
+ keymaker_function keymaker,
+ Sort_param *sort_param,
+ Key_type **sort_keys)
+{
+ DBUG_ASSERT(sort_keys != NULL);
+
+ m_sort_keys= sort_keys;
+ m_compare_length= compare_length;
+ m_keymaker= keymaker;
+ m_sort_param= sort_param;
+ // init_queue() takes an uint, and also does (max_elements + 1)
+ if (max_elements >= (UINT_MAX - 1))
+ return 1;
+ if (compare == NULL)
+ compare=
+ reinterpret_cast<compare_function>(get_ptr_compare(compare_length));
+ // We allocate space for one extra element, for replace when queue is full.
+ return init_queue(&m_queue, (uint) max_elements + 1,
+ 0, max_at_top,
+ reinterpret_cast<queue_compare>(compare),
+ &m_compare_length, 0, 0);
+}
+
+
+template<typename Element_type, typename Key_type>
+void Bounded_queue<Element_type, Key_type>::push(Element_type *element)
+{
+ DBUG_ASSERT(is_initialized());
+ if (queue_is_full((&m_queue)))
+ {
+ // Replace top element with new key, and re-order the queue.
+ Key_type **pq_top= reinterpret_cast<Key_type **>(queue_top(&m_queue));
+ (*m_keymaker)(m_sort_param, *pq_top, element);
+ queue_replace_top(&m_queue);
+ } else {
+ // Insert new key into the queue.
+ (*m_keymaker)(m_sort_param, m_sort_keys[m_queue.elements], element);
+ queue_insert(&m_queue,
+ reinterpret_cast<uchar*>(&m_sort_keys[m_queue.elements]));
+ }
+}
+
+#endif // BOUNDED_QUEUE_INCLUDED
diff --git a/sql/create_options.cc b/sql/create_options.cc
index 9a6f6b5cf7c..e4881388688 100644
--- a/sql/create_options.cc
+++ b/sql/create_options.cc
@@ -137,7 +137,10 @@ static bool set_one_value(ha_create_table_option *opt,
my_option optp=
{ opt->name, 1, 0, (uchar **)val, 0, 0, GET_ULL,
- REQUIRED_ARG, opt->def_value, opt->min_value, opt->max_value,
+ REQUIRED_ARG,
+ (longlong) opt->def_value,
+ (longlong) opt->min_value,
+ opt->max_value,
0, (long) opt->block_size, 0};
ulonglong orig_val= strtoull(value->str, NULL, 10);
diff --git a/sql/derror.cc b/sql/derror.cc
index b0cf80fdd08..665427f45bc 100644
--- a/sql/derror.cc
+++ b/sql/derror.cc
@@ -177,8 +177,8 @@ bool read_texts(const char *file_name, const char *language,
O_RDONLY | O_SHARE | O_BINARY,
MYF(0))) < 0)
goto err;
- sql_print_error("An old style --language value with language specific part detected: %s", lc_messages_dir);
- sql_print_error("Use --lc-messages-dir without language specific part instead.");
+ sql_print_warning("An old style --language or -lc-message-dir value with language specific part detected: %s", lc_messages_dir);
+ sql_print_warning("Use --lc-messages-dir without language specific part instead.");
}
funktpos=1;
diff --git a/sql/event_db_repository.cc b/sql/event_db_repository.cc
index 7da6d4f83b6..d77c6e3a683 100644
--- a/sql/event_db_repository.cc
+++ b/sql/event_db_repository.cc
@@ -1113,17 +1113,15 @@ update_timing_fields_for_event(THD *thd,
TABLE *table= NULL;
Field **fields;
int ret= 1;
- bool save_binlog_row_based;
+ enum_binlog_format save_binlog_format;
MYSQL_TIME time;
-
DBUG_ENTER("Event_db_repository::update_timing_fields_for_event");
/*
Turn off row binlogging of event timing updates. These are not used
for RBR of events replicated to the slave.
*/
- if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
- thd->clear_current_stmt_binlog_format_row();
+ save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
DBUG_ASSERT(thd->security_ctx->master_access & SUPER_ACL);
@@ -1158,10 +1156,7 @@ end:
if (table)
close_mysql_tables(thd);
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
+ thd->restore_stmt_binlog_format(save_binlog_format);
DBUG_RETURN(test(ret));
}
diff --git a/sql/event_scheduler.cc b/sql/event_scheduler.cc
index bef422e9734..c340384f068 100644
--- a/sql/event_scheduler.cc
+++ b/sql/event_scheduler.cc
@@ -42,7 +42,7 @@
cond_wait(mythd, abstime, stage, SCHED_FUNC, __FILE__, __LINE__)
extern pthread_attr_t connection_attrib;
-
+extern ulong event_executed;
Event_db_repository *Event_worker_thread::db_repository;
@@ -190,7 +190,6 @@ pre_init_event_thread(THD* thd)
my_net_init(&thd->net, NULL);
thd->security_ctx->set_user((char*)"event_scheduler");
thd->net.read_timeout= slave_net_timeout;
- thd->slave_thread= 0;
thd->variables.option_bits|= OPTION_AUTO_IS_NULL;
thd->client_capabilities|= CLIENT_MULTI_RESULTS;
mysql_mutex_lock(&LOCK_thread_count);
@@ -561,7 +560,8 @@ Event_scheduler::execute_top(Event_queue_element_for_exec *event_name)
event_name)))
goto error;
- ++started_events;
+ started_events++;
+ executed_events++; // For SHOW STATUS
DBUG_PRINT("info", ("Event is in THD: 0x%lx", (long) new_thd));
DBUG_RETURN(FALSE);
diff --git a/sql/events.cc b/sql/events.cc
index 54ad76e40d4..cac71697300 100644
--- a/sql/events.cc
+++ b/sql/events.cc
@@ -306,7 +306,8 @@ Events::create_event(THD *thd, Event_parse_data *parse_data,
bool if_not_exists)
{
bool ret;
- bool save_binlog_row_based, event_already_exists;
+ bool event_already_exists;
+ enum_binlog_format save_binlog_format;
DBUG_ENTER("Events::create_event");
if (check_if_system_tables_error())
@@ -338,8 +339,7 @@ Events::create_event(THD *thd, Event_parse_data *parse_data,
Turn off row binlogging of this statement and use statement-based
so that all supporting tables are updated for CREATE EVENT command.
*/
- if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
- thd->clear_current_stmt_binlog_format_row();
+ save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
if (lock_object_name(thd, MDL_key::EVENT,
parse_data->dbname.str, parse_data->name.str))
@@ -398,10 +398,8 @@ Events::create_event(THD *thd, Event_parse_data *parse_data,
}
}
}
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
+
+ thd->restore_stmt_binlog_format(save_binlog_format);
DBUG_RETURN(ret);
}
@@ -431,7 +429,7 @@ Events::update_event(THD *thd, Event_parse_data *parse_data,
LEX_STRING *new_dbname, LEX_STRING *new_name)
{
int ret;
- bool save_binlog_row_based;
+ enum_binlog_format save_binlog_format;
Event_queue_element *new_element;
DBUG_ENTER("Events::update_event");
@@ -478,8 +476,7 @@ Events::update_event(THD *thd, Event_parse_data *parse_data,
Turn off row binlogging of this statement and use statement-based
so that all supporting tables are updated for UPDATE EVENT command.
*/
- if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
- thd->clear_current_stmt_binlog_format_row();
+ save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
if (lock_object_name(thd, MDL_key::EVENT,
parse_data->dbname.str, parse_data->name.str))
@@ -513,11 +510,8 @@ Events::update_event(THD *thd, Event_parse_data *parse_data,
ret= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
}
}
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
+ thd->restore_stmt_binlog_format(save_binlog_format);
DBUG_RETURN(ret);
}
@@ -550,7 +544,7 @@ bool
Events::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists)
{
int ret;
- bool save_binlog_row_based;
+ enum_binlog_format save_binlog_format;
DBUG_ENTER("Events::drop_event");
if (check_if_system_tables_error())
@@ -563,8 +557,7 @@ Events::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists)
Turn off row binlogging of this statement and use statement-based so
that all supporting tables are updated for DROP EVENT command.
*/
- if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
- thd->clear_current_stmt_binlog_format_row();
+ save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
if (lock_object_name(thd, MDL_key::EVENT,
dbname.str, name.str))
@@ -578,10 +571,8 @@ Events::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists)
DBUG_ASSERT(thd->query() && thd->query_length());
ret= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
}
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
+
+ thd->restore_stmt_binlog_format(save_binlog_format);
DBUG_RETURN(ret);
}
diff --git a/sql/field.cc b/sql/field.cc
index 13bdec3a881..0ca3f1b6ad6 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -5675,7 +5675,7 @@ bool Field_newdate::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate)
if (!tmp)
return fuzzydate & TIME_NO_ZERO_DATE;
if (!ltime->month || !ltime->day)
- return !(fuzzydate & TIME_FUZZY_DATE);
+ return fuzzydate & TIME_NO_ZERO_IN_DATE;
return 0;
}
@@ -9393,9 +9393,12 @@ Field *make_field(TABLE_SHARE *share, uchar *ptr, uint32 field_length,
#ifdef HAVE_SPATIAL
if (f_is_geom(pack_flag))
+ {
+ status_var_increment(current_thd->status_var.feature_gis);
return new Field_geom(ptr,null_pos,null_bit,
unireg_check, field_name, share,
pack_length, geom_type);
+ }
#endif
if (f_is_blob(pack_flag))
return new Field_blob(ptr,null_pos,null_bit,
@@ -9565,6 +9568,17 @@ Create_field::Create_field(Field *old_field,Field *orig_field)
geom_type= ((Field_geom*)old_field)->geom_type;
break;
#endif
+ case MYSQL_TYPE_YEAR:
+ if (length != 4)
+ {
+ char buff[sizeof("YEAR()") + MY_INT64_NUM_DECIMAL_DIGITS + 1];
+ my_snprintf(buff, sizeof(buff), "YEAR(%lu)", length);
+ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ ER_WARN_DEPRECATED_SYNTAX,
+ ER(ER_WARN_DEPRECATED_SYNTAX),
+ buff, "YEAR(4)");
+ }
+ break;
default:
break;
}
diff --git a/sql/filesort.cc b/sql/filesort.cc
index 7ee4dc52557..103d3600559 100644
--- a/sql/filesort.cc
+++ b/sql/filesort.cc
@@ -34,6 +34,9 @@
#include "sql_base.h" // update_virtual_fields
#include "sql_test.h" // TEST_filesort
#include "opt_range.h" // SQL_SELECT
+#include "bounded_queue.h"
+#include "filesort_utils.h"
+#include "sql_select.h"
#include "log_slow.h"
#include "debug_sync.h"
@@ -42,26 +45,72 @@
if (my_b_write((file),(uchar*) (from),param->ref_length)) \
DBUG_RETURN(1);
+#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
+template class Bounded_queue<uchar, uchar>;
+#endif
+
/* functions defined in this file */
static uchar *read_buffpek_from_file(IO_CACHE *buffer_file, uint count,
uchar *buf);
-static ha_rows find_all_keys(SORTPARAM *param,SQL_SELECT *select,
- uchar * *sort_keys, uchar *sort_keys_buf,
- IO_CACHE *buffer_file, IO_CACHE *tempfile);
-static int write_keys(SORTPARAM *param,uchar * *sort_keys,
- uint count, IO_CACHE *buffer_file, IO_CACHE *tempfile);
-static void make_sortkey(SORTPARAM *param,uchar *to, uchar *ref_pos);
-static void register_used_fields(SORTPARAM *param);
-static bool save_index(SORTPARAM *param,uchar **sort_keys, uint count,
- FILESORT_INFO *table_sort);
+static ha_rows find_all_keys(Sort_param *param,SQL_SELECT *select,
+ Filesort_info *fs_info,
+ IO_CACHE *buffer_file,
+ IO_CACHE *tempfile,
+ Bounded_queue<uchar, uchar> *pq,
+ ha_rows *found_rows);
+static int write_keys(Sort_param *param, Filesort_info *fs_info,
+ uint count, IO_CACHE *buffer_file, IO_CACHE *tempfile);
+static void make_sortkey(Sort_param *param, uchar *to, uchar *ref_pos);
+static void register_used_fields(Sort_param *param);
+static bool save_index(Sort_param *param, uint count,
+ Filesort_info *table_sort);
+static void make_sortkey(Sort_param *param, uchar *to, uchar *ref_pos);
static uint suffix_length(ulong string_length);
static uint sortlength(THD *thd, SORT_FIELD *sortorder, uint s_length,
bool *multi_byte_charset);
-static SORT_ADDON_FIELD *get_addon_fields(THD *thd, Field **ptabfield,
+static SORT_ADDON_FIELD *get_addon_fields(ulong max_length_for_sort_data,
+ Field **ptabfield,
uint sortlength, uint *plength);
static void unpack_addon_fields(struct st_sort_addon_field *addon_field,
uchar *buff, uchar *buff_end);
+static bool check_if_pq_applicable(Sort_param *param, Filesort_info *info,
+ TABLE *table,
+ ha_rows records, ulong memory_available);
+
+
+void Sort_param::init_for_filesort(uint sortlen, TABLE *table,
+ ulong max_length_for_sort_data,
+ ha_rows maxrows, bool sort_positions)
+{
+ sort_length= sortlen;
+ ref_length= table->file->ref_length;
+ if (!(table->file->ha_table_flags() & HA_FAST_KEY_READ) &&
+ !table->fulltext_searched && !sort_positions)
+ {
+ /*
+ Get the descriptors of all fields whose values are appended
+ to sorted fields and get its total length in addon_length.
+ */
+ addon_field= get_addon_fields(max_length_for_sort_data,
+ table->field, sort_length, &addon_length);
+ }
+ if (addon_field)
+ res_length= addon_length;
+ else
+ {
+ res_length= ref_length;
+ /*
+ The reference to the record is considered
+ as an additional sorted field
+ */
+ sort_length+= ref_length;
+ }
+ rec_length= sort_length + addon_length;
+ max_rows= maxrows;
+}
+
+
/**
Sort a table.
Creates a set of pointers that can be used to read the rows
@@ -74,15 +123,17 @@ static void unpack_addon_fields(struct st_sort_addon_field *addon_field,
The result set is stored in table->io_cache or
table->record_pointers.
- @param thd Current thread
- @param table Table to sort
- @param sortorder How to sort the table
- @param s_length Number of elements in sortorder
- @param select condition to apply to the rows
- @param max_rows Return only this many rows
- @param sort_positions Set to 1 if we want to force sorting by position
- (Needed by UPDATE/INSERT or ALTER TABLE)
- @param examined_rows Store number of examined rows here
+ @param thd Current thread
+ @param table Table to sort
+ @param sortorder How to sort the table
+ @param s_length Number of elements in sortorder
+ @param select Condition to apply to the rows
+ @param max_rows Return only this many rows
+ @param sort_positions Set to TRUE if we want to force sorting by position
+ (Needed by UPDATE/INSERT or ALTER TABLE or
+ when rowids are required by executor)
+ @param[out] examined_rows Store number of examined rows here
+ @param[out] found_rows Store the number of found rows here
@note
If we sort by position (like if sort_positions is 1) filesort() will
@@ -92,30 +143,30 @@ static void unpack_addon_fields(struct st_sort_addon_field *addon_field,
HA_POS_ERROR Error
@retval
\# Number of rows
- @retval
- examined_rows will be set to number of examined rows
*/
ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
SQL_SELECT *select, ha_rows max_rows,
- bool sort_positions, ha_rows *examined_rows)
+ bool sort_positions,
+ ha_rows *examined_rows,
+ ha_rows *found_rows)
{
int error;
ulong memory_available= thd->variables.sortbuff_size;
- ulong min_sort_memory;
uint maxbuffer;
BUFFPEK *buffpek;
ha_rows num_rows= HA_POS_ERROR;
- uchar **sort_keys= 0;
IO_CACHE tempfile, buffpek_pointers, *outfile;
- SORTPARAM param;
+ Sort_param param;
bool multi_byte_charset;
+ Bounded_queue<uchar, uchar> pq;
+
DBUG_ENTER("filesort");
DBUG_EXECUTE("info",TEST_filesort(sortorder,s_length););
#ifdef SKIP_DBUG_IN_FILESORT
DBUG_PUSH(""); /* No DBUG here */
#endif
- FILESORT_INFO table_sort;
+ Filesort_info table_sort= table->sort;
TABLE_LIST *tab= table->pos_in_table_list;
Item_subselect *subselect= tab ? tab->containing_subselect() : 0;
@@ -133,7 +184,6 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
QUICK_INDEX_MERGE_SELECT. Work with a copy and put it back at the end
when index_merge select has finished with it.
*/
- memcpy(&table_sort, &table->sort, sizeof(FILESORT_INFO));
table->sort.io_cache= NULL;
outfile= table_sort.io_cache;
@@ -141,43 +191,21 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
my_b_clear(&buffpek_pointers);
buffpek=0;
error= 1;
- bzero((char*) &param,sizeof(param));
- param.sort_length= sortlength(thd, sortorder, s_length, &multi_byte_charset);
- param.ref_length= table->file->ref_length;
- if (!(table->file->ha_table_flags() & HA_FAST_KEY_READ) &&
- !table->fulltext_searched && !sort_positions)
- {
- /*
- Get the descriptors of all fields whose values are appended
- to sorted fields and get its total length in param.spack_length.
- */
- param.addon_field= get_addon_fields(thd, table->field,
- param.sort_length,
- &param.addon_length);
- }
+
+ param.init_for_filesort(sortlength(thd, sortorder, s_length,
+ &multi_byte_charset),
+ table,
+ thd->variables.max_length_for_sort_data,
+ max_rows, sort_positions);
table_sort.addon_buf= 0;
table_sort.addon_length= param.addon_length;
table_sort.addon_field= param.addon_field;
table_sort.unpack= unpack_addon_fields;
- if (param.addon_field)
- {
- param.res_length= param.addon_length;
- if (!(table_sort.addon_buf= (uchar *) my_malloc(param.addon_length,
- MYF(MY_WME))))
- goto err;
- }
- else
- {
- param.res_length= param.ref_length;
- /*
- The reference to the record is considered
- as an additional sorted field
- */
- param.sort_length+= param.ref_length;
- }
- param.rec_length= param.sort_length+param.addon_length;
- param.max_rows= max_rows;
+ if (param.addon_field &&
+ !(table_sort.addon_buf=
+ (uchar *) my_malloc(param.addon_length, MYF(MY_WME))))
+ goto err;
if (select && select->quick)
thd->inc_status_sort_range();
@@ -192,20 +220,54 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
!(param.tmp_buffer= (char*) my_malloc(param.sort_length,MYF(MY_WME))))
goto err;
- min_sort_memory= max(MIN_SORT_MEMORY, param.sort_length * MERGEBUFF2);
- if (!table_sort.sort_keys)
+ if (check_if_pq_applicable(&param, &table_sort,
+ table, num_rows, memory_available))
+ {
+ DBUG_PRINT("info", ("filesort PQ is applicable"));
+ const size_t compare_length= param.sort_length;
+ if (pq.init(param.max_rows,
+ true, // max_at_top
+ NULL, // compare_function
+ compare_length,
+ &make_sortkey, &param, table_sort.get_sort_keys()))
+ {
+ /*
+ If we fail to init pq, we have to give up:
+ out of memory means my_malloc() will call my_error().
+ */
+ DBUG_PRINT("info", ("failed to allocate PQ"));
+ table_sort.free_sort_buffer();
+ DBUG_ASSERT(thd->is_error());
+ goto err;
+ }
+ // For PQ queries (with limit) we initialize all pointers.
+ table_sort.init_record_pointers();
+ }
+ else
{
+ DBUG_PRINT("info", ("filesort PQ is not applicable"));
+
+ ulong min_sort_memory= max(MIN_SORT_MEMORY, param.sort_length*MERGEBUFF2);
+ set_if_bigger(min_sort_memory, sizeof(BUFFPEK*)*MERGEBUFF2);
while (memory_available >= min_sort_memory)
{
ulong keys= memory_available / (param.rec_length + sizeof(char*));
- table_sort.keys= (uint) min(num_rows, keys);
-
- DBUG_EXECUTE_IF("make_sort_keys_alloc_fail",
- DBUG_SET("+d,simulate_out_of_memory"););
-
- if ((table_sort.sort_keys=
- (uchar**) my_malloc(table_sort.keys*(param.rec_length+sizeof(char*)),
- MYF(0))))
+ param.max_keys_per_buffer= (uint) min(num_rows, keys);
+ if (table_sort.get_sort_keys())
+ {
+ // If we have already allocated a buffer, it better have same size!
+ if (!table_sort.check_sort_buffer_properties(param.max_keys_per_buffer,
+ param.rec_length))
+ {
+ /*
+ table->sort will still have a pointer to the same buffer,
+ but that will be overwritten by the assignment below.
+ */
+ table_sort.free_sort_buffer();
+ }
+ }
+ table_sort.alloc_sort_buffer(param.max_keys_per_buffer, param.rec_length);
+ if (table_sort.get_sort_keys())
break;
ulong old_memory_available= memory_available;
memory_available= memory_available/4*3;
@@ -213,40 +275,37 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
old_memory_available > min_sort_memory)
memory_available= min_sort_memory;
}
+ if (memory_available < min_sort_memory)
+ {
+ my_error(ER_OUT_OF_SORTMEMORY,MYF(ME_ERROR + ME_FATALERROR));
+ goto err;
+ }
}
- sort_keys= table_sort.sort_keys;
- param.keys= table_sort.keys;
- if (memory_available < min_sort_memory)
- {
- my_error(ER_OUT_OF_SORTMEMORY,MYF(ME_ERROR + ME_FATALERROR));
- goto err;
- }
if (open_cached_file(&buffpek_pointers,mysql_tmpdir,TEMP_PREFIX,
- DISK_BUFFER_SIZE, MYF(ME_ERROR | MY_WME)))
+ DISK_BUFFER_SIZE, MYF(MY_WME)))
goto err;
param.sort_form= table;
param.end=(param.local_sortorder=sortorder)+s_length;
- num_rows= find_all_keys(&param,
- select,
- sort_keys,
- (uchar *)(sort_keys+param.keys),
+ num_rows= find_all_keys(&param, select,
+ &table_sort,
&buffpek_pointers,
- &tempfile);
+ &tempfile,
+ pq.is_initialized() ? &pq : NULL,
+ found_rows);
if (num_rows == HA_POS_ERROR)
goto err;
+
maxbuffer= (uint) (my_b_tell(&buffpek_pointers)/sizeof(*buffpek));
if (maxbuffer == 0) // The whole set is in memory
{
- if (save_index(&param,sort_keys,(uint) num_rows, &table_sort))
+ if (save_index(&param, (uint) num_rows, &table_sort))
goto err;
}
else
{
- thd->query_plan_flags|= QPLAN_FILESORT_DISK;
-
/* filesort cannot handle zero-length records during merge. */
DBUG_ASSERT(param.sort_length != 0);
@@ -265,7 +324,7 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
/* Open cached file if it isn't open */
if (! my_b_inited(outfile) &&
open_cached_file(outfile,mysql_tmpdir,TEMP_PREFIX,READ_RECORD_BUFFER,
- MYF(ME_ERROR | MY_WME)))
+ MYF(MY_WME)))
goto err;
if (reinit_io_cache(outfile,WRITE_CACHE,0L,0,0))
goto err;
@@ -274,18 +333,20 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
Use also the space previously used by string pointers in sort_buffer
for temporary key storage.
*/
- param.keys=((param.keys *
- (param.rec_length+sizeof(char*))) /
- param.rec_length - 1);
+ param.max_keys_per_buffer=((param.max_keys_per_buffer *
+ (param.rec_length + sizeof(char*))) /
+ param.rec_length - 1);
maxbuffer--; // Offset from 0
- if (merge_many_buff(&param,(uchar*) sort_keys,buffpek,&maxbuffer,
+ if (merge_many_buff(&param,
+ (uchar*) table_sort.get_sort_keys(),
+ buffpek,&maxbuffer,
&tempfile))
goto err;
if (flush_io_cache(&tempfile) ||
reinit_io_cache(&tempfile,READ_CACHE,0L,0,0))
goto err;
if (merge_index(&param,
- (uchar*) sort_keys,
+ (uchar*) table_sort.get_sort_keys(),
buffpek,
maxbuffer,
&tempfile,
@@ -300,12 +361,11 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
}
error= 0;
- err:
+ err:
my_free(param.tmp_buffer);
if (!subselect || !subselect->is_uncacheable())
{
- my_free(sort_keys);
- table_sort.sort_keys= 0;
+ table_sort.free_sort_buffer();
my_free(buffpek);
table_sort.buffpek= 0;
table_sort.buffpek_len= 0;
@@ -336,7 +396,7 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
thd->killed == ABORT_QUERY ? "" : thd->stmt_da->message());
if (global_system_variables.log_warnings > 1)
- {
+ {
sql_print_warning("%s, host: %s, user: %s, thread: %lu, query: %-.4096s",
ER_THD(thd, ER_FILSORT_ABORT),
thd->security_ctx->host_or_ip,
@@ -351,8 +411,13 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
#ifdef SKIP_DBUG_IN_FILESORT
DBUG_POP(); /* Ok to DBUG */
#endif
- memcpy(&table->sort, &table_sort, sizeof(FILESORT_INFO));
- DBUG_PRINT("exit",("num_rows: %ld", (long) num_rows));
+
+ // Assign the copy back!
+ table->sort= table_sort;
+
+ DBUG_PRINT("exit",
+ ("num_rows: %ld examined_rows: %ld found_rows: %ld",
+ (long) num_rows, (long) *examined_rows, (long) *found_rows));
MYSQL_FILESORT_DONE(error, num_rows);
DBUG_RETURN(error ? HA_POS_ERROR : num_rows);
} /* filesort */
@@ -360,13 +425,13 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
void filesort_free_buffers(TABLE *table, bool full)
{
+ DBUG_ENTER("filesort_free_buffers");
my_free(table->sort.record_pointers);
table->sort.record_pointers= NULL;
if (full)
{
- my_free(table->sort.sort_keys);
- table->sort.sort_keys= NULL;
+ table->sort.free_sort_buffer();
my_free(table->sort.buffpek);
table->sort.buffpek= NULL;
table->sort.buffpek_len= 0;
@@ -376,6 +441,7 @@ void filesort_free_buffers(TABLE *table, bool full)
my_free(table->sort.addon_field);
table->sort.addon_buf= NULL;
table->sort.addon_field= NULL;
+ DBUG_VOID_RETURN;
}
@@ -454,8 +520,10 @@ static void dbug_print_record(TABLE *table, bool print_rowid)
}
#endif
+
/**
- Search after sort_keys and write them into tempfile.
+ Search after sort_keys, and write them into tempfile
+ (if we run out of space in the sort_keys buffer).
All produced sequences are guaranteed to be non-empty.
@param param Sorting parameter
@@ -464,19 +532,28 @@ static void dbug_print_record(TABLE *table, bool print_rowid)
@param buffpek_pointers File to write BUFFPEKs describing sorted segments
in tempfile.
@param tempfile File to write sorted sequences of sortkeys to.
+ @param pq If !NULL, use it for keeping top N elements
+ @param [out] found_rows The number of FOUND_ROWS().
+ For a query with LIMIT, this value will typically
+ be larger than the function return value.
@note
Basic idea:
@verbatim
while (get_next_sortkey())
{
- if (no free space in sort_keys buffers)
+ if (using priority queue)
+ push sort key into queue
+ else
{
- sort sort_keys buffer;
- dump sorted sequence to 'tempfile';
- dump BUFFPEK describing sequence location into 'buffpek_pointers';
+ if (no free space in sort_keys buffers)
+ {
+ sort sort_keys buffer;
+ dump sorted sequence to 'tempfile';
+ dump BUFFPEK describing sequence location into 'buffpek_pointers';
+ }
+ put sort key into 'sort_keys';
}
- put sort key into 'sort_keys';
}
if (sort_keys has some elements && dumped at least once)
sort-dump-dump as above;
@@ -490,10 +567,12 @@ static void dbug_print_record(TABLE *table, bool print_rowid)
HA_POS_ERROR on error.
*/
-static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
- uchar **sort_keys, uchar *sort_keys_buf,
+static ha_rows find_all_keys(Sort_param *param, SQL_SELECT *select,
+ Filesort_info *fs_info,
IO_CACHE *buffpek_pointers,
- IO_CACHE *tempfile)
+ IO_CACHE *tempfile,
+ Bounded_queue<uchar, uchar> *pq,
+ ha_rows *found_rows)
{
int error,flag,quick_select;
uint idx,indexpos,ref_length;
@@ -501,10 +580,9 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
my_off_t record;
TABLE *sort_form;
THD *thd= current_thd;
- volatile killed_state *killed= &thd->killed;
handler *file;
MY_BITMAP *save_read_set, *save_write_set, *save_vcol_set;
- uchar *next_sort_key= sort_keys_buf;
+
DBUG_ENTER("find_all_keys");
DBUG_PRINT("info",("using: %s",
(select ? select->quick ? "ranges" : "where":
@@ -518,10 +596,16 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
ref_pos= ref_buff;
quick_select=select && select->quick;
record=0;
+ *found_rows= 0;
flag= ((file->ha_table_flags() & HA_REC_NOT_IN_SEQ) || quick_select);
if (flag)
ref_pos= &file->ref[0];
next_pos=ref_pos;
+
+ DBUG_EXECUTE_IF("show_explain_in_find_all_keys",
+ dbug_serve_apcs(thd, 1);
+ );
+
if (!quick_select)
{
next_pos=(uchar*) 0; /* Find records in sequence */
@@ -531,7 +615,6 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
current_thd->variables.read_buff_size);
}
-
/* Remember original bitmaps */
save_read_set= sort_form->read_set;
save_write_set= sort_form->write_set;
@@ -585,7 +668,7 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
break;
}
- if (*killed)
+ if (thd->check_killed())
{
DBUG_PRINT("info",("Sort killed by user"));
if (!quick_select)
@@ -630,18 +713,23 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
if (write_record)
{
- if (idx == param->keys)
+ ++(*found_rows);
+ if (pq)
{
- if (write_keys(param, sort_keys,
- idx, buffpek_pointers, tempfile))
- DBUG_RETURN(HA_POS_ERROR);
- idx= 0;
- next_sort_key= sort_keys_buf;
- indexpos++;
+ pq->push(ref_pos);
+ idx= pq->num_elements();
+ }
+ else
+ {
+ if (idx == param->max_keys_per_buffer)
+ {
+ if (write_keys(param, fs_info, idx, buffpek_pointers, tempfile))
+ DBUG_RETURN(HA_POS_ERROR);
+ idx= 0;
+ indexpos++;
+ }
+ make_sortkey(param, fs_info->get_record_buffer(idx++), ref_pos);
}
- sort_keys[idx++]= next_sort_key;
- make_sortkey(param, next_sort_key, ref_pos);
- next_sort_key+= param->rec_length;
}
else
file->unlock_row();
@@ -670,12 +758,12 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
DBUG_RETURN(HA_POS_ERROR); /* purecov: inspected */
}
if (indexpos && idx &&
- write_keys(param, sort_keys,
- idx, buffpek_pointers, tempfile))
+ write_keys(param, fs_info, idx, buffpek_pointers, tempfile))
DBUG_RETURN(HA_POS_ERROR); /* purecov: inspected */
const ha_rows retval=
my_b_inited(tempfile) ?
(ha_rows) (my_b_tell(tempfile)/param->rec_length) : idx;
+ DBUG_PRINT("info", ("find_all_keys return %u", (uint) retval));
DBUG_RETURN(retval);
} /* find_all_keys */
@@ -703,21 +791,19 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
*/
static int
-write_keys(SORTPARAM *param, register uchar **sort_keys, uint count,
+write_keys(Sort_param *param, Filesort_info *fs_info, uint count,
IO_CACHE *buffpek_pointers, IO_CACHE *tempfile)
{
- size_t sort_length, rec_length;
+ size_t rec_length;
uchar **end;
BUFFPEK buffpek;
DBUG_ENTER("write_keys");
- sort_length= param->sort_length;
rec_length= param->rec_length;
-#ifdef MC68000
- quicksort(sort_keys,count,sort_length);
-#else
- my_string_ptr_sort((uchar*) sort_keys, (uint) count, sort_length);
-#endif
+ uchar **sort_keys= fs_info->get_sort_keys();
+
+ fs_info->sort_buffer(param, count);
+
if (!my_b_inited(tempfile) &&
open_cached_file(tempfile, mysql_tmpdir, TEMP_PREFIX, DISK_BUFFER_SIZE,
MYF(MY_WME)))
@@ -766,8 +852,8 @@ static inline void store_length(uchar *to, uint length, uint pack_length)
/** Make a sort-key from record. */
-static void make_sortkey(register SORTPARAM *param,
- register uchar *to, uchar *ref_pos)
+static void make_sortkey(register Sort_param *param,
+ register uchar *to, uchar *ref_pos)
{
reg3 Field *field;
reg1 SORT_FIELD *sort_field;
@@ -785,9 +871,9 @@ static void make_sortkey(register SORTPARAM *param,
if (field->is_null())
{
if (sort_field->reverse)
- bfill(to,sort_field->length+1,(char) 255);
+ memset(to, 255, sort_field->length+1);
else
- bzero((char*) to,sort_field->length+1);
+ memset(to, 0, sort_field->length+1);
to+= sort_field->length+1;
continue;
}
@@ -803,7 +889,7 @@ static void make_sortkey(register SORTPARAM *param,
switch (sort_field->result_type) {
case STRING_RESULT:
{
- CHARSET_INFO *cs=item->collation.collation;
+ const CHARSET_INFO *cs=item->collation.collation;
char fill_char= ((cs->state & MY_CS_BINSORT) ? (char) 0 : ' ');
int diff;
uint sort_field_length;
@@ -816,7 +902,7 @@ static void make_sortkey(register SORTPARAM *param,
if (!res)
{
if (maybe_null)
- bzero((char*) to-1,sort_field->length+1);
+ memset(to-1, 0, sort_field->length+1);
else
{
/* purecov: begin deadcode */
@@ -828,7 +914,7 @@ static void make_sortkey(register SORTPARAM *param,
DBUG_ASSERT(0);
DBUG_PRINT("warning",
("Got null on something that shouldn't be null"));
- bzero((char*) to,sort_field->length); // Avoid crash
+ memset(to, 0, sort_field->length); // Avoid crash
/* purecov: end */
}
break;
@@ -884,12 +970,19 @@ static void make_sortkey(register SORTPARAM *param,
}
if (maybe_null)
{
+ *to++=1; /* purecov: inspected */
if (item->null_value)
{
- bzero((char*) to++, sort_field->length+1);
+ if (maybe_null)
+ memset(to-1, 0, sort_field->length+1);
+ else
+ {
+ DBUG_PRINT("warning",
+ ("Got null on something that shouldn't be null"));
+ memset(to, 0, sort_field->length);
+ }
break;
}
- *to++=1; /* purecov: inspected */
}
to[7]= (uchar) value;
to[6]= (uchar) (value >> 8);
@@ -911,7 +1004,8 @@ static void make_sortkey(register SORTPARAM *param,
{
if (item->null_value)
{
- bzero((char*) to++, sort_field->length+1);
+ memset(to, 0, sort_field->length+1);
+ to++;
break;
}
*to++=1;
@@ -928,7 +1022,7 @@ static void make_sortkey(register SORTPARAM *param,
{
if (item->null_value)
{
- bzero((char*) to,sort_field->length+1);
+ memset(to, 0, sort_field->length+1);
to++;
break;
}
@@ -970,7 +1064,7 @@ static void make_sortkey(register SORTPARAM *param,
SORT_ADDON_FIELD *addonf= param->addon_field;
uchar *nulls= to;
DBUG_ASSERT(addonf != 0);
- bzero((char *) nulls, addonf->offset);
+ memset(nulls, 0, addonf->offset);
to+= addonf->offset;
for ( ; (field= addonf->field) ; addonf++)
{
@@ -1009,7 +1103,7 @@ static void make_sortkey(register SORTPARAM *param,
Register fields used by sorting in the sorted table's read set
*/
-static void register_used_fields(SORTPARAM *param)
+static void register_used_fields(Sort_param *param)
{
reg1 SORT_FIELD *sort_field;
TABLE *table=param->sort_form;
@@ -1054,21 +1148,19 @@ static void register_used_fields(SORTPARAM *param)
}
-static bool save_index(SORTPARAM *param, uchar **sort_keys, uint count,
- FILESORT_INFO *table_sort)
+static bool save_index(Sort_param *param, uint count, Filesort_info *table_sort)
{
uint offset,res_length;
uchar *to;
DBUG_ENTER("save_index");
- my_string_ptr_sort((uchar*) sort_keys, (uint) count, param->sort_length);
+ table_sort->sort_buffer(param, count);
res_length= param->res_length;
offset= param->rec_length-res_length;
- if ((ha_rows) count > param->max_rows)
- count=(uint) param->max_rows;
if (!(to= table_sort->record_pointers=
(uchar*) my_malloc(res_length*count, MYF(MY_WME))))
DBUG_RETURN(1); /* purecov: inspected */
+ uchar **sort_keys= table_sort->get_sort_keys();
for (uchar **end= sort_keys+count ; sort_keys != end ; sort_keys++)
{
memcpy(to, *sort_keys+offset, res_length);
@@ -1078,10 +1170,150 @@ static bool save_index(SORTPARAM *param, uchar **sort_keys, uint count,
}
+/**
+ Test whether priority queue is worth using to get top elements of an
+ ordered result set. If it is, then allocates buffer for required amount of
+ records
+
+ @param param Sort parameters.
+ @param filesort_info Filesort information.
+ @param table Table to sort.
+ @param num_rows Estimate of number of rows in source record set.
+ @param memory_available Memory available for sorting.
+
+ DESCRIPTION
+ Given a query like this:
+ SELECT ... FROM t ORDER BY a1,...,an LIMIT max_rows;
+ This function tests whether a priority queue should be used to keep
+ the result. Necessary conditions are:
+ - estimate that it is actually cheaper than merge-sort
+ - enough memory to store the <max_rows> records.
+
+ If we don't have space for <max_rows> records, but we *do* have
+ space for <max_rows> keys, we may rewrite 'table' to sort with
+ references to records instead of additional data.
+ (again, based on estimates that it will actually be cheaper).
+
+ @retval
+ true - if it's ok to use PQ
+ false - PQ will be slower than merge-sort, or there is not enough memory.
+*/
+
+bool check_if_pq_applicable(Sort_param *param,
+ Filesort_info *filesort_info,
+ TABLE *table, ha_rows num_rows,
+ ulong memory_available)
+{
+ DBUG_ENTER("check_if_pq_applicable");
+
+ /*
+ How much Priority Queue sort is slower than qsort.
+ Measurements (see unit test) indicate that PQ is roughly 3 times slower.
+ */
+ const double PQ_slowness= 3.0;
+
+ if (param->max_rows == HA_POS_ERROR)
+ {
+ DBUG_PRINT("info", ("No LIMIT"));
+ DBUG_RETURN(NULL);
+ }
+
+ if (param->max_rows + 2 >= UINT_MAX)
+ {
+ DBUG_PRINT("info", ("Too large LIMIT"));
+ DBUG_RETURN(NULL);
+ }
+
+ ulong num_available_keys=
+ memory_available / (param->rec_length + sizeof(char*));
+ // We need 1 extra record in the buffer, when using PQ.
+ param->max_keys_per_buffer= (uint) param->max_rows + 1;
+
+ if (num_rows < num_available_keys)
+ {
+ // The whole source set fits into memory.
+ if (param->max_rows < num_rows/PQ_slowness )
+ {
+ filesort_info->alloc_sort_buffer(param->max_keys_per_buffer,
+ param->rec_length);
+ DBUG_RETURN(filesort_info->get_sort_keys() != NULL);
+ }
+ else
+ {
+ // PQ will be slower.
+ DBUG_RETURN(false);
+ }
+ }
+
+ // Do we have space for LIMIT rows in memory?
+ if (param->max_keys_per_buffer < num_available_keys)
+ {
+ filesort_info->alloc_sort_buffer(param->max_keys_per_buffer,
+ param->rec_length);
+ DBUG_RETURN(filesort_info->get_sort_keys() != NULL);
+ }
+
+ // Try to strip off addon fields.
+ if (param->addon_field)
+ {
+ const ulong row_length=
+ param->sort_length + param->ref_length + sizeof(char*);
+ num_available_keys= memory_available / row_length;
+
+ // Can we fit all the keys in memory?
+ if (param->max_keys_per_buffer < num_available_keys)
+ {
+ const double sort_merge_cost=
+ get_merge_many_buffs_cost_fast(num_rows,
+ num_available_keys,
+ row_length);
+ /*
+ PQ has cost:
+ (insert + qsort) * log(queue size) / TIME_FOR_COMPARE_ROWID +
+ cost of file lookup afterwards.
+ The lookup cost is a bit pessimistic: we take scan_time and assume
+ that on average we find the row after scanning half of the file.
+ A better estimate would be lookup cost, but note that we are doing
+ random lookups here, rather than sequential scan.
+ */
+ const double pq_cpu_cost=
+ (PQ_slowness * num_rows + param->max_keys_per_buffer) *
+ log((double) param->max_keys_per_buffer) / TIME_FOR_COMPARE_ROWID;
+ const double pq_io_cost=
+ param->max_rows * table->file->scan_time() / 2.0;
+ const double pq_cost= pq_cpu_cost + pq_io_cost;
+
+ if (sort_merge_cost < pq_cost)
+ DBUG_RETURN(false);
+
+ filesort_info->alloc_sort_buffer(param->max_keys_per_buffer,
+ param->sort_length + param->ref_length);
+ if (filesort_info->get_sort_keys())
+ {
+ // Make attached data to be references instead of fields.
+ my_free(filesort_info->addon_buf);
+ my_free(filesort_info->addon_field);
+ filesort_info->addon_buf= NULL;
+ filesort_info->addon_field= NULL;
+ param->addon_field= NULL;
+ param->addon_length= 0;
+
+ param->res_length= param->ref_length;
+ param->sort_length+= param->ref_length;
+ param->rec_length= param->sort_length;
+
+ DBUG_RETURN(true);
+ }
+ }
+ }
+ DBUG_RETURN(false);
+}
+
+
/** Merge buffers to make < MERGEBUFF2 buffers. */
-int merge_many_buff(SORTPARAM *param, uchar *sort_buffer,
- BUFFPEK *buffpek, uint *maxbuffer, IO_CACHE *t_file)
+int merge_many_buff(Sort_param *param, uchar *sort_buffer,
+ BUFFPEK *buffpek, uint *maxbuffer, IO_CACHE *t_file)
{
register uint i;
IO_CACHE t_file2,*from_file,*to_file,*temp;
@@ -1212,12 +1444,11 @@ void reuse_freed_buff(QUEUE *queue, BUFFPEK *reuse, uint key_length)
other error
*/
-int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
+int merge_buffers(Sort_param *param, IO_CACHE *from_file,
IO_CACHE *to_file, uchar *sort_buffer,
BUFFPEK *lastbuff, BUFFPEK *Fb, BUFFPEK *Tb,
int flag)
{
- THD *thd= current_thd;
int error;
uint rec_length,res_length,offset;
size_t sort_length;
@@ -1231,18 +1462,13 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
void *first_cmp_arg;
element_count dupl_count= 0;
uchar *src;
- killed_state not_killable;
uchar *unique_buff= param->unique_buff;
- volatile killed_state *killed= &thd->killed;
+ const bool killable= !param->not_killable;
+ THD* const thd=current_thd;
DBUG_ENTER("merge_buffers");
thd->inc_status_sort_merge_passes();
thd->query_plan_fsort_passes++;
- if (param->not_killable)
- {
- killed= &not_killable;
- not_killable= NOT_KILLED;
- }
error=0;
rec_length= param->rec_length;
@@ -1255,13 +1481,12 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
(flag && min_dupl_count ? sizeof(dupl_count) : 0)-res_length);
uint wr_len= flag ? res_length : rec_length;
uint wr_offset= flag ? offset : 0;
- maxcount= (ulong) (param->keys/((uint) (Tb-Fb) +1));
+ maxcount= (ulong) (param->max_keys_per_buffer/((uint) (Tb-Fb) +1));
to_start_filepos= my_b_tell(to_file);
strpos= sort_buffer;
org_max_rows=max_rows= param->max_rows;
-
- /* The following will fire if there is not enough space in sort_buffer */
- DBUG_ASSERT(maxcount!=0);
+
+ set_if_bigger(maxcount, 1);
if (unique_buff)
{
@@ -1320,7 +1545,7 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
while (queue.elements > 1)
{
- if (*killed)
+ if (killable && thd->check_killed())
{
error= 1; goto err; /* purecov: inspected */
}
@@ -1396,7 +1621,7 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
}
buffpek= (BUFFPEK*) queue_top(&queue);
buffpek->base= (uchar*) sort_buffer;
- buffpek->max_keys= param->keys;
+ buffpek->max_keys= param->max_keys_per_buffer;
/*
As we know all entries in the buffer are unique, we only have to
@@ -1486,9 +1711,9 @@ err:
/* Do a merge to output-file (save only positions) */
-int merge_index(SORTPARAM *param, uchar *sort_buffer,
- BUFFPEK *buffpek, uint maxbuffer,
- IO_CACHE *tempfile, IO_CACHE *outfile)
+int merge_index(Sort_param *param, uchar *sort_buffer,
+ BUFFPEK *buffpek, uint maxbuffer,
+ IO_CACHE *tempfile, IO_CACHE *outfile)
{
DBUG_ENTER("merge_index");
if (merge_buffers(param,tempfile,outfile,sort_buffer,buffpek,buffpek,
@@ -1534,7 +1759,7 @@ sortlength(THD *thd, SORT_FIELD *sortorder, uint s_length,
bool *multi_byte_charset)
{
reg2 uint length;
- CHARSET_INFO *cs;
+ const CHARSET_INFO *cs;
*multi_byte_charset= 0;
length=0;
@@ -1635,7 +1860,8 @@ sortlength(THD *thd, SORT_FIELD *sortorder, uint s_length,
*/
static SORT_ADDON_FIELD *
-get_addon_fields(THD *thd, Field **ptabfield, uint sortlength, uint *plength)
+get_addon_fields(ulong max_length_for_sort_data,
+ Field **ptabfield, uint sortlength, uint *plength)
{
Field **pfield;
Field *field;
@@ -1672,7 +1898,7 @@ get_addon_fields(THD *thd, Field **ptabfield, uint sortlength, uint *plength)
return 0;
length+= (null_fields+7)/8;
- if (length+sortlength > thd->variables.max_length_for_sort_data ||
+ if (length+sortlength > max_length_for_sort_data ||
!(addonf= (SORT_ADDON_FIELD *) my_malloc(sizeof(SORT_ADDON_FIELD)*
(fields+1), MYF(MY_WME))))
return 0;
@@ -1755,7 +1981,7 @@ void change_double_for_sort(double nr,uchar *to)
if (nr == 0.0)
{ /* Change to zero string */
tmp[0]=(uchar) 128;
- bzero((char*) tmp+1,sizeof(nr)-1);
+ memset(tmp+1, 0, sizeof(nr)-1);
}
else
{
diff --git a/sql/filesort.h b/sql/filesort.h
index 8ee8999d055..8960fa6cb66 100644
--- a/sql/filesort.h
+++ b/sql/filesort.h
@@ -29,10 +29,8 @@ typedef struct st_sort_field SORT_FIELD;
ha_rows filesort(THD *thd, TABLE *table, st_sort_field *sortorder,
uint s_length, SQL_SELECT *select,
ha_rows max_rows, bool sort_positions,
- ha_rows *examined_rows);
+ ha_rows *examined_rows, ha_rows *found_rows);
void filesort_free_buffers(TABLE *table, bool full);
-double get_merge_many_buffs_cost(uint *buffer, uint last_n_elems,
- int elem_size);
void change_double_for_sort(double nr,uchar *to);
#endif /* FILESORT_INCLUDED */
diff --git a/sql/filesort_utils.cc b/sql/filesort_utils.cc
new file mode 100644
index 00000000000..c4480a6376d
--- /dev/null
+++ b/sql/filesort_utils.cc
@@ -0,0 +1,143 @@
+/* Copyright (c) 2010, 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
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "filesort_utils.h"
+#include "sql_const.h"
+#include "sql_sort.h"
+#include "table.h"
+#include "my_sys.h"
+
+
+namespace {
+/**
+ A local helper function. See comments for get_merge_buffers_cost().
+ */
+double get_merge_cost(ha_rows num_elements, ha_rows num_buffers, uint elem_size)
+{
+ return
+ 2.0 * ((double) num_elements * elem_size) / IO_SIZE
+ + (double) num_elements * log((double) num_buffers) /
+ (TIME_FOR_COMPARE_ROWID * M_LN2);
+}
+}
+
+/**
+ This is a simplified, and faster version of @see get_merge_many_buffs_cost().
+ We calculate the cost of merging buffers, by simulating the actions
+ of @see merge_many_buff. For explanations of formulas below,
+ see comments for get_merge_buffers_cost().
+ TODO: Use this function for Unique::get_use_cost().
+*/
+double get_merge_many_buffs_cost_fast(ha_rows num_rows,
+ ha_rows num_keys_per_buffer,
+ uint elem_size)
+{
+ ha_rows num_buffers= num_rows / num_keys_per_buffer;
+ ha_rows last_n_elems= num_rows % num_keys_per_buffer;
+ double total_cost;
+
+ // Calculate CPU cost of sorting buffers.
+ total_cost=
+ ( num_buffers * num_keys_per_buffer * log(1.0 + num_keys_per_buffer) +
+ last_n_elems * log(1.0 + last_n_elems) )
+ / TIME_FOR_COMPARE_ROWID;
+
+ // Simulate behavior of merge_many_buff().
+ while (num_buffers >= MERGEBUFF2)
+ {
+ // Calculate # of calls to merge_buffers().
+ const ha_rows loop_limit= num_buffers - MERGEBUFF*3/2;
+ const ha_rows num_merge_calls= 1 + loop_limit/MERGEBUFF;
+ const ha_rows num_remaining_buffs=
+ num_buffers - num_merge_calls * MERGEBUFF;
+
+ // Cost of merge sort 'num_merge_calls'.
+ total_cost+=
+ num_merge_calls *
+ get_merge_cost(num_keys_per_buffer * MERGEBUFF, MERGEBUFF, elem_size);
+
+ // # of records in remaining buffers.
+ last_n_elems+= num_remaining_buffs * num_keys_per_buffer;
+
+ // Cost of merge sort of remaining buffers.
+ total_cost+=
+ get_merge_cost(last_n_elems, 1 + num_remaining_buffs, elem_size);
+
+ num_buffers= num_merge_calls;
+ num_keys_per_buffer*= MERGEBUFF;
+ }
+
+ // Simulate final merge_buff call.
+ last_n_elems+= num_keys_per_buffer * num_buffers;
+ total_cost+= get_merge_cost(last_n_elems, 1 + num_buffers, elem_size);
+ return total_cost;
+}
+
+uchar **Filesort_buffer::alloc_sort_buffer(uint num_records, uint record_length)
+{
+ ulong sort_buff_sz;
+
+ DBUG_ENTER("alloc_sort_buffer");
+
+ DBUG_EXECUTE_IF("alloc_sort_buffer_fail",
+ DBUG_SET("+d,simulate_out_of_memory"););
+
+ if (m_idx_array.is_null())
+ {
+ sort_buff_sz= num_records * (record_length + sizeof(uchar*));
+ set_if_bigger(sort_buff_sz, record_length * MERGEBUFF2);
+ uchar **sort_keys=
+ (uchar**) my_malloc(sort_buff_sz, MYF(0));
+ m_idx_array= Idx_array(sort_keys, num_records);
+ m_record_length= record_length;
+ uchar **start_of_data= m_idx_array.array() + m_idx_array.size();
+ m_start_of_data= reinterpret_cast<uchar*>(start_of_data);
+ }
+ else
+ {
+ DBUG_ASSERT(num_records == m_idx_array.size());
+ DBUG_ASSERT(record_length == m_record_length);
+ }
+ DBUG_RETURN(m_idx_array.array());
+}
+
+
+void Filesort_buffer::free_sort_buffer()
+{
+ my_free(m_idx_array.array());
+ m_idx_array= Idx_array();
+ m_record_length= 0;
+ m_start_of_data= NULL;
+}
+
+
+void Filesort_buffer::sort_buffer(const Sort_param *param, uint count)
+{
+ if (count <= 1)
+ return;
+ uchar **keys= get_sort_keys();
+ uchar **buffer= NULL;
+ if (radixsort_is_appliccable(count, param->sort_length) &&
+ (buffer= (uchar**) my_malloc(count*sizeof(char*), MYF(0))))
+ {
+ radixsort_for_str_ptr(keys, count, param->sort_length, buffer);
+ my_free(buffer);
+ return;
+ }
+
+ size_t size= param->sort_length;
+ my_qsort2(keys, count, sizeof(uchar*), get_ptr_compare(size), &size);
+}
+
diff --git a/sql/filesort_utils.h b/sql/filesort_utils.h
new file mode 100644
index 00000000000..4cccf8ffa02
--- /dev/null
+++ b/sql/filesort_utils.h
@@ -0,0 +1,129 @@
+/* Copyright (c) 2010, 2012 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
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef FILESORT_UTILS_INCLUDED
+#define FILESORT_UTILS_INCLUDED
+
+#include "my_global.h"
+#include "my_base.h"
+#include "sql_array.h"
+
+class Sort_param;
+/*
+ Calculate cost of merge sort
+
+ @param num_rows Total number of rows.
+ @param num_keys_per_buffer Number of keys per buffer.
+ @param elem_size Size of each element.
+
+ Calculates cost of merge sort by simulating call to merge_many_buff().
+
+ @retval
+ Computed cost of merge sort in disk seeks.
+
+ @note
+ Declared here in order to be able to unit test it,
+ since library dependencies have not been sorted out yet.
+
+ See also comments get_merge_many_buffs_cost().
+*/
+
+double get_merge_many_buffs_cost_fast(ha_rows num_rows,
+ ha_rows num_keys_per_buffer,
+ uint elem_size);
+
+
+/**
+ A wrapper class around the buffer used by filesort().
+ The buffer is a contiguous chunk of memory,
+ where the first part is <num_records> pointers to the actual data.
+
+ We wrap the buffer in order to be able to do lazy initialization of the
+ pointers: the buffer is often much larger than what we actually need.
+
+ The buffer must be kept available for multiple executions of the
+ same sort operation, so we have explicit allocate and free functions,
+ rather than doing alloc/free in CTOR/DTOR.
+*/
+class Filesort_buffer
+{
+public:
+ Filesort_buffer() :
+ m_idx_array(), m_record_length(0), m_start_of_data(NULL)
+ {}
+
+ /** Sort me... */
+ void sort_buffer(const Sort_param *param, uint count);
+
+ /// Initializes a record pointer.
+ uchar *get_record_buffer(uint idx)
+ {
+ m_idx_array[idx]= m_start_of_data + (idx * m_record_length);
+ return m_idx_array[idx];
+ }
+
+ /// Initializes all the record pointers.
+ void init_record_pointers()
+ {
+ for (uint ix= 0; ix < m_idx_array.size(); ++ix)
+ (void) get_record_buffer(ix);
+ }
+
+ /// Returns total size: pointer array + record buffers.
+ size_t sort_buffer_size() const
+ {
+ return m_idx_array.size() * (m_record_length + sizeof(uchar*));
+ }
+
+ /// Allocates the buffer, but does *not* initialize pointers.
+ uchar **alloc_sort_buffer(uint num_records, uint record_length);
+
+
+ /// Check <num_records, record_length> for the buffer
+ bool check_sort_buffer_properties(uint num_records, uint record_length)
+ {
+ return (static_cast<uint>(m_idx_array.size()) == num_records &&
+ m_record_length == m_record_length);
+ }
+
+ /// Frees the buffer.
+ void free_sort_buffer();
+
+ /// Getter, for calling routines which still use the uchar** interface.
+ uchar **get_sort_keys() { return m_idx_array.array(); }
+
+ /**
+ We need an assignment operator, see filesort().
+ This happens to have the same semantics as the one that would be
+ generated by the compiler. We still implement it here, to show shallow
+ assignment explicitly: we have two objects sharing the same array.
+ */
+ Filesort_buffer &operator=(const Filesort_buffer &rhs)
+ {
+ m_idx_array= rhs.m_idx_array;
+ m_record_length= rhs.m_record_length;
+ m_start_of_data= rhs.m_start_of_data;
+ return *this;
+ }
+
+private:
+ typedef Bounds_checked_array<uchar*> Idx_array;
+
+ Idx_array m_idx_array;
+ uint m_record_length;
+ uchar *m_start_of_data;
+};
+
+#endif // FILESORT_UTILS_INCLUDED
diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc
index f91e19df08e..59b9d6eab6b 100644
--- a/sql/ha_ndbcluster_binlog.cc
+++ b/sql/ha_ndbcluster_binlog.cc
@@ -1295,7 +1295,9 @@ int ndbcluster_log_schema_op(THD *thd, NDB_SHARE *share,
DBUG_RETURN(0);
}
- char tmp_buf2[FN_REFLEN];
+ char tmp_buf2_mem[FN_REFLEN];
+ String tmp_buf2(tmp_buf2_mem, sizeof(tmp_buf2_mem), system_charset_info);
+ tmp_buf2.length(0);
const char *type_str;
switch (type)
{
@@ -1304,17 +1306,24 @@ int ndbcluster_log_schema_op(THD *thd, NDB_SHARE *share,
if (thd->lex->sql_command == SQLCOM_DROP_DB)
DBUG_RETURN(0);
/* redo the drop table query as is may contain several tables */
- query= tmp_buf2;
- query_length= (uint) (strxmov(tmp_buf2, "drop table `",
- table_name, "`", NullS) - tmp_buf2);
+ tmp_buf2.append(STRING_WITH_LEN("drop table "));
+ append_identifier(thd, &tmp_buf2, table_name, strlen(table_name));
+ query= tmp_buf2.c_ptr_safe();
+ query_length= tmp_buf2.length();
type_str= "drop table";
break;
case SOT_RENAME_TABLE:
/* redo the rename table query as is may contain several tables */
- query= tmp_buf2;
- query_length= (uint) (strxmov(tmp_buf2, "rename table `",
- db, ".", table_name, "` to `",
- new_db, ".", new_table_name, "`", NullS) - tmp_buf2);
+ tmp_buf2.append(STRING_WITH_LEN("rename table "));
+ append_identifier(thd, &tmp_buf2, db, strlen(db));
+ tmp_buf2.append(STRING_WITH_LEN("."));
+ append_identifier(thd, &tmp_buf2, table_name, strlen(table_name));
+ tmp_buf2.append(STRING_WITH_LEN(" to "));
+ append_identifier(thd, &tmp_buf2, new_db, strlen(new_db));
+ tmp_buf2.append(STRING_WITH_LEN("."));
+ append_identifier(thd, &tmp_buf2, new_table_name, strlen(new_table_name));
+ query= tmp_buf2.c_ptr_safe();
+ query_length= tmp_buf2.length();
type_str= "rename table";
break;
case SOT_CREATE_TABLE:
diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc
index 0b46ca4123c..4bc2b2439b4 100644
--- a/sql/ha_partition.cc
+++ b/sql/ha_partition.cc
@@ -2076,6 +2076,133 @@ partition_element *ha_partition::find_partition_element(uint part_id)
return NULL;
}
+uint ha_partition::count_query_cache_dependant_tables(uint8 *tables_type)
+{
+ DBUG_ENTER("ha_partition::count_query_cache_dependant_tables");
+ /* Here we rely on the fact that all tables are of the same type */
+ uint8 type= m_file[0]->table_cache_type();
+ (*tables_type)|= type;
+ DBUG_PRINT("info", ("cnt: %u", (uint)m_tot_parts));
+ /*
+ We need save underlying tables only for HA_CACHE_TBL_ASKTRANSACT:
+ HA_CACHE_TBL_NONTRANSACT - because all changes goes through partition table
+ HA_CACHE_TBL_NOCACHE - because will not be cached
+ HA_CACHE_TBL_TRANSACT - QC need to know that such type present
+ */
+ DBUG_RETURN(type == HA_CACHE_TBL_ASKTRANSACT ? m_tot_parts : 0);
+}
+
+my_bool ha_partition::reg_query_cache_dependant_table(THD *thd,
+ char *key, uint key_len,
+ uint8 type,
+ Query_cache *cache,
+ Query_cache_block_table **block_table,
+ handler *file,
+ uint *n)
+{
+ DBUG_ENTER("ha_partition::reg_query_cache_dependant_table");
+ qc_engine_callback engine_callback;
+ ulonglong engine_data;
+ /* ask undelying engine */
+ if (!file->register_query_cache_table(thd, key,
+ key_len,
+ &engine_callback,
+ &engine_data))
+ {
+ DBUG_PRINT("qcache", ("Handler does not allow caching for %s.%s",
+ key,
+ key + table_share->db.length + 1));
+ /*
+ As this can change from call to call, don't reset set
+ thd->lex->safe_to_cache_query
+ */
+ thd->query_cache_is_applicable= 0; // Query can't be cached
+ DBUG_RETURN(TRUE);
+ }
+ (++(*block_table))->n= ++(*n);
+ if (!cache->insert_table(key_len,
+ key, (*block_table),
+ table_share->db.length,
+ type,
+ engine_callback, engine_data,
+ FALSE))
+ DBUG_RETURN(TRUE);
+ DBUG_RETURN(FALSE);
+}
+
+
+my_bool ha_partition::register_query_cache_dependant_tables(THD *thd,
+ Query_cache *cache,
+ Query_cache_block_table **block_table,
+ uint *n)
+{
+ char *name;
+ uint prefix_length= table_share->table_cache_key.length + 3;
+ uint num_parts= m_part_info->num_parts;
+ uint num_subparts= m_part_info->num_subparts;
+ uint i= 0;
+ List_iterator<partition_element> part_it(m_part_info->partitions);
+ char key[FN_REFLEN];
+
+ DBUG_ENTER("ha_partition::register_query_cache_dependant_tables");
+
+ /* see ha_partition::count_query_cache_dependant_tables */
+ if (m_file[0]->table_cache_type() != HA_CACHE_TBL_ASKTRANSACT)
+ DBUG_RETURN(FALSE); // nothing to register
+
+ /* prepare static part of the key */
+ memmove(key, table_share->table_cache_key.str,
+ table_share->table_cache_key.length);
+
+ name= key + table_share->table_cache_key.length - 1;
+ name[0]= name[2]= '#';
+ name[1]= 'P';
+ name+= 3;
+
+ do
+ {
+ partition_element *part_elem= part_it++;
+ uint part_len= strmov(name, part_elem->partition_name) - name;
+ if (m_is_sub_partitioned)
+ {
+ List_iterator<partition_element> subpart_it(part_elem->subpartitions);
+ partition_element *sub_elem;
+ char *sname= name + part_len;
+ uint j= 0, part;
+ sname[0]= sname[3]= '#';
+ sname[1]= 'S';
+ sname[2]= 'P';
+ sname += 4;
+ do
+ {
+ sub_elem= subpart_it++;
+ part= i * num_subparts + j;
+ uint spart_len= strmov(sname, sub_elem->partition_name) - name + 1;
+ if (reg_query_cache_dependant_table(thd, key,
+ prefix_length + part_len + 4 +
+ spart_len,
+ m_file[part]->table_cache_type(),
+ cache,
+ block_table, m_file[part],
+ n))
+ DBUG_RETURN(TRUE);
+ } while (++j < num_subparts);
+ }
+ else
+ {
+ if (reg_query_cache_dependant_table(thd, key,
+ prefix_length + part_len + 1,
+ m_file[i]->table_cache_type(),
+ cache,
+ block_table, m_file[i],
+ n))
+ DBUG_RETURN(TRUE);
+ }
+ } while (++i < num_parts);
+ DBUG_PRINT("info", ("cnt: %u", (uint)m_tot_parts));
+ DBUG_RETURN(FALSE);
+}
+
/*
Set up table share object before calling create on underlying handler
@@ -3739,7 +3866,7 @@ int ha_partition::truncate_partition(Alter_info *alter_info, bool *binlog_stmt)
{
List_iterator<partition_element>
subpart_it(part_elem->subpartitions);
- partition_element __attribute__((unused)) *sub_elem;
+ partition_element *sub_elem __attribute__((unused));
uint j= 0, part;
do
{
@@ -6392,7 +6519,17 @@ ha_rows ha_partition::min_rows_for_estimate()
DBUG_ENTER("ha_partition::min_rows_for_estimate");
tot_used_partitions= bitmap_bits_set(&m_part_info->used_partitions);
- DBUG_ASSERT(tot_used_partitions);
+
+ /*
+ All partitions might have been left as unused during partition pruning
+ due to, for example, an impossible WHERE condition. Nonetheless, the
+ optimizer might still attempt to perform (e.g. range) analysis where an
+ estimate of the the number of rows is calculated using records_in_range.
+ Hence, to handle this and other possible cases, use zero as the minimum
+ number of rows to base the estimate on if no partition is being used.
+ */
+ if (!tot_used_partitions)
+ DBUG_RETURN(0);
/*
Allow O(log2(tot_partitions)) increase in number of used partitions.
diff --git a/sql/ha_partition.h b/sql/ha_partition.h
index b6b0e2a015d..ee2bd0e39b9 100644
--- a/sql/ha_partition.h
+++ b/sql/ha_partition.h
@@ -546,22 +546,20 @@ public:
virtual int extra(enum ha_extra_function operation);
virtual int extra_opt(enum ha_extra_function operation, ulong cachesize);
virtual int reset(void);
- /*
- Do not allow caching of partitioned tables, since we cannot return
- a callback or engine_data that would work for a generic engine.
- */
- virtual my_bool register_query_cache_table(THD *thd, char *table_key,
- uint key_length,
- qc_engine_callback
- *engine_callback,
- ulonglong *engine_data)
- {
- *engine_callback= NULL;
- *engine_data= 0;
- return FALSE;
- }
+ virtual uint count_query_cache_dependant_tables(uint8 *tables_type);
+ virtual my_bool
+ register_query_cache_dependant_tables(THD *thd,
+ Query_cache *cache,
+ Query_cache_block_table **block,
+ uint *n);
private:
+ my_bool reg_query_cache_dependant_table(THD *thd,
+ char *key, uint key_len, uint8 type,
+ Query_cache *cache,
+ Query_cache_block_table
+ **block_table,
+ handler *file, uint *n);
static const uint NO_CURRENT_PART_ID;
int loop_extra(enum ha_extra_function operation);
void late_extra_cache(uint partition_id);
diff --git a/sql/handler.cc b/sql/handler.cc
index ae9345d7673..7471fd8247d 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -642,6 +642,43 @@ void ha_checkpoint_state(bool disable)
}
+struct st_commit_checkpoint_request {
+ void *cookie;
+ void (*pre_hook)(void *);
+};
+
+static my_bool commit_checkpoint_request_handlerton(THD *unused1, plugin_ref plugin,
+ void *data)
+{
+ st_commit_checkpoint_request *st= (st_commit_checkpoint_request *)data;
+ handlerton *hton= plugin_data(plugin, handlerton *);
+ if (hton->state == SHOW_OPTION_YES && hton->commit_checkpoint_request)
+ {
+ void *cookie= st->cookie;
+ if (st->pre_hook)
+ (*st->pre_hook)(cookie);
+ (*hton->commit_checkpoint_request)(hton, cookie);
+ }
+ return FALSE;
+}
+
+
+/*
+ Invoke commit_checkpoint_request() in all storage engines that implement it.
+
+ If pre_hook is non-NULL, the hook will be called prior to each invocation.
+*/
+void
+ha_commit_checkpoint_request(void *cookie, void (*pre_hook)(void *))
+{
+ st_commit_checkpoint_request st;
+ st.cookie= cookie;
+ st.pre_hook= pre_hook;
+ plugin_foreach(NULL, commit_checkpoint_request_handlerton,
+ MYSQL_STORAGE_ENGINE_PLUGIN, &st);
+}
+
+
static my_bool closecon_handlerton(THD *thd, plugin_ref plugin,
void *unused)
@@ -1287,11 +1324,13 @@ int ha_commit_trans(THD *thd, bool all)
goto done;
}
+ DEBUG_SYNC(thd, "ha_commit_trans_before_log_and_order");
cookie= tc_log->log_and_order(thd, xid, all, need_prepare_ordered,
need_commit_ordered);
if (!cookie)
goto err;
+ DEBUG_SYNC(thd, "ha_commit_trans_after_log_and_order");
DBUG_EXECUTE_IF("crash_commit_after_log", DBUG_SUICIDE(););
error= commit_one_phase_2(thd, all, trans, is_real_trans) ? 2 : 0;
@@ -1783,6 +1822,17 @@ bool mysql_xa_recover(THD *thd)
DBUG_RETURN(0);
}
+/*
+ Called by engine to notify TC that a new commit checkpoint has been reached.
+ See comments on handlerton method commit_checkpoint_request() for details.
+*/
+void
+commit_checkpoint_notify_ha(handlerton *hton, void *cookie)
+{
+ tc_log->commit_checkpoint_notify(cookie);
+}
+
+
/**
@details
This function should be called when MySQL sends rows of a SELECT result set
@@ -2698,6 +2748,8 @@ int handler::update_auto_increment()
bool append= FALSE;
THD *thd= table->in_use;
struct system_variables *variables= &thd->variables;
+ int result=0, tmp;
+ enum enum_check_fields save_count_cuted_fields;
DBUG_ENTER("handler::update_auto_increment");
/*
@@ -2715,8 +2767,10 @@ int handler::update_auto_increment()
statement (case of INSERT VALUES(null),(3763),(null):
the last NULL needs to insert 3764, not the value of the first NULL plus
1).
+ Ignore negative values.
*/
- adjust_next_insert_id_after_explicit_value(nr);
+ if ((longlong) nr > 0 || (table->next_number_field->flags & UNSIGNED_FLAG))
+ adjust_next_insert_id_after_explicit_value(nr);
insert_id_for_cur_row= 0; // didn't generate anything
DBUG_RETURN(0);
}
@@ -2750,8 +2804,19 @@ int handler::update_auto_increment()
reservation means potentially losing unused values).
Note that in prelocked mode no estimation is given.
*/
+
if ((auto_inc_intervals_count == 0) && (estimation_rows_to_insert > 0))
nb_desired_values= estimation_rows_to_insert;
+ else if ((auto_inc_intervals_count == 0) &&
+ (thd->lex->many_values.elements > 0))
+ {
+ /*
+ For multi-row inserts, if the bulk inserts cannot be started, the
+ handler::estimation_rows_to_insert will not be set. But we still
+ want to reserve the autoinc values.
+ */
+ nb_desired_values= thd->lex->many_values.elements;
+ }
else /* go with the increasing defaults */
{
/* avoid overflow in formula, with this if() */
@@ -2764,7 +2829,6 @@ int handler::update_auto_increment()
else
nb_desired_values= AUTO_INC_DEFAULT_NB_MAX;
}
- /* This call ignores all its parameters but nr, currently */
get_auto_increment(variables->auto_increment_offset,
variables->auto_increment_increment,
nb_desired_values, &nr,
@@ -2801,29 +2865,24 @@ int handler::update_auto_increment()
}
if (unlikely(nr == ULONGLONG_MAX))
- DBUG_RETURN(HA_ERR_AUTOINC_ERANGE);
+ DBUG_RETURN(HA_ERR_AUTOINC_ERANGE);
- DBUG_PRINT("info",("auto_increment: %lu", (ulong) nr));
+ DBUG_PRINT("info",("auto_increment: %llu nb_reserved_values: %llu",
+ nr, nb_reserved_values));
- if (unlikely(table->next_number_field->store((longlong) nr, TRUE)))
- {
- /*
- first test if the query was aborted due to strict mode constraints
- */
- if (killed_mask_hard(thd->killed) == KILL_BAD_DATA)
- DBUG_RETURN(HA_ERR_AUTOINC_ERANGE);
+ /* Store field without warning (Warning will be printed by insert) */
+ save_count_cuted_fields= thd->count_cuted_fields;
+ thd->count_cuted_fields= CHECK_FIELD_IGNORE;
+ tmp= table->next_number_field->store((longlong) nr, TRUE);
+ thd->count_cuted_fields= save_count_cuted_fields;
+ if (unlikely(tmp)) // Out of range value in store
+ {
/*
- field refused this value (overflow) and truncated it, use the result of
- the truncation (which is going to be inserted); however we try to
- decrease it to honour auto_increment_* variables.
- That will shift the left bound of the reserved interval, we don't
- bother shifting the right bound (anyway any other value from this
- interval will cause a duplicate key).
+ It's better to return an error here than getting a confusing
+ 'duplicate key error' later.
*/
- nr= prev_insert_id(table->next_number_field->val_int(), variables);
- if (unlikely(table->next_number_field->store((longlong) nr, TRUE)))
- nr= table->next_number_field->val_int();
+ result= HA_ERR_AUTOINC_ERANGE;
}
if (append)
{
@@ -2845,6 +2904,10 @@ int handler::update_auto_increment()
already set.
*/
insert_id_for_cur_row= nr;
+
+ if (result) // overflow
+ DBUG_RETURN(result);
+
/*
Set next insert id to point to next auto-increment value to be able to
handle multi-row statements.
@@ -2968,7 +3031,7 @@ void handler::ha_release_auto_increment()
}
-void handler::print_keydup_error(uint key_nr, const char *msg)
+void handler::print_keydup_error(uint key_nr, const char *msg, myf errflag)
{
/* Write the duplicated key in the error message */
char key[MAX_KEY_LENGTH];
@@ -2978,7 +3041,7 @@ void handler::print_keydup_error(uint key_nr, const char *msg)
{
/* Key is unknown */
str.copy("", 0, system_charset_info);
- my_printf_error(ER_DUP_ENTRY, msg, MYF(0), str.c_ptr(), "*UNKNOWN*");
+ my_printf_error(ER_DUP_ENTRY, msg, errflag, str.c_ptr(), "*UNKNOWN*");
}
else
{
@@ -2991,7 +3054,7 @@ void handler::print_keydup_error(uint key_nr, const char *msg)
str.append(STRING_WITH_LEN("..."));
}
my_printf_error(ER_DUP_ENTRY, msg,
- MYF(0), str.c_ptr_safe(), table->key_info[key_nr].name);
+ errflag, str.c_ptr_safe(), table->key_info[key_nr].name);
}
}
@@ -3059,7 +3122,7 @@ void handler::print_error(int error, myf errflag)
uint key_nr=get_dup_key(error);
if ((int) key_nr >= 0)
{
- print_keydup_error(key_nr, ER(ER_DUP_ENTRY_WITH_KEY_NAME));
+ print_keydup_error(key_nr, ER(ER_DUP_ENTRY_WITH_KEY_NAME), errflag);
DBUG_VOID_RETURN;
}
}
@@ -3178,7 +3241,7 @@ void handler::print_error(int error, myf errflag)
textno=ER_TABLE_DEF_CHANGED;
break;
case HA_ERR_NO_SUCH_TABLE:
- my_error(ER_NO_SUCH_TABLE, errflag, table_share->db.str,
+ my_error(ER_NO_SUCH_TABLE_IN_ENGINE, errflag, table_share->db.str,
table_share->table_name.str);
DBUG_VOID_RETURN;
case HA_ERR_RBR_LOGGING_FAILED:
@@ -3206,7 +3269,10 @@ void handler::print_error(int error, myf errflag)
textno= ER_AUTOINC_READ_FAILED;
break;
case HA_ERR_AUTOINC_ERANGE:
- textno= ER_WARN_DATA_OUT_OF_RANGE;
+ textno= error;
+ my_error(textno, errflag, table->next_number_field->field_name,
+ table->in_use->warning_info->current_row_for_warning());
+ DBUG_VOID_RETURN;
break;
case HA_ERR_TOO_MANY_CONCURRENT_TRXS:
textno= ER_TOO_MANY_CONCURRENT_TRXS;
@@ -4991,10 +5057,14 @@ bool ha_show_status(THD *thd, handlerton *db_type, enum ha_stat_type stat)
db_type->show_status(db_type, thd, stat_print, stat) ? 1 : 0;
}
- if (!result)
+ /*
+ We also check thd->is_error() as Innodb may return 0 even if
+ there was an error.
+ */
+ if (!result && !thd->is_error())
my_eof(thd);
else if (!thd->is_error())
- my_error(ER_GET_ERRNO, MYF(0), 0);
+ my_error(ER_GET_ERRNO, MYF(0), errno);
return result;
}
@@ -5283,6 +5353,8 @@ int handler::ha_write_row(uchar *buf)
rows_changed++;
if (unlikely(error= binlog_log_row(table, 0, buf, log_func)))
DBUG_RETURN(error); /* purecov: inspected */
+
+ DEBUG_SYNC_C("ha_write_row_end");
DBUG_RETURN(0);
}
diff --git a/sql/handler.h b/sql/handler.h
index 3a6f0070567..f043f1b44cd 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -616,6 +616,7 @@ enum enum_schema_tables
SCH_COLUMN_PRIVILEGES,
SCH_ENGINES,
SCH_EVENTS,
+ SCH_EXPLAIN,
SCH_FILES,
SCH_GLOBAL_STATUS,
SCH_GLOBAL_VARIABLES,
@@ -992,6 +993,46 @@ struct handlerton
int (*recover)(handlerton *hton, XID *xid_list, uint len);
int (*commit_by_xid)(handlerton *hton, XID *xid);
int (*rollback_by_xid)(handlerton *hton, XID *xid);
+ /*
+ The commit_checkpoint_request() handlerton method is used to checkpoint
+ the XA recovery process for storage engines that support two-phase
+ commit.
+
+ The method is optional - an engine that does not implemented is expected
+ to work the traditional way, where every commit() durably flushes the
+ transaction to disk in the engine before completion, so XA recovery will
+ no longer be needed for that transaction.
+
+ An engine that does implement commit_checkpoint_request() is also
+ expected to implement commit_ordered(), so that ordering of commits is
+ consistent between 2pc participants. Such engine is no longer required to
+ durably flush to disk transactions in commit(), provided that the
+ transaction has been successfully prepare()d and commit_ordered(); thus
+ potentionally saving one fsync() call. (Engine must still durably flush
+ to disk in commit() when no prepare()/commit_ordered() steps took place,
+ at least if durable commits are wanted; this happens eg. if binlog is
+ disabled).
+
+ The TC will periodically (eg. once per binlog rotation) call
+ commit_checkpoint_request(). When this happens, the engine must arrange
+ for all transaction that have completed commit_ordered() to be durably
+ flushed to disk (this does not include transactions that might be in the
+ middle of executing commit_ordered()). When such flush has completed, the
+ engine must call commit_checkpoint_notify_ha(), passing back the opaque
+ "cookie".
+
+ The flush and call of commit_checkpoint_notify_ha() need not happen
+ immediately - it can be scheduled and performed asynchroneously (ie. as
+ part of next prepare(), or sync every second, or whatever), but should
+ not be postponed indefinitely. It is however also permissible to do it
+ immediately, before returning from commit_checkpoint_request().
+
+ When commit_checkpoint_notify_ha() is called, the TC will know that the
+ transactions are durably committed, and thus no longer require XA
+ recovery. It uses that to reduce the work needed for any subsequent XA
+ recovery process.
+ */
+ void (*commit_checkpoint_request)(handlerton *hton, void *cookie);
/*
"Disable or enable checkpointing internal to the storage engine. This is
used for FLUSH TABLES WITH READ LOCK AND DISABLE CHECKPOINT to ensure that
@@ -1742,6 +1783,8 @@ public:
virtual ~handler_add_index() {}
};
+class Query_cache;
+struct Query_cache_block_table;
/**
The handler class is the interface for dynamically loadable
storage engines. Do not add ifdefs and take care when adding or
@@ -2011,7 +2054,7 @@ public:
void adjust_next_insert_id_after_explicit_value(ulonglong nr);
int update_auto_increment();
- void print_keydup_error(uint key_nr, const char *msg);
+ void print_keydup_error(uint key_nr, const char *msg, myf errflag);
virtual void print_error(int error, myf errflag);
virtual bool get_error_message(int error, String *buf);
uint get_dup_key(int error);
@@ -2083,7 +2126,8 @@ public:
if (!error ||
((flags & HA_CHECK_DUP_KEY) &&
(error == HA_ERR_FOUND_DUPP_KEY ||
- error == HA_ERR_FOUND_DUPP_UNIQUE)))
+ error == HA_ERR_FOUND_DUPP_UNIQUE)) ||
+ error == HA_ERR_AUTOINC_ERANGE)
return FALSE;
return TRUE;
}
@@ -2601,6 +2645,46 @@ public:
return TRUE;
}
+ /*
+ Count tables invisible from all tables list on which current one built
+ (like myisammrg and partitioned tables)
+
+ tables_type mask for the tables should be added herdde
+
+ returns number of such tables
+ */
+
+ virtual uint count_query_cache_dependant_tables(uint8 *tables_type
+ __attribute__((unused)))
+ {
+ return 0;
+ }
+
+ /*
+ register tables invisible from all tables list on which current one built
+ (like myisammrg and partitioned tables).
+
+ @note they should be counted by method above
+
+ cache Query cache pointer
+ block Query cache block to write the table
+ n Number of the table
+
+ @retval FALSE - OK
+ @retval TRUE - Error
+ */
+
+ virtual my_bool
+ register_query_cache_dependant_tables(THD *thd
+ __attribute__((unused)),
+ Query_cache *cache
+ __attribute__((unused)),
+ Query_cache_block_table **block
+ __attribute__((unused)),
+ uint *n __attribute__((unused)))
+ {
+ return FALSE;
+ }
/*
Check if the primary key (if there is one) is a clustered and a
@@ -3014,6 +3098,7 @@ void ha_close_connection(THD* thd);
bool ha_flush_logs(handlerton *db_type);
void ha_drop_database(char* path);
void ha_checkpoint_state(bool disable);
+void ha_commit_checkpoint_request(void *cookie, void (*pre_hook)(void *));
int ha_create_table(THD *thd, const char *path,
const char *db, const char *table_name,
HA_CREATE_INFO *create_info,
@@ -3094,6 +3179,7 @@ int ha_binlog_end(THD *thd);
const char *get_canonical_filename(handler *file, const char *path,
char *tmp_path);
bool mysql_xa_recover(THD *thd);
+void commit_checkpoint_notify_ha(handlerton *hton, void *cookie);
inline const char *table_case_name(HA_CREATE_INFO *info, const char *name)
{
diff --git a/sql/item.cc b/sql/item.cc
index 367e603c6e7..13a4524f131 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -503,8 +503,8 @@ Item::Item(THD *thd, Item *item):
orig_name(item->orig_name),
max_length(item->max_length),
name_length(item->name_length),
- marker(item->marker),
decimals(item->decimals),
+ marker(item->marker),
maybe_null(item->maybe_null),
in_rollup(item->in_rollup),
null_value(item->null_value),
@@ -992,15 +992,31 @@ void Item::set_name(const char *str, uint length, CHARSET_INFO *cs)
if (!my_charset_same(cs, system_charset_info))
{
size_t res_length;
- name= sql_strmake_with_convert(str, name_length= length, cs,
+ name= sql_strmake_with_convert(str, length, cs,
MAX_ALIAS_NAME, system_charset_info,
&res_length);
+ name_length= res_length;
}
else
name= sql_strmake(str, (name_length= min(length,MAX_ALIAS_NAME)));
}
+void Item::set_name_no_truncate(const char *str, uint length, CHARSET_INFO *cs)
+{
+ if (!my_charset_same(cs, system_charset_info))
+ {
+ size_t res_length;
+ name= sql_strmake_with_convert(str, length, cs,
+ UINT_MAX, system_charset_info,
+ &res_length);
+ name_length= res_length;
+ }
+ else
+ name= sql_strmake(str, (name_length= length));
+}
+
+
void Item::set_name_for_rollback(THD *thd, const char *str, uint length,
CHARSET_INFO *cs)
{
@@ -6821,7 +6837,7 @@ bool Item_ref::fix_fields(THD *thd, Item **reference)
if (from_field != not_found_field)
{
Item_field* fld;
- if (!(fld= new Item_field(from_field)))
+ if (!(fld= new Item_field(thd, last_checked_context, from_field)))
goto error;
thd->change_item_tree(reference, fld);
mark_as_dependent(thd, last_checked_context->select_lex,
@@ -7741,6 +7757,13 @@ Item* Item_cache_wrapper::get_tmp_table_item(THD *thd_arg)
}
+bool Item_direct_view_ref::send(Protocol *protocol, String *buffer)
+{
+ if (check_null_ref())
+ return protocol->store_null();
+ return Item_direct_ref::send(protocol, buffer);
+}
+
/**
Prepare referenced field then call usual Item_direct_ref::fix_fields .
@@ -7755,6 +7778,7 @@ Item* Item_cache_wrapper::get_tmp_table_item(THD *thd_arg)
bool Item_direct_view_ref::fix_fields(THD *thd, Item **reference)
{
+ DBUG_ASSERT(1);
/* view fild reference must be defined */
DBUG_ASSERT(*ref);
/* (*ref)->check_cols() will be made in Item_direct_ref::fix_fields */
@@ -9267,7 +9291,7 @@ bool Item_type_holder::join_types(THD *thd, Item *item)
item->max_length, item->decimals));
fld_type= Field::field_type_merge(fld_type, get_real_type(item));
{
- int item_decimals= item->decimals;
+ uint item_decimals= item->decimals;
/* fix variable decimals which always is NOT_FIXED_DEC */
if (Field::result_merge_type(fld_type) == INT_RESULT)
item_decimals= 0;
diff --git a/sql/item.h b/sql/item.h
index 1f79f833f97..c76b443fe09 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -618,8 +618,8 @@ public:
calls.
*/
uint name_length; /* Length of name */
+ uint decimals;
int8 marker;
- uint8 decimals;
bool maybe_null; /* If item may be null */
bool in_rollup; /* If used in GROUP BY list
of a query with ROLLUP */
@@ -656,6 +656,7 @@ public:
#endif
} /*lint -e1509 */
void set_name(const char *str, uint length, CHARSET_INFO *cs);
+ void set_name_no_truncate(const char *str, uint length, CHARSET_INFO *cs);
void set_name_for_rollback(THD *thd, const char *str, uint length,
CHARSET_INFO *cs);
void rename(char *new_name);
@@ -3159,20 +3160,29 @@ class Item_direct_view_ref :public Item_direct_ref
{
Item_equal *item_equal;
TABLE_LIST *view;
+ TABLE *null_ref_table;
+
+ bool check_null_ref()
+ {
+ if (null_ref_table == NULL)
+ {
+ null_ref_table= view->get_real_join_table();
+ }
+ if (null_ref_table->null_row)
+ {
+ null_value= 1;
+ return TRUE;
+ }
+ return FALSE;
+ }
public:
Item_direct_view_ref(Name_resolution_context *context_arg, Item **item,
const char *table_name_arg,
const char *field_name_arg,
TABLE_LIST *view_arg)
:Item_direct_ref(context_arg, item, table_name_arg, field_name_arg),
- item_equal(0), view(view_arg) {}
- /* Constructor need to process subselect with temporary tables (see Item) */
- Item_direct_view_ref(THD *thd, Item_direct_ref *item)
- :Item_direct_ref(thd, item), item_equal(0) {}
- Item_direct_view_ref(TABLE_LIST *view_arg, Item **item,
- const char *field_name_arg)
- :Item_direct_ref(view_arg, item, field_name_arg), item_equal(0)
- {}
+ item_equal(0), view(view_arg),
+ null_ref_table(NULL) {}
bool fix_fields(THD *, Item **);
bool eq(const Item *item, bool binary_cmp) const;
@@ -3203,6 +3213,85 @@ public:
view_arg->view_used_tables|= (*ref)->used_tables();
return 0;
}
+ void save_val(Field *to)
+ {
+ if (check_null_ref())
+ to->set_null();
+ else
+ Item_direct_ref::save_val(to);
+ }
+ double val_real()
+ {
+ if (check_null_ref())
+ return 0;
+ else
+ return Item_direct_ref::val_real();
+ }
+ longlong val_int()
+ {
+ if (check_null_ref())
+ return 0;
+ else
+ return Item_direct_ref::val_int();
+ }
+ String *val_str(String* tmp)
+ {
+ if (check_null_ref())
+ return NULL;
+ else
+ return Item_direct_ref::val_str(tmp);
+ }
+ my_decimal *val_decimal(my_decimal *tmp)
+ {
+ if (check_null_ref())
+ return NULL;
+ else
+ return Item_direct_ref::val_decimal(tmp);
+ }
+ bool val_bool()
+ {
+ if (check_null_ref())
+ return 0;
+ else
+ return Item_direct_ref::val_bool();
+ }
+ bool is_null()
+ {
+ if (check_null_ref())
+ return 1;
+ else
+ return Item_direct_ref::is_null();
+ }
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ {
+ if (check_null_ref())
+ {
+ bzero((char*) ltime,sizeof(*ltime));
+ return 1;
+ }
+ return Item_direct_ref::get_date(ltime, fuzzydate);
+ }
+ bool send(Protocol *protocol, String *buffer);
+ void save_org_in_field(Field *field)
+ {
+ if (check_null_ref())
+ field->set_null();
+ else
+ Item_direct_ref::save_val(field);
+ }
+ void save_in_result_field(bool no_conversions)
+ {
+ if (check_null_ref())
+ result_field->set_null();
+ else
+ Item_direct_ref::save_in_result_field(no_conversions);
+ }
+
+ void cleanup()
+ {
+ null_ref_table= NULL;
+ Item_direct_ref::cleanup();
+ }
};
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 2a0ca19a4e9..d950c0c1443 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -3089,6 +3089,15 @@ void Item_func_case::fix_length_and_dec()
return;
}
}
+ /*
+ Set cmp_context of all WHEN arguments. This prevents
+ Item_field::equal_fields_propagator() from transforming a
+ zerofill argument into a string constant. Such a change would
+ require rebuilding cmp_items.
+ */
+ for (i= 0; i < ncases; i+= 2)
+ args[i]->cmp_context= item_cmp_type(left_result_type,
+ args[i]->result_type());
}
if (else_expr_num == -1 || args[else_expr_num]->maybe_null)
@@ -4069,6 +4078,16 @@ void Item_func_in::fix_length_and_dec()
}
}
}
+ /*
+ Set cmp_context of all arguments. This prevents
+ Item_field::equal_fields_propagator() from transforming a zerofill integer
+ argument into a string constant. Such a change would require rebuilding
+ cmp_itmes.
+ */
+ for (arg= args + 1, arg_end= args + arg_count; arg != arg_end ; arg++)
+ {
+ arg[0]->cmp_context= item_cmp_type(left_result_type, arg[0]->result_type());
+ }
max_length= 1;
}
@@ -4866,8 +4885,8 @@ bool Item_func_like::fix_fields(THD *thd, Item **ref)
}
if (canDoTurboBM)
{
- pattern = first + 1;
pattern_len = (int) len - 2;
+ pattern = thd->strmake(first + 1, pattern_len);
DBUG_PRINT("info", ("Initializing pattern: '%s'", first));
int *suff = (int*) thd->alloc((int) (sizeof(int)*
((pattern_len + 1)*2+
diff --git a/sql/item_create.cc b/sql/item_create.cc
index 96837a8f262..07e7f7b7ff9 100644
--- a/sql/item_create.cc
+++ b/sql/item_create.cc
@@ -4393,27 +4393,34 @@ Create_func_master_pos_wait::create_native(THD *thd, LEX_STRING name,
if (item_list != NULL)
arg_count= item_list->elements;
+ if (arg_count < 2 || arg_count > 4)
+ {
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ return func;
+ }
+
+ thd->lex->safe_to_cache_query= 0;
+
+ Item *param_1= item_list->pop();
+ Item *param_2= item_list->pop();
switch (arg_count) {
case 2:
{
- Item *param_1= item_list->pop();
- Item *param_2= item_list->pop();
func= new (thd->mem_root) Item_master_pos_wait(param_1, param_2);
- thd->lex->safe_to_cache_query= 0;
break;
}
case 3:
{
- Item *param_1= item_list->pop();
- Item *param_2= item_list->pop();
Item *param_3= item_list->pop();
func= new (thd->mem_root) Item_master_pos_wait(param_1, param_2, param_3);
- thd->lex->safe_to_cache_query= 0;
break;
}
- default:
+ case 4:
{
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ Item *param_3= item_list->pop();
+ Item *param_4= item_list->pop();
+ func= new (thd->mem_root) Item_master_pos_wait(param_1, param_2, param_3,
+ param_4);
break;
}
}
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 922d980074a..4c9f0a65d8a 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -3866,13 +3866,45 @@ longlong Item_master_pos_wait::val_int()
#ifdef HAVE_REPLICATION
longlong pos = (ulong)args[1]->val_int();
longlong timeout = (arg_count==3) ? args[2]->val_int() : 0 ;
- if ((event_count = active_mi->rli.wait_for_pos(thd, log_name, pos, timeout)) == -2)
+ String connection_name_buff;
+ LEX_STRING connection_name;
+ Master_info *mi;
+ if (arg_count == 4)
+ {
+ String *con;
+ if (!(con= args[3]->val_str(&connection_name_buff)))
+ goto err;
+
+ connection_name.str= (char*) con->ptr();
+ connection_name.length= con->length();
+ if (check_master_connection_name(&connection_name))
+ {
+ my_error(ER_WRONG_ARGUMENTS, MYF(ME_JUST_WARNING),
+ "MASTER_CONNECTION_NAME");
+ goto err;
+ }
+ }
+ else
+ connection_name= thd->variables.default_master_connection;
+
+ if (!(mi= master_info_index->get_master_info(&connection_name,
+ MYSQL_ERROR::WARN_LEVEL_WARN)))
+ goto err;
+ if ((event_count = mi->rli.wait_for_pos(thd, log_name, pos, timeout)) == -2)
{
null_value = 1;
event_count=0;
}
#endif
return event_count;
+
+#ifdef HAVE_REPLICATION
+err:
+ {
+ null_value = 1;
+ return 0;
+ }
+#endif
}
@@ -4297,7 +4329,7 @@ longlong Item_func_sleep::val_int()
#define extra_size sizeof(double)
-static user_var_entry *get_variable(HASH *hash, LEX_STRING &name,
+user_var_entry *get_variable(HASH *hash, LEX_STRING &name,
bool create_if_not_exists)
{
user_var_entry *entry;
@@ -5441,10 +5473,10 @@ my_decimal* Item_user_var_as_out_param::val_decimal(my_decimal *decimal_buffer)
}
-void Item_user_var_as_out_param::print(String *str, enum_query_type query_type)
+void Item_user_var_as_out_param::print_for_load(THD *thd, String *str)
{
str->append('@');
- str->append(name.str,name.length);
+ append_identifier(thd, str, name.str, name.length);
}
@@ -6044,6 +6076,8 @@ bool Item_func_match::fix_fields(THD *thd, Item **ref)
DBUG_ASSERT(fixed == 0);
Item *UNINIT_VAR(item); // Safe as arg_count is > 1
+ status_var_increment(thd->status_var.feature_fulltext);
+
maybe_null=1;
join_key=0;
@@ -6814,3 +6848,62 @@ longlong Item_func_uuid_short::val_int()
mysql_mutex_unlock(&LOCK_short_uuid_generator);
return (longlong) val;
}
+
+
+/**
+ Last_value - return last argument.
+*/
+
+void Item_func_last_value::evaluate_sideeffects()
+{
+ DBUG_ASSERT(fixed == 1 && arg_count > 0);
+ for (uint i= 0; i < arg_count-1 ; i++)
+ args[i]->val_int();
+}
+
+String *Item_func_last_value::val_str(String *str)
+{
+ String *tmp;
+ evaluate_sideeffects();
+ tmp= last_value->val_str(str);
+ null_value= last_value->null_value;
+ return tmp;
+}
+
+longlong Item_func_last_value::val_int()
+{
+ longlong tmp;
+ evaluate_sideeffects();
+ tmp= last_value->val_int();
+ null_value= last_value->null_value;
+ return tmp;
+}
+
+double Item_func_last_value::val_real()
+{
+ double tmp;
+ evaluate_sideeffects();
+ tmp= last_value->val_real();
+ null_value= last_value->null_value;
+ return tmp;
+}
+
+my_decimal *Item_func_last_value::val_decimal(my_decimal *decimal_value)
+{
+ my_decimal *tmp;
+ evaluate_sideeffects();
+ tmp= last_value->val_decimal(decimal_value);
+ null_value= last_value->null_value;
+ return tmp;
+}
+
+
+void Item_func_last_value::fix_length_and_dec()
+{
+ last_value= args[arg_count -1];
+ decimals= last_value->decimals;
+ max_length= last_value->max_length;
+ collation.set(last_value->collation.collation);
+ maybe_null= last_value->maybe_null;
+ unsigned_flag= last_value->unsigned_flag;
+}
diff --git a/sql/item_func.h b/sql/item_func.h
index cb9c1929d7d..07b246b3ccd 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -496,6 +496,8 @@ public:
{ collation.set_numeric(); fix_char_length(21); }
Item_int_func(Item *a,Item *b,Item *c) :Item_func(a,b,c)
{ collation.set_numeric(); fix_char_length(21); }
+ Item_int_func(Item *a,Item *b,Item *c, Item *d) :Item_func(a,b,c,d)
+ { collation.set_numeric(); fix_char_length(21); }
Item_int_func(List<Item> &list) :Item_func(list)
{ collation.set_numeric(); fix_char_length(21); }
Item_int_func(THD *thd, Item_int_func *item) :Item_func(thd, item)
@@ -1522,6 +1524,7 @@ class Item_master_pos_wait :public Item_int_func
public:
Item_master_pos_wait(Item *a,Item *b) :Item_int_func(a,b) {}
Item_master_pos_wait(Item *a,Item *b,Item *c) :Item_int_func(a,b,c) {}
+ Item_master_pos_wait(Item *a,Item *b, Item *c, Item *d) :Item_int_func(a,b,c,d) {}
longlong val_int();
const char *func_name() const { return "master_pos_wait"; }
void fix_length_and_dec() { max_length=21; maybe_null=1;}
@@ -1670,7 +1673,7 @@ public:
my_decimal *val_decimal(my_decimal *decimal_buffer);
/* fix_fields() binds variable name with its entry structure */
bool fix_fields(THD *thd, Item **ref);
- virtual void print(String *str, enum_query_type query_type);
+ void print_for_load(THD *thd, String *str);
void set_null_value(CHARSET_INFO* cs);
void set_value(const char *str, uint length, CHARSET_INFO* cs);
};
@@ -2003,6 +2006,27 @@ public:
}
};
+
+class Item_func_last_value :public Item_func
+{
+protected:
+ Item *last_value;
+public:
+ Item_func_last_value(List<Item> &list) :Item_func(list) {}
+ double val_real();
+ longlong val_int();
+ String *val_str(String *);
+ my_decimal *val_decimal(my_decimal *);
+ void fix_length_and_dec();
+ enum Item_result result_type () const { return last_value->result_type(); }
+ const char *func_name() const { return "last_value"; }
+ table_map not_null_tables() const { return 0; }
+ enum_field_types field_type() const { return last_value->field_type(); }
+ bool const_item() const { return 0; }
+ void evaluate_sideeffects();
+};
+
+
Item *get_system_var(THD *thd, enum_var_type var_type, LEX_STRING name,
LEX_STRING component);
extern bool check_reserved_words(LEX_STRING *name);
diff --git a/sql/item_geofunc.cc b/sql/item_geofunc.cc
index fdd3cab8273..bc89a6c14b3 100644
--- a/sql/item_geofunc.cc
+++ b/sql/item_geofunc.cc
@@ -135,6 +135,7 @@ String *Item_func_as_wkt::val_str_ascii(String *str)
return 0;
str->length(0);
+ str->set_charset(&my_charset_latin1);
if ((null_value= geom->as_wkt(str, &dummy)))
return 0;
@@ -182,7 +183,7 @@ String *Item_func_geometry_type::val_str_ascii(String *str)
/* String will not move */
str->copy(geom->get_class_info()->m_name.str,
geom->get_class_info()->m_name.length,
- default_charset());
+ &my_charset_latin1);
return str;
}
@@ -1694,7 +1695,8 @@ count_distance:
for (dist_point= collector.get_first(); dist_point; dist_point= dist_point->get_next())
{
/* We only check vertices of object 2 */
- if (dist_point->shape < obj2_si)
+ if (dist_point->type != Gcalc_heap::nt_shape_node ||
+ dist_point->shape < obj2_si)
continue;
/* if we have an edge to check */
diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc
index cb7b2841cfb..4c0c81aa8c6 100644
--- a/sql/item_strfunc.cc
+++ b/sql/item_strfunc.cc
@@ -3775,6 +3775,7 @@ bool Item_func_dyncol_create::fix_fields(THD *thd, Item **ref)
(arg_count / 2));
nums= (uint *) alloc_root(thd->mem_root,
sizeof(uint) * (arg_count / 2));
+ status_var_increment(thd->status_var.feature_dynamic_columns);
return res || vals == 0 || nums == 0;
}
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index c7a3ca445d6..83c7e3e1529 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -220,6 +220,8 @@ bool Item_subselect::fix_fields(THD *thd_param, Item **ref)
uint8 uncacheable;
bool res;
+ status_var_increment(thd_param->status_var.feature_subquery);
+
DBUG_ASSERT(fixed == 0);
engine->set_thd((thd= thd_param));
if (!done_first_fix_fields)
@@ -287,7 +289,7 @@ bool Item_subselect::fix_fields(THD *thd_param, Item **ref)
else
goto end;
- if ((uncacheable= engine->uncacheable()))
+ if ((uncacheable= engine->uncacheable() & ~UNCACHEABLE_EXPLAIN))
{
const_item_cache= 0;
if (uncacheable & UNCACHEABLE_RAND)
@@ -733,6 +735,19 @@ bool Item_subselect::expr_cache_is_needed(THD *thd)
/**
+ Check if the left IN argument contains NULL values.
+
+ @retval TRUE there are NULLs
+ @retval FALSE otherwise
+*/
+
+inline bool Item_in_subselect::left_expr_has_null()
+{
+ return (*(optimizer->get_cache()))->null_value;
+}
+
+
+/**
Check if an expression cache is needed for this subquery
@param thd Thread handle
@@ -819,7 +834,9 @@ table_map Item_subselect::used_tables() const
bool Item_subselect::const_item() const
{
- return thd->lex->context_analysis_only ? FALSE : const_item_cache;
+ return (thd->lex->context_analysis_only ?
+ FALSE :
+ forced_const || const_item_cache);
}
Item *Item_subselect::get_tmp_table_item(THD *thd_arg)
@@ -1272,7 +1289,7 @@ bool Item_singlerow_subselect::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate)
else
{
reset();
- return 0;
+ return 1;
}
}
@@ -1538,6 +1555,10 @@ double Item_in_subselect::val_real()
*/
DBUG_ASSERT(0);
DBUG_ASSERT(fixed == 1);
+ if (forced_const)
+ return value;
+ DBUG_ASSERT((engine->uncacheable() & ~UNCACHEABLE_EXPLAIN) ||
+ ! engine->is_executed());
null_value= was_null= FALSE;
if (exec())
{
@@ -1558,6 +1579,10 @@ longlong Item_in_subselect::val_int()
*/
DBUG_ASSERT(0);
DBUG_ASSERT(fixed == 1);
+ if (forced_const)
+ return value;
+ DBUG_ASSERT((engine->uncacheable() & ~UNCACHEABLE_EXPLAIN) ||
+ ! engine->is_executed());
null_value= was_null= FALSE;
if (exec())
{
@@ -1578,6 +1603,10 @@ String *Item_in_subselect::val_str(String *str)
*/
DBUG_ASSERT(0);
DBUG_ASSERT(fixed == 1);
+ if (forced_const)
+ goto value_is_ready;
+ DBUG_ASSERT((engine->uncacheable() & ~UNCACHEABLE_EXPLAIN) ||
+ ! engine->is_executed());
null_value= was_null= FALSE;
if (exec())
{
@@ -1589,6 +1618,7 @@ String *Item_in_subselect::val_str(String *str)
null_value= TRUE;
return 0;
}
+value_is_ready:
str->set((ulonglong)value, &my_charset_bin);
return str;
}
@@ -1599,6 +1629,8 @@ bool Item_in_subselect::val_bool()
DBUG_ASSERT(fixed == 1);
if (forced_const)
return value;
+ DBUG_ASSERT((engine->uncacheable() & ~UNCACHEABLE_EXPLAIN) ||
+ ! engine->is_executed());
null_value= was_null= FALSE;
if (exec())
{
@@ -1617,6 +1649,10 @@ my_decimal *Item_in_subselect::val_decimal(my_decimal *decimal_value)
method should not be used
*/
DBUG_ASSERT(0);
+ if (forced_const)
+ goto value_is_ready;
+ DBUG_ASSERT((engine->uncacheable() & ~UNCACHEABLE_EXPLAIN) ||
+ ! engine->is_executed());
null_value= was_null= FALSE;
DBUG_ASSERT(fixed == 1);
if (exec())
@@ -1626,6 +1662,7 @@ my_decimal *Item_in_subselect::val_decimal(my_decimal *decimal_value)
}
if (was_null && !value)
null_value= TRUE;
+value_is_ready:
int2my_decimal(E_DEC_FATAL_ERROR, value, 0, decimal_value);
return decimal_value;
}
@@ -1882,7 +1919,7 @@ bool Item_allany_subselect::is_maxmin_applicable(JOIN *join)
WHERE condition.
*/
return (abort_on_null || (upper_item && upper_item->is_top_level_item())) &&
- !join->select_lex->master_unit()->uncacheable && !func->eqne_op();
+ !(join->select_lex->master_unit()->uncacheable & ~UNCACHEABLE_EXPLAIN) && !func->eqne_op();
}
@@ -3176,6 +3213,8 @@ int subselect_single_select_engine::exec()
tab->read_record.read_record= tab->save_read_record;
}
executed= 1;
+ if (!(uncacheable() & ~UNCACHEABLE_EXPLAIN))
+ item->make_const();
thd->where= save_where;
thd->lex->current_select= save_select;
DBUG_RETURN(join->error || thd->is_fatal_error || thd->is_error());
@@ -3259,161 +3298,51 @@ int subselect_uniquesubquery_engine::scan_table()
}
-/*
- Copy ref key and check for null parts in it
-
- SYNOPSIS
- subselect_uniquesubquery_engine::copy_ref_key()
-
- DESCRIPTION
- Copy ref key and check for null parts in it.
- Depending on the nullability and conversion problems this function
- recognizes and processes the following states :
- 1. Partial match on top level. This means IN has a value of FALSE
- regardless of the data in the subquery table.
- Detected by finding a NULL in the left IN operand of a top level
- expression.
- We may actually skip reading the subquery, so return TRUE to skip
- the table scan in subselect_uniquesubquery_engine::exec and make
- the value of the IN predicate a NULL (that is equal to FALSE on
- top level).
- 2. No exact match when IN is nested inside another predicate.
- Detected by finding a NULL in the left IN operand when IN is not
- a top level predicate.
- We cannot have an exact match. But we must proceed further with a
- table scan to find out if it's a partial match (and IN has a value
- of NULL) or no match (and IN has a value of FALSE).
- So we return FALSE to continue with the scan and see if there are
- any record that would constitute a partial match (as we cannot
- determine that from the index).
- 3. Error converting the left IN operand to the column type of the
- right IN operand. This counts as no match (and IN has the value of
- FALSE). We mark the subquery table cursor as having no more rows
- (to ensure that the processing that follows will not find a match)
- and return FALSE, so IN is not treated as returning NULL.
+/**
+ Copy ref key for index access into the only subquery table.
+ @details
+ Copy ref key and check for conversion problems.
+ If there is an error converting the left IN operand to the column type of
+ the right IN operand count it as no match. In this case IN has the value of
+ FALSE. We mark the subquery table cursor as having no more rows (to ensure
+ that the processing that follows will not find a match) and return FALSE,
+ so IN is not treated as returning NULL.
- RETURN
- FALSE - The value of the IN predicate is not known. Proceed to find the
- value of the IN predicate using the determined values of
- null_keypart and table->status.
- TRUE - IN predicate has a value of NULL. Stop the processing right there
- and return NULL to the outer predicates.
+ @returns
+ @retval FALSE The outer ref was copied into an index lookup key.
+ @retval TRUE The outer ref cannot possibly match any row, IN is FALSE.
*/
-bool subselect_uniquesubquery_engine::copy_ref_key()
+bool subselect_uniquesubquery_engine::copy_ref_key(bool skip_constants)
{
DBUG_ENTER("subselect_uniquesubquery_engine::copy_ref_key");
for (store_key **copy= tab->ref.key_copy ; *copy ; copy++)
{
- if ((*copy)->store_key_is_const())
- continue;
- tab->ref.key_err= (*copy)->copy();
-
- /*
- When there is a NULL part in the key we don't need to make index
- lookup for such key thus we don't need to copy whole key.
- If we later should do a sequential scan return OK. Fail otherwise.
-
- See also the comment for the subselect_uniquesubquery_engine::exec()
- function.
- */
- null_keypart= (*copy)->null_key;
- if (null_keypart)
- {
- bool top_level= ((Item_in_subselect *) item)->is_top_level_item();
- if (top_level)
- {
- /* Partial match on top level */
- DBUG_RETURN(1);
- }
- else
- {
- /* No exact match when IN is nested inside another predicate */
- break;
- }
- }
-
- /*
- Check if the error is equal to STORE_KEY_FATAL. This is not expressed
- using the store_key::store_key_result enum because ref.key_err is a
- boolean and we want to detect both TRUE and STORE_KEY_FATAL from the
- space of the union of the values of [TRUE, FALSE] and
- store_key::store_key_result.
- TODO: fix the variable an return types.
- */
- if (tab->ref.key_err & 1)
- {
- /*
- Error converting the left IN operand to the column type of the right
- IN operand.
- */
- tab->table->status= STATUS_NOT_FOUND;
- break;
- }
- }
- DBUG_RETURN(0);
-}
-
-
-/*
- @retval 1 A NULL was found in the outer reference, index lookup is
- not applicable, the outer ref is unsusable as a lookup key,
- use some other method to find a match.
- @retval 0 The outer ref was copied into an index lookup key.
- @retval -1 The outer ref cannot possibly match any row, IN is FALSE.
-*/
-/* TIMOUR: this method is a variant of copy_ref_key(), needs refactoring. */
-
-int subselect_uniquesubquery_engine::copy_ref_key_simple()
-{
- for (store_key **copy= tab->ref.key_copy ; *copy ; copy++)
- {
enum store_key::store_key_result store_res;
+ if (skip_constants && (*copy)->store_key_is_const())
+ continue;
store_res= (*copy)->copy();
tab->ref.key_err= store_res;
- /*
- When there is a NULL part in the key we don't need to make index
- lookup for such key thus we don't need to copy whole key.
- If we later should do a sequential scan return OK. Fail otherwise.
-
- See also the comment for the subselect_uniquesubquery_engine::exec()
- function.
- */
- null_keypart= (*copy)->null_key;
- if (null_keypart)
- return 1;
-
- /*
- Check if the error is equal to STORE_KEY_FATAL. This is not expressed
- using the store_key::store_key_result enum because ref.key_err is a
- boolean and we want to detect both TRUE and STORE_KEY_FATAL from the
- space of the union of the values of [TRUE, FALSE] and
- store_key::store_key_result.
- TODO: fix the variable an return types.
- */
if (store_res == store_key::STORE_KEY_FATAL)
{
/*
Error converting the left IN operand to the column type of the right
IN operand.
*/
- return -1;
+ DBUG_RETURN(true);
}
}
- return 0;
+ DBUG_RETURN(false);
}
-/*
- Execute subselect
-
- SYNOPSIS
- subselect_uniquesubquery_engine::exec()
+/**
+ Execute subselect via unique index lookup
- DESCRIPTION
+ @details
Find rows corresponding to the ref key using index access.
If some part of the lookup key is NULL, then we're evaluating
NULL IN (SELECT ... )
@@ -3430,11 +3359,11 @@ int subselect_uniquesubquery_engine::copy_ref_key_simple()
The result of this function (info about whether a row was found) is
stored in this->empty_result_set.
- NOTE
- RETURN
- FALSE - ok
- TRUE - an error occured while scanning
+ @returns
+ @retval 0 OK
+ @retval 1 notify caller to call Item_subselect::reset(),
+ in most cases reset() sets the result to NULL
*/
int subselect_uniquesubquery_engine::exec()
@@ -3444,32 +3373,30 @@ int subselect_uniquesubquery_engine::exec()
TABLE *table= tab->table;
empty_result_set= TRUE;
table->status= 0;
+ Item_in_subselect *in_subs= (Item_in_subselect *) item;
+
+ if (!tab->preread_init_done && tab->preread_init())
+ DBUG_RETURN(1);
- /* TODO: change to use of 'full_scan' here? */
- if (copy_ref_key())
+ if (in_subs->left_expr_has_null())
{
/*
- TIMOUR: copy_ref_key() == 1 means NULL result, not error, why return 1?
- Check who reiles on this result.
+ The case when all values in left_expr are NULL is handled by
+ Item_in_optimizer::val_int().
*/
- DBUG_RETURN(1);
+ if (in_subs->is_top_level_item())
+ DBUG_RETURN(1); /* notify caller to call reset() and set NULL value. */
+ else
+ DBUG_RETURN(scan_table());
}
- if (table->status)
+
+ if (copy_ref_key(true))
{
- /*
- We know that there will be no rows even if we scan.
- Can be set in copy_ref_key.
- */
- ((Item_in_subselect *) item)->value= 0;
+ /* We know that there will be no rows even if we scan. */
+ in_subs->value= 0;
DBUG_RETURN(0);
}
- if (!tab->preread_init_done && tab->preread_init())
- DBUG_RETURN(1);
-
- if (null_keypart)
- DBUG_RETURN(scan_table());
-
if (!table->file->inited)
table->file->ha_index_init(tab->ref.key, 0);
error= table->file->ha_index_read_map(table->record[0],
@@ -3545,14 +3472,10 @@ subselect_uniquesubquery_engine::~subselect_uniquesubquery_engine()
}
-/*
- Index-lookup subselect 'engine' - run the subquery
-
- SYNOPSIS
- subselect_indexsubquery_engine:exec()
- full_scan
+/**
+ Execute subselect via unique index lookup
- DESCRIPTION
+ @details
The engine is used to resolve subqueries in form
oe IN (SELECT key FROM tbl WHERE subq_where)
@@ -3567,7 +3490,7 @@ subselect_uniquesubquery_engine::~subselect_uniquesubquery_engine()
row that satisfies subq_where. If found, return NULL, otherwise
return FALSE.
- TODO
+ @todo
The step #1 can be optimized further when the index has several key
parts. Consider a subquery:
@@ -3592,9 +3515,10 @@ subselect_uniquesubquery_engine::~subselect_uniquesubquery_engine()
cheaper. We can use index statistics to quickly check whether "ref" scan
will be cheaper than full table scan.
- RETURN
- 0
- 1
+ @returns
+ @retval 0 OK
+ @retval 1 notify caller to call Item_subselect::reset(),
+ in most cases reset() sets the result to NULL
*/
int subselect_indexsubquery_engine::exec()
@@ -3603,10 +3527,10 @@ int subselect_indexsubquery_engine::exec()
int error;
bool null_finding= 0;
TABLE *table= tab->table;
+ Item_in_subselect *in_subs= (Item_in_subselect *) item;
((Item_in_subselect *) item)->value= 0;
empty_result_set= TRUE;
- null_keypart= 0;
table->status= 0;
if (check_null)
@@ -3616,25 +3540,27 @@ int subselect_indexsubquery_engine::exec()
((Item_in_subselect *) item)->was_null= 0;
}
- /* Copy the ref key and check for nulls... */
- if (copy_ref_key())
+ if (!tab->preread_init_done && tab->preread_init())
DBUG_RETURN(1);
- if (table->status)
+ if (in_subs->left_expr_has_null())
{
- /*
- We know that there will be no rows even if we scan.
- Can be set in copy_ref_key.
+ /*
+ The case when all values in left_expr are NULL is handled by
+ Item_in_optimizer::val_int().
*/
- ((Item_in_subselect *) item)->value= 0;
- DBUG_RETURN(0);
+ if (in_subs->is_top_level_item())
+ DBUG_RETURN(1); /* notify caller to call reset() and set NULL value. */
+ else
+ DBUG_RETURN(scan_table());
}
- if (!tab->preread_init_done && tab->preread_init())
- DBUG_RETURN(1);
-
- if (null_keypart)
- DBUG_RETURN(scan_table());
+ if (copy_ref_key(true))
+ {
+ /* We know that there will be no rows even if we scan. */
+ in_subs->value= 0;
+ DBUG_RETURN(0);
+ }
if (!table->file->inited)
table->file->ha_index_init(tab->ref.key, 1);
@@ -5168,10 +5094,20 @@ Ordered_key::cmp_keys_by_row_data(ha_rows a, ha_rows b)
rowid_a= row_num_to_rowid + a * rowid_length;
rowid_b= row_num_to_rowid + b * rowid_length;
/* Fetch the rows for comparison. */
- error= tbl->file->ha_rnd_pos(tbl->record[0], rowid_a);
- DBUG_ASSERT(!error);
- error= tbl->file->ha_rnd_pos(tbl->record[1], rowid_b);
- DBUG_ASSERT(!error);
+ if ((error= tbl->file->ha_rnd_pos(tbl->record[0], rowid_a)))
+ {
+ /* purecov: begin inspected */
+ tbl->file->print_error(error, MYF(ME_FATALERROR)); // Sets fatal_error
+ return 0;
+ /* purecov: end */
+ }
+ if ((error= tbl->file->ha_rnd_pos(tbl->record[1], rowid_b)))
+ {
+ /* purecov: begin inspected */
+ tbl->file->print_error(error, MYF(ME_FATALERROR)); // Sets fatal_error
+ return 0;
+ /* purecov: end */
+ }
/*
Compare the two rows by the corresponding values of the indexed
columns.
@@ -5247,8 +5183,13 @@ int Ordered_key::cmp_key_with_search_key(rownum_t row_num)
int __attribute__((unused)) error;
int cmp_res;
- error= tbl->file->ha_rnd_pos(tbl->record[0], cur_rowid);
- DBUG_ASSERT(!error);
+ if ((error= tbl->file->ha_rnd_pos(tbl->record[0], cur_rowid)))
+ {
+ /* purecov: begin inspected */
+ tbl->file->print_error(error, MYF(ME_FATALERROR)); // Sets fatal_error
+ return 0;
+ /* purecov: end */
+ }
for (uint i= 0; i < key_column_count; i++)
{
@@ -5390,37 +5331,42 @@ subselect_partial_match_engine::subselect_partial_match_engine(
int subselect_partial_match_engine::exec()
{
Item_in_subselect *item_in= (Item_in_subselect *) item;
- int copy_res, lookup_res;
+ int lookup_res;
- /* Try to find a matching row by index lookup. */
- copy_res= lookup_engine->copy_ref_key_simple();
- if (copy_res == -1)
- {
- /* The result is FALSE based on the outer reference. */
- item_in->value= 0;
- item_in->null_value= 0;
- return 0;
- }
- else if (copy_res == 0)
+ DBUG_ASSERT(!(item_in->left_expr_has_null() &&
+ item_in->is_top_level_item()));
+
+ if (!item_in->left_expr_has_null())
{
- /* Search for a complete match. */
- if ((lookup_res= lookup_engine->index_lookup()))
+ /* Try to find a matching row by index lookup. */
+ if (lookup_engine->copy_ref_key(false))
{
- /* An error occured during lookup(). */
+ /* The result is FALSE based on the outer reference. */
item_in->value= 0;
item_in->null_value= 0;
- return lookup_res;
+ return 0;
}
- else if (item_in->value || !count_columns_with_nulls)
+ else
{
- /*
- A complete match was found, the result of IN is TRUE.
- If no match was found, and there are no NULLs in the materialized
- subquery, then the result is guaranteed to be false because this
- branch is executed when the outer reference has no NULLs as well.
- Notice: (this->item == lookup_engine->item)
- */
- return 0;
+ /* Search for a complete match. */
+ if ((lookup_res= lookup_engine->index_lookup()))
+ {
+ /* An error occured during lookup(). */
+ item_in->value= 0;
+ item_in->null_value= 0;
+ return lookup_res;
+ }
+ else if (item_in->value || !count_columns_with_nulls)
+ {
+ /*
+ A complete match was found, the result of IN is TRUE.
+ If no match was found, and there are no NULLs in the materialized
+ subquery, then the result is guaranteed to be false because this
+ branch is executed when the outer reference has no NULLs as well.
+ Notice: (this->item == lookup_engine->item)
+ */
+ return 0;
+ }
}
}
diff --git a/sql/item_subselect.h b/sql/item_subselect.h
index 05c4528490f..2a64c63a1be 100644
--- a/sql/item_subselect.h
+++ b/sql/item_subselect.h
@@ -591,6 +591,7 @@ public:
/* Inform 'this' that it was computed, and contains a valid result. */
void set_first_execution() { if (first_execution) first_execution= FALSE; }
bool expr_cache_is_needed(THD *thd);
+ inline bool left_expr_has_null();
int optimize(double *out_rows, double *cost);
/*
@@ -869,7 +870,6 @@ protected:
expression is NULL.
*/
bool empty_result_set;
- bool null_keypart; /* TRUE <=> constructed search tuple has a NULL */
public:
// constructor can assign THD because it will be called after JOIN::prepare
@@ -893,8 +893,7 @@ public:
bool no_tables();
int index_lookup(); /* TIMOUR: this method needs refactoring. */
int scan_table();
- bool copy_ref_key();
- int copy_ref_key_simple(); /* TIMOUR: this method needs refactoring. */
+ bool copy_ref_key(bool skip_constants);
bool no_rows() { return empty_result_set; }
virtual enum_engine_type engine_type() { return UNIQUESUBQUERY_ENGINE; }
};
diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc
index 857d9bc2080..117276e488b 100644
--- a/sql/item_timefunc.cc
+++ b/sql/item_timefunc.cc
@@ -361,9 +361,8 @@ static bool extract_date_time(DATE_TIME_FORMAT *format,
{
uint days;
days= calc_daynr(l_time->year,1,1) + yearday - 1;
- if (days <= 0 || days > MAX_DAY_NUMBER)
+ if (get_date_from_daynr(days,&l_time->year,&l_time->month,&l_time->day))
goto err;
- get_date_from_daynr(days,&l_time->year,&l_time->month,&l_time->day);
}
if (week_number >= 0 && weekday)
@@ -408,9 +407,8 @@ static bool extract_date_time(DATE_TIME_FORMAT *format,
(weekday - 1);
}
- if (days <= 0 || days > MAX_DAY_NUMBER)
+ if (get_date_from_daynr(days,&l_time->year,&l_time->month,&l_time->day))
goto err;
- get_date_from_daynr(days,&l_time->year,&l_time->month,&l_time->day);
}
if (l_time->month > 12 || l_time->day > 31 || l_time->hour > 23 ||
@@ -768,7 +766,7 @@ longlong Item_func_to_days::val_int()
{
DBUG_ASSERT(fixed == 1);
MYSQL_TIME ltime;
- if (get_arg0_date(&ltime, TIME_NO_ZERO_DATE))
+ if (get_arg0_date(&ltime, TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE))
return 0;
return (longlong) calc_daynr(ltime.year,ltime.month,ltime.day);
}
@@ -808,7 +806,7 @@ longlong Item_func_to_seconds::val_int()
MYSQL_TIME ltime;
longlong seconds;
longlong days;
- if (get_arg0_date(&ltime, TIME_NO_ZERO_DATE))
+ if (get_arg0_date(&ltime, TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE))
return 0;
seconds= ltime.hour * 3600L + ltime.minute * 60 + ltime.second;
seconds=ltime.neg ? -seconds : seconds;
@@ -1501,10 +1499,11 @@ bool Item_func_from_days::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
if ((fuzzy_date & TIME_NO_ZERO_DATE) && value == 0)
return (null_value= 1);
bzero(ltime, sizeof(MYSQL_TIME));
- get_date_from_daynr((long) value, &ltime->year, &ltime->month, &ltime->day);
+ if (get_date_from_daynr((long) value, &ltime->year, &ltime->month,
+ &ltime->day))
+ return (null_value= 1);
- if ((fuzzy_date & TIME_NO_ZERO_DATE) &&
- (ltime->year == 0 || ltime->month == 0 || ltime->day == 0))
+ if ((fuzzy_date & TIME_NO_ZERO_DATE) && ltime->year == 0)
return (null_value= 1);
ltime->time_type= MYSQL_TIMESTAMP_DATE;
@@ -2043,7 +2042,7 @@ bool Item_date_add_interval::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
{
INTERVAL interval;
- if (args[0]->get_date(ltime, TIME_NO_ZERO_DATE | TIME_FUZZY_DATE) ||
+ if (args[0]->get_date(ltime, TIME_NO_ZERO_DATE | TIME_FUZZY_DATE | TIME_NO_ZERO_IN_DATE) ||
get_interval_value(args[1], int_type, &interval))
return (null_value=1);
@@ -2514,14 +2513,12 @@ bool Item_func_makedate::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
year= year_2000_handling(year);
days= calc_daynr(year,1,1) + daynr - 1;
- /* Day number from year 0 to 9999-12-31 */
- if (days >= 0 && days <= MAX_DAY_NUMBER)
- {
- bzero(ltime, sizeof(*ltime));
- ltime->time_type= MYSQL_TIMESTAMP_DATE;
- get_date_from_daynr(days, &ltime->year, &ltime->month, &ltime->day);
- return (null_value= 0);
- }
+ if (get_date_from_daynr(days, &ltime->year, &ltime->month, &ltime->day))
+ goto err;
+ ltime->time_type= MYSQL_TIMESTAMP_DATE;
+ ltime->neg= 0;
+ ltime->hour= ltime->minute= ltime->second= ltime->second_part= 0;
+ return (null_value= 0);
err:
return (null_value= 1);
@@ -2615,8 +2612,8 @@ bool Item_func_add_time::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
if (!is_time)
{
- get_date_from_daynr(days,&ltime->year,&ltime->month,&ltime->day);
- if (!ltime->day)
+ if (get_date_from_daynr(days,&ltime->year,&ltime->month,&ltime->day) ||
+ !ltime->day)
return (null_value= 1);
return (null_value= 0);
}
diff --git a/sql/item_xmlfunc.cc b/sql/item_xmlfunc.cc
index 0fa2d39aea9..ae0a74c5ba6 100644
--- a/sql/item_xmlfunc.cc
+++ b/sql/item_xmlfunc.cc
@@ -2601,6 +2601,8 @@ void Item_xml_str_func::fix_length_and_dec()
MY_XPATH xpath;
int rc;
+ status_var_increment(current_thd->status_var.feature_xml);
+
nodeset_func= 0;
if (agg_arg_charsets_for_comparison(collation, args, arg_count))
diff --git a/sql/lex.h b/sql/lex.h
index 9756d5bc7cc..0af44234fca 100644
--- a/sql/lex.h
+++ b/sql/lex.h
@@ -302,6 +302,7 @@ static SYMBOL symbols[] = {
{ "KILL", SYM(KILL_SYM)},
{ "LANGUAGE", SYM(LANGUAGE_SYM)},
{ "LAST", SYM(LAST_SYM)},
+ { "LAST_VALUE", SYM(LAST_VALUE)},
{ "LEADING", SYM(LEADING)},
{ "LEAVE", SYM(LEAVE_SYM)},
{ "LEAVES", SYM(LEAVES)},
@@ -515,6 +516,7 @@ static SYMBOL symbols[] = {
{ "SIGNED", SYM(SIGNED_SYM)},
{ "SIMPLE", SYM(SIMPLE_SYM)},
{ "SLAVE", SYM(SLAVE)},
+ { "SLAVES", SYM(SLAVES)},
{ "SLOW", SYM(SLOW)},
{ "SNAPSHOT", SYM(SNAPSHOT_SYM)},
{ "SMALLINT", SYM(SMALLINT)},
diff --git a/sql/log.cc b/sql/log.cc
index 527ea133eae..5c21fa21f97 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -40,6 +40,7 @@
#include "rpl_rli.h"
#include "sql_audit.h"
#include "log_slow.h"
+#include "mysqld.h"
#include <my_dir.h>
#include <stdarg.h>
@@ -52,6 +53,7 @@
#include "sql_plugin.h"
#include "rpl_handler.h"
#include "debug_sync.h"
+#include "sql_show.h"
/* max size of the log message */
#define MAX_LOG_BUFFER_SIZE 1024
@@ -478,6 +480,14 @@ public:
*/
bool using_xa;
my_xid xa_xid;
+ bool need_unlog;
+ /*
+ Id of binlog that transaction was written to; only needed if need_unlog is
+ true.
+ */
+ ulong binlog_id;
+ /* Set if we get an error during commit that must be returned from unlog(). */
+ bool delayed_error;
private:
@@ -1664,6 +1674,20 @@ binlog_flush_cache(THD *thd, binlog_cache_mngr *cache_mngr,
end_ev, all,
using_stmt, using_trx);
}
+ else
+ {
+ /*
+ This can happen in row-format binlog with something like
+ BEGIN; INSERT INTO nontrans_table; INSERT IGNORE INTO trans_table;
+ The nontrans_table is written directly into the binlog before commit,
+ and if the trans_table is ignored there will be no rows to write when
+ we get here.
+
+ So there is no work to do. Therefore, we will not increment any XID
+ count, so we must not decrement any XID count in unlog().
+ */
+ cache_mngr->need_unlog= 0;
+ }
cache_mngr->reset(using_stmt, using_trx);
DBUG_ASSERT((!using_stmt || cache_mngr->stmt_cache.empty()) &&
@@ -2071,9 +2095,8 @@ static int binlog_savepoint_set(handlerton *hton, THD *thd, void *sv)
String log_query;
if (log_query.append(STRING_WITH_LEN("SAVEPOINT ")) ||
- log_query.append("`") ||
- log_query.append(thd->lex->ident.str, thd->lex->ident.length) ||
- log_query.append("`"))
+ append_identifier(thd, &log_query,
+ thd->lex->ident.str, thd->lex->ident.length))
DBUG_RETURN(1);
int errcode= query_error_code(thd, thd->killed == NOT_KILLED);
Query_log_event qinfo(thd, log_query.c_ptr_safe(), log_query.length(),
@@ -2111,9 +2134,8 @@ static int binlog_savepoint_rollback(handlerton *hton, THD *thd, void *sv)
{
String log_query;
if (log_query.append(STRING_WITH_LEN("ROLLBACK TO ")) ||
- log_query.append("`") ||
- log_query.append(thd->lex->ident.str, thd->lex->ident.length) ||
- log_query.append("`"))
+ append_identifier(thd, &log_query,
+ thd->lex->ident.str, thd->lex->ident.length))
DBUG_RETURN(1);
int errcode= query_error_code(thd, thd->killed == NOT_KILLED);
Query_log_event qinfo(thd, log_query.ptr(), log_query.length(),
@@ -2850,7 +2872,10 @@ bool MYSQL_QUERY_LOG::write(THD *thd, time_t current_time,
{
end= strxmov(buff, "# administrator command: ", NullS);
buff_len= (ulong) (end - buff);
- my_b_write(&log_file, (uchar*) buff, buff_len);
+ DBUG_EXECUTE_IF("simulate_slow_log_write_error",
+ {DBUG_SET("+d,simulate_file_write_error");});
+ if(my_b_write(&log_file, (uchar*) buff, buff_len))
+ tmp_errno= errno;
}
if (my_b_write(&log_file, (uchar*) sql_text, sql_text_len) ||
my_b_write(&log_file, (uchar*) ";\n",2) ||
@@ -2900,15 +2925,16 @@ const char *MYSQL_LOG::generate_name(const char *log_name,
MYSQL_BIN_LOG::MYSQL_BIN_LOG(uint *sync_period)
- :bytes_written(0), prepared_xids(0), file_id(1), open_count(1),
- need_start_event(TRUE),
+ :reset_master_pending(false),
+ 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_period_ptr(sync_period), sync_counter(0),
is_relay_log(0), signal_cnt(0),
checksum_alg_reset(BINLOG_CHECKSUM_ALG_UNDEF),
relay_log_checksum_alg(BINLOG_CHECKSUM_ALG_UNDEF),
- description_event_for_exec(0), description_event_for_queue(0)
+ description_event_for_exec(0), description_event_for_queue(0),
+ current_binlog_id(0)
{
/*
We don't want to initialize locks here as such initialization depends on
@@ -2928,23 +2954,39 @@ void MYSQL_BIN_LOG::cleanup()
DBUG_ENTER("cleanup");
if (inited)
{
+ xid_count_per_binlog *b;
+
inited= 0;
close(LOG_CLOSE_INDEX|LOG_CLOSE_STOP_EVENT);
delete description_event_for_queue;
delete description_event_for_exec;
+
+ while ((b= binlog_xid_count_list.get()))
+ {
+ /*
+ There should be no pending XIDs at shutdown, and only one entry (for
+ the active binlog file) in the list.
+ */
+ DBUG_ASSERT(b->xid_count == 0);
+ DBUG_ASSERT(!binlog_xid_count_list.head());
+ my_free(b);
+ }
+
mysql_mutex_destroy(&LOCK_log);
mysql_mutex_destroy(&LOCK_index);
+ mysql_mutex_destroy(&LOCK_xid_list);
mysql_cond_destroy(&update_cond);
+ mysql_cond_destroy(&COND_queue_busy);
+ mysql_cond_destroy(&COND_xid_list);
}
DBUG_VOID_RETURN;
}
/* Init binlog-specific vars */
-void MYSQL_BIN_LOG::init(bool no_auto_events_arg, ulong max_size_arg)
+void MYSQL_BIN_LOG::init(ulong max_size_arg)
{
DBUG_ENTER("MYSQL_BIN_LOG::init");
- no_auto_events= no_auto_events_arg;
max_size= max_size_arg;
DBUG_PRINT("info",("max_size: %lu", max_size));
DBUG_VOID_RETURN;
@@ -2956,8 +2998,11 @@ void MYSQL_BIN_LOG::init_pthread_objects()
MYSQL_LOG::init_pthread_objects();
mysql_mutex_init(m_key_LOCK_index, &LOCK_index, MY_MUTEX_INIT_SLOW);
mysql_mutex_setflags(&LOCK_index, MYF_NO_DEADLOCK_DETECTION);
+ mysql_mutex_init(key_BINLOG_LOCK_xid_list,
+ &LOCK_xid_list, MY_MUTEX_INIT_FAST);
mysql_cond_init(m_key_update_cond, &update_cond, 0);
mysql_cond_init(m_key_COND_queue_busy, &COND_queue_busy, 0);
+ mysql_cond_init(key_BINLOG_COND_xid_list, &COND_xid_list, 0);
}
@@ -3045,12 +3090,12 @@ bool MYSQL_BIN_LOG::open(const char *log_name,
enum_log_type log_type_arg,
const char *new_name,
enum cache_type io_cache_type_arg,
- bool no_auto_events_arg,
ulong max_size_arg,
bool null_created_arg,
bool need_mutex)
{
File file= -1;
+ xid_count_per_binlog *new_xid_list_entry= NULL, *b;
DBUG_ENTER("MYSQL_BIN_LOG::open");
DBUG_PRINT("enter",("log_type: %d",(int) log_type_arg));
@@ -3106,7 +3151,7 @@ bool MYSQL_BIN_LOG::open(const char *log_name,
DBUG_RETURN(1); /* all warnings issued */
}
- init(no_auto_events_arg, max_size_arg);
+ init(max_size_arg);
open_count++;
@@ -3130,11 +3175,10 @@ bool MYSQL_BIN_LOG::open(const char *log_name,
write_file_name_to_index_file= 1;
}
- if (need_start_event && !no_auto_events)
{
/*
- In 4.x we set need_start_event=0 here, but in 5.0 we want a Start event
- even if this is not the very first binlog.
+ In 4.x we put Start event only in the first binlog. But from 5.0 we
+ want a Start event even if this is not the very first binlog.
*/
Format_description_log_event s(BINLOG_VERSION);
/*
@@ -3161,6 +3205,60 @@ bool MYSQL_BIN_LOG::open(const char *log_name,
if (s.write(&log_file))
goto err;
bytes_written+= s.data_written;
+
+ if (!is_relay_log)
+ {
+ char buf[FN_REFLEN];
+ /*
+ Construct an entry in the binlog_xid_count_list for the new binlog
+ file (we will not link it into the list until we know the new file
+ is successfully created; otherwise we would have to remove it again
+ if creation failed, which gets tricky since other threads may have
+ seen the entry in the meantime - and we do not want to hold
+ LOCK_xid_list for long periods of time).
+
+ Write the current binlog checkpoint into the log, so XA recovery will
+ know from where to start recovery.
+ */
+ uint off= dirname_length(log_file_name);
+ uint len= strlen(log_file_name) - off;
+ char *entry_mem, *name_mem;
+ if (!(new_xid_list_entry = (xid_count_per_binlog *)
+ my_multi_malloc(MYF(MY_WME),
+ &entry_mem, sizeof(xid_count_per_binlog),
+ &name_mem, len,
+ NULL)))
+ goto err;
+ memcpy(name_mem, log_file_name+off, len);
+ new_xid_list_entry->binlog_name= name_mem;
+ new_xid_list_entry->binlog_name_len= len;
+ new_xid_list_entry->xid_count= 0;
+
+ /*
+ Find the name for the Initial binlog checkpoint.
+
+ Normally this will just be the first entry, as we delete entries
+ when their count drops to zero. But we scan the list to handle any
+ corner case, eg. for the first binlog file opened after startup, the
+ list will be empty.
+ */
+ mysql_mutex_lock(&LOCK_xid_list);
+ I_List_iterator<xid_count_per_binlog> it(binlog_xid_count_list);
+ while ((b= it++) && b->xid_count == 0)
+ ;
+ mysql_mutex_unlock(&LOCK_xid_list);
+ if (!b)
+ b= new_xid_list_entry;
+ strmake(buf, b->binlog_name, b->binlog_name_len);
+ Binlog_checkpoint_log_event ev(buf, len);
+ DBUG_EXECUTE_IF("crash_before_write_checkpoint_event",
+ flush_io_cache(&log_file);
+ mysql_file_sync(log_file.file, MYF(MY_WME));
+ DBUG_SUICIDE(););
+ if (ev.write(&log_file))
+ goto err;
+ bytes_written+= ev.data_written;
+ }
}
if (description_event_for_queue &&
description_event_for_queue->binlog_version>=4)
@@ -3194,7 +3292,7 @@ bool MYSQL_BIN_LOG::open(const char *log_name,
bytes_written+= description_event_for_queue->data_written;
}
if (flush_io_cache(&log_file) ||
- mysql_file_sync(log_file.file, MYF(MY_WME)))
+ mysql_file_sync(log_file.file, MYF(MY_WME|MY_SYNC_FILESIZE)))
goto err;
mysql_mutex_lock(&LOCK_commit_ordered);
strmake(last_commit_pos_file, log_file_name,
@@ -3224,7 +3322,7 @@ bool MYSQL_BIN_LOG::open(const char *log_name,
strlen(log_file_name)) ||
my_b_write(&index_file, (uchar*) "\n", 1) ||
flush_io_cache(&index_file) ||
- mysql_file_sync(index_file.file, MYF(MY_WME)))
+ mysql_file_sync(index_file.file, MYF(MY_WME|MY_SYNC_FILESIZE)))
goto err;
#ifdef HAVE_REPLICATION
@@ -3232,6 +3330,23 @@ bool MYSQL_BIN_LOG::open(const char *log_name,
#endif
}
}
+
+ if (!is_relay_log)
+ {
+ /*
+ Now the file was created successfully, so we can link in the entry for
+ the new binlog file in binlog_xid_count_list.
+ */
+ mysql_mutex_lock(&LOCK_xid_list);
+ ++current_binlog_id;
+ new_xid_list_entry->binlog_id= current_binlog_id;
+ /* Remove any initial entries with no pending XIDs. */
+ while ((b= binlog_xid_count_list.head()) && b->xid_count == 0)
+ my_free(binlog_xid_count_list.get());
+ binlog_xid_count_list.push_back(new_xid_list_entry);
+ mysql_mutex_unlock(&LOCK_xid_list);
+ }
+
log_state= LOG_OPENED;
#ifdef HAVE_REPLICATION
@@ -3250,6 +3365,8 @@ err:
Turning logging off for the whole duration of the MySQL server process. \
To turn it on again: fix the cause, \
shutdown the MySQL server and restart it.", name, errno);
+ if (new_xid_list_entry)
+ my_free(new_xid_list_entry);
if (file >= 0)
mysql_file_close(file, MYF(0));
close(LOG_CLOSE_INDEX);
@@ -3314,7 +3431,7 @@ static bool copy_up_file_and_fill(IO_CACHE *index_file, my_off_t offset)
}
/* The following will either truncate the file or fill the end with \n' */
if (mysql_file_chsize(file, offset - init_offset, '\n', MYF(MY_WME)) ||
- mysql_file_sync(file, MYF(MY_WME)))
+ mysql_file_sync(file, MYF(MY_WME|MY_SYNC_FILESIZE)))
goto err;
/* Reset data in old index cache */
@@ -3495,11 +3612,11 @@ err:
/**
Delete all logs refered to in the index file.
- Start writing to a new log file.
The new index file will only contain this file.
- @param thd Thread
+ @param thd Thread
+ @param create_new_log 1 if we should start writing to a new log file
@note
If not called from slave thread, write start event to new log
@@ -3510,7 +3627,7 @@ err:
1 error
*/
-bool MYSQL_BIN_LOG::reset_logs(THD* thd)
+bool MYSQL_BIN_LOG::reset_logs(THD* thd, bool create_new_log)
{
LOG_INFO linfo;
bool error=0;
@@ -3526,6 +3643,67 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd)
mysql_mutex_lock(&LOCK_log);
mysql_mutex_lock(&LOCK_index);
+ if (!is_relay_log)
+ {
+ /*
+ Mark that a RESET MASTER is in progress.
+ This ensures that a binlog checkpoint will not try to write binlog
+ checkpoint events, which would be useless (as we are deleting the binlog
+ anyway) and could deadlock, as we are holding LOCK_log.
+ */
+ mysql_mutex_lock(&LOCK_xid_list);
+ reset_master_pending= true;
+ mysql_mutex_unlock(&LOCK_xid_list);
+
+ /*
+ We are going to nuke all binary log files.
+ Without binlog, we cannot XA recover prepared-but-not-committed
+ transactions in engines. So force a commit checkpoint first.
+
+ Note that we take and immediately release LOCK_commit_ordered. This has
+ the effect to ensure that any on-going group commit (in
+ trx_group_commit_leader()) has completed before we request the checkpoint,
+ due to the chaining of LOCK_log and LOCK_commit_ordered in that function.
+ (We are holding LOCK_log, so no new group commit can start).
+
+ Without this, it is possible (though perhaps unlikely) that the RESET
+ MASTER could run in-between the write to the binlog and the
+ commit_ordered() in the engine of some transaction, and then a crash
+ later would leave such transaction not recoverable.
+ */
+ mysql_mutex_lock(&LOCK_commit_ordered);
+ mysql_mutex_unlock(&LOCK_commit_ordered);
+
+ mark_xids_active(current_binlog_id, 1);
+ do_checkpoint_request(current_binlog_id);
+
+ /* Now wait for all checkpoint requests and pending unlog() to complete. */
+ mysql_mutex_lock(&LOCK_xid_list);
+ xid_count_per_binlog *b;
+ for (;;)
+ {
+ I_List_iterator<xid_count_per_binlog> it(binlog_xid_count_list);
+ while ((b= it++))
+ {
+ if (b->xid_count > 0)
+ break;
+ }
+ if (!b)
+ break; /* No more pending XIDs */
+ /*
+ Wait until signalled that one more binlog dropped to zero, then check
+ again.
+ */
+ mysql_cond_wait(&COND_xid_list, &LOCK_xid_list);
+ }
+
+ /*
+ Now all XIDs are fully flushed to disk, and we are holding LOCK_log so
+ no new ones will be written. So we can proceed to delete the logs.
+ */
+ mysql_mutex_unlock(&LOCK_xid_list);
+ }
+
/*
The following mutex is needed to ensure that no threads call
'delete thd' as we would then risk missing a 'rollback' from this
@@ -3616,10 +3794,8 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd)
goto err;
}
}
- if (!thd->slave_thread)
- need_start_event=1;
- if (!open_index_file(index_file_name, 0, FALSE))
- if ((error= open(save_name, log_type, 0, io_cache_type, no_auto_events, max_size, 0, FALSE)))
+ if (create_new_log && !open_index_file(index_file_name, 0, FALSE))
+ if ((error= open(save_name, log_type, 0, io_cache_type, max_size, 0, FALSE)))
goto err;
my_free((void *) save_name);
@@ -3627,6 +3803,31 @@ err:
if (error == 1)
name= const_cast<char*>(save_name);
mysql_mutex_unlock(&LOCK_thread_count);
+
+ if (!is_relay_log)
+ {
+ xid_count_per_binlog *b;
+ /*
+ Remove all entries in the xid_count list except the last.
+ Normally we will just be deleting all the entries that we waited for to
+ drop to zero above. But if we fail during RESET MASTER for some reason
+ then we will not have created any new log file, and we may keep the last
+ of the old entries.
+ */
+ mysql_mutex_lock(&LOCK_xid_list);
+ for (;;)
+ {
+ b= binlog_xid_count_list.head();
+ DBUG_ASSERT(b /* List can never become empty. */);
+ if (b->binlog_id == current_binlog_id)
+ break;
+ DBUG_ASSERT(b->xid_count == 0);
+ my_free(binlog_xid_count_list.get());
+ }
+ reset_master_pending= false;
+ mysql_mutex_unlock(&LOCK_xid_list);
+ }
+
mysql_mutex_unlock(&LOCK_index);
mysql_mutex_unlock(&LOCK_log);
DBUG_RETURN(error);
@@ -3679,7 +3880,7 @@ int MYSQL_BIN_LOG::purge_first_log(Relay_log_info* rli, bool included)
DBUG_ENTER("purge_first_log");
DBUG_ASSERT(is_open());
- DBUG_ASSERT(rli->slave_running == 1);
+ DBUG_ASSERT(rli->slave_running == MYSQL_SLAVE_RUN_NOT_CONNECT);
DBUG_ASSERT(!strcmp(rli->linfo.log_file_name,rli->event_relay_log_name));
mysql_mutex_lock(&LOCK_index);
@@ -3836,8 +4037,7 @@ int MYSQL_BIN_LOG::purge_logs(const char *to_log,
if ((error=find_log_pos(&log_info, NullS, 0 /*no mutex*/)))
goto err;
while ((strcmp(to_log,log_info.log_file_name) || (exit_loop=included)) &&
- !is_active(log_info.log_file_name) &&
- !log_in_use(log_info.log_file_name))
+ can_purge_log(log_info.log_file_name))
{
if ((error= register_purge_index_entry(log_info.log_file_name)))
{
@@ -3951,7 +4151,7 @@ int MYSQL_BIN_LOG::sync_purge_index_file()
DBUG_ENTER("MYSQL_BIN_LOG::sync_purge_index_file");
if ((error= flush_io_cache(&purge_index_file)) ||
- (error= my_sync(purge_index_file.file, MYF(MY_WME))))
+ (error= my_sync(purge_index_file.file, MYF(MY_WME|MY_SYNC_FILESIZE))))
DBUG_RETURN(error);
DBUG_RETURN(error);
@@ -4187,8 +4387,7 @@ int MYSQL_BIN_LOG::purge_logs_before_date(time_t purge_time)
goto err;
while (strcmp(log_file_name, log_info.log_file_name) &&
- !is_active(log_info.log_file_name) &&
- !log_in_use(log_info.log_file_name))
+ can_purge_log(log_info.log_file_name))
{
if (!mysql_file_stat(m_key_file_log,
log_info.log_file_name, &stat_area, MYF(0)))
@@ -4243,6 +4442,28 @@ err:
mysql_mutex_unlock(&LOCK_index);
DBUG_RETURN(error);
}
+
+
+bool
+MYSQL_BIN_LOG::can_purge_log(const char *log_file_name)
+{
+ xid_count_per_binlog *b;
+
+ if (is_active(log_file_name))
+ return false;
+ mysql_mutex_lock(&LOCK_xid_list);
+ {
+ I_List_iterator<xid_count_per_binlog> it(binlog_xid_count_list);
+ while ((b= it++) &&
+ 0 != strncmp(log_file_name+dirname_length(log_file_name),
+ b->binlog_name, b->binlog_name_len))
+ ;
+ }
+ mysql_mutex_unlock(&LOCK_xid_list);
+ if (b)
+ return false;
+ return !log_in_use(log_file_name);
+}
#endif /* HAVE_REPLICATION */
@@ -4336,26 +4557,6 @@ int MYSQL_BIN_LOG::new_file_impl(bool need_lock)
mysql_mutex_assert_owner(&LOCK_log);
mysql_mutex_assert_owner(&LOCK_index);
- /*
- if binlog is used as tc log, be sure all xids are "unlogged",
- so that on recover we only need to scan one - latest - binlog file
- for prepared xids. As this is expected to be a rare event,
- simple wait strategy is enough. We're locking LOCK_log to be sure no
- new Xid_log_event's are added to the log (and prepared_xids is not
- increased), and waiting on COND_prep_xids for late threads to
- catch up.
- */
- if (prepared_xids)
- {
- tc_log_page_waits++;
- mysql_mutex_lock(&LOCK_prep_xids);
- while (prepared_xids) {
- DBUG_PRINT("info", ("prepared_xids=%lu", prepared_xids));
- mysql_cond_wait(&COND_prep_xids, &LOCK_prep_xids);
- }
- mysql_mutex_unlock(&LOCK_prep_xids);
- }
-
/* Reuse old name if not binlog and not update log */
new_name_ptr= name;
@@ -4370,7 +4571,6 @@ int MYSQL_BIN_LOG::new_file_impl(bool need_lock)
if (log_type == LOG_BIN)
{
- if (!no_auto_events)
{
/*
We log the whole file name for log file as the user may decide
@@ -4445,7 +4645,7 @@ int MYSQL_BIN_LOG::new_file_impl(bool need_lock)
/* reopen the binary log file. */
file_to_open= new_name_ptr;
error= open(old_name, log_type, new_name_ptr, io_cache_type,
- no_auto_events, max_size, 1, FALSE);
+ max_size, 1, FALSE);
}
/* handle reopening errors */
@@ -4517,7 +4717,7 @@ bool MYSQL_BIN_LOG::append(Log_event* ev)
DBUG_PRINT("info",("max_size: %lu",max_size));
if (flush_and_sync(0))
goto err;
- if ((uint) my_b_append_tell(&log_file) > max_size)
+ if (my_b_append_tell(&log_file) > max_size)
error= new_file_without_locking();
err:
mysql_mutex_unlock(&LOCK_log);
@@ -4548,7 +4748,7 @@ bool MYSQL_BIN_LOG::appendv(const char* buf, uint len,...)
DBUG_PRINT("info",("max_size: %lu",max_size));
if (flush_and_sync(0))
goto err;
- if ((uint) my_b_append_tell(&log_file) > max_size)
+ if (my_b_append_tell(&log_file) > max_size)
error= new_file_without_locking();
err:
if (!error)
@@ -4568,7 +4768,7 @@ bool MYSQL_BIN_LOG::flush_and_sync(bool *synced)
if (sync_period && ++sync_counter >= sync_period)
{
sync_counter= 0;
- err= mysql_file_sync(fd, MYF(MY_WME));
+ err= mysql_file_sync(fd, MYF(MY_WME|MY_SYNC_FILESIZE));
if (synced)
*synced= 1;
#ifndef DBUG_OFF
@@ -5070,6 +5270,8 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate)
bool is_trans_cache= FALSE;
bool using_trans= event_info->use_trans_cache();
bool direct= event_info->use_direct_logging();
+ ulong prev_binlog_id;
+ LINT_INIT(prev_binlog_id);
if (thd->binlog_evt_union.do_union)
{
@@ -5121,6 +5323,7 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate)
file= &log_file;
my_org_b_tell= my_b_tell(file);
mysql_mutex_lock(&LOCK_log);
+ prev_binlog_id= current_binlog_id;
}
else
{
@@ -5266,7 +5469,7 @@ err:
mysql_mutex_unlock(&LOCK_log);
if (check_purge)
- purge();
+ checkpoint_and_purge(prev_binlog_id);
}
if (error)
@@ -5351,6 +5554,64 @@ bool general_log_write(THD *thd, enum enum_server_command command,
return FALSE;
}
+
+/*
+ I would like to make this function static, but this causes compiler warnings
+ when it is declared as friend function in log.h.
+*/
+void
+binlog_checkpoint_callback(void *cookie)
+{
+ MYSQL_BIN_LOG::xid_count_per_binlog *entry=
+ (MYSQL_BIN_LOG::xid_count_per_binlog *)cookie;
+ /*
+ For every supporting engine, we increment the xid_count and issue a
+ commit_checkpoint_request(). Then we can count when all
+ commit_checkpoint_notify() callbacks have occured, and then log a new
+ binlog checkpoint event.
+ */
+ mysql_bin_log.mark_xids_active(entry->binlog_id, 1);
+}
+
+
+/*
+ Request a commit checkpoint from each supporting engine.
+ This must be called after each binlog rotate, and after LOCK_log has been
+ released. The xid_count value in the xid_count_per_binlog entry was
+ incremented by 1 and will be decremented in this function; this ensures
+ that the entry will not go away early despite LOCK_log not being held.
+*/
+void
+MYSQL_BIN_LOG::do_checkpoint_request(ulong binlog_id)
+{
+ xid_count_per_binlog *entry;
+
+ /*
+ Find the binlog entry, and invoke commit_checkpoint_request() on it in
+ each supporting storage engine.
+ */
+ mysql_mutex_lock(&LOCK_xid_list);
+ I_List_iterator<xid_count_per_binlog> it(binlog_xid_count_list);
+ do {
+ entry= it++;
+ DBUG_ASSERT(entry /* binlog_id is always somewhere in the list. */);
+ } while (entry->binlog_id != binlog_id);
+ mysql_mutex_unlock(&LOCK_xid_list);
+
+ ha_commit_checkpoint_request(entry, binlog_checkpoint_callback);
+ /*
+ When we rotated the binlog, we incremented xid_count to make sure the
+ entry would not go away until this point, where we have done all necessary
+ commit_checkpoint_request() calls.
+ So now we can (and must) decrease the count - when it reaches zero, we
+ will know that both all pending unlog() and all pending
+ commit_checkpoint_notify() calls are done, and we can log a new binlog
+ checkpoint.
+ */
+ mark_xid_done(binlog_id, true);
+}
+
+
/**
The method executes rotation when LOCK_log is already acquired
by the caller.
@@ -5359,6 +5620,15 @@ bool general_log_write(THD *thd, enum enum_server_command command,
@param check_purge is set to true if rotation took place
@note
+ Caller _must_ check the check_purge variable. If this is set, it means
+ that the binlog was rotated, and caller _must_ ensure that
+ do_checkpoint_request() is called later with the binlog_id of the rotated
+ binlog file. The call to do_checkpoint_request() must happen after
+ LOCK_log is released (which is why we cannot simply do it here).
+ Usually, checkpoint_and_purge() is appropriate, as it will both handle
+ the checkpointing and any needed purging of old logs.
+
+ @note
If rotation fails, for instance the server was unable
to create a new log file, we still try to write an
incident event to the current log.
@@ -5376,7 +5646,27 @@ int MYSQL_BIN_LOG::rotate(bool force_rotate, bool* check_purge)
if (force_rotate || (my_b_tell(&log_file) >= (my_off_t) max_size))
{
+ ulong binlog_id= current_binlog_id;
+ /*
+ We rotate the binlog, so we need to start a commit checkpoint in all
+ supporting engines - when it finishes, we can log a new binlog checkpoint
+ event.
+
+ But we cannot start the checkpoint here - there could be a group commit
+ still in progress which needs to be included in the checkpoint, and
+ besides we do not want to do the (possibly expensive) checkpoint while
+ LOCK_log is held.
+
+ On the other hand, we must be sure that the xid_count entry for the
+ previous log does not go away until we start the checkpoint - which it
+ could do as it is no longer the most recent. So we increment xid_count
+ (to count the pending checkpoint request) - this will fix the entry in
+ place until we decrement again in do_checkpoint_request().
+ */
+ mark_xids_active(binlog_id, 1);
+
if ((error= new_file_without_locking()))
+ {
/**
Be conservative... There are possible lost events (eg,
failing to log the Execute_load_query_log_event
@@ -5389,7 +5679,14 @@ int MYSQL_BIN_LOG::rotate(bool force_rotate, bool* check_purge)
if (!write_incident_already_locked(current_thd))
flush_and_sync(0);
- *check_purge= true;
+ /*
+ We failed to rotate - so we have to decrement the xid_count back that
+ we incremented before attempting the rotate.
+ */
+ mark_xid_done(binlog_id, false);
+ }
+ else
+ *check_purge= true;
}
DBUG_RETURN(error);
}
@@ -5417,6 +5714,13 @@ void MYSQL_BIN_LOG::purge()
#endif
}
+
+void MYSQL_BIN_LOG::checkpoint_and_purge(ulong binlog_id)
+{
+ do_checkpoint_request(binlog_id);
+ purge();
+}
+
/**
The method is a shortcut of @c rotate() and @c purge().
LOCK_log is acquired prior to rotate and is released after it.
@@ -5429,11 +5733,13 @@ void MYSQL_BIN_LOG::purge()
int MYSQL_BIN_LOG::rotate_and_purge(bool force_rotate)
{
int error= 0;
+ ulong prev_binlog_id;
DBUG_ENTER("MYSQL_BIN_LOG::rotate_and_purge");
bool check_purge= false;
//todo: fix the macro def and restore safe_mutex_assert_not_owner(&LOCK_log);
mysql_mutex_lock(&LOCK_log);
+ prev_binlog_id= current_binlog_id;
if ((error= rotate(force_rotate, &check_purge)))
check_purge= false;
/*
@@ -5443,7 +5749,7 @@ int MYSQL_BIN_LOG::rotate_and_purge(bool force_rotate)
mysql_mutex_unlock(&LOCK_log);
if (check_purge)
- purge();
+ checkpoint_and_purge(prev_binlog_id);
DBUG_RETURN(error);
}
@@ -5774,11 +6080,13 @@ bool MYSQL_BIN_LOG::write_incident(THD *thd)
uint error= 0;
my_off_t offset;
bool check_purge= false;
+ ulong prev_binlog_id;
DBUG_ENTER("MYSQL_BIN_LOG::write_incident");
mysql_mutex_lock(&LOCK_log);
if (likely(is_open()))
{
+ prev_binlog_id= current_binlog_id;
if (!(error= write_incident_already_locked(thd)) &&
!(error= flush_and_sync(0)))
{
@@ -5798,12 +6106,51 @@ bool MYSQL_BIN_LOG::write_incident(THD *thd)
mysql_mutex_unlock(&LOCK_log);
if (check_purge)
- purge();
+ checkpoint_and_purge(prev_binlog_id);
}
DBUG_RETURN(error);
}
+void
+MYSQL_BIN_LOG::write_binlog_checkpoint_event_already_locked(const char *name,
+ uint len)
+{
+ my_off_t offset;
+ Binlog_checkpoint_log_event ev(name, len);
+ /*
+ Note that we must sync the binlog checkpoint to disk.
+ Otherwise a subsequent log purge could delete binlogs that XA recovery
+ thinks are needed (even though they are not really).
+ */
+ if (!ev.write(&log_file) && !flush_and_sync(0))
+ {
+ signal_update();
+ }
+ else
+ {
+ /*
+ If we fail to write the checkpoint event, something is probably really
+ bad with the binlog. We complain in the error log.
+
+ Note that failure to write binlog checkpoint does not compromise the
+ ability to do crash recovery - crash recovery will just have to scan a
+ bit more of the binlog than strictly necessary.
+ */
+ sql_print_error("Failed to write binlog checkpoint event to binary log\n");
+ }
+
+ offset= my_b_tell(&log_file);
+ /*
+ Take mutex to protect against a reader seeing partial writes of 64-bit
+ offset on 32-bit CPUs.
+ */
+ mysql_mutex_lock(&LOCK_commit_ordered);
+ last_commit_pos_offset= offset;
+ mysql_mutex_unlock(&LOCK_commit_ordered);
+}
+
+
/**
Write a cached log entry to the binary log.
- To support transaction over replication, we wrap the transaction
@@ -5836,6 +6183,7 @@ MYSQL_BIN_LOG::write_transaction_to_binlog(THD *thd,
bool using_trx_cache)
{
group_commit_entry entry;
+ Ha_trx_info *ha_info;
DBUG_ENTER("MYSQL_BIN_LOG::write_transaction_to_binlog");
entry.thd= thd;
@@ -5844,6 +6192,15 @@ MYSQL_BIN_LOG::write_transaction_to_binlog(THD *thd,
entry.all= all;
entry.using_stmt_cache= using_stmt_cache;
entry.using_trx_cache= using_trx_cache;
+ entry.need_unlog= false;
+ ha_info= all ? thd->transaction.all.ha_list : thd->transaction.stmt.ha_list;
+ for (; ha_info; ha_info= ha_info->next())
+ {
+ if (ha_info->is_started() && ha_info->ht() != binlog_hton &&
+ !ha_info->ht()->commit_checkpoint_request)
+ entry.need_unlog= true;
+ break;
+ }
/*
Log "BEGIN" at the beginning of every transaction. Here, a transaction is
@@ -5932,6 +6289,18 @@ MYSQL_BIN_LOG::write_transaction_to_binlog_events(group_commit_entry *entry)
{
next->thd->signal_wakeup_ready();
}
+ else
+ {
+ /*
+ If we rotated the binlog, and if we are using the unoptimized thread
+ scheduling where every thread runs its own commit_ordered(), then we
+ must do the commit checkpoint and log purge here, after all
+ commit_ordered() calls have finished, and locks have been released.
+ */
+ if (entry->check_purge)
+ checkpoint_and_purge(entry->binlog_id);
+ }
+
}
if (likely(!entry->error))
@@ -5962,8 +6331,9 @@ MYSQL_BIN_LOG::write_transaction_to_binlog_events(group_commit_entry *entry)
we need to mark it as not needed for recovery (unlog() is not called
for a transaction if log_xid() fails).
*/
- if (entry->cache_mngr->using_xa && entry->cache_mngr->xa_xid)
- mark_xid_done();
+ if (entry->cache_mngr->using_xa && entry->cache_mngr->xa_xid &&
+ entry->cache_mngr->need_unlog)
+ mark_xid_done(entry->cache_mngr->binlog_id, true);
return 1;
}
@@ -5983,11 +6353,12 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
{
uint xid_count= 0;
my_off_t UNINIT_VAR(commit_offset);
- group_commit_entry *current;
- group_commit_entry *last_in_queue;
+ group_commit_entry *current, *last_in_queue;
group_commit_entry *queue= NULL;
bool check_purge= false;
+ ulong binlog_id;
DBUG_ENTER("MYSQL_BIN_LOG::trx_group_commit_leader");
+ LINT_INIT(binlog_id);
DBUG_ASSERT(is_open());
if (likely(is_open())) // Should always be true
@@ -5998,6 +6369,7 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
*/
mysql_mutex_lock(&LOCK_log);
DEBUG_SYNC(leader->thd, "commit_after_get_LOCK_log");
+ binlog_id= current_binlog_id;
mysql_mutex_lock(&LOCK_prepare_ordered);
current= group_commit_queue;
@@ -6044,7 +6416,24 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
commit_offset= my_b_write_tell(&log_file);
cache_mngr->last_commit_pos_offset= commit_offset;
if (cache_mngr->using_xa && cache_mngr->xa_xid)
- xid_count++;
+ {
+ /*
+ If all storage engines support commit_checkpoint_request(), then we
+ do not need to keep track of when this XID is durably committed.
+ Instead we will just ask the storage engine to durably commit all its
+ XIDs when we rotate a binlog file.
+ */
+ if (current->need_unlog)
+ {
+ xid_count++;
+ cache_mngr->need_unlog= true;
+ cache_mngr->binlog_id= binlog_id;
+ }
+ else
+ cache_mngr->need_unlog= false;
+
+ cache_mngr->delayed_error= false;
+ }
}
bool synced= 0;
@@ -6087,30 +6476,34 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
}
/*
- if any commit_events are Xid_log_event, increase the number of
- prepared_xids (it's decreased in ::unlog()). Binlog cannot be rotated
- if there're prepared xids in it - see the comment in new_file() for
- an explanation.
- If no Xid_log_events (then it's all Query_log_event) rotate binlog,
- if necessary.
+ If any commit_events are Xid_log_event, increase the number of pending
+ XIDs in current binlog (it's decreased in ::unlog()). When the count in
+ a (not active) binlog file reaches zero, we know that it is no longer
+ needed in XA recovery, and we can log a new binlog checkpoint event.
*/
if (xid_count > 0)
{
- mark_xids_active(xid_count);
+ mark_xids_active(binlog_id, xid_count);
}
- else
+
+ if (rotate(false, &check_purge))
{
- if (rotate(false, &check_purge))
- {
- /*
- If we fail to rotate, which thread should get the error?
- We give the error to the *last* transaction thread; that seems to
- make the most sense, as it was the last to write to the log.
- */
- last_in_queue->error= ER_ERROR_ON_WRITE;
- last_in_queue->commit_errno= errno;
- check_purge= false;
- }
+ /*
+ If we fail to rotate, which thread should get the error?
+ We give the error to the leader, as any my_error() thrown inside
+ rotate() will have been registered for the leader THD.
+
+ However we must not return error from here - that would cause
+ ha_commit_trans() to abort and rollback the transaction, which would
+ leave an inconsistent state with the transaction committed in the
+ binlog but rolled back in the engine.
+
+ Instead set a flag so that we can return error later, from unlog(),
+ when the transaction has been safely committed in the engine.
+ */
+ leader->cache_mngr->delayed_error= true;
+ my_error(ER_ERROR_ON_WRITE, MYF(ME_NOREFRESH), name, errno);
+ check_purge= false;
}
}
@@ -6125,9 +6518,6 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
*/
mysql_mutex_unlock(&LOCK_log);
- if (check_purge)
- purge();
-
DEBUG_SYNC(leader->thd, "commit_after_release_LOCK_log");
++num_group_commits;
@@ -6145,6 +6535,15 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
mysql_cond_wait(&COND_queue_busy, &LOCK_commit_ordered);
group_commit_queue_busy= TRUE;
+ /*
+ Set these so parent can run checkpoint_and_purge() in last thread.
+ (When using optimized thread scheduling, we run checkpoint_and_purge()
+ in this function, so parent does not need to and we need not set these
+ values).
+ */
+ last_in_queue->check_purge= check_purge;
+ last_in_queue->binlog_id= binlog_id;
+
/* Note that we return with LOCK_commit_ordered locked! */
DBUG_VOID_RETURN;
}
@@ -6160,7 +6559,8 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
DEBUG_SYNC(leader->thd, "commit_loop_entry_commit_ordered");
++num_commits;
- if (current->cache_mngr->using_xa && !current->error)
+ if (current->cache_mngr->using_xa && !current->error &&
+ DBUG_EVALUATE_IF("skip_commit_ordered", 0, 1))
run_commit_ordered(current->thd, current->all);
/*
@@ -6174,6 +6574,10 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
}
DEBUG_SYNC(leader->thd, "commit_after_group_run_commit_ordered");
mysql_mutex_unlock(&LOCK_commit_ordered);
+ DEBUG_SYNC(leader->thd, "commit_after_group_release_commit_ordered");
+
+ if (check_purge)
+ checkpoint_and_purge(binlog_id);
DBUG_VOID_RETURN;
}
@@ -6332,7 +6736,7 @@ void MYSQL_BIN_LOG::close(uint exiting)
if (log_state == LOG_OPENED)
{
#ifdef HAVE_REPLICATION
- if (log_type == LOG_BIN && !no_auto_events &&
+ if (log_type == LOG_BIN &&
(exiting & LOG_CLOSE_STOP_EVENT))
{
Stop_log_event s;
@@ -6590,16 +6994,33 @@ static void print_buffer_to_file(enum loglevel level, const char *buffer,
time_t skr;
struct tm tm_tmp;
struct tm *start;
+ THD *thd;
+ int tag_length= 0;
+ char tag[NAME_LEN];
DBUG_ENTER("print_buffer_to_file");
DBUG_PRINT("enter",("buffer: %s", buffer));
+ if (mysqld_server_initialized && (thd= current_thd))
+ {
+ if (thd->connection_name.length)
+ {
+ /*
+ Add tag for slaves so that the user can see from which connection
+ the error originates.
+ */
+ tag_length= my_snprintf(tag, sizeof(tag), ER(ER_MASTER_LOG_PREFIX),
+ (int) thd->connection_name.length,
+ thd->connection_name.str);
+ }
+ }
+
mysql_mutex_lock(&LOCK_error_log);
skr= my_time(0);
localtime_r(&skr, &tm_tmp);
start=&tm_tmp;
- fprintf(stderr, "%02d%02d%02d %2d:%02d:%02d [%s] %.*s\n",
+ fprintf(stderr, "%02d%02d%02d %2d:%02d:%02d [%s] %.*s%.*s\n",
start->tm_year % 100,
start->tm_mon+1,
start->tm_mday,
@@ -6608,6 +7029,7 @@ static void print_buffer_to_file(enum loglevel level, const char *buffer,
start->tm_sec,
(level == ERROR_LEVEL ? "ERROR" : level == WARNING_LEVEL ?
"Warning" : "Note"),
+ tag_length, tag,
(int) length, buffer);
fflush(stderr);
@@ -6966,6 +7388,8 @@ int TC_LOG_MMAP::open(const char *opt_name)
mysql_mutex_init(key_LOCK_sync, &LOCK_sync, MY_MUTEX_INIT_FAST);
mysql_mutex_init(key_LOCK_active, &LOCK_active, MY_MUTEX_INIT_FAST);
mysql_mutex_init(key_LOCK_pool, &LOCK_pool, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_LOCK_pending_checkpoint, &LOCK_pending_checkpoint,
+ MY_MUTEX_INIT_FAST);
mysql_cond_init(key_COND_active, &COND_active, 0);
mysql_cond_init(key_COND_pool, &COND_pool, 0);
mysql_cond_init(key_TC_LOG_MMAP_COND_queue_busy, &COND_queue_busy, 0);
@@ -7218,17 +7642,93 @@ int TC_LOG_MMAP::sync()
return err;
}
+static void
+mmap_do_checkpoint_callback(void *data)
+{
+ TC_LOG_MMAP::pending_cookies *pending=
+ static_cast<TC_LOG_MMAP::pending_cookies *>(data);
+ ++pending->pending_count;
+}
+
+int TC_LOG_MMAP::unlog(ulong cookie, my_xid xid)
+{
+ pending_cookies *full_buffer= NULL;
+ DBUG_ASSERT(*(my_xid *)(data+cookie) == xid);
+
+ /*
+ Do not delete the entry immediately, as there may be participating storage
+ engines which implement commit_checkpoint_request(), and thus have not yet
+ flushed the commit durably to disk.
+
+ Instead put it in a queue - and periodically, we will request a checkpoint
+ from all engines and delete a whole batch at once.
+ */
+ mysql_mutex_lock(&LOCK_pending_checkpoint);
+ if (pending_checkpoint == NULL)
+ {
+ uint32 size= sizeof(*pending_checkpoint);
+ if (!(pending_checkpoint=
+ (pending_cookies *)my_malloc(size, MYF(MY_ZEROFILL))))
+ {
+ my_error(ER_OUTOFMEMORY, MYF(0), size);
+ mysql_mutex_unlock(&LOCK_pending_checkpoint);
+ return 1;
+ }
+ }
+
+ pending_checkpoint->cookies[pending_checkpoint->count++]= cookie;
+ if (pending_checkpoint->count == sizeof(pending_checkpoint->cookies) /
+ sizeof(pending_checkpoint->cookies[0]))
+ {
+ full_buffer= pending_checkpoint;
+ pending_checkpoint= NULL;
+ }
+ mysql_mutex_unlock(&LOCK_pending_checkpoint);
+
+ if (full_buffer)
+ {
+ /*
+ We do an extra increment and notify here - this ensures that
+ things work also if there are no engines at all that support
+ commit_checkpoint_request.
+ */
+ ++full_buffer->pending_count;
+ ha_commit_checkpoint_request(full_buffer, mmap_do_checkpoint_callback);
+ commit_checkpoint_notify(full_buffer);
+ }
+ return 0;
+}
+
+
+void
+TC_LOG_MMAP::commit_checkpoint_notify(void *cookie)
+{
+ uint count;
+ pending_cookies *pending= static_cast<pending_cookies *>(cookie);
+ mysql_mutex_lock(&LOCK_pending_checkpoint);
+ DBUG_ASSERT(pending->pending_count > 0);
+ count= --pending->pending_count;
+ mysql_mutex_unlock(&LOCK_pending_checkpoint);
+ if (count == 0)
+ {
+ uint i;
+ for (i= 0; i < sizeof(pending->cookies)/sizeof(pending->cookies[0]); ++i)
+ delete_entry(pending->cookies[i]);
+ my_free(pending);
+ }
+}
+
+
/**
erase xid from the page, update page free space counters/pointers.
cookie points directly to the memory where xid was logged.
*/
-int TC_LOG_MMAP::unlog(ulong cookie, my_xid xid)
+int TC_LOG_MMAP::delete_entry(ulong cookie)
{
PAGE *p=pages+(cookie/tc_log_page_size);
my_xid *x=(my_xid *)(data+cookie);
- DBUG_ASSERT(*x == xid);
DBUG_ASSERT(x >= p->start && x < p->end);
mysql_mutex_lock(&p->lock);
@@ -7252,6 +7752,7 @@ void TC_LOG_MMAP::close()
mysql_mutex_destroy(&LOCK_sync);
mysql_mutex_destroy(&LOCK_active);
mysql_mutex_destroy(&LOCK_pool);
+ mysql_mutex_destroy(&LOCK_pending_checkpoint);
mysql_cond_destroy(&COND_pool);
mysql_cond_destroy(&COND_active);
mysql_cond_destroy(&COND_queue_busy);
@@ -7274,9 +7775,12 @@ void TC_LOG_MMAP::close()
}
if (inited>=5) // cannot do in the switch because of Windows
mysql_file_delete(key_file_tclog, logname, MYF(MY_WME));
+ if (pending_checkpoint)
+ my_free(pending_checkpoint);
inited=0;
}
+
int TC_LOG_MMAP::recover()
{
HASH xids;
@@ -7362,14 +7866,6 @@ int TC_LOG::using_heuristic_recover()
/****** transaction coordinator log for 2pc - binlog() based solution ******/
#define TC_LOG_BINLOG MYSQL_BIN_LOG
-/**
- @todo
- keep in-memory list of prepared transactions
- (add to list in log(), remove on unlog())
- and copy it to the new binlog if rotated
- but let's check the behaviour of tc_log_page_waits first!
-*/
-
int TC_LOG_BINLOG::open(const char *opt_name)
{
LOG_INFO log_info;
@@ -7378,10 +7874,6 @@ int TC_LOG_BINLOG::open(const char *opt_name)
DBUG_ASSERT(total_ha_2pc > 1);
DBUG_ASSERT(opt_name && opt_name[0]);
- mysql_mutex_init(key_BINLOG_LOCK_prep_xids,
- &LOCK_prep_xids, MY_MUTEX_INIT_FAST);
- mysql_cond_init(key_BINLOG_COND_prep_xids, &COND_prep_xids, 0);
-
if (!my_b_inited(&index_file))
{
/* There was a failure to open the index file, can't open the binlog */
@@ -7392,7 +7884,7 @@ int TC_LOG_BINLOG::open(const char *opt_name)
if (using_heuristic_recover())
{
/* generate a new binlog to mask a corrupted one */
- open(opt_name, LOG_BIN, 0, WRITE_CACHE, 0, max_binlog_size, 0, TRUE);
+ open(opt_name, LOG_BIN, 0, WRITE_CACHE, max_binlog_size, 0, TRUE);
cleanup();
return 1;
}
@@ -7440,7 +7932,8 @@ int TC_LOG_BINLOG::open(const char *opt_name)
ev->flags & LOG_EVENT_BINLOG_IN_USE_F)
{
sql_print_information("Recovering after a crash using %s", opt_name);
- error= recover(&log, (Format_description_log_event *)ev);
+ error= recover(&log_info, log_name, &log,
+ (Format_description_log_event *)ev);
}
else
error=0;
@@ -7460,9 +7953,6 @@ err:
/** This is called on shutdown, after ha_panic. */
void TC_LOG_BINLOG::close()
{
- DBUG_ASSERT(prepared_xids==0);
- mysql_mutex_destroy(&LOCK_prep_xids);
- mysql_cond_destroy(&COND_prep_xids);
}
/*
@@ -7486,7 +7976,17 @@ TC_LOG_BINLOG::log_and_order(THD *thd, my_xid xid, bool all,
DEBUG_SYNC(thd, "binlog_after_log_and_order");
- DBUG_RETURN(!err);
+ if (err)
+ DBUG_RETURN(0);
+ /*
+ If using explicit user XA, we will not have XID. We must still return a
+ non-zero cookie (as zero cookie signals error).
+ */
+ if (!xid || !cache_mngr->need_unlog)
+ DBUG_RETURN(BINLOG_COOKIE_DUMMY(cache_mngr->delayed_error));
+ else
+ DBUG_RETURN(BINLOG_COOKIE_MAKE(cache_mngr->binlog_id,
+ cache_mngr->delayed_error));
}
/*
@@ -7501,57 +8001,169 @@ TC_LOG_BINLOG::log_and_order(THD *thd, my_xid xid, bool all,
binary log.
*/
void
-TC_LOG_BINLOG::mark_xids_active(uint xid_count)
+TC_LOG_BINLOG::mark_xids_active(ulong binlog_id, uint xid_count)
{
+ xid_count_per_binlog *b;
+
DBUG_ENTER("TC_LOG_BINLOG::mark_xids_active");
- DBUG_PRINT("info", ("xid_count=%u", xid_count));
- mysql_mutex_lock(&LOCK_prep_xids);
- prepared_xids+= xid_count;
- mysql_mutex_unlock(&LOCK_prep_xids);
+ DBUG_PRINT("info", ("binlog_id=%lu xid_count=%u", binlog_id, xid_count));
+
+ mysql_mutex_lock(&LOCK_xid_list);
+ I_List_iterator<xid_count_per_binlog> it(binlog_xid_count_list);
+ while ((b= it++))
+ {
+ if (b->binlog_id == binlog_id)
+ {
+ b->xid_count += xid_count;
+ break;
+ }
+ }
+ /*
+ As we do not delete elements until count reach zero, elements should always
+ be found.
+ */
+ DBUG_ASSERT(b);
+ mysql_mutex_unlock(&LOCK_xid_list);
DBUG_VOID_RETURN;
}
/*
- Once an XID is committed, it is safe to rotate the binary log, as it can no
- longer be needed during crash recovery.
+ Once an XID is committed, it can no longer be needed during crash recovery,
+ as it has been durably recorded on disk as "committed".
This function is called to mark an XID this way. It needs to decrease the
- count of pending XIDs, and signal the log rotator thread when it reaches zero.
+ count of pending XIDs in the corresponding binlog. When the count reaches
+ zero (for an "old" binlog that is not the active one), that binlog file no
+ longer need to be scanned during crash recovery, so we can log a new binlog
+ checkpoint.
*/
void
-TC_LOG_BINLOG::mark_xid_done()
+TC_LOG_BINLOG::mark_xid_done(ulong binlog_id, bool write_checkpoint)
{
- my_bool send_signal;
+ xid_count_per_binlog *b;
+ bool first;
+ ulong current;
DBUG_ENTER("TC_LOG_BINLOG::mark_xid_done");
- mysql_mutex_lock(&LOCK_prep_xids);
- // prepared_xids can be 0 if the transaction had ignorable errors.
- DBUG_ASSERT(prepared_xids >= 0);
- if (prepared_xids > 0)
- prepared_xids--;
- send_signal= (prepared_xids == 0);
- mysql_mutex_unlock(&LOCK_prep_xids);
- if (send_signal) {
- DBUG_PRINT("info", ("prepared_xids=%lu", prepared_xids));
- mysql_cond_signal(&COND_prep_xids);
+
+ mysql_mutex_lock(&LOCK_xid_list);
+ current= current_binlog_id;
+ I_List_iterator<xid_count_per_binlog> it(binlog_xid_count_list);
+ first= true;
+ while ((b= it++))
+ {
+ if (b->binlog_id == binlog_id)
+ {
+ --b->xid_count;
+ break;
+ }
+ first= false;
+ }
+ /* Binlog is always found, as we do not remove until count reaches 0 */
+ DBUG_ASSERT(b);
+ /*
+ If a RESET MASTER is pending, we are about to remove all log files, and
+ the RESET MASTER thread is waiting for all pending unlog() calls to
+ complete while holding LOCK_log. In this case we should not log a binlog
+ checkpoint event (it would be deleted immediately anyway and we would
+ deadlock on LOCK_log) but just signal the thread.
+ */
+ if (unlikely(reset_master_pending))
+ {
+ mysql_cond_signal(&COND_xid_list);
+ mysql_mutex_unlock(&LOCK_xid_list);
+ DBUG_VOID_RETURN;
+ }
+
+ if (likely(binlog_id == current) || b->xid_count != 0 || !first ||
+ !write_checkpoint)
+ {
+ /* No new binlog checkpoint reached yet. */
+ mysql_mutex_unlock(&LOCK_xid_list);
+ DBUG_VOID_RETURN;
+ }
+
+ /*
+ Now log a binlog checkpoint for the first binlog file with a non-zero count.
+
+ Note that it is possible (though perhaps unlikely) that when count of
+ binlog (N-2) drops to zero, binlog (N-1) is already at zero. So we may
+ need to skip several entries before we find the one to log in the binlog
+ checkpoint event.
+
+ We chain the locking of LOCK_xid_list and LOCK_log, so that we ensure that
+ Binlog_checkpoint_events are logged in order. This simplifies recovery a
+ bit, as it can just take the last binlog checkpoint in the log, rather
+ than compare all found against each other to find the one pointing to the
+ most recent binlog.
+
+ Note also that we need to first release LOCK_xid_list, then aquire
+ LOCK_log, then re-aquire LOCK_xid_list. If we were to take LOCK_log while
+ holding LOCK_xid_list, we might deadlock with other threads that take the
+ locks in the opposite order.
+ */
+
+ mysql_mutex_unlock(&LOCK_xid_list);
+ mysql_mutex_lock(&LOCK_log);
+ mysql_mutex_lock(&LOCK_xid_list);
+ /* We need to reload current_binlog_id due to release/re-take of lock. */
+ current= current_binlog_id;
+
+ for (;;)
+ {
+ /* Remove initial element(s) with zero count. */
+ b= binlog_xid_count_list.head();
+ /*
+ We must not remove all elements in the list - the entry for the current
+ binlog must be present always.
+ */
+ DBUG_ASSERT(b);
+ if (b->binlog_id == current || b->xid_count > 0)
+ break;
+ my_free(binlog_xid_count_list.get());
}
+
+ mysql_mutex_unlock(&LOCK_xid_list);
+ write_binlog_checkpoint_event_already_locked(b->binlog_name,
+ b->binlog_name_len);
+ mysql_mutex_unlock(&LOCK_log);
DBUG_VOID_RETURN;
}
int TC_LOG_BINLOG::unlog(ulong cookie, my_xid xid)
{
DBUG_ENTER("TC_LOG_BINLOG::unlog");
- if (xid)
- mark_xid_done();
- /* As ::write_transaction_to_binlog() did not rotate, do it here. */
- DBUG_RETURN(rotate_and_purge(0));
+ if (!xid)
+ DBUG_RETURN(0);
+
+ if (!BINLOG_COOKIE_IS_DUMMY(cookie))
+ mark_xid_done(BINLOG_COOKIE_GET_ID(cookie), true);
+ /*
+ See comment in trx_group_commit_leader() - if rotate() gave a failure,
+ we delay the return of error code to here.
+ */
+ DBUG_RETURN(BINLOG_COOKIE_GET_ERROR_FLAG(cookie));
+}
+
+void
+TC_LOG_BINLOG::commit_checkpoint_notify(void *cookie)
+{
+ mark_xid_done(((xid_count_per_binlog *)cookie)->binlog_id, true);
}
-int TC_LOG_BINLOG::recover(IO_CACHE *log, Format_description_log_event *fdle)
+int TC_LOG_BINLOG::recover(LOG_INFO *linfo, const char *last_log_name,
+ IO_CACHE *first_log,
+ Format_description_log_event *fdle)
{
Log_event *ev;
HASH xids;
MEM_ROOT mem_root;
+ char binlog_checkpoint_name[FN_REFLEN];
+ bool binlog_checkpoint_found;
+ bool first_round;
+ IO_CACHE log;
+ File file= -1;
+ const char *errmsg;
if (! fdle->is_valid() ||
my_hash_init(&xids, &my_charset_bin, TC_LOG_PAGE_SIZE/3, 0,
@@ -7562,19 +8174,114 @@ int TC_LOG_BINLOG::recover(IO_CACHE *log, Format_description_log_event *fdle)
fdle->flags&= ~LOG_EVENT_BINLOG_IN_USE_F; // abort on the first error
- while ((ev= Log_event::read_log_event(log, 0, fdle,
- opt_master_verify_checksum))
- && ev->is_valid())
+ /*
+ Scan the binlog for XIDs that need to be committed if still in the
+ prepared stage.
+
+ Start with the latest binlog file, then continue with any other binlog
+ files if the last found binlog checkpoint indicates it is needed.
+ */
+
+ binlog_checkpoint_found= false;
+ first_round= true;
+ for (;;)
{
- if (ev->get_type_code() == XID_EVENT)
+ while ((ev= Log_event::read_log_event(first_round ? first_log : &log,
+ 0, fdle, opt_master_verify_checksum))
+ && ev->is_valid())
+ {
+ switch (ev->get_type_code())
+ {
+ 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))
+ {
+ delete ev;
+ goto err2;
+ }
+ break;
+ }
+ case BINLOG_CHECKPOINT_EVENT:
+ if (first_round)
+ {
+ uint dir_len;
+ Binlog_checkpoint_log_event *cev= (Binlog_checkpoint_log_event *)ev;
+ if (cev->binlog_file_len >= FN_REFLEN)
+ sql_print_warning("Incorrect binlog checkpoint event with too "
+ "long file name found.");
+ else
+ {
+ /*
+ Note that we cannot use make_log_name() here, as we have not yet
+ initialised MYSQL_BIN_LOG::log_file_name.
+ */
+ dir_len= dirname_length(last_log_name);
+ strmake(strnmov(binlog_checkpoint_name, last_log_name, dir_len),
+ cev->binlog_file_name, FN_REFLEN - 1 - dir_len);
+ binlog_checkpoint_found= true;
+ }
+ break;
+ }
+ default:
+ /* Nothing. */
+ break;
+ }
+ delete ev;
+ }
+
+ /*
+ 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.
+
+ If there was no binlog checkpoint event at all, this means the log was
+ written by an older version of MariaDB (or MySQL) - these always have an
+ (implicit) binlog checkpoint event at the start of the last binlog file.
+ */
+ if (first_round)
{
- 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 (!binlog_checkpoint_found)
+ break;
+ first_round= false;
+ DBUG_EXECUTE_IF("xa_recover_expect_master_bin_000004",
+ if (0 != strcmp("./master-bin.000004", binlog_checkpoint_name) &&
+ 0 != strcmp(".\\master-bin.000004", binlog_checkpoint_name))
+ DBUG_SUICIDE();
+ );
+ if (find_log_pos(linfo, binlog_checkpoint_name, 1))
+ {
+ sql_print_error("Binlog file '%s' not found in binlog index, needed "
+ "for recovery. Aborting.", binlog_checkpoint_name);
goto err2;
+ }
+ }
+ else
+ {
+ end_io_cache(&log);
+ mysql_file_close(file, MYF(MY_WME));
+ file= -1;
+ }
+
+ if (0 == strcmp(linfo->log_file_name, last_log_name))
+ break; // No more files to do
+ if ((file= open_binlog(&log, linfo->log_file_name, &errmsg)) < 0)
+ {
+ sql_print_error("%s", errmsg);
+ goto err2;
+ }
+ /*
+ We do not need to read the Format_description_log_event of other binlog
+ files. It is not possible for a binlog checkpoint to span multiple
+ binlog files written by different versions of the server. So we can use
+ the first one read for reading from all binlog files.
+ */
+ if (find_next_log(linfo, 1))
+ {
+ sql_print_error("Error reading binlog files during recovery. Aborting.");
+ goto err2;
}
- delete ev;
}
if (ha_recover(&xids))
@@ -7585,6 +8292,11 @@ int TC_LOG_BINLOG::recover(IO_CACHE *log, Format_description_log_event *fdle)
return 0;
err2:
+ if (file >= 0)
+ {
+ end_io_cache(&log);
+ mysql_file_close(file, MYF(MY_WME));
+ }
free_root(&mem_root, MYF(0));
my_hash_free(&xids);
err1:
@@ -7650,10 +8362,13 @@ binlog_checksum_update(MYSQL_THD thd, struct st_mysql_sys_var *var,
{
ulong value= *((ulong *)save);
bool check_purge= false;
+ ulong prev_binlog_id;
+ LINT_INIT(prev_binlog_id);
mysql_mutex_lock(mysql_bin_log.get_log_lock());
if(mysql_bin_log.is_open())
{
+ prev_binlog_id= mysql_bin_log.current_binlog_id;
if (binlog_checksum_options != value)
mysql_bin_log.checksum_alg_reset= (uint8) value;
if (mysql_bin_log.rotate(true, &check_purge))
@@ -7667,7 +8382,7 @@ binlog_checksum_update(MYSQL_THD thd, struct st_mysql_sys_var *var,
mysql_bin_log.checksum_alg_reset= BINLOG_CHECKSUM_ALG_UNDEF;
mysql_mutex_unlock(mysql_bin_log.get_log_lock());
if (check_purge)
- mysql_bin_log.purge();
+ mysql_bin_log.checkpoint_and_purge(prev_binlog_id);
}
diff --git a/sql/log.h b/sql/log.h
index b350f3882fa..0e887b48462 100644
--- a/sql/log.h
+++ b/sql/log.h
@@ -1,5 +1,5 @@
-/*
- Copyright (c) 2005, 2011, Oracle and/or its affiliates.
+/* Copyright (c) 2005, 2012, Oracle and/or its affiliates.
+ Copyright (c) 2009, 2012, 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
@@ -49,6 +49,7 @@ class TC_LOG
bool need_prepare_ordered,
bool need_commit_ordered) = 0;
virtual int unlog(ulong cookie, my_xid xid)=0;
+ virtual void commit_checkpoint_notify(void *cookie)= 0;
protected:
/*
@@ -98,8 +99,12 @@ public:
return 1;
}
int unlog(ulong cookie, my_xid xid) { return 0; }
+ void commit_checkpoint_notify(void *cookie) { DBUG_ASSERT(0); };
};
+#define TC_LOG_PAGE_SIZE 8192
+#define TC_LOG_MIN_SIZE (3*TC_LOG_PAGE_SIZE)
+
#ifdef HAVE_MMAP
class TC_LOG_MMAP: public TC_LOG
{
@@ -110,6 +115,12 @@ class TC_LOG_MMAP: public TC_LOG
PS_DIRTY // new xids added since last sync
} PAGE_STATE;
+ struct pending_cookies {
+ uint count;
+ uint pending_count;
+ ulong cookies[TC_LOG_PAGE_SIZE];
+ };
+
private:
typedef struct st_page {
struct st_page *next; // page a linked in a fifo queue
@@ -141,7 +152,7 @@ class TC_LOG_MMAP: public TC_LOG
one has to use active->lock.
Same for LOCK_pool and LOCK_sync
*/
- mysql_mutex_t LOCK_active, LOCK_pool, LOCK_sync;
+ mysql_mutex_t LOCK_active, LOCK_pool, LOCK_sync, LOCK_pending_checkpoint;
mysql_cond_t COND_pool, COND_active;
/*
Queue of threads that need to call commit_ordered().
@@ -163,14 +174,16 @@ class TC_LOG_MMAP: public TC_LOG
*/
mysql_cond_t COND_queue_busy;
my_bool commit_ordered_queue_busy;
+ pending_cookies* pending_checkpoint;
public:
- TC_LOG_MMAP(): inited(0) {}
+ TC_LOG_MMAP(): inited(0), pending_checkpoint(0) {}
int open(const char *opt_name);
void close();
int log_and_order(THD *thd, my_xid xid, bool all,
bool need_prepare_ordered, bool need_commit_ordered);
int unlog(ulong cookie, my_xid xid);
+ void commit_checkpoint_notify(void *cookie);
int recover();
private:
@@ -178,6 +191,7 @@ class TC_LOG_MMAP: public TC_LOG
void get_active_from_pool();
int sync();
int overflow();
+ int delete_entry(ulong cookie);
};
#else
#define TC_LOG_MMAP TC_LOG_DUMMY
@@ -354,6 +368,35 @@ private:
time_t last_time;
};
+/*
+ We assign each binlog file an internal ID, used to identify them for unlog().
+ The IDs start from 0 and increment for each new binlog created.
+
+ In unlog() we need to know the ID of the binlog file that the corresponding
+ transaction was written into. We also need a special value for a corner
+ case where there is no corresponding binlog id (since nothing was logged).
+ And we need an error flag to mark that unlog() must return failure.
+
+ We use the following macros to pack all of this information into the single
+ ulong available with log_and_order() / unlog().
+
+ Note that we cannot use the value 0 for cookie, as that is reserved as error
+ return value from log_and_order().
+ */
+#define BINLOG_COOKIE_ERROR_RETURN 0
+#define BINLOG_COOKIE_DUMMY_ID 1
+#define BINLOG_COOKIE_BASE 2
+#define BINLOG_COOKIE_DUMMY(error_flag) \
+ ( (BINLOG_COOKIE_DUMMY_ID<<1) | ((error_flag)&1) )
+#define BINLOG_COOKIE_MAKE(id, error_flag) \
+ ( (((id)+BINLOG_COOKIE_BASE)<<1) | ((error_flag)&1) )
+#define BINLOG_COOKIE_GET_ERROR_FLAG(c) ((c) & 1)
+#define BINLOG_COOKIE_GET_ID(c) ( ((ulong)(c)>>1) - BINLOG_COOKIE_BASE )
+#define BINLOG_COOKIE_IS_DUMMY(c) \
+ ( ((ulong)(c)>>1) == BINLOG_COOKIE_DUMMY_ID )
+
+void binlog_checkpoint_callback(void *cookie);
+
class binlog_cache_mngr;
class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
{
@@ -392,12 +435,57 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
IO_CACHE *error_cache;
/* This is the `all' parameter for ha_commit_ordered(). */
bool all;
+ /*
+ True if we need to increment xid_count in trx_group_commit_leader() and
+ decrement in unlog() (this is needed if there is a participating engine
+ that does not implement the commit_checkpoint_request() handlerton
+ method).
+ */
+ bool need_unlog;
+ /*
+ Fields used to pass the necessary information to the last thread in a
+ group commit, only used when opt_optimize_thread_scheduling is not set.
+ */
+ bool check_purge;
+ ulong binlog_id;
};
+ /*
+ A list of struct xid_count_per_binlog is used to keep track of how many
+ XIDs are in prepared, but not committed, state in each binlog. And how
+ many commit_checkpoint_request()'s are pending.
+
+ When count drops to zero in a binlog after rotation, it means that there
+ are no more XIDs in prepared state, so that binlog is no longer needed
+ for XA crash recovery, and we can log a new binlog checkpoint event.
+
+ The list is protected against simultaneous access from multiple
+ threads by LOCK_xid_list.
+ */
+ struct xid_count_per_binlog : public ilink {
+ char *binlog_name;
+ uint binlog_name_len;
+ ulong binlog_id;
+ /* Total prepared XIDs and pending checkpoint requests in this binlog. */
+ long xid_count;
+ xid_count_per_binlog(); /* Give link error if constructor used. */
+ };
+ I_List<xid_count_per_binlog> binlog_xid_count_list;
+ /*
+ When this is set, a RESET MASTER is in progress.
+
+ Then we should not write any binlog checkpoints into the binlog (that
+ could result in deadlock on LOCK_log, and we will delete all binlog files
+ anyway). Instead we should signal COND_xid_list whenever a new binlog
+ checkpoint arrives - when all have arrived, RESET MASTER will complete.
+ */
+ bool reset_master_pending;
+ friend void binlog_checkpoint_callback(void *cookie);
+
/* LOCK_log and LOCK_index are inited by init_pthread_objects() */
mysql_mutex_t LOCK_index;
- mysql_mutex_t LOCK_prep_xids;
- mysql_cond_t COND_prep_xids;
+ mysql_mutex_t LOCK_xid_list;
+ mysql_cond_t COND_xid_list;
mysql_cond_t update_cond;
ulonglong bytes_written;
IO_CACHE index_file;
@@ -414,27 +502,14 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
The max size before rotation (usable only if log_type == LOG_BIN: binary
logs and relay logs).
For a binlog, max_size should be max_binlog_size.
- For a relay log, it should be max_relay_log_size if this is non-zero,
- max_binlog_size otherwise.
max_size is set in init(), and dynamically changed (when one does SET
- GLOBAL MAX_BINLOG_SIZE|MAX_RELAY_LOG_SIZE) by fix_max_binlog_size and
- fix_max_relay_log_size).
+ GLOBAL MAX_BINLOG_SIZE|MAX_RELAY_LOG_SIZE) from sys_vars.cc
*/
ulong max_size;
- long prepared_xids; /* for tc log - number of xids to remember */
// current file sequence number for load data infile binary logging
uint file_id;
uint open_count; // For replication
int readers_count;
- bool need_start_event;
- /*
- no_auto_events means we don't want any of these automatic events :
- Start/Rotate/Stop. That is, in 4.x when we rotate a relay log, we don't
- want a Rotate_log event to be written to the relay log. When we start a
- relay log etc. So in 4.x this is 1 for relay logs, 0 for binlogs.
- In 5.0 it's 0 for relay logs too!
- */
- bool no_auto_events;
/* Queue of transactions queued up to participate in group commit. */
group_commit_entry *group_commit_queue;
/*
@@ -470,15 +545,17 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
*/
int new_file_without_locking();
int new_file_impl(bool need_lock);
+ void do_checkpoint_request(ulong binlog_id);
+ void purge();
int write_transaction_or_stmt(group_commit_entry *entry);
bool write_transaction_to_binlog_events(group_commit_entry *entry);
void trx_group_commit_leader(group_commit_entry *leader);
- void mark_xid_done();
- void mark_xids_active(uint xid_count);
+ void mark_xid_done(ulong cookie, bool write_checkpoint);
+ void mark_xids_active(ulong cookie, uint xid_count);
public:
- MYSQL_LOG::generate_name;
- MYSQL_LOG::is_open;
+ using MYSQL_LOG::generate_name;
+ using MYSQL_LOG::is_open;
/* This is relay log */
bool is_relay_log;
@@ -534,6 +611,7 @@ public:
*/
char last_commit_pos_file[FN_REFLEN];
my_off_t last_commit_pos_offset;
+ ulong current_binlog_id;
MYSQL_BIN_LOG(uint *sync_period);
/*
@@ -562,7 +640,9 @@ public:
int log_and_order(THD *thd, my_xid xid, bool all,
bool need_prepare_ordered, bool need_commit_ordered);
int unlog(ulong cookie, my_xid xid);
- int recover(IO_CACHE *log, Format_description_log_event *fdle);
+ 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);
#if !defined(MYSQL_CLIENT)
int flush_and_set_pending_rows_event(THD *thd, Rows_log_event* event,
@@ -590,15 +670,14 @@ public:
void signal_update();
void wait_for_update_relay_log(THD* thd);
int wait_for_update_bin_log(THD* thd, const struct timespec * timeout);
- void set_need_start_event() { need_start_event = 1; }
- void init(bool no_auto_events_arg, ulong max_size);
+ void init(ulong max_size);
void init_pthread_objects();
void cleanup();
bool open(const char *log_name,
enum_log_type log_type,
const char *new_name,
enum cache_type io_cache_type_arg,
- bool no_auto_events_arg, ulong max_size,
+ ulong max_size,
bool null_created,
bool need_mutex);
bool open_index_file(const char *index_file_name_arg,
@@ -614,6 +693,7 @@ public:
bool write_incident_already_locked(THD *thd);
bool write_incident(THD *thd);
+ void write_binlog_checkpoint_event_already_locked(const char *name, uint len);
int write_cache(THD *thd, IO_CACHE *cache);
void set_write_error(THD *thd, bool is_transactional);
bool check_write_error(THD *thd);
@@ -631,9 +711,10 @@ public:
void make_log_name(char* buf, const char* log_ident);
bool is_active(const char* log_file_name);
+ bool can_purge_log(const char *log_file_name);
int update_log_index(LOG_INFO* linfo, bool need_update_threads);
int rotate(bool force_rotate, bool* check_purge);
- void purge();
+ void checkpoint_and_purge(ulong binlog_id);
int rotate_and_purge(bool force_rotate);
/**
Flush binlog cache and synchronize to disk.
@@ -664,7 +745,7 @@ public:
int register_create_index_entry(const char* entry);
int purge_index_entry(THD *thd, ulonglong *decrease_log_space,
bool need_mutex);
- bool reset_logs(THD* thd);
+ bool reset_logs(THD* thd, bool create_new_log);
void close(uint exiting);
void clear_inuse_flag_when_closing(File file);
@@ -863,7 +944,7 @@ int query_error_code(THD *thd, bool not_killed);
uint purge_log_get_error_code(int res);
int vprint_msg_to_log(enum loglevel level, const char *format, va_list args);
-void sql_print_error(const char *format, ...) ATTRIBUTE_FORMAT(printf, 1, 2);
+void sql_print_error(const char *format, ...);
void sql_print_warning(const char *format, ...) ATTRIBUTE_FORMAT(printf, 1, 2);
void sql_print_information(const char *format, ...)
ATTRIBUTE_FORMAT(printf, 1, 2);
diff --git a/sql/log_event.cc b/sql/log_event.cc
index f884b2996f0..13de7523054 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -46,6 +46,7 @@
#include "rpl_record.h"
#include "transaction.h"
#include <my_dir.h>
+#include "sql_show.h"
#endif /* MYSQL_CLIENT */
@@ -471,29 +472,28 @@ inline bool unexpected_error_code(int unexpected_error)
pretty_print_str()
*/
-static char *pretty_print_str(char *packet, const char *str, int len)
+static void
+pretty_print_str(String *packet, const char *str, int len)
{
const char *end= str + len;
- char *pos= packet;
- *pos++= '\'';
+ packet->append(STRING_WITH_LEN("'"));
while (str < end)
{
char c;
switch ((c=*str++)) {
- case '\n': *pos++= '\\'; *pos++= 'n'; break;
- case '\r': *pos++= '\\'; *pos++= 'r'; break;
- case '\\': *pos++= '\\'; *pos++= '\\'; break;
- case '\b': *pos++= '\\'; *pos++= 'b'; break;
- case '\t': *pos++= '\\'; *pos++= 't'; break;
- case '\'': *pos++= '\\'; *pos++= '\''; break;
- case 0 : *pos++= '\\'; *pos++= '0'; break;
+ case '\n': packet->append(STRING_WITH_LEN("\\n")); break;
+ case '\r': packet->append(STRING_WITH_LEN("\\r")); break;
+ case '\\': packet->append(STRING_WITH_LEN("\\\\")); break;
+ case '\b': packet->append(STRING_WITH_LEN("\\b")); break;
+ case '\t': packet->append(STRING_WITH_LEN("\\t")); break;
+ case '\'': packet->append(STRING_WITH_LEN("\\'")); break;
+ case 0 : packet->append(STRING_WITH_LEN("\\0")); break;
default:
- *pos++= c;
+ packet->append(&c, 1);
break;
}
}
- *pos++= '\'';
- return pos;
+ packet->append(STRING_WITH_LEN("'"));
}
#endif /* !MYSQL_CLIENT */
@@ -731,6 +731,7 @@ const char* Log_event::get_type_str(Log_event_type type)
case EXECUTE_LOAD_QUERY_EVENT: return "Execute_load_query";
case INCIDENT_EVENT: return "Incident";
case ANNOTATE_ROWS_EVENT: return "Annotate_rows";
+ case BINLOG_CHECKPOINT_EVENT: return "Binlog_checkpoint";
default: return "Unknown"; /* impossible */
}
}
@@ -905,9 +906,9 @@ int Log_event::do_update_pos(Relay_log_info *rli)
Log_event::enum_skip_reason
Log_event::do_shall_skip(Relay_log_info *rli)
{
- DBUG_PRINT("info", ("ev->server_id=%lu, ::server_id=%lu,"
- " rli->replicate_same_server_id=%d,"
- " rli->slave_skip_counter=%d",
+ DBUG_PRINT("info", ("ev->server_id: %lu, ::server_id: %lu,"
+ " rli->replicate_same_server_id: %d,"
+ " rli->slave_skip_counter: %lu",
(ulong) server_id, (ulong) ::server_id,
rli->replicate_same_server_id,
rli->slave_skip_counter));
@@ -926,7 +927,7 @@ Log_event::do_shall_skip(Relay_log_info *rli)
Log_event::pack_info()
*/
-void Log_event::pack_info(Protocol *protocol)
+void Log_event::pack_info(THD *thd, Protocol *protocol)
{
protocol->store("", &my_charset_bin);
}
@@ -935,7 +936,8 @@ void Log_event::pack_info(Protocol *protocol)
/**
Only called by SHOW BINLOG EVENTS
*/
-int Log_event::net_send(Protocol *protocol, const char* log_name, my_off_t pos)
+int Log_event::net_send(THD *thd, Protocol *protocol, const char* log_name,
+ my_off_t pos)
{
const char *p= strrchr(log_name, FN_LIBCHAR);
const char *event_type;
@@ -949,7 +951,7 @@ int Log_event::net_send(Protocol *protocol, const char* log_name, my_off_t pos)
protocol->store(event_type, strlen(event_type), &my_charset_bin);
protocol->store((uint32) server_id);
protocol->store((ulonglong) log_pos);
- pack_info(protocol);
+ pack_info(thd, protocol);
return protocol->write();
}
#endif /* HAVE_REPLICATION */
@@ -1085,6 +1087,9 @@ bool Log_event::write_header(IO_CACHE* file, ulong event_data_length)
ulong now;
bool ret;
DBUG_ENTER("Log_event::write_header");
+ DBUG_PRINT("enter", ("filepos: %lld length: %lu type: %d",
+ (longlong) my_b_tell(file), event_data_length,
+ (int) get_type_code()));
/* Store number of bytes that will be written by this event */
data_written= event_data_length + sizeof(header);
@@ -1348,7 +1353,7 @@ failed my_b_read"));
Log_event *res= 0;
#ifndef max_allowed_packet
THD *thd=current_thd;
- uint max_allowed_packet= thd ? thd->variables.max_allowed_packet : ~(ulong)0;
+ uint max_allowed_packet= thd ? slave_max_allowed_packet:~(ulong)0;
#endif
if (data_len > max_allowed_packet)
@@ -1386,7 +1391,7 @@ err:
DBUG_ASSERT(error != 0);
sql_print_error("Error in Log_event::read_log_event(): "
"'%s', data_len: %d, event_type: %d",
- error,data_len,head[EVENT_TYPE_OFFSET]);
+ error,data_len,(uchar)(head[EVENT_TYPE_OFFSET]));
my_free(buf);
/*
The SQL slave thread will check if file->error<0 to know
@@ -1535,6 +1540,9 @@ Log_event* Log_event::read_log_event(const char* buf, uint event_len,
case ROTATE_EVENT:
ev = new Rotate_log_event(buf, event_len, description_event);
break;
+ case BINLOG_CHECKPOINT_EVENT:
+ ev = new Binlog_checkpoint_log_event(buf, event_len, description_event);
+ break;
#ifdef HAVE_REPLICATION
case SLAVE_EVENT: /* can never happen (unused event) */
ev = new Slave_log_event(buf, event_len, description_event);
@@ -2448,27 +2456,22 @@ Log_event::continue_group(Relay_log_info *rli)
show the catalog ??
*/
-void Query_log_event::pack_info(Protocol *protocol)
+void Query_log_event::pack_info(THD *thd, Protocol *protocol)
{
// TODO: show the catalog ??
- char *buf, *pos;
- if (!(buf= (char*) my_malloc(9 + db_len + q_len, MYF(MY_WME))))
- return;
- pos= buf;
+ char buf_mem[1024];
+ String buf(buf_mem, sizeof(buf_mem), system_charset_info);
+ buf.real_alloc(9 + db_len + q_len);
if (!(flags & LOG_EVENT_SUPPRESS_USE_F)
&& db && db_len)
{
- pos= strmov(buf, "use `");
- memcpy(pos, db, db_len);
- pos= strmov(pos+db_len, "`; ");
+ buf.append(STRING_WITH_LEN("use "));
+ append_identifier(thd, &buf, db, db_len);
+ buf.append("; ");
}
if (query && q_len)
- {
- memcpy(pos, query, q_len);
- pos+= q_len;
- }
- protocol->store(buf, pos-buf, &my_charset_bin);
- my_free(buf);
+ buf.append(query, q_len);
+ protocol->store(&buf);
}
#endif
@@ -3218,24 +3221,41 @@ Query_log_event::Query_log_event(const char* buf, uint event_len,
pos= (const uchar*) end; // Break loop
}
}
-
+
+ /**
+ Layout for the data buffer is as follows
+ +--------+-----------+------+------+---------+----+-------+
+ | catlog | time_zone | user | host | db name | \0 | Query |
+ +--------+-----------+------+------+---------+----+-------+
+
+ To support the query cache we append the following buffer to the above
+ +-------+----------------------------------------+-------+
+ |db len | uninitiatlized space of size of db len | FLAGS |
+ +-------+----------------------------------------+-------+
+
+ The area of buffer starting from Query field all the way to the end belongs
+ to the Query buffer and its structure is described in alloc_query() in
+ sql_parse.cc
+ */
+
#if !defined(MYSQL_CLIENT) && defined(HAVE_QUERY_CACHE)
- if (!(start= data_buf = (Log_event::Byte*) my_malloc(catalog_len + 1 +
- time_zone_len + 1 +
- data_len + 1 +
- QUERY_CACHE_DB_LENGTH_SIZE +
- QUERY_CACHE_FLAGS_SIZE +
- user.length + 1 +
- host.length + 1 +
- db_len + 1,
- MYF(MY_WME))))
+ if (!(start= data_buf = (Log_event::Byte*) my_malloc(catalog_len + 1
+ + time_zone_len + 1
+ + user.length + 1
+ + host.length + 1
+ + data_len + 1
+ + sizeof(size_t)//for db_len
+ + db_len + 1
+ + QUERY_CACHE_DB_LENGTH_SIZE
+ + QUERY_CACHE_FLAGS_SIZE,
+ MYF(MY_WME))))
#else
- if (!(start= data_buf = (Log_event::Byte*) my_malloc(catalog_len + 1 +
- time_zone_len + 1 +
- data_len + 1 +
- user.length + 1 +
- host.length + 1,
- MYF(MY_WME))))
+ if (!(start= data_buf = (Log_event::Byte*) my_malloc(catalog_len + 1
+ + time_zone_len + 1
+ + user.length + 1
+ + host.length + 1
+ + data_len + 1,
+ MYF(MY_WME))))
#endif
DBUG_VOID_RETURN;
if (catalog_len) // If catalog is given
@@ -3275,10 +3295,127 @@ Query_log_event::Query_log_event(const char* buf, uint event_len,
db= (char *)start;
query= (char *)(start + db_len + 1);
q_len= data_len - db_len -1;
+ /**
+ Append the db length at the end of the buffer. This will be used by
+ Query_cache::send_result_to_client() in case the query cache is On.
+ */
+#if !defined(MYSQL_CLIENT) && defined(HAVE_QUERY_CACHE)
+ size_t db_length= (size_t)db_len;
+ memcpy(start + data_len + 1, &db_length, sizeof(size_t));
+#endif
DBUG_VOID_RETURN;
}
+/*
+ Replace a binlog event read into a packet with a dummy event. Either a
+ Query_log_event that has just a comment, or if that will not fit in the
+ space used for the event to be replaced, then a NULL user_var event.
+
+ This is used when sending binlog data to a slave which does not understand
+ this particular event and which is too old to support informational events
+ or holes in the event stream.
+
+ This allows to write such events into the binlog on the master and still be
+ able to replicate against old slaves without them breaking.
+
+ Clears the flag LOG_EVENT_THREAD_SPECIFIC_F and set LOG_EVENT_SUPPRESS_USE_F.
+ Overwrites the type with QUERY_EVENT (or USER_VAR_EVENT), and replaces the
+ body with a minimal query / NULL user var.
+
+ Returns zero on success, -1 if error due to too little space in original
+ event. A minimum of 25 bytes (19 bytes fixed header + 6 bytes in the body)
+ is needed in any event to be replaced with a dummy event.
+*/
+int
+Query_log_event::dummy_event(String *packet, ulong ev_offset,
+ uint8 checksum_alg)
+{
+ uchar *p= (uchar *)packet->ptr() + ev_offset;
+ size_t data_len= packet->length() - ev_offset;
+ uint16 flags;
+ static const size_t min_user_var_event_len=
+ LOG_EVENT_HEADER_LEN + UV_NAME_LEN_SIZE + 1 + UV_VAL_IS_NULL; // 25
+ static const size_t min_query_event_len=
+ LOG_EVENT_HEADER_LEN + QUERY_HEADER_LEN + 1 + 1; // 34
+
+ if (checksum_alg == BINLOG_CHECKSUM_ALG_CRC32)
+ data_len-= BINLOG_CHECKSUM_LEN;
+ else
+ DBUG_ASSERT(checksum_alg == BINLOG_CHECKSUM_ALG_UNDEF ||
+ checksum_alg == BINLOG_CHECKSUM_ALG_OFF);
+
+ if (data_len < min_user_var_event_len)
+ /* Cannot replace with dummy, event too short. */
+ return -1;
+
+ flags= uint2korr(p + FLAGS_OFFSET);
+ flags&= ~LOG_EVENT_THREAD_SPECIFIC_F;
+ flags|= LOG_EVENT_SUPPRESS_USE_F;
+ int2store(p + FLAGS_OFFSET, flags);
+
+ if (data_len < min_query_event_len)
+ {
+ /*
+ Have to use dummy user_var event for such a short packet.
+
+ This works, but the event will be considered part of an event group with
+ the following event. So for example @@global.sql_slave_skip_counter=1
+ will skip not only the dummy event, but also the immediately following
+ event.
+
+ We write a NULL user var with the name @`!dummyvar` (or as much
+ as that as will fit within the size of the original event - so
+ possibly just @`!`).
+ */
+ static const char var_name[]= "!dummyvar";
+ uint name_len= data_len - (min_user_var_event_len - 1);
+
+ p[EVENT_TYPE_OFFSET]= USER_VAR_EVENT;
+ int4store(p + LOG_EVENT_HEADER_LEN, name_len);
+ memcpy(p + LOG_EVENT_HEADER_LEN + UV_NAME_LEN_SIZE, var_name, name_len);
+ p[LOG_EVENT_HEADER_LEN + UV_NAME_LEN_SIZE + name_len]= 1; // indicates NULL
+ }
+ else
+ {
+ /*
+ Use a dummy query event, just a comment.
+ */
+ static const char message[]=
+ "# Dummy event replacing event type %u that slave cannot handle.";
+ char buf[sizeof(message)+1]; /* +1, as %u can expand to 3 digits. */
+ uchar old_type= p[EVENT_TYPE_OFFSET];
+ uchar *q= p + LOG_EVENT_HEADER_LEN;
+ size_t comment_len, len;
+
+ p[EVENT_TYPE_OFFSET]= QUERY_EVENT;
+ int4store(q + Q_THREAD_ID_OFFSET, 0);
+ int4store(q + Q_EXEC_TIME_OFFSET, 0);
+ q[Q_DB_LEN_OFFSET]= 0;
+ int2store(q + Q_ERR_CODE_OFFSET, 0);
+ int2store(q + Q_STATUS_VARS_LEN_OFFSET, 0);
+ q[Q_DATA_OFFSET]= 0; /* Zero terminator for empty db */
+ q+= Q_DATA_OFFSET + 1;
+ len= my_snprintf(buf, sizeof(buf), message, old_type);
+ comment_len= data_len - (min_query_event_len - 1);
+ if (comment_len <= len)
+ memcpy(q, buf, comment_len);
+ else
+ {
+ memcpy(q, buf, len);
+ memset(q+len, ' ', comment_len - len);
+ }
+ }
+
+ if (checksum_alg == BINLOG_CHECKSUM_ALG_CRC32)
+ {
+ ha_checksum crc= my_checksum(0L, p, data_len);
+ int4store(p + data_len, crc);
+ }
+ return 0;
+}
+
+
#ifdef MYSQL_CLIENT
/**
Query_log_event::print().
@@ -3309,11 +3446,17 @@ void Query_log_event::print_query_header(IO_CACHE* file,
}
else if (db)
{
+ /* Room for expand ` to `` + initial/final ` + \0 */
+ char buf[FN_REFLEN*2+3];
+
different_db= memcmp(print_event_info->db, db, db_len + 1);
if (different_db)
memcpy(print_event_info->db, db, db_len + 1);
if (db[0] && different_db)
- my_b_printf(file, "use %s%s\n", db, print_event_info->delimiter);
+ {
+ my_snprintf(buf, sizeof(buf), "%`s", db);
+ my_b_printf(file, "use %s%s\n", buf, print_event_info->delimiter);
+ }
}
end=int10_to_str((long) when, strmov(buff,"SET TIMESTAMP="),10);
@@ -3461,6 +3604,12 @@ void Query_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
{
Write_on_release_cache cache(&print_event_info->head_cache, file);
+ /**
+ reduce the size of io cache so that the write function is called
+ for every call to my_b_write().
+ */
+ DBUG_EXECUTE_IF ("simulate_file_write_error",
+ {(&cache)->write_pos= (&cache)->write_end- 500;});
print_query_header(&cache, print_event_info);
my_b_write(&cache, (uchar*) query, q_len);
my_b_printf(&cache, "\n%s\n", print_event_info->delimiter);
@@ -3479,6 +3628,34 @@ int Query_log_event::do_apply_event(Relay_log_info const *rli)
return do_apply_event(rli, query, q_len);
}
+/**
+ Compare if two errors should be regarded as equal.
+ This is to handle the case when you can get slightly different errors
+ on master and slave for the same thing.
+ @param
+ expected_error Error we got on master
+ actual_error Error we got on slave
+
+ @return
+ 1 Errors are equal
+ 0 Errors are different
+*/
+
+bool test_if_equal_repl_errors(int expected_error, int actual_error)
+{
+ if (expected_error == actual_error)
+ return 1;
+ switch (expected_error) {
+ case ER_DUP_ENTRY:
+ case ER_AUTOINC_READ_FAILED:
+ return (actual_error == ER_AUTOINC_READ_FAILED ||
+ actual_error == HA_ERR_AUTOINC_ERANGE);
+ default:
+ break;
+ }
+ return 0;
+}
+
/**
@todo
@@ -3503,6 +3680,7 @@ int Query_log_event::do_apply_event(Relay_log_info const *rli,
LEX_STRING new_db;
int expected_error,actual_error= 0;
HA_CREATE_INFO db_options;
+ DBUG_ENTER("Query_log_event::do_apply_event");
/*
Colleagues: please never free(thd->catalog) in MySQL. This would
@@ -3790,7 +3968,8 @@ compare_errors:
DBUG_PRINT("info",("expected_error: %d sql_errno: %d",
expected_error, actual_error));
- if ((expected_error && expected_error != actual_error &&
+ if ((expected_error &&
+ !test_if_equal_repl_errors(expected_error, actual_error) &&
!concurrency_error_code(expected_error)) &&
!ignored_error_code(actual_error) &&
!ignored_error_code(expected_error))
@@ -3812,7 +3991,7 @@ Default database: '%s'. Query: '%s'",
If we get the same error code as expected and it is not a concurrency
issue, or should be ignored.
*/
- else if ((expected_error == actual_error &&
+ else if ((test_if_equal_repl_errors(expected_error, actual_error) &&
!concurrency_error_code(expected_error)) ||
ignored_error_code(actual_error))
{
@@ -3898,7 +4077,7 @@ end:
thd->first_successful_insert_id_in_prev_stmt= 0;
thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt= 0;
free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
- return thd->is_slave_error;
+ DBUG_RETURN(thd->is_slave_error);
}
int Query_log_event::do_update_pos(Relay_log_info *rli)
@@ -3971,7 +4150,7 @@ Start_log_event_v3::Start_log_event_v3()
*/
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-void Start_log_event_v3::pack_info(Protocol *protocol)
+void Start_log_event_v3::pack_info(THD *thd, Protocol *protocol)
{
char buf[12 + ST_SERVER_VER_LEN + 14 + 22], *pos;
pos= strmov(buf, "Server ver: ");
@@ -4262,6 +4441,8 @@ Format_description_log_event(uint8 binlog_ver, const char* server_ver)
// Set header lengths of Maria events
post_header_len[ANNOTATE_ROWS_EVENT-1]= ANNOTATE_ROWS_HEADER_LEN;
+ post_header_len[BINLOG_CHECKPOINT_EVENT-1]=
+ BINLOG_CHECKPOINT_HEADER_LEN;
// Sanity-check that all post header lengths are initialized.
int i;
@@ -4747,131 +4928,113 @@ uint8 get_checksum_alg(const char* buf, ulong len)
*/
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-uint Load_log_event::get_query_buffer_length()
+void Load_log_event::print_query(THD *thd, bool need_db, const char *cs,
+ String *buf, my_off_t *fn_start,
+ my_off_t *fn_end, const char *qualify_db)
{
- return
- 5 + db_len + 3 + // "use DB; "
- 18 + fname_len + 2 + // "LOAD DATA INFILE 'file''"
- 11 + // "CONCURRENT "
- 7 + // LOCAL
- 9 + // " REPLACE or IGNORE "
- 13 + table_name_len*2 + // "INTO TABLE `table`"
- 21 + sql_ex.field_term_len*4 + 2 + // " FIELDS TERMINATED BY 'str'"
- 23 + sql_ex.enclosed_len*4 + 2 + // " OPTIONALLY ENCLOSED BY 'str'"
- 12 + sql_ex.escaped_len*4 + 2 + // " ESCAPED BY 'str'"
- 21 + sql_ex.line_term_len*4 + 2 + // " LINES TERMINATED BY 'str'"
- 19 + sql_ex.line_start_len*4 + 2 + // " LINES STARTING BY 'str'"
- 15 + 22 + // " IGNORE xxx LINES"
- 3 + (num_fields-1)*2 + field_block_len; // " (field1, field2, ...)"
-}
-
-
-void Load_log_event::print_query(bool need_db, const char *cs, char *buf,
- char **end, char **fn_start, char **fn_end)
-{
- char *pos= buf;
-
if (need_db && db && db_len)
{
- pos= strmov(pos, "use `");
- memcpy(pos, db, db_len);
- pos= strmov(pos+db_len, "`; ");
+ buf->append(STRING_WITH_LEN("use "));
+ append_identifier(thd, buf, db, db_len);
+ buf->append(STRING_WITH_LEN("; "));
}
- pos= strmov(pos, "LOAD DATA ");
+ buf->append(STRING_WITH_LEN("LOAD DATA "));
if (is_concurrent)
- pos= strmov(pos, "CONCURRENT ");
+ buf->append(STRING_WITH_LEN("CONCURRENT "));
if (fn_start)
- *fn_start= pos;
+ *fn_start= buf->length();
if (check_fname_outside_temp_buf())
- pos= strmov(pos, "LOCAL ");
- pos= strmov(pos, "INFILE '");
- memcpy(pos, fname, fname_len);
- pos= strmov(pos+fname_len, "' ");
+ buf->append(STRING_WITH_LEN("LOCAL "));
+ buf->append(STRING_WITH_LEN("INFILE '"));
+ buf->append_for_single_quote(fname, fname_len);
+ buf->append(STRING_WITH_LEN("' "));
if (sql_ex.opt_flags & REPLACE_FLAG)
- pos= strmov(pos, "REPLACE ");
+ buf->append(STRING_WITH_LEN("REPLACE "));
else if (sql_ex.opt_flags & IGNORE_FLAG)
- pos= strmov(pos, "IGNORE ");
+ buf->append(STRING_WITH_LEN("IGNORE "));
- pos= strmov(pos ,"INTO");
+ buf->append(STRING_WITH_LEN("INTO"));
if (fn_end)
- *fn_end= pos;
+ *fn_end= buf->length();
- pos= strmov(pos ," TABLE `");
- memcpy(pos, table_name, table_name_len);
- pos+= table_name_len;
+ buf->append(STRING_WITH_LEN(" TABLE "));
+ if (qualify_db)
+ {
+ append_identifier(thd, buf, qualify_db, strlen(qualify_db));
+ buf->append(STRING_WITH_LEN("."));
+ }
+ append_identifier(thd, buf, table_name, table_name_len);
if (cs != NULL)
{
- pos= strmov(pos ,"` CHARACTER SET ");
- pos= strmov(pos , cs);
+ buf->append(STRING_WITH_LEN(" CHARACTER SET "));
+ buf->append(cs, strlen(cs));
}
- else
- pos= strmov(pos, "`");
/* We have to create all optional fields as the default is not empty */
- pos= strmov(pos, " FIELDS TERMINATED BY ");
- pos= pretty_print_str(pos, sql_ex.field_term, sql_ex.field_term_len);
+ buf->append(STRING_WITH_LEN(" FIELDS TERMINATED BY "));
+ pretty_print_str(buf, sql_ex.field_term, sql_ex.field_term_len);
if (sql_ex.opt_flags & OPT_ENCLOSED_FLAG)
- pos= strmov(pos, " OPTIONALLY ");
- pos= strmov(pos, " ENCLOSED BY ");
- pos= pretty_print_str(pos, sql_ex.enclosed, sql_ex.enclosed_len);
+ buf->append(STRING_WITH_LEN(" OPTIONALLY "));
+ buf->append(STRING_WITH_LEN(" ENCLOSED BY "));
+ pretty_print_str(buf, sql_ex.enclosed, sql_ex.enclosed_len);
- pos= strmov(pos, " ESCAPED BY ");
- pos= pretty_print_str(pos, sql_ex.escaped, sql_ex.escaped_len);
+ buf->append(STRING_WITH_LEN(" ESCAPED BY "));
+ pretty_print_str(buf, sql_ex.escaped, sql_ex.escaped_len);
- pos= strmov(pos, " LINES TERMINATED BY ");
- pos= pretty_print_str(pos, sql_ex.line_term, sql_ex.line_term_len);
+ buf->append(STRING_WITH_LEN(" LINES TERMINATED BY "));
+ pretty_print_str(buf, sql_ex.line_term, sql_ex.line_term_len);
if (sql_ex.line_start_len)
{
- pos= strmov(pos, " STARTING BY ");
- pos= pretty_print_str(pos, sql_ex.line_start, sql_ex.line_start_len);
+ buf->append(STRING_WITH_LEN(" STARTING BY "));
+ pretty_print_str(buf, sql_ex.line_start, sql_ex.line_start_len);
}
if ((long) skip_lines > 0)
{
- pos= strmov(pos, " IGNORE ");
- pos= longlong10_to_str((longlong) skip_lines, pos, 10);
- pos= strmov(pos," LINES ");
+ buf->append(STRING_WITH_LEN(" IGNORE "));
+ buf->append_ulonglong(skip_lines);
+ buf->append(STRING_WITH_LEN(" LINES "));
}
if (num_fields)
{
uint i;
const char *field= fields;
- pos= strmov(pos, " (");
+ buf->append(STRING_WITH_LEN(" ("));
for (i = 0; i < num_fields; i++)
{
if (i)
{
- *pos++= ' ';
- *pos++= ',';
+ /*
+ Yes, the space and comma is reversed here. But this is mostly dead
+ code, at most used when reading really old binlogs from old servers,
+ so better just leave it as is...
+ */
+ buf->append(STRING_WITH_LEN(" ,"));
}
- memcpy(pos, field, field_lens[i]);
- pos+= field_lens[i];
+ append_identifier(thd, buf, field, field_lens[i]);
field+= field_lens[i] + 1;
}
- *pos++= ')';
+ buf->append(STRING_WITH_LEN(")"));
}
-
- *end= pos;
}
-void Load_log_event::pack_info(Protocol *protocol)
+void Load_log_event::pack_info(THD *thd, Protocol *protocol)
{
- char *buf, *end;
+ char query_buffer[1024];
+ String query_str(query_buffer, sizeof(query_buffer), system_charset_info);
- if (!(buf= (char*) my_malloc(get_query_buffer_length(), MYF(MY_WME))))
- return;
- print_query(TRUE, NULL, buf, &end, 0, 0);
- protocol->store(buf, end-buf, &my_charset_bin);
- my_free(buf);
+ query_str.length(0);
+ print_query(thd, TRUE, NULL, &query_str, 0, 0, NULL);
+ protocol->store(query_str.ptr(), query_str.length(), &my_charset_bin);
}
#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
@@ -5253,6 +5416,8 @@ int Load_log_event::do_apply_event(NET* net, Relay_log_info const *rli,
bool use_rli_only_for_errors)
{
LEX_STRING new_db;
+ DBUG_ENTER("Load_log_event::do_apply_event");
+
new_db.length= db_len;
new_db.str= (char *) rpl_filter->get_rewrite_db(db, &new_db.length);
thd->set_db(new_db.str, new_db.length);
@@ -5327,16 +5492,20 @@ int Load_log_event::do_apply_event(NET* net, Relay_log_info const *rli,
else
{
char llbuff[22];
- char *end;
enum enum_duplicates handle_dup;
bool ignore= 0;
+ char query_buffer[1024];
+ String query_str(query_buffer, sizeof(query_buffer), system_charset_info);
char *load_data_query;
+ query_str.length(0);
/*
Forge LOAD DATA INFILE query which will be used in SHOW PROCESS LIST
and written to slave's binlog if binlogging is on.
*/
- if (!(load_data_query= (char *)thd->alloc(get_query_buffer_length() + 1)))
+ print_query(thd, FALSE, NULL, &query_str, NULL, NULL, NULL);
+ if (!(load_data_query= (char *)thd->strmake(query_str.ptr(),
+ query_str.length())))
{
/*
This will set thd->fatal_error in case of OOM. So we surely will notice
@@ -5345,9 +5514,7 @@ int Load_log_event::do_apply_event(NET* net, Relay_log_info const *rli,
goto error;
}
- print_query(FALSE, NULL, load_data_query, &end, NULL, NULL);
- *end= 0;
- thd->set_query(load_data_query, (uint) (end - load_data_query));
+ thd->set_query(load_data_query, (uint) (query_str.length()));
if (sql_ex.opt_flags & REPLACE_FLAG)
handle_dup= DUP_REPLACE;
@@ -5493,7 +5660,7 @@ error:
Error '%s' running LOAD DATA INFILE on table '%s'. Default database: '%s'",
err, (char*)table_name, print_slave_db_safe(remember_db));
free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
- return 1;
+ DBUG_RETURN(1);
}
free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
@@ -5508,10 +5675,10 @@ Error '%s' running LOAD DATA INFILE on table '%s'. Default database: '%s'",
rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
ER(ER_SLAVE_FATAL_ERROR), buf);
- return 1;
+ DBUG_RETURN(1);
}
- return ( use_rli_only_for_errors ? 0 : Log_event::do_apply_event(rli) );
+ DBUG_RETURN( use_rli_only_for_errors ? 0 : Log_event::do_apply_event(rli) );
}
#endif
@@ -5525,7 +5692,7 @@ Error '%s' running LOAD DATA INFILE on table '%s'. Default database: '%s'",
*/
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-void Rotate_log_event::pack_info(Protocol *protocol)
+void Rotate_log_event::pack_info(THD *thd, Protocol *protocol)
{
char buf1[256], buf[22];
String tmp(buf1, sizeof(buf1), log_cs);
@@ -5735,6 +5902,86 @@ Rotate_log_event::do_shall_skip(Relay_log_info *rli)
/**************************************************************************
+ Binlog_checkpoint_log_event methods
+**************************************************************************/
+
+#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
+void Binlog_checkpoint_log_event::pack_info(THD *thd, Protocol *protocol)
+{
+ protocol->store(binlog_file_name, binlog_file_len, &my_charset_bin);
+}
+#endif
+
+
+#ifdef MYSQL_CLIENT
+void Binlog_checkpoint_log_event::print(FILE *file,
+ PRINT_EVENT_INFO *print_event_info)
+{
+ Write_on_release_cache cache(&print_event_info->head_cache, file,
+ Write_on_release_cache::FLUSH_F);
+
+ if (print_event_info->short_form)
+ return;
+ print_header(&cache, print_event_info, FALSE);
+ my_b_printf(&cache, "\tBinlog checkpoint ");
+ my_b_write(&cache, (uchar*)binlog_file_name, binlog_file_len);
+ my_b_printf(&cache, "\n");
+}
+#endif /* MYSQL_CLIENT */
+
+
+#ifdef MYSQL_SERVER
+Binlog_checkpoint_log_event::Binlog_checkpoint_log_event(
+ const char *binlog_file_name_arg,
+ uint binlog_file_len_arg)
+ :Log_event(),
+ binlog_file_name(my_strndup(binlog_file_name_arg, binlog_file_len_arg,
+ MYF(MY_WME))),
+ binlog_file_len(binlog_file_len_arg)
+{
+ cache_type= EVENT_NO_CACHE;
+}
+#endif /* MYSQL_SERVER */
+
+
+Binlog_checkpoint_log_event::Binlog_checkpoint_log_event(
+ const char *buf, uint event_len,
+ const Format_description_log_event *description_event)
+ :Log_event(buf, description_event), binlog_file_name(0)
+{
+ uint8 header_size= description_event->common_header_len;
+ uint8 post_header_len=
+ description_event->post_header_len[BINLOG_CHECKPOINT_EVENT-1];
+ if (event_len < header_size + post_header_len ||
+ post_header_len < BINLOG_CHECKPOINT_HEADER_LEN)
+ return;
+ buf+= header_size;
+ /* See uint4korr and int4store below */
+ compile_time_assert(BINLOG_CHECKPOINT_HEADER_LEN == 4);
+ binlog_file_len= uint4korr(buf);
+ if (event_len - (header_size + post_header_len) < binlog_file_len)
+ return;
+ binlog_file_name= my_strndup(buf + post_header_len, binlog_file_len,
+ MYF(MY_WME));
+ return;
+}
+
+
+#ifndef MYSQL_CLIENT
+bool Binlog_checkpoint_log_event::write(IO_CACHE *file)
+{
+ uchar buf[BINLOG_CHECKPOINT_HEADER_LEN];
+ int4store(buf, binlog_file_len);
+ return write_header(file, BINLOG_CHECKPOINT_HEADER_LEN + binlog_file_len) ||
+ wrapper_my_b_safe_write(file, buf, BINLOG_CHECKPOINT_HEADER_LEN) ||
+ wrapper_my_b_safe_write(file, (const uchar *)binlog_file_name,
+ binlog_file_len) ||
+ write_footer(file);
+}
+#endif /* MYSQL_CLIENT */
+
+
+/**************************************************************************
Intvar_log_event methods
**************************************************************************/
@@ -5743,7 +5990,7 @@ Rotate_log_event::do_shall_skip(Relay_log_info *rli)
*/
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-void Intvar_log_event::pack_info(Protocol *protocol)
+void Intvar_log_event::pack_info(THD *thd, Protocol *protocol)
{
char buf[256], *pos;
pos= strmake(buf, get_var_type_name(), sizeof(buf)-23);
@@ -5897,7 +6144,7 @@ Intvar_log_event::do_shall_skip(Relay_log_info *rli)
**************************************************************************/
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-void Rand_log_event::pack_info(Protocol *protocol)
+void Rand_log_event::pack_info(THD *thd, Protocol *protocol)
{
char buf1[256], *pos;
pos= strmov(buf1,"rand_seed1=");
@@ -6022,7 +6269,7 @@ bool slave_execute_deferred_events(THD *thd)
**************************************************************************/
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-void Xid_log_event::pack_info(Protocol *protocol)
+void Xid_log_event::pack_info(THD *thd, Protocol *protocol)
{
char buf[128], *pos;
pos= strmov(buf, "COMMIT /* xid=");
@@ -6119,84 +6366,117 @@ Xid_log_event::do_shall_skip(Relay_log_info *rli)
**************************************************************************/
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-void User_var_log_event::pack_info(Protocol* protocol)
+static bool
+user_var_append_name_part(THD *thd, String *buf,
+ const char *name, size_t name_len)
{
- char *buf= 0;
- uint val_offset= 4 + name_len;
- uint event_len= val_offset;
+ return buf->append("@") ||
+ append_identifier(thd, buf, name, name_len) ||
+ buf->append("=");
+}
+void User_var_log_event::pack_info(THD *thd, Protocol* protocol)
+{
if (is_null)
{
- if (!(buf= (char*) my_malloc(val_offset + 5, MYF(MY_WME))))
+ char buf_mem[FN_REFLEN+7];
+ String buf(buf_mem, sizeof(buf_mem), system_charset_info);
+ buf.length(0);
+ if (user_var_append_name_part(thd, &buf, name, name_len) ||
+ buf.append("NULL"))
return;
- strmov(buf + val_offset, "NULL");
- event_len= val_offset + 4;
+ protocol->store(buf.ptr(), buf.length(), &my_charset_bin);
}
else
{
switch (type) {
case REAL_RESULT:
+ {
double real_val;
+ char buf2[MY_GCVT_MAX_FIELD_WIDTH+1];
+ char buf_mem[FN_REFLEN + MY_GCVT_MAX_FIELD_WIDTH + 1];
+ String buf(buf_mem, sizeof(buf_mem), system_charset_info);
float8get(real_val, val);
- if (!(buf= (char*) my_malloc(val_offset + MY_GCVT_MAX_FIELD_WIDTH + 1,
- MYF(MY_WME))))
+ buf.length(0);
+ if (user_var_append_name_part(thd, &buf, name, name_len) ||
+ buf.append(buf2, my_gcvt(real_val, MY_GCVT_ARG_DOUBLE,
+ MY_GCVT_MAX_FIELD_WIDTH, buf2, NULL)))
return;
- event_len+= my_gcvt(real_val, MY_GCVT_ARG_DOUBLE, MY_GCVT_MAX_FIELD_WIDTH,
- buf + val_offset, NULL);
+ protocol->store(buf.ptr(), buf.length(), &my_charset_bin);
break;
+ }
case INT_RESULT:
- if (!(buf= (char*) my_malloc(val_offset + 22, MYF(MY_WME))))
+ {
+ char buf2[22];
+ char buf_mem[FN_REFLEN + 22];
+ String buf(buf_mem, sizeof(buf_mem), system_charset_info);
+ buf.length(0);
+ if (user_var_append_name_part(thd, &buf, name, name_len) ||
+ buf.append(buf2,
+ longlong10_to_str(uint8korr(val), buf2,
+ ((flags & User_var_log_event::UNSIGNED_F) ? 10 : -10))-buf2))
return;
- event_len= longlong10_to_str(uint8korr(val), buf + val_offset,
- ((flags & User_var_log_event::UNSIGNED_F) ?
- 10 : -10))-buf;
+ protocol->store(buf.ptr(), buf.length(), &my_charset_bin);
break;
+ }
case DECIMAL_RESULT:
{
- if (!(buf= (char*) my_malloc(val_offset + DECIMAL_MAX_STR_LENGTH,
- MYF(MY_WME))))
- return;
- String str(buf+val_offset, DECIMAL_MAX_STR_LENGTH, &my_charset_bin);
+ char buf_mem[FN_REFLEN + DECIMAL_MAX_STR_LENGTH];
+ String buf(buf_mem, sizeof(buf_mem), system_charset_info);
+ char buf2[DECIMAL_MAX_STR_LENGTH+1];
+ String str(buf2, sizeof(buf2), &my_charset_bin);
my_decimal dec;
+ buf.length(0);
binary2my_decimal(E_DEC_FATAL_ERROR, (uchar*) (val+2), &dec, val[0],
val[1]);
my_decimal2string(E_DEC_FATAL_ERROR, &dec, 0, 0, 0, &str);
- event_len= str.length() + val_offset;
+ if (user_var_append_name_part(thd, &buf, name, name_len) ||
+ buf.append(buf2))
+ return;
+ protocol->store(buf.ptr(), buf.length(), &my_charset_bin);
break;
- }
+ }
case STRING_RESULT:
+ {
/* 15 is for 'COLLATE' and other chars */
- buf= (char*) my_malloc(event_len+val_len*2+1+2*MY_CS_NAME_SIZE+15,
- MYF(MY_WME));
+ char buf_mem[FN_REFLEN + 512 + 1 + 2*MY_CS_NAME_SIZE+15];
+ String buf(buf_mem, sizeof(buf_mem), system_charset_info);
CHARSET_INFO *cs;
- if (!buf)
- return;
+ buf.length(0);
if (!(cs= get_charset(charset_number, MYF(0))))
{
- strmov(buf+val_offset, "???");
- event_len+= 3;
+ if (buf.append("???"))
+ return;
}
else
{
- char *p= strxmov(buf + val_offset, "_", cs->csname, " ", NullS);
- p= str_to_hex(p, val, val_len);
- p= strxmov(p, " COLLATE ", cs->name, NullS);
- event_len= p-buf;
+ size_t old_len;
+ char *beg, *end;
+ if (user_var_append_name_part(thd, &buf, name, name_len) ||
+ buf.append("_") ||
+ buf.append(cs->csname) ||
+ buf.append(" "))
+ return;
+ old_len= buf.length();
+ if (buf.reserve(old_len + val_len*2 + 2 + sizeof(" COLLATE ") +
+ MY_CS_NAME_SIZE))
+ return;
+ beg= const_cast<char *>(buf.ptr()) + old_len;
+ end= str_to_hex(beg, val, val_len);
+ buf.length(old_len + (end - beg));
+ if (buf.append(" COLLATE ") ||
+ buf.append(cs->name))
+ return;
}
+ protocol->store(buf.ptr(), buf.length(), &my_charset_bin);
break;
+ }
case ROW_RESULT:
default:
DBUG_ASSERT(0);
return;
}
}
- buf[0]= '@';
- buf[1]= '`';
- memcpy(buf+2, name, name_len);
- buf[2+name_len]= '`';
- buf[3+name_len]= '=';
- protocol->store(buf, event_len, &my_charset_bin);
- my_free(buf);
}
#endif /* !MYSQL_CLIENT */
@@ -6205,6 +6485,9 @@ User_var_log_event::
User_var_log_event(const char* buf,
const Format_description_log_event* description_event)
:Log_event(buf, description_event)
+#ifndef MYSQL_CLIENT
+ , deferred(false)
+#endif
{
/* The Post-Header is empty. The Variable Data part begins immediately. */
const char *start= buf;
@@ -6351,9 +6634,8 @@ void User_var_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
my_b_printf(&cache, "\tUser_var\n");
}
- my_b_printf(&cache, "SET @`");
- my_b_write(&cache, (uchar*) name, (uint) (name_len));
- my_b_printf(&cache, "`");
+ my_b_printf(&cache, "SET @");
+ my_b_write_backtick_quote(&cache, name, name_len);
if (is_null)
{
@@ -6452,12 +6734,16 @@ int User_var_log_event::do_apply_event(Relay_log_info const *rli)
{
Item *it= 0;
CHARSET_INFO *charset;
+ DBUG_ENTER("User_var_log_event::do_apply_event");
if (rli->deferred_events_collecting)
- return rli->deferred_events->add(this);
+ {
+ set_deferred();
+ DBUG_RETURN(rli->deferred_events->add(this));
+ }
if (!(charset= get_charset(charset_number, MYF(MY_WME))))
- return 1;
+ DBUG_RETURN(1);
LEX_STRING user_var_name;
user_var_name.str= name;
user_var_name.length= name_len;
@@ -6503,10 +6789,11 @@ int User_var_log_event::do_apply_event(Relay_log_info const *rli)
case ROW_RESULT:
default:
DBUG_ASSERT(0);
- return 0;
+ DBUG_RETURN(0);
}
}
- Item_func_set_user_var e(user_var_name, it);
+
+ Item_func_set_user_var *e= new Item_func_set_user_var(user_var_name, it);
/*
Item_func_set_user_var can't substitute something else on its place =>
0 can be passed as last argument (reference on item)
@@ -6515,19 +6802,20 @@ int User_var_log_event::do_apply_event(Relay_log_info const *rli)
crash the server, so if fix fields fails, we just return with an
error.
*/
- if (e.fix_fields(thd, 0))
- return 1;
+ if (e->fix_fields(thd, 0))
+ DBUG_RETURN(1);
/*
A variable can just be considered as a table with
a single record and with a single column. Thus, like
a column value, it could always have IMPLICIT derivation.
*/
- e.update_hash(val, val_len, type, charset, DERIVATION_IMPLICIT,
- (flags & User_var_log_event::UNSIGNED_F));
- free_root(thd->mem_root,0);
+ e->update_hash(val, val_len, type, charset, DERIVATION_IMPLICIT,
+ (flags & User_var_log_event::UNSIGNED_F));
+ if (!is_deferred())
+ free_root(thd->mem_root, 0);
- return 0;
+ DBUG_RETURN(0);
}
int User_var_log_event::do_update_pos(Relay_log_info *rli)
@@ -6570,7 +6858,7 @@ void Unknown_log_event::print(FILE* file_arg, PRINT_EVENT_INFO* print_event_info
#endif
#ifndef MYSQL_CLIENT
-void Slave_log_event::pack_info(Protocol *protocol)
+void Slave_log_event::pack_info(THD *thd, Protocol *protocol)
{
char buf[256+HOSTNAME_LENGTH], *pos;
pos= strmov(buf, "host=");
@@ -6922,11 +7210,18 @@ void Create_file_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info
{
Load_log_event::print(file, print_event_info,
!check_fname_outside_temp_buf());
- /*
- That one is for "file_id: etc" below: in mysqlbinlog we want the #, in
- SHOW BINLOG EVENTS we don't.
- */
- my_b_printf(&cache, "#");
+ /**
+ reduce the size of io cache so that the write function is called
+ for every call to my_b_printf().
+ */
+ DBUG_EXECUTE_IF ("simulate_create_event_write_error",
+ {(&cache)->write_pos= (&cache)->write_end;
+ DBUG_SET("+d,simulate_file_write_error");});
+ /*
+ That one is for "file_id: etc" below: in mysqlbinlog we want the #, in
+ SHOW BINLOG EVENTS we don't.
+ */
+ my_b_printf(&cache, "#");
}
my_b_printf(&cache, " file_id: %d block_len: %d\n", file_id, block_len);
@@ -6945,7 +7240,7 @@ void Create_file_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info
*/
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-void Create_file_log_event::pack_info(Protocol *protocol)
+void Create_file_log_event::pack_info(THD *thd, Protocol *protocol)
{
char buf[SAFE_NAME_LEN*2 + 30 + 21*2], *pos;
pos= strmov(buf, "db=");
@@ -7129,7 +7424,7 @@ void Append_block_log_event::print(FILE* file,
*/
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-void Append_block_log_event::pack_info(Protocol *protocol)
+void Append_block_log_event::pack_info(THD *thd, Protocol *protocol)
{
char buf[256];
uint length;
@@ -7284,7 +7579,7 @@ void Delete_file_log_event::print(FILE* file,
*/
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-void Delete_file_log_event::pack_info(Protocol *protocol)
+void Delete_file_log_event::pack_info(THD *thd, Protocol *protocol)
{
char buf[64];
uint length;
@@ -7383,7 +7678,7 @@ void Execute_load_log_event::print(FILE* file,
*/
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-void Execute_load_log_event::pack_info(Protocol *protocol)
+void Execute_load_log_event::pack_info(THD *thd, Protocol *protocol)
{
char buf[64];
uint length;
@@ -7612,6 +7907,13 @@ void Execute_load_query_log_event::print(FILE* file,
Write_on_release_cache cache(&print_event_info->head_cache, file);
print_query_header(&cache, print_event_info);
+ /**
+ reduce the size of io cache so that the write function is called
+ for every call to my_b_printf().
+ */
+ DBUG_EXECUTE_IF ("simulate_execute_event_write_error",
+ {(&cache)->write_pos= (&cache)->write_end;
+ DBUG_SET("+d,simulate_file_write_error");});
if (local_fname)
{
@@ -7638,27 +7940,24 @@ void Execute_load_query_log_event::print(FILE* file,
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-void Execute_load_query_log_event::pack_info(Protocol *protocol)
+void Execute_load_query_log_event::pack_info(THD *thd, Protocol *protocol)
{
- char *buf, *pos;
- if (!(buf= (char*) my_malloc(9 + db_len + q_len + 10 + 21, MYF(MY_WME))))
- return;
- pos= buf;
+ char buf_mem[1024];
+ String buf(buf_mem, sizeof(buf_mem), system_charset_info);
+ buf.real_alloc(9 + db_len + q_len + 10 + 21);
if (db && db_len)
{
- pos= strmov(buf, "use `");
- memcpy(pos, db, db_len);
- pos= strmov(pos+db_len, "`; ");
- }
- if (query && q_len)
- {
- memcpy(pos, query, q_len);
- pos+= q_len;
+ if (buf.append("use ") ||
+ append_identifier(thd, &buf, db, db_len) ||
+ buf.append("; "))
+ return;
}
- pos= strmov(pos, " ;file_id=");
- pos= int10_to_str((long) file_id, pos, 10);
- protocol->store(buf, pos-buf, &my_charset_bin);
- my_free(buf);
+ if (query && q_len && buf.append(query, q_len))
+ return;
+ if (buf.append(" ;file_id=") ||
+ buf.append_ulonglong(file_id))
+ return;
+ protocol->store(buf.ptr(), buf.length(), &my_charset_bin);
}
@@ -8621,7 +8920,7 @@ bool Rows_log_event::write_data_body(IO_CACHE*file)
#endif
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-void Rows_log_event::pack_info(Protocol *protocol)
+void Rows_log_event::pack_info(THD *thd, Protocol *protocol)
{
char buf[256];
char const *const flagstr=
@@ -8725,7 +9024,7 @@ bool Annotate_rows_log_event::write_data_body(IO_CACHE *file)
#endif
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
-void Annotate_rows_log_event::pack_info(Protocol* protocol)
+void Annotate_rows_log_event::pack_info(THD *thd, Protocol* protocol)
{
if (m_query_txt && m_query_len)
protocol->store(m_query_txt, m_query_len, &my_charset_bin);
@@ -9469,7 +9768,7 @@ bool Table_map_log_event::write_data_body(IO_CACHE *file)
*/
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-void Table_map_log_event::pack_info(Protocol *protocol)
+void Table_map_log_event::pack_info(THD *thd, Protocol *protocol)
{
char buf[256];
size_t bytes= my_snprintf(buf, sizeof(buf),
@@ -9490,7 +9789,7 @@ void Table_map_log_event::print(FILE *, PRINT_EVENT_INFO *print_event_info)
{
print_header(&print_event_info->head_cache, print_event_info, TRUE);
my_b_printf(&print_event_info->head_cache,
- "\tTable_map: `%s`.`%s` mapped to number %lu\n",
+ "\tTable_map: %`s.%`s mapped to number %lu\n",
m_dbnam, m_tblnam, m_table_id);
print_base64(&print_event_info->body_cache, print_event_info, TRUE);
}
@@ -10829,7 +11128,7 @@ Incident_log_event::description() const
#ifndef MYSQL_CLIENT
-void Incident_log_event::pack_info(Protocol *protocol)
+void Incident_log_event::pack_info(THD *thd, Protocol *protocol)
{
char buf[256];
size_t bytes;
@@ -10950,6 +11249,9 @@ Heartbeat_log_event::Heartbeat_log_event(const char* buf, uint event_len,
There is a dummy replacement for this in the embedded library that returns
FALSE; this is used by XtraDB to allow it to access replication stuff while
still being able to use the same plugin in both stand-alone and embedded.
+
+ In this function it's ok to use active_mi, as this is only called for
+ the main replication server.
*/
bool rpl_get_position_info(const char **log_file_name, ulonglong *log_pos,
const char **group_relay_log_name,
diff --git a/sql/log_event.h b/sql/log_event.h
index cccab93e0d5..dfbefdb359e 100644
--- a/sql/log_event.h
+++ b/sql/log_event.h
@@ -259,6 +259,7 @@ struct sql_ex_info
#define INCIDENT_HEADER_LEN 2
#define HEARTBEAT_HEADER_LEN 0
#define ANNOTATE_ROWS_HEADER_LEN 0
+#define BINLOG_CHECKPOINT_HEADER_LEN 4
/*
Max number of possible extra bytes in a replication event compared to a
@@ -284,6 +285,13 @@ struct sql_ex_info
MAX_SIZE_LOG_EVENT_STATUS + /* status */ \
NAME_LEN + 1)
+/*
+ The new option is added to handle large packets that are sent from the master
+ to the slave. It is used to increase the thd(max_allowed) for both the
+ DUMP thread on the master and the SQL/IO thread on the slave.
+*/
+#define MAX_MAX_ALLOWED_PACKET 1024*1024*1024
+
/*
Event header offsets;
these point to places inside the fixed header.
@@ -566,6 +574,43 @@ enum enum_binlog_checksum_alg {
#define BINLOG_CHECKSUM_LEN CHECKSUM_CRC32_SIGNATURE_LEN
#define BINLOG_CHECKSUM_ALG_DESC_LEN 1 /* 1 byte checksum alg descriptor */
+/*
+ These are capability numbers for MariaDB slave servers.
+
+ Newer MariaDB slaves set this to inform the master about their capabilities.
+ This allows the master to decide which events it can send to the slave
+ without breaking replication on old slaves that maybe do not understand
+ all events from newer masters.
+
+ As new releases are backwards compatible, a given capability implies also
+ all capabilities with smaller number.
+
+ Older MariaDB slaves and other MySQL slave servers do not set this, so they
+ are recorded with capability 0.
+*/
+
+/* MySQL or old MariaDB slave with no announced capability. */
+#define MARIA_SLAVE_CAPABILITY_UNKNOWN 0
+/* MariaDB >= 5.3, which understands ANNOTATE_ROWS_EVENT. */
+#define MARIA_SLAVE_CAPABILITY_ANNOTATE 1
+/*
+ MariaDB >= 5.5. This version has the capability to tolerate events omitted
+ from the binlog stream without breaking replication (MySQL slaves fail
+ because they mis-compute the offsets into the master's binlog).
+*/
+#define MARIA_SLAVE_CAPABILITY_TOLERATE_HOLES 2
+/* MariaDB > 5.5, which knows about binlog_checkpoint_log_event. */
+#define MARIA_SLAVE_CAPABILITY_BINLOG_CHECKPOINT 3
+/*
+ MariaDB server which understands MySQL 5.6 ignorable events. This server
+ can tolerate receiving any event with the LOG_EVENT_IGNORABLE_F flag set.
+*/
+#define MARIA_SLAVE_CAPABILITY_IGNORABLE 4
+
+/* Our capability. */
+#define MARIA_SLAVE_CAPABILITY_MINE MARIA_SLAVE_CAPABILITY_BINLOG_CHECKPOINT
+
+
/**
@enum Log_event_type
@@ -641,6 +686,14 @@ enum Log_event_type
MARIA_EVENTS_BEGIN= 160,
/* New Maria event numbers start from here */
ANNOTATE_ROWS_EVENT= 160,
+ /*
+ Binlog checkpoint event. Used for XA crash recovery on the master, not used
+ in replication.
+ A binlog checkpoint event specifies a binlog file such that XA crash
+ recovery can start from that file - and it is guaranteed to find all XIDs
+ that are prepared in storage engines but not yet committed.
+ */
+ BINLOG_CHECKPOINT_EVENT= 161,
/* Add new MariaDB events here - right above this comment! */
@@ -1058,14 +1111,15 @@ public:
*/
static void init_show_field_list(List<Item>* field_list);
#ifdef HAVE_REPLICATION
- int net_send(Protocol *protocol, const char* log_name, my_off_t pos);
+ int net_send(THD *thd, Protocol *protocol, const char* log_name,
+ my_off_t pos);
/*
pack_info() is used by SHOW BINLOG EVENTS; as print() it prepares and sends
a string to display to the user, so it resembles print().
*/
- virtual void pack_info(Protocol *protocol);
+ virtual void pack_info(THD *thd, Protocol *protocol);
#endif /* HAVE_REPLICATION */
virtual const char* get_db()
@@ -1802,7 +1856,7 @@ public:
bool using_trans, bool direct, bool suppress_use, int error);
const char* get_db() { return db; }
#ifdef HAVE_REPLICATION
- void pack_info(Protocol* protocol);
+ void pack_info(THD *thd, Protocol* protocol);
#endif /* HAVE_REPLICATION */
#else
void print_query_header(IO_CACHE* file, PRINT_EVENT_INFO* print_event_info);
@@ -1819,6 +1873,7 @@ public:
my_free(data_buf);
}
Log_event_type get_type_code() { return QUERY_EVENT; }
+ static int dummy_event(String *packet, ulong ev_offset, uint8 checksum_alg);
#ifdef MYSQL_SERVER
bool write(IO_CACHE* file);
virtual bool write_post_header_for_derived(IO_CACHE* file) { return FALSE; }
@@ -1932,7 +1987,7 @@ public:
#ifdef MYSQL_SERVER
Slave_log_event(THD* thd_arg, Relay_log_info* rli);
- void pack_info(Protocol* protocol);
+ void pack_info(THD *thd, Protocol* protocol);
#else
void print(FILE* file, PRINT_EVENT_INFO* print_event_info);
#endif
@@ -2165,9 +2220,9 @@ protected:
const Format_description_log_event* description_event);
public:
- uint get_query_buffer_length();
- void print_query(bool need_db, const char *cs, char *buf, char **end,
- char **fn_start, char **fn_end);
+ void print_query(THD *thd, bool need_db, const char *cs, String *buf,
+ my_off_t *fn_start, my_off_t *fn_end,
+ const char *qualify_db);
ulong thread_id;
ulong slave_proxy_id;
uint32 table_name_len;
@@ -2228,7 +2283,7 @@ public:
Name_resolution_context *context);
const char* get_db() { return db; }
#ifdef HAVE_REPLICATION
- void pack_info(Protocol* protocol);
+ void pack_info(THD *thd, Protocol* protocol);
#endif /* HAVE_REPLICATION */
#else
void print(FILE* file, PRINT_EVENT_INFO* print_event_info);
@@ -2325,7 +2380,7 @@ public:
#ifdef MYSQL_SERVER
Start_log_event_v3();
#ifdef HAVE_REPLICATION
- void pack_info(Protocol* protocol);
+ void pack_info(THD *thd, Protocol* protocol);
#endif /* HAVE_REPLICATION */
#else
Start_log_event_v3() {}
@@ -2489,7 +2544,7 @@ Intvar_log_event(THD* thd_arg,uchar type_arg, ulonglong val_arg,
cache_type= Log_event::EVENT_NO_CACHE;
}
#ifdef HAVE_REPLICATION
- void pack_info(Protocol* protocol);
+ void pack_info(THD *thd, Protocol* protocol);
#endif /* HAVE_REPLICATION */
#else
void print(FILE* file, PRINT_EVENT_INFO* print_event_info);
@@ -2569,7 +2624,7 @@ class Rand_log_event: public Log_event
cache_type= Log_event::EVENT_NO_CACHE;
}
#ifdef HAVE_REPLICATION
- void pack_info(Protocol* protocol);
+ void pack_info(THD *thd, Protocol* protocol);
#endif /* HAVE_REPLICATION */
#else
void print(FILE* file, PRINT_EVENT_INFO* print_event_info);
@@ -2618,7 +2673,7 @@ class Xid_log_event: public Log_event
cache_type= Log_event::EVENT_NO_CACHE;
}
#ifdef HAVE_REPLICATION
- void pack_info(Protocol* protocol);
+ void pack_info(THD *thd, Protocol* protocol);
#endif /* HAVE_REPLICATION */
#else
void print(FILE* file, PRINT_EVENT_INFO* print_event_info);
@@ -2666,6 +2721,7 @@ public:
bool is_null;
uchar flags;
#ifdef MYSQL_SERVER
+ bool deferred;
User_var_log_event(THD* thd_arg, char *name_arg, uint name_len_arg,
char *val_arg, ulong val_len_arg, Item_result type_arg,
uint charset_number_arg, uchar flags_arg,
@@ -2673,13 +2729,13 @@ public:
:Log_event(thd_arg, 0, using_trans),
name(name_arg), name_len(name_len_arg), val(val_arg),
val_len(val_len_arg), type(type_arg), charset_number(charset_number_arg),
- flags(flags_arg)
+ flags(flags_arg), deferred(false)
{
is_null= !val;
if (direct)
cache_type= Log_event::EVENT_NO_CACHE;
}
- void pack_info(Protocol* protocol);
+ void pack_info(THD *thd, Protocol* protocol);
#else
void print(FILE* file, PRINT_EVENT_INFO* print_event_info);
#endif
@@ -2690,6 +2746,13 @@ public:
Log_event_type get_type_code() { return USER_VAR_EVENT;}
#ifdef MYSQL_SERVER
bool write(IO_CACHE* file);
+ /*
+ Getter and setter for deferred User-event.
+ Returns true if the event is not applied directly
+ and which case the applier adjusts execution path.
+ */
+ bool is_deferred() { return deferred; }
+ void set_deferred() { deferred= val; }
#endif
bool is_valid() const { return 1; }
@@ -2810,7 +2873,7 @@ public:
uint ident_len_arg,
ulonglong pos_arg, uint flags);
#ifdef HAVE_REPLICATION
- void pack_info(Protocol* protocol);
+ void pack_info(THD *thd, Protocol* protocol);
#endif /* HAVE_REPLICATION */
#else
void print(FILE* file, PRINT_EVENT_INFO* print_event_info);
@@ -2838,6 +2901,32 @@ private:
};
+class Binlog_checkpoint_log_event: public Log_event
+{
+public:
+ char *binlog_file_name;
+ uint binlog_file_len;
+
+#ifdef MYSQL_SERVER
+ Binlog_checkpoint_log_event(const char *binlog_file_name_arg,
+ uint binlog_file_len_arg);
+#ifdef HAVE_REPLICATION
+ void pack_info(THD *thd, Protocol *protocol);
+#endif
+#else
+ void print(FILE *file, PRINT_EVENT_INFO *print_event_info);
+#endif
+ Binlog_checkpoint_log_event(const char *buf, uint event_len,
+ const Format_description_log_event *description_event);
+ ~Binlog_checkpoint_log_event() { my_free(binlog_file_name); }
+ Log_event_type get_type_code() { return BINLOG_CHECKPOINT_EVENT;}
+ int get_data_size() { return binlog_file_len + BINLOG_CHECKPOINT_HEADER_LEN;}
+ bool is_valid() const { return binlog_file_name != 0; }
+#ifdef MYSQL_SERVER
+ bool write(IO_CACHE* file);
+#endif
+};
+
/* the classes below are for the new LOAD DATA INFILE logging */
/**
@@ -2871,7 +2960,7 @@ public:
uchar* block_arg, uint block_len_arg,
bool using_trans);
#ifdef HAVE_REPLICATION
- void pack_info(Protocol* protocol);
+ void pack_info(THD *thd, Protocol* protocol);
#endif /* HAVE_REPLICATION */
#else
void print(FILE* file, PRINT_EVENT_INFO* print_event_info);
@@ -2943,7 +3032,7 @@ public:
Append_block_log_event(THD* thd, const char* db_arg, uchar* block_arg,
uint block_len_arg, bool using_trans);
#ifdef HAVE_REPLICATION
- void pack_info(Protocol* protocol);
+ void pack_info(THD *thd, Protocol* protocol);
virtual int get_create_or_append() const;
#endif /* HAVE_REPLICATION */
#else
@@ -2984,7 +3073,7 @@ public:
#ifdef MYSQL_SERVER
Delete_file_log_event(THD* thd, const char* db_arg, bool using_trans);
#ifdef HAVE_REPLICATION
- void pack_info(Protocol* protocol);
+ void pack_info(THD *thd, Protocol* protocol);
#endif /* HAVE_REPLICATION */
#else
void print(FILE* file, PRINT_EVENT_INFO* print_event_info);
@@ -3025,7 +3114,7 @@ public:
#ifdef MYSQL_SERVER
Execute_load_log_event(THD* thd, const char* db_arg, bool using_trans);
#ifdef HAVE_REPLICATION
- void pack_info(Protocol* protocol);
+ void pack_info(THD *thd, Protocol* protocol);
#endif /* HAVE_REPLICATION */
#else
void print(FILE* file, PRINT_EVENT_INFO* print_event_info);
@@ -3121,7 +3210,7 @@ public:
bool using_trans, bool direct,
bool suppress_use, int errcode);
#ifdef HAVE_REPLICATION
- void pack_info(Protocol* protocol);
+ void pack_info(THD *thd, Protocol* protocol);
#endif /* HAVE_REPLICATION */
#else
void print(FILE* file, PRINT_EVENT_INFO* print_event_info);
@@ -3207,7 +3296,7 @@ public:
#endif
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
- virtual void pack_info(Protocol*);
+ virtual void pack_info(THD *thd, Protocol*);
#endif
#ifdef MYSQL_CLIENT
@@ -3621,7 +3710,7 @@ public:
#endif
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
- virtual void pack_info(Protocol *protocol);
+ virtual void pack_info(THD *thd, Protocol *protocol);
#endif
#ifdef MYSQL_CLIENT
@@ -3733,7 +3822,7 @@ public:
flag_set get_flags(flag_set flags_arg) const { return m_flags & flags_arg; }
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
- virtual void pack_info(Protocol *protocol);
+ virtual void pack_info(THD *thd, Protocol *protocol);
#endif
#ifdef MYSQL_CLIENT
@@ -4180,7 +4269,7 @@ public:
#endif
#ifdef MYSQL_SERVER
- void pack_info(Protocol*);
+ void pack_info(THD *thd, Protocol*);
#endif
Incident_log_event(const char *buf, uint event_len,
@@ -4279,6 +4368,16 @@ bool event_checksum_test(uchar *buf, ulong event_len, uint8 alg);
uint8 get_checksum_alg(const char* buf, ulong len);
extern TYPELIB binlog_checksum_typelib;
+#ifndef MYSQL_CLIENT
+/**
+ The function is called by slave applier in case there are
+ active table filtering rules to force gathering events associated
+ with Query-log-event into an array to execute
+ them once the fate of the Query is determined for execution.
+*/
+bool slave_execute_deferred_events(THD *thd);
+#endif
+
/**
@} (end of group Replication)
*/
diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc
index 8f2c515e11c..d7c66af769a 100644
--- a/sql/log_event_old.cc
+++ b/sql/log_event_old.cc
@@ -1935,7 +1935,7 @@ bool Old_rows_log_event::write_data_body(IO_CACHE*file)
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-void Old_rows_log_event::pack_info(Protocol *protocol)
+void Old_rows_log_event::pack_info(THD *thd, Protocol *protocol)
{
char buf[256];
char const *const flagstr=
diff --git a/sql/log_event_old.h b/sql/log_event_old.h
index 8fe2e9e0a75..3e1efd8e2c0 100644
--- a/sql/log_event_old.h
+++ b/sql/log_event_old.h
@@ -108,7 +108,7 @@ public:
flag_set get_flags(flag_set flags_arg) const { return m_flags & flags_arg; }
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
- virtual void pack_info(Protocol *protocol);
+ virtual void pack_info(THD *thd, Protocol *protocol);
#endif
#ifdef MYSQL_CLIENT
diff --git a/sql/multi_range_read.cc b/sql/multi_range_read.cc
index ac44ba7288b..12f1f7c5519 100644
--- a/sql/multi_range_read.cc
+++ b/sql/multi_range_read.cc
@@ -1404,7 +1404,7 @@ ha_rows DsMrr_impl::dsmrr_info(uint keyno, uint n_ranges, uint rows,
uint key_parts,
uint *bufsz, uint *flags, Cost_estimate *cost)
{
- ha_rows __attribute__((unused)) res;
+ ha_rows res __attribute__((unused));
uint def_flags= *flags;
uint def_bufsz= *bufsz;
diff --git a/sql/my_apc.cc b/sql/my_apc.cc
new file mode 100644
index 00000000000..5d1adb6bca7
--- /dev/null
+++ b/sql/my_apc.cc
@@ -0,0 +1,270 @@
+/*
+ Copyright (c) 2011 - 2012, 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
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+
+#ifndef MY_APC_STANDALONE
+
+#include "sql_priv.h"
+#include "sql_class.h"
+
+#endif
+
+/* For standalone testing of APC system, see unittest/sql/my_apc-t.cc */
+
+/*
+ Initialize the target.
+
+ @note
+ Initialization must be done prior to enabling/disabling the target, or making
+ any call requests to it.
+ Initial state after initialization is 'disabled'.
+*/
+void Apc_target::init(mysql_mutex_t *target_mutex)
+{
+ DBUG_ASSERT(!enabled);
+ LOCK_thd_data_ptr= target_mutex;
+#ifndef DBUG_OFF
+ n_calls_processed= 0;
+#endif
+}
+
+
+/*
+ Destroy the target. The target must be disabled when this call is made.
+*/
+void Apc_target::destroy()
+{
+ DBUG_ASSERT(!enabled);
+}
+
+
+/*
+ Enter ther state where the target is available for serving APC requests
+*/
+void Apc_target::enable()
+{
+ /* Ok to do without getting/releasing the mutex: */
+ enabled++;
+}
+
+
+/*
+ Make the target unavailable for serving APC requests.
+
+ @note
+ This call will serve all requests that were already enqueued
+*/
+
+void Apc_target::disable()
+{
+ bool process= FALSE;
+ mysql_mutex_lock(LOCK_thd_data_ptr);
+ if (!(--enabled))
+ process= TRUE;
+ mysql_mutex_unlock(LOCK_thd_data_ptr);
+ if (process)
+ process_apc_requests();
+}
+
+
+/* [internal] Put request qe into the request list */
+
+void Apc_target::enqueue_request(Call_request *qe)
+{
+ mysql_mutex_assert_owner(LOCK_thd_data_ptr);
+ if (apc_calls)
+ {
+ Call_request *after= apc_calls->prev;
+ qe->next= apc_calls;
+ apc_calls->prev= qe;
+
+ qe->prev= after;
+ after->next= qe;
+ }
+ else
+ {
+ apc_calls= qe;
+ qe->next= qe->prev= qe;
+ }
+}
+
+
+/*
+ [internal] Remove request qe from the request queue.
+
+ The request is not necessarily first in the queue.
+*/
+
+void Apc_target::dequeue_request(Call_request *qe)
+{
+ mysql_mutex_assert_owner(LOCK_thd_data_ptr);
+ if (apc_calls == qe)
+ {
+ if ((apc_calls= apc_calls->next) == qe)
+ {
+ apc_calls= NULL;
+ }
+ }
+
+ qe->prev->next= qe->next;
+ qe->next->prev= qe->prev;
+}
+
+#ifdef HAVE_PSI_INTERFACE
+
+/* One key for all conds */
+PSI_cond_key key_show_explain_request_COND;
+
+static PSI_cond_info show_explain_psi_conds[]=
+{
+ { &key_show_explain_request_COND, "show_explain", 0 /* not using PSI_FLAG_GLOBAL*/ }
+};
+
+void init_show_explain_psi_keys(void)
+{
+ if (PSI_server == NULL)
+ return;
+
+ PSI_server->register_cond("sql", show_explain_psi_conds,
+ array_elements(show_explain_psi_conds));
+}
+#endif
+
+
+/*
+ Make an APC (Async Procedure Call) to another thread.
+
+ @detail
+ Make an APC call: schedule it for execution and wait until the target
+ thread has executed it.
+
+ - The caller is responsible for making sure he's not posting request
+ to the thread he's calling this function from.
+
+ - The caller must have locked target_mutex. The function will release it.
+
+ @retval FALSE - Ok, the call has been made
+ @retval TRUE - Call wasnt made (either the target is in disabled state or
+ timeout occured)
+*/
+
+bool Apc_target::make_apc_call(THD *caller_thd, Apc_call *call,
+ int timeout_sec, bool *timed_out)
+{
+ bool res= TRUE;
+ *timed_out= FALSE;
+
+ if (enabled)
+ {
+ /* Create and post the request */
+ Call_request apc_request;
+ apc_request.call= call;
+ apc_request.processed= FALSE;
+ mysql_cond_init(key_show_explain_request_COND, &apc_request.COND_request,
+ NULL);
+ enqueue_request(&apc_request);
+ apc_request.what="enqueued by make_apc_call";
+
+ struct timespec abstime;
+ const int timeout= timeout_sec;
+ set_timespec(abstime, timeout);
+
+ int wait_res= 0;
+ PSI_stage_info old_stage;
+ caller_thd->ENTER_COND(&apc_request.COND_request, LOCK_thd_data_ptr,
+ &stage_show_explain, &old_stage);
+ /* todo: how about processing other errors here? */
+ while (!apc_request.processed && (wait_res != ETIMEDOUT))
+ {
+ /* We own LOCK_thd_data_ptr */
+ wait_res= mysql_cond_timedwait(&apc_request.COND_request,
+ LOCK_thd_data_ptr, &abstime);
+ // &apc_request.LOCK_request, &abstime);
+ if (caller_thd->killed)
+ break;
+ }
+
+ if (!apc_request.processed)
+ {
+ /*
+ The wait has timed out, or this thread was KILLed.
+ Remove the request from the queue (ok to do because we own
+ LOCK_thd_data_ptr)
+ */
+ apc_request.processed= TRUE;
+ dequeue_request(&apc_request);
+ *timed_out= TRUE;
+ res= TRUE;
+ }
+ else
+ {
+ /* Request was successfully executed and dequeued by the target thread */
+ res= FALSE;
+ }
+ /*
+ exit_cond() will call mysql_mutex_unlock(LOCK_thd_data_ptr) for us:
+ */
+ caller_thd->EXIT_COND(&old_stage);
+
+ /* Destroy all APC request data */
+ mysql_cond_destroy(&apc_request.COND_request);
+ }
+ else
+ {
+ mysql_mutex_unlock(LOCK_thd_data_ptr);
+ }
+ return res;
+}
+
+
+/*
+ Process all APC requests.
+ This should be called periodically by the APC target thread.
+*/
+
+void Apc_target::process_apc_requests()
+{
+ while (1)
+ {
+ Call_request *request;
+
+ mysql_mutex_lock(LOCK_thd_data_ptr);
+ if (!(request= get_first_in_queue()))
+ {
+ /* No requests in the queue */
+ mysql_mutex_unlock(LOCK_thd_data_ptr);
+ break;
+ }
+
+ /*
+ Remove the request from the queue (we're holding queue lock so we can be
+ sure that request owner won't try to remove it)
+ */
+ request->what="dequeued by process_apc_requests";
+ dequeue_request(request);
+ request->processed= TRUE;
+
+ request->call->call_in_target_thread();
+ request->what="func called by process_apc_requests";
+
+#ifndef DBUG_OFF
+ n_calls_processed++;
+#endif
+ mysql_cond_signal(&request->COND_request);
+ mysql_mutex_unlock(LOCK_thd_data_ptr);
+ }
+}
+
diff --git a/sql/my_apc.h b/sql/my_apc.h
new file mode 100644
index 00000000000..7f19809c082
--- /dev/null
+++ b/sql/my_apc.h
@@ -0,0 +1,138 @@
+#ifndef INCLUDES_MY_APC_H
+#define INCLUDES_MY_APC_H
+/*
+ Copyright (c) 2011 - 2012, 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
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/*
+ Interface
+ ~~~~~~~~~
+ (
+ - This is an APC request queue
+ - We assume there is a particular owner thread which periodically calls
+ process_apc_requests() to serve the call requests.
+ - Other threads can post call requests, and block until they are exectued.
+ )
+
+ Implementation
+ ~~~~~~~~~~~~~~
+ - The target has a mutex-guarded request queue.
+
+ - After the request has been put into queue, the requestor waits for request
+ to be satisfied. The worker satisifes the request and signals the
+ requestor.
+*/
+
+class THD;
+
+/*
+ Target for asynchronous procedure calls (APCs).
+ - A target is running in some particular thread,
+ - One can make calls to it from other threads.
+*/
+class Apc_target
+{
+ mysql_mutex_t *LOCK_thd_data_ptr;
+public:
+ Apc_target() : enabled(0), apc_calls(NULL) {}
+ ~Apc_target() { DBUG_ASSERT(!enabled && !apc_calls);}
+
+ void init(mysql_mutex_t *target_mutex);
+ void destroy();
+ void enable();
+ void disable();
+
+ void process_apc_requests();
+ /*
+ A lightweight function, intended to be used in frequent checks like this:
+
+ if (apc_target.have_requests()) apc_target.process_apc_requests()
+ */
+ inline bool have_apc_requests()
+ {
+ return test(apc_calls);
+ }
+
+ /* Functor class for calls you can schedule */
+ class Apc_call
+ {
+ public:
+ /* This function will be called in the target thread */
+ virtual void call_in_target_thread()= 0;
+ virtual ~Apc_call() {}
+ };
+
+ /* Make a call in the target thread (see function definition for details) */
+ bool make_apc_call(THD *caller_thd, Apc_call *call, int timeout_sec, bool *timed_out);
+
+#ifndef DBUG_OFF
+ int n_calls_processed; /* Number of calls served by this target */
+#endif
+private:
+ class Call_request;
+
+ /*
+ Non-zero value means we're enabled. It's an int, not bool, because one can
+ call enable() N times (and then needs to call disable() N times before the
+ target is really disabled)
+ */
+ int enabled;
+
+ /*
+ Circular, double-linked list of all enqueued call requests.
+ We use this structure, because we
+ - process requests sequentially: requests are added at the end of the
+ list and removed from the front. With circular list, we can keep one
+ pointer, and access both front an back of the list with it.
+ - a thread that has posted a request may time out (or be KILLed) and
+ cancel the request, which means we need a fast request-removal
+ operation.
+ */
+ Call_request *apc_calls;
+
+ class Call_request
+ {
+ public:
+ Apc_call *call; /* Functor to be called */
+
+ /* The caller will actually wait for "processed==TRUE" */
+ bool processed;
+
+ /* Condition that will be signalled when the request has been served */
+ mysql_cond_t COND_request;
+
+ /* Double linked-list linkage */
+ Call_request *next;
+ Call_request *prev;
+
+ const char *what; /* (debug) state of the request */
+ };
+
+ void enqueue_request(Call_request *qe);
+ void dequeue_request(Call_request *qe);
+
+ /* return the first call request in queue, or NULL if there are none enqueued */
+ Call_request *get_first_in_queue()
+ {
+ return apc_calls;
+ }
+};
+
+#ifdef HAVE_PSI_INTERFACE
+void init_show_explain_psi_keys(void);
+#endif
+
+#endif //INCLUDES_MY_APC_H
+
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index b259565d6e0..3cfa9fab02b 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -52,6 +52,7 @@
#include "des_key_file.h" // load_des_key_file
#include "sql_manager.h" // stop_handle_manager, start_handle_manager
#include "sql_expression_cache.h" // subquery_cache_miss, subquery_cache_hit
+#include "sys_vars_shared.h"
#include <m_ctype.h>
#include <my_dir.h>
@@ -110,7 +111,7 @@
/* We have HAVE_valgrind below as this speeds up the shutdown of MySQL */
-#if defined(HAVE_DEC_3_2_THREADS) || defined(SIGNALS_DONT_BREAK_READ) || defined(HAVE_valgrind) && defined(__linux__)
+#if defined(SIGNALS_DONT_BREAK_READ) || defined(HAVE_valgrind) && defined(__linux__)
#define HAVE_CLOSE_SERVER_SOCK 1
#endif
@@ -364,7 +365,7 @@ static DYNAMIC_ARRAY all_options;
/* Global variables */
-bool opt_bin_log, opt_ignore_builtin_innodb= 0;
+bool opt_bin_log, opt_bin_log_used=0, opt_ignore_builtin_innodb= 0;
my_bool opt_log, opt_slow_log, debug_assert_if_crashed_table= 0, opt_help= 0, opt_abort;
ulonglong log_output_options;
my_bool opt_userstat_running;
@@ -473,7 +474,7 @@ ulong back_log, connect_timeout, concurrency, server_id;
ulong table_cache_size, table_def_size;
ulong what_to_log;
ulong slow_launch_time, slave_open_temp_tables;
-ulong open_files_limit, max_binlog_size, max_relay_log_size;
+ulong open_files_limit, max_binlog_size;
ulong slave_trans_retries;
uint slave_net_timeout;
ulong slave_exec_mode_options;
@@ -481,10 +482,12 @@ ulonglong slave_type_conversions_options;
ulong thread_cache_size=0;
ulonglong binlog_cache_size=0;
ulonglong max_binlog_cache_size=0;
+ulong slave_max_allowed_packet= 0;
ulonglong binlog_stmt_cache_size=0;
ulonglong max_binlog_stmt_cache_size=0;
ulonglong query_cache_size=0;
ulong refresh_version; /* Increments on each reload */
+ulong executed_events=0;
query_id_t global_query_id;
my_atomic_rwlock_t global_query_id_lock;
my_atomic_rwlock_t thread_running_lock;
@@ -497,6 +500,7 @@ ulong binlog_cache_use= 0, binlog_cache_disk_use= 0;
ulong binlog_stmt_cache_use= 0, binlog_stmt_cache_disk_use= 0;
ulong max_connections, max_connect_errors;
ulong extra_max_connections;
+ulong slave_retried_transactions;
ulonglong denied_connections;
my_decimal decimal_zero;
@@ -582,7 +586,7 @@ char mysql_real_data_home[FN_REFLEN],
lc_messages_dir[FN_REFLEN], reg_ext[FN_EXTLEN],
mysql_charsets_dir[FN_REFLEN],
*opt_init_file, *opt_tc_log_file;
-char *lc_messages_dir_ptr, *log_error_file_ptr;
+char *lc_messages_dir_ptr= lc_messages_dir, *log_error_file_ptr;
char mysql_unpacked_real_data_home[FN_REFLEN];
int mysql_unpacked_real_data_home_len;
uint mysql_real_data_home_len, mysql_data_home_len= 1;
@@ -675,8 +679,7 @@ pthread_attr_t connection_attrib;
mysql_mutex_t LOCK_server_started;
mysql_cond_t COND_server_started;
-int mysqld_server_started= 0;
-
+int mysqld_server_started=0, mysqld_server_initialized= 0;
File_parser_dummy_hook file_parser_dummy_hook;
/* replication parameters, if master_host is not NULL, we are a slave */
@@ -718,14 +721,15 @@ char **orig_argv;
#ifdef HAVE_PSI_INTERFACE
#ifdef HAVE_MMAP
-PSI_mutex_key key_PAGE_lock, key_LOCK_sync, key_LOCK_active, key_LOCK_pool;
+PSI_mutex_key key_PAGE_lock, key_LOCK_sync, key_LOCK_active, key_LOCK_pool,
+ key_LOCK_pending_checkpoint;
#endif /* HAVE_MMAP */
#ifdef HAVE_OPENSSL
PSI_mutex_key key_LOCK_des_key_file;
#endif /* HAVE_OPENSSL */
-PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_prep_xids,
+PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_xid_list,
key_delayed_insert_mutex, key_hash_filo_lock, key_LOCK_active_mi,
key_LOCK_connection_count, key_LOCK_crypt, key_LOCK_delayed_create,
key_LOCK_delayed_insert, key_LOCK_delayed_status, key_LOCK_error_log,
@@ -759,6 +763,7 @@ static PSI_mutex_info all_server_mutexes[]=
{ &key_LOCK_sync, "TC_LOG_MMAP::LOCK_sync", 0},
{ &key_LOCK_active, "TC_LOG_MMAP::LOCK_active", 0},
{ &key_LOCK_pool, "TC_LOG_MMAP::LOCK_pool", 0},
+ { &key_LOCK_pool, "TC_LOG_MMAP::LOCK_pending_checkpoint", 0},
#endif /* HAVE_MMAP */
#ifdef HAVE_OPENSSL
@@ -766,7 +771,7 @@ static PSI_mutex_info all_server_mutexes[]=
#endif /* HAVE_OPENSSL */
{ &key_BINLOG_LOCK_index, "MYSQL_BIN_LOG::LOCK_index", 0},
- { &key_BINLOG_LOCK_prep_xids, "MYSQL_BIN_LOG::LOCK_prep_xids", 0},
+ { &key_BINLOG_LOCK_xid_list, "MYSQL_BIN_LOG::LOCK_xid_list", 0},
{ &key_RELAYLOG_LOCK_index, "MYSQL_RELAY_LOG::LOCK_index", 0},
{ &key_delayed_insert_mutex, "Delayed_insert::mutex", 0},
{ &key_hash_filo_lock, "hash_filo::lock", 0},
@@ -834,7 +839,7 @@ static PSI_rwlock_info all_server_rwlocks[]=
PSI_cond_key key_PAGE_cond, key_COND_active, key_COND_pool;
#endif /* HAVE_MMAP */
-PSI_cond_key key_BINLOG_COND_prep_xids, key_BINLOG_update_cond,
+PSI_cond_key key_BINLOG_COND_xid_list, key_BINLOG_update_cond,
key_COND_cache_status_changed, key_COND_manager,
key_COND_rpl_status, key_COND_server_started,
key_delayed_insert_cond, key_delayed_insert_cond_client,
@@ -862,7 +867,7 @@ static PSI_cond_info all_server_conds[]=
{ &key_COND_pool, "TC_LOG_MMAP::COND_pool", 0},
{ &key_TC_LOG_MMAP_COND_queue_busy, "TC_LOG_MMAP::COND_queue_busy", 0},
#endif /* HAVE_MMAP */
- { &key_BINLOG_COND_prep_xids, "MYSQL_BIN_LOG::COND_prep_xids", 0},
+ { &key_BINLOG_COND_xid_list, "MYSQL_BIN_LOG::COND_xid_list", 0},
{ &key_BINLOG_update_cond, "MYSQL_BIN_LOG::update_cond", 0},
{ &key_BINLOG_COND_queue_busy, "MYSQL_BIN_LOG::COND_queue_busy", 0},
{ &key_RELAYLOG_update_cond, "MYSQL_RELAY_LOG::update_cond", 0},
@@ -1782,7 +1787,12 @@ void clean_up(bool print_message)
if (cleanup_done++)
return; /* purecov: inspected */
- close_active_mi();
+#ifdef HAVE_REPLICATION
+ // We must call end_slave() as clean_up may have been called during startup
+ end_slave();
+ if (use_slave_mask)
+ bitmap_free(&slave_error_mask);
+#endif
stop_handle_manager();
release_ddl_log();
@@ -1797,10 +1807,6 @@ void clean_up(bool print_message)
injector::free_instance();
mysql_bin_log.cleanup();
-#ifdef HAVE_REPLICATION
- if (use_slave_mask)
- bitmap_free(&slave_error_mask);
-#endif
my_tz_free();
my_dboptions_cache_free();
#ifndef NO_EMBEDDED_ACCESS_CHECKS
@@ -2049,13 +2055,19 @@ static struct passwd *check_user(const char *user)
if (!(tmp_user_info= getpwuid(atoi(user))))
goto err;
}
+
return tmp_user_info;
/* purecov: end */
err:
sql_print_error("Fatal error: Can't change to run as user '%s' ; Please check that the user exists!\n",user);
unireg_abort(1);
+#endif
+ return NULL;
+}
+static inline void allow_coredumps()
+{
#ifdef PR_SET_DUMPABLE
if (test_flags & TEST_CORE_ON_SIGNAL)
{
@@ -2063,11 +2075,9 @@ err:
(void) prctl(PR_SET_DUMPABLE, 1);
}
#endif
-
-#endif
- return NULL;
}
+
static void set_user(const char *user, struct passwd *user_info_arg)
{
/* purecov: begin tested */
@@ -2094,6 +2104,7 @@ static void set_user(const char *user, struct passwd *user_info_arg)
sql_perror("setuid");
unireg_abort(1);
}
+ allow_coredumps();
#endif
/* purecov: end */
}
@@ -2113,6 +2124,7 @@ static void set_effective_user(struct passwd *user_info_arg)
sql_perror("setreuid");
unireg_abort(1);
}
+ allow_coredumps();
#endif
}
@@ -2955,11 +2967,9 @@ static void start_signal_handler(void)
DBUG_ENTER("start_signal_handler");
(void) pthread_attr_init(&thr_attr);
-#if !defined(HAVE_DEC_3_2_THREADS)
pthread_attr_setscope(&thr_attr,PTHREAD_SCOPE_SYSTEM);
(void) pthread_attr_setdetachstate(&thr_attr,PTHREAD_CREATE_DETACHED);
(void) my_setstacksize(&thr_attr,my_thread_stack_size);
-#endif
mysql_mutex_lock(&LOCK_thread_count);
if ((error= mysql_thread_create(key_thread_signal_hand,
@@ -3366,6 +3376,7 @@ SHOW_VAR com_status_vars[]= {
{"show_engine_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_ENGINE_STATUS]), SHOW_LONG_STATUS},
{"show_events", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_EVENTS]), SHOW_LONG_STATUS},
{"show_errors", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_ERRORS]), SHOW_LONG_STATUS},
+ {"show_explain", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_EXPLAIN]), SHOW_LONG_STATUS},
{"show_fields", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_FIELDS]), SHOW_LONG_STATUS},
#ifndef DBUG_OFF
{"show_function_code", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_FUNC_CODE]), SHOW_LONG_STATUS},
@@ -3397,8 +3408,8 @@ SHOW_VAR com_status_vars[]= {
{"show_user_statistics", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_USER_STATS]), SHOW_LONG_STATUS},
{"show_variables", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_VARIABLES]), SHOW_LONG_STATUS},
{"show_warnings", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_WARNS]), SHOW_LONG_STATUS},
- {"slave_start", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SLAVE_START]), SHOW_LONG_STATUS},
- {"slave_stop", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SLAVE_STOP]), SHOW_LONG_STATUS},
+ {"start_all_slaves", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SLAVE_ALL_START]), SHOW_LONG_STATUS},
+ {"start_slave", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SLAVE_START]), SHOW_LONG_STATUS},
{"stmt_close", (char*) offsetof(STATUS_VAR, com_stmt_close), SHOW_LONG_STATUS},
{"stmt_execute", (char*) offsetof(STATUS_VAR, com_stmt_execute), SHOW_LONG_STATUS},
{"stmt_fetch", (char*) offsetof(STATUS_VAR, com_stmt_fetch), SHOW_LONG_STATUS},
@@ -3406,6 +3417,8 @@ SHOW_VAR com_status_vars[]= {
{"stmt_reprepare", (char*) offsetof(STATUS_VAR, com_stmt_reprepare), SHOW_LONG_STATUS},
{"stmt_reset", (char*) offsetof(STATUS_VAR, com_stmt_reset), SHOW_LONG_STATUS},
{"stmt_send_long_data", (char*) offsetof(STATUS_VAR, com_stmt_send_long_data), SHOW_LONG_STATUS},
+ {"stop_all_slaves", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SLAVE_ALL_STOP]), SHOW_LONG_STATUS},
+ {"stop_slave", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SLAVE_STOP]), SHOW_LONG_STATUS},
{"truncate", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_TRUNCATE]), SHOW_LONG_STATUS},
{"uninstall_plugin", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_UNINSTALL_PLUGIN]), SHOW_LONG_STATUS},
{"unlock_tables", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_UNLOCK_TABLES]), SHOW_LONG_STATUS},
@@ -4011,6 +4024,7 @@ static int init_thread_environment()
#ifdef HAVE_EVENT_SCHEDULER
Events::init_mutexes();
#endif
+ init_show_explain_psi_keys();
/* Parameter for threads created for connections */
(void) pthread_attr_init(&connection_attrib);
(void) pthread_attr_setdetachstate(&connection_attrib,
@@ -4261,14 +4275,15 @@ static int init_server_components()
unireg_abort(1);
/* need to configure logging before initializing storage engines */
- if (opt_log_slave_updates && !opt_bin_log)
+ if (!opt_bin_log_used)
{
- sql_print_warning("You need to use --log-bin to make "
- "--log-slave-updates work.");
+ if (opt_log_slave_updates)
+ sql_print_warning("You need to use --log-bin to make "
+ "--log-slave-updates work.");
+ if (binlog_format_used)
+ sql_print_warning("You need to use --log-bin to make "
+ "--binlog-format work.");
}
- if (!opt_bin_log && binlog_format_used)
- sql_print_warning("You need to use --log-bin to make "
- "--binlog-format work.");
/* Check that we have not let the format to unspecified at this point */
DBUG_ASSERT((uint)global_system_variables.binlog_format <=
@@ -4513,7 +4528,7 @@ a file name for --log-bin-index option", opt_binlog_index_name);
}
if (opt_bin_log && mysql_bin_log.open(opt_bin_logname, LOG_BIN, 0,
- WRITE_CACHE, 0, max_binlog_size, 0, TRUE))
+ WRITE_CACHE, max_binlog_size, 0, TRUE))
unireg_abort(1);
#ifdef HAVE_REPLICATION
@@ -4711,6 +4726,7 @@ int mysqld_main(int argc, char **argv)
}
#endif
+ mysqld_server_started= mysqld_server_initialized= 0;
orig_argc= argc;
orig_argv= argv;
my_getopt_use_args_separator= TRUE;
@@ -4984,16 +5000,6 @@ int mysqld_main(int argc, char **argv)
opt_skip_slave_start= 1;
binlog_unsafe_map_init();
- /*
- init_slave() must be called after the thread keys are created.
- Some parts of the code (e.g. SHOW STATUS LIKE 'slave_running' and other
- places) assume that active_mi != 0, so let's fail if it's 0 (out of
- memory); a message has already been printed.
- */
- if (init_slave() && !active_mi)
- {
- unireg_abort(1);
- }
#ifdef WITH_PERFSCHEMA_STORAGE_ENGINE
initialize_performance_schema_acl(opt_bootstrap);
@@ -5011,9 +5017,17 @@ int mysqld_main(int argc, char **argv)
execute_ddl_log_recovery();
+ /*
+ We must have LOCK_open before LOCK_global_system_variables because
+ LOCK_open is held while sql_plugin.c::intern_sys_var_ptr() is called.
+ */
+ mysql_mutex_record_order(&LOCK_open, &LOCK_global_system_variables);
+
if (Events::init(opt_noacl || opt_bootstrap))
unireg_abort(1);
+ mysqld_server_initialized= 1;
+
if (opt_bootstrap)
{
select_thread_in_use= 0; // Allow 'kill' to work
@@ -5026,21 +5040,27 @@ int mysqld_main(int argc, char **argv)
exit(0);
}
}
+
+ create_shutdown_thread();
+ start_handle_manager();
+
+ /*
+ init_slave() must be called after the thread keys are created.
+ Some parts of the code (e.g. SHOW STATUS LIKE 'slave_running' and other
+ places) assume that active_mi != 0, so let's fail if it's 0 (out of
+ memory); a message has already been printed.
+ */
+ if (init_slave() && !active_mi)
+ {
+ unireg_abort(1);
+ }
+
if (opt_init_file && *opt_init_file)
{
if (read_init_file(opt_init_file))
unireg_abort(1);
}
- /*
- We must have LOCK_open before LOCK_global_system_variables because
- LOCK_open is hold while sql_plugin.c::intern_sys_var_ptr() is called.
- */
- mysql_mutex_record_order(&LOCK_open, &LOCK_global_system_variables);
-
- create_shutdown_thread();
- start_handle_manager();
-
sql_print_information(ER_DEFAULT(ER_STARTUP),my_progname,server_version,
((mysql_socket_getfd(unix_sock) == INVALID_SOCKET) ?
(char*) "" : mysqld_unix_port),
@@ -6337,7 +6357,7 @@ struct my_option my_long_options[]=
{"language", 'L',
"Client error messages in given language. May be given as a full path. "
"Deprecated. Use --lc-messages-dir instead.",
- &lc_messages_dir_ptr, &lc_messages_dir_ptr, 0,
+ 0, 0, 0,
GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"lc-messages", 0,
"Set the language used for the error messages.",
@@ -6486,7 +6506,7 @@ struct my_option my_long_options[]=
"will not do updates to tables in databases that start with foo and whose "
"table names start with bar.",
0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- {"safe-mode", OPT_SAFE, "Skip some optimize stages (for testing).",
+ {"safe-mode", OPT_SAFE, "Skip some optimize stages (for testing). Deprecated.",
0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
{"safe-user-create", 0,
"Don't allow new user creation by the user who has no write privileges to the mysql.user table.",
@@ -6634,63 +6654,53 @@ static int show_rpl_status(THD *thd, SHOW_VAR *var, char *buff)
static int show_slave_running(THD *thd, SHOW_VAR *var, char *buff)
{
+ Master_info *mi;
var->type= SHOW_MY_BOOL;
- mysql_mutex_lock(&LOCK_active_mi);
var->value= buff;
- *((my_bool *)buff)= (my_bool) (active_mi &&
- active_mi->slave_running == MYSQL_SLAVE_RUN_CONNECT &&
- active_mi->rli.slave_running);
- mysql_mutex_unlock(&LOCK_active_mi);
- return 0;
-}
-
-static int show_slave_retried_trans(THD *thd, SHOW_VAR *var, char *buff)
-{
- /*
- TODO: with multimaster, have one such counter per line in
- SHOW SLAVE STATUS, and have the sum over all lines here.
- */
mysql_mutex_lock(&LOCK_active_mi);
- if (active_mi)
- {
- var->type= SHOW_LONG;
- var->value= buff;
- mysql_mutex_lock(&active_mi->rli.data_lock);
- *((long *)buff)= (long)active_mi->rli.retried_trans;
- mysql_mutex_unlock(&active_mi->rli.data_lock);
- }
+ mi= master_info_index->
+ get_master_info(&thd->variables.default_master_connection,
+ MYSQL_ERROR::WARN_LEVEL_NOTE);
+ if (mi)
+ *((my_bool *)buff)= (my_bool) (mi->slave_running ==
+ MYSQL_SLAVE_RUN_CONNECT &&
+ mi->rli.slave_running);
else
var->type= SHOW_UNDEF;
mysql_mutex_unlock(&LOCK_active_mi);
return 0;
}
+
static int show_slave_received_heartbeats(THD *thd, SHOW_VAR *var, char *buff)
{
+ Master_info *mi;
+ var->type= SHOW_LONGLONG;
+ var->value= buff;
mysql_mutex_lock(&LOCK_active_mi);
- if (active_mi)
- {
- var->type= SHOW_LONGLONG;
- var->value= buff;
- mysql_mutex_lock(&active_mi->rli.data_lock);
- *((longlong *)buff)= active_mi->received_heartbeats;
- mysql_mutex_unlock(&active_mi->rli.data_lock);
- }
+ mi= master_info_index->
+ get_master_info(&thd->variables.default_master_connection,
+ MYSQL_ERROR::WARN_LEVEL_NOTE);
+ if (mi)
+ *((longlong *)buff)= mi->received_heartbeats;
else
var->type= SHOW_UNDEF;
mysql_mutex_unlock(&LOCK_active_mi);
return 0;
}
+
static int show_heartbeat_period(THD *thd, SHOW_VAR *var, char *buff)
{
+ Master_info *mi;
+ var->type= SHOW_CHAR;
+ var->value= buff;
mysql_mutex_lock(&LOCK_active_mi);
- if (active_mi)
- {
- var->type= SHOW_CHAR;
- var->value= buff;
- sprintf(buff, "%.3f", active_mi->heartbeat_period);
- }
+ mi= master_info_index->
+ get_master_info(&thd->variables.default_master_connection,
+ MYSQL_ERROR::WARN_LEVEL_NOTE);
+ if (mi)
+ sprintf(buff, "%.3f", mi->heartbeat_period);
else
var->type= SHOW_UNDEF;
mysql_mutex_unlock(&LOCK_active_mi);
@@ -7155,7 +7165,7 @@ SHOW_VAR status_vars[]= {
{"Bytes_sent", (char*) offsetof(STATUS_VAR, bytes_sent), SHOW_LONGLONG_STATUS},
{"Binlog_bytes_written", (char*) offsetof(STATUS_VAR, binlog_bytes_written), SHOW_LONGLONG_STATUS},
{"Com", (char*) com_status_vars, SHOW_ARRAY},
- {"Compression", (char*) &show_net_compression, SHOW_FUNC},
+ {"Compression", (char*) &show_net_compression, SHOW_SIMPLE_FUNC},
{"Connections", (char*) &thread_id, SHOW_LONG_NOFLUSH},
{"Cpu_time", (char*) offsetof(STATUS_VAR, cpu_time), SHOW_DOUBLE_STATUS},
{"Created_tmp_disk_tables", (char*) offsetof(STATUS_VAR, created_tmp_disk_tables_), SHOW_LONG_STATUS},
@@ -7165,6 +7175,16 @@ SHOW_VAR status_vars[]= {
{"Delayed_insert_threads", (char*) &delayed_insert_threads, SHOW_LONG_NOFLUSH},
{"Delayed_writes", (char*) &delayed_insert_writes, SHOW_LONG},
{"Empty_queries", (char*) offsetof(STATUS_VAR, empty_queries), SHOW_LONG_STATUS},
+ {"Executed_events", (char*) &executed_events, SHOW_LONG_NOFLUSH },
+ {"Executed_triggers", (char*) offsetof(STATUS_VAR, executed_triggers), SHOW_LONG_STATUS},
+ {"Feature_dynamic_columns", (char*) offsetof(STATUS_VAR, feature_dynamic_columns), SHOW_LONG_STATUS},
+ {"Feature_fulltext", (char*) offsetof(STATUS_VAR, feature_fulltext), SHOW_LONG_STATUS},
+ {"Feature_gis", (char*) offsetof(STATUS_VAR, feature_gis), SHOW_LONG_STATUS},
+ {"Feature_locale", (char*) offsetof(STATUS_VAR, feature_locale), SHOW_LONG_STATUS},
+ {"Feature_subquery", (char*) offsetof(STATUS_VAR, feature_subquery), SHOW_LONG_STATUS},
+ {"Feature_timezone", (char*) offsetof(STATUS_VAR, feature_timezone), SHOW_LONG_STATUS},
+ {"Feature_trigger", (char*) offsetof(STATUS_VAR, feature_trigger), SHOW_LONG_STATUS},
+ {"Feature_xml", (char*) offsetof(STATUS_VAR, feature_xml), SHOW_LONG_STATUS},
{"Flush_commands", (char*) &refresh_version, SHOW_LONG_NOFLUSH},
{"Handler_commit", (char*) offsetof(STATUS_VAR, ha_commit_count), SHOW_LONG_STATUS},
{"Handler_delete", (char*) offsetof(STATUS_VAR, ha_delete_count), SHOW_LONG_STATUS},
@@ -7199,12 +7219,13 @@ SHOW_VAR status_vars[]= {
{"Not_flushed_delayed_rows", (char*) &delayed_rows_in_use, SHOW_LONG_NOFLUSH},
{"Open_files", (char*) &my_file_opened, SHOW_LONG_NOFLUSH},
{"Open_streams", (char*) &my_stream_opened, SHOW_LONG_NOFLUSH},
- {"Open_table_definitions", (char*) &show_table_definitions, SHOW_FUNC},
- {"Open_tables", (char*) &show_open_tables, SHOW_FUNC},
+ {"Open_table_definitions", (char*) &show_table_definitions, SHOW_SIMPLE_FUNC},
+ {"Open_tables", (char*) &show_open_tables, SHOW_SIMPLE_FUNC},
{"Opened_files", (char*) &my_file_total_opened, SHOW_LONG_NOFLUSH},
- {"Opened_tables", (char*) offsetof(STATUS_VAR, opened_tables), SHOW_LONG_STATUS},
{"Opened_table_definitions", (char*) offsetof(STATUS_VAR, opened_shares), SHOW_LONG_STATUS},
- {"Prepared_stmt_count", (char*) &show_prepared_stmt_count, SHOW_FUNC},
+ {"Opened_tables", (char*) offsetof(STATUS_VAR, opened_tables), SHOW_LONG_STATUS},
+ {"Opened_views", (char*) offsetof(STATUS_VAR, opened_views), SHOW_LONG_STATUS},
+ {"Prepared_stmt_count", (char*) &show_prepared_stmt_count, SHOW_SIMPLE_FUNC},
{"Rows_sent", (char*) offsetof(STATUS_VAR, rows_sent), SHOW_LONGLONG_STATUS},
{"Rows_read", (char*) offsetof(STATUS_VAR, rows_read), SHOW_LONGLONG_STATUS},
{"Rows_tmp_read", (char*) offsetof(STATUS_VAR, rows_tmp_read), SHOW_LONGLONG_STATUS},
@@ -7218,10 +7239,10 @@ SHOW_VAR status_vars[]= {
{"Qcache_queries_in_cache", (char*) &query_cache.queries_in_cache, SHOW_LONG_NOFLUSH},
{"Qcache_total_blocks", (char*) &query_cache.total_blocks, SHOW_LONG_NOFLUSH},
#endif /*HAVE_QUERY_CACHE*/
- {"Queries", (char*) &show_queries, SHOW_FUNC},
+ {"Queries", (char*) &show_queries, SHOW_SIMPLE_FUNC},
{"Questions", (char*) offsetof(STATUS_VAR, questions), SHOW_LONG_STATUS},
#ifdef HAVE_REPLICATION
- {"Rpl_status", (char*) &show_rpl_status, SHOW_FUNC},
+ {"Rpl_status", (char*) &show_rpl_status, SHOW_SIMPLE_FUNC},
#endif
{"Select_full_join", (char*) offsetof(STATUS_VAR, select_full_join_count_), SHOW_LONG_STATUS},
{"Select_full_range_join", (char*) offsetof(STATUS_VAR, select_full_range_join_count_), SHOW_LONG_STATUS},
@@ -7230,10 +7251,10 @@ SHOW_VAR status_vars[]= {
{"Select_scan", (char*) offsetof(STATUS_VAR, select_scan_count_), SHOW_LONG_STATUS},
{"Slave_open_temp_tables", (char*) &slave_open_temp_tables, SHOW_LONG},
#ifdef HAVE_REPLICATION
- {"Slave_retried_transactions",(char*) &show_slave_retried_trans, SHOW_FUNC},
- {"Slave_heartbeat_period", (char*) &show_heartbeat_period, SHOW_FUNC},
- {"Slave_received_heartbeats",(char*) &show_slave_received_heartbeats, SHOW_FUNC},
- {"Slave_running", (char*) &show_slave_running, SHOW_FUNC},
+ {"Slave_retried_transactions",(char*)&slave_retried_transactions, SHOW_LONG},
+ {"Slave_heartbeat_period", (char*) &show_heartbeat_period, SHOW_SIMPLE_FUNC},
+ {"Slave_received_heartbeats",(char*) &show_slave_received_heartbeats, SHOW_SIMPLE_FUNC},
+ {"Slave_running", (char*) &show_slave_running, SHOW_SIMPLE_FUNC},
#endif
{"Slow_launch_threads", (char*) &slow_launch_threads, SHOW_LONG},
{"Slow_queries", (char*) offsetof(STATUS_VAR, long_query_count), SHOW_LONG_STATUS},
@@ -7243,33 +7264,31 @@ SHOW_VAR status_vars[]= {
{"Sort_scan", (char*) offsetof(STATUS_VAR, filesort_scan_count_), SHOW_LONG_STATUS},
#ifdef HAVE_OPENSSL
#ifndef EMBEDDED_LIBRARY
- {"Ssl_accept_renegotiates", (char*) &show_ssl_ctx_sess_accept_renegotiate, SHOW_FUNC},
- {"Ssl_accepts", (char*) &show_ssl_ctx_sess_accept, SHOW_FUNC},
- {"Ssl_callback_cache_hits", (char*) &show_ssl_ctx_sess_cb_hits, SHOW_FUNC},
- {"Ssl_cipher", (char*) &show_ssl_get_cipher, SHOW_FUNC},
- {"Ssl_cipher_list", (char*) &show_ssl_get_cipher_list, SHOW_FUNC},
- {"Ssl_client_connects", (char*) &show_ssl_ctx_sess_connect, SHOW_FUNC},
- {"Ssl_connect_renegotiates", (char*) &show_ssl_ctx_sess_connect_renegotiate, SHOW_FUNC},
- {"Ssl_ctx_verify_depth", (char*) &show_ssl_ctx_get_verify_depth, SHOW_FUNC},
- {"Ssl_ctx_verify_mode", (char*) &show_ssl_ctx_get_verify_mode, SHOW_FUNC},
- {"Ssl_default_timeout", (char*) &show_ssl_get_default_timeout, SHOW_FUNC},
- {"Ssl_finished_accepts", (char*) &show_ssl_ctx_sess_accept_good, SHOW_FUNC},
- {"Ssl_finished_connects", (char*) &show_ssl_ctx_sess_connect_good, SHOW_FUNC},
- {"Ssl_session_cache_hits", (char*) &show_ssl_ctx_sess_hits, SHOW_FUNC},
- {"Ssl_session_cache_misses", (char*) &show_ssl_ctx_sess_misses, SHOW_FUNC},
- {"Ssl_session_cache_mode", (char*) &show_ssl_ctx_get_session_cache_mode, SHOW_FUNC},
- {"Ssl_session_cache_overflows", (char*) &show_ssl_ctx_sess_cache_full, SHOW_FUNC},
- {"Ssl_session_cache_size", (char*) &show_ssl_ctx_sess_get_cache_size, SHOW_FUNC},
- {"Ssl_session_cache_timeouts", (char*) &show_ssl_ctx_sess_timeouts, SHOW_FUNC},
- {"Ssl_sessions_reused", (char*) &show_ssl_session_reused, SHOW_FUNC},
- {"Ssl_used_session_cache_entries",(char*) &show_ssl_ctx_sess_number, SHOW_FUNC},
- {"Ssl_verify_depth", (char*) &show_ssl_get_verify_depth, SHOW_FUNC},
- {"Ssl_verify_mode", (char*) &show_ssl_get_verify_mode, SHOW_FUNC},
- {"Ssl_version", (char*) &show_ssl_get_version, SHOW_FUNC},
- {"Ssl_server_not_before", (char*) &show_ssl_get_server_not_before,
- SHOW_FUNC},
- {"Ssl_server_not_after", (char*) &show_ssl_get_server_not_after,
- SHOW_FUNC},
+ {"Ssl_accept_renegotiates", (char*) &show_ssl_ctx_sess_accept_renegotiate, SHOW_SIMPLE_FUNC},
+ {"Ssl_accepts", (char*) &show_ssl_ctx_sess_accept, SHOW_SIMPLE_FUNC},
+ {"Ssl_callback_cache_hits", (char*) &show_ssl_ctx_sess_cb_hits, SHOW_SIMPLE_FUNC},
+ {"Ssl_cipher", (char*) &show_ssl_get_cipher, SHOW_SIMPLE_FUNC},
+ {"Ssl_cipher_list", (char*) &show_ssl_get_cipher_list, SHOW_SIMPLE_FUNC},
+ {"Ssl_client_connects", (char*) &show_ssl_ctx_sess_connect, SHOW_SIMPLE_FUNC},
+ {"Ssl_connect_renegotiates", (char*) &show_ssl_ctx_sess_connect_renegotiate, SHOW_SIMPLE_FUNC},
+ {"Ssl_ctx_verify_depth", (char*) &show_ssl_ctx_get_verify_depth, SHOW_SIMPLE_FUNC},
+ {"Ssl_ctx_verify_mode", (char*) &show_ssl_ctx_get_verify_mode, SHOW_SIMPLE_FUNC},
+ {"Ssl_default_timeout", (char*) &show_ssl_get_default_timeout, SHOW_SIMPLE_FUNC},
+ {"Ssl_finished_accepts", (char*) &show_ssl_ctx_sess_accept_good, SHOW_SIMPLE_FUNC},
+ {"Ssl_finished_connects", (char*) &show_ssl_ctx_sess_connect_good, SHOW_SIMPLE_FUNC},
+ {"Ssl_server_not_after", (char*) &show_ssl_get_server_not_after, SHOW_SIMPLE_FUNC},
+ {"Ssl_server_not_before", (char*) &show_ssl_get_server_not_before, SHOW_SIMPLE_FUNC},
+ {"Ssl_session_cache_hits", (char*) &show_ssl_ctx_sess_hits, SHOW_SIMPLE_FUNC},
+ {"Ssl_session_cache_misses", (char*) &show_ssl_ctx_sess_misses, SHOW_SIMPLE_FUNC},
+ {"Ssl_session_cache_mode", (char*) &show_ssl_ctx_get_session_cache_mode, SHOW_SIMPLE_FUNC},
+ {"Ssl_session_cache_overflows", (char*) &show_ssl_ctx_sess_cache_full, SHOW_SIMPLE_FUNC},
+ {"Ssl_session_cache_size", (char*) &show_ssl_ctx_sess_get_cache_size, SHOW_SIMPLE_FUNC},
+ {"Ssl_session_cache_timeouts", (char*) &show_ssl_ctx_sess_timeouts, SHOW_SIMPLE_FUNC},
+ {"Ssl_sessions_reused", (char*) &show_ssl_session_reused, SHOW_SIMPLE_FUNC},
+ {"Ssl_used_session_cache_entries",(char*) &show_ssl_ctx_sess_number, SHOW_SIMPLE_FUNC},
+ {"Ssl_verify_depth", (char*) &show_ssl_get_verify_depth, SHOW_SIMPLE_FUNC},
+ {"Ssl_verify_mode", (char*) &show_ssl_get_verify_mode, SHOW_SIMPLE_FUNC},
+ {"Ssl_version", (char*) &show_ssl_get_version, SHOW_SIMPLE_FUNC},
#endif
#endif /* HAVE_OPENSSL */
{"Syncs", (char*) &my_sync_count, SHOW_LONG_NOFLUSH},
@@ -7287,16 +7306,16 @@ SHOW_VAR status_vars[]= {
{"Tc_log_page_waits", (char*) &tc_log_page_waits, SHOW_LONG},
#endif
#ifdef HAVE_POOL_OF_THREADS
- {"Threadpool_idle_threads", (char *) &show_threadpool_idle_threads, SHOW_FUNC},
+ {"Threadpool_idle_threads", (char *) &show_threadpool_idle_threads, SHOW_SIMPLE_FUNC},
{"Threadpool_threads", (char *) &tp_stats.num_worker_threads, SHOW_INT},
#endif
{"Threads_cached", (char*) &cached_thread_count, SHOW_LONG_NOFLUSH},
{"Threads_connected", (char*) &connection_count, SHOW_INT},
{"Threads_created", (char*) &thread_created, SHOW_LONG_NOFLUSH},
{"Threads_running", (char*) &thread_running, SHOW_INT},
- {"Uptime", (char*) &show_starttime, SHOW_FUNC},
+ {"Uptime", (char*) &show_starttime, SHOW_SIMPLE_FUNC},
#ifdef ENABLED_PROFILING
- {"Uptime_since_flush_status",(char*) &show_flushstatustime, SHOW_FUNC},
+ {"Uptime_since_flush_status",(char*) &show_flushstatustime, SHOW_SIMPLE_FUNC},
#endif
{NullS, NullS, SHOW_LONG}
};
@@ -7440,7 +7459,7 @@ static int mysql_init_variables(void)
myisam_test_invalid_symlink= test_if_data_home_dir;
#endif
opt_log= opt_slow_log= 0;
- opt_bin_log= 0;
+ opt_bin_log= opt_bin_log_used= 0;
opt_disable_networking= opt_skip_show_db=0;
opt_skip_name_resolve= 0;
opt_ignore_builtin_innodb= 0;
@@ -7492,10 +7511,11 @@ static int mysql_init_variables(void)
mysql_home_ptr= mysql_home;
pidfile_name_ptr= pidfile_name;
log_error_file_ptr= log_error_file;
- lc_messages_dir_ptr= lc_messages_dir;
protocol_version= PROTOCOL_VERSION;
what_to_log= ~ (1L << (uint) COM_TIME);
refresh_version= 1L; /* Increments on each reload */
+ denied_connections= 0;
+ executed_events= 0;
global_query_id= thread_id= 1L;
my_atomic_rwlock_init(&global_query_id_lock);
my_atomic_rwlock_init(&thread_running_lock);
@@ -7522,6 +7542,7 @@ static int mysql_init_variables(void)
relay_log_info_file= (char*) "relay-log.info";
report_user= report_password = report_host= 0; /* TO BE DELETED */
opt_relay_logname= opt_relaylog_index_name= 0;
+ slave_retried_transactions= 0;
/* Variables in libraries */
charsets_dir= 0;
@@ -7681,7 +7702,6 @@ mysqld_get_one_option(int optid,
break;
case 'L':
strmake(lc_messages_dir, argument, sizeof(lc_messages_dir)-1);
- lc_messages_dir_ptr= lc_messages_dir;
break;
case OPT_BINLOG_FORMAT:
binlog_format_used= true;
@@ -7710,6 +7730,7 @@ mysqld_get_one_option(int optid,
break;
case (int) OPT_BIN_LOG:
opt_bin_log= test(argument != disabled_my_option);
+ opt_bin_log_used= 1;
break;
case (int) OPT_LOG_BASENAME:
{
@@ -7841,6 +7862,8 @@ mysqld_get_one_option(int optid,
#ifdef HAVE_QUERY_CACHE
query_cache_size=0;
#endif
+ sql_print_warning("The syntax '--safe-mode' is deprecated and will be "
+ "removed in a future release.");
break;
case (int) OPT_SKIP_PRIOR:
opt_specialflag|= SPECIAL_NO_PRIOR;
@@ -8280,8 +8303,30 @@ static int get_options(int *argc_ptr, char ***argv_ptr)
if (!max_long_data_size_used)
max_long_data_size= global_system_variables.max_allowed_packet;
- /* Rember if max_user_connections was 0 at startup */
+ /* Remember if max_user_connections was 0 at startup */
max_user_connections_checking= global_system_variables.max_user_connections != 0;
+
+ {
+ sys_var *max_relay_log_size_var, *max_binlog_size_var;
+ /* If max_relay_log_size is 0, then set it to max_binlog_size */
+ if (!global_system_variables.max_relay_log_size)
+ global_system_variables.max_relay_log_size= max_binlog_size;
+
+ /*
+ Fix so that DEFAULT and limit checking works with max_relay_log_size
+ (Yes, this is a hack, but it's required as the definition of
+ max_relay_log_size allows it to be set to 0).
+ */
+ max_relay_log_size_var= intern_find_sys_var("max_relay_log_size", 0);
+ max_binlog_size_var= intern_find_sys_var("max_binlog_size", 0);
+ if (max_binlog_size_var && max_relay_log_size_var)
+ {
+ max_relay_log_size_var->option.min_value=
+ max_binlog_size_var->option.min_value;
+ max_relay_log_size_var->option.def_value=
+ max_binlog_size_var->option.def_value;
+ }
+ }
return 0;
}
@@ -8687,6 +8732,7 @@ PSI_stage_info stage_sending_binlog_event_to_slave= { 0, "Sending binlog event t
PSI_stage_info stage_sending_cached_result_to_client= { 0, "sending cached result to client", 0};
PSI_stage_info stage_sending_data= { 0, "Sending data", 0};
PSI_stage_info stage_setup= { 0, "setup", 0};
+PSI_stage_info stage_show_explain= { 0, "show explain", 0};
PSI_stage_info stage_slave_has_read_all_relay_log= { 0, "Slave has read all relay log; waiting for the slave I/O thread to update it", 0};
PSI_stage_info stage_sorting= { 0, "Sorting", 0};
PSI_stage_info stage_sorting_for_group= { 0, "Sorting for group", 0};
@@ -8795,6 +8841,7 @@ PSI_stage_info *all_server_stages[]=
& stage_sending_data,
& stage_setup,
& stage_slave_has_read_all_relay_log,
+ & stage_show_explain,
& stage_sorting,
& stage_sorting_for_group,
& stage_sorting_for_order,
diff --git a/sql/mysqld.h b/sql/mysqld.h
index a5fe2fae7ed..67fa44fe7d9 100644
--- a/sql/mysqld.h
+++ b/sql/mysqld.h
@@ -96,6 +96,7 @@ extern my_bool opt_safe_user_create;
extern my_bool opt_safe_show_db, opt_local_infile, opt_myisam_use_mmap;
extern my_bool opt_slave_compressed_protocol, use_temp_pool;
extern ulong slave_exec_mode_options;
+extern ulong slave_retried_transactions;
extern ulonglong slave_type_conversions_options;
extern my_bool read_only, opt_readonly;
extern my_bool lower_case_file_system;
@@ -170,11 +171,13 @@ extern ulong max_prepared_stmt_count, prepared_stmt_count;
extern ulong open_files_limit;
extern ulonglong binlog_cache_size, binlog_stmt_cache_size;
extern ulonglong max_binlog_cache_size, max_binlog_stmt_cache_size;
-extern ulong max_binlog_size, max_relay_log_size;
+extern ulong max_binlog_size;
+extern ulong slave_max_allowed_packet;
extern ulong opt_binlog_rows_event_max_size;
extern ulong rpl_recovery_rank, thread_cache_size;
extern ulong stored_program_cache_size;
extern ulong back_log;
+extern ulong executed_events;
extern char language[FN_REFLEN];
extern "C" MYSQL_PLUGIN_IMPORT ulong server_id;
extern ulong concurrency;
@@ -197,7 +200,7 @@ extern handlerton *myisam_hton;
extern handlerton *heap_hton;
extern const char *load_default_groups[];
extern struct my_option my_long_options[];
-extern int mysqld_server_started;
+extern int mysqld_server_started, mysqld_server_initialized;
extern "C" MYSQL_PLUGIN_IMPORT int orig_argc;
extern "C" MYSQL_PLUGIN_IMPORT char **orig_argv;
extern pthread_attr_t connection_attrib;
@@ -219,14 +222,14 @@ extern pthread_key(MEM_ROOT**,THR_MALLOC);
#ifdef HAVE_PSI_INTERFACE
#ifdef HAVE_MMAP
extern PSI_mutex_key key_PAGE_lock, key_LOCK_sync, key_LOCK_active,
- key_LOCK_pool;
+ key_LOCK_pool, key_LOCK_pending_checkpoint;
#endif /* HAVE_MMAP */
#ifdef HAVE_OPENSSL
extern PSI_mutex_key key_LOCK_des_key_file;
#endif
-extern PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_prep_xids,
+extern PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_xid_list,
key_delayed_insert_mutex, key_hash_filo_lock, key_LOCK_active_mi,
key_LOCK_connection_count, key_LOCK_crypt, key_LOCK_delayed_create,
key_LOCK_delayed_insert, key_LOCK_delayed_status, key_LOCK_error_log,
@@ -257,7 +260,7 @@ extern PSI_rwlock_key key_rwlock_LOCK_grant, key_rwlock_LOCK_logger,
extern PSI_cond_key key_PAGE_cond, key_COND_active, key_COND_pool;
#endif /* HAVE_MMAP */
-extern PSI_cond_key key_BINLOG_COND_prep_xids, key_BINLOG_update_cond,
+extern PSI_cond_key key_BINLOG_COND_xid_list, key_BINLOG_update_cond,
key_COND_cache_status_changed, key_COND_manager,
key_COND_rpl_status, key_COND_server_started,
key_delayed_insert_cond, key_delayed_insert_cond_client,
@@ -359,6 +362,7 @@ extern PSI_stage_info stage_sending_cached_result_to_client;
extern PSI_stage_info stage_sending_data;
extern PSI_stage_info stage_setup;
extern PSI_stage_info stage_slave_has_read_all_relay_log;
+extern PSI_stage_info stage_show_explain;
extern PSI_stage_info stage_sorting;
extern PSI_stage_info stage_sorting_for_group;
extern PSI_stage_info stage_sorting_for_order;
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index a66a6755757..994b3f75989 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -2001,14 +2001,14 @@ int QUICK_RANGE_SELECT::init_ror_merged_scan(bool reuse_handler)
{
handler *save_file= file, *org_file;
my_bool org_key_read;
- THD *thd;
+ THD *thd= head->in_use;
DBUG_ENTER("QUICK_RANGE_SELECT::init_ror_merged_scan");
in_ror_merged_scan= 1;
if (reuse_handler)
{
DBUG_PRINT("info", ("Reusing handler 0x%lx", (long) file));
- if (init() || reset())
+ if (init())
{
DBUG_RETURN(1);
}
@@ -2023,7 +2023,6 @@ int QUICK_RANGE_SELECT::init_ror_merged_scan(bool reuse_handler)
DBUG_RETURN(0);
}
- thd= head->in_use;
if (!(file= head->file->clone(head->s->normalized_path.str, thd->mem_root)))
{
/*
@@ -2043,7 +2042,7 @@ int QUICK_RANGE_SELECT::init_ror_merged_scan(bool reuse_handler)
if (file->ha_external_lock(thd, F_RDLCK))
goto failure;
- if (init() || reset())
+ if (init())
{
file->ha_external_lock(thd, F_UNLCK);
file->ha_close();
@@ -2090,7 +2089,18 @@ end:
head->key_read= org_key_read;
bitmap_copy(&column_bitmap, head->read_set);
head->column_bitmaps_set(&column_bitmap, &column_bitmap);
-
+
+ if (reset())
+ {
+ if (!reuse_handler)
+ {
+ file->ha_external_lock(thd, F_UNLCK);
+ file->ha_close();
+ goto failure;
+ }
+ else
+ DBUG_RETURN(1);
+ }
DBUG_RETURN(0);
failure:
@@ -11793,9 +11803,10 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time)
have_min= TRUE;
else if (min_max_item->sum_func() == Item_sum::MAX_FUNC)
have_max= TRUE;
- else if (min_max_item->sum_func() == Item_sum::COUNT_DISTINCT_FUNC ||
- min_max_item->sum_func() == Item_sum::SUM_DISTINCT_FUNC ||
- min_max_item->sum_func() == Item_sum::AVG_DISTINCT_FUNC)
+ else if (is_agg_distinct &&
+ (min_max_item->sum_func() == Item_sum::COUNT_DISTINCT_FUNC ||
+ min_max_item->sum_func() == Item_sum::SUM_DISTINCT_FUNC ||
+ min_max_item->sum_func() == Item_sum::AVG_DISTINCT_FUNC))
continue;
else
DBUG_RETURN(NULL);
diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc
index c5b38fd4b1d..da7d3dff74d 100644
--- a/sql/opt_subselect.cc
+++ b/sql/opt_subselect.cc
@@ -160,18 +160,18 @@
3.2.1 Non-merged semi-joins and join optimization
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
For join optimization purposes, non-merged semi-join nests are similar to
- base tables - they've got one JOIN_TAB, which can be accessed with one of
- two methods:
+ base tables. Each such nest is represented by one one JOIN_TAB, which has
+ two possible access strategies:
- full table scan (representing SJ-Materialization-Scan strategy)
- eq_ref-like table lookup (representing SJ-Materialization-Lookup)
Unlike regular base tables, non-merged semi-joins have:
- non-zero JOIN_TAB::startup_cost, and
- join_tab->table->is_filled_at_execution()==TRUE, which means one
- cannot do const table detection or range analysis or other table data-
- dependent inferences
- // instead, get_delayed_table_estimates() runs optimization on the nest so that
- // we get an idea about temptable size
+ cannot do const table detection, range analysis or other dataset-dependent
+ optimizations.
+ Instead, get_delayed_table_estimates() will run optimization for the
+ subquery and produce an E(materialized table size).
3.2.2 Merged semi-joins and join optimization
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -1657,6 +1657,7 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred)
parent_lex->ftfunc_list->push_front(ifm);
}
+ parent_lex->have_merged_subqueries= TRUE;
DBUG_RETURN(FALSE);
}
@@ -1767,6 +1768,8 @@ static bool convert_subq_to_jtbm(JOIN *parent_join,
create_subquery_temptable_name(tbl_alias, hash_sj_engine->materialize_join->
select_lex->select_number);
jtbm->alias= tbl_alias;
+
+ parent_lex->have_merged_subqueries= TRUE;
#if 0
/* Inject sj_on_expr into the parent's WHERE or ON */
if (emb_tbl_nest)
diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc
index cbec039b3e4..fa3a07b72c5 100644
--- a/sql/opt_sum.cc
+++ b/sql/opt_sum.cc
@@ -791,7 +791,7 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo,
{
/* Update endpoints for MAX/MIN, see function comment. */
Item *value= args[between && max_fl ? 2 : 1];
- store_val_in_field(part->field, value, CHECK_FIELD_IGNORE);
+ value->save_in_field_no_warnings(part->field, 1);
if (part->null_bit)
*key_ptr++= (uchar) test(part->field->is_null());
part->field->get_key_image(key_ptr, part->length, Field::itRAW);
diff --git a/sql/protocol.cc b/sql/protocol.cc
index 10c85939986..3af7dc88b88 100644
--- a/sql/protocol.cc
+++ b/sql/protocol.cc
@@ -1,5 +1,5 @@
-/* Copyright (c) 2000, 2011, Oracle and/or its affiliates.
- Copyright (c) 2008, 2011, Monty Program Ab
+/* Copyright (c) 2000, 2012, Oracle and/or its affiliates.
+ Copyright (c) 2008, 2012, 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
@@ -775,6 +775,8 @@ bool Protocol::send_result_set_metadata(List<Item> *list, uint flags)
/* Store fixed length fields */
pos= (char*) local_packet->ptr()+local_packet->length();
*pos++= 12; // Length of packed fields
+ /* inject a NULL to test the client */
+ DBUG_EXECUTE_IF("poison_rs_fields", pos[-1]= 0xfb;);
if (item->charset_for_protocol() == &my_charset_bin || thd_charset == NULL)
{
/* No conversion */
diff --git a/sql/protocol.h b/sql/protocol.h
index d9bc48ce45d..1c0a28560bd 100644
--- a/sql/protocol.h
+++ b/sql/protocol.h
@@ -35,6 +35,7 @@ class Protocol
protected:
THD *thd;
String *packet;
+ /* Used by net_store_data() for charset conversions */
String *convert;
uint field_pos;
#ifndef DBUG_OFF
@@ -49,6 +50,10 @@ protected:
MYSQL_FIELD *next_mysql_field;
MEM_ROOT *alloc;
#endif
+ /*
+ The following two are low-level functions that are invoked from
+ higher-level store_xxx() funcs. The data is stored into this->packet.
+ */
bool net_store_data(const uchar *from, size_t length,
CHARSET_INFO *fromcs, CHARSET_INFO *tocs);
bool store_string_aux(const char *from, size_t length,
diff --git a/sql/repl_failsafe.cc b/sql/repl_failsafe.cc
index 4eac3dcd4f5..86f19f1b28e 100644
--- a/sql/repl_failsafe.cc
+++ b/sql/repl_failsafe.cc
@@ -35,7 +35,6 @@
#include "rpl_mi.h"
#include "rpl_filter.h"
#include "log_event.h"
-#include "sql_db.h" // mysql_create_db
#include <mysql.h>
#define SLAVE_LIST_CHUNK 128
diff --git a/sql/rpl_mi.cc b/sql/rpl_mi.cc
index a765f851812..2ca700a123a 100644
--- a/sql/rpl_mi.cc
+++ b/sql/rpl_mi.cc
@@ -20,6 +20,8 @@
#include "unireg.h" // REQUIRED by other includes
#include "rpl_mi.h"
#include "slave.h" // SLAVE_MAX_HEARTBEAT_PERIOD
+#include "strfunc.h"
+#include "sql_repl.h"
#ifdef HAVE_REPLICATION
@@ -27,7 +29,8 @@
static void init_master_log_pos(Master_info* mi);
-Master_info::Master_info(bool is_slave_recovery)
+Master_info::Master_info(LEX_STRING *connection_name_arg,
+ bool is_slave_recovery)
:Slave_reporting_capability("I/O"),
ssl(0), ssl_verify_server_cert(1), fd(-1), io_thd(0),
rli(is_slave_recovery), port(MYSQL_PORT),
@@ -41,6 +44,24 @@ Master_info::Master_info(bool is_slave_recovery)
ssl_cipher[0]= 0; ssl_key[0]= 0;
ssl_crl[0]= 0; ssl_crlpath[0]= 0;
+ /*
+ Store connection name and lower case connection name
+ It's safe to ignore any OMM errors as this is checked by error()
+ */
+ connection_name.length= cmp_connection_name.length=
+ connection_name_arg->length;
+ if ((connection_name.str= (char*) my_malloc(connection_name_arg->length*2+2,
+ MYF(MY_WME))))
+ {
+ cmp_connection_name.str= (connection_name.str +
+ connection_name_arg->length+1);
+ strmake(connection_name.str, connection_name_arg->str,
+ connection_name.length);
+ memcpy(cmp_connection_name.str, connection_name_arg->str,
+ connection_name.length+1);
+ my_casedn_str(system_charset_info, cmp_connection_name.str);
+ }
+
my_init_dynamic_array(&ignore_server_ids, sizeof(::server_id), 16, 16);
bzero((char*) &file, sizeof(file));
mysql_mutex_init(key_master_info_run_lock, &run_lock, MY_MUTEX_INIT_FAST);
@@ -56,6 +77,7 @@ Master_info::Master_info(bool is_slave_recovery)
Master_info::~Master_info()
{
+ my_free(connection_name.str);
delete_dynamic(&ignore_server_ids);
mysql_mutex_destroy(&run_lock);
mysql_mutex_destroy(&data_lock);
@@ -441,7 +463,7 @@ file '%s')", fname);
mi->master_log_name,
(ulong) mi->master_log_pos));
- mi->rli.mi = mi;
+ mi->rli.mi= mi;
if (init_relay_log_info(&mi->rli, slave_info_fname))
goto err;
@@ -596,5 +618,595 @@ void end_master_info(Master_info* mi)
DBUG_VOID_RETURN;
}
+/* Multi-Master By P.Linux */
+uchar *get_key_master_info(Master_info *mi, size_t *length,
+ my_bool not_used __attribute__((unused)))
+{
+ /* Return lower case name */
+ *length= mi->cmp_connection_name.length;
+ return (uchar*) mi->cmp_connection_name.str;
+}
+
+void free_key_master_info(Master_info *mi)
+{
+ DBUG_ENTER("free_key_master_info");
+ terminate_slave_threads(mi,SLAVE_FORCE_ALL);
+ end_master_info(mi);
+ delete mi;
+ DBUG_VOID_RETURN;
+}
+
+/**
+ Check if connection name for master_info is valid.
+
+ It's valid if it's a valid system name of length less than
+ MAX_CONNECTION_NAME.
+
+ @return
+ 0 ok
+ 1 error
+*/
+
+bool check_master_connection_name(LEX_STRING *name)
+{
+ if (name->length >= MAX_CONNECTION_NAME)
+ return 1;
+ return 0;
+}
+
+
+/**
+ Create a log file with a given suffix.
+
+ @param
+ res_file_name Store result here
+ length Length of res_file_name buffer
+ info_file Original file name (prefix)
+ append 1 if we should add suffix last (not before ext)
+ suffix Suffix
+
+ @note
+ The suffix is added before the extension of the file name prefixed with '-'.
+ The suffix is also converted to lower case and we transform
+ all not safe character, as we do with MySQL table names.
+
+ If suffix is an empty string, then we don't add any suffix.
+ This is to allow one to use this function also to generate old
+ file names without a prefix.
+*/
+
+void create_logfile_name_with_suffix(char *res_file_name, uint length,
+ const char *info_file, bool append,
+ LEX_STRING *suffix)
+{
+ char buff[MAX_CONNECTION_NAME+1], res[MAX_CONNECTION_NAME+1], *p;
+
+ p= strmake(res_file_name, info_file, length);
+ /* If not empty suffix and there is place left for some part of the suffix */
+ if (suffix->length != 0 && p <= res_file_name + length -1)
+ {
+ const char *info_file_end= info_file + (p - res_file_name);
+ const char *ext= append ? info_file_end : fn_ext2(info_file);
+ size_t res_length, ext_pos;
+ uint errors;
+
+ /* Create null terminated string */
+ strmake(buff, suffix->str, suffix->length);
+ /* Convert to lower case */
+ my_casedn_str(system_charset_info, buff);
+ /* Convert to characters usable in a file name */
+ res_length= strconvert(system_charset_info, buff,
+ &my_charset_filename, res, sizeof(res), &errors);
+
+ ext_pos= (size_t) (ext - info_file);
+ length-= (suffix->length - ext_pos); /* Leave place for extension */
+ p= res_file_name + ext_pos;
+ *p++= '-'; /* Add separator */
+ p= strmake(p, res, min((size_t) (length - (p - res_file_name)),
+ res_length));
+ /* Add back extension. We have checked above that there is space for it */
+ strmov(p, ext);
+ }
+}
+
+
+Master_info_index::Master_info_index()
+{
+ size_t filename_length, dir_length;
+ /*
+ Create the Master_info index file by prepending 'multi-' before
+ the master_info_file file name.
+ */
+ fn_format(index_file_name, master_info_file, mysql_data_home,
+ "", MY_UNPACK_FILENAME);
+ filename_length= strlen(index_file_name) + 1; /* Count 0 byte */
+ dir_length= dirname_length(index_file_name);
+ bmove_upp((uchar*) index_file_name + filename_length + 6,
+ (uchar*) index_file_name + filename_length,
+ filename_length - dir_length);
+ memcpy(index_file_name + dir_length, "multi-", 6);
+
+ bzero((char*) &index_file, sizeof(index_file));
+}
+
+Master_info_index::~Master_info_index()
+{
+ /* This will close connection for all objects in the cache */
+ my_hash_free(&master_info_hash);
+ end_io_cache(&index_file);
+ if (index_file.file > 0)
+ my_close(index_file.file, MYF(MY_WME));
+}
+
+
+/* Load All Master_info from master.info.index File
+ * RETURN:
+ * 0 - All Success
+ * 1 - All Fail
+ * 2 - Some Success, Some Fail
+ */
+
+bool Master_info_index::init_all_master_info()
+{
+ int thread_mask;
+ int err_num= 0, succ_num= 0; // The number of success read Master_info
+ char sign[MAX_CONNECTION_NAME];
+ File index_file_nr;
+ DBUG_ENTER("init_all_master_info");
+
+ if ((index_file_nr= my_open(index_file_name,
+ O_RDWR | O_CREAT | O_BINARY ,
+ MYF(MY_WME | ME_NOREFRESH))) < 0 ||
+ my_sync(index_file_nr, MYF(MY_WME)) ||
+ init_io_cache(&index_file, index_file_nr,
+ IO_SIZE, READ_CACHE,
+ my_seek(index_file_nr,0L,MY_SEEK_END,MYF(0)),
+ 0, MYF(MY_WME | MY_WAIT_IF_FULL)))
+ {
+ if (index_file_nr >= 0)
+ my_close(index_file_nr,MYF(0));
+
+ sql_print_error("Creation of Master_info index file '%s' failed",
+ index_file_name);
+ DBUG_RETURN(1);
+ }
+
+ /* Initialize Master_info Hash Table */
+ if (my_hash_init(&master_info_hash, system_charset_info,
+ MAX_REPLICATION_THREAD, 0, 0,
+ (my_hash_get_key) get_key_master_info,
+ (my_hash_free_key)free_key_master_info, HASH_UNIQUE))
+ {
+ sql_print_error("Initializing Master_info hash table failed");
+ DBUG_RETURN(1);
+ }
+
+ reinit_io_cache(&index_file, READ_CACHE, 0L,0,0);
+ while (!init_strvar_from_file(sign, sizeof(sign),
+ &index_file, NULL))
+ {
+ LEX_STRING connection_name;
+ Master_info *mi;
+ char buf_master_info_file[FN_REFLEN];
+ char buf_relay_log_info_file[FN_REFLEN];
+
+ connection_name.str= sign;
+ connection_name.length= strlen(sign);
+ if (!(mi= new Master_info(&connection_name, relay_log_recovery)) ||
+ mi->error())
+ {
+ delete mi;
+ DBUG_RETURN(1);
+ }
+
+ lock_slave_threads(mi);
+ init_thread_mask(&thread_mask,mi,0 /*not inverse*/);
+
+ create_logfile_name_with_suffix(buf_master_info_file, sizeof(buf_master_info_file),
+ master_info_file, 0, &connection_name);
+ create_logfile_name_with_suffix(buf_relay_log_info_file,
+ sizeof(buf_relay_log_info_file),
+ relay_log_info_file, 0, &connection_name);
+ if (global_system_variables.log_warnings > 1)
+ sql_print_information("Reading Master_info: '%s' Relay_info:'%s'",
+ buf_master_info_file, buf_relay_log_info_file);
+
+ if (init_master_info(mi, buf_master_info_file, buf_relay_log_info_file,
+ 0, thread_mask))
+ {
+ err_num++;
+ sql_print_error("Initialized Master_info from '%s' failed",
+ buf_master_info_file);
+ if (!master_info_index->get_master_info(&connection_name,
+ MYSQL_ERROR::WARN_LEVEL_NOTE))
+ {
+ /* Master_info is not in HASH; Add it */
+ if (master_info_index->add_master_info(mi, FALSE))
+ return 1;
+ succ_num++;
+ unlock_slave_threads(mi);
+ }
+ else
+ {
+ /* Master_info already in HASH */
+ sql_print_error(ER(ER_CONNECTION_ALREADY_EXISTS),
+ (int) connection_name.length, connection_name.str);
+ unlock_slave_threads(mi);
+ delete mi;
+ }
+ continue;
+ }
+ else
+ {
+ /* Initialization of Master_info succeded. Add it to HASH */
+ if (global_system_variables.log_warnings > 1)
+ sql_print_information("Initialized Master_info from '%s'",
+ buf_master_info_file);
+ if (master_info_index->get_master_info(&connection_name,
+ MYSQL_ERROR::WARN_LEVEL_NOTE))
+ {
+ /* Master_info was already registered */
+ sql_print_error(ER(ER_CONNECTION_ALREADY_EXISTS),
+ (int) connection_name.length, connection_name.str);
+ unlock_slave_threads(mi);
+ delete mi;
+ continue;
+ }
+
+ /* Master_info was not registered; add it */
+ if (master_info_index->add_master_info(mi, FALSE))
+ return 1;
+ succ_num++;
+ unlock_slave_threads(mi);
+
+ if (!opt_skip_slave_start)
+ {
+ if (start_slave_threads(1 /* need mutex */,
+ 0 /* no wait for start*/,
+ mi,
+ buf_master_info_file,
+ buf_relay_log_info_file,
+ SLAVE_IO | SLAVE_SQL))
+ {
+ sql_print_error("Failed to create slave threads for connection '%.*s'",
+ (int) connection_name.length,
+ connection_name.str);
+ continue;
+ }
+ if (global_system_variables.log_warnings)
+ sql_print_information("Started replication for '%.*s'",
+ (int) connection_name.length,
+ connection_name.str);
+ }
+ }
+ }
+
+ if (!err_num) // No Error on read Master_info
+ {
+ if (global_system_variables.log_warnings > 1)
+ sql_print_information("Reading of all Master_info entries succeded");
+ DBUG_RETURN(0);
+ }
+ else if (succ_num) // Have some Error and some Success
+ {
+ sql_print_warning("Reading of some Master_info entries failed");
+ DBUG_RETURN(2);
+ }
+ else // All failed
+ {
+ sql_print_error("Reading of all Master_info entries failed!");
+ DBUG_RETURN(1);
+ }
+}
+
+
+/* Write new master.info to master.info.index File */
+bool Master_info_index::write_master_name_to_index_file(LEX_STRING *name,
+ bool do_sync)
+{
+ DBUG_ASSERT(my_b_inited(&index_file) != 0);
+ DBUG_ENTER("write_master_name_to_index_file");
+
+ /* Don't write default slave to master_info.index */
+ if (name->length == 0)
+ DBUG_RETURN(0);
+
+ reinit_io_cache(&index_file, WRITE_CACHE,
+ my_b_filelength(&index_file), 0, 0);
+
+ if (my_b_write(&index_file, (uchar*) name->str, name->length) ||
+ my_b_write(&index_file, (uchar*) "\n", 1) ||
+ flush_io_cache(&index_file) ||
+ (do_sync && my_sync(index_file.file, MYF(MY_WME))))
+ {
+ sql_print_error("Write of new Master_info for '%.*s' to index file failed",
+ (int) name->length, name->str);
+ DBUG_RETURN(1);
+ }
+
+ DBUG_RETURN(0);
+}
+
+
+/**
+ Get Master_info for a connection
+
+ @param
+ connection_name Connection name
+ warning WARN_LEVEL_NOTE -> Don't print anything
+ WARN_LEVEL_WARN -> Issue warning if not exists
+ WARN_LEVEL_ERROR-> Issue error if not exists
+*/
+
+Master_info *
+Master_info_index::get_master_info(LEX_STRING *connection_name,
+ MYSQL_ERROR::enum_warning_level warning)
+{
+ Master_info *mi;
+ char buff[MAX_CONNECTION_NAME+1], *res;
+ uint buff_length;
+ DBUG_ENTER("get_master_info");
+ DBUG_PRINT("enter",
+ ("connection_name: '%.*s'", (int) connection_name->length,
+ connection_name->str));
+
+ /* Make name lower case for comparison */
+ res= strmake(buff, connection_name->str, connection_name->length);
+ my_casedn_str(system_charset_info, buff);
+ buff_length= (size_t) (res-buff);
+
+ mi= (Master_info*) my_hash_search(&master_info_hash,
+ (uchar*) buff, buff_length);
+ if (!mi && warning != MYSQL_ERROR::WARN_LEVEL_NOTE)
+ {
+ my_error(WARN_NO_MASTER_INFO,
+ MYF(warning == MYSQL_ERROR::WARN_LEVEL_WARN ? ME_JUST_WARNING :
+ 0),
+ (int) connection_name->length,
+ connection_name->str);
+ }
+ DBUG_RETURN(mi);
+}
+
+
+/* Check Master_host & Master_port is duplicated or not */
+bool Master_info_index::check_duplicate_master_info(LEX_STRING *name_arg,
+ const char *host,
+ uint port)
+{
+ Master_info *mi;
+ DBUG_ENTER("check_duplicate_master_info");
+
+ /* Get full host and port name */
+ if ((mi= master_info_index->get_master_info(name_arg,
+ MYSQL_ERROR::WARN_LEVEL_NOTE)))
+ {
+ if (!host)
+ host= mi->host;
+ if (!port)
+ port= mi->port;
+ }
+ if (!host || !port)
+ DBUG_RETURN(FALSE); // Not comparable yet
+
+ for (uint i= 0; i < master_info_hash.records; ++i)
+ {
+ Master_info *tmp_mi;
+ tmp_mi= (Master_info *) my_hash_element(&master_info_hash, i);
+ if (tmp_mi == mi)
+ continue; // Current connection
+ if (!strcasecmp(host, tmp_mi->host) && port == tmp_mi->port)
+ {
+ my_error(ER_CONNECTION_ALREADY_EXISTS, MYF(0),
+ (int) name_arg->length,
+ name_arg->str,
+ (int) tmp_mi->connection_name.length,
+ tmp_mi->connection_name.str);
+ DBUG_RETURN(TRUE);
+ }
+ }
+ DBUG_RETURN(FALSE);
+}
+
+
+/* Add a Master_info class to Hash Table */
+bool Master_info_index::add_master_info(Master_info *mi, bool write_to_file)
+{
+ if (!my_hash_insert(&master_info_hash, (uchar*) mi))
+ {
+ if (global_system_variables.log_warnings > 1)
+ sql_print_information("Added new Master_info '%.*s' to hash table",
+ (int) mi->connection_name.length,
+ mi->connection_name.str);
+ if (write_to_file)
+ return write_master_name_to_index_file(&mi->connection_name, 1);
+ return FALSE;
+ }
+
+ /* Impossible error (EOM) ? */
+ sql_print_error("Adding new entry '%.*s' to master_info failed",
+ (int) mi->connection_name.length,
+ mi->connection_name.str);
+ return TRUE;
+}
+
+
+/**
+ Remove a Master_info class From Hash Table
+
+ TODO: Change this to use my_rename() to make the file name creation
+ atomic
+*/
+
+bool Master_info_index::remove_master_info(LEX_STRING *name)
+{
+ Master_info* mi;
+ DBUG_ENTER("remove_master_info");
+
+ if ((mi= get_master_info(name, MYSQL_ERROR::WARN_LEVEL_WARN)))
+ {
+ // Delete Master_info and rewrite others to file
+ if (!my_hash_delete(&master_info_hash, (uchar*) mi))
+ {
+ File index_file_nr;
+
+ // Close IO_CACHE and FILE handler fisrt
+ end_io_cache(&index_file);
+ my_close(index_file.file, MYF(MY_WME));
+
+ // Reopen File and truncate it
+ if ((index_file_nr= my_open(index_file_name,
+ O_RDWR | O_CREAT | O_TRUNC | O_BINARY ,
+ MYF(MY_WME))) < 0 ||
+ init_io_cache(&index_file, index_file_nr,
+ IO_SIZE, WRITE_CACHE,
+ my_seek(index_file_nr,0L,MY_SEEK_END,MYF(0)),
+ 0, MYF(MY_WME | MY_WAIT_IF_FULL)))
+ {
+ int error= my_errno;
+ if (index_file_nr >= 0)
+ my_close(index_file_nr,MYF(0));
+
+ sql_print_error("Create of Master Info Index file '%s' failed with "
+ "error: %M",
+ index_file_name, error);
+ DBUG_RETURN(TRUE);
+ }
+
+ // Rewrite Master_info.index
+ for (uint i= 0; i< master_info_hash.records; ++i)
+ {
+ Master_info *tmp_mi;
+ tmp_mi= (Master_info *) my_hash_element(&master_info_hash, i);
+ write_master_name_to_index_file(&tmp_mi->connection_name, 0);
+ }
+ my_sync(index_file_nr, MYF(MY_WME));
+ }
+ }
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
+ Master_info_index::give_error_if_slave_running()
+
+ @return
+ TRUE If some slave is running. An error is printed
+ FALSE No slave is running
+*/
+
+bool Master_info_index::give_error_if_slave_running()
+{
+ DBUG_ENTER("warn_if_slave_running");
+ mysql_mutex_assert_owner(&LOCK_active_mi);
+
+ for (uint i= 0; i< master_info_hash.records; ++i)
+ {
+ Master_info *mi;
+ mi= (Master_info *) my_hash_element(&master_info_hash, i);
+ if (mi->rli.slave_running != MYSQL_SLAVE_NOT_RUN)
+ {
+ my_error(ER_SLAVE_MUST_STOP, MYF(0), (int) mi->connection_name.length,
+ mi->connection_name.str);
+ DBUG_RETURN(TRUE);
+ }
+ }
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
+ Master_info_index::start_all_slaves()
+
+ Start all slaves that was not running.
+
+ @return
+ TRUE Error
+ FALSE Everything ok.
+*/
+
+bool Master_info_index::start_all_slaves(THD *thd)
+{
+ bool result= FALSE;
+ DBUG_ENTER("warn_if_slave_running");
+ mysql_mutex_assert_owner(&LOCK_active_mi);
+
+ for (uint i= 0; i< master_info_hash.records; ++i)
+ {
+ int error;
+ Master_info *mi;
+ mi= (Master_info *) my_hash_element(&master_info_hash, i);
+
+ /*
+ Try to start all slaves that are configured (host is defined)
+ and are not already running
+ */
+ if ((mi->slave_running != MYSQL_SLAVE_RUN_CONNECT ||
+ !mi->rli.slave_running) && *mi->host)
+ {
+ if ((error= start_slave(thd, mi, 1)))
+ {
+ my_error(ER_CANT_START_STOP_SLAVE, MYF(0),
+ "START",
+ (int) mi->connection_name.length,
+ mi->connection_name.str);
+ result= 1;
+ if (error < 0) // fatal error
+ break;
+ }
+ else
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ ER_SLAVE_STARTED, ER(ER_SLAVE_STARTED),
+ (int) mi->connection_name.length,
+ mi->connection_name.str);
+ }
+ }
+ DBUG_RETURN(result);
+}
+
+
+/**
+ Master_info_index::stop_all_slaves()
+
+ Start all slaves that was not running.
+
+ @return
+ TRUE Error
+ FALSE Everything ok.
+*/
+
+bool Master_info_index::stop_all_slaves(THD *thd)
+{
+ bool result= FALSE;
+ DBUG_ENTER("warn_if_slave_running");
+ mysql_mutex_assert_owner(&LOCK_active_mi);
+
+ for (uint i= 0; i< master_info_hash.records; ++i)
+ {
+ int error;
+ Master_info *mi;
+ mi= (Master_info *) my_hash_element(&master_info_hash, i);
+ if ((mi->slave_running != MYSQL_SLAVE_NOT_RUN ||
+ mi->rli.slave_running))
+ {
+ if ((error= stop_slave(thd, mi, 1)))
+ {
+ my_error(ER_CANT_START_STOP_SLAVE, MYF(0),
+ "STOP",
+ (int) mi->connection_name.length,
+ mi->connection_name.str);
+ result= 1;
+ if (error < 0) // Fatal error
+ break;
+ }
+ else
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ ER_SLAVE_STOPPED, ER(ER_SLAVE_STOPPED),
+ (int) mi->connection_name.length,
+ mi->connection_name.str);
+ }
+ }
+ DBUG_RETURN(result);
+}
#endif /* HAVE_REPLICATION */
diff --git a/sql/rpl_mi.h b/sql/rpl_mi.h
index 462d0cfe1ed..c91699662c8 100644
--- a/sql/rpl_mi.h
+++ b/sql/rpl_mi.h
@@ -59,16 +59,23 @@ typedef struct st_mysql MYSQL;
class Master_info : public Slave_reporting_capability
{
public:
- Master_info(bool is_slave_recovery);
+ Master_info(LEX_STRING *connection_name, bool is_slave_recovery);
~Master_info();
bool shall_ignore_server_id(ulong s_id);
void clear_in_memory_info(bool all);
+ bool error()
+ {
+ /* If malloc() in initialization failed */
+ return connection_name.str == 0;
+ }
/* the variables below are needed because we can change masters on the fly */
- char master_log_name[FN_REFLEN];
+ char master_log_name[FN_REFLEN+6]; /* Room for multi-*/
char host[HOSTNAME_LENGTH+1];
char user[USERNAME_LENGTH+1];
char password[MAX_PASSWORD_LENGTH+1];
+ LEX_STRING connection_name; /* User supplied connection name */
+ LEX_STRING cmp_connection_name; /* Connection name in lower case */
bool ssl; // enables use of SSL connection if true
char ssl_ca[FN_REFLEN], ssl_capath[FN_REFLEN], ssl_cert[FN_REFLEN];
char ssl_cipher[FN_REFLEN], ssl_key[FN_REFLEN];
@@ -131,5 +138,48 @@ int flush_master_info(Master_info* mi,
bool need_lock_relay_log);
int change_master_server_id_cmp(ulong *id1, ulong *id2);
+/*
+ Multi master are handled trough this struct.
+ Changes to this needs to be protected by LOCK_active_mi;
+*/
+
+class Master_info_index
+{
+private:
+ IO_CACHE index_file;
+ char index_file_name[FN_REFLEN];
+
+public:
+ Master_info_index();
+ ~Master_info_index();
+
+ HASH master_info_hash;
+
+ bool init_all_master_info();
+ bool write_master_name_to_index_file(LEX_STRING *connection_name,
+ bool do_sync);
+
+ bool check_duplicate_master_info(LEX_STRING *connection_name,
+ const char *host, uint port);
+ bool add_master_info(Master_info *mi, bool write_to_file);
+ bool remove_master_info(LEX_STRING *connection_name);
+ Master_info *get_master_info(LEX_STRING *connection_name,
+ MYSQL_ERROR::enum_warning_level warning);
+ bool give_error_if_slave_running();
+ bool start_all_slaves(THD *thd);
+ bool stop_all_slaves(THD *thd);
+};
+
+bool check_master_connection_name(LEX_STRING *name);
+void create_logfile_name_with_suffix(char *res_file_name, uint length,
+ const char *info_file,
+ bool append,
+ LEX_STRING *suffix);
+
+uchar *get_key_master_info(Master_info *mi, size_t *length,
+ my_bool not_used __attribute__((unused)));
+void free_key_master_info(Master_info *mi);
+
+
#endif /* HAVE_REPLICATION */
#endif /* RPL_MI_H */
diff --git a/sql/rpl_record.cc b/sql/rpl_record.cc
index a49f10db440..99bf8a82004 100644
--- a/sql/rpl_record.cc
+++ b/sql/rpl_record.cc
@@ -314,7 +314,7 @@ unpack_row(Relay_log_info const *rli,
if (!pack_ptr)
{
rli->report(ERROR_LEVEL, ER_SLAVE_CORRUPT_EVENT,
- "Could not read field `%s` of table `%s`.`%s`",
+ "Could not read field '%s' of table '%s.%s'",
f->field_name, table->s->db.str,
table->s->table_name.str);
DBUG_RETURN(ER_SLAVE_CORRUPT_EVENT);
diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc
index d0d190d64dc..8c01e979226 100644
--- a/sql/rpl_rli.cc
+++ b/sql/rpl_rli.cc
@@ -51,7 +51,7 @@ Relay_log_info::Relay_log_info(bool is_slave_recovery)
last_master_timestamp(0), slave_skip_counter(0),
abort_pos_wait(0), slave_run_id(0), sql_thd(0),
inited(0), abort_slave(0), slave_running(0), until_condition(UNTIL_NONE),
- until_log_pos(0), retried_trans(0),
+ until_log_pos(0), retried_trans(0), executed_entries(0),
tables_to_lock(0), tables_to_lock_count(0),
last_event_start_time(0), deferred_events(NULL),m_flags(0),
row_stmt_start_timestamp(0), long_find_row_note_printed(false),
@@ -70,6 +70,7 @@ Relay_log_info::Relay_log_info(bool is_slave_recovery)
group_relay_log_name[0]= event_relay_log_name[0]=
group_master_log_name[0]= 0;
until_log_name[0]= ign_master_log_name_end[0]= 0;
+ max_relay_log_size= global_system_variables.max_relay_log_size;
bzero((char*) &info_file, sizeof(info_file));
bzero((char*) &cache_buf, sizeof(cache_buf));
cached_charset_invalidate();
@@ -150,15 +151,6 @@ int init_relay_log_info(Relay_log_info* rli,
event, in flush_master_info(mi, 1, ?).
*/
- /*
- For the maximum log size, we choose max_relay_log_size if it is
- non-zero, max_binlog_size otherwise. If later the user does SET
- GLOBAL on one of these variables, fix_max_binlog_size and
- fix_max_relay_log_size will reconsider the choice (for example
- if the user changes max_relay_log_size to zero, we have to
- switch to using max_binlog_size for the relay log) and update
- rli->relay_log.max_size (and mysql_bin_log.max_size).
- */
{
/* Reports an error and returns, if the --relay-log's path
is a directory.*/
@@ -207,19 +199,36 @@ a file name for --relay-log-index option", opt_relaylog_index_name);
name_warning_sent= 1;
}
+ /* For multimaster, add connection name to relay log filenames */
+ Master_info* mi= rli->mi;
+ char buf_relay_logname[FN_REFLEN], buf_relaylog_index_name_buff[FN_REFLEN];
+ char *buf_relaylog_index_name= opt_relaylog_index_name;
+
+ create_logfile_name_with_suffix(buf_relay_logname, sizeof(buf_relay_logname),
+ ln, 1, &mi->connection_name);
+ ln= buf_relay_logname;
+
+ if (opt_relaylog_index_name)
+ {
+ buf_relaylog_index_name= buf_relaylog_index_name_buff;
+ create_logfile_name_with_suffix(buf_relaylog_index_name_buff,
+ sizeof(buf_relaylog_index_name_buff),
+ opt_relaylog_index_name, 0,
+ &mi->connection_name);
+ }
+
rli->relay_log.is_relay_log= TRUE;
/*
note, that if open() fails, we'll still have index file open
but a destructor will take care of that
*/
- if (rli->relay_log.open_index_file(opt_relaylog_index_name, ln, TRUE) ||
- rli->relay_log.open(ln, LOG_BIN, 0, SEQ_READ_APPEND, 0,
- (max_relay_log_size ? max_relay_log_size :
- max_binlog_size), 1, TRUE))
+ if (rli->relay_log.open_index_file(buf_relaylog_index_name, ln, TRUE) ||
+ rli->relay_log.open(ln, LOG_BIN, 0, SEQ_READ_APPEND,
+ mi->rli.max_relay_log_size, 1, TRUE))
{
mysql_mutex_unlock(&rli->data_lock);
- sql_print_error("Failed in open_log() called from init_relay_log_info()");
+ sql_print_error("Failed when trying to open logs for '%s' in init_relay_log_info(). Error: %M", ln, my_errno);
DBUG_RETURN(1);
}
}
@@ -998,28 +1007,37 @@ int purge_relay_logs(Relay_log_info* rli, THD *thd, bool just_reset,
rli->cur_log_fd= -1;
}
- if (rli->relay_log.reset_logs(thd))
+ if (rli->relay_log.reset_logs(thd, !just_reset))
{
*errmsg = "Failed during log reset";
error=1;
goto err;
}
- /* Save name of used relay log file */
- strmake(rli->group_relay_log_name, rli->relay_log.get_log_fname(),
- sizeof(rli->group_relay_log_name)-1);
- strmake(rli->event_relay_log_name, rli->relay_log.get_log_fname(),
- sizeof(rli->event_relay_log_name)-1);
- rli->group_relay_log_pos= rli->event_relay_log_pos= BIN_LOG_HEADER_SIZE;
- if (count_relay_log_space(rli))
- {
- *errmsg= "Error counting relay log space";
- error=1;
- goto err;
- }
if (!just_reset)
+ {
+ /* Save name of used relay log file */
+ strmake(rli->group_relay_log_name, rli->relay_log.get_log_fname(),
+ sizeof(rli->group_relay_log_name)-1);
+ strmake(rli->event_relay_log_name, rli->relay_log.get_log_fname(),
+ sizeof(rli->event_relay_log_name)-1);
+ rli->group_relay_log_pos= rli->event_relay_log_pos= BIN_LOG_HEADER_SIZE;
+ rli->log_space_total= 0;
+
+ if (count_relay_log_space(rli))
+ {
+ *errmsg= "Error counting relay log space";
+ error=1;
+ goto err;
+ }
error= init_relay_log_pos(rli, rli->group_relay_log_name,
rli->group_relay_log_pos,
0 /* do not need data lock */, errmsg, 0);
+ }
+ else
+ {
+ /* Ensure relay log names are not used */
+ rli->group_relay_log_name[0]= rli->event_relay_log_name[0]= 0;
+ }
err:
#ifndef DBUG_OFF
diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h
index b989283deb4..6144d37026b 100644
--- a/sql/rpl_rli.h
+++ b/sql/rpl_rli.h
@@ -25,7 +25,6 @@
struct RPL_TABLE_LIST;
class Master_info;
-extern uint sql_slave_skip_counter;
/****************************************************************************
@@ -229,10 +228,12 @@ public:
Needed for problems when slave stops and we want to restart it
skipping one or more events in the master log that have caused
errors, and have been manually applied by DBA already.
+ Must be ulong as it's refered to from set_var.cc
*/
- volatile uint32 slave_skip_counter;
+ volatile ulong slave_skip_counter;
volatile ulong abort_pos_wait; /* Incremented on change master */
volatile ulong slave_run_id; /* Incremented on slave start */
+ ulong max_relay_log_size;
mysql_mutex_t log_space_lock;
mysql_cond_t log_space_cond;
THD * sql_thd;
@@ -286,6 +287,7 @@ public:
slave started.
*/
ulong trans_retries, retried_trans;
+ ulong executed_entries; /* For SLAVE STATUS */
/*
If the end of the hot relay log is made of master's events ignored by the
diff --git a/sql/rpl_utility.cc b/sql/rpl_utility.cc
index 388a6c9f9bb..069fac1c3ec 100644
--- a/sql/rpl_utility.cc
+++ b/sql/rpl_utility.cc
@@ -15,11 +15,10 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#include "rpl_utility.h"
+#include "log_event.h"
-#ifndef MYSQL_CLIENT
-#include "unireg.h" // REQUIRED by later includes
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
#include "rpl_rli.h"
-#include "log_event.h"
#include "sql_select.h"
/**
@@ -954,7 +953,6 @@ TABLE *table_def::create_conversion_table(THD *thd, Relay_log_info *rli, TABLE *
target_table->s->table_name.str);
DBUG_RETURN(conv_table);
}
-
#endif /* MYSQL_CLIENT */
table_def::table_def(unsigned char *types, ulong size,
@@ -1115,7 +1113,6 @@ bool event_checksum_test(uchar *event_buf, ulong event_len, uint8 alg)
return DBUG_EVALUATE_IF("simulate_checksum_test_failure", TRUE, res);
}
-
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
Deferred_log_events::Deferred_log_events(Relay_log_info *rli) : last_added(NULL)
diff --git a/sql/rpl_utility.h b/sql/rpl_utility.h
index 9046891e27f..79f4517c492 100644
--- a/sql/rpl_utility.h
+++ b/sql/rpl_utility.h
@@ -272,7 +272,7 @@ private:
public:
Deferred_log_events(Relay_log_info *rli);
~Deferred_log_events();
- /* queue for exection at Query-log-event time prior the Query */;
+ /* queue for exection at Query-log-event time prior the Query */
int add(Log_event *ev);
bool is_empty();
bool execute(Relay_log_info *rli);
diff --git a/sql/set_var.cc b/sql/set_var.cc
index ceb1adb81de..1276dd28e22 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -259,7 +259,7 @@ uchar *sys_var::value_ptr(THD *thd, enum_var_type type, LEX_STRING *base)
bool sys_var::set_default(THD *thd, enum_var_type type)
{
LEX_STRING empty={0,0};
- set_var var(type, 0, &empty, 0);
+ set_var var(type, this, &empty, 0);
if (type == OPT_GLOBAL || scope() == GLOBAL)
global_save_default(thd, &var);
diff --git a/sql/set_var.h b/sql/set_var.h
index f0beac0fa7f..dcb60ba0bdf 100644
--- a/sql/set_var.h
+++ b/sql/set_var.h
@@ -69,13 +69,14 @@ public:
enum binlog_status_enum { VARIABLE_NOT_IN_BINLOG,
SESSION_VARIABLE_IN_BINLOG } binlog_status;
+ my_option option; ///< min, max, default values are stored here
+
protected:
typedef bool (*on_check_function)(sys_var *self, THD *thd, set_var *var);
typedef bool (*on_update_function)(sys_var *self, THD *thd, enum_var_type type);
int flags; ///< or'ed flag_enum values
const SHOW_TYPE show_val_type; ///< what value_ptr() returns for sql_show.cc
- my_option option; ///< min, max, default values are stored here
PolyLock *guard; ///< *second* lock that protects the variable
ptrdiff_t offset; ///< offset to the value from global_system_variables
on_check_function on_check;
diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt
index 16a907a6eff..5a298b4d25c 100644
--- a/sql/share/errmsg-utf8.txt
+++ b/sql/share/errmsg-utf8.txt
@@ -4271,18 +4271,18 @@ ER_TRANS_CACHE_FULL
swe "Transaktionen krävde mera än 'max_binlog_cache_size' minne. Öka denna mysqld-variabel och försök på nytt"
ukr "Транзакція з багатьма виразами вимагає більше ніж 'max_binlog_cache_size' байтів для зберігання. Збільште цю змінну mysqld та спробуйте знову"
ER_SLAVE_MUST_STOP
- dan "Denne handling kunne ikke udføres med kørende slave, brug først kommandoen STOP SLAVE"
- nla "Deze operatie kan niet worden uitgevoerd met een actieve slave, doe eerst STOP SLAVE"
- eng "This operation cannot be performed with a running slave; run STOP SLAVE first"
- fre "Cette opération ne peut être réalisée avec un esclave actif, faites STOP SLAVE d'abord"
- ger "Diese Operation kann bei einem aktiven Slave nicht durchgeführt werden. Bitte zuerst STOP SLAVE ausführen"
- ita "Questa operazione non puo' essere eseguita con un database 'slave' che gira, lanciare prima STOP SLAVE"
- por "Esta operação não pode ser realizada com um 'slave' em execução. Execute STOP SLAVE primeiro"
- rus "Эту операцию невозможно выполнить при работающем потоке подчиненного сервера. Сначала выполните STOP SLAVE"
- serbian "Ova operacija ne može biti izvršena dok je aktivan podređeni server. Zadajte prvo komandu 'STOP SLAVE' da zaustavite podređeni server."
- spa "Esta operación no puede ser hecha con el esclavo funcionando, primero use STOP SLAVE"
- swe "Denna operation kan inte göras under replikering; Gör STOP SLAVE först"
- ukr "Операція не може бути виконана з запущеним підлеглим, спочатку виконайте STOP SLAVE"
+ dan "Denne handling kunne ikke udføres med kørende slave '%2$*1$s', brug først kommandoen STOP SLAVE '%2$*1$s'"
+ nla "Deze operatie kan niet worden uitgevoerd met een actieve slave '%2$*1$s', doe eerst STOP SLAVE '%2$*1$s'"
+ eng "This operation cannot be performed as you have a running slave '%2$*1$s'; run STOP SLAVE '%2$*1$s' first"
+ fre "Cette opération ne peut être réalisée avec un esclave '%2$*1$s' actif, faites STOP SLAVE '%2$*1$s' d'abord"
+ ger "Diese Operation kann bei einem aktiven Slave '%2$*1$s' nicht durchgeführt werden. Bitte zuerst STOP SLAVE '%2$*1$s' ausführen"
+ ita "Questa operazione non puo' essere eseguita con un database 'slave' '%2$*1$s' che gira, lanciare prima STOP SLAVE '%2$*1$s'"
+ por "Esta operação não pode ser realizada com um 'slave' '%2$*1$s' em execução. Execute STOP SLAVE '%2$*1$s' primeiro"
+ rus "Эту операцию невозможно выполнить при работающем потоке подчиненного сервера %2$*1$s. Сначала выполните STOP SLAVE '%2$*1$s'"
+ serbian "Ova operacija ne može biti izvršena dok je aktivan podređeni '%2$*1$s' server. Zadajte prvo komandu 'STOP SLAVE '%2$*1$s'' da zaustavite podređeni server."
+ spa "Esta operación no puede ser hecha con el esclavo '%2$*1$s' funcionando, primero use STOP SLAVE '%2$*1$s'"
+ swe "Denna operation kan inte göras under replikering; Du har en aktiv förbindelse till '%2$*1$s'. Gör STOP SLAVE '%2$*1$s' först"
+ ukr "Операція не може бути виконана з запущеним підлеглим '%2$*1$s', спочатку виконайте STOP SLAVE '%2$*1$s'"
ER_SLAVE_NOT_RUNNING
dan "Denne handling kræver en kørende slave. Konfigurer en slave og brug kommandoen START SLAVE"
nla "Deze operatie vereist een actieve slave, configureer slave en doe dan START SLAVE"
@@ -4310,11 +4310,11 @@ ER_BAD_SLAVE
swe "Servern är inte konfigurerade som en replikationsslav. Ändra konfigurationsfilen eller gör CHANGE MASTER TO"
ukr "Сервер не зконфігуровано як підлеглий, виправте це у файлі конфігурації або з CHANGE MASTER TO"
ER_MASTER_INFO
- eng "Could not initialize master info structure; more error messages can be found in the MariaDB error log"
- fre "Impossible d'initialiser les structures d'information de maître, vous trouverez des messages d'erreur supplémentaires dans le journal des erreurs de MariaDB"
- ger "Konnte Master-Info-Struktur nicht initialisieren. Weitere Fehlermeldungen können im MariaDB-Error-Log eingesehen werden"
- serbian "Nisam mogao da inicijalizujem informacionu strukturu glavnog servera, proverite da li imam privilegije potrebne za pristup file-u 'master.info'"
- swe "Kunde inte initialisera replikationsstrukturerna. See MariaDB fel fil för mera information"
+ eng "Could not initialize master info structure for '%.*s'; more error messages can be found in the MariaDB error log"
+ fre "Impossible d'initialiser les structures d'information de maître '%.*s', vous trouverez des messages d'erreur supplémentaires dans le journal des erreurs de MariaDB"
+ ger "Konnte Master-Info-Struktur '%.*s' nicht initialisieren. Weitere Fehlermeldungen können im MariaDB-Error-Log eingesehen werden"
+ serbian "Nisam mogao da inicijalizujem informacionu strukturu glavnog servera, proverite da li imam privilegije potrebne za pristup file-u 'master.info' '%.*s'"
+ swe "Kunde inte initialisera replikationsstrukturerna för '%.*s'. See MariaDB fel fil för mera information"
ER_SLAVE_THREAD
dan "Kunne ikke danne en slave-tråd; check systemressourcerne"
nla "Kon slave thread niet aanmaken, controleer systeem resources"
@@ -4345,13 +4345,13 @@ ER_TOO_MANY_USER_CONNECTIONS 42000
ER_SET_CONSTANTS_ONLY
dan "Du må kun bruge konstantudtryk med SET"
nla "U mag alleen constante expressies gebruiken bij SET"
- eng "You may only use constant expressions with SET"
+ eng "You may only use constant expressions in this statement"
est "Ainult konstantsed suurused on lubatud SET klauslis"
fre "Seules les expressions constantes sont autorisées avec SET"
- ger "Bei SET dürfen nur konstante Ausdrücke verwendet werden"
+ ger "Bei diesem Befehl dürfen nur konstante Ausdrücke verwendet werden"
ita "Si possono usare solo espressioni costanti con SET"
por "Você pode usar apenas expressões constantes com SET"
- rus "Вы можете использовать в SET только константные выражения"
+ rus "С этой командой вы можете использовать только константные выражения"
serbian "Možete upotrebiti samo konstantan iskaz sa komandom 'SET'"
spa "Tu solo debes usar expresiones constantes con SET"
swe "Man kan endast använda konstantuttryck med SET"
@@ -6157,8 +6157,8 @@ ER_DELAYED_NOT_SUPPORTED
eng "DELAYED option not supported for table '%-.192s'"
ger "Die DELAYED-Option wird für Tabelle '%-.192s' nicht unterstützt"
WARN_NO_MASTER_INFO
- eng "The master info structure does not exist"
- ger "Die Master-Info-Struktur existiert nicht"
+ eng "There is no master connection '%.*s'"
+ ger "Die Master-Info-Struktur existiert nicht '%.*s'"
WARN_OPTION_IGNORED
eng "<%-.64s> option ignored"
ger "Option <%-.64s> ignoriert"
@@ -6807,3 +6807,18 @@ ER_STORED_FUNCTION_PREVENTS_SWITCH_SKIP_REPLICATION
eng "Cannot modify @@session.skip_replication inside a stored function or trigger"
ER_QUERY_EXCEEDED_ROWS_EXAMINED_LIMIT
eng "Query execution was interrupted. The query examined at least %llu rows, which exceeds LIMIT ROWS EXAMINED (%llu). The query result may be incomplete."
+ER_NO_SUCH_TABLE_IN_ENGINE 42S02
+ eng "Table '%-.192s.%-.192s' doesn't exist in engine"
+ swe "Det finns ingen tabell som heter '%-.192s.%-.192s' i handlern"
+ER_TARGET_NOT_EXPLAINABLE
+ eng "Target is not running an EXPLAINable command"
+ER_CONNECTION_ALREADY_EXISTS
+ eng "Connection '%.*s' conflicts with existing connection '%.*s'"
+ER_MASTER_LOG_PREFIX
+ eng "Master '%.*s': "
+ER_CANT_START_STOP_SLAVE
+ eng "Can't %s SLAVE '%.*s'"
+ER_SLAVE_STARTED
+ eng "SLAVE '%.*s' started"
+ER_SLAVE_STOPPED
+ eng "SLAVE '%.*s' stopped" \ No newline at end of file
diff --git a/sql/signal_handler.cc b/sql/signal_handler.cc
index 37f28844a7c..edc33c4d63b 100644
--- a/sql/signal_handler.cc
+++ b/sql/signal_handler.cc
@@ -138,23 +138,6 @@ extern "C" sig_handler handle_fatal_signal(int sig)
my_safe_printf_stderr("%s",
"Hope that's ok; if not, decrease some variables in the equation.\n\n");
-#if defined(HAVE_LINUXTHREADS)
-#define UNSAFE_DEFAULT_LINUX_THREADS 200
- if (sizeof(char*) == 4 && thread_count > UNSAFE_DEFAULT_LINUX_THREADS)
- {
- my_safe_printf_stderr(
- "You seem to be running 32-bit Linux and have "
- "%d concurrent connections.\n"
- "If you have not changed STACK_SIZE in LinuxThreads "
- "and built the binary \n"
- "yourself, LinuxThreads is quite likely to steal "
- "a part of the global heap for\n"
- "the thread stack. Please read "
- "http://dev.mysql.com/doc/mysql/en/linux-installation.html\n\n"
- thread_count);
- }
-#endif /* HAVE_LINUXTHREADS */
-
#ifdef HAVE_STACKTRACE
thd= current_thd;
diff --git a/sql/slave.cc b/sql/slave.cc
index 36a3d51b497..e1bac640e00 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -71,8 +71,10 @@ char slave_skip_error_names[SHOW_VAR_FUNC_BUFF_SIZE];
char* slave_load_tmpdir = 0;
Master_info *active_mi= 0;
+Master_info_index *master_info_index;
my_bool replicate_same_server_id;
ulonglong relay_log_space_limit = 0;
+LEX_STRING default_master_connection_name= { (char*) "", 0 };
/*
When slave thread exits, we need to remember the temporary tables so we
@@ -144,7 +146,8 @@ static int process_io_create_file(Master_info* mi, Create_file_log_event* cev);
static bool wait_for_relay_log_space(Relay_log_info* rli);
static inline bool io_slave_killed(THD* thd,Master_info* mi);
static inline bool sql_slave_killed(THD* thd,Relay_log_info* rli);
-static int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type);
+static int init_slave_thread(THD* thd, Master_info *mi,
+ SLAVE_THD_TYPE thd_type);
static void print_slave_skip_errors(void);
static int safe_connect(THD* thd, MYSQL* mysql, Master_info* mi);
static int safe_reconnect(THD* thd, MYSQL* mysql, Master_info* mi,
@@ -159,6 +162,8 @@ static int terminate_slave_thread(THD *thd,
volatile uint *slave_running,
bool skip_lock);
static bool check_io_slave_killed(THD *thd, Master_info *mi, const char *info);
+static bool send_show_master_info_header(THD *thd, bool full);
+static bool send_show_master_info_data(THD *thd, Master_info *mi, bool full);
/*
Find out which replications threads are running
@@ -263,15 +268,33 @@ int init_slave()
So it's safer to take the lock.
*/
mysql_mutex_lock(&LOCK_active_mi);
- /*
- TODO: re-write this to interate through the list of files
- for multi-master
- */
- active_mi= new Master_info(relay_log_recovery);
if (pthread_key_create(&RPL_MASTER_INFO, NULL))
goto err;
+ master_info_index= new Master_info_index;
+ if (!master_info_index || master_info_index->init_all_master_info())
+ {
+ sql_print_error("Failed to initialize multi master structures");
+ mysql_mutex_unlock(&LOCK_active_mi);
+ DBUG_RETURN(1);
+ }
+ if (!(active_mi= new Master_info(&default_master_connection_name,
+ relay_log_recovery)) ||
+ active_mi->error())
+ {
+ delete active_mi;
+ active_mi= 0;
+ goto err;
+ }
+
+ if (master_info_index->add_master_info(active_mi, FALSE))
+ {
+ delete active_mi;
+ active_mi= 0;
+ goto err;
+ }
+
/*
If --slave-skip-errors=... was not used, the string value for the
system variable has not been set up yet. Do it now.
@@ -286,18 +309,11 @@ int init_slave()
If master_host is specified, create the master_info file if it doesn't
exists.
*/
- if (!active_mi)
- {
- sql_print_error("Failed to allocate memory for the master info structure");
- error= 1;
- goto err;
- }
if (init_master_info(active_mi,master_info_file,relay_log_info_file,
1, (SLAVE_IO | SLAVE_SQL)))
{
sql_print_error("Failed to initialize the master info structure");
- error= 1;
goto err;
}
@@ -313,14 +329,18 @@ int init_slave()
SLAVE_IO | SLAVE_SQL))
{
sql_print_error("Failed to create slave threads");
- error= 1;
goto err;
}
}
-err:
+end:
mysql_mutex_unlock(&LOCK_active_mi);
DBUG_RETURN(error);
+
+err:
+ sql_print_error("Failed to allocate memory for the Master Info structure");
+ error= 1;
+ goto end;
}
/*
@@ -834,42 +854,19 @@ void end_slave()
running presently. If a START SLAVE was in progress, the mutex lock below
will make us wait until slave threads have started, and START SLAVE
returns, then we terminate them here.
+
+ We can also be called by cleanup(), which only happens if some
+ startup parameter to the server was wrong.
*/
mysql_mutex_lock(&LOCK_active_mi);
- if (active_mi)
- {
- /*
- TODO: replace the line below with
- list_walk(&master_list, (list_walk_action)end_slave_on_walk,0);
- once multi-master code is ready.
- */
- terminate_slave_threads(active_mi,SLAVE_FORCE_ALL);
- }
+ /* This will call terminate_slave_threads() on all connections */
+ delete master_info_index;
+ master_info_index= 0;
+ active_mi= 0;
mysql_mutex_unlock(&LOCK_active_mi);
DBUG_VOID_RETURN;
}
-/**
- Free all resources used by slave threads at time of executing shutdown.
- The routine must be called after all possible users of @c active_mi
- have left.
-
- SYNOPSIS
- close_active_mi()
-
-*/
-void close_active_mi()
-{
- mysql_mutex_lock(&LOCK_active_mi);
- if (active_mi)
- {
- end_master_info(active_mi);
- delete active_mi;
- active_mi= 0;
- }
- mysql_mutex_unlock(&LOCK_active_mi);
-}
-
static bool io_slave_killed(THD* thd, Master_info* mi)
{
DBUG_ENTER("io_slave_killed");
@@ -1767,6 +1764,37 @@ past_checksum:
}
}
}
+
+ /* Announce MariaDB slave capabilities. */
+ DBUG_EXECUTE_IF("simulate_slave_capability_none", goto after_set_capability;);
+ {
+ int rc= DBUG_EVALUATE_IF("simulate_slave_capability_old_53",
+ mysql_real_query(mysql, STRING_WITH_LEN("SET @mariadb_slave_capability="
+ STRINGIFY_ARG(MARIA_SLAVE_CAPABILITY_ANNOTATE))),
+ mysql_real_query(mysql, STRING_WITH_LEN("SET @mariadb_slave_capability="
+ STRINGIFY_ARG(MARIA_SLAVE_CAPABILITY_MINE))));
+ if (rc)
+ {
+ err_code= mysql_errno(mysql);
+ if (is_network_error(err_code))
+ {
+ mi->report(ERROR_LEVEL, err_code,
+ "Setting @mariadb_slave_capability failed with error: %s",
+ mysql_error(mysql));
+ goto network_err;
+ }
+ else
+ {
+ /* Fatal error */
+ errmsg= "The slave I/O thread stops because a fatal error is "
+ "encountered when it tries to set @mariadb_slave_capability.";
+ sprintf(err_buff, "%s Error: %s", errmsg, mysql_error(mysql));
+ goto err;
+ }
+ }
+ }
+after_set_capability:
+
err:
if (errmsg)
{
@@ -1849,9 +1877,9 @@ static bool wait_for_relay_log_space(Relay_log_info* rli)
#endif
if (rli->sql_force_rotate_relay)
{
- mysql_mutex_lock(&active_mi->data_lock);
+ mysql_mutex_lock(&mi->data_lock);
rotate_relay_log(rli->mi);
- mysql_mutex_unlock(&active_mi->data_lock);
+ mysql_mutex_unlock(&mi->data_lock);
rli->sql_force_rotate_relay= false;
}
@@ -2005,15 +2033,36 @@ int register_slave_on_master(MYSQL* mysql, Master_info *mi,
@retval FALSE success
@retval TRUE failure
*/
-bool show_master_info(THD* thd, Master_info* mi)
+
+bool show_master_info(THD *thd, Master_info *mi, bool full)
+{
+ DBUG_ENTER("show_master_info");
+
+ if (send_show_master_info_header(thd, full))
+ DBUG_RETURN(TRUE);
+ if (send_show_master_info_data(thd, mi, full))
+ DBUG_RETURN(TRUE);
+ my_eof(thd);
+ DBUG_RETURN(FALSE);
+}
+
+static bool send_show_master_info_header(THD *thd, bool full)
{
- // TODO: fix this for multi-master
List<Item> field_list;
Protocol *protocol= thd->protocol;
- DBUG_ENTER("show_master_info");
+ Master_info *mi;
+ DBUG_ENTER("show_master_info_header");
+
+ if (full)
+ {
+ field_list.push_back(new Item_empty_string("Connection_name",
+ MAX_CONNECTION_NAME));
+ field_list.push_back(new Item_empty_string("Slave_SQL_State",
+ 30));
+ }
field_list.push_back(new Item_empty_string("Slave_IO_State",
- 14));
+ 30));
field_list.push_back(new Item_empty_string("Master_Host",
sizeof(mi->host)));
field_list.push_back(new Item_empty_string("Master_User",
@@ -2080,22 +2129,52 @@ bool show_master_info(THD* thd, Master_info* mi)
sizeof(mi->ssl_crl)));
field_list.push_back(new Item_empty_string("Master_SSL_Crlpath",
sizeof(mi->ssl_crlpath)));
+ if (full)
+ {
+ field_list.push_back(new Item_return_int("Retried_transactions",
+ 10, MYSQL_TYPE_LONG));
+ field_list.push_back(new Item_return_int("Max_relay_log_size",
+ 10, MYSQL_TYPE_LONGLONG));
+ field_list.push_back(new Item_return_int("Executed_log_entries",
+ 10, MYSQL_TYPE_LONG));
+ field_list.push_back(new Item_return_int("Slave_received_heartbeats",
+ 10, MYSQL_TYPE_LONG));
+ field_list.push_back(new Item_float("Slave_heartbeat_period",
+ 0.0, 3, 10));
+ }
if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
+ DBUG_RETURN(FALSE);
+}
+
+
+static bool send_show_master_info_data(THD *thd, Master_info *mi, bool full)
+{
+ DBUG_ENTER("send_show_master_info_data");
if (mi->host[0])
{
DBUG_PRINT("info",("host is set: '%s'", mi->host));
String *packet= &thd->packet;
+ Protocol *protocol= thd->protocol;
+ char buf[256];
+ String tmp(buf, sizeof(buf), &my_charset_bin);
+
protocol->prepare_for_resend();
/*
slave_running can be accessed without run_lock but not other
non-volotile members like mi->io_thd, which is guarded by the mutex.
*/
+ if (full)
+ protocol->store(mi->connection_name.str, mi->connection_name.length,
+ &my_charset_bin);
mysql_mutex_lock(&mi->run_lock);
+ if (full)
+ protocol->store(mi->rli.sql_thd ? mi->rli.sql_thd->get_proc_info() : "",
+ &my_charset_bin);
protocol->store(mi->io_thd ? mi->io_thd->get_proc_info() : "", &my_charset_bin);
mysql_mutex_unlock(&mi->run_lock);
@@ -2121,8 +2200,6 @@ bool show_master_info(THD* thd, Master_info* mi)
protocol->store(rpl_filter->get_do_db());
protocol->store(rpl_filter->get_ignore_db());
- char buf[256];
- String tmp(buf, sizeof(buf), &my_charset_bin);
rpl_filter->get_do_table(&tmp);
protocol->store(&tmp);
rpl_filter->get_ignore_table(&tmp);
@@ -2232,6 +2309,14 @@ bool show_master_info(THD* thd, Master_info* mi)
protocol->store(mi->ssl_ca, &my_charset_bin);
// Master_Ssl_Crlpath
protocol->store(mi->ssl_capath, &my_charset_bin);
+ if (full)
+ {
+ protocol->store((uint32) mi->rli.retried_trans);
+ protocol->store((ulonglong) mi->rli.max_relay_log_size);
+ protocol->store((uint32) mi->rli.executed_entries);
+ protocol->store((uint32) mi->received_heartbeats);
+ protocol->store((double) mi->heartbeat_period, 3, &tmp);
+ }
mysql_mutex_unlock(&mi->rli.err_lock);
mysql_mutex_unlock(&mi->err_lock);
@@ -2241,6 +2326,69 @@ bool show_master_info(THD* thd, Master_info* mi)
if (my_net_write(&thd->net, (uchar*) thd->packet.ptr(), packet->length()))
DBUG_RETURN(TRUE);
}
+ DBUG_RETURN(FALSE);
+}
+
+
+/* Used to sort connections by name */
+
+static int cmp_mi_by_name(const Master_info **arg1,
+ const Master_info **arg2)
+{
+ return my_strcasecmp(system_charset_info, (*arg1)->connection_name.str,
+ (*arg2)->connection_name.str);
+}
+
+
+/**
+ Execute a SHOW FULL SLAVE STATUS statement.
+
+ @param thd Pointer to THD object for the client thread executing the
+ statement.
+
+ Elements are sorted according to the original connection_name.
+
+ @retval FALSE success
+ @retval TRUE failure
+
+ @note
+ master_info_index is protected by LOCK_active_mi.
+*/
+
+bool show_all_master_info(THD* thd)
+{
+ uint i, elements;
+ Master_info **tmp;
+ DBUG_ENTER("show_master_info");
+ mysql_mutex_assert_owner(&LOCK_active_mi);
+
+ if (send_show_master_info_header(thd, 1))
+ DBUG_RETURN(TRUE);
+
+ if (!(elements= master_info_index->master_info_hash.records))
+ goto end;
+
+ /*
+ Sort lines to get them into a predicted order
+ (needed for test cases and to not confuse users)
+ */
+ if (!(tmp= (Master_info**) thd->alloc(sizeof(Master_info*) * elements)))
+ DBUG_RETURN(TRUE);
+
+ for (i= 0; i < elements; i++)
+ {
+ tmp[i]= (Master_info *) my_hash_element(&master_info_index->
+ master_info_hash, i);
+ }
+ my_qsort(tmp, elements, sizeof(Master_info*), (qsort_cmp) cmp_mi_by_name);
+
+ for (i= 0; i < elements; i++)
+ {
+ if (send_show_master_info_data(thd, tmp[i], 1))
+ DBUG_RETURN(TRUE);
+ }
+
+end:
my_eof(thd);
DBUG_RETURN(FALSE);
}
@@ -2294,7 +2442,8 @@ void set_slave_thread_default_charset(THD* thd, Relay_log_info const *rli)
init_slave_thread()
*/
-static int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type)
+static int init_slave_thread(THD* thd, Master_info *mi,
+ SLAVE_THD_TYPE thd_type)
{
DBUG_ENTER("init_slave_thread");
#if !defined(DBUG_OFF)
@@ -2309,9 +2458,9 @@ static int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type)
slave threads, since a replication event can become this much larger
than the corresponding packet (query) sent from client to master.
*/
- thd->variables.max_allowed_packet= global_system_variables.max_allowed_packet
- + MAX_LOG_EVENT_HEADER; /* note, incr over the global not session var */
- thd->slave_thread = 1;
+ thd->variables.max_allowed_packet= slave_max_allowed_packet;
+ thd->slave_thread= 1;
+ thd->connection_name= mi->connection_name;
thd->enable_slow_log= opt_log_slow_slave_statements;
thd->variables.log_slow_filter= global_system_variables.log_slow_filter;
set_slave_thread_options(thd);
@@ -2626,7 +2775,10 @@ int apply_event_and_update_pos(Log_event* ev, THD* thd, Relay_log_info* rli)
int reason= ev->shall_skip(rli);
if (reason == Log_event::EVENT_SKIP_COUNT)
- sql_slave_skip_counter= --rli->slave_skip_counter;
+ {
+ DBUG_ASSERT(rli->slave_skip_counter > 0);
+ rli->slave_skip_counter--;
+ }
mysql_mutex_unlock(&rli->data_lock);
if (reason == Log_event::EVENT_SKIP_NOT)
exec_res= ev->apply_event(rli);
@@ -2875,6 +3027,7 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli)
mysql_mutex_lock(&rli->data_lock); // because of SHOW STATUS
rli->trans_retries++;
rli->retried_trans++;
+ statistic_increment(slave_retried_transactions, LOCK_status);
mysql_mutex_unlock(&rli->data_lock);
DBUG_PRINT("info", ("Slave retries transaction "
"rli->trans_retries: %lu", rli->trans_retries));
@@ -3033,6 +3186,8 @@ pthread_handler_t handle_slave_io(void *arg)
mysql= NULL ;
retry_count= 0;
+ thd= new THD; // note that contructor of THD uses DBUG_ !
+
mysql_mutex_lock(&mi->run_lock);
/* Inform waiting threads that slave has started */
mi->slave_run_id++;
@@ -3041,14 +3196,13 @@ pthread_handler_t handle_slave_io(void *arg)
mi->events_till_disconnect = disconnect_slave_event_count;
#endif
- thd= new THD; // note that contructor of THD uses DBUG_ !
THD_CHECK_SENTRY(thd);
mi->io_thd = thd;
pthread_detach_this_thread();
thd->thread_stack= (char*) &thd; // remember where our stack is
mi->clear_error();
- if (init_slave_thread(thd, SLAVE_THD_IO))
+ if (init_slave_thread(thd, mi, SLAVE_THD_IO))
{
mysql_cond_broadcast(&mi->start_cond);
sql_print_error("Failed during slave I/O thread initialization");
@@ -3057,7 +3211,7 @@ pthread_handler_t handle_slave_io(void *arg)
mysql_mutex_lock(&LOCK_thread_count);
threads.append(thd);
mysql_mutex_unlock(&LOCK_thread_count);
- mi->slave_running = 1;
+ mi->slave_running = MYSQL_SLAVE_RUN_NOT_CONNECT;
mi->abort_slave = 0;
mysql_mutex_unlock(&mi->run_lock);
mysql_cond_broadcast(&mi->start_cond);
@@ -3097,6 +3251,7 @@ pthread_handler_t handle_slave_io(void *arg)
thread, since a replication event can become this much larger than
the corresponding packet (query) sent from client to master.
*/
+ thd->net.max_packet_size= slave_max_allowed_packet;
mysql->net.max_packet_size= thd->net.max_packet_size+= MAX_LOG_EVENT_HEADER;
}
else
@@ -3229,12 +3384,12 @@ reading event"))
switch (mysql_error_number) {
case CR_NET_PACKET_TOO_LARGE:
sql_print_error("\
-Log entry on master is longer than max_allowed_packet (%ld) on \
+Log entry on master is longer than slave_max_allowed_packet (%lu) on \
slave. If the entry is correct, restart the server with a higher value of \
-max_allowed_packet",
- thd->variables.max_allowed_packet);
+slave_max_allowed_packet",
+ slave_max_allowed_packet);
mi->report(ERROR_LEVEL, ER_NET_PACKET_TOO_LARGE,
- "%s", ER(ER_NET_PACKET_TOO_LARGE));
+ "%s", "Got a packet bigger than 'slave_max_allowed_packet' bytes");
goto err;
case ER_MASTER_FATAL_ERROR_READING_BINLOG:
mi->report(ERROR_LEVEL, ER_MASTER_FATAL_ERROR_READING_BINLOG,
@@ -3368,7 +3523,7 @@ err_during_init:
delete thd;
mysql_mutex_unlock(&LOCK_thread_count);
mi->abort_slave= 0;
- mi->slave_running= 0;
+ mi->slave_running= MYSQL_SLAVE_NOT_RUN;
mi->io_thd= 0;
/*
Note: the order of the two following calls (first broadcast, then unlock)
@@ -3446,7 +3601,8 @@ pthread_handler_t handle_slave_sql(void *arg)
my_off_t UNINIT_VAR(saved_log_pos);
my_off_t UNINIT_VAR(saved_master_log_pos);
my_off_t saved_skip= 0;
- Relay_log_info* rli = &((Master_info*)arg)->rli;
+ Master_info *mi= ((Master_info*)arg);
+ Relay_log_info* rli = &mi->rli;
const char *errmsg;
// needs to call my_thread_init(), otherwise we get a coredump in DBUG_ stuff
@@ -3460,6 +3616,7 @@ pthread_handler_t handle_slave_sql(void *arg)
thd->thread_stack = (char*)&thd; // remember where our stack is
DBUG_ASSERT(rli->inited);
+ DBUG_ASSERT(rli->mi == mi);
mysql_mutex_lock(&rli->run_lock);
DBUG_ASSERT(!rli->slave_running);
errmsg= 0;
@@ -3471,10 +3628,10 @@ pthread_handler_t handle_slave_sql(void *arg)
/* Inform waiting threads that slave has started */
rli->slave_run_id++;
- rli->slave_running = 1;
+ rli->slave_running= MYSQL_SLAVE_RUN_NOT_CONNECT;
pthread_detach_this_thread();
- if (init_slave_thread(thd, SLAVE_THD_SQL))
+ if (init_slave_thread(thd, mi, SLAVE_THD_SQL))
{
/*
TODO: this is currently broken - slave start and change master
@@ -3715,6 +3872,7 @@ the slave SQL thread with \"SLAVE START\". We stopped at log \
}
goto err;
}
+ rli->executed_entries++;
}
/* Thread stopped. Print the current replication position to the log */
@@ -3745,9 +3903,9 @@ the slave SQL thread with \"SLAVE START\". We stopped at log \
err_during_init:
/* We need data_lock, at least to wake up any waiting master_pos_wait() */
mysql_mutex_lock(&rli->data_lock);
- DBUG_ASSERT(rli->slave_running == 1); // tracking buffer overrun
+ DBUG_ASSERT(rli->slave_running == MYSQL_SLAVE_RUN_NOT_CONNECT); // tracking buffer overrun
/* When master_pos_wait() wakes up it will check this and terminate */
- rli->slave_running= 0;
+ rli->slave_running= MYSQL_SLAVE_NOT_RUN;
/* Forget the relay log's format */
delete rli->relay_log.description_event_for_exec;
rli->relay_log.description_event_for_exec= 0;
@@ -5266,11 +5424,6 @@ static Log_event* next_event(Relay_log_info* rli)
mysql_mutex_lock(log_lock);
if (rli->relay_log.is_active(rli->linfo.log_file_name))
{
-#ifdef EXTRA_DEBUG
- if (global_system_variables.log_warnings)
- sql_print_information("next log '%s' is currently active",
- rli->linfo.log_file_name);
-#endif
rli->cur_log= cur_log= rli->relay_log.get_log_file();
rli->cur_log_old_open_count= rli->relay_log.get_open_count();
DBUG_ASSERT(rli->cur_log_fd == -1);
@@ -5353,11 +5506,6 @@ static Log_event* next_event(Relay_log_info* rli)
ourselves. We are sure that the log is still not hot now (a log can get
from hot to cold, but not from cold to hot). No need for LOCK_log.
*/
-#ifdef EXTRA_DEBUG
- if (global_system_variables.log_warnings)
- sql_print_information("next log '%s' is not active",
- rli->linfo.log_file_name);
-#endif
// open_binlog() will check the magic header
if ((rli->cur_log_fd=open_binlog(cur_log,rli->linfo.log_file_name,
&errmsg)) <0)
@@ -5537,11 +5685,10 @@ bool rpl_master_has_bug(const Relay_log_info *rli, uint bug_id, bool report,
*/
bool rpl_master_erroneous_autoinc(THD *thd)
{
- if (active_mi && active_mi->rli.sql_thd == thd)
+ if (thd->rli_slave)
{
- Relay_log_info *rli= &active_mi->rli;
DBUG_EXECUTE_IF("simulate_bug33029", return TRUE;);
- return rpl_master_has_bug(rli, 33029, FALSE, NULL, NULL);
+ return rpl_master_has_bug(thd->rli_slave, 33029, FALSE, NULL, NULL);
}
return FALSE;
}
diff --git a/sql/slave.h b/sql/slave.h
index 6b4bcffe109..565f40b7236 100644
--- a/sql/slave.h
+++ b/sql/slave.h
@@ -45,9 +45,12 @@
#define MAX_SLAVE_ERROR 2000
+#define MAX_REPLICATION_THREAD 64
+
// Forward declarations
class Relay_log_info;
class Master_info;
+class Master_info_index;
int init_intvar_from_file(int* var, IO_CACHE* f, int default_val);
int init_strvar_from_file(char *var, int max_size, IO_CACHE *f,
@@ -197,7 +200,8 @@ int mysql_table_dump(THD* thd, const char* db,
int fetch_master_table(THD* thd, const char* db_name, const char* table_name,
Master_info* mi, MYSQL* mysql, bool overwrite);
-bool show_master_info(THD* thd, Master_info* mi);
+bool show_master_info(THD* thd, Master_info* mi, bool full);
+bool show_all_master_info(THD* thd);
bool show_binlog_info(THD* thd);
bool rpl_master_has_bug(const Relay_log_info *rli, uint bug_id, bool report,
bool (*pred)(const void *), const void *param);
@@ -231,6 +235,9 @@ bool net_request_file(NET* net, const char* fname);
extern bool volatile abort_loop;
extern Master_info main_mi, *active_mi; /* active_mi for multi-master */
+extern Master_info *default_master_info; /* To replace active_mi */
+extern Master_info_index *master_info_index;
+extern LEX_STRING default_master_connection_name;
extern LIST master_list;
extern my_bool replicate_same_server_id;
diff --git a/sql/sp.cc b/sql/sp.cc
index 1a2e3bd49ab..db180bde4b5 100644
--- a/sql/sp.cc
+++ b/sql/sp.cc
@@ -984,9 +984,6 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
enum_check_fields saved_count_cuted_fields;
bool store_failed= FALSE;
-
- bool save_binlog_row_based;
-
DBUG_ENTER("sp_create_routine");
DBUG_PRINT("enter", ("type: %d name: %.*s", (int) type,
(int) sp->m_name.length,
@@ -1004,14 +1001,6 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
/* Reset sql_mode during data dictionary operations. */
thd->variables.sql_mode= 0;
- /*
- This statement will be replicated as a statement, even when using
- row-based replication. The flag will be reset at the end of the
- statement.
- */
- if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
- thd->clear_current_stmt_binlog_format_row();
-
saved_count_cuted_fields= thd->count_cuted_fields;
thd->count_cuted_fields= CHECK_FIELD_WARN;
@@ -1217,10 +1206,7 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
done:
thd->count_cuted_fields= saved_count_cuted_fields;
thd->variables.sql_mode= saved_mode;
- /* Restore the state of binlog format */
DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(ret);
}
@@ -1245,7 +1231,6 @@ sp_drop_routine(THD *thd, stored_procedure_type type, sp_name *name)
{
TABLE *table;
int ret;
- bool save_binlog_row_based;
MDL_key::enum_mdl_namespace mdl_type= type == TYPE_ENUM_FUNCTION ?
MDL_key::FUNCTION : MDL_key::PROCEDURE;
DBUG_ENTER("sp_drop_routine");
@@ -1267,9 +1252,6 @@ sp_drop_routine(THD *thd, stored_procedure_type type, sp_name *name)
row-based replication. The flag will be reset at the end of the
statement.
*/
- if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
- thd->clear_current_stmt_binlog_format_row();
-
if ((ret= db_find_routine_aux(thd, type, name, table)) == SP_OK)
{
if (table->file->ha_delete_row(table->record[0]))
@@ -1296,10 +1278,7 @@ sp_drop_routine(THD *thd, stored_procedure_type type, sp_name *name)
sp_cache_flush_obsolete(spc, &sp);
}
}
- /* Restore the state of binlog format */
DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(ret);
}
@@ -1327,7 +1306,6 @@ sp_update_routine(THD *thd, stored_procedure_type type, sp_name *name,
{
TABLE *table;
int ret;
- bool save_binlog_row_based;
MDL_key::enum_mdl_namespace mdl_type= type == TYPE_ENUM_FUNCTION ?
MDL_key::FUNCTION : MDL_key::PROCEDURE;
DBUG_ENTER("sp_update_routine");
@@ -1345,14 +1323,6 @@ sp_update_routine(THD *thd, stored_procedure_type type, sp_name *name,
if (!(table= open_proc_table_for_update(thd)))
DBUG_RETURN(SP_OPEN_TABLE_FAILED);
- /*
- This statement will be replicated as a statement, even when using
- row-based replication. The flag will be reset at the end of the
- statement.
- */
- if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
- thd->clear_current_stmt_binlog_format_row();
-
if ((ret= db_find_routine_aux(thd, type, name, table)) == SP_OK)
{
if (type == TYPE_ENUM_FUNCTION && ! trust_function_creators &&
@@ -1406,10 +1376,7 @@ sp_update_routine(THD *thd, stored_procedure_type type, sp_name *name,
sp_cache_invalidate();
}
err:
- /* Restore the state of binlog format */
DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(ret);
}
@@ -1429,6 +1396,7 @@ public:
MYSQL_ERROR ** cond_hdl)
{
if (sql_errno == ER_NO_SUCH_TABLE ||
+ sql_errno == ER_NO_SUCH_TABLE_IN_ENGINE ||
sql_errno == ER_CANNOT_LOAD_FROM_TABLE_V2 ||
sql_errno == ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE ||
sql_errno == ER_COL_COUNT_DOESNT_MATCH_CORRUPTED_V2)
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 9a356cb3321..bde54ea2443 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -218,6 +218,7 @@ sp_get_flags_for_command(LEX *lex)
case SQLCOM_SHOW_CREATE_TRIGGER:
case SQLCOM_SHOW_DATABASES:
case SQLCOM_SHOW_ERRORS:
+ case SQLCOM_SHOW_EXPLAIN:
case SQLCOM_SHOW_FIELDS:
case SQLCOM_SHOW_FUNC_CODE:
case SQLCOM_SHOW_GRANTS:
@@ -3032,6 +3033,7 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
if (!res || !thd->is_error() ||
(thd->stmt_da->sql_errno() != ER_CANT_REOPEN_TABLE &&
thd->stmt_da->sql_errno() != ER_NO_SUCH_TABLE &&
+ thd->stmt_da->sql_errno() != ER_NO_SUCH_TABLE_IN_ENGINE &&
thd->stmt_da->sql_errno() != ER_UPDATE_TABLE_USED))
thd->stmt_arena->state= Query_arena::STMT_EXECUTED;
diff --git a/sql/spatial.h b/sql/spatial.h
index 0d0560656f0..07675d59040 100644
--- a/sql/spatial.h
+++ b/sql/spatial.h
@@ -554,5 +554,5 @@ public:
struct Geometry_buffer : public
my_aligned_storage<sizeof(Gis_point), MY_ALIGNOF(Gis_point)> {};
-#endif /*HAVE_SPATAIAL*/
+#endif /*HAVE_SPATIAL*/
#endif
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index c2cfb6f5477..d920202a426 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -1875,7 +1875,7 @@ bool change_password(THD *thd, const char *host, const char *user,
/* Buffer should be extended when password length is extended. */
char buff[512];
ulong query_length;
- bool save_binlog_row_based;
+ enum_binlog_format save_binlog_format;
uint new_password_len= (uint) strlen(new_password);
bool result= 1;
DBUG_ENTER("change_password");
@@ -1912,9 +1912,10 @@ bool change_password(THD *thd, const char *host, const char *user,
This statement will be replicated as a statement, even when using
row-based replication. The flag will be reset at the end of the
statement.
+ This has to be handled here as it's called by set_var.cc, which is
+ not automaticly handled by sql_parse.cc
*/
- if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
- thd->clear_current_stmt_binlog_format_row();
+ save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
mysql_mutex_lock(&acl_cache->lock);
ACL_USER *acl_user;
@@ -1963,11 +1964,7 @@ bool change_password(THD *thd, const char *host, const char *user,
}
end:
close_mysql_tables(thd);
-
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
+ thd->restore_stmt_binlog_format(save_binlog_format);
DBUG_RETURN(result);
}
@@ -3538,7 +3535,6 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
TABLE_LIST tables[3];
bool create_new_users=0;
char *db_name, *table_name;
- bool save_binlog_row_based;
DBUG_ENTER("mysql_table_grant");
if (!initialized)
@@ -3628,14 +3624,6 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
if (column_priv || (revoke_grant && ((rights & COL_ACLS) || columns.elements)))
tables[1].next_local= tables[1].next_global= tables+2;
- /*
- This statement will be replicated as a statement, even when using
- row-based replication. The flag will be reset at the end of the
- statement.
- */
- if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
- thd->clear_current_stmt_binlog_format_row();
-
#ifdef HAVE_REPLICATION
/*
GRANT and REVOKE are applied the slave in/exclusion rules as they are
@@ -3649,13 +3637,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
*/
tables[0].updating= tables[1].updating= tables[2].updating= 1;
if (!(thd->spcont || rpl_filter->tables_ok(0, tables)))
- {
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(FALSE);
- }
}
#endif
@@ -3673,11 +3655,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
thd->lex->sql_command= backup.sql_command;
if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
{ // Should never happen
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
thd->lex->restore_backup_query_tables_list(&backup);
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(TRUE); /* purecov: deadcode */
}
@@ -3805,9 +3783,6 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
/* Tables are automatically closed */
thd->lex->restore_backup_query_tables_list(&backup);
/* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(result);
}
@@ -3836,7 +3811,6 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
TABLE_LIST tables[2];
bool create_new_users=0, result=0;
char *db_name, *table_name;
- bool save_binlog_row_based;
DBUG_ENTER("mysql_routine_grant");
if (!initialized)
@@ -3866,14 +3840,6 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
C_STRING_WITH_LEN("procs_priv"), "procs_priv", TL_WRITE);
tables[0].next_local= tables[0].next_global= tables+1;
- /*
- This statement will be replicated as a statement, even when using
- row-based replication. The flag will be reset at the end of the
- statement.
- */
- if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
- thd->clear_current_stmt_binlog_format_row();
-
#ifdef HAVE_REPLICATION
/*
GRANT and REVOKE are applied the slave in/exclusion rules as they are
@@ -3888,23 +3854,15 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
tables[0].updating= tables[1].updating= 1;
if (!(thd->spcont || rpl_filter->tables_ok(0, tables)))
{
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(FALSE);
}
}
#endif
if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
- { // Should never happen
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(TRUE);
- }
+
+ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
if (!revoke_grant)
create_new_users= test_if_create_new_users(thd);
@@ -3978,10 +3936,6 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
}
mysql_rwlock_unlock(&LOCK_grant);
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
/* Tables are automatically closed */
DBUG_RETURN(result);
@@ -3996,8 +3950,8 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
char tmp_db[SAFE_NAME_LEN+1];
bool create_new_users=0;
TABLE_LIST tables[2];
- bool save_binlog_row_based;
DBUG_ENTER("mysql_grant");
+
if (!initialized)
{
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
@@ -4034,14 +3988,6 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
TL_WRITE);
tables[0].next_local= tables[0].next_global= tables+1;
- /*
- This statement will be replicated as a statement, even when using
- row-based replication. The flag will be reset at the end of the
- statement.
- */
- if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
- thd->clear_current_stmt_binlog_format_row();
-
#ifdef HAVE_REPLICATION
/*
GRANT and REVOKE are applied the slave in/exclusion rules as they are
@@ -4055,24 +4001,14 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
*/
tables[0].updating= tables[1].updating= 1;
if (!(thd->spcont || rpl_filter->tables_ok(0, tables)))
- {
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(FALSE);
- }
}
#endif
if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
- { // This should never happen
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(TRUE); /* purecov: deadcode */
- }
+
+ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
if (!revoke_grant)
create_new_users= test_if_create_new_users(thd);
@@ -4138,10 +4074,6 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
if (!result)
my_ok(thd);
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(result);
}
@@ -6454,26 +6386,11 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list)
List_iterator <LEX_USER> user_list(list);
TABLE_LIST tables[GRANT_TABLES];
bool some_users_created= FALSE;
- bool save_binlog_row_based;
DBUG_ENTER("mysql_create_user");
- /*
- This statement will be replicated as a statement, even when using
- row-based replication. The flag will be reset at the end of the
- statement.
- */
- if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
- thd->clear_current_stmt_binlog_format_row();
-
/* CREATE USER may be skipped on replication client. */
if ((result= open_grant_tables(thd, tables)))
- {
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(result != 1);
- }
mysql_rwlock_wrlock(&LOCK_grant);
mysql_mutex_lock(&acl_cache->lock);
@@ -6514,10 +6431,6 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list)
result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
mysql_rwlock_unlock(&LOCK_grant);
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(result);
}
@@ -6544,26 +6457,11 @@ bool mysql_drop_user(THD *thd, List <LEX_USER> &list)
TABLE_LIST tables[GRANT_TABLES];
bool some_users_deleted= FALSE;
ulonglong old_sql_mode= thd->variables.sql_mode;
- bool save_binlog_row_based;
DBUG_ENTER("mysql_drop_user");
- /*
- This statement will be replicated as a statement, even when using
- row-based replication. The flag will be reset at the end of the
- statement.
- */
- if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
- thd->clear_current_stmt_binlog_format_row();
-
/* DROP USER may be skipped on replication client. */
if ((result= open_grant_tables(thd, tables)))
- {
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(result != 1);
- }
thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
@@ -6599,10 +6497,6 @@ bool mysql_drop_user(THD *thd, List <LEX_USER> &list)
mysql_rwlock_unlock(&LOCK_grant);
thd->variables.sql_mode= old_sql_mode;
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(result);
}
@@ -6629,26 +6523,13 @@ bool mysql_rename_user(THD *thd, List <LEX_USER> &list)
List_iterator <LEX_USER> user_list(list);
TABLE_LIST tables[GRANT_TABLES];
bool some_users_renamed= FALSE;
- bool save_binlog_row_based;
DBUG_ENTER("mysql_rename_user");
- /*
- This statement will be replicated as a statement, even when using
- row-based replication. The flag will be reset at the end of the
- statement.
- */
- if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
- thd->clear_current_stmt_binlog_format_row();
-
/* RENAME USER may be skipped on replication client. */
if ((result= open_grant_tables(thd, tables)))
- {
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(result != 1);
- }
+
+ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
mysql_rwlock_wrlock(&LOCK_grant);
mysql_mutex_lock(&acl_cache->lock);
@@ -6694,10 +6575,6 @@ bool mysql_rename_user(THD *thd, List <LEX_USER> &list)
result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
mysql_rwlock_unlock(&LOCK_grant);
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(result);
}
@@ -6722,25 +6599,12 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
int result;
ACL_DB *acl_db;
TABLE_LIST tables[GRANT_TABLES];
- bool save_binlog_row_based;
DBUG_ENTER("mysql_revoke_all");
- /*
- This statement will be replicated as a statement, even when using
- row-based replication. The flag will be reset at the end of the
- statement.
- */
- if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
- thd->clear_current_stmt_binlog_format_row();
-
if ((result= open_grant_tables(thd, tables)))
- {
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(result != 1);
- }
+
+ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
mysql_rwlock_wrlock(&LOCK_grant);
mysql_mutex_lock(&acl_cache->lock);
@@ -6891,10 +6755,6 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
write_bin_log(thd, FALSE, thd->query(), thd->query_length());
mysql_rwlock_unlock(&LOCK_grant);
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(result);
}
@@ -6986,26 +6846,19 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
TABLE_LIST tables[GRANT_TABLES];
HASH *hash= is_proc ? &proc_priv_hash : &func_priv_hash;
Silence_routine_definer_errors error_handler;
- bool save_binlog_row_based;
DBUG_ENTER("sp_revoke_privileges");
if ((result= open_grant_tables(thd, tables)))
DBUG_RETURN(result != 1);
+ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
+
/* Be sure to pop this before exiting this scope! */
thd->push_internal_handler(&error_handler);
mysql_rwlock_wrlock(&LOCK_grant);
mysql_mutex_lock(&acl_cache->lock);
- /*
- This statement will be replicated as a statement, even when using
- row-based replication. The flag will be reset at the end of the
- statement.
- */
- if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
- thd->clear_current_stmt_binlog_format_row();
-
/* Remove procedure access */
do
{
@@ -7039,10 +6892,6 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
mysql_rwlock_unlock(&LOCK_grant);
thd->pop_internal_handler();
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(error_handler.has_errors());
}
@@ -8045,6 +7894,7 @@ static bool send_server_handshake_packet(MPVIO_EXT *mpvio,
int2store(end+3, mpvio->thd->server_status);
int2store(end+5, thd->client_capabilities >> 16);
end[7]= data_len;
+ DBUG_EXECUTE_IF("poison_srv_handshake_scramble_len", end[7]= -100;);
bzero(end + 8, 10);
end+= 18;
/* write scramble tail */
diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc
index 8d0f2e5634f..c65e56edbe0 100644
--- a/sql/sql_admin.cc
+++ b/sql/sql_admin.cc
@@ -285,6 +285,7 @@ end:
static inline bool table_not_corrupt_error(uint sql_errno)
{
return (sql_errno == ER_NO_SUCH_TABLE ||
+ sql_errno == ER_NO_SUCH_TABLE_IN_ENGINE ||
sql_errno == ER_FILE_NOT_FOUND ||
sql_errno == ER_LOCK_WAIT_TIMEOUT ||
sql_errno == ER_LOCK_DEADLOCK ||
diff --git a/sql/sql_array.h b/sql/sql_array.h
index 67f1f1c2ad7..98a4a4815af 100644
--- a/sql/sql_array.h
+++ b/sql/sql_array.h
@@ -19,6 +19,77 @@
#include <my_sys.h>
+/**
+ A wrapper class which provides array bounds checking.
+ We do *not* own the array, we simply have a pointer to the first element,
+ and a length.
+
+ @remark
+ We want the compiler-generated versions of:
+ - the copy CTOR (memberwise initialization)
+ - the assignment operator (memberwise assignment)
+
+ @param Element_type The type of the elements of the container.
+ */
+template <typename Element_type> class Bounds_checked_array
+{
+public:
+ Bounds_checked_array() : m_array(NULL), m_size(0) {}
+
+ Bounds_checked_array(Element_type *el, size_t size)
+ : m_array(el), m_size(size)
+ {}
+
+ void reset() { m_array= NULL; m_size= 0; }
+
+ void reset(Element_type *array, size_t size)
+ {
+ m_array= array;
+ m_size= size;
+ }
+
+ /**
+ Set a new bound on the array. Does not resize the underlying
+ array, so the new size must be smaller than or equal to the
+ current size.
+ */
+ void resize(size_t new_size)
+ {
+ DBUG_ASSERT(new_size <= m_size);
+ m_size= new_size;
+ }
+
+ Element_type &operator[](size_t n)
+ {
+ DBUG_ASSERT(n < m_size);
+ return m_array[n];
+ }
+
+ const Element_type &operator[](size_t n) const
+ {
+ DBUG_ASSERT(n < m_size);
+ return m_array[n];
+ }
+
+ size_t element_size() const { return sizeof(Element_type); }
+ size_t size() const { return m_size; }
+
+ bool is_null() const { return m_array == NULL; }
+
+ void pop_front()
+ {
+ DBUG_ASSERT(m_size > 0);
+ m_array+= 1;
+ m_size-= 1;
+ }
+
+ Element_type *array() const { return m_array; }
+
+private:
+ Element_type *m_array;
+ size_t m_size;
+};
+
/*
A typesafe wrapper around DYNAMIC_ARRAY
*/
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 7ce77bcf9b4..feb4e43eb51 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -70,7 +70,7 @@ No_such_table_error_handler::handle_condition(THD *,
MYSQL_ERROR ** cond_hdl)
{
*cond_hdl= NULL;
- if (sql_errno == ER_NO_SUCH_TABLE)
+ if (sql_errno == ER_NO_SUCH_TABLE || sql_errno == ER_NO_SUCH_TABLE_IN_ENGINE)
{
m_handled_errors++;
return TRUE;
@@ -144,7 +144,9 @@ Repair_mrg_table_error_handler::handle_condition(THD *,
MYSQL_ERROR ** cond_hdl)
{
*cond_hdl= NULL;
- if (sql_errno == ER_NO_SUCH_TABLE || sql_errno == ER_WRONG_MRG_TABLE)
+ if (sql_errno == ER_NO_SUCH_TABLE ||
+ sql_errno == ER_NO_SUCH_TABLE_IN_ENGINE ||
+ sql_errno == ER_WRONG_MRG_TABLE)
{
m_handled_errors= true;
return TRUE;
@@ -394,6 +396,7 @@ bool table_def_init(void)
void table_def_start_shutdown(void)
{
+ DBUG_ENTER("table_def_start_shutdown");
if (table_def_inited)
{
mysql_mutex_lock(&LOCK_open);
@@ -408,6 +411,7 @@ void table_def_start_shutdown(void)
/* Free all cached but unused TABLEs and TABLE_SHAREs. */
close_cached_tables(NULL, NULL, FALSE, LONG_TIMEOUT);
}
+ DBUG_VOID_RETURN;
}
@@ -725,7 +729,9 @@ get_table_share_with_discover(THD *thd, TABLE_LIST *table_list,
@todo Rework alternative ways to deal with ER_NO_SUCH TABLE.
*/
- if (share || (thd->is_error() && thd->stmt_da->sql_errno() != ER_NO_SUCH_TABLE))
+ if (share ||
+ (thd->is_error() && thd->stmt_da->sql_errno() != ER_NO_SUCH_TABLE &&
+ thd->stmt_da->sql_errno() != ER_NO_SUCH_TABLE_IN_ENGINE))
DBUG_RETURN(share);
*error= 0;
@@ -1090,6 +1096,9 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables,
mysql_mutex_unlock(&LOCK_open);
+ DBUG_PRINT("info", ("open table definitions: %d",
+ (int) table_def_cache.records));
+
if (!wait_for_refresh)
DBUG_RETURN(result);
@@ -3928,36 +3937,24 @@ static bool open_table_entry_fini(THD *thd, TABLE_SHARE *share, TABLE *entry)
entry->file->implicit_emptied= 0;
if (mysql_bin_log.is_open())
{
- char *query, *end;
- uint query_buf_size= 20 + share->db.length + share->table_name.length +1;
- if ((query= (char*) my_malloc(query_buf_size,MYF(MY_WME))))
- {
- /* this DELETE FROM is needed even with row-based binlogging */
- end = strxmov(strmov(query, "DELETE FROM `"),
- share->db.str,"`.`",share->table_name.str,"`", NullS);
- int errcode= query_error_code(thd, TRUE);
- if (thd->binlog_query(THD::STMT_QUERY_TYPE,
- query, (ulong)(end-query),
- FALSE, FALSE, FALSE, errcode))
- {
- my_free(query);
- return TRUE;
- }
- my_free(query);
- }
- else
- {
- /*
- As replication is maybe going to be corrupted, we need to warn the
- DBA on top of warning the client (which will automatically be done
- because of MYF(MY_WME) in my_malloc() above).
- */
- sql_print_error("When opening HEAP table, could not allocate memory "
- "to write 'DELETE FROM `%s`.`%s`' to the binary log",
- share->db.str, share->table_name.str);
- delete entry->triggers;
+ char query_buf[2*FN_REFLEN + 21];
+ String query(query_buf, sizeof(query_buf), system_charset_info);
+
+ query.length(0);
+ query.append("DELETE FROM ");
+ append_identifier(thd, &query, share->db.str, share->db.length);
+ query.append(".");
+ append_identifier(thd, &query, share->table_name.str,
+ share->table_name.length);
+
+ /*
+ we bypass thd->binlog_query() here,
+ as it does a lot of extra work, that is simply wrong in this case
+ */
+ Query_log_event qinfo(thd, query.ptr(), query.length(),
+ FALSE, TRUE, TRUE, 0);
+ if (mysql_bin_log.write(&qinfo))
return TRUE;
- }
}
}
return FALSE;
@@ -9490,6 +9487,7 @@ open_new_frm(THD *thd, TABLE_SHARE *share, const char *alias,
if (mysql_make_view(thd, parser, table_desc,
(prgflag & OPEN_VIEW_NO_PARSE)))
goto err;
+ status_var_increment(thd->status_var.opened_views);
}
else
{
diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc
index 354898a7cf2..256a51b9575 100644
--- a/sql/sql_cache.cc
+++ b/sql/sql_cache.cc
@@ -1539,7 +1539,7 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
unlock();
goto end;
}
- if (!register_all_tables(query_block, tables_used, local_tables))
+ if (!register_all_tables(thd, query_block, tables_used, local_tables))
{
refused++;
DBUG_PRINT("warning", ("tables list including failed"));
@@ -1819,7 +1819,10 @@ Query_cache::send_result_to_client(THD *thd, char *org_sql, uint query_length)
goto err;
if (query_cache_size == 0)
+ {
+ thd->query_cache_is_applicable= 0; // Query can't be cached
goto err_unlock;
+ }
Query_cache_block *query_block;
if (thd->variables.query_cache_strip_comments)
@@ -3212,6 +3215,7 @@ Query_cache::invalidate_query_block_list(THD *thd,
SYNOPSIS
Query_cache::register_tables_from_list
+ thd thread handle
tables_used given table list
counter number current position in table of tables of block
block_table pointer to current position in tables table of block
@@ -3222,24 +3226,24 @@ Query_cache::invalidate_query_block_list(THD *thd,
*/
TABLE_COUNTER_TYPE
-Query_cache::register_tables_from_list(TABLE_LIST *tables_used,
+Query_cache::register_tables_from_list(THD *thd, TABLE_LIST *tables_used,
TABLE_COUNTER_TYPE counter,
- Query_cache_block_table *block_table)
+ Query_cache_block_table **block_table)
{
TABLE_COUNTER_TYPE n;
DBUG_ENTER("Query_cache::register_tables_from_list");
for (n= counter;
tables_used;
- tables_used= tables_used->next_global, n++, block_table++)
+ tables_used= tables_used->next_global, n++, (*block_table)++)
{
if (tables_used->is_anonymous_derived_table())
{
DBUG_PRINT("qcache", ("derived table skipped"));
n--;
- block_table--;
+ (*block_table)--;
continue;
}
- block_table->n= n;
+ (*block_table)->n= n;
if (tables_used->view)
{
char key[MAX_DBKEY_LENGTH];
@@ -3252,9 +3256,9 @@ Query_cache::register_tables_from_list(TABLE_LIST *tables_used,
/*
There are not callback function for for VIEWs
*/
- if (!insert_table(key_length, key, block_table,
+ if (!insert_table(key_length, key, (*block_table),
tables_used->view_db.length + 1,
- HA_CACHE_TBL_NONTRANSACT, 0, 0))
+ HA_CACHE_TBL_NONTRANSACT, 0, 0, TRUE))
DBUG_RETURN(0);
/*
We do not need to register view tables here because they are already
@@ -3273,42 +3277,17 @@ Query_cache::register_tables_from_list(TABLE_LIST *tables_used,
if (!insert_table(tables_used->table->s->table_cache_key.length,
tables_used->table->s->table_cache_key.str,
- block_table,
+ (*block_table),
tables_used->db_length,
tables_used->table->file->table_cache_type(),
tables_used->callback_func,
- tables_used->engine_data))
+ tables_used->engine_data,
+ TRUE))
DBUG_RETURN(0);
-#ifdef WITH_MYISAMMRG_STORAGE_ENGINE
- /*
- XXX FIXME: Some generic mechanism is required here instead of this
- MYISAMMRG-specific implementation.
- */
- if (tables_used->table->s->db_type()->db_type == DB_TYPE_MRG_MYISAM)
- {
- ha_myisammrg *handler = (ha_myisammrg *) tables_used->table->file;
- MYRG_INFO *file = handler->myrg_info();
- for (MYRG_TABLE *table = file->open_tables;
- table != file->end_table ;
- table++)
- {
- char key[MAX_DBKEY_LENGTH];
- uint32 db_length;
- uint key_length= filename_2_table_key(key, table->table->filename,
- &db_length);
- (++block_table)->n= ++n;
- /*
- There are not callback function for for MyISAM, and engine data
- */
- if (!insert_table(key_length, key, block_table,
- db_length,
- tables_used->table->file->table_cache_type(),
- 0, 0))
- DBUG_RETURN(0);
- }
- }
-#endif
+ if (tables_used->table->file->
+ register_query_cache_dependant_tables(thd, this, block_table, &n))
+ DBUG_RETURN(0);
}
}
DBUG_RETURN(n - counter);
@@ -3319,12 +3298,14 @@ Query_cache::register_tables_from_list(TABLE_LIST *tables_used,
SYNOPSIS
register_all_tables()
+ thd Thread handle
block Store tables in this block
tables_used List if used tables
tables_arg Not used ?
*/
-my_bool Query_cache::register_all_tables(Query_cache_block *block,
+my_bool Query_cache::register_all_tables(THD *thd,
+ Query_cache_block *block,
TABLE_LIST *tables_used,
TABLE_COUNTER_TYPE tables_arg)
{
@@ -3335,7 +3316,7 @@ my_bool Query_cache::register_all_tables(Query_cache_block *block,
Query_cache_block_table *block_table = block->table(0);
- n= register_tables_from_list(tables_used, 0, block_table);
+ n= register_tables_from_list(thd, tables_used, 0, &block_table);
if (n==0)
{
@@ -3344,6 +3325,8 @@ my_bool Query_cache::register_all_tables(Query_cache_block *block,
tmp != block_table;
tmp++)
unlink_table(tmp);
+ if (block_table->parent)
+ unlink_table(block_table);
}
return test(n);
}
@@ -3362,7 +3345,8 @@ Query_cache::insert_table(uint key_len, char *key,
Query_cache_block_table *node,
uint32 db_length, uint8 cache_type,
qc_engine_callback callback,
- ulonglong engine_data)
+ ulonglong engine_data,
+ my_bool hash)
{
DBUG_ENTER("Query_cache::insert_table");
DBUG_PRINT("qcache", ("insert table node 0x%lx, len %d",
@@ -3370,8 +3354,10 @@ Query_cache::insert_table(uint key_len, char *key,
THD *thd= current_thd;
- Query_cache_block *table_block=
- (Query_cache_block *) my_hash_search(&tables, (uchar*) key, key_len);
+ Query_cache_block *table_block=
+ (hash ?
+ (Query_cache_block *) my_hash_search(&tables, (uchar*) key, key_len) :
+ NULL);
if (table_block &&
table_block->table()->engine_data() != engine_data)
@@ -3421,7 +3407,8 @@ Query_cache::insert_table(uint key_len, char *key,
*/
list_root->next= list_root->prev= list_root;
- if (my_hash_insert(&tables, (const uchar *) table_block))
+ if (hash &&
+ my_hash_insert(&tables, (const uchar *) table_block))
{
DBUG_PRINT("qcache", ("Can't insert table to hash"));
// write_block_data return locked block
@@ -3434,6 +3421,7 @@ Query_cache::insert_table(uint key_len, char *key,
header->type(cache_type);
header->callback(callback);
header->engine_data(engine_data);
+ header->set_hashed(hash);
/*
We insert this table without the assumption that it isn't refrenenced by
@@ -3487,7 +3475,9 @@ void Query_cache::unlink_table(Query_cache_block_table *node)
Query_cache_block *table_block= neighbour->block();
double_linked_list_exclude(table_block,
&tables_blocks);
- my_hash_delete(&tables,(uchar *) table_block);
+ Query_cache_table *header= table_block->table();
+ if (header->is_hashed())
+ my_hash_delete(&tables,(uchar *) table_block);
free_memory_block(table_block);
}
DBUG_VOID_RETURN;
@@ -3960,6 +3950,9 @@ Query_cache::process_and_count_tables(THD *thd, TABLE_LIST *tables_used,
table_alias_charset used here because it depends of
lower_case_table_names variable
*/
+ table_count+= tables_used->table->file->
+ count_query_cache_dependant_tables(tables_type);
+
if (tables_used->table->s->tmp_table != NO_TMP_TABLE ||
(*tables_type & HA_CACHE_TBL_NOCACHE) ||
(tables_used->db_length == 5 &&
@@ -3972,18 +3965,6 @@ Query_cache::process_and_count_tables(THD *thd, TABLE_LIST *tables_used,
"other non-cacheable table(s)"));
DBUG_RETURN(0);
}
-#ifdef WITH_MYISAMMRG_STORAGE_ENGINE
- /*
- XXX FIXME: Some generic mechanism is required here instead of this
- MYISAMMRG-specific implementation.
- */
- if (tables_used->table->s->db_type()->db_type == DB_TYPE_MRG_MYISAM)
- {
- ha_myisammrg *handler = (ha_myisammrg *)tables_used->table->file;
- MYRG_INFO *file = handler->myrg_info();
- table_count+= (file->end_table - file->open_tables);
- }
-#endif
}
}
DBUG_RETURN(table_count);
diff --git a/sql/sql_cache.h b/sql/sql_cache.h
index 87291e80a85..7444d444cf9 100644
--- a/sql/sql_cache.h
+++ b/sql/sql_cache.h
@@ -200,6 +200,10 @@ struct Query_cache_table
The number of queries depending of this table.
*/
int32 m_cached_query_count;
+ /**
+ If table included in the table hash to be found by other queries
+ */
+ my_bool hashed;
inline char *db() { return (char *) data(); }
inline char *table() { return tbl; }
@@ -212,6 +216,8 @@ struct Query_cache_table
inline void callback(qc_engine_callback fn){ callback_func= fn; }
inline ulonglong engine_data() { return engine_data_buff; }
inline void engine_data(ulonglong data_arg){ engine_data_buff= data_arg; }
+ inline my_bool is_hashed() { return hashed; }
+ inline void set_hashed(my_bool hash) { hashed= hash; }
inline uchar* data()
{
return (uchar*)(((uchar*)this)+
@@ -343,10 +349,6 @@ protected:
static void double_linked_list_join(Query_cache_block *head_tail,
Query_cache_block *tail_head);
- /* Table key generation */
- static uint filename_2_table_key (char *key, const char *filename,
- uint32 *db_langth);
-
/* The following functions require that structure_guard_mutex is locked */
void flush_cache();
my_bool free_old_query();
@@ -363,17 +365,12 @@ protected:
Query_cache_block_table *list_root);
TABLE_COUNTER_TYPE
- register_tables_from_list(TABLE_LIST *tables_used,
+ register_tables_from_list(THD *thd, TABLE_LIST *tables_used,
TABLE_COUNTER_TYPE counter,
- Query_cache_block_table *block_table);
- my_bool register_all_tables(Query_cache_block *block,
+ Query_cache_block_table **block_table);
+ my_bool register_all_tables(THD *thd, Query_cache_block *block,
TABLE_LIST *tables_used,
TABLE_COUNTER_TYPE tables);
- my_bool insert_table(uint key_len, char *key,
- Query_cache_block_table *node,
- uint32 db_length, uint8 cache_type,
- qc_engine_callback callback,
- ulonglong engine_data);
void unlink_table(Query_cache_block_table *node);
Query_cache_block *get_free_block (ulong len, my_bool not_less,
ulong min);
@@ -491,6 +488,12 @@ protected:
const char *packet,
ulong length,
unsigned pkt_nr);
+ my_bool insert_table(uint key_len, char *key,
+ Query_cache_block_table *node,
+ uint32 db_length, uint8 cache_type,
+ qc_engine_callback callback,
+ ulonglong engine_data,
+ my_bool hash);
void end_of_result(THD *thd);
void abort(Query_cache_tls *query_cache_tls);
@@ -513,6 +516,10 @@ protected:
const char *name);
my_bool in_blocks(Query_cache_block * point);
+ /* Table key generation */
+ static uint filename_2_table_key (char *key, const char *filename,
+ uint32 *db_langth);
+
enum Cache_try_lock_mode {WAIT, TIMEOUT, TRY};
bool try_lock(THD *thd, Cache_try_lock_mode mode= WAIT);
void lock(THD *thd);
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index a96fe63e3c9..ba1d46ec3cc 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -857,6 +857,9 @@ THD::THD()
progress.max_counter= 0;
current_linfo = 0;
slave_thread = 0;
+ connection_name.str= 0;
+ connection_name.length= 0;
+
bzero(&variables, sizeof(variables));
thread_id= 0;
one_shot_set= 0;
@@ -1224,7 +1227,14 @@ void THD::init(void)
avoid temporary tables replication failure.
*/
variables.pseudo_thread_id= thread_id;
+
+ variables.default_master_connection.str= default_master_connection_buff;
+ ::strmake(variables.default_master_connection.str,
+ global_system_variables.default_master_connection.str,
+ variables.default_master_connection.length);
+
mysql_mutex_unlock(&LOCK_global_system_variables);
+
server_status= SERVER_STATUS_AUTOCOMMIT;
if (variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES)
server_status|= SERVER_STATUS_NO_BACKSLASH_ESCAPES;
@@ -1255,6 +1265,7 @@ void THD::init(void)
/* Initialize the Debug Sync Facility. See debug_sync.cc. */
debug_sync_init_thread(this);
#endif /* defined(ENABLED_DEBUG_SYNC) */
+ apc_target.init(&LOCK_thd_data);
}
@@ -1420,6 +1431,7 @@ void THD::cleanup(void)
ull= NULL;
}
+ apc_target.destroy();
cleanup_done=1;
DBUG_VOID_RETURN;
}
@@ -2072,6 +2084,20 @@ CHANGED_TABLE_LIST* THD::changed_table_dup(const char *key, long key_length)
int THD::send_explain_fields(select_result *result)
{
List<Item> field_list;
+ make_explain_field_list(field_list);
+ return (result->send_result_set_metadata(field_list,
+ Protocol::SEND_NUM_ROWS |
+ Protocol::SEND_EOF));
+}
+
+
+/*
+ Populate the provided field_list with EXPLAIN output columns.
+ this->lex->describe has the EXPLAIN flags
+*/
+
+void THD::make_explain_field_list(List<Item> &field_list)
+{
Item *item;
CHARSET_INFO *cs= system_charset_info;
field_list.push_back(item= new Item_return_int("id",3, MYSQL_TYPE_LONGLONG));
@@ -2110,10 +2136,9 @@ int THD::send_explain_fields(select_result *result)
}
item->maybe_null= 1;
field_list.push_back(new Item_empty_string("Extra", 255, cs));
- return (result->send_result_set_metadata(field_list,
- Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF));
}
+
#ifdef SIGNAL_WITH_VIO_CLOSE
void THD::close_active_vio()
{
@@ -2345,6 +2370,7 @@ int select_send::send_data(List<Item> &items)
DBUG_RETURN(0);
}
+
bool select_send::send_eof()
{
/*
@@ -3238,6 +3264,10 @@ void THD::end_statement()
}
+/*
+ Start using arena specified by @set. Current arena data will be saved to
+ *backup.
+*/
void THD::set_n_backup_active_arena(Query_arena *set, Query_arena *backup)
{
DBUG_ENTER("THD::set_n_backup_active_arena");
@@ -3252,6 +3282,12 @@ void THD::set_n_backup_active_arena(Query_arena *set, Query_arena *backup)
}
+/*
+ Stop using the temporary arena, and start again using the arena that is
+ specified in *backup.
+ The temporary arena is returned back into *set.
+*/
+
void THD::restore_active_arena(Query_arena *set, Query_arena *backup)
{
DBUG_ENTER("THD::restore_active_arena");
@@ -3826,12 +3862,21 @@ void THD::restore_backup_open_tables_state(Open_tables_backup *backup)
@retval 1 the user thread has been killed
This is used to signal a storage engine if it should be killed.
+ See also THD::check_killed().
*/
extern "C" int thd_killed(const MYSQL_THD thd)
{
+ THD* current= current_thd;
if (!thd)
- thd= current_thd;
+ thd= current;
+
+ if (thd == current)
+ {
+ Apc_target *apc_target= (Apc_target*)&thd->apc_target;
+ if (apc_target->have_apc_requests())
+ apc_target->process_apc_requests();
+ }
if (!(thd->killed & KILL_HARD_BIT))
return 0;
@@ -5384,6 +5429,134 @@ show_query_type(THD::enum_binlog_query_type qtype)
}
#endif
+/*
+ Constants required for the limit unsafe warnings suppression
+*/
+//seconds after which the limit unsafe warnings suppression will be activated
+#define LIMIT_UNSAFE_WARNING_ACTIVATION_TIMEOUT 50
+//number of limit unsafe warnings after which the suppression will be activated
+#define LIMIT_UNSAFE_WARNING_ACTIVATION_THRESHOLD_COUNT 50
+
+static ulonglong limit_unsafe_suppression_start_time= 0;
+static bool unsafe_warning_suppression_is_activated= false;
+static int limit_unsafe_warning_count= 0;
+
+/**
+ Auxiliary function to reset the limit unsafety warning suppression.
+*/
+static void reset_binlog_unsafe_suppression()
+{
+ DBUG_ENTER("reset_binlog_unsafe_suppression");
+ unsafe_warning_suppression_is_activated= false;
+ limit_unsafe_warning_count= 0;
+ limit_unsafe_suppression_start_time= my_interval_timer()/10000000;
+ DBUG_VOID_RETURN;
+}
+
+/**
+ Auxiliary function to print warning in the error log.
+*/
+static void print_unsafe_warning_to_log(int unsafe_type, char* buf,
+ char* query)
+{
+ DBUG_ENTER("print_unsafe_warning_in_log");
+ sprintf(buf, ER(ER_BINLOG_UNSAFE_STATEMENT),
+ ER(LEX::binlog_stmt_unsafe_errcode[unsafe_type]));
+ sql_print_warning(ER(ER_MESSAGE_AND_STATEMENT), buf, query);
+ DBUG_VOID_RETURN;
+}
+
+/**
+ Auxiliary function to check if the warning for limit unsafety should be
+ thrown or suppressed. Details of the implementation can be found in the
+ comments inline.
+ SYNOPSIS:
+ @params
+ buf - buffer to hold the warning message text
+ unsafe_type - The type of unsafety.
+ query - The actual query statement.
+
+ TODO: Remove this function and implement a general service for all warnings
+ that would prevent flooding the error log.
+*/
+static void do_unsafe_limit_checkout(char* buf, int unsafe_type, char* query)
+{
+ ulonglong now= 0;
+ DBUG_ENTER("do_unsafe_limit_checkout");
+ DBUG_ASSERT(unsafe_type == LEX::BINLOG_STMT_UNSAFE_LIMIT);
+ limit_unsafe_warning_count++;
+ /*
+ INITIALIZING:
+ If this is the first time this function is called with log warning
+ enabled, the monitoring the unsafe warnings should start.
+ */
+ if (limit_unsafe_suppression_start_time == 0)
+ {
+ limit_unsafe_suppression_start_time= my_interval_timer()/10000000;
+ print_unsafe_warning_to_log(unsafe_type, buf, query);
+ }
+ else
+ {
+ if (!unsafe_warning_suppression_is_activated)
+ print_unsafe_warning_to_log(unsafe_type, buf, query);
+
+ if (limit_unsafe_warning_count >=
+ LIMIT_UNSAFE_WARNING_ACTIVATION_THRESHOLD_COUNT)
+ {
+ now= my_interval_timer()/10000000;
+ if (!unsafe_warning_suppression_is_activated)
+ {
+ /*
+ ACTIVATION:
+ We got LIMIT_UNSAFE_WARNING_ACTIVATION_THRESHOLD_COUNT warnings in
+ less than LIMIT_UNSAFE_WARNING_ACTIVATION_TIMEOUT we activate the
+ suppression.
+ */
+ if ((now-limit_unsafe_suppression_start_time) <=
+ LIMIT_UNSAFE_WARNING_ACTIVATION_TIMEOUT)
+ {
+ unsafe_warning_suppression_is_activated= true;
+ DBUG_PRINT("info",("A warning flood has been detected and the limit \
+unsafety warning suppression has been activated."));
+ }
+ else
+ {
+ /*
+ there is no flooding till now, therefore we restart the monitoring
+ */
+ limit_unsafe_suppression_start_time= my_interval_timer()/10000000;
+ limit_unsafe_warning_count= 0;
+ }
+ }
+ else
+ {
+ /*
+ Print the suppression note and the unsafe warning.
+ */
+ sql_print_information("The following warning was suppressed %d times \
+during the last %d seconds in the error log",
+ limit_unsafe_warning_count,
+ (int)
+ (now-limit_unsafe_suppression_start_time));
+ print_unsafe_warning_to_log(unsafe_type, buf, query);
+ /*
+ DEACTIVATION: We got LIMIT_UNSAFE_WARNING_ACTIVATION_THRESHOLD_COUNT
+ warnings in more than LIMIT_UNSAFE_WARNING_ACTIVATION_TIMEOUT, the
+ suppression should be deactivated.
+ */
+ if ((now - limit_unsafe_suppression_start_time) >
+ LIMIT_UNSAFE_WARNING_ACTIVATION_TIMEOUT)
+ {
+ reset_binlog_unsafe_suppression();
+ DBUG_PRINT("info",("The limit unsafety warning supression has been \
+deactivated"));
+ }
+ }
+ limit_unsafe_warning_count= 0;
+ }
+ }
+ DBUG_VOID_RETURN;
+}
/**
Auxiliary method used by @c binlog_query() to raise warnings.
@@ -5393,6 +5566,7 @@ show_query_type(THD::enum_binlog_query_type qtype)
*/
void THD::issue_unsafe_warnings()
{
+ char buf[MYSQL_ERRMSG_SIZE * 2];
DBUG_ENTER("issue_unsafe_warnings");
/*
Ensure that binlog_unsafe_warning_flags is big enough to hold all
@@ -5418,17 +5592,16 @@ void THD::issue_unsafe_warnings()
ER(LEX::binlog_stmt_unsafe_errcode[unsafe_type]));
if (global_system_variables.log_warnings)
{
- char buf[MYSQL_ERRMSG_SIZE * 2];
- sprintf(buf, ER(ER_BINLOG_UNSAFE_STATEMENT),
- ER(LEX::binlog_stmt_unsafe_errcode[unsafe_type]));
- sql_print_warning(ER(ER_MESSAGE_AND_STATEMENT), buf, query());
+ if (unsafe_type == LEX::BINLOG_STMT_UNSAFE_LIMIT)
+ do_unsafe_limit_checkout( buf, unsafe_type, query());
+ else //cases other than LIMIT unsafety
+ print_unsafe_warning_to_log(unsafe_type, buf, query());
}
}
}
DBUG_VOID_RETURN;
}
-
/**
Log the current query.
@@ -5604,3 +5777,4 @@ bool Discrete_intervals_list::append(Discrete_interval *new_interval)
}
#endif /* !defined(MYSQL_CLIENT) */
+
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 6c218a58109..c061e6c2bb2 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -55,6 +55,8 @@ void set_thd_stage_info(void *thd,
#define THD_STAGE_INFO(thd, stage) \
(thd)->enter_stage(& stage, NULL, __func__, __FILE__, __LINE__)
+#include "my_apc.h"
+
class Reprepare_observer;
class Relay_log_info;
@@ -159,9 +161,6 @@ public:
};
-#define TC_LOG_PAGE_SIZE 8192
-#define TC_LOG_MIN_SIZE (3*TC_LOG_PAGE_SIZE)
-
#define TC_HEURISTIC_RECOVER_COMMIT 1
#define TC_HEURISTIC_RECOVER_ROLLBACK 2
extern ulong tc_heuristic_recover;
@@ -546,6 +545,12 @@ typedef struct system_variables
thread the query is being run to replicate temp tables properly
*/
my_thread_id pseudo_thread_id;
+ /**
+ Place holders to store Multi-source variables in sys_var.cc during
+ update and show of variables.
+ */
+ ulong slave_skip_counter;
+ ulong max_relay_log_size;
/**
Default transaction access mode. READ ONLY (true) or READ WRITE (false).
@@ -574,6 +579,9 @@ typedef struct system_variables
CHARSET_INFO *collation_database;
CHARSET_INFO *collation_connection;
+ /* Names. These will be allocated in buffers in thd */
+ LEX_STRING default_master_connection;
+
/* Error messages */
MY_LOCALE *lc_messages;
/* Locale Support */
@@ -635,25 +643,17 @@ typedef struct system_status_var
ulong ha_savepoint_rollback_count;
ulong ha_external_lock_count;
-#if 0
- /* KEY_CACHE parts. These are copies of the original */
- ulong key_blocks_changed;
- ulong key_blocks_used;
- ulong key_cache_r_requests;
- ulong key_cache_read;
- ulong key_cache_w_requests;
- ulong key_cache_write;
- /* END OF KEY_CACHE parts */
-#endif
-
ulong net_big_packet_count;
ulong opened_tables;
ulong opened_shares;
+ ulong opened_views; /* +1 opening a view */
+
ulong select_full_join_count_;
ulong select_full_range_join_count_;
ulong select_range_count_;
ulong select_range_check_count_;
ulong select_scan_count_;
+ ulong executed_triggers;
ulong long_query_count;
ulong filesort_merge_passes_;
ulong filesort_range_count_;
@@ -668,6 +668,16 @@ typedef struct system_status_var
ulong com_stmt_reset;
ulong com_stmt_close;
+ /* Features used */
+ ulong feature_dynamic_columns; /* +1 when creating a dynamic column */
+ ulong feature_fulltext; /* +1 when MATCH is used */
+ ulong feature_gis; /* +1 opening a table with GIS features */
+ ulong feature_locale; /* +1 when LOCALE is set */
+ ulong feature_subquery; /* +1 when subqueries are used */
+ ulong feature_timezone; /* +1 when XPATH is used */
+ ulong feature_trigger; /* +1 opening a table with triggers */
+ ulong feature_xml; /* +1 when XPATH is used */
+
ulong empty_queries;
ulong access_denied_errors;
ulong lost_connections;
@@ -1536,6 +1546,11 @@ private:
extern "C" void my_message_sql(uint error, const char *str, myf MyFlags);
+class THD;
+#ifndef DBUG_OFF
+void dbug_serve_apcs(THD *thd, int n_calls);
+#endif
+
/**
@class THD
For each client connection we create a separate thread with THD serving as
@@ -1716,7 +1731,7 @@ public:
my_hrtime_t user_time;
// track down slow pthread_create
ulonglong prior_thr_create_utime, thr_create_utime;
- ulonglong start_utime, utime_after_lock;
+ ulonglong start_utime, utime_after_lock, utime_after_query;
// Process indicator
struct {
@@ -1877,6 +1892,7 @@ public:
MEM_ROOT mem_root; // Transaction-life memory allocation pool
void cleanup()
{
+ DBUG_ENTER("thd::cleanup");
changed_tables= 0;
savepoints= 0;
/*
@@ -1888,6 +1904,7 @@ public:
if (!xid_state.rm_error)
xid_state.xid.null();
free_root(&mem_root,MYF(MY_KEEP_PREALLOC));
+ DBUG_VOID_RETURN;
}
my_bool is_active()
{
@@ -2272,9 +2289,25 @@ public:
*/
killed_state volatile killed;
+ /* See also thd_killed() */
+ inline bool check_killed()
+ {
+ if (killed)
+ return TRUE;
+ if (apc_target.have_apc_requests())
+ apc_target.process_apc_requests();
+ return FALSE;
+ }
+
/* scramble - random string sent to client on handshake */
char scramble[SCRAMBLE_LENGTH+1];
+ /*
+ If this is a slave, the name of the connection stored here.
+ This is used for taging error messages in the log files.
+ */
+ LEX_STRING connection_name;
+ char default_master_connection_buff[MAX_CONNECTION_NAME+1];
bool slave_thread, one_shot_set;
bool extra_port; /* If extra connection */
@@ -2470,10 +2503,21 @@ public:
void close_active_vio();
#endif
void awake(killed_state state_to_set);
-
+
/** Disconnect the associated communication endpoint. */
void disconnect();
+
+ /*
+ Allows this thread to serve as a target for others to schedule Async
+ Procedure Calls on.
+
+ It's possible to schedule any code to be executed this way, by
+ inheriting from the Apc_call object. Currently, only
+ Show_explain_request uses this.
+ */
+ Apc_target apc_target;
+
#ifndef MYSQL_CLIENT
enum enum_binlog_query_type {
/* The query can be logged in row format or in statement format. */
@@ -2577,8 +2621,8 @@ public:
*/
void update_server_status()
{
- ulonglong end_utime_of_query= current_utime();
- if (end_utime_of_query > utime_after_lock + variables.long_query_time)
+ utime_after_query= current_utime();
+ if (utime_after_query > utime_after_lock + variables.long_query_time)
server_status|= SERVER_QUERY_WAS_SLOW;
}
inline ulonglong found_rows(void)
@@ -2676,7 +2720,7 @@ public:
void add_changed_table(const char *key, long key_length);
CHANGED_TABLE_LIST * changed_table_dup(const char *key, long key_length);
int send_explain_fields(select_result *result);
-
+ void make_explain_field_list(List<Item> &field_list);
/**
Clear the current error, if any.
We do not clear is_fatal_error or is_fatal_sub_stmt_error since we
@@ -2818,6 +2862,27 @@ public:
void set_n_backup_active_arena(Query_arena *set, Query_arena *backup);
void restore_active_arena(Query_arena *set, Query_arena *backup);
+ inline void get_binlog_format(enum_binlog_format *format,
+ enum_binlog_format *current_format)
+ {
+ *format= (enum_binlog_format) variables.binlog_format;
+ *current_format= current_stmt_binlog_format;
+ }
+ inline void set_binlog_format(enum_binlog_format format,
+ enum_binlog_format current_format)
+ {
+ DBUG_ENTER("set_binlog_format");
+ variables.binlog_format= format;
+ current_stmt_binlog_format= current_format;
+ DBUG_VOID_RETURN;
+ }
+ inline void set_binlog_format_stmt()
+ {
+ DBUG_ENTER("set_binlog_format_stmt");
+ variables.binlog_format= BINLOG_FORMAT_STMT;
+ current_stmt_binlog_format= BINLOG_FORMAT_STMT;
+ DBUG_VOID_RETURN;
+ }
/*
@todo Make these methods private or remove them completely. Only
decide_logging_format should call them. /Sven
@@ -2848,16 +2913,26 @@ public:
DBUG_VOID_RETURN;
}
+
inline void set_current_stmt_binlog_format_row()
{
DBUG_ENTER("set_current_stmt_binlog_format_row");
current_stmt_binlog_format= BINLOG_FORMAT_ROW;
DBUG_VOID_RETURN;
}
- inline void clear_current_stmt_binlog_format_row()
+ /* Set binlog format temporarily to statement. Returns old format */
+ inline enum_binlog_format set_current_stmt_binlog_format_stmt()
{
- DBUG_ENTER("clear_current_stmt_binlog_format_row");
+ enum_binlog_format orig_format= current_stmt_binlog_format;
+ DBUG_ENTER("set_current_stmt_binlog_format_stmt");
current_stmt_binlog_format= BINLOG_FORMAT_STMT;
+ DBUG_RETURN(orig_format);
+ }
+ inline void restore_stmt_binlog_format(enum_binlog_format format)
+ {
+ DBUG_ENTER("restore_stmt_binlog_format");
+ DBUG_ASSERT(!is_current_stmt_binlog_format_row());
+ current_stmt_binlog_format= format;
DBUG_VOID_RETURN;
}
inline void reset_current_stmt_binlog_format_row()
@@ -2887,7 +2962,7 @@ public:
if (variables.binlog_format == BINLOG_FORMAT_ROW)
set_current_stmt_binlog_format_row();
else if (temporary_tables == NULL)
- clear_current_stmt_binlog_format_row();
+ set_current_stmt_binlog_format_stmt();
}
DBUG_VOID_RETURN;
}
@@ -3138,7 +3213,7 @@ public:
if (global_system_variables.log_warnings > threshold)
{
Security_context *sctx= &main_security_ctx;
- sql_print_warning(ER(ER_NEW_ABORTING_CONNECTION),
+ sql_print_warning(ER_THD(this, ER_NEW_ABORTING_CONNECTION),
thread_id, (db ? db : "unconnected"),
sctx->user ? sctx->user : "unauthenticated",
sctx->host_or_ip, reason);
@@ -3303,10 +3378,42 @@ public:
class JOIN;
-class select_result :public Sql_alloc {
+/* Pure interface for sending tabular data */
+class select_result_sink: public Sql_alloc
+{
+public:
+ /*
+ send_data returns 0 on ok, 1 on error and -1 if data was ignored, for
+ example for a duplicate row entry written to a temp table.
+ */
+ virtual int send_data(List<Item> &items)=0;
+ virtual ~select_result_sink() {};
+};
+
+
+/*
+ Interface for sending tabular data, together with some other stuff:
+
+ - Primary purpose seems to be seding typed tabular data:
+ = the DDL is sent with send_fields()
+ = the rows are sent with send_data()
+ Besides that,
+ - there seems to be an assumption that the sent data is a result of
+ SELECT_LEX_UNIT *unit,
+ - nest_level is used by SQL parser
+*/
+
+class select_result :public select_result_sink
+{
protected:
THD *thd;
+ /*
+ All descendant classes have their send_data() skip the first
+ unit->offset_limit_cnt rows sent. Select_materialize
+ also uses unit->get_unit_column_types().
+ */
SELECT_LEX_UNIT *unit;
+ /* Something used only by the parser: */
public:
select_result();
virtual ~select_result() {};
@@ -3324,11 +3431,6 @@ public:
virtual uint field_count(List<Item> &fields) const
{ return fields.elements; }
virtual bool send_result_set_metadata(List<Item> &list, uint flags)=0;
- /*
- send_data returns 0 on ok, 1 on error and -1 if data was ignored, for
- example for a duplicate row entry written to a temp table.
- */
- virtual int send_data(List<Item> &items)=0;
virtual bool initialize_tables (JOIN *join=0) { return 0; }
virtual void send_error(uint errcode,const char *err);
virtual bool send_eof()=0;
@@ -3356,6 +3458,32 @@ public:
/*
+ This is a select_result_sink which simply writes all data into a (temporary)
+ table. Creation/deletion of the table is outside of the scope of the class
+
+ It is aimed at capturing SHOW EXPLAIN output, so:
+ - Unlike select_result class, we don't assume that the sent data is an
+ output of a SELECT_LEX_UNIT (and so we dont apply "LIMIT x,y" from the
+ unit)
+ - We don't try to convert the target table to MyISAM
+*/
+
+class select_result_explain_buffer : public select_result_sink
+{
+public:
+ select_result_explain_buffer(THD *thd_arg, TABLE *table_arg) :
+ thd(thd_arg), dst_table(table_arg) {};
+
+ THD *thd;
+ TABLE *dst_table; /* table to write into */
+
+ /* The following is called in the child thread: */
+ int send_data(List<Item> &items);
+};
+
+
+
+/*
Base class for select_result descendands which intercept and
transform result set rows. As the rows are not sent to the client,
sending of result set metadata should be suppressed as well.
@@ -3932,6 +4060,8 @@ class user_var_entry
DTCollation collation;
};
+user_var_entry *get_variable(HASH *hash, LEX_STRING &name,
+ bool create_if_not_exists);
/*
Unique -- class for unique (removing of duplicates).
@@ -4225,6 +4355,11 @@ public:
*/
#define CF_DISALLOW_IN_RO_TRANS (1U << 15)
+/**
+ Statement that need the binlog format to be unchanged.
+*/
+#define CF_FORCE_ORIGINAL_BINLOG_FORMAT (1U << 16)
+
/* Bits in server_command_flags */
/**
diff --git a/sql/sql_cmd.h b/sql/sql_cmd.h
index 29b478bfb52..5cf88d2566c 100644
--- a/sql/sql_cmd.h
+++ b/sql/sql_cmd.h
@@ -90,6 +90,8 @@ enum enum_sql_command {
SQLCOM_SHOW_RELAYLOG_EVENTS,
SQLCOM_SHOW_USER_STATS, SQLCOM_SHOW_TABLE_STATS, SQLCOM_SHOW_INDEX_STATS,
SQLCOM_SHOW_CLIENT_STATS,
+ SQLCOM_SLAVE_ALL_START, SQLCOM_SLAVE_ALL_STOP,
+ SQLCOM_SHOW_EXPLAIN,
/*
When a command is added here, be sure it's also added in mysqld.cc
diff --git a/sql/sql_const.h b/sql/sql_const.h
index cfbf077bd91..255a0162e93 100644
--- a/sql/sql_const.h
+++ b/sql/sql_const.h
@@ -38,6 +38,7 @@
#define MAX_REFLENGTH 4 /* Max length for record ref */
#endif
#define MAX_HOSTNAME 61 /* len+1 in mysql.user */
+#define MAX_CONNECTION_NAME NAME_LEN
#define MAX_MBWIDTH 3 /* Max multibyte sequence */
#define MAX_FIELD_CHARLENGTH 255
diff --git a/sql/sql_cursor.cc b/sql/sql_cursor.cc
index fd348e0b75f..6a664e4d20b 100644
--- a/sql/sql_cursor.cc
+++ b/sql/sql_cursor.cc
@@ -118,6 +118,8 @@ int mysql_open_cursor(THD *thd, select_result *result,
2);
parent_locker= thd->m_statement_psi;
thd->m_statement_psi= NULL;
+ /* Mark that we can't use query cache with cursors */
+ thd->query_cache_is_applicable= 0;
rc= mysql_execute_command(thd);
thd->m_statement_psi= parent_locker;
MYSQL_QUERY_EXEC_DONE(rc);
diff --git a/sql/sql_db.cc b/sql/sql_db.cc
index 052616f6965..5704b6d51b6 100644
--- a/sql/sql_db.cc
+++ b/sql/sql_db.cc
@@ -544,7 +544,6 @@ int mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create_info,
bool silent)
{
char path[FN_REFLEN+16];
- char tmp_query[FN_REFLEN+16];
long result= 1;
int error= 0;
MY_STAT stat_info;
@@ -622,17 +621,9 @@ not_silent:
char *query;
uint query_length;
- if (!thd->query()) // Only in replication
- {
- query= tmp_query;
- query_length= (uint) (strxmov(tmp_query,"create database `",
- db, "`", NullS) - tmp_query);
- }
- else
- {
- query= thd->query();
- query_length= thd->query_length();
- }
+ query= thd->query();
+ query_length= thd->query_length();
+ DBUG_ASSERT(query);
ha_binlog_log_query(thd, 0, LOGCOM_CREATE_DB,
query, query_length,
@@ -885,18 +876,11 @@ update_binlog:
{
const char *query;
ulong query_length;
- if (!thd->query())
- {
- /* The client used the old obsolete mysql_drop_db() call */
- query= path;
- query_length= (uint) (strxmov(path, "drop database `", db, "`",
- NullS) - path);
- }
- else
- {
- query= thd->query();
- query_length= thd->query_length();
- }
+
+ query= thd->query();
+ query_length= thd->query_length();
+ DBUG_ASSERT(query);
+
if (mysql_bin_log.is_open())
{
int errcode= query_error_code(thd, TRUE);
@@ -940,6 +924,7 @@ update_binlog:
{
uint tbl_name_len;
bool exists;
+ char quoted_name[FN_REFLEN+3];
// Only write drop table to the binlog for tables that no longer exist.
if (check_if_table_exists(thd, tbl, &exists))
@@ -950,8 +935,8 @@ update_binlog:
if (exists)
continue;
- /* 3 for the quotes and the comma*/
- tbl_name_len= strlen(tbl->table_name) + 3;
+ my_snprintf(quoted_name, sizeof(quoted_name), "%`s", tbl->table_name);
+ tbl_name_len= strlen(quoted_name) + 1; /* +1 for the comma */
if (query_pos + tbl_name_len + 1 >= query_end)
{
/*
@@ -966,9 +951,7 @@ update_binlog:
query_pos= query_data_start;
}
- *query_pos++ = '`';
- query_pos= strmov(query_pos,tbl->table_name);
- *query_pos++ = '`';
+ query_pos= strmov(query_pos, quoted_name);
*query_pos++ = ',';
}
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index c87c9382630..0f07f01c6fd 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -244,6 +244,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
uint length= 0;
SORT_FIELD *sortorder;
ha_rows examined_rows;
+ ha_rows found_rows;
table->update_const_key_parts(conds);
order= simple_remove_const(order, conds);
@@ -264,9 +265,10 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
MYF(MY_FAE | MY_ZEROFILL));
if (!(sortorder= make_unireg_sortorder(order, &length, NULL)) ||
- (table->sort.found_records = filesort(thd, table, sortorder, length,
- select, HA_POS_ERROR, 1,
- &examined_rows))
+ (table->sort.found_records= filesort(thd, table, sortorder, length,
+ select, HA_POS_ERROR,
+ true,
+ &examined_rows, &found_rows))
== HA_POS_ERROR)
{
delete select;
@@ -368,8 +370,14 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
}
}
}
- else
+ /*
+ Don't try unlocking the row if skip_record reported an error since in
+ this case the transaction might have been rolled back already.
+ */
+ else if (!thd->is_error())
table->file->unlock_row(); // Row failed selection, release lock on it
+ else
+ break;
}
killed_status= thd->killed;
if (killed_status != NOT_KILLED || thd->is_error())
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index 023792dddd0..599a892da9f 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -329,7 +329,7 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list,
/*
- Check update fields for the timestamp field.
+ Check update fields for the timestamp and auto_increment fields.
SYNOPSIS
check_update_fields()
@@ -342,6 +342,9 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list,
If the update fields include the timestamp field,
remove TIMESTAMP_AUTO_SET_ON_UPDATE from table->timestamp_field_type.
+ If the update fields include an autoinc field, set the
+ table->next_number_field_updated flag.
+
RETURN
0 OK
-1 Error
@@ -353,7 +356,9 @@ static int check_update_fields(THD *thd, TABLE_LIST *insert_table_list,
{
TABLE *table= insert_table_list->table;
my_bool timestamp_mark;
+ my_bool autoinc_mark;
LINT_INIT(timestamp_mark);
+ LINT_INIT(autoinc_mark);
if (table->timestamp_field)
{
@@ -365,6 +370,19 @@ static int check_update_fields(THD *thd, TABLE_LIST *insert_table_list,
table->timestamp_field->field_index);
}
+ table->next_number_field_updated= FALSE;
+
+ if (table->found_next_number_field)
+ {
+ /*
+ Unmark the auto_increment field so that we can check if this is modified
+ by update_fields
+ */
+ autoinc_mark= bitmap_test_and_clear(table->write_set,
+ table->found_next_number_field->
+ field_index);
+ }
+
/* Check the fields we are going to modify */
if (setup_fields(thd, 0, update_fields, MARK_COLUMNS_WRITE, 0, 0))
return -1;
@@ -386,6 +404,18 @@ static int check_update_fields(THD *thd, TABLE_LIST *insert_table_list,
bitmap_set_bit(table->write_set,
table->timestamp_field->field_index);
}
+
+ if (table->found_next_number_field)
+ {
+ if (bitmap_is_set(table->write_set,
+ table->found_next_number_field->field_index))
+ table->next_number_field_updated= TRUE;
+
+ if (autoinc_mark)
+ bitmap_set_bit(table->write_set,
+ table->found_next_number_field->field_index);
+ }
+
return 0;
}
@@ -827,10 +857,10 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
table->next_number_field=table->found_next_number_field;
#ifdef HAVE_REPLICATION
- if (thd->slave_thread &&
+ if (thd->rli_slave &&
(info.handle_duplicates == DUP_UPDATE) &&
(table->next_number_field != NULL) &&
- rpl_master_has_bug(&active_mi->rli, 24432, TRUE, NULL, NULL))
+ rpl_master_has_bug(thd->rli_slave, 24432, TRUE, NULL, NULL))
goto abort;
#endif
@@ -1570,6 +1600,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
MY_BITMAP *save_read_set, *save_write_set;
ulonglong prev_insert_id= table->file->next_insert_id;
ulonglong insert_id_for_cur_row= 0;
+ ulonglong prev_insert_id_for_cur_row= 0;
DBUG_ENTER("write_record");
info->records++;
@@ -1605,7 +1636,10 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
report error as usual. We will not do any duplicate key processing.
*/
if (info->ignore)
+ {
+ table->file->print_error(error, MYF(ME_JUST_WARNING));
goto ok_or_after_trg_err; /* Ignoring a not fatal error, return 0 */
+ }
goto err;
}
if ((int) (key_nr = table->file->get_dup_key(error)) < 0)
@@ -1695,6 +1729,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
if (info->ignore &&
!table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
{
+ table->file->print_error(error, MYF(ME_JUST_WARNING));
goto ok_or_after_trg_err;
}
goto err;
@@ -1712,6 +1747,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
INSERT query, which is handled separately by
THD::arg_of_last_insert_id_function.
*/
+ prev_insert_id_for_cur_row= table->file->insert_id_for_cur_row;
insert_id_for_cur_row= table->file->insert_id_for_cur_row= 0;
trg_error= (table->triggers &&
table->triggers->process_triggers(thd, TRG_EVENT_UPDATE,
@@ -1719,9 +1755,22 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
info->copied++;
}
- if (table->next_number_field)
- table->file->adjust_next_insert_id_after_explicit_value(
- table->next_number_field->val_int());
+ /*
+ Only update next_insert_id if the AUTO_INCREMENT value was explicitly
+ updated, so we don't update next_insert_id with the value from the
+ row being updated. Otherwise reset next_insert_id to what it was
+ before the duplicate key error, since that value is unused.
+ */
+ if (table->next_number_field_updated)
+ {
+ DBUG_ASSERT(table->next_number_field != NULL);
+
+ table->file->adjust_next_insert_id_after_explicit_value(table->next_number_field->val_int());
+ }
+ else
+ {
+ table->file->restore_auto_increment(prev_insert_id_for_cur_row);
+ }
goto ok_or_after_trg_err;
}
else /* DUP_REPLACE */
@@ -1810,6 +1859,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
if (!info->ignore ||
table->file->is_fatal_error(error, HA_CHECK_DUP))
goto err;
+ table->file->print_error(error, MYF(ME_JUST_WARNING));
table->file->restore_auto_increment(prev_insert_id);
goto ok_or_after_trg_err;
}
@@ -2788,9 +2838,6 @@ pthread_handler_t handle_delayed_insert(void *arg)
{
int error;
mysql_audit_release(thd);
-#if defined(HAVE_BROKEN_COND_TIMEDWAIT)
- error= mysql_cond_wait(&di->cond, &di->mutex);
-#else
error= mysql_cond_timedwait(&di->cond, &di->mutex, &abstime);
#ifdef EXTRA_DEBUG
if (error && error != EINTR && error != ETIMEDOUT)
@@ -2800,7 +2847,6 @@ pthread_handler_t handle_delayed_insert(void *arg)
error));
}
#endif
-#endif
if (error == ETIMEDOUT || error == ETIME)
thd->killed= KILL_CONNECTION;
}
@@ -3395,10 +3441,10 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
table->next_number_field=table->found_next_number_field;
#ifdef HAVE_REPLICATION
- if (thd->slave_thread &&
+ if (thd->rli_slave &&
(info.handle_duplicates == DUP_UPDATE) &&
(table->next_number_field != NULL) &&
- rpl_master_has_bug(&active_mi->rli, 24432, TRUE, NULL, NULL))
+ rpl_master_has_bug(thd->rli_slave, 24432, TRUE, NULL, NULL))
DBUG_RETURN(1);
#endif
diff --git a/sql/sql_join_cache.cc b/sql/sql_join_cache.cc
index f953cf4df57..4148842a826 100644
--- a/sql/sql_join_cache.cc
+++ b/sql/sql_join_cache.cc
@@ -2236,7 +2236,7 @@ enum_nested_loop_state JOIN_CACHE::join_matching_records(bool skip_last)
while (!(error= join_tab_scan->next()))
{
- if (join->thd->killed)
+ if (join->thd->check_killed())
{
/* The user has aborted the execution of the query */
join->thd->send_kill_message();
@@ -2506,7 +2506,7 @@ enum_nested_loop_state JOIN_CACHE::join_null_complements(bool skip_last)
for ( ; cnt; cnt--)
{
- if (join->thd->killed)
+ if (join->thd->check_killed())
{
/* The user has aborted the execution of the query */
join->thd->send_kill_message();
@@ -3356,7 +3356,7 @@ int JOIN_TAB_SCAN::next()
update_virtual_fields(thd, table);
while (!err && select && (skip_rc= select->skip_record(thd)) <= 0)
{
- if (thd->killed || skip_rc < 0)
+ if (thd->check_killed() || skip_rc < 0)
return 1;
/*
Move to the next record if the last retrieved record does not
@@ -3876,8 +3876,9 @@ int JOIN_TAB_SCAN_MRR::next()
If a record in in an incremental cache contains no fields then the
association for the last record in cache will be equal to cache->end_pos
*/
- DBUG_ASSERT(cache->buff <= (uchar *) (*ptr) &&
- (uchar *) (*ptr) <= cache->end_pos);
+ DBUG_ASSERT((!(mrr_mode & HA_MRR_NO_ASSOCIATION))?
+ (cache->buff <= (uchar *) (*ptr) &&
+ (uchar *) (*ptr) <= cache->end_pos): TRUE);
if (join_tab->table->vfield)
update_virtual_fields(join->thd, join_tab->table);
}
@@ -4543,7 +4544,7 @@ bool JOIN_CACHE_BKAH::prepare_look_for_matches(bool skip_last)
{
last_matching_rec_ref_ptr= next_matching_rec_ref_ptr= 0;
if (no_association &&
- (curr_matching_chain= get_matching_chain_by_join_key()))
+ !(curr_matching_chain= get_matching_chain_by_join_key()))
return 1;
last_matching_rec_ref_ptr= get_next_rec_ref(curr_matching_chain);
return 0;
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 5f18d9861a9..3c183d95b42 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -503,6 +503,7 @@ void lex_start(THD *thd)
lex->expr_allows_subselect= TRUE;
lex->use_only_table_context= FALSE;
lex->parse_vcol_expr= FALSE;
+ lex->verbose= 0;
lex->name.str= 0;
lex->name.length= 0;
@@ -1889,6 +1890,7 @@ void st_select_lex::init_query()
nest_level= 0;
link_next= 0;
is_prep_leaf_list_saved= FALSE;
+ have_merged_subqueries= FALSE;
bzero((char*) expr_cache_may_be_used, sizeof(expr_cache_may_be_used));
m_non_agg_field_used= false;
m_agg_func_used= false;
@@ -3480,7 +3482,7 @@ bool st_select_lex::optimize_unflattened_subqueries(bool const_only)
if (options & SELECT_DESCRIBE)
{
/* Optimize the subquery in the context of EXPLAIN. */
- sl->set_explain_type();
+ sl->set_explain_type(FALSE);
sl->options|= SELECT_DESCRIBE;
inner_join->select_options|= SELECT_DESCRIBE;
}
@@ -3930,9 +3932,12 @@ void st_select_lex::update_correlated_cache()
/**
Set the EXPLAIN type for this subquery.
+
+ @param on_the_fly TRUE<=> We're running a SHOW EXPLAIN command, so we must
+ not change any variables
*/
-void st_select_lex::set_explain_type()
+void st_select_lex::set_explain_type(bool on_the_fly)
{
bool is_primary= FALSE;
if (next_select())
@@ -3954,6 +3959,9 @@ void st_select_lex::set_explain_type()
}
}
+ if (on_the_fly && !is_primary && have_merged_subqueries)
+ is_primary= TRUE;
+
SELECT_LEX *first= master_unit()->first_select();
/* drop UNCACHEABLE_EXPLAIN, because it is for internal usage only */
uint8 is_uncacheable= (uncacheable & ~UNCACHEABLE_EXPLAIN);
@@ -4006,10 +4014,15 @@ void st_select_lex::set_explain_type()
else
{
type= is_uncacheable ? "UNCACHEABLE UNION": "UNION";
+ if (this == master_unit()->fake_select_lex)
+ type= "UNION RESULT";
+
}
}
}
- options|= SELECT_DESCRIBE;
+
+ if (!on_the_fly)
+ options|= SELECT_DESCRIBE;
}
@@ -4156,6 +4169,116 @@ bool st_select_lex::is_merged_child_of(st_select_lex *ancestor)
}
+int print_explain_message_line(select_result_sink *result,
+ SELECT_LEX *select_lex,
+ bool on_the_fly,
+ uint8 options,
+ const char *message);
+
+
+int st_select_lex::print_explain(select_result_sink *output,
+ uint8 explain_flags,
+ bool *printed_anything)
+{
+ int res;
+ if (join && join->have_query_plan == JOIN::QEP_AVAILABLE)
+ {
+ /*
+ There is a number of reasons join can be marked as degenerate, so all
+ three conditions below can happen simultaneously, or individually:
+ */
+ *printed_anything= TRUE;
+ if (!join->table_count || !join->tables_list || join->zero_result_cause)
+ {
+ /* It's a degenerate join */
+ const char *cause= join->zero_result_cause ? join-> zero_result_cause :
+ "No tables used";
+ res= join->print_explain(output, explain_flags, TRUE, FALSE, FALSE,
+ FALSE, cause);
+ }
+ else
+ {
+ res= join->print_explain(output, explain_flags, TRUE,
+ join->need_tmp, // need_tmp_table
+ !join->skip_sort_order && !join->no_order &&
+ (join->order || join->group_list), // bool need_order
+ join->select_distinct, // bool distinct
+ NULL); //const char *message
+ }
+ if (res)
+ goto err;
+
+ for (SELECT_LEX_UNIT *unit= join->select_lex->first_inner_unit();
+ unit;
+ unit= unit->next_unit())
+ {
+ /*
+ Display subqueries only if they are not parts of eliminated WHERE/ON
+ clauses.
+ */
+ if (!(unit->item && unit->item->eliminated))
+ {
+ if ((res= unit->print_explain(output, explain_flags, printed_anything)))
+ goto err;
+ }
+ }
+ }
+ else
+ {
+ const char *msg;
+ if (!join)
+ DBUG_ASSERT(0); /* Seems not to be possible */
+
+ /* Not printing anything useful, don't touch *printed_anything here */
+ if (join->have_query_plan == JOIN::QEP_NOT_PRESENT_YET)
+ msg= "Not yet optimized";
+ else
+ {
+ DBUG_ASSERT(join->have_query_plan == JOIN::QEP_DELETED);
+ msg= "Query plan already deleted";
+ }
+ res= print_explain_message_line(output, this, TRUE /* on_the_fly */,
+ 0, msg);
+ }
+err:
+ return res;
+}
+
+
+int st_select_lex_unit::print_explain(select_result_sink *output,
+ uint8 explain_flags, bool *printed_anything)
+{
+ int res= 0;
+ SELECT_LEX *first= first_select();
+
+ if (first && !first->next_select() && !first->join)
+ {
+ /*
+ If there is only one child, 'first', and it has join==NULL, emit "not in
+ EXPLAIN state" error.
+ */
+ const char *msg="Query plan already deleted";
+ res= print_explain_message_line(output, first, TRUE /* on_the_fly */,
+ 0, msg);
+ return 0;
+ }
+
+ for (SELECT_LEX *sl= first; sl; sl= sl->next_select())
+ {
+ if ((res= sl->print_explain(output, explain_flags, printed_anything)))
+ break;
+ }
+
+ /* Note: fake_select_lex->join may be NULL or non-NULL at this point */
+ if (fake_select_lex)
+ {
+ res= print_fake_select_lex_join(output, TRUE /* on the fly */,
+ fake_select_lex, explain_flags);
+ }
+ return res;
+}
+
+
/**
A routine used by the parser to decide whether we are specifying a full
partitioning or if only partitions to add or to split.
@@ -4174,7 +4297,6 @@ bool LEX::is_partition_management() const
alter_info.flags == ALTER_REORGANIZE_PARTITION));
}
-
#ifdef MYSQL_SERVER
uint binlog_unsafe_map[256];
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 3dd978e93b3..fd789143c94 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -209,6 +209,7 @@ struct LEX_MASTER_INFO
char *ssl_key, *ssl_cert, *ssl_ca, *ssl_capath, *ssl_cipher;
char *ssl_crl, *ssl_crlpath;
char *relay_log_name;
+ LEX_STRING connection_name;
ulonglong pos;
ulong relay_log_pos;
ulong server_id;
@@ -278,6 +279,8 @@ typedef uchar index_clause_map;
#define INDEX_HINT_MASK_ALL (INDEX_HINT_MASK_JOIN | INDEX_HINT_MASK_GROUP | \
INDEX_HINT_MASK_ORDER)
+class select_result_sink;
+
/* Single element of an USE/FORCE/IGNORE INDEX list specified as a SQL hint */
class Index_hint : public Sql_alloc
{
@@ -638,6 +641,8 @@ public:
friend int subselect_union_engine::exec();
List<Item> *get_unit_column_types();
+ int print_explain(select_result_sink *output, uint8 explain_flags,
+ bool *printed_anything);
};
typedef class st_select_lex_unit SELECT_LEX_UNIT;
@@ -698,6 +703,12 @@ public:
those converted to jtbm nests. The list is emptied when conversion is done.
*/
List<Item_in_subselect> sj_subselects;
+
+ /*
+ Needed to correctly generate 'PRIMARY' or 'SIMPLE' for select_type column
+ of EXPLAIN
+ */
+ bool have_merged_subqueries;
List<TABLE_LIST> leaf_tables;
List<TABLE_LIST> leaf_tables_exec;
@@ -922,7 +933,7 @@ public:
bool is_part_of_union() { return master_unit()->is_union(); }
bool optimize_unflattened_subqueries(bool const_only);
/* Set the EXPLAIN type for this subquery. */
- void set_explain_type();
+ void set_explain_type(bool on_the_fly);
bool handle_derived(LEX *lex, uint phases);
void append_table_to_list(TABLE_LIST *TABLE_LIST::*link, TABLE_LIST *table);
bool get_free_table_map(table_map *map, uint *tablenr);
@@ -946,8 +957,10 @@ public:
bool save_leaf_tables(THD *thd);
bool save_prep_leaf_tables(THD *thd);
- bool is_merged_child_of(st_select_lex *ancestor);
+ bool is_merged_child_of(st_select_lex *ancestor);
+ int print_explain(select_result_sink *output, uint8 explain_flags,
+ bool *printed_anything);
/*
For MODE_ONLY_FULL_GROUP_BY we need to maintain two flags:
- Non-aggregated fields are used in this select.
@@ -2274,7 +2287,7 @@ struct LEX: public Query_tables_list
char *backup_dir; /* For RESTORE/BACKUP */
char* to_log; /* For PURGE MASTER LOGS TO */
char* x509_subject,*x509_issuer,*ssl_cipher;
- String *wild;
+ String *wild; /* Wildcard in SHOW {something} LIKE 'wild'*/
sql_exchange *exchange;
select_result *result;
Item *default_value, *on_update_value;
@@ -2344,6 +2357,7 @@ struct LEX: public Query_tables_list
KEY_CREATE_INFO key_create_info;
LEX_MASTER_INFO mi; // used by CHANGE MASTER
LEX_SERVER_OPTIONS server_options;
+ LEX_STRING relay_log_connection_name;
USER_RESOURCES mqh;
LEX_RESET_SLAVE reset_slave_info;
ulong type;
diff --git a/sql/sql_load.cc b/sql/sql_load.cc
index 33d1741b5ec..e2c744401b9 100644
--- a/sql/sql_load.cc
+++ b/sql/sql_load.cc
@@ -39,6 +39,7 @@
#include "sp_head.h"
#include "sql_trigger.h"
#include "sql_derived.h"
+#include "sql_show.h"
class XML_TAG {
public:
@@ -374,11 +375,11 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
MY_RETURN_REAL_PATH);
}
- if (thd->slave_thread)
+ if (thd->rli_slave)
{
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
- if (strncmp(active_mi->rli.slave_patternload_file, name,
- active_mi->rli.slave_patternload_file_size))
+ if (strncmp(thd->rli_slave->slave_patternload_file, name,
+ thd->rli_slave->slave_patternload_file_size))
{
/*
LOAD DATA INFILE in the slave SQL Thread can only read from
@@ -675,24 +676,28 @@ static bool write_execute_load_query_log_event(THD *thd, sql_exchange* ex,
bool transactional_table,
int errcode)
{
- char *load_data_query,
- *end,
- *fname_start,
- *fname_end,
- *p= NULL;
- size_t pl= 0;
+ char *load_data_query;
+ my_off_t fname_start,
+ fname_end;
List<Item> fv;
Item *item, *val;
int n;
- const char *tbl= table_name_arg;
const char *tdb= (thd->db != NULL ? thd->db : db_arg);
- char name_buffer[SAFE_NAME_LEN*2];
+ const char *qualify_db= NULL;
char command_buffer[1024];
- String string_buf(name_buffer, sizeof(name_buffer),
- system_charset_info);
- String pfields(command_buffer, sizeof(command_buffer),
+ String query_str(command_buffer, sizeof(command_buffer),
system_charset_info);
+ Load_log_event lle(thd, ex, tdb, table_name_arg, fv, is_concurrent,
+ duplicates, ignore, transactional_table);
+
+ /*
+ force in a LOCAL if there was one in the original.
+ */
+ if (thd->lex->local_file)
+ lle.set_fname_outside_temp_buf(ex->file_name, strlen(ex->file_name));
+
+ query_str.length(0);
if (!thd->db || strcmp(db_arg, thd->db))
{
/*
@@ -700,49 +705,35 @@ static bool write_execute_load_query_log_event(THD *thd, sql_exchange* ex,
prefix table name with database name so that it
becomes a FQ name.
*/
- string_buf.length(0);
- string_buf.append(db_arg);
- string_buf.append("`");
- string_buf.append(".");
- string_buf.append("`");
- string_buf.append(table_name_arg);
- tbl= string_buf.c_ptr_safe();
+ qualify_db= db_arg;
}
-
- Load_log_event lle(thd, ex, tdb, tbl, fv, is_concurrent,
- duplicates, ignore, transactional_table);
-
- /*
- force in a LOCAL if there was one in the original.
- */
- if (thd->lex->local_file)
- lle.set_fname_outside_temp_buf(ex->file_name, strlen(ex->file_name));
+ lle.print_query(thd, FALSE, (const char *) ex->cs?ex->cs->csname:NULL,
+ &query_str, &fname_start, &fname_end, qualify_db);
/*
prepare fields-list and SET if needed; print_query won't do that for us.
*/
- pfields.length(0);
if (!thd->lex->field_list.is_empty())
{
List_iterator<Item> li(thd->lex->field_list);
- pfields.append(" (");
+ query_str.append(" (");
n= 0;
while ((item= li++))
{
if (n++)
- pfields.append(", ");
+ query_str.append(", ");
if (item->type() == Item::FIELD_ITEM)
+ append_identifier(thd, &query_str, item->name, strlen(item->name));
+ else
{
- pfields.append("`");
- pfields.append(item->name);
- pfields.append("`");
+ /* Actually Item_user_var_as_out_param despite claiming STRING_ITEM. */
+ DBUG_ASSERT(item->type() == Item::STRING_ITEM);
+ ((Item_user_var_as_out_param *)item)->print_for_load(thd, &query_str);
}
- else
- item->print(&pfields, QT_ORDINARY);
}
- pfields.append(")");
+ query_str.append(")");
}
if (!thd->lex->update_list.is_empty())
@@ -750,38 +741,25 @@ static bool write_execute_load_query_log_event(THD *thd, sql_exchange* ex,
List_iterator<Item> lu(thd->lex->update_list);
List_iterator<Item> lv(thd->lex->value_list);
- pfields.append(" SET ");
+ query_str.append(" SET ");
n= 0;
while ((item= lu++))
{
val= lv++;
if (n++)
- pfields.append(", ");
- pfields.append("`");
- pfields.append(item->name);
- pfields.append("`");
- pfields.append(val->name);
+ query_str.append(", ");
+ append_identifier(thd, &query_str, item->name, strlen(item->name));
+ query_str.append(val->name);
}
}
- p= pfields.c_ptr_safe();
- pl= pfields.length();
-
- if (!(load_data_query= (char *)thd->alloc(lle.get_query_buffer_length() + 1 + pl)))
+ if (!(load_data_query= (char *)thd->strmake(query_str.ptr(), query_str.length())))
return TRUE;
- lle.print_query(FALSE, (const char *) ex->cs?ex->cs->csname:NULL,
- load_data_query, &end,
- (char **)&fname_start, (char **)&fname_end);
-
- strcpy(end, p);
- end += pl;
-
Execute_load_query_log_event
- e(thd, load_data_query, end-load_data_query,
- (uint) ((char*) fname_start - load_data_query - 1),
- (uint) ((char*) fname_end - load_data_query),
+ e(thd, load_data_query, query_str.length(),
+ (uint) (fname_start - 1), (uint) fname_end,
(duplicates == DUP_REPLACE) ? LOAD_DUP_REPLACE :
(ignore ? LOAD_DUP_IGNORE : LOAD_DUP_ERROR),
transactional_table, FALSE, FALSE, errcode);
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 551e1ee1924..1a499081fcb 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -82,6 +82,7 @@
#include <myisam.h>
#include <my_dir.h>
#include "rpl_handler.h"
+#include "rpl_mi.h"
#include "sp_head.h"
#include "sp.h"
@@ -364,6 +365,7 @@ void init_update_queries(void)
sql_command_flags[SQLCOM_SHOW_ENGINE_STATUS]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_ENGINE_MUTEX]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_ENGINE_LOGS]= CF_STATUS_COMMAND;
+ sql_command_flags[SQLCOM_SHOW_EXPLAIN]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_PROCESSLIST]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_GRANTS]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_CREATE_DB]= CF_STATUS_COMMAND;
@@ -379,12 +381,7 @@ void init_update_queries(void)
sql_command_flags[SQLCOM_SHOW_CREATE_EVENT]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_PROFILES]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_PROFILE]= CF_STATUS_COMMAND;
- /*
- @todo SQLCOM_BINLOG_BASE64_EVENT should have
- CF_CAN_GENERATE_ROW_EVENTS set, because this surely generates row
- events. /Sven
- */
- sql_command_flags[SQLCOM_BINLOG_BASE64_EVENT]= CF_STATUS_COMMAND;
+ sql_command_flags[SQLCOM_BINLOG_BASE64_EVENT]= CF_STATUS_COMMAND | CF_CAN_GENERATE_ROW_EVENTS;
sql_command_flags[SQLCOM_SHOW_CLIENT_STATS]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_USER_STATS]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_TABLE_STATS]= CF_STATUS_COMMAND;
@@ -431,6 +428,19 @@ void init_update_queries(void)
sql_command_flags[SQLCOM_EXECUTE]= CF_CAN_GENERATE_ROW_EVENTS;
/*
+ We don't want to change to statement based replication for these commands
+ */
+ sql_command_flags[SQLCOM_ROLLBACK]|= CF_FORCE_ORIGINAL_BINLOG_FORMAT;
+ /* We don't want to replicate ALTER TABLE for temp tables in row format */
+ sql_command_flags[SQLCOM_ALTER_TABLE]|= CF_FORCE_ORIGINAL_BINLOG_FORMAT;
+ /* We don't want to replicate TRUNCATE for temp tables in row format */
+ sql_command_flags[SQLCOM_TRUNCATE]|= CF_FORCE_ORIGINAL_BINLOG_FORMAT;
+ /* We don't want to replicate DROP for temp tables in row format */
+ sql_command_flags[SQLCOM_DROP_TABLE]|= CF_FORCE_ORIGINAL_BINLOG_FORMAT;
+ /* One can change replication mode with SET */
+ sql_command_flags[SQLCOM_SET_OPTION]|= CF_FORCE_ORIGINAL_BINLOG_FORMAT;
+
+ /*
The following admin table operations are allowed
on log tables.
*/
@@ -1449,8 +1459,12 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
}
else
#endif
- if (reload_acl_and_cache(thd, options, (TABLE_LIST*) 0, &not_used))
- break;
+ {
+ thd->lex->relay_log_connection_name.str= (char*) "";
+ thd->lex->relay_log_connection_name.length= 0;
+ if (reload_acl_and_cache(thd, options, (TABLE_LIST*) 0, &not_used))
+ break;
+ }
if (trans_commit_implicit(thd))
break;
close_thread_tables(thd);
@@ -1491,7 +1505,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
{
STATUS_VAR *current_global_status_var; // Big; Don't allocate on stack
ulong uptime;
- uint __attribute__((unused)) length;
+ uint length __attribute__((unused));
ulonglong queries_per_second1000;
char buff[250];
uint buff_len= sizeof(buff);
@@ -1591,6 +1605,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
(thd->open_tables == NULL ||
(thd->locked_tables_mode == LTM_LOCK_TABLES)));
+ thd_proc_info(thd, "updating status");
/* Finalize server status flags after executing a command. */
thd->update_server_status();
thd->protocol->end_statement();
@@ -1652,37 +1667,29 @@ void log_slow_statement(THD *thd)
DBUG_VOID_RETURN; // Don't set time for sub stmt
/* Follow the slow log filter configuration. */
- if (!(thd->variables.log_slow_filter & thd->query_plan_flags))
+ if (!thd->enable_slow_log ||
+ !(thd->variables.log_slow_filter & thd->query_plan_flags))
DBUG_VOID_RETURN;
- /*
- If rate limiting of slow log writes is enabled, decide whether to log
- this query to the log or not.
- */
- if (thd->variables.log_slow_rate_limit > 1 &&
- (global_query_id % thd->variables.log_slow_rate_limit) != 0)
- DBUG_VOID_RETURN;
-
- /*
- Do not log administrative statements unless the appropriate option is
- set.
- */
- if (thd->enable_slow_log)
- {
- ulonglong end_utime_of_query= thd->current_utime();
+ if (((thd->server_status & SERVER_QUERY_WAS_SLOW) ||
+ ((thd->server_status &
+ (SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED)) &&
+ opt_log_queries_not_using_indexes &&
+ !(sql_command_flags[thd->lex->sql_command] & CF_STATUS_COMMAND))) &&
+ thd->get_examined_row_count() >= thd->variables.min_examined_row_limit)
+ {
+ thd->status_var.long_query_count++;
+ /*
+ If rate limiting of slow log writes is enabled, decide whether to log
+ this query to the log or not.
+ */
+ if (thd->variables.log_slow_rate_limit > 1 &&
+ (global_query_id % thd->variables.log_slow_rate_limit) != 0)
+ DBUG_VOID_RETURN;
- if (((thd->server_status & SERVER_QUERY_WAS_SLOW) ||
- ((thd->server_status &
- (SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED)) &&
- opt_log_queries_not_using_indexes &&
- !(sql_command_flags[thd->lex->sql_command] & CF_STATUS_COMMAND))) &&
- thd->get_examined_row_count() >= thd->variables.min_examined_row_limit)
- {
- THD_STAGE_INFO(thd, stage_logging_slow_query);
- thd->status_var.long_query_count++;
- slow_log_print(thd, thd->query(), thd->query_length(),
- end_utime_of_query);
- }
+ THD_STAGE_INFO(thd, stage_logging_slow_query);
+ slow_log_print(thd, thd->query(), thd->query_length(),
+ thd->utime_after_query);
}
DBUG_VOID_RETURN;
}
@@ -2271,6 +2278,22 @@ mysql_execute_command(THD *thd)
DBUG_ASSERT(thd->transaction.stmt.modified_non_trans_table == FALSE);
+ /* store old value of binlog format */
+ enum_binlog_format orig_binlog_format,orig_current_stmt_binlog_format;
+
+ thd->get_binlog_format(&orig_binlog_format,
+ &orig_current_stmt_binlog_format);
+
+ /*
+ Force statement logging for DDL commands to allow us to update
+ privilege, system or statistic tables directly without the updates
+ getting logged.
+ */
+ if (!(sql_command_flags[lex->sql_command] &
+ (CF_CAN_GENERATE_ROW_EVENTS | CF_FORCE_ORIGINAL_BINLOG_FORMAT |
+ CF_STATUS_COMMAND)))
+ thd->set_binlog_format_stmt();
+
/*
End a active transaction so that this command will have it's
own transaction and will also sync the binary log. If a DDL is
@@ -2330,6 +2353,33 @@ mysql_execute_command(THD *thd)
execute_show_status(thd, all_tables);
break;
}
+ case SQLCOM_SHOW_EXPLAIN:
+ {
+ if (!thd->security_ctx->priv_user[0] &&
+ check_global_access(thd,PROCESS_ACL))
+ break;
+
+ /*
+ The select should use only one table, it's the SHOW EXPLAIN pseudo-table
+ */
+ if (lex->sroutines.records || lex->query_tables->next_global)
+ {
+ my_message(ER_SET_CONSTANTS_ONLY, ER(ER_SET_CONSTANTS_ONLY),
+ MYF(0));
+ goto error;
+ }
+
+ Item **it= lex->value_list.head_ref();
+ if (!(*it)->basic_const_item() ||
+ (!(*it)->fixed && (*it)->fix_fields(lex->thd, it)) ||
+ (*it)->check_cols(1))
+ {
+ my_message(ER_SET_CONSTANTS_ONLY, ER(ER_SET_CONSTANTS_ONLY),
+ MYF(0));
+ goto error;
+ }
+ /* no break; fall through */
+ }
case SQLCOM_SHOW_DATABASES:
case SQLCOM_SHOW_TABLES:
case SQLCOM_SHOW_TRIGGERS:
@@ -2503,10 +2553,42 @@ case SQLCOM_PREPARE:
#ifdef HAVE_REPLICATION
case SQLCOM_CHANGE_MASTER:
{
+ LEX_MASTER_INFO *lex_mi= &thd->lex->mi;
+ Master_info *mi;
+ bool new_master= 0;
+
if (check_global_access(thd, SUPER_ACL))
goto error;
mysql_mutex_lock(&LOCK_active_mi);
- res = change_master(thd,active_mi);
+
+ mi= master_info_index->get_master_info(&lex_mi->connection_name,
+ MYSQL_ERROR::WARN_LEVEL_NOTE);
+
+ if (mi == NULL)
+ {
+ /* New replication created */
+ mi= new Master_info(&lex_mi->connection_name, relay_log_recovery);
+ if (!mi || mi->error())
+ {
+ delete mi;
+ res= 1;
+ mysql_mutex_unlock(&LOCK_active_mi);
+ break;
+ }
+ new_master= 1;
+ }
+
+ res= change_master(thd, mi);
+ if (res && new_master)
+ {
+ /*
+ The new master was added by change_master(). Remove it as it didn't
+ work.
+ */
+ master_info_index->remove_master_info(&lex_mi->connection_name);
+ delete mi;
+ }
+
mysql_mutex_unlock(&LOCK_active_mi);
break;
}
@@ -2516,15 +2598,19 @@ case SQLCOM_PREPARE:
if (check_global_access(thd, SUPER_ACL | REPL_CLIENT_ACL))
goto error;
mysql_mutex_lock(&LOCK_active_mi);
- if (active_mi != NULL)
- {
- res = show_master_info(thd, active_mi);
- }
+
+ if (lex->verbose)
+ res= show_all_master_info(thd);
else
{
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- WARN_NO_MASTER_INFO, ER(WARN_NO_MASTER_INFO));
- my_ok(thd);
+ LEX_MASTER_INFO *lex_mi= &thd->lex->mi;
+ Master_info *mi;
+ mi= master_info_index->get_master_info(&lex_mi->connection_name,
+ MYSQL_ERROR::WARN_LEVEL_ERROR);
+ if (mi != NULL)
+ {
+ res= show_master_info(thd, mi, 0);
+ }
}
mysql_mutex_unlock(&LOCK_active_mi);
break;
@@ -2824,40 +2910,79 @@ end_with_restore_list:
#ifdef HAVE_REPLICATION
case SQLCOM_SLAVE_START:
{
+ LEX_MASTER_INFO* lex_mi= &thd->lex->mi;
+ Master_info *mi;
mysql_mutex_lock(&LOCK_active_mi);
- start_slave(thd,active_mi,1 /* net report*/);
+
+ if ((mi= (master_info_index->
+ get_master_info(&lex_mi->connection_name,
+ MYSQL_ERROR::WARN_LEVEL_ERROR))))
+ if (!start_slave(thd, mi, 1 /* net report*/))
+ my_ok(thd);
mysql_mutex_unlock(&LOCK_active_mi);
break;
}
case SQLCOM_SLAVE_STOP:
- /*
- If the client thread has locked tables, a deadlock is possible.
- Assume that
- - the client thread does LOCK TABLE t READ.
- - then the master updates t.
- - then the SQL slave thread wants to update t,
- so it waits for the client thread because t is locked by it.
+ {
+ LEX_MASTER_INFO *lex_mi;
+ Master_info *mi;
+ /*
+ If the client thread has locked tables, a deadlock is possible.
+ Assume that
+ - the client thread does LOCK TABLE t READ.
+ - then the master updates t.
+ - then the SQL slave thread wants to update t,
+ so it waits for the client thread because t is locked by it.
- then the client thread does SLAVE STOP.
SLAVE STOP waits for the SQL slave thread to terminate its
update t, which waits for the client thread because t is locked by it.
- To prevent that, refuse SLAVE STOP if the
- client thread has locked tables
- */
- if (thd->locked_tables_mode ||
- thd->in_active_multi_stmt_transaction() || thd->global_read_lock.is_acquired())
+ To prevent that, refuse SLAVE STOP if the
+ client thread has locked tables
+ */
+ if (thd->locked_tables_mode ||
+ thd->in_active_multi_stmt_transaction() ||
+ thd->global_read_lock.is_acquired())
+ {
+ my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
+ ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
+ goto error;
+ }
+
+ lex_mi= &thd->lex->mi;
+ mysql_mutex_lock(&LOCK_active_mi);
+ if ((mi= (master_info_index->
+ get_master_info(&lex_mi->connection_name,
+ MYSQL_ERROR::WARN_LEVEL_ERROR))))
+ if (!stop_slave(thd, mi, 1/* net report*/))
+ my_ok(thd);
+ mysql_mutex_unlock(&LOCK_active_mi);
+ break;
+ }
+ case SQLCOM_SLAVE_ALL_START:
{
- my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
- ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
- goto error;
+ mysql_mutex_lock(&LOCK_active_mi);
+ if (!master_info_index->start_all_slaves(thd))
+ my_ok(thd);
+ mysql_mutex_unlock(&LOCK_active_mi);
+ break;
}
+ case SQLCOM_SLAVE_ALL_STOP:
{
+ if (thd->locked_tables_mode ||
+ thd->in_active_multi_stmt_transaction() ||
+ thd->global_read_lock.is_acquired())
+ {
+ my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
+ ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
+ goto error;
+ }
mysql_mutex_lock(&LOCK_active_mi);
- stop_slave(thd,active_mi,1/* net report*/);
+ if (!master_info_index->stop_all_slaves(thd))
+ my_ok(thd);
mysql_mutex_unlock(&LOCK_active_mi);
break;
}
#endif /* HAVE_REPLICATION */
-
case SQLCOM_RENAME_TABLE:
{
if (execute_rename_table(thd, first_table, all_tables))
@@ -3127,6 +3252,7 @@ end_with_restore_list:
DBUG_ASSERT(!debug_sync_set_action(thd,
STRING_WITH_LEN(act2)));
};);
+ DEBUG_SYNC(thd, "after_mysql_insert");
break;
}
case SQLCOM_REPLACE_SELECT:
@@ -4714,6 +4840,11 @@ finish:
if (lex->sql_command != SQLCOM_SET_OPTION && ! thd->in_sub_stmt)
DEBUG_SYNC(thd, "execute_command_after_close_tables");
#endif
+ if (!(sql_command_flags[lex->sql_command] &
+ (CF_CAN_GENERATE_ROW_EVENTS | CF_FORCE_ORIGINAL_BINLOG_FORMAT |
+ CF_STATUS_COMMAND)))
+ thd->set_binlog_format(orig_binlog_format,
+ orig_current_stmt_binlog_format);
if (stmt_causes_implicit_commit(thd, CF_IMPLICIT_COMMIT_END))
{
@@ -6755,23 +6886,17 @@ void add_join_natural(TABLE_LIST *a, TABLE_LIST *b, List<String> *using_fields,
/**
- kill on thread.
+ Find a thread by id and return it, locking it LOCK_thd_data
- @param thd Thread class
- @param id Thread id
- @param only_kill_query Should it kill the query or the connection
+ @param id Identifier of the thread we're looking for
- @note
- This is written such that we have a short lock on LOCK_thread_count
+ @return NULL - not found
+ pointer - thread found, and its LOCK_thd_data is locked.
*/
-uint kill_one_thread(THD *thd, ulong id, killed_state kill_signal)
+THD *find_thread_by_id(ulong id)
{
THD *tmp;
- uint error=ER_NO_SUCH_THREAD;
- DBUG_ENTER("kill_one_thread");
- DBUG_PRINT("enter", ("id: %lu signal: %u", id, (uint) kill_signal));
-
mysql_mutex_lock(&LOCK_thread_count); // For unlink from list
I_List_iterator<THD> it(threads);
while ((tmp=it++))
@@ -6785,7 +6910,29 @@ uint kill_one_thread(THD *thd, ulong id, killed_state kill_signal)
}
}
mysql_mutex_unlock(&LOCK_thread_count);
- if (tmp)
+ return tmp;
+}
+
+
+/**
+ kill on thread.
+
+ @param thd Thread class
+ @param id Thread id
+ @param only_kill_query Should it kill the query or the connection
+
+ @note
+ This is written such that we have a short lock on LOCK_thread_count
+*/
+
+uint kill_one_thread(THD *thd, ulong id, killed_state kill_signal)
+{
+ THD *tmp;
+ uint error=ER_NO_SUCH_THREAD;
+ DBUG_ENTER("kill_one_thread");
+ DBUG_PRINT("enter", ("id: %lu signal: %u", id, (uint) kill_signal));
+
+ if ((tmp= find_thread_by_id(id)))
{
/*
If we're SUPER, we can KILL anything, including system-threads.
diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc
index a9c79589faa..2bec12e4f66 100644
--- a/sql/sql_partition.cc
+++ b/sql/sql_partition.cc
@@ -5500,10 +5500,12 @@ static bool mysql_change_partitions(ALTER_PARTITION_PARAM_TYPE *lpt)
build_table_filename(path, sizeof(path) - 1, lpt->db, lpt->table_name, "", 0);
- if(mysql_trans_prepare_alter_copy_data(thd))
+ /* First lock the original tables */
+ if (file->ha_external_lock(thd, F_WRLCK))
DBUG_RETURN(TRUE);
- if (file->ha_external_lock(thd, F_WRLCK))
+ /* Disable transactions for all new tables */
+ if (mysql_trans_prepare_alter_copy_data(thd))
DBUG_RETURN(TRUE);
/* TODO: test if bulk_insert would increase the performance */
diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc
index 1059103086a..f19b8ca26ef 100644
--- a/sql/sql_plugin.cc
+++ b/sql/sql_plugin.cc
@@ -907,7 +907,7 @@ bool plugin_is_ready(const LEX_STRING *name, int type)
}
-SHOW_COMP_OPTION plugin_status(const char *name, int len, size_t type)
+SHOW_COMP_OPTION plugin_status(const char *name, size_t len, int type)
{
LEX_STRING plugin_name= { (char *) name, len };
return plugin_status(&plugin_name, type);
diff --git a/sql/sql_plugin.h b/sql/sql_plugin.h
index eaa368f3515..be1cfcdcc4f 100644
--- a/sql/sql_plugin.h
+++ b/sql/sql_plugin.h
@@ -1,5 +1,5 @@
-/*
- Copyright (c) 2005, 2010, Oracle and/or its affiliates.
+/* Copyright (c) 2005, 2012, Oracle and/or its affiliates.
+ Copyright (c) 2009, 2012, 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
@@ -165,7 +165,7 @@ extern bool mysql_uninstall_plugin(THD *thd, const LEX_STRING *name,
extern bool plugin_register_builtin(struct st_mysql_plugin *plugin);
extern void plugin_thdvar_init(THD *thd);
extern void plugin_thdvar_cleanup(THD *thd);
-extern SHOW_COMP_OPTION plugin_status(const char *name, int len, size_t type);
+extern SHOW_COMP_OPTION plugin_status(const char *name, size_t len, int type);
extern bool check_valid_path(const char *path, size_t length);
typedef my_bool (plugin_foreach_func)(THD *thd,
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 778d8ba6708..75611a054f0 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -2134,6 +2134,7 @@ static bool check_prepared_statement(Prepared_statement *stmt)
Note that we don't need to have cases in this list if they are
marked with CF_STATUS_COMMAND in sql_command_flags
*/
+ case SQLCOM_SHOW_EXPLAIN:
case SQLCOM_DROP_TABLE:
case SQLCOM_RENAME_TABLE:
case SQLCOM_ALTER_TABLE:
@@ -2151,6 +2152,8 @@ static bool check_prepared_statement(Prepared_statement *stmt)
case SQLCOM_FLUSH:
case SQLCOM_SLAVE_START:
case SQLCOM_SLAVE_STOP:
+ case SQLCOM_SLAVE_ALL_START:
+ case SQLCOM_SLAVE_ALL_STOP:
case SQLCOM_INSTALL_PLUGIN:
case SQLCOM_UNINSTALL_PLUGIN:
case SQLCOM_CREATE_DB:
diff --git a/sql/sql_priv.h b/sql/sql_priv.h
index 749e65e0902..a1c0b2d8729 100644
--- a/sql/sql_priv.h
+++ b/sql/sql_priv.h
@@ -331,6 +331,7 @@ enum enum_yes_no_unknown
External variables
*/
+
/* sql_yacc.cc */
#ifndef DBUG_OFF
extern void turn_parser_debug_on();
diff --git a/sql/sql_profile.cc b/sql/sql_profile.cc
index b3348698a35..2d00a19870b 100644
--- a/sql/sql_profile.cc
+++ b/sql/sql_profile.cc
@@ -1,5 +1,5 @@
-/* Copyright (c) 2007, 2011, Oracle and/or its affiliates.
- Copyright (c) 2008, 2011, Monty Program Ab
+/* Copyright (c) 2007, 2012, Oracle and/or its affiliates.
+ Copyright (c) 2008, 2012, 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
@@ -88,8 +88,8 @@ ST_FIELD_INFO query_profile_statistics_info[]=
int make_profile_table_for_show(THD *thd, ST_SCHEMA_TABLE *schema_table)
{
- int profile_options = thd->lex->profile_options;
- int fields_include_condition_truth_values[]= {
+ uint profile_options = thd->lex->profile_options;
+ uint fields_include_condition_truth_values[]= {
FALSE, /* Query_id */
FALSE, /* Seq */
TRUE, /* Status */
diff --git a/sql/sql_profile.h b/sql/sql_profile.h
index 2e93912fb25..7705f6ca476 100644
--- a/sql/sql_profile.h
+++ b/sql/sql_profile.h
@@ -287,5 +287,5 @@ public:
int fill_statistics_info(THD *thd, TABLE_LIST *tables, Item *cond);
};
-# endif /* HAVE_PROFILING */
+# endif /* ENABLED_PROFILING */
#endif /* _SQL_PROFILE_H */
diff --git a/sql/sql_reload.cc b/sql/sql_reload.cc
index 914b9026014..99fe9267589 100644
--- a/sql/sql_reload.cc
+++ b/sql/sql_reload.cc
@@ -26,6 +26,7 @@
#include "sql_repl.h" // reset_master, reset_slave
#include "rpl_mi.h" // Master_info::data_lock
#include "debug_sync.h"
+#include "rpl_mi.h"
static void disable_checkpoints(THD *thd);
@@ -157,10 +158,36 @@ bool reload_acl_and_cache(THD *thd, unsigned long options,
if (options & REFRESH_RELAY_LOG)
{
#ifdef HAVE_REPLICATION
- mysql_mutex_lock(&active_mi->data_lock);
- if (rotate_relay_log(active_mi))
- *write_to_binlog= -1;
- mysql_mutex_unlock(&active_mi->data_lock);
+ LEX_STRING connection_name;
+ Master_info *mi;
+ if (thd)
+ connection_name= thd->lex->relay_log_connection_name;
+ else
+ {
+ connection_name.str= (char*) "";
+ connection_name.length= 0;
+ }
+
+ /*
+ Writing this command to the binlog may cause problems as the
+ slave is not likely to have the same connection names.
+ */
+ tmp_write_to_binlog= 0;
+ mysql_mutex_lock(&LOCK_active_mi);
+ if (!(mi= (master_info_index->
+ get_master_info(&connection_name,
+ MYSQL_ERROR::WARN_LEVEL_ERROR))))
+ {
+ result= 1;
+ }
+ else
+ {
+ mysql_mutex_lock(&mi->data_lock);
+ if (rotate_relay_log(mi))
+ *write_to_binlog= -1;
+ mysql_mutex_unlock(&mi->data_lock);
+ }
+ mysql_mutex_unlock(&LOCK_active_mi);
#endif
}
#ifdef HAVE_QUERY_CACHE
@@ -314,13 +341,27 @@ bool reload_acl_and_cache(THD *thd, unsigned long options,
#ifdef HAVE_REPLICATION
if (options & REFRESH_SLAVE)
{
+ LEX_MASTER_INFO* lex_mi= &thd->lex->mi;
+ Master_info *mi;
tmp_write_to_binlog= 0;
mysql_mutex_lock(&LOCK_active_mi);
- if (reset_slave(thd, active_mi))
+
+ if (!(mi= (master_info_index->
+ get_master_info(&lex_mi->connection_name,
+ MYSQL_ERROR::WARN_LEVEL_ERROR))))
+ {
+ result= 1;
+ }
+ else if (reset_slave(thd, mi))
{
/* NOTE: my_error() has been already called by reset_slave(). */
result= 1;
}
+ else if (mi->connection_name.length && thd->lex->reset_slave_info.all)
+ {
+ /* If not default connection and 'all' is used */
+ master_info_index->remove_master_info(&mi->connection_name);
+ }
mysql_mutex_unlock(&LOCK_active_mi);
}
#endif
diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc
index 039dd046086..db8dc694502 100644
--- a/sql/sql_repl.cc
+++ b/sql/sql_repl.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2000, 2012, Oracle and/or its affiliates.
- Copyright (c) 2008, 2011, Monty Program Ab
+ Copyright (c) 2008, 2012, 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
@@ -34,14 +34,6 @@ my_bool opt_sporadic_binlog_dump_fail = 0;
static int binlog_dump_count = 0;
#endif
-/**
- a copy of active_mi->rli->slave_skip_counter, for showing in SHOW VARIABLES,
- INFORMATION_SCHEMA.GLOBAL_VARIABLES and @@sql_slave_skip_counter without
- taking all the mutexes needed to access active_mi->rli->slave_skip_counter
- properly.
-*/
-uint sql_slave_skip_counter;
-
extern TYPELIB binlog_checksum_typelib;
/*
@@ -492,6 +484,27 @@ static ulonglong get_heartbeat_period(THD * thd)
}
/*
+ Lookup the capabilities of the slave, which it announces by setting a value
+ MARIA_SLAVE_CAPABILITY_XXX in @mariadb_slave_capability.
+
+ Older MariaDB slaves, and other MySQL slaves, do not set
+ @mariadb_slave_capability, corresponding to a capability of
+ MARIA_SLAVE_CAPABILITY_UNKNOWN (0).
+*/
+static int
+get_mariadb_slave_capability(THD *thd)
+{
+ bool null_value;
+ const LEX_STRING name= { C_STRING_WITH_LEN("mariadb_slave_capability") };
+ const user_var_entry *entry=
+ (user_var_entry*) my_hash_search(&thd->user_vars, (uchar*) name.str,
+ name.length);
+ return entry ?
+ (int)(entry->val_int(&null_value)) : MARIA_SLAVE_CAPABILITY_UNKNOWN;
+}
+
+
+/*
Function prepares and sends repliation heartbeat event.
@param net net object of THD
@@ -563,14 +576,68 @@ static int send_heartbeat_event(NET* net, String* packet,
static const char *
send_event_to_slave(THD *thd, NET *net, String* const packet, ushort flags,
Log_event_type event_type, char *log_file_name,
- IO_CACHE *log)
+ IO_CACHE *log, int mariadb_slave_capability,
+ ulong ev_offset, uint8 current_checksum_alg)
{
my_off_t pos;
/* Do not send annotate_rows events unless slave requested it. */
- if (event_type == ANNOTATE_ROWS_EVENT &&
- !(flags & BINLOG_SEND_ANNOTATE_ROWS_EVENT))
- return NULL;
+ if (event_type == ANNOTATE_ROWS_EVENT && !(flags & BINLOG_SEND_ANNOTATE_ROWS_EVENT))
+ {
+ if (mariadb_slave_capability >= MARIA_SLAVE_CAPABILITY_TOLERATE_HOLES)
+ {
+ /* This slave can tolerate events omitted from the binlog stream. */
+ return NULL;
+ }
+ else if (mariadb_slave_capability >= MARIA_SLAVE_CAPABILITY_ANNOTATE)
+ {
+ /*
+ The slave did not request ANNOTATE_ROWS_EVENT (it does not need them as
+ it will not log them in its own binary log). However, it understands the
+ event and will just ignore it, and it would break if we omitted it,
+ leaving a hole in the binlog stream. So just send the event as-is.
+ */
+ }
+ else
+ {
+ /*
+ The slave does not understand ANNOTATE_ROWS_EVENT.
+
+ Older MariaDB slaves (and MySQL slaves) will break replication if there
+ are holes in the binlog stream (they will miscompute the binlog offset
+ and request the wrong position when reconnecting).
+
+ So replace the event with a dummy event of the same size that will be
+ a no-operation on the slave.
+ */
+ if (Query_log_event::dummy_event(packet, ev_offset, current_checksum_alg))
+ return "Failed to replace row annotate event with dummy: too small event.";
+ }
+ }
+
+ /*
+ Do not send binlog checkpoint events to a slave that does not understand it.
+ */
+ if (unlikely(event_type == BINLOG_CHECKPOINT_EVENT) &&
+ mariadb_slave_capability < MARIA_SLAVE_CAPABILITY_BINLOG_CHECKPOINT)
+ {
+ if (mariadb_slave_capability >= MARIA_SLAVE_CAPABILITY_TOLERATE_HOLES)
+ {
+ /* This slave can tolerate events omitted from the binlog stream. */
+ return NULL;
+ }
+ else
+ {
+ /*
+ The slave does not understand BINLOG_CHECKPOINT_EVENT. Send a dummy
+ event instead, with same length so slave does not get confused about
+ binlog positions.
+ */
+ if (Query_log_event::dummy_event(packet, ev_offset, current_checksum_alg))
+ return "Failed to replace binlog checkpoint event with dummy: "
+ "too small event.";
+ }
+ }
/*
Skip events with the @@skip_replication flag set, if slave requested
@@ -628,6 +695,7 @@ void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos,
NET* net = &thd->net;
mysql_mutex_t *log_lock;
mysql_cond_t *log_cond;
+ int mariadb_slave_capability;
uint8 current_checksum_alg= BINLOG_CHECKSUM_ALG_UNDEF;
int old_max_allowed_packet= thd->variables.max_allowed_packet;
@@ -653,7 +721,9 @@ void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos,
heartbeat_ts= &heartbeat_buf;
set_timespec_nsec(*heartbeat_ts, 0);
}
- sql_print_information("Start binlog_dump to slave_server(%d), pos(%s, %lu)",
+ mariadb_slave_capability= get_mariadb_slave_capability(thd);
+ if (global_system_variables.log_warnings > 1)
+ sql_print_information("Start binlog_dump to slave_server(%d), pos(%s, %lu)",
thd->server_id, log_ident, (ulong)pos);
if (RUN_HOOK(binlog_transmit, transmit_start, (thd, flags, log_ident, pos)))
{
@@ -765,7 +835,7 @@ impossible position";
this larger than the corresponding packet (query) sent
from client to master.
*/
- thd->variables.max_allowed_packet+= MAX_LOG_EVENT_HEADER;
+ thd->variables.max_allowed_packet= MAX_MAX_ALLOWED_PACKET;
/*
We can set log_lock now, it does not move (it's a member of
@@ -938,7 +1008,9 @@ impossible position";
}
if ((tmp_msg= send_event_to_slave(thd, net, packet, flags, event_type,
- log_file_name, &log)))
+ log_file_name, &log,
+ mariadb_slave_capability, ev_offset,
+ current_checksum_alg)))
{
errmsg= tmp_msg;
my_errno= ER_UNKNOWN_ERROR;
@@ -1096,7 +1168,9 @@ impossible position";
if (read_packet &&
(tmp_msg= send_event_to_slave(thd, net, packet, flags, event_type,
- log_file_name, &log)))
+ log_file_name, &log,
+ mariadb_slave_capability, ev_offset,
+ current_checksum_alg)))
{
errmsg= tmp_msg;
my_errno= ER_UNKNOWN_ERROR;
@@ -1223,15 +1297,28 @@ err:
@retval 0 success
@retval 1 error
+ @retval -1 fatal error
*/
+
int start_slave(THD* thd , Master_info* mi, bool net_report)
{
int slave_errno= 0;
int thread_mask;
+ char master_info_file_tmp[FN_REFLEN];
+ char relay_log_info_file_tmp[FN_REFLEN];
DBUG_ENTER("start_slave");
if (check_access(thd, SUPER_ACL, any_db, NULL, NULL, 0, 0))
- DBUG_RETURN(1);
+ DBUG_RETURN(-1);
+
+ create_logfile_name_with_suffix(master_info_file_tmp,
+ sizeof(master_info_file_tmp),
+ master_info_file, 0, &mi->connection_name);
+ create_logfile_name_with_suffix(relay_log_info_file_tmp,
+ sizeof(relay_log_info_file_tmp),
+ relay_log_info_file, 0,
+ &mi->connection_name);
+
lock_slave_threads(mi); // this allows us to cleanly read slave_running
// Get a mask of _stopped_ threads
init_thread_mask(&thread_mask,mi,1 /* inverse */);
@@ -1245,7 +1332,7 @@ int start_slave(THD* thd , Master_info* mi, bool net_report)
thread_mask&= thd->lex->slave_thd_opt;
if (thread_mask) //some threads are stopped, start them
{
- if (init_master_info(mi,master_info_file,relay_log_info_file, 0,
+ if (init_master_info(mi,master_info_file_tmp,relay_log_info_file_tmp, 0,
thread_mask))
slave_errno=ER_MASTER_INFO;
else if (server_id_supplied && *mi->host)
@@ -1319,10 +1406,11 @@ int start_slave(THD* thd , Master_info* mi, bool net_report)
if (!slave_errno)
slave_errno = start_slave_threads(0 /*no mutex */,
- 1 /* wait for start */,
- mi,
- master_info_file,relay_log_info_file,
- thread_mask);
+ 1 /* wait for start */,
+ mi,
+ master_info_file_tmp,
+ relay_log_info_file_tmp,
+ thread_mask);
}
else
slave_errno = ER_BAD_SLAVE;
@@ -1339,11 +1427,11 @@ int start_slave(THD* thd , Master_info* mi, bool net_report)
if (slave_errno)
{
if (net_report)
- my_message(slave_errno, ER(slave_errno), MYF(0));
- DBUG_RETURN(1);
+ my_error(slave_errno, MYF(0),
+ (int) mi->connection_name.length,
+ mi->connection_name.str);
+ DBUG_RETURN(slave_errno == ER_BAD_SLAVE ? -1 : 1);
}
- else if (net_report)
- my_ok(thd);
DBUG_RETURN(0);
}
@@ -1361,17 +1449,17 @@ int start_slave(THD* thd , Master_info* mi, bool net_report)
@retval 0 success
@retval 1 error
+ @retval -1 error
*/
+
int stop_slave(THD* thd, Master_info* mi, bool net_report )
{
- DBUG_ENTER("stop_slave");
-
int slave_errno;
- if (!thd)
- thd = current_thd;
+ DBUG_ENTER("stop_slave");
+ DBUG_PRINT("enter",("Connection: %s", mi->connection_name.str));
if (check_access(thd, SUPER_ACL, any_db, NULL, NULL, 0, 0))
- DBUG_RETURN(1);
+ DBUG_RETURN(-1);
THD_STAGE_INFO(thd, stage_killing_slave);
int thread_mask;
lock_slave_threads(mi);
@@ -1406,8 +1494,6 @@ int stop_slave(THD* thd, Master_info* mi, bool net_report )
my_message(slave_errno, ER(slave_errno), MYF(0));
DBUG_RETURN(1);
}
- else if (net_report)
- my_ok(thd);
DBUG_RETURN(0);
}
@@ -1431,15 +1517,18 @@ int reset_slave(THD *thd, Master_info* mi)
int thread_mask= 0, error= 0;
uint sql_errno=ER_UNKNOWN_ERROR;
const char* errmsg= "Unknown error occured while reseting slave";
+ char master_info_file_tmp[FN_REFLEN];
+ char relay_log_info_file_tmp[FN_REFLEN];
DBUG_ENTER("reset_slave");
lock_slave_threads(mi);
init_thread_mask(&thread_mask,mi,0 /* not inverse */);
if (thread_mask) // We refuse if any slave thread is running
{
- sql_errno= ER_SLAVE_MUST_STOP;
- error=1;
- goto err;
+ unlock_slave_threads(mi);
+ my_error(ER_SLAVE_MUST_STOP, MYF(0), (int) mi->connection_name.length,
+ mi->connection_name.str);
+ DBUG_RETURN(ER_SLAVE_MUST_STOP);
}
ha_reset_slave(thd);
@@ -1466,22 +1555,35 @@ int reset_slave(THD *thd, Master_info* mi)
// close master_info_file, relay_log_info_file, set mi->inited=rli->inited=0
end_master_info(mi);
+
// and delete these two files
- fn_format(fname, master_info_file, mysql_data_home, "", 4+32);
+ create_logfile_name_with_suffix(master_info_file_tmp,
+ sizeof(master_info_file_tmp),
+ master_info_file, 0, &mi->connection_name);
+ create_logfile_name_with_suffix(relay_log_info_file_tmp,
+ sizeof(relay_log_info_file_tmp),
+ relay_log_info_file, 0, &mi->connection_name);
+
+ fn_format(fname, master_info_file_tmp, mysql_data_home, "", 4+32);
if (mysql_file_stat(key_file_master_info, fname, &stat_area, MYF(0)) &&
mysql_file_delete(key_file_master_info, fname, MYF(MY_WME)))
{
error=1;
goto err;
}
+ else if (global_system_variables.log_warnings > 1)
+ sql_print_information("Deleted Master_info file '%s'.", fname);
+
// delete relay_log_info_file
- fn_format(fname, relay_log_info_file, mysql_data_home, "", 4+32);
+ fn_format(fname, relay_log_info_file_tmp, mysql_data_home, "", 4+32);
if (mysql_file_stat(key_file_relay_log_info, fname, &stat_area, MYF(0)) &&
mysql_file_delete(key_file_relay_log_info, fname, MYF(MY_WME)))
{
error=1;
goto err;
}
+ else if (global_system_variables.log_warnings > 1)
+ sql_print_information("Deleted Master_info file '%s'.", fname);
RUN_HOOK(binlog_relay_io, after_reset_slave, (thd, mi));
err:
@@ -1561,20 +1663,12 @@ bool change_master(THD* thd, Master_info* mi)
char saved_host[HOSTNAME_LENGTH + 1];
uint saved_port;
char saved_log_name[FN_REFLEN];
+ char master_info_file_tmp[FN_REFLEN];
+ char relay_log_info_file_tmp[FN_REFLEN];
my_off_t saved_log_pos;
- DBUG_ENTER("change_master");
-
- lock_slave_threads(mi);
- init_thread_mask(&thread_mask,mi,0 /*not inverse*/);
LEX_MASTER_INFO* lex_mi= &thd->lex->mi;
- if (thread_mask) // We refuse if any slave thread is running
- {
- my_message(ER_SLAVE_MUST_STOP, ER(ER_SLAVE_MUST_STOP), MYF(0));
- ret= TRUE;
- goto err;
- }
+ DBUG_ENTER("change_master");
- THD_STAGE_INFO(thd, stage_changing_master);
/*
We need to check if there is an empty master_host. Otherwise
change master succeeds, a master.info file is created containing
@@ -1582,17 +1676,61 @@ bool change_master(THD* thd, Master_info* mi)
is thrown stating that the server is not configured as slave.
(See BUG#28796).
*/
- if(lex_mi->host && !*lex_mi->host)
+ if (lex_mi->host && !*lex_mi->host)
{
my_error(ER_WRONG_ARGUMENTS, MYF(0), "MASTER_HOST");
- unlock_slave_threads(mi);
DBUG_RETURN(TRUE);
}
- // TODO: see if needs re-write
- if (init_master_info(mi, master_info_file, relay_log_info_file, 0,
+ if (master_info_index->check_duplicate_master_info(&lex_mi->connection_name,
+ lex_mi->host,
+ lex_mi->port))
+ DBUG_RETURN(TRUE);
+
+ lock_slave_threads(mi);
+ init_thread_mask(&thread_mask,mi,0 /*not inverse*/);
+ if (thread_mask) // We refuse if any slave thread is running
+ {
+ my_error(ER_SLAVE_MUST_STOP, MYF(0), (int) mi->connection_name.length,
+ mi->connection_name.str);
+ ret= TRUE;
+ goto err;
+ }
+
+ THD_STAGE_INFO(thd, stage_changing_master);
+
+ create_logfile_name_with_suffix(master_info_file_tmp,
+ sizeof(master_info_file_tmp),
+ master_info_file, 0, &mi->connection_name);
+ create_logfile_name_with_suffix(relay_log_info_file_tmp,
+ sizeof(relay_log_info_file_tmp),
+ relay_log_info_file, 0, &mi->connection_name);
+
+ /* if new Master_info doesn't exists, add it */
+ if (!master_info_index->get_master_info(&mi->connection_name,
+ MYSQL_ERROR::WARN_LEVEL_NOTE))
+ {
+ if (master_info_index->add_master_info(mi, TRUE))
+ {
+ my_error(ER_MASTER_INFO, MYF(0),
+ (int) lex_mi->connection_name.length,
+ lex_mi->connection_name.str);
+ ret= TRUE;
+ goto err;
+ }
+ }
+ if (global_system_variables.log_warnings > 1)
+ sql_print_information("Master: '%.*s' Master_info_file: '%s' "
+ "Relay_info_file: '%s'",
+ (int) mi->connection_name.length,
+ mi->connection_name.str,
+ master_info_file_tmp, relay_log_info_file_tmp);
+
+ if (init_master_info(mi, master_info_file_tmp, relay_log_info_file_tmp, 0,
thread_mask))
{
- my_message(ER_MASTER_INFO, ER(ER_MASTER_INFO), MYF(0));
+ my_error(ER_MASTER_INFO, MYF(0),
+ (int) lex_mi->connection_name.length,
+ lex_mi->connection_name.str);
ret= TRUE;
goto err;
}
@@ -1860,7 +1998,7 @@ int reset_master(THD* thd)
return 1;
}
- if (mysql_bin_log.reset_logs(thd))
+ if (mysql_bin_log.reset_logs(thd, 1))
return 1;
RUN_HOOK(binlog_transmit, after_reset_master, (thd, 0 /* flags */));
return 0;
@@ -1886,6 +2024,7 @@ bool mysql_show_binlog_events(THD* thd)
File file = -1;
MYSQL_BIN_LOG *binary_log= NULL;
int old_max_allowed_packet= thd->variables.max_allowed_packet;
+ Master_info *mi= 0;
LOG_INFO linfo;
DBUG_ENTER("mysql_show_binlog_events");
@@ -1915,10 +2054,15 @@ bool mysql_show_binlog_events(THD* thd)
}
else /* showing relay log contents */
{
- if (!active_mi)
+ mysql_mutex_lock(&LOCK_active_mi);
+ if (!(mi= master_info_index->
+ get_master_info(&thd->variables.default_master_connection,
+ MYSQL_ERROR::WARN_LEVEL_ERROR)))
+ {
+ mysql_mutex_unlock(&LOCK_active_mi);
DBUG_RETURN(TRUE);
-
- binary_log= &(active_mi->rli.relay_log);
+ }
+ binary_log= &(mi->rli.relay_log);
}
if (binary_log->is_open())
@@ -1932,6 +2076,13 @@ bool mysql_show_binlog_events(THD* thd)
mysql_mutex_t *log_lock = binary_log->get_log_lock();
Log_event* ev;
+ if (mi)
+ {
+ /* We can unlock the mutex as we have a lock on the file */
+ mysql_mutex_unlock(&LOCK_active_mi);
+ mi= 0;
+ }
+
unit->set_limit(thd->lex->current_select);
limit_start= unit->offset_limit_cnt;
limit_end= unit->select_limit_cnt;
@@ -2002,7 +2153,7 @@ bool mysql_show_binlog_events(THD* thd)
description_event->checksum_alg= ev->checksum_alg;
if (event_count >= limit_start &&
- ev->net_send(protocol, linfo.log_file_name, pos))
+ ev->net_send(thd, protocol, linfo.log_file_name, pos))
{
errmsg = "Net error";
delete ev;
@@ -2026,6 +2177,9 @@ bool mysql_show_binlog_events(THD* thd)
mysql_mutex_unlock(log_lock);
}
+ else if (mi)
+ mysql_mutex_unlock(&LOCK_active_mi);
+
// Check that linfo is still on the function scope.
DEBUG_SYNC(thd, "after_show_binlog_events");
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 7f32ca0337e..ba44aba33b9 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -266,8 +266,62 @@ Item_equal *find_item_equal(COND_EQUAL *cond_equal, Field *field,
bool *inherited_fl);
JOIN_TAB *first_depth_first_tab(JOIN* join);
JOIN_TAB *next_depth_first_tab(JOIN* join, JOIN_TAB* tab);
-JOIN_TAB *first_breadth_first_tab(JOIN *join);
-JOIN_TAB *next_breadth_first_tab(JOIN *join, JOIN_TAB *tab);
+
+enum enum_exec_or_opt {WALK_OPTIMIZATION_TABS , WALK_EXECUTION_TABS};
+JOIN_TAB *first_breadth_first_tab(JOIN *join, enum enum_exec_or_opt tabs_kind);
+JOIN_TAB *next_breadth_first_tab(JOIN *join, enum enum_exec_or_opt tabs_kind,
+ JOIN_TAB *tab);
+
+#ifndef DBUG_OFF
+
+/*
+ SHOW EXPLAIN testing: wait for, and serve n_calls APC requests.
+*/
+void dbug_serve_apcs(THD *thd, int n_calls)
+{
+ const char *save_proc_info= thd->proc_info;
+ /* This is so that mysqltest knows we're ready to serve requests: */
+ thd_proc_info(thd, "show_explain_trap");
+
+ /* Busy-wait for n_calls APC requests to arrive and be processed */
+ int n_apcs= thd->apc_target.n_calls_processed + n_calls;
+ while (thd->apc_target.n_calls_processed < n_apcs)
+ {
+ my_sleep(300);
+ if (thd->check_killed())
+ break;
+ }
+ thd_proc_info(thd, save_proc_info);
+}
+
+
+/*
+ Debugging: check if @name=value, comparing as integer
+
+ Intended usage:
+
+ DBUG_EXECUTE_IF("show_explain_probe_2",
+ if (dbug_user_var_equals_int(thd, "select_id", select_id))
+ dbug_serve_apcs(thd, 1);
+ );
+
+*/
+
+bool dbug_user_var_equals_int(THD *thd, const char *name, int value)
+{
+ user_var_entry *var;
+ LEX_STRING varname= {(char*)name, strlen(name)};
+ if ((var= get_variable(&thd->user_vars, varname, FALSE)))
+ {
+ bool null_value;
+ longlong var_value= var->val_int(&null_value);
+ if (!null_value && var_value == value)
+ return TRUE;
+ }
+ return FALSE;
+}
+#endif
+
/**
This handles SELECT with and without UNION.
@@ -710,6 +764,8 @@ JOIN::prepare(Item ***rref_pointer_array,
if (having)
{
+ Query_arena backup, *arena;
+ arena= thd->activate_stmt_arena_if_needed(&backup);
nesting_map save_allow_sum_func= thd->lex->allow_sum_func;
thd->where="having clause";
thd->lex->allow_sum_func|= 1 << select_lex_arg->nest_level;
@@ -725,6 +781,10 @@ JOIN::prepare(Item ***rref_pointer_array,
(having->fix_fields(thd, &having) ||
having->check_cols(1)));
select_lex->having_fix_field= 0;
+ select_lex->having= having;
+ if (arena)
+ thd->restore_active_arena(arena, &backup);
+
if (having_fix_rc || thd->is_error())
DBUG_RETURN(-1); /* purecov: inspected */
thd->lex->allow_sum_func= save_allow_sum_func;
@@ -925,6 +985,19 @@ err:
}
+int JOIN::optimize()
+{
+ int res= optimize_inner();
+ /*
+ If we're inside a non-correlated subquery, this function may be
+ called for the second time after the subquery has been executed
+ and deleted. The second call will not produce a valid query plan, it will
+ short-circuit because optimized==TRUE.
+ */
+ if (!res && have_query_plan != QEP_DELETED)
+ have_query_plan= QEP_AVAILABLE;
+ return res;
+}
/**
global select optimisation.
@@ -938,7 +1011,7 @@ err:
*/
int
-JOIN::optimize()
+JOIN::optimize_inner()
{
ulonglong select_opts_for_readinfo;
uint no_jbuf_after;
@@ -1430,9 +1503,10 @@ JOIN::optimize()
We have found that grouping can be removed since groups correspond to
only one row anyway, but we still have to guarantee correct result
order. The line below effectively rewrites the query from GROUP BY
- <fields> to ORDER BY <fields>. There are two exceptions:
+ <fields> to ORDER BY <fields>. There are three exceptions:
- if skip_sort_order is set (see above), then we can simply skip
GROUP BY;
+ - if we are in a subquery, we don't have to maintain order
- we can only rewrite ORDER BY if the ORDER BY fields are 'compatible'
with the GROUP BY ones, i.e. either one is a prefix of another.
We only check if the ORDER BY is a prefix of GROUP BY. In this case
@@ -1442,7 +1516,13 @@ JOIN::optimize()
'order' as is.
*/
if (!order || test_if_subpart(group_list, order))
- order= skip_sort_order ? 0 : group_list;
+ {
+ if (skip_sort_order ||
+ select_lex->master_unit()->item) // This is a subquery
+ order= NULL;
+ else
+ order= group_list;
+ }
/*
If we have an IGNORE INDEX FOR GROUP BY(fields) clause, this must be
rewritten to IGNORE INDEX FOR ORDER BY(fields).
@@ -1848,6 +1928,7 @@ int JOIN::init_execution()
if (!group_list && ! exec_tmp_table1->distinct && order && simple_order)
{
+ DBUG_PRINT("info",("Sorting for order"));
THD_STAGE_INFO(thd, stage_sorting_for_order);
if (create_sort_index(thd, this, order,
HA_POS_ERROR, HA_POS_ERROR, TRUE))
@@ -2154,6 +2235,32 @@ JOIN::save_join_tab()
}
+void JOIN::exec()
+{
+ /*
+ Enable SHOW EXPLAIN only if we're in the top-level query.
+ */
+ thd->apc_target.enable();
+ DBUG_EXECUTE_IF("show_explain_probe_join_exec_start",
+ if (dbug_user_var_equals_int(thd,
+ "show_explain_probe_select_id",
+ select_lex->select_number))
+ dbug_serve_apcs(thd, 1);
+ );
+
+ exec_inner();
+
+ DBUG_EXECUTE_IF("show_explain_probe_join_exec_end",
+ if (dbug_user_var_equals_int(thd,
+ "show_explain_probe_select_id",
+ select_lex->select_number))
+ dbug_serve_apcs(thd, 1);
+ );
+
+ thd->apc_target.disable();
+}
+
+
/**
Exec select.
@@ -2165,13 +2272,15 @@ JOIN::save_join_tab()
@todo
When can we have here thd->net.report_error not zero?
*/
-void
-JOIN::exec()
+
+void JOIN::exec_inner()
{
List<Item> *columns_list= &fields_list;
int tmp_error;
DBUG_ENTER("JOIN::exec");
+ const bool has_group_by= this->group;
+
THD_STAGE_INFO(thd, stage_executing);
error= 0;
if (procedure)
@@ -2471,6 +2580,10 @@ JOIN::exec()
DBUG_PRINT("info",("Creating group table"));
/* Free first data from old join */
+
+ /*
+ psergey-todo: this is the place of pre-mature JOIN::free call.
+ */
curr_join->join_free();
if (curr_join->make_simple_join(this, curr_tmp_table))
DBUG_VOID_RETURN;
@@ -2511,11 +2624,12 @@ JOIN::exec()
}
if (curr_join->group_list)
{
- THD_STAGE_INFO(thd, stage_creating_sort_index);
if (curr_join->join_tab == join_tab && save_join_tab())
{
DBUG_VOID_RETURN;
}
+ DBUG_PRINT("info",("Sorting for index"));
+ THD_STAGE_INFO(thd, stage_creating_sort_index);
if (create_sort_index(thd, curr_join, curr_join->group_list,
HA_POS_ERROR, HA_POS_ERROR, FALSE) ||
make_group_fields(this, curr_join))
@@ -2781,13 +2895,39 @@ JOIN::exec()
the query. XXX: it's never shown in EXPLAIN!
OPTION_FOUND_ROWS supersedes LIMIT and is taken into account.
*/
- if (create_sort_index(thd, curr_join,
- curr_join->group_list ?
- curr_join->group_list : curr_join->order,
- curr_join->select_limit,
- (select_options & OPTION_FOUND_ROWS ?
- HA_POS_ERROR : unit->select_limit_cnt),
- curr_join->group_list ? TRUE : FALSE))
+ DBUG_PRINT("info",("Sorting for order by/group by"));
+ ORDER *order_arg=
+ curr_join->group_list ? curr_join->group_list : curr_join->order;
+ /*
+ filesort_limit: Return only this many rows from filesort().
+ We can use select_limit_cnt only if we have no group_by and 1 table.
+ This allows us to use Bounded_queue for queries like:
+ "select SQL_CALC_FOUND_ROWS * from t1 order by b desc limit 1;"
+ select_limit == HA_POS_ERROR (we need a full table scan)
+ unit->select_limit_cnt == 1 (we only need one row in the result set)
+ */
+ const ha_rows filesort_limit_arg=
+ (has_group_by || curr_join->table_count > 1)
+ ? curr_join->select_limit : unit->select_limit_cnt;
+ const ha_rows select_limit_arg=
+ select_options & OPTION_FOUND_ROWS
+ ? HA_POS_ERROR : unit->select_limit_cnt;
+
+ DBUG_PRINT("info", ("has_group_by %d "
+ "curr_join->table_count %d "
+ "curr_join->m_select_limit %d "
+ "unit->select_limit_cnt %d",
+ has_group_by,
+ curr_join->table_count,
+ (int) curr_join->select_limit,
+ (int) unit->select_limit_cnt));
+
+ if (create_sort_index(thd,
+ curr_join,
+ order_arg,
+ filesort_limit_arg,
+ select_limit_arg,
+ curr_join->group_list ? FALSE : TRUE))
DBUG_VOID_RETURN;
sortorder= curr_join->sortorder;
if (curr_join->const_tables != curr_join->table_count &&
@@ -2819,6 +2959,14 @@ JOIN::exec()
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF);
error= do_select(curr_join, curr_fields_list, NULL, procedure);
thd->limit_found_rows= curr_join->send_records;
+ if (curr_join->order &&
+ curr_join->sortorder)
+ {
+ /* Use info provided by filesort. */
+ DBUG_ASSERT(curr_join->table_count > curr_join->const_tables);
+ JOIN_TAB *tab= curr_join->join_tab + curr_join->const_tables;
+ thd->limit_found_rows= tab->records;
+ }
/* Accumulate the counts from all join iterations of all join parts. */
thd->inc_examined_row_count(curr_join->examined_rows);
@@ -3708,7 +3856,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
goto error;
/* Generate an execution plan from the found optimal join order. */
- DBUG_RETURN(join->thd->killed || get_best_combination(join));
+ DBUG_RETURN(join->thd->check_killed() || get_best_combination(join));
error:
/*
@@ -6399,8 +6547,7 @@ greedy_search(JOIN *join,
POSITION best_pos;
JOIN_TAB *best_table; // the next plan node to be added to the curr QEP
// ==join->tables or # tables in the sj-mat nest we're optimizing
- uint __attribute__((unused)) n_tables;
-
+ uint n_tables __attribute__((unused));
DBUG_ENTER("greedy_search");
/* number of tables that remain to be optimized */
@@ -6639,12 +6786,12 @@ double JOIN::get_examined_rows()
{
ha_rows examined_rows;
double prev_fanout= 1;
- JOIN_TAB *tab= first_breadth_first_tab(this);
+ JOIN_TAB *tab= first_breadth_first_tab(this, WALK_OPTIMIZATION_TABS);
JOIN_TAB *prev_tab= tab;
examined_rows= tab->get_examined_rows();
- while ((tab= next_breadth_first_tab(this, tab)))
+ while ((tab= next_breadth_first_tab(this, WALK_OPTIMIZATION_TABS, tab)))
{
prev_fanout *= prev_tab->records_read;
examined_rows+= (ha_rows) (tab->get_examined_rows() * prev_fanout);
@@ -6783,7 +6930,7 @@ best_extension_by_limited_search(JOIN *join,
DBUG_ENTER("best_extension_by_limited_search");
THD *thd= join->thd;
- if (thd->killed) // Abort
+ if (thd->check_killed()) // Abort
DBUG_RETURN(TRUE);
DBUG_EXECUTE("opt", print_plan(join, idx, read_time, record_count, idx,
@@ -6940,7 +7087,7 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count,
{
DBUG_ENTER("find_best");
THD *thd= join->thd;
- if (thd->killed)
+ if (thd->check_killed())
DBUG_RETURN(TRUE);
if (!rest_tables)
{
@@ -7259,23 +7406,30 @@ prev_record_reads(POSITION *positions, uint idx, table_map found_ref)
Enumerate join tabs in breadth-first fashion, including const tables.
*/
-JOIN_TAB *first_breadth_first_tab(JOIN *join)
+JOIN_TAB *first_breadth_first_tab(JOIN *join, enum enum_exec_or_opt tabs_kind)
{
- return join->join_tab; /* There's always one (i.e. first) table */
+ /* There's always one (i.e. first) table */
+ return (tabs_kind == WALK_EXECUTION_TABS)? join->join_tab:
+ join->table_access_tabs;
}
-JOIN_TAB *next_breadth_first_tab(JOIN *join, JOIN_TAB *tab)
+JOIN_TAB *next_breadth_first_tab(JOIN *join, enum enum_exec_or_opt tabs_kind,
+ JOIN_TAB *tab)
{
+ JOIN_TAB* const first_top_tab= first_breadth_first_tab(join, tabs_kind);
+ const uint n_top_tabs_count= (tabs_kind == WALK_EXECUTION_TABS)?
+ join->top_join_tab_count:
+ join->top_table_access_tabs_count;
if (!tab->bush_root_tab)
{
/* We're at top level. Get the next top-level tab */
tab++;
- if (tab < join->join_tab + join->top_join_tab_count)
+ if (tab < first_top_tab + n_top_tabs_count)
return tab;
/* No more top-level tabs. Switch to enumerating SJM nest children */
- tab= join->join_tab;
+ tab= first_top_tab;
}
else
{
@@ -7299,7 +7453,7 @@ JOIN_TAB *next_breadth_first_tab(JOIN *join, JOIN_TAB *tab)
Ok, "tab" points to a top-level table, and we need to find the next SJM
nest and enter it.
*/
- for (; tab < join->join_tab + join->top_join_tab_count; tab++)
+ for (; tab < first_top_tab + n_top_tabs_count; tab++)
{
if (tab->bush_children)
return tab->bush_children->start;
@@ -7323,7 +7477,7 @@ JOIN_TAB *first_top_level_tab(JOIN *join, enum enum_with_const_tables with_const
JOIN_TAB *next_top_level_tab(JOIN *join, JOIN_TAB *tab)
{
- tab= next_breadth_first_tab(join, tab);
+ tab= next_breadth_first_tab(join, WALK_EXECUTION_TABS, tab);
if (tab && tab->bush_root_tab)
tab= NULL;
return tab;
@@ -7623,6 +7777,13 @@ get_best_combination(JOIN *join)
join->top_join_tab_count= join->join_tab_ranges.head()->end -
join->join_tab_ranges.head()->start;
+ /*
+ Save pointers to select join tabs for SHOW EXPLAIN
+ */
+ join->table_access_tabs= join->join_tab;
+ join->top_table_access_tabs_count= join->top_join_tab_count;
+
+
update_depend_map(join);
DBUG_RETURN(0);
}
@@ -7866,6 +8027,7 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j,
j->ref.null_rejecting= 0;
j->ref.disable_cache= FALSE;
j->ref.null_ref_part= NO_REF_PART;
+ j->ref.const_ref_part_map= 0;
keyuse=org_keyuse;
store_key **ref_key= j->ref.key_copy;
@@ -7901,6 +8063,13 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j,
if (keyuse->null_rejecting)
j->ref.null_rejecting |= 1 << i;
keyuse_uses_no_tables= keyuse_uses_no_tables && !keyuse->used_tables;
+ /*
+ Todo: we should remove this check for thd->lex->describe on the next
+ line. With SHOW EXPLAIN code, EXPLAIN printout code no longer depends
+ on it. However, removing the check caused change in lots of query
+ plans! Does the optimizer depend on the contents of
+ table_ref->key_copy ? If yes, do we produce incorrect EXPLAINs?
+ */
if (!keyuse->val->used_tables() && !thd->lex->describe)
{ // Compare against constant
store_key_item tmp(thd,
@@ -7913,6 +8082,7 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j,
if (thd->is_fatal_error)
DBUG_RETURN(TRUE);
tmp.copy();
+ j->ref.const_ref_part_map |= key_part_map(1) << i ;
}
else
*ref_key++= get_store_key(thd,
@@ -8002,37 +8172,6 @@ get_store_key(THD *thd, KEYUSE *keyuse, table_map used_tables,
}
/**
- This function is only called for const items on fields which are keys.
-
- @return
- returns 1 if there was some conversion made when the field was stored.
-*/
-
-bool
-store_val_in_field(Field *field, Item *item, enum_check_fields check_flag)
-{
- bool error;
- TABLE *table= field->table;
- THD *thd= table->in_use;
- ha_rows cuted_fields=thd->cuted_fields;
- my_bitmap_map *old_map= dbug_tmp_use_all_columns(table,
- table->write_set);
-
- /*
- we should restore old value of count_cuted_fields because
- store_val_in_field can be called from mysql_insert
- with select_insert, which make count_cuted_fields= 1
- */
- enum_check_fields old_count_cuted_fields= thd->count_cuted_fields;
- thd->count_cuted_fields= check_flag;
- error= item->save_in_field(field, 1);
- thd->count_cuted_fields= old_count_cuted_fields;
- dbug_tmp_restore_column_map(table->write_set, old_map);
- return error || cuted_fields != thd->cuted_fields;
-}
-
-
-/**
@details Initialize a JOIN as a query execution plan
that accesses a single table via a table scan.
@@ -8055,6 +8194,7 @@ JOIN::make_simple_join(JOIN *parent, TABLE *temp_table)
!(parent->join_tab_reexec= (JOIN_TAB*) thd->alloc(sizeof(JOIN_TAB))))
DBUG_RETURN(TRUE); /* purecov: inspected */
+ // psergey-todo: here, save the pointer for original join_tabs.
join_tab= parent->join_tab_reexec;
table= &parent->table_reexec[0]; parent->table_reexec[0]= temp_table;
table_count= top_join_tab_count= 1;
@@ -10316,7 +10456,7 @@ ha_rows JOIN_TAB::get_examined_rows()
{
ha_rows examined_rows;
- if (select && select->quick)
+ if (select && select->quick && use_quick != 2)
examined_rows= select->quick->records;
else if (type == JT_NEXT || type == JT_ALL ||
type == JT_HASH || type ==JT_HASH_NEXT)
@@ -10370,9 +10510,18 @@ bool JOIN_TAB::preread_init()
mysql_handle_single_derived(join->thd->lex,
derived, DT_CREATE | DT_FILL))
return TRUE;
+
preread_init_done= TRUE;
if (select && select->quick)
select->quick->replace_handler(table->file);
+
+ DBUG_EXECUTE_IF("show_explain_probe_join_tab_preread",
+ if (dbug_user_var_equals_int(join->thd,
+ "show_explain_probe_select_id",
+ join->select_lex->select_number))
+ dbug_serve_apcs(join->thd, 1);
+ );
+
return FALSE;
}
@@ -10515,7 +10664,7 @@ void JOIN::join_free()
Optimization: if not EXPLAIN and we are done with the JOIN,
free all tables.
*/
- bool full= !(select_lex->uncacheable);
+ bool full= !(select_lex->uncacheable) && !(thd->lex->describe);
bool can_unlock= full;
DBUG_ENTER("JOIN::join_free");
@@ -10580,6 +10729,8 @@ void JOIN::cleanup(bool full)
{
DBUG_ENTER("JOIN::cleanup");
DBUG_PRINT("enter", ("full %u", (uint) full));
+
+ have_query_plan= QEP_DELETED;
if (table)
{
@@ -10600,9 +10751,22 @@ void JOIN::cleanup(bool full)
if (full)
{
+ JOIN_TAB *sort_tab= first_linear_tab(this, WITHOUT_CONST_TABLES);
+ if (pre_sort_join_tab)
+ {
+ if (sort_tab && sort_tab->select == pre_sort_join_tab->select)
+ {
+ pre_sort_join_tab->select= NULL;
+ }
+ else
+ clean_pre_sort_join_tab();
+ }
+
for (tab= first_linear_tab(this, WITH_CONST_TABLES); tab;
tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS))
+ {
tab->cleanup();
+ }
}
else
{
@@ -11877,9 +12041,9 @@ static int compare_fields_by_table_order(Item *field1,
bool outer_ref= 0;
Item_field *f1= (Item_field *) (field1->real_item());
Item_field *f2= (Item_field *) (field2->real_item());
- if (f1->const_item())
+ if (field1->const_item() || f1->const_item())
return 1;
- if (f2->const_item())
+ if (field2->const_item() || f2->const_item())
return -1;
if (f2->used_tables() & OUTER_REF_TABLE_BIT)
{
@@ -15465,7 +15629,7 @@ create_internal_tmp_table_from_heap(THD *thd, TABLE *table,
DBUG_EXECUTE_IF("raise_error", write_err= HA_ERR_FOUND_DUPP_KEY ;);
if (write_err)
goto err;
- if (thd->killed)
+ if (thd->check_killed())
{
thd->send_kill_message();
goto err_killed;
@@ -15724,6 +15888,14 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure)
else
{
DBUG_ASSERT(join->table_count);
+
+ DBUG_EXECUTE_IF("show_explain_probe_do_select",
+ if (dbug_user_var_equals_int(join->thd,
+ "show_explain_probe_select_id",
+ join->select_lex->select_number))
+ dbug_serve_apcs(join->thd, 1);
+ );
+
if (join->outer_ref_cond && !join->outer_ref_cond->val_int())
error= NESTED_LOOP_NO_MORE_ROWS;
else
@@ -15854,7 +16026,7 @@ sub_select_cache(JOIN *join, JOIN_TAB *join_tab, bool end_of_records)
rc= sub_select(join, join_tab, end_of_records);
DBUG_RETURN(rc);
}
- if (join->thd->killed)
+ if (join->thd->check_killed())
{
/* The user has aborted the execution of the query */
join->thd->send_kill_message();
@@ -16141,7 +16313,7 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
DBUG_RETURN(NESTED_LOOP_ERROR);
if (error < 0)
DBUG_RETURN(NESTED_LOOP_NO_MORE_ROWS);
- if (join->thd->killed) // Aborted by user
+ if (join->thd->check_killed()) // Aborted by user
{
join->thd->send_kill_message();
DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
@@ -16416,10 +16588,11 @@ int report_error(TABLE *table, int error)
Locking reads can legally return also these errors, do not
print them to the .err log
*/
- if (error != HA_ERR_LOCK_DEADLOCK && error != HA_ERR_LOCK_WAIT_TIMEOUT)
+ if (error != HA_ERR_LOCK_DEADLOCK && error != HA_ERR_LOCK_WAIT_TIMEOUT
+ && !table->in_use->killed)
{
push_warning_printf(table->in_use, MYSQL_ERROR::WARN_LEVEL_WARN, error,
- "Got error %d when reading table `%s`.`%s`",
+ "Got error %d when reading table %`s.%`s",
error, table->s->db.str, table->s->table_name.str);
sql_print_error("Got error %d when reading table '%s'",
error, table->s->path.str);
@@ -16885,6 +17058,14 @@ int read_first_record_seq(JOIN_TAB *tab)
static int
test_if_quick_select(JOIN_TAB *tab)
{
+ DBUG_EXECUTE_IF("show_explain_probe_test_if_quick_select",
+ if (dbug_user_var_equals_int(tab->join->thd,
+ "show_explain_probe_select_id",
+ tab->join->select_lex->select_number))
+ dbug_serve_apcs(tab->join->thd, 1);
+ );
+
+
delete tab->select->quick;
tab->select->quick=0;
return tab->select->test_quick_select(tab->join->thd, tab->keys,
@@ -17124,7 +17305,25 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
if ((error= join->result->send_data(*join->fields)))
DBUG_RETURN(error < 0 ? NESTED_LOOP_OK : NESTED_LOOP_ERROR);
}
- if (++join->send_records >= join->unit->select_limit_cnt &&
+
+ ++join->send_records;
+ if (join->send_records >= join->unit->select_limit_cnt &&
+ !join->do_send_rows)
+ {
+ /*
+ If filesort is used for sorting, stop after select_limit_cnt+1
+ records are read. Because of optimization in some cases it can
+ provide only select_limit_cnt+1 records.
+ */
+ if (join->order &&
+ join->sortorder &&
+ join->select_options & OPTION_FOUND_ROWS)
+ {
+ DBUG_PRINT("info", ("filesort NESTED_LOOP_QUERY_LIMIT"));
+ DBUG_RETURN(NESTED_LOOP_QUERY_LIMIT);
+ }
+ }
+ if (join->send_records >= join->unit->select_limit_cnt &&
join->do_send_rows)
{
if (join->select_options & OPTION_FOUND_ROWS)
@@ -17345,7 +17544,7 @@ end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
}
}
end:
- if (join->thd->killed)
+ if (join->thd->check_killed())
{
join->thd->send_kill_message();
DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
@@ -17428,7 +17627,7 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
}
join->send_records++;
end:
- if (join->thd->killed)
+ if (join->thd->check_killed())
{
join->thd->send_kill_message();
DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
@@ -17478,7 +17677,7 @@ end_unique_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
}
}
- if (join->thd->killed)
+ if (join->thd->check_killed())
{
join->thd->send_kill_message();
DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
@@ -17556,7 +17755,7 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
if (join->procedure)
join->procedure->add();
end:
- if (join->thd->killed)
+ if (join->thd->check_killed())
{
join->thd->send_kill_message();
DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
@@ -17636,7 +17835,7 @@ bool test_if_ref(Item *root_cond, Item_field *left_item,Item *right_item)
field->real_type() != MYSQL_TYPE_VARCHAR &&
(field->type() != MYSQL_TYPE_FLOAT || field->decimals() == 0))
{
- return !store_val_in_field(field, right_item, CHECK_FIELD_WARN);
+ return !right_item->save_in_field_no_warnings(field, 1);
}
}
}
@@ -18809,9 +19008,13 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
{
uint length= 0;
ha_rows examined_rows;
+ ha_rows found_rows;
+ ha_rows filesort_retval= HA_POS_ERROR;
TABLE *table;
SQL_SELECT *select;
JOIN_TAB *tab;
+ int err= 0;
+ bool quick_created= FALSE;
DBUG_ENTER("create_sort_index");
if (join->table_count == join->const_tables)
@@ -18819,6 +19022,43 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
tab= join->join_tab + join->const_tables;
table= tab->table;
select= tab->select;
+
+ JOIN_TAB *save_pre_sort_join_tab= NULL;
+ if (join->pre_sort_join_tab)
+ {
+ /*
+ we've already been in this function, and stashed away the original access
+ method in join->pre_sort_join_tab, restore it now.
+ */
+
+ /* First, restore state of the handler */
+ if (join->pre_sort_index != MAX_KEY)
+ {
+ if (table->file->ha_index_or_rnd_end())
+ goto err;
+ if (join->pre_sort_idx_pushed_cond)
+ {
+ table->file->idx_cond_push(join->pre_sort_index,
+ join->pre_sort_idx_pushed_cond);
+ }
+ }
+ else
+ {
+ if (table->file->ha_index_or_rnd_end() ||
+ table->file->ha_rnd_init(TRUE))
+ goto err;
+ }
+
+ /* Second, restore access method parameters */
+ tab->records= join->pre_sort_join_tab->records;
+ tab->select= join->pre_sort_join_tab->select;
+ tab->select_cond= join->pre_sort_join_tab->select_cond;
+ tab->type= join->pre_sort_join_tab->type;
+ tab->read_first_record= join->pre_sort_join_tab->read_first_record;
+
+ save_pre_sort_join_tab= join->pre_sort_join_tab;
+ join->pre_sort_join_tab= NULL;
+ }
/* Currently ORDER BY ... LIMIT is not supported in subqueries. */
DBUG_ASSERT(join->group_list || !join->is_in_subquery());
@@ -18874,6 +19114,7 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
get_quick_select_for_ref(thd, table, &tab->ref,
tab->found_records))))
goto err;
+ quick_created= TRUE;
}
}
@@ -18886,10 +19127,42 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
goto err;
if (table->s->tmp_table)
table->file->info(HA_STATUS_VARIABLE); // Get record count
- table->sort.found_records=filesort(thd, table,join->sortorder, length,
- select, filesort_limit, 0,
- &examined_rows);
- tab->records= table->sort.found_records; // For SQL_CALC_ROWS
+ filesort_retval= filesort(thd, table, join->sortorder, length,
+ select, filesort_limit, 0,
+ &examined_rows, &found_rows);
+ table->sort.found_records= filesort_retval;
+ tab->records= found_rows; // For SQL_CALC_ROWS
+
+ if (quick_created)
+ {
+ /* This will delete the quick select. */
+ select->cleanup();
+ }
+
+ if (!join->pre_sort_join_tab)
+ {
+ if (save_pre_sort_join_tab)
+ join->pre_sort_join_tab= save_pre_sort_join_tab;
+ else if (!(join->pre_sort_join_tab= (JOIN_TAB*)thd->alloc(sizeof(JOIN_TAB))))
+ goto err;
+ }
+
+ *(join->pre_sort_join_tab)= *tab;
+
+ if (table->file->inited == handler::INDEX)
+ {
+ // Save index #, save index condition
+ join->pre_sort_index= table->file->active_index;
+ join->pre_sort_idx_pushed_cond= table->file->pushed_idx_cond;
+ // no need to save key_read?
+ err= table->file->ha_index_end();
+ }
+ else
+ join->pre_sort_index= MAX_KEY;
+
+ /*TODO: here, close the index scan, cancel index-only read. */
+#if 0
+ /* MariaDB doesn't need the following: */
if (select)
{
/*
@@ -18906,23 +19179,83 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
tablesort_result_cache= table->sort.io_cache;
table->sort.io_cache= NULL;
- select->cleanup(); // filesort did select
- table->quick_keys.clear_all(); // as far as we cleanup select->quick
- table->intersect_keys.clear_all();
+ // select->cleanup(); // filesort did select
+ /*
+ If a quick object was created outside of create_sort_index()
+ that might be reused, then do not call select->cleanup() since
+ it will delete the quick object.
+ */
+ if (!keep_quick)
+ {
+ select->cleanup();
+ /*
+ The select object should now be ready for the next use. If it
+ is re-used then there exists a backup copy of this join tab
+ which has the pointer to it. The join tab will be restored in
+ JOIN::reset(). So here we just delete the pointer to it.
+ */
+ tab->select= NULL;
+ // If we deleted the quick select object we need to clear quick_keys
+ table->quick_keys.clear_all();
+ table->intersect_keys.clear_all();
+ }
+ // Restore the output resultset
table->sort.io_cache= tablesort_result_cache;
}
+#endif
+ tab->select=NULL;
tab->set_select_cond(NULL, __LINE__);
- tab->last_inner= 0;
- tab->first_unmatched= 0;
+// tab->last_inner= 0;
+// tab->first_unmatched= 0;
tab->type=JT_ALL; // Read with normal read_record
tab->read_first_record= join_init_read_record;
+ tab->table->file->ha_index_or_rnd_end();
+
+ if (err)
+ goto err;
+
tab->join->examined_rows+=examined_rows;
- table->disable_keyread(); // Restore if we used indexes
- DBUG_RETURN(table->sort.found_records == HA_POS_ERROR);
+ DBUG_RETURN(filesort_retval == HA_POS_ERROR);
err:
DBUG_RETURN(-1);
}
+void JOIN::clean_pre_sort_join_tab()
+{
+ //TABLE *table= pre_sort_join_tab->table;
+ /*
+ Note: we can come here for fake_select_lex object. That object will have
+ the table already deleted by st_select_lex_unit::cleanup().
+ We rely on that fake_select_lex didn't have quick select.
+ */
+#if 0
+ if (pre_sort_join_tab->select && pre_sort_join_tab->select->quick)
+ {
+ /*
+ We need to preserve tablesort's output resultset here, because
+ QUICK_INDEX_MERGE_SELECT::~QUICK_INDEX_MERGE_SELECT (called by
+ SQL_SELECT::cleanup()) may free it assuming it's the result of the quick
+ select operation that we no longer need. Note that all the other parts of
+ this data structure are cleaned up when
+ QUICK_INDEX_MERGE_SELECT::get_next encounters end of data, so the next
+ SQL_SELECT::cleanup() call changes sort.io_cache alone.
+ */
+ IO_CACHE *tablesort_result_cache;
+
+ tablesort_result_cache= table->sort.io_cache;
+ table->sort.io_cache= NULL;
+ pre_sort_join_tab->select->cleanup();
+ table->quick_keys.clear_all(); // as far as we cleanup select->quick
+ table->intersect_keys.clear_all();
+ table->sort.io_cache= tablesort_result_cache;
+ }
+#endif
+ //table->disable_keyread(); // Restore if we used indexes
+ if (pre_sort_join_tab->select && pre_sort_join_tab->select->quick)
+ {
+ pre_sort_join_tab->select->cleanup();
+ }
+}
/*****************************************************************************
Remove duplicates from tmp table
This should be recoded to add a unique index to the table and remove
@@ -19041,7 +19374,7 @@ static int remove_dup_with_compare(THD *thd, TABLE *table, Field **first_field,
error= file->ha_rnd_next(record);
for (;;)
{
- if (thd->killed)
+ if (thd->check_killed())
{
thd->send_kill_message();
error=0;
@@ -19173,7 +19506,7 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table,
for (;;)
{
uchar *org_key_pos;
- if (thd->killed)
+ if (thd->check_killed())
{
thd->send_kill_message();
error=0;
@@ -19249,7 +19582,7 @@ SORT_FIELD *make_unireg_sortorder(ORDER *order, uint *length,
pos= sort= sortorder;
if (!pos)
- return 0;
+ DBUG_RETURN(0);
for (;order;order=order->next,pos++)
{
@@ -19266,6 +19599,7 @@ SORT_FIELD *make_unireg_sortorder(ORDER *order, uint *length,
else
pos->item= *order->item;
pos->reverse=! order->asc;
+ DBUG_ASSERT(pos->field != NULL || pos->item != NULL);
}
*length=count;
DBUG_RETURN(sort);
@@ -21194,29 +21528,155 @@ void JOIN::clear()
}
}
+
+/*
+ Print an EXPLAIN line with all NULLs and given message in the 'Extra' column
+*/
+int print_explain_message_line(select_result_sink *result,
+ SELECT_LEX *select_lex,
+ bool on_the_fly,
+ uint8 options,
+ const char *message)
+{
+ const CHARSET_INFO *cs= system_charset_info;
+ Item *item_null= new Item_null();
+ List<Item> item_list;
+
+ if (on_the_fly)
+ select_lex->set_explain_type(on_the_fly);
+
+ item_list.push_back(new Item_int((int32)
+ select_lex->select_number));
+ item_list.push_back(new Item_string(select_lex->type,
+ strlen(select_lex->type), cs));
+ for (uint i=0 ; i < 7; i++)
+ item_list.push_back(item_null);
+ if (options & DESCRIBE_PARTITIONS)
+ item_list.push_back(item_null);
+ if (options & DESCRIBE_EXTENDED)
+ item_list.push_back(item_null);
+
+ item_list.push_back(new Item_string(message,strlen(message),cs));
+
+ if (result->send_data(item_list))
+ return 1;
+ return 0;
+}
+
+
+int print_fake_select_lex_join(select_result_sink *result, bool on_the_fly,
+ SELECT_LEX *select_lex, uint8 explain_flags)
+{
+ const CHARSET_INFO *cs= system_charset_info;
+ Item *item_null= new Item_null();
+ List<Item> item_list;
+ if (on_the_fly)
+ select_lex->set_explain_type(on_the_fly);
+ /*
+ here we assume that the query will return at least two rows, so we
+ show "filesort" in EXPLAIN. Of course, sometimes we'll be wrong
+ and no filesort will be actually done, but executing all selects in
+ the UNION to provide precise EXPLAIN information will hardly be
+ appreciated :)
+ */
+ char table_name_buffer[SAFE_NAME_LEN];
+ item_list.empty();
+ /* id */
+ item_list.push_back(new Item_null);
+ /* select_type */
+ item_list.push_back(new Item_string(select_lex->type,
+ strlen(select_lex->type),
+ cs));
+ /* table */
+ {
+ SELECT_LEX *sl= select_lex->master_unit()->first_select();
+ uint len= 6, lastop= 0;
+ memcpy(table_name_buffer, STRING_WITH_LEN("<union"));
+ for (; sl && len + lastop + 5 < NAME_LEN; sl= sl->next_select())
+ {
+ len+= lastop;
+ lastop= my_snprintf(table_name_buffer + len, NAME_LEN - len,
+ "%u,", sl->select_number);
+ }
+ if (sl || len + lastop >= NAME_LEN)
+ {
+ memcpy(table_name_buffer + len, STRING_WITH_LEN("...>") + 1);
+ len+= 4;
+ }
+ else
+ {
+ len+= lastop;
+ table_name_buffer[len - 1]= '>'; // change ',' to '>'
+ }
+ item_list.push_back(new Item_string(table_name_buffer, len, cs));
+ }
+ /* partitions */
+ if (explain_flags & DESCRIBE_PARTITIONS)
+ item_list.push_back(item_null);
+ /* type */
+ item_list.push_back(new Item_string(join_type_str[JT_ALL],
+ strlen(join_type_str[JT_ALL]),
+ cs));
+ /* possible_keys */
+ item_list.push_back(item_null);
+ /* key*/
+ item_list.push_back(item_null);
+ /* key_len */
+ item_list.push_back(item_null);
+ /* ref */
+ item_list.push_back(item_null);
+ /* in_rows */
+ if (explain_flags & DESCRIBE_EXTENDED)
+ item_list.push_back(item_null);
+ /* rows */
+ item_list.push_back(item_null);
+ /* extra */
+ if (select_lex->master_unit()->global_parameters->order_list.first)
+ item_list.push_back(new Item_string("Using filesort",
+ 14, cs));
+ else
+ item_list.push_back(new Item_string("", 0, cs));
+
+ if (result->send_data(item_list))
+ return 1;
+ return 0;
+}
+
+
/**
EXPLAIN handling.
- Send a description about what how the select will be done to stdout.
+ Produce lines explaining execution of *this* select (not including children
+ selects)
+ @param on_the_fly TRUE <=> we're being executed on-the-fly, so don't make
+ modifications to any select's data structures
*/
-static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
- bool distinct,const char *message)
+int JOIN::print_explain(select_result_sink *result, uint8 explain_flags,
+ bool on_the_fly,
+ bool need_tmp_table, bool need_order,
+ bool distinct, const char *message)
{
List<Item> field_list;
List<Item> item_list;
+ JOIN *join= this; /* Legacy: this code used to be a non-member function */
THD *thd=join->thd;
- select_result *result=join->result;
Item *item_null= new Item_null();
CHARSET_INFO *cs= system_charset_info;
int quick_type;
- DBUG_ENTER("select_describe");
+ int error= 0;
+ DBUG_ENTER("JOIN::print_explain");
DBUG_PRINT("info", ("Select 0x%lx, type %s, message %s",
(ulong)join->select_lex, join->select_lex->type,
message ? message : "NULL"));
+ DBUG_ASSERT(on_the_fly? have_query_plan == QEP_AVAILABLE: TRUE);
/* Don't log this into the slow query log */
- thd->server_status&= ~(SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED);
- join->unit->offset_limit_cnt= 0;
+
+ if (!on_the_fly)
+ {
+ thd->server_status&= ~(SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED);
+ join->unit->offset_limit_cnt= 0;
+ }
/*
NOTE: the number/types of items pushed into item_list must be in sync with
@@ -21224,101 +21684,32 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
*/
if (message)
{
- item_list.push_back(new Item_int((int32)
- join->select_lex->select_number));
- item_list.push_back(new Item_string(join->select_lex->type,
- strlen(join->select_lex->type), cs));
- for (uint i=0 ; i < 7; i++)
- item_list.push_back(item_null);
- if (join->thd->lex->describe & DESCRIBE_PARTITIONS)
- item_list.push_back(item_null);
- if (join->thd->lex->describe & DESCRIBE_EXTENDED)
- item_list.push_back(item_null);
-
- item_list.push_back(new Item_string(message,strlen(message),cs));
- if (result->send_data(item_list))
- join->error= 1;
+ if (print_explain_message_line(result, join->select_lex, on_the_fly,
+ explain_flags, message))
+ error= 1;
+
}
else if (join->select_lex == join->unit->fake_select_lex)
{
- /*
- here we assume that the query will return at least two rows, so we
- show "filesort" in EXPLAIN. Of course, sometimes we'll be wrong
- and no filesort will be actually done, but executing all selects in
- the UNION to provide precise EXPLAIN information will hardly be
- appreciated :)
- */
- char table_name_buffer[SAFE_NAME_LEN];
- item_list.empty();
- /* id */
- item_list.push_back(new Item_null);
- /* select_type */
- item_list.push_back(new Item_string(join->select_lex->type,
- strlen(join->select_lex->type),
- cs));
- /* table */
- {
- SELECT_LEX *sl= join->unit->first_select();
- uint len= 6, lastop= 0;
- memcpy(table_name_buffer, STRING_WITH_LEN("<union"));
- for (; sl && len + lastop + 5 < NAME_LEN; sl= sl->next_select())
- {
- len+= lastop;
- lastop= my_snprintf(table_name_buffer + len, NAME_LEN - len,
- "%u,", sl->select_number);
- }
- if (sl || len + lastop >= NAME_LEN)
- {
- memcpy(table_name_buffer + len, STRING_WITH_LEN("...>") + 1);
- len+= 4;
- }
- else
- {
- len+= lastop;
- table_name_buffer[len - 1]= '>'; // change ',' to '>'
- }
- item_list.push_back(new Item_string(table_name_buffer, len, cs));
- }
- /* partitions */
- if (join->thd->lex->describe & DESCRIBE_PARTITIONS)
- item_list.push_back(item_null);
- /* type */
- item_list.push_back(new Item_string(join_type_str[JT_ALL],
- strlen(join_type_str[JT_ALL]),
- cs));
- /* possible_keys */
- item_list.push_back(item_null);
- /* key*/
- item_list.push_back(item_null);
- /* key_len */
- item_list.push_back(item_null);
- /* ref */
- item_list.push_back(item_null);
- /* in_rows */
- if (join->thd->lex->describe & DESCRIBE_EXTENDED)
- item_list.push_back(item_null);
- /* rows */
- item_list.push_back(item_null);
- /* extra */
- if (join->unit->global_parameters->order_list.first)
- item_list.push_back(new Item_string("Using filesort",
- 14, cs));
- else
- item_list.push_back(new Item_string("", 0, cs));
-
- if (result->send_data(item_list))
- join->error= 1;
+ if (print_fake_select_lex_join(result, on_the_fly,
+ join->select_lex,
+ explain_flags))
+ error= 1;
}
else if (!join->select_lex->master_unit()->derived ||
join->select_lex->master_unit()->derived->is_materialized_derived())
{
table_map used_tables=0;
+ //if (!join->select_lex->type)
+ if (on_the_fly)
+ join->select_lex->set_explain_type(on_the_fly);
bool printing_materialize_nest= FALSE;
uint select_id= join->select_lex->select_number;
+ JOIN_TAB* const first_top_tab= first_breadth_first_tab(join, WALK_OPTIMIZATION_TABS);
- for (JOIN_TAB *tab= first_breadth_first_tab(join); tab;
- tab= next_breadth_first_tab(join, tab))
+ for (JOIN_TAB *tab= first_breadth_first_tab(join, WALK_OPTIMIZATION_TABS); tab;
+ tab= next_breadth_first_tab(join, WALK_OPTIMIZATION_TABS, tab))
{
if (tab->bush_root_tab)
{
@@ -21350,6 +21741,8 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
tmp3.length(0);
tmp4.length(0);
quick_type= -1;
+ QUICK_SELECT_I *quick= NULL;
+ JOIN_TAB *saved_join_tab= NULL;
/* Don't show eliminated tables */
if (table->map & join->eliminated_tables)
@@ -21358,6 +21751,13 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
continue;
}
+ if (join->table_access_tabs == join->join_tab &&
+ tab == (first_top_tab + join->const_tables) && pre_sort_join_tab)
+ {
+ saved_join_tab= tab;
+ tab= pre_sort_join_tab;
+ }
+
item_list.empty();
/* id */
item_list.push_back(new Item_uint((uint32)select_id));
@@ -21366,17 +21766,19 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
join->select_lex->type;
item_list.push_back(new Item_string(stype, strlen(stype), cs));
+ enum join_type tab_type= tab->type;
if ((tab->type == JT_ALL || tab->type == JT_HASH) &&
- tab->select && tab->select->quick)
+ tab->select && tab->select->quick && tab->use_quick != 2)
{
+ quick= tab->select->quick;
quick_type= tab->select->quick->get_type();
if ((quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE) ||
(quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_INTERSECT) ||
(quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT) ||
(quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION))
- tab->type= tab->type == JT_ALL ? JT_INDEX_MERGE : JT_HASH_INDEX_MERGE;
+ tab_type= tab->type == JT_ALL ? JT_INDEX_MERGE : JT_HASH_INDEX_MERGE;
else
- tab->type= tab->type == JT_ALL ? JT_RANGE : JT_HASH_RANGE;
+ tab_type= tab->type == JT_ALL ? JT_RANGE : JT_HASH_RANGE;
}
/* table */
@@ -21401,19 +21803,11 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
else
{
TABLE_LIST *real_table= table->pos_in_table_list;
- /*
- Internal temporary tables have no corresponding table reference
- object. Such a table may appear in EXPLAIN when a subquery that needs
- a temporary table has been executed, and JOIN::exec replaced the
- original JOIN with a plan to access the data in the temp table
- (made by JOIN::make_simple_join).
- */
- const char *tab_name= real_table ? real_table->alias :
- "internal_tmp_table";
- item_list.push_back(new Item_string(tab_name, strlen(tab_name), cs));
+ item_list.push_back(new Item_string(real_table->alias,
+ strlen(real_table->alias), cs));
}
/* "partitions" column */
- if (join->thd->lex->describe & DESCRIBE_PARTITIONS)
+ if (explain_flags & DESCRIBE_PARTITIONS)
{
#ifdef WITH_PARTITION_STORAGE_ENGINE
partition_info *part_info;
@@ -21432,8 +21826,8 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
#endif
}
/* "type" column */
- item_list.push_back(new Item_string(join_type_str[tab->type],
- strlen(join_type_str[tab->type]),
+ item_list.push_back(new Item_string(join_type_str[tab_type],
+ strlen(join_type_str[tab_type]),
cs));
/* Build "possible_keys" value and add it to item_list */
if (!tab->keys.is_clear_all())
@@ -21457,7 +21851,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
item_list.push_back(item_null);
/* Build "key", "key_len", and "ref" values and add them to item_list */
- if (tab->type == JT_NEXT)
+ if (tab_type == JT_NEXT)
{
key_info= table->key_info+tab->index;
key_len= key_info->key_length;
@@ -21476,22 +21870,30 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
length= (longlong10_to_str(key_len, keylen_str_buf, 10) -
keylen_str_buf);
tmp3.append(keylen_str_buf, length, cs);
- if (tab->ref.key_parts)
+ if (tab->ref.key_parts && tab_type != JT_FT)
{
- for (store_key **ref=tab->ref.key_copy ; *ref ; ref++)
+ store_key **ref=tab->ref.key_copy;
+ for (uint kp= 0; kp < tab->ref.key_parts; kp++)
{
if (tmp4.length())
tmp4.append(',');
- tmp4.append((*ref)->name(), strlen((*ref)->name()), cs);
+
+ if ((key_part_map(1) << kp) & tab->ref.const_ref_part_map)
+ tmp4.append("const");
+ else
+ {
+ tmp4.append((*ref)->name(), strlen((*ref)->name()), cs);
+ ref++;
+ }
}
}
}
- if (is_hj && tab->type != JT_HASH)
+ if (is_hj && tab_type != JT_HASH)
{
tmp2.append(':');
tmp3.append(':');
}
- if (tab->type == JT_HASH_NEXT)
+ if (tab_type == JT_HASH_NEXT)
{
register uint length;
key_info= table->key_info+tab->index;
@@ -21500,15 +21902,10 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
length= (longlong10_to_str(key_len, keylen_str_buf, 10) -
keylen_str_buf);
tmp3.append(keylen_str_buf, length, cs);
-/*<<<<<<< TREE
- }
- if ((is_hj || tab->type==JT_RANGE || tab->type == JT_INDEX_MERGE) &&
- tab->select && tab->select->quick)
-=======*/
}
- if (tab->type != JT_CONST && tab->select && tab->select->quick)
+ if (tab->type != JT_CONST && tab->select && quick)
tab->select->quick->add_keys_and_lengths(&tmp2, &tmp3);
- if (key_info || (tab->select && tab->select->quick))
+ if (key_info || (tab->select && quick))
{
if (tmp2.length())
item_list.push_back(new Item_string(tmp2.ptr(),tmp2.length(),cs));
@@ -21518,7 +21915,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
item_list.push_back(new Item_string(tmp3.ptr(),tmp3.length(),cs));
else
item_list.push_back(item_null);
- if (key_info && tab->type != JT_NEXT)
+ if (key_info && tab_type != JT_NEXT)
item_list.push_back(new Item_string(tmp4.ptr(),tmp4.length(),cs));
else
item_list.push_back(item_null);
@@ -21561,7 +21958,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
table_list->schema_table)
{
/* in_rows */
- if (join->thd->lex->describe & DESCRIBE_EXTENDED)
+ if (explain_flags & DESCRIBE_EXTENDED)
item_list.push_back(item_null);
/* rows */
item_list.push_back(item_null);
@@ -21574,7 +21971,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
MY_INT64_NUM_DECIMAL_DIGITS));
/* Add "filtered" field to item_list. */
- if (join->thd->lex->describe & DESCRIBE_EXTENDED)
+ if (explain_flags & DESCRIBE_EXTENDED)
{
float f= 0.0;
if (examined_rows)
@@ -21586,11 +21983,11 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
/* Build "Extra" field and add it to item_list. */
key_read=table->key_read;
- if ((tab->type == JT_NEXT || tab->type == JT_CONST) &&
+ if ((tab_type == JT_NEXT || tab_type == JT_CONST) &&
table->covering_keys.is_set(tab->index))
key_read=1;
if (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT &&
- !((QUICK_ROR_INTERSECT_SELECT*)tab->select->quick)->need_to_fetch_row)
+ !((QUICK_ROR_INTERSECT_SELECT*)quick)->need_to_fetch_row)
key_read=1;
if (tab->info)
@@ -21618,8 +22015,8 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
uint keyno= MAX_KEY;
if (tab->ref.key_parts)
keyno= tab->ref.key;
- else if (tab->select && tab->select->quick)
- keyno = tab->select->quick->index;
+ else if (tab->select && quick)
+ keyno = quick->index;
if (keyno != MAX_KEY && keyno == table->file->pushed_idx_cond_keyno &&
table->file->pushed_idx_cond)
@@ -21658,7 +22055,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
{
extra.append(STRING_WITH_LEN("; Using where with pushed "
"condition"));
- if (thd->lex->describe & DESCRIBE_EXTENDED)
+ if (explain_flags & DESCRIBE_EXTENDED)
{
extra.append(STRING_WITH_LEN(": "));
((COND *)pushed_cond)->print(&extra, QT_ORDINARY);
@@ -21751,7 +22148,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
extra.append(STRING_WITH_LEN("; End temporary"));
else if (tab->do_firstmatch)
{
- if (tab->do_firstmatch == join->join_tab - 1)
+ if (tab->do_firstmatch == /*join->join_tab*/ first_top_tab - 1)
extra.append(STRING_WITH_LEN("; FirstMatch"));
else
{
@@ -21798,12 +22195,34 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
item_list.push_back(new Item_string(str, len, cs));
}
+ if (saved_join_tab)
+ tab= saved_join_tab;
+
// For next iteration
used_tables|=table->map;
if (result->send_data(item_list))
- join->error= 1;
+ error= 1;
}
}
+ DBUG_RETURN(error);
+}
+
+
+/*
+ See st_select_lex::print_explain() for the SHOW EXPLAIN counterpart
+*/
+
+static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
+ bool distinct,const char *message)
+{
+ THD *thd=join->thd;
+ select_result *result=join->result;
+ DBUG_ENTER("select_describe");
+ join->error= join->print_explain(result, thd->lex->describe,
+ FALSE, /* Not on-the-fly */
+ need_tmp_table, need_order, distinct,
+ message);
+
for (SELECT_LEX_UNIT *unit= join->select_lex->first_inner_unit();
unit;
unit= unit->next_unit())
@@ -21846,7 +22265,7 @@ bool mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result)
for (SELECT_LEX *sl= first; sl; sl= sl->next_select())
{
- sl->set_explain_type();
+ sl->set_explain_type(FALSE);
sl->options|= SELECT_DESCRIBE;
}
diff --git a/sql/sql_select.h b/sql/sql_select.h
index fa990d8a709..586019cae23 100644
--- a/sql/sql_select.h
+++ b/sql/sql_select.h
@@ -92,6 +92,13 @@ typedef struct st_table_ref
uchar *key_buff; ///< value to look for with key
uchar *key_buff2; ///< key_buff+key_length
store_key **key_copy; //
+
+ /*
+ Bitmap of key parts which refer to constants. key_copy only has copiers for
+ non-const key parts.
+ */
+ key_part_map const_ref_part_map;
+
Item **items; ///< val()'s for each keypart
/*
Array of pointers to trigger variables. Some/all of the pointers may be
@@ -888,6 +895,27 @@ protected:
public:
JOIN_TAB *join_tab, **best_ref;
+ /*
+ For "Using temporary+Using filesort" queries, JOIN::join_tab can point to
+ either:
+ 1. array of join tabs describing how to run the select, or
+ 2. array of single join tab describing read from the temporary table.
+
+ SHOW EXPLAIN code needs to read/show #1. This is why two next members are
+ there for saving it.
+ */
+ JOIN_TAB *table_access_tabs;
+ uint top_table_access_tabs_count;
+
+ /*
+ Saved join_tab for pre_sorting. create_sort_index() will save here..
+ */
+ JOIN_TAB *pre_sort_join_tab;
+ uint pre_sort_index;
+ Item *pre_sort_idx_pushed_cond;
+ void clean_pre_sort_join_tab();
+
+
JOIN_TAB **map2table; ///< mapping between table indexes and JOIN_TABs
JOIN_TAB *join_tab_save; ///< saved join_tab for subquery reexecution
@@ -1153,8 +1181,14 @@ public:
const char *zero_result_cause; ///< not 0 if exec must return zero result
bool union_part; ///< this subselect is part of union
+
+ enum join_optimization_state { NOT_OPTIMIZED=0,
+ OPTIMIZATION_IN_PROGRESS=1,
+ OPTIMIZATION_DONE=2};
bool optimized; ///< flag to avoid double optimization in EXPLAIN
bool initialized; ///< flag to avoid double init_execution calls
+
+ enum { QEP_NOT_PRESENT_YET, QEP_AVAILABLE, QEP_DELETED} have_query_plan;
/*
Additional WHERE and HAVING predicates to be considered for IN=>EXISTS
@@ -1237,6 +1271,7 @@ public:
ref_pointer_array_size= 0;
zero_result_cause= 0;
optimized= 0;
+ have_query_plan= QEP_NOT_PRESENT_YET;
initialized= 0;
cond_equal= 0;
having_equal= 0;
@@ -1257,6 +1292,8 @@ public:
outer_ref_cond= pseudo_bits_cond= NULL;
in_to_exists_where= NULL;
in_to_exists_having= NULL;
+
+ pre_sort_join_tab= NULL;
}
int prepare(Item ***rref_pointer_array, TABLE_LIST *tables, uint wind_num,
@@ -1265,9 +1302,11 @@ public:
SELECT_LEX_UNIT *unit);
bool prepare_stage2();
int optimize();
+ int optimize_inner();
int reinit();
int init_execution();
void exec();
+ void exec_inner();
int destroy();
void restore_tmp();
bool alloc_func_list();
@@ -1381,6 +1420,11 @@ public:
{
return (unit->item && unit->item->is_in_predicate());
}
+
+ int print_explain(select_result_sink *result, uint8 explain_flags,
+ bool on_the_fly,
+ bool need_tmp_table, bool need_order,
+ bool distinct,const char *message);
private:
/**
TRUE if the query contains an aggregate function but has no GROUP
@@ -1408,7 +1452,6 @@ typedef struct st_select_check {
extern const char *join_type_str[];
/* Extern functions in sql_select.cc */
-bool store_val_in_field(Field *field, Item *val, enum_check_fields check_flag);
void count_field_types(SELECT_LEX *select_lex, TMP_TABLE_PARAM *param,
List<Item> &fields, bool reset_with_sum_func);
bool setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param,
@@ -1482,6 +1525,7 @@ public:
enum_check_fields saved_count_cuted_fields= thd->count_cuted_fields;
ulonglong sql_mode= thd->variables.sql_mode;
thd->variables.sql_mode&= ~(MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE);
+ thd->variables.sql_mode|= MODE_INVALID_DATES;
thd->count_cuted_fields= CHECK_FIELD_IGNORE;
@@ -1732,6 +1776,9 @@ inline bool optimizer_flag(THD *thd, uint flag)
return (thd->variables.optimizer_switch & flag);
}
+int print_fake_select_lex_join(select_result_sink *result, bool on_the_fly,
+ SELECT_LEX *select_lex, uint8 select_options);
+
uint get_index_for_order(ORDER *order, TABLE *table, SQL_SELECT *select,
ha_rows limit, bool *need_sort, bool *reverse);
ORDER *simple_remove_const(ORDER *order, COND *where);
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 903dc0d9cf2..2a82ae115a0 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -660,6 +660,7 @@ public:
break;
case ER_NO_SUCH_TABLE:
+ case ER_NO_SUCH_TABLE_IN_ENGINE:
/* Established behavior: warn if underlying tables are missing. */
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_VIEW_INVALID,
@@ -992,9 +993,13 @@ static const char *require_quotes(const char *name, uint name_length)
packet target string
name the identifier to be appended
name_length length of the appending identifier
+
+ RETURN VALUES
+ true Error
+ false Ok
*/
-void
+bool
append_identifier(THD *thd, String *packet, const char *name, uint length)
{
const char *name_end;
@@ -1002,10 +1007,7 @@ append_identifier(THD *thd, String *packet, const char *name, uint length)
int q= get_quote_char_for_identifier(thd, name, length);
if (q == EOF)
- {
- packet->append(name, length, packet->charset());
- return;
- }
+ return packet->append(name, length, packet->charset());
/*
The identifier must be quoted as it includes a quote character or
@@ -1014,7 +1016,8 @@ append_identifier(THD *thd, String *packet, const char *name, uint length)
(void) packet->reserve(length*2 + 2);
quote_char= (char) q;
- packet->append(&quote_char, 1, system_charset_info);
+ if (packet->append(&quote_char, 1, system_charset_info))
+ return true;
for (name_end= name+length ; name < name_end ; name+= length)
{
@@ -1029,11 +1032,13 @@ append_identifier(THD *thd, String *packet, const char *name, uint length)
*/
if (!length)
length= 1;
- if (length == 1 && chr == (uchar) quote_char)
- packet->append(&quote_char, 1, system_charset_info);
- packet->append(name, length, system_charset_info);
+ if (length == 1 && chr == (uchar) quote_char &&
+ packet->append(&quote_char, 1, system_charset_info))
+ return true;
+ if (packet->append(name, length, system_charset_info))
+ return true;
}
- packet->append(&quote_char, 1, system_charset_info);
+ return packet->append(&quote_char, 1, system_charset_info);
}
@@ -1998,6 +2003,166 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
DBUG_VOID_RETURN;
}
+
+/*
+ Produce EXPLAIN data.
+
+ This function is APC-scheduled to be run in the context of the thread that
+ we're producing EXPLAIN for.
+*/
+
+void Show_explain_request::call_in_target_thread()
+{
+ Query_arena backup_arena;
+ bool printed_anything= FALSE;
+
+ /*
+ Change the arena because JOIN::print_explain and co. are going to allocate
+ items. Let them allocate them on our arena.
+ */
+ target_thd->set_n_backup_active_arena((Query_arena*)request_thd,
+ &backup_arena);
+
+ query_str.copy(target_thd->query(),
+ target_thd->query_length(),
+ target_thd->query_charset());
+
+ if (target_thd->lex->unit.print_explain(explain_buf, 0 /* explain flags*/,
+ &printed_anything))
+ {
+ failed_to_produce= TRUE;
+ }
+
+ if (!printed_anything)
+ failed_to_produce= TRUE;
+
+ target_thd->restore_active_arena((Query_arena*)request_thd, &backup_arena);
+}
+
+
+int select_result_explain_buffer::send_data(List<Item> &items)
+{
+ fill_record(thd, dst_table->field, items, TRUE, FALSE);
+ if ((dst_table->file->ha_write_tmp_row(dst_table->record[0])))
+ return 1;
+ return 0;
+}
+
+
+/*
+ Store the SHOW EXPLAIN output in the temporary table.
+*/
+
+int fill_show_explain(THD *thd, TABLE_LIST *table, COND *cond)
+{
+ const char *calling_user;
+ THD *tmp;
+ my_thread_id thread_id;
+ DBUG_ENTER("fill_show_explain");
+
+ DBUG_ASSERT(cond==NULL);
+ thread_id= thd->lex->value_list.head()->val_int();
+ calling_user= (thd->security_ctx->master_access & PROCESS_ACL) ? NullS :
+ thd->security_ctx->priv_user;
+
+ if ((tmp= find_thread_by_id(thread_id)))
+ {
+ Security_context *tmp_sctx= tmp->security_ctx;
+ /*
+ If calling_user==NULL, calling thread has SUPER or PROCESS
+ privilege, and so can do SHOW EXPLAIN on any user.
+
+ if calling_user!=NULL, he's only allowed to view SHOW EXPLAIN on
+ his own threads.
+ */
+ if (calling_user && (!tmp_sctx->user || strcmp(calling_user,
+ tmp_sctx->user)))
+ {
+ my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "PROCESS");
+ mysql_mutex_unlock(&tmp->LOCK_thd_data);
+ DBUG_RETURN(1);
+ }
+
+ if (tmp == thd)
+ {
+ mysql_mutex_unlock(&tmp->LOCK_thd_data);
+ my_error(ER_TARGET_NOT_EXPLAINABLE, MYF(0));
+ DBUG_RETURN(1);
+ }
+
+ bool bres;
+ /*
+ Ok we've found the thread of interest and it won't go away because
+ we're holding its LOCK_thd data. Post it a SHOW EXPLAIN request.
+ */
+ bool timed_out;
+ int timeout_sec= 30;
+ Show_explain_request explain_req;
+ select_result_explain_buffer *explain_buf;
+
+ explain_buf= new select_result_explain_buffer(thd, table->table);
+
+ explain_req.explain_buf= explain_buf;
+ explain_req.target_thd= tmp;
+ explain_req.request_thd= thd;
+ explain_req.failed_to_produce= FALSE;
+
+ /* Ok, we have a lock on target->LOCK_thd_data, can call: */
+ bres= tmp->apc_target.make_apc_call(thd, &explain_req, timeout_sec, &timed_out);
+
+ if (bres || explain_req.failed_to_produce)
+ {
+ if (thd->killed)
+ thd->send_kill_message();
+ else if (timed_out)
+ my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0));
+ else
+ my_error(ER_TARGET_NOT_EXPLAINABLE, MYF(0));
+
+ bres= TRUE;
+ }
+ else
+ {
+ /*
+ Push the query string as a warning. The query may be in a different
+ charset than the charset that's used for error messages, so, convert it
+ if needed.
+ */
+ CHARSET_INFO *fromcs= explain_req.query_str.charset();
+ CHARSET_INFO *tocs= error_message_charset_info;
+ char *warning_text;
+ if (!my_charset_same(fromcs, tocs))
+ {
+ uint conv_length= 1 + tocs->mbmaxlen * explain_req.query_str.length() /
+ fromcs->mbminlen;
+ uint dummy_errors;
+ char *to, *p;
+ if (!(to= (char*)thd->alloc(conv_length + 1)))
+ DBUG_RETURN(1);
+ p= to;
+ p+= copy_and_convert(to, conv_length, tocs,
+ explain_req.query_str.c_ptr(),
+ explain_req.query_str.length(), fromcs,
+ &dummy_errors);
+ *p= 0;
+ warning_text= to;
+ }
+ else
+ warning_text= explain_req.query_str.c_ptr_safe();
+
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ ER_YES, warning_text);
+ }
+ DBUG_RETURN(bres);
+ }
+ else
+ {
+ my_error(ER_NO_SUCH_THREAD, MYF(0), thread_id);
+ DBUG_RETURN(1);
+ }
+}
+
+
int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond)
{
TABLE *table= tables->table;
@@ -2330,6 +2495,7 @@ static bool show_status_array(THD *thd, const char *wild,
for (; variables->name; variables++)
{
+ bool wild_checked;
strnmov(prefix_end, variables->name, len);
name_buffer[sizeof(name_buffer)-1]=0; /* Safety */
if (ucase_names)
@@ -2338,11 +2504,25 @@ static bool show_status_array(THD *thd, const char *wild,
restore_record(table, s->default_values);
table->field[0]->store(name_buffer, strlen(name_buffer),
system_charset_info);
+
/*
- if var->type is SHOW_FUNC, call the function.
- Repeat as necessary, if new var is again SHOW_FUNC
+ Compare name for types that can't return arrays. We do this to not
+ calculate the value for function variables that we will not access
*/
- for (var=variables; var->type == SHOW_FUNC; var= &tmp)
+ if ((variables->type != SHOW_FUNC && variables->type != SHOW_ARRAY))
+ {
+ if (wild && wild[0] && wild_case_compare(system_charset_info,
+ name_buffer, wild))
+ continue;
+ wild_checked= 1; // Avoid checking it again
+ }
+
+ /*
+ if var->type is SHOW_FUNC or SHOW_SIMPLE_FUNC, call the function.
+ Repeat as necessary, if new var is again one of the above
+ */
+ for (var=variables; var->type == SHOW_FUNC ||
+ var->type == SHOW_SIMPLE_FUNC; var= &tmp)
((mysql_show_var_func)(var->value))(thd, &tmp, buff);
SHOW_TYPE show_type=var->type;
@@ -2353,8 +2533,9 @@ static bool show_status_array(THD *thd, const char *wild,
}
else
{
- if (!(wild && wild[0] && wild_case_compare(system_charset_info,
- name_buffer, wild)) &&
+ if ((wild_checked ||
+ (wild && wild[0] && wild_case_compare(system_charset_info,
+ name_buffer, wild))) &&
(!partial_cond || partial_cond->val_int()))
{
char *value=var->value;
@@ -3225,9 +3406,12 @@ int make_db_list(THD *thd, List<LEX_STRING> *files,
/*
If we have db lookup vaule we just add it to list and
- exit from the function
+ exit from the function.
+ We don't do this for database names longer than the maximum
+ path length.
*/
- if (lookup_field_vals->db_value.str)
+ if (lookup_field_vals->db_value.str &&
+ lookup_field_vals->db_value.length < FN_REFLEN)
{
if (is_infoschema_db(lookup_field_vals->db_value.str,
lookup_field_vals->db_value.length))
@@ -8309,6 +8493,32 @@ ST_FIELD_INFO keycache_fields_info[]=
};
+ST_FIELD_INFO show_explain_fields_info[]=
+{
+ /* field_name, length, type, value, field_flags, old_name*/
+ {"id", 3, MYSQL_TYPE_LONGLONG, 0 /*value*/, MY_I_S_MAYBE_NULL, "id",
+ SKIP_OPEN_TABLE},
+ {"select_type", 19, MYSQL_TYPE_STRING, 0 /*value*/, 0, "select_type",
+ SKIP_OPEN_TABLE},
+ {"table", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0 /*value*/, MY_I_S_MAYBE_NULL,
+ "table", SKIP_OPEN_TABLE},
+ {"type", 15, MYSQL_TYPE_STRING, 0, MY_I_S_MAYBE_NULL, "type", SKIP_OPEN_TABLE},
+ {"possible_keys", NAME_CHAR_LEN*MAX_KEY, MYSQL_TYPE_STRING, 0/*value*/,
+ MY_I_S_MAYBE_NULL, "possible_keys", SKIP_OPEN_TABLE},
+ {"key", NAME_CHAR_LEN*MAX_KEY, MYSQL_TYPE_STRING, 0/*value*/,
+ MY_I_S_MAYBE_NULL, "key", SKIP_OPEN_TABLE},
+ {"key_len", NAME_CHAR_LEN*MAX_KEY, MYSQL_TYPE_STRING, 0/*value*/,
+ MY_I_S_MAYBE_NULL, "key_len", SKIP_OPEN_TABLE},
+ {"ref", NAME_CHAR_LEN*MAX_REF_PARTS, MYSQL_TYPE_STRING, 0/*value*/,
+ MY_I_S_MAYBE_NULL, "ref", SKIP_OPEN_TABLE},
+ {"rows", 10, MYSQL_TYPE_LONGLONG, 0/*value*/, MY_I_S_MAYBE_NULL, "rows",
+ SKIP_OPEN_TABLE},
+ {"Extra", 255, MYSQL_TYPE_STRING, 0/*value*/, 0 /*flags*/, "Extra",
+ SKIP_OPEN_TABLE},
+ {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
+};
+
+
/*
Description of ST_FIELD_INFO in table.h
@@ -8340,6 +8550,8 @@ ST_SCHEMA_TABLE schema_tables[]=
{"EVENTS", events_fields_info, create_schema_table,
0, make_old_format, 0, -1, -1, 0, 0},
#endif
+ {"EXPLAIN", show_explain_fields_info, create_schema_table, fill_show_explain,
+ make_old_format, 0, -1, -1, TRUE /*hidden*/ , 0},
{"FILES", files_fields_info, create_schema_table,
hton_fill_schema_table, 0, 0, -1, -1, 0, 0},
{"GLOBAL_STATUS", variables_fields_info, create_schema_table,
diff --git a/sql/sql_show.h b/sql/sql_show.h
index 8ad9327c08c..7541ed58e48 100644
--- a/sql/sql_show.h
+++ b/sql/sql_show.h
@@ -19,6 +19,7 @@
#include "sql_list.h" /* List */
#include "handler.h" /* enum_schema_tables */
#include "table.h" /* enum_schema_table_state */
+#include "my_apc.h"
/* Forward declarations */
class JOIN;
@@ -90,7 +91,7 @@ int view_store_create_info(THD *thd, TABLE_LIST *table, String *buff);
int copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table);
-void append_identifier(THD *thd, String *packet, const char *name,
+bool append_identifier(THD *thd, String *packet, const char *name,
uint length);
void mysqld_list_fields(THD *thd,TABLE_LIST *table, const char *wild);
int mysqld_dump_create_info(THD *thd, TABLE_LIST *table_list, int fd);
@@ -131,5 +132,30 @@ enum enum_schema_tables get_schema_table_idx(ST_SCHEMA_TABLE *schema_table);
/* These functions were under INNODB_COMPATIBILITY_HOOKS */
int get_quote_char_for_identifier(THD *thd, const char *name, uint length);
+THD *find_thread_by_id(ulong id);
+
+class select_result_explain_buffer;
+/*
+ SHOW EXPLAIN request object.
+*/
+
+class Show_explain_request : public Apc_target::Apc_call
+{
+public:
+ THD *target_thd; /* thd that we're running SHOW EXPLAIN for */
+ THD *request_thd; /* thd that run SHOW EXPLAIN command */
+
+ /* If true, there was some error when producing EXPLAIN output. */
+ bool failed_to_produce;
+
+ /* SHOW EXPLAIN will be stored here */
+ select_result_explain_buffer *explain_buf;
+
+ /* Query that we've got SHOW EXPLAIN for */
+ String query_str;
+
+ /* Overloaded virtual function */
+ void call_in_target_thread();
+};
#endif /* SQL_SHOW_H */
diff --git a/sql/sql_sort.h b/sql/sql_sort.h
index f1a3a2f9d8b..d30ddfb6eec 100644
--- a/sql/sql_sort.h
+++ b/sql/sql_sort.h
@@ -16,6 +16,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+#include "m_string.h" /* memset */
#include "my_global.h" /* uchar */
#include "my_base.h" /* ha_rows */
#include "my_sys.h" /* qsort2_cmp */
@@ -27,7 +28,6 @@ typedef struct st_sort_field SORT_FIELD;
class Field;
struct TABLE;
-
/* Defines used by filesort and uniques */
#define MERGEBUFF 7
@@ -65,41 +65,51 @@ struct BUFFPEK_COMPARE_CONTEXT
void *key_compare_arg;
};
-typedef struct st_sort_param {
- uint rec_length; /* Length of sorted records */
- uint sort_length; /* Length of sorted columns */
- uint ref_length; /* Length of record ref. */
- uint addon_length; /* Length of added packed fields */
- uint res_length; /* Length of records in final sorted file/buffer */
- uint keys; /* Max keys / buffer */
+
+class Sort_param {
+public:
+ uint rec_length; // Length of sorted records.
+ uint sort_length; // Length of sorted columns.
+ uint ref_length; // Length of record ref.
+ uint addon_length; // Length of added packed fields.
+ uint res_length; // Length of records in final sorted file/buffer.
+ uint max_keys_per_buffer; // Max keys / buffer.
uint min_dupl_count;
- ha_rows max_rows,examined_rows;
- TABLE *sort_form; /* For quicker make_sortkey */
+ ha_rows max_rows; // Select limit, or HA_POS_ERROR if unlimited.
+ ha_rows examined_rows; // Number of examined rows.
+ TABLE *sort_form; // For quicker make_sortkey.
SORT_FIELD *local_sortorder;
SORT_FIELD *end;
- SORT_ADDON_FIELD *addon_field; /* Descriptors for companion fields */
+ SORT_ADDON_FIELD *addon_field; // Descriptors for companion fields.
uchar *unique_buff;
bool not_killable;
char* tmp_buffer;
- /* The fields below are used only by Unique class */
+ // The fields below are used only by Unique class.
qsort2_cmp compare;
BUFFPEK_COMPARE_CONTEXT cmp_context;
-} SORTPARAM;
+ Sort_param()
+ {
+ memset(this, 0, sizeof(*this));
+ }
+ void init_for_filesort(uint sortlen, TABLE *table,
+ ulong max_length_for_sort_data,
+ ha_rows maxrows, bool sort_positions);
+};
-int merge_many_buff(SORTPARAM *param, uchar *sort_buffer,
+
+int merge_many_buff(Sort_param *param, uchar *sort_buffer,
BUFFPEK *buffpek,
uint *maxbuffer, IO_CACHE *t_file);
uint read_to_buffer(IO_CACHE *fromfile,BUFFPEK *buffpek,
uint sort_length);
-int merge_buffers(SORTPARAM *param,IO_CACHE *from_file,
- IO_CACHE *to_file, uchar *sort_buffer,
- BUFFPEK *lastbuff,BUFFPEK *Fb,
- BUFFPEK *Tb,int flag);
-int merge_index(SORTPARAM *param, uchar *sort_buffer,
+int merge_buffers(Sort_param *param,IO_CACHE *from_file,
+ IO_CACHE *to_file, uchar *sort_buffer,
+ BUFFPEK *lastbuff,BUFFPEK *Fb,
+ BUFFPEK *Tb,int flag);
+int merge_index(Sort_param *param, uchar *sort_buffer,
BUFFPEK *buffpek, uint maxbuffer,
IO_CACHE *tempfile, IO_CACHE *outfile);
-
void reuse_freed_buff(QUEUE *queue, BUFFPEK *reuse, uint key_length);
#endif /* SQL_SORT_INCLUDED */
diff --git a/sql/sql_state.c b/sql/sql_state.c
index 511dc65917b..5acf97f16cc 100644
--- a/sql/sql_state.c
+++ b/sql/sql_state.c
@@ -17,6 +17,7 @@
#include <my_global.h>
#include <mysqld_error.h>
+#include <my_base.h>
struct st_map_errno_to_sqlstate
{
@@ -27,6 +28,7 @@ struct st_map_errno_to_sqlstate
struct st_map_errno_to_sqlstate sqlstate_map[]=
{
+#include <handler_state.h>
#include <sql_state.h>
};
diff --git a/sql/sql_string.cc b/sql/sql_string.cc
index c4f5f315b08..75029a03790 100644
--- a/sql/sql_string.cc
+++ b/sql/sql_string.cc
@@ -1121,39 +1121,47 @@ outp:
-
-void String::print(String *str)
+/*
+ Append characters to a single-quoted string '...', escaping special
+ characters as necessary.
+ Does not add the enclosing quotes, this is left up to caller.
+*/
+void String::append_for_single_quote(const char *st, uint len)
{
- char *st= (char*)Ptr, *end= st+str_length;
+ const char *end= st+len;
for (; st < end; st++)
{
uchar c= *st;
switch (c)
{
case '\\':
- str->append(STRING_WITH_LEN("\\\\"));
+ append(STRING_WITH_LEN("\\\\"));
break;
case '\0':
- str->append(STRING_WITH_LEN("\\0"));
+ append(STRING_WITH_LEN("\\0"));
break;
case '\'':
- str->append(STRING_WITH_LEN("\\'"));
+ append(STRING_WITH_LEN("\\'"));
break;
case '\n':
- str->append(STRING_WITH_LEN("\\n"));
+ append(STRING_WITH_LEN("\\n"));
break;
case '\r':
- str->append(STRING_WITH_LEN("\\r"));
+ append(STRING_WITH_LEN("\\r"));
break;
case '\032': // Ctrl-Z
- str->append(STRING_WITH_LEN("\\Z"));
+ append(STRING_WITH_LEN("\\Z"));
break;
default:
- str->append(c);
+ append(c);
}
}
}
+void String::print(String *str)
+{
+ str->append_for_single_quote(Ptr, str_length);
+}
/*
Exchange state of this object and argument.
diff --git a/sql/sql_string.h b/sql/sql_string.h
index 86af507918c..2f0cd9103dc 100644
--- a/sql/sql_string.h
+++ b/sql/sql_string.h
@@ -459,6 +459,7 @@ public:
return FALSE;
}
void print(String *print);
+ void append_for_single_quote(const char *st, uint len);
/* Swap two string objects. Efficient way to exchange data without memcpy. */
void swap(String &s);
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 8d73ce1388c..f74d5427266 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -163,6 +163,13 @@ static char* add_identifier(THD* thd, char *to_p, const char * end_p,
diagnostic, error etc. when it would be useful to know what a particular
file [and directory] means. Such as SHOW ENGINE STATUS, error messages etc.
+ Examples:
+
+ t1#P#p1 table t1 partition p1
+ t1#P#p1#SP#sp1 table t1 partition p1 subpartition sp1
+ t1#P#p1#SP#sp1#TMP# table t1 partition p1 subpartition sp1 temporary
+ t1#P#p1#SP#sp1#REN# table t1 partition p1 subpartition sp1 renamed
+
@param thd Thread handle
@param from Path name in my_charset_filename
Null terminated in my_charset_filename, normalized
@@ -201,7 +208,7 @@ uint explain_filename(THD* thd,
int part_name_len= 0;
const char *subpart_name= NULL;
int subpart_name_len= 0;
- enum enum_file_name_type {NORMAL, TEMP, RENAMED} name_type= NORMAL;
+ uint name_variant= NORMAL_PART_NAME;
const char *tmp_p;
DBUG_ENTER("explain_filename");
DBUG_PRINT("enter", ("from '%s'", from));
@@ -244,7 +251,6 @@ uint explain_filename(THD* thd,
(tmp_p[2] == 'L' || tmp_p[2] == 'l') &&
tmp_p[3] == '-')
{
- name_type= TEMP;
tmp_p+= 4; /* sql- prefix found */
}
else
@@ -255,7 +261,7 @@ uint explain_filename(THD* thd,
if ((tmp_p[1] == 'M' || tmp_p[1] == 'm') &&
(tmp_p[2] == 'P' || tmp_p[2] == 'p') &&
tmp_p[3] == '#' && !tmp_p[4])
- name_type= TEMP;
+ name_variant= TEMP_PART_NAME;
else
res= 3;
tmp_p+= 4;
@@ -265,7 +271,7 @@ uint explain_filename(THD* thd,
if ((tmp_p[1] == 'E' || tmp_p[1] == 'e') &&
(tmp_p[2] == 'N' || tmp_p[2] == 'n') &&
tmp_p[3] == '#' && !tmp_p[4])
- name_type= RENAMED;
+ name_variant= RENAMED_PART_NAME;
else
res= 4;
tmp_p+= 4;
@@ -290,7 +296,7 @@ uint explain_filename(THD* thd,
subpart_name_len= strlen(subpart_name);
else
part_name_len= strlen(part_name);
- if (name_type != NORMAL)
+ if (name_variant != NORMAL_PART_NAME)
{
if (subpart_name)
subpart_name_len-= 5;
@@ -332,9 +338,9 @@ uint explain_filename(THD* thd,
to_p= strnmov(to_p, " ", end_p - to_p);
else
to_p= strnmov(to_p, ", ", end_p - to_p);
- if (name_type != NORMAL)
+ if (name_variant != NORMAL_PART_NAME)
{
- if (name_type == TEMP)
+ if (name_variant == TEMP_PART_NAME)
to_p= strnmov(to_p, ER_THD_OR_DEFAULT(thd, ER_TEMPORARY_NAME),
end_p - to_p);
else
@@ -1826,6 +1832,7 @@ int write_bin_log(THD *thd, bool clear_error,
if (mysql_bin_log.is_open())
{
int errcode= 0;
+ thd_proc_info(thd, "Writing to binlog");
if (clear_error)
thd->clear_error();
else
@@ -1833,6 +1840,7 @@ int write_bin_log(THD *thd, bool clear_error,
error= thd->binlog_query(THD::STMT_QUERY_TYPE,
query, query_length, is_trans, FALSE, FALSE,
errcode);
+ thd_proc_info(thd, 0);
}
return error;
}
@@ -1948,6 +1956,50 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
/**
+ Find the comment in the query.
+ That's auxiliary function to be used handling DROP TABLE [comment].
+
+ @param thd Thread handler
+ @param comment_pos How many characters to skip before the comment.
+ Can be either 9 for DROP TABLE or
+ 17 for DROP TABLE IF EXISTS
+ @param comment_start returns the beginning of the comment if found.
+
+ @retval 0 no comment found
+ @retval >0 the lenght of the comment found
+
+*/
+static uint32 comment_length(THD *thd, uint32 comment_pos,
+ const char **comment_start)
+{
+ /* We use uchar * here to make array indexing portable */
+ const uchar *query= (uchar*) thd->query();
+ const uchar *query_end= (uchar*) query + thd->query_length();
+ const uchar *const state_map= thd->charset()->state_map;
+
+ for (; query < query_end; query++)
+ {
+ if (state_map[static_cast<uchar>(*query)] == MY_LEX_SKIP)
+ continue;
+ if (comment_pos-- == 0)
+ break;
+ }
+ if (query > query_end - 3 /* comment can't be shorter than 4 */ ||
+ state_map[static_cast<uchar>(*query)] != MY_LEX_LONG_COMMENT || query[1] != '*')
+ return 0;
+
+ *comment_start= (char*) query;
+
+ for (query+= 3; query < query_end; query++)
+ {
+ if (query[-1] == '*' && query[0] == '/')
+ return (char*) query - *comment_start + 1;
+ }
+ return 0;
+}
+
+
+/**
Execute the drop of a normal or temporary table.
@param thd Thread handler
@@ -2022,11 +2074,20 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
{
if (!drop_temporary)
{
+ const char *comment_start;
+ uint32 comment_len;
+
built_query.set_charset(system_charset_info);
if (if_exists)
built_query.append("DROP TABLE IF EXISTS ");
else
built_query.append("DROP TABLE ");
+
+ if ((comment_len= comment_length(thd, if_exists ? 17:9, &comment_start)))
+ {
+ built_query.append(comment_start, comment_len);
+ built_query.append(" ");
+ }
}
if (thd->is_current_stmt_binlog_format_row() || if_exists)
@@ -2049,6 +2110,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
{
bool is_trans;
char *db=table->db;
+ size_t db_length= table->db_length;
handlerton *table_type;
enum legacy_db_type frm_db_type= DB_TYPE_UNKNOWN;
@@ -2110,14 +2172,14 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
Don't write the database name if it is the current one (or if
thd->db is NULL).
*/
- built_ptr_query->append("`");
if (thd->db == NULL || strcmp(db,thd->db) != 0)
{
- built_ptr_query->append(db);
- built_ptr_query->append("`.`");
+ append_identifier(thd, built_ptr_query, db, db_length);
+ built_ptr_query->append(".");
}
- built_ptr_query->append(table->table_name);
- built_ptr_query->append("`,");
+ append_identifier(thd, built_ptr_query, table->table_name,
+ table->table_name_length);
+ built_ptr_query->append(",");
}
/*
This means that a temporary table was droped and as such there
@@ -2132,7 +2194,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
if (thd->locked_tables_mode)
{
- if (wait_while_table_is_used(thd, table->table, HA_EXTRA_FORCE_REOPEN))
+ if (wait_while_table_is_used(thd, table->table, HA_EXTRA_NOT_USED))
{
error= -1;
goto err;
@@ -2173,15 +2235,15 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
Don't write the database name if it is the current one (or if
thd->db is NULL).
*/
- built_query.append("`");
if (thd->db == NULL || strcmp(db,thd->db) != 0)
{
- built_query.append(db);
- built_query.append("`.`");
+ append_identifier(thd, &built_query, db, db_length);
+ built_query.append(".");
}
- built_query.append(table->table_name);
- built_query.append("`,");
+ append_identifier(thd, &built_query, table->table_name,
+ table->table_name_length);
+ built_query.append(",");
}
}
DEBUG_SYNC(thd, "rm_table_no_locks_before_delete_table");
@@ -6062,8 +6124,26 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
}
else
{
+ MDL_request_list mdl_requests;
+ MDL_request target_db_mdl_request;
+
target_mdl_request.init(MDL_key::TABLE, new_db, new_name,
MDL_EXCLUSIVE, MDL_TRANSACTION);
+ mdl_requests.push_front(&target_mdl_request);
+
+ /*
+ If we are moving the table to a different database, we also
+ need IX lock on the database name so that the target database
+ is protected by MDL while the table is moved.
+ */
+ if (new_db != db)
+ {
+ target_db_mdl_request.init(MDL_key::SCHEMA, new_db, "",
+ MDL_INTENTION_EXCLUSIVE,
+ MDL_TRANSACTION);
+ mdl_requests.push_front(&target_db_mdl_request);
+ }
+
/*
Global intention exclusive lock must have been already acquired when
table to be altered was open, so there is no need to do it here.
@@ -6072,14 +6152,10 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
"", "",
MDL_INTENTION_EXCLUSIVE));
- if (thd->mdl_context.try_acquire_lock(&target_mdl_request))
+ if (thd->mdl_context.acquire_locks(&mdl_requests,
+ thd->variables.lock_wait_timeout))
DBUG_RETURN(TRUE);
- if (target_mdl_request.ticket == NULL)
- {
- /* Table exists and is locked by some thread. */
- my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias);
- DBUG_RETURN(TRUE);
- }
+
DEBUG_SYNC(thd, "locked_table_name");
/*
Table maybe does not exist, but we got an exclusive lock
@@ -6505,11 +6581,23 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
the primary key is not added and dropped in the same statement.
Otherwise we have to recreate the table.
need_copy_table is no-zero at this place.
+
+ Also, in-place is not possible if we add a primary key
+ and drop another key in the same statement. If the drop fails,
+ we will not be able to revert adding of primary key.
*/
if ( pk_changed < 2 )
{
- if ((alter_flags & needed_inplace_with_read_flags) ==
- needed_inplace_with_read_flags)
+ if ((needed_inplace_with_read_flags & HA_INPLACE_ADD_PK_INDEX_NO_WRITE) &&
+ index_drop_count > 0)
+ {
+ /*
+ Do copy, not in-place ALTER.
+ Avoid setting ALTER_TABLE_METADATA_ONLY.
+ */
+ }
+ else if ((alter_flags & needed_inplace_with_read_flags) ==
+ needed_inplace_with_read_flags)
{
/* All required in-place flags to allow concurrent reads are present. */
need_copy_table= ALTER_TABLE_METADATA_ONLY;
@@ -6787,17 +6875,38 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
Tell the handler to prepare for drop indexes.
This re-numbers the indexes to get rid of gaps.
*/
- if ((error= table->file->prepare_drop_index(table, key_numbers,
- index_drop_count)))
+ error= table->file->prepare_drop_index(table, key_numbers,
+ index_drop_count);
+ if (!error)
{
- table->file->print_error(error, MYF(0));
- goto err_new_table_cleanup;
+ /* Tell the handler to finally drop the indexes. */
+ error= table->file->final_drop_index(table);
}
- /* Tell the handler to finally drop the indexes. */
- if ((error= table->file->final_drop_index(table)))
+ if (error)
{
table->file->print_error(error, MYF(0));
+ if (index_add_count) // Drop any new indexes added.
+ {
+ /*
+ Temporarily set table-key_info to include information about the
+ indexes added above that we now need to drop.
+ */
+ KEY *save_key_info= table->key_info;
+ table->key_info= key_info_buffer;
+ if ((error= table->file->prepare_drop_index(table, index_add_buffer,
+ index_add_count)))
+ table->file->print_error(error, MYF(0));
+ else if ((error= table->file->final_drop_index(table)))
+ table->file->print_error(error, MYF(0));
+ table->key_info= save_key_info;
+ }
+
+ /*
+ Mark this TABLE instance as stale to avoid
+ out-of-sync index information.
+ */
+ table->m_needs_reopen= true;
goto err_new_table_cleanup;
}
}
@@ -7159,7 +7268,7 @@ err_with_mdl:
bool mysql_trans_prepare_alter_copy_data(THD *thd)
{
- DBUG_ENTER("mysql_prepare_alter_copy_data");
+ DBUG_ENTER("mysql_trans_prepare_alter_copy_data");
/*
Turn off recovery logging since rollback of an alter table is to
delete the new table so there is no need to log the changes to it.
@@ -7179,7 +7288,7 @@ bool mysql_trans_prepare_alter_copy_data(THD *thd)
bool mysql_trans_commit_alter_copy_data(THD *thd)
{
bool error= FALSE;
- DBUG_ENTER("mysql_commit_alter_copy_data");
+ DBUG_ENTER("mysql_trans_commit_alter_copy_data");
if (ha_enable_transaction(thd, TRUE))
DBUG_RETURN(TRUE);
@@ -7219,6 +7328,7 @@ copy_data_between_tables(THD *thd, TABLE *from,TABLE *to,
List<Item> fields;
List<Item> all_fields;
ha_rows examined_rows;
+ ha_rows found_rows;
bool auto_increment_field_copied= 0;
ulonglong save_sql_mode= thd->variables.sql_mode;
ulonglong prev_insert_id, time_to_report_progress;
@@ -7302,8 +7412,9 @@ copy_data_between_tables(THD *thd, TABLE *from,TABLE *to,
&tables, fields, all_fields, order) ||
!(sortorder= make_unireg_sortorder(order, &length, NULL)) ||
(from->sort.found_records= filesort(thd, from, sortorder, length,
- (SQL_SELECT *) 0, HA_POS_ERROR,
- 1, &examined_rows)) ==
+ NULL, HA_POS_ERROR,
+ true,
+ &examined_rows, &found_rows)) ==
HA_POS_ERROR)
goto err;
}
@@ -7385,7 +7496,7 @@ copy_data_between_tables(THD *thd, TABLE *from,TABLE *to,
(to->key_info[0].key_part[0].field->flags &
AUTO_INCREMENT_FLAG))
err_msg= ER(ER_DUP_ENTRY_AUTOINCREMENT_CASE);
- to->file->print_keydup_error(key_nr, err_msg);
+ to->file->print_keydup_error(key_nr, err_msg, MYF(0));
break;
}
}
diff --git a/sql/sql_time.cc b/sql/sql_time.cc
index 5972c8918ba..5c0471fdfaa 100644
--- a/sql/sql_time.cc
+++ b/sql/sql_time.cc
@@ -25,9 +25,9 @@
#include <m_ctype.h>
- /* Some functions to calculate dates */
+#define MAX_DAY_NUMBER 3652424L
-#ifndef TESTTIME
+ /* Some functions to calculate dates */
/*
Name description of interval names used in statements.
@@ -147,46 +147,42 @@ uint calc_week(MYSQL_TIME *l_time, uint week_behaviour, uint *year)
/* Change a daynr to year, month and day */
/* Daynr 0 is returned as date 00.00.00 */
-void get_date_from_daynr(long daynr,uint *ret_year,uint *ret_month,
+bool get_date_from_daynr(long daynr,uint *ret_year,uint *ret_month,
uint *ret_day)
{
uint year,temp,leap_day,day_of_year,days_in_year;
uchar *month_pos;
DBUG_ENTER("get_date_from_daynr");
- if (daynr <= 365L || daynr >= 3652500)
- { /* Fix if wrong daynr */
- *ret_year= *ret_month = *ret_day =0;
+ if (daynr < 365 || daynr > MAX_DAY_NUMBER)
+ DBUG_RETURN(1);
+
+ year= (uint) (daynr*100 / 36525L);
+ temp=(((year-1)/100+1)*3)/4;
+ day_of_year=(uint) (daynr - (long) year * 365L) - (year-1)/4 +temp;
+ while (day_of_year > (days_in_year= calc_days_in_year(year)))
+ {
+ day_of_year-=days_in_year;
+ (year)++;
}
- else
+ leap_day=0;
+ if (days_in_year == 366)
{
- year= (uint) (daynr*100 / 36525L);
- temp=(((year-1)/100+1)*3)/4;
- day_of_year=(uint) (daynr - (long) year * 365L) - (year-1)/4 +temp;
- while (day_of_year > (days_in_year= calc_days_in_year(year)))
+ if (day_of_year > 31+28)
{
- day_of_year-=days_in_year;
- (year)++;
+ day_of_year--;
+ if (day_of_year == 31+28)
+ leap_day=1; /* Handle leapyears leapday */
}
- leap_day=0;
- if (days_in_year == 366)
- {
- if (day_of_year > 31+28)
- {
- day_of_year--;
- if (day_of_year == 31+28)
- leap_day=1; /* Handle leapyears leapday */
- }
- }
- *ret_month=1;
- for (month_pos= days_in_month ;
- day_of_year > (uint) *month_pos ;
- day_of_year-= *(month_pos++), (*ret_month)++)
- ;
- *ret_year=year;
- *ret_day=day_of_year+leap_day;
}
- DBUG_VOID_RETURN;
+ *ret_month=1;
+ for (month_pos= days_in_month ;
+ day_of_year > (uint) *month_pos ;
+ day_of_year-= *(month_pos++), (*ret_month)++)
+ ;
+ *ret_year=year;
+ *ret_day=day_of_year+leap_day;
+ DBUG_RETURN(0);
}
/* Functions to handle periods */
@@ -842,7 +838,6 @@ void make_truncated_value_warning(THD *thd,
/* Daynumber from year 0 to 9999-12-31 */
-#define MAX_DAY_NUMBER 3652424L
#define COMBINE(X) \
(((((X)->day * 24LL + (X)->hour) * 60LL + \
(X)->minute) * 60LL + (X)->second)*1000000LL + \
@@ -909,19 +904,18 @@ bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type,
daynr= usec;
/* Day number from year 0 to 9999-12-31 */
- if ((ulonglong) daynr > MAX_DAY_NUMBER)
+ if (get_date_from_daynr((long) daynr, &ltime->year, &ltime->month,
+ &ltime->day))
goto invalid_date;
- get_date_from_daynr((long) daynr, &ltime->year, &ltime->month,
- &ltime->day);
break;
}
case INTERVAL_WEEK:
period= (calc_daynr(ltime->year,ltime->month,ltime->day) +
sign * (long) interval.day);
/* Daynumber from year 0 to 9999-12-31 */
- if ((ulong) period > MAX_DAY_NUMBER)
+ if (get_date_from_daynr((long) period,&ltime->year,&ltime->month,
+ &ltime->day))
goto invalid_date;
- get_date_from_daynr((long) period,&ltime->year,&ltime->month,&ltime->day);
break;
case INTERVAL_YEAR:
ltime->year+= sign * (long) interval.year;
@@ -1071,4 +1065,3 @@ int my_time_compare(MYSQL_TIME *a, MYSQL_TIME *b)
return 0;
}
-#endif
diff --git a/sql/sql_time.h b/sql/sql_time.h
index 026a15cb796..c1a75bb2ad3 100644
--- a/sql/sql_time.h
+++ b/sql/sql_time.h
@@ -33,7 +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 get_date_from_daynr(long daynr,uint *year, uint *month, uint *day);
+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_time_with_warn(CHARSET_INFO *cs, const char *str, uint length,
MYSQL_TIME *l_time, ulonglong fuzzydate);
diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc
index 1b942ecc93b..aff00f9fcf4 100644
--- a/sql/sql_trigger.cc
+++ b/sql/sql_trigger.cc
@@ -1334,6 +1334,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
triggers->definitions_list.elements);
table->triggers= triggers;
+ status_var_increment(thd->status_var.feature_trigger);
/*
TODO: This could be avoided if there is no triggers
@@ -2116,6 +2117,8 @@ bool Table_triggers_list::process_triggers(THD *thd,
if (sp_trigger == NULL)
return FALSE;
+ status_var_increment(thd->status_var.executed_triggers);
+
if (old_row_is_record1)
{
old_field= record1_field;
diff --git a/sql/sql_truncate.cc b/sql/sql_truncate.cc
index 3c1b231d3f2..9d4ca5e1373 100644
--- a/sql/sql_truncate.cc
+++ b/sql/sql_truncate.cc
@@ -24,6 +24,7 @@
#include "sql_acl.h" // DROP_ACL
#include "sql_parse.h" // check_one_table_access()
#include "sql_truncate.h"
+#include "sql_show.h"
/**
@@ -35,7 +36,8 @@
@return TRUE on failure, FALSE otherwise.
*/
-static bool fk_info_append_fields(String *str, List<LEX_STRING> *fields)
+static bool fk_info_append_fields(THD *thd, String *str,
+ List<LEX_STRING> *fields)
{
bool res= FALSE;
LEX_STRING *field;
@@ -43,9 +45,8 @@ static bool fk_info_append_fields(String *str, List<LEX_STRING> *fields)
while ((field= it++))
{
- res|= str->append("`");
- res|= str->append(field);
- res|= str->append("`, ");
+ res|= append_identifier(thd, str, field->str, field->length);
+ res|= str->append(", ");
}
str->chop();
@@ -76,20 +77,24 @@ static const char *fk_info_str(THD *thd, FOREIGN_KEY_INFO *fk_info)
`db`.`tbl`, CONSTRAINT `id` FOREIGN KEY (`fk`) REFERENCES `db`.`tbl` (`fk`)
*/
- res|= str.append('`');
- res|= str.append(fk_info->foreign_db);
- res|= str.append("`.`");
- res|= str.append(fk_info->foreign_table);
- res|= str.append("`, CONSTRAINT `");
- res|= str.append(fk_info->foreign_id);
- res|= str.append("` FOREIGN KEY (");
- res|= fk_info_append_fields(&str, &fk_info->foreign_fields);
- res|= str.append(") REFERENCES `");
- res|= str.append(fk_info->referenced_db);
- res|= str.append("`.`");
- res|= str.append(fk_info->referenced_table);
- res|= str.append("` (");
- res|= fk_info_append_fields(&str, &fk_info->referenced_fields);
+ res|= append_identifier(thd, &str, fk_info->foreign_db->str,
+ fk_info->foreign_db->length);
+ res|= str.append(".");
+ res|= append_identifier(thd, &str, fk_info->foreign_table->str,
+ fk_info->foreign_table->length);
+ res|= str.append(", CONSTRAINT ");
+ res|= append_identifier(thd, &str, fk_info->foreign_id->str,
+ fk_info->foreign_id->length);
+ res|= str.append(" FOREIGN KEY (");
+ res|= fk_info_append_fields(thd, &str, &fk_info->foreign_fields);
+ res|= str.append(") REFERENCES ");
+ res|= append_identifier(thd, &str, fk_info->referenced_db->str,
+ fk_info->referenced_db->length);
+ res|= str.append(".");
+ res|= append_identifier(thd, &str, fk_info->referenced_table->str,
+ fk_info->referenced_table->length);
+ res|= str.append(" (");
+ res|= fk_info_append_fields(thd, &str, &fk_info->referenced_fields);
res|= str.append(')');
return res ? NULL : thd->strmake(str.ptr(), str.length());
diff --git a/sql/sql_udf.cc b/sql/sql_udf.cc
index 169e0d9e418..9069d876609 100644
--- a/sql/sql_udf.cc
+++ b/sql/sql_udf.cc
@@ -428,7 +428,6 @@ int mysql_create_function(THD *thd,udf_func *udf)
TABLE *table;
TABLE_LIST tables;
udf_func *u_d;
- bool save_binlog_row_based;
DBUG_ENTER("mysql_create_function");
if (!initialized)
@@ -459,13 +458,6 @@ int mysql_create_function(THD *thd,udf_func *udf)
DBUG_RETURN(1);
}
- /*
- Turn off row binlogging of this statement and use statement-based
- so that all supporting tables are updated for CREATE FUNCTION command.
- */
- if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
- thd->clear_current_stmt_binlog_format_row();
-
mysql_rwlock_wrlock(&THR_LOCK_udf);
if ((my_hash_search(&udf_hash,(uchar*) udf->name.str, udf->name.length)))
{
@@ -533,27 +525,14 @@ int mysql_create_function(THD *thd,udf_func *udf)
/* Binlog the create function. */
if (write_bin_log(thd, TRUE, thd->query(), thd->query_length()))
- {
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(1);
- }
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
+
DBUG_RETURN(0);
err:
if (new_dl)
dlclose(dl);
mysql_rwlock_unlock(&THR_LOCK_udf);
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(1);
}
@@ -565,7 +544,6 @@ int mysql_drop_function(THD *thd,const LEX_STRING *udf_name)
udf_func *udf;
char *exact_name_str;
uint exact_name_len;
- bool save_binlog_row_based;
DBUG_ENTER("mysql_drop_function");
if (!initialized)
@@ -577,13 +555,6 @@ int mysql_drop_function(THD *thd,const LEX_STRING *udf_name)
DBUG_RETURN(1);
}
- /*
- Turn off row binlogging of this statement and use statement-based
- so that all supporting tables are updated for DROP FUNCTION command.
- */
- if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
- thd->clear_current_stmt_binlog_format_row();
-
mysql_rwlock_wrlock(&THR_LOCK_udf);
if (!(udf=(udf_func*) my_hash_search(&udf_hash,(uchar*) udf_name->str,
(uint) udf_name->length)))
@@ -623,24 +594,12 @@ int mysql_drop_function(THD *thd,const LEX_STRING *udf_name)
while binlogging, to avoid binlog inconsistency.
*/
if (write_bin_log(thd, TRUE, thd->query(), thd->query_length()))
- {
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(1);
- }
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
+
DBUG_RETURN(0);
+
err:
mysql_rwlock_unlock(&THR_LOCK_udf);
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(1);
}
diff --git a/sql/sql_union.cc b/sql/sql_union.cc
index df5f0c9d287..7fab29a7d64 100644
--- a/sql/sql_union.cc
+++ b/sql/sql_union.cc
@@ -621,6 +621,8 @@ bool st_select_lex_unit::exec()
if (executed && !uncacheable && !describe)
DBUG_RETURN(FALSE);
executed= 1;
+ if (!(uncacheable & ~UNCACHEABLE_EXPLAIN) && item)
+ item->make_const();
saved_error= optimize();
@@ -720,6 +722,8 @@ bool st_select_lex_unit::exec()
}
}
+ DBUG_EXECUTE_IF("show_explain_probe_union_read",
+ dbug_serve_apcs(thd, 1););
/* Send result to 'result' */
saved_error= TRUE;
{
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index 4b536c71fd6..549e59329aa 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -406,7 +406,7 @@ int mysql_update(THD *thd,
table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
select= make_select(table, 0, 0, conds, 0, &error);
- if (error || !limit ||
+ if (error || !limit || thd->is_error() ||
(select && select->check_quick(thd, safe_update, limit)))
{
delete select;
@@ -498,13 +498,15 @@ int mysql_update(THD *thd,
uint length= 0;
SORT_FIELD *sortorder;
ha_rows examined_rows;
+ ha_rows found_rows;
table->sort.io_cache = (IO_CACHE *) my_malloc(sizeof(IO_CACHE),
MYF(MY_FAE | MY_ZEROFILL));
if (!(sortorder=make_unireg_sortorder(order, &length, NULL)) ||
(table->sort.found_records= filesort(thd, table, sortorder, length,
- select, limit, 1,
- &examined_rows))
+ select, limit,
+ true,
+ &examined_rows, &found_rows))
== HA_POS_ERROR)
{
goto err;
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index bb74640d9da..b14757f7581 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -65,6 +65,7 @@
#include <myisammrg.h>
#include "keycaches.h"
#include "set_var.h"
+#include "rpl_mi.h"
/* this is to get the bison compilation windows warnings out */
#ifdef _MSC_VER
@@ -1174,6 +1175,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token KILL_SYM
%token LANGUAGE_SYM /* SQL-2003-R */
%token LAST_SYM /* SQL-2003-N */
+%token LAST_VALUE
%token LE /* OPERATOR */
%token LEADING /* SQL-2003-R */
%token LEAVES
@@ -1402,6 +1404,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token SIGNED_SYM
%token SIMPLE_SYM /* SQL-2003-N */
%token SLAVE
+%token SLAVES
%token SLOW
%token SMALLINT /* SQL-2003-R */
%token SNAPSHOT_SYM
@@ -2022,7 +2025,7 @@ help:
/* change master */
change:
- CHANGE MASTER_SYM TO_SYM
+ CHANGE MASTER_SYM optional_connection_name TO_SYM
{
Lex->sql_command = SQLCOM_CHANGE_MASTER;
}
@@ -2179,6 +2182,29 @@ master_file_def:
}
;
+optional_connection_name:
+ /* empty */
+ {
+ THD *thd= YYTHD;
+ LEX *lex= thd->lex;
+ lex->mi.connection_name= thd->variables.default_master_connection;
+ }
+ | connection_name;
+ ;
+
+connection_name:
+ TEXT_STRING_sys
+ {
+ Lex->mi.connection_name= $1;
+#ifdef HAVE_REPLICATION
+ if (check_master_connection_name(&$1))
+ {
+ my_error(ER_WRONG_ARGUMENTS, MYF(0), "MASTER_CONNECTION_NAME");
+ MYSQL_YYABORT;
+ }
+#endif
+ }
+
/* create a table */
create:
@@ -5771,7 +5797,23 @@ type:
$$= MYSQL_TYPE_VARCHAR;
}
| YEAR_SYM opt_field_length field_options
- { $$=MYSQL_TYPE_YEAR; }
+ {
+ if (Lex->length)
+ {
+ errno= 0;
+ ulong length= strtoul(Lex->length, NULL, 10);
+ if (errno == 0 && length <= MAX_FIELD_BLOBLENGTH && length != 4)
+ {
+ char buff[sizeof("YEAR()") + MY_INT64_NUM_DECIMAL_DIGITS + 1];
+ my_snprintf(buff, sizeof(buff), "YEAR(%lu)", length);
+ push_warning_printf(YYTHD, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ ER_WARN_DEPRECATED_SYNTAX,
+ ER(ER_WARN_DEPRECATED_SYNTAX),
+ buff, "YEAR(4)");
+ }
+ }
+ $$=MYSQL_TYPE_YEAR;
+ }
| DATE_SYM
{ $$=MYSQL_TYPE_DATE; }
| TIME_SYM opt_field_length
@@ -5983,9 +6025,9 @@ attribute:
NULL_SYM { Lex->type&= ~ NOT_NULL_FLAG; }
| not NULL_SYM { Lex->type|= NOT_NULL_FLAG; }
| DEFAULT now_or_signed_literal { Lex->default_value=$2; }
- | ON UPDATE_SYM NOW_SYM opt_time_precision
+ | ON UPDATE_SYM NOW_SYM optional_braces
{
- Item *item= new (YYTHD->mem_root) Item_func_now_local($4);
+ Item *item= new (YYTHD->mem_root) Item_func_now_local(6);
if (item == NULL)
MYSQL_YYABORT;
Lex->on_update_value= item;
@@ -6077,9 +6119,9 @@ type_with_opt_collate:
now_or_signed_literal:
- NOW_SYM opt_time_precision
+ NOW_SYM optional_braces
{
- $$= new (YYTHD->mem_root) Item_func_now_local($2);
+ $$= new (YYTHD->mem_root) Item_func_now_local(6);
if ($$ == NULL)
MYSQL_YYABORT;
}
@@ -7184,7 +7226,7 @@ opt_to:
*/
slave:
- START_SYM SLAVE slave_thread_opts
+ START_SYM SLAVE optional_connection_name slave_thread_opts
{
LEX *lex=Lex;
lex->sql_command = SQLCOM_SLAVE_START;
@@ -7193,14 +7235,28 @@ slave:
}
slave_until
{}
- | STOP_SYM SLAVE slave_thread_opts
+ | START_SYM ALL SLAVES slave_thread_opts
+ {
+ LEX *lex=Lex;
+ lex->sql_command = SQLCOM_SLAVE_ALL_START;
+ lex->type = 0;
+ }
+ {}
+ | STOP_SYM SLAVE optional_connection_name slave_thread_opts
{
LEX *lex=Lex;
lex->sql_command = SQLCOM_SLAVE_STOP;
lex->type = 0;
/* If you change this code don't forget to update SLAVE STOP too */
}
- | SLAVE START_SYM slave_thread_opts
+ | STOP_SYM ALL SLAVES slave_thread_opts
+ {
+ LEX *lex=Lex;
+ lex->sql_command = SQLCOM_SLAVE_ALL_STOP;
+ lex->type = 0;
+ /* If you change this code don't forget to update SLAVE STOP too */
+ }
+ | SLAVE optional_connection_name START_SYM slave_thread_opts
{
LEX *lex=Lex;
lex->sql_command = SQLCOM_SLAVE_START;
@@ -7208,7 +7264,7 @@ slave:
}
slave_until
{}
- | SLAVE STOP_SYM slave_thread_opts
+ | SLAVE optional_connection_name STOP_SYM slave_thread_opts
{
LEX *lex=Lex;
lex->sql_command = SQLCOM_SLAVE_STOP;
@@ -9037,6 +9093,12 @@ function_call_conflict:
if ($$ == NULL)
MYSQL_YYABORT;
}
+ | LAST_VALUE '(' expr_list ')'
+ {
+ $$= new (YYTHD->mem_root) Item_func_last_value(* $3);
+ if ($$ == NULL)
+ MYSQL_YYABORT;
+ }
| MICROSECOND_SYM '(' expr ')'
{
$$= new (YYTHD->mem_root) Item_func_microsecond($3);
@@ -11569,7 +11631,7 @@ show_param:
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_BINLOG_EVENTS;
} opt_limit_clause_init
- | RELAYLOG_SYM EVENTS_SYM binlog_in binlog_from
+ | RELAYLOG_SYM optional_connection_name EVENTS_SYM binlog_in binlog_from
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_RELAYLOG_EVENTS;
@@ -11698,9 +11760,23 @@ show_param:
{
Lex->sql_command = SQLCOM_SHOW_MASTER_STAT;
}
+ | ALL SLAVES STATUS_SYM
+ {
+ Lex->sql_command = SQLCOM_SHOW_SLAVE_STAT;
+ Lex->verbose= 1;
+ }
| SLAVE STATUS_SYM
{
+ THD *thd= YYTHD;
+ LEX *lex= thd->lex;
+ lex->mi.connection_name= thd->variables.default_master_connection;
+ lex->sql_command = SQLCOM_SHOW_SLAVE_STAT;
+ lex->verbose= 0;
+ }
+ | SLAVE connection_name STATUS_SYM
+ {
Lex->sql_command = SQLCOM_SHOW_SLAVE_STAT;
+ Lex->verbose= 0;
}
| CLIENT_STATS_SYM
{
@@ -11779,6 +11855,14 @@ show_param:
Lex->spname= $3;
Lex->sql_command = SQLCOM_SHOW_CREATE_EVENT;
}
+ | describe_command FOR_SYM expr
+ {
+ THD *thd= YYTHD;
+ Lex->sql_command= SQLCOM_SHOW_EXPLAIN;
+ if (prepare_schema_table(thd, Lex, 0, SCH_EXPLAIN))
+ MYSQL_YYABORT;
+ add_value_to_list(thd, $3);
+ }
;
show_engine_param:
@@ -11952,8 +12036,17 @@ flush_option:
{ Lex->type|= REFRESH_SLOW_LOG; }
| BINARY LOGS_SYM
{ Lex->type|= REFRESH_BINARY_LOG; }
- | RELAY LOGS_SYM
- { Lex->type|= REFRESH_RELAY_LOG; }
+ | RELAY LOGS_SYM optional_connection_name
+ {
+ LEX *lex= Lex;
+ if (lex->type & REFRESH_RELAY_LOG)
+ {
+ my_error(ER_WRONG_USAGE, MYF(0), "FLUSH", "RELAY LOGS");
+ MYSQL_YYABORT;
+ }
+ lex->type|= REFRESH_RELAY_LOG;
+ lex->relay_log_connection_name= lex->mi.connection_name;
+ }
| QUERY_SYM CACHE_SYM
{ Lex->type|= REFRESH_QUERY_CACHE_FREE; }
| HOSTS_SYM
@@ -11961,13 +12054,23 @@ flush_option:
| PRIVILEGES
{ Lex->type|= REFRESH_GRANT; }
| LOGS_SYM
- { Lex->type|= REFRESH_LOG; }
+ {
+ Lex->type|= REFRESH_LOG;
+ Lex->relay_log_connection_name.str= (char*) "";
+ Lex->relay_log_connection_name.length= 0;
+ }
| STATUS_SYM
{ Lex->type|= REFRESH_STATUS; }
- | SLAVE
+ | SLAVE optional_connection_name
{
- Lex->type|= REFRESH_SLAVE;
- Lex->reset_slave_info.all= false;
+ LEX *lex= Lex;
+ if (lex->type & REFRESH_SLAVE)
+ {
+ my_error(ER_WRONG_USAGE, MYF(0), "FLUSH","SLAVE");
+ MYSQL_YYABORT;
+ }
+ lex->type|= REFRESH_SLAVE;
+ lex->reset_slave_info.all= false;
}
| CLIENT_STATS_SYM
{ Lex->type|= REFRESH_CLIENT_STATS; }
@@ -12011,6 +12114,7 @@ reset_options:
reset_option:
SLAVE { Lex->type|= REFRESH_SLAVE; }
+ optional_connection_name
slave_reset_options { }
| MASTER_SYM { Lex->type|= REFRESH_MASTER; }
| QUERY_SYM CACHE_SYM { Lex->type|= REFRESH_QUERY_CACHE;}
@@ -12291,7 +12395,7 @@ load_data_set_elem:
if (lex->update_list.push_back($1) ||
lex->value_list.push_back($4))
MYSQL_YYABORT;
- $4->set_name($3, (uint) ($5 - $3), YYTHD->charset());
+ $4->set_name_no_truncate($3, (uint) ($5 - $3), YYTHD->charset());
}
;
@@ -13109,6 +13213,7 @@ keyword:
| SIGNED_SYM {}
| SOCKET_SYM {}
| SLAVE {}
+ | SLAVES {}
| SONAME_SYM {}
| START_SYM {}
| STOP_SYM {}
@@ -13235,6 +13340,7 @@ keyword_sp:
| ISSUER_SYM {}
| INSERT_METHOD {}
| KEY_BLOCK_SIZE {}
+ | LAST_VALUE {}
| LAST_SYM {}
| LEAVES {}
| LESS_SYM {}
diff --git a/sql/strfunc.cc b/sql/strfunc.cc
index 0c0742b3805..9603ca30cfa 100644
--- a/sql/strfunc.cc
+++ b/sql/strfunc.cc
@@ -15,6 +15,7 @@
/* Some useful string utility functions used by the MySQL server */
+#include <my_global.h>
#include "sql_priv.h"
#include "unireg.h"
#include "strfunc.h"
diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc
index ac84d820ca6..02f7fc0601c 100644
--- a/sql/sys_vars.cc
+++ b/sql/sys_vars.cc
@@ -50,7 +50,9 @@
#include "sql_base.h" // close_cached_tables
#include <myisam.h>
#include "log_slow.h"
+#include "debug_sync.h" // DEBUG_SYNC
+#include "log_event.h"
#ifdef WITH_PERFSCHEMA_STORAGE_ENGINE
#include "../storage/perfschema/pfs_server.h"
#endif /* WITH_PERFSCHEMA_STORAGE_ENGINE */
@@ -997,6 +999,30 @@ static Sys_var_lexstring Sys_init_connect(
DEFAULT(""), &PLock_sys_init_connect, NOT_IN_BINLOG,
ON_CHECK(check_init_string));
+#ifdef HAVE_REPLICATION
+static bool check_master_connection(sys_var *self, THD *thd, set_var *var)
+{
+ LEX_STRING tmp;
+ tmp.str= var->save_result.string_value.str;
+ tmp.length= var->save_result.string_value.length;
+ if (!tmp.str || check_master_connection_name(&tmp))
+ {
+ my_error(ER_WRONG_ARGUMENTS, MYF(ME_JUST_WARNING),
+ var->var->name.str);
+ return true;
+ }
+ return false;
+}
+
+static Sys_var_session_lexstring Sys_default_master_connection(
+ "default_master_connection",
+ "Master connection to use for all slave variables and slave commands",
+ SESSION_ONLY(default_master_connection),
+ NO_CMD_LINE, IN_SYSTEM_CHARSET,
+ DEFAULT(""), MAX_CONNECTION_NAME, ON_CHECK(check_master_connection),
+ ON_UPDATE(0));
+#endif
+
static Sys_var_charptr Sys_init_file(
"init_file", "Read SQL commands from this file at startup",
READ_ONLY GLOBAL_VAR(opt_init_file),
@@ -1255,6 +1281,14 @@ static Sys_var_ulong Sys_max_allowed_packet(
BLOCK_SIZE(1024), NO_MUTEX_GUARD, NOT_IN_BINLOG,
ON_CHECK(check_max_allowed_packet));
+static Sys_var_ulong Sys_slave_max_allowed_packet(
+ "slave_max_allowed_packet",
+ "The maximum packet length to sent successfully from the master to slave.",
+ GLOBAL_VAR(slave_max_allowed_packet), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(1024, MAX_MAX_ALLOWED_PACKET),
+ DEFAULT(MAX_MAX_ALLOWED_PACKET),
+ BLOCK_SIZE(1024));
+
static Sys_var_ulonglong Sys_max_binlog_cache_size(
"max_binlog_cache_size",
"Sets the total size of the transactional cache",
@@ -1274,16 +1308,12 @@ static Sys_var_ulonglong Sys_max_binlog_stmt_cache_size(
static bool fix_max_binlog_size(sys_var *self, THD *thd, enum_var_type type)
{
mysql_bin_log.set_max_size(max_binlog_size);
-#ifdef HAVE_REPLICATION
- if (!max_relay_log_size)
- active_mi->rli.relay_log.set_max_size(max_binlog_size);
-#endif
return false;
}
static Sys_var_ulong Sys_max_binlog_size(
"max_binlog_size",
"Binary log will be rotated automatically when the size exceeds this "
- "value. Will also apply to relay logs if max_relay_log_size is 0",
+ "value.",
GLOBAL_VAR(max_binlog_size), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(IO_SIZE, 1024*1024L*1024L), DEFAULT(1024*1024L*1024L),
BLOCK_SIZE(IO_SIZE), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
@@ -1428,24 +1458,6 @@ static Sys_var_ulong Sys_max_prepared_stmt_count(
VALID_RANGE(0, 1024*1024), DEFAULT(16382), BLOCK_SIZE(1),
&PLock_prepared_stmt_count);
-static bool fix_max_relay_log_size(sys_var *self, THD *thd, enum_var_type type)
-{
-#ifdef HAVE_REPLICATION
- active_mi->rli.relay_log.set_max_size(max_relay_log_size ?
- max_relay_log_size: max_binlog_size);
-#endif
- return false;
-}
-static Sys_var_ulong Sys_max_relay_log_size(
- "max_relay_log_size",
- "If non-zero: relay log will be rotated automatically when the "
- "size exceeds this value; if zero: when the size "
- "exceeds max_binlog_size",
- GLOBAL_VAR(max_relay_log_size), CMD_LINE(REQUIRED_ARG),
- VALID_RANGE(0, 1024L*1024*1024), DEFAULT(0), BLOCK_SIZE(IO_SIZE),
- NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
- ON_UPDATE(fix_max_relay_log_size));
-
static Sys_var_ulong Sys_max_sort_length(
"max_sort_length",
"The number of bytes to use when sorting BLOB or TEXT values (only "
@@ -2216,50 +2228,22 @@ static Sys_var_mybool Sys_master_verify_checksum(
static const char *replicate_events_marked_for_skip_names[]= {
"replicate", "filter_on_slave", "filter_on_master", 0
};
-static bool
-replicate_events_marked_for_skip_check(sys_var *self, THD *thd,
- set_var *var)
-{
- int thread_mask;
- DBUG_ENTER("sys_var_replicate_events_marked_for_skip_check");
- /* Slave threads must be stopped to change the variable. */
- mysql_mutex_lock(&LOCK_active_mi);
- lock_slave_threads(active_mi);
- init_thread_mask(&thread_mask, active_mi, 0 /*not inverse*/);
- unlock_slave_threads(active_mi);
- mysql_mutex_unlock(&LOCK_active_mi);
-
- if (thread_mask) // We refuse if any slave thread is running
- {
- my_error(ER_SLAVE_MUST_STOP, MYF(0));
- DBUG_RETURN(true);
- }
- DBUG_RETURN(false);
-}
bool
Sys_var_replicate_events_marked_for_skip::global_update(THD *thd, set_var *var)
{
- bool result;
- int thread_mask;
+ bool result= true; // Assume error
DBUG_ENTER("Sys_var_replicate_events_marked_for_skip::global_update");
- /* Slave threads must be stopped to change the variable. */
+ mysql_mutex_unlock(&LOCK_global_system_variables);
mysql_mutex_lock(&LOCK_active_mi);
- lock_slave_threads(active_mi);
- init_thread_mask(&thread_mask, active_mi, 0 /*not inverse*/);
- if (thread_mask) // We refuse if any slave thread is running
- {
- my_error(ER_SLAVE_MUST_STOP, MYF(0));
- result= true;
- }
- else
+ if (!master_info_index->give_error_if_slave_running())
result= Sys_var_enum::global_update(thd, var);
-
- unlock_slave_threads(active_mi);
mysql_mutex_unlock(&LOCK_active_mi);
+ mysql_mutex_lock(&LOCK_global_system_variables);
DBUG_RETURN(result);
}
+
static Sys_var_replicate_events_marked_for_skip Replicate_events_marked_for_skip
("replicate_events_marked_for_skip",
"Whether the slave should replicate events that were created with "
@@ -2270,8 +2254,7 @@ static Sys_var_replicate_events_marked_for_skip Replicate_events_marked_for_skip
"the slave).",
GLOBAL_VAR(opt_replicate_events_marked_for_skip), CMD_LINE(REQUIRED_ARG),
replicate_events_marked_for_skip_names, DEFAULT(RPL_SKIP_REPLICATE),
- NO_MUTEX_GUARD, NOT_IN_BINLOG,
- ON_CHECK(replicate_events_marked_for_skip_check));
+ NO_MUTEX_GUARD, NOT_IN_BINLOG);
#endif
@@ -3495,43 +3478,18 @@ static Sys_var_mybool Sys_relay_log_recovery(
"processed",
GLOBAL_VAR(relay_log_recovery), CMD_LINE(OPT_ARG), DEFAULT(FALSE));
-bool Sys_var_rpl_filter::do_check(THD *thd, set_var *var)
-{
- bool status;
-
- mysql_mutex_lock(&LOCK_active_mi);
- mysql_mutex_lock(&active_mi->rli.run_lock);
-
- status= active_mi->rli.slave_running;
-
- mysql_mutex_unlock(&active_mi->rli.run_lock);
- mysql_mutex_unlock(&LOCK_active_mi);
-
- if (status)
- my_error(ER_SLAVE_MUST_STOP, MYF(0));
- else
- status= Sys_var_charptr::do_string_check(thd, var, charset(thd));
-
- return status;
-}
bool Sys_var_rpl_filter::global_update(THD *thd, set_var *var)
{
- bool slave_running, status= false;
+ bool result= true; // Assume error
+ mysql_mutex_unlock(&LOCK_global_system_variables);
mysql_mutex_lock(&LOCK_active_mi);
- mysql_mutex_lock(&active_mi->rli.run_lock);
-
- if (! (slave_running= active_mi->rli.slave_running))
- status= set_filter_value(var->save_result.string_value.str);
-
- mysql_mutex_unlock(&active_mi->rli.run_lock);
+ if (!master_info_index->give_error_if_slave_running())
+ result= set_filter_value(var->save_result.string_value.str);
mysql_mutex_unlock(&LOCK_active_mi);
-
- if (slave_running)
- my_error(ER_SLAVE_MUST_STOP, MYF(0));
-
- return slave_running || status;
+ mysql_mutex_lock(&LOCK_global_system_variables);
+ return result;
}
bool Sys_var_rpl_filter::set_filter_value(const char *value)
@@ -3569,9 +3527,6 @@ uchar *Sys_var_rpl_filter::global_value_ptr(THD *thd, LEX_STRING *base)
tmp.length(0);
- mysql_mutex_lock(&LOCK_active_mi);
- mysql_mutex_lock(&active_mi->rli.run_lock);
-
switch (opt_id) {
case OPT_REPLICATE_DO_DB:
rpl_filter->get_do_db(&tmp);
@@ -3593,9 +3548,6 @@ uchar *Sys_var_rpl_filter::global_value_ptr(THD *thd, LEX_STRING *base)
break;
}
- mysql_mutex_unlock(&active_mi->rli.run_lock);
- mysql_mutex_unlock(&LOCK_active_mi);
-
return (uchar *) thd->strmake(tmp.ptr(), tmp.length());
}
@@ -3645,66 +3597,105 @@ static Sys_var_charptr Sys_slave_load_tmpdir(
READ_ONLY GLOBAL_VAR(slave_load_tmpdir), CMD_LINE(REQUIRED_ARG),
IN_FS_CHARSET, DEFAULT(0));
-static bool fix_slave_net_timeout(sys_var *self, THD *thd, enum_var_type type)
-{
- mysql_mutex_lock(&LOCK_active_mi);
- DBUG_PRINT("info", ("slave_net_timeout=%u mi->heartbeat_period=%.3f",
- slave_net_timeout,
- (active_mi? active_mi->heartbeat_period : 0.0)));
- if (active_mi && slave_net_timeout < active_mi->heartbeat_period)
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MAX,
- ER(ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MAX));
- mysql_mutex_unlock(&LOCK_active_mi);
- return false;
-}
static Sys_var_uint Sys_slave_net_timeout(
"slave_net_timeout", "Number of seconds to wait for more data "
- "from a master/slave connection before aborting the read",
+ "from any master/slave connection before aborting the read",
GLOBAL_VAR(slave_net_timeout), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(1, LONG_TIMEOUT), DEFAULT(SLAVE_NET_TIMEOUT), BLOCK_SIZE(1),
NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
- ON_UPDATE(fix_slave_net_timeout));
+ ON_UPDATE(0));
+
+
+/*
+ Access a multi_source variable
+ Return 0 + warning if it doesn't exist
+*/
-static bool check_slave_skip_counter(sys_var *self, THD *thd, set_var *var)
+uint Sys_var_multi_source_ulong::
+get_master_info_uint_value(THD *thd, ptrdiff_t offset)
{
- bool result= false;
+ Master_info *mi;
+ uint res= 0; // Default value
mysql_mutex_lock(&LOCK_active_mi);
- mysql_mutex_lock(&active_mi->rli.run_lock);
- if (active_mi->rli.slave_running)
+ mi= master_info_index->
+ get_master_info(&thd->variables.default_master_connection,
+ MYSQL_ERROR::WARN_LEVEL_WARN);
+ if (mi)
{
- my_message(ER_SLAVE_MUST_STOP, ER(ER_SLAVE_MUST_STOP), MYF(0));
- result= true;
+ mysql_mutex_lock(&mi->rli.data_lock);
+ res= *((uint*) (((uchar*) mi) + master_info_offset));
+ mysql_mutex_unlock(&mi->rli.data_lock);
}
- mysql_mutex_unlock(&active_mi->rli.run_lock);
- mysql_mutex_unlock(&LOCK_active_mi);
- return result;
+ mysql_mutex_unlock(&LOCK_active_mi);
+ return res;
}
-static bool fix_slave_skip_counter(sys_var *self, THD *thd, enum_var_type type)
+
+
+bool update_multi_source_variable(sys_var *self_var, THD *thd,
+ enum_var_type type)
{
+ Sys_var_multi_source_ulong *self= (Sys_var_multi_source_ulong*) self_var;
+ bool result= true;
+ Master_info *mi;
+
mysql_mutex_lock(&LOCK_active_mi);
- mysql_mutex_lock(&active_mi->rli.run_lock);
- /*
- The following test should normally never be true as we test this
- in the check function; To be safe against multiple
- SQL_SLAVE_SKIP_COUNTER request, we do the check anyway
- */
- if (!active_mi->rli.slave_running)
+ mi= master_info_index->
+ get_master_info(&thd->variables.default_master_connection,
+ MYSQL_ERROR::WARN_LEVEL_ERROR);
+ if (mi)
{
- mysql_mutex_lock(&active_mi->rli.data_lock);
- active_mi->rli.slave_skip_counter= sql_slave_skip_counter;
- mysql_mutex_unlock(&active_mi->rli.data_lock);
+ mysql_mutex_lock(&mi->rli.run_lock);
+ mysql_mutex_lock(&mi->rli.data_lock);
+ result= self->update_variable(thd, mi);
+ mysql_mutex_unlock(&mi->rli.data_lock);
+ mysql_mutex_unlock(&mi->rli.run_lock);
}
- mysql_mutex_unlock(&active_mi->rli.run_lock);
mysql_mutex_unlock(&LOCK_active_mi);
- return 0;
+ return result;
+}
+
+static bool update_slave_skip_counter(sys_var *self, THD *thd, Master_info *mi)
+{
+ if (mi->rli.slave_running)
+ {
+ my_error(ER_SLAVE_MUST_STOP, MYF(0), mi->connection_name.length,
+ mi->connection_name.str);
+ return true;
+ }
+ /* The value was stored temporarly in thd */
+ mi->rli.slave_skip_counter= thd->variables.slave_skip_counter;
+ return false;
+}
+
+
+static Sys_var_multi_source_ulong
+Sys_slave_skip_counter("sql_slave_skip_counter",
+ "Skip the next N events from the master log",
+ SESSION_VAR(slave_skip_counter),
+ NO_CMD_LINE,
+ offsetof(Master_info, rli.slave_skip_counter),
+ VALID_RANGE(0, UINT_MAX), DEFAULT(0), BLOCK_SIZE(1),
+ ON_UPDATE(update_slave_skip_counter));
+
+
+static bool update_max_relay_log_size(sys_var *self, THD *thd, Master_info *mi)
+{
+ mi->rli.max_relay_log_size= thd->variables.max_relay_log_size;
+ mi->rli.relay_log.set_max_size(mi->rli.max_relay_log_size);
+ return false;
}
-static Sys_var_uint Sys_slave_skip_counter(
- "sql_slave_skip_counter", "sql_slave_skip_counter",
- GLOBAL_VAR(sql_slave_skip_counter), NO_CMD_LINE,
- VALID_RANGE(0, UINT_MAX), DEFAULT(0), BLOCK_SIZE(1),
- NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_slave_skip_counter),
- ON_UPDATE(fix_slave_skip_counter));
+
+static Sys_var_multi_source_ulong
+Sys_max_relay_log_size( "max_relay_log_size",
+ "relay log will be rotated automatically when the "
+ "size exceeds this value. If 0 are startup, it's "
+ "set to max_binlog_size",
+ SESSION_VAR(max_relay_log_size),
+ CMD_LINE(REQUIRED_ARG),
+ offsetof(Master_info, rli.max_relay_log_size),
+ VALID_RANGE(0, 1024L*1024*1024), DEFAULT(0),
+ BLOCK_SIZE(IO_SIZE),
+ ON_UPDATE(update_max_relay_log_size));
static Sys_var_charptr Sys_slave_skip_errors(
"slave_skip_errors", "Tells the slave thread to continue "
@@ -3788,22 +3779,25 @@ static bool check_locale(sys_var *self, THD *thd, set_var *var)
if (!locale->errmsgs->errmsgs)
{
+ bool res;
mysql_mutex_lock(&LOCK_error_messages);
- if (!locale->errmsgs->errmsgs &&
- read_texts(ERRMSG_FILE, locale->errmsgs->language,
- &locale->errmsgs->errmsgs,
- ER_ERROR_LAST - ER_ERROR_FIRST + 1))
+ res= (!locale->errmsgs->errmsgs &&
+ read_texts(ERRMSG_FILE, locale->errmsgs->language,
+ &locale->errmsgs->errmsgs,
+ ER_ERROR_LAST - ER_ERROR_FIRST + 1));
+ mysql_mutex_unlock(&LOCK_error_messages);
+ if (res)
{
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
"Can't process error message file for locale '%s'",
locale->name);
- mysql_mutex_unlock(&LOCK_error_messages);
return true;
}
- mysql_mutex_unlock(&LOCK_error_messages);
}
+ status_var_increment(thd->status_var.feature_locale);
return false;
}
+
static Sys_var_struct Sys_lc_messages(
"lc_messages", "Set the language used for the error messages",
SESSION_VAR(lc_messages), NO_CMD_LINE,
diff --git a/sql/sys_vars.h b/sql/sys_vars.h
index ff7ac46d96f..54ae4025a65 100644
--- a/sql/sys_vars.h
+++ b/sql/sys_vars.h
@@ -568,11 +568,13 @@ public:
option.var_type= GET_STR;
}
+ bool do_check(THD *thd, set_var *var)
+ {
+ return Sys_var_charptr::do_string_check(thd, var, charset(thd));
+ }
bool check_update_type(Item_result type)
{ return type != STRING_RESULT; }
- bool do_check(THD *thd, set_var *var);
-
void session_save_default(THD *thd, set_var *var)
{ DBUG_ASSERT(FALSE); }
@@ -633,6 +635,93 @@ public:
}
};
+
+/*
+ A LEX_STRING stored only in thd->variables
+ Only to be used for small buffers
+*/
+
+class Sys_var_session_lexstring: public sys_var
+{
+ size_t max_length;
+public:
+ Sys_var_session_lexstring(const char *name_arg,
+ const char *comment, int flag_args,
+ ptrdiff_t off, size_t size, CMD_LINE getopt,
+ enum charset_enum is_os_charset_arg,
+ const char *def_val, size_t max_length_arg,
+ on_check_function on_check_func=0,
+ on_update_function on_update_func=0)
+ : sys_var(&all_sys_vars, name_arg, comment, flag_args, off, getopt.id,
+ getopt.arg_type, SHOW_CHAR, (intptr)def_val,
+ 0, VARIABLE_NOT_IN_BINLOG, on_check_func, on_update_func,
+ 0, 0),max_length(max_length_arg)
+ {
+ option.var_type= GET_NO_ARG;
+ SYSVAR_ASSERT(scope() == ONLY_SESSION)
+ *const_cast<SHOW_TYPE*>(&show_val_type)= SHOW_LEX_STRING;
+ }
+ bool do_check(THD *thd, set_var *var)
+ {
+ char buff[STRING_BUFFER_USUAL_SIZE];
+ String str(buff, sizeof(buff), system_charset_info), *res;
+
+ if (!(res=var->value->val_str(&str)))
+ {
+ var->save_result.string_value.str= 0; /* NULL */
+ var->save_result.string_value.length= 0;
+ }
+ else
+ {
+ if (res->length() > max_length)
+ {
+ my_error(ER_WRONG_STRING_LENGTH, MYF(0),
+ res->ptr(), name.str, (int) max_length);
+ return true;
+ }
+ var->save_result.string_value.str= thd->strmake(res->ptr(),
+ res->length());
+ var->save_result.string_value.length= res->length();
+ }
+ return false;
+ }
+ bool session_update(THD *thd, set_var *var)
+ {
+ LEX_STRING *tmp= &session_var(thd, LEX_STRING);
+ tmp->length= var->save_result.string_value.length;
+ /* Store as \0 terminated string (just to be safe) */
+ strmake(tmp->str, var->save_result.string_value.str, tmp->length);
+ return false;
+ }
+ bool global_update(THD *thd, set_var *var)
+ {
+ DBUG_ASSERT(FALSE);
+ return false;
+ }
+ void session_save_default(THD *thd, set_var *var)
+ {
+ char *ptr= (char*)(intptr)option.def_value;
+ var->save_result.string_value.str= ptr;
+ var->save_result.string_value.length= strlen(ptr);
+ }
+ void global_save_default(THD *thd, set_var *var)
+ {
+ DBUG_ASSERT(FALSE);
+ }
+ uchar *session_value_ptr(THD *thd, LEX_STRING *base)
+ {
+ return (uchar*) &session_var(thd, LEX_STRING);
+ }
+ uchar *global_value_ptr(THD *thd, LEX_STRING *base)
+ {
+ DBUG_ASSERT(FALSE);
+ return NULL;
+ }
+ bool check_update_type(Item_result type)
+ { return type != STRING_RESULT; }
+};
+
+
#ifndef DBUG_OFF
/**
@@session.dbug and @@global.dbug variables.
@@ -959,7 +1048,7 @@ public:
Sys_var_max_user_conn(const char *name_arg,
const char *comment, int flag_args, ptrdiff_t off, size_t size,
CMD_LINE getopt,
- int min_val, int max_val, uint def_val,
+ int min_val, int max_val, int def_val,
uint block_size, PolyLock *lock=0,
enum binlog_status_enum binlog_status_arg=VARIABLE_NOT_IN_BINLOG,
on_check_function on_check_func=0,
@@ -1389,6 +1478,7 @@ public:
};
#endif /* defined(ENABLED_DEBUG_SYNC) */
+
/**
The class for bit variables - a variant of boolean that stores the value
in a bit.
@@ -1878,14 +1968,86 @@ public:
const char *comment, int flag_args, ptrdiff_t off, size_t size,
CMD_LINE getopt,
const char *values[], uint def_val, PolyLock *lock,
- enum binlog_status_enum binlog_status_arg,
- on_check_function on_check_func)
+ enum binlog_status_enum binlog_status_arg)
:Sys_var_enum(name_arg, comment, flag_args, off, size, getopt,
- values, def_val, lock, binlog_status_arg, on_check_func)
+ values, def_val, lock, binlog_status_arg)
{}
bool global_update(THD *thd, set_var *var);
};
+/*
+ Class for handing multi-source replication variables
+ Variable values are store in Master_info, but to make it possible to
+ access variable without locks we also store it thd->variables.
+ These can be used as GLOBAL or SESSION, but both points to the same
+ variable. This is to make things compatible with MySQL 5.5 where variables
+ like sql_slave_skip_counter are GLOBAL.
+*/
+
+class Sys_var_multi_source_ulong;
+class Master_info;
+
+typedef bool (*on_multi_source_update_function)(sys_var *self, THD *thd,
+ Master_info *mi);
+bool update_multi_source_variable(sys_var *self,
+ THD *thd, enum_var_type type);
+
+
+class Sys_var_multi_source_ulong :public Sys_var_ulong
+{
+ ptrdiff_t master_info_offset;
+ on_multi_source_update_function update_multi_source_variable_func;
+public:
+ Sys_var_multi_source_ulong(const char *name_arg,
+ const char *comment, int flag_args,
+ ptrdiff_t off, size_t size,
+ CMD_LINE getopt,
+ ptrdiff_t master_info_offset_arg,
+ uint min_val, uint max_val, uint def_val,
+ uint block_size,
+ on_multi_source_update_function on_update_func)
+ :Sys_var_ulong(name_arg, comment, flag_args, off, size,
+ getopt, min_val, max_val, def_val, block_size,
+ 0, VARIABLE_NOT_IN_BINLOG, 0, update_multi_source_variable),
+ master_info_offset(master_info_offset_arg),
+ update_multi_source_variable_func(on_update_func)
+ {
+ }
+ bool session_update(THD *thd, set_var *var)
+ {
+ session_var(thd, uint)= (uint) (var->save_result.ulonglong_value);
+ /* Value should be moved to multi_master in on_update_func */
+ return false;
+ }
+ bool global_update(THD *thd, set_var *var)
+ {
+ return session_update(thd, var);
+ }
+ void session_save_default(THD *thd, set_var *var)
+ {
+ /* Use value given in variable declaration */
+ global_save_default(thd, var);
+ }
+ uchar *session_value_ptr(THD *thd,LEX_STRING *base)
+ {
+ uint *tmp, res;
+ tmp= (uint*) (((uchar*)&(thd->variables)) + offset);
+ res= get_master_info_uint_value(thd, master_info_offset);
+ *tmp= res;
+ return (uchar*) tmp;
+ }
+ uchar *global_value_ptr(THD *thd, LEX_STRING *base)
+ {
+ return session_value_ptr(thd, base);
+ }
+ uint get_master_info_uint_value(THD *thd, ptrdiff_t offset);
+ bool update_variable(THD *thd, Master_info *mi)
+ {
+ return update_multi_source_variable_func(this, thd, mi);
+ }
+};
+
+
/****************************************************************************
Used templates
****************************************************************************/
diff --git a/sql/table.cc b/sql/table.cc
index 4a66ab6f118..f033c08553a 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -4394,7 +4394,7 @@ bool TABLE_LIST::prep_check_option(THD *thd, uint8 check_opt_type)
There are currently two mechanisms at work that handle errors for views,
this one and a more general mechanism based on an Internal_error_handler,
see Show_create_error_handler. The latter handles errors encountered during
- execution of SHOW CREATE VIEW, while the machanism using this method is
+ execution of SHOW CREATE VIEW, while the mechanism using this method is
handles SELECT from views. The two methods should not clash.
@param[in,out] thd thread handler
@@ -4545,7 +4545,14 @@ bool TABLE_LIST::check_single_table(TABLE_LIST **table_arg,
tbl;
tbl= tbl->next_local)
{
- if (tbl->table)
+ /*
+ Merged view has also temporary table attached (in 5.2 if it has table
+ then it was real table), so we have filter such temporary tables out
+ by checking that it is not merged view
+ */
+ if (tbl->table &&
+ !(tbl->is_view() &&
+ tbl->is_merged_derived()))
{
if (tbl->table->map & map)
{
@@ -4943,6 +4950,28 @@ void TABLE_LIST::set_check_materialized()
}
}
+TABLE *TABLE_LIST::get_real_join_table()
+{
+ TABLE_LIST *tbl= this;
+ while (tbl->table == NULL || tbl->table->reginfo.join_tab == NULL)
+ {
+ if (tbl->view == NULL && tbl->derived == NULL)
+ break;
+ /* we do not support merging of union yet */
+ DBUG_ASSERT(tbl->view == NULL ||
+ tbl->view->select_lex.next_select() == NULL);
+ DBUG_ASSERT(tbl->derived == NULL ||
+ tbl->derived->first_select()->next_select() == NULL);
+
+ if (tbl->table)
+ table= tbl->table;
+ tbl= (tbl->view != NULL ?
+ tbl->view->select_lex.get_table_list() :
+ tbl->derived->first_select()->get_table_list());
+ }
+ return tbl->table;
+}
+
Natural_join_column::Natural_join_column(Field_translator *field_param,
TABLE_LIST *tab)
diff --git a/sql/table.h b/sql/table.h
index 87affe984fc..458d661ceb5 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -29,6 +29,7 @@
#include "handler.h" /* row_type, ha_choice, handler */
#include "mysql_com.h" /* enum_field_types */
#include "thr_lock.h" /* thr_lock_type */
+#include "filesort_utils.h"
/* Structs that defines the TABLE */
@@ -299,11 +300,14 @@ enum tmp_table_type
};
enum release_type { RELEASE_NORMAL, RELEASE_WAIT_FOR_DROP };
-typedef struct st_filesort_info
+
+class Filesort_info
{
+ /// Buffer for sorting keys.
+ Filesort_buffer filesort_buffer;
+
+public:
IO_CACHE *io_cache; /* If sorted through filesort */
- uchar **sort_keys; /* Buffer for sorting keys */
- uint keys; /* Number of key pointers in buffer */
uchar *buffpek; /* Buffer for buffpek structures */
uint buffpek_len; /* Max number of buffpeks in the buffer */
uchar *addon_buf; /* Pointer to a buffer if sorted with fields */
@@ -312,7 +316,38 @@ typedef struct st_filesort_info
void (*unpack)(struct st_sort_addon_field *, uchar *, uchar *); /* To unpack back */
uchar *record_pointers; /* If sorted in memory */
ha_rows found_records; /* How many records in sort */
-} FILESORT_INFO;
+
+ /** Sort filesort_buffer */
+ void sort_buffer(Sort_param *param, uint count)
+ { filesort_buffer.sort_buffer(param, count); }
+
+ /**
+ Accessors for Filesort_buffer (which @c).
+ */
+ uchar *get_record_buffer(uint idx)
+ { return filesort_buffer.get_record_buffer(idx); }
+
+ uchar **get_sort_keys()
+ { return filesort_buffer.get_sort_keys(); }
+
+ uchar **alloc_sort_buffer(uint num_records, uint record_length)
+ { return filesort_buffer.alloc_sort_buffer(num_records, record_length); }
+
+ bool check_sort_buffer_properties(uint num_records, uint record_length)
+ {
+ return filesort_buffer.check_sort_buffer_properties(num_records,
+ record_length);
+ }
+
+ void free_sort_buffer()
+ { filesort_buffer.free_sort_buffer(); }
+
+ void init_record_pointers()
+ { filesort_buffer.init_record_pointers(); }
+
+ size_t sort_buffer_size() const
+ { return filesort_buffer.sort_buffer_size(); }
+};
/*
@@ -1053,15 +1088,20 @@ public:
uint db_stat; /* mode of file as in handler.h */
/* number of select if it is derived table */
uint derived_select_number;
- int current_lock; /* Type of lock on table */
- bool copy_blobs; /* copy_blobs when storing */
-
/*
0 or JOIN_TYPE_{LEFT|RIGHT}. Currently this is only compared to 0.
If maybe_null !=0, this table is inner w.r.t. some outer join operation,
and null_row may be true.
*/
uint maybe_null;
+ int current_lock; /* Type of lock on table */
+ bool copy_blobs; /* copy_blobs when storing */
+ /*
+ Set if next_number_field is in the UPDATE fields of INSERT ... ON DUPLICATE
+ KEY UPDATE.
+ */
+ bool next_number_field_updated;
+
/*
If true, the current table row is considered to have all columns set to
NULL, including columns declared as "not null" (see maybe_null).
@@ -1106,7 +1146,12 @@ public:
See TABLE_LIST::process_index_hints().
*/
bool force_index_group;
- bool distinct,const_table,no_rows, used_for_duplicate_elimination;
+ /*
+ TRUE<=> this table was created with create_tmp_table(... distinct=TRUE..)
+ call
+ */
+ bool distinct;
+ bool const_table,no_rows, used_for_duplicate_elimination;
/**
If set, the optimizer has found that row retrieval should access index
@@ -1136,7 +1181,7 @@ public:
REGINFO reginfo; /* field connections */
MEM_ROOT mem_root;
GRANT_INFO grant;
- FILESORT_INFO sort;
+ Filesort_info sort;
/*
The arena which the items for expressions from the table definition
are associated with.
@@ -1929,6 +1974,7 @@ struct TABLE_LIST
TABLE_LIST *find_underlying_table(TABLE *table);
TABLE_LIST *first_leaf_for_name_resolution();
TABLE_LIST *last_leaf_for_name_resolution();
+ TABLE *get_real_join_table();
bool is_leaf_for_name_resolution();
inline TABLE_LIST *top_table()
{ return belong_to_view ? belong_to_view : this; }
diff --git a/sql/tztime.cc b/sql/tztime.cc
index 9fae9f3fedd..ba24cab9ca7 100644
--- a/sql/tztime.cc
+++ b/sql/tztime.cc
@@ -2329,7 +2329,6 @@ my_tz_find(THD *thd, const String *name)
if (!str_to_offset(name->ptr(), name->length(), &offset))
{
-
if (!(result_tz= (Time_zone_offset *)my_hash_search(&offset_tzs,
(const uchar *)&offset,
sizeof(long))))
@@ -2371,6 +2370,9 @@ my_tz_find(THD *thd, const String *name)
mysql_mutex_unlock(&tz_LOCK);
+ if (result_tz && result_tz != my_tz_SYSTEM && result_tz != my_tz_UTC)
+ status_var_increment(thd->status_var.feature_timezone);
+
DBUG_RETURN(result_tz);
}
diff --git a/sql/uniques.cc b/sql/uniques.cc
index ae50a1d3970..c246cd637bd 100644
--- a/sql/uniques.cc
+++ b/sql/uniques.cc
@@ -620,7 +620,6 @@ bool Unique::walk(tree_walk_action action, void *walk_action_arg)
bool Unique::get(TABLE *table)
{
- SORTPARAM sort_param;
table->sort.found_records=elements+tree.elements_in_tree;
if (my_b_tell(&file) == 0)
{
@@ -660,6 +659,7 @@ bool Unique::get(TABLE *table)
return 1;
reinit_io_cache(outfile,WRITE_CACHE,0L,0,0);
+ Sort_param sort_param;
bzero((char*) &sort_param,sizeof(sort_param));
sort_param.max_rows= elements;
sort_param.sort_form=table;
@@ -667,14 +667,15 @@ bool Unique::get(TABLE *table)
full_size;
sort_param.min_dupl_count= min_dupl_count;
sort_param.res_length= 0;
- sort_param.keys= (uint) (max_in_memory_size / sort_param.sort_length);
+ sort_param.max_keys_per_buffer=
+ (uint) (max_in_memory_size / sort_param.sort_length);
sort_param.not_killable=1;
- if (!(sort_buffer=(uchar*) my_malloc((sort_param.keys+1) *
+ if (!(sort_buffer=(uchar*) my_malloc((sort_param.max_keys_per_buffer+1) *
sort_param.sort_length,
MYF(0))))
return 1;
- sort_param.unique_buff= sort_buffer+(sort_param.keys*
+ sort_param.unique_buff= sort_buffer+(sort_param.max_keys_per_buffer *
sort_param.sort_length);
sort_param.compare= (qsort2_cmp) buffpek_compare;