|
|
|
@ -1,6 +1,6 @@ |
|
|
|
/*
|
|
|
|
Copyright (c) 2000, 2012, Oracle and/or its affiliates. |
|
|
|
Copyright (c) 2009, 2012, Monty Program Ab. |
|
|
|
Copyright (c) 2000, 2013, Oracle and/or its affiliates. |
|
|
|
Copyright (c) 2009, 2013, Monty Program Ab. |
|
|
|
|
|
|
|
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 |
|
|
|
@ -152,6 +152,7 @@ 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 int interrupted_query= 0; |
|
|
|
static char *current_host,*current_db,*current_user=0,*opt_password=0, |
|
|
|
*current_prompt=0, *delimiter_str= 0, |
|
|
|
@ -1056,9 +1057,10 @@ static void initialize_readline (char *name); |
|
|
|
static void fix_history(String *final_command); |
|
|
|
#endif
|
|
|
|
|
|
|
|
static COMMANDS *find_command(char *name,char cmd_name); |
|
|
|
static bool add_line(String &buffer,char *line,char *in_string, |
|
|
|
bool *ml_comment, bool truncated); |
|
|
|
static COMMANDS *find_command(char *name); |
|
|
|
static COMMANDS *find_command(char cmd_name); |
|
|
|
static bool add_line(String &buffer, char *line, ulong line_length, |
|
|
|
char *in_string, bool *ml_comment, bool truncated); |
|
|
|
static void remove_cntrl(String &buffer); |
|
|
|
static void print_table_data(MYSQL_RES *result); |
|
|
|
static void print_table_data_html(MYSQL_RES *result); |
|
|
|
@ -1077,6 +1079,45 @@ static sig_handler window_resize(int sig); |
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
const char DELIMITER_NAME[]= "delimiter"; |
|
|
|
const uint DELIMITER_NAME_LEN= sizeof(DELIMITER_NAME) - 1; |
|
|
|
inline bool is_delimiter_command(char *name, ulong len) |
|
|
|
{ |
|
|
|
/*
|
|
|
|
Delimiter command has a parameter, so the length of the whole command |
|
|
|
is larger than DELIMITER_NAME_LEN. We don't care the parameter, so |
|
|
|
only name(first DELIMITER_NAME_LEN bytes) is checked. |
|
|
|
*/ |
|
|
|
return (len >= DELIMITER_NAME_LEN && |
|
|
|
!my_strnncoll(charset_info, (uchar*) name, DELIMITER_NAME_LEN, |
|
|
|
(uchar *) DELIMITER_NAME, DELIMITER_NAME_LEN)); |
|
|
|
} |
|
|
|
|
|
|
|
/**
|
|
|
|
Get the index of a command in the commands array. |
|
|
|
|
|
|
|
@param cmd_char Short form command. |
|
|
|
|
|
|
|
@return int |
|
|
|
The index of the command is returned if it is found, else -1 is returned. |
|
|
|
*/ |
|
|
|
inline int get_command_index(char cmd_char) |
|
|
|
{ |
|
|
|
/*
|
|
|
|
All client-specific commands are in the first part of commands array |
|
|
|
and have a function to implement it. |
|
|
|
*/ |
|
|
|
for (uint i= 0; *commands[i].func; i++) |
|
|
|
if (commands[i].cmd_char == cmd_char) |
|
|
|
return i; |
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
static int delimiter_index= -1; |
|
|
|
static int charset_index= -1; |
|
|
|
static bool real_binary_mode= FALSE; |
|
|
|
|
|
|
|
|
|
|
|
int main(int argc,char *argv[]) |
|
|
|
{ |
|
|
|
char buff[80]; |
|
|
|
@ -1085,6 +1126,8 @@ int main(int argc,char *argv[]) |
|
|
|
DBUG_ENTER("main"); |
|
|
|
DBUG_PROCESS(argv[0]); |
|
|
|
|
|
|
|
charset_index= get_command_index('C'); |
|
|
|
delimiter_index= get_command_index('d'); |
|
|
|
delimiter_str= delimiter; |
|
|
|
default_prompt = my_strdup(getenv("MYSQL_PS1") ? |
|
|
|
getenv("MYSQL_PS1") : |
|
|
|
@ -1598,6 +1641,13 @@ static struct my_option my_long_options[] = |
|
|
|
"Default authentication client-side plugin to use.", |
|
|
|
&opt_default_auth, &opt_default_auth, 0, |
|
|
|
GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
|
|
|
{"binary-mode", 0, |
|
|
|
"By default, ASCII '\\0' is disallowed and '\\r\\n' is translated to '\\n'. " |
|
|
|
"This switch turns off both features, and also turns off parsing of all client" |
|
|
|
"commands except \\C and DELIMITER, in non-interactive mode (for input " |
|
|
|
"piped to mysql or loaded using the 'source' command). This is necessary " |
|
|
|
"when processing output from mysqlbinlog that may contain blobs.", |
|
|
|
&opt_binary_mode, &opt_binary_mode, 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} |
|
|
|
}; |
|
|
|
|
|
|
|
@ -1873,29 +1923,64 @@ static int read_and_execute(bool interactive) |
|
|
|
String buffer; |
|
|
|
#endif
|
|
|
|
|
|
|
|
char *line= 0; |
|
|
|
char *line= NULL; |
|
|
|
char in_string=0; |
|
|
|
ulong line_number=0; |
|
|
|
bool ml_comment= 0; |
|
|
|
COMMANDS *com; |
|
|
|
ulong line_length= 0; |
|
|
|
status.exit_status=1; |
|
|
|
|
|
|
|
real_binary_mode= !interactive && opt_binary_mode; |
|
|
|
while (!aborted) |
|
|
|
{ |
|
|
|
if (!interactive) |
|
|
|
{ |
|
|
|
line=batch_readline(status.line_buff); |
|
|
|
/*
|
|
|
|
Skip UTF8 Byte Order Marker (BOM) 0xEFBBBF. |
|
|
|
Editors like "notepad" put this marker in |
|
|
|
the very beginning of a text file when |
|
|
|
you save the file using "Unicode UTF-8" format. |
|
|
|
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. |
|
|
|
*/ |
|
|
|
if (line && !line_number && |
|
|
|
(uchar) line[0] == 0xEF && |
|
|
|
(uchar) line[1] == 0xBB && |
|
|
|
(uchar) line[2] == 0xBF) |
|
|
|
line+= 3; |
|
|
|
line= batch_readline(status.line_buff, real_binary_mode); |
|
|
|
if (line) |
|
|
|
{ |
|
|
|
line_length= status.line_buff->read_length; |
|
|
|
|
|
|
|
/*
|
|
|
|
ASCII 0x00 is not allowed appearing in queries if it is not in binary |
|
|
|
mode. |
|
|
|
*/ |
|
|
|
if (!real_binary_mode && strlen(line) != line_length) |
|
|
|
{ |
|
|
|
status.exit_status= 1; |
|
|
|
String msg; |
|
|
|
msg.append("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); |
|
|
|
msg.append("'."); |
|
|
|
put_info(msg.c_ptr(), INFO_ERROR); |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
/*
|
|
|
|
Skip UTF8 Byte Order Marker (BOM) 0xEFBBBF. |
|
|
|
Editors like "notepad" put this marker in |
|
|
|
the very beginning of a text file when |
|
|
|
you save the file using "Unicode UTF-8" format. |
|
|
|
*/ |
|
|
|
if (!line_number && |
|
|
|
(uchar) line[0] == 0xEF && |
|
|
|
(uchar) line[1] == 0xBB && |
|
|
|
(uchar) line[2] == 0xBF) |
|
|
|
{ |
|
|
|
line+= 3; |
|
|
|
// decrease the line length accordingly to the 3 bytes chopped
|
|
|
|
line_length -=3; |
|
|
|
} |
|
|
|
} |
|
|
|
line_number++; |
|
|
|
if (!glob_buffer.length()) |
|
|
|
status.query_start_line=line_number; |
|
|
|
@ -1937,6 +2022,13 @@ static int read_and_execute(bool interactive) |
|
|
|
#else
|
|
|
|
if (opt_outfile) |
|
|
|
fputs(prompt, OUTFILE); |
|
|
|
/*
|
|
|
|
free the previous entered line. |
|
|
|
Note: my_free() cannot be used here as the memory was allocated under |
|
|
|
the readline/libedit library. |
|
|
|
*/ |
|
|
|
if (line) |
|
|
|
free(line); |
|
|
|
line= readline(prompt); |
|
|
|
#endif /* defined(__WIN__) */
|
|
|
|
|
|
|
|
@ -1946,6 +2038,8 @@ static int read_and_execute(bool interactive) |
|
|
|
*/ |
|
|
|
if (opt_outfile && line) |
|
|
|
fprintf(OUTFILE, "%s\n", line); |
|
|
|
|
|
|
|
line_length= line ? strlen(line) : 0; |
|
|
|
} |
|
|
|
// End of file or system error
|
|
|
|
if (!line) |
|
|
|
@ -1962,7 +2056,7 @@ static int read_and_execute(bool interactive) |
|
|
|
(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,0))) |
|
|
|
&& !ml_comment && !in_string && (com= find_command(line))) |
|
|
|
{ |
|
|
|
if ((*com->func)(&glob_buffer,line) > 0) |
|
|
|
break; |
|
|
|
@ -1974,7 +2068,7 @@ static int read_and_execute(bool interactive) |
|
|
|
#endif
|
|
|
|
continue; |
|
|
|
} |
|
|
|
if (add_line(glob_buffer, line, &in_string, &ml_comment, |
|
|
|
if (add_line(glob_buffer, line, line_length, &in_string, &ml_comment, |
|
|
|
status.line_buff ? status.line_buff->truncated : 0)) |
|
|
|
break; |
|
|
|
} |
|
|
|
@ -1994,72 +2088,142 @@ static int read_and_execute(bool interactive) |
|
|
|
#if defined(__WIN__)
|
|
|
|
buffer.free(); |
|
|
|
tmpbuf.free(); |
|
|
|
#else
|
|
|
|
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; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static COMMANDS *find_command(char *name,char cmd_char) |
|
|
|
/**
|
|
|
|
It checks if the input is a short form command. It returns the command's |
|
|
|
pointer if a command is found, else return NULL. Note that if binary-mode |
|
|
|
is set, then only \C is searched for. |
|
|
|
|
|
|
|
@param cmd_char A character of one byte. |
|
|
|
|
|
|
|
@return |
|
|
|
the command's pointer or NULL. |
|
|
|
*/ |
|
|
|
static COMMANDS *find_command(char cmd_char) |
|
|
|
{ |
|
|
|
DBUG_ENTER("find_command"); |
|
|
|
DBUG_PRINT("enter", ("cmd_char: %d", cmd_char)); |
|
|
|
|
|
|
|
int index= -1; |
|
|
|
|
|
|
|
/*
|
|
|
|
In binary-mode, we disallow all mysql commands except '\C' |
|
|
|
and DELIMITER. |
|
|
|
*/ |
|
|
|
if (real_binary_mode) |
|
|
|
{ |
|
|
|
if (cmd_char == 'C') |
|
|
|
index= charset_index; |
|
|
|
} |
|
|
|
else |
|
|
|
index= get_command_index(cmd_char); |
|
|
|
|
|
|
|
if (index >= 0) |
|
|
|
{ |
|
|
|
DBUG_PRINT("exit",("found command: %s", commands[index].name)); |
|
|
|
DBUG_RETURN(&commands[index]); |
|
|
|
} |
|
|
|
else |
|
|
|
DBUG_RETURN((COMMANDS *) 0); |
|
|
|
} |
|
|
|
|
|
|
|
/**
|
|
|
|
It checks if the input is a long form command. It returns the command's |
|
|
|
pointer if a command is found, else return NULL. Note that if binary-mode |
|
|
|
is set, then only DELIMITER is searched for. |
|
|
|
|
|
|
|
@param name A string. |
|
|
|
@return |
|
|
|
the command's pointer or NULL. |
|
|
|
*/ |
|
|
|
static COMMANDS *find_command(char *name) |
|
|
|
{ |
|
|
|
uint len; |
|
|
|
char *end; |
|
|
|
DBUG_ENTER("find_command"); |
|
|
|
DBUG_PRINT("enter",("name: '%s' char: %d", name ? name : "NULL", cmd_char)); |
|
|
|
|
|
|
|
if (!name) |
|
|
|
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=0; |
|
|
|
end=0; |
|
|
|
len=(uint) (end - name); |
|
|
|
while (my_isspace(charset_info, *end)) |
|
|
|
end++; |
|
|
|
if (!*end) |
|
|
|
end= 0; // no arguments to function
|
|
|
|
} |
|
|
|
else |
|
|
|
len= (uint) strlen(name); |
|
|
|
|
|
|
|
int index= -1; |
|
|
|
if (real_binary_mode) |
|
|
|
{ |
|
|
|
if (is_delimiter_command(name, len)) |
|
|
|
index= delimiter_index; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
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() |
|
|
|
All commands are in the first part of commands array and have a function |
|
|
|
to implement it. |
|
|
|
*/ |
|
|
|
if (strstr(name, "\\g") || (strstr(name, delimiter) && |
|
|
|
!(strlen(name) >= 9 && |
|
|
|
!my_strnncoll(&my_charset_latin1, |
|
|
|
(uchar*) name, 9, |
|
|
|
(const uchar*) "delimiter", |
|
|
|
9)))) |
|
|
|
DBUG_RETURN((COMMANDS *) 0); |
|
|
|
if ((end=strcont(name," \t"))) |
|
|
|
for (uint i= 0; commands[i].func; i++) |
|
|
|
{ |
|
|
|
len=(uint) (end - name); |
|
|
|
while (my_isspace(charset_info,*end)) |
|
|
|
end++; |
|
|
|
if (!*end) |
|
|
|
end=0; // no arguments to function
|
|
|
|
if (!my_strnncoll(&my_charset_latin1, (uchar*) name, len, |
|
|
|
(uchar*) commands[i].name, len) && |
|
|
|
(commands[i].name[len] == '\0') && |
|
|
|
(!end || commands[i].takes_params)) |
|
|
|
{ |
|
|
|
index= i; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
else |
|
|
|
len=(uint) strlen(name); |
|
|
|
} |
|
|
|
|
|
|
|
for (uint i= 0; commands[i].name; i++) |
|
|
|
if (index >= 0) |
|
|
|
{ |
|
|
|
if (commands[i].func && |
|
|
|
(((name && |
|
|
|
!my_strnncoll(&my_charset_latin1, (uchar*) name, len, |
|
|
|
(uchar*) commands[i].name, len) && |
|
|
|
!commands[i].name[len] && |
|
|
|
(!end || (end && commands[i].takes_params)))) || |
|
|
|
(!name && commands[i].cmd_char == cmd_char))) |
|
|
|
{ |
|
|
|
DBUG_PRINT("exit",("found command: %s", commands[i].name)); |
|
|
|
DBUG_RETURN(&commands[i]); |
|
|
|
} |
|
|
|
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,char *in_string, |
|
|
|
bool *ml_comment, bool truncated) |
|
|
|
static bool add_line(String &buffer, char *line, ulong line_length, |
|
|
|
char *in_string, bool *ml_comment, bool truncated) |
|
|
|
{ |
|
|
|
uchar inchar; |
|
|
|
char buff[80], *pos, *out; |
|
|
|
@ -2074,10 +2238,11 @@ static bool add_line(String &buffer,char *line,char *in_string, |
|
|
|
if (status.add_to_history && line[0] && not_in_history(line)) |
|
|
|
add_history(line); |
|
|
|
#endif
|
|
|
|
char *end_of_line=line+(uint) strlen(line); |
|
|
|
char *end_of_line= line + line_length; |
|
|
|
|
|
|
|
for (pos=out=line ; (inchar= (uchar) *pos) ; pos++) |
|
|
|
for (pos= out= line; pos < end_of_line; pos++) |
|
|
|
{ |
|
|
|
inchar= (uchar) *pos; |
|
|
|
if (!preserve_comments) |
|
|
|
{ |
|
|
|
// Skip spaces at the beginning of a statement
|
|
|
|
@ -2117,7 +2282,7 @@ static bool add_line(String &buffer,char *line,char *in_string, |
|
|
|
*out++= (char) inchar; |
|
|
|
continue; |
|
|
|
} |
|
|
|
if ((com=find_command(NullS,(char) inchar))) |
|
|
|
if ((com= find_command((char) inchar))) |
|
|
|
{ |
|
|
|
// Flush previously accepted characters
|
|
|
|
if (out != line) |
|
|
|
@ -2193,7 +2358,7 @@ static bool add_line(String &buffer,char *line,char *in_string, |
|
|
|
|
|
|
|
pos--; |
|
|
|
|
|
|
|
if ((com= find_command(buffer.c_ptr(), 0))) |
|
|
|
if ((com= find_command(buffer.c_ptr()))) |
|
|
|
{ |
|
|
|
|
|
|
|
if ((*com->func)(&buffer, buffer.c_ptr()) > 0) |
|
|
|
@ -2312,9 +2477,7 @@ static bool add_line(String &buffer,char *line,char *in_string, |
|
|
|
{ |
|
|
|
uint length=(uint) (out-line); |
|
|
|
|
|
|
|
if (!truncated && (length < 9 || |
|
|
|
my_strnncoll (charset_info, (uchar *)line, 9, |
|
|
|
(const uchar *) "delimiter", 9) || |
|
|
|
if (!truncated && (!is_delimiter_command(line, length) || |
|
|
|
(*in_string || *ml_comment))) |
|
|
|
{ |
|
|
|
/*
|
|
|
|
|