diff options
Diffstat (limited to 'client')
-rw-r--r-- | client/CMakeLists.txt | 18 | ||||
-rw-r--r-- | client/async_example.c | 216 | ||||
-rw-r--r-- | client/client_priv.h | 9 | ||||
-rw-r--r-- | client/get_password.c | 8 | ||||
-rw-r--r-- | client/my_readline.h | 2 | ||||
-rw-r--r-- | client/mysql.cc | 288 | ||||
-rw-r--r-- | client/mysql_plugin.c | 1 | ||||
-rw-r--r-- | client/mysql_upgrade.c | 92 | ||||
-rw-r--r-- | client/mysqladmin.cc | 177 | ||||
-rw-r--r-- | client/mysqlbinlog.cc | 414 | ||||
-rw-r--r-- | client/mysqlcheck.c | 249 | ||||
-rw-r--r-- | client/mysqldump.c | 222 | ||||
-rw-r--r-- | client/mysqlimport.c | 41 | ||||
-rw-r--r-- | client/mysqlshow.c | 9 | ||||
-rw-r--r-- | client/mysqlslap.c | 191 | ||||
-rw-r--r-- | client/mysqltest.cc | 1281 | ||||
-rw-r--r-- | client/readline.cc | 5 | ||||
-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, 2383 insertions, 869 deletions
diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 4d8fab3f4b9..e4507f9c8ba 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -21,21 +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") TARGET_LINK_LIBRARIES(mysqltest mysqlclient regex) +SET_TARGET_PROPERTIES(mysqltest PROPERTIES ENABLE_EXPORTS TRUE) MYSQL_ADD_EXECUTABLE(mysqlcheck mysqlcheck.c) @@ -47,7 +49,7 @@ TARGET_LINK_LIBRARIES(mysqldump mysqlclient) MYSQL_ADD_EXECUTABLE(mysqlimport mysqlimport.c) 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) @@ -69,9 +71,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 2362811d2b3..607bd3997c1 100644 --- a/client/client_priv.h +++ b/client/client_priv.h @@ -52,6 +52,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,9 +87,11 @@ enum options_client OPT_INIT_COMMAND, OPT_PLUGIN_DIR, OPT_DEFAULT_AUTH, - OPT_DEFAULT_PLUGIN, - OPT_ENABLE_CLEARTEXT_PLUGIN, - 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 */ }; /** diff --git a/client/get_password.c b/client/get_password.c index ea024f76fb6..09d307b5553 100644 --- a/client/get_password.c +++ b/client/get_password.c @@ -23,10 +23,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> @@ -113,7 +109,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) @@ -138,7 +134,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..11ace987b44 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 diff --git a/client/mysql.cc b/client/mysql.cc index 73787ea5eeb..67878b36227 100644 --- a/client/mysql.cc +++ b/client/mysql.cc @@ -1,5 +1,6 @@ /* - Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2000, 2012, Oracle and/or its affiliates. + Copyright (c) 2009, 2012, Monty Program Ab. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -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,8 +83,9 @@ 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 @@ -92,7 +93,6 @@ extern "C" { #define HAVE_READLINE #define USE_POPEN #endif - //int vidattr(long unsigned int attrs); // Was missing in sun curses } #if !defined(HAVE_VIDATTR) @@ -141,18 +141,18 @@ 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 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,6 +242,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); +#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. */ @@ -887,6 +894,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, ""}, @@ -1018,12 +1026,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 /* @@ -1079,9 +1088,10 @@ int main(int argc,char *argv[]) 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 @@ -1172,10 +1182,11 @@ 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); sprintf((char*) glob_buffer.ptr(), - "Your MySQL connection id is %lu\nServer version: %s\n", + "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); @@ -1288,15 +1299,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"); @@ -1311,6 +1323,7 @@ sig_handler handle_sigint(int sig) 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 */ @@ -1321,11 +1334,15 @@ 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"); + interrupted_query= 0; + if (in_com_source) + aborted= 1; // Abort source command return; err: @@ -1337,7 +1354,6 @@ err: handler called mysql_end(). */ mysql_thread_end(); - return; #else mysql_end(sig); #endif @@ -1360,6 +1376,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. " @@ -1411,16 +1431,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', @@ -1493,6 +1509,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}, @@ -1642,9 +1662,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) { @@ -1764,13 +1781,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; } @@ -1814,6 +1836,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) @@ -1837,6 +1860,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); } @@ -1847,14 +1873,14 @@ static int read_and_execute(bool interactive) String buffer; #endif - char *line; + char *line= 0; char in_string=0; ulong line_number=0; bool ml_comment= 0; COMMANDS *com; status.exit_status=1; - - for (;;) + + while (!aborted) { if (!interactive) { @@ -2017,12 +2043,12 @@ static COMMANDS *find_command(char *name,char cmd_char) for (uint i= 0; commands[i].name; i++) { 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))) + (((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]); @@ -2180,16 +2206,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) @@ -2221,7 +2252,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) { @@ -2701,6 +2732,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; } @@ -2888,13 +2921,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); @@ -3006,6 +3034,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) @@ -3893,8 +3922,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) @@ -3910,10 +3940,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) @@ -3997,7 +4031,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)); @@ -4055,6 +4089,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)) @@ -4086,16 +4121,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; } @@ -4284,6 +4330,58 @@ char *get_arg(char *line, my_bool get_next_arg) } +/** + 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. +*/ + +MYSQL_PLUGIN_EXPORT +char *mysql_authentication_dialog_ask(MYSQL *mysql, int type, + const char *prompt, + char *buf, int buf_len) +{ + char *s=buf; + + fputs("[mariadb] ", stdout); + fputs(prompt, stdout); + fputs(" ", stdout); + + 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 buf; +} + static int sql_real_connect(char *host,char *database,char *user,char *password, uint silent) @@ -4331,17 +4429,13 @@ 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_real_connect(&mysql, host, user, password, database, opt_mysql_port, opt_mysql_unix_port, connect_flag | CLIENT_MULTI_STATEMENTS)) @@ -4362,6 +4456,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 @@ -4458,6 +4559,7 @@ com_status(String *buffer __attribute__((unused)), 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)); @@ -4626,12 +4728,19 @@ put_info(const char *str,INFO_TYPE info_type, uint error, const char *sqlstate) 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); @@ -4640,7 +4749,7 @@ put_info(const char *str,INFO_TYPE info_type, uint error, const char *sqlstate) } if (unbuffered) fflush(file); - return info_type == INFO_ERROR ? -1 : 0; + return info_type == INFO_ERROR ? (ignore_errors ? -1 : 1): 0; } @@ -4777,7 +4886,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 @@ -4806,6 +4915,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; @@ -4979,3 +5094,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 bbb44678f51..f4e3111b7b7 100644 --- a/client/mysql_plugin.c +++ b/client/mysql_plugin.c @@ -100,6 +100,7 @@ int main(int argc,char *argv[]) char operation[16]; MY_INIT(argv[0]); + sf_leaking_memory=1; /* don't report memory leaks on early exits */ plugin_data.name= 0; // initialize name /* diff --git a/client/mysql_upgrade.c b/client/mysql_upgrade.c index 8bbb8864afd..71bc936cfe6 100644 --- a/client/mysql_upgrade.c +++ b/client/mysql_upgrade.c @@ -1,5 +1,6 @@ /* - Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2006, 2012, Oracle and/or its affiliates. + Copyright (C) 2010, 2012, Monty Program Ab. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -21,7 +22,7 @@ #include <welcome_copyright_notice.h> /* ORACLE_WELCOME_COPYRIGHT_NOTICE */ -#define VER "1.1" +#define VER "1.2" #ifdef HAVE_SYS_WAIT_H #include <sys/wait.h> @@ -40,6 +41,7 @@ static char mysqlcheck_path[FN_REFLEN]; static my_bool opt_force, opt_verbose, debug_info_flag, debug_check_flag, opt_systables_only; +static my_bool opt_not_used, opt_silent; static uint my_end_arg= 0; static char *opt_user= (char*)"root"; @@ -63,16 +65,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,22 +91,21 @@ 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}, {"password", 'p', "Password to use when connecting to server. If password is not given," @@ -128,6 +133,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 + {"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> @@ -140,8 +147,7 @@ static struct my_option my_long_options[]= {"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, - GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, + &opt_not_used, &opt_not_used, 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.", @@ -187,7 +193,7 @@ static void verbose(const char *fmt, ...) { va_list args; - if (!opt_verbose) + if (opt_silent) return; /* Print the verbose message */ @@ -246,9 +252,9 @@ 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.\n"); my_print_help(my_long_options); - exit(0); + die(0); break; case '#': @@ -284,6 +290,18 @@ get_one_option(int optid, const struct my_option *opt, /* FALLTHROUGH */ case 'v': /* --verbose */ + opt_verbose++; + if (argument == disabled_my_option) + { + opt_verbose= 0; + opt_silent= 1; + } + add_option= 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 */ @@ -448,7 +466,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 @@ -518,7 +537,7 @@ static int run_query(const char *query, DYNAMIC_STRING *ds_res, "--database=mysql", "--batch", /* Turns off pager etc. */ force ? "--force": "--skip-force", - ds_res ? "--silent": "", + ds_res || opt_silent ? "--silent": "", "<", query_file_path, "2>&1", @@ -600,7 +619,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 */ @@ -608,19 +626,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); } @@ -676,6 +690,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); @@ -691,6 +707,7 @@ static void print_conn_args(const char *tool_name) static int run_mysqlcheck_upgrade(void) { + verbose("Phase 2/3: Checking and upgrading tables"); print_conn_args("mysqlcheck"); return run_tool(mysqlcheck_path, NULL, /* Send output from mysqlcheck directly to screen */ @@ -699,6 +716,8 @@ static int run_mysqlcheck_upgrade(void) "--check-upgrade", "--all-databases", "--auto-repair", + !opt_silent || opt_verbose ? "--verbose": "", + opt_silent ? "--silent": "", opt_write_binlog ? "--write-binlog" : "--skip-write-binlog", NULL); } @@ -706,6 +725,7 @@ static int run_mysqlcheck_upgrade(void) static int run_mysqlcheck_fixnames(void) { + verbose("Phase 1/3: Fixing table and database names"); print_conn_args("mysqlcheck"); return run_tool(mysqlcheck_path, NULL, /* Send output from mysqlcheck directly to screen */ @@ -714,6 +734,8 @@ static int run_mysqlcheck_fixnames(void) "--all-databases", "--fix-db-names", "--fix-table-names", + opt_verbose ? "--verbose": "", + opt_silent ? "--silent": "", opt_write_binlog ? "--write-binlog" : "--skip-write-binlog", NULL); } @@ -783,7 +805,7 @@ 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 3/3: Running 'mysql_fix_privilege_tables'..."); run_query(mysql_fix_privilege_tables, &ds_result, /* Collect result */ TRUE); @@ -812,14 +834,16 @@ 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[]= { - "client", /* Read settings how to connect to server */ - "mysql_upgrade", /* Read special settings for mysql_upgrade*/ + "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 }; diff --git a/client/mysqladmin.cc b/client/mysqladmin.cc index 2a1d61395e2..e2ebb883f77 100644 --- a/client/mysqladmin.cc +++ b/client/mysqladmin.cc @@ -1,5 +1,6 @@ /* - Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2000, 2012, Oracle and/or its affiliates. + Copyright (c) 2010, 2012, Monty Program Ab. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -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; @@ -100,7 +99,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", @@ -110,7 +112,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 }; @@ -215,15 +220,12 @@ 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)), @@ -289,9 +291,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) { @@ -304,20 +303,18 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), int main(int argc,char *argv[]) { - int error= 0, ho_error; + int error= 0; MYSQL mysql; char **commands, **save_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; if (debug_info_flag) my_end_arg= MY_CHECK_ERROR | MY_GIVE_INFO; if (debug_check_flag) @@ -335,6 +332,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) @@ -364,10 +363,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)) { /* @@ -470,14 +465,17 @@ int main(int argc,char *argv[]) } /* got connection */ mysql_close(&mysql); +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; } @@ -603,11 +601,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]; @@ -684,7 +683,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)); @@ -877,9 +879,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; } @@ -889,7 +901,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; } @@ -899,7 +911,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; } @@ -909,7 +921,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; } @@ -923,7 +999,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) { @@ -1107,16 +1183,23 @@ static void usage(void) 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("\ @@ -1406,7 +1489,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 a58aa65207d..777e68902dd 100644 --- a/client/mysqlbinlog.cc +++ b/client/mysqlbinlog.cc @@ -1,5 +1,6 @@ /* - Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2000, 2012, Oracle and/or its affiliates. + Copyright (c) 2009, 2012, Monty Program Ab This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -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; @@ -59,13 +73,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 +92,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 +118,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,6 +141,71 @@ enum Exit_status { OK_STOP }; +/** + 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 *print_event_info, const char* logname); static Exit_status dump_remote_log_entries(PRINT_EVENT_INFO *print_event_info, @@ -632,6 +714,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 @@ -706,7 +854,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 +878,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 +899,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 +923,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 +959,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 +1072,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 +1094,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,6 +1116,20 @@ 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; + } } case WRITE_ROWS_EVENT: case DELETE_ROWS_EVENT: @@ -964,6 +1159,13 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev, 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 @@ -1010,6 +1212,7 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev, /* FALL THROUGH */ } default: + print_skip_replication_statement(print_event_info, ev); ev->print(result_file, print_event_info); if (head->error == -1) goto err; @@ -1022,14 +1225,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; } @@ -1037,7 +1242,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}, @@ -1107,7 +1312,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, " @@ -1125,7 +1330,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}, @@ -1194,6 +1399,18 @@ 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}, + {"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} }; @@ -1269,8 +1486,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); @@ -1291,8 +1509,8 @@ 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); + my_print_help(my_options); + my_print_variables(my_options); } @@ -1301,7 +1519,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) @@ -1374,6 +1592,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; @@ -1382,10 +1647,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); @@ -1399,7 +1666,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; @@ -1418,6 +1685,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) @@ -1426,8 +1697,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); @@ -1524,7 +1795,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': @@ -1606,6 +1888,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); @@ -1638,18 +1922,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 || @@ -1877,7 +2174,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 " @@ -1910,7 +2208,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;" @@ -2023,7 +2322,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) { /* @@ -2065,6 +2365,8 @@ end: return retval; } +/* Used in sql_alloc(). Inited and freed in main() */ +MEM_ROOT s_mem_root; int main(int argc, char** argv) { @@ -2077,17 +2379,27 @@ int main(int argc, char** argv) my_init_time(); // for time functions - 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) @@ -2173,6 +2485,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(); @@ -2184,14 +2498,24 @@ 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 */ +#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 "sql_string.cc" +#include "sql_list.cc" +#include "rpl_filter.cc" diff --git a/client/mysqlcheck.c b/client/mysqlcheck.c index 8a9711fffae..4218f2da62c 100644 --- a/client/mysqlcheck.c +++ b/client/mysqlcheck.c @@ -1,5 +1,6 @@ /* - Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2001, 2012, Oracle and/or its affiliates. + Copyright (c) 2010, 2011, 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 @@ -15,7 +16,9 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#define CHECK_VERSION "2.5.0" +/* By Jani Tolonen, 2001-04-20, MySQL Development Team */ + +#define CHECK_VERSION "2.7.0" #include "client_priv.h" #include <m_ctype.h> @@ -35,8 +38,8 @@ static my_bool opt_alldbs = 0, opt_check_only_changed = 0, opt_extended = 0, opt_medium_check = 0, opt_quick = 0, opt_all_in_1 = 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_fix_table_names= 0, opt_fix_db_names= 0, opt_upgrade= 0; +static my_bool opt_write_binlog= 1, opt_flush_tables= 0; static uint verbose = 0, opt_mysql_port=0; static int my_end_arg; static char * opt_mysql_unix_port = 0; @@ -45,9 +48,7 @@ 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; -#ifdef HAVE_SMEM 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 }; @@ -69,8 +70,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', @@ -122,10 +123,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}, @@ -191,7 +195,8 @@ static struct my_option my_long_options[] = {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); @@ -223,27 +228,27 @@ 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); my_print_help(my_long_options); my_print_variables(my_long_options); + DBUG_VOID_RETURN; } /* usage */ @@ -252,6 +257,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': @@ -332,15 +338,16 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), { 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) { @@ -348,8 +355,7 @@ 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) @@ -383,21 +389,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); @@ -405,7 +411,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 */ @@ -414,20 +420,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 */ @@ -435,19 +445,25 @@ 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 */ static int process_selected_tables(char *db, char **table_names, int tables) { + DBUG_ENTER("process_selected_tables"); + if (use_db(db)) - return 1; + DBUG_RETURN(1); if (opt_all_in_1 && what_to_do != DO_UPGRADE) { /* @@ -464,7 +480,7 @@ static int process_selected_tables(char *db, char **table_names, int tables) 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++) @@ -479,7 +495,7 @@ static int process_selected_tables(char *db, char **table_names, int tables) else for (; tables > 0; tables--, table_names++) handle_request_for_tables(*table_names, fixed_name_length(*table_names)); - return 0; + DBUG_RETURN(0); } /* process_selected_tables */ @@ -487,20 +503,24 @@ static uint fixed_name_length(const char *name) { const char *p; uint 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 (uint) ((p - name) + extra_length); + DBUG_RETURN((uint) ((p - name) + extra_length)); } static char *fix_table_name(char *dest, char *src) { + DBUG_ENTER("fix_table_name"); + *dest++= '`'; for (; *src; src++) { @@ -518,28 +538,33 @@ 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; + 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) @@ -560,7 +585,7 @@ static int process_all_tables_in_db(char *database) 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)) ;) { @@ -582,12 +607,16 @@ static int process_all_tables_in_db(char *database) /* Skip views if we don't perform renaming. */ if ((what_to_do != DO_UPGRADE) && (num_columns == 2) && (strcmp(row[1], "VIEW") == 0)) continue; + 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])); } } mysql_free_result(res); - return 0; + DBUG_RETURN(0); } /* process_all_tables_in_db */ @@ -596,8 +625,10 @@ 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); sprintf(qbuf, "RENAME TABLE `%s` TO `%s`", name, name + 9); if (mysql_query(sock, qbuf)) { @@ -607,15 +638,17 @@ static int fix_table_storage_name(const char *name) } 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); sprintf(qbuf, "ALTER DATABASE `%s` UPGRADE DATA DIRECTORY NAME", name); if (mysql_query(sock, qbuf)) { @@ -625,17 +658,19 @@ static int fix_database_storage_name(const char *name) } 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); @@ -646,11 +681,15 @@ static int rebuild_table(char *name) rc= 1; } my_free(query); - return rc; + DBUG_RETURN(rc); } static int process_one_db(char *database) { + DBUG_ENTER("process_one_db"); + + if (verbose) + puts(database); if (what_to_do == DO_UPGRADE) { int rc= 0; @@ -660,45 +699,51 @@ 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() { const char *stmt= "SET SQL_LOG_BIN=0"; + DBUG_ENTER("disable_binlog"); + if (mysql_query(sock, stmt)) { fprintf(stderr, "Failed to %s\n", stmt); fprintf(stderr, "Error: %s\n", mysql_error(sock)); - return 1; + DBUG_RETURN(1); } - return 0; + DBUG_RETURN(0); } static int handle_request_for_tables(char *tables, uint length) { char *query, *end, options[100], message[100]; + char table_name_buff[NAME_CHAR_LEN*2*2+1], *table_name; uint query_length= 0; const char *op = 0; + DBUG_ENTER("handle_request_for_tables"); options[0] = 0; end = options; @@ -725,22 +770,26 @@ static int handle_request_for_tables(char *tables, uint length) op= (opt_write_binlog) ? "OPTIMIZE" : "OPTIMIZE NO_WRITE_TO_BINLOG"; break; case DO_UPGRADE: - return fix_table_storage_name(tables); + DBUG_RETURN(fix_table_storage_name(tables)); } if (!(query =(char *) my_malloc((sizeof(char)*(length+110)), MYF(MY_WME)))) - return 1; + DBUG_RETURN(1); if (opt_all_in_1) { /* No backticks here as we added them before */ query_length= sprintf(query, "%s TABLE %s %s", op, tables, options); + table_name= tables; } else { - char *ptr; + char *ptr, *org; - ptr= strmov(strmov(query, op), " TABLE "); + org= ptr= strmov(strmov(query, op), " TABLE "); 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= (uint) (ptr - query); } @@ -748,11 +797,22 @@ static int handle_request_for_tables(char *tables, uint length) { sprintf(message, "when executing '%s TABLE ... %s'", op, 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); } @@ -760,9 +820,10 @@ static void print_result() { MYSQL_RES *res; MYSQL_ROW row; - char prev[NAME_LEN*2+2]; + char prev[(NAME_LEN+9)*2+2]; uint i; my_bool found_error=0, table_rebuild=0; + DBUG_ENTER("print_result"); res = mysql_use_result(sock); @@ -796,7 +857,15 @@ 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 TABLE")) + printf("%-50s %s", row[0], "Needs upgrade"); + else + printf("%s\n%-9s: %s", row[0], row[2], row[3]); if (strcmp(row[2],"note")) { found_error=1; @@ -818,13 +887,14 @@ static void print_result() insert_dynamic(&tables4repair, (uchar*) prev); } 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"); } @@ -838,10 +908,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); @@ -854,18 +922,20 @@ static int dbConnect(char *host, char *user, char *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 */ @@ -881,45 +951,52 @@ 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(&tables4rebuild, sizeof(char)*(NAME_LEN*2+2),16,64))) - { - first_error = 1; goto end; - } if (opt_alldbs) process_all_databases(); @@ -944,6 +1021,8 @@ int main(int argc, char **argv) for (i = 0; i < tables4rebuild.elements ; i++) rebuild_table((char*) dynamic_array_ptr(&tables4rebuild, i)); } + ret= test(first_error); + end: dbDisconnect(current_host); if (opt_auto_repair) @@ -951,10 +1030,12 @@ int main(int argc, char **argv) delete_dynamic(&tables4repair); delete_dynamic(&tables4rebuild); } + 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 4fcf309b22b..284ea1e760a 100644 --- a/client/mysqldump.c +++ b/client/mysqldump.c @@ -1,5 +1,6 @@ /* - Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2000, 2012, Oracle and/or its affiliates. + Copyright (c) 2010, 2012, Monty Program Ab. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -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,9 @@ #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 + /* general_log or slow_log tables under mysql database */ static inline my_bool general_log_or_slow_log_tables(const char *db, const char *table) @@ -229,8 +233,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}, @@ -267,8 +271,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, @@ -382,9 +386,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.", @@ -524,7 +528,8 @@ static struct my_option my_long_options[] = {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, ...); @@ -595,17 +600,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) { - printf("Usage: %s [OPTIONS] database [tables]\n", my_progname); + printf("Usage: %s [OPTIONS] database [tables]\n", my_progname_short); printf("OR %s [OPTIONS] --databases [OPTIONS] DB1 [DB2 DB3...]\n", - my_progname); - printf("OR %s [OPTIONS] --all-databases [OPTIONS]\n", my_progname); + my_progname_short); + printf("OR %s [OPTIONS] --all-databases [OPTIONS]\n", my_progname_short); } @@ -624,7 +629,7 @@ static void usage(void) static void short_usage(void) { short_usage_sub(); - printf("For more options, use %s --help\n", my_progname); + printf("For more options, use %s --help\n", my_progname_short); } @@ -673,8 +678,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\ "); } @@ -704,8 +714,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, @@ -942,7 +956,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); } @@ -960,7 +974,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) @@ -972,14 +986,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) && @@ -1003,7 +1017,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; } @@ -1031,7 +1045,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 */ @@ -1065,7 +1079,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); @@ -1148,6 +1162,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 1; + + 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, uint str_len, const char *token, @@ -1434,6 +1486,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) @@ -1442,6 +1495,7 @@ static void free_resources() dynstr_free(&insert_pat); if (defaults_argv) free_defaults(defaults_argv); + mysql_library_end(); my_end(my_end_arg); } @@ -1493,9 +1547,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 (!(mysql= mysql_real_connect(&mysql_connection,host,user,passwd, - NULL,opt_mysql_port,opt_mysql_unix_port, - 0))) + 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); @@ -2107,7 +2161,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); } @@ -2759,7 +2813,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); @@ -2870,7 +2924,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); @@ -3526,7 +3580,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; } @@ -3791,7 +3845,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, @@ -3985,8 +4039,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); } @@ -4126,13 +4180,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))) @@ -4148,6 +4203,7 @@ static int dump_all_databases() if (dump_all_views_in_db(row[0])) result=1; } + mysql_free_result(tableres); } return result; } @@ -4276,8 +4332,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 */ @@ -4641,41 +4695,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; } @@ -4728,7 +4805,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")) @@ -4736,7 +4813,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; @@ -4759,7 +4836,7 @@ static int do_show_slave_status(MYSQL *mysql_con) if (row[1]) fprintf(md_result_file, "MASTER_HOST='%s', ", row[1]); if (row[3]) - fprintf(md_result_file, "MASTER_PORT='%s', ", row[3]); + fprintf(md_result_file, "MASTER_PORT=%s, ", row[3]); } fprintf(md_result_file, "MASTER_LOG_FILE='%s', MASTER_LOG_POS=%s;\n", row[9], row[21]); @@ -4796,7 +4873,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); @@ -5233,6 +5310,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); @@ -5242,8 +5320,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); } @@ -5434,8 +5514,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)); @@ -5446,6 +5528,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. @@ -5473,7 +5556,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; @@ -5509,7 +5598,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; @@ -5519,6 +5608,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) diff --git a/client/mysqlimport.c b/client/mysqlimport.c index 2045c94619b..77d0cd0a7d3 100644 --- a/client/mysqlimport.c +++ b/client/mysqlimport.c @@ -1,5 +1,5 @@ /* - Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2000, 2012, 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 @@ -18,8 +18,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" @@ -60,6 +66,8 @@ 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 @@ -67,8 +75,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}, @@ -184,7 +192,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) @@ -196,6 +205,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("\ @@ -472,10 +483,18 @@ static void db_disconnect(char *host, MYSQL *mysql) static void safe_exit(int error, MYSQL *mysql) { - if (ignore_errors) + if (error && ignore_errors) return; 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); } @@ -594,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; @@ -606,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 */ #ifdef HAVE_LIBPTHREAD if (opt_use_threads && !lock_tables) @@ -684,11 +704,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 5677681541b..5cf2be1160c 100644 --- a/client/mysqlshow.c +++ b/client/mysqlshow.c @@ -1,5 +1,5 @@ /* - Copyright (c) 2000, 2012, 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 @@ -57,7 +57,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) @@ -67,11 +68,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) { @@ -171,7 +174,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, diff --git a/client/mysqlslap.c b/client/mysqlslap.c index ff5d2ddaf26..8e70a2609c3 100644 --- a/client/mysqlslap.c +++ b/client/mysqlslap.c @@ -1,5 +1,5 @@ /* - Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2005, 2012, 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 @@ -125,8 +125,6 @@ static char *host= NULL, *opt_password= NULL, *user= NULL, *post_system= NULL, *opt_mysql_unix_port= 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 +178,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 +293,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 +332,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 +349,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,9 +357,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 (!opt_only_print) { if (!(mysql_real_connect(&mysql, host, user, opt_password, @@ -423,6 +427,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 +466,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 +489,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 +506,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 +554,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 +585,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 +601,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,18 +615,17 @@ 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}, @@ -623,11 +635,11 @@ static struct my_option my_long_options[] = &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).", @@ -770,9 +782,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 +993,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 +1009,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 +1100,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 +1153,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 +1215,6 @@ get_options(int *argc,char ***argv) exit(1); } - - if (auto_generate_sql && num_of_query && auto_actual_queries) { fprintf(stderr, @@ -1246,6 +1255,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); } @@ -1258,6 +1268,7 @@ get_options(int *argc,char ***argv) num_char_cols_index= atoi(str->option); else num_char_cols_index= 0; + option_cleanup(str); } @@ -1379,9 +1390,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]); @@ -1406,9 +1417,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) @@ -1437,9 +1448,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) @@ -1468,9 +1479,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) @@ -1492,6 +1503,7 @@ get_options(int *argc,char ***argv) if (tty_password) opt_password= get_tty_password(NullS); + DBUG_RETURN(0); } @@ -1506,6 +1518,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); } @@ -1626,18 +1639,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; @@ -1651,8 +1652,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", @@ -1686,6 +1700,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); @@ -1822,6 +1837,7 @@ pthread_handler_t run_task(void *p) my_progname, mysql_error(mysql)); exit(0); } + set_mysql_connect_options(mysql); if (mysql_thread_init()) { @@ -1862,7 +1878,6 @@ limit_not_met: my_progname, mysql_error(mysql)); exit(0); } - if (slap_connect(mysql)) goto end; } @@ -1972,17 +1987,27 @@ 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; 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; @@ -2003,13 +2028,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; @@ -2018,7 +2045,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; @@ -2068,7 +2095,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++; } @@ -2126,6 +2153,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 */ @@ -2212,6 +2240,7 @@ slap_connect(MYSQL *mysql) int x, connect_error= 1; for (x= 0; x < 10; x++) { + set_mysql_connect_options(mysql); 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 22155005112..5415b653615 100644 --- a/client/mysqltest.cc +++ b/client/mysqltest.cc @@ -1,4 +1,5 @@ -/* Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2012, Oracle and/or its affiliates. + Copyright (c) 2009, 2012, Monty Program Ab. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -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,12 @@ #define SIGNAL_FMT "signal %d" #endif +static my_bool non_blocking_api_enabled= 0; +#if !defined(EMBEDDED_LIBRARY) +#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 +78,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 +97,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 +105,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 +124,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 +182,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 +217,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 +242,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 +255,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 +299,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 +353,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 +361,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 +373,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 +426,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 +443,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 +478,8 @@ const char *command_names[]= "move_file", "remove_files_wildcard", "send_eval", + "enable_prepare_warnings", + "disable_prepare_warnings", 0 }; @@ -488,7 +523,7 @@ struct st_command 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 +564,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 +623,8 @@ void free_all_replace(){ free_replace_column(); } +void var_set_int(const char* name, int value); + class LogFile { FILE* m_file; @@ -607,8 +645,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 +693,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 +715,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 +730,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 +740,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 +784,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 +808,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,12 +820,19 @@ 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 +/* 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 +863,10 @@ 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; default: DBUG_ASSERT(0); @@ -868,7 +919,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); @@ -916,8 +967,9 @@ 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) #endif /*EMBEDDED_LIBRARY*/ @@ -942,7 +994,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 +1274,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 +1335,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); @@ -1341,24 +1409,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 +1456,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) +{ + char *start= s; + struct st_test_file* err_file= cur_file; + if (err_file == file_stack) + return 0; + + for (;;) + { + 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) { - for (struct st_test_file* err_file= cur_file; - err_file != file_stack; - err_file--) + char *s= buf, *end= buf + len; + s+= my_snprintf(s, end - s, "mysqltest: "); + if (cur_file && cur_file != file_stack) { - fprintf(stderr, "included from %s at line %d:\n", - err_file->file_name, err_file->lineno); + 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 +1524,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 +1531,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 +1565,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 +1600,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 +1616,7 @@ void verbose_msg(const char *fmt, ...) vfprintf(stderr, fmt, args); fprintf(stderr, "\n"); va_end(args); + fflush(stderr); DBUG_VOID_RETURN; } @@ -1577,9 +1701,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 +1726,7 @@ static int run_command(char* cmd, } error= pclose(res_file); - return WEXITSTATUS(error); + DBUG_RETURN(WEXITSTATUS(error)); } @@ -1679,7 +1808,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 +1967,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 +2097,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 +2105,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 +2124,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 +2132,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 +2148,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 +2251,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 +2566,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 +2602,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 +2611,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 +2749,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 +2763,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 +2791,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 +2827,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 +2939,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 +2969,128 @@ 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); + 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 +3263,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 +3271,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 +3306,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 +3330,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 +3348,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 +3368,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 +3379,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 +3416,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 +3483,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 +3507,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 +3582,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_allow_opened(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 +3601,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,15 +3626,16 @@ 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); @@ -3337,11 +3651,11 @@ void do_remove_files_wildcard(struct st_command *command) if (ds_wild.length && wild_compare(file->name, ds_wild.str, 0)) continue; - 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; } @@ -3349,7 +3663,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); @@ -3388,8 +3702,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; @@ -3424,8 +3738,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; @@ -3469,7 +3783,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; @@ -3502,7 +3816,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; } @@ -3532,8 +3846,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; } @@ -3553,7 +3867,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"); @@ -3563,7 +3877,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; } @@ -3640,7 +3954,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; @@ -3683,7 +3997,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); @@ -3747,12 +4061,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)) { @@ -3907,7 +4221,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; } @@ -3953,9 +4267,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); @@ -3965,7 +4280,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; } @@ -4015,7 +4330,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; } @@ -4039,7 +4354,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" }, @@ -4154,8 +4470,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)) { @@ -4173,7 +4494,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); @@ -4186,7 +4507,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; @@ -4235,7 +4556,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); @@ -4265,7 +4586,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 */ @@ -4310,14 +4631,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 @@ -4355,7 +4676,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"); @@ -4612,7 +4933,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)) @@ -4684,16 +5006,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); @@ -4743,7 +5070,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"} @@ -4880,15 +5207,32 @@ const char *get_errname_from_code (uint error_code) 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; @@ -4965,7 +5309,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++; @@ -4998,11 +5342,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; } @@ -5087,11 +5435,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 @@ -5110,7 +5458,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); } @@ -5161,7 +5509,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; @@ -5183,10 +5531,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 @@ -5201,7 +5549,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); @@ -5456,7 +5805,8 @@ void do_connect(struct st_command *command) int con_port= opt_port; char *con_options; 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; @@ -5530,7 +5880,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++; @@ -5538,16 +5889,15 @@ 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 (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); @@ -5565,28 +5915,28 @@ 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); + mysql_options(con_slot->mysql, MYSQL_OPT_NONBLOCK, 0); 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; @@ -5595,12 +5945,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, + mysql_ssl_set(con_slot->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= !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 @@ -5614,22 +5964,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 @@ -5639,20 +5987,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)) @@ -5892,7 +6236,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) */ @@ -6250,7 +6594,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); } @@ -6477,13 +6821,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, @@ -6495,30 +6845,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).", @@ -6530,10 +6880,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.", @@ -6550,7 +6904,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}, @@ -6562,11 +6916,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}, @@ -6582,16 +6936,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} }; @@ -6668,6 +7026,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': @@ -6783,14 +7142,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; @@ -7079,6 +7453,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), @@ -7106,7 +7481,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, @@ -7115,8 +7490,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++) @@ -7211,6 +7589,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) @@ -7260,6 +7640,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 @@ -7277,12 +7673,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) { /* @@ -7436,7 +7838,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, @@ -7472,7 +7875,7 @@ void handle_error(struct st_command *command, DBUG_ENTER("handle_error"); - if (command->require_file[0]) + if (command->require_file) { /* The query after a "--require" failed. This is fine as long the server @@ -7490,7 +7893,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)); @@ -7536,13 +7943,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(); @@ -7569,15 +7978,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; } @@ -7729,10 +8140,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 @@ -7746,7 +8162,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)); @@ -7780,14 +8195,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 @@ -7797,6 +8204,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); + cur_con->stmt= NULL; + } + DBUG_VOID_RETURN; } @@ -7826,6 +8242,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); @@ -7833,7 +8250,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); } @@ -7853,7 +8271,7 @@ 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; @@ -7868,10 +8286,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; @@ -7898,7 +8316,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; @@ -7916,6 +8334,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)) @@ -7960,7 +8387,6 @@ void run_query(struct st_connection *cn, struct st_command *command, int flags) } dynstr_free(&query_str); - } if (sp_protocol_enabled && @@ -8040,7 +8466,7 @@ void run_query(struct st_connection *cn, struct st_command *command, int flags) 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); } @@ -8048,17 +8474,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 @@ -8080,8 +8507,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); } @@ -8218,9 +8645,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; } } @@ -8256,7 +8684,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); @@ -8290,14 +8718,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); @@ -8387,7 +8818,6 @@ static void init_signal_handling(void) #endif sigaction(SIGILL, &sa, NULL); sigaction(SIGFPE, &sa, NULL); - DBUG_VOID_RETURN; } @@ -8399,7 +8829,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; @@ -8431,8 +8866,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); @@ -8459,6 +8894,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); @@ -8478,6 +8914,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); @@ -8518,36 +8955,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 } @@ -8555,13 +8993,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' */ @@ -8576,14 +9015,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); @@ -8600,8 +9040,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 @@ -8612,8 +9056,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--) { @@ -8640,6 +9083,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; @@ -8682,6 +9127,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; @@ -8772,7 +9225,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); @@ -8870,12 +9325,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); @@ -8903,11 +9358,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; @@ -8915,7 +9376,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: /* @@ -8925,7 +9386,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 */ @@ -8936,7 +9397,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: @@ -9004,8 +9468,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. @@ -9086,7 +9548,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; @@ -9096,7 +9558,7 @@ void timer_output(void) ulonglong timer_now(void) { - return my_micro_time() / 1000; + return my_interval_timer() / 1000000; } @@ -9128,7 +9590,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)); @@ -9240,15 +9703,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; @@ -9280,7 +9743,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)); @@ -10398,17 +10861,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) @@ -10416,7 +10878,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; @@ -10428,11 +10891,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) @@ -10481,3 +10947,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..791a044e0e1 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)) @@ -58,6 +58,7 @@ char *batch_readline(LINE_BUFFER *line_buff) { char *pos; ulong out_length; + LINT_INIT(out_length); if (!(pos=intern_read_line(line_buff, &out_length))) return 0; diff --git a/client/sql_string.cc b/client/sql_string.cc.dontuse index 0ad79ba5633..64219886dd0 100644 --- a/client/sql_string.cc +++ b/client/sql_string.cc.dontuse @@ -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 @@ -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 0b92c370858..67155ebcee7 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, 2012, 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) @@ -272,7 +273,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) { @@ -361,4 +362,4 @@ public: } }; -#endif /* CLIENT_SQL_STRING_INCLUDED */ +#endif /* SQL_STRING_INCLUDED */ |