diff options
Diffstat (limited to 'client')
-rw-r--r-- | client/CMakeLists.txt | 24 | ||||
-rw-r--r-- | client/async_example.c | 216 | ||||
-rw-r--r-- | client/client_priv.h | 46 | ||||
-rw-r--r-- | client/get_password.c | 8 | ||||
-rw-r--r-- | client/my_readline.h | 4 | ||||
-rw-r--r-- | client/mysql.cc | 707 | ||||
-rw-r--r-- | client/mysql_plugin.c | 15 | ||||
-rw-r--r-- | client/mysql_upgrade.c | 354 | ||||
-rw-r--r-- | client/mysqladmin.cc | 218 | ||||
-rw-r--r-- | client/mysqlbinlog.cc | 620 | ||||
-rw-r--r-- | client/mysqlcheck.c | 516 | ||||
-rw-r--r-- | client/mysqldump.c | 310 | ||||
-rw-r--r-- | client/mysqlimport.c | 71 | ||||
-rw-r--r-- | client/mysqlshow.c | 32 | ||||
-rw-r--r-- | client/mysqlslap.c | 210 | ||||
-rw-r--r-- | client/mysqltest.cc | 1462 | ||||
-rw-r--r-- | client/readline.cc | 22 | ||||
-rw-r--r-- | client/sql_string.cc.dontuse (renamed from client/sql_string.cc) | 8 | ||||
-rw-r--r-- | client/sql_string.h.dontuse (renamed from client/sql_string.h) | 21 |
19 files changed, 3365 insertions, 1499 deletions
diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 21b1e084409..e3edacc351c 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -21,27 +21,23 @@ INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/regex ${CMAKE_SOURCE_DIR}/sql ${CMAKE_SOURCE_DIR}/strings - ${READLINE_INCLUDE_DIR} + ${MY_READLINE_INCLUDE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ) -ADD_DEFINITIONS(${READLINE_DEFINES}) ADD_DEFINITIONS(${SSL_DEFINES}) -MYSQL_ADD_EXECUTABLE(mysql completion_hash.cc mysql.cc readline.cc sql_string.cc) +MYSQL_ADD_EXECUTABLE(mysql completion_hash.cc mysql.cc readline.cc + ${CMAKE_SOURCE_DIR}/sql/sql_string.cc) TARGET_LINK_LIBRARIES(mysql mysqlclient) IF(UNIX) - TARGET_LINK_LIBRARIES(mysql ${READLINE_LIBRARY}) + TARGET_LINK_LIBRARIES(mysql ${MY_READLINE_LIBRARY}) + SET_TARGET_PROPERTIES(mysql PROPERTIES ENABLE_EXPORTS TRUE) ENDIF(UNIX) MYSQL_ADD_EXECUTABLE(mysqltest mysqltest.cc COMPONENT Test) SET_SOURCE_FILES_PROPERTIES(mysqltest.cc PROPERTIES COMPILE_FLAGS "-DTHREADS") -# mysqltest has unused result errors, so we skip Werror -CHECK_C_COMPILER_FLAG("-Werror" HAVE_WERROR_FLAG) -IF(HAVE_WERROR_FLAG) - INCLUDE(${MYSQL_CMAKE_SCRIPT_DIR}/compile_flags.cmake) - ADD_COMPILE_FLAGS(mysqltest.cc COMPILE_FLAGS "-Wno-error") -ENDIF() TARGET_LINK_LIBRARIES(mysqltest mysqlclient regex) +SET_TARGET_PROPERTIES(mysqltest PROPERTIES ENABLE_EXPORTS TRUE) MYSQL_ADD_EXECUTABLE(mysqlcheck mysqlcheck.c) @@ -54,7 +50,7 @@ MYSQL_ADD_EXECUTABLE(mysqlimport mysqlimport.c) SET_SOURCE_FILES_PROPERTIES(mysqlimport.c PROPERTIES COMPILE_FLAGS "-DTHREADS") TARGET_LINK_LIBRARIES(mysqlimport mysqlclient) -MYSQL_ADD_EXECUTABLE(mysql_upgrade mysql_upgrade.c) +MYSQL_ADD_EXECUTABLE(mysql_upgrade mysql_upgrade.c COMPONENT Server) TARGET_LINK_LIBRARIES(mysql_upgrade mysqlclient) ADD_DEPENDENCIES(mysql_upgrade GenFixPrivs) @@ -76,9 +72,13 @@ TARGET_LINK_LIBRARIES(mysqlslap mysqlclient) # "WIN32" also covers 64 bit. "echo" is used in some files below "mysql-test/". IF(WIN32) - MYSQL_ADD_EXECUTABLE(echo echo.c) + MYSQL_ADD_EXECUTABLE(echo echo.c COMPONENT Junk) ENDIF(WIN32) +# async_example is just a code example, do not install it. +ADD_EXECUTABLE(async_example async_example.c) +TARGET_LINK_LIBRARIES(async_example mysqlclient) + SET_TARGET_PROPERTIES (mysqlcheck mysqldump mysqlimport mysql_upgrade mysqlshow mysqlslap mysql_plugin PROPERTIES HAS_CXX TRUE) diff --git a/client/async_example.c b/client/async_example.c new file mode 100644 index 00000000000..ccb60950904 --- /dev/null +++ b/client/async_example.c @@ -0,0 +1,216 @@ +/* + Copyright 2011 Kristian Nielsen and Monty Program Ab. + + This file is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this. If not, see <http://www.gnu.org/licenses/>. +*/ + + +#ifndef __WIN__ +#include <poll.h> +#else +#include <WinSock2.h> +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <mysql.h> + +#define SL(s) (s), sizeof(s) + +static const char *my_groups[]= { "client", NULL }; + +static int +wait_for_mysql(MYSQL *mysql, int status) +{ +#ifdef __WIN__ + fd_set rs, ws, es; + int res; + struct timeval tv, *timeout; + my_socket s= mysql_get_socket(mysql); + FD_ZERO(&rs); + FD_ZERO(&ws); + FD_ZERO(&es); + if (status & MYSQL_WAIT_READ) + FD_SET(s, &rs); + if (status & MYSQL_WAIT_WRITE) + FD_SET(s, &ws); + if (status & MYSQL_WAIT_EXCEPT) + FD_SET(s, &es); + if (status & MYSQL_WAIT_TIMEOUT) + { + tv.tv_sec= mysql_get_timeout_value(mysql); + tv.tv_usec= 0; + timeout= &tv; + } + else + timeout= NULL; + res= select(1, &rs, &ws, &es, timeout); + if (res == 0) + return MYSQL_WAIT_TIMEOUT; + else if (res == SOCKET_ERROR) + { + /* + In a real event framework, we should handle errors and re-try the select. + */ + return MYSQL_WAIT_TIMEOUT; + } + else + { + int status= 0; + if (FD_ISSET(s, &rs)) + status|= MYSQL_WAIT_READ; + if (FD_ISSET(s, &ws)) + status|= MYSQL_WAIT_WRITE; + if (FD_ISSET(s, &es)) + status|= MYSQL_WAIT_EXCEPT; + return status; + } +#else + struct pollfd pfd; + int timeout; + int res; + + pfd.fd= mysql_get_socket(mysql); + pfd.events= + (status & MYSQL_WAIT_READ ? POLLIN : 0) | + (status & MYSQL_WAIT_WRITE ? POLLOUT : 0) | + (status & MYSQL_WAIT_EXCEPT ? POLLPRI : 0); + if (status & MYSQL_WAIT_TIMEOUT) + timeout= 1000*mysql_get_timeout_value(mysql); + else + timeout= -1; + res= poll(&pfd, 1, timeout); + if (res == 0) + return MYSQL_WAIT_TIMEOUT; + else if (res < 0) + { + /* + In a real event framework, we should handle EINTR and re-try the poll. + */ + return MYSQL_WAIT_TIMEOUT; + } + else + { + int status= 0; + if (pfd.revents & POLLIN) + status|= MYSQL_WAIT_READ; + if (pfd.revents & POLLOUT) + status|= MYSQL_WAIT_WRITE; + if (pfd.revents & POLLPRI) + status|= MYSQL_WAIT_EXCEPT; + return status; + } +#endif +} + +static void +fatal(MYSQL *mysql, const char *msg) +{ + fprintf(stderr, "%s: %s\n", msg, mysql_error(mysql)); + exit(1); +} + +static void +doit(const char *host, const char *user, const char *password) +{ + int err; + MYSQL mysql, *ret; + MYSQL_RES *res; + MYSQL_ROW row; + int status; + + mysql_init(&mysql); + mysql_options(&mysql, MYSQL_OPT_NONBLOCK, 0); + mysql_options(&mysql, MYSQL_READ_DEFAULT_GROUP, "myapp"); + + /* Returns 0 when done, else flag for what to wait for when need to block. */ + status= mysql_real_connect_start(&ret, &mysql, host, user, password, NULL, + 0, NULL, 0); + while (status) + { + status= wait_for_mysql(&mysql, status); + status= mysql_real_connect_cont(&ret, &mysql, status); + } + + if (!ret) + fatal(&mysql, "Failed to mysql_real_connect()"); + + status= mysql_real_query_start(&err, &mysql, SL("SHOW STATUS")); + while (status) + { + status= wait_for_mysql(&mysql, status); + status= mysql_real_query_cont(&err, &mysql, status); + } + if (err) + fatal(&mysql, "mysql_real_query() returns error"); + + /* This method cannot block. */ + res= mysql_use_result(&mysql); + if (!res) + fatal(&mysql, "mysql_use_result() returns error"); + + for (;;) + { + status= mysql_fetch_row_start(&row, res); + while (status) + { + status= wait_for_mysql(&mysql, status); + status= mysql_fetch_row_cont(&row, res, status); + } + if (!row) + break; + printf("%s: %s\n", row[0], row[1]); + } + if (mysql_errno(&mysql)) + fatal(&mysql, "Got error while retrieving rows"); + mysql_free_result(res); + + /* + mysql_close() sends a COM_QUIT packet, and so in principle could block + waiting for the socket to accept the data. + In practise, for many applications it will probably be fine to use the + blocking mysql_close(). + */ + status= mysql_close_start(&mysql); + while (status) + { + status= wait_for_mysql(&mysql, status); + status= mysql_close_cont(&mysql, status); + } +} + +int +main(int argc, char *argv[]) +{ + int err; + + if (argc != 4) + { + fprintf(stderr, "Usage: %s <host> <user> <password>\n", argv[0]); + exit(1); + } + + err= mysql_library_init(argc, argv, (char **)my_groups); + if (err) + { + fprintf(stderr, "Fatal: mysql_library_init() returns error: %d\n", err); + exit(1); + } + + doit(argv[1], argv[2], argv[3]); + + mysql_library_end(); + + return 0; +} diff --git a/client/client_priv.h b/client/client_priv.h index e53ced7e790..d89ca89c405 100644 --- a/client/client_priv.h +++ b/client/client_priv.h @@ -1,5 +1,6 @@ /* - Copyright (c) 2001, 2016, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2001, 2012, Oracle and/or its affiliates. + Copyright (c) 2009, 2016, MariaDB 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 @@ -52,6 +53,7 @@ enum options_client OPT_OPEN_FILES_LIMIT, OPT_SET_CHARSET, OPT_SERVER_ARG, OPT_STOP_POSITION, OPT_START_DATETIME, OPT_STOP_DATETIME, OPT_SIGINT_IGNORE, OPT_HEXBLOB, OPT_ORDER_BY_PRIMARY, OPT_COUNT, + OPT_FLUSH_TABLES, OPT_TRIGGERS, OPT_MYSQL_ONLY_PRINT, OPT_MYSQL_LOCK_DIRECTORY, @@ -86,10 +88,11 @@ enum options_client OPT_INIT_COMMAND, OPT_PLUGIN_DIR, OPT_DEFAULT_AUTH, - OPT_DEFAULT_PLUGIN, - OPT_ENABLE_CLEARTEXT_PLUGIN, - OPT_SSL_MODE, - OPT_MAX_CLIENT_OPTION + OPT_ABORT_SOURCE_ON_ERROR, + OPT_REWRITE_DB, + OPT_REPORT_PROGRESS, + OPT_SKIP_ANNOTATE_ROWS_EVENTS, + OPT_MAX_CLIENT_OPTION /* should be always the last */ }; /** @@ -112,36 +115,3 @@ enum options_client */ #define PERFORMANCE_SCHEMA_DB_NAME "performance_schema" -/** - Wrapper for mysql_real_connect() that checks if SSL connection is establised. - - The function calls mysql_real_connect() first, then if given ssl_required==TRUE - argument (i.e. --ssl-mode=REQUIRED option used) checks current SSL chiper to - ensure that SSL is used for current connection. - Otherwise it returns NULL and sets errno to CR_SSL_CONNECTION_ERROR. - - All clients (except mysqlbinlog which disregards SSL options) use this function - instead of mysql_real_connect() to handle --ssl-mode=REQUIRED option. -*/ -MYSQL *mysql_connect_ssl_check(MYSQL *mysql_arg, const char *host, - const char *user, const char *passwd, - const char *db, uint port, - const char *unix_socket, ulong client_flag, - my_bool ssl_required __attribute__((unused))) -{ - MYSQL *mysql= mysql_real_connect(mysql_arg, host, user, passwd, db, port, - unix_socket, client_flag); -#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) - if (mysql && /* connection established. */ - ssl_required && /* --ssl-mode=REQUIRED. */ - !mysql_get_ssl_cipher(mysql)) /* non-SSL connection. */ - { - NET *net= &mysql->net; - net->last_errno= CR_SSL_CONNECTION_ERROR; - strmov(net->last_error, "--ssl-mode=REQUIRED option forbids non SSL connections"); - strmov(net->sqlstate, "HY000"); - return NULL; - } -#endif - return mysql; -} diff --git a/client/get_password.c b/client/get_password.c index d8d6dc93707..8a507d94e9b 100644 --- a/client/get_password.c +++ b/client/get_password.c @@ -24,10 +24,6 @@ #include <m_string.h> #include <m_ctype.h> -#if defined(HAVE_BROKEN_GETPASS) && !defined(HAVE_GETPASSPHRASE) -#undef HAVE_GETPASS -#endif - #ifdef HAVE_GETPASS #ifdef HAVE_PWD_H #include <pwd.h> @@ -114,7 +110,7 @@ static void get_password(char *to,uint length,int fd, my_bool echo) for (;;) { - char tmp; + uchar tmp; if (my_read(fd,&tmp,1,MYF(0)) != 1) break; if (tmp == '\b' || (int) tmp == 127) @@ -139,7 +135,7 @@ static void get_password(char *to,uint length,int fd, my_bool echo) fputc('*',stderr); fflush(stderr); } - *(pos++) = tmp; + *(pos++)= (char) tmp; } while (pos != to && isspace(pos[-1]) == ' ') pos--; /* Allow dummy space at end */ diff --git a/client/my_readline.h b/client/my_readline.h index d288a33e1cc..57537308fed 100644 --- a/client/my_readline.h +++ b/client/my_readline.h @@ -2,7 +2,7 @@ #define CLIENT_MY_READLINE_INCLUDED /* - Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2000, 2011, Oracle and/or its affiliates This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -36,7 +36,7 @@ typedef struct st_line_buffer extern LINE_BUFFER *batch_readline_init(ulong max_size,FILE *file); extern LINE_BUFFER *batch_readline_command(LINE_BUFFER *buffer, char * str); -extern char *batch_readline(LINE_BUFFER *buffer); +extern char *batch_readline(LINE_BUFFER *buffer, bool binary_mode); extern void batch_readline_end(LINE_BUFFER *buffer); #endif /* CLIENT_MY_READLINE_INCLUDED */ diff --git a/client/mysql.cc b/client/mysql.cc index cdc2ab0d6e0..9d255b55430 100644 --- a/client/mysql.cc +++ b/client/mysql.cc @@ -1,5 +1,6 @@ /* - Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2000, 2014, Oracle and/or its affiliates. + Copyright (c) 2009, 2016, MariaDB 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 @@ -12,8 +13,7 @@ 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 -*/ + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* mysql command tool * Commands compatible with mSQL by David J. Hughes @@ -45,7 +45,7 @@ #include <locale.h> #endif -const char *VER= "14.14"; +const char *VER= "15.1"; /* Don't try to make a nice table if the data is too big */ #define MAX_COLUMN_LENGTH 1024 @@ -83,21 +83,28 @@ extern "C" { #include <term.h> #endif #endif -#endif +#endif /* defined(HAVE_CURSES_H) && defined(HAVE_TERM_H) */ +#undef bcmp // Fix problem with new readline #if defined(__WIN__) #include <conio.h> #else -#include <readline/readline.h> +#include <readline.h> #define HAVE_READLINE #define USE_POPEN #endif - //int vidattr(long unsigned int attrs); // Was missing in sun curses } -#if !defined(HAVE_VIDATTR) -#undef vidattr -#define vidattr(A) {} // Can't get this to work +#ifdef HAVE_VIDATTR +static int have_curses= 0; +static void my_vidattr(chtype attrs) +{ + if (have_curses) + vidattr(attrs); +} +#else +#undef HAVE_SETUPTERM +#define my_vidattr(A) {} // Can't get this to work #endif #ifdef FN_NO_CASE_SENSE @@ -141,18 +148,19 @@ static my_bool ignore_errors=0,wait_flag=0,quick=0, opt_secure_auth= 0, default_pager_set= 0, opt_sigint_ignore= 0, auto_vertical_output= 0, - show_warnings= 0, executing_query= 0, interrupted_query= 0, - ignore_spaces= 0; -static my_bool debug_info_flag, debug_check_flag; + show_warnings= 0, executing_query= 0, + ignore_spaces= 0, opt_progress_reports; +static my_bool debug_info_flag, debug_check_flag, batch_abort_on_error; static my_bool column_types_flag; static my_bool preserve_comments= 0; +static my_bool in_com_source, aborted= 0; static ulong opt_max_allowed_packet, opt_net_buffer_length; static uint verbose=0,opt_silent=0,opt_mysql_port=0, opt_local_infile=0; -static uint opt_enable_cleartext_plugin= 0; -static my_bool using_opt_enable_cleartext_plugin= 0; static uint my_end_arg; static char * opt_mysql_unix_port=0; static int connect_flag=CLIENT_INTERACTIVE; +static my_bool opt_binary_mode= FALSE; +static int interrupted_query= 0; static char *current_host,*current_db,*current_user=0,*opt_password=0, *current_prompt=0, *delimiter_str= 0, *default_charset= (char*) MYSQL_AUTODETECT_CHARSET_NAME, @@ -242,8 +250,13 @@ static void init_username(); static void add_int_to_prompt(int toadd); static int get_result_width(MYSQL_RES *res); static int get_field_disp_length(MYSQL_FIELD * field); -static int normalize_dbname(const char *line, char *buff, uint buff_size); -static int get_quote_count(const char *line); +#ifndef EMBEDDED_LIBRARY +static uint last_progress_report_length= 0; +static void report_progress(const MYSQL *mysql, uint stage, uint max_stage, + double progress, const char *proc_info, + uint proc_info_length); +#endif +static void report_progress_end(); /* A structure which contains information on the commands this program can understand. */ @@ -889,6 +902,7 @@ static COMMANDS commands[] = { { "LAST_INSERT_ID", 0, 0, 0, ""}, { "ISSIMPLE", 0, 0, 0, ""}, { "LAST_DAY", 0, 0, 0, ""}, + { "LAST_VALUE", 0, 0, 0, ""}, { "LCASE", 0, 0, 0, ""}, { "LEAST", 0, 0, 0, ""}, { "LENGTH", 0, 0, 0, ""}, @@ -1020,12 +1034,13 @@ static COMMANDS commands[] = { { (char *)NULL, 0, 0, 0, ""} }; -static const char *load_default_groups[]= { "mysql","client",0 }; +static const char *load_default_groups[]= +{ "mysql", "client", "client-server", "client-mariadb", 0 }; static int embedded_server_arg_count= 0; static char *embedded_server_args[MAX_SERVER_ARGS]; static const char *embedded_server_groups[]= -{ "server", "embedded", "mysql_SERVER", 0 }; +{ "server", "embedded", "mysql_SERVER", "mariadb_SERVER", 0 }; #ifdef HAVE_READLINE /* @@ -1049,9 +1064,10 @@ static void initialize_readline (char *name); static void fix_history(String *final_command); #endif -static COMMANDS *find_command(char *name,char cmd_name); -static bool add_line(String &buffer,char *line,char *in_string, - bool *ml_comment, bool truncated); +static COMMANDS *find_command(char *name); +static COMMANDS *find_command(char cmd_name); +static bool add_line(String &buffer, char *line, ulong line_length, + char *in_string, bool *ml_comment, bool truncated); static void remove_cntrl(String &buffer); static void print_table_data(MYSQL_RES *result); static void print_table_data_html(MYSQL_RES *result); @@ -1070,6 +1086,45 @@ static sig_handler window_resize(int sig); #endif +const char DELIMITER_NAME[]= "delimiter"; +const uint DELIMITER_NAME_LEN= sizeof(DELIMITER_NAME) - 1; +inline bool is_delimiter_command(char *name, ulong len) +{ + /* + Delimiter command has a parameter, so the length of the whole command + is larger than DELIMITER_NAME_LEN. We don't care the parameter, so + only name(first DELIMITER_NAME_LEN bytes) is checked. + */ + return (len >= DELIMITER_NAME_LEN && + !my_strnncoll(charset_info, (uchar*) name, DELIMITER_NAME_LEN, + (uchar *) DELIMITER_NAME, DELIMITER_NAME_LEN)); +} + +/** + Get the index of a command in the commands array. + + @param cmd_char Short form command. + + @return int + The index of the command is returned if it is found, else -1 is returned. +*/ +inline int get_command_index(char cmd_char) +{ + /* + All client-specific commands are in the first part of commands array + and have a function to implement it. + */ + for (uint i= 0; commands[i].func; i++) + if (commands[i].cmd_char == cmd_char) + return i; + return -1; +} + +static int delimiter_index= -1; +static int charset_index= -1; +static bool real_binary_mode= FALSE; + + int main(int argc,char *argv[]) { char buff[80]; @@ -1078,12 +1133,15 @@ int main(int argc,char *argv[]) DBUG_ENTER("main"); DBUG_PROCESS(argv[0]); + charset_index= get_command_index('C'); + delimiter_index= get_command_index('d'); delimiter_str= delimiter; default_prompt = my_strdup(getenv("MYSQL_PS1") ? getenv("MYSQL_PS1") : - "mysql> ",MYF(MY_WME)); + "\\N [\\d]> ",MYF(MY_WME)); current_prompt = my_strdup(default_prompt,MYF(MY_WME)); prompt_counter=0; + aborted= 0; outfile[0]=0; // no (default) outfile strmov(pager, "stdout"); // the default, if --pager wasn't given @@ -1174,11 +1232,12 @@ int main(int argc,char *argv[]) window_resize(0); #endif - put_info("Welcome to the MySQL monitor. Commands end with ; or \\g.", + put_info("Welcome to the MariaDB monitor. Commands end with ; or \\g.", INFO_INFO); - snprintf((char*) glob_buffer.ptr(), glob_buffer.alloced_length(), - "Your MySQL connection id is %lu\nServer version: %s\n", - mysql_thread_id(&mysql), server_version_string(&mysql)); + my_snprintf((char*) glob_buffer.ptr(), glob_buffer.alloced_length(), + "Your %s connection id is %lu\nServer version: %s\n", + mysql_get_server_name(&mysql), + mysql_thread_id(&mysql), server_version_string(&mysql)); put_info((char*) glob_buffer.ptr(),INFO_INFO); put_info(ORACLE_WELCOME_COPYRIGHT_NOTICE("2000"), INFO_INFO); @@ -1300,15 +1359,16 @@ sig_handler mysql_end(int sig) /* This function handles sigint calls If query is in process, kill query + If 'source' is executed, abort source command no query in process, terminate like previous behavior */ + sig_handler handle_sigint(int sig) { char kill_buffer[40]; MYSQL *kill_mysql= NULL; /* terminate if no query being executed, or we already tried interrupting */ - /* terminate if no query being executed, or we already tried interrupting */ if (!executing_query || (interrupted_query == 2)) { tee_fprintf(stdout, "Ctrl-C -- exit!\n"); @@ -1316,14 +1376,14 @@ sig_handler handle_sigint(int sig) } kill_mysql= mysql_init(kill_mysql); - if (!mysql_connect_ssl_check(kill_mysql, current_host, current_user, opt_password, - "", opt_mysql_port, opt_mysql_unix_port, 0, - opt_ssl_required)) + if (!mysql_real_connect(kill_mysql,current_host, current_user, opt_password, + "", opt_mysql_port, opt_mysql_unix_port,0)) { tee_fprintf(stdout, "Ctrl-C -- sorry, cannot connect to server to kill query, giving up ...\n"); goto err; } + /* First time try to kill the query, second time the connection */ interrupted_query++; /* mysqld < 5 does not understand KILL QUERY, skip to KILL CONNECTION */ @@ -1334,11 +1394,14 @@ sig_handler handle_sigint(int sig) sprintf(kill_buffer, "KILL %s%lu", (interrupted_query == 1) ? "QUERY " : "", mysql_thread_id(&mysql)); - tee_fprintf(stdout, "Ctrl-C -- sending \"%s\" to server ...\n", kill_buffer); + if (verbose) + tee_fprintf(stdout, "Ctrl-C -- sending \"%s\" to server ...\n", + kill_buffer); mysql_real_query(kill_mysql, kill_buffer, (uint) strlen(kill_buffer)); mysql_close(kill_mysql); - tee_fprintf(stdout, "Ctrl-C -- query aborted.\n"); - + tee_fprintf(stdout, "Ctrl-C -- query killed. Continuing normally.\n"); + if (in_com_source) + aborted= 1; // Abort source command return; err: @@ -1350,7 +1413,6 @@ err: handler called mysql_end(). */ mysql_thread_end(); - return; #else mysql_end(sig); #endif @@ -1363,7 +1425,8 @@ sig_handler window_resize(int sig) struct winsize window_size; if (ioctl(fileno(stdin), TIOCGWINSZ, &window_size) == 0) - terminal_width= window_size.ws_col; + if (window_size.ws_col > 0) + terminal_width= window_size.ws_col; } #endif @@ -1373,6 +1436,10 @@ static struct my_option my_long_options[] = 0, 0, 0, 0, 0}, {"help", 'I', "Synonym for -?", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"abort-source-on-error", OPT_ABORT_SOURCE_ON_ERROR, + "Abort 'source filename' operations in case of errors", + &batch_abort_on_error, &batch_abort_on_error, 0, + GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"auto-rehash", OPT_AUTO_REHASH, "Enable automatic rehashing. One doesn't need to use 'rehash' to get table " "and field completion, but startup and reconnecting may take a longer time. " @@ -1424,16 +1491,12 @@ static struct my_option my_long_options[] = &default_charset, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"delimiter", OPT_DELIMITER, "Delimiter to be used.", &delimiter_str, &delimiter_str, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"enable_cleartext_plugin", OPT_ENABLE_CLEARTEXT_PLUGIN, - "Enable/disable the clear text authentication plugin.", - &opt_enable_cleartext_plugin, &opt_enable_cleartext_plugin, - 0, GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0}, {"execute", 'e', "Execute command and quit. (Disables --force and history file.)", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"vertical", 'E', "Print the output of a query (rows) vertically.", &vertical, &vertical, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"force", 'f', "Continue even if we get an SQL error.", + {"force", 'f', "Continue even if we get an SQL error. Sets abort-source-on-error to 0", &ignore_errors, &ignore_errors, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"named-commands", 'G', @@ -1506,6 +1569,10 @@ static struct my_option my_long_options[] = "built-in default (" STRINGIFY_ARG(MYSQL_PORT) ").", &opt_mysql_port, &opt_mysql_port, 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"progress-reports", OPT_REPORT_PROGRESS, + "Get progress reports for long running commands (like ALTER TABLE)", + &opt_progress_reports, &opt_progress_reports, 0, GET_BOOL, NO_ARG, 1, 0, + 0, 0, 0, 0}, {"prompt", OPT_PROMPT, "Set the mysql prompt to this value.", ¤t_prompt, ¤t_prompt, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, @@ -1591,6 +1658,13 @@ static struct my_option my_long_options[] = "Default authentication client-side plugin to use.", &opt_default_auth, &opt_default_auth, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"binary-mode", 0, + "By default, ASCII '\\0' is disallowed and '\\r\\n' is translated to '\\n'. " + "This switch turns off both features, and also turns off parsing of all client" + "commands except \\C and DELIMITER, in non-interactive mode (for input " + "piped to mysql or loaded using the 'source' command). This is necessary " + "when processing output from mysqlbinlog that may contain blobs.", + &opt_binary_mode, &opt_binary_mode, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} }; @@ -1616,8 +1690,9 @@ static void usage(int version) return; puts(ORACLE_WELCOME_COPYRIGHT_NOTICE("2000")); printf("Usage: %s [OPTIONS] [database]\n", my_progname); - my_print_help(my_long_options); print_defaults("my", load_default_groups); + puts(""); + my_print_help(my_long_options); my_print_variables(my_long_options); } @@ -1628,7 +1703,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), { switch(optid) { case OPT_CHARSETS_DIR: - strmake(mysql_charsets_dir, argument, sizeof(mysql_charsets_dir) - 1); + strmake_buf(mysql_charsets_dir, argument); charsets_dir = mysql_charsets_dir; break; case OPT_DELIMITER: @@ -1641,7 +1716,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), /* Check that delimiter does not contain a backslash */ if (!strstr(argument, "\\")) { - strmake(delimiter, argument, sizeof(delimiter) - 1); + strmake_buf(delimiter, argument); } else { @@ -1655,9 +1730,6 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), case OPT_LOCAL_INFILE: using_opt_local_infile=1; break; - case OPT_ENABLE_CLEARTEXT_PLUGIN: - using_opt_enable_cleartext_plugin= TRUE; - break; case OPT_TEE: if (argument == disabled_my_option) { @@ -1676,7 +1748,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), if (argument && strlen(argument)) { default_pager_set= 1; - strmake(pager, argument, sizeof(pager) - 1); + strmake_buf(pager, argument); strmov(default_pager, pager); } else if (default_pager_set) @@ -1777,13 +1849,18 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), #endif break; #include <sslopt-case.h> + case 'f': + batch_abort_on_error= 0; + break; case 'V': usage(1); - exit(0); + status.exit_status= 0; + mysql_end(-1); case 'I': case '?': usage(0); - exit(0); + status.exit_status= 0; + mysql_end(-1); } return 0; } @@ -1827,6 +1904,7 @@ static int get_options(int argc, char **argv) opt_outfile= 0; opt_reconnect= 0; connect_flag= 0; /* Not in interactive mode */ + opt_progress_reports= 0; } if (argc > 1) @@ -1850,6 +1928,9 @@ static int get_options(int argc, char **argv) if (ignore_spaces) connect_flag|= CLIENT_IGNORE_SPACE; + if (opt_progress_reports) + connect_flag|= CLIENT_PROGRESS; + return(0); } @@ -1865,24 +1946,59 @@ static int read_and_execute(bool interactive) ulong line_number=0; bool ml_comment= 0; COMMANDS *com; + ulong line_length= 0; status.exit_status=1; - - for (;;) + + real_binary_mode= !interactive && opt_binary_mode; + while (!aborted) { if (!interactive) { - line=batch_readline(status.line_buff); /* - Skip UTF8 Byte Order Marker (BOM) 0xEFBBBF. - Editors like "notepad" put this marker in - the very beginning of a text file when - you save the file using "Unicode UTF-8" format. + batch_readline can return 0 on EOF or error. + In that case, we need to double check that we have a valid + line before actually setting line_length to read_length. */ - if (line && !line_number && - (uchar) line[0] == 0xEF && - (uchar) line[1] == 0xBB && - (uchar) line[2] == 0xBF) - line+= 3; + line= batch_readline(status.line_buff, real_binary_mode); + if (line) + { + line_length= status.line_buff->read_length; + + /* + ASCII 0x00 is not allowed appearing in queries if it is not in binary + mode. + */ + if (!real_binary_mode && strlen(line) != line_length) + { + status.exit_status= 1; + String msg; + msg.append("ASCII '\\0' appeared in the statement, but this is not " + "allowed unless option --binary-mode is enabled and mysql is " + "run in non-interactive mode. Set --binary-mode to 1 if ASCII " + "'\\0' is expected. Query: '"); + msg.append(glob_buffer); + msg.append(line); + msg.append("'."); + put_info(msg.c_ptr(), INFO_ERROR); + break; + } + + /* + Skip UTF8 Byte Order Marker (BOM) 0xEFBBBF. + Editors like "notepad" put this marker in + the very beginning of a text file when + you save the file using "Unicode UTF-8" format. + */ + if (!line_number && + (uchar) line[0] == 0xEF && + (uchar) line[1] == 0xBB && + (uchar) line[2] == 0xBF) + { + line+= 3; + // decrease the line length accordingly to the 3 bytes chopped + line_length -=3; + } + } line_number++; if (!glob_buffer.length()) status.query_start_line=line_number; @@ -1940,6 +2056,8 @@ static int read_and_execute(bool interactive) */ if (opt_outfile && line) fprintf(OUTFILE, "%s\n", line); + + line_length= line ? strlen(line) : 0; } // End of file or system error if (!line) @@ -1956,7 +2074,7 @@ static int read_and_execute(bool interactive) (We want to allow help, print and clear anywhere at line start */ if ((named_cmds || glob_buffer.is_empty()) - && !ml_comment && !in_string && (com=find_command(line,0))) + && !ml_comment && !in_string && (com= find_command(line))) { if ((*com->func)(&glob_buffer,line) > 0) break; @@ -1968,7 +2086,7 @@ static int read_and_execute(bool interactive) #endif continue; } - if (add_line(glob_buffer, line, &in_string, &ml_comment, + if (add_line(glob_buffer, line, line_length, &in_string, &ml_comment, status.line_buff ? status.line_buff->truncated : 0)) break; } @@ -1998,71 +2116,132 @@ static int read_and_execute(bool interactive) free(line); #endif + /* + If the function is called by 'source' command, it will return to interactive + mode, so real_binary_mode should be FALSE. Otherwise, it will exit the + program, it is safe to set real_binary_mode to FALSE. + */ + real_binary_mode= FALSE; return status.exit_status; } -static COMMANDS *find_command(char *name,char cmd_char) +/** + It checks if the input is a short form command. It returns the command's + pointer if a command is found, else return NULL. Note that if binary-mode + is set, then only \C is searched for. + + @param cmd_char A character of one byte. + + @return + the command's pointer or NULL. +*/ +static COMMANDS *find_command(char cmd_char) +{ + DBUG_ENTER("find_command"); + DBUG_PRINT("enter", ("cmd_char: %d", cmd_char)); + + int index= -1; + + /* + In binary-mode, we disallow all mysql commands except '\C' + and DELIMITER. + */ + if (real_binary_mode) + { + if (cmd_char == 'C') + index= charset_index; + } + else + index= get_command_index(cmd_char); + + if (index >= 0) + { + DBUG_PRINT("exit",("found command: %s", commands[index].name)); + DBUG_RETURN(&commands[index]); + } + else + DBUG_RETURN((COMMANDS *) 0); +} + +/** + It checks if the input is a long form command. It returns the command's + pointer if a command is found, else return NULL. Note that if binary-mode + is set, then only DELIMITER is searched for. + + @param name A string. + @return + the command's pointer or NULL. +*/ +static COMMANDS *find_command(char *name) { uint len; char *end; DBUG_ENTER("find_command"); - DBUG_PRINT("enter",("name: '%s' char: %d", name ? name : "NULL", cmd_char)); - if (!name) + DBUG_ASSERT(name != NULL); + DBUG_PRINT("enter", ("name: '%s'", name)); + + while (my_isspace(charset_info, *name)) + name++; + /* + If there is an \\g in the row or if the row has a delimiter but + this is not a delimiter command, let add_line() take care of + parsing the row and calling find_command(). + */ + if ((!real_binary_mode && strstr(name, "\\g")) || + (strstr(name, delimiter) && + !is_delimiter_command(name, DELIMITER_NAME_LEN))) + DBUG_RETURN((COMMANDS *) 0); + + if ((end=strcont(name, " \t"))) { - len=0; - end=0; + len=(uint) (end - name); + while (my_isspace(charset_info, *end)) + end++; + if (!*end) + end= 0; // no arguments to function + } + else + len= (uint) strlen(name); + + int index= -1; + if (real_binary_mode) + { + if (is_delimiter_command(name, len)) + index= delimiter_index; } else { - while (my_isspace(charset_info,*name)) - name++; /* - If there is an \\g in the row or if the row has a delimiter but - this is not a delimiter command, let add_line() take care of - parsing the row and calling find_command() + All commands are in the first part of commands array and have a function + to implement it. */ - if (strstr(name, "\\g") || (strstr(name, delimiter) && - !(strlen(name) >= 9 && - !my_strnncoll(&my_charset_latin1, - (uchar*) name, 9, - (const uchar*) "delimiter", - 9)))) - DBUG_RETURN((COMMANDS *) 0); - if ((end=strcont(name," \t"))) + for (uint i= 0; commands[i].func; i++) { - len=(uint) (end - name); - while (my_isspace(charset_info,*end)) - end++; - if (!*end) - end=0; // no arguments to function + if (!my_strnncoll(&my_charset_latin1, (uchar*) name, len, + (uchar*) commands[i].name, len) && + (commands[i].name[len] == '\0') && + (!end || commands[i].takes_params)) + { + index= i; + break; + } } - else - len=(uint) strlen(name); } - for (uint i= 0; commands[i].name; i++) + if (index >= 0) { - if (commands[i].func && - ((name && - !my_strnncoll(&my_charset_latin1, (uchar*)name, len, - (uchar*)commands[i].name,len) && - !commands[i].name[len] && - (!end || (end && commands[i].takes_params))) || - (!name && commands[i].cmd_char == cmd_char))) - { - DBUG_PRINT("exit",("found command: %s", commands[i].name)); - DBUG_RETURN(&commands[i]); - } + DBUG_PRINT("exit", ("found command: %s", commands[index].name)); + DBUG_RETURN(&commands[index]); } DBUG_RETURN((COMMANDS *) 0); } -static bool add_line(String &buffer,char *line,char *in_string, - bool *ml_comment, bool truncated) +static bool add_line(String &buffer, char *line, ulong line_length, + char *in_string, bool *ml_comment, bool truncated) { uchar inchar; char buff[80], *pos, *out; @@ -2077,10 +2256,11 @@ static bool add_line(String &buffer,char *line,char *in_string, if (status.add_to_history && line[0] && not_in_history(line)) add_history(line); #endif - char *end_of_line=line+(uint) strlen(line); + char *end_of_line= line + line_length; - for (pos=out=line ; (inchar= (uchar) *pos) ; pos++) + for (pos= out= line; pos < end_of_line; pos++) { + inchar= (uchar) *pos; if (!preserve_comments) { // Skip spaces at the beginning of a statement @@ -2120,7 +2300,7 @@ static bool add_line(String &buffer,char *line,char *in_string, *out++= (char) inchar; continue; } - if ((com=find_command(NullS,(char) inchar))) + if ((com= find_command((char) inchar))) { // Flush previously accepted characters if (out != line) @@ -2196,7 +2376,7 @@ static bool add_line(String &buffer,char *line,char *in_string, pos--; - if ((com= find_command(buffer.c_ptr(), 0))) + if ((com= find_command(buffer.c_ptr()))) { if ((*com->func)(&buffer, buffer.c_ptr()) > 0) @@ -2209,16 +2389,21 @@ static bool add_line(String &buffer,char *line,char *in_string, } buffer.length(0); } - else if (!*ml_comment && (!*in_string && (inchar == '#' || - (inchar == '-' && pos[1] == '-' && - /* - The third byte is either whitespace or is the - end of the line -- which would occur only - because of the user sending newline -- which is - itself whitespace and should also match. - */ - (my_isspace(charset_info,pos[2]) || - !pos[2]))))) + else if (!*ml_comment && + (!*in_string && + (inchar == '#' || + (inchar == '-' && pos[1] == '-' && + /* + The third byte is either whitespace or is the end of + the line -- which would occur only because of the + user sending newline -- which is itself whitespace + and should also match. + We also ignore lines starting with '--', even if there + isn't a whitespace after. (This makes it easier to run + mysql-test-run cases through the client) + */ + ((my_isspace(charset_info,pos[2]) || !pos[2]) || + (buffer.is_empty() && out == line)))))) { // Flush previously accepted characters if (out != line) @@ -2250,7 +2435,7 @@ static bool add_line(String &buffer,char *line,char *in_string, break; } else if (!*in_string && inchar == '/' && *(pos+1) == '*' && - *(pos+2) != '!') + !(*(pos+2) == '!' || (*(pos+2) == 'M' && *(pos+3) == '!'))) { if (preserve_comments) { @@ -2310,9 +2495,7 @@ static bool add_line(String &buffer,char *line,char *in_string, { uint length=(uint) (out-line); - if (!truncated && (length < 9 || - my_strnncoll (charset_info, (uchar *)line, 9, - (const uchar *) "delimiter", 9) || + if (!truncated && (!is_delimiter_command(line, length) || (*in_string || *ml_comment))) { /* @@ -2732,6 +2915,8 @@ static int reconnect(void) } if (!connected) return put_info("Can't connect to the server\n",INFO_ERROR); + my_free(server_version); + server_version= 0; /* purecov: end */ return 0; } @@ -2919,13 +3104,8 @@ com_help(String *buffer __attribute__((unused)), return com_server_help(buffer,line,help_arg); } - put_info("\nFor information about MySQL products and services, visit:\n" - " http://www.mysql.com/\n" - "For developer information, including the MySQL Reference Manual, " - "visit:\n" - " http://dev.mysql.com/\n" - "To buy MySQL Enterprise support, training, or other products, visit:\n" - " https://shop.mysql.com/\n", INFO_INFO); + put_info("\nGeneral information about MariaDB can be found at\n" + "http://mariadb.org\n", INFO_INFO); put_info("List of all MySQL commands:", INFO_INFO); if (!named_cmds) put_info("Note that all text commands must be first on line and end with ';'",INFO_INFO); @@ -2962,7 +3142,7 @@ com_charset(String *buffer __attribute__((unused)), char *line) { char buff[256], *param; CHARSET_INFO * new_cs; - strmake(buff, line, sizeof(buff) - 1); + strmake_buf(buff, line); param= get_arg(buff, 0); if (!param || !*param) { @@ -3037,6 +3217,7 @@ com_go(String *buffer,char *line __attribute__((unused))) timer=start_timer(); executing_query= 1; error= mysql_real_query_for_lazy(buffer->ptr(),buffer->length()); + report_progress_end(); #ifdef HAVE_READLINE if (status.add_to_history) @@ -3198,7 +3379,7 @@ static void init_tee(const char *file_name) return; } OUTFILE = new_outfile; - strmake(outfile, file_name, FN_REFLEN-1); + strmake_buf(outfile, file_name); tee_fprintf(stdout, "Logging to file '%s'\n", file_name); opt_outfile= 1; return; @@ -3347,6 +3528,7 @@ print_table_data(MYSQL_RES *result) if (length < 4 && !IS_NOT_NULL(field->flags)) length=4; // Room for "NULL" field->max_length=length; + num_flag[mysql_field_tell(result) - 1]= IS_NUM(field->type); separator.fill(separator.length()+length+2,'-'); separator.append('+'); } @@ -3366,7 +3548,6 @@ print_table_data(MYSQL_RES *result) tee_fprintf(PAGER, " %-*s |",(int) min(display_length, MAX_COLUMN_LENGTH), field->name); - num_flag[off]= IS_NUM(field->type); } (void) tee_fputs("\n", PAGER); tee_puts((char*) separator.ptr(), PAGER); @@ -3827,7 +4008,7 @@ com_tee(String *buffer __attribute__((unused)), /* eliminate the spaces before the parameters */ while (my_isspace(charset_info,*param)) param++; - end= strmake(file_name, param, sizeof(file_name) - 1); + end= strmake_buf(file_name, param); /* remove end space from command line */ while (end > file_name && (my_isspace(charset_info,end[-1]) || my_iscntrl(charset_info,end[-1]))) @@ -3888,7 +4069,7 @@ com_pager(String *buffer __attribute__((unused)), } else { - end= strmake(pager_name, param, sizeof(pager_name)-1); + end= strmake_buf(pager_name, param); while (end > pager_name && (my_isspace(charset_info,end[-1]) || my_iscntrl(charset_info,end[-1]))) end--; @@ -3924,8 +4105,9 @@ static int com_edit(String *buffer,char *line __attribute__((unused))) { char filename[FN_REFLEN],buff[160]; - int fd,tmp; + int fd,tmp,error; const char *editor; + MY_STAT stat_arg; if ((fd=create_temp_file(filename,NullS,"sql", O_CREAT | O_WRONLY, MYF(MY_WME))) < 0) @@ -3941,10 +4123,14 @@ com_edit(String *buffer,char *line __attribute__((unused))) !(editor = (char *)getenv("VISUAL"))) editor = "vi"; strxmov(buff,editor," ",filename,NullS); - if(system(buff) == -1) + if ((error= system(buff))) + { + char errmsg[100]; + sprintf(errmsg, "Command '%.40s' failed", buff); + put_info(errmsg, INFO_ERROR, 0, NullS); goto err; + } - MY_STAT stat_arg; if (!my_stat(filename,&stat_arg,MYF(MY_WME))) goto err; if ((fd = my_open(filename,O_RDONLY, MYF(MY_WME))) < 0) @@ -4028,7 +4214,7 @@ static int com_connect(String *buffer, char *line) { char *tmp, buff[256]; - bool save_rehash= opt_rehash; + my_bool save_rehash= opt_rehash; int error; bzero(buff, sizeof(buff)); @@ -4086,6 +4272,7 @@ static int com_source(String *buffer __attribute__((unused)), int error; STATUS old_status; FILE *sql_file; + my_bool save_ignore_errors; /* Skip space from file name */ while (my_isspace(charset_info,*line)) @@ -4095,7 +4282,7 @@ static int com_source(String *buffer __attribute__((unused)), INFO_ERROR, 0); while (my_isspace(charset_info,*param)) param++; - end=strmake(source_name,param,sizeof(source_name)-1); + end=strmake_buf(source_name, param); while (end > source_name && (my_isspace(charset_info,end[-1]) || my_iscntrl(charset_info,end[-1]))) end--; @@ -4117,16 +4304,27 @@ static int com_source(String *buffer __attribute__((unused)), /* Save old status */ old_status=status; + save_ignore_errors= ignore_errors; bfill((char*) &status,sizeof(status),(char) 0); status.batch=old_status.batch; // Run in batch mode status.line_buff=line_buff; status.file_name=source_name; glob_buffer.length(0); // Empty command buffer + ignore_errors= !batch_abort_on_error; + in_com_source= 1; error= read_and_execute(false); + ignore_errors= save_ignore_errors; status=old_status; // Continue as before + in_com_source= aborted= 0; my_fclose(sql_file,MYF(0)); batch_readline_end(line_buff); + /* + If we got an error during source operation, don't abort the client + if ignore_errors is set + */ + if (error && ignore_errors) + error= -1; // Ignore error return error; } @@ -4137,7 +4335,7 @@ com_delimiter(String *buffer __attribute__((unused)), char *line) { char buff[256], *tmp; - strmake(buff, line, sizeof(buff) - 1); + strmake_buf(buff, line); tmp= get_arg(buff, 0); if (!tmp || !*tmp) @@ -4154,7 +4352,7 @@ com_delimiter(String *buffer __attribute__((unused)), char *line) return 0; } } - strmake(delimiter, tmp, sizeof(delimiter) - 1); + strmake_buf(delimiter, tmp); delimiter_length= (int)strlen(delimiter); delimiter_str= delimiter; return 0; @@ -4168,23 +4366,8 @@ com_use(String *buffer __attribute__((unused)), char *line) int select_db; bzero(buff, sizeof(buff)); - - /* - In case number of quotes exceed 2, we try to get - the normalized db name. - */ - if (get_quote_count(line) > 2) - { - if (normalize_dbname(line, buff, sizeof(buff))) - return put_error(&mysql); - tmp= buff; - } - else - { - strmake(buff, line, sizeof(buff) - 1); - tmp= get_arg(buff, 0); - } - + strmake_buf(buff, line); + tmp= get_arg(buff, 0); if (!tmp || !*tmp) { put_info("USE must be followed by a database name", INFO_ERROR); @@ -4250,62 +4433,6 @@ com_use(String *buffer __attribute__((unused)), char *line) return 0; } -/** - Normalize database name. - - @param line [IN] The command. - @param buff [OUT] Normalized db name. - @param buff_size [IN] Buffer size. - - @return Operation status - @retval 0 Success - @retval 1 Failure - - @note Sometimes server normilizes the database names - & APIs like mysql_select_db() expect normalized - database names. Since it is difficult to perform - the name conversion/normalization on the client - side, this function tries to get the normalized - dbname (indirectly) from the server. -*/ - -static int -normalize_dbname(const char *line, char *buff, uint buff_size) -{ - MYSQL_RES *res= NULL; - - /* Send the "USE db" commmand to the server. */ - if (mysql_query(&mysql, line)) - return 1; - - /* - Now, get the normalized database name and store it - into the buff. - */ - if (!mysql_query(&mysql, "SELECT DATABASE()") && - (res= mysql_use_result(&mysql))) - { - MYSQL_ROW row= mysql_fetch_row(res); - if (row && row[0]) - { - size_t len= strlen(row[0]); - /* Make sure there is enough room to store the dbname. */ - if ((len > buff_size) || ! memcpy(buff, row[0], len)) - { - mysql_free_result(res); - return 1; - } - } - mysql_free_result(res); - } - - /* Restore the original database. */ - if (current_db && mysql_select_db(&mysql, current_db)) - return 1; - - return 0; -} - static int com_warnings(String *buffer __attribute__((unused)), char *line __attribute__((unused))) @@ -4385,19 +4512,57 @@ char *get_arg(char *line, my_bool get_next_arg) return valid_arg ? start : NullS; } -/* - Number of quotes present in the command's argument. + +/** + An example of mysql_authentication_dialog_ask callback. + + The C function with the name "mysql_authentication_dialog_ask", if exists, + will be used by the "dialog" client authentication plugin when user + input is needed. This function should be of mysql_authentication_dialog_ask_t + type. If the function does not exists, a built-in implementation will be + used. + + @param mysql mysql + @param type type of the input + 1 - normal string input + 2 - password string + @param prompt prompt + @param buf a buffer to store the use input + @param buf_len the length of the buffer + + @retval a pointer to the user input string. + It may be equal to 'buf' or to 'mysql->password'. + In all other cases it is assumed to be an allocated + string, and the "dialog" plugin will free() it. */ -static int -get_quote_count(const char *line) + +MYSQL_PLUGIN_EXPORT +char *mysql_authentication_dialog_ask(MYSQL *mysql, int type, + const char *prompt, + char *buf, int buf_len) { - int quote_count; - const char *ptr= line; + char *s=buf; + + fputs("[mariadb] ", stdout); + fputs(prompt, stdout); + fputs(" ", stdout); - for(quote_count= 0; ptr ++ && *ptr; ptr= strpbrk(ptr, "\"\'`")) - quote_count ++; + if (type == 2) /* password */ + { + s= get_tty_password(""); + strnmov(buf, s, buf_len); + buf[buf_len-1]= 0; + my_free(s); + } + else + { + if (!fgets(buf, buf_len-1, stdin)) + buf[0]= 0; + else if (buf[0] && (s= strend(buf))[-1] == '\n') + s[-1]= 0; + } - return quote_count; + return buf; } static int @@ -4447,21 +4612,16 @@ sql_real_connect(char *host,char *database,char *user,char *password, } mysql_options(&mysql, MYSQL_SET_CHARSET_NAME, default_charset); - + if (opt_plugin_dir && *opt_plugin_dir) mysql_options(&mysql, MYSQL_PLUGIN_DIR, opt_plugin_dir); if (opt_default_auth && *opt_default_auth) mysql_options(&mysql, MYSQL_DEFAULT_AUTH, opt_default_auth); - if (using_opt_enable_cleartext_plugin) - mysql_options(&mysql, MYSQL_ENABLE_CLEARTEXT_PLUGIN, - (char*) &opt_enable_cleartext_plugin); - - if (!mysql_connect_ssl_check(&mysql, host, user, password, - database, opt_mysql_port, opt_mysql_unix_port, - connect_flag | CLIENT_MULTI_STATEMENTS, - opt_ssl_required)) + if (!mysql_real_connect(&mysql, host, user, password, + database, opt_mysql_port, opt_mysql_unix_port, + connect_flag | CLIENT_MULTI_STATEMENTS)) { if (!silent || (mysql_errno(&mysql) != CR_CONN_HOST_ERROR && @@ -4479,6 +4639,13 @@ sql_real_connect(char *host,char *database,char *user,char *password, connected=1; #ifndef EMBEDDED_LIBRARY mysql.reconnect= debug_info_flag; // We want to know if this happens + + /* + CLIENT_PROGRESS is set only if we requsted it in mysql_real_connect() + and the server also supports it + */ + if (mysql.client_flag & CLIENT_PROGRESS) + mysql_options(&mysql, MYSQL_PROGRESS_CALLBACK, (void*) report_progress); #else mysql.reconnect= 1; #endif @@ -4566,15 +4733,16 @@ com_status(String *buffer __attribute__((unused)), if (skip_updates) { - vidattr(A_BOLD); + my_vidattr(A_BOLD); tee_fprintf(stdout, "\nAll updates ignored to this database\n"); - vidattr(A_NORMAL); + my_vidattr(A_NORMAL); } #ifdef USE_POPEN tee_fprintf(stdout, "Current pager:\t\t%s\n", pager); tee_fprintf(stdout, "Using outfile:\t\t'%s'\n", opt_outfile ? outfile : ""); #endif tee_fprintf(stdout, "Using delimiter:\t%s\n", delimiter); + tee_fprintf(stdout, "Server:\t\t\t%s\n", mysql_get_server_name(&mysql)); tee_fprintf(stdout, "Server version:\t\t%s\n", server_version_string(&mysql)); tee_fprintf(stdout, "Protocol version:\t%d\n", mysql_get_proto_info(&mysql)); tee_fprintf(stdout, "Connection:\t\t%s\n", mysql_get_host_info(&mysql)); @@ -4635,9 +4803,9 @@ com_status(String *buffer __attribute__((unused)), } if (safe_updates) { - vidattr(A_BOLD); + my_vidattr(A_BOLD); tee_fprintf(stdout, "\nNote that you are running in safe_update_mode:\n"); - vidattr(A_NORMAL); + my_vidattr(A_NORMAL); tee_fprintf(stdout, "\ UPDATEs and DELETEs that don't use a key in the WHERE clause are not allowed.\n\ (One can force an UPDATE/DELETE by adding LIMIT # at the end of the command.)\n\ @@ -4730,34 +4898,48 @@ put_info(const char *str,INFO_TYPE info_type, uint error, const char *sqlstate) { if (!inited) { - inited=1; #ifdef HAVE_SETUPTERM - (void) setupterm((char *)0, 1, (int *) 0); + int errret; + have_curses= setupterm((char *)0, 1, &errret) != ERR; #endif + inited=1; } if (info_type == INFO_ERROR) { if (!opt_nobeep) + { +#ifdef _WIN32 + MessageBeep(MB_ICONWARNING); +#else putchar('\a'); /* This should make a bell */ - vidattr(A_STANDOUT); +#endif + } + my_vidattr(A_STANDOUT); if (error) { if (sqlstate) - (void) tee_fprintf(file, "ERROR %d (%s): ", error, sqlstate); + (void) tee_fprintf(file, "ERROR %d (%s)", error, sqlstate); else - (void) tee_fprintf(file, "ERROR %d: ", error); + (void) tee_fprintf(file, "ERROR %d", error); } else - tee_puts("ERROR: ", file); + tee_fputs("ERROR", file); + if (status.query_start_line && line_numbers) + { + (void) fprintf(file," at line %lu",status.query_start_line); + if (status.file_name) + (void) fprintf(file," in file: '%s'", status.file_name); + } + tee_fputs(": ", file); } else - vidattr(A_BOLD); + my_vidattr(A_BOLD); (void) tee_puts(str, file); - vidattr(A_NORMAL); + my_vidattr(A_NORMAL); } if (unbuffered) fflush(file); - return info_type == INFO_ERROR ? -1 : 0; + return info_type == INFO_ERROR ? (ignore_errors ? -1 : 1): 0; } @@ -4894,7 +5076,7 @@ static void mysql_end_timer(ulong start_time,char *buff) strmov(strend(buff),")"); } -static const char* construct_prompt() +static const char *construct_prompt() { processed_prompt.free(); // Erase the old prompt time_t lclock = time(NULL); // Get the date struct @@ -4923,6 +5105,12 @@ static const char* construct_prompt() case 'd': processed_prompt.append(current_db ? current_db : "(none)"); break; + case 'N': + if (connected) + processed_prompt.append(mysql_get_server_name(&mysql)); + else + processed_prompt.append("unknown"); + break; case 'h': { const char *prompt; @@ -5096,3 +5284,32 @@ static int com_prompt(String *buffer __attribute__((unused)), tee_fprintf(stdout, "PROMPT set to '%s'\n", current_prompt); return 0; } + +#ifndef EMBEDDED_LIBRARY +static void report_progress(const MYSQL *mysql, uint stage, uint max_stage, + double progress, const char *proc_info, + uint proc_info_length) +{ + uint length= printf("Stage: %d of %d '%.*s' %6.3g%% of stage done", + stage, max_stage, proc_info_length, proc_info, + progress); + if (length < last_progress_report_length) + printf("%*s", last_progress_report_length - length, ""); + putc('\r', stdout); + fflush(stdout); + last_progress_report_length= length; +} + +static void report_progress_end() +{ + if (last_progress_report_length) + { + printf("%*s\r", last_progress_report_length, ""); + last_progress_report_length= 0; + } +} +#else +static void report_progress_end() +{ +} +#endif diff --git a/client/mysql_plugin.c b/client/mysql_plugin.c index 293bd6727cf..bc9969c9b66 100644 --- a/client/mysql_plugin.c +++ b/client/mysql_plugin.c @@ -100,7 +100,8 @@ int main(int argc,char *argv[]) char operation[16]; MY_INIT(argv[0]); - plugin_data.name= 0; // initialize name + sf_leaking_memory=1; /* don't report memory leaks on early exits */ + plugin_data.name= 0; /* initialize name */ /* The following operations comprise the method for enabling or disabling @@ -654,11 +655,11 @@ static int load_plugin_data(char *plugin_name, char *config_file) } break; } - if ((line[0] == '#') || (line[0] == '\n')) // skip comment and blank lines + if ((line[0] == '#') || (line[0] == '\n')) /* skip comment and blank lines */ { continue; } - if (i == -1) // if first pass, read this line as so_name + if (i == -1) /* if first pass, read this line as so_name */ { /* Add proper file extension for soname */ strcat(line, FN_SOEXT); @@ -706,10 +707,10 @@ error: static int check_options(int argc, char **argv, char *operation) { - int i= 0; // loop counter - int num_found= 0; // number of options found (shortcut loop) - char config_file[FN_REFLEN]; // configuration file name - char plugin_name[FN_REFLEN]; // plugin name + int i= 0; /* loop counter */ + int num_found= 0; /* number of options found (shortcut loop) */ + char config_file[FN_REFLEN]; /* configuration file name */ + char plugin_name[FN_REFLEN]; /* plugin name */ /* Form prefix strings for the options. */ const char *basedir_prefix = "--basedir="; diff --git a/client/mysql_upgrade.c b/client/mysql_upgrade.c index 507df6f7843..eaa10245a94 100644 --- a/client/mysql_upgrade.c +++ b/client/mysql_upgrade.c @@ -1,5 +1,6 @@ /* - Copyright (c) 2006, 2016, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2006, 2013, Oracle and/or its affiliates. + Copyright (c) 2010, 2016, MariaDB 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 @@ -21,7 +22,7 @@ #include <welcome_copyright_notice.h> /* ORACLE_WELCOME_COPYRIGHT_NOTICE */ -#define VER "1.1" +#define VER "1.3a" #ifdef HAVE_SYS_WAIT_H #include <sys/wait.h> @@ -35,11 +36,14 @@ # endif #endif +static int phase = 1; +static int phases_total = 4; static char mysql_path[FN_REFLEN]; static char mysqlcheck_path[FN_REFLEN]; static my_bool opt_force, opt_verbose, debug_info_flag, debug_check_flag, opt_systables_only, opt_version_check; +static my_bool opt_not_used, opt_silent; static uint my_end_arg= 0; static char *opt_user= (char*)"root"; @@ -49,6 +53,8 @@ static DYNAMIC_STRING conn_args; static char *opt_password= 0; static char *opt_plugin_dir= 0, *opt_default_auth= 0; +static char *cnf_file_path= 0, defaults_file[FN_REFLEN + 32]; + static my_bool tty_password= 0; static char opt_tmpdir[FN_REFLEN] = ""; @@ -63,16 +69,20 @@ static my_bool not_used; /* Can't use GET_BOOL without a value pointer */ static my_bool opt_write_binlog; +#define OPT_SILENT OPT_MAX_CLIENT_OPTION + static struct my_option my_long_options[]= { {"help", '?', "Display this help message and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"basedir", 'b', "Not used by mysql_upgrade. Only for backward compatibility.", + {"basedir", 'b', + "Not used by mysql_upgrade. Only for backward compatibility.", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"character-sets-dir", OPT_CHARSETS_DIR, - "Directory for character set files.", 0, - 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"compress", OPT_COMPRESS, "Use compression in server/client protocol.", + "Not used by mysql_upgrade. Only for backward compatibility.", + 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, + {"compress", OPT_COMPRESS, + "Not used by mysql_upgrade. Only for backward compatibility.", ¬_used, ¬_used, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"datadir", 'd', "Not used by mysql_upgrade. Only for backward compatibility.", @@ -85,23 +95,23 @@ static struct my_option my_long_options[]= &default_dbug_option, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, #endif {"debug-check", OPT_DEBUG_CHECK, "Check memory and open file usage at exit.", - &debug_check_flag, &debug_check_flag, 0, - GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + &debug_check_flag, &debug_check_flag, + 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"debug-info", 'T', "Print some debug info at exit.", &debug_info_flag, &debug_info_flag, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"default-character-set", OPT_DEFAULT_CHARSET, - "Set the default character set.", 0, - 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + "Not used by mysql_upgrade. Only for backward compatibility.", + 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"default_auth", OPT_DEFAULT_AUTH, "Default authentication client-side plugin to use.", &opt_default_auth, &opt_default_auth, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"force", 'f', "Force execution of mysqlcheck even if mysql_upgrade " "has already been executed for the current version of MySQL.", - &opt_force, &opt_force, 0, - GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"host",'h', "Connect to host.", 0, + &opt_force, &opt_force, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"host", 'h', "Connect to host.", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, +#define PASSWORD_OPT 12 {"password", 'p', "Password to use when connecting to server. If password is not given," " it's solicited on the tty.", &opt_password,&opt_password, @@ -128,12 +138,8 @@ static struct my_option my_long_options[]= "Base name of shared memory.", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, #endif - {"version-check", 'k', "Run this program only if its \'server version\' " - "matches the version of the server to which it's connecting, (enabled by " - "default); use --skip-version-check to avoid this check. Note: the \'server " - "version\' of the program is the version of the MySQL server with which it " - "was built/distributed.", &opt_version_check, &opt_version_check, 0, - GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, + {"silent", OPT_SILENT, "Print less information", &opt_silent, + &opt_silent, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"socket", 'S', "The socket file to use for connection.", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, #include <sslopt-longopts.h> @@ -143,27 +149,46 @@ static struct my_option my_long_options[]= "do not try to upgrade the data.", &opt_systables_only, &opt_systables_only, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, +#define USER_OPT (array_elements(my_long_options) - 6) {"user", 'u', "User for login if not current user.", &opt_user, &opt_user, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"verbose", 'v', "Display more output about the process.", - &opt_verbose, &opt_verbose, 0, + &opt_not_used, &opt_not_used, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, + {"version", 'V', "Output version information and exit.", 0, 0, 0, + GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"version-check", 'k', "Run this program only if its \'server version\' " + "matches the version of the server to which it's connecting, (enabled by " + "default); use --skip-version-check to avoid this check. Note: the \'server " + "version\' of the program is the version of the MySQL server with which it " + "was built/distributed.", &opt_version_check, &opt_version_check, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, - {"write-binlog", OPT_WRITE_BINLOG, - "All commands including mysqlcheck are binlogged. Enabled by default;" - "use --skip-write-binlog when commands should not be sent to replication slaves.", + {"write-binlog", OPT_WRITE_BINLOG, "All commands including those, " + "issued by mysqlcheck, are written to the binary log.", &opt_write_binlog, &opt_write_binlog, 0, GET_BOOL, NO_ARG, - 1, 0, 0, 0, 0, 0}, + 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} }; +static const char *load_default_groups[]= +{ + "client", /* Read settings how to connect to server */ + "mysql_upgrade", /* Read special settings for mysql_upgrade */ + "client-server", /* Reads settings common between client & server */ + "client-mariadb", /* Read mariadb unique client settings */ + 0 +}; + static void free_used_memory(void) { /* Free memory allocated by 'load_defaults' */ - free_defaults(defaults_argv); + if (defaults_argv) + free_defaults(defaults_argv); dynstr_free(&ds_args); dynstr_free(&conn_args); + if (cnf_file_path) + my_delete(cnf_file_path, MYF(MY_WME)); } @@ -173,6 +198,7 @@ static void die(const char *fmt, ...) DBUG_ENTER("die"); /* Print the error message */ + fflush(stdout); va_start(args, fmt); if (fmt) { @@ -193,7 +219,7 @@ static void verbose(const char *fmt, ...) { va_list args; - if (!opt_verbose) + if (opt_silent) return; /* Print the verbose message */ @@ -214,31 +240,32 @@ static void verbose(const char *fmt, ...) this way we pass the same arguments on to mysql and mysql_check */ -static void add_one_option(DYNAMIC_STRING* ds, - const struct my_option *opt, - const char* argument) - +static void add_one_option_cmd_line(DYNAMIC_STRING *ds, + const struct my_option *opt, + const char* arg) { - const char* eq= NullS; - const char* arg= NullS; - if (opt->arg_type != NO_ARG) + dynstr_append(ds, "--"); + dynstr_append(ds, opt->name); + if (arg) { - eq= "="; - switch (opt->var_type & GET_TYPE_MASK) { - case GET_STR: - arg= argument; - break; - case GET_BOOL: - arg= (*(my_bool *)opt->value) ? "1" : "0"; - break; - default: - die("internal error at %s: %d",__FILE__, __LINE__); - } + dynstr_append(ds, "="); + dynstr_append_os_quoted(ds, arg, NullS); } - dynstr_append_os_quoted(ds, "--", opt->name, eq, arg, NullS); dynstr_append(ds, " "); } +static void add_one_option_cnf_file(DYNAMIC_STRING *ds, + const struct my_option *opt, + const char* arg) +{ + dynstr_append(ds, opt->name); + if (arg) + { + dynstr_append(ds, "="); + dynstr_append_os_quoted(ds, arg, NullS); + } + dynstr_append(ds, "\n"); +} static my_bool get_one_option(int optid, const struct my_option *opt, @@ -252,9 +279,12 @@ get_one_option(int optid, const struct my_option *opt, printf("%s Ver %s Distrib %s, for %s (%s)\n", my_progname, VER, MYSQL_SERVER_VERSION, SYSTEM_TYPE, MACHINE_TYPE); puts(ORACLE_WELCOME_COPYRIGHT_NOTICE("2000")); - puts("MySQL utility for upgrading databases to new MySQL versions.\n"); + puts("MariaDB utility for upgrading databases to new MariaDB versions."); + print_defaults("my", load_default_groups); + puts(""); my_print_help(my_long_options); - exit(0); + my_print_variables(my_long_options); + die(0); break; case '#': @@ -266,16 +296,17 @@ get_one_option(int optid, const struct my_option *opt, case 'p': if (argument == disabled_my_option) argument= (char*) ""; /* Don't require password */ - tty_password= 1; add_option= FALSE; if (argument) { /* Add password to ds_args before overwriting the arg with x's */ - add_one_option(&ds_args, opt, argument); + add_one_option_cnf_file(&ds_args, opt, argument); while (*argument) *argument++= 'x'; /* Destroy argument */ tty_password= 0; } + else + tty_password= 1; break; case 't': @@ -291,6 +322,23 @@ get_one_option(int optid, const struct my_option *opt, case 'k': /* --version-check */ case 'v': /* --verbose */ + opt_verbose++; + if (argument == disabled_my_option) + { + opt_verbose= 0; + opt_silent= 1; + } + add_option= 0; + break; + case 'V': + printf("%s Ver %s Distrib %s, for %s (%s)\n", + my_progname, VER, MYSQL_SERVER_VERSION, SYSTEM_TYPE, MACHINE_TYPE); + die(0); + break; + case OPT_SILENT: + opt_verbose= 0; + add_option= 0; + break; case 'f': /* --force */ case 's': /* --upgrade-system-tables */ case OPT_WRITE_BINLOG: /* --write-binlog */ @@ -305,19 +353,18 @@ get_one_option(int optid, const struct my_option *opt, case OPT_SHARED_MEMORY_BASE_NAME: /* --shared-memory-base-name */ case OPT_PLUGIN_DIR: /* --plugin-dir */ case OPT_DEFAULT_AUTH: /* --default-auth */ - add_one_option(&conn_args, opt, argument); + add_one_option_cmd_line(&conn_args, opt, argument); break; -#include <sslopt-case.h> } if (add_option) { /* - This is an option that is accpted by mysql_upgrade just so + This is an option that is accepted by mysql_upgrade just so it can be passed on to "mysql" and "mysqlcheck" Save it in the ds_args string */ - add_one_option(&ds_args, opt, argument); + add_one_option_cnf_file(&ds_args, opt, argument); } return 0; } @@ -377,20 +424,13 @@ static int run_tool(char *tool_path, DYNAMIC_STRING *ds_res, ...) while ((arg= va_arg(args, char *))) { - /* Options should be os quoted */ - if (strncmp(arg, "--", 2) == 0) - dynstr_append_os_quoted(&ds_cmdline, arg, NullS); - else - dynstr_append(&ds_cmdline, arg); + /* Options should already be os quoted */ + dynstr_append(&ds_cmdline, arg); dynstr_append(&ds_cmdline, " "); } va_end(args); - /* If given --ssl-mode=REQUIRED propagate it to the tool. */ - if (opt_ssl_required) - dynstr_append(&ds_cmdline, "--ssl-mode=REQUIRED"); - #ifdef __WIN__ dynstr_append(&ds_cmdline, "\""); #endif @@ -460,7 +500,8 @@ static void find_tool(char *tool_executable_name, const char *tool_name, len, self_name, FN_LIBCHAR, tool_name); } - verbose("Looking for '%s' as: %s", tool_name, tool_executable_name); + if (opt_verbose) + verbose("Looking for '%s' as: %s", tool_name, tool_executable_name); /* Make sure it can be executed @@ -526,12 +567,11 @@ static int run_query(const char *query, DYNAMIC_STRING *ds_res, ret= run_tool(mysql_path, ds_res, - "--no-defaults", - ds_args.str, + defaults_file, "--database=mysql", "--batch", /* Turns off pager etc. */ force ? "--force": "--skip-force", - ds_res ? "--silent": "", + ds_res || opt_silent ? "--silent": "", "<", query_file_path, "2>&1", @@ -617,7 +657,6 @@ static int upgrade_already_done(void) FILE *in; char upgrade_info_file[FN_REFLEN]= {0}; char buf[sizeof(MYSQL_SERVER_VERSION)+1]; - char *res; if (get_upgrade_info_file_name(upgrade_info_file)) return 0; /* Could not get filename => not sure */ @@ -625,19 +664,15 @@ static int upgrade_already_done(void) if (!(in= my_fopen(upgrade_info_file, O_RDONLY, MYF(0)))) return 0; /* Could not open file => not sure */ - /* - Read from file, don't care if it fails since it - will be detected by the strncmp - */ bzero(buf, sizeof(buf)); - res= fgets(buf, sizeof(buf), in); + if (!fgets(buf, sizeof(buf), in)) + { + /* Ignore, will be detected by strncmp() below */ + } my_fclose(in, MYF(0)); - if (!res) - return 0; /* Could not read from file => not sure */ - - return (strncmp(res, MYSQL_SERVER_VERSION, + return (strncmp(buf, MYSQL_SERVER_VERSION, sizeof(MYSQL_SERVER_VERSION)-1)==0); } @@ -693,6 +728,8 @@ static void create_mysql_upgrade_info_file(void) static void print_conn_args(const char *tool_name) { + if (opt_verbose < 2) + return; if (conn_args.str[0]) verbose("Running '%s' with connection arguments: %s", tool_name, conn_args.str); @@ -708,30 +745,102 @@ static void print_conn_args(const char *tool_name) static int run_mysqlcheck_upgrade(void) { + int retch; + if (opt_systables_only) + { + verbose("Phase %d/%d: Checking and upgrading tables... Skipped", + phase++, phases_total); + return 0; + } + verbose("Phase %d/%d: Checking and upgrading tables", phase++, phases_total); print_conn_args("mysqlcheck"); - return run_tool(mysqlcheck_path, + retch= run_tool(mysqlcheck_path, NULL, /* Send output from mysqlcheck directly to screen */ - "--no-defaults", - ds_args.str, + defaults_file, "--check-upgrade", "--all-databases", "--auto-repair", + !opt_silent || opt_verbose ? "--verbose": "", + opt_silent ? "--silent": "", opt_write_binlog ? "--write-binlog" : "--skip-write-binlog", + "2>&1", NULL); + return retch; } +#define EVENTS_STRUCT_LEN 7000 + +static my_bool is_mysql() +{ + my_bool ret= TRUE; + DYNAMIC_STRING ds_events_struct; + + if (init_dynamic_string(&ds_events_struct, NULL, + EVENTS_STRUCT_LEN, EVENTS_STRUCT_LEN)) + die("Out of memory"); + + if (run_query("show create table mysql.event", + &ds_events_struct, FALSE) || + strstr(ds_events_struct.str, "IGNORE_BAD_TABLE_OPTIONS") != NULL) + ret= FALSE; + else + verbose("MySQL upgrade detected"); + + dynstr_free(&ds_events_struct); + return(ret); +} + +static int run_mysqlcheck_views(void) +{ + const char *upgrade_views="--process-views=YES"; + if (is_mysql()) + { + upgrade_views="--process-views=UPGRADE_FROM_MYSQL"; + verbose("Phase %d/%d: Fixing views from mysql", phase++, phases_total); + } + else if (opt_systables_only) + { + verbose("Phase %d/%d: Fixing views... Skipped", phase++, phases_total); + return 0; + } + else + verbose("Phase %d/%d: Fixing views", phase++, phases_total); + + print_conn_args("mysqlcheck"); + return run_tool(mysqlcheck_path, + NULL, /* Send output from mysqlcheck directly to screen */ + defaults_file, + "--all-databases", "--repair", + upgrade_views, + "--skip-process-tables", + opt_verbose ? "--verbose": "", + opt_silent ? "--silent": "", + opt_write_binlog ? "--write-binlog" : "--skip-write-binlog", + "2>&1", + NULL); +} static int run_mysqlcheck_fixnames(void) { + if (opt_systables_only) + { + verbose("Phase %d/%d: Fixing table and database names ... Skipped", + phase++, phases_total); + return 0; + } + verbose("Phase %d/%d: Fixing table and database names", + phase++, phases_total); print_conn_args("mysqlcheck"); return run_tool(mysqlcheck_path, NULL, /* Send output from mysqlcheck directly to screen */ - "--no-defaults", - ds_args.str, + defaults_file, "--all-databases", "--fix-db-names", "--fix-table-names", + opt_verbose ? "--verbose": "", + opt_silent ? "--silent": "", opt_write_binlog ? "--write-binlog" : "--skip-write-binlog", + "2>&1", NULL); } @@ -800,7 +909,8 @@ static int run_sql_fix_privilege_tables(void) if (init_dynamic_string(&ds_result, "", 512, 512)) die("Out of memory"); - verbose("Running 'mysql_fix_privilege_tables'..."); + verbose("Phase %d/%d: Running 'mysql_fix_privilege_tables'", + phase++, phases_total); run_query(mysql_fix_privilege_tables, &ds_result, /* Collect result */ TRUE); @@ -829,16 +939,15 @@ static int run_sql_fix_privilege_tables(void) } dynstr_free(&ds_result); - return found_real_errors; + DBUG_RETURN(found_real_errors); } -static const char *load_default_groups[]= +static void print_error(const char *error_msg, DYNAMIC_STRING *output) { - "client", /* Read settings how to connect to server */ - "mysql_upgrade", /* Read special settings for mysql_upgrade*/ - 0 -}; + fprintf(stderr, "%s\n", error_msg); + fprintf(stderr, "%s", output->str); +} /* Convert the specified version string into the numeric format. */ @@ -867,17 +976,12 @@ static int check_version_match(void) if (init_dynamic_string(&ds_version, NULL, NAME_CHAR_LEN, NAME_CHAR_LEN)) die("Out of memory"); - - if (run_query("show variables like 'version'", &ds_version, FALSE)) - { - fprintf(stderr, "Error: Failed while fetching Server version! Could be" - " due to unauthorized access.\n"); - dynstr_free(&ds_version); - return 1; /* Query failed */ - } - if (extract_variable_from_show(&ds_version, version_str)) + if (run_query("show variables like 'version'", + &ds_version, FALSE) || + extract_variable_from_show(&ds_version, version_str)) { - fprintf(stderr, "Error: Failed while extracting Server version!\n"); + print_error("Version check failed. Got the following error when calling " + "the 'mysql' command line client", &ds_version); dynstr_free(&ds_version); return 1; /* Query failed */ } @@ -929,25 +1033,30 @@ int main(int argc, char **argv) { opt_password= get_tty_password(NullS); /* add password to defaults file */ - dynstr_append_os_quoted(&ds_args, "--password=", opt_password, NullS); - dynstr_append(&ds_args, " "); + add_one_option_cnf_file(&ds_args, &my_long_options[PASSWORD_OPT], opt_password); + DBUG_ASSERT(strcmp(my_long_options[PASSWORD_OPT].name, "password") == 0); } /* add user to defaults file */ - dynstr_append_os_quoted(&ds_args, "--user=", opt_user, NullS); - dynstr_append(&ds_args, " "); + add_one_option_cnf_file(&ds_args, &my_long_options[USER_OPT], opt_user); + DBUG_ASSERT(strcmp(my_long_options[USER_OPT].name, "user") == 0); + + cnf_file_path= strmov(defaults_file, "--defaults-file="); + { + int fd= create_temp_file(cnf_file_path, opt_tmpdir[0] ? opt_tmpdir : NULL, + "mysql_upgrade-", O_CREAT | O_WRONLY, MYF(MY_FAE)); + my_write(fd, USTRING_WITH_LEN( "[client]\n"), MYF(MY_FAE)); + my_write(fd, (uchar*)ds_args.str, ds_args.length, MYF(MY_FAE)); + my_close(fd, MYF(0)); + } /* Find mysql */ find_tool(mysql_path, IF_WIN("mysql.exe", "mysql"), self_name); - if (!opt_systables_only) - { - /* Find mysqlcheck */ - find_tool(mysqlcheck_path, IF_WIN("mysqlcheck.exe", "mysqlcheck"), self_name); - } - else - { - printf("The --upgrade-system-tables option was used, databases won't be touched.\n"); - } + /* Find mysqlcheck */ + find_tool(mysqlcheck_path, IF_WIN("mysqlcheck.exe", "mysqlcheck"), self_name); + + if (opt_systables_only && !opt_silent) + printf("The --upgrade-system-tables option was used, user tables won't be touched.\n"); /* Read the mysql_upgrade_info file to check if mysql_upgrade @@ -958,7 +1067,7 @@ int main(int argc, char **argv) printf("This installation of MySQL is already upgraded to %s, " "use --force if you still need to run mysql_upgrade\n", MYSQL_SERVER_VERSION); - die(NULL); + goto end; } if (opt_version_check && check_version_match()) @@ -967,26 +1076,21 @@ int main(int argc, char **argv) /* Run "mysqlcheck" and "mysql_fix_privilege_tables.sql" */ - if (!opt_systables_only) + if (run_mysqlcheck_views() || run_mysqlcheck_fixnames() || + run_mysqlcheck_upgrade() || run_sql_fix_privilege_tables()) { - if (run_mysqlcheck_fixnames()) - die("Error during call to mysql_check for fixing the db/tables names."); - - if (run_mysqlcheck_upgrade()) - die("Error during call to mysql_check for upgrading the tables names."); - } - - if (run_sql_fix_privilege_tables()) - { - /* Specific error msg (if present) would be printed in the function call - * above */ - die("Upgrade failed"); + /* + The upgrade failed to complete in some way or another, + significant error message should have been printed to the screen + */ + die("Upgrade failed" ); } verbose("OK"); /* Create a file indicating upgrade has been performed */ create_mysql_upgrade_info_file(); +end: free_used_memory(); my_end(my_end_arg); exit(0); diff --git a/client/mysqladmin.cc b/client/mysqladmin.cc index f0ae2c12137..e7c6410978d 100644 --- a/client/mysqladmin.cc +++ b/client/mysqladmin.cc @@ -1,5 +1,6 @@ /* - Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2000, 2014, Oracle and/or its affiliates. + Copyright (c) 2010, 2016, Monty Program Ab. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -24,7 +25,7 @@ #include <sql_common.h> #include <welcome_copyright_notice.h> /* ORACLE_WELCOME_COPYRIGHT_NOTICE */ -#define ADMIN_VERSION "8.42" +#define ADMIN_VERSION "9.0" #define MAX_MYSQL_VAR 512 #define SHUTDOWN_DEF_TIMEOUT 3600 /* Wait for shutdown */ #define MAX_TRUNC_LENGTH 3 @@ -44,8 +45,6 @@ static uint opt_count_iterations= 0, my_end_arg; static ulong opt_connect_timeout, opt_shutdown_timeout; static char * unix_port=0; static char *opt_plugin_dir= 0, *opt_default_auth= 0; -static uint opt_enable_cleartext_plugin= 0; -static my_bool using_opt_enable_cleartext_plugin= 0; #ifdef HAVE_SMEM static char *shared_memory_base_name=0; @@ -101,7 +100,10 @@ enum commands { ADMIN_FLUSH_HOSTS, ADMIN_FLUSH_TABLES, ADMIN_PASSWORD, ADMIN_PING, ADMIN_EXTENDED_STATUS, ADMIN_FLUSH_STATUS, ADMIN_FLUSH_PRIVILEGES, ADMIN_START_SLAVE, ADMIN_STOP_SLAVE, - ADMIN_FLUSH_THREADS, ADMIN_OLD_PASSWORD + ADMIN_FLUSH_THREADS, ADMIN_OLD_PASSWORD, ADMIN_FLUSH_SLOW_LOG, + ADMIN_FLUSH_TABLE_STATISTICS, ADMIN_FLUSH_INDEX_STATISTICS, + ADMIN_FLUSH_USER_STATISTICS, ADMIN_FLUSH_CLIENT_STATISTICS, + ADMIN_FLUSH_ALL_STATUS, ADMIN_FLUSH_ALL_STATISTICS }; static const char *command_names[]= { "create", "drop", "shutdown", @@ -111,7 +113,10 @@ static const char *command_names[]= { "flush-hosts", "flush-tables", "password", "ping", "extended-status", "flush-status", "flush-privileges", "start-slave", "stop-slave", - "flush-threads","old-password", + "flush-threads", "old-password", "flush-slow-log", + "flush-table-statistics", "flush-index-statistics", + "flush-user-statistics", "flush-client-statistics", + "flush-all-status", "flush-all-statistics", NullS }; @@ -216,22 +221,17 @@ static struct my_option my_long_options[] = "Default authentication client-side plugin to use.", &opt_default_auth, &opt_default_auth, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"enable_cleartext_plugin", OPT_ENABLE_CLEARTEXT_PLUGIN, - "Enable/disable the clear text authentication plugin.", - &opt_enable_cleartext_plugin, &opt_enable_cleartext_plugin, - 0, GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} }; -static const char *load_default_groups[]= { "mysqladmin","client",0 }; +static const char *load_default_groups[]= +{ "mysqladmin", "client", "client-server", "client-mariadb", 0 }; my_bool get_one_option(int optid, const struct my_option *opt __attribute__((unused)), char *argument) { - int error = 0; - switch(optid) { case 'c': opt_count_iterations= 1; @@ -279,8 +279,8 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), break; case '?': case 'I': /* Info */ - error++; - break; + usage(); + exit(0); case OPT_CHARSETS_DIR: #if MYSQL_VERSION_ID > 32300 charsets_dir = argument; @@ -290,14 +290,6 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), opt_protocol= find_type_or_exit(argument, &sql_protocol_typelib, opt->name); break; - case OPT_ENABLE_CLEARTEXT_PLUGIN: - using_opt_enable_cleartext_plugin= TRUE; - break; - } - if (error) - { - usage(); - exit(1); } return 0; } @@ -305,20 +297,21 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), int main(int argc,char *argv[]) { - int error= 0, ho_error, temp_argc; + int error= 0, temp_argc; MYSQL mysql; char **commands, **save_argv, **temp_argv; MY_INIT(argv[0]); mysql_init(&mysql); - if (load_defaults("my",load_default_groups,&argc,&argv)) - exit(1); + sf_leaking_memory=1; /* don't report memory leaks on early exits */ + if ((error= load_defaults("my",load_default_groups,&argc,&argv))) + goto err1; save_argv = argv; /* Save for free_defaults */ - if ((ho_error=handle_options(&argc, &argv, my_long_options, get_one_option))) - { - free_defaults(save_argv); - exit(ho_error); - } + + if ((error=handle_options(&argc, &argv, my_long_options, get_one_option))) + goto err2; + temp_argv= mask_password(argc, &argv); + temp_argc= argc; if (debug_info_flag) my_end_arg= MY_CHECK_ERROR | MY_GIVE_INFO; @@ -330,10 +323,6 @@ int main(int argc,char *argv[]) usage(); exit(1); } - - temp_argv= mask_password(argc, &argv); - temp_argc= argc; - commands = temp_argv; if (tty_password) opt_password = get_tty_password(NullS); @@ -341,6 +330,8 @@ int main(int argc,char *argv[]) (void) signal(SIGINT,endprog); /* Here if abort */ (void) signal(SIGTERM,endprog); /* Here if abort */ + sf_leaking_memory=0; /* from now on we cleanup properly */ + if (opt_compress) mysql_options(&mysql,MYSQL_OPT_COMPRESS,NullS); if (opt_connect_timeout) @@ -370,10 +361,6 @@ int main(int argc,char *argv[]) if (opt_default_auth && *opt_default_auth) mysql_options(&mysql, MYSQL_DEFAULT_AUTH, opt_default_auth); - if (using_opt_enable_cleartext_plugin) - mysql_options(&mysql, MYSQL_ENABLE_CLEARTEXT_PLUGIN, - (char*) &opt_enable_cleartext_plugin); - if (sql_connect(&mysql, option_wait)) { /* @@ -476,12 +463,6 @@ int main(int argc,char *argv[]) } /* got connection */ mysql_close(&mysql); - my_free(opt_password); - my_free(user); -#ifdef HAVE_SMEM - my_free(shared_memory_base_name); -#endif - free_defaults(save_argv); temp_argc--; while(temp_argc >= 0) { @@ -489,8 +470,17 @@ int main(int argc,char *argv[]) temp_argc--; } my_free(temp_argv); +err2: + mysql_library_end(); + my_free(opt_password); + my_free(user); +#ifdef HAVE_SMEM + my_free(shared_memory_base_name); +#endif + free_defaults(save_argv); +err1: my_end(my_end_arg); - exit(error ? 1 : 0); + exit(error); return 0; } @@ -518,9 +508,8 @@ static my_bool sql_connect(MYSQL *mysql, uint wait) for (;;) { - if (mysql_connect_ssl_check(mysql, host, user, opt_password, NullS, - tcp_port, unix_port, - CLIENT_REMEMBER_OPTIONS, opt_ssl_required)) + if (mysql_real_connect(mysql,host,user,opt_password,NullS,tcp_port, + unix_port, CLIENT_REMEMBER_OPTIONS)) { mysql->reconnect= 1; if (info) @@ -617,11 +606,12 @@ static int execute_commands(MYSQL *mysql,int argc, char **argv) If this behaviour is ever changed, Docs should be notified. */ - struct rand_struct rand_st; + struct my_rnd_struct rand_st; for (; argc > 0 ; argv++,argc--) { - switch (find_type(argv[0],&command_typelib, FIND_TYPE_BASIC)) { + int command; + switch ((command= find_type(argv[0],&command_typelib,FIND_TYPE_BASIC))) { case ADMIN_CREATE: { char buff[FN_REFLEN+20]; @@ -698,7 +688,10 @@ static int execute_commands(MYSQL *mysql,int argc, char **argv) if (mysql_refresh(mysql, (uint) ~(REFRESH_GRANT | REFRESH_STATUS | REFRESH_READ_LOCK | REFRESH_SLAVE | - REFRESH_MASTER))) + REFRESH_MASTER | REFRESH_TABLE_STATS | + REFRESH_INDEX_STATS | + REFRESH_USER_STATS | + REFRESH_CLIENT_STATS))) { my_printf_error(0, "refresh failed; error: '%s'", error_flags, mysql_error(mysql)); @@ -891,9 +884,19 @@ static int execute_commands(MYSQL *mysql,int argc, char **argv) } case ADMIN_FLUSH_LOGS: { - if (mysql_refresh(mysql,REFRESH_LOG)) + if (mysql_query(mysql,"flush logs")) { - my_printf_error(0, "refresh failed; error: '%s'", error_flags, + my_printf_error(0, "flush failed; error: '%s'", error_flags, + mysql_error(mysql)); + return -1; + } + break; + } + case ADMIN_FLUSH_SLOW_LOG: + { + if (mysql_query(mysql,"flush slow logs")) + { + my_printf_error(0, "flush failed; error: '%s'", error_flags, mysql_error(mysql)); return -1; } @@ -903,7 +906,7 @@ static int execute_commands(MYSQL *mysql,int argc, char **argv) { if (mysql_query(mysql,"flush hosts")) { - my_printf_error(0, "refresh failed; error: '%s'", error_flags, + my_printf_error(0, "flush failed; error: '%s'", error_flags, mysql_error(mysql)); return -1; } @@ -913,7 +916,7 @@ static int execute_commands(MYSQL *mysql,int argc, char **argv) { if (mysql_query(mysql,"flush tables")) { - my_printf_error(0, "refresh failed; error: '%s'", error_flags, + my_printf_error(0, "flush failed; error: '%s'", error_flags, mysql_error(mysql)); return -1; } @@ -923,7 +926,71 @@ static int execute_commands(MYSQL *mysql,int argc, char **argv) { if (mysql_query(mysql,"flush status")) { - my_printf_error(0, "refresh failed; error: '%s'", error_flags, + my_printf_error(0, "flush failed; error: '%s'", error_flags, + mysql_error(mysql)); + return -1; + } + break; + } + case ADMIN_FLUSH_TABLE_STATISTICS: + { + if (mysql_query(mysql,"flush table_statistics")) + { + my_printf_error(0, "flush failed; error: '%s'", error_flags, + mysql_error(mysql)); + return -1; + } + break; + } + case ADMIN_FLUSH_INDEX_STATISTICS: + { + if (mysql_query(mysql,"flush index_statistics")) + { + my_printf_error(0, "flush failed; error: '%s'", error_flags, + mysql_error(mysql)); + return -1; + } + break; + } + case ADMIN_FLUSH_USER_STATISTICS: + { + if (mysql_query(mysql,"flush user_statistics")) + { + my_printf_error(0, "flush failed; error: '%s'", error_flags, + mysql_error(mysql)); + return -1; + } + break; + } + case ADMIN_FLUSH_CLIENT_STATISTICS: + { + if (mysql_query(mysql,"flush client_statistics")) + { + my_printf_error(0, "flush failed; error: '%s'", error_flags, + mysql_error(mysql)); + return -1; + } + break; + } + case ADMIN_FLUSH_ALL_STATISTICS: + { + if (mysql_query(mysql, + "flush table_statistics,index_statistics," + "user_statistics,client_statistics")) + { + my_printf_error(0, "flush failed; error: '%s'", error_flags, + mysql_error(mysql)); + return -1; + } + break; + } + case ADMIN_FLUSH_ALL_STATUS: + { + if (mysql_query(mysql, + "flush status,table_statistics,index_statistics," + "user_statistics,client_statistics")) + { + my_printf_error(0, "flush failed; error: '%s'", error_flags, mysql_error(mysql)); return -1; } @@ -937,7 +1004,7 @@ static int execute_commands(MYSQL *mysql,int argc, char **argv) char *typed_password= NULL, *verified= NULL; /* Do initialization the same way as we do in mysqld */ start_time=time((time_t*) 0); - randominit(&rand_st,(ulong) start_time,(ulong) start_time/2); + my_rnd_init(&rand_st,(ulong) start_time,(ulong) start_time/2); if (argc < 1) { @@ -1124,6 +1191,9 @@ static int execute_commands(MYSQL *mysql,int argc, char **argv) static char **mask_password(int argc, char ***argv) { char **temp_argv; + if (!argc) + return NULL; + temp_argv= (char **)(my_malloc(sizeof(char *) * argc, MYF(MY_WME))); argc--; while (argc > 0) @@ -1158,20 +1228,28 @@ static void usage(void) puts(ORACLE_WELCOME_COPYRIGHT_NOTICE("2000")); puts("Administration program for the mysqld daemon."); printf("Usage: %s [OPTIONS] command command....\n", my_progname); + print_defaults("my",load_default_groups); + puts(""); my_print_help(my_long_options); my_print_variables(my_long_options); - print_defaults("my",load_default_groups); puts("\nWhere command is a one or more of: (Commands may be shortened)\n\ - create databasename Create a new database\n\ - debug Instruct server to write debug information to log\n\ - drop databasename Delete a database and all its tables\n\ - extended-status Gives an extended status message from the server\n\ - flush-hosts Flush all cached hosts\n\ - flush-logs Flush all logs\n\ - flush-status Clear status variables\n\ - flush-tables Flush all tables\n\ - flush-threads Flush the thread cache\n\ - flush-privileges Reload grant tables (same as reload)\n\ + create databasename Create a new database\n\ + debug Instruct server to write debug information to log\n\ + drop databasename Delete a database and all its tables\n\ + extended-status Gives an extended status message from the server\n\ + flush-all-statistics Flush all statistics tables\n\ + flush-all-status Flush status and statistics\n\ + flush-client-statistics Flush client statistics\n\ + flush-hosts Flush all cached hosts\n\ + flush-index-statistics Flush index statistics\n\ + flush-logs Flush all logs\n\ + flush-privileges Reload grant tables (same as reload)\n\ + flush-slow-log Flush slow query log\n\ + flush-status Clear status variables\n\ + flush-table-statistics Clear table statistics\n\ + flush-tables Flush all tables\n\ + flush-threads Flush the thread cache\n\ + flush-user-statistics Flush user statistics\n\ kill id,id,... Kill mysql threads"); #if MYSQL_VERSION_ID >= 32200 puts("\ @@ -1461,7 +1539,7 @@ static my_bool wait_pidfile(char *pidfile, time_t last_modified, struct stat *pidfile_status) { char buff[FN_REFLEN]; - int error= 1; + my_bool error= 1; uint count= 0; DBUG_ENTER("wait_pidfile"); diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc index 73a801c4b21..250dc609891 100644 --- a/client/mysqlbinlog.cc +++ b/client/mysqlbinlog.cc @@ -1,5 +1,6 @@ /* - Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2000, 2014, Oracle and/or its affiliates. + Copyright (c) 2009, 2014, MariaDB 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 @@ -30,6 +31,7 @@ #define MYSQL_CLIENT #undef MYSQL_SERVER +#define TABLE TABLE_CLIENT #include "client_priv.h" #include <my_time.h> /* That one is necessary for defines of OPTION_NO_FOREIGN_KEY_CHECKS etc */ @@ -39,12 +41,24 @@ #include "my_dir.h" #include <welcome_copyright_notice.h> // ORACLE_WELCOME_COPYRIGHT_NOTICE + +#include "sql_string.h" // needed for Rpl_filter +#include "sql_list.h" // needed for Rpl_filter +#include "rpl_filter.h" + +#include "mysqld.h" + +Rpl_filter *binlog_filter= 0; + #define BIN_LOG_HEADER_SIZE 4 #define PROBE_HEADER_LEN (EVENT_LEN_OFFSET+4) #define CLIENT_CAPABILITIES (CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | CLIENT_LOCAL_FILES) +/* Needed for Rpl_filter */ +CHARSET_INFO* system_charset_info= &my_charset_utf8_general_ci; + char server_version[SERVER_VERSION_LENGTH]; ulong server_id = 0; @@ -52,6 +66,7 @@ ulong server_id = 0; ulong bytes_sent = 0L, bytes_received = 0L; ulong mysqld_net_retry_count = 10L; ulong open_files_limit; +ulong opt_binlog_rows_event_max_size; uint test_flags = 0; static uint opt_protocol= 0; static FILE *result_file; @@ -59,13 +74,14 @@ static FILE *result_file; #ifndef DBUG_OFF static const char* default_dbug_option = "d:t:o,/tmp/mysqlbinlog.trace"; #endif -static const char *load_default_groups[]= { "mysqlbinlog","client",0 }; +static const char *load_groups[]= +{ "mysqlbinlog", "client", "client-server", "client-mariadb", 0 }; static void error(const char *format, ...) ATTRIBUTE_FORMAT(printf, 1, 2); static void warning(const char *format, ...) ATTRIBUTE_FORMAT(printf, 1, 2); static bool one_database=0, to_last_remote_log= 0, disable_log_bin= 0; -static bool opt_hexdump= 0; +static bool opt_hexdump= 0, opt_version= 0; const char *base64_output_mode_names[]= {"NEVER", "AUTO", "ALWAYS", "UNSPEC", "DECODE-ROWS", NullS}; TYPELIB base64_output_mode_typelib= @@ -77,12 +93,13 @@ static char* database= 0; static my_bool force_opt= 0, short_form= 0, remote_opt= 0; static my_bool debug_info_flag, debug_check_flag; static my_bool force_if_open_opt= 1; +static my_bool opt_verify_binlog_checksum= 1; static ulonglong offset = 0; static char* host = 0; static int port= 0; static uint my_end_arg; static const char* sock= 0; -static char *opt_plugin_dir= 0, *opt_default_auth= 0; +static char *opt_plugindir= 0, *opt_default_auth= 0; #ifdef HAVE_SMEM static char *shared_memory_base_name= 0; @@ -102,7 +119,8 @@ static my_time_t start_datetime= 0, stop_datetime= MY_TIME_T_MAX; static ulonglong rec_count= 0; static short binlog_flags = 0; static MYSQL* mysql = NULL; -static char* dirname_for_local_load= 0; +static const char* dirname_for_local_load= 0; +static bool opt_skip_annotate_row_events= 0; /** Pointer to the Format_description_log_event of the currently active binlog. @@ -124,10 +142,73 @@ enum Exit_status { OK_STOP }; -static Exit_status dump_local_log_entries(PRINT_EVENT_INFO *print_event_info, - const char* logname); -static Exit_status dump_remote_log_entries(PRINT_EVENT_INFO *print_event_info, - const char* logname); +/** + Pointer to the last read Annotate_rows_log_event. Having read an + Annotate_rows event, we should not print it immediatedly because all + subsequent rbr events can be filtered away, and have to keep it for a while. + Also because of that when reading a remote Annotate event we have to keep + its binary log representation in a separately allocated buffer. +*/ +static Annotate_rows_log_event *annotate_event= NULL; + +void free_annotate_event() +{ + if (annotate_event) + { + delete annotate_event; + annotate_event= 0; + } +} + +Log_event* read_remote_annotate_event(uchar* net_buf, ulong event_len, + const char **error_msg) +{ + uchar *event_buf; + Log_event* event; + + if (!(event_buf= (uchar*) my_malloc(event_len + 1, MYF(MY_WME)))) + { + error("Out of memory"); + return 0; + } + + memcpy(event_buf, net_buf, event_len); + event_buf[event_len]= 0; + + if (!(event= Log_event::read_log_event((const char*) event_buf, event_len, + error_msg, glob_description_event, + opt_verify_binlog_checksum))) + { + my_free(event_buf); + return 0; + } + /* + Ensure the event->temp_buf is pointing to the allocated buffer. + (TRUE = free temp_buf on the event deletion) + */ + event->register_temp_buf((char*)event_buf, TRUE); + + return event; +} + +void keep_annotate_event(Annotate_rows_log_event* event) +{ + free_annotate_event(); + annotate_event= event; +} + +void print_annotate_event(PRINT_EVENT_INFO *print_event_info) +{ + if (annotate_event) + { + annotate_event->print(result_file, print_event_info); + delete annotate_event; // the event should not be printed more than once + annotate_event= 0; + } +} + +static Exit_status dump_local_log_entries(PRINT_EVENT_INFO *, const char*); +static Exit_status dump_remote_log_entries(PRINT_EVENT_INFO *, const char*); static Exit_status dump_log_entries(const char* logname); static Exit_status safe_connect(); @@ -632,6 +713,72 @@ static bool shall_skip_database(const char *log_dbname) /** + Print "use <db>" statement when current db is to be changed. + + We have to control emiting USE statements according to rewrite-db options. + We have to do it here (see process_event() below) and to suppress + producing USE statements by corresponding log event print-functions. +*/ + +static void +print_use_stmt(PRINT_EVENT_INFO* pinfo, const Query_log_event *ev) +{ + const char* db= ev->db; + const size_t db_len= ev->db_len; + + // pinfo->db is the current db. + // If current db is the same as required db, do nothing. + if ((ev->flags & LOG_EVENT_SUPPRESS_USE_F) || !db || + !memcmp(pinfo->db, db, db_len + 1)) + return; + + // Current db and required db are different. + // Check for rewrite rule for required db. (Note that in a rewrite rule + // neither db_from nor db_to part can be empty). + size_t len_to= 0; + const char *db_to= binlog_filter->get_rewrite_db(db, &len_to); + + // If there is no rewrite rule for db (in this case len_to is left = 0), + // printing of the corresponding USE statement is left for log event + // print-function. + if (!len_to) + return; + + // In case of rewrite rule print USE statement for db_to + my_fprintf(result_file, "use %`s%s\n", db_to, pinfo->delimiter); + + // Copy the *original* db to pinfo to suppress emiting + // of USE stmts by log_event print-functions. + memcpy(pinfo->db, db, db_len + 1); +} + + +/** + Print "SET skip_replication=..." statement when needed. + + Not all servers support this (only MariaDB from some version on). So we + mark the SET to only execute from the version of MariaDB that supports it, + and also only output it if we actually see events with the flag set, to not + get spurious errors on MySQL@Oracle servers of higher version that do not + support the flag. + + So we start out assuming @@skip_replication is 0, and only output a SET + statement when it changes. +*/ +static void +print_skip_replication_statement(PRINT_EVENT_INFO *pinfo, const Log_event *ev) +{ + int cur_val; + + cur_val= (ev->flags & LOG_EVENT_SKIP_REPLICATION_F) != 0; + if (cur_val == pinfo->skip_replication) + return; /* Not changed. */ + fprintf(result_file, "/*!50521 SET skip_replication=%d*/%s\n", + cur_val, pinfo->delimiter); + pinfo->skip_replication= cur_val; +} + +/** Prints the given event in base64 format. The header is printed to the head cache and the body is printed to @@ -670,6 +817,98 @@ write_event_header_and_base64(Log_event *ev, FILE *result_file, } +static bool print_base64(PRINT_EVENT_INFO *print_event_info, Log_event *ev) +{ + /* + These events must be printed in base64 format, if printed. + base64 format requires a FD event to be safe, so if no FD + event has been printed, we give an error. Except if user + passed --short-form, because --short-form disables printing + row events. + */ + if (!print_event_info->printed_fd_event && !short_form && + opt_base64_output_mode != BASE64_OUTPUT_DECODE_ROWS) + { + const char* type_str= ev->get_type_str(); + if (opt_base64_output_mode == BASE64_OUTPUT_NEVER) + error("--base64-output=never specified, but binlog contains a " + "%s event which must be printed in base64.", + type_str); + else + error("malformed binlog: it does not contain any " + "Format_description_log_event. I now found a %s event, which " + "is not safe to process without a " + "Format_description_log_event.", + type_str); + return 1; + } + ev->print(result_file, print_event_info); + return print_event_info->head_cache.error == -1; +} + + +static bool print_row_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev, + ulong table_id, bool is_stmt_end) +{ + Table_map_log_event *ignored_map= + print_event_info->m_table_map_ignored.get_table(table_id); + bool skip_event= (ignored_map != NULL); + + /* + end of statement check: + i) destroy/free ignored maps + ii) if skip event + a) since we are skipping the last event, + append END-MARKER(') to body cache (if required) + + b) flush cache now + */ + if (is_stmt_end) + { + /* + Now is safe to clear ignored map (clear_tables will also + delete original table map events stored in the map). + */ + if (print_event_info->m_table_map_ignored.count() > 0) + print_event_info->m_table_map_ignored.clear_tables(); + + /* + If there is a kept Annotate event and all corresponding + rbr-events were filtered away, the Annotate event was not + freed and it is just the time to do it. + */ + free_annotate_event(); + + /* + One needs to take into account an event that gets + filtered but was last event in the statement. If this is + the case, previous rows events that were written into + IO_CACHEs still need to be copied from cache to + result_file (as it would happen in ev->print(...) if + event was not skipped). + */ + if (skip_event) + { + // append END-MARKER(') with delimiter + IO_CACHE *const body_cache= &print_event_info->body_cache; + if (my_b_tell(body_cache)) + my_b_printf(body_cache, "'%s\n", print_event_info->delimiter); + + // flush cache + if ((copy_event_cache_to_file_and_reinit(&print_event_info->head_cache, result_file) || + copy_event_cache_to_file_and_reinit(&print_event_info->body_cache, result_file))) + return 1; + } + } + + /* skip the event check */ + if (skip_event) + return 0; + + return print_base64(print_event_info, ev); +} + + /** Print the given event, and either delete it or delegate the deletion to someone else. @@ -706,7 +945,7 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev, read them to be able to process the wanted events. */ if (((rec_count >= offset) && - ((my_time_t)(ev->when) >= start_datetime)) || + (ev->when >= start_datetime)) || (ev_type == FORMAT_DESCRIPTION_EVENT)) { if (ev_type != FORMAT_DESCRIPTION_EVENT) @@ -730,7 +969,7 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev, server_id && (server_id != ev->server_id)) goto end; } - if (((my_time_t)(ev->when) >= stop_datetime) + if ((ev->when >= stop_datetime) || (pos >= stop_position_mot)) { /* end the program */ @@ -751,9 +990,22 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev, switch (ev_type) { case QUERY_EVENT: - if (!((Query_log_event*)ev)->is_trans_keyword() && - shall_skip_database(((Query_log_event*)ev)->db)) - goto end; + { + Query_log_event *qe= (Query_log_event*)ev; + if (!qe->is_trans_keyword()) + { + if (shall_skip_database(qe->db)) + goto end; + } + else + { + /* + In case the event for one of these statements is obtained + from binary log 5.0, make it compatible with 5.1 + */ + qe->flags|= LOG_EVENT_SUPPRESS_USE_F; + } + print_use_stmt(print_event_info, qe); if (opt_base64_output_mode == BASE64_OUTPUT_ALWAYS) { if ((retval= write_event_header_and_base64(ev, result_file, @@ -762,10 +1014,14 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev, goto end; } else + { + print_skip_replication_statement(print_event_info, ev); ev->print(result_file, print_event_info); + } if (head->error == -1) goto err; break; + } case CREATE_FILE_EVENT: { @@ -794,6 +1050,7 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev, } else { + print_skip_replication_statement(print_event_info, ev); ce->print(result_file, print_event_info, TRUE); if (head->error == -1) goto err; @@ -906,9 +1163,11 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev, if (!shall_skip_database(exlq->db)) { + print_use_stmt(print_event_info, exlq); if (fname) { convert_path_to_forward_slashes(fname); + print_skip_replication_statement(print_event_info, ev); exlq->print(result_file, print_event_info, fname); if (head->error == -1) { @@ -926,6 +1185,19 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev, my_free(fname); break; } + case ANNOTATE_ROWS_EVENT: + if (!opt_skip_annotate_row_events) + { + /* + We don't print Annotate event just now because all subsequent + rbr-events can be filtered away. Instead we'll keep the event + till it will be printed together with the first not filtered + away Table map or the last rbr will be processed. + */ + keep_annotate_event((Annotate_rows_log_event*) ev); + destroy_evt= FALSE; + } + break; case TABLE_MAP_EVENT: { Table_map_log_event *map= ((Table_map_log_event *)ev); @@ -935,107 +1207,46 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev, destroy_evt= FALSE; goto end; } + /* + The Table map is to be printed, so it's just the time when we may + print the kept Annotate event (if there is any). + print_annotate_event() also deletes the kept Annotate event. + */ + print_annotate_event(print_event_info); + + size_t len_to= 0; + const char* db_to= binlog_filter->get_rewrite_db(map->get_db_name(), &len_to); + if (len_to && map->rewrite_db(db_to, len_to, glob_description_event)) + { + error("Could not rewrite database name"); + goto err; + } + if (print_base64(print_event_info, ev)) + goto err; + break; } case WRITE_ROWS_EVENT: case DELETE_ROWS_EVENT: case UPDATE_ROWS_EVENT: + { + Rows_log_event *e= (Rows_log_event*) ev; + if (print_row_event(print_event_info, ev, e->get_table_id(), + e->get_flags(Rows_log_event::STMT_END_F))) + goto err; + break; + } case PRE_GA_WRITE_ROWS_EVENT: case PRE_GA_DELETE_ROWS_EVENT: case PRE_GA_UPDATE_ROWS_EVENT: { - bool stmt_end= FALSE; - Table_map_log_event *ignored_map= NULL; - - if (ev_type == WRITE_ROWS_EVENT || - ev_type == DELETE_ROWS_EVENT || - ev_type == UPDATE_ROWS_EVENT) - { - Rows_log_event *new_ev= (Rows_log_event*) ev; - if (new_ev->get_flags(Rows_log_event::STMT_END_F)) - stmt_end= TRUE; - ignored_map= print_event_info->m_table_map_ignored.get_table(new_ev->get_table_id()); - } - else if (ev_type == PRE_GA_WRITE_ROWS_EVENT || - ev_type == PRE_GA_DELETE_ROWS_EVENT || - ev_type == PRE_GA_UPDATE_ROWS_EVENT) - { - Old_rows_log_event *old_ev= (Old_rows_log_event*) ev; - if (old_ev->get_flags(Rows_log_event::STMT_END_F)) - stmt_end= TRUE; - ignored_map= print_event_info->m_table_map_ignored.get_table(old_ev->get_table_id()); - } - - bool skip_event= (ignored_map != NULL); - /* - end of statement check: - i) destroy/free ignored maps - ii) if skip event - a) since we are skipping the last event, - append END-MARKER(') to body cache (if required) - - b) flush cache now - */ - if (stmt_end) - { - - /* - Now is safe to clear ignored map (clear_tables will also - delete original table map events stored in the map). - */ - if (print_event_info->m_table_map_ignored.count() > 0) - print_event_info->m_table_map_ignored.clear_tables(); - - /* - One needs to take into account an event that gets - filtered but was last event in the statement. If this is - the case, previous rows events that were written into - IO_CACHEs still need to be copied from cache to - result_file (as it would happen in ev->print(...) if - event was not skipped). - */ - if (skip_event) - { - // append END-MARKER(') with delimiter - IO_CACHE *const body_cache= &print_event_info->body_cache; - if (my_b_tell(body_cache)) - my_b_printf(body_cache, "'%s\n", print_event_info->delimiter); - - // flush cache - if ((copy_event_cache_to_file_and_reinit(&print_event_info->head_cache, result_file) || - copy_event_cache_to_file_and_reinit(&print_event_info->body_cache, result_file))) - goto err; - } - } - - /* skip the event check */ - if (skip_event) - goto end; - /* - These events must be printed in base64 format, if printed. - base64 format requires a FD event to be safe, so if no FD - event has been printed, we give an error. Except if user - passed --short-form, because --short-form disables printing - row events. - */ - if (!print_event_info->printed_fd_event && !short_form && - opt_base64_output_mode != BASE64_OUTPUT_DECODE_ROWS) - { - const char* type_str= ev->get_type_str(); - if (opt_base64_output_mode == BASE64_OUTPUT_NEVER) - error("--base64-output=never specified, but binlog contains a " - "%s event which must be printed in base64.", - type_str); - else - error("malformed binlog: it does not contain any " - "Format_description_log_event. I now found a %s event, which " - "is not safe to process without a " - "Format_description_log_event.", - type_str); + Old_rows_log_event *e= (Old_rows_log_event*) ev; + if (print_row_event(print_event_info, ev, e->get_table_id(), + e->get_flags(Old_rows_log_event::STMT_END_F))) goto err; - } - /* FALL THROUGH */ + break; } default: + print_skip_replication_statement(print_event_info, ev); ev->print(result_file, print_event_info); if (head->error == -1) goto err; @@ -1048,14 +1259,16 @@ err: retval= ERROR_STOP; end: rec_count++; + /* - Destroy the log_event object. If reading from a remote host, - set the temp_buf to NULL so that memory isn't freed twice. + Destroy the log_event object. + MariaDB MWL#36: mainline does this: + If reading from a remote host, + set the temp_buf to NULL so that memory isn't freed twice. + We no longer do that, we use Rpl_filter::event_owns_temp_buf instead. */ if (ev) { - if (remote_opt) - ev->temp_buf= 0; if (destroy_evt) /* destroy it later if not set (ignored table map) */ delete ev; } @@ -1063,7 +1276,7 @@ end: } -static struct my_option my_long_options[] = +static struct my_option my_options[] = { {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, @@ -1133,7 +1346,7 @@ static struct my_option my_long_options[] = {"password", 'p', "Password to connect to remote server.", 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, {"plugin_dir", OPT_PLUGIN_DIR, "Directory for client-side plugins.", - &opt_plugin_dir, &opt_plugin_dir, 0, + &opt_plugindir, &opt_plugindir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"port", 'P', "Port number to use for connection or 0 for default to, in " "order of preference, my.cnf, $MYSQL_TCP_PORT, " @@ -1151,7 +1364,7 @@ static struct my_option my_long_options[] = 0, 0}, {"result-file", 'r', "Direct output to a given file.", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"server-id", OPT_SERVER_ID, + {"server-id", 0, "Extract only binlog entries created by the server having the given id.", &server_id, &server_id, 0, GET_ULONG, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, @@ -1220,6 +1433,24 @@ that may lead to an endless loop.", "Used to reserve file descriptors for use by this program.", &open_files_limit, &open_files_limit, 0, GET_ULONG, REQUIRED_ARG, MY_NFILE, 8, OS_FILE_LIMIT, 0, 1, 0}, + {"binlog-row-event-max-size", 0, + "The maximum size of a row-based binary log event in bytes. Rows will be " + "grouped into events smaller than this size if possible. " + "This value must be a multiple of 256.", + &opt_binlog_rows_event_max_size, &opt_binlog_rows_event_max_size, 0, + GET_ULONG, REQUIRED_ARG, UINT_MAX, 256, ULONG_MAX, 0, 256, 0}, + {"verify-binlog-checksum", 'c', "Verify checksum binlog events.", + (uchar**) &opt_verify_binlog_checksum, (uchar**) &opt_verify_binlog_checksum, + 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"rewrite-db", OPT_REWRITE_DB, + "Updates to a database with a different name than the original. \ +Example: rewrite-db='from->to'.", + 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"skip-annotate-row-events", OPT_SKIP_ANNOTATE_ROWS_EVENTS, + "Don't print Annotate_rows events stored in the binary log.", + (uchar**) &opt_skip_annotate_row_events, + (uchar**) &opt_skip_annotate_row_events, + 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} }; @@ -1295,8 +1526,9 @@ static void cleanup() my_free(database); my_free(host); my_free(user); - my_free(dirname_for_local_load); + my_free(const_cast<char*>(dirname_for_local_load)); + delete binlog_filter; delete glob_description_event; if (mysql) mysql_close(mysql); @@ -1317,8 +1549,10 @@ static void usage() Dumps a MySQL binary log in a format usable for viewing or for piping to\n\ the mysql command line client.\n\n"); printf("Usage: %s [options] log-files\n", my_progname); - my_print_help(my_long_options); - my_print_variables(my_long_options); + print_defaults("my",load_groups); + puts(""); + my_print_help(my_options); + my_print_variables(my_options); } @@ -1327,7 +1561,7 @@ static my_time_t convert_str_to_timestamp(const char* str) int was_cut; MYSQL_TIME l_time; long dummy_my_timezone; - my_bool dummy_in_dst_time_gap; + uint dummy_in_dst_time_gap; /* We require a total specification (date AND time) */ if (str_to_datetime(str, (uint) strlen(str), &l_time, 0, &was_cut) != MYSQL_TIMESTAMP_DATETIME || was_cut) @@ -1400,6 +1634,53 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), (find_type_or_exit(argument, &base64_output_mode_typelib, opt->name)-1); } break; + case OPT_REWRITE_DB: // db_from->db_to + { + /* See also handling of OPT_REPLICATE_REWRITE_DB in sql/mysqld.cc */ + char* ptr; + char* key= argument; // db-from + char* val; // db-to + + // Where key begins + while (*key && my_isspace(&my_charset_latin1, *key)) + key++; + + // Where val begins + if (!(ptr= strstr(argument, "->"))) + { + sql_print_error("Bad syntax in rewrite-db: missing '->'!\n"); + return 1; + } + val= ptr + 2; + while (*val && my_isspace(&my_charset_latin1, *val)) + val++; + + // Write \0 and skip blanks at the end of key + *ptr-- = 0; + while (my_isspace(&my_charset_latin1, *ptr) && ptr > argument) + *ptr-- = 0; + + if (!*key) + { + sql_print_error("Bad syntax in rewrite-db: empty db-from!\n"); + return 1; + } + + // Skip blanks at the end of val + ptr= val; + while (*ptr && !my_isspace(&my_charset_latin1, *ptr)) + ptr++; + *ptr= 0; + + if (!*val) + { + sql_print_error("Bad syntax in rewrite-db: empty db-to!\n"); + return 1; + } + + binlog_filter->add_db_rewrite(key, val); + break; + } case 'v': if (argument == disabled_my_option) verbose= 0; @@ -1408,10 +1689,12 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), break; case 'V': print_version(); - exit(0); + opt_version= 1; + break; case '?': usage(); - exit(0); + opt_version= 1; + break; } if (tty_password) pass= get_tty_password(NullS); @@ -1425,7 +1708,7 @@ static int parse_args(int *argc, char*** argv) int ho_error; result_file = stdout; - if ((ho_error=handle_options(argc, argv, my_long_options, get_one_option))) + if ((ho_error=handle_options(argc, argv, my_options, get_one_option))) exit(ho_error); if (debug_info_flag) my_end_arg= MY_CHECK_ERROR | MY_GIVE_INFO; @@ -1444,6 +1727,10 @@ static int parse_args(int *argc, char*** argv) */ static Exit_status safe_connect() { + /* Close any old connections to MySQL */ + if (mysql) + mysql_close(mysql); + mysql= mysql_init(NULL); if (!mysql) @@ -1452,8 +1739,8 @@ static Exit_status safe_connect() return ERROR_STOP; } - if (opt_plugin_dir && *opt_plugin_dir) - mysql_options(mysql, MYSQL_PLUGIN_DIR, opt_plugin_dir); + if (opt_plugindir && *opt_plugindir) + mysql_options(mysql, MYSQL_PLUGIN_DIR, opt_plugindir); if (opt_default_auth && *opt_default_auth) mysql_options(mysql, MYSQL_DEFAULT_AUTH, opt_default_auth); @@ -1550,7 +1837,18 @@ static Exit_status check_master_version() "Master reported NULL for the version."); goto err; } - + /* + Make a notice to the server that this client + is checksum-aware. It does not need the first fake Rotate + necessary checksummed. + That preference is specified below. + */ + if (mysql_query(mysql, "SET @master_binlog_checksum='NONE'")) + { + error("Could not notify master about checksum awareness." + "Master returned '%s'", mysql_error(mysql)); + goto err; + } delete glob_description_event; switch (*version) { case '3': @@ -1632,6 +1930,8 @@ static Exit_status dump_remote_log_entries(PRINT_EVENT_INFO *print_event_info, cast to uint32. */ int4store(buf, (uint32)start_position); + if (!opt_skip_annotate_row_events) + binlog_flags|= BINLOG_SEND_ANNOTATE_ROWS_EVENT; int2store(buf + BIN_LOG_HEADER_SIZE, binlog_flags); size_t tlen = strlen(logname); @@ -1664,18 +1964,31 @@ static Exit_status dump_remote_log_entries(PRINT_EVENT_INFO *print_event_info, break; // end of data DBUG_PRINT("info",( "len: %lu net->read_pos[5]: %d\n", len, net->read_pos[5])); - if (!(ev= Log_event::read_log_event((const char*) net->read_pos + 1 , - len - 1, &error_msg, - glob_description_event))) + if (net->read_pos[5] == ANNOTATE_ROWS_EVENT) { - error("Could not construct log event object: %s", error_msg); - DBUG_RETURN(ERROR_STOP); - } - /* - If reading from a remote host, ensure the temp_buf for the - Log_event class is pointing to the incoming stream. - */ - ev->register_temp_buf((char *) net->read_pos + 1); + if (!(ev= read_remote_annotate_event(net->read_pos + 1, len - 1, + &error_msg))) + { + error("Could not construct annotate event object: %s", error_msg); + DBUG_RETURN(ERROR_STOP); + } + } + else + { + if (!(ev= Log_event::read_log_event((const char*) net->read_pos + 1 , + len - 1, &error_msg, + glob_description_event, + opt_verify_binlog_checksum))) + { + error("Could not construct log event object: %s", error_msg); + DBUG_RETURN(ERROR_STOP); + } + /* + If reading from a remote host, ensure the temp_buf for the + Log_event class is pointing to the incoming stream. + */ + ev->register_temp_buf((char *) net->read_pos + 1, FALSE); + } Log_event_type type= ev->get_type_code(); if (glob_description_event->binlog_version >= 3 || @@ -1903,7 +2216,8 @@ static Exit_status check_header(IO_CACHE* file, Format_description_log_event *new_description_event; my_b_seek(file, tmp_pos); /* seek back to event's start */ if (!(new_description_event= (Format_description_log_event*) - Log_event::read_log_event(file, glob_description_event))) + Log_event::read_log_event(file, glob_description_event, + opt_verify_binlog_checksum))) /* EOF can't be hit here normally, so it's a real error */ { error("Could not read a Format_description_log_event event at " @@ -1936,7 +2250,8 @@ static Exit_status check_header(IO_CACHE* file, { Log_event *ev; my_b_seek(file, tmp_pos); /* seek back to event's start */ - if (!(ev= Log_event::read_log_event(file, glob_description_event))) + if (!(ev= Log_event::read_log_event(file, glob_description_event, + opt_verify_binlog_checksum))) { /* EOF can't be hit here normally, so it's a real error */ error("Could not read a Rotate_log_event event at offset %llu;" @@ -2049,7 +2364,8 @@ static Exit_status dump_local_log_entries(PRINT_EVENT_INFO *print_event_info, char llbuff[21]; my_off_t old_off = my_b_tell(file); - Log_event* ev = Log_event::read_log_event(file, glob_description_event); + Log_event* ev = Log_event::read_log_event(file, glob_description_event, + opt_verify_binlog_checksum); if (!ev) { /* @@ -2091,6 +2407,8 @@ end: return retval; } +/* Used in sql_alloc(). Inited and freed in main() */ +MEM_ROOT s_mem_root; int main(int argc, char** argv) { @@ -2104,17 +2422,27 @@ int main(int argc, char** argv) my_init_time(); // for time functions tzset(); // set tzname - if (load_defaults("my", load_default_groups, &argc, &argv)) + init_alloc_root(&s_mem_root, 16384, 0); + if (load_defaults("my", load_groups, &argc, &argv)) + exit(1); + + if (!(binlog_filter= new Rpl_filter)) + { + error("Failed to create Rpl_filter"); exit(1); + } + defaults_argv= argv; parse_args(&argc, (char***)&argv); - if (!argc) + if (!argc || opt_version) { - usage(); + if (!argc) + usage(); + cleanup(); free_defaults(defaults_argv); my_end(my_end_arg); - exit(1); + exit(!opt_version); } if (opt_base64_output_mode == BASE64_OUTPUT_UNSPEC) @@ -2204,6 +2532,8 @@ int main(int argc, char** argv) if (result_file != stdout) my_fclose(result_file, MYF(0)); cleanup(); + free_annotate_event(); + free_root(&s_mem_root, MYF(0)); free_defaults(defaults_argv); my_free_open_file_info(); load_processor.destroy(); @@ -2215,15 +2545,25 @@ int main(int argc, char** argv) DBUG_RETURN(retval == ERROR_STOP ? 1 : 0); } + +void *sql_alloc(size_t size) +{ + return alloc_root(&s_mem_root, size); +} + /* We must include this here as it's compiled with different options for the server */ +#include "rpl_tblmap.cc" +#undef TABLE #include "my_decimal.h" #include "decimal.c" #include "my_decimal.cc" #include "log_event.cc" #include "log_event_old.cc" #include "rpl_utility.cc" -#include "rpl_tblmap.cc" +#include "sql_string.cc" +#include "sql_list.cc" +#include "rpl_filter.cc" diff --git a/client/mysqlcheck.c b/client/mysqlcheck.c index 55b941e7f1a..011a0e2d738 100644 --- a/client/mysqlcheck.c +++ b/client/mysqlcheck.c @@ -1,5 +1,6 @@ /* - Copyright (c) 2001, 2016, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2001, 2011, Oracle and/or its affiliates. + Copyright (c) 2010, 2016, MariaDB 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 @@ -15,7 +16,9 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#define CHECK_VERSION "2.5.1" +/* By Jani Tolonen, 2001-04-20, MySQL Development Team */ + +#define CHECK_VERSION "2.7.2-MariaDB" #include "client_priv.h" #include <m_ctype.h> @@ -40,10 +43,9 @@ static my_bool opt_alldbs = 0, opt_check_only_changed = 0, opt_extended = 0, opt_silent = 0, opt_auto_repair = 0, ignore_errors = 0, tty_password= 0, opt_frm= 0, debug_info_flag= 0, debug_check_flag= 0, opt_fix_table_names= 0, opt_fix_db_names= 0, opt_upgrade= 0, - opt_write_binlog= 1; + opt_do_tables= 1; +static my_bool opt_write_binlog= 1, opt_flush_tables= 0; static uint verbose = 0, opt_mysql_port=0; -static uint opt_enable_cleartext_plugin= 0; -static my_bool using_opt_enable_cleartext_plugin= 0; static int my_end_arg; static char * opt_mysql_unix_port = 0; static char *opt_password = 0, *current_user = 0, @@ -51,12 +53,21 @@ static char *opt_password = 0, *current_user = 0, static char *opt_plugin_dir= 0, *opt_default_auth= 0; static int first_error = 0; DYNAMIC_ARRAY tables4repair, tables4rebuild, alter_table_cmds; -#ifdef HAVE_SMEM +DYNAMIC_ARRAY views4repair; static char *shared_memory_base_name=0; -#endif static uint opt_protocol=0; -enum operations { DO_CHECK=1, DO_REPAIR, DO_ANALYZE, DO_OPTIMIZE, DO_UPGRADE }; +enum operations { DO_CHECK=1, DO_REPAIR, DO_ANALYZE, DO_OPTIMIZE, DO_FIX_NAMES }; +const char *operation_name[]= +{ + "???", "check", "repair", "analyze", "optimize", "fix names" +}; + +typedef enum { DO_VIEWS_NO, DO_VIEWS_YES, DO_VIEWS_FROM_MYSQL } enum_do_views; +const char *do_views_opts[]= {"NO", "YES", "UPGRADE_FROM_MYSQL", NullS}; +TYPELIB do_views_typelib= { array_elements(do_views_opts) - 1, "", + do_views_opts, NULL }; +static ulong opt_do_views= DO_VIEWS_NO; static struct my_option my_long_options[] = { @@ -75,8 +86,8 @@ static struct my_option my_long_options[] = &opt_auto_repair, &opt_auto_repair, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"character-sets-dir", OPT_CHARSETS_DIR, - "Directory for character set files.", &charsets_dir, - &charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + "Directory for character set files.", (char**) &charsets_dir, + (char**) &charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"check", 'c', "Check table for errors.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, {"check-only-changed", 'C', @@ -112,10 +123,6 @@ static struct my_option my_long_options[] = "Default authentication client-side plugin to use.", &opt_default_auth, &opt_default_auth, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"enable_cleartext_plugin", OPT_ENABLE_CLEARTEXT_PLUGIN, - "Enable/disable the clear text authentication plugin.", - &opt_enable_cleartext_plugin, &opt_enable_cleartext_plugin, - 0, GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0}, {"fast",'F', "Check only tables that haven't been closed properly.", &opt_fast, &opt_fast, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, @@ -132,10 +139,13 @@ static struct my_option my_long_options[] = "If you are using this option with CHECK TABLE, it will ensure that the table is 100 percent consistent, but will take a long time. If you are using this option with REPAIR TABLE, it will force using old slow repair with keycache method, instead of much faster repair by sorting.", &opt_extended, &opt_extended, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"flush", OPT_FLUSH_TABLES, "Flush each table after check. This is useful if you don't want to have the checked tables take up space in the caches after the check", + &opt_flush_tables, &opt_flush_tables, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, + 0, 0 }, {"help", '?', "Display this help message and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, {"host",'h', "Connect to host.", ¤t_host, - ¤t_host, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + ¤t_host, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"medium-check", 'm', "Faster than extended-check, but only finds 99.99 percent of all errors. Should be good enough for most cases.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, @@ -198,10 +208,19 @@ static struct my_option my_long_options[] = NO_ARG, 0, 0, 0, 0, 0, 0}, {"version", 'V', "Output version information and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"process-views", 0, + "Perform the requested operation (check or repair) on views. " + "One of: NO, YES (correct the checksum, if necessary, add the " + "mariadb-version field), UPGRADE_FROM_MYSQL (same as YES and toggle " + "the algorithm MERGE<->TEMPTABLE.", &opt_do_views, &opt_do_views, + &do_views_typelib, GET_ENUM, OPT_ARG, 0, 0, 0, 0, 0, 0}, + {"process-tables", 0, "Perform the requested operation on tables.", + &opt_do_tables, &opt_do_tables, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} }; -static const char *load_default_groups[] = { "mysqlcheck", "client", 0 }; +static const char *load_default_groups[]= +{ "mysqlcheck", "client", "client-server", "client-mariadb", 0 }; static void print_version(void); @@ -213,7 +232,7 @@ static int process_selected_tables(char *db, char **table_names, int tables); static int process_all_tables_in_db(char *database); static int process_one_db(char *database); static int use_db(char *database); -static int handle_request_for_tables(char *tables, size_t length); +static int handle_request_for_tables(char *tables, size_t length, my_bool view); static int dbConnect(char *host, char *user,char *passwd); static void dbDisconnect(char *host); static void DBerror(MYSQL *mysql, const char *when); @@ -233,27 +252,28 @@ static void print_version(void) static void usage(void) { + DBUG_ENTER("usage"); print_version(); puts(ORACLE_WELCOME_COPYRIGHT_NOTICE("2000")); puts("This program can be used to CHECK (-c, -m, -C), REPAIR (-r), ANALYZE (-a),"); puts("or OPTIMIZE (-o) tables. Some of the options (like -e or -q) can be"); puts("used at the same time. Not all options are supported by all storage engines."); - puts("Please consult the MySQL manual for latest information about the"); - puts("above. The options -c, -r, -a, and -o are exclusive to each other, which"); + puts("The options -c, -r, -a, and -o are exclusive to each other, which"); puts("means that the last option will be used, if several was specified.\n"); - puts("The option -c will be used by default, if none was specified. You"); - puts("can change the default behavior by making a symbolic link, or"); + puts("The option -c (--check) will be used by default, if none was specified."); + puts("You can change the default behavior by making a symbolic link, or"); puts("copying this file somewhere with another name, the alternatives are:"); puts("mysqlrepair: The default option will be -r"); puts("mysqlanalyze: The default option will be -a"); puts("mysqloptimize: The default option will be -o\n"); - printf("Usage: %s [OPTIONS] database [tables]\n", my_progname); - printf("OR %s [OPTIONS] --databases DB1 [DB2 DB3...]\n", - my_progname); - printf("OR %s [OPTIONS] --all-databases\n", my_progname); + puts("Please consult the MariaDB/MySQL knowledgebase at"); + puts("http://kb.askmonty.org/v/mysqlcheck for latest information about"); + puts("this program."); print_defaults("my", load_default_groups); + puts(""); my_print_help(my_long_options); my_print_variables(my_long_options); + DBUG_VOID_RETURN; } /* usage */ @@ -262,6 +282,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), char *argument) { int orig_what_to_do= what_to_do; + DBUG_ENTER("get_one_option"); switch(optid) { case 'a': @@ -286,11 +307,11 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), what_to_do = DO_OPTIMIZE; break; case OPT_FIX_DB_NAMES: - what_to_do= DO_UPGRADE; + what_to_do= DO_FIX_NAMES; opt_databases= 1; break; case OPT_FIX_TABLE_NAMES: - what_to_do= DO_UPGRADE; + what_to_do= DO_FIX_NAMES; break; case 'p': if (argument == disabled_my_option) @@ -331,9 +352,8 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), case 'v': verbose++; break; - case 'V': print_version(); exit(0); - case OPT_ENABLE_CLEARTEXT_PLUGIN: - using_opt_enable_cleartext_plugin= TRUE; + case 'V': + print_version(); exit(0); break; case OPT_MYSQL_PROTOCOL: opt_protocol= find_type_or_exit(argument, &sql_protocol_typelib, @@ -343,17 +363,18 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), if (orig_what_to_do && (what_to_do != orig_what_to_do)) { - fprintf(stderr, "Error: %s doesn't support multiple contradicting commands.\n", + fprintf(stderr, "Error: %s doesn't support multiple contradicting commands.\n", my_progname); - return 1; + DBUG_RETURN(1); } - return 0; + DBUG_RETURN(0); } static int get_options(int *argc, char ***argv) { int ho_error; + DBUG_ENTER("get_options"); if (*argc == 1) { @@ -361,10 +382,15 @@ static int get_options(int *argc, char ***argv) exit(0); } - if ((ho_error= load_defaults("my", load_default_groups, argc, argv)) || - (ho_error=handle_options(argc, argv, my_long_options, get_one_option))) + if ((ho_error=handle_options(argc, argv, my_long_options, get_one_option))) exit(ho_error); + if (what_to_do == DO_REPAIR && !opt_do_views && !opt_do_tables) + { + fprintf(stderr, "Error: Nothing to repair when both " + "--process-tables=NO and --process-views=NO\n"); + exit(1); + } if (!what_to_do) { size_t pnlen= strlen(my_progname); @@ -381,6 +407,13 @@ static int get_options(int *argc, char ***argv) what_to_do = DO_CHECK; } + if (opt_do_views && what_to_do != DO_REPAIR && what_to_do != DO_CHECK) + { + fprintf(stderr, "Error: %s doesn't support %s for views.\n", + my_progname, operation_name[what_to_do]); + exit(1); + } + /* If there's no --default-character-set option given with --fix-table-name or --fix-db-name set the default character set to "utf8". @@ -396,21 +429,21 @@ static int get_options(int *argc, char ***argv) !get_charset_by_csname(default_charset, MY_CS_PRIMARY, MYF(MY_WME))) { printf("Unsupported character set: %s\n", default_charset); - return 1; + DBUG_RETURN(1); } if (*argc > 0 && opt_alldbs) { printf("You should give only options, no arguments at all, with option\n"); printf("--all-databases. Please see %s --help for more information.\n", my_progname); - return 1; + DBUG_RETURN(1); } if (*argc < 1 && !opt_alldbs) { printf("You forgot to give the arguments! Please see %s --help\n", my_progname); printf("for more information.\n"); - return 1; + DBUG_RETURN(1); } if (tty_password) opt_password = get_tty_password(NullS); @@ -418,7 +451,7 @@ static int get_options(int *argc, char ***argv) my_end_arg= MY_CHECK_ERROR | MY_GIVE_INFO; if (debug_check_flag) my_end_arg= MY_CHECK_ERROR; - return(0); + DBUG_RETURN((0)); } /* get_options */ @@ -427,20 +460,24 @@ static int process_all_databases() MYSQL_ROW row; MYSQL_RES *tableres; int result = 0; + DBUG_ENTER("process_all_databases"); if (mysql_query(sock, "SHOW DATABASES") || !(tableres = mysql_store_result(sock))) { my_printf_error(0, "Error: Couldn't execute 'SHOW DATABASES': %s", MYF(0), mysql_error(sock)); - return 1; + DBUG_RETURN(1); } + if (verbose) + printf("Processing databases\n"); while ((row = mysql_fetch_row(tableres))) { if (process_one_db(row[0])) result = 1; } - return result; + mysql_free_result(tableres); + DBUG_RETURN(result); } /* process_all_databases */ @@ -448,20 +485,53 @@ static int process_all_databases() static int process_databases(char **db_names) { int result = 0; + DBUG_ENTER("process_databases"); + + if (verbose) + printf("Processing databases\n"); for ( ; *db_names ; db_names++) { if (process_one_db(*db_names)) result = 1; } - return result; + DBUG_RETURN(result); } /* process_databases */ +/* returns: -1 for error, 1 for view, 0 for table */ +static int is_view(const char *table) +{ + char query[1024]; + MYSQL_RES *res; + MYSQL_FIELD *field; + int view; + DBUG_ENTER("is_view"); + + my_snprintf(query, sizeof(query), "SHOW CREATE TABLE %`s", table); + if (mysql_query(sock, query)) + { + fprintf(stderr, "Failed to %s\n", query); + fprintf(stderr, "Error: %s\n", mysql_error(sock)); + DBUG_RETURN(-1); + } + res= mysql_store_result(sock); + field= mysql_fetch_field(res); + view= (strcmp(field->name,"View") == 0) ? 1 : 0; + mysql_free_result(res); + + DBUG_RETURN(view); +} + static int process_selected_tables(char *db, char **table_names, int tables) { + int view; + char *table; + uint table_len; + DBUG_ENTER("process_selected_tables"); + if (use_db(db)) - return 1; - if (opt_all_in_1 && what_to_do != DO_UPGRADE) + DBUG_RETURN(1); + if (opt_all_in_1 && what_to_do != DO_FIX_NAMES) { /* We need table list in form `a`, `b`, `c` @@ -472,12 +542,21 @@ static int process_selected_tables(char *db, char **table_names, int tables) size_t tot_length= 0; int i= 0; + if (opt_do_tables && opt_do_views) + { + fprintf(stderr, "Error: %s cannot process both tables and views " + "in one command (--process-tables=YES " + "--process-views=YES --all-in-1).\n", + my_progname); + DBUG_RETURN(1); + } + for (i = 0; i < tables; i++) tot_length+= fixed_name_length(*(table_names + i)) + 2; if (!(table_names_comma_sep = (char *) my_malloc((sizeof(char) * tot_length) + 4, MYF(MY_WME)))) - return 1; + DBUG_RETURN(1); for (end = table_names_comma_sep + 1; tables > 0; tables--, table_names++) @@ -486,13 +565,21 @@ static int process_selected_tables(char *db, char **table_names, int tables) *end++= ','; } *--end = 0; - handle_request_for_tables(table_names_comma_sep + 1, tot_length - 1); + handle_request_for_tables(table_names_comma_sep + 1, tot_length - 1, + opt_do_views != 0); my_free(table_names_comma_sep); } else for (; tables > 0; tables--, table_names++) - handle_request_for_tables(*table_names, fixed_name_length(*table_names)); - return 0; + { + table= *table_names; + table_len= fixed_name_length(*table_names); + view= is_view(table); + if (view < 0) + continue; + handle_request_for_tables(table, table_len, (view == 1)); + } + DBUG_RETURN(0); } /* process_selected_tables */ @@ -500,29 +587,25 @@ static size_t fixed_name_length(const char *name) { const char *p; size_t extra_length= 2; /* count the first/last backticks */ - + DBUG_ENTER("fixed_name_length"); + for (p= name; *p; p++) { if (*p == '`') extra_length++; - else if (*p == '.') - extra_length+= 2; } - return (size_t) ((p - name) + extra_length); + DBUG_RETURN((size_t) ((p - name) + extra_length)); } static char *fix_table_name(char *dest, char *src) { + DBUG_ENTER("fix_table_name"); + *dest++= '`'; for (; *src; src++) { switch (*src) { - case '.': /* add backticks around '.' */ - *dest++= '`'; - *dest++= '.'; - *dest++= '`'; - break; case '`': /* escape backtick character */ *dest++= '`'; /* fall through */ @@ -531,31 +614,37 @@ static char *fix_table_name(char *dest, char *src) } } *dest++= '`'; - return dest; + + DBUG_RETURN(dest); } static int process_all_tables_in_db(char *database) { - MYSQL_RES *res; + MYSQL_RES *UNINIT_VAR(res); MYSQL_ROW row; uint num_columns; + my_bool system_database= 0; + my_bool view= FALSE; + DBUG_ENTER("process_all_tables_in_db"); - LINT_INIT(res); if (use_db(database)) - return 1; + DBUG_RETURN(1); if ((mysql_query(sock, "SHOW /*!50002 FULL*/ TABLES") && mysql_query(sock, "SHOW TABLES")) || !(res= mysql_store_result(sock))) { my_printf_error(0, "Error: Couldn't get table list for database %s: %s", MYF(0), database, mysql_error(sock)); - return 1; + DBUG_RETURN(1); } + if (!strcmp(database, "mysql") || !strcmp(database, "MYSQL")) + system_database= 1; + num_columns= mysql_num_fields(res); - if (opt_all_in_1 && what_to_do != DO_UPGRADE) + if (opt_all_in_1 && what_to_do != DO_FIX_NAMES) { /* We need table list in form `a`, `b`, `c` @@ -566,41 +655,84 @@ static int process_all_tables_in_db(char *database) char *tables, *end; size_t tot_length = 0; + char *views, *views_end; + uint tot_views_length = 0; + while ((row = mysql_fetch_row(res))) - tot_length+= fixed_name_length(row[0]) + 2; + { + if ((num_columns == 2) && (strcmp(row[1], "VIEW") == 0) && + opt_do_views) + tot_views_length+= fixed_name_length(row[0]) + 2; + else if (opt_do_tables) + tot_length+= fixed_name_length(row[0]) + 2; + } mysql_data_seek(res, 0); if (!(tables=(char *) my_malloc(sizeof(char)*tot_length+4, MYF(MY_WME)))) { mysql_free_result(res); - return 1; + DBUG_RETURN(1); } - for (end = tables + 1; (row = mysql_fetch_row(res)) ;) + if (!(views=(char *) my_malloc(sizeof(char)*tot_views_length+4, MYF(MY_WME)))) { - if ((num_columns == 2) && (strcmp(row[1], "VIEW") == 0)) - continue; + my_free(tables); + mysql_free_result(res); + DBUG_RETURN(1); + } - end= fix_table_name(end, row[0]); - *end++= ','; + for (end = tables + 1, views_end= views + 1; (row = mysql_fetch_row(res)) ;) + { + if ((num_columns == 2) && (strcmp(row[1], "VIEW") == 0)) + { + if (!opt_do_views) + continue; + views_end= fix_table_name(views_end, row[0]); + *views_end++= ','; + } + else + { + if (!opt_do_tables) + continue; + end= fix_table_name(end, row[0]); + *end++= ','; + } } *--end = 0; + *--views_end = 0; if (tot_length) - handle_request_for_tables(tables + 1, tot_length - 1); + handle_request_for_tables(tables + 1, tot_length - 1, FALSE); + if (tot_views_length) + handle_request_for_tables(views + 1, tot_views_length - 1, TRUE); my_free(tables); + my_free(views); } else { while ((row = mysql_fetch_row(res))) { /* Skip views if we don't perform renaming. */ - if ((what_to_do != DO_UPGRADE) && (num_columns == 2) && (strcmp(row[1], "VIEW") == 0)) - continue; + if ((what_to_do != DO_FIX_NAMES) && (num_columns == 2) && (strcmp(row[1], "VIEW") == 0)) + { + if (!opt_do_views) + continue; + view= TRUE; + } + else + { + if (!opt_do_tables) + continue; + view= FALSE; + } + if (system_database && + (!strcmp(row[0], "general_log") || + !strcmp(row[0], "slow_log"))) + continue; /* Skip logging tables */ - handle_request_for_tables(row[0], fixed_name_length(row[0])); + handle_request_for_tables(row[0], fixed_name_length(row[0]), view); } } mysql_free_result(res); - return 0; + DBUG_RETURN(0); } /* process_all_tables_in_db */ @@ -620,39 +752,45 @@ static int fix_table_storage_name(const char *name) { char qbuf[100 + NAME_LEN*4]; int rc= 0; + DBUG_ENTER("fix_table_storage_name"); + if (strncmp(name, "#mysql50#", 9)) - return 1; + DBUG_RETURN(1); my_snprintf(qbuf, sizeof(qbuf), "RENAME TABLE `%s` TO `%s`", name, name + 9); rc= run_query(qbuf); if (verbose) printf("%-50s %s\n", name, rc ? "FAILED" : "OK"); - return rc; + DBUG_RETURN(rc); } static int fix_database_storage_name(const char *name) { char qbuf[100 + NAME_LEN*4]; int rc= 0; + DBUG_ENTER("fix_database_storage_name"); + if (strncmp(name, "#mysql50#", 9)) - return 1; + DBUG_RETURN(1); my_snprintf(qbuf, sizeof(qbuf), "ALTER DATABASE `%s` UPGRADE DATA DIRECTORY " "NAME", name); rc= run_query(qbuf); if (verbose) printf("%-50s %s\n", name, rc ? "FAILED" : "OK"); - return rc; + DBUG_RETURN(rc); } static int rebuild_table(char *name) { char *query, *ptr; int rc= 0; + DBUG_ENTER("rebuild_table"); + query= (char*)my_malloc(sizeof(char) * (12 + fixed_name_length(name) + 6 + 1), MYF(MY_WME)); if (!query) - return 1; + DBUG_RETURN(1); ptr= strmov(query, "ALTER TABLE "); ptr= fix_table_name(ptr, name); ptr= strxmov(ptr, " FORCE", NullS); @@ -663,12 +801,16 @@ static int rebuild_table(char *name) rc= 1; } my_free(query); - return rc; + DBUG_RETURN(rc); } static int process_one_db(char *database) { - if (what_to_do == DO_UPGRADE) + DBUG_ENTER("process_one_db"); + + if (verbose) + puts(database); + if (what_to_do == DO_FIX_NAMES) { int rc= 0; if (opt_fix_db_names && !strncmp(database,"#mysql50#", 9)) @@ -677,26 +819,28 @@ static int process_one_db(char *database) database+= 9; } if (rc || !opt_fix_table_names) - return rc; + DBUG_RETURN(rc); } - return process_all_tables_in_db(database); + DBUG_RETURN(process_all_tables_in_db(database)); } static int use_db(char *database) { + DBUG_ENTER("use_db"); + if (mysql_get_server_version(sock) >= FIRST_INFORMATION_SCHEMA_VERSION && !my_strcasecmp(&my_charset_latin1, database, INFORMATION_SCHEMA_DB_NAME)) - return 1; + DBUG_RETURN(1); if (mysql_get_server_version(sock) >= FIRST_PERFORMANCE_SCHEMA_VERSION && !my_strcasecmp(&my_charset_latin1, database, PERFORMANCE_SCHEMA_DB_NAME)) - return 1; + DBUG_RETURN(1); if (mysql_select_db(sock, database)) { DBerror(sock, "when selecting the database"); - return 1; + DBUG_RETURN(1); } - return 0; + DBUG_RETURN(0); } /* use_db */ static int disable_binlog() @@ -705,69 +849,105 @@ static int disable_binlog() return run_query(stmt); } -static int handle_request_for_tables(char *tables, size_t length) +static int handle_request_for_tables(char *tables, size_t length, my_bool view) { char *query, *end, options[100], message[100]; + char table_name_buff[NAME_CHAR_LEN*2*2+1], *table_name; size_t query_length= 0, query_size= sizeof(char)*(length+110); const char *op = 0; + const char *tab_view; + DBUG_ENTER("handle_request_for_tables"); options[0] = 0; + tab_view= view ? " VIEW " : " TABLE "; end = options; switch (what_to_do) { case DO_CHECK: op = "CHECK"; - if (opt_quick) end = strmov(end, " QUICK"); - if (opt_fast) end = strmov(end, " FAST"); - if (opt_medium_check) end = strmov(end, " MEDIUM"); /* Default */ - if (opt_extended) end = strmov(end, " EXTENDED"); - if (opt_check_only_changed) end = strmov(end, " CHANGED"); + if (view) + { + if (opt_fast || opt_check_only_changed) + DBUG_RETURN(0); + } + else + { + if (opt_quick) end = strmov(end, " QUICK"); + if (opt_fast) end = strmov(end, " FAST"); + if (opt_extended) end = strmov(end, " EXTENDED"); + if (opt_medium_check) end = strmov(end, " MEDIUM"); /* Default */ + if (opt_check_only_changed) end = strmov(end, " CHANGED"); + } if (opt_upgrade) end = strmov(end, " FOR UPGRADE"); break; case DO_REPAIR: - op= (opt_write_binlog) ? "REPAIR" : "REPAIR NO_WRITE_TO_BINLOG"; - if (opt_quick) end = strmov(end, " QUICK"); - if (opt_extended) end = strmov(end, " EXTENDED"); - if (opt_frm) end = strmov(end, " USE_FRM"); + op= opt_write_binlog ? "REPAIR" : "REPAIR NO_WRITE_TO_BINLOG"; + if (view) + { + if (opt_do_views == DO_VIEWS_FROM_MYSQL) end = strmov(end, " FROM MYSQL"); + } + else + { + if (opt_quick) end = strmov(end, " QUICK"); + if (opt_extended) end = strmov(end, " EXTENDED"); + if (opt_frm) end = strmov(end, " USE_FRM"); + } break; case DO_ANALYZE: + DBUG_ASSERT(!view); op= (opt_write_binlog) ? "ANALYZE" : "ANALYZE NO_WRITE_TO_BINLOG"; break; case DO_OPTIMIZE: + DBUG_ASSERT(!view); op= (opt_write_binlog) ? "OPTIMIZE" : "OPTIMIZE NO_WRITE_TO_BINLOG"; break; - case DO_UPGRADE: - return fix_table_storage_name(tables); + case DO_FIX_NAMES: + DBUG_ASSERT(!view); + DBUG_RETURN(fix_table_storage_name(tables)); } if (!(query =(char *) my_malloc(query_size, MYF(MY_WME)))) - { - return 1; - } + DBUG_RETURN(1); if (opt_all_in_1) { DBUG_ASSERT(strlen(op)+strlen(tables)+strlen(options)+8+1 <= query_size); /* No backticks here as we added them before */ - query_length= sprintf(query, "%s TABLE %s %s", op, tables, options); + query_length= sprintf(query, "%s%s%s %s", op, + tab_view, tables, options); + table_name= tables; } else { - char *ptr; + char *ptr, *org; - ptr= strmov(strmov(query, op), " TABLE "); + org= ptr= strmov(strmov(query, op), tab_view); ptr= fix_table_name(ptr, tables); + strmake(table_name_buff, org, min((int) sizeof(table_name_buff)-1, + (int) (ptr - org))); + table_name= table_name_buff; ptr= strxmov(ptr, " ", options, NullS); query_length= (size_t) (ptr - query); } if (mysql_real_query(sock, query, query_length)) { - sprintf(message, "when executing '%s TABLE ... %s'", op, options); + sprintf(message, "when executing '%s%s... %s'", op, tab_view, options); DBerror(sock, message); - return 1; + my_free(query); + DBUG_RETURN(1); } print_result(); + if (opt_flush_tables) + { + query_length= sprintf(query, "FLUSH TABLES %s", table_name); + if (mysql_real_query(sock, query, query_length)) + { + DBerror(sock, query); + my_free(query); + DBUG_RETURN(1); + } + } my_free(query); - return 0; + DBUG_RETURN(0); } @@ -775,12 +955,18 @@ static void print_result() { MYSQL_RES *res; MYSQL_ROW row; - char prev[NAME_LEN*2+2]; + char prev[(NAME_LEN+9)*3+2]; char prev_alter[MAX_ALTER_STR_SIZE]; + char *db_name; + uint length_of_db; uint i; my_bool found_error=0, table_rebuild=0; + DYNAMIC_ARRAY *array4repair= &tables4repair; + DBUG_ENTER("print_result"); res = mysql_use_result(sock); + db_name= sock->db; + length_of_db= strlen(db_name); prev[0] = '\0'; prev_alter[0]= 0; @@ -804,11 +990,18 @@ static void print_result() if (prev_alter[0]) insert_dynamic(&alter_table_cmds, (uchar*) prev_alter); else - insert_dynamic(&tables4rebuild, (uchar*) prev); + { + char *table_name= prev + (length_of_db+1); + insert_dynamic(&tables4rebuild, (uchar*) table_name); + } } else - insert_dynamic(&tables4repair, (uchar*) prev); + { + char *table_name= prev + (length_of_db+1); + insert_dynamic(array4repair, (uchar*) table_name); + } } + array4repair= &tables4repair; found_error=0; table_rebuild=0; prev_alter[0]= 0; @@ -819,7 +1012,18 @@ static void print_result() printf("%-50s %s", row[0], row[3]); else if (!status && changed) { - printf("%s\n%-9s: %s", row[0], row[2], row[3]); + /* + If the error message includes REPAIR TABLE, we assume it means + we have to run upgrade on it. In this case we write a nicer message + than "Please do "REPAIR TABLE""... + */ + if (!strcmp(row[2],"error") && strstr(row[3],"REPAIR ")) + { + printf("%-50s %s", row[0], "Needs upgrade"); + array4repair= strstr(row[3], "VIEW") ? &views4repair : &tables4repair; + } + else + printf("%s\n%-9s: %s", row[0], row[2], row[3]); if (opt_auto_repair && strcmp(row[2],"note")) { const char *alter_txt= strstr(row[3], "ALTER TABLE"); @@ -862,19 +1066,26 @@ static void print_result() if (prev_alter[0]) insert_dynamic(&alter_table_cmds, (uchar*) prev_alter); else - insert_dynamic(&tables4rebuild, (uchar*) prev); + { + char *table_name= prev + (length_of_db+1); + insert_dynamic(&tables4rebuild, (uchar*) table_name); + } } else - insert_dynamic(&tables4repair, (uchar*) prev); + { + char *table_name= prev + (length_of_db+1); + insert_dynamic(array4repair, (uchar*) table_name); + } } mysql_free_result(res); + DBUG_VOID_RETURN; } static int dbConnect(char *host, char *user, char *passwd) { DBUG_ENTER("dbConnect"); - if (verbose) + if (verbose > 1) { fprintf(stderr, "# Connecting to %s...\n", host ? host : "localhost"); } @@ -888,10 +1099,8 @@ static int dbConnect(char *host, char *user, char *passwd) #endif if (opt_protocol) mysql_options(&mysql_connection,MYSQL_OPT_PROTOCOL,(char*)&opt_protocol); -#ifdef HAVE_SMEM if (shared_memory_base_name) mysql_options(&mysql_connection,MYSQL_SHARED_MEMORY_BASE_NAME,shared_memory_base_name); -#endif if (opt_plugin_dir && *opt_plugin_dir) mysql_options(&mysql_connection, MYSQL_PLUGIN_DIR, opt_plugin_dir); @@ -899,29 +1108,25 @@ static int dbConnect(char *host, char *user, char *passwd) if (opt_default_auth && *opt_default_auth) mysql_options(&mysql_connection, MYSQL_DEFAULT_AUTH, opt_default_auth); - if (using_opt_enable_cleartext_plugin) - mysql_options(&mysql_connection, MYSQL_ENABLE_CLEARTEXT_PLUGIN, - (char *) &opt_enable_cleartext_plugin); - mysql_options(&mysql_connection, MYSQL_SET_CHARSET_NAME, default_charset); - if (!(sock = mysql_connect_ssl_check(&mysql_connection, host, user, passwd, - NULL, opt_mysql_port, - opt_mysql_unix_port, 0, - opt_ssl_required))) + if (!(sock = mysql_real_connect(&mysql_connection, host, user, passwd, + NULL, opt_mysql_port, opt_mysql_unix_port, 0))) { DBerror(&mysql_connection, "when trying to connect"); - return 1; + DBUG_RETURN(1); } mysql_connection.reconnect= 1; - return 0; + DBUG_RETURN(0); } /* dbConnect */ static void dbDisconnect(char *host) { - if (verbose) + DBUG_ENTER("dbDisconnect"); + if (verbose > 1) fprintf(stderr, "# Disconnecting from %s...\n", host ? host : "localhost"); mysql_close(sock); + DBUG_VOID_RETURN; } /* dbDisconnect */ @@ -937,46 +1142,54 @@ static void DBerror(MYSQL *mysql, const char *when) static void safe_exit(int error) { + DBUG_ENTER("safe_exit"); if (!first_error) first_error= error; if (ignore_errors) - return; + DBUG_VOID_RETURN; if (sock) mysql_close(sock); + sf_leaking_memory= 1; /* don't check for memory leaks */ exit(error); + DBUG_VOID_RETURN; } int main(int argc, char **argv) { + int ret= EX_USAGE; + char **defaults_argv; + MY_INIT(argv[0]); + sf_leaking_memory=1; /* don't report memory leaks on early exits */ /* ** Check out the args */ + if (load_defaults("my", load_default_groups, &argc, &argv)) + goto end2; + + defaults_argv= argv; if (get_options(&argc, &argv)) - { - my_end(my_end_arg); - exit(EX_USAGE); - } + goto end1; + sf_leaking_memory=0; /* from now on we cleanup properly */ + + ret= EX_MYSQLERR; if (dbConnect(current_host, current_user, opt_password)) - exit(EX_MYSQLERR); + goto end1; + ret= 1; if (!opt_write_binlog) { - if (disable_binlog()) { - first_error= 1; + if (disable_binlog()) goto end; - } } if (opt_auto_repair && (my_init_dynamic_array(&tables4repair, sizeof(char)*(NAME_LEN*2+2),16,64) || + my_init_dynamic_array(&views4repair, sizeof(char)*(NAME_LEN*2+2),16,64) || my_init_dynamic_array(&tables4rebuild, sizeof(char)*(NAME_LEN*2+2),16,64) || my_init_dynamic_array(&alter_table_cmds, MAX_ALTER_STR_SIZE, 0, 1))) - { - first_error = 1; goto end; - } if (opt_alldbs) process_all_databases(); @@ -996,24 +1209,37 @@ int main(int argc, char **argv) for (i = 0; i < tables4repair.elements ; i++) { char *name= (char*) dynamic_array_ptr(&tables4repair, i); - handle_request_for_tables(name, fixed_name_length(name)); + handle_request_for_tables(name, fixed_name_length(name), FALSE); } for (i = 0; i < tables4rebuild.elements ; i++) rebuild_table((char*) dynamic_array_ptr(&tables4rebuild, i)); for (i = 0; i < alter_table_cmds.elements ; i++) run_query((char*) dynamic_array_ptr(&alter_table_cmds, i)); + if (!opt_silent && views4repair.elements) + puts("\nRepairing views"); + for (i = 0; i < views4repair.elements ; i++) + { + char *name= (char*) dynamic_array_ptr(&views4repair, i); + handle_request_for_tables(name, fixed_name_length(name), TRUE); + } } + ret= test(first_error); + end: dbDisconnect(current_host); if (opt_auto_repair) { + delete_dynamic(&views4repair); delete_dynamic(&tables4repair); delete_dynamic(&tables4rebuild); + delete_dynamic(&alter_table_cmds); } + end1: my_free(opt_password); -#ifdef HAVE_SMEM my_free(shared_memory_base_name); -#endif + mysql_library_end(); + free_defaults(defaults_argv); + end2: my_end(my_end_arg); - return(first_error!=0); + return ret; } /* main */ diff --git a/client/mysqldump.c b/client/mysqldump.c index 00265def489..16b39b77cf1 100644 --- a/client/mysqldump.c +++ b/client/mysqldump.c @@ -1,5 +1,6 @@ /* - Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2000, 2013, Oracle and/or its affiliates. + Copyright (c) 2010, 2016, MariaDB 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 @@ -38,7 +39,7 @@ ** 10 Jun 2003: SET NAMES and --no-set-names by Alexander Barkov */ -#define DUMP_VERSION "10.13" +#define DUMP_VERSION "10.14" #include <my_global.h> #include <my_sys.h> @@ -83,6 +84,8 @@ #define IGNORE_DATA 0x01 /* don't dump data for this table */ #define IGNORE_INSERT_DELAYED 0x02 /* table doesn't support INSERT DELAYED */ +/* Chars needed to store LONGLONG, excluding trailing '\0'. */ +#define LONGLONG_LEN 20 static void add_load_option(DYNAMIC_STRING *str, const char *option, const char *option_value); @@ -129,8 +132,6 @@ static ulong opt_compatible_mode= 0; #define MYSQL_OPT_MASTER_DATA_COMMENTED_SQL 2 #define MYSQL_OPT_SLAVE_DATA_EFFECTIVE_SQL 1 #define MYSQL_OPT_SLAVE_DATA_COMMENTED_SQL 2 -static uint opt_enable_cleartext_plugin= 0; -static my_bool using_opt_enable_cleartext_plugin= 0; static uint opt_mysql_port= 0, opt_master_data; static uint opt_slave_data; static uint my_end_arg; @@ -159,6 +160,8 @@ static void dynstr_set_checked(DYNAMIC_STRING *str, const char *init_str); static void dynstr_append_mem_checked(DYNAMIC_STRING *str, const char *append, uint length); static void dynstr_realloc_checked(DYNAMIC_STRING *str, ulong additional_size); + +static int do_start_slave_sql(MYSQL *mysql_con); /* Constant for detection of default value of default_charset. If default_charset is equal to mysql_universal_client_charset, then @@ -223,8 +226,8 @@ static struct my_option my_long_options[] = &opt_slave_apply, &opt_slave_apply, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"character-sets-dir", OPT_CHARSETS_DIR, - "Directory for character set files.", &charsets_dir, - &charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + "Directory for character set files.", (char**) &charsets_dir, + (char**) &charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"comments", 'i', "Write additional information.", &opt_comments, &opt_comments, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, @@ -261,8 +264,8 @@ static struct my_option my_long_options[] = {"debug", '#', "This is a non-debug version. Catch this and exit.", 0,0, 0, GET_DISABLED, OPT_ARG, 0, 0, 0, 0, 0, 0}, #else - {"debug", '#', "Output debug log.", &default_dbug_option, - &default_dbug_option, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, + {"debug", '#', "Output debug log.", (char**) &default_dbug_option, + (char**) &default_dbug_option, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, #endif {"debug-check", OPT_DEBUG_CHECK, "Check memory and open file usage at exit.", &debug_check_flag, &debug_check_flag, 0, @@ -376,9 +379,9 @@ static struct my_option my_long_options[] = "This causes the binary log position and filename to be appended to the " "output. If equal to 1, will print it as a CHANGE MASTER command; if equal" " to 2, that command will be prefixed with a comment symbol. " - "This option will turn --lock-all-tables on, unless " - "--single-transaction is specified too (in which case a " - "global read lock is only taken a short time at the beginning of the dump; " + "This option will turn --lock-all-tables on, unless --single-transaction " + "is specified too (on servers before MariaDB 5.3 this will still take a " + "global read lock for a short time at the beginning of the dump; " "don't forget to read about --single-transaction below). In all cases, " "any action on logs will happen at the exact moment of the dump. " "Option automatically turns --lock-tables off.", @@ -515,14 +518,11 @@ static struct my_option my_long_options[] = "Default authentication client-side plugin to use.", &opt_default_auth, &opt_default_auth, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"enable_cleartext_plugin", OPT_ENABLE_CLEARTEXT_PLUGIN, - "Enable/disable the clear text authentication plugin.", - &opt_enable_cleartext_plugin, &opt_enable_cleartext_plugin, - 0, GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} }; -static const char *load_default_groups[]= { "mysqldump","client",0 }; +static const char *load_default_groups[]= +{ "mysqldump", "client", "client-server", "client-mariadb", 0 }; static void maybe_exit(int error); static void die(int error, const char* reason, ...); @@ -593,17 +593,17 @@ void check_io(FILE *file) static void print_version(void) { - printf("%s Ver %s Distrib %s, for %s (%s)\n",my_progname,DUMP_VERSION, + printf("%s Ver %s Distrib %s, for %s (%s)\n",my_progname_short,DUMP_VERSION, MYSQL_SERVER_VERSION,SYSTEM_TYPE,MACHINE_TYPE); } /* print_version */ -static void short_usage_sub(void) +static void short_usage_sub(FILE *f) { - printf("Usage: %s [OPTIONS] database [tables]\n", my_progname); - printf("OR %s [OPTIONS] --databases [OPTIONS] DB1 [DB2 DB3...]\n", - my_progname); - printf("OR %s [OPTIONS] --all-databases [OPTIONS]\n", my_progname); + fprintf(f, "Usage: %s [OPTIONS] database [tables]\n", my_progname_short); + fprintf(f, "OR %s [OPTIONS] --databases [OPTIONS] DB1 [DB2 DB3...]\n", + my_progname_short); + fprintf(f, "OR %s [OPTIONS] --all-databases [OPTIONS]\n", my_progname_short); } @@ -612,17 +612,18 @@ static void usage(void) print_version(); puts(ORACLE_WELCOME_COPYRIGHT_NOTICE("2000")); puts("Dumping structure and contents of MySQL databases and tables."); - short_usage_sub(); + short_usage_sub(stdout); print_defaults("my",load_default_groups); + puts(""); my_print_help(my_long_options); my_print_variables(my_long_options); } /* usage */ -static void short_usage(void) +static void short_usage(FILE *f) { - short_usage_sub(); - printf("For more options, use %s --help\n", my_progname); + short_usage_sub(f); + fprintf(f, "For more options, use %s --help\n", my_progname_short); } @@ -671,8 +672,13 @@ static void write_header(FILE *sql_file, char *db_name) if (!path) { + if (!opt_no_create_info) + { + /* We don't need unique checks as the table is created just before */ + fprintf(md_result_file,"\ +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;\n"); + } fprintf(md_result_file,"\ -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;\n\ /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;\n\ "); } @@ -702,8 +708,12 @@ static void write_footer(FILE *sql_file) if (!path) { fprintf(md_result_file,"\ -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;\n\ +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;\n"); + if (!opt_no_create_info) + { + fprintf(md_result_file,"\ /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;\n"); + } } if (opt_set_charset) fprintf(sql_file, @@ -889,9 +899,6 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), default_charset= (char*) MYSQL_DEFAULT_CHARSET_NAME; break; } - case (int) OPT_ENABLE_CLEARTEXT_PLUGIN: - using_opt_enable_cleartext_plugin= TRUE; - break; case (int) OPT_MYSQL_PROTOCOL: opt_protocol= find_type_or_exit(argument, &sql_protocol_typelib, opt->name); @@ -943,7 +950,7 @@ static int get_options(int *argc, char ***argv) fields_terminated)) { fprintf(stderr, - "%s: You must use option --tab with --fields-...\n", my_progname); + "%s: You must use option --tab with --fields-...\n", my_progname_short); return(EX_USAGE); } @@ -961,7 +968,7 @@ static int get_options(int *argc, char ***argv) if (opt_single_transaction && opt_lock_all_tables) { fprintf(stderr, "%s: You can't use --single-transaction and " - "--lock-all-tables at the same time.\n", my_progname); + "--lock-all-tables at the same time.\n", my_progname_short); return(EX_USAGE); } if (opt_master_data) @@ -973,14 +980,14 @@ static int get_options(int *argc, char ***argv) lock_tables= 0; if (enclosed && opt_enclosed) { - fprintf(stderr, "%s: You can't use ..enclosed.. and ..optionally-enclosed.. at the same time.\n", my_progname); + fprintf(stderr, "%s: You can't use ..enclosed.. and ..optionally-enclosed.. at the same time.\n", my_progname_short); return(EX_USAGE); } if ((opt_databases || opt_alldbs) && path) { fprintf(stderr, "%s: --databases or --all-databases can't be used with --tab.\n", - my_progname); + my_progname_short); return(EX_USAGE); } if (strcmp(default_charset, charset_info->csname) && @@ -989,7 +996,7 @@ static int get_options(int *argc, char ***argv) exit(1); if ((*argc < 1 && !opt_alldbs) || (*argc > 0 && opt_alldbs)) { - short_usage(); + short_usage(stderr); return EX_USAGE; } if (tty_password) @@ -1004,7 +1011,7 @@ static int get_options(int *argc, char ***argv) static void DB_error(MYSQL *mysql_arg, const char *when) { DBUG_ENTER("DB_error"); - maybe_die(EX_MYSQLERR, "Got error: %d: %s %s", + maybe_die(EX_MYSQLERR, "Got error: %d: \"%s\" %s", mysql_errno(mysql_arg), mysql_error(mysql_arg), when); DBUG_VOID_RETURN; } @@ -1032,7 +1039,7 @@ static void die(int error_num, const char* fmt_reason, ...) my_vsnprintf(buffer, sizeof(buffer), fmt_reason, args); va_end(args); - fprintf(stderr, "%s: %s\n", my_progname, buffer); + fprintf(stderr, "%s: %s\n", my_progname_short, buffer); fflush(stderr); ignore_errors= 0; /* force the exit */ @@ -1066,7 +1073,7 @@ static void maybe_die(int error_num, const char* fmt_reason, ...) my_vsnprintf(buffer, sizeof(buffer), fmt_reason, args); va_end(args); - fprintf(stderr, "%s: %s\n", my_progname, buffer); + fprintf(stderr, "%s: %s\n", my_progname_short, buffer); fflush(stderr); maybe_exit(error_num); @@ -1109,16 +1116,14 @@ static int fetch_db_collation(const char *db_name, int db_cl_size) { my_bool err_status= FALSE; - char query[QUERY_LENGTH]; MYSQL_RES *db_cl_res; MYSQL_ROW db_cl_row; - char quoted_database_buf[NAME_LEN*2+3]; - char *qdatabase= quote_name(db_name, quoted_database_buf, 1); - my_snprintf(query, sizeof (query), "use %s", qdatabase); - - if (mysql_query_with_error_report(mysql, NULL, query)) - return 1; + if (mysql_select_db(mysql, db_name)) + { + DB_error(mysql, "when selecting the database"); + return 1; /* If --force */ + } if (mysql_query_with_error_report(mysql, &db_cl_res, "select @@collation_database")) @@ -1149,6 +1154,44 @@ static int fetch_db_collation(const char *db_name, } +/* + Check if server supports non-blocking binlog position using the + binlog_snapshot_file and binlog_snapshot_position status variables. If it + does, also return the position obtained if output pointers are non-NULL. + Returns 1 if position available, 0 if not. +*/ +static int +check_consistent_binlog_pos(char *binlog_pos_file, char *binlog_pos_offset) +{ + MYSQL_RES *res; + MYSQL_ROW row; + int found; + + if (mysql_query_with_error_report(mysql, &res, + "SHOW STATUS LIKE 'binlog_snapshot_%'")) + return 0; + + found= 0; + while ((row= mysql_fetch_row(res))) + { + if (0 == strcmp(row[0], "Binlog_snapshot_file")) + { + if (binlog_pos_file) + strmake(binlog_pos_file, row[1], FN_REFLEN-1); + found++; + } + else if (0 == strcmp(row[0], "Binlog_snapshot_position")) + { + if (binlog_pos_offset) + strmake(binlog_pos_offset, row[1], LONGLONG_LEN); + found++; + } + } + mysql_free_result(res); + + return (found == 2); +} + static char *my_case_str(const char *str, size_t str_len, const char *token, @@ -1435,6 +1478,7 @@ static void free_resources() if (md_result_file && md_result_file != stdout) my_fclose(md_result_file, MYF(0)); my_free(opt_password); + my_free(current_host); if (my_hash_inited(&ignore_table)) my_hash_free(&ignore_table); if (extended_insert) @@ -1443,6 +1487,7 @@ static void free_resources() dynstr_free(&insert_pat); if (defaults_argv) free_defaults(defaults_argv); + mysql_library_end(); my_end(my_end_arg); } @@ -1453,6 +1498,9 @@ static void maybe_exit(int error) first_error= error; if (ignore_errors) return; + ignore_errors= 1; /* don't want to recurse, if something fails below */ + if (opt_slave_data) + do_start_slave_sql(mysql); if (mysql) mysql_close(mysql); free_resources(); @@ -1494,14 +1542,9 @@ static int connect_to_db(char *host, char *user,char *passwd) if (opt_default_auth && *opt_default_auth) mysql_options(&mysql_connection, MYSQL_DEFAULT_AUTH, opt_default_auth); - if (using_opt_enable_cleartext_plugin) - mysql_options(&mysql_connection, MYSQL_ENABLE_CLEARTEXT_PLUGIN, - (char *) &opt_enable_cleartext_plugin); - - if (!(mysql= mysql_connect_ssl_check(&mysql_connection, host, user, - passwd, NULL, opt_mysql_port, - opt_mysql_unix_port, 0, - opt_ssl_required))) + mysql= &mysql_connection; /* So we can mysql_close() it properly */ + if (!mysql_real_connect(&mysql_connection,host,user,passwd, + NULL,opt_mysql_port,opt_mysql_unix_port, 0)) { DB_error(&mysql_connection, "when trying to connect"); DBUG_RETURN(1); @@ -1866,7 +1909,7 @@ static void print_xml_row(FILE *xml_file, const char *row_name, const char *str_create) { uint i; - my_bool body_found= 0; + my_bool body_found __attribute__((unused)) = 0; char *create_stmt_ptr= NULL; ulong create_stmt_len= 0; MYSQL_FIELD *field; @@ -1884,7 +1927,9 @@ static void print_xml_row(FILE *xml_file, const char *row_name, { create_stmt_ptr= (*row)[i]; create_stmt_len= lengths[i]; +#ifndef DBUG_OFF body_found= 1; +#endif } else { @@ -2113,7 +2158,7 @@ static uint dump_events_for_db(char *db) if (create_delimiter(row[3], delimiter, sizeof(delimiter)) == NULL) { fprintf(stderr, "%s: Warning: Can't create delimiter for event '%s'\n", - my_progname, event_name); + my_progname_short, event_name); DBUG_RETURN(1); } @@ -2164,11 +2209,7 @@ static uint dump_events_for_db(char *db) (const char *) (query_str != NULL ? query_str : row[3]), (const char *) delimiter); - if(query_str) - { - my_free(query_str); - query_str= NULL; - } + my_free(query_str); restore_time_zone(sql_file, delimiter); restore_sql_mode(sql_file, delimiter); @@ -2277,7 +2318,7 @@ static uint dump_routines_for_db(char *db) /* Get database collation. */ - if (fetch_db_collation(db_name_buff, db_cl_name, sizeof (db_cl_name))) + if (fetch_db_collation(db, db_cl_name, sizeof (db_cl_name))) DBUG_RETURN(1); if (switch_character_set_results(mysql, "binary")) @@ -2346,7 +2387,7 @@ static uint dump_routines_for_db(char *db) if (mysql_num_fields(routine_res) >= 6) { - if (switch_db_collation(sql_file, db_name_buff, ";", + if (switch_db_collation(sql_file, db, ";", db_cl_name, row[5], &db_cl_altered)) { DBUG_RETURN(1); @@ -2393,7 +2434,7 @@ static uint dump_routines_for_db(char *db) if (db_cl_altered) { - if (restore_db_collation(sql_file, db_name_buff, ";", db_cl_name)) + if (restore_db_collation(sql_file, db, ";", db_cl_name)) DBUG_RETURN(1); } } @@ -2766,7 +2807,7 @@ static uint get_table_structure(char *table, char *db, char *table_type, else { verbose_msg("%s: Warning: Can't set SQL_QUOTE_SHOW_CREATE option (%s)\n", - my_progname, mysql_error(mysql)); + my_progname_short, mysql_error(mysql)); my_snprintf(query_buff, sizeof(query_buff), show_fields_stmt, db, table); @@ -2877,7 +2918,7 @@ static uint get_table_structure(char *table, char *db, char *table_type, goto continue_xml; } fprintf(stderr, "%s: Can't get keys for table %s (%s)\n", - my_progname, result_table, mysql_error(mysql)); + my_progname_short, result_table, mysql_error(mysql)); if (path) my_fclose(sql_file, MYF(MY_WME)); DBUG_RETURN(0); @@ -3413,8 +3454,7 @@ static void dump_table(char *table, char *db) if (!opt_events && !my_strcasecmp(&my_charset_latin1, db, "mysql") && !my_strcasecmp(&my_charset_latin1, table, "event")) { - fprintf(stderr, "-- Warning: Skipping the data of table mysql.event." - " Specify the --events option explicitly.\n"); + verbose_msg("-- Skipping data table mysql.event, --skip-events was used\n"); DBUG_VOID_RETURN; } @@ -3534,7 +3574,7 @@ static void dump_table(char *table, char *db) if (mysql_num_fields(res) != num_fields) { fprintf(stderr,"%s: Error in field count for table: %s ! Aborting.\n", - my_progname, result_table); + my_progname_short, result_table); error= EX_CONSCHECK; goto err; } @@ -3606,7 +3646,8 @@ static void dump_table(char *table, char *db) field->type == MYSQL_TYPE_BLOB || field->type == MYSQL_TYPE_LONG_BLOB || field->type == MYSQL_TYPE_MEDIUM_BLOB || - field->type == MYSQL_TYPE_TINY_BLOB)) ? 1 : 0; + field->type == MYSQL_TYPE_TINY_BLOB || + field->type == MYSQL_TYPE_GEOMETRY)) ? 1 : 0; if (extended_insert && !opt_xml) { if (i == 0) @@ -3799,7 +3840,7 @@ static void dump_table(char *table, char *db) { my_snprintf(buf, sizeof(buf), "%s: Error %d: %s when dumping table %s at row: %ld\n", - my_progname, + my_progname_short, mysql_errno(mysql), mysql_error(mysql), result_table, @@ -3950,7 +3991,13 @@ static int dump_tablespaces(char* ts_where) char *ubs; char *endsemi; DBUG_ENTER("dump_tablespaces"); - + + /* + Try to turn off semi-join optimization (if that fails, this is a + pre-optimizer_switch server, and the old query plan is ok for us. + */ + mysql_query(mysql, "set optimizer_switch='semijoin=off'"); + init_dynamic_string_checked(&sqlbuf, "SELECT LOGFILE_GROUP_NAME," " FILE_NAME," @@ -3993,8 +4040,8 @@ static int dump_tablespaces(char* ts_where) DBUG_RETURN(0); } - my_printf_error(0, "Error: '%s' when trying to dump tablespaces", - MYF(0), mysql_error(mysql)); + fprintf(stderr, "%s: Error: '%s' when trying to dump tablespaces\n", + my_progname_short, mysql_error(mysql)); DBUG_RETURN(1); } @@ -4110,6 +4157,8 @@ static int dump_tablespaces(char* ts_where) mysql_free_result(tableres); dynstr_free(&sqlbuf); + mysql_query(mysql, "set optimizer_switch=default"); + DBUG_RETURN(0); } @@ -4134,13 +4183,14 @@ static int dump_all_databases() if (dump_all_tables_in_db(row[0])) result=1; } + mysql_free_result(tableres); if (seen_views) { if (mysql_query(mysql, "SHOW DATABASES") || !(tableres= mysql_store_result(mysql))) { - my_printf_error(0, "Error: Couldn't execute 'SHOW DATABASES': %s", - MYF(0), mysql_error(mysql)); + fprintf(stderr, "%s: Error: Couldn't execute 'SHOW DATABASES': %s\n", + my_progname_short, mysql_error(mysql)); return 1; } while ((row= mysql_fetch_row(tableres))) @@ -4156,6 +4206,7 @@ static int dump_all_databases() if (dump_all_views_in_db(row[0])) result=1; } + mysql_free_result(tableres); } return result; } @@ -4284,8 +4335,6 @@ static int init_dumping(char *database, int init_func(char*)) check_io(md_result_file); } } - if (extended_insert) - init_dynamic_string_checked(&extended_row, "", 1024, 1024); return 0; } /* init_dumping */ @@ -4673,41 +4722,64 @@ static int dump_selected_tables(char *db, char **table_names, int tables) } /* dump_selected_tables */ -static int do_show_master_status(MYSQL *mysql_con) +static int do_show_master_status(MYSQL *mysql_con, int consistent_binlog_pos) { MYSQL_ROW row; - MYSQL_RES *master; + MYSQL_RES *UNINIT_VAR(master); + char binlog_pos_file[FN_REFLEN]; + char binlog_pos_offset[LONGLONG_LEN+1]; + char *file, *offset; const char *comment_prefix= (opt_master_data == MYSQL_OPT_MASTER_DATA_COMMENTED_SQL) ? "-- " : ""; - if (mysql_query_with_error_report(mysql_con, &master, "SHOW MASTER STATUS")) + + if (consistent_binlog_pos) { - return 1; + if(!check_consistent_binlog_pos(binlog_pos_file, binlog_pos_offset)) + return 1; + file= binlog_pos_file; + offset= binlog_pos_offset; } else { + if (mysql_query_with_error_report(mysql_con, &master, "SHOW MASTER STATUS")) + return 1; + row= mysql_fetch_row(master); if (row && row[0] && row[1]) { - /* SHOW MASTER STATUS reports file and position */ - print_comment(md_result_file, 0, - "\n--\n-- Position to start replication or point-in-time " - "recovery from\n--\n\n"); - fprintf(md_result_file, - "%sCHANGE MASTER TO MASTER_LOG_FILE='%s', MASTER_LOG_POS=%s;\n", - comment_prefix, row[0], row[1]); - check_io(md_result_file); + file= row[0]; + offset= row[1]; } - else if (!ignore_errors) + else { - /* SHOW MASTER STATUS reports nothing and --force is not enabled */ - my_printf_error(0, "Error: Binlogging on server not active", - MYF(0)); mysql_free_result(master); - maybe_exit(EX_MYSQLERR); - return 1; + if (!ignore_errors) + { + /* SHOW MASTER STATUS reports nothing and --force is not enabled */ + fprintf(stderr, "%s: Error: Binlogging on server not active\n", + my_progname_short); + maybe_exit(EX_MYSQLERR); + return 1; + } + else + { + return 0; + } } - mysql_free_result(master); } + + /* SHOW MASTER STATUS reports file and position */ + print_comment(md_result_file, 0, + "\n--\n-- Position to start replication or point-in-time " + "recovery from\n--\n\n"); + fprintf(md_result_file, + "%sCHANGE MASTER TO MASTER_LOG_FILE='%s', MASTER_LOG_POS=%s;\n", + comment_prefix, file, offset); + check_io(md_result_file); + + if (!consistent_binlog_pos) + mysql_free_result(master); + return 0; } @@ -4760,7 +4832,7 @@ static int add_slave_statements(void) static int do_show_slave_status(MYSQL *mysql_con) { - MYSQL_RES *slave; + MYSQL_RES *slave= 0; const char *comment_prefix= (opt_slave_data == MYSQL_OPT_SLAVE_DATA_COMMENTED_SQL) ? "-- " : ""; if (mysql_query_with_error_report(mysql_con, &slave, "SHOW SLAVE STATUS")) @@ -4768,7 +4840,7 @@ static int do_show_slave_status(MYSQL *mysql_con) if (!ignore_errors) { /* SHOW SLAVE STATUS reports nothing and --force is not enabled */ - my_printf_error(0, "Error: Slave not set up", MYF(0)); + fprintf(stderr, "%s: Error: Slave not set up\n", my_progname_short); } mysql_free_result(slave); return 1; @@ -4828,7 +4900,7 @@ static int do_start_slave_sql(MYSQL *mysql_con) /* now, start slave if stopped */ if (mysql_query_with_error_report(mysql_con, 0, "START SLAVE")) { - my_printf_error(0, "Error: Unable to start slave", MYF(0)); + fprintf(stderr, "%s: Error: Unable to start slave\n", my_progname_short); return 1; } return(0); @@ -5265,6 +5337,7 @@ static my_bool get_view_structure(char *table, char* db) field= mysql_fetch_field_direct(table_res, 0); if (strcmp(field->name, "View") != 0) { + mysql_free_result(table_res); switch_character_set_results(mysql, default_charset); verbose_msg("-- It's base table, skipped\n"); DBUG_RETURN(0); @@ -5274,8 +5347,10 @@ static my_bool get_view_structure(char *table, char* db) if (path) { if (!(sql_file= open_sql_file_for_table(table, O_WRONLY))) + { + mysql_free_result(table_res); DBUG_RETURN(1); - + } write_header(sql_file, db); } @@ -5466,8 +5541,10 @@ int main(int argc, char **argv) { char bin_log_name[FN_REFLEN]; int exit_code; - MY_INIT("mysqldump"); + int consistent_binlog_pos= 0; + MY_INIT(argv[0]); + sf_leaking_memory=1; /* don't report memory leaks on early exits */ compatible_mode_normal_str[0]= 0; default_charset= (char *)mysql_universal_client_charset; bzero((char*) &ignore_table, sizeof(ignore_table)); @@ -5478,6 +5555,7 @@ int main(int argc, char **argv) free_resources(); exit(exit_code); } + sf_leaking_memory=0; /* from now on we cleanup properly */ /* Disable comments in xml mode if 'comments' option is not explicitly used. @@ -5505,7 +5583,13 @@ int main(int argc, char **argv) if (opt_slave_data && do_stop_slave_sql(mysql)) goto err; - if ((opt_lock_all_tables || opt_master_data || + if (opt_single_transaction && opt_master_data) + { + /* See if we can avoid FLUSH TABLES WITH READ LOCK (MariaDB 5.3+). */ + consistent_binlog_pos= check_consistent_binlog_pos(NULL, NULL); + } + + if ((opt_lock_all_tables || (opt_master_data && !consistent_binlog_pos) || (opt_single_transaction && flush_logs)) && do_flush_tables_read_lock(mysql)) goto err; @@ -5541,7 +5625,7 @@ int main(int argc, char **argv) /* Add 'STOP SLAVE to beginning of dump */ if (opt_slave_apply && add_stop_slave()) goto err; - if (opt_master_data && do_show_master_status(mysql)) + if (opt_master_data && do_show_master_status(mysql, consistent_binlog_pos)) goto err; if (opt_slave_data && do_show_slave_status(mysql)) goto err; @@ -5551,6 +5635,9 @@ int main(int argc, char **argv) if (opt_alltspcs) dump_all_tablespaces(); + if (extended_insert) + init_dynamic_string_checked(&extended_row, "", 1024, 1024); + if (opt_alldbs) { if (!opt_alltspcs && !opt_notspcs) @@ -5589,10 +5676,6 @@ int main(int argc, char **argv) } } - /* if --dump-slave , start the slave sql thread */ - if (opt_slave_data && do_start_slave_sql(mysql)) - goto err; - /* add 'START SLAVE' to end of dump */ if (opt_slave_apply && add_slave_statements()) goto err; @@ -5608,9 +5691,6 @@ int main(int argc, char **argv) if (opt_delete_master_logs && purge_bin_logs_to(mysql, bin_log_name)) goto err; -#ifdef HAVE_SMEM - my_free(shared_memory_base_name); -#endif /* No reason to explicitely COMMIT the transaction, neither to explicitely UNLOCK TABLES: these will be automatically be done by the server when we @@ -5618,6 +5698,14 @@ int main(int argc, char **argv) server. */ err: + /* if --dump-slave , start the slave sql thread */ + if (opt_slave_data) + do_start_slave_sql(mysql); + +#ifdef HAVE_SMEM + my_free(shared_memory_base_name); +#endif + dbDisconnect(current_host); if (!path) write_footer(md_result_file); diff --git a/client/mysqlimport.c b/client/mysqlimport.c index 416159abd81..8fab3f28cf3 100644 --- a/client/mysqlimport.c +++ b/client/mysqlimport.c @@ -1,5 +1,6 @@ /* - Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2000, 2015, Oracle and/or its affiliates. + Copyright (c) 2011, 2016, MariaDB 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 @@ -18,8 +19,14 @@ /* ** mysqlimport.c - Imports all given files ** into a table(s). +** +** ************************* +** * * +** * AUTHOR: Monty & Jani * +** * DATE: June 24, 1997 * +** * * +** ************************* */ - #define IMPORT_VERSION "3.7" #include "client_priv.h" @@ -29,7 +36,7 @@ /* Global Thread counter */ -uint counter; +uint counter= 0; pthread_mutex_t counter_mutex; pthread_cond_t count_threshhold; @@ -49,14 +56,14 @@ static char *opt_password=0, *current_user=0, *lines_terminated=0, *enclosed=0, *opt_enclosed=0, *escaped=0, *opt_columns=0, *default_charset= (char*) MYSQL_AUTODETECT_CHARSET_NAME; -static uint opt_enable_cleartext_plugin= 0; -static my_bool using_opt_enable_cleartext_plugin= 0; static uint opt_mysql_port= 0, opt_protocol= 0; static char * opt_mysql_unix_port=0; static char *opt_plugin_dir= 0, *opt_default_auth= 0; static longlong opt_ignore_lines= -1; #include <sslopt-vars.h> +static char **argv_to_free; + #ifdef HAVE_SMEM static char *shared_memory_base_name=0; #endif @@ -64,8 +71,8 @@ static char *shared_memory_base_name=0; static struct my_option my_long_options[] = { {"character-sets-dir", OPT_CHARSETS_DIR, - "Directory for character set files.", &charsets_dir, - &charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + "Directory for character set files.", (char**) &charsets_dir, + (char**) &charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"default-character-set", OPT_DEFAULT_CHARSET, "Set the default character set.", &default_charset, &default_charset, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, @@ -90,10 +97,6 @@ static struct my_option my_long_options[] = GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"delete", 'd', "First delete all rows from table.", &opt_delete, &opt_delete, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"enable_cleartext_plugin", OPT_ENABLE_CLEARTEXT_PLUGIN, - "Enable/disable the clear text authentication plugin.", - &opt_enable_cleartext_plugin, &opt_enable_cleartext_plugin, - 0, GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0}, {"fields-terminated-by", OPT_FTB, "Fields in the input file are terminated by the given string.", &fields_terminated, &fields_terminated, 0, @@ -185,7 +188,8 @@ static struct my_option my_long_options[] = }; -static const char *load_default_groups[]= { "mysqlimport","client",0 }; +static const char *load_default_groups[]= +{ "mysqlimport","client", "client-server", "client-mariadb", 0 }; static void print_version(void) @@ -197,6 +201,8 @@ static void print_version(void) static void usage(void) { + puts("Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc."); + puts("Copyright 2008-2011 Oracle and Monty Program Ab."); print_version(); puts(ORACLE_WELCOME_COPYRIGHT_NOTICE("2000")); printf("\ @@ -206,8 +212,9 @@ If one uses sockets to connect to the MySQL server, the server will open and\n\ read the text file directly. In other cases the client will open the text\n\ file. The SQL command 'LOAD DATA INFILE' is used to import the rows.\n"); - printf("\nUsage: %s [OPTIONS] database textfile...",my_progname); + printf("\nUsage: %s [OPTIONS] database textfile...\n",my_progname); print_defaults("my",load_default_groups); + puts(""); my_print_help(my_long_options); my_print_variables(my_long_options); } @@ -240,9 +247,6 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), opt_local_file=1; break; #endif - case OPT_ENABLE_CLEARTEXT_PLUGIN: - using_opt_enable_cleartext_plugin= TRUE; - break; case OPT_MYSQL_PROTOCOL: opt_protocol= find_type_or_exit(argument, &sql_protocol_typelib, opt->name); @@ -444,14 +448,10 @@ static MYSQL *db_connect(char *host, char *database, if (opt_default_auth && *opt_default_auth) mysql_options(mysql, MYSQL_DEFAULT_AUTH, opt_default_auth); - if (using_opt_enable_cleartext_plugin) - mysql_options(mysql, MYSQL_ENABLE_CLEARTEXT_PLUGIN, - (char*)&opt_enable_cleartext_plugin); - mysql_options(mysql, MYSQL_SET_CHARSET_NAME, default_charset); - if (!(mysql_connect_ssl_check(mysql, host, user, passwd, database, - opt_mysql_port, opt_mysql_unix_port, - 0, opt_ssl_required))) + if (!(mysql_real_connect(mysql,host,user,passwd, + database,opt_mysql_port,opt_mysql_unix_port, + 0))) { ignore_errors=0; /* NO RETURN FROM db_error */ db_error(mysql); @@ -480,10 +480,23 @@ static void db_disconnect(char *host, MYSQL *mysql) static void safe_exit(int error, MYSQL *mysql) { - if (ignore_errors) + if (error && ignore_errors) return; + + /* in multi-threaded mode protect from concurrent safe_exit's */ + if (counter) + pthread_mutex_lock(&counter_mutex); + if (mysql) mysql_close(mysql); + +#ifdef HAVE_SMEM + my_free(shared_memory_base_name); +#endif + free_defaults(argv_to_free); + mysql_library_end(); + my_free(opt_password); + my_end(my_end_arg); exit(error); } @@ -600,8 +613,8 @@ error: int main(int argc, char **argv) { int error=0; - char **argv_to_free; MY_INIT(argv[0]); + sf_leaking_memory=1; /* don't report memory leaks on early exits */ if (load_defaults("my",load_default_groups,&argc,&argv)) return 1; @@ -612,6 +625,7 @@ int main(int argc, char **argv) free_defaults(argv_to_free); return(1); } + sf_leaking_memory=0; /* from now on we cleanup properly */ if (opt_use_threads && !lock_tables) { @@ -688,11 +702,6 @@ int main(int argc, char **argv) exitcode= error; db_disconnect(current_host, mysql); } - my_free(opt_password); -#ifdef HAVE_SMEM - my_free(shared_memory_base_name); -#endif - free_defaults(argv_to_free); - my_end(my_end_arg); + safe_exit(0, 0); return(exitcode); } diff --git a/client/mysqlshow.c b/client/mysqlshow.c index d0390ec443b..4349c063ee8 100644 --- a/client/mysqlshow.c +++ b/client/mysqlshow.c @@ -1,5 +1,6 @@ /* - Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2000, 2015, Oracle and/or its affiliates. + Copyright (c) 2010, 2016, MariaDB 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 @@ -37,8 +38,6 @@ static uint my_end_arg= 0; static uint opt_verbose=0; static char *default_charset= (char*) MYSQL_AUTODETECT_CHARSET_NAME; static char *opt_plugin_dir= 0, *opt_default_auth= 0; -static uint opt_enable_cleartext_plugin= 0; -static my_bool using_opt_enable_cleartext_plugin= 0; #ifdef HAVE_SMEM static char *shared_memory_base_name=0; @@ -59,7 +58,8 @@ static void print_res_header(MYSQL_RES *result); static void print_res_top(MYSQL_RES *result); static void print_res_row(MYSQL_RES *result,MYSQL_ROW cur); -static const char *load_default_groups[]= { "mysqlshow","client",0 }; +static const char *load_default_groups[]= +{ "mysqlshow","client", "client-server", "client-mariadb", 0 }; static char * opt_mysql_unix_port=0; int main(int argc, char **argv) @@ -69,11 +69,13 @@ int main(int argc, char **argv) char *wild; MYSQL mysql; MY_INIT(argv[0]); + sf_leaking_memory=1; /* don't report memory leaks on early exits */ if (load_defaults("my",load_default_groups,&argc,&argv)) exit(1); get_options(&argc,&argv); + sf_leaking_memory=0; /* from now on we cleanup properly */ wild=0; if (argc) { @@ -135,14 +137,10 @@ int main(int argc, char **argv) if (opt_default_auth && *opt_default_auth) mysql_options(&mysql, MYSQL_DEFAULT_AUTH, opt_default_auth); - if (using_opt_enable_cleartext_plugin) - mysql_options(&mysql, MYSQL_ENABLE_CLEARTEXT_PLUGIN, - (char*)&opt_enable_cleartext_plugin); - - if (!(mysql_connect_ssl_check(&mysql, host, user, opt_password, - (first_argument_uses_wildcards) ? "" : - argv[0], opt_mysql_port, opt_mysql_unix_port, - 0, opt_ssl_required))) + if (!(mysql_real_connect(&mysql,host,user,opt_password, + (first_argument_uses_wildcards) ? "" : + argv[0],opt_mysql_port,opt_mysql_unix_port, + 0))) { fprintf(stderr,"%s: %s\n",my_progname,mysql_error(&mysql)); exit(1); @@ -177,7 +175,7 @@ int main(int argc, char **argv) static struct my_option my_long_options[] = { {"character-sets-dir", 'c', "Directory for character set files.", - &charsets_dir, &charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, + (char**) &charsets_dir, (char**) &charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"default-character-set", OPT_DEFAULT_CHARSET, "Set the default character set.", &default_charset, @@ -201,10 +199,6 @@ static struct my_option my_long_options[] = "Default authentication client-side plugin to use.", &opt_default_auth, &opt_default_auth, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"enable_cleartext_plugin", OPT_ENABLE_CLEARTEXT_PLUGIN, - "Enable/disable the clear text authentication plugin.", - &opt_enable_cleartext_plugin, &opt_enable_cleartext_plugin, - 0, GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0}, {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, {"host", 'h', "Connect to host.", &host, &host, 0, GET_STR, @@ -285,6 +279,7 @@ If no table is given, then all matching tables in database are shown.\n\ If no column is given, then all matching columns and column types in table\n\ are shown."); print_defaults("my",load_default_groups); + puts(""); my_print_help(my_long_options); my_print_variables(my_long_options); } @@ -319,9 +314,6 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), opt_protocol = MYSQL_PROTOCOL_PIPE; #endif break; - case (int) OPT_ENABLE_CLEARTEXT_PLUGIN: - using_opt_enable_cleartext_plugin= TRUE; - break; case OPT_MYSQL_PROTOCOL: opt_protocol= find_type_or_exit(argument, &sql_protocol_typelib, opt->name); diff --git a/client/mysqlslap.c b/client/mysqlslap.c index eb2b577948c..4f6fc7d45cb 100644 --- a/client/mysqlslap.c +++ b/client/mysqlslap.c @@ -1,5 +1,6 @@ /* - Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2005, 2015, Oracle and/or its affiliates. + Copyright (c) 2010, 2016, MariaDB 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 @@ -123,10 +124,9 @@ static char *host= NULL, *opt_password= NULL, *user= NULL, *default_engine= NULL, *pre_system= NULL, *post_system= NULL, - *opt_mysql_unix_port= NULL; + *opt_mysql_unix_port= NULL, + *opt_init_command= NULL; static char *opt_plugin_dir= 0, *opt_default_auth= 0; -static uint opt_enable_cleartext_plugin= 0; -static my_bool using_opt_enable_cleartext_plugin= 0; const char *delimiter= "\n"; @@ -180,7 +180,8 @@ static uint opt_protocol= 0; static int get_options(int *argc,char ***argv); static uint opt_mysql_port= 0; -static const char *load_default_groups[]= { "mysqlslap","client",0 }; +static const char *load_default_groups[]= +{ "mysqlslap", "client", "client-server", "client-mariadb", 0 }; typedef struct statement statement; @@ -294,12 +295,32 @@ static int gettimeofday(struct timeval *tp, void *tzp) } #endif +void set_mysql_connect_options(MYSQL *mysql) +{ + if (opt_compress) + mysql_options(mysql,MYSQL_OPT_COMPRESS,NullS); +#ifdef HAVE_OPENSSL + if (opt_use_ssl) + mysql_ssl_set(mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca, + opt_ssl_capath, opt_ssl_cipher); +#endif + if (opt_protocol) + mysql_options(mysql,MYSQL_OPT_PROTOCOL,(char*)&opt_protocol); +#ifdef HAVE_SMEM + if (shared_memory_base_name) + mysql_options(mysql,MYSQL_SHARED_MEMORY_BASE_NAME,shared_memory_base_name); +#endif + mysql_options(mysql, MYSQL_SET_CHARSET_NAME, default_charset); +} + + int main(int argc, char **argv) { MYSQL mysql; option_string *eptr; MY_INIT(argv[0]); + sf_leaking_memory=1; /* don't report memory leaks on early exits */ if (load_defaults("my",load_default_groups,&argc,&argv)) { @@ -313,6 +334,7 @@ int main(int argc, char **argv) my_end(0); exit(1); } + sf_leaking_memory=0; /* from now on we cleanup properly */ /* Seed the random number generator if we will be using it. */ if (auto_generate_sql) @@ -329,20 +351,7 @@ int main(int argc, char **argv) exit(1); } mysql_init(&mysql); - if (opt_compress) - mysql_options(&mysql,MYSQL_OPT_COMPRESS,NullS); -#ifdef HAVE_OPENSSL - if (opt_use_ssl) - mysql_ssl_set(&mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca, - opt_ssl_capath, opt_ssl_cipher); -#endif - if (opt_protocol) - mysql_options(&mysql,MYSQL_OPT_PROTOCOL,(char*)&opt_protocol); -#ifdef HAVE_SMEM - if (shared_memory_base_name) - mysql_options(&mysql,MYSQL_SHARED_MEMORY_BASE_NAME,shared_memory_base_name); -#endif - mysql_options(&mysql, MYSQL_SET_CHARSET_NAME, default_charset); + set_mysql_connect_options(&mysql); if (opt_plugin_dir && *opt_plugin_dir) mysql_options(&mysql, MYSQL_PLUGIN_DIR, opt_plugin_dir); @@ -350,14 +359,11 @@ int main(int argc, char **argv) if (opt_default_auth && *opt_default_auth) mysql_options(&mysql, MYSQL_DEFAULT_AUTH, opt_default_auth); - if (using_opt_enable_cleartext_plugin) - mysql_options(&mysql, MYSQL_ENABLE_CLEARTEXT_PLUGIN, - (char*) &opt_enable_cleartext_plugin); if (!opt_only_print) { - if (!(mysql_connect_ssl_check(&mysql, host, user, opt_password, - NULL, opt_mysql_port, opt_mysql_unix_port, - connect_flags, opt_ssl_required))) + if (!(mysql_real_connect(&mysql, host, user, opt_password, + NULL, opt_mysql_port, + opt_mysql_unix_port, connect_flags))) { fprintf(stderr,"%s: Error when connecting to server: %s\n", my_progname,mysql_error(&mysql)); @@ -423,6 +429,7 @@ int main(int argc, char **argv) my_free(shared_memory_base_name); #endif free_defaults(defaults_argv); + mysql_library_end(); my_end(my_end_arg); return 0; @@ -461,7 +468,16 @@ void concurrency_loop(MYSQL *mysql, uint current, option_string *eptr) /* First we create */ if (create_statements) + { + /* + If we have an --engine option, then we indicate + create_schema() to add the engine type to the DDL. + */ + if (eptr) + create_statements->type= CREATE_TABLE_TYPE; + create_schema(mysql, create_schema_string, create_statements, eptr); + } /* If we generated GUID we need to build a list of them from creation that @@ -475,10 +491,10 @@ void concurrency_loop(MYSQL *mysql, uint current, option_string *eptr) if (commit_rate) run_query(mysql, "SET AUTOCOMMIT=0", strlen("SET AUTOCOMMIT=0")); - if (pre_system) - if ((sysret= system(pre_system)) != 0) - fprintf(stderr, "Warning: Execution of pre_system option returned %d.\n", - sysret); + if (pre_system && (sysret= system(pre_system)) != 0) + fprintf(stderr, + "Warning: Execution of pre_system option returned %d.\n", + sysret); /* Pre statements are always run after all other logic so they can @@ -492,11 +508,10 @@ void concurrency_loop(MYSQL *mysql, uint current, option_string *eptr) if (post_statements) run_statements(mysql, post_statements); - if (post_system) - if ((sysret= system(post_system)) != 0) - fprintf(stderr, "Warning: Execution of post_system option returned %d.\n", - sysret); - + if (post_system && (sysret= system(post_system)) != 0) + fprintf(stderr, + "Warning: Execution of post_system option returned %d.\n", + sysret); /* We are finished with this run */ if (auto_generate_sql_autoincrement || auto_generate_sql_guid_primary) drop_primary_key_list(); @@ -541,7 +556,7 @@ static struct my_option my_long_options[] = 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"auto-generate-sql-load-type", OPT_SLAP_AUTO_GENERATE_SQL_LOAD_TYPE, "Specify test load type: mixed, update, write, key, or read; default is mixed.", - &auto_generate_sql_type, &auto_generate_sql_type, + (char**) &auto_generate_sql_type, (char**) &auto_generate_sql_type, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"auto-generate-sql-secondary-indexes", OPT_SLAP_AUTO_GENERATE_SECONDARY_INDEXES, @@ -572,13 +587,13 @@ static struct my_option my_long_options[] = &opt_compress, &opt_compress, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"concurrency", 'c', "Number of clients to simulate for query to run.", - &concurrency_str, &concurrency_str, 0, GET_STR, + (char**) &concurrency_str, (char**) &concurrency_str, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"create", OPT_SLAP_CREATE_STRING, "File or string to use create tables.", &create_string, &create_string, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"create-schema", OPT_CREATE_SLAP_SCHEMA, "Schema to run tests in.", - &create_schema_string, &create_schema_string, 0, GET_STR, + (char**) &create_schema_string, (char**) &create_schema_string, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"csv", OPT_SLAP_CSV, "Generate CSV output to named file or to stdout if no file is named.", @@ -588,7 +603,7 @@ static struct my_option my_long_options[] = 0, 0, 0, GET_DISABLED, OPT_ARG, 0, 0, 0, 0, 0, 0}, #else {"debug", '#', "Output debug log. Often this is 'd:t:o,filename'.", - &default_dbug_option, &default_dbug_option, 0, GET_STR, + (char**) &default_dbug_option, (char**) &default_dbug_option, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, #endif {"debug-check", OPT_DEBUG_CHECK, "Check memory and open file usage at exit.", @@ -602,32 +617,36 @@ static struct my_option my_long_options[] = GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"delimiter", 'F', "Delimiter to use in SQL statements supplied in file or command line.", - &delimiter, &delimiter, 0, GET_STR, REQUIRED_ARG, + (char**) &delimiter, (char**) &delimiter, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"detach", OPT_SLAP_DETACH, "Detach (close and reopen) connections after X number of requests.", &detach_rate, &detach_rate, 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"enable_cleartext_plugin", OPT_ENABLE_CLEARTEXT_PLUGIN, - "Enable/disable the clear text authentication plugin.", - &opt_enable_cleartext_plugin, &opt_enable_cleartext_plugin, - 0, GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0}, - {"engine", 'e', "Storage engine to use for creating the table.", - &default_engine, &default_engine, 0, + {"engine", 'e', + "Comma separated list of storage engines to use for creating the table." + " The test is run for each engine. You can also specify an option for an " + "engine after a `:', like memory:max_row=2300", + &default_engine, &default_engine, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"host", 'h', "Connect to host.", &host, &host, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"init-command", OPT_INIT_COMMAND, + "SQL Command to execute when connecting to MySQL server. Will " + "automatically be re-executed when reconnecting.", + &opt_init_command, &opt_init_command, 0, + GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"iterations", 'i', "Number of times to run the tests.", &iterations, &iterations, 0, GET_UINT, REQUIRED_ARG, 1, 0, 0, 0, 0, 0}, {"no-drop", OPT_SLAP_NO_DROP, "Do not drop the schema after the test.", &opt_no_drop, &opt_no_drop, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"number-char-cols", 'x', "Number of VARCHAR columns to create in table if specifying --auto-generate-sql.", - &num_char_cols_opt, &num_char_cols_opt, 0, GET_STR, REQUIRED_ARG, + (char**) &num_char_cols_opt, (char**) &num_char_cols_opt, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"number-int-cols", 'y', "Number of INT columns to create in table if specifying --auto-generate-sql.", - &num_int_cols_opt, &num_int_cols_opt, 0, GET_STR, REQUIRED_ARG, + (char**) &num_int_cols_opt, (char**) &num_int_cols_opt, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"number-of-queries", OPT_MYSQL_NUMBER_OF_QUERY, "Limit each client to this number of queries (this is not exact).", @@ -714,7 +733,9 @@ static void usage(void) puts("Run a query multiple times against the server.\n"); printf("Usage: %s [OPTIONS]\n",my_progname); print_defaults("my",load_default_groups); + puts(""); my_print_help(my_long_options); + my_print_variables(my_long_options); } @@ -770,9 +791,6 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), case 'I': /* Info */ usage(); exit(0); - case OPT_ENABLE_CLEARTEXT_PLUGIN: - using_opt_enable_cleartext_plugin= TRUE; - break; } DBUG_RETURN(0); } @@ -984,6 +1002,7 @@ build_update_string(void) ptr->type= UPDATE_TYPE_REQUIRES_PREFIX ; else ptr->type= UPDATE_TYPE; + strmov(ptr->string, update_string.str); dynstr_free(&update_string); DBUG_RETURN(ptr); @@ -999,8 +1018,8 @@ build_update_string(void) static statement * build_insert_string(void) { - char buf[HUGE_STRING_LENGTH]; - unsigned int col_count; + char buf[HUGE_STRING_LENGTH]; + unsigned int col_count; statement *ptr; DYNAMIC_STRING insert_string; DBUG_ENTER("build_insert_string"); @@ -1090,8 +1109,8 @@ build_insert_string(void) static statement * build_select_string(my_bool key) { - char buf[HUGE_STRING_LENGTH]; - unsigned int col_count; + char buf[HUGE_STRING_LENGTH]; + unsigned int col_count; statement *ptr; static DYNAMIC_STRING query_string; DBUG_ENTER("build_select_string"); @@ -1143,6 +1162,7 @@ build_select_string(my_bool key) ptr->type= SELECT_TYPE_REQUIRES_PREFIX; else ptr->type= SELECT_TYPE; + strmov(ptr->string, query_string.str); dynstr_free(&query_string); DBUG_RETURN(ptr); @@ -1204,8 +1224,6 @@ get_options(int *argc,char ***argv) exit(1); } - - if (auto_generate_sql && num_of_query && auto_actual_queries) { fprintf(stderr, @@ -1252,6 +1270,7 @@ get_options(int *argc,char ***argv) num_int_cols= atoi(str->string); if (str->option) num_int_cols_index= atoi(str->option); + option_cleanup(str); } @@ -1270,6 +1289,7 @@ get_options(int *argc,char ***argv) num_char_cols_index= atoi(str->option); else num_char_cols_index= 0; + option_cleanup(str); } @@ -1391,9 +1411,9 @@ get_options(int *argc,char ***argv) fprintf(stderr,"%s: Could not open create file\n", my_progname); exit(1); } - tmp_string= (char *)my_malloc(sbuf.st_size + 1, + tmp_string= (char *)my_malloc((size_t)sbuf.st_size + 1, MYF(MY_ZEROFILL|MY_FAE|MY_WME)); - my_read(data_file, (uchar*) tmp_string, sbuf.st_size, MYF(0)); + my_read(data_file, (uchar*) tmp_string, (size_t)sbuf.st_size, MYF(0)); tmp_string[sbuf.st_size]= '\0'; my_close(data_file,MYF(0)); parse_delimiter(tmp_string, &create_statements, delimiter[0]); @@ -1418,9 +1438,9 @@ get_options(int *argc,char ***argv) fprintf(stderr,"%s: Could not open query supplied file\n", my_progname); exit(1); } - tmp_string= (char *)my_malloc(sbuf.st_size + 1, + tmp_string= (char *)my_malloc((size_t)sbuf.st_size + 1, MYF(MY_ZEROFILL|MY_FAE|MY_WME)); - my_read(data_file, (uchar*) tmp_string, sbuf.st_size, MYF(0)); + my_read(data_file, (uchar*) tmp_string, (size_t)sbuf.st_size, MYF(0)); tmp_string[sbuf.st_size]= '\0'; my_close(data_file,MYF(0)); if (user_supplied_query) @@ -1449,9 +1469,9 @@ get_options(int *argc,char ***argv) fprintf(stderr,"%s: Could not open query supplied file\n", my_progname); exit(1); } - tmp_string= (char *)my_malloc(sbuf.st_size + 1, + tmp_string= (char *)my_malloc((size_t)sbuf.st_size + 1, MYF(MY_ZEROFILL|MY_FAE|MY_WME)); - my_read(data_file, (uchar*) tmp_string, sbuf.st_size, MYF(0)); + my_read(data_file, (uchar*) tmp_string, (size_t)sbuf.st_size, MYF(0)); tmp_string[sbuf.st_size]= '\0'; my_close(data_file,MYF(0)); if (user_supplied_pre_statements) @@ -1480,9 +1500,9 @@ get_options(int *argc,char ***argv) fprintf(stderr,"%s: Could not open query supplied file\n", my_progname); exit(1); } - tmp_string= (char *)my_malloc(sbuf.st_size + 1, + tmp_string= (char *)my_malloc((size_t)sbuf.st_size + 1, MYF(MY_ZEROFILL|MY_FAE|MY_WME)); - my_read(data_file, (uchar*) tmp_string, sbuf.st_size, MYF(0)); + my_read(data_file, (uchar*) tmp_string, (size_t)sbuf.st_size, MYF(0)); tmp_string[sbuf.st_size]= '\0'; my_close(data_file,MYF(0)); if (user_supplied_post_statements) @@ -1510,6 +1530,7 @@ get_options(int *argc,char ***argv) if (tty_password) opt_password= get_tty_password(NullS); + DBUG_RETURN(0); } @@ -1524,6 +1545,7 @@ static int run_query(MYSQL *mysql, const char *query, int len) if (verbose >= 3) printf("%.*s;\n", len, query); + return mysql_real_query(mysql, query, len); } @@ -1644,18 +1666,6 @@ create_schema(MYSQL *mysql, const char *db, statement *stmt, } } - if (engine_stmt) - { - len= snprintf(query, HUGE_STRING_LENGTH, "set storage_engine=`%s`", - engine_stmt->string); - if (run_query(mysql, query, len)) - { - fprintf(stderr,"%s: Cannot set default engine: %s\n", my_progname, - mysql_error(mysql)); - exit(1); - } - } - count= 0; after_create= stmt; @@ -1669,8 +1679,21 @@ limit_not_met: { char buffer[HUGE_STRING_LENGTH]; - snprintf(buffer, HUGE_STRING_LENGTH, "%s %s", ptr->string, - engine_stmt->option); + snprintf(buffer, HUGE_STRING_LENGTH, "%s Engine = %s %s", + ptr->string, engine_stmt->string, engine_stmt->option); + if (run_query(mysql, buffer, strlen(buffer))) + { + fprintf(stderr,"%s: Cannot run query %.*s ERROR : %s\n", + my_progname, (uint)ptr->length, ptr->string, mysql_error(mysql)); + exit(1); + } + } + else if (engine_stmt && engine_stmt->string && ptr->type == CREATE_TABLE_TYPE) + { + char buffer[HUGE_STRING_LENGTH]; + + snprintf(buffer, HUGE_STRING_LENGTH, "%s Engine = %s", + ptr->string, engine_stmt->string); if (run_query(mysql, buffer, strlen(buffer))) { fprintf(stderr,"%s: Cannot run query %.*s ERROR : %s\n", @@ -1704,6 +1727,7 @@ drop_schema(MYSQL *mysql, const char *db) { char query[HUGE_STRING_LENGTH]; int len; + DBUG_ENTER("drop_schema"); len= snprintf(query, HUGE_STRING_LENGTH, "DROP SCHEMA IF EXISTS `%s`", db); @@ -1840,6 +1864,7 @@ pthread_handler_t run_task(void *p) my_progname, mysql_error(mysql)); exit(0); } + set_mysql_connect_options(mysql); if (mysql_thread_init()) { @@ -1880,7 +1905,6 @@ limit_not_met: my_progname, mysql_error(mysql)); exit(0); } - if (slap_connect(mysql)) goto end; } @@ -1990,13 +2014,18 @@ parse_option(const char *origin, option_string **stmt, char delm) uint count= 0; /* We know that there is always one */ for (tmp= *sptr= (option_string *)my_malloc(sizeof(option_string), - MYF(MY_ZEROFILL|MY_FAE|MY_WME)); + MYF(MY_ZEROFILL|MY_FAE|MY_WME)); (retstr= strchr(ptr, delm)); tmp->next= (option_string *)my_malloc(sizeof(option_string), - MYF(MY_ZEROFILL|MY_FAE|MY_WME)), + MYF(MY_ZEROFILL|MY_FAE|MY_WME)), tmp= tmp->next) { - char buffer[HUGE_STRING_LENGTH]; + /* + Initialize buffer, because otherwise an + --engine=<storage_engine>:<option>,<eng1>,<eng2> + will crash. + */ + char buffer[HUGE_STRING_LENGTH]= ""; char *buffer_ptr; /* @@ -2008,6 +2037,11 @@ parse_option(const char *origin, option_string **stmt, char delm) count++; strncpy(buffer, ptr, (size_t)(retstr - ptr)); + /* + Handle --engine=memory:max_row=200 cases, or more general speaking + --engine=<storage_engine>:<options>, which will be translated to + Engine = storage_engine option. + */ if ((buffer_ptr= strchr(buffer, ':'))) { char *option_ptr; @@ -2028,13 +2062,15 @@ parse_option(const char *origin, option_string **stmt, char delm) tmp->length= (size_t)(retstr - ptr); } + /* Skip delimiter delm */ ptr+= retstr - ptr + 1; if (isspace(*ptr)) ptr++; + count++; } - if (ptr != origin+length) + if (ptr != origin + length) { char *origin_ptr; @@ -2050,7 +2086,7 @@ parse_option(const char *origin, option_string **stmt, char delm) char *option_ptr; tmp->length= (size_t)(origin_ptr - ptr); - tmp->string= my_strndup(origin, tmp->length, MYF(MY_FAE)); + tmp->string= my_strndup(ptr, tmp->length, MYF(MY_FAE)); option_ptr= (char *)ptr + 1 + tmp->length; @@ -2100,7 +2136,7 @@ parse_delimiter(const char *script, statement **stmt, char delm) if (ptr != script+length) { tmp->string= my_strndup(ptr, (uint)((script + length) - ptr), - MYF(MY_FAE)); + MYF(MY_FAE)); tmp->length= (size_t)((script + length) - ptr); count++; } @@ -2158,6 +2194,7 @@ print_conclusions_csv(conclusions *con) { char buffer[HUGE_STRING_LENGTH]; const char *ptr= auto_generate_sql_type ? auto_generate_sql_type : "query"; + snprintf(buffer, HUGE_STRING_LENGTH, "%s,%s,%ld.%03ld,%ld.%03ld,%ld.%03ld,%d,%llu\n", con->engine ? con->engine : "", /* Storage engine we ran against */ @@ -2244,6 +2281,9 @@ slap_connect(MYSQL *mysql) int x, connect_error= 1; for (x= 0; x < 10; x++) { + set_mysql_connect_options(mysql); + if (opt_init_command) + mysql_options(mysql, MYSQL_INIT_COMMAND, opt_init_command); if (mysql_real_connect(mysql, host, user, opt_password, create_schema_string, opt_mysql_port, diff --git a/client/mysqltest.cc b/client/mysqltest.cc index 79d448cf811..3652d1a40e2 100644 --- a/client/mysqltest.cc +++ b/client/mysqltest.cc @@ -1,4 +1,5 @@ -/* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. + Copyright (c) 2009, 2016, Monty Program Ab. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -22,9 +23,17 @@ http://dev.mysql.com/doc/mysqltest/en/index.html Please keep the test framework tools identical in all versions! + + Written by: + Sasha Pachev <sasha@mysql.com> + Matt Wagner <matt@mysql.com> + Monty + Jani + Holyfoot + And many others */ -#define MTEST_VERSION "3.3" +#define MTEST_VERSION "3.4" #include "client_priv.h" #include <mysql_version.h> @@ -54,6 +63,13 @@ #define SIGNAL_FMT "signal %d" #endif +#include <my_context.h> +static my_bool non_blocking_api_enabled= 0; +#if !defined(EMBEDDED_LIBRARY) && !defined(MY_CONTEXT_DISABLE) +#define WRAP_NONBLOCK_ENABLED non_blocking_api_enabled +#include "../tests/nonblock-wrappers.h" +#endif + /* Use cygwin for --exec and --system before 5.0 */ #if MYSQL_VERSION_ID < 50000 #define USE_CYGWIN @@ -63,7 +79,9 @@ #define MAX_COLUMNS 256 #define MAX_EMBEDDED_SERVER_ARGS 64 #define MAX_DELIMITER_LENGTH 16 -#define DEFAULT_MAX_CONN 128 +#define DEFAULT_MAX_CONN 64 + +#define DIE_BUFF_SIZE 8192 /* Flags controlling send and reap */ #define QUERY_SEND_FLAG 1 @@ -80,10 +98,7 @@ static my_bool get_one_option(int optid, const struct my_option *, C_MODE_END enum { - OPT_PS_PROTOCOL=OPT_MAX_CLIENT_OPTION, OPT_SP_PROTOCOL, - OPT_CURSOR_PROTOCOL, OPT_VIEW_PROTOCOL, OPT_MAX_CONNECT_RETRIES, - OPT_MAX_CONNECTIONS, OPT_MARK_PROGRESS, OPT_LOG_DIR, - OPT_TAIL_LINES, OPT_RESULT_FORMAT_VERSION + OPT_LOG_DIR=OPT_MAX_CLIENT_OPTION, OPT_RESULT_FORMAT_VERSION }; static int record= 0, opt_sleep= -1; @@ -91,11 +106,12 @@ static char *opt_db= 0, *opt_pass= 0; const char *opt_user= 0, *opt_host= 0, *unix_sock= 0, *opt_basedir= "./"; static char *shared_memory_base_name=0; const char *opt_logdir= ""; -const char *opt_include= 0, *opt_charsets_dir; +const char *opt_prologue= 0, *opt_charsets_dir; static int opt_port= 0; static int opt_max_connect_retries; static int opt_result_format_version; static int opt_max_connections= DEFAULT_MAX_CONN; +static int error_count= 0; static my_bool opt_compress= 0, silent= 0, verbose= 0; static my_bool debug_info_flag= 0, debug_check_flag= 0; static my_bool tty_password= 0; @@ -109,13 +125,15 @@ static my_bool display_result_vertically= FALSE, display_result_lower= FALSE, display_metadata= FALSE, display_result_sorted= FALSE; static my_bool disable_query_log= 0, disable_result_log= 0; static my_bool disable_connect_log= 1; -static my_bool disable_warnings= 0; +static my_bool disable_warnings= 0, disable_column_names= 0; +static my_bool prepare_warnings_enabled= 0; static my_bool disable_info= 1; -static my_bool abort_on_error= 1; +static my_bool abort_on_error= 1, opt_continue_on_error= 0; static my_bool server_initialized= 0; static my_bool is_windows= 0; static char **default_argv; -static const char *load_default_groups[]= { "mysqltest", "client", 0 }; +static const char *load_default_groups[]= +{ "mysqltest", "client", "client-server", "client-mariadb", 0 }; static char line_buffer[MAX_DELIMITER_LENGTH], *line_buffer_pos= line_buffer; /* Info on properties that can be set with --enable_X and --disable_X */ @@ -165,6 +183,10 @@ static char delimiter[MAX_DELIMITER_LENGTH]= ";"; static uint delimiter_length= 1; static char TMPDIR[FN_REFLEN]; +static char global_subst_from[200]; +static char global_subst_to[200]; +static char *global_subst= NULL; +static MEM_ROOT require_file_root; /* Block stack */ enum block_cmd { @@ -196,7 +218,6 @@ static struct st_test_file file_stack[16]; static struct st_test_file* cur_file; static struct st_test_file* file_stack_end; - static CHARSET_INFO *charset_info= &my_charset_latin1; /* Default charset */ static const char *embedded_server_groups[]= @@ -222,7 +243,9 @@ static ulonglong timer_now(void); static ulong connection_retry_sleep= 100000; /* Microseconds */ -static char *opt_plugin_dir= 0; +static const char *opt_plugin_dir; +static const char *opt_suite_dir, *opt_overlay_dir; +static size_t suite_dir_len, overlay_dir_len; /* Precompiled re's */ static my_regex_t ps_re; /* the query can be run using PS protocol */ @@ -233,6 +256,10 @@ static void init_re(void); static int match_re(my_regex_t *, char *); static void free_re(void); +static int replace(DYNAMIC_STRING *ds_str, + const char *search_str, ulong search_len, + const char *replace_str, ulong replace_len); + static uint opt_protocol=0; DYNAMIC_ARRAY q_lines; @@ -273,7 +300,7 @@ HASH var_hash; struct st_connection { - MYSQL mysql; + MYSQL *mysql; /* Used when creating views and sp, to avoid implicit commit */ MYSQL* util_mysql; char *name; @@ -327,6 +354,7 @@ enum enum_commands { Q_ENABLE_WARNINGS, Q_DISABLE_WARNINGS, Q_ENABLE_INFO, Q_DISABLE_INFO, Q_ENABLE_METADATA, Q_DISABLE_METADATA, + Q_ENABLE_COLUMN_NAMES, Q_DISABLE_COLUMN_NAMES, Q_EXEC, Q_DELIMITER, Q_DISABLE_ABORT_ON_ERROR, Q_ENABLE_ABORT_ON_ERROR, Q_DISPLAY_VERTICAL_RESULTS, Q_DISPLAY_HORIZONTAL_RESULTS, @@ -334,6 +362,7 @@ enum enum_commands { Q_LOWERCASE, Q_START_TIMER, Q_END_TIMER, Q_CHARACTER_SET, Q_DISABLE_PS_PROTOCOL, Q_ENABLE_PS_PROTOCOL, + Q_ENABLE_NON_BLOCKING_API, Q_DISABLE_NON_BLOCKING_API, Q_DISABLE_RECONNECT, Q_ENABLE_RECONNECT, Q_IF, Q_DISABLE_PARSING, Q_ENABLE_PARSING, @@ -345,6 +374,7 @@ enum enum_commands { Q_SEND_SHUTDOWN, Q_SHUTDOWN_SERVER, Q_RESULT_FORMAT_VERSION, Q_MOVE_FILE, Q_REMOVE_FILES_WILDCARD, Q_SEND_EVAL, + Q_ENABLE_PREPARE_WARNINGS, Q_DISABLE_PREPARE_WARNINGS, Q_UNKNOWN, /* Unknown command. */ Q_COMMENT, /* Comments, ignored. */ Q_COMMENT_WITH_COMMAND, @@ -397,6 +427,8 @@ const char *command_names[]= "disable_info", "enable_metadata", "disable_metadata", + "enable_column_names", + "disable_column_names", "exec", "delimiter", "disable_abort_on_error", @@ -412,6 +444,8 @@ const char *command_names[]= "character_set", "disable_ps_protocol", "enable_ps_protocol", + "enable_non_blocking_api", + "disable_non_blocking_api", "disable_reconnect", "enable_reconnect", "if", @@ -445,6 +479,8 @@ const char *command_names[]= "move_file", "remove_files_wildcard", "send_eval", + "enable_prepare_warnings", + "disable_prepare_warnings", 0 }; @@ -485,10 +521,11 @@ struct st_command { char *query, *query_buf,*first_argument,*last_argument,*end; DYNAMIC_STRING content; + DYNAMIC_STRING eval_query; int first_word_len, query_len; my_bool abort_on_error, used_replace; struct st_expected_errors expected_errors; - char require_file[FN_REFLEN]; + char *require_file; enum enum_commands type; }; @@ -529,14 +566,15 @@ const char *from, int len); static void cleanup_and_exit(int exit_code) __attribute__((noreturn)); -void die(const char *fmt, ...) - ATTRIBUTE_FORMAT(printf, 1, 2) __attribute__((noreturn)); -void abort_not_supported_test(const char *fmt, ...) - ATTRIBUTE_FORMAT(printf, 1, 2) __attribute__((noreturn)); -void verbose_msg(const char *fmt, ...) - ATTRIBUTE_FORMAT(printf, 1, 2); -void log_msg(const char *fmt, ...) - ATTRIBUTE_FORMAT(printf, 1, 2); +void really_die(const char *msg) __attribute__((noreturn)); +void report_or_die(const char *fmt, ...) ATTRIBUTE_FORMAT(printf, 1, 2); +void die(const char *fmt, ...) ATTRIBUTE_FORMAT(printf, 1, 2) + __attribute__((noreturn)); +static void make_error_message(char *buf, size_t len, const char *fmt, va_list args); +void abort_not_supported_test(const char *fmt, ...) ATTRIBUTE_FORMAT(printf, 1, 2) + __attribute__((noreturn)); +void verbose_msg(const char *fmt, ...) ATTRIBUTE_FORMAT(printf, 1, 2); +void log_msg(const char *fmt, ...) ATTRIBUTE_FORMAT(printf, 1, 2); VAR* var_from_env(const char *, const char *); VAR* var_init(VAR* v, const char *name, int name_len, const char *val, @@ -587,6 +625,8 @@ void free_all_replace(){ free_replace_column(); } +void var_set_int(const char* name, int value); + class LogFile { FILE* m_file; @@ -607,8 +647,7 @@ public: void open(const char* dir, const char* name, const char* ext) { DBUG_ENTER("LogFile::open"); - DBUG_PRINT("enter", ("dir: '%s', name: '%s'", - dir, name)); + DBUG_PRINT("enter", ("dir: '%s', name: '%s'", dir, name)); if (!name) { m_file= stdout; @@ -656,6 +695,10 @@ public: DBUG_VOID_RETURN; DBUG_ASSERT(ds->str); +#ifdef EXTRA_DEBUG + DBUG_PRINT("QQ", ("str: %*s", (int) ds->length, ds->str)); +#endif + if (fwrite(ds->str, 1, ds->length, m_file) != ds->length) die("Failed to write %lu bytes to '%s', errno: %d", (unsigned long)ds->length, m_file_name, errno); @@ -674,14 +717,14 @@ public: lines++; int show_offset= 0; - char buf[256]; + char buf[256+1]; /* + zero termination for DBUG_PRINT */ size_t bytes; bool found_bof= false; /* Search backward in file until "lines" newline has been found */ while (lines && !found_bof) { - show_offset-= sizeof(buf); + show_offset-= sizeof(buf)-1; while(fseek(m_file, show_offset, SEEK_END) != 0 && show_offset < 0) { found_bof= true; @@ -689,7 +732,7 @@ public: show_offset++; } - if ((bytes= fread(buf, 1, sizeof(buf), m_file)) <= 0) + if ((bytes= fread(buf, 1, sizeof(buf)-1, m_file)) <= 0) { // ferror=0 will happen here if no queries executed yet if (ferror(m_file)) @@ -699,8 +742,8 @@ public: DBUG_VOID_RETURN; } - DBUG_PRINT("info", ("Read %lu bytes from file, buf: %s", - (unsigned long)bytes, buf)); + DBUG_PRINT("info", ("Read %zu bytes from file, buf: %.*s", + bytes, (int)bytes, buf)); char* show_from= buf + bytes; while(show_from > buf && lines > 0 ) @@ -743,8 +786,10 @@ public: } } - while ((bytes= fread(buf, 1, sizeof(buf), m_file)) > 0) - fwrite(buf, 1, bytes, stderr); + while ((bytes= fread(buf, 1, sizeof(buf)-1, m_file)) > 0) + if (bytes != fwrite(buf, 1, bytes, stderr)) + die("Failed to write to '%s', errno: %d", + m_file_name, errno); if (!lines) { @@ -765,7 +810,8 @@ void replace_dynstr_append_mem(DYNAMIC_STRING *ds, const char *val, int len); void replace_dynstr_append(DYNAMIC_STRING *ds, const char *val); void replace_dynstr_append_uint(DYNAMIC_STRING *ds, uint val); -void dynstr_append_sorted(DYNAMIC_STRING* ds, DYNAMIC_STRING* ds_input); +void dynstr_append_sorted(DYNAMIC_STRING* ds, DYNAMIC_STRING* ds_input, + bool keep_header); static int match_expected_error(struct st_command *command, unsigned int err_errno, @@ -776,11 +822,20 @@ void handle_error(struct st_command*, void handle_no_error(struct st_command*); void revert_properties(); +static void handle_no_active_connection(struct st_command* command, + struct st_connection *cn, DYNAMIC_STRING *ds); + #ifdef EMBEDDED_LIBRARY #define EMB_SEND_QUERY 1 #define EMB_READ_QUERY_RESULT 2 #define EMB_END_CONNECTION 3 +#define EMB_PREPARE_STMT 4 +#define EMB_EXECUTE_STMT 5 + +/* workaround for MySQL BUG#57491 */ +#undef MY_WME +#define MY_WME 0 /* attributes of the query thread */ pthread_attr_t cn_thd_attrib; @@ -812,10 +867,18 @@ pthread_handler_t connection_thread(void *arg) case EMB_END_CONNECTION: goto end_thread; case EMB_SEND_QUERY: - cn->result= mysql_send_query(&cn->mysql, cn->cur_query, cn->cur_query_len); + cn->result= mysql_send_query(cn->mysql, + cn->cur_query, cn->cur_query_len); break; case EMB_READ_QUERY_RESULT: - cn->result= mysql_read_query_result(&cn->mysql); + cn->result= mysql_read_query_result(cn->mysql); + break; + case EMB_PREPARE_STMT: + cn->result= mysql_stmt_prepare(cn->stmt, + cn->cur_query, cn->cur_query_len); + break; + case EMB_EXECUTE_STMT: + cn->result= mysql_stmt_execute(cn->stmt); break; default: DBUG_ASSERT(0); @@ -868,7 +931,7 @@ static void signal_connection_thd(struct st_connection *cn, int command) static int do_send_query(struct st_connection *cn, const char *q, int q_len) { if (!cn->has_thread) - return mysql_send_query(&cn->mysql, q, q_len); + return mysql_send_query(cn->mysql, q, q_len); cn->cur_query= q; cn->cur_query_len= q_len; signal_connection_thd(cn, EMB_SEND_QUERY); @@ -879,9 +942,37 @@ static int do_read_query_result(struct st_connection *cn) { DBUG_ASSERT(cn->has_thread); wait_query_thread_done(cn); + if (cn->result) + goto exit_func; + signal_connection_thd(cn, EMB_READ_QUERY_RESULT); wait_query_thread_done(cn); +exit_func: + return cn->result; +} + + +static int do_stmt_prepare(struct st_connection *cn, const char *q, int q_len) +{ + /* The cn->stmt is already set. */ + if (!cn->has_thread) + return mysql_stmt_prepare(cn->stmt, q, q_len); + cn->cur_query= q; + cn->cur_query_len= q_len; + signal_connection_thd(cn, EMB_PREPARE_STMT); + wait_query_thread_done(cn); + return cn->result; +} + + +static int do_stmt_execute(struct st_connection *cn) +{ + /* The cn->stmt is already set. */ + if (!cn->has_thread) + return mysql_stmt_execute(cn->stmt); + signal_connection_thd(cn, EMB_EXECUTE_STMT); + wait_query_thread_done(cn); return cn->result; } @@ -916,8 +1007,11 @@ static void init_connection_thd(struct st_connection *cn) #else /*EMBEDDED_LIBRARY*/ -#define do_send_query(cn,q,q_len) mysql_send_query(&cn->mysql, q, q_len) -#define do_read_query_result(cn) mysql_read_query_result(&cn->mysql) +#define init_connection_thd(X) do { } while(0) +#define do_send_query(cn,q,q_len) mysql_send_query(cn->mysql, q, q_len) +#define do_read_query_result(cn) mysql_read_query_result(cn->mysql) +#define do_stmt_prepare(cn, q, q_len) mysql_stmt_prepare(cn->stmt, q, q_len) +#define do_stmt_execute(cn) mysql_stmt_execute(cn->stmt) #endif /*EMBEDDED_LIBRARY*/ @@ -942,7 +1036,10 @@ void do_eval(DYNAMIC_STRING *query_eval, const char *query, else { if (!(v= var_get(p, &p, 0, 0))) - die("Bad variable in eval"); + { + report_or_die( "Bad variable in eval"); + return; + } dynstr_append_mem(query_eval, v->str_val, v->str_val_len); } break; @@ -1219,36 +1316,48 @@ void check_command_args(struct st_command *command, DBUG_VOID_RETURN; } -void handle_command_error(struct st_command *command, uint error) +void handle_command_error(struct st_command *command, uint error, + int sys_errno) { DBUG_ENTER("handle_command_error"); DBUG_PRINT("enter", ("error: %d", error)); + var_set_int("$sys_errno",sys_errno); + var_set_int("$errno",error); if (error != 0) { int i; if (command->abort_on_error) - die("command \"%.*s\" failed with error %d. my_errno=%d", - command->first_word_len, command->query, error, my_errno); + { + report_or_die("command \"%.*s\" failed with error: %u my_errno: %d " + "errno: %d", + command->first_word_len, command->query, error, my_errno, + sys_errno); + DBUG_VOID_RETURN; + } i= match_expected_error(command, error, NULL); if (i >= 0) { - DBUG_PRINT("info", ("command \"%.*s\" failed with expected error: %d", - command->first_word_len, command->query, error)); + DBUG_PRINT("info", ("command \"%.*s\" failed with expected error: %u, errno: %d", + command->first_word_len, command->query, error, + sys_errno)); revert_properties(); DBUG_VOID_RETURN; } if (command->expected_errors.count > 0) - die("command \"%.*s\" failed with wrong error: %d. my_errno=%d", - command->first_word_len, command->query, error, my_errno); + report_or_die("command \"%.*s\" failed with wrong error: %u " + "my_errno: %d errno: %d", + command->first_word_len, command->query, error, my_errno, + sys_errno); } else if (command->expected_errors.err[0].type == ERR_ERRNO && command->expected_errors.err[0].code.errnum != 0) { /* Error code we wanted was != 0, i.e. not an expected success */ - die("command \"%.*s\" succeeded - should have failed with errno %d...", + report_or_die("command \"%.*s\" succeeded - should have failed with " + "errno %d...", command->first_word_len, command->query, command->expected_errors.err[0].code.errnum); } @@ -1268,7 +1377,8 @@ void close_connections() if (next_con->stmt) mysql_stmt_close(next_con->stmt); next_con->stmt= 0; - mysql_close(&next_con->mysql); + mysql_close(next_con->mysql); + next_con->mysql= 0; if (next_con->util_mysql) mysql_close(next_con->util_mysql); my_free(next_con->name); @@ -1312,7 +1422,7 @@ void close_files() void free_used_memory() { uint i; - // Do not use DBUG_ENTER("free_used_memory"); here, see below. + DBUG_ENTER("free_used_memory"); if (connections) close_connections(); @@ -1323,6 +1433,8 @@ void free_used_memory() { struct st_command **q= dynamic_element(&q_lines, i, struct st_command**); my_free((*q)->query_buf); + if ((*q)->eval_query.str) + dynstr_free(&(*q)->eval_query); if ((*q)->content.str) dynstr_free(&(*q)->content); my_free((*q)); @@ -1341,24 +1453,34 @@ void free_used_memory() free_all_replace(); my_free(opt_pass); free_defaults(default_argv); + free_root(&require_file_root, MYF(0)); free_re(); #ifdef __WIN__ free_tmp_sh_file(); free_win_path_patterns(); #endif + DBUG_VOID_RETURN; +} + + +static void cleanup_and_exit(int exit_code) +{ + free_used_memory(); /* Only call mysql_server_end if mysql_server_init has been called */ if (server_initialized) mysql_server_end(); - /* Don't use DBUG after mysql_server_end() */ - return; -} + /* + mysqltest is fundamentally written in a way that makes impossible + to free all memory before exit (consider memory allocated + for frame local DYNAMIC_STRING's and die() invoked down the stack. + We close stderr here to stop unavoidable safemalloc reports + from polluting the output. + */ + fclose(stderr); -static void cleanup_and_exit(int exit_code) -{ - free_used_memory(); my_end(my_end_arg); if (!silent) { @@ -1378,26 +1500,64 @@ static void cleanup_and_exit(int exit_code) } } + sf_leaking_memory= 0; /* all memory should be freed by now */ exit(exit_code); } -void print_file_stack() +size_t print_file_stack(char *s, const char *end) { - for (struct st_test_file* err_file= cur_file; - err_file != file_stack; - err_file--) + char *start= s; + struct st_test_file* err_file= cur_file; + if (err_file == file_stack) + return 0; + + for (;;) { - fprintf(stderr, "included from %s at line %d:\n", - err_file->file_name, err_file->lineno); + err_file--; + s+= my_snprintf(s, end - s, "included from %s at line %d:\n", + err_file->file_name, err_file->lineno); + if (err_file == file_stack) + break; } + return s - start; +} + + +static void make_error_message(char *buf, size_t len, const char *fmt, va_list args) +{ + char *s= buf, *end= buf + len; + s+= my_snprintf(s, end - s, "mysqltest: "); + if (cur_file && cur_file != file_stack) + { + s+= my_snprintf(s, end - s, "In included file \"%s\": \n", + cur_file->file_name); + s+= print_file_stack(s, end); + } + + if (start_lineno > 0) + s+= my_snprintf(s, end -s, "At line %u: ", start_lineno); + if (!fmt) + fmt= "unknown error"; + + s+= my_vsnprintf(s, end - s, fmt, args); + s+= my_snprintf(s, end -s, "\n", start_lineno); } void die(const char *fmt, ...) { - static int dying= 0; + char buff[DIE_BUFF_SIZE]; va_list args; - DBUG_ENTER("die"); - DBUG_PRINT("enter", ("start_lineno: %d", start_lineno)); + va_start(args, fmt); + make_error_message(buff, sizeof(buff), fmt, args); + really_die(buff); +} + +void really_die(const char *msg) +{ + static int dying= 0; + fflush(stdout); + fprintf(stderr, "%s", msg); + fflush(stderr); /* Protect against dying twice @@ -1408,28 +1568,6 @@ void die(const char *fmt, ...) cleanup_and_exit(1); dying= 1; - /* Print the error message */ - fprintf(stderr, "mysqltest: "); - if (cur_file && cur_file != file_stack) - { - fprintf(stderr, "In included file \"%s\": \n", - cur_file->file_name); - print_file_stack(); - } - - if (start_lineno > 0) - fprintf(stderr, "At line %u: ", start_lineno); - if (fmt) - { - va_start(args, fmt); - vfprintf(stderr, fmt, args); - va_end(args); - } - else - fprintf(stderr, "unknown error"); - fprintf(stderr, "\n"); - fflush(stderr); - log_file.show_tail(opt_tail_lines); /* @@ -1437,11 +1575,33 @@ void die(const char *fmt, ...) been produced prior to the error */ if (cur_con && !cur_con->pending) - show_warnings_before_error(&cur_con->mysql); + show_warnings_before_error(cur_con->mysql); cleanup_and_exit(1); } +void report_or_die(const char *fmt, ...) +{ + va_list args; + DBUG_ENTER("report_or_die"); + + char buff[DIE_BUFF_SIZE]; + + va_start(args, fmt); + make_error_message(buff, sizeof(buff), fmt, args); + va_end(args); + + if (opt_continue_on_error) + { + /* Just log the error and continue */ + replace_dynstr_append(&ds_res, buff); + error_count++; + DBUG_VOID_RETURN; + } + + really_die(buff); +} + void abort_not_supported_test(const char *fmt, ...) { @@ -1449,11 +1609,15 @@ void abort_not_supported_test(const char *fmt, ...) DBUG_ENTER("abort_not_supported_test"); /* Print include filestack */ + fflush(stdout); fprintf(stderr, "The test '%s' is not supported by this installation\n", file_stack->file_name); fprintf(stderr, "Detected in file %s at line %d\n", cur_file->file_name, cur_file->lineno); - print_file_stack(); + + char buff[DIE_BUFF_SIZE]; + print_file_stack(buff, buff + sizeof(buff)); + fprintf(stderr, "%s", buff); /* Print error message */ va_start(args, fmt); @@ -1480,9 +1644,12 @@ void verbose_msg(const char *fmt, ...) { va_list args; DBUG_ENTER("verbose_msg"); + DBUG_PRINT("enter", ("format: %s", fmt)); + if (!verbose) DBUG_VOID_RETURN; + fflush(stdout); va_start(args, fmt); fprintf(stderr, "mysqltest: "); if (cur_file && cur_file != file_stack) @@ -1493,6 +1660,7 @@ void verbose_msg(const char *fmt, ...) vfprintf(stderr, fmt, args); fprintf(stderr, "\n"); va_end(args); + fflush(stderr); DBUG_VOID_RETURN; } @@ -1530,12 +1698,12 @@ int cat_file(DYNAMIC_STRING* ds, const char* filename) { int fd; size_t len; - char buff[512]; + char buff[16384]; if ((fd= my_open(filename, O_RDONLY, MYF(0))) < 0) return 1; while((len= my_read(fd, (uchar*)&buff, - sizeof(buff), MYF(0))) > 0) + sizeof(buff)-1, MYF(0))) > 0) { char *p= buff, *start= buff; while (p < buff+len) @@ -1546,7 +1714,8 @@ int cat_file(DYNAMIC_STRING* ds, const char* filename) /* Add fake newline instead of cr and output the line */ *p= '\n'; p++; /* Step past the "fake" newline */ - dynstr_append_mem(ds, start, p-start); + *p= 0; + replace_dynstr_append_mem(ds, start, p-start); p++; /* Step past the "fake" newline */ start= p; } @@ -1554,7 +1723,8 @@ int cat_file(DYNAMIC_STRING* ds, const char* filename) p++; } /* Output any chars that migh be left */ - dynstr_append_mem(ds, start, p-start); + *p= 0; + replace_dynstr_append_mem(ds, start, p-start); } my_close(fd, MYF(0)); return 0; @@ -1577,9 +1747,14 @@ static int run_command(char* cmd, char buf[512]= {0}; FILE *res_file; int error; + DBUG_ENTER("run_command"); + DBUG_PRINT("enter", ("cmd: %s", cmd)); if (!(res_file= popen(cmd, "r"))) - die("popen(\"%s\", \"r\") failed", cmd); + { + report_or_die("popen(\"%s\", \"r\") failed", cmd); + return -1; + } while (fgets(buf, sizeof(buf), res_file)) { @@ -1597,7 +1772,7 @@ static int run_command(char* cmd, } error= pclose(res_file); - return WEXITSTATUS(error); + DBUG_RETURN(WEXITSTATUS(error)); } @@ -1679,7 +1854,10 @@ static int diff_check(const char *diff_name) if (!(res_file= popen(buf, "r"))) die("popen(\"%s\", \"r\") failed", buf); - /* if diff is not present, nothing will be in stdout to increment have_diff */ + /* + if diff is not present, nothing will be in stdout to increment + have_diff + */ if (fgets(buf, sizeof(buf), res_file)) have_diff= 1; @@ -1835,49 +2013,69 @@ enum compare_files_result_enum { */ -int compare_files2(File fd, const char* filename2) +int compare_files2(File fd1, const char* filename2) { int error= RESULT_OK; File fd2; - size_t len, len2; - char buff[512], buff2[512]; + size_t fd1_length, fd2_length; + DYNAMIC_STRING fd1_result, fd2_result; if ((fd2= my_open(filename2, O_RDONLY, MYF(0))) < 0) { - my_close(fd, MYF(0)); + my_close(fd1, MYF(0)); die("Failed to open second file: '%s'", filename2); } - while((len= my_read(fd, (uchar*)&buff, - sizeof(buff), MYF(0))) > 0) - { - if ((len2= my_read(fd2, (uchar*)&buff2, - sizeof(buff2), MYF(0))) < len) - { - /* File 2 was smaller */ - error= RESULT_LENGTH_MISMATCH; - break; - } - if (len2 > len) - { - /* File 1 was smaller */ - error= RESULT_LENGTH_MISMATCH; - break; - } - if ((memcmp(buff, buff2, len))) - { - /* Content of this part differed */ - error= RESULT_CONTENT_MISMATCH; - break; - } + + fd1_length= (size_t) my_seek(fd1, 0, SEEK_END, MYF(0)); + fd2_length= (size_t) my_seek(fd2, 0, SEEK_END, MYF(0)); + + if (init_dynamic_string(&fd1_result, 0, fd1_length, 0) || + init_dynamic_string(&fd2_result, 0, fd2_length, 0)) + die("Out of memory when allocating data for result"); + + fd1_result.length= fd1_length; + fd2_result.length= fd2_length; + + (void) my_seek(fd1, 0, SEEK_SET, MYF(0)); + (void) my_seek(fd2, 0, SEEK_SET, MYF(0)); + if (my_read(fd1, (uchar*) fd1_result.str, fd1_length, MYF(MY_WME | MY_NABP))) + die("Error when reading data from result file"); + if (my_read(fd2, (uchar*) fd2_result.str, fd2_length, MYF(MY_WME | MY_NABP))) + die("Error when reading data from result file"); + + if (global_subst && + (fd1_length != fd2_length || + memcmp(fd1_result.str, fd2_result.str, fd1_length))) + { + /** + @todo MARIA_HACK + This serves for when a test is run with --default-storage-engine=X + where X is not MyISAM: tests using SHOW CREATE TABLE will always fail + because SHOW CREATE TABLE prints X instead of MyISAM. With + --global-subst=X,MyISAM , such trivial differences are eliminated and + test may be reported as passing. + --global-subst is only a quick way to run a lot of existing tests + with Maria and find bugs; it is not good enough for reaching the main + trees when Maria is merged into them. + --global-subst should be removed. + */ + uint global_subst_from_len= strlen(global_subst_from); + uint global_subst_to_len= strlen(global_subst_to); + while (replace(&fd1_result, + global_subst_from, global_subst_from_len, + global_subst_to, global_subst_to_len) == 0) + /* do nothing */ ; + /* let's compare again to see if it is ok now */ } - if (!error && my_read(fd2, (uchar*)&buff2, - sizeof(buff2), MYF(0)) > 0) - { - /* File 1 was smaller */ + + if (fd1_result.length != fd2_result.length) error= RESULT_LENGTH_MISMATCH; - } + else if ((memcmp(fd1_result.str, fd2_result.str, fd1_result.length))) + error= RESULT_CONTENT_MISMATCH; my_close(fd2, MYF(0)); + dynstr_free(&fd1_result); + dynstr_free(&fd2_result); return error; } @@ -1945,7 +2143,7 @@ int dyn_string_cmp(DYNAMIC_STRING* ds, const char *fname) { my_close(fd, MYF(0)); /* Remove the temporary file */ - my_delete(temp_file_path, MYF(0)); + my_delete(temp_file_path, MYF(MY_WME)); die("Failed to write file '%s'", temp_file_path); } @@ -1953,7 +2151,7 @@ int dyn_string_cmp(DYNAMIC_STRING* ds, const char *fname) my_close(fd, MYF(0)); /* Remove the temporary file */ - my_delete(temp_file_path, MYF(0)); + my_delete(temp_file_path, MYF(MY_WME)); DBUG_RETURN(error); } @@ -1972,7 +2170,7 @@ int dyn_string_cmp(DYNAMIC_STRING* ds, const char *fname) void check_result() { - const char* mess= "Result content mismatch\n"; + const char *mess= 0; DBUG_ENTER("check_result"); DBUG_ASSERT(result_file_name); @@ -1980,9 +2178,13 @@ void check_result() switch (compare_files(log_file.file_name(), result_file_name)) { case RESULT_OK: - break; /* ok */ + if (!error_count) + break; /* ok */ + mess= "Got errors while running test"; + /* Fallthrough */ case RESULT_LENGTH_MISMATCH: - mess= "Result length mismatch\n"; + if (!mess) + mess= "Result length mismatch\n"; /* Fallthrough */ case RESULT_CONTENT_MISMATCH: { @@ -1992,12 +2194,16 @@ void check_result() */ char reject_file[FN_REFLEN]; size_t reject_length; + + if (!mess) + mess= "Result content mismatch\n"; + dirname_part(reject_file, result_file_name, &reject_length); if (access(reject_file, W_OK) == 0) { /* Result file directory is writable, save reject file there */ - fn_format(reject_file, result_file_name, NULL, + fn_format(reject_file, result_file_name, "", ".reject", MY_REPLACE_EXT); } else @@ -2091,8 +2297,8 @@ static int strip_surrounding(char* str, char c1, char c2) static void strip_parentheses(struct st_command *command) { if (strip_surrounding(command->first_argument, '(', ')')) - die("%.*s - argument list started with '%c' must be ended with '%c'", - command->first_word_len, command->query, '(', ')'); + die("%.*s - argument list started with '%c' must be ended with '%c'", + command->first_word_len, command->query, '(', ')'); } @@ -2406,11 +2612,24 @@ void var_query_set(VAR *var, const char *query, const char** query_end) *query_end : query + strlen(query)); MYSQL_RES *res; MYSQL_ROW row; - MYSQL* mysql = &cur_con->mysql; + MYSQL* mysql = cur_con->mysql; DYNAMIC_STRING ds_query; DBUG_ENTER("var_query_set"); LINT_INIT(res); + if (!mysql) + { + struct st_command command; + memset(&command, 0, sizeof(command)); + command.query= (char*)query; + command.first_word_len= (*query_end - query); + command.first_argument= command.query + command.first_word_len; + command.end= (char*)*query_end; + command.abort_on_error= 1; /* avoid uninitialized variables */ + handle_no_active_connection(&command, cur_con, &ds_res); + DBUG_VOID_RETURN; + } + /* Only white space or ) allowed past ending ` */ while (end > query && *end != '`') { @@ -2429,8 +2648,8 @@ void var_query_set(VAR *var, const char *query, const char** query_end) if (mysql_real_query(mysql, ds_query.str, ds_query.length)) { - handle_error (curr_command, mysql_errno(mysql), mysql_error(mysql), - mysql_sqlstate(mysql), &ds_res); + handle_error(curr_command, mysql_errno(mysql), mysql_error(mysql), + mysql_sqlstate(mysql), &ds_res); /* If error was acceptable, return empty string */ dynstr_free(&ds_query); eval_expr(var, "", 0); @@ -2438,7 +2657,12 @@ void var_query_set(VAR *var, const char *query, const char** query_end) } if (!(res= mysql_store_result(mysql))) - die("Query '%s' didn't return a result set", ds_query.str); + { + report_or_die("Query '%s' didn't return a result set", ds_query.str); + dynstr_free(&ds_query); + eval_expr(var, "", 0); + return; + } dynstr_free(&ds_query); if ((row= mysql_fetch_row(res)) && row[0]) @@ -2571,7 +2795,7 @@ void var_set_query_get_value(struct st_command *command, VAR *var) long row_no; int col_no= -1; MYSQL_RES* res; - MYSQL* mysql= &cur_con->mysql; + MYSQL* mysql= cur_con->mysql; static DYNAMIC_STRING ds_query; static DYNAMIC_STRING ds_col; @@ -2585,6 +2809,12 @@ void var_set_query_get_value(struct st_command *command, VAR *var) DBUG_ENTER("var_set_query_get_value"); LINT_INIT(res); + if (!mysql) + { + handle_no_active_connection(command, cur_con, &ds_res); + DBUG_VOID_RETURN; + } + strip_parentheses(command); DBUG_PRINT("info", ("query: %s", command->query)); check_command_args(command, command->first_argument, query_get_value_args, @@ -2607,16 +2837,23 @@ void var_set_query_get_value(struct st_command *command, VAR *var) /* Run the query */ if (mysql_real_query(mysql, ds_query.str, ds_query.length)) { - handle_error (curr_command, mysql_errno(mysql), mysql_error(mysql), - mysql_sqlstate(mysql), &ds_res); + handle_error(curr_command, mysql_errno(mysql), mysql_error(mysql), + mysql_sqlstate(mysql), &ds_res); /* If error was acceptable, return empty string */ dynstr_free(&ds_query); + dynstr_free(&ds_col); eval_expr(var, "", 0); DBUG_VOID_RETURN; } if (!(res= mysql_store_result(mysql))) - die("Query '%s' didn't return a result set", ds_query.str); + { + report_or_die("Query '%s' didn't return a result set", ds_query.str); + dynstr_free(&ds_query); + dynstr_free(&ds_col); + eval_expr(var, "", 0); + return; + } { /* Find column number from the given column name */ @@ -2636,8 +2873,11 @@ void var_set_query_get_value(struct st_command *command, VAR *var) if (col_no == -1) { mysql_free_result(res); - die("Could not find column '%s' in the result of '%s'", - ds_col.str, ds_query.str); + report_or_die("Could not find column '%s' in the result of '%s'", + ds_col.str, ds_query.str); + dynstr_free(&ds_query); + dynstr_free(&ds_col); + return; } DBUG_PRINT("info", ("Found column %d with name '%s'", i, fields[i].name)); @@ -2745,6 +2985,7 @@ void eval_expr(VAR *v, const char *p, const char **p_end, command.first_word_len= len; command.first_argument= command.query + len; command.end= (char*)*p_end; + command.abort_on_error= 1; /* avoid uninitialized variables */ var_set_query_get_value(&command, v); DBUG_VOID_RETURN; } @@ -2774,40 +3015,132 @@ void eval_expr(VAR *v, const char *p, const char **p_end, } -int open_file(const char *name) +bool open_and_set_current(const char *name) +{ + FILE *opened= fopen(name, "rb"); + + if (!opened) + return false; + + cur_file++; + cur_file->file= opened; + cur_file->file_name= my_strdup(name, MYF(MY_FAE)); + cur_file->lineno=1; + return true; +} + + +void open_file(const char *name) { char buff[FN_REFLEN]; size_t length; + const char *curname= cur_file->file_name; DBUG_ENTER("open_file"); DBUG_PRINT("enter", ("name: %s", name)); - /* Extract path from current file and try it as base first */ - if (dirname_part(buff, cur_file->file_name, &length)) + if (cur_file == file_stack_end) + die("Source directives are nesting too deep"); + + if (test_if_hard_path(name)) { - strxmov(buff, buff, name, NullS); - if (access(buff, F_OK) == 0){ - DBUG_PRINT("info", ("The file exists")); - name= buff; - } + if (open_and_set_current(name)) + DBUG_VOID_RETURN; } - if (!test_if_hard_path(name)) + else { - strxmov(buff, opt_basedir, name, NullS); - name=buff; - } - fn_format(buff, name, "", "", MY_UNPACK_FILENAME); + /* + if overlay-dir is specified, and the file is located somewhere + under overlay-dir or under suite-dir, the search works as follows: + + 0.let suffix be current file dirname relative to siute-dir or overlay-dir + 1.try in overlay-dir/suffix + 2.try in suite-dir/suffix + 3.try in overlay-dir + 4.try in suite-dir + 5.try in basedir + + consider an example: 'rty' overlay of the 'qwe' suite, + file qwe/include/some.inc contains the line + --source thing.inc + we look for it in this order: + 0.suffix is "include/" + 1.try in rty/include/thing.inc + 2.try in qwe/include/thing.inc + 3.try in try/thing.inc | this is useful when t/a.test has + 4.try in qwe/thing.inc | source include/b.inc; + 5.try in mysql-test/include/thing.inc + + otherwise the search is as follows + 1.try in current file dirname + 3.try in overlay-dir (if any) + 4.try in suite-dir + 5.try in basedir + */ - if (cur_file == file_stack_end) - die("Source directives are nesting too deep"); - cur_file++; - if (!(cur_file->file = fopen(buff, "rb"))) - { - cur_file--; - die("Could not open '%s' for reading, errno: %d", buff, errno); +#ifdef __WIN__ + fix_win_paths(curname, sizeof(curname)); +#endif + + bool in_overlay= opt_overlay_dir && + !strncmp(curname, opt_overlay_dir, overlay_dir_len); + bool in_suiteir= opt_overlay_dir && !in_overlay && + !strncmp(curname, opt_suite_dir, suite_dir_len); + if (in_overlay || in_suiteir) + { + size_t prefix_len = in_overlay ? overlay_dir_len : suite_dir_len; + char buf2[FN_REFLEN], *suffix= buf2 + prefix_len; + dirname_part(buf2, curname, &length); + + /* 1. first we look in the overlay dir */ + strxnmov(buff, sizeof(buff), opt_overlay_dir, suffix, name, NullS); + + /* + Overlayed rty/include/thing.inc can contain the line + --source thing.inc + which would mean to include qwe/include/thing.inc. + But it looks like including "itself", so don't try to open the file, + if buff contains the same file name as curname. + */ + if (strcmp(buff, curname) && open_and_set_current(buff)) + DBUG_VOID_RETURN; + + /* 2. if that failed, we look in the suite dir */ + strxnmov(buff, sizeof(buff), opt_suite_dir, suffix, name, NullS); + + /* buff can not be equal to curname, as a file can never include itself */ + if (open_and_set_current(buff)) + DBUG_VOID_RETURN; + } + else + { + /* 1. try in current file dirname */ + dirname_part(buff, curname, &length); + strxnmov(buff, sizeof(buff), buff, name, NullS); + if (open_and_set_current(buff)) + DBUG_VOID_RETURN; + } + + /* 3. now, look in the overlay dir */ + if (opt_overlay_dir) + { + strxmov(buff, opt_overlay_dir, name, NullS); + if (open_and_set_current(buff)) + DBUG_VOID_RETURN; + } + + /* 4. if that failed - look in the suite dir */ + strxmov(buff, opt_suite_dir, name, NullS); + if (open_and_set_current(buff)) + DBUG_VOID_RETURN; + + /* 5. the last resort - look in the base dir */ + strxnmov(buff, sizeof(buff), opt_basedir, name, NullS); + if (open_and_set_current(buff)) + DBUG_VOID_RETURN; } - cur_file->file_name= my_strdup(buff, MYF(MY_FAE)); - cur_file->lineno=1; - DBUG_RETURN(0); + + die("Could not open '%s' for reading, errno: %d", name, errno); + DBUG_VOID_RETURN; } @@ -2980,6 +3313,7 @@ void do_exec(struct st_command *command) FILE *res_file; char *cmd= command->first_argument; DYNAMIC_STRING ds_cmd; + DYNAMIC_STRING ds_sorted, *ds_result; DBUG_ENTER("do_exec"); DBUG_PRINT("enter", ("cmd: '%s'", cmd)); @@ -2987,7 +3321,10 @@ void do_exec(struct st_command *command) while (*cmd && my_isspace(charset_info, *cmd)) cmd++; if (!*cmd) - die("Missing argument in exec"); + { + report_or_die("Missing argument in exec"); + return; + } command->last_argument= command->end; init_dynamic_string(&ds_cmd, 0, command->query_len+256, 256); @@ -3019,10 +3356,19 @@ void do_exec(struct st_command *command) DBUG_PRINT("info", ("Executing '%s' as '%s'", command->first_argument, ds_cmd.str)); - if (!(res_file= my_popen(&ds_cmd, "r")) && command->abort_on_error) + if (!(res_file= my_popen(&ds_cmd, "r"))) { dynstr_free(&ds_cmd); - die("popen(\"%s\", \"r\") failed", command->first_argument); + if (command->abort_on_error) + report_or_die("popen(\"%s\", \"r\") failed", command->first_argument); + return; + } + + ds_result= &ds_res; + if (display_result_sorted) + { + init_dynamic_string(&ds_sorted, "", 1024, 1024); + ds_result= &ds_sorted; } while (fgets(buf, sizeof(buf), res_file)) @@ -3034,10 +3380,17 @@ void do_exec(struct st_command *command) } else { - replace_dynstr_append(&ds_res, buf); + replace_dynstr_append(ds_result, buf); } } error= pclose(res_file); + + if (display_result_sorted) + { + dynstr_append_sorted(&ds_res, &ds_sorted, 0); + dynstr_free(&ds_sorted); + } + if (error > 0) { uint status= WEXITSTATUS(error); @@ -3045,11 +3398,12 @@ void do_exec(struct st_command *command) if (command->abort_on_error) { - log_msg("exec of '%s' failed, error: %d, status: %d, errno: %d", - ds_cmd.str, error, status, errno); + report_or_die("exec of '%s' failed, error: %d, status: %d, errno: %d\n" + "Output from before failure:\n%s\n", + ds_cmd.str, error, status, errno, + ds_res.str); dynstr_free(&ds_cmd); - die("command \"%s\" failed\n\nOutput from before failure:\n%s\n", - command->first_argument, ds_res.str); + return; } DBUG_PRINT("info", @@ -3064,8 +3418,8 @@ void do_exec(struct st_command *command) { dynstr_free(&ds_cmd); if (command->expected_errors.count > 0) - die("command \"%s\" failed with wrong error: %d", - command->first_argument, status); + report_or_die("command \"%s\" failed with wrong error: %d", + command->first_argument, status); } } else if (command->expected_errors.err[0].type == ERR_ERRNO && @@ -3075,8 +3429,10 @@ void do_exec(struct st_command *command) log_msg("exec of '%s failed, error: %d, errno: %d", ds_cmd.str, error, errno); dynstr_free(&ds_cmd); - die("command \"%s\" succeeded - should have failed with errno %d...", - command->first_argument, command->expected_errors.err[0].code.errnum); + report_or_die("command \"%s\" succeeded - should have failed with " + "errno %d...", + command->first_argument, + command->expected_errors.err[0].code.errnum); } dynstr_free(&ds_cmd); @@ -3110,7 +3466,8 @@ int do_modify_var(struct st_command *command, const char *p= command->first_argument; VAR* v; if (!*p) - die("Missing argument to %.*s", command->first_word_len, command->query); + die("Missing argument to %.*s", command->first_word_len, + command->query); if (*p != '$') die("The argument to %.*s must be a variable (start with $)", command->first_word_len, command->query); @@ -3176,7 +3533,10 @@ void do_system(struct st_command *command) DBUG_ENTER("do_system"); if (strlen(command->first_argument) == 0) - die("Missing arguments to system, nothing to do!"); + { + report_or_die("Missing arguments to system, nothing to do!"); + return; + } init_dynamic_string(&ds_cmd, 0, command->query_len + 64, 256); @@ -3197,12 +3557,14 @@ void do_system(struct st_command *command) if (my_system(&ds_cmd)) { if (command->abort_on_error) - die("system command '%s' failed", command->first_argument); - - /* If ! abort_on_error, log message and continue */ - dynstr_append(&ds_res, "system command '"); - replace_dynstr_append(&ds_res, command->first_argument); - dynstr_append(&ds_res, "' failed\n"); + report_or_die("system command '%s' failed", command->first_argument); + else + { + /* If ! abort_on_error, log message and continue */ + dynstr_append(&ds_res, "system command '"); + replace_dynstr_append(&ds_res, command->first_argument); + dynstr_append(&ds_res, "' failed\n"); + } } command->last_argument= command->end; @@ -3270,8 +3632,8 @@ void do_remove_file(struct st_command *command) ' '); DBUG_PRINT("info", ("removing file: %s", ds_filename.str)); - error= my_delete(ds_filename.str, MYF(0)) != 0; - handle_command_error(command, error); + error= my_delete(ds_filename.str, MYF(disable_warnings ? 0 : MY_WME)) != 0; + handle_command_error(command, error, my_errno); dynstr_free(&ds_filename); DBUG_VOID_RETURN; } @@ -3289,8 +3651,9 @@ void do_remove_file(struct st_command *command) void do_remove_files_wildcard(struct st_command *command) { - int error= 0; + int error= 0, sys_errno= 0; uint i; + size_t directory_length; MY_DIR *dir_info; FILEINFO *file; char dir_separator[2]; @@ -3313,25 +3676,22 @@ void do_remove_files_wildcard(struct st_command *command) DBUG_PRINT("info", ("listing directory: %s", dirname)); /* Note that my_dir sorts the list if not given any flags */ - if (!(dir_info= my_dir(dirname, MYF(MY_DONT_SORT | MY_WANT_STAT)))) + if (!(dir_info= my_dir(dirname, MYF(MY_DONT_SORT | MY_WANT_STAT | MY_WME)))) { error= 1; + sys_errno= my_errno; goto end; } init_dynamic_string(&ds_file_to_remove, dirname, 1024, 1024); dir_separator[0]= FN_LIBCHAR; - dir_separator[1]= 0; - dynstr_append(&ds_file_to_remove, dir_separator); + dynstr_append_mem(&ds_file_to_remove, dir_separator, 1); + directory_length= ds_file_to_remove.length; /* Set default wild chars for wild_compare, is changed in embedded mode */ set_wild_chars(1); - uint length; - /* Storing the length of the path to the file, so it can be reused */ - length= ds_file_to_remove.length; for (i= 0; i < (uint) dir_info->number_off_files; i++) { - ds_file_to_remove.length= length; file= dir_info->dir_entry + i; /* Remove only regular files, i.e. no directories etc. */ /* if (!MY_S_ISREG(file->mystat->st_mode)) */ @@ -3341,13 +3701,11 @@ void do_remove_files_wildcard(struct st_command *command) if (ds_wild.length && wild_compare(file->name, ds_wild.str, 0)) continue; - /* Not required as the var ds_file_to_remove.length already has the - length in canonnicalized form */ - /* ds_file_to_remove.length= ds_directory.length + 1; - ds_file_to_remove.str[ds_directory.length + 1]= 0; */ + ds_file_to_remove.length= directory_length; dynstr_append(&ds_file_to_remove, file->name); DBUG_PRINT("info", ("removing file: %s", ds_file_to_remove.str)); - error= my_delete(ds_file_to_remove.str, MYF(0)) != 0; + if ((error= (my_delete(ds_file_to_remove.str, MYF(MY_WME)) != 0))) + sys_errno= my_errno; if (error) break; } @@ -3355,7 +3713,7 @@ void do_remove_files_wildcard(struct st_command *command) my_dirend(dir_info); end: - handle_command_error(command, error); + handle_command_error(command, error, sys_errno); dynstr_free(&ds_directory); dynstr_free(&ds_wild); dynstr_free(&ds_file_to_remove); @@ -3394,8 +3752,8 @@ void do_copy_file(struct st_command *command) DBUG_PRINT("info", ("Copy %s to %s", ds_from_file.str, ds_to_file.str)); /* MY_HOLD_ORIGINAL_MODES prevents attempts to chown the file */ error= (my_copy(ds_from_file.str, ds_to_file.str, - MYF(MY_DONT_OVERWRITE_FILE | MY_HOLD_ORIGINAL_MODES)) != 0); - handle_command_error(command, error); + MYF(MY_DONT_OVERWRITE_FILE | MY_WME | MY_HOLD_ORIGINAL_MODES)) != 0); + handle_command_error(command, error, my_errno); dynstr_free(&ds_from_file); dynstr_free(&ds_to_file); DBUG_VOID_RETURN; @@ -3430,8 +3788,8 @@ void do_move_file(struct st_command *command) DBUG_PRINT("info", ("Move %s to %s", ds_from_file.str, ds_to_file.str)); error= (my_rename(ds_from_file.str, ds_to_file.str, - MYF(0)) != 0); - handle_command_error(command, error); + MYF(disable_warnings ? 0 : MY_WME)) != 0); + handle_command_error(command, error, my_errno); dynstr_free(&ds_from_file); dynstr_free(&ds_to_file); DBUG_VOID_RETURN; @@ -3475,7 +3833,7 @@ void do_chmod_file(struct st_command *command) err_code= chmod(ds_file.str, mode); if (err_code < 0) err_code= 1; - handle_command_error(command, err_code); + handle_command_error(command, err_code, errno); dynstr_free(&ds_mode); dynstr_free(&ds_file); DBUG_VOID_RETURN; @@ -3508,7 +3866,7 @@ void do_file_exist(struct st_command *command) DBUG_PRINT("info", ("Checking for existence of file: %s", ds_filename.str)); error= (access(ds_filename.str, F_OK) != 0); - handle_command_error(command, error); + handle_command_error(command, error, errno); dynstr_free(&ds_filename); DBUG_VOID_RETURN; } @@ -3538,8 +3896,8 @@ void do_mkdir(struct st_command *command) ' '); DBUG_PRINT("info", ("creating directory: %s", ds_dirname.str)); - error= my_mkdir(ds_dirname.str, 0777, MYF(0)) != 0; - handle_command_error(command, error); + error= my_mkdir(ds_dirname.str, 0777, MYF(MY_WME)) != 0; + handle_command_error(command, error, my_errno); dynstr_free(&ds_dirname); DBUG_VOID_RETURN; } @@ -3559,7 +3917,7 @@ void do_rmdir(struct st_command *command) int error; static DYNAMIC_STRING ds_dirname; const struct command_arg rmdir_args[] = { - {"dirname", ARG_STRING, TRUE, &ds_dirname, "Directory to remove"} + { "dirname", ARG_STRING, TRUE, &ds_dirname, "Directory to remove" } }; DBUG_ENTER("do_rmdir"); @@ -3569,7 +3927,7 @@ void do_rmdir(struct st_command *command) DBUG_PRINT("info", ("removing directory: %s", ds_dirname.str)); error= rmdir(ds_dirname.str) != 0; - handle_command_error(command, error); + handle_command_error(command, error, errno); dynstr_free(&ds_dirname); DBUG_VOID_RETURN; } @@ -3646,7 +4004,7 @@ static void do_list_files(struct st_command *command) sizeof(list_files_args)/sizeof(struct command_arg), ' '); error= get_list_files(&ds_res, &ds_dirname, &ds_wild); - handle_command_error(command, error); + handle_command_error(command, error, my_errno); dynstr_free(&ds_dirname); dynstr_free(&ds_wild); DBUG_VOID_RETURN; @@ -3689,7 +4047,7 @@ static void do_list_files_write_file_command(struct st_command *command, init_dynamic_string(&ds_content, "", 1024, 1024); error= get_list_files(&ds_content, &ds_dirname, &ds_wild); - handle_command_error(command, error); + handle_command_error(command, error, my_errno); str_to_file2(ds_filename.str, ds_content.str, ds_content.length, append); dynstr_free(&ds_content); dynstr_free(&ds_filename); @@ -3753,12 +4111,12 @@ void read_until_delimiter(DYNAMIC_STRING *ds, No characters except \n are allowed on the same line as the command */ - die("Trailing characters found after command"); + report_or_die("Trailing characters found after command"); } if (feof(cur_file->file)) - die("End of file encountered before '%s' delimiter was found", - ds_delimiter->str); + report_or_die("End of file encountered before '%s' delimiter was found", + ds_delimiter->str); if (match_delimiter(c, ds_delimiter->str, ds_delimiter->length)) { @@ -3913,7 +4271,7 @@ void do_cat_file(struct st_command *command) DBUG_PRINT("info", ("Reading from, file: %s", ds_filename.str)); error= cat_file(&ds_res, ds_filename.str); - handle_command_error(command, error); + handle_command_error(command, error, my_errno); dynstr_free(&ds_filename); DBUG_VOID_RETURN; } @@ -3959,9 +4317,10 @@ void do_diff_files(struct st_command *command) if ((error= compare_files(ds_filename.str, ds_filename2.str)) && match_expected_error(command, error, NULL) < 0) { - /* Compare of the two files failed, append them to output - so the failure can be analyzed, but only if it was not - expected to fail. + /* + Compare of the two files failed, append them to output + so the failure can be analyzed, but only if it was not + expected to fail. */ show_diff(&ds_res, ds_filename.str, ds_filename2.str); log_file.write(&ds_res); @@ -3971,7 +4330,7 @@ void do_diff_files(struct st_command *command) dynstr_free(&ds_filename); dynstr_free(&ds_filename2); - handle_command_error(command, error); + handle_command_error(command, error, -1); DBUG_VOID_RETURN; } @@ -4021,7 +4380,7 @@ void do_send_quit(struct st_command *command) if (!(con= find_connection_by_name(name))) die("connection '%s' not found in connection pool", name); - simple_command(&con->mysql,COM_QUIT,0,0,1); + simple_command(con->mysql,COM_QUIT,0,0,1); DBUG_VOID_RETURN; } @@ -4045,7 +4404,8 @@ void do_send_quit(struct st_command *command) void do_change_user(struct st_command *command) { - MYSQL *mysql = &cur_con->mysql; + MYSQL *mysql = cur_con->mysql; + /* static keyword to make the NetWare compiler happy. */ static DYNAMIC_STRING ds_user, ds_passwd, ds_db; const struct command_arg change_user_args[] = { { "user", ARG_STRING, FALSE, &ds_user, "User to connect as" }, @@ -4081,7 +4441,10 @@ void do_change_user(struct st_command *command) cur_con->name, ds_user.str, ds_passwd.str, ds_db.str)); if (mysql_change_user(mysql, ds_user.str, ds_passwd.str, ds_db.str)) - die("change user failed: %s", mysql_error(mysql)); + handle_error(command, mysql_errno(mysql), mysql_error(mysql), + mysql_sqlstate(mysql), &ds_res); + else + handle_no_error(command); dynstr_free(&ds_user); dynstr_free(&ds_passwd); @@ -4160,8 +4523,13 @@ void do_perl(struct st_command *command) /* Format the "perl <filename>" command */ my_snprintf(buf, sizeof(buf), "perl %s", temp_file_path); - if (!(res_file= popen(buf, "r")) && command->abort_on_error) - die("popen(\"%s\", \"r\") failed", buf); + if (!(res_file= popen(buf, "r"))) + { + if (command->abort_on_error) + die("popen(\"%s\", \"r\") failed", buf); + dynstr_free(&ds_delimiter); + return; + } while (fgets(buf, sizeof(buf), res_file)) { @@ -4179,7 +4547,7 @@ void do_perl(struct st_command *command) /* Remove the temporary file, but keep it if perl failed */ if (!error) - my_delete(temp_file_path, MYF(0)); + my_delete(temp_file_path, MYF(MY_WME)); /* Check for error code that indicates perl could not be started */ int exstat= WEXITSTATUS(error); @@ -4192,7 +4560,7 @@ void do_perl(struct st_command *command) abort_not_supported_test("perl not found in path"); #endif else - handle_command_error(command, exstat); + handle_command_error(command, exstat, my_errno); } dynstr_free(&ds_delimiter); DBUG_VOID_RETURN; @@ -4241,7 +4609,7 @@ int do_echo(struct st_command *command) void do_wait_for_slave_to_stop(struct st_command *c __attribute__((unused))) { static int SLAVE_POLL_INTERVAL= 300000; - MYSQL* mysql = &cur_con->mysql; + MYSQL* mysql = cur_con->mysql; for (;;) { MYSQL_RES *UNINIT_VAR(res); @@ -4271,7 +4639,7 @@ void do_sync_with_master2(struct st_command *command, long offset) { MYSQL_RES *res; MYSQL_ROW row; - MYSQL *mysql= &cur_con->mysql; + MYSQL *mysql= cur_con->mysql; char query_buf[FN_REFLEN+128]; int timeout= 300; /* seconds */ @@ -4316,14 +4684,14 @@ void do_sync_with_master2(struct st_command *command, long offset) information is not initialized, the arguments are incorrect, or an error has occured */ - die("%.*s failed: '%s' returned NULL "\ + die("%.*s failed: '%s' returned NULL " \ "indicating slave SQL thread failure", command->first_word_len, command->query, query_buf); } if (result == -1) - die("%.*s failed: '%s' returned -1 "\ + die("%.*s failed: '%s' returned -1 " \ "indicating timeout after %d seconds", command->first_word_len, command->query, query_buf, timeout); else @@ -4361,7 +4729,7 @@ int do_save_master_pos() { MYSQL_RES *res; MYSQL_ROW row; - MYSQL *mysql = &cur_con->mysql; + MYSQL *mysql = cur_con->mysql; const char *query; DBUG_ENTER("do_save_master_pos"); @@ -4618,7 +4986,8 @@ int do_sleep(struct st_command *command, my_bool real_sleep) while (my_isspace(charset_info, *p)) p++; if (!*p) - die("Missing argument to %.*s", command->first_word_len, command->query); + die("Missing argument to %.*s", command->first_word_len, + command->query); sleep_start= p; /* Check that arg starts with a digit, not handled by my_strtod */ if (!my_isdigit(charset_info, *sleep_start)) @@ -4690,16 +5059,21 @@ int query_get_string(MYSQL* mysql, const char* query, MYSQL_ROW row; if (mysql_query(mysql, query)) - die("'%s' failed: %d %s", query, - mysql_errno(mysql), mysql_error(mysql)); + { + report_or_die("'%s' failed: %d %s", query, + mysql_errno(mysql), mysql_error(mysql)); + return 1; + } if ((res= mysql_store_result(mysql)) == NULL) - die("Failed to store result: %d %s", - mysql_errno(mysql), mysql_error(mysql)); + { + report_or_die("Failed to store result: %d %s", + mysql_errno(mysql), mysql_error(mysql)); + return 1; + } if ((row= mysql_fetch_row(res)) == NULL) { mysql_free_result(res); - ds= 0; return 1; } init_dynamic_string(ds, (row[column] ? row[column] : "NULL"), ~0, 32); @@ -4712,12 +5086,13 @@ static int my_kill(int pid, int sig) { #ifdef __WIN__ HANDLE proc; - if ((proc= OpenProcess(PROCESS_TERMINATE, FALSE, pid)) == NULL) + if ((proc= OpenProcess(SYNCHRONIZE|PROCESS_TERMINATE, FALSE, pid)) == NULL) return -1; if (sig == 0) { + DWORD wait_result= WaitForSingleObject(proc, 0); CloseHandle(proc); - return 0; + return wait_result == WAIT_OBJECT_0?-1:0; } (void)TerminateProcess(proc, 201); CloseHandle(proc); @@ -4749,7 +5124,7 @@ void do_shutdown_server(struct st_command *command) long timeout=60; int pid; DYNAMIC_STRING ds_pidfile_name; - MYSQL* mysql = &cur_con->mysql; + MYSQL* mysql = cur_con->mysql; static DYNAMIC_STRING ds_timeout; const struct command_arg shutdown_args[] = { {"timeout", ARG_STRING, FALSE, &ds_timeout, "Timeout before killing server"} @@ -4834,11 +5209,17 @@ static st_error global_error_names[] = { 0, 0, 0 } }; -uint get_errcode_from_name(char *error_name, char *error_end) +#include <my_base.h> +static st_error handler_error_names[] = { - /* SQL error as string */ - st_error *e= global_error_names; + { "<No error>", UINT_MAX, "" }, +#include <handler_ername.h> + { 0, 0, 0 } +}; +uint get_errcode_from_name(const char *error_name, const char *error_end, + st_error *e) +{ DBUG_ENTER("get_errcode_from_name"); DBUG_PRINT("enter", ("error_name: %s", error_name)); @@ -4856,15 +5237,26 @@ uint get_errcode_from_name(char *error_name, char *error_end) DBUG_RETURN(e->code); } } - if (!e->name) - die("Unknown SQL error name '%s'", error_name); DBUG_RETURN(0); } -const char *get_errname_from_code (uint error_code) + +uint get_errcode_from_name(const char *error_name, const char *error_end) { - st_error *e= global_error_names; + uint tmp; + if ((tmp= get_errcode_from_name(error_name, error_end, + global_error_names))) + return tmp; + if ((tmp= get_errcode_from_name(error_name, error_end, + handler_error_names))) + return tmp; + die("Unknown SQL error name '%s'", error_name); +} +const char *unknown_error= "<Unknown>"; + +const char *get_errname_from_code (uint error_code, st_error *e) +{ DBUG_ENTER("get_errname_from_code"); DBUG_PRINT("enter", ("error_code: %d", error_code)); @@ -4880,21 +5272,47 @@ const char *get_errname_from_code (uint error_code) } } /* Apparently, errors without known names may occur */ - DBUG_RETURN("<Unknown>"); + DBUG_RETURN(unknown_error); } +const char *get_errname_from_code(uint error_code) +{ + const char *name; + if ((name= get_errname_from_code(error_code, global_error_names)) != + unknown_error) + return name; + return get_errname_from_code(error_code, handler_error_names); +} + void do_get_errcodes(struct st_command *command) { struct st_match_err *to= saved_expected_errors.err; - char *p= command->first_argument; - uint count= 0; - char *next; - DBUG_ENTER("do_get_errcodes"); - if (!*p) + if (!*command->first_argument) die("Missing argument(s) to 'error'"); + /* TODO: Potentially, there is a possibility of variables + being expanded twice, e.g. + + let $errcodes = 1,\$a; + let $a = 1051; + error $errcodes; + DROP TABLE unknown_table; + ... + Got one of the listed errors + + But since it requires manual escaping, it does not seem + particularly dangerous or error-prone. + */ + DYNAMIC_STRING ds; + init_dynamic_string(&ds, 0, command->query_len + 64, 256); + do_eval(&ds, command->first_argument, command->end, !is_windows); + char *p= ds.str; + + uint count= 0; + char *next; + do { char *end; @@ -4950,7 +5368,7 @@ void do_get_errcodes(struct st_command *command) { die("The sqlstate definition must start with an uppercase S"); } - else if (*p == 'E') + else if (*p == 'E' || *p == 'H') { /* Error name string */ @@ -4959,9 +5377,9 @@ void do_get_errcodes(struct st_command *command) to->type= ERR_ERRNO; DBUG_PRINT("info", ("ERR_ERRNO: %d", to->code.errnum)); } - else if (*p == 'e') + else if (*p == 'e' || *p == 'h') { - die("The error name definition must start with an uppercase E"); + die("The error name definition must start with an uppercase E or H"); } else { @@ -4971,7 +5389,7 @@ void do_get_errcodes(struct st_command *command) while (*p && p != end) { if (!my_isdigit(charset_info, *p)) - die("Invalid argument to error: '%s' - "\ + die("Invalid argument to error: '%s' - " \ "the errno may only consist of digits[0-9]", command->first_argument); p++; @@ -5004,11 +5422,15 @@ void do_get_errcodes(struct st_command *command) } while (*p); - command->last_argument= p; + command->last_argument= command->first_argument; + while (*command->last_argument) + command->last_argument++; + to->type= ERR_EMPTY; /* End of data */ DBUG_PRINT("info", ("Expected errors: %d", count)); saved_expected_errors.count= count; + dynstr_free(&ds); DBUG_VOID_RETURN; } @@ -5093,11 +5515,11 @@ char *get_string(char **to_ptr, char **from_ptr, } -void set_reconnect(MYSQL* mysql, int val) +void set_reconnect(MYSQL* mysql, my_bool val) { my_bool reconnect= val; DBUG_ENTER("set_reconnect"); - DBUG_PRINT("info", ("val: %d", val)); + DBUG_PRINT("info", ("val: %d", (int) val)); #if MYSQL_VERSION_ID < 50000 mysql->reconnect= reconnect; #else @@ -5116,7 +5538,7 @@ void set_current_connection(struct st_connection *con) cur_con= con; /* Update $mysql_get_server_version to that of current connection */ var_set_int("$mysql_get_server_version", - mysql_get_server_version(&con->mysql)); + mysql_get_server_version(con->mysql)); /* Update $CURRENT_CONNECTION to the name of the current connection */ var_set_string("$CURRENT_CONNECTION", con->name); } @@ -5167,7 +5589,7 @@ void select_connection(struct st_command *command) void do_close_connection(struct st_command *command) { - DBUG_ENTER("close_connection"); + DBUG_ENTER("do_close_connection"); struct st_connection *con; static DYNAMIC_STRING ds_connection; @@ -5189,10 +5611,10 @@ void do_close_connection(struct st_command *command) #ifndef EMBEDDED_LIBRARY if (command->type == Q_DIRTY_CLOSE) { - if (con->mysql.net.vio) + if (con->mysql->net.vio) { - vio_delete(con->mysql.net.vio); - con->mysql.net.vio = 0; + vio_delete(con->mysql->net.vio); + con->mysql->net.vio = 0; } } #else @@ -5207,7 +5629,8 @@ void do_close_connection(struct st_command *command) mysql_stmt_close(con->stmt); con->stmt= 0; - mysql_close(&con->mysql); + mysql_close(con->mysql); + con->mysql= 0; if (con->util_mysql) mysql_close(con->util_mysql); @@ -5281,9 +5704,8 @@ void safe_connect(MYSQL* mysql, const char *name, const char *host, verbose_msg("Connecting to server %s:%d (socket %s) as '%s'" ", connection '%s', attempt %d ...", host, port, sock, user, name, failed_attempts); - while(!mysql_connect_ssl_check(mysql, host,user, pass, db, port, sock, - CLIENT_MULTI_STATEMENTS | CLIENT_REMEMBER_OPTIONS, - opt_ssl_required)) + while(!mysql_real_connect(mysql, host,user, pass, db, port, sock, + CLIENT_MULTI_STATEMENTS | CLIENT_REMEMBER_OPTIONS)) { /* Connect failed @@ -5383,9 +5805,8 @@ int connect_n_handle_errors(struct st_command *command, dynstr_append_mem(ds, ";\n", 2); } - while (!mysql_connect_ssl_check(con, host, user, pass, db, port, - sock ? sock: 0, CLIENT_MULTI_STATEMENTS, - opt_ssl_required)) + while (!mysql_real_connect(con, host, user, pass, db, port, sock ? sock: 0, + CLIENT_MULTI_STATEMENTS)) { /* If we have used up all our connections check whether this @@ -5463,8 +5884,10 @@ void do_connect(struct st_command *command) { int con_port= opt_port; char *con_options; + char *ssl_cipher __attribute__((unused))= 0; my_bool con_ssl= 0, con_compress= 0; - my_bool con_pipe= 0, con_shm= 0, con_cleartext_enable= 0; + my_bool con_pipe= 0; + my_bool con_shm __attribute__ ((unused))= 0; struct st_connection* con_slot; static DYNAMIC_STRING ds_connection_name; @@ -5538,7 +5961,8 @@ void do_connect(struct st_command *command) con_options= ds_options.str; while (*con_options) { - char* end; + size_t length; + char *end; /* Step past any spaces in beginning of option*/ while (*con_options && my_isspace(charset_info, *con_options)) con_options++; @@ -5546,16 +5970,20 @@ void do_connect(struct st_command *command) end= con_options; while (*end && !my_isspace(charset_info, *end)) end++; - if (!strncmp(con_options, "SSL", 3)) + length= (size_t) (end - con_options); + if (length == 3 && !strncmp(con_options, "SSL", 3)) con_ssl= 1; - else if (!strncmp(con_options, "COMPRESS", 8)) + else if (!strncmp(con_options, "SSL-CIPHER=", 11)) + { + con_ssl= 1; + ssl_cipher=con_options + 11; + } + else if (length == 8 && !strncmp(con_options, "COMPRESS", 8)) con_compress= 1; - else if (!strncmp(con_options, "PIPE", 4)) + else if (length == 4 && !strncmp(con_options, "PIPE", 4)) con_pipe= 1; - else if (!strncmp(con_options, "SHM", 3)) + else if (length == 3 && !strncmp(con_options, "SHM", 3)) con_shm= 1; - else if (!strncmp(con_options, "CLEARTEXT", 9)) - con_cleartext_enable= 1; else die("Illegal option to connect: %.*s", (int) (end - con_options), con_options); @@ -5573,28 +6001,30 @@ void do_connect(struct st_command *command) if (!(con_slot= find_connection_by_name("-closed_connection-"))) die("Connection limit exhausted, you can have max %d connections", opt_max_connections); + my_free(con_slot->name); + con_slot->name= 0; } -#ifdef EMBEDDED_LIBRARY init_connection_thd(con_slot); -#endif /*EMBEDDED_LIBRARY*/ - if (!mysql_init(&con_slot->mysql)) + if (!(con_slot->mysql= mysql_init(0))) die("Failed on mysql_init()"); if (opt_connect_timeout) - mysql_options(&con_slot->mysql, MYSQL_OPT_CONNECT_TIMEOUT, + mysql_options(con_slot->mysql, MYSQL_OPT_CONNECT_TIMEOUT, (void *) &opt_connect_timeout); - +#ifndef MY_CONTEXT_DISABLE + if (mysql_options(con_slot->mysql, MYSQL_OPT_NONBLOCK, 0)) + die("Failed to initialise non-blocking API"); +#endif if (opt_compress || con_compress) - mysql_options(&con_slot->mysql, MYSQL_OPT_COMPRESS, NullS); - mysql_options(&con_slot->mysql, MYSQL_OPT_LOCAL_INFILE, 0); - mysql_options(&con_slot->mysql, MYSQL_SET_CHARSET_NAME, + mysql_options(con_slot->mysql, MYSQL_OPT_COMPRESS, NullS); + mysql_options(con_slot->mysql, MYSQL_OPT_LOCAL_INFILE, 0); + mysql_options(con_slot->mysql, MYSQL_SET_CHARSET_NAME, charset_info->csname); if (opt_charsets_dir) - mysql_options(&con_slot->mysql, MYSQL_SET_CHARSET_DIR, + mysql_options(con_slot->mysql, MYSQL_SET_CHARSET_DIR, opt_charsets_dir); - #if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) if (opt_use_ssl) con_ssl= 1; @@ -5603,12 +6033,12 @@ void do_connect(struct st_command *command) if (con_ssl) { #if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) - mysql_ssl_set(&con_slot->mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca, - opt_ssl_capath, opt_ssl_cipher); + mysql_ssl_set(con_slot->mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca, + opt_ssl_capath, ssl_cipher ? ssl_cipher : opt_ssl_cipher); #if MYSQL_VERSION_ID >= 50000 /* Turn on ssl_verify_server_cert only if host is "localhost" */ opt_ssl_verify_server_cert= !strcmp(ds_host.str, "localhost"); - mysql_options(&con_slot->mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, + mysql_options(con_slot->mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, &opt_ssl_verify_server_cert); #endif #endif @@ -5622,22 +6052,20 @@ void do_connect(struct st_command *command) } if (opt_protocol) - mysql_options(&con_slot->mysql, MYSQL_OPT_PROTOCOL, (char*) &opt_protocol); + mysql_options(con_slot->mysql, MYSQL_OPT_PROTOCOL, (char*) &opt_protocol); +#ifdef HAVE_SMEM if (con_shm) { -#ifdef HAVE_SMEM uint protocol= MYSQL_PROTOCOL_MEMORY; if (!ds_shm.length) die("Missing shared memory base name"); - mysql_options(&con_slot->mysql, MYSQL_SHARED_MEMORY_BASE_NAME, ds_shm.str); - mysql_options(&con_slot->mysql, MYSQL_OPT_PROTOCOL, &protocol); -#endif + mysql_options(con_slot->mysql, MYSQL_SHARED_MEMORY_BASE_NAME, ds_shm.str); + mysql_options(con_slot->mysql, MYSQL_OPT_PROTOCOL, &protocol); } -#ifdef HAVE_SMEM else if (shared_memory_base_name) { - mysql_options(&con_slot->mysql, MYSQL_SHARED_MEMORY_BASE_NAME, + mysql_options(con_slot->mysql, MYSQL_SHARED_MEMORY_BASE_NAME, shared_memory_base_name); } #endif @@ -5647,20 +6075,16 @@ void do_connect(struct st_command *command) dynstr_set(&ds_database, opt_db); if (opt_plugin_dir && *opt_plugin_dir) - mysql_options(&con_slot->mysql, MYSQL_PLUGIN_DIR, opt_plugin_dir); + mysql_options(con_slot->mysql, MYSQL_PLUGIN_DIR, opt_plugin_dir); if (ds_default_auth.length) - mysql_options(&con_slot->mysql, MYSQL_DEFAULT_AUTH, ds_default_auth.str); - + mysql_options(con_slot->mysql, MYSQL_DEFAULT_AUTH, ds_default_auth.str); - if (con_cleartext_enable) - mysql_options(&con_slot->mysql, MYSQL_ENABLE_CLEARTEXT_PLUGIN, - (char*) &con_cleartext_enable); /* Special database to allow one to connect without a database name */ if (ds_database.length && !strcmp(ds_database.str,"*NO-ONE*")) dynstr_set(&ds_database, ""); - if (connect_n_handle_errors(command, &con_slot->mysql, + if (connect_n_handle_errors(command, con_slot->mysql, ds_host.str,ds_user.str, ds_password.str, ds_database.str, con_port, ds_sock.str)) @@ -5900,7 +6324,7 @@ void do_block(enum block_cmd cmd, struct st_command* command) eval_expr(&v2, curr_ptr, &expr_end); if ((operand!=EQ_OP && operand!=NE_OP) && ! (v.is_int && v2.is_int)) - die ("Only == and != are supported for string values"); + die("Only == and != are supported for string values"); /* Now we overwrite the first variable with 0 or 1 (for false or true) */ @@ -5994,8 +6418,7 @@ void do_delimiter(struct st_command* command) if (!(*p)) die("Can't set empty delimiter"); - strmake(delimiter, p, sizeof(delimiter) - 1); - delimiter_length= strlen(delimiter); + delimiter_length= strmake_buf(delimiter, p) - delimiter; DBUG_PRINT("exit", ("delimiter: %s", delimiter)); command->last_argument= p + delimiter_length; @@ -6258,7 +6681,7 @@ int read_line(char *buf, int size) *p++= c; } } - die("The input buffer is too small for this query.x\n" \ + die("The input buffer is too small for this query.x\n" \ "check your query or increase MAX_QUERY and recompile"); DBUG_RETURN(0); } @@ -6485,13 +6908,19 @@ static struct my_option my_long_options[] = 0, 0, 0, 0, 0, 0}, {"basedir", 'b', "Basedir for tests.", &opt_basedir, &opt_basedir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"character-sets-dir", OPT_CHARSETS_DIR, + {"character-sets-dir", 0, "Directory for character set files.", &opt_charsets_dir, &opt_charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"compress", 'C', "Use the compressed server/client protocol.", &opt_compress, &opt_compress, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"cursor-protocol", OPT_CURSOR_PROTOCOL, "Use cursors for prepared statements.", + {"continue-on-error", 0, + "Continue test even if we got an error. " + "This is mostly useful when testing a storage engine to see what from a test file it can execute, " + "or to find all syntax errors in a newly created big test file", + &opt_continue_on_error, &opt_continue_on_error, 0, + GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"cursor-protocol", 0, "Use cursors for prepared statements.", &cursor_protocol, &cursor_protocol, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"database", 'D', "Database to use.", &opt_db, &opt_db, 0, @@ -6503,30 +6932,30 @@ static struct my_option my_long_options[] = {"debug", '#', "Output debug log. Often this is 'd:t:o,filename'.", 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, #endif - {"debug-check", OPT_DEBUG_CHECK, "Check memory and open file usage at exit.", + {"debug-check", 0, "Check memory and open file usage at exit.", &debug_check_flag, &debug_check_flag, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"debug-info", OPT_DEBUG_INFO, "Print some debug info at exit.", + {"debug-info", 0, "Print some debug info at exit.", &debug_info_flag, &debug_info_flag, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"host", 'h', "Connect to host.", &opt_host, &opt_host, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"include", 'i', "Include SQL before each test case.", &opt_include, - &opt_include, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"prologue", 0, "Include SQL before each test case.", &opt_prologue, + &opt_prologue, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"logdir", OPT_LOG_DIR, "Directory for log files", &opt_logdir, &opt_logdir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"mark-progress", OPT_MARK_PROGRESS, + {"mark-progress", 0, "Write line number and elapsed time to <testname>.progress.", &opt_mark_progress, &opt_mark_progress, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"max-connect-retries", OPT_MAX_CONNECT_RETRIES, + {"max-connect-retries", 0, "Maximum number of attempts to connect to server.", &opt_max_connect_retries, &opt_max_connect_retries, 0, GET_INT, REQUIRED_ARG, 500, 1, 10000, 0, 0, 0}, - {"max-connections", OPT_MAX_CONNECTIONS, + {"max-connections", 0, "Max number of open connections to server", &opt_max_connections, &opt_max_connections, 0, - GET_INT, REQUIRED_ARG, 128, 8, 5120, 0, 0, 0}, + GET_INT, REQUIRED_ARG, DEFAULT_MAX_CONN, 8, 5120, 0, 0, 0}, {"password", 'p', "Password to use when connecting to server.", 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, {"protocol", OPT_MYSQL_PROTOCOL, "The protocol of connection (tcp,socket,pipe,memory).", @@ -6538,10 +6967,14 @@ static struct my_option my_long_options[] = #endif "built-in default (" STRINGIFY_ARG(MYSQL_PORT) ").", &opt_port, &opt_port, 0, GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"ps-protocol", OPT_PS_PROTOCOL, + {"ps-protocol", 0, "Use prepared-statement protocol for communication.", &ps_protocol, &ps_protocol, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"non-blocking-api", 0, + "Use the non-blocking client API for communication.", + &non_blocking_api_enabled, &non_blocking_api_enabled, 0, + GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"quiet", 's', "Suppress all normal output.", &silent, &silent, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"record", 'r', "Record output of test_file into result file.", @@ -6558,7 +6991,7 @@ static struct my_option my_long_options[] = 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"server-file", 'F', "Read embedded server arguments from file.", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"shared-memory-base-name", OPT_SHARED_MEMORY_BASE_NAME, + {"shared-memory-base-name", 0, "Base name of shared memory.", &shared_memory_base_name, &shared_memory_base_name, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, @@ -6570,11 +7003,11 @@ static struct my_option my_long_options[] = {"socket", 'S', "The socket file to use for connection.", &unix_sock, &unix_sock, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"sp-protocol", OPT_SP_PROTOCOL, "Use stored procedures for select.", + {"sp-protocol", 0, "Use stored procedures for select.", &sp_protocol, &sp_protocol, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, #include "sslopt-longopts.h" - {"tail-lines", OPT_TAIL_LINES, + {"tail-lines", 0, "Number of lines of the result to include in a failure report.", &opt_tail_lines, &opt_tail_lines, 0, GET_INT, REQUIRED_ARG, 0, 0, 10000, 0, 0, 0}, @@ -6590,16 +7023,20 @@ static struct my_option my_long_options[] = GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"version", 'V', "Output version information and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"view-protocol", OPT_VIEW_PROTOCOL, "Use views for select.", + {"view-protocol", 0, "Use views for select.", &view_protocol, &view_protocol, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"connect_timeout", OPT_CONNECT_TIMEOUT, + {"connect_timeout", 0, "Number of seconds before connection timeout.", &opt_connect_timeout, &opt_connect_timeout, 0, GET_UINT, REQUIRED_ARG, 120, 0, 3600 * 12, 0, 0, 0}, - {"plugin_dir", OPT_PLUGIN_DIR, "Directory for client-side plugins.", + {"plugin_dir", 0, "Directory for client-side plugins.", &opt_plugin_dir, &opt_plugin_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"overlay-dir", 0, "Overlay directory.", &opt_overlay_dir, + &opt_overlay_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"suite-dir", 0, "Suite directory.", &opt_suite_dir, + &opt_suite_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} }; @@ -6616,8 +7053,9 @@ void usage() puts(ORACLE_WELCOME_COPYRIGHT_NOTICE("2000")); printf("Runs a test against the mysql server and compares output with a results file.\n\n"); printf("Usage: %s [OPTIONS] [database] < test_file\n", my_progname); + print_defaults("my",load_default_groups); + puts(""); my_print_help(my_long_options); - printf(" --no-defaults Don't read default options from any options file.\n"); my_print_variables(my_long_options); } @@ -6676,6 +7114,7 @@ get_one_option(int optid, const struct my_option *opt, char *argument) #ifndef DBUG_OFF DBUG_PUSH(argument ? argument : "d:t:S:i:O,/tmp/mysqltest.trace"); debug_check_flag= 1; + debug_info_flag= 1; #endif break; case 'r': @@ -6791,14 +7230,29 @@ int parse_args(int argc, char **argv) if (debug_info_flag) my_end_arg= MY_CHECK_ERROR | MY_GIVE_INFO; if (debug_check_flag) - my_end_arg= MY_CHECK_ERROR; + my_end_arg|= MY_CHECK_ERROR; + + if (global_subst != NULL) + { + char *comma= strstr(global_subst, ","); + if (comma == NULL) + die("wrong --global-subst, must be X,Y"); + memcpy(global_subst_from, global_subst, (comma-global_subst)); + global_subst_from[comma-global_subst]= 0; + memcpy(global_subst_to, comma+1, strlen(comma)); + } + if (!opt_suite_dir) + opt_suite_dir= "./"; + suite_dir_len= strlen(opt_suite_dir); + overlay_dir_len= opt_overlay_dir ? strlen(opt_overlay_dir) : 0; if (!record) { /* Check that the result file exists */ if (result_file_name && access(result_file_name, F_OK) != 0) - die("The specified result file '%s' does not exist", result_file_name); + die("The specified result file '%s' does not exist", + result_file_name); } return 0; @@ -7087,6 +7541,7 @@ void append_stmt_result(DYNAMIC_STRING *ds, MYSQL_STMT *stmt, my_bool *is_null; ulong *length; uint i; + int error; /* Allocate array with bind structs, lengths and NULL flags */ my_bind= (MYSQL_BIND*) my_malloc(num_fields * sizeof(MYSQL_BIND), @@ -7114,7 +7569,7 @@ void append_stmt_result(DYNAMIC_STRING *ds, MYSQL_STMT *stmt, die("mysql_stmt_bind_result failed: %d: %s", mysql_stmt_errno(stmt), mysql_stmt_error(stmt)); - while (mysql_stmt_fetch(stmt) == 0) + while ((error=mysql_stmt_fetch(stmt)) == 0) { for (i= 0; i < num_fields; i++) append_field(ds, i, &fields[i], (char*)my_bind[i].buffer, @@ -7123,8 +7578,11 @@ void append_stmt_result(DYNAMIC_STRING *ds, MYSQL_STMT *stmt, dynstr_append_mem(ds, "\n", 1); } + if (error != MYSQL_NO_DATA) + die("mysql_fetch didn't end with MYSQL_NO_DATA from statement: " + "error: %d", error); if (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA) - die("fetch didn't end with MYSQL_NO_DATA from statement: %d %s", + die("mysql_fetch didn't end with MYSQL_NO_DATA from statement: %d %s", mysql_stmt_errno(stmt), mysql_stmt_error(stmt)); for (i= 0; i < num_fields; i++) @@ -7219,6 +7677,8 @@ void append_table_headings(DYNAMIC_STRING *ds, uint num_fields) { uint col_idx; + if (disable_column_names) + return; for (col_idx= 0; col_idx < num_fields; col_idx++) { if (col_idx) @@ -7268,6 +7728,22 @@ int append_warnings(DYNAMIC_STRING *ds, MYSQL* mysql) /* + Handle situation where query is sent but there is no active connection + (e.g directly after disconnect). + + We emulate MySQL-compatible behaviour of sending something on a closed + connection. +*/ +static void handle_no_active_connection(struct st_command *command, + struct st_connection *cn, DYNAMIC_STRING *ds) +{ + handle_error(command, 2006, "MySQL server has gone away", "000000", ds); + cn->pending= FALSE; + var_set_errno(2006); +} + + +/* Run query using MySQL C API SYNOPSIS @@ -7285,12 +7761,18 @@ void run_query_normal(struct st_connection *cn, struct st_command *command, DYNAMIC_STRING *ds, DYNAMIC_STRING *ds_warnings) { MYSQL_RES *res= 0; - MYSQL *mysql= &cn->mysql; + MYSQL *mysql= cn->mysql; int err= 0, counter= 0; DBUG_ENTER("run_query_normal"); DBUG_PRINT("enter",("flags: %d", flags)); DBUG_PRINT("enter", ("query: '%-.60s'", query)); + if (!mysql) + { + handle_no_active_connection(command, cn, ds); + DBUG_VOID_RETURN; + } + if (flags & QUERY_SEND_FLAG) { /* @@ -7444,7 +7926,8 @@ static int match_expected_error(struct st_command *command, NULL is quite likely, but not in conjunction with a SQL-state expect! */ if (unlikely(err_sqlstate == NULL)) - die("expecting a SQL-state (%s) from query '%s' which cannot produce one...", + die("expecting a SQL-state (%s) from query '%s' which cannot " + "produce one...", command->expected_errors.err[i].code.sqlstate, command->query); if (strncmp(command->expected_errors.err[i].code.sqlstate, @@ -7480,7 +7963,8 @@ void handle_error(struct st_command *command, DBUG_ENTER("handle_error"); - if (command->require_file[0]) + command->used_replace= 1; + if (command->require_file) { /* The query after a "--require" failed. This is fine as long the server @@ -7498,7 +7982,11 @@ void handle_error(struct st_command *command, } if (command->abort_on_error) - die("query '%s' failed: %d: %s", command->query, err_errno, err_error); + { + report_or_die("query '%s' failed: %d: %s", command->query, err_errno, + err_error); + DBUG_VOID_RETURN; + } DBUG_PRINT("info", ("expected_errors.count: %d", command->expected_errors.count)); @@ -7544,13 +8032,15 @@ void handle_error(struct st_command *command, if (command->expected_errors.count > 0) { if (command->expected_errors.err[0].type == ERR_ERRNO) - die("query '%s' failed with wrong errno %d: '%s', instead of %d...", - command->query, err_errno, err_error, - command->expected_errors.err[0].code.errnum); + report_or_die("query '%s' failed with wrong errno %d: '%s', instead of " + "%d...", + command->query, err_errno, err_error, + command->expected_errors.err[0].code.errnum); else - die("query '%s' failed with wrong sqlstate %s: '%s', instead of %s...", - command->query, err_sqlstate, err_error, - command->expected_errors.err[0].code.sqlstate); + report_or_die("query '%s' failed with wrong sqlstate %s: '%s', " + "instead of %s...", + command->query, err_sqlstate, err_error, + command->expected_errors.err[0].code.sqlstate); } revert_properties(); @@ -7577,15 +8067,17 @@ void handle_no_error(struct st_command *command) command->expected_errors.err[0].code.errnum != 0) { /* Error code we wanted was != 0, i.e. not an expected success */ - die("query '%s' succeeded - should have failed with errno %d...", - command->query, command->expected_errors.err[0].code.errnum); + report_or_die("query '%s' succeeded - should have failed with errno %d...", + command->query, command->expected_errors.err[0].code.errnum); } else if (command->expected_errors.err[0].type == ERR_SQLSTATE && strcmp(command->expected_errors.err[0].code.sqlstate,"00000") != 0) { /* SQLSTATE we wanted was != "00000", i.e. not an expected success */ - die("query '%s' succeeded - should have failed with sqlstate %s...", - command->query, command->expected_errors.err[0].code.sqlstate); + report_or_die("query '%s' succeeded - should have failed with " + "sqlstate %s...", + command->query, + command->expected_errors.err[0].code.sqlstate); } DBUG_VOID_RETURN; } @@ -7606,11 +8098,12 @@ void handle_no_error(struct st_command *command) error - function will not return */ -void run_query_stmt(MYSQL *mysql, struct st_command *command, +void run_query_stmt(struct st_connection *cn, struct st_command *command, char *query, int query_len, DYNAMIC_STRING *ds, DYNAMIC_STRING *ds_warnings) { MYSQL_RES *res= NULL; /* Note that here 'res' is meta data result set */ + MYSQL *mysql= cn->mysql; MYSQL_STMT *stmt; DYNAMIC_STRING ds_prepare_warnings; DYNAMIC_STRING ds_execute_warnings; @@ -7620,11 +8113,11 @@ void run_query_stmt(MYSQL *mysql, struct st_command *command, /* Init a new stmt if it's not already one created for this connection */ - if(!(stmt= cur_con->stmt)) + if(!(stmt= cn->stmt)) { if (!(stmt= mysql_stmt_init(mysql))) die("unable to init stmt structure"); - cur_con->stmt= stmt; + cn->stmt= stmt; } /* Init dynamic strings for warnings */ @@ -7637,7 +8130,7 @@ void run_query_stmt(MYSQL *mysql, struct st_command *command, /* Prepare the query */ - if (mysql_stmt_prepare(stmt, query, query_len)) + if (do_stmt_prepare(cn, query, query_len)) { handle_error(command, mysql_stmt_errno(stmt), mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds); @@ -7672,7 +8165,7 @@ void run_query_stmt(MYSQL *mysql, struct st_command *command, /* Execute the query */ - if (mysql_stmt_execute(stmt)) + if (do_stmt_execute(cn)) { handle_error(command, mysql_stmt_errno(stmt), mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds); @@ -7737,10 +8230,15 @@ void run_query_stmt(MYSQL *mysql, struct st_command *command, mysql_free_result(res); /* Free normal result set with meta data */ /* - Clear prepare warnings if there are execute warnings, - since they are probably duplicated. + Normally, if there is a result set, we do not show warnings from the + prepare phase. This is because some warnings are generated both during + prepare and execute; this would generate different warning output + between normal and ps-protocol test runs. + + The --enable_prepare_warnings command can be used to change this so + that warnings from both the prepare and execute phase are shown. */ - if (ds_execute_warnings.length || mysql->warning_count) + if (!disable_warnings && !prepare_warnings_enabled) dynstr_set(&ds_prepare_warnings, NULL); } else @@ -7754,7 +8252,6 @@ void run_query_stmt(MYSQL *mysql, struct st_command *command, Fetch info before fetching warnings, since it will be reset otherwise. */ - if (!disable_info) append_info(ds, mysql_stmt_affected_rows(stmt), mysql_info(mysql)); @@ -7788,14 +8285,6 @@ end: dynstr_free(&ds_prepare_warnings); dynstr_free(&ds_execute_warnings); } - revert_properties(); - - /* Close the statement if - no reconnect, need new prepare */ - if (mysql->reconnect) - { - mysql_stmt_close(stmt); - cur_con->stmt= NULL; - } /* We save the return code (mysql_stmt_errno(stmt)) from the last call sent @@ -7805,6 +8294,15 @@ end: var_set_errno(mysql_stmt_errno(stmt)); + revert_properties(); + + /* Close the statement if reconnect, need new prepare */ + if (mysql->reconnect) + { + mysql_stmt_close(stmt); + cn->stmt= NULL; + } + DBUG_VOID_RETURN; } @@ -7834,6 +8332,7 @@ int util_query(MYSQL* org_mysql, const char* query){ /* enable local infile, in non-binary builds often disabled by default */ mysql_options(mysql, MYSQL_OPT_LOCAL_INFILE, 0); + mysql_options(mysql, MYSQL_OPT_NONBLOCK, 0); safe_connect(mysql, "util", org_mysql->host, org_mysql->user, org_mysql->passwd, org_mysql->db, org_mysql->port, org_mysql->unix_socket); @@ -7841,7 +8340,8 @@ int util_query(MYSQL* org_mysql, const char* query){ cur_con->util_mysql= mysql; } - DBUG_RETURN(mysql_query(mysql, query)); + int ret= mysql_query(mysql, query); + DBUG_RETURN(ret); } @@ -7861,13 +8361,12 @@ int util_query(MYSQL* org_mysql, const char* query){ void run_query(struct st_connection *cn, struct st_command *command, int flags) { - MYSQL *mysql= &cn->mysql; + MYSQL *mysql= cn->mysql; DYNAMIC_STRING *ds; DYNAMIC_STRING *save_ds= NULL; DYNAMIC_STRING ds_result; DYNAMIC_STRING ds_sorted; DYNAMIC_STRING ds_warnings; - DYNAMIC_STRING eval_query; char *query; int query_len; my_bool view_created= 0, sp_created= 0; @@ -7876,10 +8375,10 @@ void run_query(struct st_connection *cn, struct st_command *command, int flags) DBUG_ENTER("run_query"); if (cn->pending && (flags & QUERY_SEND_FLAG)) - die ("Cannot run query on connection between send and reap"); + die("Cannot run query on connection between send and reap"); if (!(flags & QUERY_SEND_FLAG) && !cn->pending) - die ("Cannot reap on a connection without pending send"); + die("Cannot reap on a connection without pending send"); init_dynamic_string(&ds_warnings, NULL, 0, 256); ds_warn= &ds_warnings; @@ -7889,10 +8388,14 @@ void run_query(struct st_connection *cn, struct st_command *command, int flags) */ if (command->type == Q_EVAL || command->type == Q_SEND_EVAL) { - init_dynamic_string(&eval_query, "", command->query_len+256, 1024); - do_eval(&eval_query, command->query, command->end, FALSE); - query = eval_query.str; - query_len = eval_query.length; + if (!command->eval_query.str) + init_dynamic_string(&command->eval_query, "", command->query_len + 256, + 1024); + else + dynstr_set(&command->eval_query, 0); + do_eval(&command->eval_query, command->query, command->end, FALSE); + query= command->eval_query.str; + query_len= command->eval_query.length; } else { @@ -7906,7 +8409,7 @@ void run_query(struct st_connection *cn, struct st_command *command, int flags) Create a temporary dynamic string to contain the output from this query. */ - if (command->require_file[0]) + if (command->require_file) { init_dynamic_string(&ds_result, "", 1024, 1024); ds= &ds_result; @@ -7924,6 +8427,15 @@ void run_query(struct st_connection *cn, struct st_command *command, int flags) dynstr_append_mem(ds, "\n", 1); } + /* + Write the command to the result file before we execute the query + This is needed to be able to analyse the log if something goes + wrong + */ + log_file.write(&ds_res); + log_file.flush(); + dynstr_set(&ds_res, 0); + if (view_protocol_enabled && complete_query && match_re(&view_re, query)) @@ -7968,7 +8480,6 @@ void run_query(struct st_connection *cn, struct st_command *command, int flags) } dynstr_free(&query_str); - } if (sp_protocol_enabled && @@ -8035,20 +8546,18 @@ void run_query(struct st_connection *cn, struct st_command *command, int flags) if (ps_protocol_enabled && complete_query && match_re(&ps_re, query)) - run_query_stmt(mysql, command, query, query_len, ds, &ds_warnings); + run_query_stmt(cn, command, query, query_len, ds, &ds_warnings); else run_query_normal(cn, command, flags, query, query_len, ds, &ds_warnings); dynstr_free(&ds_warnings); ds_warn= 0; - if (command->type == Q_EVAL || command->type == Q_SEND_EVAL) - dynstr_free(&eval_query); if (display_result_sorted) { /* Sort the result set and append it to result */ - dynstr_append_sorted(save_ds, &ds_sorted); + dynstr_append_sorted(save_ds, &ds_sorted, 1); ds= save_ds; dynstr_free(&ds_sorted); } @@ -8056,17 +8565,18 @@ void run_query(struct st_connection *cn, struct st_command *command, int flags) if (sp_created) { if (util_query(mysql, "DROP PROCEDURE mysqltest_tmp_sp ")) - die("Failed to drop sp: %d: %s", mysql_errno(mysql), mysql_error(mysql)); + report_or_die("Failed to drop sp: %d: %s", mysql_errno(mysql), + mysql_error(mysql)); } if (view_created) { if (util_query(mysql, "DROP VIEW mysqltest_tmp_v ")) - die("Failed to drop view: %d: %s", + report_or_die("Failed to drop view: %d: %s", mysql_errno(mysql), mysql_error(mysql)); } - if (command->require_file[0]) + if (command->require_file) { /* A result file was specified for _this_ query and the output should be checked against an already @@ -8088,8 +8598,8 @@ void run_query(struct st_connection *cn, struct st_command *command, int flags) char *re_eprint(int err) { static char epbuf[100]; - size_t len= my_regerror(REG_ITOA|err, (my_regex_t *)NULL, - epbuf, sizeof(epbuf)); + size_t len __attribute__((unused))= + my_regerror(REG_ITOA|err, (my_regex_t *)NULL, epbuf, sizeof(epbuf)); assert(len <= sizeof(epbuf)); return(epbuf); } @@ -8226,9 +8736,10 @@ void get_command_type(struct st_command* command) else { /* -- "comment" that didn't contain a mysqltest command */ - die("Found line beginning with -- that didn't contain "\ - "a valid mysqltest command, check your syntax or "\ + report_or_die("Found line beginning with -- that didn't contain " \ + "a valid mysqltest command, check your syntax or " \ "use # if you intended to write a comment"); + command->type= Q_COMMENT; } } @@ -8264,7 +8775,7 @@ void mark_progress(struct st_command* command __attribute__((unused)), die("Out of memory"); /* Milliseconds since start */ - end= longlong2str(timer, buf, 10); + end= longlong10_to_str(timer, buf, 10); dynstr_append_mem(&ds_progress, buf, (int)(end-buf)); dynstr_append_mem(&ds_progress, "\t", 1); @@ -8298,14 +8809,17 @@ static void dump_backtrace(void) fprintf(stderr, "read_command_buf (%p): ", read_command_buf); my_safe_print_str(read_command_buf, sizeof(read_command_buf)); + fputc('\n', stderr); if (conn) { fprintf(stderr, "conn->name (%p): ", conn->name); my_safe_print_str(conn->name, conn->name_len); + fputc('\n', stderr); #ifdef EMBEDDED_LIBRARY fprintf(stderr, "conn->cur_query (%p): ", conn->cur_query); my_safe_print_str(conn->cur_query, conn->cur_query_len); + fputc('\n', stderr); #endif } fputs("Attempting backtrace...\n", stderr); @@ -8395,7 +8909,6 @@ static void init_signal_handling(void) #endif sigaction(SIGILL, &sa, NULL); sigaction(SIGFPE, &sa, NULL); - DBUG_VOID_RETURN; } @@ -8407,7 +8920,12 @@ int main(int argc, char **argv) my_bool q_send_flag= 0, abort_flag= 0; uint command_executed= 0, last_command_executed= 0; char save_file[FN_REFLEN]; + bool empty_result= FALSE; MY_INIT(argv[0]); + DBUG_ENTER("main"); + + /* mysqltest has no way to free all its memory correctly */ + sf_leaking_memory= 1; save_file[0]= 0; TMPDIR[0]= 0; @@ -8439,8 +8957,8 @@ int main(int argc, char **argv) my_init_dynamic_array(&q_lines, sizeof(struct st_command*), 1024, 1024); - if (my_hash_init(&var_hash, charset_info, - 1024, 0, 0, get_var_key, var_free, MYF(0))) + if (my_hash_init2(&var_hash, 64, charset_info, + 128, 0, 0, get_var_key, var_free, MYF(0))) die("Variable hash initialization failed"); var_set_string("MYSQL_SERVER_VERSION", MYSQL_SERVER_VERSION); @@ -8467,6 +8985,7 @@ int main(int argc, char **argv) #endif init_dynamic_string(&ds_res, "", 2048, 2048); + init_alloc_root(&require_file_root, 1024, 1024); parse_args(argc, argv); @@ -8486,6 +9005,7 @@ int main(int argc, char **argv) next_con= connections + 1; var_set_int("$PS_PROTOCOL", ps_protocol); + var_set_int("$NON_BLOCKING_API", non_blocking_api_enabled); var_set_int("$SP_PROTOCOL", sp_protocol); var_set_int("$VIEW_PROTOCOL", view_protocol); var_set_int("$CURSOR_PROTOCOL", cursor_protocol); @@ -8526,36 +9046,37 @@ int main(int argc, char **argv) cursor_protocol_enabled= cursor_protocol; st_connection *con= connections; -#ifdef EMBEDDED_LIBRARY init_connection_thd(con); -#endif /*EMBEDDED_LIBRARY*/ - if (!( mysql_init(&con->mysql))) + if (! (con->mysql= mysql_init(0))) die("Failed in mysql_init()"); if (opt_connect_timeout) - mysql_options(&con->mysql, MYSQL_OPT_CONNECT_TIMEOUT, + mysql_options(con->mysql, MYSQL_OPT_CONNECT_TIMEOUT, (void *) &opt_connect_timeout); if (opt_compress) - mysql_options(&con->mysql,MYSQL_OPT_COMPRESS,NullS); - mysql_options(&con->mysql, MYSQL_OPT_LOCAL_INFILE, 0); - mysql_options(&con->mysql, MYSQL_SET_CHARSET_NAME, + mysql_options(con->mysql,MYSQL_OPT_COMPRESS,NullS); + mysql_options(con->mysql, MYSQL_OPT_LOCAL_INFILE, 0); + mysql_options(con->mysql, MYSQL_SET_CHARSET_NAME, charset_info->csname); if (opt_charsets_dir) - mysql_options(&con->mysql, MYSQL_SET_CHARSET_DIR, + mysql_options(con->mysql, MYSQL_SET_CHARSET_DIR, opt_charsets_dir); if (opt_protocol) - mysql_options(&con->mysql,MYSQL_OPT_PROTOCOL,(char*)&opt_protocol); + mysql_options(con->mysql,MYSQL_OPT_PROTOCOL,(char*)&opt_protocol); + + if (opt_plugin_dir && *opt_plugin_dir) + mysql_options(con->mysql, MYSQL_PLUGIN_DIR, opt_plugin_dir); #if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) if (opt_use_ssl) { - mysql_ssl_set(&con->mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca, + mysql_ssl_set(con->mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca, opt_ssl_capath, opt_ssl_cipher); #if MYSQL_VERSION_ID >= 50000 /* Turn on ssl_verify_server_cert only if host is "localhost" */ opt_ssl_verify_server_cert= opt_host && !strcmp(opt_host, "localhost"); - mysql_options(&con->mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, + mysql_options(con->mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, &opt_ssl_verify_server_cert); #endif } @@ -8563,13 +9084,14 @@ int main(int argc, char **argv) #ifdef HAVE_SMEM if (shared_memory_base_name) - mysql_options(&con->mysql,MYSQL_SHARED_MEMORY_BASE_NAME,shared_memory_base_name); + mysql_options(con->mysql,MYSQL_SHARED_MEMORY_BASE_NAME,shared_memory_base_name); #endif if (!(con->name = my_strdup("default", MYF(MY_WME)))) die("Out of memory"); + mysql_options(con->mysql, MYSQL_OPT_NONBLOCK, 0); - safe_connect(&con->mysql, con->name, opt_host, opt_user, opt_pass, + safe_connect(con->mysql, con->name, opt_host, opt_user, opt_pass, opt_db, opt_port, unix_sock); /* Use all time until exit if no explicit 'start_timer' */ @@ -8584,14 +9106,15 @@ int main(int argc, char **argv) set_current_connection(con); - if (opt_include) + if (opt_prologue) { - open_file(opt_include); + open_file(opt_prologue); } verbose_msg("Start processing test commands from '%s' ...", cur_file->file_name); while (!read_command(&command) && !abort_flag) { + my_bool ok_to_do; int current_line_inc = 1, processed = 0; if (command->type == Q_UNKNOWN || command->type == Q_COMMENT_WITH_COMMAND) get_command_type(command); @@ -8608,8 +9131,12 @@ int main(int argc, char **argv) command->abort_on_error= (command->expected_errors.count == 0 && abort_on_error); - /* delimiter needs to be executed so we can continue to parse */ - my_bool ok_to_do= cur_block->ok || command->type == Q_DELIMITER; + /* + some commmands need to be executed or at least parsed unconditionally, + because they change the grammar. + */ + ok_to_do= cur_block->ok || command->type == Q_DELIMITER + || command->type == Q_PERL; /* Some commands need to be "done" the first time if they may get re-iterated over in a true context. This can only happen if there's @@ -8620,8 +9147,7 @@ int main(int argc, char **argv) if (command->type == Q_SOURCE || command->type == Q_ERROR || command->type == Q_WRITE_FILE || - command->type == Q_APPEND_FILE || - command->type == Q_PERL) + command->type == Q_APPEND_FILE) { for (struct st_block *stb= cur_block-1; stb >= block_stack; stb--) { @@ -8648,6 +9174,8 @@ int main(int argc, char **argv) case Q_DISCONNECT: case Q_DIRTY_CLOSE: do_close_connection(command); break; + case Q_ENABLE_PREPARE_WARNINGS: prepare_warnings_enabled=1; break; + case Q_DISABLE_PREPARE_WARNINGS: prepare_warnings_enabled=0; break; case Q_ENABLE_QUERY_LOG: set_property(command, P_QUERY, 0); break; @@ -8690,6 +9218,14 @@ int main(int argc, char **argv) case Q_DISABLE_METADATA: set_property(command, P_META, 0); break; + case Q_ENABLE_COLUMN_NAMES: + disable_column_names= 0; + var_set_int("$ENABLED_COLUMN_NAMES", 0); + break; + case Q_DISABLE_COLUMN_NAMES: + disable_column_names= 1; + var_set_int("$ENABLED_COLUMN_NAMES", 1); + break; case Q_SOURCE: do_source(command); break; case Q_SLEEP: do_sleep(command, 0); break; case Q_REAL_SLEEP: do_sleep(command, 1); break; @@ -8780,7 +9316,9 @@ int main(int argc, char **argv) if (save_file[0]) { - strmake(command->require_file, save_file, sizeof(save_file) - 1); + if (!(command->require_file= strdup_root(&require_file_root, + save_file))) + die("out of memory for require_file"); save_file[0]= 0; } run_query(cur_con, command, flags); @@ -8878,12 +9416,12 @@ int main(int argc, char **argv) dynstr_append(&ds_res, "\n"); break; case Q_PING: - handle_command_error(command, mysql_ping(&cur_con->mysql)); + handle_command_error(command, mysql_ping(cur_con->mysql), -1); break; case Q_SEND_SHUTDOWN: handle_command_error(command, - mysql_shutdown(&cur_con->mysql, - SHUTDOWN_DEFAULT)); + mysql_shutdown(cur_con->mysql, + SHUTDOWN_DEFAULT), -1); break; case Q_SHUTDOWN_SERVER: do_shutdown_server(command); @@ -8911,11 +9449,17 @@ int main(int argc, char **argv) case Q_ENABLE_PS_PROTOCOL: set_property(command, P_PS, ps_protocol); break; + case Q_DISABLE_NON_BLOCKING_API: + non_blocking_api_enabled= 0; + break; + case Q_ENABLE_NON_BLOCKING_API: + non_blocking_api_enabled= 1; + break; case Q_DISABLE_RECONNECT: - set_reconnect(&cur_con->mysql, 0); + set_reconnect(cur_con->mysql, 0); break; case Q_ENABLE_RECONNECT: - set_reconnect(&cur_con->mysql, 1); + set_reconnect(cur_con->mysql, 1); /* Close any open statements - no reconnect, need new prepare */ close_statements(); break; @@ -8923,7 +9467,7 @@ int main(int argc, char **argv) if (parsing_disabled == 0) parsing_disabled= 1; else - die("Parsing is already disabled"); + report_or_die("Parsing is already disabled"); break; case Q_ENABLE_PARSING: /* @@ -8933,7 +9477,7 @@ int main(int argc, char **argv) if (parsing_disabled == 1) parsing_disabled= 0; else - die("Parsing is already enabled"); + report_or_die("Parsing is already enabled"); break; case Q_DIE: /* Abort test with error code and error message */ @@ -8944,7 +9488,10 @@ int main(int argc, char **argv) abort_flag= 1; break; case Q_SKIP: - abort_not_supported_test("%s", command->first_argument); + /* Eval the query, thus replacing all environment variables */ + dynstr_set(&ds_res, 0); + do_eval(&ds_res, command->first_argument, command->end, FALSE); + abort_not_supported_test("%s",ds_res.str); break; case Q_RESULT: @@ -9012,8 +9559,6 @@ int main(int argc, char **argv) if (parsing_disabled) die("Test ended with parsing disabled"); - my_bool empty_result= FALSE; - /* The whole test has been executed _sucessfully_. Time to compare result or save it to record file. @@ -9094,7 +9639,7 @@ void timer_output(void) { char buf[32], *end; ulonglong timer= timer_now() - timer_start; - end= longlong2str(timer, buf, 10); + end= longlong10_to_str(timer, buf, 10); str_to_file(timer_file,buf, (int) (end-buf)); /* Timer has been written to the file, don't use it anymore */ timer_file= 0; @@ -9104,7 +9649,7 @@ void timer_output(void) ulonglong timer_now(void) { - return my_micro_time() / 1000; + return my_interval_timer() / 1000000; } @@ -9136,7 +9681,8 @@ void do_get_replace_column(struct st_command *command) if (!(column_number= atoi(to)) || column_number > MAX_COLUMNS) die("Wrong column number to replace_column in '%s'", command->query); if (!*from) - die("Wrong number of arguments to replace_column in '%s'", command->query); + die("Wrong number of arguments to replace_column in '%s'", + command->query); to= get_string(&buff, &from, command); my_free(replace_column[column_number-1]); replace_column[column_number-1]= my_strdup(to, MYF(MY_WME | MY_FAE)); @@ -9248,15 +9794,15 @@ void free_replace() typedef struct st_replace { - my_bool found; + int found; struct st_replace *next[256]; } REPLACE; typedef struct st_replace_found { - my_bool found; - char *replace_string; + int found; uint to_offset; int from_offset; + char *replace_string; } REPLACE_STRING; @@ -9288,7 +9834,7 @@ void replace_strings_append(REPLACE *rep, DYNAMIC_STRING* ds, } /* Found a string that needs to be replaced */ - DBUG_PRINT("info", ("found: %d, to_offset: %d, from_offset: %d, string: %s", + DBUG_PRINT("info", ("found: %d, to_offset: %u, from_offset: %d, string: %s", rep_str->found, rep_str->to_offset, rep_str->from_offset, rep_str->replace_string)); @@ -10406,17 +10952,16 @@ void replace_dynstr_append_uint(DYNAMIC_STRING *ds, uint val) } - /* Build a list of pointer to each line in ds_input, sort the list and use the sorted list to append the strings sorted to the output ds SYNOPSIS - dynstr_append_sorted - ds - string where the sorted output will be appended - ds_input - string to be sorted - + dynstr_append_sorted() + ds string where the sorted output will be appended + ds_input string to be sorted + keep_header If header should not be sorted */ static int comp_lines(const char **a, const char **b) @@ -10424,7 +10969,8 @@ static int comp_lines(const char **a, const char **b) return (strcmp(*a,*b)); } -void dynstr_append_sorted(DYNAMIC_STRING* ds, DYNAMIC_STRING *ds_input) +void dynstr_append_sorted(DYNAMIC_STRING* ds, DYNAMIC_STRING *ds_input, + bool keep_header) { unsigned i; char *start= ds_input->str; @@ -10436,11 +10982,14 @@ void dynstr_append_sorted(DYNAMIC_STRING* ds, DYNAMIC_STRING *ds_input) my_init_dynamic_array(&lines, sizeof(const char*), 32, 32); - /* First line is result header, skip past it */ - while (*start && *start != '\n') - start++; - start++; /* Skip past \n */ - dynstr_append_mem(ds, ds_input->str, start - ds_input->str); + if (keep_header) + { + /* First line is result header, skip past it */ + while (*start && *start != '\n') + start++; + start++; /* Skip past \n */ + dynstr_append_mem(ds, ds_input->str, start - ds_input->str); + } /* Insert line(s) in array */ while (*start) @@ -10489,3 +11038,32 @@ static int setenv(const char *name, const char *value, int overwrite) return 0; } #endif + +/* + for the purpose of testing (see dialog.test) + we replace default mysql_authentication_dialog_ask function with the one, + that always reads from stdin with explicit echo. + +*/ +MYSQL_PLUGIN_EXPORT +char *mysql_authentication_dialog_ask(MYSQL *mysql, int type, + const char *prompt, + char *buf, int buf_len) +{ + char *s=buf; + + fputs(prompt, stdout); + fputs(" ", stdout); + + if (!fgets(buf, buf_len-1, stdin)) + buf[0]= 0; + else if (buf[0] && (s= strend(buf))[-1] == '\n') + s[-1]= 0; + + for (s= buf; *s; s++) + fputc(type == 2 ? '*' : *s, stdout); + + fputc('\n', stdout); + + return buf; +} diff --git a/client/readline.cc b/client/readline.cc index d850379353f..b6643b86356 100644 --- a/client/readline.cc +++ b/client/readline.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2000, 2011, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -33,9 +33,9 @@ static char *intern_read_line(LINE_BUFFER *buffer, ulong *out_length); LINE_BUFFER *batch_readline_init(ulong max_size,FILE *file) { LINE_BUFFER *line_buff; - MY_STAT input_file_stat; #ifndef __WIN__ + MY_STAT input_file_stat; if (my_fstat(fileno(file), &input_file_stat, MYF(MY_WME)) || MY_S_ISDIR(input_file_stat.st_mode) || MY_S_ISBLK(input_file_stat.st_mode)) @@ -54,16 +54,26 @@ LINE_BUFFER *batch_readline_init(ulong max_size,FILE *file) } -char *batch_readline(LINE_BUFFER *line_buff) +char *batch_readline(LINE_BUFFER *line_buff, bool binary_mode) { char *pos; ulong out_length; + LINT_INIT(out_length); if (!(pos=intern_read_line(line_buff, &out_length))) return 0; if (out_length && pos[out_length-1] == '\n') - if (--out_length && pos[out_length-1] == '\r') /* Remove '\n' */ - out_length--; /* Remove '\r' */ + { + /* + On Windows platforms we also need to remove '\r', unconditionally. On + Unix-like platforms we only remove it if we are not on binary mode. + */ + + /* Remove '\n' */ + if (--out_length && IF_WIN(1,!binary_mode) && pos[out_length-1] == '\r') + /* Remove '\r' */ + out_length--; + } line_buff->read_length=out_length; pos[out_length]=0; return pos; @@ -225,7 +235,7 @@ char *intern_read_line(LINE_BUFFER *buffer, ulong *out_length) for (;;) { pos=buffer->end_of_line; - while (*pos != '\n' && *pos) + while (*pos != '\n' && pos != buffer->end) pos++; if (pos == buffer->end) { diff --git a/client/sql_string.cc b/client/sql_string.cc.dontuse index e2e6fd962d6..9679197b207 100644 --- a/client/sql_string.cc +++ b/client/sql_string.cc.dontuse @@ -1,5 +1,5 @@ /* - Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2000, 2013, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -483,11 +483,11 @@ uint32 String::numchars() return str_charset->cset->numchars(str_charset, Ptr, Ptr+str_length); } -int String::charpos(int i,uint32 offset) +int String::charpos(longlong i,uint32 offset) { if (i <= 0) - return i; - return str_charset->cset->charpos(str_charset,Ptr+offset,Ptr+str_length,i); + return (int)i; + return (int)str_charset->cset->charpos(str_charset,Ptr+offset,Ptr+str_length,(size_t)i); } int String::strstr(const String &s,uint32 offset) diff --git a/client/sql_string.h b/client/sql_string.h.dontuse index 4b84ccd2387..94f844dc689 100644 --- a/client/sql_string.h +++ b/client/sql_string.h.dontuse @@ -1,5 +1,5 @@ -#ifndef CLIENT_SQL_STRING_INCLUDED -#define CLIENT_SQL_STRING_INCLUDED +#ifndef SQL_STRING_INCLUDED +#define SQL_STRING_INCLUDED /* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. @@ -32,13 +32,13 @@ uint32 copy_and_convert(char *to, uint32 to_length, CHARSET_INFO *to_cs, class String { char *Ptr; - uint32 str_length,Alloced_length; + uint32 str_length,Alloced_length, extra_alloc; bool alloced; CHARSET_INFO *str_charset; public: String() { - Ptr=0; str_length=Alloced_length=0; alloced=0; + Ptr=0; str_length=Alloced_length=extra_alloc=0; alloced=0; str_charset= &my_charset_bin; } String(uint32 length_arg) @@ -48,23 +48,24 @@ public: } String(const char *str, CHARSET_INFO *cs) { - Ptr=(char*) str; str_length=(uint) strlen(str); Alloced_length=0; alloced=0; + Ptr=(char*) str; str_length= (uint32) strlen(str); + Alloced_length= extra_alloc= 0; alloced=0; str_charset=cs; } String(const char *str,uint32 len, CHARSET_INFO *cs) { - Ptr=(char*) str; str_length=len; Alloced_length=0; alloced=0; + Ptr=(char*) str; str_length=len; Alloced_length= extra_alloc=0; alloced=0; str_charset=cs; } String(char *str,uint32 len, CHARSET_INFO *cs) { - Ptr=(char*) str; Alloced_length=str_length=len; alloced=0; + Ptr=(char*) str; Alloced_length=str_length=len; extra_alloc= 0; alloced=0; str_charset=cs; } String(const String &str) { Ptr=str.Ptr ; str_length=str.str_length ; - Alloced_length=str.Alloced_length; alloced=0; + Alloced_length=str.Alloced_length; extra_alloc= 0; alloced=0; str_charset=str.str_charset; } static void *operator new(size_t size, MEM_ROOT *mem_root) @@ -273,7 +274,7 @@ public: friend int stringcmp(const String *a,const String *b); friend String *copy_if_not_alloced(String *a,String *b,uint32 arg_length); uint32 numchars(); - int charpos(int i,uint32 offset=0); + int charpos(longlong i,uint32 offset=0); int reserve(uint32 space_needed) { @@ -362,4 +363,4 @@ public: } }; -#endif /* CLIENT_SQL_STRING_INCLUDED */ +#endif /* SQL_STRING_INCLUDED */ |