diff options
Diffstat (limited to 'sql/log_event.cc')
-rw-r--r-- | sql/log_event.cc | 101 |
1 files changed, 85 insertions, 16 deletions
diff --git a/sql/log_event.cc b/sql/log_event.cc index b8d80883451..3b69e1c3176 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -203,6 +203,29 @@ static inline int read_str(char * &buf, char *buf_end, char * &str, } /* + Transforms a string into "" or its expression in 0x... form. +*/ +static char *str_to_hex(char *to, char *from, uint len) +{ + char *p= to; + if (len) + { + p= strmov(p, "0x"); + for (uint i= 0; i < len; i++, p+= 2) + { + /* val[i] is char. Casting to uchar helps greatly if val[i] < 0 */ + uint tmp= (uint) (uchar) from[i]; + p[0]= _dig_vec_upper[tmp >> 4]; + p[1]= _dig_vec_upper[tmp & 15]; + } + *p= 0; + } + else + p= strmov(p, "\"\""); + return p; // pointer to end 0 of 'to' +} + +/* Prints a "session_var=value" string. Used by mysqlbinlog to print some SET commands just before it prints a query. */ @@ -607,6 +630,7 @@ end: #else #define UNLOCK_MUTEX #define LOCK_MUTEX +#define max_allowed_packet (*mysql_get_parameters()->p_max_allowed_packet) #endif /* @@ -2910,7 +2934,7 @@ int Rand_log_event::exec_event(struct st_relay_log_info* rli) void User_var_log_event::pack_info(Protocol* protocol) { char *buf= 0; - uint val_offset= 2 + name_len; + uint val_offset= 4 + name_len; uint event_len= val_offset; if (is_null) @@ -2934,16 +2958,21 @@ void User_var_log_event::pack_info(Protocol* protocol) event_len= longlong10_to_str(uint8korr(val), buf + val_offset,-10)-buf; break; case STRING_RESULT: - /* - This is correct as pack_info is used for SHOW BINLOG command - only. But be carefull this is may be incorrect in other cases as - string may contain \ and '. - */ - event_len= val_offset + 2 + val_len; - buf= my_malloc(event_len, MYF(MY_WME)); - buf[val_offset]= '\''; - memcpy(buf + val_offset + 1, val, val_len); - buf[val_offset + val_len + 1]= '\''; + /* 15 is for 'COLLATE' and other chars */ + buf= my_malloc(event_len+val_len*2+1+2*MY_CS_NAME_SIZE+15, MYF(MY_WME)); + CHARSET_INFO *cs; + if (!(cs= get_charset(charset_number, MYF(0)))) + { + strmov(buf+val_offset, "???"); + event_len+= 3; + } + else + { + char *p= strxmov(buf + val_offset, "_", cs->csname, " ", NullS); + p= str_to_hex(p, val, val_len); + p= strxmov(p, " COLLATE ", cs->name, NullS); + event_len= p-buf; + } break; case ROW_RESULT: default: @@ -2952,8 +2981,10 @@ void User_var_log_event::pack_info(Protocol* protocol) } } buf[0]= '@'; - buf[1+name_len]= '='; - memcpy(buf+1, name, name_len); + buf[1]= '`'; + buf[2+name_len]= '`'; + buf[3+name_len]= '='; + memcpy(buf+2, name, name_len); protocol->store(buf, event_len, &my_charset_bin); my_free(buf, MYF(MY_ALLOW_ZERO_PTR)); } @@ -3046,8 +3077,9 @@ void User_var_log_event::print(FILE* file, bool short_form, LAST_EVENT_INFO* las fprintf(file, "\tUser_var\n"); } - fprintf(file, "SET @"); + fprintf(file, "SET @`"); my_fwrite(file, (byte*) name, (uint) (name_len), MYF(MY_NABP | MY_WME)); + fprintf(file, "`"); if (is_null) { @@ -3067,7 +3099,42 @@ void User_var_log_event::print(FILE* file, bool short_form, LAST_EVENT_INFO* las fprintf(file, ":=%s;\n", int_buf); break; case STRING_RESULT: - fprintf(file, ":='%s';\n", val); + { + /* + Let's express the string in hex. That's the most robust way. If we + print it in character form instead, we need to escape it with + character_set_client which we don't know (we will know it in 5.0, but + in 4.1 we don't know it easily when we are printing + User_var_log_event). Explanation why we would need to bother with + character_set_client (quoting Bar): + > Note, the parser doesn't switch to another unescaping mode after + > it has met a character set introducer. + > For example, if an SJIS client says something like: + > SET @a= _ucs2 \0a\0b' + > the string constant is still unescaped according to SJIS, not + > according to UCS2. + */ + char *p, *q; + if (!(p= (char *)my_alloca(2*val_len+1+2))) // 2 hex digits per byte + break; // no error, as we are 'void' + str_to_hex(p, val, val_len); + /* + For proper behaviour when mysqlbinlog|mysql, we need to explicitely + specify the variable's collation. It will however cause problems when + people want to mysqlbinlog|mysql into another server not supporting the + character set. But there's not much to do about this and it's unlikely. + */ + CHARSET_INFO *cs; + if (!(cs= get_charset(charset_number, MYF(0)))) + /* + Generate an unusable command (=> syntax error) is probably the best + thing we can do here. + */ + fprintf(file, ":=???;\n"); + else + fprintf(file, ":=_%s %s COLLATE %s;\n", cs->csname, p, cs->name); + my_afree(p); + } break; case ROW_RESULT: default: @@ -3088,7 +3155,9 @@ void User_var_log_event::print(FILE* file, bool short_form, LAST_EVENT_INFO* las int User_var_log_event::exec_event(struct st_relay_log_info* rli) { Item *it= 0; - CHARSET_INFO *charset= get_charset(charset_number, MYF(0)); + CHARSET_INFO *charset; + if (!(charset= get_charset(charset_number, MYF(MY_WME)))) + return 1; LEX_STRING user_var_name; user_var_name.str= name; user_var_name.length= name_len; |