summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorAndrei Elkin <aelkin@mysql.com>2009-11-10 20:45:15 +0200
committerAndrei Elkin <aelkin@mysql.com>2009-11-10 20:45:15 +0200
commit7eb789a723582a503e6c7b18afee5b92d39dbbd5 (patch)
tree4673f5dde3aac3f1173eb33cbd96a67269d48499 /sql
parent19c380aaff1f1f3c0d21ac0c18904c21d7bdce76 (diff)
parent3c1e1f6d6c7caa8916a03ac9594ff950e6b380e3 (diff)
downloadmariadb-git-7eb789a723582a503e6c7b18afee5b92d39dbbd5.tar.gz
merging 5.1 main -> 5.1-rep+2 -> 5.1-rep+3; binlog_unsafe , rpl_mysql_upgrade fail and are under treatment
Diffstat (limited to 'sql')
-rwxr-xr-xsql/CMakeLists.txt1
-rw-r--r--sql/Makefile.am2
-rw-r--r--sql/debug_sync.cc1906
-rw-r--r--sql/debug_sync.h60
-rw-r--r--sql/event_db_repository.cc2
-rw-r--r--sql/field.cc75
-rw-r--r--sql/field.h8
-rw-r--r--sql/ha_ndbcluster.cc4
-rw-r--r--sql/ha_ndbcluster_binlog.cc9
-rw-r--r--sql/ha_partition.cc184
-rw-r--r--sql/ha_partition.h23
-rw-r--r--sql/handler.cc42
-rw-r--r--sql/handler.h9
-rw-r--r--sql/item.cc21
-rw-r--r--sql/item_cmpfunc.cc17
-rw-r--r--sql/item_func.cc6
-rw-r--r--sql/item_strfunc.cc7
-rw-r--r--sql/item_subselect.cc10
-rw-r--r--sql/item_timefunc.cc21
-rw-r--r--sql/item_xmlfunc.cc6
-rw-r--r--sql/log.cc14
-rw-r--r--sql/log_event.cc117
-rw-r--r--sql/log_event.h6
-rw-r--r--sql/log_event_old.cc130
-rw-r--r--sql/mysql_priv.h6
-rw-r--r--sql/mysql_priv.h.pp1
-rw-r--r--sql/mysqld.cc78
-rw-r--r--sql/opt_range.cc80
-rw-r--r--sql/opt_sum.cc3
-rw-r--r--sql/partition_info.cc6
-rw-r--r--sql/repl_failsafe.cc4
-rw-r--r--sql/rpl_filter.cc2
-rw-r--r--sql/rpl_rli.cc2
-rw-r--r--sql/set_var.cc20
-rw-r--r--sql/set_var.h18
-rw-r--r--sql/share/errmsg.txt8
-rw-r--r--sql/slave.cc110
-rw-r--r--sql/slave.h3
-rw-r--r--sql/sql_acl.cc3
-rw-r--r--sql/sql_base.cc57
-rw-r--r--sql/sql_binlog.cc51
-rw-r--r--sql/sql_cache.cc2
-rw-r--r--sql/sql_class.cc40
-rw-r--r--sql/sql_class.h60
-rw-r--r--sql/sql_connect.cc2
-rw-r--r--sql/sql_db.cc10
-rw-r--r--sql/sql_delete.cc148
-rw-r--r--sql/sql_handler.cc7
-rw-r--r--sql/sql_insert.cc87
-rw-r--r--sql/sql_lex.h7
-rw-r--r--sql/sql_load.cc114
-rw-r--r--sql/sql_parse.cc73
-rw-r--r--sql/sql_partition.cc4
-rw-r--r--sql/sql_plugin.cc22
-rw-r--r--sql/sql_repl.cc14
-rw-r--r--sql/sql_select.cc70
-rw-r--r--sql/sql_select.h17
-rw-r--r--sql/sql_show.cc147
-rw-r--r--sql/sql_table.cc95
-rw-r--r--sql/sql_yacc.yy20
-rw-r--r--sql/strfunc.cc2
-rw-r--r--sql/table.cc34
-rw-r--r--sql/table.h12
-rw-r--r--sql/udf_example.c4
-rw-r--r--sql/unireg.cc8
65 files changed, 3499 insertions, 632 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt
index 18508468f60..46cad441e12 100755
--- a/sql/CMakeLists.txt
+++ b/sql/CMakeLists.txt
@@ -64,6 +64,7 @@ SET (SQL_SOURCE
sql_error.cc sql_handler.cc sql_help.cc sql_insert.cc sql_lex.cc
sql_list.cc sql_load.cc sql_manager.cc sql_map.cc sql_parse.cc
sql_partition.cc sql_plugin.cc sql_prepare.cc sql_rename.cc
+ debug_sync.cc debug_sync.h
sql_repl.cc sql_select.cc sql_show.cc sql_state.c sql_string.cc
sql_table.cc sql_test.cc sql_trigger.cc sql_udf.cc sql_union.cc
sql_update.cc sql_view.cc strfunc.cc table.cc thr_malloc.cc
diff --git a/sql/Makefile.am b/sql/Makefile.am
index afce7db59f2..d72227f3953 100644
--- a/sql/Makefile.am
+++ b/sql/Makefile.am
@@ -58,6 +58,7 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \
ha_ndbcluster.h ha_ndbcluster_cond.h \
ha_ndbcluster_binlog.h ha_ndbcluster_tables.h \
ha_partition.h rpl_constants.h \
+ debug_sync.h \
opt_range.h protocol.h rpl_tblmap.h rpl_utility.h \
rpl_reporting.h \
log.h sql_show.h rpl_rli.h rpl_mi.h \
@@ -103,6 +104,7 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \
discover.cc time.cc opt_range.cc opt_sum.cc \
records.cc filesort.cc handler.cc \
ha_partition.cc \
+ debug_sync.cc \
sql_db.cc sql_table.cc sql_rename.cc sql_crypt.cc \
sql_load.cc mf_iocache.cc field_conv.cc sql_show.cc \
sql_udf.cc sql_analyse.cc sql_analyse.h sql_cache.cc \
diff --git a/sql/debug_sync.cc b/sql/debug_sync.cc
new file mode 100644
index 00000000000..2580d526b52
--- /dev/null
+++ b/sql/debug_sync.cc
@@ -0,0 +1,1906 @@
+/* Copyright (C) 2008 MySQL AB, 2008 - 2009 Sun Microsystems, Inc.
+
+ 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 */
+
+/**
+ == Debug Sync Facility ==
+
+ The Debug Sync Facility allows placement of synchronization points in
+ the server code by using the DEBUG_SYNC macro:
+
+ open_tables(...)
+
+ DEBUG_SYNC(thd, "after_open_tables");
+
+ lock_tables(...)
+
+ When activated, a sync point can
+
+ - Emit a signal and/or
+ - Wait for a signal
+
+ Nomenclature:
+
+ - signal: A value of a global variable that persists
+ until overwritten by a new signal. The global
+ variable can also be seen as a "signal post"
+ or "flag mast". Then the signal is what is
+ attached to the "signal post" or "flag mast".
+
+ - emit a signal: Assign the value (the signal) to the global
+ variable ("set a flag") and broadcast a
+ global condition to wake those waiting for
+ a signal.
+
+ - wait for a signal: Loop over waiting for the global condition until
+ the global value matches the wait-for signal.
+
+ By default, all sync points are inactive. They do nothing (except to
+ burn a couple of CPU cycles for checking if they are active).
+
+ A sync point becomes active when an action is requested for it.
+ To do so, put a line like this in the test case file:
+
+ SET DEBUG_SYNC= 'after_open_tables SIGNAL opened WAIT_FOR flushed';
+
+ This activates the sync point 'after_open_tables'. It requests it to
+ emit the signal 'opened' and wait for another thread to emit the signal
+ 'flushed' when the thread's execution runs through the sync point.
+
+ For every sync point there can be one action per thread only. Every
+ thread can request multiple actions, but only one per sync point. In
+ other words, a thread can activate multiple sync points.
+
+ Here is an example how to activate and use the sync points:
+
+ --connection conn1
+ SET DEBUG_SYNC= 'after_open_tables SIGNAL opened WAIT_FOR flushed';
+ send INSERT INTO t1 VALUES(1);
+ --connection conn2
+ SET DEBUG_SYNC= 'now WAIT_FOR opened';
+ SET DEBUG_SYNC= 'after_abort_locks SIGNAL flushed';
+ FLUSH TABLE t1;
+
+ When conn1 runs through the INSERT statement, it hits the sync point
+ 'after_open_tables'. It notices that it is active and executes its
+ action. It emits the signal 'opened' and waits for another thread to
+ emit the signal 'flushed'.
+
+ conn2 waits immediately at the special sync point 'now' for another
+ thread to emit the 'opened' signal.
+
+ A signal remains in effect until it is overwritten. If conn1 signals
+ 'opened' before conn2 reaches 'now', conn2 will still find the 'opened'
+ signal. It does not wait in this case.
+
+ When conn2 reaches 'after_abort_locks', it signals 'flushed', which lets
+ conn1 awake.
+
+ Normally the activation of a sync point is cleared when it has been
+ executed. Sometimes it is necessary to keep the sync point active for
+ another execution. You can add an execute count to the action:
+
+ SET DEBUG_SYNC= 'name SIGNAL sig EXECUTE 3';
+
+ This sets the signal point's activation counter to 3. Each execution
+ decrements the counter. After the third execution the sync point
+ becomes inactive.
+
+ One of the primary goals of this facility is to eliminate sleeps from
+ the test suite. In most cases it should be possible to rewrite test
+ cases so that they do not need to sleep. (But this facility cannot
+ synchronize multiple processes.) However, to support test development,
+ and as a last resort, sync point waiting times out. There is a default
+ timeout, but it can be overridden:
+
+ SET DEBUG_SYNC= 'name WAIT_FOR sig TIMEOUT 10 EXECUTE 2';
+
+ TIMEOUT 0 is special: If the signal is not present, the wait times out
+ immediately.
+
+ When a wait timed out (even on TIMEOUT 0), a warning is generated so
+ that it shows up in the test result.
+
+ You can throw an error message and kill the query when a synchronization
+ point is hit a certain number of times:
+
+ SET DEBUG_SYNC= 'name HIT_LIMIT 3';
+
+ Or combine it with signal and/or wait:
+
+ SET DEBUG_SYNC= 'name SIGNAL sig EXECUTE 2 HIT_LIMIT 3';
+
+ Here the first two hits emit the signal, the third hit returns the error
+ message and kills the query.
+
+ For cases where you are not sure that an action is taken and thus
+ cleared in any case, you can force to clear (deactivate) a sync point:
+
+ SET DEBUG_SYNC= 'name CLEAR';
+
+ If you want to clear all actions and clear the global signal, use:
+
+ SET DEBUG_SYNC= 'RESET';
+
+ This is the only way to reset the global signal to an empty string.
+
+ For testing of the facility itself you can execute a sync point just
+ as if it had been hit:
+
+ SET DEBUG_SYNC= 'name TEST';
+
+
+ === Formal Syntax ===
+
+ The string to "assign" to the DEBUG_SYNC variable can contain:
+
+ {RESET |
+ <sync point name> TEST |
+ <sync point name> CLEAR |
+ <sync point name> {{SIGNAL <signal name> |
+ WAIT_FOR <signal name> [TIMEOUT <seconds>]}
+ [EXECUTE <count>] &| HIT_LIMIT <count>}
+
+ Here '&|' means 'and/or'. This means that one of the sections
+ separated by '&|' must be present or both of them.
+
+
+ === Activation/Deactivation ===
+
+ The facility is an optional part of the MySQL server.
+ It is enabled in a debug server by default.
+
+ ./configure --enable-debug-sync
+
+ The Debug Sync Facility, when compiled in, is disabled by default. It
+ can be enabled by a mysqld command line option:
+
+ --debug-sync-timeout[=default_wait_timeout_value_in_seconds]
+
+ 'default_wait_timeout_value_in_seconds' is the default timeout for the
+ WAIT_FOR action. If set to zero, the facility stays disabled.
+
+ The facility is enabled by default in the test suite, but can be
+ disabled with:
+
+ mysql-test-run.pl ... --debug-sync-timeout=0 ...
+
+ Likewise the default wait timeout can be set:
+
+ mysql-test-run.pl ... --debug-sync-timeout=10 ...
+
+ The command line option influences the readable value of the system
+ variable 'debug_sync'.
+
+ * If the facility is not compiled in, the system variable does not exist.
+
+ * If --debug-sync-timeout=0 the value of the variable reads as "OFF".
+
+ * Otherwise the value reads as "ON - current signal: " followed by the
+ current signal string, which can be empty.
+
+ The readable variable value is the same, regardless if read as global
+ or session value.
+
+ Setting the 'debug-sync' system variable requires 'SUPER' privilege.
+ You can never read back the string that you assigned to the variable,
+ unless you assign the value that the variable does already have. But
+ that would give a parse error. A syntactically correct string is
+ parsed into a debug sync action and stored apart from the variable value.
+
+
+ === Implementation ===
+
+ Pseudo code for a sync point:
+
+ #define DEBUG_SYNC(thd, sync_point_name)
+ if (unlikely(opt_debug_sync_timeout))
+ debug_sync(thd, STRING_WITH_LEN(sync_point_name))
+
+ The sync point performs a binary search in a sorted array of actions
+ for this thread.
+
+ The SET DEBUG_SYNC statement adds a requested action to the array or
+ overwrites an existing action for the same sync point. When it adds a
+ new action, the array is sorted again.
+
+
+ === A typical synchronization pattern ===
+
+ There are quite a few places in MySQL, where we use a synchronization
+ pattern like this:
+
+ pthread_mutex_lock(&mutex);
+ thd->enter_cond(&condition_variable, &mutex, new_message);
+ #if defined(ENABLE_DEBUG_SYNC)
+ if (!thd->killed && !end_of_wait_condition)
+ DEBUG_SYNC(thd, "sync_point_name");
+ #endif
+ while (!thd->killed && !end_of_wait_condition)
+ pthread_cond_wait(&condition_variable, &mutex);
+ thd->exit_cond(old_message);
+
+ Here some explanations:
+
+ thd->enter_cond() is used to register the condition variable and the
+ mutex in thd->mysys_var. This is done to allow the thread to be
+ interrupted (killed) from its sleep. Another thread can find the
+ condition variable to signal and mutex to use for synchronization in
+ this thread's THD::mysys_var.
+
+ thd->enter_cond() requires the mutex to be acquired in advance.
+
+ thd->exit_cond() unregisters the condition variable and mutex and
+ releases the mutex.
+
+ If you want to have a Debug Sync point with the wait, please place it
+ behind enter_cond(). Only then you can safely decide, if the wait will
+ be taken. Also you will have THD::proc_info correct when the sync
+ point emits a signal. DEBUG_SYNC sets its own proc_info, but restores
+ the previous one before releasing its internal mutex. As soon as
+ another thread sees the signal, it does also see the proc_info from
+ before entering the sync point. In this case it will be "new_message",
+ which is associated with the wait that is to be synchronized.
+
+ In the example above, the wait condition is repeated before the sync
+ point. This is done to skip the sync point, if no wait takes place.
+ The sync point is before the loop (not inside the loop) to have it hit
+ once only. It is possible that the condition variable is signaled
+ multiple times without the wait condition to be true.
+
+ A bit off-topic: At some places, the loop is taken around the whole
+ synchronization pattern:
+
+ while (!thd->killed && !end_of_wait_condition)
+ {
+ pthread_mutex_lock(&mutex);
+ thd->enter_cond(&condition_variable, &mutex, new_message);
+ if (!thd->killed [&& !end_of_wait_condition])
+ {
+ [DEBUG_SYNC(thd, "sync_point_name");]
+ pthread_cond_wait(&condition_variable, &mutex);
+ }
+ thd->exit_cond(old_message);
+ }
+
+ Note that it is important to repeat the test for thd->killed after
+ enter_cond(). Otherwise the killing thread may kill this thread after
+ it tested thd->killed in the loop condition and before it registered
+ the condition variable and mutex in enter_cond(). In this case, the
+ killing thread does not know that this thread is going to wait on a
+ condition variable. It would just set THD::killed. But if we would not
+ test it again, we would go asleep though we are killed. If the killing
+ thread would kill us when we are after the second test, but still
+ before sleeping, we hold the mutex, which is registered in mysys_var.
+ The killing thread would try to acquire the mutex before signaling
+ the condition variable. Since the mutex is only released implicitly in
+ pthread_cond_wait(), the signaling happens at the right place. We
+ have a safe synchronization.
+
+ === Co-work with the DBUG facility ===
+
+ When running the MySQL test suite with the --debug command line
+ option, the Debug Sync Facility writes trace messages to the DBUG
+ trace. The following shell commands proved very useful in extracting
+ relevant information:
+
+ egrep 'query:|debug_sync_exec:' mysql-test/var/log/mysqld.1.trace
+
+ It shows all executed SQL statements and all actions executed by
+ synchronization points.
+
+ Sometimes it is also useful to see, which synchronization points have
+ been run through (hit) with or without executing actions. Then add
+ "|debug_sync_point:" to the egrep pattern.
+
+ === Further reading ===
+
+ For a discussion of other methods to synchronize threads see
+ http://forge.mysql.com/wiki/MySQL_Internals_Test_Synchronization
+
+ For complete syntax tests, functional tests, and examples see the test
+ case debug_sync.test.
+
+ See also worklog entry WL#4259 - Test Synchronization Facility
+*/
+
+#include "debug_sync.h"
+
+#if defined(ENABLED_DEBUG_SYNC)
+
+/*
+ Due to weaknesses in our include files, we need to include
+ mysql_priv.h here. To have THD declared, we need to include
+ sql_class.h. This includes log_event.h, which in turn requires
+ declarations from mysql_priv.h (e.g. OPTION_AUTO_IS_NULL).
+ mysql_priv.h includes almost everything, so is sufficient here.
+*/
+#include "mysql_priv.h"
+
+/*
+ Action to perform at a synchronization point.
+ NOTE: This structure is moved around in memory by realloc(), qsort(),
+ and memmove(). Do not add objects with non-trivial constuctors
+ or destructors, which might prevent moving of this structure
+ with these functions.
+*/
+struct st_debug_sync_action
+{
+ ulong activation_count; /* max(hit_limit, execute) */
+ ulong hit_limit; /* hits before kill query */
+ ulong execute; /* executes before self-clear */
+ ulong timeout; /* wait_for timeout */
+ String signal; /* signal to emit */
+ String wait_for; /* signal to wait for */
+ String sync_point; /* sync point name */
+ bool need_sort; /* if new action, array needs sort */
+};
+
+/* Debug sync control. Referenced by THD. */
+struct st_debug_sync_control
+{
+ st_debug_sync_action *ds_action; /* array of actions */
+ uint ds_active; /* # active actions */
+ uint ds_allocated; /* # allocated actions */
+ ulonglong dsp_hits; /* statistics */
+ ulonglong dsp_executed; /* statistics */
+ ulonglong dsp_max_active; /* statistics */
+ /*
+ thd->proc_info points at unsynchronized memory.
+ It must not go away as long as the thread exists.
+ */
+ char ds_proc_info[80]; /* proc_info string */
+};
+
+
+/**
+ Definitions for the debug sync facility.
+ 1. Global string variable to hold a "signal" ("signal post", "flag mast").
+ 2. Global condition variable for signaling and waiting.
+ 3. Global mutex to synchronize access to the above.
+*/
+struct st_debug_sync_globals
+{
+ String ds_signal; /* signal variable */
+ pthread_cond_t ds_cond; /* condition variable */
+ pthread_mutex_t ds_mutex; /* mutex variable */
+ ulonglong dsp_hits; /* statistics */
+ ulonglong dsp_executed; /* statistics */
+ ulonglong dsp_max_active; /* statistics */
+};
+static st_debug_sync_globals debug_sync_global; /* All globals in one object */
+
+/**
+ Callback pointer for C files.
+*/
+extern "C" void (*debug_sync_C_callback_ptr)(const char *, size_t);
+
+
+/**
+ Callback for debug sync, to be used by C files. See thr_lock.c for example.
+
+ @description
+
+ We cannot place a sync point directly in C files (like those in mysys or
+ certain storage engines written mostly in C like MyISAM or Maria). Because
+ they are C code and do not include mysql_priv.h. So they do not know the
+ macro DEBUG_SYNC(thd, sync_point_name). The macro needs a 'thd' argument.
+ Hence it cannot be used in files outside of the sql/ directory.
+
+ The workaround is to call back simple functions like this one from
+ non-sql/ files.
+
+ We want to allow modules like thr_lock to be used without sql/ and
+ especially without Debug Sync. So we cannot just do a simple call
+ of the callback function. Instead we provide a global pointer in
+ the other file, which is to be set to the callback by Debug Sync.
+ If the pointer is not set, no call back will be done. If Debug
+ Sync sets the pointer to a callback function like this one, it will
+ be called. That way thr_lock.c does not have an undefined reference
+ to Debug Sync and can be used without it. Debug Sync, in contrast,
+ has an undefined reference to that pointer and thus requires
+ thr_lock to be linked too. But this is not a problem as it is part
+ of the MySQL server anyway.
+
+ @note
+ The callback pointer in C files is set only if debug sync is
+ initialized. And this is done only if opt_debug_sync_timeout is set.
+*/
+
+static void debug_sync_C_callback(const char *sync_point_name,
+ size_t name_len)
+{
+ if (unlikely(opt_debug_sync_timeout))
+ debug_sync(current_thd, sync_point_name, name_len);
+}
+
+
+/**
+ Initialize the debug sync facility at server start.
+
+ @return status
+ @retval 0 ok
+ @retval != 0 error
+*/
+
+int debug_sync_init(void)
+{
+ DBUG_ENTER("debug_sync_init");
+
+ if (opt_debug_sync_timeout)
+ {
+ int rc;
+
+ /* Initialize the global variables. */
+ debug_sync_global.ds_signal.length(0);
+ if ((rc= pthread_cond_init(&debug_sync_global.ds_cond, NULL)) ||
+ (rc= pthread_mutex_init(&debug_sync_global.ds_mutex,
+ MY_MUTEX_INIT_FAST)))
+ DBUG_RETURN(rc); /* purecov: inspected */
+
+ /* Set the call back pointer in C files. */
+ debug_sync_C_callback_ptr= debug_sync_C_callback;
+ }
+
+ DBUG_RETURN(0);
+}
+
+
+/**
+ End the debug sync facility.
+
+ @description
+ This is called at server shutdown or after a thread initialization error.
+*/
+
+void debug_sync_end(void)
+{
+ DBUG_ENTER("debug_sync_end");
+
+ /* End the facility only if it had been initialized. */
+ if (debug_sync_C_callback_ptr)
+ {
+ /* Clear the call back pointer in C files. */
+ debug_sync_C_callback_ptr= NULL;
+
+ /* Destroy the global variables. */
+ debug_sync_global.ds_signal.free();
+ (void) pthread_cond_destroy(&debug_sync_global.ds_cond);
+ (void) pthread_mutex_destroy(&debug_sync_global.ds_mutex);
+
+ /* Print statistics. */
+ {
+ char llbuff[22];
+ sql_print_information("Debug sync points hit: %22s",
+ llstr(debug_sync_global.dsp_hits, llbuff));
+ sql_print_information("Debug sync points executed: %22s",
+ llstr(debug_sync_global.dsp_executed, llbuff));
+ sql_print_information("Debug sync points max active per thread: %22s",
+ llstr(debug_sync_global.dsp_max_active, llbuff));
+ }
+ }
+
+ DBUG_VOID_RETURN;
+}
+
+
+/* purecov: begin tested */
+
+/**
+ Disable the facility after lack of memory if no error can be returned.
+
+ @note
+ Do not end the facility here because the global variables can
+ be in use by other threads.
+*/
+
+static void debug_sync_emergency_disable(void)
+{
+ DBUG_ENTER("debug_sync_emergency_disable");
+
+ opt_debug_sync_timeout= 0;
+
+ DBUG_PRINT("debug_sync",
+ ("Debug Sync Facility disabled due to lack of memory."));
+ sql_print_error("Debug Sync Facility disabled due to lack of memory.");
+
+ DBUG_VOID_RETURN;
+}
+
+/* purecov: end */
+
+
+/**
+ Initialize the debug sync facility at thread start.
+
+ @param[in] thd thread handle
+*/
+
+void debug_sync_init_thread(THD *thd)
+{
+ DBUG_ENTER("debug_sync_init_thread");
+ DBUG_ASSERT(thd);
+
+ if (opt_debug_sync_timeout)
+ {
+ thd->debug_sync_control= (st_debug_sync_control*)
+ my_malloc(sizeof(st_debug_sync_control), MYF(MY_WME | MY_ZEROFILL));
+ if (!thd->debug_sync_control)
+ {
+ /*
+ Error is reported by my_malloc().
+ We must disable the facility. We have no way to return an error.
+ */
+ debug_sync_emergency_disable(); /* purecov: tested */
+ }
+ }
+
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ End the debug sync facility at thread end.
+
+ @param[in] thd thread handle
+*/
+
+void debug_sync_end_thread(THD *thd)
+{
+ DBUG_ENTER("debug_sync_end_thread");
+ DBUG_ASSERT(thd);
+
+ if (thd->debug_sync_control)
+ {
+ st_debug_sync_control *ds_control= thd->debug_sync_control;
+
+ /*
+ This synchronization point can be used to synchronize on thread end.
+ This is the latest point in a THD's life, where this can be done.
+ */
+ DEBUG_SYNC(thd, "thread_end");
+
+ if (ds_control->ds_action)
+ {
+ st_debug_sync_action *action= ds_control->ds_action;
+ st_debug_sync_action *action_end= action + ds_control->ds_allocated;
+ for (; action < action_end; action++)
+ {
+ action->signal.free();
+ action->wait_for.free();
+ action->sync_point.free();
+ }
+ my_free(ds_control->ds_action, MYF(0));
+ }
+
+ /* Statistics. */
+ pthread_mutex_lock(&debug_sync_global.ds_mutex);
+ debug_sync_global.dsp_hits+= ds_control->dsp_hits;
+ debug_sync_global.dsp_executed+= ds_control->dsp_executed;
+ if (debug_sync_global.dsp_max_active < ds_control->dsp_max_active)
+ debug_sync_global.dsp_max_active= ds_control->dsp_max_active;
+ pthread_mutex_unlock(&debug_sync_global.ds_mutex);
+
+ my_free(ds_control, MYF(0));
+ thd->debug_sync_control= NULL;
+ }
+
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Move a string by length.
+
+ @param[out] to buffer for the resulting string
+ @param[in] to_end end of buffer
+ @param[in] from source string
+ @param[in] length number of bytes to copy
+
+ @return pointer to end of copied string
+*/
+
+static char *debug_sync_bmove_len(char *to, char *to_end,
+ const char *from, size_t length)
+{
+ DBUG_ASSERT(to);
+ DBUG_ASSERT(to_end);
+ DBUG_ASSERT(!length || from);
+ set_if_smaller(length, (size_t) (to_end - to));
+ memcpy(to, from, length);
+ return (to + length);
+}
+
+
+#if !defined(DBUG_OFF)
+
+/**
+ Create a string that describes an action.
+
+ @param[out] result buffer for the resulting string
+ @param[in] size size of result buffer
+ @param[in] action action to describe
+*/
+
+static void debug_sync_action_string(char *result, uint size,
+ st_debug_sync_action *action)
+{
+ char *wtxt= result;
+ char *wend= wtxt + size - 1; /* Allow emergency '\0'. */
+ DBUG_ASSERT(result);
+ DBUG_ASSERT(action);
+
+ /* If an execute count is present, signal or wait_for are needed too. */
+ DBUG_ASSERT(!action->execute ||
+ action->signal.length() || action->wait_for.length());
+
+ if (action->execute)
+ {
+ if (action->signal.length())
+ {
+ wtxt= debug_sync_bmove_len(wtxt, wend, STRING_WITH_LEN("SIGNAL "));
+ wtxt= debug_sync_bmove_len(wtxt, wend, action->signal.ptr(),
+ action->signal.length());
+ }
+ if (action->wait_for.length())
+ {
+ if ((wtxt == result) && (wtxt < wend))
+ *(wtxt++)= ' ';
+ wtxt= debug_sync_bmove_len(wtxt, wend, STRING_WITH_LEN(" WAIT_FOR "));
+ wtxt= debug_sync_bmove_len(wtxt, wend, action->wait_for.ptr(),
+ action->wait_for.length());
+
+ if (action->timeout != opt_debug_sync_timeout)
+ {
+ wtxt+= my_snprintf(wtxt, wend - wtxt, " TIMEOUT %lu", action->timeout);
+ }
+ }
+ if (action->execute != 1)
+ {
+ wtxt+= my_snprintf(wtxt, wend - wtxt, " EXECUTE %lu", action->execute);
+ }
+ }
+ if (action->hit_limit)
+ {
+ wtxt+= my_snprintf(wtxt, wend - wtxt, "%sHIT_LIMIT %lu",
+ (wtxt == result) ? "" : " ", action->hit_limit);
+ }
+
+ /*
+ If (wtxt == wend) string may not be terminated.
+ There is one byte left for an emergency termination.
+ */
+ *wtxt= '\0';
+}
+
+
+/**
+ Print actions.
+
+ @param[in] thd thread handle
+*/
+
+static void debug_sync_print_actions(THD *thd)
+{
+ st_debug_sync_control *ds_control= thd->debug_sync_control;
+ uint idx;
+ DBUG_ENTER("debug_sync_print_actions");
+ DBUG_ASSERT(thd);
+
+ if (!ds_control)
+ DBUG_VOID_RETURN;
+
+ for (idx= 0; idx < ds_control->ds_active; idx++)
+ {
+ const char *dsp_name= ds_control->ds_action[idx].sync_point.c_ptr();
+ char action_string[256];
+
+ debug_sync_action_string(action_string, sizeof(action_string),
+ ds_control->ds_action + idx);
+ DBUG_PRINT("debug_sync_list", ("%s %s", dsp_name, action_string));
+ }
+
+ DBUG_VOID_RETURN;
+}
+
+#endif /* !defined(DBUG_OFF) */
+
+
+/**
+ Compare two actions by sync point name length, string.
+
+ @param[in] arg1 reference to action1
+ @param[in] arg2 reference to action2
+
+ @return difference
+ @retval == 0 length1/string1 is same as length2/string2
+ @retval < 0 length1/string1 is smaller
+ @retval > 0 length1/string1 is bigger
+*/
+
+static int debug_sync_qsort_cmp(const void* arg1, const void* arg2)
+{
+ st_debug_sync_action *action1= (st_debug_sync_action*) arg1;
+ st_debug_sync_action *action2= (st_debug_sync_action*) arg2;
+ int diff;
+ DBUG_ASSERT(action1);
+ DBUG_ASSERT(action2);
+
+ if (!(diff= action1->sync_point.length() - action2->sync_point.length()))
+ diff= memcmp(action1->sync_point.ptr(), action2->sync_point.ptr(),
+ action1->sync_point.length());
+
+ return diff;
+}
+
+
+/**
+ Find a debug sync action.
+
+ @param[in] actionarr array of debug sync actions
+ @param[in] quantity number of actions in array
+ @param[in] dsp_name name of debug sync point to find
+ @param[in] name_len length of name of debug sync point
+
+ @return action
+ @retval != NULL found sync point in array
+ @retval NULL not found
+
+ @description
+ Binary search. Array needs to be sorted by length, sync point name.
+*/
+
+static st_debug_sync_action *debug_sync_find(st_debug_sync_action *actionarr,
+ int quantity,
+ const char *dsp_name,
+ uint name_len)
+{
+ st_debug_sync_action *action;
+ int low ;
+ int high ;
+ int mid ;
+ int diff ;
+ DBUG_ASSERT(actionarr);
+ DBUG_ASSERT(dsp_name);
+ DBUG_ASSERT(name_len);
+
+ low= 0;
+ high= quantity;
+
+ while (low < high)
+ {
+ mid= (low + high) / 2;
+ action= actionarr + mid;
+ if (!(diff= name_len - action->sync_point.length()) &&
+ !(diff= memcmp(dsp_name, action->sync_point.ptr(), name_len)))
+ return action;
+ if (diff > 0)
+ low= mid + 1;
+ else
+ high= mid - 1;
+ }
+
+ if (low < quantity)
+ {
+ action= actionarr + low;
+ if ((name_len == action->sync_point.length()) &&
+ !memcmp(dsp_name, action->sync_point.ptr(), name_len))
+ return action;
+ }
+
+ return NULL;
+}
+
+
+/**
+ Reset the debug sync facility.
+
+ @param[in] thd thread handle
+
+ @description
+ Remove all actions of this thread.
+ Clear the global signal.
+*/
+
+static void debug_sync_reset(THD *thd)
+{
+ st_debug_sync_control *ds_control= thd->debug_sync_control;
+ DBUG_ENTER("debug_sync_reset");
+ DBUG_ASSERT(thd);
+ DBUG_ASSERT(ds_control);
+
+ /* Remove all actions of this thread. */
+ ds_control->ds_active= 0;
+
+ /* Clear the global signal. */
+ pthread_mutex_lock(&debug_sync_global.ds_mutex);
+ debug_sync_global.ds_signal.length(0);
+ pthread_mutex_unlock(&debug_sync_global.ds_mutex);
+
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Remove a debug sync action.
+
+ @param[in] ds_control control object
+ @param[in] action action to be removed
+
+ @description
+ Removing an action mainly means to decrement the ds_active counter.
+ But if the action is between other active action in the array, then
+ the array needs to be shrinked. The active actions above the one to
+ be removed have to be moved down by one slot.
+*/
+
+static void debug_sync_remove_action(st_debug_sync_control *ds_control,
+ st_debug_sync_action *action)
+{
+ uint dsp_idx= action - ds_control->ds_action;
+ DBUG_ENTER("debug_sync_remove_action");
+ DBUG_ASSERT(ds_control);
+ DBUG_ASSERT(ds_control == current_thd->debug_sync_control);
+ DBUG_ASSERT(action);
+ DBUG_ASSERT(dsp_idx < ds_control->ds_active);
+
+ /* Decrement the number of currently active actions. */
+ ds_control->ds_active--;
+
+ /*
+ If this was not the last active action in the array, we need to
+ shift remaining active actions down to keep the array gap-free.
+ Otherwise binary search might fail or take longer than necessary at
+ least. Also new actions are always put to the end of the array.
+ */
+ if (ds_control->ds_active > dsp_idx)
+ {
+ /*
+ Do not make save_action an object of class st_debug_sync_action.
+ Its destructor would tamper with the String pointers.
+ */
+ uchar save_action[sizeof(st_debug_sync_action)];
+
+ /*
+ Copy the to-be-removed action object to temporary storage before
+ the shift copies the string pointers over. Do not use assignment
+ because it would use assignment operator methods for the Strings.
+ This would copy the strings. The shift below overwrite the string
+ pointers without freeing them first. By using memmove() we save
+ the pointers, which are overwritten by the shift.
+ */
+ memmove(save_action, action, sizeof(st_debug_sync_action));
+
+ /* Move actions down. */
+ memmove(ds_control->ds_action + dsp_idx,
+ ds_control->ds_action + dsp_idx + 1,
+ (ds_control->ds_active - dsp_idx) *
+ sizeof(st_debug_sync_action));
+
+ /*
+ Copy back the saved action object to the now free array slot. This
+ replaces the double references of String pointers that have been
+ produced by the shift. Again do not use an assignment operator to
+ avoid string allocation/copy.
+ */
+ memmove(ds_control->ds_action + ds_control->ds_active, save_action,
+ sizeof(st_debug_sync_action));
+ }
+
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Get a debug sync action.
+
+ @param[in] thd thread handle
+ @param[in] dsp_name debug sync point name
+ @param[in] name_len length of sync point name
+
+ @return action
+ @retval != NULL ok
+ @retval NULL error
+
+ @description
+ Find the debug sync action for a debug sync point or make a new one.
+*/
+
+static st_debug_sync_action *debug_sync_get_action(THD *thd,
+ const char *dsp_name,
+ uint name_len)
+{
+ st_debug_sync_control *ds_control= thd->debug_sync_control;
+ st_debug_sync_action *action;
+ DBUG_ENTER("debug_sync_get_action");
+ DBUG_ASSERT(thd);
+ DBUG_ASSERT(dsp_name);
+ DBUG_ASSERT(name_len);
+ DBUG_ASSERT(ds_control);
+ DBUG_PRINT("debug_sync", ("sync_point: '%.*s'", (int) name_len, dsp_name));
+ DBUG_PRINT("debug_sync", ("active: %u allocated: %u",
+ ds_control->ds_active, ds_control->ds_allocated));
+
+ /* There cannot be more active actions than allocated. */
+ DBUG_ASSERT(ds_control->ds_active <= ds_control->ds_allocated);
+ /* If there are active actions, the action array must be present. */
+ DBUG_ASSERT(!ds_control->ds_active || ds_control->ds_action);
+
+ /* Try to reuse existing action if there is one for this sync point. */
+ if (ds_control->ds_active &&
+ (action= debug_sync_find(ds_control->ds_action, ds_control->ds_active,
+ dsp_name, name_len)))
+ {
+ /* Reuse an already active sync point action. */
+ DBUG_ASSERT((uint)(action - ds_control->ds_action) < ds_control->ds_active);
+ DBUG_PRINT("debug_sync", ("reuse action idx: %ld",
+ (long) (action - ds_control->ds_action)));
+ }
+ else
+ {
+ /* Create a new action. */
+ int dsp_idx= ds_control->ds_active++;
+ set_if_bigger(ds_control->dsp_max_active, ds_control->ds_active);
+ if (ds_control->ds_active > ds_control->ds_allocated)
+ {
+ uint new_alloc= ds_control->ds_active + 3;
+ void *new_action= my_realloc(ds_control->ds_action,
+ new_alloc * sizeof(st_debug_sync_action),
+ MYF(MY_WME | MY_ALLOW_ZERO_PTR));
+ if (!new_action)
+ {
+ /* Error is reported by my_malloc(). */
+ goto err; /* purecov: tested */
+ }
+ ds_control->ds_action= (st_debug_sync_action*) new_action;
+ ds_control->ds_allocated= new_alloc;
+ /* Clear memory as we do not run string constructors here. */
+ bzero((uchar*) (ds_control->ds_action + dsp_idx),
+ (new_alloc - dsp_idx) * sizeof(st_debug_sync_action));
+ }
+ DBUG_PRINT("debug_sync", ("added action idx: %u", dsp_idx));
+ action= ds_control->ds_action + dsp_idx;
+ if (action->sync_point.copy(dsp_name, name_len, system_charset_info))
+ {
+ /* Error is reported by my_malloc(). */
+ goto err; /* purecov: tested */
+ }
+ action->need_sort= TRUE;
+ }
+ DBUG_ASSERT(action >= ds_control->ds_action);
+ DBUG_ASSERT(action < ds_control->ds_action + ds_control->ds_active);
+ DBUG_PRINT("debug_sync", ("action: 0x%lx array: 0x%lx count: %u",
+ (long) action, (long) ds_control->ds_action,
+ ds_control->ds_active));
+
+ DBUG_RETURN(action);
+
+ /* purecov: begin tested */
+ err:
+ DBUG_RETURN(NULL);
+ /* purecov: end */
+}
+
+
+/**
+ Set a debug sync action.
+
+ @param[in] thd thread handle
+ @param[in] action synchronization action
+
+ @return status
+ @retval FALSE ok
+ @retval TRUE error
+
+ @description
+ This is called from the debug sync parser. It arms the action for
+ the requested sync point. If the action parsed into an empty action,
+ it is removed instead.
+
+ Setting an action for a sync point means to make the sync point
+ active. When it is hit it will execute this action.
+
+ Before parsing, we "get" an action object. This is placed at the
+ end of the thread's action array unless the requested sync point
+ has an action already.
+
+ Then the parser fills the action object from the request string.
+
+ Finally the action is "set" for the sync point. If it was parsed
+ to be empty, it is removed from the array. If it did belong to a
+ sync point before, the sync point becomes inactive. If the action
+ became non-empty and it did not belong to a sync point before (it
+ was added at the end of the action array), the action array needs
+ to be sorted by sync point.
+
+ If the sync point name is "now", it is executed immediately.
+*/
+
+static bool debug_sync_set_action(THD *thd, st_debug_sync_action *action)
+{
+ st_debug_sync_control *ds_control= thd->debug_sync_control;
+ bool is_dsp_now= FALSE;
+ DBUG_ENTER("debug_sync_set_action");
+ DBUG_ASSERT(thd);
+ DBUG_ASSERT(action);
+ DBUG_ASSERT(ds_control);
+
+ action->activation_count= max(action->hit_limit, action->execute);
+ if (!action->activation_count)
+ {
+ debug_sync_remove_action(ds_control, action);
+ DBUG_PRINT("debug_sync", ("action cleared"));
+ }
+ else
+ {
+ const char *dsp_name= action->sync_point.c_ptr();
+ DBUG_EXECUTE("debug_sync", {
+ /* Functions as DBUG_PRINT args can change keyword and line nr. */
+ const char *sig_emit= action->signal.c_ptr();
+ const char *sig_wait= action->wait_for.c_ptr();
+ DBUG_PRINT("debug_sync",
+ ("sync_point: '%s' activation_count: %lu hit_limit: %lu "
+ "execute: %lu timeout: %lu signal: '%s' wait_for: '%s'",
+ dsp_name, action->activation_count,
+ action->hit_limit, action->execute, action->timeout,
+ sig_emit, sig_wait));});
+
+ /* Check this before sorting the array. action may move. */
+ is_dsp_now= !my_strcasecmp(system_charset_info, dsp_name, "now");
+
+ if (action->need_sort)
+ {
+ action->need_sort= FALSE;
+ /* Sort actions by (name_len, name). */
+ my_qsort(ds_control->ds_action, ds_control->ds_active,
+ sizeof(st_debug_sync_action), debug_sync_qsort_cmp);
+ }
+ }
+ DBUG_EXECUTE("debug_sync_list", debug_sync_print_actions(thd););
+
+ /* Execute the special sync point 'now' if activated above. */
+ if (is_dsp_now)
+ {
+ DEBUG_SYNC(thd, "now");
+ /*
+ If HIT_LIMIT for sync point "now" was 1, the execution of the sync
+ point decremented it to 0. In this case the following happened:
+
+ - an error message was reported with my_error() and
+ - the statement was killed with thd->killed= THD::KILL_QUERY.
+
+ If a statement reports an error, it must not call send_ok().
+ The calling functions will not call send_ok(), if we return TRUE
+ from this function.
+
+ thd->killed is also set if the wait is interrupted from a
+ KILL or KILL QUERY statement. In this case, no error is reported
+ and shall not be reported as a result of SET DEBUG_SYNC.
+ Hence, we check for the first condition above.
+ */
+ if (thd->is_error())
+ DBUG_RETURN(TRUE);
+ }
+
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
+ Extract a token from a string.
+
+ @param[out] token_p returns start of token
+ @param[out] token_length_p returns length of token
+ @param[in,out] ptr current string pointer, adds '\0' terminators
+
+ @return string pointer or NULL
+ @retval != NULL ptr behind token terminator or at string end
+ @retval NULL no token found in remainder of string
+
+ @note
+ This function assumes that the string is in system_charset_info,
+ that this charset is single byte for ASCII NUL ('\0'), that no
+ character except of ASCII NUL ('\0') contains a byte with value 0,
+ and that ASCII NUL ('\0') is used as the string terminator.
+
+ This function needs to return tokens that are terminated with ASCII
+ NUL ('\0'). The tokens are used in my_strcasecmp(). Unfortunately
+ there is no my_strncasecmp().
+
+ To return the last token without copying it, we require the input
+ string to be nul terminated.
+
+ @description
+ This function skips space characters at string begin.
+
+ It returns a pointer to the first non-space character in *token_p.
+
+ If no non-space character is found before the string terminator
+ ASCII NUL ('\0'), the function returns NULL. *token_p and
+ *token_length_p remain unchanged in this case (they are not set).
+
+ The function takes a space character or an ASCII NUL ('\0') as a
+ terminator of the token. The space character could be multi-byte.
+
+ It returns the length of the token in bytes, excluding the
+ terminator, in *token_length_p.
+
+ If the terminator of the token is ASCII NUL ('\0'), it returns a
+ pointer to the terminator (string end).
+
+ If the terminator is a space character, it replaces the the first
+ byte of the terminator character by ASCII NUL ('\0'), skips the (now
+ corrupted) terminator character, and skips all following space
+ characters. It returns a pointer to the next non-space character or
+ to the string terminator ASCII NUL ('\0').
+*/
+
+static char *debug_sync_token(char **token_p, uint *token_length_p, char *ptr)
+{
+ DBUG_ASSERT(token_p);
+ DBUG_ASSERT(token_length_p);
+ DBUG_ASSERT(ptr);
+
+ /* Skip leading space */
+ while (my_isspace(system_charset_info, *ptr))
+ ptr+= my_mbcharlen(system_charset_info, (uchar) *ptr);
+
+ if (!*ptr)
+ {
+ ptr= NULL;
+ goto end;
+ }
+
+ /* Get token start. */
+ *token_p= ptr;
+
+ /* Find token end. */
+ while (*ptr && !my_isspace(system_charset_info, *ptr))
+ ptr+= my_mbcharlen(system_charset_info, (uchar) *ptr);
+
+ /* Get token length. */
+ *token_length_p= ptr - *token_p;
+
+ /* If necessary, terminate token. */
+ if (*ptr)
+ {
+ /* Get terminator character length. */
+ uint mbspacelen= my_mbcharlen(system_charset_info, (uchar) *ptr);
+
+ /* Terminate token. */
+ *ptr= '\0';
+
+ /* Skip the terminator. */
+ ptr+= mbspacelen;
+
+ /* Skip trailing space */
+ while (my_isspace(system_charset_info, *ptr))
+ ptr+= my_mbcharlen(system_charset_info, (uchar) *ptr);
+ }
+
+ end:
+ return ptr;
+}
+
+
+/**
+ Extract a number from a string.
+
+ @param[out] number_p returns number
+ @param[in] actstrptr current pointer in action string
+
+ @return string pointer or NULL
+ @retval != NULL ptr behind token terminator or at string end
+ @retval NULL no token found or token is not valid number
+
+ @note
+ The same assumptions about charset apply as for debug_sync_token().
+
+ @description
+ This function fetches a token from the string and converts it
+ into a number.
+
+ If there is no token left in the string, or the token is not a valid
+ decimal number, NULL is returned. The result in *number_p is
+ undefined in this case.
+*/
+
+static char *debug_sync_number(ulong *number_p, char *actstrptr)
+{
+ char *ptr;
+ char *ept;
+ char *token;
+ uint token_length;
+ DBUG_ASSERT(number_p);
+ DBUG_ASSERT(actstrptr);
+
+ /* Get token from string. */
+ if (!(ptr= debug_sync_token(&token, &token_length, actstrptr)))
+ goto end;
+
+ *number_p= strtoul(token, &ept, 10);
+ if (*ept)
+ ptr= NULL;
+
+ end:
+ return ptr;
+}
+
+
+/**
+ Evaluate a debug sync action string.
+
+ @param[in] thd thread handle
+ @param[in,out] action_str action string to receive '\0' terminators
+
+ @return status
+ @retval FALSE ok
+ @retval TRUE error
+
+ @description
+ This is called when the DEBUG_SYNC system variable is set.
+ Parse action string, build a debug sync action, activate it.
+
+ Before parsing, we "get" an action object. This is placed at the
+ end of the thread's action array unless the requested sync point
+ has an action already.
+
+ Then the parser fills the action object from the request string.
+
+ Finally the action is "set" for the sync point. This means that the
+ sync point becomes active or inactive, depending on the action
+ values.
+
+ @note
+ The input string needs to be ASCII NUL ('\0') terminated. We split
+ nul-terminated tokens in it without copy.
+
+ @see the function comment of debug_sync_token() for more constraints
+ for the string.
+*/
+
+static bool debug_sync_eval_action(THD *thd, char *action_str)
+{
+ st_debug_sync_action *action= NULL;
+ const char *errmsg;
+ char *ptr;
+ char *token;
+ uint token_length= 0;
+ DBUG_ENTER("debug_sync_eval_action");
+ DBUG_ASSERT(thd);
+ DBUG_ASSERT(action_str);
+
+ /*
+ Get debug sync point name. Or a special command.
+ */
+ if (!(ptr= debug_sync_token(&token, &token_length, action_str)))
+ {
+ errmsg= "Missing synchronization point name";
+ goto err;
+ }
+
+ /*
+ If there is a second token, the first one is the sync point name.
+ */
+ if (*ptr)
+ {
+ /* Get an action object to collect the requested action parameters. */
+ action= debug_sync_get_action(thd, token, token_length);
+ if (!action)
+ {
+ /* Error message is sent. */
+ DBUG_RETURN(TRUE); /* purecov: tested */
+ }
+ }
+
+ /*
+ Get kind of action to be taken at sync point.
+ */
+ if (!(ptr= debug_sync_token(&token, &token_length, ptr)))
+ {
+ /* No action present. Try special commands. Token unchanged. */
+
+ /*
+ Try RESET.
+ */
+ if (!my_strcasecmp(system_charset_info, token, "RESET"))
+ {
+ /* It is RESET. Reset all actions and global signal. */
+ debug_sync_reset(thd);
+ goto end;
+ }
+
+ /* Token unchanged. It still contains sync point name. */
+ errmsg= "Missing action after synchronization point name '%.*s'";
+ goto err;
+ }
+
+ /*
+ Check for pseudo actions first. Start with actions that work on
+ an existing action.
+ */
+ DBUG_ASSERT(action);
+
+ /*
+ Try TEST.
+ */
+ if (!my_strcasecmp(system_charset_info, token, "TEST"))
+ {
+ /* It is TEST. Nothing must follow it. */
+ if (*ptr)
+ {
+ errmsg= "Nothing must follow action TEST";
+ goto err;
+ }
+
+ /* Execute sync point. */
+ debug_sync(thd, action->sync_point.ptr(), action->sync_point.length());
+ /* Fix statistics. This was not a real hit of the sync point. */
+ thd->debug_sync_control->dsp_hits--;
+ goto end;
+ }
+
+ /*
+ Now check for actions that define a new action.
+ Initialize action. Do not use bzero(). Strings may have malloced.
+ */
+ action->activation_count= 0;
+ action->hit_limit= 0;
+ action->execute= 0;
+ action->timeout= 0;
+ action->signal.length(0);
+ action->wait_for.length(0);
+
+ /*
+ Try CLEAR.
+ */
+ if (!my_strcasecmp(system_charset_info, token, "CLEAR"))
+ {
+ /* It is CLEAR. Nothing must follow it. */
+ if (*ptr)
+ {
+ errmsg= "Nothing must follow action CLEAR";
+ goto err;
+ }
+
+ /* Set (clear/remove) action. */
+ goto set_action;
+ }
+
+ /*
+ Now check for real sync point actions.
+ */
+
+ /*
+ Try SIGNAL.
+ */
+ if (!my_strcasecmp(system_charset_info, token, "SIGNAL"))
+ {
+ /* It is SIGNAL. Signal name must follow. */
+ if (!(ptr= debug_sync_token(&token, &token_length, ptr)))
+ {
+ errmsg= "Missing signal name after action SIGNAL";
+ goto err;
+ }
+ if (action->signal.copy(token, token_length, system_charset_info))
+ {
+ /* Error is reported by my_malloc(). */
+ /* purecov: begin tested */
+ errmsg= NULL;
+ goto err;
+ /* purecov: end */
+ }
+
+ /* Set default for EXECUTE option. */
+ action->execute= 1;
+
+ /* Get next token. If none follows, set action. */
+ if (!(ptr= debug_sync_token(&token, &token_length, ptr)))
+ goto set_action;
+ }
+
+ /*
+ Try WAIT_FOR.
+ */
+ if (!my_strcasecmp(system_charset_info, token, "WAIT_FOR"))
+ {
+ /* It is WAIT_FOR. Wait_for signal name must follow. */
+ if (!(ptr= debug_sync_token(&token, &token_length, ptr)))
+ {
+ errmsg= "Missing signal name after action WAIT_FOR";
+ goto err;
+ }
+ if (action->wait_for.copy(token, token_length, system_charset_info))
+ {
+ /* Error is reported by my_malloc(). */
+ /* purecov: begin tested */
+ errmsg= NULL;
+ goto err;
+ /* purecov: end */
+ }
+
+ /* Set default for EXECUTE and TIMEOUT options. */
+ action->execute= 1;
+ action->timeout= opt_debug_sync_timeout;
+
+ /* Get next token. If none follows, set action. */
+ if (!(ptr= debug_sync_token(&token, &token_length, ptr)))
+ goto set_action;
+
+ /*
+ Try TIMEOUT.
+ */
+ if (!my_strcasecmp(system_charset_info, token, "TIMEOUT"))
+ {
+ /* It is TIMEOUT. Number must follow. */
+ if (!(ptr= debug_sync_number(&action->timeout, ptr)))
+ {
+ errmsg= "Missing valid number after TIMEOUT";
+ goto err;
+ }
+
+ /* Get next token. If none follows, set action. */
+ if (!(ptr= debug_sync_token(&token, &token_length, ptr)))
+ goto set_action;
+ }
+ }
+
+ /*
+ Try EXECUTE.
+ */
+ if (!my_strcasecmp(system_charset_info, token, "EXECUTE"))
+ {
+ /*
+ EXECUTE requires either SIGNAL and/or WAIT_FOR to be present.
+ In this case action->execute has been preset to 1.
+ */
+ if (!action->execute)
+ {
+ errmsg= "Missing action before EXECUTE";
+ goto err;
+ }
+
+ /* Number must follow. */
+ if (!(ptr= debug_sync_number(&action->execute, ptr)))
+ {
+ errmsg= "Missing valid number after EXECUTE";
+ goto err;
+ }
+
+ /* Get next token. If none follows, set action. */
+ if (!(ptr= debug_sync_token(&token, &token_length, ptr)))
+ goto set_action;
+ }
+
+ /*
+ Try HIT_LIMIT.
+ */
+ if (!my_strcasecmp(system_charset_info, token, "HIT_LIMIT"))
+ {
+ /* Number must follow. */
+ if (!(ptr= debug_sync_number(&action->hit_limit, ptr)))
+ {
+ errmsg= "Missing valid number after HIT_LIMIT";
+ goto err;
+ }
+
+ /* Get next token. If none follows, set action. */
+ if (!(ptr= debug_sync_token(&token, &token_length, ptr)))
+ goto set_action;
+ }
+
+ errmsg= "Illegal or out of order stuff: '%.*s'";
+
+ err:
+ if (errmsg)
+ {
+ /*
+ NOTE: errmsg must either have %.*s or none % at all.
+ It can be NULL if an error message is already reported
+ (e.g. by my_malloc()).
+ */
+ set_if_smaller(token_length, 64); /* Limit error message length. */
+ my_printf_error(ER_PARSE_ERROR, errmsg, MYF(0), token_length, token);
+ }
+ if (action)
+ debug_sync_remove_action(thd->debug_sync_control, action);
+ DBUG_RETURN(TRUE);
+
+ set_action:
+ DBUG_RETURN(debug_sync_set_action(thd, action));
+
+ end:
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
+ Check if the system variable 'debug_sync' can be set.
+
+ @param[in] thd thread handle
+ @param[in] var set variable request
+
+ @return status
+ @retval FALSE ok, variable can be set
+ @retval TRUE error, variable cannot be set
+*/
+
+bool sys_var_debug_sync::check(THD *thd, set_var *var)
+{
+ DBUG_ENTER("sys_var_debug_sync::check");
+ DBUG_ASSERT(thd);
+ DBUG_ASSERT(var);
+
+ /*
+ Variable can be set for the session only.
+
+ This could be changed later. Then we need to have a global array of
+ actions in addition to the thread local ones. SET GLOBAL would
+ manage the global array, SET [SESSION] the local array. A sync point
+ would need to look for a local and a global action. Setting and
+ executing of global actions need to be protected by a mutex.
+
+ The purpose of global actions could be to allow synchronizing with
+ connectionless threads that cannot execute SET statements.
+ */
+ if (var->type == OPT_GLOBAL)
+ {
+ my_error(ER_LOCAL_VARIABLE, MYF(0), name);
+ DBUG_RETURN(TRUE);
+ }
+
+ /*
+ Do not check for disabled facility. Test result should not
+ unnecessarily differ from enabled facility.
+ */
+
+ /*
+ Facility requires SUPER privilege. Sync points could be inside
+ global mutexes (e.g. LOCK_open). Waiting there forever would
+ stall the whole server.
+ */
+ DBUG_RETURN(check_global_access(thd, SUPER_ACL));
+}
+
+
+/**
+ Set the system variable 'debug_sync'.
+
+ @param[in] thd thread handle
+ @param[in] var set variable request
+
+ @return status
+ @retval FALSE ok, variable is set
+ @retval TRUE error, variable could not be set
+
+ @note
+ "Setting" of the system variable 'debug_sync' does not mean to
+ assign a value to it as usual. Instead a debug sync action is parsed
+ from the input string and stored apart from the variable value.
+
+ @note
+ For efficiency reasons, the action string parser places '\0'
+ terminators in the string. So we need to take a copy here.
+*/
+
+bool sys_var_debug_sync::update(THD *thd, set_var *var)
+{
+ char *val_str;
+ String *val_ptr;
+ String val_buf;
+ DBUG_ENTER("sys_var_debug_sync::update");
+ DBUG_ASSERT(thd);
+
+ /*
+ Depending on the value type (string literal, user variable, ...)
+ val_buf receives a copy of the value or not. But we always need
+ a copy. So we take a copy, if it is not done by val_str().
+ If val_str() puts a copy into val_buf, then it returns &val_buf,
+ otherwise it returns a pointer to the string object that we need
+ to copy.
+ */
+ val_ptr= var ? var->value->val_str(&val_buf) : &val_buf;
+ if (val_ptr != &val_buf)
+ {
+ val_buf.copy(*val_ptr);
+ }
+ val_str= val_buf.c_ptr();
+ DBUG_PRINT("debug_sync", ("set action: '%s'", val_str));
+
+ /*
+ debug_sync_eval_action() places '\0' in the string, which itself
+ must be '\0' terminated.
+ */
+ DBUG_RETURN(opt_debug_sync_timeout ?
+ debug_sync_eval_action(thd, val_str) :
+ FALSE);
+}
+
+
+/**
+ Retrieve the value of the system variable 'debug_sync'.
+
+ @param[in] thd thread handle
+ @param[in] type variable type, unused
+ @param[in] base variable base, unused
+
+ @return string
+ @retval != NULL ok, string pointer
+ @retval NULL memory allocation error
+
+ @note
+ The value of the system variable 'debug_sync' reflects if
+ the facility is enabled ("ON") or disabled (default, "OFF").
+
+ When "ON", the current signal is added.
+*/
+
+uchar *sys_var_debug_sync::value_ptr(THD *thd,
+ enum_var_type type __attribute__((unused)),
+ LEX_STRING *base __attribute__((unused)))
+{
+ char *value;
+ DBUG_ENTER("sys_var_debug_sync::value_ptr");
+ DBUG_ASSERT(thd);
+
+ if (opt_debug_sync_timeout)
+ {
+ static char on[]= "ON - current signal: '";
+
+ // Ensure exclusive access to debug_sync_global.ds_signal
+ pthread_mutex_lock(&debug_sync_global.ds_mutex);
+
+ size_t lgt= (sizeof(on) /* includes '\0' */ +
+ debug_sync_global.ds_signal.length() + 1 /* for '\'' */);
+ char *vend;
+ char *vptr;
+
+ if ((value= (char*) alloc_root(thd->mem_root, lgt)))
+ {
+ vend= value + lgt - 1; /* reserve space for '\0'. */
+ vptr= debug_sync_bmove_len(value, vend, STRING_WITH_LEN(on));
+ vptr= debug_sync_bmove_len(vptr, vend, debug_sync_global.ds_signal.ptr(),
+ debug_sync_global.ds_signal.length());
+ if (vptr < vend)
+ *(vptr++)= '\'';
+ *vptr= '\0'; /* We have one byte reserved for the worst case. */
+ }
+ pthread_mutex_unlock(&debug_sync_global.ds_mutex);
+ }
+ else
+ {
+ /* purecov: begin tested */
+ value= strmake_root(thd->mem_root, STRING_WITH_LEN("OFF"));
+ /* purecov: end */
+ }
+
+ DBUG_RETURN((uchar*) value);
+}
+
+
+/**
+ Execute requested action at a synchronization point.
+
+ @param[in] thd thread handle
+ @param[in] action action to be executed
+
+ @note
+ This is to be called only if activation count > 0.
+*/
+
+static void debug_sync_execute(THD *thd, st_debug_sync_action *action)
+{
+ IF_DBUG(const char *dsp_name= action->sync_point.c_ptr());
+ IF_DBUG(const char *sig_emit= action->signal.c_ptr());
+ IF_DBUG(const char *sig_wait= action->wait_for.c_ptr());
+ DBUG_ENTER("debug_sync_execute");
+ DBUG_ASSERT(thd);
+ DBUG_ASSERT(action);
+ DBUG_PRINT("debug_sync",
+ ("sync_point: '%s' activation_count: %lu hit_limit: %lu "
+ "execute: %lu timeout: %lu signal: '%s' wait_for: '%s'",
+ dsp_name, action->activation_count, action->hit_limit,
+ action->execute, action->timeout, sig_emit, sig_wait));
+
+ DBUG_ASSERT(action->activation_count);
+ action->activation_count--;
+
+ if (action->execute)
+ {
+ const char *old_proc_info;
+
+ action->execute--;
+
+ /*
+ If we will be going to wait, set proc_info for the PROCESSLIST table.
+ Do this before emitting the signal, so other threads can see it
+ if they awake before we enter_cond() below.
+ */
+ if (action->wait_for.length())
+ {
+ st_debug_sync_control *ds_control= thd->debug_sync_control;
+ strxnmov(ds_control->ds_proc_info, sizeof(ds_control->ds_proc_info)-1,
+ "debug sync point: ", action->sync_point.c_ptr(), NullS);
+ old_proc_info= thd->proc_info;
+ thd_proc_info(thd, ds_control->ds_proc_info);
+ }
+
+ /*
+ Take mutex to ensure that only one thread access
+ debug_sync_global.ds_signal at a time. Need to take mutex for
+ read access too, to create a memory barrier in order to avoid that
+ threads just reads an old cached version of the signal.
+ */
+ pthread_mutex_lock(&debug_sync_global.ds_mutex);
+
+ if (action->signal.length())
+ {
+ /* Copy the signal to the global variable. */
+ if (debug_sync_global.ds_signal.copy(action->signal))
+ {
+ /*
+ Error is reported by my_malloc().
+ We must disable the facility. We have no way to return an error.
+ */
+ debug_sync_emergency_disable(); /* purecov: tested */
+ }
+ /* Wake threads waiting in a sync point. */
+ pthread_cond_broadcast(&debug_sync_global.ds_cond);
+ DBUG_PRINT("debug_sync_exec", ("signal '%s' at: '%s'",
+ sig_emit, dsp_name));
+ } /* end if (action->signal.length()) */
+
+ if (action->wait_for.length())
+ {
+ pthread_mutex_t *old_mutex;
+ pthread_cond_t *old_cond;
+ int error= 0;
+ struct timespec abstime;
+
+ /*
+ We don't use enter_cond()/exit_cond(). They do not save old
+ mutex and cond. This would prohibit the use of DEBUG_SYNC
+ between other places of enter_cond() and exit_cond().
+ */
+ old_mutex= thd->mysys_var->current_mutex;
+ old_cond= thd->mysys_var->current_cond;
+ thd->mysys_var->current_mutex= &debug_sync_global.ds_mutex;
+ thd->mysys_var->current_cond= &debug_sync_global.ds_cond;
+
+ set_timespec(abstime, action->timeout);
+ DBUG_EXECUTE("debug_sync_exec", {
+ /* Functions as DBUG_PRINT args can change keyword and line nr. */
+ const char *sig_glob= debug_sync_global.ds_signal.c_ptr();
+ DBUG_PRINT("debug_sync_exec",
+ ("wait for '%s' at: '%s' curr: '%s'",
+ sig_wait, dsp_name, sig_glob));});
+
+ /*
+ Wait until global signal string matches the wait_for string.
+ Interrupt when thread or query is killed or facility disabled.
+ The facility can become disabled when some thread cannot get
+ the required dynamic memory allocated.
+ */
+ while (stringcmp(&debug_sync_global.ds_signal, &action->wait_for) &&
+ !thd->killed && opt_debug_sync_timeout)
+ {
+ error= pthread_cond_timedwait(&debug_sync_global.ds_cond,
+ &debug_sync_global.ds_mutex,
+ &abstime);
+ DBUG_EXECUTE("debug_sync", {
+ /* Functions as DBUG_PRINT args can change keyword and line nr. */
+ const char *sig_glob= debug_sync_global.ds_signal.c_ptr();
+ DBUG_PRINT("debug_sync",
+ ("awoke from %s global: %s error: %d",
+ sig_wait, sig_glob, error));});
+ if (error == ETIMEDOUT || error == ETIME)
+ {
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_DEBUG_SYNC_TIMEOUT, ER(ER_DEBUG_SYNC_TIMEOUT));
+ break;
+ }
+ error= 0;
+ }
+ DBUG_EXECUTE("debug_sync_exec",
+ if (thd->killed)
+ DBUG_PRINT("debug_sync_exec",
+ ("killed %d from '%s' at: '%s'",
+ thd->killed, sig_wait, dsp_name));
+ else
+ DBUG_PRINT("debug_sync_exec",
+ ("%s from '%s' at: '%s'",
+ error ? "timeout" : "resume",
+ sig_wait, dsp_name)););
+
+ /*
+ We don't use enter_cond()/exit_cond(). They do not save old
+ mutex and cond. This would prohibit the use of DEBUG_SYNC
+ between other places of enter_cond() and exit_cond(). The
+ protected mutex must always unlocked _before_ mysys_var->mutex
+ is locked. (See comment in THD::exit_cond().)
+ */
+ pthread_mutex_unlock(&debug_sync_global.ds_mutex);
+ pthread_mutex_lock(&thd->mysys_var->mutex);
+ thd->mysys_var->current_mutex= old_mutex;
+ thd->mysys_var->current_cond= old_cond;
+ thd_proc_info(thd, old_proc_info);
+ pthread_mutex_unlock(&thd->mysys_var->mutex);
+
+ }
+ else
+ {
+ /* In case we don't wait, we just release the mutex. */
+ pthread_mutex_unlock(&debug_sync_global.ds_mutex);
+ } /* end if (action->wait_for.length()) */
+
+ } /* end if (action->execute) */
+
+ /* hit_limit is zero for infinite. Don't decrement unconditionally. */
+ if (action->hit_limit)
+ {
+ if (!--action->hit_limit)
+ {
+ thd->killed= THD::KILL_QUERY;
+ my_error(ER_DEBUG_SYNC_HIT_LIMIT, MYF(0));
+ }
+ DBUG_PRINT("debug_sync_exec", ("hit_limit: %lu at: '%s'",
+ action->hit_limit, dsp_name));
+ }
+
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Execute requested action at a synchronization point.
+
+ @param[in] thd thread handle
+ @param[in] sync_point_name name of synchronization point
+ @param[in] name_len length of sync point name
+*/
+
+void debug_sync(THD *thd, const char *sync_point_name, size_t name_len)
+{
+ st_debug_sync_control *ds_control= thd->debug_sync_control;
+ st_debug_sync_action *action;
+ DBUG_ENTER("debug_sync");
+ DBUG_ASSERT(thd);
+ DBUG_ASSERT(sync_point_name);
+ DBUG_ASSERT(name_len);
+ DBUG_ASSERT(ds_control);
+ DBUG_PRINT("debug_sync_point", ("hit: '%s'", sync_point_name));
+
+ /* Statistics. */
+ ds_control->dsp_hits++;
+
+ if (ds_control->ds_active &&
+ (action= debug_sync_find(ds_control->ds_action, ds_control->ds_active,
+ sync_point_name, name_len)) &&
+ action->activation_count)
+ {
+ /* Sync point is active (action exists). */
+ debug_sync_execute(thd, action);
+
+ /* Statistics. */
+ ds_control->dsp_executed++;
+
+ /* If action became inactive, remove it to shrink the search array. */
+ if (!action->activation_count)
+ debug_sync_remove_action(ds_control, action);
+ }
+
+ DBUG_VOID_RETURN;
+}
+
+#endif /* defined(ENABLED_DEBUG_SYNC) */
diff --git a/sql/debug_sync.h b/sql/debug_sync.h
new file mode 100644
index 00000000000..f4cd0b364cf
--- /dev/null
+++ b/sql/debug_sync.h
@@ -0,0 +1,60 @@
+#ifndef DEBUG_SYNC_INCLUDED
+#define DEBUG_SYNC_INCLUDED
+
+/* Copyright (C) 2008 Sun Microsystems, Inc.
+
+ 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 */
+
+/**
+ @file
+
+ Declarations for the Debug Sync Facility. See debug_sync.cc for details.
+*/
+
+#ifdef USE_PRAGMA_INTERFACE
+#pragma interface /* gcc class implementation */
+#endif
+
+#include <my_global.h>
+
+class THD;
+
+#if defined(ENABLED_DEBUG_SYNC)
+
+/* Macro to be put in the code at synchronization points. */
+#define DEBUG_SYNC(_thd_, _sync_point_name_) \
+ do { if (unlikely(opt_debug_sync_timeout)) \
+ debug_sync(_thd_, STRING_WITH_LEN(_sync_point_name_)); \
+ } while (0)
+
+/* Command line option --debug-sync-timeout. See mysqld.cc. */
+extern uint opt_debug_sync_timeout;
+
+/* Default WAIT_FOR timeout if command line option is given without argument. */
+#define DEBUG_SYNC_DEFAULT_WAIT_TIMEOUT 300
+
+/* Debug Sync prototypes. See debug_sync.cc. */
+extern int debug_sync_init(void);
+extern void debug_sync_end(void);
+extern void debug_sync_init_thread(THD *thd);
+extern void debug_sync_end_thread(THD *thd);
+extern void debug_sync(THD *thd, const char *sync_point_name, size_t name_len);
+
+#else /* defined(ENABLED_DEBUG_SYNC) */
+
+#define DEBUG_SYNC(_thd_, _sync_point_name_) /* disabled DEBUG_SYNC */
+
+#endif /* defined(ENABLED_DEBUG_SYNC) */
+
+#endif /* DEBUG_SYNC_INCLUDED */
diff --git a/sql/event_db_repository.cc b/sql/event_db_repository.cc
index 80aa93a0ca4..0d883e030f5 100644
--- a/sql/event_db_repository.cc
+++ b/sql/event_db_repository.cc
@@ -711,7 +711,7 @@ Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data,
DBUG_ENTER("Event_db_repository::update_event");
/* None or both must be set */
- DBUG_ASSERT(new_dbname && new_name || new_dbname == new_name);
+ DBUG_ASSERT((new_dbname && new_name) || new_dbname == new_name);
/* Reset sql_mode during data dictionary operations. */
thd->variables.sql_mode= 0;
diff --git a/sql/field.cc b/sql/field.cc
index 9f46552d5bd..0df9b0fc2e4 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -8833,38 +8833,81 @@ bool Field::eq_def(Field *field)
/**
+ Compare the first t1::count type names.
+
+ @return TRUE if the type names of t1 match those of t2. FALSE otherwise.
+*/
+
+static bool compare_type_names(CHARSET_INFO *charset, TYPELIB *t1, TYPELIB *t2)
+{
+ for (uint i= 0; i < t1->count; i++)
+ if (my_strnncoll(charset,
+ (const uchar*) t1->type_names[i],
+ t1->type_lengths[i],
+ (const uchar*) t2->type_names[i],
+ t2->type_lengths[i]))
+ return FALSE;
+ return TRUE;
+}
+
+/**
@return
returns 1 if the fields are equally defined
*/
bool Field_enum::eq_def(Field *field)
{
+ TYPELIB *values;
+
if (!Field::eq_def(field))
- return 0;
- return compare_enum_values(((Field_enum*) field)->typelib);
-}
+ return FALSE;
+ values= ((Field_enum*) field)->typelib;
-bool Field_enum::compare_enum_values(TYPELIB *values)
-{
+ /* Definition must be strictly equal. */
if (typelib->count != values->count)
return FALSE;
- for (uint i= 0; i < typelib->count; i++)
- if (my_strnncoll(field_charset,
- (const uchar*) typelib->type_names[i],
- typelib->type_lengths[i],
- (const uchar*) values->type_names[i],
- values->type_lengths[i]))
- return FALSE;
- return TRUE;
+
+ return compare_type_names(field_charset, typelib, values);
}
+/**
+ Check whether two fields can be considered 'equal' for table
+ alteration purposes. Fields are equal if they retain the same
+ pack length and if new members are added to the end of the list.
+
+ @return IS_EQUAL_YES if fields are compatible.
+ IS_EQUAL_NO otherwise.
+*/
+
uint Field_enum::is_equal(Create_field *new_field)
{
- if (!Field_str::is_equal(new_field))
- return 0;
- return compare_enum_values(new_field->interval);
+ TYPELIB *values= new_field->interval;
+
+ /*
+ The fields are compatible if they have the same flags,
+ type, charset and have the same underlying length.
+ */
+ if (compare_str_field_flags(new_field, flags) ||
+ new_field->sql_type != real_type() ||
+ new_field->charset != field_charset ||
+ new_field->pack_length != pack_length())
+ return IS_EQUAL_NO;
+
+ /*
+ Changing the definition of an ENUM or SET column by adding a new
+ enumeration or set members to the end of the list of valid member
+ values only alters table metadata and not table data.
+ */
+ if (typelib->count > values->count)
+ return IS_EQUAL_NO;
+
+ /* Check whether there are modification before the end. */
+ if (! compare_type_names(field_charset, typelib, new_field->interval))
+ return IS_EQUAL_NO;
+
+ return IS_EQUAL_YES;
}
diff --git a/sql/field.h b/sql/field.h
index 36569428ee0..51397cad7b1 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -475,6 +475,13 @@ public:
/* maximum possible display length */
virtual uint32 max_display_length()= 0;
+ /**
+ Whether a field being created is compatible with a existing one.
+
+ Used by the ALTER TABLE code to evaluate whether the new definition
+ of a table is compatible with the old definition so that it can
+ determine if data needs to be copied over (table data change).
+ */
virtual uint is_equal(Create_field *new_field);
/* convert decimal to longlong with overflow check */
longlong convert_decimal2longlong(const my_decimal *val, bool unsigned_flag,
@@ -1865,7 +1872,6 @@ public:
CHARSET_INFO *sort_charset(void) const { return &my_charset_bin; }
private:
int do_save_field_metadata(uchar *first_byte);
- bool compare_enum_values(TYPELIB *values);
uint is_equal(Create_field *new_field);
};
diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc
index ddb90427fdc..dfd7ddc4d6c 100644
--- a/sql/ha_ndbcluster.cc
+++ b/sql/ha_ndbcluster.cc
@@ -9426,9 +9426,11 @@ ndb_util_thread_fail:
pthread_cond_signal(&COND_ndb_util_ready);
pthread_mutex_unlock(&LOCK_ndb_util_thread);
DBUG_PRINT("exit", ("ndb_util_thread"));
+
+ DBUG_LEAVE; // Must match DBUG_ENTER()
my_thread_end();
pthread_exit(0);
- DBUG_RETURN(NULL);
+ return NULL; // Avoid compiler warnings
}
/*
diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc
index cc4f5a9c2da..e37a520c659 100644
--- a/sql/ha_ndbcluster_binlog.cc
+++ b/sql/ha_ndbcluster_binlog.cc
@@ -3665,9 +3665,11 @@ pthread_handler_t ndb_binlog_thread_func(void *arg)
ndb_binlog_thread_running= -1;
pthread_mutex_unlock(&injector_mutex);
pthread_cond_signal(&injector_cond);
+
+ DBUG_LEAVE; // Must match DBUG_ENTER()
my_thread_end();
pthread_exit(0);
- DBUG_RETURN(NULL);
+ return NULL; // Avoid compiler warnings
}
lex_start(thd);
@@ -4378,10 +4380,11 @@ err:
(void) pthread_cond_signal(&injector_cond);
DBUG_PRINT("exit", ("ndb_binlog_thread"));
- my_thread_end();
+ DBUG_LEAVE; // Must match DBUG_ENTER()
+ my_thread_end();
pthread_exit(0);
- DBUG_RETURN(NULL);
+ return NULL; // Avoid compiler warnings
}
bool
diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc
index 4b6ce28cb97..11ec8e8d176 100644
--- a/sql/ha_partition.cc
+++ b/sql/ha_partition.cc
@@ -239,6 +239,7 @@ void ha_partition::init_handler_variables()
m_curr_key_info[0]= NULL;
m_curr_key_info[1]= NULL;
is_clone= FALSE,
+ m_part_func_monotonicity_info= NON_MONOTONIC;
auto_increment_lock= FALSE;
auto_increment_safe_stmt_log_lock= FALSE;
/*
@@ -705,6 +706,7 @@ int ha_partition::rename_partitions(const char *path)
if (m_is_sub_partitioned)
{
List_iterator<partition_element> sub_it(part_elem->subpartitions);
+ j= 0;
do
{
sub_elem= sub_it++;
@@ -999,7 +1001,7 @@ static bool print_admin_msg(THD* thd, const char* msg_type,
if (!thd->vio_ok())
{
- sql_print_error(msgbuf);
+ sql_print_error("%s", msgbuf);
return TRUE;
}
@@ -1278,10 +1280,10 @@ void ha_partition::cleanup_new_partition(uint part_count)
m_file= m_added_file;
m_added_file= NULL;
+ external_lock(ha_thd(), F_UNLCK);
/* delete_table also needed, a bit more complex */
close();
- m_added_file= m_file;
m_file= save_m_file;
}
DBUG_VOID_RETURN;
@@ -2464,11 +2466,18 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked)
}
}
+ /* Initialize the bitmap we use to minimize ha_start_bulk_insert calls */
+ if (bitmap_init(&m_bulk_insert_started, NULL, m_tot_parts + 1, FALSE))
+ DBUG_RETURN(1);
+ bitmap_clear_all(&m_bulk_insert_started);
/* Initialize the bitmap we use to determine what partitions are used */
if (!is_clone)
{
if (bitmap_init(&(m_part_info->used_partitions), NULL, m_tot_parts, TRUE))
+ {
+ bitmap_free(&m_bulk_insert_started);
DBUG_RETURN(1);
+ }
bitmap_set_all(&(m_part_info->used_partitions));
}
@@ -2552,12 +2561,18 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked)
calling open on all individual handlers.
*/
m_handler_status= handler_opened;
+ if (m_part_info->part_expr)
+ m_part_func_monotonicity_info=
+ m_part_info->part_expr->get_monotonicity_info();
+ else if (m_part_info->list_of_part_fields)
+ m_part_func_monotonicity_info= MONOTONIC_STRICT_INCREASING;
info(HA_STATUS_VARIABLE | HA_STATUS_CONST);
DBUG_RETURN(0);
err_handler:
while (file-- != m_file)
(*file)->close();
+ bitmap_free(&m_bulk_insert_started);
if (!is_clone)
bitmap_free(&(m_part_info->used_partitions));
@@ -2605,6 +2620,7 @@ int ha_partition::close(void)
DBUG_ASSERT(table->s == table_share);
delete_queue(&m_queue);
+ bitmap_free(&m_bulk_insert_started);
if (!is_clone)
bitmap_free(&(m_part_info->used_partitions));
file= m_file;
@@ -3021,10 +3037,12 @@ int ha_partition::write_row(uchar * buf)
}
m_last_part= part_id;
DBUG_PRINT("info", ("Insert in partition %d", part_id));
+ start_part_bulk_insert(thd, part_id);
+
tmp_disable_binlog(thd); /* Do not replicate the low-level changes. */
error= m_file[part_id]->ha_write_row(buf);
if (have_auto_increment && !table->s->next_number_keypart)
- set_auto_increment_if_higher(table->next_number_field->val_int());
+ set_auto_increment_if_higher(table->next_number_field);
reenable_binlog(thd);
exit:
table->timestamp_field_type= orig_timestamp_type;
@@ -3083,6 +3101,7 @@ int ha_partition::update_row(const uchar *old_data, uchar *new_data)
}
m_last_part= new_part_id;
+ start_part_bulk_insert(thd, new_part_id);
if (new_part_id == old_part_id)
{
DBUG_PRINT("info", ("Update in partition %d", new_part_id));
@@ -3128,7 +3147,7 @@ exit:
HA_DATA_PARTITION *ha_data= (HA_DATA_PARTITION*) table_share->ha_data;
if (!ha_data->auto_inc_initialized)
info(HA_STATUS_AUTO);
- set_auto_increment_if_higher(table->found_next_number_field->val_int());
+ set_auto_increment_if_higher(table->found_next_number_field);
}
table->timestamp_field_type= orig_timestamp_type;
DBUG_RETURN(error);
@@ -3247,23 +3266,112 @@ int ha_partition::delete_all_rows()
DESCRIPTION
rows == 0 means we will probably insert many rows
*/
-
void ha_partition::start_bulk_insert(ha_rows rows)
{
- handler **file;
DBUG_ENTER("ha_partition::start_bulk_insert");
- rows= rows ? rows/m_tot_parts + 1 : 0;
- file= m_file;
- do
- {
- (*file)->ha_start_bulk_insert(rows);
- } while (*(++file));
+ m_bulk_inserted_rows= 0;
+ bitmap_clear_all(&m_bulk_insert_started);
+ /* use the last bit for marking if bulk_insert_started was called */
+ bitmap_set_bit(&m_bulk_insert_started, m_tot_parts);
DBUG_VOID_RETURN;
}
/*
+ Check if start_bulk_insert has been called for this partition,
+ if not, call it and mark it called
+*/
+void ha_partition::start_part_bulk_insert(THD *thd, uint part_id)
+{
+ long old_buffer_size;
+ if (!bitmap_is_set(&m_bulk_insert_started, part_id) &&
+ bitmap_is_set(&m_bulk_insert_started, m_tot_parts))
+ {
+ old_buffer_size= thd->variables.read_buff_size;
+ /* Update read_buffer_size for this partition */
+ thd->variables.read_buff_size= estimate_read_buffer_size(old_buffer_size);
+ m_file[part_id]->ha_start_bulk_insert(guess_bulk_insert_rows());
+ bitmap_set_bit(&m_bulk_insert_started, part_id);
+ thd->variables.read_buff_size= old_buffer_size;
+ }
+ m_bulk_inserted_rows++;
+}
+
+/*
+ Estimate the read buffer size for each partition.
+ SYNOPSIS
+ ha_partition::estimate_read_buffer_size()
+ original_size read buffer size originally set for the server
+ RETURN VALUE
+ estimated buffer size.
+ DESCRIPTION
+ If the estimated number of rows to insert is less than 10 (but not 0)
+ the new buffer size is same as original buffer size.
+ In case of first partition of when partition function is monotonic
+ new buffer size is same as the original buffer size.
+ For rest of the partition total buffer of 10*original_size is divided
+ equally if number of partition is more than 10 other wise each partition
+ will be allowed to use original buffer size.
+*/
+long ha_partition::estimate_read_buffer_size(long original_size)
+{
+ /*
+ If number of rows to insert is less than 10, but not 0,
+ return original buffer size.
+ */
+ if (estimation_rows_to_insert && (estimation_rows_to_insert < 10))
+ return (original_size);
+ /*
+ If first insert/partition and monotonic partition function,
+ allow using buffer size originally set.
+ */
+ if (!m_bulk_inserted_rows &&
+ m_part_func_monotonicity_info != NON_MONOTONIC &&
+ m_tot_parts > 1)
+ return original_size;
+ /*
+ Allow total buffer used in all partition to go up to 10*read_buffer_size.
+ 11*read_buffer_size in case of monotonic partition function.
+ */
+
+ if (m_tot_parts < 10)
+ return original_size;
+ return (original_size * 10 / m_tot_parts);
+}
+
+/*
+ Try to predict the number of inserts into this partition.
+
+ If less than 10 rows (including 0 which means Unknown)
+ just give that as a guess
+ If monotonic partitioning function was used
+ guess that 50 % of the inserts goes to the first partition
+ For all other cases, guess on equal distribution between the partitions
+*/
+ha_rows ha_partition::guess_bulk_insert_rows()
+{
+ DBUG_ENTER("guess_bulk_insert_rows");
+
+ if (estimation_rows_to_insert < 10)
+ DBUG_RETURN(estimation_rows_to_insert);
+
+ /* If first insert/partition and monotonic partition function, guess 50%. */
+ if (!m_bulk_inserted_rows &&
+ m_part_func_monotonicity_info != NON_MONOTONIC &&
+ m_tot_parts > 1)
+ DBUG_RETURN(estimation_rows_to_insert / 2);
+
+ /* Else guess on equal distribution (+1 is to avoid returning 0/Unknown) */
+ if (m_bulk_inserted_rows < estimation_rows_to_insert)
+ DBUG_RETURN(((estimation_rows_to_insert - m_bulk_inserted_rows)
+ / m_tot_parts) + 1);
+ /* The estimation was wrong, must say 'Unknown' */
+ DBUG_RETURN(0);
+}
+
+
+/*
Finish a large batch of insert rows
SYNOPSIS
@@ -3272,21 +3380,29 @@ void ha_partition::start_bulk_insert(ha_rows rows)
RETURN VALUE
>0 Error code
0 Success
+
+ Note: end_bulk_insert can be called without start_bulk_insert
+ being called, see bug¤44108.
+
*/
int ha_partition::end_bulk_insert()
{
int error= 0;
- handler **file;
+ uint i;
DBUG_ENTER("ha_partition::end_bulk_insert");
- file= m_file;
- do
+ if (!bitmap_is_set(&m_bulk_insert_started, m_tot_parts))
+ DBUG_RETURN(error);
+
+ for (i= 0; i < m_tot_parts; i++)
{
int tmp;
- if ((tmp= (*file)->ha_end_bulk_insert()))
+ if (bitmap_is_set(&m_bulk_insert_started, i) &&
+ (tmp= m_file[i]->ha_end_bulk_insert()))
error= tmp;
- } while (*(++file));
+ }
+ bitmap_clear_all(&m_bulk_insert_started);
DBUG_RETURN(error);
}
@@ -4895,8 +5011,9 @@ int ha_partition::info(uint flag)
If the handler doesn't support statistics, it should set all of the
above to 0.
- We will allow the first handler to set the rec_per_key and use
- this as an estimate on the total table.
+ We first scans through all partitions to get the one holding most rows.
+ We will then allow the handler with the most rows to set
+ the rec_per_key and use this as an estimate on the total table.
max_data_file_length: Maximum data file length
We ignore it, is only used in
@@ -4908,14 +5025,33 @@ int ha_partition::info(uint flag)
ref_length: We set this to the value calculated
and stored in local object
create_time: Creation time of table
- Set by first handler
- So we calculate these constants by using the variables on the first
- handler.
+ So we calculate these constants by using the variables from the
+ handler with most rows.
*/
- handler *file;
+ handler *file, **file_array;
+ ulonglong max_records= 0;
+ uint32 i= 0;
+ uint32 handler_instance= 0;
+
+ file_array= m_file;
+ do
+ {
+ file= *file_array;
+ /* Get variables if not already done */
+ if (!(flag & HA_STATUS_VARIABLE) ||
+ !bitmap_is_set(&(m_part_info->used_partitions),
+ (file_array - m_file)))
+ file->info(HA_STATUS_VARIABLE);
+ if (file->stats.records > max_records)
+ {
+ max_records= file->stats.records;
+ handler_instance= i;
+ }
+ i++;
+ } while (*(++file_array));
- file= m_file[0];
+ file= m_file[handler_instance];
file->info(HA_STATUS_CONST);
stats.create_time= file->stats.create_time;
ref_length= m_ref_length;
diff --git a/sql/ha_partition.h b/sql/ha_partition.h
index 804db028953..96ccabe250b 100644
--- a/sql/ha_partition.h
+++ b/sql/ha_partition.h
@@ -179,6 +179,11 @@ private:
This to ensure it will work with statement based replication.
*/
bool auto_increment_safe_stmt_log_lock;
+ /** For optimizing ha_start_bulk_insert calls */
+ MY_BITMAP m_bulk_insert_started;
+ ha_rows m_bulk_inserted_rows;
+ /** used for prediction of start_bulk_insert rows */
+ enum_monotonicity_info m_part_func_monotonicity_info;
public:
handler *clone(MEM_ROOT *mem_root);
virtual void set_part_info(partition_info *part_info)
@@ -356,7 +361,6 @@ public:
Bulk inserts are supported if all underlying handlers support it.
start_bulk_insert and end_bulk_insert is called before and after a
number of calls to write_row.
- Not yet though.
*/
virtual int write_row(uchar * buf);
virtual int update_row(const uchar * old_data, uchar * new_data);
@@ -364,6 +368,11 @@ public:
virtual int delete_all_rows(void);
virtual void start_bulk_insert(ha_rows rows);
virtual int end_bulk_insert();
+private:
+ ha_rows guess_bulk_insert_rows();
+ void start_part_bulk_insert(THD *thd, uint part_id);
+ long estimate_read_buffer_size(long original_size);
+public:
virtual bool is_fatal_error(int error, uint flags)
{
@@ -767,10 +776,10 @@ public:
if (m_handler_status < handler_initialized ||
m_handler_status >= handler_closed)
DBUG_RETURN(PARTITION_ENABLED_TABLE_FLAGS);
- else
- DBUG_RETURN((m_file[0]->ha_table_flags() &
- ~(PARTITION_DISABLED_TABLE_FLAGS)) |
- (PARTITION_ENABLED_TABLE_FLAGS));
+
+ DBUG_RETURN((m_file[0]->ha_table_flags() &
+ ~(PARTITION_DISABLED_TABLE_FLAGS)) |
+ (PARTITION_ENABLED_TABLE_FLAGS));
}
/*
@@ -939,9 +948,11 @@ private:
auto_increment_lock= FALSE;
}
}
- virtual void set_auto_increment_if_higher(const ulonglong nr)
+ virtual void set_auto_increment_if_higher(Field *field)
{
HA_DATA_PARTITION *ha_data= (HA_DATA_PARTITION*) table_share->ha_data;
+ ulonglong nr= (((Field_num*) field)->unsigned_flag ||
+ field->val_int() > 0) ? field->val_int() : 0;
lock_auto_increment();
DBUG_ASSERT(ha_data->auto_inc_initialized == TRUE);
/* must check when the mutex is taken */
diff --git a/sql/handler.cc b/sql/handler.cc
index abffdaf49d1..1dba5b41e6f 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -1913,12 +1913,42 @@ bool ha_flush_logs(handlerton *db_type)
return FALSE;
}
+
+/**
+ @brief make canonical filename
+
+ @param[in] file table handler
+ @param[in] path original path
+ @param[out] tmp_path buffer for canonized path
+
+ @details Lower case db name and table name path parts for
+ non file based tables when lower_case_table_names
+ is 2 (store as is, compare in lower case).
+ Filesystem path prefix (mysql_data_home or tmpdir)
+ is left intact.
+
+ @note tmp_path may be left intact if no conversion was
+ performed.
+
+ @retval canonized path
+
+ @todo This may be done more efficiently when table path
+ gets built. Convert this function to something like
+ ASSERT_CANONICAL_FILENAME.
+*/
const char *get_canonical_filename(handler *file, const char *path,
char *tmp_path)
{
+ uint i;
if (lower_case_table_names != 2 || (file->ha_table_flags() & HA_FILE_BASED))
return path;
+ for (i= 0; i <= mysql_tmpdir_list.max; i++)
+ {
+ if (is_prefix(path, mysql_tmpdir_list.list[i]))
+ return path;
+ }
+
/* Ensure that table handler get path in lower case */
if (tmp_path != path)
strmov(tmp_path, path);
@@ -3499,14 +3529,10 @@ int handler::index_next_same(uchar *buf, const uchar *key, uint keylen)
if (!(error=index_next(buf)))
{
my_ptrdiff_t ptrdiff= buf - table->record[0];
- uchar *save_record_0;
- KEY *key_info;
- KEY_PART_INFO *key_part;
- KEY_PART_INFO *key_part_end;
- LINT_INIT(save_record_0);
- LINT_INIT(key_info);
- LINT_INIT(key_part);
- LINT_INIT(key_part_end);
+ uchar *UNINIT_VAR(save_record_0);
+ KEY *UNINIT_VAR(key_info);
+ KEY_PART_INFO *UNINIT_VAR(key_part);
+ KEY_PART_INFO *UNINIT_VAR(key_part_end);
/*
key_cmp_if_same() compares table->record[0] against 'key'.
diff --git a/sql/handler.h b/sql/handler.h
index ea0b134e53d..632c262b59a 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -923,6 +923,15 @@ typedef struct st_ha_create_information
ulong key_block_size;
SQL_LIST merge_list;
handlerton *db_type;
+ /**
+ Row type of the table definition.
+
+ Defaults to ROW_TYPE_DEFAULT for all non-ALTER statements.
+ For ALTER TABLE defaults to ROW_TYPE_NOT_USED (means "keep the current").
+
+ Can be changed either explicitly by the parser.
+ If nothing speficied inherits the value of the original table (if present).
+ */
enum row_type row_type;
uint null_bits; /* NULL bits at start of record */
uint options; /* OR of HA_CREATE_ options */
diff --git a/sql/item.cc b/sql/item.cc
index 26df3a45971..86e4551e55b 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -6331,9 +6331,26 @@ bool Item_direct_view_ref::fix_fields(THD *thd, Item **reference)
/* view fild reference must be defined */
DBUG_ASSERT(*ref);
/* (*ref)->check_cols() will be made in Item_direct_ref::fix_fields */
- if (!(*ref)->fixed &&
- ((*ref)->fix_fields(thd, ref)))
+ if ((*ref)->fixed)
+ {
+ Item *ref_item= (*ref)->real_item();
+ if (ref_item->type() == Item::FIELD_ITEM)
+ {
+ /*
+ In some cases we need to update table read set(see bug#47150).
+ If ref item is FIELD_ITEM and fixed then field and table
+ have proper values. So we can use them for update.
+ */
+ Field *fld= ((Item_field*) ref_item)->field;
+ DBUG_ASSERT(fld && fld->table);
+ if (thd->mark_used_columns == MARK_COLUMNS_READ)
+ bitmap_set_bit(fld->table->read_set, fld->field_index);
+ }
+ }
+ else if (!(*ref)->fixed &&
+ ((*ref)->fix_fields(thd, ref)))
return TRUE;
+
return Item_direct_ref::fix_fields(thd, reference);
}
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index d229453b795..c29031d25b5 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -189,6 +189,7 @@ enum_field_types agg_field_type(Item **items, uint nitems)
collect_cmp_types()
items Array of items to collect types from
nitems Number of items in the array
+ skip_nulls Don't collect types of NULL items if TRUE
DESCRIPTION
This function collects different result types for comparison of the first
@@ -199,7 +200,7 @@ enum_field_types agg_field_type(Item **items, uint nitems)
Bitmap of collected types - otherwise
*/
-static uint collect_cmp_types(Item **items, uint nitems)
+static uint collect_cmp_types(Item **items, uint nitems, bool skip_nulls= FALSE)
{
uint i;
uint found_types;
@@ -208,6 +209,8 @@ static uint collect_cmp_types(Item **items, uint nitems)
found_types= 0;
for (i= 1; i < nitems ; i++)
{
+ if (skip_nulls && items[i]->type() == Item::NULL_ITEM)
+ continue; // Skip NULL constant items
if ((left_result == ROW_RESULT ||
items[i]->result_type() == ROW_RESULT) &&
cmp_row_type(items[0], items[i]))
@@ -215,6 +218,12 @@ static uint collect_cmp_types(Item **items, uint nitems)
found_types|= 1<< (uint)item_cmp_type(left_result,
items[i]->result_type());
}
+ /*
+ Even if all right-hand items are NULLs and we are skipping them all, we need
+ at least one type bit in the found_type bitmask.
+ */
+ if (skip_nulls && !found_types)
+ found_types= 1 << (uint)left_result;
return found_types;
}
@@ -3515,7 +3524,7 @@ void Item_func_in::fix_length_and_dec()
uint type_cnt= 0, i;
Item_result cmp_type= STRING_RESULT;
left_result_type= args[0]->result_type();
- if (!(found_types= collect_cmp_types(args, arg_count)))
+ if (!(found_types= collect_cmp_types(args, arg_count, true)))
return;
for (arg= args + 1, arg_end= args + arg_count; arg != arg_end ; arg++)
@@ -3693,9 +3702,11 @@ void Item_func_in::fix_length_and_dec()
uint j=0;
for (uint i=1 ; i < arg_count ; i++)
{
- array->set(j,args[i]);
if (!args[i]->null_value) // Skip NULL values
+ {
+ array->set(j,args[i]);
j++;
+ }
else
have_null= 1;
}
diff --git a/sql/item_func.cc b/sql/item_func.cc
index c775848cff2..d9e6f76dd6b 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -435,8 +435,7 @@ bool Item_func::eq(const Item *item, bool binary_cmp) const
Field *Item_func::tmp_table_field(TABLE *table)
{
- Field *field;
- LINT_INIT(field);
+ Field *field= NULL;
switch (result_type()) {
case INT_RESULT:
@@ -4236,9 +4235,8 @@ void Item_func_set_user_var::save_item_result(Item *item)
bool
Item_func_set_user_var::update()
{
- bool res;
+ bool res= 0;
DBUG_ENTER("Item_func_set_user_var::update");
- LINT_INIT(res);
switch (cached_result_type) {
case REAL_RESULT:
diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc
index be94f19f597..39c4b8e7033 100644
--- a/sql/item_strfunc.cc
+++ b/sql/item_strfunc.cc
@@ -631,6 +631,7 @@ String *Item_func_concat_ws::val_str(String *str)
String tmp_sep_str(tmp_str_buff, sizeof(tmp_str_buff),default_charset_info),
*sep_str, *res, *res2,*use_as_buff;
uint i;
+ bool is_const= 0;
null_value=0;
if (!(sep_str= args[0]->val_str(&tmp_sep_str)))
@@ -644,7 +645,11 @@ String *Item_func_concat_ws::val_str(String *str)
// If not, return the empty string
for (i=1; i < arg_count; i++)
if ((res= args[i]->val_str(str)))
+ {
+ is_const= args[i]->const_item() || !args[i]->used_tables();
break;
+ }
+
if (i == arg_count)
return &my_empty_string;
@@ -662,7 +667,7 @@ String *Item_func_concat_ws::val_str(String *str)
current_thd->variables.max_allowed_packet);
goto null;
}
- if (res->alloced_length() >=
+ if (!is_const && res->alloced_length() >=
res->length() + sep_str->length() + res2->length())
{ // Use old buffer
res->append(*sep_str); // res->length() > 0 always
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index cdb091fa07e..da651cec70c 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -155,13 +155,11 @@ bool Item_subselect::fix_fields(THD *thd_param, Item **ref)
if (check_stack_overrun(thd, STACK_MIN_SIZE, (uchar*)&res))
return TRUE;
- res= engine->prepare();
-
- // all transformation is done (used by prepared statements)
- changed= 1;
-
- if (!res)
+ if (!(res= engine->prepare()))
{
+ // all transformation is done (used by prepared statements)
+ changed= 1;
+
if (substitution)
{
int ret= 0;
diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc
index a9e5c71ab56..b5037c08b3c 100644
--- a/sql/item_timefunc.cc
+++ b/sql/item_timefunc.cc
@@ -278,9 +278,9 @@ static bool extract_date_time(DATE_TIME_FORMAT *format,
int strict_week_number_year= -1;
int frac_part;
bool usa_time= 0;
- bool sunday_first_n_first_week_non_iso;
- bool strict_week_number;
- bool strict_week_number_year_type;
+ bool UNINIT_VAR(sunday_first_n_first_week_non_iso);
+ bool UNINIT_VAR(strict_week_number);
+ bool UNINIT_VAR(strict_week_number_year_type);
const char *val_begin= val;
const char *val_end= val + length;
const char *ptr= format->format.str;
@@ -288,11 +288,6 @@ static bool extract_date_time(DATE_TIME_FORMAT *format,
CHARSET_INFO *cs= &my_charset_bin;
DBUG_ENTER("extract_date_time");
- LINT_INIT(strict_week_number);
- /* Remove valgrind varnings when using gcc 3.3 and -O1 */
- PURIFY_OR_LINT_INIT(strict_week_number_year_type);
- PURIFY_OR_LINT_INIT(sunday_first_n_first_week_non_iso);
-
if (!sub_pattern_end)
bzero((char*) l_time, sizeof(*l_time));
@@ -1349,15 +1344,11 @@ bool get_interval_value(Item *args,interval_type int_type,
String *str_value, INTERVAL *interval)
{
ulonglong array[5];
- longlong value;
- const char *str;
- size_t length;
+ longlong UNINIT_VAR(value);
+ const char *UNINIT_VAR(str);
+ size_t UNINIT_VAR(length);
CHARSET_INFO *cs=str_value->charset();
- LINT_INIT(value);
- LINT_INIT(str);
- LINT_INIT(length);
-
bzero((char*) interval,sizeof(*interval));
if ((int) int_type <= INTERVAL_MICROSECOND)
{
diff --git a/sql/item_xmlfunc.cc b/sql/item_xmlfunc.cc
index 6320873e4d3..1eff00027f2 100644
--- a/sql/item_xmlfunc.cc
+++ b/sql/item_xmlfunc.cc
@@ -1354,7 +1354,7 @@ my_xpath_lex_scan(MY_XPATH *xpath,
MY_XPATH_LEX *lex, const char *beg, const char *end)
{
int ch, ctype, length;
- for ( ; beg < end && *beg == ' ' ; beg++); // skip leading spaces
+ for ( ; beg < end && *beg == ' ' ; beg++) ; // skip leading spaces
lex->beg= beg;
if (beg >= end)
@@ -1423,7 +1423,7 @@ my_xpath_lex_scan(MY_XPATH *xpath,
if (my_xdigit(ch)) // a sequence of digits
{
- for ( ; beg < end && my_xdigit(*beg) ; beg++);
+ for ( ; beg < end && my_xdigit(*beg) ; beg++) ;
lex->end= beg;
lex->term= MY_XPATH_LEX_DIGITS;
return;
@@ -1431,7 +1431,7 @@ my_xpath_lex_scan(MY_XPATH *xpath,
if (ch == '"' || ch == '\'') // a string: either '...' or "..."
{
- for ( ; beg < end && *beg != ch ; beg++);
+ for ( ; beg < end && *beg != ch ; beg++) ;
if (beg < end)
{
lex->end= beg+1;
diff --git a/sql/log.cc b/sql/log.cc
index fa063196e14..fc447579433 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -1068,14 +1068,10 @@ bool LOGGER::general_log_write(THD *thd, enum enum_server_command command,
Log_event_handler **current_handler= general_log_handler_list;
char user_host_buff[MAX_USER_HOST_SIZE + 1];
Security_context *sctx= thd->security_ctx;
- ulong id;
uint user_host_len= 0;
time_t current_time;
- if (thd)
- id= thd->thread_id; /* Normal thread */
- else
- id= 0; /* Log from connect handler */
+ DBUG_ASSERT(thd);
lock_shared();
if (!opt_log)
@@ -1094,7 +1090,7 @@ bool LOGGER::general_log_write(THD *thd, enum enum_server_command command,
while (*current_handler)
error|= (*current_handler++)->
log_general(thd, current_time, user_host_buff,
- user_host_len, id,
+ user_host_len, thd->thread_id,
command_name[(uint) command].str,
command_name[(uint) command].length,
query, query_length,
@@ -1633,10 +1629,10 @@ static int binlog_commit(handlerton *hton, THD *thd, bool all)
/*
This is part of the stmt rollback.
*/
- if (!all)
+ if (!all)
cache_mngr->trx_cache.set_prev_position(MY_OFF_T_UNDEF);
- DBUG_RETURN(error);
- }
+ DBUG_RETURN(error);
+}
/**
This function is called when a transaction or a statement is rolled back.
diff --git a/sql/log_event.cc b/sql/log_event.cc
index 6c7b6fdf409..f732d307b8a 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -1853,6 +1853,7 @@ Rows_log_event::print_verbose_one_row(IO_CACHE *file, table_def *td,
{
const uchar *value0= value;
const uchar *null_bits= value;
+ uint null_bit_index= 0;
char typestr[64]= "";
value+= (m_width + 7) / 8;
@@ -1861,7 +1862,8 @@ Rows_log_event::print_verbose_one_row(IO_CACHE *file, table_def *td,
for (size_t i= 0; i < td->size(); i ++)
{
- int is_null= (null_bits[i / 8] >> (i % 8)) & 0x01;
+ int is_null= (null_bits[null_bit_index / 8]
+ >> (null_bit_index % 8)) & 0x01;
if (bitmap_is_set(cols_bitmap, i) == 0)
continue;
@@ -1898,6 +1900,8 @@ Rows_log_event::print_verbose_one_row(IO_CACHE *file, table_def *td,
}
my_b_printf(file, "\n");
+
+ null_bit_index++;
}
return value - value0;
}
@@ -3977,6 +3981,7 @@ bool Format_description_log_event::write(IO_CACHE* file)
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
int Format_description_log_event::do_apply_event(Relay_log_info const *rli)
{
+ int ret= 0;
DBUG_ENTER("Format_description_log_event::do_apply_event");
#ifdef USING_TRANSACTIONS
@@ -4018,17 +4023,21 @@ int Format_description_log_event::do_apply_event(Relay_log_info const *rli)
0, then 96, then jump to first really asked event (which is
>96). So this is ok.
*/
- DBUG_RETURN(Start_log_event_v3::do_apply_event(rli));
+ ret= Start_log_event_v3::do_apply_event(rli);
}
- DBUG_RETURN(0);
+
+ if (!ret)
+ {
+ /* Save the information describing this binlog */
+ delete rli->relay_log.description_event_for_exec;
+ const_cast<Relay_log_info *>(rli)->relay_log.description_event_for_exec= this;
+ }
+
+ DBUG_RETURN(ret);
}
int Format_description_log_event::do_update_pos(Relay_log_info *rli)
{
- /* save the information describing this binlog */
- delete rli->relay_log.description_event_for_exec;
- rli->relay_log.description_event_for_exec= this;
-
if (server_id == (uint32) ::server_id)
{
/*
@@ -4132,7 +4141,7 @@ uint Load_log_event::get_query_buffer_length()
}
-void Load_log_event::print_query(bool need_db, char *buf,
+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;
@@ -4156,9 +4165,9 @@ void Load_log_event::print_query(bool need_db, char *buf,
pos= strmov(pos+fname_len, "' ");
if (sql_ex.opt_flags & REPLACE_FLAG)
- pos= strmov(pos, " REPLACE ");
+ pos= strmov(pos, "REPLACE ");
else if (sql_ex.opt_flags & IGNORE_FLAG)
- pos= strmov(pos, " IGNORE ");
+ pos= strmov(pos, "IGNORE ");
pos= strmov(pos ,"INTO");
@@ -4169,8 +4178,16 @@ void Load_log_event::print_query(bool need_db, char *buf,
memcpy(pos, table_name, table_name_len);
pos+= table_name_len;
- /* We have to create all optinal fields as the default is not empty */
- pos= strmov(pos, "` FIELDS TERMINATED BY ");
+ if (cs != NULL)
+ {
+ pos= strmov(pos ,"` CHARACTER SET ");
+ pos= strmov(pos , 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);
if (sql_ex.opt_flags & OPT_ENCLOSED_FLAG)
pos= strmov(pos, " OPTIONALLY ");
@@ -4224,7 +4241,7 @@ void Load_log_event::pack_info(Protocol *protocol)
if (!(buf= (char*) my_malloc(get_query_buffer_length(), MYF(MY_WME))))
return;
- print_query(TRUE, buf, &end, 0, 0);
+ print_query(TRUE, NULL, buf, &end, 0, 0);
protocol->store(buf, end-buf, &my_charset_bin);
my_free(buf, MYF(0));
}
@@ -4489,9 +4506,9 @@ void Load_log_event::print(FILE* file_arg, PRINT_EVENT_INFO* print_event_info,
my_b_printf(&cache, "INFILE '%-*s' ", fname_len, fname);
if (sql_ex.opt_flags & REPLACE_FLAG)
- my_b_printf(&cache," REPLACE ");
+ my_b_printf(&cache,"REPLACE ");
else if (sql_ex.opt_flags & IGNORE_FLAG)
- my_b_printf(&cache," IGNORE ");
+ my_b_printf(&cache,"IGNORE ");
my_b_printf(&cache, "INTO TABLE `%s`", table_name);
my_b_printf(&cache, " FIELDS TERMINATED BY ");
@@ -4699,8 +4716,7 @@ int Load_log_event::do_apply_event(NET* net, Relay_log_info const *rli,
goto error;
}
- print_query(FALSE, load_data_query, &end, (char **)&thd->lex->fname_start,
- (char **)&thd->lex->fname_end);
+ print_query(FALSE, NULL, load_data_query, &end, NULL, NULL);
*end= 0;
thd->set_query(load_data_query, (uint) (end - load_data_query));
@@ -6854,7 +6870,7 @@ void Execute_load_query_log_event::print(FILE* file,
my_b_printf(&cache, "\'");
if (dup_handling == LOAD_DUP_REPLACE)
my_b_printf(&cache, " REPLACE");
- my_b_printf(&cache, " INTO ");
+ my_b_printf(&cache, " INTO");
my_b_write(&cache, (uchar*) query + fn_pos_end, q_len-fn_pos_end);
my_b_printf(&cache, "\n%s\n", print_event_info->delimiter);
}
@@ -7628,7 +7644,17 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli)
thd->reset_current_stmt_binlog_format_row();
const_cast<Relay_log_info*>(rli)->cleanup_context(thd, error);
thd->is_slave_error= 1;
+ DBUG_RETURN(error);
}
+
+ if (get_flags(STMT_END_F))
+ if (error= rows_event_stmt_cleanup(rli, thd))
+ rli->report(ERROR_LEVEL, error,
+ "Error in %s event: commit of row events failed, "
+ "table `%s`.`%s`",
+ get_type_str(), m_table->s->db.str,
+ m_table->s->table_name.str);
+
DBUG_RETURN(error);
}
@@ -7737,33 +7763,19 @@ Rows_log_event::do_update_pos(Relay_log_info *rli)
if (get_flags(STMT_END_F))
{
- if ((error= rows_event_stmt_cleanup(rli, thd)) == 0)
- {
- /*
- Indicate that a statement is finished.
- Step the group log position if we are not in a transaction,
- otherwise increase the event log position.
- */
- rli->stmt_done(log_pos, when);
-
- /*
- Clear any errors pushed in thd->net.last_err* if for example "no key
- found" (as this is allowed). This is a safety measure; apparently
- those errors (e.g. when executing a Delete_rows_log_event of a
- non-existing row, like in rpl_row_mystery22.test,
- thd->net.last_error = "Can't find record in 't1'" and last_errno=1032)
- do not become visible. We still prefer to wipe them out.
- */
- thd->clear_error();
- }
- else
- {
- rli->report(ERROR_LEVEL, error,
- "Error in %s event: commit of row events failed, "
- "table `%s`.`%s`",
- get_type_str(), m_table->s->db.str,
- m_table->s->table_name.str);
- }
+ /*
+ Indicate that a statement is finished.
+ Step the group log position if we are not in a transaction,
+ otherwise increase the event log position.
+ */
+ rli->stmt_done(log_pos, when);
+ /*
+ Clear any errors in thd->net.last_err*. It is not known if this is
+ needed or not. It is believed that any errors that may exist in
+ thd->net.last_err* are allowed. Examples of errors are "key not
+ found", which is produced in the test case rpl_row_conflicts.test
+ */
+ thd->clear_error();
}
else
{
@@ -8428,6 +8440,16 @@ Write_rows_log_event::do_before_row_operations(const Slave_reporting_capability
/* Honor next number column if present */
m_table->next_number_field= m_table->found_next_number_field;
+ /*
+ * Fixed Bug#45999, In RBR, Store engine of Slave auto-generates new
+ * sequence numbers for auto_increment fields if the values of them are 0.
+ * If generateing a sequence number is decided by the values of
+ * table->auto_increment_field_not_null and SQL_MODE(if includes
+ * MODE_NO_AUTO_VALUE_ON_ZERO) in update_auto_increment function.
+ * SQL_MODE of slave sql thread is always consistency with master's.
+ * In RBR, auto_increment fields never are NULL.
+ */
+ m_table->auto_increment_field_not_null= TRUE;
return error;
}
@@ -8437,6 +8459,7 @@ Write_rows_log_event::do_after_row_operations(const Slave_reporting_capability *
{
int local_error= 0;
m_table->next_number_field=0;
+ m_table->auto_increment_field_not_null= FALSE;
if (bit_is_set(slave_exec_mode, SLAVE_EXEC_MODE_IDEMPOTENT) == 1 ||
m_table->s->db_type()->db_type == DB_TYPE_NDBCLUSTER)
{
@@ -8944,11 +8967,11 @@ int Rows_log_event::find_row(const Relay_log_info *rli)
*/
store_record(table,record[1]);
- if (table->s->keys > 0)
+ if (table->s->keys > 0 && table->s->keys_in_use.is_set(0))
{
DBUG_PRINT("info",("locating record using primary key (index_read)"));
- /* We have a key: search the table using the index */
+ /* The 0th key is active: search the table using the index */
if (!table->file->inited && (error= table->file->ha_index_init(0, FALSE)))
{
DBUG_PRINT("info",("ha_index_init returns error %d",error));
diff --git a/sql/log_event.h b/sql/log_event.h
index 5a897c55061..cba0264f4ae 100644
--- a/sql/log_event.h
+++ b/sql/log_event.h
@@ -2021,15 +2021,15 @@ private:
class Load_log_event: public Log_event
{
private:
- uint get_query_buffer_length();
- void print_query(bool need_db, char *buf, char **end,
- char **fn_start, char **fn_end);
protected:
int copy_log_event(const char *buf, ulong event_len,
int body_offset,
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);
ulong thread_id;
ulong slave_proxy_id;
uint32 table_name_len;
diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc
index fe81e865048..ee3d5768370 100644
--- a/sql/log_event_old.cc
+++ b/sql/log_event_old.cc
@@ -1214,8 +1214,8 @@ Old_rows_log_event::Old_rows_log_event(THD *thd_arg, TABLE *tbl_arg, ulong tid,
solution, to be able to terminate a started statement in the
binary log: the extraneous events will be removed in the future.
*/
- DBUG_ASSERT(tbl_arg && tbl_arg->s && tid != ~0UL ||
- !tbl_arg && !cols && tid == ~0UL);
+ DBUG_ASSERT((tbl_arg && tbl_arg->s && tid != ~0UL) ||
+ (!tbl_arg && !cols && tid == ~0UL));
if (thd_arg->options & OPTION_NO_FOREIGN_KEY_CHECKS)
set_flags(NO_FOREIGN_KEY_CHECKS_F);
@@ -1378,7 +1378,7 @@ int Old_rows_log_event::do_add_row_data(uchar *row_data, size_t length)
#endif
DBUG_ASSERT(m_rows_buf <= m_rows_cur);
- DBUG_ASSERT(!m_rows_buf || m_rows_end && m_rows_buf < m_rows_end);
+ DBUG_ASSERT(!m_rows_buf || (m_rows_end && m_rows_buf < m_rows_end));
DBUG_ASSERT(m_rows_cur <= m_rows_end);
/* The cast will always work since m_rows_cur <= m_rows_end */
@@ -1755,32 +1755,33 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli)
thd->is_slave_error= 1;
DBUG_RETURN(error);
}
- DBUG_RETURN(0);
-}
-
-Log_event::enum_skip_reason
-Old_rows_log_event::do_shall_skip(Relay_log_info *rli)
-{
/*
- If the slave skip counter is 1 and this event does not end a
- statement, then we should not start executing on the next event.
- Otherwise, we defer the decision to the normal skipping logic.
+ This code would ideally be placed in do_update_pos() instead, but
+ since we have no access to table there, we do the setting of
+ last_event_start_time here instead.
*/
- if (rli->slave_skip_counter == 1 && !get_flags(STMT_END_F))
- return Log_event::EVENT_SKIP_IGNORE;
- else
- return Log_event::do_shall_skip(rli);
-}
-
-int
-Old_rows_log_event::do_update_pos(Relay_log_info *rli)
-{
- DBUG_ENTER("Old_rows_log_event::do_update_pos");
- int error= 0;
-
- DBUG_PRINT("info", ("flags: %s",
- get_flags(STMT_END_F) ? "STMT_END_F " : ""));
+ if (table && (table->s->primary_key == MAX_KEY) &&
+ !use_trans_cache() && get_flags(STMT_END_F) == RLE_NO_FLAGS)
+ {
+ /*
+ ------------ Temporary fix until WL#2975 is implemented ---------
+
+ This event is not the last one (no STMT_END_F). If we stop now
+ (in case of terminate_slave_thread()), how will we restart? We
+ have to restart from Table_map_log_event, but as this table is
+ not transactional, the rows already inserted will still be
+ present, and idempotency is not guaranteed (no PK) so we risk
+ that repeating leads to double insert. So we desperately try to
+ continue, hope we'll eventually leave this buggy situation (by
+ executing the final Old_rows_log_event). If we are in a hopeless
+ wait (reached end of last relay log and nothing gets appended
+ there), we timeout after one minute, and notify DBA about the
+ problem. When WL#2975 is implemented, just remove the member
+ Relay_log_info::last_event_start_time and all its occurrences.
+ */
+ const_cast<Relay_log_info*>(rli)->last_event_start_time= my_time(0);
+ }
if (get_flags(STMT_END_F))
{
@@ -1810,7 +1811,12 @@ Old_rows_log_event::do_update_pos(Relay_log_info *rli)
are involved, commit the transaction and flush the pending event to the
binlog.
*/
- error= ha_autocommit_or_rollback(thd, 0);
+ if ((error= ha_autocommit_or_rollback(thd, 0)))
+ rli->report(ERROR_LEVEL, error,
+ "Error in %s event: commit of row events failed, "
+ "table `%s`.`%s`",
+ get_type_str(), m_table->s->db.str,
+ m_table->s->table_name.str);
/*
Now what if this is not a transactional engine? we still need to
@@ -1823,33 +1829,51 @@ Old_rows_log_event::do_update_pos(Relay_log_info *rli)
*/
thd->reset_current_stmt_binlog_format_row();
- rli->cleanup_context(thd, 0);
- if (error == 0)
- {
- /*
- Indicate that a statement is finished.
- Step the group log position if we are not in a transaction,
- otherwise increase the event log position.
- */
- rli->stmt_done(log_pos, when);
+ const_cast<Relay_log_info*>(rli)->cleanup_context(thd, 0);
+ }
- /*
- Clear any errors pushed in thd->net.client_last_err* if for
- example "no key found" (as this is allowed). This is a safety
- measure; apparently those errors (e.g. when executing a
- Delete_rows_log_event_old of a non-existing row, like in
- rpl_row_mystery22.test, thd->net.last_error = "Can't
- find record in 't1'" and last_errno=1032) do not become
- visible. We still prefer to wipe them out.
- */
- thd->clear_error();
- }
- else
- rli->report(ERROR_LEVEL, error,
- "Error in %s event: commit of row events failed, "
- "table `%s`.`%s`",
- get_type_str(), m_table->s->db.str,
- m_table->s->table_name.str);
+ DBUG_RETURN(error);
+}
+
+
+Log_event::enum_skip_reason
+Old_rows_log_event::do_shall_skip(Relay_log_info *rli)
+{
+ /*
+ If the slave skip counter is 1 and this event does not end a
+ statement, then we should not start executing on the next event.
+ Otherwise, we defer the decision to the normal skipping logic.
+ */
+ if (rli->slave_skip_counter == 1 && !get_flags(STMT_END_F))
+ return Log_event::EVENT_SKIP_IGNORE;
+ else
+ return Log_event::do_shall_skip(rli);
+}
+
+int
+Old_rows_log_event::do_update_pos(Relay_log_info *rli)
+{
+ DBUG_ENTER("Old_rows_log_event::do_update_pos");
+ int error= 0;
+
+ DBUG_PRINT("info", ("flags: %s",
+ get_flags(STMT_END_F) ? "STMT_END_F " : ""));
+
+ if (get_flags(STMT_END_F))
+ {
+ /*
+ Indicate that a statement is finished.
+ Step the group log position if we are not in a transaction,
+ otherwise increase the event log position.
+ */
+ rli->stmt_done(log_pos, when);
+ /*
+ Clear any errors in thd->net.last_err*. It is not known if this is
+ needed or not. It is believed that any errors that may exist in
+ thd->net.last_err* are allowed. Examples of errors are "key not
+ found", which is produced in the test case rpl_row_conflicts.test
+ */
+ thd->clear_error();
}
else
{
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index e3a1b5e6c73..7dbc3064f5f 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -2186,10 +2186,9 @@ enum enum_explain_filename_mode
{
EXPLAIN_ALL_VERBOSE= 0,
EXPLAIN_PARTITIONS_VERBOSE,
- EXPLAIN_PARTITIONS_AS_COMMENT,
- EXPLAIN_PARTITIONS_AS_COMMENT_NO_QUOTING
+ EXPLAIN_PARTITIONS_AS_COMMENT
};
-uint explain_filename(const char *from, char *to, uint to_length,
+uint explain_filename(THD* thd, const char *from, char *to, uint to_length,
enum_explain_filename_mode explain_mode);
uint filename_to_tablename(const char *from, char *to, uint to_length);
uint tablename_to_filename(const char *from, char *to, uint to_length);
@@ -2343,6 +2342,7 @@ inline void setup_table_map(TABLE *table, TABLE_LIST *table_list, uint tablenr)
table->tablenr= tablenr;
table->map= (table_map) 1 << tablenr;
table->force_index= table_list->force_index;
+ table->force_index_order= table->force_index_group= 0;
table->covering_keys= table->s->keys_for_keyread;
table->merge_keys.clear_all();
}
diff --git a/sql/mysql_priv.h.pp b/sql/mysql_priv.h.pp
index 8bb31f64587..d874a2591d1 100644
--- a/sql/mysql_priv.h.pp
+++ b/sql/mysql_priv.h.pp
@@ -773,7 +773,6 @@ extern int wild_compare(const char *str,const char *wildstr,
extern WF_PACK *wf_comp(char * str);
extern int wf_test(struct wild_file_pack *wf_pack,const char *name);
extern void wf_end(struct wild_file_pack *buffer);
-extern size_t strip_sp(char * str);
extern my_bool array_append_string_unique(const char *str,
const char **array, size_t size);
extern void get_date(char * to,int timeflag,time_t use_time);
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 80bd6b7b48c..70d7e69dbd4 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -26,6 +26,7 @@
#include "mysqld_suffix.h"
#include "mysys_err.h"
#include "events.h"
+#include "debug_sync.h"
#include "../storage/myisam/ha_myisam.h"
@@ -489,6 +490,9 @@ my_bool lower_case_file_system= 0;
my_bool opt_large_pages= 0;
my_bool opt_myisam_use_mmap= 0;
uint opt_large_page_size= 0;
+#if defined(ENABLED_DEBUG_SYNC)
+uint opt_debug_sync_timeout= 0;
+#endif /* defined(ENABLED_DEBUG_SYNC) */
my_bool opt_old_style_user_limits= 0, trust_function_creators= 0;
/*
True if there is at least one per-hour limit for some user, so we should
@@ -1112,13 +1116,13 @@ void kill_mysql(void)
#if defined(__NETWARE__)
extern "C" void kill_server(int sig_ptr)
-#define RETURN_FROM_KILL_SERVER DBUG_VOID_RETURN
+#define RETURN_FROM_KILL_SERVER return
#elif !defined(__WIN__)
static void *kill_server(void *sig_ptr)
-#define RETURN_FROM_KILL_SERVER DBUG_RETURN(0)
+#define RETURN_FROM_KILL_SERVER return 0
#else
static void __cdecl kill_server(int sig_ptr)
-#define RETURN_FROM_KILL_SERVER DBUG_VOID_RETURN
+#define RETURN_FROM_KILL_SERVER return
#endif
{
DBUG_ENTER("kill_server");
@@ -1126,7 +1130,10 @@ static void __cdecl kill_server(int sig_ptr)
int sig=(int) (long) sig_ptr; // This is passed a int
// if there is a signal during the kill in progress, ignore the other
if (kill_in_progress) // Safety
+ {
+ DBUG_LEAVE;
RETURN_FROM_KILL_SERVER;
+ }
kill_in_progress=TRUE;
abort_loop=1; // This should be set
if (sig != 0) // 0 is not a valid signal number
@@ -1161,12 +1168,19 @@ static void __cdecl kill_server(int sig_ptr)
pthread_join(select_thread, NULL); // wait for main thread
#endif /* __NETWARE__ */
+ DBUG_LEAVE; // Must match DBUG_ENTER()
my_thread_end();
pthread_exit(0);
/* purecov: end */
-#endif /* EMBEDDED_LIBRARY */
+ RETURN_FROM_KILL_SERVER; // Avoid compiler warnings
+
+#else /* EMBEDDED_LIBRARY*/
+
+ DBUG_LEAVE;
RETURN_FROM_KILL_SERVER;
+
+#endif /* EMBEDDED_LIBRARY */
}
@@ -1329,6 +1343,10 @@ void clean_up(bool print_message)
#ifdef USE_REGEX
my_regex_end();
#endif
+#if defined(ENABLED_DEBUG_SYNC)
+ /* End the debug sync facility. See debug_sync.cc. */
+ debug_sync_end();
+#endif /* defined(ENABLED_DEBUG_SYNC) */
#if !defined(EMBEDDED_LIBRARY)
if (!opt_bootstrap)
@@ -1941,8 +1959,9 @@ bool one_thread_per_connection_end(THD *thd, bool put_in_cache)
my_thread_end();
(void) pthread_cond_broadcast(&COND_thread_count);
+ DBUG_LEAVE; // Must match DBUG_ENTER()
pthread_exit(0);
- DBUG_RETURN(0); // Impossible
+ return 0; // Avoid compiler warnings
}
@@ -2762,7 +2781,9 @@ pthread_handler_t signal_hand(void *arg __attribute__((unused)))
DBUG_PRINT("quit",("signal_handler: calling my_thread_end()"));
my_thread_end();
signal_thread_in_use= 0;
+ DBUG_LEAVE; // Must match DBUG_ENTER()
pthread_exit(0); // Safety
+ return 0; // Avoid compiler warnings
}
switch (sig) {
case SIGTERM:
@@ -3458,6 +3479,12 @@ static int init_common_variables(const char *conf_file_name, int argc,
sys_var_slow_log_path.value= my_strdup(s, MYF(0));
sys_var_slow_log_path.value_length= strlen(s);
+#if defined(ENABLED_DEBUG_SYNC)
+ /* Initialize the debug sync facility. See debug_sync.cc. */
+ if (debug_sync_init())
+ return 1; /* purecov: tested */
+#endif /* defined(ENABLED_DEBUG_SYNC) */
+
#if (ENABLE_TEMP_POOL)
if (use_temp_pool && bitmap_init(&temp_pool,0,1024,1))
return 1;
@@ -3713,6 +3740,7 @@ static void end_ssl()
static int init_server_components()
{
+ FILE* reopen;
DBUG_ENTER("init_server_components");
/*
We need to call each of these following functions to ensure that
@@ -3755,7 +3783,7 @@ static int init_server_components()
if (freopen(log_error_file, "a+", stdout))
#endif
{
- freopen(log_error_file, "a+", stderr);
+ reopen= freopen(log_error_file, "a+", stderr);
setbuf(stderr, NULL);
}
}
@@ -4623,7 +4651,7 @@ default_service_handling(char **argv,
if (opt_delim= strchr(extra_opt, '='))
{
size_t length= ++opt_delim - extra_opt;
- strnmov(pos, extra_opt, length);
+ pos= strnmov(pos, extra_opt, length);
}
else
opt_delim= extra_opt;
@@ -4808,10 +4836,10 @@ static bool read_init_file(char *file_name)
DBUG_ENTER("read_init_file");
DBUG_PRINT("enter",("name: %s",file_name));
if (!(file=my_fopen(file_name,O_RDONLY,MYF(MY_WME))))
- return(1);
+ DBUG_RETURN(TRUE);
bootstrap(file);
(void) my_fclose(file,MYF(MY_WME));
- return 0;
+ DBUG_RETURN(FALSE);
}
@@ -4864,7 +4892,7 @@ void create_thread_to_handle_connection(THD *thd)
handle_one_connection,
(void*) thd)))
{
- /* purify: begin inspected */
+ /* purecov: begin inspected */
DBUG_PRINT("error",
("Can't create thread to handle request (error %d)",
error));
@@ -5678,6 +5706,9 @@ enum options_mysqld
OPT_SECURE_FILE_PRIV,
OPT_MIN_EXAMINED_ROW_LIMIT,
OPT_LOG_SLOW_SLAVE_STATEMENTS,
+#if defined(ENABLED_DEBUG_SYNC)
+ OPT_DEBUG_SYNC_TIMEOUT,
+#endif /* defined(ENABLED_DEBUG_SYNC) */
OPT_OLD_MODE,
OPT_SLAVE_EXEC_MODE,
OPT_GENERAL_LOG_FILE,
@@ -6452,6 +6483,14 @@ log and this option does nothing anymore.",
"Decision to use in heuristic recover process. Possible values are COMMIT or ROLLBACK.",
(uchar**) &opt_tc_heuristic_recover, (uchar**) &opt_tc_heuristic_recover,
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+#if defined(ENABLED_DEBUG_SYNC)
+ {"debug-sync-timeout", OPT_DEBUG_SYNC_TIMEOUT,
+ "Enable the debug sync facility "
+ "and optionally specify a default wait timeout in seconds. "
+ "A zero value keeps the facility disabled.",
+ (uchar**) &opt_debug_sync_timeout, 0,
+ 0, GET_UINT, OPT_ARG, 0, 0, UINT_MAX, 0, 0, 0},
+#endif /* defined(ENABLED_DEBUG_SYNC) */
{"temp-pool", OPT_TEMP_POOL,
#if (ENABLE_TEMP_POOL)
"Using this option will cause most temporary files created to use a small set of names, rather than a unique name for each new file.",
@@ -7695,6 +7734,9 @@ static int mysql_init_variables(void)
bzero((uchar*) &mysql_tmpdir_list, sizeof(mysql_tmpdir_list));
bzero((char *) &global_status_var, sizeof(global_status_var));
opt_large_pages= 0;
+#if defined(ENABLED_DEBUG_SYNC)
+ opt_debug_sync_timeout= 0;
+#endif /* defined(ENABLED_DEBUG_SYNC) */
key_map_full.set_all();
/* Character sets */
@@ -8429,6 +8471,22 @@ mysqld_get_one_option(int optid,
lower_case_table_names= argument ? atoi(argument) : 1;
lower_case_table_names_used= 1;
break;
+#if defined(ENABLED_DEBUG_SYNC)
+ case OPT_DEBUG_SYNC_TIMEOUT:
+ /*
+ Debug Sync Facility. See debug_sync.cc.
+ Default timeout for WAIT_FOR action.
+ Default value is zero (facility disabled).
+ If option is given without an argument, supply a non-zero value.
+ */
+ if (!argument)
+ {
+ /* purecov: begin tested */
+ opt_debug_sync_timeout= DEBUG_SYNC_DEFAULT_WAIT_TIMEOUT;
+ /* purecov: end */
+ }
+ break;
+#endif /* defined(ENABLED_DEBUG_SYNC) */
}
return 0;
}
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index 05575e2744b..119f90bc97a 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -5876,6 +5876,7 @@ get_mm_leaf(RANGE_OPT_PARAM *param, COND *conf_func, Field *field,
if (type == Item_func::LT_FUNC && (value->val_int() > 0))
type = Item_func::LE_FUNC;
else if (type == Item_func::GT_FUNC &&
+ (field->type() != FIELD_TYPE_BIT) &&
!((Field_num*)field)->unsigned_flag &&
!((Item_int*)value)->unsigned_flag &&
(value->val_int() < 0))
@@ -5913,7 +5914,9 @@ get_mm_leaf(RANGE_OPT_PARAM *param, COND *conf_func, Field *field,
*/
if (field->result_type() == INT_RESULT &&
value->result_type() == INT_RESULT &&
- ((Field_num*)field)->unsigned_flag && !((Item_int*)value)->unsigned_flag)
+ ((field->type() == FIELD_TYPE_BIT ||
+ ((Field_num *) field)->unsigned_flag) &&
+ !((Item_int*) value)->unsigned_flag))
{
longlong item_val= value->val_int();
if (item_val < 0)
@@ -6509,6 +6512,63 @@ get_range(SEL_ARG **e1,SEL_ARG **e2,SEL_ARG *root1)
}
+/**
+ Combine two range expression under a common OR. On a logical level, the
+ transformation is key_or( expr1, expr2 ) => expr1 OR expr2.
+
+ Both expressions are assumed to be in the SEL_ARG format. In a logic sense,
+ theformat is reminiscent of DNF, since an expression such as the following
+
+ ( 1 < kp1 < 10 AND p1 ) OR ( 10 <= kp2 < 20 AND p2 )
+
+ where there is a key consisting of keyparts ( kp1, kp2, ..., kpn ) and p1
+ and p2 are valid SEL_ARG expressions over keyparts kp2 ... kpn, is a valid
+ SEL_ARG condition. The disjuncts appear ordered by the minimum endpoint of
+ the first range and ranges must not overlap. It follows that they are also
+ ordered by maximum endpoints. Thus
+
+ ( 1 < kp1 <= 2 AND ( kp2 = 2 OR kp2 = 3 ) ) OR kp1 = 3
+
+ Is a a valid SER_ARG expression for a key of at least 2 keyparts.
+
+ For simplicity, we will assume that expr2 is a single range predicate,
+ i.e. on the form ( a < x < b AND ... ). It is easy to generalize to a
+ disjunction of several predicates by subsequently call key_or for each
+ disjunct.
+
+ The algorithm iterates over each disjunct of expr1, and for each disjunct
+ where the first keypart's range overlaps with the first keypart's range in
+ expr2:
+
+ If the predicates are equal for the rest of the keyparts, or if there are
+ no more, the range in expr2 has its endpoints copied in, and the SEL_ARG
+ node in expr2 is deallocated. If more ranges became connected in expr1, the
+ surplus is also dealocated. If they differ, two ranges are created.
+
+ - The range leading up to the overlap. Empty if endpoints are equal.
+
+ - The overlapping sub-range. May be the entire range if they are equal.
+
+ Finally, there may be one more range if expr2's first keypart's range has a
+ greater maximum endpoint than the last range in expr1.
+
+ For the overlapping sub-range, we recursively call key_or. Thus in order to
+ compute key_or of
+
+ (1) ( 1 < kp1 < 10 AND 1 < kp2 < 10 )
+
+ (2) ( 2 < kp1 < 20 AND 4 < kp2 < 20 )
+
+ We create the ranges 1 < kp <= 2, 2 < kp1 < 10, 10 <= kp1 < 20. For the
+ first one, we simply hook on the condition for the second keypart from (1)
+ : 1 < kp2 < 10. For the second range 2 < kp1 < 10, key_or( 1 < kp2 < 10, 4
+ < kp2 < 20 ) is called, yielding 1 < kp2 < 20. For the last range, we reuse
+ the range 4 < kp2 < 20 from (2) for the second keypart. The result is thus
+
+ ( 1 < kp1 <= 2 AND 1 < kp2 < 10 ) OR
+ ( 2 < kp1 < 10 AND 1 < kp2 < 20 ) OR
+ ( 10 <= kp1 < 20 AND 4 < kp2 < 20 )
+*/
static SEL_ARG *
key_or(RANGE_OPT_PARAM *param, SEL_ARG *key1,SEL_ARG *key2)
{
@@ -6660,7 +6720,21 @@ key_or(RANGE_OPT_PARAM *param, SEL_ARG *key1,SEL_ARG *key2)
key1=key1->tree_delete(save);
}
last->copy_min(tmp);
- if (last->copy_min(key2) || last->copy_max(key2))
+ bool full_range= last->copy_min(key2);
+ if (!full_range)
+ {
+ if (last->next && key2->cmp_max_to_min(last->next) >= 0)
+ {
+ last->max_value= last->next->min_value;
+ if (last->next->min_flag & NEAR_MIN)
+ last->max_flag&= ~NEAR_MAX;
+ else
+ last->max_flag|= NEAR_MAX;
+ }
+ else
+ full_range= last->copy_max(key2);
+ }
+ if (full_range)
{ // Full range
key1->free_tree();
for (; key2 ; key2=key2->next)
@@ -6670,8 +6744,6 @@ key_or(RANGE_OPT_PARAM *param, SEL_ARG *key1,SEL_ARG *key2)
return 0;
}
}
- key2=key2->next;
- continue;
}
if (cmp >= 0 && tmp->cmp_min_to_min(key2) < 0)
diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc
index 8e7265ba1ad..e009cf1ca9f 100644
--- a/sql/opt_sum.cc
+++ b/sql/opt_sum.cc
@@ -97,7 +97,8 @@ static ulonglong get_exact_record_count(TABLE_LIST *tables)
@note
This function is only called for queries with sum functions and no
- GROUP BY part.
+ GROUP BY part. This means that the result set shall contain a single
+ row only
@retval
0 no errors
diff --git a/sql/partition_info.cc b/sql/partition_info.cc
index e2027d3571e..ba9ea0e876e 100644
--- a/sql/partition_info.cc
+++ b/sql/partition_info.cc
@@ -483,10 +483,8 @@ static bool check_engine_condition(partition_element *p_elem,
{
DBUG_RETURN(TRUE);
}
- else
- {
- DBUG_RETURN(FALSE);
- }
+
+ DBUG_RETURN(FALSE);
}
diff --git a/sql/repl_failsafe.cc b/sql/repl_failsafe.cc
index 3d375f906ec..947c3ee0654 100644
--- a/sql/repl_failsafe.cc
+++ b/sql/repl_failsafe.cc
@@ -637,9 +637,11 @@ err:
if (recovery_captain)
mysql_close(recovery_captain);
delete thd;
+
+ DBUG_LEAVE; // Must match DBUG_ENTER()
my_thread_end();
pthread_exit(0);
- DBUG_RETURN(0);
+ return 0; // Avoid compiler warnings
}
#endif
diff --git a/sql/rpl_filter.cc b/sql/rpl_filter.cc
index 3004a3905e5..68272c58bb1 100644
--- a/sql/rpl_filter.cc
+++ b/sql/rpl_filter.cc
@@ -350,6 +350,7 @@ Rpl_filter::add_do_db(const char* table_spec)
DBUG_ENTER("Rpl_filter::add_do_db");
i_string *db = new i_string(table_spec);
do_db.push_back(db);
+ DBUG_VOID_RETURN;
}
@@ -359,6 +360,7 @@ Rpl_filter::add_ignore_db(const char* table_spec)
DBUG_ENTER("Rpl_filter::add_ignore_db");
i_string *db = new i_string(table_spec);
ignore_db.push_back(db);
+ DBUG_VOID_RETURN;
}
extern "C" uchar *get_table_key(const uchar *, size_t *, my_bool);
diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc
index ec3adc79ca2..14b560600d8 100644
--- a/sql/rpl_rli.cc
+++ b/sql/rpl_rli.cc
@@ -307,7 +307,7 @@ Failed to open the existing relay log info file '%s' (errno %d)",
DBUG_RETURN(error);
err:
- sql_print_error(msg);
+ sql_print_error("%s", msg);
end_io_cache(&rli->info_file);
if (info_fd >= 0)
my_close(info_fd, MYF(0));
diff --git a/sql/set_var.cc b/sql/set_var.cc
index b20ea500eb5..1232683c886 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -529,11 +529,11 @@ static sys_var_const sys_skip_networking(&vars, "skip_networking",
static sys_var_const sys_skip_show_database(&vars, "skip_show_database",
OPT_GLOBAL, SHOW_BOOL,
(uchar*) &opt_skip_show_db);
-#ifdef HAVE_SYS_UN_H
+
static sys_var_const sys_socket(&vars, "socket",
OPT_GLOBAL, SHOW_CHAR_PTR,
(uchar*) &mysqld_unix_port);
-#endif
+
#ifdef HAVE_THR_SETCONCURRENCY
/* purecov: begin tested */
static sys_var_const sys_thread_concurrency(&vars, "thread_concurrency",
@@ -625,6 +625,12 @@ static sys_var_long_ptr sys_table_cache_size(&vars, "table_open_cache",
&table_cache_size);
static sys_var_long_ptr sys_table_lock_wait_timeout(&vars, "table_lock_wait_timeout",
&table_lock_wait_timeout);
+
+#if defined(ENABLED_DEBUG_SYNC)
+/* Debug Sync Facility. Implemented in debug_sync.cc. */
+static sys_var_debug_sync sys_debug_sync(&vars, "debug_sync");
+#endif /* defined(ENABLED_DEBUG_SYNC) */
+
static sys_var_long_ptr sys_thread_cache_size(&vars, "thread_cache_size",
&thread_cache_size);
#if HAVE_POOL_OF_THREADS == 1
@@ -1238,6 +1244,7 @@ void fix_slave_exec_mode(enum_var_type type)
}
if (bit_is_set(slave_exec_mode_options, SLAVE_EXEC_MODE_IDEMPOTENT) == 0)
bit_do_set(slave_exec_mode_options, SLAVE_EXEC_MODE_STRICT);
+ DBUG_VOID_RETURN;
}
@@ -3377,7 +3384,7 @@ int set_var_init()
uint count= 0;
DBUG_ENTER("set_var_init");
- for (sys_var *var=vars.first; var; var= var->next, count++);
+ for (sys_var *var=vars.first; var; var= var->next, count++) ;
if (hash_init(&system_variable_hash, system_charset_info, count, 0,
0, (hash_get_key) get_sys_var_length, 0, HASH_UNIQUE))
@@ -4212,10 +4219,10 @@ bool sys_var_opt_readonly::update(THD *thd, set_var *var)
can cause to wait on a read lock, it's required for the client application
to unlock everything, and acceptable for the server to wait on all locks.
*/
- if (result= close_cached_tables(thd, NULL, FALSE, TRUE, TRUE))
+ if ((result= close_cached_tables(thd, NULL, FALSE, TRUE, TRUE)))
goto end_with_read_lock;
- if (result= make_global_read_lock_block_commit(thd))
+ if ((result= make_global_read_lock_block_commit(thd)))
goto end_with_read_lock;
/* Change the opt_readonly system variable, safe because the lock is held */
@@ -4228,6 +4235,7 @@ end_with_read_lock:
}
+#ifndef DBUG_OFF
/* even session variable here requires SUPER, because of -#o,file */
bool sys_var_thd_dbug::check(THD *thd, set_var *var)
{
@@ -4254,6 +4262,8 @@ uchar *sys_var_thd_dbug::value_ptr(THD *thd, enum_var_type type, LEX_STRING *b)
DBUG_EXPLAIN(buf, sizeof(buf));
return (uchar*) thd->strdup(buf);
}
+#endif /* DBUG_OFF */
+
#ifdef HAVE_EVENT_SCHEDULER
bool sys_var_event_scheduler::check(THD *thd, set_var *var)
diff --git a/sql/set_var.h b/sql/set_var.h
index 0202a15836d..b5d90cae966 100644
--- a/sql/set_var.h
+++ b/sql/set_var.h
@@ -645,6 +645,7 @@ public:
uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
};
+#ifndef DBUG_OFF
class sys_var_thd_dbug :public sys_var_thd
{
public:
@@ -658,8 +659,23 @@ public:
void set_default(THD *thd, enum_var_type type) { DBUG_POP(); }
uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *b);
};
+#endif /* DBUG_OFF */
-
+#if defined(ENABLED_DEBUG_SYNC)
+/* Debug Sync Facility. Implemented in debug_sync.cc. */
+class sys_var_debug_sync :public sys_var_thd
+{
+public:
+ sys_var_debug_sync(sys_var_chain *chain, const char *name_arg)
+ :sys_var_thd(name_arg)
+ { chain_sys_var(chain); }
+ bool check(THD *thd, set_var *var);
+ bool update(THD *thd, set_var *var);
+ SHOW_TYPE show_type() { return SHOW_CHAR; }
+ bool check_update_type(Item_result type) { return type != STRING_RESULT; }
+ uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
+};
+#endif /* defined(ENABLED_DEBUG_SYNC) */
/* some variables that require special handling */
diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt
index 7afc73582ab..3f58826ed4e 100644
--- a/sql/share/errmsg.txt
+++ b/sql/share/errmsg.txt
@@ -4702,7 +4702,7 @@ ER_NOT_SUPPORTED_YET 42000
swe "Denna version av MySQL kan ännu inte utföra '%s'"
ER_MASTER_FATAL_ERROR_READING_BINLOG
nla "Kreeg fatale fout %d: '%-.128s' van master tijdens lezen van data uit binaire log"
- eng "Got fatal error %d: '%-.128s' from master when reading data from binary log"
+ eng "Got fatal error %d from master when reading data from binary log: '%-.128s'"
ger "Schwerer Fehler %d: '%-.128s vom Master beim Lesen des binären Logs"
ita "Errore fatale %d: '%-.128s' dal master leggendo i dati dal log binario"
por "Obteve fatal erro %d: '%-.128s' do master quando lendo dados do binary log"
@@ -6243,3 +6243,9 @@ ER_MESSAGE_AND_STATEMENT
ER_SLAVE_IGNORE_SERVER_IDS
eng "The requested server id %d clashes with the slave startup option --replicate-same-server-id"
+ER_DEBUG_SYNC_TIMEOUT
+ eng "debug sync point wait timed out"
+ ger "Debug Sync Point Wartezeit überschritten"
+ER_DEBUG_SYNC_HIT_LIMIT
+ eng "debug sync point hit limit reached"
+ ger "Debug Sync Point Hit Limit erreicht"
diff --git a/sql/slave.cc b/sql/slave.cc
index 36111ef2bc9..c35f0641729 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -1082,17 +1082,6 @@ err:
}
-static bool check_io_slave_killed(THD *thd, Master_info *mi, const char *info)
-{
- if (io_slave_killed(thd, mi))
- {
- if (info && global_system_variables.log_warnings)
- sql_print_information(info);
- return TRUE;
- }
- return FALSE;
-}
-
/*
Check if the error is caused by network.
@param[in] errorno Number of the error.
@@ -1459,7 +1448,7 @@ err:
if (master_res)
mysql_free_result(master_res);
DBUG_ASSERT(err_code != 0);
- mi->report(ERROR_LEVEL, err_code, err_buff);
+ mi->report(ERROR_LEVEL, err_code, "%s", err_buff);
DBUG_RETURN(1);
}
@@ -2402,8 +2391,7 @@ static int has_temporary_error(THD *thd)
@retval 2 No error calling ev->apply_event(), but error calling
ev->update_pos().
*/
-int apply_event_and_update_pos(Log_event* ev, THD* thd, Relay_log_info* rli,
- bool skip)
+int apply_event_and_update_pos(Log_event* ev, THD* thd, Relay_log_info* rli)
{
int exec_res= 0;
@@ -2448,37 +2436,33 @@ int apply_event_and_update_pos(Log_event* ev, THD* thd, Relay_log_info* rli,
ev->when= my_time(0);
ev->thd = thd; // because up to this point, ev->thd == 0
- if (skip)
- {
- int reason= ev->shall_skip(rli);
- if (reason == Log_event::EVENT_SKIP_COUNT)
- --rli->slave_skip_counter;
- pthread_mutex_unlock(&rli->data_lock);
- if (reason == Log_event::EVENT_SKIP_NOT)
- exec_res= ev->apply_event(rli);
+ int reason= ev->shall_skip(rli);
+ if (reason == Log_event::EVENT_SKIP_COUNT)
+ --rli->slave_skip_counter;
+ pthread_mutex_unlock(&rli->data_lock);
+ if (reason == Log_event::EVENT_SKIP_NOT)
+ exec_res= ev->apply_event(rli);
+
#ifndef DBUG_OFF
- /*
- This only prints information to the debug trace.
+ /*
+ This only prints information to the debug trace.
- TODO: Print an informational message to the error log?
- */
- static const char *const explain[] = {
- // EVENT_SKIP_NOT,
- "not skipped",
- // EVENT_SKIP_IGNORE,
- "skipped because event should be ignored",
- // EVENT_SKIP_COUNT
- "skipped because event skip counter was non-zero"
- };
- DBUG_PRINT("info", ("OPTION_BEGIN: %d; IN_STMT: %d",
- thd->options & OPTION_BEGIN ? 1 : 0,
- rli->get_flag(Relay_log_info::IN_STMT)));
- DBUG_PRINT("skip_event", ("%s event was %s",
- ev->get_type_str(), explain[reason]));
+ TODO: Print an informational message to the error log?
+ */
+ static const char *const explain[] = {
+ // EVENT_SKIP_NOT,
+ "not skipped",
+ // EVENT_SKIP_IGNORE,
+ "skipped because event should be ignored",
+ // EVENT_SKIP_COUNT
+ "skipped because event skip counter was non-zero"
+ };
+ DBUG_PRINT("info", ("OPTION_BEGIN: %d; IN_STMT: %d",
+ thd->options & OPTION_BEGIN ? 1 : 0,
+ rli->get_flag(Relay_log_info::IN_STMT)));
+ DBUG_PRINT("skip_event", ("%s event was %s",
+ ev->get_type_str(), explain[reason]));
#endif
- }
- else
- exec_res= ev->apply_event(rli);
DBUG_PRINT("info", ("apply_event error = %d", exec_res));
if (exec_res == 0)
@@ -2619,7 +2603,7 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli)
};);
}
- exec_res= apply_event_and_update_pos(ev, thd, rli, TRUE);
+ exec_res= apply_event_and_update_pos(ev, thd, rli);
/*
Format_description_log_event should not be deleted because it will be
@@ -2722,6 +2706,17 @@ on this slave.\
}
+static bool check_io_slave_killed(THD *thd, Master_info *mi, const char *info)
+{
+ if (io_slave_killed(thd, mi))
+ {
+ if (info && global_system_variables.log_warnings)
+ sql_print_information("%s", info);
+ return TRUE;
+ }
+ return FALSE;
+}
+
/**
@brief Try to reconnect slave IO thread.
@@ -2785,13 +2780,13 @@ static int try_to_reconnect(THD *thd, MYSQL *mysql, Master_info *mi,
}
else
{
- sql_print_information(buf);
+ sql_print_information("%s", buf);
}
}
if (safe_reconnect(thd, mysql, mi, 1) || io_slave_killed(thd, mi))
{
if (global_system_variables.log_warnings)
- sql_print_information(messages[SLAVE_RECON_MSG_KILLED_AFTER]);
+ sql_print_information("%s", messages[SLAVE_RECON_MSG_KILLED_AFTER]);
return 1;
}
return 0;
@@ -3018,15 +3013,19 @@ Log entry on master is longer than max_allowed_packet (%ld) on \
slave. If the entry is correct, restart the server with a higher value of \
max_allowed_packet",
thd->variables.max_allowed_packet);
+ mi->report(ERROR_LEVEL, ER_NET_PACKET_TOO_LARGE,
+ "%s", ER(ER_NET_PACKET_TOO_LARGE));
goto err;
case ER_MASTER_FATAL_ERROR_READING_BINLOG:
- sql_print_error(ER(mysql_error_number), mysql_error_number,
- mysql_error(mysql));
+ mi->report(ERROR_LEVEL, ER_MASTER_FATAL_ERROR_READING_BINLOG,
+ ER(ER_MASTER_FATAL_ERROR_READING_BINLOG),
+ mysql_error_number, mysql_error(mysql));
goto err;
- case EE_OUTOFMEMORY:
- case ER_OUTOFMEMORY:
+ case ER_OUT_OF_RESOURCES:
sql_print_error("\
Stopping slave I/O thread due to out-of-memory error from master");
+ mi->report(ERROR_LEVEL, ER_OUT_OF_RESOURCES,
+ "%s", ER(ER_OUT_OF_RESOURCES));
goto err;
}
if (try_to_reconnect(thd, mysql, mi, &retry_count, suppress_warnings,
@@ -3159,9 +3158,11 @@ err:
pthread_cond_broadcast(&mi->stop_cond); // tell the world we are done
DBUG_EXECUTE_IF("simulate_slave_delay_at_terminate_bug38694", sleep(5););
pthread_mutex_unlock(&mi->run_lock);
+
+ DBUG_LEAVE; // Must match DBUG_ENTER()
my_thread_end();
pthread_exit(0);
- DBUG_RETURN(0); // Can't return anything here
+ return 0; // Avoid compiler warnings
}
/*
@@ -3408,7 +3409,7 @@ log '%s' at position %s, relay log '%s' position: %s", RPL_LOG_NAME,
This function is reporting an error which was not reported
while executing exec_relay_log_event().
*/
- rli->report(ERROR_LEVEL, thd->main_da.sql_errno(), errmsg);
+ rli->report(ERROR_LEVEL, thd->main_da.sql_errno(), "%s", errmsg);
}
else if (last_errno != thd->main_da.sql_errno())
{
@@ -3517,10 +3518,11 @@ the slave SQL thread with \"SLAVE START\". We stopped at log \
pthread_cond_broadcast(&rli->stop_cond);
DBUG_EXECUTE_IF("simulate_slave_delay_at_terminate_bug38694", sleep(5););
pthread_mutex_unlock(&rli->run_lock); // tell the world we are done
-
+
+ DBUG_LEAVE; // Must match DBUG_ENTER()
my_thread_end();
pthread_exit(0);
- DBUG_RETURN(0); // Can't return anything here
+ return 0; // Avoid compiler warnings
}
@@ -4193,7 +4195,7 @@ extern "C" void slave_io_thread_detach_vio()
{
#ifdef SIGNAL_WITH_VIO_CLOSE
THD *thd= current_thd;
- if (thd->slave_thread)
+ if (thd && thd->slave_thread)
thd->clear_active_vio();
#endif
}
diff --git a/sql/slave.h b/sql/slave.h
index 48213316b98..eff0fa49f61 100644
--- a/sql/slave.h
+++ b/sql/slave.h
@@ -201,8 +201,7 @@ int purge_relay_logs(Relay_log_info* rli, THD *thd, bool just_reset,
void set_slave_thread_options(THD* thd);
void set_slave_thread_default_charset(THD *thd, Relay_log_info const *rli);
void rotate_relay_log(Master_info* mi);
-int apply_event_and_update_pos(Log_event* ev, THD* thd, Relay_log_info* rli,
- bool skip);
+int apply_event_and_update_pos(Log_event* ev, THD* thd, Relay_log_info* rli);
pthread_handler_t handle_slave_io(void *arg);
pthread_handler_t handle_slave_sql(void *arg);
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index 89ba9bc091b..b3a05e21d32 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -4072,8 +4072,7 @@ bool check_column_grant_in_table_ref(THD *thd, TABLE_LIST * table_ref,
db_name= table_ref->view_db.str;
table_name= table_ref->view_name.str;
if (table_ref->belong_to_view &&
- (thd->lex->sql_command == SQLCOM_SHOW_FIELDS ||
- thd->lex->sql_command == SQLCOM_SHOW_CREATE))
+ thd->lex->sql_command == SQLCOM_SHOW_FIELDS)
{
view_privs= get_column_grant(thd, grant, db_name, table_name, name);
if (view_privs & VIEW_ANY_ACL)
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 44d15d0ed65..fb49229bcca 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -17,6 +17,7 @@
/* Basic functions needed by many modules */
#include "mysql_priv.h"
+#include "debug_sync.h"
#include "sql_select.h"
#include "sp_head.h"
#include "sp.h"
@@ -105,7 +106,7 @@ static bool open_new_frm(THD *thd, TABLE_SHARE *share, const char *alias,
static void close_old_data_files(THD *thd, TABLE *table, bool morph_locks,
bool send_refresh);
static bool
-has_two_write_locked_tables_with_auto_increment(TABLE_LIST *tables);
+has_write_table_with_auto_increment(TABLE_LIST *tables);
extern "C" uchar *table_cache_key(const uchar *record, size_t *length,
@@ -949,6 +950,7 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables, bool have_lock,
close_old_data_files(thd,thd->open_tables,1,1);
mysql_ha_flush(thd);
+ DEBUG_SYNC(thd, "after_flush_unlock");
bool found=1;
/* Wait until all threads has closed all the tables we had locked */
@@ -2302,7 +2304,8 @@ bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list, bool link_in)
table->tablenr=thd->current_tablenr++;
table->used_fields=0;
table->const_table=0;
- table->null_row= table->maybe_null= table->force_index= 0;
+ table->null_row= table->maybe_null= 0;
+ table->force_index= table->force_index_order= table->force_index_group= 0;
table->status=STATUS_NO_RECORD;
DBUG_RETURN(FALSE);
}
@@ -2960,7 +2963,8 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
table->tablenr=thd->current_tablenr++;
table->used_fields=0;
table->const_table=0;
- table->null_row= table->maybe_null= table->force_index= 0;
+ table->null_row= table->maybe_null= 0;
+ table->force_index= table->force_index_order= table->force_index_group= 0;
table->status=STATUS_NO_RECORD;
table->insert_values= 0;
table->fulltext_searched= 0;
@@ -5126,18 +5130,23 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen)
thd->in_lock_tables=1;
thd->options|= OPTION_TABLE_LOCK;
/*
- If we have >= 2 different tables to update with auto_inc columns,
- statement-based binlogging won't work. We can solve this problem in
- mixed mode by switching to row-based binlogging:
+ A query that modifies autoinc column in sub-statement can make the
+ master and slave inconsistent.
+ We can solve these problems in mixed mode by switching to binlogging
+ if at least one updated table is used by sub-statement
*/
- if (thd->variables.binlog_format == BINLOG_FORMAT_MIXED &&
- has_two_write_locked_tables_with_auto_increment(tables))
+ /* The BINLOG_FORMAT_MIXED judgement is saved for suppressing
+ warnings, but it will be removed by fixing bug#45827 */
+ if (thd->variables.binlog_format == BINLOG_FORMAT_MIXED && tables &&
+ has_write_table_with_auto_increment(thd->lex->first_not_own_table()))
{
thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_TWO_AUTOINC_COLUMNS);
thd->set_current_stmt_binlog_format_row_if_mixed();
}
}
+ DEBUG_SYNC(thd, "before_lock_tables_takes_lock");
+
if (! (thd->lock= mysql_lock_tables(thd, start, (uint) (ptr - start),
lock_flag, need_reopen)))
{
@@ -8664,47 +8673,31 @@ void mysql_wait_completed_table(ALTER_PARTITION_PARAM_TYPE *lpt, TABLE *my_table
/*
- Tells if two (or more) tables have auto_increment columns and we want to
- lock those tables with a write lock.
+ Check if one (or more) write tables have auto_increment columns.
- SYNOPSIS
- has_two_write_locked_tables_with_auto_increment
- tables Table list
+ @param[in] tables Table list
+
+ @retval 0 if at least one write tables has an auto_increment column
+ @retval 1 otherwise
NOTES:
Call this function only when you have established the list of all tables
which you'll want to update (including stored functions, triggers, views
inside your statement).
-
- RETURN
- 0 No
- 1 Yes
*/
static bool
-has_two_write_locked_tables_with_auto_increment(TABLE_LIST *tables)
+has_write_table_with_auto_increment(TABLE_LIST *tables)
{
- char *first_table_name= NULL, *first_db;
- LINT_INIT(first_db);
-
for (TABLE_LIST *table= tables; table; table= table->next_global)
{
/* we must do preliminary checks as table->table may be NULL */
if (!table->placeholder() &&
table->table->found_next_number_field &&
(table->lock_type >= TL_WRITE_ALLOW_WRITE))
- {
- if (first_table_name == NULL)
- {
- first_table_name= table->table_name;
- first_db= table->db;
- DBUG_ASSERT(first_db);
- }
- else if (strcmp(first_db, table->db) ||
- strcmp(first_table_name, table->table_name))
- return 1;
- }
+ return 1;
}
+
return 0;
}
diff --git a/sql/sql_binlog.cc b/sql/sql_binlog.cc
index 531242f64d1..58c309ef57b 100644
--- a/sql/sql_binlog.cc
+++ b/sql/sql_binlog.cc
@@ -56,17 +56,20 @@ void mysql_client_binlog_statement(THD* thd)
Format_description_event.
*/
my_bool have_fd_event= TRUE;
- if (!thd->rli_fake)
+ int err;
+ Relay_log_info *rli;
+ rli= thd->rli_fake;
+ if (!rli)
{
- thd->rli_fake= new Relay_log_info(FALSE);
+ rli= thd->rli_fake= new Relay_log_info(FALSE);
#ifdef HAVE_purify
- thd->rli_fake->is_fake= TRUE;
+ rli->is_fake= TRUE;
#endif
have_fd_event= FALSE;
}
- if (thd->rli_fake && !thd->rli_fake->relay_log.description_event_for_exec)
+ if (rli && !rli->relay_log.description_event_for_exec)
{
- thd->rli_fake->relay_log.description_event_for_exec=
+ rli->relay_log.description_event_for_exec=
new Format_description_log_event(4);
have_fd_event= FALSE;
}
@@ -78,16 +81,16 @@ void mysql_client_binlog_statement(THD* thd)
/*
Out of memory check
*/
- if (!(thd->rli_fake &&
- thd->rli_fake->relay_log.description_event_for_exec &&
+ if (!(rli &&
+ rli->relay_log.description_event_for_exec &&
buf))
{
my_error(ER_OUTOFMEMORY, MYF(0), 1); /* needed 1 bytes */
goto end;
}
- thd->rli_fake->sql_thd= thd;
- thd->rli_fake->no_storage= TRUE;
+ rli->sql_thd= thd;
+ rli->no_storage= TRUE;
for (char const *strptr= thd->lex->comment.str ;
strptr < thd->lex->comment.str + thd->lex->comment.length ; )
@@ -170,8 +173,7 @@ void mysql_client_binlog_statement(THD* thd)
}
ev= Log_event::read_log_event(bufptr, event_len, &error,
- thd->rli_fake->relay_log.
- description_event_for_exec);
+ rli->relay_log.description_event_for_exec);
DBUG_PRINT("info",("binlog base64 err=%s", error));
if (!ev)
@@ -209,18 +211,10 @@ void mysql_client_binlog_statement(THD* thd)
reporting.
*/
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
- if (apply_event_and_update_pos(ev, thd, thd->rli_fake, FALSE))
- {
- delete ev;
- /*
- TODO: Maybe a better error message since the BINLOG statement
- now contains several events.
- */
- my_error(ER_UNKNOWN_ERROR, MYF(0), "Error executing BINLOG statement");
- goto end;
- }
+ err= ev->apply_event(rli);
+#else
+ err= 0;
#endif
-
/*
Format_description_log_event should not be deleted because it
will be used to read info about the relay log's format; it
@@ -228,8 +222,17 @@ void mysql_client_binlog_statement(THD* thd)
i.e. when this thread terminates.
*/
if (ev->get_type_code() != FORMAT_DESCRIPTION_EVENT)
- delete ev;
+ delete ev;
ev= 0;
+ if (err)
+ {
+ /*
+ TODO: Maybe a better error message since the BINLOG statement
+ now contains several events.
+ */
+ my_error(ER_UNKNOWN_ERROR, MYF(0), "Error executing BINLOG statement");
+ goto end;
+ }
}
}
@@ -238,7 +241,7 @@ void mysql_client_binlog_statement(THD* thd)
my_ok(thd);
end:
- thd->rli_fake->clear_tables_to_lock();
+ rli->clear_tables_to_lock();
my_free(buf, MYF(MY_ALLOW_ZERO_PTR));
DBUG_VOID_RETURN;
}
diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc
index df19fd05417..a41b7bd40bb 100644
--- a/sql/sql_cache.cc
+++ b/sql/sql_cache.cc
@@ -383,7 +383,7 @@ static void debug_wait_for_kill(const char *info)
thd= current_thd;
prev_info= thd->proc_info;
thd->proc_info= info;
- sql_print_information(info);
+ sql_print_information("%s", info);
while(!thd->killed)
my_sleep(1000);
thd->killed= THD::NOT_KILLED;
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 94d7b679c05..3583c1fa888 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -43,6 +43,7 @@
#include "sp_rcontext.h"
#include "sp_cache.h"
+#include "debug_sync.h"
/*
The following is used to initialise Table_ident with a internal
@@ -436,6 +437,31 @@ char *thd_security_context(THD *thd, char *buffer, unsigned int length,
return buffer;
}
+
+/**
+ Implementation of Drop_table_error_handler::handle_error().
+ The reason in having this implementation is to silence technical low-level
+ warnings during DROP TABLE operation. Currently we don't want to expose
+ the following warnings during DROP TABLE:
+ - Some of table files are missed or invalid (the table is going to be
+ deleted anyway, so why bother that something was missed);
+ - A trigger associated with the table does not have DEFINER (One of the
+ MySQL specifics now is that triggers are loaded for the table being
+ dropped. So, we may have a warning that trigger does not have DEFINER
+ attribute during DROP TABLE operation).
+
+ @return TRUE if the condition is handled.
+*/
+bool Drop_table_error_handler::handle_error(uint sql_errno,
+ const char *message,
+ MYSQL_ERROR::enum_warning_level level,
+ THD *thd)
+{
+ return ((sql_errno == EE_DELETE && my_errno == ENOENT) ||
+ sql_errno == ER_TRG_NO_DEFINER);
+}
+
+
/**
Clear this diagnostics area.
@@ -596,6 +622,9 @@ THD::THD()
derived_tables_processing(FALSE),
spcont(NULL),
m_parser_state(NULL)
+#if defined(ENABLED_DEBUG_SYNC)
+ , debug_sync_control(0)
+#endif /* defined(ENABLED_DEBUG_SYNC) */
{
ulong tmp;
@@ -842,6 +871,11 @@ void THD::init(void)
reset_current_stmt_binlog_format_row();
bzero((char *) &status_var, sizeof(status_var));
sql_log_bin_toplevel= options & OPTION_BIN_LOG;
+
+#if defined(ENABLED_DEBUG_SYNC)
+ /* Initialize the Debug Sync Facility. See debug_sync.cc. */
+ debug_sync_init_thread(this);
+#endif /* defined(ENABLED_DEBUG_SYNC) */
}
@@ -921,6 +955,12 @@ void THD::cleanup(void)
lock=locked_tables; locked_tables=0;
close_thread_tables(this);
}
+
+#if defined(ENABLED_DEBUG_SYNC)
+ /* End the Debug Sync Facility. See debug_sync.cc. */
+ debug_sync_end_thread(this);
+#endif /* defined(ENABLED_DEBUG_SYNC) */
+
mysql_ha_cleanup(this);
delete_dynamic(&user_var_events);
hash_free(&user_vars);
diff --git a/sql/sql_class.h b/sql/sql_class.h
index b6950108be4..2df753ac017 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -1096,6 +1096,31 @@ public:
/**
+ This class is an internal error handler implementation for
+ DROP TABLE statements. The thing is that there may be warnings during
+ execution of these statements, which should not be exposed to the user.
+ This class is intended to silence such warnings.
+*/
+
+class Drop_table_error_handler : public Internal_error_handler
+{
+public:
+ Drop_table_error_handler(Internal_error_handler *err_handler)
+ :m_err_handler(err_handler)
+ { }
+
+public:
+ bool handle_error(uint sql_errno,
+ const char *message,
+ MYSQL_ERROR::enum_warning_level level,
+ THD *thd);
+
+private:
+ Internal_error_handler *m_err_handler;
+};
+
+
+/**
Stores status of the currently executed statement.
Cleared at the beginning of the statement, and then
can hold either OK, ERROR, or EOF status.
@@ -1920,6 +1945,11 @@ public:
partition_info *work_part_info;
#endif
+#if defined(ENABLED_DEBUG_SYNC)
+ /* Debug Sync facility. See debug_sync.cc. */
+ struct st_debug_sync_control *debug_sync_control;
+#endif /* defined(ENABLED_DEBUG_SYNC) */
+
THD();
~THD();
@@ -2680,7 +2710,32 @@ public:
MI_COLUMNDEF *recinfo,*start_recinfo;
KEY *keyinfo;
ha_rows end_write_records;
- uint field_count,sum_func_count,func_count;
+ /**
+ Number of normal fields in the query, including those referred to
+ from aggregate functions. Hence, "SELECT `field1`,
+ SUM(`field2`) from t1" sets this counter to 2.
+
+ @see count_field_types
+ */
+ uint field_count;
+ /**
+ Number of fields in the query that have functions. Includes both
+ aggregate functions (e.g., SUM) and non-aggregates (e.g., RAND).
+ Also counts functions referred to from aggregate functions, i.e.,
+ "SELECT SUM(RAND())" sets this counter to 2.
+
+ @see count_field_types
+ */
+ uint func_count;
+ /**
+ Number of fields in the query that have aggregate functions. Note
+ that the optimizer may choose to optimize away these fields by
+ replacing them with constants, in which case sum_func_count will
+ need to be updated.
+
+ @see opt_sum_query, count_field_types
+ */
+ uint sum_func_count;
uint hidden_field_count;
uint group_parts,group_length,group_null_parts;
uint quick_group;
@@ -2945,7 +3000,8 @@ public:
bool send_data(List<Item> &items);
bool initialize_tables (JOIN *join);
void send_error(uint errcode,const char *err);
- int do_deletes();
+ int do_deletes();
+ int do_table_deletes(TABLE *table, bool ignore);
bool send_eof();
virtual void abort();
};
diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc
index 98574a07a4e..bb4ba2bf21b 100644
--- a/sql/sql_connect.cc
+++ b/sql/sql_connect.cc
@@ -985,7 +985,7 @@ static void end_connection(THD *thd)
if (thd->user_connect)
decrease_user_connections(thd->user_connect);
- if (thd->killed || net->error && net->vio != 0)
+ if (thd->killed || (net->error && net->vio != 0))
{
statistic_increment(aborted_threads,&LOCK_status);
}
diff --git a/sql/sql_db.cc b/sql/sql_db.cc
index 770f68f0282..dce867eb589 100644
--- a/sql/sql_db.cc
+++ b/sql/sql_db.cc
@@ -907,6 +907,9 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
remove_db_from_cache(db);
pthread_mutex_unlock(&LOCK_open);
+ Drop_table_error_handler err_handler(thd->get_internal_handler());
+ thd->push_internal_handler(&err_handler);
+
error= -1;
/*
We temporarily disable the binary log while dropping the objects
@@ -939,6 +942,7 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
error = 0;
reenable_binlog(thd);
}
+ thd->pop_internal_handler();
}
if (!silent && deleted>=0)
{
@@ -1446,11 +1450,11 @@ cmp_db_names(const char *db1_name,
{
return
/* db1 is NULL and db2 is NULL */
- !db1_name && !db2_name ||
+ (!db1_name && !db2_name) ||
/* db1 is not-NULL, db2 is not-NULL, db1 == db2. */
- db1_name && db2_name &&
- my_strcasecmp(system_charset_info, db1_name, db2_name) == 0;
+ (db1_name && db2_name &&
+ my_strcasecmp(system_charset_info, db1_name, db2_name) == 0);
}
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index c851ffdcc09..299b0690cd4 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -848,22 +848,19 @@ void multi_delete::abort()
-/*
+/**
Do delete from other tables.
- Returns values:
- 0 ok
- 1 error
+
+ @retval 0 ok
+ @retval 1 error
+
+ @todo Is there any reason not use the normal nested-loops join? If not, and
+ there is no documentation supporting it, this method and callee should be
+ removed and there should be hooks within normal execution.
*/
int multi_delete::do_deletes()
{
- int local_error= 0, counter= 0, tmp_error;
- bool will_batch;
- /*
- If the IGNORE option is used all non fatal errors will be translated
- to warnings and we should not break the row-by-row iteration
- */
- bool ignore= thd->lex->current_select->no_error;
DBUG_ENTER("do_deletes");
DBUG_ASSERT(do_delete);
@@ -874,79 +871,108 @@ int multi_delete::do_deletes()
table_being_deleted= (delete_while_scanning ? delete_tables->next_local :
delete_tables);
- for (; table_being_deleted;
+ for (uint counter= 0; table_being_deleted;
table_being_deleted= table_being_deleted->next_local, counter++)
{
- ha_rows last_deleted= deleted;
TABLE *table = table_being_deleted->table;
if (tempfiles[counter]->get(table))
+ DBUG_RETURN(1);
+
+ int local_error=
+ do_table_deletes(table, thd->lex->current_select->no_error);
+
+ if (thd->killed && !local_error)
+ DBUG_RETURN(1);
+
+ if (local_error == -1) // End of file
+ local_error = 0;
+
+ if (local_error)
+ DBUG_RETURN(local_error);
+ }
+ DBUG_RETURN(0);
+}
+
+
+/**
+ Implements the inner loop of nested-loops join within multi-DELETE
+ execution.
+
+ @param table The table from which to delete.
+
+ @param ignore If used, all non fatal errors will be translated
+ to warnings and we should not break the row-by-row iteration.
+
+ @return Status code
+
+ @retval 0 All ok.
+ @retval 1 Triggers or handler reported error.
+ @retval -1 End of file from handler.
+*/
+int multi_delete::do_table_deletes(TABLE *table, bool ignore)
+{
+ int local_error= 0;
+ READ_RECORD info;
+ ha_rows last_deleted= deleted;
+ DBUG_ENTER("do_deletes_for_table");
+ init_read_record(&info, thd, table, NULL, 0, 1, FALSE);
+ /*
+ Ignore any rows not found in reference tables as they may already have
+ been deleted by foreign key handling
+ */
+ info.ignore_not_found_rows= 1;
+ bool will_batch= !table->file->start_bulk_delete();
+ while (!(local_error= info.read_record(&info)) && !thd->killed)
+ {
+ if (table->triggers &&
+ table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
+ TRG_ACTION_BEFORE, FALSE))
{
- local_error=1;
+ local_error= 1;
break;
}
-
- READ_RECORD info;
- init_read_record(&info, thd, table, NULL, 0, 1, FALSE);
+
+ local_error= table->file->ha_delete_row(table->record[0]);
+ if (local_error && !ignore)
+ {
+ table->file->print_error(local_error, MYF(0));
+ break;
+ }
+
/*
- Ignore any rows not found in reference tables as they may already have
- been deleted by foreign key handling
+ Increase the reported number of deleted rows only if no error occurred
+ during ha_delete_row.
+ Also, don't execute the AFTER trigger if the row operation failed.
*/
- info.ignore_not_found_rows= 1;
- will_batch= !table->file->start_bulk_delete();
- while (!(local_error=info.read_record(&info)) && !thd->killed)
+ if (!local_error)
{
+ deleted++;
if (table->triggers &&
table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
- TRG_ACTION_BEFORE, FALSE))
+ TRG_ACTION_AFTER, FALSE))
{
local_error= 1;
break;
}
-
- local_error= table->file->ha_delete_row(table->record[0]);
- if (local_error && !ignore)
- {
- table->file->print_error(local_error,MYF(0));
- break;
- }
-
- /*
- Increase the reported number of deleted rows only if no error occurred
- during ha_delete_row.
- Also, don't execute the AFTER trigger if the row operation failed.
- */
- if (!local_error)
- {
- deleted++;
- if (table->triggers &&
- table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
- TRG_ACTION_AFTER, FALSE))
- {
- local_error= 1;
- break;
- }
- }
}
- if (will_batch && (tmp_error= table->file->end_bulk_delete()))
+ }
+ if (will_batch)
+ {
+ int tmp_error= table->file->end_bulk_delete();
+ if (tmp_error && !local_error)
{
- if (!local_error)
- {
- local_error= tmp_error;
- table->file->print_error(local_error,MYF(0));
- }
+ local_error= tmp_error;
+ table->file->print_error(local_error, MYF(0));
}
- if (last_deleted != deleted && !table->file->has_transactions())
- thd->transaction.stmt.modified_non_trans_table= TRUE;
- end_read_record(&info);
- if (thd->killed && !local_error)
- local_error= 1;
- if (local_error == -1) // End of file
- local_error = 0;
}
+ if (last_deleted != deleted && !table->file->has_transactions())
+ thd->transaction.stmt.modified_non_trans_table= TRUE;
+
+ end_read_record(&info);
+
DBUG_RETURN(local_error);
}
-
/*
Send ok to the client
diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc
index 1e92d95573a..3bbf4b78d07 100644
--- a/sql/sql_handler.cc
+++ b/sql/sql_handler.cc
@@ -422,16 +422,13 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables,
String buffer(buff, sizeof(buff), system_charset_info);
int error, keyno= -1;
uint num_rows;
- uchar *key;
- uint key_len;
+ uchar *UNINIT_VAR(key);
+ uint UNINIT_VAR(key_len);
bool need_reopen;
DBUG_ENTER("mysql_ha_read");
DBUG_PRINT("enter",("'%s'.'%s' as '%s'",
tables->db, tables->table_name, tables->alias));
- LINT_INIT(key);
- LINT_INIT(key_len);
-
thd->lex->select_lex.context.resolve_in_table_list_only(tables);
list.push_front(new Item_field(&thd->lex->select_lex.context,
NULL, NULL, "*"));
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index 4bb2045c06c..c495b28751a 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -2288,44 +2288,9 @@ void kill_delayed_threads(void)
}
-/*
- * Create a new delayed insert thread
-*/
-
-pthread_handler_t handle_delayed_insert(void *arg)
+static void handle_delayed_insert_impl(THD *thd, Delayed_insert *di)
{
- Delayed_insert *di=(Delayed_insert*) arg;
- THD *thd= &di->thd;
-
- pthread_detach_this_thread();
- /* Add thread to THD list so that's it's visible in 'show processlist' */
- pthread_mutex_lock(&LOCK_thread_count);
- thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
- thd->set_current_time();
- threads.append(thd);
- thd->killed=abort_loop ? THD::KILL_CONNECTION : THD::NOT_KILLED;
- pthread_mutex_unlock(&LOCK_thread_count);
-
- /*
- Wait until the client runs into pthread_cond_wait(),
- where we free it after the table is opened and di linked in the list.
- If we did not wait here, the client might detect the opened table
- before it is linked to the list. It would release LOCK_delayed_create
- and allow another thread to create another handler for the same table,
- since it does not find one in the list.
- */
- pthread_mutex_lock(&di->mutex);
-#if !defined( __WIN__) /* Win32 calls this in pthread_create */
- if (my_thread_init())
- {
- /* Can't use my_error since store_globals has not yet been called */
- thd->main_da.set_error_status(thd, ER_OUT_OF_RESOURCES,
- ER(ER_OUT_OF_RESOURCES));
- goto end;
- }
-#endif
-
- DBUG_ENTER("handle_delayed_insert");
+ DBUG_ENTER("handle_delayed_insert_impl");
thd->thread_stack= (char*) &thd;
if (init_thr_lock() || thd->store_globals())
{
@@ -2508,6 +2473,49 @@ err:
*/
ha_autocommit_or_rollback(thd, 1);
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ * Create a new delayed insert thread
+*/
+
+pthread_handler_t handle_delayed_insert(void *arg)
+{
+ Delayed_insert *di=(Delayed_insert*) arg;
+ THD *thd= &di->thd;
+
+ pthread_detach_this_thread();
+ /* Add thread to THD list so that's it's visible in 'show processlist' */
+ pthread_mutex_lock(&LOCK_thread_count);
+ thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
+ thd->set_current_time();
+ threads.append(thd);
+ thd->killed=abort_loop ? THD::KILL_CONNECTION : THD::NOT_KILLED;
+ pthread_mutex_unlock(&LOCK_thread_count);
+
+ /*
+ Wait until the client runs into pthread_cond_wait(),
+ where we free it after the table is opened and di linked in the list.
+ If we did not wait here, the client might detect the opened table
+ before it is linked to the list. It would release LOCK_delayed_create
+ and allow another thread to create another handler for the same table,
+ since it does not find one in the list.
+ */
+ pthread_mutex_lock(&di->mutex);
+#if !defined( __WIN__) /* Win32 calls this in pthread_create */
+ if (my_thread_init())
+ {
+ /* Can't use my_error since store_globals has not yet been called */
+ thd->main_da.set_error_status(thd, ER_OUT_OF_RESOURCES,
+ ER(ER_OUT_OF_RESOURCES));
+ goto end;
+ }
+#endif
+
+ handle_delayed_insert_impl(thd, di);
+
#ifndef __WIN__
end:
#endif
@@ -2531,7 +2539,8 @@ end:
my_thread_end();
pthread_exit(0);
- DBUG_RETURN(0);
+
+ return 0;
}
@@ -3597,7 +3606,7 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
DBUG_EXECUTE_IF("sleep_create_select_before_check_if_exists", my_sleep(6000000););
if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE) &&
- create_table->table->db_stat)
+ (create_table->table && create_table->table->db_stat))
{
/* Table already exists and was open at open_and_lock_tables() stage. */
if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 3e278f784a6..b826a6851b2 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -1892,13 +1892,6 @@ typedef struct st_lex : public Query_tables_list
const char *stmt_definition_end;
- /*
- Pointers to part of LOAD DATA statement that should be rewritten
- during replication ("LOCAL 'filename' REPLACE INTO" part).
- */
- const char *fname_start;
- const char *fname_end;
-
/**
During name resolution search only in the table list given by
Name_resolution_context::first_name_resolution_table and
diff --git a/sql/sql_load.cc b/sql/sql_load.cc
index 986af637931..1def63ead47 100644
--- a/sql/sql_load.cc
+++ b/sql/sql_load.cc
@@ -83,10 +83,13 @@ static int read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
String &enclosed, ulong skip_lines,
bool ignore_check_option_errors);
#ifndef EMBEDDED_LIBRARY
-static bool write_execute_load_query_log_event(THD *thd,
- bool duplicates, bool ignore,
- bool transactional_table,
- int errcode);
+static bool write_execute_load_query_log_event(THD *thd, sql_exchange* ex,
+ const char* db_arg,
+ const char* table_name_arg,
+ enum enum_duplicates duplicates,
+ bool ignore,
+ bool transactional_table,
+ int errocode);
#endif /* EMBEDDED_LIBRARY */
/*
@@ -497,8 +500,10 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
int errcode= query_error_code(thd, killed_status == THD::NOT_KILLED);
if (thd->transaction.stmt.modified_non_trans_table)
- write_execute_load_query_log_event(thd, handle_duplicates,
- ignore, transactional_table,
+ write_execute_load_query_log_event(thd, ex,
+ tdb, table_list->table_name,
+ handle_duplicates, ignore,
+ transactional_table,
errcode);
else
{
@@ -542,8 +547,11 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
if (lf_info.wrote_create_file)
{
int errcode= query_error_code(thd, killed_status == THD::NOT_KILLED);
- write_execute_load_query_log_event(thd, handle_duplicates, ignore,
- transactional_table, errcode);
+ write_execute_load_query_log_event(thd, ex,
+ tdb, table_list->table_name,
+ handle_duplicates, ignore,
+ transactional_table,
+ errcode);
}
}
}
@@ -564,15 +572,95 @@ err:
#ifndef EMBEDDED_LIBRARY
/* Not a very useful function; just to avoid duplication of code */
-static bool write_execute_load_query_log_event(THD *thd,
- bool duplicates, bool ignore,
- bool transactional_table,
+static bool write_execute_load_query_log_event(THD *thd, sql_exchange* ex,
+ const char* db_arg,
+ const char* table_name_arg,
+ enum enum_duplicates duplicates,
+ bool ignore,
+ bool transactional_table,
int errcode)
{
+ char *load_data_query,
+ *end,
+ *fname_start,
+ *fname_end,
+ *p= NULL;
+ size_t pl= 0;
+ List<Item> fv;
+ Item *item, *val;
+ String pfield, pfields;
+ int n;
+
+ Load_log_event lle(thd, ex, db_arg, table_name_arg, fv, 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));
+
+ /*
+ prepare fields-list and SET if needed; print_query won't do that for us.
+ */
+ if (!thd->lex->field_list.is_empty())
+ {
+ List_iterator<Item> li(thd->lex->field_list);
+
+ pfields.append(" (");
+ n= 0;
+
+ while ((item= li++))
+ {
+ if (n++)
+ pfields.append(", ");
+ if (item->name)
+ pfields.append(item->name);
+ else
+ item->print(&pfields, QT_ORDINARY);
+ }
+ pfields.append(")");
+ }
+
+ if (!thd->lex->update_list.is_empty())
+ {
+ List_iterator<Item> lu(thd->lex->update_list);
+ List_iterator<Item> lv(thd->lex->value_list);
+
+ pfields.append(" SET ");
+ n= 0;
+
+ while ((item= lu++))
+ {
+ val= lv++;
+ if (n++)
+ pfields.append(", ");
+ pfields.append(item->name);
+ pfields.append("=");
+ val->print(&pfields, QT_ORDINARY);
+ }
+ }
+
+ p= pfields.c_ptr_safe();
+ pl= strlen(p);
+
+ if (!(load_data_query= (char *)thd->alloc(lle.get_query_buffer_length() + 1 + pl)))
+ 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;
+
+ thd->query_length= end - load_data_query;
+ thd->query= load_data_query;
+
Execute_load_query_log_event
e(thd, thd->query, thd->query_length,
- (uint) ((char*)thd->lex->fname_start - (char*)thd->query),
- (uint) ((char*)thd->lex->fname_end - (char*)thd->query),
+ (uint) ((char*)fname_start - (char*)thd->query - 1),
+ (uint) ((char*)fname_end - (char*)thd->query),
(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 6eb2b1879c1..fc41407fc18 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -409,29 +409,12 @@ void execute_init_command(THD *thd, sys_var_str *init_command_var,
}
-/**
- Execute commands from bootstrap_file.
-
- Used when creating the initial grant tables.
-*/
-
-pthread_handler_t handle_bootstrap(void *arg)
+static void handle_bootstrap_impl(THD *thd)
{
- THD *thd=(THD*) arg;
FILE *file=bootstrap_file;
char *buff;
const char* found_semicolon= NULL;
- /* The following must be called before DBUG_ENTER */
- thd->thread_stack= (char*) &thd;
- if (my_thread_init() || thd->store_globals())
- {
-#ifndef EMBEDDED_LIBRARY
- close_connection(thd, ER_OUT_OF_RESOURCES, 1);
-#endif
- thd->fatal_error();
- goto end;
- }
DBUG_ENTER("handle_bootstrap");
#ifndef EMBEDDED_LIBRARY
@@ -526,6 +509,33 @@ pthread_handler_t handle_bootstrap(void *arg)
#endif
}
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Execute commands from bootstrap_file.
+
+ Used when creating the initial grant tables.
+*/
+
+pthread_handler_t handle_bootstrap(void *arg)
+{
+ THD *thd=(THD*) arg;
+
+ /* The following must be called before DBUG_ENTER */
+ thd->thread_stack= (char*) &thd;
+ if (my_thread_init() || thd->store_globals())
+ {
+#ifndef EMBEDDED_LIBRARY
+ close_connection(thd, ER_OUT_OF_RESOURCES, 1);
+#endif
+ thd->fatal_error();
+ goto end;
+ }
+
+ handle_bootstrap_impl(thd);
+
end:
net_end(&thd->net);
thd->cleanup();
@@ -540,7 +550,8 @@ end:
my_thread_end();
pthread_exit(0);
#endif
- DBUG_RETURN(0);
+
+ return 0;
}
@@ -1418,7 +1429,28 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
if (check_global_access(thd,RELOAD_ACL))
break;
general_log_print(thd, command, NullS);
- if (!reload_acl_and_cache(thd, options, (TABLE_LIST*) 0, &not_used))
+#ifndef DBUG_OFF
+ bool debug_simulate= FALSE;
+ DBUG_EXECUTE_IF("simulate_detached_thread_refresh", debug_simulate= TRUE;);
+ if (debug_simulate)
+ {
+ /*
+ Simulate a reload without a attached thread session.
+ Provides a environment similar to that of when the
+ server receives a SIGHUP signal and reloads caches
+ and flushes tables.
+ */
+ bool res;
+ my_pthread_setspecific_ptr(THR_THD, NULL);
+ res= reload_acl_and_cache(NULL, options | REFRESH_FAST,
+ NULL, &not_used);
+ my_pthread_setspecific_ptr(THR_THD, thd);
+ if (!res)
+ my_ok(thd);
+ break;
+ }
+#endif
+ if (!reload_acl_and_cache(thd, options, NULL, &not_used))
my_ok(thd);
break;
}
@@ -6230,6 +6262,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
ptr->table_name_length=table->table.length;
ptr->lock_type= lock_type;
ptr->updating= test(table_options & TL_OPTION_UPDATING);
+ /* TODO: remove TL_OPTION_FORCE_INDEX as it looks like it's not used */
ptr->force_index= test(table_options & TL_OPTION_FORCE_INDEX);
ptr->ignore_leaves= test(table_options & TL_OPTION_IGNORE_LEAVES);
ptr->derived= table->sel;
diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc
index a4c0f02e480..a7b35caeb8a 100644
--- a/sql/sql_partition.cc
+++ b/sql/sql_partition.cc
@@ -7102,9 +7102,9 @@ static uint32 get_next_partition_via_walking(PARTITION_ITERATOR *part_iter)
longlong dummy;
field->store(part_iter->field_vals.cur++,
((Field_num*)field)->unsigned_flag);
- if (part_iter->part_info->is_sub_partitioned() &&
+ if ((part_iter->part_info->is_sub_partitioned() &&
!part_iter->part_info->get_part_partition_id(part_iter->part_info,
- &part_id, &dummy) ||
+ &part_id, &dummy)) ||
!part_iter->part_info->get_partition_id(part_iter->part_info,
&part_id, &dummy))
return part_id;
diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc
index 8cf8c4cb81f..5500e44d1b2 100644
--- a/sql/sql_plugin.cc
+++ b/sql/sql_plugin.cc
@@ -363,7 +363,7 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report)
if (report & REPORT_TO_USER)
my_error(ER_UDF_NO_PATHS, MYF(0));
if (report & REPORT_TO_LOG)
- sql_print_error(ER(ER_UDF_NO_PATHS));
+ sql_print_error("%s", ER(ER_UDF_NO_PATHS));
DBUG_RETURN(0);
}
/* If this dll is already loaded just increase ref_count. */
@@ -1515,7 +1515,7 @@ error:
void plugin_shutdown(void)
{
- uint i, count= plugin_array.elements, free_slots= 0;
+ uint i, count= plugin_array.elements;
struct st_plugin_int **plugins, *plugin;
struct st_plugin_dl **dl;
DBUG_ENTER("plugin_shutdown");
@@ -1536,18 +1536,13 @@ void plugin_shutdown(void)
while (reap_needed && (count= plugin_array.elements))
{
reap_plugins();
- for (i= free_slots= 0; i < count; i++)
+ for (i= 0; i < count; i++)
{
plugin= *dynamic_element(&plugin_array, i, struct st_plugin_int **);
- switch (plugin->state) {
- case PLUGIN_IS_READY:
+ if (plugin->state == PLUGIN_IS_READY)
+ {
plugin->state= PLUGIN_IS_DELETED;
reap_needed= true;
- break;
- case PLUGIN_IS_FREED:
- case PLUGIN_IS_UNINITIALIZED:
- free_slots++;
- break;
}
}
if (!reap_needed)
@@ -1560,9 +1555,6 @@ void plugin_shutdown(void)
}
}
- if (count > free_slots)
- sql_print_warning("Forcing shutdown of %d plugins", count - free_slots);
-
plugins= (struct st_plugin_int **) my_alloca(sizeof(void*) * (count+1));
/*
@@ -1584,8 +1576,8 @@ void plugin_shutdown(void)
if (!(plugins[i]->state & (PLUGIN_IS_UNINITIALIZED | PLUGIN_IS_FREED |
PLUGIN_IS_DISABLED)))
{
- sql_print_information("Plugin '%s' will be forced to shutdown",
- plugins[i]->name.str);
+ sql_print_warning("Plugin '%s' will be forced to shutdown",
+ plugins[i]->name.str);
/*
We are forcing deinit on plugins so we don't want to do a ref_count
check until we have processed all the plugins.
diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc
index 5c25d789d5c..0d709520fbe 100644
--- a/sql/sql_repl.cc
+++ b/sql/sql_repl.cc
@@ -776,7 +776,7 @@ impossible position";
*/
{
log.error=0;
- bool read_packet = 0, fatal_error = 0;
+ bool read_packet = 0;
#ifndef DBUG_OFF
if (max_binlog_dump_events && !left_events--)
@@ -803,7 +803,7 @@ impossible position";
*/
pthread_mutex_lock(log_lock);
- switch (Log_event::read_log_event(&log, packet, (pthread_mutex_t*)0)) {
+ switch (error= Log_event::read_log_event(&log, packet, (pthread_mutex_t*) 0)) {
case 0:
/* we read successfully, so we'll need to send it to the slave */
pthread_mutex_unlock(log_lock);
@@ -872,8 +872,8 @@ impossible position";
default:
pthread_mutex_unlock(log_lock);
- fatal_error = 1;
- break;
+ test_for_non_eof_log_read_errors(error, &errmsg);
+ goto err;
}
if (read_packet)
@@ -913,12 +913,6 @@ impossible position";
}
}
- if (fatal_error)
- {
- errmsg = "error reading log entry";
- my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG;
- goto err;
- }
log.error=0;
}
}
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 1a6dc7f04c8..c5e0bce498d 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -644,8 +644,11 @@ JOIN::prepare(Item ***rref_pointer_array,
this->group= group_list != 0;
unit= unit_arg;
+ if (tmp_table_param.sum_func_count && !group_list)
+ implicit_grouping= TRUE;
+
#ifdef RESTRICTED_GROUP
- if (sum_func_count && !group_list && (func_count || field_count))
+ if (implicit_grouping)
{
my_message(ER_WRONG_SUM_SELECT,ER(ER_WRONG_SUM_SELECT),MYF(0));
goto err;
@@ -881,15 +884,23 @@ JOIN::optimize()
}
#endif
- /* Optimize count(*), min() and max() */
- if (tables_list && tmp_table_param.sum_func_count && ! group_list)
+ /*
+ Try to optimize count(*), min() and max() to const fields if
+ there is implicit grouping (aggregate functions but no
+ group_list). In this case, the result set shall only contain one
+ row.
+ */
+ if (tables_list && implicit_grouping)
{
int res;
/*
opt_sum_query() returns HA_ERR_KEY_NOT_FOUND if no rows match
to the WHERE conditions,
- or 1 if all items were resolved,
+ or 1 if all items were resolved (optimized away),
or 0, or an error number HA_ERR_...
+
+ If all items were resolved by opt_sum_query, there is no need to
+ open any tables.
*/
if ((res=opt_sum_query(select_lex->leaf_tables, all_fields, conds)))
{
@@ -1231,13 +1242,22 @@ JOIN::optimize()
(!group_list && tmp_table_param.sum_func_count))
order=0;
- // Can't use sort on head table if using row cache
+ // Can't use sort on head table if using join buffering
if (full_join)
{
- if (group_list)
- simple_group=0;
- if (order)
- simple_order=0;
+ TABLE *stable= (sort_by_table == (TABLE *) 1 ?
+ join_tab[const_tables].table : sort_by_table);
+ /*
+ FORCE INDEX FOR ORDER BY can be used to prevent join buffering when
+ sorting on the first table.
+ */
+ if (!stable || !stable->force_index_order)
+ {
+ if (group_list)
+ simple_group= 0;
+ if (order)
+ simple_order= 0;
+ }
}
/*
@@ -1523,12 +1543,8 @@ JOIN::optimize()
}
}
- /*
- If this join belongs to an uncacheable subquery save
- the original join
- */
- if (select_lex->uncacheable && !is_top_level_join() &&
- init_save_join_tab())
+ /* If this join belongs to an uncacheable query save the original join */
+ if (select_lex->uncacheable && init_save_join_tab())
DBUG_RETURN(-1); /* purecov: inspected */
}
@@ -2019,7 +2035,8 @@ JOIN::exec()
count_field_types(select_lex, &curr_join->tmp_table_param,
*curr_all_fields, 0);
- if (curr_join->group || curr_join->tmp_table_param.sum_func_count ||
+ if (curr_join->group || curr_join->implicit_grouping ||
+ curr_join->tmp_table_param.sum_func_count ||
(procedure && (procedure->flags & PROC_GROUP)))
{
if (make_group_fields(this, curr_join))
@@ -2252,7 +2269,7 @@ JOIN::destroy()
tab->cleanup();
}
tmp_join->tmp_join= 0;
- tmp_table_param.copy_field=0;
+ tmp_table_param.copy_field= 0;
DBUG_RETURN(tmp_join->destroy());
}
cond_equal= 0;
@@ -3312,12 +3329,12 @@ add_key_equal_fields(KEY_FIELD **key_fields, uint and_level,
@retval FALSE it's something else
*/
-inline static bool
+static bool
is_local_field (Item *field)
{
- field= field->real_item();
- return field->type() == Item::FIELD_ITEM &&
- !((Item_field *)field)->depended_from;
+ return field->real_item()->type() == Item::FIELD_ITEM
+ && !(field->used_tables() & OUTER_REF_TABLE_BIT)
+ && !((Item_field *)field->real_item())->depended_from;
}
@@ -10806,6 +10823,12 @@ Next_select_func setup_end_select_func(JOIN *join)
}
else
{
+ /*
+ Choose method for presenting result to user. Use end_send_group
+ if the query requires grouping (has a GROUP BY clause and/or one or
+ more aggregate functions). Use end_send if the query should not
+ be grouped.
+ */
if ((join->sort_and_group ||
(join->procedure && join->procedure->flags & PROC_GROUP)) &&
!tmp_tbl->precomputed_group_by)
@@ -13682,7 +13705,10 @@ static int remove_dup_with_compare(THD *thd, TABLE *table, Field **first_field,
if (error)
{
if (error == HA_ERR_RECORD_DELETED)
- continue;
+ {
+ error= file->rnd_next(record);
+ continue;
+ }
if (error == HA_ERR_END_OF_FILE)
break;
goto err;
diff --git a/sql/sql_select.h b/sql/sql_select.h
index 12cfebfc374..0b9aa3576c7 100644
--- a/sql/sql_select.h
+++ b/sql/sql_select.h
@@ -281,7 +281,14 @@ public:
TABLE **table,**all_tables,*sort_by_table;
uint tables,const_tables;
uint send_group_parts;
- bool sort_and_group,first_record,full_join,group, no_field_update;
+ /**
+ Indicates that grouping will be performed on the result set during
+ query execution. This field belongs to query execution.
+
+ @see make_group_fields, alloc_group_fields, JOIN::exec
+ */
+ bool sort_and_group;
+ bool first_record,full_join,group, no_field_update;
bool do_send_rows;
/**
TRUE when we want to resume nested loop iterations when
@@ -360,6 +367,8 @@ public:
simple_xxxxx is set if ORDER/GROUP BY doesn't include any references
to other tables than the first non-constant table in the JOIN.
It's also set if ORDER/GROUP BY is empty.
+ Used for deciding for or against using a temporary table to compute
+ GROUP/ORDER BY.
*/
bool simple_order, simple_group;
/**
@@ -429,6 +438,7 @@ public:
tables= 0;
const_tables= 0;
join_list= 0;
+ implicit_grouping= FALSE;
sort_and_group= 0;
first_record= 0;
do_send_rows= 1;
@@ -534,6 +544,11 @@ public:
select_lex == unit->fake_select_lex));
}
private:
+ /**
+ TRUE if the query contains an aggregate function but has no GROUP
+ BY clause.
+ */
+ bool implicit_grouping;
bool make_simple_join(JOIN *join, TABLE *tmp_table);
};
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 5c2c351652b..75c9ea6c524 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -581,6 +581,126 @@ find_files(THD *thd, List<LEX_STRING> *files, const char *db,
}
+/**
+ An Internal_error_handler that suppresses errors regarding views'
+ underlying tables that occur during privilege checking within SHOW CREATE
+ VIEW commands. This happens in the cases when
+
+ - A view's underlying table (e.g. referenced in its SELECT list) does not
+ exist. There should not be an error as no attempt was made to access it
+ per se.
+
+ - Access is denied for some table, column, function or stored procedure
+ such as mentioned above. This error gets raised automatically, since we
+ can't untangle its access checking from that of the view itself.
+ */
+class Show_create_error_handler : public Internal_error_handler {
+
+ TABLE_LIST *m_top_view;
+ bool m_handling;
+ Security_context *m_sctx;
+
+ char m_view_access_denied_message[MYSQL_ERRMSG_SIZE];
+ char *m_view_access_denied_message_ptr;
+
+public:
+
+ /**
+ Creates a new Show_create_error_handler for the particular security
+ context and view.
+
+ @thd Thread context, used for security context information if needed.
+ @top_view The view. We do not verify at this point that top_view is in
+ fact a view since, alas, these things do not stay constant.
+ */
+ explicit Show_create_error_handler(THD *thd, TABLE_LIST *top_view) :
+ m_top_view(top_view), m_handling(FALSE),
+ m_view_access_denied_message_ptr(NULL)
+ {
+
+ m_sctx = test(m_top_view->security_ctx) ?
+ m_top_view->security_ctx : thd->security_ctx;
+ }
+
+ /**
+ Lazy instantiation of 'view access denied' message. The purpose of the
+ Show_create_error_handler is to hide details of underlying tables for
+ which we have no privileges behind ER_VIEW_INVALID messages. But this
+ obviously does not apply if we lack privileges on the view itself.
+ Unfortunately the information about for which table privilege checking
+ failed is not available at this point. The only way for us to check is by
+ reconstructing the actual error message and see if it's the same.
+ */
+ char* get_view_access_denied_message()
+ {
+ if (!m_view_access_denied_message_ptr)
+ {
+ m_view_access_denied_message_ptr= m_view_access_denied_message;
+ my_snprintf(m_view_access_denied_message, MYSQL_ERRMSG_SIZE,
+ ER(ER_TABLEACCESS_DENIED_ERROR), "SHOW VIEW",
+ m_sctx->priv_user,
+ m_sctx->host_or_ip, m_top_view->get_table_name());
+ }
+ return m_view_access_denied_message_ptr;
+ }
+
+ bool handle_error(uint sql_errno, const char *message,
+ MYSQL_ERROR::enum_warning_level level, THD *thd) {
+ /*
+ The handler does not handle the errors raised by itself.
+ At this point we know if top_view is really a view.
+ */
+ if (m_handling || !m_top_view->view)
+ return FALSE;
+
+ m_handling= TRUE;
+
+ bool is_handled;
+
+ switch (sql_errno)
+ {
+ case ER_TABLEACCESS_DENIED_ERROR:
+ if (!strcmp(get_view_access_denied_message(), message))
+ {
+ /* Access to top view is not granted, don't interfere. */
+ is_handled= FALSE;
+ break;
+ }
+ case ER_COLUMNACCESS_DENIED_ERROR:
+ case ER_VIEW_NO_EXPLAIN: /* Error was anonymized, ignore all the same. */
+ case ER_PROCACCESS_DENIED_ERROR:
+ is_handled= TRUE;
+ break;
+
+ case ER_NO_SUCH_TABLE:
+ /* Established behavior: warn if underlying tables are missing. */
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_VIEW_INVALID,
+ ER(ER_VIEW_INVALID),
+ m_top_view->get_db_name(),
+ m_top_view->get_table_name());
+ is_handled= TRUE;
+ break;
+
+ case ER_SP_DOES_NOT_EXIST:
+ /* Established behavior: warn if underlying functions are missing. */
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_VIEW_INVALID,
+ ER(ER_VIEW_INVALID),
+ m_top_view->get_db_name(),
+ m_top_view->get_table_name());
+ is_handled= TRUE;
+ break;
+ default:
+ is_handled= FALSE;
+ }
+
+ m_handling= FALSE;
+ return is_handled;
+ }
+};
+
+
bool
mysqld_show_create(THD *thd, TABLE_LIST *table_list)
{
@@ -594,26 +714,13 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list)
/* We want to preserve the tree for views. */
thd->lex->view_prepare_mode= TRUE;
- /* Only one table for now, but VIEW can involve several tables */
- if (open_normal_and_derived_tables(thd, table_list, 0))
{
- if (!table_list->view ||
- (thd->is_error() && thd->main_da.sql_errno() != ER_VIEW_INVALID))
+ Show_create_error_handler view_error_suppressor(thd, table_list);
+ thd->push_internal_handler(&view_error_suppressor);
+ bool error= open_normal_and_derived_tables(thd, table_list, 0);
+ thd->pop_internal_handler();
+ if (error && thd->main_da.is_error())
DBUG_RETURN(TRUE);
-
- /*
- Clear all messages with 'error' level status and
- issue a warning with 'warning' level status in
- case of invalid view and last error is ER_VIEW_INVALID
- */
- mysql_reset_errors(thd, true);
- thd->clear_error();
-
- push_warning_printf(thd,MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_VIEW_INVALID,
- ER(ER_VIEW_INVALID),
- table_list->view_db.str,
- table_list->view_name.str);
}
/* TODO: add environment variables show when it become possible */
@@ -1873,7 +1980,7 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond)
tmp->mysys_var->current_cond ?
"Waiting on cond" : NullS);
#else
- val= (char *) "Writing to net";
+ val= (char *) (tmp->proc_info ? tmp->proc_info : NullS);
#endif
if (val)
{
@@ -3529,7 +3636,9 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables,
TABLE_SHARE *share= show_table->s;
handler *file= show_table->file;
handlerton *tmp_db_type= share->db_type();
+#ifdef WITH_PARTITION_STORAGE_ENGINE
bool is_partitioned= FALSE;
+#endif
if (share->tmp_table == SYSTEM_TMP_TABLE)
table->field[3]->store(STRING_WITH_LEN("SYSTEM VIEW"), cs);
else if (share->tmp_table)
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 1d6c5ba48b4..07839bf95a7 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -70,15 +70,21 @@ static void wait_for_kill_signal(THD *thd)
/**
@brief Helper function for explain_filename
+ @param thd Thread handle
+ @param to_p Explained name in system_charset_info
+ @param end_p End of the to_p buffer
+ @param name Name to be converted
+ @param name_len Length of the name, in bytes
*/
-static char* add_identifier(char *to_p, const char * end_p,
- const char* name, uint name_len, bool add_quotes)
+static char* add_identifier(THD* thd, char *to_p, const char * end_p,
+ const char* name, uint name_len)
{
uint res;
uint errors;
const char *conv_name;
char tmp_name[FN_REFLEN];
char conv_string[FN_REFLEN];
+ int quote;
DBUG_ENTER("add_identifier");
if (!name[name_len])
@@ -102,19 +108,21 @@ static char* add_identifier(char *to_p, const char * end_p,
conv_name= conv_string;
}
- if (add_quotes && (end_p - to_p > 2))
+ quote = thd ? get_quote_char_for_identifier(thd, conv_name, res - 1) : '"';
+
+ if (quote != EOF && (end_p - to_p > 2))
{
- *(to_p++)= '`';
+ *(to_p++)= (char) quote;
while (*conv_name && (end_p - to_p - 1) > 0)
{
uint length= my_mbcharlen(system_charset_info, *conv_name);
if (!length)
length= 1;
- if (length == 1 && *conv_name == '`')
+ if (length == 1 && *conv_name == (char) quote)
{
if ((end_p - to_p) < 3)
break;
- *(to_p++)= '`';
+ *(to_p++)= (char) quote;
*(to_p++)= *(conv_name++);
}
else if (((long) length) < (end_p - to_p))
@@ -125,7 +133,11 @@ static char* add_identifier(char *to_p, const char * end_p,
else
break; /* string already filled */
}
- to_p= strnmov(to_p, "`", end_p - to_p);
+ if (end_p > to_p) {
+ *(to_p++)= (char) quote;
+ if (end_p > to_p)
+ *to_p= 0; /* terminate by NUL, but do not include it in the count */
+ }
}
else
to_p= strnmov(to_p, conv_name, end_p - to_p);
@@ -145,6 +157,7 @@ static char* add_identifier(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.
+ @param thd Thread handle
@param from Path name in my_charset_filename
Null terminated in my_charset_filename, normalized
to use '/' as directory separation character.
@@ -161,13 +174,12 @@ static char* add_identifier(char *to_p, const char * end_p,
[,[ Temporary| Renamed] Partition `p`
[, Subpartition `sp`]] *|
(| is really a /, and it is all in one line)
- EXPLAIN_PARTITIONS_AS_COMMENT_NO_QUOTING ->
- same as above but no quotes are added.
@retval Length of returned string
*/
-uint explain_filename(const char *from,
+uint explain_filename(THD* thd,
+ const char *from,
char *to,
uint to_length,
enum_explain_filename_mode explain_mode)
@@ -281,14 +293,12 @@ uint explain_filename(const char *from,
{
to_p= strnmov(to_p, ER(ER_DATABASE_NAME), end_p - to_p);
*(to_p++)= ' ';
- to_p= add_identifier(to_p, end_p, db_name, db_name_len, 1);
+ to_p= add_identifier(thd, to_p, end_p, db_name, db_name_len);
to_p= strnmov(to_p, ", ", end_p - to_p);
}
else
{
- to_p= add_identifier(to_p, end_p, db_name, db_name_len,
- (explain_mode !=
- EXPLAIN_PARTITIONS_AS_COMMENT_NO_QUOTING));
+ to_p= add_identifier(thd, to_p, end_p, db_name, db_name_len);
to_p= strnmov(to_p, ".", end_p - to_p);
}
}
@@ -296,16 +306,13 @@ uint explain_filename(const char *from,
{
to_p= strnmov(to_p, ER(ER_TABLE_NAME), end_p - to_p);
*(to_p++)= ' ';
- to_p= add_identifier(to_p, end_p, table_name, table_name_len, 1);
+ to_p= add_identifier(thd, to_p, end_p, table_name, table_name_len);
}
else
- to_p= add_identifier(to_p, end_p, table_name, table_name_len,
- (explain_mode !=
- EXPLAIN_PARTITIONS_AS_COMMENT_NO_QUOTING));
+ to_p= add_identifier(thd, to_p, end_p, table_name, table_name_len);
if (part_name)
{
- if (explain_mode == EXPLAIN_PARTITIONS_AS_COMMENT ||
- explain_mode == EXPLAIN_PARTITIONS_AS_COMMENT_NO_QUOTING)
+ if (explain_mode == EXPLAIN_PARTITIONS_AS_COMMENT)
to_p= strnmov(to_p, " /* ", end_p - to_p);
else if (explain_mode == EXPLAIN_PARTITIONS_VERBOSE)
to_p= strnmov(to_p, " ", end_p - to_p);
@@ -321,20 +328,15 @@ uint explain_filename(const char *from,
}
to_p= strnmov(to_p, ER(ER_PARTITION_NAME), end_p - to_p);
*(to_p++)= ' ';
- to_p= add_identifier(to_p, end_p, part_name, part_name_len,
- (explain_mode !=
- EXPLAIN_PARTITIONS_AS_COMMENT_NO_QUOTING));
+ to_p= add_identifier(thd, to_p, end_p, part_name, part_name_len);
if (subpart_name)
{
to_p= strnmov(to_p, ", ", end_p - to_p);
to_p= strnmov(to_p, ER(ER_SUBPARTITION_NAME), end_p - to_p);
*(to_p++)= ' ';
- to_p= add_identifier(to_p, end_p, subpart_name, subpart_name_len,
- (explain_mode !=
- EXPLAIN_PARTITIONS_AS_COMMENT_NO_QUOTING));
+ to_p= add_identifier(thd, to_p, end_p, subpart_name, subpart_name_len);
}
- if (explain_mode == EXPLAIN_PARTITIONS_AS_COMMENT ||
- explain_mode == EXPLAIN_PARTITIONS_AS_COMMENT_NO_QUOTING)
+ if (explain_mode == EXPLAIN_PARTITIONS_AS_COMMENT)
to_p= strnmov(to_p, " */", end_p - to_p);
}
DBUG_PRINT("exit", ("to '%s'", to));
@@ -1773,6 +1775,7 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
my_bool drop_temporary)
{
bool error= FALSE, need_start_waiters= FALSE;
+ Drop_table_error_handler err_handler(thd->get_internal_handler());
DBUG_ENTER("mysql_rm_table");
/* mark for close and remove all cached entries */
@@ -1793,7 +1796,10 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
LOCK_open during wait_if_global_read_lock(), other threads could not
close their tables. This would make a pretty deadlock.
*/
+ thd->push_internal_handler(&err_handler);
error= mysql_rm_table_part2(thd, tables, if_exists, drop_temporary, 0, 0);
+ thd->pop_internal_handler();
+
if (need_start_waiters)
start_waiting_global_read_lock(thd);
@@ -1895,9 +1901,6 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
DBUG_RETURN(1);
}
- /* Don't give warnings for not found errors, as we already generate notes */
- thd->no_warnings_for_error= 1;
-
for (table= tables; table; table= table->next_local)
{
char *db=table->db;
@@ -1949,7 +1952,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
being built. The string always end in a comma and the comma
will be chopped off before being written to the binary log.
*/
- if (thd->is_current_stmt_binlog_format_row() && !dont_log_query)
+ if (!drop_temporary && thd->is_current_stmt_binlog_format_row() && !dont_log_query)
{
non_temp_tables_count++;
/*
@@ -2147,7 +2150,6 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
err_with_placeholders:
unlock_table_names(thd, tables, (TABLE_LIST*) 0);
pthread_mutex_unlock(&LOCK_open);
- thd->no_warnings_for_error= 0;
DBUG_RETURN(error);
}
@@ -5219,6 +5221,7 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table,
char tmp_path[FN_REFLEN];
#endif
char ts_name[FN_LEN + 1];
+ myf flags= MY_DONT_OVERWRITE_FILE;
DBUG_ENTER("mysql_create_like_table");
@@ -5275,8 +5278,12 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table,
DBUG_EXECUTE_IF("sleep_create_like_before_copy", my_sleep(6000000););
+ if (opt_sync_frm && !(create_info->options & HA_LEX_CREATE_TMP_TABLE))
+ flags|= MY_SYNC;
+
/*
Create a new table by copying from source table
+ and sync the new table if the flag MY_SYNC is set
Altough exclusive name-lock on target table protects us from concurrent
DML and DDL operations on it we still want to wrap .FRM creation and call
@@ -5297,7 +5304,7 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table,
goto err;
}
}
- else if (my_copy(src_path, dst_path, MYF(MY_DONT_OVERWRITE_FILE)))
+ else if (my_copy(src_path, dst_path, flags))
{
if (my_errno == ENOENT)
my_error(ER_BAD_DB_ERROR,MYF(0),db);
@@ -6653,9 +6660,19 @@ view_err:
goto err;
}
+ /*
+ If this is an ALTER TABLE and no explicit row type specified reuse
+ the table's row type.
+ Note : this is the same as if the row type was specified explicitly.
+ */
if (create_info->row_type == ROW_TYPE_NOT_USED)
{
+ /* ALTER TABLE without explicit row type */
create_info->row_type= table->s->row_type;
+ }
+ else
+ {
+ /* ALTER TABLE with specific row type */
create_info->used_fields |= HA_CREATE_USED_ROW_FORMAT;
}
@@ -7884,8 +7901,14 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
for (uint i= 0; i < t->s->fields; i++ )
{
Field *f= t->field[i];
- if ((f->type() == MYSQL_TYPE_BLOB) ||
- (f->type() == MYSQL_TYPE_VARCHAR))
+ enum_field_types field_type= f->type();
+ /*
+ BLOB and VARCHAR have pointers in their field, we must convert
+ to string; GEOMETRY is implemented on top of BLOB.
+ */
+ if ((field_type == MYSQL_TYPE_BLOB) ||
+ (field_type == MYSQL_TYPE_VARCHAR) ||
+ (field_type == MYSQL_TYPE_GEOMETRY))
{
String tmp;
f->val_str(&tmp);
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 3be3f804678..8cb5f279a6b 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -7988,7 +7988,13 @@ udf_expr:
$2->is_autogenerated_name= FALSE;
$2->set_name($4.str, $4.length, system_charset_info);
}
- else
+ /*
+ A field has to have its proper name in order for name
+ resolution to work, something we are only guaranteed if we
+ parse it out. If we hijack the input stream with
+ remember_name we may get quoted or escaped names.
+ */
+ else if ($2->type() != Item::FIELD_ITEM)
$2->set_name($1, (uint) ($3 - $1), YYTHD->charset());
$$= $2;
}
@@ -10503,14 +10509,12 @@ load:
{
THD *thd= YYTHD;
LEX *lex= thd->lex;
- Lex_input_stream *lip= YYLIP;
if (lex->sphead)
{
my_error(ER_SP_BADSTATEMENT, MYF(0), "LOAD DATA");
MYSQL_YYABORT;
}
- lex->fname_start= lip->get_ptr();
}
load_data
{}
@@ -10542,14 +10546,10 @@ load_data:
if (!(lex->exchange= new sql_exchange($4.str, 0)))
MYSQL_YYABORT;
}
- opt_duplicate INTO
- {
- Lex->fname_end= YYLIP->get_ptr();
- }
- TABLE_SYM table_ident
+ opt_duplicate INTO TABLE_SYM table_ident
{
LEX *lex=Lex;
- if (!Select->add_table_to_list(YYTHD, $10, NULL, TL_OPTION_UPDATING,
+ if (!Select->add_table_to_list(YYTHD, $9, NULL, TL_OPTION_UPDATING,
lex->lock_option))
MYSQL_YYABORT;
lex->field_list.empty();
@@ -10557,7 +10557,7 @@ load_data:
lex->value_list.empty();
}
opt_load_data_charset
- { Lex->exchange->cs= $12; }
+ { Lex->exchange->cs= $11; }
opt_field_term opt_line_term opt_ignore_lines opt_field_or_var_spec
opt_load_data_set_spec
{}
diff --git a/sql/strfunc.cc b/sql/strfunc.cc
index 1fb9c1a8451..56fa4a380ea 100644
--- a/sql/strfunc.cc
+++ b/sql/strfunc.cc
@@ -147,7 +147,7 @@ static uint parse_name(TYPELIB *lib, const char **strpos, const char *end,
}
}
else
- for (; pos != end && *pos != '=' && *pos !=',' ; pos++);
+ for (; pos != end && *pos != '=' && *pos !=',' ; pos++) ;
uint var_len= (uint) (pos - start);
/* Determine which flag it is */
diff --git a/sql/table.cc b/sql/table.cc
index 4f647618979..d2538eb4d59 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -3338,7 +3338,12 @@ bool TABLE_LIST::prep_check_option(THD *thd, uint8 check_opt_type)
/**
- Hide errors which show view underlying table information
+ Hide errors which show view underlying table information.
+ 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
+ handles SELECT from views. The two methods should not clash.
@param[in,out] thd thread handler
@@ -3347,6 +3352,8 @@ bool TABLE_LIST::prep_check_option(THD *thd, uint8 check_opt_type)
void TABLE_LIST::hide_view_error(THD *thd)
{
+ if (thd->get_internal_handler())
+ return;
/* Hide "Unknown column" or "Unknown function" error */
DBUG_ASSERT(thd->is_error());
@@ -4630,7 +4637,8 @@ Item_subselect *TABLE_LIST::containing_subselect()
(TABLE_LIST::index_hints). Using the information in this tagged list
this function sets the members st_table::keys_in_use_for_query,
st_table::keys_in_use_for_group_by, st_table::keys_in_use_for_order_by,
- st_table::force_index and st_table::covering_keys.
+ st_table::force_index, st_table::force_index_order,
+ st_table::force_index_group and st_table::covering_keys.
Current implementation of the runtime does not allow mixing FORCE INDEX
and USE INDEX, so this is checked here. Then the FORCE INDEX list
@@ -4758,14 +4766,28 @@ bool TABLE_LIST::process_index_hints(TABLE *tbl)
}
/* process FORCE INDEX as USE INDEX with a flag */
+ if (!index_order[INDEX_HINT_FORCE].is_clear_all())
+ {
+ tbl->force_index_order= TRUE;
+ index_order[INDEX_HINT_USE].merge(index_order[INDEX_HINT_FORCE]);
+ }
+
+ if (!index_group[INDEX_HINT_FORCE].is_clear_all())
+ {
+ tbl->force_index_group= TRUE;
+ index_group[INDEX_HINT_USE].merge(index_group[INDEX_HINT_FORCE]);
+ }
+
+ /*
+ TODO: get rid of tbl->force_index (on if any FORCE INDEX is specified) and
+ create tbl->force_index_join instead.
+ Then use the correct force_index_XX instead of the global one.
+ */
if (!index_join[INDEX_HINT_FORCE].is_clear_all() ||
- !index_order[INDEX_HINT_FORCE].is_clear_all() ||
- !index_group[INDEX_HINT_FORCE].is_clear_all())
+ tbl->force_index_group || tbl->force_index_order)
{
tbl->force_index= TRUE;
index_join[INDEX_HINT_USE].merge(index_join[INDEX_HINT_FORCE]);
- index_order[INDEX_HINT_USE].merge(index_order[INDEX_HINT_FORCE]);
- index_group[INDEX_HINT_USE].merge(index_group[INDEX_HINT_FORCE]);
}
/* apply USE INDEX */
diff --git a/sql/table.h b/sql/table.h
index 7c43ab339e5..f9da0637b4b 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -755,6 +755,18 @@ struct st_table {
bytes, it would take up 4.
*/
my_bool force_index;
+
+ /**
+ Flag set when the statement contains FORCE INDEX FOR ORDER BY
+ See TABLE_LIST::process_index_hints().
+ */
+ my_bool force_index_order;
+
+ /**
+ Flag set when the statement contains FORCE INDEX FOR GROUP BY
+ See TABLE_LIST::process_index_hints().
+ */
+ my_bool force_index_group;
my_bool distinct,const_table,no_rows;
/**
diff --git a/sql/udf_example.c b/sql/udf_example.c
index 30d85d95034..82af58ec502 100644
--- a/sql/udf_example.c
+++ b/sql/udf_example.c
@@ -139,10 +139,10 @@ typedef long long longlong;
#include <mysql.h>
#include <ctype.h>
-static pthread_mutex_t LOCK_hostname;
-
#ifdef HAVE_DLOPEN
+static pthread_mutex_t LOCK_hostname;
+
/* These must be right or mysqld will not find the symbol! */
my_bool metaphon_init(UDF_INIT *initid, UDF_ARGS *args, char *message);
diff --git a/sql/unireg.cc b/sql/unireg.cc
index 68a352e4a44..60674b8390b 100644
--- a/sql/unireg.cc
+++ b/sql/unireg.cc
@@ -412,10 +412,10 @@ int rea_create_table(THD *thd, const char *path,
DBUG_ASSERT(*fn_rext(frm_name));
if (thd->variables.keep_files_on_create)
create_info->options|= HA_CREATE_KEEP_FILES;
- if (file->ha_create_handler_files(path, NULL, CHF_CREATE_FLAG, create_info))
- goto err_handler;
- if (!create_info->frm_only && ha_create_table(thd, path, db, table_name,
- create_info,0))
+ if (!create_info->frm_only &&
+ (file->ha_create_handler_files(path, NULL, CHF_CREATE_FLAG,
+ create_info) ||
+ ha_create_table(thd, path, db, table_name, create_info, 0)))
goto err_handler;
DBUG_RETURN(0);