diff options
author | Sergei Golubchik <sergii@pisem.net> | 2013-06-10 21:45:30 +0200 |
---|---|---|
committer | Sergei Golubchik <sergii@pisem.net> | 2013-06-10 21:45:30 +0200 |
commit | 2db4392bf4cb0447fd8f09fe85997f34b9c697d0 (patch) | |
tree | 8f9f7e98b70b87f03e6fa2f02b15be380904f6b8 /client | |
parent | 16807b5856c0279f515f5273d72b14753237efb0 (diff) | |
download | mariadb-git-2db4392bf4cb0447fd8f09fe85997f34b9c697d0.tar.gz |
MDEV-4297 mysql --binary-mode
backport mysql --binary-mode (bug#11747577, bug#33048)
Diffstat (limited to 'client')
-rw-r--r-- | client/client_priv.h | 2 | ||||
-rw-r--r-- | client/my_readline.h | 2 | ||||
-rw-r--r-- | client/mysql.cc | 271 | ||||
-rw-r--r-- | client/mysqlbinlog.cc | 6 | ||||
-rw-r--r-- | client/readline.cc | 17 |
5 files changed, 226 insertions, 72 deletions
diff --git a/client/client_priv.h b/client/client_priv.h index 70a9d129433..e206804d057 100644 --- a/client/client_priv.h +++ b/client/client_priv.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2001, 2012, Oracle and/or its affiliates. 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 diff --git a/client/my_readline.h b/client/my_readline.h index 11ace987b44..57537308fed 100644 --- a/client/my_readline.h +++ b/client/my_readline.h @@ -36,7 +36,7 @@ typedef struct st_line_buffer extern LINE_BUFFER *batch_readline_init(ulong max_size,FILE *file); extern LINE_BUFFER *batch_readline_command(LINE_BUFFER *buffer, char * str); -extern char *batch_readline(LINE_BUFFER *buffer); +extern char *batch_readline(LINE_BUFFER *buffer, bool binary_mode); extern void batch_readline_end(LINE_BUFFER *buffer); #endif /* CLIENT_MY_READLINE_INCLUDED */ diff --git a/client/mysql.cc b/client/mysql.cc index 3cd078ac1ce..2c8f7347840 100644 --- a/client/mysql.cc +++ b/client/mysql.cc @@ -1,6 +1,6 @@ /* Copyright (c) 2000, 2012, Oracle and/or its affiliates. - Copyright (c) 2009, 2012, Monty Program Ab. + 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} }; @@ -1878,24 +1928,59 @@ static int read_and_execute(bool interactive) 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; @@ -1946,6 +2031,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 +2049,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 +2061,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; } @@ -1996,70 +2083,131 @@ static int read_and_execute(bool interactive) tmpbuf.free(); #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 +2222,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 +2266,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 +2342,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 +2461,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))) { /* diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc index 387413cc5aa..d68b2a557e1 100644 --- a/client/mysqlbinlog.cc +++ b/client/mysqlbinlog.cc @@ -206,10 +206,8 @@ void print_annotate_event(PRINT_EVENT_INFO *print_event_info) } } -static Exit_status dump_local_log_entries(PRINT_EVENT_INFO *print_event_info, - const char* logname); -static Exit_status dump_remote_log_entries(PRINT_EVENT_INFO *print_event_info, - const char* logname); +static Exit_status dump_local_log_entries(PRINT_EVENT_INFO *, const char*); +static Exit_status dump_remote_log_entries(PRINT_EVENT_INFO *, const char*); static Exit_status dump_log_entries(const char* logname); static Exit_status safe_connect(); diff --git a/client/readline.cc b/client/readline.cc index 791a044e0e1..b6643b86356 100644 --- a/client/readline.cc +++ b/client/readline.cc @@ -54,7 +54,7 @@ LINE_BUFFER *batch_readline_init(ulong max_size,FILE *file) } -char *batch_readline(LINE_BUFFER *line_buff) +char *batch_readline(LINE_BUFFER *line_buff, bool binary_mode) { char *pos; ulong out_length; @@ -63,8 +63,17 @@ char *batch_readline(LINE_BUFFER *line_buff) if (!(pos=intern_read_line(line_buff, &out_length))) return 0; if (out_length && pos[out_length-1] == '\n') - if (--out_length && pos[out_length-1] == '\r') /* Remove '\n' */ - out_length--; /* Remove '\r' */ + { + /* + On Windows platforms we also need to remove '\r', unconditionally. On + Unix-like platforms we only remove it if we are not on binary mode. + */ + + /* Remove '\n' */ + if (--out_length && IF_WIN(1,!binary_mode) && pos[out_length-1] == '\r') + /* Remove '\r' */ + out_length--; + } line_buff->read_length=out_length; pos[out_length]=0; return pos; @@ -226,7 +235,7 @@ char *intern_read_line(LINE_BUFFER *buffer, ulong *out_length) for (;;) { pos=buffer->end_of_line; - while (*pos != '\n' && *pos) + while (*pos != '\n' && pos != buffer->end) pos++; if (pos == buffer->end) { |