diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 12:33:02 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 12:33:02 +0000 |
commit | 4fa488fb0159c629483b7994aa84e73926b132b9 (patch) | |
tree | 182a19db69cdcb92be54cc6a5b0b9bfab28f80fd /sql | |
parent | Adding debian version 1:10.11.6-2. (diff) | |
download | mariadb-4fa488fb0159c629483b7994aa84e73926b132b9.tar.xz mariadb-4fa488fb0159c629483b7994aa84e73926b132b9.zip |
Merging upstream version 1:10.11.7.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
127 files changed, 4051 insertions, 1626 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 13d9d02c..66c7e385 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -55,7 +55,7 @@ INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/sql ${LIBFMT_INCLUDE_DIR} -${PCRE_INCLUDES} +${PCRE_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIR} ${SSL_INCLUDE_DIRS} ${CMAKE_BINARY_DIR}/sql @@ -180,6 +180,7 @@ SET (SQL_SOURCE table_cache.cc encryption.cc temporary_tables.cc json_table.cc proxy_protocol.cc backup.cc xa.cc + socketpair.c socketpair.h ${CMAKE_CURRENT_BINARY_DIR}/lex_hash.h ${CMAKE_CURRENT_BINARY_DIR}/lex_token.h ${GEN_SOURCES} diff --git a/sql/debug.cc b/sql/debug.cc index a0e2340e..beb66775 100644 --- a/sql/debug.cc +++ b/sql/debug.cc @@ -40,7 +40,8 @@ static bool debug_decrement_counter(const LEX_CSTRING *name) THD *thd= current_thd; user_var_entry *entry= (user_var_entry*) my_hash_search(&thd->user_vars, (uchar*) name->str, name->length); - if (!entry || entry->type != INT_RESULT || ! entry->value) + if (!entry || !entry->value || + entry->type_handler()->result_type() != INT_RESULT) return 0; (*(ulonglong*) entry->value)= (*(ulonglong*) entry->value)-1; return !*(ulonglong*) entry->value; diff --git a/sql/event_queue.cc b/sql/event_queue.cc index ebd2dfee..7ed4d8c6 100644 --- a/sql/event_queue.cc +++ b/sql/event_queue.cc @@ -656,7 +656,6 @@ Event_queue::get_top_for_execution_if_time(THD *thd, top->status= Event_parse_data::DISABLED; DBUG_PRINT("info", ("event %s status is %d", top->name.str, top->status)); - top->execution_count++; (*event_name)->dropped= top->dropped; /* Save new values of last_executed timestamp and event status on stack diff --git a/sql/field.cc b/sql/field.cc index e94d5c19..76b7ff61 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -4741,30 +4741,6 @@ bool Field_longlong::is_max() single precision float ****************************************************************************/ -Field_float::Field_float(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, - uchar null_bit_arg, - enum utype unireg_check_arg, - const LEX_CSTRING *field_name_arg, - decimal_digits_t dec_arg, - bool zero_arg, bool unsigned_arg) - :Field_real(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, - unireg_check_arg, field_name_arg, - (dec_arg >= FLOATING_POINT_DECIMALS ? NOT_FIXED_DEC : dec_arg), - zero_arg, unsigned_arg) -{ -} - -Field_float::Field_float(uint32 len_arg, bool maybe_null_arg, - const LEX_CSTRING *field_name_arg, - decimal_digits_t dec_arg) - :Field_real((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0, (uint) 0, - NONE, field_name_arg, - (dec_arg >= FLOATING_POINT_DECIMALS ? NOT_FIXED_DEC : dec_arg), - 0, 0) -{ -} - - int Field_float::store(const char *from,size_t len,CHARSET_INFO *cs) { int error; @@ -4913,40 +4889,6 @@ Binlog_type_info Field_float::binlog_type_info() const double precision floating point numbers ****************************************************************************/ -Field_double::Field_double(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, - uchar null_bit_arg, - enum utype unireg_check_arg, - const LEX_CSTRING *field_name_arg, - decimal_digits_t dec_arg, - bool zero_arg, bool unsigned_arg) - :Field_real(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, - unireg_check_arg, field_name_arg, - (dec_arg >= FLOATING_POINT_DECIMALS ? NOT_FIXED_DEC : dec_arg), - zero_arg, unsigned_arg) -{ -} - -Field_double::Field_double(uint32 len_arg, bool maybe_null_arg, - const LEX_CSTRING *field_name_arg, - decimal_digits_t dec_arg) - :Field_real((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "" : 0, (uint) 0, - NONE, field_name_arg, - (dec_arg >= FLOATING_POINT_DECIMALS ? NOT_FIXED_DEC : dec_arg), - 0, 0) -{ -} - -Field_double::Field_double(uint32 len_arg, bool maybe_null_arg, - const LEX_CSTRING *field_name_arg, - decimal_digits_t dec_arg, bool not_fixed_arg) - :Field_real((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "" : 0, (uint) 0, - NONE, field_name_arg, - (dec_arg >= FLOATING_POINT_DECIMALS ? NOT_FIXED_DEC : dec_arg), - 0, 0) -{ - not_fixed= not_fixed_arg; -} - int Field_double::store(const char *from,size_t len,CHARSET_INFO *cs) { int error; @@ -7743,7 +7685,20 @@ int Field_string::cmp(const uchar *a_ptr, const uchar *b_ptr) const a_ptr, field_length, b_ptr, field_length, Field_string::char_length(), - MY_STRNNCOLLSP_NCHARS_EMULATE_TRIMMED_TRAILING_SPACES); + 0); +} + + +int Field_string::cmp_prefix(const uchar *a_ptr, const uchar *b_ptr, + size_t prefix_char_len) const +{ + size_t field_len= table->field[field_index]->field_length; + + return field_charset()->coll->strnncollsp_nchars(field_charset(), + a_ptr, field_len, + b_ptr, field_len, + prefix_char_len, + 0); } @@ -11396,6 +11351,7 @@ void Field::set_warning_truncated_wrong_value(const char *type_arg, void Field::raise_note_cannot_use_key_part(THD *thd, uint keynr, uint part, const LEX_CSTRING &op, + CHARSET_INFO *op_collation, Item *value, Data_type_compatibility reason) const @@ -11416,7 +11372,7 @@ void Field::raise_note_cannot_use_key_part(THD *thd, case Data_type_compatibility::INCOMPATIBLE_COLLATION: { const LEX_CSTRING colf(charset()->coll_name); - const LEX_CSTRING colv(value->collation.collation->coll_name); + const LEX_CSTRING colv(op_collation->coll_name); push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_UNKNOWN_ERROR, "Cannot use key %`.*s part[%u] for lookup: " diff --git a/sql/field.h b/sql/field.h index c456b2e7..8563375f 100644 --- a/sql/field.h +++ b/sql/field.h @@ -1657,6 +1657,7 @@ public: void print_key_value_binary(String *out, const uchar* key, uint32 length); void raise_note_cannot_use_key_part(THD *thd, uint keynr, uint part, const LEX_CSTRING &op, + CHARSET_INFO *op_collation, Item *value, const Data_type_compatibility reason) const; @@ -1836,6 +1837,16 @@ public: return flags & (VERS_ROW_START | VERS_ROW_END); } + bool vers_sys_start() const + { + return flags & VERS_ROW_START; + } + + bool vers_sys_end() const + { + return flags & VERS_ROW_END; + } + bool vers_update_unversioned() const { return flags & VERS_UPDATE_UNVERSIONED_FLAG; @@ -2137,7 +2148,7 @@ public: const LEX_CSTRING *field_name_arg, const DTCollation &collation); decimal_digits_t decimals() const override - { return is_created_from_null_item ? 0 : NOT_FIXED_DEC; } + { return is_created_from_null_item ? 0 : DECIMAL_NOT_SPECIFIED; } int save_in_field(Field *to) override { return save_in_field_str(to); } bool memcpy_field_possible(const Field *from) const override { @@ -2307,7 +2318,7 @@ public: Information_schema_numeric_attributes information_schema_numeric_attributes() const override { - return dec == NOT_FIXED_DEC ? + return dec == DECIMAL_NOT_SPECIFIED ? Information_schema_numeric_attributes(field_length) : Information_schema_numeric_attributes(field_length, dec); } @@ -2879,15 +2890,24 @@ public: integers. But in all other cases we treat it as TIME_RESULT! */ }; +static inline decimal_digits_t fix_dec_arg(decimal_digits_t dec_arg) +{ return dec_arg >= FLOATING_POINT_DECIMALS ? DECIMAL_NOT_SPECIFIED : dec_arg; } class Field_float final :public Field_real { public: Field_float(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg, - decimal_digits_t dec_arg,bool zero_arg,bool unsigned_arg); + decimal_digits_t dec_arg,bool zero_arg,bool unsigned_arg) + :Field_real(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg, + fix_dec_arg(dec_arg), zero_arg, unsigned_arg) + { } Field_float(uint32 len_arg, bool maybe_null_arg, - const LEX_CSTRING *field_name_arg, decimal_digits_t dec_arg); + const LEX_CSTRING *field_name_arg, decimal_digits_t dec_arg) + :Field_real((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0, (uint) 0, + NONE, field_name_arg, fix_dec_arg(dec_arg), 0, 0) + { } const Type_handler *type_handler() const override { return &type_handler_float; } enum ha_base_keytype key_type() const override { return HA_KEYTYPE_FLOAT; } @@ -2920,12 +2940,24 @@ public: Field_double(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg, - decimal_digits_t dec_arg,bool zero_arg,bool unsigned_arg); + decimal_digits_t dec_arg,bool zero_arg,bool unsigned_arg) + :Field_real(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, + unireg_check_arg, field_name_arg, + fix_dec_arg(dec_arg), zero_arg, unsigned_arg) + { } Field_double(uint32 len_arg, bool maybe_null_arg, - const LEX_CSTRING *field_name_arg, decimal_digits_t dec_arg); + const LEX_CSTRING *field_name_arg, decimal_digits_t dec_arg) + :Field_real((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "" : 0, (uint) 0, + NONE, field_name_arg, fix_dec_arg(dec_arg), 0, 0) + { } Field_double(uint32 len_arg, bool maybe_null_arg, const LEX_CSTRING *field_name_arg, - decimal_digits_t dec_arg, bool not_fixed_arg); + decimal_digits_t dec_arg, bool not_fixed_arg) + :Field_real((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "" : 0, (uint) 0, + NONE, field_name_arg, fix_dec_arg(dec_arg), 0, 0) + { + not_fixed= not_fixed_arg; + } void init_for_tmp_table(Field *org_field, TABLE *new_table) override { Field::init_for_tmp_table(org_field, new_table); @@ -4056,6 +4088,8 @@ public: String *val_str(String *, String *) override; my_decimal *val_decimal(my_decimal *) override; int cmp(const uchar *,const uchar *) const override; + int cmp_prefix(const uchar *a, const uchar *b, size_t prefix_char_len) const + override; void sort_string(uchar *buff,uint length) override; void update_data_type_statistics(Data_type_statistics *st) const override { @@ -4078,9 +4112,6 @@ public: bool compatible_field_size(uint field_metadata, const Relay_log_info *rli, uint16 mflags, int *order_var) const override; uint row_pack_length() const override { return field_length; } - int pack_cmp(const uchar *a,const uchar *b,uint key_length, - bool insert_or_update); - int pack_cmp(const uchar *b,uint key_length,bool insert_or_update); uint packed_col_length(const uchar *to, uint length) override; uint max_packed_col_length(uint max_length) override; uint size_of() const override { return sizeof *this; } diff --git a/sql/filesort.h b/sql/filesort.h index ebb521e2..8c7931e7 100644 --- a/sql/filesort.h +++ b/sql/filesort.h @@ -56,8 +56,11 @@ public: bool using_pq; /* TRUE means sort operation must produce table rowids. - FALSE means that it halso has an option of producing {sort_key, - addon_fields} pairs. + FALSE means that it also has an option of producing {sort_key, addon_fields} + pairs. + + Usually initialized with value of join_tab->keep_current_rowid to allow for + a call to table->file->position() using these table rowids. */ bool sort_positions; /* diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 5eadbe7e..e86badd5 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -4312,9 +4312,12 @@ int ha_partition::external_lock(THD *thd, int lock_type) (void) (*file)->ha_external_lock(thd, lock_type); } while (*(++file)); } - if (lock_type == F_WRLCK && m_part_info->part_expr) - m_part_info->part_expr->walk(&Item::register_field_in_read_map, 1, 0); - + if (lock_type == F_WRLCK) + { + if (m_part_info->part_expr) + m_part_info->part_expr->walk(&Item::register_field_in_read_map, 1, 0); + need_info_for_auto_inc(); + } DBUG_RETURN(0); err_handler: @@ -4628,33 +4631,8 @@ int ha_partition::write_row(const uchar * buf) */ if (have_auto_increment) { - if (!table_share->next_number_keypart) - if (unlikely(error= update_next_auto_inc_val())) - goto exit; - - /* - If we have failed to set the auto-increment value for this row, - it is highly likely that we will not be able to insert it into - the correct partition. We must check and fail if necessary. - */ if (unlikely(error= update_auto_increment())) goto exit; - - /* - Don't allow generation of auto_increment value the partitions handler. - If a partitions handler would change the value, then it might not - match the partition any longer. - This can occur if 'SET INSERT_ID = 0; INSERT (NULL)', - So allow this by adding 'MODE_NO_AUTO_VALUE_ON_ZERO' to sql_mode. - The partitions handler::next_insert_id must always be 0. Otherwise - we need to forward release_auto_increment, or reset it for all - partitions. - */ - if (table->next_number_field->val_int() == 0) - { - table->auto_increment_field_not_null= TRUE; - thd->variables.sql_mode|= MODE_NO_AUTO_VALUE_ON_ZERO; - } } old_map= dbug_tmp_use_all_columns(table, &table->read_set); error= m_part_info->get_partition_id(m_part_info, &part_id, &func_value); @@ -11017,10 +10995,7 @@ void ha_partition::get_auto_increment(ulonglong offset, ulonglong increment, else { THD *thd= ha_thd(); - /* - This is initialized in the beginning of the first write_row call. - */ - DBUG_ASSERT(part_share->auto_inc_initialized); + update_next_auto_inc_val(); /* Get a lock for handling the auto_increment in part_share for avoiding two concurrent statements getting the same number. diff --git a/sql/ha_partition.h b/sql/ha_partition.h index 49e212f6..4b82bfb3 100644 --- a/sql/ha_partition.h +++ b/sql/ha_partition.h @@ -1408,9 +1408,8 @@ private: { ulonglong nr= (((Field_num*) field)->unsigned_flag || field->val_int() > 0) ? field->val_int() : 0; + update_next_auto_inc_val(); lock_auto_increment(); - DBUG_ASSERT(part_share->auto_inc_initialized || - !can_use_for_auto_inc_init()); /* must check when the mutex is taken */ if (nr >= part_share->next_auto_inc_val) part_share->next_auto_inc_val= nr + 1; diff --git a/sql/ha_sequence.cc b/sql/ha_sequence.cc index bab06147..92b307ff 100644 --- a/sql/ha_sequence.cc +++ b/sql/ha_sequence.cc @@ -271,13 +271,26 @@ int ha_sequence::write_row(const uchar *buf) } #ifdef WITH_WSREP - /* We need to start Galera transaction for select NEXT VALUE FOR - sequence if it is not yet started. Note that ALTER is handled - as TOI. */ - if (WSREP_ON && WSREP(thd) && - !thd->wsrep_trx().active() && - wsrep_thd_is_local(thd)) - wsrep_start_transaction(thd, thd->wsrep_next_trx_id()); + if (WSREP_ON && WSREP(thd) && wsrep_thd_is_local(thd)) + { + if (sequence_locked && + (wsrep_thd_is_SR(thd) || wsrep_streaming_enabled(thd))) + { + my_error(ER_NOT_SUPPORTED_YET, MYF(0), + "SEQUENCEs with streaming replication in Galera cluster"); + DBUG_RETURN(HA_ERR_UNSUPPORTED); + } + + /* + We need to start Galera transaction for select NEXT VALUE FOR + sequence if it is not yet started. Note that ALTER is handled + as TOI. + */ + if (!thd->wsrep_trx().active()) + { + wsrep_start_transaction(thd, thd->wsrep_next_trx_id()); + } + } #endif if (likely(!(error= file->update_first_row(buf)))) diff --git a/sql/handler.cc b/sql/handler.cc index 230bcf5e..17cf018b 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -619,7 +619,7 @@ int ha_finalize_handlerton(st_plugin_int *plugin) const char *hton_no_exts[]= { 0 }; - +static bool ddl_recovery_done= false; int ha_initialize_handlerton(st_plugin_int *plugin) { @@ -769,6 +769,9 @@ int ha_initialize_handlerton(st_plugin_int *plugin) resolve_sysvar_table_options(hton); update_discovery_counters(hton, 1); + if (ddl_recovery_done && hton->signal_ddl_recovery_done) + hton->signal_ddl_recovery_done(hton); + DBUG_RETURN(ret); err_deinit: @@ -955,7 +958,8 @@ static my_bool signal_ddl_recovery_done(THD *, plugin_ref plugin, void *) { handlerton *hton= plugin_hton(plugin); if (hton->signal_ddl_recovery_done) - (hton->signal_ddl_recovery_done)(hton); + if ((hton->signal_ddl_recovery_done)(hton)) + plugin_ref_to_int(plugin)->state= PLUGIN_IS_DELETED; return 0; } @@ -965,6 +969,7 @@ void ha_signal_ddl_recovery_done() DBUG_ENTER("ha_signal_ddl_recovery_done"); plugin_foreach(NULL, signal_ddl_recovery_done, MYSQL_STORAGE_ENGINE_PLUGIN, NULL); + ddl_recovery_done= true; DBUG_VOID_RETURN; } @@ -7642,7 +7647,12 @@ int handler::ha_write_row(const uchar *buf) { DBUG_ASSERT(inited == NONE || lookup_handler != this); if ((error= check_duplicate_long_entries(buf))) + { + if (table->next_number_field && buf == table->record[0]) + if (int err= update_auto_increment()) + error= err; DBUG_RETURN(error); + } } MYSQL_INSERT_ROW_START(table_share->db.str, table_share->table_name.str); diff --git a/sql/handler.h b/sql/handler.h index 50ec0ed1..a8484e2a 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -1495,7 +1495,7 @@ struct handlerton const LEX_CUSTRING *version, ulonglong create_id); /* Called for all storage handlers after ddl recovery is done */ - void (*signal_ddl_recovery_done)(handlerton *hton); + int (*signal_ddl_recovery_done)(handlerton *hton); /* Optional clauses in the CREATE/ALTER TABLE diff --git a/sql/item.cc b/sql/item.cc index c6d51e47..6f9eb9d1 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -2674,7 +2674,11 @@ bool Type_std_attributes::agg_item_set_converter(const DTCollation &coll, if (conv->fix_fields_if_needed(thd, arg)) return TRUE; - if (!thd->stmt_arena->is_conventional()) + if (!thd->stmt_arena->is_conventional() && + ((!thd->lex->current_select && + (thd->stmt_arena->is_stmt_prepare_or_first_sp_execute() || + thd->stmt_arena->is_stmt_prepare_or_first_stmt_execute())) || + thd->lex->current_select->first_cond_optimization)) { Query_arena *arena, backup; arena= thd->activate_stmt_arena_if_needed(&backup); @@ -2798,11 +2802,11 @@ Item_sp::func_name_cstring(THD *thd, bool is_package_function) const quoted `pkg` and `func` separately, so the entire result looks like: `db`.`pkg`.`func` */ - Database_qualified_name tmp= Database_qualified_name::split(m_name->m_name); - DBUG_ASSERT(tmp.m_db.length); - append_identifier(thd, &qname, &tmp.m_db); + Identifier_chain2 tmp= Identifier_chain2::split(m_name->m_name); + DBUG_ASSERT(tmp[0].length); + append_identifier(thd, &qname, &tmp[0]); qname.append('.'); - append_identifier(thd, &qname, &tmp.m_name); + append_identifier(thd, &qname, &tmp[1]); } else append_identifier(thd, &qname, &m_name->m_name); @@ -7040,7 +7044,25 @@ Item *Item_float::neg(THD *thd) else if (value < 0 && max_length) max_length--; value= -value; - presentation= 0; + if (presentation) + { + if (*presentation == '-') + { + // Strip double minus: -(-1) -> '1' instead of '--1' + presentation++; + } + else + { + size_t presentation_length= strlen(presentation); + if (char *tmp= (char*) thd->alloc(presentation_length + 2)) + { + tmp[0]= '-'; + // Copy with the trailing '\0' + memcpy(tmp + 1, presentation, presentation_length + 1); + presentation= tmp; + } + } + } name= null_clex_str; return this; } @@ -28,6 +28,7 @@ #include "field.h" /* Derivation */ #include "sql_type.h" #include "sql_time.h" +#include "sql_schema.h" #include "mem_root_array.h" #include "cset_narrowing.h" @@ -1015,6 +1016,19 @@ public: expressions with subqueries in the ORDER/GROUP clauses. */ String *val_str() { return val_str(&str_value); } + String *val_str_null_to_empty(String *to) + { + String *res= val_str(to); + if (res) + return res; + to->set_charset(collation.collation); + to->length(0); + return to; + } + String *val_str_null_to_empty(String *to, bool null_to_empty) + { + return null_to_empty ? val_str_null_to_empty(to) : val_str(to); + } virtual Item_func *get_item_func() { return NULL; } const MY_LOCALE *locale_from_val_str(); @@ -1986,7 +2000,8 @@ public: QT_ITEM_IDENT_SKIP_DB_NAMES | QT_ITEM_IDENT_SKIP_TABLE_NAMES | QT_NO_DATA_EXPANSION | - QT_TO_SYSTEM_CHARSET), + QT_TO_SYSTEM_CHARSET | + QT_FOR_FRM), LOWEST_PRECEDENCE); } virtual void print(String *str, enum_query_type query_type); @@ -5482,6 +5497,14 @@ public: return (this->*processor)(arg); } /* + Built-in schema, e.g. mariadb_schema, oracle_schema, maxdb_schema + */ + virtual const Schema *schema() const + { + // A function does not belong to a built-in schema by default + return NULL; + } + /* This method is used for debug purposes to print the name of an item to the debug log. The second use of this method is as a helper function of print() and error messages, where it is diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index bfe03e1c..c1654bfd 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -457,40 +457,6 @@ void Item_bool_func::raise_note_if_key_become_unused(THD *thd, const Item_args & } -bool Item_func::setup_args_and_comparator(THD *thd, Arg_comparator *cmp) -{ - DBUG_ASSERT(arg_count >= 2); // Item_func_nullif has arg_count == 3 - - if (args[0]->cmp_type() == STRING_RESULT && - args[1]->cmp_type() == STRING_RESULT) - { - CHARSET_INFO *tmp; - /* - Use charset narrowing only for equalities, as that would allow - to construct ref access. - Non-equality comparisons with constants work without charset narrowing, - the constant gets converted. - Non-equality comparisons with non-constants would need narrowing to - enable range optimizer to handle e.g. - t1.mb3key_col <= const_table.mb4_col - But this doesn't look important. - */ - bool allow_narrowing= MY_TEST(functype()==Item_func::EQ_FUNC || - functype()==Item_func::EQUAL_FUNC); - - if (agg_arg_charsets_for_comparison(&tmp, &args[0], &args[1], - allow_narrowing)) - return true; - cmp->m_compare_collation= tmp; - } - // Convert constants when compared to int/year field - DBUG_ASSERT(functype() != LIKE_FUNC); - convert_const_compared_to_int_field(thd); - - return cmp->set_cmp_func(thd, this, &args[0], &args[1], true); -} - - /* Comparison operators remove arguments' dependency on PAD_CHAR_TO_FULL_LENGTH in case of PAD SPACE comparison collations: trailing spaces do not affect @@ -519,8 +485,15 @@ bool Item_bool_rowready_func2::fix_length_and_dec(THD *thd) if (!args[0] || !args[1]) return FALSE; Item_args old_args(args[0], args[1]); - if (setup_args_and_comparator(thd, &cmp)) + convert_const_compared_to_int_field(thd); + Type_handler_hybrid_field_type tmp; + if (tmp.aggregate_for_comparison(func_name_cstring(), args, 2, false) || + tmp.type_handler()->Item_bool_rowready_func2_fix_length_and_dec(thd, + this)) + { + DBUG_ASSERT(thd->is_error()); return true; + } raise_note_if_key_become_unused(thd, old_args); return false; } @@ -540,21 +513,14 @@ bool Item_bool_rowready_func2::fix_length_and_dec(THD *thd) */ int Arg_comparator::set_cmp_func(THD *thd, Item_func_or_sum *owner_arg, + const Type_handler *compare_handler, Item **a1, Item **a2) { owner= owner_arg; set_null= set_null && owner_arg; a= a1; b= a2; - Item *tmp_args[2]= {*a1, *a2}; - Type_handler_hybrid_field_type tmp; - if (tmp.aggregate_for_comparison(owner_arg->func_name_cstring(), tmp_args, 2, - false)) - { - DBUG_ASSERT(thd->is_error()); - return 1; - } - m_compare_handler= tmp.type_handler(); + m_compare_handler= compare_handler; return m_compare_handler->set_comparator_func(thd, this); } @@ -605,6 +571,14 @@ bool Arg_comparator::set_cmp_func_string(THD *thd) We must set cmp_collation here as we may be called from for an automatic generated item, like in natural join. Allow reinterpted superset as subset. + Use charset narrowing only for equalities, as that would allow + to construct ref access. + Non-equality comparisons with constants work without charset narrowing, + the constant gets converted. + Non-equality comparisons with non-constants would need narrowing to + enable range optimizer to handle e.g. + t1.mb3key_col <= const_table.mb4_col + But this doesn't look important. */ bool allow_narrowing= false; if (owner->type() == Item::FUNC_ITEM) @@ -1509,6 +1483,23 @@ bool Item_in_optimizer::invisible_mode() } +bool Item_in_optimizer::walk(Item_processor processor, + bool walk_subquery, + void *arg) +{ + bool res= FALSE; + if (args[1]->type() == Item::SUBSELECT_ITEM && + ((Item_subselect *)args[1])->substype() != Item_subselect::EXISTS_SUBS && + !(((Item_subselect *)args[1])->substype() == Item_subselect::IN_SUBS && + ((Item_in_subselect *)args[1])->test_strategy(SUBS_IN_TO_EXISTS))) + res= args[0]->walk(processor, walk_subquery, arg); + if (!res) + res= args[1]->walk(processor, walk_subquery, arg); + + return res || (this->*processor)(arg); +} + + /** Add an expression cache for this subquery if it is needed @@ -2812,8 +2803,9 @@ Item_func_nullif::fix_length_and_dec(THD *thd) fix_char_length(args[2]->max_char_length()); set_maybe_null(); m_arg0= args[0]; - if (setup_args_and_comparator(thd, &cmp)) - return TRUE; + convert_const_compared_to_int_field(thd); + if (cmp.set_cmp_func(thd, this, &args[0], &args[1], true/*set_null*/)) + return true; /* A special code for EXECUTE..PREPARE. @@ -3484,7 +3476,13 @@ void Item_func_case_simple::print(String *str, enum_query_type query_type) void Item_func_decode_oracle::print(String *str, enum_query_type query_type) { - str->append(func_name_cstring()); + if (query_type & QT_FOR_FRM) + { + // 10.3 downgrade compatibility for FRM + str->append(STRING_WITH_LEN("decode_oracle")); + } + else + print_sql_mode_qualified_name(str, query_type); str->append('('); args[0]->print(str, query_type); for (uint i= 1, count= when_count() ; i <= count; i++) @@ -6092,7 +6090,7 @@ void Regexp_processor_pcre::init(CHARSET_INFO *data_charset, int extra_flags) // Convert text data to utf-8. m_library_charset= data_charset == &my_charset_bin ? - &my_charset_bin : &my_charset_utf8mb3_general_ci; + &my_charset_bin : &my_charset_utf8mb4_general_ci; m_conversion_is_needed= (data_charset != &my_charset_bin) && !my_charset_same(data_charset, m_library_charset); diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index a67dfb5d..3d5710cd 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -57,6 +57,7 @@ class Arg_comparator: public Sql_alloc // when one of arguments is NULL. int set_cmp_func(THD *thd, Item_func_or_sum *owner_arg, + const Type_handler *compare_handler, Item **a1, Item **a2); int compare_not_null_values(longlong val1, longlong val2) @@ -95,11 +96,24 @@ public: bool set_cmp_func_decimal(THD *thd); inline int set_cmp_func(THD *thd, Item_func_or_sum *owner_arg, - Item **a1, Item **a2, bool set_null_arg) + const Type_handler *compare_handler, + Item **a1, Item **a2, bool set_null_arg) { set_null= set_null_arg; - return set_cmp_func(thd, owner_arg, a1, a2); + return set_cmp_func(thd, owner_arg, compare_handler, a1, a2); } + int set_cmp_func(THD *thd, Item_func_or_sum *owner_arg, + Item **a1, Item **a2, bool set_null_arg) + { + Item *tmp_args[2]= { *a1, *a2 }; + Type_handler_hybrid_field_type tmp; + if (tmp.aggregate_for_comparison(owner_arg->func_name_cstring(), + tmp_args, 2, false)) + return 1; + return set_cmp_func(thd, owner_arg, tmp.type_handler(), + a1, a2, set_null_arg); + } + inline int compare() { return (this->*func)(); } int compare_string(); // compare args[0] & args[1] @@ -414,6 +428,7 @@ public: void fix_after_pullout(st_select_lex *new_parent, Item **ref, bool merge) override; bool invisible_mode(); + bool walk(Item_processor processor, bool walk_subquery, void *arg) override; void reset_cache() { cache= NULL; } void print(String *str, enum_query_type query_type) override; void restore_first_argument(); @@ -561,9 +576,17 @@ public: return this; } bool fix_length_and_dec(THD *thd) override; + bool fix_length_and_dec_generic(THD *thd, + const Type_handler *compare_handler) + { + DBUG_ASSERT(args == tmp_arg); + return cmp.set_cmp_func(thd, this, compare_handler, + tmp_arg, tmp_arg + 1, true/*set_null*/); + } int set_cmp_func(THD *thd) { - return cmp.set_cmp_func(thd, this, tmp_arg, tmp_arg + 1, true); + DBUG_ASSERT(args == tmp_arg); + return cmp.set_cmp_func(thd, this, tmp_arg, tmp_arg + 1, true/*set_null*/); } CHARSET_INFO *compare_collation() const override { return cmp.compare_collation(); } @@ -2439,9 +2462,10 @@ public: Item_func_decode_oracle(THD *thd, List<Item> &list) :Item_func_case_simple(thd, list) { } + const Schema *schema() const override { return &oracle_schema_ref; } LEX_CSTRING func_name_cstring() const override { - static LEX_CSTRING name= {STRING_WITH_LEN("decode_oracle") }; + static LEX_CSTRING name= {STRING_WITH_LEN("decode") }; return name; } void print(String *str, enum_query_type query_type) override; @@ -3020,7 +3044,7 @@ public: m_pcre(NULL), m_pcre_match_data(NULL), m_conversion_is_needed(true), m_is_const(0), m_library_flags(0), - m_library_charset(&my_charset_utf8mb3_general_ci) + m_library_charset(&my_charset_utf8mb4_general_ci) {} int default_regex_flags(); void init(CHARSET_INFO *data_charset, int extra_flags); diff --git a/sql/item_create.cc b/sql/item_create.cc index b26610a5..156a6b3f 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -127,6 +127,19 @@ protected: }; +class Create_func_addmonths : public Create_func_arg2 +{ +public: + virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2); + + static Create_func_addmonths s_singleton; + +protected: + Create_func_addmonths() = default; + virtual ~Create_func_addmonths() = default; +}; + + class Create_func_aes_encrypt : public Create_func_arg2 { public: @@ -258,6 +271,19 @@ protected: }; +class Create_func_collation : public Create_func_arg1 +{ +public: + virtual Item *create_1_arg(THD *thd, Item *arg1); + + static Create_func_collation s_singleton; + +protected: + Create_func_collation() = default; + virtual ~Create_func_collation() = default; +}; + + class Create_func_chr : public Create_func_arg1 { public: @@ -345,6 +371,20 @@ protected: }; +class Create_func_coalesce : public Create_native_func +{ +public: + virtual Item *create_native(THD *thd, const LEX_CSTRING *name, + List<Item> *item_list); + + static Create_func_coalesce s_singleton; + +protected: + Create_func_coalesce() = default; + virtual ~Create_func_coalesce() = default; +}; + + class Create_func_compress : public Create_func_arg1 { public: @@ -427,9 +467,6 @@ public: virtual Item *create_native(THD *thd, const LEX_CSTRING *name, List<Item> *item_list) { - if (thd->variables.sql_mode & MODE_ORACLE) - return Create_func_decode_oracle::s_singleton.create_native(thd, name, - item_list); if (unlikely(!item_list || item_list->elements != 2)) { my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); @@ -475,6 +512,19 @@ protected: }; +class Create_func_database : public Create_func_arg0 +{ +public: + virtual Item *create_builder(THD *thd); + + static Create_func_database s_singleton; + +protected: + Create_func_database() = default; + virtual ~Create_func_database() = default; +}; + + class Create_func_nvl2 : public Create_func_arg3 { public: @@ -581,6 +631,22 @@ protected: }; +class Create_func_date_format : public Create_native_func +{ +public: + virtual Item *create_native(THD *thd, const LEX_CSTRING *name, + List<Item> *item_list); + + static Create_func_date_format s_singleton; + +protected: + Create_func_date_format() = default; + virtual ~Create_func_date_format() = default; +}; + + + + class Create_func_dayname : public Create_func_arg1 { public: @@ -1439,6 +1505,31 @@ protected: virtual ~Create_func_octet_length() = default; }; +class Create_func_old_password : public Create_func_arg1 +{ +public: + virtual Item *create_1_arg(THD *thd, Item *arg1); + + static Create_func_old_password s_singleton; + +protected: + Create_func_old_password() = default; + virtual ~Create_func_old_password() = default; +}; + + +class Create_func_password : public Create_func_arg1 +{ +public: + virtual Item *create_1_arg(THD *thd, Item *arg1); + + static Create_func_password s_singleton; + +protected: + Create_func_password() = default; + virtual ~Create_func_password() = default; +}; + #ifndef DBUG_OFF class Create_func_like_range_min : public Create_func_arg2 @@ -1554,9 +1645,7 @@ public: virtual Item *create_native(THD *thd, const LEX_CSTRING *name, List<Item> *item_list) { - return thd->variables.sql_mode & MODE_ORACLE ? - create_native_oracle(thd, name, item_list) : - create_native_std(thd, name, item_list); + return create_native_std(thd, name, item_list); } static Create_func_lpad s_singleton; @@ -1689,6 +1778,32 @@ protected: }; +class Create_func_microsecond : public Create_func_arg1 +{ +public: + virtual Item *create_1_arg(THD *thd, Item *arg1); + + static Create_func_microsecond s_singleton; + +protected: + Create_func_microsecond() = default; + virtual ~Create_func_microsecond() = default; +}; + + +class Create_func_mod : public Create_func_arg2 +{ +public: + virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2); + + static Create_func_mod s_singleton; + +protected: + Create_func_mod() = default; + virtual ~Create_func_mod() = default; +}; + + class Create_func_monthname : public Create_func_arg1 { public: @@ -1815,6 +1930,19 @@ protected: }; +class Create_func_quarter : public Create_func_arg1 +{ +public: + virtual Item *create_1_arg(THD *thd, Item *arg1); + + static Create_func_quarter s_singleton; + +protected: + Create_func_quarter() = default; + virtual ~Create_func_quarter() = default; +}; + + class Create_func_quote : public Create_func_arg1 { public: @@ -1844,7 +1972,10 @@ protected: class Create_func_regexp_replace : public Create_func_arg3 { public: - virtual Item *create_3_arg(THD *thd, Item *arg1, Item *arg2, Item *arg3); + Item *create_3_arg(THD *thd, Item *arg1, Item *arg2, Item *arg3) override + { + return new (thd->mem_root) Item_func_regexp_replace(thd, arg1, arg2, arg3); + } static Create_func_regexp_replace s_singleton; @@ -1853,6 +1984,28 @@ protected: virtual ~Create_func_regexp_replace() = default; }; +Create_func_regexp_replace Create_func_regexp_replace::s_singleton; + + +class Create_func_regexp_replace_oracle : public Create_func_arg3 +{ +public: + Item *create_3_arg(THD *thd, Item *arg1, Item *arg2, Item *arg3) override + { + return new (thd->mem_root) Item_func_regexp_replace_oracle(thd, arg1, + arg2, arg3); + } + + static Create_func_regexp_replace_oracle s_singleton; + +protected: + Create_func_regexp_replace_oracle() = default; + virtual ~Create_func_regexp_replace_oracle() = default; +}; + +Create_func_regexp_replace_oracle + Create_func_regexp_replace_oracle::s_singleton; + class Create_func_regexp_substr : public Create_func_arg2 { @@ -1969,15 +2122,26 @@ protected: }; +class Create_func_row_count : public Create_func_arg0 +{ +public: + virtual Item *create_builder(THD *thd); + + static Create_func_row_count s_singleton; + +protected: + Create_func_row_count() = default; + virtual ~Create_func_row_count() = default; +}; + + class Create_func_rpad : public Create_native_func { public: virtual Item *create_native(THD *thd, const LEX_CSTRING *name, List<Item> *item_list) { - return thd->variables.sql_mode & MODE_ORACLE ? - create_native_oracle(thd, name, item_list) : - create_native_std(thd, name, item_list); + return create_native_std(thd, name, item_list); } static Create_func_rpad s_singleton; @@ -2418,6 +2582,20 @@ protected: }; +class Create_func_week : public Create_native_func +{ +public: + virtual Item *create_native(THD *thd, const LEX_CSTRING *name, + List<Item> *item_list); + + static Create_func_week s_singleton; + +protected: + Create_func_week() = default; + virtual ~Create_func_week() = default; +}; + + class Create_func_weekday : public Create_func_arg1 { public: @@ -2897,6 +3075,16 @@ Create_func_addtime::create_2_arg(THD *thd, Item *arg1, Item *arg2) } +Create_func_addmonths Create_func_addmonths::s_singleton; + +Item* +Create_func_addmonths::create_2_arg(THD *thd, Item *arg1, Item *arg2) +{ + return new (thd->mem_root) + Item_date_add_interval(thd, arg1, arg2, INTERVAL_MONTH, false); +} + + Create_func_aes_encrypt Create_func_aes_encrypt::s_singleton; Item* @@ -3026,6 +3214,15 @@ Create_func_ceiling::create_1_arg(THD *thd, Item *arg1) } +Create_func_collation Create_func_collation::s_singleton; + +Item* +Create_func_collation::create_1_arg(THD *thd, Item *arg1) +{ + return new (thd->mem_root) Item_func_collation(thd, arg1); +} + + Create_func_chr Create_func_chr::s_singleton; Item* @@ -3086,6 +3283,26 @@ Create_func_dyncol_json::create_1_arg(THD *thd, Item *arg1) return new (thd->mem_root) Item_func_dyncol_json(thd, arg1); } +Create_func_coalesce Create_func_coalesce::s_singleton; + +Item* +Create_func_coalesce::create_native(THD *thd, const LEX_CSTRING *name, + List<Item> *item_list) +{ + int arg_count= 0; + + if (item_list != NULL) + arg_count= item_list->elements; + + if (unlikely(arg_count < 1)) + { + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); + return NULL; + } + + return new (thd->mem_root) Item_func_coalesce(thd, *item_list); +} + Create_func_concat Create_func_concat::s_singleton; Item* @@ -3103,9 +3320,7 @@ Create_func_concat::create_native(THD *thd, const LEX_CSTRING *name, return NULL; } - return thd->variables.sql_mode & MODE_ORACLE ? - new (thd->mem_root) Item_func_concat_operator_oracle(thd, *item_list) : - new (thd->mem_root) Item_func_concat(thd, *item_list); + return new (thd->mem_root) Item_func_concat(thd, *item_list); } Create_func_concat_operator_oracle @@ -3182,6 +3397,16 @@ Create_func_connection_id::create_builder(THD *thd) } +Create_func_database Create_func_database::s_singleton; + +Item* +Create_func_database::create_builder(THD *thd) +{ + thd->lex->safe_to_cache_query= 0; + return new (thd->mem_root) Item_func_database(thd); +} + + Create_func_nvl2 Create_func_nvl2::s_singleton; Item* @@ -3290,6 +3515,37 @@ Create_func_datediff::create_2_arg(THD *thd, Item *arg1, Item *arg2) return new (thd->mem_root) Item_func_minus(thd, i1, i2); } +Create_func_date_format Create_func_date_format::s_singleton; + +Item* +Create_func_date_format::create_native(THD *thd, const LEX_CSTRING *name, + List<Item> *item_list) +{ + int arg_count= 0; + + if (item_list != NULL) + arg_count= item_list->elements; + + switch (arg_count) { + case 2: + { + Item *param_1= item_list->pop(); + Item *param_2= item_list->pop(); + return new (thd->mem_root) Item_func_date_format(thd, param_1, param_2); + } + case 3: + { + Item *param_1= item_list->pop(); + Item *param_2= item_list->pop(); + Item *param_3= item_list->pop(); + return new (thd->mem_root) Item_func_date_format(thd, + param_1, param_2, param_3); + } + } + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); + return NULL; +} + Create_func_dayname Create_func_dayname::s_singleton; @@ -4430,10 +4686,24 @@ Create_func_length Create_func_length::s_singleton; Item* Create_func_length::create_1_arg(THD *thd, Item *arg1) { - if (thd->variables.sql_mode & MODE_ORACLE) - return new (thd->mem_root) Item_func_char_length(thd, arg1); - else - return new (thd->mem_root) Item_func_octet_length(thd, arg1); + return new (thd->mem_root) Item_func_octet_length(thd, arg1); +} + +Create_func_old_password Create_func_old_password::s_singleton; + +Item* +Create_func_old_password::create_1_arg(THD *thd, Item *arg1) +{ + return new (thd->mem_root) Item_func_password(thd, arg1, + Item_func_password::OLD); +} + +Create_func_password Create_func_password::s_singleton; + +Item* +Create_func_password::create_1_arg(THD *thd, Item *arg1) +{ + return new (thd->mem_root) Item_func_password(thd, arg1); } Create_func_octet_length Create_func_octet_length::s_singleton; @@ -4651,7 +4921,7 @@ Create_func_ltrim Create_func_ltrim::s_singleton; Item* Create_func_ltrim::create_1_arg(THD *thd, Item *arg1) { - return Lex_trim(TRIM_LEADING, arg1).make_item_func_trim(thd); + return Lex_trim(TRIM_LEADING, arg1).make_item_func_trim_std(thd); } @@ -4810,6 +5080,24 @@ Item *Create_func_natural_sort_key::create_1_arg(THD *thd, Item* arg1) return new (thd->mem_root) Item_func_natural_sort_key(thd, arg1); } +Create_func_microsecond Create_func_microsecond::s_singleton; + +Item* +Create_func_microsecond::create_1_arg(THD *thd, Item *arg1) +{ + return new (thd->mem_root) Item_func_microsecond(thd, arg1); +} + + +Create_func_mod Create_func_mod::s_singleton; + +Item* +Create_func_mod::create_2_arg(THD *thd, Item *arg1, Item *arg2) +{ + return new (thd->mem_root) Item_func_mod(thd, arg1, arg2); +} + + Create_func_monthname Create_func_monthname::s_singleton; Item* @@ -4912,30 +5200,30 @@ Create_func_pow::create_2_arg(THD *thd, Item *arg1, Item *arg2) } -Create_func_quote Create_func_quote::s_singleton; +Create_func_quarter Create_func_quarter::s_singleton; Item* -Create_func_quote::create_1_arg(THD *thd, Item *arg1) +Create_func_quarter::create_1_arg(THD *thd, Item *arg1) { - return new (thd->mem_root) Item_func_quote(thd, arg1); + return new (thd->mem_root) Item_func_quarter(thd, arg1); } -Create_func_regexp_instr Create_func_regexp_instr::s_singleton; +Create_func_quote Create_func_quote::s_singleton; Item* -Create_func_regexp_instr::create_2_arg(THD *thd, Item *arg1, Item *arg2) +Create_func_quote::create_1_arg(THD *thd, Item *arg1) { - return new (thd->mem_root) Item_func_regexp_instr(thd, arg1, arg2); + return new (thd->mem_root) Item_func_quote(thd, arg1); } -Create_func_regexp_replace Create_func_regexp_replace::s_singleton; +Create_func_regexp_instr Create_func_regexp_instr::s_singleton; Item* -Create_func_regexp_replace::create_3_arg(THD *thd, Item *arg1, Item *arg2, Item *arg3) +Create_func_regexp_instr::create_2_arg(THD *thd, Item *arg1, Item *arg2) { - return new (thd->mem_root) Item_func_regexp_replace(thd, arg1, arg2, arg3); + return new (thd->mem_root) Item_func_regexp_instr(thd, arg1, arg2); } @@ -5097,6 +5385,17 @@ Create_func_round::create_native(THD *thd, const LEX_CSTRING *name, } +Create_func_row_count Create_func_row_count::s_singleton; + +Item* +Create_func_row_count::create_builder(THD *thd) +{ + thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION); + thd->lex->safe_to_cache_query= 0; + return new (thd->mem_root) Item_func_row_count(thd); +} + + Create_func_rpad Create_func_rpad::s_singleton; Create_func_rpad_oracle Create_func_rpad_oracle::s_singleton; @@ -5166,7 +5465,7 @@ Create_func_rtrim Create_func_rtrim::s_singleton; Item* Create_func_rtrim::create_1_arg(THD *thd, Item *arg1) { - return Lex_trim(TRIM_TRAILING, arg1).make_item_func_trim(thd); + return Lex_trim(TRIM_TRAILING, arg1).make_item_func_trim_std(thd); } @@ -5551,6 +5850,43 @@ Create_func_version::create_builder(THD *thd) } +Create_func_week Create_func_week::s_singleton; + +Item* +Create_func_week::create_native(THD *thd, const LEX_CSTRING *name, + List<Item> *item_list) +{ + Item* func= NULL; + int arg_count= 0; + + if (item_list != NULL) + arg_count= item_list->elements; + + switch (arg_count) { + case 1: + { + Item *param_1= item_list->pop(); + func= new (thd->mem_root) Item_func_week(thd, param_1); + break; + } + case 2: + { + Item *param_1= item_list->pop(); + Item *param_2= item_list->pop(); + func= new (thd->mem_root) Item_func_week(thd, param_1, param_2); + break; + } + default: + { + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); + break; + } + } + + return func; +} + + Create_func_weekday Create_func_weekday::s_singleton; Item* @@ -5701,6 +6037,7 @@ const Native_func_registry func_array[] = { { STRING_WITH_LEN("ABS") }, BUILDER(Create_func_abs)}, { { STRING_WITH_LEN("ACOS") }, BUILDER(Create_func_acos)}, { { STRING_WITH_LEN("ADDTIME") }, BUILDER(Create_func_addtime)}, + { { STRING_WITH_LEN("ADD_MONTHS") }, BUILDER(Create_func_addmonths)}, { { STRING_WITH_LEN("AES_DECRYPT") }, BUILDER(Create_func_aes_decrypt)}, { { STRING_WITH_LEN("AES_ENCRYPT") }, BUILDER(Create_func_aes_encrypt)}, { { STRING_WITH_LEN("ASIN") }, BUILDER(Create_func_asin)}, @@ -5716,7 +6053,9 @@ const Native_func_registry func_array[] = { { STRING_WITH_LEN("CHARACTER_LENGTH") }, BUILDER(Create_func_char_length)}, { { STRING_WITH_LEN("CHAR_LENGTH") }, BUILDER(Create_func_char_length)}, { { STRING_WITH_LEN("CHR") }, BUILDER(Create_func_chr)}, + { { STRING_WITH_LEN("COALESCE") }, BUILDER(Create_func_coalesce)}, { { STRING_WITH_LEN("COERCIBILITY") }, BUILDER(Create_func_coercibility)}, + { { STRING_WITH_LEN("COLLATION") }, BUILDER(Create_func_collation)}, { { STRING_WITH_LEN("COLUMN_CHECK") }, BUILDER(Create_func_dyncol_check)}, { { STRING_WITH_LEN("COLUMN_EXISTS") }, BUILDER(Create_func_dyncol_exists)}, { { STRING_WITH_LEN("COLUMN_LIST") }, BUILDER(Create_func_dyncol_list)}, @@ -5732,7 +6071,9 @@ const Native_func_registry func_array[] = { { STRING_WITH_LEN("COT") }, BUILDER(Create_func_cot)}, { { STRING_WITH_LEN("CRC32") }, BUILDER(Create_func_crc32)}, { { STRING_WITH_LEN("CRC32C") }, BUILDER(Create_func_crc32c)}, + { { STRING_WITH_LEN("DATABASE") }, BUILDER(Create_func_database)}, { { STRING_WITH_LEN("DATEDIFF") }, BUILDER(Create_func_datediff)}, + { { STRING_WITH_LEN("DATE_FORMAT") }, BUILDER(Create_func_date_format)}, { { STRING_WITH_LEN("DAYNAME") }, BUILDER(Create_func_dayname)}, { { STRING_WITH_LEN("DAYOFMONTH") }, BUILDER(Create_func_dayofmonth)}, { { STRING_WITH_LEN("DAYOFWEEK") }, BUILDER(Create_func_dayofweek)}, @@ -5824,6 +6165,8 @@ const Native_func_registry func_array[] = { { STRING_WITH_LEN("MASTER_GTID_WAIT") }, BUILDER(Create_func_master_gtid_wait)}, { { STRING_WITH_LEN("MASTER_POS_WAIT") }, BUILDER(Create_func_master_pos_wait)}, { { STRING_WITH_LEN("MD5") }, BUILDER(Create_func_md5)}, + { { STRING_WITH_LEN("MICROSECOND") }, BUILDER(Create_func_microsecond)}, + { { STRING_WITH_LEN("MOD") }, BUILDER(Create_func_mod)}, { { STRING_WITH_LEN("MONTHNAME") }, BUILDER(Create_func_monthname)}, { { STRING_WITH_LEN("NAME_CONST") }, BUILDER(Create_func_name_const)}, { {STRING_WITH_LEN("NATURAL_SORT_KEY")}, BUILDER(Create_func_natural_sort_key)}, @@ -5832,12 +6175,15 @@ const Native_func_registry func_array[] = { { STRING_WITH_LEN("NULLIF") }, BUILDER(Create_func_nullif)}, { { STRING_WITH_LEN("OCT") }, BUILDER(Create_func_oct)}, { { STRING_WITH_LEN("OCTET_LENGTH") }, BUILDER(Create_func_octet_length)}, + { { STRING_WITH_LEN("OLD_PASSWORD") }, BUILDER(Create_func_old_password)}, { { STRING_WITH_LEN("ORD") }, BUILDER(Create_func_ord)}, + { { STRING_WITH_LEN("PASSWORD") }, BUILDER(Create_func_password)}, { { STRING_WITH_LEN("PERIOD_ADD") }, BUILDER(Create_func_period_add)}, { { STRING_WITH_LEN("PERIOD_DIFF") }, BUILDER(Create_func_period_diff)}, { { STRING_WITH_LEN("PI") }, BUILDER(Create_func_pi)}, { { STRING_WITH_LEN("POW") }, BUILDER(Create_func_pow)}, { { STRING_WITH_LEN("POWER") }, BUILDER(Create_func_pow)}, + { { STRING_WITH_LEN("QUARTER") }, BUILDER(Create_func_quarter)}, { { STRING_WITH_LEN("QUOTE") }, BUILDER(Create_func_quote)}, { { STRING_WITH_LEN("RANDOM_BYTES")}, BUILDER(Create_func_random_bytes)}, { { STRING_WITH_LEN("REGEXP_INSTR") }, BUILDER(Create_func_regexp_instr)}, @@ -5852,12 +6198,15 @@ const Native_func_registry func_array[] = BUILDER(Create_func_replace_oracle)}, { { STRING_WITH_LEN("REVERSE") }, BUILDER(Create_func_reverse)}, { { STRING_WITH_LEN("ROUND") }, BUILDER(Create_func_round)}, + { { STRING_WITH_LEN("ROW_COUNT") }, BUILDER(Create_func_row_count)}, { { STRING_WITH_LEN("RPAD") }, BUILDER(Create_func_rpad)}, { { STRING_WITH_LEN("RPAD_ORACLE") }, BUILDER(Create_func_rpad_oracle)}, { { STRING_WITH_LEN("RTRIM") }, BUILDER(Create_func_rtrim)}, { { STRING_WITH_LEN("RTRIM_ORACLE") }, BUILDER(Create_func_rtrim_oracle)}, { { STRING_WITH_LEN("SEC_TO_TIME") }, BUILDER(Create_func_sec_to_time)}, { { STRING_WITH_LEN("SFORMAT") }, BUILDER(Create_func_sformat)}, + { { STRING_WITH_LEN("SCHEMA") }, BUILDER(Create_func_database)}, + { { STRING_WITH_LEN("SCHEMAS") }, BUILDER(Create_func_database)}, { { STRING_WITH_LEN("SHA") }, BUILDER(Create_func_sha)}, { { STRING_WITH_LEN("SHA1") }, BUILDER(Create_func_sha)}, { { STRING_WITH_LEN("SHA2") }, BUILDER(Create_func_sha2)}, @@ -5890,6 +6239,7 @@ const Native_func_registry func_array[] = { { STRING_WITH_LEN("UPPER") }, BUILDER(Create_func_ucase)}, { { STRING_WITH_LEN("UUID_SHORT") }, BUILDER(Create_func_uuid_short)}, { { STRING_WITH_LEN("VERSION") }, BUILDER(Create_func_version)}, + { { STRING_WITH_LEN("WEEK") }, BUILDER(Create_func_week)}, { { STRING_WITH_LEN("WEEKDAY") }, BUILDER(Create_func_weekday)}, { { STRING_WITH_LEN("WEEKOFYEAR") }, BUILDER(Create_func_weekofyear)}, #ifdef WITH_WSREP @@ -5903,9 +6253,27 @@ const Native_func_registry func_array[] = Native_func_registry_array native_func_registry_array(func_array, array_elements(func_array)); -const size_t func_array_length= sizeof(func_array) / sizeof(Native_func_registry) - 1; +const Native_func_registry func_array_oracle_overrides[] = +{ + { { STRING_WITH_LEN("CONCAT") }, BUILDER(Create_func_concat_operator_oracle)}, + { { STRING_WITH_LEN("DECODE") }, BUILDER(Create_func_decode_oracle)}, + { { STRING_WITH_LEN("LENGTH") }, BUILDER(Create_func_char_length)}, + { { STRING_WITH_LEN("LPAD") }, BUILDER(Create_func_lpad_oracle)}, + { { STRING_WITH_LEN("LTRIM") }, BUILDER(Create_func_ltrim_oracle)}, + { { STRING_WITH_LEN("REGEXP_REPLACE") }, + BUILDER(Create_func_regexp_replace_oracle)}, + { { STRING_WITH_LEN("RPAD") }, BUILDER(Create_func_rpad_oracle)}, + { { STRING_WITH_LEN("RTRIM") }, BUILDER(Create_func_rtrim_oracle)}, + { {0, 0}, NULL} +}; + +Native_func_registry_array + oracle_func_registry_array(func_array_oracle_overrides, + array_elements(func_array_oracle_overrides)); Native_functions_hash native_functions_hash; +Native_functions_hash native_functions_hash_oracle; + /* Load the hash table for native functions. @@ -6034,13 +6402,30 @@ int item_create_init() native_func_registry_array_geom.count())) return true; #endif - return false; + + count+= oracle_func_registry_array.count(); + + if (native_functions_hash_oracle.init(count) || + native_functions_hash_oracle.append(native_func_registry_array.elements(), + native_func_registry_array.count())) + return true; + +#ifdef HAVE_SPATIAL + if (native_functions_hash_oracle.append(native_func_registry_array_geom.elements(), + native_func_registry_array_geom.count())) + return true; +#endif + + return + native_functions_hash_oracle.replace(oracle_func_registry_array.elements(), + oracle_func_registry_array.count()); } void item_create_cleanup() { native_functions_hash.cleanup(); + native_functions_hash_oracle.cleanup(); } diff --git a/sql/item_create.h b/sql/item_create.h index 80395960..ea32d661 100644 --- a/sql/item_create.h +++ b/sql/item_create.h @@ -324,6 +324,12 @@ public: bool init(size_t count); bool append(const Native_func_registry array[], size_t count); bool remove(const Native_func_registry array[], size_t count); + bool replace(const Native_func_registry array[], size_t count) + { + DBUG_ENTER("Native_functions_hash::replace"); + remove(array, count); + DBUG_RETURN(append(array, count)); + } void cleanup(); /** Find the native function builder associated with a given function name. @@ -335,6 +341,7 @@ public: }; extern MYSQL_PLUGIN_IMPORT Native_functions_hash native_functions_hash; +extern MYSQL_PLUGIN_IMPORT Native_functions_hash native_functions_hash_oracle; extern const Native_func_registry func_array[]; extern const size_t func_array_length; @@ -377,4 +384,3 @@ public: #endif - diff --git a/sql/item_func.cc b/sql/item_func.cc index a2bc4752..dd056ac4 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -131,6 +131,16 @@ Item_args::Item_args(THD *thd, const Item_args *other) } +void Item_func::wrong_param_count_error(const LEX_CSTRING &schema_name, + const LEX_CSTRING &func_name) +{ + DBUG_ASSERT(schema_name.length); + Database_qualified_name qname(schema_name, func_name); + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), + ErrConvDQName(&qname).ptr()); +} + + void Item_func::sync_with_sum_func_and_with_field(List<Item> &list) { List_iterator_fast<Item> li(list); @@ -618,13 +628,12 @@ table_map Item_func::not_null_tables() const void Item_func::print(String *str, enum_query_type query_type) { str->append(func_name_cstring()); - str->append('('); - print_args(str, 0, query_type); - str->append(')'); + print_args_parenthesized(str, query_type); } -void Item_func::print_args(String *str, uint from, enum_query_type query_type) +void Item_func::print_args(String *str, uint from, + enum_query_type query_type) const { for (uint i=from ; i < arg_count ; i++) { @@ -1814,7 +1823,7 @@ void Item_func_neg::fix_length_and_dec_int() Use val() to get value as arg_type doesn't mean that item is Item_int or Item_float due to existence of Item_param. */ - if (args[0]->const_item()) + if (args[0]->const_item() && !args[0]->is_expensive()) { longlong val= args[0]->val_int(); if ((ulonglong) val >= (ulonglong) LONGLONG_MIN && @@ -2793,8 +2802,17 @@ bool Item_func_rand::fix_fields(THD *thd,Item **ref) No need to send a Rand log event if seed was given eg: RAND(seed), as it will be replicated in the query as such. */ + DBUG_ASSERT((!rand && + (thd->active_stmt_arena_to_use()-> + is_stmt_prepare_or_first_stmt_execute() || + thd->active_stmt_arena_to_use()-> + is_conventional() || + thd->active_stmt_arena_to_use()->state == + Query_arena::STMT_SP_QUERY_ARGUMENTS + ) + ) || rand); if (!rand && !(rand= (struct my_rnd_struct*) - thd->stmt_arena->alloc(sizeof(*rand)))) + thd->active_stmt_arena_to_use()->alloc(sizeof(*rand)))) return TRUE; } else @@ -4633,7 +4651,6 @@ user_var_entry *get_variable(HASH *hash, LEX_CSTRING *name, entry->length=0; entry->update_query_id=0; entry->set_charset(NULL); - entry->unsigned_flag= 0; /* If we are here, we were called from a SET or a query which sets a variable. Imagine it is this: @@ -4645,7 +4662,7 @@ user_var_entry *get_variable(HASH *hash, LEX_CSTRING *name, by Item_func_get_user_var (because that's not necessary). */ entry->used_query_id=current_thd->query_id; - entry->type=STRING_RESULT; + entry->set_handler(&type_handler_long_blob); memcpy((char*) entry->name.str, name->str, name->length+1); if (my_hash_insert(hash,(uchar*) entry)) { @@ -4721,9 +4738,12 @@ bool Item_func_set_user_var::fix_fields(THD *thd, Item **ref) switch (args[0]->result_type()) { case STRING_RESULT: case TIME_RESULT: - set_handler(type_handler_long_blob. - type_handler_adjusted_to_max_octet_length(max_length, - collation.collation)); + if (args[0]->field_type() == MYSQL_TYPE_GEOMETRY) + set_handler(args[0]->type_handler()); + else + set_handler(type_handler_long_blob. + type_handler_adjusted_to_max_octet_length(max_length, + collation.collation)); break; case REAL_RESULT: set_handler(&type_handler_double); @@ -4848,9 +4868,9 @@ bool Item_func_set_user_var::register_field_in_bitmap(void *arg) bool update_hash(user_var_entry *entry, bool set_null, void *ptr, size_t length, - Item_result type, CHARSET_INFO *cs, - bool unsigned_arg) + const Type_handler *th, CHARSET_INFO *cs) { + entry->set_handler(th); if (set_null) { char *pos= (char*) entry+ ALIGN_SIZE(sizeof(user_var_entry)); @@ -4861,7 +4881,7 @@ update_hash(user_var_entry *entry, bool set_null, void *ptr, size_t length, } else { - if (type == STRING_RESULT) + if (th->result_type() == STRING_RESULT) length++; // Store strings with end \0 if (length <= extra_size) { @@ -4890,20 +4910,18 @@ update_hash(user_var_entry *entry, bool set_null, void *ptr, size_t length, return 1; } } - if (type == STRING_RESULT) + if (th->result_type() == STRING_RESULT) { length--; // Fix length change above entry->value[length]= 0; // Store end \0 } if (length) memmove(entry->value, ptr, length); - if (type == DECIMAL_RESULT) + if (th->result_type() == DECIMAL_RESULT) ((my_decimal*)entry->value)->fix_buffer_pointer(); entry->length= length; entry->set_charset(cs); - entry->unsigned_flag= unsigned_arg; } - entry->type=type; #ifdef USER_VAR_TRACKING #ifndef EMBEDDED_LIBRARY THD *thd= current_thd; @@ -4916,9 +4934,8 @@ update_hash(user_var_entry *entry, bool set_null, void *ptr, size_t length, bool Item_func_set_user_var::update_hash(void *ptr, size_t length, - Item_result res_type, - CHARSET_INFO *cs, - bool unsigned_arg) + const Type_handler *th, + CHARSET_INFO *cs) { /* If we set a variable explicitly to NULL then keep the old @@ -4932,9 +4949,8 @@ Item_func_set_user_var::update_hash(void *ptr, size_t length, else null_value= args[0]->null_value; if (null_value && null_item) - res_type= m_var_entry->type; // Don't change type of item - if (::update_hash(m_var_entry, null_value, - ptr, length, res_type, cs, unsigned_arg)) + th= m_var_entry->type_handler(); // Don't change type of item + if (::update_hash(m_var_entry, null_value, ptr, length, th, cs)) { null_value= 1; return 1; @@ -4950,7 +4966,7 @@ double user_var_entry::val_real(bool *null_value) if ((*null_value= (value == 0))) return 0.0; - switch (type) { + switch (type_handler()->result_type()) { case REAL_RESULT: return *(double*) value; case INT_RESULT: @@ -4975,7 +4991,7 @@ longlong user_var_entry::val_int(bool *null_value) const if ((*null_value= (value == 0))) return 0; - switch (type) { + switch (type_handler()->result_type()) { case REAL_RESULT: return (longlong) *(double*) value; case INT_RESULT: @@ -5004,12 +5020,12 @@ String *user_var_entry::val_str(bool *null_value, String *str, if ((*null_value= (value == 0))) return (String*) 0; - switch (type) { + switch (type_handler()->result_type()) { case REAL_RESULT: str->set_real(*(double*) value, decimals, charset()); break; case INT_RESULT: - if (!unsigned_flag) + if (!type_handler()->is_unsigned()) str->set(*(longlong*) value, charset()); else str->set(*(ulonglong*) value, charset()); @@ -5036,7 +5052,7 @@ my_decimal *user_var_entry::val_decimal(bool *null_value, my_decimal *val) if ((*null_value= (value == 0))) return 0; - switch (type) { + switch (type_handler()->result_type()) { case REAL_RESULT: double2my_decimal(E_DEC_FATAL_ERROR, *(double*) value, val); break; @@ -5175,33 +5191,37 @@ Item_func_set_user_var::update() case REAL_RESULT: { res= update_hash((void*) &save_result.vreal,sizeof(save_result.vreal), - REAL_RESULT, &my_charset_numeric, 0); + &type_handler_double, &my_charset_numeric); break; } case INT_RESULT: { res= update_hash((void*) &save_result.vint, sizeof(save_result.vint), - INT_RESULT, &my_charset_numeric, unsigned_flag); + unsigned_flag ? (Type_handler *) &type_handler_ulonglong : + (Type_handler *) &type_handler_slonglong, + &my_charset_numeric); break; } case STRING_RESULT: { if (!save_result.vstr) // Null value - res= update_hash((void*) 0, 0, STRING_RESULT, &my_charset_bin, 0); + res= update_hash((void*) 0, 0, &type_handler_long_blob, &my_charset_bin); else res= update_hash((void*) save_result.vstr->ptr(), - save_result.vstr->length(), STRING_RESULT, - save_result.vstr->charset(), 0); + save_result.vstr->length(), + field_type() == MYSQL_TYPE_GEOMETRY ? + type_handler() : &type_handler_long_blob, + save_result.vstr->charset()); break; } case DECIMAL_RESULT: { if (!save_result.vdec) // Null value - res= update_hash((void*) 0, 0, DECIMAL_RESULT, &my_charset_bin, 0); + res= update_hash((void*) 0, 0, &type_handler_newdecimal, &my_charset_bin); else res= update_hash((void*) save_result.vdec, - sizeof(my_decimal), DECIMAL_RESULT, - &my_charset_numeric, 0); + sizeof(my_decimal), &type_handler_newdecimal, + &my_charset_numeric); break; } case ROW_RESULT: @@ -5593,9 +5613,8 @@ get_var_with_binlog(THD *thd, enum_sql_command sql_command, user_var_event->value= (char*) user_var_event + ALIGN_SIZE(sizeof(BINLOG_USER_VAR_EVENT)); user_var_event->user_var_event= var_entry; - user_var_event->type= var_entry->type; + user_var_event->th= var_entry->type_handler(); user_var_event->charset_number= var_entry->charset()->number; - user_var_event->unsigned_flag= var_entry->unsigned_flag; if (!var_entry->value) { /* NULL value*/ @@ -5637,9 +5656,9 @@ bool Item_func_get_user_var::fix_length_and_dec(THD *thd) */ if (likely(!error && m_var_entry)) { - unsigned_flag= m_var_entry->unsigned_flag; + unsigned_flag= m_var_entry->type_handler()->is_unsigned(); max_length= (uint32)m_var_entry->length; - switch (m_var_entry->type) { + switch (m_var_entry->type_handler()->result_type()) { case REAL_RESULT: collation.set(&my_charset_numeric, DERIVATION_NUMERIC); fix_char_length(DBL_DIG + 8); @@ -5658,6 +5677,8 @@ bool Item_func_get_user_var::fix_length_and_dec(THD *thd) collation.set(m_var_entry->charset(), DERIVATION_IMPLICIT); max_length= MAX_BLOB_WIDTH - 1; set_handler(&type_handler_long_blob); + if (m_var_entry->type_handler()->field_type() == MYSQL_TYPE_GEOMETRY) + set_handler(m_var_entry->type_handler()); break; case DECIMAL_RESULT: collation.set(&my_charset_numeric, DERIVATION_NUMERIC); @@ -5730,7 +5751,7 @@ bool Item_user_var_as_out_param::fix_fields(THD *thd, Item **ref) DBUG_ASSERT(thd->lex->exchange); if (!(entry= get_variable(&thd->user_vars, &org_name, 1))) return TRUE; - entry->type= STRING_RESULT; + entry->set_handler(&type_handler_long_blob); /* Let us set the same collation which is used for loading of fields in LOAD DATA INFILE. @@ -5746,15 +5767,14 @@ bool Item_user_var_as_out_param::fix_fields(THD *thd, Item **ref) void Item_user_var_as_out_param::set_null_value(CHARSET_INFO* cs) { - ::update_hash(entry, TRUE, 0, 0, STRING_RESULT, cs, 0 /* unsigned_arg */); + ::update_hash(entry, TRUE, 0, 0, &type_handler_long_blob, cs); } void Item_user_var_as_out_param::set_value(const char *str, uint length, CHARSET_INFO* cs) { - ::update_hash(entry, FALSE, (void*)str, length, STRING_RESULT, cs, - 0 /* unsigned_arg */); + ::update_hash(entry, FALSE, (void*)str, length, &type_handler_long_blob, cs); } diff --git a/sql/item_func.h b/sql/item_func.h index 435875bd..1f185eff 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -55,8 +55,40 @@ protected: bool check_argument_types_can_return_date(uint start, uint end) const; bool check_argument_types_can_return_time(uint start, uint end) const; void print_cast_temporal(String *str, enum_query_type query_type); + + void print_schema_qualified_name(String *to, + const LEX_CSTRING &schema_name, + const LEX_CSTRING &function_name) const + { + // e.g. oracle_schema.func() + to->append(schema_name); + to->append('.'); + to->append(function_name); + } + + void print_sql_mode_qualified_name(String *to, + enum_query_type query_type, + const LEX_CSTRING &function_name) const + { + const Schema *func_schema= schema(); + if (!func_schema || func_schema == Schema::find_implied(current_thd)) + to->append(function_name); + else + print_schema_qualified_name(to, func_schema->name(), function_name); + } + + void print_sql_mode_qualified_name(String *to, enum_query_type query_type) + const + { + return print_sql_mode_qualified_name(to, query_type, func_name_cstring()); + } + public: + // Print an error message for a builtin-schema qualified function call + static void wrong_param_count_error(const LEX_CSTRING &schema_name, + const LEX_CSTRING &func_name); + table_map not_null_tables_cache; enum Functype { UNKNOWN_FUNC,EQ_FUNC,EQUAL_FUNC,NE_FUNC,LT_FUNC,LE_FUNC, @@ -79,6 +111,38 @@ public: CASE_SEARCHED_FUNC, // Used by ColumnStore/Spider CASE_SIMPLE_FUNC, // Used by ColumnStore/spider, }; + + /* + A function bitmap. Useful when some operation needs to be applied only + to certain functions. For now we only need to distinguish some + comparison predicates. + */ + enum Bitmap : ulonglong + { + BITMAP_NONE= 0, + BITMAP_EQ= 1ULL << EQ_FUNC, + BITMAP_EQUAL= 1ULL << EQUAL_FUNC, + BITMAP_NE= 1ULL << NE_FUNC, + BITMAP_LT= 1ULL << LT_FUNC, + BITMAP_LE= 1ULL << LE_FUNC, + BITMAP_GE= 1ULL << GE_FUNC, + BITMAP_GT= 1ULL << GT_FUNC, + BITMAP_LIKE= 1ULL << LIKE_FUNC, + BITMAP_BETWEEN= 1ULL << BETWEEN, + BITMAP_IN= 1ULL << IN_FUNC, + BITMAP_MULT_EQUAL= 1ULL << MULT_EQUAL_FUNC, + BITMAP_OTHER= 1ULL << 63, + BITMAP_ALL= 0xFFFFFFFFFFFFFFFFULL, + BITMAP_ANY_EQUALITY= BITMAP_EQ | BITMAP_EQUAL | BITMAP_MULT_EQUAL, + BITMAP_EXCEPT_ANY_EQUALITY= BITMAP_ALL & ~BITMAP_ANY_EQUALITY, + }; + + ulonglong bitmap_bit() const + { + Functype type= functype(); + return 1ULL << (type > 63 ? 63 : type); + } + static scalar_comparison_op functype_to_scalar_comparison_op(Functype type) { switch (type) { @@ -170,9 +234,15 @@ public: List<Item> &fields, uint flags) override; void print(String *str, enum_query_type query_type) override; void print_op(String *str, enum_query_type query_type); - void print_args(String *str, uint from, enum_query_type query_type); + void print_args(String *str, uint from, enum_query_type query_type) const; + void print_args_parenthesized(String *str, enum_query_type query_type) const + { + str->append('('); + print_args(str, 0, query_type); + str->append(')'); + } bool is_null() override - { + { update_null_value(); return null_value; } @@ -388,15 +458,6 @@ public: } } void convert_const_compared_to_int_field(THD *thd); - /** - Prepare arguments and setup a comparator. - Used in Item_func_xxx with two arguments and a comparator, - e.g. Item_bool_func2 and Item_func_nullif. - args[0] or args[1] can be modified: - - converted to character set and collation of the operation - - or replaced to an Item_int_with_ref - */ - bool setup_args_and_comparator(THD *thd, Arg_comparator *cmp); Item_func *get_item_func() override { return this; } bool is_simplified_cond_processor(void *arg) override { return const_item() && !val_int(); } @@ -3386,8 +3447,8 @@ public: String *str_result(String *str) override; my_decimal *val_decimal_result(my_decimal *) override; bool is_null_result() override; - bool update_hash(void *ptr, size_t length, enum Item_result type, - CHARSET_INFO *cs, bool unsigned_arg); + bool update_hash(void *ptr, size_t length, const Type_handler *th, + CHARSET_INFO *cs); bool send(Protocol *protocol, st_value *buffer) override; void make_send_field(THD *thd, Send_field *tmp_field) override; bool check(bool use_result_field); @@ -4234,7 +4295,6 @@ double my_double_round(double value, longlong dec, bool dec_unsigned, extern bool volatile mqh_used; bool update_hash(user_var_entry *entry, bool set_null, void *ptr, size_t length, - Item_result type, CHARSET_INFO *cs, - bool unsigned_arg); + const Type_handler *th, CHARSET_INFO *cs); #endif /* ITEM_FUNC_INCLUDED */ diff --git a/sql/item_jsonfunc.cc b/sql/item_jsonfunc.cc index 590fde88..97d7b89f 100644 --- a/sql/item_jsonfunc.cc +++ b/sql/item_jsonfunc.cc @@ -911,7 +911,7 @@ static int alloc_tmp_paths(THD *thd, uint n_paths, { if (*tmp_paths == 0) { - MEM_ROOT *root= thd->stmt_arena->mem_root; + MEM_ROOT *root= thd->active_stmt_arena_to_use()->mem_root; *paths= (json_path_with_flags *) alloc_root(root, sizeof(json_path_with_flags) * n_paths); @@ -943,21 +943,47 @@ static void mark_constant_paths(json_path_with_flags *p, } -bool Item_json_str_multipath::fix_fields(THD *thd, Item **ref) +Item_json_str_multipath::~Item_json_str_multipath() { - return alloc_tmp_paths(thd, get_n_paths(), &paths, &tmp_paths) || - Item_str_func::fix_fields(thd, ref); + if (tmp_paths) + { + for (uint i= n_paths; i>0; i--) + tmp_paths[i-1].free(); + } } -void Item_json_str_multipath::cleanup() +bool Item_json_str_multipath::fix_fields(THD *thd, Item **ref) { - if (tmp_paths) + if (!tmp_paths) { - for (uint i= get_n_paths(); i>0; i--) - tmp_paths[i-1].free(); + /* + Remember the number of paths and allocate required memory on first time + the method fix_fields() is invoked. For prepared statements the method + fix_fields can be called several times for the same item because its + clean up is performed every item a prepared statement finishing its + execution. In result, the data member fixed is reset and the method + fix_field() is invoked on next time the same prepared statement be + executed. On the other side, any memory allocations on behalf of + the prepared statement must be performed only once on its first execution. + The data member tmp_path is kind a guard to do these activities only once + on first time the method fix_field() is called. + */ + n_paths= get_n_paths(); + + if (alloc_tmp_paths(thd, n_paths, &paths, &tmp_paths)) + return true; } - Item_str_func::cleanup(); + +#ifdef PROTECT_STATEMENT_MEMROOT + /* + Check that the number of paths remembered on first run of a statement + never changed later. + */ + DBUG_ASSERT(n_paths == get_n_paths()); +#endif + + return Item_str_func::fix_fields(thd, ref); } @@ -1498,10 +1524,19 @@ return_null: bool Item_func_json_contains_path::fix_fields(THD *thd, Item **ref) { - return alloc_tmp_paths(thd, arg_count-2, &paths, &tmp_paths) || - (p_found= (bool *) alloc_root(thd->mem_root, - (arg_count-2)*sizeof(bool))) == NULL || - Item_int_func::fix_fields(thd, ref); + /* + See comments on Item_json_str_multipath::fix_fields regarding + the aim of the condition 'if (!tmp_paths)'. + */ + if (!tmp_paths) + { + if (alloc_tmp_paths(thd, arg_count-2, &paths, &tmp_paths) || + (p_found= (bool *) alloc_root(thd->active_stmt_arena_to_use()->mem_root, + (arg_count-2)*sizeof(bool))) == NULL) + return true; + } + + return Item_int_func::fix_fields(thd, ref); } @@ -1514,8 +1549,7 @@ bool Item_func_json_contains_path::fix_length_and_dec(THD *thd) return Item_bool_func::fix_length_and_dec(thd); } - -void Item_func_json_contains_path::cleanup() +Item_func_json_contains_path::~Item_func_json_contains_path() { if (tmp_paths) { @@ -1523,7 +1557,6 @@ void Item_func_json_contains_path::cleanup() tmp_paths[i-1].free(); tmp_paths= 0; } - Item_int_func::cleanup(); } @@ -4088,6 +4121,13 @@ int Arg_comparator::compare_e_json_str_basic(Item *j, Item *s) return MY_TEST(sortcmp(res1, res2, compare_collation()) == 0); } +bool Item_func_json_arrayagg::fix_fields(THD *thd, Item **ref) +{ + bool res= Item_func_group_concat::fix_fields(thd, ref); + m_tmp_json.set_charset(collation.collation); + return res; +} + String *Item_func_json_arrayagg::get_str_from_item(Item *i, String *tmp) { diff --git a/sql/item_jsonfunc.h b/sql/item_jsonfunc.h index 6f6b6a7a..1694013f 100644 --- a/sql/item_jsonfunc.h +++ b/sql/item_jsonfunc.h @@ -276,11 +276,26 @@ class Item_json_str_multipath: public Item_json_func protected: json_path_with_flags *paths; String *tmp_paths; +private: + /** + Number of paths returned by calling virtual method get_n_paths() and + remembered inside fix_fields(). It is used by the virtual destructor + ~Item_json_str_multipath() to iterate along allocated memory chunks stored + in the array tmp_paths and free every of them. The virtual method + get_n_paths() can't be used for this goal from within virtual destructor. + We could get rid of the virtual method get_n_paths() and store the number + of paths directly in the constructor of classes derived from the class + Item_json_str_multipath but presence of the method get_n_paths() allows + to check invariant that the number of arguments not changed between + sequential runs of the same prepared statement that seems to be useful. + */ + uint n_paths; public: Item_json_str_multipath(THD *thd, List<Item> &list): - Item_json_func(thd, list), tmp_paths(0) {} + Item_json_func(thd, list), paths(NULL), tmp_paths(0), n_paths(0) {} + virtual ~Item_json_str_multipath(); + bool fix_fields(THD *thd, Item **ref); - void cleanup(); virtual uint get_n_paths() const = 0; }; @@ -347,6 +362,7 @@ protected: public: Item_func_json_contains_path(THD *thd, List<Item> &list): Item_bool_func(thd, list), tmp_paths(0) {} + virtual ~Item_func_json_contains_path(); LEX_CSTRING func_name_cstring() const override { static LEX_CSTRING name= {STRING_WITH_LEN("json_contains_path") }; @@ -354,7 +370,6 @@ public: } bool fix_fields(THD *thd, Item **ref) override; bool fix_length_and_dec(THD *thd) override; - void cleanup() override; longlong val_int() override; Item *get_copy(THD *thd) override { return get_item_copy<Item_func_json_contains_path>(thd, this); } @@ -717,6 +732,7 @@ public: static LEX_CSTRING name= {STRING_WITH_LEN("json_arrayagg(") }; return name; } + bool fix_fields(THD *thd, Item **ref) override; enum Sumfunctype sum_func() const override { return JSON_ARRAYAGG_FUNC; } String* val_str(String *str) override; diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 4bbf36ec..ce094bec 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -1184,8 +1184,7 @@ bool Item_func_reverse::fix_length_and_dec(THD *thd) Fix that this works with binary strings when using USE_MB */ -String *Item_func_replace::val_str_internal(String *str, - String *empty_string_for_null) +String *Item_func_replace::val_str_internal(String *str, bool null_to_empty) { DBUG_ASSERT(fixed()); String *res,*res2,*res3; @@ -1203,13 +1202,8 @@ String *Item_func_replace::val_str_internal(String *str, res=args[0]->val_str(str); if (args[0]->null_value) goto null; - res2=args[1]->val_str(&tmp_value); - if (args[1]->null_value) - { - if (!empty_string_for_null) - goto null; - res2= empty_string_for_null; - } + if (!(res2= args[1]->val_str_null_to_empty(&tmp_value, null_to_empty))) + goto null; res->set_charset(collation.collation); #ifdef USE_MB @@ -1226,12 +1220,8 @@ String *Item_func_replace::val_str_internal(String *str, if (binary_cmp && (offset=res->strstr(*res2)) < 0) return res; #endif - if (!(res3=args[2]->val_str(&tmp_value2))) - { - if (!empty_string_for_null) - goto null; - res3= empty_string_for_null; - } + if (!(res3= args[2]->val_str_null_to_empty(&tmp_value2, null_to_empty))) + goto null; from_length= res2->length(); to_length= res3->length(); @@ -1314,7 +1304,7 @@ redo: } while ((offset=res->strstr(*res2,(uint) offset)) >= 0); } - if (empty_string_for_null && !res->length()) + if (null_to_empty && !res->length()) goto null; return res; @@ -1638,20 +1628,22 @@ bool Item_func_regexp_replace::append_replacement(String *str, } -String *Item_func_regexp_replace::val_str(String *str) +String *Item_func_regexp_replace::val_str_internal(String *str, + bool null_to_empty) { DBUG_ASSERT(fixed()); char buff0[MAX_FIELD_WIDTH]; char buff2[MAX_FIELD_WIDTH]; String tmp0(buff0,sizeof(buff0),&my_charset_bin); String tmp2(buff2,sizeof(buff2),&my_charset_bin); - String *source= args[0]->val_str(&tmp0); - String *replace= args[2]->val_str(&tmp2); + String *source, *replace; LEX_CSTRING src, rpl; size_t startoffset= 0; - if ((null_value= (args[0]->null_value || args[2]->null_value || - re.recompile(args[1])))) + if ((null_value= + (!(source= args[0]->val_str(&tmp0)) || + !(replace= args[2]->val_str_null_to_empty(&tmp2, null_to_empty)) || + re.recompile(args[1])))) return (String *) 0; if (!(source= re.convert_if_needed(source, &re.subject_converter)) || @@ -2417,13 +2409,31 @@ bool Item_func_trim::fix_length_and_dec(THD *thd) void Item_func_trim::print(String *str, enum_query_type query_type) { + LEX_CSTRING suffix= {STRING_WITH_LEN("_oracle")}; if (arg_count == 1) { - Item_func::print(str, query_type); + if (query_type & QT_FOR_FRM) + { + // 10.3 downgrade compatibility for FRM + str->append(func_name_cstring()); + if (schema() == &oracle_schema_ref) + str->append(suffix); + } + else + print_sql_mode_qualified_name(str, query_type, func_name_cstring()); + print_args_parenthesized(str, query_type); return; } - str->append(Item_func_trim::func_name_cstring()); - str->append(func_name_ext()); + + if (query_type & QT_FOR_FRM) + { + // 10.3 downgrade compatibility for FRM + str->append(Item_func_trim::func_name_cstring()); + if (schema() == &oracle_schema_ref) + str->append(suffix); + } + else + print_sql_mode_qualified_name(str, query_type, Item_func_trim::func_name_cstring()); str->append('('); str->append(mode_name()); str->append(' '); @@ -3463,13 +3473,13 @@ String *Item_func_binlog_gtid_pos::val_str(String *str) String name_str, *name; longlong pos; - if (args[0]->null_value || args[1]->null_value) - goto err; - name= args[0]->val_str(&name_str); pos= args[1]->val_int(); - if (pos < 0 || pos > UINT_MAX32) + if (args[0]->null_value || args[1]->null_value) + goto err; + + if (pos < 0 || pos > (longlong) UINT_MAX32) goto err; if (gtid_state_from_binlog_pos(name->c_ptr_safe(), (uint32)pos, str)) diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index 3a3c5338..340847dd 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -307,6 +307,12 @@ protected: public: Item_func_concat(THD *thd, List<Item> &list): Item_str_func(thd, list) {} Item_func_concat(THD *thd, Item *a, Item *b): Item_str_func(thd, a, b) {} + const Schema *schema() const override { return &mariadb_schema; } + void print(String *str, enum_query_type query_type) override + { + print_sql_mode_qualified_name(str, query_type); + print_args_parenthesized(str, query_type); + } String *val_str(String *) override; bool fix_length_and_dec(THD *thd) override; LEX_CSTRING func_name_cstring() const override @@ -333,10 +339,17 @@ public: :Item_func_concat(thd, a, b) { } String *val_str(String *) override; - LEX_CSTRING func_name_cstring() const override + const Schema *schema() const override { return &oracle_schema_ref; } + void print(String *str, enum_query_type query_type) override { - static LEX_CSTRING name= {STRING_WITH_LEN("concat_operator_oracle") }; - return name; + if (query_type & QT_FOR_FRM) + { + // 10.3 downgrade compatibility for FRM + str->append(STRING_WITH_LEN("concat_operator_oracle")); + } + else + print_sql_mode_qualified_name(str, query_type); + print_args_parenthesized(str, query_type); } Item *get_copy(THD *thd) override { @@ -430,12 +443,19 @@ public: class Item_func_replace :public Item_str_func { String tmp_value,tmp_value2; +protected: + String *val_str_internal(String *str, bool null_to_empty); public: Item_func_replace(THD *thd, Item *org, Item *find, Item *replace): Item_str_func(thd, org, find, replace) {} - String *val_str(String *to) override { return val_str_internal(to, NULL); }; + String *val_str(String *to) override { return val_str_internal(to, false); }; bool fix_length_and_dec(THD *thd) override; - String *val_str_internal(String *str, String *empty_string_for_null); + const Schema *schema() const override { return &mariadb_schema; } + void print(String *str, enum_query_type query_type) override + { + print_sql_mode_qualified_name(str, query_type); + print_args_parenthesized(str, query_type); + } LEX_CSTRING func_name_cstring() const override { static LEX_CSTRING name= {STRING_WITH_LEN("replace") }; @@ -453,11 +473,18 @@ public: Item_func_replace_oracle(THD *thd, Item *org, Item *find, Item *replace): Item_func_replace(thd, org, find, replace) {} String *val_str(String *to) override - { return val_str_internal(to, &tmp_emtpystr); }; - LEX_CSTRING func_name_cstring() const override + { return val_str_internal(to, true); }; + const Schema *schema() const override { return &oracle_schema_ref; } + void print(String *str, enum_query_type query_type) override { - static LEX_CSTRING name= {STRING_WITH_LEN("replace_oracle") }; - return name; + if (query_type & QT_FOR_FRM) + { + // 10.3 downgrade compatibility for FRM + str->append(STRING_WITH_LEN("replace_oracle")); + } + else + print_sql_mode_qualified_name(str, query_type); + print_args_parenthesized(str, query_type); } Item *get_copy(THD *thd) override { return get_item_copy<Item_func_replace_oracle>(thd, this); } @@ -470,10 +497,18 @@ class Item_func_regexp_replace :public Item_str_func bool append_replacement(String *str, const LEX_CSTRING *source, const LEX_CSTRING *replace); +protected: + String *val_str_internal(String *str, bool null_to_empty); public: Item_func_regexp_replace(THD *thd, Item *a, Item *b, Item *c): Item_str_func(thd, a, b, c) {} + const Schema *schema() const override { return &mariadb_schema; } + void print(String *str, enum_query_type query_type) override + { + print_sql_mode_qualified_name(str, query_type); + print_args_parenthesized(str, query_type); + } void cleanup() override { DBUG_ENTER("Item_func_regexp_replace::cleanup"); @@ -481,7 +516,10 @@ public: re.cleanup(); DBUG_VOID_RETURN; } - String *val_str(String *str) override; + String *val_str(String *str) override + { + return val_str_internal(str, false); + } bool fix_length_and_dec(THD *thd) override; LEX_CSTRING func_name_cstring() const override { @@ -492,6 +530,26 @@ public: }; +class Item_func_regexp_replace_oracle: public Item_func_regexp_replace +{ +public: + Item_func_regexp_replace_oracle(THD *thd, Item *a, Item *b, Item *c) + :Item_func_regexp_replace(thd, a, b, c) + {} + const Schema *schema() const { return &oracle_schema_ref; } + bool fix_length_and_dec(THD *thd) + { + bool rc= Item_func_regexp_replace::fix_length_and_dec(thd); + set_maybe_null(); // Empty result is converted to NULL + return rc; + } + String *val_str(String *str) + { + return val_str_internal(str, true); + } +}; + + class Item_func_regexp_substr :public Item_str_func { Regexp_processor_pcre re; @@ -621,8 +679,16 @@ public: Item_func_substr(THD *thd, Item *a, Item *b): Item_str_func(thd, a, b) {} Item_func_substr(THD *thd, Item *a, Item *b, Item *c): Item_str_func(thd, a, b, c) {} + Item_func_substr(THD *thd, List<Item> &list) + :Item_str_func(thd, list) {} String *val_str(String *) override; bool fix_length_and_dec(THD *thd) override; + const Schema *schema() const override { return &mariadb_schema; } + void print(String *str, enum_query_type query_type) override + { + print_sql_mode_qualified_name(str, query_type); + print_args_parenthesized(str, query_type); + } LEX_CSTRING func_name_cstring() const override { static LEX_CSTRING name= {STRING_WITH_LEN("substr") }; @@ -661,16 +727,25 @@ public: Item_func_substr(thd, a, b) {} Item_func_substr_oracle(THD *thd, Item *a, Item *b, Item *c): Item_func_substr(thd, a, b, c) {} + Item_func_substr_oracle(THD *thd, List<Item> &list) + :Item_func_substr(thd, list) {} bool fix_length_and_dec(THD *thd) override { bool res= Item_func_substr::fix_length_and_dec(thd); set_maybe_null(); return res; } - LEX_CSTRING func_name_cstring() const override + const Schema *schema() const override { return &oracle_schema_ref; } + void print(String *str, enum_query_type query_type) override { - static LEX_CSTRING name= {STRING_WITH_LEN("substr_oracle") }; - return name; + if (query_type & QT_FOR_FRM) + { + // 10.3 downgrade compatibility for FRM + str->append(STRING_WITH_LEN("substr_oracle")); + } + else + print_sql_mode_qualified_name(str, query_type); + print_args_parenthesized(str, query_type); } Item *get_copy(THD *thd) override { return get_item_copy<Item_func_substr_oracle>(thd, this); } @@ -718,17 +793,13 @@ protected: { return trimmed_value(res, 0, res->length()); } - virtual LEX_CSTRING func_name_ext() const - { - static LEX_CSTRING name_ext= {STRING_WITH_LEN("") }; - return name_ext; - } public: Item_func_trim(THD *thd, Item *a, Item *b): Item_str_func(thd, a, b) {} Item_func_trim(THD *thd, Item *a): Item_str_func(thd, a) {} Sql_mode_dependency value_depends_on_sql_mode() const override; String *val_str(String *) override; bool fix_length_and_dec(THD *thd) override; + const Schema *schema() const override { return &mariadb_schema; } LEX_CSTRING func_name_cstring() const override { static LEX_CSTRING name= {STRING_WITH_LEN("trim") }; @@ -746,20 +817,11 @@ class Item_func_trim_oracle :public Item_func_trim protected: String *make_empty_result(String *str) override { null_value= 1; return NULL; } - LEX_CSTRING func_name_ext() const override - { - static LEX_CSTRING name_ext= {STRING_WITH_LEN("_oracle") }; - return name_ext; - } public: Item_func_trim_oracle(THD *thd, Item *a, Item *b): Item_func_trim(thd, a, b) {} Item_func_trim_oracle(THD *thd, Item *a): Item_func_trim(thd, a) {} - LEX_CSTRING func_name_cstring() const override - { - static LEX_CSTRING name= {STRING_WITH_LEN("trim_oracle") }; - return name; - } + const Schema *schema() const override { return &oracle_schema_ref; } bool fix_length_and_dec(THD *thd) override { bool res= Item_func_trim::fix_length_and_dec(thd); @@ -781,6 +843,7 @@ public: return Item_func::value_depends_on_sql_mode(); } String *val_str(String *) override; + const Schema *schema() const override { return &mariadb_schema; } LEX_CSTRING func_name_cstring() const override { static LEX_CSTRING name= {STRING_WITH_LEN("ltrim") }; @@ -798,20 +861,11 @@ class Item_func_ltrim_oracle :public Item_func_ltrim protected: String *make_empty_result(String *str) override { null_value= 1; return NULL; } - LEX_CSTRING func_name_ext() const override - { - static LEX_CSTRING name_ext= {STRING_WITH_LEN("_oracle") }; - return name_ext; - } public: Item_func_ltrim_oracle(THD *thd, Item *a, Item *b): Item_func_ltrim(thd, a, b) {} Item_func_ltrim_oracle(THD *thd, Item *a): Item_func_ltrim(thd, a) {} - LEX_CSTRING func_name_cstring() const override - { - static LEX_CSTRING name= {STRING_WITH_LEN("ltrim_oracle") }; - return name; - } + const Schema *schema() const override { return &oracle_schema_ref; } bool fix_length_and_dec(THD *thd) override { bool res= Item_func_ltrim::fix_length_and_dec(thd); @@ -829,6 +883,7 @@ public: Item_func_rtrim(THD *thd, Item *a, Item *b): Item_func_trim(thd, a, b) {} Item_func_rtrim(THD *thd, Item *a): Item_func_trim(thd, a) {} String *val_str(String *) override; + const Schema *schema() const override { return &mariadb_schema; } LEX_CSTRING func_name_cstring() const override { static LEX_CSTRING name= {STRING_WITH_LEN("rtrim") }; @@ -846,20 +901,11 @@ class Item_func_rtrim_oracle :public Item_func_rtrim protected: String *make_empty_result(String *str) override { null_value= 1; return NULL; } - LEX_CSTRING func_name_ext() const override - { - static LEX_CSTRING name_ext= {STRING_WITH_LEN("_oracle") }; - return name_ext; - } public: Item_func_rtrim_oracle(THD *thd, Item *a, Item *b): Item_func_rtrim(thd, a, b) {} Item_func_rtrim_oracle(THD *thd, Item *a): Item_func_rtrim(thd, a) {} - LEX_CSTRING func_name_cstring() const override - { - static LEX_CSTRING name= {STRING_WITH_LEN("rtrim_oracle") }; - return name; - } + const Schema *schema() const override { return &oracle_schema_ref; } bool fix_length_and_dec(THD *thd) override { bool res= Item_func_rtrim::fix_length_and_dec(thd); @@ -1032,6 +1078,12 @@ class Item_func_decode :public Item_func_encode { public: Item_func_decode(THD *thd, Item *a, Item *seed_arg): Item_func_encode(thd, a, seed_arg) {} + const Schema *schema() const override { return &mariadb_schema; } + void print(String *str, enum_query_type query_type) override + { + print_sql_mode_qualified_name(str, query_type); + print_args_parenthesized(str, query_type); + } LEX_CSTRING func_name_cstring() const override { static LEX_CSTRING name= {STRING_WITH_LEN("decode") }; @@ -1402,6 +1454,8 @@ public: Item_str_func(thd, arg1, arg2, arg3) {} Item_func_pad(THD *thd, Item *arg1, Item *arg2): Item_str_func(thd, arg1, arg2) {} + Item_func_pad(THD *thd, List<Item> &list): + Item_str_func(thd,list) {} bool fix_length_and_dec(THD *thd) override; }; @@ -1413,7 +1467,15 @@ public: Item_func_pad(thd, arg1, arg2, arg3) {} Item_func_rpad(THD *thd, Item *arg1, Item *arg2): Item_func_pad(thd, arg1, arg2) {} + Item_func_rpad(THD *thd, List<Item> &list): + Item_func_pad(thd,list) {} String *val_str(String *) override; + const Schema *schema() const override { return &mariadb_schema; } + void print(String *str, enum_query_type query_type) override + { + print_sql_mode_qualified_name(str, query_type); + print_args_parenthesized(str, query_type); + } LEX_CSTRING func_name_cstring() const override { static LEX_CSTRING name= {STRING_WITH_LEN("rpad") }; @@ -1434,16 +1496,25 @@ public: Item_func_rpad(thd, arg1, arg2, arg3) {} Item_func_rpad_oracle(THD *thd, Item *arg1, Item *arg2): Item_func_rpad(thd, arg1, arg2) {} + Item_func_rpad_oracle(THD *thd, List<Item> &list): + Item_func_rpad(thd,list) {} bool fix_length_and_dec(THD *thd) override { bool res= Item_func_rpad::fix_length_and_dec(thd); set_maybe_null(); return res; } - LEX_CSTRING func_name_cstring() const override + const Schema *schema() const override { return &oracle_schema_ref; } + void print(String *str, enum_query_type query_type) override { - static LEX_CSTRING name= {STRING_WITH_LEN("rpad_oracle") }; - return name; + if (query_type & QT_FOR_FRM) + { + // 10.3 downgrade compatibility for FRM + str->append(STRING_WITH_LEN("rpad_oracle")); + } + else + print_sql_mode_qualified_name(str, query_type); + print_args_parenthesized(str, query_type); } Item *get_copy(THD *thd) override { return get_item_copy<Item_func_rpad_oracle>(thd, this); } @@ -1457,7 +1528,15 @@ public: Item_func_pad(thd, arg1, arg2, arg3) {} Item_func_lpad(THD *thd, Item *arg1, Item *arg2): Item_func_pad(thd, arg1, arg2) {} + Item_func_lpad(THD *thd, List<Item> &list): + Item_func_pad(thd,list) {} String *val_str(String *) override; + const Schema *schema() const override { return &mariadb_schema; } + void print(String *str, enum_query_type query_type) override + { + print_sql_mode_qualified_name(str, query_type); + print_args_parenthesized(str, query_type); + } LEX_CSTRING func_name_cstring() const override { static LEX_CSTRING name= {STRING_WITH_LEN("lpad") }; @@ -1477,16 +1556,25 @@ public: Item_func_lpad(thd, arg1, arg2, arg3) {} Item_func_lpad_oracle(THD *thd, Item *arg1, Item *arg2): Item_func_lpad(thd, arg1, arg2) {} + Item_func_lpad_oracle(THD *thd, List<Item> &list): + Item_func_lpad(thd,list) {} bool fix_length_and_dec(THD *thd) override { bool res= Item_func_lpad::fix_length_and_dec(thd); set_maybe_null(); return res; } - LEX_CSTRING func_name_cstring() const override + const Schema *schema() const override { return &oracle_schema_ref; } + void print(String *str, enum_query_type query_type) override { - static LEX_CSTRING name= {STRING_WITH_LEN("lpad_oracle") }; - return name; + if (query_type & QT_FOR_FRM) + { + // 10.3 downgrade compatibility for FRM + str->append(STRING_WITH_LEN("lpad_oracle")); + } + else + print_sql_mode_qualified_name(str, query_type); + print_args_parenthesized(str, query_type); } Item *get_copy(THD *thd) override { return get_item_copy<Item_func_lpad_oracle>(thd, this); } @@ -1732,6 +1820,9 @@ public: collation.set(args[0]->collation); ulonglong max_result_length= (ulonglong) args[0]->max_length * 2 + 2 * collation.collation->mbmaxlen; + // NULL argument is returned as a string "NULL" without quotes + if (args[0]->maybe_null()) + set_if_bigger(max_result_length, 4 * collation.collation->mbmaxlen); max_length= (uint32) MY_MIN(max_result_length, MAX_BLOB_WIDTH); return FALSE; } diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 8b473278..7d1a72b9 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -3281,8 +3281,12 @@ bool Item_exists_subselect::exists2in_processor(void *opt_arg) if (eqs.at(i).outer_exp-> walk(&Item::find_item_processor, TRUE, upper->item)) break; + DBUG_ASSERT(thd->stmt_arena->is_stmt_prepare_or_first_stmt_execute() || + thd->stmt_arena->is_conventional()); + DBUG_ASSERT(thd->stmt_arena->mem_root == thd->mem_root); if (i == (uint)eqs.elements() && - (in_subs->upper_refs.push_back(upper, thd->stmt_arena->mem_root))) + (in_subs->upper_refs.push_back( + upper, thd->mem_root))) goto out; } } @@ -3989,14 +3993,14 @@ bool subselect_union_engine::fix_length_and_dec(Item_cache **row) if (unit->first_select()->item_list.elements == 1) { - if (set_row(unit->types, row)) + if (set_row(unit->item_list, row)) return TRUE; item->collation.set(row[0]->collation); } else { bool maybe_null_saved= maybe_null; - if (set_row(unit->types, row)) + if (set_row(unit->item_list, row)) return TRUE; maybe_null= maybe_null_saved; } diff --git a/sql/item_sum.cc b/sql/item_sum.cc index bbd09a59..bcaf229d 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -1296,9 +1296,14 @@ void Item_sum_min_max::setup_hybrid(THD *thd, Item *item, Item *value_arg) /* Don't cache value, as it will change */ if (!item->const_item()) arg_cache->set_used_tables(RAND_TABLE_BIT); + DBUG_ASSERT(item->type_handler_for_comparison() == + value->type_handler_for_comparison()); + DBUG_ASSERT(item->type_handler_for_comparison() == + arg_cache->type_handler_for_comparison()); cmp= new (thd->mem_root) Arg_comparator(); if (cmp) - cmp->set_cmp_func(thd, this, (Item**)&arg_cache, (Item**)&value, FALSE); + cmp->set_cmp_func(thd, this, item->type_handler_for_comparison(), + (Item**)&arg_cache, (Item**)&value, FALSE); DBUG_VOID_RETURN; } @@ -4287,8 +4292,14 @@ Item_func_group_concat::fix_fields(THD *thd, Item **ref) char *buf; String *new_separator; - if (!(buf= (char*) thd->stmt_arena->alloc(buflen)) || - !(new_separator= new(thd->stmt_arena->mem_root) + DBUG_ASSERT(thd->active_stmt_arena_to_use()-> + is_stmt_prepare_or_first_stmt_execute() || + thd->active_stmt_arena_to_use()-> + is_conventional() || + thd->active_stmt_arena_to_use()->state == + Query_arena::STMT_SP_QUERY_ARGUMENTS); + if (!(buf= (char*) thd->active_stmt_arena_to_use()->alloc(buflen)) || + !(new_separator= new(thd->active_stmt_arena_to_use()->mem_root) String(buf, buflen, collation.collation))) return TRUE; diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index b624a381..db38e44d 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -3080,6 +3080,13 @@ void Item_char_typecast::print(String *str, enum_query_type query_type) { str->append(STRING_WITH_LEN(" charset ")); str->append(cast_cs->cs_name); + /* + Print the "binary" keyword in cases like: + CAST('str' AS CHAR CHARACTER SET latin1 BINARY) + */ + if ((cast_cs->state & MY_CS_BINSORT) && + Charset(cast_cs).can_have_collate_clause()) + str->append(STRING_WITH_LEN(" binary")); } str->append(')'); } @@ -609,7 +609,7 @@ int key_rec_cmp(void *key_p, uchar *first_rec, uchar *second_rec) } /* No null values in the fields - We use the virtual method cmp_max with a max length parameter. + We use the virtual method cmp_prefix with a max length parameter. For most field types this translates into a cmp without max length. The exceptions are the BLOB and VARCHAR field types that take the max length into account. @@ -79,9 +79,9 @@ SYMBOL symbols[] = { { "AT", SYM(AT_SYM)}, { "ATOMIC", SYM(ATOMIC_SYM)}, { "AUTHORS", SYM(AUTHORS_SYM)}, + { "AUTO", SYM(AUTO_SYM)}, { "AUTO_INCREMENT", SYM(AUTO_INC)}, { "AUTOEXTEND_SIZE", SYM(AUTOEXTEND_SIZE_SYM)}, - { "AUTO", SYM(AUTO_SYM)}, { "AVG", SYM(AVG_SYM)}, { "AVG_ROW_LENGTH", SYM(AVG_ROW_LENGTH)}, { "BACKUP", SYM(BACKUP_SYM)}, @@ -428,7 +428,6 @@ SYMBOL symbols[] = { { "NCHAR", SYM(NCHAR_SYM)}, { "NESTED", SYM(NESTED_SYM)}, { "NEVER", SYM(NEVER_SYM)}, - { "NEW", SYM(NEW_SYM)}, { "NEXT", SYM(NEXT_SYM)}, { "NEXTVAL", SYM(NEXTVAL_SYM)}, { "NO", SYM(NO_SYM)}, @@ -686,7 +685,6 @@ SYMBOL symbols[] = { { "TRUE", SYM(TRUE_SYM)}, { "TRUNCATE", SYM(TRUNCATE_SYM)}, { "TYPE", SYM(TYPE_SYM)}, - { "TYPES", SYM(TYPES_SYM)}, { "UNBOUNDED", SYM(UNBOUNDED_SYM)}, { "UNCOMMITTED", SYM(UNCOMMITTED_SYM)}, { "UNDEFINED", SYM(UNDEFINED_SYM)}, @@ -752,7 +750,6 @@ SYMBOL symbols[] = { SYMBOL sql_functions[] = { { "ADDDATE", SYM(ADDDATE_SYM)}, - { "ADD_MONTHS", SYM(ADD_MONTHS_SYM)}, { "BIT_AND", SYM(BIT_AND)}, { "BIT_OR", SYM(BIT_OR)}, { "BIT_XOR", SYM(BIT_XOR)}, @@ -763,7 +760,6 @@ SYMBOL sql_functions[] = { { "CURTIME", SYM(CURTIME)}, { "DATE_ADD", SYM(DATE_ADD_INTERVAL)}, { "DATE_SUB", SYM(DATE_SUB_INTERVAL)}, - { "DATE_FORMAT", SYM(DATE_FORMAT_SYM)}, { "DENSE_RANK", SYM(DENSE_RANK_SYM)}, { "EXTRACT", SYM(EXTRACT_SYM)}, { "FIRST_VALUE", SYM(FIRST_VALUE_SYM)}, @@ -1457,7 +1457,7 @@ bool LOGGER::slow_log_print(THD *thd, const char *query, size_t query_length, query_utime= (current_utime - thd->start_utime); lock_utime= (thd->utime_after_lock - thd->start_utime); my_hrtime_t current_time= { hrtime_from_time(thd->start_time) + - thd->start_time_sec_part + query_utime }; + thd->start_time_sec_part }; if (!query || thd->get_command() == COM_STMT_PREPARE) { @@ -2539,6 +2539,23 @@ bool MYSQL_BIN_LOG::check_write_error(THD *thd) } +/* + Check if there was an error while writing the statement cache. + If the cache content is corrupt due to an error, we should write an incident + event to the binlog rather than write corrupt data to it. +*/ +bool +MYSQL_BIN_LOG::check_cache_error(THD *thd, binlog_cache_data *cache_data) +{ + if (!cache_data) + return false; + if (check_write_error(thd)) + return true; + if (!cache_data->empty() && cache_data->cache_log.error) + return true; + return false; +} + /** @note How do we handle this (unlikely but legal) case: @@ -3796,7 +3813,6 @@ bool MYSQL_BIN_LOG::open(const char *log_name, bool null_created_arg, bool need_mutex) { - File file= -1; xid_count_per_binlog *new_xid_list_entry= NULL, *b; DBUG_ENTER("MYSQL_BIN_LOG::open"); @@ -4192,8 +4208,6 @@ err: sql_print_error(fatal_log_error, (name) ? name : log_name, tmp_errno); if (new_xid_list_entry) delete new_xid_list_entry; - if (file >= 0) - mysql_file_close(file, MYF(0)); close(LOG_CLOSE_INDEX); DBUG_RETURN(1); } @@ -5465,8 +5479,6 @@ int MYSQL_BIN_LOG::new_file_without_locking() /** Start writing to a new log file or reopen the old file. - @param need_lock Set to 1 if caller has not locked LOCK_log - @retval nonzero - error @@ -6269,12 +6281,13 @@ bool THD::binlog_write_table_map(TABLE *table, bool with_annotate) int error= 1; bool is_transactional= table->file->row_logging_has_trans; DBUG_ENTER("THD::binlog_write_table_map"); - DBUG_PRINT("enter", ("table: %p (%s: #%lu)", + DBUG_PRINT("enter", ("table: %p (%s: #%llu)", table, table->s->table_name.str, table->s->table_map_id)); /* Pre-conditions */ - DBUG_ASSERT(table->s->table_map_id != ULONG_MAX); + DBUG_ASSERT((table->s->table_map_id & MAX_TABLE_MAP_ID) != UINT32_MAX && + (table->s->table_map_id & MAX_TABLE_MAP_ID) != 0); /* Ensure that all events in a GTID group are in the same cache */ if (variables.option_bits & OPTION_GTID_BEGIN) @@ -6315,7 +6328,7 @@ write_err: engines, data is written to table but writing to binary log failed. In these scenarios rollback is not possible. Hence report an incident. */ - if (mysql_bin_log.check_write_error(this) && cache_data && + if (mysql_bin_log.check_cache_error(this, cache_data) && lex->stmt_accessed_table(LEX::STMT_WRITES_NON_TRANS_TABLE) && table->current_lock == F_WRLCK) cache_data->set_incident(); @@ -6447,20 +6460,37 @@ MYSQL_BIN_LOG::flush_and_set_pending_rows_event(THD *thd, /* Write pending event to the cache. */ +#ifndef DBUG_OFF + bool clear_dbug= false; +#endif DBUG_EXECUTE_IF("simulate_disk_full_at_flush_pending", - {DBUG_SET("+d,simulate_file_write_error");}); + { + if (my_b_tell(&cache_data->cache_log) > 10000) + { + DBUG_SET("+d,simulate_file_write_error"); + clear_dbug= true; + } + }); if (writer.write(pending)) { set_write_error(thd, is_transactional); - if (check_write_error(thd) && cache_data && + if (check_cache_error(thd, cache_data) && stmt_has_updated_non_trans_table(thd)) cache_data->set_incident(); delete pending; cache_data->set_pending(NULL); DBUG_EXECUTE_IF("simulate_disk_full_at_flush_pending", - {DBUG_SET("-d,simulate_file_write_error");}); + { + if (clear_dbug) + DBUG_SET("-d,simulate_file_write_error"); + }); DBUG_RETURN(1); } + DBUG_EXECUTE_IF("simulate_disk_full_at_flush_pending", + { + if (clear_dbug) + DBUG_SET("-d,simulate_file_write_error"); + }); delete pending; } @@ -6950,18 +6980,12 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate) BINLOG_USER_VAR_EVENT *user_var_event; get_dynamic(&thd->user_var_events,(uchar*) &user_var_event, i); - /* setting flags for user var log event */ - uchar flags= User_var_log_event::UNDEF_F; - if (user_var_event->unsigned_flag) - flags|= User_var_log_event::UNSIGNED_F; - User_var_log_event e(thd, user_var_event->user_var_event->name.str, user_var_event->user_var_event->name.length, user_var_event->value, user_var_event->length, - user_var_event->type, - user_var_event->charset_number, - flags, + user_var_event->th->user_var_log_event_data_type( + user_var_event->charset_number), using_trans, direct); if (write_event(&e, cache_data, file)) @@ -7057,7 +7081,7 @@ err: if (unlikely(error)) { set_write_error(thd, is_trans_cache); - if (check_write_error(thd) && cache_data && + if (check_cache_error(thd, cache_data) && stmt_has_updated_non_trans_table(thd)) cache_data->set_incident(); } @@ -8751,13 +8775,10 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader) DEBUG_SYNC(leader->thd, "commit_loop_entry_commit_ordered"); ++num_commits; + set_current_thd(current->thd); if (current->cache_mngr->using_xa && likely(!current->error) && !DBUG_IF("skip_commit_ordered")) - { - mysql_mutex_lock(¤t->thd->LOCK_thd_data); run_commit_ordered(current->thd, current->all); - mysql_mutex_unlock(¤t->thd->LOCK_thd_data); - } current->thd->wakeup_subsequent_commits(current->error); /* @@ -8774,6 +8795,7 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader) } current= next; } + set_current_thd(leader->thd); DEBUG_SYNC(leader->thd, "commit_after_group_run_commit_ordered"); mysql_mutex_unlock(&LOCK_commit_ordered); DEBUG_SYNC(leader->thd, "commit_after_group_release_commit_ordered"); @@ -8794,6 +8816,20 @@ MYSQL_BIN_LOG::write_transaction_or_stmt(group_commit_entry *entry, DBUG_ENTER("MYSQL_BIN_LOG::write_transaction_or_stmt"); + /* + An error in the trx_cache will truncate the cache to the last good + statement, it won't leave a lingering error. Assert that this holds. + */ + DBUG_ASSERT(!(entry->using_trx_cache && !mngr->trx_cache.empty() && + mngr->get_binlog_cache_log(TRUE)->error)); + /* + An error in the stmt_cache would be caught on the higher level and result + in an incident event being written over a (possibly corrupt) cache content. + Assert that this holds. + */ + DBUG_ASSERT(!(entry->using_stmt_cache && !mngr->stmt_cache.empty() && + mngr->get_binlog_cache_log(FALSE)->error)); + if (write_gtid_event(entry->thd, is_prepared_xa(entry->thd), entry->using_trx_cache, commit_id, has_xid, entry->ro_1pc)) @@ -11136,7 +11172,7 @@ Recovery_context::Recovery_context() : prev_event_pos(0), last_gtid_standalone(false), last_gtid_valid(false), last_gtid_no2pc(false), last_gtid_engines(0), - do_truncate(rpl_semi_sync_slave_enabled), + do_truncate(repl_semisync_slave.get_slave_enabled()), truncate_validated(false), truncate_reset_done(false), truncate_set_in_1st(false), id_binlog(MAX_binlog_id), checksum_alg(BINLOG_CHECKSUM_ALG_UNDEF), gtid_maybe_to_truncate(NULL) @@ -11945,14 +11981,21 @@ set_binlog_snapshot_file(const char *src) void TC_LOG_BINLOG::set_status_variables(THD *thd) { - binlog_cache_mngr *cache_mngr; + bool have_snapshot= false; if (thd && opt_bin_log) - cache_mngr= (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton); - else - cache_mngr= 0; + { + mysql_mutex_lock(&thd->LOCK_thd_data); + auto cache_mngr= (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton); + have_snapshot= cache_mngr && cache_mngr->last_commit_pos_file[0]; + if (have_snapshot) + { + set_binlog_snapshot_file(cache_mngr->last_commit_pos_file); + binlog_snapshot_position= cache_mngr->last_commit_pos_offset; + } + mysql_mutex_unlock(&thd->LOCK_thd_data); + } - bool have_snapshot= (cache_mngr && cache_mngr->last_commit_pos_file[0] != 0); mysql_mutex_lock(&LOCK_commit_ordered); binlog_status_var_num_commits= this->num_commits; binlog_status_var_num_group_commits= this->num_group_commits; @@ -11967,12 +12010,6 @@ TC_LOG_BINLOG::set_status_variables(THD *thd) binlog_status_group_commit_trigger_timeout= this->group_commit_trigger_timeout; binlog_status_group_commit_trigger_lock_wait= this->group_commit_trigger_lock_wait; mysql_mutex_unlock(&LOCK_prepare_ordered); - - if (have_snapshot) - { - set_binlog_snapshot_file(cache_mngr->last_commit_pos_file); - binlog_snapshot_position= cache_mngr->last_commit_pos_offset; - } } @@ -12083,6 +12120,15 @@ IO_CACHE *wsrep_get_cache(THD * thd, bool is_transactional) return NULL; } +bool wsrep_is_binlog_cache_empty(THD *thd) +{ + binlog_cache_mngr *cache_mngr= + (binlog_cache_mngr *) thd_get_ha_data(thd, binlog_hton); + if (cache_mngr) + return cache_mngr->trx_cache.empty() && cache_mngr->stmt_cache.empty(); + return true; +} + void wsrep_thd_binlog_trx_reset(THD * thd) { DBUG_ENTER("wsrep_thd_binlog_trx_reset"); @@ -12143,12 +12189,9 @@ void wsrep_register_binlog_handler(THD *thd, bool trx) /* Set an implicit savepoint in order to be able to truncate a trx-cache. */ - if (cache_mngr->trx_cache.get_prev_position() == MY_OFF_T_UNDEF) - { - my_off_t pos= 0; - binlog_trans_log_savepos(thd, &pos); - cache_mngr->trx_cache.set_prev_position(pos); - } + my_off_t pos= 0; + binlog_trans_log_savepos(thd, &pos); + cache_mngr->trx_cache.set_prev_position(pos); /* Set callbacks in order to be able to call commmit or rollback. @@ -824,6 +824,7 @@ public: int write_cache(THD *thd, IO_CACHE *cache); void set_write_error(THD *thd, bool is_transactional); bool check_write_error(THD *thd); + bool check_cache_error(THD *thd, binlog_cache_data *cache_data); void start_union_events(THD *thd, query_id_t query_id_param); void stop_union_events(THD *thd); @@ -1253,6 +1254,7 @@ static inline TC_LOG *get_tc_log_implementation() #ifdef WITH_WSREP IO_CACHE* wsrep_get_cache(THD *, bool); +bool wsrep_is_binlog_cache_empty(THD *); void wsrep_thd_binlog_trx_reset(THD * thd); void wsrep_thd_binlog_stmt_rollback(THD * thd); #endif /* WITH_WSREP */ diff --git a/sql/log_event.cc b/sql/log_event.cc index 5e255646..336b032f 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -915,7 +915,8 @@ int Log_event::read_log_event(IO_CACHE* file, String* packet, Log_event* Log_event::read_log_event(IO_CACHE* file, const Format_description_log_event *fdle, - my_bool crc_check) + my_bool crc_check, + my_bool print_errors) { DBUG_ENTER("Log_event::read_log_event(IO_CACHE*,Format_description_log_event*...)"); DBUG_ASSERT(fdle != 0); @@ -954,8 +955,12 @@ Log_event* Log_event::read_log_event(IO_CACHE* file, goto err; } + /* + print_errors is false to prevent redundant error messages cluttering up the + log, as it will be printed below (if _our_ print_errors is true) + */ if ((res= read_log_event((uchar*) event.ptr(), event.length(), - &error, fdle, crc_check))) + &error, fdle, crc_check, false))) res->register_temp_buf((uchar*) event.release(), true); err: @@ -966,13 +971,7 @@ err: if (force_opt) DBUG_RETURN(new Unknown_log_event()); #endif - if (event.length() >= OLD_HEADER_LEN) - sql_print_error("Error in Log_event::read_log_event(): '%s'," - " 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); + /* The SQL slave thread will check if file->error<0 to know if there was an I/O error. Even if there is no "low-level" I/O errors @@ -982,6 +981,19 @@ err: only corrupt the slave's databases. So stop. */ file->error= -1; + +#ifndef MYSQL_CLIENT + if (!print_errors) + DBUG_RETURN(res); +#endif + + if (event.length() >= OLD_HEADER_LEN) + sql_print_error("Error in Log_event::read_log_event(): '%s'," + " 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); } DBUG_RETURN(res); } @@ -995,7 +1007,8 @@ err: Log_event* Log_event::read_log_event(const uchar *buf, uint event_len, const char **error, const Format_description_log_event *fdle, - my_bool crc_check) + my_bool crc_check, + my_bool print_errors) { Log_event* ev; enum enum_binlog_checksum_alg alg; @@ -1063,7 +1076,8 @@ Log_event* Log_event::read_log_event(const uchar *buf, uint event_len, DBUG_RETURN(NULL); #else *error= ER_THD_OR_DEFAULT(current_thd, ER_BINLOG_READ_EVENT_CHECKSUM_FAILURE); - sql_print_error("%s", *error); + if (print_errors) + sql_print_error("%s", *error); DBUG_RETURN(NULL); #endif } @@ -2928,6 +2942,41 @@ XA_prepare_log_event(const uchar *buf, User_var_log_event methods **************************************************************************/ +bool Log_event_data_type::unpack_optional_attributes(const char *pos, + const char *end) + +{ + for ( ; pos < end; ) + { + switch (*pos) { + case CHUNK_SIGNED: + m_is_unsigned= false; + pos++; + continue; + case CHUNK_UNSIGNED: + m_is_unsigned= true; + pos++; + continue; + case CHUNK_DATA_TYPE_NAME: + { + pos++; + if (pos >= end) + return true; + uint length= (uchar) *pos++; + if (pos + length > end) + return true; + m_data_type_name= {pos, length}; + pos+= length; + continue; + } + default: + break; // Unknown chunk + } + } + return false; +} + + User_var_log_event:: User_var_log_event(const uchar *buf, uint event_len, const Format_description_log_event* description_event) @@ -2937,7 +2986,8 @@ User_var_log_event(const uchar *buf, uint event_len, #endif { bool error= false; - const uchar *buf_start= buf, *buf_end= buf + event_len; + const uchar *const buf_start= buf; + const char *buf_end= reinterpret_cast<const char*>(buf) + event_len; /* The Post-Header is empty. The Variable Data part begins immediately. */ buf+= description_event->common_header_len + @@ -2965,11 +3015,8 @@ User_var_log_event(const uchar *buf, uint event_len, buf+= UV_NAME_LEN_SIZE + name_len; is_null= (bool) *buf; - flags= User_var_log_event::UNDEF_F; // defaults to UNDEF_F if (is_null) { - type= STRING_RESULT; - charset_number= my_charset_bin.number; val_len= 0; val= 0; } @@ -2984,8 +3031,8 @@ User_var_log_event(const uchar *buf, uint event_len, goto err; } - type= (Item_result) buf[UV_VAL_IS_NULL]; - charset_number= uint4korr(buf + UV_VAL_IS_NULL + UV_VAL_TYPE_SIZE); + m_type= (Item_result) buf[UV_VAL_IS_NULL]; + m_charset_number= uint4korr(buf + UV_VAL_IS_NULL + UV_VAL_TYPE_SIZE); val_len= uint4korr(buf + UV_VAL_IS_NULL + UV_VAL_TYPE_SIZE + UV_CHARSET_NUMBER_SIZE); @@ -2998,20 +3045,14 @@ User_var_log_event(const uchar *buf, uint event_len, the flags value. Old events will not have this extra byte, thence, - we keep the flags set to UNDEF_F. + we keep m_is_unsigned==false. */ - size_t bytes_read= (val + val_len) - (char*) buf_start; - if (bytes_read > event_len) + const char *pos= val + val_len; + if (pos > buf_end || unpack_optional_attributes(pos, buf_end)) { error= true; goto err; } - if ((data_written - bytes_read) > 0) - { - flags= (uint) *(buf + UV_VAL_IS_NULL + UV_VAL_TYPE_SIZE + - UV_CHARSET_NUMBER_SIZE + UV_VAL_LEN_SIZE + - val_len); - } } err: @@ -3305,7 +3346,7 @@ Rows_log_event::Rows_log_event(const uchar *buf, uint event_len, } else { - m_table_id= (ulong) uint6korr(post_start); + m_table_id= (ulonglong) uint6korr(post_start); post_start+= RW_FLAGS_OFFSET; } @@ -3660,11 +3701,12 @@ Table_map_log_event::Table_map_log_event(const uchar *buf, uint event_len, else { DBUG_ASSERT(post_header_len == TABLE_MAP_HEADER_LEN); - m_table_id= (ulong) uint6korr(post_start); + m_table_id= (ulonglong) uint6korr(post_start); post_start+= TM_FLAGS_OFFSET; } - DBUG_ASSERT(m_table_id != ~0ULL); + DBUG_ASSERT((m_table_id & MAX_TABLE_MAP_ID) != UINT32_MAX && + (m_table_id & MAX_TABLE_MAP_ID) != 0); m_flags= uint2korr(post_start); diff --git a/sql/log_event.h b/sql/log_event.h index 67e06d70..f6101eb7 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -57,6 +57,8 @@ #include "rpl_gtid.h" +#include "log_event_data_type.h" + /* Forward declarations */ #ifndef MYSQL_CLIENT class String; @@ -156,6 +158,12 @@ class String; #define NUM_LOAD_DELIM_STRS 5 +/* + The following is the max table_map_id. This is limited by that we + are using 6 bytes for it in replication +*/ +#define MAX_TABLE_MAP_ID ((1ULL << (6*8)) -1) + /***************************************************************************** MySQL Binary Log @@ -1379,7 +1387,8 @@ public: static Log_event* read_log_event(IO_CACHE* file, const Format_description_log_event *description_event, - my_bool crc_check); + my_bool crc_check, + my_bool print_errors= 1); /** Reads an event from a binlog or relay log. Used by the dump thread @@ -1523,7 +1532,8 @@ public: static Log_event* read_log_event(const uchar *buf, uint event_len, const char **error, const Format_description_log_event - *description_event, my_bool crc_check); + *description_event, my_bool crc_check, + my_bool print_errors= 1); /** Returns the human readable name of the given event type. */ @@ -3338,33 +3348,27 @@ private: @section User_var_log_event_binary_format Binary Format */ -class User_var_log_event: public Log_event + +class User_var_log_event: public Log_event, public Log_event_data_type { public: - enum { - UNDEF_F= 0, - UNSIGNED_F= 1 - }; const char *name; size_t name_len; const char *val; size_t val_len; - Item_result type; - uint charset_number; bool is_null; - uchar flags; #ifdef MYSQL_SERVER bool deferred; query_id_t query_id; User_var_log_event(THD* thd_arg, const char *name_arg, size_t name_len_arg, const char *val_arg, size_t val_len_arg, - Item_result type_arg, - uint charset_number_arg, uchar flags_arg, + const Log_event_data_type &data_type, bool using_trans, bool direct) :Log_event(thd_arg, 0, using_trans), + Log_event_data_type(data_type), name(name_arg), name_len(name_len_arg), val(val_arg), - val_len(val_len_arg), type(type_arg), charset_number(charset_number_arg), - flags(flags_arg), deferred(false) + val_len(val_len_arg), + deferred(false) { is_null= !val; if (direct) @@ -4865,7 +4869,8 @@ public: flag_set get_flags(flag_set flag) const { return m_flags & flag; } #ifdef MYSQL_SERVER - Table_map_log_event(THD *thd, TABLE *tbl, ulong tid, bool is_transactional); + Table_map_log_event(THD *thd, TABLE *tbl, ulonglong tid, + bool is_transactional); #endif #ifdef HAVE_REPLICATION Table_map_log_event(const uchar *buf, uint event_len, @@ -5191,7 +5196,7 @@ protected: this class, not create instances of this class. */ #ifdef MYSQL_SERVER - Rows_log_event(THD*, TABLE*, ulong table_id, + Rows_log_event(THD*, TABLE*, ulonglong table_id, MY_BITMAP const *cols, bool is_transactional, Log_event_type event_type); #endif @@ -5425,7 +5430,7 @@ public: }; #if defined(MYSQL_SERVER) - Write_rows_log_event(THD*, TABLE*, ulong table_id, + Write_rows_log_event(THD*, TABLE*, ulonglong table_id, bool is_transactional); #endif #ifdef HAVE_REPLICATION @@ -5466,7 +5471,7 @@ class Write_rows_compressed_log_event : public Write_rows_log_event { public: #if defined(MYSQL_SERVER) - Write_rows_compressed_log_event(THD*, TABLE*, ulong table_id, + Write_rows_compressed_log_event(THD*, TABLE*, ulonglong table_id, bool is_transactional); virtual bool write(); #endif @@ -5502,7 +5507,7 @@ public: }; #ifdef MYSQL_SERVER - Update_rows_log_event(THD*, TABLE*, ulong table_id, + Update_rows_log_event(THD*, TABLE*, ulonglong table_id, bool is_transactional); void init(MY_BITMAP const *cols); @@ -5554,7 +5559,7 @@ class Update_rows_compressed_log_event : public Update_rows_log_event { public: #if defined(MYSQL_SERVER) - Update_rows_compressed_log_event(THD*, TABLE*, ulong table_id, + Update_rows_compressed_log_event(THD*, TABLE*, ulonglong table_id, bool is_transactional); virtual bool write(); #endif @@ -5598,7 +5603,7 @@ public: }; #ifdef MYSQL_SERVER - Delete_rows_log_event(THD*, TABLE*, ulong, bool is_transactional); + Delete_rows_log_event(THD*, TABLE*, ulonglong, bool is_transactional); #endif #ifdef HAVE_REPLICATION Delete_rows_log_event(const uchar *buf, uint event_len, @@ -5639,7 +5644,8 @@ class Delete_rows_compressed_log_event : public Delete_rows_log_event { public: #if defined(MYSQL_SERVER) - Delete_rows_compressed_log_event(THD*, TABLE*, ulong, bool is_transactional); + Delete_rows_compressed_log_event(THD*, TABLE*, ulonglong, + bool is_transactional); virtual bool write(); #endif #ifdef HAVE_REPLICATION diff --git a/sql/log_event_client.cc b/sql/log_event_client.cc index 4ae8bffc..ddd62b08 100644 --- a/sql/log_event_client.cc +++ b/sql/log_event_client.cc @@ -1503,8 +1503,9 @@ bool Rows_log_event::print_verbose(IO_CACHE *file, if (!(map= print_event_info->m_table_map.get_table(m_table_id)) || !(td= map->create_table_def())) { - return (my_b_printf(file, "### Row event for unknown table #%lu", - (ulong) m_table_id)); + char llbuff[22]; + return (my_b_printf(file, "### Row event for unknown table #%s", + ullstr(m_table_id, llbuff))); } /* If the write rows event contained no values for the AI */ @@ -2488,7 +2489,7 @@ bool User_var_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) } else { - switch (type) { + switch (m_type) { case REAL_RESULT: double real_val; char real_buf[FMT_G_BUFSIZE(14)]; @@ -2500,8 +2501,7 @@ bool User_var_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) break; case INT_RESULT: char int_buf[22]; - longlong10_to_str(uint8korr(val), int_buf, - ((flags & User_var_log_event::UNSIGNED_F) ? 10 : -10)); + longlong10_to_str(uint8korr(val), int_buf, is_unsigned() ? 10 : -10); if (my_b_printf(&cache, ":=%s%s\n", int_buf, print_event_info->delimiter)) goto err; @@ -2556,7 +2556,7 @@ bool User_var_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) people want to mysqlbinlog|mysql into another server not supporting the character set. But there's not much to do about this and it's unlikely. */ - if (!(cs= get_charset(charset_number, MYF(0)))) + if (!(cs= get_charset(m_charset_number, MYF(0)))) { /* Generate an unusable command (=> syntax error) is probably the best thing we can do here. diff --git a/sql/log_event_data_type.h b/sql/log_event_data_type.h new file mode 100644 index 00000000..e3b2039a --- /dev/null +++ b/sql/log_event_data_type.h @@ -0,0 +1,74 @@ +/* Copyright (c) 2024, MariaDB Corporation. + + 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-1335 USA */ + +#ifndef LOG_EVENT_DATA_TYPE_H +#define LOG_EVENT_DATA_TYPE_H + +class Log_event_data_type +{ +public: + + enum { + CHUNK_SIGNED= 0, + CHUNK_UNSIGNED= 1, + CHUNK_DATA_TYPE_NAME= 2 + }; + +protected: + LEX_CSTRING m_data_type_name; + Item_result m_type; + uint m_charset_number; + bool m_is_unsigned; + +public: + + Log_event_data_type() + :m_data_type_name({NULL,0}), + m_type(STRING_RESULT), + m_charset_number(my_charset_bin.number), + m_is_unsigned(false) + { } + + Log_event_data_type(const LEX_CSTRING &data_type_name_arg, + Item_result type_arg, + uint charset_number_arg, + bool is_unsigned_arg) + :m_data_type_name(data_type_name_arg), + m_type(type_arg), + m_charset_number(charset_number_arg), + m_is_unsigned(is_unsigned_arg) + { } + + const LEX_CSTRING & data_type_name() const + { + return m_data_type_name; + } + Item_result type() const + { + return m_type; + } + uint charset_number() const + { + return m_charset_number; + } + bool is_unsigned() const + { + return m_is_unsigned; + } + + bool unpack_optional_attributes(const char *str, const char *end); +}; + +#endif // LOG_EVENT_DATA_TYPE_H diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc index 19901035..c02c22f4 100644 --- a/sql/log_event_old.cc +++ b/sql/log_event_old.cc @@ -47,12 +47,12 @@ Old_rows_log_event::do_apply_event(Old_rows_log_event *ev, rpl_group_info *rgi) const Relay_log_info *rli= rgi->rli; /* - If m_table_id == ~0UL, then we have a dummy event that does not + If m_table_id == UINT32_MAX, then we have a dummy event that does not contain any data. In that case, we just remove all tables in the tables_to_lock list, close the thread tables, and return with success. */ - if (ev->m_table_id == ~0UL) + if (ev->m_table_id == UINT32_MAX) { /* This one is supposed to be set: just an extra check so that @@ -1123,13 +1123,14 @@ int Update_rows_log_event_old::do_exec_row(TABLE *table) **************************************************************************/ #ifndef MYSQL_CLIENT -Old_rows_log_event::Old_rows_log_event(THD *thd_arg, TABLE *tbl_arg, ulong tid, +Old_rows_log_event::Old_rows_log_event(THD *thd_arg, TABLE *tbl_arg, + ulonglong table_id, MY_BITMAP const *cols, bool is_transactional) : Log_event(thd_arg, 0, is_transactional), m_row_count(0), m_table(tbl_arg), - m_table_id(tid), + m_table_id(table_id), m_width(tbl_arg ? tbl_arg->s->fields : 1), m_rows_buf(0), m_rows_cur(0), m_rows_end(0), m_flags(0) #ifdef HAVE_REPLICATION @@ -1142,12 +1143,12 @@ Old_rows_log_event::Old_rows_log_event(THD *thd_arg, TABLE *tbl_arg, ulong tid, /* We allow a special form of dummy event when the table, and cols - are null and the table id is ~0UL. This is a temporary + are null and the table id is UINT32_MAX. This is a temporary solution, to be able to terminate a started statement in the binary log: the extraneous events will be removed in the future. */ - DBUG_ASSERT((tbl_arg && tbl_arg->s && tid != ~0UL) || - (!tbl_arg && !cols && tid == ~0UL)); + DBUG_ASSERT((tbl_arg && tbl_arg->s && table_id != UINT32_MAX) || + (!tbl_arg && !cols && table_id == UINT32_MAX)); if (thd_arg->variables.option_bits & OPTION_NO_FOREIGN_KEY_CHECKS) set_flags(NO_FOREIGN_KEY_CHECKS_F); @@ -1208,7 +1209,7 @@ Old_rows_log_event::Old_rows_log_event(const uchar *buf, uint event_len, } else { - m_table_id= (ulong) uint6korr(post_start); + m_table_id= (ulonglong) uint6korr(post_start); post_start+= RW_FLAGS_OFFSET; } @@ -1248,7 +1249,7 @@ Old_rows_log_event::Old_rows_log_event(const uchar *buf, uint event_len, const uchar* const ptr_rows_data= (const uchar*) ptr_after_width; size_t const data_size= event_len - (ptr_rows_data - (const uchar *) buf); - DBUG_PRINT("info",("m_table_id: %lu m_flags: %d m_width: %lu data_size: %zu", + DBUG_PRINT("info",("m_table_id: %llu m_flags: %d m_width: %lu data_size: %zu", m_table_id, m_flags, m_width, data_size)); DBUG_DUMP("rows_data", (uchar*) ptr_rows_data, data_size); @@ -1362,12 +1363,12 @@ int Old_rows_log_event::do_apply_event(rpl_group_info *rgi) Relay_log_info const *rli= rgi->rli; /* - If m_table_id == ~0UL, then we have a dummy event that does not + If m_table_id == UINT32_MAX, then we have a dummy event that does not contain any data. In that case, we just remove all tables in the tables_to_lock list, close the thread tables, and return with success. */ - if (m_table_id == ~0UL) + if (m_table_id == UINT32_MAX) { /* This one is supposed to be set: just an extra check so that @@ -1784,10 +1785,10 @@ bool Old_rows_log_event::write_data_header() // This method should not be reached. assert(0); - DBUG_ASSERT(m_table_id != ~0UL); + DBUG_ASSERT(m_table_id != UINT32_MAX); DBUG_EXECUTE_IF("old_row_based_repl_4_byte_map_id_master", { - int4store(buf + 0, m_table_id); + int4store(buf + 0, (ulong) m_table_id); int2store(buf + 4, m_flags); return write_data(buf, 6); }); @@ -1834,7 +1835,7 @@ void Old_rows_log_event::pack_info(Protocol *protocol) char const *const flagstr= get_flags(STMT_END_F) ? " flags: STMT_END_F" : ""; size_t bytes= my_snprintf(buf, sizeof(buf), - "table_id: %lu%s", m_table_id, flagstr); + "table_id: %llu%s", m_table_id, flagstr); protocol->store(buf, bytes, &my_charset_bin); } #endif @@ -1856,9 +1857,10 @@ bool Old_rows_log_event::print_helper(FILE *file, if (!print_event_info->short_form) { + char llbuff[22]; if (print_header(head, print_event_info, !do_print_encoded) || - my_b_printf(head, "\t%s: table id %lu%s\n", - name, m_table_id, + my_b_printf(head, "\t%s: table id %s%s\n", + name, ullstr(m_table_id, llbuff), do_print_encoded ? " flags: STMT_END_F" : "") || print_base64(body, print_event_info, do_print_encoded)) goto err; @@ -2398,7 +2400,7 @@ int Old_rows_log_event::find_row(rpl_group_info *rgi) #if !defined(MYSQL_CLIENT) Write_rows_log_event_old::Write_rows_log_event_old(THD *thd_arg, TABLE *tbl_arg, - ulong tid_arg, + ulonglong tid_arg, MY_BITMAP const *cols, bool is_transactional) : Old_rows_log_event(thd_arg, tbl_arg, tid_arg, cols, is_transactional) @@ -2510,7 +2512,7 @@ bool Write_rows_log_event_old::print(FILE *file, #ifndef MYSQL_CLIENT Delete_rows_log_event_old::Delete_rows_log_event_old(THD *thd_arg, TABLE *tbl_arg, - ulong tid, + ulonglong tid, MY_BITMAP const *cols, bool is_transactional) : Old_rows_log_event(thd_arg, tbl_arg, tid, cols, is_transactional), @@ -2618,7 +2620,7 @@ bool Delete_rows_log_event_old::print(FILE *file, #if !defined(MYSQL_CLIENT) Update_rows_log_event_old::Update_rows_log_event_old(THD *thd_arg, TABLE *tbl_arg, - ulong tid, + ulonglong tid, MY_BITMAP const *cols, bool is_transactional) : Old_rows_log_event(thd_arg, tbl_arg, tid, cols, is_transactional), diff --git a/sql/log_event_old.h b/sql/log_event_old.h index e5aaacec..1afe9aba 100644 --- a/sql/log_event_old.h +++ b/sql/log_event_old.h @@ -131,7 +131,7 @@ public: MY_BITMAP const *get_cols() const { return &m_cols; } size_t get_width() const { return m_width; } - ulong get_table_id() const { return m_table_id; } + ulonglong get_table_id() const { return m_table_id; } #ifndef MYSQL_CLIENT virtual bool write_data_header(); @@ -158,7 +158,7 @@ protected: this class, not create instances of this class. */ #ifndef MYSQL_CLIENT - Old_rows_log_event(THD*, TABLE*, ulong table_id, + Old_rows_log_event(THD*, TABLE*, ulonglong table_id, MY_BITMAP const *cols, bool is_transactional); #endif Old_rows_log_event(const uchar *row_data, uint event_len, @@ -176,7 +176,7 @@ protected: #ifndef MYSQL_CLIENT TABLE *m_table; /* The table the rows belong to */ #endif - ulong m_table_id; /* Table ID */ + ulonglong m_table_id; /* Table ID */ MY_BITMAP m_cols; /* Bitmap denoting columns available */ ulong m_width; /* The width of the columns bitmap */ @@ -359,7 +359,7 @@ class Write_rows_log_event_old : public Old_rows_log_event /********** BEGIN CUT & PASTE FROM Write_rows_log_event **********/ public: #if !defined(MYSQL_CLIENT) - Write_rows_log_event_old(THD*, TABLE*, ulong table_id, + Write_rows_log_event_old(THD*, TABLE*, ulonglong table_id, MY_BITMAP const *cols, bool is_transactional); #endif #ifdef HAVE_REPLICATION @@ -430,7 +430,7 @@ class Update_rows_log_event_old : public Old_rows_log_event /********** BEGIN CUT & PASTE FROM Update_rows_log_event **********/ public: #ifndef MYSQL_CLIENT - Update_rows_log_event_old(THD*, TABLE*, ulong table_id, + Update_rows_log_event_old(THD*, TABLE*, ulonglong table_id, MY_BITMAP const *cols, bool is_transactional); #endif @@ -507,7 +507,7 @@ class Delete_rows_log_event_old : public Old_rows_log_event /********** BEGIN CUT & PASTE FROM Update_rows_log_event **********/ public: #ifndef MYSQL_CLIENT - Delete_rows_log_event_old(THD*, TABLE*, ulong, + Delete_rows_log_event_old(THD*, TABLE*, ulonglong, MY_BITMAP const *cols, bool is_transactional); #endif #ifdef HAVE_REPLICATION diff --git a/sql/log_event_server.cc b/sql/log_event_server.cc index 5cb15c1c..84a00b5d 100644 --- a/sql/log_event_server.cc +++ b/sql/log_event_server.cc @@ -918,6 +918,10 @@ int Log_event_writer::write_header(uchar *pos, size_t len) int Log_event_writer::write_data(const uchar *pos, size_t len) { DBUG_ENTER("Log_event_writer::write_data"); + + if (!len) + DBUG_RETURN(0); + if (checksum_len) crc= my_checksum(crc, pos, len); @@ -4561,11 +4565,16 @@ bool XA_prepare_log_event::write() #if defined(HAVE_REPLICATION) static bool user_var_append_name_part(THD *thd, String *buf, - const char *name, size_t name_len) + const char *name, size_t name_len, + const LEX_CSTRING &data_type_name) { return buf->append('@') || append_identifier(thd, buf, name, name_len) || - buf->append('='); + buf->append('=') || + (data_type_name.length && + (buf->append(STRING_WITH_LEN("/*")) || + buf->append(data_type_name.str, data_type_name.length) || + buf->append(STRING_WITH_LEN("*/")))); } void User_var_log_event::pack_info(Protocol* protocol) @@ -4575,14 +4584,15 @@ void User_var_log_event::pack_info(Protocol* protocol) char buf_mem[FN_REFLEN+7]; String buf(buf_mem, sizeof(buf_mem), system_charset_info); buf.length(0); - if (user_var_append_name_part(protocol->thd, &buf, name, name_len) || + if (user_var_append_name_part(protocol->thd, &buf, name, name_len, + m_data_type_name) || buf.append(NULL_clex_str)) return; protocol->store(buf.ptr(), buf.length(), &my_charset_bin); } else { - switch (type) { + switch (m_type) { case REAL_RESULT: { double real_val; @@ -4591,7 +4601,8 @@ void User_var_log_event::pack_info(Protocol* protocol) String buf(buf_mem, sizeof(buf_mem), system_charset_info); float8get(real_val, val); buf.length(0); - if (user_var_append_name_part(protocol->thd, &buf, name, name_len) || + if (user_var_append_name_part(protocol->thd, &buf, name, name_len, + m_data_type_name) || buf.append(buf2, my_gcvt(real_val, MY_GCVT_ARG_DOUBLE, MY_GCVT_MAX_FIELD_WIDTH, buf2, NULL))) return; @@ -4604,10 +4615,11 @@ void User_var_log_event::pack_info(Protocol* protocol) char buf_mem[FN_REFLEN + 22]; String buf(buf_mem, sizeof(buf_mem), system_charset_info); buf.length(0); - if (user_var_append_name_part(protocol->thd, &buf, name, name_len) || + if (user_var_append_name_part(protocol->thd, &buf, name, name_len, + m_data_type_name) || buf.append(buf2, longlong10_to_str(uint8korr(val), buf2, - ((flags & User_var_log_event::UNSIGNED_F) ? 10 : -10))-buf2)) + (is_unsigned() ? 10 : -10))-buf2)) return; protocol->store(buf.ptr(), buf.length(), &my_charset_bin); break; @@ -4620,7 +4632,8 @@ void User_var_log_event::pack_info(Protocol* protocol) String str(buf2, sizeof(buf2), &my_charset_bin); buf.length(0); my_decimal((const uchar *) (val + 2), val[0], val[1]).to_string(&str); - if (user_var_append_name_part(protocol->thd, &buf, name, name_len) || + if (user_var_append_name_part(protocol->thd, &buf, name, name_len, + m_data_type_name) || buf.append(str)) return; protocol->store(buf.ptr(), buf.length(), &my_charset_bin); @@ -4636,7 +4649,7 @@ void User_var_log_event::pack_info(Protocol* protocol) String buf(buf_mem, sizeof(buf_mem), system_charset_info); CHARSET_INFO *cs; buf.length(0); - if (!(cs= get_charset(charset_number, MYF(0)))) + if (!(cs= get_charset(m_charset_number, MYF(0)))) { if (buf.append(STRING_WITH_LEN("???"))) return; @@ -4645,7 +4658,8 @@ void User_var_log_event::pack_info(Protocol* protocol) { size_t old_len; char *beg, *end; - if (user_var_append_name_part(protocol->thd, &buf, name, name_len) || + if (user_var_append_name_part(protocol->thd, &buf, name, name_len, + m_data_type_name) || buf.append('_') || buf.append(cs->cs_name) || buf.append(' ')) @@ -4693,10 +4707,10 @@ bool User_var_log_event::write() } else { - buf1[1]= type; - int4store(buf1 + 2, charset_number); + buf1[1]= m_type; + int4store(buf1 + 2, m_charset_number); - switch (type) { + switch (m_type) { case REAL_RESULT: float8store(buf2, *(double*) val); break; @@ -4726,15 +4740,28 @@ bool User_var_log_event::write() buf1_length= 10; } + uchar data_type_name_chunk_signature= (uchar) CHUNK_DATA_TYPE_NAME; + uint data_type_name_chunk_signature_length= m_data_type_name.length ? 1 : 0; + uchar data_type_name_length_length= m_data_type_name.length ? 1 : 0; + /* Length of the whole event */ - event_length= sizeof(buf)+ name_len + buf1_length + val_len + unsigned_len; + event_length= sizeof(buf)+ name_len + buf1_length + val_len + unsigned_len + + data_type_name_chunk_signature_length + + data_type_name_length_length + + (uint) m_data_type_name.length; + uchar unsig= m_is_unsigned ? CHUNK_UNSIGNED : CHUNK_SIGNED; + uchar data_type_name_length= (uchar) m_data_type_name.length; return write_header(event_length) || write_data(buf, sizeof(buf)) || write_data(name, name_len) || write_data(buf1, buf1_length) || write_data(pos, val_len) || - write_data(&flags, unsigned_len) || + write_data(&unsig, unsigned_len) || + write_data(&data_type_name_chunk_signature, + data_type_name_chunk_signature_length) || + write_data(&data_type_name_length, data_type_name_length_length) || + write_data(m_data_type_name.str, (uint) m_data_type_name.length) || write_footer(); } @@ -4758,7 +4785,7 @@ int User_var_log_event::do_apply_event(rpl_group_info *rgi) current_thd->query_id= query_id; /* recreating original time context */ } - if (!(charset= get_charset(charset_number, MYF(MY_WME)))) + if (!(charset= get_charset(m_charset_number, MYF(MY_WME)))) { rgi->rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, ER_THD(thd, ER_SLAVE_FATAL_ERROR), @@ -4777,7 +4804,7 @@ int User_var_log_event::do_apply_event(rpl_group_info *rgi) } else { - switch (type) { + switch (m_type) { case REAL_RESULT: if (val_len != 8) { @@ -4841,13 +4868,10 @@ int User_var_log_event::do_apply_event(rpl_group_info *rgi) if (e->fix_fields(thd, 0)) DBUG_RETURN(1); - /* - A variable can just be considered as a table with - a single record and with a single column. Thus, like - a column value, it could always have IMPLICIT derivation. - */ - e->update_hash((void*) val, val_len, type, charset, - (flags & User_var_log_event::UNSIGNED_F)); + const Type_handler *th= Type_handler::handler_by_log_event_data_type(thd, + *this); + e->update_hash((void*) val, val_len, th, charset); + if (!is_deferred()) free_root(thd->mem_root, 0); else @@ -5575,13 +5599,14 @@ bool sql_ex_info::write_data(Log_event_writer *writer) Rows_log_event member functions **************************************************************************/ -Rows_log_event::Rows_log_event(THD *thd_arg, TABLE *tbl_arg, ulong tid, +Rows_log_event::Rows_log_event(THD *thd_arg, TABLE *tbl_arg, + ulonglong table_id, MY_BITMAP const *cols, bool is_transactional, Log_event_type event_type) : Log_event(thd_arg, 0, is_transactional), m_row_count(0), m_table(tbl_arg), - m_table_id(tid), + m_table_id(table_id), m_width(tbl_arg ? tbl_arg->s->fields : 1), m_rows_buf(0), m_rows_cur(0), m_rows_end(0), m_flags(0), m_type(event_type), m_extra_row_data(0) @@ -5593,12 +5618,13 @@ Rows_log_event::Rows_log_event(THD *thd_arg, TABLE *tbl_arg, ulong tid, { /* We allow a special form of dummy event when the table, and cols - are null and the table id is ~0UL. This is a temporary + are null and the table id is UINT32_MAX. This is a temporary solution, to be able to terminate a started statement in the binary log: the extraneous events will be removed in the future. */ - DBUG_ASSERT((tbl_arg && tbl_arg->s && tid != ~0UL) || - (!tbl_arg && !cols && tid == ~0UL)); + DBUG_ASSERT((tbl_arg && tbl_arg->s && + (table_id & MAX_TABLE_MAP_ID) != UINT32_MAX) || + (!tbl_arg && !cols && (table_id & MAX_TABLE_MAP_ID) == UINT32_MAX)); if (thd_arg->variables.option_bits & OPTION_NO_FOREIGN_KEY_CHECKS) set_flags(NO_FOREIGN_KEY_CHECKS_F); @@ -5745,12 +5771,12 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi) LEX *lex= thd->lex; uint8 new_trg_event_map= get_trg_event_map(); /* - If m_table_id == ~0ULL, then we have a dummy event that does not + If m_table_id == UINT32_MAX, then we have a dummy event that does not contain any data. In that case, we just remove all tables in the tables_to_lock list, close the thread tables, and return with success. */ - if (m_table_id == ~0ULL) + if (m_table_id == UINT32_MAX) { /* This one is supposed to be set: just an extra check so that @@ -6412,10 +6438,10 @@ Rows_log_event::do_update_pos(rpl_group_info *rgi) bool Rows_log_event::write_data_header() { uchar buf[ROWS_HEADER_LEN_V2]; // No need to init the buffer - DBUG_ASSERT(m_table_id != ~0ULL); + DBUG_ASSERT(m_table_id != UINT32_MAX); DBUG_EXECUTE_IF("old_row_based_repl_4_byte_map_id_master", { - int4store(buf + 0, m_table_id); + int4store(buf + 0, (ulong) m_table_id); int2store(buf + 4, m_flags); return (write_data(buf, 6)); }); @@ -6620,7 +6646,7 @@ int Table_map_log_event::save_field_metadata() Mats says tbl->s lives longer than this event so it's ok to copy pointers (tbl->s->db etc) and not pointer content. */ -Table_map_log_event::Table_map_log_event(THD *thd, TABLE *tbl, ulong tid, +Table_map_log_event::Table_map_log_event(THD *thd, TABLE *tbl, ulonglong tid, bool is_transactional) : Log_event(thd, 0, is_transactional), m_table(tbl), @@ -6643,7 +6669,7 @@ Table_map_log_event::Table_map_log_event(THD *thd, TABLE *tbl, ulong tid, uchar cbuf[MAX_INT_WIDTH]; uchar *cbuf_end; DBUG_ENTER("Table_map_log_event::Table_map_log_event(TABLE)"); - DBUG_ASSERT(m_table_id != ~0ULL); + DBUG_ASSERT(m_table_id != UINT32_MAX); /* In TABLE_SHARE, "db" and "table_name" are 0-terminated (see this comment in table.cc / alloc_table_share(): @@ -6929,7 +6955,7 @@ int Table_map_log_event::do_apply_event(rpl_group_info *rgi) char buf[256]; my_snprintf(buf, sizeof(buf), - "Found table map event mapping table id %u which " + "Found table map event mapping table id %llu which " "was already mapped but with different settings.", table_list->table_id); @@ -6970,11 +6996,11 @@ int Table_map_log_event::do_update_pos(rpl_group_info *rgi) bool Table_map_log_event::write_data_header() { - DBUG_ASSERT(m_table_id != ~0ULL); + DBUG_ASSERT(m_table_id != UINT32_MAX); uchar buf[TABLE_MAP_HEADER_LEN]; DBUG_EXECUTE_IF("old_row_based_repl_4_byte_map_id_master", { - int4store(buf + 0, m_table_id); + int4store(buf + 0, (ulong) m_table_id); int2store(buf + 4, m_flags); return (write_data(buf, 6)); }); @@ -7410,7 +7436,7 @@ void Table_map_log_event::pack_info(Protocol *protocol) { char buf[256]; size_t bytes= my_snprintf(buf, sizeof(buf), - "table_id: %llu (%s.%s)", + "table_id: %llu (%s.%s)", m_table_id, m_dbnam, m_tblnam); protocol->store(buf, bytes, &my_charset_bin); } @@ -7425,7 +7451,7 @@ void Table_map_log_event::pack_info(Protocol *protocol) Constructor used to build an event for writing to the binary log. */ Write_rows_log_event::Write_rows_log_event(THD *thd_arg, TABLE *tbl_arg, - ulong tid_arg, + ulonglong tid_arg, bool is_transactional) :Rows_log_event(thd_arg, tbl_arg, tid_arg, tbl_arg->rpl_write_set, is_transactional, WRITE_ROWS_EVENT_V1) @@ -7435,7 +7461,7 @@ Write_rows_log_event::Write_rows_log_event(THD *thd_arg, TABLE *tbl_arg, Write_rows_compressed_log_event::Write_rows_compressed_log_event( THD *thd_arg, TABLE *tbl_arg, - ulong tid_arg, + ulonglong tid_arg, bool is_transactional) : Write_rows_log_event(thd_arg, tbl_arg, tid_arg, is_transactional) { @@ -7521,7 +7547,7 @@ Write_rows_log_event::do_before_row_operations(const Slave_reporting_capability indexed and it cannot have a DEFAULT value). */ m_table->auto_increment_field_not_null= FALSE; - m_table->mark_auto_increment_column(); + m_table->mark_auto_increment_column(true); } return error; @@ -8554,7 +8580,8 @@ end: */ Delete_rows_log_event::Delete_rows_log_event(THD *thd_arg, TABLE *tbl_arg, - ulong tid, bool is_transactional) + ulonglong tid, + bool is_transactional) : Rows_log_event(thd_arg, tbl_arg, tid, tbl_arg->read_set, is_transactional, DELETE_ROWS_EVENT_V1) { @@ -8562,7 +8589,7 @@ Delete_rows_log_event::Delete_rows_log_event(THD *thd_arg, TABLE *tbl_arg, Delete_rows_compressed_log_event::Delete_rows_compressed_log_event( THD *thd_arg, TABLE *tbl_arg, - ulong tid_arg, + ulonglong tid_arg, bool is_transactional) : Delete_rows_log_event(thd_arg, tbl_arg, tid_arg, is_transactional) { @@ -8702,7 +8729,7 @@ uint8 Delete_rows_log_event::get_trg_event_map() Constructor used to build an event for writing to the binary log. */ Update_rows_log_event::Update_rows_log_event(THD *thd_arg, TABLE *tbl_arg, - ulong tid, + ulonglong tid, bool is_transactional) : Rows_log_event(thd_arg, tbl_arg, tid, tbl_arg->read_set, is_transactional, UPDATE_ROWS_EVENT_V1) @@ -8710,9 +8737,9 @@ Update_rows_log_event::Update_rows_log_event(THD *thd_arg, TABLE *tbl_arg, init(tbl_arg->rpl_write_set); } -Update_rows_compressed_log_event::Update_rows_compressed_log_event(THD *thd_arg, TABLE *tbl_arg, - ulong tid, - bool is_transactional) +Update_rows_compressed_log_event:: +Update_rows_compressed_log_event(THD *thd_arg, TABLE *tbl_arg, + ulonglong tid, bool is_transactional) : Update_rows_log_event(thd_arg, tbl_arg, tid, is_transactional) { m_type = UPDATE_ROWS_COMPRESSED_EVENT_V1; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 95286923..738fd73e 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -5221,6 +5221,14 @@ static int init_server_components() if (!opt_abort && ddl_log_initialize()) unireg_abort(1); + /* + Plugins may not be completed because system table DDLs are only + run after the ddl recovery done. Therefore between the + plugin_init() call and the ha_signal_ddl_recovery_done() call + below only things related to preparation for recovery should be + done and nothing else, and definitely not anything assuming that + all plugins have been initialised. + */ if (plugin_init(&remaining_argc, remaining_argv, (opt_noacl ? PLUGIN_INIT_SKIP_PLUGIN_TABLE : 0) | (opt_abort ? PLUGIN_INIT_SKIP_INITIALIZATION : 0))) @@ -5544,6 +5552,15 @@ static int init_server_components() #else locked_in_memory= 0; #endif +#ifdef PR_SET_THP_DISABLE + /* + Engine page buffers are now allocated. + Disable transparent huge pages for all + future allocations as these causes memory + leaks. + */ + prctl(PR_SET_THP_DISABLE, 1, 0, 0, 0); +#endif ft_init_stopwords(); @@ -5791,8 +5808,6 @@ int mysqld_main(int argc, char **argv) SYSVAR_AUTOSIZE(my_thread_stack_size, new_thread_stack_size); } - (void) thr_setconcurrency(concurrency); // 10 by default - select_thread=pthread_self(); select_thread_in_use=1; @@ -6867,8 +6882,8 @@ struct my_option my_long_options[]= #endif }; -static int show_queries(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +static int show_queries(THD *thd, SHOW_VAR *var, void *, + system_status_var *, enum_var_type) { var->type= SHOW_LONGLONG; var->value= &thd->query_id; @@ -6876,16 +6891,16 @@ static int show_queries(THD *thd, SHOW_VAR *var, char *buff, } -static int show_net_compression(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +static int show_net_compression(THD *thd, SHOW_VAR *var, void *, + system_status_var *, enum_var_type) { var->type= SHOW_MY_BOOL; var->value= &thd->net.compress; return 0; } -static int show_starttime(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +static int show_starttime(THD *thd, SHOW_VAR *var, void *buff, + system_status_var *, enum_var_type) { var->type= SHOW_LONG; var->value= buff; @@ -6894,8 +6909,8 @@ static int show_starttime(THD *thd, SHOW_VAR *var, char *buff, } #ifdef ENABLED_PROFILING -static int show_flushstatustime(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +static int show_flushstatustime(THD *thd, SHOW_VAR *var, void *buff, + system_status_var *, enum_var_type) { var->type= SHOW_LONG; var->value= buff; @@ -6905,32 +6920,28 @@ static int show_flushstatustime(THD *thd, SHOW_VAR *var, char *buff, #endif #ifdef HAVE_REPLICATION -static int show_rpl_status(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +static int show_rpl_status(THD *, SHOW_VAR *var, void *, system_status_var *, + enum_var_type) { var->type= SHOW_CHAR; var->value= const_cast<char*>(rpl_status_type[(int)rpl_status]); return 0; } -static int show_slave_running(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +static int show_slave_running(THD *thd, SHOW_VAR *var, void *buff, + system_status_var *, enum_var_type) { - Master_info *mi= NULL; - bool UNINIT_VAR(tmp); - - var->type= SHOW_MY_BOOL; - var->value= buff; - - if ((mi= get_master_info(&thd->variables.default_master_connection, - Sql_condition::WARN_LEVEL_NOTE))) + if (Master_info *mi= + get_master_info(&thd->variables.default_master_connection, + Sql_condition::WARN_LEVEL_NOTE)) { - tmp= (my_bool) (mi->slave_running == MYSQL_SLAVE_RUN_READING && - mi->rli.slave_running != MYSQL_SLAVE_NOT_RUN); + *((my_bool*) buff)= + (mi->slave_running == MYSQL_SLAVE_RUN_READING && + mi->rli.slave_running != MYSQL_SLAVE_NOT_RUN); mi->release(); + var->type= SHOW_MY_BOOL; + var->value= buff; } - if (mi) - *((my_bool *)buff)= tmp; else var->type= SHOW_UNDEF; return 0; @@ -6940,7 +6951,8 @@ static int show_slave_running(THD *thd, SHOW_VAR *var, char *buff, /* How many masters this slave is connected to */ -static int show_slaves_running(THD *thd, SHOW_VAR *var, char *buff) +static int show_slaves_running(THD *, SHOW_VAR *var, void *buff, + system_status_var *, enum_var_type) { var->type= SHOW_LONGLONG; var->value= buff; @@ -6951,19 +6963,17 @@ static int show_slaves_running(THD *thd, SHOW_VAR *var, char *buff) } -static int show_slave_received_heartbeats(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +static int show_slave_received_heartbeats(THD *thd, SHOW_VAR *var, void *buff, + system_status_var *, enum_var_type) { - Master_info *mi; - - var->type= SHOW_LONGLONG; - var->value= buff; - - if ((mi= get_master_info(&thd->variables.default_master_connection, - Sql_condition::WARN_LEVEL_NOTE))) + if (Master_info *mi= + get_master_info(&thd->variables.default_master_connection, + Sql_condition::WARN_LEVEL_NOTE)) { *((longlong *)buff)= mi->received_heartbeats; mi->release(); + var->type= SHOW_LONGLONG; + var->value= buff; } else var->type= SHOW_UNDEF; @@ -6971,19 +6981,17 @@ static int show_slave_received_heartbeats(THD *thd, SHOW_VAR *var, char *buff, } -static int show_heartbeat_period(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +static int show_heartbeat_period(THD *thd, SHOW_VAR *var, void *buff, + system_status_var *, enum_var_type) { - Master_info *mi; - - var->type= SHOW_CHAR; - var->value= buff; - - if ((mi= get_master_info(&thd->variables.default_master_connection, - Sql_condition::WARN_LEVEL_NOTE))) + if (Master_info *mi= + get_master_info(&thd->variables.default_master_connection, + Sql_condition::WARN_LEVEL_NOTE)) { - sprintf(buff, "%.3f", mi->heartbeat_period); + sprintf(static_cast<char*>(buff), "%.3f", mi->heartbeat_period); mi->release(); + var->type= SHOW_CHAR; + var->value= buff; } else var->type= SHOW_UNDEF; @@ -6993,8 +7001,8 @@ static int show_heartbeat_period(THD *thd, SHOW_VAR *var, char *buff, #endif /* HAVE_REPLICATION */ -static int show_open_tables(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +static int show_open_tables(THD *, SHOW_VAR *var, void *buff, + system_status_var *, enum_var_type) { var->type= SHOW_LONG; var->value= buff; @@ -7002,8 +7010,8 @@ static int show_open_tables(THD *thd, SHOW_VAR *var, char *buff, return 0; } -static int show_prepared_stmt_count(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +static int show_prepared_stmt_count(THD *, SHOW_VAR *var, void *buff, + system_status_var *, enum_var_type) { var->type= SHOW_LONG; var->value= buff; @@ -7013,8 +7021,8 @@ static int show_prepared_stmt_count(THD *thd, SHOW_VAR *var, char *buff, return 0; } -static int show_table_definitions(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +static int show_table_definitions(THD *, SHOW_VAR *var, void *buff, + system_status_var *, enum_var_type) { var->type= SHOW_LONG; var->value= buff; @@ -7033,8 +7041,8 @@ static int show_table_definitions(THD *thd, SHOW_VAR *var, char *buff, inside an Event. */ -static int show_ssl_get_version(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +static int show_ssl_get_version(THD *thd, SHOW_VAR *var, void *, + system_status_var *, enum_var_type) { var->type= SHOW_CHAR; if( thd->vio_ok() && thd->net.vio->ssl_arg ) @@ -7044,8 +7052,8 @@ static int show_ssl_get_version(THD *thd, SHOW_VAR *var, char *buff, return 0; } -static int show_ssl_get_default_timeout(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +static int show_ssl_get_default_timeout(THD *thd, SHOW_VAR *var, void *buff, + system_status_var *, enum_var_type) { var->type= SHOW_LONG; var->value= buff; @@ -7056,8 +7064,8 @@ static int show_ssl_get_default_timeout(THD *thd, SHOW_VAR *var, char *buff, return 0; } -static int show_ssl_get_verify_mode(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +static int show_ssl_get_verify_mode(THD *thd, SHOW_VAR *var, void *buff, + system_status_var *, enum_var_type) { var->type= SHOW_LONG; var->value= buff; @@ -7072,8 +7080,8 @@ static int show_ssl_get_verify_mode(THD *thd, SHOW_VAR *var, char *buff, return 0; } -static int show_ssl_get_verify_depth(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +static int show_ssl_get_verify_depth(THD *thd, SHOW_VAR *var, void *buff, + system_status_var *, enum_var_type) { var->type= SHOW_LONG; var->value= buff; @@ -7085,8 +7093,8 @@ static int show_ssl_get_verify_depth(THD *thd, SHOW_VAR *var, char *buff, return 0; } -static int show_ssl_get_cipher(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +static int show_ssl_get_cipher(THD *thd, SHOW_VAR *var, void *buff, + system_status_var *, enum_var_type) { var->type= SHOW_CHAR; if( thd->vio_ok() && thd->net.vio->ssl_arg ) @@ -7096,9 +7104,10 @@ static int show_ssl_get_cipher(THD *thd, SHOW_VAR *var, char *buff, return 0; } -static int show_ssl_get_cipher_list(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +static int show_ssl_get_cipher_list(THD *thd, SHOW_VAR *var, void *buf, + system_status_var *, enum_var_type) { + char *buff= static_cast<char*>(buf); var->type= SHOW_CHAR; var->value= buff; if (thd->vio_ok() && thd->net.vio->ssl_arg) @@ -7183,8 +7192,8 @@ end: */ static int -show_ssl_get_server_not_before(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +show_ssl_get_server_not_before(THD *thd, SHOW_VAR *var, void *buff, + system_status_var *, enum_var_type) { var->type= SHOW_CHAR; if(thd->vio_ok() && thd->net.vio->ssl_arg) @@ -7193,7 +7202,7 @@ show_ssl_get_server_not_before(THD *thd, SHOW_VAR *var, char *buff, X509 *cert= SSL_get_certificate(ssl); const ASN1_TIME *not_before= X509_get0_notBefore(cert); - var->value= my_asn1_time_to_string(not_before, buff, + var->value= my_asn1_time_to_string(not_before, static_cast<char*>(buff), SHOW_VAR_FUNC_BUFF_SIZE); if (!var->value) return 1; @@ -7217,8 +7226,8 @@ show_ssl_get_server_not_before(THD *thd, SHOW_VAR *var, char *buff, */ static int -show_ssl_get_server_not_after(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +show_ssl_get_server_not_after(THD *thd, SHOW_VAR *var, void *buff, + system_status_var *, enum_var_type) { var->type= SHOW_CHAR; if(thd->vio_ok() && thd->net.vio->ssl_arg) @@ -7227,7 +7236,7 @@ show_ssl_get_server_not_after(THD *thd, SHOW_VAR *var, char *buff, X509 *cert= SSL_get_certificate(ssl); const ASN1_TIME *not_after= X509_get0_notAfter(cert); - var->value= my_asn1_time_to_string(not_after, buff, + var->value= my_asn1_time_to_string(not_after, static_cast<char*>(buff), SHOW_VAR_FUNC_BUFF_SIZE); if (!var->value) return 1; @@ -7281,7 +7290,7 @@ static int show_default_keycache(THD *thd, SHOW_VAR *var, void *buff, } -static int show_memory_used(THD *thd, SHOW_VAR *var, char *buff, +static int show_memory_used(THD *thd, SHOW_VAR *var, void *buff, struct system_status_var *status_var, enum enum_var_type scope) { @@ -7337,8 +7346,8 @@ static int debug_status_func(THD *thd, SHOW_VAR *var, void *buff, #endif #ifdef HAVE_POOL_OF_THREADS -static int show_threadpool_idle_threads(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +static int show_threadpool_idle_threads(THD *, SHOW_VAR *var, void *buff, + system_status_var *, enum_var_type) { var->type= SHOW_INT; var->value= buff; @@ -7347,8 +7356,8 @@ static int show_threadpool_idle_threads(THD *thd, SHOW_VAR *var, char *buff, } -static int show_threadpool_threads(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +static int show_threadpool_threads(THD *, SHOW_VAR *var, void *buff, + system_status_var *, enum_var_type) { var->type= SHOW_INT; var->value= buff; @@ -7500,7 +7509,7 @@ SHOW_VAR status_vars[]= { SHOW_FUNC_ENTRY("Rpl_semi_sync_master_net_avg_wait_time", &SHOW_FNAME(avg_net_wait_time)), {"Rpl_semi_sync_master_request_ack", (char*) &rpl_semi_sync_master_request_ack, SHOW_LONGLONG}, {"Rpl_semi_sync_master_get_ack", (char*)&rpl_semi_sync_master_get_ack, SHOW_LONGLONG}, - {"Rpl_semi_sync_slave_status", (char*) &rpl_semi_sync_slave_status, SHOW_BOOL}, + SHOW_FUNC_ENTRY("Rpl_semi_sync_slave_status", &rpl_semi_sync_enabled), {"Rpl_semi_sync_slave_send_ack", (char*) &rpl_semi_sync_slave_send_ack, SHOW_LONGLONG}, #endif /* HAVE_REPLICATION */ #ifdef HAVE_QUERY_CACHE @@ -8082,6 +8091,9 @@ mysqld_get_one_option(const struct my_option *opt, const char *argument, test_flags= argument ? ((uint) atoi(argument) & ~TEST_BLOCKING) : 0; opt_endinfo=1; break; + case OPT_SECURE_AUTH: + WARN_DEPRECATED_NO_REPLACEMENT(NULL, "--secure-auth"); + break; case OPT_THREAD_CONCURRENCY: WARN_DEPRECATED_NO_REPLACEMENT(NULL, "THREAD_CONCURRENCY"); break; diff --git a/sql/mysqld.h b/sql/mysqld.h index 2139b9b6..13b824d1 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -842,7 +842,7 @@ enum options_mysqld OPT_WSREP_SYNC_WAIT, #endif /* WITH_WSREP */ OPT_MYSQL_COMPATIBILITY, - OPT_TLS_VERSION, + OPT_TLS_VERSION, OPT_SECURE_AUTH, OPT_MYSQL_TO_BE_IMPLEMENTED, OPT_SEQURE_FILE_PRIV, OPT_which_is_always_the_last @@ -884,6 +884,11 @@ enum enum_query_type /// good for parsing QT_PARSABLE= (1 << 8), + // If an expression is constant, print the expression, not the value + // it evaluates to. Should be used for error messages, so that they + // don't reveal values. + QT_NO_DATA_EXPANSION= (1 << 9), + /// This value means focus on readability, not on ability to parse back, etc. QT_EXPLAIN= QT_TO_SYSTEM_CHARSET | QT_ITEM_IDENT_SKIP_DB_NAMES | @@ -904,12 +909,12 @@ enum enum_query_type QT_EXPLAIN_EXTENDED= QT_TO_SYSTEM_CHARSET| QT_SHOW_SELECT_NUMBER, - // If an expression is constant, print the expression, not the value - // it evaluates to. Should be used for error messages, so that they - // don't reveal values. - QT_NO_DATA_EXPANSION= (1 << 9), // Remove wrappers added for TVC when creating or showing view - QT_NO_WRAPPERS_FOR_TVC_IN_VIEW= (1 << 12) + QT_NO_WRAPPERS_FOR_TVC_IN_VIEW= (1 << 12), + + /// Print for FRM file. Focus on parse-back. + /// e.g. VIEW expressions and virtual column expressions + QT_FOR_FRM= (1 << 13) }; diff --git a/sql/net_serv.cc b/sql/net_serv.cc index 70e71d9a..3dff8442 100644 --- a/sql/net_serv.cc +++ b/sql/net_serv.cc @@ -156,6 +156,7 @@ my_bool my_net_init(NET *net, Vio *vio, void *thd, uint my_flags) net->where_b = net->remain_in_buf=0; net->net_skip_rest_factor= 0; net->last_errno=0; + net->pkt_nr_can_be_reset= 0; net->thread_specific_malloc= MY_TEST(my_flags & MY_THREAD_SPECIFIC); net->thd= 0; #ifdef MYSQL_SERVER @@ -1057,8 +1058,10 @@ retry: { /* Probably in MIT threads */ if (retry_count++ < net->retry_count) continue; - EXTRA_DEBUG_fprintf(stderr, "%s: read looped with error %d, aborting thread\n", - my_progname,vio_errno(net->vio)); + EXTRA_DEBUG_fprintf(stderr, "%s: read looped with error %d on " + "file %lld, aborting thread\n", + my_progname, vio_errno(net->vio), + (longlong) vio_fd(net->vio)); } #ifndef MYSQL_SERVER if (length != 0 && vio_errno(net->vio) == SOCKET_EINTR) @@ -1094,19 +1097,31 @@ retry: #endif if (net->buff[net->where_b + 3] != (uchar) net->pkt_nr) { -#ifndef MYSQL_SERVER - if (net->buff[net->where_b + 3] == (uchar) (net->pkt_nr -1)) + if (net->pkt_nr_can_be_reset) { /* - If the server was killed then the server may have missed the - last sent client packet and the packet numbering may be one off. + We are using a protocol like semi-sync where master and slave + sends packets in parallel. + Copy current one as it can be useful for debugging. */ - DBUG_PRINT("warning", ("Found possible out of order packets")); - expect_error_packet= 1; + net->pkt_nr= net->buff[net->where_b + 3]; } else + { +#ifndef MYSQL_SERVER + if (net->buff[net->where_b + 3] == (uchar) (net->pkt_nr -1)) + { + /* + If the server was killed then the server may have missed the + last sent client packet and the packet numbering may be one off. + */ + DBUG_PRINT("warning", ("Found possible out of order packets")); + expect_error_packet= 1; + } + else #endif - goto packets_out_of_order; + goto packets_out_of_order; + } } net->compress_pkt_nr= ++net->pkt_nr; #ifdef HAVE_COMPRESS diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 0ca8402f..42afc930 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -2692,7 +2692,7 @@ SQL_SELECT::test_quick_select(THD *thd, bool ordered_output, bool remove_false_parts_of_where, bool only_single_index_range_scan, - bool suppress_unusable_key_notes) + Item_func::Bitmap note_unusable_keys) { uint idx; double scan_time; @@ -2786,9 +2786,9 @@ SQL_SELECT::test_quick_select(THD *thd, param.max_key_parts= 0; param.remove_false_where_parts= remove_false_parts_of_where; param.force_default_mrr= ordered_output; - param.note_unusable_keys= (!suppress_unusable_key_notes && - thd->give_notes_for_unusable_keys()); - + param.note_unusable_keys= thd->give_notes_for_unusable_keys() ? + note_unusable_keys : + Item_func::BITMAP_NONE; param.possible_keys.clear_all(); thd->no_errors=1; // Don't warn about NULL @@ -3999,7 +3999,7 @@ bool prune_partitions(THD *thd, TABLE *table, Item *pprune_cond) range_par->remove_jump_scans= FALSE; range_par->real_keynr[0]= 0; range_par->alloced_sel_args= 0; - range_par->note_unusable_keys= 0; + range_par->note_unusable_keys= Item_func::BITMAP_NONE; thd->no_errors=1; // Don't warn about NULL thd->mem_root=&alloc; @@ -8762,9 +8762,11 @@ Item_func_like::get_mm_leaf(RANGE_OPT_PARAM *param, if (field->result_type() == STRING_RESULT && field->charset() != compare_collation()) { - if (param->note_unusable_keys) + if (param->note_unusable_keys & BITMAP_LIKE) field->raise_note_cannot_use_key_part(param->thd, keynr, key_part->part, - func_name_cstring(), value, + func_name_cstring(), + compare_collation(), + value, Data_type_compatibility:: INCOMPATIBLE_COLLATION); DBUG_RETURN(0); @@ -8780,9 +8782,11 @@ Item_func_like::get_mm_leaf(RANGE_OPT_PARAM *param, field->type_handler() == &type_handler_enum || field->type_handler() == &type_handler_set) { - if (param->note_unusable_keys) + if (param->note_unusable_keys & BITMAP_LIKE) field->raise_note_cannot_use_key_part(param->thd, keynr, key_part->part, - func_name_cstring(), value, + func_name_cstring(), + compare_collation(), + value, Data_type_compatibility:: INCOMPATIBLE_DATA_TYPE); DBUG_RETURN(0); @@ -8887,7 +8891,8 @@ Field::can_optimize_scalar_range(const RANGE_OPT_PARAM *param, TODO: Perhaps we also need to raise a similar note when a partition could not be used (when using_real_indexes==false). */ - if (param->using_real_indexes && param->note_unusable_keys) + if (param->using_real_indexes && param->note_unusable_keys && + (param->note_unusable_keys & cond->bitmap_bit())) { DBUG_ASSERT(keynr < table->s->keys); /* @@ -8901,6 +8906,7 @@ Field::can_optimize_scalar_range(const RANGE_OPT_PARAM *param, */ raise_note_cannot_use_key_part(param->thd, keynr, key_part->part, scalar_comparison_op_to_lex_cstring(op), + cond->compare_collation(), value, compat); } return compat; @@ -15173,13 +15179,6 @@ int QUICK_GROUP_MIN_MAX_SELECT::init() { if (group_prefix) /* Already initialized. */ return 0; - - /* - We allocate one byte more to serve the case when the last field in - the buffer is compared using uint3korr (e.g. a Field_newdate field) - */ - if (!(last_prefix= (uchar*) alloc_root(&alloc, group_prefix_len+1))) - return 1; /* We may use group_prefix to store keys with all select fields, so allocate enough space for it. @@ -15436,8 +15435,7 @@ void QUICK_GROUP_MIN_MAX_SELECT::update_key_stat() QUICK_GROUP_MIN_MAX_SELECT::reset() DESCRIPTION - Initialize the index chosen for access and find and store the prefix - of the last group. The method is expensive since it performs disk access. + Initialize the index chosen for access. RETURN 0 OK @@ -15459,12 +15457,6 @@ int QUICK_GROUP_MIN_MAX_SELECT::reset(void) } if (quick_prefix_select && quick_prefix_select->reset()) DBUG_RETURN(1); - result= file->ha_index_last(record); - if (result == HA_ERR_END_OF_FILE) - DBUG_RETURN(0); - /* Save the prefix of the last group. */ - key_copy(last_prefix, record, index_info, group_prefix_len); - DBUG_RETURN(0); } @@ -15510,34 +15502,20 @@ int QUICK_GROUP_MIN_MAX_SELECT::get_next() #else int result; #endif - int is_last_prefix= 0; - DBUG_ENTER("QUICK_GROUP_MIN_MAX_SELECT::get_next"); /* - Loop until a group is found that satisfies all query conditions or the last - group is reached. + Loop until a group is found that satisfies all query conditions or + there are no satisfying groups left */ do { result= next_prefix(); + if (result != 0) + break; /* - Check if this is the last group prefix. Notice that at this point - this->record contains the current prefix in record format. + At this point this->record contains the current prefix in record format. */ - if (!result) - { - is_last_prefix= key_cmp(index_info->key_part, last_prefix, - group_prefix_len); - DBUG_ASSERT(is_last_prefix <= 0); - } - else - { - if (result == HA_ERR_KEY_NOT_FOUND) - continue; - break; - } - if (have_min) { min_res= next_min(); @@ -15566,8 +15544,7 @@ int QUICK_GROUP_MIN_MAX_SELECT::get_next() HA_READ_KEY_EXACT); result= have_min ? min_res : have_max ? max_res : result; - } while ((result == HA_ERR_KEY_NOT_FOUND || result == HA_ERR_END_OF_FILE) && - is_last_prefix != 0); + } while (result == HA_ERR_KEY_NOT_FOUND || result == HA_ERR_END_OF_FILE); if (result == HA_ERR_KEY_NOT_FOUND) result= HA_ERR_END_OF_FILE; diff --git a/sql/opt_range.h b/sql/opt_range.h index 4f766534..25bb9f0a 100644 --- a/sql/opt_range.h +++ b/sql/opt_range.h @@ -889,7 +889,10 @@ public: */ bool remove_false_where_parts; - bool note_unusable_keys; // Give SQL notes for unusable keys + /* + Which functions should give SQL notes for unusable keys. + */ + Item_func::Bitmap note_unusable_keys; /* used_key_no -> table_key_no translation table. Only makes sense if @@ -1768,7 +1771,6 @@ private: uchar *group_prefix; /* Key prefix consisting of the GROUP fields. */ const uint group_prefix_len; /* Length of the group prefix. */ uint group_key_parts; /* A number of keyparts in the group prefix */ - uchar *last_prefix; /* Prefix of the last group for detecting EOF. */ bool have_min; /* Specify whether we are computing */ bool have_max; /* a MIN, a MAX, or both. */ bool have_agg_distinct;/* aggregate_function(DISTINCT ...). */ @@ -1894,12 +1896,14 @@ class SQL_SELECT :public Sql_alloc { true - for ERROR and IMPOSSIBLE_RANGE false - Ok */ - bool check_quick(THD *thd, bool force_quick_range, ha_rows limit) + bool check_quick(THD *thd, bool force_quick_range, ha_rows limit, + Item_func::Bitmap note_unusable_keys) { key_map tmp; tmp.set_all(); return test_quick_select(thd, tmp, 0, limit, force_quick_range, - FALSE, FALSE, FALSE) != OK; + FALSE, FALSE, FALSE, + note_unusable_keys) != OK; } /* @@ -1929,7 +1933,7 @@ class SQL_SELECT :public Sql_alloc { bool ordered_output, bool remove_false_parts_of_where, bool only_single_index_range_scan, - bool suppress_unusable_key_notes = 0); + Item_func::Bitmap note_unusable_keys); }; typedef enum SQL_SELECT::quick_select_return_type quick_select_return; diff --git a/sql/opt_trace.cc b/sql/opt_trace.cc index 4bc49394..972c7da6 100644 --- a/sql/opt_trace.cc +++ b/sql/opt_trace.cc @@ -103,7 +103,8 @@ inline bool sql_command_can_be_traced(enum enum_sql_command sql_command) sql_command == SQLCOM_UPDATE || sql_command == SQLCOM_DELETE || sql_command == SQLCOM_DELETE_MULTI || - sql_command == SQLCOM_UPDATE_MULTI; + sql_command == SQLCOM_UPDATE_MULTI || + sql_command == SQLCOM_INSERT_SELECT; } void opt_trace_print_expanded_query(THD *thd, SELECT_LEX *select_lex, diff --git a/sql/protocol.cc b/sql/protocol.cc index 6667129d..d2ef52e0 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -414,7 +414,6 @@ static bool write_eof_packet(THD *thd, NET *net, bool Protocol::net_send_error_packet(THD *thd, uint sql_errno, const char *err, const char* sqlstate) - { NET *net= &thd->net; uint length; diff --git a/sql/protocol.h b/sql/protocol.h index 4fdfde3e..09dbdfbd 100644 --- a/sql/protocol.h +++ b/sql/protocol.h @@ -229,9 +229,9 @@ public: #ifdef EMBEDDED_LIBRARY void remove_last_row() override; #endif - virtual bool store_field_metadata(const THD *thd, const Send_field &field, - CHARSET_INFO *charset_for_protocol, - uint pos); + bool store_field_metadata(const THD *thd, const Send_field &field, + CHARSET_INFO *charset_for_protocol, + uint pos); bool store_item_metadata(THD *thd, Item *item, uint pos); bool store_field_metadata_for_list_fields(const THD *thd, Field *field, const TABLE_LIST *table_list, diff --git a/sql/rpl_gtid.cc b/sql/rpl_gtid.cc index 11629059..20188d6c 100644 --- a/sql/rpl_gtid.cc +++ b/sql/rpl_gtid.cc @@ -721,6 +721,7 @@ rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id, if (WSREP_ON_ && wsrep_thd_is_local(thd)) { thd->wsrep_ignore_table= false; + table->file->row_logging= 1; // replication requires binary logging wsrep_start_trx_if_not_started(thd); } else diff --git a/sql/rpl_mi.cc b/sql/rpl_mi.cc index 3c698f27..4fc204dd 100644 --- a/sql/rpl_mi.cc +++ b/sql/rpl_mi.cc @@ -43,7 +43,8 @@ Master_info::Master_info(LEX_CSTRING *connection_name_arg, gtid_reconnect_event_skip_count(0), gtid_event_seen(false), in_start_all_slaves(0), in_stop_all_slaves(0), in_flush_all_relay_logs(0), users(0), killed(0), - total_ddl_groups(0), total_non_trans_groups(0), total_trans_groups(0) + total_ddl_groups(0), total_non_trans_groups(0), total_trans_groups(0), + semi_sync_reply_enabled(0) { char *tmp; host[0] = 0; user[0] = 0; password[0] = 0; diff --git a/sql/rpl_mi.h b/sql/rpl_mi.h index 6058b7fb..159e099f 100644 --- a/sql/rpl_mi.h +++ b/sql/rpl_mi.h @@ -210,6 +210,16 @@ class Master_info : public Slave_reporting_capability void lock_slave_threads(); void unlock_slave_threads(); + ulonglong get_slave_skip_counter() + { + return rli.slave_skip_counter; + } + + ulonglong get_max_relay_log_size() + { + return rli.max_relay_log_size; + } + /* the variables below are needed because we can change masters on the fly */ char master_log_name[FN_REFLEN+6]; /* Room for multi-*/ char host[HOSTNAME_LENGTH*SYSTEM_CHARSET_MBMAXLEN+1]; @@ -366,6 +376,12 @@ class Master_info : public Slave_reporting_capability it must be ignored similarly to the replicate-same-server-id rule. */ bool do_accept_own_server_id= false; + /* + Set to 1 when semi_sync is enabled. Set to 0 if there is any transmit + problems to the slave, in which case any furter semi-sync reply is + ignored + */ + bool semi_sync_reply_enabled; List <start_alter_info> start_alter_list; MEM_ROOT mem_root; /* diff --git a/sql/rpl_parallel.cc b/sql/rpl_parallel.cc index 95a1234e..333a3960 100644 --- a/sql/rpl_parallel.cc +++ b/sql/rpl_parallel.cc @@ -895,8 +895,7 @@ do_retry: thd->wait_for_commit_ptr->unregister_wait_for_prior_commit(); DBUG_EXECUTE_IF("inject_mdev8031", { /* Simulate that we get deadlock killed at this exact point. */ - rgi->killed_for_retry= rpl_group_info::RETRY_KILL_KILLED; - thd->set_killed(KILL_CONNECTION); + slave_background_kill_request(thd); }); #ifdef ENABLED_DEBUG_SYNC DBUG_EXECUTE_IF("rpl_parallel_simulate_wait_at_retry", { @@ -2877,23 +2876,12 @@ rpl_parallel::stop_during_until() bool -rpl_parallel::workers_idle() +rpl_parallel::workers_idle(Relay_log_info *rli) { - struct rpl_parallel_entry *e; - uint32 i, max_i; - - max_i= domain_hash.records; - for (i= 0; i < max_i; ++i) - { - bool active; - e= (struct rpl_parallel_entry *)my_hash_element(&domain_hash, i); - mysql_mutex_lock(&e->LOCK_parallel_entry); - active= e->current_sub_id > e->last_committed_sub_id; - mysql_mutex_unlock(&e->LOCK_parallel_entry); - if (active) - break; - } - return (i == max_i); + mysql_mutex_assert_owner(&rli->data_lock); + return !rli->last_inuse_relaylog || + rli->last_inuse_relaylog->queued_count == + rli->last_inuse_relaylog->dequeued_count; } diff --git a/sql/rpl_parallel.h b/sql/rpl_parallel.h index a9cfefcb..307d0e3b 100644 --- a/sql/rpl_parallel.h +++ b/sql/rpl_parallel.h @@ -460,9 +460,10 @@ struct rpl_parallel { rpl_parallel_entry *find(uint32 domain_id, Relay_log_info *rli); void wait_for_done(THD *thd, Relay_log_info *rli); void stop_during_until(); - bool workers_idle(); int wait_for_workers_idle(THD *thd); int do_event(rpl_group_info *serial_rgi, Log_event *ev, ulonglong event_size); + + static bool workers_idle(Relay_log_info *rli); }; diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h index 0fd90704..30e1bb45 100644 --- a/sql/rpl_rli.h +++ b/sql/rpl_rli.h @@ -623,7 +623,7 @@ struct inuse_relaylog { rpl_gtid *relay_log_state; uint32 relay_log_state_count; /* Number of events in this relay log queued for worker threads. */ - int64 queued_count; + Atomic_counter<int64> queued_count; /* Number of events completed by worker threads. */ Atomic_counter<int64> dequeued_count; /* Set when all events have been read from a relaylog. */ diff --git a/sql/semisync_master.cc b/sql/semisync_master.cc index 670a6d8d..9f30a820 100644 --- a/sql/semisync_master.cc +++ b/sql/semisync_master.cc @@ -91,7 +91,9 @@ Active_tranx::Active_tranx(mysql_mutex_t *lock, for (int idx = 0; idx < m_num_entries; ++idx) m_trx_htb[idx] = NULL; +#ifdef EXTRA_DEBUG sql_print_information("Semi-sync replication initialized for transactions."); +#endif } Active_tranx::~Active_tranx() @@ -352,8 +354,7 @@ Repl_semi_sync_master::Repl_semi_sync_master() m_state(0), m_wait_point(0) { - strcpy(m_reply_file_name, ""); - strcpy(m_wait_file_name, ""); + m_reply_file_name[0]= m_wait_file_name[0]= 0; } int Repl_semi_sync_master::init_object() @@ -379,20 +380,10 @@ int Repl_semi_sync_master::init_object() { result = enable_master(); if (!result) - { result= ack_receiver.start(); /* Start the ACK thread. */ - /* - If rpl_semi_sync_master_wait_no_slave is disabled, let's temporarily - switch off semisync to avoid hang if there's none active slave. - */ - if (!rpl_semi_sync_master_wait_no_slave) - switch_off(); - } } else - { disable_master(); - } return result; } @@ -441,7 +432,7 @@ void Repl_semi_sync_master::disable_master() */ switch_off(); - assert(m_active_tranxs != NULL); + DBUG_ASSERT(m_active_tranxs != NULL); delete m_active_tranxs; m_active_tranxs = NULL; @@ -450,7 +441,6 @@ void Repl_semi_sync_master::disable_master() m_commit_file_name_inited = false; set_master_enabled(false); - sql_print_information("Semi-sync replication disabled on the master."); } unlock(); @@ -537,31 +527,34 @@ void Repl_semi_sync_master::add_slave() void Repl_semi_sync_master::remove_slave() { lock(); - rpl_semi_sync_master_clients--; - - /* Only switch off if semi-sync is enabled and is on */ - if (get_master_enabled() && is_on()) + if (!(--rpl_semi_sync_master_clients) && !rpl_semi_sync_master_wait_no_slave) { - /* If user has chosen not to wait if no semi-sync slave available - and the last semi-sync slave exits, turn off semi-sync on master - immediately. - */ - if (!rpl_semi_sync_master_wait_no_slave && - rpl_semi_sync_master_clients == 0) - switch_off(); + /* + Signal transactions waiting in commit_trx() that they do not have to + wait anymore. + */ + cond_broadcast(); } unlock(); } + +/* + Check report package + + @retval 0 ok + @retval 1 Error + @retval -1 Slave is going down (ok) +*/ + int Repl_semi_sync_master::report_reply_packet(uint32 server_id, const uchar *packet, ulong packet_len) { - int result= -1; + int result= 1; // Assume error char log_file_name[FN_REFLEN+1]; my_off_t log_file_pos; ulong log_file_len = 0; - DBUG_ENTER("Repl_semi_sync_master::report_reply_packet"); DBUG_EXECUTE_IF("semisync_corrupt_magic", @@ -569,7 +562,14 @@ int Repl_semi_sync_master::report_reply_packet(uint32 server_id, if (unlikely(packet[REPLY_MAGIC_NUM_OFFSET] != Repl_semi_sync_master::k_packet_magic_num)) { - sql_print_error("Read semi-sync reply magic number error"); + if (packet[0] == COM_QUIT && packet_len == 1) + { + /* Slave sent COM_QUIT as part of IO thread going down */ + sql_print_information("slave IO thread has stopped"); + DBUG_RETURN(-1); + } + else + sql_print_error("Read semi-sync reply magic number error"); goto l_end; } @@ -597,14 +597,13 @@ int Repl_semi_sync_master::report_reply_packet(uint32 server_id, rpl_semi_sync_master_get_ack++; report_reply_binlog(server_id, log_file_name, log_file_pos); - result= 0; + DBUG_RETURN(0); l_end: - if (result == -1) { char buf[256]; - octet2hex(buf, (const char*) packet, std::min(static_cast<ulong>(sizeof(buf)-1), - packet_len)); + octet2hex(buf, (const char*) packet, + MY_MIN(sizeof(buf)-1, (size_t) packet_len)); sql_print_information("First bytes of the packet from semisync slave " "server-id %d: %s", server_id, buf); @@ -668,7 +667,7 @@ int Repl_semi_sync_master::report_reply_binlog(uint32 server_id, m_reply_file_name_inited = true; /* Remove all active transaction nodes before this point. */ - assert(m_active_tranxs != NULL); + DBUG_ASSERT(m_active_tranxs != NULL); m_active_tranxs->clear_active_tranx_nodes(log_file_name, log_file_pos); DBUG_PRINT("semisync", ("%s: Got reply at (%s, %lu)", @@ -809,6 +808,8 @@ int Repl_semi_sync_master::dump_start(THD* thd, (long) thd->variables.server_id, log_file, (ulong) log_pos); + /* Mark that semi-sync net->pkt_nr is not reliable */ + thd->net.pkt_nr_can_be_reset= 1; return 0; } @@ -827,8 +828,15 @@ void Repl_semi_sync_master::dump_end(THD* thd) int Repl_semi_sync_master::commit_trx(const char* trx_wait_binlog_name, my_off_t trx_wait_binlog_pos) { + bool success= 0; DBUG_ENTER("Repl_semi_sync_master::commit_trx"); + if (!rpl_semi_sync_master_clients && !rpl_semi_sync_master_wait_no_slave) + { + rpl_semi_sync_master_no_transactions++; + DBUG_RETURN(0); + } + if (get_master_enabled() && trx_wait_binlog_name) { struct timespec start_ts; @@ -836,7 +844,7 @@ int Repl_semi_sync_master::commit_trx(const char* trx_wait_binlog_name, int wait_result; PSI_stage_info old_stage; THD *thd= current_thd; - + bool aborted= 0; set_timespec(start_ts, 0); DEBUG_SYNC(thd, "rpl_semisync_master_commit_trx_before_lock"); @@ -859,6 +867,13 @@ int Repl_semi_sync_master::commit_trx(const char* trx_wait_binlog_name, while (is_on() && !thd_killed(thd)) { + /* We have to check these again as things may have changed */ + if (!rpl_semi_sync_master_clients && !rpl_semi_sync_master_wait_no_slave) + { + aborted= 1; + break; + } + if (m_reply_file_name_inited) { int cmp = Active_tranx::compare(m_reply_file_name, m_reply_file_pos, @@ -873,6 +888,7 @@ int Repl_semi_sync_master::commit_trx(const char* trx_wait_binlog_name, "Repl_semi_sync_master::commit_trx", m_reply_file_name, (ulong)m_reply_file_pos)); + success= 1; break; } } @@ -973,13 +989,13 @@ int Repl_semi_sync_master::commit_trx(const char* trx_wait_binlog_name, m_active_tranxs may be NULL if someone disabled semi sync during cond_timewait() */ - assert(thd_killed(thd) || !m_active_tranxs || - !m_active_tranxs->is_tranx_end_pos(trx_wait_binlog_name, - trx_wait_binlog_pos)); + DBUG_ASSERT(thd_killed(thd) || !m_active_tranxs || aborted || + !m_active_tranxs->is_tranx_end_pos(trx_wait_binlog_name, + trx_wait_binlog_pos)); l_end: /* Update the status counter. */ - if (is_on()) + if (success) rpl_semi_sync_master_yes_transactions++; else rpl_semi_sync_master_no_transactions++; @@ -1014,18 +1030,20 @@ void Repl_semi_sync_master::switch_off() { DBUG_ENTER("Repl_semi_sync_master::switch_off"); - m_state = false; - - /* Clear the active transaction list. */ - assert(m_active_tranxs != NULL); - m_active_tranxs->clear_active_tranx_nodes(NULL, 0); + if (m_state) + { + m_state = false; - rpl_semi_sync_master_off_times++; - m_wait_file_name_inited = false; - m_reply_file_name_inited = false; - sql_print_information("Semi-sync replication switched OFF."); - cond_broadcast(); /* wake up all waiting threads */ + /* Clear the active transaction list. */ + DBUG_ASSERT(m_active_tranxs != NULL); + m_active_tranxs->clear_active_tranx_nodes(NULL, 0); + rpl_semi_sync_master_off_times++; + m_wait_file_name_inited = false; + m_reply_file_name_inited = false; + sql_print_information("Semi-sync replication switched OFF."); + } + cond_broadcast(); /* wake up all waiting threads */ DBUG_VOID_RETURN; } @@ -1072,9 +1090,10 @@ int Repl_semi_sync_master::reserve_sync_header(String* packet) { DBUG_ENTER("Repl_semi_sync_master::reserve_sync_header"); - /* Set the magic number and the sync status. By default, no sync - * is required. - */ + /* + Set the magic number and the sync status. By default, no sync + is required. + */ packet->append(reinterpret_cast<const char*>(k_sync_header), sizeof(k_sync_header)); DBUG_RETURN(0); @@ -1087,7 +1106,6 @@ int Repl_semi_sync_master::update_sync_header(THD* thd, unsigned char *packet, { int cmp = 0; bool sync = false; - DBUG_ENTER("Repl_semi_sync_master::update_sync_header"); /* If the semi-sync master is not enabled, or the slave is not a semi-sync @@ -1103,16 +1121,11 @@ int Repl_semi_sync_master::update_sync_header(THD* thd, unsigned char *packet, /* This is the real check inside the mutex. */ if (!get_master_enabled()) - { - assert(sync == false); goto l_end; - } if (is_on()) { /* semi-sync is ON */ - sync = false; /* No sync unless a transaction is involved. */ - if (m_reply_file_name_inited) { cmp = Active_tranx::compare(log_file_name, log_file_pos, @@ -1126,15 +1139,10 @@ int Repl_semi_sync_master::update_sync_header(THD* thd, unsigned char *packet, } } + cmp = 1; if (m_wait_file_name_inited) - { cmp = Active_tranx::compare(log_file_name, log_file_pos, m_wait_file_name, m_wait_file_pos); - } - else - { - cmp = 1; - } /* If we are already waiting for some transaction replies which * are later in binlog, do not wait for this one event. @@ -1144,7 +1152,7 @@ int Repl_semi_sync_master::update_sync_header(THD* thd, unsigned char *packet, /* * We only wait if the event is a transaction's ending event. */ - assert(m_active_tranxs != NULL); + DBUG_ASSERT(m_active_tranxs != NULL); sync = m_active_tranxs->is_tranx_end_pos(log_file_name, log_file_pos); } @@ -1172,13 +1180,12 @@ int Repl_semi_sync_master::update_sync_header(THD* thd, unsigned char *packet, l_end: unlock(); - /* We do not need to clear sync flag because we set it to 0 when we - * reserve the packet header. - */ + /* + We do not need to clear sync flag in packet because we set it to 0 when we + reserve the packet header. + */ if (sync) - { - (packet)[2] = k_packet_flag_sync; - } + packet[2]= k_packet_flag_sync; DBUG_RETURN(0); } @@ -1225,7 +1232,7 @@ int Repl_semi_sync_master::write_tranx_in_binlog(const char* log_file_name, if (is_on()) { - assert(m_active_tranxs != NULL); + DBUG_ASSERT(m_active_tranxs != NULL); if(m_active_tranxs->insert_tranx_node(log_file_name, log_file_pos)) { /* @@ -1256,7 +1263,7 @@ int Repl_semi_sync_master::flush_net(THD *thd, DBUG_ENTER("Repl_semi_sync_master::flush_net"); - assert((unsigned char)event_buf[1] == k_packet_magic_num); + DBUG_ASSERT((unsigned char)event_buf[1] == k_packet_magic_num); if ((unsigned char)event_buf[2] != k_packet_flag_sync) { /* current event does not require reply */ @@ -1274,6 +1281,11 @@ int Repl_semi_sync_master::flush_net(THD *thd, goto l_end; } + /* + We have to do a net_clear() as with semi-sync the slave_reply's are + interleaved with data from the master and then the net->pkt_nr + cannot be kept in sync. Better to start pkt_nr from 0 again. + */ net_clear(net, 0); net->pkt_nr++; net->compress_pkt_nr++; @@ -1300,11 +1312,7 @@ int Repl_semi_sync_master::after_reset_master() lock(); - if (rpl_semi_sync_master_clients == 0 && - !rpl_semi_sync_master_wait_no_slave) - m_state = 0; - else - m_state = get_master_enabled()? 1 : 0; + m_state = get_master_enabled() ? 1 : 0; m_wait_file_name_inited = false; m_reply_file_name_inited = false; @@ -1338,18 +1346,6 @@ int Repl_semi_sync_master::before_reset_master() DBUG_RETURN(result); } -void Repl_semi_sync_master::check_and_switch() -{ - lock(); - if (get_master_enabled() && is_on()) - { - if (!rpl_semi_sync_master_wait_no_slave - && rpl_semi_sync_master_clients == 0) - switch_off(); - } - unlock(); -} - void Repl_semi_sync_master::set_export_stats() { lock(); @@ -1363,7 +1359,6 @@ void Repl_semi_sync_master::set_export_stats() ((rpl_semi_sync_master_net_wait_num) ? (ulong)((double)rpl_semi_sync_master_net_wait_time / ((double)rpl_semi_sync_master_net_wait_num)) : 0); - unlock(); } diff --git a/sql/semisync_master.h b/sql/semisync_master.h index 5451ad51..99f46869 100644 --- a/sql/semisync_master.h +++ b/sql/semisync_master.h @@ -633,8 +633,6 @@ class Repl_semi_sync_master /*called before reset master*/ int before_reset_master(); - void check_and_switch(); - /* Determines if the given thread is currently awaiting a semisync_ack. Note that the thread's value is protected by this class's LOCK_binlog, so this diff --git a/sql/semisync_master_ack_receiver.cc b/sql/semisync_master_ack_receiver.cc index 559f939c..a311599c 100644 --- a/sql/semisync_master_ack_receiver.cc +++ b/sql/semisync_master_ack_receiver.cc @@ -24,7 +24,8 @@ extern PSI_cond_key key_COND_ack_receiver; #ifdef HAVE_PSI_THREAD_INTERFACE extern PSI_thread_key key_thread_ack_receiver; #endif -extern Repl_semi_sync_master repl_semisync; + +my_socket global_ack_signal_fd= -1; /* Callback function of ack receive thread */ pthread_handler_t ack_receive_handler(void *arg) @@ -45,6 +46,7 @@ Ack_receiver::Ack_receiver() m_status= ST_DOWN; mysql_mutex_init(key_LOCK_ack_receiver, &m_mutex, NULL); mysql_cond_init(key_COND_ack_receiver, &m_cond, NULL); + mysql_cond_init(key_COND_ack_receiver, &m_cond_reply, NULL); m_pid= 0; DBUG_VOID_RETURN; @@ -57,6 +59,7 @@ void Ack_receiver::cleanup() stop(); mysql_mutex_destroy(&m_mutex); mysql_cond_destroy(&m_cond); + mysql_cond_destroy(&m_cond_reply); DBUG_VOID_RETURN; } @@ -104,6 +107,7 @@ void Ack_receiver::stop() if (m_status == ST_UP) { m_status= ST_STOPPING; + signal_listener(); // Signal listener thread to stop mysql_cond_broadcast(&m_cond); while (m_status == ST_STOPPING) @@ -118,6 +122,21 @@ void Ack_receiver::stop() DBUG_VOID_RETURN; } +#ifndef DBUG_OFF +void static dbug_verify_no_duplicate_slaves(Slave_ilist *m_slaves, THD *thd) +{ + I_List_iterator<Slave> it(*m_slaves); + Slave *slave; + while ((slave= it++)) + { + DBUG_ASSERT(slave->thd->variables.server_id != thd->variables.server_id); + } +} +#else +#define dbug_verify_no_duplicate_slaves(A,B) do {} while(0) +#endif + + bool Ack_receiver::add_slave(THD *thd) { Slave *slave; @@ -126,17 +145,23 @@ bool Ack_receiver::add_slave(THD *thd) if (!(slave= new Slave)) DBUG_RETURN(true); + slave->active= 0; slave->thd= thd; slave->vio= *thd->net.vio; slave->vio.mysql_socket.m_psi= NULL; slave->vio.read_timeout= 1; mysql_mutex_lock(&m_mutex); + + dbug_verify_no_duplicate_slaves(&m_slaves, thd); + m_slaves.push_back(slave); m_slaves_changed= true; mysql_cond_broadcast(&m_cond); mysql_mutex_unlock(&m_mutex); + signal_listener(); // Inform listener that there are new slaves + DBUG_RETURN(false); } @@ -144,6 +169,7 @@ void Ack_receiver::remove_slave(THD *thd) { I_List_iterator<Slave> it(m_slaves); Slave *slave; + bool slaves_changed= 0; DBUG_ENTER("Ack_receiver::remove_slave"); mysql_mutex_lock(&m_mutex); @@ -153,10 +179,23 @@ void Ack_receiver::remove_slave(THD *thd) if (slave->thd == thd) { delete slave; - m_slaves_changed= true; + slaves_changed= true; break; } } + if (slaves_changed) + { + m_slaves_changed= true; + mysql_cond_broadcast(&m_cond); + /* + Wait until Ack_receiver::run() acknowledges remove of slave + As this is only sent under the mutex and after listners has + been collected, we know that listener has ignored the found + slave. + */ + if (m_status != ST_DOWN) + mysql_cond_wait(&m_cond_reply, &m_mutex); + } mysql_mutex_unlock(&m_mutex); DBUG_VOID_RETURN; @@ -167,10 +206,15 @@ inline void Ack_receiver::set_stage_info(const PSI_stage_info &stage) (void)MYSQL_SET_STAGE(stage.m_key, __FILE__, __LINE__); } -inline void Ack_receiver::wait_for_slave_connection() +void Ack_receiver::wait_for_slave_connection(THD *thd) { - set_stage_info(stage_waiting_for_semi_sync_slave); - mysql_cond_wait(&m_cond, &m_mutex); + thd->enter_cond(&m_cond, &m_mutex, &stage_waiting_for_semi_sync_slave, + 0, __func__, __FILE__, __LINE__); + + while (m_status == ST_UP && m_slaves.is_empty()) + mysql_cond_wait(&m_cond, &m_mutex); + + thd->exit_cond(0, __func__, __FILE__, __LINE__); } /* Auxilary function to initialize a NET object with given net buffer. */ @@ -188,17 +232,23 @@ void Ack_receiver::run() THD *thd= new THD(next_thread_id()); NET net; unsigned char net_buff[REPLY_MESSAGE_MAX_LENGTH]; + DBUG_ENTER("Ack_receiver::run"); my_thread_init(); - DBUG_ENTER("Ack_receiver::run"); - #ifdef HAVE_POLL Poll_socket_listener listener(m_slaves); #else Select_socket_listener listener(m_slaves); #endif //HAVE_POLL + if (listener.got_error()) + { + sql_print_error("Got error %M starting ack receiver thread", + listener.got_error()); + return; + } + sql_print_information("Starting ack receiver thread"); thd->system_thread= SYSTEM_THREAD_SEMISYNC_MASTER_BACKGROUND; thd->thread_stack= (char*) &thd; @@ -207,64 +257,79 @@ void Ack_receiver::run() thd->set_command(COM_DAEMON); init_net(&net, net_buff, REPLY_MESSAGE_MAX_LENGTH); - mysql_mutex_lock(&m_mutex); + /* + Mark that we have to setup the listener. Note that only this functions can + set m_slaves_changed to false + */ m_slaves_changed= true; - mysql_mutex_unlock(&m_mutex); while (1) { - int ret; - uint slave_count __attribute__((unused))= 0; + int ret, slave_count= 0; Slave *slave; mysql_mutex_lock(&m_mutex); - if (unlikely(m_status == ST_STOPPING)) + if (unlikely(m_status != ST_UP)) goto end; - set_stage_info(stage_waiting_for_semi_sync_ack_from_slave); if (unlikely(m_slaves_changed)) { if (unlikely(m_slaves.is_empty())) { - wait_for_slave_connection(); - mysql_mutex_unlock(&m_mutex); + m_slaves_changed= false; + mysql_cond_broadcast(&m_cond_reply); // Signal remove_slave + wait_for_slave_connection(thd); + /* Wait for slave unlocks m_mutex */ continue; } + set_stage_info(stage_waiting_for_semi_sync_ack_from_slave); if ((slave_count= listener.init_slave_sockets()) == 0) + { + mysql_mutex_unlock(&m_mutex); + m_slaves_changed= true; + continue; // Retry + } + if (slave_count < 0) goto end; m_slaves_changed= false; + mysql_cond_broadcast(&m_cond_reply); // Signal remove_slave + } + #ifdef HAVE_POLL DBUG_PRINT("info", ("fd count %u", slave_count)); #else DBUG_PRINT("info", ("fd count %u, max_fd %d", slave_count, (int) listener.get_max_fd())); #endif - } + mysql_mutex_unlock(&m_mutex); ret= listener.listen_on_sockets(); + if (ret <= 0) { - mysql_mutex_unlock(&m_mutex); ret= DBUG_IF("rpl_semisync_simulate_select_error") ? -1 : ret; if (ret == -1 && errno != EINTR) sql_print_information("Failed to wait on semi-sync sockets, " "error: errno=%d", socket_errno); - /* Sleep 1us, so other threads can catch the m_mutex easily. */ - my_sleep(1); continue; } + listener.clear_signal(); + mysql_mutex_lock(&m_mutex); set_stage_info(stage_reading_semi_sync_ack); Slave_ilist_iterator it(m_slaves); while ((slave= it++)) { - if (listener.is_socket_active(slave)) + if (slave->active && + ((slave->vio.read_pos < slave->vio.read_end) || + listener.is_socket_active(slave))) { ulong len; + /* Semi-sync packets will always be sent with pkt_nr == 1 */ net_clear(&net, 0); net.vio= &slave->vio; /* @@ -275,29 +340,42 @@ void Ack_receiver::run() len= my_net_read(&net); if (likely(len != packet_error)) - repl_semisync_master.report_reply_packet(slave->server_id(), - net.read_pos, len); - else { - if (net.last_errno == ER_NET_READ_ERROR) + int res; + res= repl_semisync_master.report_reply_packet(slave->server_id(), + net.read_pos, len); + if (unlikely(res < 0)) { - listener.clear_socket_info(slave); + /* + Slave has sent COM_QUIT or other failure. + Delete it from listener + */ + it.remove(); + m_slaves_changed= true; } + } + else if (net.last_errno == ER_NET_READ_ERROR) + { if (net.last_errno > 0 && global_system_variables.log_warnings > 2) sql_print_warning("Semisync ack receiver got error %d \"%s\" " "from slave server-id %d", net.last_errno, ER_DEFAULT(net.last_errno), slave->server_id()); + it.remove(); + m_slaves_changed= true; } } } mysql_mutex_unlock(&m_mutex); } + end: sql_print_information("Stopping ack receiver thread"); m_status= ST_DOWN; - delete thd; mysql_cond_broadcast(&m_cond); + mysql_cond_broadcast(&m_cond_reply); mysql_mutex_unlock(&m_mutex); + + delete thd; DBUG_VOID_RETURN; } diff --git a/sql/semisync_master_ack_receiver.h b/sql/semisync_master_ack_receiver.h index d869bd2e..eacb4b20 100644 --- a/sql/semisync_master_ack_receiver.h +++ b/sql/semisync_master_ack_receiver.h @@ -20,6 +20,7 @@ #include "my_pthread.h" #include "sql_class.h" #include "semisync.h" +#include "socketpair.h" #include <vector> struct Slave :public ilink @@ -29,6 +30,7 @@ struct Slave :public ilink #ifdef HAVE_POLL uint m_fds_index; #endif + bool active; my_socket sock_fd() const { return vio.mysql_socket.fd; } uint server_id() const { return thd->variables.server_id; } }; @@ -46,6 +48,7 @@ typedef I_List_iterator<Slave> Slave_ilist_iterator; add_slave: maintain a new semisync slave's information remove_slave: remove a semisync slave's information */ + class Ack_receiver : public Repl_semi_sync_base { public: @@ -96,15 +99,20 @@ public: { m_trace_level= trace_level; } + bool running() + { + return m_status != ST_DOWN; + } + private: enum status {ST_UP, ST_DOWN, ST_STOPPING}; - uint8 m_status; + enum status m_status; /* Protect m_status, m_slaves_changed and m_slaves. ack thread and other session may access the variables at the same time. */ mysql_mutex_t m_mutex; - mysql_cond_t m_cond; + mysql_cond_t m_cond, m_cond_reply; /* If slave list is updated(add or remove). */ bool m_slaves_changed; @@ -116,25 +124,103 @@ private: Ack_receiver& operator=(const Ack_receiver &ack_receiver); void set_stage_info(const PSI_stage_info &stage); - void wait_for_slave_connection(); + void wait_for_slave_connection(THD *thd); }; +extern my_socket global_ack_signal_fd; + +class Ack_listener +{ +public: + my_socket local_read_signal; + const Slave_ilist &m_slaves; + int error; + + Ack_listener(const Slave_ilist &slaves) + :local_read_signal(-1), m_slaves(slaves), error(0) + { + my_socket pipes[2]; +#ifdef _WIN32 + error= create_socketpair(pipes); +#else + if (!pipe(pipes)) + { + fcntl(pipes[0], F_SETFL, O_NONBLOCK); + fcntl(pipes[1], F_SETFL, O_NONBLOCK); + } + else + { + pipes[0]= pipes[1]= -1; + } +#endif /* _WIN32 */ + local_read_signal= pipes[0]; + global_ack_signal_fd= pipes[1]; + } + + virtual ~Ack_listener() + { +#ifdef _WIN32 + my_socket pipes[2]; + pipes[0]= local_read_signal; + pipes[1]= global_ack_signal_fd; + close_socketpair(pipes); +#else + if (global_ack_signal_fd >= 0) + close(global_ack_signal_fd); + if (local_read_signal >= 0) + close(local_read_signal); +#endif /* _WIN32 */ + global_ack_signal_fd= local_read_signal= -1; + } + + int got_error() { return error; } + + virtual bool has_signal_data()= 0; + + /* Clear data sent by signal_listener() to abort read */ + void clear_signal() + { + if (has_signal_data()) + { + char buff[100]; + /* Clear the signal message */ +#ifndef _WIN32 + read(local_read_signal, buff, sizeof(buff)); +#else + recv(local_read_signal, buff, sizeof(buff), 0); +#endif /* _WIN32 */ + } + } +}; + +static inline void signal_listener() +{ +#ifndef _WIN32 + my_write(global_ack_signal_fd, (uchar*) "a", 1, MYF(0)); +#else + send(global_ack_signal_fd, "a", 1, 0); +#endif /* _WIN32 */ +} + #ifdef HAVE_POLL #include <sys/poll.h> -#include <vector> -class Poll_socket_listener +class Poll_socket_listener final : public Ack_listener { +private: + std::vector<pollfd> m_fds; + public: Poll_socket_listener(const Slave_ilist &slaves) - :m_slaves(slaves) - { - } + :Ack_listener(slaves) + {} + + virtual ~Poll_socket_listener() = default; bool listen_on_sockets() { - return poll(m_fds.data(), m_fds.size(), 1000 /*1 Second timeout*/); + return poll(m_fds.data(), m_fds.size(), -1); } bool is_socket_active(const Slave *slave) @@ -148,15 +234,29 @@ public: m_fds[slave->m_fds_index].events= 0; } - uint init_slave_sockets() + bool has_signal_data() override + { + /* The signal fd is always first */ + return (m_fds[0].revents & POLLIN); + } + + int init_slave_sockets() { Slave_ilist_iterator it(const_cast<Slave_ilist&>(m_slaves)); Slave *slave; uint fds_index= 0; + pollfd poll_fd; m_fds.clear(); + /* First put in the signal socket */ + poll_fd.fd= local_read_signal; + poll_fd.events= POLLIN; + m_fds.push_back(poll_fd); + fds_index++; + while ((slave= it++)) { + slave->active= 1; pollfd poll_fd; poll_fd.fd= slave->sock_fd(); poll_fd.events= POLLIN; @@ -165,29 +265,30 @@ public: } return fds_index; } - -private: - const Slave_ilist &m_slaves; - std::vector<pollfd> m_fds; }; #else //NO POLL -class Select_socket_listener +class Select_socket_listener final : public Ack_listener { +private: + my_socket m_max_fd; + fd_set m_init_fds; + fd_set m_fds; + public: Select_socket_listener(const Slave_ilist &slaves) - :m_slaves(slaves), m_max_fd(INVALID_SOCKET) - { - } + :Ack_listener(slaves), m_max_fd(INVALID_SOCKET) + {} + + virtual ~Select_socket_listener() = default; bool listen_on_sockets() { /* Reinitialize the fds with active fds before calling select */ m_fds= m_init_fds; - struct timeval tv= {1,0}; /* select requires max fd + 1 for the first argument */ - return select((int) m_max_fd+1, &m_fds, NULL, NULL, &tv); + return select((int) m_max_fd+1, &m_fds, NULL, NULL, NULL); } bool is_socket_active(const Slave *slave) @@ -195,43 +296,61 @@ public: return FD_ISSET(slave->sock_fd(), &m_fds); } + bool has_signal_data() override + { + return FD_ISSET(local_read_signal, &m_fds); + } + void clear_socket_info(const Slave *slave) { FD_CLR(slave->sock_fd(), &m_init_fds); } - uint init_slave_sockets() + int init_slave_sockets() { Slave_ilist_iterator it(const_cast<Slave_ilist&>(m_slaves)); Slave *slave; uint fds_index= 0; FD_ZERO(&m_init_fds); + m_max_fd= -1; + + /* First put in the signal socket */ + FD_SET(local_read_signal, &m_init_fds); + fds_index++; + set_if_bigger(m_max_fd, local_read_signal); +#ifndef _WIN32 + if (local_read_signal > FD_SETSIZE) + { + int socket_id= local_read_signal; + sql_print_error("Semisync slave socket fd is %u. " + "select() cannot handle if the socket fd is " + "greater than %u (FD_SETSIZE).", socket_id, FD_SETSIZE); + return -1; + } +#endif + while ((slave= it++)) { my_socket socket_id= slave->sock_fd(); - m_max_fd= (socket_id > m_max_fd ? socket_id : m_max_fd); + set_if_bigger(m_max_fd, socket_id); #ifndef _WIN32 if (socket_id > FD_SETSIZE) { sql_print_error("Semisync slave socket fd is %u. " "select() cannot handle if the socket fd is " "greater than %u (FD_SETSIZE).", socket_id, FD_SETSIZE); - return 0; + it.remove(); + continue; } #endif //_WIN32 FD_SET(socket_id, &m_init_fds); fds_index++; + slave->active= 1; } return fds_index; } my_socket get_max_fd() { return m_max_fd; } - -private: - const Slave_ilist &m_slaves; - my_socket m_max_fd; - fd_set m_init_fds; - fd_set m_fds; }; #endif //HAVE_POLL diff --git a/sql/semisync_slave.cc b/sql/semisync_slave.cc index 788aab78..4314b116 100644 --- a/sql/semisync_slave.cc +++ b/sql/semisync_slave.cc @@ -20,20 +20,9 @@ Repl_semi_sync_slave repl_semisync_slave; -my_bool rpl_semi_sync_slave_enabled= 0; - +my_bool global_rpl_semi_sync_slave_enabled= 0; char rpl_semi_sync_slave_delay_master; -my_bool rpl_semi_sync_slave_status= 0; ulong rpl_semi_sync_slave_trace_level; - -/* - indicate whether or not the slave should send a reply to the master. - - This is set to true in repl_semi_slave_read_event if the current - event read is the last event of a transaction. And the value is - checked in repl_semi_slave_queue_event. -*/ -bool semi_sync_need_reply= false; unsigned int rpl_semi_sync_slave_kill_conn_timeout; unsigned long long rpl_semi_sync_slave_send_ack = 0; @@ -44,14 +33,26 @@ int Repl_semi_sync_slave::init_object() m_init_done = true; /* References to the parameter works after set_options(). */ - set_slave_enabled(rpl_semi_sync_slave_enabled); + set_slave_enabled(global_rpl_semi_sync_slave_enabled); set_trace_level(rpl_semi_sync_slave_trace_level); set_delay_master(rpl_semi_sync_slave_delay_master); set_kill_conn_timeout(rpl_semi_sync_slave_kill_conn_timeout); - return result; } +static bool local_semi_sync_enabled; + +int rpl_semi_sync_enabled(THD *thd, SHOW_VAR *var, void *buff, + system_status_var *status_var, + enum_var_type scope) +{ + local_semi_sync_enabled= repl_semisync_slave.get_slave_enabled(); + var->type= SHOW_BOOL; + var->value= (char*) &local_semi_sync_enabled; + return 0; +} + + int Repl_semi_sync_slave::slave_read_sync_header(const uchar *header, unsigned long total_len, int *semi_flags, @@ -61,12 +62,12 @@ int Repl_semi_sync_slave::slave_read_sync_header(const uchar *header, int read_res = 0; DBUG_ENTER("Repl_semi_sync_slave::slave_read_sync_header"); - if (rpl_semi_sync_slave_status) + if (get_slave_enabled()) { if (!DBUG_IF("semislave_corrupt_log") && header[0] == k_packet_magic_num) { - semi_sync_need_reply = (header[1] & k_packet_flag_sync); + bool semi_sync_need_reply = (header[1] & k_packet_flag_sync); *payload_len = total_len - 2; *payload = header + 2; @@ -85,7 +86,9 @@ int Repl_semi_sync_slave::slave_read_sync_header(const uchar *header, "len: %lu", total_len); read_res = -1; } - } else { + } + else + { *payload= header; *payload_len= total_len; } @@ -93,9 +96,23 @@ int Repl_semi_sync_slave::slave_read_sync_header(const uchar *header, DBUG_RETURN(read_res); } -int Repl_semi_sync_slave::slave_start(Master_info *mi) +/* + Set default semisync variables and print some replication info to the log + + Note that the main setup is done in request_transmit() +*/ + +void Repl_semi_sync_slave::slave_start(Master_info *mi) { - bool semi_sync= get_slave_enabled(); + + /* + Set semi_sync_enabled at slave start. This is not changed until next + slave start or reconnect. + */ + bool semi_sync= global_rpl_semi_sync_slave_enabled; + + set_slave_enabled(semi_sync); + mi->semi_sync_reply_enabled= 0; sql_print_information("Slave I/O thread: Start %s replication to\ master '%s@%s:%d' in log '%s' at position %lu", @@ -104,30 +121,29 @@ int Repl_semi_sync_slave::slave_start(Master_info *mi) const_cast<char *>(mi->master_log_name), (unsigned long)(mi->master_log_pos)); - if (semi_sync && !rpl_semi_sync_slave_status) - rpl_semi_sync_slave_status= 1; - /*clear the counter*/ rpl_semi_sync_slave_send_ack= 0; - return 0; } -int Repl_semi_sync_slave::slave_stop(Master_info *mi) +void Repl_semi_sync_slave::slave_stop(Master_info *mi) { if (get_slave_enabled()) kill_connection(mi->mysql); - if (rpl_semi_sync_slave_status) - rpl_semi_sync_slave_status= 0; - - return 0; + set_slave_enabled(0); } -int Repl_semi_sync_slave::reset_slave(Master_info *mi) +void Repl_semi_sync_slave::slave_reconnect(Master_info *mi) { - return 0; + /* + Start semi-sync either if it globally enabled or if was enabled + before the reconnect. + */ + if (global_rpl_semi_sync_slave_enabled || get_slave_enabled()) + slave_start(mi); } + void Repl_semi_sync_slave::kill_connection(MYSQL *mysql) { if (!mysql) @@ -194,33 +210,43 @@ int Repl_semi_sync_slave::request_transmit(Master_info *mi) !(res= mysql_store_result(mysql))) { sql_print_error("Execution failed on master: %s, error :%s", query, mysql_error(mysql)); + set_slave_enabled(0); return 1; } row= mysql_fetch_row(res); - if (DBUG_IF("master_not_support_semisync") || !row) + if (DBUG_IF("master_not_support_semisync") || (!row || ! row[1])) { /* Master does not support semi-sync */ - sql_print_warning("Master server does not support semi-sync, " - "fallback to asynchronous replication"); - rpl_semi_sync_slave_status= 0; + if (!row) + sql_print_warning("Master server does not support semi-sync, " + "fallback to asynchronous replication"); + set_slave_enabled(0); mysql_free_result(res); return 0; } + if (strcmp(row[1], "ON")) + sql_print_information("Slave has semi-sync enabled but master server does " + "not. Semi-sync will be activated when master " + "enables it"); mysql_free_result(res); /* Tell master dump thread that we want to do semi-sync - replication + replication. This is done by setting a thread local variable in + the master connection. */ query= "SET @rpl_semi_sync_slave= 1"; if (mysql_real_query(mysql, query, (ulong)strlen(query))) { - sql_print_error("Set 'rpl_semi_sync_slave=1' on master failed"); + sql_print_error("%s on master failed", query); + set_slave_enabled(0); return 1; } + mi->semi_sync_reply_enabled= 1; + /* Inform net_server that pkt_nr can come out of order */ + mi->mysql->net.pkt_nr_can_be_reset= 1; mysql_free_result(mysql_store_result(mysql)); - rpl_semi_sync_slave_status= 1; return 0; } @@ -230,46 +256,40 @@ int Repl_semi_sync_slave::slave_reply(Master_info *mi) MYSQL* mysql= mi->mysql; const char *binlog_filename= const_cast<char *>(mi->master_log_name); my_off_t binlog_filepos= mi->master_log_pos; - NET *net= &mysql->net; uchar reply_buffer[REPLY_MAGIC_NUM_LEN + REPLY_BINLOG_POS_LEN + REPLY_BINLOG_NAME_LEN]; int reply_res = 0; size_t name_len = strlen(binlog_filename); - DBUG_ENTER("Repl_semi_sync_slave::slave_reply"); + DBUG_ASSERT(get_slave_enabled() && mi->semi_sync_reply_enabled); - if (rpl_semi_sync_slave_status && semi_sync_need_reply) + /* Prepare the buffer of the reply. */ + reply_buffer[REPLY_MAGIC_NUM_OFFSET] = k_packet_magic_num; + int8store(reply_buffer + REPLY_BINLOG_POS_OFFSET, binlog_filepos); + memcpy(reply_buffer + REPLY_BINLOG_NAME_OFFSET, + binlog_filename, + name_len + 1 /* including trailing '\0' */); + + DBUG_PRINT("semisync", ("%s: reply (%s, %lu)", + "Repl_semi_sync_slave::slave_reply", + binlog_filename, (ulong)binlog_filepos)); + + /* + We have to do a net_clear() as with semi-sync the slave_reply's are + interleaved with data from the master and then the net->pkt_nr + cannot be kept in sync. Better to start pkt_nr from 0 again. + */ + net_clear(net, 0); + /* Send the reply. */ + reply_res = my_net_write(net, reply_buffer, + name_len + REPLY_BINLOG_NAME_OFFSET); + if (!reply_res) { - /* Prepare the buffer of the reply. */ - reply_buffer[REPLY_MAGIC_NUM_OFFSET] = k_packet_magic_num; - int8store(reply_buffer + REPLY_BINLOG_POS_OFFSET, binlog_filepos); - memcpy(reply_buffer + REPLY_BINLOG_NAME_OFFSET, - binlog_filename, - name_len + 1 /* including trailing '\0' */); - - DBUG_PRINT("semisync", ("%s: reply (%s, %lu)", - "Repl_semi_sync_slave::slave_reply", - binlog_filename, (ulong)binlog_filepos)); - - net_clear(net, 0); - /* Send the reply. */ - reply_res = my_net_write(net, reply_buffer, - name_len + REPLY_BINLOG_NAME_OFFSET); + reply_res= DBUG_IF("semislave_failed_net_flush") || net_flush(net); if (!reply_res) - { - reply_res = (DBUG_IF("semislave_failed_net_flush") || net_flush(net)); - if (reply_res) - sql_print_error("Semi-sync slave net_flush() reply failed"); rpl_semi_sync_slave_send_ack++; - } - else - { - sql_print_error("Semi-sync slave send reply failed: %s (%d)", - net->last_error, net->last_errno); - } } - DBUG_RETURN(reply_res); } diff --git a/sql/semisync_slave.h b/sql/semisync_slave.h index a8229245..6811584c 100644 --- a/sql/semisync_slave.h +++ b/sql/semisync_slave.h @@ -33,7 +33,7 @@ class Master_info; class Repl_semi_sync_slave :public Repl_semi_sync_base { public: - Repl_semi_sync_slave() :m_slave_enabled(false) {} + Repl_semi_sync_slave() :m_slave_enabled(false) {} ~Repl_semi_sync_slave() = default; void set_trace_level(unsigned long trace_level) { @@ -45,7 +45,7 @@ public: */ int init_object(); - bool get_slave_enabled() { + inline bool get_slave_enabled() { return m_slave_enabled; } @@ -53,7 +53,7 @@ public: m_slave_enabled = enabled; } - bool is_delay_master(){ + inline bool is_delay_master(){ return m_delay_master; } @@ -88,24 +88,23 @@ public: * binlog position. */ int slave_reply(Master_info* mi); - int slave_start(Master_info *mi); - int slave_stop(Master_info *mi); - int request_transmit(Master_info*); + void slave_start(Master_info *mi); + void slave_stop(Master_info *mi); + void slave_reconnect(Master_info *mi); + int request_transmit(Master_info *mi); void kill_connection(MYSQL *mysql); - int reset_slave(Master_info *mi); private: /* True when init_object has been called */ bool m_init_done; - bool m_slave_enabled; /* semi-sycn is enabled on the slave */ + bool m_slave_enabled; /* semi-sync is enabled on the slave */ bool m_delay_master; unsigned int m_kill_conn_timeout; }; /* System and status variables for the slave component */ -extern my_bool rpl_semi_sync_slave_enabled; -extern my_bool rpl_semi_sync_slave_status; +extern my_bool global_rpl_semi_sync_slave_enabled; extern ulong rpl_semi_sync_slave_trace_level; extern Repl_semi_sync_slave repl_semisync_slave; @@ -113,4 +112,7 @@ extern char rpl_semi_sync_slave_delay_master; extern unsigned int rpl_semi_sync_slave_kill_conn_timeout; extern unsigned long long rpl_semi_sync_slave_send_ack; +extern int rpl_semi_sync_enabled(THD *thd, SHOW_VAR *var, void *buff, + system_status_var *status_var, + enum_var_type scope); #endif /* SEMISYNC_SLAVE_H */ diff --git a/sql/service_wsrep.cc b/sql/service_wsrep.cc index e1a4a25b..17240fd4 100644 --- a/sql/service_wsrep.cc +++ b/sql/service_wsrep.cc @@ -395,17 +395,9 @@ extern "C" void wsrep_thd_set_PA_unsafe(THD *thd) } } -extern "C" int wsrep_thd_append_table_key(MYSQL_THD thd, - const char* db, - const char* table, - enum Wsrep_service_key_type key_type) +extern "C" uint32 wsrep_get_domain_id() { - wsrep_key_arr_t key_arr = {0, 0}; - int ret = wsrep_prepare_keys_for_isolation(thd, db, table, NULL, &key_arr); - ret = ret || wsrep_thd_append_key(thd, key_arr.keys, - (int)key_arr.keys_len, key_type); - wsrep_keys_free(&key_arr); - return ret; + return wsrep_gtid_domain_id; } extern "C" my_bool wsrep_thd_is_local_transaction(const THD *thd) @@ -413,4 +405,3 @@ extern "C" my_bool wsrep_thd_is_local_transaction(const THD *thd) return (wsrep_thd_is_local(thd) && thd->wsrep_cs().transaction().active()); } - diff --git a/sql/slave.cc b/sql/slave.cc index e781c461..27721e1b 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -46,7 +46,7 @@ #include <signal.h> #include <mysql.h> #include <myisam.h> - +#include "debug_sync.h" // debug_sync_set_action #include "sql_base.h" // close_thread_tables #include "tztime.h" // struct Time_zone #include "log_event.h" // Rotate_log_event, @@ -63,7 +63,6 @@ Master_info_index *master_info_index; #ifdef HAVE_REPLICATION #include "rpl_tblmap.h" -#include "debug_sync.h" #include "rpl_parallel.h" #include "sql_show.h" #include "semisync_slave.h" @@ -3200,6 +3199,14 @@ static bool send_show_master_info_data(THD *thd, Master_info *mi, bool full, mysql_mutex_lock(&mi->err_lock); /* err_lock is to protect mi->rli.last_error() */ mysql_mutex_lock(&mi->rli.err_lock); + + DBUG_EXECUTE_IF("hold_sss_with_err_lock", { + DBUG_ASSERT(!debug_sync_set_action( + thd, STRING_WITH_LEN("now SIGNAL sss_got_err_lock " + "WAIT_FOR sss_continue"))); + DBUG_SET("-d,hold_sss_with_err_lock"); + }); + protocol->store_string_or_null(mi->host, &my_charset_bin); protocol->store_string_or_null(mi->user, &my_charset_bin); protocol->store((uint32) mi->port); @@ -3279,7 +3286,8 @@ static bool send_show_master_info_data(THD *thd, Master_info *mi, bool full, while the slave is processing ignored events, such as those skipped due to slave_skip_counter. */ - if (mi->using_parallel() && idle && !mi->rli.parallel.workers_idle()) + if (mi->using_parallel() && idle && + !rpl_parallel::workers_idle(&mi->rli)) idle= false; } if (idle) @@ -4446,6 +4454,15 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli, { Gtid_log_event *gev= static_cast<Gtid_log_event *>(ev); +#ifdef ENABLED_DEBUG_SYNC + DBUG_EXECUTE_IF( + "pause_sql_thread_on_relay_fde_after_trans", + { + DBUG_SET("-d,pause_sql_thread_on_relay_fde_after_trans"); + DBUG_SET("+d,pause_sql_thread_on_next_relay_fde"); + }); +#endif + /* For GTID, allocate a new sub_id for the given domain_id. The sub_id must be allocated in increasing order of binlog order. @@ -4598,12 +4615,14 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli, #endif /* WITH_WSREP */ #ifdef ENABLED_DEBUG_SYNC DBUG_EXECUTE_IF( - "pause_sql_thread_on_fde", - if (ev && typ == FORMAT_DESCRIPTION_EVENT) { + "pause_sql_thread_on_next_relay_fde", + if (ev && typ == FORMAT_DESCRIPTION_EVENT && + ((Format_description_log_event *) ev)->is_relay_log_event()) { DBUG_ASSERT(!debug_sync_set_action( thd, STRING_WITH_LEN( "now SIGNAL paused_on_fde WAIT_FOR sql_thread_continue"))); + DBUG_SET("-d,pause_sql_thread_on_next_relay_fde"); }); #endif @@ -4720,6 +4739,7 @@ static int try_to_reconnect(THD *thd, MYSQL *mysql, Master_info *mi, sql_print_information("%s", messages[SLAVE_RECON_MSG_KILLED_AFTER]); return 1; } + repl_semisync_slave.slave_reconnect(mi); return 0; } @@ -4808,14 +4828,7 @@ pthread_handler_t handle_slave_io(void *arg) } thd->variables.wsrep_on= 0; - if (DBUG_IF("failed_slave_start") - || repl_semisync_slave.slave_start(mi)) - { - mi->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, NULL, - ER_THD(thd, ER_SLAVE_FATAL_ERROR), - "Failed to run 'thread_start' hook"); - goto err; - } + repl_semisync_slave.slave_start(mi); if (!(mi->mysql = mysql = mysql_init(NULL))) { @@ -4909,6 +4922,7 @@ connected: if (try_to_reconnect(thd, mysql, mi, &retry_count, suppress_warnings, reconnect_messages[SLAVE_RECON_ACT_REG])) goto err; + goto connected; } @@ -4932,7 +4946,13 @@ connected: goto err; goto connected; } - DBUG_EXECUTE_IF("fail_com_register_slave", goto err;); + DBUG_EXECUTE_IF("fail_com_register_slave", + { + mi->report(ERROR_LEVEL, ER_SLAVE_MASTER_COM_FAILURE, NULL, + ER(ER_SLAVE_MASTER_COM_FAILURE), "COM_REGISTER_SLAVE", + "Debug Induced Error"); + goto err; + }); } DBUG_PRINT("info",("Starting reading binary log from master")); @@ -4966,6 +4986,15 @@ connected: we're in fact receiving nothing. */ THD_STAGE_INFO(thd, stage_waiting_for_master_to_send_event); + +#ifdef ENABLED_DEBUG_SYNC + DBUG_EXECUTE_IF("pause_before_io_read_event", + { + DBUG_ASSERT(!debug_sync_set_action( thd, STRING_WITH_LEN( + "now signal io_thread_at_read_event wait_for io_thread_continue_read_event"))); + DBUG_SET("-d,pause_before_io_read_event"); + };); +#endif event_len= read_event(mysql, mi, &suppress_warnings, &network_read_len); if (check_io_slave_killed(mi, NullS)) goto err; @@ -5065,17 +5094,36 @@ Stopping slave I/O thread due to out-of-memory error from master"); goto err; } - if (rpl_semi_sync_slave_status && (mi->semi_ack & SEMI_SYNC_NEED_ACK)) + if (repl_semisync_slave.get_slave_enabled() && + mi->semi_sync_reply_enabled && + (mi->semi_ack & SEMI_SYNC_NEED_ACK)) { - /* - We deliberately ignore the error in slave_reply, such error should - not cause the slave IO thread to stop, and the error messages are - already reported. - */ - DBUG_EXECUTE_IF("simulate_delay_semisync_slave_reply", my_sleep(800000);); - (void)repl_semisync_slave.slave_reply(mi); + DBUG_EXECUTE_IF("simulate_delay_semisync_slave_reply", + my_sleep(800000);); + if (repl_semisync_slave.slave_reply(mi)) + { + /* + Master is not responding (gone away?) or it has turned semi sync + off. Turning off semi-sync responses as there is no point in sending + data to the master if the master not receiving the messages. + This also stops the logs from getting filled with + "Semi-sync slave net_flush() reply failed" messages. + On reconnect semi sync will be turned on again, if the + master has semi-sync enabled. + + We check mi->abort_slave to see if the io thread was + killed and in this case we do not need an error message as + we know what is going on. + */ + if (!mi->abort_slave) + sql_print_error("Master server does not read semi-sync messages " + "last_error: %s (%d). " + "Fallback to asynchronous replication", + mi->mysql->net.last_error, + mi->mysql->net.last_errno); + mi->semi_sync_reply_enabled= 0; + } } - if (mi->using_gtid == Master_info::USE_GTID_NO && /* If rpl_semi_sync_slave_delay_master is enabled, we will flush @@ -5513,19 +5561,25 @@ pthread_handler_t handle_slave_sql(void *arg) } else rli->gtid_skip_flag = GTID_SKIP_NOT; + mysql_mutex_lock(&rli->data_lock); if (init_relay_log_pos(rli, rli->group_relay_log_name, rli->group_relay_log_pos, - 1 /*need data lock*/, &errmsg, + 0 /*need data lock*/, &errmsg, 1 /*look for a description_event*/)) { rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, NULL, "Error initializing relay log position: %s", errmsg); + mysql_mutex_unlock(&rli->data_lock); goto err_before_start; } rli->reset_inuse_relaylog(); if (rli->alloc_inuse_relaylog(rli->group_relay_log_name)) + { + mysql_mutex_unlock(&rli->data_lock); goto err_before_start; + } + mysql_mutex_unlock(&rli->data_lock); strcpy(rli->future_event_master_log_name, rli->group_master_log_name); THD_CHECK_SENTRY(thd); @@ -6948,7 +7002,7 @@ dbug_gtid_accept: */ mi->do_accept_own_server_id= (s_id == global_system_variables.server_id && - rpl_semi_sync_slave_enabled && opt_gtid_strict_mode && + repl_semisync_slave.get_slave_enabled() && opt_gtid_strict_mode && mi->using_gtid != Master_info::USE_GTID_NO && !mysql_bin_log.check_strict_gtid_sequence(event_gtid.domain_id, event_gtid.server_id, diff --git a/sql/socketpair.c b/sql/socketpair.c new file mode 100644 index 00000000..ef89fa04 --- /dev/null +++ b/sql/socketpair.c @@ -0,0 +1,156 @@ +/* socketpair.c +Copyright 2007, 2010 by Nathan C. Myers <ncm@cantrip.org> +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + The name of the author must not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* Changes: + * 2023-12-25 Addopted for MariaDB usage + * 2014-02-12: merge David Woodhouse, Ger Hobbelt improvements + * git.infradead.org/users/dwmw2/openconnect.git/commitdiff/bdeefa54 + * github.com/GerHobbelt/selectable-socketpair + * always init the socks[] to -1/INVALID_SOCKET on error, both on Win32/64 + * and UNIX/other platforms + * 2013-07-18: Change to BSD 3-clause license + * 2010-03-31: + * set addr to 127.0.0.1 because win32 getsockname does not always set it. + * 2010-02-25: + * set SO_REUSEADDR option to avoid leaking some windows resource. + * Windows System Error 10049, "Event ID 4226 TCP/IP has reached + * the security limit imposed on the number of concurrent TCP connect + * attempts." Bleah. + * 2007-04-25: + * preserve value of WSAGetLastError() on all error returns. + * 2007-04-22: (Thanks to Matthew Gregan <kinetik@flim.org>) + * s/EINVAL/WSAEINVAL/ fix trivial compile failure + * s/socket/WSASocket/ enable creation of sockets suitable as stdin/stdout + * of a child process. + * add argument make_overlapped + */ + +#include <my_global.h> +#ifdef _WIN32 +#include <ws2tcpip.h> /* socklen_t, et al (MSVC20xx) */ +#include <windows.h> +#include <io.h> +#include "socketpair.h" + +#define safe_errno (errno != 0) ? errno : -1 + +/** + create_socketpair() + + @param socks[2] Will be filled by 2 SOCKET entries (similar to pipe()) + socks[0] for reading + socks[1] for writing + + @return: 0 ok + # System error code. -1 if unknown + */ + +int create_socketpair(SOCKET socks[2]) +{ + union + { + struct sockaddr_in inaddr; + struct sockaddr addr; + } a; + SOCKET listener= -1; + int reuse = 1; + int last_error; + socklen_t addrlen = sizeof(a.inaddr); + + socks[0]= socks[1]= -1; + + if ((listener= socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) + return safe_errno; + + memset(&a, 0, sizeof(a)); + a.inaddr.sin_family = AF_INET; + a.inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + a.inaddr.sin_port = 0; + + for (;;) /* Avoid using goto */ + { + if (setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, + (char*) &reuse, (socklen_t) sizeof(reuse)) == -1) + break; + if (bind(listener, &a.addr, sizeof(a.inaddr)) == SOCKET_ERROR) + break; + + memset(&a, 0, sizeof(a)); + if (getsockname(listener, &a.addr, &addrlen) == SOCKET_ERROR) + break; + // win32 getsockname may only set the port number, p=0.0005. + // ( http://msdn.microsoft.com/library/ms738543.aspx ): + a.inaddr.sin_addr.s_addr= htonl(INADDR_LOOPBACK); + a.inaddr.sin_family= AF_INET; + + if (listen(listener, 1) == SOCKET_ERROR) + break; + + socks[1]= socket(AF_INET, SOCK_STREAM, 0); + if (socks[1] == -1) + break; + if (connect(socks[1], &a.addr, sizeof(a.inaddr)) == SOCKET_ERROR) + break; + + socks[0]= accept(listener, NULL, NULL); + if (socks[0] == -1) + break; + + closesocket(listener); + + { + /* Make both sockets non blocking */ + ulong arg= 1; + ioctlsocket(socks[0], FIONBIO,(void*) &arg); + ioctlsocket(socks[1], FIONBIO,(void*) &arg); + } + return 0; + } + /* Error handling */ + last_error= WSAGetLastError(); + if (listener != -1) + closesocket(listener); + close_socketpair(socks); + WSASetLastError(last_error); + + return last_error; +} + +/* + Free socketpair +*/ + +void close_socketpair(SOCKET socks[2]) +{ + if (socks[0] != -1) + closesocket(socks[0]); + if (socks[1] != -1) + closesocket(socks[1]); + socks[0]= socks[1]= -1; +} + +#endif /*_WIN32 */ diff --git a/sql/socketpair.h b/sql/socketpair.h new file mode 100644 index 00000000..d9f89c84 --- /dev/null +++ b/sql/socketpair.h @@ -0,0 +1,21 @@ +/* Copyright (c) 2023, MariaDB Plc + + 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 */ + +#ifdef _WIN32 +C_MODE_START + int create_socketpair(SOCKET socks[2]); + void close_socketpair(SOCKET socks[2]); +C_MODE_END +#endif /* _WIN32 */ @@ -2731,7 +2731,13 @@ sp_update_stmt_used_routines(THD *thd, Query_tables_list *prelocking_ctx, for (uint i=0 ; i < src->records ; i++) { Sroutine_hash_entry *rt= (Sroutine_hash_entry *)my_hash_element(src, i); - (void)sp_add_used_routine(prelocking_ctx, thd->stmt_arena, + DBUG_ASSERT(thd->active_stmt_arena_to_use()-> + is_stmt_prepare_or_first_stmt_execute() || + thd->active_stmt_arena_to_use()-> + is_conventional() || + thd->active_stmt_arena_to_use()->state == + Query_arena::STMT_SP_QUERY_ARGUMENTS); + (void)sp_add_used_routine(prelocking_ctx, thd->active_stmt_arena_to_use(), &rt->mdl_request.key, rt->m_handler, belong_to_view); } @@ -2757,7 +2763,7 @@ void sp_update_stmt_used_routines(THD *thd, Query_tables_list *prelocking_ctx, TABLE_LIST *belong_to_view) { for (Sroutine_hash_entry *rt= src->first; rt; rt= rt->next) - (void)sp_add_used_routine(prelocking_ctx, thd->stmt_arena, + (void)sp_add_used_routine(prelocking_ctx, thd->active_stmt_arena_to_use(), &rt->mdl_request.key, rt->m_handler, belong_to_view); } diff --git a/sql/sp_cache.cc b/sql/sp_cache.cc index 36ad3710..1da807e9 100644 --- a/sql/sp_cache.cc +++ b/sql/sp_cache.cc @@ -195,7 +195,7 @@ sp_head *sp_cache_lookup(sp_cache **cp, const Database_qualified_name *name) sp_cache *c= *cp; if (! c) return NULL; - return c->lookup(buf, name->make_qname(buf, sizeof(buf))); + return c->lookup(buf, name->make_qname(buf, sizeof(buf), true)); } @@ -302,7 +302,7 @@ sp_cache::~sp_cache() void sp_cache::init() { - my_hash_init(key_memory_sp_cache, &m_hashtable, system_charset_info, 0, 0, 0, + my_hash_init(key_memory_sp_cache, &m_hashtable, &my_charset_bin, 0, 0, 0, hash_get_key_for_sp_head, hash_free_sp_head, 0); } diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 5fd6ab52..58235055 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -1575,7 +1575,7 @@ sp_head::execute(THD *thd, bool merge_da_on_success) { // Don't count a call ended with an error as normal run executed_counter= 0; - main_mem_root.read_only= 0; + main_mem_root.flags &= ~ROOT_FLAG_READ_ONLY; reset_instrs_executed_counter(); } #endif @@ -1696,10 +1696,10 @@ sp_head::execute(THD *thd, bool merge_da_on_success) #ifdef PROTECT_STATEMENT_MEMROOT if (!err_status) { - if (!main_mem_root.read_only && + if (!(main_mem_root.flags & ROOT_FLAG_READ_ONLY) && has_all_instrs_executed()) { - main_mem_root.read_only= 1; + main_mem_root.flags |= ROOT_FLAG_READ_ONLY; } ++executed_counter; DBUG_PRINT("info", ("execute counter: %lu", executed_counter)); diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 029b12ad..c6080e5b 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -957,6 +957,7 @@ class User_table_tabular: public User_table int get_auth(THD *thd, MEM_ROOT *root, ACL_USER *u) const { + mysql_mutex_assert_owner(&acl_cache->lock); u->alloc_auth(root, 1); if (have_password()) { @@ -2316,6 +2317,9 @@ static bool validate_password(THD *thd, const LEX_CSTRING &user, static int set_user_salt(ACL_USER::AUTH *auth, plugin_ref plugin) { st_mysql_auth *info= (st_mysql_auth *) plugin_decl(plugin)->info; + + mysql_mutex_assert_owner(&acl_cache->lock); + if (info->interface_version >= 0x0202 && info->preprocess_hash && auth->auth_string.length) { @@ -2351,6 +2355,8 @@ static int set_user_auth(THD *thd, const LEX_CSTRING &user, plugin_ref plugin= get_auth_plugin(thd, auth->plugin, &unlock_plugin); int res= 1; + mysql_mutex_assert_owner(&acl_cache->lock); + if (!plugin) { push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, @@ -2427,10 +2433,13 @@ static bool set_user_salt_if_needed(ACL_USER *user_copy, int curr_auth, if (auth_copy->salt.str) return 0; // already done + mysql_mutex_lock(&acl_cache->lock); if (set_user_salt(auth_copy, plugin)) + { + mysql_mutex_unlock(&acl_cache->lock); return 1; + } - mysql_mutex_lock(&acl_cache->lock); ACL_USER *user= find_user_exact(user_copy->host.hostname, user_copy->user.str); // make sure the user wasn't altered or dropped meanwhile if (user) @@ -3403,10 +3412,18 @@ end: check_role_is_granted_callback, NULL) == -1)) { - /* Role is not granted but current user can see the role */ - my_printf_error(ER_INVALID_ROLE, "User %`s@%`s has not been granted role %`s", - MYF(0), thd->security_ctx->priv_user, - thd->security_ctx->priv_host, rolename); + /* This happens for SET ROLE case and when `--skip-name-resolve` option + is used. In that situation host can be NULL and current user is always + target user, so printing `priv_user@priv_host` is not incorrect. + */ + if (!host) + my_printf_error(ER_INVALID_ROLE, "User %`s@%`s has not been granted role %`s", + MYF(0), thd->security_ctx->priv_user, + thd->security_ctx->priv_host, rolename); + else + /* Role is not granted but current user can see the role */ + my_printf_error(ER_INVALID_ROLE, "User %`s@%`s has not been granted role %`s", + MYF(0), user, host, rolename); } else { @@ -3477,6 +3494,7 @@ ACL_USER::ACL_USER(THD *thd, const LEX_USER &combo, const Account_options &options, const privilege_t privileges) { + mysql_mutex_assert_owner(&acl_cache->lock); user= safe_lexcstrdup_root(&acl_memroot, combo.user); update_hostname(&host, safe_strdup_root(&acl_memroot, combo.host.str)); hostname_length= combo.host.length; @@ -3493,6 +3511,8 @@ static int acl_user_update(THD *thd, ACL_USER *acl_user, uint nauth, const privilege_t privileges) { ACL_USER_PARAM::AUTH *work_copy= NULL; + mysql_mutex_assert_owner(&acl_cache->lock); + if (nauth) { if (!(work_copy= (ACL_USER_PARAM::AUTH*) @@ -5201,6 +5221,7 @@ update_role_mapping(LEX_CSTRING *user, LEX_CSTRING *host, LEX_CSTRING *role, return 0; } + mysql_mutex_assert_owner(&acl_cache->lock); /* allocate a new entry that will go in the hash */ ROLE_GRANT_PAIR *hash_entry= new (&acl_memroot) ROLE_GRANT_PAIR; if (hash_entry->init(&acl_memroot, user->str, host->str, @@ -5265,6 +5286,7 @@ replace_proxies_priv_table(THD *thd, TABLE *table, const LEX_USER *user, DBUG_ENTER("replace_proxies_priv_table"); + mysql_mutex_assert_owner(&acl_cache->lock); if (!table) { my_error(ER_NO_SUCH_TABLE, MYF(0), MYSQL_SCHEMA_NAME.str, @@ -8373,11 +8395,6 @@ bool check_grant(THD *thd, privilege_t want_access, TABLE_LIST *tables, INSERT_ACL : SELECT_ACL); } - if (tl->with || !tl->db.str || - (tl->select_lex && - (tl->with= tl->select_lex->find_table_def_in_with_clauses(tl)))) - continue; - const ACL_internal_table_access *access= get_cached_table_access(&t_ref->grant.m_internal, t_ref->get_db_name(), @@ -12159,8 +12176,8 @@ static my_bool count_column_grants(void *grant_table, This must be performed under the mutex in order to make sure the iteration does not fail. */ -static int show_column_grants(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +static int show_column_grants(THD *thd, SHOW_VAR *var, void *buff, + system_status_var *, enum enum_var_type scope) { var->type= SHOW_ULONG; var->value= buff; @@ -12176,8 +12193,8 @@ static int show_column_grants(THD *thd, SHOW_VAR *var, char *buff, return 0; } -static int show_database_grants(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +static int show_database_grants(THD *thd, SHOW_VAR *var, void *buff, + system_status_var *, enum enum_var_type scope) { var->type= SHOW_UINT; var->value= buff; @@ -13568,8 +13585,37 @@ static bool find_mpvio_user(MPVIO_EXT *mpvio) DBUG_RETURN(0); } + +/** + Determine if the client is MySQL Connector/NET. + + Checks whether the given connection attributes blob corresponds to + MySQL Connector/NET by examining the "_client_name" attribute, which is + expected to be the first attribute in the blob. + + @param connection_attrs - The connection attributes blob. + @param length - The length of the blob. + + @return true if the client is MySQL Connector/NET, false otherwise. +*/ +static inline bool is_connector_net_client(const char *connection_attrs, + size_t length) +{ + constexpr LEX_CSTRING prefix= + {STRING_WITH_LEN("\x0c_client_name\x13mysql-connector-net")}; + + if (length < prefix.length) + return false; + + /* Optimization to avoid following memcmp in common cases.*/ + if (connection_attrs[prefix.length - 1] != prefix.str[prefix.length - 1]) + return false; + + return !memcmp(connection_attrs, prefix.str, prefix.length); +} + static bool -read_client_connect_attrs(char **ptr, char *end, CHARSET_INFO *from_cs) +read_client_connect_attrs(char **ptr, char *end, THD* thd) { ulonglong length; char *ptr_save= *ptr; @@ -13592,10 +13638,14 @@ read_client_connect_attrs(char **ptr, char *end, CHARSET_INFO *from_cs) if (length > 65535) return true; - if (PSI_CALL_set_thread_connect_attrs(*ptr, (uint)length, from_cs) && + if (PSI_CALL_set_thread_connect_attrs(*ptr, (uint)length, thd->charset()) && current_thd->variables.log_warnings) sql_print_warning("Connection attributes of length %llu were truncated", length); + + /* Connector/Net crashes, when "show collations" returns NULL IDs*/ + if (is_connector_net_client(*ptr, length)) + thd->variables.old_behavior |= OLD_MODE_NO_NULL_COLLATION_IDS; return false; } @@ -13729,7 +13779,7 @@ static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length) } if ((thd->client_capabilities & CLIENT_CONNECT_ATTRS) && - read_client_connect_attrs(&next_field, end, thd->charset())) + read_client_connect_attrs(&next_field, end, thd)) { my_message(ER_UNKNOWN_COM_ERROR, ER_THD(thd, ER_UNKNOWN_COM_ERROR), MYF(0)); @@ -13979,7 +14029,7 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio, if ((thd->client_capabilities & CLIENT_CONNECT_ATTRS) && read_client_connect_attrs(&next_field, ((char *)net->read_pos) + pkt_len, - mpvio->auth_info.thd->charset())) + mpvio->auth_info.thd)) return packet_error; /* diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc index fcbd8a55..a3b9bbd4 100644 --- a/sql/sql_admin.cc +++ b/sql/sql_admin.cc @@ -818,7 +818,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, if (lock_type == TL_WRITE && table->mdl_request.type > MDL_SHARED_WRITE) { if (table->table->s->tmp_table) - thd->close_unused_temporary_table_instances(tables); + thd->close_unused_temporary_table_instances(table); else { /* Store information about table for ddl log */ diff --git a/sql/sql_alter.cc b/sql/sql_alter.cc index 3b9c8402..ff7b0be7 100644 --- a/sql/sql_alter.cc +++ b/sql/sql_alter.cc @@ -641,19 +641,19 @@ bool Sql_cmd_alter_table::execute(THD *thd) } wsrep::key_array keys; - wsrep_append_fk_parent_table(thd, first_table, &keys); - - WSREP_TO_ISOLATION_BEGIN_ALTER(lex->name.str ? select_lex->db.str - : first_table->db.str, - lex->name.str ? lex->name.str - : first_table->table_name.str, - first_table, &alter_info, &keys, - used_engine ? &create_info : nullptr) + if (!wsrep_append_fk_parent_table(thd, first_table, &keys)) { - WSREP_WARN("ALTER TABLE isolation failure"); - DBUG_RETURN(TRUE); + WSREP_TO_ISOLATION_BEGIN_ALTER(lex->name.str ? select_lex->db.str + : first_table->db.str, + lex->name.str ? lex->name.str + : first_table->table_name.str, + first_table, &alter_info, &keys, + used_engine ? &create_info : nullptr) + { + WSREP_WARN("ALTER TABLE isolation failure"); + DBUG_RETURN(TRUE); + } } - DEBUG_SYNC(thd, "wsrep_alter_table_after_toi"); } #endif diff --git a/sql/sql_analyse.cc b/sql/sql_analyse.cc index 4c853689..93b0dbb3 100644 --- a/sql/sql_analyse.cc +++ b/sql/sql_analyse.cc @@ -953,7 +953,8 @@ void field_longlong::get_opt_type(String *answer, UINT_MAX24 : INT_MAX24)) snprintf(buff, sizeof(buff), "MEDIUMINT(%d)", (int) max_length); else if (min_arg >= INT_MIN32 && max_arg <= (min_arg >= 0 ? - UINT_MAX32 : INT_MAX32)) + (longlong) UINT_MAX32 : + (longlong) INT_MAX32)) snprintf(buff, sizeof(buff), "INT(%d)", (int) max_length); else snprintf(buff, sizeof(buff), "BIGINT(%d)", (int) max_length); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index a0656e48..7b9ffc2e 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -74,7 +74,9 @@ No_such_table_error_handler::handle_condition(THD *, *cond_hdl= NULL; if (!first_error) first_error= sql_errno; - if (sql_errno == ER_NO_SUCH_TABLE || sql_errno == ER_NO_SUCH_TABLE_IN_ENGINE) + if (sql_errno == ER_NO_SUCH_TABLE + || sql_errno == ER_NO_SUCH_TABLE_IN_ENGINE + || sql_errno == ER_UNKNOWN_SEQUENCES) { m_handled_errors++; return TRUE; @@ -2288,6 +2290,7 @@ retry_share: if (thd->has_read_only_protection()) { MYSQL_UNBIND_TABLE(table->file); + table->vcol_cleanup_expr(thd); tc_release_table(table); DBUG_RETURN(TRUE); } @@ -2307,6 +2310,7 @@ retry_share: if (result) { MYSQL_UNBIND_TABLE(table->file); + table->vcol_cleanup_expr(thd); tc_release_table(table); DBUG_RETURN(TRUE); } @@ -7202,6 +7206,7 @@ set_new_item_local_context(THD *thd, Item_ident *item, TABLE_LIST *table_ref) if (!(context= new (thd->mem_root) Name_resolution_context)) return TRUE; context->init(); + context->select_lex= table_ref->select_lex; context->first_name_resolution_table= context->last_name_resolution_table= table_ref; item->context= context; @@ -8020,7 +8025,7 @@ bool setup_fields(THD *thd, Ref_ptr_array ref_pointer_array, while ((item= it++)) { if (make_pre_fix) - pre_fix->push_back(item, thd->stmt_arena->mem_root); + pre_fix->push_back(item, thd->active_stmt_arena_to_use()->mem_root); if (item->fix_fields_if_needed_for_scalar(thd, it.ref())) { diff --git a/sql/sql_binlog.cc b/sql/sql_binlog.cc index e71c7015..118b361e 100644 --- a/sql/sql_binlog.cc +++ b/sql/sql_binlog.cc @@ -137,7 +137,7 @@ int binlog_defragment(THD *thd) entry[k]= (user_var_entry*) my_hash_search(&thd->user_vars, (uchar*) name[k].str, name[k].length); - if (!entry[k] || entry[k]->type != STRING_RESULT) + if (!entry[k] || entry[k]->type_handler()->result_type() != STRING_RESULT) { my_error(ER_WRONG_TYPE_FOR_VAR, MYF(0), name[k].str); return -1; @@ -162,7 +162,8 @@ int binlog_defragment(THD *thd) gathered_length += entry[k]->length; } for (uint k=0; k < 2; k++) - update_hash(entry[k], true, NULL, 0, STRING_RESULT, &my_charset_bin, 0); + update_hash(entry[k], true, NULL, 0, + &type_handler_long_blob, &my_charset_bin); DBUG_ASSERT(gathered_length == thd->lex->comment.length); diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 9da1ec54..17d89188 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -727,7 +727,6 @@ THD::THD(my_thread_id id, bool is_wsrep_applier) wsrep_wfc() #endif /*WITH_WSREP */ { - ulong tmp; bzero(&variables, sizeof(variables)); /* @@ -879,14 +878,6 @@ THD::THD(my_thread_id id, bool is_wsrep_applier) tablespace_op=FALSE; - /* - Initialize the random generator. We call my_rnd() without a lock as - it's not really critical if two threads modifies the structure at the - same time. We ensure that we have an unique number foreach thread - by adding the address of the stack. - */ - tmp= (ulong) (my_rnd(&sql_rand) * 0xffffffff); - my_rnd_init(&rand, tmp + (ulong)((size_t) &rand), tmp + (ulong) ::global_query_id); substitute_null_with_insert_id = FALSE; lock_info.mysql_thd= (void *)this; @@ -1247,7 +1238,9 @@ void THD::init() user_time.val= start_time= start_time_sec_part= 0; - server_status= SERVER_STATUS_AUTOCOMMIT; + server_status= 0; + if (variables.option_bits & OPTION_AUTOCOMMIT) + server_status|= SERVER_STATUS_AUTOCOMMIT; if (variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES) server_status|= SERVER_STATUS_NO_BACKSLASH_ESCAPES; if (variables.sql_mode & MODE_ANSI_QUOTES) @@ -1312,6 +1305,17 @@ void THD::init() /* Set to handle counting of aborted connections */ userstat_running= opt_userstat_running; last_global_update_time= current_connect_time= time(NULL); + + /* + Initialize the random generator. We call my_rnd() without a lock as + it's not really critical if two threads modify the structure at the + same time. We ensure that we have a unique number for each thread + by adding the address of this THD. + */ + ulong tmp= (ulong) (my_rnd(&sql_rand) * 0xffffffff); + my_rnd_init(&rand, tmp + (ulong)(intptr) this, + (ulong)(my_timer_cycles() + global_query_id)); + #ifndef EMBEDDED_LIBRARY session_tracker.enable(this); #endif //EMBEDDED_LIBRARY @@ -1604,6 +1608,10 @@ void THD::free_connection() vio_delete(net.vio); net.vio= nullptr; net_end(&net); + delete(rgi_fake); + rgi_fake= NULL; + delete(rli_fake); + rli_fake= NULL; #endif if (!cleanup_done) cleanup(); @@ -1642,6 +1650,7 @@ void THD::reset_for_reuse() abort_on_warning= 0; free_connection_done= 0; m_command= COM_CONNECT; + proc_info= "login"; // Same as in THD::THD() transaction->on= 1; #if defined(ENABLED_PROFILING) profiling.reset(); @@ -1654,6 +1663,7 @@ void THD::reset_for_reuse() wsrep_cs().reset_error(); wsrep_aborter= 0; wsrep_abort_by_kill= NOT_KILLED; + my_free(wsrep_abort_by_kill_err); wsrep_abort_by_kill_err= 0; #ifndef DBUG_OFF wsrep_killed_state= 0; @@ -1696,6 +1706,8 @@ THD::~THD() #ifdef WITH_WSREP mysql_cond_destroy(&COND_wsrep_thd); + my_free(wsrep_abort_by_kill_err); + wsrep_abort_by_kill_err= 0; #endif mdl_context.destroy(); @@ -1708,17 +1720,6 @@ THD::~THD() dbug_sentry= THD_SENTRY_GONE; #endif #ifndef EMBEDDED_LIBRARY - if (rgi_fake) - { - delete rgi_fake; - rgi_fake= NULL; - } - if (rli_fake) - { - delete rli_fake; - rli_fake= NULL; - } - if (rgi_slave) rgi_slave->cleanup_after_session(); my_free(semisync_info); @@ -1726,6 +1727,7 @@ THD::~THD() main_lex.free_set_stmt_mem_root(); free_root(&main_mem_root, MYF(0)); my_free(m_token_array); + my_free(killed_err); main_da.free_memory(); if (tdc_hash_pins) lf_hash_put_pins(tdc_hash_pins); @@ -1857,14 +1859,6 @@ void add_diff_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var, */ } -#define SECONDS_TO_WAIT_FOR_KILL 2 -#if !defined(_WIN32) && defined(HAVE_SELECT) -/* my_sleep() can wait for sub second times */ -#define WAIT_FOR_KILL_TRY_TIMES 20 -#else -#define WAIT_FOR_KILL_TRY_TIMES 2 -#endif - /** Awake a thread. @@ -2142,7 +2136,11 @@ void THD::reset_killed() mysql_mutex_assert_not_owner(&LOCK_thd_kill); mysql_mutex_lock(&LOCK_thd_kill); killed= NOT_KILLED; - killed_err= 0; + if (unlikely(killed_err)) + { + my_free(killed_err); + killed_err= 0; + } mysql_mutex_unlock(&LOCK_thd_kill); } #ifdef WITH_WSREP @@ -2153,6 +2151,7 @@ void THD::reset_killed() mysql_mutex_assert_not_owner(&LOCK_thd_kill); mysql_mutex_lock(&LOCK_thd_kill); wsrep_abort_by_kill= NOT_KILLED; + my_free(wsrep_abort_by_kill_err); wsrep_abort_by_kill_err= 0; mysql_mutex_unlock(&LOCK_thd_kill); } @@ -5933,8 +5932,6 @@ void THD::set_examined_row_count(ha_rows count) void THD::inc_sent_row_count(ha_rows count) { m_sent_row_count+= count; - DBUG_EXECUTE_IF("debug_huge_number_of_examined_rows", - m_examined_row_count= (ULONGLONG_MAX - 1000000);); MYSQL_SET_STATEMENT_ROWS_SENT(m_statement_psi, m_sent_row_count); } @@ -6998,7 +6995,8 @@ THD::binlog_prepare_pending_rows_event(TABLE* table, uint32 serv_id, { DBUG_ENTER("binlog_prepare_pending_rows_event"); /* Pre-conditions */ - DBUG_ASSERT(table->s->table_map_id != ~0UL); + DBUG_ASSERT((table->s->table_map_id & MAX_TABLE_MAP_ID) != UINT32_MAX && + (table->s->table_map_id & MAX_TABLE_MAP_ID) != 0); /* Fetch the type code for the RowsEventT template parameter */ int const general_type_code= RowsEventT::TYPE_CODE; diff --git a/sql/sql_class.h b/sql/sql_class.h index beb33d83..7543df39 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -201,6 +201,7 @@ enum enum_binlog_row_image { #define OLD_MODE_UTF8_IS_UTF8MB3 (1 << 3) #define OLD_MODE_IGNORE_INDEX_ONLY_FOR_JOIN (1 << 4) #define OLD_MODE_COMPAT_5_1_CHECKSUM (1 << 5) +#define OLD_MODE_NO_NULL_COLLATION_IDS (1 << 6) extern char internal_table_name[2]; extern char empty_c_string[1]; @@ -281,9 +282,8 @@ typedef struct st_user_var_events user_var_entry *user_var_event; char *value; size_t length; - Item_result type; + const Type_handler *th; uint charset_number; - bool unsigned_flag; } BINLOG_USER_VAR_EVENT; /* @@ -657,6 +657,15 @@ enum killed_type KILL_TYPE_QUERY }; +#define SECONDS_TO_WAIT_FOR_KILL 2 +#define SECONDS_TO_WAIT_FOR_DUMP_THREAD_KILL 10 +#if !defined(_WIN32) && defined(HAVE_SELECT) +/* my_sleep() can wait for sub second times */ +#define WAIT_FOR_KILL_TRY_TIMES 20 +#else +#define WAIT_FOR_KILL_TRY_TIMES 2 +#endif + #include "sql_lex.h" /* Must be here */ class Delayed_insert; @@ -694,7 +703,6 @@ typedef struct system_variables ulonglong max_statement_time; ulonglong optimizer_switch; ulonglong optimizer_trace; - ulong optimizer_trace_max_mem_size; sql_mode_t sql_mode; ///< which non-standard SQL behaviour should be enabled sql_mode_t old_behavior; ///< which old SQL behaviour should be enabled ulonglong option_bits; ///< OPTION_xxx constants, e.g. OPTION_PROFILING @@ -758,6 +766,8 @@ typedef struct system_variables ulong optimizer_use_condition_selectivity; ulong optimizer_max_sel_arg_weight; ulong optimizer_max_sel_args; + ulong optimizer_trace_max_mem_size; + ulong optimizer_adjust_secondary_key_costs; ulong use_stat_tables; double sample_percentage; ulong histogram_size; @@ -3204,6 +3214,17 @@ public: */ Query_arena *stmt_arena; + /** + Get either call or statement arena. In case some function is called from + within a query the call arena has to be used for a memory allocation, + else use the statement arena. + */ + Query_arena *active_stmt_arena_to_use() + { + return (state == Query_arena::STMT_SP_QUERY_ARGUMENTS) ? this : + stmt_arena; + } + void *bulk_param; /* @@ -3435,7 +3456,11 @@ public: { return m_sent_row_count; } ha_rows get_examined_row_count() const - { return m_examined_row_count; } + { + DBUG_EXECUTE_IF("debug_huge_number_of_examined_rows", + return (ULONGLONG_MAX - 1000000);); + return m_examined_row_count; + } ulonglong get_affected_rows() const { return affected_rows; } @@ -4608,7 +4633,8 @@ public: The worst things that can happen is that we get a suboptimal error message. */ - killed_err= (err_info*) alloc_root(&main_mem_root, sizeof(*killed_err)); + if (!killed_err) + killed_err= (err_info*) my_malloc(PSI_INSTRUMENT_ME, sizeof(*killed_err), MYF(MY_WME)); if (likely(killed_err)) { killed_err->no= killed_errno_arg; @@ -5039,13 +5065,24 @@ public: public: /** Overloaded to guard query/query_length fields */ virtual void set_statement(Statement *stmt); - void set_command(enum enum_server_command command) + inline void set_command(enum enum_server_command command) { + DBUG_ASSERT(command != COM_SLEEP); m_command= command; #ifdef HAVE_PSI_THREAD_INTERFACE PSI_STATEMENT_CALL(set_thread_command)(m_command); #endif } + /* As sleep needs a bit of special handling, we have a special case for it */ + inline void mark_connection_idle() + { + proc_info= 0; + m_command= COM_SLEEP; +#ifdef HAVE_PSI_THREAD_INTERFACE + PSI_STATEMENT_CALL(set_thread_command)(m_command); +#endif + } + inline enum enum_server_command get_command() const { return m_command; } @@ -7118,7 +7155,7 @@ public: // this is needed for user_vars hash -class user_var_entry +class user_var_entry: public Type_handler_hybrid_field_type { CHARSET_INFO *m_charset; public: @@ -7127,8 +7164,6 @@ class user_var_entry char *value; size_t length; query_id_t update_query_id, used_query_id; - Item_result type; - bool unsigned_flag; double val_real(bool *null_value); longlong val_int(bool *null_value) const; @@ -7809,6 +7844,66 @@ public: }; +class Identifier_chain2 +{ + LEX_CSTRING m_name[2]; +public: + Identifier_chain2() + :m_name{Lex_cstring(), Lex_cstring()} + { } + Identifier_chain2(const LEX_CSTRING &a, const LEX_CSTRING &b) + :m_name{a, b} + { } + + const LEX_CSTRING& operator [] (size_t i) const + { + return m_name[i]; + } + + static Identifier_chain2 split(const LEX_CSTRING &txt) + { + DBUG_ASSERT(txt.str[txt.length] == '\0'); // Expect 0-terminated input + const char *dot= strchr(txt.str, '.'); + if (!dot) + return Identifier_chain2(Lex_cstring(), txt); + size_t length0= dot - txt.str; + Lex_cstring name0(txt.str, length0); + Lex_cstring name1(txt.str + length0 + 1, txt.length - length0 - 1); + return Identifier_chain2(name0, name1); + } + + // Export as a qualified name string: 'db.name' + size_t make_qname(char *dst, size_t dstlen, bool casedn_part1) const + { + size_t res= my_snprintf(dst, dstlen, "%.*s.%.*s", + (int) m_name[0].length, m_name[0].str, + (int) m_name[1].length, m_name[1].str); + if (casedn_part1 && dstlen > m_name[0].length) + my_casedn_str(system_charset_info, dst + m_name[0].length + 1); + return res; + } + + // Export as a qualified name string, allocate on mem_root. + LEX_CSTRING make_qname(MEM_ROOT *mem_root, bool casedn_part1) const + { + LEX_STRING dst; + /* format: [pkg + dot] + name + '\0' */ + size_t dst_size= m_name[0].length + 1 /*dot*/ + m_name[1].length + 1/*\0*/; + if (unlikely(!(dst.str= (char*) alloc_root(mem_root, dst_size)))) + return {NULL, 0}; + if (!m_name[0].length) + { + DBUG_ASSERT(!casedn_part1); // Should not be called this way + dst.length= my_snprintf(dst.str, dst_size, "%.*s", + (int) m_name[1].length, m_name[1].str); + return {dst.str, dst.length}; + } + dst.length= make_qname(dst.str, dst_size, casedn_part1); + return {dst.str, dst.length}; + } +}; + + /** This class resembles the SQL Standard schema qualified object name: <schema qualified name> ::= [ <schema name> <period> ] <qualified identifier> @@ -7849,41 +7944,16 @@ public: void copy(MEM_ROOT *mem_root, const LEX_CSTRING &db, const LEX_CSTRING &name); - static Database_qualified_name split(const LEX_CSTRING &txt) - { - DBUG_ASSERT(txt.str[txt.length] == '\0'); // Expect 0-terminated input - const char *dot= strchr(txt.str, '.'); - if (!dot) - return Database_qualified_name(NULL, 0, txt.str, txt.length); - size_t dblen= dot - txt.str; - Lex_cstring db(txt.str, dblen); - Lex_cstring name(txt.str + dblen + 1, txt.length - dblen - 1); - return Database_qualified_name(db, name); - } - // Export db and name as a qualified name string: 'db.name' - size_t make_qname(char *dst, size_t dstlen) const + size_t make_qname(char *dst, size_t dstlen, bool casedn_name) const { - return my_snprintf(dst, dstlen, "%.*s.%.*s", - (int) m_db.length, m_db.str, - (int) m_name.length, m_name.str); + return Identifier_chain2(m_db, m_name).make_qname(dst, dstlen, casedn_name); } // Export db and name as a qualified name string, allocate on mem_root. - bool make_qname(MEM_ROOT *mem_root, LEX_CSTRING *dst) const + LEX_CSTRING make_qname(MEM_ROOT *mem_root, bool casedn_name) const { - const uint dot= !!m_db.length; - char *tmp; - /* format: [database + dot] + name + '\0' */ - dst->length= m_db.length + dot + m_name.length; - if (unlikely(!(dst->str= tmp= (char*) alloc_root(mem_root, - dst->length + 1)))) - return true; - snprintf(tmp, dst->length + 1, "%.*s%.*s%.*s", - (int) m_db.length, (m_db.length ? m_db.str : ""), - dot, ".", - (int) m_name.length, m_name.str); DBUG_SLOW_ASSERT(ok_for_lower_case_names(m_db.str)); - return false; + return Identifier_chain2(m_db, m_name).make_qname(mem_root, casedn_name); } bool make_package_routine_name(MEM_ROOT *mem_root, @@ -7894,9 +7964,8 @@ public: size_t length= package.length + 1 + routine.length + 1; if (unlikely(!(tmp= (char *) alloc_root(mem_root, length)))) return true; - m_name.length= my_snprintf(tmp, length, "%.*s.%.*s", - (int) package.length, package.str, - (int) routine.length, routine.str); + m_name.length= Identifier_chain2(package, routine).make_qname(tmp, length, + false); m_name.str= tmp; return false; } @@ -7925,7 +7994,7 @@ public: { } LEX_CSTRING lex_cstring() const override { - size_t length= m_name->make_qname(err_buffer, sizeof(err_buffer)); + size_t length= m_name->make_qname(err_buffer, sizeof(err_buffer), false); return {err_buffer, length}; } }; diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc index 6b195ac9..8878c722 100644 --- a/sql/sql_connect.cc +++ b/sql/sql_connect.cc @@ -1240,8 +1240,7 @@ void prepare_new_connection_state(THD* thd) embedded server library. TODO: refactor this to avoid code duplication there */ - thd->proc_info= 0; - thd->set_command(COM_SLEEP); + thd->mark_connection_idle(); thd->init_for_queries(); if (opt_init_connect.length && diff --git a/sql/sql_cte.cc b/sql/sql_cte.cc index ad385128..5b3db9bc 100644 --- a/sql/sql_cte.cc +++ b/sql/sql_cte.cc @@ -106,6 +106,7 @@ bool LEX::check_dependencies_in_with_clauses() @param tables Points to the beginning of the sub-chain @param tables_last Points to the address with the sub-chain barrier + @param excl_spec Ignore the definition with this spec @details The method resolves tables references to CTE from the chain of @@ -147,7 +148,8 @@ bool LEX::check_dependencies_in_with_clauses() */ bool LEX::resolve_references_to_cte(TABLE_LIST *tables, - TABLE_LIST **tables_last) + TABLE_LIST **tables_last, + st_select_lex_unit *excl_spec) { With_element *with_elem= 0; @@ -156,7 +158,8 @@ bool LEX::resolve_references_to_cte(TABLE_LIST *tables, if (tbl->derived) continue; if (!tbl->db.str && !tbl->with) - tbl->with= tbl->select_lex->find_table_def_in_with_clauses(tbl); + tbl->with= tbl->select_lex->find_table_def_in_with_clauses(tbl, + excl_spec); if (!tbl->with) // no CTE matches table reference tbl { if (only_cte_resolution) @@ -244,7 +247,7 @@ LEX::check_cte_dependencies_and_resolve_references() return true; if (!with_cte_resolution) return false; - if (resolve_references_to_cte(query_tables, query_tables_last)) + if (resolve_references_to_cte(query_tables, query_tables_last, NULL)) return true; return false; } @@ -388,6 +391,7 @@ bool With_element::check_dependencies_in_spec() @param table The reference to the table that is looked for @param barrier The barrier with element for the search + @param excl_spec Ignore the definition with this spec @details The function looks through the elements of this with clause trying to find @@ -401,12 +405,15 @@ bool With_element::check_dependencies_in_spec() */ With_element *With_clause::find_table_def(TABLE_LIST *table, - With_element *barrier) + With_element *barrier, + st_select_lex_unit *excl_spec) { for (With_element *with_elem= with_list.first; with_elem != barrier; with_elem= with_elem->next) { + if (excl_spec && with_elem->spec == excl_spec) + continue; if (my_strcasecmp(system_charset_info, with_elem->get_name_str(), table->table_name.str) == 0 && !table->is_fqtn) @@ -466,7 +473,7 @@ With_element *find_table_def_in_with_clauses(TABLE_LIST *tbl, top_unit->with_element && top_unit->with_element->get_owner() == with_clause) barrier= top_unit->with_element; - found= with_clause->find_table_def(tbl, barrier); + found= with_clause->find_table_def(tbl, barrier, NULL); if (found) break; } @@ -521,10 +528,11 @@ void With_element::check_dependencies_in_select(st_select_lex *sl, { With_clause *with_clause= sl->master_unit()->with_clause; if (with_clause) - tbl->with= with_clause->find_table_def(tbl, NULL); + tbl->with= with_clause->find_table_def(tbl, NULL, NULL); if (!tbl->with) tbl->with= owner->find_table_def(tbl, - owner->with_recursive ? NULL : this); + owner->with_recursive ? NULL : this, + NULL); } if (!tbl->with) tbl->with= find_table_def_in_with_clauses(tbl, ctxt); @@ -643,6 +651,8 @@ void With_element::check_dependencies_in_unit(st_select_lex_unit *unit, { check_dependencies_in_select(sl, &unit_ctxt_elem, in_subq, dep_map); } + if ((sl= unit->fake_select_lex)) + check_dependencies_in_select(sl, &unit_ctxt_elem, in_subq, dep_map); } @@ -1099,7 +1109,8 @@ st_select_lex_unit *With_element::clone_parsed_spec(LEX *old_lex, */ lex->only_cte_resolution= old_lex->only_cte_resolution; if (lex->resolve_references_to_cte(lex->query_tables, - lex->query_tables_last)) + lex->query_tables_last, + spec)) { res= NULL; goto err; @@ -1199,7 +1210,7 @@ With_element::process_columns_of_derived_unit(THD *thd, /* Rename the columns of the first select in the unit */ while ((item= it++, name= nm++)) { - item->set_name(thd, *name); + lex_string_set(&item->name, name->str); item->base_flags|= item_base_t::IS_EXPLICIT_NAME; } @@ -1279,14 +1290,14 @@ bool With_element::prepare_unreferenced(THD *thd) sl= sl->next_select()) sl->context.outer_context= 0; + uint8 save_context_analysys_only= thd->lex->context_analysis_only; thd->lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_DERIVED; if (!spec->prepared && (spec->prepare(spec->derived, 0, 0) || process_columns_of_derived_unit(thd, spec) || check_duplicate_names(thd, first_sl->item_list, 1))) rc= true; - - thd->lex->context_analysis_only&= ~CONTEXT_ANALYSIS_ONLY_DERIVED; + thd->lex->context_analysis_only= save_context_analysys_only; return rc; } @@ -1302,6 +1313,7 @@ bool With_element::is_anchor(st_select_lex *sel) Search for the definition of the given table referred in this select node @param table reference to the table whose definition is searched for + @param excl_spec ignore the definition with this spec @details The method looks for the definition of the table whose reference is occurred @@ -1314,7 +1326,8 @@ bool With_element::is_anchor(st_select_lex *sel) NULL - otherwise */ -With_element *st_select_lex::find_table_def_in_with_clauses(TABLE_LIST *table) +With_element *st_select_lex::find_table_def_in_with_clauses(TABLE_LIST *table, + st_select_lex_unit *excl_spec) { With_element *found= NULL; With_clause *containing_with_clause= NULL; @@ -1331,7 +1344,7 @@ With_element *st_select_lex::find_table_def_in_with_clauses(TABLE_LIST *table) With_clause *attached_with_clause= sl->get_with_clause(); if (attached_with_clause && attached_with_clause != containing_with_clause && - (found= attached_with_clause->find_table_def(table, NULL))) + (found= attached_with_clause->find_table_def(table, NULL, excl_spec))) break; master_unit= sl->master_unit(); outer_sl= master_unit->outer_select(); @@ -1341,7 +1354,8 @@ With_element *st_select_lex::find_table_def_in_with_clauses(TABLE_LIST *table) containing_with_clause= with_elem->get_owner(); With_element *barrier= containing_with_clause->with_recursive ? NULL : with_elem; - if ((found= containing_with_clause->find_table_def(table, barrier))) + if ((found= containing_with_clause->find_table_def(table, barrier, + excl_spec))) break; if (outer_sl && !outer_sl->get_with_element()) break; diff --git a/sql/sql_cte.h b/sql/sql_cte.h index 6a1f67d3..1da7c6c3 100644 --- a/sql/sql_cte.h +++ b/sql/sql_cte.h @@ -325,7 +325,8 @@ public: friend bool LEX::resolve_references_to_cte(TABLE_LIST *tables, - TABLE_LIST **tables_last); + TABLE_LIST **tables_last, + st_select_lex_unit *excl_spec); }; const uint max_number_of_elements_in_with_clause= sizeof(table_map)*8; @@ -425,7 +426,8 @@ public: void move_anchors_ahead(); - With_element *find_table_def(TABLE_LIST *table, With_element *barrier); + With_element *find_table_def(TABLE_LIST *table, With_element *barrier, + st_select_lex_unit *excl_spec); With_element *find_table_def_in_with_clauses(TABLE_LIST *table); diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 90194659..202dde11 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -534,7 +534,8 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, select=make_select(table, 0, 0, conds, (SORT_INFO*) 0, 0, &error); if (unlikely(error)) DBUG_RETURN(TRUE); - if ((select && select->check_quick(thd, safe_update, limit)) || !limit) + if ((select && select->check_quick(thd, safe_update, limit, + Item_func::BITMAP_ALL)) || !limit) { query_plan.set_impossible_where(); if (thd->lex->describe || thd->lex->analyze_stmt) @@ -1268,6 +1269,13 @@ multi_delete::initialize_tables(JOIN *join) { TABLE_LIST *tbl= walk->correspondent_table->find_table_for_update(); tables_to_delete_from|= tbl->table->map; + + /* + Ensure that filesort re-reads the row from the engine before + delete is called. + */ + join->map2table[tbl->table->tablenr]->keep_current_rowid= true; + if (delete_while_scanning && unique_table(thd, tbl, join->tables_list, 0)) { diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index 4e42bcd3..afcbb7f8 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -1243,6 +1243,9 @@ bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived) goto err; JOIN *join= unit->first_select()->join; join->first_record= false; + if (join->zero_result_cause) + goto err; + for (uint i= join->top_join_tab_count; i < join->top_join_tab_count + join->aggr_tables; i++) @@ -1351,6 +1354,10 @@ bool mysql_derived_reinit(THD *thd, LEX *lex, TABLE_LIST *derived) derived->get_unit())); st_select_lex_unit *unit= derived->get_unit(); + // reset item names to that saved after wildcard expansion in JOIN::prepare + for(st_select_lex *sl= unit->first_select(); sl; sl= sl->next_select()) + sl->restore_item_list_names(); + derived->merged_for_insert= FALSE; unit->unclean(); unit->types.empty(); diff --git a/sql/sql_help.cc b/sql/sql_help.cc index f9932f11..34e77e37 100644 --- a/sql/sql_help.cc +++ b/sql/sql_help.cc @@ -667,7 +667,8 @@ SQL_SELECT *prepare_simple_select(THD *thd, Item *cond, SQL_SELECT *res= make_select(table, 0, 0, cond, 0, 0, error); if (unlikely(*error) || - (likely(res) && unlikely(res->check_quick(thd, 0, HA_POS_ERROR))) || + (likely(res) && unlikely(res->check_quick(thd, 0, HA_POS_ERROR, + Item_func::BITMAP_ALL))) || (likely(res) && res->quick && unlikely(res->quick->reset()))) { delete res; diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 26431968..97e1c102 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -83,6 +83,7 @@ #include "rpl_rli.h" #ifdef WITH_WSREP +#include "wsrep_mysqld.h" /* wsrep_append_table_keys() */ #include "wsrep_trans_observer.h" /* wsrep_start_transction() */ #endif /* WITH_WSREP */ @@ -95,7 +96,8 @@ static void end_delayed_insert(THD *thd); pthread_handler_t handle_delayed_insert(void *arg); static void unlink_blobs(TABLE *table); #endif -static bool check_view_insertability(THD *thd, TABLE_LIST *view); +static bool check_view_insertability(THD *thd, TABLE_LIST *view, + List<Item> &fields); static int binlog_show_create_table_(THD *thd, TABLE *table, Table_specification_st *create_info); @@ -310,7 +312,7 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list, if (check_key_in_view(thd, table_list) || (table_list->view && - check_view_insertability(thd, table_list))) + check_view_insertability(thd, table_list, fields))) { my_error(ER_NON_INSERTABLE_TABLE, MYF(0), table_list->alias.str, "INSERT"); DBUG_RETURN(-1); @@ -591,7 +593,8 @@ bool open_and_lock_for_insert_delayed(THD *thd, TABLE_LIST *table_list) Open tables used for sub-selects or in stored functions, will also cache these functions. */ - if (open_and_lock_tables(thd, table_list->next_global, TRUE, + if (table_list->next_global && + open_and_lock_tables(thd, table_list->next_global, TRUE, MYSQL_OPEN_IGNORE_ENGINE_STATS)) { end_delayed_insert(thd); @@ -1424,6 +1427,7 @@ abort: check_view_insertability() thd - thread handler view - reference on VIEW + fields - fields used in insert IMPLEMENTATION A view is insertable if the folloings are true: @@ -1439,7 +1443,8 @@ abort: TRUE - can't be used for insert */ -static bool check_view_insertability(THD * thd, TABLE_LIST *view) +static bool check_view_insertability(THD *thd, TABLE_LIST *view, + List<Item> &fields) { uint num= view->view->first_select_lex()->item_list.elements; TABLE *table= view->table; @@ -1450,6 +1455,8 @@ static bool check_view_insertability(THD * thd, TABLE_LIST *view) uint32 *used_fields_buff= (uint32*)thd->alloc(used_fields_buff_size); MY_BITMAP used_fields; enum_column_usage saved_column_usage= thd->column_usage; + List_iterator_fast<Item> it(fields); + Item *ex; DBUG_ENTER("check_key_in_view"); if (!used_fields_buff) @@ -1478,6 +1485,17 @@ static bool check_view_insertability(THD * thd, TABLE_LIST *view) /* simple SELECT list entry (field without expression) */ if (!(field= trans->item->field_for_view_update())) { + // Do not check fields which we are not inserting into + while((ex= it++)) + { + // The field used in the INSERT + if (ex->real_item()->field_for_view_update() == + trans->item->field_for_view_update()) + break; + } + it.rewind(); + if (!ex) + continue; thd->column_usage= saved_column_usage; DBUG_RETURN(TRUE); } @@ -1492,11 +1510,12 @@ static bool check_view_insertability(THD * thd, TABLE_LIST *view) } thd->column_usage= saved_column_usage; /* unique test */ - for (trans= trans_start; trans != trans_end; trans++) + while((ex= it++)) { /* Thanks to test above, we know that all columns are of type Item_field */ - Item_field *field= (Item_field *)trans->item; - /* check fields belong to table in which we are inserting */ + DBUG_ASSERT(ex->real_item()->field_for_view_update()->type() == + Item::FIELD_ITEM); + Item_field *field= (Item_field *)ex->real_item()->field_for_view_update(); if (field->field->table == table && bitmap_fast_test_and_set(&used_fields, field->field->field_index)) DBUG_RETURN(TRUE); @@ -1780,7 +1799,7 @@ int mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, /* Check if there is more uniq keys after field */ -static int last_uniq_key(TABLE *table,uint keynr) +static int last_uniq_key(TABLE *table, const KEY *key, uint keynr) { /* When an underlying storage engine informs that the unique key @@ -1800,7 +1819,7 @@ static int last_uniq_key(TABLE *table,uint keynr) return 0; while (++keynr < table->s->keys) - if (table->key_info[keynr].flags & HA_NOSAME) + if (key[keynr].flags & HA_NOSAME) return 0; return 1; } @@ -2115,8 +2134,27 @@ int write_record(THD *thd, TABLE *table, COPY_INFO *info, select_result *sink) tables which have ON UPDATE but have no ON DELETE triggers, we just should not expose this fact to users by invoking ON UPDATE triggers. + + Note, TABLE_SHARE and TABLE see long uniques differently: + - TABLE_SHARE sees as HA_KEY_ALG_LONG_HASH and HA_NOSAME + - TABLE sees as usual non-unique indexes */ - if (last_uniq_key(table,key_nr) && + bool is_long_unique= table->s->key_info && + table->s->key_info[key_nr].algorithm == + HA_KEY_ALG_LONG_HASH; + if ((is_long_unique ? + /* + We have a long unique. Test that there are no in-engine + uniques and the current long unique is the last long unique. + */ + !(table->key_info[0].flags & HA_NOSAME) && + last_uniq_key(table, table->s->key_info, key_nr) : + /* + We have a normal key - not a long unique. + Test is the current normal key is unique and + it is the last normal unique. + */ + last_uniq_key(table, table->key_info, key_nr)) && !table->file->referenced_by_foreign_key() && (!table->triggers || !table->triggers->has_delete_triggers())) { @@ -2721,7 +2759,7 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd) } THD_STAGE_INFO(client_thd, stage_got_handler_lock); if (client_thd->killed) - goto error; + goto error2; if (thd.killed) { /* @@ -2746,7 +2784,7 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd) my_message(thd.get_stmt_da()->sql_errno(), thd.get_stmt_da()->message(), MYF(0)); } - goto error; + goto error2; } } share= table->s; @@ -2775,11 +2813,14 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd) &record, (uint) share->reclength, &bitmap, (uint) share->column_bitmap_size*4, NullS)) - goto error; + goto error2; /* Copy the TABLE object. */ copy= new (copy_tmp) TABLE; *copy= *table; + copy->vcol_refix_list.empty(); + init_sql_alloc(key_memory_TABLE, ©->mem_root, TABLE_ALLOC_BLOCK_SIZE, 0, + MYF(MY_THREAD_SPECIFIC)); /* We don't need to change the file handler here */ /* Assign the pointers for the field pointers array and the record. */ @@ -2863,11 +2904,15 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd) bzero((char*) bitmap, share->column_bitmap_size * bitmaps_used); copy->read_set= ©->def_read_set; copy->write_set= ©->def_write_set; + move_root(client_thd->mem_root, ©->mem_root); + free_root(©->mem_root, 0); DBUG_RETURN(copy); /* Got fatal error */ error: + free_root(©->mem_root, 0); +error2: tables_in_use--; mysql_cond_signal(&cond); // Inform thread about abort DBUG_RETURN(0); @@ -5115,17 +5160,13 @@ bool select_create::send_eof() thd->wsrep_trx_id(), thd->thread_id, thd->query_id); /* - append table level exclusive key for CTAS + For CTAS, append table level exclusive key for created table + and table level shared key for selected table. */ - wsrep_key_arr_t key_arr= {0, 0}; - wsrep_prepare_keys_for_isolation(thd, - table_list->db.str, - table_list->table_name.str, - table_list, - &key_arr); - int rcode= wsrep_thd_append_key(thd, key_arr.keys, key_arr.keys_len, - WSREP_SERVICE_KEY_EXCLUSIVE); - wsrep_keys_free(&key_arr); + int rcode= wsrep_append_table_keys(thd, table_list, table_list, + WSREP_SERVICE_KEY_EXCLUSIVE); + rcode= rcode || wsrep_append_table_keys(thd, nullptr, select_tables, + WSREP_SERVICE_KEY_SHARED); if (rcode) { DBUG_PRINT("wsrep", ("row key failed: %d", rcode)); diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index d9706020..e6db1456 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1459,7 +1459,7 @@ bool is_lex_native_function(const LEX_CSTRING *name) bool is_native_function(THD *thd, const LEX_CSTRING *name) { - if (native_functions_hash.find(thd, *name)) + if (mariadb_schema.find_native_function_builder(thd, *name)) return true; if (is_lex_native_function(name)) @@ -2138,7 +2138,18 @@ int Lex_input_stream::lex_one_token(YYSTYPE *yylval, THD *thd) if (lex->parsing_options.lookup_keywords_after_qualifier) next_state= MY_LEX_IDENT_OR_KEYWORD; else - next_state= MY_LEX_IDENT_START; // Next is ident (not keyword) + { + /* + Next is: + - A qualified func with a special syntax: + mariadb_schema.REPLACE('a','b','c') + mariadb_schema.SUSTRING('a',1,2) + mariadb_schema.TRIM('a') + - Or an identifier otherwise. No keyword lookup is done, + all keywords are treated as identifiers. + */ + next_state= MY_LEX_IDENT_OR_QUALIFIED_SPECIAL_FUNC; + } if (!ident_map[(uchar) yyPeek()]) // Probably ` or " next_state= MY_LEX_START; return((int) c); @@ -2582,7 +2593,12 @@ int Lex_input_stream::lex_one_token(YYSTYPE *yylval, THD *thd) We should now be able to handle: [(global | local | session) .]variable_name */ - return scan_ident_sysvar(thd, &yylval->ident_cli); + return scan_ident_common(thd, &yylval->ident_cli, + GENERAL_KEYWORD_OR_FUNC_LPAREN); + + case MY_LEX_IDENT_OR_QUALIFIED_SPECIAL_FUNC: + return scan_ident_common(thd, &yylval->ident_cli, + QUALIFIED_SPECIAL_FUNC_LPAREN); } } } @@ -2604,7 +2620,64 @@ bool Lex_input_stream::get_7bit_or_8bit_ident(THD *thd, uchar *last_char) } -int Lex_input_stream::scan_ident_sysvar(THD *thd, Lex_ident_cli_st *str) +/* + Resolve special SQL functions that have a qualified syntax in sql_yacc.yy. + These functions are not listed in the native function registry + because of a special syntax, or a reserved keyword: + + mariadb_schema.SUBSTRING('a' FROM 1 FOR 2) -- Special syntax + mariadb_schema.TRIM(BOTH ' ' FROM 'a') -- Special syntax + mariadb_schema.REPLACE('a','b','c') -- Verb keyword +*/ + +int Lex_input_stream::find_keyword_qualified_special_func(Lex_ident_cli_st *str, + uint length) const +{ + /* + There are many other special functions, see the following grammar rules: + function_call_keyword + function_call_nonkeyword + Here we resolve only those that have a qualified syntax to handle + different behavior in different @@sql_mode settings. + + Other special functions do not work in qualified context: + SELECT mariadb_schema.year(now()); -- Function year is not defined + SELECT mariadb_schema.now(); -- Function now is not defined + + We don't resolve TRIM_ORACLE here, because it does not have + a qualified syntax yet. Search for "trim_operands" in sql_yacc.yy + to find more comments. + */ + static LEX_CSTRING funcs[]= + { + {STRING_WITH_LEN("SUBSTRING")}, + {STRING_WITH_LEN("SUBSTR")}, + {STRING_WITH_LEN("TRIM")}, + {STRING_WITH_LEN("REPLACE")} + }; + + int tokval= find_keyword(str, length, true); + if (!tokval) + return 0; + for (size_t i= 0; i < array_elements(funcs); i++) + { + CHARSET_INFO *cs= system_charset_info; + /* + Check length equality to avoid non-ASCII variants + compared as equal to ASCII variants. + */ + if (length == funcs[i].length && + !cs->coll->strnncollsp(cs, + (const uchar *) m_tok_start, length, + (const uchar *) funcs[i].str, funcs[i].length)) + return tokval; + } + return 0; +} + + +int Lex_input_stream::scan_ident_common(THD *thd, Lex_ident_cli_st *str, + Ident_mode mode) { uchar last_char; uint length; @@ -2618,10 +2691,41 @@ int Lex_input_stream::scan_ident_sysvar(THD *thd, Lex_ident_cli_st *str) next_state= MY_LEX_IDENT_SEP; if (!(length= yyLength())) return ABORT_SYM; // Names must be nonempty. - if ((tokval= find_keyword(str, length, 0))) - { - yyUnget(); // Put back 'c' - return tokval; // Was keyword + + switch (mode) { + case GENERAL_KEYWORD_OR_FUNC_LPAREN: + /* + We can come here inside a system variable after "@@", + e.g. @@global.character_set_client. + We resolve all general purpose keywords here. + + We can come here when LEX::parsing_options.lookup_keywords_after_qualifier + is true, i.e. within the "field_spec" Bison rule. + We need to resolve functions that have special rules inside sql_yacc.yy, + such as SUBSTR, REPLACE, TRIM, to make this work: + c2 varchar(4) GENERATED ALWAYS AS (mariadb_schema.substr(c1,1,4)) + */ + if ((tokval= find_keyword(str, length, last_char == '('))) + { + yyUnget(); // Put back 'c' + return tokval; // Was keyword + } + break; + case QUALIFIED_SPECIAL_FUNC_LPAREN: + /* + We come here after '.' in various contexts: + SELECT @@global.character_set_client; + SELECT t1.a FROM t1; + SELECT test.f1() FROM t1; + SELECT mariadb_schema.trim('a'); + */ + if (last_char == '(' && + (tokval= find_keyword_qualified_special_func(str, length))) + { + yyUnget(); // Put back 'c' + return tokval; // Was keyword + } + break; } yyUnget(); // ptr points now after last token char @@ -2960,6 +3064,7 @@ void st_select_lex::init_query() tvc= 0; versioned_tables= 0; pushdown_select= 0; + orig_names_of_item_list_elems= 0; } void st_select_lex::init_select() @@ -3011,6 +3116,7 @@ void st_select_lex::init_select() versioned_tables= 0; is_tvc_wrapper= false; nest_flags= 0; + orig_names_of_item_list_elems= 0; } /* @@ -3526,8 +3632,9 @@ bool st_select_lex::setup_ref_array(THD *thd, uint order_group_num) uint n_elems= get_cardinality_of_ref_ptrs_slice(order_group_num) * 5; if (!ref_pointer_array.is_null()) return false; - Item **array= static_cast<Item**>(thd->stmt_arena->alloc(sizeof(Item*) * - n_elems)); + + Item **array= static_cast<Item**>( + thd->active_stmt_arena_to_use()->alloc(sizeof(Item*) * n_elems)); if (likely(array != NULL)) ref_pointer_array= Ref_ptr_array(array, n_elems); return array == NULL; @@ -4671,18 +4778,27 @@ static void fix_prepare_info_in_table_list(THD *thd, TABLE_LIST *tbl) void st_select_lex::fix_prepare_information(THD *thd, Item **conds, Item **having_conds) { + Query_arena *active_arena= thd->active_stmt_arena_to_use(); + DBUG_ENTER("st_select_lex::fix_prepare_information"); - if (!thd->stmt_arena->is_conventional() && + + if (!active_arena->is_conventional() && !(changed_elements & TOUCHED_SEL_COND)) { Query_arena_stmt on_stmt_arena(thd); changed_elements|= TOUCHED_SEL_COND; + /* + TODO: return after MDEV-33218 fix + DBUG_ASSERT( + active_arena->is_stmt_prepare_or_first_stmt_execute() || + active_arena->state == Query_arena::STMT_SP_QUERY_ARGUMENTS); + */ if (group_list.first) { if (!group_list_ptrs) { - void *mem= thd->stmt_arena->alloc(sizeof(Group_list_ptrs)); - group_list_ptrs= new (mem) Group_list_ptrs(thd->stmt_arena->mem_root); + void *mem= active_arena->alloc(sizeof(Group_list_ptrs)); + group_list_ptrs= new (mem) Group_list_ptrs(active_arena->mem_root); } group_list_ptrs->reserve(group_list.elements); for (ORDER *order= group_list.first; order; order= order->next) @@ -7342,7 +7458,8 @@ sp_head *LEX::make_sp_head(THD *thd, const sp_name *name, name->m_name); else sp->init_sp_name(name); - sp->make_qname(sp->get_main_mem_root(), &sp->m_qname); + if (!(sp->m_qname= sp->make_qname(sp->get_main_mem_root(), true)).str) + return NULL; } sphead= sp; } @@ -9229,7 +9346,7 @@ bool LEX::call_statement_start(THD *thd, const Lex_ident_sys_st *proc) { Database_qualified_name q_db_pkg(db, pkg); - Database_qualified_name q_pkg_proc(pkg, proc); + Identifier_chain2 q_pkg_proc(*pkg, *proc); sp_name *spname; sql_command= SQLCOM_CALL; @@ -9247,7 +9364,7 @@ bool LEX::call_statement_start(THD *thd, // Concat `pkg` and `name` to `pkg.name` LEX_CSTRING pkg_dot_proc; - if (q_pkg_proc.make_qname(thd->mem_root, &pkg_dot_proc) || + if (!(pkg_dot_proc= q_pkg_proc.make_qname(thd->mem_root, false)).str || check_ident_length(&pkg_dot_proc) || !(spname= new (thd->mem_root) sp_name(db, &pkg_dot_proc, true))) return true; @@ -9313,7 +9430,8 @@ sp_package *LEX::create_package_start(THD *thd, return NULL; pkg->reset_thd_mem_root(thd); pkg->init(this); - pkg->make_qname(pkg->get_main_mem_root(), &pkg->m_qname); + if (!(pkg->m_qname= pkg->make_qname(pkg->get_main_mem_root(), true)).str) + return NULL; sphead= pkg; return pkg; } @@ -9376,6 +9494,136 @@ Item *LEX::make_item_func_sysdate(THD *thd, uint fsp) } +const Schema * +LEX::find_func_schema_by_name_or_error(const Lex_ident_sys &schema, + const Lex_ident_sys &func) +{ + Schema *res= Schema::find_by_name(schema); + if (res) + return res; + Database_qualified_name qname(schema, func); + my_error(ER_FUNCTION_NOT_DEFINED, MYF(0), ErrConvDQName(&qname).ptr()); + return NULL; +} + + +Item *LEX::make_item_func_substr(THD *thd, + const Lex_ident_cli_st &schema_name_cli, + const Lex_ident_cli_st &func_name_cli, + const Lex_substring_spec_st &spec) +{ + Lex_ident_sys schema_name(thd, &schema_name_cli); + Lex_ident_sys func_name(thd, &func_name_cli); + if (schema_name.is_null() || func_name.is_null()) + return NULL; // EOM + const Schema *schema= find_func_schema_by_name_or_error(schema_name, + func_name); + return schema ? schema->make_item_func_substr(thd, spec) : NULL; +} + + +Item *LEX::make_item_func_substr(THD *thd, + const Lex_ident_cli_st &schema_name_cli, + const Lex_ident_cli_st &func_name_cli, + List<Item> *item_list) +{ + Lex_ident_sys schema_name(thd, &schema_name_cli); + Lex_ident_sys func_name(thd, &func_name_cli); + if (schema_name.is_null() || func_name.is_null()) + return NULL; // EOM + Schema *schema; + if (item_list && + (item_list->elements == 2 || item_list->elements == 3) && + (schema= Schema::find_by_name(schema_name))) + { + Item_args args(thd, *item_list); + Lex_substring_spec_st spec= + Lex_substring_spec_st::init(args.arguments()[0], + args.arguments()[1], + item_list->elements == 3 ? + args.arguments()[2] : NULL); + return schema->make_item_func_substr(thd, spec); + } + return make_item_func_call_generic(thd, schema_name, func_name, item_list); +} + + +Item *LEX::make_item_func_replace(THD *thd, + const Lex_ident_cli_st &schema_name_cli, + const Lex_ident_cli_st &func_name_cli, + Item *org, + Item *find, + Item *replace) +{ + Lex_ident_sys schema_name(thd, &schema_name_cli); + Lex_ident_sys func_name(thd, &func_name_cli); + if (schema_name.is_null() || func_name.is_null()) + return NULL; // EOM + const Schema *schema= find_func_schema_by_name_or_error(schema_name, + func_name); + return schema ? schema->make_item_func_replace(thd, org, find, replace) : + NULL; +} + + +Item *LEX::make_item_func_replace(THD *thd, + const Lex_ident_cli_st &schema_name_cli, + const Lex_ident_cli_st &func_name_cli, + List<Item> *item_list) +{ + Lex_ident_sys schema_name(thd, &schema_name_cli); + Lex_ident_sys func_name(thd, &func_name_cli); + if (schema_name.is_null() || func_name.is_null()) + return NULL; // EOM + const Schema *schema; + if (item_list && item_list->elements == 3 && + (schema= Schema::find_by_name(schema_name))) + { + Item_args args(thd, *item_list); + return schema->make_item_func_replace(thd, args.arguments()[0], + args.arguments()[1], + args.arguments()[2]); + } + return make_item_func_call_generic(thd, schema_name, func_name, item_list); +} + + +Item *LEX::make_item_func_trim(THD *thd, + const Lex_ident_cli_st &schema_name_cli, + const Lex_ident_cli_st &func_name_cli, + const Lex_trim_st &spec) +{ + Lex_ident_sys schema_name(thd, &schema_name_cli); + Lex_ident_sys func_name(thd, &func_name_cli); + if (schema_name.is_null() || func_name.is_null()) + return NULL; // EOM + const Schema *schema= find_func_schema_by_name_or_error(schema_name, + func_name); + return schema ? schema->make_item_func_trim(thd, spec) : NULL; +} + + +Item *LEX::make_item_func_trim(THD *thd, + const Lex_ident_cli_st &schema_name_cli, + const Lex_ident_cli_st &func_name_cli, + List<Item> *item_list) +{ + Lex_ident_sys schema_name(thd, &schema_name_cli); + Lex_ident_sys func_name(thd, &func_name_cli); + if (schema_name.is_null() || func_name.is_null()) + return NULL; // EOM + const Schema *schema; + if (item_list && item_list->elements == 1 && + (schema= Schema::find_by_name(schema_name))) + { + Item_args args(thd, *item_list); + Lex_trim spec(TRIM_BOTH, args.arguments()[0]); + return schema->make_item_func_trim(thd, spec); + } + return make_item_func_call_generic(thd, schema_name, func_name, item_list); +} + + bool SELECT_LEX::vers_push_field(THD *thd, TABLE_LIST *table, const LEX_CSTRING field_name) { @@ -9454,16 +9702,10 @@ Item *Lex_trim_st::make_item_func_trim_oracle(THD *thd) const } -Item *Lex_trim_st::make_item_func_trim(THD *thd) const -{ - return (thd->variables.sql_mode & MODE_ORACLE) ? - make_item_func_trim_oracle(thd) : - make_item_func_trim_std(thd); -} - - -Item *LEX::make_item_func_call_generic(THD *thd, Lex_ident_cli_st *cdb, - Lex_ident_cli_st *cname, List<Item> *args) +Item *LEX::make_item_func_call_generic(THD *thd, + const Lex_ident_cli_st *cdb, + const Lex_ident_cli_st *cname, + List<Item> *args) { Lex_ident_sys db(thd, cdb), name(thd, cname); if (db.is_null() || name.is_null()) @@ -9490,6 +9732,19 @@ Item *LEX::make_item_func_call_generic(THD *thd, Lex_ident_cli_st *cdb, if (check_routine_name(&name)) return NULL; + return make_item_func_call_generic(thd, db, name, args); +} + + +Item *LEX::make_item_func_call_generic(THD *thd, + const Lex_ident_sys &db, + const Lex_ident_sys &name, + List<Item> *args) +{ + const Schema *schema= Schema::find_by_name(db); + if (schema) + return schema->make_item_func_call_native(thd, name, args); + Create_qfunc *builder= find_qualified_function_builder(thd); DBUG_ASSERT(builder); return builder->create_with_db(thd, &db, &name, true, args); @@ -9510,7 +9765,7 @@ Item *LEX::make_item_func_call_generic(THD *thd, static Lex_cstring dot(".", 1); Lex_ident_sys db(thd, cdb), pkg(thd, cpkg), func(thd, cfunc); Database_qualified_name q_db_pkg(db, pkg); - Database_qualified_name q_pkg_func(pkg, func); + Identifier_chain2 q_pkg_func(pkg, func); sp_name *qname; if (db.is_null() || pkg.is_null() || func.is_null()) @@ -9527,7 +9782,7 @@ Item *LEX::make_item_func_call_generic(THD *thd, // Concat `pkg` and `name` to `pkg.name` LEX_CSTRING pkg_dot_func; - if (q_pkg_func.make_qname(thd->mem_root, &pkg_dot_func) || + if (!(pkg_dot_func= q_pkg_func.make_qname(thd->mem_root, false)).str || check_ident_length(&pkg_dot_func) || !(qname= new (thd->mem_root) sp_name(&db, &pkg_dot_func, true))) return NULL; @@ -9936,8 +10191,17 @@ bool Lex_order_limit_lock::set_to(SELECT_LEX *sel) "CUBE/ROLLUP", "ORDER BY"); return TRUE; } + for (ORDER *order= order_list->first; order; order= order->next) + (*order->item)->walk(&Item::change_context_processor, FALSE, + &sel->context); sel->order_list= *(order_list); } + if (limit.select_limit) + limit.select_limit->walk(&Item::change_context_processor, FALSE, + &sel->context); + if (limit.offset_limit) + limit.offset_limit->walk(&Item::change_context_processor, FALSE, + &sel->context); sel->is_set_query_expr_tail= true; return FALSE; } @@ -11191,6 +11455,72 @@ exit: } +/** + @brief + Save the original names of items from the item list. + + @retval + true - if an error occurs + false - otherwise +*/ + +bool st_select_lex::save_item_list_names(THD *thd) +{ + if (orig_names_of_item_list_elems) + return false; + + Query_arena *arena, backup; + arena= thd->activate_stmt_arena_if_needed(&backup); + + if (unlikely(!(orig_names_of_item_list_elems= new(thd->mem_root) + List<Lex_ident_sys>))) + return true; + + List_iterator_fast<Item> li(item_list); + Item *item; + + while ((item= li++)) + { + Lex_ident_sys *name= new (thd->mem_root) Lex_ident_sys(thd, &item->name); + if (unlikely(!name || + orig_names_of_item_list_elems->push_back(name, thd->mem_root))) + { + if (arena) + thd->restore_active_arena(arena, &backup); + orig_names_of_item_list_elems= 0; + return true; + } + } + + if (arena) + thd->restore_active_arena(arena, &backup); + + return false; +} + + +/** + @brief + Restore the name of each item in the item_list of this st_select_lex + from orig_names_of_item_list_elems. +*/ + +void st_select_lex::restore_item_list_names() +{ + if (!orig_names_of_item_list_elems) + return; + + DBUG_ASSERT(item_list.elements == orig_names_of_item_list_elems->elements); + + List_iterator_fast<Lex_ident_sys> it(*orig_names_of_item_list_elems); + Lex_ident_sys *new_name; + List_iterator_fast<Item> li(item_list); + Item *item; + + while ((item= li++) && (new_name= it++)) + lex_string_set( &item->name, new_name->str); +} + bool LEX::stmt_install_plugin(const DDL_options_st &opt, const Lex_ident_sys_st &name, const LEX_CSTRING &soname) diff --git a/sql/sql_lex.h b/sql/sql_lex.h index f548fbe5..3151f237 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -146,6 +146,11 @@ public: struct Lex_ident_sys_st: public LEX_CSTRING { public: + static void *operator new(size_t size, MEM_ROOT *mem_root) throw () + { return alloc_root(mem_root, size); } + static void operator delete(void *ptr,size_t size) { TRASH_FREE(ptr, size); } + static void operator delete(void *ptr, MEM_ROOT *mem_root) {} + bool copy_ident_cli(THD *thd, const Lex_ident_cli_st *str); bool copy_keyword(THD *thd, const Lex_ident_cli_st *str); bool copy_sys(THD *thd, const LEX_CSTRING *str); @@ -179,6 +184,10 @@ public: LEX_CSTRING tmp= {name, length}; set_valid_utf8(&tmp); } + Lex_ident_sys(THD *thd, const LEX_CSTRING *str) + { + set_valid_utf8(str); + } Lex_ident_sys & operator=(const Lex_ident_sys_st &name) { Lex_ident_sys_st::operator=(name); @@ -1221,6 +1230,11 @@ public: List<Field_pair> grouping_tmp_fields; List<udf_func> udf_list; /* udf function calls stack */ List<Index_hint> *index_hints; /* list of USE/FORCE/IGNORE INDEX */ + /* + This list is used to restore the names of items + from item_list after each execution of the statement. + */ + List<Lex_ident_sys> *orig_names_of_item_list_elems; List<List_item> save_many_values; List<Item> *save_insert_list; @@ -1438,6 +1452,9 @@ public: bool straight_fl); TABLE_LIST *convert_right_join(); List<Item>* get_item_list(); + bool save_item_list_names(THD *thd); + void restore_item_list_names(); + ulong get_table_join_options(); void set_lock_for_tables(thr_lock_type lock_type, bool for_update, bool skip_locks); @@ -1577,7 +1594,8 @@ public: master_unit()->cloned_from->with_element : master_unit()->with_element; } - With_element *find_table_def_in_with_clauses(TABLE_LIST *table); + With_element *find_table_def_in_with_clauses(TABLE_LIST *table, + st_select_lex_unit * excl_spec); bool check_unrestricted_recursive(bool only_standard_compliant); bool check_subqueries_with_recursive_references(); void collect_grouping_fields_for_derived(THD *thd, ORDER *grouping_list); @@ -2461,6 +2479,15 @@ public: void reduce_digest_token(uint token_left, uint token_right); private: + + enum Ident_mode + { + GENERAL_KEYWORD_OR_FUNC_LPAREN, + QUALIFIED_SPECIAL_FUNC_LPAREN + }; + + int scan_ident_common(THD *thd, Lex_ident_cli_st *str, Ident_mode mode); + /** Set the echo mode. @@ -2781,8 +2808,8 @@ private: bool consume_comment(int remaining_recursions_permitted); int lex_one_token(union YYSTYPE *yylval, THD *thd); int find_keyword(Lex_ident_cli_st *str, uint len, bool function) const; + int find_keyword_qualified_special_func(Lex_ident_cli_st *str, uint len) const; LEX_CSTRING get_token(uint skip, uint length); - int scan_ident_sysvar(THD *thd, Lex_ident_cli_st *str); int scan_ident_start(THD *thd, Lex_ident_cli_st *str); int scan_ident_middle(THD *thd, Lex_ident_cli_st *str, CHARSET_INFO **cs, my_lex_states *); @@ -4128,8 +4155,42 @@ public: Item *create_item_query_expression(THD *thd, st_select_lex_unit *unit); Item *make_item_func_sysdate(THD *thd, uint fsp); - Item *make_item_func_call_generic(THD *thd, Lex_ident_cli_st *db, - Lex_ident_cli_st *name, List<Item> *args); + + static const Schema * + find_func_schema_by_name_or_error(const Lex_ident_sys &schema_name, + const Lex_ident_sys &func_name); + Item *make_item_func_replace(THD *thd, + const Lex_ident_cli_st &schema_name, + const Lex_ident_cli_st &func_name, + Item *org, Item *find, Item *replace); + Item *make_item_func_replace(THD *thd, + const Lex_ident_cli_st &schema_name, + const Lex_ident_cli_st &func_name, + List<Item> *args); + Item *make_item_func_substr(THD *thd, + const Lex_ident_cli_st &schema_name, + const Lex_ident_cli_st &func_name, + const Lex_substring_spec_st &spec); + Item *make_item_func_substr(THD *thd, + const Lex_ident_cli_st &schema_name, + const Lex_ident_cli_st &func_name, + List<Item> *args); + Item *make_item_func_trim(THD *thd, + const Lex_ident_cli_st &schema_name, + const Lex_ident_cli_st &func_name, + const Lex_trim_st &spec); + Item *make_item_func_trim(THD *thd, + const Lex_ident_cli_st &schema_name, + const Lex_ident_cli_st &func_name, + List<Item> *args); + Item *make_item_func_call_generic(THD *thd, + const Lex_ident_cli_st *db, + const Lex_ident_cli_st *name, + List<Item> *args); + Item *make_item_func_call_generic(THD *thd, + const Lex_ident_sys &db, + const Lex_ident_sys &name, + List<Item> *args); Item *make_item_func_call_generic(THD *thd, Lex_ident_cli_st *db, Lex_ident_cli_st *pkg, @@ -4830,7 +4891,8 @@ public: bool check_dependencies_in_with_clauses(); bool check_cte_dependencies_and_resolve_references(); bool resolve_references_to_cte(TABLE_LIST *tables, - TABLE_LIST **tables_last); + TABLE_LIST **tables_last, + st_select_lex_unit *excl_spec); /** Turn on the SELECT_DESCRIBE flag for every SELECT_LEX involved into diff --git a/sql/sql_list.h b/sql/sql_list.h index 5a57c86e..faec566c 100644 --- a/sql/sql_list.h +++ b/sql/sql_list.h @@ -799,7 +799,9 @@ public: class base_ilist_iterator { base_ilist *list; - struct ilink **el,*current; + struct ilink **el; +protected: + struct ilink *current; public: base_ilist_iterator(base_ilist &list_par) :list(&list_par), el(&list_par.first),current(0) {} @@ -811,6 +813,13 @@ public: el= ¤t->next; return current; } + /* Unlink element returned by last next() call */ + inline void unlink(void) + { + struct ilink **tmp= current->prev; + current->unlink(); + el= tmp; + } }; @@ -840,6 +849,13 @@ template <class T> class I_List_iterator :public base_ilist_iterator public: I_List_iterator(I_List<T> &a) : base_ilist_iterator(a) {} inline T* operator++(int) { return (T*) base_ilist_iterator::next(); } + /* Remove element returned by last next() call */ + inline void remove(void) + { + unlink(); + delete (T*) current; + current= 0; // Safety + } }; /** diff --git a/sql/sql_load.cc b/sql/sql_load.cc index 8aa1452c..be3063e5 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -255,6 +255,10 @@ public: */ void skip_data_till_eof() { +#ifndef EMBEDDED_LIBRARY + if (mysql_bin_log.is_open()) + cache.read_function= cache.real_read_function; +#endif while (GET != my_b_EOF) ; } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 1817f811..f9da2c40 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2124,7 +2124,6 @@ dispatch_command_return dispatch_command(enum enum_server_command command, THD * { ulong pos; ushort flags; - uint32 slave_server_id; status_var_increment(thd->status_var.com_other); @@ -2135,10 +2134,26 @@ dispatch_command_return dispatch_command(enum enum_server_command command, THD * /* TODO: The following has to be changed to an 8 byte integer */ pos = uint4korr(packet); flags = uint2korr(packet + 4); - thd->variables.server_id=0; /* avoid suicide */ - if ((slave_server_id= uint4korr(packet+6))) // mysqlbinlog.server_id==0 - kill_zombie_dump_threads(slave_server_id); - thd->variables.server_id = slave_server_id; + if ((thd->variables.server_id= uint4korr(packet+6))) + { + bool got_error; + + got_error= kill_zombie_dump_threads(thd, + thd->variables.server_id); + if (got_error || thd->killed) + { + if (!thd->killed) + my_printf_error(ER_MASTER_FATAL_ERROR_READING_BINLOG, + "Could not start dump thread for slave: %u as " + "it has already a running dump thread", + MYF(0), (uint) thd->variables.server_id); + else if (! thd->get_stmt_da()->is_set()) + thd->send_kill_message(); + error= TRUE; + thd->unregister_slave(); // todo: can be extraneous + break; + } + } const char *name= packet + 10; size_t nlen= strlen(name); @@ -2146,6 +2161,8 @@ dispatch_command_return dispatch_command(enum enum_server_command command, THD * general_log_print(thd, command, "Log: '%s' Pos: %lu", name, pos); if (nlen < FN_REFLEN) mysql_binlog_send(thd, thd->strmake(name, nlen), (my_off_t)pos, flags); + if (thd->killed && ! thd->get_stmt_da()->is_set()) + thd->send_kill_message(); thd->unregister_slave(); // todo: can be extraneous /* fake COM_QUIT -- if we get here, the thread needs to terminate */ error = TRUE; @@ -2435,7 +2452,7 @@ resume: /* Performance Schema Interface instrumentation, end */ MYSQL_END_STATEMENT(thd->m_statement_psi, thd->get_stmt_da()); thd->set_examined_row_count(0); // For processlist - thd->set_command(COM_SLEEP); + thd->mark_connection_idle(); thd->m_statement_psi= NULL; thd->m_digest= NULL; @@ -2449,6 +2466,8 @@ resume: */ thd->lex->m_sql_cmd= NULL; free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC)); + DBUG_EXECUTE_IF("print_allocated_thread_memory", + SAFEMALLOC_REPORT_MEMORY(sf_malloc_dbug_id());); #if defined(ENABLED_PROFILING) thd->profiling.finish_current_query(); @@ -5173,9 +5192,55 @@ mysql_execute_command(THD *thd, bool is_called_from_prepared_stmt) my_ok(thd); break; case SQLCOM_BACKUP_LOCK: - if (check_global_access(thd, RELOAD_ACL)) - goto error; - /* first table is set for lock. For unlock the list is empty */ + if (check_global_access(thd, RELOAD_ACL, true)) + { +#ifndef NO_EMBEDDED_ACCESS_CHECKS + /* + In case there is no global privilege, check DB privilege for LOCK TABLES. + */ + if (first_table) // BACKUP LOCK + { + if (check_single_table_access(thd, LOCK_TABLES_ACL, first_table, true)) + { + char command[30]; + get_privilege_desc(command, sizeof(command), RELOAD_ACL|LOCK_TABLES_ACL); + my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), command); + goto error; + } + } + else // BACKUP UNLOCK + { + /* + We test mdl_backup_lock here because, if a user could obtain a lock + it would be silly to error and say `you can't BACKUP UNLOCK` + (because its obvious you did a `BACKUP LOCK`). + As `BACKUP UNLOCK` doesn't have a database reference, + there's no way we can check if the `BACKUP LOCK` privilege is missing. + Testing `thd->db` would involve faking a `TABLE_LIST` structure, + which because of the depth of inspection + in `check_single_table_access` makes the faking likely to cause crashes, + or unintended effects. The outcome of this is, + if a user does an `BACKUP UNLOCK` without a `BACKUP LOCKED` table, + there may be a` ER_SPECIFIC_ACCESS_DENIED` error even though + user has the privilege. + Its a bit different to what happens if the user has RELOAD_ACL, + where the error is silently ignored. + */ + if (!thd->mdl_backup_lock) + { + + char command[30]; + get_privilege_desc(command, sizeof(command), RELOAD_ACL|LOCK_TABLES_ACL); + my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), command); + goto error; + } + } +#endif + } + /* + There is reload privilege, first table is set for lock. + For unlock the list is empty + */ if (first_table) res= backup_lock(thd, first_table); else @@ -7931,6 +7996,7 @@ static bool wsrep_mysql_parse(THD *thd, char *rawbuf, uint length, thd->wsrep_retry_query = NULL; thd->wsrep_retry_query_len = 0; thd->wsrep_retry_command = COM_CONNECT; + thd->proc_info= 0; } return false; } @@ -9285,11 +9351,13 @@ kill_one_thread(THD *thd, my_thread_id id, killed_state kill_signal, killed_type struct kill_threads_callback_arg { - kill_threads_callback_arg(THD *thd_arg, LEX_USER *user_arg): - thd(thd_arg), user(user_arg) {} + kill_threads_callback_arg(THD *thd_arg, LEX_USER *user_arg, + killed_state kill_signal_arg): + thd(thd_arg), user(user_arg), kill_signal(kill_signal_arg), counter(0) {} THD *thd; LEX_USER *user; - List<THD> threads_to_kill; + killed_state kill_signal; + uint counter; }; @@ -9312,11 +9380,12 @@ static my_bool kill_threads_callback(THD *thd, kill_threads_callback_arg *arg) { return MY_TEST(arg->thd->security_ctx->master_access & PROCESS_ACL); } - if (!arg->threads_to_kill.push_back(thd, arg->thd->mem_root)) - { - mysql_mutex_lock(&thd->LOCK_thd_kill); // Lock from delete - mysql_mutex_lock(&thd->LOCK_thd_data); - } + arg->counter++; + mysql_mutex_lock(&thd->LOCK_thd_kill); // Lock from delete + mysql_mutex_lock(&thd->LOCK_thd_data); + thd->awake_no_mutex(arg->kill_signal); + mysql_mutex_unlock(&thd->LOCK_thd_data); + mysql_mutex_unlock(&thd->LOCK_thd_kill); } } return 0; @@ -9326,42 +9395,17 @@ static my_bool kill_threads_callback(THD *thd, kill_threads_callback_arg *arg) static uint kill_threads_for_user(THD *thd, LEX_USER *user, killed_state kill_signal, ha_rows *rows) { - kill_threads_callback_arg arg(thd, user); + kill_threads_callback_arg arg(thd, user, kill_signal); DBUG_ENTER("kill_threads_for_user"); - - *rows= 0; - - if (unlikely(thd->is_fatal_error)) // If we run out of memory - DBUG_RETURN(ER_OUT_OF_RESOURCES); - DBUG_PRINT("enter", ("user: %s signal: %u", user->user.str, (uint) kill_signal)); + *rows= 0; + if (server_threads.iterate(kill_threads_callback, &arg)) DBUG_RETURN(ER_KILL_DENIED_ERROR); - if (!arg.threads_to_kill.is_empty()) - { - List_iterator_fast<THD> it2(arg.threads_to_kill); - THD *next_ptr; - THD *ptr= it2++; - do - { - ptr->awake_no_mutex(kill_signal); - /* - Careful here: The list nodes are allocated on the memroots of the - THDs to be awakened. - But those THDs may be terminated and deleted as soon as we release - LOCK_thd_kill, which will make the list nodes invalid. - Since the operation "it++" dereferences the "next" pointer of the - previous list node, we need to do this while holding LOCK_thd_kill. - */ - next_ptr= it2++; - mysql_mutex_unlock(&ptr->LOCK_thd_kill); - mysql_mutex_unlock(&ptr->LOCK_thd_data); - (*rows)++; - } while ((ptr= next_ptr)); - } + *rows= arg.counter; DBUG_RETURN(0); } diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index 35767307..c968637d 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -1688,7 +1688,6 @@ int plugin_init(int *argc, char **argv, int flags) } } - free_root(&tmp_root, MYF(MY_MARK_BLOCKS_FREE)); tmp.state= PLUGIN_IS_UNINITIALIZED; if (register_builtin(plugin, &tmp, &plugin_ptr)) goto err_unlock; @@ -1967,7 +1966,7 @@ static void plugin_load(MEM_ROOT *tmp_root) the mutex here to satisfy the assert */ mysql_mutex_lock(&LOCK_plugin); - plugin_add(tmp_root, false, &name, &dl, MYF(ME_ERROR_LOG)); + plugin_add(tmp_root, true, &name, &dl, MYF(ME_ERROR_LOG)); free_root(tmp_root, MYF(MY_MARK_BLOCKS_FREE)); mysql_mutex_unlock(&LOCK_plugin); } diff --git a/sql/sql_plugin_services.inl b/sql/sql_plugin_services.inl index f2b2d08d..5ac06e76 100644 --- a/sql/sql_plugin_services.inl +++ b/sql/sql_plugin_services.inl @@ -183,7 +183,8 @@ static struct wsrep_service_st wsrep_handler = { wsrep_report_bf_lock_wait, wsrep_thd_kill_LOCK, wsrep_thd_kill_UNLOCK, - wsrep_thd_set_PA_unsafe + wsrep_thd_set_PA_unsafe, + wsrep_get_domain_id }; static struct thd_specifics_service_st thd_specifics_handler= @@ -228,11 +229,6 @@ static struct json_service_st json_handler= json_unescape_json }; -static struct thd_mdl_service_st thd_mdl_handler= -{ - thd_mdl_context -}; - struct sql_service_st sql_service_handler= { mysql_init, @@ -251,7 +247,13 @@ struct sql_service_st sql_service_handler= mysql_fetch_lengths, mysql_set_character_set, mysql_num_fields, - mysql_select_db + mysql_select_db, + mysql_ssl_set +}; + +static struct thd_mdl_service_st thd_mdl_handler= +{ + thd_mdl_context }; #define DEFINE_warning_function(name, ret) { \ @@ -348,8 +350,8 @@ static struct st_service_ref list_of_services[]= { "thd_wait_service", VERSION_thd_wait, &thd_wait_handler }, { "wsrep_service", VERSION_wsrep, &wsrep_handler }, { "json_service", VERSION_json, &json_handler }, - { "thd_mdl_service", VERSION_thd_mdl, &thd_mdl_handler }, { "sql_service", VERSION_sql_service, &sql_service_handler }, + { "thd_mdl_service", VERSION_thd_mdl, &thd_mdl_handler }, { "provider_service_bzip2", VERSION_provider_bzip2, &provider_handler_bzip2 }, { "provider_service_lz4", VERSION_provider_lz4, &provider_handler_lz4 }, { "provider_service_lzma", VERSION_provider_lzma, &provider_handler_lzma }, diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index bf3c7cbb..df200a69 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -132,6 +132,7 @@ static const uint PARAMETER_FLAG_UNSIGNED= 128U << 8; #include "wsrep_mysqld.h" #include "wsrep_trans_observer.h" #endif /* WITH_WSREP */ +#include "sql_audit.h" // mysql_audit_release #include "xa.h" // xa_recover_get_fields #include "sql_audit.h" // mysql_audit_release @@ -181,7 +182,7 @@ public: /* The following data member is wholly for debugging purpose. It can be used for possible crash analysis to determine how many times - the stored routine was executed before the mem_root marked read_only + the stored routine was executed before the mem_root marked ROOT_FLAG_READ_ONLY was requested for a memory chunk. Additionally, a value of this data member is output to the log with DBUG_PRINT. */ @@ -265,7 +266,6 @@ private: class Ed_connection; - /****************************************************************************** Implementation ******************************************************************************/ @@ -4068,6 +4068,7 @@ static bool execute_server_code(THD *thd, const char *sql_text, size_t sql_len) { PSI_statement_locker *parent_locker; + Reprepare_observer *reprepare_observer; bool error; query_id_t save_query_id= thd->query_id; query_id_t next_id= next_query_id(); @@ -4092,28 +4093,33 @@ static bool execute_server_code(THD *thd, parent_locker= thd->m_statement_psi; thd->m_statement_psi= NULL; + reprepare_observer= thd->m_reprepare_observer; + thd->m_reprepare_observer= NULL; error= mysql_execute_command(thd); thd->m_statement_psi= parent_locker; + thd->m_reprepare_observer= reprepare_observer; /* report error issued during command execution */ if (likely(error == 0) && thd->spcont == NULL) - general_log_write(thd, COM_QUERY, - thd->query(), thd->query_length()); + general_log_write(thd, COM_QUERY, thd->query(), thd->query_length()); end: thd->lex->restore_set_statement_var(); thd->query_id= save_query_id; delete_explain_query(thd->lex); + lex_end(thd->lex); return error; } + bool Execute_sql_statement::execute_server_code(THD *thd) { return ::execute_server_code(thd, m_sql_text.str, m_sql_text.length); } + /*************************************************************************** Prepared_statement ****************************************************************************/ @@ -4685,7 +4691,7 @@ reexecute: #ifdef PROTECT_STATEMENT_MEMROOT // There was reprepare so the counter of runs should be reset executed_counter= 0; - mem_root->read_only= 0; + mem_root->flags &= ~ROOT_FLAG_READ_ONLY; #endif goto reexecute; } @@ -4694,7 +4700,7 @@ reexecute: #ifdef PROTECT_STATEMENT_MEMROOT if (!error) { - mem_root->read_only= 1; + mem_root->flags |= ROOT_FLAG_READ_ONLY; ++executed_counter; DBUG_PRINT("info", ("execute counter: %lu", executed_counter)); @@ -4703,7 +4709,7 @@ reexecute: { // Error on call shouldn't be counted as a normal run executed_counter= 0; - mem_root->read_only= 0; + mem_root->flags &= ~ROOT_FLAG_READ_ONLY; } #endif @@ -6081,7 +6087,8 @@ bool Protocol_local::send_result_set_metadata(List<Item> *list, uint flags) for (uint pos= 0 ; (item= it++); pos++) { - if (store_item_metadata(thd, item, pos)) + Send_field sf(thd, item); + if (store_field_metadata(thd, sf, item->charset_for_protocol(), pos)) goto err; } @@ -6473,10 +6480,9 @@ extern "C" MYSQL *mysql_real_connect_local(MYSQL *mysql) new_thd->security_ctx->skip_grants(); new_thd->query_cache_is_applicable= 0; new_thd->variables.wsrep_on= 0; + new_thd->client_capabilities= client_flag; new_thd->variables.sql_log_bin= 0; new_thd->set_binlog_bit(); - new_thd->client_capabilities= client_flag; - /* TOSO: decide if we should turn the auditing off for such threads. @@ -6507,4 +6513,3 @@ extern "C" MYSQL *mysql_real_connect_local(MYSQL *mysql) DBUG_PRINT("exit",("Mysql handler: %p", mysql)); DBUG_RETURN(mysql); } - diff --git a/sql/sql_profile.cc b/sql/sql_profile.cc index f576e693..863f0369 100644 --- a/sql/sql_profile.cc +++ b/sql/sql_profile.cc @@ -202,7 +202,8 @@ void PROF_MEASUREMENT::set_label(const char *status_arg, allocated_status_memory= (char *) my_malloc(key_memory_PROFILE, sizes[0] + sizes[1] + sizes[2], MYF(0)); - DBUG_ASSERT(allocated_status_memory != NULL); + if (!allocated_status_memory) + return; cursor= allocated_status_memory; @@ -266,6 +267,8 @@ QUERY_PROFILE::QUERY_PROFILE(PROFILING *profiling_arg, const char *status_arg) { m_seq_counter= 1; PROF_MEASUREMENT *prof= new PROF_MEASUREMENT(this, status_arg); + if (!prof) + return; prof->m_seq= m_seq_counter++; m_start_time_usecs= prof->time_usecs; m_end_time_usecs= m_start_time_usecs; @@ -307,6 +310,8 @@ void QUERY_PROFILE::new_status(const char *status_arg, prof= new PROF_MEASUREMENT(this, status_arg, function_arg, base_name(file_arg), line_arg); else prof= new PROF_MEASUREMENT(this, status_arg); + if (!prof) + DBUG_VOID_RETURN; prof->m_seq= m_seq_counter++; m_end_time_usecs= prof->time_usecs; diff --git a/sql/sql_profile.h b/sql/sql_profile.h index 88136559..c225f617 100644 --- a/sql/sql_profile.h +++ b/sql/sql_profile.h @@ -103,6 +103,8 @@ public: new_item= (struct queue_item *) my_malloc(key_memory_queue_item, sizeof(struct queue_item), MYF(0)); + if (!new_item) + return; new_item->payload= payload; @@ -296,7 +298,11 @@ public: { DBUG_ASSERT(!current); if (unlikely(enabled)) - current= new QUERY_PROFILE(this, initial_state); + { + QUERY_PROFILE *new_profile= new QUERY_PROFILE(this, initial_state); + if (new_profile) + current= new_profile; + } } void discard_current_query(); diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 8bde0f3b..ad71bf6f 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -510,7 +510,7 @@ static enum enum_binlog_checksum_alg get_binlog_checksum_value_at_connect(THD * } else { - DBUG_ASSERT(entry->type == STRING_RESULT); + DBUG_ASSERT(entry->type_handler()->result_type() == STRING_RESULT); String str; uint dummy_errors; str.copy(entry->value, entry->length, &my_charset_bin, &my_charset_bin, @@ -2060,7 +2060,7 @@ send_event_to_slave(binlog_send_info *info, Log_event_type event_type, } if (need_sync && repl_semisync_master.flush_net(info->thd, - packet->c_ptr_safe())) + packet->c_ptr())) { info->error= ER_UNKNOWN_ERROR; return "Failed to run hook 'after_send_event'"; @@ -3011,8 +3011,13 @@ err: if (info->thd->killed == KILL_SLAVE_SAME_ID) { - info->errmsg= "A slave with the same server_uuid/server_id as this slave " - "has connected to the master"; + /* + Note that the text is limited to 64 characters in errmsg-utf8 in + ER_ABORTING_CONNECTION. + */ + info->errmsg= + "A slave with the same server_uuid/server_id is already " + "connected"; info->error= ER_SLAVE_SAME_ID; } @@ -3385,6 +3390,7 @@ int stop_slave(THD* thd, Master_info* mi, bool net_report ) @retval 0 success @retval 1 error */ + int reset_slave(THD *thd, Master_info* mi) { MY_STAT stat_area; @@ -3482,8 +3488,6 @@ int reset_slave(THD *thd, Master_info* mi) else if (global_system_variables.log_warnings > 1) sql_print_information("Deleted Master_info file '%s'.", fname); - if (rpl_semi_sync_slave_enabled) - repl_semisync_slave.reset_slave(mi); err: mi->unlock_slave_threads(); if (unlikely(error)) @@ -3511,43 +3515,89 @@ err: struct kill_callback_arg { - kill_callback_arg(uint32 id): slave_server_id(id), thd(0) {} - uint32 slave_server_id; + kill_callback_arg(THD *thd_arg, uint32 id): + thd(thd_arg), slave_server_id(id), counter(0) {} THD *thd; + uint32 slave_server_id; + uint counter; }; -static my_bool kill_callback(THD *thd, kill_callback_arg *arg) + +/* + Collect all active dump threads +*/ + +static my_bool kill_callback_collect(THD *thd, kill_callback_arg *arg) { if (thd->get_command() == COM_BINLOG_DUMP && - thd->variables.server_id == arg->slave_server_id) + thd->variables.server_id == arg->slave_server_id && + thd != arg->thd) { - arg->thd= thd; + arg->counter++; mysql_mutex_lock(&thd->LOCK_thd_kill); // Lock from delete mysql_mutex_lock(&thd->LOCK_thd_data); - return 1; + thd->awake_no_mutex(KILL_SLAVE_SAME_ID); // Mark killed + /* + Remover the thread from ack_receiver to ensure it is not + sending acks to the master anymore. + */ + ack_receiver.remove_slave(thd); + + mysql_mutex_unlock(&thd->LOCK_thd_data); + mysql_mutex_unlock(&thd->LOCK_thd_kill); } return 0; } -void kill_zombie_dump_threads(uint32 slave_server_id) +/* + Check if there are any active dump threads +*/ + +static my_bool kill_callback_check(THD *thd, kill_callback_arg *arg) +{ + return (thd->get_command() == COM_BINLOG_DUMP && + thd->variables.server_id == arg->slave_server_id && + thd != arg->thd); +} + + +/** + Try to kill running dump threads on the master + + @result 0 ok + @result 1 old slave thread exists and does not want to die + + There should not be more than one dump thread with the same server id + this code has however in the past has several issues. To ensure that + things works in all cases (now and in the future), this code is collecting + all matching server id's and killing all of them. +*/ + +bool kill_zombie_dump_threads(THD *thd, uint32 slave_server_id) { - kill_callback_arg arg(slave_server_id); - server_threads.iterate(kill_callback, &arg); + kill_callback_arg arg(thd, slave_server_id); + server_threads.iterate(kill_callback_collect, &arg); + + if (!arg.counter) + return 0; - if (arg.thd) + /* + Wait up to SECONDS_TO_WAIT_FOR_DUMP_THREAD_KILL for kill + of all dump thread, trying every 1/10 of second. + */ + for (uint i= 10 * SECONDS_TO_WAIT_FOR_DUMP_THREAD_KILL ; + --i > 0 && !thd->killed; + i++) { - /* - Here we do not call kill_one_thread() as - it will be slow because it will iterate through the list - again. We just to do kill the thread ourselves. - */ - arg.thd->awake_no_mutex(KILL_SLAVE_SAME_ID); - mysql_mutex_unlock(&arg.thd->LOCK_thd_kill); - mysql_mutex_unlock(&arg.thd->LOCK_thd_data); + if (!server_threads.iterate(kill_callback_check, &arg)) + return 0; // All dump thread are killed + my_sleep(1000000L / 10); // Wait 1/10 of a second } + return 1; } + /** Get value for a string parameter with error checking @@ -4303,11 +4353,17 @@ bool mysql_show_binlog_events(THD* thd) } } + /* + Omit error messages from server log in Log_event::read_log_event. That + is, we only need to notify the client to correct their 'from' offset; + writing about this in the server log would be confusing as it isn't + related to server operational status. + */ for (event_count = 0; (ev = Log_event::read_log_event(&log, description_event, (opt_master_verify_checksum || - verify_checksum_once))); ) + verify_checksum_once), false)); ) { if (!unit->lim.check_offset(event_count) && ev->net_send(protocol, linfo.log_file_name, pos)) @@ -4595,6 +4651,10 @@ int log_loaded_block(IO_CACHE* file, uchar *Buffer, size_t Count) /* buffer contains position where we started last read */ uchar* buffer= (uchar*) my_b_get_buffer_start(file); uint max_event_size= lf_info->thd->variables.max_allowed_packet; + int res; +#ifndef DBUG_OFF + bool did_dbug_inject= false; +#endif if (lf_info->thd->is_current_stmt_binlog_format_row()) goto ret; @@ -4602,6 +4662,19 @@ int log_loaded_block(IO_CACHE* file, uchar *Buffer, size_t Count) lf_info->last_pos_in_file >= my_b_get_pos_in_file(file)) goto ret; + DBUG_EXECUTE_IF("load_data_binlog_cache_error", + { + /* + Simulate "disk full" error in the middle of writing to + the binlog cache. + */ + if (lf_info->last_pos_in_file >= 2*4096) + { + DBUG_SET("+d,simulate_file_write_error"); + did_dbug_inject= true; + } + };); + for (block_len= (uint) (my_b_get_bytes_in_buffer(file)); block_len > 0; buffer += MY_MIN(block_len, max_event_size), block_len -= MY_MIN(block_len, max_event_size)) @@ -4613,7 +4686,10 @@ int log_loaded_block(IO_CACHE* file, uchar *Buffer, size_t Count) MY_MIN(block_len, max_event_size), lf_info->log_delayed); if (mysql_bin_log.write(&a)) - DBUG_RETURN(1); + { + res= 1; + goto err; + } } else { @@ -4622,12 +4698,20 @@ int log_loaded_block(IO_CACHE* file, uchar *Buffer, size_t Count) MY_MIN(block_len, max_event_size), lf_info->log_delayed); if (mysql_bin_log.write(&b)) - DBUG_RETURN(1); + { + res= 1; + goto err; + } lf_info->wrote_create_file= 1; } } ret: - int res= Buffer ? lf_info->real_read_function(file, Buffer, Count) : 0; + res= Buffer ? lf_info->real_read_function(file, Buffer, Count) : 0; +err: +#ifndef DBUG_OFF + if (did_dbug_inject) + DBUG_SET("-d,simulate_file_write_error"); +#endif DBUG_RETURN(res); } diff --git a/sql/sql_repl.h b/sql/sql_repl.h index 95916e31..3be1e18c 100644 --- a/sql/sql_repl.h +++ b/sql/sql_repl.h @@ -43,7 +43,7 @@ void adjust_linfo_offsets(my_off_t purge_offset); void show_binlogs_get_fields(THD *thd, List<Item> *field_list); bool show_binlogs(THD* thd); extern int init_master_info(Master_info* mi); -void kill_zombie_dump_threads(uint32 slave_server_id); +bool kill_zombie_dump_threads(THD *thd, uint32 slave_server_id); int check_binlog_magic(IO_CACHE* log, const char** errmsg); int compare_log_name(const char *log_1, const char *log_2); diff --git a/sql/sql_schema.cc b/sql/sql_schema.cc index f08204d2..7a6c0c99 100644 --- a/sql/sql_schema.cc +++ b/sql/sql_schema.cc @@ -33,6 +33,12 @@ public: return src; } + Create_func *find_native_function_builder(THD *thd, const LEX_CSTRING &name) + const + { + return native_functions_hash_oracle.find(thd, name); + } + Item *make_item_func_replace(THD *thd, Item *subj, Item *find, @@ -64,6 +70,7 @@ Schema mariadb_schema(Lex_cstring(STRING_WITH_LEN("mariadb_schema"))); Schema_oracle oracle_schema(Lex_cstring(STRING_WITH_LEN("oracle_schema"))); Schema_maxdb maxdb_schema(Lex_cstring(STRING_WITH_LEN("maxdb_schema"))); +const Schema &oracle_schema_ref= oracle_schema; Schema *Schema::find_by_name(const LEX_CSTRING &name) { @@ -88,6 +95,26 @@ Schema *Schema::find_implied(THD *thd) } +Create_func * +Schema::find_native_function_builder(THD *thd, const LEX_CSTRING &name) const +{ + return native_functions_hash.find(thd, name); +} + + +Item *Schema::make_item_func_call_native(THD *thd, + const Lex_ident_sys &name, + List<Item> *args) const +{ + Create_func *builder= find_native_function_builder(thd, name); + if (builder) + return builder->create_func(thd, &name, args); + my_error(ER_FUNCTION_NOT_DEFINED, MYF(0), name.str); + return NULL; +} + + + Item *Schema::make_item_func_replace(THD *thd, Item *subj, Item *find, diff --git a/sql/sql_schema.h b/sql/sql_schema.h index 0258ff2d..af83c5e9 100644 --- a/sql/sql_schema.h +++ b/sql/sql_schema.h @@ -19,6 +19,9 @@ #include "mysqld.h" #include "lex_string.h" +class Lex_ident_sys; +class Create_func; + class Schema { LEX_CSTRING m_name; @@ -34,6 +37,24 @@ public: return src; } + /** + Find a native function builder, return an error if not found, + build an Item otherwise. + */ + Item *make_item_func_call_native(THD *thd, + const Lex_ident_sys &name, + List<Item> *args) const; + + /** + Find the native function builder associated with a given function name. + @param thd The current thread + @param name The native function name + @return The native function builder associated with the name, or NULL + */ + virtual Create_func *find_native_function_builder(THD *thd, + const LEX_CSTRING &name) + const; + // Builders for native SQL function with a special syntax in sql_yacc.yy virtual Item *make_item_func_replace(THD *thd, Item *subj, @@ -67,5 +88,6 @@ public: extern Schema mariadb_schema; +extern const Schema &oracle_schema_ref; #endif // SQL_SCHEMA_H_INCLUDED diff --git a/sql/sql_select.cc b/sql/sql_select.cc index f4cbed58..c3ce21d1 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -1457,6 +1457,26 @@ JOIN::prepare(TABLE_LIST *tables_init, COND *conds_init, uint og_num, if (setup_wild(thd, tables_list, fields_list, &all_fields, select_lex, false)) DBUG_RETURN(-1); + /* + If the select_lex is immediately contained within a derived table + AND this derived table is a CTE + WITH supplied column names + AND we have the correct number of elements in both lists + (mismatches found in mysql_derived_prepare/rename_columns_of_derived_unit) + THEN NOW is the time to take a copy of these item_names for + later restoration if required. + */ + TABLE_LIST *derived= select_lex->master_unit()->derived; + + if (derived && + derived->with && + derived->with->column_list.elements && + (derived->with->column_list.elements == select_lex->item_list.elements)) + { + if (select_lex->save_item_list_names(thd)) + DBUG_RETURN(-1); + } + if (thd->lex->current_select->first_cond_optimization) { if ( conds && ! thd->lex->current_select->merged_into) @@ -1965,9 +1985,14 @@ bool JOIN::make_range_rowid_filters() bool force_index_save= tab->table->force_index; tab->table->force_index= true; quick_select_return rc; + /* + EQ_FUNC and EQUAL_FUNC already sent unusable key notes (if any) + during update_ref_and_keys(). Have only other functions raise notes + from can_optimize_scalar_range(). + */ rc= sel->test_quick_select(thd, filter_map, (table_map) 0, (ha_rows) HA_POS_ERROR, true, false, true, - true); + true, Item_func::BITMAP_EXCEPT_ANY_EQUALITY); tab->table->force_index= force_index_save; if (rc == SQL_SELECT::ERROR || thd->is_error()) { @@ -5194,13 +5219,19 @@ static bool get_quick_record_count(THD *thd, SQL_SELECT *select, if (unlikely(check_stack_overrun(thd, STACK_MIN_SIZE, buff))) DBUG_RETURN(false); // Fatal error flag is set if (select) - { + { select->head=table; table->reginfo.impossible_range=0; + /* + EQ_FUNC and EQUAL_FUNC already sent unusable key notes (if any) + during update_ref_and_keys(). Have only other functions raise notes + from can_optimize_scalar_range(). + */ error= select->test_quick_select(thd, *(key_map *)keys, (table_map) 0, limit, 0, FALSE, TRUE, /* remove_where_parts*/ - FALSE, TRUE); + FALSE, + Item_func::BITMAP_EXCEPT_ANY_EQUALITY); if (error == SQL_SELECT::OK && select->quick) { @@ -5915,11 +5946,15 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list, This is can't be to high as otherwise we are likely to use table scan. */ - s->worst_seeks= MY_MIN((double) s->found_records / 10, - (double) s->read_time*3); - if (s->worst_seeks < 2.0) // Fix for small tables - s->worst_seeks=2.0; - + /* Largest integer that can be stored in double (no compiler warning) */ + s->worst_seeks= (double) (1ULL << 53); + if (thd->variables.optimizer_adjust_secondary_key_costs != 2) + { + s->worst_seeks= MY_MIN((double) s->found_records / 10, + (double) s->read_time*3); + if (s->worst_seeks < 2.0) // Fix for small tables + s->worst_seeks=2.0; + } /* Add to stat->const_keys those indexes for which all group fields or all select distinct fields participate in one index. @@ -7031,6 +7066,7 @@ add_key_part(DYNAMIC_ARRAY *keyuse_array, KEY_FIELD *key_field) { field->raise_note_cannot_use_key_part(thd, key, part, equal_str, + key_field->cond->compare_collation(), key_field->val, compat); } @@ -7896,8 +7932,27 @@ double cost_for_index_read(const THD *thd, const TABLE *table, uint key, if (table->covering_keys.is_set(key)) cost= file->keyread_time(key, 1, records); else + { cost= ((file->keyread_time(key, 0, records) + file->read_time(key, 1, MY_MIN(records, worst_seeks)))); + if (thd->variables.optimizer_adjust_secondary_key_costs == 1 && + file->is_clustering_key(0)) + { + /* + According to benchmarks done in 11.0 to calculate the new cost + model secondary key ranges are about 7x slower than primary + key ranges for big tables. Here we are a bit conservative and + only calculate with 5x. The reason for having it only 5x and + not for example 7x is is that choosing plans with more rows + that are read (ignored by the WHERE clause) causes the 10.x + optimizer to believe that there are more rows in the result + set, which can cause problems in finding the best join order. + Note: A clustering primary key is always key 0. + */ + double clustering_key_cost= file->read_time(0, 1, records); + cost= MY_MAX(cost, clustering_key_cost * 5); + } + } DBUG_PRINT("statistics", ("cost: %.3f", cost)); DBUG_RETURN(cost); @@ -8071,6 +8126,14 @@ best_access_path(JOIN *join, double keyread_tmp= 0; ha_rows rec; bool best_uses_jbuf= FALSE; + /* + if optimizer_use_condition_selectivity adjust filter cost to be slightly + higher to ensure that ref|filter is not less than range over same + number of rows + */ + double filter_setup_cost= (thd->variables. + optimizer_adjust_secondary_key_costs == 2 ? + 1.0 : 0.0); MY_BITMAP *eq_join_set= &s->table->eq_join_set; KEYUSE *hj_start_key= 0; SplM_plan_info *spl_plan= 0; @@ -8301,6 +8364,9 @@ best_access_path(JOIN *join, trace_access_idx.add("used_range_estimates", true); tmp= adjust_quick_cost(table->opt_range[key].cost, table->opt_range[key].rows); + keyread_tmp= table->file->keyread_time(key, 1, + table->opt_range[key]. + rows); goto got_cost; } else @@ -8625,6 +8691,7 @@ best_access_path(JOIN *join, type == JT_EQ_REF ? 0.5 * tmp : MY_MIN(tmp, keyread_tmp); double access_cost_factor= MY_MIN((tmp - key_access_cost) / rows, 1.0); + if (!(records < s->worst_seeks && records <= thd->variables.max_seeks_for_key)) { @@ -8641,7 +8708,9 @@ best_access_path(JOIN *join, } if (filter) { - tmp-= filter->get_adjusted_gain(rows) - filter->get_cmp_gain(rows); + tmp-= (filter->get_adjusted_gain(rows) - + filter->get_cmp_gain(rows) - + filter_setup_cost); DBUG_ASSERT(tmp >= 0); trace_access_idx.add("rowid_filter_key", table->key_info[filter->key_no].name); @@ -8891,7 +8960,7 @@ best_access_path(JOIN *join, access_cost_factor); if (filter) { - tmp-= filter->get_adjusted_gain(rows); + tmp-= filter->get_adjusted_gain(rows) - filter_setup_cost; DBUG_ASSERT(tmp >= 0); } @@ -13049,7 +13118,8 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) HA_POS_ERROR : join->unit->lim.get_select_limit()), 0, - FALSE, FALSE, FALSE)) == + FALSE, FALSE, FALSE, + Item_func::BITMAP_ALL)) == SQL_SELECT::IMPOSSIBLE_RANGE) { /* @@ -13064,7 +13134,8 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) OPTION_FOUND_ROWS ? HA_POS_ERROR : join->unit->lim.get_select_limit()), - 0, FALSE, FALSE, FALSE, TRUE)) == + 0, FALSE, FALSE, FALSE, + Item_func::BITMAP_NONE)) == SQL_SELECT::IMPOSSIBLE_RANGE) DBUG_RETURN(1); // Impossible WHERE } @@ -23199,7 +23270,9 @@ test_if_quick_select(JOIN_TAB *tab) res= tab->select->test_quick_select(tab->join->thd, tab->keys, (table_map) 0, HA_POS_ERROR, 0, FALSE, /*remove where parts*/FALSE, - FALSE, /* no warnings */ TRUE); + FALSE, + /* no unusable key notes */ + Item_func::BITMAP_NONE); if (tab->explain_plan && tab->explain_plan->range_checked_fer) tab->explain_plan->range_checked_fer->collect_data(tab->select->quick); @@ -25250,7 +25323,8 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, HA_POS_ERROR : tab->join->unit-> lim.get_select_limit(), - TRUE, TRUE, FALSE, FALSE); + TRUE, TRUE, FALSE, FALSE, + Item_func::BITMAP_ALL); // if we cannot use quick select if (res != SQL_SELECT::OK || !tab->select->quick) { @@ -25355,7 +25429,8 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, join->select_options & OPTION_FOUND_ROWS ? HA_POS_ERROR : join->unit->lim.get_select_limit(), - TRUE, FALSE, FALSE, FALSE); + TRUE, FALSE, FALSE, FALSE, + Item_func::BITMAP_ALL); if (res == SQL_SELECT::ERROR) { *fatal_error= true; @@ -29163,7 +29238,6 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, bool distinct,const char *message) { THD *thd=join->thd; - select_result *result=join->result; DBUG_ENTER("select_describe"); if (join->select_lex->pushdown_select) @@ -29198,7 +29272,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, if (unit->explainable()) { - if (mysql_explain_union(thd, unit, result)) + if (mysql_explain_union(thd, unit, unit->result)) DBUG_VOID_RETURN; } } diff --git a/sql/sql_sequence.cc b/sql/sql_sequence.cc index f5652bd8..f6d7c310 100644 --- a/sql/sql_sequence.cc +++ b/sql/sql_sequence.cc @@ -921,6 +921,7 @@ bool Sql_cmd_alter_sequence::execute(THD *thd) TABLE_LIST *first_table= lex->query_tables; TABLE *table; sequence_definition *new_seq= lex->create_info.seq_create_info; + uint saved_used_fields= new_seq->used_fields; SEQUENCE *seq; No_such_table_error_handler no_such_table_handler; DBUG_ENTER("Sql_cmd_alter_sequence::execute"); @@ -1042,5 +1043,6 @@ bool Sql_cmd_alter_sequence::execute(THD *thd) my_ok(thd); end: + new_seq->used_fields= saved_used_fields; DBUG_RETURN(error); } diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 32b29468..980c2fa7 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2777,7 +2777,7 @@ static const char *thread_state_info(THD *tmp) if (cond) return "Waiting on cond"; } - return NULL; + return ""; } @@ -6398,7 +6398,8 @@ int fill_schema_collation(THD *thd, TABLE_LIST *tables, COND *cond) tmp_cl->get_collation_name(MY_COLLATION_NAME_MODE_CONTEXT); LEX_CSTRING full_collation_name= tmp_cl->get_collation_name(MY_COLLATION_NAME_MODE_FULL); - bool is_context= cmp(context_collation_name, full_collation_name); + bool is_context= cmp(context_collation_name, full_collation_name) && + !(thd->variables.old_behavior & OLD_MODE_NO_NULL_COLLATION_IDS); /* Some collations are applicable to multiple character sets. Display them only once, with the short name (without the @@ -8833,9 +8834,9 @@ int mysql_schema_table(THD *thd, LEX *lex, TABLE_LIST *table_list) } List_iterator_fast<Item> it(sel->item_list); if (!(transl= - (Field_translator*)(thd->stmt_arena-> + (Field_translator*)(thd->active_stmt_arena_to_use()-> alloc(sel->item_list.elements * - sizeof(Field_translator))))) + sizeof(Field_translator))))) // ??? { DBUG_RETURN(1); } @@ -9795,7 +9796,7 @@ ST_FIELD_INFO partitions_fields_info[]= ST_FIELD_INFO variables_fields_info[]= { Column("VARIABLE_NAME", Varchar(64), NOT_NULL, "Variable_name"), - Column("VARIABLE_VALUE", Varchar(2048), NOT_NULL, "Value"), + Column("VARIABLE_VALUE", Varchar(4096), NOT_NULL, "Value"), CEnd() }; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index b33d2ff4..7e4d271b 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -7208,6 +7208,14 @@ bool mysql_compare_tables(TABLE *table, Alter_info *alter_info, (uint) (field->flags & NOT_NULL_FLAG)) DBUG_RETURN(false); + if (field->vcol_info) + { + if (!tmp_new_field->field->vcol_info) + DBUG_RETURN(false); + if (!field->vcol_info->is_equal(tmp_new_field->field->vcol_info)) + DBUG_RETURN(false); + } + /* mysql_prepare_alter_table() clears HA_OPTION_PACK_RECORD bit when preparing description of existing table. In ALTER TABLE it is later @@ -9089,6 +9097,30 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, goto err; case Alter_drop::FOREIGN_KEY: // Leave the DROP FOREIGN KEY names in the alter_info->drop_list. + /* If this is DROP FOREIGN KEY without IF EXIST, + we can now check does it exists and if not report a error. */ + if (!drop->drop_if_exists) + { + List <FOREIGN_KEY_INFO> fk_child_key_list; + table->file->get_foreign_key_list(thd, &fk_child_key_list); + if (fk_child_key_list.is_empty()) + { + fk_not_found: + my_error(ER_CANT_DROP_FIELD_OR_KEY, MYF(0), drop->type_name(), + drop->name); + goto err; + } + List_iterator<FOREIGN_KEY_INFO> fk_key_it(fk_child_key_list); + while (FOREIGN_KEY_INFO *f_key= fk_key_it++) + { + if (my_strcasecmp(system_charset_info, f_key->foreign_id->str, + drop->name) == 0) + goto fk_found; + } + goto fk_not_found; + fk_found: + break; + } break; } } @@ -12278,13 +12310,18 @@ bool check_engine(THD *thd, const char *db_name, if (!*new_engine) DBUG_RETURN(true); - /* Enforced storage engine should not be used in - ALTER TABLE that does not use explicit ENGINE = x to - avoid unwanted unrelated changes.*/ - if (!(thd->lex->sql_command == SQLCOM_ALTER_TABLE && - !(create_info->used_fields & HA_CREATE_USED_ENGINE))) - enf_engine= thd->variables.enforced_table_plugin ? - plugin_hton(thd->variables.enforced_table_plugin) : NULL; + /* + Enforced storage engine should not be used in ALTER TABLE that does not + use explicit ENGINE = x to avoid unwanted unrelated changes. It should not + be used in CREATE INDEX too. + */ + if (!((thd->lex->sql_command == SQLCOM_ALTER_TABLE && + !(create_info->used_fields & HA_CREATE_USED_ENGINE)) || + thd->lex->sql_command == SQLCOM_CREATE_INDEX)) + { + plugin_ref enf_plugin= thd->variables.enforced_table_plugin; + enf_engine= enf_plugin ? plugin_hton(enf_plugin) : NULL; + } if (enf_engine && enf_engine != *new_engine) { @@ -12381,8 +12418,18 @@ bool Sql_cmd_create_table_like::execute(THD *thd) Alter_info alter_info(lex->alter_info, thd->mem_root); #ifdef WITH_WSREP + bool wsrep_ctas= false; // If CREATE TABLE AS SELECT and wsrep_on - const bool wsrep_ctas= (select_lex->item_list.elements && WSREP(thd)); + if (WSREP(thd) && (select_lex->item_list.elements || + // Only CTAS may be applied not using TOI. + (wsrep_thd_is_applying(thd) && !wsrep_thd_is_toi(thd)))) + { + wsrep_ctas= true; + + // MDEV-22232: Disable CTAS retry by setting the retry counter to the + // threshold value. + thd->wsrep_retry_counter= thd->variables.wsrep_retry_autocommit; + } // This will be used in THD::decide_logging_format if CTAS Enable_wsrep_ctas_guard wsrep_ctas_guard(thd, wsrep_ctas); diff --git a/sql/sql_test.cc b/sql/sql_test.cc index 9163d8fc..5cc192af 100644 --- a/sql/sql_test.cc +++ b/sql/sql_test.cc @@ -29,11 +29,16 @@ #include <thr_alarm.h> #include "sql_connect.h" #include "thread_cache.h" -#if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_H) + +#if defined(HAVE_MALLOC_H) #include <malloc.h> -#elif defined(HAVE_MALLINFO) && defined(HAVE_SYS_MALLOC_H) +#endif + +#if defined(HAVE_SYS_MALLOC_H) #include <sys/malloc.h> -#elif defined(HAVE_MALLOC_ZONE) +#endif + +#if defined(HAVE_MALLOC_ZONE) #include <malloc/malloc.h> #endif diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index 067b921e..d7600108 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -2537,7 +2537,8 @@ add_tables_and_routines_for_triggers(THD *thd, MDL_key key(MDL_key::TRIGGER, trigger->m_db.str, trigger->m_name.str); - if (sp_add_used_routine(prelocking_ctx, thd->stmt_arena, + if (sp_add_used_routine(prelocking_ctx, + thd->active_stmt_arena_to_use(), &key, &sp_handler_trigger, table_list->belong_to_view)) { diff --git a/sql/sql_tvc.cc b/sql/sql_tvc.cc index c5a2b16a..fc1db382 100644 --- a/sql/sql_tvc.cc +++ b/sql/sql_tvc.cc @@ -271,7 +271,10 @@ bool table_value_constr::prepare(THD *thd, SELECT_LEX *sl, if (!holders) { - holders= type_holders= new (thd->stmt_arena->mem_root) Type_holder[cnt]; + DBUG_ASSERT(thd->stmt_arena->is_stmt_prepare_or_first_stmt_execute() || + thd->stmt_arena->is_conventional()); + holders= type_holders= + new (thd->active_stmt_arena_to_use()->mem_root) Type_holder[cnt]; if (!holders || join_type_handlers_for_tvc(thd, li, holders, cnt) || get_type_attributes_for_tvc(thd, li, holders, diff --git a/sql/sql_type.cc b/sql/sql_type.cc index 277f495a..a086e338 100644 --- a/sql/sql_type.cc +++ b/sql/sql_type.cc @@ -1446,22 +1446,6 @@ Type_handler_string_result::charset_for_protocol(const Item *item) const } -const Type_handler * -Type_handler::get_handler_by_cmp_type(Item_result type) -{ - switch (type) { - case REAL_RESULT: return &type_handler_double; - case INT_RESULT: return &type_handler_slonglong; - case DECIMAL_RESULT: return &type_handler_newdecimal; - case STRING_RESULT: return &type_handler_long_blob; - case TIME_RESULT: return &type_handler_datetime; - case ROW_RESULT: return &type_handler_row; - } - DBUG_ASSERT(0); - return &type_handler_string; -} - - /* If we have a mixture of: - a MariaDB standard (built-in permanent) data type, and @@ -2258,6 +2242,34 @@ Type_handler::get_handler_by_real_type(enum_field_types type) } +const Type_handler * +Type_handler::handler_by_log_event_data_type(THD *thd, + const Log_event_data_type &type) +{ + if (type.data_type_name().length) + { + const Type_handler *th= handler_by_name(thd, type.data_type_name()); + if (th) + return th; + } + switch (type.type()) { + case STRING_RESULT: + case ROW_RESULT: + case TIME_RESULT: + break; + case REAL_RESULT: + return &type_handler_double; + case INT_RESULT: + if (type.is_unsigned()) + return &type_handler_ulonglong; + return &type_handler_slonglong; + case DECIMAL_RESULT: + return &type_handler_newdecimal; + } + return &type_handler_long_blob; +} + + /** Create a DOUBLE field by default. */ @@ -5668,6 +5680,14 @@ Type_handler_string_result::Item_func_hybrid_field_type_get_date( /***************************************************************************/ +bool Type_handler::Item_bool_rowready_func2_fix_length_and_dec(THD *thd, + Item_bool_rowready_func2 *func) const +{ + return func->fix_length_and_dec_generic(thd, this); +} + +/***************************************************************************/ + bool Type_handler_numeric:: Item_func_between_fix_length_and_dec(Item_func_between *func) const { diff --git a/sql/sql_type.h b/sql/sql_type.h index 35c13a38..5ce17447 100644 --- a/sql/sql_type.h +++ b/sql/sql_type.h @@ -30,6 +30,8 @@ #include "sql_type_string.h" #include "sql_type_real.h" #include "compat56.h" +#include "log_event_data_type.h" + C_MODE_START #include <ma_dyncol.h> C_MODE_END @@ -55,6 +57,7 @@ class Item_hybrid_func; class Item_func_min_max; class Item_func_hybrid_field_type; class Item_bool_func2; +class Item_bool_rowready_func2; class Item_func_between; class Item_func_in; class Item_func_round; @@ -150,8 +153,8 @@ scalar_comparison_op_to_lex_cstring(scalar_comparison_op op) case SCALAR_CMP_EQUAL: return LEX_CSTRING{STRING_WITH_LEN("<=>")}; case SCALAR_CMP_LT: return LEX_CSTRING{STRING_WITH_LEN("<")}; case SCALAR_CMP_LE: return LEX_CSTRING{STRING_WITH_LEN("<=")}; - case SCALAR_CMP_GE: return LEX_CSTRING{STRING_WITH_LEN(">")}; - case SCALAR_CMP_GT: return LEX_CSTRING{STRING_WITH_LEN(">=")}; + case SCALAR_CMP_GE: return LEX_CSTRING{STRING_WITH_LEN(">=")}; + case SCALAR_CMP_GT: return LEX_CSTRING{STRING_WITH_LEN(">")}; } DBUG_ASSERT(0); return LEX_CSTRING{STRING_WITH_LEN("<?>")}; @@ -3672,6 +3675,9 @@ public: static const Type_handler *handler_by_name(THD *thd, const LEX_CSTRING &name); static const Type_handler *handler_by_name_or_error(THD *thd, const LEX_CSTRING &name); + static const Type_handler *handler_by_log_event_data_type( + THD *thd, + const Log_event_data_type &type); static const Type_handler *odbc_literal_type_handler(const LEX_CSTRING *str); static const Type_handler *blob_type_handler(uint max_octet_length); static const Type_handler *string_type_handler(uint max_octet_length); @@ -3689,7 +3695,6 @@ public: static const Type_handler *blob_type_handler(const Item *item); static const Type_handler *get_handler_by_field_type(enum_field_types type); static const Type_handler *get_handler_by_real_type(enum_field_types type); - static const Type_handler *get_handler_by_cmp_type(Item_result type); static const Type_collection * type_collection_for_aggregation(const Type_handler *h1, const Type_handler *h2); @@ -3957,6 +3962,12 @@ public: { return false; } + + virtual Log_event_data_type user_var_log_event_data_type(uint charset_nr) const + { + return Log_event_data_type({NULL,0}/*data type name*/, result_type(), + charset_nr, is_unsigned()); + } virtual uint Column_definition_gis_options_image(uchar *buff, const Column_definition &def) const @@ -4257,6 +4268,8 @@ public: } virtual bool Item_eq_value(THD *thd, const Type_cmp_attributes *attr, Item *a, Item *b) const= 0; + virtual bool Item_bool_rowready_func2_fix_length_and_dec(THD *thd, + Item_bool_rowready_func2 *func) const; virtual bool Item_hybrid_func_fix_attributes(THD *thd, const LEX_CSTRING &name, Type_handler_hybrid_field_type *, diff --git a/sql/sql_type_fixedbin.h b/sql/sql_type_fixedbin.h index 88c24ba0..65418c34 100644 --- a/sql/sql_type_fixedbin.h +++ b/sql/sql_type_fixedbin.h @@ -136,6 +136,21 @@ public: return Fbt_null(item, false).is_null(); } + /* + Check at fix_fields() time if any of the items can return a nullable + value on conversion to Fbt. + */ + static bool fix_fields_maybe_null_on_conversion_to_fbt(Item **items, + uint count) + { + for (uint i= 0; i < count; i++) + { + if (Fbt::fix_fields_maybe_null_on_conversion_to_fbt(items[i])) + return true; + } + return false; + } + public: Fbt(Item *item, bool *error, bool warn= true) @@ -1534,6 +1549,16 @@ public: Fbt_null na(a), nb(b); return !na.is_null() && !nb.is_null() && !na.cmp(nb); } + bool Item_bool_rowready_func2_fix_length_and_dec(THD *thd, + Item_bool_rowready_func2 *func) const override + { + if (Type_handler::Item_bool_rowready_func2_fix_length_and_dec(thd, func)) + return true; + if (!func->maybe_null() && + Fbt::fix_fields_maybe_null_on_conversion_to_fbt(func->arguments(), 2)) + func->set_maybe_null(); + return false; + } bool Item_hybrid_func_fix_attributes(THD *thd, const LEX_CSTRING &name, Type_handler_hybrid_field_type *h, Type_all_attributes *attr, @@ -1715,6 +1740,9 @@ public: bool Item_func_between_fix_length_and_dec(Item_func_between *func) const override { + if (!func->maybe_null() && + Fbt::fix_fields_maybe_null_on_conversion_to_fbt(func->arguments(), 3)) + func->set_maybe_null(); return false; } longlong Item_func_between_val_int(Item_func_between *func) const override @@ -1737,6 +1765,10 @@ public: Item_func_in *func) const override { + if (!func->maybe_null() && + Fbt::fix_fields_maybe_null_on_conversion_to_fbt(func->arguments(), + func->argument_count())) + func->set_maybe_null(); if (func->compatible_types_scalar_bisection_possible()) { return func->value_list_convert_const_to_int(thd) || diff --git a/sql/sql_type_geom.h b/sql/sql_type_geom.h index d86d1181..14d3d062 100644 --- a/sql/sql_type_geom.h +++ b/sql/sql_type_geom.h @@ -82,6 +82,13 @@ public: Field *make_conversion_table_field(MEM_ROOT *root, TABLE *table, uint metadata, const Field *target) const override; + Log_event_data_type user_var_log_event_data_type(uint charset_nr) + const override + { + return Log_event_data_type(name().lex_cstring(), result_type(), + charset_nr, false/*unsigned*/); + } + uint Column_definition_gis_options_image(uchar *buff, const Column_definition &def) const override; diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 1be0bbcd..145299ac 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -587,7 +587,8 @@ int mysql_update(THD *thd, select= make_select(table, 0, 0, conds, (SORT_INFO*) 0, 0, &error); if (unlikely(error || !limit || thd->is_error() || - (select && select->check_quick(thd, safe_update, limit)))) + (select && select->check_quick(thd, safe_update, limit, + Item_func::BITMAP_ALL)))) { query_plan.set_impossible_where(); if (thd->lex->describe || thd->lex->analyze_stmt) @@ -2444,7 +2445,8 @@ loop_end: group.direction= ORDER::ORDER_ASC; group.item= (Item**) temp_fields.head_ref(); - tmp_param->quick_group= 1; + tmp_param->init(); + tmp_param->tmp_name="update"; tmp_param->field_count= temp_fields.elements; tmp_param->func_count= temp_fields.elements - 1; calc_group_buffer(tmp_param, &group); diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 08208048..d9422272 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -297,7 +297,8 @@ bool create_view_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *view, for (tbl= sl->get_table_list(); tbl; tbl= tbl->next_local) { if (!tbl->with && tbl->select_lex) - tbl->with= tbl->select_lex->find_table_def_in_with_clauses(tbl); + tbl->with= tbl->select_lex->find_table_def_in_with_clauses(tbl, + NULL); /* Ensure that we have some privileges on this table, more strict check will be done on column level after preparation, @@ -1005,7 +1006,8 @@ static int mysql_register_view(THD *thd, DDL_LOG_STATE *ddl_log_state, { Sql_mode_save_for_frm_handling sql_mode_save(thd); - lex->unit.print(&view_query, enum_query_type(QT_VIEW_INTERNAL | + lex->unit.print(&view_query, enum_query_type(QT_FOR_FRM | + QT_VIEW_INTERNAL | QT_ITEM_ORIGINAL_FUNC_NULLIF | QT_NO_WRAPPERS_FOR_TVC_IN_VIEW)); lex->unit.print(&is_query, enum_query_type(QT_TO_SYSTEM_CHARSET | @@ -1725,7 +1727,7 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table, objects of the view. */ if (!(table->view_sctx= (Security_context *) - thd->stmt_arena->calloc(sizeof(Security_context)))) + thd->active_stmt_arena_to_use()->calloc(sizeof(Security_context)))) goto err; security_ctx= table->view_sctx; } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index ffa70dea..dcf0e995 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -733,7 +733,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); %token <kwd> ACTION /* SQL-2003-N */ %token <kwd> ADMIN_SYM /* SQL-2003-N */ %token <kwd> ADDDATE_SYM /* MYSQL-FUNC */ -%token <kwd> ADD_MONTHS_SYM /* Oracle FUNC*/ %token <kwd> AFTER_SYM /* SQL-2003-N */ %token <kwd> AGAINST %token <kwd> AGGREGATE_SYM @@ -809,7 +808,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); %token <kwd> DATAFILE_SYM %token <kwd> DATA_SYM /* SQL-2003-N */ %token <kwd> DATETIME -%token <kwd> DATE_FORMAT_SYM /* MYSQL-FUNC */ %token <kwd> DATE_SYM /* SQL-2003-R, Oracle-R, PLSQL-R */ %token <kwd> DAY_SYM /* SQL-2003-R */ %token <kwd> DEALLOCATE_SYM /* SQL-2003-R */ @@ -961,7 +959,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); %token <kwd> NATIONAL_SYM /* SQL-2003-R */ %token <kwd> NCHAR_SYM /* SQL-2003-R */ %token <kwd> NEVER_SYM /* MySQL */ -%token <kwd> NEW_SYM /* SQL-2003-R */ %token <kwd> NEXT_SYM /* SQL-2003-N */ %token <kwd> NEXTVAL_SYM /* PostgreSQL sequence function */ %token <kwd> NOCACHE_SYM @@ -1124,7 +1121,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); %token <kwd> TRIGGERS_SYM %token <kwd> TRIM_ORACLE %token <kwd> TRUNCATE_SYM -%token <kwd> TYPES_SYM %token <kwd> TYPE_SYM /* SQL-2003-N */ %token <kwd> UDF_RETURNS_SYM %token <kwd> UNBOUNDED_SYM /* SQL-2011-N */ @@ -1315,6 +1311,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); %type <ident_sys> IDENT_sys + ident_func ident label_ident sp_decl_ident @@ -1339,6 +1336,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); IDENT_cli ident_cli ident_cli_set_usual_case + ident_cli_func %type <ident_sys_ptr> ident_sys_alloc @@ -1353,6 +1351,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); keyword_sp_block_section keyword_sp_decl keyword_sp_head + keyword_func_sp_var_and_label + keyword_func_sp_var_not_label keyword_sp_var_and_label keyword_sp_var_not_label keyword_sysvar_name @@ -1833,8 +1833,17 @@ rule: %type <spvar> sp_param_anchored %type <for_loop> sp_for_loop_index_and_bounds %type <for_loop_bounds> sp_for_loop_bounds -%type <trim> trim_operands -%type <substring_spec> substring_operands + +%type <trim> + trim_operands + trim_operands_regular + trim_operands_special + +%type <substring_spec> + substring_operands + substring_operands_regular + substring_operands_special + %type <num> opt_sp_for_loop_direction %type <spvar_mode> sp_parameter_type %type <index_hint> index_hint_type @@ -7176,11 +7185,7 @@ alter: | ALTER SEQUENCE_SYM opt_if_exists { LEX *lex= Lex; - lex->name= null_clex_str; - lex->table_type= TABLE_TYPE_UNKNOWN; lex->sql_command= SQLCOM_ALTER_SEQUENCE; - lex->create_info.init(); - lex->no_write_to_binlog= 0; DBUG_ASSERT(!lex->m_sql_cmd); if (Lex->main_select_push()) MYSQL_YYABORT; @@ -9656,8 +9661,16 @@ explicit_cursor_attr: trim_operands: + trim_operands_regular + | trim_operands_special + ; + +trim_operands_regular: expr { $$.set(TRIM_BOTH, $1); } - | LEADING expr FROM expr { $$.set(TRIM_LEADING, $2, $4); } + ; + +trim_operands_special: + LEADING expr FROM expr { $$.set(TRIM_LEADING, $2, $4); } | TRAILING expr FROM expr { $$.set(TRIM_TRAILING, $2, $4); } | BOTH expr FROM expr { $$.set(TRIM_BOTH, $2, $4); } | LEADING FROM expr { $$.set(TRIM_LEADING, $3); } @@ -10067,6 +10080,11 @@ function_call_keyword: ; substring_operands: + substring_operands_regular + | substring_operands_special + ; + +substring_operands_regular: expr ',' expr ',' expr { $$= Lex_substring_spec_st::init($1, $3, $5); @@ -10075,7 +10093,10 @@ substring_operands: { $$= Lex_substring_spec_st::init($1, $3); } - | expr FROM expr FOR_SYM expr + ; + +substring_operands_special: + expr FROM expr FOR_SYM expr { $$= Lex_substring_spec_st::init($1, $3, $5); } @@ -10099,14 +10120,7 @@ substring_operands: discouraged. */ function_call_nonkeyword: - ADD_MONTHS_SYM '(' expr ',' expr ')' - { - $$= new (thd->mem_root) Item_date_add_interval(thd, $3, $5, - INTERVAL_MONTH, 0); - if (unlikely($$ == NULL)) - MYSQL_YYABORT; - } - | ADDDATE_SYM '(' expr ',' expr ')' + ADDDATE_SYM '(' expr ',' expr ')' { $$= new (thd->mem_root) Item_date_add_interval(thd, $3, $5, INTERVAL_DAY, 0); @@ -10145,18 +10159,6 @@ function_call_nonkeyword: if (unlikely($$ == NULL)) MYSQL_YYABORT; } - | DATE_FORMAT_SYM '(' expr ',' expr ')' - { - $$= new (thd->mem_root) Item_func_date_format(thd, $3, $5); - if (unlikely($$ == NULL)) - MYSQL_YYABORT; - } - | DATE_FORMAT_SYM '(' expr ',' expr ',' expr ')' - { - $$= new (thd->mem_root) Item_func_date_format(thd, $3, $5, $7); - if (unlikely($$ == NULL)) - MYSQL_YYABORT; - } | EXTRACT_SYM '(' interval FROM expr ')' { $$=new (thd->mem_root) Item_extract(thd, $3, $5); @@ -10282,13 +10284,6 @@ function_call_nonkeyword: MYSQL_YYABORT; } | - COLUMN_CHECK_SYM '(' expr ')' - { - $$= new (thd->mem_root) Item_func_dyncol_check(thd, $3); - if (unlikely($$ == NULL)) - MYSQL_YYABORT; - } - | COLUMN_CREATE_SYM '(' dyncall_create_list ')' { $$= create_func_dyncol_create(thd, *$3); @@ -10323,43 +10318,12 @@ function_call_conflict: if (unlikely($$ == NULL)) MYSQL_YYABORT; } - | COALESCE '(' expr_list ')' - { - $$= new (thd->mem_root) Item_func_coalesce(thd, *$3); - if (unlikely($$ == NULL)) - MYSQL_YYABORT; - } - | COLLATION_SYM '(' expr ')' - { - $$= new (thd->mem_root) Item_func_collation(thd, $3); - if (unlikely($$ == NULL)) - MYSQL_YYABORT; - } - | DATABASE '(' ')' - { - $$= new (thd->mem_root) Item_func_database(thd); - if (unlikely($$ == NULL)) - MYSQL_YYABORT; - Lex->safe_to_cache_query=0; - } | IF_SYM '(' expr ',' expr ',' expr ')' { $$= new (thd->mem_root) Item_func_if(thd, $3, $5, $7); if (unlikely($$ == NULL)) MYSQL_YYABORT; } - | FORMAT_SYM '(' expr ',' expr ')' - { - $$= new (thd->mem_root) Item_func_format(thd, $3, $5); - if (unlikely($$ == NULL)) - MYSQL_YYABORT; - } - | FORMAT_SYM '(' expr ',' expr ',' expr ')' - { - $$= new (thd->mem_root) Item_func_format(thd, $3, $5, $7); - if (unlikely($$ == NULL)) - MYSQL_YYABORT; - } /* LAST_VALUE here conflicts with the definition for window functions. We have these 2 separate rules to remove the shift/reduce conflict. */ @@ -10381,25 +10345,12 @@ function_call_conflict: if (unlikely($$ == NULL)) MYSQL_YYABORT; } - | MICROSECOND_SYM '(' expr ')' - { - $$= new (thd->mem_root) Item_func_microsecond(thd, $3); - if (unlikely($$ == NULL)) - MYSQL_YYABORT; - } | MOD_SYM '(' expr ',' expr ')' { $$= new (thd->mem_root) Item_func_mod(thd, $3, $5); if (unlikely($$ == NULL)) MYSQL_YYABORT; } - | OLD_PASSWORD_SYM '(' expr ')' - { - $$= new (thd->mem_root) - Item_func_password(thd, $3, Item_func_password::OLD); - if (unlikely($$ == NULL)) - MYSQL_YYABORT; - } | PASSWORD_SYM '(' expr ')' { Item* i1; @@ -10408,12 +10359,6 @@ function_call_conflict: MYSQL_YYABORT; $$= i1; } - | QUARTER_SYM '(' expr ')' - { - $$= new (thd->mem_root) Item_func_quarter(thd, $3); - if (unlikely($$ == NULL)) - MYSQL_YYABORT; - } | REPEAT_SYM '(' expr ',' expr ')' { $$= new (thd->mem_root) Item_func_repeat(thd, $3, $5); @@ -10426,38 +10371,12 @@ function_call_conflict: make_item_func_replace(thd, $3, $5, $7)))) MYSQL_YYABORT; } - | REVERSE_SYM '(' expr ')' - { - $$= new (thd->mem_root) Item_func_reverse(thd, $3); - if (unlikely($$ == NULL)) - MYSQL_YYABORT; - } - | ROW_COUNT_SYM '(' ')' - { - $$= new (thd->mem_root) Item_func_row_count(thd); - if (unlikely($$ == NULL)) - MYSQL_YYABORT; - Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION); - Lex->safe_to_cache_query= 0; - } | TRUNCATE_SYM '(' expr ',' expr ')' { $$= new (thd->mem_root) Item_func_round(thd, $3, $5, 1); if (unlikely($$ == NULL)) MYSQL_YYABORT; } - | WEEK_SYM '(' expr ')' - { - $$= new (thd->mem_root) Item_func_week(thd, $3); - if (unlikely($$ == NULL)) - MYSQL_YYABORT; - } - | WEEK_SYM '(' expr ',' expr ')' - { - $$= new (thd->mem_root) Item_func_week(thd, $3, $5); - if (unlikely($$ == NULL)) - MYSQL_YYABORT; - } | WEIGHT_STRING_SYM '(' expr opt_ws_levels ')' { $$= new (thd->mem_root) Item_func_weight_string(thd, $3, 0, 0, $4); @@ -10503,7 +10422,7 @@ function_call_conflict: in sql/item_create.cc */ function_call_generic: - IDENT_sys '(' + ident_func '(' { #ifdef HAVE_DLOPEN udf_func *udf= 0; @@ -10541,7 +10460,9 @@ function_call_generic: This will be revised with WL#2128 (SQL PATH) */ - if ((builder= native_functions_hash.find(thd, $1))) + builder= Schema::find_implied(thd)-> + find_native_function_builder(thd, $1); + if (builder) { item= builder->create_func(thd, &$1, $4); } @@ -10605,6 +10526,43 @@ function_call_generic: if (unlikely(!($$= Lex->make_item_func_call_generic(thd, &$1, &$3, &$5, $7)))) MYSQL_YYABORT; } + | ident_cli '.' REPLACE '(' opt_expr_list ')' + { + if (unlikely(!($$= Lex->make_item_func_replace(thd, $1, $3, $5)))) + MYSQL_YYABORT; + } + | ident_cli '.' SUBSTRING '(' opt_expr_list ')' + { + if (unlikely(!($$= Lex->make_item_func_substr(thd, $1, $3, $5)))) + MYSQL_YYABORT; + } + | ident_cli '.' SUBSTRING '(' substring_operands_special ')' + { + if (unlikely(!($$= Lex->make_item_func_substr(thd, $1, $3, $5)))) + MYSQL_YYABORT; + } + | ident_cli '.' TRIM '(' opt_expr_list ')' + { + if (unlikely(!($$= Lex->make_item_func_trim(thd, $1, $3, $5)))) + MYSQL_YYABORT; + } + | ident_cli '.' TRIM '(' trim_operands_special ')' + { + if (unlikely(!($$= Lex->make_item_func_trim(thd, $1, $3, $5)))) + MYSQL_YYABORT; + } + /* + We don't add a qualified syntax for TRIM_ORACLE here, + as this syntax is not absolutely required: + SELECT mariadb_schema.TRIM_ORACLE(..); + What absolutely required is only: + SELECT mariadb_schema.TRIM(..); + Adding a qualified syntax for TRIM_ORACLE would be tricky because + it is a non-reserved keyword. To avoid new shift/reduce conflicts + it would require grammar changes, like introducing a new rule + ident_step2_cli (which would include everything that ident_cli + includes but TRIM_ORACLE). + */ ; fulltext_options: @@ -15159,10 +15117,7 @@ with_column_list: ident_sys_alloc: ident_cli { - void *buf= thd->alloc(sizeof(Lex_ident_sys)); - if (!buf) - MYSQL_YYABORT; - $$= new (buf) Lex_ident_sys(thd, &$1); + $$= new (thd->mem_root) Lex_ident_sys(thd, &$1); } ; @@ -15383,6 +15338,22 @@ IDENT_sys: } ; +ident_cli_func: + IDENT + | IDENT_QUOTED + | keyword_func_sp_var_and_label { $$= $1; } + | keyword_func_sp_var_not_label { $$= $1; } + ; + +ident_func: + ident_cli_func + { + if (unlikely(thd->to_ident_sys_alloc(&$$, &$1))) + MYSQL_YYABORT; + } + ; + + TEXT_STRING_sys: TEXT_STRING { @@ -15606,7 +15577,8 @@ non_reserved_keyword_udt: TODO: check if some of them can migrate to keyword_sp_var_and_label. */ keyword_sp_var_not_label: - ASCII_SYM + keyword_func_sp_var_not_label + | ASCII_SYM | BACKUP_SYM | BINLOG_SYM | BYTE_SYM @@ -15614,7 +15586,6 @@ keyword_sp_var_not_label: | CHECKSUM_SYM | CHECKPOINT_SYM | COLUMN_ADD_SYM - | COLUMN_CHECK_SYM | COLUMN_CREATE_SYM | COLUMN_DELETE_SYM | COLUMN_GET_SYM @@ -15626,7 +15597,6 @@ keyword_sp_var_not_label: | EXECUTE_SYM | FLUSH_SYM | FOLLOWING_SYM - | FORMAT_SYM | GET_SYM | HELP_SYM | HOST_SYM @@ -15780,21 +15750,15 @@ keyword_cast_type: ; -/* - These keywords are fine for both SP variable names and SP labels. -*/ -keyword_sp_var_and_label: - ACTION +keyword_func_sp_var_and_label: + ACTION | ACCOUNT_SYM - | ADDDATE_SYM - | ADD_MONTHS_SYM | ADMIN_SYM | AFTER_SYM | AGAINST | AGGREGATE_SYM | ALGORITHM_SYM | ALWAYS_SYM - | ANY_SYM | AT_SYM | ATOMIC_SYM | AUTHORS_SYM @@ -15802,7 +15766,6 @@ keyword_sp_var_and_label: | AUTOEXTEND_SIZE_SYM | AUTO_SYM | AVG_ROW_LENGTH - | AVG_SYM | BLOCK_SYM | BODY_MARIADB_SYM | BTREE_SYM @@ -15815,7 +15778,6 @@ keyword_sp_var_and_label: | CLIENT_SYM | CLASS_ORIGIN_SYM | COALESCE - | CODE_SYM | COLLATION_SYM | COLUMN_NAME_SYM | COLUMNS @@ -15841,16 +15803,15 @@ keyword_sp_var_and_label: | CURSOR_NAME_SYM | CYCLE_SYM | DATA_SYM + | DATABASE | DATAFILE_SYM - | DATE_FORMAT_SYM - | DAY_SYM | DEFINER_SYM | DELAY_KEY_WRITE_SYM | DES_KEY_FILE | DIAGNOSTICS_SYM + | DISCARD | DIRECTORY_SYM | DISABLE_SYM - | DISCARD | DISK_SYM | DUMPFILE | DUPLICATE_SYM @@ -15858,6 +15819,11 @@ keyword_sp_var_and_label: | ELSEIF_ORACLE_SYM | ELSIF_MARIADB_SYM | EMPTY_SYM + | EXPIRE_SYM + | EXPORT_SYM + | EXTENDED_SYM + | EXTENT_SIZE_SYM + | ENABLE_SYM | ENDS_SYM | ENGINE_SYM | ENGINES_SYM @@ -15870,29 +15836,21 @@ keyword_sp_var_and_label: | EXCEPTION_MARIADB_SYM | EXCHANGE_SYM | EXPANSION_SYM - | EXPIRE_SYM - | EXPORT_SYM - | EXTENDED_SYM - | EXTENT_SIZE_SYM | FAULTS_SYM | FAST_SYM - | FOUND_SYM - | ENABLE_SYM | FEDERATED_SYM - | FULL | FILE_SYM | FIRST_SYM + | FOUND_SYM + | FULL | GENERAL | GENERATED_SYM - | GET_FORMAT | GRANTS | GOTO_MARIADB_SYM | HASH_SYM | HARD_SYM | HISTORY_SYM | HOSTS_SYM - | HOUR_SYM - | ID_SYM | IDENTIFIED_SYM | IGNORE_SERVER_IDS_SYM | INCREMENT_SYM @@ -15910,9 +15868,7 @@ keyword_sp_var_and_label: | INVISIBLE_SYM | JSON_TABLE_SYM | KEY_BLOCK_SIZE - | LAST_VALUE | LAST_SYM - | LASTVAL_SYM | LEAVES | LESS_SYM | LEVEL_SYM @@ -15954,7 +15910,6 @@ keyword_sp_var_and_label: | MESSAGE_TEXT_SYM | MICROSECOND_SYM | MIGRATE_SYM - | MINUTE_SYM %ifdef MARIADB | MINUS_ORACLE_SYM %endif @@ -15963,7 +15918,6 @@ keyword_sp_var_and_label: | MODIFY_SYM | MODE_SYM | MONITOR_SYM - | MONTH_SYM | MUTEX_SYM | MYSQL_SYM | MYSQL_ERRNO_SYM @@ -15971,8 +15925,6 @@ keyword_sp_var_and_label: | NESTED_SYM | NEVER_SYM | NEXT_SYM %prec PREC_BELOW_CONTRACTION_TOKEN2 - | NEXTVAL_SYM - | NEW_SYM | NOCACHE_SYM | NOCYCLE_SYM | NOMINVALUE_SYM @@ -15988,7 +15940,6 @@ keyword_sp_var_and_label: | ONLINE_SYM | ONLY_SYM | ORDINALITY_SYM - | OVERLAPS_SYM | PACKAGE_MARIADB_SYM | PACK_KEYS_SYM | PAGE_SYM @@ -16020,10 +15971,10 @@ keyword_sp_var_and_label: | REDOFILE_SYM | REDUNDANT_SYM | RELAY - | RELAYLOG_SYM | RELAY_LOG_FILE_SYM | RELAY_LOG_POS_SYM | RELAY_THREAD + | RELAYLOG_SYM | RELOAD | REORGANIZE_SYM | REPEATABLE_SYM @@ -16038,20 +15989,15 @@ keyword_sp_var_and_label: | REVERSE_SYM | ROLLUP_SYM | ROUTINE_SYM + | ROW_COUNT_SYM | ROWCOUNT_SYM | ROWTYPE_MARIADB_SYM - | ROW_COUNT_SYM | ROW_FORMAT_SYM -%ifdef MARIADB - | ROWNUM_SYM -%endif | RTREE_SYM | SCHEDULE_SYM | SCHEMA_NAME_SYM - | SECOND_SYM | SEQUENCE_SYM | SERIALIZABLE_SYM - | SETVAL_SYM | SIMPLE_SYM | SHARE_SYM | SKIP_SYM @@ -16059,7 +16005,6 @@ keyword_sp_var_and_label: | SLOW | SNAPSHOT_SYM | SOFT_SYM - | SOUNDS_SYM | SOURCE_SYM | SQL_CACHE_SYM | SQL_BUFFER_RESULT @@ -16072,7 +16017,6 @@ keyword_sp_var_and_label: | STORAGE_SYM | STRING_SYM | SUBCLASS_ORIGIN_SYM - | SUBDATE_SYM | SUBJECT_SYM | SUBPARTITION_SYM | SUBPARTITIONS_SYM @@ -16080,9 +16024,6 @@ keyword_sp_var_and_label: | SUSPEND_SYM | SWAPS_SYM | SWITCHES_SYM -%ifdef MARIADB - | SYSDATE -%endif | SYSTEM | SYSTEM_TIME_SYM | TABLE_NAME_SYM @@ -16096,10 +16037,6 @@ keyword_sp_var_and_label: | TRANSACTIONAL_SYM | THREADS_SYM | TRIGGERS_SYM - | TRIM_ORACLE - | TIMESTAMP_ADD - | TIMESTAMP_DIFF - | TYPES_SYM | TYPE_SYM | UDF_RETURNS_SYM | UNCOMMITTED_SYM @@ -16108,23 +16045,61 @@ keyword_sp_var_and_label: | UNDOFILE_SYM | UNKNOWN_SYM | UNTIL_SYM - | USER_SYM %prec PREC_BELOW_CONTRACTION_TOKEN2 | USE_FRM | VARIABLES | VERSIONING_SYM | VIEW_SYM | VIRTUAL_SYM | VISIBLE_SYM - | VALUE_SYM | WARNINGS | WAIT_SYM - | WEEK_SYM - | WEIGHT_STRING_SYM | WITHOUT | WORK_SYM | X509_SYM | XML_SYM | VIA_SYM + | WEEK_SYM + ; + +keyword_func_sp_var_not_label: + FORMAT_SYM + | COLUMN_CHECK_SYM + ; +/* + These keywords are fine for both SP variable names and SP labels. +*/ +keyword_sp_var_and_label: + keyword_func_sp_var_and_label + | ADDDATE_SYM + | ANY_SYM + | AVG_SYM + | CODE_SYM + | DAY_SYM + | GET_FORMAT + | HOUR_SYM + | ID_SYM + | LAST_VALUE + | LASTVAL_SYM + | MINUTE_SYM + | MONTH_SYM + | NEXTVAL_SYM + | OVERLAPS_SYM +%ifdef MARIADB + | ROWNUM_SYM +%endif + | SECOND_SYM + | SETVAL_SYM + | SOUNDS_SYM + | SUBDATE_SYM +%ifdef MARIADB + | SYSDATE +%endif + | TRIM_ORACLE + | TIMESTAMP_ADD + | TIMESTAMP_DIFF + | USER_SYM %prec PREC_BELOW_CONTRACTION_TOKEN2 + | VALUE_SYM + | WEIGHT_STRING_SYM ; @@ -16165,7 +16140,6 @@ reserved_keyword_udt_not_param_type: | CURRENT_USER | CURRENT_ROLE | CURTIME - | DATABASE | DATABASES | DATE_ADD_INTERVAL | DATE_SUB_INTERVAL diff --git a/sql/structs.h b/sql/structs.h index a77bb8cb..318df056 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -34,6 +34,7 @@ struct TABLE; class Type_handler; class Field; class Index_statistics; +struct Lex_ident_cli_st; class THD; @@ -902,12 +903,6 @@ public: } Item *make_item_func_trim_std(THD *thd) const; Item *make_item_func_trim_oracle(THD *thd) const; - /* - This method is still used to handle LTRIM and RTRIM, - while the special syntax TRIM(... BOTH|LEADING|TRAILING) - is now handled by Schema::make_item_func_trim(). - */ - Item *make_item_func_trim(THD *thd) const; }; diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 0df11e0b..dce04c59 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -1037,11 +1037,10 @@ static Sys_var_charptr_fscs Sys_datadir( static Sys_var_dbug Sys_dbug( "debug", "Built-in DBUG debugger", sys_var::SESSION, CMD_LINE(OPT_ARG, '#'), DEFAULT(""), NO_MUTEX_GUARD, NOT_IN_BINLOG, - ON_CHECK(check_has_super), ON_UPDATE(0), - DEPRECATED("'@@debug_dbug'")); // since 5.5.37 + ON_CHECK(check_has_super)); static Sys_var_dbug Sys_debug_dbug( - "debug_dbug", "Built-in DBUG debugger", sys_var::SESSION, + "debug_dbug", "Built-in DBUG debugger. Alias for --debug", sys_var::SESSION, CMD_LINE(OPT_ARG, '#'), DEFAULT(""), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_has_super)); #endif @@ -1538,7 +1537,7 @@ static Sys_var_bit Sys_log_slow_slave_statements( static Sys_var_ulong Sys_log_warnings( "log_warnings", - "Log some not critical warnings to the general log file." + "Log some non critical warnings to the error log." "Value can be between 0 and 11. Higher values mean more verbosity", SESSION_VAR(log_warnings), CMD_LINE(OPT_ARG, 'W'), @@ -2938,6 +2937,19 @@ static Sys_var_ulong Sys_optimizer_trace_max_mem_size( SESSION_VAR(optimizer_trace_max_mem_size), CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, ULONG_MAX), DEFAULT(1024 * 1024), BLOCK_SIZE(1)); +static Sys_var_ulong Sys_optimizer_adjust_secondary_key_costs( + "optimizer_adjust_secondary_key_costs", + "0 = No changes. " + "1 = Update secondary key costs for ranges to be at least 5x of clustered " + "primary key costs. " + "2 = Remove 'max_seek optimization' for secondary keys and slight " + "adjustment of filter cost. " + "This option will be deleted in MariaDB 11.0 as it is not needed with the " + "new 11.0 optimizer.", + SESSION_VAR(optimizer_adjust_secondary_key_costs), CMD_LINE(REQUIRED_ARG), + VALID_RANGE(0, 2), DEFAULT(0), BLOCK_SIZE(1)); + + static Sys_var_charptr_fscs Sys_pid_file( "pid_file", "Pid file used by safe_mysqld", READ_ONLY GLOBAL_VAR(pidfile_name_ptr), CMD_LINE(REQUIRED_ARG), @@ -3348,8 +3360,9 @@ Sys_secure_auth( "secure_auth", "Disallow authentication for accounts that have old (pre-4.1) " "passwords", - GLOBAL_VAR(opt_secure_auth), CMD_LINE(OPT_ARG), - DEFAULT(TRUE)); + GLOBAL_VAR(opt_secure_auth), CMD_LINE(OPT_ARG, OPT_SECURE_AUTH), + DEFAULT(TRUE), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), ON_UPDATE(0), + DEPRECATED("")); // since 10.6.17 static bool check_require_secure_transport(sys_var *self, THD *thd, set_var *var) { @@ -3599,13 +3612,6 @@ static bool fix_rpl_semi_sync_master_wait_point(sys_var *self, THD *thd, return false; } -static bool fix_rpl_semi_sync_master_wait_no_slave(sys_var *self, THD *thd, - enum_var_type type) -{ - repl_semisync_master.check_and_switch(); - return false; -} - static Sys_var_on_access_global<Sys_var_mybool, PRIV_SET_SYSTEM_GLOBAL_VAR_RPL_SEMI_SYNC_MASTER_ENABLED> Sys_semisync_master_enabled( @@ -3632,12 +3638,11 @@ static Sys_var_on_access_global<Sys_var_mybool, PRIV_SET_SYSTEM_GLOBAL_VAR_RPL_SEMI_SYNC_MASTER_WAIT_NO_SLAVE> Sys_semisync_master_wait_no_slave( "rpl_semi_sync_master_wait_no_slave", - "Wait until timeout when no semi-synchronous replication slave " - "available (enabled by default).", + "Wait until timeout when no semi-synchronous replication slave is " + "available.", GLOBAL_VAR(rpl_semi_sync_master_wait_no_slave), CMD_LINE(OPT_ARG), DEFAULT(TRUE), - NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), - ON_UPDATE(fix_rpl_semi_sync_master_wait_no_slave)); + NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0)); static Sys_var_on_access_global<Sys_var_ulong, PRIV_SET_SYSTEM_GLOBAL_VAR_RPL_SEMI_SYNC_MASTER_TRACE_LEVEL> @@ -3664,13 +3669,6 @@ Sys_semisync_master_wait_point( NO_MUTEX_GUARD, NOT_IN_BINLOG,ON_CHECK(0), ON_UPDATE(fix_rpl_semi_sync_master_wait_point)); -static bool fix_rpl_semi_sync_slave_enabled(sys_var *self, THD *thd, - enum_var_type type) -{ - repl_semisync_slave.set_slave_enabled(rpl_semi_sync_slave_enabled != 0); - return false; -} - static bool fix_rpl_semi_sync_slave_trace_level(sys_var *self, THD *thd, enum_var_type type) { @@ -3698,10 +3696,9 @@ static Sys_var_on_access_global<Sys_var_mybool, Sys_semisync_slave_enabled( "rpl_semi_sync_slave_enabled", "Enable semi-synchronous replication slave (disabled by default).", - GLOBAL_VAR(rpl_semi_sync_slave_enabled), + GLOBAL_VAR(global_rpl_semi_sync_slave_enabled), CMD_LINE(OPT_ARG), DEFAULT(FALSE), - NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), - ON_UPDATE(fix_rpl_semi_sync_slave_enabled)); + NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0)); static Sys_var_on_access_global<Sys_var_ulong, PRIV_SET_SYSTEM_GLOBAL_VAR_RPL_SEMI_SYNC_SLAVE_TRACE_LEVEL> @@ -3887,6 +3884,7 @@ static const char *old_mode_names[]= "UTF8_IS_UTF8MB3", "IGNORE_INDEX_ONLY_FOR_JOIN", "COMPAT_5_1_CHECKSUM", + "NO_NULL_COLLATION_IDS", 0 }; @@ -5652,7 +5650,7 @@ Sys_slave_net_timeout( */ ulonglong Sys_var_multi_source_ulonglong:: -get_master_info_ulonglong_value(THD *thd, ptrdiff_t offset) const +get_master_info_ulonglong_value(THD *thd) const { Master_info *mi; ulonglong res= 0; // Default value @@ -5660,7 +5658,7 @@ get_master_info_ulonglong_value(THD *thd, ptrdiff_t offset) const if ((mi= get_master_info(&thd->variables.default_master_connection, Sql_condition::WARN_LEVEL_WARN))) { - res= *((ulonglong*) (((uchar*) mi) + master_info_offset)); + res= (mi->*mi_accessor_func)(); mi->release(); } mysql_mutex_lock(&LOCK_global_system_variables); @@ -5730,7 +5728,7 @@ static bool update_slave_skip_counter(sys_var *self, THD *thd, Master_info *mi) static Sys_var_multi_source_ulonglong Sys_slave_skip_counter( "sql_slave_skip_counter", "Skip the next N events from the master log", SESSION_VAR(slave_skip_counter), NO_CMD_LINE, - MASTER_INFO_VAR(rli.slave_skip_counter), + &Master_info::get_slave_skip_counter, VALID_RANGE(0, UINT_MAX), DEFAULT(0), BLOCK_SIZE(1), ON_UPDATE(update_slave_skip_counter)); @@ -5746,7 +5744,7 @@ static Sys_var_multi_source_ulonglong Sys_max_relay_log_size( "relay log will be rotated automatically when the size exceeds this " "value. If 0 at startup, it's set to max_binlog_size", SESSION_VAR(max_relay_log_size), CMD_LINE(REQUIRED_ARG), - MASTER_INFO_VAR(rli.max_relay_log_size), + &Master_info::get_max_relay_log_size, VALID_RANGE(0, 1024L*1024*1024), DEFAULT(0), BLOCK_SIZE(IO_SIZE), ON_UPDATE(update_max_relay_log_size)); diff --git a/sql/sys_vars.inl b/sql/sys_vars.inl index 385ad897..2fd6d042 100644 --- a/sql/sys_vars.inl +++ b/sql/sys_vars.inl @@ -2378,10 +2378,10 @@ public: like sql_slave_skip_counter are GLOBAL. */ -#define MASTER_INFO_VAR(X) my_offsetof(Master_info, X), sizeof(((Master_info *)0x10)->X) class Sys_var_multi_source_ulonglong; class Master_info; +typedef ulonglong (Master_info::*mi_ulonglong_accessor_function)(void); typedef bool (*on_multi_source_update_function)(sys_var *self, THD *thd, Master_info *mi); bool update_multi_source_variable(sys_var *self, @@ -2390,26 +2390,23 @@ bool update_multi_source_variable(sys_var *self, class Sys_var_multi_source_ulonglong :public Sys_var_ulonglong { - ptrdiff_t master_info_offset; + mi_ulonglong_accessor_function mi_accessor_func; on_multi_source_update_function update_multi_source_variable_func; public: Sys_var_multi_source_ulonglong(const char *name_arg, const char *comment, int flag_args, ptrdiff_t off, size_t size, CMD_LINE getopt, - ptrdiff_t master_info_offset_arg, - size_t master_info_arg_size, + mi_ulonglong_accessor_function mi_accessor_arg, ulonglong min_val, ulonglong max_val, ulonglong def_val, uint block_size, on_multi_source_update_function on_update_func) :Sys_var_ulonglong(name_arg, comment, flag_args, off, size, getopt, min_val, max_val, def_val, block_size, 0, VARIABLE_NOT_IN_BINLOG, 0, update_multi_source_variable), - master_info_offset(master_info_offset_arg), + mi_accessor_func(mi_accessor_arg), update_multi_source_variable_func(on_update_func) - { - SYSVAR_ASSERT(master_info_arg_size == size); - } + { } bool global_update(THD *thd, set_var *var) { return session_update(thd, var); @@ -2423,7 +2420,7 @@ public: { ulonglong *tmp, res; tmp= (ulonglong*) (((uchar*)&(thd->variables)) + offset); - res= get_master_info_ulonglong_value(thd, master_info_offset); + res= get_master_info_ulonglong_value(thd); *tmp= res; return (uchar*) tmp; } @@ -2431,7 +2428,7 @@ public: { return session_value_ptr(thd, base); } - ulonglong get_master_info_ulonglong_value(THD *thd, ptrdiff_t offset) const; + ulonglong get_master_info_ulonglong_value(THD *thd) const; bool update_variable(THD *thd, Master_info *mi) { return update_multi_source_variable_func(this, thd, mi); diff --git a/sql/table.cc b/sql/table.cc index b85d23b2..88b65fe0 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -49,6 +49,7 @@ #ifdef WITH_WSREP #include "wsrep_schema.h" #endif +#include "log_event.h" // MAX_TABLE_MAP_ID /* For MySQL 5.7 virtual fields */ #define MYSQL57_GENERATED_FIELD 128 @@ -108,7 +109,7 @@ LEX_CSTRING MYSQL_PROC_NAME= {STRING_WITH_LEN("proc")}; */ static LEX_CSTRING parse_vcol_keyword= { STRING_WITH_LEN("PARSE_VCOL_EXPR ") }; -static std::atomic<ulong> last_table_id; +static std::atomic<ulonglong> last_table_id; /* Functions defined in this file */ @@ -384,17 +385,20 @@ TABLE_SHARE *alloc_table_share(const char *db, const char *table_name, DBUG_EXECUTE_IF("simulate_big_table_id", if (last_table_id < UINT_MAX32) - last_table_id= UINT_MAX32 - 1;); + last_table_id= UINT_MAX32-1;); /* - There is one reserved number that cannot be used. Remember to - change this when 6-byte global table id's are introduced. + Replication is using 6 bytes as table_map_id. Ensure that + the 6 lowest bytes are not 0. + We also have to ensure that we do not use the special value + UINT_MAX32 as this is used to mark a dummy event row event. See + comments in Rows_log_event::Rows_log_event(). */ do { share->table_map_id= last_table_id.fetch_add(1, std::memory_order_relaxed); - } while (unlikely(share->table_map_id == ~0UL || - share->table_map_id == 0)); + } while (unlikely((share->table_map_id & MAX_TABLE_MAP_ID) == 0) || + unlikely((share->table_map_id & MAX_TABLE_MAP_ID) == UINT_MAX32)); } DBUG_RETURN(share); } @@ -457,7 +461,7 @@ void init_tmp_table_share(THD *thd, TABLE_SHARE *share, const char *key, table_map_id is also used for MERGE tables to suppress repeated compatibility checks. */ - share->table_map_id= (ulong) thd->query_id; + share->table_map_id= (ulonglong) thd->query_id; DBUG_VOID_RETURN; } @@ -1286,12 +1290,11 @@ bool parse_vcol_defs(THD *thd, MEM_ROOT *mem_root, TABLE *table, if (keypart->key_part_flag & HA_PART_KEY_SEG) { int length= keypart->length/keypart->field->charset()->mbmaxlen; + Field *kpf= table->field[keypart->field->field_index]; list_item= new (mem_root) Item_func_left(thd, - new (mem_root) Item_field(thd, keypart->field), + new (mem_root) Item_field(thd, kpf), new (mem_root) Item_int(thd, length)); list_item->fix_fields(thd, NULL); - keypart->field->vcol_info= - table->field[keypart->field->field_index]->vcol_info; } else list_item= new (mem_root) Item_field(thd, keypart->field); @@ -5958,7 +5961,7 @@ allocate: /* Create view fields translation table */ if (!(transl= - (Field_translator*)(thd->stmt_arena-> + (Field_translator*)(thd-> alloc(select->item_list.elements * sizeof(Field_translator))))) { @@ -7585,7 +7588,7 @@ inline void TABLE::mark_index_columns_for_read(uint index) always set and sometimes read. */ -void TABLE::mark_auto_increment_column() +void TABLE::mark_auto_increment_column(bool is_insert) { DBUG_ASSERT(found_next_number_field); /* @@ -7593,7 +7596,8 @@ void TABLE::mark_auto_increment_column() store() to check overflow of auto_increment values */ bitmap_set_bit(read_set, found_next_number_field->field_index); - bitmap_set_bit(write_set, found_next_number_field->field_index); + if (is_insert) + bitmap_set_bit(write_set, found_next_number_field->field_index); if (s->next_number_keypart) mark_index_columns_for_read(s->next_number_index); file->column_bitmaps_signal(); @@ -7718,7 +7722,7 @@ void TABLE::mark_columns_needed_for_update() else { if (found_next_number_field) - mark_auto_increment_column(); + mark_auto_increment_column(false); } if (file->ha_table_flags() & HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) @@ -7794,7 +7798,7 @@ void TABLE::mark_columns_needed_for_insert() triggers->mark_fields_used(TRG_EVENT_INSERT); } if (found_next_number_field) - mark_auto_increment_column(); + mark_auto_increment_column(true); if (default_field) mark_default_fields_for_write(TRUE); if (s->versioned) @@ -10467,6 +10471,12 @@ bool Vers_history_point::check_unit(THD *thd) { if (!item) return false; + if (item->real_type() == Item::FIELD_ITEM) + { + my_error(ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION, MYF(0), + item->full_name(), "FOR SYSTEM_TIME"); + return true; + } if (item->fix_fields_if_needed(thd, &item)) return true; const Type_handler *t= item->this_item()->real_type_handler(); diff --git a/sql/table.h b/sql/table.h index 12ad29b1..ebf582c1 100644 --- a/sql/table.h +++ b/sql/table.h @@ -861,7 +861,7 @@ struct TABLE_SHARE /* 1 if frm version cannot be updated as part of upgrade */ bool keep_original_mysql_version; - ulong table_map_id; /* for row-based replication */ + ulonglong table_map_id; /* for row-based replication */ /* Things that are incompatible between the stored version and the @@ -1023,7 +1023,7 @@ struct TABLE_SHARE return (table_category == TABLE_CATEGORY_LOG); } - inline ulong get_table_def_version() + inline ulonglong get_table_def_version() { return table_map_id; } @@ -1102,7 +1102,7 @@ struct TABLE_SHARE @sa TABLE_LIST::is_the_same_definition() */ - ulong get_table_ref_version() const + ulonglong get_table_ref_version() const { return (tmp_table == SYSTEM_TMP_TABLE) ? 0 : table_map_id; } @@ -1585,7 +1585,7 @@ public: void mark_index_columns_no_reset(uint index, MY_BITMAP *bitmap); void mark_index_columns_for_read(uint index); void restore_column_maps_after_keyread(MY_BITMAP *backup); - void mark_auto_increment_column(void); + void mark_auto_increment_column(bool insert_fl); void mark_columns_needed_for_update(void); void mark_columns_needed_for_delete(void); void mark_columns_needed_for_insert(void); @@ -2088,7 +2088,6 @@ public: void empty() { unit= VERS_TIMESTAMP; item= NULL; } void print(String *str, enum_query_type, const char *prefix, size_t plen) const; bool check_unit(THD *thd); - void bad_expression_data_type_error(const char *type) const; bool eq(const vers_history_point_t &point) const; }; @@ -2827,7 +2826,7 @@ struct TABLE_LIST { set_table_ref_id(s->get_table_ref_type(), s->get_table_ref_version()); } inline void set_table_ref_id(enum_table_ref_type table_ref_type_arg, - ulong table_ref_version_arg) + ulonglong table_ref_version_arg) { m_table_ref_type= table_ref_type_arg; m_table_ref_version= table_ref_version_arg; @@ -2982,7 +2981,7 @@ private: /** See comments for set_table_ref_id() */ enum enum_table_ref_type m_table_ref_type; /** See comments for set_table_ref_id() */ - ulong m_table_ref_version; + ulonglong m_table_ref_version; }; class Item; diff --git a/sql/table_cache.cc b/sql/table_cache.cc index 0039c96a..91292e18 100644 --- a/sql/table_cache.cc +++ b/sql/table_cache.cc @@ -1213,8 +1213,8 @@ int tdc_iterate(THD *thd, my_hash_walk_action action, void *argument, } -int show_tc_active_instances(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope) +int show_tc_active_instances(THD *thd, SHOW_VAR *var, void *buff, + system_status_var *, enum enum_var_type scope) { var->type= SHOW_UINT; var->value= buff; diff --git a/sql/table_cache.h b/sql/table_cache.h index 433df5e0..71704ff2 100644 --- a/sql/table_cache.h +++ b/sql/table_cache.h @@ -87,8 +87,8 @@ extern int tdc_iterate(THD *thd, my_hash_walk_action action, void *argument, bool no_dups= false); extern uint tc_records(void); -int show_tc_active_instances(THD *thd, SHOW_VAR *var, char *buff, - enum enum_var_type scope); +int show_tc_active_instances(THD *thd, SHOW_VAR *var, void *buff, + system_status_var *, enum enum_var_type scope); extern void tc_purge(); extern void tc_add_table(THD *thd, TABLE *table); extern void tc_release_table(TABLE *table); diff --git a/sql/temporary_tables.cc b/sql/temporary_tables.cc index ecbfdde1..fa9abf7b 100644 --- a/sql/temporary_tables.cc +++ b/sql/temporary_tables.cc @@ -1588,6 +1588,11 @@ void THD::close_unused_temporary_table_instances(const TABLE_LIST *tl) { /* Note: removing current list element doesn't invalidate iterator. */ share->all_tmp_tables.remove(table); + /* + At least one instance should be left (guaratead by calling this + function for table which is opened and the table is under processing) + */ + DBUG_ASSERT(share->all_tmp_tables.front()); free_temporary_table(table); } } diff --git a/sql/wsrep_applier.cc b/sql/wsrep_applier.cc index 90ede81a..8767f698 100644 --- a/sql/wsrep_applier.cc +++ b/sql/wsrep_applier.cc @@ -204,6 +204,11 @@ int wsrep_apply_events(THD* thd, (thd->variables.option_bits & ~OPTION_SKIP_REPLICATION) | (ev->flags & LOG_EVENT_SKIP_REPLICATION_F ? OPTION_SKIP_REPLICATION : 0); + if (ev->get_type_code() == GTID_EVENT) + { + thd->variables.option_bits &= ~OPTION_GTID_BEGIN; + } + ev->thd= thd; exec_res= ev->apply_event(thd->wsrep_rgi); DBUG_PRINT("info", ("exec_event result: %d", exec_res)); diff --git a/sql/wsrep_client_service.cc b/sql/wsrep_client_service.cc index d3b4a181..e26cac50 100644 --- a/sql/wsrep_client_service.cc +++ b/sql/wsrep_client_service.cc @@ -281,11 +281,18 @@ enum wsrep::provider::status Wsrep_client_service::replay() original THD state during replication event applying. */ THD *replayer_thd= new THD(true, true); + // Replace the security context of the replayer with the security context + // of the original THD. Since security context class doesn't have proper + // copy constructors, we need to store the original one and set it back + // before destruction so that THD desctruction doesn't cause double-free + // on the replaced security context. + Security_context old_ctx = replayer_thd->main_security_ctx; + replayer_thd->main_security_ctx = m_thd->main_security_ctx; replayer_thd->thread_stack= m_thd->thread_stack; replayer_thd->real_id= pthread_self(); replayer_thd->prior_thr_create_utime= replayer_thd->start_utime= microsecond_interval_timer(); - replayer_thd->set_command(COM_SLEEP); + replayer_thd->mark_connection_idle(); replayer_thd->reset_for_next_command(true); enum wsrep::provider::status ret; @@ -297,6 +304,7 @@ enum wsrep::provider::status Wsrep_client_service::replay() replayer_service.replay_status(ret); } + replayer_thd->main_security_ctx = old_ctx; delete replayer_thd; DBUG_RETURN(ret); } diff --git a/sql/wsrep_dummy.cc b/sql/wsrep_dummy.cc index e1508884..8762dd99 100644 --- a/sql/wsrep_dummy.cc +++ b/sql/wsrep_dummy.cc @@ -167,3 +167,5 @@ void wsrep_report_bf_lock_wait(const THD*, void wsrep_thd_set_PA_unsafe(THD*) {} +uint32 wsrep_get_domain_id() +{ return 0;} diff --git a/sql/wsrep_high_priority_service.cc b/sql/wsrep_high_priority_service.cc index fb93273d..ecb0e487 100644 --- a/sql/wsrep_high_priority_service.cc +++ b/sql/wsrep_high_priority_service.cc @@ -569,6 +569,7 @@ int Wsrep_applier_service::apply_write_set(const wsrep::ws_meta& ws_meta, THD* thd= m_thd; thd->variables.option_bits |= OPTION_BEGIN; + thd->variables.option_bits |= OPTION_GTID_BEGIN; thd->variables.option_bits |= OPTION_NOT_AUTOCOMMIT; DBUG_ASSERT(thd->wsrep_trx().active()); DBUG_ASSERT(thd->wsrep_trx().state() == wsrep::transaction::s_executing); @@ -600,6 +601,8 @@ int Wsrep_applier_service::apply_write_set(const wsrep::ws_meta& ws_meta, thd->wsrep_cs().fragment_applied(ws_meta.seqno()); } thd_proc_info(thd, "wsrep applied write set"); + + thd->variables.option_bits &= ~OPTION_GTID_BEGIN; DBUG_RETURN(ret); } diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc index 0a615228..68649a95 100644 --- a/sql/wsrep_mysqld.cc +++ b/sql/wsrep_mysqld.cc @@ -584,7 +584,8 @@ my_bool wsrep_ready_get (void) return ret; } -int wsrep_show_ready(THD *thd, SHOW_VAR *var, char *buff) +int wsrep_show_ready(THD *thd, SHOW_VAR *var, void *buff, + system_status_var *, enum_var_type) { var->type= SHOW_MY_BOOL; var->value= buff; @@ -1713,7 +1714,13 @@ bool wsrep_sync_wait(THD* thd, enum enum_sql_command command) return res; } -void wsrep_keys_free(wsrep_key_arr_t* key_arr) +typedef struct wsrep_key_arr +{ + wsrep_key_t* keys; + size_t keys_len; +} wsrep_key_arr_t; + +static void wsrep_keys_free(wsrep_key_arr_t* key_arr) { for (size_t i= 0; i < key_arr->keys_len; ++i) { @@ -1729,7 +1736,7 @@ void wsrep_keys_free(wsrep_key_arr_t* key_arr) * @param tables list of tables * @param keys prepared keys - * @return true if parent table append was successfull, otherwise false. + * @return 0 if parent table append was successful, non-zero otherwise. */ bool wsrep_append_fk_parent_table(THD* thd, TABLE_LIST* tables, wsrep::key_array* keys) @@ -1785,6 +1792,8 @@ wsrep_append_fk_parent_table(THD* thd, TABLE_LIST* tables, wsrep::key_array* key } exit: + DEBUG_SYNC(thd, "wsrep_append_fk_toi_keys_before_close_tables"); + /* close the table and release MDL locks */ close_thread_tables(thd); thd->mdl_context.rollback_to_savepoint(mdl_savepoint); @@ -1803,6 +1812,24 @@ exit: } } + /* + MDEV-32938: Check if DDL operation has been killed before. + + It may be that during collecting foreign keys this operation gets BF-aborted + by another already-running TOI operation because it got MDL locks on the same + table for checking foreign keys. + After `close_thread_tables()` has been called it's safe to assume that no-one + can BF-abort this operation as it's not holding any MDL locks any more. + */ + if (!fail) + { + mysql_mutex_lock(&thd->LOCK_thd_kill); + if (thd->killed) + { + fail= true; + } + mysql_mutex_unlock(&thd->LOCK_thd_kill); + } return fail; } @@ -2006,18 +2033,43 @@ err: } /* - * Prepare key list from db/table and table_list + * Prepare key list from db/table and table_list and append it to Wsrep + * with the given key type. * * Return zero in case of success, 1 in case of failure. */ +int wsrep_append_table_keys(THD* thd, + TABLE_LIST* first_table, + TABLE_LIST* table_list, + Wsrep_service_key_type key_type) +{ + wsrep_key_arr_t key_arr= {0, 0}; + const char* db_name= first_table ? first_table->db.str : NULL; + const char* table_name= first_table ? first_table->table_name.str : NULL; + int rcode= wsrep_prepare_keys_for_isolation(thd, db_name, table_name, + table_list, NULL, &key_arr); + + if (!rcode && key_arr.keys_len) + { + rcode= wsrep_thd_append_key(thd, key_arr.keys, + key_arr.keys_len, key_type); + } + + wsrep_keys_free(&key_arr); + return rcode; +} -bool wsrep_prepare_keys_for_isolation(THD* thd, - const char* db, - const char* table, - const TABLE_LIST* table_list, - wsrep_key_arr_t* ka) +extern "C" int wsrep_thd_append_table_key(MYSQL_THD thd, + const char* db, + const char* table, + enum Wsrep_service_key_type key_type) { - return wsrep_prepare_keys_for_isolation(thd, db, table, table_list, NULL, ka); + wsrep_key_arr_t key_arr = {0, 0}; + int ret = wsrep_prepare_keys_for_isolation(thd, db, table, NULL, NULL, &key_arr); + ret = ret || wsrep_thd_append_key(thd, key_arr.keys, + (int)key_arr.keys_len, key_type); + wsrep_keys_free(&key_arr); + return ret; } bool wsrep_prepare_key(const uchar* cache_key, size_t cache_key_len, @@ -2939,6 +2991,15 @@ int wsrep_to_isolation_begin(THD *thd, const char *db_, const char *table_, const wsrep::key_array *fk_tables, const HA_CREATE_INFO *create_info) { + mysql_mutex_lock(&thd->LOCK_thd_kill); + const killed_state killed = thd->killed; + mysql_mutex_unlock(&thd->LOCK_thd_kill); + if (killed) + { + DBUG_ASSERT(FALSE); + return -1; + } + /* No isolation for applier or replaying threads. */ @@ -3689,8 +3750,7 @@ void* start_wsrep_THD(void *arg) thd->security_ctx->skip_grants(); /* handle_one_connection() again... */ - thd->proc_info= 0; - thd->set_command(COM_SLEEP); + thd->mark_connection_idle(); thd->init_for_queries(); mysql_mutex_lock(&LOCK_wsrep_slave_threads); diff --git a/sql/wsrep_mysqld.h b/sql/wsrep_mysqld.h index 3efe3829..14ea07f4 100644 --- a/sql/wsrep_mysqld.h +++ b/sql/wsrep_mysqld.h @@ -162,7 +162,8 @@ extern char* wsrep_cluster_capabilities; int wsrep_show_status(THD *thd, SHOW_VAR *var, void *buff, system_status_var *status_var, enum_var_type scope); -int wsrep_show_ready(THD *thd, SHOW_VAR *var, char *buff); +int wsrep_show_ready(THD *thd, SHOW_VAR *var, void *buff, + system_status_var *, enum_var_type); void wsrep_free_status(THD *thd); void wsrep_update_cluster_state_uuid(const char* str); @@ -503,17 +504,10 @@ void wsrep_init_gtid(); bool wsrep_check_gtid_seqno(const uint32&, const uint32&, uint64&); bool wsrep_get_binlog_gtid_seqno(wsrep_server_gtid_t&); -typedef struct wsrep_key_arr -{ - wsrep_key_t* keys; - size_t keys_len; -} wsrep_key_arr_t; -bool wsrep_prepare_keys_for_isolation(THD* thd, - const char* db, - const char* table, - const TABLE_LIST* table_list, - wsrep_key_arr_t* ka); -void wsrep_keys_free(wsrep_key_arr_t* key_arr); +int wsrep_append_table_keys(THD* thd, + TABLE_LIST* first_table, + TABLE_LIST* table_list, + Wsrep_service_key_type key_type); extern void wsrep_handle_mdl_conflict(MDL_context *requestor_ctx, diff --git a/sql/wsrep_plugin.cc b/sql/wsrep_plugin.cc index d23c51b1..63bdc0a4 100644 --- a/sql/wsrep_plugin.cc +++ b/sql/wsrep_plugin.cc @@ -18,18 +18,6 @@ #include <mysql/plugin.h> -static int wsrep_plugin_init(void *p) -{ - WSREP_DEBUG("wsrep_plugin_init()"); - return 0; -} - -static int wsrep_plugin_deinit(void *p) -{ - WSREP_DEBUG("wsrep_plugin_deinit()"); - return 0; -} - struct Mysql_replication wsrep_plugin= { MYSQL_REPLICATION_INTERFACE_VERSION }; @@ -42,8 +30,8 @@ maria_declare_plugin(wsrep) "Codership Oy", "Wsrep replication plugin", PLUGIN_LICENSE_GPL, - wsrep_plugin_init, - wsrep_plugin_deinit, + NULL, + NULL, 0x0100, NULL, /* Status variables */ NULL, /* System variables */ diff --git a/sql/wsrep_server_service.cc b/sql/wsrep_server_service.cc index 6f902130..c3df6e9f 100644 --- a/sql/wsrep_server_service.cc +++ b/sql/wsrep_server_service.cc @@ -39,7 +39,7 @@ static void init_service_thd(THD* thd, char* thread_stack) thd->thread_stack= thread_stack; thd->real_id= pthread_self(); thd->prior_thr_create_utime= thd->start_utime= microsecond_interval_timer(); - thd->set_command(COM_SLEEP); + thd->mark_connection_idle(); thd->reset_for_next_command(true); server_threads.insert(thd); // as wsrep_innobase_kill_one_trx() uses find_thread_by_id() } diff --git a/sql/wsrep_sst.cc b/sql/wsrep_sst.cc index db138f25..573aa70c 100644 --- a/sql/wsrep_sst.cc +++ b/sql/wsrep_sst.cc @@ -732,7 +732,9 @@ static void* sst_joiner_thread (void* a) { proc.wait(); // Read state ID (UUID:SEQNO) followed by wsrep_gtid_domain_id (if any). + unsigned long int domain_id= wsrep_gtid_domain_id; const char *pos= strchr(out, ' '); + WSREP_DEBUG("SST state ID tmp=%s out=%s pos=%p", tmp, out, pos); if (!pos) { @@ -742,6 +744,13 @@ static void* sst_joiner_thread (void* a) WSREP_WARN("Did not find domain ID from SST script output '%s'. " "Domain ID must be set manually to keep binlog consistent", out); + if (wsrep_gtid_domain_id) + { + WSREP_INFO("This node is configured to use wsrep_gtid_domain_id=%lu by user.", + domain_id); + wsrep_gtid_server.domain_id= (uint32)domain_id; + wsrep_gtid_domain_id= (uint32)domain_id; + } } err= sst_scan_uuid_seqno (out, &ret_uuid, &ret_seqno); @@ -1769,6 +1778,8 @@ static int sst_flush_tables(THD* thd) char content[100]; snprintf(content, sizeof(content), "%s:%lld %d\n", wsrep_cluster_state_uuid, (long long)wsrep_locked_seqno, wsrep_gtid_server.domain_id); + WSREP_DEBUG("sst_flush_tables : %s:%lld %d", wsrep_cluster_state_uuid, + (long long)wsrep_locked_seqno, wsrep_gtid_server.domain_id); err= sst_create_file(flush_success, content); if (err) diff --git a/sql/wsrep_thd.cc b/sql/wsrep_thd.cc index 682e6485..ede2c906 100644 --- a/sql/wsrep_thd.cc +++ b/sql/wsrep_thd.cc @@ -36,7 +36,7 @@ static Wsrep_thd_queue* wsrep_rollback_queue= 0; static Atomic_counter<uint64_t> wsrep_bf_aborts_counter; -int wsrep_show_bf_aborts (THD *thd, SHOW_VAR *var, char *buff, +int wsrep_show_bf_aborts (THD *thd, SHOW_VAR *var, void *, system_status_var *, enum enum_var_type scope) { wsrep_local_bf_aborts= wsrep_bf_aborts_counter; @@ -487,6 +487,7 @@ void wsrep_backup_kill_for_commit(THD *thd) thd->wsrep_trx().state() != wsrep::transaction::s_must_replay) { thd->wsrep_abort_by_kill= thd->killed; + my_free(thd->wsrep_abort_by_kill_err); thd->wsrep_abort_by_kill_err= thd->killed_err; thd->killed= NOT_KILLED; thd->killed_err= 0; @@ -499,6 +500,7 @@ void wsrep_restore_kill_after_commit(THD *thd) DBUG_ASSERT(WSREP(thd)); mysql_mutex_assert_owner(&thd->LOCK_thd_kill); thd->killed= thd->wsrep_abort_by_kill; + my_free(thd->killed_err); thd->killed_err= thd->wsrep_abort_by_kill_err; thd->wsrep_abort_by_kill= NOT_KILLED; thd->wsrep_abort_by_kill_err= 0; diff --git a/sql/wsrep_thd.h b/sql/wsrep_thd.h index f3790887..bf5baf9a 100644 --- a/sql/wsrep_thd.h +++ b/sql/wsrep_thd.h @@ -82,7 +82,7 @@ private: mysql_cond_t COND_wsrep_thd_queue; }; -int wsrep_show_bf_aborts (THD *thd, SHOW_VAR *var, char *buff, +int wsrep_show_bf_aborts (THD *thd, SHOW_VAR *var, void *, system_status_var *, enum enum_var_type scope); bool wsrep_create_appliers(long threads, bool mutex_protected=false); void wsrep_create_rollbacker(); diff --git a/sql/wsrep_trans_observer.h b/sql/wsrep_trans_observer.h index a963a2b1..25e71638 100644 --- a/sql/wsrep_trans_observer.h +++ b/sql/wsrep_trans_observer.h @@ -91,7 +91,13 @@ static inline bool wsrep_is_real(THD* thd, bool all) */ static inline bool wsrep_has_changes(THD* thd) { - return (thd->wsrep_trx().is_empty() == false); + // Transaction has changes to replicate if it + // has appended one or more certification keys, + // and has actual changes to replicate in binlog + // cache. Except for streaming replication, + // where commit message may have no payload. + return !thd->wsrep_trx().is_empty() && + (!wsrep_is_binlog_cache_empty(thd) || thd->wsrep_trx().is_streaming()); } /* |