diff options
Diffstat (limited to 'client')
-rw-r--r-- | client/CMakeLists.txt | 80 | ||||
-rw-r--r-- | client/Makefile.am | 59 | ||||
-rw-r--r-- | client/client_priv.h | 13 | ||||
-rw-r--r-- | client/client_priv.h.rej | 15 | ||||
-rw-r--r-- | client/mysql.cc | 108 | ||||
-rw-r--r-- | client/mysqlbinlog.cc | 77 | ||||
-rw-r--r-- | client/mysqlcheck.c | 64 | ||||
-rw-r--r-- | client/mysqldump.c | 309 | ||||
-rw-r--r-- | client/mysqlimport.c | 231 | ||||
-rw-r--r-- | client/mysqlmanager-pwgen.c | 161 | ||||
-rw-r--r-- | client/mysqlmanagerc.c | 174 | ||||
-rw-r--r-- | client/mysqlslap.c | 1343 | ||||
-rw-r--r-- | client/mysqltest.c | 659 |
13 files changed, 2769 insertions, 524 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 9d133125a0d..aa78f825c87 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) $(yassl_includes) + $(openssl_includes) $(yassl_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 $(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,19 +77,20 @@ 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 \ rm -f $$f; \ @LN_CP_F@ $(top_srcdir)/sql/$$f $$f; \ - done; \ + done; \ for f in $(strings_src) ; do \ rm -f $(srcdir)/$$f; \ @LN_CP_F@ $(top_srcdir)/strings/$$f $$f; \ - done; \ - rm -f $(srcdir)/my_user.c; \ - @LN_CP_F@ $(top_srcdir)/sql-common/my_user.c my_user.c; - + done; \ + rm -f $(srcdir)/my_user.c; \ + @LN_CP_F@ $(top_srcdir)/sql-common/my_user.c my_user.c + # Don't update the files from bitkeeper %::SCCS/s.% 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/client_priv.h.rej b/client/client_priv.h.rej new file mode 100644 index 00000000000..ac3818bb1e1 --- /dev/null +++ b/client/client_priv.h.rej @@ -0,0 +1,15 @@ +*************** +*** 50,55 **** + OPT_NDBCLUSTER, OPT_NDB_CONNECTSTRING, + #endif + OPT_TRIGGERS, + OPT_IGNORE_TABLE,OPT_INSERT_IGNORE,OPT_SHOW_WARNINGS,OPT_DROP_DATABASE, +! OPT_TZ_UTC, OPT_AUTO_CLOSE + }; +--- 50,55 ---- + OPT_NDBCLUSTER, OPT_NDB_CONNECTSTRING, + #endif + OPT_TRIGGERS, + OPT_IGNORE_TABLE,OPT_INSERT_IGNORE,OPT_SHOW_WARNINGS,OPT_DROP_DATABASE, +! OPT_TZ_UTC, OPT_AUTO_CLOSE, OPT_SSL_VERIFY_SERVER_CERT + }; diff --git a/client/mysql.cc b/client/mysql.cc index 900df825b25..8b121579cb0 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 handle_sigint(int sig); int main(int argc,char *argv[]) { @@ -420,7 +420,7 @@ 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, handle_sigint); // Catch SIGINT to clean up signal(SIGQUIT, mysql_end); // Catch SIGQUIT to clean up /* @@ -531,6 +531,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, @@ -970,7 +999,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 @@ -1007,7 +1036,7 @@ static int read_and_execute(bool interactive) if (opt_outfile && glob_buffer.is_empty()) fflush(OUTFILE); -#if defined( __WIN__) || defined(OS2) || defined(__NETWARE__) +#if defined( __WIN__) || defined(__NETWARE__) tee_fputs(prompt, stdout); #if defined(__NETWARE__) line=fgets(linebuffer, sizeof(linebuffer)-1, stdin); @@ -1018,7 +1047,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); @@ -1034,32 +1063,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 @@ -1111,7 +1120,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__) @@ -1960,6 +1969,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 @@ -1995,7 +2005,7 @@ com_go(String *buffer,char *line __attribute__((unused))) } timer=start_timer(); - + executing_query= 1; error= mysql_real_query_for_lazy(buffer->ptr(),buffer->length()); #ifdef HAVE_READLINE @@ -2009,6 +2019,7 @@ com_go(String *buffer,char *line __attribute__((unused))) if (error) { + executing_query= 0; buffer->length(0); // Remove query on error return error; } @@ -2020,13 +2031,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) @@ -2101,6 +2118,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 */ } @@ -2314,6 +2332,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); @@ -2421,6 +2441,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++) @@ -2450,6 +2472,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++) @@ -2484,6 +2508,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); @@ -3434,9 +3460,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) @@ -3452,9 +3475,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); } @@ -3465,9 +3485,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); @@ -3478,14 +3495,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> @@ -3497,7 +3511,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; diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc index b1a931a9df1..490a74e6035 100644 --- a/client/mysqlbinlog.cc +++ b/client/mysqlbinlog.cc @@ -63,6 +63,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; @@ -516,6 +517,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)) { @@ -530,11 +534,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: { @@ -554,7 +566,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) @@ -645,11 +663,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 @@ -659,13 +684,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 " @@ -676,13 +701,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.", @@ -698,15 +724,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}, {"short-form", 's', "Just show the queries, no extra info.", (gptr*) &short_form, (gptr*) &short_form, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, @@ -721,6 +747,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 " @@ -729,13 +762,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.", @@ -751,11 +777,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} }; @@ -1470,7 +1497,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..db7ae062203 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; @@ -502,6 +519,43 @@ static int process_all_tables_in_db(char *database) } /* 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 +600,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 ee6d7b9d12b..58cd2342bd3 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 */ @@ -94,7 +95,10 @@ 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 my_bool insert_pat_inited=0; @@ -131,7 +135,6 @@ static CHARSET_INFO *charset_info= &my_charset_latin1; const char *default_dbug_option="d:t:o,/tmp/mysqldump.trace"; /* do we met VIEWs during tables scaning */ my_bool was_views= 0; - const char *compatible_mode_names[]= { "MYSQL323", "MYSQL40", "POSTGRESQL", "ORACLE", "MSSQL", "DB2", @@ -162,6 +165,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 +233,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, @@ -341,6 +351,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}, @@ -1232,9 +1245,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(sock, 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(sock, "LOCK TABLES mysql.event READ"); + + if (mysql_query_with_error_report(sock, &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(sock, &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(sock, 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 +1387,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; @@ -1288,9 +1428,9 @@ 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); @@ -1403,7 +1543,7 @@ static uint get_table_structure(char *table, char *db, char *table_type, 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 table_buff2[NAME_LEN*2+3], query_buff[QUERY_LENGTH]; FILE *sql_file = md_result_file; int len; MYSQL_RES *result; @@ -1581,7 +1721,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); @@ -1654,7 +1797,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); @@ -1873,7 +2019,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; @@ -2525,6 +2671,130 @@ 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(sock, &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(sock, &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; @@ -2669,6 +2939,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) @@ -2706,6 +2978,12 @@ static int dump_all_tables_in_db(char *database) dump_triggers_for_table(table, database); } } + if (opt_events && !opt_xml && + mysql_get_server_version(sock) >= 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) { @@ -2826,7 +3104,7 @@ static int get_actual_table_name(const char *old_table_name, } mysql_free_result(table_res); } - return retval; + DBUG_RETURN(retval); } @@ -2841,7 +3119,7 @@ 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 hash table for storing the actual name of tables to dump */ if (hash_init(&dump_tables, charset_info, 16, 0, 0, @@ -2916,6 +3194,12 @@ static int dump_selected_tables(char *db, char **table_names, int tables) get_view_structure(table_name, db); } } + if (opt_events && !opt_xml && + mysql_get_server_version(sock) >= 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) @@ -3524,6 +3808,9 @@ int main(int argc, char **argv) if (opt_single_transaction && do_unlock_tables(sock)) /* 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..ccd6932e25b 100644 --- a/client/mysqlimport.c +++ b/client/mysqlimport.c @@ -29,6 +29,16 @@ #include "client_priv.h" #include "mysql_version.h" +#ifdef HAVE_LIBPTHREAD +#include <my_pthread.h> +#endif + + +/* Global Thread counter */ +int counter= 0; +#ifdef HAVE_LIBPTHREAD +pthread_mutex_t counter_mutex; +#endif static void db_error_with_table(MYSQL *mysql, char *table); static void db_error(MYSQL *mysql); @@ -39,8 +49,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 +118,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 +149,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 +286,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 +294,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 +303,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 +349,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 +367,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 +382,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 +457,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 +466,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 +520,54 @@ 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, "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_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 +578,81 @@ 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 */ + VOID(pthread_mutex_init(&counter_mutex, NULL)); + + for (; *argv != NULL; argv++) /* Loop through tables */ + { + /* + If we hit thread count limit we loop until some threads exit. + We sleep for a second, so that we don't chew up a lot of + CPU in the loop. + */ +sanity_label: + if (counter == opt_use_threads) + { + sleep(1); + goto sanity_label; + } + pthread_mutex_lock(&counter_mutex); + counter++; + pthread_mutex_unlock(&counter_mutex); + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, + PTHREAD_CREATE_DETACHED); + + /* 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. + */ +loop_label: + if (counter) + { + sleep(1); + goto loop_label; + } + VOID(pthread_mutex_destroy(&counter_mutex)); } + 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, "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/mysqlslap.c b/client/mysqlslap.c new file mode 100644 index 00000000000..11d3ae5a2df --- /dev/null +++ b/client/mysqlslap.c @@ -0,0 +1,1343 @@ +/* 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 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 client_flag= 0; + 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); + + client_flag|= CLIENT_MULTI_RESULTS; + if (!opt_only_print) + { + if (!(mysql_real_connect(&mysql, host, user, opt_password, + NULL, opt_mysql_port, + opt_mysql_unix_port, client_flag))) + { + 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) + { + if (!(mysql_real_connect(mysql, host, user, opt_password, + create_schema_string, + opt_mysql_port, + opt_mysql_unix_port, + 0))) + { + fprintf(stderr,"%s: %s\n",my_progname,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 fbbab4a0b3b..774cbac6e3f 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -148,6 +148,7 @@ 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; @@ -179,6 +180,35 @@ static test_file* cur_file; static test_file* file_stack_end; uint start_lineno; /* 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; static uint delimiter_length= 1; @@ -218,6 +248,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 +298,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]; @@ -315,6 +349,7 @@ Q_EXIT, Q_DISABLE_RECONNECT, Q_ENABLE_RECONNECT, Q_IF, Q_DISABLE_PARSING, Q_ENABLE_PARSING, +Q_REPLACE_REGEX, Q_UNKNOWN, /* Unknown command. */ Q_COMMENT, /* Comments, ignored. */ @@ -403,6 +438,7 @@ const char *command_names[]= "if", "disable_parsing", "enable_parsing", + "replace_regex", 0 }; @@ -442,6 +478,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); @@ -644,10 +681,11 @@ static void die(const char *fmt, ...) /* 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 *fname) { DBUG_ENTER("abort_not_supported_test"); - fprintf(stderr, "This test is not supported by this installation\n"); + fprintf(stderr, "The test '%s' is not supported by this installation\n", + fname); if (!silent) printf("skipped\n"); free_used_memory(); @@ -770,7 +808,7 @@ static void check_result(DYNAMIC_STRING* ds, const char *fname, DBUG_ENTER("check_result"); if (res && require_option) - abort_not_supported_test(); + abort_not_supported_test(fname); switch (res) { case RESULT_OK: break; /* ok */ @@ -855,10 +893,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 +909,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 +921,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); } @@ -969,7 +1030,7 @@ int do_wait_for_slave_to_stop(struct st_query *q __attribute__((unused))) int do_require_manager(struct st_query *query __attribute__((unused)) ) { if (!manager) - abort_not_supported_test(); + abort_not_supported_test("manager"); return 0; } @@ -1211,6 +1272,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 +1299,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 +1337,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 +1514,7 @@ void do_system(struct st_query *command) } command->last_argument= command->end; + dynstr_free(&ds_cmd); DBUG_VOID_RETURN; } @@ -1490,7 +1555,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 +1592,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 +1622,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 +1637,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 +1776,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 +1916,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(charset_name); } static uint get_errcodes(match_err *to,struct st_query *q) @@ -1794,7 +1953,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 +2065,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 +2314,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"); @@ -2245,6 +2620,7 @@ int connect_n_handle_errors(struct st_query *q, MYSQL* con, const char* host, err: free_replace(); + free_replace_regex(); return error; } @@ -2903,6 +3279,8 @@ 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}, + {"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}, {"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}, @@ -3156,6 +3534,214 @@ void dump_result_to_log_file(const char *record_file, char *buf, int size) str_to_file(fn_format(log_file, record_file,"",".log",2), buf, size); } +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; +} + #ifdef __WIN__ @@ -3245,6 +3831,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))) { @@ -3272,6 +3859,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 @@ -3695,6 +4291,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 @@ -3744,7 +4341,7 @@ 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_not_supported_test("failed_query"); } if (q->abort_on_error) @@ -4021,7 +4618,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); @@ -4033,6 +4631,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); @@ -4461,15 +5060,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; } @@ -4511,7 +5113,7 @@ static void init_var_hash(MYSQL *mysql) DBUG_VOID_RETURN; } -static void mark_progress(int line) +static void mark_progress(int line __attribute__((unused))) { #ifdef NOT_YET static FILE* fp = NULL; @@ -4645,6 +5247,11 @@ int main(int argc, char **argv) */ var_set_errno(-1); + if (opt_include) + { + open_file(opt_include); + } + while (!abort_flag && !read_query(&q)) { int current_line_inc = 1, processed = 0; @@ -4793,6 +5400,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; |