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