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