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 "&", "&",
190 "<", "<",
191 ">", ">",
192 "\"", """,
193 /* Turn \0 into a space. Why not �? 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.", ¤t_db,
1509 ¤t_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.", ¤t_host,
1544 ¤t_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 ¤t_prompt, ¤t_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.", ¤t_user,
1628 ¤t_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 ? " " : "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