diff options
Diffstat (limited to 'client')
-rw-r--r-- | client/CMakeLists.txt | 80 | ||||
-rw-r--r-- | client/Makefile.am | 47 | ||||
-rw-r--r-- | client/client_priv.h | 13 | ||||
-rw-r--r-- | client/get_password.c | 4 | ||||
-rw-r--r-- | client/mysql.cc | 192 | ||||
-rw-r--r-- | client/mysql_upgrade.c | 15 | ||||
-rw-r--r-- | client/mysqlbinlog.cc | 80 | ||||
-rw-r--r-- | client/mysqlcheck.c | 80 | ||||
-rw-r--r-- | client/mysqldump.c | 623 | ||||
-rw-r--r-- | client/mysqlimport.c | 236 | ||||
-rw-r--r-- | client/mysqlmanager-pwgen.c | 161 | ||||
-rw-r--r-- | client/mysqlmanagerc.c | 174 | ||||
-rw-r--r-- | client/mysqlshow.c | 27 | ||||
-rw-r--r-- | client/mysqlslap.c | 1358 | ||||
-rw-r--r-- | client/mysqltest.c | 957 |
15 files changed, 3177 insertions, 870 deletions
diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt new file mode 100644 index 00000000000..26cc36c7f6f --- /dev/null +++ b/client/CMakeLists.txt @@ -0,0 +1,80 @@ +SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") +SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") + +# The old Windows build method used renamed (.cc -> .cpp) source files, fails +# in #include in mysqlbinlog.cc. So disable that using the USING_CMAKE define. +ADD_DEFINITIONS(-DUSING_CMAKE) +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/zlib + ${CMAKE_SOURCE_DIR}/extra/yassl/include + ${CMAKE_SOURCE_DIR}/libmysql + ${CMAKE_SOURCE_DIR}/regex + ${CMAKE_SOURCE_DIR}/sql + ${CMAKE_SOURCE_DIR}/strings) + +ADD_LIBRARY(mysqlclient ../mysys/array.c ../strings/bchange.c ../strings/bmove.c + ../strings/bmove_upp.c ../mysys/charset-def.c ../mysys/charset.c + ../sql-common/client.c ../strings/ctype-big5.c ../strings/ctype-bin.c + ../strings/ctype-cp932.c ../strings/ctype-czech.c ../strings/ctype-euc_kr.c + ../strings/ctype-eucjpms.c ../strings/ctype-extra.c ../strings/ctype-gb2312.c + ../strings/ctype-gbk.c ../strings/ctype-latin1.c ../strings/ctype-mb.c + ../strings/ctype-simple.c ../strings/ctype-sjis.c ../strings/ctype-tis620.c + ../strings/ctype-uca.c ../strings/ctype-ucs2.c ../strings/ctype-ujis.c + ../strings/ctype-utf8.c ../strings/ctype-win1250ch.c ../strings/ctype.c + ../mysys/default.c ../libmysql/errmsg.c ../mysys/errors.c + ../libmysql/get_password.c ../strings/int2str.c ../strings/is_prefix.c + ../libmysql/libmysql.c ../mysys/list.c ../strings/llstr.c + ../strings/longlong2str.c ../libmysql/manager.c ../mysys/mf_cache.c + ../mysys/mf_dirname.c ../mysys/mf_fn_ext.c ../mysys/mf_format.c + ../mysys/mf_iocache.c ../mysys/mf_iocache2.c ../mysys/mf_loadpath.c + ../mysys/mf_pack.c ../mysys/mf_path.c ../mysys/mf_tempfile.c ../mysys/mf_unixpath.c + ../mysys/mf_wcomp.c ../mysys/mulalloc.c ../mysys/my_access.c ../mysys/my_alloc.c + ../mysys/my_chsize.c ../mysys/my_compress.c ../mysys/my_create.c + ../mysys/my_delete.c ../mysys/my_div.c ../mysys/my_error.c ../mysys/my_file.c + ../mysys/my_fopen.c ../mysys/my_fstream.c ../mysys/my_gethostbyname.c + ../mysys/my_getopt.c ../mysys/my_getwd.c ../mysys/my_init.c ../mysys/my_lib.c + ../mysys/my_malloc.c ../mysys/my_messnc.c ../mysys/my_net.c ../mysys/my_once.c + ../mysys/my_open.c ../mysys/my_pread.c ../mysys/my_pthread.c ../mysys/my_read.c + ../mysys/my_realloc.c ../mysys/my_rename.c ../mysys/my_seek.c + ../mysys/my_static.c ../strings/my_strtoll10.c ../mysys/my_symlink.c + ../mysys/my_symlink2.c ../mysys/my_thr_init.c ../sql-common/my_time.c + ../strings/my_vsnprintf.c ../mysys/my_wincond.c ../mysys/my_winthread.c + ../mysys/my_write.c ../sql/net_serv.cc ../sql-common/pack.c ../sql/password.c + ../mysys/safemalloc.c ../mysys/sha1.c ../strings/str2int.c + ../strings/str_alloc.c ../strings/strcend.c ../strings/strcont.c ../strings/strend.c + ../strings/strfill.c ../mysys/string.c ../strings/strinstr.c ../strings/strmake.c + ../strings/strmov.c ../strings/strnlen.c ../strings/strnmov.c ../strings/strtod.c + ../strings/strtoll.c ../strings/strtoull.c ../strings/strxmov.c ../strings/strxnmov.c + ../mysys/thr_mutex.c ../mysys/typelib.c ../vio/vio.c ../vio/viosocket.c + ../vio/viossl.c ../vio/viosslfactories.c ../strings/xml.c) + +ADD_DEPENDENCIES(mysqlclient GenError) +ADD_EXECUTABLE(mysql completion_hash.cc mysql.cc readline.cc sql_string.cc) +LINK_DIRECTORIES(${MYSQL_BINARY_DIR}/mysys ${MYSQL_BINARY_DIR}/zlib) +TARGET_LINK_LIBRARIES(mysql mysqlclient mysys yassl taocrypt zlib dbug wsock32) + +ADD_EXECUTABLE(mysqltest mysqltest.c) +TARGET_LINK_LIBRARIES(mysqltest mysqlclient mysys yassl taocrypt zlib dbug regex wsock32) + +ADD_EXECUTABLE(mysqlcheck mysqlcheck.c) +TARGET_LINK_LIBRARIES(mysqlcheck mysqlclient dbug yassl taocrypt zlib wsock32) + +ADD_EXECUTABLE(mysqldump mysqldump.c ../sql-common/my_user.c) +TARGET_LINK_LIBRARIES(mysqldump mysqlclient mysys dbug yassl taocrypt zlib wsock32) + +ADD_EXECUTABLE(mysqlimport mysqlimport.c) +TARGET_LINK_LIBRARIES(mysqlimport mysqlclient mysys dbug yassl taocrypt zlib wsock32) + +ADD_EXECUTABLE(mysqlshow mysqlshow.c) +TARGET_LINK_LIBRARIES(mysqlshow mysqlclient mysys dbug yassl taocrypt zlib wsock32) + +ADD_EXECUTABLE(mysqlbinlog mysqlbinlog.cc ../mysys/mf_tempdir.c ../mysys/my_new.cc + ../mysys/my_bit.c ../mysys/my_bitmap.c ../mysys/my_vle.c + ../mysys/base64.c) +TARGET_LINK_LIBRARIES(mysqlbinlog mysqlclient dbug yassl taocrypt zlib wsock32) + +ADD_EXECUTABLE(mysqladmin mysqladmin.cc) +TARGET_LINK_LIBRARIES(mysqladmin mysqlclient mysys dbug yassl taocrypt zlib wsock32) + +ADD_EXECUTABLE(mysqlslap mysqlslap.c) +TARGET_LINK_LIBRARIES(mysqlslap mysqlclient mysys yassl taocrypt zlib wsock32 dbug) diff --git a/client/Makefile.am b/client/Makefile.am index 5787905fd35..d3e96dd126f 100644 --- a/client/Makefile.am +++ b/client/Makefile.am @@ -21,34 +21,54 @@ if HAVE_YASSL else yassl_dummy_link_fix= endif + +if THREAD_SAFE_CLIENT +LIBMYSQLCLIENT_LA = $(top_builddir)/libmysql_r/libmysqlclient_r.la +else +LIBMYSQLCLIENT_LA = $(top_builddir)/libmysql/libmysqlclient.la +endif + #AUTOMAKE_OPTIONS = nostdinc INCLUDES = -I$(top_builddir)/include \ -I$(top_srcdir)/include \ -I$(top_srcdir)/regex \ $(openssl_includes) LIBS = @CLIENT_LIBS@ -LDADD= @CLIENT_EXTRA_LDFLAGS@ \ - $(top_builddir)/libmysql/libmysqlclient.la +LDADD= @CLIENT_EXTRA_LDFLAGS@ $(CLIENT_THREAD_LIBS) \ + $(top_builddir)/libmysql/libmysqlclient.la bin_PROGRAMS = mysql mysqladmin mysqlcheck mysqlshow \ mysqldump mysqlimport mysqltest mysqlbinlog \ - mysql_upgrade \ - mysqltestmanagerc mysqltestmanager-pwgen + mysqlslap mysql_upgrade noinst_HEADERS = sql_string.h completion_hash.h my_readline.h \ client_priv.h mysql_SOURCES = mysql.cc readline.cc sql_string.cc completion_hash.cc mysqladmin_SOURCES = mysqladmin.cc mysql_LDADD = @readline_link@ @TERMCAP_LIB@ $(LDADD) $(CXXLDFLAGS) -mysqltest_SOURCES= mysqltest.c $(top_srcdir)/mysys/my_getsystime.c \ - $(yassl_dummy_link_fix) +mysqltest_SOURCES= mysqltest.c $(top_srcdir)/mysys/my_getsystime.c \ + $(yassl_dummy_link_fix) mysqltest_LDADD = $(top_builddir)/regex/libregex.a $(LDADD) -mysqlbinlog_SOURCES = mysqlbinlog.cc $(top_srcdir)/mysys/mf_tempdir.c $(top_srcdir)/mysys/my_new.cc +mysqlbinlog_SOURCES = mysqlbinlog.cc $(top_srcdir)/mysys/mf_tempdir.c \ + $(top_srcdir)/mysys/my_new.cc \ + $(top_srcdir)/mysys/my_bit.c \ + $(top_srcdir)/mysys/my_bitmap.c \ + $(top_srcdir)/mysys/my_vle.c \ + $(top_srcdir)/mysys/base64.c mysqlbinlog_LDADD = $(LDADD) $(CXXLDFLAGS) -mysqltestmanager_pwgen_SOURCES = mysqlmanager-pwgen.c -mysqltestmanagerc_SOURCES= mysqlmanagerc.c $(yassl_dummy_link_fix) -mysqlcheck_SOURCES= mysqlcheck.c $(yassl_dummy_link_fix) -mysqlshow_SOURCES= mysqlshow.c $(yassl_dummy_link_fix) -mysqldump_SOURCES= mysqldump.c my_user.c $(yassl_dummy_link_fix) -mysqlimport_SOURCES= mysqlimport.c $(yassl_dummy_link_fix) +mysqlslap_LDADD = $(CXXLDFLAGS) $(CLIENT_THREAD_LIBS) \ + @CLIENT_EXTRA_LDFLAGS@ \ + $(LIBMYSQLCLIENT_LA) \ + $(top_builddir)/mysys/libmysys.a +mysqlimport_LDADD = $(CXXLDFLAGS) $(CLIENT_THREAD_LIBS) \ + @CLIENT_EXTRA_LDFLAGS@ \ + $(LIBMYSQLCLIENT_LA) \ + $(top_builddir)/mysys/libmysys.a +mysqlcheck_SOURCES= mysqlcheck.c $(yassl_dummy_link_fix) +mysqlshow_SOURCES= mysqlshow.c $(yassl_dummy_link_fix) +mysqlslap_SOURCES= mysqlslap.c $(yassl_dummy_link_fix) +mysqldump_SOURCES= mysqldump.c my_user.c \ + $(top_srcdir)/mysys/mf_getdate.c \ + $(yassl_dummy_link_fix) +mysqlimport_SOURCES= mysqlimport.c $(yassl_dummy_link_fix) mysql_upgrade_SOURCES= mysql_upgrade.c $(yassl_dummy_link_fix) sql_src=log_event.h mysql_priv.h log_event.cc my_decimal.h my_decimal.cc strings_src=decimal.c @@ -57,6 +77,7 @@ strings_src=decimal.c DEFS = -DUNDEF_THREADS_HACK \ -DDEFAULT_MYSQL_HOME="\"$(prefix)\"" \ -DDATADIR="\"$(localstatedir)\"" +EXTRA_DIST = get_password.c CMakeLists.txt link_sources: for f in $(sql_src) ; do \ diff --git a/client/client_priv.h b/client/client_priv.h index 9e011144836..bcaa74d3228 100644 --- a/client/client_priv.h +++ b/client/client_priv.h @@ -24,8 +24,6 @@ #include <errmsg.h> #include <my_getopt.h> -/* We have to define 'enum options' identical in all files to keep OS2 happy */ - enum options_client { OPT_CHARSETS_DIR=256, OPT_DEFAULT_CHARSET, @@ -50,6 +48,15 @@ enum options_client OPT_NDBCLUSTER, OPT_NDB_CONNECTSTRING, #endif OPT_TRIGGERS, + OPT_MYSQL_ONLY_PRINT, + OPT_MYSQL_LOCK_DIRECTORY, + OPT_MYSQL_SLAP_SLAVE, + OPT_USE_THREADS, + OPT_IMPORT_USE_THREADS, + OPT_MYSQL_NUMBER_OF_QUERY, + OPT_MYSQL_PRESERVE_SCHEMA, OPT_IGNORE_TABLE,OPT_INSERT_IGNORE,OPT_SHOW_WARNINGS,OPT_DROP_DATABASE, - OPT_TZ_UTC, OPT_AUTO_CLOSE, OPT_SSL_VERIFY_SERVER_CERT + OPT_TZ_UTC, OPT_AUTO_CLOSE, OPT_CREATE_SLAP_SCHEMA, + OPT_MYSQL_REPLACE_INTO, OPT_BASE64_OUTPUT, OPT_SERVER_ID, + OPT_FIX_TABLE_NAMES, OPT_FIX_DB_NAMES, OPT_SSL_VERIFY_SERVER_CERT }; diff --git a/client/get_password.c b/client/get_password.c index 1b7b4e65a9f..b643b760718 100644 --- a/client/get_password.c +++ b/client/get_password.c @@ -64,7 +64,7 @@ /* were just going to fake it here and get input from the keyboard */ -char *get_tty_password(char *opt_message) +char *get_tty_password(const char *opt_message) { char to[80]; char *pos=to,*end=to+sizeof(to)-1; @@ -150,7 +150,7 @@ static void get_password(char *to,uint length,int fd,bool echo) #endif /* ! HAVE_GETPASS */ -char *get_tty_password(char *opt_message) +char *get_tty_password(const char *opt_message) { #ifdef HAVE_GETPASS char *passbuff; diff --git a/client/mysql.cc b/client/mysql.cc index 94b43d030e8..ae24c4e0d88 100644 --- a/client/mysql.cc +++ b/client/mysql.cc @@ -81,7 +81,7 @@ extern "C" { #endif #undef bcmp // Fix problem with new readline -#if defined( __WIN__) || defined(OS2) +#if defined( __WIN__) #include <conio.h> #elif !defined(__NETWARE__) #include <readline/readline.h> @@ -101,7 +101,7 @@ extern "C" { #define cmp_database(cs,A,B) strcmp((A),(B)) #endif -#if !defined( __WIN__) && !defined( OS2) && !defined(__NETWARE__) && (!defined(HAVE_mit_thread) || !defined(THREAD)) +#if !defined( __WIN__) && !defined(__NETWARE__) && !defined(THREAD) #define USE_POPEN #endif @@ -136,7 +136,7 @@ static my_bool info_flag=0,ignore_errors=0,wait_flag=0,quick=0, tty_password= 0, opt_nobeep=0, opt_reconnect=1, default_charset_used= 0, opt_secure_auth= 0, default_pager_set= 0, opt_sigint_ignore= 0, - show_warnings = 0; + show_warnings= 0, executing_query= 0, interrupted_query= 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 my_string opt_mysql_unix_port=0; @@ -338,7 +338,7 @@ static void end_timer(ulong start_time,char *buff); static void mysql_end_timer(ulong start_time,char *buff); static void nice_time(double sec,char *buff,bool part_second); static sig_handler mysql_end(int sig); - +static sig_handler mysql_sigint(int sig); int main(int argc,char *argv[]) { @@ -420,7 +420,8 @@ int main(int argc,char *argv[]) if (opt_sigint_ignore) signal(SIGINT, SIG_IGN); else - signal(SIGINT, mysql_end); // Catch SIGINT to clean up + signal(SIGINT, mysql_sigint); // Catch SIGINT to clean up + signal(SIGQUIT, mysql_end); // Catch SIGQUIT to clean up /* @@ -488,6 +489,28 @@ int main(int argc,char *argv[]) #endif } +sig_handler mysql_sigint(int sig) +{ + char kill_buffer[40]; + MYSQL *kill_mysql= NULL; + + signal(SIGINT, mysql_sigint); + + /* terminate if no query being executed, or we already tried interrupting */ + if (!executing_query || interrupted_query++) + mysql_end(sig); + + kill_mysql= mysql_init(kill_mysql); + if (!mysql_real_connect(kill_mysql,current_host, current_user, opt_password, + "", opt_mysql_port, opt_mysql_unix_port,0)) + mysql_end(sig); + /* kill_buffer is always big enough because max length of %lu is 15 */ + sprintf(kill_buffer, "KILL /*!50000 QUERY */ %lu", mysql_thread_id(&mysql)); + mysql_real_query(kill_mysql, kill_buffer, strlen(kill_buffer)); + mysql_close(kill_mysql); + tee_fprintf(stdout, "Query aborted by Ctrl+C\n"); +} + sig_handler mysql_end(int sig) { mysql_close(&mysql); @@ -531,6 +554,35 @@ sig_handler mysql_end(int sig) } +/* + This function handles sigint calls + If query is in process, kill query + 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 */ + if (!executing_query || interrupted_query) + mysql_end(sig); + + kill_mysql= mysql_init(kill_mysql); + if (!mysql_real_connect(kill_mysql,current_host, current_user, opt_password, + "", opt_mysql_port, opt_mysql_unix_port,0)) + mysql_end(sig); + + /* kill_buffer is always big enough because max length of %lu is 15 */ + sprintf(kill_buffer, "KILL /*!50000 QUERY */ %lu", mysql_thread_id(&mysql)); + mysql_real_query(kill_mysql, kill_buffer, strlen(kill_buffer)); + mysql_close(kill_mysql); + tee_fprintf(stdout, "Query aborted by Ctrl+C\n"); + + interrupted_query= 1; +} + + static struct my_option my_long_options[] = { {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, @@ -577,13 +629,13 @@ static struct my_option my_long_options[] = {"force", 'f', "Continue even if we get an sql error.", (gptr*) &ignore_errors, (gptr*) &ignore_errors, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"no-named-commands", 'g', - "Named commands are disabled. Use \\* form only, or use named commands only in the beginning of a line ending with a semicolon (;) Since version 10.9 the client now starts with this option ENABLED by default! Disable with '-G'. Long format commands still work from the first line. WARNING: option deprecated; use --disable-named-commands instead.", - 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, {"named-commands", 'G', "Enable named commands. Named commands mean this program's internal commands; see mysql> help . When enabled, the named commands can be used from any line of the query, otherwise only from the first line, before an enter. Disable with --disable-named-commands. This option is disabled by default.", (gptr*) &named_cmds, (gptr*) &named_cmds, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"no-named-commands", 'g', + "Named commands are disabled. Use \\* form only, or use named commands only in the beginning of a line ending with a semicolon (;) Since version 10.9 the client now starts with this option ENABLED by default! Disable with '-G'. Long format commands still work from the first line. WARNING: option deprecated; use --disable-named-commands instead.", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, {"ignore-spaces", 'i', "Ignore space after function names.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, {"local-infile", OPT_LOCAL_INFILE, "Enable/disable LOAD DATA LOCAL INFILE.", @@ -602,13 +654,6 @@ static struct my_option my_long_options[] = NO_ARG, 1, 0, 0, 0, 0, 0}, {"skip-line-numbers", 'L', "Don't write line number for errors. WARNING: -L is deprecated, use long version of this option instead.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, -#ifdef USE_POPEN - {"no-pager", OPT_NOPAGER, - "Disable pager and print to stdout. See interactive help (\\h) also. WARNING: option deprecated; use --disable-pager instead.", - 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, -#endif - {"no-tee", OPT_NOTEE, "Disable outfile. See interactive help (\\h) also. WARNING: option deprecated; use --disable-tee instead", 0, 0, 0, GET_NO_ARG, - NO_ARG, 0, 0, 0, 0, 0, 0}, {"unbuffered", 'n', "Flush buffer after each query.", (gptr*) &unbuffered, (gptr*) &unbuffered, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"column-names", OPT_COLUMN_NAMES, "Write column names in results.", @@ -628,8 +673,11 @@ static struct my_option my_long_options[] = 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, #ifdef USE_POPEN {"pager", OPT_PAGER, - "Pager to use to display results. If you don't supply an option the default pager is taken from your ENV variable PAGER. Valid pagers are less, more, cat [> filename], etc. See interactive help (\\h) also. This option does not work in batch mode.", + "Pager to use to display results. If you don't supply an option the default pager is taken from your ENV variable PAGER. Valid pagers are less, more, cat [> filename], etc. See interactive help (\\h) also. This option does not work in batch mode. Disable with --disable-pager. This option is disabled by default.", 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, + {"no-pager", OPT_NOPAGER, + "Disable pager and print to stdout. See interactive help (\\h) also. WARNING: option deprecated; use --disable-pager instead.", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, #endif {"password", 'p', "Password to use when connecting to server. If password is not given it's asked from the tty.", @@ -670,8 +718,10 @@ static struct my_option my_long_options[] = {"debug-info", 'T', "Print some debug info at exit.", (gptr*) &info_flag, (gptr*) &info_flag, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"tee", OPT_TEE, - "Append everything into outfile. See interactive help (\\h) also. Does not work in batch mode.", + "Append everything into outfile. See interactive help (\\h) also. Does not work in batch mode. Disable with --disable-tee. This option is disabled by default.", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"no-tee", OPT_NOTEE, "Disable outfile. See interactive help (\\h) also. WARNING: option deprecated; use --disable-tee instead", 0, 0, 0, GET_NO_ARG, + NO_ARG, 0, 0, 0, 0, 0, 0}, #ifndef DONT_ALLOW_USER_CHANGE {"user", 'u', "User for login if not current user.", (gptr*) ¤t_user, (gptr*) ¤t_user, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, @@ -971,7 +1021,7 @@ static int get_options(int argc, char **argv) static int read_and_execute(bool interactive) { -#if defined(OS2) || defined(__NETWARE__) +#if defined(__NETWARE__) char linebuffer[254]; String buffer; #endif @@ -1008,7 +1058,9 @@ static int read_and_execute(bool interactive) if (opt_outfile && glob_buffer.is_empty()) fflush(OUTFILE); -#if defined( __WIN__) || defined(OS2) || defined(__NETWARE__) + interrupted_query= 0; + +#if defined( __WIN__) || defined(__NETWARE__) tee_fputs(prompt, stdout); #if defined(__NETWARE__) line=fgets(linebuffer, sizeof(linebuffer)-1, stdin); @@ -1019,7 +1071,7 @@ static int read_and_execute(bool interactive) if (p != NULL) *p = '\0'; } -#elif defined(__WIN__) +#else defined(__WIN__) if (!tmpbuf.is_alloced()) tmpbuf.alloc(65535); tmpbuf.length(0); @@ -1035,32 +1087,12 @@ static int read_and_execute(bool interactive) */ } while (tmpbuf.alloced_length() <= clen); line= buffer.c_ptr(); -#else /* OS2 */ - buffer.length(0); - /* _cgets() expects the buffer size - 3 as the first byte */ - linebuffer[0]= (char) sizeof(linebuffer) - 3; - do - { - line= _cgets(linebuffer); - buffer.append(line, (unsigned char)linebuffer[1]); - /* - If _cgets() gets an input line that is linebuffer[0] bytes - long, the next call to _cgets() will return immediately with - linebuffer[1] == 0, and it does the same thing for input that - is linebuffer[0]-1 bytes long. So it appears that even though - _cgets() replaces the newline (which is two bytes on Window) with - a nil, it still needs the space in the linebuffer for it. This is, - naturally, undocumented. - */ - } while ((unsigned char)linebuffer[0] <= - (unsigned char)linebuffer[1] + 1); - line= buffer.c_ptr(); #endif /* __NETWARE__ */ #else if (opt_outfile) fputs(prompt, OUTFILE); line= readline(prompt); -#endif /* defined( __WIN__) || defined(OS2) || defined(__NETWARE__) */ +#endif /* defined( __WIN__) || defined(__NETWARE__) */ /* When Ctrl+d or Ctrl+z is pressed, the line may be NULL on some OS @@ -1112,7 +1144,7 @@ static int read_and_execute(bool interactive) } } -#if defined( __WIN__) || defined(OS2) || defined(__NETWARE__) +#if defined( __WIN__) || defined(__NETWARE__) buffer.free(); #endif #if defined( __WIN__) @@ -1221,7 +1253,8 @@ static bool add_line(String &buffer,char *line,char *in_string, continue; } #endif - if (!*ml_comment && inchar == '\\') + if (!*ml_comment && inchar == '\\' && + !(mysql.server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES)) { // Found possbile one character command like \c @@ -1794,7 +1827,14 @@ static int com_server_help(String *buffer __attribute__((unused)), if (help_arg[0] != '\'') { - (void) strxnmov(cmd_buf, sizeof(cmd_buf), "help '", help_arg, "'", NullS); + char *end_arg= strend(help_arg); + if(--end_arg) + { + while (my_isspace(charset_info,*end_arg)) + end_arg--; + *++end_arg= '\0'; + } + (void) strxnmov(cmd_buf, sizeof(cmd_buf), "help '", help_arg, "'", NullS); server_cmd= cmd_buf; } @@ -1880,9 +1920,13 @@ com_help(String *buffer __attribute__((unused)), { reg1 int i, j; char * help_arg= strchr(line,' '), buff[32], *end; - if (help_arg) - return com_server_help(buffer,line,help_arg+1); + { + while (my_isspace(charset_info,*help_arg)) + help_arg++; + if (*help_arg) + return com_server_help(buffer,line,help_arg); + } put_info("\nFor information about MySQL products and services, visit:\n" " http://www.mysql.com/\n" @@ -1961,6 +2005,7 @@ com_go(String *buffer,char *line __attribute__((unused))) uint error= 0; int err= 0; + interrupted_query= 0; if (!status.batch) { old_buffer= *buffer; // Save for edit command @@ -1997,6 +2042,8 @@ com_go(String *buffer,char *line __attribute__((unused))) timer=start_timer(); + executing_query= 1; + error= mysql_real_query_for_lazy(buffer->ptr(),buffer->length()); #ifdef HAVE_READLINE @@ -2010,7 +2057,9 @@ com_go(String *buffer,char *line __attribute__((unused))) if (error) { + executing_query= 0; buffer->length(0); // Remove query on error + executing_query= 0; return error; } error=0; @@ -2021,13 +2070,19 @@ com_go(String *buffer,char *line __attribute__((unused))) if (quick) { if (!(result=mysql_use_result(&mysql)) && mysql_field_count(&mysql)) - return put_error(&mysql); + { + executing_query= 0; + return put_error(&mysql); + } } else { error= mysql_store_result_for_lazy(&result); if (error) - return error; + { + executing_query= 0; + return error; + } } if (verbose >= 3 || !opt_silent) @@ -2088,6 +2143,9 @@ com_go(String *buffer,char *line __attribute__((unused))) fflush(stdout); mysql_free_result(result); } while (!(err= mysql_next_result(&mysql))); + + executing_query= 0; + if (err >= 1) error= put_error(&mysql); @@ -2102,6 +2160,7 @@ com_go(String *buffer,char *line __attribute__((unused))) (mysql.server_status & SERVER_STATUS_DB_DROPPED)) get_current_db(); + executing_query= 0; return error; /* New command follows */ } @@ -2315,6 +2374,8 @@ print_table_data(MYSQL_RES *result) while ((cur= mysql_fetch_row(result))) { + if (interrupted_query) + break; ulong *lengths= mysql_fetch_lengths(result); (void) tee_fputs("| ", PAGER); mysql_field_seek(result, 0); @@ -2323,11 +2384,9 @@ print_table_data(MYSQL_RES *result) const char *buffer; uint data_length; uint field_max_length; - bool right_justified; uint visible_length; uint extra_padding; - /* If this column may have a null value, use "NULL" for empty. */ if (! not_null_flag[off] && (cur[off] == NULL)) { buffer= "NULL"; @@ -2422,6 +2481,8 @@ print_table_data_html(MYSQL_RES *result) } while ((cur = mysql_fetch_row(result))) { + if (interrupted_query) + break; ulong *lengths=mysql_fetch_lengths(result); (void) tee_fputs("<TR>", PAGER); for (uint i=0; i < mysql_num_fields(result); i++) @@ -2451,6 +2512,8 @@ print_table_data_xml(MYSQL_RES *result) fields = mysql_fetch_fields(result); while ((cur = mysql_fetch_row(result))) { + if (interrupted_query) + break; ulong *lengths=mysql_fetch_lengths(result); (void) tee_fputs("\n <row>\n", PAGER); for (uint i=0; i < mysql_num_fields(result); i++) @@ -2485,6 +2548,8 @@ print_table_data_vertically(MYSQL_RES *result) mysql_field_seek(result,0); for (uint row_count=1; (cur= mysql_fetch_row(result)); row_count++) { + if (interrupted_query) + break; mysql_field_seek(result,0); tee_fprintf(PAGER, "*************************** %d. row ***************************\n", row_count); @@ -2858,7 +2923,7 @@ com_connect(String *buffer, char *line) bzero(buff, sizeof(buff)); if (buffer) { - strmov(buff, line); + strmake(buff, line, sizeof(buff)); tmp= get_arg(buff, 0); if (tmp && *tmp) { @@ -3454,9 +3519,6 @@ void tee_fprintf(FILE *file, const char *fmt, ...) NETWARE_YIELD; va_start(args, fmt); (void) vfprintf(file, fmt, args); -#ifdef OS2 - fflush( file); -#endif va_end(args); if (opt_outfile) @@ -3472,9 +3534,6 @@ void tee_fputs(const char *s, FILE *file) { NETWARE_YIELD; fputs(s, file); -#ifdef OS2 - fflush( file); -#endif if (opt_outfile) fputs(s, OUTFILE); } @@ -3485,9 +3544,6 @@ void tee_puts(const char *s, FILE *file) NETWARE_YIELD; fputs(s, file); fputs("\n", file); -#ifdef OS2 - fflush( file); -#endif if (opt_outfile) { fputs(s, OUTFILE); @@ -3498,14 +3554,11 @@ void tee_puts(const char *s, FILE *file) void tee_putc(int c, FILE *file) { putc(c, file); -#ifdef OS2 - fflush( file); -#endif if (opt_outfile) putc(c, OUTFILE); } -#if defined( __WIN__) || defined( OS2) || defined(__NETWARE__) +#if defined( __WIN__) || defined(__NETWARE__) #include <time.h> #else #include <sys/times.h> @@ -3517,7 +3570,7 @@ void tee_putc(int c, FILE *file) static ulong start_timer(void) { -#if defined( __WIN__) || defined( OS2) || defined(__NETWARE__) +#if defined( __WIN__) || defined(__NETWARE__) return clock(); #else struct tms tms_tmp; @@ -3642,12 +3695,14 @@ static const char* construct_prompt() case 'U': if (!full_username) init_username(); - processed_prompt.append(full_username); + processed_prompt.append(full_username ? full_username : + (current_user ? current_user : "(unknown)")); break; case 'u': if (!full_username) init_username(); - processed_prompt.append(part_username); + processed_prompt.append(part_username ? part_username : + (current_user ? current_user : "(unknown)")); break; case PROMPT_CHAR: processed_prompt.append(PROMPT_CHAR); @@ -3724,6 +3779,9 @@ static const char* construct_prompt() case 't': processed_prompt.append('\t'); break; + case 'l': + processed_prompt.append(delimiter_str); + break; default: processed_prompt.append(c); } diff --git a/client/mysql_upgrade.c b/client/mysql_upgrade.c index 3288b627554..c68f8777624 100644 --- a/client/mysql_upgrade.c +++ b/client/mysql_upgrade.c @@ -17,6 +17,14 @@ #include "client_priv.h" #include <my_dir.h> +#ifdef __WIN__ +const char *mysqlcheck_name= "mysqlcheck.exe"; +const char *mysql_name= "mysql.exe"; +#else +const char *mysqlcheck_name= "mysqlcheck"; +const char *mysql_name= "mysql"; +#endif /*__WIN__*/ + static my_bool opt_force= 0, opt_verbose= 0, tty_password= 0; static char *user= (char*) "root", *basedir= 0, *datadir= 0, *opt_password= 0; static my_bool upgrade_defaults_created= 0; @@ -65,7 +73,7 @@ static struct my_option my_long_options[]= }; static const char *load_default_groups[]= { - "mysql_upgrade", "client", 0 + "mysql_upgrade", 0 }; #include <help_end.h> @@ -272,7 +280,7 @@ int main(int argc, char **argv) strmake(bindir_end, "/bin", sizeof(bindir) - (int) (bindir_end - bindir)-1); if (!test_file_exists_res - (bindir, "mysqlcheck", mysqlcheck_line, &mysqlcheck_end)) + (bindir, mysqlcheck_name, mysqlcheck_line, &mysqlcheck_end)) { printf("Can't find program '%s'\n", mysqlcheck_line); puts("Please restart with --basedir=mysql-install-directory"); @@ -342,7 +350,8 @@ int main(int argc, char **argv) goto err_exit; fix_priv_tables: - if (!test_file_exists_res(bindir, "mysql", fix_priv_tables_cmd, &fix_cmd_end)) + if (!test_file_exists_res(bindir, mysql_name, + fix_priv_tables_cmd, &fix_cmd_end)) { puts("Could not find MySQL command-line client (mysql)."); puts diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc index 518ab7cf832..83385e9d005 100644 --- a/client/mysqlbinlog.cc +++ b/client/mysqlbinlog.cc @@ -36,6 +36,7 @@ /* That one is necessary for defines of OPTION_NO_FOREIGN_KEY_CHECKS etc */ #include "mysql_priv.h" #include "log_event.h" +#include "sql_common.h" #define BIN_LOG_HEADER_SIZE 4 #define PROBE_HEADER_LEN (EVENT_LEN_OFFSET+4) @@ -63,6 +64,7 @@ void sql_print_error(const char *format, ...); static bool one_database=0, to_last_remote_log= 0, disable_log_bin= 0; static bool opt_hexdump= 0; +static bool opt_base64_output= 0; static const char* database= 0; static my_bool force_opt= 0, short_form= 0, remote_opt= 0; static ulonglong offset = 0; @@ -517,6 +519,9 @@ int process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev, start_datetime= 0; offset= 0; // print everything and protect against cycling rec_count } + if (server_id && (server_id != ev->server_id)) { + DBUG_RETURN(0); + } if (((my_time_t)(ev->when) >= stop_datetime) || (pos >= stop_position_mot)) { @@ -531,11 +536,19 @@ int process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev, else print_event_info->hexdump_from= pos; + print_event_info->base64_output= opt_base64_output; + switch (ev_type) { case QUERY_EVENT: if (check_database(((Query_log_event*)ev)->db)) goto end; - ev->print(result_file, print_event_info); + if (opt_base64_output) + { + ev->print_header(result_file, print_event_info); + ev->print_base64(result_file, print_event_info); + } + else + ev->print(result_file, print_event_info); break; case CREATE_FILE_EVENT: { @@ -555,7 +568,13 @@ int process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev, filename and use LOCAL), prepared in the 'case EXEC_LOAD_EVENT' below. */ - ce->print(result_file, print_event_info, TRUE); + if (opt_base64_output) + { + ce->print_header(result_file, print_event_info); + ce->print_base64(result_file, print_event_info); + } + else + ce->print(result_file, print_event_info, TRUE); // If this binlog is not 3.23 ; why this test?? if (description_event->binlog_version >= 3) @@ -646,11 +665,18 @@ end: static struct my_option my_long_options[] = { - + {"help", '?', "Display this help and exit.", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, #ifdef __NETWARE__ {"autoclose", OPT_AUTO_CLOSE, "Auto close the screen on exit for Netware.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, #endif + {"base64-output", OPT_BASE64_OUTPUT, + "Print all binlog entries using base64 encoding. " + "This is for debugging only. Logs produced using this option " + "should not be applied on production systems.", + (gptr*) &opt_base64_output, (gptr*) &opt_base64_output, 0, GET_BOOL, + NO_ARG, 0, 0, 0, 0, 0, 0}, /* mysqlbinlog needs charsets knowledge, to be able to convert a charset number found in binlog to a charset name (to be able to print things @@ -660,13 +686,13 @@ static struct my_option my_long_options[] = {"character-sets-dir", OPT_CHARSETS_DIR, "Directory where character sets are.", (gptr*) &charsets_dir, (gptr*) &charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"database", 'd', "List entries for just this database (local log only).", + (gptr*) &database, (gptr*) &database, 0, GET_STR_ALLOC, REQUIRED_ARG, + 0, 0, 0, 0, 0, 0}, #ifndef DBUG_OFF {"debug", '#', "Output debug log.", (gptr*) &default_dbug_option, (gptr*) &default_dbug_option, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, #endif - {"database", 'd', "List entries for just this database (local log only).", - (gptr*) &database, (gptr*) &database, 0, GET_STR_ALLOC, REQUIRED_ARG, - 0, 0, 0, 0, 0, 0}, {"disable-log-bin", 'D', "Disable binary log. This is useful, if you " "enabled --to-last-log and are sending the output to the same MySQL server. " "This way you could avoid an endless loop. You would also like to use it " @@ -677,13 +703,14 @@ static struct my_option my_long_options[] = {"force-read", 'f', "Force reading unknown binlog events.", (gptr*) &force_opt, (gptr*) &force_opt, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"help", '?', "Display this help and exit.", - 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, {"hexdump", 'H', "Augment output with hexadecimal and ASCII event dump.", (gptr*) &opt_hexdump, (gptr*) &opt_hexdump, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"host", 'h', "Get the binlog from server.", (gptr*) &host, (gptr*) &host, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"local-load", 'l', "Prepare local temporary files for LOAD DATA INFILE in the specified directory.", + (gptr*) &dirname_for_local_load, (gptr*) &dirname_for_local_load, 0, + GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"offset", 'o', "Skip the first N entries.", (gptr*) &offset, (gptr*) &offset, 0, GET_ULL, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"password", 'p', "Password to connect to remote server.", @@ -699,15 +726,15 @@ static struct my_option my_long_options[] = {"protocol", OPT_MYSQL_PROTOCOL, "The protocol of connection (tcp,socket,pipe,memory).", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"result-file", 'r', "Direct output to a given file.", 0, 0, 0, GET_STR, - REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"read-from-remote-server", 'R', "Read binary logs from a MySQL server", (gptr*) &remote_opt, (gptr*) &remote_opt, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"open_files_limit", OPT_OPEN_FILES_LIMIT, - "Used to reserve file descriptors for usage by this program", - (gptr*) &open_files_limit, (gptr*) &open_files_limit, 0, GET_ULONG, - REQUIRED_ARG, MY_NFILE, 8, OS_FILE_LIMIT, 0, 1, 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, + "Extract only binlog entries created by the server having the given id.", + (gptr*) &server_id, (gptr*) &server_id, 0, GET_ULONG, + REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"set-charset", OPT_SET_CHARSET, "Add 'SET NAMES character_set' to the output.", (gptr*) &charset, (gptr*) &charset, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, @@ -725,6 +752,13 @@ static struct my_option my_long_options[] = "(you should probably use quotes for your shell to set it properly).", (gptr*) &start_datetime_str, (gptr*) &start_datetime_str, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"start-position", OPT_START_POSITION, + "Start reading the binlog at position N. Applies to the first binlog " + "passed on the command line.", + (gptr*) &start_position, (gptr*) &start_position, 0, GET_ULL, + REQUIRED_ARG, BIN_LOG_HEADER_SIZE, BIN_LOG_HEADER_SIZE, + /* COM_BINLOG_DUMP accepts only 4 bytes for the position */ + (ulonglong)(~(uint32)0), 0, 0, 0}, {"stop-datetime", OPT_STOP_DATETIME, "Stop reading the binlog at first event having a datetime equal or " "posterior to the argument; the argument must be a date and time " @@ -733,13 +767,6 @@ static struct my_option my_long_options[] = "(you should probably use quotes for your shell to set it properly).", (gptr*) &stop_datetime_str, (gptr*) &stop_datetime_str, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"start-position", OPT_START_POSITION, - "Start reading the binlog at position N. Applies to the first binlog " - "passed on the command line.", - (gptr*) &start_position, (gptr*) &start_position, 0, GET_ULL, - REQUIRED_ARG, BIN_LOG_HEADER_SIZE, BIN_LOG_HEADER_SIZE, - /* COM_BINLOG_DUMP accepts only 4 bytes for the position */ - (ulonglong)(~(uint32)0), 0, 0, 0}, {"stop-position", OPT_STOP_POSITION, "Stop reading the binlog at position N. Applies to the last binlog " "passed on the command line.", @@ -755,11 +782,12 @@ that may lead to an endless loop.", {"user", 'u', "Connect to the remote server as username.", (gptr*) &user, (gptr*) &user, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"local-load", 'l', "Prepare local temporary files for LOAD DATA INFILE in the specified directory.", - (gptr*) &dirname_for_local_load, (gptr*) &dirname_for_local_load, 0, - GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"version", 'V', "Print version and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"open_files_limit", OPT_OPEN_FILES_LIMIT, + "Used to reserve file descriptors for usage by this program", + (gptr*) &open_files_limit, (gptr*) &open_files_limit, 0, GET_ULONG, + REQUIRED_ARG, MY_NFILE, 8, OS_FILE_LIMIT, 0, 1, 0}, {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} }; @@ -1077,7 +1105,7 @@ could be out of memory"); const char *error_msg; Log_event *ev; - len = net_safe_read(mysql); + len= cli_safe_read(mysql); if (len == packet_error) { fprintf(stderr, "Got error reading packet from server: %s\n", @@ -1489,7 +1517,7 @@ int main(int argc, char** argv) the server */ -#ifdef __WIN__ +#if defined(__WIN__) && !defined(USING_CMAKE) #include "my_decimal.h" #include "decimal.c" #include "my_decimal.cpp" diff --git a/client/mysqlcheck.c b/client/mysqlcheck.c index 804fa14956f..fdfd9fc36fb 100644 --- a/client/mysqlcheck.c +++ b/client/mysqlcheck.c @@ -34,7 +34,8 @@ static my_bool opt_alldbs = 0, opt_check_only_changed = 0, opt_extended = 0, opt_compress = 0, opt_databases = 0, opt_fast = 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, opt_upgrade= 0; + tty_password = 0, opt_frm = 0, + opt_fix_table_names= 0, opt_fix_db_names= 0, opt_upgrade= 0; static uint verbose = 0, opt_mysql_port=0; static my_string opt_mysql_unix_port = 0; static char *opt_password = 0, *current_user = 0, @@ -48,7 +49,7 @@ static char *shared_memory_base_name=0; static uint opt_protocol=0; static CHARSET_INFO *charset_info= &my_charset_latin1; -enum operations {DO_CHECK, DO_REPAIR, DO_ANALYZE, DO_OPTIMIZE}; +enum operations { DO_CHECK, DO_REPAIR, DO_ANALYZE, DO_OPTIMIZE, DO_UPGRADE }; static struct my_option my_long_options[] = { @@ -101,6 +102,12 @@ static struct my_option my_long_options[] = {"fast",'F', "Check only tables that haven't been closed properly.", (gptr*) &opt_fast, (gptr*) &opt_fast, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"fix-db-names", OPT_FIX_DB_NAMES, "Fix database names.", + (gptr*) &opt_fix_db_names, (gptr*) &opt_fix_db_names, + 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"fix-table-names", OPT_FIX_TABLE_NAMES, "Fix table names.", + (gptr*) &opt_fix_table_names, (gptr*) &opt_fix_table_names, + 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"force", 'f', "Continue even if we get an sql-error.", (gptr*) &ignore_errors, (gptr*) &ignore_errors, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, @@ -174,6 +181,7 @@ static int process_all_databases(); static int process_databases(char **db_names); static int process_selected_tables(char *db, char **table_names, int tables); static int process_all_tables_in_db(char *database); +static int process_one_db(char *database); static int use_db(char *database); static int handle_request_for_tables(char *tables, uint length); static int dbConnect(char *host, char *user,char *passwd); @@ -254,6 +262,15 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), case 'o': what_to_do = DO_OPTIMIZE; break; + case OPT_FIX_DB_NAMES: + what_to_do= DO_UPGRADE; + default_charset= (char*) "utf8"; + opt_databases= 1; + break; + case OPT_FIX_TABLE_NAMES: + what_to_do= DO_UPGRADE; + default_charset= (char*) "utf8"; + break; case 'p': if (argument) { @@ -376,7 +393,7 @@ static int process_all_databases() } while ((row = mysql_fetch_row(tableres))) { - if (process_all_tables_in_db(row[0])) + if (process_one_db(row[0])) result = 1; } return result; @@ -389,7 +406,7 @@ static int process_databases(char **db_names) int result = 0; for ( ; *db_names ; db_names++) { - if (process_all_tables_in_db(*db_names)) + if (process_one_db(*db_names)) result = 1; } return result; @@ -458,7 +475,7 @@ static int process_all_tables_in_db(char *database) LINT_INIT(res); if (use_db(database)) return 1; - if (mysql_query(sock, "SHOW TABLES") || + if (mysql_query(sock, "SHOW TABLE STATUS") || !((res= mysql_store_result(sock)))) return 1; @@ -484,8 +501,12 @@ static int process_all_tables_in_db(char *database) } for (end = tables + 1; (row = mysql_fetch_row(res)) ;) { - end= fix_table_name(end, row[0]); - *end++= ','; + /* Skip tables with an engine of NULL (probably a view). */ + if (row[1]) + { + end= fix_table_name(end, row[0]); + *end++= ','; + } } *--end = 0; if (tot_length) @@ -495,13 +516,54 @@ static int process_all_tables_in_db(char *database) else { while ((row = mysql_fetch_row(res))) - handle_request_for_tables(row[0], strlen(row[0])); + /* Skip tables with an engine of NULL (probably a view). */ + if (row[1]) + { + handle_request_for_tables(row[0], strlen(row[0])); + } } mysql_free_result(res); return 0; } /* process_all_tables_in_db */ + +static int fix_object_name(const char *obj, const char *name) +{ + char qbuf[100 + NAME_LEN*4]; + int rc= 0; + if (strncmp(name, "#mysql50#", 9)) + return 1; + sprintf(qbuf, "RENAME %s `%s` TO `%s`", obj, name, name + 9); + if (mysql_query(sock, qbuf)) + { + fprintf(stderr, "Failed to %s\n", qbuf); + fprintf(stderr, "Error: %s\n", mysql_error(sock)); + rc= 1; + } + if (verbose) + printf("%-50s %s\n", name, rc ? "FAILED" : "OK"); + return rc; +} + + +static int process_one_db(char *database) +{ + if (what_to_do == DO_UPGRADE) + { + int rc= 0; + if (opt_fix_db_names && !strncmp(database,"#mysql50#", 9)) + { + rc= fix_object_name("DATABASE", database); + database+= 9; + } + if (rc || !opt_fix_table_names) + return rc; + } + return process_all_tables_in_db(database); +} + + static int use_db(char *database) { if (mysql_get_server_version(sock) >= 50003 && @@ -546,6 +608,8 @@ static int handle_request_for_tables(char *tables, uint length) case DO_OPTIMIZE: op = "OPTIMIZE"; break; + case DO_UPGRADE: + return fix_object_name("TABLE", tables); } if (!(query =(char *) my_malloc((sizeof(char)*(length+110)), MYF(MY_WME)))) diff --git a/client/mysqldump.c b/client/mysqldump.c index 3661473a8a6..72f60fc260d 100644 --- a/client/mysqldump.c +++ b/client/mysqldump.c @@ -50,6 +50,7 @@ #include "mysql.h" #include "mysql_version.h" #include "mysqld_error.h" +#include "../sql/ha_ndbcluster_tables.h" /* Exit codes */ @@ -83,7 +84,8 @@ static ulong find_set(TYPELIB *lib, const char *x, uint length, static char *alloc_query_str(ulong size); static char *field_escape(char *to,const char *from,uint length); -static my_bool verbose=0,tFlag=0,dFlag=0,quick= 1, extended_insert= 1, +static my_bool verbose= 0, opt_no_create_info= 0, opt_no_data= 0, + quick= 1, extended_insert= 1, lock_tables=1,ignore_errors=0,flush_logs=0, opt_drop=1,opt_keywords=0,opt_lock=1,opt_compress=0, opt_delayed=0,create_options=1,opt_quoted=0,opt_databases=0, @@ -94,9 +96,12 @@ static my_bool verbose=0,tFlag=0,dFlag=0,quick= 1, extended_insert= 1, opt_single_transaction=0, opt_comments= 0, opt_compact= 0, opt_hex_blob=0, opt_order_by_primary=0, opt_ignore=0, opt_complete_insert= 0, opt_drop_database= 0, - opt_dump_triggers= 0, opt_routines=0, opt_tz_utc=1; + opt_replace_into= 0, + opt_dump_triggers= 0, opt_routines=0, opt_tz_utc=1, + opt_events= 0, + opt_alltspcs=0; static ulong opt_max_allowed_packet, opt_net_buffer_length; -static MYSQL mysql_connection,*sock=0; +static MYSQL mysql_connection,*mysql=0; static my_bool insert_pat_inited=0; static DYNAMIC_STRING insert_pat; static char *opt_password=0,*current_user=0, @@ -131,7 +136,6 @@ static CHARSET_INFO *charset_info= &my_charset_latin1; const char *default_dbug_option="d:t:o,/tmp/mysqldump.trace"; /* have we seen any VIEWs during table scanning? */ my_bool seen_views= 0; - const char *compatible_mode_names[]= { "MYSQL323", "MYSQL40", "POSTGRESQL", "ORACLE", "MSSQL", "DB2", @@ -162,6 +166,10 @@ static struct my_option my_long_options[] = "Dump all the databases. This will be same as --databases with all databases selected.", (gptr*) &opt_alldbs, (gptr*) &opt_alldbs, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"all-tablespaces", 'Y', + "Dump all the tablespaces.", + (gptr*) &opt_alltspcs, (gptr*) &opt_alltspcs, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, + 0, 0}, {"add-drop-database", OPT_DROP_DATABASE, "Add a 'DROP DATABASE' before each create.", (gptr*) &opt_drop_database, (gptr*) &opt_drop_database, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, @@ -226,6 +234,9 @@ static struct my_option my_long_options[] = {"disable-keys", 'K', "'/*!40000 ALTER TABLE tb_name DISABLE KEYS */; and '/*!40000 ALTER TABLE tb_name ENABLE KEYS */; will be put in the output.", (gptr*) &opt_disable_keys, (gptr*) &opt_disable_keys, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, + {"events", 'E', "Dump events.", + (gptr*) &opt_events, (gptr*) &opt_events, 0, GET_BOOL, + NO_ARG, 0, 0, 0, 0, 0, 0}, {"extended-insert", 'e', "Allows utilization of the new, much faster INSERT syntax.", (gptr*) &extended_insert, (gptr*) &extended_insert, 0, GET_BOOL, NO_ARG, @@ -312,9 +323,10 @@ static struct my_option my_long_options[] = (gptr*) &opt_create_db, (gptr*) &opt_create_db, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"no-create-info", 't', "Don't write table creation info.", - (gptr*) &tFlag, (gptr*) &tFlag, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"no-data", 'd', "No row information.", (gptr*) &dFlag, (gptr*) &dFlag, 0, - GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + (gptr*) &opt_no_create_info, (gptr*) &opt_no_create_info, 0, GET_BOOL, + NO_ARG, 0, 0, 0, 0, 0, 0}, + {"no-data", 'd', "No row information.", (gptr*) &opt_no_data, + (gptr*) &opt_no_data, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"no-set-names", 'N', "Deprecated. Use --skip-set-charset instead.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, @@ -341,6 +353,9 @@ static struct my_option my_long_options[] = {"quote-names",'Q', "Quote table and column names with backticks (`).", (gptr*) &opt_quoted, (gptr*) &opt_quoted, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, + {"replace", OPT_MYSQL_REPLACE_INTO, "Use REPLACE INTO instead of INSERT INTO.", + (gptr*) &opt_replace_into, (gptr*) &opt_replace_into, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, + 0, 0}, {"result-file", 'r', "Direct output to a given file. This option should be used in MSDOS, because it prevents new line '\\n' from being converted to '\\r\\n' (carriage return + line feed).", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, @@ -427,6 +442,30 @@ static my_bool dump_all_views_in_db(char *database); #include <help_start.h> /* + Print the supplied message if in verbose mode + + SYNOPSIS + verbose_msg() + fmt format specifier + ... variable number of parameters +*/ + +static void verbose_msg(const char *fmt, ...) +{ + va_list args; + DBUG_ENTER("verbose_msg"); + + if (!verbose) + DBUG_VOID_RETURN; + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + + DBUG_VOID_RETURN; +} + +/* exit with message if ferror(file) SYNOPSIS @@ -565,6 +604,13 @@ static void write_footer(FILE *sql_file) fprintf(sql_file, "/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;\n"); fputs("\n", sql_file); + if (opt_comments) + { + char time_str[20]; + get_date(time_str, GETDATE_DATE_TIME, 0); + fprintf(sql_file, "-- Dump completed on %s\n", + time_str); + } check_io(sql_file); } } /* write_footer */ @@ -844,9 +890,9 @@ static int mysql_query_with_error_report(MYSQL *mysql_con, MYSQL_RES **res, if (mysql_query(mysql_con, query) || (res && !((*res)= mysql_store_result(mysql_con)))) { - my_printf_error(0, "%s: Couldn't execute '%s': %s (%d)", - MYF(0), my_progname, query, - mysql_error(mysql_con), mysql_errno(mysql_con)); + my_printf_error(0, "Couldn't execute '%s': %s (%d)", MYF(0), + query, mysql_error(mysql_con), mysql_errno(mysql_con)); + safe_exit(EX_MYSQLERR); return 1; } return 0; @@ -880,8 +926,8 @@ static void safe_exit(int error) first_error= error; if (ignore_errors) return; - if (sock) - mysql_close(sock); + if (mysql) + mysql_close(mysql); exit(error); } /* safe_exit */ @@ -894,10 +940,8 @@ static int dbConnect(char *host, char *user,char *passwd) { char buff[20+FN_REFLEN]; DBUG_ENTER("dbConnect"); - if (verbose) - { - fprintf(stderr, "-- Connecting to %s...\n", host ? host : "localhost"); - } + + verbose_msg("-- Connecting to %s...\n", host ? host : "localhost"); mysql_init(&mysql_connection); if (opt_compress) mysql_options(&mysql_connection,MYSQL_OPT_COMPRESS,NullS); @@ -915,7 +959,7 @@ static int dbConnect(char *host, char *user,char *passwd) mysql_options(&mysql_connection,MYSQL_SHARED_MEMORY_BASE_NAME,shared_memory_base_name); #endif mysql_options(&mysql_connection, MYSQL_SET_CHARSET_NAME, default_charset); - if (!(sock= mysql_real_connect(&mysql_connection,host,user,passwd, + if (!(mysql= mysql_real_connect(&mysql_connection,host,user,passwd, NULL,opt_mysql_port,opt_mysql_unix_port, 0))) { @@ -931,12 +975,11 @@ static int dbConnect(char *host, char *user,char *passwd) As we're going to set SQL_MODE, it would be lost on reconnect, so we cannot reconnect. */ - sock->reconnect= 0; + mysql->reconnect= 0; my_snprintf(buff, sizeof(buff), "/*!40100 SET @@SQL_MODE='%s' */", compatible_mode_normal_str); - if (mysql_query_with_error_report(sock, 0, buff)) + if (mysql_query_with_error_report(mysql, 0, buff)) { - mysql_close(sock); safe_exit(EX_MYSQLERR); return 1; } @@ -947,9 +990,8 @@ static int dbConnect(char *host, char *user,char *passwd) if (opt_tz_utc) { my_snprintf(buff, sizeof(buff), "/*!40103 SET TIME_ZONE='+00:00' */"); - if (mysql_query_with_error_report(sock, 0, buff)) + if (mysql_query_with_error_report(mysql, 0, buff)) { - mysql_close(sock); safe_exit(EX_MYSQLERR); return 1; } @@ -963,9 +1005,8 @@ static int dbConnect(char *host, char *user,char *passwd) */ static void dbDisconnect(char *host) { - if (verbose) - fprintf(stderr, "-- Disconnecting from %s...\n", host ? host : "localhost"); - mysql_close(sock); + verbose_msg("-- Disconnecting from %s...\n", host ? host : "localhost"); + mysql_close(mysql); } /* dbDisconnect */ @@ -1232,9 +1273,136 @@ static void print_xml_row(FILE *xml_file, const char *row_name, check_io(xml_file); } + +/* + create_delimiter + Generate a new (null-terminated) string that does not exist in query + and is therefore suitable for use as a query delimiter. Store this + delimiter in delimiter_buff . + + This is quite simple in that it doesn't even try to parse statements as an + interpreter would. It merely returns a string that is not in the query, which + is much more than adequate for constructing a delimiter. + + RETURN + ptr to the delimiter on Success + NULL on Failure +*/ +static char *create_delimiter(char *query, char *delimiter_buff, + int delimiter_max_size) +{ + int proposed_length; + char *presence; + + delimiter_buff[0]= ';'; /* start with one semicolon, and */ + + for (proposed_length= 2; proposed_length < delimiter_max_size; + delimiter_max_size++) { + + delimiter_buff[proposed_length-1]= ';'; /* add semicolons, until */ + delimiter_buff[proposed_length]= '\0'; + + presence = strstr(query, delimiter_buff); + if (presence == NULL) { /* the proposed delimiter is not in the query. */ + return delimiter_buff; + } + + } + return NULL; /* but if we run out of space, return nothing at all. */ +} + + +/* + dump_events_for_db + -- retrieves list of events for a given db, and prints out + the CREATE EVENT statement into the output (the dump). + + RETURN + 0 Success + 1 Error +*/ +static uint dump_events_for_db(char *db) +{ + char query_buff[QUERY_LENGTH]; + char db_name_buff[NAME_LEN*2+3], name_buff[NAME_LEN*2+3]; + char *event_name; + char delimiter[QUERY_LENGTH], *delimit_test; + FILE *sql_file= md_result_file; + MYSQL_RES *event_res, *event_list_res; + MYSQL_ROW row, event_list_row; + DBUG_ENTER("dump_events_for_db"); + DBUG_PRINT("enter", ("db: '%s'", db)); + + mysql_real_escape_string(mysql, db_name_buff, db, strlen(db)); + + /* nice comments */ + if (opt_comments) + fprintf(sql_file, "\n--\n-- Dumping events for database '%s'\n--\n", db); + + /* + not using "mysql_query_with_error_report" because we may have not + enough privileges to lock mysql.events. + */ + if (lock_tables) + mysql_query(mysql, "LOCK TABLES mysql.event READ"); + + if (mysql_query_with_error_report(mysql, &event_list_res, "show events")) + { + safe_exit(EX_MYSQLERR); + DBUG_RETURN(0); + } + + strcpy(delimiter, ";"); + if (mysql_num_rows(event_list_res) > 0) + { + while ((event_list_row= mysql_fetch_row(event_list_res)) != NULL) + { + event_name= quote_name(event_list_row[1], name_buff, 0); + DBUG_PRINT("info", ("retrieving CREATE EVENT for %s", name_buff)); + my_snprintf(query_buff, sizeof(query_buff), "SHOW CREATE EVENT %s", + event_name); + + if (mysql_query_with_error_report(mysql, &event_res, query_buff)) + DBUG_RETURN(1); + + while ((row= mysql_fetch_row(event_res)) != NULL) + { + /* + if the user has EXECUTE privilege he can see event names, but not the + event body! + */ + if (strlen(row[2]) != 0) + { + if (opt_drop) + fprintf(sql_file, "/*!50106 DROP EVENT IF EXISTS %s */%s\n", + event_name, delimiter); + + delimit_test= create_delimiter(row[2], delimiter, sizeof(delimiter)); + if (delimit_test == NULL) { + fprintf(stderr, "%s: Warning: Can't dump event '%s'\n", + event_name, my_progname); + DBUG_RETURN(1); + } + + fprintf(sql_file, "DELIMITER %s\n", delimiter); + fprintf(sql_file, "/*!50106 %s */ %s\n", row[2], delimiter); + } + } /* end of event printing */ + } /* end of list of events */ + fprintf(sql_file, "DELIMITER ;\n"); + mysql_free_result(event_res); + } + mysql_free_result(event_list_res); + + if (lock_tables) + VOID(mysql_query_with_error_report(mysql, 0, "UNLOCK TABLES")); + DBUG_RETURN(0); +} + + /* dump_routines_for_db - -- retrievs list of routines for a given db, and prints out + -- retrieves list of routines for a given db, and prints out the CREATE PROCEDURE definition into the output (the dump). This function has logic to print the appropriate syntax depending on whether @@ -1247,7 +1415,7 @@ static void print_xml_row(FILE *xml_file, const char *row_name, static uint dump_routines_for_db(char *db) { - char query_buff[512]; + char query_buff[QUERY_LENGTH]; const char *routine_type[]= {"FUNCTION", "PROCEDURE"}; char db_name_buff[NAME_LEN*2+3], name_buff[NAME_LEN*2+3]; char *routine_name; @@ -1258,7 +1426,7 @@ static uint dump_routines_for_db(char *db) DBUG_ENTER("dump_routines_for_db"); DBUG_PRINT("enter", ("db: '%s'", db)); - mysql_real_escape_string(sock, db_name_buff, db, strlen(db)); + mysql_real_escape_string(mysql, db_name_buff, db, strlen(db)); /* nice comments */ if (opt_comments) @@ -1269,7 +1437,7 @@ static uint dump_routines_for_db(char *db) enough privileges to lock mysql.proc. */ if (lock_tables) - mysql_query(sock, "LOCK TABLES mysql.proc READ"); + mysql_query(mysql, "LOCK TABLES mysql.proc READ"); fprintf(sql_file, "DELIMITER ;;\n"); @@ -1280,7 +1448,7 @@ static uint dump_routines_for_db(char *db) "SHOW %s STATUS WHERE Db = '%s'", routine_type[i], db_name_buff); - if (mysql_query_with_error_report(sock, &routine_list_res, query_buff)) + if (mysql_query_with_error_report(mysql, &routine_list_res, query_buff)) DBUG_RETURN(1); if (mysql_num_rows(routine_list_res)) @@ -1288,13 +1456,13 @@ static uint dump_routines_for_db(char *db) while ((routine_list_row= mysql_fetch_row(routine_list_res))) { + routine_name= quote_name(routine_list_row[1], name_buff, 0); DBUG_PRINT("info", ("retrieving CREATE %s for %s", routine_type[i], name_buff)); - routine_name= quote_name(routine_list_row[1], name_buff, 0); my_snprintf(query_buff, sizeof(query_buff), "SHOW CREATE %s %s", routine_type[i], routine_name); - if (mysql_query_with_error_report(sock, &routine_res, query_buff)) + if (mysql_query_with_error_report(mysql, &routine_res, query_buff)) DBUG_RETURN(1); while ((row= mysql_fetch_row(routine_res))) @@ -1376,7 +1544,7 @@ static uint dump_routines_for_db(char *db) fprintf(sql_file, "DELIMITER ;\n"); if (lock_tables) - VOID(mysql_query_with_error_report(sock, 0, "UNLOCK TABLES")); + VOID(mysql_query_with_error_report(mysql, 0, "UNLOCK TABLES")); DBUG_RETURN(0); } @@ -1402,8 +1570,8 @@ static uint get_table_structure(char *table, char *db, char *table_type, my_ulonglong num_fields; char *result_table, *opt_quoted_table; const char *insert_option; - char name_buff[NAME_LEN+3],table_buff[NAME_LEN*2+3]; - char table_buff2[NAME_LEN*2+3], query_buff[512]; + char name_buff[NAME_LEN+3],table_buff[NAME_LEN*2+3]; + char table_buff2[NAME_LEN*2+3], query_buff[QUERY_LENGTH]; FILE *sql_file= md_result_file; int len; MYSQL_RES *result; @@ -1418,10 +1586,8 @@ static uint get_table_structure(char *table, char *db, char *table_type, if (delayed && (*ignore_flag & IGNORE_INSERT_DELAYED)) { delayed= 0; - if (verbose) - fprintf(stderr, - "-- Warning: Unable to use delayed inserts for table '%s' " - "because it's of type %s\n", table, table_type); + verbose_msg("-- Warning: Unable to use delayed inserts for table '%s' " + "because it's of type %s\n", table, table_type); } complete_insert= 0; @@ -1437,8 +1603,7 @@ static uint get_table_structure(char *table, char *db, char *table_type, insert_option= ((delayed && opt_ignore) ? " DELAYED IGNORE " : delayed ? " DELAYED " : opt_ignore ? " IGNORE " : ""); - if (verbose) - fprintf(stderr, "-- Retrieving table structure for table %s...\n", table); + verbose_msg("-- Retrieving table structure for table %s...\n", table); len= my_snprintf(query_buff, sizeof(query_buff), "SET OPTION SQL_QUOTE_SHOW_CREATE=%d", @@ -1453,17 +1618,17 @@ static uint get_table_structure(char *table, char *db, char *table_type, if (opt_order_by_primary) order_by= primary_key_fields(result_table); - if (!opt_xml && !mysql_query_with_error_report(sock, 0, query_buff)) + if (!opt_xml && !mysql_query_with_error_report(mysql, 0, query_buff)) { /* using SHOW CREATE statement */ - if (!tFlag) + if (!opt_no_create_info) { /* Make an sql-file, if path was given iow. option -T was given */ char buff[20+FN_REFLEN]; MYSQL_FIELD *field; my_snprintf(buff, sizeof(buff), "show create table %s", result_table); - if (mysql_query_with_error_report(sock, 0, buff)) + if (mysql_query_with_error_report(mysql, 0, buff)) { safe_exit(EX_MYSQLERR); DBUG_RETURN(0); @@ -1499,14 +1664,13 @@ static uint get_table_structure(char *table, char *db, char *table_type, check_io(sql_file); } - result= mysql_store_result(sock); + result= mysql_store_result(mysql); field= mysql_fetch_field_direct(result, 0); if (strcmp(field->name, "View") == 0) { char *scv_buff= NULL; - if (verbose) - fprintf(stderr, "-- It's a view, create dummy table for view\n"); + verbose_msg("-- It's a view, create dummy table for view\n"); /* save "show create" statement for later */ if ((row= mysql_fetch_row(result)) && (scv_buff=row[1])) @@ -1527,7 +1691,7 @@ static uint get_table_structure(char *table, char *db, char *table_type, */ my_snprintf(query_buff, sizeof(query_buff), "SHOW FIELDS FROM %s", result_table); - if (mysql_query_with_error_report(sock, 0, query_buff)) + if (mysql_query_with_error_report(mysql, 0, query_buff)) { /* View references invalid or privileged table/col/fun (err 1356), @@ -1535,7 +1699,7 @@ static uint get_table_structure(char *table, char *db, char *table_type, a comment with the view's 'show create' statement. (Bug #17371) */ - if (mysql_errno(sock) == ER_VIEW_INVALID) + if (mysql_errno(mysql) == ER_VIEW_INVALID) fprintf(sql_file, "\n-- failed on view %s: %s\n\n", result_table, scv_buff ? scv_buff : ""); my_free(scv_buff, MYF(MY_ALLOW_ZERO_PTR)); @@ -1546,7 +1710,7 @@ static uint get_table_structure(char *table, char *db, char *table_type, else my_free(scv_buff, MYF(MY_ALLOW_ZERO_PTR)); - if ((result= mysql_store_result(sock))) + if ((result= mysql_store_result(mysql))) { if (mysql_num_rows(result)) { @@ -1599,7 +1763,7 @@ static uint get_table_structure(char *table, char *db, char *table_type, } my_snprintf(query_buff, sizeof(query_buff), "show fields from %s", result_table); - if (mysql_query_with_error_report(sock, &result, query_buff)) + if (mysql_query_with_error_report(mysql, &result, query_buff)) { if (path) my_fclose(sql_file, MYF(MY_WME)); @@ -1615,7 +1779,10 @@ static uint get_table_structure(char *table, char *db, char *table_type, */ if (write_data) { - dynstr_append_mem(&insert_pat, "INSERT ", 7); + if (opt_replace_into) + dynstr_append_mem(&insert_pat, "REPLACE ", 8); + else + dynstr_append_mem(&insert_pat, "INSERT ", 7); dynstr_append(&insert_pat, insert_option); dynstr_append_mem(&insert_pat, "INTO ", 5); dynstr_append(&insert_pat, opt_quoted_table); @@ -1649,21 +1816,19 @@ static uint get_table_structure(char *table, char *db, char *table_type, } else { - if (verbose) - fprintf(stderr, - "%s: Warning: Can't set SQL_QUOTE_SHOW_CREATE option (%s)\n", - my_progname, mysql_error(sock)); + verbose_msg("%s: Warning: Can't set SQL_QUOTE_SHOW_CREATE option (%s)\n", + my_progname, mysql_error(mysql)); my_snprintf(query_buff, sizeof(query_buff), "show fields from %s", result_table); - if (mysql_query_with_error_report(sock, &result, query_buff)) + if (mysql_query_with_error_report(mysql, &result, query_buff)) { safe_exit(EX_MYSQLERR); DBUG_RETURN(0); } /* Make an sql-file, if path was given iow. option -T was given */ - if (!tFlag) + if (!opt_no_create_info) { if (path) { @@ -1688,7 +1853,10 @@ static uint get_table_structure(char *table, char *db, char *table_type, if (write_data) { - dynstr_append_mem(&insert_pat, "INSERT ", 7); + if (opt_replace_into) + dynstr_append_mem(&insert_pat, "REPLACE ", 8); + else + dynstr_append_mem(&insert_pat, "INSERT ", 7); dynstr_append(&insert_pat, insert_option); dynstr_append_mem(&insert_pat, "INTO ", 5); dynstr_append(&insert_pat, result_table); @@ -1707,7 +1875,7 @@ static uint get_table_structure(char *table, char *db, char *table_type, ulong *lengths= mysql_fetch_lengths(result); if (init) { - if (!opt_xml && !tFlag) + if (!opt_xml && !opt_no_create_info) { fputs(",\n",sql_file); check_io(sql_file); @@ -1719,7 +1887,7 @@ static uint get_table_structure(char *table, char *db, char *table_type, if (opt_complete_insert) dynstr_append(&insert_pat, quote_name(row[SHOW_FIELDNAME], name_buff, 0)); - if (!tFlag) + if (!opt_no_create_info) { if (opt_xml) { @@ -1749,22 +1917,22 @@ static uint get_table_structure(char *table, char *db, char *table_type, } num_fields= mysql_num_rows(result); mysql_free_result(result); - if (!tFlag) + if (!opt_no_create_info) { /* Make an sql-file, if path was given iow. option -T was given */ char buff[20+FN_REFLEN]; uint keynr,primary_key; my_snprintf(buff, sizeof(buff), "show keys from %s", result_table); - if (mysql_query_with_error_report(sock, &result, buff)) + if (mysql_query_with_error_report(mysql, &result, buff)) { - if (mysql_errno(sock) == ER_WRONG_OBJECT) + if (mysql_errno(mysql) == ER_WRONG_OBJECT) { /* it is VIEW */ fputs("\t\t<options Comment=\"view\" />\n", sql_file); goto continue_xml; } fprintf(stderr, "%s: Can't get keys for table %s (%s)\n", - my_progname, result_table, mysql_error(sock)); + my_progname, result_table, mysql_error(mysql)); if (path) my_fclose(sql_file, MYF(MY_WME)); safe_exit(EX_MYSQLERR); @@ -1837,21 +2005,19 @@ static uint get_table_structure(char *table, char *db, char *table_type, my_snprintf(buff, sizeof(buff), "show table status like %s", quote_for_like(table, show_name_buff)); - if (mysql_query_with_error_report(sock, &result, buff)) + if (mysql_query_with_error_report(mysql, &result, buff)) { - if (mysql_errno(sock) != ER_PARSE_ERROR) + if (mysql_errno(mysql) != ER_PARSE_ERROR) { /* If old MySQL version */ - if (verbose) - fprintf(stderr, - "-- Warning: Couldn't get status information for table %s (%s)\n", - result_table,mysql_error(sock)); + verbose_msg("-- Warning: Couldn't get status information for " \ + "table %s (%s)\n", result_table,mysql_error(mysql)); } } else if (!(row= mysql_fetch_row(result))) { fprintf(stderr, "Error: Couldn't read status information for table %s (%s)\n", - result_table,mysql_error(sock)); + result_table,mysql_error(mysql)); } else { @@ -1907,7 +2073,7 @@ static void dump_triggers_for_table (char *table, char *db) { char *result_table; char name_buff[NAME_LEN*4+3], table_buff[NAME_LEN*2+3]; - char query_buff[512]; + char query_buff[QUERY_LENGTH]; uint old_opt_compatible_mode=opt_compatible_mode; FILE *sql_file= md_result_file; MYSQL_RES *result; @@ -1924,7 +2090,7 @@ static void dump_triggers_for_table (char *table, char *db) "SHOW TRIGGERS LIKE %s", quote_for_like(table, name_buff)); - if (mysql_query_with_error_report(sock, &result, query_buff)) + if (mysql_query_with_error_report(mysql, &result, query_buff)) { if (path) my_fclose(sql_file, MYF(MY_WME)); @@ -2094,12 +2260,10 @@ static void dump_table(char *table, char *db) return; /* Check --no-data flag */ - if (dFlag) + if (opt_no_data) { - if (verbose) - fprintf(stderr, - "-- Skipping dump data for table '%s', --no-data was used\n", - table); + verbose_msg("-- Skipping dump data for table '%s', --no-data was used\n", + table); DBUG_VOID_RETURN; } @@ -2112,27 +2276,22 @@ static void dump_table(char *table, char *db) */ if (ignore_flag & IGNORE_DATA) { - if (verbose) - fprintf(stderr, - "-- Warning: Skipping data for table '%s' because it's of type %s\n", - table, table_type); + verbose_msg("-- Warning: Skipping data for table '%s' because " \ + "it's of type %s\n", table, table_type); DBUG_VOID_RETURN; } /* Check that there are any fields in the table */ if (num_fields == 0) { - if (verbose) - fprintf(stderr, - "-- Skipping dump data for table '%s', it has no fields\n", - table); + verbose_msg("-- Skipping dump data for table '%s', it has no fields\n", + table); DBUG_VOID_RETURN; } result_table= quote_name(table,table_buff, 1); opt_quoted_table= quote_name(table, table_buff2, 0); - if (verbose) - fprintf(stderr, "-- Sending SELECT query...\n"); + verbose_msg("-- Sending SELECT query...\n"); if (path) { char filename[FN_REFLEN], tmp_path[FN_REFLEN]; @@ -2170,9 +2329,9 @@ static void dump_table(char *table, char *db) if (order_by) end= strxmov(end, " ORDER BY ", order_by, NullS); } - if (mysql_real_query(sock, query, (uint) (end - query))) + if (mysql_real_query(mysql, query, (uint) (end - query))) { - DB_error(sock, "when executing 'SELECT INTO OUTFILE'"); + DB_error(mysql, "when executing 'SELECT INTO OUTFILE'"); DBUG_VOID_RETURN; } } @@ -2218,19 +2377,19 @@ static void dump_table(char *table, char *db) fputs("\n", md_result_file); check_io(md_result_file); } - if (mysql_query_with_error_report(sock, 0, query)) - DB_error(sock, "when retrieving data from server"); + if (mysql_query_with_error_report(mysql, 0, query)) + DB_error(mysql, "when retrieving data from server"); if (quick) - res=mysql_use_result(sock); + res=mysql_use_result(mysql); else - res=mysql_store_result(sock); + res=mysql_store_result(mysql); if (!res) { - DB_error(sock, "when retrieving data from server"); + DB_error(mysql, "when retrieving data from server"); goto err; } - if (verbose) - fprintf(stderr, "-- Retrieving rows...\n"); + + verbose_msg("-- Retrieving rows...\n"); if (mysql_num_fields(res) != num_fields) { fprintf(stderr,"%s: Error in field count for table: %s ! Aborting.\n", @@ -2499,13 +2658,13 @@ static void dump_table(char *table, char *db) fputs(";\n", md_result_file); /* If not empty table */ fflush(md_result_file); check_io(md_result_file); - if (mysql_errno(sock)) + if (mysql_errno(mysql)) { my_snprintf(query, QUERY_LENGTH, "%s: Error %d: %s when dumping table %s at row: %ld\n", my_progname, - mysql_errno(sock), - mysql_error(sock), + mysql_errno(mysql), + mysql_error(mysql), result_table, rownr); fputs(query,stderr); @@ -2551,7 +2710,7 @@ static char *getTableName(int reset) if (!res) { - if (!(res= mysql_list_tables(sock,NullS))) + if (!(res= mysql_list_tables(mysql,NullS))) return(NULL); } if ((row= mysql_fetch_row(res))) @@ -2568,13 +2727,137 @@ static char *getTableName(int reset) } /* getTableName */ +/* + dump all logfile groups and tablespaces +*/ + +static int dump_all_tablespaces() +{ + MYSQL_ROW row; + MYSQL_RES *tableres; + char buf[FN_REFLEN]; + int first; + + if (mysql_query_with_error_report(mysql, &tableres, + "SELECT DISTINCT" + " LOGFILE_GROUP_NAME," + " FILE_NAME," + " TOTAL_EXTENTS," + " INITIAL_SIZE," + " ENGINE" + " FROM INFORMATION_SCHEMA.FILES" + " WHERE FILE_TYPE = \"UNDO LOG\"" + " ORDER BY LOGFILE_GROUP_NAME")) + return 1; + + buf[0]= 0; + while ((row= mysql_fetch_row(tableres))) + { + if (strcmp(buf, row[0]) != 0) + first= 1; + if (first) + { + if (!opt_xml && opt_comments) + { + fprintf(md_result_file,"\n--\n-- Logfile group: %s\n--\n", row[0]); + check_io(md_result_file); + } + fprintf(md_result_file, "\nCREATE"); + } + else + { + fprintf(md_result_file, "\nALTER"); + } + fprintf(md_result_file, + " LOGFILE GROUP %s\n" + " ADD UNDOFILE '%s'\n", + row[0], + row[1]); + if (first) + { + fprintf(md_result_file, + " UNDO_BUFFER_SIZE %s\n", + row[2]); + } + fprintf(md_result_file, + " INITIAL_SIZE %s\n" + " ENGINE=%s;\n", + row[3], + row[4]); + check_io(md_result_file); + if (first) + { + first= 0; + strxmov(buf, row[0], NullS); + } + } + + if (mysql_query_with_error_report(mysql, &tableres, + "SELECT DISTINCT" + " TABLESPACE_NAME," + " FILE_NAME," + " LOGFILE_GROUP_NAME," + " EXTENT_SIZE," + " INITIAL_SIZE," + " ENGINE" + " FROM INFORMATION_SCHEMA.FILES" + " WHERE FILE_TYPE = \"DATAFILE\"" + " ORDER BY TABLESPACE_NAME, LOGFILE_GROUP_NAME")) + return 1; + + buf[0]= 0; + while ((row= mysql_fetch_row(tableres))) + { + if (strcmp(buf, row[0]) != 0) + first= 1; + if (first) + { + if (!opt_xml && opt_comments) + { + fprintf(md_result_file,"\n--\n-- Tablespace: %s\n--\n", row[0]); + check_io(md_result_file); + } + fprintf(md_result_file, "\nCREATE"); + } + else + { + fprintf(md_result_file, "\nALTER"); + } + fprintf(md_result_file, + " TABLESPACE %s\n" + " ADD DATAFILE '%s'\n", + row[0], + row[1]); + if (first) + { + fprintf(md_result_file, + " USE LOGFILE GROUP %s\n" + " EXTENT_SIZE %s\n", + row[2], + row[3]); + } + fprintf(md_result_file, + " INITIAL_SIZE %s\n" + " ENGINE=%s;\n", + row[4], + row[5]); + check_io(md_result_file); + if (first) + { + first= 0; + strxmov(buf, row[0], NullS); + } + } + return 0; +} + static int dump_all_databases() { MYSQL_ROW row; MYSQL_RES *tableres; int result=0; - if (mysql_query_with_error_report(sock, &tableres, "SHOW DATABASES")) + if (mysql_query_with_error_report(mysql, &tableres, "SHOW DATABASES")) return 1; while ((row= mysql_fetch_row(tableres))) { @@ -2583,11 +2866,11 @@ static int dump_all_databases() } if (seen_views) { - if (mysql_query(sock, "SHOW DATABASES") || - !(tableres= mysql_store_result(sock))) + 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(sock)); + MYF(0), mysql_error(mysql)); return 1; } while ((row= mysql_fetch_row(tableres))) @@ -2624,13 +2907,13 @@ static int dump_databases(char **db_names) static int init_dumping(char *database) { - if (mysql_get_server_version(sock) >= 50003 && + if (mysql_get_server_version(mysql) >= 50003 && !my_strcasecmp(&my_charset_latin1, database, "information_schema")) return 1; - if (mysql_select_db(sock, database)) + if (mysql_select_db(mysql, database)) { - DB_error(sock, "when selecting the database"); + DB_error(mysql, "when selecting the database"); return 1; /* If --force */ } if (!path && !opt_xml) @@ -2657,7 +2940,7 @@ static int init_dumping(char *database) "SHOW CREATE DATABASE IF NOT EXISTS %s", qdatabase); - if (mysql_query(sock, qbuf) || !(dbinfo= mysql_store_result(sock))) + if (mysql_query(mysql, qbuf) || !(dbinfo= mysql_store_result(mysql))) { /* Old server version, dump generic CREATE DATABASE */ if (opt_drop_database) @@ -2712,6 +2995,8 @@ static int dump_all_tables_in_db(char *database) afterdot= strmov(hash_key, database); *afterdot++= '.'; + if (!strcmp(database, NDB_REP_DB)) /* Skip cluster internal database */ + return 0; if (init_dumping(database)) return 1; if (opt_xml) @@ -2725,15 +3010,15 @@ static int dump_all_tables_in_db(char *database) dynstr_append(&query, quote_name(table, table_buff, 1)); dynstr_append(&query, " READ /*!32311 LOCAL */,"); } - if (numrows && mysql_real_query(sock, query.str, query.length-1)) - DB_error(sock, "when using LOCK TABLES"); + if (numrows && mysql_real_query(mysql, query.str, query.length-1)) + DB_error(mysql, "when using LOCK TABLES"); /* We shall continue here, if --force was given */ dynstr_free(&query); } if (flush_logs) { - if (mysql_refresh(sock, REFRESH_LOG)) - DB_error(sock, "when doing refresh"); + if (mysql_refresh(mysql, REFRESH_LOG)) + DB_error(mysql, "when doing refresh"); /* We shall continue here, if --force was given */ } while ((table= getTableName(0))) @@ -2745,12 +3030,18 @@ static int dump_all_tables_in_db(char *database) my_free(order_by, MYF(MY_ALLOW_ZERO_PTR)); order_by= 0; if (opt_dump_triggers && ! opt_xml && - mysql_get_server_version(sock) >= 50009) + mysql_get_server_version(mysql) >= 50009) dump_triggers_for_table(table, database); } } + if (opt_events && !opt_xml && + mysql_get_server_version(mysql) >= 50106) + { + DBUG_PRINT("info", ("Dumping events for database %s", database)); + dump_events_for_db(database); + } if (opt_routines && !opt_xml && - mysql_get_server_version(sock) >= 50009) + mysql_get_server_version(mysql) >= 50009) { DBUG_PRINT("info", ("Dumping routines for database %s", database)); dump_routines_for_db(database); @@ -2761,7 +3052,7 @@ static int dump_all_tables_in_db(char *database) check_io(md_result_file); } if (lock_tables) - VOID(mysql_query_with_error_report(sock, 0, "UNLOCK TABLES")); + VOID(mysql_query_with_error_report(mysql, 0, "UNLOCK TABLES")); return 0; } /* dump_all_tables_in_db */ @@ -2784,9 +3075,9 @@ static my_bool dump_all_views_in_db(char *database) uint numrows; char table_buff[NAME_LEN*2+3]; - if (mysql_select_db(sock, database)) + if (mysql_select_db(mysql, database)) { - DB_error(sock, "when selecting the database"); + DB_error(mysql, "when selecting the database"); return 1; } if (opt_databases || opt_alldbs) @@ -2812,15 +3103,15 @@ static my_bool dump_all_views_in_db(char *database) dynstr_append(&query, quote_name(table, table_buff, 1)); dynstr_append(&query, " READ /*!32311 LOCAL */,"); } - if (numrows && mysql_real_query(sock, query.str, query.length-1)) - DB_error(sock, "when using LOCK TABLES"); + if (numrows && mysql_real_query(mysql, query.str, query.length-1)) + DB_error(mysql, "when using LOCK TABLES"); /* We shall continue here, if --force was given */ dynstr_free(&query); } if (flush_logs) { - if (mysql_refresh(sock, REFRESH_LOG)) - DB_error(sock, "when doing refresh"); + if (mysql_refresh(mysql, REFRESH_LOG)) + DB_error(mysql, "when doing refresh"); /* We shall continue here, if --force was given */ } while ((table= getTableName(0))) @@ -2831,7 +3122,7 @@ static my_bool dump_all_views_in_db(char *database) check_io(md_result_file); } if (lock_tables) - VOID(mysql_query_with_error_report(sock, 0, "UNLOCK TABLES")); + VOID(mysql_query_with_error_report(mysql, 0, "UNLOCK TABLES")); return 0; } /* dump_all_tables_in_db */ @@ -2861,12 +3152,12 @@ static char *get_actual_table_name(const char *old_table_name, MEM_ROOT *root) my_snprintf(query, sizeof(query), "SHOW TABLES LIKE %s", quote_for_like(old_table_name, show_name_buff)); - if (mysql_query_with_error_report(sock, 0, query)) + if (mysql_query_with_error_report(mysql, 0, query)) { safe_exit(EX_MYSQLERR); } - if ((table_res= mysql_store_result(sock))) + if ((table_res= mysql_store_result(mysql))) { my_ulonglong num_rows= mysql_num_rows(table_res); if (num_rows > 0) @@ -2896,11 +3187,11 @@ static int dump_selected_tables(char *db, char **table_names, int tables) DBUG_ENTER("dump_selected_tables"); if (init_dumping(db)) - return 1; + DBUG_RETURN(1); init_alloc_root(&root, 8192, 0); if (!(dump_tables= pos= (char**) alloc_root(&root, tables * sizeof(char *)))) - exit(EX_EOM); + exit(EX_EOM); init_dynamic_string(&lock_tables_query, "LOCK TABLES ", 256, 1024); for (; tables > 0 ; tables-- , table_names++) @@ -2911,7 +3202,7 @@ static int dump_selected_tables(char *db, char **table_names, int tables) /* Add found table name to lock_tables_query */ if (lock_tables) { - dynstr_append(&lock_tables_query, quote_name(*pos, table_buff, 1)); + dynstr_append(&lock_tables_query, quote_name(*pos, table_buff, 1)); dynstr_append(&lock_tables_query, " READ /*!32311 LOCAL */,"); } pos++; @@ -2928,16 +3219,16 @@ static int dump_selected_tables(char *db, char **table_names, int tables) if (lock_tables) { - if (mysql_real_query(sock, lock_tables_query.str, + if (mysql_real_query(mysql, lock_tables_query.str, lock_tables_query.length-1)) - DB_error(sock, "when doing LOCK TABLES"); + DB_error(mysql, "when doing LOCK TABLES"); /* We shall countinue here, if --force was given */ } dynstr_free(&lock_tables_query); if (flush_logs) { - if (mysql_refresh(sock, REFRESH_LOG)) - DB_error(sock, "when doing refresh"); + if (mysql_refresh(mysql, REFRESH_LOG)) + DB_error(mysql, "when doing refresh"); /* We shall countinue here, if --force was given */ } if (opt_xml) @@ -2949,7 +3240,7 @@ static int dump_selected_tables(char *db, char **table_names, int tables) DBUG_PRINT("info",("Dumping table %s", *pos)); dump_table(*pos, db); if (opt_dump_triggers && - mysql_get_server_version(sock) >= 50009) + mysql_get_server_version(mysql) >= 50009) dump_triggers_for_table(*pos, db); } @@ -2959,9 +3250,15 @@ static int dump_selected_tables(char *db, char **table_names, int tables) for (pos= dump_tables; pos < end; pos++) get_view_structure(*pos, db); } + if (opt_events && !opt_xml && + mysql_get_server_version(mysql) >= 50106) + { + DBUG_PRINT("info", ("Dumping events for database %s", db)); + dump_events_for_db(db); + } /* obtain dump of routines (procs/functions) */ if (opt_routines && !opt_xml && - mysql_get_server_version(sock) >= 50009) + mysql_get_server_version(mysql) >= 50009) { DBUG_PRINT("info", ("Dumping routines for database %s", db)); dump_routines_for_db(db); @@ -2975,7 +3272,7 @@ static int dump_selected_tables(char *db, char **table_names, int tables) check_io(md_result_file); } if (lock_tables) - VOID(mysql_query_with_error_report(sock, 0, "UNLOCK TABLES")); + VOID(mysql_query_with_error_report(mysql, 0, "UNLOCK TABLES")); DBUG_RETURN(0); } /* dump_selected_tables */ @@ -2988,8 +3285,6 @@ static int do_show_master_status(MYSQL *mysql_con) (opt_master_data == MYSQL_OPT_MASTER_DATA_COMMENTED_SQL) ? "-- " : ""; if (mysql_query_with_error_report(mysql_con, &master, "SHOW MASTER STATUS")) { - my_printf_error(0, "Error: Couldn't execute 'SHOW MASTER STATUS': %s", - MYF(0), mysql_error(mysql_con)); return 1; } else @@ -3160,7 +3455,7 @@ static void print_value(FILE *file, MYSQL_RES *result, MYSQL_ROW row, table_type Type of table GLOBAL VARIABLES - sock MySQL socket + mysql MySQL connection verbose Write warning messages RETURN @@ -3179,14 +3474,12 @@ char check_if_ignore_table(const char *table_name, char *table_type) DBUG_ASSERT(2*sizeof(table_name) < sizeof(show_name_buff)); my_snprintf(buff, sizeof(buff), "show table status like %s", quote_for_like(table_name, show_name_buff)); - if (mysql_query_with_error_report(sock, &res, buff)) + if (mysql_query_with_error_report(mysql, &res, buff)) { - if (mysql_errno(sock) != ER_PARSE_ERROR) + if (mysql_errno(mysql) != ER_PARSE_ERROR) { /* If old MySQL version */ - if (verbose) - fprintf(stderr, - "-- Warning: Couldn't get status information for table %s (%s)\n", - table_name,mysql_error(sock)); + verbose_msg("-- Warning: Couldn't get status information for " \ + "table %s (%s)\n", table_name,mysql_error(mysql)); DBUG_RETURN(result); /* assume table is ok */ } } @@ -3194,7 +3487,7 @@ char check_if_ignore_table(const char *table_name, char *table_type) { fprintf(stderr, "Error: Couldn't read status information for table %s (%s)\n", - table_name, mysql_error(sock)); + table_name, mysql_error(mysql)); mysql_free_result(res); DBUG_RETURN(result); /* assume table is ok */ } @@ -3222,7 +3515,7 @@ char check_if_ignore_table(const char *table_name, char *table_type) /* If these two types, we do want to skip dumping the table */ - if (!dFlag && + if (!opt_no_data && (!strcmp(table_type,"MRG_MyISAM") || !strcmp(table_type,"MRG_ISAM"))) result= IGNORE_DATA; } @@ -3230,6 +3523,7 @@ char check_if_ignore_table(const char *table_name, char *table_type) DBUG_RETURN(result); } + /* Get string of comma-separated primary key field names @@ -3261,12 +3555,12 @@ static char *primary_key_fields(const char *table_name) my_snprintf(show_keys_buff, sizeof(show_keys_buff), "SHOW KEYS FROM %s", table_name); - if (mysql_query(sock, show_keys_buff) || - !(res= mysql_store_result(sock))) + if (mysql_query(mysql, show_keys_buff) || + !(res= mysql_store_result(mysql))) { fprintf(stderr, "Warning: Couldn't read keys from table %s;" " records are NOT sorted (%s)\n", - table_name, mysql_error(sock)); + table_name, mysql_error(mysql)); /* Don't exit, because it's better to print out unsorted records */ goto cleanup; } @@ -3377,11 +3671,10 @@ static my_bool get_view_structure(char *table, char* db) FILE *sql_file= md_result_file; DBUG_ENTER("get_view_structure"); - if (tFlag) /* Don't write table creation info */ + if (opt_no_create_info) /* Don't write table creation info */ DBUG_RETURN(0); - if (verbose) - fprintf(stderr, "-- Retrieving view structure for table %s...\n", table); + verbose_msg("-- Retrieving view structure for table %s...\n", table); #ifdef NOT_REALLY_USED_YET sprintf(insert_pat,"SET OPTION SQL_QUOTE_SHOW_CREATE=%d", @@ -3392,7 +3685,7 @@ static my_bool get_view_structure(char *table, char* db) opt_quoted_table= quote_name(table, table_buff2, 0); my_snprintf(query, sizeof(query), "SHOW CREATE TABLE %s", result_table); - if (mysql_query_with_error_report(sock, &table_res, query)) + if (mysql_query_with_error_report(mysql, &table_res, query)) { safe_exit(EX_MYSQLERR); DBUG_RETURN(0); @@ -3402,8 +3695,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) { - if (verbose) - fprintf(stderr, "-- It's base table, skipped\n"); + verbose_msg("-- It's base table, skipped\n"); DBUG_RETURN(0); } @@ -3438,7 +3730,7 @@ static my_bool get_view_structure(char *table, char* db) "SELECT CHECK_OPTION, DEFINER, SECURITY_TYPE " \ "FROM information_schema.views " \ "WHERE table_name=\"%s\" AND table_schema=\"%s\"", table, db); - if (mysql_query(sock, query)) + if (mysql_query(mysql, query)) { /* Use the raw output from SHOW CREATE TABLE if @@ -3464,7 +3756,7 @@ static my_bool get_view_structure(char *table, char* db) mysql_free_result(table_res); /* Get the result from "select ... information_schema" */ - if (!(table_res= mysql_store_result(sock)) || + if (!(table_res= mysql_store_result(mysql)) || !(row= mysql_fetch_row(table_res))) { safe_exit(EX_MYSQLERR); @@ -3559,23 +3851,26 @@ int main(int argc, char **argv) write_header(md_result_file, *argv); if ((opt_lock_all_tables || opt_master_data) && - do_flush_tables_read_lock(sock)) + do_flush_tables_read_lock(mysql)) goto err; - if (opt_single_transaction && start_transaction(sock, test(opt_master_data))) + if (opt_single_transaction && start_transaction(mysql, test(opt_master_data))) goto err; - if (opt_delete_master_logs && do_reset_master(sock)) + if (opt_delete_master_logs && do_reset_master(mysql)) goto err; if (opt_lock_all_tables || opt_master_data) { - if (flush_logs && mysql_refresh(sock, REFRESH_LOG)) + if (flush_logs && mysql_refresh(mysql, REFRESH_LOG)) goto err; flush_logs= 0; /* not anymore; that would not be sensible */ } - if (opt_master_data && do_show_master_status(sock)) + if (opt_master_data && do_show_master_status(mysql)) goto err; - if (opt_single_transaction && do_unlock_tables(sock)) /* unlock but no commit! */ + if (opt_single_transaction && do_unlock_tables(mysql)) /* unlock but no commit! */ goto err; + if (opt_alltspcs) + dump_all_tablespaces(); + if (opt_alldbs) dump_all_databases(); else if (argc > 1 && !opt_databases) diff --git a/client/mysqlimport.c b/client/mysqlimport.c index 1f9b96f91be..2ef08c9a504 100644 --- a/client/mysqlimport.c +++ b/client/mysqlimport.c @@ -29,6 +29,17 @@ #include "client_priv.h" #include "mysql_version.h" +#ifdef HAVE_LIBPTHREAD +#include <my_pthread.h> +#endif + + +/* Global Thread counter */ +int counter; +#ifdef HAVE_LIBPTHREAD +pthread_mutex_t counter_mutex; +pthread_cond_t count_threshhold; +#endif static void db_error_with_table(MYSQL *mysql, char *table); static void db_error(MYSQL *mysql); @@ -39,8 +50,8 @@ static char *add_load_option(char *ptr,const char *object, static my_bool verbose=0,lock_tables=0,ignore_errors=0,opt_delete=0, replace=0,silent=0,ignore=0,opt_compress=0, opt_low_priority= 0, tty_password= 0; +static my_bool opt_use_threads= 0; static uint opt_local_file=0; -static MYSQL mysql_connection; static char *opt_password=0, *current_user=0, *current_host=0, *current_db=0, *fields_terminated=0, *lines_terminated=0, *enclosed=0, *opt_enclosed=0, @@ -108,8 +119,9 @@ static struct my_option my_long_options[] = REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"local", 'L', "Read all files through the client.", (gptr*) &opt_local_file, (gptr*) &opt_local_file, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"lock-tables", 'l', "Lock all tables for write.", (gptr*) &lock_tables, - (gptr*) &lock_tables, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"lock-tables", 'l', "Lock all tables for write (this disables threads).", + (gptr*) &lock_tables, (gptr*) &lock_tables, 0, GET_BOOL, NO_ARG, + 0, 0, 0, 0, 0, 0}, {"low-priority", OPT_LOW_PRIORITY, "Use LOW_PRIORITY when updating the table.", (gptr*) &opt_low_priority, (gptr*) &opt_low_priority, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, @@ -138,6 +150,11 @@ static struct my_option my_long_options[] = (gptr*) &opt_mysql_unix_port, (gptr*) &opt_mysql_unix_port, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, #include <sslopt-longopts.h> + {"use-threads", OPT_USE_THREADS, + "Load files in parallel. The argument is the number " + "of threads to use for loading data.", + (gptr*) &opt_use_threads, (gptr*) &opt_use_threads, 0, + GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, #ifndef DONT_ALLOW_USER_CHANGE {"user", 'u', "User for login if not current user.", (gptr*) ¤t_user, (gptr*) ¤t_user, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, @@ -270,7 +287,7 @@ static int get_options(int *argc, char ***argv) -static int write_to_table(char *filename, MYSQL *sock) +static int write_to_table(char *filename, MYSQL *mysql) { char tablename[FN_REFLEN], hard_path[FN_REFLEN], sql_statement[FN_REFLEN*16+256], *end; @@ -278,7 +295,7 @@ static int write_to_table(char *filename, MYSQL *sock) DBUG_PRINT("enter",("filename: %s",filename)); fn_format(tablename, filename, "", "", 1 | 2); /* removes path & ext. */ - if (! opt_local_file) + if (!opt_local_file) strmov(hard_path,filename); else my_load_path(hard_path, filename, NULL); /* filename includes the path */ @@ -287,10 +304,14 @@ static int write_to_table(char *filename, MYSQL *sock) { if (verbose) fprintf(stdout, "Deleting the old data from table %s\n", tablename); +#ifdef HAVE_SNPRINTF + snprintf(sql_statement, FN_REFLEN*16+256, "DELETE FROM %s", tablename); +#else sprintf(sql_statement, "DELETE FROM %s", tablename); - if (mysql_query(sock, sql_statement)) +#endif + if (mysql_query(mysql, sql_statement)) { - db_error_with_table(sock, tablename); + db_error_with_table(mysql, tablename); DBUG_RETURN(1); } } @@ -329,17 +350,17 @@ static int write_to_table(char *filename, MYSQL *sock) end= strmov(strmov(strmov(end, " ("), opt_columns), ")"); *end= '\0'; - if (mysql_query(sock, sql_statement)) + if (mysql_query(mysql, sql_statement)) { - db_error_with_table(sock, tablename); + db_error_with_table(mysql, tablename); DBUG_RETURN(1); } if (!silent) { - if (mysql_info(sock)) /* If NULL-pointer, print nothing */ + if (mysql_info(mysql)) /* If NULL-pointer, print nothing */ { fprintf(stdout, "%s.%s: %s\n", current_db, tablename, - mysql_info(sock)); + mysql_info(mysql)); } } DBUG_RETURN(0); @@ -347,7 +368,7 @@ static int write_to_table(char *filename, MYSQL *sock) -static void lock_table(MYSQL *sock, int tablecount, char **raw_tablename) +static void lock_table(MYSQL *mysql, int tablecount, char **raw_tablename) { DYNAMIC_STRING query; int i; @@ -362,72 +383,74 @@ static void lock_table(MYSQL *sock, int tablecount, char **raw_tablename) dynstr_append(&query, tablename); dynstr_append(&query, " WRITE,"); } - if (mysql_real_query(sock, query.str, query.length-1)) - db_error(sock); /* We shall countinue here, if --force was given */ + if (mysql_real_query(mysql, query.str, query.length-1)) + db_error(mysql); /* We shall countinue here, if --force was given */ } -static MYSQL *db_connect(char *host, char *database, char *user, char *passwd) +static MYSQL *db_connect(char *host, char *database, + char *user, char *passwd) { - MYSQL *sock; + MYSQL *mysql; if (verbose) fprintf(stdout, "Connecting to %s\n", host ? host : "localhost"); - mysql_init(&mysql_connection); + if (!(mysql= mysql_init(NULL))) + return 0; if (opt_compress) - mysql_options(&mysql_connection,MYSQL_OPT_COMPRESS,NullS); + mysql_options(mysql,MYSQL_OPT_COMPRESS,NullS); if (opt_local_file) - mysql_options(&mysql_connection,MYSQL_OPT_LOCAL_INFILE, + mysql_options(mysql,MYSQL_OPT_LOCAL_INFILE, (char*) &opt_local_file); #ifdef HAVE_OPENSSL if (opt_use_ssl) - mysql_ssl_set(&mysql_connection, opt_ssl_key, opt_ssl_cert, opt_ssl_ca, + mysql_ssl_set(mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca, opt_ssl_capath, opt_ssl_cipher); - mysql_options(&mysql_connection,MYSQL_OPT_SSL_VERIFY_SERVER_CERT, + mysql_options(mysql,MYSQL_OPT_SSL_VERIFY_SERVER_CERT, (char*)&opt_ssl_verify_server_cert); #endif if (opt_protocol) - mysql_options(&mysql_connection,MYSQL_OPT_PROTOCOL,(char*)&opt_protocol); + mysql_options(mysql,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); + mysql_options(mysql,MYSQL_SHARED_MEMORY_BASE_NAME,shared_memory_base_name); #endif - if (!(sock= mysql_real_connect(&mysql_connection,host,user,passwd, - database,opt_mysql_port,opt_mysql_unix_port, - 0))) + if (!(mysql_real_connect(mysql,host,user,passwd, + database,opt_mysql_port,opt_mysql_unix_port, + 0))) { ignore_errors=0; /* NO RETURN FROM db_error */ - db_error(&mysql_connection); + db_error(mysql); } - mysql_connection.reconnect= 0; + mysql->reconnect= 0; if (verbose) fprintf(stdout, "Selecting database %s\n", database); - if (mysql_select_db(sock, database)) + if (mysql_select_db(mysql, database)) { ignore_errors=0; - db_error(&mysql_connection); + db_error(mysql); } - return sock; + return mysql; } -static void db_disconnect(char *host, MYSQL *sock) +static void db_disconnect(char *host, MYSQL *mysql) { if (verbose) fprintf(stdout, "Disconnecting from %s\n", host ? host : "localhost"); - mysql_close(sock); + mysql_close(mysql); } -static void safe_exit(int error, MYSQL *sock) +static void safe_exit(int error, MYSQL *mysql) { if (ignore_errors) return; - if (sock) - mysql_close(sock); + if (mysql) + mysql_close(mysql); exit(error); } @@ -435,8 +458,8 @@ static void safe_exit(int error, MYSQL *sock) static void db_error_with_table(MYSQL *mysql, char *table) { - my_printf_error(0,"Error: %s, when using table: %s", - MYF(0), mysql_error(mysql), table); + my_printf_error(0,"Error: %d, %s, when using table: %s", + MYF(0), mysql_errno(mysql), mysql_error(mysql), table); safe_exit(1, mysql); } @@ -444,7 +467,7 @@ static void db_error_with_table(MYSQL *mysql, char *table) static void db_error(MYSQL *mysql) { - my_printf_error(0,"Error: %s", MYF(0), mysql_error(mysql)); + my_printf_error(0,"Error: %d %s", MYF(0), mysql_errno(mysql), mysql_error(mysql)); safe_exit(1, mysql); } @@ -498,13 +521,55 @@ static char *field_escape(char *to,const char *from,uint length) return to; } +int exitcode= 0; + +#ifdef HAVE_LIBPTHREAD +pthread_handler_t worker_thread(void *arg) +{ + int error; + char *raw_table_name= (char *)arg; + MYSQL *mysql= 0; + + if (mysql_thread_init()) + goto error; + + if (!(mysql= db_connect(current_host,current_db,current_user,opt_password))) + { + goto error; + } + + if (mysql_query(mysql, "/*!40101 set @@character_set_database=binary */;")) + { + db_error(mysql); /* We shall countinue here, if --force was given */ + goto error; + } + + /* + We are not currently catching the error here. + */ + if((error= write_to_table(raw_table_name, mysql))) + if (exitcode == 0) + exitcode= error; + +error: + if (mysql) + db_disconnect(current_host, mysql); + + pthread_mutex_lock(&counter_mutex); + counter--; + pthread_cond_signal(&count_threshhold); + pthread_mutex_unlock(&counter_mutex); + my_thread_end(); + + return 0; +} +#endif int main(int argc, char **argv) { - int exitcode=0, error=0; + int error=0; char **argv_to_free; - MYSQL *sock=0; MY_INIT(argv[0]); load_defaults("my",load_default_groups,&argc,&argv); @@ -515,25 +580,84 @@ int main(int argc, char **argv) free_defaults(argv_to_free); return(1); } - if (!(sock= db_connect(current_host,current_db,current_user,opt_password))) - { - free_defaults(argv_to_free); - return(1); /* purecov: deadcode */ - } - if (mysql_query(sock, "set @@character_set_database=binary;")) +#ifdef HAVE_LIBPTHREAD + if (opt_use_threads && !lock_tables) { - db_error(sock); /* We shall countinue here, if --force was given */ - return(1); + pthread_t mainthread; /* Thread descriptor */ + pthread_attr_t attr; /* Thread attributes */ + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, + PTHREAD_CREATE_DETACHED); + + VOID(pthread_mutex_init(&counter_mutex, NULL)); + VOID(pthread_cond_init(&count_threshhold, NULL)); + + for (counter= 0; *argv != NULL; argv++) /* Loop through tables */ + { + pthread_mutex_lock(&counter_mutex); + while (counter == opt_use_threads) + { + struct timespec abstime; + + set_timespec(abstime, 3); + pthread_cond_timedwait(&count_threshhold, &counter_mutex, &abstime); + } + /* Before exiting the lock we set ourselves up for the next thread */ + counter++; + pthread_mutex_unlock(&counter_mutex); + /* now create the thread */ + if (pthread_create(&mainthread, &attr, worker_thread, + (void *)*argv) != 0) + { + pthread_mutex_lock(&counter_mutex); + counter--; + pthread_mutex_unlock(&counter_mutex); + fprintf(stderr,"%s: Could not create thread\n", + my_progname); + } + } + + /* + We loop until we know that all children have cleaned up. + */ + pthread_mutex_lock(&counter_mutex); + while (counter) + { + struct timespec abstime; + + set_timespec(abstime, 3); + pthread_cond_timedwait(&count_threshhold, &counter_mutex, &abstime); + } + pthread_mutex_unlock(&counter_mutex); + VOID(pthread_mutex_destroy(&counter_mutex)); + VOID(pthread_cond_destroy(&count_threshhold)); + pthread_attr_destroy(&attr); } + else +#endif + { + MYSQL *mysql= 0; + if (!(mysql= db_connect(current_host,current_db,current_user,opt_password))) + { + free_defaults(argv_to_free); + return(1); /* purecov: deadcode */ + } - if (lock_tables) - lock_table(sock, argc, argv); - for (; *argv != NULL; argv++) - if ((error=write_to_table(*argv, sock))) - if (exitcode == 0) - exitcode = error; - db_disconnect(current_host, sock); + if (mysql_query(mysql, "/*!40101 set @@character_set_database=binary */;")) + { + db_error(mysql); /* We shall countinue here, if --force was given */ + return(1); + } + + if (lock_tables) + lock_table(mysql, argc, argv); + for (; *argv != NULL; argv++) + if ((error= write_to_table(*argv, mysql))) + if (exitcode == 0) + exitcode= error; + db_disconnect(current_host, mysql); + } my_free(opt_password,MYF(MY_ALLOW_ZERO_PTR)); #ifdef HAVE_SMEM my_free(shared_memory_base_name,MYF(MY_ALLOW_ZERO_PTR)); diff --git a/client/mysqlmanager-pwgen.c b/client/mysqlmanager-pwgen.c deleted file mode 100644 index 1d942e207ad..00000000000 --- a/client/mysqlmanager-pwgen.c +++ /dev/null @@ -1,161 +0,0 @@ -/* Copyright (C) 2000 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#define MANAGER_PWGEN_VERSION "1.4" - -#include <my_global.h> -#include <m_ctype.h> -#include <my_sys.h> -#include <m_string.h> -#include <mysql_version.h> -#include <errno.h> -#include <my_getopt.h> -#include <md5.h> - -const char* outfile=0,*user="root"; - -static struct my_option my_long_options[] = -{ - {"output-file", 'o', "Write the output to the file with the given name.", - (gptr*) &outfile, (gptr*) &outfile, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, - 0, 0}, - {"user", 'u', "Put given user in the password file.", (gptr*) &user, - (gptr*) &user, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"help", '?', "Display this message and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, - 0, 0, 0, 0, 0, 0}, - {"version", 'V', "Display version info.", 0, 0, 0, GET_NO_ARG, 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} -}; - -static void die(const char* fmt, ...) -{ - va_list args; - DBUG_ENTER("die"); - va_start(args, fmt); - if (fmt) - { - fprintf(stderr, "%s: ", my_progname); - vfprintf(stderr, fmt, args); - fprintf(stderr, "\n"); - fflush(stderr); - } - va_end(args); - exit(1); -} - -static void print_version(void) -{ - printf("%s Ver %s Distrib %s, for %s (%s)\n",my_progname, - MANAGER_PWGEN_VERSION, - MYSQL_SERVER_VERSION,SYSTEM_TYPE,MACHINE_TYPE); -} - -void usage() -{ - print_version(); - printf("MySQL AB, by Sasha\n"); - printf("This software comes with ABSOLUTELY NO WARRANTY\n\n"); - printf("Generates a password file to be used by mysqltest.\n\n"); - printf("Usage: %s [OPTIONS]\n", my_progname); - my_print_help(my_long_options); - my_print_variables(my_long_options); -} - -static my_bool -get_one_option(int optid, const struct my_option *opt __attribute__((unused)), - char *argument __attribute__((unused))) -{ - switch (optid) { - case '?': - usage(); - exit(0); - case 'V': - print_version(); - exit(0); - } - return 0; -} - - -int parse_args(int argc, char** argv) -{ - int ho_error; - - if ((ho_error=handle_options(&argc, &argv, my_long_options, get_one_option))) - exit(ho_error); - - return 0; -} - -void get_pass(char* pw, int len) -{ - FILE* fp; - char* pw_end=pw+len; - /* - /dev/random is more secure than rand() because the seed is easy to - predict, so we resort to rand() only if /dev/random is not available - */ - if ((fp=fopen("/dev/random","r"))) - { - fread(pw,len,1,fp); - fclose(fp); - while (pw<pw_end) - { - char tmp= 'a'+((uint)*pw % 26); - *pw++= tmp; - } - } - else - { - srand(time(NULL)); - while (pw<pw_end) - { - char tmp= 'a'+((uint)*pw % 26); - *pw++= tmp; - } - } - *pw_end=0; -} - - -int main(int argc, char** argv) -{ - FILE* fp; - my_MD5_CTX context; - uchar digest[16]; - char pw[17]; - uint i; - - MY_INIT(argv[0]); - parse_args(argc,argv); - if (!outfile) - die("Missing --output-file"); - - if (!(fp=fopen(outfile,"w"))) - die("Could not open '%s'(errno=%d)",outfile,errno); - get_pass(pw,sizeof(pw)-1); - my_MD5Init(&context); - my_MD5Update(&context,(uchar*) pw,sizeof(pw)-1); - my_MD5Final(digest,&context); - fprintf(fp,"%s:",user); - for (i=0;i<sizeof(digest);i++) - fprintf(fp,"%02x",digest[i]); - fprintf(fp,"\n"); - fclose(fp); - printf("%s\n",pw); - return 0; -} diff --git a/client/mysqlmanagerc.c b/client/mysqlmanagerc.c deleted file mode 100644 index 0001a0266e6..00000000000 --- a/client/mysqlmanagerc.c +++ /dev/null @@ -1,174 +0,0 @@ -/* Copyright (C) 2000 MySQL 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#define MANAGER_CLIENT_VERSION "1.4" - -#include <my_global.h> -#include <mysql.h> -#include <mysql_version.h> -#include <mysqld_error.h> -#include <my_sys.h> -#include <m_string.h> -#include <my_getopt.h> -#include <stdarg.h> -#include <sys/stat.h> -#include <unistd.h> - -#ifndef MYSQL_MANAGER_PORT -#define MYSQL_MANAGER_PORT 9305 -#endif - -static void die(const char* fmt, ...); - -const char* user="root",*host="localhost"; -char* pass=0; -my_bool quiet=0; -uint port=MYSQL_MANAGER_PORT; -static const char *load_default_groups[]= { "mysqlmanagerc",0 }; -char** default_argv; -MYSQL_MANAGER *manager; -FILE* fp, *fp_out; - -static struct my_option my_long_options[] = -{ - {"host", 'h', "Connect to host.", (gptr*) &host, (gptr*) &host, 0, - GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"user", 'u', "User for login.", (gptr*) &user, (gptr*) &user, 0, - GET_STR, REQUIRED_ARG, 0, 0, 0, 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}, - {"port", 'P', "Port number to use for connection.", (gptr*) &port, - (gptr*) &port, 0, GET_UINT, REQUIRED_ARG, MYSQL_MANAGER_PORT, 0, 0, 0, 0, - 0}, - {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, - 0, 0, 0, 0, 0, 0}, - {"version", 'V', "Output version information and exit.", 0, 0, 0, - GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"quiet", 'q', "Suppress all normal output.", (gptr*) &quiet, (gptr*) &quiet, - 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} -}; - -static void die(const char* fmt, ...) -{ - va_list args; - DBUG_ENTER("die"); - va_start(args, fmt); - if (fmt) - { - fprintf(stderr, "%s: ", my_progname); - vfprintf(stderr, fmt, args); - fprintf(stderr, "\n"); - fflush(stderr); - } - va_end(args); - exit(1); -} - -static void print_version(void) -{ - printf("%s Ver %s Distrib %s, for %s (%s)\n",my_progname, - MANAGER_CLIENT_VERSION, - MYSQL_SERVER_VERSION,SYSTEM_TYPE,MACHINE_TYPE); -} - -void usage() -{ - print_version(); - printf("MySQL AB, by Sasha\n"); - printf("This software comes with ABSOLUTELY NO WARRANTY\n\n"); - printf("Command-line client for MySQL manager daemon.\n\n"); - printf("Usage: %s [OPTIONS] < command_file\n", my_progname); - my_print_help(my_long_options); - printf(" --no-defaults Don't read default options from any options file.\n"); - my_print_variables(my_long_options); -} - - -static my_bool -get_one_option(int optid, const struct my_option *opt __attribute__((unused)), - char *argument) -{ - my_bool tty_password=0; - - switch (optid) { - case 'p': - if (argument) - { - my_free(pass, MYF(MY_ALLOW_ZERO_PTR)); - pass= my_strdup(argument, MYF(MY_FAE)); - while (*argument) *argument++= 'x'; /* Destroy argument */ - } - else - tty_password=1; - break; - case 'V': - print_version(); - exit(0); - case '?': - usage(); - exit(0); - } - return 0; -} - - -int parse_args(int argc, char **argv) -{ - int ho_error; - - load_defaults("my",load_default_groups,&argc,&argv); - default_argv= argv; - - if ((ho_error=handle_options(&argc, &argv, my_long_options, get_one_option))) - exit(ho_error); - - return 0; -} - - -int main(int argc, char** argv) -{ - MY_INIT(argv[0]); - fp=stdin; - fp_out=stdout; - parse_args(argc,argv); - if (!(manager=mysql_manager_init(0))) - die("Failed in mysql_manager_init()"); - if (!mysql_manager_connect(manager,host,user,pass,port)) - die("Could not connect to MySQL manager: %s (%d)",manager->last_error, - manager->last_errno); - for (;!feof(fp);) - { - char buf[4096]; - if (!fgets(buf,sizeof(buf),fp)) - break; - if (!quiet) - fprintf(fp_out,"<<%s",buf); - if (mysql_manager_command(manager,buf,strlen(buf))) - die("Error in command: %s (%d)",manager->last_error,manager->last_errno); - while (!manager->eof) - { - if (mysql_manager_fetch_line(manager,buf,sizeof(buf))) - die("Error fetching result line: %s (%d)", manager->last_error, - manager->last_errno); - if (!quiet) - fprintf(fp_out,">>%s\n",buf); - } - } - mysql_manager_close(manager); - return 0; -} diff --git a/client/mysqlshow.c b/client/mysqlshow.c index d090495ff81..40405c53565 100644 --- a/client/mysqlshow.c +++ b/client/mysqlshow.c @@ -344,7 +344,7 @@ list_dbs(MYSQL *mysql,const char *wild) char query[255]; MYSQL_FIELD *field; MYSQL_RES *result; - MYSQL_ROW row, rrow; + MYSQL_ROW row= NULL, rrow; if (!(result=mysql_list_dbs(mysql,wild))) { @@ -352,6 +352,26 @@ list_dbs(MYSQL *mysql,const char *wild) mysql_error(mysql)); return 1; } + + /* + If a wildcard was used, but there was only one row and it's name is an + exact match, we'll assume they really wanted to see the contents of that + database. This is because it is fairly common for database names to + contain the underscore (_), like INFORMATION_SCHEMA. + */ + if (wild && mysql_num_rows(result) == 1) + { + row= mysql_fetch_row(result); + if (!my_strcasecmp(&my_charset_latin1, row[0], wild)) + { + mysql_free_result(result); + if (opt_status) + return list_table_status(mysql, wild, NULL); + else + return list_tables(mysql, wild, NULL); + } + } + if (wild) printf("Wildcard: %s\n",wild); @@ -368,7 +388,8 @@ list_dbs(MYSQL *mysql,const char *wild) else print_header(header,length,"Tables",6,"Total Rows",12,NullS); - while ((row = mysql_fetch_row(result))) + /* The first row may have already been read up above. */ + while (row || (row= mysql_fetch_row(result))) { counter++; @@ -422,6 +443,8 @@ list_dbs(MYSQL *mysql,const char *wild) print_row(row[0],length,tables,6,NullS); else print_row(row[0],length,tables,6,rows,12,NullS); + + row= NULL; } print_trailer(length, diff --git a/client/mysqlslap.c b/client/mysqlslap.c new file mode 100644 index 00000000000..9c8585915a9 --- /dev/null +++ b/client/mysqlslap.c @@ -0,0 +1,1358 @@ +/* Copyright (C) 2005 MySQL 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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + original idea: Brian Aker via playing with ab for too many years + coded by: Patrick Galbraith +*/ + + +/* + MySQL Slap + + A simple program designed to work as if multiple clients querying the database, + then reporting the timing of each stage. + + MySQL slap runs three stages: + 1) Create schema,table, and optionally any SP or data you want to beign + the test with. (single client) + 2) Load test (many clients) + 3) Cleanup (disconnection, drop table if specified, single client) + + Examples: + + Supply your own create and query SQL statements, with 50 clients + querying (200 selects for each): + + mysqlslap --create="CREATE TABLE A (a int);INSERT INTO A (23)" \ + --query="SELECT * FROM A" --concurrency=50 --iterations=200 + + Let the program build the query SQL statement with a table of two int + columns, three varchar columns, five clients querying (20 times each), + don't create the table or insert the data (using the previous test's + schema and data): + + mysqlslap --concurrency=5 --iterations=20 \ + --number-int-cols=2 --number-char-cols=3 \ + --auto-generate-sql + + Tell the program to load the create, insert and query SQL statements from + the specified files, where the create.sql file has multiple table creation + statements delimited by ';' and multiple insert statements delimited by ';'. + The --query file will have multiple queries delimited by ';', run all the + load statements, and then run all the queries in the query file + with five clients (five times each): + + mysqlslap --concurrency=5 \ + --iterations=5 --query=query.sql --create=create.sql \ + --delimiter=";" + +TODO: + Add language for better tests + String length for files and those put on the command line are not + setup to handle binary data. + Report results of each thread into the lock file we use. + More stats + Break up tests and run them on multiple hosts at once. + Allow output to be fed into a database directly. + +*/ + +#define SHOW_VERSION "0.9" + +#define HUGE_STRING_LENGTH 8096 +#define RAND_STRING_SIZE 126 + +#include "client_priv.h" +#ifdef HAVE_LIBPTHREAD +#include <my_pthread.h> +#endif +#include <my_sys.h> +#include <m_string.h> +#include <mysql.h> +#include <mysqld_error.h> +#include <my_dir.h> +#include <signal.h> +#include <stdarg.h> +#include <sslopt-vars.h> +#include <sys/types.h> +#ifndef __WIN__ +#include <sys/wait.h> +#endif +#include <ctype.h> + +#define MYSLAPLOCK "/myslaplock.lck" +#define MYSLAPLOCK_DIR "/tmp" + +#ifdef __WIN__ +#define srandom srand +#define random rand +#define snprintf _snprintf +#endif + +#ifdef HAVE_SMEM +static char *shared_memory_base_name=0; +#endif + +static char **defaults_argv; + +static char *host= NULL, *opt_password= NULL, *user= NULL, + *user_supplied_query= NULL, + *default_engine= NULL, + *opt_mysql_unix_port= NULL; + +const char *delimiter= "\n"; + +const char *create_schema_string= "mysqlslap"; + +const char *lock_directory; +char lock_file_str[FN_REFLEN]; + +static my_bool opt_preserve; + +static my_bool opt_only_print= FALSE; + +static my_bool opt_slave; + +static my_bool opt_compress= FALSE, tty_password= FALSE, + opt_silent= FALSE, + auto_generate_sql= FALSE; + +static unsigned long connect_flags= CLIENT_MULTI_RESULTS; + +static int verbose, num_int_cols, num_char_cols, delimiter_length; +static int iterations; +static char *default_charset= (char*) MYSQL_DEFAULT_CHARSET_NAME; +static ulonglong actual_queries= 0; +static ulonglong num_of_query; +const char *concurrency_str= NULL; +static char *create_string; +uint *concurrency; + +const char *default_dbug_option="d:t:o,/tmp/mysqlslap.trace"; +const char *opt_csv_str; +File csv_file; + +static uint opt_protocol= 0; + +static int get_options(int *argc,char ***argv); +static uint opt_mysql_port= 0; +static uint opt_use_threads; + +static const char *load_default_groups[]= { "mysqlslap","client",0 }; + +typedef struct statement statement; + +struct statement { + char *string; + size_t length; + statement *next; +}; + +typedef struct stats stats; + +struct stats { + long int timing; + uint users; + unsigned long long rows; +}; + +typedef struct thread_context thread_context; + +struct thread_context { + statement *stmt; + ulonglong limit; + bool thread; +}; + +typedef struct conclusions conclusions; + +struct conclusions { + char *engine; + long int avg_timing; + long int max_timing; + long int min_timing; + uint users; + unsigned long long avg_rows; + /* The following are not used yet */ + unsigned long long max_rows; + unsigned long long min_rows; +}; + +static statement *create_statements= NULL, + *engine_statements= NULL, + *query_statements= NULL; + +/* Prototypes */ +void print_conclusions(conclusions *con); +void print_conclusions_csv(conclusions *con); +void generate_stats(conclusions *con, statement *eng, stats *sptr); +uint parse_comma(const char *string, uint **range); +uint parse_delimiter(const char *script, statement **stmt, char delm); +static int drop_schema(MYSQL *mysql, const char *db); +uint get_random_string(char *buf); +static statement *build_table_string(void); +static statement *build_insert_string(void); +static statement *build_query_string(void); +static int create_schema(MYSQL *mysql, const char *db, statement *stmt, + statement *engine_stmt); +static int run_scheduler(stats *sptr, statement *stmts, uint concur, + ulonglong limit); +int run_task(thread_context *con); +void statement_cleanup(statement *stmt); + +static const char ALPHANUMERICS[]= + "0123456789ABCDEFGHIJKLMNOPQRSTWXYZabcdefghijklmnopqrstuvwxyz"; + +#define ALPHANUMERICS_SIZE (sizeof(ALPHANUMERICS)-1) + + +static long int timedif(struct timeval a, struct timeval b) +{ + register int us, s; + + us = a.tv_usec - b.tv_usec; + us /= 1000; + s = a.tv_sec - b.tv_sec; + s *= 1000; + return s + us; +} + +#ifdef __WIN__ +static int gettimeofday(struct timeval *tp, void *tzp) +{ + unsigned int ticks; + ticks= GetTickCount(); + tp->tv_usec= ticks*1000; + tp->tv_sec= ticks/1000; + + return 0; +} +#endif + +int main(int argc, char **argv) +{ + MYSQL mysql; + int x; + unsigned long long client_limit; + statement *eptr; + +#ifdef __WIN__ + opt_use_threads= 1; +#endif + + MY_INIT(argv[0]); + + load_defaults("my",load_default_groups,&argc,&argv); + defaults_argv=argv; + if (get_options(&argc,&argv)) + { + free_defaults(defaults_argv); + my_end(0); + exit(1); + } + + /* Seed the random number generator if we will be using it. */ + if (auto_generate_sql) + srandom((uint)time(NULL)); + + /* globals? Yes, so we only have to run strlen once */ + delimiter_length= strlen(delimiter); + + if (argc > 2) + { + fprintf(stderr,"%s: Too many arguments\n",my_progname); + free_defaults(defaults_argv); + my_end(0); + 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); + + if (!opt_only_print) + { + if (!(mysql_real_connect(&mysql, host, user, opt_password, + NULL, opt_mysql_port, + opt_mysql_unix_port, connect_flags))) + { + fprintf(stderr,"%s: Error when connecting to server: %s\n", + my_progname,mysql_error(&mysql)); + free_defaults(defaults_argv); + my_end(0); + exit(1); + } + } + + /* Main iterations loop */ + eptr= engine_statements; + do + { + /* For the final stage we run whatever queries we were asked to run */ + uint *current; + conclusions conclusion; + + for (current= concurrency; current && *current; current++) + { + stats *head_sptr; + stats *sptr; + + head_sptr= (stats *)my_malloc(sizeof(stats) * iterations, MYF(MY_ZEROFILL)); + + bzero(&conclusion, sizeof(conclusions)); + + if (num_of_query) + client_limit= num_of_query / *current; + else + client_limit= actual_queries; + + for (x= 0, sptr= head_sptr; x < iterations; x++, sptr++) + { + /* + We might not want to load any data, such as when we are calling + a stored_procedure that doesn't use data, or we know we already have + data in the table. + */ + if (!opt_preserve) + drop_schema(&mysql, create_schema_string); + /* First we create */ + if (create_statements) + create_schema(&mysql, create_schema_string, create_statements, eptr); + + run_scheduler(sptr, query_statements, *current, client_limit); + } + + generate_stats(&conclusion, eptr, head_sptr); + + if (!opt_silent) + print_conclusions(&conclusion); + if (opt_csv_str) + print_conclusions_csv(&conclusion); + + my_free((byte *)head_sptr, MYF(0)); + } + + if (!opt_preserve) + drop_schema(&mysql, create_schema_string); + } while (eptr ? (eptr= eptr->next) : 0); + + if (!opt_only_print) + mysql_close(&mysql); /* Close & free connection */ + + + /* Remove lock file */ + my_delete(lock_file_str, MYF(0)); + + /* now free all the strings we created */ + if (opt_password) + my_free(opt_password, MYF(0)); + + my_free((byte *)concurrency, MYF(0)); + + statement_cleanup(create_statements); + statement_cleanup(engine_statements); + statement_cleanup(query_statements); + +#ifdef HAVE_SMEM + if (shared_memory_base_name) + my_free(shared_memory_base_name,MYF(MY_ALLOW_ZERO_PTR)); +#endif + free_defaults(defaults_argv); + my_end(0); + + return 0; +} + + +static struct my_option my_long_options[] = +{ + {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, + 0, 0, 0, 0, 0, 0}, + {"auto-generate-sql", 'a', + "Generate SQL where not supplied by file or command line.", + (gptr*) &auto_generate_sql, (gptr*) &auto_generate_sql, + 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"compress", 'C', "Use compression in server/client protocol.", + (gptr*) &opt_compress, (gptr*) &opt_compress, 0, GET_BOOL, NO_ARG, 0, 0, 0, + 0, 0, 0}, + {"concurrency", 'c', "Number of clients to simulate for query to run.", + (gptr*) &concurrency_str, (gptr*) &concurrency_str, 0, GET_STR, + REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"create", OPT_CREATE_SLAP_SCHEMA, "File or string to use create tables.", + (gptr*) &create_string, (gptr*) &create_string, 0, GET_STR, REQUIRED_ARG, + 0, 0, 0, 0, 0, 0}, + {"create-schema", OPT_CREATE_SLAP_SCHEMA, "Schema to run tests in.", + (gptr*) &create_schema_string, (gptr*) &create_schema_string, 0, GET_STR, + REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"csv", OPT_CREATE_SLAP_SCHEMA, + "Generate CSV output to named file or to stdout if no file is named.", + (gptr*) &opt_csv_str, (gptr*) &opt_csv_str, 0, GET_STR, + OPT_ARG, 0, 0, 0, 0, 0, 0}, + {"debug", '#', "Output debug log. Often this is 'd:t:o,filename'.", + (gptr*) &default_dbug_option, (gptr*) &default_dbug_option, 0, GET_STR, + OPT_ARG, 0, 0, 0, 0, 0, 0}, + {"delimiter", 'F', + "Delimiter to use in SQL statements supplied in file or command line.", + (gptr*) &delimiter, (gptr*) &delimiter, 0, GET_STR, REQUIRED_ARG, + 0, 0, 0, 0, 0, 0}, + {"engine", 'e', "Storage engine to use for creating the table.", + (gptr*) &default_engine, (gptr*) &default_engine, 0, + GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"host", 'h', "Connect to host.", (gptr*) &host, (gptr*) &host, 0, GET_STR, + REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"iterations", 'i', "Number of times too run the tests.", (gptr*) &iterations, + (gptr*) &iterations, 0, GET_UINT, REQUIRED_ARG, 1, 0, 0, 0, 0, 0}, + {"lock-directory", OPT_MYSQL_LOCK_DIRECTORY, "Directory to use to keep locks.", + (gptr*) &lock_directory, (gptr*) &lock_directory, 0, GET_STR, + REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"number-char-cols", 'x', + "Number of INT columns to create table with if specifying --auto-generate-sql.", + (gptr*) &num_char_cols, (gptr*) &num_char_cols, 0, GET_UINT, REQUIRED_ARG, + 1, 0, 0, 0, 0, 0}, + {"number-int-cols", 'y', + "Number of VARCHAR columns to create table with if specifying " + "--auto-generate-sql.", (gptr*) &num_int_cols, (gptr*) &num_int_cols, 0, + GET_UINT, REQUIRED_ARG, 1, 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).", + (gptr*) &num_of_query, (gptr*) &num_of_query, 0, + GET_ULL, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"only-print", OPT_MYSQL_ONLY_PRINT, + "This causes mysqlslap to not connect to the databases, but instead print " + "out what it would have done instead.", + (gptr*) &opt_only_print, (gptr*) &opt_only_print, 0, GET_BOOL, NO_ARG, + 0, 0, 0, 0, 0, 0}, + {"password", 'p', + "Password to use when connecting to server. If password is not given it's " + "asked from the tty.", 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, +#ifdef __WIN__ + {"pipe", 'W', "Use named pipes to connect to server.", 0, 0, 0, GET_NO_ARG, + NO_ARG, 0, 0, 0, 0, 0, 0}, +#endif + {"port", 'P', "Port number to use for connection.", (gptr*) &opt_mysql_port, + (gptr*) &opt_mysql_port, 0, GET_UINT, REQUIRED_ARG, MYSQL_PORT, 0, 0, 0, 0, + 0}, + {"preserve-schema", OPT_MYSQL_PRESERVE_SCHEMA, + "Preserve the schema from the mysqlslap run, this happens unless " + "--auto-generate-sql or --create are used.", + (gptr*) &opt_preserve, (gptr*) &opt_preserve, 0, GET_BOOL, + NO_ARG, TRUE, 0, 0, 0, 0, 0}, + {"protocol", OPT_MYSQL_PROTOCOL, + "The protocol of connection (tcp,socket,pipe,memory).", + 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"query", 'q', "Query to run or file containing query to run.", + (gptr*) &user_supplied_query, (gptr*) &user_supplied_query, + 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, +#ifdef HAVE_SMEM + {"shared-memory-base-name", OPT_SHARED_MEMORY_BASE_NAME, + "Base name of shared memory.", (gptr*) &shared_memory_base_name, + (gptr*) &shared_memory_base_name, 0, GET_STR_ALLOC, REQUIRED_ARG, + 0, 0, 0, 0, 0, 0}, +#endif + {"silent", 's', "Run program in silent mode - no output.", + (gptr*) &opt_silent, (gptr*) &opt_silent, 0, GET_BOOL, NO_ARG, + 0, 0, 0, 0, 0, 0}, + {"slave", OPT_MYSQL_SLAP_SLAVE, "Follow master locks for other slap clients", + (gptr*) &opt_slave, (gptr*) &opt_slave, 0, GET_BOOL, NO_ARG, + 0, 0, 0, 0, 0, 0}, + {"socket", 'S', "Socket file to use for connection.", + (gptr*) &opt_mysql_unix_port, (gptr*) &opt_mysql_unix_port, 0, GET_STR, + REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, +#include <sslopt-longopts.h> + {"use-threads", OPT_USE_THREADS, + "Use pthread calls instead of fork() calls (default on Windows)", + (gptr*) &opt_use_threads, (gptr*) &opt_use_threads, 0, + GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, +#ifndef DONT_ALLOW_USER_CHANGE + {"user", 'u', "User for login if not current user.", (gptr*) &user, + (gptr*) &user, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, +#endif + {"verbose", 'v', + "More verbose output; You can use this multiple times to get even more " + "verbose output.", (gptr*) &verbose, (gptr*) &verbose, 0, + GET_NO_ARG, 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}, + {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} +}; + + +#include <help_start.h> + +static void print_version(void) +{ + printf("%s Ver %s Distrib %s, for %s (%s)\n",my_progname,SHOW_VERSION, + MYSQL_SERVER_VERSION,SYSTEM_TYPE,MACHINE_TYPE); +} + + +static void usage(void) +{ + print_version(); + puts("Copyright (C) 2005 MySQL AB"); + puts("This software comes with ABSOLUTELY NO WARRANTY. This is free software,\ + \nand you are welcome to modify and redistribute it under the GPL \ + license\n"); + puts("Run a query multiple times against the server\n"); + printf("Usage: %s [OPTIONS]\n",my_progname); + print_defaults("my",load_default_groups); + my_print_help(my_long_options); +} + +#include <help_end.h> + +static my_bool +get_one_option(int optid, const struct my_option *opt __attribute__((unused)), + char *argument) +{ + DBUG_ENTER("get_one_option"); + switch(optid) { +#ifdef __NETWARE__ + case OPT_AUTO_CLOSE: + setscreenmode(SCR_AUTOCLOSE_ON_EXIT); + break; +#endif + case 'v': + verbose++; + break; + case 'p': + if (argument) + { + char *start= argument; + my_free(opt_password,MYF(MY_ALLOW_ZERO_PTR)); + opt_password= my_strdup(argument,MYF(MY_FAE)); + while (*argument) *argument++= 'x'; /* Destroy argument */ + if (*start) + start[1]= 0; /* Cut length of argument */ + tty_password= 0; + } + else + tty_password= 1; + break; + case 'W': +#ifdef __WIN__ + opt_protocol= MYSQL_PROTOCOL_PIPE; +#endif + break; + case OPT_MYSQL_PROTOCOL: + { + if ((opt_protocol= find_type(argument, &sql_protocol_typelib,0)) <= 0) + { + fprintf(stderr, "Unknown option to protocol: %s\n", argument); + exit(1); + } + break; + } + case '#': + DBUG_PUSH(argument ? argument : default_dbug_option); + break; +#include <sslopt-case.h> + case 'V': + print_version(); + exit(0); + break; + case '?': + case 'I': /* Info */ + usage(); + exit(0); + } + DBUG_RETURN(0); +} + + +uint +get_random_string(char *buf) +{ + char *buf_ptr= buf; + int x; + DBUG_ENTER("get_random_string"); + for (x= RAND_STRING_SIZE; x > 0; x--) + *buf_ptr++= ALPHANUMERICS[random() % ALPHANUMERICS_SIZE]; + DBUG_PRINT("info", ("random string: '%*s'", buf_ptr - buf, buf)); + DBUG_RETURN(buf_ptr - buf); +} + + +/* + build_table_string + + This function builds a create table query if the user opts to not supply + a file or string containing a create table statement +*/ +static statement * +build_table_string(void) +{ + char buf[512]; + int col_count; + statement *ptr; + DYNAMIC_STRING table_string; + DBUG_ENTER("build_table_string"); + + DBUG_PRINT("info", ("num int cols %d num char cols %d", + num_int_cols, num_char_cols)); + + init_dynamic_string(&table_string, "", 1024, 1024); + + dynstr_append(&table_string, "CREATE TABLE `t1` ("); + for (col_count= 1; col_count <= num_int_cols; col_count++) + { + sprintf(buf, "intcol%d INT(32)", col_count); + dynstr_append(&table_string, buf); + + if (col_count < num_int_cols || num_char_cols > 0) + dynstr_append(&table_string, ","); + } + for (col_count= 1; col_count <= num_char_cols; col_count++) + { + sprintf(buf, "charcol%d VARCHAR(128)", col_count); + dynstr_append(&table_string, buf); + + if (col_count < num_char_cols) + dynstr_append(&table_string, ","); + } + dynstr_append(&table_string, ")"); + ptr= (statement *)my_malloc(sizeof(statement), MYF(MY_ZEROFILL)); + ptr->string = (char *)my_malloc(table_string.length+1, MYF(MY_WME)); + ptr->length= table_string.length+1; + strmov(ptr->string, table_string.str); + DBUG_PRINT("info", ("create_string %s", ptr->string)); + dynstr_free(&table_string); + DBUG_RETURN(ptr); +} + + +/* + build_insert_string() + + This function builds insert statements when the user opts to not supply + an insert file or string containing insert data +*/ +static statement * +build_insert_string(void) +{ + char buf[RAND_STRING_SIZE]; + int col_count; + statement *ptr; + DYNAMIC_STRING insert_string; + DBUG_ENTER("build_insert_string"); + + init_dynamic_string(&insert_string, "", 1024, 1024); + + dynstr_append_mem(&insert_string, "INSERT INTO t1 VALUES (", 23); + for (col_count= 1; col_count <= num_int_cols; col_count++) + { + sprintf(buf, "%ld", random()); + dynstr_append(&insert_string, buf); + + if (col_count < num_int_cols || num_char_cols > 0) + dynstr_append_mem(&insert_string, ",", 1); + } + for (col_count= 1; col_count <= num_char_cols; col_count++) + { + int buf_len= get_random_string(buf); + dynstr_append_mem(&insert_string, "'", 1); + dynstr_append_mem(&insert_string, buf, buf_len); + dynstr_append_mem(&insert_string, "'", 1); + + if (col_count < num_char_cols) + dynstr_append_mem(&insert_string, ",", 1); + } + dynstr_append_mem(&insert_string, ")", 1); + + ptr= (statement *)my_malloc(sizeof(statement), MYF(MY_ZEROFILL)); + ptr->string= (char *)my_malloc(insert_string.length+1, MYF(MY_WME)); + ptr->length= insert_string.length+1; + strmov(ptr->string, insert_string.str); + DBUG_PRINT("info", ("generated_insert_data %s", ptr->string)); + dynstr_free(&insert_string); + DBUG_RETURN(ptr); +} + + +/* + build_query_string() + + This function builds a query if the user opts to not supply a query + statement or file containing a query statement +*/ +static statement * +build_query_string(void) +{ + char buf[512]; + int col_count; + statement *ptr; + static DYNAMIC_STRING query_string; + DBUG_ENTER("build_query_string"); + + init_dynamic_string(&query_string, "", 1024, 1024); + + dynstr_append_mem(&query_string, "SELECT ", 7); + for (col_count= 1; col_count <= num_int_cols; col_count++) + { + sprintf(buf, "intcol%d", col_count); + dynstr_append(&query_string, buf); + + if (col_count < num_int_cols || num_char_cols > 0) + dynstr_append_mem(&query_string, ",", 1); + + } + for (col_count= 1; col_count <= num_char_cols; col_count++) + { + sprintf(buf, "charcol%d", col_count); + dynstr_append(&query_string, buf); + + if (col_count < num_char_cols) + dynstr_append_mem(&query_string, ",", 1); + + } + dynstr_append_mem(&query_string, " FROM t1", 8); + ptr= (statement *)my_malloc(sizeof(statement), MYF(MY_ZEROFILL)); + ptr->string= (char *)my_malloc(query_string.length+1, MYF(MY_WME)); + ptr->length= query_string.length+1; + strmov(ptr->string, query_string.str); + DBUG_PRINT("info", ("user_supplied_query %s", ptr->string)); + dynstr_free(&query_string); + DBUG_RETURN(ptr); +} + +static int +get_options(int *argc,char ***argv) +{ + int ho_error; + char *tmp_string; + MY_STAT sbuf; /* Stat information for the data file */ + + DBUG_ENTER("get_options"); + if ((ho_error= handle_options(argc, argv, my_long_options, get_one_option))) + exit(ho_error); + + if (!user) + user= (char *)"root"; + + if (create_string || auto_generate_sql) + { + if (verbose >= 1) + fprintf(stderr, "Turning off preserve-schema!\n"); + opt_preserve= FALSE; + } + + if (auto_generate_sql && (create_string || user_supplied_query)) + { + fprintf(stderr, + "%s: Can't use --auto-generate-sql when create and query strings are specified!\n", + my_progname); + exit(1); + } + + parse_comma(concurrency_str ? concurrency_str : "1", &concurrency); + + if (lock_directory) + snprintf(lock_file_str, FN_REFLEN, "%s/%s", lock_directory, MYSLAPLOCK); + else + snprintf(lock_file_str, FN_REFLEN, "%s/%s", MYSLAPLOCK_DIR, MYSLAPLOCK); + + if (opt_csv_str) + { + opt_silent= TRUE; + + if (opt_csv_str[0] == '-') + { + csv_file= fileno(stdout); + } + else + { + if ((csv_file= my_open(opt_csv_str, O_CREAT|O_WRONLY|O_APPEND, MYF(0))) + == -1) + { + fprintf(stderr,"%s: Could not open csv file: %sn\n", + my_progname, opt_csv_str); + exit(1); + } + } + } + + if (opt_only_print) + opt_silent= TRUE; + + if (auto_generate_sql) + { + create_statements= build_table_string(); + query_statements= build_insert_string(); + DBUG_PRINT("info", ("auto-generated insert is %s", query_statements->string)); + query_statements->next= build_query_string(); + DBUG_PRINT("info", ("auto-generated is %s", query_statements->next->string)); + if (verbose >= 1) + { + fprintf(stderr, "auto-generated insert is:\n"); + fprintf(stderr, "%s\n", query_statements->string); + fprintf(stderr, "auto-generated is:\n"); + fprintf(stderr, "%s\n", query_statements->next->string); + } + + } + else + { + if (create_string && my_stat(create_string, &sbuf, MYF(0))) + { + File data_file; + if (!MY_S_ISREG(sbuf.st_mode)) + { + fprintf(stderr,"%s: Create file was not a regular file\n", + my_progname); + exit(1); + } + if ((data_file= my_open(create_string, O_RDWR, MYF(0))) == -1) + { + fprintf(stderr,"%s: Could not open create file\n", my_progname); + exit(1); + } + tmp_string= (char *)my_malloc(sbuf.st_size+1, MYF(MY_WME)); + my_read(data_file, tmp_string, 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]); + my_free(tmp_string, MYF(0)); + } + else if (create_string) + { + parse_delimiter(create_string, &create_statements, delimiter[0]); + } + + if (user_supplied_query && my_stat(user_supplied_query, &sbuf, MYF(0))) + { + File data_file; + if (!MY_S_ISREG(sbuf.st_mode)) + { + fprintf(stderr,"%s: User query supplied file was not a regular file\n", + my_progname); + exit(1); + } + if ((data_file= my_open(user_supplied_query, O_RDWR, MYF(0))) == -1) + { + fprintf(stderr,"%s: Could not open query supplied file\n", my_progname); + exit(1); + } + tmp_string= (char *)my_malloc(sbuf.st_size+1, MYF(MY_WME)); + my_read(data_file, tmp_string, sbuf.st_size, MYF(0)); + tmp_string[sbuf.st_size]= '\0'; + my_close(data_file,MYF(0)); + if (user_supplied_query) + actual_queries= parse_delimiter(tmp_string, &query_statements, + delimiter[0]); + my_free(tmp_string, MYF(0)); + } + else if (user_supplied_query) + { + actual_queries= parse_delimiter(user_supplied_query, &query_statements, + delimiter[0]); + } + } + + if (default_engine) + parse_delimiter(default_engine, &engine_statements, ','); + + if (tty_password) + opt_password= get_tty_password(NullS); + DBUG_RETURN(0); +} + + +static int run_query(MYSQL *mysql, const char *query, int len) +{ + if (opt_only_print) + { + printf("%.*s;\n", len, query); + return 0; + } + + if (verbose >= 2) + printf("%.*s;\n", len, query); + return mysql_real_query(mysql, query, len); +} + + + +static int +create_schema(MYSQL *mysql, const char *db, statement *stmt, + statement *engine_stmt) +{ + char query[HUGE_STRING_LENGTH]; + statement *ptr; + int len; + DBUG_ENTER("create_schema"); + + len= snprintf(query, HUGE_STRING_LENGTH, "CREATE SCHEMA `%s`", db); + DBUG_PRINT("info", ("query %s", query)); + + if (run_query(mysql, query, len)) + { + fprintf(stderr,"%s: Cannot create schema %s : %s\n", my_progname, db, + mysql_error(mysql)); + exit(1); + } + + if (opt_only_print) + { + printf("use %s;\n", db); + } + else + { + if (verbose >= 2) + printf("%s;\n", query); + if (mysql_select_db(mysql, db)) + { + fprintf(stderr,"%s: Cannot select schema '%s': %s\n",my_progname, db, + mysql_error(mysql)); + exit(1); + } + } + + 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); + } + } + + for (ptr= stmt; ptr && ptr->length; ptr= ptr->next) + { + if (run_query(mysql, ptr->string, ptr->length)) + { + fprintf(stderr,"%s: Cannot run query %.*s ERROR : %s\n", + my_progname, (uint)ptr->length, ptr->string, mysql_error(mysql)); + exit(1); + } + } + + DBUG_RETURN(0); +} + +static int +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); + + if (run_query(mysql, query, len)) + { + fprintf(stderr,"%s: Cannot drop database '%s' ERROR : %s\n", + my_progname, db, mysql_error(mysql)); + exit(1); + } + + + + DBUG_RETURN(0); +} + +static int +run_scheduler(stats *sptr, statement *stmts, uint concur, ulonglong limit) +{ + uint x; + File lock_file; + struct timeval start_time, end_time; + thread_context con; + DBUG_ENTER("run_scheduler"); + + con.stmt= stmts; + con.limit= limit; + con.thread= opt_use_threads ? 1 :0; + + lock_file= my_open(lock_file_str, O_CREAT|O_WRONLY|O_TRUNC, MYF(0)); + + if (!opt_slave) + if (my_lock(lock_file, F_WRLCK, 0, F_TO_EOF, MYF(0))) + { + fprintf(stderr,"%s: Could not get lockfile\n", + my_progname); + exit(0); + } + +#ifdef HAVE_LIBPTHREAD + if (opt_use_threads) + { + pthread_t mainthread; /* Thread descriptor */ + pthread_attr_t attr; /* Thread attributes */ + + for (x= 0; x < concur; x++) + { + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, + PTHREAD_CREATE_DETACHED); + + /* now create the thread */ + if (pthread_create(&mainthread, &attr, (void *)run_task, + (void *)&con) != 0) + { + fprintf(stderr,"%s: Could not create thread\n", + my_progname); + exit(0); + } + } + } +#endif +#if !(defined(__WIN__) || defined(__NETWARE__)) +#ifdef HAVE_LIBPTHREAD + else +#endif + { + fflush(NULL); + for (x= 0; x < concur; x++) + { + int pid; + DBUG_PRINT("info", ("x %d concurrency %d", x, concurrency)); + pid= fork(); + switch(pid) + { + case 0: + /* child */ + DBUG_PRINT("info", ("fork returned 0, calling task(\"%s\"), pid %d gid %d", + stmts ? stmts->string : "", pid, getgid())); + if (verbose >= 2) + fprintf(stderr, + "%s: fork returned 0, calling task pid %d gid %d\n", + my_progname, pid, getgid()); + run_task(&con); + exit(0); + break; + case -1: + /* error */ + DBUG_PRINT("info", + ("fork returned -1, failing pid %d gid %d", pid, getgid())); + fprintf(stderr, + "%s: Failed on fork: -1, max procs per parent exceeded.\n", + my_progname); + /*exit(1);*/ + goto WAIT; + default: + /* parent, forked */ + DBUG_PRINT("info", ("default, break: pid %d gid %d", pid, getgid())); + if (verbose >= 2) + fprintf(stderr,"%s: fork returned %d, gid %d\n", + my_progname, pid, getgid()); + break; + } + } + } +#endif + + /* Lets release use some clients! */ + if (!opt_slave) + my_lock(lock_file, F_UNLCK, 0, F_TO_EOF, MYF(0)); + + gettimeofday(&start_time, NULL); + + /* + We look to grab a write lock at this point. Once we get it we know that + all clients have completed their work. + */ + if (opt_use_threads) + { + if (my_lock(lock_file, F_WRLCK, 0, F_TO_EOF, MYF(0))) + { + fprintf(stderr,"%s: Could not get lockfile\n", + my_progname); + exit(0); + } + my_lock(lock_file, F_UNLCK, 0, F_TO_EOF, MYF(0)); + } +#ifndef __WIN__ + else + { +WAIT: + while (x--) + { + int status, pid; + pid= wait(&status); + DBUG_PRINT("info", ("Parent: child %d status %d", pid, status)); + if (status != 0) + printf("%s: Child %d died with the status %d\n", + my_progname, pid, status); + } + } +#endif + gettimeofday(&end_time, NULL); + + my_close(lock_file, MYF(0)); + + sptr->timing= timedif(end_time, start_time); + sptr->users= concur; + sptr->rows= limit; + + DBUG_RETURN(0); +} + + +int +run_task(thread_context *con) +{ + ulonglong counter= 0, queries; + File lock_file= -1; + MYSQL *mysql; + MYSQL_RES *result; + MYSQL_ROW row; + statement *ptr; + + DBUG_ENTER("run_task"); + DBUG_PRINT("info", ("task script \"%s\"", con->stmt ? con->stmt->string : "")); + + if (!(mysql= mysql_init(NULL))) + goto end; + + if (con->thread && mysql_thread_init()) + goto end; + + DBUG_PRINT("info", ("trying to connect to host %s as user %s", host, user)); + lock_file= my_open(lock_file_str, O_RDWR, MYF(0)); + my_lock(lock_file, F_RDLCK, 0, F_TO_EOF, MYF(0)); + if (!opt_only_print) + { + /* Connect to server */ + static ulong connection_retry_sleep= 100000; /* Microseconds */ + int i, connect_error= 1; + for (i= 0; i < 10; i++) + { + if (mysql_real_connect(mysql, host, user, opt_password, + create_schema_string, + opt_mysql_port, + opt_mysql_unix_port, + connect_flags)) + { + /* Connect suceeded */ + connect_error= 0; + break; + } + my_sleep(connection_retry_sleep); + } + if (connect_error) + { + fprintf(stderr,"%s: Error when connecting to server: %d %s\n", + my_progname, mysql_errno(mysql), mysql_error(mysql)); + goto end; + } + } + DBUG_PRINT("info", ("connected.")); + if (verbose >= 3) + fprintf(stderr, "connected!\n"); + queries= 0; + +limit_not_met: + for (ptr= con->stmt; ptr && ptr->length; ptr= ptr->next) + { + if (run_query(mysql, ptr->string, ptr->length)) + { + fprintf(stderr,"%s: Cannot run query %.*s ERROR : %s\n", + my_progname, (uint)ptr->length, ptr->string, mysql_error(mysql)); + goto end; + } + if (mysql_field_count(mysql)) + { + result= mysql_store_result(mysql); + while ((row = mysql_fetch_row(result))) + counter++; + mysql_free_result(result); + } + queries++; + + if (con->limit && queries == con->limit) + goto end; + } + + if (!con->stmt && con->limit && queries < con->limit) + goto limit_not_met; + +end: + + if (lock_file != -1) + { + my_lock(lock_file, F_UNLCK, 0, F_TO_EOF, MYF(0)); + my_close(lock_file, MYF(0)); + } + + if (!opt_only_print) + mysql_close(mysql); + + if (con->thread) + my_thread_end(); + DBUG_RETURN(0); +} + + +uint +parse_delimiter(const char *script, statement **stmt, char delm) +{ + char *retstr; + char *ptr= (char *)script; + statement **sptr= stmt; + statement *tmp; + uint length= strlen(script); + uint count= 0; /* We know that there is always one */ + + DBUG_PRINT("info", ("Parsing %s\n", script)); + + for (tmp= *sptr= (statement *)my_malloc(sizeof(statement), MYF(MY_ZEROFILL)); + (retstr= strchr(ptr, delm)); + tmp->next= (statement *)my_malloc(sizeof(statement), MYF(MY_ZEROFILL)), + tmp= tmp->next) + { + count++; + tmp->string= my_strndup(ptr, (size_t)(retstr - ptr), MYF(MY_FAE)); + tmp->length= (size_t)(retstr - ptr); + DBUG_PRINT("info", (" Creating : %.*s\n", (uint)tmp->length, tmp->string)); + ptr+= retstr - ptr + 1; + if (isspace(*ptr)) + ptr++; + count++; + } + + if (ptr != script+length) + { + tmp->string= my_strndup(ptr, (size_t)((script + length) - ptr), + MYF(MY_FAE)); + tmp->length= (size_t)((script + length) - ptr); + DBUG_PRINT("info", (" Creating : %.*s\n", (uint)tmp->length, tmp->string)); + count++; + } + + return count; +} + + +uint +parse_comma(const char *string, uint **range) +{ + uint count= 1,x; /* We know that there is always one */ + char *retstr; + char *ptr= (char *)string; + uint *nptr; + + for (;*ptr; ptr++) + if (*ptr == ',') count++; + + /* One extra spot for the NULL */ + nptr= *range= (uint *)my_malloc(sizeof(uint) * (count + 1), MYF(MY_ZEROFILL)); + + ptr= (char *)string; + x= 0; + while ((retstr= strchr(ptr,','))) + { + nptr[x++]= atoi(ptr); + ptr+= retstr - ptr + 1; + } + nptr[x++]= atoi(ptr); + + return count; +} + +void +print_conclusions(conclusions *con) +{ + printf("Benchmark\n"); + if (con->engine) + printf("\tRunning for engine %s\n", con->engine); + printf("\tAverage number of seconds to run all queries: %ld.%03ld seconds\n", + con->avg_timing / 1000, con->avg_timing % 1000); + printf("\tMinimum number of seconds to run all queries: %ld.%03ld seconds\n", + con->min_timing / 1000, con->min_timing % 1000); + printf("\tMaximum number of seconds to run all queries: %ld.%03ld seconds\n", + con->max_timing / 1000, con->max_timing % 1000); + printf("\tNumber of clients running queries: %d\n", con->users); + printf("\tAverage number of queries per client: %llu\n", con->avg_rows); + printf("\n"); +} + +void +print_conclusions_csv(conclusions *con) +{ + char buffer[HUGE_STRING_LENGTH]; + snprintf(buffer, HUGE_STRING_LENGTH, + "%s,query,%ld.%03ld,%ld.%03ld,%ld.%03ld,%d,%llu\n", + con->engine ? con->engine : "", /* Storage engine we ran against */ + con->avg_timing / 1000, con->avg_timing % 1000, /* Time to load */ + con->min_timing / 1000, con->min_timing % 1000, /* Min time */ + con->max_timing / 1000, con->max_timing % 1000, /* Max time */ + con->users, /* Children used */ + con->avg_rows /* Queries run */ + ); + my_write(csv_file, buffer, strlen(buffer), MYF(0)); +} + +void +generate_stats(conclusions *con, statement *eng, stats *sptr) +{ + stats *ptr; + int x; + + con->min_timing= sptr->timing; + con->max_timing= sptr->timing; + con->min_rows= sptr->rows; + con->max_rows= sptr->rows; + + /* At the moment we assume uniform */ + con->users= sptr->users; + con->avg_rows= sptr->rows; + + /* With no next, we know it is the last element that was malloced */ + for (ptr= sptr, x= 0; x < iterations; ptr++, x++) + { + con->avg_timing+= ptr->timing; + + if (ptr->timing > con->max_timing) + con->max_timing= ptr->timing; + if (ptr->timing < con->min_timing) + con->min_timing= ptr->timing; + } + con->avg_timing= con->avg_timing/iterations; + + if (eng && eng->string) + con->engine= eng->string; + else + con->engine= NULL; +} + +void +statement_cleanup(statement *stmt) +{ + statement *ptr, *nptr; + if (!stmt) + return; + + for (ptr= stmt; ptr; ptr= nptr) + { + nptr= ptr->next; + if (ptr->string) + my_free(ptr->string, MYF(0)); + my_free((byte *)ptr, MYF(0)); + } +} diff --git a/client/mysqltest.c b/client/mysqltest.c index 2fc09fbc3d2..fdd00ba4af2 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -82,9 +82,6 @@ #define MAX_EXPECTED_ERRORS 10 #define QUERY_SEND 1 #define QUERY_REAP 2 -#ifndef MYSQL_MANAGER_PORT -#define MYSQL_MANAGER_PORT 23546 -#endif #define MAX_SERVER_ARGS 64 @@ -96,11 +93,11 @@ #define RESULT_CONTENT_MISMATCH 1 #define RESULT_LENGTH_MISMATCH 2 -enum {OPT_MANAGER_USER=256,OPT_MANAGER_HOST,OPT_MANAGER_PASSWD, - OPT_MANAGER_PORT,OPT_MANAGER_WAIT_TIMEOUT, OPT_SKIP_SAFEMALLOC, - OPT_SSL_SSL, OPT_SSL_KEY, OPT_SSL_CERT, OPT_SSL_CA, OPT_SSL_CAPATH, - OPT_SSL_CIPHER,OPT_PS_PROTOCOL,OPT_SP_PROTOCOL,OPT_CURSOR_PROTOCOL, - OPT_VIEW_PROTOCOL, OPT_SSL_VERIFY_SERVER_CERT, OPT_MAX_CONNECT_RETRIES}; +enum {OPT_SKIP_SAFEMALLOC=256, OPT_SSL_SSL, OPT_SSL_KEY, OPT_SSL_CERT, + OPT_SSL_CA, OPT_SSL_CAPATH, OPT_SSL_CIPHER, OPT_PS_PROTOCOL, + OPT_SP_PROTOCOL, OPT_CURSOR_PROTOCOL, OPT_VIEW_PROTOCOL, + OPT_SSL_VERIFY_SERVER_CERT, OPT_MAX_CONNECT_RETRIES, + OPT_MARK_PROGRESS}; /* ************************************************************************ */ /* @@ -148,20 +145,17 @@ static uint global_expected_errors; static int record= 0, opt_sleep= -1; static char *db = 0, *pass=0; const char *user = 0, *host = 0, *unix_sock = 0, *opt_basedir="./"; +const char *opt_include= 0; static int port = 0; static int opt_max_connect_retries; static my_bool opt_big_test= 0, opt_compress= 0, silent= 0, verbose = 0; static my_bool tty_password= 0; +static my_bool opt_mark_progress= 0; static my_bool ps_protocol= 0, ps_protocol_enabled= 0; static my_bool sp_protocol= 0, sp_protocol_enabled= 0; static my_bool view_protocol= 0, view_protocol_enabled= 0; static my_bool cursor_protocol= 0, cursor_protocol_enabled= 0; static int parsing_disabled= 0; -const char *manager_user="root",*manager_host=0; -char *manager_pass=0; -int manager_port=MYSQL_MANAGER_PORT; -int manager_wait_timeout=3; -MYSQL_MANAGER* manager=0; static char **default_argv; static const char *load_default_groups[]= { "mysqltest","client",0 }; @@ -177,7 +171,36 @@ typedef struct static test_file file_stack[MAX_INCLUDE_DEPTH]; static test_file* cur_file; static test_file* file_stack_end; -uint start_lineno; /* Start line of query */ +uint start_lineno= 0; /* Start line of query */ + +/* Stores regex substitutions */ + +struct st_regex +{ + char* pattern; /* Pattern to be replaced */ + char* replace; /* String or expression to replace the pattern with */ + int icase; /* true if the match is case insensitive */ +}; + +struct st_replace_regex +{ + DYNAMIC_ARRAY regex_arr; /* stores a list of st_regex subsitutions */ + + /* + Temporary storage areas for substitutions. To reduce unnessary copying + and memory freeing/allocation, we pre-allocate two buffers, and alternate + their use, one for input/one for output, the roles changing on the next + st_regex substition. At the end of substitutions buf points to the + one containing the final result. + */ + char* buf; + char* even_buf; + char* odd_buf; + int even_buf_len; + int odd_buf_len; +}; + +struct st_replace_regex *glob_replace_regex= 0; static char TMPDIR[FN_REFLEN]; static char delimiter[MAX_DELIMITER]= DEFAULT_DELIMITER; @@ -204,7 +227,7 @@ static my_bool display_result_vertically= FALSE, display_metadata= FALSE; /* See the timer_output() definition for details */ static char *timer_file = NULL; -static ulonglong timer_start; +static ulonglong timer_start, progress_start= 0; static int got_end_timer= FALSE; static void timer_output(void); static ulonglong timer_now(void); @@ -218,6 +241,9 @@ static void init_re(void); static int match_re(my_regex_t *, char *); static void free_re(void); +static int reg_replace(char** buf_p, int* buf_len_p, char *pattern, char *replace, + char *string, int icase); + static const char *embedded_server_groups[]= { "server", @@ -265,6 +291,7 @@ typedef struct int alloced_len; int int_dirty; /* do not update string if int is updated until first read */ int alloced; + char *env_s; } VAR; VAR var_reg[10]; @@ -299,7 +326,6 @@ Q_RPL_PROBE, Q_ENABLE_RPL_PARSE, Q_DISABLE_RPL_PARSE, Q_EVAL_RESULT, Q_ENABLE_QUERY_LOG, Q_DISABLE_QUERY_LOG, Q_ENABLE_RESULT_LOG, Q_DISABLE_RESULT_LOG, -Q_SERVER_START, Q_SERVER_STOP,Q_REQUIRE_MANAGER, Q_WAIT_FOR_SLAVE_TO_STOP, Q_ENABLE_WARNINGS, Q_DISABLE_WARNINGS, Q_ENABLE_PS_WARNINGS, Q_DISABLE_PS_WARNINGS, @@ -311,10 +337,10 @@ Q_DISPLAY_VERTICAL_RESULTS, Q_DISPLAY_HORIZONTAL_RESULTS, Q_QUERY_VERTICAL, Q_QUERY_HORIZONTAL, Q_START_TIMER, Q_END_TIMER, Q_CHARACTER_SET, Q_DISABLE_PS_PROTOCOL, Q_ENABLE_PS_PROTOCOL, -Q_EXIT, Q_DISABLE_RECONNECT, Q_ENABLE_RECONNECT, Q_IF, Q_DISABLE_PARSING, Q_ENABLE_PARSING, +Q_REPLACE_REGEX, Q_DIE, Q_UNKNOWN, /* Unknown command. */ Q_COMMENT, /* Comments, ignored. */ @@ -372,9 +398,6 @@ const char *command_names[]= /* Enable/disable that the _result_ from a query is logged to result file */ "enable_result_log", "disable_result_log", - "server_start", - "server_stop", - "require_manager", "wait_for_slave_to_stop", "enable_warnings", "disable_warnings", @@ -397,19 +420,20 @@ const char *command_names[]= "character_set", "disable_ps_protocol", "enable_ps_protocol", - "exit", "disable_reconnect", "enable_reconnect", "if", "disable_parsing", "enable_parsing", + "replace_regex", + "die", 0 }; TYPELIB command_typelib= {array_elements(command_names),"", command_names, 0}; -DYNAMIC_STRING ds_res; +DYNAMIC_STRING ds_res, ds_progress; static void die(const char *fmt, ...); static void init_var_hash(); static VAR* var_from_env(const char *, const char *); @@ -442,6 +466,7 @@ struct st_replace; struct st_replace *init_replace(my_string *from, my_string *to, uint count, my_string word_end_chars); void free_replace(); +static void free_replace_regex(); static int insert_pointer_name(reg1 POINTER_ARRAY *pa,my_string name); static void replace_strings_append(struct st_replace *rep, DYNAMIC_STRING* ds, const char *from, int len); @@ -571,10 +596,7 @@ static void free_used_memory() { uint i; DBUG_ENTER("free_used_memory"); -#ifndef EMBEDDED_LIBRARY - if (manager) - mysql_manager_close(manager); -#endif + close_cons(); close_files(); hash_free(&var_hash); @@ -594,6 +616,7 @@ static void free_used_memory() my_free(embedded_server_args[--embedded_server_arg_count],MYF(0)); delete_dynamic(&q_lines); dynstr_free(&ds_res); + dynstr_free(&ds_progress); free_replace(); free_replace_column(); my_free(pass,MYF(MY_ALLOW_ZERO_PTR)); @@ -620,7 +643,7 @@ static void die(const char *fmt, ...) if (cur_file && cur_file != file_stack) fprintf(stderr, "In included file \"%s\": ", cur_file->file_name); - if (start_lineno != 0) + if (start_lineno > 0) fprintf(stderr, "At line %u: ", start_lineno); vfprintf(stderr, fmt, args); fprintf(stderr, "\n"); @@ -642,16 +665,43 @@ static void die(const char *fmt, ...) exit(1); } -/* Note that we will get some memory leaks when calling this! */ -static void abort_not_supported_test() +static void abort_not_supported_test(const char *fmt, ...) { + va_list args; + test_file* err_file= cur_file; DBUG_ENTER("abort_not_supported_test"); - fprintf(stderr, "This test is not supported by this installation\n"); - if (!silent) - printf("skipped\n"); + + /* Print include filestack */ + 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", + err_file->file_name, err_file->lineno); + while (err_file != file_stack) + { + err_file--; + fprintf(stderr, "included from %s at line %d\n", + err_file->file_name, err_file->lineno); + } + + /* Print error message */ + va_start(args, fmt); + if (fmt) + { + fprintf(stderr, "reason: "); + vfprintf(stderr, fmt, args); + fprintf(stderr, "\n"); + fflush(stderr); + } + va_end(args); + + /* Clean up and exit */ free_used_memory(); my_end(MY_CHECK_ERROR); + + if (!silent) + printf("skipped\n"); + exit(62); } @@ -663,13 +713,13 @@ static void verbose_msg(const char *fmt, ...) DBUG_VOID_RETURN; va_start(args, fmt); - fprintf(stderr, "mysqltest: "); if (start_lineno != 0) fprintf(stderr, "At line %u: ", start_lineno); vfprintf(stderr, fmt, args); fprintf(stderr, "\n"); va_end(args); + DBUG_VOID_RETURN; } @@ -695,10 +745,10 @@ static int dyn_string_cmp(DYNAMIC_STRING* ds, const char *fname) if (!test_if_hard_path(fname)) { strxmov(eval_file, opt_basedir, fname, NullS); - fn_format(eval_file, eval_file,"","",4); + fn_format(eval_file, eval_file, "", "", MY_UNPACK_FILENAME); } else - fn_format(eval_file, fname,"","",4); + fn_format(eval_file, fname, "", "", MY_UNPACK_FILENAME); if (!my_stat(eval_file, &stat_info, MYF(MY_WME))) die(NullS); @@ -739,8 +789,9 @@ static int dyn_string_cmp(DYNAMIC_STRING* ds, const char *fname) err: if (res && eval_result) - str_to_file(fn_format(eval_file, fname, "", ".eval",2), res_ptr, - res_len); + str_to_file(fn_format(eval_file, fname, "", ".eval", + MY_REPLACE_EXT), + res_ptr, res_len); my_free((gptr) tmp, MYF(0)); my_close(fd, MYF(MY_WME)); @@ -770,7 +821,11 @@ static void check_result(DYNAMIC_STRING* ds, const char *fname, DBUG_ENTER("check_result"); if (res && require_option) - abort_not_supported_test(); + { + char reason[FN_REFLEN]; + fn_format(reason, fname, "", "", MY_REPLACE_EXT | MY_REPLACE_DIR); + abort_not_supported_test("Test requires: '%s'", reason); + } switch (res) { case RESULT_OK: break; /* ok */ @@ -855,10 +910,15 @@ static VAR *var_obtain(const char *name, int len) return v; } +/* + - if variable starts with a $ it is regarded as a local test varable + - if not it is treated as a environment variable, and the corresponding + environment variable will be updated +*/ int var_set(const char *var_name, const char *var_name_end, const char *var_val, const char *var_val_end) { - int digit; + int digit, result, env_var= 0; VAR* v; DBUG_ENTER("var_set"); DBUG_PRINT("enter", ("var_name: '%.*s' = '%.*s' (length: %d)", @@ -866,11 +926,11 @@ int var_set(const char *var_name, const char *var_name_end, (int) (var_val_end - var_val), var_val, (int) (var_val_end - var_val))); - if (*var_name++ != '$') - { - var_name--; - die("Variable name in %s does not start with '$'", var_name); - } + if (*var_name != '$') + env_var= 1; + else + var_name++; + digit = *var_name - '0'; if (!(digit < 10 && digit >= 0)) { @@ -878,7 +938,25 @@ int var_set(const char *var_name, const char *var_name_end, } else v = var_reg + digit; - DBUG_RETURN(eval_expr(v, var_val, (const char**)&var_val_end)); + + result= eval_expr(v, var_val, (const char**) &var_val_end); + + if (env_var) + { + char buf[1024], *old_env_s= v->env_s; + if (v->int_dirty) + { + sprintf(v->str_val, "%d", v->int_val); + v->int_dirty= 0; + v->str_val_len= strlen(v->str_val); + } + strxmov(buf, v->name, "=", v->str_val, NullS); + if (!(v->env_s= my_strdup(buf, MYF(MY_WME)))) + die("Out of memory"); + putenv(v->env_s); + my_free((gptr)old_env_s, MYF(MY_ALLOW_ZERO_PTR)); + } + DBUG_RETURN(result); } @@ -892,7 +970,7 @@ int open_file(const char *name) strxmov(buff, opt_basedir, name, NullS); name=buff; } - fn_format(buff,name,"","",4); + fn_format(buff, name, "", "", MY_UNPACK_FILENAME); if (cur_file == file_stack_end) die("Source directives are nesting too deep"); @@ -966,57 +1044,6 @@ int do_wait_for_slave_to_stop(struct st_query *q __attribute__((unused))) return 0; } -int do_require_manager(struct st_query *query __attribute__((unused)) ) -{ - if (!manager) - abort_not_supported_test(); - return 0; -} - -#ifndef EMBEDDED_LIBRARY -static int do_server_op(struct st_query *q, const char *op) -{ - char *p= q->first_argument; - char com_buf[256], *com_p; - if (!manager) - { - die("Manager is not initialized, manager commands are not possible"); - } - com_p= strmov(com_buf,op); - com_p= strmov(com_p,"_exec "); - if (!*p) - die("Missing server name in server_%s", op); - while (*p && !my_isspace(charset_info, *p)) - *com_p++= *p++; - *com_p++= ' '; - com_p= int10_to_str(manager_wait_timeout, com_p, 10); - *com_p++= '\n'; - *com_p= 0; - if (mysql_manager_command(manager, com_buf, (int)(com_p-com_buf))) - die("Error in command: %s(%d)", manager->last_error, manager->last_errno); - while (!manager->eof) - { - if (mysql_manager_fetch_line(manager, com_buf, sizeof(com_buf))) - die("Error fetching result line: %s(%d)", manager->last_error, - manager->last_errno); - } - - q->last_argument= p; - return 0; -} - -int do_server_start(struct st_query *q) -{ - return do_server_op(q, "start"); -} - -int do_server_stop(struct st_query *q) -{ - return do_server_op(q, "stop"); -} - -#endif - /* Source and execute the given file @@ -1143,7 +1170,7 @@ static void do_exec(struct st_query *query) } } error= pclose(res_file); - if (error != 0) + if (error > 0) { uint status= WEXITSTATUS(error), i; my_bool ok= 0; @@ -1211,6 +1238,7 @@ int var_query_set(VAR* var, const char *query, const char** query_end) MYSQL_RES *res; MYSQL_ROW row; MYSQL* mysql = &cur_con->mysql; + DBUG_ENTER("var_query_set"); LINT_INIT(res); while (end > query && *end != '`') @@ -1237,7 +1265,9 @@ int var_query_set(VAR* var, const char *query, const char** query_end) uint i; ulong *lengths; char *end; +#ifdef NOT_YET MYSQL_FIELD *fields= mysql_fetch_fields(res); +#endif init_dynamic_string(&result, "", 16384, 65536); lengths= mysql_fetch_lengths(res); @@ -1273,7 +1303,7 @@ int var_query_set(VAR* var, const char *query, const char** query_end) eval_expr(var, "", 0); mysql_free_result(res); - return 0; + DBUG_RETURN(0); } void var_copy(VAR *dest, VAR *src) @@ -1450,6 +1480,7 @@ void do_system(struct st_query *command) } command->last_argument= command->end; + dynstr_free(&ds_cmd); DBUG_VOID_RETURN; } @@ -1490,7 +1521,7 @@ int do_echo(struct st_query *command) dynstr_append_mem(ds, "\n", 1); dynstr_free(&ds_echo); command->last_argument= command->end; - return 0; + return(0); } @@ -1527,9 +1558,9 @@ wait_for_position: It may be that the slave SQL thread has not started yet, though START SLAVE has been issued ? */ - if (tries++ == 3) + if (tries++ == 30) die("could not sync with master ('%s' returned NULL)", query_buf); - sleep(1); /* So at most we will wait 3 seconds and make 4 tries */ + sleep(1); /* So at most we will wait 30 seconds and make 31 tries */ mysql_free_result(res); goto wait_for_position; } @@ -1557,6 +1588,10 @@ int do_sync_with_master(struct st_query *query) return do_sync_with_master2(offset); } +/* + when ndb binlog is on, this call will wait until last updated epoch + (locally in the mysqld) has been received into the binlog +*/ int do_save_master_pos() { MYSQL_RES* res; @@ -1568,6 +1603,95 @@ int do_save_master_pos() rpl_parse = mysql_rpl_parse_enabled(mysql); mysql_disable_rpl_parse(mysql); +#ifdef HAVE_NDB_BINLOG + /* + Wait for ndb binlog to be up-to-date with all changes + done on the local mysql server + */ + { + ulong have_ndbcluster; + if (mysql_query(mysql, query= "show variables like 'have_ndbcluster'")) + die("At line %u: failed in %s: %d: %s", start_lineno, query, + mysql_errno(mysql), mysql_error(mysql)); + if (!(res= mysql_store_result(mysql))) + die("line %u: mysql_store_result() retuned NULL for '%s'", start_lineno, + query); + if (!(row= mysql_fetch_row(res))) + die("line %u: empty result in %s", start_lineno, query); + + have_ndbcluster= strcmp("YES", row[1]) == 0; + mysql_free_result(res); + + if (have_ndbcluster) + { + ulonglong epoch=0, tmp_epoch= 0; + int count= 0; + int do_continue= 1; + while (do_continue) + { + const char binlog[]= "binlog"; + const char latest_trans_epoch[]= + "latest_trans_epoch="; + const char latest_handled_binlog_epoch[]= + "latest_handled_binlog_epoch="; + if (count) + sleep(1); + if (mysql_query(mysql, query= "show engine ndb status")) + die("At line %u: failed in '%s': %d: %s", start_lineno, query, + mysql_errno(mysql), mysql_error(mysql)); + if (!(res= mysql_store_result(mysql))) + die("line %u: mysql_store_result() retuned NULL for '%s'", + start_lineno, query); + while ((row= mysql_fetch_row(res))) + { + if (strcmp(row[1], binlog) == 0) + { + const char *status= row[2]; + /* latest_trans_epoch */ + if (count == 0) + { + while (*status && strncmp(status, latest_trans_epoch, + sizeof(latest_trans_epoch)-1)) + status++; + if (*status) + { + status+= sizeof(latest_trans_epoch)-1; + epoch= strtoull(status, (char**) 0, 10); + } + else + die("line %u: result does not contain '%s' in '%s'", + start_lineno, latest_trans_epoch, query); + } + /* latest_applied_binlog_epoch */ + while (*status && strncmp(status, latest_handled_binlog_epoch, + sizeof(latest_handled_binlog_epoch)-1)) + status++; + if (*status) + { + status+= sizeof(latest_handled_binlog_epoch)-1; + tmp_epoch= strtoull(status, (char**) 0, 10); + } + else + die("line %u: result does not contain '%s' in '%s'", + start_lineno, latest_handled_binlog_epoch, query); + break; + } + } + if (!row) + die("line %u: result does not contain '%s' in '%s'", + start_lineno, binlog, query); + count++; + if (tmp_epoch >= epoch) + do_continue= 0; + else if (count > 30) + { + break; + } + mysql_free_result(res); + } + } + } +#endif if (mysql_query(mysql, query= "show master status")) die("failed in show master status: %d: %s", mysql_errno(mysql), mysql_error(mysql)); @@ -1618,7 +1742,8 @@ int do_let(struct st_query *query) while (*p && (*p != '=') && !my_isspace(charset_info,*p)) p++; var_name_end= p; - if (var_name+1 == var_name_end) + if (var_name == var_name_end || + (var_name+1 == var_name_end && *var_name == '$')) die("Missing variable name in let"); while (my_isspace(charset_info,*p)) p++; @@ -1757,7 +1882,7 @@ static void set_charset(struct st_query *q) q->last_argument= p; charset_info= get_charset_by_csname(charset_name,MY_CS_PRIMARY,MYF(MY_WME)); if (!charset_info) - abort_not_supported_test(); + abort_not_supported_test("Test requires charset '%s'", charset_name); } static uint get_errcodes(match_err *to,struct st_query *q) @@ -1794,7 +1919,13 @@ static uint get_errcodes(match_err *to,struct st_query *q) ; for (; e->name; e++) { - if (!strncmp(start, e->name, (int) (p - start))) + /* + If we get a match, we need to check the length of the name we + matched against in case it was longer than what we are checking + (as in ER_WRONG_VALUE vs. ER_WRONG_VALUE_COUNT). + */ + if (!strncmp(start, e->name, (int) (p - start)) && + (uint) strlen(e->name) == (uint) (p - start)) { to[count].code.errnum= (uint) e->code; to[count].type= ERR_ERRNO; @@ -1900,6 +2031,204 @@ static char *get_string(char **to_ptr, char **from_ptr, DBUG_RETURN(start); } +/* + Finds the next (non-escaped) '/' in the expression. + (If the character '/' is needed, it can be escaped using '\'.) +*/ + +#define PARSE_REGEX_ARG \ + while (p < expr_end) \ + {\ + char c= *p;\ + if (c == '/')\ + {\ + if (last_c == '\\')\ + {\ + buf_p[-1]= '/';\ + }\ + else\ + {\ + *buf_p++ = 0;\ + break;\ + } \ + } \ + else\ + *buf_p++ = c;\ + \ + last_c= c;\ + p++;\ + } \ + +/* + Initializes the regular substitution expression to be used in the + result output of test. + + Returns: st_replace_regex struct with pairs of substitutions +*/ + +static struct st_replace_regex* init_replace_regex(char* expr) +{ + struct st_replace_regex* res; + char* buf,*expr_end; + char* p; + char* buf_p; + uint expr_len= strlen(expr); + char last_c = 0; + struct st_regex reg; + + /* my_malloc() will die on fail with MY_FAE */ + res=(struct st_replace_regex*)my_malloc( + sizeof(*res)+expr_len ,MYF(MY_FAE+MY_WME)); + my_init_dynamic_array(&res->regex_arr,sizeof(struct st_regex),128,128); + + buf= (char*)res + sizeof(*res); + expr_end= expr + expr_len; + p= expr; + buf_p= buf; + + /* for each regexp substitution statement */ + while (p < expr_end) + { + bzero(®,sizeof(reg)); + /* find the start of the statement */ + while (p < expr_end) + { + if (*p == '/') + break; + p++; + } + + if (p == expr_end || ++p == expr_end) + { + if (res->regex_arr.elements) + break; + else + goto err; + } + /* we found the start */ + reg.pattern= buf_p; + + /* Find first argument -- pattern string to be removed */ + PARSE_REGEX_ARG + + if (p == expr_end || ++p == expr_end) + goto err; + + /* buf_p now points to the replacement pattern terminated with \0 */ + reg.replace= buf_p; + + /* Find second argument -- replace string to replace pattern */ + PARSE_REGEX_ARG + + if (p == expr_end) + goto err; + + /* skip the ending '/' in the statement */ + p++; + + /* Check if we should do matching case insensitive */ + if (p < expr_end && *p == 'i') + reg.icase= 1; + + /* done parsing the statement, now place it in regex_arr */ + if (insert_dynamic(&res->regex_arr,(gptr) ®)) + die("Out of memory"); + } + res->odd_buf_len= res->even_buf_len= 8192; + res->even_buf= (char*)my_malloc(res->even_buf_len,MYF(MY_WME+MY_FAE)); + res->odd_buf= (char*)my_malloc(res->odd_buf_len,MYF(MY_WME+MY_FAE)); + res->buf= res->even_buf; + + return res; + +err: + my_free((gptr)res,0); + die("Error parsing replace_regex \"%s\"", expr); + return 0; +} + +/* + Execute all substitutions on val. + + Returns: true if substituition was made, false otherwise + Side-effect: Sets r->buf to be the buffer with all substitutions done. + + IN: + struct st_replace_regex* r + char* val + Out: + struct st_replace_regex* r + r->buf points at the resulting buffer + r->even_buf and r->odd_buf might have been reallocated + r->even_buf_len and r->odd_buf_len might have been changed + + TODO: at some point figure out if there is a way to do everything + in one pass +*/ + +static int multi_reg_replace(struct st_replace_regex* r,char* val) +{ + uint i; + char* in_buf, *out_buf; + int* buf_len_p; + + in_buf= val; + out_buf= r->even_buf; + buf_len_p= &r->even_buf_len; + r->buf= 0; + + /* For each substitution, do the replace */ + for (i= 0; i < r->regex_arr.elements; i++) + { + struct st_regex re; + char* save_out_buf= out_buf; + + get_dynamic(&r->regex_arr,(gptr)&re,i); + + if (!reg_replace(&out_buf, buf_len_p, re.pattern, re.replace, + in_buf, re.icase)) + { + /* if the buffer has been reallocated, make adjustements */ + if (save_out_buf != out_buf) + { + if (save_out_buf == r->even_buf) + r->even_buf= out_buf; + else + r->odd_buf= out_buf; + } + + r->buf= out_buf; + if (in_buf == val) + in_buf= r->odd_buf; + + swap_variables(char*,in_buf,out_buf); + + buf_len_p= (out_buf == r->even_buf) ? &r->even_buf_len : + &r->odd_buf_len; + } + } + + return (r->buf == 0); +} + +/* + Parse the regular expression to be used in all result files + from now on. + + The syntax is --replace_regex /from/to/i /from/to/i ... + i means case-insensitive match. If omitted, the match is + case-sensitive + +*/ +static void get_replace_regex(struct st_query *q) +{ + char *expr= q->first_argument; + free_replace_regex(); + if (!(glob_replace_regex=init_replace_regex(expr))) + die("Could not init replace_regex"); + q->last_argument= q->end; +} + /* Get arguments for replace. The syntax is: @@ -1951,6 +2280,18 @@ static void get_replace(struct st_query *q) DBUG_VOID_RETURN; } +static void free_replace_regex() +{ + if (glob_replace_regex) + { + my_free(glob_replace_regex->even_buf,MYF(MY_ALLOW_ZERO_PTR)); + my_free(glob_replace_regex->odd_buf,MYF(MY_ALLOW_ZERO_PTR)); + my_free((char*) glob_replace_regex,MYF(0)); + glob_replace_regex=0; + } +} + + void free_replace() { DBUG_ENTER("free_replace"); @@ -2095,19 +2436,6 @@ char* safe_get_param(char *str, char** arg, const char *msg) DBUG_RETURN(str); } -#ifndef EMBEDDED_LIBRARY -void init_manager() -{ - if (!(manager=mysql_manager_init(0))) - die("Failed in mysql_manager_init()"); - if (!mysql_manager_connect(manager,manager_host,manager_user, - manager_pass,manager_port)) - die("Could not connect to MySQL manager: %s(%d)",manager->last_error, - manager->last_errno); - -} -#endif - /* Connect to a server doing several retries if needed. @@ -2143,7 +2471,7 @@ int safe_connect(MYSQL* mysql, const char *host, const char *user, { int con_error= 1; my_bool reconnect= 1; - static int connection_retry_sleep= 2; /* Seconds */ + static ulong connection_retry_sleep= 100000; /* Microseconds */ int i; for (i= 0; i < opt_max_connect_retries; i++) { @@ -2153,7 +2481,7 @@ int safe_connect(MYSQL* mysql, const char *host, const char *user, con_error= 0; break; } - sleep(connection_retry_sleep); + my_sleep(connection_retry_sleep); } /* TODO: change this to 0 in future versions, but the 'kill' test relies on @@ -2245,6 +2573,7 @@ int connect_n_handle_errors(struct st_query *q, MYSQL* con, const char* host, err: free_replace(); + free_replace_regex(); return error; } @@ -2391,7 +2720,7 @@ int do_connect(struct st_query *q) } #endif if (con_sock && !free_con_sock && *con_sock && *con_sock != FN_LIBCHAR) - con_sock=fn_format(buff, con_sock, TMPDIR, "",0); + con_sock=fn_format(buff, con_sock, TMPDIR, "", 0); if (!con_db[0]) con_db= db; /* Special database to allow one to connect without a database name */ @@ -2906,24 +3235,16 @@ static struct my_option my_long_options[] = #endif {"host", 'h', "Connect to host.", (gptr*) &host, (gptr*) &host, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"manager-host", OPT_MANAGER_HOST, "Undocumented: Used for debugging.", - (gptr*) &manager_host, (gptr*) &manager_host, 0, GET_STR, REQUIRED_ARG, - 0, 0, 0, 0, 0, 0}, - {"manager-password", OPT_MANAGER_PASSWD, "Undocumented: Used for debugging.", - 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"manager-port", OPT_MANAGER_PORT, "Undocumented: Used for debugging.", - (gptr*) &manager_port, (gptr*) &manager_port, 0, GET_INT, REQUIRED_ARG, - MYSQL_MANAGER_PORT, 0, 0, 0, 0, 0}, - {"manager-user", OPT_MANAGER_USER, "Undocumented: Used for debugging.", - (gptr*) &manager_user, (gptr*) &manager_user, 0, GET_STR, REQUIRED_ARG, 0, - 0, 0, 0, 0, 0}, - {"manager-wait-timeout", OPT_MANAGER_WAIT_TIMEOUT, - "Undocumented: Used for debugging.", (gptr*) &manager_wait_timeout, - (gptr*) &manager_wait_timeout, 0, GET_INT, REQUIRED_ARG, 3, 0, 0, 0, 0, 0}, + {"include", 'i', "Include SQL before each test case.", (gptr*) &opt_include, + (gptr*) &opt_include, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"mark-progress", OPT_MARK_PROGRESS, + "Write linenumber and elapsed time to <testname>.progress ", + (gptr*) &opt_mark_progress, (gptr*) &opt_mark_progress, 0, + GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"max-connect-retries", OPT_MAX_CONNECT_RETRIES, "Max number of connection attempts when connecting to server", (gptr*) &opt_max_connect_retries, (gptr*) &opt_max_connect_retries, 0, - GET_INT, REQUIRED_ARG, 5, 1, 10, 0, 0, 0}, + GET_INT, REQUIRED_ARG, 500, 1, 10000, 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}, {"port", 'P', "Port number to use for connection.", (gptr*) &port, @@ -3012,11 +3333,6 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), case 'r': record = 1; break; - case (int)OPT_MANAGER_PASSWD: - my_free(manager_pass,MYF(MY_ALLOW_ZERO_PTR)); - manager_pass=my_strdup(argument, MYF(MY_FAE)); - while (*argument) *argument++= 'x'; /* Destroy argument */ - break; case 'x': { char buff[FN_REFLEN]; @@ -3025,7 +3341,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), strxmov(buff, opt_basedir, argument, NullS); argument= buff; } - fn_format(buff, argument, "", "", 4); + fn_format(buff, argument, "", "", MY_UNPACK_FILENAME); DBUG_ASSERT(cur_file == file_stack && cur_file->file == 0); if (!(cur_file->file= my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(0)))) @@ -3042,7 +3358,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), strxmov(buff, opt_basedir, argument, NullS); argument= buff; } - fn_format(buff, argument, "", "", 4); + fn_format(buff, argument, "", "", MY_UNPACK_FILENAME); timer_file= buff; unlink(timer_file); /* Ignore error, may not exist */ break; @@ -3136,7 +3452,7 @@ static void str_to_file(const char *fname, char *str, int size) strxmov(buff, opt_basedir, fname, NullS); fname= buff; } - fn_format(buff,fname,"","",4); + fn_format(buff, fname, "", "", MY_UNPACK_FILENAME); if ((fd= my_open(buff, O_WRONLY | O_CREAT | O_TRUNC, MYF(MY_WME | MY_FFNF))) < 0) @@ -3150,13 +3466,233 @@ static void str_to_file(const char *fname, char *str, int size) void dump_result_to_reject_file(const char *record_file, char *buf, int size) { char reject_file[FN_REFLEN]; - str_to_file(fn_format(reject_file, record_file,"",".reject",2), buf, size); + str_to_file(fn_format(reject_file, record_file, "", ".reject", + MY_REPLACE_EXT), + buf, size); } void dump_result_to_log_file(const char *record_file, char *buf, int size) { char log_file[FN_REFLEN]; - str_to_file(fn_format(log_file, record_file,"",".log",2), buf, size); + str_to_file(fn_format(log_file, record_file, "", ".log", + MY_REPLACE_EXT), + buf, size); +} + +void dump_progress(const char *record_file) +{ + char log_file[FN_REFLEN]; + str_to_file(fn_format(log_file, record_file, "", ".progress", + MY_REPLACE_EXT), + ds_progress.str, ds_progress.length); +} + +static void check_regerr(my_regex_t* r, int err) +{ + char err_buf[1024]; + + if (err) + { + my_regerror(err,r,err_buf,sizeof(err_buf)); + die("Regex error: %s\n", err_buf); + } +} + +/* + auxiluary macro used by reg_replace + makes sure the result buffer has sufficient length +*/ +#define SECURE_REG_BUF if (buf_len < need_buf_len)\ + {\ + int off= res_p - buf;\ + buf= (char*)my_realloc(buf,need_buf_len,MYF(MY_WME+MY_FAE));\ + res_p= buf + off;\ + buf_len= need_buf_len;\ + }\ + +/* + Performs a regex substitution + + IN: + + buf_p - result buffer pointer. Will change if reallocated + buf_len_p - result buffer length. Will change if the buffer is reallocated + pattern - regexp pattern to match + replace - replacement expression + string - the string to perform substituions in + icase - flag, if set to 1 the match is case insensitive + */ +static int reg_replace(char** buf_p, int* buf_len_p, char *pattern, + char *replace, char *string, int icase) +{ + my_regex_t r; + my_regmatch_t* subs; + char* buf_end, *replace_end; + char* buf= *buf_p; + int len; + int buf_len,need_buf_len; + int cflags= REG_EXTENDED; + int err_code; + char* res_p,*str_p,*str_end; + + buf_len= *buf_len_p; + len= strlen(string); + str_end= string + len; + + /* start with a buffer of a reasonable size that hopefully will not + need to be reallocated + */ + need_buf_len= len * 2 + 1; + res_p= buf; + + SECURE_REG_BUF + + buf_end = buf + buf_len; + + if (icase) + cflags |= REG_ICASE; + + if ((err_code=my_regcomp(&r,pattern,cflags,&my_charset_latin1))) + { + check_regerr(&r,err_code); + return 1; + } + + subs= (my_regmatch_t*)my_malloc(sizeof(my_regmatch_t) * (r.re_nsub+1), + MYF(MY_WME+MY_FAE)); + + *res_p= 0; + str_p= string; + replace_end= replace + strlen(replace); + + /* for each pattern match instance perform a replacement */ + while (!err_code) + { + /* find the match */ + err_code= my_regexec(&r,str_p, r.re_nsub+1, subs, + (str_p == string) ? REG_NOTBOL : 0); + + /* if regular expression error (eg. bad syntax, or out of memory) */ + if (err_code && err_code != REG_NOMATCH) + { + check_regerr(&r,err_code); + my_regfree(&r); + return 1; + } + + /* if match found */ + if (!err_code) + { + char* expr_p= replace; + int c; + + /* + we need at least what we have so far in the buffer + the part + before this match + */ + need_buf_len= (res_p - buf) + subs[0].rm_so; + + /* on this pass, calculate the memory for the result buffer */ + while (expr_p < replace_end) + { + int back_ref_num= -1; + c= *expr_p; + + if (c == '\\' && expr_p + 1 < replace_end) + { + back_ref_num= expr_p[1] - '0'; + } + + /* found a valid back_ref (eg. \1)*/ + if (back_ref_num >= 0 && back_ref_num <= (int)r.re_nsub) + { + int start_off,end_off; + if ((start_off=subs[back_ref_num].rm_so) > -1 && + (end_off=subs[back_ref_num].rm_eo) > -1) + { + need_buf_len += (end_off - start_off); + } + expr_p += 2; + } + else + { + expr_p++; + need_buf_len++; + } + } + need_buf_len++; + /* + now that we know the size of the buffer, + make sure it is big enough + */ + SECURE_REG_BUF + + /* copy the pre-match part */ + if (subs[0].rm_so) + { + memcpy(res_p,str_p,subs[0].rm_so); + res_p += subs[0].rm_so; + } + + expr_p= replace; + + /* copy the match and expand back_refs */ + while (expr_p < replace_end) + { + int back_ref_num= -1; + c= *expr_p; + + if (c == '\\' && expr_p + 1 < replace_end) + { + back_ref_num= expr_p[1] - '0'; + } + + if (back_ref_num >= 0 && back_ref_num <= (int)r.re_nsub) + { + int start_off,end_off; + if ((start_off=subs[back_ref_num].rm_so) > -1 && + (end_off=subs[back_ref_num].rm_eo) > -1) + { + int block_len= end_off - start_off; + memcpy(res_p,str_p + start_off, block_len); + res_p += block_len; + } + expr_p += 2; + } + else + { + *res_p++ = *expr_p++; + } + } + + /* handle the post-match part */ + if (subs[0].rm_so == subs[0].rm_eo) + { + if (str_p + subs[0].rm_so >= str_end) + break; + str_p += subs[0].rm_eo ; + *res_p++ = *str_p++; + } + else + { + str_p += subs[0].rm_eo; + } + } + else /* no match this time, just copy the string as is */ + { + int left_in_str= str_end-str_p; + need_buf_len= (res_p-buf) + left_in_str; + SECURE_REG_BUF + memcpy(res_p,str_p,left_in_str); + res_p += left_in_str; + str_p= str_end; + } + } + my_regfree(&r); + *res_p= 0; + *buf_p= buf; + *buf_len_p= buf_len; + return 0; } @@ -3248,6 +3784,7 @@ static void fix_win_paths(const char* val, int len) { const char** pattern= dynamic_element(&patterns, i, const char**); DBUG_PRINT("info", ("pattern: %s", *pattern)); + if (strlen(*pattern) == 0) continue; /* Search for the path in string */ while ((p= strstr(val, *pattern))) { @@ -3275,6 +3812,15 @@ static void replace_dynstr_append_mem(DYNAMIC_STRING *ds, fix_win_paths(val, len); #endif + if (glob_replace_regex) + { + if (!multi_reg_replace(glob_replace_regex, (char*)val)) + { + val= glob_replace_regex->buf; + len= strlen(val); + } + } + if (glob_replace) replace_strings_append(glob_replace, ds, val, len); else @@ -3499,8 +4045,8 @@ static void append_metadata(DYNAMIC_STRING *ds, static void append_info(DYNAMIC_STRING *ds, ulonglong affected_rows, const char *info) { - char buf[40]; - sprintf(buf,"affected rows: %llu\n", affected_rows); + char buf[40], buff2[21]; + sprintf(buf,"affected rows: %s\n", llstr(affected_rows, buff2)); dynstr_append(ds, buf); if (info) { @@ -3698,6 +4244,7 @@ static void run_query_normal(MYSQL *mysql, struct st_query *command, end: free_replace(); + free_replace_regex(); /* We save the return code (mysql_errno(mysql)) from the last call sent @@ -3747,7 +4294,9 @@ static void handle_error(const char *query, struct st_query *q, if (err_errno == CR_SERVER_LOST || err_errno == CR_SERVER_GONE_ERROR) die("require query '%s' failed: %d: %s", query, err_errno, err_error); - abort_not_supported_test(); + + /* Abort the run of this test, pass the failed query as reason */ + abort_not_supported_test("Query '%s' failed, required functionality not supported", query); } if (q->abort_on_error) @@ -4024,7 +4573,8 @@ static void run_query_stmt(MYSQL *mysql, struct st_query *command, end: free_replace(); - + free_replace_regex(); + if (!disable_warnings) { dynstr_free(&ds_prepare_warnings); @@ -4036,6 +4586,7 @@ end: to the server into the mysqltest builtin variable $mysql_errno. This variable then can be used from the test case itself. */ + var_set_errno(mysql_stmt_errno(stmt)); #ifndef BUG15518_FIXED mysql_stmt_close(stmt); @@ -4464,15 +5015,18 @@ static VAR *var_init(VAR *v, const char *name, int name_len, const char *val, if (!(tmp_var->str_val = my_malloc(val_alloc_len+1, MYF(MY_WME)))) die("Out of memory"); - if (name) - strmake(tmp_var->name, name, name_len); + memcpy(tmp_var->name, name, name_len); if (val) - strmake(tmp_var->str_val, val, val_len); + { + memcpy(tmp_var->str_val, val, val_len); + tmp_var->str_val[val_len]= 0; + } tmp_var->name_len = name_len; tmp_var->str_val_len = val_len; tmp_var->alloced_len = val_alloc_len; tmp_var->int_val = (val) ? atoi(val) : 0; tmp_var->int_dirty = 0; + tmp_var->env_s = 0; return tmp_var; } @@ -4514,40 +5068,51 @@ static void init_var_hash(MYSQL *mysql) DBUG_VOID_RETURN; } -static void mark_progress(int line) + +/* + Record how many milliseconds it took to execute the test file + up until the current line and save it in the dynamic string ds_progress. + + The ds_progress will be dumped to <test_name>.progress when + test run completes + +*/ +static void mark_progress(struct st_query* q __attribute__((unused)), int line) { -#ifdef NOT_YET - static FILE* fp = NULL; - static double first; + char buf[32], *end; + ulonglong timer= timer_now(); + if (!progress_start) + progress_start= timer; + timer-= progress_start; - struct timeval tv; - double now; + /* Milliseconds since start */ + end= longlong2str(timer, buf, 10); + dynstr_append_mem(&ds_progress, buf, (int)(end-buf)); + dynstr_append_mem(&ds_progress, "\t", 1); - if (!fp) - { + /* Parser line number */ + end= int10_to_str(line, buf, 10); + dynstr_append_mem(&ds_progress, buf, (int)(end-buf)); + dynstr_append_mem(&ds_progress, "\t", 1); - fp = fopen("/tmp/mysqltest_progress.log", "wt"); + /* Filename */ + dynstr_append(&ds_progress, cur_file->file_name); + dynstr_append_mem(&ds_progress, ":", 1); - if (!fp) - { - abort(); - } + /* Line in file */ + end= int10_to_str(cur_file->lineno, buf, 10); + dynstr_append_mem(&ds_progress, buf, (int)(end-buf)); - gettimeofday(&tv, NULL); - first = tv.tv_sec * 1e6 + tv.tv_usec; - } - gettimeofday(&tv, NULL); - now = tv.tv_sec * 1e6 + tv.tv_usec; + dynstr_append_mem(&ds_progress, "\n", 1); - fprintf(fp, "%d %f\n", parser.current_line, (now - first) / 1e6); -#endif } + int main(int argc, char **argv) { struct st_query *q; - my_bool require_file=0, q_send_flag=0, abort_flag= 0, + my_bool require_file=0, q_send_flag=0, query_executed= 0; char save_file[FN_REFLEN]; MY_STAT res_info; @@ -4583,6 +5148,7 @@ int main(int argc, char **argv) memset(&master_pos, 0, sizeof(master_pos)); init_dynamic_string(&ds_res, "", 0, 65536); + init_dynamic_string(&ds_progress, "", 0, 2048); parse_args(argc, argv); DBUG_PRINT("info",("result_file: '%s'", result_file ? result_file : "")); @@ -4596,10 +5162,6 @@ int main(int argc, char **argv) cur_file->file_name= my_strdup("<stdin>", MYF(MY_WME)); cur_file->lineno= 1; } -#ifndef EMBEDDED_LIBRARY - if (manager_host) - init_manager(); -#endif init_re(); ps_protocol_enabled= ps_protocol; sp_protocol_enabled= sp_protocol; @@ -4648,7 +5210,12 @@ int main(int argc, char **argv) */ var_set_errno(-1); - while (!abort_flag && !read_query(&q)) + if (opt_include) + { + open_file(opt_include); + } + + while (!read_query(&q)) { int current_line_inc = 1, processed = 0; if (q->type == Q_UNKNOWN || q->type == Q_COMMENT_WITH_COMMAND) @@ -4686,11 +5253,6 @@ int main(int argc, char **argv) case Q_SLEEP: do_sleep(q, 0); break; case Q_REAL_SLEEP: do_sleep(q, 1); break; case Q_WAIT_FOR_SLAVE_TO_STOP: do_wait_for_slave_to_stop(q); break; - case Q_REQUIRE_MANAGER: do_require_manager(q); break; -#ifndef EMBEDDED_LIBRARY - case Q_SERVER_START: do_server_start(q); break; - case Q_SERVER_STOP: do_server_stop(q); break; -#endif case Q_INC: do_modify_var(q, DO_INC); break; case Q_DEC: do_modify_var(q, DO_DEC); break; case Q_ECHO: do_echo(q); query_executed= 1; break; @@ -4757,6 +5319,14 @@ int main(int argc, char **argv) q->require_file=require_file; save_file[0]=0; } + /* + To force something being sent as a query to the mysqld one can + use the prefix "query". Remove "query" from string before executing + */ + if (strncmp(q->query, "query ", 6) == 0) + { + q->query= q->first_argument; + } run_query(&cur_con->mysql, q, flags); query_executed= 1; q->last_argument= q->end; @@ -4796,6 +5366,10 @@ int main(int argc, char **argv) case Q_REPLACE: get_replace(q); break; + case Q_REPLACE_REGEX: + get_replace_regex(q); + break; + case Q_REPLACE_COLUMN: get_replace_column(q); break; @@ -4864,8 +5438,8 @@ int main(int argc, char **argv) parsing_disabled--; break; - case Q_EXIT: - abort_flag= 1; + case Q_DIE: + die("%s", q->first_argument); break; default: @@ -4898,7 +5472,8 @@ int main(int argc, char **argv) } parser.current_line += current_line_inc; - mark_progress(parser.current_line); + if ( opt_mark_progress ) + mark_progress(q, parser.current_line); } start_lineno= 0; @@ -4949,9 +5524,9 @@ int main(int argc, char **argv) die("No queries executed but result file found!"); } - - dynstr_free(&ds_res); - + if ( opt_mark_progress ) + dump_progress(result_file); + dynstr_free(&ds_progress); if (!got_end_timer) timer_output(); /* No end_timer cmd, end it */ free_used_memory(); @@ -4981,7 +5556,7 @@ static int read_server_arguments(const char *name) strxmov(buff, opt_basedir, name, NullS); name=buff; } - fn_format(buff,name,"","",4); + fn_format(buff, name, "", "", MY_UNPACK_FILENAME); if (!embedded_server_arg_count) { |