diff options
author | Aleksey Midenkov <midenok@gmail.com> | 2018-01-10 12:36:55 +0300 |
---|---|---|
committer | Aleksey Midenkov <midenok@gmail.com> | 2018-01-10 12:36:55 +0300 |
commit | c59c1a0736e36e9c66f798d199a124f362353377 (patch) | |
tree | 80ee18faad049687b0b4f0e2f147caf870d68390 /sql | |
parent | 26971c9aea67a62f348cd105348a8dc4407bcf4a (diff) | |
parent | 0b597d3ab2494bc1db97cc4a30d697a5fdf48c21 (diff) | |
download | mariadb-git-c59c1a0736e36e9c66f798d199a124f362353377.tar.gz |
System Versioning 1.0 pre8
Merge branch '10.3' into trunk
Diffstat (limited to 'sql')
75 files changed, 3317 insertions, 1381 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index c362ba4a8ee..a412a65ccc9 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -146,6 +146,7 @@ SET (SQL_SOURCE item_vers.cc sql_sequence.cc sql_sequence.h ha_sequence.h sql_tvc.cc sql_tvc.h + opt_split.cc ${WSREP_SOURCES} table_cache.cc encryption.cc temporary_tables.cc proxy_protocol.cc diff --git a/sql/events.cc b/sql/events.cc index 36f03e26125..3ad546217a7 100644 --- a/sql/events.cc +++ b/sql/events.cc @@ -421,7 +421,7 @@ Events::create_event(THD *thd, Event_parse_data *parse_data) DBUG_RETURN(ret); #ifdef WITH_WSREP error: - DBUG_RETURN(true); + DBUG_RETURN(TRUE); #endif /* WITH_WSREP */ } diff --git a/sql/field.cc b/sql/field.cc index 8cc9e7d1223..642ad6a65e9 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -1518,7 +1518,7 @@ bool Field_num::get_int(CHARSET_INFO *cs, const char *from, uint len, goto out_of_range; } } - if (get_thd()->count_cuted_fields && + if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION && check_int(cs, from, len, end, error)) return 1; return 0; @@ -1539,7 +1539,7 @@ double Field_real::get_double(const char *str, uint length, CHARSET_INFO *cs, set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); *error= 1; } - else if (get_thd()->count_cuted_fields && + else if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION && check_edom_and_truncation("double", str == end, cs, str, length, end)) *error= 1; @@ -1874,7 +1874,7 @@ void Field::make_field(Send_field *field) if (orig_table && orig_table->s->db.str && *orig_table->s->db.str) { field->db_name= orig_table->s->db.str; - if (orig_table->pos_in_table_list && + if (orig_table->pos_in_table_list && orig_table->pos_in_table_list->schema_table) field->org_table_name= (orig_table->pos_in_table_list-> schema_table->table_name); @@ -2536,7 +2536,7 @@ int Field_decimal::store(const char *from_arg, uint len, CHARSET_INFO *cs) it makes the code easer to read. */ - if (get_thd()->count_cuted_fields) + if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION) { // Skip end spaces for (;from != end && my_isspace(&my_charset_bin, *from); from++) ; @@ -2706,7 +2706,8 @@ int Field_decimal::store(const char *from_arg, uint len, CHARSET_INFO *cs) { if (pos == right_wall) { - if (get_thd()->count_cuted_fields && !is_cuted_fields_incr) + if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION && + !is_cuted_fields_incr) break; // Go on below to see if we lose non zero digits return 0; } @@ -3099,7 +3100,7 @@ int Field_new_decimal::store(const char *from, uint length, DBUG_RETURN(1); } - if (thd->count_cuted_fields) + if (thd->count_cuted_fields > CHECK_FIELD_EXPRESSION) { if (check_edom_and_important_data_truncation("decimal", err && err != E_DEC_TRUNCATED, @@ -3144,7 +3145,7 @@ int Field_new_decimal::store(const char *from, uint length, - in err2: store_value() truncated 1.123 to 1.12, e.g. for DECIMAL(10,2) Also, we send a note if a string had some trailing spaces: '1.12 ' */ - if (thd->count_cuted_fields && + if (thd->count_cuted_fields > CHECK_FIELD_EXPRESSION && (err == E_DEC_TRUNCATED || err2 == E_DEC_TRUNCATED || end < from + length)) @@ -4221,7 +4222,7 @@ int Field_longlong::store(const char *from,uint len,CHARSET_INFO *cs) set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } - else if (get_thd()->count_cuted_fields && + else if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION && check_int(cs, from, len, end, error)) error= 1; else @@ -6162,7 +6163,7 @@ int Field_year::store(const char *from, uint len,CHARSET_INFO *cs) set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1); return 1; } - if (get_thd()->count_cuted_fields && + if (get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION && (error= check_int(cs, from, len, end, error))) { if (error == 1) /* empty or incorrect string */ @@ -6870,7 +6871,7 @@ Field_longstr::report_if_important_data(const char *pstr, const char *end, bool count_spaces) { THD *thd= get_thd(); - if ((pstr < end) && thd->count_cuted_fields) + if ((pstr < end) && thd->count_cuted_fields > CHECK_FIELD_EXPRESSION) { if (test_if_important_data(field_charset, pstr, end)) { @@ -8804,7 +8805,7 @@ int Field_geom::store(const char *from, uint length, CHARSET_INFO *cs) wkb_type > (uint32) Geometry::wkb_last) goto err; - if (geom_type != Field::GEOM_GEOMETRY && + if (geom_type != Field::GEOM_GEOMETRY && geom_type != Field::GEOM_GEOMETRYCOLLECTION && (uint32) geom_type != wkb_type) { @@ -8944,7 +8945,7 @@ int Field_enum::store(const char *from,uint length,CHARSET_INFO *cs) set_warning(WARN_DATA_TRUNCATED, 1); err= 1; } - if (!get_thd()->count_cuted_fields && !length) + if ((get_thd()->count_cuted_fields <= CHECK_FIELD_EXPRESSION) && !length) err= 0; } else @@ -8971,7 +8972,7 @@ int Field_enum::store(longlong nr, bool unsigned_val) if ((ulonglong) nr > typelib->count || nr == 0) { set_warning(WARN_DATA_TRUNCATED, 1); - if (nr != 0 || get_thd()->count_cuted_fields) + if (nr != 0 || get_thd()->count_cuted_fields > CHECK_FIELD_EXPRESSION) { nr= 0; error= 1; @@ -10197,6 +10198,8 @@ bool check_expression(Virtual_column_info *vcol, LEX_CSTRING *name, uint filter= VCOL_IMPOSSIBLE; if (type != VCOL_GENERATED_VIRTUAL && type != VCOL_DEFAULT) filter|= VCOL_NOT_STRICTLY_DETERMINISTIC; + if (type == VCOL_GENERATED_VIRTUAL) + filter|= VCOL_NOT_VIRTUAL; if (ret || (res.errors & filter)) { @@ -11023,11 +11026,12 @@ uint32 Field_blob::max_display_length() @param cut_increment - whenever we should increase cut fields count @note - This function won't produce warning and increase cut fields counter - if count_cuted_fields == CHECK_FIELD_IGNORE for current thread. + This function won't produce warning or notes or increase cut fields counter + if count_cuted_fields == CHECK_FIELD_IGNORE or CHECK_FIELD_EXPRESSION + for the current thread. - if count_cuted_fields == CHECK_FIELD_IGNORE then we ignore notes. - This allows us to avoid notes in optimisation, like convert_constant_item(). + This allows us to avoid notes in optimisation, like + convert_constant_item(). @retval 1 if count_cuted_fields == CHECK_FIELD_IGNORE and error level is not NOTE @@ -11044,7 +11048,7 @@ Field::set_warning(Sql_condition::enum_warning_level level, uint code, will have table == NULL. */ THD *thd= get_thd(); - if (thd->count_cuted_fields) + if (thd->count_cuted_fields > CHECK_FIELD_EXPRESSION) { thd->cuted_fields+= cut_increment; push_warning_printf(thd, level, code, ER_THD(thd, code), field_name.str, diff --git a/sql/field.h b/sql/field.h index 8b841aeeaa9..9fd584e88c2 100644 --- a/sql/field.h +++ b/sql/field.h @@ -50,8 +50,9 @@ class Virtual_tmp_table; enum enum_check_fields { CHECK_FIELD_IGNORE, + CHECK_FIELD_EXPRESSION, CHECK_FIELD_WARN, - CHECK_FIELD_ERROR_FOR_NULL + CHECK_FIELD_ERROR_FOR_NULL, }; /* @@ -558,6 +559,7 @@ static inline const char *vcol_type_name(enum_vcol_info_type type) #define VCOL_TIME_FUNC 8 #define VCOL_AUTO_INC 16 #define VCOL_IMPOSSIBLE 32 +#define VCOL_NOT_VIRTUAL 64 /* Function can't be virtual */ #define VCOL_NOT_STRICTLY_DETERMINISTIC \ (VCOL_NON_DETERMINISTIC | VCOL_TIME_FUNC | VCOL_SESSION_FUNC) diff --git a/sql/field_conv.cc b/sql/field_conv.cc index a817db51569..db5c9429954 100644 --- a/sql/field_conv.cc +++ b/sql/field_conv.cc @@ -122,6 +122,7 @@ static int set_bad_null_error(Field *field, int err) field->set_warning(Sql_condition::WARN_LEVEL_WARN, err, 1); /* fall through */ case CHECK_FIELD_IGNORE: + case CHECK_FIELD_EXPRESSION: return 0; case CHECK_FIELD_ERROR_FOR_NULL: if (!field->table->in_use->no_errors) @@ -528,7 +529,8 @@ static void do_varstring1(Copy_field *copy) if (length > copy->to_length- 1) { length=copy->to_length - 1; - if (copy->from_field->table->in_use->count_cuted_fields && + if (copy->from_field->table->in_use->count_cuted_fields > + CHECK_FIELD_EXPRESSION && copy->to_field) copy->to_field->set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); @@ -547,7 +549,7 @@ static void do_varstring1_mb(Copy_field *copy) Well_formed_prefix prefix(cs, (char*) from_ptr, from_length, to_char_length); if (prefix.length() < from_length) { - if (current_thd->count_cuted_fields) + if (current_thd->count_cuted_fields > CHECK_FIELD_EXPRESSION) copy->to_field->set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); } @@ -562,7 +564,8 @@ static void do_varstring2(Copy_field *copy) if (length > copy->to_length- HA_KEY_BLOB_LENGTH) { length=copy->to_length-HA_KEY_BLOB_LENGTH; - if (copy->from_field->table->in_use->count_cuted_fields && + if (copy->from_field->table->in_use->count_cuted_fields > + CHECK_FIELD_EXPRESSION && copy->to_field) copy->to_field->set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); @@ -582,7 +585,7 @@ static void do_varstring2_mb(Copy_field *copy) Well_formed_prefix prefix(cs, (char*) from_beg, from_length, char_length); if (prefix.length() < from_length) { - if (current_thd->count_cuted_fields) + if (current_thd->count_cuted_fields > CHECK_FIELD_EXPRESSION) copy->to_field->set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); } diff --git a/sql/filesort.cc b/sql/filesort.cc index b762773b11e..4cf2a00dbc4 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -710,6 +710,7 @@ static ha_rows find_all_keys(THD *thd, Sort_param *param, SQL_SELECT *select, handler *file; MY_BITMAP *save_read_set, *save_write_set, *save_vcol_set; Item *sort_cond; + ha_rows retval; DBUG_ENTER("find_all_keys"); DBUG_PRINT("info",("using: %s", (select ? select->quick ? "ranges" : "where": @@ -767,7 +768,7 @@ static ha_rows find_all_keys(THD *thd, Sort_param *param, SQL_SELECT *select, if (quick_select) { if (select->quick->reset()) - DBUG_RETURN(HA_POS_ERROR); + goto err; } DEBUG_SYNC(thd, "after_index_merge_phase1"); @@ -804,7 +805,7 @@ static ha_rows find_all_keys(THD *thd, Sort_param *param, SQL_SELECT *select, (void) file->extra(HA_EXTRA_NO_CACHE); file->ha_rnd_end(); } - DBUG_RETURN(HA_POS_ERROR); /* purecov: inspected */ + goto err; /* purecov: inspected */ } bool write_record= false; @@ -852,7 +853,7 @@ static ha_rows find_all_keys(THD *thd, Sort_param *param, SQL_SELECT *select, if (idx == param->max_keys_per_buffer) { if (write_keys(param, fs_info, idx, buffpek_pointers, tempfile)) - DBUG_RETURN(HA_POS_ERROR); + goto err; idx= 0; indexpos++; } @@ -878,12 +879,12 @@ static ha_rows find_all_keys(THD *thd, Sort_param *param, SQL_SELECT *select, file->ha_rnd_end(); } - if (thd->is_error()) - DBUG_RETURN(HA_POS_ERROR); - /* Signal we should use orignal column read and write maps */ sort_form->column_bitmaps_set(save_read_set, save_write_set, save_vcol_set); + if (thd->is_error()) + DBUG_RETURN(HA_POS_ERROR); + DBUG_PRINT("test",("error: %d indexpos: %d",error,indexpos)); if (error != HA_ERR_END_OF_FILE) { @@ -893,11 +894,15 @@ static ha_rows find_all_keys(THD *thd, Sort_param *param, SQL_SELECT *select, if (indexpos && idx && write_keys(param, fs_info, idx, buffpek_pointers, tempfile)) DBUG_RETURN(HA_POS_ERROR); /* purecov: inspected */ - const ha_rows retval= - my_b_inited(tempfile) ? - (ha_rows) (my_b_tell(tempfile)/param->rec_length) : idx; + retval= (my_b_inited(tempfile) ? + (ha_rows) (my_b_tell(tempfile)/param->rec_length) : + idx); DBUG_PRINT("info", ("find_all_keys return %llu", (ulonglong) retval)); DBUG_RETURN(retval); + +err: + sort_form->column_bitmaps_set(save_read_set, save_write_set, save_vcol_set); + DBUG_RETURN(HA_POS_ERROR); } /* find_all_keys */ @@ -995,7 +1000,8 @@ Type_handler_string_result::make_sort_key(uchar *to, Item *item, if (maybe_null) *to++= 1; char *tmp_buffer= param->tmp_buffer ? param->tmp_buffer : (char*) to; - String tmp(tmp_buffer, param->sort_length, cs); + String tmp(tmp_buffer, param->tmp_buffer ? param->sort_length : + sort_field->length, cs); String *res= item->str_result(&tmp); if (!res) { diff --git a/sql/filesort_utils.cc b/sql/filesort_utils.cc index 11be9229c1d..b39bb880c15 100644 --- a/sql/filesort_utils.cc +++ b/sql/filesort_utils.cc @@ -105,7 +105,7 @@ uchar **Filesort_buffer::alloc_sort_buffer(uint num_records, DBUG_EXECUTE_IF("alloc_sort_buffer_fail", DBUG_SET("+d,simulate_out_of_memory");); - buff_size= num_records * (record_length + sizeof(uchar*)); + buff_size= ((size_t)num_records) * (record_length + sizeof(uchar*)); set_if_bigger(buff_size, record_length * MERGEBUFF2); if (!m_idx_array.is_null()) diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index da274bbdf67..20fa2eabea1 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -2201,38 +2201,19 @@ void ha_partition::update_create_info(HA_CREATE_INFO *create_info) DBUG_ASSERT(sub_elem); part= i * num_subparts + j; DBUG_ASSERT(part < m_file_tot_parts && m_file[part]); - if (ha_legacy_type(m_file[part]->ht) == DB_TYPE_INNODB) - { - dummy_info.data_file_name= dummy_info.index_file_name= NULL; - m_file[part]->update_create_info(&dummy_info); - - if (dummy_info.data_file_name || sub_elem->data_file_name) - { - sub_elem->data_file_name= (char*) dummy_info.data_file_name; - } - if (dummy_info.index_file_name || sub_elem->index_file_name) - { - sub_elem->index_file_name= (char*) dummy_info.index_file_name; - } - } + dummy_info.data_file_name= dummy_info.index_file_name = NULL; + m_file[part]->update_create_info(&dummy_info); + sub_elem->data_file_name = (char*) dummy_info.data_file_name; + sub_elem->index_file_name = (char*) dummy_info.index_file_name; } } else { DBUG_ASSERT(m_file[i]); - if (ha_legacy_type(m_file[i]->ht) == DB_TYPE_INNODB) - { - dummy_info.data_file_name= dummy_info.index_file_name= NULL; - m_file[i]->update_create_info(&dummy_info); - if (dummy_info.data_file_name || part_elem->data_file_name) - { - part_elem->data_file_name= (char*) dummy_info.data_file_name; - } - if (dummy_info.index_file_name || part_elem->index_file_name) - { - part_elem->index_file_name= (char*) dummy_info.index_file_name; - } - } + dummy_info.data_file_name= dummy_info.index_file_name= NULL; + m_file[i]->update_create_info(&dummy_info); + part_elem->data_file_name = (char*) dummy_info.data_file_name; + part_elem->index_file_name = (char*) dummy_info.index_file_name; } } DBUG_VOID_RETURN; @@ -9703,20 +9684,36 @@ uint ha_partition::alter_table_flags(uint flags) bool ha_partition::check_if_incompatible_data(HA_CREATE_INFO *create_info, uint table_changes) { - handler **file; - bool ret= COMPATIBLE_DATA_YES; - /* The check for any partitioning related changes have already been done in mysql_alter_table (by fix_partition_func), so it is only up to the underlying handlers. */ - for (file= m_file; *file; file++) - if ((ret= (*file)->check_if_incompatible_data(create_info, - table_changes)) != - COMPATIBLE_DATA_YES) - break; - return ret; + List_iterator<partition_element> part_it(m_part_info->partitions); + HA_CREATE_INFO dummy_info= *create_info; + uint i=0; + while (partition_element *part_elem= part_it++) + { + if (m_is_sub_partitioned) + { + List_iterator<partition_element> subpart_it(part_elem->subpartitions); + while (partition_element *sub_elem= subpart_it++) + { + dummy_info.data_file_name= sub_elem->data_file_name; + dummy_info.index_file_name= sub_elem->index_file_name; + if (m_file[i++]->check_if_incompatible_data(&dummy_info, table_changes)) + return COMPATIBLE_DATA_NO; + } + } + else + { + dummy_info.data_file_name= part_elem->data_file_name; + dummy_info.index_file_name= part_elem->index_file_name; + if (m_file[i++]->check_if_incompatible_data(&dummy_info, table_changes)) + return COMPATIBLE_DATA_NO; + } + } + return COMPATIBLE_DATA_YES; } diff --git a/sql/handler.cc b/sql/handler.cc index 6e1001090c8..a7530723c79 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -2113,7 +2113,7 @@ uint get_sql_xid(XID *xid, char *buf) MY_INT64_NUM_DECIMAL_DIGITS, -10, xid->formatID); } - return buf - orig_buf; + return (uint)(buf - orig_buf); } diff --git a/sql/item.cc b/sql/item.cc index e501fa6b488..d916e4328d7 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -2185,8 +2185,10 @@ bool Item_name_const::fix_fields(THD *thd, Item **ref) String s(buf, sizeof(buf), &my_charset_bin); s.length(0); - if (value_item->fix_fields(thd, &value_item) || - name_item->fix_fields(thd, &name_item) || + 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 @@ -5569,7 +5571,8 @@ resolve_ref_in_select_and_group(THD *thd, Item_ident *ref, SELECT_LEX *select) if (thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY && select->having_fix_field && - select_ref != not_found_item && !group_by_ref) + select_ref != not_found_item && !group_by_ref && + !ref->alias_name_used) { /* Report the error if fields was found only in the SELECT item list and diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 1d4a44d5326..780c60e4eb3 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -1842,6 +1842,19 @@ bool Item_func_opt_neg::eq(const Item *item, bool binary_cmp) const } +bool Item_func_interval::fix_fields(THD *thd, Item **ref) +{ + if (Item_long_func::fix_fields(thd, ref)) + return true; + for (uint i= 0 ; i < row->cols(); i++) + { + if (row->element_index(i)->check_cols(1)) + return true; + } + return false; +} + + void Item_func_interval::fix_length_and_dec() { uint rows= row->cols(); diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 1561a78a12a..8b47f09497f 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -976,6 +976,7 @@ public: Item_func_interval(THD *thd, Item_row *a): Item_long_func(thd, a), row(a), intervals(0) { } + bool fix_fields(THD *, Item **); longlong val_int(); void fix_length_and_dec(); const char *func_name() const { return "interval"; } diff --git a/sql/item_func.cc b/sql/item_func.cc index 55d141b9968..ac7ed75e7f9 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -6686,14 +6686,22 @@ longlong Item_func_nextval::val_int() longlong value; int error; const char *key; - TABLE *table= table_list->table; uint length= get_table_def_key(table_list, &key); - THD *thd= table->in_use; + THD *thd; SEQUENCE_LAST_VALUE *entry; char buff[80]; String key_buff(buff,sizeof(buff), &my_charset_bin); - DBUG_ASSERT(table && table->s->sequence); DBUG_ENTER("Item_func_nextval::val_int"); + update_table(); + DBUG_ASSERT(table && table->s->sequence); + thd= table->in_use; + + if (thd->count_cuted_fields == CHECK_FIELD_EXPRESSION) + { + /* Alter table checking if function works */ + null_value= 0; + DBUG_RETURN(0); + } if (table->s->tmp_table != NO_TMP_TABLE) { @@ -6745,7 +6753,7 @@ void Item_func_nextval::print(String *str, enum_query_type query_type) char d_name_buff[MAX_ALIAS_NAME], t_name_buff[MAX_ALIAS_NAME]; const char *d_name= table_list->db, *t_name= table_list->table_name; bool use_db_name= d_name && d_name[0]; - THD *thd= current_thd; + THD *thd= current_thd; // Don't trust 'table' str->append(func_name()); str->append('('); @@ -6785,12 +6793,14 @@ longlong Item_func_lastval::val_int() const char *key; SEQUENCE_LAST_VALUE *entry; uint length= get_table_def_key(table_list, &key); - THD *thd= table_list->table->in_use; + THD *thd; char buff[80]; String key_buff(buff,sizeof(buff), &my_charset_bin); DBUG_ENTER("Item_func_lastval::val_int"); + update_table(); + thd= table->in_use; - if (table_list->table->s->tmp_table != NO_TMP_TABLE) + if (table->s->tmp_table != NO_TMP_TABLE) { /* Temporary tables has an extra \0 at end to distinguish it from @@ -6809,7 +6819,7 @@ longlong Item_func_lastval::val_int() null_value= 1; DBUG_RETURN(0); } - if (entry->check_version(table_list->table)) + if (entry->check_version(table)) { /* Table droped and re-created, remove current version */ my_hash_delete(&thd->sequences, (uchar*) entry); @@ -6834,10 +6844,20 @@ longlong Item_func_setval::val_int() { longlong value; int error; - TABLE *table= table_list->table; - DBUG_ASSERT(table && table->s->sequence); + THD *thd; DBUG_ENTER("Item_func_setval::val_int"); + update_table(); + DBUG_ASSERT(table && table->s->sequence); + thd= table->in_use; + + if (thd->count_cuted_fields == CHECK_FIELD_EXPRESSION) + { + /* Alter table checking if function works */ + null_value= 0; + DBUG_RETURN(0); + } + value= nextval; error= table->s->sequence->set_value(table, nextval, round, is_used); if (error) @@ -6856,7 +6876,7 @@ void Item_func_setval::print(String *str, enum_query_type query_type) char d_name_buff[MAX_ALIAS_NAME], t_name_buff[MAX_ALIAS_NAME]; const char *d_name= table_list->db, *t_name= table_list->table_name; bool use_db_name= d_name && d_name[0]; - THD *thd= table_list->table->in_use; + THD *thd= current_thd; // Don't trust 'table' str->append(func_name()); str->append('('); diff --git a/sql/item_func.h b/sql/item_func.h index 4c78edcb1fa..c226d82c7b8 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -3008,9 +3008,10 @@ class Item_func_nextval :public Item_longlong_func { protected: TABLE_LIST *table_list; + TABLE *table; public: - Item_func_nextval(THD *thd, TABLE_LIST *table): - Item_longlong_func(thd), table_list(table) {} + Item_func_nextval(THD *thd, TABLE_LIST *table_list_arg): + Item_longlong_func(thd), table_list(table_list_arg) {} longlong val_int(); const char *func_name() const { return "nextval"; } void fix_length_and_dec() @@ -3019,6 +3020,22 @@ public: max_length= MAX_BIGINT_WIDTH; maybe_null= 1; /* In case of errors */ } + /* + update_table() function must be called during the value function + as in case of DEFAULT the sequence table may not yet be open + while fix_fields() are called + */ + void update_table() + { + if (!(table= table_list->table)) + { + /* + If nextval was used in DEFAULT then next_local points to + the table_list used by to open the sequence table + */ + table= table_list->next_local->table; + } + } bool const_item() const { return 0; } Item *get_copy(THD *thd) { return get_item_copy<Item_func_nextval>(thd, this); } @@ -3026,7 +3043,8 @@ public: bool check_vcol_func_processor(void *arg) { return mark_unsupported_function(func_name(), "()", arg, - VCOL_NON_DETERMINISTIC); + (VCOL_NON_DETERMINISTIC | + VCOL_NOT_VIRTUAL)); } }; @@ -3036,8 +3054,8 @@ public: class Item_func_lastval :public Item_func_nextval { public: - Item_func_lastval(THD *thd, TABLE_LIST *table): - Item_func_nextval(thd, table) {} + Item_func_lastval(THD *thd, TABLE_LIST *table_list_arg): + Item_func_nextval(thd, table_list_arg) {} longlong val_int(); const char *func_name() const { return "lastval"; } Item *get_copy(THD *thd) @@ -3053,9 +3071,9 @@ class Item_func_setval :public Item_func_nextval ulonglong round; bool is_used; public: - Item_func_setval(THD *thd, TABLE_LIST *table, longlong nextval_arg, + Item_func_setval(THD *thd, TABLE_LIST *table_list_arg, longlong nextval_arg, ulonglong round_arg, bool is_used_arg) - : Item_func_nextval(thd, table), + : Item_func_nextval(thd, table_list_arg), nextval(nextval_arg), round(round_arg), is_used(is_used_arg) {} longlong val_int(); diff --git a/sql/item_jsonfunc.cc b/sql/item_jsonfunc.cc index 7afb94ba332..4b2de7cf28b 100644 --- a/sql/item_jsonfunc.cc +++ b/sql/item_jsonfunc.cc @@ -781,10 +781,10 @@ String *Item_func_json_extract::read_json(String *str, { str->set_charset(js->charset()); str->length(0); - } - if (possible_multiple_values && str->append("[", 1)) - goto error; + if (possible_multiple_values && str->append("[", 1)) + goto error; + } json_get_path_start(&je, js->charset(),(const uchar *) js->ptr(), (const uchar *) js->ptr() + js->length(), &p); diff --git a/sql/item_sum.cc b/sql/item_sum.cc index d4abdfc614f..945224c2623 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -4136,7 +4136,7 @@ void Item_func_group_concat::print(String *str, enum_query_type query_type) } } str->append(STRING_WITH_LEN(" separator \'")); - str->append(*separator); + str->append_for_single_quote(separator->ptr(), separator->length()); str->append(STRING_WITH_LEN("\')")); } diff --git a/sql/lock.cc b/sql/lock.cc index 8e43001e742..11cadb528d2 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -294,7 +294,8 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, uint flags) if (lock_tables_check(thd, tables, count, flags)) DBUG_RETURN(NULL); - if (!(thd->variables.option_bits & OPTION_TABLE_LOCK)) + if (!(thd->variables.option_bits & OPTION_TABLE_LOCK) && + !(flags & MYSQL_LOCK_USE_MALLOC)) gld_flags|= GET_LOCK_ON_THD; if (! (sql_lock= get_lock_data(thd, tables, count, gld_flags))) @@ -415,7 +416,8 @@ static int lock_external(THD *thd, TABLE **tables, uint count) void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock) { mysql_unlock_tables(thd, sql_lock, - thd->variables.option_bits & OPTION_TABLE_LOCK); + (thd->variables.option_bits & OPTION_TABLE_LOCK) || + !(sql_lock->flags & GET_LOCK_ON_THD)); } @@ -433,7 +435,10 @@ void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock, bool free_lock) if (sql_lock->lock_count) thr_multi_unlock(sql_lock->locks, sql_lock->lock_count, 0); if (free_lock) + { + DBUG_ASSERT(!(sql_lock->flags & GET_LOCK_ON_THD)); my_free(sql_lock); + } if (!errors) thd->clear_error(); THD_STAGE_INFO(thd, org_stage); @@ -668,6 +673,7 @@ MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b) sql_lock->table_count=a->table_count+b->table_count; sql_lock->locks=(THR_LOCK_DATA**) (sql_lock+1); sql_lock->table=(TABLE**) (sql_lock->locks+sql_lock->lock_count*2); + sql_lock->flags= 0; memcpy(sql_lock->locks,a->locks,a->lock_count*sizeof(*a->locks)); memcpy(sql_lock->locks+a->lock_count,b->locks, b->lock_count*sizeof(*b->locks)); @@ -782,6 +788,7 @@ MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, uint flags) locks= locks_buf= sql_lock->locks= (THR_LOCK_DATA**) (sql_lock + 1); to= table_buf= sql_lock->table= (TABLE**) (locks + lock_count * 2); sql_lock->table_count= table_count; + sql_lock->flags= flags; for (i=0 ; i < count ; i++) { diff --git a/sql/log.cc b/sql/log.cc index bdf0b6fdc59..6923a6241cd 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -2470,7 +2470,7 @@ static int find_uniq_filename(char *name, ulong next_log_number) char buff[FN_REFLEN], ext_buf[FN_REFLEN]; struct st_my_dir *dir_info; reg1 struct fileinfo *file_info; - ulong max_found, next, number; + ulong max_found, next, UNINIT_VAR(number); size_t buf_length, length; char *start, *end; int error= 0; @@ -2993,7 +2993,6 @@ bool MYSQL_QUERY_LOG::write(THD *thd, time_t current_time, mysql_mutex_lock(&LOCK_log); if (is_open()) { // Safety agains reopen - int tmp_errno= 0; char buff[80], *end; char query_time_buff[22+7], lock_time_buff[22+7]; size_t buff_len; @@ -3015,16 +3014,13 @@ bool MYSQL_QUERY_LOG::write(THD *thd, time_t current_time, /* Note that my_b_write() assumes it knows the length for this */ if (my_b_write(&log_file, (uchar*) buff, buff_len)) - tmp_errno= errno; + goto err; } const uchar uh[]= "# User@Host: "; - if (my_b_write(&log_file, uh, sizeof(uh) - 1)) - tmp_errno= errno; - if (my_b_write(&log_file, (uchar*) user_host, user_host_len)) - tmp_errno= errno; - if (my_b_write(&log_file, (uchar*) "\n", 1)) - tmp_errno= errno; - } + if (my_b_write(&log_file, uh, sizeof(uh) - 1) || + my_b_write(&log_file, (uchar*) user_host, user_host_len) || + my_b_write(&log_file, (uchar*) "\n", 1)) + goto err; /* For slow query log */ sprintf(query_time_buff, "%.6f", ulonglong2double(query_utime)/1000000.0); @@ -3039,9 +3035,8 @@ bool MYSQL_QUERY_LOG::write(THD *thd, time_t current_time, (ulong) thd->get_sent_row_count(), (ulong) thd->get_examined_row_count(), (ulong) thd->get_affected_rows(), - (ulong) (thd->status_var.bytes_sent - thd->bytes_sent_old)) - == (size_t) -1) - tmp_errno= errno; + (ulong) (thd->status_var.bytes_sent - thd->bytes_sent_old))) + goto err; if ((thd->variables.log_slow_verbosity & LOG_SLOW_VERBOSITY_QUERY_PLAN) && thd->tmp_tables_used && @@ -3050,13 +3045,13 @@ bool MYSQL_QUERY_LOG::write(THD *thd, time_t current_time, "Tmp_table_sizes: %s\n", (ulong) thd->tmp_tables_used, (ulong) thd->tmp_tables_disk_used, - llstr(thd->tmp_tables_size, llbuff)) == (uint) -1) - tmp_errno= errno; + llstr(thd->tmp_tables_size, llbuff))) + goto err; - if (thd->spcont) - if (my_b_printf(&log_file, "# Stored_routine: %s\n", - ErrConvDQName(thd->spcont->m_sp).ptr()) == (uint) -1) - tmp_errno= errno; + if (thd->spcont && + my_b_printf(&log_file, "# Stored_routine: %s\n", + ErrConvDQName(thd->spcont->m_sp).ptr())) + goto err; if ((thd->variables.log_slow_verbosity & LOG_SLOW_VERBOSITY_QUERY_PLAN) && (thd->query_plan_flags & @@ -3078,21 +3073,22 @@ bool MYSQL_QUERY_LOG::write(THD *thd, time_t current_time, thd->query_plan_fsort_passes, ((thd->query_plan_flags & QPLAN_FILESORT_PRIORITY_QUEUE) ? "Yes" : "No") - ) == (size_t) -1) - tmp_errno= errno; + )) + goto err; if (thd->variables.log_slow_verbosity & LOG_SLOW_VERBOSITY_EXPLAIN && thd->lex->explain) { StringBuffer<128> buf; DBUG_ASSERT(!thd->free_list); if (!print_explain_for_slow_log(thd->lex, thd, &buf)) - my_b_printf(&log_file, "%s", buf.c_ptr_safe()); + if (my_b_printf(&log_file, "%s", buf.c_ptr_safe())) + goto err; thd->free_items(); } if (thd->db && strcmp(thd->db, db)) { // Database changed - if (my_b_printf(&log_file,"use %s;\n",thd->db) == (size_t) -1) - tmp_errno= errno; + if (my_b_printf(&log_file,"use %s;\n",thd->db)) + goto err; strmov(db,thd->db); } if (thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt) @@ -3128,7 +3124,7 @@ bool MYSQL_QUERY_LOG::write(THD *thd, time_t current_time, *end='\n'; if (my_b_write(&log_file, (uchar*) "SET ", 4) || my_b_write(&log_file, (uchar*) buff + 1, (uint) (end-buff))) - tmp_errno= errno; + goto err; } if (is_command) { @@ -3137,24 +3133,27 @@ bool MYSQL_QUERY_LOG::write(THD *thd, time_t current_time, DBUG_EXECUTE_IF("simulate_slow_log_write_error", {DBUG_SET("+d,simulate_file_write_error");}); if(my_b_write(&log_file, (uchar*) buff, buff_len)) - tmp_errno= errno; + goto err; } if (my_b_write(&log_file, (uchar*) sql_text, sql_text_len) || my_b_write(&log_file, (uchar*) ";\n",2) || flush_io_cache(&log_file)) - tmp_errno= errno; - if (tmp_errno) - { - error= 1; - if (! write_error) - { - write_error= 1; - sql_print_error(ER_THD(thd, ER_ERROR_ON_WRITE), name, tmp_errno); - } + goto err; + } } +end: mysql_mutex_unlock(&LOCK_log); DBUG_RETURN(error); + +err: + error= 1; + if (! write_error) + { + write_error= 1; + sql_print_error(ER_THD(thd, ER_ERROR_ON_WRITE), name, errno); + } + goto end; } @@ -3650,6 +3649,7 @@ bool MYSQL_BIN_LOG::open(const char *log_name, new_xid_list_entry->binlog_name= name_mem; new_xid_list_entry->binlog_name_len= len; new_xid_list_entry->xid_count= 0; + new_xid_list_entry->notify_count= 0; /* Find the name for the Initial binlog checkpoint. @@ -5006,7 +5006,55 @@ MYSQL_BIN_LOG::is_xidlist_idle_nolock() return true; } +#ifdef WITH_WSREP +inline bool +is_gtid_cached_internal(IO_CACHE *file) +{ + uchar data[EVENT_TYPE_OFFSET+1]; + bool result= false; + my_off_t write_pos= my_b_tell(file); + if (reinit_io_cache(file, READ_CACHE, 0, 0, 0)) + return false; + /* + In the cache we have gtid event if , below condition is true, + */ + my_b_read(file, data, sizeof(data)); + uint event_type= (uchar)data[EVENT_TYPE_OFFSET]; + if (event_type == GTID_LOG_EVENT) + result= true; + /* + Cleanup , Why because we have not read the full buffer + and this will cause next to next reinit_io_cache(called in write_cache) + to make cache empty. + */ + file->read_pos= file->read_end; + if (reinit_io_cache(file, WRITE_CACHE, write_pos, 0, 0)) + return false; + return result; +} +#endif +#ifdef WITH_WSREP +inline bool +MYSQL_BIN_LOG::is_gtid_cached(THD *thd) +{ + binlog_cache_mngr *mngr= (binlog_cache_mngr *) thd_get_ha_data( + thd, binlog_hton); + if (!mngr) + return false; + binlog_cache_data *cache_trans= mngr->get_binlog_cache_data( + use_trans_cache(thd, true)); + binlog_cache_data *cache_stmt= mngr->get_binlog_cache_data( + use_trans_cache(thd, false)); + if (cache_trans && !cache_trans->empty() && + is_gtid_cached_internal(&cache_trans->cache_log)) + return true; + if (cache_stmt && !cache_stmt->empty() && + is_gtid_cached_internal(&cache_stmt->cache_log)) + return true; + return false; +} +#endif /** Create a new log file name. @@ -5611,7 +5659,36 @@ THD::binlog_start_trans_and_stmt() cache_mngr->trx_cache.get_prev_position() == MY_OFF_T_UNDEF) { this->binlog_set_stmt_begin(); - if (in_multi_stmt_transaction_mode()) + bool mstmt_mode= in_multi_stmt_transaction_mode(); +#ifdef WITH_WSREP + /* Write Gtid + Get domain id only when gtid mode is set + If this event is replicate through a master then , + we will forward the same gtid another nodes + We have to do this only one time in mysql transaction. + Since this function is called multiple times , We will check for + ha_info->is_started() + */ + Ha_trx_info *ha_info; + ha_info= this->ha_data[binlog_hton->slot].ha_info + (mstmt_mode ? 1 : 0); + + if (!ha_info->is_started() && wsrep_gtid_mode + && this->variables.gtid_seq_no) + { + binlog_cache_mngr *const cache_mngr= + (binlog_cache_mngr*) thd_get_ha_data(this, binlog_hton); + binlog_cache_data *cache_data= cache_mngr->get_binlog_cache_data(1); + IO_CACHE *file= &cache_data->cache_log; + Log_event_writer writer(file, cache_data); + Gtid_log_event gtid_event(this, this->variables.gtid_seq_no, + this->variables.gtid_domain_id, + true, LOG_EVENT_SUPPRESS_USE_F, + true, 0); + gtid_event.server_id= this->variables.server_id; + writer.write(>id_event); + } +#endif + if (mstmt_mode) trans_register_ha(this, TRUE, binlog_hton); trans_register_ha(this, FALSE, binlog_hton); /* @@ -5891,7 +5968,7 @@ MYSQL_BIN_LOG::write_gtid_event(THD *thd, bool standalone, DBUG_PRINT("enter", ("standalone: %d", standalone)); #ifdef WITH_WSREP - if (WSREP(thd) && thd->wsrep_trx_meta.gtid.seqno != -1 && wsrep_gtid_mode) + if (WSREP(thd) && thd->wsrep_trx_meta.gtid.seqno != -1 && wsrep_gtid_mode && !thd->variables.gtid_seq_no) { domain_id= wsrep_gtid_domain_id; } else { @@ -5945,6 +6022,12 @@ MYSQL_BIN_LOG::write_gtid_event(THD *thd, bool standalone, /* Write the event to the binary log. */ DBUG_ASSERT(this == &mysql_bin_log); + +#ifdef WITH_WSREP + if (wsrep_gtid_mode && is_gtid_cached(thd)) + DBUG_RETURN(false); +#endif + if (write_event(>id_event)) DBUG_RETURN(true); status_var_add(thd->status_var.binlog_bytes_written, gtid_event.data_written); @@ -9720,9 +9803,20 @@ void TC_LOG_BINLOG::commit_checkpoint_notify(void *cookie) { xid_count_per_binlog *entry= static_cast<xid_count_per_binlog *>(cookie); + bool found_entry= false; mysql_mutex_lock(&LOCK_binlog_background_thread); - entry->next_in_queue= binlog_background_thread_queue; - binlog_background_thread_queue= entry; + /* count the same notification kind from different engines */ + for (xid_count_per_binlog *link= binlog_background_thread_queue; + link && !found_entry; link= link->next_in_queue) + { + if ((found_entry= (entry == link))) + entry->notify_count++; + } + if (!found_entry) + { + entry->next_in_queue= binlog_background_thread_queue; + binlog_background_thread_queue= entry; + } mysql_cond_signal(&COND_binlog_background_thread); mysql_mutex_unlock(&LOCK_binlog_background_thread); } @@ -9817,13 +9911,16 @@ binlog_background_thread(void *arg __attribute__((unused))) ); while (queue) { + long count= queue->notify_count; THD_STAGE_INFO(thd, stage_binlog_processing_checkpoint_notify); DEBUG_SYNC(thd, "binlog_background_thread_before_mark_xid_done"); /* Set the thread start time */ thd->set_time(); /* Grab next pointer first, as mark_xid_done() may free the element. */ next= queue->next_in_queue; - mysql_bin_log.mark_xid_done(queue->binlog_id, true); + queue->notify_count= 0; + for (long i= 0; i <= count; i++) + mysql_bin_log.mark_xid_done(queue->binlog_id, true); queue= next; DBUG_EXECUTE_IF("binlog_background_checkpoint_processed", diff --git a/sql/log.h b/sql/log.h index 02ace7c7921..6305dd97355 100644 --- a/sql/log.h +++ b/sql/log.h @@ -571,7 +571,13 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG bool write_transaction_to_binlog_events(group_commit_entry *entry); void trx_group_commit_leader(group_commit_entry *leader); bool is_xidlist_idle_nolock(); - +#ifdef WITH_WSREP + /* + When this mariadb node is slave and galera enabled. So in this case + we write the gtid in wsrep_run_commit itself. + */ + inline bool is_gtid_cached(THD *thd); +#endif public: /* A list of struct xid_count_per_binlog is used to keep track of how many @@ -591,6 +597,7 @@ public: ulong binlog_id; /* Total prepared XIDs and pending checkpoint requests in this binlog. */ long xid_count; + long notify_count; /* For linking in requests to the binlog background thread. */ xid_count_per_binlog *next_in_queue; xid_count_per_binlog(); /* Give link error if constructor used. */ diff --git a/sql/log_event.cc b/sql/log_event.cc index e01ddf6ac65..ca659085228 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -18,7 +18,6 @@ #include "mariadb.h" #include "sql_priv.h" -#include "mysqld_error.h" #ifndef MYSQL_CLIENT #include "unireg.h" @@ -45,6 +44,8 @@ #include "compat56.h" #include "wsrep_mysqld.h" #include "sql_insert.h" +#else +#include "mysqld_error.h" #endif /* MYSQL_CLIENT */ #include <my_bitmap.h> @@ -217,7 +218,7 @@ is_parallel_retry_error(rpl_group_info *rgi, int err) static void inline slave_rows_error_report(enum loglevel level, int ha_error, rpl_group_info *rgi, THD *thd, TABLE *table, const char * type, - const char *log_name, ulong pos) + const char *log_name, my_off_t pos) { const char *handler_error= (ha_error ? HA_ERR(ha_error) : NULL); char buff[MAX_SLAVE_ERRMSG], *slider; @@ -251,14 +252,14 @@ static void inline slave_rows_error_report(enum loglevel level, int ha_error, rli->report(level, errcode, rgi->gtid_info(), "Could not execute %s event on table %s.%s;" "%s handler error %s; " - "the event's master log %s, end_log_pos %lu", + "the event's master log %s, end_log_pos %llu", type, table->s->db.str, table->s->table_name.str, buff, handler_error == NULL ? "<unknown>" : handler_error, log_name, pos); else rli->report(level, errcode, rgi->gtid_info(), "Could not execute %s event on table %s.%s;" - "%s the event's master log %s, end_log_pos %lu", + "%s the event's master log %s, end_log_pos %llu", type, table->s->db.str, table->s->table_name.str, buff, log_name, pos); } @@ -291,10 +292,9 @@ public: flags Flags for the cache DESCRIPTION - - Class used to guarantee copy of cache to file before exiting the - current block. On successful copy of the cache, the cache will - be reinited as a WRITE_CACHE. + Cache common parameters and ensure common flush_data() code + on successful copy of the cache, the cache will be reinited as a + WRITE_CACHE. Currently, a pointer to the cache is provided in the constructor, but it would be possible to create a subclass @@ -306,28 +306,35 @@ public: reinit_io_cache(m_cache, WRITE_CACHE, 0L, FALSE, TRUE); } - ~Write_on_release_cache() + ~Write_on_release_cache() {} + + bool flush_data() { #ifdef MYSQL_CLIENT - if(m_ev == NULL) + if (m_ev == NULL) { - copy_event_cache_to_file_and_reinit(m_cache, m_file); - if (m_flags & FLUSH_F) - fflush(m_file); + if (copy_event_cache_to_file_and_reinit(m_cache, m_file)) + return 1; + if ((m_flags & FLUSH_F) && fflush(m_file)) + return 1; } else // if m_ev<>NULL, then storing the output in output_buf { LEX_STRING tmp_str; + bool res; if (copy_event_cache_to_string_and_reinit(m_cache, &tmp_str)) - exit(1); - m_ev->output_buf.append(&tmp_str); + return 1; + res= m_ev->output_buf.append(&tmp_str) != 0; my_free(tmp_str.str); + return res ? res : 0; } #else /* MySQL_SERVER */ - copy_event_cache_to_file_and_reinit(m_cache, m_file); - if (m_flags & FLUSH_F) - fflush(m_file); + if (copy_event_cache_to_file_and_reinit(m_cache, m_file)) + return 1; + if ((m_flags & FLUSH_F) && fflush(m_file)) + return 1; #endif + return 0; } /* @@ -365,27 +372,36 @@ private: */ #ifdef MYSQL_CLIENT -static void pretty_print_str(IO_CACHE* cache, const char* str, int len) +static bool pretty_print_str(IO_CACHE* cache, const char* str, int len) { const char* end = str + len; - my_b_write_byte(cache, '\''); + if (my_b_write_byte(cache, '\'')) + goto err; + while (str < end) { char c; + int error; + switch ((c=*str++)) { - case '\n': my_b_write(cache, (uchar*)"\\n", 2); break; - case '\r': my_b_write(cache, (uchar*)"\\r", 2); break; - case '\\': my_b_write(cache, (uchar*)"\\\\", 2); break; - case '\b': my_b_write(cache, (uchar*)"\\b", 2); break; - case '\t': my_b_write(cache, (uchar*)"\\t", 2); break; - case '\'': my_b_write(cache, (uchar*)"\\'", 2); break; - case 0 : my_b_write(cache, (uchar*)"\\0", 2); break; + case '\n': error= my_b_write(cache, (uchar*)"\\n", 2); break; + case '\r': error= my_b_write(cache, (uchar*)"\\r", 2); break; + case '\\': error= my_b_write(cache, (uchar*)"\\\\", 2); break; + case '\b': error= my_b_write(cache, (uchar*)"\\b", 2); break; + case '\t': error= my_b_write(cache, (uchar*)"\\t", 2); break; + case '\'': error= my_b_write(cache, (uchar*)"\\'", 2); break; + case 0 : error= my_b_write(cache, (uchar*)"\\0", 2); break; default: - my_b_write_byte(cache, c); + error= my_b_write_byte(cache, c); break; } + if (error) + goto err; } - my_b_write_byte(cache, '\''); + return my_b_write_byte(cache, '\''); + +err: + return 1; } #endif /* MYSQL_CLIENT */ @@ -1145,19 +1161,25 @@ int append_query_string(CHARSET_INFO *csinfo, String *to, #ifdef MYSQL_CLIENT -static void print_set_option(IO_CACHE* file, uint32 bits_changed, +static bool print_set_option(IO_CACHE* file, uint32 bits_changed, uint32 option, uint32 flags, const char* name, bool* need_comma) { if (bits_changed & option) { if (*need_comma) - my_b_write(file, (uchar*)", ", 2); - my_b_printf(file, "%s=%d", name, MY_TEST(flags & option)); + if (my_b_write(file, (uchar*)", ", 2)) + goto err; + if (my_b_printf(file, "%s=%d", name, MY_TEST(flags & option))) + goto err; *need_comma= 1; } + return 0; +err: + return 1; } #endif + /************************************************************************** Log_event methods (= the parent class of all events) **************************************************************************/ @@ -1328,7 +1350,7 @@ Log_event::Log_event(const char* buf, */ log_pos+= data_written; /* purecov: inspected */ } - DBUG_PRINT("info", ("log_pos: %lu", (ulong) log_pos)); + DBUG_PRINT("info", ("log_pos: %llu", log_pos)); flags= uint2korr(buf + FLAGS_OFFSET); if (((uchar)buf[EVENT_TYPE_OFFSET] == FORMAT_DESCRIPTION_EVENT) || @@ -1457,7 +1479,7 @@ void Log_event::init_show_field_list(THD *thd, List<Item>* field_list) mem_root); field_list->push_back(new (mem_root) Item_return_int(thd, "Pos", - MY_INT32_NUM_DECIMAL_DIGITS, + MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG), mem_root); field_list->push_back(new (mem_root) @@ -1469,7 +1491,7 @@ void Log_event::init_show_field_list(THD *thd, List<Item>* field_list) mem_root); field_list->push_back(new (mem_root) Item_return_int(thd, "End_log_pos", - MY_INT32_NUM_DECIMAL_DIGITS, + MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG), mem_root); field_list->push_back(new (mem_root) Item_empty_string(thd, "Info", 20), @@ -1910,9 +1932,9 @@ err: #endif if (event.length() >= OLD_HEADER_LEN) sql_print_error("Error in Log_event::read_log_event(): '%s'," - " data_len: %lu, event_type: %d", error, - uint4korr(&event[EVENT_LEN_OFFSET]), - (uchar)event[EVENT_TYPE_OFFSET]); + " data_len: %lu, event_type: %u", error, + (ulong) uint4korr(&event[EVENT_LEN_OFFSET]), + (uint) (uchar)event[EVENT_TYPE_OFFSET]); else sql_print_error("Error in Log_event::read_log_event(): '%s'", error); /* @@ -2242,7 +2264,7 @@ Log_event* Log_event::read_log_event(const char* buf, uint event_len, #ifdef MYSQL_CLIENT -static void hexdump_minimal_header_to_io_cache(IO_CACHE *file, +static bool hexdump_minimal_header_to_io_cache(IO_CACHE *file, my_off_t offset, uchar *ptr) { @@ -2255,15 +2277,18 @@ static void hexdump_minimal_header_to_io_cache(IO_CACHE *file, more headers (which must be printed by other methods, if desired). */ char emit_buf[120]; // Enough for storing one line - my_b_printf(file, - "# " - "|Timestamp " - "|Type " - "|Master ID " - "|Size " - "|Master Pos " - "|Flags\n"); - size_t const emit_buf_written= + size_t emit_buf_written; + + if (my_b_printf(file, + "# " + "|Timestamp " + "|Type " + "|Master ID " + "|Size " + "|Master Pos " + "|Flags\n")) + goto err; + emit_buf_written= my_snprintf(emit_buf, sizeof(emit_buf), "# %8llx " /* Position */ "|%02x %02x %02x %02x " /* Timestamp */ @@ -2281,8 +2306,13 @@ static void hexdump_minimal_header_to_io_cache(IO_CACHE *file, ptr[17], ptr[18]); /* Flags */ DBUG_ASSERT(static_cast<size_t>(emit_buf_written) < sizeof(emit_buf)); - my_b_write(file, reinterpret_cast<uchar*>(emit_buf), emit_buf_written); - my_b_write(file, (uchar*)"#\n", 2); + if (my_b_write(file, reinterpret_cast<uchar*>(emit_buf), emit_buf_written) || + my_b_write(file, (uchar*)"#\n", 2)) + goto err; + + return 0; +err: + return 1; } @@ -2307,7 +2337,7 @@ static void format_hex_line(char *emit_buff) HEXDUMP_BYTES_PER_LINE + 2]= '\0'; } -static void hexdump_data_to_io_cache(IO_CACHE *file, +static bool hexdump_data_to_io_cache(IO_CACHE *file, my_off_t offset, uchar *ptr, my_off_t size) @@ -2329,7 +2359,7 @@ static void hexdump_data_to_io_cache(IO_CACHE *file, my_off_t i; if (size == 0) - return; + return 0; // ok, nothing to do format_hex_line(emit_buffer); /* @@ -2359,8 +2389,9 @@ static void hexdump_data_to_io_cache(IO_CACHE *file, (ulonglong) starting_offset); /* remove \0 left after printing address */ emit_buffer[2 + emit_buf_written]= ' '; - my_b_write(file, reinterpret_cast<uchar*>(emit_buffer), - sizeof(emit_buffer) - 1); + if (my_b_write(file, reinterpret_cast<uchar*>(emit_buffer), + sizeof(emit_buffer) - 1)) + goto err; c= emit_buffer + 2 + 8 + 2 + (HEXDUMP_BYTES_PER_LINE * 3 + 1) + 2; h= emit_buffer + 2 + 8 + 2; format_hex_line(emit_buffer); @@ -2395,17 +2426,23 @@ static void hexdump_data_to_io_cache(IO_CACHE *file, /* pad unprinted area */ memset(h, ' ', (HEXDUMP_BYTES_PER_LINE * 3 + 1) - (h - (emit_buffer + 2 + 8 + 2))); - my_b_write(file, reinterpret_cast<uchar*>(emit_buffer), - c - emit_buffer); + if (my_b_write(file, reinterpret_cast<uchar*>(emit_buffer), + c - emit_buffer)) + goto err; } - my_b_write(file, (uchar*)"#\n", 2); + if (my_b_write(file, (uchar*)"#\n", 2)) + goto err; + + return 0; +err: + return 1; } /* Log_event::print_header() */ -void Log_event::print_header(IO_CACHE* file, +bool Log_event::print_header(IO_CACHE* file, PRINT_EVENT_INFO* print_event_info, bool is_more __attribute__((unused))) { @@ -2413,10 +2450,11 @@ void Log_event::print_header(IO_CACHE* file, my_off_t hexdump_from= print_event_info->hexdump_from; DBUG_ENTER("Log_event::print_header"); - my_b_write_byte(file, '#'); - print_timestamp(file); - my_b_printf(file, " server id %lu end_log_pos %s ", (ulong) server_id, - llstr(log_pos,llbuff)); + if (my_b_write_byte(file, '#') || + print_timestamp(file) || + my_b_printf(file, " server id %lu end_log_pos %s ", (ulong) server_id, + llstr(log_pos,llbuff))) + goto err; /* print the checksum */ @@ -2426,8 +2464,10 @@ void Log_event::print_header(IO_CACHE* file, char checksum_buf[BINLOG_CHECKSUM_LEN * 2 + 4]; // to fit to "%p " size_t const bytes_written= my_snprintf(checksum_buf, sizeof(checksum_buf), "0x%08x ", crc); - my_b_printf(file, "%s ", get_type(&binlog_checksum_typelib, checksum_alg)); - my_b_printf(file, checksum_buf, bytes_written); + if (my_b_printf(file, "%s ", get_type(&binlog_checksum_typelib, + checksum_alg)) || + my_b_printf(file, checksum_buf, bytes_written)) + goto err; } /* mysqlbinlog --hexdump */ @@ -2440,24 +2480,32 @@ void Log_event::print_header(IO_CACHE* file, size-= hdr_len; - my_b_printf(file, "# Position\n"); + if (my_b_printf(file, "# Position\n")) + goto err; /* Write the header, nicely formatted by field. */ - hexdump_minimal_header_to_io_cache(file, hexdump_from, ptr); + if (hexdump_minimal_header_to_io_cache(file, hexdump_from, ptr)) + goto err; ptr+= hdr_len; hexdump_from+= hdr_len; /* Print the rest of the data, mimicking "hexdump -C" output. */ - hexdump_data_to_io_cache(file, hexdump_from, ptr, size); + if (hexdump_data_to_io_cache(file, hexdump_from, ptr, size)) + goto err; /* Prefix the next line so that the output from print_helper() will appear as a comment. */ - my_b_write(file, (uchar*)"# Event: ", 9); + if (my_b_write(file, (uchar*)"# Event: ", 9)) + goto err; } - DBUG_VOID_RETURN; + + DBUG_RETURN(0); + +err: + DBUG_RETURN(1); } @@ -2465,7 +2513,7 @@ 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 Single-quote and backslash characters are escaped with a \ - + @param[in] file IO cache @param[in] prt Pointer to string @param[in] length String length @@ -2552,12 +2600,14 @@ my_b_write_quoted_with_length(IO_CACHE *file, const uchar *ptr, uint length) @param[in] sl Signed number @param[in] ul Unsigned number */ -static void +static bool my_b_write_sint32_and_uint32(IO_CACHE *file, int32 si, uint32 ui) { - my_b_printf(file, "%d", si); + bool res= my_b_printf(file, "%d", si); if (si < 0) - my_b_printf(file, " (%u)", ui); + if (my_b_printf(file, " (%u)", ui)) + res= 1; + return res; } @@ -2576,8 +2626,8 @@ my_b_write_sint32_and_uint32(IO_CACHE *file, int32 si, uint32 ui) */ static size_t -log_event_print_value(IO_CACHE *file, const uchar *ptr, - uint type, uint meta, +log_event_print_value(IO_CACHE *file, PRINT_EVENT_INFO *print_event_info, + const uchar *ptr, uint type, uint meta, char *typestr, size_t typestr_length) { uint32 length= 0; @@ -2960,19 +3010,15 @@ log_event_print_value(IO_CACHE *file, const uchar *ptr, return my_b_write_quoted_with_length(file, ptr, length); case MYSQL_TYPE_DECIMAL: - my_b_printf(file, - "!! Old DECIMAL (mysql-4.1 or earlier). " - "Not enough metadata to display the value. "); + print_event_info->flush_for_error(); + fprintf(stderr, "\nError: Found Old DECIMAL (mysql-4.1 or earlier). " + "Not enough metadata to display the value.\n"); 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); - } + print_event_info->flush_for_error(); + fprintf(stderr, + "\nError: Don't know how to handle column type: %d meta: %d (%04x)\n", + type, meta, meta); break; } *typestr= 0; @@ -2993,7 +3039,8 @@ return_null: @param[in] value Pointer to packed row @param[in] prefix Row's SQL clause ("SET", "WHERE", etc) - @retval - number of bytes scanned. + @retval 0 error + # number of bytes scanned. */ @@ -3023,7 +3070,8 @@ Rows_log_event::print_verbose_one_row(IO_CACHE *file, table_def *td, value+= (bitmap_bits_set(cols_bitmap) + 7) / 8; if (!no_fill_output) - my_b_printf(file, "%s", prefix); + if (my_b_printf(file, "%s", prefix)) + goto err; for (size_t i= 0; i < td->size(); i ++) { @@ -3035,7 +3083,8 @@ Rows_log_event::print_verbose_one_row(IO_CACHE *file, table_def *td, continue; if (!no_fill_output) - my_b_printf(file, "### @%d=", static_cast<int>(i + 1)); + if (my_b_printf(file, "### @%d=", static_cast<int>(i + 1))) + goto err; if (!is_null) { @@ -3043,8 +3092,9 @@ Rows_log_event::print_verbose_one_row(IO_CACHE *file, table_def *td, if (value + fsize > m_rows_end) { if (!no_fill_output) - my_b_printf(file, "***Corrupted replication event was detected." - " Not printing the value***\n"); + if (my_b_printf(file, "***Corrupted replication event was detected." + " Not printing the value***\n")) + goto err; value+= fsize; return 0; } @@ -3052,7 +3102,7 @@ Rows_log_event::print_verbose_one_row(IO_CACHE *file, table_def *td, if (!no_fill_output) { - size= log_event_print_value(file,is_null? NULL: value, + size= log_event_print_value(file, print_event_info, is_null? NULL: value, td->type(i), td->field_metadata(i), typestr, sizeof(typestr)); #ifdef WHEN_FLASHBACK_REVIEW_READY @@ -3063,12 +3113,14 @@ Rows_log_event::print_verbose_one_row(IO_CACHE *file, table_def *td, // Using a tmp IO_CACHE to get the value output open_cached_file(&tmp_cache, NULL, NULL, 0, MYF(MY_WME | MY_NABP)); - size= log_event_print_value(&tmp_cache, is_null? NULL: value, + size= log_event_print_value(&tmp_cache, print_event_info, + is_null ? NULL: value, td->type(i), td->field_metadata(i), typestr, sizeof(typestr)); - if (copy_event_cache_to_string_and_reinit(&tmp_cache, &review_str)) - exit(1); + error= copy_event_cache_to_string_and_reinit(&tmp_cache, &review_str); close_cached_file(&tmp_cache); + if (error) + return 0; switch (td->type(i)) // Converting a string to HEX format { @@ -3086,12 +3138,14 @@ Rows_log_event::print_verbose_one_row(IO_CACHE *file, table_def *td, exit(1); } octet2hex((char*) hex_str.ptr(), tmp_str.ptr(), tmp_str.length()); - my_b_printf(review_sql, ", UNHEX('%s')", hex_str.ptr()); + if (my_b_printf(review_sql, ", UNHEX('%s')", hex_str.ptr())) + goto err; break; default: tmp_str.free(); - tmp_str.append(review_str.str, review_str.length); - my_b_printf(review_sql, ", %s", tmp_str.ptr()); + if (tmp_str.append(review_str.str, review_str.length) || + my_b_printf(review_sql, ", %s", tmp_str.ptr())) + goto err; break; } my_free(revieww_str.str); @@ -3102,36 +3156,40 @@ Rows_log_event::print_verbose_one_row(IO_CACHE *file, table_def *td, { IO_CACHE tmp_cache; open_cached_file(&tmp_cache, NULL, NULL, 0, MYF(MY_WME | MY_NABP)); - size= log_event_print_value(&tmp_cache,is_null? NULL: value, + size= log_event_print_value(&tmp_cache, print_event_info, + is_null ? NULL: value, td->type(i), td->field_metadata(i), typestr, sizeof(typestr)); close_cached_file(&tmp_cache); } if (!size) - return 0; + goto err; if (!is_null) value+= size; if (print_event_info->verbose > 1 && !no_fill_output) { - my_b_write(file, (uchar*)" /* ", 4); - - my_b_printf(file, "%s ", typestr); - - my_b_printf(file, "meta=%d nullable=%d is_null=%d ", - td->field_metadata(i), - td->maybe_null(i), is_null); - my_b_write(file, (uchar*)"*/", 2); + if (my_b_write(file, (uchar*)" /* ", 4) || + my_b_printf(file, "%s ", typestr) || + my_b_printf(file, "meta=%d nullable=%d is_null=%d ", + td->field_metadata(i), + td->maybe_null(i), is_null) || + my_b_write(file, (uchar*)"*/", 2)) + goto err; } if (!no_fill_output) - my_b_write_byte(file, '\n'); + if (my_b_write_byte(file, '\n')) + goto err; null_bit_index++; } return value - value0; + +err: + return 0; } @@ -3170,10 +3228,10 @@ void Rows_log_event::change_to_flashback_event(PRINT_EVENT_INFO *print_event_inf if (!(length1= print_verbose_one_row(NULL, td, print_event_info, &m_cols, value, (const uchar*) "", TRUE))) - { - fprintf(stderr, "\nError row length: %zu\n", length1); - exit(1); - } + { + fprintf(stderr, "\nError row length: %zu\n", length1); + exit(1); + } value+= length1; swap_buff1= (uchar *) my_malloc(length1, MYF(0)); @@ -3252,18 +3310,230 @@ end: delete td; } +/** + Calc length of a packed value of the given SQL type + + @param[in] ptr Pointer to string + @param[in] type Column type + @param[in] meta Column meta information + + @retval - number of bytes scanned from ptr. + Except in case of NULL, in which case we return 1 to indicate ok +*/ + +static size_t calc_field_event_length(const uchar *ptr, uint type, uint meta) +{ + 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; + } + else + length = meta & 0xFF; + } + else + length= meta; + } + + switch (type) { + case MYSQL_TYPE_LONG: + case MYSQL_TYPE_TIMESTAMP: + return 4; + case MYSQL_TYPE_TINY: + case MYSQL_TYPE_YEAR: + return 1; + case MYSQL_TYPE_SHORT: + return 2; + case MYSQL_TYPE_INT24: + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_NEWDATE: + case MYSQL_TYPE_DATE: + return 3; + case MYSQL_TYPE_LONGLONG: + case MYSQL_TYPE_DATETIME: + return 8; + case MYSQL_TYPE_NEWDECIMAL: + { + uint precision= meta >> 8; + uint decimals= meta & 0xFF; + uint bin_size= my_decimal_get_binary_size(precision, decimals); + return bin_size; + } + case MYSQL_TYPE_FLOAT: + return 4; + case MYSQL_TYPE_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; + return length; + } + case MYSQL_TYPE_TIMESTAMP2: + return my_timestamp_binary_length(meta); + case MYSQL_TYPE_DATETIME2: + return my_datetime_binary_length(meta); + case MYSQL_TYPE_TIME2: + return my_time_binary_length(meta); + case MYSQL_TYPE_ENUM: + switch (meta & 0xFF) { + case 1: + case 2: + return (meta & 0xFF); + default: + /* Unknown ENUM packlen=%d", meta & 0xFF */ + return 0; + } + break; + case MYSQL_TYPE_SET: + return meta & 0xFF; + case MYSQL_TYPE_BLOB: + return (meta <= 4 ? meta : 0); + case MYSQL_TYPE_VARCHAR: + case MYSQL_TYPE_VAR_STRING: + length= meta; + /* fall through */ + case MYSQL_TYPE_STRING: + if (length < 256) + return (uint) *ptr + 1; + return uint2korr(ptr) + 2; + case MYSQL_TYPE_DECIMAL: + break; + default: + break; + } + return 0; +} + + +size_t +Rows_log_event::calc_row_event_length(table_def *td, + PRINT_EVENT_INFO *print_event_info, + MY_BITMAP *cols_bitmap, + const uchar *value) +{ + const uchar *value0= value; + const uchar *null_bits= value; + uint null_bit_index= 0; + + /* + Skip metadata bytes which gives the information about nullabity of master + columns. Master writes one bit for each affected column. + */ + + value+= (bitmap_bits_set(cols_bitmap) + 7) / 8; + + for (size_t i= 0; i < td->size(); i ++) + { + int is_null; + is_null= (null_bits[null_bit_index / 8] >> (null_bit_index % 8)) & 0x01; + + if (bitmap_is_set(cols_bitmap, i) == 0) + continue; + + if (!is_null) + { + size_t size; + size_t fsize= td->calc_field_size((uint)i, (uchar*) value); + if (value + fsize > m_rows_end) + { + /* Corrupted replication event was detected, skipping entry */ + return 0; + } + if (!(size= calc_field_event_length(value, td->type(i), + td->field_metadata(i)))) + return 0; + value+= size; + } + null_bit_index++; + } + return value - value0; +} + + +/** + Calculate how many rows there are in the event + + @param[in] file IO cache + @param[in] print_event_into Print parameters +*/ + +void Rows_log_event::count_row_events(PRINT_EVENT_INFO *print_event_info) +{ + Table_map_log_event *map; + table_def *td; + uint row_events; + Log_event_type general_type_code= get_general_type_code(); + + switch (general_type_code) { + case WRITE_ROWS_EVENT: + case DELETE_ROWS_EVENT: + row_events= 1; + break; + case UPDATE_ROWS_EVENT: + row_events= 2; + break; + default: + DBUG_ASSERT(0); /* Not possible */ + return; + } + + if (!(map= print_event_info->m_table_map.get_table(m_table_id)) || + !(td= map->create_table_def())) + { + /* Row event for unknown table */ + return; + } + + for (const uchar *value= m_rows_buf; value < m_rows_end; ) + { + size_t length; + print_event_info->row_events++; + + /* Print the first image */ + if (!(length= calc_row_event_length(td, print_event_info, + &m_cols, value))) + break; + value+= length; + DBUG_ASSERT(value <= m_rows_end); + + /* Print the second image (for UPDATE only) */ + if (row_events == 2) + { + if (!(length= calc_row_event_length(td, print_event_info, + &m_cols_ai, value))) + break; + value+= length; + DBUG_ASSERT(value <= m_rows_end); + } + } + delete td; +} + /** 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, + +bool Rows_log_event::print_verbose(IO_CACHE *file, PRINT_EVENT_INFO *print_event_info) { Table_map_log_event *map; - table_def *td; + table_def *td= 0; const char *sql_command, *sql_clause1, *sql_clause2; const char *sql_command_short __attribute__((unused)); Log_event_type general_type_code= get_general_type_code(); @@ -3277,9 +3547,10 @@ void Rows_log_event::print_verbose(IO_CACHE *file, uint8 extra_payload_len= extra_data_len - EXTRA_ROW_INFO_HDR_BYTES; assert(extra_data_len >= EXTRA_ROW_INFO_HDR_BYTES); - my_b_printf(file, "### Extra row data format: %u, len: %u :", - m_extra_row_data[EXTRA_ROW_INFO_FORMAT_OFFSET], - extra_payload_len); + if (my_b_printf(file, "### Extra row data format: %u, len: %u :", + m_extra_row_data[EXTRA_ROW_INFO_FORMAT_OFFSET], + extra_payload_len)) + goto err; if (extra_payload_len) { /* @@ -3290,9 +3561,11 @@ void Rows_log_event::print_verbose(IO_CACHE *file, char buff[buff_len]; str_to_hex(buff, (const char*) &m_extra_row_data[EXTRA_ROW_INFO_HDR_BYTES], extra_payload_len); - my_b_printf(file, "%s", buff); + if (my_b_printf(file, "%s", buff)) + goto err; } - my_b_printf(file, "\n"); + if (my_b_printf(file, "\n")) + goto err; } switch (general_type_code) { @@ -3319,41 +3592,45 @@ void Rows_log_event::print_verbose(IO_CACHE *file, sql_command_short= ""; 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 #%lu", - (ulong) m_table_id); - return; + return (my_b_printf(file, "### Row event for unknown table #%lu", + (ulong) m_table_id)); } /* If the write rows event contained no values for the AI */ if (((general_type_code == WRITE_ROWS_EVENT) && (m_rows_buf==m_rows_end))) { - my_b_printf(file, "### INSERT INTO %`s.%`s VALUES ()\n", - map->get_db_name(), map->get_table_name()); + if (my_b_printf(file, "### INSERT INTO %`s.%`s VALUES ()\n", + map->get_db_name(), map->get_table_name())) + goto err; goto end; } 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_event_info->row_events++; + if (my_b_printf(file, "### %s %`s.%`s\n", + sql_command, + map->get_db_name(), map->get_table_name())) + goto err; #ifdef WHEN_FLASHBACK_REVIEW_READY if (need_flashback_review) - my_b_printf(review_sql, "\nINSERT INTO `%s`.`%s` VALUES ('%s'", - map->get_review_dbname(), map->get_review_tablename(), sql_command_short); + if (my_b_printf(review_sql, "\nINSERT INTO `%s`.`%s` VALUES ('%s'", + map->get_review_dbname(), map->get_review_tablename(), + sql_command_short)) + goto err; #endif /* Print the first image */ if (!(length= print_verbose_one_row(file, td, print_event_info, &m_cols, value, (const uchar*) sql_clause1))) - goto end; + goto err; value+= length; /* Print the second image (for UPDATE only) */ @@ -3362,7 +3639,7 @@ void Rows_log_event::print_verbose(IO_CACHE *file, if (!(length= print_verbose_one_row(file, td, print_event_info, &m_cols_ai, value, (const uchar*) sql_clause2))) - goto end; + goto err; value+= length; } #ifdef WHEN_FLASHBACK_REVIEW_READY @@ -3370,16 +3647,22 @@ void Rows_log_event::print_verbose(IO_CACHE *file, { if (need_flashback_review) for (size_t i= 0; i < td->size(); i ++) - my_b_printf(review_sql, ", NULL"); + if (my_b_printf(review_sql, ", NULL")) + goto err; } if (need_flashback_review) - my_b_printf(review_sql, ")%s\n", print_event_info->delimiter); + if (my_b_printf(review_sql, ")%s\n", print_event_info->delimiter)) + goto err; #endif } end: delete td; + return 0; +err: + delete td; + return 1; } void free_table_map_log_event(Table_map_log_event *event) @@ -3387,7 +3670,7 @@ void free_table_map_log_event(Table_map_log_event *event) delete event; } -void Log_event::print_base64(IO_CACHE* file, +bool Log_event::print_base64(IO_CACHE* file, PRINT_EVENT_INFO* print_event_info, bool more) { @@ -3395,14 +3678,6 @@ void Log_event::print_base64(IO_CACHE* file, uint32 size= uint4korr(ptr + EVENT_LEN_OFFSET); DBUG_ENTER("Log_event::print_base64"); - size_t const tmp_str_sz= my_base64_needed_encoded_length((int) size); - char *const tmp_str= (char *) my_malloc(tmp_str_sz, MYF(MY_WME)); - if (!tmp_str) { - fprintf(stderr, "\nError: Out of memory. " - "Could not print correct binlog event.\n"); - DBUG_VOID_RETURN; - } - if (is_flashback) { uint tmp_size= size; @@ -3448,27 +3723,41 @@ void Log_event::print_base64(IO_CACHE* file, delete ev; } - if (my_base64_encode(ptr, (size_t) size, tmp_str)) + if (print_event_info->base64_output_mode != BASE64_OUTPUT_NEVER && + print_event_info->base64_output_mode != BASE64_OUTPUT_DECODE_ROWS && + ! print_event_info->short_form) { - 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"); + size_t const tmp_str_sz= my_base64_needed_encoded_length((int) size); + bool error= 0; + char *tmp_str; + if (!(tmp_str= (char *) my_malloc(tmp_str_sz, MYF(MY_WME)))) + goto err; - my_b_printf(file, "%s\n", tmp_str); + if (my_base64_encode(ptr, (size_t) size, tmp_str)) + { + DBUG_ASSERT(0); + } - if (!more) - my_b_printf(file, "'%s\n", print_event_info->delimiter); + if (my_b_tell(file) == 0) + if (my_b_write_string(file, "\nBINLOG '\n")) + error= 1; + if (!error && my_b_printf(file, "%s\n", tmp_str)) + error= 1; + if (!more && !error) + if (my_b_printf(file, "'%s\n", print_event_info->delimiter)) + error= 1; + my_free(tmp_str); + if (error) + goto err; } #ifdef WHEN_FLASHBACK_REVIEW_READY - if (print_event_info->verbose || need_flashback_review) + if (print_event_info->verbose || print_event_info->print_row_count || + need_flashback_review) #else // Flashback need the table_map to parse the event - if (print_event_info->verbose || is_flashback) + if (print_event_info->verbose || print_event_info->print_row_count || + is_flashback) #endif { Rows_log_event *ev= NULL; @@ -3543,27 +3832,49 @@ void Log_event::print_base64(IO_CACHE* file, if (ev) { + bool error= 0; + #ifdef WHEN_FLASHBACK_REVIEW_READY ev->need_flashback_review= need_flashback_review; if (print_event_info->verbose) - ev->print_verbose(file, print_event_info); + { + if (ev->print_verbose(file, print_event_info)) + goto err; + } else { IO_CACHE tmp_cache; - open_cached_file(&tmp_cache, NULL, NULL, 0, MYF(MY_WME | MY_NABP)); - ev->print_verbose(&tmp_cache, print_event_info); + + if (open_cached_file(&tmp_cache, NULL, NULL, 0, + MYF(MY_WME | MY_NABP))) + { + delete ev; + goto err; + } + + error= ev->print_verbose(&tmp_cache, print_event_info); close_cached_file(&tmp_cache); + if (error) + { + delete ev; + goto err; + } } #else if (print_event_info->verbose) - ev->print_verbose(file, print_event_info); + error= ev->print_verbose(file, print_event_info); + else + ev->count_row_events(print_event_info); #endif delete ev; + if (error) + goto err; } } + DBUG_RETURN(0); - my_free(tmp_str); - DBUG_VOID_RETURN; +err: + DBUG_RETURN(1); } @@ -3571,7 +3882,7 @@ void Log_event::print_base64(IO_CACHE* file, Log_event::print_timestamp() */ -void Log_event::print_timestamp(IO_CACHE* file, time_t* ts) +bool Log_event::print_timestamp(IO_CACHE* file, time_t* ts) { struct tm *res; time_t my_when= when; @@ -3580,14 +3891,13 @@ void Log_event::print_timestamp(IO_CACHE* file, time_t* ts) ts = &my_when; res=localtime(ts); - my_b_printf(file,"%02d%02d%02d %2d:%02d:%02d", - res->tm_year % 100, - res->tm_mon+1, - res->tm_mday, - res->tm_hour, - res->tm_min, - res->tm_sec); - DBUG_VOID_RETURN; + DBUG_RETURN(my_b_printf(file,"%02d%02d%02d %2d:%02d:%02d", + res->tm_year % 100, + res->tm_mon+1, + res->tm_mday, + res->tm_hour, + res->tm_min, + res->tm_sec)); } #endif /* MYSQL_CLIENT */ @@ -4746,7 +5056,7 @@ Query_log_event::begin_event(String *packet, ulong ev_offset, @todo print the catalog ?? */ -void Query_log_event::print_query_header(IO_CACHE* file, +bool Query_log_event::print_query_header(IO_CACHE* file, PRINT_EVENT_INFO* print_event_info) { // TODO: print the catalog ?? @@ -4756,10 +5066,12 @@ void Query_log_event::print_query_header(IO_CACHE* file, if (!print_event_info->short_form) { - print_header(file, print_event_info, FALSE); - my_b_printf(file, "\t%s\tthread_id=%lu\texec_time=%lu\terror_code=%d\n", - get_type_str(), (ulong) thread_id, (ulong) exec_time, - error_code); + if (print_header(file, print_event_info, FALSE) || + my_b_printf(file, + "\t%s\tthread_id=%lu\texec_time=%lu\terror_code=%d\n", + get_type_str(), (ulong) thread_id, (ulong) exec_time, + error_code)) + goto err; } if ((flags & LOG_EVENT_SUPPRESS_USE_F)) @@ -4773,7 +5085,8 @@ void Query_log_event::print_query_header(IO_CACHE* file, if (different_db) memcpy(print_event_info->db, db, db_len + 1); if (db[0] && different_db) - my_b_printf(file, "use %`s%s\n", db, print_event_info->delimiter); + if (my_b_printf(file, "use %`s%s\n", db, print_event_info->delimiter)) + goto err; } end=int10_to_str((long) when, strmov(buff,"SET TIMESTAMP="),10); @@ -4784,15 +5097,17 @@ void Query_log_event::print_query_header(IO_CACHE* file, } end= strmov(end, print_event_info->delimiter); *end++='\n'; - my_b_write(file, (uchar*) buff, (uint) (end-buff)); + if (my_b_write(file, (uchar*) buff, (uint) (end-buff))) + goto err; if ((!print_event_info->thread_id_printed || ((flags & LOG_EVENT_THREAD_SPECIFIC_F) && thread_id != print_event_info->thread_id))) { // If --short-form, print deterministic value instead of pseudo_thread_id. - my_b_printf(file,"SET @@session.pseudo_thread_id=%lu%s\n", - short_form ? 999999999 : (ulong)thread_id, - print_event_info->delimiter); + if (my_b_printf(file,"SET @@session.pseudo_thread_id=%lu%s\n", + short_form ? 999999999 : (ulong)thread_id, + print_event_info->delimiter)) + goto err; print_event_info->thread_id= thread_id; print_event_info->thread_id_printed= 1; } @@ -4817,18 +5132,20 @@ void Query_log_event::print_query_header(IO_CACHE* file, if (unlikely(tmp)) /* some bits have changed */ { bool need_comma= 0; - my_b_write_string(file, "SET "); - print_set_option(file, tmp, OPTION_NO_FOREIGN_KEY_CHECKS, ~flags2, - "@@session.foreign_key_checks", &need_comma); - print_set_option(file, tmp, OPTION_AUTO_IS_NULL, flags2, - "@@session.sql_auto_is_null", &need_comma); - print_set_option(file, tmp, OPTION_RELAXED_UNIQUE_CHECKS, ~flags2, - "@@session.unique_checks", &need_comma); - print_set_option(file, tmp, OPTION_NOT_AUTOCOMMIT, ~flags2, - "@@session.autocommit", &need_comma); - print_set_option(file, tmp, OPTION_NO_CHECK_CONSTRAINT_CHECKS, ~flags2, - "@@session.check_constraint_checks", &need_comma); - my_b_printf(file,"%s\n", print_event_info->delimiter); + if (my_b_write_string(file, "SET ") || + print_set_option(file, tmp, OPTION_NO_FOREIGN_KEY_CHECKS, ~flags2, + "@@session.foreign_key_checks", &need_comma)|| + print_set_option(file, tmp, OPTION_AUTO_IS_NULL, flags2, + "@@session.sql_auto_is_null", &need_comma) || + print_set_option(file, tmp, OPTION_RELAXED_UNIQUE_CHECKS, ~flags2, + "@@session.unique_checks", &need_comma) || + print_set_option(file, tmp, OPTION_NOT_AUTOCOMMIT, ~flags2, + "@@session.autocommit", &need_comma) || + print_set_option(file, tmp, OPTION_NO_CHECK_CONSTRAINT_CHECKS, + ~flags2, + "@@session.check_constraint_checks", &need_comma) || + my_b_printf(file,"%s\n", print_event_info->delimiter)) + goto err; print_event_info->flags2= flags2; } } @@ -4851,17 +5168,19 @@ void Query_log_event::print_query_header(IO_CACHE* file, !print_event_info->sql_mode_inited))) { char llbuff[22]; - my_b_printf(file,"SET @@session.sql_mode=%s%s\n", - ullstr(sql_mode, llbuff), print_event_info->delimiter); + if (my_b_printf(file,"SET @@session.sql_mode=%s%s\n", + ullstr(sql_mode, llbuff), print_event_info->delimiter)) + goto err; print_event_info->sql_mode= sql_mode; print_event_info->sql_mode_inited= 1; } if (print_event_info->auto_increment_increment != auto_increment_increment || print_event_info->auto_increment_offset != auto_increment_offset) { - my_b_printf(file,"SET @@session.auto_increment_increment=%lu, @@session.auto_increment_offset=%lu%s\n", - auto_increment_increment,auto_increment_offset, - print_event_info->delimiter); + if (my_b_printf(file,"SET @@session.auto_increment_increment=%lu, @@session.auto_increment_offset=%lu%s\n", + auto_increment_increment,auto_increment_offset, + print_event_info->delimiter)) + goto err; print_event_info->auto_increment_increment= auto_increment_increment; print_event_info->auto_increment_offset= auto_increment_offset; } @@ -4876,18 +5195,20 @@ void Query_log_event::print_query_header(IO_CACHE* file, if (cs_info) { /* for mysql client */ - my_b_printf(file, "/*!\\C %s */%s\n", - cs_info->csname, print_event_info->delimiter); - } - my_b_printf(file,"SET " - "@@session.character_set_client=%d," - "@@session.collation_connection=%d," - "@@session.collation_server=%d" - "%s\n", - uint2korr(charset), - uint2korr(charset+2), - uint2korr(charset+4), - print_event_info->delimiter); + if (my_b_printf(file, "/*!\\C %s */%s\n", + cs_info->csname, print_event_info->delimiter)) + goto err; + } + if (my_b_printf(file,"SET " + "@@session.character_set_client=%d," + "@@session.collation_connection=%d," + "@@session.collation_server=%d" + "%s\n", + uint2korr(charset), + uint2korr(charset+2), + uint2korr(charset+4), + print_event_info->delimiter)) + goto err; memcpy(print_event_info->charset, charset, 6); print_event_info->charset_inited= 1; } @@ -4896,31 +5217,40 @@ void Query_log_event::print_query_header(IO_CACHE* file, if (memcmp(print_event_info->time_zone_str, time_zone_str, time_zone_len+1)) { - my_b_printf(file,"SET @@session.time_zone='%s'%s\n", - time_zone_str, print_event_info->delimiter); + if (my_b_printf(file,"SET @@session.time_zone='%s'%s\n", + time_zone_str, print_event_info->delimiter)) + goto err; memcpy(print_event_info->time_zone_str, time_zone_str, time_zone_len+1); } } if (lc_time_names_number != print_event_info->lc_time_names_number) { - my_b_printf(file, "SET @@session.lc_time_names=%d%s\n", - lc_time_names_number, print_event_info->delimiter); + if (my_b_printf(file, "SET @@session.lc_time_names=%d%s\n", + lc_time_names_number, print_event_info->delimiter)) + goto err; print_event_info->lc_time_names_number= lc_time_names_number; } if (charset_database_number != print_event_info->charset_database_number) { if (charset_database_number) - my_b_printf(file, "SET @@session.collation_database=%d%s\n", - charset_database_number, print_event_info->delimiter); - else - my_b_printf(file, "SET @@session.collation_database=DEFAULT%s\n", - print_event_info->delimiter); + { + if (my_b_printf(file, "SET @@session.collation_database=%d%s\n", + charset_database_number, print_event_info->delimiter)) + goto err; + } + else if (my_b_printf(file, "SET @@session.collation_database=DEFAULT%s\n", + print_event_info->delimiter)) + goto err; print_event_info->charset_database_number= charset_database_number; } + return 0; + +err: + return 1; } -void Query_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) +bool Query_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) { Write_on_release_cache cache(&print_event_info->head_cache, file, 0, this); @@ -4930,25 +5260,32 @@ void Query_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) */ DBUG_EXECUTE_IF ("simulate_file_write_error", {(&cache)->write_pos= (&cache)->write_end- 500;}); - print_query_header(&cache, print_event_info); + if (print_query_header(&cache, print_event_info)) + goto err; if (!is_flashback) { - my_b_write(&cache, (uchar*) query, q_len); - my_b_printf(&cache, "\n%s\n", print_event_info->delimiter); + if (my_b_write(&cache, (uchar*) query, q_len) || + my_b_printf(&cache, "\n%s\n", print_event_info->delimiter)) + goto err; } else // is_flashback == 1 { if (strcmp("BEGIN", query) == 0) { - my_b_write(&cache, (uchar*) "COMMIT", 6); - my_b_printf(&cache, "\n%s\n", print_event_info->delimiter); + if (my_b_write(&cache, (uchar*) "COMMIT", 6) || + my_b_printf(&cache, "\n%s\n", print_event_info->delimiter)) + goto err; } else if (strcmp("COMMIT", query) == 0) { - my_b_write(&cache, (uchar*) "BEGIN", 5); - my_b_printf(&cache, "\n%s\n", print_event_info->delimiter); + if (my_b_write(&cache, (uchar*) "BEGIN", 5) || + my_b_printf(&cache, "\n%s\n", print_event_info->delimiter)) + goto err; } } + return cache.flush_data(); +err: + return 1; } #endif /* MYSQL_CLIENT */ @@ -5559,7 +5896,7 @@ void Start_log_event_v3::pack_info(Protocol *protocol) */ #ifdef MYSQL_CLIENT -void Start_log_event_v3::print(FILE* file, PRINT_EVENT_INFO* print_event_info) +bool Start_log_event_v3::print(FILE* file, PRINT_EVENT_INFO* print_event_info) { DBUG_ENTER("Start_log_event_v3::print"); @@ -5568,16 +5905,21 @@ void Start_log_event_v3::print(FILE* file, PRINT_EVENT_INFO* print_event_info) if (!print_event_info->short_form) { - print_header(&cache, print_event_info, FALSE); - my_b_printf(&cache, "\tStart: binlog v %d, server v %s created ", - binlog_version, server_version); - print_timestamp(&cache); + if (print_header(&cache, print_event_info, FALSE) || + my_b_printf(&cache, "\tStart: binlog v %d, server v %s created ", + binlog_version, server_version) || + print_timestamp(&cache)) + goto err; if (created) - my_b_printf(&cache," at startup"); - my_b_printf(&cache, "\n"); + if (my_b_printf(&cache," at startup")) + goto err; + if (my_b_printf(&cache, "\n")) + goto err; if (flags & LOG_EVENT_BINLOG_IN_USE_F) - my_b_printf(&cache, "# Warning: this binlog is either in use or was not " - "closed properly.\n"); + if (my_b_printf(&cache, + "# Warning: this binlog is either in use or was not " + "closed properly.\n")) + goto err; } if (!is_artificial_event() && created) { @@ -5588,9 +5930,12 @@ void Start_log_event_v3::print(FILE* file, PRINT_EVENT_INFO* print_event_info) and rollback unfinished transaction. Probably this can be done with RESET CONNECTION (syntax to be defined). */ - my_b_printf(&cache,"RESET CONNECTION%s\n", print_event_info->delimiter); + if (my_b_printf(&cache,"RESET CONNECTION%s\n", + print_event_info->delimiter)) + goto err; #else - my_b_printf(&cache,"ROLLBACK%s\n", print_event_info->delimiter); + if (my_b_printf(&cache,"ROLLBACK%s\n", print_event_info->delimiter)) + goto err; #endif } if (temp_buf && @@ -5598,11 +5943,15 @@ void Start_log_event_v3::print(FILE* file, PRINT_EVENT_INFO* print_event_info) !print_event_info->short_form) { if (print_event_info->base64_output_mode != BASE64_OUTPUT_DECODE_ROWS) - my_b_printf(&cache, "BINLOG '\n"); - print_base64(&cache, print_event_info, FALSE); + if (my_b_printf(&cache, "BINLOG '\n")) + goto err; + if (print_base64(&cache, print_event_info, FALSE)) + goto err; print_event_info->printed_fd_event= TRUE; } - DBUG_VOID_RETURN; + DBUG_RETURN(cache.flush_data()); +err: + DBUG_RETURN(1); } #endif /* MYSQL_CLIENT */ @@ -6285,7 +6634,7 @@ int Start_encryption_log_event::do_update_pos(rpl_group_info *rgi) #endif #ifndef MYSQL_SERVER -void Start_encryption_log_event::print(FILE* file, +bool Start_encryption_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) { Write_on_release_cache cache(&print_event_info->head_cache, file); @@ -6297,7 +6646,9 @@ void Start_encryption_log_event::print(FILE* file, buf.append(STRING_WITH_LEN(", nonce: ")); buf.append_hex(nonce, BINLOG_NONCE_LENGTH); buf.append(STRING_WITH_LEN("\n# The rest of the binlog is encrypted!\n")); - my_b_write(&cache, (uchar*)buf.ptr(), buf.length()); + if (my_b_write(&cache, (uchar*)buf.ptr(), buf.length())) + return 1; + return (cache.flush_data()); } #endif /************************************************************************** @@ -6322,7 +6673,7 @@ void Start_encryption_log_event::print(FILE* file, */ #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) -void Load_log_event::print_query(THD *thd, bool need_db, const char *cs, +bool Load_log_event::print_query(THD *thd, bool need_db, const char *cs, String *buf, my_off_t *fn_start, my_off_t *fn_end, const char *qualify_db) { @@ -6418,6 +6769,7 @@ void Load_log_event::print_query(THD *thd, bool need_db, const char *cs, } buf->append(STRING_WITH_LEN(")")); } + return 0; } @@ -6661,26 +7013,27 @@ err: */ #ifdef MYSQL_CLIENT -void Load_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) +bool Load_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) { - print(file, print_event_info, 0); + return print(file, print_event_info, 0); } -void Load_log_event::print(FILE* file_arg, PRINT_EVENT_INFO* print_event_info, +bool Load_log_event::print(FILE* file_arg, PRINT_EVENT_INFO* print_event_info, bool commented) { Write_on_release_cache cache(&print_event_info->head_cache, file_arg); - + bool different_db= 1; DBUG_ENTER("Load_log_event::print"); + if (!print_event_info->short_form) { - print_header(&cache, print_event_info, FALSE); - my_b_printf(&cache, "\tQuery\tthread_id=%ld\texec_time=%ld\n", - thread_id, exec_time); + if (print_header(&cache, print_event_info, FALSE) || + my_b_printf(&cache, "\tQuery\tthread_id=%ld\texec_time=%ld\n", + thread_id, exec_time)) + goto err; } - bool different_db= 1; if (db) { /* @@ -6693,69 +7046,86 @@ void Load_log_event::print(FILE* file_arg, PRINT_EVENT_INFO* print_event_info, !commented) memcpy(print_event_info->db, db, db_len + 1); } - + if (db && db[0] && different_db) - my_b_printf(&cache, "%suse %`s%s\n", - commented ? "# " : "", - db, print_event_info->delimiter); + if (my_b_printf(&cache, "%suse %`s%s\n", + commented ? "# " : "", + db, print_event_info->delimiter)) + goto err; if (flags & LOG_EVENT_THREAD_SPECIFIC_F) - my_b_printf(&cache,"%sSET @@session.pseudo_thread_id=%lu%s\n", - commented ? "# " : "", (ulong)thread_id, - print_event_info->delimiter); - my_b_printf(&cache, "%sLOAD DATA ", - commented ? "# " : ""); + if (my_b_printf(&cache,"%sSET @@session.pseudo_thread_id=%lu%s\n", + commented ? "# " : "", (ulong)thread_id, + print_event_info->delimiter)) + goto err; + if (my_b_printf(&cache, "%sLOAD DATA ", + commented ? "# " : "")) + goto err; if (check_fname_outside_temp_buf()) - my_b_write_string(&cache, "LOCAL "); - my_b_printf(&cache, "INFILE '%-*s' ", fname_len, fname); + if (my_b_write_string(&cache, "LOCAL ")) + goto err; + if (my_b_printf(&cache, "INFILE '%-*s' ", fname_len, fname)) + goto err; if (sql_ex.opt_flags & REPLACE_FLAG) - my_b_write_string(&cache, "REPLACE "); + { + if (my_b_write_string(&cache, "REPLACE ")) + goto err; + } else if (sql_ex.opt_flags & IGNORE_FLAG) - my_b_write_string(&cache, "IGNORE "); - - my_b_printf(&cache, "INTO TABLE `%s`", table_name); - my_b_write_string(&cache, " FIELDS TERMINATED BY "); - pretty_print_str(&cache, sql_ex.field_term, sql_ex.field_term_len); + if (my_b_write_string(&cache, "IGNORE ")) + goto err; - if (sql_ex.opt_flags & OPT_ENCLOSED_FLAG) - my_b_write_string(&cache, " OPTIONALLY "); - my_b_write_string(&cache, " ENCLOSED BY "); - pretty_print_str(&cache, sql_ex.enclosed, sql_ex.enclosed_len); - - my_b_write_string(&cache, " ESCAPED BY "); - pretty_print_str(&cache, sql_ex.escaped, sql_ex.escaped_len); - - my_b_write_string(&cache, " LINES TERMINATED BY "); - pretty_print_str(&cache, sql_ex.line_term, sql_ex.line_term_len); + if (my_b_printf(&cache, "INTO TABLE `%s`", table_name) || + my_b_write_string(&cache, " FIELDS TERMINATED BY ") || + pretty_print_str(&cache, sql_ex.field_term, sql_ex.field_term_len)) + goto err; + if (sql_ex.opt_flags & OPT_ENCLOSED_FLAG) + if (my_b_write_string(&cache, " OPTIONALLY ")) + goto err; + if (my_b_write_string(&cache, " ENCLOSED BY ") || + pretty_print_str(&cache, sql_ex.enclosed, sql_ex.enclosed_len) || + my_b_write_string(&cache, " ESCAPED BY ") || + pretty_print_str(&cache, sql_ex.escaped, sql_ex.escaped_len) || + my_b_write_string(&cache, " LINES TERMINATED BY ") || + pretty_print_str(&cache, sql_ex.line_term, sql_ex.line_term_len)) + goto err; if (sql_ex.line_start) { - my_b_write_string(&cache," STARTING BY "); - pretty_print_str(&cache, sql_ex.line_start, sql_ex.line_start_len); + if (my_b_write_string(&cache," STARTING BY ") || + pretty_print_str(&cache, sql_ex.line_start, sql_ex.line_start_len)) + goto err; } if ((long) skip_lines > 0) - my_b_printf(&cache, " IGNORE %ld LINES", (long) skip_lines); + if (my_b_printf(&cache, " IGNORE %ld LINES", (long) skip_lines)) + goto err; if (num_fields) { uint i; const char* field = fields; - my_b_write_string(&cache, " ("); + if (my_b_write_string(&cache, " (")) + goto err; for (i = 0; i < num_fields; i++) { if (i) - my_b_write_byte(&cache, ','); - my_b_printf(&cache, "%`s", field); - + if (my_b_write_byte(&cache, ',')) + goto err; + if (my_b_printf(&cache, "%`s", field)) + goto err; field += field_lens[i] + 1; } - my_b_write_byte(&cache, ')'); + if (my_b_write_byte(&cache, ')')) + goto err; } - my_b_printf(&cache, "%s\n", print_event_info->delimiter); - DBUG_VOID_RETURN; + if (my_b_printf(&cache, "%s\n", print_event_info->delimiter)) + goto err; + DBUG_RETURN(cache.flush_data()); +err: + DBUG_RETURN(1); } #endif /* MYSQL_CLIENT */ @@ -7114,19 +7484,25 @@ void Rotate_log_event::pack_info(Protocol *protocol) */ #ifdef MYSQL_CLIENT -void Rotate_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) +bool Rotate_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) { + if (print_event_info->short_form) + return 0; + char buf[22]; Write_on_release_cache cache(&print_event_info->head_cache, file, Write_on_release_cache::FLUSH_F); - - if (print_event_info->short_form) - return; - print_header(&cache, print_event_info, FALSE); - my_b_write_string(&cache, "\tRotate to "); + if (print_header(&cache, print_event_info, FALSE) || + my_b_write_string(&cache, "\tRotate to ")) + goto err; if (new_log_ident) - my_b_write(&cache, (uchar*) new_log_ident, (uint)ident_len); - my_b_printf(&cache, " pos: %s\n", llstr(pos, buf)); + if (my_b_write(&cache, (uchar*) new_log_ident, (uint)ident_len)) + goto err; + if (my_b_printf(&cache, " pos: %s\n", llstr(pos, buf))) + goto err; + return cache.flush_data(); +err: + return 1; } #endif /* MYSQL_CLIENT */ @@ -7331,18 +7707,21 @@ Binlog_checkpoint_log_event::do_shall_skip(rpl_group_info *rgi) #ifdef MYSQL_CLIENT -void Binlog_checkpoint_log_event::print(FILE *file, +bool Binlog_checkpoint_log_event::print(FILE *file, PRINT_EVENT_INFO *print_event_info) { + if (print_event_info->short_form) + return 0; + Write_on_release_cache cache(&print_event_info->head_cache, file, Write_on_release_cache::FLUSH_F); - if (print_event_info->short_form) - return; - print_header(&cache, print_event_info, FALSE); - my_b_write_string(&cache, "\tBinlog checkpoint "); - my_b_write(&cache, (uchar*)binlog_file_name, binlog_file_len); - my_b_write_byte(&cache, '\n'); + if (print_header(&cache, print_event_info, FALSE) || + my_b_write_string(&cache, "\tBinlog checkpoint ") || + my_b_write(&cache, (uchar*)binlog_file_name, binlog_file_len) || + my_b_write_byte(&cache, '\n')) + return 1; + return cache.flush_data(); } #endif /* MYSQL_CLIENT */ @@ -7660,7 +8039,7 @@ Gtid_log_event::do_shall_skip(rpl_group_info *rgi) #else /* !MYSQL_SERVER */ -void +bool Gtid_log_event::print(FILE *file, PRINT_EVENT_INFO *print_event_info) { Write_on_release_cache cache(&print_event_info->head_cache, file, @@ -7672,26 +8051,34 @@ Gtid_log_event::print(FILE *file, PRINT_EVENT_INFO *print_event_info) { print_header(&cache, print_event_info, FALSE); longlong10_to_str(seq_no, buf, 10); - my_b_printf(&cache, "\tGTID %u-%u-%s", domain_id, server_id, buf); + if (my_b_printf(&cache, "\tGTID %u-%u-%s", domain_id, server_id, buf)) + goto err; if (flags2 & FL_GROUP_COMMIT_ID) { longlong10_to_str(commit_id, buf2, 10); - my_b_printf(&cache, " cid=%s", buf2); + if (my_b_printf(&cache, " cid=%s", buf2)) + goto err; } if (flags2 & FL_DDL) - my_b_write_string(&cache, " ddl"); + if (my_b_write_string(&cache, " ddl")) + goto err; if (flags2 & FL_TRANSACTIONAL) - my_b_write_string(&cache, " trans"); + if (my_b_write_string(&cache, " trans")) + goto err; if (flags2 & FL_WAITED) - my_b_write_string(&cache, " waited"); - my_b_printf(&cache, "\n"); + if (my_b_write_string(&cache, " waited")) + goto err; + if (my_b_printf(&cache, "\n")) + goto err; if (!print_event_info->allow_parallel_printed || print_event_info->allow_parallel != !!(flags2 & FL_ALLOW_PARALLEL)) { - my_b_printf(&cache, + if (my_b_printf(&cache, "/*!100101 SET @@session.skip_parallel_replication=%u*/%s\n", - !(flags2 & FL_ALLOW_PARALLEL), print_event_info->delimiter); + !(flags2 & FL_ALLOW_PARALLEL), + print_event_info->delimiter)) + goto err; print_event_info->allow_parallel= !!(flags2 & FL_ALLOW_PARALLEL); print_event_info->allow_parallel_printed= true; } @@ -7699,8 +8086,10 @@ Gtid_log_event::print(FILE *file, PRINT_EVENT_INFO *print_event_info) if (!print_event_info->domain_id_printed || print_event_info->domain_id != domain_id) { - my_b_printf(&cache, "/*!100001 SET @@session.gtid_domain_id=%u*/%s\n", - domain_id, print_event_info->delimiter); + if (my_b_printf(&cache, + "/*!100001 SET @@session.gtid_domain_id=%u*/%s\n", + domain_id, print_event_info->delimiter)) + goto err; print_event_info->domain_id= domain_id; print_event_info->domain_id_printed= true; } @@ -7708,18 +8097,25 @@ Gtid_log_event::print(FILE *file, PRINT_EVENT_INFO *print_event_info) if (!print_event_info->server_id_printed || print_event_info->server_id != server_id) { - my_b_printf(&cache, "/*!100001 SET @@session.server_id=%u*/%s\n", - server_id, print_event_info->delimiter); + if (my_b_printf(&cache, "/*!100001 SET @@session.server_id=%u*/%s\n", + server_id, print_event_info->delimiter)) + goto err; print_event_info->server_id= server_id; print_event_info->server_id_printed= true; } if (!is_flashback) - my_b_printf(&cache, "/*!100001 SET @@session.gtid_seq_no=%s*/%s\n", - buf, print_event_info->delimiter); + if (my_b_printf(&cache, "/*!100001 SET @@session.gtid_seq_no=%s*/%s\n", + buf, print_event_info->delimiter)) + goto err; } if (!(flags2 & FL_STANDALONE)) - my_b_printf(&cache, is_flashback ? "COMMIT\n%s\n" : "BEGIN\n%s\n", print_event_info->delimiter); + if (my_b_printf(&cache, is_flashback ? "COMMIT\n%s\n" : "BEGIN\n%s\n", print_event_info->delimiter)) + goto err; + + return cache.flush_data(); +err: + return 1; } #endif /* MYSQL_SERVER */ @@ -7957,28 +8353,37 @@ Gtid_list_log_event::pack_info(Protocol *protocol) #else /* !MYSQL_SERVER */ -void +bool Gtid_list_log_event::print(FILE *file, PRINT_EVENT_INFO *print_event_info) { - if (!print_event_info->short_form) - { - Write_on_release_cache cache(&print_event_info->head_cache, file, - Write_on_release_cache::FLUSH_F); - char buf[21]; - uint32 i; + if (print_event_info->short_form) + return 0; - print_header(&cache, print_event_info, FALSE); - my_b_printf(&cache, "\tGtid list ["); - for (i= 0; i < count; ++i) - { - longlong10_to_str(list[i].seq_no, buf, 10); - my_b_printf(&cache, "%u-%u-%s", list[i].domain_id, - list[i].server_id, buf); - if (i < count-1) - my_b_printf(&cache, ",\n# "); - } - my_b_printf(&cache, "]\n"); + Write_on_release_cache cache(&print_event_info->head_cache, file, + Write_on_release_cache::FLUSH_F); + char buf[21]; + uint32 i; + + if (print_header(&cache, print_event_info, FALSE) || + my_b_printf(&cache, "\tGtid list [")) + goto err; + + for (i= 0; i < count; ++i) + { + longlong10_to_str(list[i].seq_no, buf, 10); + if (my_b_printf(&cache, "%u-%u-%s", list[i].domain_id, + list[i].server_id, buf)) + goto err; + if (i < count-1) + if (my_b_printf(&cache, ",\n# ")) + goto err; } + if (my_b_printf(&cache, "]\n")) + goto err; + + return cache.flush_data(); +err: + return 1; } #endif /* MYSQL_SERVER */ @@ -8110,7 +8515,7 @@ bool Intvar_log_event::write() */ #ifdef MYSQL_CLIENT -void Intvar_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) +bool Intvar_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) { char llbuff[22]; const char *UNINIT_VAR(msg); @@ -8119,11 +8524,13 @@ void Intvar_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) if (!print_event_info->short_form) { - print_header(&cache, print_event_info, FALSE); - my_b_write_string(&cache, "\tIntvar\n"); + if (print_header(&cache, print_event_info, FALSE) || + my_b_write_string(&cache, "\tIntvar\n")) + goto err; } - my_b_printf(&cache, "SET "); + if (my_b_printf(&cache, "SET ")) + goto err; switch (type) { case LAST_INSERT_ID_EVENT: msg="LAST_INSERT_ID"; @@ -8136,8 +8543,13 @@ void Intvar_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) msg="INVALID_INT"; break; } - my_b_printf(&cache, "%s=%s%s\n", - msg, llstr(val,llbuff), print_event_info->delimiter); + if (my_b_printf(&cache, "%s=%s%s\n", + msg, llstr(val,llbuff), print_event_info->delimiter)) + goto err; + + return cache.flush_data(); +err: + return 1; } #endif @@ -8236,7 +8648,7 @@ bool Rand_log_event::write() #ifdef MYSQL_CLIENT -void Rand_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) +bool Rand_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) { Write_on_release_cache cache(&print_event_info->head_cache, file, Write_on_release_cache::FLUSH_F); @@ -8244,12 +8656,18 @@ void Rand_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) char llbuff[22],llbuff2[22]; if (!print_event_info->short_form) { - print_header(&cache, print_event_info, FALSE); - my_b_write_string(&cache, "\tRand\n"); + if (print_header(&cache, print_event_info, FALSE) || + my_b_write_string(&cache, "\tRand\n")) + goto err; } - my_b_printf(&cache, "SET @@RAND_SEED1=%s, @@RAND_SEED2=%s%s\n", - llstr(seed1, llbuff),llstr(seed2, llbuff2), - print_event_info->delimiter); + if (my_b_printf(&cache, "SET @@RAND_SEED1=%s, @@RAND_SEED2=%s%s\n", + llstr(seed1, llbuff),llstr(seed2, llbuff2), + print_event_info->delimiter)) + goto err; + + return cache.flush_data(); +err: + return 1; } #endif /* MYSQL_CLIENT */ @@ -8361,7 +8779,7 @@ bool Xid_log_event::write() #ifdef MYSQL_CLIENT -void Xid_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) +bool Xid_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) { Write_on_release_cache cache(&print_event_info->head_cache, file, Write_on_release_cache::FLUSH_F, this); @@ -8371,10 +8789,17 @@ void Xid_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) char buf[64]; longlong10_to_str(xid, buf, 10); - print_header(&cache, print_event_info, FALSE); - my_b_printf(&cache, "\tXid = %s\n", buf); + if (print_header(&cache, print_event_info, FALSE) || + my_b_printf(&cache, "\tXid = %s\n", buf)) + goto err; } - my_b_printf(&cache, is_flashback ? "BEGIN%s\n" : "COMMIT%s\n", print_event_info->delimiter); + if (my_b_printf(&cache, is_flashback ? "BEGIN%s\n" : "COMMIT%s\n", + print_event_info->delimiter)) + goto err; + + return cache.flush_data(); +err: + return 1; } #endif /* MYSQL_CLIENT */ @@ -8763,23 +9188,26 @@ bool User_var_log_event::write() */ #ifdef MYSQL_CLIENT -void User_var_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) +bool User_var_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) { Write_on_release_cache cache(&print_event_info->head_cache, file, Write_on_release_cache::FLUSH_F); if (!print_event_info->short_form) { - print_header(&cache, print_event_info, FALSE); - my_b_write_string(&cache, "\tUser_var\n"); + if (print_header(&cache, print_event_info, FALSE) || + my_b_write_string(&cache, "\tUser_var\n")) + goto err; } - my_b_write_string(&cache, "SET @"); - my_b_write_backtick_quote(&cache, name, name_len); + if (my_b_write_string(&cache, "SET @") || + my_b_write_backtick_quote(&cache, name, name_len)) + goto err; if (is_null) { - my_b_printf(&cache, ":=NULL%s\n", print_event_info->delimiter); + if (my_b_printf(&cache, ":=NULL%s\n", print_event_info->delimiter)) + goto err; } else { @@ -8789,13 +9217,17 @@ void User_var_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) char real_buf[FMT_G_BUFSIZE(14)]; float8get(real_val, val); sprintf(real_buf, "%.14g", real_val); - my_b_printf(&cache, ":=%s%s\n", real_buf, print_event_info->delimiter); + if (my_b_printf(&cache, ":=%s%s\n", real_buf, + print_event_info->delimiter)) + goto err; break; case INT_RESULT: char int_buf[22]; longlong10_to_str(uint8korr(val), int_buf, ((flags & User_var_log_event::UNSIGNED_F) ? 10 : -10)); - my_b_printf(&cache, ":=%s%s\n", int_buf, print_event_info->delimiter); + if (my_b_printf(&cache, ":=%s%s\n", int_buf, + print_event_info->delimiter)) + goto err; break; case DECIMAL_RESULT: { @@ -8811,7 +9243,9 @@ void User_var_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) bin2decimal((uchar*) val+2, &dec, precision, scale); decimal2string(&dec, str_buf, &str_len, 0, 0, 0); str_buf[str_len]= 0; - my_b_printf(&cache, ":=%s%s\n", str_buf, print_event_info->delimiter); + if (my_b_printf(&cache, ":=%s%s\n", str_buf, + print_event_info->delimiter)) + goto err; break; } case STRING_RESULT: @@ -8832,11 +9266,12 @@ void User_var_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) */ char *hex_str; CHARSET_INFO *cs; + bool error; // 2 hex digits / byte hex_str= (char *) my_malloc(2 * val_len + 1 + 3, MYF(MY_WME)); if (!hex_str) - return; + goto err; str_to_hex(hex_str, val, val_len); /* For proper behaviour when mysqlbinlog|mysql, we need to explicitly @@ -8845,24 +9280,31 @@ void User_var_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) character set. But there's not much to do about this and it's unlikely. */ if (!(cs= get_charset(charset_number, MYF(0)))) - /* + { /* Generate an unusable command (=> syntax error) is probably the best thing we can do here. */ - my_b_printf(&cache, ":=???%s\n", print_event_info->delimiter); + error= my_b_printf(&cache, ":=???%s\n", print_event_info->delimiter); + } else - my_b_printf(&cache, ":=_%s %s COLLATE `%s`%s\n", - cs->csname, hex_str, cs->name, - print_event_info->delimiter); + error= my_b_printf(&cache, ":=_%s %s COLLATE `%s`%s\n", + cs->csname, hex_str, cs->name, + print_event_info->delimiter); my_free(hex_str); - } + if (error) + goto err; break; + } case ROW_RESULT: default: DBUG_ASSERT(0); - return; + break; } } + + return cache.flush_data(); +err: + return 1; } #endif @@ -8985,19 +9427,25 @@ User_var_log_event::do_shall_skip(rpl_group_info *rgi) #ifdef HAVE_REPLICATION #ifdef MYSQL_CLIENT -void Unknown_log_event::print(FILE* file_arg, PRINT_EVENT_INFO* print_event_info) +bool Unknown_log_event::print(FILE* file_arg, PRINT_EVENT_INFO* print_event_info) { + if (print_event_info->short_form) + return 0; + Write_on_release_cache cache(&print_event_info->head_cache, file_arg); - if (print_event_info->short_form) - return; if (what != ENCRYPTED) { - print_header(&cache, print_event_info, FALSE); - my_b_printf(&cache, "\n# Unknown event\n"); + if (print_header(&cache, print_event_info, FALSE) || + my_b_printf(&cache, "\n# Unknown event\n")) + goto err; } - else - my_b_printf(&cache, "# Encrypted event\n"); + else if (my_b_printf(&cache, "# Encrypted event\n")) + goto err; + + return cache.flush_data(); +err: + return 1; } #endif @@ -9010,16 +9458,18 @@ void Unknown_log_event::print(FILE* file_arg, PRINT_EVENT_INFO* print_event_info */ #ifdef MYSQL_CLIENT -void Stop_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) +bool Stop_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) { + if (print_event_info->short_form) + return 0; + Write_on_release_cache cache(&print_event_info->head_cache, file, Write_on_release_cache::FLUSH_F, this); - if (print_event_info->short_form) - return; - - print_header(&cache, print_event_info, FALSE); - my_b_write_string(&cache, "\tStop\n"); + if (print_header(&cache, print_event_info, FALSE) || + my_b_write_string(&cache, "\tStop\n")) + return 1; + return cache.flush_data(); } #endif /* MYSQL_CLIENT */ @@ -9201,22 +9651,25 @@ Create_file_log_event::Create_file_log_event(const char* buf, uint len, */ #ifdef MYSQL_CLIENT -void Create_file_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info, +bool Create_file_log_event::print(FILE* file, + PRINT_EVENT_INFO* print_event_info, bool enable_local) { - Write_on_release_cache cache(&print_event_info->head_cache, file); - if (print_event_info->short_form) { if (enable_local && check_fname_outside_temp_buf()) - Load_log_event::print(file, print_event_info); - return; + return Load_log_event::print(file, print_event_info); + return 0; } + Write_on_release_cache cache(&print_event_info->head_cache, file); + if (enable_local) { - Load_log_event::print(file, print_event_info, - !check_fname_outside_temp_buf()); + if (Load_log_event::print(file, print_event_info, + !check_fname_outside_temp_buf())) + goto err; + /** reduce the size of io cache so that the write function is called for every call to my_b_printf(). @@ -9228,16 +9681,24 @@ void Create_file_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info That one is for "file_id: etc" below: in mysqlbinlog we want the #, in SHOW BINLOG EVENTS we don't. */ - my_b_write_byte(&cache, '#'); + if (my_b_write_byte(&cache, '#')) + goto err; } - my_b_printf(&cache, " file_id: %d block_len: %d\n", file_id, block_len); + if (my_b_printf(&cache, " file_id: %d block_len: %d\n", file_id, block_len)) + goto err; + + return cache.flush_data(); +err: + return 1; + } -void Create_file_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) +bool Create_file_log_event::print(FILE* file, + PRINT_EVENT_INFO* print_event_info) { - print(file, print_event_info, 0); + return print(file, print_event_info, 0); } #endif /* MYSQL_CLIENT */ @@ -9303,7 +9764,7 @@ int Create_file_log_event::do_apply_event(rpl_group_info *rgi) fname_buf); goto err; } - + // a trick to avoid allocating another buffer fname= fname_buf; fname_len= (uint) (strmov(ext, ".data") - fname); @@ -9318,7 +9779,7 @@ int Create_file_log_event::do_apply_event(rpl_group_info *rgi) } end_io_cache(&file); mysql_file_close(fd, MYF(0)); - + // fname_buf now already has .data, not .info, because we did our trick /* old copy may exist already */ mysql_file_delete(key_file_log_event_data, fname_buf, MYF(0)); @@ -9416,16 +9877,22 @@ bool Append_block_log_event::write() */ #ifdef MYSQL_CLIENT -void Append_block_log_event::print(FILE* file, +bool Append_block_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) { + if (print_event_info->short_form) + return 0; + Write_on_release_cache cache(&print_event_info->head_cache, file); - if (print_event_info->short_form) - return; - print_header(&cache, print_event_info, FALSE); - my_b_printf(&cache, "\n#%s: file_id: %d block_len: %d\n", - get_type_str(), file_id, block_len); + if (print_header(&cache, print_event_info, FALSE) || + my_b_printf(&cache, "\n#%s: file_id: %d block_len: %d\n", + get_type_str(), file_id, block_len)) + goto err; + + return cache.flush_data(); +err: + return 1; } #endif /* MYSQL_CLIENT */ @@ -9575,15 +10042,19 @@ bool Delete_file_log_event::write() */ #ifdef MYSQL_CLIENT -void Delete_file_log_event::print(FILE* file, +bool Delete_file_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) { + if (print_event_info->short_form) + return 0; + Write_on_release_cache cache(&print_event_info->head_cache, file); - if (print_event_info->short_form) - return; - print_header(&cache, print_event_info, FALSE); - my_b_printf(&cache, "\n#Delete_file: file_id=%u\n", file_id); + if (print_header(&cache, print_event_info, FALSE) || + my_b_printf(&cache, "\n#Delete_file: file_id=%u\n", file_id)) + return 1; + + return cache.flush_data(); } #endif /* MYSQL_CLIENT */ @@ -9675,16 +10146,20 @@ bool Execute_load_log_event::write() */ #ifdef MYSQL_CLIENT -void Execute_load_log_event::print(FILE* file, +bool Execute_load_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) { + if (print_event_info->short_form) + return 0; + Write_on_release_cache cache(&print_event_info->head_cache, file); - if (print_event_info->short_form) - return; - print_header(&cache, print_event_info, FALSE); - my_b_printf(&cache, "\n#Exec_load: file_id=%d\n", - file_id); + if (print_header(&cache, print_event_info, FALSE) || + my_b_printf(&cache, "\n#Exec_load: file_id=%d\n", + file_id)) + return 1; + + return cache.flush_data(); } #endif @@ -9906,22 +10381,24 @@ Execute_load_query_log_event::write_post_header_for_derived() #ifdef MYSQL_CLIENT -void Execute_load_query_log_event::print(FILE* file, +bool Execute_load_query_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) { - print(file, print_event_info, 0); + return print(file, print_event_info, 0); } /** Prints the query as LOAD DATA LOCAL and with rewritten filename. */ -void Execute_load_query_log_event::print(FILE* file, +bool Execute_load_query_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info, const char *local_fname) { Write_on_release_cache cache(&print_event_info->head_cache, file); - print_query_header(&cache, print_event_info); + if (print_query_header(&cache, print_event_info)) + goto err; + /** reduce the size of io cache so that the write function is called for every call to my_b_printf(). @@ -9932,24 +10409,33 @@ void Execute_load_query_log_event::print(FILE* file, if (local_fname) { - my_b_write(&cache, (uchar*) query, fn_pos_start); - my_b_write_string(&cache, " LOCAL INFILE "); - pretty_print_str(&cache, local_fname, strlen(local_fname)); + if (my_b_write(&cache, (uchar*) query, fn_pos_start) || + my_b_write_string(&cache, " LOCAL INFILE ") || + pretty_print_str(&cache, local_fname, strlen(local_fname))) + goto err; if (dup_handling == LOAD_DUP_REPLACE) - my_b_write_string(&cache, " REPLACE"); - my_b_write_string(&cache, " INTO"); - my_b_write(&cache, (uchar*) query + fn_pos_end, q_len-fn_pos_end); - my_b_printf(&cache, "\n%s\n", print_event_info->delimiter); + if (my_b_write_string(&cache, " REPLACE")) + goto err; + + if (my_b_write_string(&cache, " INTO") || + my_b_write(&cache, (uchar*) query + fn_pos_end, q_len-fn_pos_end) || + my_b_printf(&cache, "\n%s\n", print_event_info->delimiter)) + goto err; } else { - my_b_write(&cache, (uchar*) query, q_len); - my_b_printf(&cache, "\n%s\n", print_event_info->delimiter); + if (my_b_write(&cache, (uchar*) query, q_len) || + my_b_printf(&cache, "\n%s\n", print_event_info->delimiter)) + goto err; } if (!print_event_info->short_form) my_b_printf(&cache, "# file_id: %d \n", file_id); + + return cache.flush_data(); +err: + return 1; } #endif @@ -10929,7 +11415,7 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi) if (global_system_variables.log_warnings) slave_rows_error_report(WARNING_LEVEL, error, rgi, thd, table, get_type_str(), - RPL_LOG_NAME, (ulong) log_pos); + RPL_LOG_NAME, log_pos); thd->clear_error(1); error= 0; if (idempotent_error == 0) @@ -10981,7 +11467,7 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi) if (global_system_variables.log_warnings) slave_rows_error_report(WARNING_LEVEL, error, rgi, thd, table, get_type_str(), - RPL_LOG_NAME, (ulong) log_pos); + RPL_LOG_NAME, log_pos); thd->clear_error(1); error= 0; } @@ -10992,7 +11478,7 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi) { slave_rows_error_report(ERROR_LEVEL, error, rgi, thd, table, get_type_str(), - RPL_LOG_NAME, (ulong) log_pos); + RPL_LOG_NAME, log_pos); /* @todo We should probably not call reset_current_stmt_binlog_format_row() from here. @@ -11022,7 +11508,7 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi) thd->is_error() ? 0 : error, rgi, thd, table, get_type_str(), - RPL_LOG_NAME, (ulong) log_pos); + RPL_LOG_NAME, log_pos); DBUG_RETURN(error); err: @@ -11262,7 +11748,7 @@ void Rows_log_event::pack_info(Protocol *protocol) #endif #ifdef MYSQL_CLIENT -void Rows_log_event::print_helper(FILE *file, +bool Rows_log_event::print_helper(FILE *file, PRINT_EVENT_INFO *print_event_info, char const *const name) { @@ -11271,33 +11757,51 @@ 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 const last_stmt_event= get_flags(STMT_END_F); if (!print_event_info->short_form) { - bool const last_stmt_event= get_flags(STMT_END_F); print_header(head, print_event_info, !last_stmt_event); - 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); + if (my_b_printf(head, "\t%s: table id %lu%s\n", + name, m_table_id, + last_stmt_event ? " flags: STMT_END_F" : "")) + goto err; } + if (!print_event_info->short_form || print_event_info->print_row_count) + if (print_base64(body, print_event_info, !last_stmt_event)) + goto err; - if (get_flags(STMT_END_F)) + if (last_stmt_event) { - LEX_STRING tmp_str; - - copy_event_cache_to_string_and_reinit(head, &tmp_str); - output_buf.append(&tmp_str); - my_free(tmp_str.str); - copy_event_cache_to_string_and_reinit(body, &tmp_str); - output_buf.append(&tmp_str); - my_free(tmp_str.str); + if (!is_flashback) + { + if (copy_event_cache_to_file_and_reinit(head, file) || + copy_event_cache_to_file_and_reinit(body, file)) + goto err; + } + else + { + LEX_STRING tmp_str; + if (copy_event_cache_to_string_and_reinit(head, &tmp_str)) + return 1; + output_buf.append(&tmp_str); + my_free(tmp_str.str); + if (copy_event_cache_to_string_and_reinit(body, &tmp_str)) + return 1; + 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); - output_buf.append(&tmp_str); - my_free(tmp_str.str); + if (copy_event_cache_to_string_and_reinit(sql, &tmp_str)) + return 1; + output_buf.append(&tmp_str); + my_free(tmp_str.str); #endif + } } + + return 0; +err: + return 1; } #endif @@ -11385,25 +11889,28 @@ void Annotate_rows_log_event::pack_info(Protocol* protocol) #endif #ifdef MYSQL_CLIENT -void Annotate_rows_log_event::print(FILE *file, PRINT_EVENT_INFO *pinfo) +bool Annotate_rows_log_event::print(FILE *file, PRINT_EVENT_INFO *pinfo) { - if (pinfo->short_form) - return; - - print_header(&pinfo->head_cache, pinfo, TRUE); - my_b_printf(&pinfo->head_cache, "\tAnnotate_rows:\n"); - char *pbeg; // beginning of the next line char *pend; // end of the next line uint cnt= 0; // characters counter + if (!pinfo->short_form) + { + if (print_header(&pinfo->head_cache, pinfo, TRUE) || + my_b_printf(&pinfo->head_cache, "\tAnnotate_rows:\n")) + goto err; + } + else if (my_b_printf(&pinfo->head_cache, "# Annotate_rows:\n")) + goto err; + for (pbeg= m_query_txt; ; pbeg= pend) { // skip all \r's and \n's at the beginning of the next line for (;; pbeg++) { if (++cnt > m_query_len) - return; + return 0; if (*pbeg != '\r' && *pbeg != '\n') break; @@ -11416,10 +11923,15 @@ void Annotate_rows_log_event::print(FILE *file, PRINT_EVENT_INFO *pinfo) ; // print next line - my_b_write(&pinfo->head_cache, (const uchar*) "#Q> ", 4); - my_b_write(&pinfo->head_cache, (const uchar*) pbeg, pend - pbeg); - my_b_write(&pinfo->head_cache, (const uchar*) "\n", 1); + if (my_b_write(&pinfo->head_cache, (const uchar*) "#Q> ", 4) || + my_b_write(&pinfo->head_cache, (const uchar*) pbeg, pend - pbeg) || + my_b_write(&pinfo->head_cache, (const uchar*) "\n", 1)) + goto err; } + + return 0; +err: + return 1; } #endif @@ -11716,7 +12228,7 @@ Table_map_log_event::Table_map_log_event(const char *buf, uint event_len, ptr_after_colcnt= ptr_after_colcnt + m_colcnt; bytes_read= (uint) (ptr_after_colcnt - (uchar *)buf); - DBUG_PRINT("info", ("Bytes read: %d.\n", bytes_read)); + DBUG_PRINT("info", ("Bytes read: %d", bytes_read)); if (bytes_read < event_len) { m_field_metadata_size= net_field_length(&ptr_after_colcnt); @@ -12154,19 +12666,29 @@ void Table_map_log_event::pack_info(Protocol *protocol) #ifdef MYSQL_CLIENT -void Table_map_log_event::print(FILE *file, PRINT_EVENT_INFO *print_event_info) +bool Table_map_log_event::print(FILE *file, PRINT_EVENT_INFO *print_event_info) { if (!print_event_info->short_form) { 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, - ((m_flags & TM_BIT_HAS_TRIGGERS_F) ? - " (has triggers)" : "")); - print_base64(&print_event_info->body_cache, print_event_info, TRUE); - copy_event_cache_to_file_and_reinit(&print_event_info->head_cache, file); + if (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, + ((m_flags & TM_BIT_HAS_TRIGGERS_F) ? + " (has triggers)" : ""))) + goto err; + } + if (!print_event_info->short_form || print_event_info->print_row_count) + { + if (print_base64(&print_event_info->body_cache, print_event_info, TRUE) || + copy_event_cache_to_file_and_reinit(&print_event_info->head_cache, + file)) + goto err; } + + return 0; +err: + return 1; } #endif @@ -12747,14 +13269,14 @@ Write_rows_log_event::do_exec_row(rpl_group_info *rgi) #endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */ #ifdef MYSQL_CLIENT -void Write_rows_log_event::print(FILE *file, PRINT_EVENT_INFO* print_event_info) +bool Write_rows_log_event::print(FILE *file, PRINT_EVENT_INFO* print_event_info) { DBUG_EXECUTE_IF("simulate_cache_read_error", {DBUG_SET("+d,simulate_my_b_fill_error");}); - Rows_log_event::print_helper(file, print_event_info, is_flashback ? "Delete_rows" : "Write_rows"); + return Rows_log_event::print_helper(file, print_event_info, is_flashback ? "Delete_rows" : "Write_rows"); } -void Write_rows_compressed_log_event::print(FILE *file, +bool Write_rows_compressed_log_event::print(FILE *file, PRINT_EVENT_INFO* print_event_info) { char *new_buf; @@ -12766,14 +13288,20 @@ void Write_rows_compressed_log_event::print(FILE *file, { free_temp_buf(); register_temp_buf(new_buf, true); - Rows_log_event::print_helper(file, print_event_info, - "Write_compressed_rows"); + if (Rows_log_event::print_helper(file, print_event_info, + "Write_compressed_rows")) + goto err; } else { - my_b_printf(&print_event_info->head_cache, - "ERROR: uncompress write_compressed_rows failed\n"); + if (my_b_printf(&print_event_info->head_cache, + "ERROR: uncompress write_compressed_rows failed\n")) + goto err; } + + return 0; +err: + return 1; } #endif @@ -13457,13 +13985,13 @@ int Delete_rows_log_event::do_exec_row(rpl_group_info *rgi) #endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */ #ifdef MYSQL_CLIENT -void Delete_rows_log_event::print(FILE *file, +bool Delete_rows_log_event::print(FILE *file, PRINT_EVENT_INFO* print_event_info) { - Rows_log_event::print_helper(file, print_event_info, is_flashback ? "Write_rows" : "Delete_rows"); + return Rows_log_event::print_helper(file, print_event_info, is_flashback ? "Write_rows" : "Delete_rows"); } -void Delete_rows_compressed_log_event::print(FILE *file, +bool Delete_rows_compressed_log_event::print(FILE *file, PRINT_EVENT_INFO* print_event_info) { char *new_buf; @@ -13475,14 +14003,20 @@ void Delete_rows_compressed_log_event::print(FILE *file, { free_temp_buf(); register_temp_buf(new_buf, true); - Rows_log_event::print_helper(file, print_event_info, - "Delete_compressed_rows"); + if (Rows_log_event::print_helper(file, print_event_info, + "Delete_compressed_rows")) + goto err; } else { - my_b_printf(&print_event_info->head_cache, - "ERROR: uncompress delete_compressed_rows failed\n"); + if (my_b_printf(&print_event_info->head_cache, + "ERROR: uncompress delete_compressed_rows failed\n")) + goto err; } + + return 0; +err: + return 1; } #endif @@ -13726,13 +14260,15 @@ err: #endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */ #ifdef MYSQL_CLIENT -void Update_rows_log_event::print(FILE *file, +bool Update_rows_log_event::print(FILE *file, PRINT_EVENT_INFO* print_event_info) { - Rows_log_event::print_helper(file, print_event_info, "Update_rows"); + return Rows_log_event::print_helper(file, print_event_info, "Update_rows"); } -void Update_rows_compressed_log_event::print(FILE *file, PRINT_EVENT_INFO *print_event_info) +bool +Update_rows_compressed_log_event::print(FILE *file, + PRINT_EVENT_INFO *print_event_info) { char *new_buf; ulong len; @@ -13743,14 +14279,20 @@ void Update_rows_compressed_log_event::print(FILE *file, PRINT_EVENT_INFO *print { free_temp_buf(); register_temp_buf(new_buf, true); - Rows_log_event::print_helper(file, print_event_info, - "Update_compressed_rows"); + if (Rows_log_event::print_helper(file, print_event_info, + "Update_compressed_rows")) + goto err; } else { - my_b_printf(&print_event_info->head_cache, - "ERROR: uncompress update_compressed_rows failed\n"); + if (my_b_printf(&print_event_info->head_cache, + "ERROR: uncompress update_compressed_rows failed\n")) + goto err; } + + return 0; +err: + return 1; } #endif @@ -13889,16 +14431,18 @@ err: #ifdef MYSQL_CLIENT -void -Incident_log_event::print(FILE *file, - PRINT_EVENT_INFO *print_event_info) +bool Incident_log_event::print(FILE *file, + PRINT_EVENT_INFO *print_event_info) { if (print_event_info->short_form) - return; + return 0; Write_on_release_cache cache(&print_event_info->head_cache, file); - print_header(&cache, print_event_info, FALSE); - my_b_printf(&cache, "\n# Incident: %s\nRELOAD DATABASE; # Shall generate syntax error\n", description()); + + if (print_header(&cache, print_event_info, FALSE) || + my_b_printf(&cache, "\n# Incident: %s\nRELOAD DATABASE; # Shall generate syntax error\n", description())) + return 1; + return cache.flush_data(); } #endif @@ -13974,19 +14518,20 @@ void Ignorable_log_event::pack_info(Protocol *protocol) #ifdef MYSQL_CLIENT /* Print for its unrecognized ignorable event */ -void -Ignorable_log_event::print(FILE *file, - PRINT_EVENT_INFO *print_event_info) +bool Ignorable_log_event::print(FILE *file, + PRINT_EVENT_INFO *print_event_info) { if (print_event_info->short_form) - return; + return 0; - print_header(&print_event_info->head_cache, print_event_info, FALSE); - my_b_printf(&print_event_info->head_cache, "\tIgnorable\n"); - my_b_printf(&print_event_info->head_cache, - "# Ignorable event type %d (%s)\n", number, description); - copy_event_cache_to_file_and_reinit(&print_event_info->head_cache, - file); + if (print_header(&print_event_info->head_cache, print_event_info, FALSE) || + my_b_printf(&print_event_info->head_cache, "\tIgnorable\n") || + my_b_printf(&print_event_info->head_cache, + "# Ignorable event type %d (%s)\n", number, description) || + copy_event_cache_to_file_and_reinit(&print_event_info->head_cache, + file)) + return 1; + return 0; } #endif @@ -13998,15 +14543,8 @@ Ignorable_log_event::print(FILE *file, they will always be printed for the first event. */ st_print_event_info::st_print_event_info() - :flags2_inited(0), sql_mode_inited(0), sql_mode(0), - auto_increment_increment(0),auto_increment_offset(0), charset_inited(0), - lc_time_names_number(~0), - charset_database_number(ILLEGAL_CHARSET_INFO_NUMBER), - thread_id(0), thread_id_printed(false), server_id(0), - server_id_printed(false), domain_id(0), domain_id_printed(false), - allow_parallel(true), allow_parallel_printed(false), skip_replication(0), - base64_output_mode(BASE64_OUTPUT_UNSPEC), printed_fd_event(FALSE) { + myf const flags = MYF(MY_WME | MY_NABP); /* Currently we only use static PRINT_EVENT_INFO objects, so zeroed at program's startup, but these explicit bzero() is for the day someone @@ -14017,14 +14555,68 @@ st_print_event_info::st_print_event_info() bzero(time_zone_str, sizeof(time_zone_str)); delimiter[0]= ';'; delimiter[1]= 0; - myf const flags = MYF(MY_WME | MY_NABP); + flags2_inited= 0; + sql_mode_inited= 0; + row_events= 0; + sql_mode= 0; + auto_increment_increment= 0; + auto_increment_offset= 0; + charset_inited= 0; + lc_time_names_number= ~0; + charset_database_number= ILLEGAL_CHARSET_INFO_NUMBER; + thread_id= 0; + server_id= 0; + domain_id= 0; + thread_id_printed= false; + server_id_printed= false; + domain_id_printed= false; + allow_parallel= true; + allow_parallel_printed= false; + found_row_event= false; + print_row_count= false; + short_form= false; + skip_replication= 0; + printed_fd_event=FALSE; + file= 0; + base64_output_mode=BASE64_OUTPUT_UNSPEC; open_cached_file(&head_cache, NULL, NULL, 0, flags); open_cached_file(&body_cache, NULL, NULL, 0, flags); #ifdef WHEN_FLASHBACK_REVIEW_READY open_cached_file(&review_sql_cache, NULL, NULL, 0, flags); #endif } -#endif + + +bool copy_event_cache_to_string_and_reinit(IO_CACHE *cache, LEX_STRING *to) +{ + reinit_io_cache(cache, READ_CACHE, 0L, FALSE, FALSE); + if (cache->end_of_file > SIZE_T_MAX || + !(to->str= (char*) my_malloc((to->length= (size_t)cache->end_of_file), MYF(0)))) + { + perror("Out of memory: can't allocate memory in copy_event_cache_to_string_and_reinit()."); + goto err; + } + if (my_b_read(cache, (uchar*) to->str, to->length)) + { + my_free(to->str); + perror("Can't read data from IO_CACHE"); + return true; + } + reinit_io_cache(cache, WRITE_CACHE, 0, FALSE, TRUE); + return false; + +err: + to->str= 0; + to->length= 0; + return true; +} +#endif /* MYSQL_CLIENT */ + +bool copy_event_cache_to_file_and_reinit(IO_CACHE *cache, FILE *file) +{ + return (my_b_copy_to_file(cache, file) || + reinit_io_cache(cache, WRITE_CACHE, 0, FALSE, TRUE)); +} #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) Heartbeat_log_event::Heartbeat_log_event(const char* buf, uint event_len, @@ -14062,4 +14654,4 @@ bool event_that_should_be_ignored(const char *buf) return 1; return 0; } -#endif +#endif /* MYSQL_SERVER */ diff --git a/sql/log_event.h b/sql/log_event.h index 29cae604678..700301a7f34 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -802,6 +802,8 @@ class Format_description_log_event; class Relay_log_info; class binlog_cache_data; +bool copy_event_cache_to_file_and_reinit(IO_CACHE *cache, FILE *file); + #ifdef MYSQL_CLIENT enum enum_base64_output_mode { BASE64_OUTPUT_NEVER= 0, @@ -813,6 +815,8 @@ enum enum_base64_output_mode { BASE64_OUTPUT_MODE_COUNT }; +bool copy_event_cache_to_string_and_reinit(IO_CACHE *cache, LEX_STRING *to); + /* A structure for mysqlbinlog to know how to print events @@ -832,53 +836,38 @@ typedef struct st_print_event_info that was printed. We cache these so that we don't have to print them if they are unchanged. */ - // TODO: have the last catalog here ?? char db[FN_REFLEN+1]; // TODO: make this a LEX_STRING when thd->db is - bool flags2_inited; - uint32 flags2; - bool sql_mode_inited; - sql_mode_t sql_mode; /* must be same as THD.variables.sql_mode */ - ulong auto_increment_increment, auto_increment_offset; - bool charset_inited; char charset[6]; // 3 variables, each of them storable in 2 bytes char time_zone_str[MAX_TIME_ZONE_NAME_LENGTH]; + char delimiter[16]; + sql_mode_t sql_mode; /* must be same as THD.variables.sql_mode */ + my_thread_id thread_id; + ulonglong row_events; + ulong auto_increment_increment, auto_increment_offset; uint lc_time_names_number; uint charset_database_number; - my_thread_id thread_id; - bool thread_id_printed; + uint verbose; + uint32 flags2; uint32 server_id; - bool server_id_printed; uint32 domain_id; + uint8 common_header_len; + enum_base64_output_mode base64_output_mode; + my_off_t hexdump_from; + + table_mapping m_table_map; + table_mapping m_table_map_ignored; + bool flags2_inited; + bool sql_mode_inited; + bool charset_inited; + bool thread_id_printed; + bool server_id_printed; bool domain_id_printed; bool allow_parallel; bool allow_parallel_printed; - - /* - Track when @@skip_replication changes so we need to output a SET - statement for it. - */ - int skip_replication; - - st_print_event_info(); - - ~st_print_event_info() { - close_cached_file(&head_cache); - close_cached_file(&body_cache); -#ifdef WHEN_FLASHBACK_REVIEW_READY - close_cached_file(&review_sql_cache); -#endif - } - bool init_ok() /* tells if construction was successful */ - { return my_b_inited(&head_cache) && my_b_inited(&body_cache) -#ifdef WHEN_FLASHBACK_REVIEW_READY - && my_b_inited(&review_sql_cache) -#endif - ; } - - + bool found_row_event; + bool print_row_count; /* Settings on how to print the events */ bool short_form; - enum_base64_output_mode base64_output_mode; /* This is set whenever a Format_description_event is printed. Later, when an event is printed in base64, this flag is tested: if @@ -886,13 +875,11 @@ typedef struct st_print_event_info the base64 event, so an error message is generated. */ bool printed_fd_event; - my_off_t hexdump_from; - uint8 common_header_len; - char delimiter[16]; - - uint verbose; - table_mapping m_table_map; - table_mapping m_table_map_ignored; + /* + Track when @@skip_replication changes so we need to output a SET + statement for it. + */ + bool skip_replication; /* These two caches are used by the row-based replication events to @@ -905,6 +892,28 @@ typedef struct st_print_event_info /* Storing the SQL for reviewing */ IO_CACHE review_sql_cache; #endif + FILE *file; + st_print_event_info(); + + ~st_print_event_info() { + close_cached_file(&head_cache); + close_cached_file(&body_cache); +#ifdef WHEN_FLASHBACK_REVIEW_READY + close_cached_file(&review_sql_cache); +#endif + } + bool init_ok() /* tells if construction was successful */ + { return my_b_inited(&head_cache) && my_b_inited(&body_cache) +#ifdef WHEN_FLASHBACK_REVIEW_READY + && my_b_inited(&review_sql_cache) +#endif + ; } + void flush_for_error() + { + if (!copy_event_cache_to_file_and_reinit(&head_cache, file)) + copy_event_cache_to_file_and_reinit(&body_cache, file); + fflush(file); + } } PRINT_EVENT_INFO; #endif @@ -1250,11 +1259,11 @@ public: Log_event() : temp_buf(0), when(0), flags(0) {} ha_checksum crc; /* print*() functions are used by mysqlbinlog */ - virtual void print(FILE* file, PRINT_EVENT_INFO* print_event_info) = 0; - void print_timestamp(IO_CACHE* file, time_t *ts = 0); - void print_header(IO_CACHE* file, PRINT_EVENT_INFO* print_event_info, + virtual bool print(FILE* file, PRINT_EVENT_INFO* print_event_info) = 0; + bool print_timestamp(IO_CACHE* file, time_t *ts = 0); + bool 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 print_base64(IO_CACHE* file, PRINT_EVENT_INFO* print_event_info, bool is_more); #endif /* MYSQL_SERVER */ @@ -2112,8 +2121,8 @@ public: void pack_info(Protocol* protocol); #endif /* HAVE_REPLICATION */ #else - void print_query_header(IO_CACHE* file, PRINT_EVENT_INFO* print_event_info); - void print(FILE* file, PRINT_EVENT_INFO* print_event_info); + bool print_query_header(IO_CACHE* file, PRINT_EVENT_INFO* print_event_info); + bool print(FILE* file, PRINT_EVENT_INFO* print_event_info); #endif Query_log_event(); @@ -2452,7 +2461,7 @@ protected: const Format_description_log_event* description_event); public: - void print_query(THD *thd, bool need_db, const char *cs, String *buf, + bool print_query(THD *thd, bool need_db, const char *cs, String *buf, my_off_t *fn_start, my_off_t *fn_end, const char *qualify_db); my_thread_id thread_id; @@ -2518,8 +2527,8 @@ public: void pack_info(Protocol* protocol); #endif /* HAVE_REPLICATION */ #else - void print(FILE* file, PRINT_EVENT_INFO* print_event_info); - void print(FILE* file, PRINT_EVENT_INFO* print_event_info, bool commented); + bool print(FILE* file, PRINT_EVENT_INFO* print_event_info); + bool print(FILE* file, PRINT_EVENT_INFO* print_event_info, bool commented); #endif /* @@ -2616,7 +2625,7 @@ public: #endif /* HAVE_REPLICATION */ #else Start_log_event_v3() {} - void print(FILE* file, PRINT_EVENT_INFO* print_event_info); + bool print(FILE* file, PRINT_EVENT_INFO* print_event_info); #endif Start_log_event_v3(const char* buf, uint event_len, @@ -2685,7 +2694,7 @@ public: write_data(nonce, BINLOG_NONCE_LENGTH); } #else - void print(FILE* file, PRINT_EVENT_INFO* print_event_info); + bool print(FILE* file, PRINT_EVENT_INFO* print_event_info); #endif Start_encryption_log_event( @@ -2873,7 +2882,7 @@ Intvar_log_event(THD* thd_arg,uchar type_arg, ulonglong val_arg, void pack_info(Protocol* protocol); #endif /* HAVE_REPLICATION */ #else - void print(FILE* file, PRINT_EVENT_INFO* print_event_info); + bool print(FILE* file, PRINT_EVENT_INFO* print_event_info); #endif Intvar_log_event(const char* buf, @@ -2954,7 +2963,7 @@ class Rand_log_event: public Log_event void pack_info(Protocol* protocol); #endif /* HAVE_REPLICATION */ #else - void print(FILE* file, PRINT_EVENT_INFO* print_event_info); + bool print(FILE* file, PRINT_EVENT_INFO* print_event_info); #endif Rand_log_event(const char* buf, @@ -3004,7 +3013,7 @@ class Xid_log_event: public Log_event void pack_info(Protocol* protocol); #endif /* HAVE_REPLICATION */ #else - void print(FILE* file, PRINT_EVENT_INFO* print_event_info); + bool print(FILE* file, PRINT_EVENT_INFO* print_event_info); #endif Xid_log_event(const char* buf, @@ -3066,7 +3075,7 @@ public: } void pack_info(Protocol* protocol); #else - void print(FILE* file, PRINT_EVENT_INFO* print_event_info); + bool print(FILE* file, PRINT_EVENT_INFO* print_event_info); #endif User_var_log_event(const char* buf, uint event_len, @@ -3114,7 +3123,7 @@ public: Stop_log_event() :Log_event() {} #else - void print(FILE* file, PRINT_EVENT_INFO* print_event_info); + bool print(FILE* file, PRINT_EVENT_INFO* print_event_info); #endif Stop_log_event(const char* buf, @@ -3210,7 +3219,7 @@ public: void pack_info(Protocol* protocol); #endif /* HAVE_REPLICATION */ #else - void print(FILE* file, PRINT_EVENT_INFO* print_event_info); + bool print(FILE* file, PRINT_EVENT_INFO* print_event_info); #endif Rotate_log_event(const char* buf, uint event_len, @@ -3250,7 +3259,7 @@ public: void pack_info(Protocol *protocol); #endif #else - void print(FILE *file, PRINT_EVENT_INFO *print_event_info); + bool print(FILE *file, PRINT_EVENT_INFO *print_event_info); #endif Binlog_checkpoint_log_event(const char *buf, uint event_len, const Format_description_log_event *description_event); @@ -3375,7 +3384,7 @@ public: virtual enum_skip_reason do_shall_skip(rpl_group_info *rgi); #endif #else - void print(FILE *file, PRINT_EVENT_INFO *print_event_info); + bool print(FILE *file, PRINT_EVENT_INFO *print_event_info); #endif Gtid_log_event(const char *buf, uint event_len, const Format_description_log_event *description_event); @@ -3489,7 +3498,7 @@ public: void pack_info(Protocol *protocol); #endif #else - void print(FILE *file, PRINT_EVENT_INFO *print_event_info); + bool print(FILE *file, PRINT_EVENT_INFO *print_event_info); #endif Gtid_list_log_event(const char *buf, uint event_len, const Format_description_log_event *description_event); @@ -3553,8 +3562,8 @@ public: void pack_info(Protocol* protocol); #endif /* HAVE_REPLICATION */ #else - void print(FILE* file, PRINT_EVENT_INFO* print_event_info); - void print(FILE* file, PRINT_EVENT_INFO* print_event_info, + bool print(FILE* file, PRINT_EVENT_INFO* print_event_info); + bool print(FILE* file, PRINT_EVENT_INFO* print_event_info, bool enable_local); #endif @@ -3626,7 +3635,7 @@ public: virtual int get_create_or_append() const; #endif /* HAVE_REPLICATION */ #else - void print(FILE* file, PRINT_EVENT_INFO* print_event_info); + bool print(FILE* file, PRINT_EVENT_INFO* print_event_info); #endif Append_block_log_event(const char* buf, uint event_len, @@ -3666,8 +3675,8 @@ public: void pack_info(Protocol* protocol); #endif /* HAVE_REPLICATION */ #else - void print(FILE* file, PRINT_EVENT_INFO* print_event_info); - void print(FILE* file, PRINT_EVENT_INFO* print_event_info, + bool print(FILE* file, PRINT_EVENT_INFO* print_event_info); + bool print(FILE* file, PRINT_EVENT_INFO* print_event_info, bool enable_local); #endif @@ -3707,7 +3716,7 @@ public: void pack_info(Protocol* protocol); #endif /* HAVE_REPLICATION */ #else - void print(FILE* file, PRINT_EVENT_INFO* print_event_info); + bool print(FILE* file, PRINT_EVENT_INFO* print_event_info); #endif Execute_load_log_event(const char* buf, uint event_len, @@ -3803,9 +3812,9 @@ public: void pack_info(Protocol* protocol); #endif /* HAVE_REPLICATION */ #else - void print(FILE* file, PRINT_EVENT_INFO* print_event_info); + bool print(FILE* file, PRINT_EVENT_INFO* print_event_info); /* Prints the query as LOAD DATA LOCAL and with rewritten filename */ - void print(FILE* file, PRINT_EVENT_INFO* print_event_info, + bool print(FILE* file, PRINT_EVENT_INFO* print_event_info, const char *local_fname); #endif Execute_load_query_log_event(const char* buf, uint event_len, @@ -3850,7 +3859,7 @@ public: /* constructor for hopelessly corrupted events */ Unknown_log_event(): Log_event(), what(ENCRYPTED) {} ~Unknown_log_event() {} - void print(FILE* file, PRINT_EVENT_INFO* print_event_info); + bool print(FILE* file, PRINT_EVENT_INFO* print_event_info); Log_event_type get_type_code() { return UNKNOWN_EVENT;} bool is_valid() const { return 1; } }; @@ -3895,7 +3904,7 @@ public: #endif #ifdef MYSQL_CLIENT - virtual void print(FILE*, PRINT_EVENT_INFO*); + virtual bool print(FILE*, PRINT_EVENT_INFO*); #endif #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) @@ -4315,7 +4324,7 @@ public: #endif #ifdef MYSQL_CLIENT - virtual void print(FILE *file, PRINT_EVENT_INFO *print_event_info); + virtual bool print(FILE *file, PRINT_EVENT_INFO *print_event_info); #endif @@ -4436,15 +4445,21 @@ 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; + virtual bool print(FILE *file, PRINT_EVENT_INFO *print_event_info)= 0; void change_to_flashback_event(PRINT_EVENT_INFO *print_event_info, uchar *rows_buff, Log_event_type ev_type); - void print_verbose(IO_CACHE *file, + bool 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, const my_bool no_fill_output= 0); // if no_fill_output=1, then print result is unnecessary + size_t calc_row_event_length(table_def *td, + PRINT_EVENT_INFO *print_event_info, + MY_BITMAP *cols_bitmap, + const uchar *value); + void count_row_events(PRINT_EVENT_INFO *print_event_info); + #endif #ifdef MYSQL_SERVER @@ -4551,7 +4566,7 @@ protected: void uncompress_buf(); #ifdef MYSQL_CLIENT - void print_helper(FILE *, PRINT_EVENT_INFO *, char const *const name); + bool print_helper(FILE *, PRINT_EVENT_INFO *, char const *const name); #endif #ifdef MYSQL_SERVER @@ -4758,7 +4773,7 @@ private: virtual Log_event_type get_general_type_code() { return (Log_event_type)TYPE_CODE; } #ifdef MYSQL_CLIENT - void print(FILE *file, PRINT_EVENT_INFO *print_event_info); + bool print(FILE *file, PRINT_EVENT_INFO *print_event_info); #endif #if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION) @@ -4782,7 +4797,7 @@ public: #endif private: #if defined(MYSQL_CLIENT) - void print(FILE *file, PRINT_EVENT_INFO *print_event_info); + bool print(FILE *file, PRINT_EVENT_INFO *print_event_info); #endif }; @@ -4846,7 +4861,7 @@ protected: virtual Log_event_type get_general_type_code() { return (Log_event_type)TYPE_CODE; } #ifdef MYSQL_CLIENT - void print(FILE *file, PRINT_EVENT_INFO *print_event_info); + bool print(FILE *file, PRINT_EVENT_INFO *print_event_info); #endif #if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION) @@ -4870,7 +4885,7 @@ public: #endif private: #if defined(MYSQL_CLIENT) - void print(FILE *file, PRINT_EVENT_INFO *print_event_info); + bool print(FILE *file, PRINT_EVENT_INFO *print_event_info); #endif }; @@ -4931,7 +4946,7 @@ protected: virtual Log_event_type get_general_type_code() { return (Log_event_type)TYPE_CODE; } #ifdef MYSQL_CLIENT - void print(FILE *file, PRINT_EVENT_INFO *print_event_info); + bool print(FILE *file, PRINT_EVENT_INFO *print_event_info); #endif #if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION) @@ -4954,7 +4969,7 @@ public: #endif private: #if defined(MYSQL_CLIENT) - void print(FILE *file, PRINT_EVENT_INFO *print_event_info); + bool print(FILE *file, PRINT_EVENT_INFO *print_event_info); #endif }; @@ -5048,7 +5063,7 @@ public: virtual ~Incident_log_event(); #ifdef MYSQL_CLIENT - virtual void print(FILE *file, PRINT_EVENT_INFO *print_event_info); + virtual bool print(FILE *file, PRINT_EVENT_INFO *print_event_info); #endif #if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION) @@ -5115,7 +5130,7 @@ public: #endif #ifdef MYSQL_CLIENT - virtual void print(FILE *file, PRINT_EVENT_INFO *print_event_info); + virtual bool print(FILE *file, PRINT_EVENT_INFO *print_event_info); #endif virtual Log_event_type get_type_code() { return IGNORABLE_LOG_EVENT; } @@ -5125,38 +5140,6 @@ public: virtual int get_data_size() { return IGNORABLE_HEADER_LEN; } }; - -static inline bool copy_event_cache_to_string_and_reinit(IO_CACHE *cache, LEX_STRING *to) -{ - String tmp; - - reinit_io_cache(cache, READ_CACHE, 0L, FALSE, FALSE); - if (tmp.append(cache, (uint32)cache->end_of_file)) - goto err; - reinit_io_cache(cache, WRITE_CACHE, 0, FALSE, TRUE); - - /* - Can't change the order, because the String::release() will clear the - length. - */ - to->length= tmp.length(); - to->str= tmp.release(); - - return false; - -err: - perror("Out of memory: can't allocate memory in copy_event_cache_to_string_and_reinit()."); - return true; -} - -static inline bool copy_event_cache_to_file_and_reinit(IO_CACHE *cache, - FILE *file) -{ - return - my_b_copy_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 2c079a34d56..20986050203 100644 --- a/sql/log_event_old.cc +++ b/sql/log_event_old.cc @@ -1845,7 +1845,7 @@ void Old_rows_log_event::pack_info(Protocol *protocol) #ifdef MYSQL_CLIENT -void Old_rows_log_event::print_helper(FILE *file, +bool Old_rows_log_event::print_helper(FILE *file, PRINT_EVENT_INFO *print_event_info, char const *const name) { @@ -1854,18 +1854,23 @@ void Old_rows_log_event::print_helper(FILE *file, if (!print_event_info->short_form) { bool const last_stmt_event= get_flags(STMT_END_F); - print_header(head, print_event_info, !last_stmt_event); - 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); + if (print_header(head, print_event_info, !last_stmt_event) || + 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)) + goto err; } if (get_flags(STMT_END_F)) { - copy_event_cache_to_file_and_reinit(head, file); - copy_event_cache_to_file_and_reinit(body, file); + if (copy_event_cache_to_file_and_reinit(head, file) || + copy_event_cache_to_file_and_reinit(body, file)) + goto err; } + return 0; +err: + return 1; } #endif @@ -2491,10 +2496,11 @@ Write_rows_log_event_old::do_exec_row(rpl_group_info *rgi) #ifdef MYSQL_CLIENT -void Write_rows_log_event_old::print(FILE *file, +bool Write_rows_log_event_old::print(FILE *file, PRINT_EVENT_INFO* print_event_info) { - Old_rows_log_event::print_helper(file, print_event_info, "Write_rows_old"); + return Old_rows_log_event::print_helper(file, print_event_info, + "Write_rows_old"); } #endif @@ -2598,10 +2604,11 @@ int Delete_rows_log_event_old::do_exec_row(rpl_group_info *rgi) #ifdef MYSQL_CLIENT -void Delete_rows_log_event_old::print(FILE *file, +bool Delete_rows_log_event_old::print(FILE *file, PRINT_EVENT_INFO* print_event_info) { - Old_rows_log_event::print_helper(file, print_event_info, "Delete_rows_old"); + return Old_rows_log_event::print_helper(file, print_event_info, + "Delete_rows_old"); } #endif @@ -2736,9 +2743,10 @@ Update_rows_log_event_old::do_exec_row(rpl_group_info *rgi) #ifdef MYSQL_CLIENT -void Update_rows_log_event_old::print(FILE *file, +bool Update_rows_log_event_old::print(FILE *file, PRINT_EVENT_INFO* print_event_info) { - Old_rows_log_event::print_helper(file, print_event_info, "Update_rows_old"); + return Old_rows_log_event::print_helper(file, print_event_info, + "Update_rows_old"); } #endif diff --git a/sql/log_event_old.h b/sql/log_event_old.h index 40e01d37318..d18c980bdfe 100644 --- a/sql/log_event_old.h +++ b/sql/log_event_old.h @@ -116,7 +116,7 @@ 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; + virtual bool print(FILE *file, PRINT_EVENT_INFO *print_event_info)= 0; #endif #ifndef MYSQL_CLIENT @@ -166,7 +166,7 @@ protected: const Format_description_log_event *description_event); #ifdef MYSQL_CLIENT - void print_helper(FILE *, PRINT_EVENT_INFO *, char const *const name); + bool print_helper(FILE *, PRINT_EVENT_INFO *, char const *const name); #endif #ifndef MYSQL_CLIENT @@ -379,7 +379,7 @@ public: private: #ifdef MYSQL_CLIENT - void print(FILE *file, PRINT_EVENT_INFO *print_event_info); + bool print(FILE *file, PRINT_EVENT_INFO *print_event_info); #endif #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) @@ -455,7 +455,7 @@ public: protected: #ifdef MYSQL_CLIENT - void print(FILE *file, PRINT_EVENT_INFO *print_event_info); + bool print(FILE *file, PRINT_EVENT_INFO *print_event_info); #endif #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) @@ -529,7 +529,7 @@ public: protected: #ifdef MYSQL_CLIENT - void print(FILE *file, PRINT_EVENT_INFO *print_event_info); + bool print(FILE *file, PRINT_EVENT_INFO *print_event_info); #endif #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) diff --git a/sql/mdl.cc b/sql/mdl.cc index 4efc8b0e361..bf708c945f4 100644 --- a/sql/mdl.cc +++ b/sql/mdl.cc @@ -20,7 +20,7 @@ #include "sql_array.h" #include "rpl_rli.h" #include <lf.h> -#include <mysqld_error.h> +#include "unireg.h" #include <mysql/plugin.h> #include <mysql/service_thd_wait.h> #include <mysql/psi/mysql_stage.h> diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 15dfcfdb649..c0d55306871 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -3683,8 +3683,6 @@ void my_message_sql(uint error, const char *str, myf MyFlags) extern "C" void *my_str_malloc_mysqld(size_t size); -extern "C" void my_str_free_mysqld(void *ptr); -extern "C" void *my_str_realloc_mysqld(void *ptr, size_t size); void *my_str_malloc_mysqld(size_t size) { @@ -3692,17 +3690,6 @@ void *my_str_malloc_mysqld(size_t size) } -void my_str_free_mysqld(void *ptr) -{ - my_free(ptr); -} - -void *my_str_realloc_mysqld(void *ptr, size_t size) -{ - return my_realloc(ptr, size, MYF(MY_FAE)); -} - - #ifdef __WIN__ pthread_handler_t handle_shutdown(void *arg) @@ -3758,14 +3745,8 @@ check_enough_stack_size(int recurse_level) } -/* - Initialize my_str_malloc() and my_str_free() -*/ static void init_libstrings() { - my_str_malloc= &my_str_malloc_mysqld; - my_str_free= &my_str_free_mysqld; - my_str_realloc= &my_str_realloc_mysqld; #ifndef EMBEDDED_LIBRARY my_string_stack_guard= check_enough_stack_size; #endif @@ -3776,7 +3757,7 @@ ulonglong my_pcre_frame_size; static void init_pcre() { pcre_malloc= pcre_stack_malloc= my_str_malloc_mysqld; - pcre_free= pcre_stack_free= my_str_free_mysqld; + pcre_free= pcre_stack_free= my_free; pcre_stack_guard= check_enough_stack_size_slow; /* See http://pcre.org/original/doc/html/pcrestack.html */ my_pcre_frame_size= -pcre_exec(NULL, NULL, NULL, -999, -999, 0, NULL, 0); @@ -4735,6 +4716,8 @@ static int init_common_variables() return 1; } + global_system_variables.in_subquery_conversion_threshold= IN_SUBQUERY_CONVERSION_THRESHOLD; + return 0; } @@ -7367,7 +7350,7 @@ struct my_option my_long_options[]= "The value has to be a multiple of 256.", &opt_binlog_rows_event_max_size, &opt_binlog_rows_event_max_size, 0, GET_ULONG, REQUIRED_ARG, - /* def_value */ 8192, /* min_value */ 256, /* max_value */ ULONG_MAX, + /* def_value */ 8192, /* min_value */ 256, /* max_value */ UINT_MAX32-1, /* sub_size */ 0, /* block_size */ 256, /* app_type */ 0 }, diff --git a/sql/opt_range.cc b/sql/opt_range.cc index fba449e7ce2..369729127dd 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -1541,7 +1541,7 @@ end: head->file= org_file; /* Restore head->read_set (and write_set) to what they had before the call */ - head->column_bitmaps_set(save_read_set, save_write_set); + head->column_bitmaps_set(save_read_set, save_write_set, save_vcol_set); if (reset()) { @@ -6738,7 +6738,7 @@ static TRP_RANGE *get_key_scans_params(PARAM *param, SEL_TREE *tree, bool update_tbl_stats, double read_time) { - uint idx, best_idx; + uint idx, UNINIT_VAR(best_idx); SEL_ARG *key_to_read= NULL; ha_rows UNINIT_VAR(best_records); /* protected by key_to_read */ uint UNINIT_VAR(best_mrr_flags), /* protected by key_to_read */ @@ -10273,8 +10273,8 @@ void SEL_ARG::test_use_count(SEL_ARG *root) ulong count=count_key_part_usage(root,pos->next_key_part); if (count > pos->next_key_part->use_count) { - sql_print_information("Use_count: Wrong count for key at %p, %lu " - "should be %lu", (long unsigned int)pos, + sql_print_information("Use_count: Wrong count for key at %p: %lu " + "should be %lu", pos, pos->next_key_part->use_count, count); return; } @@ -10283,7 +10283,7 @@ void SEL_ARG::test_use_count(SEL_ARG *root) } if (e_count != elements) sql_print_warning("Wrong use count: %u (should be %u) for tree at %p", - e_count, elements, (long unsigned int) this); + e_count, elements, this); } #endif @@ -11396,7 +11396,10 @@ int QUICK_RANGE_SELECT::reset() buf_size/= 2; } if (!mrr_buf_desc) - DBUG_RETURN(HA_ERR_OUT_OF_MEM); + { + error= HA_ERR_OUT_OF_MEM; + goto err; + } /* Initialize the handler buffer. */ mrr_buf_desc->buffer= mrange_buff; diff --git a/sql/opt_split.cc b/sql/opt_split.cc new file mode 100644 index 00000000000..ac2972f4264 --- /dev/null +++ b/sql/opt_split.cc @@ -0,0 +1,1134 @@ +/* + Copyright (c) 2017 MariaDB + + 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +/* + This file contains functions to support the splitting technique. + This optimization technique can be applied to equi-joins involving + materialized tables such as materialized views, materialized derived tables + and materialized CTEs. The technique also could be applied to materialized + semi-joins though the code below does not support this usage yet. + + Here are the main ideas behind this technique that we'll call SM optimization + (SplitMaterialization). + + Consider the query + SELECT t1.a, t.min + FROM t1, (SELECT t2.a, MIN(t2.b) as min FROM t2 GROUP BY t2.a) t + WHERE t1.a = t.a and t1.b < const + + Re-write the query into + SELECT t1.a, t.min + FROM t1, LATERAL (SELECT t2.a, MIN(t2.b) as min + FROM t2 WHERE t2.a = t1.a GROUP BY t2.a) t + WHERE t1.b < const + + The execution of the original query (Q1) does the following: + 1. Executes the query in the specification of the derived table + and puts the result set into a temporary table with an index + on the first column. + 2. Joins t1 with the temporary table using the its index. + + The execution of the transformed query (Q1R) follows these steps: + 1. For each row of t1 where t1.b < const a temporary table + containing all rows of of t2 with t2.a = t1.a is created + 2. If there are any rows in the temporary table aggregation + is performed for them + 3. The result of the aggregation is joined with t1. + + The second execution can win if: + a) There is an efficient way to select rows of t2 for which t2.a = t1.a + (For example if there is an index on t2.a) + and + b) The number of temporary tables created for partitions + is much smaller that the total number of partitions + + It should be noted that for the transformed query aggregation + for a partition may be performed several times. + + As we can see the optimization basically splits table t2 into + partitions and performs aggregation over each of them + independently. + + If we have only one equi-join condition then we either push it as + for Q1R or we don't. In a general case we may have much more options. + Consider the query (Q3) + SELECT + FROM t1,t2 (SELECT t3.a, t3.b, MIN(t3.c) as min + FROM t3 GROUP BY a,b) t + WHERE t.a = t1.a AND t.b = t2.b + AND t1.c < c1 and t2.c < c2 + AND P(t1,t2); + (P(t1,t2) designates some additional conditions over columns of t1,t2). + + Assuming that there indexes on t3(a,b) and t3(b) here we have several + reasonable options to push equi-join conditions into the derived. + All these options should be taken into account when the optimizer + evaluates different join orders. When the join order (t1,t,t2) is + evaluated there is only one way of splitting : to push the condition + t.a = t1.a into t. With the join order (t2,t,t1) only the condition + t.b = t2.b can be pushed. When the join orders (t1,t2,t) and (t2,t1,t) + are evaluated then the optimizer should consider pushing t.a = t1.a, + t.b = t2.b and (t.a = t1.a AND t.b = t2.b) to choose the best condition + for splitting. Apparently here last condition is the best one because + it provides the miximum possible number of partitions. + + If we dropped the index on t3(a,b) and created the index on t3(a) instead + then we would have two options for splitting: to push t.a = t1.a or to + push t.b = t2.b. If the selectivity of the index t3(a) is better than + the selectivity of t3(b) then the first option is preferred. + + Although the condition (t.a = t1.a AND t.b = t2.b) provides a better + splitting than the condition t.a = t1.a the latter will be used for + splitting if the execution plan with the join order (t1,t,t2) turns out + to be the cheapest one. It's quite possible when the join condition + P(t1,t2) has a bad selectivity. + + Whenever the optimizer evaluates the cost of using a splitting it + compares it with the cost of materialization without splitting. + + If we just drop the index on t3(a,b) the chances that the splitting + will be used becomes much lower but they still exists providing that + the fanout of the partial join of t1 and t2 is small enough. +*/ + +/* + Splitting can be applied to a materialized table specified by the query + with post-join operations that require partitioning of the result set produced + by the join expression used in the FROM clause the query such as GROUP BY + operation and window function operation. In any of these cases the post-join + operation can be executed independently for any partition only over the rows + of this partition. Also if the set of all partitions is divided into disjoint + subsets the operation can applied to each subset independently. In this case + all rows are first partitioned into the groups each of which contains all the + rows from the partitions belonging the same subset and then each group + is subpartitioned into groups in the the post join operation. + + The set of all rows belonging to the union of several partitions is called + here superpartition. If a grouping operation is defined by the list + e_1,...,e_n then any set S = {e_i1,...,e_ik} can be used to devide all rows + into superpartions such that for any two rows r1, r2 the following holds: + e_ij(r1) = e_ij(r2) for each e_ij from S. We use the splitting technique + only if S consists of references to colums of the joined tables. + For example if the GROUP BY list looks like this a, g(b), c we can consider + applying the splitting technique to the superpartitions defined by {a,c}, + {a}, {c} (a and c here may be the references to the columns from different + tables). +*/ + + /* + The following describes when and how the optimizer decides whether it + makes sense to employ the splitting technique. + + 1. For each instance of a materialized table (derived/view/CTE) it is + checked that it is potentially splittable. Now it is done right after the + execution plan for the select specifying this table has been chosen. + + 2. Any potentially splittable materialized table T is subject to two-phase + optimization. It means that the optimizer first builds the best execution + plan for join that specifies T. Then the control is passed back to the + optimization process of the embedding select Q. After the execution plan + for Q has been chosen the optimizer finishes the optimization of the join + specifying T. + + 3. When the optimizer builds the container with the KEYUSE structures + for the join of embedding select it detects the equi-join conditions + PC that potentially could be pushed into a potentially splittable + materialized table T. The collected information about such conditions + is stored together with other facts on potential splittings for table T. + + 4. When the optimizer starts looking for the best execution plan for the + embedding select Q for each potentially splittable materialized table T + it creates special KEYUSE structures for pushable equi-join conditions + PC. These structures are used to add new elements to the container + of KEYUSE structures built for T. The specifics of these elements is + that they can be ebabled and disabled during the process of choosing + the best plan for Q. + + 5. When the optimizer extends a partial join order with a potentially + splittable materialized table T (in function best_access_path) it + first evaluates a new execution plan for the modified specification + of T that adds all equi-join conditions that can be pushed with + current join prefix to the WHERE conditions of the original + specification of T. If the cost of the new plan is better than the + the cost of the original materialized table then the optimizer + prefers to use splitting for the current join prefix. As the cost + of the plan depends only on the pushed conditions it makes sense + to cache this plan for other prefixes. + + 6. The optimizer takes into account the cost of splitting / materialization + of a potentially splittable materialized table T as a startup cost + to access table T. + + 7. When the optimizer finally chooses the best execution plan for + the embedding select Q and this plan prefers using splitting + for table T with pushed equi-join conditions PC then the execution + plan for the underlying join with these conditions is chosen for T. +*/ + +/* + The implementation of the splitting technique below allows to apply + the technique only to a materialized derived table / view / CTE whose + specification is either a select with GROUP BY or a non-grouping select + with window functions that share the same PARTITION BY list. +*/ + +#include "mariadb.h" +#include "sql_select.h" + +/* Info on a splitting field */ +struct SplM_field_info +{ + /* Splitting field in the materialized table T */ + Field *mat_field; + /* The item from the select list of the specification of T */ + Item *producing_item; + /* The corresponding splitting field from the specification of T */ + Field *underlying_field; +}; + + +/* Info on the splitting execution plan saved in SplM_opt_info::cache */ +struct SplM_plan_info +{ + /* The cached splitting execution plan P */ + struct st_position *best_positions; + /* The cost of the above plan */ + double cost; + /* Selectivity of splitting used in P */ + double split_sel; + /* For fast search of KEYUSE_EXT elements used for splitting in P */ + struct KEYUSE_EXT *keyuse_ext_start; + /* The tables that contains the fields used for splitting in P */ + TABLE *table; + /* The number of the key from 'table' used for splitting in P */ + uint key; + /* Number of the components of 'key' used for splitting in P */ + uint parts; +}; + + +/* + The structure contains the information that is used by the optimizer + for potentially splittable materialization of T that is a materialized + derived_table / view / CTE +*/ +class SplM_opt_info : public Sql_alloc +{ +public: + /* The join for the select specifying T */ + JOIN *join; + /* The map of tables from 'join' whose columns can be used for partitioning */ + table_map tables_usable_for_splitting; + /* Info about the fields of the joined tables usable for splitting */ + SplM_field_info *spl_fields; + /* The number of elements in the above list */ + uint spl_field_cnt; + /* Contains the structures to generate all KEYUSEs for pushable equalities */ + List<KEY_FIELD> added_key_fields; + /* The cache of evaluated execution plans for 'join' with pushed equalities */ + List<SplM_plan_info> plan_cache; + /* Cost of best execution plan for join when nothing is pushed */ + double unsplit_cost; + /* Cardinality of T when nothing is pushed */ + double unsplit_card; + /* Lastly evaluated execution plan for 'join' with pushed equalities */ + SplM_plan_info *last_plan; + + SplM_plan_info *find_plan(TABLE *table, uint key, uint parts); +}; + + +void TABLE::set_spl_opt_info(SplM_opt_info *spl_info) +{ + if (spl_info) + spl_info->join->spl_opt_info= spl_info; + spl_opt_info= spl_info; +} + + +void TABLE::deny_splitting() +{ + DBUG_ASSERT(spl_opt_info != NULL); + spl_opt_info->join->spl_opt_info= NULL; + spl_opt_info= NULL; +} + + +/* This structure is auxiliary and used only in the function that follows it */ +struct SplM_field_ext_info: public SplM_field_info +{ + uint item_no; + bool is_usable_for_ref_access; +}; + + +/** + @brief + Check whether this join is one for potentially splittable materialized table + + @details + The function checks whether this join is for select that specifies + a potentially splittable materialized table T. If so, the collected + info on potential splittability of T is attached to the field spl_opt_info + of the TABLE structure for T. + + The function returns a positive answer if the following holds: + 1. the optimizer switch 'split_materialized' is set 'on' + 2. the select owning this join specifies a materialized derived/view/cte T + 3. this is the only select in the specification of T + 4. condition pushdown is not prohibited into T + 5. T is not recursive + 6. not all of this join are constant or optimized away + 7. T is either + 7.1. a grouping table with GROUP BY list P + or + 7.2. a non-grouping table with window functions over the same non-empty + partition specified by the PARTITION BY list P + 8. P contains some references on the columns of the joined tables C + occurred also in the select list of this join + 9. There are defined some keys usable for ref access of fields from C + with available statistics. + + @retval + true if the answer is positive + false otherwise +*/ + +bool JOIN::check_for_splittable_materialized() +{ + ORDER *partition_list= 0; + st_select_lex_unit *unit= select_lex->master_unit(); + TABLE_LIST *derived= unit->derived; + if (!(optimizer_flag(thd, OPTIMIZER_SWITCH_SPLIT_MATERIALIZED)) || // !(1) + !(derived && derived->is_materialized_derived()) || // !(2) + (unit->first_select()->next_select()) || // !(3) + (derived->prohibit_cond_pushdown) || // !(4) + (derived->is_recursive_with_table()) || // !(5) + (table_count == 0 || const_tables == top_join_tab_count)) // !(6) + return false; + if (group_list) // (7.1) + { + if (!select_lex->have_window_funcs()) + partition_list= group_list; + } + else if (select_lex->have_window_funcs() && + select_lex->window_specs.elements == 1) // (7.2) + { + partition_list= + select_lex->window_specs.head()->partition_list->first; + } + if (!partition_list) + return false; + + ORDER *ord; + Dynamic_array<SplM_field_ext_info> candidates; + + /* + Select from partition_list all candidates for splitting. + A candidate must be + - field item or refer to such (8.1) + - item mentioned in the select list (8.2) + Put info about such candidates into the array candidates + */ + table_map usable_tables= 0; // tables that contains the candidate + for (ord= partition_list; ord; ord= ord->next) + { + Item *ord_item= *ord->item; + if (ord_item->real_item()->type() != Item::FIELD_ITEM) // !(8.1) + continue; + + Field *ord_field= ((Item_field *) (ord_item->real_item()))->field; + + JOIN_TAB *tab= ord_field->table->reginfo.join_tab; + if (tab->is_inner_table_of_outer_join()) + continue; + + List_iterator<Item> li(fields_list); + Item *item; + uint item_no= 0; + while ((item= li++)) + { + if ((*ord->item)->eq(item, 0)) // (8.2) + { + SplM_field_ext_info new_elem; + new_elem.producing_item= item; + new_elem.item_no= item_no; + new_elem.mat_field= derived->table->field[item_no]; + new_elem.underlying_field= ord_field; + new_elem.is_usable_for_ref_access= false; + candidates.push(new_elem); + usable_tables|= ord_field->table->map; + break; + } + item_no++; + } + } + if (candidates.elements() == 0) // no candidates satisfying (8.1) && (8.2) + return false; + + /* + For each table from this join find the keys that can be used for ref access + of the fields mentioned in the 'array candidates' + */ + + SplM_field_ext_info *cand; + SplM_field_ext_info *cand_start= &candidates.at(0); + SplM_field_ext_info *cand_end= cand_start + candidates.elements(); + + for (JOIN_TAB *tab= join_tab; + tab < join_tab + top_join_tab_count; tab++) + { + TABLE *table= tab->table; + if (!(table->map & usable_tables)) + continue; + + table->keys_usable_for_splitting.clear_all(); + uint i; + for (i= 0; i < table->s->keys; i++) + { + if (!table->keys_in_use_for_query.is_set(i)) + continue; + KEY *key_info= table->key_info + i; + uint key_parts= table->actual_n_key_parts(key_info); + uint usable_kp_cnt= 0; + for ( ; usable_kp_cnt < key_parts; usable_kp_cnt++) + { + if (key_info->actual_rec_per_key(usable_kp_cnt) == 0) + break; + int fldnr= key_info->key_part[usable_kp_cnt].fieldnr; + + for (cand= cand_start; cand < cand_end; cand++) + { + if (cand->underlying_field->field_index + 1 == fldnr) + { + cand->is_usable_for_ref_access= true; + break; + } + } + if (cand == cand_end) + break; + } + if (usable_kp_cnt) + table->keys_usable_for_splitting.set_bit(i); + } + } + + /* Count the candidate fields that can be accessed by ref */ + uint spl_field_cnt= candidates.elements(); + for (cand= cand_start; cand < cand_end; cand++) + { + if (!cand->is_usable_for_ref_access) + spl_field_cnt--; + } + + if (!spl_field_cnt) // No candidate field can be accessed by ref => !(9) + return false; + + /* + Create a structure of the type SplM_opt_info and fill it with + the collected info on potential splittability of T + */ + SplM_opt_info *spl_opt_info= new (thd->mem_root) SplM_opt_info(); + SplM_field_info *spl_field= + (SplM_field_info *) (thd->calloc(sizeof(SplM_field_info) * + spl_field_cnt)); + + if (!(spl_opt_info && spl_field)) // consider T as not good for splitting + return false; + + spl_opt_info->join= this; + spl_opt_info->tables_usable_for_splitting= 0; + spl_opt_info->spl_field_cnt= spl_field_cnt; + spl_opt_info->spl_fields= spl_field; + for (cand= cand_start; cand < cand_end; cand++) + { + if (!cand->is_usable_for_ref_access) + continue; + spl_field->producing_item= cand->producing_item; + spl_field->underlying_field= cand->underlying_field; + spl_field->mat_field= cand->mat_field; + spl_opt_info->tables_usable_for_splitting|= + cand->underlying_field->table->map; + spl_field++; + } + + /* Attach this info to the table T */ + derived->table->set_spl_opt_info(spl_opt_info); + + return true; +} + + +/** + @brief + Collect info on KEY_FIELD usable for splitting + + @param + key_field KEY_FIELD to collect info on + + @details + The function assumes that this table is potentially splittable. + The function checks whether the KEY_FIELD structure key_field built for + this table was created for a splitting field f. If so, the function does + the following using info from key_field: + 1. Builds an equality of the form f = key_field->val that could be + pushed into this table. + 2. Creates a new KEY_FIELD structure for this equality and stores + a reference to this structure in this->spl_opt_info. +*/ + +void TABLE::add_splitting_info_for_key_field(KEY_FIELD *key_field) +{ + DBUG_ASSERT(spl_opt_info != NULL); + JOIN *join= spl_opt_info->join; + Field *field= key_field->field; + SplM_field_info *spl_field= spl_opt_info->spl_fields; + uint i= spl_opt_info->spl_field_cnt; + for ( ; i; i--, spl_field++) + { + if (spl_field->mat_field == field) + break; + } + if (!i) // field is not usable for splitting + return; + + /* + Any equality condition that can be potentially pushed into the + materialized derived table is constructed now though later it may turn out + that it is not needed, because it is not used for splitting. + The reason for this is that the failure to construct it when it has to be + injected causes denial for further processing of the query. + Formally this equality is needed in the KEY_FIELD structure constructed + here that will be used to generate additional keyuses usable for splitting. + However key_field.cond could be used for this purpose (see implementations + of virtual function can_optimize_keypart_ref()). + + The condition is built in such a form that it can be added to the WHERE + condition of the select that specifies this table. + */ + THD *thd= in_use; + Item *left_item= spl_field->producing_item->build_clone(thd); + Item *right_item= key_field->val->build_clone(thd); + Item_func_eq *eq_item= 0; + if (left_item && right_item) + { + right_item->walk(&Item::set_fields_as_dependent_processor, + false, join->select_lex); + right_item->update_used_tables(); + eq_item= new (thd->mem_root) Item_func_eq(thd, left_item, right_item); + } + if (!eq_item) + return; + KEY_FIELD *added_key_field= + (KEY_FIELD *) thd->alloc(sizeof(KEY_FIELD)); + if (!added_key_field || + spl_opt_info->added_key_fields.push_back(added_key_field,thd->mem_root)) + return; + added_key_field->field= spl_field->underlying_field; + added_key_field->cond= eq_item; + added_key_field->val= key_field->val; + added_key_field->level= 0; + added_key_field->optimize= KEY_OPTIMIZE_EQ; + added_key_field->eq_func= true; + added_key_field->null_rejecting= true; + added_key_field->cond_guard= NULL; + added_key_field->sj_pred_no= UINT_MAX; + return; +} + + +static bool +add_ext_keyuse_for_splitting(Dynamic_array<KEYUSE_EXT> *ext_keyuses, + KEY_FIELD *added_key_field, uint key, uint part) +{ + KEYUSE_EXT keyuse_ext; + Field *field= added_key_field->field; + + JOIN_TAB *tab=field->table->reginfo.join_tab; + key_map possible_keys=field->get_possible_keys(); + possible_keys.intersect(field->table->keys_usable_for_splitting); + tab->keys.merge(possible_keys); + + Item_func_eq *eq_item= (Item_func_eq *) (added_key_field->cond); + keyuse_ext.table= field->table; + keyuse_ext.val= eq_item->arguments()[1]; + keyuse_ext.key= key; + keyuse_ext.keypart=part; + keyuse_ext.keypart_map= (key_part_map) 1 << part; + keyuse_ext.used_tables= keyuse_ext.val->used_tables(); + keyuse_ext.optimize= added_key_field->optimize & KEY_OPTIMIZE_REF_OR_NULL; + keyuse_ext.ref_table_rows= 0; + keyuse_ext.null_rejecting= added_key_field->null_rejecting; + keyuse_ext.cond_guard= added_key_field->cond_guard; + keyuse_ext.sj_pred_no= added_key_field->sj_pred_no; + keyuse_ext.validity_ref= 0; + keyuse_ext.needed_in_prefix= added_key_field->val->used_tables(); + keyuse_ext.validity_var= false; + return ext_keyuses->push(keyuse_ext); +} + + +static int +sort_ext_keyuse(KEYUSE_EXT *a, KEYUSE_EXT *b) +{ + if (a->table->tablenr != b->table->tablenr) + return (int) (a->table->tablenr - b->table->tablenr); + if (a->key != b->key) + return (int) (a->key - b->key); + return (int) (a->keypart - b->keypart); +} + + +static void +sort_ext_keyuses(Dynamic_array<KEYUSE_EXT> *keyuses) +{ + KEYUSE_EXT *first_keyuse= &keyuses->at(0); + my_qsort(first_keyuse, keyuses->elements(), sizeof(KEYUSE_EXT), + (qsort_cmp) sort_ext_keyuse); +} + + +/** + @brief + Add info on keyuses usable for splitting into an array +*/ + +static bool +add_ext_keyuses_for_splitting_field(Dynamic_array<KEYUSE_EXT> *ext_keyuses, + KEY_FIELD *added_key_field) +{ + Field *field= added_key_field->field; + TABLE *table= field->table; + for (uint key= 0; key < table->s->keys; key++) + { + if (!(table->keys_usable_for_splitting.is_set(key))) + continue; + KEY *key_info= table->key_info + key; + uint key_parts= table->actual_n_key_parts(key_info); + KEY_PART_INFO *key_part_info= key_info->key_part; + for (uint part=0; part < key_parts; part++, key_part_info++) + { + if (!field->eq(key_part_info->field)) + continue; + if (add_ext_keyuse_for_splitting(ext_keyuses, added_key_field, key, part)) + return true; + } + } + return false; +} + + +/* + @brief + Cost of the post join operation used in specification of splittable table +*/ + +static +double spl_postjoin_oper_cost(THD *thd, double join_record_count, uint rec_len) +{ + double cost; + cost= get_tmp_table_write_cost(thd, join_record_count,rec_len) * + join_record_count; // cost to fill tmp table + cost+= get_tmp_table_lookup_cost(thd, join_record_count,rec_len) * + join_record_count; // cost to perform post join operation used here + cost+= get_tmp_table_lookup_cost(thd, join_record_count, rec_len) + + join_record_count * log2 (join_record_count) * + SORT_INDEX_CMP_COST; // cost to perform sorting + return cost; +} + +/** + @brief + Add KEYUSE structures that can be usable for splitting + + @details + This function is called only for joins created for potentially + splittable materialized tables. The function does the following: + 1. Creates the dynamic array ext_keyuses_for_splitting of KEYUSE_EXT + structures and fills is with info about all keyuses that + could be used for splitting. + 2. Sort the array ext_keyuses_for_splitting for fast access by key + on certain columns. + 3. Collects and stores cost and cardinality info on the best execution + plan that does not use splitting and save this plan together with + corresponding array of keyuses. + 4. Expand this array with KEYUSE elements built from the info stored + in ext_keyuses_for_splitting that could be produced by pushed + equalities employed for splitting. + 5. Prepare the extended array of keyuses to be used in the function + best_access_plan() +*/ + +void JOIN::add_keyuses_for_splitting() +{ + uint i; + uint idx; + KEYUSE_EXT *keyuse_ext; + KEYUSE_EXT keyuse_ext_end; + double oper_cost; + uint rec_len; + uint added_keyuse_count; + TABLE *table= select_lex->master_unit()->derived->table; + List_iterator_fast<KEY_FIELD> li(spl_opt_info->added_key_fields); + KEY_FIELD *added_key_field; + if (!spl_opt_info->added_key_fields.elements) + goto err; + if (!(ext_keyuses_for_splitting= new Dynamic_array<KEYUSE_EXT>)) + goto err; + while ((added_key_field= li++)) + { + (void) add_ext_keyuses_for_splitting_field(ext_keyuses_for_splitting, + added_key_field); + } + added_keyuse_count= ext_keyuses_for_splitting->elements(); + if (!added_keyuse_count) + goto err; + sort_ext_keyuses(ext_keyuses_for_splitting); + bzero((char*) &keyuse_ext_end, sizeof(keyuse_ext_end)); + if (ext_keyuses_for_splitting->push(keyuse_ext_end)) + goto err; + + spl_opt_info->unsplit_card= join_record_count; + + rec_len= table->s->rec_buff_length; + + oper_cost= spl_postjoin_oper_cost(thd, join_record_count, rec_len); + + spl_opt_info->unsplit_cost= best_positions[table_count-1].read_time + + oper_cost; + + if (!(save_qep= new Join_plan_state(table_count + 1))) + goto err; + + save_query_plan(save_qep); + + if (!keyuse.buffer && + my_init_dynamic_array(&keyuse, sizeof(KEYUSE), 20, 64, + MYF(MY_THREAD_SPECIFIC))) + goto err; + + if (allocate_dynamic(&keyuse, + save_qep->keyuse.elements + + added_keyuse_count)) + goto err; + + memcpy(keyuse.buffer, + save_qep->keyuse.buffer, + (size_t) save_qep->keyuse.elements * keyuse.size_of_element); + keyuse.elements= save_qep->keyuse.elements; + + keyuse_ext= &ext_keyuses_for_splitting->at(0); + idx= save_qep->keyuse.elements; + for (i=0; i < added_keyuse_count; i++, keyuse_ext++, idx++) + { + set_dynamic(&keyuse, (KEYUSE *) keyuse_ext, idx); + KEYUSE *added_keyuse= ((KEYUSE *) (keyuse.buffer)) + idx; + added_keyuse->validity_ref= &keyuse_ext->validity_var; + } + + if (sort_and_filter_keyuse(thd, &keyuse, true)) + goto err; + optimize_keyuse(this, &keyuse); + + for (uint i= 0; i < table_count; i++) + { + JOIN_TAB *tab= join_tab + i; + map2table[tab->table->tablenr]= tab; + } + + return; + +err: + if (save_qep) + restore_query_plan(save_qep); + table->deny_splitting(); + return; +} + + +/** + @brief + Add KEYUSE structures that can be usable for splitting of this joined table +*/ + +void JOIN_TAB::add_keyuses_for_splitting() +{ + DBUG_ASSERT(table->spl_opt_info != NULL); + SplM_opt_info *spl_opt_info= table->spl_opt_info; + spl_opt_info->join->add_keyuses_for_splitting(); +} + + +/* + @brief + Find info on the splitting plan by the splitting key +*/ + +SplM_plan_info *SplM_opt_info::find_plan(TABLE *table, uint key, uint parts) +{ + List_iterator_fast<SplM_plan_info> li(plan_cache); + SplM_plan_info *spl_plan; + while ((spl_plan= li++)) + { + if (spl_plan->table == table && + spl_plan->key == key && + spl_plan->parts == parts) + break; + } + return spl_plan; +} + + +/* + @breaf + Enable/Disable a keyuses that can be used for splitting + */ + +static +void reset_validity_vars_for_keyuses(KEYUSE_EXT *key_keyuse_ext_start, + TABLE *table, uint key, + table_map remaining_tables, + bool validity_val) +{ + KEYUSE_EXT *keyuse_ext= key_keyuse_ext_start; + do + { + if (!(keyuse_ext->needed_in_prefix & remaining_tables)) + { + /* + The enabling/disabling flags are set just in KEYUSE_EXT structures. + Yet keyuses that are used by best_access_path() have pointers + to these flags. + */ + keyuse_ext->validity_var= validity_val; + } + keyuse_ext++; + } + while (keyuse_ext->key == key && keyuse_ext->table == table); +} + + +/** + @brief + Choose the best splitting to extend the evaluated partial join + + @param + record_count estimated cardinality of the extended partial join + remaining_tables tables not joined yet + + @details + This function is called during the search for the best execution + plan of the join that contains this table T. The function is called + every time when the optimizer tries to extend a partial join by + joining it with table T. Depending on what tables are already in the + partial join different equalities usable for splitting can be pushed + into T. The function evaluates different variants and chooses the + best one. Then the function finds the plan for the materializing join + with the chosen equality conditions pushed into it. If the cost of the + plan turns out to be less than the cost of the best plan without + splitting the function set it as the true plan of materialization + of the table T. + The function caches the found plans for materialization of table T + together if the info what key was used for splitting. Next time when + the optimizer prefers to use the same key the plan is taken from + the cache of plans + + @retval + Pointer to the info on the found plan that employs the pushed equalities + if the plan has been chosen, NULL - otherwise. +*/ + +SplM_plan_info * JOIN_TAB::choose_best_splitting(double record_count, + table_map remaining_tables) +{ + SplM_opt_info *spl_opt_info= table->spl_opt_info; + DBUG_ASSERT(spl_opt_info != NULL); + JOIN *join= spl_opt_info->join; + THD *thd= join->thd; + table_map tables_usable_for_splitting= + spl_opt_info->tables_usable_for_splitting; + KEYUSE_EXT *keyuse_ext= &join->ext_keyuses_for_splitting->at(0); + KEYUSE_EXT *best_key_keyuse_ext_start; + TABLE *best_table= 0; + double best_rec_per_key= DBL_MAX; + SplM_plan_info *spl_plan= 0; + uint best_key; + uint best_key_parts; + + /* + Check whether there are keys that can be used to join T employing splitting + and if so, select the best out of such keys + */ + for (uint tablenr= 0; tablenr < join->table_count; tablenr++) + { + if (!((1ULL << tablenr) & tables_usable_for_splitting)) + continue; + JOIN_TAB *tab= join->map2table[tablenr]; + TABLE *table= tab->table; + do + { + uint key= keyuse_ext->key; + KEYUSE_EXT *key_keyuse_ext_start= keyuse_ext; + key_part_map found_parts= 0; + do + { + if (keyuse_ext->needed_in_prefix & remaining_tables) + { + keyuse_ext++; + continue; + } + if (!(keyuse_ext->keypart_map & found_parts)) + { + if ((!found_parts && !keyuse_ext->keypart) || + (found_parts && ((keyuse_ext->keypart_map >> 1) & found_parts))) + found_parts|= keyuse_ext->keypart_map; + else + { + do + { + keyuse_ext++; + } + while (keyuse_ext->key == key && keyuse_ext->table == table); + break; + } + } + KEY *key_info= table->key_info + key; + double rec_per_key= + key_info->actual_rec_per_key(keyuse_ext->keypart); + if (rec_per_key < best_rec_per_key) + { + best_table= keyuse_ext->table; + best_key= keyuse_ext->key; + best_key_parts= keyuse_ext->keypart + 1; + best_rec_per_key= rec_per_key; + best_key_keyuse_ext_start= key_keyuse_ext_start; + } + keyuse_ext++; + } + while (keyuse_ext->key == key && keyuse_ext->table == table); + } + while (keyuse_ext->table == table); + } + spl_opt_info->last_plan= 0; + if (best_table) + { + /* + The key for splitting was chosen, look for the plan for this key + in the cache + */ + spl_plan= spl_opt_info->find_plan(best_table, best_key, best_key_parts); + if (!spl_plan && + (spl_plan= (SplM_plan_info *) thd->alloc(sizeof(SplM_plan_info))) && + (spl_plan->best_positions= + (POSITION *) thd->alloc(sizeof(POSITION) * join->table_count)) && + !spl_opt_info->plan_cache.push_back(spl_plan)) + { + /* + The plan for the chosen key has not been found in the cache. + Build a new plan and save info on it in the cache + */ + table_map all_table_map= (1 << join->table_count) - 1; + reset_validity_vars_for_keyuses(best_key_keyuse_ext_start, best_table, + best_key, remaining_tables, true); + choose_plan(join, all_table_map & ~join->const_table_map); + spl_plan->keyuse_ext_start= best_key_keyuse_ext_start; + spl_plan->table= best_table; + spl_plan->key= best_key; + spl_plan->parts= best_key_parts; + spl_plan->split_sel= best_rec_per_key / spl_opt_info->unsplit_card; + + uint rec_len= table->s->rec_buff_length; + + double split_card= spl_opt_info->unsplit_card * spl_plan->split_sel; + double oper_cost= split_card * + spl_postjoin_oper_cost(thd, split_card, rec_len); + spl_plan->cost= join->best_positions[join->table_count-1].read_time + + + oper_cost; + + memcpy((char *) spl_plan->best_positions, + (char *) join->best_positions, + sizeof(POSITION) * join->table_count); + reset_validity_vars_for_keyuses(best_key_keyuse_ext_start, best_table, + best_key, remaining_tables, false); + } + if (spl_plan) + { + if(record_count * spl_plan->cost < spl_opt_info->unsplit_cost) + { + /* + The best plan that employs splitting is cheaper than + the plan without splitting + */ + spl_opt_info->last_plan= spl_plan; + } + } + } + + /* Set the cost of the preferred materialization for this partial join */ + records= (ha_rows)spl_opt_info->unsplit_card; + spl_plan= spl_opt_info->last_plan; + if (spl_plan) + { + startup_cost= record_count * spl_plan->cost; + records= (ha_rows) (records * spl_plan->split_sel); + } + else + startup_cost= spl_opt_info->unsplit_cost; + return spl_plan; +} + + +/** + @brief + Inject equalities for splitting used by the materialization join + + @param + remaining_tables used to filter out the equalities that cannot + be pushed. + + @details + This function is called by JOIN_TAB::fix_splitting that is used + to fix the chosen splitting of a splittable materialized table T + in the final query execution plan. In this plan the table T + is joined just before the 'remaining_tables'. So all equalities + usable for splitting whose right parts do not depend on any of + remaining tables can be pushed into join for T. + The function also marks the select that specifies T as + UNCACHEABLE_DEPENDENT_INJECTED. + + @retval + false on success + true on failure +*/ + +bool JOIN::inject_best_splitting_cond(table_map remaining_tables) +{ + Item *inj_cond= 0; + List<Item> inj_cond_list; + List_iterator<KEY_FIELD> li(spl_opt_info->added_key_fields); + KEY_FIELD *added_key_field; + while ((added_key_field= li++)) + { + if (remaining_tables & added_key_field->val->used_tables()) + continue; + if (inj_cond_list.push_back(added_key_field->cond, thd->mem_root)) + return true; + } + DBUG_ASSERT(inj_cond_list.elements); + switch (inj_cond_list.elements) { + case 1: + inj_cond= inj_cond_list.head(); break; + default: + inj_cond= new (thd->mem_root) Item_cond_and(thd, inj_cond_list); + if (!inj_cond) + return true; + } + if (inj_cond) + inj_cond->fix_fields(thd,0); + + if (inject_cond_into_where(inj_cond)) + return true; + + select_lex->uncacheable|= UNCACHEABLE_DEPENDENT_INJECTED; + st_select_lex_unit *unit= select_lex->master_unit(); + unit->uncacheable|= UNCACHEABLE_DEPENDENT_INJECTED; + + return false; +} + + +/** + @brief + Fix the splitting chosen for a splittable table in the final query plan + + @param + spl_plan info on the splitting plan chosen for the splittable table T + remaining_tables the table T is joined just before these tables + + @details + If in the final query plan the optimizer has chosen a splitting plan + then the function sets this plan as the final execution plan to + materialized the table T. Otherwise the plan that does not use + splitting is set for the materialization. + + @retval + false on success + true on failure +*/ + +bool JOIN_TAB::fix_splitting(SplM_plan_info *spl_plan, + table_map remaining_tables) +{ + SplM_opt_info *spl_opt_info= table->spl_opt_info; + DBUG_ASSERT(table->spl_opt_info != 0); + JOIN *md_join= spl_opt_info->join; + if (spl_plan) + { + memcpy((char *) md_join->best_positions, + (char *) spl_plan->best_positions, + sizeof(POSITION) * md_join->table_count); + if (md_join->inject_best_splitting_cond(remaining_tables)) + return true; + /* + This is called for a proper work of JOIN::get_best_combination() + called for the join that materializes T + */ + reset_validity_vars_for_keyuses(spl_plan->keyuse_ext_start, + spl_plan->table, + spl_plan->key, + remaining_tables, + true); + } + else + { + md_join->restore_query_plan(md_join->save_qep); + } + return false; +} + + +/** + @brief + Fix the splittings chosen splittable tables in the final query plan + + @details + The function calls JOIN_TAB::fix_splittins for all potentially + splittable tables in this join to set all final materialization + plans chosen for these tables. + + @retval + false on success + true on failure +*/ + +bool JOIN::fix_all_splittings_in_plan() +{ + table_map prev_tables= 0; + table_map all_tables= (1 << table_count) - 1; + for (uint tablenr=0 ; tablenr < table_count ; tablenr++) + { + POSITION *cur_pos= &best_positions[tablenr]; + JOIN_TAB *tab= cur_pos->table; + if (tab->table->is_splittable()) + { + SplM_plan_info *spl_plan= cur_pos->spl_plan; + if (tab->fix_splitting(spl_plan, all_tables & ~prev_tables)) + return true; + } + prev_tables|= tab->table->map; + } + return false; +} diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index 9cd2eedb55d..dada23508b5 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -446,10 +446,6 @@ static bool convert_subq_to_jtbm(JOIN *parent_join, Item_in_subselect *subq_pred, bool *remove); static TABLE_LIST *alloc_join_nest(THD *thd); static uint get_tmp_table_rec_length(Ref_ptr_array p_list, uint elements); -static double get_tmp_table_lookup_cost(THD *thd, double row_count, - uint row_size); -static double get_tmp_table_write_cost(THD *thd, double row_count, - uint row_size); bool find_eq_ref_candidate(TABLE *table, table_map sj_inner_tables); static SJ_MATERIALIZATION_INFO * at_sjmat_pos(const JOIN *join, table_map remaining_tables, const JOIN_TAB *tab, @@ -2468,7 +2464,7 @@ static uint get_tmp_table_rec_length(Ref_ptr_array p_items, uint elements) @return the cost of one lookup */ -static double +double get_tmp_table_lookup_cost(THD *thd, double row_count, uint row_size) { if (row_count * row_size > thd->variables.max_heap_table_size) @@ -2488,7 +2484,7 @@ get_tmp_table_lookup_cost(THD *thd, double row_count, uint row_size) @return the cost of writing one row */ -static double +double get_tmp_table_write_cost(THD *thd, double row_count, uint row_size) { double lookup_cost= get_tmp_table_lookup_cost(thd, row_count, row_size); diff --git a/sql/partition_info.cc b/sql/partition_info.cc index 890385d0e87..8133f15012c 100644 --- a/sql/partition_info.cc +++ b/sql/partition_info.cc @@ -2989,6 +2989,25 @@ end: DBUG_RETURN(result); } + +bool partition_info::error_if_requires_values() const +{ + switch (part_type) { + case NOT_A_PARTITION: + case HASH_PARTITION: + case VERSIONING_PARTITION: + break; + case RANGE_PARTITION: + my_error(ER_PARTITION_REQUIRES_VALUES_ERROR, MYF(0), "RANGE", "LESS THAN"); + return true; + case LIST_PARTITION: + my_error(ER_PARTITION_REQUIRES_VALUES_ERROR, MYF(0), "LIST", "IN"); + return true; + } + return false; +} + + /** Fix partition data from parser. @@ -3079,6 +3098,8 @@ bool partition_info::fix_parser_data(THD *thd) part_elem= it++; List_iterator<part_elem_value> list_val_it(part_elem->list_val_list); num_elements= part_elem->list_val_list.elements; + if (!num_elements && error_if_requires_values()) + DBUG_RETURN(true); DBUG_ASSERT(part_type == RANGE_PARTITION ? num_elements == 1U : TRUE); @@ -3420,33 +3441,6 @@ bool partition_info::has_same_partitioning(partition_info *new_part_info) } -static bool has_same_column_order(List<Create_field> *create_list, - Field** field_array) -{ - Field **f_ptr; - List_iterator_fast<Create_field> new_field_it; - Create_field *new_field= NULL; - new_field_it.init(*create_list); - - for (f_ptr= field_array; *f_ptr; f_ptr++) - { - while ((new_field= new_field_it++)) - { - if (new_field->field == *f_ptr) - break; - } - if (!new_field) - break; - } - - if (!new_field) - { - /* Not same order!*/ - return false; - } - return true; -} - bool partition_info::vers_trx_id_to_ts(THD* thd, Field* in_trx_id, Field_timestamp& out_ts) { DBUG_ASSERT(table); @@ -3471,32 +3465,6 @@ bool partition_info::vers_trx_id_to_ts(THD* thd, Field* in_trx_id, Field_timesta } -/** - Check if the partitioning columns are in the same order as the given list. - - Used to see if INPLACE alter can be allowed or not. If the order is - different then the rows must be redistributed for KEY [sub]partitioning. - - @param[in] create_list Column list after ALTER TABLE. - @return true is same order as before ALTER TABLE, else false. -*/ -bool partition_info::same_key_column_order(List<Create_field> *create_list) -{ - /* Only need to check for KEY [sub] partitioning. */ - if (list_of_part_fields && !column_list) - { - if (!has_same_column_order(create_list, part_field_array)) - return false; - } - if (list_of_subpart_fields) - { - if (!has_same_column_order(create_list, subpart_field_array)) - return false; - } - return true; -} - - void partition_info::print_debug(const char *str, uint *value) { DBUG_ENTER("print_debug"); diff --git a/sql/partition_info.h b/sql/partition_info.h index 3883fac5181..6f1414efb02 100644 --- a/sql/partition_info.h +++ b/sql/partition_info.h @@ -374,28 +374,7 @@ public: size_t file_name_size, uint32 *part_id); void report_part_expr_error(bool use_subpart_expr); bool has_same_partitioning(partition_info *new_part_info); - inline bool is_partition_used(uint part_id) const - { - return bitmap_is_set(&read_partitions, part_id); - } - inline bool is_partition_locked(uint part_id) const - { - return bitmap_is_set(&lock_partitions, part_id); - } - inline uint num_partitions_used() - { - return bitmap_bits_set(&read_partitions); - } - inline uint get_first_used_partition() const - { - return bitmap_get_first_set(&read_partitions); - } - inline uint get_next_used_partition(uint part_id) const - { - return bitmap_get_next_set(&read_partitions, part_id); - } - bool same_key_column_order(List<Create_field> *create_list); - + bool error_if_requires_values() const; private: static int list_part_cmp(const void* a, const void* b); bool set_up_default_partitions(THD *thd, handler *file, HA_CREATE_INFO *info, diff --git a/sql/rpl_gtid.cc b/sql/rpl_gtid.cc index 369b072ab47..9f48f908102 100644 --- a/sql/rpl_gtid.cc +++ b/sql/rpl_gtid.cc @@ -1673,7 +1673,6 @@ rpl_binlog_state::write_to_iocache(IO_CACHE *dest) mysql_mutex_lock(&LOCK_binlog_state); for (i= 0; i < hash.records; ++i) { - size_t res; element *e= (element *)my_hash_element(&hash, i); if (!e->last_gtid) { @@ -1693,8 +1692,8 @@ rpl_binlog_state::write_to_iocache(IO_CACHE *dest) gtid= e->last_gtid; longlong10_to_str(gtid->seq_no, buf, 10); - res= my_b_printf(dest, "%u-%u-%s\n", gtid->domain_id, gtid->server_id, buf); - if (res == (size_t) -1) + if (my_b_printf(dest, "%u-%u-%s\n", gtid->domain_id, gtid->server_id, + buf)) { res= 1; goto end; @@ -2140,10 +2139,7 @@ slave_connection_state::load(const char *slave_request, size_t len) for (;;) { if (!(rec= (uchar *)my_malloc(sizeof(entry), MYF(MY_WME)))) - { - my_error(ER_OUTOFMEMORY, MYF(0), (int) sizeof(*gtid)); return 1; - } gtid= &((entry *)rec)->gtid; if (gtid_parser_helper(&p, end, gtid)) { @@ -2759,10 +2755,7 @@ gtid_waiting::get_entry(uint32 domain_id) return e; if (!(e= (hash_element *)my_malloc(sizeof(*e), MYF(MY_WME)))) - { - my_error(ER_OUTOFMEMORY, MYF(0), (int) sizeof(*e)); return NULL; - } if (init_queue(&e->queue, 8, offsetof(queue_element, wait_seq_no), 0, cmp_queue_elem, NULL, 1+offsetof(queue_element, queue_idx), 1)) diff --git a/sql/semisync_master_ack_receiver.cc b/sql/semisync_master_ack_receiver.cc index 244e9396889..aa588a5f300 100644 --- a/sql/semisync_master_ack_receiver.cc +++ b/sql/semisync_master_ack_receiver.cc @@ -258,7 +258,7 @@ void Ack_receiver::run() struct timeval tv= {1, 0}; fds= read_fds; /* select requires max fd + 1 for the first argument */ - ret= select(max_fd+1, &fds, NULL, NULL, &tv); + ret= select((int)(max_fd+1), &fds, NULL, NULL, &tv); if (ret <= 0) { mysql_mutex_unlock(&m_mutex); diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 8522efaba48..5a404997a56 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -1804,30 +1804,30 @@ ER_WRONG_AUTO_KEY 42000 S1009 ER_BINLOG_CANT_DELETE_GTID_DOMAIN eng "Could not delete gtid domain. Reason: %s." ER_NORMAL_SHUTDOWN - cze "%s (%s): normální ukončení\n" - dan "%s (%s): Normal nedlukning\n" - nla "%s (%s): Normaal afgesloten \n" - eng "%s (%s): Normal shutdown\n" - est "%s (%s): MariaDB lõpetas\n" - fre "%s (%s): Arrêt normal du serveur\n" - ger "%s (%s): Normal heruntergefahren\n" - greek "%s (%s): Φυσιολογική διαδικασία shutdown\n" - hindi "%s (%s): सामान्य शटडाउन\n" - hun "%s (%s): Normal leallitas\n" - ita "%s (%s): Shutdown normale\n" - jpn "%s (%s): 通常シャットダウン\n" - kor "%s (%s): 정상적인 shutdown\n" - nor "%s (%s): Normal avslutning\n" - norwegian-ny "%s (%s): Normal nedkopling\n" - pol "%s (%s): Standardowe zakończenie działania\n" - por "%s (%s): 'Shutdown' normal\n" - rum "%s (%s): Terminare normala\n" - rus "%s (%s): Корректная остановка\n" - serbian "%s (%s): Normalno gašenje\n" - slo "%s (%s): normálne ukončenie\n" - spa "%s (%s): Apagado normal\n" - swe "%s (%s): Normal avslutning\n" - ukr "%s (%s): Нормальне завершення\n" + cze "%s (%s): normální ukončení" + dan "%s (%s): Normal nedlukning" + nla "%s (%s): Normaal afgesloten " + eng "%s (%s): Normal shutdown" + est "%s (%s): MariaDB lõpetas" + fre "%s (%s): Arrêt normal du serveur" + ger "%s (%s): Normal heruntergefahren" + greek "%s (%s): Φυσιολογική διαδικασία shutdown" + hindi "%s (%s): सामान्य शटडाउन" + hun "%s (%s): Normal leallitas" + ita "%s (%s): Shutdown normale" + jpn "%s (%s): 通常シャットダウン" + kor "%s (%s): 정상적인 shutdown" + nor "%s (%s): Normal avslutning" + norwegian-ny "%s (%s): Normal nedkopling" + pol "%s (%s): Standardowe zakończenie działania" + por "%s (%s): 'Shutdown' normal" + rum "%s (%s): Terminare normala" + rus "%s (%s): Корректная остановка" + serbian "%s (%s): Normalno gašenje" + slo "%s (%s): normálne ukončenie" + spa "%s (%s): Apagado normal" + swe "%s (%s): Normal avslutning" + ukr "%s (%s): Нормальне завершення" ER_GOT_SIGNAL cze "%s: přijat signal %d, končím\n" dan "%s: Fangede signal %d. Afslutter!!\n" diff --git a/sql/signal_handler.cc b/sql/signal_handler.cc index c99ad088e9f..0452e181e22 100644 --- a/sql/signal_handler.cc +++ b/sql/signal_handler.cc @@ -298,7 +298,9 @@ extern "C" sig_handler handle_fatal_signal(int sig) #ifdef HAVE_WRITE_CORE if (test_flags & TEST_CORE_ON_SIGNAL) { - my_safe_printf_stderr("%s", "Writing a core file\n"); + char buff[80]; + my_getwd(buff, sizeof(buff), 0); + my_safe_printf_stderr("Writing a core file at %s\n", buff); fflush(stderr); my_write_core(sig); } diff --git a/sql/slave.cc b/sql/slave.cc index f36af66f780..90ad8bbdef1 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -41,7 +41,7 @@ #include <sql_common.h> #include <errmsg.h> #include <ssl_compat.h> -#include <mysqld_error.h> +#include "unireg.h" #include <mysys_err.h> #include <signal.h> #include <mysql.h> diff --git a/sql/slave.h b/sql/slave.h index 74bb4356dfb..649d55b45b9 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -52,7 +52,7 @@ #define SLAVE_NET_TIMEOUT 60 -#define MAX_SLAVE_ERROR 2000 +#define MAX_SLAVE_ERROR ER_ERROR_LAST+1 #define MAX_REPLICATION_THREAD 64 diff --git a/sql/sp.cc b/sql/sp.cc index 58dd8cfee3d..843c40cf2b6 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -1345,8 +1345,8 @@ log: { thd->clear_error(); - String log_query; - log_query.set_charset(system_charset_info); + StringBuffer<128> log_query(thd->variables.character_set_client); + DBUG_ASSERT(log_query.charset()->mbminlen == 1); if (show_create_sp(thd, &log_query, sp->m_explicit_name ? sp->m_db : null_clex_str, diff --git a/sql/sp_head.cc b/sql/sp_head.cc index e1a28602a44..decf007b2d8 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -2418,10 +2418,9 @@ sp_head::check_unresolved_goto() if (m_backpatch_goto.elements > 0) { List_iterator_fast<bp_t> li(m_backpatch_goto); - bp_t *bp; - while ((bp= li++)) + while (bp_t* bp= li++) { - if ((bp->instr_type == GOTO)) + if (bp->instr_type == GOTO) { my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "GOTO", bp->lab->name.str); has_unresolved_label=true; @@ -4652,7 +4651,8 @@ sp_head::add_used_tables_to_table_list(THD *thd, table->init_one_table_for_prelocking(key_buff, stab->db_length, key_buff + stab->db_length + 1, stab->table_name_length, key_buff + stab->db_length + stab->table_name_length + 2, - stab->lock_type, true, belong_to_view, stab->trg_event_map, + stab->lock_type, TABLE_LIST::PRELOCK_ROUTINE, belong_to_view, + stab->trg_event_map, query_tables_last_ptr); tab_buff+= ALIGN_SIZE(sizeof(TABLE_LIST)); diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index f402892e18e..d3edaa2b56a 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -2869,37 +2869,42 @@ static void acl_insert_user(const char *user, const char *host, } -static void acl_update_db(const char *user, const char *host, const char *db, +static bool acl_update_db(const char *user, const char *host, const char *db, ulong privileges) { mysql_mutex_assert_owner(&acl_cache->lock); + bool updated= false; + for (uint i=0 ; i < acl_dbs.elements ; i++) { ACL_DB *acl_db=dynamic_element(&acl_dbs,i,ACL_DB*); if ((!acl_db->user && !user[0]) || - (acl_db->user && - !strcmp(user,acl_db->user))) + (acl_db->user && + !strcmp(user,acl_db->user))) { if ((!acl_db->host.hostname && !host[0]) || - (acl_db->host.hostname && - !strcmp(host, acl_db->host.hostname))) + (acl_db->host.hostname && + !strcmp(host, acl_db->host.hostname))) { - if ((!acl_db->db && !db[0]) || - (acl_db->db && !strcmp(db,acl_db->db))) + if ((!acl_db->db && !db[0]) || + (acl_db->db && !strcmp(db,acl_db->db))) - { - if (privileges) + { + if (privileges) { acl_db->access= privileges; acl_db->initial_access= acl_db->access; } - else - delete_dynamic_element(&acl_dbs,i); - } + else + delete_dynamic_element(&acl_dbs,i); + updated= true; + } } } } + + return updated; } @@ -4391,9 +4396,21 @@ static int replace_db_table(TABLE *table, const char *db, acl_cache->clear(1); // Clear privilege cache if (old_row_exists) acl_update_db(combo.user.str,combo.host.str,db,rights); - else - if (rights) - acl_insert_db(combo.user.str,combo.host.str,db,rights); + else if (rights) + { + /* + If we did not have an already existing row, for users, we must always + insert an ACL_DB entry. For roles however, it is possible that one was + already created when DB privileges were propagated from other granted + roles onto the current role. For this case, first try to update the + existing entry, otherwise insert a new one. + */ + if (!combo.is_role() || + !acl_update_db(combo.user.str, combo.host.str, db, rights)) + { + acl_insert_db(combo.user.str,combo.host.str,db,rights); + } + } DBUG_RETURN(0); /* This could only happen if the grant tables got corrupted */ @@ -6316,9 +6333,12 @@ static int merge_role_privileges(ACL_ROLE *role __attribute__((unused)), { PRIVS_TO_MERGE *data= (PRIVS_TO_MERGE *)context; + DBUG_ASSERT(grantee->counter > 0); if (--grantee->counter) return 1; // don't recurse into grantee just yet + grantee->counter= 1; // Mark the grantee as merged. + /* if we'll do db/table/routine privileges, create a hash of role names */ role_hash_t role_hash(role_key); if (data->what != PRIVS_TO_MERGE::GLOBAL) @@ -7410,11 +7430,10 @@ end_index_init: DBUG_RETURN(return_val); } - -static my_bool role_propagate_grants_action(void *ptr, - void *unused __attribute__((unused))) +static my_bool propagate_role_grants_action(void *role_ptr, + void *ptr __attribute__((unused))) { - ACL_ROLE *role= (ACL_ROLE *)ptr; + ACL_ROLE *role= static_cast<ACL_ROLE *>(role_ptr); if (role->counter) return 0; @@ -7490,7 +7509,7 @@ bool grant_reload(THD *thd) } mysql_mutex_lock(&acl_cache->lock); - my_hash_iterate(&acl_roles, role_propagate_grants_action, NULL); + my_hash_iterate(&acl_roles, propagate_role_grants_action, NULL); mysql_mutex_unlock(&acl_cache->lock); mysql_rwlock_unlock(&LOCK_grant); @@ -8589,7 +8608,7 @@ bool mysql_show_create_user(THD *thd, LEX_USER *lex_user) hostname, NullS) - buff); Item_string *field = new (thd->mem_root) Item_string_ascii(thd, "", 0); if (!field) - DBUG_RETURN(true); + DBUG_RETURN(true); // Error given my my_alloc() field->name.str= buff; field->name.length= head_length; @@ -10233,13 +10252,13 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list, bool handle_as_role) } } - binlog= true; if (replace_user_table(thd, tables.user_table(), *user_name, 0, 0, 1, 0)) { append_user(thd, &wrong_users, user_name); result= TRUE; continue; } + binlog= true; // every created role is automatically granted to its creator-admin if (handle_as_role) diff --git a/sql/sql_acl.h b/sql/sql_acl.h index cff59036867..bafb9ca23d8 100644 --- a/sql/sql_acl.h +++ b/sql/sql_acl.h @@ -198,8 +198,12 @@ extern LEX_CSTRING current_user_and_current_role; static inline int access_denied_error_code(int passwd_used) { +#ifdef mysqld_error_find_printf_error_used + return 0; +#else return passwd_used == 2 ? ER_ACCESS_DENIED_NO_PASSWORD_ERROR : ER_ACCESS_DENIED_ERROR; +#endif } /* prototypes */ diff --git a/sql/sql_base.cc b/sql/sql_base.cc index e5191feba14..69b867da0cb 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -3528,7 +3528,7 @@ open_and_process_table(THD *thd, LEX *lex, TABLE_LIST *tables, */ if (thd->locked_tables_mode <= LTM_LOCK_TABLES && ! has_prelocking_list && - tables->lock_type >= TL_WRITE_ALLOW_WRITE) + (tables->lock_type >= TL_WRITE_ALLOW_WRITE || thd->lex->default_used)) { bool need_prelocking= FALSE; TABLE_LIST **save_query_tables_last= lex->query_tables_last; @@ -4234,7 +4234,7 @@ static bool table_already_fk_prelocked(TABLE_LIST *tl, LEX_CSTRING *db, for (; tl; tl= tl->next_global ) { if (tl->lock_type >= lock_type && - tl->prelocking_placeholder == TABLE_LIST::FK && + tl->prelocking_placeholder == TABLE_LIST::PRELOCK_FK && strcmp(tl->db, db->str) == 0 && strcmp(tl->table_name, table->str) == 0) return true; @@ -4243,6 +4243,55 @@ static bool table_already_fk_prelocked(TABLE_LIST *tl, LEX_CSTRING *db, } +static bool internal_table_exists(TABLE_LIST *global_list, + const char *table_name) +{ + do + { + if (global_list->table_name == table_name) + return 1; + } while ((global_list= global_list->next_global)); + return 0; +} + + +static bool +add_internal_tables(THD *thd, Query_tables_list *prelocking_ctx, + TABLE_LIST *tables) +{ + TABLE_LIST *global_table_list= prelocking_ctx->query_tables; + + do + { + /* + Skip table if already in the list. Can happen with prepared statements + */ + if (tables->next_local && + internal_table_exists(global_table_list, tables->table_name)) + continue; + + TABLE_LIST *tl= (TABLE_LIST *) thd->alloc(sizeof(TABLE_LIST)); + if (!tl) + return TRUE; + tl->init_one_table_for_prelocking(tables->db, + strlen(tables->db), + tables->table_name, + strlen(tables->table_name), + NULL, tables->lock_type, + TABLE_LIST::PRELOCK_NONE, + 0, 0, + &prelocking_ctx->query_tables_last); + /* + Store link to the new table_list that will be used by open so that + Item_func_nextval() can find it + */ + tables->next_local= tl; + } while ((tables= tables->next_global)); + return FALSE; +} + + + /** Defines how prelocking algorithm for DML statements should handle table list elements: @@ -4269,21 +4318,23 @@ bool DML_prelocking_strategy:: handle_table(THD *thd, Query_tables_list *prelocking_ctx, TABLE_LIST *table_list, bool *need_prelocking) { + TABLE *table= table_list->table; /* We rely on a caller to check that table is going to be changed. */ - DBUG_ASSERT(table_list->lock_type >= TL_WRITE_ALLOW_WRITE); + DBUG_ASSERT(table_list->lock_type >= TL_WRITE_ALLOW_WRITE || + thd->lex->default_used); if (table_list->trg_event_map) { - if (table_list->table->triggers) + if (table->triggers) { *need_prelocking= TRUE; - if (table_list->table->triggers-> + if (table->triggers-> add_tables_and_routines_for_triggers(thd, prelocking_ctx, table_list)) return TRUE; } - if (table_list->table->file->referenced_by_foreign_key()) + if (table->file->referenced_by_foreign_key()) { List <FOREIGN_KEY_INFO> fk_list; List_iterator<FOREIGN_KEY_INFO> fk_list_it(fk_list); @@ -4292,7 +4343,7 @@ handle_table(THD *thd, Query_tables_list *prelocking_ctx, arena= thd->activate_stmt_arena_if_needed(&backup); - table_list->table->file->get_parent_foreign_key_list(thd, &fk_list); + table->file->get_parent_foreign_key_list(thd, &fk_list); if (thd->is_error()) { if (arena) @@ -4321,21 +4372,96 @@ handle_table(THD *thd, Query_tables_list *prelocking_ctx, continue; TABLE_LIST *tl= (TABLE_LIST *) thd->alloc(sizeof(TABLE_LIST)); - tl->init_one_table_for_prelocking(fk->foreign_db->str, fk->foreign_db->length, - fk->foreign_table->str, fk->foreign_table->length, - NULL, lock_type, false, table_list->belong_to_view, - op, &prelocking_ctx->query_tables_last); + tl->init_one_table_for_prelocking(fk->foreign_db->str, + fk->foreign_db->length, + fk->foreign_table->str, + fk->foreign_table->length, + NULL, lock_type, + TABLE_LIST::PRELOCK_FK, + table_list->belong_to_view, op, + &prelocking_ctx->query_tables_last); } if (arena) thd->restore_active_arena(arena, &backup); } } + /* Open any tables used by DEFAULT (like sequence tables) */ + if (table->internal_tables && + ((sql_command_flags[thd->lex->sql_command] & CF_INSERTS_DATA) || + thd->lex->default_used)) + { + Query_arena *arena, backup; + bool error; + arena= thd->activate_stmt_arena_if_needed(&backup); + error= add_internal_tables(thd, prelocking_ctx, + table->internal_tables); + if (arena) + thd->restore_active_arena(arena, &backup); + if (error) + { + *need_prelocking= TRUE; + return TRUE; + } + } return FALSE; } /** + Open all tables used by DEFAULT functions. + + This is different from normal open_and_lock_tables() as we may + already have other tables opened and locked and we have to merge the + new table with the old ones. +*/ + +bool open_and_lock_internal_tables(TABLE *table, bool lock_table) +{ + THD *thd= table->in_use; + TABLE_LIST *tl; + MYSQL_LOCK *save_lock,*new_lock; + DBUG_ENTER("open_internal_tables"); + + /* remove pointer to old select_lex which is already destroyed */ + for (tl= table->internal_tables ; tl ; tl= tl->next_global) + tl->select_lex= 0; + + uint counter; + MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint(); + TABLE_LIST *tmp= table->internal_tables; + DML_prelocking_strategy prelocking_strategy; + + if (open_tables(thd, thd->lex->create_info, &tmp, &counter, 0, + &prelocking_strategy)) + goto err; + + if (lock_table) + { + save_lock= thd->lock; + thd->lock= 0; + if (lock_tables(thd, table->internal_tables, counter, + MYSQL_LOCK_USE_MALLOC)) + goto err; + + if (!(new_lock= mysql_lock_merge(save_lock, thd->lock))) + { + thd->lock= save_lock; + mysql_unlock_tables(thd, save_lock, 1); + /* We don't have to close tables as caller will do that */ + goto err; + } + thd->lock= new_lock; + } + DBUG_RETURN(0); + +err: + thd->mdl_context.rollback_to_savepoint(mdl_savepoint); + DBUG_RETURN(1); +} + + +/** Defines how prelocking algorithm for DML statements should handle view - all view routines should be added to the prelocking set. @@ -4486,7 +4612,7 @@ static bool check_lock_and_start_stmt(THD *thd, Prelocking placeholder is not set for TABLE_LIST that are directly used by TOP level statement. */ - DBUG_ASSERT(table_list->prelocking_placeholder == false); + DBUG_ASSERT(table_list->prelocking_placeholder == TABLE_LIST::PRELOCK_NONE); /* TL_WRITE_DEFAULT and TL_READ_DEFAULT are supposed to be parser only @@ -4499,7 +4625,6 @@ static bool check_lock_and_start_stmt(THD *thd, Last argument routine_modifies_data for read_lock_type_for_table() is ignored, as prelocking placeholder will never be set here. */ - DBUG_ASSERT(table_list->prelocking_placeholder == false); if (table_list->lock_type == TL_WRITE_DEFAULT) lock_type= thd->update_lock_default; else if (table_list->lock_type == TL_READ_DEFAULT) @@ -4507,8 +4632,8 @@ static bool check_lock_and_start_stmt(THD *thd, else lock_type= table_list->lock_type; - if ((int) lock_type > (int) TL_WRITE_ALLOW_WRITE && - (int) table_list->table->reginfo.lock_type <= (int) TL_WRITE_ALLOW_WRITE) + if ((int) lock_type >= (int) TL_WRITE_ALLOW_WRITE && + (int) table_list->table->reginfo.lock_type < (int) TL_WRITE_ALLOW_WRITE) { my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0), table_list->table->alias.c_ptr()); @@ -5532,7 +5657,7 @@ find_field_in_table(THD *thd, TABLE *table, const char *name, uint length, DBUG_EVALUATE_IF("test_completely_invisible", 0, 1)) DBUG_RETURN((Field*)0); - *cached_field_index_ptr= field_ptr - table->field; + *cached_field_index_ptr= (uint)(field_ptr - table->field); field= *field_ptr; } else diff --git a/sql/sql_base.h b/sql/sql_base.h index bb33af66590..bf140cb5d17 100644 --- a/sql/sql_base.h +++ b/sql/sql_base.h @@ -100,6 +100,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type update, */ #define MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK 0x1000 #define MYSQL_LOCK_NOT_TEMPORARY 0x2000 +#define MYSQL_LOCK_USE_MALLOC 0x4000 /** Only check THD::killed if waits happen (e.g. wait on MDL, wait on table flush, wait on thr_lock.c locks) while opening and locking table. @@ -257,6 +258,7 @@ bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags, uint dt_phases); bool open_tables_only_view_structure(THD *thd, TABLE_LIST *tables, bool can_deadlock); +bool open_and_lock_internal_tables(TABLE *table, bool lock); bool lock_tables(THD *thd, TABLE_LIST *tables, uint counter, uint flags); int decide_logging_format(THD *thd, TABLE_LIST *tables); void close_thread_table(THD *thd, TABLE **table_ptr); diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index 2dc977c9b5d..766aa8099ba 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -1814,7 +1814,7 @@ Query_cache::send_result_to_client(THD *thd, char *org_sql, uint query_length) sql++; continue; } - /* fall trough */ + /* fall through */ default: break; } diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 00144a9a12d..84fdaf8b1b6 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -2750,6 +2750,8 @@ void THD::nocheck_register_item_tree_change(Item **place, Item *old_value, MEM_ROOT *runtime_memroot) { Item_change_record *change; + DBUG_ENTER("THD::nocheck_register_item_tree_change"); + DBUG_PRINT("enter", ("Register %p <- %p", old_value, (*place))); /* Now we use one node per change, which adds some memory overhead, but still is rather fast as we use alloc_root for allocations. @@ -2762,12 +2764,13 @@ void THD::nocheck_register_item_tree_change(Item **place, Item *old_value, OOM, thd->fatal_error() is called by the error handler of the memroot. Just return. */ - return; + DBUG_VOID_RETURN; } change= new (change_mem) Item_change_record; change->place= place; change->old_value= old_value; change_list.append(change); + DBUG_VOID_RETURN; } /** @@ -2808,7 +2811,11 @@ void THD::rollback_item_tree_changes() DBUG_ENTER("rollback_item_tree_changes"); while ((change= it++)) + { + DBUG_PRINT("info", ("revert %p -> %p", + change->old_value, (*change->place))); *change->place= change->old_value; + } /* We can forget about changes memory: it's allocated in runtime memroot */ change_list.empty(); DBUG_VOID_RETURN; @@ -4688,7 +4695,7 @@ TABLE *find_fk_open_table(THD *thd, const char *db, size_t db_len, { if (t->s->db.length == db_len && t->s->table_name.length == table_len && !strcmp(t->s->db.str, db) && !strcmp(t->s->table_name.str, table) && - t->pos_in_table_list->prelocking_placeholder == TABLE_LIST::FK) + t->pos_in_table_list->prelocking_placeholder == TABLE_LIST::PRELOCK_FK) return t; } return NULL; @@ -6081,7 +6088,7 @@ int THD::decide_logging_format(TABLE_LIST *tables) replicated_tables_count++; if (table->lock_type <= TL_READ_NO_INSERT && - table->prelocking_placeholder != TABLE_LIST::FK) + table->prelocking_placeholder != TABLE_LIST::PRELOCK_FK) has_read_tables= true; else if (table->table->found_next_number_field && (table->lock_type >= TL_WRITE_ALLOW_WRITE)) diff --git a/sql/sql_class.h b/sql/sql_class.h index ebb98ac9407..c15bbe54a1b 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -373,8 +373,9 @@ public: typedef struct st_mysql_lock { TABLE **table; - uint table_count,lock_count; THR_LOCK_DATA **locks; + uint table_count,lock_count; + uint flags; } MYSQL_LOCK; @@ -704,7 +705,7 @@ typedef struct system_variables uint idle_write_transaction_timeout; uint column_compression_threshold; uint column_compression_zlib_level; - ulong in_subquery_conversion_threshold; + uint in_subquery_conversion_threshold; vers_asof_timestamp_t vers_asof_timestamp; #ifdef VERS_EXPERIMENTAL @@ -4479,7 +4480,8 @@ public: const char *path, const char *db, const char *table_name, - bool open_in_engine); + bool open_in_engine, + bool open_internal_tables); TABLE *find_temporary_table(const char *db, const char *table_name); TABLE *find_temporary_table(const TABLE_LIST *tl); @@ -5586,8 +5588,6 @@ public: }; - - /* Optimizer and executor structure for the materialized semi-join info. This structure contains diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc index e428d969db0..1e3994dea66 100644 --- a/sql/sql_connect.cc +++ b/sql/sql_connect.cc @@ -316,13 +316,9 @@ extern "C" void free_user(struct user_conn *uc) void init_max_user_conn(void) { #ifndef NO_EMBEDDED_ACCESS_CHECKS - if (my_hash_init(&hash_user_connections,system_charset_info,max_connections, - 0,0, (my_hash_get_key) get_key_conn, - (my_hash_free_key) free_user, 0)) - { - sql_print_error("Initializing hash_user_connections failed."); - exit(1); - } + my_hash_init(&hash_user_connections, system_charset_info, max_connections, + 0, 0, (my_hash_get_key) get_key_conn, + (my_hash_free_key) free_user, 0); #endif } @@ -481,24 +477,16 @@ void init_user_stats(USER_STATS *user_stats, void init_global_user_stats(void) { - if (my_hash_init(&global_user_stats, system_charset_info, max_connections, - 0, 0, (my_hash_get_key) get_key_user_stats, - (my_hash_free_key)free_user_stats, 0)) - { - sql_print_error("Initializing global_user_stats failed."); - exit(1); - } + my_hash_init(&global_user_stats, system_charset_info, max_connections, + 0, 0, (my_hash_get_key) get_key_user_stats, + (my_hash_free_key) free_user_stats, 0); } void init_global_client_stats(void) { - if (my_hash_init(&global_client_stats, system_charset_info, max_connections, - 0, 0, (my_hash_get_key) get_key_user_stats, - (my_hash_free_key)free_user_stats, 0)) - { - sql_print_error("Initializing global_client_stats failed."); - exit(1); - } + my_hash_init(&global_client_stats, system_charset_info, max_connections, + 0, 0, (my_hash_get_key) get_key_user_stats, + (my_hash_free_key) free_user_stats, 0); } extern "C" uchar *get_key_table_stats(TABLE_STATS *table_stats, size_t *length, @@ -515,12 +503,9 @@ extern "C" void free_table_stats(TABLE_STATS* table_stats) void init_global_table_stats(void) { - if (my_hash_init(&global_table_stats, system_charset_info, max_connections, - 0, 0, (my_hash_get_key) get_key_table_stats, - (my_hash_free_key)free_table_stats, 0)) { - sql_print_error("Initializing global_table_stats failed."); - exit(1); - } + my_hash_init(&global_table_stats, system_charset_info, max_connections, + 0, 0, (my_hash_get_key) get_key_table_stats, + (my_hash_free_key) free_table_stats, 0); } extern "C" uchar *get_key_index_stats(INDEX_STATS *index_stats, size_t *length, @@ -537,13 +522,9 @@ extern "C" void free_index_stats(INDEX_STATS* index_stats) void init_global_index_stats(void) { - if (my_hash_init(&global_index_stats, system_charset_info, max_connections, - 0, 0, (my_hash_get_key) get_key_index_stats, - (my_hash_free_key)free_index_stats, 0)) - { - sql_print_error("Initializing global_index_stats failed."); - exit(1); - } + my_hash_init(&global_index_stats, system_charset_info, max_connections, + 0, 0, (my_hash_get_key) get_key_index_stats, + (my_hash_free_key) free_index_stats, 0); } diff --git a/sql/sql_const.h b/sql/sql_const.h index 007b7faeebb..65742235bee 100644 --- a/sql/sql_const.h +++ b/sql/sql_const.h @@ -231,6 +231,7 @@ */ #define HEAP_TEMPTABLE_LOOKUP_COST 0.05 #define DISK_TEMPTABLE_LOOKUP_COST 1.0 +#define SORT_INDEX_CMP_COST 0.02 #define MY_CHARSET_BIN_MB_MAXLEN 1 diff --git a/sql/sql_cte.cc b/sql/sql_cte.cc index ff80e198023..9608436226c 100644 --- a/sql/sql_cte.cc +++ b/sql/sql_cte.cc @@ -681,7 +681,7 @@ void With_element::move_anchors_ahead() { st_select_lex *next_sl; st_select_lex *new_pos= spec->first_select(); - st_select_lex *last_sl; + st_select_lex *UNINIT_VAR(last_sl); new_pos->linkage= UNION_TYPE; for (st_select_lex *sl= new_pos; sl; sl= next_sl) { diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index 0d237b94af2..f9801d2bf36 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -364,6 +364,9 @@ bool mysql_derived_merge(THD *thd, LEX *lex, TABLE_LIST *derived) SELECT_LEX *parent_lex= derived->select_lex; Query_arena *arena, backup; DBUG_ENTER("mysql_derived_merge"); + DBUG_PRINT("enter", ("Alias: '%s' Unit: %p", + (derived->alias ? derived->alias : "<NULL>"), + derived->get_unit())); if (derived->merged) DBUG_RETURN(FALSE); @@ -510,7 +513,9 @@ unconditional_materialization: bool mysql_derived_merge_for_insert(THD *thd, LEX *lex, TABLE_LIST *derived) { DBUG_ENTER("mysql_derived_merge_for_insert"); - DBUG_PRINT("enter", ("derived: %p", derived)); + DBUG_PRINT("enter", ("Alias: '%s' Unit: %p", + (derived->alias ? derived->alias : "<NULL>"), + derived->get_unit())); DBUG_PRINT("info", ("merged_for_insert: %d is_materialized_derived: %d " "is_multitable: %d single_table_updatable: %d " "merge_underlying_list: %d", @@ -566,7 +571,9 @@ bool mysql_derived_init(THD *thd, LEX *lex, TABLE_LIST *derived) { SELECT_LEX_UNIT *unit= derived->get_unit(); DBUG_ENTER("mysql_derived_init"); - DBUG_PRINT("enter", ("derived: %p", derived)); + DBUG_PRINT("enter", ("Alias: '%s' Unit: %p", + (derived->alias ? derived->alias : "<NULL>"), + derived->get_unit())); // Skip already prepared views/DT if (!unit || unit->prepared) @@ -642,8 +649,9 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived) SELECT_LEX_UNIT *unit= derived->get_unit(); DBUG_ENTER("mysql_derived_prepare"); bool res= FALSE; - DBUG_PRINT("enter", ("unit: %p table_list: %p Alias '%s'", - unit, derived, derived->alias)); + DBUG_PRINT("enter", ("Alias: '%s' Unit: %p", + (derived->alias ? derived->alias : "<NULL>"), + unit)); if (!unit) DBUG_RETURN(FALSE); @@ -876,6 +884,9 @@ bool mysql_derived_optimize(THD *thd, LEX *lex, TABLE_LIST *derived) bool res= FALSE; DBUG_ENTER("mysql_derived_optimize"); + DBUG_PRINT("enter", ("Alias: '%s' Unit: %p", + (derived->alias ? derived->alias : "<NULL>"), + derived->get_unit())); lex->current_select= first_select; @@ -954,6 +965,9 @@ err: bool mysql_derived_create(THD *thd, LEX *lex, TABLE_LIST *derived) { DBUG_ENTER("mysql_derived_create"); + DBUG_PRINT("enter", ("Alias: '%s' Unit: %p", + (derived->alias ? derived->alias : "<NULL>"), + derived->get_unit())); TABLE *table= derived->table; SELECT_LEX_UNIT *unit= derived->get_unit(); @@ -1047,10 +1061,14 @@ bool TABLE_LIST::fill_recursive(THD *thd) bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived) { - DBUG_ENTER("mysql_derived_fill"); + Field_iterator_table field_iterator; SELECT_LEX_UNIT *unit= derived->get_unit(); bool derived_is_recursive= derived->is_recursive_with_table(); bool res= FALSE; + DBUG_ENTER("mysql_derived_fill"); + DBUG_PRINT("enter", ("Alias: '%s' Unit: %p", + (derived->alias ? derived->alias : "<NULL>"), + derived->get_unit())); if (unit->executed && !unit->uncacheable && !unit->describe && !derived_is_recursive) @@ -1121,9 +1139,28 @@ bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived) if (derived_result->flush()) res= TRUE; unit->executed= TRUE; + + if (derived->field_translation) + { + /* reset translation table to materialized table */ + field_iterator.set_table(derived->table); + for (uint i= 0; + !field_iterator.end_of_fields(); + field_iterator.next(), i= i + 1) + { + Item *item; + + if (!(item= field_iterator.create_item(thd))) + { + res= TRUE; + break; + } + thd->change_item_tree(&derived->field_translation[i].item, item); + } + } } err: - if (res || (!lex->describe && !derived_is_recursive && !unit->uncacheable)) + if (res || (!lex->describe && !derived_is_recursive && !unit->uncacheable)) unit->cleanup(); lex->current_select= save_current_select; @@ -1151,6 +1188,9 @@ err: bool mysql_derived_reinit(THD *thd, LEX *lex, TABLE_LIST *derived) { DBUG_ENTER("mysql_derived_reinit"); + DBUG_PRINT("enter", ("Alias: '%s' Unit: %p", + (derived->alias ? derived->alias : "<NULL>"), + derived->get_unit())); st_select_lex_unit *unit= derived->get_unit(); derived->merged_for_insert= FALSE; diff --git a/sql/sql_digest.cc b/sql/sql_digest.cc index 65edcc122f1..2f88d62fbbb 100644 --- a/sql/sql_digest.cc +++ b/sql/sql_digest.cc @@ -20,7 +20,7 @@ #include "mariadb.h" #include "my_md5.h" -#include "mysqld_error.h" +#include "unireg.h" #include "sql_string.h" #include "sql_class.h" diff --git a/sql/sql_explain.cc b/sql/sql_explain.cc index fed38a5f4f4..ad80303e1b3 100644 --- a/sql/sql_explain.cc +++ b/sql/sql_explain.cc @@ -974,6 +974,7 @@ Explain_aggr_filesort::Explain_aggr_filesort(MEM_ROOT *mem_root, for (ORDER *ord= filesort->order; ord; ord= ord->next) { sort_items.push_back(ord->item[0], mem_root); + sort_directions.push_back(&ord->direction, mem_root); } filesort->tracker= &tracker; } @@ -987,10 +988,13 @@ void Explain_aggr_filesort::print_json_members(Json_writer *writer, str.length(0); List_iterator_fast<Item> it(sort_items); - Item *item; + List_iterator_fast<ORDER::enum_order> it_dir(sort_directions); + Item* item; + ORDER::enum_order *direction; bool first= true; while ((item= it++)) { + direction= it_dir++; if (first) first= false; else @@ -998,6 +1002,8 @@ void Explain_aggr_filesort::print_json_members(Json_writer *writer, str.append(", "); } append_item_to_str(&str, item); + if (*direction == ORDER::ORDER_DESC) + str.append(" desc"); } writer->add_member("sort_key").add_str(str.c_ptr_safe()); diff --git a/sql/sql_explain.h b/sql/sql_explain.h index 895c059f1b0..7fb50a6cb04 100644 --- a/sql/sql_explain.h +++ b/sql/sql_explain.h @@ -286,6 +286,7 @@ public: class Explain_aggr_filesort : public Explain_aggr_node { List<Item> sort_items; + List<ORDER::enum_order> sort_directions; public: enum_explain_aggr_node_type get_type() { return AGGR_OP_FILESORT; } Filesort_tracker tracker; diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 7384831d5e3..197fe86957c 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -196,8 +196,9 @@ init_lex_with_single_table(THD *thd, TABLE *table, LEX *lex) lex_start(thd); context->init(); if ((!(table_ident= new Table_ident(thd, + &table->s->db, &table->s->table_name, - &table->s->db, TRUE))) || + TRUE))) || (!(table_list= select_lex->add_table_to_list(thd, table_ident, NULL, @@ -737,6 +738,7 @@ void LEX::start(THD *thd_arg) spcont= NULL; proc_list.first= 0; escape_used= FALSE; + default_used= FALSE; query_tables= 0; reset_query_tables_list(FALSE); expr_allows_subselect= TRUE; @@ -2453,10 +2455,8 @@ void st_select_lex_node::move_as_slave(st_select_lex_node *new_master) prev= &curr->next; } else - { prev= &new_master->slave; - new_master->slave= this; - } + *prev= this; next= 0; master= new_master; } @@ -3051,7 +3051,7 @@ void Query_tables_list::destroy_query_tables_list() LEX::LEX() : explain(NULL), result(0), arena_for_set_stmt(0), mem_root_for_set_stmt(0), option_type(OPT_DEFAULT), context_analysis_only(0), sphead(0), - is_lex_started(0), limit_rows_examined_cnt(ULONGLONG_MAX) + default_used(0), is_lex_started(0), limit_rows_examined_cnt(ULONGLONG_MAX) { init_dynamic_array2(&plugins, sizeof(plugin_ref), plugins_static_buffer, diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 79c0f377a20..1465465a56c 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -2960,6 +2960,7 @@ public: st_alter_tablespace *alter_tablespace_info; bool escape_used; + bool default_used; /* using default() function */ bool is_lex_started; /* If lex_start() did run. For debugging. */ /* diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 4c05c5cb98d..b058af531c6 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1575,7 +1575,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd, if (thd->wsrep_conflict_state == ABORTED && command != COM_STMT_CLOSE && command != COM_QUIT) { - my_message(ER_LOCK_DEADLOCK, "wsrep aborted transaction", MYF(0)); + my_message(ER_LOCK_DEADLOCK, "Deadlock: wsrep aborted transaction", + MYF(0)); WSREP_DEBUG("Deadlock error for: %s", thd->query()); mysql_mutex_unlock(&thd->LOCK_wsrep_thd); thd->reset_killed(); @@ -7851,7 +7852,8 @@ static void wsrep_mysql_parse(THD *thd, char *rawbuf, uint length, (longlong) thd->thread_id, is_autocommit, thd->wsrep_retry_counter, thd->variables.wsrep_retry_autocommit, thd->query()); - my_message(ER_LOCK_DEADLOCK, "wsrep aborted transaction", MYF(0)); + my_message(ER_LOCK_DEADLOCK, "Deadlock: wsrep aborted transaction", + MYF(0)); thd->reset_killed(); thd->wsrep_conflict_state= NO_CONFLICT; if (thd->wsrep_conflict_state != REPLAYING) diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 24cd5fc5123..d1da9823aa4 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -4743,16 +4743,11 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, { my_error(ER_PARTITION_WRONG_TYPE, MYF(0), "SYSTEM_TIME"); } - else if (tab_part_info->part_type == RANGE_PARTITION) - { - my_error(ER_PARTITION_REQUIRES_VALUES_ERROR, MYF(0), - "RANGE", "LESS THAN"); - } else { - DBUG_ASSERT(tab_part_info->part_type == LIST_PARTITION); - my_error(ER_PARTITION_REQUIRES_VALUES_ERROR, MYF(0), - "LIST", "IN"); + DBUG_ASSERT(tab_part_info->part_type == RANGE_PARTITION || + tab_part_info->part_type == LIST_PARTITION); + (void) tab_part_info->error_if_requires_values(); } goto err; } @@ -8368,8 +8363,11 @@ int create_partition_name(char *out, size_t outlen, const char *in1, end= strxnmov(out, outlen-1, in1, "#P#", transl_part, NullS); else if (name_variant == TEMP_PART_NAME) end= strxnmov(out, outlen-1, in1, "#P#", transl_part, "#TMP#", NullS); - else if (name_variant == RENAMED_PART_NAME) + else + { + DBUG_ASSERT(name_variant == RENAMED_PART_NAME); end= strxnmov(out, outlen-1, in1, "#P#", transl_part, "#REN#", NullS); + } if (end - out == static_cast<ptrdiff_t>(outlen-1)) { my_error(ER_PATH_LENGTH, MYF(0), longest_str(in1, transl_part)); @@ -8409,9 +8407,12 @@ int create_subpartition_name(char *out, size_t outlen, else if (name_variant == TEMP_PART_NAME) end= strxnmov(out, outlen-1, in1, "#P#", transl_part_name, "#SP#", transl_subpart_name, "#TMP#", NullS); - else if (name_variant == RENAMED_PART_NAME) + else + { + DBUG_ASSERT(name_variant == RENAMED_PART_NAME); end= strxnmov(out, outlen-1, in1, "#P#", transl_part_name, "#SP#", transl_subpart_name, "#REN#", NullS); + } if (end - out == static_cast<ptrdiff_t>(outlen-1)) { my_error(ER_PATH_LENGTH, MYF(0), diff --git a/sql/sql_partition_admin.cc b/sql/sql_partition_admin.cc index efddcbf3857..8168397da85 100644 --- a/sql/sql_partition_admin.cc +++ b/sql/sql_partition_admin.cc @@ -379,15 +379,13 @@ static bool exchange_name_with_ddl_log(THD *thd, */ /* call rename table from table to tmp-name */ DBUG_EXECUTE_IF("exchange_partition_fail_3", - my_error(ER_ERROR_ON_RENAME, MYF(0), - name, tmp_name, 0); + my_error(ER_ERROR_ON_RENAME, MYF(0), name, tmp_name, 0); error_set= TRUE; goto err_rename;); DBUG_EXECUTE_IF("exchange_partition_abort_3", DBUG_SUICIDE();); if (file->ha_rename_table(name, tmp_name)) { - my_error(ER_ERROR_ON_RENAME, MYF(0), name, tmp_name, - my_errno); + my_error(ER_ERROR_ON_RENAME, MYF(0), name, tmp_name, my_errno); error_set= TRUE; goto err_rename; } @@ -398,8 +396,7 @@ static bool exchange_name_with_ddl_log(THD *thd, /* call rename table from partition to table */ DBUG_EXECUTE_IF("exchange_partition_fail_5", - my_error(ER_ERROR_ON_RENAME, MYF(0), - from_name, name, 0); + my_error(ER_ERROR_ON_RENAME, MYF(0), from_name, name, 0); error_set= TRUE; goto err_rename;); DBUG_EXECUTE_IF("exchange_partition_abort_5", DBUG_SUICIDE();); @@ -416,8 +413,7 @@ static bool exchange_name_with_ddl_log(THD *thd, /* call rename table from tmp-nam to partition */ DBUG_EXECUTE_IF("exchange_partition_fail_7", - my_error(ER_ERROR_ON_RENAME, MYF(0), - tmp_name, from_name, 0); + my_error(ER_ERROR_ON_RENAME, MYF(0), tmp_name, from_name, 0); error_set= TRUE; goto err_rename;); DBUG_EXECUTE_IF("exchange_partition_abort_7", DBUG_SUICIDE();); diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 243d9d524c2..b31c2f36db1 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -2230,10 +2230,8 @@ static int mysql_test_handler_read(Prepared_statement *stmt, if (!stmt->is_sql_prepare()) { if (!lex->result && !(lex->result= new (stmt->mem_root) select_send(thd))) - { - my_error(ER_OUTOFMEMORY, MYF(0), (int) sizeof(select_send)); DBUG_RETURN(1); - } + if (send_prep_stmt(stmt, ha_table->fields.elements) || lex->result->send_result_set_metadata(ha_table->fields, Protocol::SEND_EOF) || thd->protocol->flush()) @@ -4732,7 +4730,19 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor) if (error == 0 && this->lex->sql_command == SQLCOM_CALL) { if (is_sql_prepare()) + { + /* + Here we have the diagnostics area status already set to DA_OK. + sent_out_parameters() can raise errors when assigning OUT parameters: + DECLARE a DATETIME; + EXECUTE IMMEDIATE 'CALL p1(?)' USING a; + when the procedure p1 assigns a DATETIME-incompatible value (e.g. 10) + to the out parameter. Allow to overwrite status (to DA_ERROR). + */ + thd->get_stmt_da()->set_overwrite_status(true); thd->protocol_text.send_out_parameters(&this->lex->param_list); + thd->get_stmt_da()->set_overwrite_status(false); + } else thd->protocol->send_out_parameters(&this->lex->param_list); } diff --git a/sql/sql_priv.h b/sql/sql_priv.h index b3b041a3602..ba37d933f12 100644 --- a/sql/sql_priv.h +++ b/sql/sql_priv.h @@ -227,7 +227,7 @@ #define OPTIMIZER_SWITCH_EXISTS_TO_IN (1ULL << 28) #define OPTIMIZER_SWITCH_ORDERBY_EQ_PROP (1ULL << 29) #define OPTIMIZER_SWITCH_COND_PUSHDOWN_FOR_DERIVED (1ULL << 30) -#define OPTIMIZER_SWITCH_SPLIT_GROUPING_DERIVED (1ULL << 31) +#define OPTIMIZER_SWITCH_SPLIT_MATERIALIZED (1ULL << 31) #define OPTIMIZER_SWITCH_DEFAULT (OPTIMIZER_SWITCH_INDEX_MERGE | \ OPTIMIZER_SWITCH_INDEX_MERGE_UNION | \ @@ -254,7 +254,7 @@ OPTIMIZER_SWITCH_EXISTS_TO_IN | \ OPTIMIZER_SWITCH_ORDERBY_EQ_PROP | \ OPTIMIZER_SWITCH_COND_PUSHDOWN_FOR_DERIVED | \ - OPTIMIZER_SWITCH_SPLIT_GROUPING_DERIVED) + OPTIMIZER_SWITCH_SPLIT_MATERIALIZED) /* Replication uses 8 bytes to store SQL_MODE in the binary log. The day you @@ -327,6 +327,8 @@ /* Used to check GROUP BY list in the MODE_ONLY_FULL_GROUP_BY mode */ #define UNDEF_POS (-1) +#define IN_SUBQUERY_CONVERSION_THRESHOLD 1000 + #endif /* !MYSQL_CLIENT */ /* BINLOG_DUMP options */ diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 1d6aa0aaab1..7db9f49f25e 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -3414,7 +3414,8 @@ static bool get_string_parameter(char *to, const char *from, size_t length, uint from_numchars= cs->cset->numchars(cs, from, from + from_length); if (from_numchars > length / cs->mbmaxlen) { - my_error(ER_WRONG_STRING_LENGTH, MYF(0), from, name, (int) (length / cs->mbmaxlen)); + my_error(ER_WRONG_STRING_LENGTH, MYF(0), from, name, + (int) (length / cs->mbmaxlen)); return 1; } memcpy(to, from, from_length+1); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 51f577aa6b0..135e27a3a38 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -89,7 +89,6 @@ LEX_CSTRING distinct_key= {STRING_WITH_LEN("distinct_key")}; struct st_sargable_param; -static void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array); static bool make_join_statistics(JOIN *join, List<TABLE_LIST> &leaves, DYNAMIC_ARRAY *keyuse); static bool update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse, @@ -97,8 +96,6 @@ static bool update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse, uint tables, COND *conds, table_map table_map, SELECT_LEX *select_lex, SARGABLE_PARAM **sargables); -static bool sort_and_filter_keyuse(THD *thd, DYNAMIC_ARRAY *keyuse, - bool skip_unprefixed_keyparts); static int sort_keyuse(KEYUSE *a,KEYUSE *b); static bool are_tables_local(JOIN_TAB *jtab, table_map used_tables); static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse, @@ -1267,6 +1264,22 @@ JOIN::prepare(TABLE_LIST *tables_init, } } + /* + After setting up window functions, we may have discovered additional + used tables from the PARTITION BY and ORDER BY list. Update all items + that contain window functions. + */ + if (select_lex->have_window_funcs()) + { + List_iterator_fast<Item> it(select_lex->item_list); + Item *item; + while ((item= it++)) + { + if (item->with_window_func) + item->update_used_tables(); + } + } + With_clause *with_clause=select_lex->get_with_clause(); if (with_clause && with_clause->prepare_unreferenced_elements(thd)) DBUG_RETURN(1); @@ -1533,7 +1546,6 @@ int JOIN::optimize() if (optimization_state != JOIN::NOT_OPTIMIZED) return FALSE; optimization_state= JOIN::OPTIMIZATION_IN_PROGRESS; - is_for_splittable_grouping_derived= false; res= optimize_inner(); } if (!with_two_phase_optimization || @@ -1605,7 +1617,7 @@ JOIN::optimize_inner() /* Needed in case optimizer short-cuts, - set properly in make_tmp_tables_info() + set properly in make_aggr_tables_info() */ fields= &select_lex->item_list; @@ -1979,9 +1991,6 @@ int JOIN::optimize_stage2() if (subq_exit_fl) goto setup_subq_exit; - if (select_lex->handle_derived(thd->lex, DT_OPTIMIZE)) - DBUG_RETURN(1); - if (thd->check_killed()) DBUG_RETURN(1); @@ -1989,6 +1998,9 @@ int JOIN::optimize_stage2() if (get_best_combination()) DBUG_RETURN(1); + if (select_lex->handle_derived(thd->lex, DT_OPTIMIZE)) + DBUG_RETURN(1); + if (optimizer_flag(thd, OPTIMIZER_SWITCH_DERIVED_WITH_KEYS)) drop_unused_derived_keys(); @@ -4090,7 +4102,11 @@ JOIN::destroy() cleanup_item_list(tmp_all_fields1); cleanup_item_list(tmp_all_fields3); destroy_sj_tmp_tables(this); - delete_dynamic(&keyuse); + delete_dynamic(&keyuse); + if (save_qep) + delete(save_qep); + if (ext_keyuses_for_splitting) + delete(ext_keyuses_for_splitting); delete procedure; DBUG_RETURN(error); } @@ -4544,6 +4560,12 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list, DBUG_EXECUTE("opt", print_keyuse_array(keyuse_array);); } + for (s= stat; s < stat_end; s++) + { + if (s->table->is_splittable()) + s->add_keyuses_for_splitting(); + } + join->const_table_map= no_rows_const_tables; join->const_tables= const_count; eliminate_tables(join); @@ -5017,9 +5039,6 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list, if (join->choose_subquery_plan(all_table_map & ~join->const_table_map)) goto error; - if (join->improve_chosen_plan(join->thd)) - goto error; - DEBUG_SYNC(join->thd, "inside_make_join_statistics"); DBUG_RETURN(0); @@ -5049,23 +5068,6 @@ error: keyuse Pointer to possible keys *****************************************************************************/ -/// Used when finding key fields -struct KEY_FIELD { - Field *field; - Item_bool_func *cond; - Item *val; ///< May be empty if diff constant - uint level; - uint optimize; - bool eq_func; - /** - If true, the condition this struct represents will not be satisfied - when val IS NULL. - */ - bool null_rejecting; - bool *cond_guard; /* See KEYUSE::cond_guard */ - uint sj_pred_no; /* See KEYUSE::sj_pred_no */ -}; - /** Merge new key definitions to old ones, remove those not used in both. @@ -5716,7 +5718,7 @@ Item_func_ne::add_key_fields(JOIN *join, KEY_FIELD **key_fields, /* QQ: perhaps test for !is_local_field(args[1]) is not really needed here. Other comparison functions, e.g. Item_func_le, Item_func_gt, etc, - do not have this test. See Item_bool_func2::add_key_field_optimize_op(). + do not have this test. See Item_bool_func2::add_key_fieldoptimize_op(). Check with the optimizer team. */ if (is_local_field(args[0]) && !is_local_field(args[1])) @@ -5899,6 +5901,7 @@ add_keyuse(DYNAMIC_ARRAY *keyuse_array, KEY_FIELD *key_field, keyuse.null_rejecting= key_field->null_rejecting; keyuse.cond_guard= key_field->cond_guard; keyuse.sj_pred_no= key_field->sj_pred_no; + keyuse.validity_ref= 0; return (insert_dynamic(keyuse_array,(uchar*) &keyuse)); } @@ -5944,7 +5947,9 @@ add_key_part(DYNAMIC_ARRAY *keyuse_array, KEY_FIELD *key_field) key_field->val->used_tables()) { if (!field->can_optimize_hash_join(key_field->cond, key_field->val)) - return false; + return false; + if (form->is_splittable()) + form->add_splitting_info_for_key_field(key_field); /* If a key use is extracted from an equi-join predicate then it is added not only as a key use for every index whose component can @@ -5958,7 +5963,6 @@ add_key_part(DYNAMIC_ARRAY *keyuse_array, KEY_FIELD *key_field) return FALSE; } - static bool add_ft_keys(DYNAMIC_ARRAY *keyuse_array, JOIN_TAB *stat,COND *cond,table_map usable_tables) @@ -6020,6 +6024,7 @@ add_ft_keys(DYNAMIC_ARRAY *keyuse_array, keyuse.optimize= 0; keyuse.keypart_map= 0; keyuse.sj_pred_no= UINT_MAX; + keyuse.validity_ref= 0; return insert_dynamic(keyuse_array,(uchar*) &keyuse); } @@ -6307,8 +6312,8 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab, Special treatment for ft-keys. */ -static bool sort_and_filter_keyuse(THD *thd, DYNAMIC_ARRAY *keyuse, - bool skip_unprefixed_keyparts) +bool sort_and_filter_keyuse(THD *thd, DYNAMIC_ARRAY *keyuse, + bool skip_unprefixed_keyparts) { KEYUSE key_end, *prev, *save_pos, *use; uint found_eq_constant, i; @@ -6376,7 +6381,7 @@ static bool sort_and_filter_keyuse(THD *thd, DYNAMIC_ARRAY *keyuse, Update some values in keyuse for faster choose_plan() loop. */ -static void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array) +void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array) { KEYUSE *end,*keyuse= dynamic_element(keyuse_array, 0, KEYUSE*); @@ -6417,7 +6422,6 @@ static void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array) } - /** Check for the presence of AGGFN(DISTINCT a) queries that may be subject to loose index scan. @@ -6725,6 +6729,7 @@ best_access_path(JOIN *join, bool best_uses_jbuf= FALSE; MY_BITMAP *eq_join_set= &s->table->eq_join_set; KEYUSE *hj_start_key= 0; + SplM_plan_info *spl_plan= 0; disable_jbuf= disable_jbuf || idx == join->const_tables; @@ -6734,7 +6739,10 @@ best_access_path(JOIN *join, bitmap_clear_all(eq_join_set); loose_scan_opt.init(join, s, remaining_tables); - + + if (s->table->is_splittable()) + spl_plan= s->choose_best_splitting(record_count, remaining_tables); + if (s->keyuse) { /* Use key if possible */ KEYUSE *keyuse; @@ -6799,6 +6807,7 @@ best_access_path(JOIN *join, 2. we won't get two ref-or-null's */ if (!(remaining_tables & keyuse->used_tables) && + (!keyuse->validity_ref || *keyuse->validity_ref) && s->access_from_tables_is_allowed(keyuse->used_tables, join->sjm_lookup_tables) && !(ref_or_null_part && (keyuse->optimize & @@ -7111,6 +7120,7 @@ best_access_path(JOIN *join, tmp += s->startup_cost; loose_scan_opt.check_ref_access_part2(key, start_key, records, tmp); } /* not ft_key */ + if (tmp + 0.0001 < best_time - records/(double) TIME_FOR_COMPARE) { best_time= tmp + records/(double) TIME_FOR_COMPARE; @@ -7298,6 +7308,7 @@ best_access_path(JOIN *join, pos->ref_depend_map= best_ref_depends_map; pos->loosescan_picker.loosescan_key= MAX_KEY; pos->use_join_buffer= best_uses_jbuf; + pos->spl_plan= spl_plan; loose_scan_opt.save_to_position(s, loose_scan_pos); @@ -9352,192 +9363,15 @@ JOIN_TAB *next_depth_first_tab(JOIN* join, JOIN_TAB* tab) return tab; } -static -bool key_can_be_used_to_split_by_fields(KEY *key_info, uint used_key_parts, - List<Field> &fields) -{ - if (used_key_parts < fields.elements) - return false; - List_iterator_fast<Field> li(fields); - Field *fld; - KEY_PART_INFO *start= key_info->key_part; - KEY_PART_INFO *end= start + fields.elements; - while ((fld= li++)) - { - KEY_PART_INFO *key_part; - for (key_part= start; key_part < end; key_part++) - { - if (key_part->fieldnr == fld->field_index + 1) - break; - } - if (key_part == end) - return false; - } - return true; -} - -bool JOIN::check_for_splittable_grouping_derived(THD *thd) -{ - partition_list= 0; - st_select_lex_unit *unit= select_lex->master_unit(); - TABLE_LIST *derived= unit->derived; - if (!optimizer_flag(thd, OPTIMIZER_SWITCH_SPLIT_GROUPING_DERIVED)) - return false; - if (!(derived && derived->is_materialized_derived())) - return false; - if (unit->first_select()->next_select()) - return false; - if (derived->prohibit_cond_pushdown) - return false; - if (derived->is_recursive_with_table()) - return false; - if (group_list) - { - if (!select_lex->have_window_funcs()) - partition_list= group_list; - } - else if (select_lex->have_window_funcs() && - select_lex->window_specs.elements == 1) - { - partition_list= - select_lex->window_specs.head()->partition_list->first; - } - if (!partition_list) - return false; - - ORDER *ord; - TABLE *table= 0; - key_map ref_keys; - uint group_fields= 0; - ref_keys.set_all(); - for (ord= partition_list; ord; ord= ord->next, group_fields++) - { - Item *ord_item= *ord->item; - if (ord_item->real_item()->type() != Item::FIELD_ITEM) - return false; - Field *ord_field= ((Item_field *) (ord_item->real_item()))->field; - if (!table) - table= ord_field->table; - else if (table != ord_field->table) - return false; - ref_keys.intersect(ord_field->part_of_key); - } - if (ref_keys.is_clear_all()) - return false; - - uint i; - List<Field> grouping_fields; - List<Field> splitting_fields; - List_iterator<Item> li(fields_list); - for (ord= partition_list; ord; ord= ord->next) - { - Item *item; - i= 0; - while ((item= li++)) - { - if ((*ord->item)->eq(item, 0)) - break; - i++; - } - if (!item) - return false; - if (splitting_fields.push_back(derived->table->field[i], thd->mem_root)) - return false; - Item_field *ord_field= (Item_field *)(item->real_item()); - if (grouping_fields.push_back(ord_field->field, thd->mem_root)) - return false; - li.rewind(); - } - - for (i= 0; i < table->s->keys; i++) - { - if (!(ref_keys.is_set(i))) - continue; - KEY *key_info= table->key_info + i; - if (key_can_be_used_to_split_by_fields(key_info, - table->actual_n_key_parts(key_info), - grouping_fields)) - break; - } - if (i == table->s->keys) - return false; - - derived->table->splitting_fields= splitting_fields; - is_for_splittable_grouping_derived= true; - return true; -} - bool JOIN::check_two_phase_optimization(THD *thd) { - if (!check_for_splittable_grouping_derived(thd)) - return false; - return true; + if (check_for_splittable_materialized()) + return true; + return false; } -Item *JOIN_TAB::get_splitting_cond_for_grouping_derived(THD *thd) -{ - /* this is a stub */ - TABLE_LIST *derived= table->pos_in_table_list; - st_select_lex *sel= derived->get_unit()->first_select(); - Item *cond= 0; - table_map used_tables= OUTER_REF_TABLE_BIT; - POSITION *pos= join->best_positions; - for (; pos->table != this; pos++) - { - used_tables|= pos->table->table->map; - } - - if (!pos->key) - return 0; - - KEY *key_info= table->key_info + pos->key->key; - if (!key_can_be_used_to_split_by_fields(key_info, - key_info->user_defined_key_parts, - table->splitting_fields)) - return 0; - - create_ref_for_key(join, this, pos->key, - false, used_tables); - List<Item> cond_list; - KEY_PART_INFO *start= key_info->key_part; - KEY_PART_INFO *end= start + table->splitting_fields.elements; - List_iterator_fast<Field> li(table->splitting_fields); - Field *fld= li++; - for (ORDER *ord= sel->join->partition_list; ord; - ord= ord->next, fld= li++) - { - Item *left_item= (*ord->item)->build_clone(thd); - uint i= 0; - for (KEY_PART_INFO *key_part= start; key_part < end; key_part++, i++) - { - if (key_part->fieldnr == fld->field_index + 1) - break; - } - Item *right_item= ref.items[i]->build_clone(thd); - Item_func_eq *eq_item= 0; - right_item= right_item->build_clone(thd); - if (left_item && right_item) - { - right_item->walk(&Item::set_fields_as_dependent_processor, - false, join->select_lex); - right_item->update_used_tables(); - eq_item= new (thd->mem_root) Item_func_eq(thd, left_item, right_item); - } - if (!eq_item || cond_list.push_back(eq_item, thd->mem_root)) - return 0; - } - switch (cond_list.elements) { - case 0: break; - case 1: cond= cond_list.head(); break; - default: cond= new (thd->mem_root) Item_cond_and(thd, cond_list); - } - if (cond) - cond->fix_fields(thd,0); - return cond; -} - bool JOIN::inject_cond_into_where(Item *injected_cond) { Item *where_item= injected_cond; @@ -9572,48 +9406,6 @@ bool JOIN::inject_cond_into_where(Item *injected_cond) } -bool JOIN::push_splitting_cond_into_derived(THD *thd, Item *cond) -{ - enum_reopt_result reopt_result= REOPT_NONE; - table_map all_table_map= 0; - for (JOIN_TAB *tab= join_tab; - tab < join_tab + top_join_tab_count; tab++) - all_table_map|= tab->table->map; - reopt_result= reoptimize(cond, all_table_map & ~const_table_map, NULL); - if (reopt_result == REOPT_ERROR) - return true; - if (inject_cond_into_where(cond)) - return true; - if (cond->used_tables() & OUTER_REF_TABLE_BIT) - { - select_lex->uncacheable|= UNCACHEABLE_DEPENDENT_INJECTED; - st_select_lex_unit *unit= select_lex->master_unit(); - unit->uncacheable|= UNCACHEABLE_DEPENDENT_INJECTED; - } - return false; -} - -bool JOIN::improve_chosen_plan(THD *thd) -{ - for (JOIN_TAB *tab= join_tab + const_tables; - tab < join_tab + table_count; tab++) - { - TABLE_LIST *tbl= tab->table->pos_in_table_list; - if (tbl->is_materialized_derived()) - { - st_select_lex *sel= tbl->get_unit()->first_select(); - JOIN *derived_join= sel->join; - if (derived_join && derived_join->is_for_splittable_grouping_derived) - { - Item *cond= tab->get_splitting_cond_for_grouping_derived(thd); - if (cond && derived_join->push_splitting_cond_into_derived(thd, cond)) - return true; - } - } - } - return false; -} - static Item * const null_ptr= NULL; @@ -9682,6 +9474,9 @@ bool JOIN::get_best_combination() full_join=0; hash_join= FALSE; + if (fix_all_splittings_in_plan()) + DBUG_RETURN(TRUE); + fix_semijoin_strategies_for_picked_join_order(this); JOIN_TAB_RANGE *root_range; @@ -10022,6 +9817,7 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, do { if (!(~used_tables & keyuse->used_tables) && + (!keyuse->validity_ref || *keyuse->validity_ref) && j->keyuse_is_valid_for_access_in_chosen_plan(join, keyuse)) { if (are_tables_local(j, keyuse->val->used_tables())) @@ -10092,6 +9888,7 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, for (i=0 ; i < keyparts ; keyuse++,i++) { while (((~used_tables) & keyuse->used_tables) || + (keyuse->validity_ref && !(*keyuse->validity_ref)) || !j->keyuse_is_valid_for_access_in_chosen_plan(join, keyuse) || keyuse->keypart == NO_KEYPART || (keyuse->keypart != @@ -18092,7 +17889,6 @@ err: } - /****************************************************************************/ void *Virtual_tmp_table::operator new(size_t size, THD *thd) throw() diff --git a/sql/sql_select.h b/sql/sql_select.h index 662047c02e3..fbadca2255d 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -73,9 +73,45 @@ typedef struct keyuse_t { */ uint sj_pred_no; + /* + If this is NULL than KEYUSE is always enabled. + Otherwise it points to the enabling flag for this keyuse (true <=> enabled) + */ + bool *validity_ref; + bool is_for_hash_join() { return is_hash_join_key_no(key); } } KEYUSE; + +struct KEYUSE_EXT: public KEYUSE +{ + /* + This keyuse can be used only when the partial join being extended + contains the tables from this table map + */ + table_map needed_in_prefix; + /* The enabling flag for keyuses usable for splitting */ + bool validity_var; +}; + +/// Used when finding key fields +struct KEY_FIELD { + Field *field; + Item_bool_func *cond; + Item *val; ///< May be empty if diff constant + uint level; + uint optimize; + bool eq_func; + /** + If true, the condition this struct represents will not be satisfied + when val IS NULL. + */ + bool null_rejecting; + bool *cond_guard; /* See KEYUSE::cond_guard */ + uint sj_pred_no; /* See KEYUSE::sj_pred_no */ +}; + + #define NO_KEYPART ((uint)(-1)) class store_key; @@ -201,6 +237,8 @@ class SJ_TMP_TABLE; class JOIN_TAB_RANGE; class AGGR_OP; class Filesort; +struct SplM_plan_info; +class SplM_opt_info; typedef struct st_join_table { st_join_table() {} @@ -438,7 +476,7 @@ typedef struct st_join_table { will be turned to fields. These variables are pointing to tmp_fields_list[123]. Valid only for tmp tables and the last non-tmp table in the query plan. - @see JOIN::make_tmp_tables_info() + @see JOIN::make_aggr_tables_info() */ List<Item> *fields; /** List of all expressions in the select list */ @@ -614,8 +652,10 @@ typedef struct st_join_table { bool use_order() const; ///< Use ordering provided by chosen index? bool sort_table(); bool remove_duplicates(); - Item *get_splitting_cond_for_grouping_derived(THD *thd); - + void add_keyuses_for_splitting(); + SplM_plan_info *choose_best_splitting(double record_count, + table_map remaining_tables); + bool fix_splitting(SplM_plan_info *spl_plan, table_map remaining_tables); } JOIN_TAB; @@ -920,6 +960,9 @@ typedef struct st_position Firstmatch_picker firstmatch_picker; LooseScan_picker loosescan_picker; Sj_materialization_picker sjmat_picker; + + /* Info on splitting plan used at this position */ + SplM_plan_info *spl_plan; } POSITION; typedef Bounds_checked_array<Item_null_result*> Item_null_array; @@ -1053,11 +1096,13 @@ protected: /* Support for plan reoptimization with rewritten conditions. */ enum_reopt_result reoptimize(Item *added_where, table_map join_tables, Join_plan_state *save_to); + /* Choose a subquery plan for a table-less subquery. */ + bool choose_tableless_subquery_plan(); + +public: void save_query_plan(Join_plan_state *save_to); void reset_query_plan(); void restore_query_plan(Join_plan_state *restore_from); - /* Choose a subquery plan for a table-less subquery. */ - bool choose_tableless_subquery_plan(); public: JOIN_TAB *join_tab, **best_ref; @@ -1415,9 +1460,14 @@ public: */ bool implicit_grouping; - bool is_for_splittable_grouping_derived; bool with_two_phase_optimization; - ORDER *partition_list; + + /* Saved execution plan for this join */ + Join_plan_state *save_qep; + /* Info on splittability of the table materialized by this plan*/ + SplM_opt_info *spl_opt_info; + /* Contains info on keyuses usable for splitting */ + Dynamic_array<KEYUSE_EXT> *ext_keyuses_for_splitting; JOIN_TAB *sort_and_group_aggr_tab; @@ -1465,7 +1515,10 @@ public: need_distinct= 0; skip_sort_order= 0; with_two_phase_optimization= 0; - is_for_splittable_grouping_derived= 0; + save_qep= 0; + spl_opt_info= 0; + ext_keyuses_for_splitting= 0; + spl_opt_info= 0; need_tmp= 0; hidden_group_fields= 0; /*safety*/ error= 0; @@ -1674,10 +1727,12 @@ public: const char *message); JOIN_TAB *first_breadth_first_tab() { return join_tab; } bool check_two_phase_optimization(THD *thd); - bool check_for_splittable_grouping_derived(THD *thd); bool inject_cond_into_where(Item *injected_cond); - bool push_splitting_cond_into_derived(THD *thd, Item *cond); - bool improve_chosen_plan(THD *thd); + bool check_for_splittable_materialized(); + void add_keyuses_for_splitting(); + bool inject_best_splitting_cond(table_map remaining_tables); + bool fix_all_splittings_in_plan(); + bool transform_in_predicates_into_in_subq(THD *thd); private: /** @@ -2300,6 +2355,11 @@ bool open_tmp_table(TABLE *table); void setup_tmp_table_column_bitmaps(TABLE *table, uchar *bitmaps); double prev_record_reads(POSITION *positions, uint idx, table_map found_ref); void fix_list_after_tbl_changes(SELECT_LEX *new_parent, List<TABLE_LIST> *tlist); +double get_tmp_table_lookup_cost(THD *thd, double row_count, uint row_size); +double get_tmp_table_write_cost(THD *thd, double row_count, uint row_size); +void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array); +bool sort_and_filter_keyuse(THD *thd, DYNAMIC_ARRAY *keyuse, + bool skip_unprefixed_keyparts); struct st_cond_statistic { diff --git a/sql/sql_string.cc b/sql/sql_string.cc index 70ddf7b1241..d0d0e35000d 100644 --- a/sql/sql_string.cc +++ b/sql/sql_string.cc @@ -606,7 +606,7 @@ bool String::append(IO_CACHE* file, uint32 arg_length) return TRUE; if (my_b_read(file, (uchar*) Ptr + str_length, arg_length)) { - shrink(str_length); + shrink(str_length ? str_length : 1); return TRUE; } str_length+=arg_length; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 50b551a904f..58e29ad9b18 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -4993,7 +4993,8 @@ int create_table_impl(THD *thd, if (!frm_only && create_info->tmp_table()) { TABLE *table= thd->create_and_open_tmp_table(create_info->db_type, frm, - path, db, table_name, true); + path, db, table_name, true, + false); if (!table) { @@ -5089,7 +5090,8 @@ int mysql_create_table_no_lock(THD *thd, // Check if we hit FN_REFLEN bytes along with file extension. if (length+reg_ext_length > FN_REFLEN) { - my_error(ER_IDENT_CAUSES_TOO_LONG_PATH, MYF(0), (int) sizeof(path)-1, path); + my_error(ER_IDENT_CAUSES_TOO_LONG_PATH, MYF(0), (int) sizeof(path)-1, + path); return true; } } @@ -5228,7 +5230,7 @@ err: /* Write log if no error or if we already deleted a table */ if (!result || thd->log_current_statement) { - if (result && create_info->table_was_deleted) + if (result && create_info->table_was_deleted && pos_in_locked_tables) { /* Possible locked table was dropped. We should remove meta data locks @@ -9582,7 +9584,7 @@ bool mysql_alter_table(THD *thd, const char *new_db, const char *new_name, thd->create_and_open_tmp_table(new_db_type, &frm, alter_ctx.get_tmp_path(), alter_ctx.new_db, alter_ctx.tmp_name, - false))) + false, true))) goto err_new_table_cleanup; /* Set markers for fields in TABLE object for altered table. */ @@ -9597,10 +9599,12 @@ bool mysql_alter_table(THD *thd, const char *new_db, const char *new_name, &altered_table->s->all_set); restore_record(altered_table, s->default_values); // Create empty record /* Check that we can call default functions with default field values */ + thd->count_cuted_fields= CHECK_FIELD_EXPRESSION; altered_table->reset_default_fields(); if (altered_table->default_field && altered_table->update_default_fields(0, 1)) goto err_new_table_cleanup; + thd->count_cuted_fields= CHECK_FIELD_IGNORE; // Ask storage engine whether to use copy or in-place enum_alter_inplace_result inplace_supported= @@ -9725,7 +9729,8 @@ bool mysql_alter_table(THD *thd, const char *new_db, const char *new_name, } // It's now safe to take the table level lock. - if (lock_tables(thd, table_list, alter_ctx.tables_opened, 0)) + if (lock_tables(thd, table_list, alter_ctx.tables_opened, + MYSQL_LOCK_USE_MALLOC)) goto err_new_table_cleanup; if (ha_create_table(thd, alter_ctx.get_tmp_path(), @@ -9742,7 +9747,7 @@ bool mysql_alter_table(THD *thd, const char *new_db, const char *new_name, thd->create_and_open_tmp_table(new_db_type, &frm, alter_ctx.get_tmp_path(), alter_ctx.new_db, alter_ctx.tmp_name, - true); + true, true); if (!tmp_table) { goto err_new_table_cleanup; @@ -9776,7 +9781,7 @@ bool mysql_alter_table(THD *thd, const char *new_db, const char *new_name, thd->create_and_open_tmp_table(new_db_type, &frm, alter_ctx.get_tmp_path(), alter_ctx.new_db, alter_ctx.tmp_name, - true); + true, true); } if (!new_table) goto err_new_table_cleanup; diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index 53bc05e7c47..8a1cc1ada7a 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -1603,7 +1603,7 @@ err_with_lex_cleanup: thd->spcont= save_spcont; thd->variables.sql_mode= save_sql_mode; thd->reset_db(save_db.str, save_db.length); - /* Fall trough to error */ + /* Fall through to error */ } } diff --git a/sql/sql_union.cc b/sql/sql_union.cc index b3a7227678d..82745b61929 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -68,14 +68,14 @@ void select_unit::change_select() curr_sel= current_select_number; /* New SELECT processing starts */ DBUG_ASSERT(table->file->inited == 0); - switch (thd->lex->current_select->linkage) + step= thd->lex->current_select->linkage; + switch (step) { case INTERSECT_TYPE: intersect_mark->value= prev_step= curr_step; curr_step= current_select_number; - /* fall through */ + break; case EXCEPT_TYPE: - step= thd->lex->current_select->linkage; break; default: step= UNION_TYPE; diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 50ea5581cc9..c36c1fa2a93 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -651,7 +651,8 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, if (!res && mysql_bin_log.is_open()) { - String buff; + StringBuffer<128> buff(thd->variables.character_set_client); + DBUG_ASSERT(buff.charset()->mbminlen == 1); const LEX_STRING command[3]= {{ C_STRING_WITH_LEN("CREATE ") }, { C_STRING_WITH_LEN("ALTER ") }, diff --git a/sql/sql_window.cc b/sql/sql_window.cc index bf393ab1c4d..db34b77ddcb 100644 --- a/sql/sql_window.cc +++ b/sql/sql_window.cc @@ -315,14 +315,7 @@ setup_windows(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables, } List_iterator_fast<Item_window_func> li(win_funcs); - Item_window_func *win_func_item; - while ((win_func_item= li++)) - { - win_func_item->update_used_tables(); - } - - li.rewind(); - while ((win_func_item= li++)) + while (Item_window_func * win_func_item= li++) { if (win_func_item->check_result_type_of_order_item()) DBUG_RETURN(1); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 64b6ca6dd54..5dd5493203e 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -5424,12 +5424,8 @@ opt_part_values: partition_info *part_info= lex->part_info; if (! lex->is_partition_management()) { - if (part_info->part_type == RANGE_PARTITION) - my_yyabort_error((ER_PARTITION_REQUIRES_VALUES_ERROR, MYF(0), - "RANGE", "LESS THAN")); - if (part_info->part_type == LIST_PARTITION) - my_yyabort_error((ER_PARTITION_REQUIRES_VALUES_ERROR, MYF(0), - "LIST", "IN")); + if (part_info->error_if_requires_values()) + MYSQL_YYABORT; if (part_info->part_type == VERSIONING_PARTITION) my_yyabort_error((ER_VERS_WRONG_PARTS, MYF(0), lex->create_last_non_select_table->table_name)); @@ -9975,6 +9971,7 @@ column_default_non_parenthesized_expr: $3); if ($$ == NULL) MYSQL_YYABORT; + Lex->default_used= TRUE; } | VALUE_SYM '(' simple_ident_nospvar ')' { diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy index 61002142014..e96e8bdcb9a 100644 --- a/sql/sql_yacc_ora.yy +++ b/sql/sql_yacc_ora.yy @@ -9509,6 +9509,7 @@ column_default_non_parenthesized_expr: $3); if ($$ == NULL) MYSQL_YYABORT; + Lex->default_used= TRUE; } | VALUE_SYM '(' simple_ident_nospvar ')' { diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 43a016a524a..3b37fac5c39 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -2512,7 +2512,7 @@ export const char *optimizer_switch_names[]= "exists_to_in", "orderby_uses_equalities", "condition_pushdown_for_derived", - "split_grouping_derived", + "split_materialized", "default", NullS }; @@ -6053,10 +6053,11 @@ static Sys_var_mybool Sys_session_track_state_change( #endif //EMBEDDED_LIBRARY -static Sys_var_ulong Sys_in_subquery_conversion_threshold( - "in_subquery_conversion_threshold", +#ifndef DBUG_OFF +static Sys_var_uint Sys_in_subquery_conversion_threshold( + "in_predicate_conversion_threshold", "The minimum number of scalar elements in the value list of " "IN predicate that triggers its conversion to IN subquery", SESSION_VAR(in_subquery_conversion_threshold), CMD_LINE(OPT_ARG), - VALID_RANGE(0, ULONG_MAX), DEFAULT(1000), BLOCK_SIZE(1)); - + VALID_RANGE(0, UINT_MAX), DEFAULT(IN_SUBQUERY_CONVERSION_THRESHOLD), BLOCK_SIZE(1)); +#endif diff --git a/sql/table.cc b/sql/table.cc index 052b3e2fe01..d7e7e3f0a89 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -997,7 +997,7 @@ bool parse_vcol_defs(THD *thd, MEM_ROOT *mem_root, TABLE *table, Virtual_column_info **check_constraint_ptr= table->check_constraints; sql_mode_t saved_mode= thd->variables.sql_mode; Query_arena backup_arena; - Virtual_column_info *vcol; + Virtual_column_info *vcol= 0; StringBuffer<MAX_FIELD_WIDTH> expr_str; bool res= 1; DBUG_ENTER("parse_vcol_defs"); @@ -1168,7 +1168,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, uint new_frm_ver, field_pack_length, new_field_pack_flag; uint interval_count, interval_parts, read_length, int_length; uint db_create_options, keys, key_parts, n_length; - uint com_length, null_bit_pos, mysql57_vcol_null_bit_pos, bitmap_count; + uint com_length, null_bit_pos, UNINIT_VAR(mysql57_vcol_null_bit_pos), bitmap_count; uint i; bool use_hash, mysql57_null_bits= 0; char *keynames, *names, *comment_pos; @@ -2214,6 +2214,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, } } + key_first_info= keyinfo; for (uint key=0 ; key < keys ; key++,keyinfo++) { uint usable_parts= 0; @@ -2231,9 +2232,6 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, keyinfo->name.length+1); } - if (!key) - key_first_info= keyinfo; - if (ext_key_parts > share->key_parts && key) { KEY_PART_INFO *new_key_part= (keyinfo-1)->key_part + @@ -3054,6 +3052,14 @@ unpack_vcol_info_from_frm(THD *thd, MEM_ROOT *mem_root, TABLE *table, if (error) goto end; + if (lex.current_select->table_list.first[0].next_global) + { + /* We are using NEXT VALUE FOR sequence. Remember table name for open */ + TABLE_LIST *sequence= lex.current_select->table_list.first[0].next_global; + sequence->next_global= table->internal_tables; + table->internal_tables= sequence; + } + vcol_storage.vcol_info->set_vcol_type(vcol->get_vcol_type()); vcol_storage.vcol_info->stored_in_db= vcol->stored_in_db; vcol_storage.vcol_info->name= vcol->name; @@ -4751,6 +4757,9 @@ bool TABLE_LIST::create_field_translation(THD *thd) Query_arena *arena, backup; bool res= FALSE; DBUG_ENTER("TABLE_LIST::create_field_translation"); + DBUG_PRINT("enter", ("Alias: '%s' Unit: %p", + (alias ? alias : "<NULL>"), + get_unit())); if (thd->stmt_arena->is_conventional() || thd->stmt_arena->is_stmt_prepare_or_first_sp_execute()) @@ -6985,6 +6994,14 @@ void TABLE::create_key_part_by_field(KEY_PART_INFO *key_part_info, might be reused. */ key_part_info->store_length= key_part_info->length; + /* + For BIT fields null_bit is not set to 0 even if the field is defined + as NOT NULL, look at Field_bit::Field_bit + */ + if (!field->real_maybe_null()) + { + key_part_info->null_bit= 0; + } /* The total store length of the key part is the raw length of the field + @@ -7624,7 +7641,6 @@ int TABLE::update_virtual_fields(handler *h, enum_vcol_update_mode update_mode) swap_values= 1; break; case VCOL_UPDATE_FOR_DELETE: - /* Fall trough */ case VCOL_UPDATE_FOR_WRITE: update= bitmap_is_set(vcol_set, vf->field_index); break; diff --git a/sql/table.h b/sql/table.h index 53391559de3..6899b65eb57 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1104,6 +1104,8 @@ struct st_cond_statistic; /* Bitmap of table's fields */ typedef Bitmap<MAX_FIELDS> Field_map; +class SplM_opt_info; + struct TABLE { TABLE() {} /* Remove gcc warning */ @@ -1168,6 +1170,8 @@ public: TABLE_LIST *pos_in_table_list;/* Element referring to this table */ /* Position in thd->locked_table_list under LOCK TABLES */ TABLE_LIST *pos_in_locked_tables; + /* Tables used in DEFAULT and CHECK CONSTRAINT (normally sequence tables) */ + TABLE_LIST *internal_tables; /* Not-null for temporary tables only. Non-null values means this table is @@ -1381,7 +1385,13 @@ public: bool stats_is_read; /* Persistent statistics is read for the table */ bool histograms_are_read; MDL_ticket *mdl_ticket; - List<Field> splitting_fields; + + /* + This is used only for potentially splittable materialized tables and it + points to the info used by the optimizer to apply splitting optimization + */ + SplM_opt_info *spl_opt_info; + key_map keys_usable_for_splitting; void init(THD *thd, TABLE_LIST *tl); bool fill_item_list(List<Item> *item_list) const; @@ -1526,6 +1536,10 @@ public: bool with_cleanup); Field *find_field_by_name(LEX_CSTRING *str) const; bool export_structure(THD *thd, class Row_definition_list *defs); + bool is_splittable() { return spl_opt_info != NULL; } + void set_spl_opt_info(SplM_opt_info *spl_info); + void deny_splitting(); + void add_splitting_info_for_key_field(struct KEY_FIELD *key_field); /** System Versioning support @@ -1914,6 +1928,11 @@ struct TABLE_LIST { TABLE_LIST() {} /* Remove gcc warning */ + enum prelocking_types + { + PRELOCK_NONE, PRELOCK_ROUTINE, PRELOCK_FK + }; + /** Prepare TABLE_LIST that consists of one table instance to use in open_and_lock_tables @@ -1954,7 +1973,7 @@ struct TABLE_LIST size_t table_name_length_arg, const char *alias_arg, enum thr_lock_type lock_type_arg, - bool routine, + prelocking_types prelocking_type, TABLE_LIST *belong_to_view_arg, uint8 trg_event_map_arg, TABLE_LIST ***last_ptr) @@ -1962,8 +1981,10 @@ struct TABLE_LIST init_one_table(db_name_arg, db_length_arg, table_name_arg, table_name_length_arg, alias_arg, lock_type_arg); cacheable_table= 1; - prelocking_placeholder= routine ? ROUTINE : FK; - open_type= routine ? OT_TEMPORARY_OR_BASE : OT_BASE_ONLY; + prelocking_placeholder= prelocking_type; + open_type= (prelocking_type == PRELOCK_ROUTINE ? + OT_TEMPORARY_OR_BASE : + OT_BASE_ONLY); belong_to_view= belong_to_view_arg; trg_event_map= trg_event_map_arg; @@ -2252,7 +2273,7 @@ struct TABLE_LIST This TABLE_LIST object is just placeholder for prelocking, it will be used for implicit LOCK TABLES only and won't be used in real statement. */ - enum { USER, ROUTINE, FK } prelocking_placeholder; + prelocking_types prelocking_placeholder; /** Indicates that if TABLE_LIST object corresponds to the table/view which requires special handling. @@ -2507,6 +2528,9 @@ struct TABLE_LIST inline void set_merged_derived() { DBUG_ENTER("set_merged_derived"); + DBUG_PRINT("enter", ("Alias: '%s' Unit: %p", + (alias ? alias : "<NULL>"), + get_unit())); derived_type= ((derived_type & DTYPE_MASK) | DTYPE_TABLE | DTYPE_MERGE); set_check_merged(); @@ -2519,6 +2543,9 @@ struct TABLE_LIST void set_materialized_derived() { DBUG_ENTER("set_materialized_derived"); + DBUG_PRINT("enter", ("Alias: '%s' Unit: %p", + (alias ? alias : "<NULL>"), + get_unit())); derived_type= ((derived_type & (derived ? DTYPE_MASK : DTYPE_VIEW)) | DTYPE_TABLE | DTYPE_MATERIALIZE); set_check_materialized(); diff --git a/sql/temporary_tables.cc b/sql/temporary_tables.cc index 2c3cd0fe24e..a5088a012ae 100644 --- a/sql/temporary_tables.cc +++ b/sql/temporary_tables.cc @@ -65,7 +65,8 @@ TABLE *THD::create_and_open_tmp_table(handlerton *hton, const char *path, const char *db, const char *table_name, - bool open_in_engine) + bool open_in_engine, + bool open_internal_tables) { DBUG_ENTER("THD::create_and_open_tmp_table"); @@ -90,6 +91,15 @@ TABLE *THD::create_and_open_tmp_table(handlerton *hton, /* Free the TMP_TABLE_SHARE. */ free_tmp_table_share(share, false); + DBUG_RETURN(0); + } + + /* Open any related tables */ + if (open_internal_tables && table->internal_tables && + open_and_lock_internal_tables(table, open_in_engine)) + { + drop_temporary_table(table, NULL, false); + DBUG_RETURN(0); } } diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc index da92aa9bedb..54313281625 100644 --- a/sql/wsrep_mysqld.cc +++ b/sql/wsrep_mysqld.cc @@ -1264,6 +1264,16 @@ int wsrep_to_buf_helper( if (!ret && writer.write(>id_ev)) ret= 1; } #endif /* GTID_SUPPORT */ + if (wsrep_gtid_mode && thd->variables.gtid_seq_no) + { + Gtid_log_event gtid_event(thd, thd->variables.gtid_seq_no, + thd->variables.gtid_domain_id, + true, LOG_EVENT_SUPPRESS_USE_F, + true, 0); + gtid_event.server_id= thd->variables.server_id; + if (!gtid_event.is_valid()) ret= 0; + ret= writer.write(>id_event); + } /* if there is prepare query, add event for it */ if (!ret && thd->wsrep_TOI_pre_query) |