00001 /* Copyright (C) 2000-2003 MySQL AB 00002 00003 This program is free software; you can redistribute it and/or modify 00004 it under the terms of the GNU General Public License as published by 00005 the Free Software Foundation; either version 2 of the License, or 00006 (at your option) any later version. 00007 00008 This program is distributed in the hope that it will be useful, 00009 but WITHOUT ANY WARRANTY; without even the implied warranty of 00010 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00011 GNU General Public License for more details. 00012 00013 You should have received a copy of the GNU General Public License 00014 along with this program; if not, write to the Free Software 00015 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ 00016 00017 /* mysql command tool 00018 * Commands compatible with mSQL by David J. Hughes 00019 * 00020 * Written by: 00021 * Michael 'Monty' Widenius 00022 * Andi Gutmans <andi@zend.com> 00023 * Zeev Suraski <zeev@zend.com> 00024 * Jani Tolonen <jani@mysql.com> 00025 * Matt Wagner <matt@mysql.com> 00026 * Jeremy Cole <jcole@mysql.com> 00027 * Tonu Samuel <tonu@mysql.com> 00028 * Harrison Fisk <harrison@mysql.com> 00029 * 00030 **/ 00031 00032 #include "client_priv.h" 00033 #include <m_ctype.h> 00034 #include <stdarg.h> 00035 #include <my_dir.h> 00036 #ifndef __GNU_LIBRARY__ 00037 #define __GNU_LIBRARY__ // Skip warnings in getopt.h 00038 #endif 00039 #include "my_readline.h" 00040 #include <signal.h> 00041 #include <violite.h> 00042 00043 #if defined(USE_LIBEDIT_INTERFACE) && defined(HAVE_LOCALE_H) 00044 #include <locale.h> 00045 #endif 00046 00047 const char *VER= "14.12"; 00048 00049 /* Don't try to make a nice table if the data is too big */ 00050 #define MAX_COLUMN_LENGTH 1024 00051 00052 gptr sql_alloc(unsigned size); // Don't use mysqld alloc for these 00053 void sql_element_free(void *ptr); 00054 #include "sql_string.h" 00055 00056 extern "C" { 00057 #if defined(HAVE_CURSES_H) && defined(HAVE_TERM_H) 00058 #include <curses.h> 00059 #include <term.h> 00060 #else 00061 #if defined(HAVE_TERMIOS_H) 00062 #include <termios.h> 00063 #include <unistd.h> 00064 #elif defined(HAVE_TERMBITS_H) 00065 #include <termbits.h> 00066 #elif defined(HAVE_ASM_TERMBITS_H) && (!defined __GLIBC__ || !(__GLIBC__ > 2 || __GLIBC__ == 2 && __GLIBC_MINOR__ > 0)) 00067 #include <asm/termbits.h> // Standard linux 00068 #endif 00069 #undef VOID 00070 #if defined(HAVE_TERMCAP_H) 00071 #include <termcap.h> 00072 #else 00073 #ifdef HAVE_CURSES_H 00074 #include <curses.h> 00075 #endif 00076 #undef SYSV // hack to avoid syntax error 00077 #ifdef HAVE_TERM_H 00078 #include <term.h> 00079 #endif 00080 #endif 00081 #endif 00082 00083 #undef bcmp // Fix problem with new readline 00084 #if defined( __WIN__) 00085 #include <conio.h> 00086 #elif !defined(__NETWARE__) 00087 #include <readline/readline.h> 00088 #define HAVE_READLINE 00089 #endif 00090 //int vidattr(long unsigned int attrs); // Was missing in sun curses 00091 } 00092 00093 #if !defined(HAVE_VIDATTR) 00094 #undef vidattr 00095 #define vidattr(A) {} // Can't get this to work 00096 #endif 00097 00098 #ifdef FN_NO_CASE_SENCE 00099 #define cmp_database(cs,A,B) my_strcasecmp((cs), (A), (B)) 00100 #else 00101 #define cmp_database(cs,A,B) strcmp((A),(B)) 00102 #endif 00103 00104 #if !defined( __WIN__) && !defined(__NETWARE__) && !defined(THREAD) 00105 #define USE_POPEN 00106 #endif 00107 00108 #include "completion_hash.h" 00109 00110 #define PROMPT_CHAR '\\' 00111 #define DEFAULT_DELIMITER ";" 00112 00113 typedef struct st_status 00114 { 00115 int exit_status; 00116 ulong query_start_line; 00117 char *file_name; 00118 LINE_BUFFER *line_buff; 00119 bool batch,add_to_history; 00120 } STATUS; 00121 00122 00123 static HashTable ht; 00124 static char **defaults_argv; 00125 00126 enum enum_info_type { INFO_INFO,INFO_ERROR,INFO_RESULT}; 00127 typedef enum enum_info_type INFO_TYPE; 00128 00129 static MYSQL mysql; /* The connection */ 00130 static my_bool info_flag=0,ignore_errors=0,wait_flag=0,quick=0, 00131 connected=0,opt_raw_data=0,unbuffered=0,output_tables=0, 00132 rehash=1,skip_updates=0,safe_updates=0,one_database=0, 00133 opt_compress=0, using_opt_local_infile=0, 00134 vertical=0, line_numbers=1, column_names=1,opt_html=0, 00135 opt_xml=0,opt_nopager=1, opt_outfile=0, named_cmds= 0, 00136 tty_password= 0, opt_nobeep=0, opt_reconnect=1, 00137 default_charset_used= 0, opt_secure_auth= 0, 00138 default_pager_set= 0, opt_sigint_ignore= 0, 00139 show_warnings= 0, executing_query= 0, interrupted_query= 0; 00140 static ulong opt_max_allowed_packet, opt_net_buffer_length; 00141 static uint verbose=0,opt_silent=0,opt_mysql_port=0, opt_local_infile=0; 00142 static my_string opt_mysql_unix_port=0; 00143 static int connect_flag=CLIENT_INTERACTIVE; 00144 static char *current_host,*current_db,*current_user=0,*opt_password=0, 00145 *current_prompt=0, *delimiter_str= 0, 00146 *default_charset= (char*) MYSQL_DEFAULT_CHARSET_NAME; 00147 static char *histfile; 00148 static char *histfile_tmp; 00149 static String glob_buffer,old_buffer; 00150 static String processed_prompt; 00151 static char *full_username=0,*part_username=0,*default_prompt=0; 00152 static int wait_time = 5; 00153 static STATUS status; 00154 static ulong select_limit,max_join_size,opt_connect_timeout=0; 00155 static char mysql_charsets_dir[FN_REFLEN+1]; 00156 static const char *xmlmeta[] = { 00157 "&", "&", 00158 "<", "<", 00159 ">", ">", 00160 "\"", """, 00161 0, 0 00162 }; 00163 static const char *day_names[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"}; 00164 static const char *month_names[]={"Jan","Feb","Mar","Apr","May","Jun","Jul", 00165 "Aug","Sep","Oct","Nov","Dec"}; 00166 static char default_pager[FN_REFLEN]; 00167 static char pager[FN_REFLEN], outfile[FN_REFLEN]; 00168 static FILE *PAGER, *OUTFILE; 00169 static MEM_ROOT hash_mem_root; 00170 static uint prompt_counter; 00171 static char delimiter[16]= DEFAULT_DELIMITER; 00172 static uint delimiter_length= 1; 00173 00174 #ifdef HAVE_SMEM 00175 static char *shared_memory_base_name=0; 00176 #endif 00177 static uint opt_protocol=0; 00178 static CHARSET_INFO *charset_info= &my_charset_latin1; 00179 00180 #include "sslopt-vars.h" 00181 00182 const char *default_dbug_option="d:t:o,/tmp/mysql.trace"; 00183 00184 void tee_fprintf(FILE *file, const char *fmt, ...); 00185 void tee_fputs(const char *s, FILE *file); 00186 void tee_puts(const char *s, FILE *file); 00187 void tee_putc(int c, FILE *file); 00188 static void tee_print_sized_data(const char *, unsigned int, unsigned int, bool); 00189 /* The names of functions that actually do the manipulation. */ 00190 static int get_options(int argc,char **argv); 00191 static int com_quit(String *str,char*), 00192 com_go(String *str,char*), com_ego(String *str,char*), 00193 com_print(String *str,char*), 00194 com_help(String *str,char*), com_clear(String *str,char*), 00195 com_connect(String *str,char*), com_status(String *str,char*), 00196 com_use(String *str,char*), com_source(String *str, char*), 00197 com_rehash(String *str, char*), com_tee(String *str, char*), 00198 com_notee(String *str, char*), com_charset(String *str,char*), 00199 com_prompt(String *str, char*), com_delimiter(String *str, char*), 00200 com_warnings(String *str, char*), com_nowarnings(String *str, char*); 00201 00202 #ifdef USE_POPEN 00203 static int com_nopager(String *str, char*), com_pager(String *str, char*), 00204 com_edit(String *str,char*), com_shell(String *str, char *); 00205 #endif 00206 00207 static int read_and_execute(bool interactive); 00208 static int sql_connect(char *host,char *database,char *user,char *password, 00209 uint silent); 00210 static int put_info(const char *str,INFO_TYPE info,uint error=0, 00211 const char *sql_state=0); 00212 static int put_error(MYSQL *mysql); 00213 static void safe_put_field(const char *pos,ulong length); 00214 static void xmlencode_print(const char *src, uint length); 00215 static void init_pager(); 00216 static void end_pager(); 00217 static void init_tee(const char *); 00218 static void end_tee(); 00219 static const char* construct_prompt(); 00220 static char *get_arg(char *line, my_bool get_next_arg); 00221 static void init_username(); 00222 static void add_int_to_prompt(int toadd); 00223 00224 /* A structure which contains information on the commands this program 00225 can understand. */ 00226 00227 typedef struct { 00228 const char *name; /* User printable name of the function. */ 00229 char cmd_char; /* msql command character */ 00230 int (*func)(String *str,char *); /* Function to call to do the job. */ 00231 bool takes_params; /* Max parameters for command */ 00232 const char *doc; /* Documentation for this function. */ 00233 } COMMANDS; 00234 00235 static COMMANDS commands[] = { 00236 { "?", '?', com_help, 1, "Synonym for `help'." }, 00237 { "clear", 'c', com_clear, 0, "Clear command."}, 00238 { "connect",'r', com_connect,1, 00239 "Reconnect to the server. Optional arguments are db and host." }, 00240 { "delimiter", 'd', com_delimiter, 1, 00241 "Set statement delimiter. NOTE: Takes the rest of the line as new delimiter." }, 00242 #ifdef USE_POPEN 00243 { "edit", 'e', com_edit, 0, "Edit command with $EDITOR."}, 00244 #endif 00245 { "ego", 'G', com_ego, 0, 00246 "Send command to mysql server, display result vertically."}, 00247 { "exit", 'q', com_quit, 0, "Exit mysql. Same as quit."}, 00248 { "go", 'g', com_go, 0, "Send command to mysql server." }, 00249 { "help", 'h', com_help, 1, "Display this help." }, 00250 #ifdef USE_POPEN 00251 { "nopager",'n', com_nopager,0, "Disable pager, print to stdout." }, 00252 #endif 00253 { "notee", 't', com_notee, 0, "Don't write into outfile." }, 00254 #ifdef USE_POPEN 00255 { "pager", 'P', com_pager, 1, 00256 "Set PAGER [to_pager]. Print the query results via PAGER." }, 00257 #endif 00258 { "print", 'p', com_print, 0, "Print current command." }, 00259 { "prompt", 'R', com_prompt, 1, "Change your mysql prompt."}, 00260 { "quit", 'q', com_quit, 0, "Quit mysql." }, 00261 { "rehash", '#', com_rehash, 0, "Rebuild completion hash." }, 00262 { "source", '.', com_source, 1, 00263 "Execute an SQL script file. Takes a file name as an argument."}, 00264 { "status", 's', com_status, 0, "Get status information from the server."}, 00265 #ifdef USE_POPEN 00266 { "system", '!', com_shell, 1, "Execute a system shell command."}, 00267 #endif 00268 { "tee", 'T', com_tee, 1, 00269 "Set outfile [to_outfile]. Append everything into given outfile." }, 00270 { "use", 'u', com_use, 1, 00271 "Use another database. Takes database name as argument." }, 00272 { "charset", 'C', com_charset, 1, 00273 "Switch to another charset. Might be needed for processing binlog with multi-byte charsets." }, 00274 { "warnings", 'W', com_warnings, 0, 00275 "Show warnings after every statement." }, 00276 { "nowarning", 'w', com_nowarnings, 0, 00277 "Don't show warnings after every statement." }, 00278 /* Get bash-like expansion for some commands */ 00279 { "create table", 0, 0, 0, ""}, 00280 { "create database", 0, 0, 0, ""}, 00281 { "drop", 0, 0, 0, ""}, 00282 { "select", 0, 0, 0, ""}, 00283 { "insert", 0, 0, 0, ""}, 00284 { "replace", 0, 0, 0, ""}, 00285 { "update", 0, 0, 0, ""}, 00286 { "delete", 0, 0, 0, ""}, 00287 { "explain", 0, 0, 0, ""}, 00288 { "show databases", 0, 0, 0, ""}, 00289 { "show fields from", 0, 0, 0, ""}, 00290 { "show keys from", 0, 0, 0, ""}, 00291 { "show tables", 0, 0, 0, ""}, 00292 { "load data from", 0, 0, 0, ""}, 00293 { "alter table", 0, 0, 0, ""}, 00294 { "set option", 0, 0, 0, ""}, 00295 { "lock tables", 0, 0, 0, ""}, 00296 { "unlock tables", 0, 0, 0, ""}, 00297 { (char *)NULL, 0, 0, 0, ""} 00298 }; 00299 00300 static const char *load_default_groups[]= { "mysql","client",0 }; 00301 static const char *server_default_groups[]= 00302 { "server", "embedded", "mysql_SERVER", 0 }; 00303 00304 #ifdef HAVE_READLINE 00305 /* 00306 HIST_ENTRY is defined for libedit, but not for the real readline 00307 Need to redefine it for real readline to find it 00308 */ 00309 #if !defined(HAVE_HIST_ENTRY) 00310 typedef struct _hist_entry { 00311 const char *line; 00312 const char *data; 00313 } HIST_ENTRY; 00314 #endif 00315 00316 extern "C" int add_history(const char *command); /* From readline directory */ 00317 extern "C" int read_history(const char *command); 00318 extern "C" int write_history(const char *command); 00319 extern "C" HIST_ENTRY *history_get(int num); 00320 extern "C" int history_length; 00321 static int not_in_history(const char *line); 00322 static void initialize_readline (char *name); 00323 static void fix_history(String *final_command); 00324 #endif 00325 00326 static COMMANDS *find_command(char *name,char cmd_name); 00327 static bool add_line(String &buffer,char *line,char *in_string, 00328 bool *ml_comment); 00329 static void remove_cntrl(String &buffer); 00330 static void print_table_data(MYSQL_RES *result); 00331 static void print_table_data_html(MYSQL_RES *result); 00332 static void print_table_data_xml(MYSQL_RES *result); 00333 static void print_tab_data(MYSQL_RES *result); 00334 static void print_table_data_vertically(MYSQL_RES *result); 00335 static void print_warnings(void); 00336 static ulong start_timer(void); 00337 static void end_timer(ulong start_time,char *buff); 00338 static void mysql_end_timer(ulong start_time,char *buff); 00339 static void nice_time(double sec,char *buff,bool part_second); 00340 static sig_handler mysql_end(int sig); 00341 static sig_handler mysql_sigint(int sig); 00342 00343 int main(int argc,char *argv[]) 00344 { 00345 char buff[80]; 00346 char *defaults, *extra_defaults, *group_suffix; 00347 char *emb_argv[4]; 00348 int emb_argc; 00349 00350 /* Get --defaults-xxx args for mysql_server_init() */ 00351 emb_argc= get_defaults_options(argc, argv, &defaults, &extra_defaults, 00352 &group_suffix)+1; 00353 memcpy((char*) emb_argv, (char*) argv, emb_argc * sizeof(*argv)); 00354 emb_argv[emb_argc]= 0; 00355 00356 MY_INIT(argv[0]); 00357 DBUG_ENTER("main"); 00358 DBUG_PROCESS(argv[0]); 00359 00360 delimiter_str= delimiter; 00361 default_prompt = my_strdup(getenv("MYSQL_PS1") ? 00362 getenv("MYSQL_PS1") : 00363 "mysql> ",MYF(MY_WME)); 00364 current_prompt = my_strdup(default_prompt,MYF(MY_WME)); 00365 prompt_counter=0; 00366 00367 outfile[0]=0; // no (default) outfile 00368 strmov(pager, "stdout"); // the default, if --pager wasn't given 00369 { 00370 char *tmp=getenv("PAGER"); 00371 if (tmp && strlen(tmp)) 00372 { 00373 default_pager_set= 1; 00374 strmov(default_pager, tmp); 00375 } 00376 } 00377 if (!isatty(0) || !isatty(1)) 00378 { 00379 status.batch=1; opt_silent=1; 00380 ignore_errors=0; 00381 } 00382 else 00383 status.add_to_history=1; 00384 status.exit_status=1; 00385 load_defaults("my",load_default_groups,&argc,&argv); 00386 defaults_argv=argv; 00387 if (get_options(argc, (char **) argv)) 00388 { 00389 free_defaults(defaults_argv); 00390 my_end(0); 00391 exit(1); 00392 } 00393 if (status.batch && !status.line_buff && 00394 !(status.line_buff=batch_readline_init(opt_max_allowed_packet+512,stdin))) 00395 { 00396 free_defaults(defaults_argv); 00397 my_end(0); 00398 exit(1); 00399 } 00400 if (mysql_server_init(emb_argc, emb_argv, (char**) server_default_groups)) 00401 { 00402 free_defaults(defaults_argv); 00403 my_end(0); 00404 exit(1); 00405 } 00406 glob_buffer.realloc(512); 00407 completion_hash_init(&ht, 128); 00408 init_alloc_root(&hash_mem_root, 16384, 0); 00409 bzero((char*) &mysql, sizeof(mysql)); 00410 if (sql_connect(current_host,current_db,current_user,opt_password, 00411 opt_silent)) 00412 { 00413 quick=1; // Avoid history 00414 status.exit_status=1; 00415 mysql_end(-1); 00416 } 00417 if (!status.batch) 00418 ignore_errors=1; // Don't abort monitor 00419 00420 if (opt_sigint_ignore) 00421 signal(SIGINT, SIG_IGN); 00422 else 00423 signal(SIGINT, mysql_sigint); // Catch SIGINT to clean up 00424 00425 signal(SIGQUIT, mysql_end); // Catch SIGQUIT to clean up 00426 00427 /* 00428 Run in interactive mode like the ingres/postgres monitor 00429 */ 00430 00431 put_info("Welcome to the MySQL monitor. Commands end with ; or \\g.", 00432 INFO_INFO); 00433 sprintf((char*) glob_buffer.ptr(), 00434 "Your MySQL connection id is %lu to server version: %s\n", 00435 mysql_thread_id(&mysql),mysql_get_server_info(&mysql)); 00436 put_info((char*) glob_buffer.ptr(),INFO_INFO); 00437 00438 #ifdef HAVE_READLINE 00439 initialize_readline((char*) my_progname); 00440 if (!status.batch && !quick && !opt_html && !opt_xml) 00441 { 00442 /* read-history from file, default ~/.mysql_history*/ 00443 if (getenv("MYSQL_HISTFILE")) 00444 histfile=my_strdup(getenv("MYSQL_HISTFILE"),MYF(MY_WME)); 00445 else if (getenv("HOME")) 00446 { 00447 histfile=(char*) my_malloc((uint) strlen(getenv("HOME")) 00448 + (uint) strlen("/.mysql_history")+2, 00449 MYF(MY_WME)); 00450 if (histfile) 00451 sprintf(histfile,"%s/.mysql_history",getenv("HOME")); 00452 char link_name[FN_REFLEN]; 00453 if (my_readlink(link_name, histfile, 0) == 0 && 00454 strncmp(link_name, "/dev/null", 10) == 0) 00455 { 00456 /* The .mysql_history file is a symlink to /dev/null, don't use it */ 00457 my_free(histfile, MYF(MY_ALLOW_ZERO_PTR)); 00458 histfile= 0; 00459 } 00460 } 00461 if (histfile) 00462 { 00463 if (verbose) 00464 tee_fprintf(stdout, "Reading history-file %s\n",histfile); 00465 read_history(histfile); 00466 if (!(histfile_tmp= (char*) my_malloc((uint) strlen(histfile) + 5, 00467 MYF(MY_WME)))) 00468 { 00469 fprintf(stderr, "Couldn't allocate memory for temp histfile!\n"); 00470 exit(1); 00471 } 00472 sprintf(histfile_tmp, "%s.TMP", histfile); 00473 } 00474 } 00475 #endif 00476 sprintf(buff, "%s", 00477 #ifndef NOT_YET 00478 "Type 'help;' or '\\h' for help. Type '\\c' to clear the buffer.\n"); 00479 #else 00480 "Type 'help [[%]function name[%]]' to get help on usage of function.\n"); 00481 #endif 00482 put_info(buff,INFO_INFO); 00483 status.exit_status= read_and_execute(!status.batch); 00484 if (opt_outfile) 00485 end_tee(); 00486 mysql_end(0); 00487 #ifndef _lint 00488 DBUG_RETURN(0); // Keep compiler happy 00489 #endif 00490 } 00491 00492 sig_handler mysql_sigint(int sig) 00493 { 00494 char kill_buffer[40]; 00495 MYSQL *kill_mysql= NULL; 00496 00497 signal(SIGINT, mysql_sigint); 00498 00499 /* terminate if no query being executed, or we already tried interrupting */ 00500 if (!executing_query || interrupted_query++) 00501 mysql_end(sig); 00502 00503 kill_mysql= mysql_init(kill_mysql); 00504 if (!mysql_real_connect(kill_mysql,current_host, current_user, opt_password, 00505 "", opt_mysql_port, opt_mysql_unix_port,0)) 00506 mysql_end(sig); 00507 /* kill_buffer is always big enough because max length of %lu is 15 */ 00508 sprintf(kill_buffer, "KILL /*!50000 QUERY */ %lu", mysql_thread_id(&mysql)); 00509 mysql_real_query(kill_mysql, kill_buffer, strlen(kill_buffer)); 00510 mysql_close(kill_mysql); 00511 tee_fprintf(stdout, "Query aborted by Ctrl+C\n"); 00512 } 00513 00514 sig_handler mysql_end(int sig) 00515 { 00516 mysql_close(&mysql); 00517 #ifdef HAVE_READLINE 00518 if (!status.batch && !quick && !opt_html && !opt_xml && histfile) 00519 { 00520 /* write-history */ 00521 if (verbose) 00522 tee_fprintf(stdout, "Writing history-file %s\n",histfile); 00523 if (!write_history(histfile_tmp)) 00524 my_rename(histfile_tmp, histfile, MYF(MY_WME)); 00525 } 00526 batch_readline_end(status.line_buff); 00527 completion_hash_free(&ht); 00528 free_root(&hash_mem_root,MYF(0)); 00529 00530 #endif 00531 if (sig >= 0) 00532 put_info(sig ? "Aborted" : "Bye", INFO_RESULT); 00533 glob_buffer.free(); 00534 old_buffer.free(); 00535 processed_prompt.free(); 00536 my_free(opt_password,MYF(MY_ALLOW_ZERO_PTR)); 00537 my_free(opt_mysql_unix_port,MYF(MY_ALLOW_ZERO_PTR)); 00538 my_free(histfile,MYF(MY_ALLOW_ZERO_PTR)); 00539 my_free(histfile_tmp,MYF(MY_ALLOW_ZERO_PTR)); 00540 my_free(current_db,MYF(MY_ALLOW_ZERO_PTR)); 00541 my_free(current_host,MYF(MY_ALLOW_ZERO_PTR)); 00542 my_free(current_user,MYF(MY_ALLOW_ZERO_PTR)); 00543 my_free(full_username,MYF(MY_ALLOW_ZERO_PTR)); 00544 my_free(part_username,MYF(MY_ALLOW_ZERO_PTR)); 00545 my_free(default_prompt,MYF(MY_ALLOW_ZERO_PTR)); 00546 #ifdef HAVE_SMEM 00547 my_free(shared_memory_base_name,MYF(MY_ALLOW_ZERO_PTR)); 00548 #endif 00549 my_free(current_prompt,MYF(MY_ALLOW_ZERO_PTR)); 00550 mysql_server_end(); 00551 free_defaults(defaults_argv); 00552 my_end(info_flag ? MY_CHECK_ERROR | MY_GIVE_INFO : 0); 00553 exit(status.exit_status); 00554 } 00555 00556 00557 /* 00558 This function handles sigint calls 00559 If query is in process, kill query 00560 no query in process, terminate like previous behavior 00561 */ 00562 sig_handler handle_sigint(int sig) 00563 { 00564 char kill_buffer[40]; 00565 MYSQL *kill_mysql= NULL; 00566 00567 /* terminate if no query being executed, or we already tried interrupting */ 00568 if (!executing_query || interrupted_query) 00569 mysql_end(sig); 00570 00571 kill_mysql= mysql_init(kill_mysql); 00572 if (!mysql_real_connect(kill_mysql,current_host, current_user, opt_password, 00573 "", opt_mysql_port, opt_mysql_unix_port,0)) 00574 mysql_end(sig); 00575 00576 /* kill_buffer is always big enough because max length of %lu is 15 */ 00577 sprintf(kill_buffer, "KILL /*!50000 QUERY */ %lu", mysql_thread_id(&mysql)); 00578 mysql_real_query(kill_mysql, kill_buffer, strlen(kill_buffer)); 00579 mysql_close(kill_mysql); 00580 tee_fprintf(stdout, "Query aborted by Ctrl+C\n"); 00581 00582 interrupted_query= 1; 00583 } 00584 00585 00586 static struct my_option my_long_options[] = 00587 { 00588 {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 00589 0, 0, 0, 0, 0}, 00590 {"help", 'I', "Synonym for -?", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 00591 0, 0, 0, 0, 0}, 00592 #ifdef __NETWARE__ 00593 {"autoclose", OPT_AUTO_CLOSE, "Auto close the screen on exit for Netware.", 00594 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, 00595 #endif 00596 {"auto-rehash", OPT_AUTO_REHASH, 00597 "Enable automatic rehashing. One doesn't need to use 'rehash' to get table and field completion, but startup and reconnecting may take a longer time. Disable with --disable-auto-rehash.", 00598 (gptr*) &rehash, (gptr*) &rehash, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, 00599 {"no-auto-rehash", 'A', 00600 "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.", 00601 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, 00602 {"batch", 'B', 00603 "Don't use history file. Disable interactive behavior. (Enables --silent)", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, 00604 {"character-sets-dir", OPT_CHARSETS_DIR, 00605 "Directory where character sets are.", (gptr*) &charsets_dir, 00606 (gptr*) &charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, 00607 {"default-character-set", OPT_DEFAULT_CHARSET, 00608 "Set the default character set.", (gptr*) &default_charset, 00609 (gptr*) &default_charset, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, 00610 {"compress", 'C', "Use compression in server/client protocol.", 00611 (gptr*) &opt_compress, (gptr*) &opt_compress, 0, GET_BOOL, NO_ARG, 0, 0, 0, 00612 0, 0, 0}, 00613 #ifdef DBUG_OFF 00614 {"debug", '#', "This is a non-debug version. Catch this and exit", 00615 0,0, 0, GET_DISABLED, OPT_ARG, 0, 0, 0, 0, 0, 0}, 00616 #else 00617 {"debug", '#', "Output debug log", (gptr*) &default_dbug_option, 00618 (gptr*) &default_dbug_option, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, 00619 #endif 00620 {"database", 'D', "Database to use.", (gptr*) ¤t_db, 00621 (gptr*) ¤t_db, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, 00622 {"delimiter", OPT_DELIMITER, "Delimiter to be used.", (gptr*) &delimiter_str, 00623 (gptr*) &delimiter_str, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, 00624 {"execute", 'e', "Execute command and quit. (Disables --force and history file)", 0, 00625 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, 00626 {"vertical", 'E', "Print the output of a query (rows) vertically.", 00627 (gptr*) &vertical, (gptr*) &vertical, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 00628 0}, 00629 {"force", 'f', "Continue even if we get an sql error.", 00630 (gptr*) &ignore_errors, (gptr*) &ignore_errors, 0, GET_BOOL, NO_ARG, 0, 0, 00631 0, 0, 0, 0}, 00632 {"named-commands", 'G', 00633 "Enable named commands. Named commands mean this program's internal commands; see mysql> help . When enabled, the named commands can be used from any line of the query, otherwise only from the first line, before an enter. Disable with --disable-named-commands. This option is disabled by default.", 00634 (gptr*) &named_cmds, (gptr*) &named_cmds, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 00635 0, 0}, 00636 {"no-named-commands", 'g', 00637 "Named commands are disabled. Use \\* form only, or use named commands only in the beginning of a line ending with a semicolon (;) Since version 10.9 the client now starts with this option ENABLED by default! Disable with '-G'. Long format commands still work from the first line. WARNING: option deprecated; use --disable-named-commands instead.", 00638 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, 00639 {"ignore-spaces", 'i', "Ignore space after function names.", 0, 0, 0, 00640 GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, 00641 {"local-infile", OPT_LOCAL_INFILE, "Enable/disable LOAD DATA LOCAL INFILE.", 00642 (gptr*) &opt_local_infile, 00643 (gptr*) &opt_local_infile, 0, GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0}, 00644 {"no-beep", 'b', "Turn off beep on error.", (gptr*) &opt_nobeep, 00645 (gptr*) &opt_nobeep, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, 00646 {"host", 'h', "Connect to host.", (gptr*) ¤t_host, 00647 (gptr*) ¤t_host, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, 00648 {"html", 'H', "Produce HTML output.", (gptr*) &opt_html, (gptr*) &opt_html, 00649 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, 00650 {"xml", 'X', "Produce XML output", (gptr*) &opt_xml, (gptr*) &opt_xml, 0, 00651 GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, 00652 {"line-numbers", OPT_LINE_NUMBERS, "Write line numbers for errors.", 00653 (gptr*) &line_numbers, (gptr*) &line_numbers, 0, GET_BOOL, 00654 NO_ARG, 1, 0, 0, 0, 0, 0}, 00655 {"skip-line-numbers", 'L', "Don't write line number for errors. WARNING: -L is deprecated, use long version of this option instead.", 0, 0, 0, GET_NO_ARG, 00656 NO_ARG, 0, 0, 0, 0, 0, 0}, 00657 {"unbuffered", 'n', "Flush buffer after each query.", (gptr*) &unbuffered, 00658 (gptr*) &unbuffered, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, 00659 {"column-names", OPT_COLUMN_NAMES, "Write column names in results.", 00660 (gptr*) &column_names, (gptr*) &column_names, 0, GET_BOOL, 00661 NO_ARG, 1, 0, 0, 0, 0, 0}, 00662 {"skip-column-names", 'N', 00663 "Don't write column names in results. WARNING: -N is deprecated, use long version of this options instead.", 00664 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, 00665 {"set-variable", 'O', 00666 "Change the value of a variable. Please note that this option is deprecated; you can set variables directly with --variable-name=value.", 00667 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, 00668 {"sigint-ignore", OPT_SIGINT_IGNORE, "Ignore SIGINT (CTRL-C)", 00669 (gptr*) &opt_sigint_ignore, (gptr*) &opt_sigint_ignore, 0, GET_BOOL, 00670 NO_ARG, 0, 0, 0, 0, 0, 0}, 00671 {"one-database", 'o', 00672 "Only update the default database. This is useful for skipping updates to other database in the update log.", 00673 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, 00674 #ifdef USE_POPEN 00675 {"pager", OPT_PAGER, 00676 "Pager to use to display results. If you don't supply an option the default pager is taken from your ENV variable PAGER. Valid pagers are less, more, cat [> filename], etc. See interactive help (\\h) also. This option does not work in batch mode. Disable with --disable-pager. This option is disabled by default.", 00677 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, 00678 {"no-pager", OPT_NOPAGER, 00679 "Disable pager and print to stdout. See interactive help (\\h) also. WARNING: option deprecated; use --disable-pager instead.", 00680 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, 00681 #endif 00682 {"password", 'p', 00683 "Password to use when connecting to server. If password is not given it's asked from the tty.", 00684 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, 00685 #ifdef __WIN__ 00686 {"pipe", 'W', "Use named pipes to connect to server.", 0, 0, 0, GET_NO_ARG, 00687 NO_ARG, 0, 0, 0, 0, 0, 0}, 00688 #endif 00689 {"port", 'P', "Port number to use for connection.", (gptr*) &opt_mysql_port, 00690 (gptr*) &opt_mysql_port, 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 00691 0}, 00692 {"prompt", OPT_PROMPT, "Set the mysql prompt to this value.", 00693 (gptr*) ¤t_prompt, (gptr*) ¤t_prompt, 0, GET_STR_ALLOC, 00694 REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, 00695 {"protocol", OPT_MYSQL_PROTOCOL, "The protocol of connection (tcp,socket,pipe,memory).", 00696 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, 00697 {"quick", 'q', 00698 "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.", 00699 (gptr*) &quick, (gptr*) &quick, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, 00700 {"raw", 'r', "Write fields without conversion. Used with --batch.", 00701 (gptr*) &opt_raw_data, (gptr*) &opt_raw_data, 0, GET_BOOL, NO_ARG, 0, 0, 0, 00702 0, 0, 0}, 00703 {"reconnect", OPT_RECONNECT, "Reconnect if the connection is lost. Disable with --disable-reconnect. This option is enabled by default.", 00704 (gptr*) &opt_reconnect, (gptr*) &opt_reconnect, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, 00705 {"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, 00706 0, 0}, 00707 #ifdef HAVE_SMEM 00708 {"shared-memory-base-name", OPT_SHARED_MEMORY_BASE_NAME, 00709 "Base name of shared memory.", (gptr*) &shared_memory_base_name, (gptr*) &shared_memory_base_name, 00710 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, 00711 #endif 00712 {"socket", 'S', "Socket file to use for connection.", 00713 (gptr*) &opt_mysql_unix_port, (gptr*) &opt_mysql_unix_port, 0, GET_STR_ALLOC, 00714 REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, 00715 #include "sslopt-longopts.h" 00716 {"table", 't', "Output in table format.", (gptr*) &output_tables, 00717 (gptr*) &output_tables, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, 00718 {"debug-info", 'T', "Print some debug info at exit.", (gptr*) &info_flag, 00719 (gptr*) &info_flag, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, 00720 {"tee", OPT_TEE, 00721 "Append everything into outfile. See interactive help (\\h) also. Does not work in batch mode. Disable with --disable-tee. This option is disabled by default.", 00722 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, 00723 {"no-tee", OPT_NOTEE, "Disable outfile. See interactive help (\\h) also. WARNING: option deprecated; use --disable-tee instead", 0, 0, 0, GET_NO_ARG, 00724 NO_ARG, 0, 0, 0, 0, 0, 0}, 00725 #ifndef DONT_ALLOW_USER_CHANGE 00726 {"user", 'u', "User for login if not current user.", (gptr*) ¤t_user, 00727 (gptr*) ¤t_user, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, 00728 #endif 00729 {"safe-updates", 'U', "Only allow UPDATE and DELETE that uses keys.", 00730 (gptr*) &safe_updates, (gptr*) &safe_updates, 0, GET_BOOL, NO_ARG, 0, 0, 00731 0, 0, 0, 0}, 00732 {"i-am-a-dummy", 'U', "Synonym for option --safe-updates, -U.", 00733 (gptr*) &safe_updates, (gptr*) &safe_updates, 0, GET_BOOL, NO_ARG, 0, 0, 00734 0, 0, 0, 0}, 00735 {"verbose", 'v', "Write more. (-v -v -v gives the table output format).", 0, 00736 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, 00737 {"version", 'V', "Output version information and exit.", 0, 0, 0, 00738 GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, 00739 {"wait", 'w', "Wait and retry if connection is down.", 0, 0, 0, GET_NO_ARG, 00740 NO_ARG, 0, 0, 0, 0, 0, 0}, 00741 {"connect_timeout", OPT_CONNECT_TIMEOUT, 00742 "Number of seconds before connection timeout.", 00743 (gptr*) &opt_connect_timeout, 00744 (gptr*) &opt_connect_timeout, 0, GET_ULONG, REQUIRED_ARG, 0, 0, 3600*12, 0, 00745 0, 1}, 00746 {"max_allowed_packet", OPT_MAX_ALLOWED_PACKET, 00747 "Max packet length to send to, or receive from server", 00748 (gptr*) &opt_max_allowed_packet, (gptr*) &opt_max_allowed_packet, 0, GET_ULONG, 00749 REQUIRED_ARG, 16 *1024L*1024L, 4096, (longlong) 2*1024L*1024L*1024L, 00750 MALLOC_OVERHEAD, 1024, 0}, 00751 {"net_buffer_length", OPT_NET_BUFFER_LENGTH, 00752 "Buffer for TCP/IP and socket communication", 00753 (gptr*) &opt_net_buffer_length, (gptr*) &opt_net_buffer_length, 0, GET_ULONG, 00754 REQUIRED_ARG, 16384, 1024, 512*1024*1024L, MALLOC_OVERHEAD, 1024, 0}, 00755 {"select_limit", OPT_SELECT_LIMIT, 00756 "Automatic limit for SELECT when using --safe-updates", 00757 (gptr*) &select_limit, 00758 (gptr*) &select_limit, 0, GET_ULONG, REQUIRED_ARG, 1000L, 1, ~0L, 0, 1, 0}, 00759 {"max_join_size", OPT_MAX_JOIN_SIZE, 00760 "Automatic limit for rows in a join when using --safe-updates", 00761 (gptr*) &max_join_size, 00762 (gptr*) &max_join_size, 0, GET_ULONG, REQUIRED_ARG, 1000000L, 1, ~0L, 0, 1, 00763 0}, 00764 {"secure-auth", OPT_SECURE_AUTH, "Refuse client connecting to server if it" 00765 " uses old (pre-4.1.1) protocol", (gptr*) &opt_secure_auth, 00766 (gptr*) &opt_secure_auth, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, 00767 {"show-warnings", OPT_SHOW_WARNINGS, "Show warnings after every statement.", 00768 (gptr*) &show_warnings, (gptr*) &show_warnings, 0, GET_BOOL, NO_ARG, 00769 0, 0, 0, 0, 0, 0}, 00770 { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} 00771 }; 00772 00773 00774 static void usage(int version) 00775 { 00776 /* Divert all help information on NetWare to logger screen. */ 00777 #ifdef __NETWARE__ 00778 #define printf consoleprintf 00779 #endif 00780 00781 #if defined(USE_LIBEDIT_INTERFACE) 00782 const char* readline= ""; 00783 #else 00784 const char* readline= "readline"; 00785 #endif 00786 00787 #ifdef HAVE_READLINE 00788 printf("%s Ver %s Distrib %s, for %s (%s) using %s %s\n", 00789 my_progname, VER, MYSQL_SERVER_VERSION, SYSTEM_TYPE, MACHINE_TYPE, 00790 readline, rl_library_version); 00791 #else 00792 printf("%s Ver %s Distrib %s, for %s (%s)\n", my_progname, VER, 00793 MYSQL_SERVER_VERSION, SYSTEM_TYPE, MACHINE_TYPE); 00794 #endif 00795 00796 if (version) 00797 return; 00798 printf("\ 00799 Copyright (C) 2002 MySQL AB\n\ 00800 This software comes with ABSOLUTELY NO WARRANTY. This is free software,\n\ 00801 and you are welcome to modify and redistribute it under the GPL license\n"); 00802 printf("Usage: %s [OPTIONS] [database]\n", my_progname); 00803 my_print_help(my_long_options); 00804 print_defaults("my", load_default_groups); 00805 my_print_variables(my_long_options); 00806 NETWARE_SET_SCREEN_MODE(1); 00807 #ifdef __NETWARE__ 00808 #undef printf 00809 #endif 00810 } 00811 00812 00813 static my_bool 00814 get_one_option(int optid, const struct my_option *opt __attribute__((unused)), 00815 char *argument) 00816 { 00817 switch(optid) { 00818 #ifdef __NETWARE__ 00819 case OPT_AUTO_CLOSE: 00820 setscreenmode(SCR_AUTOCLOSE_ON_EXIT); 00821 break; 00822 #endif 00823 case OPT_CHARSETS_DIR: 00824 strmov(mysql_charsets_dir, argument); 00825 charsets_dir = mysql_charsets_dir; 00826 break; 00827 case OPT_DEFAULT_CHARSET: 00828 default_charset_used= 1; 00829 break; 00830 case OPT_DELIMITER: 00831 if (argument == disabled_my_option) 00832 strmov(delimiter, DEFAULT_DELIMITER); 00833 else 00834 strmake(delimiter, argument, sizeof(delimiter) - 1); 00835 delimiter_length= (uint)strlen(delimiter); 00836 delimiter_str= delimiter; 00837 break; 00838 case OPT_LOCAL_INFILE: 00839 using_opt_local_infile=1; 00840 break; 00841 case OPT_TEE: 00842 if (argument == disabled_my_option) 00843 { 00844 if (opt_outfile) 00845 end_tee(); 00846 } 00847 else 00848 init_tee(argument); 00849 break; 00850 case OPT_NOTEE: 00851 printf("WARNING: option deprecated; use --disable-tee instead.\n"); 00852 if (opt_outfile) 00853 end_tee(); 00854 break; 00855 case OPT_PAGER: 00856 if (argument == disabled_my_option) 00857 opt_nopager= 1; 00858 else 00859 { 00860 opt_nopager= 0; 00861 if (argument && strlen(argument)) 00862 { 00863 default_pager_set= 1; 00864 strmov(pager, argument); 00865 strmov(default_pager, pager); 00866 } 00867 else if (default_pager_set) 00868 strmov(pager, default_pager); 00869 else 00870 opt_nopager= 1; 00871 } 00872 break; 00873 case OPT_NOPAGER: 00874 printf("WARNING: option deprecated; use --disable-pager instead.\n"); 00875 opt_nopager= 1; 00876 break; 00877 case OPT_MYSQL_PROTOCOL: 00878 { 00879 if ((opt_protocol= find_type(argument, &sql_protocol_typelib,0)) <= 0) 00880 { 00881 fprintf(stderr, "Unknown option to protocol: %s\n", argument); 00882 exit(1); 00883 } 00884 break; 00885 } 00886 break; 00887 case 'A': 00888 rehash= 0; 00889 break; 00890 case 'N': 00891 column_names= 0; 00892 break; 00893 case 'e': 00894 status.batch= 1; 00895 status.add_to_history= 0; 00896 if (!status.line_buff) 00897 ignore_errors= 0; // do it for the first -e only 00898 if (!(status.line_buff= batch_readline_command(status.line_buff, argument))) 00899 return 1; 00900 break; 00901 case 'o': 00902 if (argument == disabled_my_option) 00903 one_database= 0; 00904 else 00905 one_database= skip_updates= 1; 00906 break; 00907 case 'p': 00908 if (argument == disabled_my_option) 00909 argument= (char*) ""; // Don't require password 00910 if (argument) 00911 { 00912 char *start= argument; 00913 my_free(opt_password, MYF(MY_ALLOW_ZERO_PTR)); 00914 opt_password= my_strdup(argument, MYF(MY_FAE)); 00915 while (*argument) *argument++= 'x'; // Destroy argument 00916 if (*start) 00917 start[1]=0 ; 00918 tty_password= 0; 00919 } 00920 else 00921 tty_password= 1; 00922 break; 00923 case '#': 00924 DBUG_PUSH(argument ? argument : default_dbug_option); 00925 info_flag= 1; 00926 break; 00927 case 's': 00928 if (argument == disabled_my_option) 00929 opt_silent= 0; 00930 else 00931 opt_silent++; 00932 break; 00933 case 'v': 00934 if (argument == disabled_my_option) 00935 verbose= 0; 00936 else 00937 verbose++; 00938 break; 00939 case 'B': 00940 status.batch= 1; 00941 status.add_to_history= 0; 00942 set_if_bigger(opt_silent,1); // more silent 00943 break; 00944 case 'W': 00945 #ifdef __WIN__ 00946 opt_protocol = MYSQL_PROTOCOL_PIPE; 00947 #endif 00948 break; 00949 #include <sslopt-case.h> 00950 case 'V': 00951 usage(1); 00952 exit(0); 00953 case 'I': 00954 case '?': 00955 usage(0); 00956 exit(0); 00957 } 00958 return 0; 00959 } 00960 00961 00962 static int get_options(int argc, char **argv) 00963 { 00964 char *tmp, *pagpoint; 00965 int ho_error; 00966 MYSQL_PARAMETERS *mysql_params= mysql_get_parameters(); 00967 00968 tmp= (char *) getenv("MYSQL_HOST"); 00969 if (tmp) 00970 current_host= my_strdup(tmp, MYF(MY_WME)); 00971 00972 pagpoint= getenv("PAGER"); 00973 if (!((char*) (pagpoint))) 00974 { 00975 strmov(pager, "stdout"); 00976 opt_nopager= 1; 00977 } 00978 else 00979 strmov(pager, pagpoint); 00980 strmov(default_pager, pager); 00981 00982 opt_max_allowed_packet= *mysql_params->p_max_allowed_packet; 00983 opt_net_buffer_length= *mysql_params->p_net_buffer_length; 00984 00985 if ((ho_error=handle_options(&argc, &argv, my_long_options, get_one_option))) 00986 exit(ho_error); 00987 00988 *mysql_params->p_max_allowed_packet= opt_max_allowed_packet; 00989 *mysql_params->p_net_buffer_length= opt_net_buffer_length; 00990 00991 if (status.batch) /* disable pager and outfile in this case */ 00992 { 00993 strmov(default_pager, "stdout"); 00994 strmov(pager, "stdout"); 00995 opt_nopager= 1; 00996 default_pager_set= 0; 00997 opt_outfile= 0; 00998 opt_reconnect= 0; 00999 connect_flag= 0; /* Not in interactive mode */ 01000 } 01001 01002 if (strcmp(default_charset, charset_info->csname) && 01003 !(charset_info= get_charset_by_csname(default_charset, 01004 MY_CS_PRIMARY, MYF(MY_WME)))) 01005 exit(1); 01006 if (argc > 1) 01007 { 01008 usage(0); 01009 exit(1); 01010 } 01011 if (argc == 1) 01012 { 01013 skip_updates= 0; 01014 my_free(current_db, MYF(MY_ALLOW_ZERO_PTR)); 01015 current_db= my_strdup(*argv, MYF(MY_WME)); 01016 } 01017 if (tty_password) 01018 opt_password= get_tty_password(NullS); 01019 return(0); 01020 } 01021 01022 static int read_and_execute(bool interactive) 01023 { 01024 #if defined(__NETWARE__) 01025 char linebuffer[254]; 01026 String buffer; 01027 #endif 01028 #if defined(__WIN__) 01029 String tmpbuf; 01030 String buffer; 01031 #endif 01032 01033 char *line; 01034 char in_string=0; 01035 ulong line_number=0; 01036 bool ml_comment= 0; 01037 COMMANDS *com; 01038 status.exit_status=1; 01039 01040 for (;;) 01041 { 01042 if (!interactive) 01043 { 01044 line=batch_readline(status.line_buff); 01045 line_number++; 01046 if (!glob_buffer.length()) 01047 status.query_start_line=line_number; 01048 } 01049 else 01050 { 01051 char *prompt= (char*) (ml_comment ? " /*> " : 01052 glob_buffer.is_empty() ? construct_prompt() : 01053 !in_string ? " -> " : 01054 in_string == '\'' ? 01055 " '> " : (in_string == '`' ? 01056 " `> " : 01057 " \"> ")); 01058 if (opt_outfile && glob_buffer.is_empty()) 01059 fflush(OUTFILE); 01060 01061 interrupted_query= 0; 01062 01063 #if defined( __WIN__) || defined(__NETWARE__) 01064 tee_fputs(prompt, stdout); 01065 #if defined(__NETWARE__) 01066 line=fgets(linebuffer, sizeof(linebuffer)-1, stdin); 01067 /* Remove the '\n' */ 01068 if (line) 01069 { 01070 char *p = strrchr(line, '\n'); 01071 if (p != NULL) 01072 *p = '\0'; 01073 } 01074 #else defined(__WIN__) 01075 if (!tmpbuf.is_alloced()) 01076 tmpbuf.alloc(65535); 01077 tmpbuf.length(0); 01078 buffer.length(0); 01079 unsigned long clen; 01080 do 01081 { 01082 line= my_cgets((char*)tmpbuf.ptr(), tmpbuf.alloced_length()-1, &clen); 01083 buffer.append(line, clen); 01084 /* 01085 if we got buffer fully filled than there is a chance that 01086 something else is still in console input buffer 01087 */ 01088 } while (tmpbuf.alloced_length() <= clen); 01089 line= buffer.c_ptr(); 01090 #endif /* __NETWARE__ */ 01091 #else 01092 if (opt_outfile) 01093 fputs(prompt, OUTFILE); 01094 line= readline(prompt); 01095 #endif /* defined( __WIN__) || defined(__NETWARE__) */ 01096 01097 /* 01098 When Ctrl+d or Ctrl+z is pressed, the line may be NULL on some OS 01099 which may cause coredump. 01100 */ 01101 if (opt_outfile && line) 01102 fprintf(OUTFILE, "%s\n", line); 01103 } 01104 if (!line) // End of file 01105 { 01106 status.exit_status=0; 01107 break; 01108 } 01109 if (!in_string && (line[0] == '#' || 01110 (line[0] == '-' && line[1] == '-') || 01111 line[0] == 0)) 01112 continue; // Skip comment lines 01113 01114 /* 01115 Check if line is a mysql command line 01116 (We want to allow help, print and clear anywhere at line start 01117 */ 01118 if ((named_cmds || glob_buffer.is_empty()) 01119 && !ml_comment && !in_string && (com=find_command(line,0))) 01120 { 01121 if ((*com->func)(&glob_buffer,line) > 0) 01122 break; 01123 if (glob_buffer.is_empty()) // If buffer was emptied 01124 in_string=0; 01125 #ifdef HAVE_READLINE 01126 if (interactive && status.add_to_history && not_in_history(line)) 01127 add_history(line); 01128 #endif 01129 continue; 01130 } 01131 if (add_line(glob_buffer,line,&in_string,&ml_comment)) 01132 break; 01133 } 01134 /* if in batch mode, send last query even if it doesn't end with \g or go */ 01135 01136 if (!interactive && !status.exit_status) 01137 { 01138 remove_cntrl(glob_buffer); 01139 if (!glob_buffer.is_empty()) 01140 { 01141 status.exit_status=1; 01142 if (com_go(&glob_buffer,line) <= 0) 01143 status.exit_status=0; 01144 } 01145 } 01146 01147 #if defined( __WIN__) || defined(__NETWARE__) 01148 buffer.free(); 01149 #endif 01150 #if defined( __WIN__) 01151 tmpbuf.free(); 01152 #endif 01153 01154 return status.exit_status; 01155 } 01156 01157 01158 static COMMANDS *find_command(char *name,char cmd_char) 01159 { 01160 uint len; 01161 char *end; 01162 DBUG_ENTER("find_command"); 01163 DBUG_PRINT("enter",("name: '%s' char: %d", name ? name : "NULL", cmd_char)); 01164 01165 if (!name) 01166 { 01167 len=0; 01168 end=0; 01169 } 01170 else 01171 { 01172 while (my_isspace(charset_info,*name)) 01173 name++; 01174 /* 01175 If there is an \\g in the row or if the row has a delimiter but 01176 this is not a delimiter command, let add_line() take care of 01177 parsing the row and calling find_command() 01178 */ 01179 if (strstr(name, "\\g") || (strstr(name, delimiter) && 01180 !(strlen(name) >= 9 && 01181 !my_strnncoll(charset_info, 01182 (uchar*) name, 9, 01183 (const uchar*) "delimiter", 01184 9)))) 01185 DBUG_RETURN((COMMANDS *) 0); 01186 if ((end=strcont(name," \t"))) 01187 { 01188 len=(uint) (end - name); 01189 while (my_isspace(charset_info,*end)) 01190 end++; 01191 if (!*end) 01192 end=0; // no arguments to function 01193 } 01194 else 01195 len=(uint) strlen(name); 01196 } 01197 01198 for (uint i= 0; commands[i].name; i++) 01199 { 01200 if (commands[i].func && 01201 ((name && 01202 !my_strnncoll(charset_info,(uchar*)name,len, 01203 (uchar*)commands[i].name,len) && 01204 !commands[i].name[len] && 01205 (!end || (end && commands[i].takes_params))) || 01206 !name && commands[i].cmd_char == cmd_char)) 01207 { 01208 DBUG_PRINT("exit",("found command: %s", commands[i].name)); 01209 DBUG_RETURN(&commands[i]); 01210 } 01211 } 01212 DBUG_RETURN((COMMANDS *) 0); 01213 } 01214 01215 01216 static bool add_line(String &buffer,char *line,char *in_string, 01217 bool *ml_comment) 01218 { 01219 uchar inchar; 01220 char buff[80], *pos, *out; 01221 COMMANDS *com; 01222 bool need_space= 0; 01223 DBUG_ENTER("add_line"); 01224 01225 if (!line[0] && buffer.is_empty()) 01226 DBUG_RETURN(0); 01227 #ifdef HAVE_READLINE 01228 if (status.add_to_history && line[0] && not_in_history(line)) 01229 add_history(line); 01230 #endif 01231 #ifdef USE_MB 01232 char *end_of_line=line+(uint) strlen(line); 01233 #endif 01234 01235 for (pos=out=line ; (inchar= (uchar) *pos) ; pos++) 01236 { 01237 if (my_isspace(charset_info,inchar) && out == line && 01238 buffer.is_empty()) 01239 continue; 01240 #ifdef USE_MB 01241 int length; 01242 if (use_mb(charset_info) && 01243 (length= my_ismbchar(charset_info, pos, end_of_line))) 01244 { 01245 if (!*ml_comment) 01246 { 01247 while (length--) 01248 *out++ = *pos++; 01249 pos--; 01250 } 01251 else 01252 pos+= length - 1; 01253 continue; 01254 } 01255 #endif 01256 if (!*ml_comment && inchar == '\\' && 01257 !(mysql.server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES)) 01258 { 01259 // Found possbile one character command like \c 01260 01261 if (!(inchar = (uchar) *++pos)) 01262 break; // readline adds one '\' 01263 if (*in_string || inchar == 'N') // \N is short for NULL 01264 { // Don't allow commands in string 01265 *out++='\\'; 01266 *out++= (char) inchar; 01267 continue; 01268 } 01269 if ((com=find_command(NullS,(char) inchar))) 01270 { 01271 const String tmp(line,(uint) (out-line), charset_info); 01272 buffer.append(tmp); 01273 if ((*com->func)(&buffer,pos-1) > 0) 01274 DBUG_RETURN(1); // Quit 01275 if (com->takes_params) 01276 { 01277 for (pos++ ; 01278 *pos && (*pos != *delimiter || 01279 !is_prefix(pos + 1, delimiter + 1)) ; pos++) 01280 ; // Remove parameters 01281 if (!*pos) 01282 pos--; 01283 else 01284 pos+= delimiter_length - 1; // Point at last delim char 01285 } 01286 out=line; 01287 } 01288 else 01289 { 01290 sprintf(buff,"Unknown command '\\%c'.",inchar); 01291 if (put_info(buff,INFO_ERROR) > 0) 01292 DBUG_RETURN(1); 01293 *out++='\\'; 01294 *out++=(char) inchar; 01295 continue; 01296 } 01297 } 01298 else if (!*ml_comment && !*in_string && 01299 (*pos == *delimiter && is_prefix(pos + 1, delimiter + 1) || 01300 buffer.length() == 0 && (out - line) >= 9 && 01301 !my_strcasecmp(charset_info, line, "delimiter"))) 01302 { 01303 uint old_delimiter_length= delimiter_length; 01304 if (out != line) 01305 buffer.append(line, (uint) (out - line)); // Add this line 01306 if ((com= find_command(buffer.c_ptr(), 0))) 01307 { 01308 if (com->func == com_delimiter) 01309 { 01310 /* 01311 Delimiter wants the get rest of the given line as argument to 01312 allow one to change ';' to ';;' and back 01313 */ 01314 char *end= strend(pos); 01315 buffer.append(pos, (uint) (end - pos)); 01316 /* Ensure pos will point at \0 after the pos+= below */ 01317 pos= end - old_delimiter_length + 1; 01318 } 01319 if ((*com->func)(&buffer, buffer.c_ptr()) > 0) 01320 DBUG_RETURN(1); // Quit 01321 } 01322 else 01323 { 01324 if (com_go(&buffer, 0) > 0) // < 0 is not fatal 01325 DBUG_RETURN(1); 01326 } 01327 buffer.length(0); 01328 out= line; 01329 pos+= old_delimiter_length - 1; 01330 } 01331 else if (!*ml_comment && (!*in_string && (inchar == '#' || 01332 inchar == '-' && pos[1] == '-' && 01333 my_isspace(charset_info,pos[2])))) 01334 break; // comment to end of line 01335 else if (!*in_string && inchar == '/' && *(pos+1) == '*' && 01336 *(pos+2) != '!') 01337 { 01338 pos++; 01339 *ml_comment= 1; 01340 if (out != line) 01341 { 01342 buffer.append(line,(uint) (out-line)); 01343 out=line; 01344 } 01345 } 01346 else if (*ml_comment && inchar == '*' && *(pos + 1) == '/') 01347 { 01348 pos++; 01349 *ml_comment= 0; 01350 need_space= 1; 01351 } 01352 else 01353 { // Add found char to buffer 01354 if (inchar == *in_string) 01355 *in_string= 0; 01356 else if (!*ml_comment && !*in_string && 01357 (inchar == '\'' || inchar == '"' || inchar == '`')) 01358 *in_string= (char) inchar; 01359 if (!*ml_comment) 01360 { 01361 if (need_space && !my_isspace(charset_info, (char)inchar)) 01362 { 01363 *out++= ' '; 01364 need_space= 0; 01365 } 01366 *out++= (char) inchar; 01367 } 01368 } 01369 } 01370 if (out != line || !buffer.is_empty()) 01371 { 01372 *out++='\n'; 01373 uint length=(uint) (out-line); 01374 if (buffer.length() + length >= buffer.alloced_length()) 01375 buffer.realloc(buffer.length()+length+IO_SIZE); 01376 if (!(*ml_comment) && buffer.append(line,length)) 01377 DBUG_RETURN(1); 01378 } 01379 DBUG_RETURN(0); 01380 } 01381 01382 /***************************************************************** 01383 Interface to Readline Completion 01384 ******************************************************************/ 01385 01386 #ifdef HAVE_READLINE 01387 01388 static char *new_command_generator(const char *text, int); 01389 static char **new_mysql_completion (const char *text, int start, int end); 01390 01391 /* 01392 Tell the GNU Readline library how to complete. We want to try to complete 01393 on command names if this is the first word in the line, or on filenames 01394 if not. 01395 */ 01396 01397 #if defined(USE_NEW_READLINE_INTERFACE) || defined(USE_LIBEDIT_INTERFACE) 01398 char *no_completion(const char*,int) 01399 #else 01400 int no_completion() 01401 #endif 01402 { 01403 return 0; /* No filename completion */ 01404 } 01405 01406 /* glues pieces of history back together if in pieces */ 01407 static void fix_history(String *final_command) 01408 { 01409 int total_lines = 1; 01410 char *ptr = final_command->c_ptr(); 01411 String fixed_buffer; /* Converted buffer */ 01412 char str_char = '\0'; /* Character if we are in a string or not */ 01413 01414 /* find out how many lines we have and remove newlines */ 01415 while (*ptr != '\0') 01416 { 01417 switch (*ptr) { 01418 /* string character */ 01419 case '"': 01420 case '\'': 01421 case '`': 01422 if (str_char == '\0') /* open string */ 01423 str_char = *ptr; 01424 else if (str_char == *ptr) /* close string */ 01425 str_char = '\0'; 01426 fixed_buffer.append(ptr,1); 01427 break; 01428 case '\n': 01429 /* 01430 not in string, change to space 01431 if in string, leave it alone 01432 */ 01433 fixed_buffer.append(str_char == '\0' ? " " : "\n"); 01434 total_lines++; 01435 break; 01436 case '\\': 01437 fixed_buffer.append('\\'); 01438 /* need to see if the backslash is escaping anything */ 01439 if (str_char) 01440 { 01441 ptr++; 01442 /* special characters that need escaping */ 01443 if (*ptr == '\'' || *ptr == '"' || *ptr == '\\') 01444 fixed_buffer.append(ptr,1); 01445 else 01446 ptr--; 01447 } 01448 break; 01449 01450 default: 01451 fixed_buffer.append(ptr,1); 01452 } 01453 ptr++; 01454 } 01455 if (total_lines > 1) 01456 add_history(fixed_buffer.ptr()); 01457 } 01458 01459 /* 01460 returns 0 if line matches the previous history entry 01461 returns 1 if the line doesn't match the previous history entry 01462 */ 01463 static int not_in_history(const char *line) 01464 { 01465 HIST_ENTRY *oldhist = history_get(history_length); 01466 01467 if (oldhist == 0) 01468 return 1; 01469 if (strcmp(oldhist->line,line) == 0) 01470 return 0; 01471 return 1; 01472 } 01473 01474 static void initialize_readline (char *name) 01475 { 01476 /* Allow conditional parsing of the ~/.inputrc file. */ 01477 rl_readline_name = name; 01478 01479 /* Tell the completer that we want a crack first. */ 01480 #if defined(USE_NEW_READLINE_INTERFACE) 01481 rl_attempted_completion_function= (rl_completion_func_t*)&new_mysql_completion; 01482 rl_completion_entry_function= (rl_compentry_func_t*)&no_completion; 01483 #elif defined(USE_LIBEDIT_INTERFACE) 01484 #ifdef HAVE_LOCALE_H 01485 setlocale(LC_ALL,""); /* so as libedit use isprint */ 01486 #endif 01487 rl_attempted_completion_function= (CPPFunction*)&new_mysql_completion; 01488 rl_completion_entry_function= (Function*)&no_completion; 01489 #else 01490 rl_attempted_completion_function= (CPPFunction*)&new_mysql_completion; 01491 rl_completion_entry_function= (Function*)&no_completion; 01492 #endif 01493 } 01494 01495 /* 01496 Attempt to complete on the contents of TEXT. START and END show the 01497 region of TEXT that contains the word to complete. We can use the 01498 entire line in case we want to do some simple parsing. Return the 01499 array of matches, or NULL if there aren't any. 01500 */ 01501 01502 static char **new_mysql_completion (const char *text, 01503 int start __attribute__((unused)), 01504 int end __attribute__((unused))) 01505 { 01506 if (!status.batch && !quick) 01507 #if defined(USE_NEW_READLINE_INTERFACE) 01508 return rl_completion_matches(text, new_command_generator); 01509 #else 01510 return completion_matches((char *)text, (CPFunction *)new_command_generator); 01511 #endif 01512 else 01513 return (char**) 0; 01514 } 01515 01516 static char *new_command_generator(const char *text,int state) 01517 { 01518 static int textlen; 01519 char *ptr; 01520 static Bucket *b; 01521 static entry *e; 01522 static uint i; 01523 01524 if (!state) 01525 textlen=(uint) strlen(text); 01526 01527 if (textlen>0) 01528 { /* lookup in the hash */ 01529 if (!state) 01530 { 01531 uint len; 01532 01533 b = find_all_matches(&ht,text,(uint) strlen(text),&len); 01534 if (!b) 01535 return NullS; 01536 e = b->pData; 01537 } 01538 01539 if (e) 01540 { 01541 ptr= strdup(e->str); 01542 e = e->pNext; 01543 return ptr; 01544 } 01545 } 01546 else 01547 { /* traverse the entire hash, ugly but works */ 01548 01549 if (!state) 01550 { 01551 /* find the first used bucket */ 01552 for (i=0 ; i < ht.nTableSize ; i++) 01553 { 01554 if (ht.arBuckets[i]) 01555 { 01556 b = ht.arBuckets[i]; 01557 e = b->pData; 01558 break; 01559 } 01560 } 01561 } 01562 ptr= NullS; 01563 while (e && !ptr) 01564 { /* find valid entry in bucket */ 01565 if ((uint) strlen(e->str) == b->nKeyLength) 01566 ptr = strdup(e->str); 01567 /* find the next used entry */ 01568 e = e->pNext; 01569 if (!e) 01570 { /* find the next used bucket */ 01571 b = b->pNext; 01572 if (!b) 01573 { 01574 for (i++ ; i<ht.nTableSize; i++) 01575 { 01576 if (ht.arBuckets[i]) 01577 { 01578 b = ht.arBuckets[i]; 01579 e = b->pData; 01580 break; 01581 } 01582 } 01583 } 01584 else 01585 e = b->pData; 01586 } 01587 } 01588 if (ptr) 01589 return ptr; 01590 } 01591 return NullS; 01592 } 01593 01594 01595 /* Build up the completion hash */ 01596 01597 static void build_completion_hash(bool rehash, bool write_info) 01598 { 01599 COMMANDS *cmd=commands; 01600 MYSQL_RES *databases=0,*tables=0; 01601 MYSQL_RES *fields; 01602 static char ***field_names= 0; 01603 MYSQL_ROW database_row,table_row; 01604 MYSQL_FIELD *sql_field; 01605 char buf[NAME_LEN*2+2]; // table name plus field name plus 2 01606 int i,j,num_fields; 01607 DBUG_ENTER("build_completion_hash"); 01608 01609 if (status.batch || quick || !current_db) 01610 DBUG_VOID_RETURN; // We don't need completion in batches 01611 01612 /* hash SQL commands */ 01613 while (cmd->name) { 01614 add_word(&ht,(char*) cmd->name); 01615 cmd++; 01616 } 01617 if (!rehash) 01618 DBUG_VOID_RETURN; 01619 01620 /* Free old used memory */ 01621 if (field_names) 01622 field_names=0; 01623 completion_hash_clean(&ht); 01624 free_root(&hash_mem_root,MYF(0)); 01625 01626 /* hash MySQL functions (to be implemented) */ 01627 01628 /* hash all database names */ 01629 if (mysql_query(&mysql,"show databases") == 0) 01630 { 01631 if (!(databases = mysql_store_result(&mysql))) 01632 put_info(mysql_error(&mysql),INFO_INFO); 01633 else 01634 { 01635 while ((database_row=mysql_fetch_row(databases))) 01636 { 01637 char *str=strdup_root(&hash_mem_root, (char*) database_row[0]); 01638 if (str) 01639 add_word(&ht,(char*) str); 01640 } 01641 mysql_free_result(databases); 01642 } 01643 } 01644 /* hash all table names */ 01645 if (mysql_query(&mysql,"show tables")==0) 01646 { 01647 if (!(tables = mysql_store_result(&mysql))) 01648 put_info(mysql_error(&mysql),INFO_INFO); 01649 else 01650 { 01651 if (mysql_num_rows(tables) > 0 && !opt_silent && write_info) 01652 { 01653 tee_fprintf(stdout, "\ 01654 Reading table information for completion of table and column names\n\ 01655 You can turn off this feature to get a quicker startup with -A\n\n"); 01656 } 01657 while ((table_row=mysql_fetch_row(tables))) 01658 { 01659 char *str=strdup_root(&hash_mem_root, (char*) table_row[0]); 01660 if (str && 01661 !completion_hash_exists(&ht,(char*) str, (uint) strlen(str))) 01662 add_word(&ht,str); 01663 } 01664 } 01665 } 01666 01667 /* hash all field names, both with the table prefix and without it */ 01668 if (!tables) /* no tables */ 01669 { 01670 DBUG_VOID_RETURN; 01671 } 01672 mysql_data_seek(tables,0); 01673 if (!(field_names= (char ***) alloc_root(&hash_mem_root,sizeof(char **) * 01674 (uint) (mysql_num_rows(tables)+1)))) 01675 { 01676 mysql_free_result(tables); 01677 DBUG_VOID_RETURN; 01678 } 01679 i=0; 01680 while ((table_row=mysql_fetch_row(tables))) 01681 { 01682 if ((fields=mysql_list_fields(&mysql,(const char*) table_row[0],NullS))) 01683 { 01684 num_fields=mysql_num_fields(fields); 01685 if (!(field_names[i] = (char **) alloc_root(&hash_mem_root, 01686 sizeof(char *) * 01687 (num_fields*2+1)))) 01688 { 01689 mysql_free_result(fields); 01690 break; 01691 } 01692 field_names[i][num_fields*2]= '\0'; 01693 j=0; 01694 while ((sql_field=mysql_fetch_field(fields))) 01695 { 01696 sprintf(buf,"%.64s.%.64s",table_row[0],sql_field->name); 01697 field_names[i][j] = strdup_root(&hash_mem_root,buf); 01698 add_word(&ht,field_names[i][j]); 01699 field_names[i][num_fields+j] = strdup_root(&hash_mem_root, 01700 sql_field->name); 01701 if (!completion_hash_exists(&ht,field_names[i][num_fields+j], 01702 (uint) strlen(field_names[i][num_fields+j]))) 01703 add_word(&ht,field_names[i][num_fields+j]); 01704 j++; 01705 } 01706 mysql_free_result(fields); 01707 } 01708 else 01709 field_names[i]= 0; 01710 01711 i++; 01712 } 01713 mysql_free_result(tables); 01714 field_names[i]=0; // End pointer 01715 DBUG_VOID_RETURN; 01716 } 01717 01718 /* for gnu readline */ 01719 01720 #ifndef HAVE_INDEX 01721 extern "C" { 01722 extern char *index(const char *,int c),*rindex(const char *,int); 01723 01724 char *index(const char *s,int c) 01725 { 01726 for (;;) 01727 { 01728 if (*s == (char) c) return (char*) s; 01729 if (!*s++) return NullS; 01730 } 01731 } 01732 01733 char *rindex(const char *s,int c) 01734 { 01735 reg3 char *t; 01736 01737 t = NullS; 01738 do if (*s == (char) c) t = (char*) s; while (*s++); 01739 return (char*) t; 01740 } 01741 } 01742 #endif 01743 #endif /* HAVE_READLINE */ 01744 01745 01746 static int reconnect(void) 01747 { 01748 if (opt_reconnect) 01749 { 01750 put_info("No connection. Trying to reconnect...",INFO_INFO); 01751 (void) com_connect((String *) 0, 0); 01752 if (rehash) 01753 com_rehash(NULL, NULL); 01754 } 01755 if (!connected) 01756 return put_info("Can't connect to the server\n",INFO_ERROR); 01757 return 0; 01758 } 01759 01760 static void get_current_db() 01761 { 01762 MYSQL_RES *res; 01763 01764 my_free(current_db, MYF(MY_ALLOW_ZERO_PTR)); 01765 current_db= NULL; 01766 /* In case of error below current_db will be NULL */ 01767 if (!mysql_query(&mysql, "SELECT DATABASE()") && 01768 (res= mysql_use_result(&mysql))) 01769 { 01770 MYSQL_ROW row= mysql_fetch_row(res); 01771 if (row[0]) 01772 current_db= my_strdup(row[0], MYF(MY_WME)); 01773 mysql_free_result(res); 01774 } 01775 } 01776 01777 /*************************************************************************** 01778 The different commands 01779 ***************************************************************************/ 01780 01781 int mysql_real_query_for_lazy(const char *buf, int length) 01782 { 01783 for (uint retry=0;; retry++) 01784 { 01785 int error; 01786 if (!mysql_real_query(&mysql,buf,length)) 01787 return 0; 01788 error= put_error(&mysql); 01789 if (mysql_errno(&mysql) != CR_SERVER_GONE_ERROR || retry > 1 || 01790 !opt_reconnect) 01791 return error; 01792 if (reconnect()) 01793 return error; 01794 } 01795 } 01796 01797 int mysql_store_result_for_lazy(MYSQL_RES **result) 01798 { 01799 if ((*result=mysql_store_result(&mysql))) 01800 return 0; 01801 01802 if (mysql_error(&mysql)[0]) 01803 return put_error(&mysql); 01804 return 0; 01805 } 01806 01807 static void print_help_item(MYSQL_ROW *cur, int num_name, int num_cat, char *last_char) 01808 { 01809 char ccat= (*cur)[num_cat][0]; 01810 if (*last_char != ccat) 01811 { 01812 put_info(ccat == 'Y' ? "categories:" : "topics:", INFO_INFO); 01813 *last_char= ccat; 01814 } 01815 tee_fprintf(PAGER, " %s\n", (*cur)[num_name]); 01816 } 01817 01818 01819 static int com_server_help(String *buffer __attribute__((unused)), 01820 char *line __attribute__((unused)), char *help_arg) 01821 { 01822 MYSQL_ROW cur; 01823 const char *server_cmd= buffer->ptr(); 01824 char cmd_buf[100]; 01825 MYSQL_RES *result; 01826 int error; 01827 01828 if (help_arg[0] != '\'') 01829 { 01830 char *end_arg= strend(help_arg); 01831 if(--end_arg) 01832 { 01833 while (my_isspace(charset_info,*end_arg)) 01834 end_arg--; 01835 *++end_arg= '\0'; 01836 } 01837 (void) strxnmov(cmd_buf, sizeof(cmd_buf), "help '", help_arg, "'", NullS); 01838 server_cmd= cmd_buf; 01839 } 01840 01841 if (!status.batch) 01842 { 01843 old_buffer= *buffer; 01844 old_buffer.copy(); 01845 } 01846 01847 if (!connected && reconnect()) 01848 return 1; 01849 01850 if ((error= mysql_real_query_for_lazy(server_cmd,(int)strlen(server_cmd))) || 01851 (error= mysql_store_result_for_lazy(&result))) 01852 return error; 01853 01854 if (result) 01855 { 01856 unsigned int num_fields= mysql_num_fields(result); 01857 my_ulonglong num_rows= mysql_num_rows(result); 01858 mysql_fetch_fields(result); 01859 if (num_fields==3 && num_rows==1) 01860 { 01861 if (!(cur= mysql_fetch_row(result))) 01862 { 01863 error= -1; 01864 goto err; 01865 } 01866 01867 init_pager(); 01868 tee_fprintf(PAGER, "Name: \'%s\'\n", cur[0]); 01869 tee_fprintf(PAGER, "Description:\n%s", cur[1]); 01870 if (cur[2] && *((char*)cur[2])) 01871 tee_fprintf(PAGER, "Examples:\n%s", cur[2]); 01872 tee_fprintf(PAGER, "\n"); 01873 end_pager(); 01874 } 01875 else if (num_fields >= 2 && num_rows) 01876 { 01877 init_pager(); 01878 char last_char= 0; 01879 01880 int num_name= 0, num_cat= 0; 01881 LINT_INIT(num_name); 01882 LINT_INIT(num_cat); 01883 01884 if (num_fields == 2) 01885 { 01886 put_info("Many help items for your request exist.", INFO_INFO); 01887 put_info("To make a more specific request, please type 'help <item>',\nwhere <item> is one of the following", INFO_INFO); 01888 num_name= 0; 01889 num_cat= 1; 01890 } 01891 else if ((cur= mysql_fetch_row(result))) 01892 { 01893 tee_fprintf(PAGER, "You asked for help about help category: \"%s\"\n", cur[0]); 01894 put_info("For more information, type 'help <item>', where <item> is one of the following", INFO_INFO); 01895 num_name= 1; 01896 num_cat= 2; 01897 print_help_item(&cur,1,2,&last_char); 01898 } 01899 01900 while ((cur= mysql_fetch_row(result))) 01901 print_help_item(&cur,num_name,num_cat,&last_char); 01902 tee_fprintf(PAGER, "\n"); 01903 end_pager(); 01904 } 01905 else 01906 { 01907 put_info("\nNothing found", INFO_INFO); 01908 put_info("Please try to run 'help contents' for a list of all accessible topics\n", INFO_INFO); 01909 } 01910 } 01911 01912 err: 01913 mysql_free_result(result); 01914 return error; 01915 } 01916 01917 static int 01918 com_help(String *buffer __attribute__((unused)), 01919 char *line __attribute__((unused))) 01920 { 01921 reg1 int i, j; 01922 char * help_arg= strchr(line,' '), buff[32], *end; 01923 if (help_arg) 01924 { 01925 while (my_isspace(charset_info,*help_arg)) 01926 help_arg++; 01927 if (*help_arg) 01928 return com_server_help(buffer,line,help_arg); 01929 } 01930 01931 put_info("\nFor information about MySQL products and services, visit:\n" 01932 " http://www.mysql.com/\n" 01933 "For developer information, including the MySQL Reference Manual, " 01934 "visit:\n" 01935 " http://dev.mysql.com/\n" 01936 "To buy MySQL Network Support, training, or other products, visit:\n" 01937 " https://shop.mysql.com/\n", INFO_INFO); 01938 put_info("List of all MySQL commands:", INFO_INFO); 01939 if (!named_cmds) 01940 put_info("Note that all text commands must be first on line and end with ';'",INFO_INFO); 01941 for (i = 0; commands[i].name; i++) 01942 { 01943 end= strmov(buff, commands[i].name); 01944 for (j= (int)strlen(commands[i].name); j < 10; j++) 01945 end= strmov(end, " "); 01946 if (commands[i].func) 01947 tee_fprintf(stdout, "%s(\\%c) %s\n", buff, 01948 commands[i].cmd_char, commands[i].doc); 01949 } 01950 if (connected && mysql_get_server_version(&mysql) >= 40100) 01951 put_info("\nFor server side help, type 'help contents'\n", INFO_INFO); 01952 return 0; 01953 } 01954 01955 01956 /* ARGSUSED */ 01957 static int 01958 com_clear(String *buffer,char *line __attribute__((unused))) 01959 { 01960 #ifdef HAVE_READLINE 01961 if (status.add_to_history) 01962 fix_history(buffer); 01963 #endif 01964 buffer->length(0); 01965 return 0; 01966 } 01967 01968 /* ARGSUSED */ 01969 static int 01970 com_charset(String *buffer __attribute__((unused)), char *line) 01971 { 01972 char buff[256], *param; 01973 CHARSET_INFO * new_cs; 01974 strmake(buff, line, sizeof(buff) - 1); 01975 param= get_arg(buff, 0); 01976 if (!param || !*param) 01977 { 01978 return put_info("Usage: \\C char_setname | charset charset_name", 01979 INFO_ERROR, 0); 01980 } 01981 new_cs= get_charset_by_csname(param, MY_CS_PRIMARY, MYF(MY_WME)); 01982 if (new_cs) 01983 { 01984 charset_info= new_cs; 01985 put_info("Charset changed", INFO_INFO); 01986 } 01987 else put_info("Charset is not found", INFO_INFO); 01988 return 0; 01989 } 01990 01991 /* 01992 Execute command 01993 Returns: 0 if ok 01994 -1 if not fatal error 01995 1 if fatal error 01996 */ 01997 01998 01999 static int 02000 com_go(String *buffer,char *line __attribute__((unused))) 02001 { 02002 char buff[200], time_buff[32], *pos; 02003 MYSQL_RES *result; 02004 ulong timer, warnings; 02005 uint error= 0; 02006 int err= 0; 02007 02008 interrupted_query= 0; 02009 if (!status.batch) 02010 { 02011 old_buffer= *buffer; // Save for edit command 02012 old_buffer.copy(); 02013 } 02014 02015 /* Remove garbage for nicer messages */ 02016 LINT_INIT(buff[0]); 02017 remove_cntrl(*buffer); 02018 02019 if (buffer->is_empty()) 02020 { 02021 if (status.batch) // Ignore empty quries 02022 return 0; 02023 return put_info("No query specified\n",INFO_ERROR); 02024 02025 } 02026 if (!connected && reconnect()) 02027 { 02028 buffer->length(0); // Remove query on error 02029 return opt_reconnect ? -1 : 1; // Fatal error 02030 } 02031 if (verbose) 02032 (void) com_print(buffer,0); 02033 02034 if (skip_updates && 02035 (buffer->length() < 4 || my_strnncoll(charset_info, 02036 (const uchar*)buffer->ptr(),4, 02037 (const uchar*)"SET ",4))) 02038 { 02039 (void) put_info("Ignoring query to other database",INFO_INFO); 02040 return 0; 02041 } 02042 02043 timer=start_timer(); 02044 02045 executing_query= 1; 02046 02047 error= mysql_real_query_for_lazy(buffer->ptr(),buffer->length()); 02048 02049 #ifdef HAVE_READLINE 02050 if (status.add_to_history) 02051 { 02052 buffer->append(vertical ? "\\G" : delimiter); 02053 /* Append final command onto history */ 02054 fix_history(buffer); 02055 } 02056 #endif 02057 02058 if (error) 02059 { 02060 executing_query= 0; 02061 buffer->length(0); // Remove query on error 02062 executing_query= 0; 02063 return error; 02064 } 02065 error=0; 02066 buffer->length(0); 02067 02068 do 02069 { 02070 if (quick) 02071 { 02072 if (!(result=mysql_use_result(&mysql)) && mysql_field_count(&mysql)) 02073 { 02074 executing_query= 0; 02075 return put_error(&mysql); 02076 } 02077 } 02078 else 02079 { 02080 error= mysql_store_result_for_lazy(&result); 02081 if (error) 02082 { 02083 executing_query= 0; 02084 return error; 02085 } 02086 } 02087 02088 if (verbose >= 3 || !opt_silent) 02089 mysql_end_timer(timer,time_buff); 02090 else 02091 time_buff[0]=0; 02092 if (result) 02093 { 02094 if (!mysql_num_rows(result) && ! quick && !info_flag) 02095 { 02096 strmov(buff, "Empty set"); 02097 } 02098 else 02099 { 02100 init_pager(); 02101 if (opt_html) 02102 print_table_data_html(result); 02103 else if (opt_xml) 02104 print_table_data_xml(result); 02105 else if (vertical) 02106 print_table_data_vertically(result); 02107 else if (opt_silent && verbose <= 2 && !output_tables) 02108 print_tab_data(result); 02109 else 02110 print_table_data(result); 02111 sprintf(buff,"%ld %s in set", 02112 (long) mysql_num_rows(result), 02113 (long) mysql_num_rows(result) == 1 ? "row" : "rows"); 02114 end_pager(); 02115 } 02116 } 02117 else if (mysql_affected_rows(&mysql) == ~(ulonglong) 0) 02118 strmov(buff,"Query OK"); 02119 else 02120 sprintf(buff,"Query OK, %ld %s affected", 02121 (long) mysql_affected_rows(&mysql), 02122 (long) mysql_affected_rows(&mysql) == 1 ? "row" : "rows"); 02123 02124 pos=strend(buff); 02125 if ((warnings= mysql_warning_count(&mysql))) 02126 { 02127 *pos++= ','; 02128 *pos++= ' '; 02129 pos=int10_to_str(warnings, pos, 10); 02130 pos=strmov(pos, " warning"); 02131 if (warnings != 1) 02132 *pos++= 's'; 02133 } 02134 strmov(pos, time_buff); 02135 put_info(buff,INFO_RESULT); 02136 if (mysql_info(&mysql)) 02137 put_info(mysql_info(&mysql),INFO_RESULT); 02138 put_info("",INFO_RESULT); // Empty row 02139 02140 if (result && !mysql_eof(result)) /* Something wrong when using quick */ 02141 error= put_error(&mysql); 02142 else if (unbuffered) 02143 fflush(stdout); 02144 mysql_free_result(result); 02145 } while (!(err= mysql_next_result(&mysql))); 02146 02147 executing_query= 0; 02148 02149 if (err >= 1) 02150 error= put_error(&mysql); 02151 02152 if (show_warnings == 1 && warnings >= 1) /* Show warnings if any */ 02153 { 02154 init_pager(); 02155 print_warnings(); 02156 end_pager(); 02157 } 02158 02159 if (!error && !status.batch && 02160 (mysql.server_status & SERVER_STATUS_DB_DROPPED)) 02161 get_current_db(); 02162 02163 executing_query= 0; 02164 return error; /* New command follows */ 02165 } 02166 02167 02168 static void init_pager() 02169 { 02170 #ifdef USE_POPEN 02171 if (!opt_nopager) 02172 { 02173 if (!(PAGER= popen(pager, "w"))) 02174 { 02175 tee_fprintf(stdout, "popen() failed! defaulting PAGER to stdout!\n"); 02176 PAGER= stdout; 02177 } 02178 } 02179 else 02180 #endif 02181 PAGER= stdout; 02182 } 02183 02184 static void end_pager() 02185 { 02186 #ifdef USE_POPEN 02187 if (!opt_nopager) 02188 pclose(PAGER); 02189 #endif 02190 } 02191 02192 02193 static void init_tee(const char *file_name) 02194 { 02195 FILE* new_outfile; 02196 if (opt_outfile) 02197 end_tee(); 02198 if (!(new_outfile= my_fopen(file_name, O_APPEND | O_WRONLY, MYF(MY_WME)))) 02199 { 02200 tee_fprintf(stdout, "Error logging to file '%s'\n", file_name); 02201 return; 02202 } 02203 OUTFILE = new_outfile; 02204 strmake(outfile, file_name, FN_REFLEN-1); 02205 tee_fprintf(stdout, "Logging to file '%s'\n", file_name); 02206 opt_outfile= 1; 02207 return; 02208 } 02209 02210 02211 static void end_tee() 02212 { 02213 my_fclose(OUTFILE, MYF(0)); 02214 OUTFILE= 0; 02215 opt_outfile= 0; 02216 return; 02217 } 02218 02219 02220 static int 02221 com_ego(String *buffer,char *line) 02222 { 02223 int result; 02224 bool oldvertical=vertical; 02225 vertical=1; 02226 result=com_go(buffer,line); 02227 vertical=oldvertical; 02228 return result; 02229 } 02230 02231 02232 static const char *fieldtype2str(enum enum_field_types type) 02233 { 02234 switch (type) { 02235 case FIELD_TYPE_BIT: return "BIT"; 02236 case FIELD_TYPE_BLOB: return "BLOB"; 02237 case FIELD_TYPE_DATE: return "DATE"; 02238 case FIELD_TYPE_DATETIME: return "DATETIME"; 02239 case FIELD_TYPE_NEWDECIMAL: return "NEWDECIMAL"; 02240 case FIELD_TYPE_DECIMAL: return "DECIMAL"; 02241 case FIELD_TYPE_DOUBLE: return "DOUBLE"; 02242 case FIELD_TYPE_ENUM: return "ENUM"; 02243 case FIELD_TYPE_FLOAT: return "FLOAT"; 02244 case FIELD_TYPE_GEOMETRY: return "GEOMETRY"; 02245 case FIELD_TYPE_INT24: return "INT24"; 02246 case FIELD_TYPE_LONG: return "LONG"; 02247 case FIELD_TYPE_LONGLONG: return "LONGLONG"; 02248 case FIELD_TYPE_LONG_BLOB: return "LONG_BLOB"; 02249 case FIELD_TYPE_MEDIUM_BLOB: return "MEDIUM_BLOB"; 02250 case FIELD_TYPE_NEWDATE: return "NEWDATE"; 02251 case FIELD_TYPE_NULL: return "NULL"; 02252 case FIELD_TYPE_SET: return "SET"; 02253 case FIELD_TYPE_SHORT: return "SHORT"; 02254 case FIELD_TYPE_STRING: return "STRING"; 02255 case FIELD_TYPE_TIME: return "TIME"; 02256 case FIELD_TYPE_TIMESTAMP: return "TIMESTAMP"; 02257 case FIELD_TYPE_TINY: return "TINY"; 02258 case FIELD_TYPE_TINY_BLOB: return "TINY_BLOB"; 02259 case FIELD_TYPE_VAR_STRING: return "VAR_STRING"; 02260 case FIELD_TYPE_YEAR: return "YEAR"; 02261 default: return "?-unknown-?"; 02262 } 02263 } 02264 02265 static char *fieldflags2str(uint f) { 02266 static char buf[1024]; 02267 char *s=buf; 02268 *s=0; 02269 #define ff2s_check_flag(X) \ 02270 if (f & X ## _FLAG) { s=strmov(s, # X " "); f &= ~ X ## _FLAG; } 02271 ff2s_check_flag(NOT_NULL); 02272 ff2s_check_flag(PRI_KEY); 02273 ff2s_check_flag(UNIQUE_KEY); 02274 ff2s_check_flag(MULTIPLE_KEY); 02275 ff2s_check_flag(BLOB); 02276 ff2s_check_flag(UNSIGNED); 02277 ff2s_check_flag(ZEROFILL); 02278 ff2s_check_flag(BINARY); 02279 ff2s_check_flag(ENUM); 02280 ff2s_check_flag(AUTO_INCREMENT); 02281 ff2s_check_flag(TIMESTAMP); 02282 ff2s_check_flag(SET); 02283 ff2s_check_flag(NO_DEFAULT_VALUE); 02284 ff2s_check_flag(NUM); 02285 ff2s_check_flag(PART_KEY); 02286 ff2s_check_flag(GROUP); 02287 ff2s_check_flag(UNIQUE); 02288 ff2s_check_flag(BINCMP); 02289 #undef ff2s_check_flag 02290 if (f) 02291 sprintf(s, " unknows=0x%04x", f); 02292 return buf; 02293 } 02294 02295 static void 02296 print_field_types(MYSQL_RES *result) 02297 { 02298 MYSQL_FIELD *field; 02299 uint i=0; 02300 02301 while ((field = mysql_fetch_field(result))) 02302 { 02303 tee_fprintf(PAGER, "Field %3u: `%s`\n" 02304 "Catalog: `%s`\n" 02305 "Database: `%s`\n" 02306 "Table: `%s`\n" 02307 "Org_table: `%s`\n" 02308 "Type: %s\n" 02309 "Collation: %s (%u)\n" 02310 "Length: %lu\n" 02311 "Max_length: %lu\n" 02312 "Decimals: %u\n" 02313 "Flags: %s\n\n", 02314 ++i, 02315 field->name, field->catalog, field->db, field->table, 02316 field->org_table, fieldtype2str(field->type), 02317 get_charset_name(field->charsetnr), field->charsetnr, 02318 field->length, field->max_length, field->decimals, 02319 fieldflags2str(field->flags)); 02320 } 02321 tee_puts("", PAGER); 02322 } 02323 02324 02325 static void 02326 print_table_data(MYSQL_RES *result) 02327 { 02328 String separator(256); 02329 MYSQL_ROW cur; 02330 MYSQL_FIELD *field; 02331 bool *num_flag; 02332 bool *not_null_flag; 02333 02334 num_flag=(bool*) my_alloca(sizeof(bool)*mysql_num_fields(result)); 02335 not_null_flag=(bool*) my_alloca(sizeof(bool)*mysql_num_fields(result)); 02336 if (info_flag) 02337 { 02338 print_field_types(result); 02339 if (!mysql_num_rows(result)) 02340 return; 02341 mysql_field_seek(result,0); 02342 } 02343 separator.copy("+",1,charset_info); 02344 while ((field = mysql_fetch_field(result))) 02345 { 02346 uint length= column_names ? field->name_length : 0; 02347 if (quick) 02348 length=max(length,field->length); 02349 else 02350 length=max(length,field->max_length); 02351 if (length < 4 && !IS_NOT_NULL(field->flags)) 02352 length=4; // Room for "NULL" 02353 field->max_length=length; 02354 separator.fill(separator.length()+length+2,'-'); 02355 separator.append('+'); 02356 } 02357 separator.append('\0'); // End marker for \0 02358 tee_puts((char*) separator.ptr(), PAGER); 02359 if (column_names) 02360 { 02361 mysql_field_seek(result,0); 02362 (void) tee_fputs("|", PAGER); 02363 for (uint off=0; (field = mysql_fetch_field(result)) ; off++) 02364 { 02365 tee_fprintf(PAGER, " %-*s |",(int) min(field->max_length, 02366 MAX_COLUMN_LENGTH), 02367 field->name); 02368 num_flag[off]= IS_NUM(field->type); 02369 not_null_flag[off]= IS_NOT_NULL(field->flags); 02370 } 02371 (void) tee_fputs("\n", PAGER); 02372 tee_puts((char*) separator.ptr(), PAGER); 02373 } 02374 02375 while ((cur= mysql_fetch_row(result))) 02376 { 02377 if (interrupted_query) 02378 break; 02379 ulong *lengths= mysql_fetch_lengths(result); 02380 (void) tee_fputs("| ", PAGER); 02381 mysql_field_seek(result, 0); 02382 for (uint off= 0; off < mysql_num_fields(result); off++) 02383 { 02384 const char *buffer; 02385 uint data_length; 02386 uint field_max_length; 02387 bool right_justified; 02388 uint visible_length; 02389 uint extra_padding; 02390 02391 if (! not_null_flag[off] && (cur[off] == NULL)) 02392 { 02393 buffer= "NULL"; 02394 data_length= 4; 02395 } 02396 else 02397 { 02398 buffer= cur[off]; 02399 data_length= (uint) lengths[off]; 02400 } 02401 02402 field= mysql_fetch_field(result); 02403 field_max_length= field->max_length; 02404 02405 /* 02406 How many text cells on the screen will this string span? If it contains 02407 multibyte characters, then the number of characters we occupy on screen 02408 will be fewer than the number of bytes we occupy in memory. 02409 02410 We need to find how much screen real-estate we will occupy to know how 02411 many extra padding-characters we should send with the printing function. 02412 */ 02413 visible_length= charset_info->cset->numcells(charset_info, buffer, buffer + data_length); 02414 extra_padding= data_length - visible_length; 02415 02416 if (field_max_length > MAX_COLUMN_LENGTH) 02417 tee_print_sized_data(buffer, data_length, MAX_COLUMN_LENGTH+extra_padding, FALSE); 02418 else 02419 { 02420 if (num_flag[off] != 0) /* if it is numeric, we right-justify it */ 02421 tee_print_sized_data(buffer, data_length, field_max_length+extra_padding, TRUE); 02422 else 02423 tee_print_sized_data(buffer, data_length, field_max_length+extra_padding, FALSE); 02424 } 02425 tee_fputs(" | ", PAGER); 02426 } 02427 (void) tee_fputs("\n", PAGER); 02428 } 02429 tee_puts((char*) separator.ptr(), PAGER); 02430 my_afree((gptr) num_flag); 02431 my_afree((gptr) not_null_flag); 02432 } 02433 02434 02435 static void 02436 tee_print_sized_data(const char *data, unsigned int data_length, unsigned int total_bytes_to_send, bool right_justified) 02437 { 02438 /* 02439 For '\0's print ASCII spaces instead, as '\0' is eaten by (at 02440 least my) console driver, and that messes up the pretty table 02441 grid. (The \0 is also the reason we can't use fprintf() .) 02442 */ 02443 unsigned int i; 02444 const char *p; 02445 02446 if (right_justified) 02447 for (i= data_length; i < total_bytes_to_send; i++) 02448 tee_putc((int)' ', PAGER); 02449 02450 for (i= 0, p= data; i < data_length; i+= 1, p+= 1) 02451 { 02452 if (*p == '\0') 02453 tee_putc((int)' ', PAGER); 02454 else 02455 tee_putc((int)*p, PAGER); 02456 } 02457 02458 if (! right_justified) 02459 for (i= data_length; i < total_bytes_to_send; i++) 02460 tee_putc((int)' ', PAGER); 02461 } 02462 02463 02464 02465 static void 02466 print_table_data_html(MYSQL_RES *result) 02467 { 02468 MYSQL_ROW cur; 02469 MYSQL_FIELD *field; 02470 02471 mysql_field_seek(result,0); 02472 (void) tee_fputs("<TABLE BORDER=1><TR>", PAGER); 02473 if (column_names) 02474 { 02475 while((field = mysql_fetch_field(result))) 02476 { 02477 tee_fprintf(PAGER, "<TH>%s</TH>", (field->name ? 02478 (field->name[0] ? field->name : 02479 " ") : "NULL")); 02480 } 02481 (void) tee_fputs("</TR>", PAGER); 02482 } 02483 while ((cur = mysql_fetch_row(result))) 02484 { 02485 if (interrupted_query) 02486 break; 02487 ulong *lengths=mysql_fetch_lengths(result); 02488 (void) tee_fputs("<TR>", PAGER); 02489 for (uint i=0; i < mysql_num_fields(result); i++) 02490 { 02491 (void) tee_fputs("<TD>", PAGER); 02492 safe_put_field(cur[i],lengths[i]); 02493 (void) tee_fputs("</TD>", PAGER); 02494 } 02495 (void) tee_fputs("</TR>", PAGER); 02496 } 02497 (void) tee_fputs("</TABLE>", PAGER); 02498 } 02499 02500 02501 static void 02502 print_table_data_xml(MYSQL_RES *result) 02503 { 02504 MYSQL_ROW cur; 02505 MYSQL_FIELD *fields; 02506 02507 mysql_field_seek(result,0); 02508 02509 tee_fputs("<?xml version=\"1.0\"?>\n\n<resultset statement=\"", PAGER); 02510 xmlencode_print(glob_buffer.ptr(), (int)strlen(glob_buffer.ptr())); 02511 tee_fputs("\">", PAGER); 02512 02513 fields = mysql_fetch_fields(result); 02514 while ((cur = mysql_fetch_row(result))) 02515 { 02516 if (interrupted_query) 02517 break; 02518 ulong *lengths=mysql_fetch_lengths(result); 02519 (void) tee_fputs("\n <row>\n", PAGER); 02520 for (uint i=0; i < mysql_num_fields(result); i++) 02521 { 02522 tee_fprintf(PAGER, "\t<field name=\""); 02523 xmlencode_print(fields[i].name, (uint) strlen(fields[i].name)); 02524 tee_fprintf(PAGER, "\">"); 02525 xmlencode_print(cur[i], lengths[i]); 02526 tee_fprintf(PAGER, "</field>\n"); 02527 } 02528 (void) tee_fputs(" </row>\n", PAGER); 02529 } 02530 (void) tee_fputs("</resultset>\n", PAGER); 02531 } 02532 02533 02534 static void 02535 print_table_data_vertically(MYSQL_RES *result) 02536 { 02537 MYSQL_ROW cur; 02538 uint max_length=0; 02539 MYSQL_FIELD *field; 02540 02541 while ((field = mysql_fetch_field(result))) 02542 { 02543 uint length= field->name_length; 02544 if (length > max_length) 02545 max_length= length; 02546 field->max_length=length; 02547 } 02548 02549 mysql_field_seek(result,0); 02550 for (uint row_count=1; (cur= mysql_fetch_row(result)); row_count++) 02551 { 02552 if (interrupted_query) 02553 break; 02554 mysql_field_seek(result,0); 02555 tee_fprintf(PAGER, 02556 "*************************** %d. row ***************************\n", row_count); 02557 for (uint off=0; off < mysql_num_fields(result); off++) 02558 { 02559 field= mysql_fetch_field(result); 02560 tee_fprintf(PAGER, "%*s: ",(int) max_length,field->name); 02561 tee_fprintf(PAGER, "%s\n",cur[off] ? (char*) cur[off] : "NULL"); 02562 } 02563 } 02564 } 02565 02566 02567 /* print_warnings should be called right after executing a statement */ 02568 02569 static void print_warnings() 02570 { 02571 const char *query; 02572 MYSQL_RES *result; 02573 MYSQL_ROW cur; 02574 my_ulonglong num_rows; 02575 02576 /* Get the warnings */ 02577 query= "show warnings"; 02578 mysql_real_query_for_lazy(query, strlen(query)); 02579 mysql_store_result_for_lazy(&result); 02580 02581 /* Bail out when no warnings */ 02582 if (!(num_rows= mysql_num_rows(result))) 02583 { 02584 mysql_free_result(result); 02585 return; 02586 } 02587 02588 /* Print the warnings */ 02589 while ((cur= mysql_fetch_row(result))) 02590 { 02591 tee_fprintf(PAGER, "%s (Code %s): %s\n", cur[0], cur[1], cur[2]); 02592 } 02593 mysql_free_result(result); 02594 } 02595 02596 02597 static const char *array_value(const char **array, char key) 02598 { 02599 for (; *array; array+= 2) 02600 if (**array == key) 02601 return array[1]; 02602 return 0; 02603 } 02604 02605 02606 static void 02607 xmlencode_print(const char *src, uint length) 02608 { 02609 if (!src) 02610 tee_fputs("NULL", PAGER); 02611 else 02612 { 02613 for (const char *p = src; *p && length; *p++, length--) 02614 { 02615 const char *t; 02616 if ((t = array_value(xmlmeta, *p))) 02617 tee_fputs(t, PAGER); 02618 else 02619 tee_putc(*p, PAGER); 02620 } 02621 } 02622 } 02623 02624 02625 static void 02626 safe_put_field(const char *pos,ulong length) 02627 { 02628 if (!pos) 02629 tee_fputs("NULL", PAGER); 02630 else 02631 { 02632 if (opt_raw_data) 02633 tee_fputs(pos, PAGER); 02634 else for (const char *end=pos+length ; pos != end ; pos++) 02635 { 02636 #ifdef USE_MB 02637 int l; 02638 if (use_mb(charset_info) && 02639 (l = my_ismbchar(charset_info, pos, end))) 02640 { 02641 while (l--) 02642 tee_putc(*pos++, PAGER); 02643 pos--; 02644 continue; 02645 } 02646 #endif 02647 if (!*pos) 02648 tee_fputs("\\0", PAGER); // This makes everything hard 02649 else if (*pos == '\t') 02650 tee_fputs("\\t", PAGER); // This would destroy tab format 02651 else if (*pos == '\n') 02652 tee_fputs("\\n", PAGER); // This too 02653 else if (*pos == '\\') 02654 tee_fputs("\\\\", PAGER); 02655 else 02656 tee_putc(*pos, PAGER); 02657 } 02658 } 02659 } 02660 02661 02662 static void 02663 print_tab_data(MYSQL_RES *result) 02664 { 02665 MYSQL_ROW cur; 02666 MYSQL_FIELD *field; 02667 ulong *lengths; 02668 02669 if (opt_silent < 2 && column_names) 02670 { 02671 int first=0; 02672 while ((field = mysql_fetch_field(result))) 02673 { 02674 if (first++) 02675 (void) tee_fputs("\t", PAGER); 02676 (void) tee_fputs(field->name, PAGER); 02677 } 02678 (void) tee_fputs("\n", PAGER); 02679 } 02680 while ((cur = mysql_fetch_row(result))) 02681 { 02682 lengths=mysql_fetch_lengths(result); 02683 safe_put_field(cur[0],lengths[0]); 02684 for (uint off=1 ; off < mysql_num_fields(result); off++) 02685 { 02686 (void) tee_fputs("\t", PAGER); 02687 safe_put_field(cur[off], lengths[off]); 02688 } 02689 (void) tee_fputs("\n", PAGER); 02690 } 02691 } 02692 02693 static int 02694 com_tee(String *buffer, char *line __attribute__((unused))) 02695 { 02696 char file_name[FN_REFLEN], *end, *param; 02697 02698 if (status.batch) 02699 return 0; 02700 while (my_isspace(charset_info,*line)) 02701 line++; 02702 if (!(param = strchr(line, ' '))) // if outfile wasn't given, use the default 02703 { 02704 if (!strlen(outfile)) 02705 { 02706 printf("No previous outfile available, you must give a filename!\n"); 02707 return 0; 02708 } 02709 else if (opt_outfile) 02710 { 02711 tee_fprintf(stdout, "Currently logging to file '%s'\n", outfile); 02712 return 0; 02713 } 02714 else 02715 param = outfile; //resume using the old outfile 02716 } 02717 02718 /* eliminate the spaces before the parameters */ 02719 while (my_isspace(charset_info,*param)) 02720 param++; 02721 end= strmake(file_name, param, sizeof(file_name) - 1); 02722 /* remove end space from command line */ 02723 while (end > file_name && (my_isspace(charset_info,end[-1]) || 02724 my_iscntrl(charset_info,end[-1]))) 02725 end--; 02726 end[0]= 0; 02727 if (end == file_name) 02728 { 02729 printf("No outfile specified!\n"); 02730 return 0; 02731 } 02732 init_tee(file_name); 02733 return 0; 02734 } 02735 02736 02737 static int 02738 com_notee(String *buffer __attribute__((unused)), 02739 char *line __attribute__((unused))) 02740 { 02741 if (opt_outfile) 02742 end_tee(); 02743 tee_fprintf(stdout, "Outfile disabled.\n"); 02744 return 0; 02745 } 02746 02747 /* 02748 Sorry, this command is not available in Windows. 02749 */ 02750 02751 #ifdef USE_POPEN 02752 static int 02753 com_pager(String *buffer, char *line __attribute__((unused))) 02754 { 02755 char pager_name[FN_REFLEN], *end, *param; 02756 02757 if (status.batch) 02758 return 0; 02759 /* Skip spaces in front of the pager command */ 02760 while (my_isspace(charset_info, *line)) 02761 line++; 02762 /* Skip the pager command */ 02763 param= strchr(line, ' '); 02764 /* Skip the spaces between the command and the argument */ 02765 while (param && my_isspace(charset_info, *param)) 02766 param++; 02767 if (!param || !strlen(param)) // if pager was not given, use the default 02768 { 02769 if (!default_pager_set) 02770 { 02771 tee_fprintf(stdout, "Default pager wasn't set, using stdout.\n"); 02772 opt_nopager=1; 02773 strmov(pager, "stdout"); 02774 PAGER= stdout; 02775 return 0; 02776 } 02777 strmov(pager, default_pager); 02778 } 02779 else 02780 { 02781 end= strmake(pager_name, param, sizeof(pager_name)-1); 02782 while (end > pager_name && (my_isspace(charset_info,end[-1]) || 02783 my_iscntrl(charset_info,end[-1]))) 02784 end--; 02785 end[0]=0; 02786 strmov(pager, pager_name); 02787 strmov(default_pager, pager_name); 02788 } 02789 opt_nopager=0; 02790 tee_fprintf(stdout, "PAGER set to '%s'\n", pager); 02791 return 0; 02792 } 02793 02794 02795 static int 02796 com_nopager(String *buffer __attribute__((unused)), 02797 char *line __attribute__((unused))) 02798 { 02799 strmov(pager, "stdout"); 02800 opt_nopager=1; 02801 PAGER= stdout; 02802 tee_fprintf(stdout, "PAGER set to stdout\n"); 02803 return 0; 02804 } 02805 #endif 02806 02807 02808 /* 02809 Sorry, you can't send the result to an editor in Win32 02810 */ 02811 02812 #ifdef USE_POPEN 02813 static int 02814 com_edit(String *buffer,char *line __attribute__((unused))) 02815 { 02816 char filename[FN_REFLEN],buff[160]; 02817 int fd,tmp; 02818 const char *editor; 02819 02820 if ((fd=create_temp_file(filename,NullS,"sql", O_CREAT | O_WRONLY, 02821 MYF(MY_WME))) < 0) 02822 goto err; 02823 if (buffer->is_empty() && !old_buffer.is_empty()) 02824 (void) my_write(fd,(byte*) old_buffer.ptr(),old_buffer.length(), 02825 MYF(MY_WME)); 02826 else 02827 (void) my_write(fd,(byte*) buffer->ptr(),buffer->length(),MYF(MY_WME)); 02828 (void) my_close(fd,MYF(0)); 02829 02830 if (!(editor = (char *)getenv("EDITOR")) && 02831 !(editor = (char *)getenv("VISUAL"))) 02832 editor = "vi"; 02833 strxmov(buff,editor," ",filename,NullS); 02834 (void) system(buff); 02835 02836 MY_STAT stat_arg; 02837 if (!my_stat(filename,&stat_arg,MYF(MY_WME))) 02838 goto err; 02839 if ((fd = my_open(filename,O_RDONLY, MYF(MY_WME))) < 0) 02840 goto err; 02841 (void) buffer->alloc((uint) stat_arg.st_size); 02842 if ((tmp=read(fd,(char*) buffer->ptr(),buffer->alloced_length())) >= 0L) 02843 buffer->length((uint) tmp); 02844 else 02845 buffer->length(0); 02846 (void) my_close(fd,MYF(0)); 02847 (void) my_delete(filename,MYF(MY_WME)); 02848 err: 02849 return 0; 02850 } 02851 #endif 02852 02853 02854 /* If arg is given, exit without errors. This happens on command 'quit' */ 02855 02856 static int 02857 com_quit(String *buffer __attribute__((unused)), 02858 char *line __attribute__((unused))) 02859 { 02860 /* let the screen auto close on a normal shutdown */ 02861 NETWARE_SET_SCREEN_MODE(SCR_AUTOCLOSE_ON_EXIT); 02862 status.exit_status=0; 02863 return 1; 02864 } 02865 02866 static int 02867 com_rehash(String *buffer __attribute__((unused)), 02868 char *line __attribute__((unused))) 02869 { 02870 #ifdef HAVE_READLINE 02871 build_completion_hash(1, 0); 02872 #endif 02873 return 0; 02874 } 02875 02876 02877 #ifdef USE_POPEN 02878 static int 02879 com_shell(String *buffer, char *line __attribute__((unused))) 02880 { 02881 char *shell_cmd; 02882 02883 /* Skip space from line begin */ 02884 while (my_isspace(charset_info, *line)) 02885 line++; 02886 if (!(shell_cmd = strchr(line, ' '))) 02887 { 02888 put_info("Usage: \\! shell-command", INFO_ERROR); 02889 return -1; 02890 } 02891 /* 02892 The output of the shell command does not 02893 get directed to the pager or the outfile 02894 */ 02895 if (system(shell_cmd) == -1) 02896 { 02897 put_info(strerror(errno), INFO_ERROR, errno); 02898 return -1; 02899 } 02900 return 0; 02901 } 02902 #endif 02903 02904 02905 static int 02906 com_print(String *buffer,char *line __attribute__((unused))) 02907 { 02908 tee_puts("--------------", stdout); 02909 (void) tee_fputs(buffer->c_ptr(), stdout); 02910 if (!buffer->length() || (*buffer)[buffer->length()-1] != '\n') 02911 tee_putc('\n', stdout); 02912 tee_puts("--------------\n", stdout); 02913 return 0; /* If empty buffer */ 02914 } 02915 02916 /* ARGSUSED */ 02917 static int 02918 com_connect(String *buffer, char *line) 02919 { 02920 char *tmp, buff[256]; 02921 bool save_rehash= rehash; 02922 int error; 02923 02924 bzero(buff, sizeof(buff)); 02925 if (buffer) 02926 { 02927 strmake(buff, line, sizeof(buff)); 02928 tmp= get_arg(buff, 0); 02929 if (tmp && *tmp) 02930 { 02931 my_free(current_db, MYF(MY_ALLOW_ZERO_PTR)); 02932 current_db= my_strdup(tmp, MYF(MY_WME)); 02933 tmp= get_arg(buff, 1); 02934 if (tmp) 02935 { 02936 my_free(current_host,MYF(MY_ALLOW_ZERO_PTR)); 02937 current_host=my_strdup(tmp,MYF(MY_WME)); 02938 } 02939 } 02940 else 02941 rehash= 0; // Quick re-connect 02942 buffer->length(0); // command used 02943 } 02944 else 02945 rehash= 0; 02946 error=sql_connect(current_host,current_db,current_user,opt_password,0); 02947 rehash= save_rehash; 02948 02949 if (connected) 02950 { 02951 sprintf(buff,"Connection id: %lu",mysql_thread_id(&mysql)); 02952 put_info(buff,INFO_INFO); 02953 sprintf(buff,"Current database: %.128s\n", 02954 current_db ? current_db : "*** NONE ***"); 02955 put_info(buff,INFO_INFO); 02956 } 02957 return error; 02958 } 02959 02960 02961 static int com_source(String *buffer, char *line) 02962 { 02963 char source_name[FN_REFLEN], *end, *param; 02964 LINE_BUFFER *line_buff; 02965 int error; 02966 STATUS old_status; 02967 FILE *sql_file; 02968 02969 /* Skip space from file name */ 02970 while (my_isspace(charset_info,*line)) 02971 line++; 02972 if (!(param = strchr(line, ' '))) // Skip command name 02973 return put_info("Usage: \\. <filename> | source <filename>", 02974 INFO_ERROR, 0); 02975 while (my_isspace(charset_info,*param)) 02976 param++; 02977 end=strmake(source_name,param,sizeof(source_name)-1); 02978 while (end > source_name && (my_isspace(charset_info,end[-1]) || 02979 my_iscntrl(charset_info,end[-1]))) 02980 end--; 02981 end[0]=0; 02982 unpack_filename(source_name,source_name); 02983 /* open file name */ 02984 if (!(sql_file = my_fopen(source_name, O_RDONLY | O_BINARY,MYF(0)))) 02985 { 02986 char buff[FN_REFLEN+60]; 02987 sprintf(buff,"Failed to open file '%s', error: %d", source_name,errno); 02988 return put_info(buff, INFO_ERROR, 0); 02989 } 02990 02991 if (!(line_buff=batch_readline_init(opt_max_allowed_packet+512,sql_file))) 02992 { 02993 my_fclose(sql_file,MYF(0)); 02994 return put_info("Can't initialize batch_readline", INFO_ERROR, 0); 02995 } 02996 02997 /* Save old status */ 02998 old_status=status; 02999 bfill((char*) &status,sizeof(status),(char) 0); 03000 03001 status.batch=old_status.batch; // Run in batch mode 03002 status.line_buff=line_buff; 03003 status.file_name=source_name; 03004 glob_buffer.length(0); // Empty command buffer 03005 error= read_and_execute(false); 03006 status=old_status; // Continue as before 03007 my_fclose(sql_file,MYF(0)); 03008 batch_readline_end(line_buff); 03009 return error; 03010 } 03011 03012 03013 /* ARGSUSED */ 03014 static int 03015 com_delimiter(String *buffer __attribute__((unused)), char *line) 03016 { 03017 char buff[256], *tmp; 03018 03019 strmake(buff, line, sizeof(buff) - 1); 03020 tmp= get_arg(buff, 0); 03021 03022 if (!tmp || !*tmp) 03023 { 03024 put_info("DELIMITER must be followed by a 'delimiter' character or string", 03025 INFO_ERROR); 03026 return 0; 03027 } 03028 strmake(delimiter, tmp, sizeof(delimiter) - 1); 03029 delimiter_length= (int)strlen(delimiter); 03030 delimiter_str= delimiter; 03031 return 0; 03032 } 03033 03034 /* ARGSUSED */ 03035 static int 03036 com_use(String *buffer __attribute__((unused)), char *line) 03037 { 03038 char *tmp, buff[FN_REFLEN + 1]; 03039 int select_db; 03040 03041 bzero(buff, sizeof(buff)); 03042 strmov(buff, line); 03043 tmp= get_arg(buff, 0); 03044 if (!tmp || !*tmp) 03045 { 03046 put_info("USE must be followed by a database name", INFO_ERROR); 03047 return 0; 03048 } 03049 /* 03050 We need to recheck the current database, because it may change 03051 under our feet, for example if DROP DATABASE or RENAME DATABASE 03052 (latter one not yet available by the time the comment was written) 03053 */ 03054 get_current_db(); 03055 03056 if (!current_db || cmp_database(charset_info, current_db,tmp)) 03057 { 03058 if (one_database) 03059 { 03060 skip_updates= 1; 03061 select_db= 0; // don't do mysql_select_db() 03062 } 03063 else 03064 select_db= 2; // do mysql_select_db() and build_completion_hash() 03065 } 03066 else 03067 { 03068 /* 03069 USE to the current db specified. 03070 We do need to send mysql_select_db() to make server 03071 update database level privileges, which might 03072 change since last USE (see bug#10979). 03073 For performance purposes, we'll skip rebuilding of completion hash. 03074 */ 03075 skip_updates= 0; 03076 select_db= 1; // do only mysql_select_db(), without completion 03077 } 03078 03079 if (select_db) 03080 { 03081 /* 03082 reconnect once if connection is down or if connection was found to 03083 be down during query 03084 */ 03085 if (!connected && reconnect()) 03086 return opt_reconnect ? -1 : 1; // Fatal error 03087 if (mysql_select_db(&mysql,tmp)) 03088 { 03089 if (mysql_errno(&mysql) != CR_SERVER_GONE_ERROR) 03090 return put_error(&mysql); 03091 03092 if (reconnect()) 03093 return opt_reconnect ? -1 : 1; // Fatal error 03094 if (mysql_select_db(&mysql,tmp)) 03095 return put_error(&mysql); 03096 } 03097 my_free(current_db,MYF(MY_ALLOW_ZERO_PTR)); 03098 current_db=my_strdup(tmp,MYF(MY_WME)); 03099 #ifdef HAVE_READLINE 03100 if (select_db > 1) 03101 build_completion_hash(rehash, 1); 03102 #endif 03103 } 03104 03105 put_info("Database changed",INFO_INFO); 03106 return 0; 03107 } 03108 03109 static int 03110 com_warnings(String *buffer __attribute__((unused)), 03111 char *line __attribute__((unused))) 03112 { 03113 show_warnings = 1; 03114 put_info("Show warnings enabled.",INFO_INFO); 03115 return 0; 03116 } 03117 03118 static int 03119 com_nowarnings(String *buffer __attribute__((unused)), 03120 char *line __attribute__((unused))) 03121 { 03122 show_warnings = 0; 03123 put_info("Show warnings disabled.",INFO_INFO); 03124 return 0; 03125 } 03126 03127 /* 03128 Gets argument from a command on the command line. If get_next_arg is 03129 not defined, skips the command and returns the first argument. The 03130 line is modified by adding zero to the end of the argument. If 03131 get_next_arg is defined, then the function searches for end of string 03132 first, after found, returns the next argument and adds zero to the 03133 end. If you ever wish to use this feature, remember to initialize all 03134 items in the array to zero first. 03135 */ 03136 03137 char *get_arg(char *line, my_bool get_next_arg) 03138 { 03139 char *ptr, *start; 03140 my_bool quoted= 0, valid_arg= 0; 03141 char qtype= 0; 03142 03143 ptr= line; 03144 if (get_next_arg) 03145 { 03146 for (; *ptr; ptr++) ; 03147 if (*(ptr + 1)) 03148 ptr++; 03149 } 03150 else 03151 { 03152 /* skip leading white spaces */ 03153 while (my_isspace(charset_info, *ptr)) 03154 ptr++; 03155 if (*ptr == '\\') // short command was used 03156 ptr+= 2; 03157 else 03158 while (*ptr &&!my_isspace(charset_info, *ptr)) // skip command 03159 ptr++; 03160 } 03161 if (!*ptr) 03162 return NullS; 03163 while (my_isspace(charset_info, *ptr)) 03164 ptr++; 03165 if (*ptr == '\'' || *ptr == '\"' || *ptr == '`') 03166 { 03167 qtype= *ptr; 03168 quoted= 1; 03169 ptr++; 03170 } 03171 for (start=ptr ; *ptr; ptr++) 03172 { 03173 if (*ptr == '\\' && ptr[1]) // escaped character 03174 { 03175 // Remove the backslash 03176 strmov(ptr, ptr+1); 03177 } 03178 else if ((!quoted && *ptr == ' ') || (quoted && *ptr == qtype)) 03179 { 03180 *ptr= 0; 03181 break; 03182 } 03183 } 03184 valid_arg= ptr != start; 03185 return valid_arg ? start : NullS; 03186 } 03187 03188 03189 static int 03190 sql_real_connect(char *host,char *database,char *user,char *password, 03191 uint silent) 03192 { 03193 if (connected) 03194 { 03195 connected= 0; 03196 mysql_close(&mysql); 03197 } 03198 mysql_init(&mysql); 03199 if (opt_connect_timeout) 03200 { 03201 uint timeout=opt_connect_timeout; 03202 mysql_options(&mysql,MYSQL_OPT_CONNECT_TIMEOUT, 03203 (char*) &timeout); 03204 } 03205 if (opt_compress) 03206 mysql_options(&mysql,MYSQL_OPT_COMPRESS,NullS); 03207 if (opt_secure_auth) 03208 mysql_options(&mysql, MYSQL_SECURE_AUTH, (char *) &opt_secure_auth); 03209 if (using_opt_local_infile) 03210 mysql_options(&mysql,MYSQL_OPT_LOCAL_INFILE, (char*) &opt_local_infile); 03211 #ifdef HAVE_OPENSSL 03212 if (opt_use_ssl) 03213 mysql_ssl_set(&mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca, 03214 opt_ssl_capath, opt_ssl_cipher); 03215 mysql_options(&mysql,MYSQL_OPT_SSL_VERIFY_SERVER_CERT, 03216 (char*)&opt_ssl_verify_server_cert); 03217 #endif 03218 if (opt_protocol) 03219 mysql_options(&mysql,MYSQL_OPT_PROTOCOL,(char*)&opt_protocol); 03220 #ifdef HAVE_SMEM 03221 if (shared_memory_base_name) 03222 mysql_options(&mysql,MYSQL_SHARED_MEMORY_BASE_NAME,shared_memory_base_name); 03223 #endif 03224 if (safe_updates) 03225 { 03226 char init_command[100]; 03227 sprintf(init_command, 03228 "SET SQL_SAFE_UPDATES=1,SQL_SELECT_LIMIT=%lu,SQL_MAX_JOIN_SIZE=%lu", 03229 select_limit,max_join_size); 03230 mysql_options(&mysql, MYSQL_INIT_COMMAND, init_command); 03231 } 03232 if (default_charset_used) 03233 mysql_options(&mysql, MYSQL_SET_CHARSET_NAME, default_charset); 03234 if (!mysql_real_connect(&mysql, host, user, password, 03235 database, opt_mysql_port, opt_mysql_unix_port, 03236 connect_flag | CLIENT_MULTI_STATEMENTS)) 03237 { 03238 if (!silent || 03239 (mysql_errno(&mysql) != CR_CONN_HOST_ERROR && 03240 mysql_errno(&mysql) != CR_CONNECTION_ERROR)) 03241 { 03242 (void) put_error(&mysql); 03243 (void) fflush(stdout); 03244 return ignore_errors ? -1 : 1; // Abort 03245 } 03246 return -1; // Retryable 03247 } 03248 connected=1; 03249 #ifndef EMBEDDED_LIBRARY 03250 mysql.reconnect=info_flag ? 1 : 0; // We want to know if this happens 03251 #else 03252 mysql.reconnect= 1; 03253 #endif 03254 #ifdef HAVE_READLINE 03255 build_completion_hash(rehash, 1); 03256 #endif 03257 return 0; 03258 } 03259 03260 03261 static int 03262 sql_connect(char *host,char *database,char *user,char *password,uint silent) 03263 { 03264 bool message=0; 03265 uint count=0; 03266 int error; 03267 for (;;) 03268 { 03269 if ((error=sql_real_connect(host,database,user,password,wait_flag)) >= 0) 03270 { 03271 if (count) 03272 { 03273 tee_fputs("\n", stderr); 03274 (void) fflush(stderr); 03275 } 03276 return error; 03277 } 03278 if (!wait_flag) 03279 return ignore_errors ? -1 : 1; 03280 if (!message && !silent) 03281 { 03282 message=1; 03283 tee_fputs("Waiting",stderr); (void) fflush(stderr); 03284 } 03285 (void) sleep(wait_time); 03286 if (!silent) 03287 { 03288 putc('.',stderr); (void) fflush(stderr); 03289 count++; 03290 } 03291 } 03292 } 03293 03294 03295 03296 static int 03297 com_status(String *buffer __attribute__((unused)), 03298 char *line __attribute__((unused))) 03299 { 03300 const char *status; 03301 char buff[22]; 03302 ulonglong id; 03303 MYSQL_RES *result; 03304 LINT_INIT(result); 03305 03306 tee_puts("--------------", stdout); 03307 usage(1); /* Print version */ 03308 if (connected) 03309 { 03310 tee_fprintf(stdout, "\nConnection id:\t\t%lu\n",mysql_thread_id(&mysql)); 03311 /* 03312 Don't remove "limit 1", 03313 it is protection againts SQL_SELECT_LIMIT=0 03314 */ 03315 if (!mysql_query(&mysql,"select DATABASE(), USER() limit 1") && 03316 (result=mysql_use_result(&mysql))) 03317 { 03318 MYSQL_ROW cur=mysql_fetch_row(result); 03319 if (cur) 03320 { 03321 tee_fprintf(stdout, "Current database:\t%s\n", cur[0] ? cur[0] : ""); 03322 tee_fprintf(stdout, "Current user:\t\t%s\n", cur[1]); 03323 } 03324 mysql_free_result(result); 03325 } 03326 #ifdef HAVE_OPENSSL 03327 if ((status= mysql_get_ssl_cipher(&mysql))) 03328 tee_fprintf(stdout, "SSL:\t\t\tCipher in use is %s\n", 03329 status); 03330 else 03331 #endif /* HAVE_OPENSSL */ 03332 tee_puts("SSL:\t\t\tNot in use", stdout); 03333 } 03334 else 03335 { 03336 vidattr(A_BOLD); 03337 tee_fprintf(stdout, "\nNo connection\n"); 03338 vidattr(A_NORMAL); 03339 return 0; 03340 } 03341 if (skip_updates) 03342 { 03343 vidattr(A_BOLD); 03344 tee_fprintf(stdout, "\nAll updates ignored to this database\n"); 03345 vidattr(A_NORMAL); 03346 } 03347 #ifdef USE_POPEN 03348 tee_fprintf(stdout, "Current pager:\t\t%s\n", pager); 03349 tee_fprintf(stdout, "Using outfile:\t\t'%s'\n", opt_outfile ? outfile : ""); 03350 #endif 03351 tee_fprintf(stdout, "Using delimiter:\t%s\n", delimiter); 03352 tee_fprintf(stdout, "Server version:\t\t%s\n", mysql_get_server_info(&mysql)); 03353 tee_fprintf(stdout, "Protocol version:\t%d\n", mysql_get_proto_info(&mysql)); 03354 tee_fprintf(stdout, "Connection:\t\t%s\n", mysql_get_host_info(&mysql)); 03355 if ((id= mysql_insert_id(&mysql))) 03356 tee_fprintf(stdout, "Insert id:\t\t%s\n", llstr(id, buff)); 03357 03358 /* 03359 Don't remove "limit 1", 03360 it is protection againts SQL_SELECT_LIMIT=0 03361 */ 03362 if (!mysql_query(&mysql,"select @@character_set_client, @@character_set_connection, @@character_set_server, @@character_set_database limit 1") && 03363 (result=mysql_use_result(&mysql))) 03364 { 03365 MYSQL_ROW cur=mysql_fetch_row(result); 03366 if (cur) 03367 { 03368 tee_fprintf(stdout, "Server characterset:\t%s\n", cur[2] ? cur[2] : ""); 03369 tee_fprintf(stdout, "Db characterset:\t%s\n", cur[3] ? cur[3] : ""); 03370 tee_fprintf(stdout, "Client characterset:\t%s\n", cur[0] ? cur[0] : ""); 03371 tee_fprintf(stdout, "Conn. characterset:\t%s\n", cur[1] ? cur[1] : ""); 03372 } 03373 mysql_free_result(result); 03374 } 03375 else 03376 { 03377 /* Probably pre-4.1 server */ 03378 tee_fprintf(stdout, "Client characterset:\t%s\n", charset_info->csname); 03379 tee_fprintf(stdout, "Server characterset:\t%s\n", mysql.charset->csname); 03380 } 03381 03382 #ifndef EMBEDDED_LIBRARY 03383 if (strstr(mysql_get_host_info(&mysql),"TCP/IP") || ! mysql.unix_socket) 03384 tee_fprintf(stdout, "TCP port:\t\t%d\n", mysql.port); 03385 else 03386 tee_fprintf(stdout, "UNIX socket:\t\t%s\n", mysql.unix_socket); 03387 if (mysql.net.compress) 03388 tee_fprintf(stdout, "Protocol:\t\tCompressed\n"); 03389 #endif 03390 03391 if ((status=mysql_stat(&mysql)) && !mysql_error(&mysql)[0]) 03392 { 03393 ulong sec; 03394 char buff[40]; 03395 const char *pos= strchr(status,' '); 03396 /* print label */ 03397 tee_fprintf(stdout, "%.*s\t\t\t", (int) (pos-status), status); 03398 if ((status=str2int(pos,10,0,LONG_MAX,(long*) &sec))) 03399 { 03400 nice_time((double) sec,buff,0); 03401 tee_puts(buff, stdout); /* print nice time */ 03402 while (*status == ' ') status++; /* to next info */ 03403 } 03404 if (status) 03405 { 03406 tee_putc('\n', stdout); 03407 tee_puts(status, stdout); 03408 } 03409 } 03410 if (safe_updates) 03411 { 03412 vidattr(A_BOLD); 03413 tee_fprintf(stdout, "\nNote that you are running in safe_update_mode:\n"); 03414 vidattr(A_NORMAL); 03415 tee_fprintf(stdout, "\ 03416 UPDATEs and DELETEs that don't use a key in the WHERE clause are not allowed.\n\ 03417 (One can force an UPDATE/DELETE by adding LIMIT # at the end of the command.)\n\ 03418 SELECT has an automatic 'LIMIT %lu' if LIMIT is not used.\n\ 03419 Max number of examined row combination in a join is set to: %lu\n\n", 03420 select_limit, max_join_size); 03421 } 03422 tee_puts("--------------\n", stdout); 03423 return 0; 03424 } 03425 03426 03427 static int 03428 put_info(const char *str,INFO_TYPE info_type, uint error, const char *sqlstate) 03429 { 03430 FILE *file= (info_type == INFO_ERROR ? stderr : stdout); 03431 static int inited=0; 03432 03433 if (status.batch) 03434 { 03435 if (info_type == INFO_ERROR) 03436 { 03437 (void) fflush(file); 03438 fprintf(file,"ERROR"); 03439 if (error) 03440 { 03441 if (sqlstate) 03442 (void) fprintf(file," %d (%s)",error, sqlstate); 03443 else 03444 (void) fprintf(file," %d",error); 03445 } 03446 if (status.query_start_line && line_numbers) 03447 { 03448 (void) fprintf(file," at line %lu",status.query_start_line); 03449 if (status.file_name) 03450 (void) fprintf(file," in file: '%s'", status.file_name); 03451 } 03452 (void) fprintf(file,": %s\n",str); 03453 (void) fflush(file); 03454 if (!ignore_errors) 03455 return 1; 03456 } 03457 else if (info_type == INFO_RESULT && verbose > 1) 03458 tee_puts(str, file); 03459 if (unbuffered) 03460 fflush(file); 03461 return info_type == INFO_ERROR ? -1 : 0; 03462 } 03463 if (!opt_silent || info_type == INFO_ERROR) 03464 { 03465 if (!inited) 03466 { 03467 inited=1; 03468 #ifdef HAVE_SETUPTERM 03469 (void) setupterm((char *)0, 1, (int *) 0); 03470 #endif 03471 } 03472 if (info_type == INFO_ERROR) 03473 { 03474 if (!opt_nobeep) 03475 putchar('\a'); /* This should make a bell */ 03476 vidattr(A_STANDOUT); 03477 if (error) 03478 { 03479 if (sqlstate) 03480 (void) tee_fprintf(file, "ERROR %d (%s): ", error, sqlstate); 03481 else 03482 (void) tee_fprintf(file, "ERROR %d: ", error); 03483 } 03484 else 03485 tee_puts("ERROR: ", file); 03486 } 03487 else 03488 vidattr(A_BOLD); 03489 (void) tee_puts(str, file); 03490 vidattr(A_NORMAL); 03491 } 03492 if (unbuffered) 03493 fflush(file); 03494 return info_type == INFO_ERROR ? -1 : 0; 03495 } 03496 03497 03498 static int 03499 put_error(MYSQL *mysql) 03500 { 03501 return put_info(mysql_error(mysql), INFO_ERROR, mysql_errno(mysql), 03502 mysql_sqlstate(mysql)); 03503 } 03504 03505 03506 static void remove_cntrl(String &buffer) 03507 { 03508 char *start,*end; 03509 end=(start=(char*) buffer.ptr())+buffer.length(); 03510 while (start < end && !my_isgraph(charset_info,end[-1])) 03511 end--; 03512 buffer.length((uint) (end-start)); 03513 } 03514 03515 03516 void tee_fprintf(FILE *file, const char *fmt, ...) 03517 { 03518 va_list args; 03519 03520 NETWARE_YIELD; 03521 va_start(args, fmt); 03522 (void) vfprintf(file, fmt, args); 03523 va_end(args); 03524 03525 if (opt_outfile) 03526 { 03527 va_start(args, fmt); 03528 (void) vfprintf(OUTFILE, fmt, args); 03529 va_end(args); 03530 } 03531 } 03532 03533 03534 void tee_fputs(const char *s, FILE *file) 03535 { 03536 NETWARE_YIELD; 03537 fputs(s, file); 03538 if (opt_outfile) 03539 fputs(s, OUTFILE); 03540 } 03541 03542 03543 void tee_puts(const char *s, FILE *file) 03544 { 03545 NETWARE_YIELD; 03546 fputs(s, file); 03547 fputs("\n", file); 03548 if (opt_outfile) 03549 { 03550 fputs(s, OUTFILE); 03551 fputs("\n", OUTFILE); 03552 } 03553 } 03554 03555 void tee_putc(int c, FILE *file) 03556 { 03557 putc(c, file); 03558 if (opt_outfile) 03559 putc(c, OUTFILE); 03560 } 03561 03562 #if defined( __WIN__) || defined(__NETWARE__) 03563 #include <time.h> 03564 #else 03565 #include <sys/times.h> 03566 #ifdef _SC_CLK_TCK // For mit-pthreads 03567 #undef CLOCKS_PER_SEC 03568 #define CLOCKS_PER_SEC (sysconf(_SC_CLK_TCK)) 03569 #endif 03570 #endif 03571 03572 static ulong start_timer(void) 03573 { 03574 #if defined( __WIN__) || defined(__NETWARE__) 03575 return clock(); 03576 #else 03577 struct tms tms_tmp; 03578 return times(&tms_tmp); 03579 #endif 03580 } 03581 03582 03583 static void nice_time(double sec,char *buff,bool part_second) 03584 { 03585 ulong tmp; 03586 if (sec >= 3600.0*24) 03587 { 03588 tmp=(ulong) floor(sec/(3600.0*24)); 03589 sec-=3600.0*24*tmp; 03590 buff=int10_to_str((long) tmp, buff, 10); 03591 buff=strmov(buff,tmp > 1 ? " days " : " day "); 03592 } 03593 if (sec >= 3600.0) 03594 { 03595 tmp=(ulong) floor(sec/3600.0); 03596 sec-=3600.0*tmp; 03597 buff=int10_to_str((long) tmp, buff, 10); 03598 buff=strmov(buff,tmp > 1 ? " hours " : " hour "); 03599 } 03600 if (sec >= 60.0) 03601 { 03602 tmp=(ulong) floor(sec/60.0); 03603 sec-=60.0*tmp; 03604 buff=int10_to_str((long) tmp, buff, 10); 03605 buff=strmov(buff," min "); 03606 } 03607 if (part_second) 03608 sprintf(buff,"%.2f sec",sec); 03609 else 03610 sprintf(buff,"%d sec",(int) sec); 03611 } 03612 03613 03614 static void end_timer(ulong start_time,char *buff) 03615 { 03616 nice_time((double) (start_timer() - start_time) / 03617 CLOCKS_PER_SEC,buff,1); 03618 } 03619 03620 03621 static void mysql_end_timer(ulong start_time,char *buff) 03622 { 03623 buff[0]=' '; 03624 buff[1]='('; 03625 end_timer(start_time,buff+2); 03626 strmov(strend(buff),")"); 03627 } 03628 03629 static const char* construct_prompt() 03630 { 03631 processed_prompt.free(); // Erase the old prompt 03632 time_t lclock = time(NULL); // Get the date struct 03633 struct tm *t = localtime(&lclock); 03634 03635 /* parse thru the settings for the prompt */ 03636 for (char *c = current_prompt; *c ; *c++) 03637 { 03638 if (*c != PROMPT_CHAR) 03639 processed_prompt.append(*c); 03640 else 03641 { 03642 switch (*++c) { 03643 case '\0': 03644 c--; // stop it from going beyond if ends with % 03645 break; 03646 case 'c': 03647 add_int_to_prompt(++prompt_counter); 03648 break; 03649 case 'v': 03650 if (connected) 03651 processed_prompt.append(mysql_get_server_info(&mysql)); 03652 else 03653 processed_prompt.append("not_connected"); 03654 break; 03655 case 'd': 03656 processed_prompt.append(current_db ? current_db : "(none)"); 03657 break; 03658 case 'h': 03659 { 03660 const char *prompt; 03661 prompt= connected ? mysql_get_host_info(&mysql) : "not_connected"; 03662 if (strstr(prompt, "Localhost")) 03663 processed_prompt.append("localhost"); 03664 else 03665 { 03666 const char *end=strcend(prompt,' '); 03667 processed_prompt.append(prompt, (uint) (end-prompt)); 03668 } 03669 break; 03670 } 03671 case 'p': 03672 { 03673 #ifndef EMBEDDED_LIBRARY 03674 if (!connected) 03675 { 03676 processed_prompt.append("not_connected"); 03677 break; 03678 } 03679 03680 const char *host_info = mysql_get_host_info(&mysql); 03681 if (strstr(host_info, "memory")) 03682 { 03683 processed_prompt.append( mysql.host ); 03684 } 03685 else if (strstr(host_info,"TCP/IP") || 03686 !mysql.unix_socket) 03687 add_int_to_prompt(mysql.port); 03688 else 03689 { 03690 char *pos=strrchr(mysql.unix_socket,'/'); 03691 processed_prompt.append(pos ? pos+1 : mysql.unix_socket); 03692 } 03693 #endif 03694 } 03695 break; 03696 case 'U': 03697 if (!full_username) 03698 init_username(); 03699 processed_prompt.append(full_username ? full_username : 03700 (current_user ? current_user : "(unknown)")); 03701 break; 03702 case 'u': 03703 if (!full_username) 03704 init_username(); 03705 processed_prompt.append(part_username ? part_username : 03706 (current_user ? current_user : "(unknown)")); 03707 break; 03708 case PROMPT_CHAR: 03709 processed_prompt.append(PROMPT_CHAR); 03710 break; 03711 case 'n': 03712 processed_prompt.append('\n'); 03713 break; 03714 case ' ': 03715 case '_': 03716 processed_prompt.append(' '); 03717 break; 03718 case 'R': 03719 if (t->tm_hour < 10) 03720 processed_prompt.append('0'); 03721 add_int_to_prompt(t->tm_hour); 03722 break; 03723 case 'r': 03724 int getHour; 03725 getHour = t->tm_hour % 12; 03726 if (getHour == 0) 03727 getHour=12; 03728 if (getHour < 10) 03729 processed_prompt.append('0'); 03730 add_int_to_prompt(getHour); 03731 break; 03732 case 'm': 03733 if (t->tm_min < 10) 03734 processed_prompt.append('0'); 03735 add_int_to_prompt(t->tm_min); 03736 break; 03737 case 'y': 03738 int getYear; 03739 getYear = t->tm_year % 100; 03740 if (getYear < 10) 03741 processed_prompt.append('0'); 03742 add_int_to_prompt(getYear); 03743 break; 03744 case 'Y': 03745 add_int_to_prompt(t->tm_year+1900); 03746 break; 03747 case 'D': 03748 char* dateTime; 03749 time_t lclock; 03750 lclock = time(NULL); 03751 dateTime = ctime(&lclock); 03752 processed_prompt.append(strtok(dateTime,"\n")); 03753 break; 03754 case 's': 03755 if (t->tm_sec < 10) 03756 processed_prompt.append('0'); 03757 add_int_to_prompt(t->tm_sec); 03758 break; 03759 case 'w': 03760 processed_prompt.append(day_names[t->tm_wday]); 03761 break; 03762 case 'P': 03763 processed_prompt.append(t->tm_hour < 12 ? "am" : "pm"); 03764 break; 03765 case 'o': 03766 add_int_to_prompt(t->tm_mon+1); 03767 break; 03768 case 'O': 03769 processed_prompt.append(month_names[t->tm_mon]); 03770 break; 03771 case '\'': 03772 processed_prompt.append("'"); 03773 break; 03774 case '"': 03775 processed_prompt.append('"'); 03776 break; 03777 case 'S': 03778 processed_prompt.append(';'); 03779 break; 03780 case 't': 03781 processed_prompt.append('\t'); 03782 break; 03783 case 'l': 03784 processed_prompt.append(delimiter_str); 03785 break; 03786 default: 03787 processed_prompt.append(c); 03788 } 03789 } 03790 } 03791 processed_prompt.append('\0'); 03792 return processed_prompt.ptr(); 03793 } 03794 03795 03796 static void add_int_to_prompt(int toadd) 03797 { 03798 char buffer[16]; 03799 int10_to_str(toadd,buffer,10); 03800 processed_prompt.append(buffer); 03801 } 03802 03803 static void init_username() 03804 { 03805 my_free(full_username,MYF(MY_ALLOW_ZERO_PTR)); 03806 my_free(part_username,MYF(MY_ALLOW_ZERO_PTR)); 03807 03808 MYSQL_RES *result; 03809 LINT_INIT(result); 03810 if (!mysql_query(&mysql,"select USER()") && 03811 (result=mysql_use_result(&mysql))) 03812 { 03813 MYSQL_ROW cur=mysql_fetch_row(result); 03814 full_username=my_strdup(cur[0],MYF(MY_WME)); 03815 part_username=my_strdup(strtok(cur[0],"@"),MYF(MY_WME)); 03816 (void) mysql_fetch_row(result); // Read eof 03817 } 03818 } 03819 03820 static int com_prompt(String *buffer, char *line) 03821 { 03822 char *ptr=strchr(line, ' '); 03823 prompt_counter = 0; 03824 my_free(current_prompt,MYF(MY_ALLOW_ZERO_PTR)); 03825 current_prompt=my_strdup(ptr ? ptr+1 : default_prompt,MYF(MY_WME)); 03826 if (!ptr) 03827 tee_fprintf(stdout, "Returning to default PROMPT of %s\n", default_prompt); 03828 else 03829 tee_fprintf(stdout, "PROMPT set to '%s'\n", current_prompt); 03830 return 0; 03831 } 03832 03833 #ifndef EMBEDDED_LIBRARY 03834 /* Keep sql_string library happy */ 03835 03836 gptr sql_alloc(unsigned int Size) 03837 { 03838 return my_malloc(Size,MYF(MY_WME)); 03839 } 03840 03841 void sql_element_free(void *ptr) 03842 { 03843 my_free((gptr) ptr,MYF(0)); 03844 } 03845 #endif /* EMBEDDED_LIBRARY */
1.4.7

