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