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