summaryrefslogtreecommitdiffstats
path: root/client/mysql.cc
diff options
context:
space:
mode:
Diffstat (limited to 'client/mysql.cc')
-rw-r--r--client/mysql.cc5610
1 files changed, 5610 insertions, 0 deletions
diff --git a/client/mysql.cc b/client/mysql.cc
new file mode 100644
index 00000000..1c842dae
--- /dev/null
+++ b/client/mysql.cc
@@ -0,0 +1,5610 @@
+/*
+ Copyright (c) 2000, 2018, Oracle and/or its affiliates.
+ Copyright (c) 2009, 2022, MariaDB Corporation.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
+
+/* mysql command tool
+ * Commands compatible with mSQL by David J. Hughes
+ *
+ * Written by:
+ * Michael 'Monty' Widenius
+ * Andi Gutmans <andi@zend.com>
+ * Zeev Suraski <zeev@zend.com>
+ * Jani Tolonen <jani@mysql.com>
+ * Matt Wagner <matt@mysql.com>
+ * Jeremy Cole <jcole@mysql.com>
+ * Tonu Samuel <tonu@mysql.com>
+ * Harrison Fisk <harrison@mysql.com>
+ *
+ **/
+
+#include "client_priv.h"
+#include <m_ctype.h>
+#include <stdarg.h>
+#include <my_dir.h>
+#ifndef __GNU_LIBRARY__
+#define __GNU_LIBRARY__ // Skip warnings in getopt.h
+#endif
+#include "my_readline.h"
+#include <signal.h>
+#include <violite.h>
+#include <my_sys.h>
+#include <source_revision.h>
+#if defined(HAVE_LOCALE_H)
+#include <locale.h>
+#endif
+
+const char *VER= "15.1";
+
+/* Don't try to make a nice table if the data is too big */
+#define MAX_COLUMN_LENGTH 1024
+
+/* Buffer to hold 'version' and 'version_comment' */
+static char *server_version= NULL;
+
+/* Array of options to pass to libemysqld */
+#define MAX_SERVER_ARGS 64
+
+#include "sql_string.h"
+#include "client_metadata.h"
+
+extern "C" {
+#if defined(HAVE_CURSES_H) && defined(HAVE_TERM_H)
+#include <curses.h>
+#include <term.h>
+#else
+#if defined(HAVE_TERMIOS_H)
+#include <termios.h>
+#include <unistd.h>
+#elif defined(HAVE_TERMBITS_H)
+#include <termbits.h>
+#elif defined(HAVE_ASM_TERMBITS_H) && (!defined __GLIBC__ || !(__GLIBC__ > 2 || __GLIBC__ == 2 && __GLIBC_MINOR__ > 0))
+#include <asm/termbits.h> // Standard linux
+#endif
+#undef VOID
+#if defined(HAVE_TERMCAP_H)
+#include <termcap.h>
+#else
+#ifdef HAVE_CURSES_H
+#include <curses.h>
+#endif
+#undef SYSV // hack to avoid syntax error
+#ifdef HAVE_TERM_H
+#include <term.h>
+#endif
+#endif
+#endif /* defined(HAVE_CURSES_H) && defined(HAVE_TERM_H) */
+
+#undef bcmp // Fix problem with new readline
+#if !defined(_WIN32)
+# ifdef __APPLE__
+# include <editline/readline.h>
+# else
+# include <readline.h>
+# if !defined(USE_LIBEDIT_INTERFACE)
+# include <history.h>
+# endif
+# endif
+#define HAVE_READLINE
+#endif
+#define USE_POPEN
+}
+
+static CHARSET_INFO *charset_info= &my_charset_latin1;
+
+#if defined(_WIN32)
+/*
+ Set console mode for the whole duration of the client session.
+
+ We need for input
+ - line input (i.e read lines from console)
+ - echo typed characters
+ - "cooked" mode, i.e we do not want to handle all keystrokes,
+ like DEL etc ourselves, yet. We might want handle keystrokes
+ in the future, to implement tab completion, and better
+ (multiline) history.
+
+ Disable VT escapes for the output.We do not know what kind of escapes SELECT would return.
+*/
+struct Console_mode
+{
+ HANDLE in= GetStdHandle(STD_INPUT_HANDLE);
+ HANDLE out= GetStdHandle(STD_OUTPUT_HANDLE);
+ DWORD mode_in=0;
+ DWORD mode_out=0;
+
+ enum {STDIN_CHANGED = 1, STDOUT_CHANGED = 2};
+ int changes=0;
+
+ Console_mode()
+ {
+ if (in && in != INVALID_HANDLE_VALUE && GetConsoleMode(in, &mode_in))
+ {
+ SetConsoleMode(in, ENABLE_ECHO_INPUT|ENABLE_LINE_INPUT|ENABLE_PROCESSED_INPUT);
+ changes |= STDIN_CHANGED;
+ }
+
+ if (out && out != INVALID_HANDLE_VALUE && GetConsoleMode(out, &mode_out))
+ {
+#ifdef ENABLE_VIRTUAL_TERMINAL_INPUT
+ SetConsoleMode(out, mode_out & ~ENABLE_VIRTUAL_TERMINAL_INPUT);
+ changes |= STDOUT_CHANGED;
+#endif
+ }
+ }
+
+ ~Console_mode()
+ {
+ if (changes & STDIN_CHANGED)
+ SetConsoleMode(in, mode_in);
+
+ if(changes & STDOUT_CHANGED)
+ SetConsoleMode(out, mode_out);
+ }
+};
+
+static Console_mode my_conmode;
+
+#define MAX_CGETS_LINE_LEN 65535
+/** Read line from console, chomp EOL*/
+static char *win_readline()
+{
+ static wchar_t wstrbuf[MAX_CGETS_LINE_LEN];
+ static char strbuf[MAX_CGETS_LINE_LEN * 4];
+
+ DWORD nchars= 0;
+ uint len= 0;
+ SetLastError(0);
+ if (!ReadConsoleW(GetStdHandle(STD_INPUT_HANDLE), wstrbuf, MAX_CGETS_LINE_LEN-1,
+ &nchars, NULL))
+ goto err;
+ if (nchars == 0 && GetLastError() == ERROR_OPERATION_ABORTED)
+ goto err;
+
+ for (;nchars > 0; nchars--)
+ {
+ if (wstrbuf[nchars - 1] != '\n' && wstrbuf[nchars - 1] != '\r')
+ break;
+ }
+
+ if (nchars > 0)
+ {
+ uint errors;
+ len= my_convert(strbuf, sizeof(strbuf), charset_info,
+ (const char *) wstrbuf, nchars * sizeof(wchar_t),
+ &my_charset_utf16le_bin, &errors);
+ }
+ strbuf[len]= 0;
+ return strbuf;
+err:
+ return NULL;
+}
+#endif
+
+
+#ifdef HAVE_VIDATTR
+static int have_curses= 0;
+static void my_vidattr(chtype attrs)
+{
+ if (have_curses)
+ vidattr(attrs);
+}
+#else
+#undef HAVE_SETUPTERM
+#define my_vidattr(A) {} // Can't get this to work
+#endif
+
+#ifdef FN_NO_CASE_SENSE
+#define cmp_database(cs,A,B) my_strcasecmp((cs), (A), (B))
+#else
+#define cmp_database(cs,A,B) strcmp((A),(B))
+#endif
+
+#include "completion_hash.h"
+#include <welcome_copyright_notice.h> // ORACLE_WELCOME_COPYRIGHT_NOTICE
+
+#define PROMPT_CHAR '\\'
+#define DEFAULT_DELIMITER ";"
+
+#define MAX_BATCH_BUFFER_SIZE (1024L * 1024L * 1024L)
+
+typedef struct st_status
+{
+ int exit_status;
+ ulong query_start_line;
+ char *file_name;
+ LINE_BUFFER *line_buff;
+ bool batch,add_to_history;
+} STATUS;
+
+
+static HashTable ht;
+static char **defaults_argv;
+
+enum enum_info_type { INFO_INFO,INFO_ERROR,INFO_RESULT};
+typedef enum enum_info_type INFO_TYPE;
+
+static MYSQL mysql; /* The connection */
+static my_bool ignore_errors=0,wait_flag=0,quick=0,
+ connected=0,opt_raw_data=0,unbuffered=0,output_tables=0,
+ opt_rehash=1,skip_updates=0,safe_updates=0,one_database=0,
+ opt_compress=0, using_opt_local_infile=0,
+ vertical=0, line_numbers=1, column_names=1,opt_html=0,
+ opt_xml=0,opt_nopager=1, opt_outfile=0, named_cmds= 0,
+ tty_password= 0, opt_nobeep=0, opt_reconnect=1,
+ opt_secure_auth= 0,
+ default_pager_set= 0, opt_sigint_ignore= 0,
+ auto_vertical_output= 0,
+ show_warnings= 0, executing_query= 0,
+ ignore_spaces= 0, opt_binhex= 0, opt_progress_reports;
+static my_bool debug_info_flag, debug_check_flag, batch_abort_on_error;
+static my_bool column_types_flag;
+static my_bool preserve_comments= 0;
+static my_bool in_com_source, aborted= 0;
+static ulong opt_max_allowed_packet, opt_net_buffer_length;
+static uint verbose=0,opt_silent=0,opt_mysql_port=0, opt_local_infile=0;
+static uint my_end_arg;
+static char * opt_mysql_unix_port=0;
+static int connect_flag=CLIENT_INTERACTIVE;
+static my_bool opt_binary_mode= FALSE;
+static my_bool opt_connect_expired_password= FALSE;
+static int interrupted_query= 0;
+static char *current_host,*current_db,*current_user=0,*opt_password=0,
+ *current_prompt=0, *delimiter_str= 0,
+ *default_charset= (char*) MYSQL_AUTODETECT_CHARSET_NAME,
+ *opt_init_command= 0;
+static char *histfile;
+static char *histfile_tmp;
+static String glob_buffer,old_buffer;
+static String processed_prompt;
+static char *full_username=0,*part_username=0,*default_prompt=0;
+static int wait_time = 5;
+static STATUS status;
+static ulong select_limit,max_join_size,opt_connect_timeout=0;
+static char mysql_charsets_dir[FN_REFLEN+1];
+static char *opt_plugin_dir= 0, *opt_default_auth= 0;
+static const char *xmlmeta[] = {
+ "&", "&amp;",
+ "<", "&lt;",
+ ">", "&gt;",
+ "\"", "&quot;",
+ /* Turn \0 into a space. Why not &#0;? That's not valid XML or HTML. */
+ "\0", " ",
+ 0, 0
+};
+static const char *day_names[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
+static const char *month_names[]={"Jan","Feb","Mar","Apr","May","Jun","Jul",
+ "Aug","Sep","Oct","Nov","Dec"};
+static char default_pager[FN_REFLEN];
+static char pager[FN_REFLEN], outfile[FN_REFLEN];
+static FILE *PAGER, *OUTFILE;
+static MEM_ROOT hash_mem_root;
+static uint prompt_counter;
+static char delimiter[16]= DEFAULT_DELIMITER;
+static uint delimiter_length= 1;
+unsigned short terminal_width= 80;
+
+static uint opt_protocol=0;
+static const char *opt_protocol_type= "";
+
+#include "sslopt-vars.h"
+
+const char *default_dbug_option="d:t:o,/tmp/mariadb.trace";
+
+void tee_fprintf(FILE *file, const char *fmt, ...);
+void tee_fputs(const char *s, FILE *file);
+void tee_puts(const char *s, FILE *file);
+void tee_putc(int c, FILE *file);
+static void tee_print_sized_data(const char *, unsigned int, unsigned int, bool);
+/* The names of functions that actually do the manipulation. */
+static int get_options(int argc,char **argv);
+extern "C" my_bool get_one_option(int optid, const struct my_option *opt,
+ const char *argument);
+static int com_quit(String *str,char*),
+ com_go(String *str,char*), com_ego(String *str,char*),
+ com_print(String *str,char*),
+ com_help(String *str,char*), com_clear(String *str,char*),
+ com_connect(String *str,char*), com_status(String *str,char*),
+ com_use(String *str,char*), com_source(String *str, char*),
+ com_rehash(String *str, char*), com_tee(String *str, char*),
+ com_notee(String *str, char*), com_charset(String *str,char*),
+ com_prompt(String *str, char*), com_delimiter(String *str, char*),
+ com_warnings(String *str, char*), com_nowarnings(String *str, char*);
+
+#ifdef USE_POPEN
+static int com_nopager(String *str, char*), com_pager(String *str, char*),
+ com_edit(String *str,char*), com_shell(String *str, char *);
+#endif
+
+static int read_and_execute(bool interactive);
+static int sql_connect(char *host,char *database,char *user,char *password,
+ uint silent);
+static const char *server_version_string(MYSQL *mysql);
+static int put_info(const char *str,INFO_TYPE info,uint error=0,
+ const char *sql_state=0);
+static int put_error(MYSQL *mysql);
+static void safe_put_field(const char *pos,ulong length);
+static void xmlencode_print(const char *src, uint length);
+static void init_pager();
+static void end_pager();
+static void init_tee(const char *);
+static void end_tee();
+static const char* construct_prompt();
+enum get_arg_mode { CHECK, GET, GET_NEXT};
+static char *get_arg(char *line, get_arg_mode mode);
+static void init_username();
+static void add_int_to_prompt(int toadd);
+static int get_result_width(MYSQL_RES *res);
+static int get_field_disp_length(MYSQL_FIELD * field);
+#ifndef EMBEDDED_LIBRARY
+static uint last_progress_report_length= 0;
+static void report_progress(const MYSQL *mysql, uint stage, uint max_stage,
+ double progress, const char *proc_info,
+ uint proc_info_length);
+#endif
+static void report_progress_end();
+
+/* A structure which contains information on the commands this program
+ can understand. */
+
+typedef struct {
+ const char *name; /* User printable name of the function. */
+ char cmd_char; /* msql command character */
+ int (*func)(String *str,char *); /* Function to call to do the job. */
+ bool takes_params; /* Max parameters for command */
+ const char *doc; /* Documentation for this function. */
+} COMMANDS;
+
+static COMMANDS commands[] = {
+ { "?", '?', com_help, 1, "Synonym for `help'." },
+ { "clear", 'c', com_clear, 0, "Clear the current input statement."},
+ { "connect",'r', com_connect,1,
+ "Reconnect to the server. Optional arguments are db and host." },
+ { "delimiter", 'd', com_delimiter, 1,
+ "Set statement delimiter." },
+#ifdef USE_POPEN
+ { "edit", 'e', com_edit, 0, "Edit command with $EDITOR."},
+#endif
+ { "ego", 'G', com_ego, 0,
+ "Send command to MariaDB server, display result vertically."},
+ { "exit", 'q', com_quit, 0, "Exit mysql. Same as quit."},
+ { "go", 'g', com_go, 0, "Send command to MariaDB server." },
+ { "help", 'h', com_help, 1, "Display this help." },
+#ifdef USE_POPEN
+ { "nopager",'n', com_nopager,0, "Disable pager, print to stdout." },
+#endif
+ { "notee", 't', com_notee, 0, "Don't write into outfile." },
+#ifdef USE_POPEN
+ { "pager", 'P', com_pager, 1,
+ "Set PAGER [to_pager]. Print the query results via PAGER." },
+#endif
+ { "print", 'p', com_print, 0, "Print current command." },
+ { "prompt", 'R', com_prompt, 1, "Change your mysql prompt."},
+ { "quit", 'q', com_quit, 0, "Quit mysql." },
+ { "rehash", '#', com_rehash, 0, "Rebuild completion hash." },
+ { "source", '.', com_source, 1,
+ "Execute an SQL script file. Takes a file name as an argument."},
+ { "status", 's', com_status, 0, "Get status information from the server."},
+#ifdef USE_POPEN
+ { "system", '!', com_shell, 1, "Execute a system shell command."},
+#endif
+ { "tee", 'T', com_tee, 1,
+ "Set outfile [to_outfile]. Append everything into given outfile." },
+ { "use", 'u', com_use, 1,
+ "Use another database. Takes database name as argument." },
+ { "charset", 'C', com_charset, 1,
+ "Switch to another charset. Might be needed for processing binlog with multi-byte charsets." },
+ { "warnings", 'W', com_warnings, 0,
+ "Show warnings after every statement." },
+ { "nowarning", 'w', com_nowarnings, 0,
+ "Don't show warnings after every statement." },
+ /* Get bash-like expansion for some commands */
+ { "create table", 0, 0, 0, ""},
+ { "create database", 0, 0, 0, ""},
+ { "show databases", 0, 0, 0, ""},
+ { "show fields from", 0, 0, 0, ""},
+ { "show keys from", 0, 0, 0, ""},
+ { "show tables", 0, 0, 0, ""},
+ { "load data from", 0, 0, 0, ""},
+ { "alter table", 0, 0, 0, ""},
+ { "set option", 0, 0, 0, ""},
+ { "lock tables", 0, 0, 0, ""},
+ { "unlock tables", 0, 0, 0, ""},
+ /* generated 2006-12-28. Refresh occasionally from lexer. */
+ { "ACTION", 0, 0, 0, ""},
+ { "ADD", 0, 0, 0, ""},
+ { "AFTER", 0, 0, 0, ""},
+ { "AGAINST", 0, 0, 0, ""},
+ { "AGGREGATE", 0, 0, 0, ""},
+ { "ALL", 0, 0, 0, ""},
+ { "ALGORITHM", 0, 0, 0, ""},
+ { "ALTER", 0, 0, 0, ""},
+ { "ANALYZE", 0, 0, 0, ""},
+ { "AND", 0, 0, 0, ""},
+ { "ANY", 0, 0, 0, ""},
+ { "AS", 0, 0, 0, ""},
+ { "ASC", 0, 0, 0, ""},
+ { "ASCII", 0, 0, 0, ""},
+ { "ASENSITIVE", 0, 0, 0, ""},
+ { "AUTO_INCREMENT", 0, 0, 0, ""},
+ { "AVG", 0, 0, 0, ""},
+ { "AVG_ROW_LENGTH", 0, 0, 0, ""},
+ { "BACKUP", 0, 0, 0, ""},
+ { "BDB", 0, 0, 0, ""},
+ { "BEFORE", 0, 0, 0, ""},
+ { "BEGIN", 0, 0, 0, ""},
+ { "BERKELEYDB", 0, 0, 0, ""},
+ { "BETWEEN", 0, 0, 0, ""},
+ { "BIGINT", 0, 0, 0, ""},
+ { "BINARY", 0, 0, 0, ""},
+ { "BINLOG", 0, 0, 0, ""},
+ { "BIT", 0, 0, 0, ""},
+ { "BLOB", 0, 0, 0, ""},
+ { "BOOL", 0, 0, 0, ""},
+ { "BOOLEAN", 0, 0, 0, ""},
+ { "BOTH", 0, 0, 0, ""},
+ { "BTREE", 0, 0, 0, ""},
+ { "BY", 0, 0, 0, ""},
+ { "BYTE", 0, 0, 0, ""},
+ { "CACHE", 0, 0, 0, ""},
+ { "CALL", 0, 0, 0, ""},
+ { "CASCADE", 0, 0, 0, ""},
+ { "CASCADED", 0, 0, 0, ""},
+ { "CASE", 0, 0, 0, ""},
+ { "CHAIN", 0, 0, 0, ""},
+ { "CHANGE", 0, 0, 0, ""},
+ { "CHANGED", 0, 0, 0, ""},
+ { "CHAR", 0, 0, 0, ""},
+ { "CHARACTER", 0, 0, 0, ""},
+ { "CHARSET", 0, 0, 0, ""},
+ { "CHECK", 0, 0, 0, ""},
+ { "CHECKSUM", 0, 0, 0, ""},
+ { "CIPHER", 0, 0, 0, ""},
+ { "CLIENT", 0, 0, 0, ""},
+ { "CLOSE", 0, 0, 0, ""},
+ { "CODE", 0, 0, 0, ""},
+ { "COLLATE", 0, 0, 0, ""},
+ { "COLLATION", 0, 0, 0, ""},
+ { "COLUMN", 0, 0, 0, ""},
+ { "COLUMNS", 0, 0, 0, ""},
+ { "COMMENT", 0, 0, 0, ""},
+ { "COMMIT", 0, 0, 0, ""},
+ { "COMMITTED", 0, 0, 0, ""},
+ { "COMPACT", 0, 0, 0, ""},
+ { "COMPRESSED", 0, 0, 0, ""},
+ { "CONCURRENT", 0, 0, 0, ""},
+ { "CONDITION", 0, 0, 0, ""},
+ { "CONNECTION", 0, 0, 0, ""},
+ { "CONSISTENT", 0, 0, 0, ""},
+ { "CONSTRAINT", 0, 0, 0, ""},
+ { "CONTAINS", 0, 0, 0, ""},
+ { "CONTINUE", 0, 0, 0, ""},
+ { "CONVERT", 0, 0, 0, ""},
+ { "CREATE", 0, 0, 0, ""},
+ { "CROSS", 0, 0, 0, ""},
+ { "CUBE", 0, 0, 0, ""},
+ { "CURRENT_DATE", 0, 0, 0, ""},
+ { "CURRENT_TIME", 0, 0, 0, ""},
+ { "CURRENT_TIMESTAMP", 0, 0, 0, ""},
+ { "CURRENT_USER", 0, 0, 0, ""},
+ { "CURSOR", 0, 0, 0, ""},
+ { "DATA", 0, 0, 0, ""},
+ { "DATABASE", 0, 0, 0, ""},
+ { "DATABASES", 0, 0, 0, ""},
+ { "DATE", 0, 0, 0, ""},
+ { "DATETIME", 0, 0, 0, ""},
+ { "DAY", 0, 0, 0, ""},
+ { "DAY_HOUR", 0, 0, 0, ""},
+ { "DAY_MICROSECOND", 0, 0, 0, ""},
+ { "DAY_MINUTE", 0, 0, 0, ""},
+ { "DAY_SECOND", 0, 0, 0, ""},
+ { "DEALLOCATE", 0, 0, 0, ""},
+ { "DEC", 0, 0, 0, ""},
+ { "DECIMAL", 0, 0, 0, ""},
+ { "DECLARE", 0, 0, 0, ""},
+ { "DEFAULT", 0, 0, 0, ""},
+ { "DEFINER", 0, 0, 0, ""},
+ { "DELAYED", 0, 0, 0, ""},
+ { "DELAY_KEY_WRITE", 0, 0, 0, ""},
+ { "DELETE", 0, 0, 0, ""},
+ { "DESC", 0, 0, 0, ""},
+ { "DESCRIBE", 0, 0, 0, ""},
+ { "DES_KEY_FILE", 0, 0, 0, ""},
+ { "DETERMINISTIC", 0, 0, 0, ""},
+ { "DIRECTORY", 0, 0, 0, ""},
+ { "DISABLE", 0, 0, 0, ""},
+ { "DISCARD", 0, 0, 0, ""},
+ { "DISTINCT", 0, 0, 0, ""},
+ { "DISTINCTROW", 0, 0, 0, ""},
+ { "DIV", 0, 0, 0, ""},
+ { "DO", 0, 0, 0, ""},
+ { "DOUBLE", 0, 0, 0, ""},
+ { "DROP", 0, 0, 0, ""},
+ { "DUAL", 0, 0, 0, ""},
+ { "DUMPFILE", 0, 0, 0, ""},
+ { "DUPLICATE", 0, 0, 0, ""},
+ { "DYNAMIC", 0, 0, 0, ""},
+ { "EACH", 0, 0, 0, ""},
+ { "ELSE", 0, 0, 0, ""},
+ { "ELSEIF", 0, 0, 0, ""},
+ { "ENABLE", 0, 0, 0, ""},
+ { "ENCLOSED", 0, 0, 0, ""},
+ { "END", 0, 0, 0, ""},
+ { "ENGINE", 0, 0, 0, ""},
+ { "ENGINES", 0, 0, 0, ""},
+ { "ENUM", 0, 0, 0, ""},
+ { "ERRORS", 0, 0, 0, ""},
+ { "ESCAPE", 0, 0, 0, ""},
+ { "ESCAPED", 0, 0, 0, ""},
+ { "EVENTS", 0, 0, 0, ""},
+ { "EXECUTE", 0, 0, 0, ""},
+ { "EXISTS", 0, 0, 0, ""},
+ { "EXIT", 0, 0, 0, ""},
+ { "EXPANSION", 0, 0, 0, ""},
+ { "EXPLAIN", 0, 0, 0, ""},
+ { "EXTENDED", 0, 0, 0, ""},
+ { "FALSE", 0, 0, 0, ""},
+ { "FAST", 0, 0, 0, ""},
+ { "FETCH", 0, 0, 0, ""},
+ { "FIELDS", 0, 0, 0, ""},
+ { "FILE", 0, 0, 0, ""},
+ { "FIRST", 0, 0, 0, ""},
+ { "FIXED", 0, 0, 0, ""},
+ { "FLOAT", 0, 0, 0, ""},
+ { "FLOAT4", 0, 0, 0, ""},
+ { "FLOAT8", 0, 0, 0, ""},
+ { "FLUSH", 0, 0, 0, ""},
+ { "FOR", 0, 0, 0, ""},
+ { "FORCE", 0, 0, 0, ""},
+ { "FOREIGN", 0, 0, 0, ""},
+ { "FOUND", 0, 0, 0, ""},
+ { "FROM", 0, 0, 0, ""},
+ { "FULL", 0, 0, 0, ""},
+ { "FULLTEXT", 0, 0, 0, ""},
+ { "FUNCTION", 0, 0, 0, ""},
+ { "GEOMETRY", 0, 0, 0, ""},
+ { "GEOMETRYCOLLECTION", 0, 0, 0, ""},
+ { "GET_FORMAT", 0, 0, 0, ""},
+ { "GLOBAL", 0, 0, 0, ""},
+ { "GRANT", 0, 0, 0, ""},
+ { "GRANTS", 0, 0, 0, ""},
+ { "GROUP", 0, 0, 0, ""},
+ { "HANDLER", 0, 0, 0, ""},
+ { "HASH", 0, 0, 0, ""},
+ { "HAVING", 0, 0, 0, ""},
+ { "HELP", 0, 0, 0, ""},
+ { "HIGH_PRIORITY", 0, 0, 0, ""},
+ { "HOSTS", 0, 0, 0, ""},
+ { "HOUR", 0, 0, 0, ""},
+ { "HOUR_MICROSECOND", 0, 0, 0, ""},
+ { "HOUR_MINUTE", 0, 0, 0, ""},
+ { "HOUR_SECOND", 0, 0, 0, ""},
+ { "IDENTIFIED", 0, 0, 0, ""},
+ { "IF", 0, 0, 0, ""},
+ { "IGNORE", 0, 0, 0, ""},
+ { "IMPORT", 0, 0, 0, ""},
+ { "IN", 0, 0, 0, ""},
+ { "INDEX", 0, 0, 0, ""},
+ { "INDEXES", 0, 0, 0, ""},
+ { "INFILE", 0, 0, 0, ""},
+ { "INNER", 0, 0, 0, ""},
+ { "INNOBASE", 0, 0, 0, ""},
+ { "INNODB", 0, 0, 0, ""},
+ { "INOUT", 0, 0, 0, ""},
+ { "INSENSITIVE", 0, 0, 0, ""},
+ { "INSERT", 0, 0, 0, ""},
+ { "INSERT_METHOD", 0, 0, 0, ""},
+ { "INT", 0, 0, 0, ""},
+ { "INT1", 0, 0, 0, ""},
+ { "INT2", 0, 0, 0, ""},
+ { "INT3", 0, 0, 0, ""},
+ { "INT4", 0, 0, 0, ""},
+ { "INT8", 0, 0, 0, ""},
+ { "INTEGER", 0, 0, 0, ""},
+ { "INTERVAL", 0, 0, 0, ""},
+ { "INTO", 0, 0, 0, ""},
+ { "IO_THREAD", 0, 0, 0, ""},
+ { "IS", 0, 0, 0, ""},
+ { "ISOLATION", 0, 0, 0, ""},
+ { "ISSUER", 0, 0, 0, ""},
+ { "ITERATE", 0, 0, 0, ""},
+ { "INVOKER", 0, 0, 0, ""},
+ { "JOIN", 0, 0, 0, ""},
+ { "KEY", 0, 0, 0, ""},
+ { "KEYS", 0, 0, 0, ""},
+ { "KILL", 0, 0, 0, ""},
+ { "LANGUAGE", 0, 0, 0, ""},
+ { "LAST", 0, 0, 0, ""},
+ { "LEADING", 0, 0, 0, ""},
+ { "LEAVE", 0, 0, 0, ""},
+ { "LEAVES", 0, 0, 0, ""},
+ { "LEFT", 0, 0, 0, ""},
+ { "LEVEL", 0, 0, 0, ""},
+ { "LIKE", 0, 0, 0, ""},
+ { "LIMIT", 0, 0, 0, ""},
+ { "LINES", 0, 0, 0, ""},
+ { "LINESTRING", 0, 0, 0, ""},
+ { "LOAD", 0, 0, 0, ""},
+ { "LOCAL", 0, 0, 0, ""},
+ { "LOCALTIME", 0, 0, 0, ""},
+ { "LOCALTIMESTAMP", 0, 0, 0, ""},
+ { "LOCK", 0, 0, 0, ""},
+ { "LOCKS", 0, 0, 0, ""},
+ { "LOGS", 0, 0, 0, ""},
+ { "LONG", 0, 0, 0, ""},
+ { "LONGBLOB", 0, 0, 0, ""},
+ { "LONGTEXT", 0, 0, 0, ""},
+ { "LOOP", 0, 0, 0, ""},
+ { "LOW_PRIORITY", 0, 0, 0, ""},
+ { "MASTER", 0, 0, 0, ""},
+ { "MASTER_CONNECT_RETRY", 0, 0, 0, ""},
+ { "MASTER_HOST", 0, 0, 0, ""},
+ { "MASTER_LOG_FILE", 0, 0, 0, ""},
+ { "MASTER_LOG_POS", 0, 0, 0, ""},
+ { "MASTER_PASSWORD", 0, 0, 0, ""},
+ { "MASTER_PORT", 0, 0, 0, ""},
+ { "MASTER_SERVER_ID", 0, 0, 0, ""},
+ { "MASTER_SSL", 0, 0, 0, ""},
+ { "MASTER_SSL_CA", 0, 0, 0, ""},
+ { "MASTER_SSL_CAPATH", 0, 0, 0, ""},
+ { "MASTER_SSL_CERT", 0, 0, 0, ""},
+ { "MASTER_SSL_CIPHER", 0, 0, 0, ""},
+ { "MASTER_SSL_KEY", 0, 0, 0, ""},
+ { "MASTER_USER", 0, 0, 0, ""},
+ { "MATCH", 0, 0, 0, ""},
+ { "MAX_CONNECTIONS_PER_HOUR", 0, 0, 0, ""},
+ { "MAX_QUERIES_PER_HOUR", 0, 0, 0, ""},
+ { "MAX_ROWS", 0, 0, 0, ""},
+ { "MAX_UPDATES_PER_HOUR", 0, 0, 0, ""},
+ { "MAX_USER_CONNECTIONS", 0, 0, 0, ""},
+ { "MEDIUM", 0, 0, 0, ""},
+ { "MEDIUMBLOB", 0, 0, 0, ""},
+ { "MEDIUMINT", 0, 0, 0, ""},
+ { "MEDIUMTEXT", 0, 0, 0, ""},
+ { "MERGE", 0, 0, 0, ""},
+ { "MICROSECOND", 0, 0, 0, ""},
+ { "MIDDLEINT", 0, 0, 0, ""},
+ { "MIGRATE", 0, 0, 0, ""},
+ { "MINUTE", 0, 0, 0, ""},
+ { "MINUTE_MICROSECOND", 0, 0, 0, ""},
+ { "MINUTE_SECOND", 0, 0, 0, ""},
+ { "MIN_ROWS", 0, 0, 0, ""},
+ { "MOD", 0, 0, 0, ""},
+ { "MODE", 0, 0, 0, ""},
+ { "MODIFIES", 0, 0, 0, ""},
+ { "MODIFY", 0, 0, 0, ""},
+ { "MONTH", 0, 0, 0, ""},
+ { "MULTILINESTRING", 0, 0, 0, ""},
+ { "MULTIPOINT", 0, 0, 0, ""},
+ { "MULTIPOLYGON", 0, 0, 0, ""},
+ { "MUTEX", 0, 0, 0, ""},
+ { "NAME", 0, 0, 0, ""},
+ { "NAMES", 0, 0, 0, ""},
+ { "NATIONAL", 0, 0, 0, ""},
+ { "NATURAL", 0, 0, 0, ""},
+ { "NCHAR", 0, 0, 0, ""},
+ { "NEW", 0, 0, 0, ""},
+ { "NEXT", 0, 0, 0, ""},
+ { "NO", 0, 0, 0, ""},
+ { "NONE", 0, 0, 0, ""},
+ { "NOT", 0, 0, 0, ""},
+ { "NO_WRITE_TO_BINLOG", 0, 0, 0, ""},
+ { "NULL", 0, 0, 0, ""},
+ { "NUMERIC", 0, 0, 0, ""},
+ { "NVARCHAR", 0, 0, 0, ""},
+ { "OFFSET", 0, 0, 0, ""},
+ { "OLD_PASSWORD", 0, 0, 0, ""},
+ { "ON", 0, 0, 0, ""},
+ { "ONE", 0, 0, 0, ""},
+ { "OPEN", 0, 0, 0, ""},
+ { "OPTIMIZE", 0, 0, 0, ""},
+ { "OPTION", 0, 0, 0, ""},
+ { "OPTIONALLY", 0, 0, 0, ""},
+ { "OR", 0, 0, 0, ""},
+ { "ORDER", 0, 0, 0, ""},
+ { "OUT", 0, 0, 0, ""},
+ { "OUTER", 0, 0, 0, ""},
+ { "OUTFILE", 0, 0, 0, ""},
+ { "PACK_KEYS", 0, 0, 0, ""},
+ { "PARTIAL", 0, 0, 0, ""},
+ { "PASSWORD", 0, 0, 0, ""},
+ { "PHASE", 0, 0, 0, ""},
+ { "POINT", 0, 0, 0, ""},
+ { "POLYGON", 0, 0, 0, ""},
+ { "PRECISION", 0, 0, 0, ""},
+ { "PREPARE", 0, 0, 0, ""},
+ { "PREV", 0, 0, 0, ""},
+ { "PRIMARY", 0, 0, 0, ""},
+ { "PRIVILEGES", 0, 0, 0, ""},
+ { "PROCEDURE", 0, 0, 0, ""},
+ { "PROCESS", 0, 0, 0, ""},
+ { "PROCESSLIST", 0, 0, 0, ""},
+ { "PURGE", 0, 0, 0, ""},
+ { "QUARTER", 0, 0, 0, ""},
+ { "QUERY", 0, 0, 0, ""},
+ { "QUICK", 0, 0, 0, ""},
+ { "READ", 0, 0, 0, ""},
+ { "READS", 0, 0, 0, ""},
+ { "REAL", 0, 0, 0, ""},
+ { "RECOVER", 0, 0, 0, ""},
+ { "REDUNDANT", 0, 0, 0, ""},
+ { "REFERENCES", 0, 0, 0, ""},
+ { "REGEXP", 0, 0, 0, ""},
+ { "RELAY_LOG_FILE", 0, 0, 0, ""},
+ { "RELAY_LOG_POS", 0, 0, 0, ""},
+ { "RELAY_THREAD", 0, 0, 0, ""},
+ { "RELEASE", 0, 0, 0, ""},
+ { "RELOAD", 0, 0, 0, ""},
+ { "RENAME", 0, 0, 0, ""},
+ { "REPAIR", 0, 0, 0, ""},
+ { "REPEATABLE", 0, 0, 0, ""},
+ { "REPLACE", 0, 0, 0, ""},
+ { "REPLICATION", 0, 0, 0, ""},
+ { "REPEAT", 0, 0, 0, ""},
+ { "REQUIRE", 0, 0, 0, ""},
+ { "RESET", 0, 0, 0, ""},
+ { "RESTORE", 0, 0, 0, ""},
+ { "RESTRICT", 0, 0, 0, ""},
+ { "RESUME", 0, 0, 0, ""},
+ { "RETURN", 0, 0, 0, ""},
+ { "RETURNS", 0, 0, 0, ""},
+ { "REVOKE", 0, 0, 0, ""},
+ { "RIGHT", 0, 0, 0, ""},
+ { "RLIKE", 0, 0, 0, ""},
+ { "ROLLBACK", 0, 0, 0, ""},
+ { "ROLLUP", 0, 0, 0, ""},
+ { "ROUTINE", 0, 0, 0, ""},
+ { "ROW", 0, 0, 0, ""},
+ { "ROWS", 0, 0, 0, ""},
+ { "ROW_FORMAT", 0, 0, 0, ""},
+ { "RTREE", 0, 0, 0, ""},
+ { "SAVEPOINT", 0, 0, 0, ""},
+ { "SCHEMA", 0, 0, 0, ""},
+ { "SCHEMAS", 0, 0, 0, ""},
+ { "SECOND", 0, 0, 0, ""},
+ { "SECOND_MICROSECOND", 0, 0, 0, ""},
+ { "SECURITY", 0, 0, 0, ""},
+ { "SELECT", 0, 0, 0, ""},
+ { "SENSITIVE", 0, 0, 0, ""},
+ { "SEPARATOR", 0, 0, 0, ""},
+ { "SERIAL", 0, 0, 0, ""},
+ { "SERIALIZABLE", 0, 0, 0, ""},
+ { "SESSION", 0, 0, 0, ""},
+ { "SET", 0, 0, 0, ""},
+ { "SHARE", 0, 0, 0, ""},
+ { "SHOW", 0, 0, 0, ""},
+ { "SHUTDOWN", 0, 0, 0, ""},
+ { "SIGNED", 0, 0, 0, ""},
+ { "SIMPLE", 0, 0, 0, ""},
+ { "SLAVE", 0, 0, 0, ""},
+ { "SNAPSHOT", 0, 0, 0, ""},
+ { "SMALLINT", 0, 0, 0, ""},
+ { "SOME", 0, 0, 0, ""},
+ { "SONAME", 0, 0, 0, ""},
+ { "SOUNDS", 0, 0, 0, ""},
+ { "SPATIAL", 0, 0, 0, ""},
+ { "SPECIFIC", 0, 0, 0, ""},
+ { "SQL", 0, 0, 0, ""},
+ { "SQLEXCEPTION", 0, 0, 0, ""},
+ { "SQLSTATE", 0, 0, 0, ""},
+ { "SQLWARNING", 0, 0, 0, ""},
+ { "SQL_BIG_RESULT", 0, 0, 0, ""},
+ { "SQL_BUFFER_RESULT", 0, 0, 0, ""},
+ { "SQL_CACHE", 0, 0, 0, ""},
+ { "SQL_CALC_FOUND_ROWS", 0, 0, 0, ""},
+ { "SQL_NO_CACHE", 0, 0, 0, ""},
+ { "SQL_SMALL_RESULT", 0, 0, 0, ""},
+ { "SQL_THREAD", 0, 0, 0, ""},
+ { "SQL_TSI_SECOND", 0, 0, 0, ""},
+ { "SQL_TSI_MINUTE", 0, 0, 0, ""},
+ { "SQL_TSI_HOUR", 0, 0, 0, ""},
+ { "SQL_TSI_DAY", 0, 0, 0, ""},
+ { "SQL_TSI_WEEK", 0, 0, 0, ""},
+ { "SQL_TSI_MONTH", 0, 0, 0, ""},
+ { "SQL_TSI_QUARTER", 0, 0, 0, ""},
+ { "SQL_TSI_YEAR", 0, 0, 0, ""},
+ { "SSL", 0, 0, 0, ""},
+ { "START", 0, 0, 0, ""},
+ { "STARTING", 0, 0, 0, ""},
+ { "STATUS", 0, 0, 0, ""},
+ { "STOP", 0, 0, 0, ""},
+ { "STORAGE", 0, 0, 0, ""},
+ { "STRAIGHT_JOIN", 0, 0, 0, ""},
+ { "STRING", 0, 0, 0, ""},
+ { "STRIPED", 0, 0, 0, ""},
+ { "SUBJECT", 0, 0, 0, ""},
+ { "SUPER", 0, 0, 0, ""},
+ { "SUSPEND", 0, 0, 0, ""},
+ { "TABLE", 0, 0, 0, ""},
+ { "TABLES", 0, 0, 0, ""},
+ { "TABLESPACE", 0, 0, 0, ""},
+ { "TEMPORARY", 0, 0, 0, ""},
+ { "TEMPTABLE", 0, 0, 0, ""},
+ { "TERMINATED", 0, 0, 0, ""},
+ { "TEXT", 0, 0, 0, ""},
+ { "THEN", 0, 0, 0, ""},
+ { "TIME", 0, 0, 0, ""},
+ { "TIMESTAMP", 0, 0, 0, ""},
+ { "TIMESTAMPADD", 0, 0, 0, ""},
+ { "TIMESTAMPDIFF", 0, 0, 0, ""},
+ { "TINYBLOB", 0, 0, 0, ""},
+ { "TINYINT", 0, 0, 0, ""},
+ { "TINYTEXT", 0, 0, 0, ""},
+ { "TO", 0, 0, 0, ""},
+ { "TRAILING", 0, 0, 0, ""},
+ { "TRANSACTION", 0, 0, 0, ""},
+ { "TRIGGER", 0, 0, 0, ""},
+ { "TRIGGERS", 0, 0, 0, ""},
+ { "TRUE", 0, 0, 0, ""},
+ { "TRUNCATE", 0, 0, 0, ""},
+ { "TYPE", 0, 0, 0, ""},
+ { "TYPES", 0, 0, 0, ""},
+ { "UNCOMMITTED", 0, 0, 0, ""},
+ { "UNDEFINED", 0, 0, 0, ""},
+ { "UNDO", 0, 0, 0, ""},
+ { "UNICODE", 0, 0, 0, ""},
+ { "UNION", 0, 0, 0, ""},
+ { "UNIQUE", 0, 0, 0, ""},
+ { "UNKNOWN", 0, 0, 0, ""},
+ { "UNLOCK", 0, 0, 0, ""},
+ { "UNSIGNED", 0, 0, 0, ""},
+ { "UNTIL", 0, 0, 0, ""},
+ { "UPDATE", 0, 0, 0, ""},
+ { "UPGRADE", 0, 0, 0, ""},
+ { "USAGE", 0, 0, 0, ""},
+ { "USE", 0, 0, 0, ""},
+ { "USER", 0, 0, 0, ""},
+ { "USER_RESOURCES", 0, 0, 0, ""},
+ { "USE_FRM", 0, 0, 0, ""},
+ { "USING", 0, 0, 0, ""},
+ { "UTC_DATE", 0, 0, 0, ""},
+ { "UTC_TIME", 0, 0, 0, ""},
+ { "UTC_TIMESTAMP", 0, 0, 0, ""},
+ { "VALUE", 0, 0, 0, ""},
+ { "VALUES", 0, 0, 0, ""},
+ { "VARBINARY", 0, 0, 0, ""},
+ { "VARCHAR", 0, 0, 0, ""},
+ { "VARCHARACTER", 0, 0, 0, ""},
+ { "VARIABLES", 0, 0, 0, ""},
+ { "VARYING", 0, 0, 0, ""},
+ { "WARNINGS", 0, 0, 0, ""},
+ { "WEEK", 0, 0, 0, ""},
+ { "WHEN", 0, 0, 0, ""},
+ { "WHERE", 0, 0, 0, ""},
+ { "WHILE", 0, 0, 0, ""},
+ { "VIEW", 0, 0, 0, ""},
+ { "WITH", 0, 0, 0, ""},
+ { "WORK", 0, 0, 0, ""},
+ { "WRITE", 0, 0, 0, ""},
+ { "X509", 0, 0, 0, ""},
+ { "XOR", 0, 0, 0, ""},
+ { "XA", 0, 0, 0, ""},
+ { "YEAR", 0, 0, 0, ""},
+ { "YEAR_MONTH", 0, 0, 0, ""},
+ { "ZEROFILL", 0, 0, 0, ""},
+ { "ABS", 0, 0, 0, ""},
+ { "ACOS", 0, 0, 0, ""},
+ { "ADDDATE", 0, 0, 0, ""},
+ { "ADDTIME", 0, 0, 0, ""},
+ { "AES_ENCRYPT", 0, 0, 0, ""},
+ { "AES_DECRYPT", 0, 0, 0, ""},
+ { "AREA", 0, 0, 0, ""},
+ { "ASIN", 0, 0, 0, ""},
+ { "ASBINARY", 0, 0, 0, ""},
+ { "ASTEXT", 0, 0, 0, ""},
+ { "ASWKB", 0, 0, 0, ""},
+ { "ASWKT", 0, 0, 0, ""},
+ { "ATAN", 0, 0, 0, ""},
+ { "ATAN2", 0, 0, 0, ""},
+ { "BENCHMARK", 0, 0, 0, ""},
+ { "BIN", 0, 0, 0, ""},
+ { "BIT_COUNT", 0, 0, 0, ""},
+ { "BIT_OR", 0, 0, 0, ""},
+ { "BIT_AND", 0, 0, 0, ""},
+ { "BIT_XOR", 0, 0, 0, ""},
+ { "CAST", 0, 0, 0, ""},
+ { "CEIL", 0, 0, 0, ""},
+ { "CEILING", 0, 0, 0, ""},
+ { "BIT_LENGTH", 0, 0, 0, ""},
+ { "CENTROID", 0, 0, 0, ""},
+ { "CHAR_LENGTH", 0, 0, 0, ""},
+ { "CHARACTER_LENGTH", 0, 0, 0, ""},
+ { "COALESCE", 0, 0, 0, ""},
+ { "COERCIBILITY", 0, 0, 0, ""},
+ { "COMPRESS", 0, 0, 0, ""},
+ { "CONCAT", 0, 0, 0, ""},
+ { "CONCAT_WS", 0, 0, 0, ""},
+ { "CONNECTION_ID", 0, 0, 0, ""},
+ { "CONV", 0, 0, 0, ""},
+ { "CONVERT_TZ", 0, 0, 0, ""},
+ { "COUNT", 0, 0, 0, ""},
+ { "COS", 0, 0, 0, ""},
+ { "COT", 0, 0, 0, ""},
+ { "CRC32", 0, 0, 0, ""},
+ { "CROSSES", 0, 0, 0, ""},
+ { "CURDATE", 0, 0, 0, ""},
+ { "CURTIME", 0, 0, 0, ""},
+ { "DATE_ADD", 0, 0, 0, ""},
+ { "DATEDIFF", 0, 0, 0, ""},
+ { "DATE_FORMAT", 0, 0, 0, ""},
+ { "DATE_SUB", 0, 0, 0, ""},
+ { "DAYNAME", 0, 0, 0, ""},
+ { "DAYOFMONTH", 0, 0, 0, ""},
+ { "DAYOFWEEK", 0, 0, 0, ""},
+ { "DAYOFYEAR", 0, 0, 0, ""},
+ { "DECODE", 0, 0, 0, ""},
+ { "DEGREES", 0, 0, 0, ""},
+ { "DES_ENCRYPT", 0, 0, 0, ""},
+ { "DES_DECRYPT", 0, 0, 0, ""},
+ { "DIMENSION", 0, 0, 0, ""},
+ { "DISJOINT", 0, 0, 0, ""},
+ { "ELT", 0, 0, 0, ""},
+ { "ENCODE", 0, 0, 0, ""},
+ { "ENCRYPT", 0, 0, 0, ""},
+ { "ENDPOINT", 0, 0, 0, ""},
+ { "ENVELOPE", 0, 0, 0, ""},
+ { "EQUALS", 0, 0, 0, ""},
+ { "EXTERIORRING", 0, 0, 0, ""},
+ { "EXTRACT", 0, 0, 0, ""},
+ { "EXP", 0, 0, 0, ""},
+ { "EXPORT_SET", 0, 0, 0, ""},
+ { "FIELD", 0, 0, 0, ""},
+ { "FIND_IN_SET", 0, 0, 0, ""},
+ { "FLOOR", 0, 0, 0, ""},
+ { "FORMAT", 0, 0, 0, ""},
+ { "FOUND_ROWS", 0, 0, 0, ""},
+ { "FROM_DAYS", 0, 0, 0, ""},
+ { "FROM_UNIXTIME", 0, 0, 0, ""},
+ { "GET_LOCK", 0, 0, 0, ""},
+ { "GEOMETRYN", 0, 0, 0, ""},
+ { "GEOMETRYTYPE", 0, 0, 0, ""},
+ { "GEOMCOLLFROMTEXT", 0, 0, 0, ""},
+ { "GEOMCOLLFROMWKB", 0, 0, 0, ""},
+ { "GEOMETRYCOLLECTIONFROMTEXT", 0, 0, 0, ""},
+ { "GEOMETRYCOLLECTIONFROMWKB", 0, 0, 0, ""},
+ { "GEOMETRYFROMTEXT", 0, 0, 0, ""},
+ { "GEOMETRYFROMWKB", 0, 0, 0, ""},
+ { "GEOMFROMTEXT", 0, 0, 0, ""},
+ { "GEOMFROMWKB", 0, 0, 0, ""},
+ { "GLENGTH", 0, 0, 0, ""},
+ { "GREATEST", 0, 0, 0, ""},
+ { "GROUP_CONCAT", 0, 0, 0, ""},
+ { "GROUP_UNIQUE_USERS", 0, 0, 0, ""},
+ { "HEX", 0, 0, 0, ""},
+ { "IFNULL", 0, 0, 0, ""},
+ { "INET_ATON", 0, 0, 0, ""},
+ { "INET_NTOA", 0, 0, 0, ""},
+ { "INSTR", 0, 0, 0, ""},
+ { "INTERIORRINGN", 0, 0, 0, ""},
+ { "INTERSECTS", 0, 0, 0, ""},
+ { "ISCLOSED", 0, 0, 0, ""},
+ { "ISEMPTY", 0, 0, 0, ""},
+ { "ISNULL", 0, 0, 0, ""},
+ { "IS_FREE_LOCK", 0, 0, 0, ""},
+ { "IS_USED_LOCK", 0, 0, 0, ""},
+ { "LAST_INSERT_ID", 0, 0, 0, ""},
+ { "ISSIMPLE", 0, 0, 0, ""},
+ { "LAST_DAY", 0, 0, 0, ""},
+ { "LAST_VALUE", 0, 0, 0, ""},
+ { "LCASE", 0, 0, 0, ""},
+ { "LEAST", 0, 0, 0, ""},
+ { "LENGTH", 0, 0, 0, ""},
+ { "LN", 0, 0, 0, ""},
+ { "LINEFROMTEXT", 0, 0, 0, ""},
+ { "LINEFROMWKB", 0, 0, 0, ""},
+ { "LINESTRINGFROMTEXT", 0, 0, 0, ""},
+ { "LINESTRINGFROMWKB", 0, 0, 0, ""},
+ { "LOAD_FILE", 0, 0, 0, ""},
+ { "LOCATE", 0, 0, 0, ""},
+ { "LOG", 0, 0, 0, ""},
+ { "LOG2", 0, 0, 0, ""},
+ { "LOG10", 0, 0, 0, ""},
+ { "LOWER", 0, 0, 0, ""},
+ { "LPAD", 0, 0, 0, ""},
+ { "LTRIM", 0, 0, 0, ""},
+ { "MAKE_SET", 0, 0, 0, ""},
+ { "MAKEDATE", 0, 0, 0, ""},
+ { "MAKETIME", 0, 0, 0, ""},
+ { "MASTER_GTID_WAIT", 0, 0, 0, ""},
+ { "MASTER_POS_WAIT", 0, 0, 0, ""},
+ { "MAX", 0, 0, 0, ""},
+ { "MBRCONTAINS", 0, 0, 0, ""},
+ { "MBRDISJOINT", 0, 0, 0, ""},
+ { "MBREQUAL", 0, 0, 0, ""},
+ { "MBRINTERSECTS", 0, 0, 0, ""},
+ { "MBROVERLAPS", 0, 0, 0, ""},
+ { "MBRTOUCHES", 0, 0, 0, ""},
+ { "MBRWITHIN", 0, 0, 0, ""},
+ { "MD5", 0, 0, 0, ""},
+ { "MID", 0, 0, 0, ""},
+ { "MIN", 0, 0, 0, ""},
+ { "MLINEFROMTEXT", 0, 0, 0, ""},
+ { "MLINEFROMWKB", 0, 0, 0, ""},
+ { "MPOINTFROMTEXT", 0, 0, 0, ""},
+ { "MPOINTFROMWKB", 0, 0, 0, ""},
+ { "MPOLYFROMTEXT", 0, 0, 0, ""},
+ { "MPOLYFROMWKB", 0, 0, 0, ""},
+ { "MONTHNAME", 0, 0, 0, ""},
+ { "MULTILINESTRINGFROMTEXT", 0, 0, 0, ""},
+ { "MULTILINESTRINGFROMWKB", 0, 0, 0, ""},
+ { "MULTIPOINTFROMTEXT", 0, 0, 0, ""},
+ { "MULTIPOINTFROMWKB", 0, 0, 0, ""},
+ { "MULTIPOLYGONFROMTEXT", 0, 0, 0, ""},
+ { "MULTIPOLYGONFROMWKB", 0, 0, 0, ""},
+ { "NAME_CONST", 0, 0, 0, ""},
+ { "NOW", 0, 0, 0, ""},
+ { "NULLIF", 0, 0, 0, ""},
+ { "NUMGEOMETRIES", 0, 0, 0, ""},
+ { "NUMINTERIORRINGS", 0, 0, 0, ""},
+ { "NUMPOINTS", 0, 0, 0, ""},
+ { "OCTET_LENGTH", 0, 0, 0, ""},
+ { "OCT", 0, 0, 0, ""},
+ { "ORD", 0, 0, 0, ""},
+ { "OVERLAPS", 0, 0, 0, ""},
+ { "PERIOD_ADD", 0, 0, 0, ""},
+ { "PERIOD_DIFF", 0, 0, 0, ""},
+ { "PI", 0, 0, 0, ""},
+ { "POINTFROMTEXT", 0, 0, 0, ""},
+ { "POINTFROMWKB", 0, 0, 0, ""},
+ { "POINTN", 0, 0, 0, ""},
+ { "POLYFROMTEXT", 0, 0, 0, ""},
+ { "POLYFROMWKB", 0, 0, 0, ""},
+ { "POLYGONFROMTEXT", 0, 0, 0, ""},
+ { "POLYGONFROMWKB", 0, 0, 0, ""},
+ { "POSITION", 0, 0, 0, ""},
+ { "POW", 0, 0, 0, ""},
+ { "POWER", 0, 0, 0, ""},
+ { "QUOTE", 0, 0, 0, ""},
+ { "RADIANS", 0, 0, 0, ""},
+ { "RAND", 0, 0, 0, ""},
+ { "RELEASE_LOCK", 0, 0, 0, ""},
+ { "REVERSE", 0, 0, 0, ""},
+ { "ROUND", 0, 0, 0, ""},
+ { "ROW_COUNT", 0, 0, 0, ""},
+ { "RPAD", 0, 0, 0, ""},
+ { "RTRIM", 0, 0, 0, ""},
+ { "SEC_TO_TIME", 0, 0, 0, ""},
+ { "SESSION_USER", 0, 0, 0, ""},
+ { "SUBDATE", 0, 0, 0, ""},
+ { "SIGN", 0, 0, 0, ""},
+ { "SIN", 0, 0, 0, ""},
+ { "SHA", 0, 0, 0, ""},
+ { "SHA1", 0, 0, 0, ""},
+ { "SLEEP", 0, 0, 0, ""},
+ { "SOUNDEX", 0, 0, 0, ""},
+ { "SPACE", 0, 0, 0, ""},
+ { "SQRT", 0, 0, 0, ""},
+ { "SRID", 0, 0, 0, ""},
+ { "STARTPOINT", 0, 0, 0, ""},
+ { "STD", 0, 0, 0, ""},
+ { "STDDEV", 0, 0, 0, ""},
+ { "STDDEV_POP", 0, 0, 0, ""},
+ { "STDDEV_SAMP", 0, 0, 0, ""},
+ { "STR_TO_DATE", 0, 0, 0, ""},
+ { "STRCMP", 0, 0, 0, ""},
+ { "SUBSTR", 0, 0, 0, ""},
+ { "SUBSTRING", 0, 0, 0, ""},
+ { "SUBSTRING_INDEX", 0, 0, 0, ""},
+ { "SUBTIME", 0, 0, 0, ""},
+ { "SUM", 0, 0, 0, ""},
+ { "SYSDATE", 0, 0, 0, ""},
+ { "SYSTEM_USER", 0, 0, 0, ""},
+ { "TAN", 0, 0, 0, ""},
+ { "TIME_FORMAT", 0, 0, 0, ""},
+ { "TIME_TO_SEC", 0, 0, 0, ""},
+ { "TIMEDIFF", 0, 0, 0, ""},
+ { "TO_DAYS", 0, 0, 0, ""},
+ { "TOUCHES", 0, 0, 0, ""},
+ { "TRIM", 0, 0, 0, ""},
+ { "UCASE", 0, 0, 0, ""},
+ { "UNCOMPRESS", 0, 0, 0, ""},
+ { "UNCOMPRESSED_LENGTH", 0, 0, 0, ""},
+ { "UNHEX", 0, 0, 0, ""},
+ { "UNIQUE_USERS", 0, 0, 0, ""},
+ { "UNIX_TIMESTAMP", 0, 0, 0, ""},
+ { "UPPER", 0, 0, 0, ""},
+ { "UUID", 0, 0, 0, ""},
+ { "VARIANCE", 0, 0, 0, ""},
+ { "VAR_POP", 0, 0, 0, ""},
+ { "VAR_SAMP", 0, 0, 0, ""},
+ { "VERSION", 0, 0, 0, ""},
+ { "WEEKDAY", 0, 0, 0, ""},
+ { "WEEKOFYEAR", 0, 0, 0, ""},
+ { "WITHIN", 0, 0, 0, ""},
+ { "X", 0, 0, 0, ""},
+ { "Y", 0, 0, 0, ""},
+ { "YEARWEEK", 0, 0, 0, ""},
+ /* end sentinel */
+ { (char *)NULL, 0, 0, 0, ""}
+};
+
+static const char *load_default_groups[]=
+{ "mysql", "mariadb-client", "client", "client-server", "client-mariadb", 0 };
+
+static int embedded_server_arg_count= 0;
+static char *embedded_server_args[MAX_SERVER_ARGS];
+static const char *embedded_server_groups[]=
+{ "server", "embedded", "mysql_SERVER", "mariadb_SERVER", 0 };
+
+#ifdef HAVE_READLINE
+static int not_in_history(const char *line);
+static void initialize_readline ();
+static void fix_history(String *final_command);
+#endif
+
+static COMMANDS *find_command(char *name);
+static COMMANDS *find_command(char cmd_name);
+static bool add_line(String &, char *, size_t line_length, char *, bool *, bool);
+static void remove_cntrl(String &buffer);
+static void print_table_data(MYSQL_RES *result);
+static void print_table_data_html(MYSQL_RES *result);
+static void print_table_data_xml(MYSQL_RES *result);
+static void print_tab_data(MYSQL_RES *result);
+static void print_table_data_vertically(MYSQL_RES *result);
+static void print_warnings(void);
+static void end_timer(ulonglong start_time, char *buff);
+static void nice_time(double sec,char *buff,bool part_second);
+extern "C" sig_handler mysql_end(int sig) __attribute__ ((noreturn));
+extern "C" sig_handler handle_sigint(int sig);
+#if defined(HAVE_TERMIOS_H) && defined(GWINSZ_IN_SYS_IOCTL)
+static sig_handler window_resize(int sig);
+#endif
+
+
+const char DELIMITER_NAME[]= "delimiter";
+const uint DELIMITER_NAME_LEN= sizeof(DELIMITER_NAME) - 1;
+inline bool is_delimiter_command(char *name, ulong len)
+{
+ /*
+ Delimiter command has a parameter, so the length of the whole command
+ is larger than DELIMITER_NAME_LEN. We don't care the parameter, so
+ only name(first DELIMITER_NAME_LEN bytes) is checked.
+ */
+ return (len >= DELIMITER_NAME_LEN &&
+ !my_charset_latin1.strnncoll(name, DELIMITER_NAME_LEN,
+ DELIMITER_NAME, DELIMITER_NAME_LEN));
+}
+
+/**
+ Get the index of a command in the commands array.
+
+ @param cmd_char Short form command.
+
+ @return int
+ The index of the command is returned if it is found, else -1 is returned.
+*/
+inline int get_command_index(char cmd_char)
+{
+ /*
+ All client-specific commands are in the first part of commands array
+ and have a function to implement it.
+ */
+ for (uint i= 0; commands[i].func; i++)
+ if (commands[i].cmd_char == cmd_char)
+ return i;
+ return -1;
+}
+
+static int delimiter_index= -1;
+static int charset_index= -1;
+static bool real_binary_mode= FALSE;
+
+
+int main(int argc,char *argv[])
+{
+ char buff[80];
+
+ MY_INIT(argv[0]);
+ DBUG_ENTER("main");
+ DBUG_PROCESS(argv[0]);
+
+ charset_index= get_command_index('C');
+ delimiter_index= get_command_index('d');
+ delimiter_str= delimiter;
+ default_prompt = my_strdup(PSI_NOT_INSTRUMENTED, getenv("MYSQL_PS1") ?
+ getenv("MYSQL_PS1") :
+ "\\N [\\d]> ",MYF(MY_WME));
+ current_prompt = my_strdup(PSI_NOT_INSTRUMENTED, default_prompt,MYF(MY_WME));
+ prompt_counter=0;
+ aborted= 0;
+ sf_leaking_memory= 1; /* no memory leak reports yet */
+
+ outfile[0]=0; // no (default) outfile
+ strmov(pager, "stdout"); // the default, if --pager wasn't given
+
+ {
+ char *tmp=getenv("PAGER");
+ if (tmp && strlen(tmp))
+ {
+ default_pager_set= 1;
+ strmov(default_pager, tmp);
+ }
+ }
+ if (!isatty(0) || !isatty(1))
+ {
+ status.batch=1; opt_silent=1;
+ ignore_errors=0;
+ }
+ else
+ status.add_to_history=1;
+ status.exit_status=1;
+
+ {
+ /*
+ The file descriptor-layer may be out-of-sync with the file-number layer,
+ so we make sure that "stdout" is really open. If its file is closed then
+ explicitly close the FD layer.
+ */
+ int stdout_fileno_copy;
+ stdout_fileno_copy= dup(fileno(stdout)); /* Okay if fileno fails. */
+ if (stdout_fileno_copy == -1)
+ fclose(stdout);
+ else
+ close(stdout_fileno_copy); /* Clean up dup(). */
+ }
+
+ /* We need to know if protocol-related options originate from CLI args */
+ my_defaults_mark_files = TRUE;
+
+ load_defaults_or_exit("my", load_default_groups, &argc, &argv);
+ defaults_argv=argv;
+ if ((status.exit_status= get_options(argc, (char **) argv)))
+ {
+ free_defaults(defaults_argv);
+ my_end(0);
+ exit(status.exit_status);
+ }
+
+ if (status.batch && !status.line_buff &&
+ !(status.line_buff= batch_readline_init(MAX_BATCH_BUFFER_SIZE, stdin)))
+ {
+ put_info("Can't initialize batch_readline - may be the input source is "
+ "a directory or a block device.", INFO_ERROR, 0);
+ free_defaults(defaults_argv);
+ my_end(0);
+ exit(1);
+ }
+ if (mysql_server_init(embedded_server_arg_count, embedded_server_args,
+ (char**) embedded_server_groups))
+ {
+ put_error(NULL);
+ free_defaults(defaults_argv);
+ my_end(0);
+ exit(1);
+ }
+ sf_leaking_memory= 0;
+ glob_buffer.realloc(512);
+ completion_hash_init(&ht, 128);
+ init_alloc_root(PSI_NOT_INSTRUMENTED, &hash_mem_root, 16384, 0, MYF(0));
+ if (sql_connect(current_host,current_db,current_user,opt_password,
+ opt_silent))
+ {
+ quick= 1; // Avoid history
+ status.exit_status= 1;
+ mysql_end(-1);
+ }
+ if (!status.batch)
+ ignore_errors=1; // Don't abort monitor
+
+ if (opt_sigint_ignore)
+ signal(SIGINT, SIG_IGN);
+ else
+ signal(SIGINT, handle_sigint); // Catch SIGINT to clean up
+ signal(SIGQUIT, mysql_end); // Catch SIGQUIT to clean up
+
+#if defined(HAVE_TERMIOS_H) && defined(GWINSZ_IN_SYS_IOCTL)
+ /* Readline will call this if it installs a handler */
+ signal(SIGWINCH, window_resize);
+ /* call the SIGWINCH handler to get the default term width */
+ window_resize(0);
+#endif
+
+ if (!status.batch)
+ {
+ put_info("Welcome to the MariaDB monitor. Commands end with ; or \\g.",
+ INFO_INFO);
+ my_snprintf((char*) glob_buffer.ptr(), glob_buffer.alloced_length(),
+ "Your %s connection id is %lu\nServer version: %s\n",
+ mysql_get_server_name(&mysql),
+ mysql_thread_id(&mysql), server_version_string(&mysql));
+ put_info((char*) glob_buffer.ptr(),INFO_INFO);
+ put_info(ORACLE_WELCOME_COPYRIGHT_NOTICE("2000"), INFO_INFO);
+ }
+
+#ifdef HAVE_READLINE
+ initialize_readline();
+ if (!status.batch && !quick && !opt_html && !opt_xml)
+ {
+ /* read-history from file, default ~/.mysql_history*/
+ if (getenv("MYSQL_HISTFILE"))
+ histfile=my_strdup(PSI_NOT_INSTRUMENTED, getenv("MYSQL_HISTFILE"),MYF(MY_WME));
+ else if (getenv("HOME"))
+ {
+ histfile=(char*) my_malloc(PSI_NOT_INSTRUMENTED,
+ strlen(getenv("HOME")) + strlen("/.mysql_history")+2, MYF(MY_WME));
+ if (histfile)
+ sprintf(histfile,"%s/.mysql_history",getenv("HOME"));
+ char link_name[FN_REFLEN];
+ if (my_readlink(link_name, histfile, 0) == 0 &&
+ strncmp(link_name, "/dev/null", 10) == 0)
+ {
+ /* The .mysql_history file is a symlink to /dev/null, don't use it */
+ my_free(histfile);
+ histfile= 0;
+ }
+ }
+
+ /* We used to suggest setting MYSQL_HISTFILE=/dev/null. */
+ if (histfile && strncmp(histfile, "/dev/null", 10) == 0)
+ histfile= NULL;
+
+ if (histfile && histfile[0])
+ {
+ if (verbose)
+ tee_fprintf(stdout, "Reading history-file %s\n",histfile);
+ read_history(histfile);
+ if (!(histfile_tmp= (char*) my_malloc(PSI_NOT_INSTRUMENTED,
+ strlen(histfile) + 5, MYF(MY_WME))))
+ {
+ fprintf(stderr, "Couldn't allocate memory for temp histfile!\n");
+ exit(1);
+ }
+ sprintf(histfile_tmp, "%s.TMP", histfile);
+ }
+ }
+
+#endif
+
+ sprintf(buff, "%s",
+ "Type 'help;' or '\\h' for help. Type '\\c' to clear the current input statement.\n");
+ put_info(buff,INFO_INFO);
+ status.exit_status= read_and_execute(!status.batch);
+ if (opt_outfile)
+ end_tee();
+ mysql_end(0);
+#ifndef _lint
+ DBUG_RETURN(0); // Keep compiler happy
+#endif
+}
+
+sig_handler mysql_end(int sig)
+{
+#ifndef _WIN32
+ /*
+ Ignoring SIGQUIT and SIGINT signals when cleanup process starts.
+ This will help in resolving the double free issues, which occurs in case
+ the signal handler function is started in between the clean up function.
+ */
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGINT, SIG_IGN);
+#endif
+
+ mysql_close(&mysql);
+#ifdef HAVE_READLINE
+ if (!status.batch && !quick && !opt_html && !opt_xml &&
+ histfile && histfile[0])
+ {
+ /* write-history */
+ if (verbose)
+ tee_fprintf(stdout, "Writing history-file %s\n",histfile);
+ if (!write_history(histfile_tmp))
+ my_rename(histfile_tmp, histfile, MYF(MY_WME));
+ }
+ batch_readline_end(status.line_buff);
+ completion_hash_free(&ht);
+ free_root(&hash_mem_root,MYF(0));
+
+#endif
+ if (sig >= 0)
+ put_info(sig ? "Aborted" : "Bye", INFO_RESULT);
+ glob_buffer.free();
+ old_buffer.free();
+ processed_prompt.free();
+ my_free(server_version);
+ my_free(opt_password);
+ my_free(opt_mysql_unix_port);
+ my_free(histfile);
+ my_free(histfile_tmp);
+ my_free(current_db);
+ my_free(current_host);
+ my_free(current_user);
+ my_free(full_username);
+ my_free(part_username);
+ my_free(default_prompt);
+ my_free(current_prompt);
+ while (embedded_server_arg_count > 1)
+ my_free(embedded_server_args[--embedded_server_arg_count]);
+ mysql_server_end();
+ free_defaults(defaults_argv);
+ my_end(my_end_arg);
+ exit(status.exit_status);
+}
+
+#ifdef _WIN32
+#define CNV_BUFSIZE 1024
+
+/**
+ Convert user,database,and password to requested charset.
+
+ This is done in the single case when user connects with non-UTF8
+ default-character-set, on UTF8 capable Windows.
+
+ User, password, and database are UTF8 encoded, prior to the function,
+ this needs to be fixed, in case they contain non-ASCIIs.
+
+ Mostly a workaround, to allow existng users with non-ASCII password
+ to survive upgrade without losing connectivity.
+*/
+static void maybe_convert_charset(const char **user, const char **password,
+ const char **database, const char *csname)
+{
+ if (GetACP() != CP_UTF8 || !strncmp(csname, "utf8", 4))
+ return;
+ static char bufs[3][CNV_BUFSIZE];
+ const char **from[]= {user, password, database};
+ CHARSET_INFO *cs= get_charset_by_csname(csname, MY_CS_PRIMARY,
+ MYF(MY_UTF8_IS_UTF8MB3 | MY_WME));
+ if (!cs)
+ return;
+ for (int i= 0; i < 3; i++)
+ {
+ const char *str= *from[i];
+ if (!str)
+ continue;
+ uint errors;
+ uint len= my_convert(bufs[i], CNV_BUFSIZE, cs, str, (uint32) strlen(str),
+ &my_charset_utf8mb4_bin, &errors);
+ bufs[i][len]= 0;
+ *from[i]= bufs[i];
+ }
+}
+#endif
+
+/*
+ set connection-specific options and call mysql_real_connect
+*/
+static bool do_connect(MYSQL *mysql, const char *host, const char *user,
+ const char *password, const char *database, ulong flags)
+{
+ if (opt_secure_auth)
+ mysql_options(mysql, MYSQL_SECURE_AUTH, (char *) &opt_secure_auth);
+#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
+ if (opt_use_ssl && opt_protocol <= MYSQL_PROTOCOL_SOCKET)
+ {
+ mysql_ssl_set(mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca,
+ opt_ssl_capath, opt_ssl_cipher);
+ mysql_options(mysql, MYSQL_OPT_SSL_CRL, opt_ssl_crl);
+ mysql_options(mysql, MYSQL_OPT_SSL_CRLPATH, opt_ssl_crlpath);
+ mysql_options(mysql, MARIADB_OPT_TLS_VERSION, opt_tls_version);
+ }
+ mysql_options(mysql,MYSQL_OPT_SSL_VERIFY_SERVER_CERT,
+ (char*)&opt_ssl_verify_server_cert);
+#endif
+ if (opt_protocol)
+ mysql_options(mysql,MYSQL_OPT_PROTOCOL,(char*)&opt_protocol);
+ if (opt_plugin_dir && *opt_plugin_dir)
+ mysql_options(mysql, MYSQL_PLUGIN_DIR, opt_plugin_dir);
+
+ if (opt_default_auth && *opt_default_auth)
+ mysql_options(mysql, MYSQL_DEFAULT_AUTH, opt_default_auth);
+
+ mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_RESET, 0);
+ mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD,
+ "program_name", "mysql");
+#ifdef _WIN32
+ maybe_convert_charset(&user, &password, &database,default_charset);
+#endif
+
+ return mysql_real_connect(mysql, host, user, password, database,
+ opt_mysql_port, opt_mysql_unix_port, flags);
+}
+
+
+/*
+ This function handles sigint calls
+ If query is in process, kill query
+ If 'source' is executed, abort source command
+ no query in process, terminate like previous behavior
+ */
+
+sig_handler handle_sigint(int sig)
+{
+ char kill_buffer[40];
+ MYSQL *kill_mysql= NULL;
+
+ /* terminate if no query being executed, or we already tried interrupting */
+ if (!executing_query || (interrupted_query == 2))
+ {
+ tee_fprintf(stdout, "Ctrl-C -- exit!\n");
+ goto err;
+ }
+
+ kill_mysql= mysql_init(kill_mysql);
+ if (!do_connect(kill_mysql,current_host, current_user, opt_password, "", 0))
+ {
+ tee_fprintf(stdout, "Ctrl-C -- sorry, cannot connect to server to kill query, giving up ...\n");
+ goto err;
+ }
+
+ /* First time try to kill the query, second time the connection */
+ interrupted_query++;
+
+ /* mysqld < 5 does not understand KILL QUERY, skip to KILL CONNECTION */
+ if ((interrupted_query == 1) && (mysql_get_server_version(&mysql) < 50000))
+ interrupted_query= 2;
+
+ /* kill_buffer is always big enough because max length of %lu is 15 */
+ sprintf(kill_buffer, "KILL %s%lu",
+ (interrupted_query == 1) ? "QUERY " : "",
+ mysql_thread_id(&mysql));
+ if (verbose)
+ tee_fprintf(stdout, "Ctrl-C -- sending \"%s\" to server ...\n",
+ kill_buffer);
+ mysql_real_query(kill_mysql, kill_buffer, (uint) strlen(kill_buffer));
+ mysql_close(kill_mysql);
+ tee_fprintf(stdout, "Ctrl-C -- query killed. Continuing normally.\n");
+ if (in_com_source)
+ aborted= 1; // Abort source command
+ return;
+
+err:
+#ifdef _WIN32
+ /*
+ When SIGINT is raised on Windows, the OS creates a new thread to handle the
+ interrupt. Once that thread completes, the main thread continues running
+ only to find that it's resources have already been free'd when the sigint
+ handler called mysql_end().
+ */
+ mysql_thread_end();
+#else
+ mysql_end(sig);
+#endif
+}
+
+
+#if defined(HAVE_TERMIOS_H) && defined(GWINSZ_IN_SYS_IOCTL)
+sig_handler window_resize(int sig)
+{
+ struct winsize window_size;
+
+ if (ioctl(fileno(stdin), TIOCGWINSZ, &window_size) == 0)
+ if (window_size.ws_col > 0)
+ terminal_width= window_size.ws_col;
+}
+#endif
+
+static struct my_option my_long_options[] =
+{
+ {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0,
+ 0, 0, 0, 0, 0},
+ {"help", 'I', "Synonym for -?", 0, 0, 0, GET_NO_ARG, NO_ARG, 0,
+ 0, 0, 0, 0, 0},
+ {"abort-source-on-error", OPT_ABORT_SOURCE_ON_ERROR,
+ "Abort 'source filename' operations in case of errors",
+ &batch_abort_on_error, &batch_abort_on_error, 0,
+ GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"auto-rehash", OPT_AUTO_REHASH,
+ "Enable automatic rehashing. One doesn't need to use 'rehash' to get table "
+ "and field completion, but startup and reconnecting may take a longer time. "
+ "Disable with --disable-auto-rehash.",
+ &opt_rehash, &opt_rehash, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0,
+ 0, 0},
+ {"no-auto-rehash", 'A',
+ "No automatic rehashing. One has to use 'rehash' to get table and field "
+ "completion. This gives a quicker start of mysql and disables rehashing "
+ "on reconnect.",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"auto-vertical-output", OPT_AUTO_VERTICAL_OUTPUT,
+ "Automatically switch to vertical output mode if the result is wider "
+ "than the terminal width.",
+ &auto_vertical_output, &auto_vertical_output, 0, GET_BOOL, NO_ARG, 0,
+ 0, 0, 0, 0, 0},
+ {"batch", 'B',
+ "Don't use history file. Disable interactive behavior. (Enables --silent.)",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"binary-as-hex", 0, "Print binary data as hex", &opt_binhex, &opt_binhex,
+ 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"character-sets-dir", OPT_CHARSETS_DIR,
+ "Directory for character set files.", &charsets_dir,
+ &charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"column-type-info", OPT_COLUMN_TYPES, "Display column type information.",
+ &column_types_flag, &column_types_flag,
+ 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"comments", 'c', "Preserve comments. Send comments to the server."
+ " The default is --skip-comments (discard comments), enable with --comments.",
+ &preserve_comments, &preserve_comments,
+ 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"compress", 'C', "Use compression in server/client protocol.",
+ &opt_compress, &opt_compress, 0, GET_BOOL, NO_ARG, 0, 0, 0,
+ 0, 0, 0},
+#ifdef DBUG_OFF
+ {"debug", '#', "This is a non-debug version. Catch this and exit.",
+ 0,0, 0, GET_DISABLED, OPT_ARG, 0, 0, 0, 0, 0, 0},
+#else
+ {"debug", '#', "Output debug log.", &default_dbug_option,
+ &default_dbug_option, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
+#endif
+ {"debug-check", OPT_DEBUG_CHECK, "Check memory and open file usage at exit.",
+ &debug_check_flag, &debug_check_flag, 0,
+ GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"debug-info", 'T', "Print some debug info at exit.", &debug_info_flag,
+ &debug_info_flag, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"database", 'D', "Database to use.", &current_db,
+ &current_db, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"default-character-set", OPT_DEFAULT_CHARSET,
+ "Set the default character set.", &default_charset,
+ &default_charset, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"delimiter", OPT_DELIMITER, "Delimiter to be used.", &delimiter_str,
+ &delimiter_str, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"execute", 'e', "Execute command and quit. (Disables --force and history file.)", 0,
+ 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"enable-cleartext-plugin", OPT_COMPATIBILTY_CLEARTEXT_PLUGIN, "Obsolete option. Exists only for MySQL compatibility.",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"vertical", 'E', "Print the output of a query (rows) vertically.",
+ &vertical, &vertical, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0,
+ 0},
+ {"force", 'f', "Continue even if we get an SQL error. Sets abort-source-on-error to 0",
+ &ignore_errors, &ignore_errors, 0, GET_BOOL, NO_ARG, 0, 0,
+ 0, 0, 0, 0},
+ {"named-commands", 'G',
+ "Enable named commands. Named commands mean this program's internal "
+ "commands; see mysql> help . When enabled, the named commands can be "
+ "used from any line of the query, otherwise only from the first line, "
+ "before an enter. Disable with --disable-named-commands. This option "
+ "is disabled by default.",
+ &named_cmds, &named_cmds, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0,
+ 0, 0},
+ {"ignore-spaces", 'i', "Ignore space after function names.",
+ &ignore_spaces, &ignore_spaces, 0, GET_BOOL, NO_ARG, 0, 0,
+ 0, 0, 0, 0},
+ {"init-command", OPT_INIT_COMMAND,
+ "SQL Command to execute when connecting to MariaDB server. Will "
+ "automatically be re-executed when reconnecting.",
+ &opt_init_command, &opt_init_command, 0,
+ GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"local-infile", OPT_LOCAL_INFILE, "Enable/disable LOAD DATA LOCAL INFILE.",
+ &opt_local_infile, &opt_local_infile, 0, GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0},
+ {"no-beep", 'b', "Turn off beep on error.", &opt_nobeep,
+ &opt_nobeep, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"host", 'h', "Connect to host.", &current_host,
+ &current_host, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"html", 'H', "Produce HTML output.", &opt_html, &opt_html,
+ 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"xml", 'X', "Produce XML output.", &opt_xml, &opt_xml, 0,
+ GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"line-numbers", OPT_LINE_NUMBERS, "Write line numbers for errors.",
+ &line_numbers, &line_numbers, 0, GET_BOOL,
+ NO_ARG, 1, 0, 0, 0, 0, 0},
+ {"skip-line-numbers", 'L', "Don't write line number for errors.", 0, 0, 0, GET_NO_ARG,
+ NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"unbuffered", 'n', "Flush buffer after each query.", &unbuffered,
+ &unbuffered, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"column-names", OPT_COLUMN_NAMES, "Write column names in results.",
+ &column_names, &column_names, 0, GET_BOOL,
+ NO_ARG, 1, 0, 0, 0, 0, 0},
+ {"skip-column-names", 'N',
+ "Don't write column names in results.",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"sigint-ignore", OPT_SIGINT_IGNORE, "Ignore SIGINT (CTRL-C).",
+ &opt_sigint_ignore, &opt_sigint_ignore, 0, GET_BOOL,
+ NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"one-database", 'o',
+ "Ignore statements except those that occur while the default "
+ "database is the one named at the command line.",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+#ifdef USE_POPEN
+ {"pager", OPT_PAGER,
+ "Pager to use to display results. If you don't supply an option, the "
+ "default pager is taken from your ENV variable PAGER. Valid pagers are "
+ "less, more, cat [> filename], etc. See interactive help (\\h) also. "
+ "This option does not work in batch mode. Disable with --disable-pager. "
+ "This option is disabled by default.",
+ 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
+#endif
+ {"password", 'p',
+ "Password to use when connecting to server. If password is not given it's asked from the tty.",
+ 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
+#ifdef _WIN32
+ {"pipe", 'W', "Use named pipes to connect to server.", 0, 0, 0, GET_NO_ARG,
+ NO_ARG, 0, 0, 0, 0, 0, 0},
+#endif
+ {"port", 'P', "Port number to use for connection or 0 for default to, in "
+ "order of preference, my.cnf, $MYSQL_TCP_PORT, "
+#if MYSQL_PORT_DEFAULT == 0
+ "/etc/services, "
+#endif
+ "built-in default (" STRINGIFY_ARG(MYSQL_PORT) ").",
+ &opt_mysql_port,
+ &opt_mysql_port, 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"progress-reports", OPT_REPORT_PROGRESS,
+ "Get progress reports for long running commands (like ALTER TABLE)",
+ &opt_progress_reports, &opt_progress_reports, 0, GET_BOOL, NO_ARG, 1, 0,
+ 0, 0, 0, 0},
+ {"prompt", OPT_PROMPT, "Set the command line prompt to this value.",
+ &current_prompt, &current_prompt, 0, GET_STR_ALLOC,
+ REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"protocol", OPT_MYSQL_PROTOCOL, "The protocol to use for connection (tcp, socket, pipe).",
+ &opt_protocol_type, &opt_protocol_type, 0, GET_STR, REQUIRED_ARG,
+ 0, 0, 0, 0, 0, 0},
+ {"quick", 'q',
+ "Don't cache result, print it row by row. This may slow down the server "
+ "if the output is suspended. Doesn't use history file.",
+ &quick, &quick, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"raw", 'r', "Write fields without conversion. Used with --batch.",
+ &opt_raw_data, &opt_raw_data, 0, GET_BOOL, NO_ARG, 0, 0, 0,
+ 0, 0, 0},
+ {"reconnect", OPT_RECONNECT, "Reconnect if the connection is lost. Disable "
+ "with --disable-reconnect. This option is enabled by default.",
+ &opt_reconnect, &opt_reconnect, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
+ {"silent", 's', "Be more silent. Print results with a tab as separator, "
+ "each row on new line.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"socket", 'S', "The socket file to use for connection.",
+ &opt_mysql_unix_port, &opt_mysql_unix_port, 0, GET_STR_ALLOC,
+ REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+#include "sslopt-longopts.h"
+ {"table", 't', "Output in table format.", &output_tables,
+ &output_tables, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"tee", OPT_TEE,
+ "Append everything into outfile. See interactive help (\\h) also. "
+ "Does not work in batch mode. Disable with --disable-tee. "
+ "This option is disabled by default.",
+ 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+#ifndef DONT_ALLOW_USER_CHANGE
+ {"user", 'u', "User for login if not current user.", &current_user,
+ &current_user, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+#endif
+ {"safe-updates", 'U', "Only allow UPDATE and DELETE that uses keys.",
+ &safe_updates, &safe_updates, 0, GET_BOOL, NO_ARG, 0, 0,
+ 0, 0, 0, 0},
+ {"i-am-a-dummy", 'U', "Synonym for option --safe-updates, -U.",
+ &safe_updates, &safe_updates, 0, GET_BOOL, NO_ARG, 0, 0,
+ 0, 0, 0, 0},
+ {"verbose", 'v', "Write more. (-v -v -v gives the table output format).", 0,
+ 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"version", 'V', "Output version information and exit.", 0, 0, 0,
+ GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"wait", 'w', "Wait and retry if connection is down.", 0, 0, 0, GET_NO_ARG,
+ NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"connect_timeout", OPT_CONNECT_TIMEOUT,
+ "Number of seconds before connection timeout.",
+ &opt_connect_timeout, &opt_connect_timeout, 0, GET_ULONG, REQUIRED_ARG,
+ 0, 0, 3600*12, 0, 0, 0},
+ {"max_allowed_packet", OPT_MAX_ALLOWED_PACKET,
+ "The maximum packet length to send to or receive from server.",
+ &opt_max_allowed_packet, &opt_max_allowed_packet, 0,
+ GET_ULONG, REQUIRED_ARG, 16 *1024L*1024L, 4096,
+ (longlong) 2*1024L*1024L*1024L, MALLOC_OVERHEAD, 1024, 0},
+ {"net_buffer_length", OPT_NET_BUFFER_LENGTH,
+ "The buffer size for TCP/IP and socket communication.",
+ &opt_net_buffer_length, &opt_net_buffer_length, 0, GET_ULONG,
+ REQUIRED_ARG, 16384, 1024, 512*1024*1024L, MALLOC_OVERHEAD, 1024, 0},
+ {"select_limit", OPT_SELECT_LIMIT,
+ "Automatic limit for SELECT when using --safe-updates.",
+ &select_limit, &select_limit, 0, GET_ULONG, REQUIRED_ARG, 1000L,
+ 1, ULONG_MAX, 0, 1, 0},
+ {"max_join_size", OPT_MAX_JOIN_SIZE,
+ "Automatic limit for rows in a join when using --safe-updates.",
+ &max_join_size, &max_join_size, 0, GET_ULONG, REQUIRED_ARG, 1000000L,
+ 1, ULONG_MAX, 0, 1, 0},
+ {"secure-auth", OPT_SECURE_AUTH, "Refuse client connecting to server if it"
+ " uses old (pre-4.1.1) protocol.", &opt_secure_auth,
+ &opt_secure_auth, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"server-arg", OPT_SERVER_ARG, "Send embedded server this as a parameter.",
+ 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"show-warnings", OPT_SHOW_WARNINGS, "Show warnings after every statement.",
+ &show_warnings, &show_warnings, 0, GET_BOOL, NO_ARG,
+ 0, 0, 0, 0, 0, 0},
+ {"plugin_dir", OPT_PLUGIN_DIR, "Directory for client-side plugins.",
+ &opt_plugin_dir, &opt_plugin_dir, 0,
+ GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"default_auth", OPT_DEFAULT_AUTH,
+ "Default authentication client-side plugin to use.",
+ &opt_default_auth, &opt_default_auth, 0,
+ GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"binary-mode", 0,
+ "Binary mode allows certain character sequences to be processed as data "
+ "that would otherwise be treated with a special meaning by the parser. "
+ "Specifically, this switch turns off parsing of all client commands except "
+ "\\C and DELIMITER in non-interactive mode (i.e., when binary mode is "
+ "combined with either 1) piped input, 2) the --batch mysql option, or 3) "
+ "the 'source' command). Also, in binary mode, occurrences of '\\r\\n' and "
+ "ASCII '\\0' are preserved within strings, whereas by default, '\\r\\n' is "
+ "translated to '\\n' and '\\0' is disallowed in user input.",
+ &opt_binary_mode, &opt_binary_mode, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"connect-expired-password", 0,
+ "Notify the server that this client is prepared to handle expired "
+ "password sandbox mode even if --batch was specified.",
+ &opt_connect_expired_password, &opt_connect_expired_password, 0, GET_BOOL,
+ NO_ARG, 0, 0, 0, 0, 0, 0},
+ { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
+};
+
+
+static void usage(int version)
+{
+#ifdef HAVE_READLINE
+#if defined(USE_LIBEDIT_INTERFACE)
+ const char* readline= "";
+#else
+ const char* readline= "readline";
+#endif
+ printf("%s Ver %s Distrib %s, for %s (%s) using %s %s\n",
+ my_progname, VER, MYSQL_SERVER_VERSION, SYSTEM_TYPE, MACHINE_TYPE,
+ readline, rl_library_version);
+#else
+ printf("%s Ver %s Distrib %s, for %s (%s), source revision %s\n", my_progname, VER,
+ MYSQL_SERVER_VERSION, SYSTEM_TYPE, MACHINE_TYPE,SOURCE_REVISION);
+#endif
+
+ if (version)
+ return;
+ puts(ORACLE_WELCOME_COPYRIGHT_NOTICE("2000"));
+ printf("Usage: %s [OPTIONS] [database]\n", my_progname);
+ print_defaults("my", load_default_groups);
+ puts("");
+ my_print_help(my_long_options);
+ my_print_variables(my_long_options);
+}
+
+
+my_bool
+get_one_option(const struct my_option *opt, const char *argument,
+ const char *filename)
+{
+ switch(opt->id) {
+ case OPT_CHARSETS_DIR:
+ strmake_buf(mysql_charsets_dir, argument);
+ charsets_dir = mysql_charsets_dir;
+ break;
+ case OPT_DELIMITER:
+ if (argument == disabled_my_option)
+ {
+ strmov(delimiter, DEFAULT_DELIMITER);
+ }
+ else
+ {
+ /* Check that delimiter does not contain a backslash */
+ if (!strstr(argument, "\\"))
+ {
+ strmake_buf(delimiter, argument);
+ }
+ else
+ {
+ put_info("DELIMITER cannot contain a backslash character", INFO_ERROR);
+ return 0;
+ }
+ }
+ delimiter_length= (uint)strlen(delimiter);
+ delimiter_str= delimiter;
+ break;
+ case OPT_LOCAL_INFILE:
+ using_opt_local_infile=1;
+ break;
+ case OPT_TEE:
+ if (argument == disabled_my_option)
+ {
+ if (opt_outfile)
+ end_tee();
+ }
+ else
+ init_tee(argument);
+ break;
+ case OPT_PAGER:
+ if (argument == disabled_my_option)
+ opt_nopager= 1;
+ else
+ {
+ opt_nopager= 0;
+ if (argument && strlen(argument))
+ {
+ default_pager_set= 1;
+ strmake_buf(pager, argument);
+ strmov(default_pager, pager);
+ }
+ else if (default_pager_set)
+ strmov(pager, default_pager);
+ else
+ opt_nopager= 1;
+ }
+ break;
+ case OPT_MYSQL_PROTOCOL:
+#ifndef EMBEDDED_LIBRARY
+ if (!argument[0])
+ opt_protocol= 0;
+ else if ((opt_protocol=
+ find_type_with_warning(argument, &sql_protocol_typelib,
+ opt->name)) <= 0)
+ exit(1);
+#endif
+ break;
+ case OPT_SERVER_ARG:
+#ifdef EMBEDDED_LIBRARY
+ /*
+ When the embedded server is being tested, the client needs to be
+ able to pass command-line arguments to the embedded server so it can
+ locate the language files and data directory.
+ */
+ if (!embedded_server_arg_count)
+ {
+ embedded_server_arg_count= 1;
+ embedded_server_args[0]= (char*) "";
+ }
+ if (embedded_server_arg_count == MAX_SERVER_ARGS-1 ||
+ !(embedded_server_args[embedded_server_arg_count++]=
+ my_strdup(PSI_NOT_INSTRUMENTED, argument, MYF(MY_FAE))))
+ {
+ put_info("Can't use server argument", INFO_ERROR);
+ return 0;
+ }
+#else /*EMBEDDED_LIBRARY */
+ printf("WARNING: --server-arg option not supported in this configuration.\n");
+#endif
+ break;
+ case OPT_COMPATIBILTY_CLEARTEXT_PLUGIN:
+ /*
+ This option exists in MySQL client but not in MariaDB. Users switching from
+ MySQL might still have this option in their commands, and it will not work
+ in MariaDB unless it is handled. Therefore output a warning and continue.
+ */
+ printf("WARNING: option '--enable-cleartext-plugin' is obsolete.\n");
+ break;
+ case 'A':
+ opt_rehash= 0;
+ break;
+ case 'N':
+ column_names= 0;
+ break;
+ case 'e':
+ status.batch= 1;
+ status.add_to_history= 0;
+ if (!status.line_buff)
+ ignore_errors= 0; // do it for the first -e only
+ if (!(status.line_buff= batch_readline_command(status.line_buff,
+ (char*) argument)))
+ return 1;
+ break;
+ case 'o':
+ if (argument == disabled_my_option)
+ one_database= 0;
+ else
+ one_database= skip_updates= 1;
+ break;
+ case 'p':
+ if (argument == disabled_my_option)
+ argument= (char*) ""; // Don't require password
+ if (argument)
+ {
+ /*
+ One should not really change the argument, but we make an
+ exception for passwords
+ */
+ char *start= (char*) argument;
+ my_free(opt_password);
+ opt_password= my_strdup(PSI_NOT_INSTRUMENTED, argument, MYF(MY_FAE));
+ while (*argument)
+ *(char*)argument++= 'x'; // Destroy argument
+ if (*start)
+ start[1]=0 ;
+ tty_password= 0;
+ }
+ else
+ tty_password= 1;
+ break;
+ case '#':
+ DBUG_PUSH(argument ? argument : default_dbug_option);
+ debug_info_flag= 1;
+ break;
+ case 's':
+ if (argument == disabled_my_option)
+ opt_silent= 0;
+ else
+ opt_silent++;
+ break;
+ case 'v':
+ if (argument == disabled_my_option)
+ verbose= 0;
+ else
+ verbose++;
+ break;
+ case 'B':
+ status.batch= 1;
+ status.add_to_history= 0;
+ set_if_bigger(opt_silent,1); // more silent
+ break;
+ case 'W':
+#ifdef _WIN32
+ opt_protocol = MYSQL_PROTOCOL_PIPE;
+ opt_protocol_type= "pipe";
+#endif
+ break;
+#include <sslopt-case.h>
+ case 'f':
+ batch_abort_on_error= 0;
+ break;
+ case 'V':
+ usage(1);
+ status.exit_status= 0;
+ mysql_end(-1);
+ break;
+ case 'P':
+ if (filename[0] == '\0')
+ {
+ /* Port given on command line, switch protocol to use TCP */
+ opt_protocol= MYSQL_PROTOCOL_TCP;
+ }
+ break;
+ case 'S':
+ if (filename[0] == '\0')
+ {
+ /*
+ Socket given on command line, switch protocol to use SOCKETSt
+ Except on Windows if 'protocol= pipe' has been provided in
+ the config file or command line.
+ */
+ if (opt_protocol != MYSQL_PROTOCOL_PIPE)
+ {
+ opt_protocol= MYSQL_PROTOCOL_SOCKET;
+ }
+ }
+ break;
+ case 'I':
+ case '?':
+ usage(0);
+ status.exit_status= 0;
+ mysql_end(-1);
+ }
+ return 0;
+}
+
+
+static int get_options(int argc, char **argv)
+{
+ char *tmp, *pagpoint;
+ int ho_error;
+ MYSQL_PARAMETERS *mysql_params= mysql_get_parameters();
+
+ tmp= (char *) getenv("MYSQL_HOST");
+ if (tmp)
+ current_host= my_strdup(PSI_NOT_INSTRUMENTED, tmp, MYF(MY_WME));
+
+ pagpoint= getenv("PAGER");
+ if (!((char*) (pagpoint)))
+ {
+ strmov(pager, "stdout");
+ opt_nopager= 1;
+ }
+ else
+ strmov(pager, pagpoint);
+ strmov(default_pager, pager);
+
+ opt_max_allowed_packet= *mysql_params->p_max_allowed_packet;
+ opt_net_buffer_length= *mysql_params->p_net_buffer_length;
+
+ if ((ho_error=handle_options(&argc, &argv, my_long_options, get_one_option)))
+ return(ho_error);
+
+ *mysql_params->p_max_allowed_packet= opt_max_allowed_packet;
+ *mysql_params->p_net_buffer_length= opt_net_buffer_length;
+
+ if (status.batch) /* disable pager and outfile in this case */
+ {
+ strmov(default_pager, "stdout");
+ strmov(pager, "stdout");
+ opt_nopager= 1;
+ default_pager_set= 0;
+ opt_outfile= 0;
+ opt_reconnect= 0;
+ connect_flag= 0; /* Not in interactive mode */
+ opt_progress_reports= 0;
+ }
+
+ if (argc > 1)
+ {
+ usage(0);
+ exit(1);
+ }
+ if (argc == 1)
+ {
+ skip_updates= 0;
+ my_free(current_db);
+ current_db= my_strdup(PSI_NOT_INSTRUMENTED, *argv, MYF(MY_WME));
+ }
+ if (tty_password)
+ opt_password= my_get_tty_password(NullS);
+ if (debug_info_flag)
+ my_end_arg= MY_CHECK_ERROR | MY_GIVE_INFO;
+ if (debug_check_flag)
+ my_end_arg= MY_CHECK_ERROR;
+
+ if (ignore_spaces)
+ connect_flag|= CLIENT_IGNORE_SPACE;
+
+ if (opt_progress_reports)
+ connect_flag|= CLIENT_PROGRESS_OBSOLETE;
+
+ return(0);
+}
+
+static int read_and_execute(bool interactive)
+{
+ char *line= NULL;
+ char in_string=0;
+ ulong line_number=0;
+ bool ml_comment= 0;
+ COMMANDS *com;
+ size_t line_length= 0;
+ status.exit_status=1;
+
+ real_binary_mode= !interactive && opt_binary_mode;
+ while (!aborted)
+ {
+ if (!interactive)
+ {
+ /*
+ batch_readline can return 0 on EOF or error.
+ In that case, we need to double check that we have a valid
+ line before actually setting line_length to read_length.
+ */
+ line= batch_readline(status.line_buff, real_binary_mode);
+ if (line)
+ {
+ line_length= status.line_buff->read_length;
+
+ /*
+ ASCII 0x00 is not allowed appearing in queries if it is not in binary
+ mode.
+ */
+ if (!real_binary_mode && strlen(line) != line_length)
+ {
+ status.exit_status= 1;
+ String msg;
+ msg.append(STRING_WITH_LEN(
+ "ASCII '\\0' appeared in the statement, but this is not "
+ "allowed unless option --binary-mode is enabled and mysql is "
+ "run in non-interactive mode. Set --binary-mode to 1 if ASCII "
+ "'\\0' is expected. Query: '"));
+ msg.append(glob_buffer);
+ msg.append(line, strlen(line));
+ msg.append(STRING_WITH_LEN("'."));
+ put_info(msg.c_ptr(), INFO_ERROR);
+ break;
+ }
+
+ /*
+ Skip UTF8 Byte Order Marker (BOM) 0xEFBBBF.
+ Editors like "notepad" put this marker in
+ the very beginning of a text file when
+ you save the file using "Unicode UTF-8" format.
+ */
+ if (!line_number &&
+ (uchar) line[0] == 0xEF &&
+ (uchar) line[1] == 0xBB &&
+ (uchar) line[2] == 0xBF)
+ {
+ line+= 3;
+ // decrease the line length accordingly to the 3 bytes chopped
+ line_length -=3;
+ }
+ }
+ line_number++;
+ if (!glob_buffer.length())
+ status.query_start_line=line_number;
+ }
+ else
+ {
+ char *prompt= (char*) (ml_comment ? " /*> " :
+ glob_buffer.is_empty() ? construct_prompt() :
+ !in_string ? " -> " :
+ in_string == '\'' ?
+ " '> " : (in_string == '`' ?
+ " `> " :
+ " \"> "));
+ if (opt_outfile && glob_buffer.is_empty())
+ fflush(OUTFILE);
+
+#if defined(_WIN32)
+ tee_fputs(prompt, stdout);
+ line= win_readline();
+#else
+ if (opt_outfile)
+ fputs(prompt, OUTFILE);
+ /*
+ free the previous entered line.
+ Note: my_free() cannot be used here as the memory was allocated under
+ the readline/libedit library.
+ */
+ if (line)
+ free(line);
+ line= readline(prompt);
+#endif /* defined(_WIN32) */
+
+ /*
+ When Ctrl+d or Ctrl+z is pressed, the line may be NULL on some OS
+ which may cause coredump.
+ */
+ if (opt_outfile && line)
+ fprintf(OUTFILE, "%s\n", line);
+
+ line_length= line ? strlen(line) : 0;
+ }
+ // End of file or system error
+ if (!line)
+ {
+ if (status.line_buff && status.line_buff->error)
+ status.exit_status= 1;
+ else
+ status.exit_status= 0;
+ break;
+ }
+
+ /*
+ Check if line is a mysql command line
+ (We want to allow help, print and clear anywhere at line start
+ */
+ if ((named_cmds || glob_buffer.is_empty())
+ && !ml_comment && !in_string && (com= find_command(line)))
+ {
+ if ((*com->func)(&glob_buffer,line) > 0)
+ break;
+ if (glob_buffer.is_empty()) // If buffer was emptied
+ in_string=0;
+#ifdef HAVE_READLINE
+ if (interactive && status.add_to_history && not_in_history(line))
+ add_history(line);
+#endif
+ continue;
+ }
+ if (add_line(glob_buffer, line, line_length, &in_string, &ml_comment,
+ status.line_buff ? status.line_buff->truncated : 0))
+ break;
+ }
+ /* if in batch mode, send last query even if it doesn't end with \g or go */
+
+ if (!interactive && !status.exit_status)
+ {
+ remove_cntrl(glob_buffer);
+ if (!glob_buffer.is_empty())
+ {
+ status.exit_status=1;
+ if (com_go(&glob_buffer,line) <= 0)
+ status.exit_status=0;
+ }
+ }
+
+#if !defined(_WIN32)
+ if (interactive)
+ /*
+ free the last entered line.
+ Note: my_free() cannot be used here as the memory was allocated under
+ the readline/libedit library.
+ */
+ free(line);
+#endif
+
+ /*
+ If the function is called by 'source' command, it will return to interactive
+ mode, so real_binary_mode should be FALSE. Otherwise, it will exit the
+ program, it is safe to set real_binary_mode to FALSE.
+ */
+ real_binary_mode= FALSE;
+
+ return status.exit_status;
+}
+
+
+/**
+ It checks if the input is a short form command. It returns the command's
+ pointer if a command is found, else return NULL. Note that if binary-mode
+ is set, then only \C is searched for.
+
+ @param cmd_char A character of one byte.
+
+ @return
+ the command's pointer or NULL.
+*/
+static COMMANDS *find_command(char cmd_char)
+{
+ DBUG_ENTER("find_command");
+ DBUG_PRINT("enter", ("cmd_char: %d", cmd_char));
+
+ int index= -1;
+
+ /*
+ In binary-mode, we disallow all mysql commands except '\C'
+ and DELIMITER.
+ */
+ if (real_binary_mode)
+ {
+ if (cmd_char == 'C')
+ index= charset_index;
+ }
+ else
+ index= get_command_index(cmd_char);
+
+ if (index >= 0)
+ {
+ DBUG_PRINT("exit",("found command: %s", commands[index].name));
+ DBUG_RETURN(&commands[index]);
+ }
+ else
+ DBUG_RETURN((COMMANDS *) 0);
+}
+
+/**
+ It checks if the input is a long form command. It returns the command's
+ pointer if a command is found, else return NULL. Note that if binary-mode
+ is set, then only DELIMITER is searched for.
+
+ @param name A string.
+ @return
+ the command's pointer or NULL.
+*/
+static COMMANDS *find_command(char *name)
+{
+ uint len;
+ char *end;
+ DBUG_ENTER("find_command");
+
+ DBUG_ASSERT(name != NULL);
+ DBUG_PRINT("enter", ("name: '%s'", name));
+
+ while (my_isspace(charset_info, *name))
+ name++;
+ /*
+ If there is an \\g in the row or if the row has a delimiter but
+ this is not a delimiter command, let add_line() take care of
+ parsing the row and calling find_command().
+ */
+ if ((!real_binary_mode && strstr(name, "\\g")) ||
+ (strstr(name, delimiter) &&
+ !is_delimiter_command(name, DELIMITER_NAME_LEN)))
+ DBUG_RETURN((COMMANDS *) 0);
+
+ if ((end=strcont(name, " \t")))
+ {
+ len=(uint) (end - name);
+ while (my_isspace(charset_info, *end))
+ end++;
+ if (!*end)
+ end= 0; // no arguments to function
+ }
+ else
+ len= (uint) strlen(name);
+
+ int index= -1;
+ if (real_binary_mode)
+ {
+ if (is_delimiter_command(name, len))
+ index= delimiter_index;
+ }
+ else
+ {
+ /*
+ All commands are in the first part of commands array and have a function
+ to implement it.
+ */
+ for (uint i= 0; commands[i].func; i++)
+ {
+ if (!my_charset_latin1.strnncoll((uchar*) name, len,
+ (uchar*) commands[i].name, len) &&
+ (commands[i].name[len] == '\0') &&
+ (!end || (commands[i].takes_params && get_arg(name, CHECK))))
+ {
+ index= i;
+ break;
+ }
+ }
+ }
+
+ if (index >= 0)
+ {
+ DBUG_PRINT("exit", ("found command: %s", commands[index].name));
+ DBUG_RETURN(&commands[index]);
+ }
+ DBUG_RETURN((COMMANDS *) 0);
+}
+
+
+static bool add_line(String &buffer, char *line, size_t line_length,
+ char *in_string, bool *ml_comment, bool truncated)
+{
+ uchar inchar;
+ char buff[80], *pos, *out;
+ COMMANDS *com;
+ bool need_space= 0;
+ bool ss_comment= 0;
+ DBUG_ENTER("add_line");
+
+ if (!line[0] && buffer.is_empty())
+ DBUG_RETURN(0);
+#ifdef HAVE_READLINE
+ if (status.add_to_history && line[0] && not_in_history(line))
+ add_history(line);
+#endif
+ char *end_of_line= line + line_length;
+
+ for (pos= out= line; pos < end_of_line; pos++)
+ {
+ inchar= (uchar) *pos;
+ if (!preserve_comments)
+ {
+ // Skip spaces at the beginning of a statement
+ if (my_isspace(charset_info,inchar) && (out == line) &&
+ buffer.is_empty())
+ continue;
+ }
+
+#ifdef USE_MB
+ // Accept multi-byte characters as-is
+ int length;
+ if (charset_info->use_mb() &&
+ (length= my_ismbchar(charset_info, pos, end_of_line)))
+ {
+ if (!*ml_comment || preserve_comments)
+ {
+ while (length--)
+ *out++ = *pos++;
+ pos--;
+ }
+ else
+ pos+= length - 1;
+ continue;
+ }
+#endif
+ if (!*ml_comment && inchar == '\\' && *in_string != '`' &&
+ !(*in_string == '"' &&
+ (mysql.server_status & SERVER_STATUS_ANSI_QUOTES)) &&
+ !(*in_string &&
+ (mysql.server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES)))
+ {
+ // Found possible one character command like \c
+
+ /*
+ The null-terminating character (ASCII '\0') marks the end of user
+ input. Then, by default, upon encountering a '\0' while parsing, it
+ should stop. However, some data naturally contains binary zeros
+ (e.g., zipped files). Real_binary_mode signals the parser to expect
+ '\0' within the data and not to end parsing if found.
+ */
+ if (!(inchar = (uchar) *++pos) && (!real_binary_mode || !*in_string))
+ break; // readline adds one '\'
+ if (*in_string || inchar == 'N') // \N is short for NULL
+ { // Don't allow commands in string
+ *out++='\\';
+ *out++= (char) inchar;
+ continue;
+ }
+ if ((com= find_command((char) inchar)))
+ {
+ // Flush previously accepted characters
+ if (out != line)
+ {
+ buffer.append(line, (uint) (out-line));
+ out= line;
+ }
+
+ if ((*com->func)(&buffer,pos-1) > 0)
+ DBUG_RETURN(1); // Quit
+ if (com->takes_params)
+ {
+ if (ss_comment)
+ {
+ /*
+ If a client-side macro appears inside a server-side comment,
+ discard all characters in the comment after the macro (that is,
+ until the end of the comment rather than the next delimiter)
+ */
+ for (pos++; *pos && (*pos != '*' || *(pos + 1) != '/'); pos++)
+ ;
+ pos--;
+ }
+ else
+ {
+ for (pos++ ;
+ *pos && (*pos != *delimiter ||
+ !is_prefix(pos + 1, delimiter + 1)) ; pos++)
+ ; // Remove parameters
+ if (!*pos)
+ pos--;
+ else
+ pos+= delimiter_length - 1; // Point at last delim char
+ }
+ }
+ }
+ else
+ {
+ sprintf(buff,"Unknown command '\\%c'.",inchar);
+ if (put_info(buff,INFO_ERROR) > 0)
+ DBUG_RETURN(1);
+ *out++='\\';
+ *out++=(char) inchar;
+ continue;
+ }
+ }
+ else if (!*ml_comment && !*in_string && is_prefix(pos, delimiter))
+ {
+ // Found a statement. Continue parsing after the delimiter
+ pos+= delimiter_length;
+
+ if (preserve_comments)
+ {
+ while (my_isspace(charset_info, *pos))
+ *out++= *pos++;
+ }
+ // Flush previously accepted characters
+ if (out != line)
+ {
+ buffer.append(line, (uint32) (out-line));
+ out= line;
+ }
+
+ if (preserve_comments && ((*pos == '#') ||
+ ((*pos == '-') &&
+ (pos[1] == '-') &&
+ my_isspace(charset_info, pos[2]))))
+ {
+ // Add trailing single line comments to this statement
+ size_t length= strlen(pos);
+ buffer.append(pos, length);
+ pos+= length;
+ }
+
+ pos--;
+
+ if ((com= find_command(buffer.c_ptr())))
+ {
+
+ if ((*com->func)(&buffer, buffer.c_ptr()) > 0)
+ DBUG_RETURN(1); // Quit
+ }
+ else
+ {
+ if (com_go(&buffer, 0) > 0) // < 0 is not fatal
+ DBUG_RETURN(1);
+ }
+ buffer.length(0);
+ }
+ else if (!*ml_comment &&
+ (!*in_string &&
+ (inchar == '#' ||
+ (inchar == '-' && pos[1] == '-' &&
+ /*
+ The third byte is either whitespace or is the end of
+ the line -- which would occur only because of the
+ user sending newline -- which is itself whitespace
+ and should also match.
+ We also ignore lines starting with '--', even if there
+ isn't a whitespace after. (This makes it easier to run
+ mysql-test-run cases through the client)
+ */
+ ((my_isspace(charset_info,pos[2]) || !pos[2]) ||
+ (buffer.is_empty() && out == line))))))
+ {
+ // Flush previously accepted characters
+ if (out != line)
+ {
+ buffer.append(line, (uint32) (out - line));
+ out= line;
+ }
+
+ // comment to end of line
+ if (preserve_comments)
+ {
+ bool started_with_nothing= !buffer.length();
+
+ buffer.append(pos, strlen(pos));
+
+ /*
+ A single-line comment by itself gets sent immediately so that
+ client commands (delimiter, status, etc) will be interpreted on
+ the next line.
+ */
+ if (started_with_nothing)
+ {
+ if (com_go(&buffer, 0) > 0) // < 0 is not fatal
+ DBUG_RETURN(1);
+ buffer.length(0);
+ }
+ }
+
+ break;
+ }
+ else if (!*in_string && inchar == '/' && *(pos+1) == '*' &&
+ !(*(pos+2) == '!' || (*(pos+2) == 'M' && *(pos+3) == '!')))
+ {
+ if (preserve_comments)
+ {
+ *out++= *pos++; // copy '/'
+ *out++= *pos; // copy '*'
+ }
+ else
+ pos++;
+ *ml_comment= 1;
+ if (out != line)
+ {
+ buffer.append(line,(uint) (out-line));
+ out=line;
+ }
+ }
+ else if (*ml_comment && !ss_comment && inchar == '*' && *(pos + 1) == '/')
+ {
+ if (preserve_comments)
+ {
+ *out++= *pos++; // copy '*'
+ *out++= *pos; // copy '/'
+ }
+ else
+ pos++;
+ *ml_comment= 0;
+ if (out != line)
+ {
+ buffer.append(line, (uint32) (out - line));
+ out= line;
+ }
+ // Consumed a 2 chars or more, and will add 1 at most,
+ // so using the 'line' buffer to edit data in place is ok.
+ need_space= 1;
+ }
+ else
+ { // Add found char to buffer
+ if (!*in_string && inchar == '/' && *(pos + 1) == '*' &&
+ *(pos + 2) == '!')
+ ss_comment= 1;
+ else if (!*in_string && ss_comment && inchar == '*' && *(pos + 1) == '/')
+ ss_comment= 0;
+ if (inchar == *in_string)
+ *in_string= 0;
+ else if (!*ml_comment && !*in_string &&
+ (inchar == '\'' || inchar == '"' || inchar == '`'))
+ *in_string= (char) inchar;
+ if (!*ml_comment || preserve_comments)
+ {
+ if (need_space && !my_isspace(charset_info, (char)inchar))
+ *out++= ' ';
+ need_space= 0;
+ *out++= (char) inchar;
+ }
+ }
+ }
+ if (out != line || !buffer.is_empty())
+ {
+ uint length=(uint) (out-line);
+
+ if (!truncated && (!is_delimiter_command(line, length) ||
+ (*in_string || *ml_comment)))
+ {
+ /*
+ Don't add a new line in case there's a DELIMITER command to be
+ added to the glob buffer (e.g. on processing a line like
+ "<command>;DELIMITER <non-eof>") : similar to how a new line is
+ not added in the case when the DELIMITER is the first command
+ entered with an empty glob buffer. However, if the delimiter is
+ part of a string or a comment, the new line should be added. (e.g.
+ SELECT '\ndelimiter\n';\n)
+ */
+ *out++='\n';
+ length++;
+ }
+ if (buffer.length() + length >= buffer.alloced_length())
+ buffer.realloc(buffer.length()+length+IO_SIZE);
+ if ((!*ml_comment || preserve_comments) && buffer.append(line, length))
+ DBUG_RETURN(1);
+ }
+ DBUG_RETURN(0);
+}
+
+/*****************************************************************
+ Interface to Readline Completion
+******************************************************************/
+
+#ifdef HAVE_READLINE
+
+C_MODE_START
+static char *new_command_generator(const char *text, int);
+static char **new_mysql_completion(const char *text, int start, int end);
+C_MODE_END
+
+/*
+ Tell the GNU Readline library how to complete. We want to try to complete
+ on command names if this is the first word in the line, or on filenames
+ if not.
+*/
+
+#if defined(USE_NEW_READLINE_INTERFACE)
+static int fake_magic_space(int, int);
+extern "C" char *no_completion(const char*,int)
+#elif defined(USE_LIBEDIT_INTERFACE)
+static int fake_magic_space(const char *, int);
+extern "C" int no_completion(const char*,int)
+#else
+extern "C" char *no_completion()
+#endif
+{
+ return 0; /* No filename completion */
+}
+
+/* glues pieces of history back together if in pieces */
+static void fix_history(String *final_command)
+{
+ int total_lines = 1;
+ char *ptr = final_command->c_ptr();
+ String fixed_buffer; /* Converted buffer */
+ char str_char = '\0'; /* Character if we are in a string or not */
+
+ /* find out how many lines we have and remove newlines */
+ while (*ptr != '\0')
+ {
+ switch (*ptr) {
+ /* string character */
+ case '"':
+ case '\'':
+ case '`':
+ if (str_char == '\0') /* open string */
+ str_char = *ptr;
+ else if (str_char == *ptr) /* close string */
+ str_char = '\0';
+ fixed_buffer.append(ptr,1);
+ break;
+ case '\n':
+ /*
+ not in string, change to space
+ if in string, leave it alone
+ */
+ fixed_buffer.append(str_char == '\0' ? ' ' : '\n');
+ total_lines++;
+ break;
+ case '\\':
+ fixed_buffer.append('\\');
+ /* need to see if the backslash is escaping anything */
+ if (str_char)
+ {
+ ptr++;
+ /* special characters that need escaping */
+ if (*ptr == '\'' || *ptr == '"' || *ptr == '\\')
+ fixed_buffer.append(ptr,1);
+ else
+ ptr--;
+ }
+ break;
+
+ default:
+ fixed_buffer.append(ptr,1);
+ }
+ ptr++;
+ }
+ if (total_lines > 1)
+ add_history(fixed_buffer.ptr());
+}
+
+/*
+ returns 0 if line matches the previous history entry
+ returns 1 if the line doesn't match the previous history entry
+*/
+static int not_in_history(const char *line)
+{
+ HIST_ENTRY *oldhist = history_get(history_length);
+
+ if (oldhist == 0)
+ return 1;
+ if (strcmp(oldhist->line,line) == 0)
+ return 0;
+ return 1;
+}
+
+
+#if defined(USE_NEW_READLINE_INTERFACE)
+static int fake_magic_space(int, int)
+#else
+static int fake_magic_space(const char *, int)
+#endif
+{
+ rl_insert(1, ' ');
+ return 0;
+}
+
+
+static void initialize_readline ()
+{
+ /* Allow conditional parsing of the ~/.inputrc file. */
+ rl_readline_name= (char *) "mysql";
+ rl_terminal_name= getenv("TERM");
+#ifdef HAVE_SETLOCALE
+ setlocale(LC_ALL,"");
+#endif
+
+ /* Tell the completer that we want a crack first. */
+#if defined(USE_NEW_READLINE_INTERFACE)
+ rl_attempted_completion_function= (rl_completion_func_t*)&new_mysql_completion;
+ rl_completion_entry_function= (rl_compentry_func_t*)&no_completion;
+
+ rl_add_defun("magic-space", (rl_command_func_t *)&fake_magic_space, -1);
+#elif defined(USE_LIBEDIT_INTERFACE)
+ rl_attempted_completion_function= (CPPFunction*)&new_mysql_completion;
+ rl_completion_entry_function= &no_completion;
+ rl_add_defun("magic-space", (Function*)&fake_magic_space, -1);
+#else
+ rl_attempted_completion_function= (CPPFunction*)&new_mysql_completion;
+ rl_completion_entry_function= &no_completion;
+#endif
+}
+
+/*
+ Attempt to complete on the contents of TEXT. START and END show the
+ region of TEXT that contains the word to complete. We can use the
+ entire line in case we want to do some simple parsing. Return the
+ array of matches, or NULL if there aren't any.
+*/
+
+static char **new_mysql_completion(const char *text,
+ int start __attribute__((unused)),
+ int end __attribute__((unused)))
+{
+ if (!status.batch && !quick)
+#if defined(USE_NEW_READLINE_INTERFACE)
+ return rl_completion_matches(text, new_command_generator);
+#else
+ return completion_matches((char *)text, (CPFunction *)new_command_generator);
+#endif
+ else
+ return (char**) 0;
+}
+
+static char *new_command_generator(const char *text,int state)
+{
+ static int textlen;
+ char *ptr;
+ static Bucket *b;
+ static entry *e;
+ static uint i;
+
+ if (!state)
+ textlen=(uint) strlen(text);
+
+ if (textlen>0)
+ { /* lookup in the hash */
+ if (!state)
+ {
+ uint len;
+
+ b = find_all_matches(&ht,text,(uint) strlen(text),&len);
+ if (!b)
+ return NullS;
+ e = b->pData;
+ }
+
+ if (e)
+ {
+ ptr= strdup(e->str);
+ e = e->pNext;
+ return ptr;
+ }
+ }
+ else
+ { /* traverse the entire hash, ugly but works */
+
+ if (!state)
+ {
+ /* find the first used bucket */
+ for (i=0 ; i < ht.nTableSize ; i++)
+ {
+ if (ht.arBuckets[i])
+ {
+ b = ht.arBuckets[i];
+ e = b->pData;
+ break;
+ }
+ }
+ }
+ ptr= NullS;
+ while (e && !ptr)
+ { /* find valid entry in bucket */
+ if ((uint) strlen(e->str) == b->nKeyLength)
+ ptr = strdup(e->str);
+ /* find the next used entry */
+ e = e->pNext;
+ if (!e)
+ { /* find the next used bucket */
+ b = b->pNext;
+ if (!b)
+ {
+ for (i++ ; i<ht.nTableSize; i++)
+ {
+ if (ht.arBuckets[i])
+ {
+ b = ht.arBuckets[i];
+ e = b->pData;
+ break;
+ }
+ }
+ }
+ else
+ e = b->pData;
+ }
+ }
+ if (ptr)
+ return ptr;
+ }
+ return NullS;
+}
+
+
+/* Build up the completion hash */
+
+static void build_completion_hash(bool rehash, bool write_info)
+{
+ COMMANDS *cmd=commands;
+ MYSQL_RES *databases=0,*tables=0;
+ MYSQL_RES *fields;
+ static char ***field_names= 0;
+ MYSQL_ROW database_row,table_row;
+ MYSQL_FIELD *sql_field;
+ char buf[NAME_LEN*2+2]; // table name plus field name plus 2
+ int i,j,num_fields;
+ DBUG_ENTER("build_completion_hash");
+
+ if (status.batch || quick || !current_db)
+ DBUG_VOID_RETURN; // We don't need completion in batches
+ if (!rehash)
+ DBUG_VOID_RETURN;
+
+ /* Free old used memory */
+ if (field_names)
+ field_names=0;
+ completion_hash_clean(&ht);
+ free_root(&hash_mem_root,MYF(0));
+
+ /* hash this file's known subset of SQL commands */
+ while (cmd->name) {
+ add_word(&ht,(char*) cmd->name);
+ cmd++;
+ }
+
+ /* hash MySQL functions (to be implemented) */
+
+ /* hash all database names */
+ if (mysql_query(&mysql,"show databases") == 0)
+ {
+ if (!(databases = mysql_store_result(&mysql)))
+ put_info(mysql_error(&mysql),INFO_INFO);
+ else
+ {
+ while ((database_row=mysql_fetch_row(databases)))
+ {
+ char *str=strdup_root(&hash_mem_root, (char*) database_row[0]);
+ if (str)
+ add_word(&ht,(char*) str);
+ }
+ mysql_free_result(databases);
+ }
+ }
+ /* hash all table names */
+ if (mysql_query(&mysql,"show tables")==0)
+ {
+ if (!(tables = mysql_store_result(&mysql)))
+ put_info(mysql_error(&mysql),INFO_INFO);
+ else
+ {
+ if (mysql_num_rows(tables) > 0 && !opt_silent && write_info)
+ {
+ tee_fprintf(stdout, "\
+Reading table information for completion of table and column names\n\
+You can turn off this feature to get a quicker startup with -A\n\n");
+ }
+ while ((table_row=mysql_fetch_row(tables)))
+ {
+ char *str=strdup_root(&hash_mem_root, (char*) table_row[0]);
+ if (str &&
+ !completion_hash_exists(&ht,(char*) str, (uint) strlen(str)))
+ add_word(&ht,str);
+ }
+ }
+ }
+
+ /* hash all field names, both with the table prefix and without it */
+ if (!tables) /* no tables */
+ {
+ DBUG_VOID_RETURN;
+ }
+ mysql_data_seek(tables,0);
+ if (!(field_names= (char ***) alloc_root(&hash_mem_root,sizeof(char **) *
+ (uint) (mysql_num_rows(tables)+1))))
+ {
+ mysql_free_result(tables);
+ DBUG_VOID_RETURN;
+ }
+ i=0;
+ while ((table_row=mysql_fetch_row(tables)))
+ {
+ if ((fields=mysql_list_fields(&mysql,(const char*) table_row[0],NullS)))
+ {
+ num_fields=mysql_num_fields(fields);
+ if (!(field_names[i] = (char **) alloc_root(&hash_mem_root,
+ sizeof(char *) *
+ (num_fields*2+1))))
+ {
+ mysql_free_result(fields);
+ break;
+ }
+ field_names[i][num_fields*2]= NULL;
+ j=0;
+ while ((sql_field=mysql_fetch_field(fields)))
+ {
+ sprintf(buf,"%.64s.%.64s",table_row[0],sql_field->name);
+ field_names[i][j] = strdup_root(&hash_mem_root,buf);
+ add_word(&ht,field_names[i][j]);
+ field_names[i][num_fields+j] = strdup_root(&hash_mem_root,
+ sql_field->name);
+ if (!completion_hash_exists(&ht,field_names[i][num_fields+j],
+ (uint) strlen(field_names[i][num_fields+j])))
+ add_word(&ht,field_names[i][num_fields+j]);
+ j++;
+ }
+ mysql_free_result(fields);
+ }
+ else
+ field_names[i]= 0;
+
+ i++;
+ }
+ mysql_free_result(tables);
+ field_names[i]=0; // End pointer
+ DBUG_VOID_RETURN;
+}
+
+ /* for gnu readline */
+
+#ifndef HAVE_INDEX
+extern "C" {
+extern char *index(const char *,int c),*rindex(const char *,int);
+
+char *index(const char *s,int c)
+{
+ for (;;)
+ {
+ if (*s == (char) c) return (char*) s;
+ if (!*s++) return NullS;
+ }
+}
+
+char *rindex(const char *s,int c)
+{
+ reg3 char *t;
+
+ t = NullS;
+ do if (*s == (char) c) t = (char*) s; while (*s++);
+ return (char*) t;
+}
+}
+#endif
+#endif /* HAVE_READLINE */
+
+
+static int reconnect(void)
+{
+ /* purecov: begin tested */
+ if (opt_reconnect)
+ {
+ put_info("No connection. Trying to reconnect...",INFO_INFO);
+ (void) com_connect((String *) 0, 0);
+ if (opt_rehash)
+ com_rehash(NULL, NULL);
+ }
+ if (!connected)
+ return put_info("Can't connect to the server\n",INFO_ERROR);
+ my_free(server_version);
+ server_version= 0;
+ /* purecov: end */
+ return 0;
+}
+
+static void get_current_db()
+{
+ MYSQL_RES *res;
+
+ /* If one_database is set, current_db is not supposed to change. */
+ if (one_database)
+ return;
+
+ my_free(current_db);
+ current_db= NULL;
+ /* In case of error below current_db will be NULL */
+ if (!mysql_query(&mysql, "SELECT DATABASE()") &&
+ (res= mysql_use_result(&mysql)))
+ {
+ MYSQL_ROW row= mysql_fetch_row(res);
+ if (row && row[0])
+ current_db= my_strdup(PSI_NOT_INSTRUMENTED, row[0], MYF(MY_WME));
+ mysql_free_result(res);
+ }
+}
+
+/***************************************************************************
+ The different commands
+***************************************************************************/
+
+int mysql_real_query_for_lazy(const char *buf, size_t length)
+{
+ for (uint retry=0;; retry++)
+ {
+ int error;
+ if (!mysql_real_query(&mysql,buf,(ulong)length))
+ return 0;
+ error= put_error(&mysql);
+ if (mysql_errno(&mysql) != CR_SERVER_GONE_ERROR || retry > 1 ||
+ !opt_reconnect)
+ return error;
+ if (reconnect())
+ return error;
+ }
+}
+
+int mysql_store_result_for_lazy(MYSQL_RES **result)
+{
+ if ((*result=mysql_store_result(&mysql)))
+ return 0;
+
+ if (mysql_error(&mysql)[0])
+ return put_error(&mysql);
+ return 0;
+}
+
+static void print_help_item(MYSQL_ROW *cur, int num_name, int num_cat, char *last_char)
+{
+ char ccat= (*cur)[num_cat][0];
+ if (*last_char != ccat)
+ {
+ put_info(ccat == 'Y' ? "categories:" : "topics:", INFO_INFO);
+ *last_char= ccat;
+ }
+ tee_fprintf(PAGER, " %s\n", (*cur)[num_name]);
+}
+
+
+static int com_server_help(String *buffer __attribute__((unused)),
+ char *line __attribute__((unused)), char *help_arg)
+{
+ MYSQL_ROW cur;
+ const char *server_cmd;
+ char cmd_buf[100 + 1];
+ MYSQL_RES *result;
+ int error;
+
+ if (help_arg[0] != '\'')
+ {
+ char *end_arg= strend(help_arg);
+ if(--end_arg)
+ {
+ while (my_isspace(charset_info,*end_arg))
+ end_arg--;
+ *++end_arg= '\0';
+ }
+ (void) strxnmov(cmd_buf, sizeof(cmd_buf), "help '", help_arg, "'", NullS);
+ }
+ else
+ (void) strxnmov(cmd_buf, sizeof(cmd_buf), "help ", help_arg, NullS);
+
+ server_cmd= cmd_buf;
+
+ if (!status.batch)
+ {
+ old_buffer= *buffer;
+ old_buffer.copy();
+ }
+
+ if (!connected && reconnect())
+ return 1;
+
+ if ((error= mysql_real_query_for_lazy(server_cmd,(int)strlen(server_cmd))) ||
+ (error= mysql_store_result_for_lazy(&result)))
+ return error;
+
+ if (result)
+ {
+ unsigned int num_fields= mysql_num_fields(result);
+ my_ulonglong num_rows= mysql_num_rows(result);
+ if (num_fields==3 && num_rows==1)
+ {
+ if (!(cur= mysql_fetch_row(result)))
+ {
+ error= -1;
+ goto err;
+ }
+
+ init_pager();
+ tee_fprintf(PAGER, "Name: \'%s\'\n", cur[0]);
+ tee_fprintf(PAGER, "Description:\n%s", cur[1]);
+ if (cur[2] && *((char*)cur[2]))
+ tee_fprintf(PAGER, "Examples:\n%s", cur[2]);
+ tee_fprintf(PAGER, "\n");
+ end_pager();
+ }
+ else if (num_fields >= 2 && num_rows)
+ {
+ init_pager();
+ char last_char= 0;
+
+ int UNINIT_VAR(num_name), UNINIT_VAR(num_cat);
+
+ if (num_fields == 2)
+ {
+ put_info("Many help items for your request exist.", INFO_INFO);
+ put_info("To make a more specific request, please type 'help <item>',\nwhere <item> is one of the following", INFO_INFO);
+ num_name= 0;
+ num_cat= 1;
+ }
+ else if ((cur= mysql_fetch_row(result)))
+ {
+ tee_fprintf(PAGER, "You asked for help about help category: \"%s\"\n", cur[0]);
+ put_info("For more information, type 'help <item>', where <item> is one of the following", INFO_INFO);
+ num_name= 1;
+ num_cat= 2;
+ print_help_item(&cur,1,2,&last_char);
+ }
+
+ while ((cur= mysql_fetch_row(result)))
+ print_help_item(&cur,num_name,num_cat,&last_char);
+ tee_fprintf(PAGER, "\n");
+ end_pager();
+ }
+ else
+ {
+ put_info("\nNothing found", INFO_INFO);
+ if (strncasecmp(server_cmd, "help 'contents'", 15) == 0)
+ {
+ put_info("\nPlease check if 'help tables' are loaded.\n", INFO_INFO);
+ goto err;
+ }
+ put_info("Please try to run 'help contents' for a list of all accessible topics\n", INFO_INFO);
+ }
+ }
+
+err:
+ mysql_free_result(result);
+ return error;
+}
+
+static int
+com_help(String *buffer __attribute__((unused)),
+ char *line __attribute__((unused)))
+{
+ int i, j;
+ char * help_arg= strchr(line,' '), buff[32], *end;
+ if (help_arg)
+ {
+ while (my_isspace(charset_info,*help_arg))
+ help_arg++;
+ if (*help_arg)
+ return com_server_help(buffer,line,help_arg);
+ }
+
+ put_info("\nGeneral information about MariaDB can be found at\n"
+ "http://mariadb.org\n", INFO_INFO);
+ put_info("List of all client commands:", INFO_INFO);
+ if (!named_cmds)
+ put_info("Note that all text commands must be first on line and end with ';'",INFO_INFO);
+ for (i = 0; commands[i].name; i++)
+ {
+ end= strmov(buff, commands[i].name);
+ for (j= (int)strlen(commands[i].name); j < 10; j++)
+ end= strmov(end, " ");
+ if (commands[i].func)
+ tee_fprintf(stdout, "%s(\\%c) %s\n", buff,
+ commands[i].cmd_char, commands[i].doc);
+ }
+ if (connected && mysql_get_server_version(&mysql) >= 40100)
+ put_info("\nFor server side help, type 'help contents'\n", INFO_INFO);
+ return 0;
+}
+
+
+ /* ARGSUSED */
+static int
+com_clear(String *buffer,char *line __attribute__((unused)))
+{
+#ifdef HAVE_READLINE
+ if (status.add_to_history)
+ fix_history(buffer);
+#endif
+ buffer->length(0);
+ return 0;
+}
+
+static void adjust_console_codepage(const char *name __attribute__((unused)))
+{
+#ifdef _WIN32
+ if (my_set_console_cp(name) < 0)
+ {
+ char buf[128];
+ snprintf(buf, sizeof(buf),
+ "WARNING: Could not determine Windows codepage for charset '%s',"
+ "continue using codepage %u", name, GetConsoleOutputCP());
+ put_info(buf, INFO_INFO);
+ }
+#endif
+}
+
+
+ /* ARGSUSED */
+static int
+com_charset(String *buffer __attribute__((unused)), char *line)
+{
+ char buff[256], *param;
+ CHARSET_INFO * new_cs;
+ strmake_buf(buff, line);
+ param= get_arg(buff, GET);
+ if (!param || !*param)
+ {
+ return put_info("Usage: \\C charset_name | charset charset_name",
+ INFO_ERROR, 0);
+ }
+ new_cs= get_charset_by_csname(param, MY_CS_PRIMARY,
+ MYF(MY_UTF8_IS_UTF8MB3 | MY_WME));
+ if (new_cs)
+ {
+ charset_info= new_cs;
+ mysql_set_character_set(&mysql, charset_info->cs_name.str);
+ default_charset= (char *)charset_info->cs_name.str;
+ put_info("Charset changed", INFO_INFO);
+ adjust_console_codepage(charset_info->cs_name.str);
+ }
+ else put_info("Charset is not found", INFO_INFO);
+ return 0;
+}
+
+/*
+ Execute command
+ Returns: 0 if ok
+ -1 if not fatal error
+ 1 if fatal error
+*/
+
+
+static int
+com_go(String *buffer,char *line __attribute__((unused)))
+{
+ char buff[200]; /* about 110 chars used so far */
+ char time_buff[53+3+1]; /* time max + space & parens + NUL */
+ MYSQL_RES *result;
+ ulonglong timer;
+ ulong warnings= 0;
+ uint error= 0;
+ int err= 0;
+
+ interrupted_query= 0;
+ if (!status.batch)
+ {
+ old_buffer= *buffer; // Save for edit command
+ old_buffer.copy();
+ }
+
+ /* Remove garbage for nicer messages */
+ LINT_INIT_STRUCT(buff[0]);
+ remove_cntrl(*buffer);
+
+ if (buffer->is_empty())
+ {
+ if (status.batch) // Ignore empty queries.
+ return 0;
+ return put_info("No query specified\n",INFO_ERROR);
+
+ }
+ if (!connected && reconnect())
+ {
+ buffer->length(0); // Remove query on error
+ return opt_reconnect ? -1 : 1; // Fatal error
+ }
+ if (verbose)
+ (void) com_print(buffer,0);
+
+ if (skip_updates &&
+ (buffer->length() < 4 || charset_info->strnncoll((const uchar*)buffer->ptr(),4,
+ (const uchar*)"SET ",4)))
+ {
+ (void) put_info("Ignoring query to other database",INFO_INFO);
+ return 0;
+ }
+
+ timer= microsecond_interval_timer();
+ executing_query= 1;
+ error= mysql_real_query_for_lazy(buffer->ptr(),buffer->length());
+ report_progress_end();
+
+#ifdef HAVE_READLINE
+ if (status.add_to_history)
+ {
+ const char *delim= vertical ? "\\G" : delimiter;
+ buffer->append(delim, strlen(delim));
+ /* Append final command onto history */
+ fix_history(buffer);
+ }
+#endif
+
+ buffer->length(0);
+
+ if (error)
+ goto end;
+
+ do
+ {
+ char *pos;
+
+ if (quick)
+ {
+ if (!(result=mysql_use_result(&mysql)) && mysql_field_count(&mysql))
+ {
+ error= put_error(&mysql);
+ goto end;
+ }
+ }
+ else
+ {
+ error= mysql_store_result_for_lazy(&result);
+ if (error)
+ goto end;
+ }
+
+ if (verbose >= 3 || !opt_silent)
+ end_timer(timer, time_buff);
+ else
+ time_buff[0]= '\0';
+
+ /* Every branch must truncate buff. */
+ if (result)
+ {
+ if (!mysql_num_rows(result) && ! quick && !column_types_flag)
+ {
+ strmov(buff, "Empty set");
+ if (opt_xml)
+ {
+ /*
+ We must print XML header and footer
+ to produce a well-formed XML even if
+ the result set is empty (Bug#27608).
+ */
+ init_pager();
+ print_table_data_xml(result);
+ end_pager();
+ }
+ }
+ else
+ {
+ init_pager();
+ if (opt_html)
+ print_table_data_html(result);
+ else if (opt_xml)
+ print_table_data_xml(result);
+ else if (vertical || (auto_vertical_output &&
+ (terminal_width < get_result_width(result))))
+ print_table_data_vertically(result);
+ else if (opt_silent && verbose <= 2 && !output_tables)
+ print_tab_data(result);
+ else
+ print_table_data(result);
+ sprintf(buff,"%ld %s in set",
+ (long) mysql_num_rows(result),
+ (long) mysql_num_rows(result) == 1 ? "row" : "rows");
+ end_pager();
+ if (mysql_errno(&mysql))
+ error= put_error(&mysql);
+ }
+ }
+ else if (mysql_affected_rows(&mysql) == ~(ulonglong) 0)
+ strmov(buff,"Query OK");
+ else
+ sprintf(buff,"Query OK, %ld %s affected",
+ (long) mysql_affected_rows(&mysql),
+ (long) mysql_affected_rows(&mysql) == 1 ? "row" : "rows");
+
+ pos=strend(buff);
+ if ((warnings= mysql_warning_count(&mysql)))
+ {
+ *pos++= ',';
+ *pos++= ' ';
+ pos=int10_to_str(warnings, pos, 10);
+ pos=strmov(pos, " warning");
+ if (warnings != 1)
+ *pos++= 's';
+ }
+ strmov(pos, time_buff);
+ put_info(buff,INFO_RESULT);
+ if (mysql_info(&mysql))
+ put_info(mysql_info(&mysql),INFO_RESULT);
+ put_info("",INFO_RESULT); // Empty row
+
+ if (result && !mysql_eof(result)) /* Something wrong when using quick */
+ error= put_error(&mysql);
+ else if (unbuffered)
+ fflush(stdout);
+ mysql_free_result(result);
+ } while (!(err= mysql_next_result(&mysql)));
+ if (err >= 1)
+ error= put_error(&mysql);
+
+end:
+
+ /* Show warnings if any or error occurred */
+ if (show_warnings == 1 && (warnings >= 1 || error))
+ print_warnings();
+
+ if (!error && !status.batch &&
+ (mysql.server_status & SERVER_STATUS_DB_DROPPED))
+ get_current_db();
+
+ executing_query= 0;
+ return error; /* New command follows */
+}
+
+
+static void init_pager()
+{
+#ifdef USE_POPEN
+ if (!opt_nopager)
+ {
+ if (!(PAGER= popen(pager, "w")))
+ {
+ tee_fprintf(stdout, "popen() failed! defaulting PAGER to stdout!\n");
+ PAGER= stdout;
+ }
+ }
+ else
+#endif
+ PAGER= stdout;
+}
+
+static void end_pager()
+{
+#ifdef USE_POPEN
+ if (!opt_nopager)
+ pclose(PAGER);
+#endif
+}
+
+
+static void init_tee(const char *file_name)
+{
+ FILE* new_outfile;
+ if (opt_outfile)
+ end_tee();
+ if (!(new_outfile= my_fopen(file_name, O_APPEND | O_WRONLY, MYF(MY_WME))))
+ {
+ tee_fprintf(stdout, "Error logging to file '%s'\n", file_name);
+ return;
+ }
+ OUTFILE = new_outfile;
+ strmake_buf(outfile, file_name);
+ tee_fprintf(stdout, "Logging to file '%s'\n", file_name);
+ opt_outfile= 1;
+ return;
+}
+
+
+static void end_tee()
+{
+ my_fclose(OUTFILE, MYF(0));
+ OUTFILE= 0;
+ opt_outfile= 0;
+ return;
+}
+
+
+static int
+com_ego(String *buffer,char *line)
+{
+ int result;
+ bool oldvertical=vertical;
+ vertical=1;
+ result=com_go(buffer,line);
+ vertical=oldvertical;
+ return result;
+}
+
+
+static const char *fieldtype2str(enum enum_field_types type)
+{
+ switch (type) {
+ case MYSQL_TYPE_BIT: return "BIT";
+ case MYSQL_TYPE_BLOB: return "BLOB";
+ case MYSQL_TYPE_DATE: return "DATE";
+ case MYSQL_TYPE_DATETIME: return "DATETIME";
+ case MYSQL_TYPE_NEWDECIMAL: return "NEWDECIMAL";
+ case MYSQL_TYPE_DECIMAL: return "DECIMAL";
+ case MYSQL_TYPE_DOUBLE: return "DOUBLE";
+ case MYSQL_TYPE_ENUM: return "ENUM";
+ case MYSQL_TYPE_FLOAT: return "FLOAT";
+ case MYSQL_TYPE_GEOMETRY: return "GEOMETRY";
+ case MYSQL_TYPE_INT24: return "INT24";
+ case MYSQL_TYPE_LONG: return "LONG";
+ case MYSQL_TYPE_LONGLONG: return "LONGLONG";
+ case MYSQL_TYPE_LONG_BLOB: return "LONG_BLOB";
+ case MYSQL_TYPE_MEDIUM_BLOB: return "MEDIUM_BLOB";
+ case MYSQL_TYPE_NEWDATE: return "NEWDATE";
+ case MYSQL_TYPE_NULL: return "NULL";
+ case MYSQL_TYPE_SET: return "SET";
+ case MYSQL_TYPE_SHORT: return "SHORT";
+ case MYSQL_TYPE_STRING: return "STRING";
+ case MYSQL_TYPE_TIME: return "TIME";
+ case MYSQL_TYPE_TIMESTAMP: return "TIMESTAMP";
+ case MYSQL_TYPE_TINY: return "TINY";
+ case MYSQL_TYPE_TINY_BLOB: return "TINY_BLOB";
+ case MYSQL_TYPE_VAR_STRING: return "VAR_STRING";
+ case MYSQL_TYPE_YEAR: return "YEAR";
+ default: return "?-unknown-?";
+ }
+}
+
+static char *fieldflags2str(uint f) {
+ static char buf[1024];
+ char *s=buf;
+ *s=0;
+#define ff2s_check_flag(X) \
+ if (f & X ## _FLAG) { s=strmov(s, # X " "); f &= ~ X ## _FLAG; }
+ ff2s_check_flag(NOT_NULL);
+ ff2s_check_flag(PRI_KEY);
+ ff2s_check_flag(UNIQUE_KEY);
+ ff2s_check_flag(MULTIPLE_KEY);
+ ff2s_check_flag(BLOB);
+ ff2s_check_flag(UNSIGNED);
+ ff2s_check_flag(ZEROFILL);
+ ff2s_check_flag(BINARY);
+ ff2s_check_flag(ENUM);
+ ff2s_check_flag(AUTO_INCREMENT);
+ ff2s_check_flag(TIMESTAMP);
+ ff2s_check_flag(SET);
+ ff2s_check_flag(NO_DEFAULT_VALUE);
+ ff2s_check_flag(NUM);
+ ff2s_check_flag(PART_KEY);
+ ff2s_check_flag(GROUP);
+ /*
+ CONTEXT_COLLATION_FLAG (former BINCMP_FLAG) is used at parse
+ time only and should never show up on the client side. Don't test it.
+ */
+ ff2s_check_flag(ON_UPDATE_NOW);
+#undef ff2s_check_flag
+ if (f)
+ sprintf(s, " unknows=0x%04x", f);
+ return buf;
+}
+
+static void
+print_field_types(MYSQL_RES *result)
+{
+ MYSQL_FIELD *field;
+ uint i=0;
+
+ while ((field = mysql_fetch_field(result)))
+ {
+ Client_field_metadata metadata(field);
+ BinaryStringBuffer<128> data_type_metadata_str;
+ metadata.print_data_type_related_attributes(&data_type_metadata_str);
+ tee_fprintf(PAGER, "Field %3u: `%s`\n"
+ "Org_field: `%s`\n"
+ "Catalog: `%s`\n"
+ "Database: `%s`\n"
+ "Table: `%s`\n"
+ "Org_table: `%s`\n"
+ "Type: %s%s%.*s%s\n"
+ "Collation: %s (%u)\n"
+ "Length: %lu\n"
+ "Max_length: %lu\n"
+ "Decimals: %u\n"
+ "Flags: %s\n\n",
+ ++i,
+ field->name, field->org_name, field->catalog, field->db,
+ field->table, field->org_table, fieldtype2str(field->type),
+ data_type_metadata_str.length() ? " (" : "",
+ data_type_metadata_str.length(), data_type_metadata_str.ptr(),
+ data_type_metadata_str.length() ? ")" : "",
+ get_charset_name(field->charsetnr), field->charsetnr,
+ field->length, field->max_length, field->decimals,
+ fieldflags2str(field->flags));
+ }
+ tee_puts("", PAGER);
+}
+
+
+/* Used to determine if we should invoke print_as_hex for this field */
+
+static bool
+is_binary_field(MYSQL_FIELD *field)
+{
+ if ((field->charsetnr == 63) &&
+ (field->type == MYSQL_TYPE_BIT ||
+ field->type == MYSQL_TYPE_BLOB ||
+ field->type == MYSQL_TYPE_LONG_BLOB ||
+ field->type == MYSQL_TYPE_MEDIUM_BLOB ||
+ field->type == MYSQL_TYPE_TINY_BLOB ||
+ field->type == MYSQL_TYPE_VAR_STRING ||
+ field->type == MYSQL_TYPE_STRING ||
+ field->type == MYSQL_TYPE_VARCHAR ||
+ field->type == MYSQL_TYPE_GEOMETRY))
+ return 1;
+ return 0;
+}
+
+
+/* Print binary value as hex literal (0x ...) */
+
+static void
+print_as_hex(FILE *output_file, const char *str, size_t len, size_t total_bytes_to_send)
+{
+ const char *ptr= str, *end= ptr+len;
+ size_t i;
+ fprintf(output_file, "0x");
+ for(; ptr < end; ptr++)
+ fprintf(output_file, "%02X", *((uchar*)ptr));
+ for (i= 2*len+2; i < total_bytes_to_send; i++)
+ tee_putc((int)' ', output_file);
+}
+
+
+static void
+print_table_data(MYSQL_RES *result)
+{
+ String separator(256);
+ MYSQL_ROW cur;
+ bool *num_flag;
+
+ num_flag=(bool*) my_alloca(sizeof(bool)*mysql_num_fields(result));
+ if (column_types_flag)
+ {
+ print_field_types(result);
+ if (!mysql_num_rows(result))
+ {
+ my_afree((uchar*) num_flag);
+ return;
+ }
+ mysql_field_seek(result,0);
+ }
+ separator.copy("+",1,charset_info);
+ while (MYSQL_FIELD *field= mysql_fetch_field(result))
+ {
+ uint length= column_names ? field->name_length : 0;
+ if (quick)
+ length= MY_MAX(length,field->length);
+ else
+ length= MY_MAX(length,field->max_length);
+ if (length < 4 && !IS_NOT_NULL(field->flags))
+ length=4; // Room for "NULL"
+ if (opt_binhex && is_binary_field(field))
+ length= 2 + length * 2;
+ field->max_length=length;
+ num_flag[mysql_field_tell(result) - 1]= IS_NUM(field->type);
+ separator.fill(separator.length()+length+2,'-');
+ separator.append('+');
+ }
+ separator.append('\0'); // End marker for \0
+ tee_puts((char*) separator.ptr(), PAGER);
+ if (column_names)
+ {
+ mysql_field_seek(result,0);
+ (void) tee_fputs("|", PAGER);
+ while (MYSQL_FIELD *field= mysql_fetch_field(result))
+ {
+ size_t name_length= (uint) strlen(field->name);
+ size_t numcells= charset_info->numcells(field->name,
+ field->name + name_length);
+ size_t display_length= field->max_length + name_length - numcells;
+ tee_fprintf(PAGER, " %-*s |",(int) MY_MIN(display_length,
+ MAX_COLUMN_LENGTH),
+ field->name);
+ }
+ (void) tee_fputs("\n", PAGER);
+ tee_puts((char*) separator.ptr(), PAGER);
+ }
+
+ while ((cur= mysql_fetch_row(result)))
+ {
+ if (interrupted_query)
+ break;
+ ulong *lengths= mysql_fetch_lengths(result);
+ (void) tee_fputs("| ", PAGER);
+ mysql_field_seek(result, 0);
+ for (uint off= 0; off < mysql_num_fields(result); off++)
+ {
+ const char *buffer;
+ uint data_length;
+ uint field_max_length;
+ uint extra_padding;
+
+ if (off)
+ (void) tee_fputs(" ", PAGER);
+
+ if (cur[off] == NULL)
+ {
+ buffer= "NULL";
+ data_length= 4;
+ }
+ else
+ {
+ buffer= cur[off];
+ data_length= (uint) lengths[off];
+ }
+
+ MYSQL_FIELD *field= mysql_fetch_field(result);
+ field_max_length= field->max_length;
+
+ /*
+ How many text cells on the screen will this string span? If it contains
+ multibyte characters, then the number of characters we occupy on screen
+ will be fewer than the number of bytes we occupy in memory.
+
+ We need to find how much screen real-estate we will occupy to know how
+ many extra padding-characters we should send with the printing function.
+ */
+ size_t visible_length= charset_info->numcells(buffer, buffer + data_length);
+ extra_padding= (uint) (data_length - visible_length);
+
+ if (opt_binhex && is_binary_field(field))
+ print_as_hex(PAGER, cur[off], lengths[off], field_max_length);
+ else if (field_max_length > MAX_COLUMN_LENGTH)
+ tee_print_sized_data(buffer, data_length, MAX_COLUMN_LENGTH+extra_padding, FALSE);
+ else
+ {
+ if (num_flag[off] != 0) /* if it is numeric, we right-justify it */
+ tee_print_sized_data(buffer, data_length, field_max_length+extra_padding, TRUE);
+ else
+ tee_print_sized_data(buffer, data_length, field_max_length+extra_padding, FALSE);
+ }
+ tee_fputs(" |", PAGER);
+ }
+ (void) tee_fputs("\n", PAGER);
+ }
+ tee_puts((char*) separator.ptr(), PAGER);
+ my_afree((uchar*) num_flag);
+}
+
+/**
+ Return the length of a field after it would be rendered into text.
+
+ This doesn't know or care about multibyte characters. Assume we're
+ using such a charset. We can't know that all of the upcoming rows
+ for this column will have bytes that each render into some fraction
+ of a character. It's at least possible that a row has bytes that
+ all render into one character each, and so the maximum length is
+ still the number of bytes. (Assumption 1: This can't be better
+ because we can never know the number of characters that the DB is
+ going to send -- only the number of bytes. 2: Chars <= Bytes.)
+
+ @param field Pointer to a field to be inspected
+
+ @returns number of character positions to be used, at most
+*/
+static int get_field_disp_length(MYSQL_FIELD *field)
+{
+ uint length= column_names ? field->name_length : 0;
+
+ if (quick)
+ length= MY_MAX(length, field->length);
+ else
+ length= MY_MAX(length, field->max_length);
+
+ if (length < 4 && !IS_NOT_NULL(field->flags))
+ length= 4; /* Room for "NULL" */
+
+ return length;
+}
+
+/**
+ For a new result, return the max number of characters that any
+ upcoming row may return.
+
+ @param result Pointer to the result to judge
+
+ @returns The max number of characters in any row of this result
+*/
+
+static int get_result_width(MYSQL_RES *result)
+{
+ unsigned int len= 0;
+ MYSQL_FIELD *field;
+ MYSQL_FIELD_OFFSET offset;
+
+#ifndef DBUG_OFF
+ offset= mysql_field_tell(result);
+ DBUG_ASSERT(offset == 0);
+#else
+ offset= 0;
+#endif
+
+ while ((field= mysql_fetch_field(result)) != NULL)
+ len+= get_field_disp_length(field) + 3; /* plus bar, space, & final space */
+
+ (void) mysql_field_seek(result, offset);
+
+ return len + 1; /* plus final bar. */
+}
+
+static void
+tee_print_sized_data(const char *data, unsigned int data_length, unsigned int total_bytes_to_send, bool right_justified)
+{
+ /*
+ For '\0's print ASCII spaces instead, as '\0' is eaten by (at
+ least my) console driver, and that messes up the pretty table
+ grid. (The \0 is also the reason we can't use fprintf() .)
+ */
+ unsigned int i;
+ const char *p;
+
+ if (right_justified)
+ for (i= data_length; i < total_bytes_to_send; i++)
+ tee_putc((int)' ', PAGER);
+
+ for (i= 0, p= data; i < data_length; i+= 1, p+= 1)
+ {
+ if (*p == '\0')
+ tee_putc((int)' ', PAGER);
+ else
+ tee_putc((int)*p, PAGER);
+ }
+
+ if (! right_justified)
+ for (i= data_length; i < total_bytes_to_send; i++)
+ tee_putc((int)' ', PAGER);
+}
+
+
+
+static void
+print_table_data_html(MYSQL_RES *result)
+{
+ MYSQL_ROW cur;
+ MYSQL_FIELD *field;
+
+ mysql_field_seek(result,0);
+ (void) tee_fputs("<TABLE BORDER=1>", PAGER);
+ if (column_names)
+ {
+ (void) tee_fputs("<TR>", PAGER);
+ while((field = mysql_fetch_field(result)))
+ {
+ tee_fputs("<TH>", PAGER);
+ if (field->name && field->name[0])
+ xmlencode_print(field->name, field->name_length);
+ else
+ tee_fputs(field->name ? " &nbsp; " : "NULL", PAGER);
+ tee_fputs("</TH>", PAGER);
+ }
+ (void) tee_fputs("</TR>", PAGER);
+ }
+ while ((cur = mysql_fetch_row(result)))
+ {
+ if (interrupted_query)
+ break;
+ ulong *lengths=mysql_fetch_lengths(result);
+ field= mysql_fetch_fields(result);
+ (void) tee_fputs("<TR>", PAGER);
+ for (uint i=0; i < mysql_num_fields(result); i++)
+ {
+ (void) tee_fputs("<TD>", PAGER);
+ if (opt_binhex && is_binary_field(&field[i]))
+ print_as_hex(PAGER, cur[i], lengths[i], lengths[i]);
+ else
+ xmlencode_print(cur[i], lengths[i]);
+ (void) tee_fputs("</TD>", PAGER);
+ }
+ (void) tee_fputs("</TR>", PAGER);
+ }
+ (void) tee_fputs("</TABLE>", PAGER);
+}
+
+
+static void
+print_table_data_xml(MYSQL_RES *result)
+{
+ MYSQL_ROW cur;
+ MYSQL_FIELD *fields;
+
+ mysql_field_seek(result,0);
+
+ tee_fputs("<?xml version=\"1.0\"?>\n\n<resultset statement=\"", PAGER);
+ xmlencode_print(glob_buffer.ptr(), (int)strlen(glob_buffer.ptr()));
+ tee_fputs("\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">",
+ PAGER);
+
+ fields = mysql_fetch_fields(result);
+ while ((cur = mysql_fetch_row(result)))
+ {
+ if (interrupted_query)
+ break;
+ ulong *lengths=mysql_fetch_lengths(result);
+ (void) tee_fputs("\n <row>\n", PAGER);
+ for (uint i=0; i < mysql_num_fields(result); i++)
+ {
+ tee_fprintf(PAGER, "\t<field name=\"");
+ xmlencode_print(fields[i].name, (uint) strlen(fields[i].name));
+ if (cur[i])
+ {
+ tee_fprintf(PAGER, "\">");
+ if (opt_binhex && is_binary_field(&fields[i]))
+ print_as_hex(PAGER, cur[i], lengths[i], lengths[i]);
+ else
+ xmlencode_print(cur[i], lengths[i]);
+ tee_fprintf(PAGER, "</field>\n");
+ }
+ else
+ tee_fprintf(PAGER, "\" xsi:nil=\"true\" />\n");
+ }
+ (void) tee_fputs(" </row>\n", PAGER);
+ }
+ (void) tee_fputs("</resultset>\n", PAGER);
+}
+
+
+static void
+print_table_data_vertically(MYSQL_RES *result)
+{
+ MYSQL_ROW cur;
+ uint max_length=0;
+ MYSQL_FIELD *field;
+
+ while ((field = mysql_fetch_field(result)))
+ {
+ uint length= field->name_length;
+ if (length > max_length)
+ max_length= length;
+ field->max_length=length;
+ }
+
+ mysql_field_seek(result,0);
+ for (uint row_count=1; (cur= mysql_fetch_row(result)); row_count++)
+ {
+ if (interrupted_query)
+ break;
+ mysql_field_seek(result,0);
+ tee_fprintf(PAGER,
+ "*************************** %d. row ***************************\n", row_count);
+
+ ulong *lengths= mysql_fetch_lengths(result);
+
+ for (uint off=0; off < mysql_num_fields(result); off++)
+ {
+ field= mysql_fetch_field(result);
+ if (column_names)
+ tee_fprintf(PAGER, "%*s: ",(int) max_length,field->name);
+ if (cur[off])
+ {
+ unsigned int i;
+ const char *p;
+ if (opt_binhex && is_binary_field(field))
+ fprintf(PAGER, "0x");
+ for (i= 0, p= cur[off]; i < lengths[off]; i+= 1, p+= 1)
+ {
+ if (opt_binhex && is_binary_field(field))
+ fprintf(PAGER, "%02X", *((uchar*)p));
+ else
+ {
+ if (*p == '\0')
+ tee_putc((int)' ', PAGER);
+ else
+ tee_putc((int)*p, PAGER);
+ }
+ }
+ tee_putc('\n', PAGER);
+ }
+ else
+ tee_fprintf(PAGER, "NULL\n");
+ }
+ }
+}
+
+/* print_warnings should be called right after executing a statement */
+
+static void print_warnings()
+{
+ const char *query;
+ MYSQL_RES *result;
+ MYSQL_ROW cur;
+ my_ulonglong num_rows;
+
+ /* Save current error before calling "show warnings" */
+ uint error= mysql_errno(&mysql);
+
+ /* Get the warnings */
+ query= "show warnings";
+ mysql_real_query_for_lazy(query, strlen(query));
+ mysql_store_result_for_lazy(&result);
+
+ /* Bail out when no warnings */
+ if (!result || !(num_rows= mysql_num_rows(result)))
+ goto end;
+
+ cur= mysql_fetch_row(result);
+
+ /*
+ Don't print a duplicate of the current error. It is possible for SHOW
+ WARNINGS to return multiple errors with the same code, but different
+ messages. To be safe, skip printing the duplicate only if it is the only
+ warning.
+ */
+ if (!cur || (num_rows == 1 && error == (uint) strtoul(cur[1], NULL, 10)))
+ goto end;
+
+ /* Print the warnings */
+ init_pager();
+ do
+ {
+ tee_fprintf(PAGER, "%s (Code %s): %s\n", cur[0], cur[1], cur[2]);
+ } while ((cur= mysql_fetch_row(result)));
+ end_pager();
+
+end:
+ mysql_free_result(result);
+}
+
+
+static const char *array_value(const char **array, char key)
+{
+ for (; *array; array+= 2)
+ if (**array == key)
+ return array[1];
+ return 0;
+}
+
+
+static void
+xmlencode_print(const char *src, uint length)
+{
+ if (!src)
+ tee_fputs("NULL", PAGER);
+ else
+ {
+ for (const char *p = src; length; p++, length--)
+ {
+ const char *t;
+ if ((t = array_value(xmlmeta, *p)))
+ tee_fputs(t, PAGER);
+ else
+ tee_putc(*p, PAGER);
+ }
+ }
+}
+
+
+static void
+safe_put_field(const char *pos,ulong length)
+{
+ if (!pos)
+ tee_fputs("NULL", PAGER);
+ else
+ {
+ if (opt_raw_data)
+ {
+ unsigned long i;
+ /* Can't use tee_fputs(), it stops with NUL characters. */
+ for (i= 0; i < length; i++, pos++)
+ tee_putc(*pos, PAGER);
+ }
+ else for (const char *end=pos+length ; pos != end ; pos++)
+ {
+#ifdef USE_MB
+ int l;
+ if (charset_info->use_mb() &&
+ (l = my_ismbchar(charset_info, pos, end)))
+ {
+ while (l--)
+ tee_putc(*pos++, PAGER);
+ pos--;
+ continue;
+ }
+#endif
+ if (!*pos)
+ tee_fputs("\\0", PAGER); // This makes everything hard
+ else if (*pos == '\t')
+ tee_fputs("\\t", PAGER); // This would destroy tab format
+ else if (*pos == '\n')
+ tee_fputs("\\n", PAGER); // This too
+ else if (*pos == '\\')
+ tee_fputs("\\\\", PAGER);
+ else
+ tee_putc(*pos, PAGER);
+ }
+ }
+}
+
+
+static void
+print_tab_data(MYSQL_RES *result)
+{
+ MYSQL_ROW cur;
+ MYSQL_FIELD *field;
+ ulong *lengths;
+
+ if (opt_silent < 2 && column_names)
+ {
+ int first=0;
+ while ((field = mysql_fetch_field(result)))
+ {
+ if (first++)
+ (void) tee_fputs("\t", PAGER);
+ (void) tee_fputs(field->name, PAGER);
+ }
+ (void) tee_fputs("\n", PAGER);
+ }
+ while ((cur = mysql_fetch_row(result)))
+ {
+ lengths=mysql_fetch_lengths(result);
+ field= mysql_fetch_fields(result);
+ if (opt_binhex && is_binary_field(&field[0]))
+ print_as_hex(PAGER, cur[0], lengths[0], lengths[0]);
+ else
+ safe_put_field(cur[0],lengths[0]);
+
+ for (uint off=1 ; off < mysql_num_fields(result); off++)
+ {
+ (void) tee_fputs("\t", PAGER);
+ if (opt_binhex && field && is_binary_field(&field[off]))
+ print_as_hex(PAGER, cur[off], lengths[off], lengths[off]);
+ else
+ safe_put_field(cur[off], lengths[off]);
+ }
+ (void) tee_fputs("\n", PAGER);
+ }
+}
+
+static int
+com_tee(String *buffer __attribute__((unused)),
+ char *line __attribute__((unused)))
+{
+ char file_name[FN_REFLEN], *end, *param;
+
+ if (status.batch)
+ return 0;
+ while (my_isspace(charset_info,*line))
+ line++;
+ if (!(param = strchr(line, ' '))) // if outfile wasn't given, use the default
+ {
+ if (!strlen(outfile))
+ {
+ printf("No previous outfile available, you must give a filename!\n");
+ return 0;
+ }
+ else if (opt_outfile)
+ {
+ tee_fprintf(stdout, "Currently logging to file '%s'\n", outfile);
+ return 0;
+ }
+ else
+ param = outfile; //resume using the old outfile
+ }
+
+ /* eliminate the spaces before the parameters */
+ while (my_isspace(charset_info,*param))
+ param++;
+ end= strmake_buf(file_name, param);
+ /* remove end space from command line */
+ while (end > file_name && (my_isspace(charset_info,end[-1]) ||
+ my_iscntrl(charset_info,end[-1])))
+ end--;
+ end[0]= 0;
+ if (end == file_name)
+ {
+ printf("No outfile specified!\n");
+ return 0;
+ }
+ init_tee(file_name);
+ return 0;
+}
+
+
+static int
+com_notee(String *buffer __attribute__((unused)),
+ char *line __attribute__((unused)))
+{
+ if (opt_outfile)
+ end_tee();
+ tee_fprintf(stdout, "Outfile disabled.\n");
+ return 0;
+}
+
+/*
+ Sorry, this command is not available in Windows.
+*/
+
+#ifdef USE_POPEN
+static int
+com_pager(String *buffer __attribute__((unused)),
+ char *line __attribute__((unused)))
+{
+ char pager_name[FN_REFLEN], *end, *param;
+
+ if (status.batch)
+ return 0;
+ /* Skip spaces in front of the pager command */
+ while (my_isspace(charset_info, *line))
+ line++;
+ /* Skip the pager command */
+ param= strchr(line, ' ');
+ /* Skip the spaces between the command and the argument */
+ while (param && my_isspace(charset_info, *param))
+ param++;
+ if (!param || !strlen(param)) // if pager was not given, use the default
+ {
+ if (!default_pager_set)
+ {
+ tee_fprintf(stdout, "Default pager wasn't set, using stdout.\n");
+ opt_nopager=1;
+ strmov(pager, "stdout");
+ PAGER= stdout;
+ return 0;
+ }
+ strmov(pager, default_pager);
+ }
+ else
+ {
+ end= strmake_buf(pager_name, param);
+ while (end > pager_name && (my_isspace(charset_info,end[-1]) ||
+ my_iscntrl(charset_info,end[-1])))
+ end--;
+ end[0]=0;
+ strmov(pager, pager_name);
+ strmov(default_pager, pager_name);
+ }
+ opt_nopager=0;
+ tee_fprintf(stdout, "PAGER set to '%s'\n", pager);
+ return 0;
+}
+
+
+static int
+com_nopager(String *buffer __attribute__((unused)),
+ char *line __attribute__((unused)))
+{
+ strmov(pager, "stdout");
+ opt_nopager=1;
+ PAGER= stdout;
+ tee_fprintf(stdout, "PAGER set to stdout\n");
+ return 0;
+}
+#endif
+
+#ifdef USE_POPEN
+static int
+com_edit(String *buffer,char *line __attribute__((unused)))
+{
+ char filename[FN_REFLEN],buff[160];
+ int fd,tmp,error;
+ const char *editor;
+ MY_STAT stat_arg;
+
+ if ((fd= create_temp_file(filename,NullS,"sql", 0, MYF(MY_WME))) < 0)
+ goto err;
+ if (buffer->is_empty() && !old_buffer.is_empty())
+ (void) my_write(fd,(uchar*) old_buffer.ptr(),old_buffer.length(),
+ MYF(MY_WME));
+ else
+ (void) my_write(fd,(uchar*) buffer->ptr(),buffer->length(),MYF(MY_WME));
+ (void) my_close(fd,MYF(0));
+
+ if (!(editor = (char *)getenv("EDITOR")) &&
+ !(editor = (char *)getenv("VISUAL")))
+ editor = IF_WIN("notepad","vi");
+ strxmov(buff,editor," ",filename,NullS);
+ if ((error= system(buff)))
+ {
+ char errmsg[100];
+ sprintf(errmsg, "Command '%.40s' failed", buff);
+ put_info(errmsg, INFO_ERROR, 0, NullS);
+ goto err;
+ }
+
+ if (!my_stat(filename,&stat_arg,MYF(MY_WME)))
+ goto err;
+ if ((fd = my_open(filename,O_RDONLY, MYF(MY_WME))) < 0)
+ goto err;
+ (void) buffer->alloc((uint) stat_arg.st_size);
+ if ((tmp=(int)my_read(fd,(uchar*) buffer->ptr(),buffer->alloced_length(),MYF(0))) >= 0)
+ buffer->length((uint) tmp);
+ else
+ buffer->length(0);
+ (void) my_close(fd,MYF(0));
+ (void) my_delete(filename,MYF(MY_WME));
+err:
+ return 0;
+}
+#endif
+
+
+/* If arg is given, exit without errors. This happens on command 'quit' */
+
+static int
+com_quit(String *buffer __attribute__((unused)),
+ char *line __attribute__((unused)))
+{
+ status.exit_status=0;
+ return 1;
+}
+
+static int
+com_rehash(String *buffer __attribute__((unused)),
+ char *line __attribute__((unused)))
+{
+#ifdef HAVE_READLINE
+ build_completion_hash(1, 0);
+#endif
+ return 0;
+}
+
+
+#ifdef USE_POPEN
+static int
+com_shell(String *buffer __attribute__((unused)),
+ char *line __attribute__((unused)))
+{
+ char *shell_cmd;
+
+ /* Skip space from line begin */
+ while (my_isspace(charset_info, *line))
+ line++;
+ if (!(shell_cmd = strchr(line, ' ')))
+ {
+ put_info("Usage: \\! shell-command", INFO_ERROR);
+ return -1;
+ }
+ /*
+ The output of the shell command does not
+ get directed to the pager or the outfile
+ */
+ if (system(shell_cmd) == -1)
+ {
+ put_info(strerror(errno), INFO_ERROR, errno);
+ return -1;
+ }
+ return 0;
+}
+#endif
+
+
+static int
+com_print(String *buffer,char *line __attribute__((unused)))
+{
+ tee_puts("--------------", stdout);
+ (void) tee_fputs(buffer->c_ptr(), stdout);
+ if (!buffer->length() || (*buffer)[buffer->length()-1] != '\n')
+ tee_putc('\n', stdout);
+ tee_puts("--------------\n", stdout);
+ return 0; /* If empty buffer */
+}
+
+ /* ARGSUSED */
+static int
+com_connect(String *buffer, char *line)
+{
+ char *tmp, buff[256];
+ my_bool save_rehash= opt_rehash;
+ int error;
+
+ bzero(buff, sizeof(buff));
+ if (buffer)
+ {
+ /*
+ Two null bytes are needed in the end of buff to allow
+ get_arg to find end of string the second time it's called.
+ */
+ tmp= strmake(buff, line, sizeof(buff)-2);
+#ifdef EXTRA_DEBUG
+ tmp[1]= 0;
+#endif
+ tmp= get_arg(buff, GET);
+ if (tmp && *tmp)
+ {
+ my_free(current_db);
+ current_db= my_strdup(PSI_NOT_INSTRUMENTED, tmp, MYF(MY_WME));
+ tmp= get_arg(buff, GET_NEXT);
+ if (tmp)
+ {
+ my_free(current_host);
+ current_host=my_strdup(PSI_NOT_INSTRUMENTED, tmp,MYF(MY_WME));
+ }
+ }
+ else
+ {
+ /* Quick re-connect */
+ opt_rehash= 0; /* purecov: tested */
+ }
+ buffer->length(0); // command used
+ }
+ else
+ opt_rehash= 0;
+ error=sql_connect(current_host,current_db,current_user,opt_password,0);
+ opt_rehash= save_rehash;
+
+ if (connected)
+ {
+ sprintf(buff,"Connection id: %lu",mysql_thread_id(&mysql));
+ put_info(buff,INFO_INFO);
+ sprintf(buff,"Current database: %.128s\n",
+ current_db ? current_db : "*** NONE ***");
+ put_info(buff,INFO_INFO);
+ }
+ return error;
+}
+
+
+static int com_source(String *buffer __attribute__((unused)),
+ char *line)
+{
+ char source_name[FN_REFLEN], *end, *param;
+ LINE_BUFFER *line_buff;
+ int error;
+ STATUS old_status;
+ FILE *sql_file;
+ my_bool save_ignore_errors;
+
+ /* Skip space from file name */
+ while (my_isspace(charset_info,*line))
+ line++;
+ if (!(param = strchr(line, ' '))) // Skip command name
+ return put_info("Usage: \\. <filename> | source <filename>",
+ INFO_ERROR, 0);
+ while (my_isspace(charset_info,*param))
+ param++;
+ end=strmake_buf(source_name, param);
+ while (end > source_name && (my_isspace(charset_info,end[-1]) ||
+ my_iscntrl(charset_info,end[-1])))
+ end--;
+ end[0]=0;
+ unpack_filename(source_name,source_name);
+ /* open file name */
+ if (!(sql_file = my_fopen(source_name, O_RDONLY | O_BINARY,MYF(0))))
+ {
+ char buff[FN_REFLEN+60];
+ sprintf(buff,"Failed to open file '%s', error: %d", source_name,errno);
+ return put_info(buff, INFO_ERROR, 0);
+ }
+
+ if (!(line_buff= batch_readline_init(MAX_BATCH_BUFFER_SIZE, sql_file)))
+ {
+ my_fclose(sql_file,MYF(0));
+ return put_info("Can't initialize batch_readline", INFO_ERROR, 0);
+ }
+
+ /* Save old status */
+ old_status=status;
+ save_ignore_errors= ignore_errors;
+ bfill((char*) &status,sizeof(status),(char) 0);
+
+ status.batch=old_status.batch; // Run in batch mode
+ status.line_buff=line_buff;
+ status.file_name=source_name;
+ glob_buffer.length(0); // Empty command buffer
+ ignore_errors= !batch_abort_on_error;
+ in_com_source= 1;
+ error= read_and_execute(false);
+ ignore_errors= save_ignore_errors;
+ status=old_status; // Continue as before
+ in_com_source= aborted= 0;
+ my_fclose(sql_file,MYF(0));
+ batch_readline_end(line_buff);
+ /*
+ If we got an error during source operation, don't abort the client
+ if ignore_errors is set
+ */
+ if (error && ignore_errors)
+ error= -1; // Ignore error
+ return error;
+}
+
+
+ /* ARGSUSED */
+static int
+com_delimiter(String *buffer __attribute__((unused)), char *line)
+{
+ char buff[256], *tmp;
+
+ strmake_buf(buff, line);
+ tmp= get_arg(buff, GET);
+
+ if (!tmp || !*tmp)
+ {
+ put_info("DELIMITER must be followed by a 'delimiter' character or string",
+ INFO_ERROR);
+ return 0;
+ }
+ else
+ {
+ if (strstr(tmp, "\\"))
+ {
+ put_info("DELIMITER cannot contain a backslash character", INFO_ERROR);
+ return 0;
+ }
+ }
+ strmake_buf(delimiter, tmp);
+ delimiter_length= (int)strlen(delimiter);
+ delimiter_str= delimiter;
+ return 0;
+}
+
+ /* ARGSUSED */
+static int
+com_use(String *buffer __attribute__((unused)), char *line)
+{
+ char *tmp, buff[FN_REFLEN + 1];
+ int select_db;
+
+ bzero(buff, sizeof(buff));
+ strmake_buf(buff, line);
+ tmp= get_arg(buff, GET);
+ if (!tmp || !*tmp)
+ {
+ put_info("USE must be followed by a database name", INFO_ERROR);
+ return 0;
+ }
+ /*
+ We need to recheck the current database, because it may change
+ under our feet, for example if DROP DATABASE or RENAME DATABASE
+ (latter one not yet available by the time the comment was written)
+ */
+ get_current_db();
+
+ if (!current_db || cmp_database(charset_info, current_db,tmp))
+ {
+ if (one_database)
+ {
+ skip_updates= 1;
+ select_db= 0; // don't do mysql_select_db()
+ }
+ else
+ select_db= 2; // do mysql_select_db() and build_completion_hash()
+ }
+ else
+ {
+ /*
+ USE to the current db specified.
+ We do need to send mysql_select_db() to make server
+ update database level privileges, which might
+ change since last USE (see bug#10979).
+ For performance purposes, we'll skip rebuilding of completion hash.
+ */
+ skip_updates= 0;
+ select_db= 1; // do only mysql_select_db(), without completion
+ }
+
+ if (select_db)
+ {
+ /*
+ reconnect once if connection is down or if connection was found to
+ be down during query
+ */
+ if (!connected && reconnect())
+ return opt_reconnect ? -1 : 1; // Fatal error
+ if (mysql_select_db(&mysql,tmp))
+ {
+ if (mysql_errno(&mysql) != CR_SERVER_GONE_ERROR)
+ return put_error(&mysql);
+
+ if (reconnect())
+ return opt_reconnect ? -1 : 1; // Fatal error
+ if (mysql_select_db(&mysql,tmp))
+ return put_error(&mysql);
+ }
+ my_free(current_db);
+ current_db=my_strdup(PSI_NOT_INSTRUMENTED, tmp,MYF(MY_WME));
+#ifdef HAVE_READLINE
+ if (select_db > 1)
+ build_completion_hash(opt_rehash, 1);
+#endif
+ }
+
+ put_info("Database changed",INFO_INFO);
+ return 0;
+}
+
+static int
+com_warnings(String *buffer __attribute__((unused)),
+ char *line __attribute__((unused)))
+{
+ show_warnings = 1;
+ put_info("Show warnings enabled.",INFO_INFO);
+ return 0;
+}
+
+static int
+com_nowarnings(String *buffer __attribute__((unused)),
+ char *line __attribute__((unused)))
+{
+ show_warnings = 0;
+ put_info("Show warnings disabled.",INFO_INFO);
+ return 0;
+}
+
+/*
+ Gets argument from a command on the command line. If mode is not GET_NEXT,
+ skips the command and returns the first argument. The line is modified by
+ adding zero to the end of the argument. If mode is GET_NEXT, then the
+ function searches for end of string first, after found, returns the next
+ argument and adds zero to the end. If you ever wish to use this feature,
+ remember to initialize all items in the array to zero first.
+*/
+
+static char *get_arg(char *line, get_arg_mode mode)
+{
+ char *ptr, *start;
+ bool short_cmd= false;
+ char qtype= 0;
+
+ ptr= line;
+ if (mode == GET_NEXT)
+ {
+ for (; *ptr; ptr++) ;
+ if (*(ptr + 1))
+ ptr++;
+ }
+ else
+ {
+ /* skip leading white spaces */
+ while (my_isspace(charset_info, *ptr))
+ ptr++;
+ if ((short_cmd= *ptr == '\\')) // short command was used
+ ptr+= 2;
+ else
+ while (*ptr &&!my_isspace(charset_info, *ptr)) // skip command
+ ptr++;
+ }
+ if (!*ptr)
+ return NullS;
+ while (my_isspace(charset_info, *ptr))
+ ptr++;
+ if (*ptr == '\'' || *ptr == '\"' || *ptr == '`')
+ {
+ qtype= *ptr;
+ ptr++;
+ }
+ for (start=ptr ; *ptr; ptr++)
+ {
+ /* if short_cmd use historical rules (only backslash) otherwise SQL rules */
+ if (short_cmd
+ ? (*ptr == '\\' && ptr[1]) // escaped character
+ : (*ptr == '\\' && ptr[1] && qtype != '`') || // escaped character
+ (qtype && *ptr == qtype && ptr[1] == qtype)) // quote
+ {
+ // Remove (or skip) the backslash (or a second quote)
+ if (mode != CHECK)
+ strmov_overlapp(ptr, ptr+1);
+ else
+ ptr++;
+ }
+ else if (*ptr == (qtype ? qtype : ' '))
+ {
+ qtype= 0;
+ if (mode != CHECK)
+ *ptr= 0;
+ break;
+ }
+ }
+ return ptr != start && !qtype ? start : NullS;
+}
+
+
+/**
+ An example of mysql_authentication_dialog_ask callback.
+
+ The C function with the name "mysql_authentication_dialog_ask", if exists,
+ will be used by the "dialog" client authentication plugin when user
+ input is needed. This function should be of mysql_authentication_dialog_ask_t
+ type. If the function does not exists, a built-in implementation will be
+ used.
+
+ @param mysql mysql
+ @param type type of the input
+ 1 - normal string input
+ 2 - password string
+ @param prompt prompt
+ @param buf a buffer to store the use input
+ @param buf_len the length of the buffer
+
+ @retval a pointer to the user input string.
+ It may be equal to 'buf' or to 'mysql->password'.
+ In all other cases it is assumed to be an allocated
+ string, and the "dialog" plugin will free() it.
+*/
+
+extern "C"
+#ifdef _MSC_VER
+__declspec(dllexport)
+#endif
+char *mysql_authentication_dialog_ask(MYSQL *mysql, int type,
+ const char *prompt,
+ char *buf, int buf_len)
+{
+ char *s=buf;
+
+ fputs("[mariadb] ", stdout);
+ fputs(prompt, stdout);
+ fputs(" ", stdout);
+
+ if (type == 2) /* password */
+ {
+ s= my_get_tty_password("");
+ strnmov(buf, s, buf_len);
+ buf[buf_len-1]= 0;
+ my_free(s);
+ }
+ else
+ {
+ if (!fgets(buf, buf_len-1, stdin))
+ buf[0]= 0;
+ else if (buf[0] && (s= strend(buf))[-1] == '\n')
+ s[-1]= 0;
+ }
+
+ return buf;
+}
+
+static int
+sql_real_connect(char *host,char *database,char *user,char *password,
+ uint silent)
+{
+ const char *charset_name;
+
+ if (connected)
+ {
+ connected= 0;
+ mysql_close(&mysql);
+ }
+ mysql_init(&mysql);
+ if (opt_init_command)
+ mysql_options(&mysql, MYSQL_INIT_COMMAND, opt_init_command);
+ if (opt_connect_timeout)
+ {
+ uint timeout=opt_connect_timeout;
+ mysql_options(&mysql,MYSQL_OPT_CONNECT_TIMEOUT,
+ (char*) &timeout);
+ }
+ if (opt_compress)
+ mysql_options(&mysql,MYSQL_OPT_COMPRESS,NullS);
+ if (using_opt_local_infile)
+ mysql_options(&mysql,MYSQL_OPT_LOCAL_INFILE, (char*) &opt_local_infile);
+ if (safe_updates)
+ {
+ char init_command[100];
+ sprintf(init_command,
+ "SET SQL_SAFE_UPDATES=1,SQL_SELECT_LIMIT=%lu,MAX_JOIN_SIZE=%lu",
+ select_limit,max_join_size);
+ mysql_options(&mysql, MYSQL_INIT_COMMAND, init_command);
+ }
+ if (!strcmp(default_charset,MYSQL_AUTODETECT_CHARSET_NAME))
+ default_charset= (char *)my_default_csname();
+ mysql_options(&mysql, MYSQL_SET_CHARSET_NAME, default_charset);
+
+ my_bool can_handle_expired= opt_connect_expired_password || !status.batch;
+ mysql_options(&mysql, MYSQL_OPT_CAN_HANDLE_EXPIRED_PASSWORDS, &can_handle_expired);
+
+ if (!do_connect(&mysql, host, user, password, database,
+ connect_flag | CLIENT_MULTI_STATEMENTS))
+ {
+ if (!silent ||
+ (mysql_errno(&mysql) != CR_CONN_HOST_ERROR &&
+ mysql_errno(&mysql) != CR_CONNECTION_ERROR))
+ {
+ (void) put_error(&mysql);
+ (void) fflush(stdout);
+ return ignore_errors ? -1 : 1; // Abort
+ }
+ return -1; // Retryable
+ }
+
+ charset_name= IF_EMBEDDED(mysql.charset->coll_name.str,
+ mysql.charset->name);
+ charset_info= get_charset_by_name(charset_name, MYF(MY_UTF8_IS_UTF8MB3));
+ if (!charset_info)
+ {
+ char buff[128];
+ my_snprintf(buff, sizeof(buff)-1,
+ "Unknown default character set %s", charset_name);
+ put_info(buff, INFO_ERROR);
+ return 1;
+ }
+ adjust_console_codepage(charset_info->cs_name.str);
+ connected=1;
+#ifndef EMBEDDED_LIBRARY
+ mysql_options(&mysql, MYSQL_OPT_RECONNECT, &debug_info_flag);
+
+ /*
+ CLIENT_PROGRESS_OBSOLETE is set only if we requested it in
+ mysql_real_connect() and the server also supports it
+*/
+ if (mysql.client_flag & CLIENT_PROGRESS_OBSOLETE)
+ mysql_options(&mysql, MYSQL_PROGRESS_CALLBACK, (void*) report_progress);
+#else
+ {
+ my_bool reconnect= 1;
+ mysql_options(&mysql, MYSQL_OPT_RECONNECT, &reconnect);
+ }
+#endif
+#ifdef HAVE_READLINE
+ build_completion_hash(opt_rehash, 1);
+#endif
+ return 0;
+}
+
+
+static int
+sql_connect(char *host,char *database,char *user,char *password,uint silent)
+{
+ bool message=0;
+ uint count=0;
+ int error;
+ for (;;)
+ {
+ if ((error=sql_real_connect(host,database,user,password,wait_flag)) >= 0)
+ {
+ if (count)
+ {
+ tee_fputs("\n", stderr);
+ (void) fflush(stderr);
+ }
+ return error;
+ }
+ if (!wait_flag)
+ return ignore_errors ? -1 : 1;
+ if (!message && !silent)
+ {
+ message=1;
+ tee_fputs("Waiting",stderr); (void) fflush(stderr);
+ }
+ (void) sleep(wait_time);
+ if (!silent)
+ {
+ putc('.',stderr); (void) fflush(stderr);
+ count++;
+ }
+ }
+}
+
+
+
+static int
+com_status(String *buffer __attribute__((unused)),
+ char *line __attribute__((unused)))
+{
+ const char *status_str;
+ char buff[40];
+ ulonglong id;
+ MYSQL_RES *UNINIT_VAR(result);
+
+ if (mysql_real_query_for_lazy(
+ C_STRING_WITH_LEN("select DATABASE(), USER() limit 1")))
+ return 0;
+
+ tee_puts("--------------", stdout);
+ usage(1); /* Print version */
+ tee_fprintf(stdout, "\nConnection id:\t\t%lu\n",mysql_thread_id(&mysql));
+ /*
+ Don't remove "limit 1",
+ it is protection against SQL_SELECT_LIMIT=0
+ */
+ if (!mysql_store_result_for_lazy(&result))
+ {
+ MYSQL_ROW cur=mysql_fetch_row(result);
+ if (cur)
+ {
+ tee_fprintf(stdout, "Current database:\t%s\n", cur[0] ? cur[0] : "");
+ tee_fprintf(stdout, "Current user:\t\t%s\n", cur[1]);
+ }
+ mysql_free_result(result);
+ }
+
+#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
+ if ((status_str= mysql_get_ssl_cipher(&mysql)))
+ tee_fprintf(stdout, "SSL:\t\t\tCipher in use is %s\n",
+ status_str);
+ else
+#endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */
+ tee_puts("SSL:\t\t\tNot in use", stdout);
+
+ if (skip_updates)
+ {
+ my_vidattr(A_BOLD);
+ tee_fprintf(stdout, "\nAll updates ignored to this database\n");
+ my_vidattr(A_NORMAL);
+ }
+#ifdef USE_POPEN
+ tee_fprintf(stdout, "Current pager:\t\t%s\n", pager);
+ tee_fprintf(stdout, "Using outfile:\t\t'%s'\n", opt_outfile ? outfile : "");
+#endif
+ tee_fprintf(stdout, "Using delimiter:\t%s\n", delimiter);
+ tee_fprintf(stdout, "Server:\t\t\t%s\n", mysql_get_server_name(&mysql));
+ tee_fprintf(stdout, "Server version:\t\t%s\n", server_version_string(&mysql));
+ tee_fprintf(stdout, "Protocol version:\t%d\n", mysql_get_proto_info(&mysql));
+ tee_fprintf(stdout, "Connection:\t\t%s\n", mysql_get_host_info(&mysql));
+ if ((id= mysql_insert_id(&mysql)))
+ tee_fprintf(stdout, "Insert id:\t\t%s\n", llstr(id, buff));
+
+ /* "limit 1" is protection against SQL_SELECT_LIMIT=0 */
+ if (mysql_real_query_for_lazy(C_STRING_WITH_LEN(
+ "select @@character_set_client, @@character_set_connection, "
+ "@@character_set_server, @@character_set_database limit 1")))
+ {
+ if (mysql_errno(&mysql) == CR_SERVER_GONE_ERROR)
+ return 0;
+ }
+ if (!mysql_store_result_for_lazy(&result))
+ {
+ MYSQL_ROW cur=mysql_fetch_row(result);
+ if (cur)
+ {
+ tee_fprintf(stdout, "Server characterset:\t%s\n", cur[2] ? cur[2] : "");
+ tee_fprintf(stdout, "Db characterset:\t%s\n", cur[3] ? cur[3] : "");
+ tee_fprintf(stdout, "Client characterset:\t%s\n", cur[0] ? cur[0] : "");
+ tee_fprintf(stdout, "Conn. characterset:\t%s\n", cur[1] ? cur[1] : "");
+ }
+ mysql_free_result(result);
+ }
+ else
+ {
+ /* Probably pre-4.1 server */
+ tee_fprintf(stdout, "Client characterset:\t%s\n", charset_info->cs_name.str);
+ tee_fprintf(stdout, "Server characterset:\t%s\n",
+ mysql_character_set_name(&mysql));
+ }
+
+#ifndef EMBEDDED_LIBRARY
+ if (strstr(mysql_get_host_info(&mysql),"TCP/IP") || ! mysql.unix_socket)
+ tee_fprintf(stdout, "TCP port:\t\t%d\n", mysql.port);
+ else
+ tee_fprintf(stdout, "UNIX socket:\t\t%s\n", mysql.unix_socket);
+ if (mysql.net.compress)
+ tee_fprintf(stdout, "Protocol:\t\tCompressed\n");
+#endif
+
+ const char *pos;
+ if ((status_str= mysql_stat(&mysql)) && !mysql_error(&mysql)[0] &&
+ (pos= strchr(status_str,' ')))
+ {
+ ulong sec;
+ /* print label */
+ tee_fprintf(stdout, "%.*s\t\t\t", (int) (pos-status_str), status_str);
+ if ((status_str= str2int(pos,10,0,LONG_MAX,(long*) &sec)))
+ {
+ nice_time((double) sec,buff,0);
+ tee_puts(buff, stdout); /* print nice time */
+ while (*status_str == ' ')
+ status_str++; /* to next info */
+ tee_putc('\n', stdout);
+ tee_puts(status_str, stdout);
+ }
+ }
+ if (safe_updates)
+ {
+ my_vidattr(A_BOLD);
+ tee_fprintf(stdout, "\nNote that you are running in safe_update_mode:\n");
+ my_vidattr(A_NORMAL);
+ tee_fprintf(stdout, "\
+UPDATEs and DELETEs that don't use a key in the WHERE clause are not allowed.\n\
+(One can force an UPDATE/DELETE by adding LIMIT # at the end of the command.)\n\
+SELECT has an automatic 'LIMIT %lu' if LIMIT is not used.\n\
+Max number of examined row combination in a join is set to: %lu\n\n",
+select_limit, max_join_size);
+ }
+ tee_puts("--------------\n", stdout);
+ return 0;
+}
+
+static const char *
+server_version_string(MYSQL *con)
+{
+ /* Only one thread calls this, so no synchronization is needed */
+ if (server_version == NULL)
+ {
+ MYSQL_RES *result;
+
+ /* "limit 1" is protection against SQL_SELECT_LIMIT=0 */
+ if (!mysql_query(con, "select @@version_comment limit 1") &&
+ (result = mysql_use_result(con)))
+ {
+ MYSQL_ROW cur = mysql_fetch_row(result);
+ if (cur && cur[0])
+ {
+ /* version, space, comment, \0 */
+ size_t len= strlen(mysql_get_server_info(con)) + strlen(cur[0]) + 2;
+
+ if ((server_version= (char *) my_malloc(PSI_NOT_INSTRUMENTED, len, MYF(MY_WME))))
+ {
+ char *bufp;
+ bufp = strmov(server_version, mysql_get_server_info(con));
+ bufp = strmov(bufp, " ");
+ (void) strmov(bufp, cur[0]);
+ }
+ }
+ mysql_free_result(result);
+ }
+
+ /*
+ If for some reason we didn't get a version_comment, we'll
+ keep things simple.
+ */
+
+ if (server_version == NULL)
+ server_version= my_strdup(PSI_NOT_INSTRUMENTED, mysql_get_server_info(con), MYF(MY_WME));
+ }
+
+ return server_version ? server_version : "";
+}
+
+static int
+put_info(const char *str,INFO_TYPE info_type, uint error, const char *sqlstate)
+{
+ FILE *file= (info_type == INFO_ERROR ? stderr : stdout);
+ static int inited=0;
+
+ if (status.batch)
+ {
+ if (info_type == INFO_ERROR)
+ {
+ (void) fflush(file);
+ fprintf(file,"ERROR");
+ if (error)
+ {
+ if (sqlstate)
+ (void) fprintf(file," %d (%s)",error, sqlstate);
+ else
+ (void) fprintf(file," %d",error);
+ }
+ if (status.query_start_line && line_numbers)
+ {
+ (void) fprintf(file," at line %lu",status.query_start_line);
+ if (status.file_name)
+ (void) fprintf(file," in file: '%s'", status.file_name);
+ }
+ (void) fprintf(file,": %s\n",str);
+ (void) fflush(file);
+ if (!ignore_errors)
+ return 1;
+ }
+ else if (info_type == INFO_RESULT && verbose > 1)
+ tee_puts(str, file);
+ if (unbuffered)
+ fflush(file);
+ return info_type == INFO_ERROR ? -1 : 0;
+ }
+ if (!opt_silent || info_type == INFO_ERROR)
+ {
+ if (!inited)
+ {
+#ifdef HAVE_SETUPTERM
+ int errret;
+ have_curses= setupterm((char *)0, 1, &errret) != ERR;
+#endif
+ inited=1;
+ }
+ if (info_type == INFO_ERROR)
+ {
+ if (!opt_nobeep)
+ {
+#ifdef _WIN32
+ MessageBeep(MB_ICONWARNING);
+#else
+ putchar('\a'); /* This should make a bell */
+#endif
+ }
+ my_vidattr(A_STANDOUT);
+ if (error)
+ {
+ if (sqlstate)
+ (void) tee_fprintf(file, "ERROR %d (%s)", error, sqlstate);
+ else
+ (void) tee_fprintf(file, "ERROR %d", error);
+ }
+ else
+ tee_fputs("ERROR", file);
+ if (status.query_start_line && line_numbers)
+ {
+ (void) fprintf(file," at line %lu",status.query_start_line);
+ if (status.file_name)
+ (void) fprintf(file," in file: '%s'", status.file_name);
+ }
+ tee_fputs(": ", file);
+ }
+ else
+ my_vidattr(A_BOLD);
+ (void) tee_puts(str, file);
+ my_vidattr(A_NORMAL);
+ }
+ if (unbuffered)
+ fflush(file);
+ return info_type == INFO_ERROR ? (ignore_errors ? -1 : 1): 0;
+}
+
+
+static int
+put_error(MYSQL *con)
+{
+ return put_info(mysql_error(con), INFO_ERROR, mysql_errno(con),
+ mysql_sqlstate(con));
+}
+
+
+static void remove_cntrl(String &buffer)
+{
+ char *start,*end;
+ end=(start=(char*) buffer.ptr())+buffer.length();
+ while (start < end && !my_isgraph(charset_info,end[-1]))
+ end--;
+ buffer.length((uint) (end-start));
+}
+
+
+void tee_fprintf(FILE *file, const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ (void) vfprintf(file, fmt, args);
+ va_end(args);
+
+ if (opt_outfile)
+ {
+ va_start(args, fmt);
+ (void) vfprintf(OUTFILE, fmt, args);
+ va_end(args);
+ }
+}
+
+
+void tee_fputs(const char *s, FILE *file)
+{
+ fputs(s, file);
+ if (opt_outfile)
+ fputs(s, OUTFILE);
+}
+
+
+void tee_puts(const char *s, FILE *file)
+{
+ fputs(s, file);
+ fputc('\n', file);
+ if (opt_outfile)
+ {
+ fputs(s, OUTFILE);
+ fputc('\n', OUTFILE);
+ }
+}
+
+void tee_putc(int c, FILE *file)
+{
+ putc(c, file);
+ if (opt_outfile)
+ putc(c, OUTFILE);
+}
+
+
+/**
+ Write as many as 52+1 bytes to buff, in the form of a legible duration of time.
+
+ len("4294967296 days, 23 hours, 59 minutes, 60.000 seconds") -> 53
+*/
+static void nice_time(double sec,char *buff,bool part_second)
+{
+ ulong tmp;
+ if (sec >= 3600.0*24)
+ {
+ tmp=(ulong) floor(sec/(3600.0*24));
+ sec-=3600.0*24*tmp;
+ buff=int10_to_str((long) tmp, buff, 10);
+ buff=strmov(buff,tmp > 1 ? " days " : " day ");
+ }
+ if (sec >= 3600.0)
+ {
+ tmp=(ulong) floor(sec/3600.0);
+ sec-=3600.0*tmp;
+ buff=int10_to_str((long) tmp, buff, 10);
+ buff=strmov(buff,tmp > 1 ? " hours " : " hour ");
+ }
+ if (sec >= 60.0)
+ {
+ tmp=(ulong) floor(sec/60.0);
+ sec-=60.0*tmp;
+ buff=int10_to_str((long) tmp, buff, 10);
+ buff=strmov(buff," min ");
+ }
+ if (part_second)
+ sprintf(buff,"%.3f sec",sec);
+ else
+ sprintf(buff,"%d sec",(int) sec);
+}
+
+
+static void end_timer(ulonglong start_time, char *buff)
+{
+ double sec;
+
+ buff[0]=' ';
+ buff[1]='(';
+ sec= (microsecond_interval_timer() - start_time) / (double) (1000 * 1000);
+ nice_time(sec, buff + 2, 1);
+ strmov(strend(buff),")");
+}
+
+static const char *construct_prompt()
+{
+ processed_prompt.free(); // Erase the old prompt
+ time_t lclock = time(NULL); // Get the date struct
+ struct tm *t = localtime(&lclock);
+
+ /* parse through the settings for the prompt */
+ for (char *c = current_prompt; *c ; c++)
+ {
+ if (*c != PROMPT_CHAR)
+ processed_prompt.append(*c);
+ else
+ {
+ switch (*++c) {
+ case '\0':
+ c--; // stop it from going beyond if ends with %
+ break;
+ case 'c':
+ add_int_to_prompt(++prompt_counter);
+ break;
+ case 'v':
+ {
+ const char *info= (connected ?
+ mysql_get_server_info(&mysql) :
+ "not_connected");
+ processed_prompt.append(info, strlen(info));
+ break;
+ }
+ case 'd':
+ {
+ const char *db= current_db ? current_db : "(none)";
+ processed_prompt.append(db, strlen(db));
+ break;
+ }
+ case 'N':
+ {
+ const char *name= (connected ?
+ mysql_get_server_name(&mysql) :
+ "unknown");
+ processed_prompt.append(name, strlen(name));
+ break;
+ }
+ case 'h':
+ case 'H':
+ {
+ const char *prompt;
+ prompt= connected ? mysql_get_host_info(&mysql) : "not_connected";
+ if (strstr(prompt, "Localhost") || strstr(prompt, "localhost "))
+ {
+ if (*c == 'h')
+ processed_prompt.append(STRING_WITH_LEN("localhost"));
+ else
+ {
+ static char hostname[FN_REFLEN];
+ static size_t hostname_length;
+ if (hostname_length)
+ processed_prompt.append(hostname, hostname_length);
+ else if (gethostname(hostname, sizeof(hostname)) == 0)
+ {
+ hostname_length= strlen(hostname);
+ processed_prompt.append(hostname, hostname_length);
+ }
+ else
+ processed_prompt.append(STRING_WITH_LEN("gethostname(2) failed"));
+ }
+ }
+ else
+ {
+ const char *end=strcend(prompt,' ');
+ processed_prompt.append(prompt, (uint) (end-prompt));
+ }
+ break;
+ }
+ case 'p':
+ {
+#ifndef EMBEDDED_LIBRARY
+ if (!connected)
+ {
+ processed_prompt.append(STRING_WITH_LEN("not_connected"));
+ break;
+ }
+
+ const char *host_info = mysql_get_host_info(&mysql);
+ if (strstr(host_info, "memory"))
+ {
+ processed_prompt.append( mysql.host, strlen(mysql.host));
+ }
+ else if (strstr(host_info,"TCP/IP") ||
+ !mysql.unix_socket)
+ add_int_to_prompt(mysql.port);
+ else
+ {
+ char *pos= strrchr(mysql.unix_socket,'/');
+ const char *tmp= pos ? pos+1 : mysql.unix_socket;
+ processed_prompt.append(tmp, strlen(tmp));
+ }
+#endif
+ }
+ break;
+ case 'U':
+ {
+ const char *name;
+ if (!full_username)
+ init_username();
+ name= (full_username ? full_username :
+ (current_user ? current_user : "(unknown)"));
+ processed_prompt.append(name, strlen(name));
+ break;
+ }
+ case 'u':
+ {
+ const char *name;
+ if (!full_username)
+ init_username();
+ name= (part_username ? part_username :
+ (current_user ? current_user : "(unknown)"));
+ processed_prompt.append(name, strlen(name));
+ break;
+ }
+ case PROMPT_CHAR:
+ processed_prompt.append(PROMPT_CHAR);
+ break;
+ case 'n':
+ processed_prompt.append('\n');
+ break;
+ case ' ':
+ case '_':
+ processed_prompt.append(' ');
+ break;
+ case 'R':
+ if (t->tm_hour < 10)
+ processed_prompt.append('0');
+ add_int_to_prompt(t->tm_hour);
+ break;
+ case 'r':
+ int getHour;
+ getHour = t->tm_hour % 12;
+ if (getHour == 0)
+ getHour=12;
+ if (getHour < 10)
+ processed_prompt.append('0');
+ add_int_to_prompt(getHour);
+ break;
+ case 'm':
+ if (t->tm_min < 10)
+ processed_prompt.append('0');
+ add_int_to_prompt(t->tm_min);
+ break;
+ case 'y':
+ int getYear;
+ getYear = t->tm_year % 100;
+ if (getYear < 10)
+ processed_prompt.append('0');
+ add_int_to_prompt(getYear);
+ break;
+ case 'Y':
+ add_int_to_prompt(t->tm_year+1900);
+ break;
+ case 'D':
+ {
+ char* dateTime;
+ const char *tmp;
+ dateTime = ctime(&lclock);
+ tmp= strtok(dateTime,"\n");
+ processed_prompt.append(tmp, strlen(tmp));
+ break;
+ }
+ case 's':
+ if (t->tm_sec < 10)
+ processed_prompt.append('0');
+ add_int_to_prompt(t->tm_sec);
+ break;
+ case 'w':
+ {
+ const char *name= day_names[t->tm_wday];
+ processed_prompt.append(name, strlen(name));
+ break;
+ }
+ case 'P':
+ processed_prompt.append(t->tm_hour < 12 ? "am" : "pm", 2);
+ break;
+ case 'o':
+ add_int_to_prompt(t->tm_mon+1);
+ break;
+ case 'O':
+ {
+ const char *name= month_names[t->tm_mon];
+ processed_prompt.append(name, strlen(name));
+ break;
+ }
+ case '\'':
+ processed_prompt.append('\'');
+ break;
+ case '"':
+ processed_prompt.append('"');
+ break;
+ case 'S':
+ processed_prompt.append(';');
+ break;
+ case 't':
+ processed_prompt.append('\t');
+ break;
+ case 'l':
+ processed_prompt.append(delimiter_str, strlen(delimiter_str));
+ break;
+ default:
+ processed_prompt.append(*c);
+ }
+ }
+ }
+ processed_prompt.append('\0');
+ return processed_prompt.ptr();
+}
+
+
+static void add_int_to_prompt(int toadd)
+{
+ char buffer[16];
+ size_t length= (size_t) (int10_to_str(toadd,buffer,10) - buffer);
+ processed_prompt.append(buffer, length);
+}
+
+static void init_username()
+{
+ my_free(full_username);
+ my_free(part_username);
+
+ MYSQL_RES *UNINIT_VAR(result);
+ if (!mysql_query(&mysql,"select USER()") &&
+ (result=mysql_use_result(&mysql)))
+ {
+ MYSQL_ROW cur=mysql_fetch_row(result);
+ full_username=my_strdup(PSI_NOT_INSTRUMENTED, cur[0],MYF(MY_WME));
+ part_username=my_strdup(PSI_NOT_INSTRUMENTED, strtok(cur[0],"@"),MYF(MY_WME));
+ (void) mysql_fetch_row(result); // Read eof
+ mysql_free_result(result);
+ }
+}
+
+static int com_prompt(String *buffer __attribute__((unused)),
+ char *line)
+{
+ char *ptr=strchr(line, ' ');
+ prompt_counter = 0;
+ my_free(current_prompt);
+ current_prompt=my_strdup(PSI_NOT_INSTRUMENTED, ptr ? ptr+1 : default_prompt,MYF(MY_WME));
+ if (!ptr)
+ tee_fprintf(stdout, "Returning to default PROMPT of %s\n", default_prompt);
+ else
+ tee_fprintf(stdout, "PROMPT set to '%s'\n", current_prompt);
+ return 0;
+}
+
+#ifndef EMBEDDED_LIBRARY
+static void report_progress(const MYSQL *mysql, uint stage, uint max_stage,
+ double progress, const char *proc_info,
+ uint proc_info_length)
+{
+ uint length= printf("Stage: %d of %d '%.*s' %6.3g%% of stage done",
+ stage, max_stage, proc_info_length, proc_info,
+ progress);
+ if (length < last_progress_report_length)
+ printf("%*s", last_progress_report_length - length, "");
+ putc('\r', stdout);
+ fflush(stdout);
+ last_progress_report_length= length;
+}
+
+static void report_progress_end()
+{
+ if (last_progress_report_length)
+ {
+ printf("%*s\r", last_progress_report_length, "");
+ last_progress_report_length= 0;
+ }
+}
+#else
+static void report_progress_end()
+{
+}
+#endif