From b1dcf448ead09e9a70cf035f20ac0e1a168e79a6 Mon Sep 17 00:00:00 2001 From: Mats Kindahl Date: Mon, 7 Jun 2010 16:01:39 +0200 Subject: [PATCH 01/31] 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 71553c2c9db3c4c27973f12461b986c59e1dabfb Mon Sep 17 00:00:00 2001 From: Mats Kindahl Date: Tue, 15 Jun 2010 09:44:26 +0200 Subject: [PATCH 02/31] 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 0b273845def50dfd22e8a3e34c4b6f82fa522539 Mon Sep 17 00:00:00 2001 From: Mattias Jonsson Date: Thu, 8 Jul 2010 14:36:55 +0200 Subject: [PATCH 03/31] 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 | 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 bb3fbba1af97d95ec60f604e10796b9cff9b432d Mon Sep 17 00:00:00 2001 From: Alexey Kopytov Date: Fri, 23 Jul 2010 15:52:54 +0400 Subject: [PATCH 04/31] 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 | 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 38165ce4a3fdd0d539fa5b0972d105af31a0d134 Mon Sep 17 00:00:00 2001 From: Gleb Shchepa Date: Sun, 1 Aug 2010 22:12:36 +0400 Subject: [PATCH 05/31] 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 | 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 4ee89071af88ad61ce2e1291c336630df548d6b9 Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Mon, 2 Aug 2010 10:48:24 +0300 Subject: [PATCH 06/31] 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 1feee134fdc346bb1ad73cce54cd7e87df5c839a Mon Sep 17 00:00:00 2001 From: Alfranio Correia Date: Mon, 2 Aug 2010 20:48:56 +0100 Subject: [PATCH 07/31] 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 | 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 12f7d57d42f426803a94877664063c2809e63cf9 Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Fri, 13 Aug 2010 11:07:39 +0300 Subject: [PATCH 08/31] 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 b6e3adf10f603d0ec670bc21c3c67a975d6717f7 Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Fri, 13 Aug 2010 16:05:46 +0300 Subject: [PATCH 09/31] 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 e1e6c32b7290e2ff8dd894ba46eb21fda2d3a6af Mon Sep 17 00:00:00 2001 From: Mats Kindahl Date: Tue, 17 Aug 2010 07:46:53 +0200 Subject: [PATCH 10/31] 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 b766a51f419eb5ff831e935f28c17a6a41cd5e58 Mon Sep 17 00:00:00 2001 From: Date: Wed, 18 Aug 2010 12:56:06 +0800 Subject: [PATCH 11/31] 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. --- .../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 f0af63ad5085d31bf93e2dc396f66a8871f3e40b Mon Sep 17 00:00:00 2001 From: Mats Kindahl Date: Wed, 18 Aug 2010 12:53:43 +0200 Subject: [PATCH 12/31] 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 12e10ba2ba9eacb92567b26f2bcd57868b7fdaaa Mon Sep 17 00:00:00 2001 From: MySQL Build Team Date: Thu, 19 Aug 2010 17:03:29 +0200 Subject: [PATCH 13/31] 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 6973c354de11570f5575abdbd40bb47dce636d34 Mon Sep 17 00:00:00 2001 From: Mattias Jonsson Date: Fri, 20 Aug 2010 21:17:51 +0200 Subject: [PATCH 14/31] 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 756076bd23d9e194d5730745d07ee86aa543d4e5 Mon Sep 17 00:00:00 2001 From: Alexey Kopytov Date: Tue, 24 Aug 2010 19:51:32 +0400 Subject: [PATCH 15/31] 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 a22b5651fd18da897da322b4081fa05dffaee328 Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Wed, 25 Aug 2010 10:23:19 +0200 Subject: [PATCH 16/31] 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 8520a9fd62ea14df8ed9f52d83f05e3895be5e76 Mon Sep 17 00:00:00 2001 From: Dmitry Shulga Date: Wed, 25 Aug 2010 15:47:45 +0700 Subject: [PATCH 17/31] 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 | 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 8560cafb1ed3afbb1f845eeaeb40808c516b369e Mon Sep 17 00:00:00 2001 From: "joerg.bruehe@oracle.com" <> Date: Wed, 25 Aug 2010 16:05:33 +0200 Subject: [PATCH 18/31] 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 16b7af9ae0a5833442138b02f0b2eb844f2e9fd0 Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Wed, 25 Aug 2010 16:34:31 +0200 Subject: [PATCH 19/31] 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 151af144ffdc6ae4eed7ec78df7b87cf4928c3a7 Mon Sep 17 00:00:00 2001 From: Evgeny Potemkin Date: Thu, 26 Aug 2010 13:31:04 +0400 Subject: [PATCH 20/31] 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 | 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 0648087ca035bf3aebd4e886f11eec8ebdd4366c Mon Sep 17 00:00:00 2001 From: Sergey Vojtovich Date: Thu, 26 Aug 2010 15:23:44 +0400 Subject: [PATCH 21/31] 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 fba50f8401453502757b7de8927d62a5a5880509 Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Thu, 26 Aug 2010 15:14:50 +0200 Subject: [PATCH 22/31] 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 c88cfec29cdb47bbc76afd0c027d0fcce2109961 Mon Sep 17 00:00:00 2001 From: Alexander Nozdrin Date: Thu, 26 Aug 2010 19:29:44 +0400 Subject: [PATCH 23/31] 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 1087cfc4d5f616f386d7d2b4f912bac358a05517 Mon Sep 17 00:00:00 2001 From: Ramil Kalimullin Date: Fri, 27 Aug 2010 11:44:06 +0400 Subject: [PATCH 24/31] 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 b409a2218e7fe7371ce81f71f7beb2716da8a05b Mon Sep 17 00:00:00 2001 From: Alexey Kopytov Date: Fri, 27 Aug 2010 13:44:35 +0400 Subject: [PATCH 25/31] 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 | 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 a87059b9406b2fbdf8d890220821e64e1767717a Mon Sep 17 00:00:00 2001 From: Alexander Nozdrin Date: Mon, 30 Aug 2010 12:32:53 +0400 Subject: [PATCH 26/31] 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 05482f3c7b4dd40c4a99ee012f6c3a49b7ab0fde Mon Sep 17 00:00:00 2001 From: Dmitry Shulga Date: Mon, 30 Aug 2010 16:09:28 +0700 Subject: [PATCH 27/31] 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 9554b7ac6ef48ea68ce5c8eed34e5dcb21031791 Mon Sep 17 00:00:00 2001 From: Gleb Shchepa Date: Tue, 31 Aug 2010 02:16:38 +0400 Subject: [PATCH 28/31] 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 | 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 { From a33400442031745a115f3ef82f6891d8eb231885 Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Wed, 1 Sep 2010 08:38:53 +0500 Subject: [PATCH 29/31] Bug#54906 Inconsistent license of libmysqld made libmysqld/Makefile.am to have same licence as libmysqld/CMakeLists.txt per-file comments: libmysqld/Makefile.am Bug#54906 Inconsistent license of libmysqld Added GPL license header instead of Library GPL. --- libmysqld/Makefile.am | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/libmysqld/Makefile.am b/libmysqld/Makefile.am index b4083be0966..dabfa2b02cc 100644 --- a/libmysqld/Makefile.am +++ b/libmysqld/Makefile.am @@ -1,21 +1,17 @@ -# Copyright (C) 2001-2006 MySQL AB +# Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. # -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Library General Public -# License as published by the Free Software Foundation; version 2 -# of the License. +# 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 library is distributed in the hope that it will be useful, +# 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 -# Library General Public License for more details. +# 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 Library General Public -# License along with this library; if not, write to the Free -# Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, -# MA 02111-1307, USA -# -# This file is public domain and comes with NO WARRANTY of any kind +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA MYSQLDATAdir = $(localstatedir) MYSQLSHAREdir = $(pkgdatadir) From b9e063ca31aebb7db1a6b87d16542bbef9065f67 Mon Sep 17 00:00:00 2001 From: Marc Alff Date: Wed, 1 Sep 2010 19:13:24 -0600 Subject: [PATCH 30/31] Bug#55873 short startup options do not work in 5.5 Merge cleanup, fixed a build warning: my_getopt.c:156: warning: 'opt_found' may be used uninitialized in this function --- mysys/my_getopt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysys/my_getopt.c b/mysys/my_getopt.c index 4faaadb3f70..bc99bad3794 100644 --- a/mysys/my_getopt.c +++ b/mysys/my_getopt.c @@ -163,7 +163,6 @@ int handle_options(int *argc, char ***argv, int error, i; my_bool is_cmdline_arg= 1; - LINT_INIT(opt_found); /* handle_options() assumes arg0 (program name) always exists */ DBUG_ASSERT(argc && *argc >= 1); DBUG_ASSERT(argv && *argv); @@ -188,6 +187,7 @@ int handle_options(int *argc, char ***argv, { char **first= pos; char *cur_arg= *pos; + opt_found= 0; if (!is_cmdline_arg && (cur_arg == args_separator)) { is_cmdline_arg= 1; From 08e1a5b770e099430e046d3dcd5f8d5fe4f1a279 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Fri, 3 Sep 2010 00:17:08 +0200 Subject: [PATCH 31/31] Small fixes in CMake: create data dir correctly in initial_database target on Windows handle case where INSTALL_MYSQLTESTDIR is empty (e.g someone does not want to install tests) --- mysql-test/CMakeLists.txt | 10 +++++++--- sql/CMakeLists.txt | 3 +-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/mysql-test/CMakeLists.txt b/mysql-test/CMakeLists.txt index b8eb11b2fea..3c38e5772d0 100644 --- a/mysql-test/CMakeLists.txt +++ b/mysql-test/CMakeLists.txt @@ -13,6 +13,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +IF(INSTALL_MYSQLTESTDIR) INSTALL( DIRECTORY . DESTINATION ${INSTALL_MYSQLTESTDIR} @@ -28,6 +29,7 @@ INSTALL( PATTERN "*.am" EXCLUDE PATTERN "*.in" EXCLUDE ) +ENDIF() @@ -48,9 +50,11 @@ IF(UNIX) ./mysql-test-run.pl mysql-test-run WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} ) - INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/mtr - ${CMAKE_CURRENT_BINARY_DIR}/mysql-test-run - DESTINATION ${INSTALL_MYSQLTESTDIR}) + IF(INSTALL_MYSQLTESTDIR) + INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/mtr + ${CMAKE_CURRENT_BINARY_DIR}/mysql-test-run + DESTINATION ${INSTALL_MYSQLTESTDIR}) + ENDIF() ENDIF() IF(CMAKE_GENERATOR MATCHES "Visual Studio") diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 224cf67af7f..9d307da364d 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -271,8 +271,7 @@ IF(WIN32 AND MYSQLD_EXECUTABLE) COMMAND ${CMAKE_COMMAND} ${CONFIG_PARAM} -P ${CMAKE_CURRENT_BINARY_DIR}/create_initial_db.cmake WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/data - COMMAND ${CMAKE_COMMAND} -E touch initdb.dep - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/initdb.dep DEPENDS mysqld ) ADD_CUSTOM_TARGET(initial_database