diff options
Diffstat (limited to 'client')
-rw-r--r-- | client/Makefile.am | 30 | ||||
-rw-r--r-- | client/client_priv.h | 40 | ||||
-rw-r--r-- | client/completion_hash.cc | 9 | ||||
-rw-r--r-- | client/completion_hash.h | 4 | ||||
-rw-r--r-- | client/connect_test.c | 65 | ||||
-rw-r--r-- | client/get_password.c | 1 | ||||
-rw-r--r-- | client/insert_test.c | 59 | ||||
-rw-r--r-- | client/list_test.c | 70 | ||||
-rw-r--r-- | client/my_readline.h | 2 | ||||
-rw-r--r-- | client/mysql.cc | 1252 | ||||
-rw-r--r-- | client/mysqladmin.cc (renamed from client/mysqladmin.c) | 250 | ||||
-rw-r--r-- | client/mysqlbinlog.cc | 478 | ||||
-rw-r--r-- | client/mysqlcheck.c | 80 | ||||
-rw-r--r-- | client/mysqldump.c | 1802 | ||||
-rw-r--r-- | client/mysqlimport.c | 60 | ||||
-rw-r--r-- | client/mysqlmanager-pwgen.c | 8 | ||||
-rw-r--r-- | client/mysqlshow.c | 57 | ||||
-rw-r--r-- | client/mysqltest.c | 1727 | ||||
-rw-r--r-- | client/readline.cc | 40 | ||||
-rw-r--r-- | client/select_test.c | 73 | ||||
-rw-r--r-- | client/showdb_test.c | 66 | ||||
-rw-r--r-- | client/sql_string.cc | 462 | ||||
-rw-r--r-- | client/sql_string.h | 132 | ||||
-rw-r--r-- | client/ssl_test.c | 81 | ||||
-rw-r--r-- | client/thimble.cc | 107 | ||||
-rw-r--r-- | client/thread_test.c | 251 |
26 files changed, 4740 insertions, 2466 deletions
diff --git a/client/Makefile.am b/client/Makefile.am index 9c994814714..1e8851fb3b9 100644 --- a/client/Makefile.am +++ b/client/Makefile.am @@ -16,33 +16,24 @@ # This file is public domain and comes with NO WARRANTY of any kind -INCLUDES = -I$(srcdir)/../include \ - -I../include -I$(srcdir)/.. -I$(top_srcdir) \ - -I.. $(openssl_includes) +#AUTOMAKE_OPTIONS = nostdinc +INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \ + -I$(top_srcdir)/regex $(openssl_includes) LIBS = @CLIENT_LIBS@ -LDADD = @CLIENT_EXTRA_LDFLAGS@ ../libmysql/libmysqlclient.la +LDADD= @CLIENT_EXTRA_LDFLAGS@ \ + $(top_builddir)/libmysql/libmysqlclient.la bin_PROGRAMS = mysql mysqladmin mysqlcheck mysqlshow \ mysqldump mysqlimport mysqltest mysqlbinlog mysqlmanagerc mysqlmanager-pwgen -noinst_PROGRAMS = insert_test select_test thread_test 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) mysqlbinlog_LDADD = $(LDADD) $(CXXLDFLAGS) -mysql_DEPENDENCIES= $(LIBRARIES) $(pkglib_LTLIBRARIES) -mysqladmin_DEPENDENCIES= $(LIBRARIES) $(pkglib_LTLIBRARIES) -mysqlcheck_DEPENDENCIES= $(LIBRARIES) $(pkglib_LTLIBRARIES) -mysqlshow_DEPENDENCIES= $(LIBRARIES) $(pkglib_LTLIBRARIES) -mysqldump_DEPENDENCIES= $(LIBRARIES) $(pkglib_LTLIBRARIES) -mysqlimport_DEPENDENCIES= $(LIBRARIES) $(pkglib_LTLIBRARIES) -insert_test_DEPENDENCIES= $(LIBRARIES) $(pkglib_LTLIBRARIES) -select_test_DEPENDENCIES= $(LIBRARIES) $(pkglib_LTLIBRARIES) -mysqltest_SOURCES= mysqltest.c -mysqltest_DEPENDENCIES= $(LIBRARIES) $(pkglib_LTLIBRARIES) -mysqlbinlog_SOURCES = mysqlbinlog.cc -mysqlbinlog_DEPENDENCIES= $(LIBRARIES) $(pkglib_LTLIBRARIES) +mysqltest_SOURCES= mysqltest.c $(top_srcdir)/mysys/my_getsystime.c +mysqltest_LDADD = $(top_builddir)/regex/libregex.a $(LDADD) +mysqlbinlog_SOURCES = mysqlbinlog.cc ../mysys/mf_tempdir.c mysqlmanagerc_SOURCES = mysqlmanagerc.c -mysqlmanagerc_DEPENDENCIES= $(LIBRARIES) $(pkglib_LTLIBRARIES) sql_src=log_event.h log_event.cc # Fix for mit-threads @@ -54,8 +45,5 @@ link_sources: @LN_CP_F@ $(top_srcdir)/sql/$$f $(srcdir)/$$f; \ done; -thread_test.o: thread_test.c - $(COMPILE) -c @MT_INCLUDES@ $(INCLUDES) $< - # Don't update the files from bitkeeper %::SCCS/s.% diff --git a/client/client_priv.h b/client/client_priv.h index 3e4e4e0971d..37ed407de68 100644 --- a/client/client_priv.h +++ b/client/client_priv.h @@ -26,17 +26,29 @@ /* We have to define 'enum options' identical in all files to keep OS2 happy */ -enum options_client { OPT_CHARSETS_DIR=256, OPT_DEFAULT_CHARSET, - OPT_PAGER, OPT_NOPAGER, OPT_TEE, OPT_NOTEE, - OPT_LOW_PRIORITY, OPT_AUTO_REPAIR, OPT_COMPRESS, - OPT_DROP, OPT_LOCKS, OPT_KEYWORDS, OPT_DELAYED, OPT_OPTIMIZE, - OPT_FTB, OPT_LTB, OPT_ENC, OPT_O_ENC, OPT_ESC, OPT_TABLES, - OPT_MASTER_DATA, OPT_AUTOCOMMIT, OPT_AUTO_REHASH, - OPT_LINE_NUMBERS, OPT_COLUMN_NAMES, OPT_CONNECT_TIMEOUT, - OPT_MAX_ALLOWED_PACKET, OPT_NET_BUFFER_LENGTH, - OPT_SELECT_LIMIT, OPT_MAX_JOIN_SIZE, OPT_SSL_SSL, - OPT_SSL_KEY, OPT_SSL_CERT, OPT_SSL_CA, OPT_SSL_CAPATH, - OPT_SSL_CIPHER, OPT_SHUTDOWN_TIMEOUT, OPT_LOCAL_INFILE, - OPT_DELETE_MASTER_LOGS, - OPT_PROMPT, OPT_IGN_LINES,OPT_TRANSACTION, OPT_FRM, - OPT_HEXBLOB, OPT_AUTO_CLOSE }; +enum options_client +{ + OPT_CHARSETS_DIR=256, OPT_DEFAULT_CHARSET, + OPT_PAGER, OPT_NOPAGER, OPT_TEE, OPT_NOTEE, + OPT_LOW_PRIORITY, OPT_AUTO_REPAIR, OPT_COMPRESS, + OPT_DROP, OPT_LOCKS, OPT_KEYWORDS, OPT_DELAYED, OPT_OPTIMIZE, + OPT_FTB, OPT_LTB, OPT_ENC, OPT_O_ENC, OPT_ESC, OPT_TABLES, + OPT_MASTER_DATA, OPT_AUTOCOMMIT, OPT_AUTO_REHASH, + OPT_LINE_NUMBERS, OPT_COLUMN_NAMES, OPT_CONNECT_TIMEOUT, + OPT_MAX_ALLOWED_PACKET, OPT_NET_BUFFER_LENGTH, + OPT_SELECT_LIMIT, OPT_MAX_JOIN_SIZE, OPT_SSL_SSL, + OPT_SSL_KEY, OPT_SSL_CERT, OPT_SSL_CA, OPT_SSL_CAPATH, + OPT_SSL_CIPHER, OPT_SHUTDOWN_TIMEOUT, OPT_LOCAL_INFILE, + OPT_DELETE_MASTER_LOGS, OPT_COMPACT, + OPT_PROMPT, OPT_IGN_LINES,OPT_TRANSACTION,OPT_MYSQL_PROTOCOL, + OPT_SHARED_MEMORY_BASE_NAME, OPT_FRM, OPT_SKIP_OPTIMIZATION, + OPT_COMPATIBLE, OPT_RECONNECT, OPT_DELIMITER, OPT_SECURE_AUTH, + OPT_OPEN_FILES_LIMIT, OPT_SET_CHARSET, OPT_CREATE_OPTIONS, + OPT_START_POSITION, OPT_STOP_POSITION, OPT_START_DATETIME, OPT_STOP_DATETIME, + OPT_SIGINT_IGNORE, OPT_HEXBLOB, OPT_ORDER_BY_PRIMARY +#ifdef HAVE_NDBCLUSTER_DB + ,OPT_NDBCLUSTER,OPT_NDB_CONNECTSTRING +#endif + ,OPT_IGNORE_TABLE,OPT_INSERT_IGNORE,OPT_DROP_DATABASE, + OPT_AUTO_CLOSE +}; diff --git a/client/completion_hash.cc b/client/completion_hash.cc index ff5d0b28e41..7a3b363c93c 100644 --- a/client/completion_hash.cc +++ b/client/completion_hash.cc @@ -27,7 +27,7 @@ #include <my_sys.h> #include "completion_hash.h" -uint hashpjw(char *arKey, uint nKeyLength) +uint hashpjw(const char *arKey, uint nKeyLength) { uint h = 0, g, i; @@ -79,7 +79,8 @@ int completion_hash_update(HashTable *ht, char *arKey, uint nKeyLength, if (!memcmp(p->arKey, arKey, nKeyLength)) { entry *n; - n = (entry *) alloc_root(&ht->mem_root,sizeof(entry)); + if (!(n = (entry *) alloc_root(&ht->mem_root,sizeof(entry)))) + return FAILURE; n->pNext = p->pData; n->str = str; p->pData = n; @@ -111,7 +112,7 @@ int completion_hash_update(HashTable *ht, char *arKey, uint nKeyLength, return SUCCESS; } -static Bucket *completion_hash_find(HashTable *ht, char *arKey, +static Bucket *completion_hash_find(HashTable *ht, const char *arKey, uint nKeyLength) { uint h, nIndex; @@ -156,7 +157,7 @@ int completion_hash_exists(HashTable *ht, char *arKey, uint nKeyLength) return 0; } -Bucket *find_all_matches(HashTable *ht, char *str, uint length, +Bucket *find_all_matches(HashTable *ht, const char *str, uint length, uint *res_length) { Bucket *b; diff --git a/client/completion_hash.h b/client/completion_hash.h index c0853fddfe7..2595a445c9d 100644 --- a/client/completion_hash.h +++ b/client/completion_hash.h @@ -43,14 +43,14 @@ typedef struct hashtable { uint nTableSize; uint initialized; MEM_ROOT mem_root; - uint(*pHashFunction) (char *arKey, uint nKeyLength); + uint(*pHashFunction) (const char *arKey, uint nKeyLength); Bucket **arBuckets; } HashTable; extern int completion_hash_init(HashTable *ht, uint nSize); extern int completion_hash_update(HashTable *ht, char *arKey, uint nKeyLength, char *str); extern int hash_exists(HashTable *ht, char *arKey); -extern Bucket *find_all_matches(HashTable *ht, char *str, uint length, uint *res_length); +extern Bucket *find_all_matches(HashTable *ht, const char *str, uint length, uint *res_length); extern Bucket *find_longest_match(HashTable *ht, char *str, uint length, uint *res_length); extern void add_word(HashTable *ht,char *str); extern void completion_hash_clean(HashTable *ht); diff --git a/client/connect_test.c b/client/connect_test.c deleted file mode 100644 index fd81ad635ad..00000000000 --- a/client/connect_test.c +++ /dev/null @@ -1,65 +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 */ - -#include <stdio.h> -#include <stdlib.h> -#include "mysql.h" - -static void change_user(MYSQL *sock,const char *user, const char *password, - const char *db,my_bool warning) -{ - if (mysql_change_user(sock,user,password,db) != warning) - { - fprintf(stderr,"Couldn't change user to: user: '%s', password: '%s', db: '%s': Error: %s\n", - user, password ? password : "", db ? db : "", - mysql_error(sock)); - } -} - - -int main(int argc, char **argv) -{ - MYSQL *sock; - - if (!(sock=mysql_init(0))) - { - fprintf(stderr,"Couldn't initialize mysql struct\n"); - exit(1); - } - mysql_options(sock,MYSQL_READ_DEFAULT_GROUP,"connect"); - if (!mysql_real_connect(sock,NULL,NULL,NULL,NULL,0,NULL,0)) - { - fprintf(stderr,"Couldn't connect to engine!\n%s\n",mysql_error(sock)); - perror(""); - exit(1); - } - - if (mysql_select_db(sock,"test")) - { - fprintf(stderr,"Couldn't select database test: Error: %s\n", - mysql_error(sock)); - } - - change_user(sock,"test_user","test_user","test",0); - change_user(sock,"test",NULL,"test",0); - change_user(sock,"test_user",NULL,"test",1); - change_user(sock,"test_user",NULL,NULL,1); - change_user(sock,"test_user","test_user","mysql",1); - - mysql_close(sock); - exit(0); - return 0; -} diff --git a/client/get_password.c b/client/get_password.c index 5d78656ab0a..1b7b4e65a9f 100644 --- a/client/get_password.c +++ b/client/get_password.c @@ -23,7 +23,6 @@ #include "mysql.h" #include <m_string.h> #include <m_ctype.h> -#include <dbug.h> #if defined(HAVE_BROKEN_GETPASS) && !defined(HAVE_GETPASSPHRASE) #undef HAVE_GETPASS diff --git a/client/insert_test.c b/client/insert_test.c deleted file mode 100644 index 052c12bfdf0..00000000000 --- a/client/insert_test.c +++ /dev/null @@ -1,59 +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 */ - -#include <stdio.h> -#include <stdlib.h> -#include "mysql.h" - -#define INSERT_QUERY "insert into test (name,num) values ('item %d', %d)" - - -int main(int argc, char **argv) -{ - int count,num; - MYSQL *sock,mysql; - char qbuf[160]; - - if (argc != 3) - { - fprintf(stderr,"usage : insert_test <dbname> <Num>\n\n"); - exit(1); - } - - mysql_init(&mysql); - if (!(sock = mysql_real_connect(&mysql,NULL,NULL,NULL,argv[1],0,NULL,0))) - { - fprintf(stderr,"Couldn't connect to engine!\n%s\n",mysql_error(&mysql)); - perror(""); - exit(1); - } - - num = atoi(argv[2]); - count = 0; - while (count < num) - { - sprintf(qbuf,INSERT_QUERY,count,count); - if(mysql_query(sock,qbuf)) - { - fprintf(stderr,"Query failed (%s)\n",mysql_error(sock)); - exit(1); - } - count++; - } - mysql_close(sock); - exit(0); - return 0; -} diff --git a/client/list_test.c b/client/list_test.c deleted file mode 100644 index 06bf16d2751..00000000000 --- a/client/list_test.c +++ /dev/null @@ -1,70 +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 */ - -#ifdef __WIN__ -#include <windows.h> -#endif -#include <stdio.h> -#include <stdlib.h> -#include "mysql.h" - -#define SELECT_QUERY "select name from test where num = %d" - - -int main(int argc, char **argv) -{ - int count, num; - MYSQL mysql,*sock; - MYSQL_RES *res; - char qbuf[160]; - - if (argc != 2) - { - fprintf(stderr,"usage : select_test <dbname>\n\n"); - exit(1); - } - - if (!(sock = mysql_connect(&mysql,NULL,0,0))) - { - fprintf(stderr,"Couldn't connect to engine!\n%s\n\n",mysql_error(&mysql)); - perror(""); - exit(1); - } - - if (mysql_select_db(sock,argv[1]) < 0) - { - fprintf(stderr,"Couldn't select database %s!\n%s\n",argv[1], - mysql_error(sock)); - exit(1); - } - - if (!(res=mysql_list_dbs(sock,NULL))) - { - fprintf(stderr,"Couldn't list dbs!\n%s\n",mysql_error(sock)); - exit(1); - } - mysql_free_result(res); - if (!(res=mysql_list_tables(sock,NULL))) - { - fprintf(stderr,"Couldn't list tables!\n%s\n",mysql_error(sock)); - exit(1); - } - mysql_free_result(res); - - mysql_close(sock); - exit(0); - return 0; -} diff --git a/client/my_readline.h b/client/my_readline.h index 2e716eec4cf..6052d462ab9 100644 --- a/client/my_readline.h +++ b/client/my_readline.h @@ -29,6 +29,6 @@ typedef struct st_line_buffer } LINE_BUFFER; extern LINE_BUFFER *batch_readline_init(ulong max_size,FILE *file); -extern LINE_BUFFER *batch_readline_command(my_string str); +extern LINE_BUFFER *batch_readline_command(LINE_BUFFER *buffer, my_string str); extern char *batch_readline(LINE_BUFFER *buffer); extern void batch_readline_end(LINE_BUFFER *buffer); diff --git a/client/mysql.cc b/client/mysql.cc index bbe3212be14..51822b64c82 100644 --- a/client/mysql.cc +++ b/client/mysql.cc @@ -25,7 +25,7 @@ * Matt Wagner <matt@mysql.com> * Jeremy Cole <jcole@mysql.com> * Tonu Samuel <tonu@mysql.com> - * Harrison Fisk <hcfisk@buffalo.edu> + * Harrison Fisk <harrison@mysql.com> * **/ @@ -40,7 +40,11 @@ #include <signal.h> #include <violite.h> -const char *VER= "12.22"; +#if defined(USE_LIBEDIT_INTERFACE) && defined(HAVE_LOCALE_H) +#include <locale.h> +#endif + +const char *VER= "14.7"; /* Don't try to make a nice table if the data is too big */ #define MAX_COLUMN_LENGTH 1024 @@ -92,9 +96,9 @@ extern "C" { #endif #ifdef FN_NO_CASE_SENCE -#define cmp_database(A,B) my_strcasecmp((A),(B)) +#define cmp_database(cs,A,B) my_strcasecmp((cs), (A), (B)) #else -#define cmp_database(A,B) strcmp((A),(B)) +#define cmp_database(cs,A,B) strcmp((A),(B)) #endif #if !defined( __WIN__) && !defined( OS2) && !defined(__NETWARE__) && (!defined(HAVE_mit_thread) || !defined(THREAD)) @@ -104,6 +108,7 @@ extern "C" { #include "completion_hash.h" #define PROMPT_CHAR '\\' +#define DEFAULT_DELIMITER ";" typedef struct st_status { @@ -128,13 +133,16 @@ static my_bool info_flag=0,ignore_errors=0,wait_flag=0,quick=0, opt_compress=0, using_opt_local_infile=0, vertical=0, line_numbers=1, column_names=1,opt_html=0, opt_xml=0,opt_nopager=1, opt_outfile=0, named_cmds= 0, - tty_password= 0, opt_nobeep=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; 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; static int connect_flag=CLIENT_INTERACTIVE; static char *current_host,*current_db,*current_user=0,*opt_password=0, - *current_prompt=0, *default_charset; + *current_prompt=0, *delimiter_str= 0, + *default_charset= (char*) MYSQL_DEFAULT_CHARSET_NAME; static char *histfile; static String glob_buffer,old_buffer; static String processed_prompt; @@ -156,12 +164,18 @@ static char pager[FN_REFLEN], outfile[FN_REFLEN]; static FILE *PAGER, *OUTFILE; static MEM_ROOT hash_mem_root; static uint prompt_counter; +static char delimiter[16]= DEFAULT_DELIMITER; +static uint delimiter_length= 1; + +#ifdef HAVE_SMEM +static char *shared_memory_base_name=0; +#endif +static uint opt_protocol=0; +static CHARSET_INFO *charset_info= &my_charset_latin1; #include "sslopt-vars.h" -#ifndef DBUG_OFF const char *default_dbug_option="d:t:o,/tmp/mysql.trace"; -#endif void tee_fprintf(FILE *file, const char *fmt, ...); void tee_fputs(const char *s, FILE *file); @@ -177,7 +191,7 @@ static int com_quit(String *str,char*), com_use(String *str,char*), com_source(String *str, char*), com_rehash(String *str, char*), com_tee(String *str, char*), com_notee(String *str, char*), - com_prompt(String *str, char*); + com_prompt(String *str, char*), com_delimiter(String *str, char*); #ifdef USE_POPEN static int com_nopager(String *str, char*), com_pager(String *str, char*), @@ -187,7 +201,9 @@ static int com_nopager(String *str, char*), com_pager(String *str, char*), static int read_lines(bool execute_commands); static int sql_connect(char *host,char *database,char *user,char *password, uint silent); -static int put_info(const char *str,INFO_TYPE info,uint error=0); +static int put_info(const char *str,INFO_TYPE info,uint error=0, + const char *sql_state=0); +static int put_error(MYSQL *mysql); static void safe_put_field(const char *pos,ulong length); static void xmlencode_print(const char *src, uint length); static void init_pager(); @@ -195,6 +211,7 @@ static void end_pager(); static void init_tee(const char *); static void end_tee(); static const char* construct_prompt(); +static char *get_arg(char *line, my_bool get_next_arg); static void init_username(); static void add_int_to_prompt(int toadd); @@ -210,11 +227,12 @@ typedef struct { } COMMANDS; static COMMANDS commands[] = { - { "help", 'h', com_help, 0, "Display this help." }, - { "?", '?', com_help, 0, "Synonym for `help'." }, + { "?", '?', com_help, 1, "Synonym for `help'." }, { "clear", 'c', com_clear, 0, "Clear command."}, { "connect",'r', com_connect,1, "Reconnect to the server. Optional arguments are db and host." }, + { "delimiter", 'd', com_delimiter, 1, + "Set query delimiter. " }, #ifdef USE_POPEN { "edit", 'e', com_edit, 0, "Edit command with $EDITOR."}, #endif @@ -222,6 +240,7 @@ static COMMANDS commands[] = { "Send command to mysql server, display result vertically."}, { "exit", 'q', com_quit, 0, "Exit mysql. Same as quit."}, { "go", 'g', com_go, 0, "Send command to mysql server." }, + { "help", 'h', com_help, 1, "Display this help." }, #ifdef USE_POPEN { "nopager",'n', com_nopager,0, "Disable pager, print to stdout." }, #endif @@ -244,7 +263,6 @@ static COMMANDS commands[] = { "Set outfile [to_outfile]. Append everything into given outfile." }, { "use", 'u', com_use, 1, "Use another database. Takes database name as argument." }, - /* Get bash-like expansion for some commands */ { "create table", 0, 0, 0, ""}, { "create database", 0, 0, 0, ""}, @@ -272,15 +290,30 @@ static const char *server_default_groups[]= { "server", "embedded", "mysql_SERVER", 0 }; #ifdef HAVE_READLINE -extern "C" void add_history(char *command); /* From readline directory */ -extern "C" int read_history(char *command); -extern "C" int write_history(char *command); +/* + HIST_ENTRY is defined for libedit, but not for the real readline + Need to redefine it for real readline to find it +*/ +#if !defined(HAVE_HIST_ENTRY) +typedef struct _hist_entry { + const char *line; + const char *data; +} HIST_ENTRY; +#endif + +extern "C" int add_history(const char *command); /* From readline directory */ +extern "C" int read_history(const char *command); +extern "C" int write_history(const char *command); +extern "C" HIST_ENTRY *history_get(int num); +extern "C" int history_length; +static int not_in_history(const char *line); static void initialize_readline (char *name); +static void fix_history(String *final_command); #endif static COMMANDS *find_command (char *name,char cmd_name); -static bool add_line(String &buffer, char *line, char *in_string, - my_bool *in_comment); +static bool add_line(String &buffer,char *line,char *in_string, + bool *ml_comment); static void remove_cntrl(String &buffer); static void print_table_data(MYSQL_RES *result); static void print_table_data_html(MYSQL_RES *result); @@ -297,11 +330,22 @@ static sig_handler mysql_end(int sig); int main(int argc,char *argv[]) { char buff[80]; + char *defaults, *extra_defaults; + char *emb_argv[3]; + int emb_argc= 1; + + emb_argv[0]= argv[0]; + get_defaults_files(argc, argv, &defaults, &extra_defaults); + if (defaults) + emb_argv[emb_argc++]= defaults; + if (extra_defaults) + emb_argv[emb_argc++]= extra_defaults; MY_INIT(argv[0]); DBUG_ENTER("main"); DBUG_PROCESS(argv[0]); - + + delimiter_str= delimiter; default_prompt = my_strdup(getenv("MYSQL_PS1") ? getenv("MYSQL_PS1") : "mysql> ",MYF(MY_WME)); @@ -312,8 +356,11 @@ int main(int argc,char *argv[]) strmov(pager, "stdout"); // the default, if --pager wasn't given { char *tmp=getenv("PAGER"); - if (tmp) - strmov(default_pager,tmp); + if (tmp && strlen(tmp)) + { + default_pager_set= 1; + strmov(default_pager, tmp); + } } if (!isatty(0) || !isatty(1)) { @@ -335,10 +382,16 @@ int main(int argc,char *argv[]) !(status.line_buff=batch_readline_init(opt_max_allowed_packet+512,stdin))) { free_defaults(defaults_argv); + my_end(0); + exit(1); + } + if (mysql_server_init(emb_argc, emb_argv, (char**) server_default_groups)) + { + free_defaults(defaults_argv); + my_end(0); exit(1); } glob_buffer.realloc(512); - mysql_server_init(0, NULL, (char**) server_default_groups); completion_hash_init(&ht, 128); init_alloc_root(&hash_mem_root, 16384, 0); bzero((char*) &mysql, sizeof(mysql)); @@ -351,7 +404,12 @@ int main(int argc,char *argv[]) } if (!status.batch) ignore_errors=1; // Don't abort monitor - signal(SIGINT, mysql_end); // Catch SIGINT to clean up + + if (opt_sigint_ignore) + signal(SIGINT, SIG_IGN); + else + signal(SIGINT, mysql_end); // Catch SIGINT to clean up + signal(SIGQUIT, mysql_end); // Catch SIGQUIT to clean up /* Run in interactive mode like the ingres/postgres monitor @@ -387,8 +445,12 @@ int main(int argc,char *argv[]) } } #endif - sprintf(buff, + sprintf(buff, "%s", +#ifndef NOT_YET "Type 'help;' or '\\h' for help. Type '\\c' to clear the buffer.\n"); +#else + "Type 'help [[%]function name[%]]' to get help on usage of function.\n"); +#endif put_info(buff,INFO_INFO); status.exit_status=read_lines(1); // read lines and execute them if (opt_outfile) @@ -429,6 +491,9 @@ sig_handler mysql_end(int sig) my_free(full_username,MYF(MY_ALLOW_ZERO_PTR)); my_free(part_username,MYF(MY_ALLOW_ZERO_PTR)); my_free(default_prompt,MYF(MY_ALLOW_ZERO_PTR)); +#ifdef HAVE_SMEM + my_free(shared_memory_base_name,MYF(MY_ALLOW_ZERO_PTR)); +#endif my_free(current_prompt,MYF(MY_ALLOW_ZERO_PTR)); mysql_server_end(); free_defaults(defaults_argv); @@ -441,6 +506,8 @@ 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}, + {"help", 'I', "Synonym for -?", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, + 0, 0, 0, 0, 0}, #ifdef __NETWARE__ {"auto-close", 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}, @@ -452,7 +519,7 @@ static struct my_option my_long_options[] = "No automatic rehashing. One has to use 'rehash' to get table and field completion. This gives a quicker start of mysql and disables rehashing on reconnect. WARNING: options deprecated; use --disable-auto-rehash instead.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, {"batch", 'B', - "Print results with a tab as separator, each row on new line. Doesn't use history file.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + "Don't use history file. Disable interactive behavior. (Enables --silent)", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, {"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}, @@ -462,13 +529,18 @@ static struct my_option my_long_options[] = {"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}, -#ifndef DBUG_OFF - {"debug", '#', "Output debug log.", (gptr*) &default_dbug_option, +#ifdef DBUG_OFF + {"debug", '#', "This is a non-debug version. Catch this and exit", + 0,0, 0, GET_DISABLED, OPT_ARG, 0, 0, 0, 0, 0, 0}, +#else + {"debug", '#', "Output debug log", (gptr*) &default_dbug_option, (gptr*) &default_dbug_option, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, #endif {"database", 'D', "Database to use.", (gptr*) ¤t_db, (gptr*) ¤t_db, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"execute", 'e', "Execute command and quit. (Output like with --batch).", 0, + {"delimiter", OPT_DELIMITER, "Delimiter to be used.", (gptr*) &delimiter_str, + (gptr*) &delimiter_str, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"execute", 'e', "Execute command and quit. (Disables --force and history file)", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"vertical", 'E', "Print the output of a query (rows) vertically.", (gptr*) &vertical, (gptr*) &vertical, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, @@ -519,6 +591,9 @@ static struct my_option my_long_options[] = {"set-variable", 'O', "Change the value of a variable. Please note that this option is deprecated; you can set variables directly with --variable-name=value.", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"sigint-ignore", OPT_SIGINT_IGNORE, "Ignore SIGINT (CTRL-C)", + (gptr*) &opt_sigint_ignore, (gptr*) &opt_sigint_ignore, 0, GET_BOOL, + NO_ARG, 0, 0, 0, 0, 0, 0}, {"one-database", 'o', "Only update the default database. This is useful for skipping updates to other database in the update log.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, @@ -540,14 +615,23 @@ static struct my_option my_long_options[] = {"prompt", OPT_PROMPT, "Set the mysql prompt to this value.", (gptr*) ¤t_prompt, (gptr*) ¤t_prompt, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 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}, {"quick", 'q', - "Don't cache result, print it row by row. This may slow down the server if the output is suspended. Doesn't use history file. ", + "Don't cache result, print it row by row. This may slow down the server if the output is suspended. Doesn't use history file.", (gptr*) &quick, (gptr*) &quick, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"raw", 'r', "Write fields without conversion. Used with --batch", + {"raw", 'r', "Write fields without conversion. Used with --batch.", (gptr*) &opt_raw_data, (gptr*) &opt_raw_data, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"silent", 's', "Be more silent.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, + {"reconnect", OPT_RECONNECT, "Reconnect if the connection is lost. Disable with --disable-reconnect. This option is enabled by default.", + (gptr*) &opt_reconnect, (gptr*) &opt_reconnect, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, + {"silent", 's', "Be more silent. Print results with a tab as separator, each row on new line.", 0, 0, 0, GET_NO_ARG, NO_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 {"socket", 'S', "Socket file to use for connection.", (gptr*) &opt_mysql_unix_port, (gptr*) &opt_mysql_unix_port, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, @@ -564,32 +648,43 @@ static struct my_option my_long_options[] = (gptr*) ¤t_user, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, #endif {"safe-updates", 'U', "Only allow UPDATE and DELETE that uses keys.", - (gptr*) &safe_updates, (gptr*) &safe_updates, 0, GET_BOOL, OPT_ARG, 0, 0, + (gptr*) &safe_updates, (gptr*) &safe_updates, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"i-am-a-dummy", 'U', "Synonym for option --safe-updates, -U.", - (gptr*) &safe_updates, (gptr*) &safe_updates, 0, GET_BOOL, OPT_ARG, 0, 0, + (gptr*) &safe_updates, (gptr*) &safe_updates, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"verbose", 'v', "Write more. (-v -v -v gives the table output format)", 0, + {"verbose", 'v', "Write more. (-v -v -v gives the table output format).", 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}, {"wait", 'w', "Wait and retry if connection is down.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"connect_timeout", OPT_CONNECT_TIMEOUT, "", (gptr*) &opt_connect_timeout, + {"connect_timeout", OPT_CONNECT_TIMEOUT, + "Number of seconds before connection timeout.", + (gptr*) &opt_connect_timeout, (gptr*) &opt_connect_timeout, 0, GET_ULONG, REQUIRED_ARG, 0, 0, 3600*12, 0, 0, 1}, - {"max_allowed_packet", OPT_MAX_ALLOWED_PACKET, "", + {"max_allowed_packet", OPT_MAX_ALLOWED_PACKET, + "Max packet length to send to, or receive from server", (gptr*) &opt_max_allowed_packet, (gptr*) &opt_max_allowed_packet, 0, GET_ULONG, REQUIRED_ARG, 16 *1024L*1024L, 4096, (longlong) 2*1024L*1024L*1024L, MALLOC_OVERHEAD, 1024, 0}, - {"net_buffer_length", OPT_NET_BUFFER_LENGTH, "", + {"net_buffer_length", OPT_NET_BUFFER_LENGTH, + "Buffer for TCP/IP and socket communication", (gptr*) &opt_net_buffer_length, (gptr*) &opt_net_buffer_length, 0, GET_ULONG, REQUIRED_ARG, 16384, 1024, 512*1024*1024L, MALLOC_OVERHEAD, 1024, 0}, - {"select_limit", OPT_SELECT_LIMIT, "", (gptr*) &select_limit, + {"select_limit", OPT_SELECT_LIMIT, + "Automatic limit for SELECT when using --safe-updates", + (gptr*) &select_limit, (gptr*) &select_limit, 0, GET_ULONG, REQUIRED_ARG, 1000L, 1, ~0L, 0, 1, 0}, - {"max_join_size", OPT_MAX_JOIN_SIZE, "", (gptr*) &max_join_size, + {"max_join_size", OPT_MAX_JOIN_SIZE, + "Automatic limit for rows in a join when using --safe-updates", + (gptr*) &max_join_size, (gptr*) &max_join_size, 0, GET_ULONG, REQUIRED_ARG, 1000000L, 1, ~0L, 0, 1, 0}, + {"secure-auth", OPT_SECURE_AUTH, "Refuse client connecting to server if it" + " uses old (pre-4.1.1) protocol", (gptr*) &opt_secure_auth, + (gptr*) &opt_secure_auth, 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} }; @@ -600,8 +695,22 @@ static void usage(int version) #ifdef __NETWARE__ #define printf consoleprintf #endif - printf("%s Ver %s Distrib %s, for %s (%s)\n", - my_progname, VER, MYSQL_SERVER_VERSION, SYSTEM_TYPE, MACHINE_TYPE); + +#if defined(USE_LIBEDIT_INTERFACE) + const char* readline= ""; +#else + const char* readline= "readline"; +#endif + +#ifdef HAVE_READLINE + printf("%s Ver %s Distrib %s, for %s (%s) using %s %s\n", + my_progname, VER, MYSQL_SERVER_VERSION, SYSTEM_TYPE, MACHINE_TYPE, + readline, rl_library_version); +#else + printf("%s Ver %s Distrib %s, for %s (%s)", my_progname, VER, + MYSQL_SERVER_VERSION, SYSTEM_TYPE, MACHINE_TYPE); +#endif + if (version) return; printf("\ @@ -633,6 +742,17 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), strmov(mysql_charsets_dir, argument); charsets_dir = mysql_charsets_dir; break; + case OPT_DEFAULT_CHARSET: + default_charset_used= 1; + break; + case OPT_DELIMITER: + if (argument == disabled_my_option) + strmov(delimiter, DEFAULT_DELIMITER); + else + strmake(delimiter, argument, sizeof(delimiter) - 1); + delimiter_length= (uint)strlen(delimiter); + delimiter_str= delimiter; + break; case OPT_LOCAL_INFILE: using_opt_local_infile=1; break; @@ -656,17 +776,31 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), else { opt_nopager= 0; - if (argument) + if (argument && strlen(argument)) + { + default_pager_set= 1; strmov(pager, argument); - else + strmov(default_pager, pager); + } + else if (default_pager_set) strmov(pager, default_pager); - strmov(default_pager, pager); + else + opt_nopager= 1; } break; case OPT_NOPAGER: printf("WARNING: option deprecated; use --disable-pager instead.\n"); opt_nopager= 1; + 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; + } + break; case 'A': rehash= 0; break; @@ -676,10 +810,10 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), case 'e': status.batch= 1; status.add_to_history= 0; - batch_readline_end(status.line_buff); // If multiple -e - if (!(status.line_buff= batch_readline_command(argument))) + if (!status.line_buff) + ignore_errors= 0; // do it for the first -e only + if (!(status.line_buff= batch_readline_command(status.line_buff, argument))) return 1; - ignore_errors= 0; break; case 'o': if (argument == disabled_my_option) @@ -720,17 +854,13 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), verbose++; break; case 'B': - if (!status.batch) - { - status.batch= 1; - status.add_to_history= 0; - opt_silent++; // more silent - } + status.batch= 1; + status.add_to_history= 0; + set_if_bigger(opt_silent,1); // more silent break; case 'W': #ifdef __WIN__ - my_free(opt_mysql_unix_port, MYF(MY_ALLOW_ZERO_PTR)); - opt_mysql_unix_port= my_strdup(MYSQL_NAMEDPIPE, MYF(0)); + opt_protocol = MYSQL_PROTOCOL_PIPE; #endif break; #include <sslopt-case.h> @@ -780,14 +910,16 @@ static int get_options(int argc, char **argv) strmov(default_pager, "stdout"); strmov(pager, "stdout"); opt_nopager= 1; + default_pager_set= 0; opt_outfile= 0; + opt_reconnect= 0; connect_flag= 0; /* Not in interactive mode */ } - if (default_charset) - { - if (set_default_charset_by_name(default_charset, MYF(MY_WME))) - exit(1); - } + + if (strcmp(default_charset, charset_info->csname) && + !(charset_info= get_charset_by_csname(default_charset, + MY_CS_PRIMARY, MYF(MY_WME)))) + exit(1); if (argc > 1) { usage(0); @@ -808,14 +940,15 @@ static int read_lines(bool execute_commands) { #if defined( __WIN__) || defined(OS2) || defined(__NETWARE__) char linebuffer[254]; + String buffer; #endif char *line; char in_string=0; - my_bool in_comment= 0; ulong line_number=0; + bool ml_comment= 0; COMMANDS *com; status.exit_status=1; - + for (;;) { if (status.batch || !execute_commands) @@ -827,7 +960,8 @@ static int read_lines(bool execute_commands) } else { - char *prompt= (char*) (glob_buffer.is_empty() ? construct_prompt() : + char *prompt= (char*) (ml_comment ? " /*> " : + glob_buffer.is_empty() ? construct_prompt() : !in_string ? " -> " : in_string == '\'' ? " '> " : (in_string == '`' ? @@ -848,8 +982,25 @@ static int read_lines(bool execute_commands) *p = '\0'; } #else - linebuffer[0]= (char) sizeof(linebuffer); - line= _cgets(linebuffer); + 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) @@ -886,12 +1037,12 @@ static int read_lines(bool execute_commands) if (glob_buffer.is_empty()) // If buffer was emptied in_string=0; #ifdef HAVE_READLINE - if (status.add_to_history) + if (status.add_to_history && not_in_history(line)) add_history(line); #endif continue; } - if (add_line(glob_buffer, line, &in_string, &in_comment)) + if (add_line(glob_buffer,line,&in_string,&ml_comment)) break; } /* if in batch mode, send last query even if it doesn't end with \g or go */ @@ -906,6 +1057,9 @@ static int read_lines(bool execute_commands) status.exit_status=0; } } +#if defined( __WIN__) || defined(OS2) || defined(__NETWARE__) + buffer.free(); +#endif return status.exit_status; } @@ -922,14 +1076,14 @@ static COMMANDS *find_command (char *name,char cmd_char) } else { - while (isspace(*name)) + while (my_isspace(charset_info,*name)) name++; - if (strchr(name,';') || strstr(name,"\\g")) + if (strstr(name, delimiter) || strstr(name, "\\g")) return ((COMMANDS *) 0); if ((end=strcont(name," \t"))) { len=(uint) (end - name); - while (isspace(*end)) + while (my_isspace(charset_info,*end)) end++; if (!*end) end=0; // no arguments to function @@ -941,7 +1095,9 @@ static COMMANDS *find_command (char *name,char cmd_char) for (uint i= 0; commands[i].name; i++) { if (commands[i].func && - ((name && !my_casecmp(name,commands[i].name,len) && + ((name && + !my_strnncoll(charset_info,(uchar*)name,len, + (uchar*)commands[i].name,len) && !commands[i].name[len] && (!end || (end && commands[i].takes_params))) || !name && commands[i].cmd_char == cmd_char)) @@ -951,17 +1107,17 @@ static COMMANDS *find_command (char *name,char cmd_char) } -static bool add_line(String &buffer,char *line,char *in_string, - my_bool *in_comment) +static bool add_line(String &buffer,char *line,char *in_string, + bool *ml_comment) { uchar inchar; - char buff[80],*pos,*out; + char buff[80], *pos, *out; COMMANDS *com; if (!line[0] && buffer.is_empty()) return 0; #ifdef HAVE_READLINE - if (status.add_to_history && line[0]) + if (status.add_to_history && line[0] && not_in_history(line)) add_history(line); #endif #ifdef USE_MB @@ -970,20 +1126,23 @@ static bool add_line(String &buffer,char *line,char *in_string, for (pos=out=line ; (inchar= (uchar) *pos) ; pos++) { - if (isspace(inchar) && out == line && buffer.is_empty()) + if (my_isspace(charset_info,inchar) && out == line && + buffer.is_empty()) continue; #ifdef USE_MB int l; - if (use_mb(default_charset_info) && - (l = my_ismbchar(default_charset_info, pos, strend))) { + if (use_mb(charset_info) && + (l = my_ismbchar(charset_info, pos, strend))) { while (l--) *out++ = *pos++; pos--; continue; } #endif - if (inchar == '\\') - { // mSQL or postgreSQL style command ? + if (!*ml_comment && inchar == '\\') + { + // Found possbile one character command like \c + if (!(inchar = (uchar) *++pos)) break; // readline adds one '\' if (*in_string || inchar == 'N') // \N is short for NULL @@ -994,15 +1153,20 @@ static bool add_line(String &buffer,char *line,char *in_string, } if ((com=find_command(NullS,(char) inchar))) { - const String tmp(line,(uint) (out-line)); + const String tmp(line,(uint) (out-line), charset_info); buffer.append(tmp); if ((*com->func)(&buffer,pos-1) > 0) return 1; // Quit if (com->takes_params) { - for (pos++ ; *pos && *pos != ';' ; pos++) ; // Remove parameters + for (pos++ ; + *pos && (*pos != *delimiter || + !is_prefix(pos + 1, delimiter + 1)) ; pos++) + ; // Remove parameters if (!*pos) pos--; + else + pos+= delimiter_length - 1; // Point at last delim char } out=line; } @@ -1016,44 +1180,57 @@ static bool add_line(String &buffer,char *line,char *in_string, continue; } } - else if (inchar == ';' && !*in_string && !*in_comment) - { // ';' is end of command + + else if (!*ml_comment && (*pos == *delimiter && + is_prefix(pos + 1, delimiter + 1)) && + !*in_string) + { + uint old_delimiter_length= delimiter_length; if (out != line) - buffer.append(line,(uint) (out-line)); // Add this line - if ((com=find_command(buffer.c_ptr(),0))) + buffer.append(line, (uint) (out - line)); // Add this line + if ((com= find_command(buffer.c_ptr(), 0))) { - if ((*com->func)(&buffer,buffer.c_ptr()) > 0) + if ((*com->func)(&buffer, buffer.c_ptr()) > 0) return 1; // Quit } else { - int error=com_go(&buffer,0); - if (error) - { - return error < 0 ? 0 : 1; // < 0 is not fatal - } + if (com_go(&buffer, 0) > 0) // < 0 is not fatal + return 1; } buffer.length(0); - out=line; + out= line; + pos+= old_delimiter_length - 1; } - else if (!*in_string && (inchar == '#' || - inchar == '-' && pos[1] == '-' && - isspace(pos[2]))) + else if (!*ml_comment && (!*in_string && (inchar == '#' || + inchar == '-' && pos[1] == '-' && + my_isspace(charset_info,pos[2])))) break; // comment to end of line + else if (!*in_string && inchar == '/' && *(pos+1) == '*' && + *(pos+2) != '!') + { + pos++; + *ml_comment= 1; + if (out != line) + { + buffer.append(line,(uint) (out-line)); + out=line; + } + } + else if (*ml_comment && inchar == '*' && *(pos + 1) == '/') + { + pos++; + *ml_comment= 0; + } else { // Add found char to buffer if (inchar == *in_string) - *in_string=0; - else if (!*in_comment && !*in_string && (inchar == '\'' || inchar == '"' || inchar == '`')) - *in_string=(char) inchar; - *out++ = (char) inchar; - if (inchar == '*' && !*in_string) - { - if (pos != line && pos[-1] == '/') - *in_comment= 1; - else if (in_comment && pos[1] == '/') - *in_comment= 0; - } + *in_string= 0; + else if (!*ml_comment && !*in_string && + (inchar == '\'' || inchar == '"' || inchar == '`')) + *in_string= (char) inchar; + if (!*ml_comment) + *out++= (char) inchar; } } if (out != line || !buffer.is_empty()) @@ -1062,7 +1239,7 @@ static bool add_line(String &buffer,char *line,char *in_string, uint length=(uint) (out-line); if (buffer.length() + length >= buffer.alloced_length()) buffer.realloc(buffer.length()+length+IO_SIZE); - if (buffer.append(line,length)) + if (!(*ml_comment) && buffer.append(line,length)) return 1; } return 0; @@ -1074,8 +1251,8 @@ static bool add_line(String &buffer,char *line,char *in_string, #ifdef HAVE_READLINE -static char *new_command_generator(char *text, int); -static char **new_mysql_completion (char *text, int start, int end); +static char *new_command_generator(const char *text, int); +static char **new_mysql_completion (const char *text, int start, int end); /* Tell the GNU Readline library how to complete. We want to try to complete @@ -1083,21 +1260,102 @@ static char **new_mysql_completion (char *text, int start, int end); if not. */ -char **no_completion (char *text __attribute__ ((unused)), - char *word __attribute__ ((unused))) +#if defined(USE_NEW_READLINE_INTERFACE) || defined(USE_LIBEDIT_INTERFACE) +char *no_completion(const char*,int) +#else +int no_completion() +#endif { return 0; /* No filename completion */ } +/* glues pieces of history back together if in pieces */ +static void fix_history(String *final_command) +{ + int total_lines = 1; + char *ptr = final_command->c_ptr(); + String fixed_buffer; /* Converted buffer */ + char str_char = '\0'; /* Character if we are in a string or not */ + + /* find out how many lines we have and remove newlines */ + while (*ptr != '\0') + { + switch (*ptr) { + /* string character */ + case '"': + case '\'': + case '`': + if (str_char == '\0') /* open string */ + str_char = *ptr; + else if (str_char == *ptr) /* close string */ + str_char = '\0'; + fixed_buffer.append(ptr,1); + break; + case '\n': + /* + not in string, change to space + if in string, leave it alone + */ + fixed_buffer.append(str_char == '\0' ? " " : "\n"); + total_lines++; + break; + case '\\': + fixed_buffer.append('\\'); + /* need to see if the backslash is escaping anything */ + if (str_char) + { + ptr++; + /* special characters that need escaping */ + if (*ptr == '\'' || *ptr == '"' || *ptr == '\\') + fixed_buffer.append(ptr,1); + else + ptr--; + } + break; + + default: + fixed_buffer.append(ptr,1); + } + ptr++; + } + if (total_lines > 1) + add_history(fixed_buffer.ptr()); +} + +/* + returns 0 if line matches the previous history entry + returns 1 if the line doesn't match the previous history entry +*/ +static int not_in_history(const char *line) +{ + HIST_ENTRY *oldhist = history_get(history_length); + + if (oldhist == 0) + return 1; + if (strcmp(oldhist->line,line) == 0) + return 0; + return 1; +} + static void initialize_readline (char *name) { /* Allow conditional parsing of the ~/.inputrc file. */ rl_readline_name = name; /* Tell the completer that we want a crack first. */ - /* rl_attempted_completion_function = (CPPFunction *)mysql_completion;*/ - rl_attempted_completion_function = (CPPFunction *) new_mysql_completion; - rl_completion_entry_function=(Function *) no_completion; +#if defined(USE_NEW_READLINE_INTERFACE) + rl_attempted_completion_function= (rl_completion_func_t*)&new_mysql_completion; + rl_completion_entry_function= (rl_compentry_func_t*)&no_completion; +#elif defined(USE_LIBEDIT_INTERFACE) +#ifdef HAVE_LOCALE_H + setlocale(LC_ALL,""); /* so as libedit use isprint */ +#endif + rl_attempted_completion_function= (CPPFunction*)&new_mysql_completion; + rl_completion_entry_function= (Function*)&no_completion; +#else + rl_attempted_completion_function= (CPPFunction*)&new_mysql_completion; + rl_completion_entry_function= (Function*)&no_completion; +#endif } /* @@ -1107,17 +1365,21 @@ static void initialize_readline (char *name) array of matches, or NULL if there aren't any. */ -static char **new_mysql_completion (char *text, +static char **new_mysql_completion (const char *text, int start __attribute__((unused)), int end __attribute__((unused))) { if (!status.batch && !quick) - return completion_matches(text, (CPFunction*) new_command_generator); +#if defined(USE_NEW_READLINE_INTERFACE) + return rl_completion_matches(text, new_command_generator); +#else + return completion_matches((char *)text, (CPFunction *)new_command_generator); +#endif else return (char**) 0; } -static char *new_command_generator(char *text,int state) +static char *new_command_generator(const char *text,int state) { static int textlen; char *ptr; @@ -1213,12 +1475,6 @@ static void build_completion_hash(bool rehash, bool write_info) if (status.batch || quick || !current_db) DBUG_VOID_RETURN; // We don't need completion in batches - if (tables) - { - mysql_free_result(tables); - tables=0; - } - /* hash SQL commands */ while (cmd->name) { add_word(&ht,(char*) cmd->name); @@ -1295,12 +1551,15 @@ You can turn off this feature to get a quicker startup with -A\n\n"); if (!(field_names[i] = (char **) alloc_root(&hash_mem_root, sizeof(char *) * (num_fields*2+1)))) - break; - field_names[i][num_fields*2]='\0'; + { + mysql_free_result(fields); + break; + } + field_names[i][num_fields*2]= '\0'; j=0; while ((sql_field=mysql_fetch_field(fields))) { - sprintf(buf,"%s.%s",table_row[0],sql_field->name); + sprintf(buf,"%.64s.%.64s",table_row[0],sql_field->name); field_names[i][j] = strdup_root(&hash_mem_root,buf); add_word(&ht,field_names[i][j]); field_names[i][num_fields+j] = strdup_root(&hash_mem_root, @@ -1313,11 +1572,8 @@ You can turn off this feature to get a quicker startup with -A\n\n"); mysql_free_result(fields); } else - { - tee_fprintf(stdout, - "Didn't find any fields in table '%s'\n",table_row[0]); - field_names[i]=0; - } + field_names[i]= 0; + i++; } mysql_free_result(tables); @@ -1355,7 +1611,7 @@ char *rindex(const char *s,int c) static int reconnect(void) { - if (!status.batch) + if (opt_reconnect) { put_info("No connection. Trying to reconnect...",INFO_INFO); (void) com_connect((String *) 0, 0); @@ -1367,36 +1623,182 @@ static int reconnect(void) return 0; } +static void get_current_db() +{ + MYSQL_RES *res; + + my_free(current_db, MYF(MY_ALLOW_ZERO_PTR)); + current_db= NULL; + /* In case of error below current_db will be NULL */ + if (!mysql_query(&mysql, "SELECT DATABASE()") && + (res= mysql_use_result(&mysql))) + { + MYSQL_ROW row= mysql_fetch_row(res); + if (row[0]) + current_db= my_strdup(row[0], MYF(MY_WME)); + mysql_free_result(res); + } +} /*************************************************************************** The different commands ***************************************************************************/ +int mysql_real_query_for_lazy(const char *buf, int length) +{ + for (uint retry=0;; retry++) + { + if (!mysql_real_query(&mysql,buf,length)) + return 0; + int error= put_error(&mysql); + if (mysql_errno(&mysql) != CR_SERVER_GONE_ERROR || retry > 1 || + !opt_reconnect) + return error; + if (reconnect()) + return error; + } +} + +int mysql_store_result_for_lazy(MYSQL_RES **result) +{ + if ((*result=mysql_store_result(&mysql))) + return 0; + + if (mysql_error(&mysql)[0]) + return put_error(&mysql); + return 0; +} + +static void print_help_item(MYSQL_ROW *cur, int num_name, int num_cat, char *last_char) +{ + char ccat= (*cur)[num_cat][0]; + if (*last_char != ccat) + { + put_info(ccat == 'Y' ? "categories:" : "topics:", INFO_INFO); + *last_char= ccat; + } + tee_fprintf(PAGER, " %s\n", (*cur)[num_name]); +} + + +static int com_server_help(String *buffer __attribute__((unused)), + char *line __attribute__((unused)), char *help_arg) +{ + MYSQL_ROW cur; + const char *server_cmd= buffer->ptr(); + char cmd_buf[100]; + MYSQL_RES *result; + int error; + + if (help_arg[0] != '\'') + { + (void) strxnmov(cmd_buf, sizeof(cmd_buf), "help '", help_arg, "'", NullS); + server_cmd= cmd_buf; + } + + if (!status.batch) + { + old_buffer= *buffer; + old_buffer.copy(); + } + + if (!connected && reconnect()) + return 1; + + if ((error= mysql_real_query_for_lazy(server_cmd,(int)strlen(server_cmd))) || + (error= mysql_store_result_for_lazy(&result))) + return error; + + if (result) + { + unsigned int num_fields= mysql_num_fields(result); + my_ulonglong num_rows= mysql_num_rows(result); + mysql_fetch_fields(result); + if (num_fields==3 && num_rows==1) + { + if (!(cur= mysql_fetch_row(result))) + { + error= -1; + goto err; + } + + init_pager(); + tee_fprintf(PAGER, "Name: \'%s\'\n", cur[0]); + tee_fprintf(PAGER, "Description:\n%s", cur[1]); + if (cur[2] && *((char*)cur[2])) + tee_fprintf(PAGER, "Examples:\n%s", cur[2]); + tee_fprintf(PAGER, "\n"); + end_pager(); + } + else if (num_fields >= 2 && num_rows) + { + init_pager(); + char last_char= 0; + + int num_name= 0, num_cat= 0; + LINT_INIT(num_name); + LINT_INIT(num_cat); + + if (num_fields == 2) + { + put_info("Many help items for your request exist.", INFO_INFO); + put_info("To make a more specific request, please type 'help <item>',\nwhere <item> is one of the following", INFO_INFO); + num_name= 0; + num_cat= 1; + } + else if ((cur= mysql_fetch_row(result))) + { + tee_fprintf(PAGER, "You asked for help about help category: \"%s\"\n", cur[0]); + put_info("For more information, type 'help <item>', where <item> is one of the following", INFO_INFO); + num_name= 1; + num_cat= 2; + print_help_item(&cur,1,2,&last_char); + } + + while ((cur= mysql_fetch_row(result))) + print_help_item(&cur,num_name,num_cat,&last_char); + tee_fprintf(PAGER, "\n"); + end_pager(); + } + else + { + put_info("\nNothing found", INFO_INFO); + put_info("Please try to run 'help contents' for a list of all accessible topics\n", INFO_INFO); + } + } + +err: + mysql_free_result(result); + return error; +} + static int -com_help (String *buffer __attribute__((unused)), - char *line __attribute__((unused))) +com_help(String *buffer __attribute__((unused)), + char *line __attribute__((unused))) { - reg1 int i; + reg1 int i, j; + char * help_arg= strchr(line,' '), buff[32], *end; + + if (help_arg) + return com_server_help(buffer,line,help_arg+1); - put_info("\nFor the complete MySQL Manual online visit:\n http://www.mysql.com/documentation\n", INFO_INFO); - put_info("For info on technical support from MySQL developers visit:\n http://www.mysql.com/support\n", INFO_INFO); - put_info("For info on MySQL books, utilities, consultants, etc. visit:\n http://www.mysql.com/portal\n", INFO_INFO); + put_info("\nFor the complete MySQL Manual online, visit:\n http://www.mysql.com/documentation\n", INFO_INFO); + put_info("For info on technical support from MySQL developers, visit:\n http://www.mysql.com/support\n", INFO_INFO); + put_info("For info on MySQL books, utilities, consultants, etc., visit:\n http://www.mysql.com/portal\n", INFO_INFO); put_info("List of all MySQL commands:", INFO_INFO); if (!named_cmds) - put_info(" (Commands must appear first on line and end with ';')\n", - INFO_INFO); + put_info("Note that all text commands must be first on line and end with ';'",INFO_INFO); for (i = 0; commands[i].name; i++) { + end= strmov(buff, commands[i].name); + for (j= (int)strlen(commands[i].name); j < 10; j++) + end= strmov(end, " "); if (commands[i].func) - tee_fprintf(stdout, "%s\t(\\%c)\t%s\n", commands[i].name, + tee_fprintf(stdout, "%s(\\%c) %s\n", buff, commands[i].cmd_char, commands[i].doc); } - if (connected) - tee_fprintf(stdout, - "\nConnection id: %lu (Can be used with mysqladmin kill)\n\n", - mysql_thread_id(&mysql)); - else - tee_fprintf(stdout, "Not connected! Reconnect with 'connect'!\n\n"); + if (connected && mysql_get_server_version(&mysql) >= 40100) + put_info("\nFor server side help, type 'help contents'\n", INFO_INFO); return 0; } @@ -1405,6 +1807,10 @@ com_help (String *buffer __attribute__((unused)), static int com_clear(String *buffer,char *line __attribute__((unused))) { +#ifdef HAVE_READLINE + if (status.add_to_history) + fix_history(buffer); +#endif buffer->length(0); return 0; } @@ -1421,10 +1827,11 @@ com_clear(String *buffer,char *line __attribute__((unused))) static int com_go(String *buffer,char *line __attribute__((unused))) { - char buff[160],time_buff[32]; + char buff[200], time_buff[32], *pos; MYSQL_RES *result; - ulong timer; - uint error=0; + ulong timer, warnings; + uint error= 0; + int err= 0; if (!status.batch) { @@ -1446,104 +1853,120 @@ com_go(String *buffer,char *line __attribute__((unused))) if (!connected && reconnect()) { buffer->length(0); // Remove query on error - return status.batch ? 1 : -1; // Fatal error + return opt_reconnect ? -1 : 1; // Fatal error } if (verbose) (void) com_print(buffer,0); if (skip_updates && - (buffer->length() < 4 || my_sortcmp(buffer->ptr(),"SET ",4))) + (buffer->length() < 4 || my_strnncoll(charset_info, + (const uchar*)buffer->ptr(),4, + (const uchar*)"SET ",4))) { (void) put_info("Ignoring query to other database",INFO_INFO); return 0; } timer=start_timer(); - for (uint retry=0;; retry++) + + error= mysql_real_query_for_lazy(buffer->ptr(),buffer->length()); + +#ifdef HAVE_READLINE + if (status.add_to_history) + { + buffer->append(vertical ? "\\G" : delimiter); + /* Append final command onto history */ + fix_history(buffer); + } +#endif + + if (error) { - if (!mysql_real_query(&mysql,buffer->ptr(),buffer->length())) - break; - error=put_info(mysql_error(&mysql),INFO_ERROR, mysql_errno(&mysql)); - if (mysql_errno(&mysql) != CR_SERVER_GONE_ERROR || retry > 1 - || status.batch) - { - buffer->length(0); // Remove query on error - return error; - } - if (reconnect()) - { - buffer->length(0); // Remove query on error - return error; - } + buffer->length(0); // Remove query on error + return error; } error=0; buffer->length(0); - if (quick) + do { - if (!(result=mysql_use_result(&mysql)) && mysql_field_count(&mysql)) + if (quick) { - return put_info(mysql_error(&mysql),INFO_ERROR,mysql_errno(&mysql)); + if (!(result=mysql_use_result(&mysql)) && mysql_field_count(&mysql)) + return put_error(&mysql); } - } - else - { - if (!(result=mysql_store_result(&mysql))) + else { - if (mysql_error(&mysql)[0]) - { - return put_info(mysql_error(&mysql),INFO_ERROR,mysql_errno(&mysql)); - } + error= mysql_store_result_for_lazy(&result); + if (error) + return error; } - } - if (verbose >= 3 || !opt_silent) - mysql_end_timer(timer,time_buff); - else - time_buff[0]=0; - if (result) - { - if (!mysql_num_rows(result) && ! quick) + if (verbose >= 3 || !opt_silent) + mysql_end_timer(timer,time_buff); + else + time_buff[0]=0; + if (result) { - sprintf(buff,"Empty set%s",time_buff); + if (!mysql_num_rows(result) && ! quick) + { + strmov(buff, "Empty set"); + } + else + { + init_pager(); + if (opt_html) + print_table_data_html(result); + else if (opt_xml) + print_table_data_xml(result); + else if (vertical) + print_table_data_vertically(result); + else if (opt_silent && verbose <= 2 && !output_tables) + print_tab_data(result); + else + print_table_data(result); + sprintf(buff,"%ld %s in set", + (long) mysql_num_rows(result), + (long) mysql_num_rows(result) == 1 ? "row" : "rows"); + end_pager(); + } } + else if (mysql_affected_rows(&mysql) == ~(ulonglong) 0) + strmov(buff,"Query OK"); else + sprintf(buff,"Query OK, %ld %s affected", + (long) mysql_affected_rows(&mysql), + (long) mysql_affected_rows(&mysql) == 1 ? "row" : "rows"); + + pos=strend(buff); + if ((warnings= mysql_warning_count(&mysql))) { - init_pager(); - if (opt_html) - print_table_data_html(result); - else if (opt_xml) - print_table_data_xml(result); - else if (vertical) - print_table_data_vertically(result); - else if (opt_silent && verbose <= 2 && !output_tables) - print_tab_data(result); - else - print_table_data(result); - sprintf(buff,"%ld %s in set%s", - (long) mysql_num_rows(result), - (long) mysql_num_rows(result) == 1 ? "row" : "rows", - time_buff); - end_pager(); + *pos++= ','; + *pos++= ' '; + pos=int10_to_str(warnings, pos, 10); + pos=strmov(pos, " warning"); + if (warnings != 1) + *pos++= 's'; } - } - else if (mysql_affected_rows(&mysql) == ~(ulonglong) 0) - sprintf(buff,"Query OK%s",time_buff); - else - sprintf(buff,"Query OK, %ld %s affected%s", - (long) mysql_affected_rows(&mysql), - (long) mysql_affected_rows(&mysql) == 1 ? "row" : "rows", - time_buff); - put_info(buff,INFO_RESULT); - if (mysql_info(&mysql)) - put_info(mysql_info(&mysql),INFO_RESULT); - put_info("",INFO_RESULT); // Empty row - - if (result && !mysql_eof(result)) /* Something wrong when using quick */ - error=put_info(mysql_error(&mysql),INFO_ERROR,mysql_errno(&mysql)); - else if (unbuffered) - fflush(stdout); - mysql_free_result(result); + strmov(pos, time_buff); + put_info(buff,INFO_RESULT); + if (mysql_info(&mysql)) + put_info(mysql_info(&mysql),INFO_RESULT); + put_info("",INFO_RESULT); // Empty row + + if (result && !mysql_eof(result)) /* Something wrong when using quick */ + error= put_error(&mysql); + else if (unbuffered) + fflush(stdout); + mysql_free_result(result); + } while (!(err= mysql_next_result(&mysql))); + if (err >= 1) + error= put_error(&mysql); + + if (!error && !status.batch && + (mysql.server_status & SERVER_STATUS_DB_DROPPED)) + get_current_db(); + return error; /* New command follows */ } @@ -1616,9 +2039,8 @@ print_field_types(MYSQL_RES *result) MYSQL_FIELD *field; while ((field = mysql_fetch_field(result))) { - tee_fprintf(PAGER,"Name: '%s'\nTable: '%s'\nType: %d\nLength: %d\nMax length: %d\nIs_null: %d\nFlags: %d\nDecimals: %d\n\n", - field->name, - field->table ? "" : field->table, + tee_fprintf(PAGER,"Catalog: '%s'\nDatabase: '%s'\nTable: '%s'\nName: '%s'\nType: %d\nLength: %ld\nMax length: %ld\nIs_null: %d\nFlags: %u\nDecimals: %u\n\n", + field->catalog, field->db, field->table, field->name, (int) field->type, field->length, field->max_length, !IS_NOT_NULL(field->flags), @@ -1642,10 +2064,10 @@ print_table_data(MYSQL_RES *result) print_field_types(result); mysql_field_seek(result,0); } - separator.copy("+",1); + separator.copy("+",1,charset_info); while ((field = mysql_fetch_field(result))) { - uint length= column_names ? (uint) strlen(field->name) : 0; + uint length= column_names ? field->name_length : 0; if (quick) length=max(length,field->length); else @@ -1656,42 +2078,50 @@ print_table_data(MYSQL_RES *result) separator.fill(separator.length()+length+2,'-'); separator.append('+'); } - tee_puts(separator.c_ptr(), PAGER); + separator.append('\0'); // End marker for \0 + tee_puts((char*) separator.ptr(), PAGER); if (column_names) { mysql_field_seek(result,0); (void) tee_fputs("|", PAGER); for (uint off=0; (field = mysql_fetch_field(result)) ; off++) { - tee_fprintf(PAGER, " %-*s|",min(field->max_length,MAX_COLUMN_LENGTH), + tee_fprintf(PAGER, " %-*s|",(int) min(field->max_length, + MAX_COLUMN_LENGTH), field->name); num_flag[off]= IS_NUM(field->type); } (void) tee_fputs("\n", PAGER); - tee_puts(separator.c_ptr(), PAGER); + tee_puts((char*) separator.ptr(), PAGER); } while ((cur= mysql_fetch_row(result))) { + ulong *lengths= mysql_fetch_lengths(result); (void) tee_fputs("|", PAGER); mysql_field_seek(result, 0); for (uint off= 0; off < mysql_num_fields(result); off++) { const char *str= cur[off] ? cur[off] : "NULL"; field= mysql_fetch_field(result); - uint length= field->max_length; - if (length > MAX_COLUMN_LENGTH) + uint maxlength= field->max_length; + if (maxlength > MAX_COLUMN_LENGTH) { tee_fputs(str, PAGER); tee_fputs(" |", PAGER); } else - tee_fprintf(PAGER, num_flag[off] ? "%*s |" : " %-*s|", - length, str); + { + uint currlength= (uint) lengths[off]; + uint numcells= charset_info->cset->numcells(charset_info, + str, str + currlength); + tee_fprintf(PAGER, num_flag[off] ? "%*s |" : " %-*s|", + maxlength + currlength - numcells, str); + } } (void) tee_fputs("\n", PAGER); } - tee_puts(separator.c_ptr(), PAGER); + tee_puts((char*) separator.ptr(), PAGER); my_afree((gptr) num_flag); } @@ -1716,10 +2146,10 @@ print_table_data_html(MYSQL_RES *result) } while ((cur = mysql_fetch_row(result))) { + ulong *lengths=mysql_fetch_lengths(result); (void) tee_fputs("<TR>", PAGER); for (uint i=0; i < mysql_num_fields(result); i++) { - ulong *lengths=mysql_fetch_lengths(result); (void) tee_fputs("<TD>", PAGER); safe_put_field(cur[i],lengths[i]); (void) tee_fputs("</TD>", PAGER); @@ -1739,16 +2169,16 @@ print_table_data_xml(MYSQL_RES *result) mysql_field_seek(result,0); tee_fputs("<?xml version=\"1.0\"?>\n\n<resultset statement=\"", PAGER); - xmlencode_print(glob_buffer.ptr(), strlen(glob_buffer.ptr())); + xmlencode_print(glob_buffer.ptr(), (int)strlen(glob_buffer.ptr())); tee_fputs("\">", PAGER); fields = mysql_fetch_fields(result); while ((cur = mysql_fetch_row(result))) { + ulong *lengths=mysql_fetch_lengths(result); (void) tee_fputs("\n <row>\n", PAGER); for (uint i=0; i < mysql_num_fields(result); i++) { - ulong *lengths=mysql_fetch_lengths(result); tee_fprintf(PAGER, "\t<%s>", (fields[i].name ? (fields[i].name[0] ? fields[i].name : " ") : "NULL")); @@ -1772,7 +2202,7 @@ print_table_data_vertically(MYSQL_RES *result) while ((field = mysql_fetch_field(result))) { - uint length=(uint) strlen(field->name); + uint length= field->name_length; if (length > max_length) max_length= length; field->max_length=length; @@ -1837,8 +2267,9 @@ safe_put_field(const char *pos,ulong length) { #ifdef USE_MB int l; - if (use_mb(default_charset_info) && - (l = my_ismbchar(default_charset_info, pos, end))) { + if (use_mb(charset_info) && + (l = my_ismbchar(charset_info, pos, end))) + { while (l--) tee_putc(*pos++, PAGER); pos--; @@ -1898,7 +2329,7 @@ com_tee(String *buffer, char *line __attribute__((unused))) if (status.batch) return 0; - while (isspace(*line)) + while (my_isspace(charset_info,*line)) line++; if (!(param = strchr(line, ' '))) // if outfile wasn't given, use the default { @@ -1917,11 +2348,12 @@ com_tee(String *buffer, char *line __attribute__((unused))) } /* eliminate the spaces before the parameters */ - while (isspace(*param)) + while (my_isspace(charset_info,*param)) param++; end= strmake(file_name, param, sizeof(file_name) - 1); /* remove end space from command line */ - while (end > file_name && (isspace(end[-1]) || iscntrl(end[-1]))) + while (end > file_name && (my_isspace(charset_info,end[-1]) || + my_iscntrl(charset_info,end[-1]))) end--; end[0]= 0; if (end == file_name) @@ -1956,12 +2388,17 @@ com_pager(String *buffer, char *line __attribute__((unused))) if (status.batch) return 0; - /* Skip space from file name */ - while (isspace(*line)) + /* Skip spaces in front of the pager command */ + while (my_isspace(charset_info, *line)) line++; - if (!(param= strchr(line, ' '))) // if pager was not given, use the default + /* Skip the pager command */ + param= strchr(line, ' '); + /* Skip the spaces between the command and the argument */ + while (param && my_isspace(charset_info, *param)) + param++; + if (!param || !strlen(param)) // if pager was not given, use the default { - if (!default_pager[0]) + if (!default_pager_set) { tee_fprintf(stdout, "Default pager wasn't set, using stdout.\n"); opt_nopager=1; @@ -1973,17 +2410,16 @@ com_pager(String *buffer, char *line __attribute__((unused))) } else { - while (isspace(*param)) - param++; - end=strmake(pager_name, param, sizeof(pager_name)-1); - while (end > pager_name && (isspace(end[-1]) || iscntrl(end[-1]))) + end= strmake(pager_name, param, sizeof(pager_name)-1); + while (end > pager_name && (my_isspace(charset_info,end[-1]) || + my_iscntrl(charset_info,end[-1]))) end--; end[0]=0; strmov(pager, pager_name); strmov(default_pager, pager_name); } opt_nopager=0; - tee_fprintf(stdout, "PAGER set to %s\n", pager); + tee_fprintf(stdout, "PAGER set to '%s'\n", pager); return 0; } @@ -1994,6 +2430,7 @@ com_nopager(String *buffer __attribute__((unused)), { strmov(pager, "stdout"); opt_nopager=1; + PAGER= stdout; tee_fprintf(stdout, "PAGER set to stdout\n"); return 0; } @@ -2076,7 +2513,7 @@ com_shell(String *buffer, char *line __attribute__((unused))) char *shell_cmd; /* Skip space from line begin */ - while (isspace(*line)) + while (my_isspace(charset_info, *line)) line++; if (!(shell_cmd = strchr(line, ' '))) { @@ -2112,23 +2549,21 @@ com_print(String *buffer,char *line __attribute__((unused))) static int com_connect(String *buffer, char *line) { - char *tmp,buff[256]; + char *tmp, buff[256]; bool save_rehash= rehash; int error; + bzero(buff, sizeof(buff)); if (buffer) { - while (isspace(*line)) - line++; - strnmov(buff,line,sizeof(buff)-1); // Don't destroy history - if (buff[0] == '\\') // Short command - buff[1]=' '; - tmp=(char *) strtok(buff," \t"); // Skip connect command - if (tmp && (tmp=(char *) strtok(NullS," \t;"))) + strmov(buff, line); + tmp= get_arg(buff, 0); + if (tmp && *tmp) { - my_free(current_db,MYF(MY_ALLOW_ZERO_PTR)); - current_db=my_strdup(tmp,MYF(MY_WME)); - if ((tmp=(char *) strtok(NullS," \t;"))) + my_free(current_db, MYF(MY_ALLOW_ZERO_PTR)); + current_db= my_strdup(tmp, MYF(MY_WME)); + tmp= get_arg(buff, 1); + if (tmp) { my_free(current_host,MYF(MY_ALLOW_ZERO_PTR)); current_host=my_strdup(tmp,MYF(MY_WME)); @@ -2147,7 +2582,7 @@ com_connect(String *buffer, char *line) { sprintf(buff,"Connection id: %lu",mysql_thread_id(&mysql)); put_info(buff,INFO_INFO); - sprintf(buff,"Current database: %s\n", + sprintf(buff,"Current database: %.128s\n", current_db ? current_db : "*** NONE ***"); put_info(buff,INFO_INFO); } @@ -2164,15 +2599,16 @@ static int com_source(String *buffer, char *line) FILE *sql_file; /* Skip space from file name */ - while (isspace(*line)) + while (my_isspace(charset_info,*line)) line++; if (!(param = strchr(line, ' '))) // Skip command name return put_info("Usage: \\. <filename> | source <filename>", INFO_ERROR, 0); - while (isspace(*param)) + while (my_isspace(charset_info,*param)) param++; end=strmake(source_name,param,sizeof(source_name)-1); - while (end > source_name && (isspace(end[-1]) || iscntrl(end[-1]))) + while (end > source_name && (my_isspace(charset_info,end[-1]) || + my_iscntrl(charset_info,end[-1]))) end--; end[0]=0; unpack_filename(source_name,source_name); @@ -2208,53 +2644,37 @@ static int com_source(String *buffer, char *line) /* ARGSUSED */ static int -com_use(String *buffer __attribute__((unused)), char *line) +com_delimiter(String *buffer __attribute__((unused)), char *line) { - char tmp[FN_REFLEN], buff[FN_REFLEN + 1]; - MYSQL_RES *res; - MYSQL_ROW row; - char *c_buff, *c_tmp; + char buff[256], *tmp; - while (isspace(*line)) - line++; - strnmov(buff,line,sizeof(buff)-1); // Don't destroy history - if (buff[0] == '\\') // Short command - buff[1]=' '; - c_buff= buff; - while ((*c_buff != ' ') && (*c_buff != '\t')) // Skip connect command - c_buff++; - c_buff++; - - while ((*c_buff == ' ') || (*c_buff == '\t')) - c_buff++; - c_tmp= tmp; - if (*c_buff == '`') // Handling backticks - { - c_buff++; - for (; *c_buff; c_tmp++) - { - if (*c_buff == '`') - { - if (c_buff[1] == '`') - { - *c_tmp= '`'; - c_buff+= 2; - } - else - break; - } - else - *c_tmp= *(c_buff++); - } + strmake(buff, line, sizeof(buff) - 1); + tmp= get_arg(buff, 0); + + if (!tmp || !*tmp) + { + put_info("DELIMITER must be followed by a 'delimiter' character or string", + INFO_ERROR); + return 0; } - else - for (; !strchr(" \t;", *c_buff); c_buff++, c_tmp++) - *c_tmp= *c_buff; - *c_tmp= '\0'; - - if (!*tmp) + strmake(delimiter, tmp, sizeof(delimiter) - 1); + delimiter_length= (int)strlen(delimiter); + delimiter_str= delimiter; + return 0; +} + + /* ARGSUSED */ +static int +com_use(String *buffer __attribute__((unused)), char *line) +{ + char *tmp, buff[FN_REFLEN + 1]; + + bzero(buff, sizeof(buff)); + strmov(buff, line); + tmp= get_arg(buff, 0); + if (!tmp || !*tmp) { - put_info("USE must be followed by a database name",INFO_ERROR); + put_info("USE must be followed by a database name", INFO_ERROR); return 0; } /* @@ -2262,26 +2682,9 @@ com_use(String *buffer __attribute__((unused)), char *line) under our feet, for example if DROP DATABASE or RENAME DATABASE (latter one not yet available by the time the comment was written) */ - /* Let's reset current_db, assume it's gone */ - my_free(current_db, MYF(MY_ALLOW_ZERO_PTR)); - current_db= 0; - /* - We don't care about in case of an error below because current_db - was just set to 0. - */ - if (!mysql_query(&mysql, "SELECT DATABASE()") && - (res= mysql_use_result(&mysql))) - { - row= mysql_fetch_row(res); - if (row[0]) - { - current_db= my_strdup(row[0], MYF(MY_WME)); - } - (void) mysql_fetch_row(res); // Read eof - mysql_free_result(res); - } + get_current_db(); - if (!current_db || cmp_database(current_db,tmp)) + if (!current_db || cmp_database(charset_info, current_db,tmp)) { if (one_database) skip_updates= 1; @@ -2292,16 +2695,16 @@ com_use(String *buffer __attribute__((unused)), char *line) be down during query */ if (!connected && reconnect()) - return status.batch ? 1 : -1; // Fatal error + return opt_reconnect ? -1 : 1; // Fatal error if (mysql_select_db(&mysql,tmp)) { if (mysql_errno(&mysql) != CR_SERVER_GONE_ERROR) - return put_info(mysql_error(&mysql),INFO_ERROR,mysql_errno(&mysql)); + return put_error(&mysql); if (reconnect()) - return status.batch ? 1 : -1; // Fatal error + return opt_reconnect ? -1 : 1; // Fatal error if (mysql_select_db(&mysql,tmp)) - return put_info(mysql_error(&mysql),INFO_ERROR,mysql_errno(&mysql)); + return put_error(&mysql); } my_free(current_db,MYF(MY_ALLOW_ZERO_PTR)); current_db=my_strdup(tmp,MYF(MY_WME)); @@ -2317,6 +2720,68 @@ com_use(String *buffer __attribute__((unused)), char *line) } +/* + Gets argument from a command on the command line. If get_next_arg is + not defined, skips the command and returns the first argument. The + line is modified by adding zero to the end of the argument. If + get_next_arg is defined, then the function searches for end of string + first, after found, returns the next argument and adds zero to the + end. If you ever wish to use this feature, remember to initialize all + items in the array to zero first. +*/ + +char *get_arg(char *line, my_bool get_next_arg) +{ + char *ptr, *start; + my_bool quoted= 0, valid_arg= 0; + char qtype= 0; + + ptr= line; + if (get_next_arg) + { + for (; *ptr; ptr++) ; + if (*(ptr + 1)) + ptr++; + } + else + { + /* skip leading white spaces */ + while (my_isspace(charset_info, *ptr)) + ptr++; + if (*ptr == '\\') // short command was used + ptr+= 2; + else + while (*ptr &&!my_isspace(charset_info, *ptr)) // skip command + ptr++; + } + if (!*ptr) + return NullS; + while (my_isspace(charset_info, *ptr)) + ptr++; + if (*ptr == '\'' || *ptr == '\"' || *ptr == '`') + { + qtype= *ptr; + quoted= 1; + ptr++; + } + for (start=ptr ; *ptr; ptr++) + { + if (*ptr == '\\' && ptr[1]) // escaped character + { + // Remove the backslash + strmov(ptr, ptr+1); + } + else if ((!quoted && *ptr == ' ') || (quoted && *ptr == qtype)) + { + *ptr= 0; + break; + } + } + valid_arg= ptr != start; + return valid_arg ? start : NullS; +} + + static int sql_real_connect(char *host,char *database,char *user,char *password, uint silent) @@ -2335,6 +2800,8 @@ sql_real_connect(char *host,char *database,char *user,char *password, } if (opt_compress) mysql_options(&mysql,MYSQL_OPT_COMPRESS,NullS); + if (opt_secure_auth) + mysql_options(&mysql, MYSQL_SECURE_AUTH, (char *) &opt_secure_auth); if (using_opt_local_infile) mysql_options(&mysql,MYSQL_OPT_LOCAL_INFILE, (char*) &opt_local_infile); #ifdef HAVE_OPENSSL @@ -2342,6 +2809,12 @@ sql_real_connect(char *host,char *database,char *user,char *password, 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 if (safe_updates) { char init_command[100]; @@ -2350,22 +2823,26 @@ sql_real_connect(char *host,char *database,char *user,char *password, select_limit,max_join_size); mysql_options(&mysql, MYSQL_INIT_COMMAND, init_command); } + if (default_charset_used) + mysql_options(&mysql, MYSQL_SET_CHARSET_NAME, default_charset); if (!mysql_real_connect(&mysql, host, user, password, database, opt_mysql_port, opt_mysql_unix_port, - connect_flag)) + connect_flag | CLIENT_MULTI_STATEMENTS)) { if (!silent || (mysql_errno(&mysql) != CR_CONN_HOST_ERROR && mysql_errno(&mysql) != CR_CONNECTION_ERROR)) { - put_info(mysql_error(&mysql),INFO_ERROR,mysql_errno(&mysql)); + (void) put_error(&mysql); (void) fflush(stdout); return ignore_errors ? -1 : 1; // Abort } return -1; // Retryable } connected=1; +#ifndef EMBEDDED_LIBRARY mysql.reconnect=info_flag ? 1 : 0; // We want to know if this happens +#endif #ifdef HAVE_READLINE build_completion_hash(rehash, 1); #endif @@ -2413,13 +2890,20 @@ com_status(String *buffer __attribute__((unused)), char *line __attribute__((unused))) { const char *status; + char buff[22]; + ulonglong id; + MYSQL_RES *result; + LINT_INIT(result); + tee_puts("--------------", stdout); usage(1); /* Print version */ if (connected) { - MYSQL_RES *result; - LINT_INIT(result); tee_fprintf(stdout, "\nConnection id:\t\t%lu\n",mysql_thread_id(&mysql)); + /* + Don't remove "limit 1", + it is protection againts SQL_SELECT_LIMIT=0 + */ if (!mysql_query(&mysql,"select DATABASE(), USER() limit 1") && (result=mysql_use_result(&mysql))) { @@ -2430,7 +2914,7 @@ com_status(String *buffer __attribute__((unused)), tee_fprintf(stdout, "Current user:\t\t%s\n", cur[1]); } mysql_free_result(result); - } + } #ifdef HAVE_OPENSSL if (mysql.net.vio && mysql.net.vio->ssl_arg && SSL_get_cipher((SSL*) mysql.net.vio->ssl_arg)) @@ -2457,18 +2941,45 @@ com_status(String *buffer __attribute__((unused)), tee_fprintf(stdout, "Current pager:\t\t%s\n", pager); tee_fprintf(stdout, "Using outfile:\t\t'%s'\n", opt_outfile ? outfile : ""); #endif + tee_fprintf(stdout, "Using delimiter:\t%s\n", delimiter); tee_fprintf(stdout, "Server version:\t\t%s\n", mysql_get_server_info(&mysql)); tee_fprintf(stdout, "Protocol version:\t%d\n", mysql_get_proto_info(&mysql)); tee_fprintf(stdout, "Connection:\t\t%s\n", mysql_get_host_info(&mysql)); - tee_fprintf(stdout, "Client characterset:\t%s\n", - default_charset_info->name); - tee_fprintf(stdout, "Server characterset:\t%s\n", mysql.charset->name); + if ((id= mysql_insert_id(&mysql))) + tee_fprintf(stdout, "Insert id:\t\t%s\n", llstr(id, buff)); + + /* + Don't remove "limit 1", + it is protection againts SQL_SELECT_LIMIT=0 + */ + if (!mysql_query(&mysql,"select @@character_set_client, @@character_set_connection, @@character_set_server, @@character_set_database limit 1") && + (result=mysql_use_result(&mysql))) + { + MYSQL_ROW cur=mysql_fetch_row(result); + if (cur) + { + tee_fprintf(stdout, "Server characterset:\t%s\n", cur[2] ? cur[2] : ""); + tee_fprintf(stdout, "Db characterset:\t%s\n", cur[3] ? cur[3] : ""); + tee_fprintf(stdout, "Client characterset:\t%s\n", cur[0] ? cur[0] : ""); + tee_fprintf(stdout, "Conn. characterset:\t%s\n", cur[1] ? cur[1] : ""); + } + mysql_free_result(result); + } + else + { + /* Probably pre-4.1 server */ + tee_fprintf(stdout, "Client characterset:\t%s\n", charset_info->csname); + tee_fprintf(stdout, "Server characterset:\t%s\n", mysql.charset->csname); + } + +#ifndef EMBEDDED_LIBRARY if (strstr(mysql_get_host_info(&mysql),"TCP/IP") || ! mysql.unix_socket) tee_fprintf(stdout, "TCP port:\t\t%d\n", mysql.port); else tee_fprintf(stdout, "UNIX socket:\t\t%s\n", mysql.unix_socket); if (mysql.net.compress) tee_fprintf(stdout, "Protocol:\t\tCompressed\n"); +#endif if ((status=mysql_stat(&mysql)) && !mysql_error(&mysql)[0]) { @@ -2507,7 +3018,7 @@ select_limit, max_join_size); static int -put_info(const char *str,INFO_TYPE info_type,uint error) +put_info(const char *str,INFO_TYPE info_type, uint error, const char *sqlstate) { FILE *file= (info_type == INFO_ERROR ? stderr : stdout); static int inited=0; @@ -2519,7 +3030,12 @@ put_info(const char *str,INFO_TYPE info_type,uint error) (void) fflush(file); fprintf(file,"ERROR"); if (error) - (void) fprintf(file," %d",error); + { + if (sqlstate) + (void) fprintf(file," %d (%s)",error, sqlstate); + else + (void) fprintf(file," %d",error); + } if (status.query_start_line && line_numbers) { (void) fprintf(file," at line %lu",status.query_start_line); @@ -2552,7 +3068,12 @@ put_info(const char *str,INFO_TYPE info_type,uint error) putchar('\007'); /* This should make a bell */ vidattr(A_STANDOUT); if (error) - (void) tee_fprintf(file, "ERROR %d: ", error); + { + if (sqlstate) + (void) tee_fprintf(file, "ERROR %d (%s): ", error, sqlstate); + else + (void) tee_fprintf(file, "ERROR %d: ", error); + } else tee_puts("ERROR: ", file); } @@ -2567,11 +3088,19 @@ put_info(const char *str,INFO_TYPE info_type,uint error) } +static int +put_error(MYSQL *mysql) +{ + return put_info(mysql_error(mysql), INFO_ERROR, mysql_errno(mysql), + mysql_sqlstate(mysql)); +} + + static void remove_cntrl(String &buffer) { char *start,*end; end=(start=(char*) buffer.ptr())+buffer.length(); - while (start < end && !isgraph(end[-1])) + while (start < end && !my_isgraph(charset_info,end[-1])) end--; buffer.length((uint) (end-start)); } @@ -2587,9 +3116,14 @@ void tee_fprintf(FILE *file, const char *fmt, ...) #ifdef OS2 fflush( file); #endif + va_end(args); + if (opt_outfile) + { + va_start(args, fmt); (void) vfprintf(OUTFILE, fmt, args); - va_end(args); + va_end(args); + } } @@ -2658,21 +3192,21 @@ static void nice_time(double sec,char *buff,bool part_second) { tmp=(ulong) floor(sec/(3600.0*24)); sec-=3600.0*24*tmp; - buff=int2str((long) tmp,buff,10); + buff=int10_to_str((long) tmp, buff, 10); buff=strmov(buff,tmp > 1 ? " days " : " day "); } if (sec >= 3600.0) { tmp=(ulong) floor(sec/3600.0); sec-=3600.0*tmp; - buff=int2str((long) tmp,buff,10); + buff=int10_to_str((long) tmp, buff, 10); buff=strmov(buff,tmp > 1 ? " hours " : " hour "); } if (sec >= 60.0) { tmp=(ulong) floor(sec/60.0); sec-=60.0*tmp; - buff=int2str((long) tmp,buff,10); + buff=int10_to_str((long) tmp, buff, 10); buff=strmov(buff," min "); } if (part_second) @@ -2740,19 +3274,29 @@ static const char* construct_prompt() break; } case 'p': + { +#ifndef EMBEDDED_LIBRARY if (!connected) { processed_prompt.append("not_connected"); break; } - if (strstr(mysql_get_host_info(&mysql),"TCP/IP") || ! - mysql.unix_socket) + + const char *host_info = mysql_get_host_info(&mysql); + if (strstr(host_info, "memory")) + { + processed_prompt.append( mysql.host ); + } + else if (strstr(host_info,"TCP/IP") || + !mysql.unix_socket) add_int_to_prompt(mysql.port); else { char *pos=strrchr(mysql.unix_socket,'/'); - processed_prompt.append(pos ? pos+1 : mysql.unix_socket); + processed_prompt.append(pos ? pos+1 : mysql.unix_socket); } +#endif + } break; case 'U': if (!full_username) diff --git a/client/mysqladmin.c b/client/mysqladmin.cc index 98e54b695b8..da790bce375 100644 --- a/client/mysqladmin.c +++ b/client/mysqladmin.cc @@ -16,20 +16,25 @@ /* maintaince of mysql databases */ - #include "client_priv.h" #include <signal.h> #ifdef THREAD #include <my_pthread.h> /* because of signal() */ #endif #include <sys/stat.h> +#include <mysql.h> + +#ifdef LATER_HAVE_NDBCLUSTER_DB +#include "../ndb/src/mgmclient/ndb_mgmclient.h" +#endif -#define ADMIN_VERSION "8.40" +#define ADMIN_VERSION "8.41" #define MAX_MYSQL_VAR 256 #define SHUTDOWN_DEF_TIMEOUT 3600 /* Wait for shutdown */ #define MAX_TRUNC_LENGTH 3 -char *host= NULL, *user= 0, *opt_password= 0; +char *host= NULL, *user= 0, *opt_password= 0, + *default_charset= NULL; char truncated_var_names[MAX_MYSQL_VAR][MAX_TRUNC_LENGTH]; char ex_var_names[MAX_MYSQL_VAR][FN_REFLEN]; ulonglong last_values[MAX_MYSQL_VAR]; @@ -41,6 +46,15 @@ static uint tcp_port = 0, option_wait = 0, option_silent=0, nr_iterations, opt_count_iterations= 0; static ulong opt_connect_timeout, opt_shutdown_timeout; static my_string unix_port=0; +#ifdef LATER_HAVE_NDBCLUSTER_DB +static my_bool opt_ndbcluster=0; +static char *opt_ndb_connectstring=0; +#endif + +#ifdef HAVE_SMEM +static char *shared_memory_base_name=0; +#endif +static uint opt_protocol=0; /* When using extended-status relatively, ex_val_max_len is the estimated @@ -78,16 +92,19 @@ static void store_values(MYSQL_RES *result); The order of commands must be the same as command_names, except ADMIN_ERROR */ -enum commands { - ADMIN_ERROR, +enum commands { + ADMIN_ERROR, ADMIN_CREATE, ADMIN_DROP, ADMIN_SHUTDOWN, - ADMIN_RELOAD, ADMIN_REFRESH, ADMIN_VER, - ADMIN_PROCESSLIST, ADMIN_STATUS, ADMIN_KILL, - ADMIN_DEBUG, ADMIN_VARIABLES, ADMIN_FLUSH_LOGS, - ADMIN_FLUSH_HOSTS, ADMIN_FLUSH_TABLES, ADMIN_PASSWORD, - ADMIN_PING, ADMIN_EXTENDED_STATUS, ADMIN_FLUSH_STATUS, - ADMIN_FLUSH_PRIVILEGES, ADMIN_START_SLAVE, ADMIN_STOP_SLAVE, - ADMIN_FLUSH_THREADS + ADMIN_RELOAD, ADMIN_REFRESH, ADMIN_VER, + ADMIN_PROCESSLIST, ADMIN_STATUS, ADMIN_KILL, + ADMIN_DEBUG, ADMIN_VARIABLES, ADMIN_FLUSH_LOGS, + ADMIN_FLUSH_HOSTS, ADMIN_FLUSH_TABLES, ADMIN_PASSWORD, + ADMIN_PING, ADMIN_EXTENDED_STATUS, ADMIN_FLUSH_STATUS, + ADMIN_FLUSH_PRIVILEGES, ADMIN_START_SLAVE, ADMIN_STOP_SLAVE, + ADMIN_FLUSH_THREADS, ADMIN_OLD_PASSWORD +#ifdef LATER_HAVE_NDBCLUSTER_DB + ,ADMIN_NDB_MGM +#endif }; static const char *command_names[]= { "create", "drop", "shutdown", @@ -96,13 +113,16 @@ static const char *command_names[]= { "debug", "variables", "flush-logs", "flush-hosts", "flush-tables", "password", "ping", "extended-status", "flush-status", - "flush-privileges", "start-slave", "stop-slave", - "flush-threads", + "flush-privileges", "start-slave", "stop-slave", + "flush-threads","old-password", +#ifdef LATER_HAVE_NDBCLUSTER_DB + "ndb-mgm", +#endif NullS }; static TYPELIB command_typelib= -{ array_elements(command_names)-1,"commands", command_names}; +{ array_elements(command_names)-1,"commands", command_names, NULL}; static struct my_option my_long_options[] = { @@ -111,24 +131,27 @@ static struct my_option my_long_options[] = 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, #endif {"count", 'c', - "Number of iterations to make. This works with -i (--sleep) only", + "Number of iterations to make. This works with -i (--sleep) only.", (gptr*) &nr_iterations, (gptr*) &nr_iterations, 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"debug", '#', "Output debug log. Often this is 'd:t:o,filename'", + {"debug", '#', "Output debug log. Often this is 'd:t:o,filename'.", 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, {"force", 'f', "Don't ask for confirmation on drop database; with multiple commands, continue even if an error occurs.", (gptr*) &option_force, (gptr*) &option_force, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"compress", 'C', "Use compression in server/client protocol", + {"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}, {"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}, + {"default-character-set", OPT_DEFAULT_CHARSET, + "Set the default character set.", (gptr*) &default_charset, + (gptr*) &default_charset, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"host", 'h', "Connect to host", (gptr*) &host, (gptr*) &host, 0, GET_STR, + {"host", 'h', "Connect to host.", (gptr*) &host, (gptr*) &host, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"password", 'p', "Password to use when connecting to server. If password is not given it's asked from the tty.", @@ -139,6 +162,8 @@ static struct my_option my_long_options[] = #endif {"port", 'P', "Port number to use for connection.", (gptr*) &tcp_port, (gptr*) &tcp_port, 0, GET_UINT, REQUIRED_ARG, MYSQL_PORT, 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}, {"relative", 'r', "Show difference between current and previous values when used with -i. Currently works only with extended-status.", (gptr*) &opt_relative, (gptr*) &opt_relative, 0, GET_BOOL, NO_ARG, 0, 0, 0, @@ -146,7 +171,12 @@ static struct my_option my_long_options[] = {"set-variable", 'O', "Change the value of a variable. Please note that this option is deprecated; you can set variables directly with --variable-name=value.", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"silent", 's', "Silently exit if one can't connect to server", +#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', "Silently exit if one can't connect to server.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, {"socket", 'S', "Socket file to use for connection.", (gptr*) &unix_port, (gptr*) &unix_port, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, @@ -161,13 +191,13 @@ static struct my_option my_long_options[] = #endif {"verbose", 'v', "Write more information.", (gptr*) &opt_verbose, (gptr*) &opt_verbose, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"version", 'V', "Output version information and exit", 0, 0, 0, GET_NO_ARG, + {"version", 'V', "Output version information and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"vertical", 'E', + {"vertical", 'E', "Print output vertically. Is similar to --relative, but prints output vertically.", (gptr*) &opt_vertical, (gptr*) &opt_vertical, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"wait", 'w', "Wait and retry if connection is down", 0, 0, 0, GET_UINT, + {"wait", 'w', "Wait and retry if connection is down.", 0, 0, 0, GET_UINT, OPT_ARG, 0, 0, 0, 0, 0, 0}, {"connect_timeout", OPT_CONNECT_TIMEOUT, "", (gptr*) &opt_connect_timeout, (gptr*) &opt_connect_timeout, 0, GET_ULONG, REQUIRED_ARG, 3600*12, 0, @@ -175,6 +205,14 @@ static struct my_option my_long_options[] = {"shutdown_timeout", OPT_SHUTDOWN_TIMEOUT, "", (gptr*) &opt_shutdown_timeout, (gptr*) &opt_shutdown_timeout, 0, GET_ULONG, REQUIRED_ARG, SHUTDOWN_DEF_TIMEOUT, 0, 3600*12, 0, 1, 0}, +#ifdef LATER_HAVE_NDBCLUSTER_DB + {"ndbcluster", OPT_NDBCLUSTER, "" + "", (gptr*) &opt_ndbcluster, + (gptr*) &opt_ndbcluster, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"ndb-connectstring", OPT_NDB_CONNECTSTRING, "" + "", (gptr*) &opt_ndb_connectstring, + (gptr*) &opt_ndb_connectstring, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, +#endif { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} }; @@ -205,6 +243,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), while (*argument) *argument++= 'x'; /* Destroy argument */ if (*start) start[1]=0; /* Cut length of argument */ + tty_password= 0; } else tty_password=1; @@ -214,7 +253,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), break; case 'W': #ifdef __WIN__ - unix_port=MYSQL_NAMEDPIPE; + opt_protocol = MYSQL_PROTOCOL_PIPE; #endif break; case '#': @@ -232,7 +271,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), option_wait=1; } else - option_wait= ~0; + option_wait= ~(uint)0; break; case '?': case 'I': /* Info */ @@ -243,6 +282,15 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), charsets_dir = argument; #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; + } } if (error) { @@ -293,6 +341,14 @@ int main(int argc,char *argv[]) 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 + if (default_charset) + mysql_options(&mysql, MYSQL_SET_CHARSET_NAME, default_charset); if (sql_connect(&mysql, option_wait)) { unsigned int err= mysql_errno(&mysql); @@ -350,6 +406,9 @@ int main(int argc,char *argv[]) } my_free(opt_password,MYF(MY_ALLOW_ZERO_PTR)); my_free(user,MYF(MY_ALLOW_ZERO_PTR)); +#ifdef HAVE_SMEM + my_free(shared_memory_base_name,MYF(MY_ALLOW_ZERO_PTR)); +#endif free_defaults(save_argv); my_end(0); exit(error ? 1 : 0); @@ -410,7 +469,7 @@ static my_bool sql_connect(MYSQL *mysql, uint wait) wait--; /* One less retry */ if ((mysql_errno(mysql) != CR_CONN_HOST_ERROR) && (mysql_errno(mysql) != CR_CONNECTION_ERROR)) - { + { fprintf(stderr,"Got error: %s\n", mysql_error(mysql)); if (!option_force) return 1; @@ -425,7 +484,7 @@ static my_bool sql_connect(MYSQL *mysql, uint wait) } else { - putc('.',stderr); + putc('.',stderr); (void) fflush(stderr); } } @@ -444,7 +503,6 @@ static my_bool sql_connect(MYSQL *mysql, uint wait) static int execute_commands(MYSQL *mysql,int argc, char **argv) { const char *status; - /* MySQL documentation relies on the fact that mysqladmin will execute commands in the order specified, e.g. @@ -453,6 +511,8 @@ static int execute_commands(MYSQL *mysql,int argc, char **argv) If this behaviour is ever changed, Docs should be notified. */ + struct rand_struct rand_st; + for (; argc > 0 ; argv++,argc--) { switch (find_type(argv[0],&command_typelib,2)) { @@ -501,7 +561,7 @@ static int execute_commands(MYSQL *mysql,int argc, char **argv) !stat(pidfile, &pidfile_status)) last_modified= pidfile_status.st_mtime; - if (mysql_shutdown(mysql)) + if (mysql_shutdown(mysql, SHUTDOWN_DEFAULT)) { my_printf_error(0,"shutdown failed; error: '%s'",MYF(ME_BELL), mysql_error(mysql)); @@ -521,7 +581,7 @@ static int execute_commands(MYSQL *mysql,int argc, char **argv) } case ADMIN_FLUSH_PRIVILEGES: case ADMIN_RELOAD: - if (mysql_refresh(mysql,REFRESH_GRANT) < 0) + if (mysql_query(mysql,"flush privileges")) { my_printf_error(0,"reload failed; error: '%s'",MYF(ME_BELL), mysql_error(mysql)); @@ -532,7 +592,7 @@ static int execute_commands(MYSQL *mysql,int argc, char **argv) if (mysql_refresh(mysql, (uint) ~(REFRESH_GRANT | REFRESH_STATUS | REFRESH_READ_LOCK | REFRESH_SLAVE | - REFRESH_MASTER)) < 0) + REFRESH_MASTER))) { my_printf_error(0,"refresh failed; error: '%s'",MYF(ME_BELL), mysql_error(mysql)); @@ -540,7 +600,7 @@ static int execute_commands(MYSQL *mysql,int argc, char **argv) } break; case ADMIN_FLUSH_THREADS: - if (mysql_refresh(mysql,(uint) REFRESH_THREADS) < 0) + if (mysql_refresh(mysql,(uint) REFRESH_THREADS)) { my_printf_error(0,"refresh failed; error: '%s'",MYF(ME_BELL), mysql_error(mysql)); @@ -663,7 +723,7 @@ static int execute_commands(MYSQL *mysql,int argc, char **argv) MYSQL_RES *res; MYSQL_ROW row; uint rownr = 0; - void (*func) (MYSQL_RES*, MYSQL_ROW, uint); + void (*func) (MYSQL_RES*, MYSQL_ROW, uint); new_line = 1; if (mysql_query(mysql, "show status") || @@ -727,7 +787,7 @@ static int execute_commands(MYSQL *mysql,int argc, char **argv) } case ADMIN_FLUSH_HOSTS: { - if (mysql_refresh(mysql,REFRESH_HOSTS)) + if (mysql_query(mysql,"flush hosts")) { my_printf_error(0,"refresh failed; error: '%s'",MYF(ME_BELL), mysql_error(mysql)); @@ -737,7 +797,7 @@ static int execute_commands(MYSQL *mysql,int argc, char **argv) } case ADMIN_FLUSH_TABLES: { - if (mysql_refresh(mysql,REFRESH_TABLES)) + if (mysql_query(mysql,"flush tables")) { my_printf_error(0,"refresh failed; error: '%s'",MYF(ME_BELL), mysql_error(mysql)); @@ -747,7 +807,7 @@ static int execute_commands(MYSQL *mysql,int argc, char **argv) } case ADMIN_FLUSH_STATUS: { - if (mysql_refresh(mysql,REFRESH_STATUS)) + if (mysql_query(mysql,"flush status")) { my_printf_error(0,"refresh failed; error: '%s'",MYF(ME_BELL), mysql_error(mysql)); @@ -755,9 +815,14 @@ static int execute_commands(MYSQL *mysql,int argc, char **argv) } break; } + case ADMIN_OLD_PASSWORD: case ADMIN_PASSWORD: { - char buff[128],crypted_pw[33]; + char buff[128],crypted_pw[64]; + time_t start_time; + /* Do initialization the same way as we do in mysqld */ + start_time=time((time_t*) 0); + randominit(&rand_st,(ulong) start_time,(ulong) start_time/2); if (argc < 2) { @@ -765,7 +830,54 @@ static int execute_commands(MYSQL *mysql,int argc, char **argv) return 1; } if (argv[1][0]) - make_scrambled_password(crypted_pw,argv[1]); + { + char *pw= argv[1]; + bool old= (find_type(argv[0], &command_typelib, 2) == + ADMIN_OLD_PASSWORD); +#ifdef __WIN__ + uint pw_len= strlen(pw); + if (pw_len > 1 && pw[0] == '\'' && pw[pw_len-1] == '\'') + printf("Warning: single quotes were not trimmed from the password by" + " your command\nline client, as you might have expected.\n"); +#endif + /* + If we don't already know to use an old-style password, see what + the server is using + */ + if (!old) + { + if (mysql_query(mysql, "SHOW VARIABLES LIKE 'old_passwords'")) + { + my_printf_error(0, "Could not determine old_passwords setting from server; error: '%s'", + MYF(ME_BELL),mysql_error(mysql)); + return -1; + } + else + { + MYSQL_RES *res= mysql_store_result(mysql); + if (!res) + { + my_printf_error(0, + "Could not get old_passwords setting from " + "server; error: '%s'", + MYF(ME_BELL),mysql_error(mysql)); + return -1; + } + if (!mysql_num_rows(res)) + old= 1; + else + { + MYSQL_ROW row= mysql_fetch_row(res); + old= !strncmp(row[1], "ON", 2); + } + mysql_free_result(res); + } + } + if (old) + make_scrambled_password_323(crypted_pw, pw); + else + make_scrambled_password(crypted_pw, pw); + } else crypted_pw[0]=0; /* No password */ sprintf(buff,"set password='%s',sql_log_off=0",crypted_pw); @@ -778,16 +890,33 @@ static int execute_commands(MYSQL *mysql,int argc, char **argv) } if (mysql_query(mysql,buff)) { - my_printf_error(0,"unable to change password; error: '%s'", - MYF(ME_BELL),mysql_error(mysql)); - return -1; + if (mysql_errno(mysql)!=1290) + { + my_printf_error(0,"unable to change password; error: '%s'", + MYF(ME_BELL),mysql_error(mysql)); + return -1; + } + else + { + /* + We don't try to execute 'update mysql.user set..' + because we can't perfectly find out the host + */ + my_printf_error(0,"\n" + "You cannot use 'password' command as mysqld runs\n" + " with grant tables disabled (was started with" + " --skip-grant-tables).\n" + "Use: \"mysqladmin flush-privileges password '*'\"" + " instead", MYF(ME_BELL)); + return -1; + } } argc--; argv++; break; } case ADMIN_START_SLAVE: - if (mysql_query(mysql, "SLAVE START")) + if (mysql_query(mysql, "START SLAVE")) { my_printf_error(0, "Error starting slave: %s", MYF(ME_BELL), mysql_error(mysql)); @@ -797,7 +926,7 @@ static int execute_commands(MYSQL *mysql,int argc, char **argv) puts("Slave started"); break; case ADMIN_STOP_SLAVE: - if (mysql_query(mysql, "SLAVE STOP")) + if (mysql_query(mysql, "STOP SLAVE")) { my_printf_error(0, "Error stopping slave: %s", MYF(ME_BELL), mysql_error(mysql)); @@ -831,6 +960,24 @@ static int execute_commands(MYSQL *mysql,int argc, char **argv) } mysql->reconnect=1; /* Automatic reconnect is default */ break; +#ifdef LATER_HAVE_NDBCLUSTER_DB + case ADMIN_NDB_MGM: + { + if (argc < 2) + { + my_printf_error(0,"Too few arguments to ndb-mgm",MYF(ME_BELL)); + return 1; + } + { + Ndb_mgmclient_handle cmd= + ndb_mgmclient_handle_create(opt_ndb_connectstring); + ndb_mgmclient_execute(cmd, --argc, ++argv); + ndb_mgmclient_handle_destroy(cmd); + } + argc= 0; + } + break; +#endif default: my_printf_error(0,"Unknown command: '%-.60s'",MYF(ME_BELL),argv[0]); return 1; @@ -861,6 +1008,7 @@ static void usage(void) print_defaults("my",load_default_groups); puts("\nWhere command is a one or more of: (Commands may be shortened)\n\ create databasename Create a new database\n\ + debug Instruct server to write debug information to log\n\ drop databasename Delete a database and all its tables\n\ extended-status Gives an extended status message from the server\n\ flush-hosts Flush all cached hosts\n\ @@ -872,7 +1020,8 @@ static void usage(void) kill id,id,... Kill mysql threads"); #if MYSQL_VERSION_ID >= 32200 puts("\ - password new-password Change old password to new-password"); + password new-password Change old password to new-password, MySQL 4.1 hashing.\n\ + old-password new-password Change old password to new-password in old format.\n"); #endif puts("\ ping Check if mysqld is alive\n\ @@ -925,24 +1074,24 @@ static void nice_time(ulong sec,char *buff) { tmp=sec/(3600L*24); sec-=3600L*24*tmp; - buff=int2str(tmp,buff,10); + buff=int10_to_str(tmp, buff, 10); buff=strmov(buff,tmp > 1 ? " days " : " day "); } if (sec >= 3600L) { tmp=sec/3600L; sec-=3600L*tmp; - buff=int2str(tmp,buff,10); + buff=int10_to_str(tmp, buff, 10); buff=strmov(buff,tmp > 1 ? " hours " : " hour "); } if (sec >= 60) { tmp=sec/60; sec-=60*tmp; - buff=int2str(tmp,buff,10); + buff=int10_to_str(tmp, buff, 10); buff=strmov(buff," min "); } - strmov(int2str(sec,buff,10)," sec"); + strmov(int10_to_str(sec, buff, 10)," sec"); } @@ -1032,7 +1181,7 @@ static void print_relative_row_vert(MYSQL_RES *result __attribute__((unused)), putchar('|'); tmp = cur[1] ? strtoull(cur[1], NULL, 10) : (ulonglong) 0; - printf(" %-*s|", ex_val_max_len[row] + 1, + printf(" %-*s|", ex_val_max_len[row] + 1, llstr((tmp - last_values[row]), buff)); /* Find the minimum row length needed to output the relative value */ @@ -1196,3 +1345,6 @@ static my_bool wait_pidfile(char *pidfile, time_t last_modified, } DBUG_RETURN(error); } +#ifdef __GNUC__ +FIX_GCC_LINKING_PROBLEM +#endif diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc index 9ae280a997d..802d5081ad6 100644 --- a/client/mysqlbinlog.cc +++ b/client/mysqlbinlog.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2001 MySQL AB +/* Copyright (C) 2001-2004 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 @@ -17,8 +17,7 @@ #define MYSQL_CLIENT #undef MYSQL_SERVER #include "client_priv.h" -#include <time.h> -#include <assert.h> +#include <my_time.h> #include "log_event.h" #define BIN_LOG_HEADER_SIZE 4 @@ -33,8 +32,9 @@ ulong server_id = 0; // needed by net_serv.c ulong bytes_sent = 0L, bytes_received = 0L; ulong mysqld_net_retry_count = 10L; +ulong open_files_limit; uint test_flags = 0; - +static uint opt_protocol= 0; static FILE *result_file; #ifndef DBUG_OFF @@ -44,7 +44,7 @@ static const char *load_default_groups[]= { "mysqlbinlog","client",0 }; void sql_print_error(const char *format, ...); -static bool one_database = 0; +static bool one_database=0, to_last_remote_log= 0, disable_log_bin= 0; static const char* database= 0; static my_bool force_opt= 0, short_form= 0, remote_opt= 0; static ulonglong offset = 0; @@ -53,11 +53,18 @@ static int port = MYSQL_PORT; static const char* sock= 0; static const char* user = 0; static char* pass = 0; -static ulonglong position = 0; + +static ulonglong start_position, stop_position; +#define start_position_mot ((my_off_t)start_position) +#define stop_position_mot ((my_off_t)stop_position) + +static char *start_datetime_str, *stop_datetime_str; +static my_time_t start_datetime= 0, stop_datetime= MY_TIME_T_MAX; +static ulonglong rec_count= 0; static short binlog_flags = 0; static MYSQL* mysql = NULL; - static const char* dirname_for_local_load= 0; +static bool stop_passed= 0; static int dump_local_log_entries(const char* logname); static int dump_remote_log_entries(const char* logname); @@ -69,7 +76,7 @@ static MYSQL* safe_connect(); class Load_log_processor { - char target_dir_name[MY_NFILE]; + char target_dir_name[FN_REFLEN]; int target_dir_name_len; DYNAMIC_ARRAY file_names; @@ -197,7 +204,7 @@ int Load_log_processor::load_old_format_file(NET* net, const char*server_fname, for (;;) { - uint packet_len = my_net_read(net); + ulong packet_len = my_net_read(net); if (packet_len == 0) { if (my_net_write(net, "", 0) || net_flush(net)) @@ -219,7 +226,13 @@ int Load_log_processor::load_old_format_file(NET* net, const char*server_fname, return -1; } - if (my_write(file, (byte*) net->read_pos, packet_len,MYF(MY_WME|MY_NABP))) + if (packet_len > UINT_MAX) + { + sql_print_error("Illegal length of packet read from net"); + return -1; + } + if (my_write(file, (byte*) net->read_pos, + (uint) packet_len, MYF(MY_WME|MY_NABP))) return -1; } @@ -235,15 +248,16 @@ int Load_log_processor::process(Create_file_log_event *ce) int error= 0; char *fname, *ptr; File file; + DBUG_ENTER("Load_log_processor::process"); if (set_dynamic(&file_names,(gptr)&ce,ce->file_id)) { sql_print_error("Could not construct local filename %s%s", target_dir_name,bname); - return -1; + DBUG_RETURN(-1); } if (!(fname= my_malloc(full_len,MYF(MY_WME)))) - return -1; + DBUG_RETURN(-1); memcpy(fname, target_dir_name, target_dir_name_len); ptr= fname + target_dir_name_len; @@ -255,20 +269,21 @@ int Load_log_processor::process(Create_file_log_event *ce) { sql_print_error("Could not construct local filename %s%s", target_dir_name,bname); - return -1; + DBUG_RETURN(-1); } ce->set_fname_outside_temp_buf(fname,strlen(fname)); if (my_write(file,(byte*) ce->block,ce->block_len,MYF(MY_WME|MY_NABP))) error= -1; - if (my_close(file,MYF(MY_WME))) + if (my_close(file, MYF(MY_WME))) error= -1; - return error; + DBUG_RETURN(error); } int Load_log_processor::process(Append_block_log_event *ae) { + DBUG_ENTER("Load_log_processor::process"); Create_file_log_event* ce= ((ae->file_id < file_names.elements) ? *((Create_file_log_event**)file_names.buffer + ae->file_id) : @@ -280,12 +295,12 @@ int Load_log_processor::process(Append_block_log_event *ae) int error= 0; if (((file= my_open(ce->fname, O_APPEND|O_BINARY|O_WRONLY,MYF(MY_WME))) < 0)) - return -1; + DBUG_RETURN(-1); if (my_write(file,(byte*)ae->block,ae->block_len,MYF(MY_WME|MY_NABP))) error= -1; if (my_close(file,MYF(MY_WME))) error= -1; - return error; + DBUG_RETURN(error); } /* @@ -295,20 +310,42 @@ int Load_log_processor::process(Append_block_log_event *ae) */ fprintf(stderr,"Warning: ignoring Append_block as there is no \ Create_file event for file_id: %u\n",ae->file_id); - return -1; + DBUG_RETURN(-1); } Load_log_processor load_processor; +/* + RETURN + 0 ok and continue + 1 error and terminate + -1 ok and terminate -int process_event(ulonglong *rec_count, char *last_db, Log_event *ev, - my_off_t pos, int old_format) + TODO + This function returns 0 even in some error cases. This should be changed. +*/ +int process_event(char *last_db, Log_event *ev, my_off_t pos, int old_format) { char ll_buff[21]; + DBUG_ENTER("process_event"); - if ((*rec_count) >= offset) + if ((rec_count >= offset) && + ((my_time_t)(ev->when) >= start_datetime)) { + /* + We have found an event after start_datetime, from now on print + everything (in case the binlog has timestamps increasing and decreasing, + we do this to avoid cutting the middle). + */ + start_datetime= 0; + offset= 0; // print everything and protect against cycling rec_count + if (((my_time_t)(ev->when) >= stop_datetime) + || (pos >= stop_position_mot)) + { + stop_passed= 1; // skip all next binlogs + DBUG_RETURN(-1); + } if (!short_form) fprintf(result_file, "# at %s\n",llstr(pos,ll_buff)); @@ -318,11 +355,7 @@ int process_event(ulonglong *rec_count, char *last_db, Log_event *ev, { const char * log_dbname = ((Query_log_event*)ev)->db; if ((log_dbname != NULL) && (strcmp(log_dbname, database))) - { - (*rec_count)++; - delete ev; - return 0; // Time for next event - } + goto end; } ev->print(result_file, short_form, last_db); break; @@ -339,11 +372,7 @@ int process_event(ulonglong *rec_count, char *last_db, Log_event *ev, */ const char * log_dbname = ce->db; if ((log_dbname != NULL) && (strcmp(log_dbname, database))) - { - (*rec_count)++; - delete ev; - return 0; // next - } + goto end; // Next event } /* We print the event, but with a leading '#': this is just to inform @@ -391,10 +420,12 @@ Create_file event for file_id: %u\n",exv->file_id); ev->print(result_file, short_form, last_db); } } - (*rec_count)++; + +end: + rec_count++; if (ev) delete ev; - return 0; + DBUG_RETURN(0); } @@ -412,6 +443,13 @@ static struct my_option my_long_options[] = {"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 " + "when restoring after a crash to avoid duplication of the statements you " + "already have. NOTE: you will need a SUPER privilege to use this option.", + (gptr*) &disable_log_bin, (gptr*) &disable_log_bin, 0, GET_BOOL, + NO_ARG, 0, 0, 0, 0, 0, 0}, {"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}, @@ -426,20 +464,64 @@ static struct my_option my_long_options[] = {"port", 'P', "Use port to connect to the remote server.", (gptr*) &port, (gptr*) &port, 0, GET_INT, REQUIRED_ARG, MYSQL_PORT, 0, 0, 0, 0, 0}, - {"position", 'j', "Start reading the binlog at position N.", - (gptr*) &position, (gptr*) &position, 0, GET_ULL, REQUIRED_ARG, 0, 0, 0, 0, - 0, 0}, + {"position", 'j', "Deprecated. Use --start-position instead.", + (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}, + {"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}, {"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}, {"socket", 'S', "Socket file to use for connection.", (gptr*) &sock, (gptr*) &sock, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"start-datetime", OPT_START_DATETIME, + "Start reading the binlog at first event having a datetime equal or " + "posterior to the argument; the argument must be a date and time " + "in the local time zone, in any format accepted by the MySQL server " + "for DATETIME and TIMESTAMP types, for example: 2004-12-25 11:25:56 " + "(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}, + {"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 " + "in the local time zone, in any format accepted by the MySQL server " + "for DATETIME and TIMESTAMP types, for example: 2004-12-25 11:25:56 " + "(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.", + (gptr*) &stop_position, (gptr*) &stop_position, 0, GET_ULL, + REQUIRED_ARG, (ulonglong)(~(my_off_t)0), BIN_LOG_HEADER_SIZE, + (ulonglong)(~(my_off_t)0), 0, 0, 0}, + {"to-last-log", 't', "Requires -R. Will not stop at the end of the \ +requested binlog but rather continue printing until the end of the last \ +binlog of the MySQL server. If you send the output to the same MySQL server, \ +that may lead to an endless loop.", + (gptr*) &to_last_remote_log, (gptr*) &to_last_remote_log, 0, GET_BOOL, + NO_ARG, 0, 0, 0, 0, 0, 0}, {"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}, @@ -488,7 +570,7 @@ static void die(const char* fmt, ...) static void print_version() { - printf("%s Ver 2.6 for %s at %s\n", my_progname, SYSTEM_TYPE, MACHINE_TYPE); + printf("%s Ver 3.0 for %s at %s\n", my_progname, SYSTEM_TYPE, MACHINE_TYPE); NETWARE_SET_SCREEN_MODE(1); } @@ -508,6 +590,29 @@ the mysql command line client\n\n"); my_print_variables(my_long_options); } + +static my_time_t convert_str_to_timestamp(const char* str) +{ + int was_cut; + MYSQL_TIME l_time; + long dummy_my_timezone; + bool dummy_in_dst_time_gap; + /* We require a total specification (date AND time) */ + if (str_to_datetime(str, strlen(str), &l_time, 0, &was_cut) != + MYSQL_TIMESTAMP_DATETIME || was_cut) + { + fprintf(stderr, "Incorrect date and time argument: %s\n", str); + exit(1); + } + /* + Note that Feb 30th, Apr 31st cause no error messages and are mapped to + the next existing day, like in mysqld. Maybe this could be changed when + mysqld is changed too (with its "strict" mode?). + */ + return + my_system_gmt_sec(&l_time, &dummy_my_timezone, &dummy_in_dst_time_gap); +} + #include <help_end.h> extern "C" my_bool @@ -549,6 +654,21 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), case 'R': remote_opt= 1; 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 OPT_START_DATETIME: + start_datetime= convert_str_to_timestamp(start_datetime_str); + break; + case OPT_STOP_DATETIME: + stop_datetime= convert_str_to_timestamp(stop_datetime_str); + break; case 'V': print_version(); exit(0); @@ -577,23 +697,28 @@ static int parse_args(int *argc, char*** argv) static MYSQL* safe_connect() { - MYSQL *local_mysql = mysql_init(NULL); + MYSQL *local_mysql= mysql_init(NULL); - if(!local_mysql) + if (!local_mysql) die("Failed on mysql_init"); - - if (!mysql_real_connect(local_mysql, host, user, pass, 0, port, sock, 0)) - die("failed on connect: %s", mysql_error(local_mysql)); + if (opt_protocol) + mysql_options(local_mysql, MYSQL_OPT_PROTOCOL, (char*) &opt_protocol); + if (!mysql_real_connect(local_mysql, host, user, pass, 0, port, sock, 0)) + { + char errmsg[256]; + strmake(errmsg, mysql_error(local_mysql), sizeof(errmsg)-1); + mysql_close(local_mysql); + die("failed on connect: %s", errmsg); + } return local_mysql; } static int dump_log_entries(const char* logname) { - if (remote_opt) - return dump_remote_log_entries(logname); - return dump_local_log_entries(logname); + return (remote_opt ? dump_remote_log_entries(logname) : + dump_local_log_entries(logname)); } @@ -607,9 +732,10 @@ static int check_master_version(MYSQL* mysql) if (mysql_query(mysql, "SELECT VERSION()") || !(res = mysql_store_result(mysql))) { + char errmsg[256]; + strmake(errmsg, mysql_error(mysql), sizeof(errmsg)-1); mysql_close(mysql); - die("Error checking master version: %s", - mysql_error(mysql)); + die("Error checking master version: %s", errmsg); } if (!(row = mysql_fetch_row(res))) { @@ -649,61 +775,119 @@ static int dump_remote_log_entries(const char* logname) { char buf[128]; char last_db[FN_REFLEN+1] = ""; - uint len; - NET* net = &mysql->net; + ulong len; + uint logname_len; + NET* net; int old_format; + int error= 0; + my_off_t old_off= start_position_mot; + char fname[FN_REFLEN+1]; + DBUG_ENTER("dump_remote_log_entries"); + + /* + Even if we already read one binlog (case of >=2 binlogs on command line), + we cannot re-use the same connection as before, because it is now dead + (COM_BINLOG_DUMP kills the thread when it finishes). + */ + mysql= safe_connect(); + net= &mysql->net; old_format = check_master_version(mysql); - if (!position) - position = BIN_LOG_HEADER_SIZE; // protect the innocent from spam - if (position < BIN_LOG_HEADER_SIZE) + /* + COM_BINLOG_DUMP accepts only 4 bytes for the position, so we are forced to + cast to uint32. + */ + int4store(buf, (uint32)start_position); + int2store(buf + BIN_LOG_HEADER_SIZE, binlog_flags); + + size_s tlen = strlen(logname); + if (tlen > UINT_MAX) { - position = BIN_LOG_HEADER_SIZE; - // warn the user - sql_print_error("Warning: The position in the binary log can't be less than %d.\nStarting from position %d\n", BIN_LOG_HEADER_SIZE, BIN_LOG_HEADER_SIZE); + fprintf(stderr,"Log name too long\n"); + error= 1; + goto err; } - int4store(buf, position); - int2store(buf + BIN_LOG_HEADER_SIZE, binlog_flags); - len = (uint) strlen(logname); + logname_len = (uint) tlen; int4store(buf + 6, 0); - memcpy(buf + 10, logname,len); - if (simple_command(mysql, COM_BINLOG_DUMP, buf, len + 10, 1)) + memcpy(buf + 10, logname, logname_len); + if (simple_command(mysql, COM_BINLOG_DUMP, buf, logname_len + 10, 1)) { fprintf(stderr,"Got fatal error sending the log dump command\n"); - return 1; + error= 1; + goto err; } - my_off_t old_off= 0; - ulonglong rec_count= 0; - char fname[FN_REFLEN+1]; - for (;;) { - const char *error; + const char *error_msg; len = net_safe_read(mysql); if (len == packet_error) { fprintf(stderr, "Got error reading packet from server: %s\n", mysql_error(mysql)); - return 1; + error= 1; + goto err; } if (len < 8 && net->read_pos[0] == 254) break; // end of data DBUG_PRINT("info",( "len= %u, net->read_pos[5] = %d\n", len, net->read_pos[5])); Log_event *ev = Log_event::read_log_event((const char*) net->read_pos + 1 , - len - 1, &error, old_format); + len - 1, &error_msg, old_format); if (!ev) { fprintf(stderr, "Could not construct log event object\n"); - return 1; + error= 1; + goto err; } Log_event_type type= ev->get_type_code(); if (!old_format || ( type != LOAD_EVENT && type != CREATE_FILE_EVENT)) { - if (process_event(&rec_count,last_db,ev,old_off,old_format)) - return 1; + if (ev->get_type_code() == ROTATE_EVENT) + { + Rotate_log_event *rev= (Rotate_log_event *)ev; + /* + mysqld is sending us all its binlogs after the requested one, but we + don't want them. + If this is a fake Rotate event, and not about our log, we can stop + transfer. If this a real Rotate event (so it's not about our log, + it's in our log describing the next log), we print it (because it's + part of our log) and then we will stop when we receive the fake one + soon. + This is suitable for binlogs, not relay logs (but for now we don't + read relay logs remotely because the server is not able to do + that). If one day we read relay logs remotely, then we will have a + problem with the detection below: relay logs contain Rotate events + which are about the binlogs, so which would trigger the end-detection + below. + */ + if (rev->when == 0) + { + if (!to_last_remote_log) + { + if ((rev->ident_len != logname_len) || + memcmp(rev->new_log_ident, logname, logname_len)) + { + error= 0; + goto err; + } + /* + Otherwise, this is a fake Rotate for our log, at the very + beginning for sure. Skip it, because it was not in the original + log. If we are running with to_last_remote_log, we print it, + because it serves as a useful marker between binlogs then. + */ + continue; + } + len= 1; // fake Rotate, so don't increment old_off + } + } + if ((error= process_event(last_db,ev,old_off,old_format))) + { + error= ((error < 0) ? 0 : 1); + goto err; + } } else { @@ -713,30 +897,35 @@ static int dump_remote_log_entries(const char* logname) File file; if ((file= load_processor.prepare_new_file_for_old_format(le,fname)) < 0) - return 1; - if (process_event(&rec_count,last_db,ev,old_off,old_format)) + { + error= 1; + goto err; + } + + if ((error= process_event(last_db,ev,old_off,old_format))) { my_close(file,MYF(MY_WME)); - return 1; + error= ((error < 0) ? 0 : 1); + goto err; } if (load_processor.load_old_format_file(net,old_fname,old_len,file)) { my_close(file,MYF(MY_WME)); - return 1; + error= 1; + goto err; } my_close(file,MYF(MY_WME)); } /* Let's adjust offset for remote log as for local log to produce - similar text.. + similar text. */ - if (old_off) - old_off+= len-1; - else // first event, so it's a fake Rotate event - old_off= position; + old_off+= len-1; } - return 0; +err: + mysql_close(mysql); + DBUG_RETURN(error); } @@ -745,6 +934,7 @@ static int check_header(IO_CACHE* file) byte header[BIN_LOG_HEADER_SIZE]; byte buf[PROBE_HEADER_LEN]; int old_format=0; + DBUG_ENTER("check_header"); my_off_t pos = my_b_tell(file); my_b_seek(file, (my_off_t)0); @@ -762,7 +952,7 @@ static int check_header(IO_CACHE* file) } } my_b_seek(file, pos); - return old_format; + DBUG_RETURN(old_format); } @@ -770,7 +960,6 @@ static int dump_local_log_entries(const char* logname) { File fd = -1; IO_CACHE cache,*file= &cache; - ulonglong rec_count = 0; char last_db[FN_REFLEN+1]; byte tmp_buff[BIN_LOG_HEADER_SIZE]; bool old_format = 0; @@ -782,7 +971,7 @@ static int dump_local_log_entries(const char* logname) { if ((fd = my_open(logname, O_RDONLY | O_BINARY, MYF(MY_WME))) < 0) return 1; - if (init_io_cache(file, fd, 0, READ_CACHE, (my_off_t) position, 0, + if (init_io_cache(file, fd, 0, READ_CACHE, start_position_mot, 0, MYF(MY_WME | MY_NABP))) { my_close(fd, MYF(MY_WME)); @@ -796,12 +985,12 @@ static int dump_local_log_entries(const char* logname) 0, MYF(MY_WME | MY_NABP | MY_DONT_CHECK_FILESIZE))) return 1; old_format = check_header(file); - if (position) + if (start_position) { - /* skip 'position' characters from stdout */ + /* skip 'start_position' characters from stdout */ byte buff[IO_SIZE]; my_off_t length,tmp; - for (length= (my_off_t) position ; length > 0 ; length-=tmp) + for (length= start_position_mot ; length > 0 ; length-=tmp) { tmp=min(length,sizeof(buff)); if (my_b_read(file, buff, (uint) tmp)) @@ -811,11 +1000,11 @@ static int dump_local_log_entries(const char* logname) } } } - file->pos_in_file=position; + file->pos_in_file= start_position_mot; file->seek_not_done=0; } - if (!position) + if (!start_position) { // Skip header if (my_b_read(file, tmp_buff, BIN_LOG_HEADER_SIZE)) @@ -844,9 +1033,10 @@ static int dump_local_log_entries(const char* logname) // file->error == 0 means EOF, that's OK, we break in this case break; } - if (process_event(&rec_count,last_db,ev,old_off,false)) + if ((error= process_event(last_db,ev,old_off,false))) { - error= 1; + if (error < 0) + error= 0; break; } } @@ -859,82 +1049,16 @@ end: } -#if MYSQL_VERSION_ID < 40101 - -typedef struct st_my_tmpdir -{ - char **list; - uint cur, max; -} MY_TMPDIR; - -#if defined( __WIN__) || defined(OS2) || defined(__NETWARE__) -#define DELIM ';' -#else -#define DELIM ':' -#endif - -my_bool init_tmpdir(MY_TMPDIR *tmpdir, const char *pathlist) -{ - char *end, *copy; - char buff[FN_REFLEN]; - DYNAMIC_ARRAY t_arr; - if (my_init_dynamic_array(&t_arr, sizeof(char*), 1, 5)) - return TRUE; - if (!pathlist || !pathlist[0]) - { - /* Get default temporary directory */ - pathlist=getenv("TMPDIR"); /* Use this if possible */ -#if defined( __WIN__) || defined(OS2) - if (!pathlist) - pathlist=getenv("TEMP"); - if (!pathlist) - pathlist=getenv("TMP"); -#endif - if (!pathlist || !pathlist[0]) - pathlist=(char*) P_tmpdir; - } - do - { - end=strcend(pathlist, DELIM); - convert_dirname(buff, pathlist, end); - if (!(copy=my_strdup(buff, MYF(MY_WME)))) - return TRUE; - if (insert_dynamic(&t_arr, (gptr)©)) - return TRUE; - pathlist=end+1; - } - while (*end); - freeze_size(&t_arr); - tmpdir->list=(char **)t_arr.buffer; - tmpdir->max=t_arr.elements-1; - tmpdir->cur=0; - return FALSE; -} - -char *my_tmpdir(MY_TMPDIR *tmpdir) -{ - char *dir; - dir=tmpdir->list[tmpdir->cur]; - tmpdir->cur= (tmpdir->cur == tmpdir->max) ? 0 : tmpdir->cur+1; - return dir; -} - -void free_tmpdir(MY_TMPDIR *tmpdir) -{ - uint i; - for (i=0; i<=tmpdir->max; i++) - my_free(tmpdir->list[i], MYF(0)); - my_free((gptr)tmpdir->list, MYF(0)); -} - -#endif - - int main(int argc, char** argv) { static char **defaults_argv; - int exit_value; + int exit_value= 0; + ulonglong save_stop_position; MY_INIT(argv[0]); + DBUG_ENTER("main"); + DBUG_PROCESS(argv[0]); + + init_time(); // for time functions parse_args(&argc, (char***)&argv); defaults_argv=argv; @@ -946,8 +1070,7 @@ int main(int argc, char** argv) exit(1); } - if (remote_opt) - mysql = safe_connect(); + my_set_max_open_files(open_files_limit); MY_TMPDIR tmpdir; tmpdir.list= 0; @@ -955,39 +1078,50 @@ int main(int argc, char** argv) { if (init_tmpdir(&tmpdir, 0)) exit(1); - dirname_for_local_load= my_strdup(my_tmpdir(&tmpdir),MY_WME); + dirname_for_local_load= my_strdup(my_tmpdir(&tmpdir), MY_WME); } if (load_processor.init()) exit(1); if (dirname_for_local_load) load_processor.init_by_dir_name(dirname_for_local_load); - else /* my_malloc() failed in my_strdup() */ + else load_processor.init_by_cur_dir(); - exit_value= 0; fprintf(result_file, "/*!40019 SET @@session.max_insert_delayed_threads=0*/;\n"); - while (--argc >= 0) + + if (disable_log_bin) + fprintf(result_file, + "/*!32316 SET @OLD_SQL_LOG_BIN=@@SQL_LOG_BIN, SQL_LOG_BIN=0*/;\n"); + + for (save_stop_position= stop_position, stop_position= ~(my_off_t)0 ; + (--argc >= 0) && !stop_passed ; ) { + if (argc == 0) // last log, --stop-position applies + stop_position= save_stop_position; if (dump_log_entries(*(argv++))) { exit_value=1; break; } + // For next log, --start-position does not apply + start_position= BIN_LOG_HEADER_SIZE; } + if (disable_log_bin) + fprintf(result_file, "/*!32316 SET SQL_LOG_BIN=@OLD_SQL_LOG_BIN*/;\n"); + if (tmpdir.list) free_tmpdir(&tmpdir); if (result_file != stdout) my_fclose(result_file, MYF(0)); - if (remote_opt) - mysql_close(mysql); cleanup(); free_defaults(defaults_argv); + my_free_open_file_info(); my_end(0); exit(exit_value); - return exit_value; // Keep compilers happy + DBUG_RETURN(exit_value); // Keep compilers happy } /* diff --git a/client/mysqlcheck.c b/client/mysqlcheck.c index 98ef59c19d8..2a2ae1311c9 100644 --- a/client/mysqlcheck.c +++ b/client/mysqlcheck.c @@ -16,7 +16,7 @@ /* By Jani Tolonen, 2001-04-20, MySQL Development Team */ -#define CHECK_VERSION "2.4.3" +#define CHECK_VERSION "2.4.4" #include "client_priv.h" #include <m_ctype.h> @@ -37,10 +37,16 @@ static my_bool opt_alldbs = 0, opt_check_only_changed = 0, opt_extended = 0, tty_password = 0, opt_frm = 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, *default_charset = 0, - *current_host = 0; +static char *opt_password = 0, *current_user = 0, + *default_charset = (char *)MYSQL_DEFAULT_CHARSET_NAME, + *current_host = 0; static int first_error = 0; DYNAMIC_ARRAY tables4repair; +#ifdef HAVE_SMEM +static char *shared_memory_base_name=0; +#endif +static uint opt_protocol=0; +static CHARSET_INFO *charset_info= &my_charset_latin1; enum operations {DO_CHECK, DO_REPAIR, DO_ANALYZE, DO_OPTIMIZE}; @@ -65,9 +71,9 @@ static struct my_option my_long_options[] = (gptr*) &opt_auto_repair, (gptr*) &opt_auto_repair, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"character-sets-dir", OPT_CHARSETS_DIR, - "Directory where character sets are", (gptr*) &charsets_dir, + "Directory where character sets are.", (gptr*) &charsets_dir, (gptr*) &charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"check", 'c', "Check table for errors", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, + {"check", 'c', "Check table for errors.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, {"check-only-changed", 'C', "Check only tables that have changed since last check or haven't been closed properly.", @@ -79,12 +85,17 @@ static struct my_option my_long_options[] = "To check several databases. Note the difference in usage; In this case no tables are given. All name arguments are regarded as databasenames.", (gptr*) &opt_databases, (gptr*) &opt_databases, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"debug", '#', "Output debug log. Often this is 'd:t:o,filename'", +#ifdef DBUG_OFF + {"debug", '#', "This is a non-debug version. Catch this and exit.", + 0, 0, 0, GET_DISABLED, OPT_ARG, 0, 0, 0, 0, 0, 0}, +#else + {"debug", '#', "Output debug log. Often this is 'd:t:o,filename'.", 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, +#endif {"default-character-set", OPT_DEFAULT_CHARSET, - "Set the default character set", (gptr*) &default_charset, + "Set the default character set.", (gptr*) &default_charset, (gptr*) &default_charset, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"fast",'F', "Check only tables that haven't been closed properly", + {"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}, {"force", 'f', "Continue even if we get an sql-error.", @@ -113,6 +124,8 @@ static struct my_option my_long_options[] = {"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}, + {"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}, {"quick", 'q', "If you are using this option with CHECK TABLE, it prevents the check from scanning the rows to check for wrong links. This is the fastest check. If you are using this option with REPAIR TABLE, it will try to repair only the index tree. This is the fastest repair method for a table.", (gptr*) &opt_quick, (gptr*) &opt_quick, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, @@ -120,6 +133,11 @@ static struct my_option my_long_options[] = {"repair", 'r', "Can fix almost anything except unique keys that aren't unique.", 0, 0, 0, GET_NO_ARG, NO_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', "Print only error messages.", (gptr*) &opt_silent, (gptr*) &opt_silent, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"socket", 'S', "Socket file to use for connection.", @@ -181,7 +199,7 @@ static void usage(void) puts("and you are welcome to modify and redistribute it under the GPL license.\n"); puts("This program can be used to CHECK (-c,-m,-C), REPAIR (-r), ANALYZE (-a)"); puts("or OPTIMIZE (-o) tables. Some of the options (like -e or -q) can be"); - puts("used at the same time. It works on MyISAM and in some cases on BDB tables."); + puts("used at the same time. Not all options are supported by all storage engines."); puts("Please consult the MySQL manual for latest information about the"); puts("above. The options -c,-r,-a and -o are exclusive to each other, which"); puts("means that the last option will be used, if several was specified.\n"); @@ -242,6 +260,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), while (*argument) *argument++= 'x'; /* Destroy argument */ if (*start) start[1] = 0; /* Cut length of argument */ + tty_password= 0; } else tty_password = 1; @@ -251,7 +270,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), break; case 'W': #ifdef __WIN__ - opt_mysql_unix_port = MYSQL_NAMEDPIPE; + opt_protocol = MYSQL_PROTOCOL_PIPE; #endif break; case '#': @@ -265,6 +284,15 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), verbose++; break; case 'V': print_version(); exit(0); + 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; + } } return 0; } @@ -300,11 +328,12 @@ static int get_options(int *argc, char ***argv) else what_to_do = DO_CHECK; } - if (default_charset) - { - if (set_default_charset_by_name(default_charset, MYF(MY_WME))) + + /* TODO: This variable is not yet used */ + if (strcmp(default_charset, charset_info->csname) && + !(charset_info= get_charset_by_csname(default_charset, + MY_CS_PRIMARY, MYF(MY_WME)))) exit(1); - } if (*argc > 0 && opt_alldbs) { printf("You should give only options, no arguments at all, with option\n"); @@ -555,8 +584,13 @@ static void print_result() if (status) { + /* + if there was an error with the table, we have --auto-repair set, + and this isn't a repair op, then add the table to the tables4repair + list + */ if (found_error && opt_auto_repair && what_to_do != DO_REPAIR && - (!opt_fast || strcmp(row[3],"OK"))) + strcmp(row[3],"OK")) insert_dynamic(&tables4repair, prev); found_error=0; if (opt_silent) @@ -567,15 +601,16 @@ static void print_result() else if (!status && changed) { printf("%s\n%-9s: %s", row[0], row[2], row[3]); - found_error=1; + if (strcmp(row[2],"note")) + found_error=1; } else printf("%-9s: %s", row[2], row[3]); strmov(prev, row[0]); putchar('\n'); } - if (found_error && opt_auto_repair && what_to_do != DO_REPAIR && - !opt_fast) + /* add the last table to be repaired to the list */ + if (found_error && opt_auto_repair && what_to_do != DO_REPAIR) insert_dynamic(&tables4repair, prev); mysql_free_result(res); } @@ -596,6 +631,12 @@ static int dbConnect(char *host, char *user, char *passwd) mysql_ssl_set(&mysql_connection, opt_ssl_key, opt_ssl_cert, opt_ssl_ca, opt_ssl_capath, opt_ssl_cipher); #endif + if (opt_protocol) + mysql_options(&mysql_connection,MYSQL_OPT_PROTOCOL,(char*)&opt_protocol); +#ifdef HAVE_SMEM + if (shared_memory_base_name) + mysql_options(&mysql_connection,MYSQL_SHARED_MEMORY_BASE_NAME,shared_memory_base_name); +#endif if (!(sock = mysql_real_connect(&mysql_connection, host, user, passwd, NULL, opt_mysql_port, opt_mysql_unix_port, 0))) { @@ -683,6 +724,9 @@ int main(int argc, char **argv) if (opt_auto_repair) delete_dynamic(&tables4repair); my_free(opt_password, MYF(MY_ALLOW_ZERO_PTR)); +#ifdef HAVE_SMEM + my_free(shared_memory_base_name,MYF(MY_ALLOW_ZERO_PTR)); +#endif my_end(0); return(first_error!=0); } /* main */ diff --git a/client/mysqldump.c b/client/mysqldump.c index 145b84f3f45..3458f74d8a2 100644 --- a/client/mysqldump.c +++ b/client/mysqldump.c @@ -30,18 +30,20 @@ ** master/autocommit code by Brian Aker <brian@tangent.org> ** SSL by ** Andrei Errapart <andreie@no.spam.ee> -** Tõnu Samuel <tonu@please.do.not.remove.this.spam.ee> +** Tõnu Samuel <tonu@please.do.not.remove.this.spam.ee> ** XML by Gary Huntress <ghuntress@mediaone.net> 10/10/01, cleaned up ** and adapted to mysqldump 05/11/01 by Jani Tolonen ** Added --single-transaction option 06/06/2002 by Peter Zaitsev +** 10 Jun 2003: SET NAMES and --no-set-names by Alexander Barkov */ -#define DUMP_VERSION "9.11" +#define DUMP_VERSION "10.9" #include <my_global.h> #include <my_sys.h> #include <m_string.h> #include <m_ctype.h> +#include <hash.h> #include "client_priv.h" #include "mysql.h" @@ -54,6 +56,8 @@ #define EX_MYSQLERR 2 #define EX_CONSCHECK 3 #define EX_EOM 4 +#define EX_EOF 5 /* ferror for output file was got */ +#define EX_ILLEGAL_TABLE 6 /* index into 'show fields from table' */ @@ -62,51 +66,100 @@ #define SHOW_NULL 2 #define SHOW_DEFAULT 4 #define SHOW_EXTRA 5 -#define QUOTE_CHAR '`' /* Size of buffer for dump's select query */ #define QUERY_LENGTH 1536 static char *add_load_option(char *ptr, const char *object, const char *statement); +static ulong find_set(TYPELIB *lib, const char *x, uint length, + char **err_pos, uint *err_len); static char *field_escape(char *to,const char *from,uint length); -static my_bool verbose=0,tFlag=0,cFlag=0,dFlag=0,quick=0, extended_insert = 0, - lock_tables=0,ignore_errors=0,flush_logs=0,replace=0, - ignore=0,opt_drop=0,opt_keywords=0,opt_lock=0,opt_compress=0, - opt_delayed=0,create_options=0,opt_quoted=0,opt_databases=0, - opt_alldbs=0,opt_create_db=0,opt_first_slave=0, - opt_autocommit=0,opt_master_data,opt_disable_keys=0,opt_xml=0, - opt_delete_master_logs=0, tty_password=0, - opt_single_transaction=0, opt_comments= 0, - opt_hex_blob; +static my_bool verbose=0,tFlag=0,dFlag=0,quick= 1, extended_insert= 1, + lock_tables=1,ignore_errors=0,flush_logs=0, + opt_drop=1,opt_keywords=0,opt_lock=1,opt_compress=0, + opt_delayed=0,create_options=1,opt_quoted=0,opt_databases=0, + opt_alldbs=0,opt_create_db=0,opt_lock_all_tables=0, + opt_set_charset=0, + opt_autocommit=0,opt_disable_keys=1,opt_xml=0, + opt_delete_master_logs=0, tty_password=0, + 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; static ulong opt_max_allowed_packet, opt_net_buffer_length; -static MYSQL mysql_connection,*sock=0; -static char insert_pat[12 * 1024],*opt_password=0,*current_user=0, +static MYSQL mysql_connection,*sock=0; +static my_bool insert_pat_inited=0; +static DYNAMIC_STRING insert_pat; +static char *opt_password=0,*current_user=0, *current_host=0,*path=0,*fields_terminated=0, *lines_terminated=0, *enclosed=0, *opt_enclosed=0, *escaped=0, - *where=0, *default_charset; -static uint opt_mysql_port=0; + *where=0, *order_by=0, + *opt_compatible_mode_str= 0, + *err_ptr= 0; +static char compatible_mode_normal_str[255]; +static ulong opt_compatible_mode= 0; +#define MYSQL_OPT_MASTER_DATA_EFFECTIVE_SQL 1 +#define MYSQL_OPT_MASTER_DATA_COMMENTED_SQL 2 +static uint opt_mysql_port= 0, err_len= 0, opt_master_data; static my_string opt_mysql_unix_port=0; static int first_error=0; static DYNAMIC_STRING extended_row; #include <sslopt-vars.h> FILE *md_result_file; +#ifdef HAVE_SMEM +static char *shared_memory_base_name=0; +#endif +static uint opt_protocol= 0; +/* + Constant for detection of default value of default_charset. + If default_charset is equal to mysql_universal_client_charset, then + it is the default value which assigned at the very beginning of main(). +*/ +static const char *mysql_universal_client_charset= + MYSQL_UNIVERSAL_CLIENT_CHARSET; +static char *default_charset; +static CHARSET_INFO *charset_info= &my_charset_latin1; +const char *default_dbug_option="d:t:o,/tmp/mysqldump.trace"; + +const char *compatible_mode_names[]= +{ + "MYSQL323", "MYSQL40", "POSTGRESQL", "ORACLE", "MSSQL", "DB2", + "MAXDB", "NO_KEY_OPTIONS", "NO_TABLE_OPTIONS", "NO_FIELD_OPTIONS", + "ANSI", + NullS +}; +#define MASK_ANSI_QUOTES \ +(\ + (1<<2) | /* POSTGRESQL */\ + (1<<3) | /* ORACLE */\ + (1<<4) | /* MSSQL */\ + (1<<5) | /* DB2 */\ + (1<<6) | /* MAXDB */\ + (1<<10) /* ANSI */\ +) +TYPELIB compatible_mode_typelib= {array_elements(compatible_mode_names) - 1, + "", compatible_mode_names, NULL}; + +HASH ignore_table; static struct my_option my_long_options[] = { + {"all", 'a', "Deprecated. Use --create-options instead.", + (gptr*) &create_options, (gptr*) &create_options, 0, GET_BOOL, NO_ARG, 1, + 0, 0, 0, 0, 0}, {"all-databases", 'A', "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", 'a', "Include all MySQL specific create options.", - (gptr*) &create_options, (gptr*) &create_options, 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}, {"add-drop-table", OPT_DROP, "Add a 'drop table' before each create.", - (gptr*) &opt_drop, (gptr*) &opt_drop, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, + (gptr*) &opt_drop, (gptr*) &opt_drop, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, {"add-locks", OPT_LOCKS, "Add locks around insert statements.", - (gptr*) &opt_lock, (gptr*) &opt_lock, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, + (gptr*) &opt_lock, (gptr*) &opt_lock, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, {"allow-keywords", OPT_KEYWORDS, "Allow creation of column names that are keywords.", (gptr*) &opt_keywords, @@ -116,22 +169,40 @@ static struct my_option my_long_options[] = 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, #endif {"character-sets-dir", OPT_CHARSETS_DIR, - "Directory where character sets are", (gptr*) &charsets_dir, + "Directory where character sets are.", (gptr*) &charsets_dir, (gptr*) &charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"comments", 'i', "Write additional information.", (gptr*) &opt_comments, (gptr*) &opt_comments, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, - {"complete-insert", 'c', "Use complete insert statements.", (gptr*) &cFlag, - (gptr*) &cFlag, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"compatible", OPT_COMPATIBLE, + "Change the dump to be compatible with a given mode. By default tables are dumped in a format optimized for MySQL. Legal modes are: ansi, mysql323, mysql40, postgresql, oracle, mssql, db2, maxdb, no_key_options, no_table_options, no_field_options. One can use several modes separated by commas. Note: Requires MySQL server version 4.1.0 or higher. This option is ignored with earlier server versions.", + (gptr*) &opt_compatible_mode_str, (gptr*) &opt_compatible_mode_str, 0, + GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"compact", OPT_COMPACT, + "Give less verbose output (useful for debugging). Disables structure comments and header/footer constructs. Enables options --skip-add-drop-table --no-set-names --skip-disable-keys --skip-add-locks", + (gptr*) &opt_compact, (gptr*) &opt_compact, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, + 0, 0}, + {"complete-insert", 'c', "Use complete insert statements.", + (gptr*) &opt_complete_insert, (gptr*) &opt_complete_insert, 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}, + {"create-options", OPT_CREATE_OPTIONS, + "Include all MySQL specific create options.", + (gptr*) &create_options, (gptr*) &create_options, 0, GET_BOOL, NO_ARG, 1, + 0, 0, 0, 0, 0}, {"databases", 'B', "To dump several databases. Note the difference in usage; In this case no tables are given. All name arguments are regarded as databasenames. 'USE db_name;' will be included in the output.", (gptr*) &opt_databases, (gptr*) &opt_databases, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"debug", '#', "Output debug log. Often this is 'd:t:o,filename'.", - 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, +#ifdef DBUG_OFF + {"debug", '#', "This is a non-debug version. Catch this and exit", + 0,0, 0, GET_DISABLED, OPT_ARG, 0, 0, 0, 0, 0, 0}, +#else + {"debug", '#', "Output debug log", (gptr*) &default_dbug_option, + (gptr*) &default_dbug_option, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, +#endif {"default-character-set", OPT_DEFAULT_CHARSET, "Set the default character set.", (gptr*) &default_charset, (gptr*) &default_charset, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, @@ -139,15 +210,16 @@ static struct my_option my_long_options[] = (gptr*) &opt_delayed, (gptr*) &opt_delayed, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"delete-master-logs", OPT_DELETE_MASTER_LOGS, - "Delete logs on master after backup. This automatically enables --first-slave.", - 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + "Delete logs on master after backup. This automatically enables --master-data.", + (gptr*) &opt_delete_master_logs, (gptr*) &opt_delete_master_logs, 0, + GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"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, 0, 0, 0, 0, 0, 0}, + (gptr*) &opt_disable_keys, 0, GET_BOOL, NO_ARG, 1, 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, - 0, 0, 0, 0, 0, 0}, + 1, 0, 0, 0, 0, 0}, {"fields-terminated-by", OPT_FTB, "Fields in the textfile are terminated by ...", (gptr*) &fields_terminated, (gptr*) &fields_terminated, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, @@ -159,13 +231,18 @@ static struct my_option my_long_options[] = (gptr*) &opt_enclosed, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0 ,0, 0}, {"fields-escaped-by", OPT_ESC, "Fields in the i.file are escaped by ...", (gptr*) &escaped, (gptr*) &escaped, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"first-slave", 'x', "Locks all tables across all databases.", - (gptr*) &opt_first_slave, (gptr*) &opt_first_slave, 0, GET_BOOL, NO_ARG, + {"first-slave", 'x', "Deprecated, renamed to --lock-all-tables.", + (gptr*) &opt_lock_all_tables, (gptr*) &opt_lock_all_tables, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"flush-logs", 'F', "Flush logs file in server before starting dump. " - "Note that if you dump many databases at once (using the option " - "--databases= or --all-databases), the logs will be flushed for " - "each database dumped.", + "Note that if you dump many databases at once (using the option " + "--databases= or --all-databases), the logs will be flushed for " + "each database dumped. The exception is when using --lock-all-tables " + "or --master-data: " + "in this case the logs will be flushed only once, corresponding " + "to the moment all tables are locked. So if you want your dump and " + "the log flush to happen at the same exact moment you should use " + "--lock-all-tables or --master-data with --flush-logs", (gptr*) &flush_logs, (gptr*) &flush_logs, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"force", 'f', "Continue even if we get an sql-error.", @@ -173,59 +250,118 @@ static struct my_option my_long_options[] = 0, 0, 0, 0, 0, 0}, {"help", '?', "Display this help message and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"hex-blob", OPT_HEXBLOB, "Dump binary strings (CHAR BINARY, " - "VARCHAR BINARY, BLOB) in hexadecimal format.", + {"hex-blob", OPT_HEXBLOB, "Dump binary strings (BINARY, " + "VARBINARY, BLOB) in hexadecimal format.", (gptr*) &opt_hex_blob, (gptr*) &opt_hex_blob, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"host", 'h', "Connect to host.", (gptr*) ¤t_host, (gptr*) ¤t_host, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"ignore-table", OPT_IGNORE_TABLE, + "Do not dump the specified table. To specify more than one table to ignore, " + "use the directive multiple times, once for each table. Each table must " + "be specified with both database and table names, e.g. --ignore-table=database.table", + 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"insert-ignore", OPT_INSERT_IGNORE, "Insert rows with INSERT IGNORE.", + (gptr*) &opt_ignore, (gptr*) &opt_ignore, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, + 0, 0}, {"lines-terminated-by", OPT_LTB, "Lines in the i.file are terminated by ...", (gptr*) &lines_terminated, (gptr*) &lines_terminated, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"lock-all-tables", 'x', "Locks all tables across all databases. This " + "is achieved by taking a global read lock for the duration of the whole " + "dump. Automatically turns --single-transaction and --lock-tables off.", + (gptr*) &opt_lock_all_tables, (gptr*) &opt_lock_all_tables, 0, GET_BOOL, NO_ARG, + 0, 0, 0, 0, 0, 0}, {"lock-tables", 'l', "Lock all tables for read.", (gptr*) &lock_tables, - (gptr*) &lock_tables, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + (gptr*) &lock_tables, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, {"master-data", OPT_MASTER_DATA, - "This causes the master position and filename to be appended to your output. This automatically enables --first-slave.", - 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + "This causes the binary log position and filename to be appended to the " + "output. If equal to 1, will print it as a CHANGE MASTER command; if equal" + " to 2, that command will be prefixed with a comment symbol. " + "This option will turn --lock-all-tables on, unless " + "--single-transaction is specified too (in which case a " + "global read lock is only taken a short time at the beginning of the dump " + "- don't forget to read about --single-transaction below). In all cases " + "any action on logs will happen at the exact moment of the dump." + "Option automatically turns --lock-tables off.", + (gptr*) &opt_master_data, (gptr*) &opt_master_data, 0, + GET_UINT, OPT_ARG, 0, 0, MYSQL_OPT_MASTER_DATA_COMMENTED_SQL, 0, 0, 0}, + {"max_allowed_packet", OPT_MAX_ALLOWED_PACKET, "", + (gptr*) &opt_max_allowed_packet, (gptr*) &opt_max_allowed_packet, 0, + GET_ULONG, REQUIRED_ARG, 24*1024*1024, 4096, + (longlong) 2L*1024L*1024L*1024L, MALLOC_OVERHEAD, 1024, 0}, + {"net_buffer_length", OPT_NET_BUFFER_LENGTH, "", + (gptr*) &opt_net_buffer_length, (gptr*) &opt_net_buffer_length, 0, + GET_ULONG, REQUIRED_ARG, 1024*1024L-1025, 4096, 16*1024L*1024L, + MALLOC_OVERHEAD-1024, 1024, 0}, {"no-autocommit", OPT_AUTOCOMMIT, "Wrap tables with autocommit/commit statements.", (gptr*) &opt_autocommit, (gptr*) &opt_autocommit, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"no-create-db", 'n', - "'CREATE DATABASE /*!32312 IF NOT EXISTS*/ db_name;' will not be put in the output. The above line will be added otherwise, if --databases or --all-databases option was given.}", + "'CREATE DATABASE /*!32312 IF NOT EXISTS*/ db_name;' will not be put in the output. The above line will be added otherwise, if --databases or --all-databases option was given.}.", (gptr*) &opt_create_db, (gptr*) &opt_create_db, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"no-create-info", 't', "Don't write table creation info.", (gptr*) &tFlag, (gptr*) &tFlag, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"no-data", 'd', "No row information.", (gptr*) &dFlag, (gptr*) &dFlag, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"no-set-names", 'N', + "Deprecated. Use --skip-set-charset instead.", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, {"opt", OPT_OPTIMIZE, - "Same as --add-drop-table --add-locks --all --quick --extended-insert --lock-tables --disable-keys", + "Same as --add-drop-table, --add-locks, --create-options, --quick, --extended-insert, --lock-tables, --set-charset, and --disable-keys. Enabled by default, disable with --skip-opt.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"order-by-primary", OPT_ORDER_BY_PRIMARY, + "Sorts each table's rows by primary key, or first unique key, if such a key exists. Useful when dumping a MyISAM table to be loaded into an InnoDB table, but will make the dump itself take considerably longer.", + (gptr*) &opt_order_by_primary, (gptr*) &opt_order_by_primary, 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 solicited on 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, + {"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}, + {"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}, {"quick", 'q', "Don't buffer query, dump directly to stdout.", - (gptr*) &quick, (gptr*) &quick, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"quote-names",'Q', "Quote table and column names with a `", - (gptr*) &opt_quoted, (gptr*) &opt_quoted, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, + (gptr*) &quick, (gptr*) &quick, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, + {"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}, {"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}, + {"set-charset", OPT_SET_CHARSET, + "Add 'SET NAMES default_character_set' to the output. Enabled by default; suppress with --skip-set-charset.", + (gptr*) &opt_set_charset, (gptr*) &opt_set_charset, 0, GET_BOOL, NO_ARG, 1, + 0, 0, 0, 0, 0}, {"set-variable", 'O', "Change the value of a variable. Please note that this option is deprecated; you can set variables directly with --variable-name=value.", 0, 0, 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 + /* + Note that the combination --single-transaction --master-data + will give bullet-proof binlog position only if server >=4.1.3. That's the + old "FLUSH TABLES WITH READ LOCK does not block commit" fixed bug. + */ {"single-transaction", OPT_TRANSACTION, - "Dump all tables in single transaction to get consistent snapshot. Mutually exclusive with --lock-tables.", - (gptr*) &opt_single_transaction, (gptr*) &opt_single_transaction, 0, GET_BOOL, NO_ARG, - 0, 0, 0, 0, 0, 0}, + "Creates a consistent snapshot by dumping all tables in a single " + "transaction. Works ONLY for tables stored in storage engines which " + "support multiversioning (currently only InnoDB does); the dump is NOT " + "guaranteed to be consistent for other storage engines. Option " + "automatically turns off --lock-tables.", + (gptr*) &opt_single_transaction, (gptr*) &opt_single_transaction, 0, + GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"skip-opt", OPT_SKIP_OPTIMIZATION, + "Disable --opt. Disables --add-drop-table, --add-locks, --create-options, --quick, --extended-insert, --lock-tables, --set-charset, and --disable-keys.", + 0, 0, 0, GET_NO_ARG, 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}, @@ -248,15 +384,7 @@ static struct my_option my_long_options[] = (gptr*) &where, (gptr*) &where, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"xml", 'X', "Dump a database as well formed XML.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"max_allowed_packet", OPT_MAX_ALLOWED_PACKET, "", - (gptr*) &opt_max_allowed_packet, (gptr*) &opt_max_allowed_packet, 0, - GET_ULONG, REQUIRED_ARG, 24*1024*1024, 4096, - (longlong) 2L*1024L*1024L*1024L, MALLOC_OVERHEAD, 1024, 0}, - {"net_buffer_length", OPT_NET_BUFFER_LENGTH, "", - (gptr*) &opt_net_buffer_length, (gptr*) &opt_net_buffer_length, 0, - GET_ULONG, REQUIRED_ARG, 1024*1024L-1025, 4096, 16*1024L*1024L, - MALLOC_OVERHEAD-1024, 1024, 0}, - {0, 0, 0, 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 const char *load_default_groups[]= { "mysqldump","client",0 }; @@ -272,11 +400,28 @@ static int init_dumping(char *); static int dump_databases(char **); static int dump_all_databases(); static char *quote_name(const char *name, char *buff, my_bool force); -static void print_quoted_xml(FILE *output, char *fname, char *str, uint len); static const char *check_if_ignore_table(const char *table_name); +static char *primary_key_fields(const char *table_name); #include <help_start.h> +/* + exit with message if ferror(file) + + SYNOPSIS + check_io() + file - checked file +*/ + +void check_io(FILE *file) +{ + if (ferror(file)) + { + fprintf(stderr, "%s: Got errno %d on write\n", my_progname, errno); + safe_exit(EX_EOF); + } +} + static void print_version(void) { printf("%s Ver %s Distrib %s, for %s (%s)\n",my_progname,DUMP_VERSION, @@ -321,29 +466,99 @@ static void write_header(FILE *sql_file, char *db_name) { if (opt_xml) { - fprintf(sql_file,"<?xml version=\"1.0\"?>\n"); - fprintf(sql_file,"<mysqldump>\n"); + fputs("<?xml version=\"1.0\"?>\n", sql_file); + fputs("<mysqldump ", sql_file); + fputs("xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"", + sql_file); + fputs(">\n", sql_file); + check_io(sql_file); } - else if (opt_comments) + else if (!opt_compact) { - fprintf(sql_file, "-- MySQL dump %s\n--\n", DUMP_VERSION); - fprintf(sql_file, "-- Host: %s Database: %s\n", - current_host ? current_host : "localhost", db_name ? db_name : ""); - fputs("-- ------------------------------------------------------\n", - sql_file); - fprintf(sql_file, "-- Server version\t%s\n", - mysql_get_server_info(&mysql_connection)); + if (opt_comments) + { + fprintf(sql_file, "-- MySQL dump %s\n--\n", DUMP_VERSION); + fprintf(sql_file, "-- Host: %s Database: %s\n", + current_host ? current_host : "localhost", db_name ? db_name : + ""); + fputs("-- ------------------------------------------------------\n", + sql_file); + fprintf(sql_file, "-- Server version\t%s\n", + mysql_get_server_info(&mysql_connection)); + } + if (opt_set_charset) + fprintf(sql_file, +"\n/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;" +"\n/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;" +"\n/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;" +"\n/*!40101 SET NAMES %s */;\n",default_charset); + if (!path) + { + fprintf(md_result_file,"\ +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;\n\ +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;\n\ +"); + } + fprintf(sql_file, + "/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='%s%s%s' */;\n" + "/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;\n", + path?"":"NO_AUTO_VALUE_ON_ZERO",compatible_mode_normal_str[0]==0?"":",", + compatible_mode_normal_str); + check_io(sql_file); } - return; } /* write_header */ + static void write_footer(FILE *sql_file) { if (opt_xml) - fprintf(sql_file,"</mysqldump>"); - fputs("\n", sql_file); + { + fputs("</mysqldump>\n", sql_file); + check_io(sql_file); + } + else if (!opt_compact) + { + fprintf(sql_file,"\n/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;\n"); + if (!path) + { + fprintf(md_result_file,"\ +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;\n\ +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;\n"); + } + if (opt_set_charset) + fprintf(sql_file, +"/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;\n" +"/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;\n" +"/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;\n"); + fprintf(sql_file, + "/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;\n"); + fputs("\n", sql_file); + check_io(sql_file); + } } /* write_footer */ +static void free_table_ent(char *key) + +{ + my_free((gptr) key, MYF(0)); +} + + +byte* get_table_key(const char *entry, uint *length, + my_bool not_used __attribute__((unused))) +{ + *length= strlen(entry); + return (byte*) entry; +} + + +void init_table_rule_hash(HASH* h) +{ + if (hash_init(h, charset_info, 16, 0, 0, + (hash_get_key) get_table_key, + (hash_free_key) free_table_ent, 0)) + exit(EX_EOM); +} static my_bool get_one_option(int optid, const struct my_option *opt __attribute__((unused)), @@ -355,14 +570,6 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), setscreenmode(SCR_AUTOCLOSE_ON_EXIT); break; #endif - case OPT_MASTER_DATA: - opt_master_data=1; - opt_first_slave=1; - break; - case OPT_DELETE_MASTER_LOGS: - opt_delete_master_logs=1; - opt_first_slave=1; - break; case 'p': if (argument) { @@ -372,6 +579,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), while (*argument) *argument++= 'x'; /* Destroy argument */ if (*start) start[1]=0; /* Cut length of argument */ + tty_password= 0; } else tty_password=1; @@ -383,38 +591,125 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), break; case 'W': #ifdef __WIN__ - opt_mysql_unix_port=MYSQL_NAMEDPIPE; + opt_protocol = MYSQL_PROTOCOL_PIPE; #endif break; + case 'N': + opt_set_charset= 0; + break; case 'T': opt_disable_keys=0; break; case '#': - DBUG_PUSH(argument ? argument : "d:t:o"); + DBUG_PUSH(argument ? argument : default_dbug_option); break; #include <sslopt-case.h> case 'V': print_version(); exit(0); case 'X': opt_xml = 1; - opt_disable_keys=0; + extended_insert= opt_drop= opt_lock= + opt_disable_keys= opt_autocommit= opt_create_db= 0; break; case 'I': case '?': usage(); exit(0); + case (int) OPT_MASTER_DATA: + if (!argument) /* work like in old versions */ + opt_master_data= MYSQL_OPT_MASTER_DATA_EFFECTIVE_SQL; + break; case (int) OPT_OPTIMIZE: - extended_insert=opt_drop=opt_lock=quick=create_options=opt_disable_keys= - lock_tables=1; - if (opt_single_transaction) lock_tables=0; + extended_insert= opt_drop= opt_lock= quick= create_options= + opt_disable_keys= lock_tables= opt_set_charset= 1; break; + case (int) OPT_SKIP_OPTIMIZATION: + extended_insert= opt_drop= opt_lock= quick= create_options= + opt_disable_keys= lock_tables= opt_set_charset= 0; + break; + case (int) OPT_COMPACT: + if (opt_compact) + { + opt_comments= opt_drop= opt_disable_keys= opt_lock= 0; + opt_set_charset= 0; + } case (int) OPT_TABLES: opt_databases=0; break; + case (int) OPT_IGNORE_TABLE: + { + if (!strchr(argument, '.')) + { + fprintf(stderr, "Illegal use of option --ignore-table=<database>.<table>\n"); + exit(1); + } + if (!hash_inited(&ignore_table)) + init_table_rule_hash(&ignore_table); + + if (my_hash_insert(&ignore_table, (byte*)my_strdup(argument, MYF(0)))) + exit(EX_EOM); + break; + } + case (int) OPT_COMPATIBLE: + { + char buff[255]; + char *end= compatible_mode_normal_str; + int i; + ulong mode; + + opt_quoted= 1; + opt_set_charset= 0; + opt_compatible_mode_str= argument; + opt_compatible_mode= find_set(&compatible_mode_typelib, + argument, strlen(argument), + &err_ptr, &err_len); + if (err_len) + { + strmake(buff, err_ptr, min(sizeof(buff), err_len)); + fprintf(stderr, "Invalid mode to --compatible: %s\n", buff); + exit(1); + } +#if !defined(DBUG_OFF) + { + uint size_for_sql_mode= 0; + const char **ptr; + for (ptr= compatible_mode_names; *ptr; ptr++) + size_for_sql_mode+= strlen(*ptr); + size_for_sql_mode+= sizeof(compatible_mode_names)-1; + DBUG_ASSERT(sizeof(compatible_mode_normal_str)>=size_for_sql_mode); + } +#endif + mode= opt_compatible_mode; + for (i= 0, mode= opt_compatible_mode; mode; mode>>= 1, i++) + { + if (mode & 1) + { + end= strmov(end, compatible_mode_names[i]); + end= strmov(end, ","); + } + } + if (end!=compatible_mode_normal_str) + end[-1]= 0; + /* + Set charset to the default compiled value if it hasn't + been reset yet by --default-character-set=xxx. + */ + if (default_charset == mysql_universal_client_charset) + default_charset= (char*) MYSQL_DEFAULT_CHARSET_NAME; + break; + } + case (int) 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; + } } return 0; } - static int get_options(int *argc, char ***argv) { int ho_error; @@ -441,21 +736,23 @@ static int get_options(int *argc, char ***argv) "%s: You must use option --tab with --fields-...\n", my_progname); return(1); } - - if (opt_single_transaction && lock_tables) - { - fprintf(stderr, "%s: You can't use --lock-tables and --single-transaction at the same time.\n", my_progname); - return(1); - } - if (enclosed && opt_enclosed) + /* Ensure consistency of the set of binlog & locking options */ + if (opt_delete_master_logs && !opt_master_data) + opt_master_data= MYSQL_OPT_MASTER_DATA_COMMENTED_SQL; + if (opt_single_transaction && opt_lock_all_tables) { - fprintf(stderr, "%s: You can't use ..enclosed.. and ..optionally-enclosed.. at the same time.\n", my_progname); + fprintf(stderr, "%s: You can't use --single-transaction and " + "--lock-all-tables at the same time.\n", my_progname); return(1); - } - if (replace && ignore) + } + if (opt_master_data) + opt_lock_all_tables= !opt_single_transaction; + if (opt_single_transaction || opt_lock_all_tables) + lock_tables= 0; + if (enclosed && opt_enclosed) { - fprintf(stderr, "%s: You can't use --ignore (-i) and --replace (-r) at the same time.\n",my_progname); + fprintf(stderr, "%s: You can't use ..enclosed.. and ..optionally-enclosed.. at the same time.\n", my_progname); return(1); } if ((opt_databases || opt_alldbs) && path) @@ -465,11 +762,10 @@ static int get_options(int *argc, char ***argv) my_progname); return(1); } - if (default_charset) - { - if (set_default_charset_by_name(default_charset, MYF(MY_WME))) - exit(1); - } + if (strcmp(default_charset, charset_info->csname) && + !(charset_info= get_charset_by_csname(default_charset, + MY_CS_PRIMARY, MYF(MY_WME)))) + exit(1); if ((*argc < 1 && !opt_alldbs) || (*argc > 0 && opt_alldbs)) { short_usage(); @@ -494,6 +790,36 @@ static void DBerror(MYSQL *mysql, const char *when) } /* DBerror */ +/* + Sends a query to server, optionally reads result, prints error message if + some. + + SYNOPSIS + mysql_query_with_error_report() + mysql_con connection to use + res if non zero, result will be put there with mysql_store_result + query query to send to server + + RETURN VALUES + 0 query sending and (if res!=0) result reading went ok + 1 error +*/ + +static int mysql_query_with_error_report(MYSQL *mysql_con, MYSQL_RES **res, + const char *query) +{ + if (mysql_query(mysql_con, query) || + (res && !((*res)= mysql_store_result(mysql_con)))) + { + my_printf_error(0, "%s: Couldn't execute '%s': %s (%d)", + MYF(0), my_progname, query, + mysql_error(mysql_con), mysql_errno(mysql_con)); + return 1; + } + return 0; +} + + static void safe_exit(int error) { if (!first_error) @@ -509,10 +835,10 @@ static void safe_exit(int error) /* ** dbConnect -- connects to the host and selects DB. -** Also checks whether the tablename is a valid table name. */ static int dbConnect(char *host, char *user,char *passwd) { + char buff[20+FN_REFLEN]; DBUG_ENTER("dbConnect"); if (verbose) { @@ -526,6 +852,13 @@ static int dbConnect(char *host, char *user,char *passwd) mysql_ssl_set(&mysql_connection, opt_ssl_key, opt_ssl_cert, opt_ssl_ca, opt_ssl_capath, opt_ssl_cipher); #endif + if (opt_protocol) + mysql_options(&mysql_connection,MYSQL_OPT_PROTOCOL,(char*)&opt_protocol); +#ifdef HAVE_SMEM + if (shared_memory_base_name) + mysql_options(&mysql_connection,MYSQL_SHARED_MEMORY_BASE_NAME,shared_memory_base_name); +#endif + mysql_options(&mysql_connection, MYSQL_SET_CHARSET_NAME, default_charset); if (!(sock= mysql_real_connect(&mysql_connection,host,user,passwd, NULL,opt_mysql_port,opt_mysql_unix_port, 0))) @@ -533,6 +866,24 @@ static int dbConnect(char *host, char *user,char *passwd) DBerror(&mysql_connection, "when trying to connect"); return 1; } + /* + Don't dump SET NAMES with a pre-4.1 server (bug#7997). + */ + if (mysql_get_server_version(&mysql_connection) < 40100) + opt_set_charset= 0; + /* + As we're going to set SQL_MODE, it would be lost on reconnect, so we + cannot reconnect. + */ + sock->reconnect= 0; + my_snprintf(buff, sizeof(buff), "/*!40100 SET @@SQL_MODE='%s' */", + compatible_mode_normal_str); + if (mysql_query_with_error_report(sock, 0, buff)) + { + mysql_close(sock); + safe_exit(EX_MYSQLERR); + return 1; + } return 0; } /* dbConnect */ @@ -561,6 +912,7 @@ static void unescape(FILE *file,char *pos,uint length) fputc('\'', file); fputs(tmp, file); fputc('\'', file); + check_io(file); my_free(tmp, MYF(MY_WME)); DBUG_VOID_RETURN; } /* unescape */ @@ -570,39 +922,69 @@ static my_bool test_if_special_chars(const char *str) { #if MYSQL_VERSION_ID >= 32300 for ( ; *str ; str++) - if (!isvar(*str) && *str != '$') + if (!my_isvar(charset_info,*str) && *str != '$') return 1; #endif return 0; } /* test_if_special_chars */ + static char *quote_name(const char *name, char *buff, my_bool force) { char *to= buff; + char qtype= (opt_compatible_mode & MASK_ANSI_QUOTES) ? '\"' : '`'; + if (!force && !opt_quoted && !test_if_special_chars(name)) return (char*) name; - *to++= QUOTE_CHAR; + *to++= qtype; while (*name) { - if (*name == QUOTE_CHAR) - *to= QUOTE_CHAR; + if (*name == qtype) + *to++= qtype; *to++= *name++; } - to[0]=QUOTE_CHAR; - to[1]=0; + to[0]= qtype; + to[1]= 0; return buff; } /* quote_name */ +/* + Quote a table name so it can be used in "SHOW TABLES LIKE <tabname>" + + SYNOPSIS + quote_for_like() + name name of the table + buff quoted name of the table + + DESCRIPTION + Quote \, _, ' and % characters + Note: Because MySQL uses the C escape syntax in strings + (for example, '\n' to represent newline), you must double + any '\' that you use in your LIKE strings. For example, to + search for '\n', specify it as '\\n'. To search for '\', specify + it as '\\\\' (the backslashes are stripped once by the parser + and another time when the pattern match is done, leaving a + single backslash to be matched). + + Example: "t\1" => "t\\\\1" + +*/ static char *quote_for_like(const char *name, char *buff) { char *to= buff; *to++= '\''; while (*name) { - if (*name == '\'' || *name == '_' || *name == '\\' || *name == '%') + if (*name == '\\') + { + *to++='\\'; + *to++='\\'; + *to++='\\'; + } + else if (*name == '\'' || *name == '_' || *name == '%') *to++= '\\'; *to++= *name++; } @@ -613,7 +995,160 @@ static char *quote_for_like(const char *name, char *buff) /* - getStructure -- retrievs database structure, prints out corresponding + Quote and print a string. + + SYNOPSIS + print_quoted_xml() + output - output file + str - string to print + len - its length + + DESCRIPTION + Quote '<' '>' '&' '\"' chars and print a string to the xml_file. +*/ + +static void print_quoted_xml(FILE *xml_file, const char *str, ulong len) +{ + const char *end; + + for (end= str + len; str != end; str++) + { + switch (*str) { + case '<': + fputs("<", xml_file); + break; + case '>': + fputs(">", xml_file); + break; + case '&': + fputs("&", xml_file); + break; + case '\"': + fputs(""", xml_file); + break; + default: + fputc(*str, xml_file); + break; + } + } + check_io(xml_file); +} + + +/* + Print xml tag with one attribute. + + SYNOPSIS + print_xml_tag1() + xml_file - output file + sbeg - line beginning + stag_atr - tag and attribute + sval - value of attribute + send - line ending + + DESCRIPTION + Print tag with one attribute to the xml_file. Format is: + sbeg<stag_atr="sval">send + NOTE + sval MUST be a NULL terminated string. + sval string will be qouted before output. +*/ + +static void print_xml_tag1(FILE * xml_file, const char* sbeg, + const char* stag_atr, const char* sval, + const char* send) +{ + fputs(sbeg, xml_file); + fputs("<", xml_file); + fputs(stag_atr, xml_file); + fputs("\"", xml_file); + print_quoted_xml(xml_file, sval, strlen(sval)); + fputs("\">", xml_file); + fputs(send, xml_file); + check_io(xml_file); +} + + +/* + Print xml tag with for a field that is null + + SYNOPSIS + print_xml_null_tag() + xml_file - output file + sbeg - line beginning + stag_atr - tag and attribute + sval - value of attribute + send - line ending + + DESCRIPTION + Print tag with one attribute to the xml_file. Format is: + <stag_atr="sval" xsi:nil="true"/> + NOTE + sval MUST be a NULL terminated string. + sval string will be qouted before output. +*/ + +static void print_xml_null_tag(FILE * xml_file, const char* sbeg, + const char* stag_atr, const char* sval, + const char* send) +{ + fputs(sbeg, xml_file); + fputs("<", xml_file); + fputs(stag_atr, xml_file); + fputs("\"", xml_file); + print_quoted_xml(xml_file, sval, strlen(sval)); + fputs("\" xsi:nil=\"true\" />", xml_file); + fputs(send, xml_file); + check_io(xml_file); +} + + +/* + Print xml tag with many attributes. + + SYNOPSIS + print_xml_row() + xml_file - output file + row_name - xml tag name + tableRes - query result + row - result row + + DESCRIPTION + Print tag with many attribute to the xml_file. Format is: + \t\t<row_name Atr1="Val1" Atr2="Val2"... /> + NOTE + All atributes and values will be quoted before output. +*/ + +static void print_xml_row(FILE *xml_file, const char *row_name, + MYSQL_RES *tableRes, MYSQL_ROW *row) +{ + uint i; + MYSQL_FIELD *field; + ulong *lengths= mysql_fetch_lengths(tableRes); + + fprintf(xml_file, "\t\t<%s", row_name); + check_io(xml_file); + mysql_field_seek(tableRes, 0); + for (i= 0; (field= mysql_fetch_field(tableRes)); i++) + { + if ((*row)[i]) + { + fputc(' ', xml_file); + print_quoted_xml(xml_file, field->name, field->name_length); + fputs("=\"", xml_file); + print_quoted_xml(xml_file, (*row)[i], lengths[i]); + fputc('"', xml_file); + check_io(xml_file); + } + } + fputs(" />\n", xml_file); + check_io(xml_file); +} + + +/* + getTableStructure -- retrievs database structure, prints out corresponding CREATE statement and fills out insert_pat. RETURN @@ -626,23 +1161,43 @@ static uint getTableStructure(char *table, char* db) MYSQL_ROW row; my_bool init=0; uint numFields; - char *strpos, *result_table, *opt_quoted_table; - const char *delayed; + 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]; + char query_buff[512]; FILE *sql_file = md_result_file; + int len; DBUG_ENTER("getTableStructure"); + DBUG_PRINT("enter", ("db: %s, table: %s", db, table)); - delayed= opt_delayed ? " DELAYED " : ""; + if (!insert_pat_inited) + { + insert_pat_inited= init_dynamic_string(&insert_pat, "", 1024, 1024); + } + else + dynstr_set(&insert_pat, ""); + + insert_option= ((opt_delayed && opt_ignore) ? " DELAYED IGNORE " : + opt_delayed ? " DELAYED " : + opt_ignore ? " IGNORE " : ""); if (verbose) fprintf(stderr, "-- Retrieving table structure for table %s...\n", table); - sprintf(insert_pat,"SET OPTION SQL_QUOTE_SHOW_CREATE=%d", - (opt_quoted || opt_keywords)); + len= my_snprintf(query_buff, sizeof(query_buff), + "SET OPTION SQL_QUOTE_SHOW_CREATE=%d", + (opt_quoted || opt_keywords)); + if (!create_options) + strmov(query_buff+len, "/*!40102 ,SQL_MODE=concat(@@sql_mode, _utf8 ',NO_KEY_OPTIONS,NO_TABLE_OPTIONS,NO_FIELD_OPTIONS') */"); + result_table= quote_name(table, table_buff, 1); opt_quoted_table= quote_name(table, table_buff2, 0); - if (!mysql_query(sock,insert_pat)) + + if (opt_order_by_primary) + order_by = primary_key_fields(opt_quoted_table); + + if (!opt_xml && !mysql_query_with_error_report(sock, 0, query_buff)) { /* using SHOW CREATE statement */ if (!tFlag) @@ -650,11 +1205,9 @@ static uint getTableStructure(char *table, char* db) /* Make an sql-file, if path was given iow. option -T was given */ char buff[20+FN_REFLEN]; - sprintf(buff,"show create table %s", result_table); - if (mysql_query(sock, buff)) + my_snprintf(buff, sizeof(buff), "show create table %s", result_table); + if (mysql_query_with_error_report(sock, 0, buff)) { - fprintf(stderr, "%s: Can't get CREATE TABLE for table %s (%s)\n", - my_progname, result_table, mysql_error(sock)); safe_exit(EX_MYSQLERR); DBUG_RETURN(0); } @@ -673,63 +1226,74 @@ static uint getTableStructure(char *table, char* db) write_header(sql_file, db); } if (!opt_xml && opt_comments) - fprintf(sql_file, "\n--\n-- Table structure for table %s\n--\n\n", + { + fprintf(sql_file, "\n--\n-- Table structure for table %s\n--\n\n", result_table); + check_io(sql_file); + } if (opt_drop) + { fprintf(sql_file, "DROP TABLE IF EXISTS %s;\n", opt_quoted_table); + check_io(sql_file); + } tableRes=mysql_store_result(sock); row=mysql_fetch_row(tableRes); - if (!opt_xml) - fprintf(sql_file, "%s;\n", row[1]); + fprintf(sql_file, "%s;\n", row[1]); + check_io(sql_file); mysql_free_result(tableRes); } - sprintf(insert_pat,"show fields from %s", result_table); - if (mysql_query(sock,insert_pat) || !(tableRes=mysql_store_result(sock))) + my_snprintf(query_buff, sizeof(query_buff), "show fields from %s", + result_table); + if (mysql_query_with_error_report(sock, &tableRes, query_buff)) { - fprintf(stderr, "%s: Can't get info about table: %s\nerror: %s\n", - my_progname, result_table, mysql_error(sock)); if (path) my_fclose(sql_file, MYF(MY_WME)); safe_exit(EX_MYSQLERR); DBUG_RETURN(0); } - if (cFlag) - sprintf(insert_pat, "INSERT %sINTO %s (", delayed, opt_quoted_table); + 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); + if (opt_complete_insert) + { + dynstr_append_mem(&insert_pat, " (", 2); + } else { - sprintf(insert_pat, "INSERT %sINTO %s VALUES ", delayed, - opt_quoted_table); + dynstr_append_mem(&insert_pat, " VALUES ", 8); if (!extended_insert) - strcat(insert_pat,"("); + dynstr_append_mem(&insert_pat, "(", 1); } - strpos=strend(insert_pat); while ((row=mysql_fetch_row(tableRes))) { if (init) { - if (cFlag) - strpos=strmov(strpos,", "); + if (opt_complete_insert) + dynstr_append_mem(&insert_pat, ", ", 2); } init=1; - if (cFlag) - strpos=strmov(strpos,quote_name(row[SHOW_FIELDNAME], name_buff, 0)); + if (opt_complete_insert) + dynstr_append(&insert_pat, + quote_name(row[SHOW_FIELDNAME], name_buff, 0)); } numFields = (uint) mysql_num_rows(tableRes); mysql_free_result(tableRes); } else { - /* fprintf(stderr, "%s: Can't set SQL_QUOTE_SHOW_CREATE option (%s)\n", - my_progname, mysql_error(sock)); */ + if (verbose) + fprintf(stderr, + "%s: Warning: Can't set SQL_QUOTE_SHOW_CREATE option (%s)\n", + my_progname, mysql_error(sock)); - sprintf(insert_pat,"show fields from %s", result_table); - if (mysql_query(sock,insert_pat) || !(tableRes=mysql_store_result(sock))) + my_snprintf(query_buff, sizeof(query_buff), "show fields from %s", + result_table); + if (mysql_query_with_error_report(sock, &tableRes, query_buff)) { - fprintf(stderr, "%s: Can't get info about table: %s\nerror: %s\n", - my_progname, result_table, mysql_error(sock)); safe_exit(EX_MYSQLERR); DBUG_RETURN(0); } @@ -755,33 +1319,53 @@ static uint getTableStructure(char *table, char* db) result_table); if (opt_drop) fprintf(sql_file, "DROP TABLE IF EXISTS %s;\n",result_table); - fprintf(sql_file, "CREATE TABLE %s (\n", result_table); + if (!opt_xml) + fprintf(sql_file, "CREATE TABLE %s (\n", result_table); + else + print_xml_tag1(sql_file, "\t", "table_structure name=", table, "\n"); + check_io(sql_file); + } + + 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); + if (opt_complete_insert) + { + dynstr_append_mem(&insert_pat, " (", 2); } - if (cFlag) - sprintf(insert_pat, "INSERT %sINTO %s (", delayed, result_table); else { - sprintf(insert_pat, "INSERT %sINTO %s VALUES ", delayed, result_table); + dynstr_append_mem(&insert_pat, " VALUES ", 8); if (!extended_insert) - strcat(insert_pat,"("); + dynstr_append_mem(&insert_pat, "(", 1); } - strpos=strend(insert_pat); while ((row=mysql_fetch_row(tableRes))) { ulong *lengths=mysql_fetch_lengths(tableRes); if (init) { - if (!tFlag) + if (!opt_xml && !tFlag) + { fputs(",\n",sql_file); - if (cFlag) - strpos=strmov(strpos,", "); + check_io(sql_file); + } + if (opt_complete_insert) + dynstr_append_mem(&insert_pat, ", ", 2); } init=1; - if (cFlag) - strpos=strmov(strpos,quote_name(row[SHOW_FIELDNAME], name_buff, 0)); + if (opt_complete_insert) + dynstr_append(&insert_pat, + quote_name(row[SHOW_FIELDNAME], name_buff, 0)); if (!tFlag) { + if (opt_xml) + { + print_xml_row(sql_file, "field", tableRes, &row); + continue; + } + if (opt_keywords) fprintf(sql_file, " %s.%s %s", result_table, quote_name(row[SHOW_FIELDNAME],name_buff, 0), @@ -799,6 +1383,7 @@ static uint getTableStructure(char *table, char* db) fputs(" NOT NULL", sql_file); if (row[SHOW_EXTRA][0]) fprintf(sql_file, " %s",row[SHOW_EXTRA]); + check_io(sql_file); } } numFields = (uint) mysql_num_rows(tableRes); @@ -808,18 +1393,15 @@ static uint getTableStructure(char *table, char* db) /* Make an sql-file, if path was given iow. option -T was given */ char buff[20+FN_REFLEN]; uint keynr,primary_key; - sprintf(buff,"show keys from %s", result_table); - if (mysql_query(sock, buff)) + my_snprintf(buff, sizeof(buff), "show keys from %s", result_table); + if (mysql_query_with_error_report(sock, &tableRes, buff)) { - fprintf(stderr, "%s: Can't get keys for table %s (%s)\n", - my_progname, result_table, mysql_error(sock)); if (path) my_fclose(sql_file, MYF(MY_WME)); safe_exit(EX_MYSQLERR); DBUG_RETURN(0); } - tableRes=mysql_store_result(sock); /* Find first which key is primary key */ keynr=0; primary_key=INT_MAX; @@ -843,6 +1425,12 @@ static uint getTableStructure(char *table, char* db) keynr=0; while ((row=mysql_fetch_row(tableRes))) { + if (opt_xml) + { + print_xml_row(sql_file, "key", tableRes, &row); + continue; + } + if (atoi(row[3]) == 1) { if (keynr++) @@ -853,25 +1441,35 @@ static uint getTableStructure(char *table, char* db) else if (keynr == primary_key) fputs(",\n PRIMARY KEY (",sql_file); /* First UNIQUE is primary */ else - fprintf(sql_file, ",\n UNIQUE %s (",quote_name(row[2],name_buff,0)); + fprintf(sql_file, ",\n UNIQUE %s (",quote_name(row[2],name_buff, + 0)); } else putc(',', sql_file); fputs(quote_name(row[4], name_buff, 0), sql_file); if (row[7]) fprintf(sql_file, " (%s)",row[7]); /* Sub key */ + check_io(sql_file); + } + if (!opt_xml) + { + if (keynr) + putc(')', sql_file); + fputs("\n)",sql_file); + check_io(sql_file); } - if (keynr) - putc(')', sql_file); - fputs("\n)",sql_file); /* Get MySQL specific create options */ if (create_options) { char show_name_buff[FN_REFLEN]; - sprintf(buff,"show table status like %s", - quote_for_like(table, show_name_buff)); - if (mysql_query(sock, buff)) + + /* Check memory for quote_for_like() */ + DBUG_ASSERT(2*sizeof(table) < sizeof(show_name_buff)); + my_snprintf(buff, sizeof(buff), "show table status like %s", + quote_for_like(table, show_name_buff)); + + if (mysql_query_with_error_report(sock, &tableRes, buff)) { if (mysql_errno(sock) != ER_PARSE_ERROR) { /* If old MySQL version */ @@ -881,8 +1479,7 @@ static uint getTableStructure(char *table, char* db) result_table,mysql_error(sock)); } } - else if (!(tableRes=mysql_store_result(sock)) || - !(row=mysql_fetch_row(tableRes))) + else if (!(row=mysql_fetch_row(tableRes))) { fprintf(stderr, "Error: Couldn't read status information for table %s (%s)\n", @@ -890,25 +1487,41 @@ static uint getTableStructure(char *table, char* db) } else { - fputs("/*!",sql_file); - print_value(sql_file,tableRes,row,"type=","Type",0); - print_value(sql_file,tableRes,row,"","Create_options",0); - print_value(sql_file,tableRes,row,"comment=","Comment",1); - fputs(" */",sql_file); + if (opt_xml) + { + print_xml_row(sql_file, "options", tableRes, &row); + } + else + { + fputs("/*!",sql_file); + print_value(sql_file,tableRes,row,"engine=","Engine",0); + print_value(sql_file,tableRes,row,"","Create_options",0); + print_value(sql_file,tableRes,row,"comment=","Comment",1); + fputs(" */",sql_file); + check_io(sql_file); + } } mysql_free_result(tableRes); /* Is always safe to free */ } - fputs(";\n", sql_file); + if (!opt_xml) + fputs(";\n", sql_file); + else + fputs("\t</table_structure>\n", sql_file); + check_io(sql_file); } } - if (cFlag) + if (opt_complete_insert) { - strpos=strmov(strpos,") VALUES "); + dynstr_append_mem(&insert_pat, ") VALUES ", 9); if (!extended_insert) - strpos=strmov(strpos,"("); + dynstr_append_mem(&insert_pat, "(", 1); } if (sql_file != md_result_file) + { + fputs("\n", sql_file); + write_footer(sql_file); my_fclose(sql_file, MYF(MY_WME)); + } DBUG_RETURN(numFields); } /* getTableStructure */ @@ -1015,13 +1628,10 @@ static void dumpTable(uint numFields, char *table) my_delete(filename, MYF(0)); /* 'INTO OUTFILE' doesn't work, if filename wasn't deleted */ to_unix_path(filename); - sprintf(query, "SELECT /*!40001 SQL_NO_CACHE */ * INTO OUTFILE '%s'", - filename); + my_snprintf(query, QUERY_LENGTH, + "SELECT /*!40001 SQL_NO_CACHE */ * INTO OUTFILE '%s'", + filename); end= strend(query); - if (replace) - end= strmov(end, " REPLACE"); - if (ignore) - end= strmov(end, " IGNORE"); if (fields_terminated || enclosed || opt_enclosed || escaped) end= strmov(end, " FIELDS"); @@ -1032,12 +1642,19 @@ static void dumpTable(uint numFields, char *table) end= add_load_option(end, lines_terminated, " LINES TERMINATED BY"); *end= '\0'; - sprintf(buff," FROM %s", result_table); + my_snprintf(buff, sizeof(buff), " FROM %s", result_table); end= strmov(end,buff); - if (where) + if (where || order_by) { - query= alloc_query_str((ulong) (strlen(where) + (end - query) + 10)); - end= strxmov(query, query_buf, " WHERE ", where, NullS); + query = alloc_query_str((ulong) ((end - query) + 1 + + (where ? strlen(where) + 7 : 0) + + (order_by ? strlen(order_by) + 10 : 0))); + end = strmov(query, query_buf); + + if (where) + end = strxmov(end, " WHERE ", where, NullS); + if (order_by) + end = strxmov(end, " ORDER BY ", order_by, NullS); } if (mysql_real_query(sock, query, (uint) (end - query))) { @@ -1048,35 +1665,53 @@ static void dumpTable(uint numFields, char *table) else { if (!opt_xml && opt_comments) + { fprintf(md_result_file,"\n--\n-- Dumping data for table %s\n--\n", result_table); - sprintf(query, "SELECT /*!40001 SQL_NO_CACHE */ * FROM %s", - result_table); - if (where) + check_io(md_result_file); + } + my_snprintf(query, QUERY_LENGTH, + "SELECT /*!40001 SQL_NO_CACHE */ * FROM %s", + result_table); + if (where || order_by) { - if (!opt_xml && opt_comments) - fprintf(md_result_file,"-- WHERE: %s\n",where); - query= alloc_query_str((ulong) (strlen(where) + strlen(query) + 10)); - strxmov(query, query_buf, " WHERE ", where, NullS); + query = alloc_query_str((ulong) (strlen(query) + 1 + + (where ? strlen(where) + 7 : 0) + + (order_by ? strlen(order_by) + 10 : 0))); + end = strmov(query, query_buf); + + if (where) + { + if (!opt_xml && opt_comments) + { + fprintf(md_result_file, "-- WHERE: %s\n", where); + check_io(md_result_file); + } + end = strxmov(end, " WHERE ", where, NullS); + } + if (order_by) + { + if (!opt_xml && opt_comments) + { + fprintf(md_result_file, "-- ORDER BY: %s\n", order_by); + check_io(md_result_file); + } + end = strxmov(end, " ORDER BY ", order_by, NullS); + } } - if (!opt_xml) - fputs("\n", md_result_file); - if (mysql_query(sock, query)) + if (!opt_xml && !opt_compact) { - DBerror(sock, "when retrieving data from server"); - error= EX_CONSCHECK; - goto err; + fputs("\n", md_result_file); + check_io(md_result_file); } + if (mysql_query_with_error_report(sock, 0, query)) + DBerror(sock, "when retrieving data from server"); if (quick) res=mysql_use_result(sock); else res=mysql_store_result(sock); if (!res) - { DBerror(sock, "when retrieving data from server"); - error= EX_CONSCHECK; - goto err; - } if (verbose) fprintf(stderr, "-- Retrieving rows...\n"); if (mysql_num_fields(res) != numFields) @@ -1088,20 +1723,29 @@ static void dumpTable(uint numFields, char *table) } if (opt_disable_keys) + { fprintf(md_result_file, "\n/*!40000 ALTER TABLE %s DISABLE KEYS */;\n", opt_quoted_table); + check_io(md_result_file); + } if (opt_lock) + { fprintf(md_result_file,"LOCK TABLES %s WRITE;\n", opt_quoted_table); + check_io(md_result_file); + } total_length= opt_net_buffer_length; /* Force row break */ row_break=0; rownr=0; - init_length=(uint) strlen(insert_pat)+4; + init_length=(uint) insert_pat.length+4; if (opt_xml) - fprintf(md_result_file, "\t<table name=\"%s\">\n", table); + print_xml_tag1(md_result_file, "\t", "table_data name=", table, "\n"); if (opt_autocommit) + { fprintf(md_result_file, "set autocommit=0;\n"); + check_io(md_result_file); + } while ((row=mysql_fetch_row(res))) { @@ -1109,25 +1753,37 @@ static void dumpTable(uint numFields, char *table) ulong *lengths=mysql_fetch_lengths(res); rownr++; if (!extended_insert && !opt_xml) - fputs(insert_pat,md_result_file); + { + fputs(insert_pat.str,md_result_file); + check_io(md_result_file); + } mysql_field_seek(res,0); if (opt_xml) - fprintf(md_result_file, "\t<row>\n"); + { + fputs("\t<row>\n", md_result_file); + check_io(md_result_file); + } for (i = 0; i < mysql_num_fields(res); i++) { int is_blob; if (!(field = mysql_fetch_field(res))) { - sprintf(query,"%s: Not enough fields from table %s! Aborting.\n", - my_progname, result_table); + my_snprintf(query, QUERY_LENGTH, + "%s: Not enough fields from table %s! Aborting.\n", + my_progname, result_table); fputs(query,stderr); error= EX_CONSCHECK; goto err; } - is_blob= (opt_hex_blob && (field->flags & BINARY_FLAG) && + /* + 63 is my_charset_bin. If charsetnr is not 63, + we have not a BLOB but a TEXT column. + we'll dump in hex only BLOB columns. + */ + is_blob= (opt_hex_blob && field->charsetnr == 63 && (field->type == FIELD_TYPE_STRING || field->type == FIELD_TYPE_VAR_STRING || field->type == FIELD_TYPE_BLOB || @@ -1171,29 +1827,30 @@ static void dumpTable(uint numFields, char *table) } else { - dynstr_append(&extended_row,"\'"); - extended_row.length += - mysql_real_escape_string(&mysql_connection, - &extended_row.str[extended_row.length], - row[i],length); - extended_row.str[extended_row.length]='\0'; - dynstr_append(&extended_row,"\'"); - } + dynstr_append(&extended_row,"'"); + extended_row.length += + mysql_real_escape_string(&mysql_connection, + &extended_row.str[extended_row.length], + row[i],length); + extended_row.str[extended_row.length]='\0'; + dynstr_append(&extended_row,"'"); + } } else { /* change any strings ("inf", "-inf", "nan") into NULL */ char *ptr = row[i]; - if (isalpha(*ptr) || (*ptr == '-' && isalpha(ptr[1]))) + if (my_isalpha(charset_info, *ptr) || (*ptr == '-' && + my_isalpha(charset_info, ptr[1]))) dynstr_append(&extended_row, "NULL"); else { if (field->type == FIELD_TYPE_DECIMAL) { /* add " signs around */ - dynstr_append(&extended_row, "\'"); + dynstr_append(&extended_row, "'"); dynstr_append(&extended_row, ptr); - dynstr_append(&extended_row, "\'"); + dynstr_append(&extended_row, "'"); } else dynstr_append(&extended_row, ptr); @@ -1201,7 +1858,7 @@ static void dumpTable(uint numFields, char *table) } } else - dynstr_append(&extended_row,"\'\'"); + dynstr_append(&extended_row,"''"); } else if (dynstr_append(&extended_row,"NULL")) { @@ -1213,65 +1870,76 @@ static void dumpTable(uint numFields, char *table) else { if (i && !opt_xml) + { fputc(',', md_result_file); + check_io(md_result_file); + } if (row[i]) { if (!IS_NUM_FIELD(field)) { if (opt_xml) - print_quoted_xml(md_result_file, field->name, row[i], - lengths[i]); + { + print_xml_tag1(md_result_file, "\t\t", "field name=", + field->name, ""); + print_quoted_xml(md_result_file, row[i], lengths[i]); + fputs("</field>\n", md_result_file); + } else if (opt_hex_blob && is_blob) - { /* sakaik got this idea. */ - ulong counter; - char xx[4]; - unsigned char *ptr= row[i]; + { + /* sakaik got the idea to to provide blob's in hex notation. */ + char *ptr= row[i], *end= ptr+ lengths[i]; fputs("0x", md_result_file); - for (counter = 0; counter < lengths[i]; counter++) - { - sprintf(xx, "%02X", ptr[counter]); - fputs(xx, md_result_file); - } + for (; ptr < end ; ptr++) + fprintf(md_result_file, "%02X", *((uchar *)ptr)); } else - unescape(md_result_file, row[i], lengths[i]); + unescape(md_result_file, row[i], lengths[i]); } else { /* change any strings ("inf", "-inf", "nan") into NULL */ char *ptr = row[i]; if (opt_xml) - fprintf(md_result_file, "\t\t<field name=\"%s\">%s</field>\n", - field->name,!isalpha(*ptr) ?ptr: "NULL"); - else if (isalpha(*ptr) || (*ptr == '-' && isalpha(ptr[1]))) + { + print_xml_tag1(md_result_file, "\t\t", "field name=", + field->name, ""); + fputs(!my_isalpha(charset_info, *ptr) ? ptr: "NULL", + md_result_file); + fputs("</field>\n", md_result_file); + } + else if (my_isalpha(charset_info, *ptr) || + (*ptr == '-' && my_isalpha(charset_info, ptr[1]))) fputs("NULL", md_result_file); - else + else if (field->type == FIELD_TYPE_DECIMAL) { - if (field->type == FIELD_TYPE_DECIMAL) - { - /* add " signs around */ - fputs("\'", md_result_file); - fputs(ptr, md_result_file); - fputs("\'", md_result_file); - } - else - fputs(ptr, md_result_file); + /* add " signs around */ + fputc('\'', md_result_file); + fputs(ptr, md_result_file); + fputc('\'', md_result_file); } + else + fputs(ptr, md_result_file); } } else - { - if (opt_xml) - fprintf(md_result_file, "\t\t<field name=\"%s\">%s</field>\n", - field->name, "NULL"); - else - fputs("NULL", md_result_file); - } + { + /* The field value is NULL */ + if (!opt_xml) + fputs("NULL", md_result_file); + else + print_xml_null_tag(md_result_file, "\t\t", "field name=", + field->name, "\n"); + } + check_io(md_result_file); } } if (opt_xml) - fprintf(md_result_file, "\t</row>\n"); + { + fputs("\t</row>\n", md_result_file); + check_io(md_result_file); + } if (extended_insert) { @@ -1286,47 +1954,59 @@ static void dumpTable(uint numFields, char *table) } else { - if (row_break && !opt_xml) + if (row_break) fputs(";\n", md_result_file); row_break=1; /* This is first row */ - if (!opt_xml) - { - fputs(insert_pat,md_result_file); - fputs(extended_row.str,md_result_file); - } + fputs(insert_pat.str,md_result_file); + fputs(extended_row.str,md_result_file); total_length = row_length+init_length; } + check_io(md_result_file); } else if (!opt_xml) + { fputs(");\n", md_result_file); + check_io(md_result_file); + } } /* XML - close table tag and supress regular output */ if (opt_xml) - fprintf(md_result_file, "\t</table>\n"); + fputs("\t</table_data>\n", md_result_file); else if (extended_insert && row_break) fputs(";\n", md_result_file); /* If not empty table */ fflush(md_result_file); + check_io(md_result_file); if (mysql_errno(sock)) { - sprintf(query,"%s: Error %d: %s when dumping table %s at row: %ld\n", - my_progname, - mysql_errno(sock), - mysql_error(sock), - result_table, - rownr); + my_snprintf(query, QUERY_LENGTH, + "%s: Error %d: %s when dumping table %s at row: %ld\n", + my_progname, + mysql_errno(sock), + mysql_error(sock), + result_table, + rownr); fputs(query,stderr); error= EX_CONSCHECK; goto err; } if (opt_lock) + { fputs("UNLOCK TABLES;\n", md_result_file); + check_io(md_result_file); + } if (opt_disable_keys) + { fprintf(md_result_file,"/*!40000 ALTER TABLE %s ENABLE KEYS */;\n", opt_quoted_table); + check_io(md_result_file); + } if (opt_autocommit) + { fprintf(md_result_file, "commit;\n"); + check_io(md_result_file); + } mysql_free_result(res); if (query != query_buf) my_free(query, MYF(MY_ALLOW_ZERO_PTR)); @@ -1341,27 +2021,6 @@ err: } /* dumpTable */ -static void print_quoted_xml(FILE *output, char *fname, char *str, uint len) -{ - const char *end; - - fprintf(output, "\t\t<field name=\"%s\">", fname); - for (end = str + len; str != end; str++) - { - if (*str == '<') - fputs("<", output); - else if (*str == '>') - fputs(">", output); - else if (*str == '&') - fputs("&", output); - else if (*str == '\"') - fputs(""", output); - else - fputc(*str, output); - } - fprintf(output, "</field>\n"); -} - static char *getTableName(int reset) { static MYSQL_RES *res = NULL; @@ -1392,13 +2051,8 @@ static int dump_all_databases() MYSQL_RES *tableres; int result=0; - if (mysql_query(sock, "SHOW DATABASES") || - !(tableres = mysql_store_result(sock))) - { - my_printf_error(0, "Error: Couldn't execute 'SHOW DATABASES': %s", - MYF(0), mysql_error(sock)); + if (mysql_query_with_error_report(sock, &tableres, "SHOW DATABASES")) return 1; - } while ((row = mysql_fetch_row(tableres))) { if (dump_all_tables_in_db(row[0])) @@ -1432,39 +2086,90 @@ static int init_dumping(char *database) { if (opt_databases || opt_alldbs) { - /* length of table name * 2 (if name contain quotas), 2 quotas and 0 */ + /* + length of table name * 2 (if name contains quotes), 2 quotes and 0 + */ char quoted_database_buf[64*2+3]; char *qdatabase= quote_name(database,quoted_database_buf,opt_quoted); if (opt_comments) - fprintf(md_result_file,"\n--\n-- Current Database: %s\n--\n", database); + { + fprintf(md_result_file,"\n--\n-- Current Database: %s\n--\n", qdatabase); + check_io(md_result_file); + } if (!opt_create_db) - fprintf(md_result_file,"\nCREATE DATABASE /*!32312 IF NOT EXISTS*/ %s;\n", - qdatabase); - fprintf(md_result_file,"\nUSE %s;\n", database); + { + char qbuf[256]; + MYSQL_ROW row; + MYSQL_RES *dbinfo; + + my_snprintf(qbuf, sizeof(qbuf), + "SHOW CREATE DATABASE IF NOT EXISTS %s", + qdatabase); + + if (mysql_query(sock, qbuf) || !(dbinfo = mysql_store_result(sock))) + { + /* Old server version, dump generic CREATE DATABASE */ + if (opt_drop_database) + fprintf(md_result_file, + "\n/*!40000 DROP DATABASE IF EXISTS %s;*/\n", + qdatabase); + fprintf(md_result_file, + "\nCREATE DATABASE /*!32312 IF NOT EXISTS*/ %s;\n", + qdatabase); + } + else + { + if (opt_drop_database) + fprintf(md_result_file, + "\n/*!40000 DROP DATABASE IF EXISTS %s*/;\n", + qdatabase); + row = mysql_fetch_row(dbinfo); + if (row[1]) + { + fprintf(md_result_file,"\n%s;\n",row[1]); + } + } + } + fprintf(md_result_file,"\nUSE %s;\n", qdatabase); + check_io(md_result_file); } } - if (extended_insert) - if (init_dynamic_string(&extended_row, "", 1024, 1024)) - exit(EX_EOM); + if (extended_insert && init_dynamic_string(&extended_row, "", 1024, 1024)) + exit(EX_EOM); return 0; } /* init_dumping */ +my_bool include_table(byte* hash_key, uint len) +{ + if (hash_search(&ignore_table, (byte*) hash_key, len)) + return FALSE; + + return TRUE; +} + + static int dump_all_tables_in_db(char *database) { char *table; uint numrows; char table_buff[NAME_LEN*2+3]; + char hash_key[2*NAME_LEN+2]; /* "db.tablename" */ + char *afterdot; + + afterdot= strmov(hash_key, database); + *afterdot++= '.'; + if (init_dumping(database)) return 1; if (opt_xml) - fprintf(md_result_file, "<database name=\"%s\">\n", database); + print_xml_tag1(md_result_file, "", "database name=", database, "\n"); if (lock_tables) { DYNAMIC_STRING query; init_dynamic_string(&query, "LOCK TABLES ", 256, 1024); - for (numrows=0 ; (table = getTableName(1)) ; numrows++) + for (numrows= 0 ; (table= getTableName(1)) ; numrows++) { dynstr_append(&query, quote_name(table, table_buff, 1)); dynstr_append(&query, " READ /*!32311 LOCAL */,"); @@ -1480,44 +2185,137 @@ static int dump_all_tables_in_db(char *database) DBerror(sock, "when doing refresh"); /* We shall continue here, if --force was given */ } - while ((table = getTableName(0))) + while ((table= getTableName(0))) { - numrows = getTableStructure(table, database); - if (!dFlag && numrows > 0) - dumpTable(numrows,table); + char *end= strmov(afterdot, table); + if (include_table(hash_key, end - hash_key)) + { + numrows = getTableStructure(table, database); + if (!dFlag && numrows > 0) + dumpTable(numrows,table); + my_free(order_by, MYF(MY_ALLOW_ZERO_PTR)); + order_by= 0; + } } if (opt_xml) - fprintf(md_result_file, "</database>\n"); + { + fputs("</database>\n", md_result_file); + check_io(md_result_file); + } if (lock_tables) - mysql_query(sock,"UNLOCK TABLES"); + mysql_query_with_error_report(sock, 0, "UNLOCK TABLES"); return 0; } /* dump_all_tables_in_db */ +/* + get_actual_table_name -- executes a SHOW TABLES LIKE '%s' to get the actual + table name from the server for the table name given on the command line. + we do this because the table name given on the command line may be a + different case (e.g. T1 vs t1) + + RETURN + int - 0 if a tablename was retrieved. 1 if not +*/ + +static int get_actual_table_name(const char *old_table_name, + char *new_table_name, + int buf_size) +{ + int retval; + MYSQL_RES *tableRes; + MYSQL_ROW row; + char query[50 + 2*NAME_LEN]; + char show_name_buff[FN_REFLEN]; + DBUG_ENTER("get_actual_table_name"); + DBUG_PRINT("enter", ("old_table_name: %s", old_table_name)); + + /* Check memory for quote_for_like() */ + DBUG_ASSERT(2*sizeof(old_table_name) < sizeof(show_name_buff)); + my_snprintf(query, sizeof(query), "SHOW TABLES LIKE %s", + quote_for_like(old_table_name, show_name_buff)); + + if (mysql_query_with_error_report(sock, 0, query)) + { + safe_exit(EX_MYSQLERR); + } + + tableRes= mysql_store_result( sock ); + retval = 1; + if (tableRes != NULL) + { + my_ulonglong numRows= mysql_num_rows(tableRes); + if (numRows > 0) + { + row= mysql_fetch_row( tableRes ); + strmake(new_table_name, row[0], buf_size-1); + retval= 0; + DBUG_PRINT("info", ("new_table_name: %s", new_table_name)); + } + mysql_free_result(tableRes); + } + DBUG_PRINT("exit", ("retval: %d", retval)); + DBUG_RETURN(retval); +} + static int dump_selected_tables(char *db, char **table_names, int tables) { - uint numrows; + uint numrows, i; char table_buff[NAME_LEN*+3]; + char new_table_name[NAME_LEN]; + DYNAMIC_STRING lock_tables_query; + HASH dump_tables; + DBUG_ENTER("dump_selected_tables"); if (init_dumping(db)) return 1; - if (lock_tables) + + /* Init hash table for storing the actual name of tables to dump */ + if (hash_init(&dump_tables, charset_info, 16, 0, 0, + (hash_get_key) get_table_key, (hash_free_key) free_table_ent, + 0)) + exit(EX_EOM); + + init_dynamic_string(&lock_tables_query, "LOCK TABLES ", 256, 1024); + for (; tables > 0 ; tables-- , table_names++) { - DYNAMIC_STRING query; - int i; - init_dynamic_string(&query, "LOCK TABLES ", 256, 1024); - for (i=0 ; i < tables ; i++) + /* the table name passed on commandline may be wrong case */ + if (!get_actual_table_name(*table_names, + new_table_name, sizeof(new_table_name) )) { - dynstr_append(&query, quote_name(table_names[i], table_buff, 1)); - dynstr_append(&query, " READ /*!32311 LOCAL */,"); + /* Add found table name to lock_tables_query */ + if (lock_tables) + { + dynstr_append(&lock_tables_query, + quote_name(new_table_name, table_buff, 1)); + dynstr_append(&lock_tables_query, " READ /*!32311 LOCAL */,"); + } + + /* Add found table name to dump_tables list */ + if (my_hash_insert(&dump_tables, + (byte*)my_strdup(new_table_name, MYF(0)))) + exit(EX_EOM); + } - if (mysql_real_query(sock, query.str, query.length-1)) + else + { + my_printf_error(0,"Couldn't find table: \"%s\"\n", MYF(0), + *table_names); + safe_exit(EX_ILLEGAL_TABLE); + /* We shall countinue here, if --force was given */ + } + } + + if (lock_tables) + { + if (mysql_real_query(sock, lock_tables_query.str, + lock_tables_query.length-1)) DBerror(sock, "when doing LOCK TABLES"); /* We shall countinue here, if --force was given */ - dynstr_free(&query); } + dynstr_free(&lock_tables_query); if (flush_logs) { if (mysql_refresh(sock, REFRESH_LOG)) @@ -1525,21 +2323,164 @@ static int dump_selected_tables(char *db, char **table_names, int tables) /* We shall countinue here, if --force was given */ } if (opt_xml) - fprintf(md_result_file, "<database name=\"%s\">\n", db); - for (; tables > 0 ; tables-- , table_names++) + print_xml_tag1(md_result_file, "", "database name=", db, "\n"); + + /* Dump each selected table */ + for (i= 0 ; i < dump_tables.records ; i++) { - numrows = getTableStructure(*table_names, db); + const char *table_name= hash_element(&dump_tables, i); + DBUG_PRINT("info",("Dumping table %s", table_name)); + numrows= getTableStructure((char*) table_name, db); if (!dFlag && numrows > 0) - dumpTable(numrows, *table_names); + dumpTable(numrows, (char*) table_name); } + hash_free(&dump_tables); + my_free(order_by, MYF(MY_ALLOW_ZERO_PTR)); + order_by= 0; if (opt_xml) - fprintf(md_result_file, "</database>\n"); + { + fputs("</database>\n", md_result_file); + check_io(md_result_file); + } if (lock_tables) - mysql_query(sock,"UNLOCK TABLES"); - return 0; + mysql_query_with_error_report(sock, 0, "UNLOCK TABLES"); + DBUG_RETURN(0); } /* dump_selected_tables */ +static int do_show_master_status(MYSQL *mysql_con) +{ + MYSQL_ROW row; + MYSQL_RES *master; + const char *comment_prefix= + (opt_master_data == MYSQL_OPT_MASTER_DATA_COMMENTED_SQL) ? "-- " : ""; + if (mysql_query_with_error_report(mysql_con, &master, "SHOW MASTER STATUS")) + { + my_printf_error(0, "Error: Couldn't execute 'SHOW MASTER STATUS': %s", + MYF(0), mysql_error(mysql_con)); + return 1; + } + else + { + row = mysql_fetch_row(master); + if (row && row[0] && row[1]) + { + /* SHOW MASTER STATUS reports file and position */ + if (opt_comments) + fprintf(md_result_file, + "\n--\n-- Position to start replication or point-in-time " + "recovery from\n--\n\n"); + fprintf(md_result_file, + "%sCHANGE MASTER TO MASTER_LOG_FILE='%s', MASTER_LOG_POS=%s;\n", + comment_prefix, row[0], row[1]); + check_io(md_result_file); + } + else if (!ignore_errors) + { + /* SHOW MASTER STATUS reports nothing and --force is not enabled */ + my_printf_error(0, "Error: Binlogging on server not active", + MYF(0)); + mysql_free_result(master); + return 1; + } + mysql_free_result(master); + } + return 0; +} + + +static int do_flush_tables_read_lock(MYSQL *mysql_con) +{ + /* + We do first a FLUSH TABLES. If a long update is running, the FLUSH TABLES + will wait but will not stall the whole mysqld, and when the long update is + done the FLUSH TABLES WITH READ LOCK will start and succeed quickly. So, + FLUSH TABLES is to lower the probability of a stage where both mysqldump + and most client connections are stalled. Of course, if a second long + update starts between the two FLUSHes, we have that bad stall. + */ + return + ( mysql_query_with_error_report(mysql_con, 0, "FLUSH TABLES") || + mysql_query_with_error_report(mysql_con, 0, + "FLUSH TABLES WITH READ LOCK") ); +} + + +static int do_unlock_tables(MYSQL *mysql_con) +{ + return mysql_query_with_error_report(mysql_con, 0, "UNLOCK TABLES"); +} + + +static int do_reset_master(MYSQL *mysql_con) +{ + return mysql_query_with_error_report(mysql_con, 0, "RESET MASTER"); +} + + +static int start_transaction(MYSQL *mysql_con, my_bool consistent_read_now) +{ + /* + We use BEGIN for old servers. --single-transaction --master-data will fail + on old servers, but that's ok as it was already silently broken (it didn't + do a consistent read, so better tell people frankly, with the error). + + We want the first consistent read to be used for all tables to dump so we + need the REPEATABLE READ level (not anything lower, for example READ + COMMITTED would give one new consistent read per dumped table). + */ + return (mysql_query_with_error_report(mysql_con, 0, + "SET SESSION TRANSACTION ISOLATION " + "LEVEL REPEATABLE READ") || + mysql_query_with_error_report(mysql_con, 0, + consistent_read_now ? + "START TRANSACTION " + "WITH CONSISTENT SNAPSHOT" : + "BEGIN")); +} + + +static ulong find_set(TYPELIB *lib, const char *x, uint length, + char **err_pos, uint *err_len) +{ + const char *end= x + length; + ulong found= 0; + uint find; + char buff[255]; + + *err_pos= 0; /* No error yet */ + while (end > x && my_isspace(charset_info, end[-1])) + end--; + + *err_len= 0; + if (x != end) + { + const char *start= x; + for (;;) + { + const char *pos= start; + uint var_len; + + for (; pos != end && *pos != ','; pos++) ; + var_len= (uint) (pos - start); + strmake(buff, start, min(sizeof(buff), var_len)); + find= find_type(buff, lib, var_len); + if (!find) + { + *err_pos= (char*) start; + *err_len= var_len; + } + else + found|= ((longlong) 1 << (find - 1)); + if (pos == end) + break; + start= pos + 1; + } + } + return found; +} + + /* Print a value with a prefix on file */ static void print_value(FILE *file, MYSQL_RES *result, MYSQL_ROW row, const char *prefix, const char *name, @@ -1560,6 +2501,7 @@ static void print_value(FILE *file, MYSQL_RES *result, MYSQL_ROW row, unescape(file,row[0],(uint) strlen(row[0])); else fputs(row[0], file); + check_io(file); return; } } @@ -1592,9 +2534,11 @@ static const char *check_if_ignore_table(const char *table_name) MYSQL_ROW row; const char *result= 0; - sprintf(buff,"show table status like %s", - quote_for_like(table_name, show_name_buff)); - if (mysql_query(sock, buff)) + /* Check memory for quote_for_like() */ + DBUG_ASSERT(2*sizeof(table_name) < sizeof(show_name_buff)); + my_snprintf(buff, sizeof(buff), "show table status like %s", + quote_for_like(table_name, show_name_buff)); + if (mysql_query_with_error_report(sock, &res, buff)) { if (mysql_errno(sock) != ER_PARSE_ERROR) { /* If old MySQL version */ @@ -1605,30 +2549,104 @@ static const char *check_if_ignore_table(const char *table_name) return 0; /* assume table is ok */ } } - if (!(res= mysql_store_result(sock)) || - !(row= mysql_fetch_row(res))) + if (!(row= mysql_fetch_row(res))) { fprintf(stderr, "Error: Couldn't read status information for table %s (%s)\n", table_name, mysql_error(sock)); - if (res) - mysql_free_result(res); + mysql_free_result(res); return 0; /* assume table is ok */ } if (strcmp(row[1], (result= "MRG_MyISAM")) && strcmp(row[1], (result= "MRG_ISAM"))) result= 0; - mysql_free_result(res); + mysql_free_result(res); + return result; +} + +/* + Get string of comma-separated primary key field names + + SYNOPSIS + char *primary_key_fields(const char *table_name) + RETURNS pointer to allocated buffer (must be freed by caller) + table_name quoted table name + + DESCRIPTION + Use SHOW KEYS FROM table_name, allocate a buffer to hold the + field names, and then build that string and return the pointer + to that buffer. + + Returns NULL if there is no PRIMARY or UNIQUE key on the table, + or if there is some failure. It is better to continue to dump + the table unsorted, rather than exit without dumping the data. +*/ +static char *primary_key_fields(const char *table_name) +{ + MYSQL_RES *res = NULL; + MYSQL_ROW row; + /* SHOW KEYS FROM + table name * 2 (escaped) + 2 quotes + \0 */ + char show_keys_buff[15 + 64 * 2 + 3]; + uint result_length = 0; + char *result = 0; + + my_snprintf(show_keys_buff, sizeof(show_keys_buff), + "SHOW KEYS FROM %s", table_name); + if (mysql_query(sock, show_keys_buff) || + !(res = mysql_store_result(sock))) + { + fprintf(stderr, "Warning: Couldn't read keys from table %s;" + " records are NOT sorted (%s)\n", + table_name, mysql_error(sock)); + /* Don't exit, because it's better to print out unsorted records */ + goto cleanup; + } + + /* + * Figure out the length of the ORDER BY clause result. + * Note that SHOW KEYS is ordered: a PRIMARY key is always the first + * row, and UNIQUE keys come before others. So we only need to check + * the first key, not all keys. + */ + if ((row = mysql_fetch_row(res)) && atoi(row[1]) == 0) + { + /* Key is unique */ + do + result_length += strlen(row[4]) + 1; /* + 1 for ',' or \0 */ + while ((row = mysql_fetch_row(res)) && atoi(row[3]) > 1); + } + + /* Build the ORDER BY clause result */ + if (result_length) { + char *end; + /* result (terminating \0 is already in result_length) */ + result = my_malloc(result_length + 10, MYF(MY_WME)); + if (!result) { + fprintf(stderr, "Error: Not enough memory to store ORDER BY clause\n"); + goto cleanup; + } + mysql_data_seek(res, 0); + row = mysql_fetch_row(res); + end = strmov(result, row[4]); + while ((row = mysql_fetch_row(res)) && atoi(row[3]) > 1) + end = strxmov(end, ",", row[4], NullS); + } + +cleanup: + if (res) + mysql_free_result(res); + return result; } int main(int argc, char **argv) { - MYSQL_ROW row; - MYSQL_RES *master; + compatible_mode_normal_str[0]= 0; + default_charset= (char *)mysql_universal_client_charset; + bzero((char*) &ignore_table, sizeof(ignore_table)); - MY_INIT(argv[0]); + MY_INIT("mysqldump"); if (get_options(&argc, &argv)) { my_end(0); @@ -1639,28 +2657,24 @@ int main(int argc, char **argv) if (!path) write_header(md_result_file, *argv); - if (opt_first_slave) - { - lock_tables=0; /* No other locks needed */ - if (mysql_query(sock, "FLUSH TABLES WITH READ LOCK")) - { - my_printf_error(0, "Error: Couldn't execute 'FLUSH TABLES WITH READ LOCK': %s", - MYF(0), mysql_error(sock)); - my_end(0); - return(first_error); - } - } - else if (opt_single_transaction) + if ((opt_lock_all_tables || opt_master_data) && + do_flush_tables_read_lock(sock)) + goto err; + if (opt_single_transaction && start_transaction(sock, test(opt_master_data))) + goto err; + if (opt_delete_master_logs && do_reset_master(sock)) + goto err; + if (opt_lock_all_tables || opt_master_data) { - /* There is no sense to start transaction if all tables are locked */ - if (mysql_query(sock, "BEGIN")) - { - my_printf_error(0, "Error: Couldn't execute 'BEGIN': %s", - MYF(0), mysql_error(sock)); - my_end(0); - return(first_error); - } + if (flush_logs && mysql_refresh(sock, REFRESH_LOG)) + goto err; + flush_logs= 0; /* not anymore; that would not be sensible */ } + if (opt_master_data && do_show_master_status(sock)) + goto err; + if (opt_single_transaction && do_unlock_tables(sock)) /* unlock but no commit! */ + goto err; + if (opt_alldbs) dump_all_databases(); else if (argc > 1 && !opt_databases) @@ -1673,60 +2687,28 @@ int main(int argc, char **argv) /* One or more databases, all tables */ dump_databases(argv); } - - if (opt_first_slave) - { - if (opt_delete_master_logs && mysql_query(sock, "FLUSH MASTER")) - { - my_printf_error(0, "Error: Couldn't execute 'FLUSH MASTER': %s", - MYF(0), mysql_error(sock)); - } - if (opt_master_data) - { - if (mysql_query(sock, "SHOW MASTER STATUS") || - !(master = mysql_store_result(sock))) - my_printf_error(0, "Error: Couldn't execute 'SHOW MASTER STATUS': %s", - MYF(0), mysql_error(sock)); - else - { - row = mysql_fetch_row(master); - if (row && row[0] && row[1]) - { - if (opt_comments) - fprintf(md_result_file, - "\n--\n-- Position to start replication from\n--\n\n"); - fprintf(md_result_file, - "CHANGE MASTER TO MASTER_LOG_FILE='%s', \ -MASTER_LOG_POS=%s ;\n",row[0],row[1]); - } - mysql_free_result(master); - } - } - if (mysql_query(sock, "UNLOCK TABLES")) - my_printf_error(0, "Error: Couldn't execute 'UNLOCK TABLES': %s", - MYF(0), mysql_error(sock)); - } - else if (opt_single_transaction) /* Just to make it beautiful enough */ - { - /* - In case we were locking all tables, we did not start transaction - so there is no need to commit it. - */ - - /* This should just free locks as we did not change anything */ - if (mysql_query(sock, "COMMIT")) - { - my_printf_error(0, "Error: Couldn't execute 'COMMIT': %s", - MYF(0), mysql_error(sock)); - } - } +#ifdef HAVE_SMEM + my_free(shared_memory_base_name,MYF(MY_ALLOW_ZERO_PTR)); +#endif + /* + No reason to explicitely COMMIT the transaction, neither to explicitely + UNLOCK TABLES: these will be automatically be done by the server when we + disconnect now. Saves some code here, some network trips, adds nothing to + server. + */ +err: dbDisconnect(current_host); - write_footer(md_result_file); + if (!path) + write_footer(md_result_file); if (md_result_file != stdout) my_fclose(md_result_file, MYF(0)); my_free(opt_password, MYF(MY_ALLOW_ZERO_PTR)); + if (hash_inited(&ignore_table)) + hash_free(&ignore_table); if (extended_insert) dynstr_free(&extended_row); + if (insert_pat_inited) + dynstr_free(&insert_pat); my_end(0); return(first_error); } /* main */ diff --git a/client/mysqlimport.c b/client/mysqlimport.c index 24392fedc1f..019e3080023 100644 --- a/client/mysqlimport.c +++ b/client/mysqlimport.c @@ -25,7 +25,7 @@ ** * * ** ************************* */ -#define IMPORT_VERSION "3.4" +#define IMPORT_VERSION "3.5" #include "client_priv.h" #include "mysql_version.h" @@ -43,12 +43,18 @@ 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, - *escaped=0, *opt_columns=0, *default_charset; -static uint opt_mysql_port=0; + *escaped=0, *opt_columns=0, + *default_charset= (char*) MYSQL_DEFAULT_CHARSET_NAME; +static uint opt_mysql_port= 0, opt_protocol= 0; static my_string opt_mysql_unix_port=0; static longlong opt_ignore_lines= -1; +static CHARSET_INFO *charset_info= &my_charset_latin1; #include <sslopt-vars.h> +#ifdef HAVE_SMEM +static char *shared_memory_base_name=0; +#endif + static struct my_option my_long_options[] = { #ifdef __NETWARE__ @@ -56,7 +62,7 @@ static struct my_option my_long_options[] = 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, #endif {"character-sets-dir", OPT_CHARSETS_DIR, - "Directory where character sets are", (gptr*) &charsets_dir, + "Directory where character sets are.", (gptr*) &charsets_dir, (gptr*) &charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"default-character-set", OPT_DEFAULT_CHARSET, "Set the default character set.", (gptr*) &default_charset, @@ -68,8 +74,8 @@ static struct my_option my_long_options[] = {"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}, - {"debug",'#', "Output debug log. Often this is 'd:t:o,filename'", 0, 0, 0, - GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, + {"debug",'#', "Output debug log. Often this is 'd:t:o,filename'.", 0, 0, 0, + GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, {"delete", 'd', "First delete all rows from table.", (gptr*) &opt_delete, (gptr*) &opt_delete, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"fields-terminated-by", OPT_FTB, @@ -99,12 +105,12 @@ static struct my_option my_long_options[] = {"lines-terminated-by", OPT_LTB, "Lines in the i.file are terminated by ...", (gptr*) &lines_terminated, (gptr*) &lines_terminated, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"local", 'L', "Read all files through the client", (gptr*) &opt_local_file, + {"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}, {"low-priority", OPT_LOW_PRIORITY, - "Use LOW_PRIORITY when updating the table", (gptr*) &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}, {"password", 'p', "Password to use when connecting to server. If password is not given it's asked from the tty.", @@ -116,8 +122,15 @@ static struct my_option my_long_options[] = {"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}, + {"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}, {"replace", 'r', "If duplicate unique key was found, replace old row.", (gptr*) &replace, (gptr*) &replace, 0, GET_BOOL, NO_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', "Be more silent.", (gptr*) &silent, (gptr*) &silent, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"socket", 'S', "Socket file to use for connection.", @@ -187,16 +200,26 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), while (*argument) *argument++= 'x'; /* Destroy argument */ if (*start) start[1]=0; /* Cut length of argument */ + tty_password= 0; } else tty_password= 1; break; #ifdef __WIN__ case 'W': - opt_mysql_unix_port=MYSQL_NAMEDPIPE; + opt_protocol = MYSQL_PROTOCOL_PIPE; opt_local_file=1; break; #endif + 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 : "d:t:o"); break; @@ -228,11 +251,10 @@ static int get_options(int *argc, char ***argv) fprintf(stderr, "You can't use --ignore (-i) and --replace (-r) at the same time.\n"); return(1); } - if (default_charset) - { - if (set_default_charset_by_name(default_charset, MYF(MY_WME))) - exit(1); - } + if (strcmp(default_charset, charset_info->csname) && + !(charset_info= get_charset_by_csname(default_charset, + MY_CS_PRIMARY, MYF(MY_WME)))) + exit(1); if (*argc < 2) { usage(); @@ -251,7 +273,6 @@ static int write_to_table(char *filename, MYSQL *sock) { char tablename[FN_REFLEN], hard_path[FN_REFLEN], sql_statement[FN_REFLEN*16+256], *end; - my_bool local_file; DBUG_ENTER("write_to_table"); DBUG_PRINT("enter",("filename: %s",filename)); @@ -363,6 +384,12 @@ static MYSQL *db_connect(char *host, char *database, char *user, char *passwd) mysql_ssl_set(&mysql_connection, opt_ssl_key, opt_ssl_cert, opt_ssl_ca, opt_ssl_capath, opt_ssl_cipher); #endif + if (opt_protocol) + mysql_options(&mysql_connection,MYSQL_OPT_PROTOCOL,(char*)&opt_protocol); +#ifdef HAVE_SMEM + if (shared_memory_base_name) + mysql_options(&mysql_connection,MYSQL_SHARED_MEMORY_BASE_NAME,shared_memory_base_name); +#endif if (!(sock= mysql_real_connect(&mysql_connection,host,user,passwd, database,opt_mysql_port,opt_mysql_unix_port, 0))) @@ -497,6 +524,9 @@ int main(int argc, char **argv) exitcode = error; db_disconnect(current_host, sock); my_free(opt_password,MYF(MY_ALLOW_ZERO_PTR)); +#ifdef HAVE_SMEM + my_free(shared_memory_base_name,MYF(MY_ALLOW_ZERO_PTR)); +#endif free_defaults(argv_to_free); my_end(0); return(exitcode); diff --git a/client/mysqlmanager-pwgen.c b/client/mysqlmanager-pwgen.c index 57d91b52f49..1d942e207ad 100644 --- a/client/mysqlmanager-pwgen.c +++ b/client/mysqlmanager-pwgen.c @@ -29,14 +29,14 @@ 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", + {"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, + {"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, + {"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, + {"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} }; diff --git a/client/mysqlshow.c b/client/mysqlshow.c index 35afc1f5780..44fc9448782 100644 --- a/client/mysqlshow.c +++ b/client/mysqlshow.c @@ -16,7 +16,7 @@ /* Show databases, tables or columns */ -#define SHOW_VERSION "9.4" +#define SHOW_VERSION "9.5" #include "client_priv.h" #include <my_sys.h> @@ -30,6 +30,12 @@ static my_string host=0,opt_password=0,user=0; static my_bool opt_show_keys=0,opt_compress=0,opt_status=0, tty_password=0; static uint opt_verbose=0; +static char *default_charset= (char*) MYSQL_DEFAULT_CHARSET_NAME; + +#ifdef HAVE_SMEM +static char *shared_memory_base_name=0; +#endif +static uint opt_protocol=0; static void get_options(int *argc,char ***argv); static uint opt_mysql_port=0; @@ -104,6 +110,14 @@ int main(int argc, char **argv) mysql_ssl_set(&mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca, opt_ssl_capath, opt_ssl_cipher); #endif + if (opt_protocol) + mysql_options(&mysql,MYSQL_OPT_PROTOCOL,(char*)&opt_protocol); +#ifdef HAVE_SMEM + if (shared_memory_base_name) + mysql_options(&mysql,MYSQL_SHARED_MEMORY_BASE_NAME,shared_memory_base_name); +#endif + mysql_options(&mysql, MYSQL_SET_CHARSET_NAME, default_charset); + if (!(mysql_real_connect(&mysql,host,user,opt_password, (first_argument_uses_wildcards) ? "" : argv[0],opt_mysql_port,opt_mysql_unix_port, 0))) @@ -131,6 +145,9 @@ int main(int argc, char **argv) mysql_close(&mysql); /* Close & free connection */ if (opt_password) my_free(opt_password,MYF(0)); +#ifdef HAVE_SMEM + my_free(shared_memory_base_name,MYF(MY_ALLOW_ZERO_PTR)); +#endif my_end(0); exit(error ? 1 : 0); return 0; /* No compiler warnings */ @@ -142,13 +159,16 @@ static struct my_option my_long_options[] = {"auto-close", 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 - {"character-sets-dir", 'c', "Directory where character sets are", + {"character-sets-dir", 'c', "Directory where character sets are.", (gptr*) &charsets_dir, (gptr*) &charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"compress", 'C', "Use compression in server/client protocol", + {"default-character-set", OPT_DEFAULT_CHARSET, + "Set the default character set.", (gptr*) &default_charset, + (gptr*) &default_charset, 0, GET_STR, REQUIRED_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}, - {"debug", '#', "Output debug log. Often this is 'd:t:o,filename'", + {"debug", '#', "Output debug log. Often this is 'd:t:o,filename'.", 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, @@ -157,10 +177,10 @@ static struct my_option my_long_options[] = {"status", 'i', "Shows a lot of extra information about each table.", (gptr*) &opt_status, (gptr*) &opt_status, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"keys", 'k', "Show keys for table", (gptr*) &opt_show_keys, + {"keys", 'k', "Show keys for table.", (gptr*) &opt_show_keys, (gptr*) &opt_show_keys, 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.", + "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}, {"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, @@ -169,6 +189,13 @@ static struct my_option my_long_options[] = {"pipe", 'W', "Use named pipes to connect to server.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, #endif + {"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}, +#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 {"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}, @@ -184,7 +211,7 @@ static struct my_option my_long_options[] = 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> @@ -239,15 +266,25 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), 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_mysql_unix_port=MYSQL_NAMEDPIPE; + 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 : "d:t:o"); break; @@ -543,7 +580,7 @@ list_fields(MYSQL *mysql,const char *db,const char *table, mysql_error(mysql)); return 1; } - end=strmov(strmov(query,"show /*!32332 FULL */ columns from "),table); + end=strmov(strmov(strmov(query,"show /*!32332 FULL */ columns from `"),table),"`"); if (wild && wild[0]) strxmov(end," like '",wild,"'",NullS); if (mysql_query(mysql,query) || !(result=mysql_store_result(mysql))) @@ -565,7 +602,7 @@ list_fields(MYSQL *mysql,const char *db,const char *table, print_res_top(result); if (opt_show_keys) { - end=strmov(strmov(query,"show keys from "),table); + end=strmov(strmov(strmov(query,"show keys from `"),table),"`"); if (mysql_query(mysql,query) || !(result=mysql_store_result(mysql))) { fprintf(stderr,"%s: Cannot list keys in db: %s, table: %s: %s\n", diff --git a/client/mysqltest.c b/client/mysqltest.c index 582ce7c37ad..29ccbc3e1b8 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -42,7 +42,7 @@ **********************************************************************/ -#define MTEST_VERSION "1.30" +#define MTEST_VERSION "2.4" #include <my_global.h> #include <mysql_embed.h> @@ -53,13 +53,21 @@ #include <mysqld_error.h> #include <m_ctype.h> #include <my_dir.h> +#include <errmsg.h> /* Error codes */ #include <hash.h> #include <my_getopt.h> #include <stdarg.h> #include <sys/stat.h> #include <violite.h> - +#include <regex.h> /* Our own version of lib */ +#ifdef HAVE_SYS_WAIT_H +#include <sys/wait.h> +#endif +#ifndef WEXITSTATUS +# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) +#endif #define MAX_QUERY 131072 +#define MAX_VAR_NAME 256 #define MAX_COLUMNS 256 #define PAD_SIZE 128 #define MAX_CONS 128 @@ -74,12 +82,7 @@ #ifndef MYSQL_MANAGER_PORT #define MYSQL_MANAGER_PORT 23546 #endif -#define MAX_SERVER_ARGS 20 - -/* Defines to make this look like MySQL 4.1 */ -#define charset_info default_charset_info -#define my_isvar(A,B) isvar(B) -#define my_hash_insert(A,B) hash_insert((A), (B)) +#define MAX_SERVER_ARGS 64 /* Sometimes in a test the client starts before @@ -91,19 +94,52 @@ #define MAX_CON_TRIES 5 #define SLAVE_POLL_INTERVAL 300000 /* 0.3 of a sec */ - +#define DEFAULT_DELIMITER ";" +#define MAX_DELIMITER 16 enum {OPT_MANAGER_USER=256,OPT_MANAGER_HOST,OPT_MANAGER_PASSWD, OPT_MANAGER_PORT,OPT_MANAGER_WAIT_TIMEOUT, OPT_SKIP_SAFEMALLOC, OPT_SSL_SSL, OPT_SSL_KEY, OPT_SSL_CERT, OPT_SSL_CA, OPT_SSL_CAPATH, - OPT_SSL_CIPHER}; + OPT_SSL_CIPHER,OPT_PS_PROTOCOL}; + +/* ************************************************************************ */ +/* + A line that starts with !$ or $S, and the list of error codes to + --error are stored in an internal array of structs. This struct can + hold numeric SQL error codes or SQLSTATE codes as strings. The + element next to the last active element in the list is set to type + ERR_EMPTY. When an SQL statement return an error we use this list to + check if this is an expected error. +*/ + +enum match_err_type +{ + ERR_EMPTY= 0, + ERR_ERRNO, + ERR_SQLSTATE +}; + +typedef struct +{ + enum match_err_type type; + union + { + uint errnum; + char sqlstate[SQLSTATE_LENGTH+1]; /* \0 terminated string */ + } code; +} match_err; + +static match_err global_expected_errno[MAX_EXPECTED_ERRORS]; +static uint global_expected_errors; + +/* ************************************************************************ */ static int record = 0, opt_sleep=0; static char *db = 0, *pass=0; const char* user = 0, *host = 0, *unix_sock = 0, *opt_basedir="./"; static int port = 0; -static my_bool opt_big_test= 0, opt_compress= 0, silent= 0, verbose = 0, - tty_password= 0; +static my_bool opt_big_test= 0, opt_compress= 0, silent= 0, verbose = 0; +static my_bool tty_password= 0, ps_protocol= 0, ps_protocol_enabled= 0; static uint start_lineno, *lineno; const char* manager_user="root",*manager_host=0; char *manager_pass=0; @@ -113,22 +149,37 @@ MYSQL_MANAGER* manager=0; static char **default_argv; static const char *load_default_groups[]= { "mysqltest","client",0 }; +static char line_buffer[MAX_DELIMITER], *line_buffer_pos= line_buffer; static FILE* file_stack[MAX_INCLUDE_DEPTH]; static FILE** cur_file; static FILE** file_stack_end; static uint lineno_stack[MAX_INCLUDE_DEPTH]; static char TMPDIR[FN_REFLEN]; +static char delimiter[MAX_DELIMITER]= DEFAULT_DELIMITER; +static uint delimiter_length= 1; -static int *cur_block, *block_stack_end; -static int block_stack[BLOCK_STACK_DEPTH]; - -static int block_ok_stack[BLOCK_STACK_DEPTH]; -static uint global_expected_errno[MAX_EXPECTED_ERRORS], global_expected_errors; +static CHARSET_INFO *charset_info= &my_charset_latin1; /* Default charset */ +static const char *charset_name= "latin1"; /* Default character set name */ static int embedded_server_arg_count=0; static char *embedded_server_args[MAX_SERVER_ARGS]; +static my_bool display_result_vertically= FALSE, display_metadata= FALSE; + +/* See the timer_output() definition for details */ +static char *timer_file = NULL; +static ulonglong timer_start; +static int got_end_timer= FALSE; +static void timer_output(void); +static ulonglong timer_now(void); + +static regex_t ps_re; /* Holds precompiled re for valid PS statements */ +static void ps_init_re(void); +static int ps_match_re(char *); +static char *ps_eprint(int); +static void ps_free_reg(void); + static const char *embedded_server_groups[] = { "server", "embedded", @@ -161,8 +212,6 @@ MYSQL_RES *last_result=0; PARSER parser; MASTER_POS master_pos; -int *block_ok; /* set to 0 if the current block should not be executed */ -int false_block_depth = 0; /* if set, all results are concated and compared against this file */ const char *result_file = 0; @@ -178,7 +227,7 @@ typedef struct int alloced; } VAR; -#ifdef __NETWARE__ +#if defined(__NETWARE__) || defined(__WIN__) /* Netware doesn't proved environment variable substitution that is done by the shell in unix environments. We do this in the following function: @@ -194,6 +243,7 @@ VAR var_reg[10]; HASH var_hash; my_bool disable_query_log=0, disable_result_log=0, disable_warnings=0; my_bool disable_info= 1; /* By default off */ +my_bool abort_on_error= 1; struct connection cons[MAX_CONS]; struct connection* cur_con, *next_con, *cons_end; @@ -221,10 +271,18 @@ Q_ENABLE_QUERY_LOG, Q_DISABLE_QUERY_LOG, Q_ENABLE_RESULT_LOG, Q_DISABLE_RESULT_LOG, Q_SERVER_START, Q_SERVER_STOP,Q_REQUIRE_MANAGER, Q_WAIT_FOR_SLAVE_TO_STOP, -Q_REQUIRE_VERSION, Q_ENABLE_WARNINGS, Q_DISABLE_WARNINGS, Q_ENABLE_INFO, Q_DISABLE_INFO, -Q_EXEC, +Q_ENABLE_METADATA, Q_DISABLE_METADATA, +Q_EXEC, Q_DELIMITER, +Q_DISABLE_ABORT_ON_ERROR, Q_ENABLE_ABORT_ON_ERROR, +Q_DISPLAY_VERTICAL_RESULTS, Q_DISPLAY_HORIZONTAL_RESULTS, +Q_QUERY_VERTICAL, Q_QUERY_HORIZONTAL, +Q_START_TIMER, Q_END_TIMER, +Q_CHARACTER_SET, Q_DISABLE_PS_PROTOCOL, Q_ENABLE_PS_PROTOCOL, +Q_DISABLE_RECONNECT, Q_ENABLE_RECONNECT, +Q_IF, + Q_UNKNOWN, /* Unknown command. */ Q_COMMENT, /* Comments, ignored. */ Q_COMMENT_WITH_COMMAND @@ -236,7 +294,7 @@ struct st_query char *query, *query_buf,*first_argument,*end; int first_word_len; my_bool abort_on_error, require_file; - uint expected_errno[MAX_EXPECTED_ERRORS]; + match_err expected_errno[MAX_EXPECTED_ERRORS]; uint expected_errors; char record_file[FN_REFLEN]; enum enum_commands type; @@ -290,23 +348,49 @@ const char *command_names[]= "server_stop", "require_manager", "wait_for_slave_to_stop", - "require_version", "enable_warnings", "disable_warnings", "enable_info", "disable_info", + "enable_metadata", + "disable_metadata", "exec", + "delimiter", + "disable_abort_on_error", + "enable_abort_on_error", + "vertical_results", + "horizontal_results", + "query_vertical", + "query_horizontal", + "start_timer", + "end_timer", + "character_set", + "disable_ps_protocol", + "enable_ps_protocol", + "disable_reconnect", + "enable_reconnect", + "if", 0 }; +/* Block stack */ +typedef struct +{ + int line; /* Start line of block */ + my_bool ok; /* Should block be executed */ + enum enum_commands cmd; /* Command owning the block */ +} BLOCK; +static BLOCK block_stack[BLOCK_STACK_DEPTH]; +static BLOCK *cur_block, *block_stack_end; + TYPELIB command_typelib= {array_elements(command_names),"", - command_names}; + command_names, 0}; DYNAMIC_STRING ds_res; static void die(const char *fmt, ...); static void init_var_hash(); -static byte* get_var_key(const byte* rec, uint* len, - my_bool __attribute__((unused)) t); +static VAR* var_from_env(const char *, const char *); +static byte* get_var_key(const byte* rec, uint* len, my_bool t); static VAR* var_init(VAR* v, const char *name, int name_len, const char *val, int val_len); @@ -316,6 +400,7 @@ int dyn_string_cmp(DYNAMIC_STRING* ds, const char *fname); void reject_dump(const char *record_file, char *buf, int size); int close_connection(struct st_query* q); +static void set_charset(struct st_query*); VAR* var_get(const char *var_name, const char** var_name_end, my_bool raw, my_bool ignore_not_existing); int eval_expr(VAR* v, const char *p, const char** p_end); @@ -357,11 +442,11 @@ static void get_replace_column(struct st_query *q); static void free_replace_column(); /* Disable functions that only exist in MySQL 4.0 */ -#if MYSQL_VERSION_ID < 40000 || defined(EMBEDDED_LIBRARY) +#if MYSQL_VERSION_ID < 40000 void mysql_enable_rpl_parse(MYSQL* mysql __attribute__((unused))) {} void mysql_disable_rpl_parse(MYSQL* mysql __attribute__((unused))) {} int mysql_rpl_parse_enabled(MYSQL* mysql __attribute__((unused))) { return 1; } -int mysql_rpl_probe(MYSQL *mysql __attribute__((unused))) { return 1; } +my_bool mysql_rpl_probe(MYSQL *mysql __attribute__((unused))) { return 1; } #endif static void replace_dynstr_append_mem(DYNAMIC_STRING *ds, const char *val, int len); @@ -372,6 +457,7 @@ static void do_eval(DYNAMIC_STRING* query_eval, const char* query) register char c; register int escaped = 0; VAR* v; + DBUG_ENTER("do_eval"); for (p= query; (c = *p); ++p) { @@ -403,6 +489,7 @@ static void do_eval(DYNAMIC_STRING* query_eval, const char* query) break; } } + DBUG_VOID_RETURN; } @@ -464,7 +551,8 @@ static void free_used_memory() my_free(pass,MYF(MY_ALLOW_ZERO_PTR)); free_defaults(default_argv); mysql_server_end(); - my_end(MY_CHECK_ERROR); + if (ps_protocol) + ps_free_reg(); DBUG_VOID_RETURN; } @@ -482,6 +570,7 @@ static void die(const char* fmt, ...) } va_end(args); free_used_memory(); + my_end(MY_CHECK_ERROR); exit(1); } @@ -494,7 +583,8 @@ static void abort_not_supported_test() if (!silent) printf("skipped\n"); free_used_memory(); - exit(2); + my_end(MY_CHECK_ERROR); + exit(62); } static void verbose_msg(const char* fmt, ...) @@ -614,6 +704,7 @@ static int check_result(DYNAMIC_STRING* ds, const char* fname, return error; } + VAR* var_get(const char* var_name, const char** var_name_end, my_bool raw, my_bool ignore_not_existing) { @@ -625,29 +716,30 @@ VAR* var_get(const char* var_name, const char** var_name_end, my_bool raw, if (*var_name != '$') goto err; digit = *++var_name - '0'; - if (!(digit < 10 && digit >= 0)) + if (digit < 0 || digit >= 10) { const char* save_var_name = var_name, *end; + uint length; end = (var_name_end) ? *var_name_end : 0; while (my_isvar(charset_info,*var_name) && var_name != end) - ++var_name; + var_name++; if (var_name == save_var_name) { if (ignore_not_existing) DBUG_RETURN(0); die("Empty variable"); } + length= (uint) (var_name - save_var_name); + if (length >= MAX_VAR_NAME) + die("Too long variable name: %s", save_var_name); - if (!(v = (VAR*) hash_search(&var_hash, save_var_name, - var_name - save_var_name))) + if (!(v = (VAR*) hash_search(&var_hash, save_var_name, length))) { - if (ignore_not_existing) - DBUG_RETURN(0); - if (end) - *(char*) end = 0; - die("Variable '%s' used uninitialized", save_var_name); + char buff[MAX_VAR_NAME+1]; + strmake(buff, save_var_name, length); + v= var_from_env(buff, ""); } - --var_name; /* Point at last character */ + var_name--; /* Point at last character */ } else v = var_reg + digit; @@ -668,7 +760,7 @@ err: DBUG_RETURN(0); } -static VAR* var_obtain(char* name, int len) +static VAR *var_obtain(const char* name, int len) { VAR* v; if ((v = (VAR*)hash_search(&var_hash, name, len))) @@ -678,28 +770,33 @@ static VAR* var_obtain(char* name, int len) return v; } -int var_set(char* var_name, char* var_name_end, char* var_val, - char* var_val_end) +int var_set(const char *var_name, const char *var_name_end, + const char *var_val, const char *var_val_end) { int digit; VAR* v; + DBUG_ENTER("var_set"); + DBUG_PRINT("enter", ("var_name: '%.*s' = '%.*s' (length: %d)", + (int) (var_name_end - var_name), var_name, + (int) (var_val_end - var_val), var_val, + (int) (var_val_end - var_val))); + if (*var_name++ != '$') - { - --var_name; - *var_name_end = 0; - die("Variable name in %s does not start with '$'", var_name); - } + { + var_name--; + die("Variable name in %s does not start with '$'", var_name); + } digit = *var_name - '0'; if (!(digit < 10 && digit >= 0)) - { - v = var_obtain(var_name, var_name_end - var_name); - } + { + v = var_obtain(var_name, (uint) (var_name_end - var_name)); + } else - v = var_reg + digit; - - return eval_expr(v, var_val, (const char**)&var_val_end); + v = var_reg + digit; + DBUG_RETURN(eval_expr(v, var_val, (const char**)&var_val_end)); } + int open_file(const char* name) { char buff[FN_REFLEN]; @@ -799,42 +896,6 @@ int do_server_op(struct st_query* q,const char* op) } #endif -int do_require_version(struct st_query* q) -{ - MYSQL* mysql = &cur_con->mysql; - MYSQL_RES* res; - MYSQL_ROW row; - char* p=q->first_argument, *ver_arg; - uint ver_arg_len,ver_len; - LINT_INIT(res); - - if (!*p) - die("Missing version argument in require_version\n"); - ver_arg = p; - while (*p && !my_isspace(charset_info,*p)) - p++; - *p = 0; - ver_arg_len = p - ver_arg; - - if (mysql_query(mysql, "select version()") || - !(res=mysql_store_result(mysql))) - die("Query failed while check server version: %s", - mysql_error(mysql)); - if (!(row=mysql_fetch_row(res)) || !row[0]) - { - mysql_free_result(res); - die("Strange result from query while checking version"); - } - ver_len = strlen(row[0]); - if (ver_len < ver_arg_len || memcmp(row[0],ver_arg,ver_arg_len)) - { - mysql_free_result(res); - abort_not_supported_test(); - } - mysql_free_result(res); - return 0; -} - int do_source(struct st_query* q) { char* p=q->first_argument, *name; @@ -864,10 +925,10 @@ int do_source(struct st_query* q) 1 error */ -int do_exec(struct st_query* q) +static void do_exec(struct st_query* q) { - int error= 0; - DYNAMIC_STRING *ds; + int error; + DYNAMIC_STRING *ds= NULL; /* Assign just to avoid warning */ DYNAMIC_STRING ds_tmp; char buf[1024]; FILE *res_file; @@ -887,7 +948,10 @@ int do_exec(struct st_query* q) if (disable_result_log) { while (fgets(buf, sizeof(buf), res_file)) - {} + { + buf[strlen(buf)-1]=0; + DBUG_PRINT("exec_result",("%s", buf)); + } } else { @@ -901,7 +965,42 @@ int do_exec(struct st_query* q) while (fgets(buf, sizeof(buf), res_file)) replace_dynstr_append_mem(ds, buf, strlen(buf)); + } + error= pclose(res_file); + if (error != 0) + { + uint status= WEXITSTATUS(error), i; + my_bool ok= 0; + + if (q->abort_on_error) + die("At line %u: command \"%s\" failed", start_lineno, cmd); + + DBUG_PRINT("info", + ("error: %d, status: %d", error, status)); + for (i=0 ; (uint) i < q->expected_errors ; i++) + { + DBUG_PRINT("info", ("expected error: %d", + q->expected_errno[i].code.errnum)); + if ((q->expected_errno[i].type == ERR_ERRNO) && + (q->expected_errno[i].code.errnum == status)) + ok= 1; + verbose_msg("At line %u: command \"%s\" failed with expected error: %d", + start_lineno, cmd, status); + } + if (!ok) + die("At line: %u: command \"%s\" failed with wrong error: %d", + start_lineno, cmd, status); + } + else if (q->expected_errno[0].type == ERR_ERRNO && + q->expected_errno[0].code.errnum != 0) + { + /* Error code we wanted was != 0, i.e. not an expected success */ + die("At line: %u: command \"%s\" succeeded - should have failed with errno %d...", + start_lineno, cmd, q->expected_errno[0].code.errnum); + } + if (!disable_result_log) + { if (glob_replace) free_replace(); @@ -919,9 +1018,6 @@ int do_exec(struct st_query* q) if (ds == &ds_tmp) dynstr_free(&ds_tmp); } - pclose(res_file); - - DBUG_RETURN(error); } @@ -1196,6 +1292,22 @@ int do_let(struct st_query* q) return var_set(var_name, var_name_end, var_val_start, q->end); } + +/* + Store an integer (typically the returncode of the last SQL) + statement in the mysqltest builtin variable $mysql_errno, by + simulating of a user statement "let $mysql_errno= <integer>" +*/ + +int var_set_errno(int sql_errno) +{ + const char *var_name= "$mysql_errno"; + char var_val[21]; + uint length= my_sprintf(var_val, (var_val, "%d", sql_errno)); + return var_set(var_name, var_name + 12, var_val, var_val + length); +} + + int do_rpl_probe(struct st_query* q __attribute__((unused))) { DBUG_ENTER("do_rpl_probe"); @@ -1204,12 +1316,14 @@ int do_rpl_probe(struct st_query* q __attribute__((unused))) DBUG_RETURN(0); } + int do_enable_rpl_parse(struct st_query* q __attribute__((unused))) { mysql_enable_rpl_parse(&cur_con->mysql); return 0; } + int do_disable_rpl_parse(struct st_query* q __attribute__((unused))) { mysql_disable_rpl_parse(&cur_con->mysql); @@ -1241,25 +1355,58 @@ static void get_file_name(char *filename, struct st_query* q) p[0]=0; } +static void set_charset(struct st_query* q) +{ + char* charset_name= q->first_argument; + char* tmp; -static uint get_ints(uint *to,struct st_query* q) + if (!charset_name || !*charset_name) + die("Missing charset name in 'character_set'\n"); + /* Remove end space */ + tmp= charset_name; + while (*tmp && !my_isspace(charset_info,*tmp)) + tmp++; + *tmp= 0; + + charset_info= get_charset_by_csname(charset_name,MY_CS_PRIMARY,MYF(MY_WME)); + if (!charset_info) + abort_not_supported_test(); +} + +static uint get_errcodes(match_err *to,struct st_query* q) { - char* p=q->first_argument; - long val; - uint count=0; - DBUG_ENTER("get_ints"); + char* p= q->first_argument; + uint count= 0; + DBUG_ENTER("get_errcodes"); if (!*p) die("Missing argument in %s\n", q->query); - for (; (p=str2int(p,10,(long) INT_MIN, (long) INT_MAX, &val)) ; p++) + do { + if (*p == 'S') + { + /* SQLSTATE string */ + int i; + p++; + for (i = 0; my_isalnum(charset_info, *p) && i < SQLSTATE_LENGTH; p++, i++) + to[count].code.sqlstate[i]= *p; + to[count].code.sqlstate[i]= '\0'; + to[count].type= ERR_SQLSTATE; + } + else + { + long val; + p=str2int(p,10,(long) INT_MIN, (long) INT_MAX, &val); + if (p == NULL) + die("Invalid argument in %s\n", q->query); + to[count].code.errnum= (uint) val; + to[count].type= ERR_ERRNO; + } count++; - *to++= (uint) val; - if (*p != ',') - break; - } - *to++=0; /* End of data */ + } while (*(p++) == ','); + + to[count].type= ERR_EMPTY; /* End of data */ DBUG_RETURN(count); } @@ -1515,7 +1662,8 @@ int safe_connect(MYSQL* con, const char* host, const char* user, int i; for (i = 0; i < MAX_CON_TRIES; ++i) { - if (mysql_real_connect(con, host,user, pass, db, port, sock, 0)) + if (mysql_real_connect(con, host,user, pass, db, port, sock, + CLIENT_MULTI_STATEMENTS)) { con_error = 0; break; @@ -1584,6 +1732,7 @@ int do_connect(struct st_query* q) if (opt_compress) mysql_options(&next_con->mysql,MYSQL_OPT_COMPRESS,NullS); mysql_options(&next_con->mysql, MYSQL_OPT_LOCAL_INFILE, 0); + mysql_options(&next_con->mysql, MYSQL_SET_CHARSET_NAME, charset_name); #ifdef HAVE_OPENSSL if (opt_use_ssl) @@ -1614,36 +1763,54 @@ int do_connect(struct st_query* q) int do_done(struct st_query* q) { + /* Dummy statement to eliminate compiler warning */ q->type = Q_END_BLOCK; + + /* Check if empty block stack */ if (cur_block == block_stack) die("Stray '}' - end of block before beginning"); - if (*block_ok--) + + /* Test if inner block has been executed */ + if (cur_block->ok && cur_block->cmd == Q_WHILE) { - parser.current_line = *--cur_block; + /* Pop block from stack, re-execute outer block */ + cur_block--; + parser.current_line = cur_block->line; } else { - ++parser.current_line; - --cur_block; + /* Pop block from stack, goto next line */ + cur_block--; + parser.current_line++; } return 0; } -int do_while(struct st_query* q) + +int do_block(enum enum_commands cmd, struct st_query* q) { char* p=q->first_argument; const char* expr_start, *expr_end; VAR v; + + /* Check stack overflow */ if (cur_block == block_stack_end) die("Nesting too deeply"); - if (!*block_ok) + + /* Set way to find outer block again, increase line counter */ + cur_block->line= parser.current_line++; + + /* If this block is ignored */ + if (!cur_block->ok) { - ++false_block_depth; - *++block_ok = 0; - *cur_block++ = parser.current_line++; + /* Inner block should be ignored too */ + cur_block++; + cur_block->cmd= cmd; + cur_block->ok= FALSE; return 0; } + /* Parse and evaluate test expression */ expr_start = strchr(p, '('); if (!expr_start) die("missing '(' in while"); @@ -1652,51 +1819,102 @@ int do_while(struct st_query* q) die("missing ')' in while"); var_init(&v,0,0,0,0); eval_expr(&v, ++expr_start, &expr_end); - *cur_block++ = parser.current_line++; - if (!v.int_val) - { - *++block_ok = 0; - false_block_depth++; - } - else - *++block_ok = 1; + + /* Define inner block */ + cur_block++; + cur_block->cmd= cmd; + cur_block->ok= (v.int_val ? TRUE : FALSE); + var_free(&v); return 0; } +/* + Read characters from line buffer or file. This is needed to allow + my_ungetc() to buffer MAX_DELIMITER characters for a file + + NOTE: + This works as long as one doesn't change files (with 'source file_name') + when there is things pushed into the buffer. This should however not + happen for any tests in the test suite. +*/ + +int my_getc(FILE *file) +{ + if (line_buffer_pos == line_buffer) + return fgetc(file); + return *--line_buffer_pos; +} + +void my_ungetc(int c) +{ + *line_buffer_pos++= (char) c; +} + + +my_bool end_of_query(int c) +{ + uint i; + char tmp[MAX_DELIMITER]; + + if (c != *delimiter) + return 0; + + for (i= 1; i < delimiter_length && + (c= my_getc(*cur_file)) == *(delimiter + i); + i++) + tmp[i]= c; + + if (i == delimiter_length) + return 1; /* Found delimiter */ + + /* didn't find delimiter, push back things that we read */ + my_ungetc(c); + while (i > 1) + my_ungetc(tmp[--i]); + return 0; +} + + int read_line(char* buf, int size) { int c; - char* p = buf, *buf_end = buf + size-1; - int no_save = 0; + char* p= buf, *buf_end= buf + size - 1; + int no_save= 0; enum {R_NORMAL, R_Q1, R_ESC_Q_Q1, R_ESC_Q_Q2, R_ESC_SLASH_Q1, R_ESC_SLASH_Q2, - R_Q2, R_COMMENT, R_LINE_START} state = R_LINE_START; + R_Q2, R_COMMENT, R_LINE_START} state= R_LINE_START; + DBUG_ENTER("read_line"); start_lineno= *lineno; for (; p < buf_end ;) { - no_save = 0; - c = fgetc(*cur_file); + no_save= 0; + c= my_getc(*cur_file); if (feof(*cur_file)) { + found_eof: if ((*cur_file) != stdin) - my_fclose(*cur_file,MYF(0)); + my_fclose(*cur_file, MYF(0)); cur_file--; lineno--; if (cur_file == file_stack) - return 1; + DBUG_RETURN(1); continue; } + /* Line counting is independent of state */ + if (c == '\n') + (*lineno)++; + switch(state) { case R_NORMAL: /* Only accept '{' in the beginning of a line */ - if (c == ';') + if (end_of_query(c)) { - *p = 0; - return 0; + *p= 0; + DBUG_RETURN(0); } else if (c == '\'') state = R_Q1; @@ -1705,15 +1923,13 @@ int read_line(char* buf, int size) else if (c == '\n') { state = R_LINE_START; - (*lineno)++; } break; case R_COMMENT: if (c == '\n') { - *p=0; - (*lineno)++; - return 0; + *p= 0; + DBUG_RETURN(0); } break; case R_LINE_START: @@ -1721,107 +1937,143 @@ int read_line(char* buf, int size) { state = R_COMMENT; } - else if (my_isspace(charset_info,c)) + else if (my_isspace(charset_info, c)) { if (c == '\n') - start_lineno= ++*lineno; /* Query hasn't started yet */ - no_save = 1; + start_lineno= *lineno; /* Query hasn't started yet */ + no_save= 1; } else if (c == '}') { - *buf++ = '}'; - *buf = 0; - return 0; + *buf++= '}'; + *buf= 0; + DBUG_RETURN(0); } - else if (c == ';' || c == '{') + else if (end_of_query(c) || c == '{') { - *p = 0; - return 0; + *p= 0; + DBUG_RETURN(0); } else if (c == '\'') - state = R_Q1; + state= R_Q1; else if (c == '"') - state = R_Q2; + state= R_Q2; else - state = R_NORMAL; + state= R_NORMAL; break; case R_Q1: if (c == '\'') - state = R_ESC_Q_Q1; + state= R_ESC_Q_Q1; else if (c == '\\') - state = R_ESC_SLASH_Q1; + state= R_ESC_SLASH_Q1; break; case R_ESC_Q_Q1: - if (c == ';') + if (end_of_query(c)) { - *p = 0; - return 0; + *p= 0; + DBUG_RETURN(0); } if (c != '\'') - state = R_NORMAL; + state= R_NORMAL; else - state = R_Q1; + state= R_Q1; break; case R_ESC_SLASH_Q1: - state = R_Q1; + state= R_Q1; break; case R_Q2: if (c == '"') - state = R_ESC_Q_Q2; + state= R_ESC_Q_Q2; else if (c == '\\') - state = R_ESC_SLASH_Q2; + state= R_ESC_SLASH_Q2; break; case R_ESC_Q_Q2: - if (c == ';') + if (end_of_query(c)) { - *p = 0; - return 0; + *p= 0; + DBUG_RETURN(0); } if (c != '"') - state = R_NORMAL; + state= R_NORMAL; else - state = R_Q2; + state= R_Q2; break; case R_ESC_SLASH_Q2: - state = R_Q2; + state= R_Q2; break; } if (!no_save) - *p++ = c; + { + /* Could be a multibyte character */ + /* This code is based on the code in "sql_load.cc" */ +#ifdef USE_MB + int charlen = my_mbcharlen(charset_info, c); + /* We give up if multibyte character is started but not */ + /* completed before we pass buf_end */ + if ((charlen > 1) && (p + charlen) <= buf_end) + { + int i; + char* mb_start = p; + + *p++ = c; + + for (i= 1; i < charlen; i++) + { + if (feof(*cur_file)) + goto found_eof; /* FIXME: could we just break here?! */ + c= my_getc(*cur_file); + *p++ = c; + } + if (! my_ismbchar(charset_info, mb_start, p)) + { + /* It was not a multiline char, push back the characters */ + /* We leave first 'c', i.e. pretend it was a normal char */ + while (p > mb_start) + my_ungetc(*--p); + } + } + else +#endif + *p++= c; + } } - *p=0; /* Always end with \0 */ - return feof(*cur_file); + *p= 0; /* Always end with \0 */ + DBUG_RETURN(feof(*cur_file)); } + static char read_query_buf[MAX_QUERY]; int read_query(struct st_query** q_ptr) { char *p = read_query_buf, * p1 ; - int expected_errno; struct st_query* q; - DBUG_ENTER("read_query_buf"); + DBUG_ENTER("read_query"); if (parser.current_line < parser.read_lines) { get_dynamic(&q_lines, (gptr) q_ptr, parser.current_line) ; DBUG_RETURN(0); } - if (!(*q_ptr=q=(struct st_query*) my_malloc(sizeof(*q), MYF(MY_WME))) || + if (!(*q_ptr= q= (struct st_query*) my_malloc(sizeof(*q), MYF(MY_WME))) || insert_dynamic(&q_lines, (gptr) &q)) die(NullS); - q->record_file[0] = 0; - q->require_file=0; - q->first_word_len = 0; + q->record_file[0]= 0; + q->require_file= 0; + q->first_word_len= 0; + q->type = Q_UNKNOWN; - q->query_buf=q->query=0; + q->query_buf= q->query= 0; if (read_line(read_query_buf, sizeof(read_query_buf))) + { + DBUG_PRINT("warning",("too long query")); DBUG_RETURN(1); - + } + DBUG_PRINT("info", ("query: %s", read_query_buf)); if (*p == '#') { q->type = Q_COMMENT; @@ -1830,40 +2082,52 @@ int read_query(struct st_query** q_ptr) } memcpy((gptr) q->expected_errno, (gptr) global_expected_errno, sizeof(global_expected_errno)); - q->expected_errors=global_expected_errors; - q->abort_on_error = global_expected_errno[0] == 0; - bzero((gptr) global_expected_errno,sizeof(global_expected_errno)); + q->expected_errors= global_expected_errors; + q->abort_on_error= (global_expected_errors == 0 && abort_on_error); + bzero((gptr) global_expected_errno, sizeof(global_expected_errno)); global_expected_errors=0; if (p[0] == '-' && p[1] == '-') { - q->type = Q_COMMENT_WITH_COMMAND; - p+=2; /* To calculate first word */ + q->type= Q_COMMENT_WITH_COMMAND; + p+= 2; /* To calculate first word */ } else { if (*p == '!') { - q->abort_on_error = 0; + q->abort_on_error= 0; p++; if (*p == '$') { - expected_errno = 0; - p++; - for (;my_isdigit(charset_info,*p);p++) - expected_errno = expected_errno * 10 + *p - '0'; - q->expected_errno[0] = expected_errno; - q->expected_errno[1] = 0; - q->expected_errors=1; + int expected_errno= 0; + p++; + for (; my_isdigit(charset_info, *p); p++) + expected_errno = expected_errno * 10 + *p - '0'; + q->expected_errno[0].code.errnum = expected_errno; + q->expected_errno[0].type= ERR_ERRNO; + q->expected_errno[1].type= ERR_EMPTY; + q->expected_errors=1; + } + else if (*p == 'S') /* SQLSTATE */ + { + int i; + p++; + for (i = 0; my_isalnum(charset_info, *p) && i < SQLSTATE_LENGTH; p++, i++) + q->expected_errno[0].code.sqlstate[i]= *p; + q->expected_errno[0].code.sqlstate[i]= '\0'; + q->expected_errno[0].type= ERR_SQLSTATE; + q->expected_errno[1].type= ERR_EMPTY; + q->expected_errors=1; } } - while (*p && my_isspace(charset_info,*p)) + while (*p && my_isspace(charset_info, *p)) p++ ; if (*p == '@') { p++; p1 = q->record_file; - while (!my_isspace(charset_info,*p) && + while (!my_isspace(charset_info, *p) && p1 < q->record_file + sizeof(q->record_file) - 1) *p1++ = *p++; *p1 = 0; @@ -1871,18 +2135,18 @@ int read_query(struct st_query** q_ptr) } end: - while (*p && my_isspace(charset_info,*p)) + while (*p && my_isspace(charset_info, *p)) p++; - if (!(q->query_buf=q->query=my_strdup(p,MYF(MY_WME)))) + if (!(q->query_buf= q->query= my_strdup(p, MYF(MY_WME)))) die(NullS); /* Calculate first word and first argument */ - for (p=q->query; *p && !my_isspace(charset_info,*p) ; p++) ; - q->first_word_len = (uint) (p - q->query); - while (*p && my_isspace(charset_info,*p)) + for (p= q->query; *p && !my_isspace(charset_info, *p) ; p++) ; + q->first_word_len= (uint) (p - q->query); + while (*p && my_isspace(charset_info, *p)) p++; - q->first_argument=p; - q->end = strend(q->query); + q->first_argument= p; + q->end= strend(q->query); parser.read_lines++; DBUG_RETURN(0); } @@ -1923,6 +2187,9 @@ static struct my_option my_long_options[] = 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_INT, REQUIRED_ARG, MYSQL_PORT, 0, 0, 0, 0, 0}, + {"ps-protocol", OPT_PS_PROTOCOL, "Use prepared statements protocol for communication", + (gptr*) &ps_protocol, (gptr*) &ps_protocol, 0, + GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"quiet", 's', "Suppress all normal output.", (gptr*) &silent, (gptr*) &silent, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"record", 'r', "Record output of test_file into result file.", @@ -1948,6 +2215,8 @@ static struct my_option my_long_options[] = #include "sslopt-longopts.h" {"test-file", 'x', "Read test from/in this file (default stdin).", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"timer-file", 'm', "File where the timing in micro seconds is stored.", + 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"tmpdir", 't', "Temporary directory where sockets are put.", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"user", 'u', "User for login.", (gptr*) &user, (gptr*) &user, 0, GET_STR, @@ -2014,12 +2283,26 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), die("Could not open %s: errno = %d", argument, errno); break; } + case 'm': + { + static char buff[FN_REFLEN]; + if (!test_if_hard_path(argument)) + { + strxmov(buff, opt_basedir, argument, NullS); + argument= buff; + } + fn_format(buff, argument, "", "", 4); + timer_file= buff; + unlink(timer_file); /* Ignore error, may not exist */ + break; + } 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 */ + tty_password= 0; } else tty_password= 1; @@ -2034,10 +2317,9 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), embedded_server_arg_count=1; embedded_server_args[0]= (char*) ""; } - embedded_server_args[embedded_server_arg_count++]= - my_strdup(argument, MYF(MY_FAE)); - if (embedded_server_arg_count == MAX_SERVER_ARGS || - !embedded_server_args[embedded_server_arg_count-1]) + if (embedded_server_arg_count == MAX_SERVER_ARGS-1 || + !(embedded_server_args[embedded_server_arg_count++]= + my_strdup(argument, MYF(MY_FAE)))) { die("Can't use server argument"); } @@ -2143,6 +2425,7 @@ static void append_result(DYNAMIC_STRING *ds, MYSQL_RES *res) { MYSQL_ROW row; uint num_fields= mysql_num_fields(res); + MYSQL_FIELD *fields= !display_result_vertically ? 0 : mysql_fetch_fields(res); unsigned long *lengths; while ((row = mysql_fetch_row(res))) { @@ -2163,11 +2446,22 @@ static void append_result(DYNAMIC_STRING *ds, MYSQL_RES *res) val= "NULL"; len= 4; } - if (i) + if (!display_result_vertically) + { + if (i) + dynstr_append_mem(ds, "\t", 1); + replace_dynstr_append_mem(ds, val, len); + } + else + { + dynstr_append(ds, fields[i].name); dynstr_append_mem(ds, "\t", 1); - replace_dynstr_append_mem(ds, val, len); + replace_dynstr_append_mem(ds, val, len); + dynstr_append_mem(ds, "\n", 1); + } } - dynstr_append_mem(ds, "\n", 1); + if (!display_result_vertically) + dynstr_append_mem(ds, "\n", 1); } free_replace_column(); } @@ -2179,16 +2473,47 @@ static void append_result(DYNAMIC_STRING *ds, MYSQL_RES *res) * the result will be read - for regular query, both bits must be on */ -int run_query(MYSQL* mysql, struct st_query* q, int flags) +static int run_query_normal(MYSQL *mysql, struct st_query *q, int flags); +static int run_query_stmt (MYSQL *mysql, struct st_query *q, int flags); +static void run_query_stmt_handle_warnings(MYSQL *mysql, DYNAMIC_STRING *ds); +static int run_query_stmt_handle_error(char *query, struct st_query *q, + MYSQL_STMT *stmt, DYNAMIC_STRING *ds); +static void run_query_display_metadata(MYSQL_FIELD *field, uint num_fields, + DYNAMIC_STRING *ds); + +static int run_query(MYSQL *mysql, struct st_query *q, int flags) +{ + + /* + Try to find out if we can run this statement using the prepared + statement protocol. + + We don't have a mysql_stmt_send_execute() so we only handle + complete SEND+REAP. + + If it is a '?' in the query it may be a SQL level prepared + statement already and we can't do it twice + */ + + if (ps_protocol_enabled && disable_info && + (flags & QUERY_SEND) && (flags & QUERY_REAP) && ps_match_re(q->query)) + return run_query_stmt(mysql, q, flags); + return run_query_normal(mysql, q, flags); +} + + +static int run_query_normal(MYSQL* mysql, struct st_query* q, int flags) { - MYSQL_RES* res = 0; - int i, error = 0; + MYSQL_RES* res= 0; + uint i; + int error= 0, err= 0, counter= 0; DYNAMIC_STRING *ds; DYNAMIC_STRING ds_tmp; DYNAMIC_STRING eval_query; char* query; - int query_len; - DBUG_ENTER("run_query"); + int query_len, got_error_on_send= 0; + DBUG_ENTER("run_query_normal"); + DBUG_PRINT("enter",("flags: %d", flags)); if (q->type != Q_EVAL) { @@ -2212,109 +2537,543 @@ int run_query(MYSQL* mysql, struct st_query* q, int flags) else ds= &ds_res; - if ((flags & QUERY_SEND) && mysql_send_query(mysql, query, query_len)) - die("At line %u: unable to send query '%s'(mysql_errno=%d,errno=%d)", - start_lineno, query, - mysql_errno(mysql), errno); - if ((flags & QUERY_SEND) && !disable_query_log) + if (flags & QUERY_SEND) { - replace_dynstr_append_mem(ds,query, query_len); - dynstr_append_mem(ds,";\n",2); + got_error_on_send= mysql_send_query(mysql, query, query_len); + if (got_error_on_send && q->expected_errno[0].type == ERR_EMPTY) + die("At line %u: unable to send query '%s' (mysql_errno=%d , errno=%d)", + start_lineno, query, mysql_errno(mysql), errno); } - if (!(flags & QUERY_REAP)) - DBUG_RETURN(0); - if (mysql_read_query_result(mysql) || - (!(last_result = res = mysql_store_result(mysql)) && - mysql_field_count(mysql))) + do { - if (q->require_file) + if ((flags & QUERY_SEND) && !disable_query_log && !counter) { - abort_not_supported_test(); + replace_dynstr_append_mem(ds,query, query_len); + dynstr_append_mem(ds, delimiter, delimiter_length); + dynstr_append_mem(ds, "\n", 1); } - if (q->abort_on_error) - die("At line %u: query '%s' failed: %d: %s", start_lineno, query, - mysql_errno(mysql), mysql_error(mysql)); - else + if (!(flags & QUERY_REAP)) + DBUG_RETURN(0); + + if (got_error_on_send || + (!counter && (*mysql->methods->read_query_result)(mysql)) || + (!(last_result= res= mysql_store_result(mysql)) && + mysql_field_count(mysql))) { - for (i=0 ; (uint) i < q->expected_errors ; i++) + if (q->require_file) { - if ((q->expected_errno[i] == mysql_errno(mysql))) + abort_not_supported_test(); + } + if (q->abort_on_error) + die("At line %u: query '%s' failed: %d: %s", start_lineno, query, + mysql_errno(mysql), mysql_error(mysql)); + else + { + for (i=0 ; (uint) i < q->expected_errors ; i++) { - if (i == 0 && q->expected_errors == 1) + if (((q->expected_errno[i].type == ERR_ERRNO) && + (q->expected_errno[i].code.errnum == mysql_errno(mysql))) || + ((q->expected_errno[i].type == ERR_SQLSTATE) && + (strcmp(q->expected_errno[i].code.sqlstate,mysql_sqlstate(mysql)) == 0))) { - /* Only log error if there is one possible error */ - replace_dynstr_append_mem(ds,mysql_error(mysql), - strlen(mysql_error(mysql))); - dynstr_append_mem(ds,"\n",1); + if (i == 0 && q->expected_errors == 1) + { + /* Only log error if there is one possible error */ + dynstr_append_mem(ds,"ERROR ",6); + replace_dynstr_append_mem(ds, mysql_sqlstate(mysql), + strlen(mysql_sqlstate(mysql))); + dynstr_append_mem(ds, ": ", 2); + replace_dynstr_append_mem(ds,mysql_error(mysql), + strlen(mysql_error(mysql))); + dynstr_append_mem(ds,"\n",1); + } + /* Don't log error if we may not get an error */ + else if (q->expected_errno[0].type == ERR_SQLSTATE || + (q->expected_errno[0].type == ERR_ERRNO && + q->expected_errno[0].code.errnum != 0)) + dynstr_append(ds,"Got one of the listed errors\n"); + goto end; /* Ok */ } - /* Don't log error if we may not get an error */ - else if (q->expected_errno[0] != 0) - dynstr_append(ds,"Got one of the listed errors\n"); - goto end; /* Ok */ } + DBUG_PRINT("info",("i: %d expected_errors: %d", i, + q->expected_errors)); + dynstr_append_mem(ds, "ERROR ",6); + replace_dynstr_append_mem(ds, mysql_sqlstate(mysql), + strlen(mysql_sqlstate(mysql))); + dynstr_append_mem(ds,": ",2); + replace_dynstr_append_mem(ds, mysql_error(mysql), + strlen(mysql_error(mysql))); + dynstr_append_mem(ds,"\n",1); + if (i) + { + if (q->expected_errno[0].type == ERR_ERRNO) + verbose_msg("query '%s' failed with wrong errno %d instead of %d...", + q->query, mysql_errno(mysql), q->expected_errno[0].code.errnum); + else + verbose_msg("query '%s' failed with wrong sqlstate %s instead of %s...", + q->query, mysql_sqlstate(mysql), q->expected_errno[0].code.sqlstate); + error= 1; + goto end; + } + verbose_msg("query '%s' failed: %d: %s", q->query, mysql_errno(mysql), + mysql_error(mysql)); + /* + if we do not abort on error, failure to run the query does + not fail the whole test case + */ + goto end; } - DBUG_PRINT("info",("i: %d expected_errors: %d", i, q->expected_errors)); - replace_dynstr_append_mem(ds, mysql_error(mysql), - strlen(mysql_error(mysql))); - dynstr_append_mem(ds,"\n",1); - if (i) - { - verbose_msg("query '%s' failed with wrong errno %d instead of %d...", - q->query, mysql_errno(mysql), q->expected_errno[0]); - error= 1; + /*{ + verbose_msg("failed in mysql_store_result for query '%s' (%d)", query, + mysql_errno(mysql)); + error = 1; goto end; + }*/ + } + + if (q->expected_errno[0].type == ERR_ERRNO && + q->expected_errno[0].code.errnum != 0) + { + /* Error code we wanted was != 0, i.e. not an expected success */ + verbose_msg("query '%s' succeeded - should have failed with errno %d...", + q->query, q->expected_errno[0].code.errnum); + error = 1; + goto end; + } + else if (q->expected_errno[0].type == ERR_SQLSTATE && + strcmp(q->expected_errno[0].code.sqlstate,"00000") != 0) + { + /* SQLSTATE we wanted was != "00000", i.e. not an expected success */ + verbose_msg("query '%s' succeeded - should have failed with sqlstate %s...", + q->query, q->expected_errno[0].code.sqlstate); + error = 1; + goto end; + } + + if (!disable_result_log) + { + ulong affected_rows; /* Ok to be undef if 'disable_info' is set */ + LINT_INIT(affected_rows); + + if (res) + { + MYSQL_FIELD *field= mysql_fetch_fields(res); + uint num_fields= mysql_num_fields(res); + + if (display_metadata) + run_query_display_metadata(field, num_fields, ds); + + if (!display_result_vertically) + { + for (i = 0; i < num_fields; i++) + { + if (i) + dynstr_append_mem(ds, "\t", 1); + replace_dynstr_append_mem(ds, field[i].name, + strlen(field[i].name)); + } + dynstr_append_mem(ds, "\n", 1); + } + append_result(ds, res); } - verbose_msg("query '%s' failed: %d: %s", q->query, mysql_errno(mysql), - mysql_error(mysql)); + /* - if we do not abort on error, failure to run the query does - not fail the whole test case + Need to call mysql_affected_rows() before the new + query to find the warnings */ + if (!disable_info) + affected_rows= (ulong)mysql_affected_rows(mysql); + + /* Add all warnings to the result */ + if (!disable_warnings && mysql_warning_count(mysql)) + { + MYSQL_RES *warn_res=0; + uint count= mysql_warning_count(mysql); + if (!mysql_real_query(mysql, "SHOW WARNINGS", 13)) + { + warn_res= mysql_store_result(mysql); + } + if (!warn_res) + verbose_msg("Warning count is %u but didn't get any warnings\n", + count); + else + { + dynstr_append_mem(ds, "Warnings:\n", 10); + append_result(ds, warn_res); + mysql_free_result(warn_res); + } + } + if (!disable_info) + { + char buf[40]; + sprintf(buf,"affected rows: %lu\n", affected_rows); + dynstr_append(ds, buf); + if (mysql_info(mysql)) + { + dynstr_append(ds, "info: "); + dynstr_append(ds, mysql_info(mysql)); + dynstr_append_mem(ds, "\n", 1); + } + } + } + + if (record) + { + if (!q->record_file[0] && !result_file) + die("At line %u: Missing result file", start_lineno); + if (!result_file) + str_to_file(q->record_file, ds->str, ds->length); + } + else if (q->record_file[0]) + { + error = check_result(ds, q->record_file, q->require_file); + } + if (res) + mysql_free_result(res); + last_result= 0; + counter++; + } while (!(err= mysql_next_result(mysql))); + if (err >= 1) + mysql_error(mysql); + +end: + free_replace(); + last_result=0; + if (ds == &ds_tmp) + dynstr_free(&ds_tmp); + if (q->type == Q_EVAL) + dynstr_free(&eval_query); + + /* + We save the return code (mysql_errno(mysql)) from the last call sent + 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_errno(mysql)); + DBUG_RETURN(error); +} + + +/****************************************************************************\ + * If --ps-protocol run ordinary statements using prepared statemnt C API +\****************************************************************************/ + +/* + We don't have a mysql_stmt_send_execute() so we only handle + complete SEND+REAP +*/ + +static int run_query_stmt(MYSQL *mysql, struct st_query *q, int flags) +{ + int error= 0; /* Function return code if "goto end;" */ + int err; /* Temporary storage of return code from calls */ + int query_len, got_error_on_execute; + uint num_rows; + char *query; + MYSQL_RES *res= NULL; /* Note that here 'res' is meta data result set */ + DYNAMIC_STRING *ds; + DYNAMIC_STRING ds_tmp; + DYNAMIC_STRING eval_query; + MYSQL_STMT *stmt; + DBUG_ENTER("run_query_stmt"); + + /* + We must allocate a new stmt for each query in this program becasue this + may be a new connection. + */ + if (!(stmt= mysql_stmt_init(mysql))) + die("At line %u: unable init stmt structure"); + + if (q->type != Q_EVAL) + { + query= q->query; + query_len= strlen(query); + } + else + { + init_dynamic_string(&eval_query, "", 16384, 65536); + do_eval(&eval_query, q->query); + query= eval_query.str; + query_len= eval_query.length; + } + DBUG_PRINT("query", ("'%-.60s'", query)); + + if (q->record_file[0]) + { + init_dynamic_string(&ds_tmp, "", 16384, 65536); + ds= &ds_tmp; + } + else + ds= &ds_res; + + /* Store the query into the output buffer if not disabled */ + if (!disable_query_log) + { + replace_dynstr_append_mem(ds,query, query_len); + dynstr_append_mem(ds, delimiter, delimiter_length); + dynstr_append_mem(ds, "\n", 1); + } + + /* + We use the prepared statement interface but there is actually no + '?' in the query. If unpreparable we fall back to use normal + C API. + */ + if ((err= mysql_stmt_prepare(stmt, query, query_len)) == CR_NO_PREPARE_STMT) + return run_query_normal(mysql, q, flags); + + if (err != 0) + { + if (q->abort_on_error) + { + die("At line %u: unable to prepare statement '%s': " + "%s (mysql_stmt_errno=%d returned=%d)", + start_lineno, query, + mysql_stmt_error(stmt), mysql_stmt_errno(stmt), err); + } + else + { + /* + Preparing is part of normal execution and some errors may be expected + */ + error= run_query_stmt_handle_error(query, q, stmt, ds); goto end; } - /*{ - verbose_msg("failed in mysql_store_result for query '%s' (%d)", query, - mysql_errno(mysql)); - error = 1; + } + + /* We may have got warnings already, collect them if any */ + /* FIXME we only want this if the statement succeeds I think */ + run_query_stmt_handle_warnings(mysql, ds); + + /* + No need to call mysql_stmt_bind_param() because we have no + parameter markers. + + To optimize performance we use a global 'stmt' that is initiated + once. A new prepare will implicitely close the old one. When we + terminate we will lose the connection, this also closes the last + prepared statement. + */ + + if ((got_error_on_execute= mysql_stmt_execute(stmt)) != 0) /* 0 == Success */ + { + if (q->abort_on_error) + { + /* We got an error, unexpected */ + die("At line %u: unable to execute statement '%s': " + "%s (mysql_stmt_errno=%d returned=%d)", + start_lineno, query, mysql_stmt_error(stmt), + mysql_stmt_errno(stmt), got_error_on_execute); + } + else + { + /* We got an error, maybe expected */ + error= run_query_stmt_handle_error(query, q, stmt, ds); goto end; - }*/ + } } - if (q->expected_errno[0]) + /* + We instruct that we want to update the "max_length" field in + mysql_stmt_store_result(), this is our only way to know how much + buffer to allocate for result data + */ + { + my_bool one= 1; + if (mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, + (void*) &one) != 0) + die("At line %u: unable to set stmt attribute " + "'STMT_ATTR_UPDATE_MAX_LENGTH': %s (returned=%d)", + start_lineno, query, err); + } + + /* + If we got here the statement succeeded and was expected to do so, + get data. Note that this can still give errors found during execution! + */ + if ((err= mysql_stmt_store_result(stmt)) != 0) + { + if (q->abort_on_error) + { + /* We got an error, unexpected */ + die("At line %u: unable to execute statement '%s': " + "%s (mysql_stmt_errno=%d returned=%d)", + start_lineno, query, mysql_stmt_error(stmt), + mysql_stmt_errno(stmt), got_error_on_execute); + } + else + { + /* We got an error, maybe expected */ + error= run_query_stmt_handle_error(query, q, stmt, ds); + goto end; + } + } + + /* If we got here the statement was both executed and read succeesfully */ + + if (q->expected_errno[0].type == ERR_ERRNO && + q->expected_errno[0].code.errnum != 0) { - error = 1; verbose_msg("query '%s' succeeded - should have failed with errno %d...", - q->query, q->expected_errno[0]); + q->query, q->expected_errno[0].code.errnum); + error= 1; goto end; } - if (!disable_result_log) + num_rows= mysql_stmt_num_rows(stmt); + + /* + Not all statements creates a result set. If there is one we can + now create another normal result set that contains the meta + data. This set can be handled almost like any other non prepared + statement result set. + */ + if (!disable_result_log && ((res= mysql_stmt_result_metadata(stmt)) != NULL)) { - if (res) + /* Take the column count from meta info */ + MYSQL_FIELD *field= mysql_fetch_fields(res); + uint num_fields= mysql_num_fields(res); + + /* FIXME check error from the above? */ + + if (display_metadata) + run_query_display_metadata(field, num_fields, ds); + + if (!display_result_vertically) { - int num_fields= mysql_num_fields(res); - MYSQL_FIELD *fields= mysql_fetch_fields(res); - for (i = 0; i < num_fields; i++) + /* Display the table heading with the names tab separated */ + uint col_idx; + for (col_idx= 0; col_idx < num_fields; col_idx++) { - if (i) - dynstr_append_mem(ds, "\t", 1); - replace_dynstr_append_mem(ds, fields[i].name, strlen(fields[i].name)); + if (col_idx) + dynstr_append_mem(ds, "\t", 1); + replace_dynstr_append_mem(ds, field[col_idx].name, + strlen(field[col_idx].name)); } dynstr_append_mem(ds, "\n", 1); - append_result(ds, res); } - if (!disable_info && mysql_info(mysql)) + /* Now we are to put the real result into the output buffer */ + /* FIXME when it works, create function append_stmt_result() */ { - dynstr_append(ds, "info: "); - dynstr_append(ds, mysql_info(mysql)); - dynstr_append_mem(ds, "\n", 1); + MYSQL_BIND *bind; + my_bool *is_null; + unsigned long *length; + /* FIXME we don't handle vertical display ..... */ + uint col_idx, row_idx; + + /* Allocate array with bind structs, lengths and NULL flags */ + bind= (MYSQL_BIND*) my_malloc(num_fields * sizeof(MYSQL_BIND), + MYF(MY_WME | MY_FAE)); + length= (unsigned long*) my_malloc(num_fields * sizeof(unsigned long), + MYF(MY_WME | MY_FAE)); + is_null= (my_bool*) my_malloc(num_fields * sizeof(my_bool), + MYF(MY_WME | MY_FAE)); + + for (col_idx= 0; col_idx < num_fields; col_idx++) + { + /* Allocate data for output */ + /* + FIXME it may be a bug that for non string/blob types + 'max_length' is 0, should try out 'length' in that case + */ + uint max_length= max(field[col_idx].max_length + 1, 1024); + char *str_data= (char *) my_malloc(max_length, MYF(MY_WME | MY_FAE)); + + bind[col_idx].buffer_type= MYSQL_TYPE_STRING; + bind[col_idx].buffer= (char *)str_data; + bind[col_idx].buffer_length= max_length; + bind[col_idx].is_null= &is_null[col_idx]; + bind[col_idx].length= &length[col_idx]; + } + + /* Fill in the data into the structures created above */ + if ((err= mysql_stmt_bind_result(stmt, bind)) != 0) + die("At line %u: unable to bind result to statement '%s': " + "%s (mysql_stmt_errno=%d returned=%d)", + start_lineno, query, + mysql_stmt_error(stmt), mysql_stmt_errno(stmt), err); + + /* Read result from each row */ + for (row_idx= 0; row_idx < num_rows; row_idx++) + { + if ((err= mysql_stmt_fetch(stmt)) != 0) + die("At line %u: unable to fetch all rows from statement '%s': " + "%s (mysql_stmt_errno=%d returned=%d)", + start_lineno, query, + mysql_stmt_error(stmt), mysql_stmt_errno(stmt), err); + + /* Read result from each column */ + for (col_idx= 0; col_idx < num_fields; col_idx++) + { + /* FIXME is string terminated? */ + const char *val= (const char *)bind[col_idx].buffer; + ulonglong len= *bind[col_idx].length; + if (col_idx < max_replace_column && replace_column[col_idx]) + { + val= replace_column[col_idx]; + len= strlen(val); + } + if (*bind[col_idx].is_null) + { + val= "NULL"; + len= 4; + } + if (!display_result_vertically) + { + if (col_idx) /* No tab before first col */ + dynstr_append_mem(ds, "\t", 1); + replace_dynstr_append_mem(ds, val, len); + } + else + { + dynstr_append(ds, field[col_idx].name); + dynstr_append_mem(ds, "\t", 1); + replace_dynstr_append_mem(ds, val, len); + dynstr_append_mem(ds, "\n", 1); + } + } + if (!display_result_vertically) + dynstr_append_mem(ds, "\n", 1); + } + + if ((err= mysql_stmt_fetch(stmt)) != MYSQL_NO_DATA) + die("At line %u: fetch didn't end with MYSQL_NO_DATA from statement " + "'%s': %s (mysql_stmt_errno=%d returned=%d)", + start_lineno, query, + mysql_stmt_error(stmt), mysql_stmt_errno(stmt), err); + + free_replace_column(); + + for (col_idx= 0; col_idx < num_fields; col_idx++) + { + /* Free data for output */ + my_free((gptr)bind[col_idx].buffer, MYF(MY_WME | MY_FAE)); + } + /* Free array with bind structs, lengths and NULL flags */ + my_free((gptr)bind , MYF(MY_WME | MY_FAE)); + my_free((gptr)length , MYF(MY_WME | MY_FAE)); + my_free((gptr)is_null , MYF(MY_WME | MY_FAE)); } - } - if (glob_replace) - free_replace(); + /* Add all warnings to the result */ + run_query_stmt_handle_warnings(mysql, ds); + + if (!disable_info) + { + char buf[40]; + sprintf(buf,"affected rows: %lu\n",(ulong) mysql_affected_rows(mysql)); + dynstr_append(ds, buf); + if (mysql_info(mysql)) + { + dynstr_append(ds, "info: "); + dynstr_append(ds, mysql_info(mysql)); + dynstr_append_mem(ds, "\n", 1); + } + } + } + run_query_stmt_handle_warnings(mysql, ds); if (record) { @@ -2325,21 +3084,248 @@ int run_query(MYSQL* mysql, struct st_query* q, int flags) } else if (q->record_file[0]) { - error = check_result(ds, q->record_file, q->require_file); + error= check_result(ds, q->record_file, q->require_file); } + if (res) + mysql_free_result(res); /* Free normal result set with meta data */ + last_result= 0; /* FIXME have no idea what this is about... */ + + if (err >= 1) + mysql_error(mysql); /* FIXME strange, has no effect... */ end: - if (res) - mysql_free_result(res); + free_replace(); last_result=0; if (ds == &ds_tmp) dynstr_free(&ds_tmp); if (q->type == Q_EVAL) dynstr_free(&eval_query); + var_set_errno(mysql_stmt_errno(stmt)); + mysql_stmt_close(stmt); DBUG_RETURN(error); } +/****************************************************************************\ + * Broken out sub functions to run_query_stmt() +\****************************************************************************/ + +static void run_query_display_metadata(MYSQL_FIELD *field, uint num_fields, + DYNAMIC_STRING *ds) +{ + MYSQL_FIELD *field_end; + dynstr_append(ds,"Catalog\tDatabase\tTable\tTable_alias\tColumn\t" + "Column_alias\tType\tLength\tMax length\tIs_null\t" + "Flags\tDecimals\tCharsetnr\n"); + + for (field_end= field+num_fields ; + field < field_end ; + field++) + { + char buff[22]; + dynstr_append_mem(ds, field->catalog, + field->catalog_length); + dynstr_append_mem(ds, "\t", 1); + dynstr_append_mem(ds, field->db, field->db_length); + dynstr_append_mem(ds, "\t", 1); + dynstr_append_mem(ds, field->org_table, + field->org_table_length); + dynstr_append_mem(ds, "\t", 1); + dynstr_append_mem(ds, field->table, + field->table_length); + dynstr_append_mem(ds, "\t", 1); + dynstr_append_mem(ds, field->org_name, + field->org_name_length); + dynstr_append_mem(ds, "\t", 1); + dynstr_append_mem(ds, field->name, field->name_length); + dynstr_append_mem(ds, "\t", 1); + int10_to_str((int) field->type, buff, 10); + dynstr_append(ds, buff); + dynstr_append_mem(ds, "\t", 1); + longlong10_to_str((unsigned int) field->length, buff, 10); + dynstr_append(ds, buff); + dynstr_append_mem(ds, "\t", 1); + longlong10_to_str((unsigned int) field->max_length, buff, 10); + dynstr_append(ds, buff); + dynstr_append_mem(ds, "\t", 1); + dynstr_append_mem(ds, (char*) (IS_NOT_NULL(field->flags) ? + "N" : "Y"), 1); + dynstr_append_mem(ds, "\t", 1); + + int10_to_str((int) field->flags, buff, 10); + dynstr_append(ds, buff); + dynstr_append_mem(ds, "\t", 1); + int10_to_str((int) field->decimals, buff, 10); + dynstr_append(ds, buff); + dynstr_append_mem(ds, "\t", 1); + int10_to_str((int) field->charsetnr, buff, 10); + dynstr_append(ds, buff); + dynstr_append_mem(ds, "\n", 1); + } +} + + +static void run_query_stmt_handle_warnings(MYSQL *mysql, DYNAMIC_STRING *ds) +{ + uint count; + DBUG_ENTER("run_query_stmt_handle_warnings"); + + if (!disable_warnings && (count= mysql_warning_count(mysql))) + { + if (mysql_real_query(mysql, "SHOW WARNINGS", 13) == 0) + { + MYSQL_RES *warn_res= mysql_store_result(mysql); + if (!warn_res) + verbose_msg("Warning count is %u but didn't get any warnings\n", + count); + else + { + dynstr_append_mem(ds, "Warnings:\n", 10); + append_result(ds, warn_res); + mysql_free_result(warn_res); + } + } + } + DBUG_VOID_RETURN; +} + + +static int run_query_stmt_handle_error(char *query, struct st_query *q, + MYSQL_STMT *stmt, DYNAMIC_STRING *ds) +{ + if (q->require_file) /* FIXME don't understand this one */ + { + abort_not_supported_test(); + } + + if (q->abort_on_error) + die("At line %u: query '%s' failed: %d: %s", start_lineno, query, + mysql_stmt_errno(stmt), mysql_stmt_error(stmt)); + else + { + int i; + + for (i=0 ; (uint) i < q->expected_errors ; i++) + { + if (((q->expected_errno[i].type == ERR_ERRNO) && + (q->expected_errno[i].code.errnum == mysql_stmt_errno(stmt))) || + ((q->expected_errno[i].type == ERR_SQLSTATE) && + (strcmp(q->expected_errno[i].code.sqlstate, + mysql_stmt_sqlstate(stmt)) == 0))) + { + if (i == 0 && q->expected_errors == 1) + { + /* Only log error if there is one possible error */ + dynstr_append_mem(ds,"ERROR ",6); + replace_dynstr_append_mem(ds, mysql_stmt_sqlstate(stmt), + strlen(mysql_stmt_sqlstate(stmt))); + dynstr_append_mem(ds, ": ", 2); + replace_dynstr_append_mem(ds,mysql_stmt_error(stmt), + strlen(mysql_stmt_error(stmt))); + dynstr_append_mem(ds,"\n",1); + } + /* Don't log error if we may not get an error */ + else if (q->expected_errno[0].type == ERR_SQLSTATE || + (q->expected_errno[0].type == ERR_ERRNO && + q->expected_errno[0].code.errnum != 0)) + dynstr_append(ds,"Got one of the listed errors\n"); + return 0; /* Ok */ + } + } + DBUG_PRINT("info",("i: %d expected_errors: %d", i, + q->expected_errors)); + dynstr_append_mem(ds, "ERROR ",6); + replace_dynstr_append_mem(ds, mysql_stmt_sqlstate(stmt), + strlen(mysql_stmt_sqlstate(stmt))); + dynstr_append_mem(ds,": ",2); + replace_dynstr_append_mem(ds, mysql_stmt_error(stmt), + strlen(mysql_stmt_error(stmt))); + dynstr_append_mem(ds,"\n",1); + if (i) + { + verbose_msg("query '%s' failed with wrong errno %d instead of %d...", + q->query, mysql_stmt_errno(stmt), q->expected_errno[0]); + return 1; /* Error */ + } + verbose_msg("query '%s' failed: %d: %s", q->query, mysql_stmt_errno(stmt), + mysql_stmt_error(stmt)); + /* + if we do not abort on error, failure to run the query does + not fail the whole test case + */ + return 0; + } + + return 0; +} + +/****************************************************************************\ + * Functions to match SQL statements that can be prepared +\****************************************************************************/ + +static void ps_init_re(void) +{ + const char *ps_re_str = + "^(" + "[[:space:]]*REPLACE[[:space:]]|" + "[[:space:]]*INSERT[[:space:]]|" + "[[:space:]]*UPDATE[[:space:]]|" + "[[:space:]]*DELETE[[:space:]]|" + "[[:space:]]*SELECT[[:space:]]|" + "[[:space:]]*CREATE[[:space:]]+TABLE[[:space:]]|" + "[[:space:]]*DO[[:space:]]|" + "[[:space:]]*SET[[:space:]]+OPTION[[:space:]]|" + "[[:space:]]*DELETE[[:space:]]+MULTI[[:space:]]|" + "[[:space:]]*UPDATE[[:space:]]+MULTI[[:space:]]|" + "[[:space:]]*INSERT[[:space:]]+SELECT[[:space:]])"; + + int err= regcomp(&ps_re, ps_re_str, (REG_EXTENDED | REG_ICASE | REG_NOSUB), + &my_charset_latin1); + if (err) + { + char erbuf[100]; + int len= regerror(err, &ps_re, erbuf, sizeof(erbuf)); + fprintf(stderr, "error %s, %d/%d `%s'\n", + ps_eprint(err), len, (int)sizeof(erbuf), erbuf); + exit(1); + } +} + + +static int ps_match_re(char *stmt_str) +{ + int err= regexec(&ps_re, stmt_str, (size_t)0, NULL, 0); + + if (err == 0) + return 1; + else if (err == REG_NOMATCH) + return 0; + else + { + char erbuf[100]; + int len= regerror(err, &ps_re, erbuf, sizeof(erbuf)); + fprintf(stderr, "error %s, %d/%d `%s'\n", + ps_eprint(err), len, (int)sizeof(erbuf), erbuf); + exit(1); + } +} + +static char *ps_eprint(int err) +{ + static char epbuf[100]; + size_t len= regerror(REG_ITOA|err, (regex_t *)NULL, epbuf, sizeof(epbuf)); + assert(len <= sizeof(epbuf)); + return(epbuf); +} + + +static void ps_free_reg(void) +{ + regfree(&ps_re); +} + +/****************************************************************************/ + void get_query_type(struct st_query* q) { char save; @@ -2415,15 +3401,16 @@ static void var_free(void *v) } -static void var_from_env(const char *name, const char *def_val) +static VAR* var_from_env(const char *name, const char *def_val) { const char *tmp; VAR *v; if (!(tmp = getenv(name))) tmp = def_val; - v = var_init(0, name, 0, tmp, 0); + v = var_init(0, name, strlen(name), tmp, strlen(tmp)); my_hash_insert(&var_hash, (byte*)v); + return v; } @@ -2431,18 +3418,17 @@ static void init_var_hash(MYSQL *mysql) { VAR *v; DBUG_ENTER("init_var_hash"); - if (hash_init(&var_hash, + if (hash_init(&var_hash, charset_info, 1024, 0, 0, get_var_key, var_free, MYF(0))) die("Variable hash initialization failed"); - var_from_env("MASTER_MYPORT", "9306"); - var_from_env("SLAVE_MYPORT", "9307"); - var_from_env("MYSQL_TEST_DIR", "/tmp"); - var_from_env("BIG_TEST", opt_big_test ? "1" : "0"); + if (opt_big_test) + my_hash_insert(&var_hash, (byte*) var_init(0,"BIG_TEST", 0, "1",0)); v= var_init(0,"MAX_TABLES", 0, (sizeof(ulong) == 4) ? "31" : "62",0); my_hash_insert(&var_hash, (byte*) v); v= var_init(0,"SERVER_VERSION", 0, mysql_get_server_info(mysql), 0); my_hash_insert(&var_hash, (byte*) v); - + v= var_init(0,"DB", 2, db, 0); + my_hash_insert(&var_hash, (byte*) v); DBUG_VOID_RETURN; } @@ -2458,6 +3444,9 @@ int main(int argc, char **argv) DBUG_ENTER("main"); DBUG_PROCESS(argv[0]); + /* Use all time until exit if no explicit 'start_timer' */ + timer_start= timer_now(); + save_file[0]=0; TMPDIR[0]=0; memset(cons, 0, sizeof(cons)); @@ -2472,12 +3461,13 @@ int main(int argc, char **argv) lineno = lineno_stack; my_init_dynamic_array(&q_lines, sizeof(struct st_query*), INIT_Q_LINES, INIT_Q_LINES); + memset(block_stack, 0, sizeof(block_stack)); - block_stack_end = block_stack + BLOCK_STACK_DEPTH; - memset(block_ok_stack, 0, sizeof(block_stack)); - cur_block = block_stack; - block_ok = block_ok_stack; - *block_ok = 1; + block_stack_end= block_stack + BLOCK_STACK_DEPTH; + cur_block= block_stack; + cur_block->ok= TRUE; /* Outer block should always be executed */ + cur_block->cmd= Q_UNKNOWN; + init_dynamic_string(&ds_res, "", 0, 65536); parse_args(argc, argv); if (mysql_server_init(embedded_server_arg_count, @@ -2491,11 +3481,18 @@ int main(int argc, char **argv) if (manager_host) init_manager(); #endif + if (ps_protocol) + { + ps_protocol_enabled= 1; + ps_init_re(); + } if (!( mysql_init(&cur_con->mysql))) die("Failed in mysql_init()"); if (opt_compress) mysql_options(&cur_con->mysql,MYSQL_OPT_COMPRESS,NullS); mysql_options(&cur_con->mysql, MYSQL_OPT_LOCAL_INFILE, 0); + mysql_options(&cur_con->mysql, MYSQL_SET_CHARSET_NAME, charset_name); + #ifdef HAVE_OPENSSL if (opt_use_ssl) mysql_ssl_set(&cur_con->mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca, @@ -2510,12 +3507,19 @@ int main(int argc, char **argv) init_var_hash(&cur_con->mysql); + /* + Initialize $mysql_errno with -1, so we can + - distinguish it from valid values ( >= 0 ) and + - detect if there was never a command sent to the server + */ + var_set_errno(-1); + while (!read_query(&q)) { int current_line_inc = 1, processed = 0; if (q->type == Q_UNKNOWN || q->type == Q_COMMENT_WITH_COMMAND) get_query_type(q); - if (*block_ok) + if (cur_block->ok) { processed = 1; switch (q->type) { @@ -2529,16 +3533,19 @@ int main(int argc, char **argv) case Q_DISABLE_RPL_PARSE: do_disable_rpl_parse(q); break; case Q_ENABLE_QUERY_LOG: disable_query_log=0; break; case Q_DISABLE_QUERY_LOG: disable_query_log=1; break; + case Q_ENABLE_ABORT_ON_ERROR: abort_on_error=1; break; + case Q_DISABLE_ABORT_ON_ERROR: abort_on_error=0; break; case Q_ENABLE_RESULT_LOG: disable_result_log=0; break; case Q_DISABLE_RESULT_LOG: disable_result_log=1; break; case Q_ENABLE_WARNINGS: disable_warnings=0; break; case Q_DISABLE_WARNINGS: disable_warnings=1; break; - case Q_ENABLE_INFO: disable_info=0; break; - case Q_DISABLE_INFO: disable_info=1; break; + case Q_ENABLE_INFO: disable_info=0; break; + case Q_DISABLE_INFO: disable_info=1; break; + case Q_ENABLE_METADATA: display_metadata=1; break; + case Q_DISABLE_METADATA: display_metadata=0; break; case Q_SOURCE: do_source(q); break; case Q_SLEEP: do_sleep(q, 0); break; case Q_REAL_SLEEP: do_sleep(q, 1); break; - case Q_REQUIRE_VERSION: do_require_version(q); break; case Q_WAIT_FOR_SLAVE_TO_STOP: do_wait_for_slave_to_stop(q); break; case Q_REQUIRE_MANAGER: do_require_manager(q); break; #ifndef EMBEDDED_LIBRARY @@ -2549,12 +3556,49 @@ int main(int argc, char **argv) case Q_DEC: do_dec(q); break; case Q_ECHO: do_echo(q); break; case Q_SYSTEM: do_system(q); break; + case Q_DELIMITER: + strmake(delimiter, q->first_argument, sizeof(delimiter) - 1); + delimiter_length= strlen(delimiter); + break; + case Q_DISPLAY_VERTICAL_RESULTS: display_result_vertically= TRUE; break; + case Q_DISPLAY_HORIZONTAL_RESULTS: + display_result_vertically= FALSE; break; case Q_LET: do_let(q); break; case Q_EVAL_RESULT: eval_result = 1; break; case Q_EVAL: if (q->query == q->query_buf) + { q->query= q->first_argument; + q->first_word_len= 0; + } /* fall through */ + case Q_QUERY_VERTICAL: + case Q_QUERY_HORIZONTAL: + { + my_bool old_display_result_vertically= display_result_vertically; + if (!q->query[q->first_word_len]) + { + /* This happens when we use 'query_..' on it's own line */ + q_send_flag=1; + DBUG_PRINT("info", + ("query: '%s' first_word_len: %d send_flag=1", + q->query, q->first_word_len)); + break; + } + /* fix up query pointer if this is * first iteration for this line */ + if (q->query == q->query_buf) + q->query += q->first_word_len + 1; + display_result_vertically= (q->type==Q_QUERY_VERTICAL); + if (save_file[0]) + { + strmov(q->record_file,save_file); + q->require_file=require_file; + save_file[0]=0; + } + error|= run_query(&cur_con->mysql, q, QUERY_REAP|QUERY_SEND); + display_result_vertically= old_display_result_vertically; + break; + } case Q_QUERY: case Q_REAP: { @@ -2602,7 +3646,7 @@ int main(int argc, char **argv) require_file=0; break; case Q_ERROR: - global_expected_errors=get_ints(global_expected_errno,q); + global_expected_errors=get_errcodes(global_expected_errno,q); break; case Q_REQUIRE: get_file_name(save_file,q); @@ -2636,8 +3680,33 @@ int main(int argc, char **argv) (void) mysql_ping(&cur_con->mysql); break; case Q_EXEC: - (void) do_exec(q); + do_exec(q); + break; + case Q_START_TIMER: + /* Overwrite possible earlier start of timer */ + timer_start= timer_now(); break; + case Q_END_TIMER: + /* End timer before ending mysqltest */ + timer_output(); + got_end_timer= TRUE; + break; + case Q_CHARACTER_SET: + set_charset(q); + break; + case Q_DISABLE_PS_PROTOCOL: + ps_protocol_enabled= 0; + break; + case Q_ENABLE_PS_PROTOCOL: + ps_protocol_enabled= ps_protocol; + break; + case Q_DISABLE_RECONNECT: + cur_con->mysql.reconnect= 0; + break; + case Q_ENABLE_RECONNECT: + cur_con->mysql.reconnect= 1; + break; + default: processed = 0; break; } } @@ -2646,7 +3715,8 @@ int main(int argc, char **argv) { current_line_inc = 0; switch (q->type) { - case Q_WHILE: do_while(q); break; + case Q_WHILE: do_block(Q_WHILE, q); break; + case Q_IF: do_block(Q_IF, q); break; case Q_END_BLOCK: do_done(q); break; default: current_line_inc = 1; break; } @@ -2672,12 +3742,16 @@ int main(int argc, char **argv) printf("ok\n"); } + if (!got_end_timer) + timer_output(); /* No end_timer cmd, end it */ free_used_memory(); + my_end(MY_CHECK_ERROR); exit(error ? 1 : 0); return error ? 1 : 0; /* Keep compiler happy */ } } + /* Read arguments for embedded server and put them into embedded_server_args_count and embedded_server_args[] @@ -2724,6 +3798,45 @@ static int read_server_arguments(const char *name) return 0; } +/****************************************************************************\ + * + * A primitive timer that give results in milliseconds if the + * --timer-file=<filename> is given. The timer result is written + * to that file when the result is available. To not confuse + * mysql-test-run with an old obsolete result, we remove the file + * before executing any commands. The time we measure is + * + * - If no explicit 'start_timer' or 'end_timer' is given in the + * test case, the timer measure how long we execute in mysqltest. + * + * - If only 'start_timer' is given we measure how long we execute + * from that point until we terminate mysqltest. + * + * - If only 'end_timer' is given we measure how long we execute + * from that we enter mysqltest to the 'end_timer' is command is + * executed. + * + * - If both 'start_timer' and 'end_timer' are given we measure + * the time between executing the two commands. + * +\****************************************************************************/ + +static void timer_output(void) +{ + if (timer_file) + { + char buf[32], *end; + ulonglong timer= timer_now() - timer_start; + end= longlong2str(timer, buf, 10); + str_to_file(timer_file,buf, (int) (end-buf)); + } +} + +static ulonglong timer_now(void) +{ + return my_getsystime() / 10000; +} + /**************************************************************************** * Handle replacement of strings ****************************************************************************/ @@ -3494,8 +4607,7 @@ static void get_replace_column(struct st_query *q) my_free(start, MYF(0)); } -#ifdef __NETWARE__ - +#if defined(__NETWARE__) || defined(__WIN__) /* Substitute environment variables with text. @@ -3579,6 +4691,9 @@ static char *subst_env_var(const char *str) */ #undef popen /* Remove wrapper */ +#ifdef __WIN__ +#define popen _popen /* redefine for windows */ +#endif FILE *my_popen(const char *cmd, const char *mode __attribute__((unused))) { @@ -3591,4 +4706,4 @@ FILE *my_popen(const char *cmd, const char *mode __attribute__((unused))) return res_file; } -#endif /* __NETWARE__ */ +#endif /* __NETWARE__ or __WIN__*/ diff --git a/client/readline.cc b/client/readline.cc index f5fbfd8cd0c..3d524633d69 100644 --- a/client/readline.cc +++ b/client/readline.cc @@ -31,7 +31,8 @@ static char *intern_read_line(LINE_BUFFER *buffer,ulong *out_length); LINE_BUFFER *batch_readline_init(ulong max_size,FILE *file) { LINE_BUFFER *line_buff; - if (!(line_buff=(LINE_BUFFER*) my_malloc(sizeof(*line_buff),MYF(MY_WME)))) + if (!(line_buff=(LINE_BUFFER*) + my_malloc(sizeof(*line_buff),MYF(MY_WME | MY_ZEROFILL)))) return 0; if (init_line_buffer(line_buff,fileno(file),IO_SIZE,max_size)) { @@ -67,11 +68,12 @@ void batch_readline_end(LINE_BUFFER *line_buff) } -LINE_BUFFER *batch_readline_command(my_string str) +LINE_BUFFER *batch_readline_command(LINE_BUFFER *line_buff, my_string str) { - LINE_BUFFER *line_buff; - if (!(line_buff=(LINE_BUFFER*) my_malloc(sizeof(*line_buff),MYF(MY_WME)))) - return 0; + if (!line_buff) + if (!(line_buff=(LINE_BUFFER*) + my_malloc(sizeof(*line_buff),MYF(MY_WME | MY_ZEROFILL)))) + return 0; if (init_line_buffer_from_string(line_buff,str)) { my_free((char*) line_buff,MYF(0)); @@ -88,7 +90,6 @@ LINE_BUFFER *batch_readline_command(my_string str) static bool init_line_buffer(LINE_BUFFER *buffer,File file,ulong size,ulong max_buffer) { - bzero((char*) buffer,sizeof(buffer[0])); buffer->file=file; buffer->bufread=size; buffer->max_size=max_buffer; @@ -100,19 +101,26 @@ init_line_buffer(LINE_BUFFER *buffer,File file,ulong size,ulong max_buffer) return 0; } - +/* + init_line_buffer_from_string can be called on the same buffer + several times. the resulting buffer will contain a + concatenation of all strings separated by spaces +*/ static bool init_line_buffer_from_string(LINE_BUFFER *buffer,my_string str) { - uint length; - bzero((char*) buffer,sizeof(buffer[0])); - length=(uint) strlen(str); - if (!(buffer->buffer=buffer->start_of_line=buffer->end_of_line= - (char*)my_malloc(length+2,MYF(MY_FAE)))) + uint old_length=(uint)(buffer->end - buffer->buffer); + uint length= (uint) strlen(str); + if (!(buffer->buffer= buffer->start_of_line= buffer->end_of_line= + (char*)my_realloc(buffer->buffer, old_length+length+2, + MYF(MY_FAE|MY_ALLOW_ZERO_PTR)))) return 1; - memcpy(buffer->buffer,str,length); - buffer->buffer[length]='\n'; - buffer->buffer[length+1]=0; - buffer->end=buffer->buffer+length+1; + buffer->end= buffer->buffer + old_length; + if (old_length) + buffer->end[-1]=' '; + memcpy(buffer->end, str, length); + buffer->end[length]= '\n'; + buffer->end[length+1]= 0; + buffer->end+= length+1; buffer->eof=1; buffer->max_size=1; return 0; diff --git a/client/select_test.c b/client/select_test.c deleted file mode 100644 index ee2a9192865..00000000000 --- a/client/select_test.c +++ /dev/null @@ -1,73 +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 */ - -#if defined(_WIN32) || defined(_WIN64) -#include <windows.h> -#endif -#include <stdio.h> -#include <stdlib.h> -#include "mysql.h" - -#define SELECT_QUERY "select name from test where num = %d" - - -int main(int argc, char **argv) -{ - int count, num; - MYSQL mysql,*sock; - MYSQL_RES *res; - char qbuf[160]; - - if (argc != 3) - { - fprintf(stderr,"usage : select_test <dbname> <num>\n\n"); - exit(1); - } - - mysql_init(&mysql); - if (!(sock = mysql_real_connect(&mysql,NULL,0,0,argv[1],0,NULL,0))) - { - fprintf(stderr,"Couldn't connect to engine!\n%s\n\n",mysql_error(&mysql)); - perror(""); - exit(1); - } - - count = 0; - num = atoi(argv[2]); - while (count < num) - { - sprintf(qbuf,SELECT_QUERY,count); - if(mysql_query(sock,qbuf)) - { - fprintf(stderr,"Query failed (%s)\n",mysql_error(sock)); - exit(1); - } - if (!(res=mysql_store_result(sock))) - { - fprintf(stderr,"Couldn't get result from %s\n", - mysql_error(sock)); - exit(1); - } -#ifdef TEST - printf("number of fields: %d\n",mysql_num_fields(res)); -#endif - mysql_free_result(res); - count++; - } - mysql_close(sock); - exit(0); - return 0; /* Keep some compilers happy */ -} diff --git a/client/showdb_test.c b/client/showdb_test.c deleted file mode 100644 index df2b3037c00..00000000000 --- a/client/showdb_test.c +++ /dev/null @@ -1,66 +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 */ - - -#ifdef __WIN__ -#include <windows.h> -#endif -#include <stdio.h> -#include <stdlib.h> -#include "mysql.h" - -#define SELECT_QUERY "select name from test where num = %d" - - -int main(int argc, char **argv) -{ - int count, num; - MYSQL mysql,*sock; - MYSQL_RES *res; - char qbuf[160]; - - if (argc != 3) - { - fprintf(stderr,"usage : select_test <dbname> <num>\n\n"); - exit(1); - } - - mysql_init(&mysql); - if (!(sock = mysql_real_connect(&mysql,NULL,0,0,argv[1],0,NULL,0))) - { - fprintf(stderr,"Couldn't connect to engine!\n%s\n\n",mysql_error(&mysql)); - perror(""); - exit(1); - } - - count = 0; - num = atoi(argv[2]); - while (count < num) - { - sprintf(qbuf,SELECT_QUERY,count); - if(!(res=mysql_list_dbs(sock,NULL))) - { - fprintf(stderr,"Query failed (%s)\n",mysql_error(sock)); - exit(1); - } - printf("number of fields: %d\n",mysql_num_rows(res)); - mysql_free_result(res); - count++; - } - mysql_close(sock); - exit(0); - return 0; /* Keep some compilers happy */ -} diff --git a/client/sql_string.cc b/client/sql_string.cc index 3c5e481eaad..690997152f1 100644 --- a/client/sql_string.cc +++ b/client/sql_string.cc @@ -16,7 +16,7 @@ /* This file is originally from the mysql distribution. Coded by monty */ -#ifdef __GNUC__ +#ifdef USE_PRAGMA_IMPLEMENTATION #pragma implementation // gcc: Class implementation #endif @@ -40,19 +40,16 @@ extern void sql_element_free(void *ptr); bool String::real_alloc(uint32 arg_length) { arg_length=ALIGN_SIZE(arg_length+1); + str_length=0; if (Alloced_length < arg_length) { free(); if (!(Ptr=(char*) my_malloc(arg_length,MYF(MY_WME)))) - { - str_length=0; return TRUE; - } Alloced_length=arg_length; alloced=1; } Ptr[0]=0; - str_length=0; return FALSE; } @@ -94,36 +91,58 @@ bool String::realloc(uint32 alloc_length) return FALSE; } -bool String::set(longlong num) +bool String::set(longlong num, CHARSET_INFO *cs) { - if (alloc(21)) + uint l=20*cs->mbmaxlen+1; + + if (alloc(l)) return TRUE; - str_length=(uint32) (longlong10_to_str(num,Ptr,-10)-Ptr); + if (cs->cset->snprintf == my_snprintf_8bit) + { + str_length=(uint32) (longlong10_to_str(num,Ptr,-10)-Ptr); + } + else + { + str_length=cs->cset->snprintf(cs,Ptr,l,"%d",num); + } + str_charset=cs; return FALSE; } -bool String::set(ulonglong num) +bool String::set(ulonglong num, CHARSET_INFO *cs) { - if (alloc(21)) + uint l=20*cs->mbmaxlen+1; + + if (alloc(l)) return TRUE; - str_length=(uint32) (longlong10_to_str(num,Ptr,10)-Ptr); + if (cs->cset->snprintf == my_snprintf_8bit) + { + str_length=(uint32) (longlong10_to_str(num,Ptr,10)-Ptr); + } + else + { + str_length=cs->cset->snprintf(cs,Ptr,l,"%d",num); + } + str_charset=cs; return FALSE; } -bool String::set(double num,uint decimals) +bool String::set(double num,uint decimals, CHARSET_INFO *cs) { char buff[331]; + + str_charset=cs; if (decimals >= NOT_FIXED_DEC) { sprintf(buff,"%.14g",num); // Enough for a DATETIME - return copy(buff, (uint32) strlen(buff)); + return copy(buff, (uint32) strlen(buff), &my_charset_latin1, cs); } #ifdef HAVE_FCONVERT int decpt,sign; char *pos,*to; VOID(fconvert(num,(int) decimals,&decpt,&sign,buff+1)); - if (!isdigit(buff[1])) + if (!my_isdigit(&my_charset_latin1, buff[1])) { // Nan or Inf pos=buff+1; if (sign) @@ -131,7 +150,7 @@ bool String::set(double num,uint decimals) buff[0]='-'; pos=buff; } - return copy(pos,(uint32) strlen(pos)); + return copy(pos,(uint32) strlen(pos), &my_charset_latin1, cs); } if (alloc((uint32) ((uint32) decpt+3+decimals))) return TRUE; @@ -181,7 +200,7 @@ end: #else sprintf(buff,"%.*f",(int) decimals,num); #endif - return copy(buff,(uint32) strlen(buff)); + return copy(buff,(uint32) strlen(buff), &my_charset_latin1, cs); #endif } @@ -203,16 +222,67 @@ bool String::copy(const String &str) str_length=str.str_length; bmove(Ptr,str.Ptr,str_length); // May be overlapping Ptr[str_length]=0; + str_charset=str.str_charset; return FALSE; } -bool String::copy(const char *str,uint32 arg_length) +bool String::copy(const char *str,uint32 arg_length, CHARSET_INFO *cs) { if (alloc(arg_length)) return TRUE; if ((str_length=arg_length)) memcpy(Ptr,str,arg_length); Ptr[arg_length]=0; + str_charset=cs; + return FALSE; +} + +/* Copy with charset convertion */ +bool String::copy(const char *str,uint32 arg_length, CHARSET_INFO *from, CHARSET_INFO *to) +{ + uint32 new_length=to->mbmaxlen*arg_length; + int cnvres; + my_wc_t wc; + const uchar *s=(const uchar *)str; + const uchar *se=s+arg_length; + uchar *d, *de; + + if (alloc(new_length)) + return TRUE; + + d=(uchar *)Ptr; + de=d+new_length; + + for (str_length=new_length ; s < se && d < de ; ) + { + if ((cnvres=from->cset->mb_wc(from,&wc,s,se)) > 0 ) + { + s+=cnvres; + } + else if (cnvres==MY_CS_ILSEQ) + { + s++; + wc='?'; + } + else + break; + +outp: + if((cnvres=to->cset->wc_mb(to,wc,d,de)) >0 ) + { + d+=cnvres; + } + else if (cnvres==MY_CS_ILUNI && wc!='?') + { + wc='?'; + goto outp; + } + else + break; + } + Ptr[new_length]=0; + length((uint32) (d-(uchar *)Ptr)); + str_charset=to; return FALSE; } @@ -234,7 +304,7 @@ bool String::fill(uint32 max_length,char fill_char) void String::strip_sp() { - while (str_length && isspace(Ptr[str_length-1])) + while (str_length && my_isspace(str_charset,Ptr[str_length-1])) str_length--; } @@ -296,10 +366,10 @@ uint32 String::numchars() register uint32 n=0,mblen; register const char *mbstr=Ptr; register const char *end=mbstr+str_length; - if (use_mb(default_charset_info)) + if (use_mb(str_charset)) { while (mbstr < end) { - if ((mblen=my_ismbchar(default_charset_info, mbstr,end))) mbstr+=mblen; + if ((mblen=my_ismbchar(str_charset, mbstr,end))) mbstr+=mblen; else ++mbstr; ++n; } @@ -316,11 +386,11 @@ int String::charpos(int i,uint32 offset) register uint32 mblen; register const char *mbstr=Ptr+offset; register const char *end=Ptr+str_length; - if (use_mb(default_charset_info)) + if (use_mb(str_charset)) { if (i<=0) return i; while (i && mbstr < end) { - if ((mblen=my_ismbchar(default_charset_info, mbstr,end))) mbstr+=mblen; + if ((mblen=my_ismbchar(str_charset, mbstr,end))) mbstr+=mblen; else ++mbstr; --i; } @@ -345,7 +415,7 @@ int String::strstr(const String &s,uint32 offset) register const char *search=s.ptr(); const char *end=Ptr+str_length-s.length()+1; const char *search_end=s.ptr()+s.length(); -skipp: +skip: while (str != end) { if (*str++ == *search) @@ -353,7 +423,7 @@ skipp: register char *i,*j; i=(char*) str; j=(char*) search+1; while (j != search_end) - if (*i++ != *j++) goto skipp; + if (*i++ != *j++) goto skip; return (int) (str-Ptr) -1; } } @@ -361,6 +431,39 @@ skipp: return -1; } +/* + Search after a string without regarding to case + This needs to be replaced when we have character sets per string +*/ + +int String::strstr_case(const String &s,uint32 offset) +{ + if (s.length()+offset <= str_length) + { + if (!s.length()) + return ((int) offset); // Empty string is always found + + register const char *str = Ptr+offset; + register const char *search=s.ptr(); + const char *end=Ptr+str_length-s.length()+1; + const char *search_end=s.ptr()+s.length(); +skip: + while (str != end) + { + if (str_charset->sort_order[*str++] == str_charset->sort_order[*search]) + { + register char *i,*j; + i=(char*) str; j=(char*) search+1; + while (j != search_end) + if (str_charset->sort_order[*i++] != + str_charset->sort_order[*j++]) + goto skip; + return (int) (str-Ptr) -1; + } + } + } + return -1; +} /* ** Search string from end. Offset is offset to the end of string @@ -377,7 +480,7 @@ int String::strrstr(const String &s,uint32 offset) const char *end=Ptr+s.length()-2; const char *search_end=s.ptr()-1; -skipp: +skip: while (str != end) { if (*str-- == *search) @@ -385,7 +488,7 @@ skipp: register char *i,*j; i=(char*) str; j=(char*) search-1; while (j != search_end) - if (*i-- != *j--) goto skipp; + if (*i-- != *j--) goto skip; return (int) (i-Ptr) +1; } } @@ -428,6 +531,44 @@ bool String::replace(uint32 offset,uint32 arg_length,const String &to) return FALSE; } +// added by Holyfoot for "geometry" needs +int String::reserve(uint32 space_needed, uint32 grow_by) +{ + if (Alloced_length < str_length + space_needed) + { + if (realloc(Alloced_length + max(space_needed, grow_by) - 1)) + return TRUE; + } + return FALSE; +} + +void String::qs_append(const char *str) +{ + int len = (int)strlen(str); + memcpy(Ptr + str_length, str, len + 1); + str_length += len; +} + +void String::qs_append(double d) +{ + char *buff = Ptr + str_length; + sprintf(buff,"%.14g", d); + str_length += (int)strlen(buff); +} + +void String::qs_append(double *d) +{ + double ld; + float8get(ld, (char*) d); + qs_append(ld); +} + +void String::qs_append(const char &c) +{ + Ptr[str_length] = c; + str_length += sizeof(c); +} + int sortcmp(const String *x,const String *y) { @@ -435,28 +576,38 @@ int sortcmp(const String *x,const String *y) const char *t= y->ptr(); uint32 x_len=x->length(),y_len=y->length(),len=min(x_len,y_len); -#ifdef USE_STRCOLL - if (use_strcoll(default_charset_info)) + if (use_strnxfrm(x->str_charset)) { #ifndef CMP_ENDSPACE - while (x_len && isspace(s[x_len-1])) + while (x_len && my_isspace(x->str_charset,s[x_len-1])) x_len--; - while (y_len && isspace(t[y_len-1])) + while (y_len && my_isspace(x->str_charset,t[y_len-1])) y_len--; #endif - return my_strnncoll(default_charset_info, + return my_strnncoll(x->str_charset, (unsigned char *)s,x_len,(unsigned char *)t,y_len); } else { -#endif /* USE_STRCOLL */ x_len-=len; // For easy end space test y_len-=len; - while (len--) + if (x->str_charset->sort_order) { - if (my_sort_order[(uchar) *s++] != my_sort_order[(uchar) *t++]) - return ((int) my_sort_order[(uchar) s[-1]] - - (int) my_sort_order[(uchar) t[-1]]); + while (len--) + { + if (x->str_charset->sort_order[(uchar) *s++] != + x->str_charset->sort_order[(uchar) *t++]) + return ((int) x->str_charset->sort_order[(uchar) s[-1]] - + (int) x->str_charset->sort_order[(uchar) t[-1]]); + } + } + else + { + while (len--) + { + if (*s++ != *t++) + return ((int) s[-1] - (int) t[-1]); + } } #ifndef CMP_ENDSPACE /* Don't compare end space in strings */ @@ -465,14 +616,14 @@ int sortcmp(const String *x,const String *y) { const char *end=t+y_len; for (; t != end ; t++) - if (!isspace(*t)) + if (!my_isspace(x->str_charset,*t)) return -1; } else { const char *end=s+x_len; for (; s != end ; s++) - if (!isspace(*s)) + if (!my_isspace(x->str_charset,*s)) return 1; } return 0; @@ -480,9 +631,7 @@ int sortcmp(const String *x,const String *y) #else return (int) (x_len-y_len); #endif /* CMP_ENDSPACE */ -#ifdef USE_STRCOLL } -#endif } @@ -514,240 +663,9 @@ String *copy_if_not_alloced(String *to,String *from,uint32 from_length) return from; // Actually an error if ((to->str_length=min(from->str_length,from_length))) memcpy(to->Ptr,from->Ptr,to->str_length); + to->str_charset=from->str_charset; return to; } -/* Make it easier to handle different charactersets */ -#ifdef USE_MB -#define INC_PTR(A,B) A+=((use_mb_flag && \ - my_ismbchar(default_charset_info,A,B)) ? \ - my_ismbchar(default_charset_info,A,B) : 1) -#else -#define INC_PTR(A,B) A++ -#endif -/* -** Compare string against string with wildcard -** 0 if matched -** -1 if not matched with wildcard -** 1 if matched with wildcard -*/ - -#ifdef LIKE_CMP_TOUPPER -#define likeconv(A) (uchar) toupper(A) -#else -#define likeconv(A) (uchar) my_sort_order[(uchar) (A)] -#endif - -static int wild_case_compare(const char *str,const char *str_end, - const char *wildstr,const char *wildend, - char escape) -{ - int result= -1; // Not found, using wildcards -#ifdef USE_MB - bool use_mb_flag=use_mb(default_charset_info); -#endif - while (wildstr != wildend) - { - while (*wildstr != wild_many && *wildstr != wild_one) - { - if (*wildstr == escape && wildstr+1 != wildend) - wildstr++; -#ifdef USE_MB - int l; - if (use_mb_flag && - (l = my_ismbchar(default_charset_info, wildstr, wildend))) - { - if (str+l > str_end || memcmp(str, wildstr, l) != 0) - return 1; - str += l; - wildstr += l; - } - else -#endif - if (str == str_end || likeconv(*wildstr++) != likeconv(*str++)) - return(1); // No match - if (wildstr == wildend) - return (str != str_end); // Match if both are at end - result=1; // Found an anchor char - } - if (*wildstr == wild_one) - { - do - { - if (str == str_end) // Skipp one char if possible - return (result); - INC_PTR(str,str_end); - } while (++wildstr < wildend && *wildstr == wild_one); - if (wildstr == wildend) - break; - } - if (*wildstr == wild_many) - { // Found wild_many - wildstr++; - /* Remove any '%' and '_' from the wild search string */ - for ( ; wildstr != wildend ; wildstr++) - { - if (*wildstr == wild_many) - continue; - if (*wildstr == wild_one) - { - if (str == str_end) - return (-1); - INC_PTR(str,str_end); - continue; - } - break; // Not a wild character - } - if (wildstr == wildend) - return(0); // Ok if wild_many is last - if (str == str_end) - return -1; - - uchar cmp; - if ((cmp= *wildstr) == escape && wildstr+1 != wildend) - cmp= *++wildstr; -#ifdef USE_MB - const char* mb = wildstr; - int mblen; - LINT_INIT(mblen); - if (use_mb_flag) - mblen = my_ismbchar(default_charset_info, wildstr, wildend); -#endif - INC_PTR(wildstr,wildend); // This is compared trough cmp - cmp=likeconv(cmp); - do - { -#ifdef USE_MB - if (use_mb_flag) - { - for (;;) - { - if (str >= str_end) - return -1; - if (mblen) - { - if (str+mblen <= str_end && memcmp(str, mb, mblen) == 0) - { - str += mblen; - break; - } - } - else if (!my_ismbchar(default_charset_info, str, str_end) && - likeconv(*str) == cmp) - { - str++; - break; - } - INC_PTR(str, str_end); - } - } - else - { -#endif /* USE_MB */ - while (str != str_end && likeconv(*str) != cmp) - str++; - if (str++ == str_end) return (-1); -#ifdef USE_MB - } -#endif - { - int tmp=wild_case_compare(str,str_end,wildstr,wildend,escape); - if (tmp <= 0) - return (tmp); - } - } while (str != str_end && wildstr[0] != wild_many); - return(-1); - } - } - return (str != str_end ? 1 : 0); -} - - -int wild_case_compare(String &match,String &wild, char escape) -{ - return wild_case_compare(match.ptr(),match.ptr()+match.length(), - wild.ptr(), wild.ptr()+wild.length(),escape); -} - -/* -** The following is used when using LIKE on binary strings -*/ - -static int wild_compare(const char *str,const char *str_end, - const char *wildstr,const char *wildend,char escape) -{ - int result= -1; // Not found, using wildcards - while (wildstr != wildend) - { - while (*wildstr != wild_many && *wildstr != wild_one) - { - if (*wildstr == escape && wildstr+1 != wildend) - wildstr++; - if (str == str_end || *wildstr++ != *str++) - return(1); - if (wildstr == wildend) - return (str != str_end); // Match if both are at end - result=1; // Found an anchor char - } - if (*wildstr == wild_one) - { - do - { - if (str == str_end) // Skipp one char if possible - return (result); - str++; - } while (*++wildstr == wild_one && wildstr != wildend); - if (wildstr == wildend) - break; - } - if (*wildstr == wild_many) - { // Found wild_many - wildstr++; - /* Remove any '%' and '_' from the wild search string */ - for ( ; wildstr != wildend ; wildstr++) - { - if (*wildstr == wild_many) - continue; - if (*wildstr == wild_one) - { - if (str == str_end) - return (-1); - str++; - continue; - } - break; // Not a wild character - } - if (wildstr == wildend) - return(0); // Ok if wild_many is last - if (str == str_end) - return -1; - - char cmp; - if ((cmp= *wildstr) == escape && wildstr+1 != wildend) - cmp= *++wildstr; - wildstr++; // This is compared trough cmp - do - { - while (str != str_end && *str != cmp) - str++; - if (str++ == str_end) return (-1); - { - int tmp=wild_compare(str,str_end,wildstr,wildend,escape); - if (tmp <= 0) - return (tmp); - } - } while (str != str_end && wildstr[0] != wild_many); - return(-1); - } - } - return (str != str_end ? 1 : 0); -} - - -int wild_compare(String &match,String &wild, char escape) -{ - return wild_compare(match.ptr(),match.ptr()+match.length(), - wild.ptr(), wild.ptr()+wild.length(),escape); -} diff --git a/client/sql_string.h b/client/sql_string.h index cffe78936a0..fd6d3ef59d9 100644 --- a/client/sql_string.h +++ b/client/sql_string.h @@ -16,7 +16,7 @@ /* This file is originally from the mysql distribution. Coded by monty */ -#ifdef __GNUC__ +#ifdef USE_PRAGMA_INTERFACE #pragma interface /* gcc class implementation */ #endif @@ -24,31 +24,56 @@ #define NOT_FIXED_DEC 31 #endif +class String; +int sortcmp(const String *a,const String *b); +int stringcmp(const String *a,const String *b); +String *copy_if_not_alloced(String *a,String *b,uint32 arg_length); + class String { char *Ptr; uint32 str_length,Alloced_length; bool alloced; + CHARSET_INFO *str_charset; public: String() - { Ptr=0; str_length=Alloced_length=0; alloced=0; } + { + Ptr=0; str_length=Alloced_length=0; alloced=0; + str_charset= &my_charset_latin1; + } String(uint32 length_arg) - { alloced=0; Alloced_length=0; (void) real_alloc(length_arg); } - String(const char *str) - { Ptr=(char*) str; str_length=(uint) strlen(str); Alloced_length=0; alloced=0;} - String(const char *str,uint32 len) - { Ptr=(char*) str; str_length=len; Alloced_length=0; alloced=0;} - String(char *str,uint32 len) - { Ptr=(char*) str; Alloced_length=str_length=len; alloced=0;} + { + alloced=0; Alloced_length=0; (void) real_alloc(length_arg); + str_charset= &my_charset_latin1; + } + String(const char *str, CHARSET_INFO *cs) + { + Ptr=(char*) str; str_length=(uint) strlen(str); Alloced_length=0; alloced=0; + str_charset=cs; + } + String(const char *str,uint32 len, CHARSET_INFO *cs) + { + Ptr=(char*) str; str_length=len; Alloced_length=0; alloced=0; + str_charset=cs; + } + String(char *str,uint32 len, CHARSET_INFO *cs) + { + Ptr=(char*) str; Alloced_length=str_length=len; alloced=0; + str_charset=cs; + } String(const String &str) - { Ptr=str.Ptr ; str_length=str.str_length ; - Alloced_length=str.Alloced_length; alloced=0; } - + { + Ptr=str.Ptr ; str_length=str.str_length ; + Alloced_length=str.Alloced_length; alloced=0; + str_charset=str.str_charset; + } static void *operator new(size_t size) { return (void*) sql_alloc((uint) size); } static void operator delete(void *ptr_arg,size_t size) /*lint -e715 */ { sql_element_free(ptr_arg); } ~String() { free(); } + inline void set_charset(CHARSET_INFO *charset) { str_charset=charset; } + inline CHARSET_INFO *charset() const { return str_charset; } inline uint32 length() const { return str_length;} inline uint32 alloced_length() const { return Alloced_length;} inline char& operator [] (uint32 i) const { return Ptr[i]; } @@ -67,6 +92,14 @@ public: Ptr[str_length]=0; return Ptr; } + inline char *c_ptr_safe() + { + if (Ptr && str_length < Alloced_length) + Ptr[str_length]=0; + else + (void) realloc(str_length); + return Ptr; + } void set(String &str,uint32 offset,uint32 arg_length) { @@ -76,28 +109,31 @@ public: Alloced_length=str.Alloced_length-offset; else Alloced_length=0; + str_charset=str.str_charset; } - inline void set(char *str,uint32 arg_length) + inline void set(char *str,uint32 arg_length, CHARSET_INFO *cs) { free(); Ptr=(char*) str; str_length=Alloced_length=arg_length ; alloced=0; + str_charset=cs; } - inline void set(const char *str,uint32 arg_length) + inline void set(const char *str,uint32 arg_length, CHARSET_INFO *cs) { free(); Ptr=(char*) str; str_length=arg_length; Alloced_length=0 ; alloced=0; + str_charset=cs; } - inline void set_quick(char *str,uint32 arg_length) + inline void set_quick(char *str,uint32 arg_length, CHARSET_INFO *cs) { if (!alloced) { Ptr=(char*) str; str_length=Alloced_length=arg_length; } + str_charset=cs; } - bool set(longlong num); - /* bool set(long num); */ - bool set(ulonglong num); - bool set(double num,uint decimals=2); + bool set(longlong num, CHARSET_INFO *cs); + bool set(ulonglong num, CHARSET_INFO *cs); + bool set(double num,uint decimals, CHARSET_INFO *cs); inline void free() { if (alloced) @@ -124,7 +160,7 @@ public: char *new_ptr; if (!(new_ptr=(char*) my_realloc(Ptr,arg_length,MYF(0)))) { - (void) my_free(Ptr,MYF(0)); + Alloced_length = 0; real_alloc(arg_length); } else @@ -148,11 +184,13 @@ public: bool copy(); // Alloc string if not alloced bool copy(const String &s); // Allocate new string - bool copy(const char *s,uint32 arg_length); // Allocate new string + bool copy(const char *s,uint32 arg_length, CHARSET_INFO *cs); // Allocate new string + bool copy(const char*s,uint32 arg_length, CHARSET_INFO *csfrom, CHARSET_INFO *csto); bool append(const String &s); bool append(const char *s,uint32 arg_length=0); bool append(IO_CACHE* file, uint32 arg_length); int strstr(const String &search,uint32 offset=0); // Returns offset to substring or -1 + int strstr_case(const String &s,uint32 offset=0); int strrstr(const String &search,uint32 offset=0); // Returns offset to substring or -1 bool replace(uint32 offset,uint32 arg_length,const String &to); inline bool append(char chr) @@ -171,13 +209,57 @@ public: } bool fill(uint32 max_length,char fill); void strip_sp(); - inline void caseup() { ::caseup(Ptr,str_length); } - inline void casedn() { ::casedn(Ptr,str_length); } + inline void caseup() { my_caseup(str_charset,Ptr,str_length); } + inline void casedn() { my_casedn(str_charset,Ptr,str_length); } friend int sortcmp(const String *a,const String *b); friend int stringcmp(const String *a,const String *b); friend String *copy_if_not_alloced(String *a,String *b,uint32 arg_length); - friend int wild_case_compare(String &match,String &wild,char escape); - friend int wild_compare(String &match,String &wild,char escape); uint32 numchars(); int charpos(int i,uint32 offset=0); + + int reserve(uint32 space_needed) + { + return realloc(str_length + space_needed); + } + int reserve(uint32 space_needed, uint32 grow_by); + + /* + The following append operations do NOT check alloced memory + q_*** methods writes values of parameters itself + qs_*** methods writes string representation of value + */ + void q_append(const char &c) + { + Ptr[str_length++] = c; + } + void q_append(const uint32 &n) + { + int4store(Ptr + str_length, n); + str_length += 4; + } + void q_append(double d) + { + float8store(Ptr + str_length, d); + str_length += 8; + } + void q_append(double *d) + { + float8store(Ptr + str_length, *d); + str_length += 8; + } + void q_append(const char *data, uint32 data_len) + { + memcpy(Ptr + str_length, data, data_len); + str_length += data_len; + } + + void WriteAtPosition(int position, uint32 value) + { + int4store(Ptr + position,value); + } + + void qs_append(const char *str); + void qs_append(double d); + void qs_append(double *d); + void qs_append(const char &c); }; diff --git a/client/ssl_test.c b/client/ssl_test.c deleted file mode 100644 index 279c1e95fdc..00000000000 --- a/client/ssl_test.c +++ /dev/null @@ -1,81 +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 */ - - -#ifdef __WIN__ -#include <windows.h> -#endif -#include <stdio.h> -#include <stdlib.h> -#include "mysql.h" -#include "config.h" -#define SELECT_QUERY "select name from test where num = %d" - - -int main(int argc, char **argv) -{ -#ifdef HAVE_OPENSSL - int count, num; - MYSQL mysql,*sock; - MYSQL_RES *res; - char qbuf[160]; - - if (argc != 3) - { - fprintf(stderr,"usage : ssl_test <dbname> <num>\n\n"); - exit(1); - } - - mysql_init(&mysql); -#ifdef HAVE_OPENSSL - mysql_ssl_set(&mysql,"../SSL/MySQL-client-key.pem","../SSL/MySQL-client-cert.pem","../SSL/MySQL-ca-cert.pem","../SSL/"); -#endif - if (!(sock = mysql_real_connect(&mysql,"127.0.0.1",0,0,argv[1],3306,NULL,0))) - { - fprintf(stderr,"Couldn't connect to engine!\n%s\n\n",mysql_error(&mysql)); - perror(""); - exit(1); - } - printf("Cipher:%s\n",mysql_ssl_cipher(&mysql)); - count = 0; - num = atoi(argv[2]); - while (count < num) - { - sprintf(qbuf,SELECT_QUERY,count); - if(mysql_query(sock,qbuf)) - { - fprintf(stderr,"Query failed (%s)\n",mysql_error(sock)); - exit(1); - } - if (!(res=mysql_store_result(sock))) - { - fprintf(stderr,"Couldn't get result from query failed\n", - mysql_error(sock)); - exit(1); - } -#ifdef TEST - printf("number of fields: %d\n",mysql_num_fields(res)); -#endif - mysql_free_result(res); - count++; - } - mysql_close(sock); -#else /* HAVE_OPENSSL */ - printf("ssl_test: SSL not configured.\n"); -#endif /* HAVE_OPENSSL */ - exit(0); - return 0; /* Keep some compilers happy */ -} diff --git a/client/thimble.cc b/client/thimble.cc deleted file mode 100644 index 94b75d8fb35..00000000000 --- a/client/thimble.cc +++ /dev/null @@ -1,107 +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 */ - -#include <pthread.h> -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include "my_my_global.h" - -static void spawn_stern_thread(pthread_t *t); -static int act_goofy(void); -static void *be_stern(void *); - -static struct { - pthread_mutex_t lock; - pthread_cond_t cond; - int msg; -} comm = { PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, 0 }; - - -int -main(void) -{ - pthread_t t; - spawn_stern_thread(&t); - - while (act_goofy() != 0) - /* do nothing */; - - pthread_exit(NULL); - - /* notreached */ - return EXIT_SUCCESS; -} - -static void spawn_stern_thread(pthread_t *t) -{ - pthread_attr_t attr; - - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - - if (pthread_create(t, &attr, be_stern, NULL) != 0) - exit(EXIT_FAILURE); - - pthread_attr_destroy(&attr); -} - -static int act_goofy(void) -{ - int ret; - char buf[30]; - - fputs("Are you ready to act goofy (Y/n)? ", stdout); fflush(stdout); - fgets(buf, sizeof(buf), stdin); - pthread_mutex_lock(&comm.lock); - if (buf[0] == 'y' || buf[0] == '\n') { - fputs("** Waawlwalkwwwaa!!\n", stdout); fflush(stdout); - ++comm.msg; - ret = 1; - } - else { - fputs("OK, I hate you. Let me go.\n", stdout); fflush(stdout); - comm.msg = -1; - ret = 0; - } - pthread_mutex_unlock(&comm.lock); - pthread_cond_signal(&comm.cond); - - return ret; -} - -static void *be_stern(void *v __attribute__((unused))) -{ - int msg; - for (;;) { - pthread_mutex_lock(&comm.lock); - while (comm.msg == 0) - pthread_cond_wait(&comm.cond, &comm.lock); - msg = comm.msg; - comm.msg = 0; - pthread_mutex_unlock(&comm.lock); - - if (msg < 0) - break; /* the goofy one learned a lesson! */ - - fputs("I HAVE TO BE STERN WITH YOU!\n", stderr); - fprintf(stderr, "I should give you %d lashes.\n", msg); - sleep(msg); - } - - fputs("You are NOTHING!\n", stderr); - return NULL; -} diff --git a/client/thread_test.c b/client/thread_test.c deleted file mode 100644 index 66ca45bf1cf..00000000000 --- a/client/thread_test.c +++ /dev/null @@ -1,251 +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 */ - -#include <my_global.h> - -#ifndef THREAD - -int main(int argc __attribute__((unused)), char **argv __attribute__((unused))) -{ - printf("This test must be compiled with multithread support to work\n"); - exit(1); -} -#else - -#include <my_sys.h> -#include <my_pthread.h> -#include "mysql.h" -#include <my_getopt.h> - -static my_bool version, verbose, tty_password= 0; -static uint thread_count,number_of_tests=1000,number_of_threads=2; -static pthread_cond_t COND_thread_count; -static pthread_mutex_t LOCK_thread_count; - -static char *database,*host,*user,*password,*unix_socket,*query; -uint tcp_port; - -#ifndef __WIN__ -void *test_thread(void *arg) -#else -unsigned __stdcall test_thread(void *arg) -#endif -{ - MYSQL *mysql; - uint count; - - mysql=mysql_init(NULL); - if (!mysql_real_connect(mysql,host,user,password,database,tcp_port, - unix_socket,0)) - { - fprintf(stderr,"Couldn't connect to engine!\n%s\n\n",mysql_error(mysql)); - perror(""); - goto end; - } - if (verbose) { putchar('*'); fflush(stdout); } - for (count=0 ; count < number_of_tests ; count++) - { - MYSQL_RES *res; - if (mysql_query(mysql,query)) - { - fprintf(stderr,"Query failed (%s)\n",mysql_error(mysql)); - goto end; - } - if (!(res=mysql_store_result(mysql))) - { - fprintf(stderr,"Couldn't get result from %s\n", mysql_error(mysql)); - goto end; - } - mysql_free_result(res); - if (verbose) { putchar('.'); fflush(stdout); } - } -end: - if (verbose) { putchar('#'); fflush(stdout); } - mysql_close(mysql); - pthread_mutex_lock(&LOCK_thread_count); - thread_count--; - VOID(pthread_cond_signal(&COND_thread_count)); /* Tell main we are ready */ - pthread_mutex_unlock(&LOCK_thread_count); - pthread_exit(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}, - {"database", 'D', "Database to use", (gptr*) &database, (gptr*) &database, - 0, GET_STR_ALLOC, 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}, - {"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}, - {"user", 'u', "User for login if not current user", (gptr*) &user, - (gptr*) &user, 0, GET_STR_ALLOC, REQUIRED_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}, - {"verbose", 'v', "Write some progress indicators", (gptr*) &verbose, - (gptr*) &verbose, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"query", 'Q', "Query to execute in each threads", (gptr*) &query, - (gptr*) &query, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"port", 'P', "Port number to use for connection", (gptr*) &tcp_port, - (gptr*) &tcp_port, 0, GET_UINT, REQUIRED_ARG, MYSQL_PORT, 0, 0, 0, 0, 0}, - {"socket", 'S', "Socket file to use for connection", (gptr*) &unix_socket, - (gptr*) &unix_socket, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"test-count", 'c', "Run test count times (default %d)", - (gptr*) &number_of_tests, (gptr*) &number_of_tests, 0, GET_UINT, - REQUIRED_ARG, 1000, 0, 0, 0, 0, 0}, - {"thread-count", 't', "Number of threads to start", - (gptr*) &number_of_threads, (gptr*) &number_of_threads, 0, GET_UINT, - REQUIRED_ARG, 2, 0, 0, 0, 0, 0}, - { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} -}; - - -static const char *load_default_groups[]= { "client",0 }; - -static void usage() -{ - printf("Connection to a mysql server with multiple threads\n"); - if (version) - return; - puts("This software comes with ABSOLUTELY NO WARRANTY.\n"); - printf("Usage: %s [OPTIONS] [database]\n", my_progname); - - my_print_help(my_long_options); - print_defaults("my",load_default_groups); - my_print_variables(my_long_options); - printf("\nExample usage:\n\n\ -%s -Q 'select * from mysql.user' -c %d -t %d\n", - my_progname, number_of_tests, number_of_threads); -} - - -static my_bool -get_one_option(int optid, const struct my_option *opt __attribute__((unused)), - char *argument) -{ - switch (optid) { - case 'p': - if (argument) - { - my_free(password, MYF(MY_ALLOW_ZERO_PTR)); - password= my_strdup(argument, MYF(MY_FAE)); - while (*argument) *argument++= 'x'; /* Destroy argument */ - } - else - tty_password= 1; - break; - case 'V': - version= 1; - usage(); - exit(0); - break; - case '?': - case 'I': /* Info */ - usage(); - exit(1); - break; - } - return 0; -} - - -static void get_options(int argc, char **argv) -{ - int ho_error; - - load_defaults("my",load_default_groups,&argc,&argv); - - if ((ho_error=handle_options(&argc, &argv, my_long_options, get_one_option))) - exit(ho_error); - - free_defaults(argv); - if (tty_password) - password=get_tty_password(NullS); - return; -} - - -int main(int argc, char **argv) -{ - pthread_t tid; - pthread_attr_t thr_attr; - int i,error; - MY_INIT(argv[0]); - get_options(argc,argv); - - if ((error=pthread_cond_init(&COND_thread_count,NULL))) - { - fprintf(stderr,"Got error: %d from pthread_cond_init (errno: %d)", - error,errno); - exit(1); - } - pthread_mutex_init(&LOCK_thread_count,MY_MUTEX_INIT_FAST); - - if ((error=pthread_attr_init(&thr_attr))) - { - fprintf(stderr,"Got error: %d from pthread_attr_init (errno: %d)", - error,errno); - exit(1); - } - if ((error=pthread_attr_setdetachstate(&thr_attr,PTHREAD_CREATE_DETACHED))) - { - fprintf(stderr, - "Got error: %d from pthread_attr_setdetachstate (errno: %d)", - error,errno); - exit(1); - } - - printf("Init ok. Creating %d threads\n",number_of_threads); - for (i=1 ; i <= number_of_threads ; i++) - { - int *param= &i; - - if (verbose) { putchar('+'); fflush(stdout); } - pthread_mutex_lock(&LOCK_thread_count); - if ((error=pthread_create(&tid,&thr_attr,test_thread,(void*) param))) - { - fprintf(stderr,"\nGot error: %d from pthread_create (errno: %d) when creating thread: %i\n", - error,errno,i); - pthread_mutex_unlock(&LOCK_thread_count); - exit(1); - } - thread_count++; - pthread_mutex_unlock(&LOCK_thread_count); - } - - printf("Waiting for threads to finnish\n"); - error=pthread_mutex_lock(&LOCK_thread_count); - while (thread_count) - { - if ((error=pthread_cond_wait(&COND_thread_count,&LOCK_thread_count))) - fprintf(stderr,"\nGot error: %d from pthread_cond_wait\n",error); - } - pthread_mutex_unlock(&LOCK_thread_count); - pthread_attr_destroy(&thr_attr); - printf("\nend\n"); - - my_end(0); - return 0; - - exit(0); - return 0; /* Keep some compilers happy */ -} - -#endif /* THREAD */ |