From: Date: August 15 2008 1:32pm Subject: commit into mysql-5.1 branch (bar:2675) Bug#31455 List-Archive: http://lists.mysql.com/commits/51720 X-Bug: 31455 Message-Id: <200808151132.m7FBWSYn001308@bar.myoffice.izhnet.ru> MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit #At file:///home/bar/mysql-bzr/mysql-5.1.b31455/ 2675 Alexander Barkov 2008-08-15 Bug#31455 mysqlbinlog don't print user readable info about RBR events Implementing -v command line parameter to mysqlbinlog to decode and print row events. mysql-test/r/mysqlbinlog_row.result mysql-test/r/mysqlbinlog_row_trans.result mysql-test/t/mysqlbinlog_row.test mysql-test/t/mysqlbinlog_row_trans.test Adding tests client/Makefile.am Adding new files to symlink client/mysqlbinlog.cc Adding -v option sql/log_event.cc Impelentations of the new methods sql/log_event.h Declaration of the new methods and member sql/rpl_tblmap.cc Adding pre-processor conditions sql/rpl_tblmap.h Adding pre-processor conditions sql/rpl_utility.h Adding pre-processor conditions modified: client/Makefile.am client/mysqlbinlog.cc sql/log_event.cc sql/log_event.h sql/rpl_tblmap.cc sql/rpl_tblmap.h sql/rpl_utility.h === modified file 'client/Makefile.am' --- a/client/Makefile.am 2008-06-18 16:17:15 +0000 +++ b/client/Makefile.am 2008-08-15 11:30:49 +0000 @@ -103,6 +103,7 @@ -DDATADIR="\"$(localstatedir)\"" sql_src=log_event.h mysql_priv.h rpl_constants.h \ + rpl_utility.h rpl_tblmap.h rpl_tblmap.cc \ log_event.cc my_decimal.h my_decimal.cc \ log_event_old.h log_event_old.cc \ rpl_record_old.h rpl_record_old.cc === modified file 'client/mysqlbinlog.cc' --- a/client/mysqlbinlog.cc 2008-04-03 17:14:57 +0000 +++ b/client/mysqlbinlog.cc 2008-08-15 11:30:49 +0000 @@ -83,6 +83,8 @@ static char* pass = 0; static char *charset= 0; +static uint verbose= 0; + static ulonglong start_position, stop_position; #define start_position_mot ((my_off_t)start_position) #define stop_position_mot ((my_off_t)stop_position) @@ -1063,6 +1065,8 @@ {"user", 'u', "Connect to the remote server as username.", (uchar**) &user, (uchar**) &user, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"verbose", 'v', "Write more. (-v -v -v gives the table output format).", 0, + 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, {"version", 'V', "Print version and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, {"open_files_limit", OPT_OPEN_FILES_LIMIT, @@ -1258,6 +1262,12 @@ (find_type_or_exit(argument, &base64_output_mode_typelib, opt->name)-1); } break; + case 'v': + if (argument == disabled_my_option) + verbose= 0; + else + verbose++; + break; case 'V': print_version(); exit(0); @@ -1343,6 +1353,8 @@ */ fprintf(result_file, "DELIMITER /*!*/;\n"); strmov(print_event_info.delimiter, "/*!*/;"); + + print_event_info.verbose= verbose; rc= (remote_opt ? dump_remote_log_entries(&print_event_info, logname) : dump_local_log_entries(&print_event_info, logname)); === modified file 'sql/log_event.cc' --- a/sql/log_event.cc 2008-08-04 05:04:47 +0000 +++ b/sql/log_event.cc 2008-08-15 11:30:49 +0000 @@ -32,7 +32,6 @@ #include "rpl_utility.h" #include "rpl_record.h" #include - #endif /* MYSQL_CLIENT */ #include @@ -1331,7 +1330,7 @@ } *c= '\0'; - if (hex_string[0]) + if (hex_string[0]); { char emit_buf[256]; int const bytes_written= @@ -1353,6 +1352,486 @@ } +static void +my_b_write_quoted(IO_CACHE *file, const uchar *ptr, uint length) +{ + const uchar *s; + my_b_printf(file, "'"); + for (s= ptr; length; s++, length--) + { + if (*s > 0x1F) + my_b_write(file, s, 1); + else + { + uchar hex[10]; + size_t len= sprintf((char*) hex, "\\x%02X", *s); + my_b_write(file, hex, len); + } + } + my_b_write(file, ptr, length); + my_b_printf(file, "'"); +} + + +static void +my_b_write_set(IO_CACHE *file, const uchar *ptr, uint nbits) +{ + uint bitnum; + my_b_printf(file, "b'"); + for (bitnum=0 ; bitnum < nbits; bitnum++) + { + int is_set= (ptr[bitnum / 8] >> (7 - (bitnum % 8))) & 0x01; + my_b_write(file, (const uchar*) (is_set ? "1" : "0"), 1); + } + my_b_printf(file, "'"); +} + + +static void +my_b_write_bit(IO_CACHE *file, const uchar *ptr, uint nbits) +{ + uint bitnum, nbits8= ((nbits + 7) / 8) * 8, skip_bits= nbits8 - nbits; + my_b_printf(file, "b'"); + for (bitnum= skip_bits ; bitnum < nbits8; bitnum++) + { + int is_set= (ptr[(bitnum) / 8] >> (7 - bitnum % 8)) & 0x01; + my_b_write(file, (const uchar*) (is_set ? "1" : "0"), 1); + } + my_b_printf(file, "'"); +} + + +static size_t +my_b_write_quoted_with_length(IO_CACHE *file, const uchar *ptr, uint length) +{ + if (length < 256) + { + length= *ptr; + my_b_write_quoted(file, ptr + 1, length); + return length + 1; + } + else + { + length= uint2korr(ptr); + my_b_write_quoted(file, ptr + 2, length); + return length + 2; + } +} + + +static size_t +log_event_print_value(IO_CACHE *file, const uchar *ptr, + uint type, uint meta, char *typestr) +{ + uint32 length= 0; + + if (type == MYSQL_TYPE_STRING) + { + if (meta >= 256) + { + uint byte0= meta >> 8; + uint byte1= meta & 0xFF; + + if (((byte0 & 0x30) != 0x30)) + { + /* a long CHAR() field: see #37426 */ + length= byte1 | (((byte0 & 0x30) ^ 0x30) << 4); + type= byte0 | 0x30; + goto beg; + } + + switch (byte0) + { + case MYSQL_TYPE_SET: + case MYSQL_TYPE_ENUM: + case MYSQL_TYPE_STRING: + type= byte0; + length= byte1; + break; + + default: + + { + char smeta[256]; + sprintf(smeta, "%04X", meta); + my_b_printf(file, + "!! Don't know how to handle column type=%d meta=%d (%s)", + type, meta, smeta); + return 0; + } + } + } + else + length= meta; + } + + +beg: + + switch (type) { + case MYSQL_TYPE_TIMESTAMP: + { + uint32 i32= uint4korr(ptr); + my_b_printf(file, "%d", i32); + strcpy(typestr, "TIMESTAMP"); + return 4; + } + + case MYSQL_TYPE_LONG: + { + int32 i32= sint4korr(ptr); + my_b_printf(file, "%d", i32); + strcpy(typestr, "INT"); + return 4; + } + + case MYSQL_TYPE_DATETIME: + { + uint d, t; + uint64 i64= uint8korr(ptr); /* YYYYMMDDhhmmss */ + d= i64 / 1000000; + t= i64 % 1000000; + my_b_printf(file, "%04d-%02d-%02d %02d:%02d:%02d", + d / 10000, (d % 10000) / 100, d % 100, + t / 10000, (t % 10000) / 100, t % 100); + strcpy(typestr, "DATETIME"); + return 8; + } + + case MYSQL_TYPE_LONGLONG: + { + CHARSET_INFO *cs= &my_charset_bin; + char tmp[30]; + uint64 i64= uint8korr(ptr); + length= (*cs->cset->longlong10_to_str)(cs, tmp, sizeof(tmp), -10, i64); + tmp[length]= '\0'; + my_b_printf(file, "%s", tmp); + strcpy(typestr, "LONGINT"); + return 8; + } + + case MYSQL_TYPE_INT24: + { + int32 i32= sint3korr(ptr); + my_b_printf(file, "%d", i32); + strcpy(typestr, "MEDIUMINT"); + return 3; + } + + case MYSQL_TYPE_TIME: + { + uint32 i32= uint3korr(ptr); + my_b_printf(file, "'%02d:%02d:%02d'", + i32 / 10000, (i32 % 10000) / 100, i32 % 100); + strcpy(typestr, "TIME"); + return 3; + } + + case MYSQL_TYPE_DATE: + { + uint i32= uint3korr(ptr); + my_b_printf(file , "'%04d:%02d:%02d'", + (i32 / (16L * 32L)), (i32 / 32L % 16L), (i32 % 32L)); + strcpy(typestr, "DATE"); + return 3; + } + + case MYSQL_TYPE_YEAR: + { + uint32 i32= *ptr; + my_b_printf(file, "%04d", i32+ 1900); + strcpy(typestr, "YEAR"); + return 1; + } + + case MYSQL_TYPE_SHORT: + { + int32 i32= sint2korr(ptr); + my_b_printf(file, "%d", i32); + strcpy(typestr, "SHORTINT"); + return 2; + } + + case MYSQL_TYPE_TINY: + my_b_printf(file, "%d", (int) (signed char) *ptr); + strcpy(typestr, "TINYINT"); + return 1; + + case MYSQL_TYPE_ENUM: + switch (length) { + case 1: + my_b_printf(file, "%d", (int) *ptr); + strcpy(typestr, "ENUM(1 byte)"); + return 1; + case 2: + { + int32 i32= uint2korr(ptr); + my_b_printf(file, "%d", i32); + strcpy(typestr, "ENUM(2 bytes)"); + return 2; + } + default: + my_b_printf(file, "!! Unknown ENUM packlen=%d", length); + return 0; + } + break; + + case MYSQL_TYPE_BLOB: + switch (meta) { + case 1: + length= *ptr; + my_b_write_quoted(file, ptr + 1, length); + strcpy(typestr, "TINYBLOB/TINYTEXT"); + return length + 1; + case 2: + length= uint2korr(ptr); + my_b_write_quoted(file, ptr + 2, length); + strcpy(typestr, "BLOB/TEXT"); + return length + 2; + case 3: + length= uint3korr(ptr); + my_b_write_quoted(file, ptr + 3, length); + strcpy(typestr, "MEDIUMBLOB/MEDIUMTEXT"); + return length + 3; + case 4: + length= uint4korr(ptr); + my_b_write_quoted(file, ptr + 4, length); + strcpy(typestr, "LONGBLOB/LONGTEXT"); + return length + 4; + default: + my_b_printf(file, "!! Unknown BLOB packlen=%d", length); + return 0; + } + + case MYSQL_TYPE_SET: + my_b_write_set(file, ptr , length * 8); + sprintf(typestr, "SET(%d bytes)", length); + return length; + + case MYSQL_TYPE_VARCHAR: + case MYSQL_TYPE_VAR_STRING: + length= meta; + sprintf(typestr, "VARSTRING(%d)", length); + return my_b_write_quoted_with_length(file, ptr, length); + + case MYSQL_TYPE_STRING: + sprintf(typestr, "STRING(%d)", length); + return my_b_write_quoted_with_length(file, ptr, length); + + case MYSQL_TYPE_NEWDECIMAL: + { + uint precision= meta >> 8; + uint decimals= meta & 0xFF; + uint bin_size= my_decimal_get_binary_size(precision, decimals); + my_decimal dec; + binary2my_decimal(E_DEC_FATAL_ERROR, (uchar*) ptr, &dec, + precision, decimals); + int i, end; + char buff[512], *pos; + pos= buff; + pos+= my_sprintf(buff, (buff, "%s", dec.sign() ? "-" : "")); + end= ROUND_UP(dec.frac) + ROUND_UP(dec.intg)-1; + for (i=0; i < end; i++) + pos+= my_sprintf(pos, (pos, "%09d.", dec.buf[i])); + pos+= my_sprintf(pos, (pos, "%09d", dec.buf[i])); + my_b_printf(file, "%s", buff); + sprintf(typestr, "DECIMAL(%d,%d)", precision, decimals); + return bin_size; + } + + case MYSQL_TYPE_FLOAT: + { + float fl; + float4get(fl, ptr); + char tmp[320]; + sprintf(tmp, "%-20g", (double) fl); + my_b_printf(file, "%s", tmp); + strcpy(typestr, "FLOAT"); + return 4; + } + + case MYSQL_TYPE_DOUBLE: + { + double dbl; + doubleget(dbl, ptr); + char tmp[320]; + sprintf(tmp, "%-.20g", dbl); + my_b_printf(file, "%s", tmp); + strcpy(typestr, "DOUBLE"); + return 8; + } + + case MYSQL_TYPE_BIT: + { + /* Meta-data: bit_len, bytes_in_rec, 2 bytes */ + uint nbits= ((meta >> 8) * 8) + (meta & 0xFF); + length= (nbits + 7) / 8; + my_b_write_bit(file, ptr, nbits); + sprintf(typestr, "BIT(%d)", nbits); + return length; + } + + default: + { + char smeta[256]; + sprintf(smeta, "%04X", meta); + my_b_printf(file, + "!! Don't know how to handle column type=%d meta=%d (%s)", + type, meta, smeta); + } + break; + } + *typestr= 0; + + return 0; +} + + +size_t +Rows_log_event::print_verbose_one_row(IO_CACHE *file, table_def *td, + PRINT_EVENT_INFO *print_event_info, + MY_BITMAP *cols_bitmap, + const uchar *value, const uchar *prefix) +{ + const uchar *value0= value; + const uchar *null_bits= value; + char typestr[64]= ""; + + value+= (m_width + 7) / 8; + + my_b_printf(file, "%s", prefix); + + for (size_t i= 0; i < td->size(); i ++) + { + int is_null= (null_bits[i / 8] >> (i % 8)) & 0x01; + + if (bitmap_is_set(cols_bitmap, i) == 0) + continue; + + if (is_null) + { + my_b_printf(file, "### @%d=NULL", i + 1); + } + else + { + my_b_printf(file, "### @%d=", i + 1); + size_t size= log_event_print_value(file, value, + td->type(i), td->field_metadata(i), + typestr); + + if (!size) + return 0; + + value+= size; + } + + if (print_event_info->verbose > 1) + { + my_b_printf(file, " /* "); + + if (typestr[0]) + my_b_printf(file, "%s ", typestr); + else + my_b_printf(file, "type=%d ", td->type(i)); + + my_b_printf(file, "meta=%d null=%d is_null=%d ", + td->field_metadata(i), + td->maybe_null(i), is_null); + my_b_printf(file, "*/"); + } + + my_b_printf(file, "\n"); + } + return value - value0; +} + + +void Write_rows_log_event::print_verbose(IO_CACHE *file, + PRINT_EVENT_INFO *print_event_info) +{ + Table_map_log_event *map; + table_def *td; + + if (!(map= print_event_info->m_table_map.get_table(m_table_id)) || + !(td= map->create_table_def())) + { + my_b_printf(file, "### Error in INSERT into unknown table #%d", m_table_id); + return; + } + + for (const uchar *value= m_rows_buf ; value < m_rows_end ;) + { + my_b_printf(file, "### INSERT INTO %s.%s\n", map->m_dbnam, map->m_tblnam); + size_t length= print_verbose_one_row(file, td, print_event_info, + &m_cols, value, + (const uchar*) "### SET\n"); + if (!length) + break; + value+= length; + } + delete td; +} + + +void Delete_rows_log_event::print_verbose(IO_CACHE *file, + PRINT_EVENT_INFO *print_event_info) +{ + Table_map_log_event *map; + table_def *td; + + if (!(map= print_event_info->m_table_map.get_table(m_table_id)) || + !(td= map->create_table_def())) + { + my_b_printf(file, "### Error in DELETE from unknown table #%d", m_table_id); + return; + } + + for (const uchar *value= m_rows_buf ; value < m_rows_end ;) + { + my_b_printf(file, "### DELETE FROM %s.%s\n", map->m_dbnam, map->m_tblnam); + size_t length= print_verbose_one_row(file, td, print_event_info, + &m_cols, value, + (const uchar*) "### WHERE \n"); + if (!length) + break; + value+= length; + } + delete td; +} + + +void Update_rows_log_event::print_verbose(IO_CACHE *file, + PRINT_EVENT_INFO *print_event_info) +{ + Table_map_log_event *map; + table_def *td; + + if (!(map= print_event_info->m_table_map.get_table(m_table_id)) || + !(td= map->create_table_def())) + { + my_b_printf(file, "### Error in UPDATE to unknown table #%d", m_table_id); + return; + } + + for (const uchar *value= m_rows_buf; value < m_rows_end; ) + { + my_b_printf(file, "### UPDATE %s.%s\n", map->m_dbnam, map->m_tblnam); + for (uchar j= 0; j < 2; j++) + { + const char *prefix= ((j == 0) ? "### WHERE \n" : "### SET \n"); + MY_BITMAP *bitmap= ((j == 0) ? &m_cols_ai : &m_cols); + size_t length= print_verbose_one_row(file, td, print_event_info, bitmap, + value, (const uchar*) prefix); + if (!length) + break; + + value+= length; + } + } + delete td; +} + + void Log_event::print_base64(IO_CACHE* file, PRINT_EVENT_INFO* print_event_info, bool more) @@ -1382,6 +1861,40 @@ if (!more) my_b_printf(file, "'%s\n", print_event_info->delimiter); + if (print_event_info->verbose) + { + Rows_log_event *ev= NULL; + + if (ptr[4] == TABLE_MAP_EVENT) + { + Table_map_log_event *map; + map= new Table_map_log_event((const char*) ptr, size, + glob_description_event); + print_event_info->m_table_map.set_table(map->get_table_id(), map); + } + else if (ptr[4] == WRITE_ROWS_EVENT) + { + ev= new Write_rows_log_event((const char*) ptr, size, + glob_description_event); + } + else if (ptr[4] == DELETE_ROWS_EVENT) + { + ev= new Delete_rows_log_event((const char*) ptr, size, + glob_description_event); + } + else if (ptr[4] == UPDATE_ROWS_EVENT) + { + ev= new Update_rows_log_event((const char*) ptr, size, + glob_description_event); + } + + if (ev) + { + ev->print_verbose(file, print_event_info); + delete ev; + } + } + my_free(tmp_str, MYF(0)); DBUG_VOID_RETURN; } === modified file 'sql/log_event.h' --- a/sql/log_event.h 2008-03-28 13:52:33 +0000 +++ b/sql/log_event.h 2008-08-15 11:30:49 +0000 @@ -34,6 +34,14 @@ #include #include "rpl_constants.h" + +#ifdef MYSQL_CLIENT +#include "rpl_utility.h" +#include "hash.h" +#include "rpl_tblmap.h" +#include "rpl_tblmap.cc" +#endif + #ifndef MYSQL_CLIENT #include "rpl_record.h" #include "rpl_reporting.h" @@ -634,6 +642,11 @@ uint8 common_header_len; char delimiter[16]; +#ifdef MYSQL_CLIENT + uint verbose; + table_mapping m_table_map; +#endif + /* These two caches are used by the row-based replication events to collect the header information and the main body of the events @@ -3235,6 +3248,15 @@ ~Table_map_log_event(); +#ifdef MYSQL_CLIENT + table_def *create_table_def() + { + return new table_def(m_coltype, m_colcnt, m_field_metadata, + m_field_metadata_size, m_null_bits); + } + ulong get_table_id() const { return m_table_id; } +#endif + virtual Log_event_type get_type_code() { return TABLE_MAP_EVENT; } virtual bool is_valid() const { return m_memory != NULL; /* we check malloc */ } @@ -3265,10 +3287,13 @@ #ifndef MYSQL_CLIENT TABLE *m_table; #endif +public: char const *m_dbnam; size_t m_dblen; char const *m_tblnam; size_t m_tbllen; + +private: ulong m_colcnt; uchar *m_coltype; @@ -3365,6 +3390,12 @@ #ifdef MYSQL_CLIENT /* not for direct call, each derived has its own ::print() */ virtual void print(FILE *file, PRINT_EVENT_INFO *print_event_info)= 0; + virtual void print_verbose(IO_CACHE *file, + PRINT_EVENT_INFO *print_event_info)= 0; + size_t print_verbose_one_row(IO_CACHE *file, table_def *td, + PRINT_EVENT_INFO *print_event_info, + MY_BITMAP *cols_bitmap, + const uchar *ptr, const uchar *prefix); #endif #ifndef MYSQL_CLIENT @@ -3578,6 +3609,7 @@ #ifdef MYSQL_CLIENT void print(FILE *file, PRINT_EVENT_INFO *print_event_info); + void print_verbose(IO_CACHE* file, PRINT_EVENT_INFO* print_event_info); #endif #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) @@ -3652,6 +3684,7 @@ #ifdef MYSQL_CLIENT void print(FILE *file, PRINT_EVENT_INFO *print_event_info); + void print_verbose(IO_CACHE* file, PRINT_EVENT_INFO* print_event_info); #endif #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) @@ -3717,6 +3750,7 @@ #ifdef MYSQL_CLIENT void print(FILE *file, PRINT_EVENT_INFO *print_event_info); + void print_verbose(IO_CACHE* file, PRINT_EVENT_INFO* print_event_info); #endif #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) === modified file 'sql/rpl_tblmap.cc' --- a/sql/rpl_tblmap.cc 2007-05-10 09:59:39 +0000 +++ b/sql/rpl_tblmap.cc 2008-08-15 11:30:49 +0000 @@ -19,7 +19,11 @@ #include "rpl_tblmap.h" +#ifdef MYSQL_CLIENT +#define MAYBE_TABLE_NAME(T) ("") +#else #define MAYBE_TABLE_NAME(T) ((T) ? (T)->s->table_name.str : "<>") +#endif #define TABLE_ID_HASH_SIZE 32 #define TABLE_ID_CHUNK 256 @@ -46,7 +50,7 @@ free_root(&m_mem_root, MYF(0)); } -st_table* table_mapping::get_table(ulong table_id) +TABLE* table_mapping::get_table(ulong table_id) { DBUG_ENTER("table_mapping::get_table(ulong)"); DBUG_PRINT("enter", ("table_id: %lu", table_id)); === modified file 'sql/rpl_tblmap.h' --- a/sql/rpl_tblmap.h 2007-05-10 09:59:39 +0000 +++ b/sql/rpl_tblmap.h 2008-08-15 11:30:49 +0000 @@ -17,8 +17,14 @@ #define TABLE_MAPPING_H /* Forward declarations */ +#ifndef MYSQL_CLIENT struct st_table; typedef st_table TABLE; +#else +class Table_map_log_event; +typedef Table_map_log_event TABLE; +#endif + /* CLASS table_mapping === modified file 'sql/rpl_utility.h' --- a/sql/rpl_utility.h 2007-10-01 09:25:32 +0000 +++ b/sql/rpl_utility.h 2008-08-15 11:30:49 +0000 @@ -236,7 +236,9 @@ @retval 1 if the table definition is not compatible with @c table @retval 0 if the table definition is compatible with @c table */ +#ifndef MYSQL_CLIENT int compatible_with(Relay_log_info const *rli, TABLE *table) const; +#endif private: ulong m_size; // Number of elements in the types array @@ -247,6 +249,8 @@ uchar *m_memory; }; + +#ifndef MYSQL_CLIENT /** Extend the normal table list with a few new fields needed by the slave thread, but nowhere else. @@ -288,6 +292,7 @@ }; } +#endif #define DBUG_PRINT_BITSET(N,FRM,BS) \ do { \