diff options
Diffstat (limited to 'sql')
-rw-r--r-- | sql/item.cc | 19 | ||||
-rw-r--r-- | sql/item_func.cc | 2 | ||||
-rw-r--r-- | sql/item_func.h | 4 | ||||
-rw-r--r-- | sql/log_event.cc | 286 | ||||
-rw-r--r-- | sql/log_event.h | 26 | ||||
-rw-r--r-- | sql/log_event_old.cc | 24 | ||||
-rw-r--r-- | sql/rpl_tblmap.cc | 20 | ||||
-rw-r--r-- | sql/rpl_tblmap.h | 10 | ||||
-rw-r--r-- | sql/sql_binlog.cc | 91 | ||||
-rw-r--r-- | sql/sql_class.cc | 23 | ||||
-rw-r--r-- | sql/sql_lex.h | 4 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 11 | ||||
-rw-r--r-- | sql/sys_vars.cc | 13 | ||||
-rw-r--r-- | sql/table.h | 2 | ||||
-rw-r--r-- | sql/wsrep_mysqld.cc | 1 | ||||
-rw-r--r-- | sql/wsrep_mysqld.h | 1 | ||||
-rw-r--r-- | sql/wsrep_mysqld_c.h | 26 | ||||
-rw-r--r-- | sql/wsrep_utils.h | 3 |
18 files changed, 466 insertions, 100 deletions
diff --git a/sql/item.cc b/sql/item.cc index 761c9fbec3d..b4958c68be6 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -1793,10 +1793,14 @@ bool Item_name_const::is_null() Item_name_const::Item_name_const(THD *thd, Item *name_arg, Item *val): Item(thd), value_item(val), name_item(name_arg) { + StringBuffer<128> name_buffer; + String *name_str; Item::maybe_null= TRUE; valid_args= true; - if (!name_item->basic_const_item()) + if (!name_item->basic_const_item() || + !(name_str= name_item->val_str(&name_buffer))) // Can't have a NULL name goto err; + set_name(thd, name_str->ptr(), name_str->length(), name_str->charset()); if (value_item->basic_const_item()) return; // ok @@ -1858,27 +1862,16 @@ Item::Type Item_name_const::type() const bool Item_name_const::fix_fields(THD *thd, Item **ref) { - char buf[128]; - String *item_name; - String s(buf, sizeof(buf), &my_charset_bin); - s.length(0); - if ((!value_item->fixed && value_item->fix_fields(thd, &value_item)) || (!name_item->fixed && name_item->fix_fields(thd, &name_item)) || !value_item->const_item() || - !name_item->const_item() || - !(item_name= name_item->val_str(&s))) // Can't have a NULL name + !name_item->const_item()) { my_error(ER_RESERVED_SYNTAX, MYF(0), "NAME_CONST"); return TRUE; } - if (is_autogenerated_name) - { - set_name(thd, item_name->ptr(), (uint) item_name->length(), - system_charset_info); - } if (value_item->collation.derivation == DERIVATION_NUMERIC) collation.set_numeric(); else diff --git a/sql/item_func.cc b/sql/item_func.cc index 89ca25bbfd4..f755deea23b 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -4818,7 +4818,7 @@ bool Item_func_set_user_var::register_field_in_bitmap(void *arg) true failure */ -static bool +bool update_hash(user_var_entry *entry, bool set_null, void *ptr, uint length, Item_result type, CHARSET_INFO *cs, bool unsigned_arg) diff --git a/sql/item_func.h b/sql/item_func.h index 4f1ece45183..fea8c28f74f 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -2531,4 +2531,8 @@ bool eval_const_cond(COND *cond); extern bool volatile mqh_used; +bool update_hash(user_var_entry *entry, bool set_null, void *ptr, uint length, + Item_result type, CHARSET_INFO *cs, + bool unsigned_arg); + #endif /* ITEM_FUNC_INCLUDED */ diff --git a/sql/log_event.cc b/sql/log_event.cc index f6b89cd1a80..947bcbf3509 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -3413,9 +3413,23 @@ void free_table_map_log_event(Table_map_log_event *event) delete event; } +/** + Encode the event, optionally per 'do_print_encoded' arg store the + result into the argument cache; optionally per event_info's + 'verbose' print into the cache a verbose representation of the event. + Note, no extra wrapping is done to the being io-cached data, like + to producing a BINLOG query. It's left for a routine that extracts from + the cache. + + @param file pointer to IO_CACHE + @param print_event_info pointer to print_event_info specializing + what out of and how to print the event + @param do_print_encoded whether to store base64-encoded event + into @file. +*/ void Log_event::print_base64(IO_CACHE* file, PRINT_EVENT_INFO* print_event_info, - bool more) + bool do_print_encoded) { uchar *ptr= (uchar *)temp_buf; uint32 size= uint4korr(ptr + EVENT_LEN_OFFSET); @@ -3479,17 +3493,9 @@ void Log_event::print_base64(IO_CACHE* file, DBUG_ASSERT(0); } - if (print_event_info->base64_output_mode != BASE64_OUTPUT_DECODE_ROWS) - { - if (my_b_tell(file) == 0) - my_b_write_string(file, "\nBINLOG '\n"); - + if (do_print_encoded) my_b_printf(file, "%s\n", tmp_str); - if (!more) - my_b_printf(file, "'%s\n", print_event_info->delimiter); - } - #ifdef WHEN_FLASHBACK_REVIEW_READY if (print_event_info->verbose || need_flashback_review) #else @@ -3582,7 +3588,18 @@ void Log_event::print_base64(IO_CACHE* file, } #else if (print_event_info->verbose) + { + /* + Verbose event printout can't start before encoded data + got enquoted. This is done at this point though multi-row + statement remain vulnerable. + TODO: fix MDEV-10362 to remove this workaround. + */ + if (print_event_info->base64_output_mode != + BASE64_OUTPUT_DECODE_ROWS) + my_b_printf(file, "'%s\n", print_event_info->delimiter); ev->print_verbose(file, print_event_info); + } #endif delete ev; } @@ -5234,6 +5251,22 @@ int Query_log_event::do_apply_event(rpl_group_info *rgi, else thd->variables.collation_database= thd->db_charset; + { + const CHARSET_INFO *cs= thd->charset(); + /* + We cannot ask for parsing a statement using a character set + without state_maps (parser internal data). + */ + if (!cs->state_map) + { + rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, + ER_THD(thd, ER_SLAVE_FATAL_ERROR), + "character_set cannot be parsed"); + thd->is_slave_error= true; + goto end; + } + } + /* Record any GTID in the same transaction, so slave state is transactionally consistent. @@ -5653,9 +5686,17 @@ void Start_log_event_v3::print(FILE* file, PRINT_EVENT_INFO* print_event_info) print_event_info->base64_output_mode != BASE64_OUTPUT_NEVER && !print_event_info->short_form) { - if (print_event_info->base64_output_mode != BASE64_OUTPUT_DECODE_ROWS) + /* BINLOG is matched with the delimiter below on the same level */ + bool do_print_encoded= + print_event_info->base64_output_mode != BASE64_OUTPUT_DECODE_ROWS; + if (do_print_encoded) my_b_printf(&cache, "BINLOG '\n"); - print_base64(&cache, print_event_info, FALSE); + + print_base64(&cache, print_event_info, do_print_encoded); + + if (do_print_encoded) + my_b_printf(&cache, "'%s\n", print_event_info->delimiter); + print_event_info->printed_fd_event= TRUE; } DBUG_VOID_RETURN; @@ -8726,12 +8767,6 @@ User_var_log_event(const char* buf, uint event_len, val_len= uint4korr(buf + UV_VAL_IS_NULL + UV_VAL_TYPE_SIZE + UV_CHARSET_NUMBER_SIZE); - if (val + val_len > buf_end) - { - error= true; - goto err; - } - /** We need to check if this is from an old server that did not pack information for flags. @@ -10446,7 +10481,7 @@ Rows_log_event::Rows_log_event(const char *buf, uint event_len, DBUG_VOID_RETURN; } size_t const data_size= event_len - read_size; - DBUG_PRINT("info",("m_table_id: %lu m_flags: %d m_width: %lu data_size: %lu", + DBUG_PRINT("info",("m_table_id: %llu m_flags: %d m_width: %lu data_size: %lu", m_table_id, m_flags, m_width, (ulong) data_size)); m_rows_buf= (uchar*) my_malloc(data_size, MYF(MY_WME)); @@ -10659,12 +10694,12 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi) DBUG_ENTER("Rows_log_event::do_apply_event(Relay_log_info*)"); int error= 0; /* - If m_table_id == ~0UL, then we have a dummy event that does not + If m_table_id == ~0ULL, then we have a dummy event that does not contain any data. In that case, we just remove all tables in the tables_to_lock list, close the thread tables, and return with success. */ - if (m_table_id == ~0UL) + if (m_table_id == ~0ULL) { /* This one is supposed to be set: just an extra check so that @@ -10930,7 +10965,7 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi) table= m_table= rgi->m_table_map.get_table(m_table_id); - DBUG_PRINT("debug", ("m_table:%p, m_table_id: %lu%s", + DBUG_PRINT("debug", ("m_table:%p, m_table_id: %llu%s", m_table, m_table_id, table && master_had_triggers ? " (master had triggers)" : "")); @@ -11288,14 +11323,14 @@ Rows_log_event::do_update_pos(rpl_group_info *rgi) bool Rows_log_event::write_data_header() { uchar buf[ROWS_HEADER_LEN_V2]; // No need to init the buffer - DBUG_ASSERT(m_table_id != ~0UL); + DBUG_ASSERT(m_table_id != ~0ULL); DBUG_EXECUTE_IF("old_row_based_repl_4_byte_map_id_master", { int4store(buf + 0, m_table_id); int2store(buf + 4, m_flags); return (write_data(buf, 6)); }); - int6store(buf + RW_MAPID_OFFSET, (ulonglong)m_table_id); + int6store(buf + RW_MAPID_OFFSET, m_table_id); int2store(buf + RW_FLAGS_OFFSET, m_flags); return write_data(buf, ROWS_HEADER_LEN); } @@ -11363,12 +11398,159 @@ void Rows_log_event::pack_info(Protocol *protocol) char const *const flagstr= get_flags(STMT_END_F) ? " flags: STMT_END_F" : ""; size_t bytes= my_snprintf(buf, sizeof(buf), - "table_id: %lu%s", m_table_id, flagstr); + "table_id: %llu%s", m_table_id, flagstr); protocol->store(buf, bytes, &my_charset_bin); } #endif #ifdef MYSQL_CLIENT +class my_String : public String +{ +public: + my_String() : String(), error(false) {}; + bool error; + + bool append(const LEX_STRING *ls) + { + return error= error || String::append(ls); + } + bool append(IO_CACHE* file, uint32 arg_length) + { + return error= error || String::append(file, arg_length); + } +}; + +/** + Print an event "body" cache to @c file possibly in two fragments. + Each fragement is optionally per @c do_wrap to produce an SQL statement. + + @param file a file to print to + @param body the "body" IO_CACHE of event + @param do_wrap whether to wrap base64-encoded strings with + SQL cover. + @param delimiter delimiter string + + The function signals on any error through setting @c body->error to -1. +*/ +void copy_cache_to_string_wrapped(IO_CACHE *cache, + LEX_STRING *to, + bool do_wrap, + const char *delimiter, + bool is_verbose) +{ + const char str_binlog[]= "\nBINLOG '\n"; + const char fmt_delim[]= "'%s\n"; + const char fmt_n_delim[]= "\n'%s"; + const char fmt_frag[]= "\nSET @binlog_fragment_%d ='\n"; + const my_off_t cache_size= my_b_tell(cache); + my_String ret; + /* + substring to hold parts of encoded possibly defragramented event + whose size is roughly estimated from the top. + */ + char tmp[sizeof(str_binlog) + 2*(sizeof(fmt_frag) + 2 /* %d */) + + sizeof(fmt_delim) + sizeof(fmt_n_delim) + + PRINT_EVENT_INFO::max_delimiter_size]; + LEX_STRING str_tmp= { tmp, 0 }; + + if (reinit_io_cache(cache, READ_CACHE, 0L, FALSE, FALSE)) + { + cache->error= -1; + goto end; + } + + if (!do_wrap) + { + if (ret.append(cache, (uint32) cache->end_of_file)) + { + cache->error= -1; + goto end; + } + } + else if (4 + sizeof(str_binlog) + cache_size + sizeof(fmt_delim) > + opt_binlog_rows_event_max_encoded_size) + { + /* + 2 fragments can always represent near 1GB row-based + base64-encoded event as two strings each of size less than + max(max_allowed_packet). Greater number of fragments does not + save from potential need to tweak (increase) @@max_allowed_packet + before to process the fragments. So 2 is safe and enough. + + Split the big query when its packet size's estimation exceeds a + limit. The estimate includes the maximum packet header + contribution of non-compressed packet. + */ + str_tmp.length= sprintf(str_tmp.str, fmt_frag, 0); + ret.append(&str_tmp); + ret.append(cache, (uint32) cache_size/2 + 1); + str_tmp.length= sprintf(str_tmp.str, fmt_n_delim, delimiter); + ret.append(&str_tmp); + + str_tmp.length= sprintf(str_tmp.str, fmt_frag, 1); + ret.append(&str_tmp); + ret.append(cache, uint32(cache->end_of_file - (cache_size/2 + 1))); + if (!is_verbose) + { + str_tmp.length= sprintf(str_tmp.str, fmt_delim, delimiter); + ret.append(&str_tmp); + } + str_tmp.length= sprintf(str_tmp.str, "BINLOG @binlog_fragment_0, @binlog_fragment_1%s\n", + delimiter); + ret.append(&str_tmp); + } + else + { + str_tmp.length= sprintf(str_tmp.str, str_binlog); + ret.append(&str_tmp); + ret.append(cache, (uint32) cache->end_of_file); + if (!is_verbose) + { + str_tmp.length= sprintf(str_tmp.str, fmt_delim, delimiter); + ret.append(&str_tmp); + } + } + + to->length= ret.length(); + to->str= ret.release(); + + reinit_io_cache(cache, WRITE_CACHE, 0, FALSE, TRUE); + + if (ret.error) + cache->error= -1; +end: + return; +} + +/** + The function invokes base64 encoder to run on the current + event string and store the result into two caches. + When the event ends the current statement the caches are is copied into + the argument file. + Copying is also concerned how to wrap the event, specifically to produce + a valid SQL syntax. + When the encoded data size is within max(MAX_ALLOWED_PACKET) + a regular BINLOG query is composed. Otherwise it is build as fragmented + + SET @binlog_fragment_0='...'; + SET @binlog_fragment_1='...'; + BINLOG @binlog_fragment_0, @binlog_fragment_1; + + where fragments are represented by a pair of indexed user + "one shot" variables. + + @note + If any changes made don't forget to duplicate them to + Old_rows_log_event as long as it's supported. + + @param file pointer to IO_CACHE + @param print_event_info pointer to print_event_info specializing + what out of and how to print the event + @param name the name of a table that the event operates on + + The function signals on any error of cache access through setting + that cache's @c error to -1. +*/ void Rows_log_event::print_helper(FILE *file, PRINT_EVENT_INFO *print_event_info, char const *const name) @@ -11378,32 +11560,44 @@ void Rows_log_event::print_helper(FILE *file, #ifdef WHEN_FLASHBACK_REVIEW_READY IO_CACHE *const sql= &print_event_info->review_sql_cache; #endif + bool do_print_encoded= + print_event_info->base64_output_mode != BASE64_OUTPUT_DECODE_ROWS && + !print_event_info->short_form; if (!print_event_info->short_form) { + bool const last_stmt_event= get_flags(STMT_END_F); + char llbuff[22]; + print_header(head, print_event_info, !last_stmt_event); - my_b_printf(head, "\t%s: table id %lu%s\n", - name, m_table_id, + my_b_printf(head, "\t%s: table id %s%s\n", + name, ullstr(m_table_id, llbuff), last_stmt_event ? " flags: STMT_END_F" : ""); - print_base64(body, print_event_info, !last_stmt_event); + print_base64(body, print_event_info, do_print_encoded); } if (get_flags(STMT_END_F)) { LEX_STRING tmp_str; - - copy_event_cache_to_string_and_reinit(head, &tmp_str); +#ifdef WHEN_FLASHBACK_REVIEW_READY + copy_event_cache_to_string_and_reinit(sql, &tmp_str); output_buf.append(&tmp_str); my_free(tmp_str.str); - copy_event_cache_to_string_and_reinit(body, &tmp_str); +#endif + if (copy_event_cache_to_string_and_reinit(head, &tmp_str)) + { + head->error= -1; + return; + } output_buf.append(&tmp_str); my_free(tmp_str.str); -#ifdef WHEN_FLASHBACK_REVIEW_READY - copy_event_cache_to_string_and_reinit(sql, &tmp_str); + + copy_cache_to_string_wrapped(body, &tmp_str, do_print_encoded, + print_event_info->delimiter, + print_event_info->verbose); output_buf.append(&tmp_str); my_free(tmp_str.str); -#endif } } #endif @@ -11651,7 +11845,7 @@ Table_map_log_event::Table_map_log_event(THD *thd, TABLE *tbl, ulong tid, uchar cbuf[MAX_INT_WIDTH]; uchar *cbuf_end; DBUG_ENTER("Table_map_log_event::Table_map_log_event(TABLE)"); - DBUG_ASSERT(m_table_id != ~0UL); + DBUG_ASSERT(m_table_id != ~0ULL); /* In TABLE_SHARE, "db" and "table_name" are 0-terminated (see this comment in table.cc / alloc_table_share(): @@ -11736,7 +11930,7 @@ Table_map_log_event::Table_map_log_event(const char *buf, uint event_len, #endif m_dbnam(NULL), m_dblen(0), m_tblnam(NULL), m_tbllen(0), m_colcnt(0), m_coltype(0), - m_memory(NULL), m_table_id(ULONG_MAX), m_flags(0), + m_memory(NULL), m_table_id(ULONGLONG_MAX), m_flags(0), m_data_size(0), m_field_metadata(0), m_field_metadata_size(0), m_null_bits(0), m_meta_memory(NULL) { @@ -11773,7 +11967,7 @@ Table_map_log_event::Table_map_log_event(const char *buf, uint event_len, post_start+= TM_FLAGS_OFFSET; } - DBUG_ASSERT(m_table_id != ~0UL); + DBUG_ASSERT(m_table_id != ~0ULL); m_flags= uint2korr(post_start); @@ -12093,7 +12287,7 @@ int Table_map_log_event::do_apply_event(rpl_group_info *rgi) table_list->updating= 1; table_list->required_type= FRMTYPE_TABLE; - DBUG_PRINT("debug", ("table: %s is mapped to %lu", table_list->table_name, + DBUG_PRINT("debug", ("table: %s is mapped to %llu", table_list->table_name, table_list->table_id)); table_list->master_had_triggers= ((m_flags & TM_BIT_HAS_TRIGGERS_F) ? 1 : 0); DBUG_PRINT("debug", ("table->master_had_triggers=%d", @@ -12194,7 +12388,7 @@ int Table_map_log_event::do_update_pos(rpl_group_info *rgi) #ifndef MYSQL_CLIENT bool Table_map_log_event::write_data_header() { - DBUG_ASSERT(m_table_id != ~0UL); + DBUG_ASSERT(m_table_id != ~0ULL); uchar buf[TABLE_MAP_HEADER_LEN]; DBUG_EXECUTE_IF("old_row_based_repl_4_byte_map_id_master", { @@ -12202,7 +12396,7 @@ bool Table_map_log_event::write_data_header() int2store(buf + 4, m_flags); return (write_data(buf, 6)); }); - int6store(buf + TM_MAPID_OFFSET, (ulonglong)m_table_id); + int6store(buf + TM_MAPID_OFFSET, m_table_id); int2store(buf + TM_FLAGS_OFFSET, m_flags); return write_data(buf, TABLE_MAP_HEADER_LEN); } @@ -12252,7 +12446,7 @@ void Table_map_log_event::pack_info(Protocol *protocol) { char buf[256]; size_t bytes= my_snprintf(buf, sizeof(buf), - "table_id: %lu (%s.%s)", + "table_id: %llu (%s.%s)", m_table_id, m_dbnam, m_tblnam); protocol->store(buf, bytes, &my_charset_bin); } @@ -12267,13 +12461,17 @@ void Table_map_log_event::print(FILE *file, PRINT_EVENT_INFO *print_event_info) { if (!print_event_info->short_form) { + char llbuff[22]; + print_header(&print_event_info->head_cache, print_event_info, TRUE); my_b_printf(&print_event_info->head_cache, - "\tTable_map: %`s.%`s mapped to number %lu%s\n", - m_dbnam, m_tblnam, m_table_id, + "\tTable_map: %`s.%`s mapped to number %s%s\n", + m_dbnam, m_tblnam, ullstr(m_table_id, llbuff), ((m_flags & TM_BIT_HAS_TRIGGERS_F) ? " (has triggers)" : "")); - print_base64(&print_event_info->body_cache, print_event_info, TRUE); + print_base64(&print_event_info->body_cache, print_event_info, + print_event_info->base64_output_mode != + BASE64_OUTPUT_DECODE_ROWS); copy_event_cache_to_file_and_reinit(&print_event_info->head_cache, file); } } diff --git a/sql/log_event.h b/sql/log_event.h index 34000a5f9de..4ecb3d49e63 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -836,7 +836,7 @@ typedef struct st_print_event_info bool domain_id_printed; bool allow_parallel; bool allow_parallel_printed; - + static const uint max_delimiter_size= 16; /* Track when @@skip_replication changes so we need to output a SET statement for it. @@ -872,7 +872,7 @@ typedef struct st_print_event_info bool printed_fd_event; my_off_t hexdump_from; uint8 common_header_len; - char delimiter[16]; + char delimiter[max_delimiter_size]; uint verbose; table_mapping m_table_map; @@ -1235,7 +1235,7 @@ public: void print_header(IO_CACHE* file, PRINT_EVENT_INFO* print_event_info, bool is_more); void print_base64(IO_CACHE* file, PRINT_EVENT_INFO* print_event_info, - bool is_more); + bool do_print_encoded); #endif /* The following code used for Flashback */ @@ -4267,7 +4267,7 @@ public: int rewrite_db(const char* new_name, size_t new_name_len, const Format_description_log_event*); #endif - ulong get_table_id() const { return m_table_id; } + ulonglong 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; } @@ -4310,7 +4310,7 @@ private: uchar *m_coltype; uchar *m_memory; - ulong m_table_id; + ulonglong m_table_id; flag_set m_flags; size_t m_data_size; @@ -4432,7 +4432,7 @@ public: MY_BITMAP const *get_cols() const { return &m_cols; } MY_BITMAP const *get_cols_ai() const { return &m_cols_ai; } size_t get_width() const { return m_width; } - ulong get_table_id() const { return m_table_id; } + ulonglong get_table_id() const { return m_table_id; } #if defined(MYSQL_SERVER) /* @@ -4533,7 +4533,7 @@ protected: #ifdef MYSQL_SERVER TABLE *m_table; /* The table the rows belong to */ #endif - ulong m_table_id; /* Table ID */ + ulonglong m_table_id; /* Table ID */ MY_BITMAP m_cols; /* Bitmap denoting columns available */ ulong m_width; /* The width of the columns bitmap */ /* @@ -5091,6 +5091,13 @@ public: virtual int get_data_size() { return IGNORABLE_HEADER_LEN; } }; +#ifdef MYSQL_CLIENT +void copy_cache_to_string_wrapped(IO_CACHE *body, + LEX_STRING *to, + bool do_wrap, + const char *delimiter, + bool is_verbose); +#endif static inline bool copy_event_cache_to_string_and_reinit(IO_CACHE *cache, LEX_STRING *to) { @@ -5118,11 +5125,12 @@ err: static inline bool copy_event_cache_to_file_and_reinit(IO_CACHE *cache, FILE *file) { - return - my_b_copy_to_file(cache, file) || + return + my_b_copy_all_to_file(cache, file) || reinit_io_cache(cache, WRITE_CACHE, 0, FALSE, TRUE); } + #ifdef MYSQL_SERVER /***************************************************************************** diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc index ce9bca920fe..2b6509048ba 100644 --- a/sql/log_event_old.cc +++ b/sql/log_event_old.cc @@ -1853,12 +1853,17 @@ void Old_rows_log_event::pack_info(Protocol *protocol) #ifdef MYSQL_CLIENT +/* Method duplicates Rows_log_event's one */ void Old_rows_log_event::print_helper(FILE *file, PRINT_EVENT_INFO *print_event_info, char const *const name) { IO_CACHE *const head= &print_event_info->head_cache; IO_CACHE *const body= &print_event_info->body_cache; + bool do_print_encoded= + print_event_info->base64_output_mode != BASE64_OUTPUT_DECODE_ROWS && + !print_event_info->short_form; + if (!print_event_info->short_form) { bool const last_stmt_event= get_flags(STMT_END_F); @@ -1866,13 +1871,26 @@ void Old_rows_log_event::print_helper(FILE *file, my_b_printf(head, "\t%s: table id %lu%s\n", name, m_table_id, last_stmt_event ? " flags: STMT_END_F" : ""); - print_base64(body, print_event_info, !last_stmt_event); + print_base64(body, print_event_info, do_print_encoded); } if (get_flags(STMT_END_F)) { - copy_event_cache_to_file_and_reinit(head, file); - copy_event_cache_to_file_and_reinit(body, file); + LEX_STRING tmp_str; + + if (copy_event_cache_to_string_and_reinit(head, &tmp_str)) + { + head->error= -1; + return; + } + output_buf.append(&tmp_str); + my_free(tmp_str.str); + + copy_cache_to_string_wrapped(body, &tmp_str, do_print_encoded, + print_event_info->delimiter, + print_event_info->verbose); + output_buf.append(&tmp_str); + my_free(tmp_str.str); } } #endif diff --git a/sql/rpl_tblmap.cc b/sql/rpl_tblmap.cc index 48111bc5d0a..15bb8a054eb 100644 --- a/sql/rpl_tblmap.cc +++ b/sql/rpl_tblmap.cc @@ -43,7 +43,7 @@ table_mapping::table_mapping() constructor is called at startup only. */ (void) my_hash_init(&m_table_ids,&my_charset_bin,TABLE_ID_HASH_SIZE, - offsetof(entry,table_id),sizeof(ulong), + offsetof(entry,table_id),sizeof(ulonglong), 0,0,0); /* We don't preallocate any block, this is consistent with m_free=0 above */ init_alloc_root(&m_mem_root, TABLE_ID_HASH_SIZE*sizeof(entry), 0, MYF(0)); @@ -59,20 +59,20 @@ table_mapping::~table_mapping() free_root(&m_mem_root, MYF(0)); } -TABLE* table_mapping::get_table(ulong table_id) +TABLE* table_mapping::get_table(ulonglong table_id) { DBUG_ENTER("table_mapping::get_table(ulong)"); - DBUG_PRINT("enter", ("table_id: %lu", table_id)); + DBUG_PRINT("enter", ("table_id: %llu", table_id)); entry *e= find_entry(table_id); if (e) { - DBUG_PRINT("info", ("tid %lu -> table %p (%s)", + DBUG_PRINT("info", ("tid %llu -> table %p (%s)", table_id, e->table, MAYBE_TABLE_NAME(e->table))); DBUG_RETURN(e->table); } - DBUG_PRINT("info", ("tid %lu is not mapped!", table_id)); + DBUG_PRINT("info", ("tid %llu is not mapped!", table_id)); DBUG_RETURN(NULL); } @@ -102,11 +102,11 @@ int table_mapping::expand() return 0; } -int table_mapping::set_table(ulong table_id, TABLE* table) +int table_mapping::set_table(ulonglong table_id, TABLE* table) { DBUG_ENTER("table_mapping::set_table(ulong,TABLE*)"); - DBUG_PRINT("enter", ("table_id: %lu table: %p (%s)", - table_id, + DBUG_PRINT("enter", ("table_id: %llu table: %p (%s)", + table_id, table, MAYBE_TABLE_NAME(table))); entry *e= find_entry(table_id); if (e == 0) @@ -133,13 +133,13 @@ int table_mapping::set_table(ulong table_id, TABLE* table) DBUG_RETURN(ERR_MEMORY_ALLOCATION); } - DBUG_PRINT("info", ("tid %lu -> table %p (%s)", + DBUG_PRINT("info", ("tid %llu -> table %p (%s)", table_id, e->table, MAYBE_TABLE_NAME(e->table))); DBUG_RETURN(0); // All OK } -int table_mapping::remove_table(ulong table_id) +int table_mapping::remove_table(ulonglong table_id) { entry *e= find_entry(table_id); if (e) diff --git a/sql/rpl_tblmap.h b/sql/rpl_tblmap.h index 9fb1c4afbd7..05b298e6053 100644 --- a/sql/rpl_tblmap.h +++ b/sql/rpl_tblmap.h @@ -70,10 +70,10 @@ public: table_mapping(); ~table_mapping(); - TABLE* get_table(ulong table_id); + TABLE* get_table(ulonglong table_id); - int set_table(ulong table_id, TABLE* table); - int remove_table(ulong table_id); + int set_table(ulonglong table_id, TABLE* table); + int remove_table(ulonglong table_id); void clear_tables(); ulong count() const { return m_table_ids.records; } @@ -83,14 +83,14 @@ private: it, which only works for PODs) */ struct entry { - ulong table_id; + ulonglong table_id; union { TABLE *table; entry *next; }; }; - entry *find_entry(ulong table_id) + entry *find_entry(ulonglong table_id) { return (entry *) my_hash_search(&m_table_ids, (uchar*)&table_id, diff --git a/sql/sql_binlog.cc b/sql/sql_binlog.cc index f58bcf2e8fe..5cd2a341353 100644 --- a/sql/sql_binlog.cc +++ b/sql/sql_binlog.cc @@ -110,6 +110,64 @@ static int check_event_type(int type, Relay_log_info *rli) } /** + Copy fragments into the standard placeholder thd->lex->comment.str. + + Compute the size of the (still) encoded total, + allocate and then copy fragments one after another. + The size can exceed max(max_allowed_packet) which is not a + problem as no String instance is created off this char array. + + @param thd THD handle + @return + 0 at success, + -1 otherwise. +*/ +int binlog_defragment(THD *thd) +{ + user_var_entry *entry[2]; + LEX_STRING name[2]= { thd->lex->comment, thd->lex->ident }; + + /* compute the total size */ + thd->lex->comment.str= NULL; + thd->lex->comment.length= 0; + for (uint k= 0; k < 2; k++) + { + entry[k]= + (user_var_entry*) my_hash_search(&thd->user_vars, (uchar*) name[k].str, + name[k].length); + if (!entry[k] || entry[k]->type != STRING_RESULT) + { + my_error(ER_WRONG_TYPE_FOR_VAR, MYF(0), name[k].str); + return -1; + } + thd->lex->comment.length += entry[k]->length; + } + + thd->lex->comment.str= // to be freed by the caller + (char *) my_malloc(thd->lex->comment.length, MYF(MY_WME)); + if (!thd->lex->comment.str) + { + my_error(ER_OUTOFMEMORY, MYF(ME_FATALERROR), 1); + return -1; + } + + /* fragments are merged into allocated buf while the user var:s get reset */ + size_t gathered_length= 0; + for (uint k=0; k < 2; k++) + { + memcpy(thd->lex->comment.str + gathered_length, entry[k]->value, + entry[k]->length); + gathered_length += entry[k]->length; + update_hash(entry[k], true, NULL, 0, STRING_RESULT, &my_charset_bin, 0); + } + + DBUG_ASSERT(gathered_length == thd->lex->comment.length); + + return 0; +} + + +/** Execute a BINLOG statement. To execute the BINLOG command properly the server needs to know @@ -134,14 +192,6 @@ void mysql_client_binlog_statement(THD* thd) if (check_global_access(thd, SUPER_ACL)) DBUG_VOID_RETURN; - size_t coded_len= thd->lex->comment.length; - if (!coded_len) - { - my_error(ER_SYNTAX_ERROR, MYF(0)); - DBUG_VOID_RETURN; - } - size_t decoded_len= my_base64_needed_decoded_length(coded_len); - /* option_bits will be changed when applying the event. But we don't expect it be changed permanently after BINLOG statement, so backup it first. @@ -156,6 +206,8 @@ void mysql_client_binlog_statement(THD* thd) int err; Relay_log_info *rli; rpl_group_info *rgi; + char *buf= NULL; + size_t coded_len= 0, decoded_len= 0; rli= thd->rli_fake; if (!rli && (rli= thd->rli_fake= new Relay_log_info(FALSE))) @@ -165,13 +217,13 @@ void mysql_client_binlog_statement(THD* thd) rgi->thd= thd; const char *error= 0; - char *buf= (char *) my_malloc(decoded_len, MYF(MY_WME)); Log_event *ev = 0; + my_bool is_fragmented= FALSE; /* Out of memory check */ - if (!(rli && buf)) + if (!(rli)) { my_error(ER_OUTOFMEMORY, MYF(ME_FATALERROR), 1); /* needed 1 bytes */ goto end; @@ -179,6 +231,23 @@ void mysql_client_binlog_statement(THD* thd) DBUG_ASSERT(rli->belongs_to_client()); + if (unlikely(is_fragmented= thd->lex->comment.str && thd->lex->ident.str)) + if (binlog_defragment(thd)) + goto end; + + if (!(coded_len= thd->lex->comment.length)) + { + my_error(ER_SYNTAX_ERROR, MYF(0)); + goto end; + } + + decoded_len= my_base64_needed_decoded_length(coded_len); + if (!(buf= (char *) my_malloc(decoded_len, MYF(MY_WME)))) + { + my_error(ER_OUTOFMEMORY, MYF(ME_FATALERROR), 1); + goto end; + } + for (char const *strptr= thd->lex->comment.str ; strptr < thd->lex->comment.str + thd->lex->comment.length ; ) { @@ -317,6 +386,8 @@ void mysql_client_binlog_statement(THD* thd) my_ok(thd); end: + if (unlikely(is_fragmented)) + my_free(thd->lex->comment.str); thd->variables.option_bits= thd_options; rgi->slave_close_thread_tables(thd); my_free(buf); diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 7b5190757e2..1146359f451 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -6367,6 +6367,22 @@ int THD::binlog_update_row(TABLE* table, bool is_trans, DBUG_ASSERT(is_current_stmt_binlog_format_row() && ((WSREP(this) && wsrep_emulate_bin_log) || mysql_bin_log.is_open())); + /** + Save a reference to the original read bitmaps + We will need this to restore the bitmaps at the end as + binlog_prepare_row_images() may change table->read_set. + table->read_set is used by pack_row and deep in + binlog_prepare_pending_events(). + */ + MY_BITMAP *old_read_set= table->read_set; + + /** + This will remove spurious fields required during execution but + not needed for binlogging. This is done according to the: + binlog-row-image option. + */ + binlog_prepare_row_images(table); + size_t const before_maxlen= max_row_length(table, table->read_set, before_record); size_t const after_maxlen= max_row_length(table, table->rpl_write_set, @@ -6380,9 +6396,9 @@ int THD::binlog_update_row(TABLE* table, bool is_trans, uchar *after_row= row_data.slot(1); size_t const before_size= pack_row(table, table->read_set, before_row, - before_record); + before_record); size_t const after_size= pack_row(table, table->rpl_write_set, after_row, - after_record); + after_record); /* Ensure that all events in a GTID group are in the same cache */ if (variables.option_bits & OPTION_GTID_BEGIN) @@ -6417,6 +6433,9 @@ int THD::binlog_update_row(TABLE* table, bool is_trans, int error= ev->add_row_data(before_row, before_size) || ev->add_row_data(after_row, after_size); + /* restore read set for the rest of execution */ + table->column_bitmaps_set_no_signal(old_read_set, + table->write_set); return error; } diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 76e280175a1..53f2ec15341 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -2587,6 +2587,10 @@ struct LEX: public Query_tables_list String *wild; /* Wildcard in SHOW {something} LIKE 'wild'*/ sql_exchange *exchange; select_result *result; + /** + @c the two may also hold BINLOG arguments: either comment holds a + base64-char string or both represent the BINLOG fragment user variables. + */ LEX_STRING comment, ident; LEX_USER *grant_user; XID *xid; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index b2b9cb40eab..10c2204d193 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -8157,8 +8157,17 @@ binlog_base64_event: { Lex->sql_command = SQLCOM_BINLOG_BASE64_EVENT; Lex->comment= $2; + Lex->ident.str= NULL; + Lex->ident.length= 0; } - ; + | + BINLOG_SYM '@' ident_or_text ',' '@' ident_or_text + { + Lex->sql_command = SQLCOM_BINLOG_BASE64_EVENT; + Lex->comment= $3; + Lex->ident= $6; + } + ; check_view_or_table: table_or_tables table_list opt_mi_check_type diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 1ec035f35ae..9bb7436b2da 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -5069,6 +5069,19 @@ static Sys_var_mybool Sys_wsrep_certify_nonPK( GLOBAL_VAR(wsrep_certify_nonPK), CMD_LINE(OPT_ARG), DEFAULT(TRUE)); +static const char *wsrep_certification_rules_names[]= { "strict", "optimized", NullS }; +static Sys_var_enum Sys_wsrep_certification_rules( + "wsrep_certification_rules", + "Certification rules to use in the cluster. Possible values are: " + "\"strict\": stricter rules that could result in more certification " + "failures. " + "\"optimized\": relaxed rules that allow more concurrency and " + "cause less certification failures.", + GLOBAL_VAR(wsrep_certification_rules), CMD_LINE(REQUIRED_ARG), + wsrep_certification_rules_names, DEFAULT(WSREP_CERTIFICATION_RULES_STRICT), + NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), + ON_UPDATE(0)); + static Sys_var_mybool Sys_wsrep_causal_reads( "wsrep_causal_reads", "Setting this variable is equivalent " "to setting wsrep_sync_wait READ flag", diff --git a/sql/table.h b/sql/table.h index 85b63eda010..13acae533f7 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1892,7 +1892,7 @@ struct TABLE_LIST /* Index names in a "... JOIN ... USE/IGNORE INDEX ..." clause. */ List<Index_hint> *index_hints; TABLE *table; /* opened table */ - ulong table_id; /* table id (from binlog) for opened table */ + ulonglong table_id; /* table id (from binlog) for opened table */ /* select_result for derived table to pass it from table creation to table filling procedure diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc index aa9a5460049..08efa0086a7 100644 --- a/sql/wsrep_mysqld.cc +++ b/sql/wsrep_mysqld.cc @@ -80,6 +80,7 @@ my_bool wsrep_convert_LOCK_to_trx; // Convert locking sessions to t my_bool wsrep_auto_increment_control; // Control auto increment variables my_bool wsrep_drupal_282555_workaround; // Retry autoinc insert after dupkey my_bool wsrep_certify_nonPK; // Certify, even when no primary key +ulong wsrep_certification_rules = WSREP_CERTIFICATION_RULES_STRICT; my_bool wsrep_recovery; // Recovery my_bool wsrep_replicate_myisam; // Enable MyISAM replication my_bool wsrep_log_conflicts; diff --git a/sql/wsrep_mysqld.h b/sql/wsrep_mysqld.h index 5ea5fbf3bcc..0b1e75102e8 100644 --- a/sql/wsrep_mysqld.h +++ b/sql/wsrep_mysqld.h @@ -30,6 +30,7 @@ typedef struct st_mysql_show_var SHOW_VAR; #include "mdl.h" #include "mysqld.h" #include "sql_table.h" +#include "wsrep_mysqld_c.h" #define WSREP_UNDEFINED_TRX_ID ULONGLONG_MAX diff --git a/sql/wsrep_mysqld_c.h b/sql/wsrep_mysqld_c.h new file mode 100644 index 00000000000..15ca0ae2a6d --- /dev/null +++ b/sql/wsrep_mysqld_c.h @@ -0,0 +1,26 @@ +/* Copyright 2018-2018 Codership Oy <http://www.codership.com> + + 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 + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifndef WSREP_MYSQLD_C_H +#define WSREP_MYSQLD_C_H + +enum enum_wsrep_certification_rules { + WSREP_CERTIFICATION_RULES_STRICT, + WSREP_CERTIFICATION_RULES_OPTIMIZED +}; + +extern ulong wsrep_certification_rules; + +#endif /* WSREP_MYSQLD_C_H */ diff --git a/sql/wsrep_utils.h b/sql/wsrep_utils.h index 277cea9dc31..0afca96ea41 100644 --- a/sql/wsrep_utils.h +++ b/sql/wsrep_utils.h @@ -108,7 +108,8 @@ private: /* Hostname with port (host:port) */ start= addr_in; end= colon; - parse_port(colon + 1); + if (parse_port(colon + 1)) + return; /* Error: invalid port */ break; default: /* IPv6 address */ |