diff options
Diffstat (limited to 'sql')
-rw-r--r-- | sql/log_event.cc | 570 | ||||
-rw-r--r-- | sql/log_event.h | 30 | ||||
-rw-r--r-- | sql/mysql_priv.h | 1 | ||||
-rw-r--r-- | sql/rpl_tblmap.cc | 18 | ||||
-rw-r--r-- | sql/rpl_tblmap.h | 7 | ||||
-rw-r--r-- | sql/rpl_utility.h | 5 | ||||
-rw-r--r-- | sql/sql_base.cc | 9 | ||||
-rw-r--r-- | sql/sql_repl.cc | 5 |
8 files changed, 641 insertions, 4 deletions
diff --git a/sql/log_event.cc b/sql/log_event.cc index ff6afd013c5..0690e0b4f01 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -1353,6 +1353,542 @@ void Log_event::print_header(IO_CACHE* file, } +/** + Prints a quoted string to io cache. + Control characters are displayed as hex sequence, e.g. \x00 + + @param[in] file IO cache + @param[in] prt Pointer to string + @param[in] length String length +*/ + +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 > 0 ; s++, length--) + { + if (*s > 0x1F) + my_b_write(file, s, 1); + else + { + uchar hex[10]; + size_t len= my_snprintf((char*) hex, sizeof(hex), "%s%02x", "\\x", *s); + my_b_write(file, hex, len); + } + } + my_b_printf(file, "'"); +} + + +/** + Prints a bit string to io cache in format b'1010'. + + @param[in] file IO cache + @param[in] ptr Pointer to string + @param[in] nbits Number of bits +*/ +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, "'"); +} + + +/** + Prints a packed string to io cache. + The string consists of length packed to 1 or 2 bytes, + followed by string data itself. + + @param[in] file IO cache + @param[in] ptr Pointer to string + @param[in] length String size + + @retval - number of bytes scanned. +*/ +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; + } +} + + +/** + Prints a 32-bit number in both signed and unsigned representation + + @param[in] file IO cache + @param[in] sl Signed number + @param[in] ul Unsigned number +*/ +static void +my_b_write_sint32_and_uint32(IO_CACHE *file, int32 si, uint32 ui) +{ + my_b_printf(file, "%d", si); + if (si < 0) + my_b_printf(file, " (%u)", ui); +} + + +/** + Print a packed value of the given SQL type into IO cache + + @param[in] file IO cache + @param[in] ptr Pointer to string + @param[in] type Column type + @param[in] meta Column meta information + @param[out] typestr SQL type string buffer (for verbose output) + @param[out] typestr_length Size of typestr + + @retval - number of bytes scanned from ptr. +*/ + +static size_t +log_event_print_value(IO_CACHE *file, const uchar *ptr, + uint type, uint meta, + char *typestr, size_t typestr_length) +{ + 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 tmp[5]; + my_snprintf(tmp, sizeof(tmp), "%04X", meta); + my_b_printf(file, + "!! Don't know how to handle column type=%d meta=%d (%s)", + type, meta, tmp); + return 0; + } + } + } + else + length= meta; + } + + +beg: + + switch (type) { + case MYSQL_TYPE_LONG: + { + int32 si= sint4korr(ptr); + uint32 ui= uint4korr(ptr); + my_b_write_sint32_and_uint32(file, si, ui); + my_snprintf(typestr, typestr_length, "INT"); + return 4; + } + + case MYSQL_TYPE_TINY: + { + my_b_write_sint32_and_uint32(file, (int) (signed char) *ptr, + (uint) (unsigned char) *ptr); + my_snprintf(typestr, typestr_length, "TINYINT"); + return 1; + } + + case MYSQL_TYPE_SHORT: + { + int32 si= (int32) sint2korr(ptr); + uint32 ui= (uint32) uint2korr(ptr); + my_b_write_sint32_and_uint32(file, si, ui); + my_snprintf(typestr, typestr_length, "SHORTINT"); + return 2; + } + + case MYSQL_TYPE_INT24: + { + int32 si= sint3korr(ptr); + uint32 ui= uint3korr(ptr); + my_b_write_sint32_and_uint32(file, si, ui); + my_snprintf(typestr, typestr_length, "MEDIUMINT"); + return 3; + } + + case MYSQL_TYPE_LONGLONG: + { + char tmp[64]; + longlong si= sint8korr(ptr); + longlong10_to_str(si, tmp, -10); + my_b_printf(file, "%s", tmp); + if (si < 0) + { + ulonglong ui= uint8korr(ptr); + longlong10_to_str((longlong) ui, tmp, 10); + my_b_printf(file, " (%s)", tmp); + } + my_snprintf(typestr, typestr_length, "LONGINT"); + return 8; + } + + 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); + my_snprintf(typestr, typestr_length, "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); /* my_snprintf doesn't support %-20g */ + my_snprintf(typestr, typestr_length, "FLOAT"); + return 4; + } + + case MYSQL_TYPE_DOUBLE: + { + double dbl; + doubleget(dbl, ptr); + char tmp[320]; + sprintf(tmp, "%-.20g", dbl); /* my_snprintf doesn't support %-20g */ + 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); + my_snprintf(typestr, typestr_length, "BIT(%d)", nbits); + return length; + } + + case MYSQL_TYPE_TIMESTAMP: + { + uint32 i32= uint4korr(ptr); + my_b_printf(file, "%d", i32); + my_snprintf(typestr, typestr_length, "TIMESTAMP"); + 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); + my_snprintf(typestr, typestr_length, "DATETIME"); + return 8; + } + + case MYSQL_TYPE_TIME: + { + uint32 i32= uint3korr(ptr); + my_b_printf(file, "'%02d:%02d:%02d'", + i32 / 10000, (i32 % 10000) / 100, i32 % 100); + my_snprintf(typestr, typestr_length, "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)); + my_snprintf(typestr, typestr_length, "DATE"); + return 3; + } + + case MYSQL_TYPE_YEAR: + { + uint32 i32= *ptr; + my_b_printf(file, "%04d", i32+ 1900); + my_snprintf(typestr, typestr_length, "YEAR"); + return 1; + } + + case MYSQL_TYPE_ENUM: + switch (length) { + case 1: + my_b_printf(file, "%d", (int) *ptr); + my_snprintf(typestr, typestr_length, "ENUM(1 byte)"); + return 1; + case 2: + { + int32 i32= uint2korr(ptr); + my_b_printf(file, "%d", i32); + my_snprintf(typestr, typestr_length, "ENUM(2 bytes)"); + return 2; + } + default: + my_b_printf(file, "!! Unknown ENUM packlen=%d", length); + return 0; + } + break; + + case MYSQL_TYPE_SET: + my_b_write_bit(file, ptr , length * 8); + my_snprintf(typestr, typestr_length, "SET(%d bytes)", length); + return length; + + case MYSQL_TYPE_BLOB: + switch (meta) { + case 1: + length= *ptr; + my_b_write_quoted(file, ptr + 1, length); + my_snprintf(typestr, typestr_length, "TINYBLOB/TINYTEXT"); + return length + 1; + case 2: + length= uint2korr(ptr); + my_b_write_quoted(file, ptr + 2, length); + my_snprintf(typestr, typestr_length, "BLOB/TEXT"); + return length + 2; + case 3: + length= uint3korr(ptr); + my_b_write_quoted(file, ptr + 3, length); + my_snprintf(typestr, typestr_length, "MEDIUMBLOB/MEDIUMTEXT"); + return length + 3; + case 4: + length= uint4korr(ptr); + my_b_write_quoted(file, ptr + 4, length); + my_snprintf(typestr, typestr_length, "LONGBLOB/LONGTEXT"); + return length + 4; + default: + my_b_printf(file, "!! Unknown BLOB packlen=%d", length); + return 0; + } + + case MYSQL_TYPE_VARCHAR: + case MYSQL_TYPE_VAR_STRING: + length= meta; + my_snprintf(typestr, typestr_length, "VARSTRING(%d)", length); + return my_b_write_quoted_with_length(file, ptr, length); + + case MYSQL_TYPE_STRING: + my_snprintf(typestr, typestr_length, "STRING(%d)", length); + return my_b_write_quoted_with_length(file, ptr, length); + + default: + { + char tmp[5]; + my_snprintf(tmp, sizeof(tmp), "%04x", meta); + my_b_printf(file, + "!! Don't know how to handle column type=%d meta=%d (%s)", + type, meta, tmp); + } + break; + } + *typestr= 0; + return 0; +} + + +/** + Print a packed row into IO cache + + @param[in] file IO cache + @param[in] td Table definition + @param[in] print_event_into Print parameters + @param[in] cols_bitmap Column bitmaps. + @param[in] value Pointer to packed row + @param[in] prefix Row's SQL clause ("SET", "WHERE", etc) + + @retval - number of bytes scanned. +*/ + + +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, sizeof(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 nullable=%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; +} + + +/** + Print a row event into IO cache in human readable form (in SQL format) + + @param[in] file IO cache + @param[in] print_event_into Print parameters +*/ +void Rows_log_event::print_verbose(IO_CACHE *file, + PRINT_EVENT_INFO *print_event_info) +{ + Table_map_log_event *map; + table_def *td; + const char *sql_command, *sql_clause1, *sql_clause2; + Log_event_type type_code= get_type_code(); + + switch (type_code) { + case WRITE_ROWS_EVENT: + sql_command= "INSERT INTO"; + sql_clause1= "### SET\n"; + sql_clause2= NULL; + break; + case DELETE_ROWS_EVENT: + sql_command= "DELETE FROM"; + sql_clause1= "### WHERE\n"; + sql_clause2= NULL; + break; + case UPDATE_ROWS_EVENT: + sql_command= "UPDATE"; + sql_clause1= "### WHERE\n"; + sql_clause2= "### SET\n"; + break; + default: + sql_command= sql_clause1= sql_clause2= NULL; + DBUG_ASSERT(0); /* Not possible */ + } + + if (!(map= print_event_info->m_table_map.get_table(m_table_id)) || + !(td= map->create_table_def())) + { + my_b_printf(file, "### Row event for unknown table #%d", m_table_id); + return; + } + + for (const uchar *value= m_rows_buf; value < m_rows_end; ) + { + size_t length; + my_b_printf(file, "### %s %s.%s\n", + sql_command, + map->get_db_name(), map->get_table_name()); + /* Print the first image */ + if (!(length= print_verbose_one_row(file, td, print_event_info, + &m_cols_ai, value, + (const uchar*) sql_clause1))) + goto end; + value+= length; + + /* Print the second image (for UPDATE only) */ + if (sql_clause2) + { + if (!(length= print_verbose_one_row(file, td, print_event_info, + &m_cols, value, + (const uchar*) sql_clause2))) + goto end; + value+= length; + } + } + +end: + delete td; +} + +#ifdef MYSQL_CLIENT +void free_table_map_log_event(Table_map_log_event *event) +{ + delete event; +} +#endif + void Log_event::print_base64(IO_CACHE* file, PRINT_EVENT_INFO* print_event_info, bool more) @@ -1382,6 +1918,40 @@ void Log_event::print_base64(IO_CACHE* file, 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; } diff --git a/sql/log_event.h b/sql/log_event.h index 76d92b23189..e1b00474361 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -34,6 +34,14 @@ #include <my_bitmap.h> #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 @@ typedef struct st_print_event_info 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,17 @@ public: ~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; } + const char *get_table_name() const { return m_tblnam; } + const char *get_db_name() const { return m_dbnam; } +#endif + virtual Log_event_type get_type_code() { return TABLE_MAP_EVENT; } virtual bool is_valid() const { return m_memory != NULL; /* we check malloc */ } @@ -3365,6 +3389,12 @@ public: #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; + void print_verbose(IO_CACHE *file, + PRINT_EVENT_INFO *print_event_info); + 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 diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index f6ba5fc9739..c9a806ef74a 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1078,6 +1078,7 @@ void table_cache_free(void); bool table_def_init(void); void table_def_free(void); void assign_new_table_id(TABLE_SHARE *share); +void reset_table_id_sequence(); uint cached_open_tables(void); uint cached_table_definitions(void); void kill_mysql(void); diff --git a/sql/rpl_tblmap.cc b/sql/rpl_tblmap.cc index 6c8b494dfa9..a004c354263 100644 --- a/sql/rpl_tblmap.cc +++ b/sql/rpl_tblmap.cc @@ -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 @@ -42,11 +46,14 @@ table_mapping::table_mapping() table_mapping::~table_mapping() { +#ifdef MYSQL_CLIENT + clear_tables(); +#endif hash_free(&m_table_ids); 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)); @@ -104,8 +111,12 @@ int table_mapping::set_table(ulong table_id, TABLE* table) m_free= m_free->next; } else + { +#ifdef MYSQL_CLIENT + free_table_map_log_event(e->table); +#endif hash_delete(&m_table_ids,(uchar *)e); - + } e->table_id= table_id; e->table= table; my_hash_insert(&m_table_ids,(uchar *)e); @@ -140,6 +151,9 @@ void table_mapping::clear_tables() for (uint i= 0; i < m_table_ids.records; i++) { entry *e= (entry *)hash_element(&m_table_ids, i); +#ifdef MYSQL_CLIENT + free_table_map_log_event(e->table); +#endif e->next= m_free; m_free= e; } diff --git a/sql/rpl_tblmap.h b/sql/rpl_tblmap.h index 446833d5ed6..3b5b10be580 100644 --- a/sql/rpl_tblmap.h +++ b/sql/rpl_tblmap.h @@ -17,8 +17,15 @@ #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; +void free_table_map_log_event(TABLE *table); +#endif + /* CLASS table_mapping diff --git a/sql/rpl_utility.h b/sql/rpl_utility.h index 375715c7858..8e2f4a7374f 100644 --- a/sql/rpl_utility.h +++ b/sql/rpl_utility.h @@ -236,7 +236,9 @@ public: @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 @@ private: 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 @@ namespace { }; } +#endif #define DBUG_PRINT_BITSET(N,FRM,BS) \ do { \ diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 39dd815e738..6d394e04b4a 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -3693,9 +3693,10 @@ void abort_locked_tables(THD *thd,const char *db, const char *table_name) share->table_map_id is not ~0UL. */ +static ulong last_table_id= ~0UL; + void assign_new_table_id(TABLE_SHARE *share) { - static ulong last_table_id= ~0UL; DBUG_ENTER("assign_new_table_id"); @@ -3719,6 +3720,12 @@ void assign_new_table_id(TABLE_SHARE *share) DBUG_VOID_RETURN; } +void reset_table_id_sequence() +{ + pthread_mutex_lock(&LOCK_open); + last_table_id= ~0UL; + pthread_mutex_unlock(&LOCK_open); +} /** Compare metadata versions of an element obtained from the table diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 932b7a67b4d..44f4e8ffee8 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -1285,13 +1285,16 @@ bool change_master(THD* thd, Master_info* mi) int reset_master(THD* thd) { + int rc; if (!mysql_bin_log.is_open()) { my_message(ER_FLUSH_MASTER_BINLOG_CLOSED, ER(ER_FLUSH_MASTER_BINLOG_CLOSED), MYF(ME_BELL+ME_WAITTANG)); return 1; } - return mysql_bin_log.reset_logs(thd); + if (!(rc= mysql_bin_log.reset_logs(thd))) + reset_table_id_sequence(); + return rc; } int cmp_master_pos(const char* log_file_name1, ulonglong log_pos1, |