/*
Copyright ( c ) 2000 , 2018 , Oracle and / or its affiliates .
Copyright ( c ) 2009 , 2024 , 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.2" ;
/* 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
}
#define HISTORY_FILE ".mariadb_history"
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(A,B) my_strcasecmp_latin1((A), (B))
#else
#define cmp_database(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 (1024 L * 1024 L * 1024 L)
typedef struct st_status
{
int exit_status;
ulong query_start_line;
char *file_name;
LINE_BUFFER *line_buff;
bool batch, add_to_history, sandbox;
} 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;
enum ss_comment_type { SSC_NONE= 0 , SSC_CONDITIONAL, SSC_HINT };
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 ,
output_plain=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_query_cost= 0 ,
show_warnings= 0 , executing_query= 0 ,
ignore_spaces= 0 , opt_binhex= 0 , opt_progress_reports,
opt_print_query_on_error;
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;
unsigned long quick_max_column_width= LONG_MAX;
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 ;
#ifdef USE_LIBEDIT_INTERFACE
static int sigint_received= 0 ;
#endif
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 char *script_dir = NULL;
static const char *xmlmeta[] = {
"&" , "&" ,
"<" , "<" ,
">" , ">" ,
"\" ", " "",
/* Turn \0 into a space. Why not ? 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_silent(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 *),
com_sandbox(String *str, char *);
static void print_query_to_stderr(String *buffer);
static int com_query_cost(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'." },
{ "charset" , 'C' , com_charset, 1 ,
"Switch to another charset. Might be needed for processing binlog with multi-byte charsets." },
{ "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." },
{ "nowarning" , 'w' , com_nowarnings, 0 ,
"Don't show warnings after every statement." },
#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." },
{ "costs" , 'Q' , com_query_cost, 0 ,
"Toggle showing query costs after each query" },
{ "rehash" , '#' , com_rehash, 0 , "Rebuild completion hash." },
{ "sandbox" , '-' , com_sandbox, 0 ,
"Disallow commands that access the file system (except \\P without an argument and \\e)." },
{ "silent" , 'S' , com_silent, 0 ,
"Send command to MariaDB server, display result without column names or borders." },
{ "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." },
{ "warnings" , 'W' , com_warnings, 0 ,
"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 , "" },
{ "MBRCOVEREDBY" , 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 *, ss_comment_type *, 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 print_last_query_cost(void );
static void end_timer(ulonglong start_time, char *buff, size_t buff_size);
static void nice_time(double sec, char *buff, size_t buff_size,
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
static void end_in_sig_handler(int sig);
static bool kill_query(const char *reason);
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 LINE_BUFFER *batch_readline_init(ulong max_size, const char *path)
{
LINE_BUFFER *line_buff;
File file;
MY_STAT input_file_stat;
char buff[FN_REFLEN + 512 ];
if (path)
{
file= my_open(path, O_RDONLY | O_BINARY, MYF(0 ));
if (file < 0 && my_errno == ENOENT && script_dir)
{
char full_path[FN_REFLEN];
strxnmov(full_path, sizeof (full_path)-1 , script_dir, "/" , path, NULL);
file= my_open(full_path, O_RDONLY | O_BINARY, MYF(0 ));
}
if (file < 0 )
{
#ifdef _WIN32
if (my_errno == EACCES && my_stat(path, &input_file_stat, MYF(0 )) &&
MY_S_ISDIR(input_file_stat.st_mode))
my_snprintf(buff, sizeof (buff), "Can't read from a directory '%.*s'" ,
FN_REFLEN, path);
else
#endif
my_snprintf(buff, sizeof (buff), "Failed to open file '%.*s', error: %d" ,
FN_REFLEN, path, my_errno);
put_info(buff, INFO_ERROR, 0 );
return 0 ;
}
}
else
{
file= my_fileno(stdin);
}
if (my_fstat(file, &input_file_stat, MYF(0 )))
{
my_snprintf(buff, sizeof (buff), "Failed to stat file '%.*s', error: %d" ,
FN_REFLEN, path ? path : "stdin" , my_errno);
goto err1;
}
if (MY_S_ISDIR(input_file_stat.st_mode))
{
my_snprintf(buff, sizeof (buff), "Can't read from a directory '%.*s'" ,
FN_REFLEN, path ? path : "stdin" );
goto err1;
}
#ifndef _WIN32
if (MY_S_ISBLK(input_file_stat.st_mode))
{
my_snprintf(buff, sizeof (buff), "Can't read from a block device '%.*s'" ,
FN_REFLEN, path ? path : "stdin" );
goto err1;
}
#endif
if (!(line_buff= (LINE_BUFFER*) my_malloc(PSI_NOT_INSTRUMENTED,
sizeof (*line_buff),
MYF(MY_WME | MY_ZEROFILL))))
{
goto err;
}
if (init_line_buffer(line_buff, file, IO_SIZE, max_size))
{
my_free(line_buff);
goto err;
}
return line_buff;
err1:
put_info(buff, INFO_ERROR, 0 );
err:
if (path)
my_close(file, MYF(0 ));
return 0 ;
}
static int delimiter_index= -1 ;
static int charset_index= -1 ;
static int sandbox_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 ]);
sandbox_index= get_command_index('-' );
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, NULL)))
{
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);
#if SERVER_MATURITY_LEVEL < MariaDB_PLUGIN_MATURITY_STABLE
put_info("Help others discover MariaDB."
" Star it on GitHub: https://github.com/MariaDB/server\n ",
INFO_INFO);
#endif
}
#ifdef HAVE_READLINE
initialize_readline();
if (!status.batch && !quick && !opt_html && !opt_xml)
{
const char *home;
/* read-history from file, default ~/.mariadb_history*/
if ((histfile= getenv("MARIADB_HISTFILE" ))
|| (histfile= getenv("MYSQL_HISTFILE" )))
histfile=my_strdup(PSI_NOT_INSTRUMENTED, histfile, MYF(MY_WME));
else if ((home= getenv("HOME" )))
{
size_t histfile_size=
strlen(home) + strlen("/" HISTORY_FILE) + 2 ;
histfile=(char *) my_malloc(PSI_NOT_INSTRUMENTED,
histfile_size, MYF(MY_WME));
if (histfile)
{
snprintf(histfile, histfile_size, "%s/%s" , home, HISTORY_FILE);
if (my_access(histfile, F_OK))
{
/* no .mariadb_history, look for historical name and use if present */
snprintf(histfile, histfile_size, "%s/.mysql_history" , home);
/* and go back to original if not found */
if (my_access(histfile, F_OK))
snprintf(histfile, histfile_size, "%s/%s" , home, HISTORY_FILE);
}
char link_name[FN_REFLEN];
if (my_readlink(link_name, histfile, 0 ) == 0 &&
strncmp(link_name, "/dev/null" , 10 ) == 0 )
{
/* The .mariadb_history file is a symlink to /dev/null, don't use it */
my_free(histfile);
histfile= 0 ;
}
}
}
/* We used to suggest setting MARIADB_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);
{
/* Extra space for the suffix appended to histfile:
" . " - separator ( sizeof = 2 , includes NUL )
+ PID - up to 20 digits ( ULONG_MAX = 18446744073709551615 )
+ " . TMP " - extension ( sizeof = 5 , includes NUL , doubles
as the string NUL terminator )
sizeof ( " . " ) and sizeof ( " . TMP " ) each carry a NUL , so the sum
over-allocates by one byte, which is intentional. */
size_t hlen= strlen(histfile) + sizeof ("." ) + 20 + sizeof (".TMP" );
if (!(histfile_tmp= (char *) my_malloc(PSI_NOT_INSTRUMENTED, hlen, MYF(MY_WME))))
{
fprintf(stderr, "Couldn't allocate memory for temp histfile!\n" );
exit (1 );
}
/* Include PID in name to avoid rename collision when concurrent sessions exit. */
/* pid_t is signed but getpid() always returns a non-negative value (POSIX). */
snprintf(histfile_tmp, hlen, "%s.%lu.TMP" , histfile,
(unsigned long ) getpid());
}
}
}
#endif
snprintf(buff, sizeof (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 );
DBUG_RETURN(0 );
}
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 existing 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);
SET_SSL_OPTS_WITH_CHECK(mysql);
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);
}
void end_in_sig_handler(int sig)
{
#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
}
/*
Kill a running query . Returns true if we were unable to connect to the server .
*/
bool kill_query(const char *reason)
{
char kill_buffer[40 ];
MYSQL *kill_mysql= NULL;
kill_mysql= mysql_init(kill_mysql);
if (!do_connect(kill_mysql,current_host, current_user, opt_password, "" , 0 ))
{
tee_fprintf(stdout, "%s -- sorry, cannot connect to server to kill query, giving up ...\n" , reason);
return true ;
}
/* 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 */
snprintf(kill_buffer, sizeof (kill_buffer), "KILL %s%lu" ,
(interrupted_query == 1 ) ? "QUERY " : "" ,
mysql_thread_id(&mysql));
if (verbose)
tee_fprintf(stdout, "%s -- sending \" %s\" to server ...\n" , reason,
kill_buffer);
mysql_real_query(kill_mysql, kill_buffer, (uint) strlen(kill_buffer));
mysql_close(kill_mysql);
if (interrupted_query == 1 )
tee_fprintf(stdout, "%s -- query killed.\n" , reason);
else
tee_fprintf(stdout, "%s -- connection killed.\n" , reason);
if (in_com_source)
aborted= 1 ; // Abort source command
return false ;
}
/*
This function handles sigint calls
If query is in process , kill query
If ' source ' is executed , abort source command
no query in process , regenerate prompt .
*/
sig_handler handle_sigint(int sig)
{
/*
On Unix only , if no query is being executed just clear the prompt ,
don ' t exit . On Windows we exit .
*/
if (!executing_query)
{
#ifndef _WIN32
tee_fprintf(stdout, "^C\n" );
#ifdef USE_LIBEDIT_INTERFACE
/* Libedit will regenerate it outside of the signal handler. */
sigint_received= 1 ;
#else
rl_on_new_line(); // Regenerate the prompt on a newline
rl_replace_line("" , 0 ); // Clear the previous text
rl_redisplay();
#endif
#else // WIN32
tee_fprintf(stdout, "Ctrl-C -- exit!\n" );
end_in_sig_handler(sig);
#endif
return ;
}
/*
When executing a query , this newline makes the prompt look like so :
^ C
Ctrl - C - - query killed .
*/
tee_fprintf(stdout, "\n" );
if (kill_query("Ctrl-C" ))
{
aborted= 1 ;
end_in_sig_handler(sig);
}
}
#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" , 0 ,
"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" , 0 ,
"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." ,
&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" , 0 ,
"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 },
{"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 },
{"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-names" , 0 , "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 },
{"column-type-info" , 0 , "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 },
{"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 },
{"connect_timeout" , 0 , "Number of seconds before connection timeout." ,
&opt_connect_timeout, &opt_connect_timeout, 0 , GET_ULONG, REQUIRED_ARG,
0 , 0 , 3600 *12 , 0 , 0 , 0 },
{"database" , 'D' , "Database to use." , ¤t_db,
¤t_db, 0 , GET_STR_ALLOC, REQUIRED_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" , 0 , "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 },
{"default-auth" , 0 , "Default authentication client-side plugin to use." ,
&opt_default_auth, &opt_default_auth, 0 ,
GET_STR, REQUIRED_ARG, 0 , 0 , 0 , 0 , 0 , 0 },
{"default-character-set" , 0 ,
"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 },
{"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 },
{"execute" , 'e' , "Execute command and quit. (Disables --force and history file.)" , 0 ,
0 , 0 , GET_STR, REQUIRED_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 },
{"host" , 'h' , "Connect to host. Defaults in the following order: "
"$MARIADB_HOST, $MYSQL_HOST, and then localhost" ,
¤t_host, ¤t_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 },
{"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" , 0 ,
"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 },
{"line-numbers" , 0 , "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 },
{"local-infile" , OPT_LOCAL_INFILE, "Enable LOAD DATA LOCAL INFILE." ,
&opt_local_infile, &opt_local_infile, 0 , GET_BOOL, OPT_ARG, 0 , 0 , 0 , 0 , 0 , 0 },
{"max-allowed-packet" , 0 ,
"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 *1024 LL*1024 LL, 4096 , 2 *1024 LL*1024 LL*1024 LL, 0 , 1024 , 0 },
{"max-join-size" , 0 ,
"Automatic limit for rows in a join when using --safe-updates." ,
&max_join_size, &max_join_size, 0 , GET_ULONG, REQUIRED_ARG, 1000000 L,
1 , ULONG_MAX, 0 , 1 , 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 },
{"net-buffer-length" , 0 ,
"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 ULL*1024 ULL, 0 , 1024 , 0 },
{"no-beep" , 'b' , "Turn off beep on error." , &opt_nobeep,
&opt_nobeep, 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
{"plugin-dir" , 0 , "Directory for client-side plugins." , &opt_plugin_dir,
&opt_plugin_dir, 0 , GET_STR, REQUIRED_ARG, 0 , 0 , 0 , 0 , 0 , 0 },
{"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 },
{"print-query-on-error" , 0 ,
"Print the query if there was an error. Is only enabled in --batch mode if verbose is not set (as then the query would be printed anyway)" ,
&opt_print_query_on_error, &opt_print_query_on_error, 0 , GET_BOOL, NO_ARG,
1 , 0 , 0 , 0 , 0 , 0 },
{"progress-reports" , 0 ,
"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" , 0 , "Set the command line prompt to this value." ,
¤t_prompt, ¤t_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 },
{"quick-max-column-width" , 0 ,
"Maximum number of characters displayed in a column header"
" when using --quick" , &quick_max_column_width,
&quick_max_column_width, 0 , GET_ULONG, REQUIRED_ARG, LONG_MAX, 0 , ULONG_MAX,
0 , 1 , 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" , 0 , "Reconnect if the connection is lost." ,
&opt_reconnect, &opt_reconnect, 0 , GET_BOOL, NO_ARG, 1 , 0 , 0 , 0 , 0 , 0 },
{"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 },
{"sandbox" , 0 , "Disallow commands that access the file system (except \\P without an argument and \\e)." ,
&status.sandbox, &status.sandbox, 0 , GET_BOOL, NO_ARG, 0 , 0 , 0 , 0 , 0 , 0 },
{"script-dir" , 0 , "Set an alternative directory path for searching scripts invoked via the source command." ,
&script_dir, &script_dir, 0 , GET_STR, REQUIRED_ARG, 0 , 0 , 0 , 0 , 0 , 0 },
{"secure-auth" , 0 , "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 },
{"select-limit" , 0 ,
"Automatic limit for SELECT when using --safe-updates." , &select_limit,
&select_limit, 0 , GET_ULONG, REQUIRED_ARG, 1000 L, 1 , ULONG_MAX, 0 , 1 , 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-query-costs" , 0 , "Show query cost after every statement." ,
&show_query_cost, &show_query_cost, 0 , GET_BOOL, NO_ARG, 0 , 0 , 0 , 0 , 0 , 0 },
{"show-warnings" , 0 , "Show warnings after every statement." ,
&show_warnings, &show_warnings, 0 , GET_BOOL, NO_ARG, 0 , 0 , 0 , 0 , 0 , 0 },
{"sigint-ignore" , 0 , "Ignore SIGINT (CTRL-C)." , &opt_sigint_ignore,
&opt_sigint_ignore, 0 , GET_BOOL, NO_ARG, 0 , 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 },
{"unbuffered" , 'n' , "Flush buffer after each query." , &unbuffered,
&unbuffered, 0 , GET_BOOL, NO_ARG, 0 , 0 , 0 , 0 , 0 , 0 },
#ifndef DONT_ALLOW_USER_CHANGE
{"user" , 'u' , "User for login if not current user." , ¤t_user,
¤t_user, 0 , GET_STR_ALLOC, REQUIRED_ARG, 0 , 0 , 0 , 0 , 0 , 0 },
#endif
{"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 },
{"vertical" , 'E' , "Print the output of a query (rows) vertically." ,
&vertical, &vertical, 0 , GET_BOOL, 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 },
{"xml" , 'X' , "Produce XML output." , &opt_xml, &opt_xml, 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 from %s, client %s for %s (%s) using %s %s\n" ,
my_progname, MYSQL_SERVER_VERSION, VER, SYSTEM_TYPE, MACHINE_TYPE,
readline, rl_library_version);
#else
printf("%s from %s, client %s for %s (%s), source revision %s\n" , my_progname,
MYSQL_SERVER_VERSION, VER, 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();
//MARIADB_HOST will be preferred over MYSQL_HOST.
tmp= getenv("MARIADB_HOST" );
if (tmp == NULL)
tmp= 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 );
}
#if !defined (_WIN32) && defined (USE_LIBEDIT_INTERFACE)
static inline void reset_prompt(char *in_string, bool *ml_comment) {
glob_buffer.length(0 );
*ml_comment = false ;
*in_string = 0 ;
}
#endif
static int read_and_execute(bool interactive)
{
char *line= NULL;
char in_string=0 ;
ulong line_number=0 ;
bool ml_comment= 0 ;
ss_comment_type ss_comment= SSC_NONE;
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 0 x00 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 ) 0 xEFBBBF .
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 ] == 0 xEF &&
(uchar) line[1 ] == 0 xBB &&
(uchar) line[2 ] == 0 xBF)
{
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);
#ifdef USE_LIBEDIT_INTERFACE
/*
libedit handles interrupts different than libreadline .
libreadline has its own signal handlers , thus a sigint during readline
doesn ' t force readline to return null string .
However libedit returns null if the interrupt signal is raised .
We can also get an empty string when ctrl + d is pressed ( EoF ) .
We need this sigint_received flag , to differentiate between the two
cases . This flag is only set during our handle_sigint function when
LIBEDIT_INTERFACE is used .
*/
if (!line && sigint_received)
{
// User asked to clear the input.
sigint_received= 0 ;
reset_prompt(&in_string, &ml_comment);
continue ;
}
// For safety, we always mark this as cleared.
sigint_received= 0 ;
#endif
#endif /* defined(__WIN__) */
/*
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, &ss_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 and \ - are 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 client commands except ' \ C ' ,
DELIMITER ( see long command finding find_command ( char * ) )
and ' \ - ' ( sandbox , see following comment ) .
*/
if (real_binary_mode)
{
if (cmd_char == 'C' )
index= charset_index;
/*
binary - mode enforces stricter controls compared to sandbox mode .
Whether sandbox mode is enabled or not is irrelevant when
binary - mode is active .
The only purpose of processing sandbox mode here is to avoid error
messages on files made by mysqldump .
*/
else if (cmd_char == '-' )
index= sandbox_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 ;
/*
In binary - mode , we disallow all client commands except DELIMITER
and short commands ' \ C ' and ' \ - ' ( see short command finding
find_command ( char ) ) .
*/
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,
ss_comment_type *ss_comment, bool truncated)
{
uchar inchar;
char buff[80 ], *pos, *out;
COMMANDS *com;
bool need_space= 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 != SSC_NONE)
{
/*
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
{
snprintf(buff, sizeof (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 && *ss_comment != SSC_HINT &&
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 && *ss_comment != SSC_HINT &&
(!*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 ] == '!' ) &&
pos[2 ] != '+' && *ss_comment != SSC_HINT)
{
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 == SSC_NONE &&
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 ] == '*' )
{
if (pos[2 ] == '!' )
*ss_comment= SSC_CONDITIONAL;
else if (pos[2 ] == '+' )
*ss_comment= SSC_HINT;
}
else if (!*in_string && *ss_comment != SSC_NONE &&
inchar == '*' && *(pos + 1 ) == '/' )
{
*ss_comment= SSC_NONE;
*out++= *pos++; // copy '*'
*out++= *pos; // copy '/'
continue ;
}
if (inchar == *in_string)
*in_string= 0 ;
else if (!*ml_comment && !*in_string && *ss_comment != SSC_HINT &&
(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.c_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" );
setlocale(LC_ALL,"" );
/* 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 , int )
{
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)))
{
snprintf(buf, sizeof (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 ;
}
#ifndef EMBEDDED_LIBRARY
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wvarargs"
/* CONC-789 */
#endif
static void status_info_cb(void *data, enum enum_mariadb_status_info type, ...)
{
va_list ap;
va_start(ap, type);
if (type == SESSION_TRACK_TYPE && va_arg(ap, int ) == SESSION_TRACK_SCHEMA)
{
MARIADB_CONST_STRING *val= va_arg(ap, MARIADB_CONST_STRING *);
my_free(current_db);
if (val->length)
current_db= my_strndup(PSI_NOT_INSTRUMENTED, val->str, val->length, MYF(MY_FAE));
else
current_db= NULL;
}
va_end(ap);
}
#ifdef __clang__
#pragma clang diagnostic pop
#endif
#else
#define mysql_optionsv(A,B,C,D) do { } while (0 )
#endif
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 ;
if (opt_print_query_on_error)
{
String query(buf, length, charset_info);
(void ) print_query_to_stderr(&query);
}
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, char *, 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, char *line)
{
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 ;
}
static int com_clear(String *buffer,char *)
{
#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
}
static int com_charset(String *, 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)
{
if (new_cs->mbminlen > 1 )
return put_info("Character sets with mbminlen>1 are not supported" ,
INFO_ERROR, 0 );
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 *)
{
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_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());
#ifdef HAVE_READLINE
if (status.add_to_history)
{
const char *delim= output_plain ? "\\S" : 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))
{
if (opt_print_query_on_error)
print_query_to_stderr(buffer);
error= put_error(&mysql);
goto end;
}
}
else
{
error= mysql_store_result_for_lazy(&result);
if (error)
goto end;
}
report_progress_end();
if (verbose >= 3 || !opt_silent)
end_timer(timer, time_buff, sizeof (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 (output_plain)
print_tab_data(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);
snprintf(buff, sizeof (buff), "%llu %s in set" ,
(unsigned long long ) mysql_num_rows(result),
mysql_num_rows(result) == 1 ? "row" : "rows" );
end_pager();
if (mysql_errno(&mysql))
{
if (opt_print_query_on_error)
print_query_to_stderr(buffer);
error= put_error(&mysql);
}
}
}
else if (mysql_affected_rows(&mysql) == ~(ulonglong) 0 )
strmov(buff,"Query OK" );
else
snprintf(buff, sizeof (buff), "Query OK, %llu %s affected" ,
(unsigned long long ) mysql_affected_rows(&mysql),
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 */
{
if (opt_print_query_on_error)
print_query_to_stderr(buffer);
error= put_error(&mysql);
}
else if (unbuffered)
fflush(stdout);
mysql_free_result(result);
} while (!(err= mysql_next_result(&mysql)));
if (err >= 1 )
{
if (opt_print_query_on_error)
print_query_to_stderr(buffer);
error= put_error(&mysql);
}
end:
/* Show warnings if any or error occurred */
if (show_warnings == 1 && (warnings >= 1 || error))
print_warnings();
if (show_query_cost)
print_last_query_cost();
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 int
com_silent(String *buffer,char *line)
{
int result;
uint old_opt_silent=opt_silent;
bool old_output_plain=output_plain;
bool old_column_names=column_names;
opt_silent=1 ;
output_plain=1 ;
column_names=0 ;
result=com_go(buffer,line);
opt_silent=old_opt_silent;
output_plain=old_output_plain;
column_names=old_column_names;
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)
snprintf(s, sizeof (buf) - (size_t)(s - buf), " unknown=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 inline MYSQL_ROW fetch_row(MYSQL_RES *result)
{
MYSQL_ROW row = mysql_fetch_row(result);
#ifndef EMBEDDED_LIBRARY
if (last_progress_report_length) {
report_progress_end();
}
#endif
return row;
}
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, MY_MIN(field->length, quick_max_column_width));
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 = 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 ? " " : "NULL" , PAGER);
tee_fputs("</TH>" , PAGER);
}
(void ) tee_fputs("</TR>" , PAGER);
}
while ((cur = 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 = 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 = 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);
}
/* print_last_query_cost */
static void print_last_query_cost()
{
const char *query;
char *end;
MYSQL_RES *result;
MYSQL_ROW cur;
query= "show status like 'last_query_cost'" ;
mysql_real_query_for_lazy(query, strlen(query));
mysql_store_result_for_lazy(&result);
if (!result)
goto end;
cur= mysql_fetch_row(result);
if (strtod(cur[1 ], &end) != 0 .0 )
{
init_pager();
tee_fprintf(PAGER, "%s: %s\n\n" , cur[0 ], cur[1 ]);
}
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 = 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 *, char *line)
{
char file_name[FN_REFLEN], *end, *param;
if (status.sandbox)
return put_info("Not allowed in the sandbox mode" , INFO_ERROR, 0 );
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 *, char *)
{
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 *, char *line)
{
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
{
if (status.sandbox)
return put_info("Not allowed in the sandbox mode" , INFO_ERROR, 0 );
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 *, char *)
{
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 *)
{
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)))
{
#define EDITOR_FAIL_MSG "Command '%.40s' failed"
char errmsg[sizeof (EDITOR_FAIL_MSG) - 1 + 40 ];
snprintf(errmsg, sizeof (errmsg), EDITOR_FAIL_MSG, buff);
#undef EDITOR_FAIL_MSG
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 *, char *)
{
status.exit_status=0 ;
return 1 ;
}
static int
com_rehash(String *,
char *)
{
#ifdef HAVE_READLINE
build_completion_hash(1 , 0 );
#endif
return 0 ;
}
#ifdef USE_POPEN
static int com_shell(String *, char *line)
{
char *shell_cmd;
if (status.sandbox)
return put_info("Not allowed in the sandbox mode" , INFO_ERROR, 0 );
/* 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 void print_query(String *buffer, FILE *file)
{
tee_puts("--------------" , file);
(void ) tee_fputs(buffer->c_ptr(), file);
if (!buffer->length() || (*buffer)[buffer->length()-1 ] != '\n' )
tee_putc('\n' , file);
tee_puts("--------------\n" , file);
}
/*
Print query to stderr in batch mode if verbose is not set
*/
static void print_query_to_stderr(String *buffer)
{
if ((status.batch || in_com_source) && !verbose)
{
fflush(stdout);
print_query(buffer, stderr);
fflush(stderr);
}
}
static int com_print(String *buffer,char *)
{
print_query(buffer, stdout);
return 0 ;
}
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)
{
snprintf(buff, sizeof (buff), "Connection id: %lu" ,mysql_thread_id(&mysql));
put_info(buff,INFO_INFO);
snprintf(buff, sizeof (buff), "Current database: %.128s\n" ,
current_db ? current_db : "*** NONE ***" );
put_info(buff,INFO_INFO);
}
return error;
}
static int com_source(String *, char *line)
{
char source_name[FN_REFLEN], *end, *param;
LINE_BUFFER *line_buff;
int error;
STATUS old_status;
my_bool save_ignore_errors;
if (status.sandbox)
return put_info("Not allowed in the sandbox mode" , INFO_ERROR, 0 );
/* 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);
if (!(line_buff= batch_readline_init(MAX_BATCH_BUFFER_SIZE, source_name)))
{
return ignore_errors ? -1 : 1 ;
}
/* 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.sandbox=old_status.sandbox;
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_close(line_buff->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;
}
static int com_delimiter(String *, 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 ;
}
static int com_use(String *, 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(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_sandbox(String *, char *)
{
status.sandbox= 1 ;
put_info("Sandbox mode." , INFO_INFO);
return 0 ;
}
static int com_warnings(String *, char *)
{
show_warnings = 1 ;
put_info("Show warnings enabled." ,INFO_INFO);
return 0 ;
}
static int com_nowarnings(String *, char *)
{
show_warnings = 0 ;
put_info("Show warnings disabled." ,INFO_INFO);
return 0 ;
}
static int
com_query_cost(String *buffer __attribute__((unused)),
char *line __attribute__((unused)))
{
show_query_cost= 1 - show_query_cost;
if (show_query_cost)
put_info("Last_query_cost enabled." ,INFO_INFO);
else
put_info("Last_query_cost 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 (!one_database)
mysql_optionsv(&mysql, MARIADB_OPT_STATUS_CALLBACK, status_info_cb, NULL);
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 ];
snprintf(init_command, sizeof (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 *, char *)
{
const char *status_str;
char buff[40 ];
ulonglong id;
MYSQL_RES *UNINIT_VAR(result);
/*
Don ' t remove " limit 1 " ,
it is protection against SQL_SELECT_LIMIT = 0
*/
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));
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, cert is %s\n" ,
status_str, opt_ssl_verify_server_cert ? "OK" : "UNKNOWN" );
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,INT16_MAX, 10 , &sec)))
{
nice_time((double ) sec,buff, sizeof (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)
{
report_progress_end();
fflush(stdout);
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)
{
DBUG_ENTER("put_error" );
DBUG_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, size_t buff_size,
bool part_second)
{
char *buff_end= buff + buff_size;
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)
snprintf(buff, buff_end - buff, "%.3f sec" , sec);
else
snprintf(buff, buff_end - buff, "%d sec" , (int ) sec);
}
static void end_timer(ulonglong start_time, char *buff, size_t buff_size)
{
double sec;
if (buff_size < 4 )
return ;
buff[0 ]=' ' ;
buff[1 ]='(' ;
sec= (microsecond_interval_timer() - start_time) / (double ) (1000 * 1000 );
nice_time(sec, buff + 2 , buff_size - 2 , 1 );
snprintf(strend(buff), buff_size - (strend(buff) - 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 *, 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
Messung V0.5 in Prozent C=96 H=91 G=93
¤ Dauer der Verarbeitung: 0.359 Sekunden
(vorverarbeitet am 2026-10-08)
¤
*© Formatika GbR, Deutschland