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