1 /*
2    Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License, version 2.0,
6    as published by the Free Software Foundation.
7 
8    This program is also distributed with certain software (including
9    but not limited to OpenSSL) that is licensed under separate terms,
10    as designated in a particular file or component or in included license
11    documentation.  The authors of MySQL hereby grant you an additional
12    permission to link the program and your derivative works with the
13    separately licensed software that they have included with MySQL.
14 
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License, version 2.0, for more details.
19 
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
23 */
24 
25 /* mysql command tool
26  * Commands compatible with mSQL by David J. Hughes
27  *
28  * Written by:
29  *   Michael 'Monty' Widenius
30  *   Andi Gutmans  <andi@zend.com>
31  *   Zeev Suraski  <zeev@zend.com>
32  *   Jani Tolonen  <jani@mysql.com>
33  *   Matt Wagner   <matt@mysql.com>
34  *   Jeremy Cole   <jcole@mysql.com>
35  *   Tonu Samuel   <tonu@mysql.com>
36  *   Harrison Fisk <harrison@mysql.com>
37  *
38  **/
39 
40 #include "client_priv.h"
41 #include "my_default.h"
42 #include <m_ctype.h>
43 #include <stdarg.h>
44 #include <my_dir.h>
45 #ifndef __GNU_LIBRARY__
46 #define __GNU_LIBRARY__		      // Skip warnings in getopt.h
47 #endif
48 #include "my_readline.h"
49 #include <signal.h>
50 #include <violite.h>
51 
52 #include <algorithm>
53 
54 using std::min;
55 using std::max;
56 
57 #if defined(USE_LIBEDIT_INTERFACE) && defined(HAVE_LOCALE_H)
58 #include <locale.h>
59 #endif
60 
61 const char *VER= "14.14";
62 
63 /* Don't try to make a nice table if the data is too big */
64 #define MAX_COLUMN_LENGTH	     1024
65 
66 /* Buffer to hold 'version' and 'version_comment' */
67 static char *server_version= NULL;
68 
69 /* Array of options to pass to libemysqld */
70 #define MAX_SERVER_ARGS               64
71 
72 /* Maximum memory limit that can be claimed by alloca(). */
73 #define MAX_ALLOCA_SIZE              512
74 
75 #include "sql_string.h"
76 
77 extern "C" {
78 #if defined(HAVE_CURSES_H) && defined(HAVE_TERM_H)
79 #include <curses.h>
80 #include <term.h>
81 #else
82 #if defined(HAVE_TERMIOS_H)
83 #include <termios.h>
84 #include <unistd.h>
85 #elif defined(HAVE_TERMBITS_H)
86 #include <termbits.h>
87 #elif defined(HAVE_ASM_TERMBITS_H) && (!defined __GLIBC__ || !(__GLIBC__ > 2 || __GLIBC__ == 2 && __GLIBC_MINOR__ > 0))
88 #include <asm/termbits.h>		// Standard linux
89 #endif
90 #undef VOID
91 #if defined(HAVE_TERMCAP_H)
92 #include <termcap.h>
93 #else
94 #ifdef HAVE_CURSES_H
95 #include <curses.h>
96 #endif
97 #undef SYSV				// hack to avoid syntax error
98 #ifdef HAVE_TERM_H
99 #include <term.h>
100 #endif
101 #endif
102 #endif
103 
104 #if defined(__WIN__)
105 #include <conio.h>
106 #else
107 #include <readline.h>
108 #define HAVE_READLINE
109 #define USE_POPEN
110 #endif
111   //int vidattr(long unsigned int attrs);	// Was missing in sun curses
112 }
113 
114 #if !defined(HAVE_VIDATTR)
115 #undef vidattr
116 #define vidattr(A) {}			// Can't get this to work
117 #endif
118 
119 #ifdef FN_NO_CASE_SENSE
120 #define cmp_database(cs,A,B) my_strcasecmp((cs), (A), (B))
121 #else
122 #define cmp_database(cs,A,B) strcmp((A),(B))
123 #endif
124 
125 #include "completion_hash.h"
126 #include <welcome_copyright_notice.h> // ORACLE_WELCOME_COPYRIGHT_NOTICE
127 
128 #define PROMPT_CHAR '\\'
129 #define DEFAULT_DELIMITER ";"
130 
131 #define MAX_BATCH_BUFFER_SIZE (1024L * 1024L * 1024L)
132 
133 typedef struct st_status
134 {
135   int exit_status;
136   ulong query_start_line;
137   char *file_name;
138   LINE_BUFFER *line_buff;
139   bool batch,add_to_history;
140 } STATUS;
141 
142 
143 static HashTable ht;
144 static char **defaults_argv;
145 
146 enum enum_info_type { INFO_INFO,INFO_ERROR,INFO_RESULT};
147 typedef enum enum_info_type INFO_TYPE;
148 
149 static MYSQL mysql;			/* The connection */
150 static my_bool ignore_errors=0,wait_flag=0,quick=0,
151                connected=0,opt_raw_data=0,unbuffered=0,output_tables=0,
152 	       opt_rehash=1,skip_updates=0,safe_updates=0,one_database=0,
153 	       opt_compress=0, using_opt_local_infile=0,
154 	       vertical=0, line_numbers=1, column_names=1,opt_html=0,
155                opt_xml=0,opt_nopager=1, opt_outfile=0, named_cmds= 0,
156 	       tty_password= 0, opt_nobeep=0, opt_reconnect=1,
157 	       opt_secure_auth= TRUE,
158                default_pager_set= 0, opt_sigint_ignore= 0,
159                auto_vertical_output= 0,
160                show_warnings= 0, executing_query= 0, interrupted_query= 0,
161                ignore_spaces= 0, opt_binhex= 0;
162 static my_bool debug_info_flag, debug_check_flag;
163 static my_bool column_types_flag;
164 static my_bool preserve_comments= 0;
165 static ulong opt_max_allowed_packet, opt_net_buffer_length;
166 static uint verbose=0,opt_silent=0,opt_mysql_port=0, opt_local_infile=0;
167 static uint opt_enable_cleartext_plugin= 0;
168 static my_bool using_opt_enable_cleartext_plugin= 0;
169 static uint my_end_arg;
170 static char * opt_mysql_unix_port=0;
171 static char *opt_bind_addr = NULL;
172 static int connect_flag=CLIENT_INTERACTIVE;
173 static my_bool opt_binary_mode= FALSE;
174 static my_bool opt_connect_expired_password= FALSE;
175 static char *current_host,*current_db,*current_user=0,*opt_password=0,
176             *current_prompt=0, *delimiter_str= 0,
177             *default_charset= (char*) MYSQL_AUTODETECT_CHARSET_NAME,
178             *opt_init_command= 0;
179 static char *histfile;
180 static char *histfile_tmp;
181 static char *opt_histignore= NULL;
182 DYNAMIC_STRING histignore_buffer;
183 static String glob_buffer,old_buffer;
184 static String processed_prompt;
185 static char *full_username=0,*part_username=0,*default_prompt=0;
186 static int wait_time = 5;
187 static STATUS status;
188 static ulong select_limit,max_join_size,opt_connect_timeout=0;
189 static char mysql_charsets_dir[FN_REFLEN+1];
190 static char *opt_plugin_dir= 0, *opt_default_auth= 0;
191 static char *opt_server_public_key= 0;
192 static const char *xmlmeta[] = {
193   "&", "&amp;",
194   "<", "&lt;",
195   ">", "&gt;",
196   "\"", "&quot;",
197   /* Turn \0 into a space. Why not &#0;? That's not valid XML or HTML. */
198   "\0", " ",
199   0, 0
200 };
201 static const char *day_names[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
202 static const char *month_names[]={"Jan","Feb","Mar","Apr","May","Jun","Jul",
203 			    "Aug","Sep","Oct","Nov","Dec"};
204 static char default_pager[FN_REFLEN];
205 static char pager[FN_REFLEN], outfile[FN_REFLEN];
206 static FILE *PAGER, *OUTFILE;
207 static MEM_ROOT hash_mem_root;
208 static uint prompt_counter;
209 static char delimiter[16]= DEFAULT_DELIMITER;
210 static uint delimiter_length= 1;
211 unsigned short terminal_width= 80;
212 
213 #ifdef HAVE_SMEM
214 static char *shared_memory_base_name=0;
215 #endif
216 static uint opt_protocol=0;
217 static const CHARSET_INFO *charset_info= &my_charset_latin1;
218 
219 #include "sslopt-vars.h"
220 
221 const char *default_dbug_option="d:t:o,/tmp/mysql.trace";
222 
223 #ifdef __WIN__
224 /*
225   A flag that indicates if --execute buffer has already been converted,
226   to avoid double conversion on reconnect.
227 */
228 static my_bool execute_buffer_conversion_done= 0;
229 
230 /*
231   my_win_is_console(...) is quite slow.
232   We cache my_win_is_console() results for stdout and stderr.
233   Any other output files, except stdout and stderr,
234   cannot be Windows console.
235   Note, if mysql.exe is executed from a service, its _fileno(stdout) is -1,
236   so shift (1 << -1) can return implementation defined result.
237   This corner case is taken into account, as the shift result
238   will be multiplied to 0 and we'll get 0 as a result.
239   The same is true for stderr.
240 */
241 static uint win_is_console_cache=
242   (MY_TEST(my_win_is_console(stdout)) * (1 << _fileno(stdout))) |
243   (MY_TEST(my_win_is_console(stderr)) * (1 << _fileno(stderr)));
244 
245 static inline my_bool
my_win_is_console_cached(FILE * file)246 my_win_is_console_cached(FILE *file)
247 {
248   return win_is_console_cache & (1 << _fileno(file));
249 }
250 #endif /* __WIN__ */
251 
252 /* Various printing flags */
253 #define MY_PRINT_ESC_0 1  /* Replace 0x00 bytes to "\0"              */
254 #define MY_PRINT_SPS_0 2  /* Replace 0x00 bytes to space             */
255 #define MY_PRINT_XML   4  /* Encode XML entities                     */
256 #define MY_PRINT_MB    8  /* Recognize multi-byte characters         */
257 #define MY_PRINT_CTRL 16  /* Replace TAB, NL, CR to "\t", "\n", "\r" */
258 
259 void tee_write(FILE *file, const char *s, size_t slen, int flags);
260 void tee_fprintf(FILE *file, const char *fmt, ...);
261 void tee_fputs(const char *s, FILE *file);
262 void tee_puts(const char *s, FILE *file);
263 void tee_putc(int c, FILE *file);
264 static void tee_print_sized_data(const char *, unsigned int, unsigned int, bool);
265 /* The names of functions that actually do the manipulation. */
266 static int get_options(int argc,char **argv);
267 extern "C" my_bool get_one_option(int optid, const struct my_option *opt,
268                                   char *argument);
269 static int com_quit(String *str,char*),
270 	   com_go(String *str,char*), com_ego(String *str,char*),
271 	   com_print(String *str,char*),
272 	   com_help(String *str,char*), com_clear(String *str,char*),
273 	   com_connect(String *str,char*), com_status(String *str,char*),
274 	   com_use(String *str,char*), com_source(String *str, char*),
275 	   com_rehash(String *str, char*), com_tee(String *str, char*),
276            com_notee(String *str, char*), com_charset(String *str,char*),
277            com_prompt(String *str, char*), com_delimiter(String *str, char*),
278      com_warnings(String *str, char*), com_nowarnings(String *str, char*);
279 
280 #ifdef USE_POPEN
281 static int com_nopager(String *str, char*), com_pager(String *str, char*),
282            com_edit(String *str,char*), com_shell(String *str, char *);
283 #endif
284 
285 static int read_and_execute(bool interactive);
286 static int sql_connect(char *host,char *database,char *user,char *password,
287 		       uint silent);
288 static const char *server_version_string(MYSQL *mysql);
289 static int put_info(const char *str,INFO_TYPE info,uint error=0,
290 		    const char *sql_state=0);
291 static int put_error(MYSQL *mysql);
292 static void safe_put_field(const char *pos,ulong length);
293 static void xmlencode_print(const char *src, uint length);
294 static void init_pager();
295 static void end_pager();
296 static void init_tee(const char *);
297 static void end_tee();
298 static const char* construct_prompt();
299 static char *get_arg(char *line, my_bool get_next_arg);
300 static void init_username();
301 static void add_int_to_prompt(int toadd);
302 static int get_result_width(MYSQL_RES *res);
303 static int get_field_disp_length(MYSQL_FIELD * field);
304 static int normalize_dbname(const char *line, char *buff, uint buff_size);
305 static int get_quote_count(const char *line);
306 
307 #if defined(HAVE_READLINE)
308 static void add_filtered_history(const char *string);
309 static my_bool check_histignore(const char *string);
310 static my_bool parse_histignore();
311 static my_bool init_hist_patterns();
312 static void free_hist_patterns();
313 DYNAMIC_ARRAY histignore_patterns;
314 #endif                                          /* HAVE_READLINE */
315 
316 /* A structure which contains information on the commands this program
317    can understand. */
318 
319 typedef struct {
320   const char *name;		/* User printable name of the function. */
321   char cmd_char;		/* msql command character */
322   int (*func)(String *str,char *); /* Function to call to do the job. */
323   bool takes_params;		/* Max parameters for command */
324   const char *doc;		/* Documentation for this function.  */
325 } COMMANDS;
326 
327 static COMMANDS commands[] = {
328   { "?",      '?', com_help,   1, "Synonym for `help'." },
329   { "clear",  'c', com_clear,  0, "Clear the current input statement."},
330   { "connect",'r', com_connect,1,
331     "Reconnect to the server. Optional arguments are db and host." },
332   { "delimiter", 'd', com_delimiter,    1,
333     "Set statement delimiter." },
334 #ifdef USE_POPEN
335   { "edit",   'e', com_edit,   0, "Edit command with $EDITOR."},
336 #endif
337   { "ego",    'G', com_ego,    0,
338     "Send command to mysql server, display result vertically."},
339   { "exit",   'q', com_quit,   0, "Exit mysql. Same as quit."},
340   { "go",     'g', com_go,     0, "Send command to mysql server." },
341   { "help",   'h', com_help,   1, "Display this help." },
342 #ifdef USE_POPEN
343   { "nopager",'n', com_nopager,0, "Disable pager, print to stdout." },
344 #endif
345   { "notee",  't', com_notee,  0, "Don't write into outfile." },
346 #ifdef USE_POPEN
347   { "pager",  'P', com_pager,  1,
348     "Set PAGER [to_pager]. Print the query results via PAGER." },
349 #endif
350   { "print",  'p', com_print,  0, "Print current command." },
351   { "prompt", 'R', com_prompt, 1, "Change your mysql prompt."},
352   { "quit",   'q', com_quit,   0, "Quit mysql." },
353   { "rehash", '#', com_rehash, 0, "Rebuild completion hash." },
354   { "source", '.', com_source, 1,
355     "Execute an SQL script file. Takes a file name as an argument."},
356   { "status", 's', com_status, 0, "Get status information from the server."},
357 #ifdef USE_POPEN
358   { "system", '!', com_shell,  1, "Execute a system shell command."},
359 #endif
360   { "tee",    'T', com_tee,    1,
361     "Set outfile [to_outfile]. Append everything into given outfile." },
362   { "use",    'u', com_use,    1,
363     "Use another database. Takes database name as argument." },
364   { "charset",    'C', com_charset,    1,
365     "Switch to another charset. Might be needed for processing binlog with multi-byte charsets." },
366   { "warnings", 'W', com_warnings,  0,
367     "Show warnings after every statement." },
368   { "nowarning", 'w', com_nowarnings, 0,
369     "Don't show warnings after every statement." },
370   /* Get bash-like expansion for some commands */
371   { "create table",     0, 0, 0, ""},
372   { "create database",  0, 0, 0, ""},
373   { "show databases",   0, 0, 0, ""},
374   { "show fields from", 0, 0, 0, ""},
375   { "show keys from",   0, 0, 0, ""},
376   { "show tables",      0, 0, 0, ""},
377   { "load data from",   0, 0, 0, ""},
378   { "alter table",      0, 0, 0, ""},
379   { "set option",       0, 0, 0, ""},
380   { "lock tables",      0, 0, 0, ""},
381   { "unlock tables",    0, 0, 0, ""},
382   /* generated 2006-12-28.  Refresh occasionally from lexer. */
383   { "ACTION", 0, 0, 0, ""},
384   { "ADD", 0, 0, 0, ""},
385   { "AFTER", 0, 0, 0, ""},
386   { "AGAINST", 0, 0, 0, ""},
387   { "AGGREGATE", 0, 0, 0, ""},
388   { "ALL", 0, 0, 0, ""},
389   { "ALGORITHM", 0, 0, 0, ""},
390   { "ALTER", 0, 0, 0, ""},
391   { "ANALYZE", 0, 0, 0, ""},
392   { "AND", 0, 0, 0, ""},
393   { "ANY", 0, 0, 0, ""},
394   { "AS", 0, 0, 0, ""},
395   { "ASC", 0, 0, 0, ""},
396   { "ASCII", 0, 0, 0, ""},
397   { "ASENSITIVE", 0, 0, 0, ""},
398   { "AUTO_INCREMENT", 0, 0, 0, ""},
399   { "AVG", 0, 0, 0, ""},
400   { "AVG_ROW_LENGTH", 0, 0, 0, ""},
401   { "BACKUP", 0, 0, 0, ""},
402   { "BDB", 0, 0, 0, ""},
403   { "BEFORE", 0, 0, 0, ""},
404   { "BEGIN", 0, 0, 0, ""},
405   { "BERKELEYDB", 0, 0, 0, ""},
406   { "BETWEEN", 0, 0, 0, ""},
407   { "BIGINT", 0, 0, 0, ""},
408   { "BINARY", 0, 0, 0, ""},
409   { "BINLOG", 0, 0, 0, ""},
410   { "BIT", 0, 0, 0, ""},
411   { "BLOB", 0, 0, 0, ""},
412   { "BOOL", 0, 0, 0, ""},
413   { "BOOLEAN", 0, 0, 0, ""},
414   { "BOTH", 0, 0, 0, ""},
415   { "BTREE", 0, 0, 0, ""},
416   { "BY", 0, 0, 0, ""},
417   { "BYTE", 0, 0, 0, ""},
418   { "CACHE", 0, 0, 0, ""},
419   { "CALL", 0, 0, 0, ""},
420   { "CASCADE", 0, 0, 0, ""},
421   { "CASCADED", 0, 0, 0, ""},
422   { "CASE", 0, 0, 0, ""},
423   { "CHAIN", 0, 0, 0, ""},
424   { "CHANGE", 0, 0, 0, ""},
425   { "CHANGED", 0, 0, 0, ""},
426   { "CHAR", 0, 0, 0, ""},
427   { "CHARACTER", 0, 0, 0, ""},
428   { "CHARSET", 0, 0, 0, ""},
429   { "CHECK", 0, 0, 0, ""},
430   { "CHECKSUM", 0, 0, 0, ""},
431   { "CIPHER", 0, 0, 0, ""},
432   { "CLIENT", 0, 0, 0, ""},
433   { "CLOSE", 0, 0, 0, ""},
434   { "CODE", 0, 0, 0, ""},
435   { "COLLATE", 0, 0, 0, ""},
436   { "COLLATION", 0, 0, 0, ""},
437   { "COLUMN", 0, 0, 0, ""},
438   { "COLUMNS", 0, 0, 0, ""},
439   { "COMMENT", 0, 0, 0, ""},
440   { "COMMIT", 0, 0, 0, ""},
441   { "COMMITTED", 0, 0, 0, ""},
442   { "COMPACT", 0, 0, 0, ""},
443   { "COMPRESSED", 0, 0, 0, ""},
444   { "CONCURRENT", 0, 0, 0, ""},
445   { "CONDITION", 0, 0, 0, ""},
446   { "CONNECTION", 0, 0, 0, ""},
447   { "CONSISTENT", 0, 0, 0, ""},
448   { "CONSTRAINT", 0, 0, 0, ""},
449   { "CONTAINS", 0, 0, 0, ""},
450   { "CONTINUE", 0, 0, 0, ""},
451   { "CONVERT", 0, 0, 0, ""},
452   { "CREATE", 0, 0, 0, ""},
453   { "CROSS", 0, 0, 0, ""},
454   { "CUBE", 0, 0, 0, ""},
455   { "CURRENT_DATE", 0, 0, 0, ""},
456   { "CURRENT_TIME", 0, 0, 0, ""},
457   { "CURRENT_TIMESTAMP", 0, 0, 0, ""},
458   { "CURRENT_USER", 0, 0, 0, ""},
459   { "CURSOR", 0, 0, 0, ""},
460   { "DATA", 0, 0, 0, ""},
461   { "DATABASE", 0, 0, 0, ""},
462   { "DATABASES", 0, 0, 0, ""},
463   { "DATE", 0, 0, 0, ""},
464   { "DATETIME", 0, 0, 0, ""},
465   { "DAY", 0, 0, 0, ""},
466   { "DAY_HOUR", 0, 0, 0, ""},
467   { "DAY_MICROSECOND", 0, 0, 0, ""},
468   { "DAY_MINUTE", 0, 0, 0, ""},
469   { "DAY_SECOND", 0, 0, 0, ""},
470   { "DEALLOCATE", 0, 0, 0, ""},
471   { "DEC", 0, 0, 0, ""},
472   { "DECIMAL", 0, 0, 0, ""},
473   { "DECLARE", 0, 0, 0, ""},
474   { "DEFAULT", 0, 0, 0, ""},
475   { "DEFINER", 0, 0, 0, ""},
476   { "DELAYED", 0, 0, 0, ""},
477   { "DELAY_KEY_WRITE", 0, 0, 0, ""},
478   { "DELETE", 0, 0, 0, ""},
479   { "DESC", 0, 0, 0, ""},
480   { "DESCRIBE", 0, 0, 0, ""},
481   { "DES_KEY_FILE", 0, 0, 0, ""},
482   { "DETERMINISTIC", 0, 0, 0, ""},
483   { "DIRECTORY", 0, 0, 0, ""},
484   { "DISABLE", 0, 0, 0, ""},
485   { "DISCARD", 0, 0, 0, ""},
486   { "DISTINCT", 0, 0, 0, ""},
487   { "DISTINCTROW", 0, 0, 0, ""},
488   { "DIV", 0, 0, 0, ""},
489   { "DO", 0, 0, 0, ""},
490   { "DOUBLE", 0, 0, 0, ""},
491   { "DROP", 0, 0, 0, ""},
492   { "DUAL", 0, 0, 0, ""},
493   { "DUMPFILE", 0, 0, 0, ""},
494   { "DUPLICATE", 0, 0, 0, ""},
495   { "DYNAMIC", 0, 0, 0, ""},
496   { "EACH", 0, 0, 0, ""},
497   { "ELSE", 0, 0, 0, ""},
498   { "ELSEIF", 0, 0, 0, ""},
499   { "ENABLE", 0, 0, 0, ""},
500   { "ENCLOSED", 0, 0, 0, ""},
501   { "END", 0, 0, 0, ""},
502   { "ENGINE", 0, 0, 0, ""},
503   { "ENGINES", 0, 0, 0, ""},
504   { "ENUM", 0, 0, 0, ""},
505   { "ERRORS", 0, 0, 0, ""},
506   { "ESCAPE", 0, 0, 0, ""},
507   { "ESCAPED", 0, 0, 0, ""},
508   { "EVENTS", 0, 0, 0, ""},
509   { "EXECUTE", 0, 0, 0, ""},
510   { "EXISTS", 0, 0, 0, ""},
511   { "EXIT", 0, 0, 0, ""},
512   { "EXPANSION", 0, 0, 0, ""},
513   { "EXPLAIN", 0, 0, 0, ""},
514   { "EXTENDED", 0, 0, 0, ""},
515   { "FALSE", 0, 0, 0, ""},
516   { "FAST", 0, 0, 0, ""},
517   { "FETCH", 0, 0, 0, ""},
518   { "FIELDS", 0, 0, 0, ""},
519   { "FILE", 0, 0, 0, ""},
520   { "FIRST", 0, 0, 0, ""},
521   { "FIXED", 0, 0, 0, ""},
522   { "FLOAT", 0, 0, 0, ""},
523   { "FLOAT4", 0, 0, 0, ""},
524   { "FLOAT8", 0, 0, 0, ""},
525   { "FLUSH", 0, 0, 0, ""},
526   { "FOR", 0, 0, 0, ""},
527   { "FORCE", 0, 0, 0, ""},
528   { "FOREIGN", 0, 0, 0, ""},
529   { "FOUND", 0, 0, 0, ""},
530   { "FROM", 0, 0, 0, ""},
531   { "FULL", 0, 0, 0, ""},
532   { "FULLTEXT", 0, 0, 0, ""},
533   { "FUNCTION", 0, 0, 0, ""},
534   { "GEOMETRY", 0, 0, 0, ""},
535   { "GEOMETRYCOLLECTION", 0, 0, 0, ""},
536   { "GET_FORMAT", 0, 0, 0, ""},
537   { "GLOBAL", 0, 0, 0, ""},
538   { "GRANT", 0, 0, 0, ""},
539   { "GRANTS", 0, 0, 0, ""},
540   { "GROUP", 0, 0, 0, ""},
541   { "HANDLER", 0, 0, 0, ""},
542   { "HASH", 0, 0, 0, ""},
543   { "HAVING", 0, 0, 0, ""},
544   { "HELP", 0, 0, 0, ""},
545   { "HIGH_PRIORITY", 0, 0, 0, ""},
546   { "HOSTS", 0, 0, 0, ""},
547   { "HOUR", 0, 0, 0, ""},
548   { "HOUR_MICROSECOND", 0, 0, 0, ""},
549   { "HOUR_MINUTE", 0, 0, 0, ""},
550   { "HOUR_SECOND", 0, 0, 0, ""},
551   { "IDENTIFIED", 0, 0, 0, ""},
552   { "IF", 0, 0, 0, ""},
553   { "IGNORE", 0, 0, 0, ""},
554   { "IMPORT", 0, 0, 0, ""},
555   { "IN", 0, 0, 0, ""},
556   { "INDEX", 0, 0, 0, ""},
557   { "INDEXES", 0, 0, 0, ""},
558   { "INFILE", 0, 0, 0, ""},
559   { "INNER", 0, 0, 0, ""},
560   { "INNOBASE", 0, 0, 0, ""},
561   { "INNODB", 0, 0, 0, ""},
562   { "INOUT", 0, 0, 0, ""},
563   { "INSENSITIVE", 0, 0, 0, ""},
564   { "INSERT", 0, 0, 0, ""},
565   { "INSERT_METHOD", 0, 0, 0, ""},
566   { "INT", 0, 0, 0, ""},
567   { "INT1", 0, 0, 0, ""},
568   { "INT2", 0, 0, 0, ""},
569   { "INT3", 0, 0, 0, ""},
570   { "INT4", 0, 0, 0, ""},
571   { "INT8", 0, 0, 0, ""},
572   { "INTEGER", 0, 0, 0, ""},
573   { "INTERVAL", 0, 0, 0, ""},
574   { "INTO", 0, 0, 0, ""},
575   { "IO_THREAD", 0, 0, 0, ""},
576   { "IS", 0, 0, 0, ""},
577   { "ISOLATION", 0, 0, 0, ""},
578   { "ISSUER", 0, 0, 0, ""},
579   { "ITERATE", 0, 0, 0, ""},
580   { "INVOKER", 0, 0, 0, ""},
581   { "JOIN", 0, 0, 0, ""},
582   { "KEY", 0, 0, 0, ""},
583   { "KEYS", 0, 0, 0, ""},
584   { "KILL", 0, 0, 0, ""},
585   { "LANGUAGE", 0, 0, 0, ""},
586   { "LAST", 0, 0, 0, ""},
587   { "LEADING", 0, 0, 0, ""},
588   { "LEAVE", 0, 0, 0, ""},
589   { "LEAVES", 0, 0, 0, ""},
590   { "LEFT", 0, 0, 0, ""},
591   { "LEVEL", 0, 0, 0, ""},
592   { "LIKE", 0, 0, 0, ""},
593   { "LIMIT", 0, 0, 0, ""},
594   { "LINES", 0, 0, 0, ""},
595   { "LINESTRING", 0, 0, 0, ""},
596   { "LOAD", 0, 0, 0, ""},
597   { "LOCAL", 0, 0, 0, ""},
598   { "LOCALTIME", 0, 0, 0, ""},
599   { "LOCALTIMESTAMP", 0, 0, 0, ""},
600   { "LOCK", 0, 0, 0, ""},
601   { "LOCKS", 0, 0, 0, ""},
602   { "LOGS", 0, 0, 0, ""},
603   { "LONG", 0, 0, 0, ""},
604   { "LONGBLOB", 0, 0, 0, ""},
605   { "LONGTEXT", 0, 0, 0, ""},
606   { "LOOP", 0, 0, 0, ""},
607   { "LOW_PRIORITY", 0, 0, 0, ""},
608   { "MASTER", 0, 0, 0, ""},
609   { "MASTER_CONNECT_RETRY", 0, 0, 0, ""},
610   { "MASTER_HOST", 0, 0, 0, ""},
611   { "MASTER_LOG_FILE", 0, 0, 0, ""},
612   { "MASTER_LOG_POS", 0, 0, 0, ""},
613   { "MASTER_PASSWORD", 0, 0, 0, ""},
614   { "MASTER_PORT", 0, 0, 0, ""},
615   { "MASTER_SERVER_ID", 0, 0, 0, ""},
616   { "MASTER_SSL", 0, 0, 0, ""},
617   { "MASTER_SSL_CA", 0, 0, 0, ""},
618   { "MASTER_SSL_CAPATH", 0, 0, 0, ""},
619   { "MASTER_SSL_CERT", 0, 0, 0, ""},
620   { "MASTER_SSL_CIPHER", 0, 0, 0, ""},
621   { "MASTER_SSL_KEY", 0, 0, 0, ""},
622   { "MASTER_USER", 0, 0, 0, ""},
623   { "MATCH", 0, 0, 0, ""},
624   { "MAX_CONNECTIONS_PER_HOUR", 0, 0, 0, ""},
625   { "MAX_QUERIES_PER_HOUR", 0, 0, 0, ""},
626   { "MAX_ROWS", 0, 0, 0, ""},
627   { "MAX_UPDATES_PER_HOUR", 0, 0, 0, ""},
628   { "MAX_USER_CONNECTIONS", 0, 0, 0, ""},
629   { "MEDIUM", 0, 0, 0, ""},
630   { "MEDIUMBLOB", 0, 0, 0, ""},
631   { "MEDIUMINT", 0, 0, 0, ""},
632   { "MEDIUMTEXT", 0, 0, 0, ""},
633   { "MERGE", 0, 0, 0, ""},
634   { "MICROSECOND", 0, 0, 0, ""},
635   { "MIDDLEINT", 0, 0, 0, ""},
636   { "MIGRATE", 0, 0, 0, ""},
637   { "MINUTE", 0, 0, 0, ""},
638   { "MINUTE_MICROSECOND", 0, 0, 0, ""},
639   { "MINUTE_SECOND", 0, 0, 0, ""},
640   { "MIN_ROWS", 0, 0, 0, ""},
641   { "MOD", 0, 0, 0, ""},
642   { "MODE", 0, 0, 0, ""},
643   { "MODIFIES", 0, 0, 0, ""},
644   { "MODIFY", 0, 0, 0, ""},
645   { "MONTH", 0, 0, 0, ""},
646   { "MULTILINESTRING", 0, 0, 0, ""},
647   { "MULTIPOINT", 0, 0, 0, ""},
648   { "MULTIPOLYGON", 0, 0, 0, ""},
649   { "MUTEX", 0, 0, 0, ""},
650   { "NAME", 0, 0, 0, ""},
651   { "NAMES", 0, 0, 0, ""},
652   { "NATIONAL", 0, 0, 0, ""},
653   { "NATURAL", 0, 0, 0, ""},
654   { "NDB", 0, 0, 0, ""},
655   { "NDBCLUSTER", 0, 0, 0, ""},
656   { "NCHAR", 0, 0, 0, ""},
657   { "NEW", 0, 0, 0, ""},
658   { "NEXT", 0, 0, 0, ""},
659   { "NO", 0, 0, 0, ""},
660   { "NONE", 0, 0, 0, ""},
661   { "NOT", 0, 0, 0, ""},
662   { "NO_WRITE_TO_BINLOG", 0, 0, 0, ""},
663   { "NULL", 0, 0, 0, ""},
664   { "NUMERIC", 0, 0, 0, ""},
665   { "NVARCHAR", 0, 0, 0, ""},
666   { "OFFSET", 0, 0, 0, ""},
667   { "OLD_PASSWORD", 0, 0, 0, ""},
668   { "ON", 0, 0, 0, ""},
669   { "ONE", 0, 0, 0, ""},
670   { "ONE_SHOT", 0, 0, 0, ""},
671   { "OPEN", 0, 0, 0, ""},
672   { "OPTIMIZE", 0, 0, 0, ""},
673   { "OPTION", 0, 0, 0, ""},
674   { "OPTIONALLY", 0, 0, 0, ""},
675   { "OR", 0, 0, 0, ""},
676   { "ORDER", 0, 0, 0, ""},
677   { "OUT", 0, 0, 0, ""},
678   { "OUTER", 0, 0, 0, ""},
679   { "OUTFILE", 0, 0, 0, ""},
680   { "PACK_KEYS", 0, 0, 0, ""},
681   { "PARTIAL", 0, 0, 0, ""},
682   { "PASSWORD", 0, 0, 0, ""},
683   { "PHASE", 0, 0, 0, ""},
684   { "POINT", 0, 0, 0, ""},
685   { "POLYGON", 0, 0, 0, ""},
686   { "PRECISION", 0, 0, 0, ""},
687   { "PREPARE", 0, 0, 0, ""},
688   { "PREV", 0, 0, 0, ""},
689   { "PRIMARY", 0, 0, 0, ""},
690   { "PRIVILEGES", 0, 0, 0, ""},
691   { "PROCEDURE", 0, 0, 0, ""},
692   { "PROCESS", 0, 0, 0, ""},
693   { "PROCESSLIST", 0, 0, 0, ""},
694   { "PURGE", 0, 0, 0, ""},
695   { "QUARTER", 0, 0, 0, ""},
696   { "QUERY", 0, 0, 0, ""},
697   { "QUICK", 0, 0, 0, ""},
698   { "READ", 0, 0, 0, ""},
699   { "READS", 0, 0, 0, ""},
700   { "REAL", 0, 0, 0, ""},
701   { "RECOVER", 0, 0, 0, ""},
702   { "REDUNDANT", 0, 0, 0, ""},
703   { "REFERENCES", 0, 0, 0, ""},
704   { "REGEXP", 0, 0, 0, ""},
705   { "RELAY_LOG_FILE", 0, 0, 0, ""},
706   { "RELAY_LOG_POS", 0, 0, 0, ""},
707   { "RELAY_THREAD", 0, 0, 0, ""},
708   { "RELEASE", 0, 0, 0, ""},
709   { "RELOAD", 0, 0, 0, ""},
710   { "RENAME", 0, 0, 0, ""},
711   { "REPAIR", 0, 0, 0, ""},
712   { "REPEATABLE", 0, 0, 0, ""},
713   { "REPLACE", 0, 0, 0, ""},
714   { "REPLICATION", 0, 0, 0, ""},
715   { "REPEAT", 0, 0, 0, ""},
716   { "REQUIRE", 0, 0, 0, ""},
717   { "RESET", 0, 0, 0, ""},
718   { "RESTORE", 0, 0, 0, ""},
719   { "RESTRICT", 0, 0, 0, ""},
720   { "RESUME", 0, 0, 0, ""},
721   { "RETURN", 0, 0, 0, ""},
722   { "RETURNS", 0, 0, 0, ""},
723   { "REVOKE", 0, 0, 0, ""},
724   { "RIGHT", 0, 0, 0, ""},
725   { "RLIKE", 0, 0, 0, ""},
726   { "ROLLBACK", 0, 0, 0, ""},
727   { "ROLLUP", 0, 0, 0, ""},
728   { "ROUTINE", 0, 0, 0, ""},
729   { "ROW", 0, 0, 0, ""},
730   { "ROWS", 0, 0, 0, ""},
731   { "ROW_FORMAT", 0, 0, 0, ""},
732   { "RTREE", 0, 0, 0, ""},
733   { "SAVEPOINT", 0, 0, 0, ""},
734   { "SCHEMA", 0, 0, 0, ""},
735   { "SCHEMAS", 0, 0, 0, ""},
736   { "SECOND", 0, 0, 0, ""},
737   { "SECOND_MICROSECOND", 0, 0, 0, ""},
738   { "SECURITY", 0, 0, 0, ""},
739   { "SELECT", 0, 0, 0, ""},
740   { "SENSITIVE", 0, 0, 0, ""},
741   { "SEPARATOR", 0, 0, 0, ""},
742   { "SERIAL", 0, 0, 0, ""},
743   { "SERIALIZABLE", 0, 0, 0, ""},
744   { "SESSION", 0, 0, 0, ""},
745   { "SET", 0, 0, 0, ""},
746   { "SHARE", 0, 0, 0, ""},
747   { "SHOW", 0, 0, 0, ""},
748   { "SHUTDOWN", 0, 0, 0, ""},
749   { "SIGNED", 0, 0, 0, ""},
750   { "SIMPLE", 0, 0, 0, ""},
751   { "SLAVE", 0, 0, 0, ""},
752   { "SNAPSHOT", 0, 0, 0, ""},
753   { "SMALLINT", 0, 0, 0, ""},
754   { "SOME", 0, 0, 0, ""},
755   { "SONAME", 0, 0, 0, ""},
756   { "SOUNDS", 0, 0, 0, ""},
757   { "SPATIAL", 0, 0, 0, ""},
758   { "SPECIFIC", 0, 0, 0, ""},
759   { "SQL", 0, 0, 0, ""},
760   { "SQLEXCEPTION", 0, 0, 0, ""},
761   { "SQLSTATE", 0, 0, 0, ""},
762   { "SQLWARNING", 0, 0, 0, ""},
763   { "SQL_BIG_RESULT", 0, 0, 0, ""},
764   { "SQL_BUFFER_RESULT", 0, 0, 0, ""},
765   { "SQL_CACHE", 0, 0, 0, ""},
766   { "SQL_CALC_FOUND_ROWS", 0, 0, 0, ""},
767   { "SQL_NO_CACHE", 0, 0, 0, ""},
768   { "SQL_SMALL_RESULT", 0, 0, 0, ""},
769   { "SQL_THREAD", 0, 0, 0, ""},
770   { "SQL_TSI_SECOND", 0, 0, 0, ""},
771   { "SQL_TSI_MINUTE", 0, 0, 0, ""},
772   { "SQL_TSI_HOUR", 0, 0, 0, ""},
773   { "SQL_TSI_DAY", 0, 0, 0, ""},
774   { "SQL_TSI_WEEK", 0, 0, 0, ""},
775   { "SQL_TSI_MONTH", 0, 0, 0, ""},
776   { "SQL_TSI_QUARTER", 0, 0, 0, ""},
777   { "SQL_TSI_YEAR", 0, 0, 0, ""},
778   { "SSL", 0, 0, 0, ""},
779   { "START", 0, 0, 0, ""},
780   { "STARTING", 0, 0, 0, ""},
781   { "STATUS", 0, 0, 0, ""},
782   { "STOP", 0, 0, 0, ""},
783   { "STORAGE", 0, 0, 0, ""},
784   { "STRAIGHT_JOIN", 0, 0, 0, ""},
785   { "STRING", 0, 0, 0, ""},
786   { "STRIPED", 0, 0, 0, ""},
787   { "SUBJECT", 0, 0, 0, ""},
788   { "SUPER", 0, 0, 0, ""},
789   { "SUSPEND", 0, 0, 0, ""},
790   { "TABLE", 0, 0, 0, ""},
791   { "TABLES", 0, 0, 0, ""},
792   { "TABLESPACE", 0, 0, 0, ""},
793   { "TEMPORARY", 0, 0, 0, ""},
794   { "TEMPTABLE", 0, 0, 0, ""},
795   { "TERMINATED", 0, 0, 0, ""},
796   { "TEXT", 0, 0, 0, ""},
797   { "THEN", 0, 0, 0, ""},
798   { "TIME", 0, 0, 0, ""},
799   { "TIMESTAMP", 0, 0, 0, ""},
800   { "TIMESTAMPADD", 0, 0, 0, ""},
801   { "TIMESTAMPDIFF", 0, 0, 0, ""},
802   { "TINYBLOB", 0, 0, 0, ""},
803   { "TINYINT", 0, 0, 0, ""},
804   { "TINYTEXT", 0, 0, 0, ""},
805   { "TO", 0, 0, 0, ""},
806   { "TRAILING", 0, 0, 0, ""},
807   { "TRANSACTION", 0, 0, 0, ""},
808   { "TRIGGER", 0, 0, 0, ""},
809   { "TRIGGERS", 0, 0, 0, ""},
810   { "TRUE", 0, 0, 0, ""},
811   { "TRUNCATE", 0, 0, 0, ""},
812   { "TYPE", 0, 0, 0, ""},
813   { "TYPES", 0, 0, 0, ""},
814   { "UNCOMMITTED", 0, 0, 0, ""},
815   { "UNDEFINED", 0, 0, 0, ""},
816   { "UNDO", 0, 0, 0, ""},
817   { "UNICODE", 0, 0, 0, ""},
818   { "UNION", 0, 0, 0, ""},
819   { "UNIQUE", 0, 0, 0, ""},
820   { "UNKNOWN", 0, 0, 0, ""},
821   { "UNLOCK", 0, 0, 0, ""},
822   { "UNSIGNED", 0, 0, 0, ""},
823   { "UNTIL", 0, 0, 0, ""},
824   { "UPDATE", 0, 0, 0, ""},
825   { "UPGRADE", 0, 0, 0, ""},
826   { "USAGE", 0, 0, 0, ""},
827   { "USE", 0, 0, 0, ""},
828   { "USER", 0, 0, 0, ""},
829   { "USER_RESOURCES", 0, 0, 0, ""},
830   { "USE_FRM", 0, 0, 0, ""},
831   { "USING", 0, 0, 0, ""},
832   { "UTC_DATE", 0, 0, 0, ""},
833   { "UTC_TIME", 0, 0, 0, ""},
834   { "UTC_TIMESTAMP", 0, 0, 0, ""},
835   { "VALUE", 0, 0, 0, ""},
836   { "VALUES", 0, 0, 0, ""},
837   { "VARBINARY", 0, 0, 0, ""},
838   { "VARCHAR", 0, 0, 0, ""},
839   { "VARCHARACTER", 0, 0, 0, ""},
840   { "VARIABLES", 0, 0, 0, ""},
841   { "VARYING", 0, 0, 0, ""},
842   { "WARNINGS", 0, 0, 0, ""},
843   { "WEEK", 0, 0, 0, ""},
844   { "WHEN", 0, 0, 0, ""},
845   { "WHERE", 0, 0, 0, ""},
846   { "WHILE", 0, 0, 0, ""},
847   { "VIEW", 0, 0, 0, ""},
848   { "WITH", 0, 0, 0, ""},
849   { "WORK", 0, 0, 0, ""},
850   { "WRITE", 0, 0, 0, ""},
851   { "X509", 0, 0, 0, ""},
852   { "XOR", 0, 0, 0, ""},
853   { "XA", 0, 0, 0, ""},
854   { "YEAR", 0, 0, 0, ""},
855   { "YEAR_MONTH", 0, 0, 0, ""},
856   { "ZEROFILL", 0, 0, 0, ""},
857   { "ABS", 0, 0, 0, ""},
858   { "ACOS", 0, 0, 0, ""},
859   { "ADDDATE", 0, 0, 0, ""},
860   { "ADDTIME", 0, 0, 0, ""},
861   { "AES_ENCRYPT", 0, 0, 0, ""},
862   { "AES_DECRYPT", 0, 0, 0, ""},
863   { "AREA", 0, 0, 0, ""},
864   { "ASIN", 0, 0, 0, ""},
865   { "ASBINARY", 0, 0, 0, ""},
866   { "ASTEXT", 0, 0, 0, ""},
867   { "ASWKB", 0, 0, 0, ""},
868   { "ASWKT", 0, 0, 0, ""},
869   { "ATAN", 0, 0, 0, ""},
870   { "ATAN2", 0, 0, 0, ""},
871   { "BENCHMARK", 0, 0, 0, ""},
872   { "BIN", 0, 0, 0, ""},
873   { "BIT_COUNT", 0, 0, 0, ""},
874   { "BIT_OR", 0, 0, 0, ""},
875   { "BIT_AND", 0, 0, 0, ""},
876   { "BIT_XOR", 0, 0, 0, ""},
877   { "CAST", 0, 0, 0, ""},
878   { "CEIL", 0, 0, 0, ""},
879   { "CEILING", 0, 0, 0, ""},
880   { "BIT_LENGTH", 0, 0, 0, ""},
881   { "CENTROID", 0, 0, 0, ""},
882   { "CHAR_LENGTH", 0, 0, 0, ""},
883   { "CHARACTER_LENGTH", 0, 0, 0, ""},
884   { "COALESCE", 0, 0, 0, ""},
885   { "COERCIBILITY", 0, 0, 0, ""},
886   { "COMPRESS", 0, 0, 0, ""},
887   { "CONCAT", 0, 0, 0, ""},
888   { "CONCAT_WS", 0, 0, 0, ""},
889   { "CONNECTION_ID", 0, 0, 0, ""},
890   { "CONV", 0, 0, 0, ""},
891   { "CONVERT_TZ", 0, 0, 0, ""},
892   { "COUNT", 0, 0, 0, ""},
893   { "COS", 0, 0, 0, ""},
894   { "COT", 0, 0, 0, ""},
895   { "CRC32", 0, 0, 0, ""},
896   { "CROSSES", 0, 0, 0, ""},
897   { "CURDATE", 0, 0, 0, ""},
898   { "CURTIME", 0, 0, 0, ""},
899   { "DATE_ADD", 0, 0, 0, ""},
900   { "DATEDIFF", 0, 0, 0, ""},
901   { "DATE_FORMAT", 0, 0, 0, ""},
902   { "DATE_SUB", 0, 0, 0, ""},
903   { "DAYNAME", 0, 0, 0, ""},
904   { "DAYOFMONTH", 0, 0, 0, ""},
905   { "DAYOFWEEK", 0, 0, 0, ""},
906   { "DAYOFYEAR", 0, 0, 0, ""},
907   { "DECODE", 0, 0, 0, ""},
908   { "DEGREES", 0, 0, 0, ""},
909   { "DES_ENCRYPT", 0, 0, 0, ""},
910   { "DES_DECRYPT", 0, 0, 0, ""},
911   { "DIMENSION", 0, 0, 0, ""},
912   { "DISJOINT", 0, 0, 0, ""},
913   { "ELT", 0, 0, 0, ""},
914   { "ENCODE", 0, 0, 0, ""},
915   { "ENCRYPT", 0, 0, 0, ""},
916   { "ENDPOINT", 0, 0, 0, ""},
917   { "ENVELOPE", 0, 0, 0, ""},
918   { "EQUALS", 0, 0, 0, ""},
919   { "EXTERIORRING", 0, 0, 0, ""},
920   { "EXTRACT", 0, 0, 0, ""},
921   { "EXP", 0, 0, 0, ""},
922   { "EXPORT_SET", 0, 0, 0, ""},
923   { "FIELD", 0, 0, 0, ""},
924   { "FIND_IN_SET", 0, 0, 0, ""},
925   { "FLOOR", 0, 0, 0, ""},
926   { "FORMAT", 0, 0, 0, ""},
927   { "FOUND_ROWS", 0, 0, 0, ""},
928   { "FROM_DAYS", 0, 0, 0, ""},
929   { "FROM_UNIXTIME", 0, 0, 0, ""},
930   { "GET_LOCK", 0, 0, 0, ""},
931   { "GEOMETRYN", 0, 0, 0, ""},
932   { "GEOMETRYTYPE", 0, 0, 0, ""},
933   { "GEOMCOLLFROMTEXT", 0, 0, 0, ""},
934   { "GEOMCOLLFROMWKB", 0, 0, 0, ""},
935   { "GEOMETRYCOLLECTIONFROMTEXT", 0, 0, 0, ""},
936   { "GEOMETRYCOLLECTIONFROMWKB", 0, 0, 0, ""},
937   { "GEOMETRYFROMTEXT", 0, 0, 0, ""},
938   { "GEOMETRYFROMWKB", 0, 0, 0, ""},
939   { "GEOMFROMTEXT", 0, 0, 0, ""},
940   { "GEOMFROMWKB", 0, 0, 0, ""},
941   { "GLENGTH", 0, 0, 0, ""},
942   { "GREATEST", 0, 0, 0, ""},
943   { "GROUP_CONCAT", 0, 0, 0, ""},
944   { "GROUP_UNIQUE_USERS", 0, 0, 0, ""},
945   { "HEX", 0, 0, 0, ""},
946   { "IFNULL", 0, 0, 0, ""},
947   { "INET_ATON", 0, 0, 0, ""},
948   { "INET_NTOA", 0, 0, 0, ""},
949   { "INSTR", 0, 0, 0, ""},
950   { "INTERIORRINGN", 0, 0, 0, ""},
951   { "INTERSECTS", 0, 0, 0, ""},
952   { "ISCLOSED", 0, 0, 0, ""},
953   { "ISEMPTY", 0, 0, 0, ""},
954   { "ISNULL", 0, 0, 0, ""},
955   { "IS_FREE_LOCK", 0, 0, 0, ""},
956   { "IS_USED_LOCK", 0, 0, 0, ""},
957   { "LAST_INSERT_ID", 0, 0, 0, ""},
958   { "ISSIMPLE", 0, 0, 0, ""},
959   { "LAST_DAY", 0, 0, 0, ""},
960   { "LCASE", 0, 0, 0, ""},
961   { "LEAST", 0, 0, 0, ""},
962   { "LENGTH", 0, 0, 0, ""},
963   { "LN", 0, 0, 0, ""},
964   { "LINEFROMTEXT", 0, 0, 0, ""},
965   { "LINEFROMWKB", 0, 0, 0, ""},
966   { "LINESTRINGFROMTEXT", 0, 0, 0, ""},
967   { "LINESTRINGFROMWKB", 0, 0, 0, ""},
968   { "LOAD_FILE", 0, 0, 0, ""},
969   { "LOCATE", 0, 0, 0, ""},
970   { "LOG", 0, 0, 0, ""},
971   { "LOG2", 0, 0, 0, ""},
972   { "LOG10", 0, 0, 0, ""},
973   { "LOWER", 0, 0, 0, ""},
974   { "LPAD", 0, 0, 0, ""},
975   { "LTRIM", 0, 0, 0, ""},
976   { "MAKE_SET", 0, 0, 0, ""},
977   { "MAKEDATE", 0, 0, 0, ""},
978   { "MAKETIME", 0, 0, 0, ""},
979   { "MASTER_POS_WAIT", 0, 0, 0, ""},
980   { "MAX", 0, 0, 0, ""},
981   { "MBRCONTAINS", 0, 0, 0, ""},
982   { "MBRDISJOINT", 0, 0, 0, ""},
983   { "MBREQUAL", 0, 0, 0, ""},
984   { "MBRINTERSECTS", 0, 0, 0, ""},
985   { "MBROVERLAPS", 0, 0, 0, ""},
986   { "MBRTOUCHES", 0, 0, 0, ""},
987   { "MBRWITHIN", 0, 0, 0, ""},
988   { "MD5", 0, 0, 0, ""},
989   { "MID", 0, 0, 0, ""},
990   { "MIN", 0, 0, 0, ""},
991   { "MLINEFROMTEXT", 0, 0, 0, ""},
992   { "MLINEFROMWKB", 0, 0, 0, ""},
993   { "MPOINTFROMTEXT", 0, 0, 0, ""},
994   { "MPOINTFROMWKB", 0, 0, 0, ""},
995   { "MPOLYFROMTEXT", 0, 0, 0, ""},
996   { "MPOLYFROMWKB", 0, 0, 0, ""},
997   { "MONTHNAME", 0, 0, 0, ""},
998   { "MULTILINESTRINGFROMTEXT", 0, 0, 0, ""},
999   { "MULTILINESTRINGFROMWKB", 0, 0, 0, ""},
1000   { "MULTIPOINTFROMTEXT", 0, 0, 0, ""},
1001   { "MULTIPOINTFROMWKB", 0, 0, 0, ""},
1002   { "MULTIPOLYGONFROMTEXT", 0, 0, 0, ""},
1003   { "MULTIPOLYGONFROMWKB", 0, 0, 0, ""},
1004   { "NAME_CONST", 0, 0, 0, ""},
1005   { "NOW", 0, 0, 0, ""},
1006   { "NULLIF", 0, 0, 0, ""},
1007   { "NUMGEOMETRIES", 0, 0, 0, ""},
1008   { "NUMINTERIORRINGS", 0, 0, 0, ""},
1009   { "NUMPOINTS", 0, 0, 0, ""},
1010   { "OCTET_LENGTH", 0, 0, 0, ""},
1011   { "OCT", 0, 0, 0, ""},
1012   { "ORD", 0, 0, 0, ""},
1013   { "OVERLAPS", 0, 0, 0, ""},
1014   { "PERIOD_ADD", 0, 0, 0, ""},
1015   { "PERIOD_DIFF", 0, 0, 0, ""},
1016   { "PI", 0, 0, 0, ""},
1017   { "POINTFROMTEXT", 0, 0, 0, ""},
1018   { "POINTFROMWKB", 0, 0, 0, ""},
1019   { "POINTN", 0, 0, 0, ""},
1020   { "POLYFROMTEXT", 0, 0, 0, ""},
1021   { "POLYFROMWKB", 0, 0, 0, ""},
1022   { "POLYGONFROMTEXT", 0, 0, 0, ""},
1023   { "POLYGONFROMWKB", 0, 0, 0, ""},
1024   { "POSITION", 0, 0, 0, ""},
1025   { "POW", 0, 0, 0, ""},
1026   { "POWER", 0, 0, 0, ""},
1027   { "QUOTE", 0, 0, 0, ""},
1028   { "RADIANS", 0, 0, 0, ""},
1029   { "RAND", 0, 0, 0, ""},
1030   { "RELEASE_LOCK", 0, 0, 0, ""},
1031   { "REVERSE", 0, 0, 0, ""},
1032   { "ROUND", 0, 0, 0, ""},
1033   { "ROW_COUNT", 0, 0, 0, ""},
1034   { "RPAD", 0, 0, 0, ""},
1035   { "RTRIM", 0, 0, 0, ""},
1036   { "SEC_TO_TIME", 0, 0, 0, ""},
1037   { "SESSION_USER", 0, 0, 0, ""},
1038   { "SUBDATE", 0, 0, 0, ""},
1039   { "SIGN", 0, 0, 0, ""},
1040   { "SIN", 0, 0, 0, ""},
1041   { "SHA", 0, 0, 0, ""},
1042   { "SHA1", 0, 0, 0, ""},
1043   { "SLEEP", 0, 0, 0, ""},
1044   { "SOUNDEX", 0, 0, 0, ""},
1045   { "SPACE", 0, 0, 0, ""},
1046   { "SQRT", 0, 0, 0, ""},
1047   { "SRID", 0, 0, 0, ""},
1048   { "STARTPOINT", 0, 0, 0, ""},
1049   { "STD", 0, 0, 0, ""},
1050   { "STDDEV", 0, 0, 0, ""},
1051   { "STDDEV_POP", 0, 0, 0, ""},
1052   { "STDDEV_SAMP", 0, 0, 0, ""},
1053   { "STR_TO_DATE", 0, 0, 0, ""},
1054   { "STRCMP", 0, 0, 0, ""},
1055   { "SUBSTR", 0, 0, 0, ""},
1056   { "SUBSTRING", 0, 0, 0, ""},
1057   { "SUBSTRING_INDEX", 0, 0, 0, ""},
1058   { "SUBTIME", 0, 0, 0, ""},
1059   { "SUM", 0, 0, 0, ""},
1060   { "SYSDATE", 0, 0, 0, ""},
1061   { "SYSTEM_USER", 0, 0, 0, ""},
1062   { "TAN", 0, 0, 0, ""},
1063   { "TIME_FORMAT", 0, 0, 0, ""},
1064   { "TIME_TO_SEC", 0, 0, 0, ""},
1065   { "TIMEDIFF", 0, 0, 0, ""},
1066   { "TO_DAYS", 0, 0, 0, ""},
1067   { "TOUCHES", 0, 0, 0, ""},
1068   { "TRIM", 0, 0, 0, ""},
1069   { "UCASE", 0, 0, 0, ""},
1070   { "UNCOMPRESS", 0, 0, 0, ""},
1071   { "UNCOMPRESSED_LENGTH", 0, 0, 0, ""},
1072   { "UNHEX", 0, 0, 0, ""},
1073   { "UNIQUE_USERS", 0, 0, 0, ""},
1074   { "UNIX_TIMESTAMP", 0, 0, 0, ""},
1075   { "UPPER", 0, 0, 0, ""},
1076   { "UUID", 0, 0, 0, ""},
1077   { "VARIANCE", 0, 0, 0, ""},
1078   { "VAR_POP", 0, 0, 0, ""},
1079   { "VAR_SAMP", 0, 0, 0, ""},
1080   { "VERSION", 0, 0, 0, ""},
1081   { "WEEKDAY", 0, 0, 0, ""},
1082   { "WEEKOFYEAR", 0, 0, 0, ""},
1083   { "WITHIN", 0, 0, 0, ""},
1084   { "X", 0, 0, 0, ""},
1085   { "Y", 0, 0, 0, ""},
1086   { "YEARWEEK", 0, 0, 0, ""},
1087   /* end sentinel */
1088   { (char *)NULL,       0, 0, 0, ""}
1089 };
1090 
1091 static const char *load_default_groups[]= { "mysql","client",0 };
1092 
1093 static int         embedded_server_arg_count= 0;
1094 static char       *embedded_server_args[MAX_SERVER_ARGS];
1095 static const char *embedded_server_groups[]=
1096 { "server", "embedded", "mysql_SERVER", 0 };
1097 
1098 #ifdef HAVE_READLINE
1099 /*
1100  HIST_ENTRY is defined for libedit, but not for the real readline
1101  Need to redefine it for real readline to find it
1102 */
1103 #if !defined(HAVE_HIST_ENTRY)
1104 typedef struct _hist_entry {
1105   const char      *line;
1106   const char      *data;
1107 } HIST_ENTRY;
1108 #endif
1109 
1110 extern "C" int add_history(const char *command); /* From readline directory */
1111 extern "C" int read_history(const char *command);
1112 extern "C" int write_history(const char *command);
1113 extern "C" HIST_ENTRY *history_get(int num);
1114 extern "C" int history_length;
1115 static int not_in_history(const char *line);
1116 static void initialize_readline (char *name);
1117 static void fix_history(String *final_command);
1118 #endif
1119 
1120 static COMMANDS *find_command(char *name);
1121 static COMMANDS *find_command(char cmd_name);
1122 static bool add_line(String &buffer, char *line, ulong line_length,
1123                      char *in_string, bool *ml_comment, bool truncated);
1124 static void remove_cntrl(String &buffer);
1125 static void print_table_data(MYSQL_RES *result);
1126 static void print_table_data_html(MYSQL_RES *result);
1127 static void print_table_data_xml(MYSQL_RES *result);
1128 static void print_tab_data(MYSQL_RES *result);
1129 static void print_table_data_vertically(MYSQL_RES *result);
1130 static void print_warnings(void);
1131 static ulong start_timer(void);
1132 static void end_timer(ulong start_time,char *buff);
1133 static void mysql_end_timer(ulong start_time,char *buff);
1134 static void nice_time(double sec,char *buff,bool part_second);
1135 extern "C" sig_handler mysql_end(int sig);
1136 extern "C" sig_handler handle_kill_signal(int sig);
1137 #if defined(HAVE_TERMIOS_H) && defined(GWINSZ_IN_SYS_IOCTL)
1138 static sig_handler window_resize(int sig);
1139 #endif
1140 
1141 const char DELIMITER_NAME[]= "delimiter";
1142 const uint DELIMITER_NAME_LEN= sizeof(DELIMITER_NAME) - 1;
is_delimiter_command(char * name,ulong len)1143 inline bool is_delimiter_command(char *name, ulong len)
1144 {
1145   /*
1146     Delimiter command has a parameter, so the length of the whole command
1147     is larger than DELIMITER_NAME_LEN.  We don't care the parameter, so
1148     only name(first DELIMITER_NAME_LEN bytes) is checked.
1149   */
1150   return (len >= DELIMITER_NAME_LEN &&
1151           !my_strnncoll(charset_info, (uchar*) name, DELIMITER_NAME_LEN,
1152                         (uchar *) DELIMITER_NAME, DELIMITER_NAME_LEN));
1153 }
1154 
1155 /**
1156    Get the index of a command in the commands array.
1157 
1158    @param cmd_char    Short form command.
1159 
1160    @return int
1161      The index of the command is returned if it is found, else -1 is returned.
1162 */
get_command_index(char cmd_char)1163 inline int get_command_index(char cmd_char)
1164 {
1165   /*
1166     All client-specific commands are in the first part of commands array
1167     and have a function to implement it.
1168   */
1169   for (uint i= 0; *commands[i].func; i++)
1170     if (commands[i].cmd_char == cmd_char)
1171       return i;
1172   return -1;
1173 }
1174 
1175 static int delimiter_index= -1;
1176 static int charset_index= -1;
1177 static bool real_binary_mode= FALSE;
1178 
1179 #ifdef _WIN32
windows_ctrl_handler(DWORD fdwCtrlType)1180 BOOL windows_ctrl_handler(DWORD fdwCtrlType)
1181 {
1182   switch (fdwCtrlType)
1183   {
1184   case CTRL_C_EVENT:
1185   case CTRL_BREAK_EVENT:
1186     if (!opt_sigint_ignore)
1187       handle_kill_signal(SIGINT);
1188     /* Indicate that signal has beed handled. */
1189     return TRUE;
1190   case CTRL_CLOSE_EVENT:
1191   case CTRL_LOGOFF_EVENT:
1192   case CTRL_SHUTDOWN_EVENT:
1193     handle_kill_signal(SIGINT + 1);
1194   }
1195   /* Pass signal to the next control handler function. */
1196   return FALSE;
1197 }
1198 #endif
1199 
1200 
main(int argc,char * argv[])1201 int main(int argc,char *argv[])
1202 {
1203   char buff[80];
1204 
1205   MY_INIT(argv[0]);
1206   DBUG_ENTER("main");
1207   DBUG_PROCESS(argv[0]);
1208 
1209   charset_index= get_command_index('C');
1210   delimiter_index= get_command_index('d');
1211   delimiter_str= delimiter;
1212   default_prompt = my_strdup(getenv("MYSQL_PS1") ?
1213 			     getenv("MYSQL_PS1") :
1214 			     "mysql> ",MYF(MY_WME));
1215   current_prompt = my_strdup(default_prompt,MYF(MY_WME));
1216   prompt_counter=0;
1217 
1218   outfile[0]=0;			// no (default) outfile
1219   strmov(pager, "stdout");	// the default, if --pager wasn't given
1220   {
1221     char *tmp=getenv("PAGER");
1222     if (tmp && strlen(tmp))
1223     {
1224       default_pager_set= 1;
1225       strmov(default_pager, tmp);
1226     }
1227   }
1228   if (!isatty(0) || !isatty(1))
1229   {
1230     status.batch=1; opt_silent=1;
1231     ignore_errors=0;
1232   }
1233   else
1234     status.add_to_history=1;
1235   status.exit_status=1;
1236 
1237   {
1238     /*
1239      The file descriptor-layer may be out-of-sync with the file-number layer,
1240      so we make sure that "stdout" is really open.  If its file is closed then
1241      explicitly close the FD layer.
1242     */
1243     int stdout_fileno_copy;
1244     stdout_fileno_copy= dup(fileno(stdout)); /* Okay if fileno fails. */
1245     if (stdout_fileno_copy == -1)
1246       fclose(stdout);
1247     else
1248       close(stdout_fileno_copy);             /* Clean up dup(). */
1249   }
1250 
1251 #ifdef __WIN__
1252   /* Convert command line parameters from UTF16LE to UTF8MB4. */
1253   my_win_translate_command_line_args(&my_charset_utf8mb4_bin, &argc, &argv);
1254 #endif
1255 
1256   my_getopt_use_args_separator= TRUE;
1257   if (load_defaults("my",load_default_groups,&argc,&argv))
1258   {
1259     my_end(0);
1260     exit(1);
1261   }
1262   my_getopt_use_args_separator= FALSE;
1263 
1264   defaults_argv=argv;
1265   if (get_options(argc, (char **) argv))
1266   {
1267     free_defaults(defaults_argv);
1268     my_end(0);
1269     exit(1);
1270   }
1271   if (status.batch && !status.line_buff &&
1272       !(status.line_buff= batch_readline_init(MAX_BATCH_BUFFER_SIZE, stdin)))
1273   {
1274     put_info("Can't initialize batch_readline - may be the input source is "
1275              "a directory or a block device.", INFO_ERROR, 0);
1276     free_defaults(defaults_argv);
1277     my_end(0);
1278     exit(1);
1279   }
1280   if (mysql_server_init(embedded_server_arg_count, embedded_server_args,
1281                         (char**) embedded_server_groups))
1282   {
1283     put_error(NULL);
1284     free_defaults(defaults_argv);
1285     my_end(0);
1286     exit(1);
1287   }
1288   glob_buffer.realloc(512);
1289   completion_hash_init(&ht, 128);
1290   init_alloc_root(&hash_mem_root, 16384, 0);
1291   memset(&mysql, 0, sizeof(mysql));
1292   if (sql_connect(current_host,current_db,current_user,opt_password,
1293 		  opt_silent))
1294   {
1295     quick= 1;					// Avoid history
1296     status.exit_status= 1;
1297     mysql_end(-1);
1298   }
1299   if (!status.batch)
1300     ignore_errors=1;				// Don't abort monitor
1301 
1302 #ifndef _WIN32
1303   if (opt_sigint_ignore)
1304     signal(SIGINT, SIG_IGN);
1305   else
1306     signal(SIGINT, handle_kill_signal);         // Catch SIGINT to clean up
1307   signal(SIGQUIT, mysql_end);			// Catch SIGQUIT to clean up
1308   signal(SIGHUP, handle_kill_signal);         // Catch SIGHUP to clean up
1309 #else
1310   SetConsoleCtrlHandler((PHANDLER_ROUTINE) windows_ctrl_handler, TRUE);
1311 #endif
1312 
1313 
1314 #if defined(HAVE_TERMIOS_H) && defined(GWINSZ_IN_SYS_IOCTL)
1315   /* Readline will call this if it installs a handler */
1316   signal(SIGWINCH, window_resize);
1317   /* call the SIGWINCH handler to get the default term width */
1318   window_resize(0);
1319 #endif
1320 
1321   put_info("Welcome to the MySQL monitor.  Commands end with ; or \\g.",
1322 	   INFO_INFO);
1323   snprintf((char*) glob_buffer.ptr(), glob_buffer.alloced_length(),
1324 	   "Your MySQL connection id is %lu\nServer version: %s\n",
1325 	   mysql_thread_id(&mysql), server_version_string(&mysql));
1326   put_info((char*) glob_buffer.ptr(),INFO_INFO);
1327 
1328   put_info(ORACLE_WELCOME_COPYRIGHT_NOTICE("2000"), INFO_INFO);
1329 
1330 #ifdef HAVE_READLINE
1331   initialize_readline((char*) my_progname);
1332   if (!status.batch && !quick && !opt_html && !opt_xml)
1333   {
1334     init_dynamic_string(&histignore_buffer, "*IDENTIFIED*:*PASSWORD*",
1335                         1024, 1024);
1336 
1337     /*
1338       More history-ignore patterns can be supplied using either --histignore
1339       option or MYSQL_HISTIGNORE environment variable. If supplied, it will
1340       get appended to the default pattern (*IDENTIFIED*:*PASSWORD*). In case
1341       both are specified, pattern(s) supplied using --histignore option will
1342       be used.
1343     */
1344     if (opt_histignore)
1345     {
1346       dynstr_append(&histignore_buffer, ":");
1347       dynstr_append(&histignore_buffer, opt_histignore);
1348     }
1349     else if (getenv("MYSQL_HISTIGNORE"))
1350     {
1351       dynstr_append(&histignore_buffer, ":");
1352       dynstr_append(&histignore_buffer, getenv("MYSQL_HISTIGNORE"));
1353     }
1354 
1355     parse_histignore();
1356 
1357     /* read-history from file, default ~/.mysql_history*/
1358     if (getenv("MYSQL_HISTFILE"))
1359       histfile=my_strdup(getenv("MYSQL_HISTFILE"),MYF(MY_WME));
1360     else if (getenv("HOME"))
1361     {
1362       histfile=(char*) my_malloc((uint) strlen(getenv("HOME"))
1363 				 + (uint) strlen("/.mysql_history")+2,
1364 				 MYF(MY_WME));
1365       if (histfile)
1366 	sprintf(histfile,"%s/.mysql_history",getenv("HOME"));
1367       char link_name[FN_REFLEN];
1368       if (my_readlink(link_name, histfile, 0) == 0 &&
1369           strncmp(link_name, "/dev/null", 10) == 0)
1370       {
1371         /* The .mysql_history file is a symlink to /dev/null, don't use it */
1372         my_free(histfile);
1373         histfile= 0;
1374       }
1375     }
1376 
1377     /* We used to suggest setting MYSQL_HISTFILE=/dev/null. */
1378     if (histfile && strncmp(histfile, "/dev/null", 10) == 0)
1379       histfile= NULL;
1380 
1381     if (histfile && histfile[0])
1382     {
1383       if (verbose)
1384 	tee_fprintf(stdout, "Reading history-file %s\n",histfile);
1385       read_history(histfile);
1386       if (!(histfile_tmp= (char*) my_malloc((uint) strlen(histfile) + 5,
1387 					    MYF(MY_WME))))
1388       {
1389 	fprintf(stderr, "Couldn't allocate memory for temp histfile!\n");
1390 	exit(1);
1391       }
1392       sprintf(histfile_tmp, "%s.TMP", histfile);
1393     }
1394   }
1395 
1396 #endif
1397 
1398   sprintf(buff, "%s",
1399 	  "Type 'help;' or '\\h' for help. Type '\\c' to clear the current input statement.\n");
1400   put_info(buff,INFO_INFO);
1401   status.exit_status= read_and_execute(!status.batch);
1402   if (opt_outfile)
1403     end_tee();
1404   mysql_end(0);
1405 #ifndef _lint
1406   DBUG_RETURN(0);				// Keep compiler happy
1407 #endif
1408 }
1409 
mysql_end(int sig)1410 sig_handler mysql_end(int sig)
1411 {
1412 #ifndef _WIN32
1413   /*
1414     Ingnoring SIGQUIT, SIGINT and SIGHUP signals when cleanup process starts.
1415     This will help in resolving the double free issues, which occures in case
1416     the signal handler function is started in between the clean up function.
1417   */
1418   signal(SIGQUIT, SIG_IGN);
1419   signal(SIGINT, SIG_IGN);
1420   signal(SIGHUP, SIG_IGN);
1421 #endif
1422 
1423   mysql_close(&mysql);
1424 #ifdef HAVE_READLINE
1425   if (!status.batch && !quick && !opt_html && !opt_xml &&
1426       histfile && histfile[0])
1427   {
1428     /* write-history */
1429     if (verbose)
1430       tee_fprintf(stdout, "Writing history-file %s\n",histfile);
1431     if (!write_history(histfile_tmp))
1432       my_rename(histfile_tmp, histfile, MYF(MY_WME));
1433   }
1434   batch_readline_end(status.line_buff);
1435   completion_hash_free(&ht);
1436   free_root(&hash_mem_root,MYF(0));
1437 
1438   my_free(opt_histignore);
1439   my_free(histfile);
1440   my_free(histfile_tmp);
1441   dynstr_free(&histignore_buffer);
1442   free_hist_patterns();
1443 #endif
1444   if (sig >= 0)
1445     put_info(sig ? "Aborted" : "Bye", INFO_RESULT);
1446   glob_buffer.free();
1447   old_buffer.free();
1448   processed_prompt.free();
1449   my_free(server_version);
1450   my_free(opt_password);
1451   my_free(opt_mysql_unix_port);
1452   my_free(current_db);
1453   my_free(current_host);
1454   my_free(current_user);
1455   my_free(full_username);
1456   my_free(part_username);
1457   my_free(default_prompt);
1458 #ifdef HAVE_SMEM
1459   my_free(shared_memory_base_name);
1460 #endif
1461   my_free(current_prompt);
1462   while (embedded_server_arg_count > 1)
1463     my_free(embedded_server_args[--embedded_server_arg_count]);
1464   mysql_server_end();
1465   free_defaults(defaults_argv);
1466   my_end(my_end_arg);
1467   exit(status.exit_status);
1468 }
1469 
1470 
1471 /*
1472   This function handles sigint calls
1473   If query is in process, kill query
1474   no query in process, terminate like previous behavior
1475  */
handle_kill_signal(int sig)1476 sig_handler handle_kill_signal(int sig)
1477 {
1478   char kill_buffer[40];
1479   MYSQL *kill_mysql= NULL;
1480   const char *reason = sig == SIGINT ? "Ctrl-C" : "Terminal close";
1481 
1482   /* terminate if no query being executed, or we already tried interrupting */
1483   /* terminate if no query being executed, or we already tried interrupting */
1484   if (!executing_query || (interrupted_query == 2))
1485   {
1486     tee_fprintf(stdout, "%s -- exit!\n", reason);
1487     goto err;
1488   }
1489 
1490   kill_mysql= mysql_init(kill_mysql);
1491   mysql_options(kill_mysql, MYSQL_OPT_CONNECT_ATTR_RESET, 0);
1492   mysql_options4(kill_mysql, MYSQL_OPT_CONNECT_ATTR_ADD,
1493                  "program_name", "mysql");
1494   if (!mysql_connect_ssl_check(kill_mysql, current_host, current_user,
1495                                opt_password, "", opt_mysql_port,
1496                                opt_mysql_unix_port, 0,
1497                                opt_ssl_mode == SSL_MODE_REQUIRED))
1498   {
1499     tee_fprintf(stdout, "%s -- sorry, cannot connect to server to kill query, giving up ...\n", reason);
1500     goto err;
1501   }
1502 
1503   interrupted_query++;
1504 
1505   /* mysqld < 5 does not understand KILL QUERY, skip to KILL CONNECTION */
1506   if ((interrupted_query == 1) && (mysql_get_server_version(&mysql) < 50000))
1507     interrupted_query= 2;
1508 
1509   /* kill_buffer is always big enough because max length of %lu is 15 */
1510   sprintf(kill_buffer, "KILL %s%lu",
1511           (interrupted_query == 1) ? "QUERY " : "",
1512           mysql_thread_id(&mysql));
1513   tee_fprintf(stdout, "%s -- sending \"%s\" to server ...\n",
1514               reason, kill_buffer);
1515   mysql_real_query(kill_mysql, kill_buffer, (uint) strlen(kill_buffer));
1516   mysql_close(kill_mysql);
1517   tee_fprintf(stdout, "%s -- query aborted.\n", reason);
1518 
1519   return;
1520 
1521 err:
1522 #ifdef _WIN32
1523   /*
1524    When SIGINT is raised on Windows, the OS creates a new thread to handle the
1525    interrupt. Once that thread completes, the main thread continues running
1526    only to find that it's resources have already been free'd when the sigint
1527    handler called mysql_end().
1528   */
1529   mysql_thread_end();
1530   return;
1531 #else
1532   mysql_end(sig);
1533 #endif
1534 }
1535 
1536 
1537 #if defined(HAVE_TERMIOS_H) && defined(GWINSZ_IN_SYS_IOCTL)
window_resize(int sig)1538 sig_handler window_resize(int sig)
1539 {
1540   struct winsize window_size;
1541 
1542   if (ioctl(fileno(stdin), TIOCGWINSZ, &window_size) == 0)
1543     terminal_width= window_size.ws_col;
1544 }
1545 #endif
1546 
1547 static struct my_option my_long_options[] =
1548 {
1549   {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0,
1550    0, 0, 0, 0, 0},
1551   {"help", 'I', "Synonym for -?", 0, 0, 0, GET_NO_ARG, NO_ARG, 0,
1552    0, 0, 0, 0, 0},
1553   {"auto-rehash", OPT_AUTO_REHASH,
1554    "Enable automatic rehashing. One doesn't need to use 'rehash' to get table "
1555    "and field completion, but startup and reconnecting may take a longer time. "
1556    "Disable with --disable-auto-rehash.",
1557    &opt_rehash, &opt_rehash, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0,
1558    0, 0},
1559   {"no-auto-rehash", 'A',
1560    "No automatic rehashing. One has to use 'rehash' to get table and field "
1561    "completion. This gives a quicker start of mysql and disables rehashing "
1562    "on reconnect.",
1563    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
1564    {"auto-vertical-output", OPT_AUTO_VERTICAL_OUTPUT,
1565     "Automatically switch to vertical output mode if the result is wider "
1566     "than the terminal width.",
1567     &auto_vertical_output, &auto_vertical_output, 0, GET_BOOL, NO_ARG, 0,
1568     0, 0, 0, 0, 0},
1569   {"batch", 'B',
1570    "Don't use history file. Disable interactive behavior. (Enables --silent.)",
1571    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
1572   {"bind-address", 0, "IP address to bind to.",
1573    (uchar**) &opt_bind_addr, (uchar**) &opt_bind_addr, 0, GET_STR,
1574    REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1575   {"binary-as-hex", 0, "Print binary data as hex", &opt_binhex, &opt_binhex,
1576    0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
1577   {"character-sets-dir", OPT_CHARSETS_DIR,
1578    "Directory for character set files.", &charsets_dir,
1579    &charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1580   {"column-type-info", OPT_COLUMN_TYPES, "Display column type information.",
1581    &column_types_flag, &column_types_flag,
1582    0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
1583   {"comments", 'c', "Preserve comments. Send comments to the server."
1584    " The default is --skip-comments (discard comments), enable with --comments.",
1585    &preserve_comments, &preserve_comments,
1586    0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
1587   {"compress", 'C', "Use compression in server/client protocol.",
1588    &opt_compress, &opt_compress, 0, GET_BOOL, NO_ARG, 0, 0, 0,
1589    0, 0, 0},
1590 #ifdef DBUG_OFF
1591   {"debug", '#', "This is a non-debug version. Catch this and exit.",
1592    0,0, 0, GET_DISABLED, OPT_ARG, 0, 0, 0, 0, 0, 0},
1593 #else
1594   {"debug", '#', "Output debug log.", &default_dbug_option,
1595    &default_dbug_option, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
1596 #endif
1597   {"debug-check", OPT_DEBUG_CHECK, "Check memory and open file usage at exit.",
1598    &debug_check_flag, &debug_check_flag, 0,
1599    GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
1600   {"debug-info", 'T', "Print some debug info at exit.", &debug_info_flag,
1601    &debug_info_flag, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
1602   {"database", 'D', "Database to use.", &current_db,
1603    &current_db, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1604   {"default-character-set", OPT_DEFAULT_CHARSET,
1605    "Set the default character set.", &default_charset,
1606    &default_charset, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1607   {"delimiter", OPT_DELIMITER, "Delimiter to be used.", &delimiter_str,
1608    &delimiter_str, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1609   {"enable_cleartext_plugin", OPT_ENABLE_CLEARTEXT_PLUGIN,
1610     "Enable/disable the clear text authentication plugin.",
1611    &opt_enable_cleartext_plugin, &opt_enable_cleartext_plugin,
1612    0, GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0},
1613   {"execute", 'e', "Execute command and quit. (Disables --force and history file.)", 0,
1614    0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1615   {"vertical", 'E', "Print the output of a query (rows) vertically.",
1616    &vertical, &vertical, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0,
1617    0},
1618   {"force", 'f', "Continue even if we get an SQL error.",
1619    &ignore_errors, &ignore_errors, 0, GET_BOOL, NO_ARG, 0, 0,
1620    0, 0, 0, 0},
1621   {"named-commands", 'G',
1622    "Enable named commands. Named commands mean this program's internal "
1623    "commands; see mysql> help . When enabled, the named commands can be "
1624    "used from any line of the query, otherwise only from the first line, "
1625    "before an enter. Disable with --disable-named-commands. This option "
1626    "is disabled by default.",
1627    &named_cmds, &named_cmds, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0,
1628    0, 0},
1629   {"ignore-spaces", 'i', "Ignore space after function names.",
1630    &ignore_spaces, &ignore_spaces, 0, GET_BOOL, NO_ARG, 0, 0,
1631    0, 0, 0, 0},
1632   {"init-command", OPT_INIT_COMMAND,
1633    "SQL Command to execute when connecting to MySQL server. Will "
1634    "automatically be re-executed when reconnecting.",
1635    &opt_init_command, &opt_init_command, 0,
1636    GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1637   {"local-infile", OPT_LOCAL_INFILE, "Enable/disable LOAD DATA LOCAL INFILE.",
1638    &opt_local_infile, &opt_local_infile, 0, GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0},
1639   {"no-beep", 'b', "Turn off beep on error.", &opt_nobeep,
1640    &opt_nobeep, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
1641   {"host", 'h', "Connect to host.", &current_host,
1642    &current_host, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1643   {"html", 'H', "Produce HTML output.", &opt_html, &opt_html,
1644    0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
1645   {"xml", 'X', "Produce XML output.", &opt_xml, &opt_xml, 0,
1646    GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
1647   {"line-numbers", OPT_LINE_NUMBERS, "Write line numbers for errors.",
1648    &line_numbers, &line_numbers, 0, GET_BOOL,
1649    NO_ARG, 1, 0, 0, 0, 0, 0},
1650   {"skip-line-numbers", 'L', "Don't write line number for errors.", 0, 0, 0, GET_NO_ARG,
1651    NO_ARG, 0, 0, 0, 0, 0, 0},
1652   {"unbuffered", 'n', "Flush buffer after each query.", &unbuffered,
1653    &unbuffered, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
1654   {"column-names", OPT_COLUMN_NAMES, "Write column names in results.",
1655    &column_names, &column_names, 0, GET_BOOL,
1656    NO_ARG, 1, 0, 0, 0, 0, 0},
1657   {"skip-column-names", 'N',
1658    "Don't write column names in results.",
1659    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
1660   {"sigint-ignore", OPT_SIGINT_IGNORE, "Ignore SIGINT (CTRL-C).",
1661    &opt_sigint_ignore,  &opt_sigint_ignore, 0, GET_BOOL,
1662    NO_ARG, 0, 0, 0, 0, 0, 0},
1663   {"one-database", 'o',
1664    "Ignore statements except those that occur while the default "
1665    "database is the one named at the command line.",
1666    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
1667 #ifdef USE_POPEN
1668   {"pager", OPT_PAGER,
1669    "Pager to use to display results. If you don't supply an option, the "
1670    "default pager is taken from your ENV variable PAGER. Valid pagers are "
1671    "less, more, cat [> filename], etc. See interactive help (\\h) also. "
1672    "This option does not work in batch mode. Disable with --disable-pager. "
1673    "This option is disabled by default.",
1674    0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
1675 #endif
1676   {"password", 'p',
1677    "Password to use when connecting to server. If password is not given it's asked from the tty.",
1678    0, 0, 0, GET_PASSWORD, OPT_ARG, 0, 0, 0, 0, 0, 0},
1679 #ifdef __WIN__
1680   {"pipe", 'W', "Use named pipes to connect to server.", 0, 0, 0, GET_NO_ARG,
1681    NO_ARG, 0, 0, 0, 0, 0, 0},
1682 #endif
1683   {"port", 'P', "Port number to use for connection or 0 for default to, in "
1684    "order of preference, my.cnf, $MYSQL_TCP_PORT, "
1685 #if MYSQL_PORT_DEFAULT == 0
1686    "/etc/services, "
1687 #endif
1688    "built-in default (" STRINGIFY_ARG(MYSQL_PORT) ").",
1689    &opt_mysql_port,
1690    &opt_mysql_port, 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0,  0},
1691   {"prompt", OPT_PROMPT, "Set the mysql prompt to this value.",
1692    &current_prompt, &current_prompt, 0, GET_STR_ALLOC,
1693    REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1694   {"protocol", OPT_MYSQL_PROTOCOL, "The protocol to use for connection (tcp, socket, pipe, memory).",
1695    0, 0, 0, GET_STR,  REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1696   {"quick", 'q',
1697    "Don't cache result, print it row by row. This may slow down the server "
1698    "if the output is suspended. Doesn't use history file.",
1699    &quick, &quick, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
1700   {"raw", 'r', "Write fields without conversion. Used with --batch.",
1701    &opt_raw_data, &opt_raw_data, 0, GET_BOOL, NO_ARG, 0, 0, 0,
1702    0, 0, 0},
1703   {"reconnect", OPT_RECONNECT, "Reconnect if the connection is lost. Disable "
1704    "with --disable-reconnect. This option is enabled by default.",
1705    &opt_reconnect, &opt_reconnect, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
1706   {"silent", 's', "Be more silent. Print results with a tab as separator, "
1707    "each row on new line.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
1708 #ifdef HAVE_SMEM
1709   {"shared-memory-base-name", OPT_SHARED_MEMORY_BASE_NAME,
1710    "Base name of shared memory.", &shared_memory_base_name,
1711    &shared_memory_base_name, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1712 #endif
1713   {"socket", 'S', "The socket file to use for connection.",
1714    &opt_mysql_unix_port, &opt_mysql_unix_port, 0, GET_STR_ALLOC,
1715    REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1716 #include "sslopt-longopts.h"
1717   {"table", 't', "Output in table format.", &output_tables,
1718    &output_tables, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
1719   {"tee", OPT_TEE,
1720    "Append everything into outfile. See interactive help (\\h) also. "
1721    "Does not work in batch mode. Disable with --disable-tee. "
1722    "This option is disabled by default.",
1723    0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1724 #ifndef DONT_ALLOW_USER_CHANGE
1725   {"user", 'u', "User for login if not current user.", &current_user,
1726    &current_user, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1727 #endif
1728   {"safe-updates", 'U', "Only allow UPDATE and DELETE that uses keys.",
1729    &safe_updates, &safe_updates, 0, GET_BOOL, NO_ARG, 0, 0,
1730    0, 0, 0, 0},
1731   {"i-am-a-dummy", 'U', "Synonym for option --safe-updates, -U.",
1732    &safe_updates, &safe_updates, 0, GET_BOOL, NO_ARG, 0, 0,
1733    0, 0, 0, 0},
1734   {"verbose", 'v', "Write more. (-v -v -v gives the table output format).", 0,
1735    0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
1736   {"version", 'V', "Output version information and exit.", 0, 0, 0,
1737    GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
1738   {"wait", 'w', "Wait and retry if connection is down.", 0, 0, 0, GET_NO_ARG,
1739    NO_ARG, 0, 0, 0, 0, 0, 0},
1740   {"connect_timeout", OPT_CONNECT_TIMEOUT,
1741    "Number of seconds before connection timeout.",
1742    &opt_connect_timeout, &opt_connect_timeout, 0, GET_ULONG, REQUIRED_ARG,
1743    0, 0, 3600*12, 0, 0, 0},
1744   {"max_allowed_packet", OPT_MAX_ALLOWED_PACKET,
1745    "The maximum packet length to send to or receive from server.",
1746    &opt_max_allowed_packet, &opt_max_allowed_packet, 0,
1747    GET_ULONG, REQUIRED_ARG, 16 *1024L*1024L, 4096,
1748    (longlong) 2*1024L*1024L*1024L, MALLOC_OVERHEAD, 1024, 0},
1749   {"net_buffer_length", OPT_NET_BUFFER_LENGTH,
1750    "The buffer size for TCP/IP and socket communication.",
1751    &opt_net_buffer_length, &opt_net_buffer_length, 0, GET_ULONG,
1752    REQUIRED_ARG, 16384, 1024, 512*1024*1024L, MALLOC_OVERHEAD, 1024, 0},
1753   {"select_limit", OPT_SELECT_LIMIT,
1754    "Automatic limit for SELECT when using --safe-updates.",
1755    &select_limit, &select_limit, 0, GET_ULONG, REQUIRED_ARG, 1000L,
1756    1, ULONG_MAX, 0, 1, 0},
1757   {"max_join_size", OPT_MAX_JOIN_SIZE,
1758    "Automatic limit for rows in a join when using --safe-updates.",
1759    &max_join_size, &max_join_size, 0, GET_ULONG, REQUIRED_ARG, 1000000L,
1760    1, ULONG_MAX, 0, 1, 0},
1761   {"secure-auth", OPT_SECURE_AUTH, "Refuse client connecting to server if it"
1762     " uses old (pre-4.1.1) protocol.", &opt_secure_auth,
1763     &opt_secure_auth, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
1764   {"server-arg", OPT_SERVER_ARG, "Send embedded server this as a parameter.",
1765    0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1766   {"show-warnings", OPT_SHOW_WARNINGS, "Show warnings after every statement.",
1767     &show_warnings, &show_warnings, 0, GET_BOOL, NO_ARG,
1768     0, 0, 0, 0, 0, 0},
1769   {"plugin_dir", OPT_PLUGIN_DIR, "Directory for client-side plugins.",
1770     &opt_plugin_dir, &opt_plugin_dir, 0,
1771    GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1772   {"default_auth", OPT_DEFAULT_AUTH,
1773     "Default authentication client-side plugin to use.",
1774     &opt_default_auth, &opt_default_auth, 0,
1775    GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1776   {"histignore", OPT_HISTIGNORE, "A colon-separated list of patterns "
1777    "to keep statements from getting logged into mysql history.",
1778    &opt_histignore, &opt_histignore, 0, GET_STR_ALLOC, REQUIRED_ARG,
1779    0, 0, 0, 0, 0, 0},
1780   {"binary-mode", OPT_BINARY_MODE,
1781    "By default, ASCII '\\0' is disallowed and '\\r\\n' is translated to '\\n'. "
1782    "This switch turns off both features, and also turns off parsing of all client"
1783    "commands except \\C and DELIMITER, in non-interactive mode (for input "
1784    "piped to mysql or loaded using the 'source' command). This is necessary "
1785    "when processing output from mysqlbinlog that may contain blobs.",
1786    &opt_binary_mode, &opt_binary_mode, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
1787   {"server-public-key-path", OPT_SERVER_PUBLIC_KEY,
1788    "File path to the server public RSA key in PEM format.",
1789    &opt_server_public_key, &opt_server_public_key, 0,
1790    GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1791   {"connect-expired-password", 0,
1792    "Notify the server that this client is prepared to handle expired "
1793    "password sandbox mode.",
1794    &opt_connect_expired_password, &opt_connect_expired_password, 0, GET_BOOL,
1795    NO_ARG, 0, 0, 0, 0, 0, 0},
1796   { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
1797 };
1798 
1799 
usage(int version)1800 static void usage(int version)
1801 {
1802 #if defined(USE_LIBEDIT_INTERFACE)
1803   const char* readline= "";
1804 #else
1805   const char* readline= "readline";
1806 #endif
1807 
1808 #ifdef HAVE_READLINE
1809   printf("%s  Ver %s Distrib %s, for %s (%s) using %s %s\n",
1810 	 my_progname, VER, MYSQL_SERVER_VERSION, SYSTEM_TYPE, MACHINE_TYPE,
1811          readline, rl_library_version);
1812 #else
1813   printf("%s  Ver %s Distrib %s, for %s (%s)\n", my_progname, VER,
1814 	MYSQL_SERVER_VERSION, SYSTEM_TYPE, MACHINE_TYPE);
1815 #endif
1816 
1817   if (version)
1818     return;
1819   puts(ORACLE_WELCOME_COPYRIGHT_NOTICE("2000"));
1820   printf("Usage: %s [OPTIONS] [database]\n", my_progname);
1821   my_print_help(my_long_options);
1822   print_defaults("my", load_default_groups);
1823   my_print_variables(my_long_options);
1824 }
1825 
1826 
1827 my_bool
get_one_option(int optid,const struct my_option * opt MY_ATTRIBUTE ((unused)),char * argument)1828 get_one_option(int optid, const struct my_option *opt MY_ATTRIBUTE((unused)),
1829 	       char *argument)
1830 {
1831   switch(optid) {
1832   case OPT_CHARSETS_DIR:
1833     strmake(mysql_charsets_dir, argument, sizeof(mysql_charsets_dir) - 1);
1834     charsets_dir = mysql_charsets_dir;
1835     break;
1836   case OPT_DELIMITER:
1837     if (argument == disabled_my_option)
1838     {
1839       strmov(delimiter, DEFAULT_DELIMITER);
1840     }
1841     else
1842     {
1843       /* Check that delimiter does not contain a backslash */
1844       if (!strstr(argument, "\\"))
1845       {
1846         strmake(delimiter, argument, sizeof(delimiter) - 1);
1847       }
1848       else
1849       {
1850         put_info("DELIMITER cannot contain a backslash character", INFO_ERROR);
1851         return 0;
1852       }
1853     }
1854     delimiter_length= (uint)strlen(delimiter);
1855     delimiter_str= delimiter;
1856     break;
1857   case OPT_LOCAL_INFILE:
1858     using_opt_local_infile=1;
1859     break;
1860   case OPT_ENABLE_CLEARTEXT_PLUGIN:
1861     using_opt_enable_cleartext_plugin= TRUE;
1862     break;
1863   case OPT_TEE:
1864     if (argument == disabled_my_option)
1865     {
1866       if (opt_outfile)
1867 	end_tee();
1868     }
1869     else
1870       init_tee(argument);
1871     break;
1872   case OPT_PAGER:
1873     if (argument == disabled_my_option)
1874       opt_nopager= 1;
1875     else
1876     {
1877       opt_nopager= 0;
1878       if (argument && strlen(argument))
1879       {
1880 	default_pager_set= 1;
1881 	strmake(pager, argument, sizeof(pager) - 1);
1882 	strmov(default_pager, pager);
1883       }
1884       else if (default_pager_set)
1885 	strmov(pager, default_pager);
1886       else
1887 	opt_nopager= 1;
1888     }
1889     break;
1890   case OPT_MYSQL_PROTOCOL:
1891 #ifndef EMBEDDED_LIBRARY
1892     opt_protocol= find_type_or_exit(argument, &sql_protocol_typelib,
1893                                     opt->name);
1894 #endif
1895     break;
1896   case OPT_SERVER_ARG:
1897 #ifdef EMBEDDED_LIBRARY
1898     /*
1899       When the embedded server is being tested, the client needs to be
1900       able to pass command-line arguments to the embedded server so it can
1901       locate the language files and data directory.
1902     */
1903     if (!embedded_server_arg_count)
1904     {
1905       embedded_server_arg_count= 1;
1906       embedded_server_args[0]= (char*) "";
1907     }
1908     if (embedded_server_arg_count == MAX_SERVER_ARGS-1 ||
1909         !(embedded_server_args[embedded_server_arg_count++]=
1910           my_strdup(argument, MYF(MY_FAE))))
1911     {
1912         put_info("Can't use server argument", INFO_ERROR);
1913         return 0;
1914     }
1915 #else /*EMBEDDED_LIBRARY */
1916     printf("WARNING: --server-arg option not supported in this configuration.\n");
1917 #endif
1918     break;
1919   case 'A':
1920     opt_rehash= 0;
1921     break;
1922   case 'N':
1923     column_names= 0;
1924     break;
1925   case 'e':
1926     status.batch= 1;
1927     status.add_to_history= 0;
1928     if (!status.line_buff)
1929       ignore_errors= 0;                         // do it for the first -e only
1930     if (!(status.line_buff= batch_readline_command(status.line_buff, argument)))
1931       return 1;
1932     break;
1933   case 'o':
1934     if (argument == disabled_my_option)
1935       one_database= 0;
1936     else
1937       one_database= skip_updates= 1;
1938     break;
1939   case 'p':
1940     if (argument == disabled_my_option)
1941       argument= (char*) "";			// Don't require password
1942     if (argument)
1943     {
1944       char *start= argument;
1945       my_free(opt_password);
1946       opt_password= my_strdup(argument, MYF(MY_FAE));
1947       while (*argument) *argument++= 'x';		// Destroy argument
1948       if (*start)
1949 	start[1]=0 ;
1950       tty_password= 0;
1951     }
1952     else
1953       tty_password= 1;
1954     break;
1955   case '#':
1956     DBUG_PUSH(argument ? argument : default_dbug_option);
1957     debug_info_flag= 1;
1958     break;
1959   case 's':
1960     if (argument == disabled_my_option)
1961       opt_silent= 0;
1962     else
1963       opt_silent++;
1964     break;
1965   case 'v':
1966     if (argument == disabled_my_option)
1967       verbose= 0;
1968     else
1969       verbose++;
1970     break;
1971   case 'B':
1972     status.batch= 1;
1973     status.add_to_history= 0;
1974     set_if_bigger(opt_silent,1);                         // more silent
1975     break;
1976   case 'W':
1977 #ifdef __WIN__
1978     opt_protocol = MYSQL_PROTOCOL_PIPE;
1979 #endif
1980     break;
1981 #include <sslopt-case.h>
1982   case 'V':
1983     usage(1);
1984     exit(0);
1985   case 'I':
1986   case '?':
1987     usage(0);
1988     exit(0);
1989   }
1990   return 0;
1991 }
1992 
1993 
get_options(int argc,char ** argv)1994 static int get_options(int argc, char **argv)
1995 {
1996   char *tmp, *pagpoint;
1997   int ho_error;
1998   MYSQL_PARAMETERS *mysql_params= mysql_get_parameters();
1999 
2000   tmp= (char *) getenv("MYSQL_HOST");
2001   if (tmp)
2002     current_host= my_strdup(tmp, MYF(MY_WME));
2003 
2004   pagpoint= getenv("PAGER");
2005   if (!((char*) (pagpoint)))
2006   {
2007     strmov(pager, "stdout");
2008     opt_nopager= 1;
2009   }
2010   else
2011     strmov(pager, pagpoint);
2012   strmov(default_pager, pager);
2013 
2014   opt_max_allowed_packet= *mysql_params->p_max_allowed_packet;
2015   opt_net_buffer_length= *mysql_params->p_net_buffer_length;
2016 
2017   if ((ho_error=handle_options(&argc, &argv, my_long_options, get_one_option)))
2018     exit(ho_error);
2019 
2020   *mysql_params->p_max_allowed_packet= opt_max_allowed_packet;
2021   *mysql_params->p_net_buffer_length= opt_net_buffer_length;
2022 
2023   if (status.batch) /* disable pager and outfile in this case */
2024   {
2025     strmov(default_pager, "stdout");
2026     strmov(pager, "stdout");
2027     opt_nopager= 1;
2028     default_pager_set= 0;
2029     opt_outfile= 0;
2030     opt_reconnect= 0;
2031     connect_flag= 0; /* Not in interactive mode */
2032   }
2033 
2034   if (argc > 1)
2035   {
2036     usage(0);
2037     exit(1);
2038   }
2039   if (argc == 1)
2040   {
2041     skip_updates= 0;
2042     my_free(current_db);
2043     current_db= my_strdup(*argv, MYF(MY_WME));
2044   }
2045   if (tty_password)
2046     opt_password= get_tty_password(NullS);
2047   if (debug_info_flag)
2048     my_end_arg= MY_CHECK_ERROR | MY_GIVE_INFO;
2049   if (debug_check_flag)
2050     my_end_arg= MY_CHECK_ERROR;
2051 
2052   if (ignore_spaces)
2053     connect_flag|= CLIENT_IGNORE_SPACE;
2054 
2055   return(0);
2056 }
2057 
read_and_execute(bool interactive)2058 static int read_and_execute(bool interactive)
2059 {
2060 #if defined(__WIN__)
2061   String tmpbuf;
2062   String buffer;
2063 #endif
2064 
2065   char	*line= NULL;
2066   char	in_string=0;
2067   ulong line_number=0;
2068   bool ml_comment= 0;
2069   COMMANDS *com;
2070   ulong line_length= 0;
2071   status.exit_status=1;
2072 
2073   real_binary_mode= !interactive && opt_binary_mode;
2074   for (;;)
2075   {
2076     if (!interactive)
2077     {
2078       /*
2079         batch_readline can return 0 on EOF or error.
2080         In that case, we need to double check that we have a valid
2081         line before actually setting line_length to read_length.
2082         */
2083       line= batch_readline(status.line_buff, real_binary_mode);
2084       if (line)
2085       {
2086         line_length= status.line_buff->read_length;
2087 
2088         /*
2089           ASCII 0x00 is not allowed appearing in queries if it is not in binary
2090           mode.
2091         */
2092         if (!real_binary_mode && strlen(line) != line_length)
2093         {
2094           status.exit_status= 1;
2095           String msg;
2096           msg.append("ASCII '\\0' appeared in the statement, but this is not "
2097                      "allowed unless option --binary-mode is enabled and mysql is "
2098                      "run in non-interactive mode. Set --binary-mode to 1 if ASCII "
2099                      "'\\0' is expected. Query: '");
2100           msg.append(glob_buffer);
2101           msg.append(line);
2102           msg.append("'.");
2103           put_info(msg.c_ptr(), INFO_ERROR);
2104           break;
2105         }
2106 
2107         /*
2108           Skip UTF8 Byte Order Marker (BOM) 0xEFBBBF.
2109           Editors like "notepad" put this marker in
2110           the very beginning of a text file when
2111           you save the file using "Unicode UTF-8" format.
2112         */
2113         if (!line_number &&
2114              (uchar) line[0] == 0xEF &&
2115              (uchar) line[1] == 0xBB &&
2116              (uchar) line[2] == 0xBF)
2117         {
2118           line+= 3;
2119           // decrease the line length accordingly to the 3 bytes chopped
2120           line_length -=3;
2121         }
2122       }
2123       line_number++;
2124       if (!glob_buffer.length())
2125 	status.query_start_line=line_number;
2126     }
2127     else
2128     {
2129       char *prompt= (char*) (ml_comment ? "   /*> " :
2130                              glob_buffer.is_empty() ?  construct_prompt() :
2131 			     !in_string ? "    -> " :
2132 			     in_string == '\'' ?
2133 			     "    '> " : (in_string == '`' ?
2134 			     "    `> " :
2135 			     "    \"> "));
2136       if (opt_outfile && glob_buffer.is_empty())
2137 	fflush(OUTFILE);
2138 
2139 #if defined(__WIN__)
2140       tee_fputs(prompt, stdout);
2141       if (!tmpbuf.is_alloced())
2142         tmpbuf.alloc(65535);
2143       tmpbuf.length(0);
2144       buffer.length(0);
2145       line= my_win_console_readline(charset_info,
2146                                     (char *) tmpbuf.ptr(),
2147                                     tmpbuf.alloced_length());
2148 #else
2149       if (opt_outfile)
2150 	fputs(prompt, OUTFILE);
2151       /*
2152         free the previous entered line.
2153         Note: my_free() cannot be used here as the memory was allocated under
2154         the readline/libedit library.
2155       */
2156       if (line)
2157         free(line);
2158       line= readline(prompt);
2159 #endif /* defined(__WIN__) */
2160 
2161       /*
2162         When Ctrl+d or Ctrl+z is pressed, the line may be NULL on some OS
2163         which may cause coredump.
2164       */
2165       if (opt_outfile && line)
2166 	fprintf(OUTFILE, "%s\n", line);
2167 
2168       line_length= line ? strlen(line) : 0;
2169     }
2170     // End of file or system error
2171     if (!line)
2172     {
2173       if (status.line_buff && status.line_buff->error)
2174         status.exit_status= 1;
2175       else
2176         status.exit_status= 0;
2177       break;
2178     }
2179 
2180     /*
2181       Check if line is a mysql command line
2182       (We want to allow help, print and clear anywhere at line start
2183     */
2184     if ((named_cmds || glob_buffer.is_empty())
2185 	&& !ml_comment && !in_string && (com= find_command(line)))
2186     {
2187       if ((*com->func)(&glob_buffer,line) > 0)
2188 	break;
2189       if (glob_buffer.is_empty())		// If buffer was emptied
2190 	in_string=0;
2191 #ifdef HAVE_READLINE
2192       if (interactive && status.add_to_history && not_in_history(line))
2193 	add_filtered_history(line);
2194 #endif
2195       continue;
2196     }
2197     if (add_line(glob_buffer, line, line_length, &in_string, &ml_comment,
2198                  status.line_buff ? status.line_buff->truncated : 0))
2199       break;
2200   }
2201   /* if in batch mode, send last query even if it doesn't end with \g or go */
2202 
2203   if (!interactive && !status.exit_status)
2204   {
2205     remove_cntrl(glob_buffer);
2206     if (!glob_buffer.is_empty())
2207     {
2208       status.exit_status=1;
2209       if (com_go(&glob_buffer,line) <= 0)
2210 	status.exit_status=0;
2211     }
2212   }
2213 
2214 #if defined(__WIN__)
2215   buffer.free();
2216   tmpbuf.free();
2217 #else
2218   if (interactive)
2219     /*
2220       free the last entered line.
2221       Note: my_free() cannot be used here as the memory was allocated under
2222       the readline/libedit library.
2223     */
2224     free(line);
2225 #endif
2226 
2227   /*
2228     If the function is called by 'source' command, it will return to interactive
2229     mode, so real_binary_mode should be FALSE. Otherwise, it will exit the
2230     program, it is safe to set real_binary_mode to FALSE.
2231   */
2232   real_binary_mode= FALSE;
2233   return status.exit_status;
2234 }
2235 
2236 /**
2237    It checks if the input is a short form command. It returns the command's
2238    pointer if a command is found, else return NULL. Note that if binary-mode
2239    is set, then only \C is searched for.
2240 
2241    @param cmd_char    A character of one byte.
2242 
2243    @return
2244      the command's pointer or NULL.
2245 */
find_command(char cmd_char)2246 static COMMANDS *find_command(char cmd_char)
2247 {
2248   DBUG_ENTER("find_command");
2249   DBUG_PRINT("enter", ("cmd_char: %d", cmd_char));
2250 
2251   int index= -1;
2252 
2253   /*
2254     In binary-mode, we disallow all mysql commands except '\C'
2255     and DELIMITER.
2256   */
2257   if (real_binary_mode)
2258   {
2259     if (cmd_char == 'C')
2260       index= charset_index;
2261   }
2262   else
2263     index= get_command_index(cmd_char);
2264 
2265   if (index >= 0)
2266   {
2267     DBUG_PRINT("exit",("found command: %s", commands[index].name));
2268     DBUG_RETURN(&commands[index]);
2269   }
2270   else
2271     DBUG_RETURN((COMMANDS *) 0);
2272 }
2273 
2274 /**
2275    It checks if the input is a long form command. It returns the command's
2276    pointer if a command is found, else return NULL. Note that if binary-mode
2277    is set, then only DELIMITER is searched for.
2278 
2279    @param name    A string.
2280    @return
2281      the command's pointer or NULL.
2282 */
find_command(char * name)2283 static COMMANDS *find_command(char *name)
2284 {
2285   uint len;
2286   char *end;
2287   DBUG_ENTER("find_command");
2288 
2289   DBUG_ASSERT(name != NULL);
2290   DBUG_PRINT("enter", ("name: '%s'", name));
2291 
2292   while (my_isspace(charset_info, *name))
2293     name++;
2294   /*
2295     If there is an \\g in the row or if the row has a delimiter but
2296     this is not a delimiter command, let add_line() take care of
2297     parsing the row and calling find_command().
2298   */
2299   if ((!real_binary_mode && strstr(name, "\\g")) ||
2300       (strstr(name, delimiter) &&
2301        !is_delimiter_command(name, DELIMITER_NAME_LEN)))
2302       DBUG_RETURN((COMMANDS *) 0);
2303 
2304   if ((end=strcont(name, " \t")))
2305   {
2306     len=(uint) (end - name);
2307     while (my_isspace(charset_info, *end))
2308       end++;
2309     if (!*end)
2310       end= 0;					// no arguments to function
2311   }
2312   else
2313     len= (uint) strlen(name);
2314 
2315   int index= -1;
2316   if (real_binary_mode)
2317   {
2318     if (is_delimiter_command(name, len))
2319       index= delimiter_index;
2320   }
2321   else
2322   {
2323     /*
2324       All commands are in the first part of commands array and have a function
2325       to implement it.
2326     */
2327     for (uint i= 0; commands[i].func; i++)
2328     {
2329       if (!my_strnncoll(&my_charset_latin1, (uchar*) name, len,
2330                         (uchar*) commands[i].name, len) &&
2331           (commands[i].name[len] == '\0') &&
2332           (!end || commands[i].takes_params))
2333       {
2334         index= i;
2335         break;
2336       }
2337     }
2338   }
2339 
2340   if (index >= 0)
2341   {
2342     DBUG_PRINT("exit", ("found command: %s", commands[index].name));
2343     DBUG_RETURN(&commands[index]);
2344   }
2345   DBUG_RETURN((COMMANDS *) 0);
2346 }
2347 
2348 
add_line(String & buffer,char * line,ulong line_length,char * in_string,bool * ml_comment,bool truncated)2349 static bool add_line(String &buffer, char *line, ulong line_length,
2350                      char *in_string, bool *ml_comment, bool truncated)
2351 {
2352   uchar inchar;
2353   char buff[80], *pos, *out;
2354   COMMANDS *com;
2355   bool need_space= 0;
2356   bool ss_comment= 0;
2357   DBUG_ENTER("add_line");
2358 
2359   if (!line[0] && buffer.is_empty())
2360     DBUG_RETURN(0);
2361 #ifdef HAVE_READLINE
2362   if (status.add_to_history && line[0] && not_in_history(line))
2363     add_filtered_history(line);
2364 #endif
2365   char *end_of_line= line + line_length;
2366 
2367   for (pos= out= line; pos < end_of_line; pos++)
2368   {
2369     inchar= (uchar) *pos;
2370     if (!preserve_comments)
2371     {
2372       // Skip spaces at the beginning of a statement
2373       if (my_isspace(charset_info,inchar) && (out == line) &&
2374           buffer.is_empty())
2375         continue;
2376     }
2377 
2378 #ifdef USE_MB
2379     // Accept multi-byte characters as-is
2380     int length;
2381     if (use_mb(charset_info) &&
2382         (length= my_ismbchar(charset_info, pos, end_of_line)))
2383     {
2384       if (!*ml_comment || preserve_comments)
2385       {
2386         while (length--)
2387           *out++ = *pos++;
2388         pos--;
2389       }
2390       else
2391         pos+= length - 1;
2392       continue;
2393     }
2394 #endif
2395     if (!*ml_comment && inchar == '\\' &&
2396         !(*in_string &&
2397           (mysql.server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES)))
2398     {
2399       // Found possbile one character command like \c
2400 
2401       if (!(inchar = (uchar) *++pos))
2402 	break;				// readline adds one '\'
2403       if (*in_string || inchar == 'N')	// \N is short for NULL
2404       {					// Don't allow commands in string
2405 	*out++='\\';
2406         if ((inchar == '`') && (*in_string == inchar))
2407           pos--;
2408         else
2409 	  *out++= (char) inchar;
2410 	continue;
2411       }
2412       if ((com= find_command((char) inchar)))
2413       {
2414         // Flush previously accepted characters
2415         if (out != line)
2416         {
2417           buffer.append(line, (uint) (out-line));
2418           out= line;
2419         }
2420 
2421         if ((*com->func)(&buffer,pos-1) > 0)
2422           DBUG_RETURN(1);                       // Quit
2423         if (com->takes_params)
2424         {
2425           if (ss_comment)
2426           {
2427             /*
2428               If a client-side macro appears inside a server-side comment,
2429               discard all characters in the comment after the macro (that is,
2430               until the end of the comment rather than the next delimiter)
2431             */
2432             for (pos++; *pos && (*pos != '*' || *(pos + 1) != '/'); pos++)
2433               ;
2434             pos--;
2435           }
2436           else
2437           {
2438             for (pos++ ;
2439                  *pos && (*pos != *delimiter ||
2440                           !is_prefix(pos + 1, delimiter + 1)) ; pos++)
2441               ;	// Remove parameters
2442             if (!*pos)
2443               pos--;
2444             else
2445               pos+= delimiter_length - 1; // Point at last delim char
2446           }
2447         }
2448       }
2449       else
2450       {
2451 	sprintf(buff,"Unknown command '\\%c'.",inchar);
2452 	if (put_info(buff,INFO_ERROR) > 0)
2453 	  DBUG_RETURN(1);
2454 	*out++='\\';
2455 	*out++=(char) inchar;
2456 	continue;
2457       }
2458     }
2459     else if (!*ml_comment && !*in_string && is_prefix(pos, delimiter))
2460     {
2461       // Found a statement. Continue parsing after the delimiter
2462       pos+= delimiter_length;
2463 
2464       if (preserve_comments)
2465       {
2466         while (my_isspace(charset_info, *pos))
2467           *out++= *pos++;
2468       }
2469       // Flush previously accepted characters
2470       if (out != line)
2471       {
2472         buffer.append(line, (uint32) (out-line));
2473         out= line;
2474       }
2475 
2476       if (preserve_comments && ((*pos == '#') ||
2477                                 ((*pos == '-') &&
2478                                  (pos[1] == '-') &&
2479                                  my_isspace(charset_info, pos[2]))))
2480       {
2481         // Add trailing single line comments to this statement
2482         buffer.append(pos);
2483         pos+= strlen(pos);
2484       }
2485 
2486       pos--;
2487 
2488       if ((com= find_command(buffer.c_ptr())))
2489       {
2490 
2491         if ((*com->func)(&buffer, buffer.c_ptr()) > 0)
2492           DBUG_RETURN(1);                       // Quit
2493       }
2494       else
2495       {
2496         if (com_go(&buffer, 0) > 0)             // < 0 is not fatal
2497           DBUG_RETURN(1);
2498       }
2499       buffer.length(0);
2500     }
2501     else if (!*ml_comment && (!*in_string && (inchar == '#' ||
2502                                               (inchar == '-' && pos[1] == '-' &&
2503                               /*
2504                                 The third byte is either whitespace or is the
2505                                 end of the line -- which would occur only
2506                                 because of the user sending newline -- which is
2507                                 itself whitespace and should also match.
2508                               */
2509 			      (my_isspace(charset_info,pos[2]) ||
2510                                !pos[2])))))
2511     {
2512       // Flush previously accepted characters
2513       if (out != line)
2514       {
2515         buffer.append(line, (uint32) (out - line));
2516         out= line;
2517       }
2518 
2519       // comment to end of line
2520       if (preserve_comments)
2521       {
2522         bool started_with_nothing= !buffer.length();
2523 
2524         buffer.append(pos);
2525 
2526         /*
2527           A single-line comment by itself gets sent immediately so that
2528           client commands (delimiter, status, etc) will be interpreted on
2529           the next line.
2530         */
2531         if (started_with_nothing)
2532         {
2533           if (com_go(&buffer, 0) > 0)             // < 0 is not fatal
2534             DBUG_RETURN(1);
2535           buffer.length(0);
2536         }
2537       }
2538 
2539       break;
2540     }
2541     else if (!*in_string && inchar == '/' && *(pos+1) == '*' &&
2542 	     *(pos+2) != '!')
2543     {
2544       if (preserve_comments)
2545       {
2546         *out++= *pos++;                       // copy '/'
2547         *out++= *pos;                         // copy '*'
2548       }
2549       else
2550         pos++;
2551       *ml_comment= 1;
2552       if (out != line)
2553       {
2554         buffer.append(line,(uint) (out-line));
2555         out=line;
2556       }
2557     }
2558     else if (*ml_comment && !ss_comment && inchar == '*' && *(pos + 1) == '/')
2559     {
2560       if (preserve_comments)
2561       {
2562         *out++= *pos++;                       // copy '*'
2563         *out++= *pos;                         // copy '/'
2564       }
2565       else
2566         pos++;
2567       *ml_comment= 0;
2568       if (out != line)
2569       {
2570         buffer.append(line, (uint32) (out - line));
2571         out= line;
2572       }
2573       // Consumed a 2 chars or more, and will add 1 at most,
2574       // so using the 'line' buffer to edit data in place is ok.
2575       need_space= 1;
2576     }
2577     else
2578     {						// Add found char to buffer
2579       if (!*in_string && inchar == '/' && *(pos + 1) == '*' &&
2580           *(pos + 2) == '!')
2581         ss_comment= 1;
2582       else if (!*in_string && ss_comment && inchar == '*' && *(pos + 1) == '/')
2583         ss_comment= 0;
2584       if (inchar == *in_string)
2585 	*in_string= 0;
2586       else if (!*ml_comment && !*in_string &&
2587 	       (inchar == '\'' || inchar == '"' || inchar == '`'))
2588 	*in_string= (char) inchar;
2589       if (!*ml_comment || preserve_comments)
2590       {
2591         if (need_space && !my_isspace(charset_info, (char)inchar))
2592           *out++= ' ';
2593         need_space= 0;
2594         *out++= (char) inchar;
2595       }
2596     }
2597   }
2598   if (out != line || !buffer.is_empty())
2599   {
2600     uint length=(uint) (out-line);
2601 
2602     if (!truncated && (!is_delimiter_command(line, length) ||
2603                        (*in_string || *ml_comment)))
2604     {
2605       /*
2606         Don't add a new line in case there's a DELIMITER command to be
2607         added to the glob buffer (e.g. on processing a line like
2608         "<command>;DELIMITER <non-eof>") : similar to how a new line is
2609         not added in the case when the DELIMITER is the first command
2610         entered with an empty glob buffer. However, if the delimiter is
2611         part of a string or a comment, the new line should be added. (e.g.
2612         SELECT '\ndelimiter\n';\n)
2613       */
2614       *out++='\n';
2615       length++;
2616     }
2617     if (buffer.length() + length >= buffer.alloced_length())
2618       buffer.realloc(buffer.length()+length+IO_SIZE);
2619     if ((!*ml_comment || preserve_comments) && buffer.append(line, length))
2620       DBUG_RETURN(1);
2621   }
2622   DBUG_RETURN(0);
2623 }
2624 
2625 /*****************************************************************
2626 	    Interface to Readline Completion
2627 ******************************************************************/
2628 
2629 #ifdef HAVE_READLINE
2630 
2631 C_MODE_START
2632 static char *new_command_generator(const char *text, int);
2633 static char **new_mysql_completion(const char *text, int start, int end);
2634 C_MODE_END
2635 
2636 /*
2637   Tell the GNU Readline library how to complete.  We want to try to complete
2638   on command names if this is the first word in the line, or on filenames
2639   if not.
2640 */
2641 
2642 #if defined(USE_NEW_EDITLINE_INTERFACE)
2643 static int fake_magic_space(int, int);
no_completion(const char *,int)2644 extern "C" char *no_completion(const char*,int)
2645 #elif defined(USE_LIBEDIT_INTERFACE)
2646 static int fake_magic_space(const char *, int);
2647 extern "C" int no_completion(const char*,int)
2648 #else
2649 extern "C" char *no_completion()
2650 #endif
2651 {
2652   return 0;					/* No filename completion */
2653 }
2654 
2655 /*	glues pieces of history back together if in pieces   */
fix_history(String * final_command)2656 static void fix_history(String *final_command)
2657 {
2658   int total_lines = 1;
2659   char *ptr = final_command->c_ptr();
2660   String fixed_buffer; 	/* Converted buffer */
2661   char str_char = '\0';  /* Character if we are in a string or not */
2662 
2663   /* find out how many lines we have and remove newlines */
2664   while (*ptr != '\0')
2665   {
2666     switch (*ptr) {
2667       /* string character */
2668     case '"':
2669     case '\'':
2670     case '`':
2671       if (str_char == '\0')	/* open string */
2672 	str_char = *ptr;
2673       else if (str_char == *ptr)   /* close string */
2674 	str_char = '\0';
2675       fixed_buffer.append(ptr,1);
2676       break;
2677     case '\n':
2678       /*
2679 	 not in string, change to space
2680 	 if in string, leave it alone
2681       */
2682       fixed_buffer.append(str_char == '\0' ? " " : "\n");
2683       total_lines++;
2684       break;
2685     case '\\':
2686       fixed_buffer.append('\\');
2687       /* need to see if the backslash is escaping anything */
2688       if (str_char)
2689       {
2690 	ptr++;
2691 	/* special characters that need escaping */
2692 	if (*ptr == '\'' || *ptr == '"' || *ptr == '\\')
2693 	  fixed_buffer.append(ptr,1);
2694 	else
2695 	  ptr--;
2696       }
2697       break;
2698 
2699     default:
2700       fixed_buffer.append(ptr,1);
2701     }
2702     ptr++;
2703   }
2704   if (total_lines > 1)
2705     add_filtered_history(fixed_buffer.ptr());
2706 }
2707 
2708 /*
2709   returns 0 if line matches the previous history entry
2710   returns 1 if the line doesn't match the previous history entry
2711 */
not_in_history(const char * line)2712 static int not_in_history(const char *line)
2713 {
2714   HIST_ENTRY *oldhist = history_get(history_length);
2715 
2716   if (oldhist == 0)
2717     return 1;
2718   if (strcmp(oldhist->line,line) == 0)
2719     return 0;
2720   return 1;
2721 }
2722 
2723 
2724 #if defined(USE_NEW_EDITLINE_INTERFACE)
fake_magic_space(int,int)2725 static int fake_magic_space(int, int)
2726 #else
2727 static int fake_magic_space(const char *, int)
2728 #endif
2729 {
2730   rl_insert(1, ' ');
2731   return 0;
2732 }
2733 
2734 
initialize_readline(char * name)2735 static void initialize_readline (char *name)
2736 {
2737   /* Allow conditional parsing of the ~/.inputrc file. */
2738   rl_readline_name = name;
2739 
2740   /* Tell the completer that we want a crack first. */
2741 #if defined(USE_NEW_EDITLINE_INTERFACE)
2742   rl_attempted_completion_function= (rl_completion_func_t*)&new_mysql_completion;
2743   rl_completion_entry_function= (rl_compentry_func_t*)&no_completion;
2744 
2745   rl_add_defun("magic-space", (rl_command_func_t *)&fake_magic_space, -1);
2746 #elif defined(USE_LIBEDIT_INTERFACE)
2747 #ifdef HAVE_LOCALE_H
2748   setlocale(LC_ALL,""); /* so as libedit use isprint */
2749 #endif
2750   rl_attempted_completion_function= (CPPFunction*)&new_mysql_completion;
2751   rl_completion_entry_function= &no_completion;
2752   rl_add_defun("magic-space", (Function*)&fake_magic_space, -1);
2753 #else
2754   rl_attempted_completion_function= (CPPFunction*)&new_mysql_completion;
2755   rl_completion_entry_function= &no_completion;
2756 #endif
2757 }
2758 
2759 /*
2760   Attempt to complete on the contents of TEXT.  START and END show the
2761   region of TEXT that contains the word to complete.  We can use the
2762   entire line in case we want to do some simple parsing.  Return the
2763   array of matches, or NULL if there aren't any.
2764 */
2765 
new_mysql_completion(const char * text,int start MY_ATTRIBUTE ((unused)),int end MY_ATTRIBUTE ((unused)))2766 static char **new_mysql_completion(const char *text,
2767                                    int start MY_ATTRIBUTE((unused)),
2768                                    int end MY_ATTRIBUTE((unused)))
2769 {
2770   if (!status.batch && !quick)
2771 #if defined(USE_NEW_EDITLINE_INTERFACE)
2772     return rl_completion_matches(text, new_command_generator);
2773 #else
2774     return completion_matches((char *)text, (CPFunction *)new_command_generator);
2775 #endif
2776   else
2777     return (char**) 0;
2778 }
2779 
new_command_generator(const char * text,int state)2780 static char *new_command_generator(const char *text,int state)
2781 {
2782   static int textlen;
2783   char *ptr;
2784   static Bucket *b;
2785   static entry *e;
2786   static uint i;
2787 
2788   if (!state)
2789     textlen=(uint) strlen(text);
2790 
2791   if (textlen>0)
2792   {						/* lookup in the hash */
2793     if (!state)
2794     {
2795       uint len;
2796 
2797       b = find_all_matches(&ht,text,(uint) strlen(text),&len);
2798       if (!b)
2799 	return NullS;
2800       e = b->pData;
2801     }
2802 
2803     if (e)
2804     {
2805       ptr= strdup(e->str);
2806       e = e->pNext;
2807       return ptr;
2808     }
2809   }
2810   else
2811   { /* traverse the entire hash, ugly but works */
2812 
2813     if (!state)
2814     {
2815       /* find the first used bucket */
2816       for (i=0 ; i < ht.nTableSize ; i++)
2817       {
2818 	if (ht.arBuckets[i])
2819 	{
2820 	  b = ht.arBuckets[i];
2821 	  e = b->pData;
2822 	  break;
2823 	}
2824       }
2825     }
2826     ptr= NullS;
2827     while (e && !ptr)
2828     {					/* find valid entry in bucket */
2829       if ((uint) strlen(e->str) == b->nKeyLength)
2830 	ptr = strdup(e->str);
2831       /* find the next used entry */
2832       e = e->pNext;
2833       if (!e)
2834       { /* find the next used bucket */
2835 	b = b->pNext;
2836 	if (!b)
2837 	{
2838 	  for (i++ ; i<ht.nTableSize; i++)
2839 	  {
2840 	    if (ht.arBuckets[i])
2841 	    {
2842 	      b = ht.arBuckets[i];
2843 	      e = b->pData;
2844 	      break;
2845 	    }
2846 	  }
2847 	}
2848 	else
2849 	  e = b->pData;
2850       }
2851     }
2852     if (ptr)
2853       return ptr;
2854   }
2855   return NullS;
2856 }
2857 
2858 
2859 /* Build up the completion hash */
2860 
build_completion_hash(bool rehash,bool write_info)2861 static void build_completion_hash(bool rehash, bool write_info)
2862 {
2863   COMMANDS *cmd=commands;
2864   MYSQL_RES *databases=0,*tables=0;
2865   MYSQL_RES *fields;
2866   static char ***field_names= 0;
2867   MYSQL_ROW database_row,table_row;
2868   MYSQL_FIELD *sql_field;
2869   char buf[NAME_LEN*2+2];		 // table name plus field name plus 2
2870   int i,j,num_fields;
2871   DBUG_ENTER("build_completion_hash");
2872 
2873   if (status.batch || quick || !current_db)
2874     DBUG_VOID_RETURN;			// We don't need completion in batches
2875   if (!rehash)
2876     DBUG_VOID_RETURN;
2877 
2878   /* Free old used memory */
2879   if (field_names)
2880     field_names=0;
2881   completion_hash_clean(&ht);
2882   free_root(&hash_mem_root,MYF(0));
2883 
2884   /* hash this file's known subset of SQL commands */
2885   while (cmd->name) {
2886     add_word(&ht,(char*) cmd->name);
2887     cmd++;
2888   }
2889 
2890   /* hash MySQL functions (to be implemented) */
2891 
2892   /* hash all database names */
2893   if (mysql_query(&mysql,"show databases") == 0)
2894   {
2895     if (!(databases = mysql_store_result(&mysql)))
2896       put_info(mysql_error(&mysql),INFO_INFO);
2897     else
2898     {
2899       while ((database_row=mysql_fetch_row(databases)))
2900       {
2901 	char *str=strdup_root(&hash_mem_root, (char*) database_row[0]);
2902 	if (str)
2903 	  add_word(&ht,(char*) str);
2904       }
2905       mysql_free_result(databases);
2906     }
2907   }
2908   /* hash all table names */
2909   if (mysql_query(&mysql,"show tables")==0)
2910   {
2911     if (!(tables = mysql_store_result(&mysql)))
2912       put_info(mysql_error(&mysql),INFO_INFO);
2913     else
2914     {
2915       if (mysql_num_rows(tables) > 0 && !opt_silent && write_info)
2916       {
2917 	tee_fprintf(stdout, "\
2918 Reading table information for completion of table and column names\n\
2919 You can turn off this feature to get a quicker startup with -A\n\n");
2920       }
2921       while ((table_row=mysql_fetch_row(tables)))
2922       {
2923 	char *str=strdup_root(&hash_mem_root, (char*) table_row[0]);
2924 	if (str &&
2925 	    !completion_hash_exists(&ht,(char*) str, (uint) strlen(str)))
2926 	  add_word(&ht,str);
2927       }
2928     }
2929   }
2930 
2931   /* hash all field names, both with the table prefix and without it */
2932   if (!tables)					/* no tables */
2933   {
2934     DBUG_VOID_RETURN;
2935   }
2936   mysql_data_seek(tables,0);
2937   if (!(field_names= (char ***) alloc_root(&hash_mem_root,sizeof(char **) *
2938 					   (uint) (mysql_num_rows(tables)+1))))
2939   {
2940     mysql_free_result(tables);
2941     DBUG_VOID_RETURN;
2942   }
2943   i=0;
2944   while ((table_row=mysql_fetch_row(tables)))
2945   {
2946     if ((fields=mysql_list_fields(&mysql,(const char*) table_row[0],NullS)))
2947     {
2948       num_fields=mysql_num_fields(fields);
2949       if (!(field_names[i] = (char **) alloc_root(&hash_mem_root,
2950 						  sizeof(char *) *
2951 						  (num_fields*2+1))))
2952       {
2953         mysql_free_result(fields);
2954         break;
2955       }
2956       field_names[i][num_fields*2]= NULL;
2957       j=0;
2958       while ((sql_field=mysql_fetch_field(fields)))
2959       {
2960 	sprintf(buf,"%.64s.%.64s",table_row[0],sql_field->name);
2961 	field_names[i][j] = strdup_root(&hash_mem_root,buf);
2962 	add_word(&ht,field_names[i][j]);
2963 	field_names[i][num_fields+j] = strdup_root(&hash_mem_root,
2964 						   sql_field->name);
2965 	if (!completion_hash_exists(&ht,field_names[i][num_fields+j],
2966 				    (uint) strlen(field_names[i][num_fields+j])))
2967 	  add_word(&ht,field_names[i][num_fields+j]);
2968 	j++;
2969       }
2970       mysql_free_result(fields);
2971     }
2972     else
2973       field_names[i]= 0;
2974 
2975     i++;
2976   }
2977   mysql_free_result(tables);
2978   field_names[i]=0;				// End pointer
2979   DBUG_VOID_RETURN;
2980 }
2981 
2982 	/* for gnu readline */
2983 
2984 #ifndef HAVE_INDEX
2985 extern "C" {
2986 extern char *index(const char *,int c),*rindex(const char *,int);
2987 
index(const char * s,int c)2988 char *index(const char *s,int c)
2989 {
2990   for (;;)
2991   {
2992      if (*s == (char) c) return (char*) s;
2993      if (!*s++) return NullS;
2994   }
2995 }
2996 
rindex(const char * s,int c)2997 char *rindex(const char *s,int c)
2998 {
2999   reg3 char *t;
3000 
3001   t = NullS;
3002   do if (*s == (char) c) t = (char*) s; while (*s++);
3003   return (char*) t;
3004 }
3005 }
3006 #endif
3007 
3008 /* Add the given line to mysql history. */
add_filtered_history(const char * string)3009 static void add_filtered_history(const char *string)
3010 {
3011   if (!check_histignore(string))
3012     add_history(string);
3013 }
3014 
3015 
3016 /**
3017   Perform a check on the given string if it contains
3018   any of the histignore patterns.
3019 
3020   @param string [IN]        String that needs to be checked.
3021 
3022   @return Operation status
3023       @retval 0    No match found
3024       @retval 1    Match found
3025 */
3026 
3027 static
check_histignore(const char * string)3028 my_bool check_histignore(const char *string)
3029 {
3030   uint i;
3031   int rc;
3032 
3033   LEX_STRING *tmp;
3034 
3035   DBUG_ENTER("check_histignore");
3036 
3037   for (i= 0; i < histignore_patterns.elements; i++)
3038   {
3039     tmp= dynamic_element(&histignore_patterns, i, LEX_STRING *);
3040     if ((rc= charset_info->coll->wildcmp(charset_info,
3041                                          string, string + strlen(string),
3042                                          tmp->str, tmp->str + tmp->length,
3043                                          wild_prefix, wild_one,
3044                                          wild_many)) == 0)
3045       DBUG_RETURN(1);
3046   }
3047   DBUG_RETURN(0);
3048 }
3049 
3050 
3051 /**
3052   Parse the histignore list into pattern tokens.
3053 
3054   @return Operation status
3055       @retval 0    Success
3056       @retval 1    Failure
3057 */
3058 
3059 static
parse_histignore()3060 my_bool parse_histignore()
3061 {
3062   LEX_STRING pattern;
3063 
3064   char *token;
3065   const char *search= ":";
3066 
3067   DBUG_ENTER("parse_histignore");
3068 
3069   if (init_hist_patterns())
3070     DBUG_RETURN(1);
3071 
3072   token= strtok(histignore_buffer.str, search);
3073 
3074   while(token != NULL)
3075   {
3076     pattern.str= token;
3077     pattern.length= strlen(pattern.str);
3078     insert_dynamic(&histignore_patterns, &pattern);
3079     token= strtok(NULL, search);
3080   }
3081   DBUG_RETURN(0);
3082 }
3083 
3084 static
init_hist_patterns()3085 my_bool init_hist_patterns()
3086 {
3087   return my_init_dynamic_array(&histignore_patterns,
3088                                sizeof(LEX_STRING), 50, 50);
3089 }
3090 
3091 static
free_hist_patterns()3092 void free_hist_patterns()
3093 {
3094   delete_dynamic(&histignore_patterns);
3095 }
3096 #endif /* HAVE_READLINE */
3097 
3098 
reconnect(void)3099 static int reconnect(void)
3100 {
3101   /* purecov: begin tested */
3102   if (opt_reconnect)
3103   {
3104     put_info("No connection. Trying to reconnect...",INFO_INFO);
3105     (void) com_connect((String *) 0, 0);
3106     if (opt_rehash)
3107       com_rehash(NULL, NULL);
3108   }
3109   if (!connected)
3110     return put_info("Can't connect to the server\n",INFO_ERROR);
3111   /* purecov: end */
3112   return 0;
3113 }
3114 
get_current_db()3115 static void get_current_db()
3116 {
3117   MYSQL_RES *res;
3118 
3119   /* If one_database is set, current_db is not supposed to change. */
3120   if (one_database)
3121     return;
3122 
3123   my_free(current_db);
3124   current_db= NULL;
3125   /* In case of error below current_db will be NULL */
3126   if (!mysql_query(&mysql, "SELECT DATABASE()") &&
3127       (res= mysql_use_result(&mysql)))
3128   {
3129     MYSQL_ROW row= mysql_fetch_row(res);
3130     if (row && row[0])
3131       current_db= my_strdup(row[0], MYF(MY_WME));
3132     mysql_free_result(res);
3133   }
3134 }
3135 
3136 /***************************************************************************
3137  The different commands
3138 ***************************************************************************/
3139 
mysql_real_query_for_lazy(const char * buf,int length)3140 int mysql_real_query_for_lazy(const char *buf, int length)
3141 {
3142   for (uint retry=0;; retry++)
3143   {
3144     int error;
3145     if (!mysql_real_query(&mysql,buf,length))
3146       return 0;
3147     error= put_error(&mysql);
3148     if (mysql_errno(&mysql) != CR_SERVER_GONE_ERROR || retry > 1 ||
3149         !opt_reconnect)
3150       return error;
3151     if (reconnect())
3152       return error;
3153   }
3154 }
3155 
mysql_store_result_for_lazy(MYSQL_RES ** result)3156 int mysql_store_result_for_lazy(MYSQL_RES **result)
3157 {
3158   if ((*result=mysql_store_result(&mysql)))
3159     return 0;
3160 
3161   if (mysql_error(&mysql)[0])
3162     return put_error(&mysql);
3163   return 0;
3164 }
3165 
print_help_item(MYSQL_ROW * cur,int num_name,int num_cat,char * last_char)3166 static void print_help_item(MYSQL_ROW *cur, int num_name, int num_cat, char *last_char)
3167 {
3168   char ccat= (*cur)[num_cat][0];
3169   if (*last_char != ccat)
3170   {
3171     put_info(ccat == 'Y' ? "categories:" : "topics:", INFO_INFO);
3172     *last_char= ccat;
3173   }
3174   tee_fprintf(PAGER, "   %s\n", (*cur)[num_name]);
3175 }
3176 
3177 
com_server_help(String * buffer MY_ATTRIBUTE ((unused)),char * line MY_ATTRIBUTE ((unused)),char * help_arg)3178 static int com_server_help(String *buffer MY_ATTRIBUTE((unused)),
3179 			   char *line MY_ATTRIBUTE((unused)), char *help_arg)
3180 {
3181   MYSQL_ROW cur;
3182   const char *server_cmd;
3183   char cmd_buf[100 + 1];
3184   MYSQL_RES *result;
3185   int error;
3186 
3187   if (help_arg[0] != '\'')
3188   {
3189 	char *end_arg= strend(help_arg);
3190 	if(--end_arg)
3191 	{
3192 		while (my_isspace(charset_info,*end_arg))
3193           end_arg--;
3194 		*++end_arg= '\0';
3195 	}
3196 	(void) strxnmov(cmd_buf, sizeof(cmd_buf), "help '", help_arg, "'", NullS);
3197   }
3198   else
3199     (void) strxnmov(cmd_buf, sizeof(cmd_buf), "help ", help_arg, NullS);
3200 
3201   server_cmd= cmd_buf;
3202 
3203   if (!status.batch)
3204   {
3205     old_buffer= *buffer;
3206     old_buffer.copy();
3207   }
3208 
3209   if (!connected && reconnect())
3210     return 1;
3211 
3212   if ((error= mysql_real_query_for_lazy(server_cmd,(int)strlen(server_cmd))) ||
3213       (error= mysql_store_result_for_lazy(&result)))
3214     return error;
3215 
3216   if (result)
3217   {
3218     unsigned int num_fields= mysql_num_fields(result);
3219     my_ulonglong num_rows= mysql_num_rows(result);
3220     mysql_fetch_fields(result);
3221     if (num_fields==3 && num_rows==1)
3222     {
3223       if (!(cur= mysql_fetch_row(result)))
3224       {
3225 	error= -1;
3226 	goto err;
3227       }
3228 
3229       init_pager();
3230       tee_fprintf(PAGER,   "Name: \'%s\'\n", cur[0]);
3231       tee_fprintf(PAGER,   "Description:\n%s", cur[1]);
3232       if (cur[2] && *((char*)cur[2]))
3233 	tee_fprintf(PAGER, "Examples:\n%s", cur[2]);
3234       tee_fprintf(PAGER,   "\n");
3235       end_pager();
3236     }
3237     else if (num_fields >= 2 && num_rows)
3238     {
3239       init_pager();
3240       char last_char= 0;
3241 
3242       int num_name= 0, num_cat= 0;
3243       LINT_INIT(num_name);
3244       LINT_INIT(num_cat);
3245 
3246       if (num_fields == 2)
3247       {
3248 	put_info("Many help items for your request exist.", INFO_INFO);
3249 	put_info("To make a more specific request, please type 'help <item>',\nwhere <item> is one of the following", INFO_INFO);
3250 	num_name= 0;
3251 	num_cat= 1;
3252       }
3253       else if ((cur= mysql_fetch_row(result)))
3254       {
3255 	tee_fprintf(PAGER, "You asked for help about help category: \"%s\"\n", cur[0]);
3256 	put_info("For more information, type 'help <item>', where <item> is one of the following", INFO_INFO);
3257 	num_name= 1;
3258 	num_cat= 2;
3259 	print_help_item(&cur,1,2,&last_char);
3260       }
3261 
3262       while ((cur= mysql_fetch_row(result)))
3263 	print_help_item(&cur,num_name,num_cat,&last_char);
3264       tee_fprintf(PAGER, "\n");
3265       end_pager();
3266     }
3267     else
3268     {
3269       put_info("\nNothing found", INFO_INFO);
3270       if (strncasecmp(server_cmd, "help 'contents'", 15) == 0)
3271       {
3272          put_info("\nPlease check if 'help tables' are loaded.\n", INFO_INFO);
3273          goto err;
3274       }
3275       put_info("Please try to run 'help contents' for a list of all accessible topics\n", INFO_INFO);
3276     }
3277   }
3278 
3279 err:
3280   mysql_free_result(result);
3281   return error;
3282 }
3283 
3284 static int
com_help(String * buffer MY_ATTRIBUTE ((unused)),char * line MY_ATTRIBUTE ((unused)))3285 com_help(String *buffer MY_ATTRIBUTE((unused)),
3286 	 char *line MY_ATTRIBUTE((unused)))
3287 {
3288   reg1 int i, j;
3289   char * help_arg= strchr(line,' '), buff[32], *end;
3290   if (help_arg)
3291   {
3292     while (my_isspace(charset_info,*help_arg))
3293       help_arg++;
3294 	if (*help_arg)
3295 	  return com_server_help(buffer,line,help_arg);
3296   }
3297 
3298   put_info("\nFor information about MySQL products and services, visit:\n"
3299            "   http://www.mysql.com/\n"
3300            "For developer information, including the MySQL Reference Manual, "
3301            "visit:\n"
3302            "   http://dev.mysql.com/\n"
3303            "To buy MySQL Enterprise support, training, or other products, visit:\n"
3304            "   https://shop.mysql.com/\n", INFO_INFO);
3305   put_info("List of all MySQL commands:", INFO_INFO);
3306   if (!named_cmds)
3307     put_info("Note that all text commands must be first on line and end with ';'",INFO_INFO);
3308   for (i = 0; commands[i].name; i++)
3309   {
3310     end= strmov(buff, commands[i].name);
3311     for (j= (int)strlen(commands[i].name); j < 10; j++)
3312       end= strmov(end, " ");
3313     if (commands[i].func)
3314       tee_fprintf(stdout, "%s(\\%c) %s\n", buff,
3315 		  commands[i].cmd_char, commands[i].doc);
3316   }
3317   if (connected && mysql_get_server_version(&mysql) >= 40100)
3318     put_info("\nFor server side help, type 'help contents'\n", INFO_INFO);
3319   return 0;
3320 }
3321 
3322 
3323 	/* ARGSUSED */
3324 static int
com_clear(String * buffer,char * line MY_ATTRIBUTE ((unused)))3325 com_clear(String *buffer,char *line MY_ATTRIBUTE((unused)))
3326 {
3327 #ifdef HAVE_READLINE
3328   if (status.add_to_history)
3329     fix_history(buffer);
3330 #endif
3331   buffer->length(0);
3332   return 0;
3333 }
3334 
3335 	/* ARGSUSED */
3336 static int
com_charset(String * buffer MY_ATTRIBUTE ((unused)),char * line)3337 com_charset(String *buffer MY_ATTRIBUTE((unused)), char *line)
3338 {
3339   char buff[256], *param;
3340   const CHARSET_INFO *new_cs;
3341   strmake(buff, line, sizeof(buff) - 1);
3342   param= get_arg(buff, 0);
3343   if (!param || !*param)
3344   {
3345     return put_info("Usage: \\C charset_name | charset charset_name",
3346 		    INFO_ERROR, 0);
3347   }
3348   new_cs= get_charset_by_csname(param, MY_CS_PRIMARY, MYF(MY_WME));
3349   if (new_cs)
3350   {
3351     charset_info= new_cs;
3352     mysql_set_character_set(&mysql, charset_info->csname);
3353     default_charset= (char *)charset_info->csname;
3354     put_info("Charset changed", INFO_INFO);
3355   }
3356   else put_info("Charset is not found", INFO_INFO);
3357   return 0;
3358 }
3359 
3360 /*
3361   Execute command
3362   Returns: 0  if ok
3363           -1 if not fatal error
3364 	  1  if fatal error
3365 */
3366 
3367 
3368 static int
com_go(String * buffer,char * line MY_ATTRIBUTE ((unused)))3369 com_go(String *buffer,char *line MY_ATTRIBUTE((unused)))
3370 {
3371   char		buff[200]; /* about 110 chars used so far */
3372   char		time_buff[52+3+1]; /* time max + space&parens + NUL */
3373   MYSQL_RES	*result;
3374   ulong		timer, warnings= 0;
3375   uint		error= 0;
3376   int           err= 0;
3377 
3378   interrupted_query= 0;
3379   if (!status.batch)
3380   {
3381     old_buffer= *buffer;			// Save for edit command
3382     old_buffer.copy();
3383   }
3384 
3385   /* Remove garbage for nicer messages */
3386   LINT_INIT(buff[0]);
3387   remove_cntrl(*buffer);
3388 
3389   if (buffer->is_empty())
3390   {
3391     if (status.batch)				// Ignore empty quries
3392       return 0;
3393     return put_info("No query specified\n",INFO_ERROR);
3394 
3395   }
3396   if (!connected && reconnect())
3397   {
3398     buffer->length(0);				// Remove query on error
3399     return opt_reconnect ? -1 : 1;          // Fatal error
3400   }
3401   if (verbose)
3402     (void) com_print(buffer,0);
3403 
3404   if (skip_updates &&
3405       (buffer->length() < 4 || my_strnncoll(charset_info,
3406 					    (const uchar*)buffer->ptr(),4,
3407 					    (const uchar*)"SET ",4)))
3408   {
3409     (void) put_info("Ignoring query to other database",INFO_INFO);
3410     return 0;
3411   }
3412 
3413   timer=start_timer();
3414   executing_query= 1;
3415   error= mysql_real_query_for_lazy(buffer->ptr(),buffer->length());
3416 
3417 #ifdef HAVE_READLINE
3418   if (status.add_to_history)
3419   {
3420     buffer->append(vertical ? "\\G" : delimiter);
3421     /* Append final command onto history */
3422     fix_history(buffer);
3423   }
3424 #endif
3425 
3426   buffer->length(0);
3427 
3428   if (error)
3429     goto end;
3430 
3431   do
3432   {
3433     char *pos;
3434     bool batchmode= (status.batch && verbose <= 1) ? TRUE : FALSE;
3435     buff[0]= 0;
3436 
3437     if (quick)
3438     {
3439       if (!(result=mysql_use_result(&mysql)) && mysql_field_count(&mysql))
3440       {
3441         error= put_error(&mysql);
3442         goto end;
3443       }
3444     }
3445     else
3446     {
3447       error= mysql_store_result_for_lazy(&result);
3448       if (error)
3449         goto end;
3450     }
3451 
3452     if (verbose >= 3 || !opt_silent)
3453       mysql_end_timer(timer,time_buff);
3454     else
3455       time_buff[0]= '\0';
3456 
3457     /* Every branch must truncate  buff . */
3458     if (result)
3459     {
3460       if (!mysql_num_rows(result) && ! quick && !column_types_flag)
3461       {
3462 	strmov(buff, "Empty set");
3463         if (opt_xml)
3464         {
3465           /*
3466             We must print XML header and footer
3467             to produce a well-formed XML even if
3468             the result set is empty (Bug#27608).
3469           */
3470           init_pager();
3471           print_table_data_xml(result);
3472           end_pager();
3473         }
3474       }
3475       else
3476       {
3477 	init_pager();
3478 	if (opt_html)
3479 	  print_table_data_html(result);
3480 	else if (opt_xml)
3481 	  print_table_data_xml(result);
3482   else if (vertical || (auto_vertical_output && (terminal_width < get_result_width(result))))
3483 	  print_table_data_vertically(result);
3484 	else if (opt_silent && verbose <= 2 && !output_tables)
3485 	  print_tab_data(result);
3486 	else
3487 	  print_table_data(result);
3488         if( !batchmode )
3489 	  sprintf(buff,"%lld %s in set",
3490 	          mysql_num_rows(result),
3491 		  mysql_num_rows(result) == 1LL ? "row" : "rows");
3492 	end_pager();
3493         if (mysql_errno(&mysql))
3494           error= put_error(&mysql);
3495       }
3496     }
3497     else if (mysql_affected_rows(&mysql) == ~(ulonglong) 0)
3498       strmov(buff,"Query OK");
3499     else if( !batchmode )
3500       sprintf(buff,"Query OK, %lld %s affected",
3501 	      mysql_affected_rows(&mysql),
3502 	      mysql_affected_rows(&mysql) == 1LL ? "row" : "rows");
3503 
3504     pos=strend(buff);
3505     if ((warnings= mysql_warning_count(&mysql)) && !batchmode)
3506     {
3507       *pos++= ',';
3508       *pos++= ' ';
3509       pos=int10_to_str(warnings, pos, 10);
3510       pos=strmov(pos, " warning");
3511       if (warnings != 1)
3512 	*pos++= 's';
3513     }
3514     strmov(pos, time_buff);
3515     put_info(buff,INFO_RESULT);
3516     if (mysql_info(&mysql))
3517       put_info(mysql_info(&mysql),INFO_RESULT);
3518     put_info("",INFO_RESULT);			// Empty row
3519 
3520     if (result && !mysql_eof(result))	/* Something wrong when using quick */
3521       error= put_error(&mysql);
3522     else if (unbuffered)
3523       fflush(stdout);
3524     mysql_free_result(result);
3525   } while (!(err= mysql_next_result(&mysql)));
3526   if (err >= 1)
3527     error= put_error(&mysql);
3528 
3529 end:
3530 
3531  /* Show warnings if any or error occured */
3532   if (show_warnings == 1 && (warnings >= 1 || error))
3533     print_warnings();
3534 
3535   if (!error && !status.batch &&
3536       (mysql.server_status & SERVER_STATUS_DB_DROPPED))
3537     get_current_db();
3538 
3539   executing_query= 0;
3540   return error;				/* New command follows */
3541 }
3542 
3543 
init_pager()3544 static void init_pager()
3545 {
3546 #ifdef USE_POPEN
3547   if (!opt_nopager)
3548   {
3549     if (!(PAGER= popen(pager, "w")))
3550     {
3551       tee_fprintf(stdout, "popen() failed! defaulting PAGER to stdout!\n");
3552       PAGER= stdout;
3553     }
3554   }
3555   else
3556 #endif
3557     PAGER= stdout;
3558 }
3559 
end_pager()3560 static void end_pager()
3561 {
3562 #ifdef USE_POPEN
3563   if (!opt_nopager)
3564     pclose(PAGER);
3565 #endif
3566 }
3567 
3568 
init_tee(const char * file_name)3569 static void init_tee(const char *file_name)
3570 {
3571   FILE* new_outfile;
3572   if (opt_outfile)
3573     end_tee();
3574   if (!(new_outfile= my_fopen(file_name, O_APPEND | O_WRONLY, MYF(MY_WME))))
3575   {
3576     tee_fprintf(stdout, "Error logging to file '%s'\n", file_name);
3577     return;
3578   }
3579   OUTFILE = new_outfile;
3580   strmake(outfile, file_name, FN_REFLEN-1);
3581   tee_fprintf(stdout, "Logging to file '%s'\n", file_name);
3582   opt_outfile= 1;
3583   return;
3584 }
3585 
3586 
end_tee()3587 static void end_tee()
3588 {
3589   my_fclose(OUTFILE, MYF(0));
3590   OUTFILE= 0;
3591   opt_outfile= 0;
3592   return;
3593 }
3594 
3595 
3596 static int
com_ego(String * buffer,char * line)3597 com_ego(String *buffer,char *line)
3598 {
3599   int result;
3600   bool oldvertical=vertical;
3601   vertical=1;
3602   result=com_go(buffer,line);
3603   vertical=oldvertical;
3604   return result;
3605 }
3606 
3607 
fieldtype2str(enum enum_field_types type)3608 static const char *fieldtype2str(enum enum_field_types type)
3609 {
3610   switch (type) {
3611     case MYSQL_TYPE_BIT:         return "BIT";
3612     case MYSQL_TYPE_BLOB:        return "BLOB";
3613     case MYSQL_TYPE_DATE:        return "DATE";
3614     case MYSQL_TYPE_DATETIME:    return "DATETIME";
3615     case MYSQL_TYPE_NEWDECIMAL:  return "NEWDECIMAL";
3616     case MYSQL_TYPE_DECIMAL:     return "DECIMAL";
3617     case MYSQL_TYPE_DOUBLE:      return "DOUBLE";
3618     case MYSQL_TYPE_ENUM:        return "ENUM";
3619     case MYSQL_TYPE_FLOAT:       return "FLOAT";
3620     case MYSQL_TYPE_GEOMETRY:    return "GEOMETRY";
3621     case MYSQL_TYPE_INT24:       return "INT24";
3622     case MYSQL_TYPE_LONG:        return "LONG";
3623     case MYSQL_TYPE_LONGLONG:    return "LONGLONG";
3624     case MYSQL_TYPE_LONG_BLOB:   return "LONG_BLOB";
3625     case MYSQL_TYPE_MEDIUM_BLOB: return "MEDIUM_BLOB";
3626     case MYSQL_TYPE_NEWDATE:     return "NEWDATE";
3627     case MYSQL_TYPE_NULL:        return "NULL";
3628     case MYSQL_TYPE_SET:         return "SET";
3629     case MYSQL_TYPE_SHORT:       return "SHORT";
3630     case MYSQL_TYPE_STRING:      return "STRING";
3631     case MYSQL_TYPE_TIME:        return "TIME";
3632     case MYSQL_TYPE_TIMESTAMP:   return "TIMESTAMP";
3633     case MYSQL_TYPE_TINY:        return "TINY";
3634     case MYSQL_TYPE_TINY_BLOB:   return "TINY_BLOB";
3635     case MYSQL_TYPE_VAR_STRING:  return "VAR_STRING";
3636     case MYSQL_TYPE_YEAR:        return "YEAR";
3637     default:                     return "?-unknown-?";
3638   }
3639 }
3640 
fieldflags2str(uint f)3641 static char *fieldflags2str(uint f) {
3642   static char buf[1024];
3643   char *s=buf;
3644   *s=0;
3645 #define ff2s_check_flag(X) \
3646                 if (f & X ## _FLAG) { s=strmov(s, # X " "); f &= ~ X ## _FLAG; }
3647   ff2s_check_flag(NOT_NULL);
3648   ff2s_check_flag(PRI_KEY);
3649   ff2s_check_flag(UNIQUE_KEY);
3650   ff2s_check_flag(MULTIPLE_KEY);
3651   ff2s_check_flag(BLOB);
3652   ff2s_check_flag(UNSIGNED);
3653   ff2s_check_flag(ZEROFILL);
3654   ff2s_check_flag(BINARY);
3655   ff2s_check_flag(ENUM);
3656   ff2s_check_flag(AUTO_INCREMENT);
3657   ff2s_check_flag(TIMESTAMP);
3658   ff2s_check_flag(SET);
3659   ff2s_check_flag(NO_DEFAULT_VALUE);
3660   ff2s_check_flag(NUM);
3661   ff2s_check_flag(PART_KEY);
3662   ff2s_check_flag(GROUP);
3663   ff2s_check_flag(UNIQUE);
3664   ff2s_check_flag(BINCMP);
3665   ff2s_check_flag(ON_UPDATE_NOW);
3666 #undef ff2s_check_flag
3667   if (f)
3668     sprintf(s, " unknows=0x%04x", f);
3669   return buf;
3670 }
3671 
3672 static void
print_field_types(MYSQL_RES * result)3673 print_field_types(MYSQL_RES *result)
3674 {
3675   MYSQL_FIELD   *field;
3676   uint i=0;
3677 
3678   while ((field = mysql_fetch_field(result)))
3679   {
3680     tee_fprintf(PAGER, "Field %3u:  `%s`\n"
3681                        "Catalog:    `%s`\n"
3682                        "Database:   `%s`\n"
3683                        "Table:      `%s`\n"
3684                        "Org_table:  `%s`\n"
3685                        "Type:       %s\n"
3686                        "Collation:  %s (%u)\n"
3687                        "Length:     %lu\n"
3688                        "Max_length: %lu\n"
3689                        "Decimals:   %u\n"
3690                        "Flags:      %s\n\n",
3691                 ++i,
3692                 field->name, field->catalog, field->db, field->table,
3693                 field->org_table, fieldtype2str(field->type),
3694                 get_charset_name(field->charsetnr), field->charsetnr,
3695                 field->length, field->max_length, field->decimals,
3696                 fieldflags2str(field->flags));
3697   }
3698   tee_puts("", PAGER);
3699 }
3700 
3701 /* Used to determine if we should invoke print_as_hex for this field */
3702 
3703 static bool
is_binary_field(MYSQL_FIELD * field)3704 is_binary_field(MYSQL_FIELD *field)
3705 {
3706   if ((field->charsetnr == 63) &&
3707       (field->type == MYSQL_TYPE_BIT ||
3708        field->type == MYSQL_TYPE_BLOB ||
3709        field->type == MYSQL_TYPE_LONG_BLOB ||
3710        field->type == MYSQL_TYPE_MEDIUM_BLOB ||
3711        field->type == MYSQL_TYPE_TINY_BLOB ||
3712        field->type == MYSQL_TYPE_VAR_STRING ||
3713        field->type == MYSQL_TYPE_STRING ||
3714        field->type == MYSQL_TYPE_VARCHAR ||
3715        field->type == MYSQL_TYPE_GEOMETRY))
3716     return 1;
3717   return 0;
3718 }
3719 
3720 
3721 /* Print binary value as hex literal (0x ...) */
3722 
3723 static void
print_as_hex(FILE * output_file,const char * str,ulong len,ulong total_bytes_to_send)3724 print_as_hex(FILE *output_file, const char *str, ulong len, ulong total_bytes_to_send)
3725 {
3726   const char *ptr= str, *end= ptr+len;
3727   ulong i;
3728   fprintf(output_file, "0x");
3729   for(; ptr < end; ptr++)
3730     fprintf(output_file, "%02X", *((uchar*)ptr));
3731   for (i= 2*len+2; i < total_bytes_to_send; i++)
3732     tee_putc((int)' ', output_file);
3733 }
3734 
3735 static void
print_table_data(MYSQL_RES * result)3736 print_table_data(MYSQL_RES *result)
3737 {
3738   String separator(256);
3739   MYSQL_ROW	cur;
3740   MYSQL_FIELD	*field;
3741   bool		*num_flag;
3742   size_t        sz;
3743 
3744   sz= sizeof(bool) * mysql_num_fields(result);
3745   num_flag= (bool *) my_safe_alloca(sz, MAX_ALLOCA_SIZE);
3746   if (column_types_flag)
3747   {
3748     print_field_types(result);
3749     if (!mysql_num_rows(result))
3750       return;
3751     mysql_field_seek(result,0);
3752   }
3753   separator.copy("+",1,charset_info);
3754   while ((field = mysql_fetch_field(result)))
3755   {
3756     uint length= column_names ? field->name_length : 0;
3757     if (quick)
3758       length= max<size_t>(length, field->length);
3759     else
3760       length= max<size_t>(length, field->max_length);
3761     if (length < 4 && !IS_NOT_NULL(field->flags))
3762       length=4;					// Room for "NULL"
3763     if (opt_binhex && is_binary_field(field))
3764       length= 2 + length * 2;
3765     field->max_length=(ulong) length;
3766     separator.fill(separator.length()+length+2,'-');
3767     separator.append('+');
3768   }
3769   separator.append('\0');                       // End marker for \0
3770   tee_puts((char*) separator.ptr(), PAGER);
3771   if (column_names)
3772   {
3773     mysql_field_seek(result,0);
3774     (void) tee_fputs("|", PAGER);
3775     for (uint off=0; (field = mysql_fetch_field(result)) ; off++)
3776     {
3777       uint name_length= (uint) strlen(field->name);
3778       uint numcells= charset_info->cset->numcells(charset_info,
3779                                                   field->name,
3780                                                   field->name + name_length);
3781       uint display_length= field->max_length + name_length - numcells;
3782       tee_fprintf(PAGER, " %-*s |",
3783                   min<int>(display_length, MAX_COLUMN_LENGTH),
3784                   field->name);
3785       num_flag[off]= IS_NUM(field->type);
3786     }
3787     (void) tee_fputs("\n", PAGER);
3788     tee_puts((char*) separator.ptr(), PAGER);
3789   }
3790 
3791   while ((cur= mysql_fetch_row(result)))
3792   {
3793     if (interrupted_query)
3794       break;
3795     ulong *lengths= mysql_fetch_lengths(result);
3796     (void) tee_fputs("| ", PAGER);
3797     mysql_field_seek(result, 0);
3798     for (uint off= 0; off < mysql_num_fields(result); off++)
3799     {
3800       const char *buffer;
3801       uint data_length;
3802       uint field_max_length;
3803       uint visible_length;
3804       uint extra_padding;
3805 
3806       if (off)
3807         (void) tee_fputs(" ", PAGER);
3808 
3809       if (cur[off] == NULL)
3810       {
3811         buffer= "NULL";
3812         data_length= 4;
3813       }
3814       else
3815       {
3816         buffer= cur[off];
3817         data_length= (uint) lengths[off];
3818       }
3819 
3820       field= mysql_fetch_field(result);
3821       field_max_length= field->max_length;
3822 
3823       /*
3824        How many text cells on the screen will this string span?  If it contains
3825        multibyte characters, then the number of characters we occupy on screen
3826        will be fewer than the number of bytes we occupy in memory.
3827 
3828        We need to find how much screen real-estate we will occupy to know how
3829        many extra padding-characters we should send with the printing function.
3830       */
3831       visible_length= charset_info->cset->numcells(charset_info, buffer, buffer + data_length);
3832       extra_padding= (uint) (data_length - visible_length);
3833 
3834       if (opt_binhex && is_binary_field(field))
3835         print_as_hex(PAGER, cur[off], lengths[off], field_max_length);
3836       else if (field_max_length > MAX_COLUMN_LENGTH)
3837         tee_print_sized_data(buffer, data_length, MAX_COLUMN_LENGTH+extra_padding, FALSE);
3838       else
3839       {
3840         if (num_flag[off] != 0) /* if it is numeric, we right-justify it */
3841           tee_print_sized_data(buffer, data_length, field_max_length+extra_padding, TRUE);
3842         else
3843           tee_print_sized_data(buffer, data_length, field_max_length+extra_padding, FALSE);
3844       }
3845       tee_fputs(" |", PAGER);
3846     }
3847     (void) tee_fputs("\n", PAGER);
3848   }
3849   tee_puts((char*) separator.ptr(), PAGER);
3850   my_safe_afree((bool *) num_flag, sz, MAX_ALLOCA_SIZE);
3851 }
3852 
3853 /**
3854   Return the length of a field after it would be rendered into text.
3855 
3856   This doesn't know or care about multibyte characters.  Assume we're
3857   using such a charset.  We can't know that all of the upcoming rows
3858   for this column will have bytes that each render into some fraction
3859   of a character.  It's at least possible that a row has bytes that
3860   all render into one character each, and so the maximum length is
3861   still the number of bytes.  (Assumption 1:  This can't be better
3862   because we can never know the number of characters that the DB is
3863   going to send -- only the number of bytes.  2: Chars <= Bytes.)
3864 
3865   @param  field  Pointer to a field to be inspected
3866 
3867   @returns  number of character positions to be used, at most
3868 */
get_field_disp_length(MYSQL_FIELD * field)3869 static int get_field_disp_length(MYSQL_FIELD *field)
3870 {
3871   uint length= column_names ? field->name_length : 0;
3872 
3873   if (quick)
3874     length= max<uint>(length, field->length);
3875   else
3876     length= max<uint>(length, field->max_length);
3877 
3878   if (length < 4 && !IS_NOT_NULL(field->flags))
3879     length= 4;				/* Room for "NULL" */
3880 
3881   return length;
3882 }
3883 
3884 /**
3885   For a new result, return the max number of characters that any
3886   upcoming row may return.
3887 
3888   @param  result  Pointer to the result to judge
3889 
3890   @returns  The max number of characters in any row of this result
3891 */
get_result_width(MYSQL_RES * result)3892 static int get_result_width(MYSQL_RES *result)
3893 {
3894   unsigned int len= 0;
3895   MYSQL_FIELD *field;
3896   MYSQL_FIELD_OFFSET offset;
3897 
3898 #ifndef DBUG_OFF
3899   offset= mysql_field_tell(result);
3900   DBUG_ASSERT(offset == 0);
3901 #else
3902   offset= 0;
3903 #endif
3904 
3905   while ((field= mysql_fetch_field(result)) != NULL)
3906     len+= get_field_disp_length(field) + 3; /* plus bar, space, & final space */
3907 
3908   (void) mysql_field_seek(result, offset);
3909 
3910   return len + 1; /* plus final bar. */
3911 }
3912 
3913 static void
tee_print_sized_data(const char * data,unsigned int data_length,unsigned int total_bytes_to_send,bool right_justified)3914 tee_print_sized_data(const char *data, unsigned int data_length, unsigned int total_bytes_to_send, bool right_justified)
3915 {
3916   /*
3917     For '\0's print ASCII spaces instead, as '\0' is eaten by (at
3918     least my) console driver, and that messes up the pretty table
3919     grid.  (The \0 is also the reason we can't use fprintf() .)
3920   */
3921   unsigned int i;
3922 
3923   if (right_justified)
3924     for (i= data_length; i < total_bytes_to_send; i++)
3925       tee_putc((int)' ', PAGER);
3926 
3927   tee_write(PAGER, data, data_length, MY_PRINT_SPS_0 | MY_PRINT_MB);
3928 
3929   if (! right_justified)
3930     for (i= data_length; i < total_bytes_to_send; i++)
3931       tee_putc((int)' ', PAGER);
3932 }
3933 
3934 
3935 
3936 static void
print_table_data_html(MYSQL_RES * result)3937 print_table_data_html(MYSQL_RES *result)
3938 {
3939   MYSQL_ROW	cur;
3940   MYSQL_FIELD	*field;
3941 
3942   mysql_field_seek(result,0);
3943   (void) tee_fputs("<TABLE BORDER=1><TR>", PAGER);
3944   if (column_names)
3945   {
3946     while((field = mysql_fetch_field(result)))
3947     {
3948       tee_fputs("<TH>", PAGER);
3949       if (field->name && field->name[0])
3950         xmlencode_print(field->name, field->name_length);
3951       else
3952         tee_fputs(field->name ? " &nbsp; " : "NULL", PAGER);
3953       tee_fputs("</TH>", PAGER);
3954     }
3955     (void) tee_fputs("</TR>", PAGER);
3956   }
3957   while ((cur = mysql_fetch_row(result)))
3958   {
3959     if (interrupted_query)
3960       break;
3961     ulong *lengths=mysql_fetch_lengths(result);
3962     field= mysql_fetch_fields(result);
3963     (void) tee_fputs("<TR>", PAGER);
3964     for (uint i=0; i < mysql_num_fields(result); i++)
3965     {
3966       (void) tee_fputs("<TD>", PAGER);
3967       if (opt_binhex && is_binary_field(&field[i]))
3968         print_as_hex(PAGER, cur[i], lengths[i], lengths[i]);
3969       else
3970         xmlencode_print(cur[i], lengths[i]);
3971       (void) tee_fputs("</TD>", PAGER);
3972     }
3973     (void) tee_fputs("</TR>", PAGER);
3974   }
3975   (void) tee_fputs("</TABLE>", PAGER);
3976 }
3977 
3978 static void
print_table_data_xml(MYSQL_RES * result)3979 print_table_data_xml(MYSQL_RES *result)
3980 {
3981   MYSQL_ROW   cur;
3982   MYSQL_FIELD *fields;
3983 
3984   mysql_field_seek(result,0);
3985 
3986   tee_fputs("<?xml version=\"1.0\"?>\n\n<resultset statement=\"", PAGER);
3987   xmlencode_print(glob_buffer.ptr(), (int)strlen(glob_buffer.ptr()));
3988   tee_fputs("\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">",
3989             PAGER);
3990 
3991   fields = mysql_fetch_fields(result);
3992   while ((cur = mysql_fetch_row(result)))
3993   {
3994     if (interrupted_query)
3995       break;
3996     ulong *lengths=mysql_fetch_lengths(result);
3997     (void) tee_fputs("\n  <row>\n", PAGER);
3998     for (uint i=0; i < mysql_num_fields(result); i++)
3999     {
4000       tee_fprintf(PAGER, "\t<field name=\"");
4001       xmlencode_print(fields[i].name, (uint) strlen(fields[i].name));
4002       if (cur[i])
4003       {
4004         tee_fprintf(PAGER, "\">");
4005         if (opt_binhex && is_binary_field(&fields[i]))
4006           print_as_hex(PAGER, cur[i], lengths[i], lengths[i]);
4007         else
4008           xmlencode_print(cur[i], lengths[i]);
4009         tee_fprintf(PAGER, "</field>\n");
4010       }
4011       else
4012         tee_fprintf(PAGER, "\" xsi:nil=\"true\" />\n");
4013     }
4014     (void) tee_fputs("  </row>\n", PAGER);
4015   }
4016   (void) tee_fputs("</resultset>\n", PAGER);
4017 }
4018 
4019 
4020 static void
print_table_data_vertically(MYSQL_RES * result)4021 print_table_data_vertically(MYSQL_RES *result)
4022 {
4023   MYSQL_ROW	cur;
4024   uint		max_length=0;
4025   MYSQL_FIELD	*field;
4026 
4027   while ((field = mysql_fetch_field(result)))
4028   {
4029     uint length= field->name_length;
4030     if (length > max_length)
4031       max_length= length;
4032     field->max_length=length;
4033   }
4034 
4035   mysql_field_seek(result,0);
4036   for (uint row_count=1; (cur= mysql_fetch_row(result)); row_count++)
4037   {
4038     if (interrupted_query)
4039       break;
4040     mysql_field_seek(result,0);
4041     tee_fprintf(PAGER,
4042 		"*************************** %d. row ***************************\n", row_count);
4043 
4044     ulong *lengths= mysql_fetch_lengths(result);
4045 
4046     for (uint off=0; off < mysql_num_fields(result); off++)
4047     {
4048       field= mysql_fetch_field(result);
4049       if (column_names)
4050         tee_fprintf(PAGER, "%*s: ",(int) max_length,field->name);
4051       if (cur[off])
4052       {
4053         if (opt_binhex && is_binary_field(field))
4054           print_as_hex(PAGER, cur[off], lengths[off], lengths[off]);
4055         else
4056           tee_write(PAGER, cur[off], lengths[off], MY_PRINT_SPS_0 | MY_PRINT_MB);
4057         tee_putc('\n', PAGER);
4058       }
4059       else
4060         tee_fprintf(PAGER, "NULL\n");
4061     }
4062   }
4063 }
4064 
4065 
4066 /* print_warnings should be called right after executing a statement */
4067 
print_warnings()4068 static void print_warnings()
4069 {
4070   const char   *query;
4071   MYSQL_RES    *result;
4072   MYSQL_ROW    cur;
4073   my_ulonglong num_rows;
4074 
4075   /* Save current error before calling "show warnings" */
4076   uint error= mysql_errno(&mysql);
4077 
4078   /* Get the warnings */
4079   query= "show warnings";
4080   mysql_real_query_for_lazy(query, strlen(query));
4081   mysql_store_result_for_lazy(&result);
4082 
4083   /* Bail out when no warnings */
4084   if (!result || !(num_rows= mysql_num_rows(result)))
4085     goto end;
4086 
4087   cur= mysql_fetch_row(result);
4088 
4089   /*
4090     Don't print a duplicate of the current error.  It is possible for SHOW
4091     WARNINGS to return multiple errors with the same code, but different
4092     messages.  To be safe, skip printing the duplicate only if it is the only
4093     warning.
4094   */
4095   if (!cur || (num_rows == 1 && error == (uint) strtoul(cur[1], NULL, 10)))
4096     goto end;
4097 
4098   /* Print the warnings */
4099   init_pager();
4100   do
4101   {
4102     tee_fprintf(PAGER, "%s (Code %s): %s\n", cur[0], cur[1], cur[2]);
4103   } while ((cur= mysql_fetch_row(result)));
4104   end_pager();
4105 
4106 end:
4107   mysql_free_result(result);
4108 }
4109 
4110 
array_value(const char ** array,char key)4111 static const char *array_value(const char **array, char key)
4112 {
4113   for (; *array; array+= 2)
4114     if (**array == key)
4115       return array[1];
4116   return 0;
4117 }
4118 
4119 
4120 static void
xmlencode_print(const char * src,uint length)4121 xmlencode_print(const char *src, uint length)
4122 {
4123   if (!src)
4124     tee_fputs("NULL", PAGER);
4125   else
4126     tee_write(PAGER, src, length, MY_PRINT_XML | MY_PRINT_MB);
4127 }
4128 
4129 
4130 static void
safe_put_field(const char * pos,ulong length)4131 safe_put_field(const char *pos,ulong length)
4132 {
4133   if (!pos)
4134     tee_fputs("NULL", PAGER);
4135   else
4136   {
4137     int flags= MY_PRINT_MB | (opt_raw_data ? 0 : (MY_PRINT_ESC_0 | MY_PRINT_CTRL));
4138     /* Can't use tee_fputs(), it stops with NUL characters. */
4139     tee_write(PAGER, pos, length, flags);
4140   }
4141 }
4142 
4143 
4144 static void
print_tab_data(MYSQL_RES * result)4145 print_tab_data(MYSQL_RES *result)
4146 {
4147   MYSQL_ROW	cur;
4148   MYSQL_FIELD	*field;
4149   ulong		*lengths;
4150 
4151   if (opt_silent < 2 && column_names)
4152   {
4153     int first=0;
4154     while ((field = mysql_fetch_field(result)))
4155     {
4156       if (first++)
4157 	(void) tee_fputs("\t", PAGER);
4158       (void) tee_fputs(field->name, PAGER);
4159     }
4160     (void) tee_fputs("\n", PAGER);
4161   }
4162   while ((cur = mysql_fetch_row(result)))
4163   {
4164     lengths=mysql_fetch_lengths(result);
4165 
4166     field= mysql_fetch_fields(result);
4167     if (opt_binhex && is_binary_field(&field[0]))
4168       print_as_hex(PAGER, cur[0], lengths[0], lengths[0]);
4169     else
4170       safe_put_field(cur[0],lengths[0]);
4171 
4172     for (uint off=1 ; off < mysql_num_fields(result); off++)
4173     {
4174       (void) tee_fputs("\t", PAGER);
4175 
4176       if (opt_binhex && field && is_binary_field(&field[off]))
4177         print_as_hex(PAGER, cur[off], lengths[off], lengths[off]);
4178       else
4179         safe_put_field(cur[off], lengths[off]);
4180     }
4181     (void) tee_fputs("\n", PAGER);
4182   }
4183 }
4184 
4185 static int
com_tee(String * buffer MY_ATTRIBUTE ((unused)),char * line MY_ATTRIBUTE ((unused)))4186 com_tee(String *buffer MY_ATTRIBUTE((unused)),
4187         char *line MY_ATTRIBUTE((unused)))
4188 {
4189   char file_name[FN_REFLEN], *end, *param;
4190 
4191   while (my_isspace(charset_info,*line))
4192     line++;
4193   if (!(param = strchr(line, ' '))) // if outfile wasn't given, use the default
4194   {
4195     if (!strlen(outfile))
4196     {
4197       printf("No previous outfile available, you must give a filename!\n");
4198       return 0;
4199     }
4200     else if (opt_outfile)
4201     {
4202       tee_fprintf(stdout, "Currently logging to file '%s'\n", outfile);
4203       return 0;
4204     }
4205     else
4206       param = outfile;			//resume using the old outfile
4207   }
4208 
4209   /* eliminate the spaces before the parameters */
4210   while (my_isspace(charset_info,*param))
4211     param++;
4212   end= strmake(file_name, param, sizeof(file_name) - 1);
4213   /* remove end space from command line */
4214   while (end > file_name && (my_isspace(charset_info,end[-1]) ||
4215 			     my_iscntrl(charset_info,end[-1])))
4216     end--;
4217   end[0]= 0;
4218   if (end == file_name)
4219   {
4220     printf("No outfile specified!\n");
4221     return 0;
4222   }
4223   init_tee(file_name);
4224   return 0;
4225 }
4226 
4227 
4228 static int
com_notee(String * buffer MY_ATTRIBUTE ((unused)),char * line MY_ATTRIBUTE ((unused)))4229 com_notee(String *buffer MY_ATTRIBUTE((unused)),
4230 	  char *line MY_ATTRIBUTE((unused)))
4231 {
4232   if (opt_outfile)
4233     end_tee();
4234   tee_fprintf(stdout, "Outfile disabled.\n");
4235   return 0;
4236 }
4237 
4238 /*
4239   Sorry, this command is not available in Windows.
4240 */
4241 
4242 #ifdef USE_POPEN
4243 static int
com_pager(String * buffer MY_ATTRIBUTE ((unused)),char * line MY_ATTRIBUTE ((unused)))4244 com_pager(String *buffer MY_ATTRIBUTE((unused)),
4245           char *line MY_ATTRIBUTE((unused)))
4246 {
4247   char pager_name[FN_REFLEN], *end, *param;
4248 
4249   if (status.batch)
4250     return 0;
4251   /* Skip spaces in front of the pager command */
4252   while (my_isspace(charset_info, *line))
4253     line++;
4254   /* Skip the pager command */
4255   param= strchr(line, ' ');
4256   /* Skip the spaces between the command and the argument */
4257   while (param && my_isspace(charset_info, *param))
4258     param++;
4259   if (!param || !strlen(param)) // if pager was not given, use the default
4260   {
4261     if (!default_pager_set)
4262     {
4263       tee_fprintf(stdout, "Default pager wasn't set, using stdout.\n");
4264       opt_nopager=1;
4265       strmov(pager, "stdout");
4266       PAGER= stdout;
4267       return 0;
4268     }
4269     strmov(pager, default_pager);
4270   }
4271   else
4272   {
4273     end= strmake(pager_name, param, sizeof(pager_name)-1);
4274     while (end > pager_name && (my_isspace(charset_info,end[-1]) ||
4275                                 my_iscntrl(charset_info,end[-1])))
4276       end--;
4277     end[0]=0;
4278     strmov(pager, pager_name);
4279     strmov(default_pager, pager_name);
4280   }
4281   opt_nopager=0;
4282   tee_fprintf(stdout, "PAGER set to '%s'\n", pager);
4283   return 0;
4284 }
4285 
4286 
4287 static int
com_nopager(String * buffer MY_ATTRIBUTE ((unused)),char * line MY_ATTRIBUTE ((unused)))4288 com_nopager(String *buffer MY_ATTRIBUTE((unused)),
4289 	    char *line MY_ATTRIBUTE((unused)))
4290 {
4291   strmov(pager, "stdout");
4292   opt_nopager=1;
4293   PAGER= stdout;
4294   tee_fprintf(stdout, "PAGER set to stdout\n");
4295   return 0;
4296 }
4297 #endif
4298 
4299 
4300 /*
4301   Sorry, you can't send the result to an editor in Win32
4302 */
4303 
4304 #ifdef USE_POPEN
4305 static int
com_edit(String * buffer,char * line MY_ATTRIBUTE ((unused)))4306 com_edit(String *buffer,char *line MY_ATTRIBUTE((unused)))
4307 {
4308   char	filename[FN_REFLEN],buff[160];
4309   int	fd,tmp;
4310   const char *editor;
4311 
4312   if ((fd=create_temp_file(filename,NullS,"sql", O_CREAT | O_WRONLY,
4313 			   MYF(MY_WME))) < 0)
4314     goto err;
4315   if (buffer->is_empty() && !old_buffer.is_empty())
4316     (void) my_write(fd,(uchar*) old_buffer.ptr(),old_buffer.length(),
4317 		    MYF(MY_WME));
4318   else
4319     (void) my_write(fd,(uchar*) buffer->ptr(),buffer->length(),MYF(MY_WME));
4320   (void) my_close(fd,MYF(0));
4321 
4322   if (!(editor = (char *)getenv("EDITOR")) &&
4323       !(editor = (char *)getenv("VISUAL")))
4324     editor = "vi";
4325   strxmov(buff,editor," ",filename,NullS);
4326   if(system(buff) == -1)
4327     goto err;
4328 
4329   MY_STAT stat_arg;
4330   if (!my_stat(filename,&stat_arg,MYF(MY_WME)))
4331     goto err;
4332   if ((fd = my_open(filename,O_RDONLY, MYF(MY_WME))) < 0)
4333     goto err;
4334   (void) buffer->alloc((uint) stat_arg.st_size);
4335   if ((tmp=read(fd,(char*) buffer->ptr(),buffer->alloced_length())) >= 0L)
4336     buffer->length((uint) tmp);
4337   else
4338     buffer->length(0);
4339   (void) my_close(fd,MYF(0));
4340   (void) my_delete(filename,MYF(MY_WME));
4341 err:
4342   return 0;
4343 }
4344 #endif
4345 
4346 
4347 /* If arg is given, exit without errors. This happens on command 'quit' */
4348 
4349 static int
com_quit(String * buffer MY_ATTRIBUTE ((unused)),char * line MY_ATTRIBUTE ((unused)))4350 com_quit(String *buffer MY_ATTRIBUTE((unused)),
4351 	 char *line MY_ATTRIBUTE((unused)))
4352 {
4353   status.exit_status=0;
4354   return 1;
4355 }
4356 
4357 static int
com_rehash(String * buffer MY_ATTRIBUTE ((unused)),char * line MY_ATTRIBUTE ((unused)))4358 com_rehash(String *buffer MY_ATTRIBUTE((unused)),
4359 	 char *line MY_ATTRIBUTE((unused)))
4360 {
4361 #ifdef HAVE_READLINE
4362   build_completion_hash(1, 0);
4363 #endif
4364   return 0;
4365 }
4366 
4367 
4368 #ifdef USE_POPEN
4369 static int
com_shell(String * buffer MY_ATTRIBUTE ((unused)),char * line MY_ATTRIBUTE ((unused)))4370 com_shell(String *buffer MY_ATTRIBUTE((unused)),
4371           char *line MY_ATTRIBUTE((unused)))
4372 {
4373   char *shell_cmd;
4374 
4375   /* Skip space from line begin */
4376   while (my_isspace(charset_info, *line))
4377     line++;
4378   if (!(shell_cmd = strchr(line, ' ')))
4379   {
4380     put_info("Usage: \\! shell-command", INFO_ERROR);
4381     return -1;
4382   }
4383   /*
4384     The output of the shell command does not
4385     get directed to the pager or the outfile
4386   */
4387   if (system(shell_cmd) == -1)
4388   {
4389     put_info(strerror(errno), INFO_ERROR, errno);
4390     return -1;
4391   }
4392   return 0;
4393 }
4394 #endif
4395 
4396 
4397 static int
com_print(String * buffer,char * line MY_ATTRIBUTE ((unused)))4398 com_print(String *buffer,char *line MY_ATTRIBUTE((unused)))
4399 {
4400   tee_puts("--------------", stdout);
4401   (void) tee_fputs(buffer->c_ptr(), stdout);
4402   if (!buffer->length() || (*buffer)[buffer->length()-1] != '\n')
4403     tee_putc('\n', stdout);
4404   tee_puts("--------------\n", stdout);
4405   return 0;					/* If empty buffer */
4406 }
4407 
4408 	/* ARGSUSED */
4409 static int
com_connect(String * buffer,char * line)4410 com_connect(String *buffer, char *line)
4411 {
4412   char *tmp, buff[256];
4413   bool save_rehash= opt_rehash;
4414   int error;
4415 
4416   memset(buff, 0, sizeof(buff));
4417   if (buffer)
4418   {
4419     /*
4420       Two null bytes are needed in the end of buff to allow
4421       get_arg to find end of string the second time it's called.
4422     */
4423     tmp= strmake(buff, line, sizeof(buff)-2);
4424 #ifdef EXTRA_DEBUG
4425     tmp[1]= 0;
4426 #endif
4427     tmp= get_arg(buff, 0);
4428     if (tmp && *tmp)
4429     {
4430       my_free(current_db);
4431       current_db= my_strdup(tmp, MYF(MY_WME));
4432       tmp= get_arg(buff, 1);
4433       if (tmp)
4434       {
4435 	my_free(current_host);
4436 	current_host=my_strdup(tmp,MYF(MY_WME));
4437       }
4438     }
4439     else
4440     {
4441       /* Quick re-connect */
4442       opt_rehash= 0;                            /* purecov: tested */
4443     }
4444     buffer->length(0);				// command used
4445   }
4446   else
4447     opt_rehash= 0;
4448   error=sql_connect(current_host,current_db,current_user,opt_password,0);
4449   opt_rehash= save_rehash;
4450 
4451   if (connected)
4452   {
4453     sprintf(buff,"Connection id:    %lu",mysql_thread_id(&mysql));
4454     put_info(buff,INFO_INFO);
4455     sprintf(buff,"Current database: %.128s\n",
4456 	    current_db ? current_db : "*** NONE ***");
4457     put_info(buff,INFO_INFO);
4458   }
4459   return error;
4460 }
4461 
4462 
com_source(String * buffer MY_ATTRIBUTE ((unused)),char * line)4463 static int com_source(String *buffer MY_ATTRIBUTE((unused)),
4464                       char *line)
4465 {
4466   char source_name[FN_REFLEN], *end, *param;
4467   LINE_BUFFER *line_buff;
4468   int error;
4469   STATUS old_status;
4470   FILE *sql_file;
4471 
4472   /* Skip space from file name */
4473   while (my_isspace(charset_info,*line))
4474     line++;
4475   if (!(param = strchr(line, ' ')))		// Skip command name
4476     return put_info("Usage: \\. <filename> | source <filename>",
4477 		    INFO_ERROR, 0);
4478   while (my_isspace(charset_info,*param))
4479     param++;
4480   end=strmake(source_name,param,sizeof(source_name)-1);
4481   while (end > source_name && (my_isspace(charset_info,end[-1]) ||
4482                                my_iscntrl(charset_info,end[-1])))
4483     end--;
4484   end[0]=0;
4485   unpack_filename(source_name,source_name);
4486   /* open file name */
4487   if (!(sql_file = my_fopen(source_name, O_RDONLY | O_BINARY,MYF(0))))
4488   {
4489     char buff[FN_REFLEN+60];
4490     sprintf(buff,"Failed to open file '%s', error: %d", source_name,errno);
4491     return put_info(buff, INFO_ERROR, 0);
4492   }
4493 
4494   if (!(line_buff= batch_readline_init(MAX_BATCH_BUFFER_SIZE, sql_file)))
4495   {
4496     my_fclose(sql_file,MYF(0));
4497     return put_info("Can't initialize batch_readline", INFO_ERROR, 0);
4498   }
4499 
4500   /* Save old status */
4501   old_status=status;
4502   memset(&status, 0, sizeof(status));
4503 
4504   status.batch=old_status.batch;		// Run in batch mode
4505   status.line_buff=line_buff;
4506   status.file_name=source_name;
4507   glob_buffer.length(0);			// Empty command buffer
4508   error= read_and_execute(false);
4509   status=old_status;				// Continue as before
4510   my_fclose(sql_file,MYF(0));
4511   batch_readline_end(line_buff);
4512   return error;
4513 }
4514 
4515 
4516 	/* ARGSUSED */
4517 static int
com_delimiter(String * buffer MY_ATTRIBUTE ((unused)),char * line)4518 com_delimiter(String *buffer MY_ATTRIBUTE((unused)), char *line)
4519 {
4520   char buff[256], *tmp;
4521 
4522   strmake(buff, line, sizeof(buff) - 1);
4523   tmp= get_arg(buff, 0);
4524 
4525   if (!tmp || !*tmp)
4526   {
4527     put_info("DELIMITER must be followed by a 'delimiter' character or string",
4528 	     INFO_ERROR);
4529     return 0;
4530   }
4531   else
4532   {
4533     if (strstr(tmp, "\\"))
4534     {
4535       put_info("DELIMITER cannot contain a backslash character", INFO_ERROR);
4536       return 0;
4537     }
4538   }
4539   strmake(delimiter, tmp, sizeof(delimiter) - 1);
4540   delimiter_length= (int)strlen(delimiter);
4541   delimiter_str= delimiter;
4542   return 0;
4543 }
4544 
4545 	/* ARGSUSED */
4546 static int
com_use(String * buffer MY_ATTRIBUTE ((unused)),char * line)4547 com_use(String *buffer MY_ATTRIBUTE((unused)), char *line)
4548 {
4549   char *tmp, buff[FN_REFLEN + 1];
4550   int select_db;
4551 
4552   memset(buff, 0, sizeof(buff));
4553 
4554   /*
4555     In case of quotes used, try to get the normalized db name.
4556   */
4557   if (get_quote_count(line) > 0)
4558   {
4559     if (normalize_dbname(line, buff, sizeof(buff)))
4560       return put_error(&mysql);
4561     tmp= buff;
4562   }
4563   else
4564   {
4565     strmake(buff, line, sizeof(buff) - 1);
4566     tmp= get_arg(buff, 0);
4567   }
4568 
4569   if (!tmp || !*tmp)
4570   {
4571     put_info("USE must be followed by a database name", INFO_ERROR);
4572     return 0;
4573   }
4574   /*
4575     We need to recheck the current database, because it may change
4576     under our feet, for example if DROP DATABASE or RENAME DATABASE
4577     (latter one not yet available by the time the comment was written)
4578   */
4579   get_current_db();
4580 
4581   if (!current_db || cmp_database(charset_info, current_db,tmp))
4582   {
4583     if (one_database)
4584     {
4585       skip_updates= 1;
4586       select_db= 0;    // don't do mysql_select_db()
4587     }
4588     else
4589       select_db= 2;    // do mysql_select_db() and build_completion_hash()
4590   }
4591   else
4592   {
4593     /*
4594       USE to the current db specified.
4595       We do need to send mysql_select_db() to make server
4596       update database level privileges, which might
4597       change since last USE (see bug#10979).
4598       For performance purposes, we'll skip rebuilding of completion hash.
4599     */
4600     skip_updates= 0;
4601     select_db= 1;      // do only mysql_select_db(), without completion
4602   }
4603 
4604   if (select_db)
4605   {
4606     /*
4607       reconnect once if connection is down or if connection was found to
4608       be down during query
4609     */
4610     if (!connected && reconnect())
4611       return opt_reconnect ? -1 : 1;                        // Fatal error
4612     if (mysql_select_db(&mysql,tmp))
4613     {
4614       if (mysql_errno(&mysql) != CR_SERVER_GONE_ERROR)
4615         return put_error(&mysql);
4616 
4617       if (reconnect())
4618         return opt_reconnect ? -1 : 1;                      // Fatal error
4619       if (mysql_select_db(&mysql,tmp))
4620         return put_error(&mysql);
4621     }
4622     my_free(current_db);
4623     current_db=my_strdup(tmp,MYF(MY_WME));
4624 #ifdef HAVE_READLINE
4625     if (select_db > 1)
4626       build_completion_hash(opt_rehash, 1);
4627 #endif
4628   }
4629 
4630   put_info("Database changed",INFO_INFO);
4631   return 0;
4632 }
4633 
4634 /**
4635   Normalize database name.
4636 
4637   @param line [IN]          The command.
4638   @param buff [OUT]         Normalized db name.
4639   @param buff_size [IN]     Buffer size.
4640 
4641   @return Operation status
4642       @retval 0    Success
4643       @retval 1    Failure
4644 
4645   @note Sometimes server normilizes the database names
4646         & APIs like mysql_select_db() expect normalized
4647         database names. Since it is difficult to perform
4648         the name conversion/normalization on the client
4649         side, this function tries to get the normalized
4650         dbname (indirectly) from the server.
4651 */
4652 
4653 static int
normalize_dbname(const char * line,char * buff,uint buff_size)4654 normalize_dbname(const char *line, char *buff, uint buff_size)
4655 {
4656   MYSQL_RES *res= NULL;
4657 
4658   /* Send the "USE db" commmand to the server. */
4659   if (mysql_query(&mysql, line))
4660     return 1;
4661 
4662   /*
4663     Now, get the normalized database name and store it
4664     into the buff.
4665   */
4666   if (!mysql_query(&mysql, "SELECT DATABASE()") &&
4667       (res= mysql_use_result(&mysql)))
4668   {
4669     MYSQL_ROW row= mysql_fetch_row(res);
4670     if (row && row[0])
4671     {
4672       size_t len= strlen(row[0]);
4673       /* Make sure there is enough room to store the dbname. */
4674       if ((len > buff_size) || ! memcpy(buff, row[0], len))
4675       {
4676         mysql_free_result(res);
4677         return 1;
4678       }
4679     }
4680     mysql_free_result(res);
4681   }
4682 
4683   /* Restore the original database. */
4684   if (current_db && mysql_select_db(&mysql, current_db))
4685     return 1;
4686 
4687   return 0;
4688 }
4689 
4690 static int
com_warnings(String * buffer MY_ATTRIBUTE ((unused)),char * line MY_ATTRIBUTE ((unused)))4691 com_warnings(String *buffer MY_ATTRIBUTE((unused)),
4692    char *line MY_ATTRIBUTE((unused)))
4693 {
4694   show_warnings = 1;
4695   put_info("Show warnings enabled.",INFO_INFO);
4696   return 0;
4697 }
4698 
4699 static int
com_nowarnings(String * buffer MY_ATTRIBUTE ((unused)),char * line MY_ATTRIBUTE ((unused)))4700 com_nowarnings(String *buffer MY_ATTRIBUTE((unused)),
4701    char *line MY_ATTRIBUTE((unused)))
4702 {
4703   show_warnings = 0;
4704   put_info("Show warnings disabled.",INFO_INFO);
4705   return 0;
4706 }
4707 
4708 /*
4709   Gets argument from a command on the command line. If get_next_arg is
4710   not defined, skips the command and returns the first argument. The
4711   line is modified by adding zero to the end of the argument. If
4712   get_next_arg is defined, then the function searches for end of string
4713   first, after found, returns the next argument and adds zero to the
4714   end. If you ever wish to use this feature, remember to initialize all
4715   items in the array to zero first.
4716 */
4717 
get_arg(char * line,my_bool get_next_arg)4718 char *get_arg(char *line, my_bool get_next_arg)
4719 {
4720   char *ptr, *start;
4721   my_bool quoted= 0, valid_arg= 0;
4722   char qtype= 0;
4723 
4724   ptr= line;
4725   if (get_next_arg)
4726   {
4727     for (; *ptr; ptr++) ;
4728     if (*(ptr + 1))
4729       ptr++;
4730   }
4731   else
4732   {
4733     /* skip leading white spaces */
4734     while (my_isspace(charset_info, *ptr))
4735       ptr++;
4736     if (*ptr == '\\') // short command was used
4737       ptr+= 2;
4738     else
4739       while (*ptr &&!my_isspace(charset_info, *ptr)) // skip command
4740         ptr++;
4741   }
4742   if (!*ptr)
4743     return NullS;
4744   while (my_isspace(charset_info, *ptr))
4745     ptr++;
4746   if (*ptr == '\'' || *ptr == '\"' || *ptr == '`')
4747   {
4748     qtype= *ptr;
4749     quoted= 1;
4750     ptr++;
4751   }
4752   for (start=ptr ; *ptr; ptr++)
4753   {
4754     if (*ptr == '\\' && ptr[1]) // escaped character
4755     {
4756       // Remove the backslash
4757       strmov_overlapp(ptr, ptr+1);
4758     }
4759     else if ((!quoted && *ptr == ' ') || (quoted && *ptr == qtype))
4760     {
4761       *ptr= 0;
4762       break;
4763     }
4764   }
4765   valid_arg= ptr != start;
4766   return valid_arg ? start : NullS;
4767 }
4768 
4769 /*
4770   Number of quotes present in the command's argument.
4771 */
4772 static int
get_quote_count(const char * line)4773 get_quote_count(const char *line)
4774 {
4775   int quote_count= 0;
4776   const char *quote= line;
4777 
4778   while ((quote= strpbrk(quote, "'`\"")) != NULL) {
4779     quote_count++;
4780     quote++;
4781   }
4782 
4783   return quote_count;
4784 }
4785 
4786 static int
sql_real_connect(char * host,char * database,char * user,char * password,uint silent)4787 sql_real_connect(char *host,char *database,char *user,char *password,
4788 		 uint silent)
4789 {
4790   my_bool handle_expired= (opt_connect_expired_password || !status.batch) ?
4791     TRUE : FALSE;
4792 
4793   if (connected)
4794   {
4795     connected= 0;
4796     mysql_close(&mysql);
4797   }
4798   mysql_init(&mysql);
4799   if (opt_init_command)
4800     mysql_options(&mysql, MYSQL_INIT_COMMAND, opt_init_command);
4801   if (opt_connect_timeout)
4802   {
4803     uint timeout=opt_connect_timeout;
4804     mysql_options(&mysql,MYSQL_OPT_CONNECT_TIMEOUT,
4805 		  (char*) &timeout);
4806   }
4807   if (opt_bind_addr)
4808     mysql_options(&mysql, MYSQL_OPT_BIND, opt_bind_addr);
4809   if (opt_compress)
4810     mysql_options(&mysql,MYSQL_OPT_COMPRESS,NullS);
4811   if (!opt_secure_auth)
4812     mysql_options(&mysql, MYSQL_SECURE_AUTH, (char *) &opt_secure_auth);
4813   if (using_opt_local_infile)
4814     mysql_options(&mysql,MYSQL_OPT_LOCAL_INFILE, (char*) &opt_local_infile);
4815 #if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
4816   if (opt_use_ssl)
4817   {
4818     mysql_ssl_set(&mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca,
4819 		  opt_ssl_capath, opt_ssl_cipher);
4820     mysql_options(&mysql, MYSQL_OPT_SSL_CRL, opt_ssl_crl);
4821     mysql_options(&mysql, MYSQL_OPT_SSL_CRLPATH, opt_ssl_crlpath);
4822   }
4823   mysql_options(&mysql,MYSQL_OPT_SSL_VERIFY_SERVER_CERT,
4824                 (char*)&opt_ssl_verify_server_cert);
4825 #endif
4826   if (opt_protocol)
4827     mysql_options(&mysql,MYSQL_OPT_PROTOCOL,(char*)&opt_protocol);
4828 #ifdef HAVE_SMEM
4829   if (shared_memory_base_name)
4830     mysql_options(&mysql,MYSQL_SHARED_MEMORY_BASE_NAME,shared_memory_base_name);
4831 #endif
4832   if (safe_updates)
4833   {
4834     char init_command[100];
4835     sprintf(init_command,
4836 	    "SET SQL_SAFE_UPDATES=1,SQL_SELECT_LIMIT=%lu,MAX_JOIN_SIZE=%lu",
4837 	    select_limit,max_join_size);
4838     mysql_options(&mysql, MYSQL_INIT_COMMAND, init_command);
4839   }
4840 
4841   mysql_set_character_set(&mysql, default_charset);
4842 #ifdef __WIN__
4843   uint cnv_errors;
4844   String converted_database, converted_user;
4845   if (!my_charset_same(&my_charset_utf8mb4_bin, mysql.charset))
4846   {
4847     /* Convert user and database from UTF8MB4 to connection character set */
4848     if (user)
4849     {
4850       converted_user.copy(user, strlen(user) + 1,
4851                           &my_charset_utf8mb4_bin, mysql.charset,
4852                           &cnv_errors);
4853       user= (char *) converted_user.ptr();
4854     }
4855     if (database)
4856     {
4857       converted_database.copy(database, strlen(database) + 1,
4858                               &my_charset_utf8mb4_bin, mysql.charset,
4859                               &cnv_errors);
4860       database= (char *) converted_database.ptr();
4861     }
4862   }
4863 #endif
4864 
4865   if (opt_plugin_dir && *opt_plugin_dir)
4866     mysql_options(&mysql, MYSQL_PLUGIN_DIR, opt_plugin_dir);
4867 
4868   if (opt_default_auth && *opt_default_auth)
4869     mysql_options(&mysql, MYSQL_DEFAULT_AUTH, opt_default_auth);
4870 
4871   if (opt_server_public_key && *opt_server_public_key)
4872     mysql_options(&mysql, MYSQL_SERVER_PUBLIC_KEY, opt_server_public_key);
4873 
4874   if (using_opt_enable_cleartext_plugin)
4875     mysql_options(&mysql, MYSQL_ENABLE_CLEARTEXT_PLUGIN,
4876                   (char*) &opt_enable_cleartext_plugin);
4877 
4878   mysql_options(&mysql, MYSQL_OPT_CONNECT_ATTR_RESET, 0);
4879   mysql_options4(&mysql, MYSQL_OPT_CONNECT_ATTR_ADD,
4880                  "program_name", "mysql");
4881   mysql_options(&mysql, MYSQL_OPT_CAN_HANDLE_EXPIRED_PASSWORDS, &handle_expired);
4882 
4883   if (!mysql_connect_ssl_check(&mysql, host, user, password,
4884                                database, opt_mysql_port, opt_mysql_unix_port,
4885                                connect_flag | CLIENT_MULTI_STATEMENTS,
4886                                opt_ssl_mode == SSL_MODE_REQUIRED))
4887   {
4888     if (!silent ||
4889 	(mysql_errno(&mysql) != CR_CONN_HOST_ERROR &&
4890 	 mysql_errno(&mysql) != CR_CONNECTION_ERROR))
4891     {
4892       (void) put_error(&mysql);
4893       (void) fflush(stdout);
4894       return ignore_errors ? -1 : 1;		// Abort
4895     }
4896     return -1;					// Retryable
4897   }
4898 
4899 #ifdef __WIN__
4900   /* Convert --execute buffer from UTF8MB4 to connection character set */
4901   if (!execute_buffer_conversion_done++ &&
4902       status.line_buff &&
4903       !status.line_buff->file && /* Convert only -e buffer, not real file */
4904       status.line_buff->buffer < status.line_buff->end && /* Non-empty */
4905       !my_charset_same(&my_charset_utf8mb4_bin, mysql.charset))
4906   {
4907     String tmp;
4908     size_t len= status.line_buff->end - status.line_buff->buffer;
4909     uint dummy_errors;
4910     /*
4911       Don't convert trailing '\n' character - it was appended during
4912       last batch_readline_command() call.
4913       Oherwise we'll get an extra line, which makes some tests fail.
4914     */
4915     if (status.line_buff->buffer[len - 1] == '\n')
4916       len--;
4917     if (tmp.copy(status.line_buff->buffer, len,
4918                  &my_charset_utf8mb4_bin, mysql.charset, &dummy_errors))
4919       return 1;
4920 
4921     /* Free the old line buffer */
4922     batch_readline_end(status.line_buff);
4923 
4924     /* Re-initialize line buffer from the converted string */
4925     if (!(status.line_buff= batch_readline_command(NULL, (char *) tmp.c_ptr_safe())))
4926       return 1;
4927   }
4928 #endif /* __WIN__ */
4929 
4930   charset_info= mysql.charset;
4931 
4932   connected=1;
4933 #ifndef EMBEDDED_LIBRARY
4934   mysql.reconnect= debug_info_flag; // We want to know if this happens
4935 #else
4936   mysql.reconnect= 1;
4937 #endif
4938 #ifdef HAVE_READLINE
4939   build_completion_hash(opt_rehash, 1);
4940 #endif
4941   return 0;
4942 }
4943 
4944 
4945 static int
sql_connect(char * host,char * database,char * user,char * password,uint silent)4946 sql_connect(char *host,char *database,char *user,char *password,uint silent)
4947 {
4948   bool message=0;
4949   uint count=0;
4950   int error;
4951   for (;;)
4952   {
4953     if ((error=sql_real_connect(host,database,user,password,wait_flag)) >= 0)
4954     {
4955       if (count)
4956       {
4957 	tee_fputs("\n", stderr);
4958 	(void) fflush(stderr);
4959       }
4960       return error;
4961     }
4962     if (!wait_flag)
4963       return ignore_errors ? -1 : 1;
4964     if (!message && !silent)
4965     {
4966       message=1;
4967       tee_fputs("Waiting",stderr); (void) fflush(stderr);
4968     }
4969     (void) sleep(wait_time);
4970     if (!silent)
4971     {
4972       putc('.',stderr); (void) fflush(stderr);
4973       count++;
4974     }
4975   }
4976 }
4977 
4978 
4979 
4980 static int
com_status(String * buffer MY_ATTRIBUTE ((unused)),char * line MY_ATTRIBUTE ((unused)))4981 com_status(String *buffer MY_ATTRIBUTE((unused)),
4982 	   char *line MY_ATTRIBUTE((unused)))
4983 {
4984   const char *status_str;
4985   char buff[40];
4986   ulonglong id;
4987   MYSQL_RES *result;
4988   LINT_INIT(result);
4989 
4990   if (mysql_real_query_for_lazy(
4991         C_STRING_WITH_LEN("select DATABASE(), USER() limit 1")))
4992     return 0;
4993 
4994   tee_puts("--------------", stdout);
4995   usage(1);					/* Print version */
4996   tee_fprintf(stdout, "\nConnection id:\t\t%lu\n",mysql_thread_id(&mysql));
4997   /*
4998     Don't remove "limit 1",
4999     it is protection againts SQL_SELECT_LIMIT=0
5000   */
5001   if (!mysql_store_result_for_lazy(&result))
5002   {
5003     MYSQL_ROW cur=mysql_fetch_row(result);
5004     if (cur)
5005     {
5006       tee_fprintf(stdout, "Current database:\t%s\n", cur[0] ? cur[0] : "");
5007       tee_fprintf(stdout, "Current user:\t\t%s\n", cur[1]);
5008     }
5009     mysql_free_result(result);
5010   }
5011 
5012 #if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
5013   if ((status_str= mysql_get_ssl_cipher(&mysql)))
5014     tee_fprintf(stdout, "SSL:\t\t\tCipher in use is %s\n",
5015                 status_str);
5016   else
5017 #endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */
5018     tee_puts("SSL:\t\t\tNot in use", stdout);
5019 
5020   if (skip_updates)
5021   {
5022     vidattr(A_BOLD);
5023     tee_fprintf(stdout, "\nAll updates ignored to this database\n");
5024     vidattr(A_NORMAL);
5025   }
5026 #ifdef USE_POPEN
5027   tee_fprintf(stdout, "Current pager:\t\t%s\n", pager);
5028   tee_fprintf(stdout, "Using outfile:\t\t'%s'\n", opt_outfile ? outfile : "");
5029 #endif
5030   tee_fprintf(stdout, "Using delimiter:\t%s\n", delimiter);
5031   tee_fprintf(stdout, "Server version:\t\t%s\n", server_version_string(&mysql));
5032   tee_fprintf(stdout, "Protocol version:\t%d\n", mysql_get_proto_info(&mysql));
5033   tee_fprintf(stdout, "Connection:\t\t%s\n", mysql_get_host_info(&mysql));
5034   if ((id= mysql_insert_id(&mysql)))
5035     tee_fprintf(stdout, "Insert id:\t\t%s\n", llstr(id, buff));
5036 
5037   /* "limit 1" is protection against SQL_SELECT_LIMIT=0 */
5038   if (mysql_real_query_for_lazy(C_STRING_WITH_LEN(
5039         "select @@character_set_client, @@character_set_connection, "
5040         "@@character_set_server, @@character_set_database limit 1")))
5041   {
5042     if (mysql_errno(&mysql) == CR_SERVER_GONE_ERROR)
5043       return 0;
5044   }
5045   if (!mysql_store_result_for_lazy(&result))
5046   {
5047     MYSQL_ROW cur=mysql_fetch_row(result);
5048     if (cur)
5049     {
5050       tee_fprintf(stdout, "Server characterset:\t%s\n", cur[2] ? cur[2] : "");
5051       tee_fprintf(stdout, "Db     characterset:\t%s\n", cur[3] ? cur[3] : "");
5052       tee_fprintf(stdout, "Client characterset:\t%s\n", cur[0] ? cur[0] : "");
5053       tee_fprintf(stdout, "Conn.  characterset:\t%s\n", cur[1] ? cur[1] : "");
5054     }
5055     mysql_free_result(result);
5056   }
5057   else
5058   {
5059     /* Probably pre-4.1 server */
5060     tee_fprintf(stdout, "Client characterset:\t%s\n", charset_info->csname);
5061     tee_fprintf(stdout, "Server characterset:\t%s\n", mysql.charset->csname);
5062   }
5063 
5064 #ifndef EMBEDDED_LIBRARY
5065   if (strstr(mysql_get_host_info(&mysql),"TCP/IP") || ! mysql.unix_socket)
5066     tee_fprintf(stdout, "TCP port:\t\t%d\n", mysql.port);
5067   else
5068     tee_fprintf(stdout, "UNIX socket:\t\t%s\n", mysql.unix_socket);
5069   if (mysql.net.compress)
5070     tee_fprintf(stdout, "Protocol:\t\tCompressed\n");
5071 #endif
5072 
5073   if ((status_str= mysql_stat(&mysql)) && !mysql_error(&mysql)[0])
5074   {
5075     ulong sec;
5076     const char *pos= strchr(status_str,' ');
5077     /* print label */
5078     tee_fprintf(stdout, "%.*s\t\t\t", (int) (pos-status_str), status_str);
5079     if ((status_str= str2int(pos,10,0,LONG_MAX,(long*) &sec)))
5080     {
5081       nice_time((double) sec,buff,0);
5082       tee_puts(buff, stdout);			/* print nice time */
5083       while (*status_str == ' ')
5084         status_str++;  /* to next info */
5085       tee_putc('\n', stdout);
5086       tee_puts(status_str, stdout);
5087     }
5088   }
5089   if (safe_updates)
5090   {
5091     vidattr(A_BOLD);
5092     tee_fprintf(stdout, "\nNote that you are running in safe_update_mode:\n");
5093     vidattr(A_NORMAL);
5094     tee_fprintf(stdout, "\
5095 UPDATEs and DELETEs that don't use a key in the WHERE clause are not allowed.\n\
5096 (One can force an UPDATE/DELETE by adding LIMIT # at the end of the command.)\n\
5097 SELECT has an automatic 'LIMIT %lu' if LIMIT is not used.\n\
5098 Max number of examined row combination in a join is set to: %lu\n\n",
5099 select_limit, max_join_size);
5100   }
5101   tee_puts("--------------\n", stdout);
5102   return 0;
5103 }
5104 
5105 static const char *
server_version_string(MYSQL * con)5106 server_version_string(MYSQL *con)
5107 {
5108   /* Only one thread calls this, so no synchronization is needed */
5109   if (server_version == NULL)
5110   {
5111     MYSQL_RES *result;
5112 
5113     /* "limit 1" is protection against SQL_SELECT_LIMIT=0 */
5114     if (!mysql_query(con, "select @@version_comment limit 1") &&
5115         (result = mysql_use_result(con)))
5116     {
5117       MYSQL_ROW cur = mysql_fetch_row(result);
5118       if (cur && cur[0])
5119       {
5120         /* version, space, comment, \0 */
5121         size_t len= strlen(mysql_get_server_info(con)) + strlen(cur[0]) + 2;
5122 
5123         if ((server_version= (char *) my_malloc(len, MYF(MY_WME))))
5124         {
5125           char *bufp;
5126           bufp = strmov(server_version, mysql_get_server_info(con));
5127           bufp = strmov(bufp, " ");
5128           (void) strmov(bufp, cur[0]);
5129         }
5130       }
5131       mysql_free_result(result);
5132     }
5133 
5134     /*
5135       If for some reason we didn't get a version_comment, we'll
5136       keep things simple.
5137     */
5138 
5139     if (server_version == NULL)
5140       server_version= my_strdup(mysql_get_server_info(con), MYF(MY_WME));
5141   }
5142 
5143   return server_version ? server_version : "";
5144 }
5145 
5146 static int
put_info(const char * str,INFO_TYPE info_type,uint error,const char * sqlstate)5147 put_info(const char *str,INFO_TYPE info_type, uint error, const char *sqlstate)
5148 {
5149   FILE *file= (info_type == INFO_ERROR ? stderr : stdout);
5150   static int inited=0;
5151 
5152   if (status.batch)
5153   {
5154     if (info_type == INFO_ERROR)
5155     {
5156       (void) fflush(file);
5157       fprintf(file,"ERROR");
5158       if (error)
5159       {
5160 	if (sqlstate)
5161 	  (void) fprintf(file," %d (%s)",error, sqlstate);
5162         else
5163 	  (void) fprintf(file," %d",error);
5164       }
5165       if (status.query_start_line && line_numbers)
5166       {
5167 	(void) fprintf(file," at line %lu",status.query_start_line);
5168 	if (status.file_name)
5169 	  (void) fprintf(file," in file: '%s'", status.file_name);
5170       }
5171       (void) fprintf(file,": %s\n",str);
5172       (void) fflush(file);
5173       if (!ignore_errors)
5174 	return 1;
5175     }
5176     else if (info_type == INFO_RESULT && verbose > 1)
5177       tee_puts(str, file);
5178     if (unbuffered)
5179       fflush(file);
5180     return info_type == INFO_ERROR ? -1 : 0;
5181   }
5182   if (!opt_silent || info_type == INFO_ERROR)
5183   {
5184     if (!inited)
5185     {
5186       inited=1;
5187 #ifdef HAVE_SETUPTERM
5188       (void) setupterm((char *)0, 1, (int *) 0);
5189 #endif
5190     }
5191     if (info_type == INFO_ERROR)
5192     {
5193       if (!opt_nobeep)
5194         putchar('\a');		      	/* This should make a bell */
5195       vidattr(A_STANDOUT);
5196       if (error)
5197       {
5198 	if (sqlstate)
5199           (void) tee_fprintf(file, "ERROR %d (%s): ", error, sqlstate);
5200         else
5201           (void) tee_fprintf(file, "ERROR %d: ", error);
5202       }
5203       else
5204         tee_puts("ERROR: ", file);
5205     }
5206     else
5207       vidattr(A_BOLD);
5208     (void) tee_puts(str, file);
5209     vidattr(A_NORMAL);
5210   }
5211   if (unbuffered)
5212     fflush(file);
5213   return info_type == INFO_ERROR ? -1 : 0;
5214 }
5215 
5216 
5217 static int
put_error(MYSQL * con)5218 put_error(MYSQL *con)
5219 {
5220   return put_info(mysql_error(con), INFO_ERROR, mysql_errno(con),
5221 		  mysql_sqlstate(con));
5222 }
5223 
5224 
remove_cntrl(String & buffer)5225 static void remove_cntrl(String &buffer)
5226 {
5227   char *start,*end;
5228   end=(start=(char*) buffer.ptr())+buffer.length();
5229   while (start < end && !my_isgraph(charset_info,end[-1]))
5230     end--;
5231   buffer.length((uint) (end-start));
5232 }
5233 
5234 
5235 /**
5236   Write data to a stream.
5237   Various modes, corresponding to --tab, --xml, --raw parameters,
5238   are supported.
5239 
5240   @param file   Stream to write to
5241   @param s      String to write
5242   @param slen   String length
5243   @flags        Flags for --tab, --xml, --raw.
5244 */
tee_write(FILE * file,const char * s,size_t slen,int flags)5245 void tee_write(FILE *file, const char *s, size_t slen, int flags)
5246 {
5247 #ifdef __WIN__
5248   my_bool is_console= my_win_is_console_cached(file);
5249 #endif
5250   const char *se;
5251   for (se= s + slen; s < se; s++)
5252   {
5253     const char *t;
5254 
5255     if (flags & MY_PRINT_MB)
5256     {
5257       int mblen;
5258       if (use_mb(charset_info) &&
5259           (mblen= my_ismbchar(charset_info, s, se)))
5260       {
5261 #ifdef __WIN__
5262         if (is_console)
5263           my_win_console_write(charset_info, s, mblen);
5264         else
5265 #endif
5266         fwrite(s, 1, mblen, file);
5267         if (opt_outfile)
5268           fwrite(s, 1, mblen, OUTFILE);
5269         s+= mblen - 1;
5270         continue;
5271       }
5272     }
5273 
5274     if ((flags & MY_PRINT_XML) && (t= array_value(xmlmeta, *s)))
5275       tee_fputs(t, file);
5276     else if ((flags & MY_PRINT_SPS_0) && *s == '\0')
5277       tee_putc((int) ' ', file);   // This makes everything hard
5278     else if ((flags & MY_PRINT_ESC_0) && *s == '\0')
5279       tee_fputs("\\0", file);      // This makes everything hard
5280     else if ((flags & MY_PRINT_CTRL) && *s == '\t')
5281       tee_fputs("\\t", file);      // This would destroy tab format
5282     else if ((flags & MY_PRINT_CTRL) && *s == '\n')
5283       tee_fputs("\\n", file);      // This too
5284     else if ((flags & MY_PRINT_CTRL) && *s == '\\')
5285       tee_fputs("\\\\", file);
5286     else
5287     {
5288 #ifdef __WIN__
5289       if (is_console)
5290         my_win_console_putc(charset_info, (int) *s);
5291       else
5292 #endif
5293       putc((int) *s, file);
5294       if (opt_outfile)
5295         putc((int) *s, OUTFILE);
5296     }
5297   }
5298 }
5299 
5300 
tee_fprintf(FILE * file,const char * fmt,...)5301 void tee_fprintf(FILE *file, const char *fmt, ...)
5302 {
5303   va_list args;
5304 
5305   va_start(args, fmt);
5306 #ifdef __WIN__
5307   if (my_win_is_console_cached(file))
5308     my_win_console_vfprintf(charset_info, fmt, args);
5309   else
5310 #endif
5311   (void) vfprintf(file, fmt, args);
5312   va_end(args);
5313 
5314   if (opt_outfile)
5315   {
5316     va_start(args, fmt);
5317     (void) vfprintf(OUTFILE, fmt, args);
5318     va_end(args);
5319   }
5320 }
5321 
5322 
5323 /*
5324   Write a 0-terminated string to file and OUTFILE.
5325   TODO: possibly it's nice to have a version with length some day,
5326   e.g. tee_fnputs(s, slen, file),
5327   to print numerous ASCII constant strings among mysql.cc
5328   code, to avoid strlen(s) in my_win_console_fputs().
5329 */
tee_fputs(const char * s,FILE * file)5330 void tee_fputs(const char *s, FILE *file)
5331 {
5332 #ifdef __WIN__
5333   if (my_win_is_console_cached(file))
5334     my_win_console_fputs(charset_info, s);
5335   else
5336 #endif
5337   fputs(s, file);
5338   if (opt_outfile)
5339     fputs(s, OUTFILE);
5340 }
5341 
5342 
tee_puts(const char * s,FILE * file)5343 void tee_puts(const char *s, FILE *file)
5344 {
5345   tee_fputs(s, file);
5346   tee_putc('\n', file);
5347 }
5348 
tee_putc(int c,FILE * file)5349 void tee_putc(int c, FILE *file)
5350 {
5351 #ifdef __WIN__
5352   if (my_win_is_console_cached(file))
5353     my_win_console_putc(charset_info, c);
5354   else
5355 #endif
5356   putc(c, file);
5357   if (opt_outfile)
5358     putc(c, OUTFILE);
5359 }
5360 
5361 #if defined(__WIN__)
5362 #include <time.h>
5363 #else
5364 #include <sys/times.h>
5365 #ifdef _SC_CLK_TCK				// For mit-pthreads
5366 #undef CLOCKS_PER_SEC
5367 #define CLOCKS_PER_SEC (sysconf(_SC_CLK_TCK))
5368 #endif
5369 #endif
5370 
start_timer(void)5371 static ulong start_timer(void)
5372 {
5373 #if defined(__WIN__)
5374   return clock();
5375 #else
5376   struct tms tms_tmp;
5377   return times(&tms_tmp);
5378 #endif
5379 }
5380 
5381 
5382 /**
5383   Write as many as 52+1 bytes to buff, in the form of a legible duration of time.
5384 
5385   len("4294967296 days, 23 hours, 59 minutes, 60.00 seconds")  ->  52
5386 */
nice_time(double sec,char * buff,bool part_second)5387 static void nice_time(double sec,char *buff,bool part_second)
5388 {
5389   ulong tmp;
5390   if (sec >= 3600.0*24)
5391   {
5392     tmp=(ulong) floor(sec/(3600.0*24));
5393     sec-=3600.0*24*tmp;
5394     buff=int10_to_str((long) tmp, buff, 10);
5395     buff=strmov(buff,tmp > 1 ? " days " : " day ");
5396   }
5397   if (sec >= 3600.0)
5398   {
5399     tmp=(ulong) floor(sec/3600.0);
5400     sec-=3600.0*tmp;
5401     buff=int10_to_str((long) tmp, buff, 10);
5402     buff=strmov(buff,tmp > 1 ? " hours " : " hour ");
5403   }
5404   if (sec >= 60.0)
5405   {
5406     tmp=(ulong) floor(sec/60.0);
5407     sec-=60.0*tmp;
5408     buff=int10_to_str((long) tmp, buff, 10);
5409     buff=strmov(buff," min ");
5410   }
5411   if (part_second)
5412     sprintf(buff,"%.2f sec",sec);
5413   else
5414     sprintf(buff,"%d sec",(int) sec);
5415 }
5416 
5417 
end_timer(ulong start_time,char * buff)5418 static void end_timer(ulong start_time,char *buff)
5419 {
5420   nice_time((double) (start_timer() - start_time) /
5421 	    CLOCKS_PER_SEC,buff,1);
5422 }
5423 
5424 
mysql_end_timer(ulong start_time,char * buff)5425 static void mysql_end_timer(ulong start_time,char *buff)
5426 {
5427   buff[0]=' ';
5428   buff[1]='(';
5429   end_timer(start_time,buff+2);
5430   strmov(strend(buff),")");
5431 }
5432 
construct_prompt()5433 static const char* construct_prompt()
5434 {
5435   processed_prompt.free();			// Erase the old prompt
5436   time_t  lclock = time(NULL);			// Get the date struct
5437   struct tm *t = localtime(&lclock);
5438 
5439   /* parse thru the settings for the prompt */
5440   for (char *c = current_prompt; *c ; c++)
5441   {
5442     if (*c != PROMPT_CHAR)
5443 	processed_prompt.append(*c);
5444     else
5445     {
5446       switch (*++c) {
5447       case '\0':
5448 	c--;			// stop it from going beyond if ends with %
5449 	break;
5450       case 'c':
5451 	add_int_to_prompt(++prompt_counter);
5452 	break;
5453       case 'v':
5454 	if (connected)
5455 	  processed_prompt.append(mysql_get_server_info(&mysql));
5456 	else
5457 	  processed_prompt.append("not_connected");
5458 	break;
5459       case 'd':
5460 	processed_prompt.append(current_db ? current_db : "(none)");
5461 	break;
5462       case 'h':
5463       {
5464 	const char *prompt;
5465 	prompt= connected ? mysql_get_host_info(&mysql) : "not_connected";
5466 	if (strstr(prompt, "Localhost"))
5467 	  processed_prompt.append("localhost");
5468 	else
5469 	{
5470 	  const char *end=strcend(prompt,' ');
5471 	  processed_prompt.append(prompt, (uint) (end-prompt));
5472 	}
5473 	break;
5474       }
5475       case 'p':
5476       {
5477 #ifndef EMBEDDED_LIBRARY
5478 	if (!connected)
5479 	{
5480 	  processed_prompt.append("not_connected");
5481 	  break;
5482 	}
5483 
5484 	const char *host_info = mysql_get_host_info(&mysql);
5485 	if (strstr(host_info, "memory"))
5486 	{
5487 		processed_prompt.append( mysql.host );
5488 	}
5489 	else if (strstr(host_info,"TCP/IP") ||
5490 	    !mysql.unix_socket)
5491 	  add_int_to_prompt(mysql.port);
5492 	else
5493 	{
5494 	  char *pos=strrchr(mysql.unix_socket,'/');
5495  	  processed_prompt.append(pos ? pos+1 : mysql.unix_socket);
5496 	}
5497 #endif
5498       }
5499 	break;
5500       case 'U':
5501 	if (!full_username)
5502 	  init_username();
5503         processed_prompt.append(full_username ? full_username :
5504                                 (current_user ?  current_user : "(unknown)"));
5505 	break;
5506       case 'u':
5507 	if (!full_username)
5508 	  init_username();
5509         processed_prompt.append(part_username ? part_username :
5510                                 (current_user ?  current_user : "(unknown)"));
5511 	break;
5512       case PROMPT_CHAR:
5513 	processed_prompt.append(PROMPT_CHAR);
5514 	break;
5515       case 'n':
5516 	processed_prompt.append('\n');
5517 	break;
5518       case ' ':
5519       case '_':
5520 	processed_prompt.append(' ');
5521 	break;
5522       case 'R':
5523 	if (t->tm_hour < 10)
5524 	  processed_prompt.append('0');
5525 	add_int_to_prompt(t->tm_hour);
5526 	break;
5527       case 'r':
5528 	int getHour;
5529 	getHour = t->tm_hour % 12;
5530 	if (getHour == 0)
5531 	  getHour=12;
5532 	if (getHour < 10)
5533 	  processed_prompt.append('0');
5534 	add_int_to_prompt(getHour);
5535 	break;
5536       case 'm':
5537 	if (t->tm_min < 10)
5538 	  processed_prompt.append('0');
5539 	add_int_to_prompt(t->tm_min);
5540 	break;
5541       case 'y':
5542 	int getYear;
5543 	getYear = t->tm_year % 100;
5544 	if (getYear < 10)
5545 	  processed_prompt.append('0');
5546 	add_int_to_prompt(getYear);
5547 	break;
5548       case 'Y':
5549 	add_int_to_prompt(t->tm_year+1900);
5550 	break;
5551       case 'D':
5552 	char* dateTime;
5553 	dateTime = ctime(&lclock);
5554 	processed_prompt.append(strtok(dateTime,"\n"));
5555 	break;
5556       case 's':
5557 	if (t->tm_sec < 10)
5558 	  processed_prompt.append('0');
5559 	add_int_to_prompt(t->tm_sec);
5560 	break;
5561       case 'w':
5562 	processed_prompt.append(day_names[t->tm_wday]);
5563 	break;
5564       case 'P':
5565 	processed_prompt.append(t->tm_hour < 12 ? "am" : "pm");
5566 	break;
5567       case 'o':
5568 	add_int_to_prompt(t->tm_mon+1);
5569 	break;
5570       case 'O':
5571 	processed_prompt.append(month_names[t->tm_mon]);
5572 	break;
5573       case '\'':
5574 	processed_prompt.append("'");
5575 	break;
5576       case '"':
5577 	processed_prompt.append('"');
5578 	break;
5579       case 'S':
5580 	processed_prompt.append(';');
5581 	break;
5582       case 't':
5583 	processed_prompt.append('\t');
5584 	break;
5585       case 'l':
5586 	processed_prompt.append(delimiter_str);
5587 	break;
5588       default:
5589 	processed_prompt.append(c);
5590       }
5591     }
5592   }
5593   processed_prompt.append('\0');
5594   return processed_prompt.ptr();
5595 }
5596 
5597 
add_int_to_prompt(int toadd)5598 static void add_int_to_prompt(int toadd)
5599 {
5600   char buffer[16];
5601   int10_to_str(toadd,buffer,10);
5602   processed_prompt.append(buffer);
5603 }
5604 
init_username()5605 static void init_username()
5606 {
5607   my_free(full_username);
5608   my_free(part_username);
5609 
5610   MYSQL_RES *result;
5611   LINT_INIT(result);
5612   if (!mysql_query(&mysql,"select USER()") &&
5613       (result=mysql_use_result(&mysql)))
5614   {
5615     MYSQL_ROW cur=mysql_fetch_row(result);
5616     full_username=my_strdup(cur[0],MYF(MY_WME));
5617     part_username=my_strdup(strtok(cur[0],"@"),MYF(MY_WME));
5618     (void) mysql_fetch_row(result);		// Read eof
5619   }
5620 }
5621 
com_prompt(String * buffer MY_ATTRIBUTE ((unused)),char * line)5622 static int com_prompt(String *buffer MY_ATTRIBUTE((unused)),
5623                       char *line)
5624 {
5625   char *ptr=strchr(line, ' ');
5626   prompt_counter = 0;
5627   my_free(current_prompt);
5628   current_prompt=my_strdup(ptr ? ptr+1 : default_prompt,MYF(MY_WME));
5629   if (!ptr)
5630     tee_fprintf(stdout, "Returning to default PROMPT of %s\n", default_prompt);
5631   else
5632     tee_fprintf(stdout, "PROMPT set to '%s'\n", current_prompt);
5633   return 0;
5634 }
5635