From aaf2bdde94c582086e4ced9f9ce84b9a276d36aa Mon Sep 17 00:00:00 2001 From: Mats Kindahl Date: Mon, 7 Jun 2010 16:01:39 +0200 Subject: [PATCH 01/28] WL#5363: Thread Pool Service Interface In order to allow thread schedulers to be dynamically loaded, it is necessary to make the following changes to the server: - Two new service interfaces - Modifications to InnoDB to inform the thread scheduler of state changes. - Changes to the VIO subsystem for checking if data is available on a socket. - Elimination of remains of the old thread pool implementation. The two new service interfaces introduces are: my_thread_scheduler A service interface to register a thread scheduler. thd_wait A service interface to inform thread scheduler that the thread is about to start waiting. In addition, the patch adds code that: - Add a call to thd_wait for table locks in mysys thd_lock.c by introducing a set function that can be used to set a callback to be used when waiting on a lock and resuming from waiting. - Calling the mysys set function from the server to set the callbacks correctly. --- .bzr-mysql/default.conf | 4 +- include/Makefile.am | 2 + include/config-win.h | 7 - include/mysql/plugin.h | 2 +- include/mysql/plugin.h.pp | 21 +++ include/mysql/service_thd_wait.h | 83 +++++++++++ include/mysql/service_thread_scheduler.h | 65 +++++++++ include/mysql/services.h | 2 + include/service_versions.h | 3 +- include/thr_lock.h | 2 + include/violite.h | 1 + libservices/CMakeLists.txt | 6 +- libservices/Makefile.am | 4 +- libservices/my_thread_scheduler_service.c | 21 +++ libservices/thd_wait_service.c | 19 +++ mysys/my_init.c | 2 +- mysys/thr_lock.c | 31 ++++ sql/authors.h | 1 + sql/mysqld.cc | 51 +++---- sql/mysqld.h | 3 +- sql/scheduler.cc | 164 +++++++++++++++++----- sql/scheduler.h | 61 ++++++-- sql/sql_callback.h | 43 ++++++ sql/sql_class.cc | 75 +++++++++- sql/sql_class.h | 7 +- sql/sql_connect.cc | 15 +- sql/sql_connect.h | 4 + sql/sql_plugin_services.h | 16 ++- sql/sql_show.cc | 6 +- sql/sys_vars.cc | 19 +-- storage/innobase/buf/buf0flu.c | 4 + storage/innobase/buf/buf0rea.c | 4 + storage/innobase/srv/srv0srv.c | 6 + vio/vio.c | 10 ++ vio/vio_priv.h | 3 + vio/viosocket.c | 4 + vio/viossl.c | 5 +- 37 files changed, 647 insertions(+), 129 deletions(-) create mode 100644 include/mysql/service_thd_wait.h create mode 100644 include/mysql/service_thread_scheduler.h create mode 100644 libservices/my_thread_scheduler_service.c create mode 100644 libservices/thd_wait_service.c create mode 100644 sql/sql_callback.h diff --git a/.bzr-mysql/default.conf b/.bzr-mysql/default.conf index fcb3cab2de6..144adb1d68b 100644 --- a/.bzr-mysql/default.conf +++ b/.bzr-mysql/default.conf @@ -1,4 +1,4 @@ [MYSQL] -post_commit_to = "commits@lists.mysql.com" -post_push_to = "commits@lists.mysql.com" +# post_commit_to = "commits@lists.mysql.com" +# post_push_to = "commits@lists.mysql.com" tree_name = "mysql-trunk" diff --git a/include/Makefile.am b/include/Makefile.am index 5ede6d7591f..6f53ba8e296 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -24,6 +24,8 @@ HEADERS_ABI = mysql.h mysql_com.h mysql_time.h \ pkginclude_HEADERS = $(HEADERS_ABI) my_dbug.h m_string.h my_sys.h \ my_xml.h mysql_embed.h mysql/services.h \ mysql/service_my_snprintf.h mysql/service_thd_alloc.h \ + mysql/service_thread_scheduler.h \ + mysql/service_thd_wait.h \ my_pthread.h my_no_pthread.h \ mysql/psi/psi.h mysql/psi/mysql_thread.h \ mysql/psi/mysql_file.h \ diff --git a/include/config-win.h b/include/config-win.h index 269ec0e925a..70983c91fc3 100644 --- a/include/config-win.h +++ b/include/config-win.h @@ -20,13 +20,6 @@ #define BIG_TABLES -/* - Minimal version of Windows we should be able to run on. - Currently Windows XP. -*/ -#define _WIN32_WINNT 0x0501 - - #if defined(_MSC_VER) && _MSC_VER >= 1400 /* Avoid endless warnings about sprintf() etc. being unsafe. */ #define _CRT_SECURE_NO_DEPRECATE 1 diff --git a/include/mysql/plugin.h b/include/mysql/plugin.h index 567734c1d5c..ced6983eda0 100644 --- a/include/mysql/plugin.h +++ b/include/mysql/plugin.h @@ -71,7 +71,7 @@ typedef struct st_mysql_xid MYSQL_XID; Plugin API. Common for all plugin types. */ -#define MYSQL_PLUGIN_INTERFACE_VERSION 0x0101 +#define MYSQL_PLUGIN_INTERFACE_VERSION 0x0102 /* The allowable types of plugins diff --git a/include/mysql/plugin.h.pp b/include/mysql/plugin.h.pp index 5dad31bd008..6b7a8f860ed 100644 --- a/include/mysql/plugin.h.pp +++ b/include/mysql/plugin.h.pp @@ -33,6 +33,27 @@ void *thd_memdup(void* thd, const void* str, unsigned int size); MYSQL_LEX_STRING *thd_make_lex_string(void* thd, MYSQL_LEX_STRING *lex_str, const char *str, unsigned int size, int allocate_lex_string); +#include +typedef enum _thd_wait_type_e { + THD_WAIT_MUTEX= 1, + THD_WAIT_DISKIO= 2, + THD_WAIT_ROW_TABLE_LOCK= 3, + THD_WAIT_GLOBAL_LOCK= 4 +} thd_wait_type; +extern struct thd_wait_service_st { + void (*thd_wait_begin_func)(void*, thd_wait_type); + void (*thd_wait_end_func)(void*); +} *thd_wait_service; +void thd_wait_begin(void* thd, thd_wait_type wait_type); +void thd_wait_end(void* thd); +#include +struct scheduler_functions; +extern struct my_thread_scheduler_service { + int (*set)(struct scheduler_functions *scheduler); + int (*reset)(); +} *my_thread_scheduler_service; +int my_thread_scheduler_set(struct scheduler_functions *scheduler); +int my_thread_scheduler_reset(); struct st_mysql_xid { long formatID; long gtrid_length; diff --git a/include/mysql/service_thd_wait.h b/include/mysql/service_thd_wait.h new file mode 100644 index 00000000000..2a8f5e610a3 --- /dev/null +++ b/include/mysql/service_thd_wait.h @@ -0,0 +1,83 @@ +/* Copyright (C) 2010, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifndef MYSQL_SERVICE_THD_WAIT_INCLUDED +#define MYSQL_SERVICE_THD_WAIT_INCLUDED + +/** + @file include/mysql/service_thd_wait.h + This service provides functions for plugins and storage engines to report + when they are going to sleep/stall. + + SYNOPSIS + thd_wait_begin() - call just before a wait begins + thd Thread object + Use NULL if the thd is NOT known. + wait_type Type of wait + 1 -- short wait (e.g. for mutex) + 2 -- medium wait (e.g. for disk io) + 3 -- large wait (e.g. for locked row/table) + NOTES + This is used by the threadpool to have better knowledge of which + threads that currently are actively running on CPUs. When a thread + reports that it's going to sleep/stall, the threadpool scheduler is + free to start another thread in the pool most likely. The expected wait + time is simply an indication of how long the wait is expected to + become, the real wait time could be very different. + + thd_wait_end() called immediately after the wait is complete + + thd_wait_end() MUST be called if thd_wait_begin() was called. + + Using thd_wait_...() service is optional but recommended. Using it will + improve performance as the thread pool will be more active at managing the + thread workload. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum _thd_wait_type_e { + THD_WAIT_MUTEX= 1, + THD_WAIT_DISKIO= 2, + THD_WAIT_ROW_TABLE_LOCK= 3, + THD_WAIT_GLOBAL_LOCK= 4 +} thd_wait_type; + +extern struct thd_wait_service_st { + void (*thd_wait_begin_func)(MYSQL_THD, thd_wait_type); + void (*thd_wait_end_func)(MYSQL_THD); +} *thd_wait_service; + +#ifdef MYSQL_DYNAMIC_PLUGIN + +#define thd_wait_begin(_THD, _WAIT_TYPE) \ + thd_wait_service->thd_wait_begin_func(_THD, _WAIT_TYPE) +#define thd_wait_end(_THD) thd_wait_service->thd_wait_end_func(_THD) + +#else + +void thd_wait_begin(MYSQL_THD thd, thd_wait_type wait_type); +void thd_wait_end(MYSQL_THD thd); + +#endif + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/include/mysql/service_thread_scheduler.h b/include/mysql/service_thread_scheduler.h new file mode 100644 index 00000000000..a4396b721bd --- /dev/null +++ b/include/mysql/service_thread_scheduler.h @@ -0,0 +1,65 @@ +/* + Copyright (C) 2010, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef SERVICE_THREAD_SCHEDULER_INCLUDED +#define SERVICE_THREAD_SCHEDULER_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +struct scheduler_functions; + +extern struct my_thread_scheduler_service { + int (*set)(struct scheduler_functions *scheduler); + int (*reset)(); +} *my_thread_scheduler_service; + +#ifdef MYSQL_DYNAMIC_PLUGIN + +#define my_thread_scheduler_set(F) my_thread_scheduler_service->set((F)) +#define my_thread_scheduler_reset() my_thread_scheduler_service->reset() + +#else + +/** + Set the thread scheduler to use for the server. + + @param scheduler Pointer to scheduler callbacks to use. + @retval 0 Scheduler installed correctly. + @retval 1 Invalid value (NULL) used for scheduler. +*/ +int my_thread_scheduler_set(struct scheduler_functions *scheduler); + +/** + Restore the previous thread scheduler. + + @note If no thread scheduler was installed previously with + thd_set_thread_scheduler, this function will report an error. + + @retval 0 Scheduler installed correctly. + @retval 1 No scheduler installed. +*/ +int my_thread_scheduler_reset(); + +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* SERVICE_THREAD_SCHEDULER_INCLUDED */ diff --git a/include/mysql/services.h b/include/mysql/services.h index 19003e66b96..6c67a582fb8 100644 --- a/include/mysql/services.h +++ b/include/mysql/services.h @@ -20,6 +20,8 @@ extern "C" { #include #include +#include +#include #ifdef __cplusplus } diff --git a/include/service_versions.h b/include/service_versions.h index 114957cdd86..4fd886c8a83 100644 --- a/include/service_versions.h +++ b/include/service_versions.h @@ -21,4 +21,5 @@ #define VERSION_my_snprintf 0x0100 #define VERSION_thd_alloc 0x0100 - +#define VERSION_thd_wait 0x0100 +#define VERSION_my_thread_scheduler 0x0100 diff --git a/include/thr_lock.h b/include/thr_lock.h index 1f4072ca0c5..32fe353bdb1 100644 --- a/include/thr_lock.h +++ b/include/thr_lock.h @@ -174,6 +174,8 @@ void thr_downgrade_write_lock(THR_LOCK_DATA *data, enum thr_lock_type new_lock_type); my_bool thr_reschedule_write_lock(THR_LOCK_DATA *data, ulong lock_wait_timeout); +void thr_set_lock_wait_callback(void (*before_wait)(void), + void (*after_wait)(void)); #ifdef __cplusplus } #endif diff --git a/include/violite.h b/include/violite.h index 05ceaa272c1..f4083216894 100644 --- a/include/violite.h +++ b/include/violite.h @@ -217,6 +217,7 @@ struct st_vio void (*timeout)(Vio*, unsigned int which, unsigned int timeout); my_bool (*poll_read)(Vio *vio, uint timeout); my_bool (*is_connected)(Vio*); + my_bool (*has_data) (Vio*); #ifdef HAVE_OPENSSL void *ssl_arg; #endif diff --git a/libservices/CMakeLists.txt b/libservices/CMakeLists.txt index da84368b46c..113d15a18e8 100644 --- a/libservices/CMakeLists.txt +++ b/libservices/CMakeLists.txt @@ -15,7 +15,11 @@ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include) -SET(MYSQLSERVICES_SOURCES my_snprintf_service.c thd_alloc_service.c) +SET(MYSQLSERVICES_SOURCES + my_snprintf_service.c + thd_alloc_service.c + thd_wait_service.c + my_thread_scheduler_service.c) ADD_LIBRARY(mysqlservices ${MYSQLSERVICES_SOURCES}) INSTALL(TARGETS mysqlservices DESTINATION ${INSTALL_LIBDIR}) diff --git a/libservices/Makefile.am b/libservices/Makefile.am index 642081859c1..d25c5c9680c 100644 --- a/libservices/Makefile.am +++ b/libservices/Makefile.am @@ -15,5 +15,7 @@ AM_CPPFLAGS = -I$(top_srcdir)/include pkglib_LIBRARIES = libmysqlservices.a -libmysqlservices_a_SOURCES = my_snprintf_service.c thd_alloc_service.c +libmysqlservices_a_SOURCES = my_snprintf_service.c thd_alloc_service.c \ + thd_wait_service.c \ + my_thread_scheduler_service.c EXTRA_DIST = CMakeLists.txt diff --git a/libservices/my_thread_scheduler_service.c b/libservices/my_thread_scheduler_service.c new file mode 100644 index 00000000000..dc8d40c6713 --- /dev/null +++ b/libservices/my_thread_scheduler_service.c @@ -0,0 +1,21 @@ +/* + Copyright (C) 2010, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA +*/ + +#include +SERVICE_VERSION my_thread_scheduler_service= + (void*)VERSION_my_thread_scheduler; diff --git a/libservices/thd_wait_service.c b/libservices/thd_wait_service.c new file mode 100644 index 00000000000..3c87a2d0d1b --- /dev/null +++ b/libservices/thd_wait_service.c @@ -0,0 +1,19 @@ +/* + Copyright (C) 2010, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +SERVICE_VERSION *thd_wait_service= (void*)VERSION_thd_wait; diff --git a/mysys/my_init.c b/mysys/my_init.c index 80f9a493bb0..f6a005dfdbd 100644 --- a/mysys/my_init.c +++ b/mysys/my_init.c @@ -499,7 +499,7 @@ static my_bool win32_init_tcp_ip() { if (win32_have_tcpip()) { - WORD wVersionRequested = MAKEWORD( 2, 0 ); + WORD wVersionRequested = MAKEWORD( 2, 2 ); WSADATA wsaData; /* Be a good citizen: maybe another lib has already initialised sockets, so dont clobber them unless necessary */ diff --git a/mysys/thr_lock.c b/mysys/thr_lock.c index 43db0470735..085606843df 100644 --- a/mysys/thr_lock.c +++ b/mysys/thr_lock.c @@ -93,6 +93,16 @@ enum thr_lock_type thr_upgraded_concurrent_insert_lock = TL_WRITE; LIST *thr_lock_thread_list; /* List of threads in use */ ulong max_write_lock_count= ~(ulong) 0L; +static void (*before_lock_wait)(void)= 0; +static void (*after_lock_wait)(void)= 0; + +void thr_set_lock_wait_callback(void (*before_wait)(void), + void (*after_wait)(void)) +{ + before_lock_wait= before_wait; + after_lock_wait= after_wait; +} + static inline mysql_cond_t *get_cond(void) { return &my_thread_var->suspend; @@ -436,6 +446,19 @@ wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data, old_proc_info= proc_info_hook(NULL, "Table lock", __func__, __FILE__, __LINE__); + /* + Since before_lock_wait potentially can create more threads to + scheduler work for, we don't want to call the before_lock_wait + callback unless it will really start to wait. + + For similar reasons, we do not want to call before_lock_wait and + after_lock_wait for each lap around the loop, so we restrict + ourselves to call it before_lock_wait once before starting to wait + and once after the thread has exited the wait loop. + */ + if ((!thread_var->abort || in_wait_list) && before_lock_wait) + (*before_lock_wait)(); + set_timespec(wait_timeout, lock_wait_timeout); while (!thread_var->abort || in_wait_list) { @@ -467,6 +490,14 @@ wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data, /* purecov: end */ } } + + /* + We call the after_lock_wait callback once the wait loop has + finished. + */ + if (after_lock_wait) + (*after_lock_wait)(); + DBUG_PRINT("thr_lock", ("aborted: %d in_wait_list: %d", thread_var->abort, in_wait_list)); diff --git a/sql/authors.h b/sql/authors.h index 555fe2ae43a..210141b5e22 100644 --- a/sql/authors.h +++ b/sql/authors.h @@ -92,6 +92,7 @@ struct show_table_authors_st show_table_authors[]= { { "Arjen Lentz", "Brisbane, Australia", "Documentation (2001-2004), Dutch error messages, LOG2()" }, { "Marc Liyanage", "", "Created Mac OS X packages" }, + { "Kelly Long", "Denver, CO, USA", "Pool Of Threads" }, { "Zarko Mocnik", "", "Sorting for Slovenian language" }, { "Per-Erik Martin", "Uppsala, Sweden", "Stored Procedures (5.0)" }, { "Alexis Mikhailov", "", "User-defined functions" }, diff --git a/sql/mysqld.cc b/sql/mysqld.cc index b35e2545a18..a61f2ccbc23 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -64,7 +64,9 @@ #include "events.h" #include "sql_audit.h" #include "probes_mysql.h" +#include "scheduler.h" #include "debug_sync.h" +#include "sql_callback.h" #ifdef WITH_PERFSCHEMA_STORAGE_ENGINE #include "../storage/perfschema/pfs_server.h" @@ -504,7 +506,7 @@ ulong slave_trans_retries; uint slave_net_timeout; uint slave_exec_mode_options; ulonglong slave_type_conversions_options; -ulong thread_cache_size=0, thread_pool_size= 0; +ulong thread_cache_size=0; ulong binlog_cache_size=0; ulonglong max_binlog_cache_size=0; ulong query_cache_size=0; @@ -935,8 +937,6 @@ my_bool opt_enable_shared_memory; HANDLE smem_event_connect_request= 0; #endif -scheduler_functions thread_scheduler; - my_bool opt_use_ssl = 0; char *opt_ssl_ca= NULL, *opt_ssl_capath= NULL, *opt_ssl_cert= NULL, *opt_ssl_cipher= NULL, *opt_ssl_key= NULL; @@ -1125,7 +1125,8 @@ static void close_connections(void) continue; tmp->killed= THD::KILL_CONNECTION; - thread_scheduler.post_kill_notification(tmp); + MYSQL_CALLBACK(thread_scheduler, post_kill_notification, (tmp)); + mysql_mutex_lock(&tmp->LOCK_thd_data); if (tmp->mysys_var) { tmp->mysys_var->abort=1; @@ -1138,6 +1139,7 @@ static void close_connections(void) } mysql_mutex_unlock(&tmp->mysys_var->mutex); } + mysql_mutex_unlock(&tmp->LOCK_thd_data); } mysql_mutex_unlock(&LOCK_thread_count); // For unlink from list @@ -1543,7 +1545,7 @@ void clean_up(bool print_message) if (print_message && my_default_lc_messages && server_start_time) sql_print_information(ER_DEFAULT(ER_SHUTDOWN_COMPLETE),my_progname); cleanup_errmsgs(); - thread_scheduler.end(); + MYSQL_CALLBACK(thread_scheduler, end, ()); finish_client_errs(); DBUG_PRINT("quit", ("Error messages freed")); /* Tell main we are ready */ @@ -1823,7 +1825,7 @@ static void network_init(void) DBUG_ENTER("network_init"); LINT_INIT(ret); - if (thread_scheduler.init()) + if (MYSQL_CALLBACK_ELSE(thread_scheduler, init, (), 0)) unireg_abort(1); /* purecov: inspected */ set_ports(); @@ -2071,7 +2073,7 @@ extern "C" sig_handler end_thread_signal(int sig __attribute__((unused))) if (thd && ! thd->bootstrap) { statistic_increment(killed_threads, &LOCK_status); - thread_scheduler.end_thread(thd,0); /* purecov: inspected */ + MYSQL_CALLBACK(thread_scheduler, end_thread, (thd,0)); /* purecov: inspected */ } DBUG_VOID_RETURN; /* purecov: deadcode */ } @@ -2679,7 +2681,7 @@ and this may fail.\n\n"); (ulong) dflt_key_cache->key_cache_mem_size); fprintf(stderr, "read_buffer_size=%ld\n", (long) global_system_variables.read_buff_size); fprintf(stderr, "max_used_connections=%lu\n", max_used_connections); - fprintf(stderr, "max_threads=%u\n", thread_scheduler.max_threads); + fprintf(stderr, "max_threads=%u\n", thread_scheduler->max_threads); fprintf(stderr, "thread_count=%u\n", thread_count); fprintf(stderr, "connection_count=%u\n", connection_count); fprintf(stderr, "It is possible that mysqld could use up to \n\ @@ -2687,7 +2689,7 @@ key_buffer_size + (read_buffer_size + sort_buffer_size)*max_threads = %lu K\n\ bytes of memory\n", ((ulong) dflt_key_cache->key_cache_mem_size + (global_system_variables.read_buff_size + global_system_variables.sortbuff_size) * - thread_scheduler.max_threads + + thread_scheduler->max_threads + max_connections * sizeof(THD)) / 1024); fprintf(stderr, "Hope that's ok; if not, decrease some variables in the equation.\n\n"); @@ -2932,7 +2934,7 @@ pthread_handler_t signal_hand(void *arg __attribute__((unused))) This should actually be '+ max_number_of_slaves' instead of +10, but the +10 should be quite safe. */ - init_thr_alarm(thread_scheduler.max_threads + + init_thr_alarm(thread_scheduler->max_threads + global_system_variables.max_insert_delayed_threads + 10); if (thd_lib_detected != THD_LIB_LT && (test_flags & TEST_SIGINT)) { @@ -4640,23 +4642,6 @@ int mysqld_main(int argc, char **argv) } #endif -#ifdef __WIN__ - /* - Before performing any socket operation (like retrieving hostname - in init_common_variables we have to call WSAStartup - */ - { - WSADATA WsaData; - if (SOCKET_ERROR == WSAStartup (0x0101, &WsaData)) - { - /* errors are not read yet, so we use english text here */ - my_message(ER_WSAS_FAILED, "WSAStartup Failed", MYF(0)); - /* Not enough initializations for unireg_abort() */ - return 1; - } - } -#endif /* __WIN__ */ - if (init_common_variables()) unireg_abort(1); // Will do exit @@ -5310,7 +5295,7 @@ static void create_new_thread(THD *thd) thread_count++; - thread_scheduler.add_connection(thd); + MYSQL_CALLBACK(thread_scheduler, add_connection, (thd)); DBUG_VOID_RETURN; } @@ -7633,14 +7618,12 @@ static int get_options(int *argc_ptr, char ***argv_ptr) return 1; #ifdef EMBEDDED_LIBRARY - one_thread_scheduler(&thread_scheduler); + one_thread_scheduler(); #else if (thread_handling <= SCHEDULER_ONE_THREAD_PER_CONNECTION) - one_thread_per_connection_scheduler(&thread_scheduler); - else if (thread_handling == SCHEDULER_NO_THREADS) - one_thread_scheduler(&thread_scheduler); - else - pool_of_threads_scheduler(&thread_scheduler); /* purecov: tested */ + one_thread_per_connection_scheduler(); + else /* thread_handling == SCHEDULER_NO_THREADS) */ + one_thread_scheduler(); #endif global_system_variables.engine_condition_pushdown= diff --git a/sql/mysqld.h b/sql/mysqld.h index 2547100d8ff..3ad43df3208 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -177,7 +177,7 @@ extern ulong binlog_cache_size, open_files_limit; extern ulonglong max_binlog_cache_size; extern ulong max_binlog_size, max_relay_log_size; extern ulong opt_binlog_rows_event_max_size; -extern ulong rpl_recovery_rank, thread_cache_size, thread_pool_size; +extern ulong rpl_recovery_rank, thread_cache_size; extern ulong back_log; extern char language[FN_REFLEN]; extern ulong server_id, concurrency; @@ -211,7 +211,6 @@ extern int bootstrap_error; extern FILE *stderror_file; extern I_List threads; extern char err_shared_dir[]; -extern scheduler_functions thread_scheduler; extern TYPELIB thread_handling_typelib; extern my_decimal decimal_zero; diff --git a/sql/scheduler.cc b/sql/scheduler.cc index 10009246428..19f8ddc7355 100644 --- a/sql/scheduler.cc +++ b/sql/scheduler.cc @@ -25,30 +25,8 @@ #include "unireg.h" // REQUIRED: for other includes #include "scheduler.h" #include "sql_connect.h" // init_new_connection_handler_thread - -/* - 'Dummy' functions to be used when we don't need any handling for a scheduler - event - */ - -static bool init_dummy(void) {return 0;} -static void post_kill_dummy(THD* thd) {} -static void end_dummy(void) {} -static bool end_thread_dummy(THD *thd, bool cache_thread) { return 0; } - -/* - Initialize default scheduler with dummy functions so that setup functions - only need to declare those that are relvant for their usage -*/ - -scheduler_functions::scheduler_functions() - :init(init_dummy), - init_new_connection_thread(init_new_connection_handler_thread), - add_connection(0), // Must be defined - post_kill_notification(post_kill_dummy), - end_thread(end_thread_dummy), end(end_dummy) -{} - +#include "scheduler.h" +#include "sql_callback.h" /* End connection, in case when we are using 'no-threads' @@ -61,19 +39,84 @@ static bool no_threads_end(THD *thd, bool put_in_cache) return 1; // Abort handle_one_connection } +static scheduler_functions one_thread_scheduler_functions= +{ + 1, // max_threads + NULL, // init + init_new_connection_handler_thread, // init_new_connection_thread + handle_connection_in_main_thread, // add_connection + NULL, // thd_wait_begin + NULL, // thd_wait_end + NULL, // post_kill_notification + no_threads_end, // end_thread + NULL, // end +}; + +static scheduler_functions one_thread_per_connection_scheduler_functions= +{ + 0, // max_threads + NULL, // init + init_new_connection_handler_thread, // init_new_connection_thread + create_thread_to_handle_connection, // add_connection + NULL, // thd_wait_begin + NULL, // thd_wait_end + NULL, // post_kill_notification + one_thread_per_connection_end, // end_thread + NULL, // end +}; + + +scheduler_functions *thread_scheduler= + &one_thread_per_connection_scheduler_functions; + +/** @internal + Helper functions to allow mysys to call the thread scheduler when + waiting for locks. +*/ + +/**@{*/ +static void scheduler_wait_begin(void) { + MYSQL_CALLBACK(thread_scheduler, + thd_wait_begin, (current_thd, THD_WAIT_ROW_TABLE_LOCK)); +} + +static void scheduler_wait_end(void) { + MYSQL_CALLBACK(thread_scheduler, thd_wait_end, (current_thd)); +} +/**@}*/ + +/** + Common scheduler init function. + + The scheduler is either initialized by calling + one_thread_scheduler() or one_thread_per_connection_scheduler() in + mysqld.cc, so this init function will always be called. + */ +static void scheduler_init() { + thr_set_lock_wait_callback(scheduler_wait_begin, scheduler_wait_end); +} + +/* + Initialize scheduler for --thread-handling=one-thread-per-connection +*/ + +#ifndef EMBEDDED_LIBRARY +void one_thread_per_connection_scheduler() +{ + scheduler_init(); + one_thread_per_connection_scheduler_functions.max_threads= max_connections; + thread_scheduler= &one_thread_per_connection_scheduler_functions; +} +#endif /* Initailize scheduler for --thread-handling=no-threads */ -void one_thread_scheduler(scheduler_functions* func) +void one_thread_scheduler() { - func->max_threads= 1; -#ifndef EMBEDDED_LIBRARY - func->add_connection= handle_connection_in_main_thread; -#endif - func->init_new_connection_thread= init_dummy; - func->end_thread= no_threads_end; + scheduler_init(); + thread_scheduler= &one_thread_scheduler_functions; } @@ -81,11 +124,58 @@ void one_thread_scheduler(scheduler_functions* func) Initialize scheduler for --thread-handling=one-thread-per-connection */ -#ifndef EMBEDDED_LIBRARY -void one_thread_per_connection_scheduler(scheduler_functions* func) +/* + thd_scheduler keeps the link between THD and events. + It's embedded in the THD class. +*/ + +thd_scheduler::thd_scheduler() + : m_psi(NULL), data(NULL) { - func->max_threads= max_connections; - func->add_connection= create_thread_to_handle_connection; - func->end_thread= one_thread_per_connection_end; +#ifndef DBUG_OFF + dbug_explain[0]= '\0'; + set_explain= FALSE; +#endif } -#endif /* EMBEDDED_LIBRARY */ + + +thd_scheduler::~thd_scheduler() +{ +} + +static scheduler_functions *saved_thread_scheduler; +static uint saved_thread_handling; + +extern "C" +int my_thread_scheduler_set(scheduler_functions *scheduler) +{ + DBUG_ASSERT(scheduler != 0); + + if (scheduler == NULL) + return 1; + + saved_thread_scheduler= thread_scheduler; + saved_thread_handling= thread_handling; + thread_scheduler= scheduler; + // Scheduler loaded dynamically + thread_handling= SCHEDULER_TYPES_COUNT; + return 0; +} + + +extern "C" +int my_thread_scheduler_reset() +{ + DBUG_ASSERT(saved_thread_scheduler != NULL); + + if (saved_thread_scheduler == NULL) + return 1; + + thread_scheduler= saved_thread_scheduler; + thread_handling= saved_thread_handling; + saved_thread_scheduler= 0; + return 0; +} + + + diff --git a/sql/scheduler.h b/sql/scheduler.h index e7916031a27..40f0e28bc2c 100644 --- a/sql/scheduler.h +++ b/sql/scheduler.h @@ -28,38 +28,77 @@ class THD; /* Functions used when manipulating threads */ -class scheduler_functions +struct scheduler_functions { -public: uint max_threads; bool (*init)(void); bool (*init_new_connection_thread)(void); void (*add_connection)(THD *thd); + void (*thd_wait_begin)(THD *thd, int wait_type); + void (*thd_wait_end)(THD *thd); void (*post_kill_notification)(THD *thd); bool (*end_thread)(THD *thd, bool cache_thread); void (*end)(void); - scheduler_functions(); }; + +/** + Scheduler types enumeration. + + The default of --thread-handling is the first one in the + thread_handling_names array, this array has to be consistent with + the order in this array, so to change default one has to change the + first entry in this enum and the first entry in the + thread_handling_names array. + + @note The last entry of the enumeration is also used to mark the + thread handling as dynamic. In this case the name of the thread + handling is fetched from the name of the plugin that implements it. +*/ enum scheduler_types { SCHEDULER_ONE_THREAD_PER_CONNECTION=0, SCHEDULER_NO_THREADS, - SCHEDULER_POOL_OF_THREADS + SCHEDULER_TYPES_COUNT }; -void one_thread_per_connection_scheduler(scheduler_functions* func); -void one_thread_scheduler(scheduler_functions* func); +void one_thread_per_connection_scheduler(); +void one_thread_scheduler(); enum pool_command_op { NOT_IN_USE_OP= 0, NORMAL_OP= 1, CONNECT_OP, KILL_OP, DIE_OP }; -#define HAVE_POOL_OF_THREADS 0 /* For easyer tests */ -#define pool_of_threads_scheduler(A) one_thread_per_connection_scheduler(A) - +/* + To be used for pool-of-threads (implemeneted differently on various OSs) +*/ class thd_scheduler -{}; +{ +public: + /* + Thread instrumentation for the user job. + This member holds the instrumentation while the user job is not run + by a thread. -#endif /* SCHEDULER_INCLUDED */ + Note that this member is not conditionally declared + (ifdef HAVE_PSI_INTERFACE), because doing so will change the binary + layout of THD, which is exposed to plugin code that may be compiled + differently. + */ + PSI_thread *m_psi; + + void *data; /* scheduler-specific data structure */ + +# ifndef DBUG_OFF + char dbug_explain[512]; + bool set_explain; +# endif + + thd_scheduler(); + ~thd_scheduler(); +}; + +extern scheduler_functions *thread_scheduler; + +#endif diff --git a/sql/sql_callback.h b/sql/sql_callback.h new file mode 100644 index 00000000000..430514d3d7e --- /dev/null +++ b/sql/sql_callback.h @@ -0,0 +1,43 @@ +/* + Copyright (C) 2010, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef SQL_CALLBACK_INCLUDED +#define SQL_CALLBACK_INCLUDED + +/** + Macro used for an internal callback. + + The macro will check that the object exists and that the function + is defined. If that is the case, it will call the function with the + given parameters. + + If the object or the function is not defined, the callback will be + considered successful (nothing needed to be done) and will + therefore return no error. + */ + +#define MYSQL_CALLBACK(OBJ, FUNC, PARAMS) \ + do { \ + if ((OBJ) && ((OBJ)->FUNC)) \ + (OBJ)->FUNC PARAMS; \ + } while (0) + +#define MYSQL_CALLBACK_ELSE(OBJ, FUNC, PARAMS, ELSE) \ + (((OBJ) && ((OBJ)->FUNC)) ? (OBJ)->FUNC PARAMS : (ELSE)) + + +#endif /* SQL_CALLBACK_INCLUDED */ diff --git a/sql/sql_class.cc b/sql/sql_class.cc index ef6dc6cf209..a47c6046bac 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -58,6 +58,7 @@ #include "transaction.h" #include "debug_sync.h" #include "sql_parse.h" // is_update_query +#include "sql_callback.h" /* The following is used to initialise Table_ident with a internal @@ -1055,6 +1056,7 @@ THD::~THD() DBUG_ENTER("~THD()"); /* Ensure that no one is using THD */ mysql_mutex_lock(&LOCK_thd_data); + mysys_var=0; // Safety (shouldn't be needed) mysql_mutex_unlock(&LOCK_thd_data); add_to_status(&global_status_var, &status_var); @@ -1080,7 +1082,6 @@ THD::~THD() main_security_ctx.destroy(); safeFree(db); free_root(&transaction.mem_root,MYF(0)); - mysys_var=0; // Safety (shouldn't be needed) mysql_mutex_destroy(&LOCK_thd_data); #ifndef DBUG_OFF dbug_sentry= THD_SENTRY_GONE; @@ -1163,7 +1164,7 @@ void THD::awake(THD::killed_state state_to_set) { thr_alarm_kill(thread_id); if (!slave_thread) - thread_scheduler.post_kill_notification(this); + MYSQL_CALLBACK(thread_scheduler, post_kill_notification, (this)); #ifdef SIGNAL_WITH_VIO_CLOSE if (this != current_thd) { @@ -1232,6 +1233,15 @@ bool THD::store_globals() if (my_pthread_setspecific_ptr(THR_THD, this) || my_pthread_setspecific_ptr(THR_MALLOC, &mem_root)) return 1; + /* + mysys_var is concurrently readable by a killer thread. + It is protected by LOCK_thd_data, it is not needed to lock while the + pointer is changing from NULL not non-NULL. If the kill thread reads + NULL it doesn't refer to anything, but if it is non-NULL we need to + ensure that the thread doesn't proceed to assign another thread to + have the mysys_var reference (which in fact refers to the worker + threads local storage with key THR_KEY_mysys. + */ mysys_var=my_thread_var; /* Let mysqld define the thread id (not mysys) @@ -3145,6 +3155,60 @@ extern "C" bool thd_binlog_filter_ok(const MYSQL_THD thd) { return binlog_filter->db_ok(thd->db); } + +#ifndef EMBEDDED_LIBRARY +extern "C" void thd_pool_wait_begin(MYSQL_THD thd, int wait_type); +extern "C" void thd_pool_wait_end(MYSQL_THD thd); + +/* + Interface for MySQL Server, plugins and storage engines to report + when they are going to sleep/stall. + + SYNOPSIS + thd_wait_begin() + thd Thread object + wait_type Type of wait + 1 -- short wait (e.g. for mutex) + 2 -- medium wait (e.g. for disk io) + 3 -- large wait (e.g. for locked row/table) + NOTES + This is used by the threadpool to have better knowledge of which + threads that currently are actively running on CPUs. When a thread + reports that it's going to sleep/stall, the threadpool scheduler is + free to start another thread in the pool most likely. The expected wait + time is simply an indication of how long the wait is expected to + become, the real wait time could be very different. + + thd_wait_end MUST be called immediately after waking up again. +*/ +extern "C" void thd_wait_begin(MYSQL_THD thd, thd_wait_type wait_type) +{ + MYSQL_CALLBACK(thread_scheduler, thd_wait_begin, (thd, wait_type)); +} + +/** + Interface for MySQL Server, plugins and storage engines to report + when they waking up from a sleep/stall. + + @param thd Thread handle +*/ +extern "C" void thd_wait_end(MYSQL_THD thd) +{ + MYSQL_CALLBACK(thread_scheduler, thd_wait_end, (thd)); +} +#else +extern "C" void thd_wait_begin(MYSQL_THD thd, thd_wait_type wait_type) +{ + /* do NOTHING for the embedded library */ + return; +} + +extern "C" void thd_wait_end(MYSQL_THD thd) +{ + /* do NOTHING for the embedded library */ + return; +} +#endif #endif // INNODB_COMPATIBILITY_HOOKS */ /**************************************************************************** @@ -3324,6 +3388,13 @@ void THD::set_query_id(query_id_t new_query_id) mysql_mutex_unlock(&LOCK_thd_data); } +/** Assign a new value to thd->mysys_var. */ +void THD::set_mysys_var(struct st_my_thread_var *new_mysys_var) +{ + mysql_mutex_lock(&LOCK_thd_data); + mysys_var= new_mysys_var; + mysql_mutex_unlock(&LOCK_thd_data); +} /** Leave explicit LOCK TABLES or prelocked mode and restore value of diff --git a/sql/sql_class.h b/sql/sql_class.h index 0a098fc8492..50c6855e2c3 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1812,6 +1812,10 @@ public: xid_state.xid.null(); free_root(&mem_root,MYF(MY_KEEP_PREALLOC)); } + my_bool is_active() + { + return (all.ha_list != NULL); + } st_transactions() { bzero((char*)this, sizeof(*this)); @@ -2734,13 +2738,14 @@ public: virtual void set_statement(Statement *stmt); /** - Assign a new value to thd->query and thd->query_id. + Assign a new value to thd->query and thd->query_id and mysys_var. Protected with LOCK_thd_data mutex. */ void set_query(char *query_arg, uint32 query_length_arg); void set_query_and_id(char *query_arg, uint32 query_length_arg, query_id_t new_query_id); void set_query_id(query_id_t new_query_id); + void set_mysys_var(struct st_my_thread_var *new_mysys_var); void enter_locked_tables_mode(enum_locked_tables_mode mode_arg) { DBUG_ASSERT(locked_tables_mode == LTM_NONE); diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc index e2d0977def7..00bfe372efd 100644 --- a/sql/sql_connect.cc +++ b/sql/sql_connect.cc @@ -35,6 +35,7 @@ #include "hostname.h" // inc_host_errors, ip_to_hostname, // reset_host_errors #include "sql_acl.h" // acl_getroot, NO_ACCESS, SUPER_ACL +#include "sql_callback.h" #if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) /* @@ -958,7 +959,7 @@ bool setup_connection_thread_globals(THD *thd) { close_connection(thd, ER_OUT_OF_RESOURCES, 1); statistic_increment(aborted_connects,&LOCK_status); - thread_scheduler.end_thread(thd, 0); + MYSQL_CALLBACK(thread_scheduler, end_thread, (thd, 0)); return 1; // Error } return 0; @@ -981,7 +982,7 @@ bool setup_connection_thread_globals(THD *thd) */ -static bool login_connection(THD *thd) +bool login_connection(THD *thd) { NET *net= &thd->net; int error; @@ -1019,7 +1020,7 @@ static bool login_connection(THD *thd) This mainly updates status variables */ -static void end_connection(THD *thd) +void end_connection(THD *thd) { NET *net= &thd->net; plugin_thdvar_cleanup(thd); @@ -1060,7 +1061,7 @@ static void end_connection(THD *thd) Initialize THD to handle queries */ -static void prepare_new_connection_state(THD* thd) +void prepare_new_connection_state(THD* thd) { Security_context *sctx= thd->security_ctx; @@ -1134,11 +1135,11 @@ void do_handle_one_connection(THD *thd_arg) thd->thr_create_utime= my_micro_time(); - if (thread_scheduler.init_new_connection_thread()) + if (MYSQL_CALLBACK_ELSE(thread_scheduler, init_new_connection_thread, (), 0)) { close_connection(thd, ER_OUT_OF_RESOURCES, 1); statistic_increment(aborted_connects,&LOCK_status); - thread_scheduler.end_thread(thd,0); + MYSQL_CALLBACK(thread_scheduler, end_thread, (thd, 0)); return; } @@ -1192,7 +1193,7 @@ void do_handle_one_connection(THD *thd_arg) end_thread: close_connection(thd, 0, 1); - if (thread_scheduler.end_thread(thd,1)) + if (MYSQL_CALLBACK_ELSE(thread_scheduler, end_thread, (thd, 1), 0)) return; // Probably no-threads /* diff --git a/sql/sql_connect.h b/sql/sql_connect.h index 2334b7303be..bfcc04ba093 100644 --- a/sql/sql_connect.h +++ b/sql/sql_connect.h @@ -40,4 +40,8 @@ int check_user(THD *thd, enum enum_server_command command, const char *passwd, uint passwd_len, const char *db, bool check_count); +bool login_connection(THD *thd); +void prepare_new_connection_state(THD* thd); +void end_connection(THD *thd); + #endif /* SQL_CONNECT_INCLUDED */ diff --git a/sql/sql_plugin_services.h b/sql/sql_plugin_services.h index 7491ddab79d..f39e22f1e21 100644 --- a/sql/sql_plugin_services.h +++ b/sql/sql_plugin_services.h @@ -36,9 +36,23 @@ static struct thd_alloc_service_st thd_alloc_handler= { thd_make_lex_string }; +static struct thd_wait_service_st thd_wait_handler= { + thd_wait_begin, + thd_wait_end +}; + +static struct my_thread_scheduler_service my_thread_scheduler_handler= { + my_thread_scheduler_set, + my_thread_scheduler_reset, +}; + + static struct st_service_ref list_of_services[]= { { "my_snprintf_service", VERSION_my_snprintf, &my_snprintf_handler }, - { "thd_alloc_service", VERSION_thd_alloc, &thd_alloc_handler } + { "thd_alloc_service", VERSION_thd_alloc, &thd_alloc_handler }, + { "thd_wait_service", VERSION_thd_wait, &thd_wait_handler }, + { "my_thread_scheduler_service", + VERSION_my_thread_scheduler, &my_thread_scheduler_handler }, }; diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 41117650e4a..217128653c6 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1789,6 +1789,7 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose) if ((thd_info->db=tmp->db)) // Safe test thd_info->db=thd->strdup(thd_info->db); thd_info->command=(int) tmp->command; + mysql_mutex_lock(&tmp->LOCK_thd_data); if ((mysys_var= tmp->mysys_var)) mysql_mutex_lock(&mysys_var->mutex); thd_info->proc_info= (char*) (tmp->killed == THD::KILL_CONNECTION? "Killed" : 0); @@ -1796,16 +1797,15 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose) if (mysys_var) mysql_mutex_unlock(&mysys_var->mutex); - thd_info->start_time= tmp->start_time; thd_info->query=0; /* Lock THD mutex that protects its data when looking at it. */ - mysql_mutex_lock(&tmp->LOCK_thd_data); if (tmp->query()) { uint length= min(max_query_length, tmp->query_length()); thd_info->query= (char*) thd->strmake(tmp->query(),length); } mysql_mutex_unlock(&tmp->LOCK_thd_data); + thd_info->start_time= tmp->start_time; thread_infos.append(thd_info); } } @@ -1892,6 +1892,7 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond) table->field[3]->set_notnull(); } + mysql_mutex_lock(&tmp->LOCK_thd_data); if ((mysys_var= tmp->mysys_var)) mysql_mutex_lock(&mysys_var->mutex); /* COMMAND */ @@ -1912,6 +1913,7 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond) if (mysys_var) mysql_mutex_unlock(&mysys_var->mutex); + mysql_mutex_unlock(&tmp->LOCK_thd_data); /* INFO */ if (tmp->query()) diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index b5df2ae58c1..e743e994444 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -1658,19 +1658,13 @@ static Sys_var_ulong Sys_trans_prealloc_size( static const char *thread_handling_names[]= { - "one-thread-per-connection", "no-threads", -#if HAVE_POOL_OF_THREADS == 1 - "pool-of-threads", -#endif + "one-thread-per-connection", "no-threads", "loaded-dynamically", 0 }; static Sys_var_enum Sys_thread_handling( "thread_handling", "Define threads usage for handling queries, one of " - "one-thread-per-connection, no-threads" -#if HAVE_POOL_OF_THREADS == 1 - ", pool-of-threads" -#endif + "one-thread-per-connection, no-threads, loaded-dynamically" , READ_ONLY GLOBAL_VAR(thread_handling), CMD_LINE(REQUIRED_ARG), thread_handling_names, DEFAULT(0)); @@ -1997,15 +1991,6 @@ static Sys_var_ulong Sys_thread_cache_size( GLOBAL_VAR(thread_cache_size), CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 16384), DEFAULT(0), BLOCK_SIZE(1)); -#if HAVE_POOL_OF_THREADS == 1 -static Sys_var_ulong Sys_thread_pool_size( - "thread_pool_size", - "How many threads we should create to handle query requests in " - "case of 'thread_handling=pool-of-threads'", - GLOBAL_VAR(thread_pool_size), CMD_LINE(REQUIRED_ARG), - VALID_RANGE(1, 16384), DEFAULT(20), BLOCK_SIZE(0)); -#endif - // Can't change the 'next' tx_isolation if we are already in a transaction static bool check_tx_isolation(sys_var *self, THD *thd, set_var *var) { diff --git a/storage/innobase/buf/buf0flu.c b/storage/innobase/buf/buf0flu.c index 8b614ce90e5..a1fea902301 100644 --- a/storage/innobase/buf/buf0flu.c +++ b/storage/innobase/buf/buf0flu.c @@ -43,6 +43,8 @@ Created 11/11/1995 Heikki Tuuri #include "log0log.h" #include "os0file.h" #include "trx0sys.h" +#include "mysql/plugin.h" +#include "mysql/service_thd_wait.h" /********************************************************************** These statistics are generated for heuristics used in estimating the @@ -1165,7 +1167,9 @@ buf_flush_wait_batch_end( { ut_ad((type == BUF_FLUSH_LRU) || (type == BUF_FLUSH_LIST)); + thd_wait_begin(NULL, THD_WAIT_DISKIO); os_event_wait(buf_pool->no_flush[type]); + thd_wait_end(NULL); } /******************************************************************//** diff --git a/storage/innobase/buf/buf0rea.c b/storage/innobase/buf/buf0rea.c index dd98ea17eb5..5a92aa87d0e 100644 --- a/storage/innobase/buf/buf0rea.c +++ b/storage/innobase/buf/buf0rea.c @@ -37,6 +37,8 @@ Created 11/5/1995 Heikki Tuuri #include "os0file.h" #include "srv0start.h" #include "srv0srv.h" +#include "mysql/plugin.h" +#include "mysql/service_thd_wait.h" /** The linear read-ahead area size */ #define BUF_READ_AHEAD_LINEAR_AREA BUF_READ_AHEAD_AREA @@ -135,6 +137,7 @@ buf_read_page_low( ut_ad(buf_page_in_file(bpage)); + thd_wait_begin(NULL, THD_WAIT_DISKIO); if (zip_size) { *err = fil_io(OS_FILE_READ | wake_later, sync, space, zip_size, offset, 0, zip_size, @@ -146,6 +149,7 @@ buf_read_page_low( sync, space, 0, offset, 0, UNIV_PAGE_SIZE, ((buf_block_t*) bpage)->frame, bpage); } + thd_wait_end(NULL); ut_a(*err == DB_SUCCESS); if (sync) { diff --git a/storage/innobase/srv/srv0srv.c b/storage/innobase/srv/srv0srv.c index 3b0e29b9b48..ba6a424256f 100644 --- a/storage/innobase/srv/srv0srv.c +++ b/storage/innobase/srv/srv0srv.c @@ -103,6 +103,8 @@ Created 10/8/1995 Heikki Tuuri #include "ha_prototypes.h" #include "trx0i_s.h" #include "os0sync.h" /* for HAVE_ATOMIC_BUILTINS */ +#include "mysql/plugin.h" +#include "mysql/service_thd_wait.h" /* This is set to TRUE if the MySQL user has set it in MySQL; currently affects only FOREIGN KEY definition parsing */ @@ -1186,7 +1188,9 @@ retry: trx->op_info = "waiting in InnoDB queue"; + thd_wait_begin(trx->mysql_thd, THD_WAIT_ROW_TABLE_LOCK); os_event_wait(slot->event); + thd_wait_end(trx->mysql_thd); trx->op_info = ""; @@ -1551,7 +1555,9 @@ srv_suspend_mysql_thread( /* Suspend this thread and wait for the event. */ + thd_wait_begin(trx->mysql_thd, THD_WAIT_ROW_TABLE_LOCK); os_event_wait(event); + thd_wait_end(trx->mysql_thd); /* After resuming, reacquire the data dictionary latch if necessary. */ diff --git a/vio/vio.c b/vio/vio.c index 73dd68b938f..210bcdb584b 100644 --- a/vio/vio.c +++ b/vio/vio.c @@ -44,6 +44,11 @@ static my_bool no_poll_read(Vio *vio __attribute__((unused)), #endif +static my_bool has_no_data(Vio *vio __attribute__((unused))) +{ + return FALSE; +} + /* * Helper to fill most of the Vio* with defaults. */ @@ -83,6 +88,7 @@ static void vio_init(Vio* vio, enum enum_vio_type type, vio->poll_read =no_poll_read; vio->is_connected =vio_is_connected_pipe; + vio->has_data =has_no_data; vio->timeout=vio_win32_timeout; /* Set default timeout */ @@ -110,6 +116,7 @@ static void vio_init(Vio* vio, enum enum_vio_type type, vio->poll_read =no_poll_read; vio->is_connected =vio_is_connected_shared_memory; + vio->has_data =has_no_data; /* Currently, shared memory is on Windows only, hence the below is ok*/ vio->timeout= vio_win32_timeout; @@ -137,6 +144,7 @@ static void vio_init(Vio* vio, enum enum_vio_type type, vio->timeout =vio_timeout; vio->poll_read =vio_poll_read; vio->is_connected =vio_is_connected; + vio->has_data =vio_ssl_has_data; DBUG_VOID_RETURN; } #endif /* HAVE_OPENSSL */ @@ -155,6 +163,8 @@ static void vio_init(Vio* vio, enum enum_vio_type type, vio->timeout =vio_timeout; vio->poll_read =vio_poll_read; vio->is_connected =vio_is_connected; + vio->has_data= (flags & VIO_BUFFERED_READ) ? + vio_buff_has_data : has_no_data; DBUG_VOID_RETURN; } diff --git a/vio/vio_priv.h b/vio/vio_priv.h index 69eb26083d6..1bfb857b039 100644 --- a/vio/vio_priv.h +++ b/vio/vio_priv.h @@ -49,6 +49,7 @@ int vio_close_shared_memory(Vio * vio); #endif void vio_timeout(Vio *vio,uint which, uint timeout); +my_bool vio_buff_has_data(Vio *vio); #ifdef HAVE_OPENSSL #include "my_net.h" /* needed because of struct in_addr */ @@ -62,5 +63,7 @@ void vio_ssl_delete(Vio *vio); int vio_ssl_blocking(Vio *vio, my_bool set_blocking_mode, my_bool *old_mode); +my_bool vio_ssl_has_data(Vio *vio); + #endif /* HAVE_OPENSSL */ #endif /* VIO_PRIV_INCLUDED */ diff --git a/vio/viosocket.c b/vio/viosocket.c index 6c361e4a462..e60fe9bb225 100644 --- a/vio/viosocket.c +++ b/vio/viosocket.c @@ -98,6 +98,10 @@ size_t vio_read_buff(Vio *vio, uchar* buf, size_t size) #undef VIO_UNBUFFERED_READ_MIN_SIZE } +my_bool vio_buff_has_data(Vio *vio) +{ + return (vio->read_pos != vio->read_end); +} size_t vio_write(Vio * vio, const uchar* buf, size_t size) { diff --git a/vio/viossl.c b/vio/viossl.c index 0651fd8b7a3..1c6863c9943 100644 --- a/vio/viossl.c +++ b/vio/viossl.c @@ -274,6 +274,9 @@ int vio_ssl_blocking(Vio *vio __attribute__((unused)), return (set_blocking_mode ? 0 : 1); } - +my_bool vio_ssl_has_data(Vio *vio) +{ + return SSL_pending(vio->ssl_arg) > 0 ? TRUE : FALSE; +} #endif /* HAVE_OPENSSL */ From 7017775fd11c4249c1b64ff45564c88b94cf526f Mon Sep 17 00:00:00 2001 From: Mats Kindahl Date: Tue, 15 Jun 2010 09:44:26 +0200 Subject: [PATCH 02/28] WL#5363: Thread Pool Service Interface Fixes to ensure that it builds with the embedded server as well. --- sql/scheduler.cc | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/sql/scheduler.cc b/sql/scheduler.cc index 19f8ddc7355..d61a452b99e 100644 --- a/sql/scheduler.cc +++ b/sql/scheduler.cc @@ -44,7 +44,11 @@ static scheduler_functions one_thread_scheduler_functions= 1, // max_threads NULL, // init init_new_connection_handler_thread, // init_new_connection_thread +#ifndef EMBEDDED_LIBRARY handle_connection_in_main_thread, // add_connection +#else + NULL, // add_connection +#endif // EMBEDDED_LIBRARY NULL, // thd_wait_begin NULL, // thd_wait_end NULL, // post_kill_notification @@ -52,6 +56,7 @@ static scheduler_functions one_thread_scheduler_functions= NULL, // end }; +#ifndef EMBEDDED_LIBRARY static scheduler_functions one_thread_per_connection_scheduler_functions= { 0, // max_threads @@ -64,10 +69,10 @@ static scheduler_functions one_thread_per_connection_scheduler_functions= one_thread_per_connection_end, // end_thread NULL, // end }; +#endif // EMBEDDED_LIBRARY -scheduler_functions *thread_scheduler= - &one_thread_per_connection_scheduler_functions; +scheduler_functions *thread_scheduler= NULL; /** @internal Helper functions to allow mysys to call the thread scheduler when From b7ad17d06a31e0ca8b8ef771e8ba3fcd8b534b32 Mon Sep 17 00:00:00 2001 From: Mattias Jonsson Date: Thu, 8 Jul 2010 14:36:55 +0200 Subject: [PATCH 03/28] Bug#46086: crash when dropping a partitioned table and the original engine is disabled Missing check that engine is available. mysql-test/include/not_blackhole.inc: new include file mysql-test/r/partition_not_blackhole.result: new result file mysql-test/std_data/parts/t1_blackhole.frm: blackhole partitioned table .frm file: create table `t1` (`id` int primary key) engine=blackhole partition by key () partitions 1; mysql-test/std_data/parts/t1_blackhole.par: .par file matching blackhole partitioned .frm mysql-test/t/partition_not_blackhole-master.opt: new master-opt to disable blackhole if compiled in. mysql-test/t/partition_not_blackhole.test: New test sql/ha_partition.cc: Added check that engine is available. --- mysql-test/include/not_blackhole.inc | 5 ++++ mysql-test/r/partition_not_blackhole.result | 16 +++++++++++ mysql-test/std_data/parts/t1_blackhole.frm | Bin 0 -> 8556 bytes mysql-test/std_data/parts/t1_blackhole.par | Bin 0 -> 24 bytes .../t/partition_not_blackhole-master.opt | 1 + mysql-test/t/partition_not_blackhole.test | 25 ++++++++++++++++++ sql/ha_partition.cc | 7 ++++- 7 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 mysql-test/include/not_blackhole.inc create mode 100644 mysql-test/r/partition_not_blackhole.result create mode 100644 mysql-test/std_data/parts/t1_blackhole.frm create mode 100644 mysql-test/std_data/parts/t1_blackhole.par create mode 100644 mysql-test/t/partition_not_blackhole-master.opt create mode 100644 mysql-test/t/partition_not_blackhole.test diff --git a/mysql-test/include/not_blackhole.inc b/mysql-test/include/not_blackhole.inc new file mode 100644 index 00000000000..078927ec4ca --- /dev/null +++ b/mysql-test/include/not_blackhole.inc @@ -0,0 +1,5 @@ +if (`SELECT count(*) FROM information_schema.engines WHERE + (support = 'YES' OR support = 'DEFAULT') AND + engine = 'blackhole'`){ + skip Blackhole engine enabled; +} diff --git a/mysql-test/r/partition_not_blackhole.result b/mysql-test/r/partition_not_blackhole.result new file mode 100644 index 00000000000..dc0339f8c48 --- /dev/null +++ b/mysql-test/r/partition_not_blackhole.result @@ -0,0 +1,16 @@ +DROP TABLE IF EXISTS t1; +# +# Bug#46086: crash when dropping a partitioned table and +# the original engine is disabled +# Copy a .frm and .par file which was created with: +# create table `t1` (`id` int primary key) engine=blackhole +# partition by key () partitions 1; +SHOW TABLES; +Tables_in_test +t1 +SHOW CREATE TABLE t1; +ERROR HY000: Incorrect information in file: './test/t1.frm' +DROP TABLE t1; +ERROR 42S02: Unknown table 't1' +t1.frm +t1.par diff --git a/mysql-test/std_data/parts/t1_blackhole.frm b/mysql-test/std_data/parts/t1_blackhole.frm new file mode 100644 index 0000000000000000000000000000000000000000..be77b7a041abe8aeb50ac1add88c9a5178904808 GIT binary patch literal 8556 zcmeI&KMR6D7zXg?`~#_rkhHX1TTT^3a|I0&l~{{o&~gwJ;Roou^;!BF*^5bYQy4t& za(asQ+;8*cp2~}CAXLB*Fv`WJtR7lGH6i1>jJ)@_1LNwp4Gac=t{-xs00k&O0SZun z0u-PC1t>rP3Q(Y#0!n`9eTn8kE}(7}(49Ic(=1udvb=0&>OV|XOM1BMuZ1bh`P7qL z=yaCtl3lV{>v^u1i(L0(|1-DMqd(j!BU69^6rcbFC_n)UP=Epypg;o!jtXG|WBn|W vlD@|Tfvo4gk2+Ki$U|?Vb(!xN@48#RMJ^&A(0aXA1 literal 0 HcmV?d00001 diff --git a/mysql-test/t/partition_not_blackhole-master.opt b/mysql-test/t/partition_not_blackhole-master.opt new file mode 100644 index 00000000000..1e47be930bc --- /dev/null +++ b/mysql-test/t/partition_not_blackhole-master.opt @@ -0,0 +1 @@ +--loose-skip-blackhole diff --git a/mysql-test/t/partition_not_blackhole.test b/mysql-test/t/partition_not_blackhole.test new file mode 100644 index 00000000000..222c1bb091e --- /dev/null +++ b/mysql-test/t/partition_not_blackhole.test @@ -0,0 +1,25 @@ +--source include/have_partition.inc +--source include/not_blackhole.inc + +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +let $MYSQLD_DATADIR= `SELECT @@datadir`; + +--echo # +--echo # Bug#46086: crash when dropping a partitioned table and +--echo # the original engine is disabled +--echo # Copy a .frm and .par file which was created with: +--echo # create table `t1` (`id` int primary key) engine=blackhole +--echo # partition by key () partitions 1; +--copy_file std_data/parts/t1_blackhole.frm $MYSQLD_DATADIR/test/t1.frm +--copy_file std_data/parts/t1_blackhole.par $MYSQLD_DATADIR/test/t1.par +SHOW TABLES; +--error ER_NOT_FORM_FILE +SHOW CREATE TABLE t1; +--error ER_BAD_TABLE_ERROR +DROP TABLE t1; +--list_files $MYSQLD_DATADIR/test t1* +--remove_file $MYSQLD_DATADIR/test/t1.frm +--remove_file $MYSQLD_DATADIR/test/t1.par diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 60722f0100e..07a9035f865 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -2403,9 +2403,14 @@ bool ha_partition::get_from_handler_file(const char *name, MEM_ROOT *mem_root) tot_partition_words= (m_tot_parts + 3) / 4; engine_array= (handlerton **) my_alloca(m_tot_parts * sizeof(handlerton*)); for (i= 0; i < m_tot_parts; i++) + { engine_array[i]= ha_resolve_by_legacy_type(ha_thd(), (enum legacy_db_type) - *(uchar *) ((file_buffer) + 12 + i)); + *(uchar *) ((file_buffer) + + 12 + i)); + if (!engine_array[i]) + goto err3; + } address_tot_name_len= file_buffer + 12 + 4 * tot_partition_words; tot_name_words= (uint4korr(address_tot_name_len) + 3) / 4; if (len_words != (tot_partition_words + tot_name_words + 4)) From 1837dcfee747b697bce2023d94a8daff6e393039 Mon Sep 17 00:00:00 2001 From: Alexey Kopytov Date: Fri, 23 Jul 2010 15:52:54 +0400 Subject: [PATCH 04/28] Bug #54476: crash when group_concat and 'with rollup' in prepared statements Using GROUP_CONCAT() together with the WITH ROLLUP modifier could crash the server. The reason was a combination of several facts: 1. The Item_func_group_concat class stores pointers to ORDER objects representing the columns in the ORDER BY clause of GROUP_CONCAT(). 2. find_order_in_list() called from Item_func_group_concat::setup() modifies the ORDER objects so that their 'item' member points to the arguments list allocated in the Item_func_group_concat constructor. 3. In some cases (e.g. in JOIN::rollup_make_fields) a copy of the original Item_func_group_concat object could be created by using the Item_func_group_concat::Item_func_group_concat(THD *thd, Item_func_group_concat *item) copy constructor. The latter essentially creates a shallow copy of the source object. Memory for the arguments array is allocated on thd->mem_root, but the pointers for arguments and ORDER are copied verbatim. What happens in the test case is that when executing the query for the first time, after a copy of the original Item_func_group_concat object has been created by JOIN::rollup_make_fields(), find_order_in_list() is called for this new object. It then resolves ORDER BY by modifying the ORDER objects so that they point to elements of the arguments array which is local to the cloned object. When thd->mem_root is freed upon completing the execution, pointers in the ORDER objects become invalid. Those ORDER objects, however, are also shared with the original Item_func_group_concat object which is preserved between executions of a prepared statement. So the first call to find_order_in_list() for the original object on the second execution tries to dereference an invalid pointer. The solution is to create copies of the ORDER objects when copying Item_func_group_concat to not leave any stale pointers in other instances with different lifecycles. mysql-test/r/func_gconcat.result: Test case for bug #54476. mysql-test/t/func_gconcat.test: Test case for bug #54476. sql/item_sum.cc: Copy the ORDER objects pointed to by the elements of the 'order' array in the copy constructor of Item_func_group_concat. sql/table.h: Removed the unused 'item_copy' member of the ORDER class. --- mysql-test/r/func_gconcat.result | 21 ++++++++++++++++++++- mysql-test/t/func_gconcat.test | 16 +++++++++++++++- sql/item_sum.cc | 19 ++++++++++++++++++- sql/table.h | 1 - 4 files changed, 53 insertions(+), 4 deletions(-) diff --git a/mysql-test/r/func_gconcat.result b/mysql-test/r/func_gconcat.result index 766f3b6bfaa..ae48eb1e0ff 100644 --- a/mysql-test/r/func_gconcat.result +++ b/mysql-test/r/func_gconcat.result @@ -995,6 +995,7 @@ SELECT 1 FROM 1 1 DROP TABLE t1; +End of 5.0 tests # # Bug #52397: another crash with explain extended and group_concat # @@ -1010,4 +1011,22 @@ id select_type table type possible_keys key key_len ref rows filtered Extra Warnings: Note 1003 select 1 AS `1` from (select group_concat(`test`.`t1`.`a` order by `test`.`t1`.`a` ASC separator ',') AS `GROUP_CONCAT(t1.a ORDER BY t1.a ASC)` from `test`.`t1` `t2` join `test`.`t1` group by `test`.`t1`.`a`) `d` DROP TABLE t1; -End of 5.0 tests +# +# Bug #54476: crash when group_concat and 'with rollup' in prepared statements +# +CREATE TABLE t1 (a INT); +INSERT INTO t1 VALUES (1), (2); +PREPARE stmt FROM "SELECT GROUP_CONCAT(t1.a ORDER BY t1.a) FROM t1 JOIN t1 t2 GROUP BY t1.a WITH ROLLUP"; +EXECUTE stmt; +GROUP_CONCAT(t1.a ORDER BY t1.a) +1,1 +2,2 +1,1,2,2 +EXECUTE stmt; +GROUP_CONCAT(t1.a ORDER BY t1.a) +1,1 +2,2 +1,1,2,2 +DEALLOCATE PREPARE stmt; +DROP TABLE t1; +End of 5.1 tests diff --git a/mysql-test/t/func_gconcat.test b/mysql-test/t/func_gconcat.test index e832ea316eb..926c1f92855 100644 --- a/mysql-test/t/func_gconcat.test +++ b/mysql-test/t/func_gconcat.test @@ -708,6 +708,7 @@ SELECT 1 FROM DROP TABLE t1; +--echo End of 5.0 tests --echo # --echo # Bug #52397: another crash with explain extended and group_concat @@ -719,5 +720,18 @@ EXPLAIN EXTENDED SELECT 1 FROM t1 t2, t1 GROUP BY t1.a) AS d; DROP TABLE t1; +--echo # +--echo # Bug #54476: crash when group_concat and 'with rollup' in prepared statements +--echo # ---echo End of 5.0 tests +CREATE TABLE t1 (a INT); +INSERT INTO t1 VALUES (1), (2); + +PREPARE stmt FROM "SELECT GROUP_CONCAT(t1.a ORDER BY t1.a) FROM t1 JOIN t1 t2 GROUP BY t1.a WITH ROLLUP"; +EXECUTE stmt; +EXECUTE stmt; + +DEALLOCATE PREPARE stmt; +DROP TABLE t1; + +--echo End of 5.1 tests diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 228e36fc9f9..1048bd3d6ff 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -3034,7 +3034,6 @@ Item_func_group_concat::Item_func_group_concat(THD *thd, tree(item->tree), unique_filter(item->unique_filter), table(item->table), - order(item->order), context(item->context), arg_count_order(item->arg_count_order), arg_count_field(item->arg_count_field), @@ -3047,6 +3046,24 @@ Item_func_group_concat::Item_func_group_concat(THD *thd, { quick_group= item->quick_group; result.set_charset(collation.collation); + + /* + Since the ORDER structures pointed to by the elements of the 'order' array + may be modified in find_order_in_list() called from + Item_func_group_concat::setup(), create a copy of those structures so that + such modifications done in this object would not have any effect on the + object being copied. + */ + ORDER *tmp; + if (!(order= (ORDER **) thd->alloc(sizeof(ORDER *) * arg_count_order + + sizeof(ORDER) * arg_count_order))) + return; + tmp= (ORDER *)(order + arg_count_order); + for (uint i= 0; i < arg_count_order; i++, tmp++) + { + memcpy(tmp, item->order[i], sizeof(ORDER)); + order[i]= tmp; + } } diff --git a/sql/table.h b/sql/table.h index 3ef3c5e0cb2..9088b3b6965 100644 --- a/sql/table.h +++ b/sql/table.h @@ -55,7 +55,6 @@ typedef struct st_order { struct st_order *next; Item **item; /* Point at item in select fields */ Item *item_ptr; /* Storage for initial item */ - Item **item_copy; /* For SPs; the original item ptr */ int counter; /* position in SELECT list, correct only if counter_used is true*/ bool asc; /* true if ascending */ From 80aa8824971de3e5524537e30175b2390d0570db Mon Sep 17 00:00:00 2001 From: Gleb Shchepa Date: Sun, 1 Aug 2010 22:12:36 +0400 Subject: [PATCH 05/28] Bug #54461: crash with longblob and union or update with subquery Queries may crash, if 1) the GREATEST or the LEAST function has a mixed list of numeric and LONGBLOB arguments and 2) the result of such a function goes through an intermediate temporary table. An Item that references a LONGBLOB field has max_length of UINT_MAX32 == (2^32 - 1). The current implementation of GREATEST/LEAST returns REAL result for a mixed list of numeric and string arguments (that contradicts with the current documentation, this contradiction was discussed and it was decided to update the documentation). The max_length of such a function call was calculated as a maximum of argument max_length values (i.e. UINT_MAX32). That max_length value of UINT_MAX32 was used as a length for the intermediate temporary table Field_double to hold GREATEST/LEAST function result. The Field_double::val_str() method call on that field allocates a String value. Since an allocation of String reserves an additional byte for a zero-termination, the size of String buffer was set to (UINT_MAX32 + 1), that caused an integer overflow: actually, an empty buffer of size 0 was allocated. An initialization of the "first" byte of that zero-size buffer with '\0' caused a crash. The Item_func_min_max::fix_length_and_dec() has been modified to calculate max_length for the REAL result like we do it for arithmetical operators. ****** Bug #54461: crash with longblob and union or update with subquery Queries may crash, if 1) the GREATEST or the LEAST function has a mixed list of numeric and LONGBLOB arguments and 2) the result of such a function goes through an intermediate temporary table. An Item that references a LONGBLOB field has max_length of UINT_MAX32 == (2^32 - 1). The current implementation of GREATEST/LEAST returns REAL result for a mixed list of numeric and string arguments (that contradicts with the current documentation, this contradiction was discussed and it was decided to update the documentation). The max_length of such a function call was calculated as a maximum of argument max_length values (i.e. UINT_MAX32). That max_length value of UINT_MAX32 was used as a length for the intermediate temporary table Field_double to hold GREATEST/LEAST function result. The Field_double::val_str() method call on that field allocates a String value. Since an allocation of String reserves an additional byte for a zero-termination, the size of String buffer was set to (UINT_MAX32 + 1), that caused an integer overflow: actually, an empty buffer of size 0 was allocated. An initialization of the "first" byte of that zero-size buffer with '\0' caused a crash. The Item_func_min_max::fix_length_and_dec() has been modified to calculate max_length for the REAL result like we do it for arithmetical operators. mysql-test/r/func_misc.result: Test case for bug #54461. ****** Test case for bug #54461. mysql-test/t/func_misc.test: Test case for bug #54461. ****** Test case for bug #54461. sql/item_func.cc: Bug #54461: crash with longblob and union or update with subquery The Item_func_min_max::fix_length_and_dec() has been modified to calculate max_length for the REAL result like we do it for arithmetical operators. ****** Bug #54461: crash with longblob and union or update with subquery The Item_func_min_max::fix_length_and_dec() has been modified to calculate max_length for the REAL result like we do it for arithmetical operators. --- mysql-test/r/func_misc.result | 15 +++++++++++++++ mysql-test/t/func_misc.test | 12 ++++++++++++ sql/item_func.cc | 2 ++ 3 files changed, 29 insertions(+) diff --git a/mysql-test/r/func_misc.result b/mysql-test/r/func_misc.result index 81dddd0f648..eee56ae7461 100644 --- a/mysql-test/r/func_misc.result +++ b/mysql-test/r/func_misc.result @@ -336,4 +336,19 @@ End of 5.0 tests select connection_id() > 0; connection_id() > 0 1 +# +# Bug #54461: crash with longblob and union or update with subquery +# +CREATE TABLE t1 (a INT, b LONGBLOB); +INSERT INTO t1 VALUES (1, '2'), (2, '3'), (3, '2'); +SELECT DISTINCT LEAST(a, (SELECT b FROM t1 LIMIT 1)) FROM t1 UNION SELECT 1; +LEAST(a, (SELECT b FROM t1 LIMIT 1)) +1 +2 +SELECT DISTINCT GREATEST(a, (SELECT b FROM t1 LIMIT 1)) FROM t1 UNION SELECT 1; +GREATEST(a, (SELECT b FROM t1 LIMIT 1)) +2 +3 +1 +DROP TABLE t1; End of tests diff --git a/mysql-test/t/func_misc.test b/mysql-test/t/func_misc.test index 6590b43f2dc..c6b5ffd5a3f 100644 --- a/mysql-test/t/func_misc.test +++ b/mysql-test/t/func_misc.test @@ -467,4 +467,16 @@ select NAME_CONST('_id',1234) as id; select connection_id() > 0; +--echo # +--echo # Bug #54461: crash with longblob and union or update with subquery +--echo # + +CREATE TABLE t1 (a INT, b LONGBLOB); +INSERT INTO t1 VALUES (1, '2'), (2, '3'), (3, '2'); + +SELECT DISTINCT LEAST(a, (SELECT b FROM t1 LIMIT 1)) FROM t1 UNION SELECT 1; +SELECT DISTINCT GREATEST(a, (SELECT b FROM t1 LIMIT 1)) FROM t1 UNION SELECT 1; + +DROP TABLE t1; + --echo End of tests diff --git a/sql/item_func.cc b/sql/item_func.cc index 1bec4700bff..1b13297c951 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -2243,6 +2243,8 @@ void Item_func_min_max::fix_length_and_dec() max_length= my_decimal_precision_to_length_no_truncation(max_int_part + decimals, decimals, unsigned_flag); + else if (cmp_type == REAL_RESULT) + max_length= float_length(decimals); cached_field_type= agg_field_type(args, arg_count); } From e2729eb4d40df96082a9a0667f7dcd2f41fbfc01 Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Mon, 2 Aug 2010 10:48:24 +0300 Subject: [PATCH 06/28] tree name update --- .bzr-mysql/default.conf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.bzr-mysql/default.conf b/.bzr-mysql/default.conf index 4b2affc1529..f79c1cd6319 100644 --- a/.bzr-mysql/default.conf +++ b/.bzr-mysql/default.conf @@ -1,4 +1,4 @@ [MYSQL] -post_commit_to = "dbg_mysql_security@sun.com" -post_push_to = "dbg_mysql_security@sun.com" -tree_name = "mysql-5.0-security" +post_commit_to = "commits@lists.mysql.com" +post_push_to = "commits@lists.mysql.com" +tree_name = "mysql-5.0" From f62e89fade7cda645de80e2996de69e7d980cbdd Mon Sep 17 00:00:00 2001 From: Alfranio Correia Date: Mon, 2 Aug 2010 20:48:56 +0100 Subject: [PATCH 07/28] BUG#55625 RBR breaks on failing 'CREATE TABLE' A CREATE...SELECT that fails is written to the binary log if a non-transactional statement is updated. If the logging format is ROW, the CREATE statement and the changes are written to the binary log as distinct events and by consequence the created table is not rolled back in the slave. In this patch, we opted to let the slave goes out of sync by not writting to the binary log the CREATE statement. We do this by simply reseting the binary log's cache. mysql-test/suite/rpl/r/rpl_drop.result: Added a test case. mysql-test/suite/rpl/t/rpl_drop.test: Added a test case. sql/log.cc: Introduced a function to clean up the cache. sql/log.h: Introduced a function to clean up the cache. sql/sql_insert.cc: Cleaned up the binary log cache if a CREATE...SELECT fails. --- mysql-test/suite/rpl/r/rpl_drop.result | 24 ++++++++++++ mysql-test/suite/rpl/t/rpl_drop.test | 53 +++++++++++++++++++++++++- sql/log.cc | 13 +++++++ sql/log.h | 3 +- sql/sql_insert.cc | 11 ++++++ 5 files changed, 102 insertions(+), 2 deletions(-) diff --git a/mysql-test/suite/rpl/r/rpl_drop.result b/mysql-test/suite/rpl/r/rpl_drop.result index b83594c9bb1..5ebbc4f9ce7 100644 --- a/mysql-test/suite/rpl/r/rpl_drop.result +++ b/mysql-test/suite/rpl/r/rpl_drop.result @@ -8,3 +8,27 @@ drop table if exists t1, t2; create table t1 (a int); drop table t1, t2; ERROR 42S02: Unknown table 't2' +include/stop_slave.inc +SET @old_binlog_format= @@global.binlog_format; +SET GLOBAL binlog_format = ROW; +include/start_slave.inc +SET @old_binlog_format= @@global.binlog_format; +SET binlog_format = ROW; +CREATE TABLE t2(a INT) ENGINE=MYISAM; +CREATE TABLE t3(a INT) ENGINE=INNODB; +CREATE FUNCTION f1() RETURNS INT +BEGIN +insert into t2 values(1); +insert into t3 values(1); +return 1; +END| +CREATE TABLE t1(UNIQUE(a)) ENGINE=MYISAM SELECT 1 AS a UNION ALL SELECT f1(); +ERROR 23000: Duplicate entry '1' for key 'a' +CREATE TABLE t1(UNIQUE(a)) ENGINE=INNODB SELECT 1 AS a UNION ALL SELECT f1(); +ERROR 23000: Duplicate entry '1' for key 'a' +show binlog events in 'master-bin.000001' from ; +Log_name Pos Event_type Server_id End_log_pos Info +DROP FUNCTION f1; +DROP TABLE t2, t3; +SET @@global.binlog_format= @old_binlog_format; +SET @@global.binlog_format= @old_binlog_format; diff --git a/mysql-test/suite/rpl/t/rpl_drop.test b/mysql-test/suite/rpl/t/rpl_drop.test index b38007a755f..336edad6fc5 100644 --- a/mysql-test/suite/rpl/t/rpl_drop.test +++ b/mysql-test/suite/rpl/t/rpl_drop.test @@ -1,6 +1,7 @@ # Testcase for BUG#4552 (DROP on two tables, one of which does not # exist, must be binlogged with a non-zero error code) source include/master-slave.inc; +source include/have_innodb.inc; --disable_warnings drop table if exists t1, t2; --enable_warnings @@ -10,7 +11,57 @@ drop table t1, t2; save_master_pos; connection slave; sync_with_master; - # End of 4.1 tests +# BUG#55625 RBR breaks on failing 'CREATE TABLE' +# A CREATE...SELECT that fails is written to the binary log if a non-transactional +# statement is updated. If the logging format is ROW, the CREATE statement and the +# changes are written to the binary log as distinct events and by consequence the +# created table is not rolled back in the slave. +# To fix the problem, we do not write a CREATE...SELECT that fails to the binary +# log. Howerver, the changes to non-transactional tables are not replicated and +# thus the slave goes out of sync. This should be fixed after BUG#47899. +# +# In the test case, we verify if the binary log contains no information for a +# CREATE...SELECT that fails. +connection slave; +--source include/stop_slave.inc +SET @old_binlog_format= @@global.binlog_format; +SET GLOBAL binlog_format = ROW; +--source include/start_slave.inc + +connection master; +SET @old_binlog_format= @@global.binlog_format; +SET binlog_format = ROW; + +CREATE TABLE t2(a INT) ENGINE=MYISAM; +CREATE TABLE t3(a INT) ENGINE=INNODB; + +delimiter |; +CREATE FUNCTION f1() RETURNS INT +BEGIN + insert into t2 values(1); + insert into t3 values(1); + return 1; +END| +delimiter ;| + +let $binlog_start= query_get_value("SHOW MASTER STATUS", Position, 1); +let $binlog_file= query_get_value("SHOW MASTER STATUS", File, 1); + +--error 1062 +CREATE TABLE t1(UNIQUE(a)) ENGINE=MYISAM SELECT 1 AS a UNION ALL SELECT f1(); +--error 1062 +CREATE TABLE t1(UNIQUE(a)) ENGINE=INNODB SELECT 1 AS a UNION ALL SELECT f1(); + +--source include/show_binlog_events.inc + +DROP FUNCTION f1; +DROP TABLE t2, t3; +SET @@global.binlog_format= @old_binlog_format; + +--sync_slave_with_master +SET @@global.binlog_format= @old_binlog_format; + +# End of 5.1 tests diff --git a/sql/log.cc b/sql/log.cc index 614a07e6b63..3f41bf1c929 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -1628,6 +1628,19 @@ static int binlog_rollback(handlerton *hton, THD *thd, bool all) DBUG_RETURN(error); } +/** + Cleanup the cache. + + @param thd The client thread that wants to clean up the cache. +*/ +void MYSQL_BIN_LOG::reset_gathered_updates(THD *thd) +{ + binlog_trx_data *const trx_data= + (binlog_trx_data*) thd_get_ha_data(thd, binlog_hton); + + trx_data->reset(); +} + void MYSQL_BIN_LOG::set_write_error(THD *thd) { DBUG_ENTER("MYSQL_BIN_LOG::set_write_error"); diff --git a/sql/log.h b/sql/log.h index 8d3880d9171..8f1ed7ee90c 100644 --- a/sql/log.h +++ b/sql/log.h @@ -356,10 +356,11 @@ public: /* Use this to start writing a new log file */ void new_file(); + void reset_gathered_updates(THD *thd); bool write(Log_event* event_info); // binary log write bool write(THD *thd, IO_CACHE *cache, Log_event *commit_event, bool incident); - bool write_incident(THD *thd, bool lock); + bool write_incident(THD *thd, bool lock); int write_cache(IO_CACHE *cache, bool lock_log, bool flush_and_sync); void set_write_error(THD *thd); bool check_write_error(THD *thd); diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 35c24e7571e..83b1834da0b 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -3873,6 +3873,17 @@ void select_create::abort() if (table) { + if (thd->lex->sql_command == SQLCOM_CREATE_TABLE && + thd->current_stmt_binlog_row_based && + !(thd->lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) && + mysql_bin_log.is_open()) + { + /* + This should be removed after BUG#47899. + */ + mysql_bin_log.reset_gathered_updates(thd); + } + table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE); if (!create_info->table_existed) From 790852c0c91df8bf104687753c019ceefaed6622 Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Fri, 13 Aug 2010 11:07:39 +0300 Subject: [PATCH 08/28] Bug #55580 : segfault in read_view_sees_trx_id The server was not checking for errors generated during the execution of Item::val_xxx() methods when copying data to the group, order, or distinct temp table's row. Fixed by extending the copy_funcs() to return an error code and by checking for that error code on the places copy_funcs() is called. Test case added. --- mysql-test/suite/innodb/r/innodb_mysql.result | 22 ++++++++++ mysql-test/suite/innodb/t/innodb_mysql.test | 35 +++++++++++++++ sql/item_sum.cc | 6 ++- sql/sql_select.cc | 44 ++++++++++++++++--- sql/sql_select.h | 2 +- 5 files changed, 99 insertions(+), 10 deletions(-) diff --git a/mysql-test/suite/innodb/r/innodb_mysql.result b/mysql-test/suite/innodb/r/innodb_mysql.result index ba37a46b62a..247f461c760 100644 --- a/mysql-test/suite/innodb/r/innodb_mysql.result +++ b/mysql-test/suite/innodb/r/innodb_mysql.result @@ -2499,4 +2499,26 @@ ORDER BY f1 DESC LIMIT 5; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 range f2,f4 f4 1 NULL 11 Using where DROP TABLE t1; +# +# Bug#55580: segfault in read_view_sees_trx_id +# +CREATE TABLE t1 (a INT) ENGINE=Innodb; +CREATE TABLE t2 (a INT) ENGINE=Innodb; +INSERT INTO t1 VALUES (1),(2); +INSERT INTO t2 VALUES (1),(2); +START TRANSACTION; +SELECT * FROM t2 LOCK IN SHARE MODE; +a +1 +2 +START TRANSACTION; +SELECT * FROM t1 LOCK IN SHARE MODE; +a +1 +2 +SELECT * FROM t1 FOR UPDATE; +# should not crash +SELECT * FROM t1 GROUP BY (SELECT a FROM t2 LIMIT 1 FOR UPDATE) + t1.a; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +DROP TABLE t1,t2; End of 5.1 tests diff --git a/mysql-test/suite/innodb/t/innodb_mysql.test b/mysql-test/suite/innodb/t/innodb_mysql.test index d3405d1adb4..25e834cf3f8 100644 --- a/mysql-test/suite/innodb/t/innodb_mysql.test +++ b/mysql-test/suite/innodb/t/innodb_mysql.test @@ -733,4 +733,39 @@ ORDER BY f1 DESC LIMIT 5; DROP TABLE t1; +--echo # +--echo # Bug#55580: segfault in read_view_sees_trx_id +--echo # + +CREATE TABLE t1 (a INT) ENGINE=Innodb; +CREATE TABLE t2 (a INT) ENGINE=Innodb; +INSERT INTO t1 VALUES (1),(2); +INSERT INTO t2 VALUES (1),(2); + +connect (con1,localhost,root,,test); +connect (con2,localhost,root,,test); + +connection con1; +START TRANSACTION; +SELECT * FROM t2 LOCK IN SHARE MODE; + +connection con2; +START TRANSACTION; +SELECT * FROM t1 LOCK IN SHARE MODE; + +connection con1; +--send SELECT * FROM t1 FOR UPDATE + +connection con2; +--echo # should not crash +--error ER_LOCK_DEADLOCK +SELECT * FROM t1 GROUP BY (SELECT a FROM t2 LIMIT 1 FOR UPDATE) + t1.a; + +connection default; +disconnect con1; +disconnect con2; + +DROP TABLE t1,t2; + + --echo End of 5.1 tests diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 228e36fc9f9..f92bde9ce87 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -2556,7 +2556,8 @@ bool Item_sum_count_distinct::add() if (always_null) return 0; copy_fields(tmp_table_param); - copy_funcs(tmp_table_param->items_to_copy); + if (copy_funcs(tmp_table_param->items_to_copy, table->in_use)) + return TRUE; for (Field **field=table->field ; *field ; field++) if ((*field)->is_real_null(0)) @@ -3128,7 +3129,8 @@ bool Item_func_group_concat::add() if (always_null) return 0; copy_fields(tmp_table_param); - copy_funcs(tmp_table_param->items_to_copy); + if (copy_funcs(tmp_table_param->items_to_copy, table->in_use)) + return TRUE; for (uint i= 0; i < arg_count_field; i++) { diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 4bb0d3a9610..7ee1762295f 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -12487,7 +12487,9 @@ end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), if (!end_of_records) { copy_fields(&join->tmp_table_param); - copy_funcs(join->tmp_table_param.items_to_copy); + if (copy_funcs(join->tmp_table_param.items_to_copy, join->thd)) + DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */ + #ifdef TO_BE_DELETED if (!table->uniques) // If not unique handling { @@ -12593,7 +12595,8 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), memcpy(table->record[0]+key_part->offset, group->buff, 1); } init_tmptable_sum_functions(join->sum_funcs); - copy_funcs(join->tmp_table_param.items_to_copy); + if (copy_funcs(join->tmp_table_param.items_to_copy, join->thd)) + DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */ if ((error=table->file->ha_write_row(table->record[0]))) { if (create_myisam_from_heap(join->thd, table, &join->tmp_table_param, @@ -12628,7 +12631,8 @@ end_unique_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), init_tmptable_sum_functions(join->sum_funcs); copy_fields(&join->tmp_table_param); // Groups are copied twice. - copy_funcs(join->tmp_table_param.items_to_copy); + if (copy_funcs(join->tmp_table_param.items_to_copy, join->thd)) + DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */ if (!(error=table->file->ha_write_row(table->record[0]))) join->send_records++; // New group @@ -12715,7 +12719,8 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), if (idx < (int) join->send_group_parts) { copy_fields(&join->tmp_table_param); - copy_funcs(join->tmp_table_param.items_to_copy); + if (copy_funcs(join->tmp_table_param.items_to_copy, join->thd)) + DBUG_RETURN(NESTED_LOOP_ERROR); if (init_sum_functions(join->sum_funcs, join->sum_funcs_end[idx+1])) DBUG_RETURN(NESTED_LOOP_ERROR); if (join->procedure) @@ -15775,14 +15780,39 @@ update_sum_func(Item_sum **func_ptr) return 0; } -/** Copy result of functions to record in tmp_table. */ +/** + Copy result of functions to record in tmp_table. -void -copy_funcs(Item **func_ptr) + Uses the thread pointer to check for errors in + some of the val_xxx() methods called by the + save_in_result_field() function. + TODO: make the Item::val_xxx() return error code + + @param func_ptr array of the function Items to copy to the tmp table + @param thd pointer to the current thread for error checking + @retval + FALSE if OK + @retval + TRUE on error +*/ + +bool +copy_funcs(Item **func_ptr, const THD *thd) { Item *func; for (; (func = *func_ptr) ; func_ptr++) + { func->save_in_result_field(1); + /* + Need to check the THD error state because Item::val_xxx() don't + return error code, but can generate errors + TODO: change it for a real status check when Item::val_xxx() + are extended to return status code. + */ + if (thd->is_error()) + return TRUE; + } + return FALSE; } diff --git a/sql/sql_select.h b/sql/sql_select.h index b39827ef61b..fbe23bbd8fe 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -601,7 +601,7 @@ bool setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param, List &new_list1, List &new_list2, uint elements, List &fields); void copy_fields(TMP_TABLE_PARAM *param); -void copy_funcs(Item **func_ptr); +bool copy_funcs(Item **func_ptr, const THD *thd); bool create_myisam_from_heap(THD *thd, TABLE *table, TMP_TABLE_PARAM *param, int error, bool ignore_last_dupp_error); uint find_shortest_key(TABLE *table, const key_map *usable_keys); From 8b25c0e4dc6cb18de7ce4be25eb49c44eeab35cf Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Fri, 13 Aug 2010 16:05:46 +0300 Subject: [PATCH 09/28] Bug #55565: debug assertion when ordering by expressions with user variable assignments The assert() that is firing is checking if expressions that can't be null return a NULL when evaluated. MAKEDATE() function can return NULL if the second argument is less then or equal to 0. Thus its nullability depends not only on the nullability of its arguments but also on their values. Fixed by (overoptimistically) setting MAKEDATE() to be nullable despite the nullability of its arguments. Test added. Had to update one test result to reflect the metadata change. --- mysql-test/r/func_sapdb.result | 2 +- mysql-test/r/func_time.result | 8 ++++++++ mysql-test/t/func_time.test | 11 +++++++++++ sql/item_timefunc.h | 2 ++ 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/func_sapdb.result b/mysql-test/r/func_sapdb.result index bbc5390895b..87b88692a34 100644 --- a/mysql-test/r/func_sapdb.result +++ b/mysql-test/r/func_sapdb.result @@ -194,7 +194,7 @@ date("1997-12-31 23:59:59.000001") as f8, time("1997-12-31 23:59:59.000001") as f9; describe t1; Field Type Null Key Default Extra -f1 date NO 0000-00-00 +f1 date YES NULL f2 datetime YES NULL f3 time YES NULL f4 time YES NULL diff --git a/mysql-test/r/func_time.result b/mysql-test/r/func_time.result index 93317020c5b..4ff4cfa586b 100644 --- a/mysql-test/r/func_time.result +++ b/mysql-test/r/func_time.result @@ -1335,4 +1335,12 @@ date_sub("0069-01-01 00:00:01",INTERVAL 2 SECOND) select date_sub("0169-01-01 00:00:01",INTERVAL 2 SECOND); date_sub("0169-01-01 00:00:01",INTERVAL 2 SECOND) 0168-12-31 23:59:59 +CREATE TABLE t1(a DOUBLE NOT NULL); +INSERT INTO t1 VALUES (0),(9.216e-096); +# should not crash +SELECT 1 FROM t1 ORDER BY @x:=makedate(a,a); +1 +1 +1 +DROP TABLE t1; End of 5.1 tests diff --git a/mysql-test/t/func_time.test b/mysql-test/t/func_time.test index b9da946a55f..e764906c374 100644 --- a/mysql-test/t/func_time.test +++ b/mysql-test/t/func_time.test @@ -838,4 +838,15 @@ select date_sub("0069-01-01 00:00:01",INTERVAL 2 SECOND); select date_sub("0169-01-01 00:00:01",INTERVAL 2 SECOND); +# +# Bug #55565: debug assertion when ordering by expressions with user +# variable assignments +# + +CREATE TABLE t1(a DOUBLE NOT NULL); +INSERT INTO t1 VALUES (0),(9.216e-096); +--echo # should not crash +SELECT 1 FROM t1 ORDER BY @x:=makedate(a,a); +DROP TABLE t1; + --echo End of 5.1 tests diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index a7a64090f6c..11eed70f399 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -881,6 +881,8 @@ public: { decimals=0; max_length=MAX_DATE_WIDTH*MY_CHARSET_BIN_MB_MAXLEN; + /* It returns NULL when the second argument is less or equal to 0 */ + maybe_null= 1; } longlong val_int(); }; From b8dde1ef402cb82cb48177718437cab019c65d04 Mon Sep 17 00:00:00 2001 From: Mats Kindahl Date: Tue, 17 Aug 2010 07:46:53 +0200 Subject: [PATCH 10/28] WL#5363: Thread pool interface Patch to make Visual C++ happy. --- sql/mysqld.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/mysqld.h b/sql/mysqld.h index 2dc854134c7..6135fcead8b 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -28,7 +28,7 @@ class THD; struct handlerton; class Time_zone; -class scheduler_functions; +struct scheduler_functions; typedef struct st_mysql_const_lex_string LEX_CSTRING; typedef struct st_mysql_show_var SHOW_VAR; From 9d6811502ed22f7b4aa99e2be1d5c8ac45792790 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 18 Aug 2010 12:56:06 +0800 Subject: [PATCH 11/28] WL#5370 Keep forward-compatibility when changing 'CREATE TABLE IF NOT EXISTS ... SELECT' behaviour BUG#55474, BUG#55499, BUG#55598, BUG#55616 and BUG#55777 are fixed in this patch too. This is the 5.1 part. It implements: - if the table exists, binlog two events: CREATE TABLE IF NOT EXISTS and INSERT ... SELECT - Insert nothing and binlog nothing on master if the existing object is a view. It only generates a warning that table already exists. mysql-test/r/trigger.result: Ather this patch, 'CREATE TABLE IF NOT EXISTS ... SELECT' will not insert anything if the creating table already exists and is a view. sql/sql_class.h: Declare virtual function write_to_binlog() for select_insert. It's used to binlog 'create select' sql/sql_insert.cc: Implement write_to_binlog(); Use write_to_binlog() instead of binlog_query() to binlog the statement. if the table exists, binlog two events: CREATE TABLE IF NOT EXISTS and INSERT ... SELECT sql/sql_lex.h: Declare create_select_start_with_brace and create_select_pos. They are helpful for binlogging 'create select' sql/sql_parse.cc: Do nothing on master if the existing object is a view. sql/sql_yacc.yy: Record the relative postion of 'SELECT' in the 'CREATE ...SELECT' statement. Record whether there is a '(' before the 'SELECT' clause. --- .../rpl_stm_create_if_not_exists.test | 235 ++++++ mysql-test/include/rpl_diff_tables.inc | 1 + mysql-test/r/trigger.result | 8 - .../rpl/r/rpl_create_if_not_exists.result | 9 + .../rpl_create_tmp_table_if_not_exists.result | 7 +- .../suite/rpl/r/rpl_row_create_table.result | 6 + .../rpl/r/rpl_stm_create_if_not_exists.result | 704 ++++++++++++++++++ .../suite/rpl/t/rpl_create_if_not_exists.test | 27 + .../suite/rpl/t/rpl_row_create_table.test | 13 + .../rpl/t/rpl_stm_create_if_not_exists.test | 14 + sql/sql_class.h | 8 +- sql/sql_insert.cc | 122 ++- sql/sql_lex.h | 17 + sql/sql_parse.cc | 19 + sql/sql_yacc.yy | 28 +- 15 files changed, 1191 insertions(+), 27 deletions(-) create mode 100644 mysql-test/extra/rpl_tests/rpl_stm_create_if_not_exists.test create mode 100644 mysql-test/suite/rpl/r/rpl_stm_create_if_not_exists.result create mode 100644 mysql-test/suite/rpl/t/rpl_stm_create_if_not_exists.test diff --git a/mysql-test/extra/rpl_tests/rpl_stm_create_if_not_exists.test b/mysql-test/extra/rpl_tests/rpl_stm_create_if_not_exists.test new file mode 100644 index 00000000000..65d2483dc4b --- /dev/null +++ b/mysql-test/extra/rpl_tests/rpl_stm_create_if_not_exists.test @@ -0,0 +1,235 @@ +--echo +--echo +connection master; + +if ($is_temporary) +{ + --let $_temporary=TEMPORARY +} + +CREATE TABLE t2(c1 INT, c2 char(10)); +INSERT INTO t2 VALUES(1, 'abc'), (2, 'abc'); + +--echo +--echo # The original query should be binlogged if the table does not exist. +--echo # ------------------------------------------------------------------ +--echo +let binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1); +eval CREATE $_temporary TABLE IF NOT EXISTS t1 (c1 INT , c2 INT, c3 char(10), c4 INT KEY) + SELECT 'abc' AS c3, 1 AS c4; +source include/show_binlog_events.inc; + +if (!$is_temporary) +{ + let $diff_table= test.t1; + source include/rpl_diff_tables.inc; +} + +--echo +--echo # The statement should be binlogged as two events. one is +--echo # 'CREATE $_temporary TABLE IF NOT EXISTS ..', another one is +--echo # 'INSERT ... SELECT'. +--echo # ------------------------------------------------------------------ +--echo +let binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1); +eval CREATE $_temporary TABLE IF NOT EXISTS t1 + SELECT 'abc', 2; +source include/show_binlog_events.inc; + +if (!$is_temporary) +{ + let $diff_table= test.t1; + source include/rpl_diff_tables.inc; +} + +--echo +--echo # Verify if it can be binlogged with right database name when the table +--echo # is not in the default database +--echo +--disable_warnings +DROP DATABASE IF EXISTS db1; +--enable_warnings +CREATE DATABASE db1; +USE db1; + +let binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1); +eval CREATE $_temporary TABLE IF NOT EXISTS test.t1 + SELECT 'abc', 20; +source include/show_binlog_events.inc; + +if (!$is_temporary) +{ + let $diff_table= test.t1; + source include/rpl_diff_tables.inc; +} +USE test; +DROP DATABASE db1; + +--echo +--echo # It should be binlogged as 'REPLACE ... SELECT' +--echo # if the original statement has option REPLACE +--echo +let binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1); +eval CREATE $_temporary TABLE IF NOT EXISTS t1 + REPLACE SELECT '123', 2; +source include/show_binlog_events.inc; + +if (!$is_temporary) +{ + let $diff_table= test.t1; + source include/rpl_diff_tables.inc; +} + +--echo +--echo # It should be binlogged as 'INSERT IGNORE... SELECT' +--echo # if the original statement has option IGNORE +--echo +let binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1); +eval CREATE $_temporary TABLE IF NOT EXISTS t1 + IGNORE SELECT '123', 2; +source include/show_binlog_events.inc; + +if (!$is_temporary) +{ + let $diff_table= test.t1; + source include/rpl_diff_tables.inc; +} + +--echo +--echo # Nothing should be binlogged if error happens and no any row is inserted +--echo +let binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1); +--error ER_DUP_ENTRY +eval CREATE $_temporary TABLE IF NOT EXISTS t1 + SELECT '123', 2; +source include/show_binlog_events.inc; +if (!$is_temporary) +{ + let $diff_table= test.t1; + source include/rpl_diff_tables.inc; +} + +--echo +--echo # Verify it can binlog well when there are some braces('(') +--echo +let binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1); +eval CREATE $_temporary TABLE IF NOT EXISTS t1 + (SELECT '123', 3) UNION (SELECT '123', 4); +eval CREATE $_temporary TABLE IF NOT EXISTS t1 + REPLACE (SELECT 'abc', 3) UNION (SELECT 'abc', 4); +eval CREATE $_temporary TABLE IF NOT EXISTS t1 + IGNORE (SELECT '123', 3) UNION (SELECT '123', 4); +source include/show_binlog_events.inc; + +if (!$is_temporary) +{ + let $diff_table= test.t1; + source include/rpl_diff_tables.inc; +} + +if (!$is_temporary) +{ + --echo + --echo # Throw a warning that table already exists and don't insert anything + --echo + CREATE VIEW t3 AS SELECT * FROM t2; + let binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1); + + CREATE TABLE IF NOT EXISTS t3 + SELECT '123', 2; + source include/show_binlog_events.inc; + DROP VIEW t3; +} + +--echo +--echo # The statement can be binlogged correctly when it is in a SP/EVENT/TRIGGER +--echo + +--disable_warnings +DROP PROCEDURE IF EXISTS p1; +--enable_warnings +eval CREATE PROCEDURE p1(IN a INT) + CREATE $_temporary TABLE IF NOT EXISTS t1 SELECT '123', a; + +let binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1); +call p1(500); +call p1(600); +source include/show_binlog_events.inc; +if (!$is_temporary) +{ + let $diff_table= test.t1; + source include/rpl_diff_tables.inc; +} +DROP PROCEDURE p1; + +--echo +--echo # The statement can be binlogged correctly when it is in a prepared statement +--echo +eval PREPARE stm FROM "CREATE $_temporary TABLE IF NOT EXISTS t1 SELECT '123', ?"; + +let binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1); +SET @a= 700; +EXECUTE stm USING @a; +SET @a= 800; +EXECUTE stm USING @a; +source include/show_binlog_events.inc; +if (!$is_temporary) +{ + let $diff_table= test.t1; + source include/rpl_diff_tables.inc; +} + +--echo +--echo # The statement can be binlogged correctly when it is in a conditional comment +--echo +let binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1); + +--echo # The whole statement in a conditional comment +eval /*!CREATE $_temporary TABLE IF NOT EXISTS t1 + SELECT 'abc', 900*/; +source include/show_binlog_events.inc; +let binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1); + +--echo +--echo # There is an long comment before SELECT +eval /*!CREATE $_temporary /*blabla*/ TABLE IF NOT EXISTS t1 + SELECT 'abc', 901*/; +source include/show_binlog_events.inc; +let binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1); + +--echo +--echo # Conditional comment starts just from SELECT +eval CREATE $_temporary TABLE IF NOT EXISTS t1 + /*!SELECT 'abc',*/ 902; +source include/show_binlog_events.inc; +let binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1); + +--echo +--echo # Only SELECT keyword is in the conditional comment +eval CREATE $_temporary TABLE IF NOT EXISTS t1 + /*!SELECT*/ /*!'abc',*/ 904; +source include/show_binlog_events.inc; +let binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1); + +--echo +--echo # Conditional comment is after SELECT keyword +eval CREATE $_temporary TABLE IF NOT EXISTS t1 + SELECT /*!'abc',*/ 903; +source include/show_binlog_events.inc; +let binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1); + +--echo +--echo # Conditional comment ends just before SELECT keyword +eval /*!CREATE $_temporary TABLE IF NOT EXISTS t1 + */SELECT 'abc', 905; +source include/show_binlog_events.inc; + +if (!$is_temporary) +{ + let $diff_table= test.t1; + source include/rpl_diff_tables.inc; +} + +DROP TABLE t2; +eval DROP $_temporary TABLE t1; + diff --git a/mysql-test/include/rpl_diff_tables.inc b/mysql-test/include/rpl_diff_tables.inc index c3a45578a79..7fc68422c40 100644 --- a/mysql-test/include/rpl_diff_tables.inc +++ b/mysql-test/include/rpl_diff_tables.inc @@ -33,3 +33,4 @@ while (`SELECT "XX$_servers" <> "XX"`) --source include/diff_tables.inc connection $_slave; } +connection $_master; diff --git a/mysql-test/r/trigger.result b/mysql-test/r/trigger.result index e3c0b0e1dd9..16b165cdc11 100644 --- a/mysql-test/r/trigger.result +++ b/mysql-test/r/trigger.result @@ -1824,11 +1824,8 @@ Note 1050 Table 'v1' already exists set @id=last_insert_id(); select * from t1; id operation -1 CREATE TABLE ... SELECT, inserting a new key select * from t1_op_log; operation -Before INSERT, new=CREATE TABLE ... SELECT, inserting a new key -After INSERT, new=CREATE TABLE ... SELECT, inserting a new key truncate t1_op_log; create table if not exists v1 replace select @id, "CREATE TABLE ... REPLACE SELECT, deleting a duplicate key"; @@ -1836,13 +1833,8 @@ Warnings: Note 1050 Table 'v1' already exists select * from t1; id operation -1 CREATE TABLE ... REPLACE SELECT, deleting a duplicate key select * from t1_op_log; operation -Before INSERT, new=CREATE TABLE ... REPLACE SELECT, deleting a duplicate key -Before DELETE, old=CREATE TABLE ... SELECT, inserting a new key -After DELETE, old=CREATE TABLE ... SELECT, inserting a new key -After INSERT, new=CREATE TABLE ... REPLACE SELECT, deleting a duplicate key truncate t1; truncate t1_op_log; insert into v1 (id, operation) diff --git a/mysql-test/suite/rpl/r/rpl_create_if_not_exists.result b/mysql-test/suite/rpl/r/rpl_create_if_not_exists.result index fc53aca5136..e3cddf4c606 100644 --- a/mysql-test/suite/rpl/r/rpl_create_if_not_exists.result +++ b/mysql-test/suite/rpl/r/rpl_create_if_not_exists.result @@ -65,3 +65,12 @@ c1 DROP TABLE t1; DROP TABLE t2; DROP TABLE t3; + +# Bug#55616 Killing thread or query during CREATE IF NOT EXISTS makes +# slave SQL thread abort + +CREATE TABLE t1 ( i INT ); +CREATE TABLE IF NOT EXISTS t1 +AS SELECT SLEEP(3); +KILL QUERY master1; +DROP TABLE t1; diff --git a/mysql-test/suite/rpl/r/rpl_create_tmp_table_if_not_exists.result b/mysql-test/suite/rpl/r/rpl_create_tmp_table_if_not_exists.result index 8d0b61cc6d8..de44a1e5d2a 100644 --- a/mysql-test/suite/rpl/r/rpl_create_tmp_table_if_not_exists.result +++ b/mysql-test/suite/rpl/r/rpl_create_tmp_table_if_not_exists.result @@ -19,4 +19,9 @@ master-bin.000001 # Query # # use `test`; CREATE TEMPORARY TABLE IF NOT EXISTS t master-bin.000001 # Query # # use `test`; CREATE TEMPORARY TABLE IF NOT EXISTS tmp1 LIKE tmp master-bin.000001 # Query # # use `test`; CREATE TEMPORARY TABLE IF NOT EXISTS tmp1 LIKE tmp master-bin.000001 # Query # # use `test`; CREATE TEMPORARY TABLE IF NOT EXISTS tmp2 SELECT * FROM tmp -master-bin.000001 # Query # # use `test`; CREATE TEMPORARY TABLE IF NOT EXISTS tmp2 SELECT * FROM tmp +master-bin.000001 # Query # # BEGIN +master-bin.000001 # Query # # use `test`; CREATE TEMPORARY TABLE IF NOT EXISTS `tmp2` ( + `c1` int(11) DEFAULT NULL +) +master-bin.000001 # Query # # use `test`; INSERT INTO `test`.`tmp2` (`c1`) SELECT * FROM tmp +master-bin.000001 # Query # # COMMIT diff --git a/mysql-test/suite/rpl/r/rpl_row_create_table.result b/mysql-test/suite/rpl/r/rpl_row_create_table.result index e480ff3dfe9..c6aa05f9e90 100644 --- a/mysql-test/suite/rpl/r/rpl_row_create_table.result +++ b/mysql-test/suite/rpl/r/rpl_row_create_table.result @@ -467,4 +467,10 @@ DROP VIEW IF EXISTS bug48506_t1, bug48506_t2, bug48506_t3; DROP TEMPORARY TABLES t7; DROP TABLES t4, t5; DROP TABLES IF EXISTS bug48506_t4; +CREATE TABLE t1 SELECT 1; +CREATE TABLE IF NOT EXISTS t1 SELECT 1; +Warnings: +Note 1050 Table 't1' already exists +Comparing tables master:test.t1 and slave:test.t1 +DROP TABLE t1; end of the tests diff --git a/mysql-test/suite/rpl/r/rpl_stm_create_if_not_exists.result b/mysql-test/suite/rpl/r/rpl_stm_create_if_not_exists.result new file mode 100644 index 00000000000..9ae1ef315b6 --- /dev/null +++ b/mysql-test/suite/rpl/r/rpl_stm_create_if_not_exists.result @@ -0,0 +1,704 @@ +# WL#5370 Keep forward-compatibility when changing 'CREATE TABLE IF NOT +# EXISTS ... SELECT' behaviour +# +stop slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +reset master; +reset slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +start slave; + + +CREATE TABLE t2(c1 INT, c2 char(10)); +INSERT INTO t2 VALUES(1, 'abc'), (2, 'abc'); + +# The original query should be binlogged if the table does not exist. +# ------------------------------------------------------------------ + +CREATE TABLE IF NOT EXISTS t1 (c1 INT , c2 INT, c3 char(10), c4 INT KEY) +SELECT 'abc' AS c3, 1 AS c4; +show binlog events from ; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # use `test`; CREATE TABLE IF NOT EXISTS t1 (c1 INT , c2 INT, c3 char(10), c4 INT KEY) +SELECT 'abc' AS c3, 1 AS c4 +Comparing tables master:test.t1 and slave:test.t1 + +# The statement should be binlogged as two events. one is +# 'CREATE TABLE IF NOT EXISTS ..', another one is +# 'INSERT ... SELECT'. +# ------------------------------------------------------------------ + +CREATE TABLE IF NOT EXISTS t1 +SELECT 'abc', 2; +Warnings: +Note 1050 Table 't1' already exists +show binlog events from ; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # BEGIN +master-bin.000001 # Query # # use `test`; CREATE TABLE IF NOT EXISTS `t1` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL, + `c3` char(10) DEFAULT NULL, + `c4` int(11) NOT NULL, + PRIMARY KEY (`c4`) +) +master-bin.000001 # Query # # use `test`; INSERT INTO `test`.`t1` (`c3`,`c4`) SELECT 'abc', 2 +master-bin.000001 # Query # # COMMIT +Comparing tables master:test.t1 and slave:test.t1 + +# Verify if it can be binlogged with right database name when the table +# is not in the default database + +DROP DATABASE IF EXISTS db1; +CREATE DATABASE db1; +USE db1; +CREATE TABLE IF NOT EXISTS test.t1 +SELECT 'abc', 20; +Warnings: +Note 1050 Table 't1' already exists +show binlog events from ; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # BEGIN +master-bin.000001 # Query # # use `db1`; CREATE TABLE IF NOT EXISTS `test`.`t1` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL, + `c3` char(10) DEFAULT NULL, + `c4` int(11) NOT NULL, + PRIMARY KEY (`c4`) +) +master-bin.000001 # Query # # use `db1`; INSERT INTO `test`.`t1` (`c3`,`c4`) SELECT 'abc', 20 +master-bin.000001 # Query # # COMMIT +Comparing tables master:test.t1 and slave:test.t1 +USE test; +DROP DATABASE db1; + +# It should be binlogged as 'REPLACE ... SELECT' +# if the original statement has option REPLACE + +CREATE TABLE IF NOT EXISTS t1 +REPLACE SELECT '123', 2; +Warnings: +Note 1050 Table 't1' already exists +show binlog events from ; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # BEGIN +master-bin.000001 # Query # # use `test`; CREATE TABLE IF NOT EXISTS `t1` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL, + `c3` char(10) DEFAULT NULL, + `c4` int(11) NOT NULL, + PRIMARY KEY (`c4`) +) +master-bin.000001 # Query # # use `test`; REPLACE INTO `test`.`t1` (`c3`,`c4`) SELECT '123', 2 +master-bin.000001 # Query # # COMMIT +Comparing tables master:test.t1 and slave:test.t1 + +# It should be binlogged as 'INSERT IGNORE... SELECT' +# if the original statement has option IGNORE + +CREATE TABLE IF NOT EXISTS t1 +IGNORE SELECT '123', 2; +Warnings: +Note 1050 Table 't1' already exists +show binlog events from ; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # BEGIN +master-bin.000001 # Query # # use `test`; CREATE TABLE IF NOT EXISTS `t1` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL, + `c3` char(10) DEFAULT NULL, + `c4` int(11) NOT NULL, + PRIMARY KEY (`c4`) +) +master-bin.000001 # Query # # use `test`; INSERT IGNORE INTO `test`.`t1` (`c3`,`c4`) SELECT '123', 2 +master-bin.000001 # Query # # COMMIT +Comparing tables master:test.t1 and slave:test.t1 + +# Nothing should be binlogged if error happens and no any row is inserted + +CREATE TABLE IF NOT EXISTS t1 +SELECT '123', 2; +ERROR 23000: Duplicate entry '2' for key 'PRIMARY' +show binlog events from ; +Log_name Pos Event_type Server_id End_log_pos Info +Comparing tables master:test.t1 and slave:test.t1 + +# Verify it can binlog well when there are some braces('(') + +CREATE TABLE IF NOT EXISTS t1 +(SELECT '123', 3) UNION (SELECT '123', 4); +Warnings: +Note 1050 Table 't1' already exists +CREATE TABLE IF NOT EXISTS t1 +REPLACE (SELECT 'abc', 3) UNION (SELECT 'abc', 4); +Warnings: +Note 1050 Table 't1' already exists +CREATE TABLE IF NOT EXISTS t1 +IGNORE (SELECT '123', 3) UNION (SELECT '123', 4); +Warnings: +Note 1050 Table 't1' already exists +show binlog events from ; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # BEGIN +master-bin.000001 # Query # # use `test`; CREATE TABLE IF NOT EXISTS `t1` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL, + `c3` char(10) DEFAULT NULL, + `c4` int(11) NOT NULL, + PRIMARY KEY (`c4`) +) +master-bin.000001 # Query # # use `test`; INSERT INTO `test`.`t1` (`c3`,`c4`) (SELECT '123', 3) UNION (SELECT '123', 4) +master-bin.000001 # Query # # COMMIT +master-bin.000001 # Query # # BEGIN +master-bin.000001 # Query # # use `test`; CREATE TABLE IF NOT EXISTS `t1` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL, + `c3` char(10) DEFAULT NULL, + `c4` int(11) NOT NULL, + PRIMARY KEY (`c4`) +) +master-bin.000001 # Query # # use `test`; REPLACE INTO `test`.`t1` (`c3`,`c4`) (SELECT 'abc', 3) UNION (SELECT 'abc', 4) +master-bin.000001 # Query # # COMMIT +master-bin.000001 # Query # # BEGIN +master-bin.000001 # Query # # use `test`; CREATE TABLE IF NOT EXISTS `t1` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL, + `c3` char(10) DEFAULT NULL, + `c4` int(11) NOT NULL, + PRIMARY KEY (`c4`) +) +master-bin.000001 # Query # # use `test`; INSERT IGNORE INTO `test`.`t1` (`c3`,`c4`) (SELECT '123', 3) UNION (SELECT '123', 4) +master-bin.000001 # Query # # COMMIT +Comparing tables master:test.t1 and slave:test.t1 + +# Throw a warning that table already exists and don't insert anything + +CREATE VIEW t3 AS SELECT * FROM t2; +CREATE TABLE IF NOT EXISTS t3 +SELECT '123', 2; +Warnings: +Note 1050 Table 't3' already exists +show binlog events from ; +Log_name Pos Event_type Server_id End_log_pos Info +DROP VIEW t3; + +# The statement can be binlogged correctly when it is in a SP/EVENT/TRIGGER + +DROP PROCEDURE IF EXISTS p1; +CREATE PROCEDURE p1(IN a INT) +CREATE TABLE IF NOT EXISTS t1 SELECT '123', a; +call p1(500); +Warnings: +Note 1050 Table 't1' already exists +call p1(600); +Warnings: +Note 1050 Table 't1' already exists +show binlog events from ; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # BEGIN +master-bin.000001 # Query # # use `test`; CREATE TABLE IF NOT EXISTS `t1` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL, + `c3` char(10) DEFAULT NULL, + `c4` int(11) NOT NULL, + PRIMARY KEY (`c4`) +) +master-bin.000001 # Query # # use `test`; INSERT INTO `test`.`t1` (`c3`,`c4`) SELECT '123', NAME_CONST('a',500) +master-bin.000001 # Query # # COMMIT +master-bin.000001 # Query # # BEGIN +master-bin.000001 # Query # # use `test`; CREATE TABLE IF NOT EXISTS `t1` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL, + `c3` char(10) DEFAULT NULL, + `c4` int(11) NOT NULL, + PRIMARY KEY (`c4`) +) +master-bin.000001 # Query # # use `test`; INSERT INTO `test`.`t1` (`c3`,`c4`) SELECT '123', NAME_CONST('a',600) +master-bin.000001 # Query # # COMMIT +Comparing tables master:test.t1 and slave:test.t1 +DROP PROCEDURE p1; + +# The statement can be binlogged correctly when it is in a prepared statement + +PREPARE stm FROM "CREATE TABLE IF NOT EXISTS t1 SELECT '123', ?"; +SET @a= 700; +EXECUTE stm USING @a; +Warnings: +Note 1050 Table 't1' already exists +SET @a= 800; +EXECUTE stm USING @a; +Warnings: +Note 1050 Table 't1' already exists +show binlog events from ; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # BEGIN +master-bin.000001 # Query # # use `test`; CREATE TABLE IF NOT EXISTS `t1` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL, + `c3` char(10) DEFAULT NULL, + `c4` int(11) NOT NULL, + PRIMARY KEY (`c4`) +) +master-bin.000001 # Query # # use `test`; INSERT INTO `test`.`t1` (`c3`,`c4`) SELECT '123', 700 +master-bin.000001 # Query # # COMMIT +master-bin.000001 # Query # # BEGIN +master-bin.000001 # Query # # use `test`; CREATE TABLE IF NOT EXISTS `t1` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL, + `c3` char(10) DEFAULT NULL, + `c4` int(11) NOT NULL, + PRIMARY KEY (`c4`) +) +master-bin.000001 # Query # # use `test`; INSERT INTO `test`.`t1` (`c3`,`c4`) SELECT '123', 800 +master-bin.000001 # Query # # COMMIT +Comparing tables master:test.t1 and slave:test.t1 + +# The statement can be binlogged correctly when it is in a conditional comment + +# The whole statement in a conditional comment +/*!CREATE TABLE IF NOT EXISTS t1 +SELECT 'abc', 900*/; +Warnings: +Note 1050 Table 't1' already exists +show binlog events from ; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # BEGIN +master-bin.000001 # Query # # use `test`; CREATE TABLE IF NOT EXISTS `t1` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL, + `c3` char(10) DEFAULT NULL, + `c4` int(11) NOT NULL, + PRIMARY KEY (`c4`) +) +master-bin.000001 # Query # # use `test`; /*! INSERT INTO `test`.`t1` (`c3`,`c4`) SELECT 'abc', 900*/ +master-bin.000001 # Query # # COMMIT + +# There is an long comment before SELECT +/*!CREATE /*blabla*/ TABLE IF NOT EXISTS t1 +SELECT 'abc', 901*/; +Warnings: +Note 1050 Table 't1' already exists +show binlog events from ; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # BEGIN +master-bin.000001 # Query # # use `test`; CREATE TABLE IF NOT EXISTS `t1` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL, + `c3` char(10) DEFAULT NULL, + `c4` int(11) NOT NULL, + PRIMARY KEY (`c4`) +) +master-bin.000001 # Query # # use `test`; /*! INSERT INTO `test`.`t1` (`c3`,`c4`) SELECT 'abc', 901*/ +master-bin.000001 # Query # # COMMIT + +# Conditional comment starts just from SELECT +CREATE TABLE IF NOT EXISTS t1 +/*!SELECT 'abc',*/ 902; +Warnings: +Note 1050 Table 't1' already exists +show binlog events from ; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # BEGIN +master-bin.000001 # Query # # use `test`; CREATE TABLE IF NOT EXISTS `t1` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL, + `c3` char(10) DEFAULT NULL, + `c4` int(11) NOT NULL, + PRIMARY KEY (`c4`) +) +master-bin.000001 # Query # # use `test`; /*! INSERT INTO `test`.`t1` (`c3`,`c4`) SELECT 'abc',*/ 902 +master-bin.000001 # Query # # COMMIT + +# Only SELECT keyword is in the conditional comment +CREATE TABLE IF NOT EXISTS t1 +/*!SELECT*/ /*!'abc',*/ 904; +Warnings: +Note 1050 Table 't1' already exists +show binlog events from ; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # BEGIN +master-bin.000001 # Query # # use `test`; CREATE TABLE IF NOT EXISTS `t1` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL, + `c3` char(10) DEFAULT NULL, + `c4` int(11) NOT NULL, + PRIMARY KEY (`c4`) +) +master-bin.000001 # Query # # use `test`; /*! INSERT INTO `test`.`t1` (`c3`,`c4`) SELECT*/ /*!'abc',*/ 904 +master-bin.000001 # Query # # COMMIT + +# Conditional comment is after SELECT keyword +CREATE TABLE IF NOT EXISTS t1 +SELECT /*!'abc',*/ 903; +Warnings: +Note 1050 Table 't1' already exists +show binlog events from ; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # BEGIN +master-bin.000001 # Query # # use `test`; CREATE TABLE IF NOT EXISTS `t1` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL, + `c3` char(10) DEFAULT NULL, + `c4` int(11) NOT NULL, + PRIMARY KEY (`c4`) +) +master-bin.000001 # Query # # use `test`; INSERT INTO `test`.`t1` (`c3`,`c4`) SELECT /*!'abc',*/ 903 +master-bin.000001 # Query # # COMMIT + +# Conditional comment ends just before SELECT keyword +/*!CREATE TABLE IF NOT EXISTS t1 +*/SELECT 'abc', 905; +Warnings: +Note 1050 Table 't1' already exists +show binlog events from ; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # BEGIN +master-bin.000001 # Query # # use `test`; CREATE TABLE IF NOT EXISTS `t1` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL, + `c3` char(10) DEFAULT NULL, + `c4` int(11) NOT NULL, + PRIMARY KEY (`c4`) +) +master-bin.000001 # Query # # use `test`; INSERT INTO `test`.`t1` (`c3`,`c4`) SELECT 'abc', 905 +master-bin.000001 # Query # # COMMIT +Comparing tables master:test.t1 and slave:test.t1 +DROP TABLE t2; +DROP TABLE t1; + + +CREATE TABLE t2(c1 INT, c2 char(10)); +INSERT INTO t2 VALUES(1, 'abc'), (2, 'abc'); + +# The original query should be binlogged if the table does not exist. +# ------------------------------------------------------------------ + +CREATE TEMPORARY TABLE IF NOT EXISTS t1 (c1 INT , c2 INT, c3 char(10), c4 INT KEY) +SELECT 'abc' AS c3, 1 AS c4; +show binlog events from ; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # use `test`; CREATE TEMPORARY TABLE IF NOT EXISTS t1 (c1 INT , c2 INT, c3 char(10), c4 INT KEY) +SELECT 'abc' AS c3, 1 AS c4 + +# The statement should be binlogged as two events. one is +# 'CREATE TEMPORARY TABLE IF NOT EXISTS ..', another one is +# 'INSERT ... SELECT'. +# ------------------------------------------------------------------ + +CREATE TEMPORARY TABLE IF NOT EXISTS t1 +SELECT 'abc', 2; +Warnings: +Note 1050 Table 't1' already exists +show binlog events from ; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # BEGIN +master-bin.000001 # Query # # use `test`; CREATE TEMPORARY TABLE IF NOT EXISTS `t1` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL, + `c3` char(10) DEFAULT NULL, + `c4` int(11) NOT NULL, + PRIMARY KEY (`c4`) +) +master-bin.000001 # Query # # use `test`; INSERT INTO `test`.`t1` (`c3`,`c4`) SELECT 'abc', 2 +master-bin.000001 # Query # # COMMIT + +# Verify if it can be binlogged with right database name when the table +# is not in the default database + +DROP DATABASE IF EXISTS db1; +CREATE DATABASE db1; +USE db1; +CREATE TEMPORARY TABLE IF NOT EXISTS test.t1 +SELECT 'abc', 20; +Warnings: +Note 1050 Table 't1' already exists +show binlog events from ; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # BEGIN +master-bin.000001 # Query # # use `db1`; CREATE TEMPORARY TABLE IF NOT EXISTS `test`.`t1` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL, + `c3` char(10) DEFAULT NULL, + `c4` int(11) NOT NULL, + PRIMARY KEY (`c4`) +) +master-bin.000001 # Query # # use `db1`; INSERT INTO `test`.`t1` (`c3`,`c4`) SELECT 'abc', 20 +master-bin.000001 # Query # # COMMIT +USE test; +DROP DATABASE db1; + +# It should be binlogged as 'REPLACE ... SELECT' +# if the original statement has option REPLACE + +CREATE TEMPORARY TABLE IF NOT EXISTS t1 +REPLACE SELECT '123', 2; +Warnings: +Note 1050 Table 't1' already exists +show binlog events from ; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # BEGIN +master-bin.000001 # Query # # use `test`; CREATE TEMPORARY TABLE IF NOT EXISTS `t1` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL, + `c3` char(10) DEFAULT NULL, + `c4` int(11) NOT NULL, + PRIMARY KEY (`c4`) +) +master-bin.000001 # Query # # use `test`; REPLACE INTO `test`.`t1` (`c3`,`c4`) SELECT '123', 2 +master-bin.000001 # Query # # COMMIT + +# It should be binlogged as 'INSERT IGNORE... SELECT' +# if the original statement has option IGNORE + +CREATE TEMPORARY TABLE IF NOT EXISTS t1 +IGNORE SELECT '123', 2; +Warnings: +Note 1050 Table 't1' already exists +show binlog events from ; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # BEGIN +master-bin.000001 # Query # # use `test`; CREATE TEMPORARY TABLE IF NOT EXISTS `t1` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL, + `c3` char(10) DEFAULT NULL, + `c4` int(11) NOT NULL, + PRIMARY KEY (`c4`) +) +master-bin.000001 # Query # # use `test`; INSERT IGNORE INTO `test`.`t1` (`c3`,`c4`) SELECT '123', 2 +master-bin.000001 # Query # # COMMIT + +# Nothing should be binlogged if error happens and no any row is inserted + +CREATE TEMPORARY TABLE IF NOT EXISTS t1 +SELECT '123', 2; +ERROR 23000: Duplicate entry '2' for key 'PRIMARY' +show binlog events from ; +Log_name Pos Event_type Server_id End_log_pos Info + +# Verify it can binlog well when there are some braces('(') + +CREATE TEMPORARY TABLE IF NOT EXISTS t1 +(SELECT '123', 3) UNION (SELECT '123', 4); +Warnings: +Note 1050 Table 't1' already exists +CREATE TEMPORARY TABLE IF NOT EXISTS t1 +REPLACE (SELECT 'abc', 3) UNION (SELECT 'abc', 4); +Warnings: +Note 1050 Table 't1' already exists +CREATE TEMPORARY TABLE IF NOT EXISTS t1 +IGNORE (SELECT '123', 3) UNION (SELECT '123', 4); +Warnings: +Note 1050 Table 't1' already exists +show binlog events from ; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # BEGIN +master-bin.000001 # Query # # use `test`; CREATE TEMPORARY TABLE IF NOT EXISTS `t1` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL, + `c3` char(10) DEFAULT NULL, + `c4` int(11) NOT NULL, + PRIMARY KEY (`c4`) +) +master-bin.000001 # Query # # use `test`; INSERT INTO `test`.`t1` (`c3`,`c4`) (SELECT '123', 3) UNION (SELECT '123', 4) +master-bin.000001 # Query # # COMMIT +master-bin.000001 # Query # # BEGIN +master-bin.000001 # Query # # use `test`; CREATE TEMPORARY TABLE IF NOT EXISTS `t1` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL, + `c3` char(10) DEFAULT NULL, + `c4` int(11) NOT NULL, + PRIMARY KEY (`c4`) +) +master-bin.000001 # Query # # use `test`; REPLACE INTO `test`.`t1` (`c3`,`c4`) (SELECT 'abc', 3) UNION (SELECT 'abc', 4) +master-bin.000001 # Query # # COMMIT +master-bin.000001 # Query # # BEGIN +master-bin.000001 # Query # # use `test`; CREATE TEMPORARY TABLE IF NOT EXISTS `t1` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL, + `c3` char(10) DEFAULT NULL, + `c4` int(11) NOT NULL, + PRIMARY KEY (`c4`) +) +master-bin.000001 # Query # # use `test`; INSERT IGNORE INTO `test`.`t1` (`c3`,`c4`) (SELECT '123', 3) UNION (SELECT '123', 4) +master-bin.000001 # Query # # COMMIT + +# The statement can be binlogged correctly when it is in a SP/EVENT/TRIGGER + +DROP PROCEDURE IF EXISTS p1; +CREATE PROCEDURE p1(IN a INT) +CREATE TEMPORARY TABLE IF NOT EXISTS t1 SELECT '123', a; +call p1(500); +Warnings: +Note 1050 Table 't1' already exists +call p1(600); +Warnings: +Note 1050 Table 't1' already exists +show binlog events from ; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # BEGIN +master-bin.000001 # Query # # use `test`; CREATE TEMPORARY TABLE IF NOT EXISTS `t1` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL, + `c3` char(10) DEFAULT NULL, + `c4` int(11) NOT NULL, + PRIMARY KEY (`c4`) +) +master-bin.000001 # Query # # use `test`; INSERT INTO `test`.`t1` (`c3`,`c4`) SELECT '123', NAME_CONST('a',500) +master-bin.000001 # Query # # COMMIT +master-bin.000001 # Query # # BEGIN +master-bin.000001 # Query # # use `test`; CREATE TEMPORARY TABLE IF NOT EXISTS `t1` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL, + `c3` char(10) DEFAULT NULL, + `c4` int(11) NOT NULL, + PRIMARY KEY (`c4`) +) +master-bin.000001 # Query # # use `test`; INSERT INTO `test`.`t1` (`c3`,`c4`) SELECT '123', NAME_CONST('a',600) +master-bin.000001 # Query # # COMMIT +DROP PROCEDURE p1; + +# The statement can be binlogged correctly when it is in a prepared statement + +PREPARE stm FROM "CREATE TEMPORARY TABLE IF NOT EXISTS t1 SELECT '123', ?"; +SET @a= 700; +EXECUTE stm USING @a; +Warnings: +Note 1050 Table 't1' already exists +SET @a= 800; +EXECUTE stm USING @a; +Warnings: +Note 1050 Table 't1' already exists +show binlog events from ; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # BEGIN +master-bin.000001 # Query # # use `test`; CREATE TEMPORARY TABLE IF NOT EXISTS `t1` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL, + `c3` char(10) DEFAULT NULL, + `c4` int(11) NOT NULL, + PRIMARY KEY (`c4`) +) +master-bin.000001 # Query # # use `test`; INSERT INTO `test`.`t1` (`c3`,`c4`) SELECT '123', 700 +master-bin.000001 # Query # # COMMIT +master-bin.000001 # Query # # BEGIN +master-bin.000001 # Query # # use `test`; CREATE TEMPORARY TABLE IF NOT EXISTS `t1` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL, + `c3` char(10) DEFAULT NULL, + `c4` int(11) NOT NULL, + PRIMARY KEY (`c4`) +) +master-bin.000001 # Query # # use `test`; INSERT INTO `test`.`t1` (`c3`,`c4`) SELECT '123', 800 +master-bin.000001 # Query # # COMMIT + +# The statement can be binlogged correctly when it is in a conditional comment + +# The whole statement in a conditional comment +/*!CREATE TEMPORARY TABLE IF NOT EXISTS t1 +SELECT 'abc', 900*/; +Warnings: +Note 1050 Table 't1' already exists +show binlog events from ; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # BEGIN +master-bin.000001 # Query # # use `test`; CREATE TEMPORARY TABLE IF NOT EXISTS `t1` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL, + `c3` char(10) DEFAULT NULL, + `c4` int(11) NOT NULL, + PRIMARY KEY (`c4`) +) +master-bin.000001 # Query # # use `test`; /*! INSERT INTO `test`.`t1` (`c3`,`c4`) SELECT 'abc', 900*/ +master-bin.000001 # Query # # COMMIT + +# There is an long comment before SELECT +/*!CREATE TEMPORARY /*blabla*/ TABLE IF NOT EXISTS t1 +SELECT 'abc', 901*/; +Warnings: +Note 1050 Table 't1' already exists +show binlog events from ; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # BEGIN +master-bin.000001 # Query # # use `test`; CREATE TEMPORARY TABLE IF NOT EXISTS `t1` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL, + `c3` char(10) DEFAULT NULL, + `c4` int(11) NOT NULL, + PRIMARY KEY (`c4`) +) +master-bin.000001 # Query # # use `test`; /*! INSERT INTO `test`.`t1` (`c3`,`c4`) SELECT 'abc', 901*/ +master-bin.000001 # Query # # COMMIT + +# Conditional comment starts just from SELECT +CREATE TEMPORARY TABLE IF NOT EXISTS t1 +/*!SELECT 'abc',*/ 902; +Warnings: +Note 1050 Table 't1' already exists +show binlog events from ; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # BEGIN +master-bin.000001 # Query # # use `test`; CREATE TEMPORARY TABLE IF NOT EXISTS `t1` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL, + `c3` char(10) DEFAULT NULL, + `c4` int(11) NOT NULL, + PRIMARY KEY (`c4`) +) +master-bin.000001 # Query # # use `test`; /*! INSERT INTO `test`.`t1` (`c3`,`c4`) SELECT 'abc',*/ 902 +master-bin.000001 # Query # # COMMIT + +# Only SELECT keyword is in the conditional comment +CREATE TEMPORARY TABLE IF NOT EXISTS t1 +/*!SELECT*/ /*!'abc',*/ 904; +Warnings: +Note 1050 Table 't1' already exists +show binlog events from ; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # BEGIN +master-bin.000001 # Query # # use `test`; CREATE TEMPORARY TABLE IF NOT EXISTS `t1` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL, + `c3` char(10) DEFAULT NULL, + `c4` int(11) NOT NULL, + PRIMARY KEY (`c4`) +) +master-bin.000001 # Query # # use `test`; /*! INSERT INTO `test`.`t1` (`c3`,`c4`) SELECT*/ /*!'abc',*/ 904 +master-bin.000001 # Query # # COMMIT + +# Conditional comment is after SELECT keyword +CREATE TEMPORARY TABLE IF NOT EXISTS t1 +SELECT /*!'abc',*/ 903; +Warnings: +Note 1050 Table 't1' already exists +show binlog events from ; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # BEGIN +master-bin.000001 # Query # # use `test`; CREATE TEMPORARY TABLE IF NOT EXISTS `t1` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL, + `c3` char(10) DEFAULT NULL, + `c4` int(11) NOT NULL, + PRIMARY KEY (`c4`) +) +master-bin.000001 # Query # # use `test`; INSERT INTO `test`.`t1` (`c3`,`c4`) SELECT /*!'abc',*/ 903 +master-bin.000001 # Query # # COMMIT + +# Conditional comment ends just before SELECT keyword +/*!CREATE TEMPORARY TABLE IF NOT EXISTS t1 +*/SELECT 'abc', 905; +Warnings: +Note 1050 Table 't1' already exists +show binlog events from ; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # BEGIN +master-bin.000001 # Query # # use `test`; CREATE TEMPORARY TABLE IF NOT EXISTS `t1` ( + `c1` int(11) DEFAULT NULL, + `c2` int(11) DEFAULT NULL, + `c3` char(10) DEFAULT NULL, + `c4` int(11) NOT NULL, + PRIMARY KEY (`c4`) +) +master-bin.000001 # Query # # use `test`; INSERT INTO `test`.`t1` (`c3`,`c4`) SELECT 'abc', 905 +master-bin.000001 # Query # # COMMIT +DROP TABLE t2; +DROP TEMPORARY TABLE t1; diff --git a/mysql-test/suite/rpl/t/rpl_create_if_not_exists.test b/mysql-test/suite/rpl/t/rpl_create_if_not_exists.test index 114f71af873..cf26e58c3ec 100644 --- a/mysql-test/suite/rpl/t/rpl_create_if_not_exists.test +++ b/mysql-test/suite/rpl/t/rpl_create_if_not_exists.test @@ -119,5 +119,32 @@ SELECT * FROM t2; DROP TABLE t1; DROP TABLE t2; DROP TABLE t3; +sync_slave_with_master; + +--echo +--echo # Bug#55616 Killing thread or query during CREATE IF NOT EXISTS makes +--echo # slave SQL thread abort +--echo + +--connection master1 +let $con_id = `SELECT CONNECTION_ID()`; + +CREATE TABLE t1 ( i INT ); +send CREATE TABLE IF NOT EXISTS t1 + AS SELECT SLEEP(3); + +connection master; +let $wait_timeout = 3; +let $show_statement = SHOW PROCESSLIST; +let $field = State; +let $condition = = 'User sleep'; +source include/wait_show_condition.inc; + +--replace_result $con_id master1 +eval KILL QUERY $con_id; +sync_slave_with_master; + +connection master; +DROP TABLE t1; source include/master-slave-end.inc; diff --git a/mysql-test/suite/rpl/t/rpl_row_create_table.test b/mysql-test/suite/rpl/t/rpl_row_create_table.test index a72ca75e975..148032f2987 100644 --- a/mysql-test/suite/rpl/t/rpl_row_create_table.test +++ b/mysql-test/suite/rpl/t/rpl_row_create_table.test @@ -299,5 +299,18 @@ DROP VIEW IF EXISTS bug48506_t1, bug48506_t2, bug48506_t3; DROP TEMPORARY TABLES t7; DROP TABLES t4, t5; DROP TABLES IF EXISTS bug48506_t4; +sync_slave_with_master; + +# +# Bug#55598 RBR: CREATE TABLE IF NOT EXISTS and INSERT written to binary log +# twice +# +connection master; +CREATE TABLE t1 SELECT 1; +CREATE TABLE IF NOT EXISTS t1 SELECT 1; +let $diff_table=test.t1; +source include/rpl_diff_tables.inc; +DROP TABLE t1; + source include/master-slave-end.inc; --echo end of the tests diff --git a/mysql-test/suite/rpl/t/rpl_stm_create_if_not_exists.test b/mysql-test/suite/rpl/t/rpl_stm_create_if_not_exists.test new file mode 100644 index 00000000000..69866cad267 --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_stm_create_if_not_exists.test @@ -0,0 +1,14 @@ +# +--echo # WL#5370 Keep forward-compatibility when changing 'CREATE TABLE IF NOT +--echo # EXISTS ... SELECT' behaviour +--echo # + +source include/master-slave.inc; +source include/have_binlog_format_statement.inc; + +source extra/rpl_tests/rpl_stm_create_if_not_exists.test; + +let $is_temporary=1; +source extra/rpl_tests/rpl_stm_create_if_not_exists.test; + +source include/master-slave-end.inc; diff --git a/sql/sql_class.h b/sql/sql_class.h index 1627b6ec02d..42c873e9fc3 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -2622,7 +2622,9 @@ public: class select_insert :public select_result_interceptor { - public: +protected: + virtual int write_to_binlog(bool is_trans, int errcode); +public: TABLE_LIST *table_list; TABLE *table; List *fields; @@ -2658,6 +2660,8 @@ class select_create: public select_insert { MYSQL_LOCK *m_lock; /* m_lock or thd->extra_lock */ MYSQL_LOCK **m_plock; + + virtual int write_to_binlog(bool is_trans, int errcode); public: select_create (TABLE_LIST *table_arg, HA_CREATE_INFO *create_info_par, @@ -2673,7 +2677,7 @@ public: {} int prepare(List &list, SELECT_LEX_UNIT *u); - int binlog_show_create_table(TABLE **tables, uint count); + int binlog_show_create_table(TABLE **tables, uint count, int errcode); void store_values(List &values); void send_error(uint errcode,const char *err); bool send_eof(); diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index eb9e1e5b3af..8604f876f37 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -3268,7 +3268,7 @@ bool select_insert::send_eof() /* Write to binlog before commiting transaction. No statement will - be written by the binlog_query() below in RBR mode. All the + be written by the write_to_binlog() below in RBR mode. All the events are in the transaction cache and will be written when ha_autocommit_or_rollback() is issued below. */ @@ -3280,9 +3280,8 @@ bool select_insert::send_eof() thd->clear_error(); else errcode= query_error_code(thd, killed_status == THD::NOT_KILLED); - if (thd->binlog_query(THD::ROW_QUERY_TYPE, - thd->query(), thd->query_length(), - trans_table, FALSE, errcode)) + + if (write_to_binlog(trans_table, errcode)) { table->file->ha_release_auto_increment(); DBUG_RETURN(1); @@ -3356,9 +3355,7 @@ void select_insert::abort() { { int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED); /* error of writing binary log is ignored */ - (void) thd->binlog_query(THD::ROW_QUERY_TYPE, thd->query(), - thd->query_length(), - transactional_table, FALSE, errcode); + write_to_binlog(transactional_table, errcode); } if (!thd->current_stmt_binlog_row_based && !can_rollback_data()) thd->transaction.all.modified_non_trans_table= TRUE; @@ -3373,6 +3370,103 @@ void select_insert::abort() { DBUG_VOID_RETURN; } +int select_insert::write_to_binlog(bool is_trans, int errcode) +{ + /* It is only for statement mode */ + if (thd->current_stmt_binlog_row_based) + return 0; + + return thd->binlog_query(THD::ROW_QUERY_TYPE, + thd->query(), thd->query_length(), + is_trans, FALSE, errcode); +} + +/* Override the select_insert::write_to_binlog */ +int select_create::write_to_binlog(bool is_trans, int errcode) +{ + /* It is only for statement mode */ + if (thd->current_stmt_binlog_row_based) + return 0; + + /* + WL#5370 Keep the compatibility between 5.1 master and 5.5 slave. + Binlog a 'INSERT ... SELECT' statement only when it has the option + 'IF NOT EXISTS' and the table already exists as a base table. + */ + if ((create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS) && + create_info->table_existed) + { + String query; + int result; + + thd->binlog_start_trans_and_stmt(); + /* Binlog the CREATE TABLE IF NOT EXISTS statement */ + result= binlog_show_create_table(&table, 1, 0); + if (result) + return result; + + uint db_len= strlen(create_table->db); + uint table_len= strlen(create_info->alias); + uint select_len= thd->query_length() - thd->lex->create_select_pos; + uint field_len= (table->s->fields - (field - table->field)) * + (MAX_FIELD_NAME + 3); + + /* + pre-allocating memory reduces the times of reallocating memory, + when calling query.appen(). + 40bytes is enough for other words("INSERT IGNORE INTO", etc.). + */ + if (query.real_alloc(40 + db_len + table_len + field_len + select_len)) + return 1; + + if (thd->lex->create_select_in_comment) + query.append(STRING_WITH_LEN("/*! ")); + if (thd->lex->ignore) + query.append(STRING_WITH_LEN("INSERT IGNORE INTO `")); + else if (thd->lex->duplicates == DUP_REPLACE) + query.append(STRING_WITH_LEN("REPLACE INTO `")); + else + query.append(STRING_WITH_LEN("INSERT INTO `")); + + query.append(create_table->db, db_len); + query.append(STRING_WITH_LEN("`.`")); + query.append(create_info->alias, table_len); + query.append(STRING_WITH_LEN("` ")); + + /* + The insert items. + Field is the the rightmost columns that the rows are inster in. + */ + query.append(STRING_WITH_LEN("(")); + for (Field **f= field ; *f ; f++) + { + if (f != field) + query.append(STRING_WITH_LEN(",")); + + query.append(STRING_WITH_LEN("`")); + query.append((*f)->field_name, strlen((*f)->field_name)); + query.append(STRING_WITH_LEN("`")); + } + query.append(STRING_WITH_LEN(") ")); + + /* The SELECT clause*/ + DBUG_ASSERT(thd->lex->create_select_pos); + if (thd->lex->create_select_start_with_brace) + query.append(STRING_WITH_LEN("(")); + if (query.append(thd->query() + thd->lex->create_select_pos, select_len)) + return 1; + + /* + Avoid to use thd->binlog_query() twice, otherwise it will print the unsafe + warning twice. + */ + Query_log_event ev(thd, query.c_ptr_safe(), query.length(), is_trans, + FALSE, errcode); + return mysql_bin_log.write(&ev); + } + else + return select_insert::write_to_binlog(is_trans, errcode); +} /*************************************************************************** CREATE TABLE (SELECT) ... @@ -3613,7 +3707,8 @@ select_create::prepare(List &values, SELECT_LEX_UNIT *u) !table->s->tmp_table && !ptr->get_create_info()->table_existed) { - if (int error= ptr->binlog_show_create_table(tables, count)) + int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED); + if (int error= ptr->binlog_show_create_table(tables, count, errcode)) return error; } return 0; @@ -3654,7 +3749,10 @@ select_create::prepare(List &values, SELECT_LEX_UNIT *u) ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR), create_table->table_name); if (thd->current_stmt_binlog_row_based) - binlog_show_create_table(&(create_table->table), 1); + { + int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED); + binlog_show_create_table(&(create_table->table), 1, errcode); + } table= create_table->table; } else @@ -3722,10 +3820,10 @@ select_create::prepare(List &values, SELECT_LEX_UNIT *u) } int -select_create::binlog_show_create_table(TABLE **tables, uint count) +select_create::binlog_show_create_table(TABLE **tables, uint count, int errcode) { /* - Note 1: In RBR mode, we generate a CREATE TABLE statement for the + Note 1: We generate a CREATE TABLE statement for the created table by calling store_create_info() (behaves as SHOW CREATE TABLE). In the event of an error, nothing should be written to the binary log, even if the table is non-transactional; @@ -3741,7 +3839,6 @@ select_create::binlog_show_create_table(TABLE **tables, uint count) schema that will do a close_thread_tables(), destroying the statement transaction cache. */ - DBUG_ASSERT(thd->current_stmt_binlog_row_based); DBUG_ASSERT(tables && *tables && count > 0); char buf[2048]; @@ -3759,7 +3856,6 @@ select_create::binlog_show_create_table(TABLE **tables, uint count) if (mysql_bin_log.is_open()) { - int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED); result= thd->binlog_query(THD::STMT_QUERY_TYPE, query.ptr(), query.length(), /* is_trans */ TRUE, diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 7403bb5a1a4..9131cec9d04 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -1817,6 +1817,23 @@ typedef struct st_lex : public Query_tables_list */ bool protect_against_global_read_lock; + /* + The following three variables are used in 'CREATE TABLE IF NOT EXISTS ... + SELECT' statement. They are used to binlog the statement. + + create_select_start_with_brace will be set if there is a '(' before + the first SELECT clause + + create_select_pos records the relative position of the SELECT clause + in the whole statement. + + create_select_in_comment will be set if SELECT keyword is in conditional + comment. + */ + bool create_select_start_with_brace; + uint create_select_pos; + bool create_select_in_comment; + st_lex(); virtual ~st_lex() diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 9ec03ea1d5f..fbe9c9753d9 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2717,6 +2717,25 @@ mysql_execute_command(THD *thd) { TABLE_LIST *duplicate; create_table= lex->unlink_first_table(&link_to_local); + + if (create_table->view) + { + if (create_info.options & HA_LEX_CREATE_IF_NOT_EXISTS) + { + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_TABLE_EXISTS_ERROR, + ER(ER_TABLE_EXISTS_ERROR), + create_info.alias); + my_ok(thd); + } + else + { + my_error(ER_TABLE_EXISTS_ERROR, MYF(0), create_info.alias); + res= 1; + } + goto end_with_restore_list; + } + if ((duplicate= unique_table(thd, create_table, select_tables, 0))) { update_non_unique_table_error(create_table, "CREATE", duplicate); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 5a3ad0b3eba..ed367582ba5 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -3881,17 +3881,26 @@ create2a: create3 {} | opt_partitioning create_select ')' - { Select->set_braces(1);} + { + Select->set_braces(1); + Lex->create_select_start_with_brace= TRUE; + } union_opt {} ; create3: /* empty */ {} | opt_duplicate opt_as create_select - { Select->set_braces(0);} + { + Select->set_braces(0); + Lex->create_select_start_with_brace= FALSE; + } union_clause {} | opt_duplicate opt_as '(' create_select ')' - { Select->set_braces(1);} + { + Select->set_braces(1); + Lex->create_select_start_with_brace= TRUE; + } union_opt {} ; @@ -4516,6 +4525,19 @@ create_select: lex->current_select->table_list.save_and_clear(&lex->save_list); mysql_init_select(lex); lex->current_select->parsing_place= SELECT_LIST; + + if (lex->sql_command == SQLCOM_CREATE_TABLE && + (lex->create_info.options & HA_LEX_CREATE_IF_NOT_EXISTS)) + { + Lex_input_stream *lip= YYLIP; + + if (lex->spcont) + lex->create_select_pos= lip->get_tok_start() - + lex->sphead->m_tmp_query; + else + lex->create_select_pos= lip->get_tok_start() - lip->get_buf(); + lex->create_select_in_comment= (lip->in_comment == DISCARD_COMMENT); + } } select_options select_item_list { From f57888054936dad42dc75376c87e39d9539f8240 Mon Sep 17 00:00:00 2001 From: Mats Kindahl Date: Wed, 18 Aug 2010 12:53:43 +0200 Subject: [PATCH 12/28] WL#5363: Thread pool interface Updating result files containing output of --help --verbose. --- mysql-test/r/mysqld--help-notwin.result | 2 +- mysql-test/r/mysqld--help-win.result | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/mysqld--help-notwin.result b/mysql-test/r/mysqld--help-notwin.result index 533f35c1113..bc67a448f77 100644 --- a/mysql-test/r/mysqld--help-notwin.result +++ b/mysql-test/r/mysqld--help-notwin.result @@ -688,7 +688,7 @@ The following options may be given as the first argument: How many threads we should keep in a cache for reuse --thread-handling=name Define threads usage for handling queries, one of - one-thread-per-connection, no-threads + one-thread-per-connection, no-threads, loaded-dynamically --thread-stack=# The stack size for each thread --time-format=name The TIME format (ignored) --timed-mutexes Specify whether to time mutexes (only InnoDB mutexes are diff --git a/mysql-test/r/mysqld--help-win.result b/mysql-test/r/mysqld--help-win.result index 9b6b0f53b01..7eee15913f4 100644 --- a/mysql-test/r/mysqld--help-win.result +++ b/mysql-test/r/mysqld--help-win.result @@ -692,7 +692,7 @@ The following options may be given as the first argument: How many threads we should keep in a cache for reuse --thread-handling=name Define threads usage for handling queries, one of - one-thread-per-connection, no-threads + one-thread-per-connection, no-threads, loaded-dynamically --thread-stack=# The stack size for each thread --time-format=name The TIME format (ignored) --timed-mutexes Specify whether to time mutexes (only InnoDB mutexes are From 3ac03a0b1b3af3b1139dfc94372d9d441fcaebc7 Mon Sep 17 00:00:00 2001 From: MySQL Build Team Date: Thu, 19 Aug 2010 17:03:29 +0200 Subject: [PATCH 13/28] Raise the version number, 5.1.50 is (was) being built in a parellel tree. --- configure.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.in b/configure.in index a475185d880..7c3e304e0c7 100644 --- a/configure.in +++ b/configure.in @@ -12,7 +12,7 @@ dnl dnl When changing the major version number please also check the switch dnl statement in mysqlbinlog::check_master_version(). You may also need dnl to update version.c in ndb. -AC_INIT([MySQL Server], [5.1.50], [], [mysql]) +AC_INIT([MySQL Server], [5.1.51], [], [mysql]) AC_CONFIG_SRCDIR([sql/mysqld.cc]) AC_CANONICAL_SYSTEM From bc05efd8aa116e9609941ad1f19665b79eca0eef Mon Sep 17 00:00:00 2001 From: Mattias Jonsson Date: Fri, 20 Aug 2010 21:17:51 +0200 Subject: [PATCH 14/28] post push test fix --- mysql-test/t/partition_not_blackhole.test | 1 + 1 file changed, 1 insertion(+) diff --git a/mysql-test/t/partition_not_blackhole.test b/mysql-test/t/partition_not_blackhole.test index 222c1bb091e..7352aeaa230 100644 --- a/mysql-test/t/partition_not_blackhole.test +++ b/mysql-test/t/partition_not_blackhole.test @@ -16,6 +16,7 @@ let $MYSQLD_DATADIR= `SELECT @@datadir`; --copy_file std_data/parts/t1_blackhole.frm $MYSQLD_DATADIR/test/t1.frm --copy_file std_data/parts/t1_blackhole.par $MYSQLD_DATADIR/test/t1.par SHOW TABLES; +--replace_result $MYSQLD_DATADIR ./ --error ER_NOT_FORM_FILE SHOW CREATE TABLE t1; --error ER_BAD_TABLE_ERROR From e7b268827130b6bc5fa6c2c0f90a850ee309668c Mon Sep 17 00:00:00 2001 From: Alexey Kopytov Date: Tue, 24 Aug 2010 19:51:32 +0400 Subject: [PATCH 15/28] Bug #54802: 'NOT BETWEEN' evaluation is incorrect Queries involving predicates of the form "const NOT BETWEEN not_indexed_column AND indexed_column" could return wrong data due to incorrect handling by the range optimizer. For "c NOT BETWEEN f1 AND f2" predicates, get_mm_tree() produces a disjunction of the SEL_ARG trees for "f1 > c" and "f2 < c". If one of the trees is empty (i.e. one of the arguments is not sargable) the resulting tree should be empty as well, since the whole expression in this case is not sargable. The above logic is implemented in get_mm_tree() as follows. The initial state of the resulting tree is NULL (aka empty). We then iterate through arguments and compute the corresponding SEL_ARG tree (either "f1 > c" or "f2 < c"). If the resulting tree is NULL, it is simply replaced by the generated tree. Otherwise it is replaced by a disjunction of itself and the generated tree. The obvious flaw in this implementation is that if the first argument is not sargable and thus produces a NULL tree, the resulting tree will simply be replaced by the tree for the second argument. As a result, "c NOT BETWEEN f1 AND f2" will end up as just "f2 < c". Fixed by adding a check so that when the first argument produces an empty tree for the NOT BETWEEN case, the loop is aborted with an empty tree as a result. The whole idea of using a loop for 2 arguments does not make much sense, but it was probably used to avoid code duplication for several BETWEEN variants. --- mysql-test/r/range.result | 13 +++++++++++++ mysql-test/t/range.test | 12 ++++++++++++ sql/opt_range.cc | 4 ++++ 3 files changed, 29 insertions(+) diff --git a/mysql-test/r/range.result b/mysql-test/r/range.result index 64e00521cd2..d989896514c 100644 --- a/mysql-test/r/range.result +++ b/mysql-test/r/range.result @@ -1653,4 +1653,17 @@ a b 0 0 1 1 DROP TABLE t1; +# +# Bug #54802: 'NOT BETWEEN' evaluation is incorrect +# +CREATE TABLE t1 (c_key INT, c_notkey INT, KEY(c_key)); +INSERT INTO t1 VALUES (1, 1), (2, 2), (3, 3); +EXPLAIN SELECT * FROM t1 WHERE 2 NOT BETWEEN c_notkey AND c_key; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL c_key NULL NULL NULL 3 Using where +SELECT * FROM t1 WHERE 2 NOT BETWEEN c_notkey AND c_key; +c_key c_notkey +1 1 +3 3 +DROP TABLE t1; End of 5.1 tests diff --git a/mysql-test/t/range.test b/mysql-test/t/range.test index 5d5ad180f1a..0ad3d3e8504 100644 --- a/mysql-test/t/range.test +++ b/mysql-test/t/range.test @@ -1313,4 +1313,16 @@ SELECT * FROM t1 FORCE INDEX (PRIMARY) DROP TABLE t1; +--echo # +--echo # Bug #54802: 'NOT BETWEEN' evaluation is incorrect +--echo # + +CREATE TABLE t1 (c_key INT, c_notkey INT, KEY(c_key)); +INSERT INTO t1 VALUES (1, 1), (2, 2), (3, 3); + +EXPLAIN SELECT * FROM t1 WHERE 2 NOT BETWEEN c_notkey AND c_key; +SELECT * FROM t1 WHERE 2 NOT BETWEEN c_notkey AND c_key; + +DROP TABLE t1; + --echo End of 5.1 tests diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 1f42d0567a7..eae79e63c19 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -5526,7 +5526,11 @@ static SEL_TREE *get_mm_tree(RANGE_OPT_PARAM *param,COND *cond) SEL_TREE *tmp= get_full_func_mm_tree(param, cond_func, field_item, (Item*)(intptr)i, inv); if (inv) + { tree= !tree ? tmp : tree_or(param, tree, tmp); + if (tree == NULL) + break; + } else tree= tree_and(param, tree, tmp); } From 76907f8591d1c8d1fa91eca388304b3fb4d7d4c8 Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Wed, 25 Aug 2010 10:23:19 +0200 Subject: [PATCH 16/28] Bug #52301 Add --protocol to mysqltest Added code resulted in strange linking problem for embedded on Windows Avoided by not doing this for embedded mode It's irrelevant for embedded server anyway, --protocol will be ignored --- client/mysqltest.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/client/mysqltest.cc b/client/mysqltest.cc index d7a302912b4..756208a0f96 100644 --- a/client/mysqltest.cc +++ b/client/mysqltest.cc @@ -5220,8 +5220,10 @@ void do_connect(struct st_command *command) } #endif +#ifndef EMBEDDED_LIBRARY if (opt_protocol) mysql_options(&con_slot->mysql, MYSQL_OPT_PROTOCOL, (char*) &opt_protocol); +#endif #ifdef HAVE_SMEM if (con_shm) @@ -6179,8 +6181,10 @@ get_one_option(int optid, const struct my_option *opt, print_version(); exit(0); case OPT_MYSQL_PROTOCOL: +#ifndef EMBEDDED_LIBRARY opt_protocol= find_type_or_exit(argument, &sql_protocol_typelib, opt->name); +#endif break; case '?': usage(); @@ -7932,8 +7936,10 @@ int main(int argc, char **argv) mysql_options(&con->mysql, MYSQL_SET_CHARSET_DIR, opt_charsets_dir); +#ifndef EMBEDDED_LIBRARY if (opt_protocol) mysql_options(&con->mysql,MYSQL_OPT_PROTOCOL,(char*)&opt_protocol); +#endif #ifdef HAVE_OPENSSL From 800feb16cb9e3fcb363f0856581c8822bd29d549 Mon Sep 17 00:00:00 2001 From: Dmitry Shulga Date: Wed, 25 Aug 2010 15:47:45 +0700 Subject: [PATCH 17/28] Fixed bug #29751 - do not rename the error log at FLUSH LOGS. Added open log file with FILE_SHARE_DELETE flag on Windows. sql/log.cc: added reopen_fstreams(); modified redirect_std_streams(): call to sequence of freopen() replaced to reopen_fstreams(); modified flush_error_log(): removed file rename for flushed error log file. sql/mysqld.cc: modified main() and init_server_components(): do open log error file over call to reopen_fstreams(). --- sql/log.cc | 117 ++++++++++++++++++++++++++++++-------------------- sql/mysqld.cc | 19 +++++--- 2 files changed, 82 insertions(+), 54 deletions(-) diff --git a/sql/log.cc b/sql/log.cc index 3f41bf1c929..156c293e3aa 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -5063,70 +5063,93 @@ void sql_perror(const char *message) } +#ifdef __WIN__ +extern "C" my_bool reopen_fstreams(const char *filename, + FILE *outstream, FILE *errstream) +{ + int handle_fd; + int stream_fd; + HANDLE osfh; + + DBUG_ASSERT(filename && (outstream || errstream)); + + if ((osfh= CreateFile(filename, GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE | + FILE_SHARE_DELETE, NULL, + OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, + NULL)) == INVALID_HANDLE_VALUE) + return TRUE; + + if ((handle_fd= _open_osfhandle((intptr_t)osfh, + _O_APPEND | _O_TEXT)) == -1) + { + CloseHandle(osfh); + return TRUE; + } + + if (outstream) + { + stream_fd= _fileno(outstream); + if (_dup2(handle_fd, stream_fd) < 0) + { + CloseHandle(osfh); + return TRUE; + } + } + + if (errstream) + { + stream_fd= _fileno(errstream); + if (_dup2(handle_fd, stream_fd) < 0) + { + CloseHandle(osfh); + return TRUE; + } + } + + _close(handle_fd); + return FALSE; +} +#else +extern "C" my_bool reopen_fstreams(const char *filename, + FILE *outstream, FILE *errstream) +{ + if (outstream && !freopen(filename, "a+", outstream)) + return TRUE; + + if (errstream && !freopen(filename, "a+", errstream)) + return TRUE; + + return FALSE; +} +#endif + + /* Unfortunately, there seems to be no good way to restore the original streams upon failure. */ static bool redirect_std_streams(const char *file) { - if (freopen(file, "a+", stdout) && freopen(file, "a+", stderr)) - { - setbuf(stderr, NULL); - return FALSE; - } + if (reopen_fstreams(file, stdout, stderr)) + return TRUE; - return TRUE; + setbuf(stderr, NULL); + return FALSE; } bool flush_error_log() { - bool result=0; + bool result= 0; if (opt_error_log) { - char err_renamed[FN_REFLEN], *end; - end= strmake(err_renamed,log_error_file,FN_REFLEN-5); - strmov(end, "-old"); VOID(pthread_mutex_lock(&LOCK_error_log)); -#ifdef __WIN__ - char err_temp[FN_REFLEN+5]; - /* - On Windows is necessary a temporary file for to rename - the current error file. - */ - strxmov(err_temp, err_renamed,"-tmp",NullS); - (void) my_delete(err_temp, MYF(0)); - if (freopen(err_temp,"a+",stdout)) - { - int fd; - size_t bytes; - uchar buf[IO_SIZE]; - - freopen(err_temp,"a+",stderr); - setbuf(stderr, NULL); - (void) my_delete(err_renamed, MYF(0)); - my_rename(log_error_file,err_renamed,MYF(0)); - redirect_std_streams(log_error_file); - - if ((fd = my_open(err_temp, O_RDONLY, MYF(0))) >= 0) - { - while ((bytes= my_read(fd, buf, IO_SIZE, MYF(0))) && - bytes != MY_FILE_ERROR) - my_fwrite(stderr, buf, bytes, MYF(0)); - my_close(fd, MYF(0)); - } - (void) my_delete(err_temp, MYF(0)); - } - else - result= 1; -#else - my_rename(log_error_file,err_renamed,MYF(0)); - if (redirect_std_streams(log_error_file)) - result= 1; -#endif + if (redirect_std_streams(log_error_file)) + result= 1; VOID(pthread_mutex_unlock(&LOCK_error_log)); } - return result; + return result; } void MYSQL_BIN_LOG::signal_update() diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 08407d52e09..d9c4c7fc3f5 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -199,6 +199,9 @@ typedef fp_except fp_except_t; # endif #endif +extern "C" my_bool reopen_fstreams(const char *filename, + FILE *outstream, FILE *errstream); + inline void setup_fpu() { #if defined(__FreeBSD__) && defined(HAVE_IEEEFP_H) @@ -3821,13 +3824,15 @@ static int init_server_components() opt_error_log= 1; // Too long file name else { + my_bool res; #ifndef EMBEDDED_LIBRARY - if (freopen(log_error_file, "a+", stdout)) + res= reopen_fstreams(log_error_file, stdout, stderr); +#else + res= reopen_fstreams(log_error_file, NULL, stderr); #endif - { - if (freopen(log_error_file, "a+", stderr)) - setbuf(stderr, NULL); - } + + if (!res) + setbuf(stderr, NULL); } } @@ -4475,8 +4480,8 @@ we force server id to 2, but this MySQL server will not act as a slave."); #ifdef __WIN__ if (!opt_console) { - freopen(log_error_file,"a+",stdout); - freopen(log_error_file,"a+",stderr); + if (reopen_fstreams(log_error_file, stdout, stderr)) + unireg_abort(1); setbuf(stderr, NULL); FreeConsole(); // Remove window } From af16216298835b314a28379acffd8050b7d48f33 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 25 Aug 2010 16:05:33 +0200 Subject: [PATCH 18/28] Raise version number after cloning 5.5.6-m3 --- configure.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.in b/configure.in index 59d08f0a9e5..1f56d958a7f 100644 --- a/configure.in +++ b/configure.in @@ -27,7 +27,7 @@ dnl dnl When changing the major version number please also check the switch dnl statement in mysqlbinlog::check_master_version(). You may also need dnl to update version.c in ndb. -AC_INIT([MySQL Server], [5.5.6-m3], [], [mysql]) +AC_INIT([MySQL Server], [5.5.7-m3], [], [mysql]) AC_CONFIG_SRCDIR([sql/mysqld.cc]) AC_CANONICAL_SYSTEM From 09c62e4ff81f0091608f67cf86ea219d40df4a1a Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Wed, 25 Aug 2010 16:34:31 +0200 Subject: [PATCH 19/28] Cherry pick 55501 --- mysql-test/include/have_innodb_plugin.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/mysql-test/include/have_innodb_plugin.inc b/mysql-test/include/have_innodb_plugin.inc index 99a79465f52..df876deb2d7 100644 --- a/mysql-test/include/have_innodb_plugin.inc +++ b/mysql-test/include/have_innodb_plugin.inc @@ -1,3 +1,4 @@ +--source include/not_embedded.inc disable_query_log; --require r/true.require SELECT (plugin_library LIKE 'ha_innodb_plugin%') AS `TRUE` FROM information_schema.plugins WHERE LOWER(plugin_name) = 'innodb' AND LOWER(plugin_status) = 'active'; From b4dc600af919f8a7fa8403535847256161abc1db Mon Sep 17 00:00:00 2001 From: Evgeny Potemkin Date: Thu, 26 Aug 2010 13:31:04 +0400 Subject: [PATCH 20/28] Bug #55656: mysqldump can be slower after bug 39653 fix. After fix for bug 39653 the shortest available secondary index was used for full table scan. Primary clustered key was used only if no secondary index can be used. However, when chosen secondary index includes all fields of the table being scanned it's better to use primary index since the amount of data to scan is the same but the primary index is clustered. Now the find_shortest_key function takes this into account. mysql-test/suite/innodb/r/innodb_mysql.result: Added a test case for the bug#55656. mysql-test/suite/innodb/t/innodb_mysql.test: Added a test case for the bug#55656. sql/sql_select.cc: Bug #55656: mysqldump can be slower after bug #39653 fix. The find_shortest_key function now prefers clustered primary key if found secondary key includes all fields of the table. --- mysql-test/suite/innodb/r/innodb_mysql.result | 58 ++++++++++++++++++ mysql-test/suite/innodb/t/innodb_mysql.test | 25 ++++++++ sql/sql_select.cc | 60 +++++++++++++------ 3 files changed, 125 insertions(+), 18 deletions(-) diff --git a/mysql-test/suite/innodb/r/innodb_mysql.result b/mysql-test/suite/innodb/r/innodb_mysql.result index 23619b91fbc..ba8ac0ba86c 100644 --- a/mysql-test/suite/innodb/r/innodb_mysql.result +++ b/mysql-test/suite/innodb/r/innodb_mysql.result @@ -2529,4 +2529,62 @@ SELECT * FROM t1 FOR UPDATE; SELECT * FROM t1 GROUP BY (SELECT a FROM t2 LIMIT 1 FOR UPDATE) + t1.a; ERROR 40001: Deadlock found when trying to get lock; try restarting transaction DROP TABLE t1,t2; +# +# Bug#55656: mysqldump can be slower after bug #39653 fix +# +CREATE TABLE t1 (a INT , b INT, c INT, d INT, +KEY (b), PRIMARY KEY (a,b)) ENGINE=INNODB; +INSERT INTO t1 VALUES (1,1,1,1), (2,2,2,2), (3,3,3,3); +EXPLAIN SELECT COUNT(*) FROM t1; +id 1 +select_type SIMPLE +table t1 +type index +possible_keys NULL +key b +key_len 4 +ref NULL +rows 3 +Extra Using index +DROP INDEX b ON t1; +CREATE INDEX b ON t1(a,b); +EXPLAIN SELECT COUNT(*) FROM t1; +id 1 +select_type SIMPLE +table t1 +type index +possible_keys NULL +key b +key_len 8 +ref NULL +rows 3 +Extra Using index +DROP INDEX b ON t1; +CREATE INDEX b ON t1(a,b,c); +EXPLAIN SELECT COUNT(*) FROM t1; +id 1 +select_type SIMPLE +table t1 +type index +possible_keys NULL +key b +key_len 13 +ref NULL +rows 3 +Extra Using index +DROP INDEX b ON t1; +CREATE INDEX b ON t1(a,b,c,d); +EXPLAIN SELECT COUNT(*) FROM t1; +id 1 +select_type SIMPLE +table t1 +type index +possible_keys NULL +key PRIMARY +key_len 8 +ref NULL +rows 3 +Extra Using index +DROP TABLE t1; +# End of 5.1 tests diff --git a/mysql-test/suite/innodb/t/innodb_mysql.test b/mysql-test/suite/innodb/t/innodb_mysql.test index d633cb2222e..4f13e15a627 100644 --- a/mysql-test/suite/innodb/t/innodb_mysql.test +++ b/mysql-test/suite/innodb/t/innodb_mysql.test @@ -781,5 +781,30 @@ disconnect con2; DROP TABLE t1,t2; +--echo # +--echo # Bug#55656: mysqldump can be slower after bug #39653 fix +--echo # + +CREATE TABLE t1 (a INT , b INT, c INT, d INT, + KEY (b), PRIMARY KEY (a,b)) ENGINE=INNODB; +INSERT INTO t1 VALUES (1,1,1,1), (2,2,2,2), (3,3,3,3); +--query_vertical EXPLAIN SELECT COUNT(*) FROM t1 + +DROP INDEX b ON t1; +CREATE INDEX b ON t1(a,b); +--query_vertical EXPLAIN SELECT COUNT(*) FROM t1 + +DROP INDEX b ON t1; +CREATE INDEX b ON t1(a,b,c); +--query_vertical EXPLAIN SELECT COUNT(*) FROM t1 + +DROP INDEX b ON t1; +CREATE INDEX b ON t1(a,b,c,d); +--query_vertical EXPLAIN SELECT COUNT(*) FROM t1 + +DROP TABLE t1; + +--echo # + --echo End of 5.1 tests diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 7ee1762295f..4a32ca34790 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -13017,6 +13017,34 @@ static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx, } +/** + Find shortest key suitable for full table scan. + + @param table Table to scan + @param usable_keys Allowed keys + + @note + As far as + 1) clustered primary key entry data set is a set of all record + fields (key fields and not key fields) and + 2) secondary index entry data is a union of its key fields and + primary key fields (at least InnoDB and its derivatives don't + duplicate primary key fields there, even if the primary and + the secondary keys have a common subset of key fields), + then secondary index entry data is always a subset of primary key entry. + Unfortunately, key_info[nr].key_length doesn't show the length + of key/pointer pair but a sum of key field lengths only, thus + we can't estimate index IO volume comparing only this key_length + value of secondary keys and clustered PK. + So, try secondary keys first, and choose PK only if there are no + usable secondary covering keys or found best secondary key include + all table fields (i.e. same as PK): + + @return + MAX_KEY no suitable key found + key index otherwise +*/ + uint find_shortest_key(TABLE *table, const key_map *usable_keys) { uint best= MAX_KEY; @@ -13029,23 +13057,6 @@ uint find_shortest_key(TABLE *table, const key_map *usable_keys) uint min_length= (uint) ~0; for (uint nr=0; nr < table->s->keys ; nr++) { - /* - As far as - 1) clustered primary key entry data set is a set of all record - fields (key fields and not key fields) and - 2) secondary index entry data is a union of its key fields and - primary key fields (at least InnoDB and its derivatives don't - duplicate primary key fields there, even if the primary and - the secondary keys have a common subset of key fields), - then secondary index entry data is always a subset of primary key - entry, and the PK is always longer. - Unfortunately, key_info[nr].key_length doesn't show the length - of key/pointer pair but a sum of key field lengths only, thus - we can't estimate index IO volume comparing only this key_length - value of seconday keys and clustered PK. - So, try secondary keys first, and choose PK only if there are no - usable secondary covering keys: - */ if (nr == usable_clustered_pk) continue; if (usable_keys->is_set(nr)) @@ -13058,7 +13069,20 @@ uint find_shortest_key(TABLE *table, const key_map *usable_keys) } } } - return best != MAX_KEY ? best : usable_clustered_pk; + if (usable_clustered_pk != MAX_KEY) + { + /* + If the primary key is clustered and found shorter key covers all table + fields then primary key scan normally would be faster because amount of + data to scan is the same but PK is clustered. + It's safe to compare key parts with table fields since duplicate key + parts aren't allowed. + */ + if (best == MAX_KEY || + table->key_info[best].key_parts >= table->s->fields) + best= usable_clustered_pk; + } + return best; } /** From d1fb4ba2995b3a0364d1a7bca21a20a203b5bfa1 Mon Sep 17 00:00:00 2001 From: Sergey Vojtovich Date: Thu, 26 Aug 2010 15:23:44 +0400 Subject: [PATCH 21/28] Fixed race condition in a test case for BUG#55580. --- mysql-test/suite/innodb/t/innodb_mysql.test | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mysql-test/suite/innodb/t/innodb_mysql.test b/mysql-test/suite/innodb/t/innodb_mysql.test index 4f13e15a627..ae03bebfbe3 100644 --- a/mysql-test/suite/innodb/t/innodb_mysql.test +++ b/mysql-test/suite/innodb/t/innodb_mysql.test @@ -768,9 +768,14 @@ START TRANSACTION; SELECT * FROM t1 LOCK IN SHARE MODE; connection con1; +let $conn_id= `SELECT CONNECTION_ID()`; --send SELECT * FROM t1 FOR UPDATE connection con2; +let $wait_timeout= 2; +let $wait_condition= SELECT 1 FROM INFORMATION_SCHEMA.PROCESSLIST +WHERE ID=$conn_id AND STATE='Sending data'; +--source include/wait_condition.inc --echo # should not crash --error ER_LOCK_DEADLOCK SELECT * FROM t1 GROUP BY (SELECT a FROM t2 LIMIT 1 FOR UPDATE) + t1.a; From a4154bd0a04ee863c07848e77ee727b4dc8244e8 Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Thu, 26 Aug 2010 15:14:50 +0200 Subject: [PATCH 22/28] Reduced #ifndef for 52301, caused compile warning --- client/mysqltest.cc | 4 ---- 1 file changed, 4 deletions(-) diff --git a/client/mysqltest.cc b/client/mysqltest.cc index c39ba4ff6ce..07eb66bc2b8 100644 --- a/client/mysqltest.cc +++ b/client/mysqltest.cc @@ -5220,10 +5220,8 @@ void do_connect(struct st_command *command) } #endif -#ifndef EMBEDDED_LIBRARY if (opt_protocol) mysql_options(&con_slot->mysql, MYSQL_OPT_PROTOCOL, (char*) &opt_protocol); -#endif #ifdef HAVE_SMEM if (con_shm) @@ -7936,10 +7934,8 @@ int main(int argc, char **argv) mysql_options(&con->mysql, MYSQL_SET_CHARSET_DIR, opt_charsets_dir); -#ifndef EMBEDDED_LIBRARY if (opt_protocol) mysql_options(&con->mysql,MYSQL_OPT_PROTOCOL,(char*)&opt_protocol); -#endif #ifdef HAVE_OPENSSL From ff17069f496ca7ae1e7fdd5944ec43e6e31a4075 Mon Sep 17 00:00:00 2001 From: Alexander Nozdrin Date: Thu, 26 Aug 2010 19:29:44 +0400 Subject: [PATCH 23/28] Enable plugin tests (Bug 55966) to check if they still fail. --- mysql-test/t/disabled.def | 3 --- 1 file changed, 3 deletions(-) diff --git a/mysql-test/t/disabled.def b/mysql-test/t/disabled.def index e2e1d76ef65..ce42668542f 100644 --- a/mysql-test/t/disabled.def +++ b/mysql-test/t/disabled.def @@ -14,8 +14,5 @@ lowercase_table3 : Bug#54845 2010-06-30 alik main.lowercase_table3 on Ma mysqlhotcopy_myisam : bug#54129 2010-06-04 Horst mysqlhotcopy_archive : bug#54129 2010-06-04 Horst partition_innodb_plugin : Bug#53307 2010-04-30 VasilDimov valgrind warnings -plugin : Bug#55966 2010-08-13 alik "plugin" tests fail in 5.5 -plugin_load : Bug#55966 2010-08-13 alik "plugin" tests fail in 5.5 -plugin_not_embedded : Bug#55966 2010-08-13 alik "plugin" tests fail in 5.5 query_cache_28249 : Bug#43861 2009-03-25 main.query_cache_28249 fails sporadically sp_sync : Bug#48157 2010-02-06 5.5-m3 demands a differnt solution From 7ebd2cd797f25013e2d2492ef6faa01fbf3cefc2 Mon Sep 17 00:00:00 2001 From: Ramil Kalimullin Date: Fri, 27 Aug 2010 11:44:06 +0400 Subject: [PATCH 24/28] Fix for bug #54253: memory leak when using I_S plugins w/o deinit method Free memory allocated by the server for all plugins, with or without deinit() method. --- sql/sql_show.cc | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 3e66cda16e1..e074461b452 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -6930,13 +6930,16 @@ int finalize_schema_table(st_plugin_int *plugin) ST_SCHEMA_TABLE *schema_table= (ST_SCHEMA_TABLE *)plugin->data; DBUG_ENTER("finalize_schema_table"); - if (schema_table && plugin->plugin->deinit) + if (schema_table) { - DBUG_PRINT("info", ("Deinitializing plugin: '%s'", plugin->name.str)); - if (plugin->plugin->deinit(NULL)) + if (plugin->plugin->deinit) { - DBUG_PRINT("warning", ("Plugin '%s' deinit function returned error.", - plugin->name.str)); + DBUG_PRINT("info", ("Deinitializing plugin: '%s'", plugin->name.str)); + if (plugin->plugin->deinit(NULL)) + { + DBUG_PRINT("warning", ("Plugin '%s' deinit function returned error.", + plugin->name.str)); + } } my_free(schema_table, MYF(0)); } From d7d0f6390b8d95b34ffb79f1a77ab11657e064a8 Mon Sep 17 00:00:00 2001 From: Alexey Kopytov Date: Fri, 27 Aug 2010 13:44:35 +0400 Subject: [PATCH 25/28] Bug #54465: assert: field_types == 0 || field_types[field_pos] == MYSQL_TYPE_LONGLONG A MIN/MAX() function with a subquery as its argument could lead to a debug assertion on debug builds or wrong data on release ones. The problem was a combination of the following factors: - Item_sum_hybrid::fix_fields() might use the argument (args[0]) to calculate 'hybrid_field_type' which was later used to decide how the data should be sent to the client. - Item_sum::make_field() might use the argument again to calculate the field's type when sending result set metadata to the client. - The argument could be changed in between these two calls via Item::set_arg() leading to inconsistent metadata being reported. Here is what was happening for the bug's test case: 1. Item_sum_hybrid::fix_fields() calculates hybrid_field_type as MYSQL_TYPE_LONGLONG based on args[0] which is an Item::SUBSELECT_ITEM at that time. 2. A temporary table is created to execute the query. create_tmp_field_from_item() creates a Field_long object according to the subselect's max_length. 3. The subselect item in Item_sum_hybrid is replaced by the Item_field object referencing the newly created Field_long. 4. Item_sum::make_field() rightfully returns the MYSQL_TYPE_LONG type when calculating the result set metadata. 5. When sending the actual data, Item::send() relies on the virtual field_type() function which in our case returns previously calculated hybrid_field_type == MYSQL_TYPE_LONGLONG. It looks like the only solution is to never refer to the argument's metadata after the result metadata has been calculated in fix_fields(), since the argument itself may be different by then. In this sense, Item_sum::make_field() should never be used, because it may rely on the argument's metadata and is only called after fix_fields(). The "default" implementation in Item::make_field() should be used instead as it relies only on field_type(), but not on the argument's type. Fixed by removing Item_sum::make_field() so that the superclass implementation Item::make_field() is always used. mysql-test/r/func_group.result: Added a test case for bug #54465. mysql-test/t/func_group.test: Added a test case for bug #54465. sql/item_sum.cc: Removed Item_sum::make_field() so that the superclass implementation Item::make_field() is always used. sql/item_sum.h: Removed Item_sum::make_field() so that the superclass implementation Item::make_field() is always used. --- mysql-test/r/func_group.result | 11 +++++++++++ mysql-test/t/func_group.test | 14 ++++++++++++++ sql/item_sum.cc | 20 -------------------- sql/item_sum.h | 1 - 4 files changed, 25 insertions(+), 21 deletions(-) diff --git a/mysql-test/r/func_group.result b/mysql-test/r/func_group.result index b36f561578b..606f879b47f 100644 --- a/mysql-test/r/func_group.result +++ b/mysql-test/r/func_group.result @@ -1713,4 +1713,15 @@ f1 f2 f3 f4 f1 = f2 NULL NULL NULL NULL NULL drop table t1; # +# Bug #54465: assert: field_types == 0 || field_types[field_pos] == +# MYSQL_TYPE_LONGLONG +# +CREATE TABLE t1 (a INT); +INSERT INTO t1 VALUES (1), (2); +SELECT MAX((SELECT 1 FROM t1 ORDER BY @var LIMIT 1)) m FROM t1 t2, t1 +ORDER BY t1.a; +m +1 +DROP TABLE t1; +# End of 5.1 tests diff --git a/mysql-test/t/func_group.test b/mysql-test/t/func_group.test index 6dbc8a05789..72a78f612a2 100644 --- a/mysql-test/t/func_group.test +++ b/mysql-test/t/func_group.test @@ -1082,6 +1082,20 @@ select a.f1 as a, b.f4 as b, a.f1 > b.f4 as gt, from t1 a, t1 b; select *, f1 = f2 from t1; drop table t1; + +--echo # +--echo # Bug #54465: assert: field_types == 0 || field_types[field_pos] == +--echo # MYSQL_TYPE_LONGLONG +--echo # + +CREATE TABLE t1 (a INT); +INSERT INTO t1 VALUES (1), (2); + +SELECT MAX((SELECT 1 FROM t1 ORDER BY @var LIMIT 1)) m FROM t1 t2, t1 + ORDER BY t1.a; + +DROP TABLE t1; + --echo # --echo End of 5.1 tests diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 25b3bd5d91d..ae9e46e2abf 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -417,26 +417,6 @@ void Item_sum::mark_as_sum_func() } -void Item_sum::make_field(Send_field *tmp_field) -{ - if (args[0]->type() == Item::FIELD_ITEM && keep_field_type()) - { - ((Item_field*) args[0])->field->make_field(tmp_field); - /* For expressions only col_name should be non-empty string. */ - char *empty_string= (char*)""; - tmp_field->db_name= empty_string; - tmp_field->org_table_name= empty_string; - tmp_field->table_name= empty_string; - tmp_field->org_col_name= empty_string; - tmp_field->col_name= name; - if (maybe_null) - tmp_field->flags&= ~NOT_NULL_FLAG; - } - else - init_make_field(tmp_field, field_type()); -} - - void Item_sum::print(String *str, enum_query_type query_type) { /* orig_args is not filled with valid values until fix_fields() */ diff --git a/sql/item_sum.h b/sql/item_sum.h index fe05858ab1d..26290a812f4 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -339,7 +339,6 @@ public: forced_const= TRUE; } virtual bool const_item() const { return forced_const; } - void make_field(Send_field *field); virtual void print(String *str, enum_query_type query_type); void fix_num_length_and_dec(); From 84628ea2ce62d4d44dfa6214f39c983269752ba4 Mon Sep 17 00:00:00 2001 From: Alexander Nozdrin Date: Mon, 30 Aug 2010 12:32:53 +0400 Subject: [PATCH 26/28] Disable 'plugin*' tests due to Bug 55966. --- mysql-test/t/disabled.def | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mysql-test/t/disabled.def b/mysql-test/t/disabled.def index ce42668542f..e2e1d76ef65 100644 --- a/mysql-test/t/disabled.def +++ b/mysql-test/t/disabled.def @@ -14,5 +14,8 @@ lowercase_table3 : Bug#54845 2010-06-30 alik main.lowercase_table3 on Ma mysqlhotcopy_myisam : bug#54129 2010-06-04 Horst mysqlhotcopy_archive : bug#54129 2010-06-04 Horst partition_innodb_plugin : Bug#53307 2010-04-30 VasilDimov valgrind warnings +plugin : Bug#55966 2010-08-13 alik "plugin" tests fail in 5.5 +plugin_load : Bug#55966 2010-08-13 alik "plugin" tests fail in 5.5 +plugin_not_embedded : Bug#55966 2010-08-13 alik "plugin" tests fail in 5.5 query_cache_28249 : Bug#43861 2009-03-25 main.query_cache_28249 fails sporadically sp_sync : Bug#48157 2010-02-06 5.5-m3 demands a differnt solution From be4ce1b811ac44366a3b0e55476eda41b2a5a4fb Mon Sep 17 00:00:00 2001 From: Dmitry Shulga Date: Mon, 30 Aug 2010 16:09:28 +0700 Subject: [PATCH 27/28] Follow-up for Bug#29751: FLUSH LOGS doesn't create -old file, so test case has to be updated. --- mysql-test/suite/rpl/r/rpl_flush_logs.result | 12 ---------- mysql-test/suite/rpl/t/rpl_flush_logs.test | 25 -------------------- 2 files changed, 37 deletions(-) diff --git a/mysql-test/suite/rpl/r/rpl_flush_logs.result b/mysql-test/suite/rpl/r/rpl_flush_logs.result index 7f50ce0cdd0..4c3352559c4 100644 --- a/mysql-test/suite/rpl/r/rpl_flush_logs.result +++ b/mysql-test/suite/rpl/r/rpl_flush_logs.result @@ -4,12 +4,8 @@ reset master; reset slave; drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; start slave; -# Make sure the 'master_log.err-old' file does not -# exist before execute 'flush error logs' statement. # Test if support 'flush error logs' statement. flush error logs; -# Check the 'master_log.err-old' file is created -# after executed 'flush error logs' statement. # Make sure binary logs was not be flushed # after execute 'flush error logs' statement. # Make sure relay logs was not be flushed @@ -42,12 +38,8 @@ flush binary logs; # after executed 'flush binary logs' statement. # Make sure the 'slave-relay-bin.000007' file does not exist # exist before execute 'flush error logs, relay logs' statement. -# Make sure the 'master_log.err-old' file does not exist -# before execute 'flush error logs, relay logs' statement. # Test if support to combine all kinds of logs into one statement. flush error logs, relay logs; -# Check the 'master_log.err-old' file is created -# after executed 'flush error logs, relay logs' statement. # Make sure binary logs was not be flushed # after execute 'flush error logs, relay logs' statement. # Check the 'slave-relay-bin.000007' file is created after @@ -55,12 +47,8 @@ flush error logs, relay logs; # Make sure the 'slave-relay-bin.000008' and 'slave-relay-bin.000009' # files do not exist before execute 'flush error logs, relay logs' # statement. -# Make sure the 'master_log.err-old' file does not exist -# before execute 'flush logs' statement. # Test if 'flush logs' statement works fine and flush all the logs. flush logs; -# Check the 'master_log.err-old' file is created -# after executed 'flush logs' statement. # Check 'master-bin.000003' is created # after execute 'flush logs' statement. # Check the 'slave-relay-bin.000008' and 'slave-relay-bin.000009' diff --git a/mysql-test/suite/rpl/t/rpl_flush_logs.test b/mysql-test/suite/rpl/t/rpl_flush_logs.test index 2118b48f946..5159acaae14 100644 --- a/mysql-test/suite/rpl/t/rpl_flush_logs.test +++ b/mysql-test/suite/rpl/t/rpl_flush_logs.test @@ -9,17 +9,10 @@ connection master; # Test 'flush error logs' statement. ---echo # Make sure the 'master_log.err-old' file does not ---echo # exist before execute 'flush error logs' statement. ---error 1 -file_exists $MYSQLTEST_VARDIR/tmp/master_log.err-old; --echo # Test if support 'flush error logs' statement. flush error logs; ---echo # Check the 'master_log.err-old' file is created ---echo # after executed 'flush error logs' statement. -file_exists $MYSQLTEST_VARDIR/tmp/master_log.err-old; file_exists $MYSQLTEST_VARDIR/tmp/master_log.err; --echo # Make sure binary logs was not be flushed @@ -109,19 +102,10 @@ sync_slave_with_master; file_exists $MYSQLTEST_VARDIR/mysqld.2/data/slave-relay-bin.000007; connection master; -remove_file $MYSQLTEST_VARDIR/tmp/master_log.err-old; - ---echo # Make sure the 'master_log.err-old' file does not exist ---echo # before execute 'flush error logs, relay logs' statement. ---error 1 -file_exists $MYSQLTEST_VARDIR/tmp/master_log.err-old; --echo # Test if support to combine all kinds of logs into one statement. flush error logs, relay logs; ---echo # Check the 'master_log.err-old' file is created ---echo # after executed 'flush error logs, relay logs' statement. -file_exists $MYSQLTEST_VARDIR/tmp/master_log.err-old; file_exists $MYSQLTEST_VARDIR/tmp/master_log.err; --echo # Make sure binary logs was not be flushed @@ -145,19 +129,10 @@ file_exists $MYSQLTEST_VARDIR/mysqld.2/data/slave-relay-bin.000008; file_exists $MYSQLTEST_VARDIR/mysqld.2/data/slave-relay-bin.000009; connection master; -remove_file $MYSQLTEST_VARDIR/tmp/master_log.err-old; - ---echo # Make sure the 'master_log.err-old' file does not exist ---echo # before execute 'flush logs' statement. ---error 1 -file_exists $MYSQLTEST_VARDIR/tmp/master_log.err-old; --echo # Test if 'flush logs' statement works fine and flush all the logs. flush logs; ---echo # Check the 'master_log.err-old' file is created ---echo # after executed 'flush logs' statement. -file_exists $MYSQLTEST_VARDIR/tmp/master_log.err-old; file_exists $MYSQLTEST_VARDIR/tmp/master_log.err; --echo # Check 'master-bin.000003' is created From ccab4d8771b728e334b26d0be1150b4ecf0bc7a6 Mon Sep 17 00:00:00 2001 From: Gleb Shchepa Date: Tue, 31 Aug 2010 02:16:38 +0400 Subject: [PATCH 28/28] Bug #53034: Multiple-table DELETE statements not accepting "Access compatibility" syntax The "wild" "DELETE FROM table_name.* ... USING ..." syntax for multi-table DELETE statements is documented but it was lost in the fix for the bug 30234. The table_ident_opt_wild parser rule has been added to restore the lost syntax. mysql-test/r/delete.result: Test case for bug #53034. mysql-test/t/delete.test: Test case for bug #53034. sql/sql_yacc.yy: Bug #53034: Multiple-table DELETE statements not accepting "Access compatibility" syntax The table_ident_opt_wild parser rule has been added to restore the lost syntax. Note: simple extending of table_ident with opt_wild in the table_alias_ref rule is not acceptable, because a) it adds one conflict more and b) this conflict resolves in the inappropriate way. --- mysql-test/r/delete.result | 9 +++++++++ mysql-test/t/delete.test | 13 +++++++++++++ sql/sql_yacc.yy | 18 +++++++++++++++++- 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/delete.result b/mysql-test/r/delete.result index 36025cbfb35..7d6fc30be67 100644 --- a/mysql-test/r/delete.result +++ b/mysql-test/r/delete.result @@ -358,4 +358,13 @@ INDEX(a), INDEX(b), INDEX(c)); INSERT INTO t1 VALUES (1,2,3), (4,5,6), (7,8,9); DELETE FROM t1 WHERE a = 10 OR b = 20 ORDER BY c LIMIT 1; DROP TABLE t1; +# +# Bug #53034: Multiple-table DELETE statements not accepting +# "Access compatibility" syntax +# +CREATE TABLE t1 (id INT); +CREATE TABLE t2 LIKE t1; +CREATE TABLE t3 LIKE t1; +DELETE FROM t1.*, test.t2.*, a.* USING t1, t2, t3 AS a; +DROP TABLE t1, t2, t3; End of 5.1 tests diff --git a/mysql-test/t/delete.test b/mysql-test/t/delete.test index 5a0e86568f3..ea5c87babbb 100644 --- a/mysql-test/t/delete.test +++ b/mysql-test/t/delete.test @@ -387,4 +387,17 @@ DELETE FROM t1 WHERE a = 10 OR b = 20 ORDER BY c LIMIT 1; DROP TABLE t1; +--echo # +--echo # Bug #53034: Multiple-table DELETE statements not accepting +--echo # "Access compatibility" syntax +--echo # + +CREATE TABLE t1 (id INT); +CREATE TABLE t2 LIKE t1; +CREATE TABLE t3 LIKE t1; + +DELETE FROM t1.*, test.t2.*, a.* USING t1, t2, t3 AS a; + +DROP TABLE t1, t2, t3; + --echo End of 5.1 tests diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index ed367582ba5..aa41a408e5b 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1297,6 +1297,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type table_ident table_ident_nodb references xid + table_ident_opt_wild %type remember_name remember_end opt_ident opt_db text_or_password @@ -9622,7 +9623,7 @@ table_alias_ref_list: ; table_alias_ref: - table_ident + table_ident_opt_wild { if (!Select->add_table_to_list(YYTHD, $1, NULL, TL_OPTION_UPDATING | TL_OPTION_ALIAS, @@ -11405,6 +11406,21 @@ table_ident: } ; +table_ident_opt_wild: + ident opt_wild + { + $$= new Table_ident($1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | ident '.' ident opt_wild + { + $$= new Table_ident(YYTHD, $1,$3,0); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + table_ident_nodb: ident {