diff options
Diffstat (limited to 'sql')
89 files changed, 1507 insertions, 755 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 75652c06649..598462d1bba 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -300,7 +300,7 @@ ADD_CUSTOM_TARGET(distclean IF(INSTALL_LAYOUT STREQUAL "STANDALONE") # Copy db.opt into data/test/ -SET(DBOPT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/db.opt ) +SET(DBOPT_FILE ${CMAKE_SOURCE_DIR}/support-files/db.opt ) INSTALL(FILES ${DBOPT_FILE} DESTINATION data/test COMPONENT DataFiles) # Install initial database on windows diff --git a/sql/db.opt b/sql/db.opt deleted file mode 100644 index d8429c4e0de..00000000000 --- a/sql/db.opt +++ /dev/null @@ -1,2 +0,0 @@ -default-character-set=latin1 -default-collation=latin1_swedish_ci diff --git a/sql/events.cc b/sql/events.cc index 944e91a4d21..e7631e33bf9 100644 --- a/sql/events.cc +++ b/sql/events.cc @@ -596,6 +596,8 @@ Events::drop_schema_events(THD *thd, char *db) DBUG_ENTER("Events::drop_schema_events"); DBUG_PRINT("enter", ("dropping events from %s", db)); + DBUG_ASSERT(ok_for_lower_case_names(db)); + /* Sic: no check if the scheduler is disabled or system tables are damaged, as intended. diff --git a/sql/filesort.cc b/sql/filesort.cc index 776ec064365..0d554df6e18 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -1,5 +1,5 @@ -/* Copyright (c) 2000, 2012, Oracle and/or its affiliates. - Copyright (c) 2009, 2012, Monty Program Ab. +/* Copyright (c) 2000, 2014, Oracle and/or its affiliates. + Copyright (c) 2009, 2014, Monty Program Ab. 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 diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index fd6ac3b180a..788254bcc3f 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -8151,7 +8151,6 @@ class ha_partition_inplace_ctx : public inplace_alter_handler_ctx { public: inplace_alter_handler_ctx **handler_ctx_array; - bool rollback_done; private: uint m_tot_parts; @@ -8159,7 +8158,6 @@ public: ha_partition_inplace_ctx(THD *thd, uint tot_parts) : inplace_alter_handler_ctx(), handler_ctx_array(NULL), - rollback_done(false), m_tot_parts(tot_parts) {} @@ -8178,14 +8176,11 @@ enum_alter_inplace_result ha_partition::check_if_supported_inplace_alter(TABLE *altered_table, Alter_inplace_info *ha_alter_info) { -#ifdef PARTITION_SUPPORTS_INPLACE_ALTER uint index= 0; enum_alter_inplace_result result= HA_ALTER_INPLACE_NO_LOCK; ha_partition_inplace_ctx *part_inplace_ctx; + bool first_is_set= false; THD *thd= ha_thd(); -#else - enum_alter_inplace_result result= HA_ALTER_INPLACE_NOT_SUPPORTED; -#endif DBUG_ENTER("ha_partition::check_if_supported_inplace_alter"); /* @@ -8196,34 +8191,21 @@ ha_partition::check_if_supported_inplace_alter(TABLE *altered_table, if (ha_alter_info->alter_info->flags == Alter_info::ALTER_PARTITION) DBUG_RETURN(HA_ALTER_INPLACE_NO_LOCK); -#ifndef PARTITION_SUPPORTS_INPLACE_ALTER - /* - Due to bug#14760210 partitions can be out-of-sync in case - commit_inplace_alter_table fails after the first partition. - - Until we can either commit all partitions at the same time or - have an atomic recover on failure/crash we don't support any - inplace alter. - - TODO: investigate what happens when indexes are out-of-sync - between partitions. If safe and possible to recover from, - then we could allow ADD/DROP INDEX. - */ - DBUG_RETURN(result); -#else part_inplace_ctx= new (thd->mem_root) ha_partition_inplace_ctx(thd, m_tot_parts); if (!part_inplace_ctx) DBUG_RETURN(HA_ALTER_ERROR); part_inplace_ctx->handler_ctx_array= (inplace_alter_handler_ctx **) - thd->alloc(sizeof(inplace_alter_handler_ctx *) * m_tot_parts); + thd->alloc(sizeof(inplace_alter_handler_ctx *) * (m_tot_parts + 1)); if (!part_inplace_ctx->handler_ctx_array) DBUG_RETURN(HA_ALTER_ERROR); - for (index= 0; index < m_tot_parts; index++) + /* Set all to NULL, including the terminating one. */ + for (index= 0; index <= m_tot_parts; index++) part_inplace_ctx->handler_ctx_array[index]= NULL; + ha_alter_info->handler_flags |= Alter_inplace_info::ALTER_PARTITIONED; for (index= 0; index < m_tot_parts; index++) { enum_alter_inplace_result p_result= @@ -8231,15 +8213,32 @@ ha_partition::check_if_supported_inplace_alter(TABLE *altered_table, ha_alter_info); part_inplace_ctx->handler_ctx_array[index]= ha_alter_info->handler_ctx; + if (index == 0) + { + first_is_set= (ha_alter_info->handler_ctx != NULL); + } + else if (first_is_set != (ha_alter_info->handler_ctx != NULL)) + { + /* Either none or all partitions must set handler_ctx! */ + DBUG_ASSERT(0); + DBUG_RETURN(HA_ALTER_ERROR); + } if (p_result < result) result= p_result; if (result == HA_ALTER_ERROR) break; } + ha_alter_info->handler_ctx= part_inplace_ctx; + /* + To indicate for future inplace calls that there are several + partitions/handlers that need to be committed together, + we set group_commit_ctx to the NULL terminated array of + the partitions handlers. + */ + ha_alter_info->group_commit_ctx= part_inplace_ctx->handler_ctx_array; DBUG_RETURN(result); -#endif } @@ -8320,8 +8319,8 @@ bool ha_partition::commit_inplace_alter_table(TABLE *altered_table, Alter_inplace_info *ha_alter_info, bool commit) { - uint index= 0; ha_partition_inplace_ctx *part_inplace_ctx; + bool error= false; DBUG_ENTER("ha_partition::commit_inplace_alter_table"); @@ -8335,117 +8334,52 @@ bool ha_partition::commit_inplace_alter_table(TABLE *altered_table, part_inplace_ctx= static_cast<class ha_partition_inplace_ctx*>(ha_alter_info->handler_ctx); - if (!commit && part_inplace_ctx->rollback_done) - DBUG_RETURN(false); // We have already rolled back changes. - - for (index= 0; index < m_tot_parts; index++) + if (commit) { - ha_alter_info->handler_ctx= part_inplace_ctx->handler_ctx_array[index]; - if (m_file[index]->ha_commit_inplace_alter_table(altered_table, - ha_alter_info, commit)) + DBUG_ASSERT(ha_alter_info->group_commit_ctx == + part_inplace_ctx->handler_ctx_array); + ha_alter_info->handler_ctx= part_inplace_ctx->handler_ctx_array[0]; + error= m_file[0]->ha_commit_inplace_alter_table(altered_table, + ha_alter_info, commit); + if (error) + goto end; + if (ha_alter_info->group_commit_ctx) { - part_inplace_ctx->handler_ctx_array[index]= ha_alter_info->handler_ctx; - goto err; - } - part_inplace_ctx->handler_ctx_array[index]= ha_alter_info->handler_ctx; - DBUG_EXECUTE_IF("ha_partition_fail_final_add_index", { - /* Simulate failure by rollback of the second partition */ - if (m_tot_parts > 1) + /* + If ha_alter_info->group_commit_ctx is not set to NULL, + then the engine did only commit the first partition! + The engine is probably new, since both innodb and the default + implementation of handler::commit_inplace_alter_table sets it to NULL + and simply return false, since it allows metadata changes only. + Loop over all other partitions as to follow the protocol! + */ + uint i; + DBUG_ASSERT(0); + for (i= 1; i < m_tot_parts; i++) { - index++; - ha_alter_info->handler_ctx= part_inplace_ctx->handler_ctx_array[index]; - m_file[index]->ha_commit_inplace_alter_table(altered_table, - ha_alter_info, false); - part_inplace_ctx->handler_ctx_array[index]= ha_alter_info->handler_ctx; - goto err; + ha_alter_info->handler_ctx= part_inplace_ctx->handler_ctx_array[i]; + error|= m_file[i]->ha_commit_inplace_alter_table(altered_table, + ha_alter_info, + true); } - }); } - ha_alter_info->handler_ctx= part_inplace_ctx; - - DBUG_RETURN(false); - -err: - ha_alter_info->handler_ctx= part_inplace_ctx; - /* - Reverting committed changes is (for now) only possible for ADD INDEX - For other changes we will just try to rollback changes. - */ - if (index > 0 && - ha_alter_info->handler_flags & (Alter_inplace_info::ADD_INDEX | - Alter_inplace_info::ADD_UNIQUE_INDEX | - Alter_inplace_info::ADD_PK_INDEX)) - { - Alter_inplace_info drop_info(ha_alter_info->create_info, - ha_alter_info->alter_info, - NULL, 0, - ha_alter_info->modified_part_info, - ha_alter_info->ignore); - - if (ha_alter_info->handler_flags & Alter_inplace_info::ADD_INDEX) - drop_info.handler_flags|= Alter_inplace_info::DROP_INDEX; - if (ha_alter_info->handler_flags & Alter_inplace_info::ADD_UNIQUE_INDEX) - drop_info.handler_flags|= Alter_inplace_info::DROP_UNIQUE_INDEX; - if (ha_alter_info->handler_flags & Alter_inplace_info::ADD_PK_INDEX) - drop_info.handler_flags|= Alter_inplace_info::DROP_PK_INDEX; - drop_info.index_drop_count= ha_alter_info->index_add_count; - drop_info.index_drop_buffer= - (KEY**) ha_thd()->alloc(sizeof(KEY*) * drop_info.index_drop_count); - if (!drop_info.index_drop_buffer) - { - sql_print_error("Failed with error handling of adding index:\n" - "committing index failed, and when trying to revert " - "already committed partitions we failed allocating\n" - "memory for the index for table '%s'", - table_share->table_name.str); - DBUG_RETURN(true); - } - for (uint i= 0; i < drop_info.index_drop_count; i++) - drop_info.index_drop_buffer[i]= - &ha_alter_info->key_info_buffer[ha_alter_info->index_add_buffer[i]]; - - // Drop index for each partition where we already committed new index. - for (uint i= 0; i < index; i++) - { - bool error= m_file[i]->ha_prepare_inplace_alter_table(altered_table, - &drop_info); - error|= m_file[i]->ha_inplace_alter_table(altered_table, &drop_info); - error|= m_file[i]->ha_commit_inplace_alter_table(altered_table, - &drop_info, true); - if (error) - sql_print_error("Failed with error handling of adding index:\n" - "committing index failed, and when trying to revert " - "already committed partitions we failed removing\n" - "the index for table '%s' partition nr %d", - table_share->table_name.str, i); } - - // Rollback uncommitted changes. - for (uint i= index+1; i < m_tot_parts; i++) + else + { + uint i; + for (i= 0; i < m_tot_parts; i++) { + /* Rollback, commit == false, is done for each partition! */ ha_alter_info->handler_ctx= part_inplace_ctx->handler_ctx_array[i]; if (m_file[i]->ha_commit_inplace_alter_table(altered_table, ha_alter_info, false)) - { - /* How could this happen? */ - sql_print_error("Failed with error handling of adding index:\n" - "Rollback of add_index failed for table\n" - "'%s' partition nr %d", - table_share->table_name.str, i); + error= true; } - part_inplace_ctx->handler_ctx_array[i]= ha_alter_info->handler_ctx; } - - // We have now reverted/rolled back changes. Set flag to prevent - // it from being done again. - part_inplace_ctx->rollback_done= true; - - print_error(HA_ERR_NO_PARTITION_FOUND, MYF(0)); - } - +end: ha_alter_info->handler_ctx= part_inplace_ctx; - DBUG_RETURN(true); + DBUG_RETURN(error); } diff --git a/sql/ha_partition.h b/sql/ha_partition.h index 1f55301d14e..eb265363ee5 100644 --- a/sql/ha_partition.h +++ b/sql/ha_partition.h @@ -1284,11 +1284,14 @@ public: } #ifdef WITH_WSREP virtual int wsrep_db_type() const; +#if 0 + // TODO: Verify : https://mariadb.atlassian.net/browse/MDEV-4953 void wsrep_reset_files() { for (uint i=0; i < m_tot_parts; i++) m_file[i]->ha_start_of_new_statement(); } +#endif #endif /* WITH_WSREP */ diff --git a/sql/handler.cc b/sql/handler.cc index af0e1c74b15..b3d4bdf757c 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -362,6 +362,7 @@ int ha_init_errors(void) SETMSG(HA_FTS_INVALID_DOCID, "Invalid InnoDB FTS Doc ID"); SETMSG(HA_ERR_TABLE_IN_FK_CHECK, ER_DEFAULT(ER_TABLE_IN_FK_CHECK)); SETMSG(HA_ERR_DISK_FULL, ER_DEFAULT(ER_DISK_FULL)); + SETMSG(HA_ERR_FTS_TOO_MANY_WORDS_IN_PHRASE, "Too many words in a FTS phrase or proximity search"); /* Register the error messages for use with my_error(). */ return my_error_register(get_handler_errmsgs, HA_ERR_FIRST, HA_ERR_LAST); @@ -2042,6 +2043,41 @@ int ha_release_temporary_latches(THD *thd) return 0; } +/** + Check if all storage engines used in transaction agree that after + rollback to savepoint it is safe to release MDL locks acquired after + savepoint creation. + + @param thd The client thread that executes the transaction. + + @return true - It is safe to release MDL locks. + false - If it is not. +*/ +bool ha_rollback_to_savepoint_can_release_mdl(THD *thd) +{ + Ha_trx_info *ha_info; + THD_TRANS *trans= (thd->in_sub_stmt ? &thd->transaction.stmt : + &thd->transaction.all); + + DBUG_ENTER("ha_rollback_to_savepoint_can_release_mdl"); + + /** + Checking whether it is safe to release metadata locks after rollback to + savepoint in all the storage engines that are part of the transaction. + */ + for (ha_info= trans->ha_list; ha_info; ha_info= ha_info->next()) + { + handlerton *ht= ha_info->ht(); + DBUG_ASSERT(ht); + + if (ht->savepoint_rollback_can_release_mdl == 0 || + ht->savepoint_rollback_can_release_mdl(ht, thd) == false) + DBUG_RETURN(false); + } + + DBUG_RETURN(true); +} + int ha_rollback_to_savepoint(THD *thd, SAVEPOINT *sv) { int error=0; @@ -3922,14 +3958,11 @@ int handler::ha_check(THD *thd, HA_CHECK_OPT *check_opt) if it is started. */ +inline void -handler::mark_trx_read_write_part2() +handler::mark_trx_read_write() { Ha_trx_info *ha_info= &ha_thd()->ha_data[ht->slot].ha_info[0]; - - /* Don't call this function again for this statement */ - mark_trx_done= TRUE; - /* When a storage engine method is called, the transaction must have been started, unless it's a DDL call, for which the @@ -6165,6 +6198,10 @@ void signal_log_not_needed(struct handlerton, char *log_file) DBUG_VOID_RETURN; } +void handler::set_lock_type(enum thr_lock_type lock) +{ + table->reginfo.lock_type= lock; +} #ifdef WITH_WSREP /** diff --git a/sql/handler.h b/sql/handler.h index 7b49ff1ffd4..53e8997acbe 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -1,8 +1,8 @@ #ifndef HANDLER_INCLUDED #define HANDLER_INCLUDED /* - Copyright (c) 2000, 2011, Oracle and/or its affiliates. - Copyright (c) 2009, 2013, Monty Program Ab. + Copyright (c) 2000, 2014, Oracle and/or its affiliates. + Copyright (c) 2009, 2014, Monty Program Ab. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -1026,6 +1026,13 @@ struct handlerton to the savepoint_set call */ int (*savepoint_rollback)(handlerton *hton, THD *thd, void *sv); + /** + Check if storage engine allows to release metadata locks which were + acquired after the savepoint if rollback to savepoint is done. + @return true - If it is safe to release MDL locks. + false - If it is not. + */ + bool (*savepoint_rollback_can_release_mdl)(handlerton *hton, THD *thd); int (*savepoint_release)(handlerton *hton, THD *thd, void *sv); /* 'all' is true if it's a real commit, that makes persistent changes @@ -1678,8 +1685,7 @@ public: All these operations are supported as in-place operations by the SQL layer. This means that operations that by their nature must be performed by copying the table to a temporary table, will not - have their own flags here (e.g. ALTER TABLE FORCE, ALTER TABLE - ENGINE). + have their own flags here. We generally try to specify handler flags only if there are real changes. But in cases when it is cumbersome to determine if some @@ -1783,8 +1789,17 @@ public: // Partition operation with ALL keyword static const HA_ALTER_FLAGS ALTER_ALL_PARTITION = 1L << 28; + /** + Recreate the table for ALTER TABLE FORCE, ALTER TABLE ENGINE + and OPTIMIZE TABLE operations. + */ + static const HA_ALTER_FLAGS RECREATE_TABLE = 1L << 29; + // Virtual columns changed - static const HA_ALTER_FLAGS ALTER_COLUMN_VCOL = 1L << 29; + static const HA_ALTER_FLAGS ALTER_COLUMN_VCOL = 1L << 30; + + // ALTER TABLE for a partitioned table + static const HA_ALTER_FLAGS ALTER_PARTITIONED = 1L << 31; /** Create options (like MAX_ROWS) for the new version of table. @@ -1857,6 +1872,18 @@ public: inplace_alter_handler_ctx *handler_ctx; /** + If the table uses several handlers, like ha_partition uses one handler + per partition, this contains a Null terminated array of ctx pointers + that should all be committed together. + Or NULL if only handler_ctx should be committed. + Set to NULL if the low level handler::commit_inplace_alter_table uses it, + to signal to the main handler that everything was committed as atomically. + + @see inplace_alter_handler_ctx for information about object lifecycle. + */ + inplace_alter_handler_ctx **group_commit_ctx; + + /** Flags describing in detail which operations the storage engine is to execute. */ HA_ALTER_FLAGS handler_flags; @@ -1904,6 +1931,7 @@ public: index_add_count(0), index_add_buffer(NULL), handler_ctx(NULL), + group_commit_ctx(NULL), handler_flags(0), modified_part_info(modified_part_info_arg), ignore(ignore_arg), @@ -2451,7 +2479,6 @@ public: FT_INFO *ft_handler; enum {NONE=0, INDEX, RND} inited; bool implicit_emptied; /* Can be !=0 only if HEAP */ - bool mark_trx_done; const COND *pushed_cond; /** next_insert_id is the next value which should be inserted into the @@ -2532,7 +2559,7 @@ public: in_range_check_pushed_down(FALSE), ref_length(sizeof(my_off_t)), ft_handler(0), inited(NONE), - implicit_emptied(0), mark_trx_done(FALSE), + implicit_emptied(0), pushed_cond(0), next_insert_id(0), insert_id_for_cur_row(0), pushed_idx_cond(NULL), pushed_idx_cond_keyno(MAX_KEY), @@ -2613,13 +2640,6 @@ public: } int ha_rnd_init_with_error(bool scan) __attribute__ ((warn_unused_result)); int ha_reset(); - /* Tell handler (not storage engine) this is start of a new statement */ - void ha_start_of_new_statement() - { - ft_handler= 0; - mark_trx_done= FALSE; - } - /* this is necessary in many places, e.g. in HANDLER command */ int ha_index_or_rnd_end() { @@ -3623,6 +3643,10 @@ protected: @note In case of partitioning, this function might be called for rollback without prepare_inplace_alter_table() having been called first. + Also partitioned tables sets ha_alter_info->group_commit_ctx to a NULL + terminated array of the partitions handlers and if all of them are + committed as one, then group_commit_ctx should be set to NULL to indicate + to the partitioning handler that all partitions handlers are committed. @see prepare_inplace_alter_table(). @param altered_table TABLE object for new version of table. @@ -3637,7 +3661,11 @@ protected: virtual bool commit_inplace_alter_table(TABLE *altered_table, Alter_inplace_info *ha_alter_info, bool commit) - { return false; } +{ + /* Nothing to commit/rollback, mark all handlers committed! */ + ha_alter_info->group_commit_ctx= NULL; + return false; +} /** @@ -3710,12 +3738,8 @@ protected: private: /* Private helpers */ - void mark_trx_read_write_part2(); - inline void mark_trx_read_write() - { - if (!mark_trx_done) - mark_trx_read_write_part2(); - } + inline void mark_trx_read_write(); +private: inline void increment_statistics(ulong SSV::*offset) const; inline void decrement_statistics(ulong SSV::*offset) const; @@ -3921,6 +3945,8 @@ public: inline int ha_write_tmp_row(uchar *buf); inline int ha_update_tmp_row(const uchar * old_data, uchar * new_data); + virtual void set_lock_type(enum thr_lock_type lock); + friend enum icp_result handler_index_cond_check(void* h_arg); protected: Handler_share *get_ha_share_ptr(); @@ -4057,6 +4083,7 @@ int ha_enable_transaction(THD *thd, bool on); /* savepoints */ int ha_rollback_to_savepoint(THD *thd, SAVEPOINT *sv); +bool ha_rollback_to_savepoint_can_release_mdl(THD *thd); int ha_savepoint(THD *thd, SAVEPOINT *sv); int ha_release_savepoint(THD *thd, SAVEPOINT *sv); #ifdef WITH_WSREP diff --git a/sql/hostname.cc b/sql/hostname.cc index 11cd16ac857..c6c58a0db92 100644 --- a/sql/hostname.cc +++ b/sql/hostname.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2012, Oracle and/or its affiliates. +/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. Copyright (c) 2011, 2014, SkySQL Ab. This program is free software; you can redistribute it and/or modify @@ -457,12 +457,12 @@ int ip_to_hostname(struct sockaddr_storage *ip_storage, if (entry) { entry->m_last_seen= now; + *connect_errors= entry->m_errors.m_connect; - if (entry->m_errors.m_connect > max_connect_errors) + if (entry->m_errors.m_connect >= max_connect_errors) { entry->m_errors.m_host_blocked++; entry->set_error_timestamps(now); - *connect_errors= entry->m_errors.m_connect; mysql_mutex_unlock(&hostname_cache->lock); DBUG_RETURN(RC_BLOCKED_HOST); } diff --git a/sql/item.cc b/sql/item.cc index 9b27f730e80..2c963322eb6 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -4742,6 +4742,10 @@ bool is_outer_table(TABLE_LIST *table, SELECT_LEX *select) DBUG_ASSERT(table->select_lex != select); TABLE_LIST *tl; + if (table->belong_to_view && + table->belong_to_view->select_lex == select) + return FALSE; + for (tl= select->master_unit()->derived; tl && tl->is_merged_derived(); select= tl->select_lex, tl= select->master_unit()->derived) @@ -5318,15 +5322,23 @@ mark_non_agg_field: /* Mark selects according to presence of non aggregated fields. Fields from outer selects added to the aggregate function - outer_fields list as its unknown at the moment whether it's + outer_fields list as it's unknown at the moment whether it's aggregated or not. - We're using either the select lex of the cached table (if present) - or the field's resolution context. context->select_lex is - safe for use because it's either the SELECT we want to use - (the current level) or a stub added by non-SELECT queries. + We're using the select lex of the cached table (if present). */ - SELECT_LEX *select_lex= cached_table ? - cached_table->select_lex : field->table->pos_in_table_list->select_lex; + SELECT_LEX *select_lex; + if (cached_table) + select_lex= cached_table->select_lex; + else if (!(select_lex= field->table->pos_in_table_list->select_lex)) + { + /* + This can only happen when there is no real table in the query. + We are using the field's resolution context. context->select_lex is eee + safe for use because it's either the SELECT we want to use + (the current level) or a stub added by non-SELECT queries. + */ + select_lex= context->select_lex; + } if (!thd->lex->in_sum_func) select_lex->set_non_agg_field_used(true); else @@ -6509,6 +6521,7 @@ void Item_date_literal::print(String *str, enum_query_type query_type) bool Item_date_literal::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) { DBUG_ASSERT(fixed); + fuzzy_date |= sql_mode_for_dates(current_thd); *ltime= cached_time; return (null_value= check_date_with_warn(ltime, fuzzy_date, MYSQL_TIMESTAMP_ERROR)); @@ -6528,6 +6541,7 @@ void Item_datetime_literal::print(String *str, enum_query_type query_type) bool Item_datetime_literal::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) { DBUG_ASSERT(fixed); + fuzzy_date |= sql_mode_for_dates(current_thd); *ltime= cached_time; return (null_value= check_date_with_warn(ltime, fuzzy_date, MYSQL_TIMESTAMP_ERROR)); diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 4b9eb37488e..f142c51db4d 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -2763,13 +2763,13 @@ Item_func_nullif::fix_length_and_dec() maybe_null=1; if (args[0]) // Only false if EOM { - max_length=args[0]->max_length; decimals=args[0]->decimals; unsigned_flag= args[0]->unsigned_flag; cached_result_type= args[0]->result_type(); if (cached_result_type == STRING_RESULT && agg_arg_charsets_for_comparison(collation, args, arg_count)) return; + fix_char_length(args[0]->max_char_length()); } } @@ -5055,6 +5055,11 @@ bool Item_func_like::find_selective_predicates_list_processor(uchar *arg) } +int Regexp_processor_pcre::default_regex_flags() +{ + return default_regex_flags_pcre(current_thd); +} + /** Convert string to lib_charset, if needed. diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 4b3acf83f85..bf28b00c908 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -1513,9 +1513,10 @@ public: m_library_charset(&my_charset_utf8_general_ci), m_subpatterns_needed(0) {} + int default_regex_flags(); void init(CHARSET_INFO *data_charset, int extra_flags, uint nsubpatterns) { - m_library_flags= extra_flags | + m_library_flags= default_regex_flags() | extra_flags | (data_charset != &my_charset_bin ? (PCRE_UTF8 | PCRE_UCP) : 0) | ((data_charset->state & diff --git a/sql/item_func.cc b/sql/item_func.cc index c3fd95c2b2d..572c9fdde60 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -6262,18 +6262,39 @@ bool Item_func_match::fix_fields(THD *thd, Item **ref) bool allows_multi_table_search= true; const_item_cache=0; + table= 0; for (uint i=1 ; i < arg_count ; i++) { item=args[i]; if (item->type() == Item::REF_ITEM) args[i]= item= *((Item_ref *)item)->ref; - if (item->type() != Item::FIELD_ITEM) + /* + When running in PS mode, some Item_field's can already be replaced + to Item_func_conv_charset during PREPARE time. This is possible + in case of "MATCH (f1,..,fN) AGAINST (... IN BOOLEAN MODE)" + when running without any fulltext indexes and when fields f1..fN + have different character sets. + So we check for FIELD_ITEM only during prepare time and in non-PS mode, + and do not check in PS execute time. + */ + if (!thd->stmt_arena->is_stmt_execute() && + item->type() != Item::FIELD_ITEM) { my_error(ER_WRONG_ARGUMENTS, MYF(0), "AGAINST"); return TRUE; } - allows_multi_table_search &= - allows_search_on_non_indexed_columns(((Item_field *)item)->field->table); + /* + During the prepare-time execution of fix_fields() of a PS query some + Item_fields's could have been already replaced to Item_func_conv_charset + (by the call for agg_arg_charsets_for_comparison below()). + But agg_arg_charsets_for_comparison() is written in a way that + at least *one* of the Item_field's is not replaced. + This makes sure that "table" gets initialized during PS execution time. + */ + if (item->type() == Item::FIELD_ITEM) + table= ((Item_field *)item)->field->table; + + allows_multi_table_search &= allows_search_on_non_indexed_columns(table); } /* @@ -6289,15 +6310,13 @@ bool Item_func_match::fix_fields(THD *thd, Item **ref) my_error(ER_WRONG_ARGUMENTS,MYF(0),"MATCH"); return TRUE; } - table=((Item_field *)item)->field->table; if (!(table->file->ha_table_flags() & HA_CAN_FULLTEXT)) { my_error(ER_TABLE_CANT_HANDLE_FT, MYF(0), table->file->table_type()); return 1; } table->fulltext_searched=1; - return agg_item_collations_for_comparison(cmp_collation, func_name(), - args+1, arg_count-1, 0); + return agg_arg_charsets_for_comparison(cmp_collation, args+1, arg_count-1); } bool Item_func_match::fix_index() diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 1ae080ba22d..100d54133dd 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -1604,7 +1604,7 @@ String *Item_str_conv::val_str(String *str) if (multiply == 1) { uint len; - res= copy_if_not_alloced(str,res,res->length()); + res= copy_if_not_alloced(&tmp_value, res, res->length()); len= converter(collation.collation, (char*) res->ptr(), res->length(), (char*) res->ptr(), res->length()); DBUG_ASSERT(len <= res->length()); @@ -1810,8 +1810,10 @@ void Item_func_substr_index::fix_length_and_dec() String *Item_func_substr_index::val_str(String *str) { DBUG_ASSERT(fixed == 1); + char buff[MAX_FIELD_WIDTH]; + String tmp(buff,sizeof(buff),system_charset_info); String *res= args[0]->val_str(str); - String *delimiter= args[1]->val_str(&tmp_value); + String *delimiter= args[1]->val_str(&tmp); int32 count= (int32) args[2]->val_int(); uint offset; @@ -1918,6 +1920,8 @@ String *Item_func_substr_index::val_str(String *str) break; } } + if (count) + return res; // Didn't find, return org string } } /* diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 67af49c4f9f..38bb3121ed8 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -2626,7 +2626,7 @@ static bool check_equality_for_exist2in(Item_func *func, typedef struct st_eq_field_outer { - Item_func **eq_ref; + Item **eq_ref; Item_ident *local_field; Item *outer_exp; } EQ_FIELD_OUTER; @@ -2665,7 +2665,7 @@ static bool find_inner_outer_equalities(Item **conds, &element.outer_exp)) { found= TRUE; - element.eq_ref= (Item_func **)li.ref(); + element.eq_ref= li.ref(); if (result.append(element)) goto alloc_err; } @@ -2677,7 +2677,7 @@ static bool find_inner_outer_equalities(Item **conds, &element.outer_exp)) { found= TRUE; - element.eq_ref= (Item_func **)conds; + element.eq_ref= conds; if (result.append(element)) goto alloc_err; } @@ -2700,7 +2700,7 @@ bool Item_exists_subselect::exists2in_processor(uchar *opt_arg) THD *thd= (THD *)opt_arg; SELECT_LEX *first_select=unit->first_select(), *save_select; JOIN *join= first_select->join; - Item_func *eq= NULL, **eq_ref= NULL; + Item **eq_ref= NULL; Item_ident *local_field= NULL; Item *outer_exp= NULL; Item *left_exp= NULL; Item_in_subselect *in_subs; @@ -2774,7 +2774,6 @@ bool Item_exists_subselect::exists2in_processor(uchar *opt_arg) { Item *item= it++; eq_ref= eqs.at(i).eq_ref; - eq= *eq_ref; local_field= eqs.at(i).local_field; outer_exp= eqs.at(i).outer_exp; /* Add the field to the SELECT_LIST */ @@ -2789,10 +2788,7 @@ bool Item_exists_subselect::exists2in_processor(uchar *opt_arg) /* remove the parts from condition */ if (!upper_not || !local_field->maybe_null) - { - eq->arguments()[0]= new Item_int(1); - eq->arguments()[1]= new Item_int(1); - } + *eq_ref= new Item_int(1); else { *eq_ref= new Item_func_isnotnull( diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 4d261e7a7d9..0aaeb3d55db 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -2523,10 +2523,10 @@ bool Item_date_typecast::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) if (get_arg0_date(ltime, fuzzy_date & ~TIME_TIME_ONLY)) return 1; - ltime->hour= ltime->minute= ltime->second= ltime->second_part= 0; - ltime->time_type= MYSQL_TIMESTAMP_DATE; - return (null_value= check_date_with_warn(ltime, fuzzy_date, - MYSQL_TIMESTAMP_DATE)); + if (make_date_with_warn(ltime, fuzzy_date, MYSQL_TIMESTAMP_DATE)) + return (null_value= 1); + + return 0; } @@ -3144,7 +3144,7 @@ void Item_func_str_to_date::fix_length_and_dec() } cached_field_type= MYSQL_TYPE_DATETIME; - decimals= NOT_FIXED_DEC; + decimals= TIME_SECOND_PART_DIGITS; if ((const_item= args[1]->const_item())) { char format_buff[64]; diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index 8f881487e21..29badddad8e 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -809,7 +809,7 @@ public: bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); void fix_length_and_dec() { - decimals= args[0]->decimals; + decimals= MY_MIN(args[0]->decimals, TIME_SECOND_PART_DIGITS); Item_timefunc::fix_length_and_dec(); } const char *func_name() const { return "sec_to_time"; } diff --git a/sql/lock.cc b/sql/lock.cc index 793b56f965d..082a5827908 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -331,6 +331,7 @@ bool mysql_lock_tables(THD *thd, MYSQL_LOCK *sql_lock, uint flags) (void) unlock_external(thd, sql_lock->table, sql_lock->table_count); end: + THD_STAGE_INFO(thd, stage_after_table_lock); #ifdef WITH_WSREP thd_proc_info(thd, "mysql_lock_tables(): unlocking tables II"); #else /* WITH_WSREP */ @@ -877,6 +878,8 @@ bool lock_object_name(THD *thd, MDL_key::enum_mdl_namespace mdl_type, MDL_request schema_request; MDL_request mdl_request; + DBUG_ASSERT(ok_for_lower_case_names(db)); + if (thd->locked_tables_mode) { my_message(ER_LOCK_OR_ACTIVE_TRANSACTION, diff --git a/sql/log.cc b/sql/log.cc index 0ac5a4818aa..0da411d42e2 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -76,6 +76,8 @@ static int binlog_init(void *p); static int binlog_close_connection(handlerton *hton, THD *thd); static int binlog_savepoint_set(handlerton *hton, THD *thd, void *sv); static int binlog_savepoint_rollback(handlerton *hton, THD *thd, void *sv); +static bool binlog_savepoint_rollback_can_release_mdl(handlerton *hton, + THD *thd); static int binlog_commit(handlerton *hton, THD *thd, bool all); static int binlog_rollback(handlerton *hton, THD *thd, bool all); static int binlog_prepare(handlerton *hton, THD *thd, bool all); @@ -1770,6 +1772,8 @@ int binlog_init(void *p) binlog_hton->close_connection= binlog_close_connection; binlog_hton->savepoint_set= binlog_savepoint_set; binlog_hton->savepoint_rollback= binlog_savepoint_rollback; + binlog_hton->savepoint_rollback_can_release_mdl= + binlog_savepoint_rollback_can_release_mdl; binlog_hton->commit= binlog_commit; binlog_hton->rollback= binlog_rollback; binlog_hton->prepare= binlog_prepare; @@ -2028,6 +2032,32 @@ static int binlog_prepare(handlerton *hton, THD *thd, bool all) return 0; } +/* + We flush the cache wrapped in a beging/rollback if: + . aborting a single or multi-statement transaction and; + . the OPTION_KEEP_LOG is active or; + . the format is STMT and a non-trans table was updated or; + . the format is MIXED and a temporary non-trans table was + updated or; + . the format is MIXED, non-trans table was updated and + aborting a single statement transaction; +*/ +static bool trans_cannot_safely_rollback(THD *thd, bool all) +{ + binlog_cache_mngr *const cache_mngr= + (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton); + + return ((thd->variables.option_bits & OPTION_KEEP_LOG) || + (trans_has_updated_non_trans_table(thd) && + WSREP_FORMAT(thd->variables.binlog_format) == BINLOG_FORMAT_STMT) || + (cache_mngr->trx_cache.changes_to_non_trans_temp_table() && + WSREP_FORMAT(thd->variables.binlog_format) == BINLOG_FORMAT_MIXED) || + (trans_has_updated_non_trans_table(thd) && + ending_single_stmt_trans(thd,all) && + WSREP_FORMAT(thd->variables.binlog_format) == BINLOG_FORMAT_MIXED)); +} + + /** This function is called once after each statement. @@ -2157,26 +2187,8 @@ static int binlog_rollback(handlerton *hton, THD *thd, bool all) error |= binlog_truncate_trx_cache(thd, cache_mngr, all); } else if (!error) - { - /* - We flush the cache wrapped in a beging/rollback if: - . aborting a single or multi-statement transaction and; - . the OPTION_KEEP_LOG is active or; - . the format is STMT and a non-trans table was updated or; - . the format is MIXED and a temporary non-trans table was - updated or; - . the format is MIXED, non-trans table was updated and - aborting a single statement transaction; - */ - if (ending_trans(thd, all) && - ((thd->variables.option_bits & OPTION_KEEP_LOG) || - (trans_has_updated_non_trans_table(thd) && - WSREP_FORMAT(thd->variables.binlog_format) == BINLOG_FORMAT_STMT) || - (cache_mngr->trx_cache.changes_to_non_trans_temp_table() && - WSREP_FORMAT(thd->variables.binlog_format) == BINLOG_FORMAT_MIXED) || - (trans_has_updated_non_trans_table(thd) && - ending_single_stmt_trans(thd,all) && - WSREP_FORMAT(thd->variables.binlog_format) == BINLOG_FORMAT_MIXED))) + { + if (ending_trans(thd, all) && trans_cannot_safely_rollback(thd, all)) error= binlog_rollback_flush_trx_cache(thd, all, cache_mngr); /* Truncate the cache if: @@ -2366,6 +2378,30 @@ static int binlog_savepoint_rollback(handlerton *hton, THD *thd, void *sv) } +/** + Check whether binlog state allows to safely release MDL locks after + rollback to savepoint. + + @param hton The binlog handlerton. + @param thd The client thread that executes the transaction. + + @return true - It is safe to release MDL locks. + false - If it is not. +*/ +static bool binlog_savepoint_rollback_can_release_mdl(handlerton *hton, + THD *thd) +{ + DBUG_ENTER("binlog_savepoint_rollback_can_release_mdl"); + /* + If we have not updated any non-transactional tables rollback + to savepoint will simply truncate binlog cache starting from + SAVEPOINT command. So it should be safe to release MDL acquired + after SAVEPOINT command in this case. + */ + DBUG_RETURN(!trans_cannot_safely_rollback(thd, true)); +} + + int check_binlog_magic(IO_CACHE* log, const char** errmsg) { uchar magic[4]; diff --git a/sql/log_event.cc b/sql/log_event.cc index 9886e90bb41..c8b394e8290 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -1,6 +1,6 @@ /* - Copyright (c) 2000, 2013, Oracle and/or its affiliates. - Copyright (c) 2009, 2013, Monty Program Ab. + Copyright (c) 2000, 2014, Oracle and/or its affiliates. + Copyright (c) 2009, 2014, Monty Program Ab. 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 @@ -41,6 +41,7 @@ #include "transaction.h" #include <my_dir.h> #include "sql_show.h" // append_identifier +#include <mysql/psi/mysql_statement.h> #include <strfunc.h> #include "compat56.h" @@ -4167,7 +4168,8 @@ int Query_log_event::do_apply_event(rpl_group_info *rgi, (sql_mode & ~(ulong) MODE_NO_DIR_IN_CREATE)); if (charset_inited) { - if (rli->cached_charset_compare(charset)) + rpl_sql_thread_info *sql_info= thd->system_thread_info.rpl_sql_info; + if (sql_info->cached_charset_compare(charset)) { /* Verify that we support the charsets found in the event. */ if (!(thd->variables.character_set_client= @@ -4183,7 +4185,7 @@ int Query_log_event::do_apply_event(rpl_group_info *rgi, stop with EE_UNKNOWN_CHARSET in compare_errors (unless set to ignore this error). */ - set_slave_thread_default_charset(thd, rli); + set_slave_thread_default_charset(thd, rgi); goto compare_errors; } thd->update_charset(); // for the charset change to take effect @@ -4285,6 +4287,13 @@ int Query_log_event::do_apply_event(rpl_group_info *rgi, Parser_state parser_state; if (!parser_state.init(thd, thd->query(), thd->query_length())) { + thd->m_statement_psi= MYSQL_START_STATEMENT(&thd->m_statement_state, + stmt_info_rpl.m_key, + thd->db, thd->db_length, + thd->charset()); + THD_STAGE_INFO(thd, stage_init); + MYSQL_SET_STATEMENT_TEXT(thd->m_statement_psi, thd->query(), thd->query_length()); + mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); /* Finalize server status flags after executing a statement. */ thd->update_server_status(); @@ -4468,6 +4477,11 @@ end: thd->set_db(NULL, 0); /* will free the current database */ thd->reset_query(); DBUG_PRINT("info", ("end: query= 0")); + + /* Mark the statement completed. */ + MYSQL_END_STATEMENT(thd->m_statement_psi, thd->get_stmt_da()); + thd->m_statement_psi= NULL; + /* As a disk space optimization, future masters will not log an event for LAST_INSERT_ID() if that function returned 0 (and thus they will be able @@ -4484,18 +4498,7 @@ end: int Query_log_event::do_update_pos(rpl_group_info *rgi) { - /* - Note that we will not increment group* positions if we are just - after a SET ONE_SHOT, because SET ONE_SHOT should not be separated - from its following updating query. - */ - if (thd->one_shot_set) - { - rgi->inc_event_relay_log_pos(); - return 0; - } - else - return Log_event::do_update_pos(rgi); + return Log_event::do_update_pos(rgi); } @@ -6240,7 +6243,7 @@ int Rotate_log_event::do_update_pos(rpl_group_info *rgi) master is 4.0 then the events are in the slave's format (conversion). */ set_slave_thread_options(thd); - set_slave_thread_default_charset(thd, rli); + set_slave_thread_default_charset(thd, rgi); thd->variables.sql_mode= global_system_variables.sql_mode; thd->variables.auto_increment_increment= thd->variables.auto_increment_offset= 1; @@ -9501,8 +9504,31 @@ int Rows_log_event::do_add_row_data(uchar *row_data, size_t length) if (static_cast<size_t>(m_rows_end - m_rows_cur) <= length) { size_t const block_size= 1024; - my_ptrdiff_t const cur_size= m_rows_cur - m_rows_buf; - my_ptrdiff_t const new_alloc= + ulong cur_size= m_rows_cur - m_rows_buf; + DBUG_EXECUTE_IF("simulate_too_big_row_case1", + cur_size= UINT_MAX32 - (block_size * 10); + length= UINT_MAX32 - (block_size * 10);); + DBUG_EXECUTE_IF("simulate_too_big_row_case2", + cur_size= UINT_MAX32 - (block_size * 10); + length= block_size * 10;); + DBUG_EXECUTE_IF("simulate_too_big_row_case3", + cur_size= block_size * 10; + length= UINT_MAX32 - (block_size * 10);); + DBUG_EXECUTE_IF("simulate_too_big_row_case4", + cur_size= UINT_MAX32 - (block_size * 10); + length= (block_size * 10) - block_size + 1;); + ulong remaining_space= UINT_MAX32 - cur_size; + /* Check that the new data fits within remaining space and we can add + block_size without wrapping. + */ + if (length > remaining_space || + ((length + block_size) > remaining_space)) + { + sql_print_error("The row data is greater than 4GB, which is too big to " + "write to the binary log."); + DBUG_RETURN(ER_BINLOG_ROW_LOGGING_FAILED); + } + ulong const new_alloc= block_size * ((cur_size + length + block_size - 1) / block_size); uchar* const new_buf= (uchar*)my_realloc((uchar*)m_rows_buf, (uint) new_alloc, @@ -9834,10 +9860,14 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi) /* Bug#56662 Assertion failed: next_insert_id == 0, file handler.cc Don't allow generation of auto_increment value when processing - rows event by setting 'MODE_NO_AUTO_VALUE_ON_ZERO'. + rows event by setting 'MODE_NO_AUTO_VALUE_ON_ZERO'. The exception + to this rule happens when the auto_inc column exists on some + extra columns on the slave. In that case, do not force + MODE_NO_AUTO_VALUE_ON_ZERO. */ ulonglong saved_sql_mode= thd->variables.sql_mode; - thd->variables.sql_mode= MODE_NO_AUTO_VALUE_ON_ZERO; + if (!is_auto_inc_in_extra_columns()) + thd->variables.sql_mode= MODE_NO_AUTO_VALUE_ON_ZERO; // row processing loop @@ -11171,9 +11201,28 @@ Write_rows_log_event::do_before_row_operations(const Slave_reporting_capability * table->auto_increment_field_not_null and SQL_MODE(if includes * MODE_NO_AUTO_VALUE_ON_ZERO) in update_auto_increment function. * SQL_MODE of slave sql thread is always consistency with master's. - * In RBR, auto_increment fields never are NULL. + * In RBR, auto_increment fields never are NULL, except if the auto_inc + * column exists only on the slave side (i.e., in an extra column + * on the slave's table). */ - m_table->auto_increment_field_not_null= TRUE; + if (!is_auto_inc_in_extra_columns()) + m_table->auto_increment_field_not_null= TRUE; + else + { + /* + Here we have checked that there is an extra field + on this server's table that has an auto_inc column. + + Mark that the auto_increment field is null and mark + the read and write set bits. + + (There can only be one AUTO_INC column, it is always + indexed and it cannot have a DEFAULT value). + */ + m_table->auto_increment_field_not_null= FALSE; + m_table->mark_auto_increment_column(); + } + return error; } @@ -11182,6 +11231,19 @@ Write_rows_log_event::do_after_row_operations(const Slave_reporting_capability * int error) { int local_error= 0; + + /** + Clear the write_set bit for auto_inc field that only + existed on the destination table as an extra column. + */ + if (is_auto_inc_in_extra_columns()) + { + bitmap_clear_bit(m_table->write_set, m_table->next_number_field->field_index); + bitmap_clear_bit( m_table->read_set, m_table->next_number_field->field_index); + + if (get_flags(STMT_END_F)) + m_table->file->ha_release_auto_increment(); + } m_table->next_number_field=0; m_table->auto_increment_field_not_null= FALSE; if ((slave_exec_mode == SLAVE_EXEC_MODE_IDEMPOTENT) || @@ -11334,7 +11396,13 @@ Rows_log_event::write_row(rpl_group_info *rgi, ulong estimated_rows= (m_rows_end - m_curr_row) / (m_curr_row_end - m_curr_row); table->file->ha_start_bulk_insert(estimated_rows); } - + + /* + Explicitly set the auto_inc to null to make sure that + it gets an auto_generated value. + */ + if (is_auto_inc_in_extra_columns()) + m_table->next_number_field->set_null(); #ifndef DBUG_OFF DBUG_DUMP("record[0]", table->record[0], table->s->reclength); diff --git a/sql/log_event.h b/sql/log_event.h index bba1b907a57..020af59ae81 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -1,5 +1,5 @@ -/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. - Copyright (c) 2009, 2013, Monty Program Ab. +/* Copyright (c) 2000, 2014, Oracle and/or its affiliates. + Copyright (c) 2009, 2014, Monty Program Ab. 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 @@ -663,7 +663,8 @@ enum Log_event_type PRE_GA_DELETE_ROWS_EVENT = 22, /* - These event numbers are used from 5.1.16 until mysql-trunk-xx + These event numbers are used from 5.1.16 until mysql-5.6.6, + and in MariaDB */ WRITE_ROWS_EVENT_V1 = 23, UPDATE_ROWS_EVENT_V1 = 24, @@ -685,11 +686,13 @@ enum Log_event_type data to the slave: data that a slave can handle in case there is code for handling it, but which can be ignored if it is not recognized. + + These mysql-5.6 events are not recognized (and ignored) by MariaDB */ IGNORABLE_LOG_EVENT= 28, ROWS_QUERY_LOG_EVENT= 29, - /* Version 2 of the Row events */ + /* Version 2 of the Row events, generated only by mysql-5.6.6+ */ WRITE_ROWS_EVENT = 30, UPDATE_ROWS_EVENT = 31, DELETE_ROWS_EVENT = 32, @@ -4340,7 +4343,21 @@ protected: bool process_triggers(trg_event_type event, trg_action_time_type time_type, bool old_row_is_record1); -#endif //defined(MYSQL_SERVER) && defined(HAVE_REPLICATION) + + /** + Helper function to check whether there is an auto increment + column on the table where the event is to be applied. + + @return true if there is an autoincrement field on the extra + columns, false otherwise. + */ + inline bool is_auto_inc_in_extra_columns() + { + DBUG_ASSERT(m_table); + return (m_table->next_number_field && + m_table->next_number_field->field_index >= m_width); + } +#endif private: diff --git a/sql/mdl.cc b/sql/mdl.cc index 8360243b4da..5755b2bbfd5 100644 --- a/sql/mdl.cc +++ b/sql/mdl.cc @@ -730,6 +730,7 @@ static inline int mdl_iterate_lock(MDL_lock *lock, int mdl_iterate(int (*callback)(MDL_ticket *ticket, void *arg), void *arg) { + DYNAMIC_ARRAY locks; uint i, j; int res; DBUG_ENTER("mdl_iterate"); @@ -738,18 +739,48 @@ int mdl_iterate(int (*callback)(MDL_ticket *ticket, void *arg), void *arg) (res= mdl_iterate_lock(mdl_locks.m_commit_lock, callback, arg))) DBUG_RETURN(res); + my_init_dynamic_array(&locks, sizeof(MDL_lock*), 512, 1, MYF(0)); + for (i= 0; i < mdl_locks.m_partitions.elements(); i++) { MDL_map_partition *part= mdl_locks.m_partitions.at(i); + /* Collect all locks first */ mysql_mutex_lock(&part->m_mutex); + if (allocate_dynamic(&locks, part->m_locks.records)) + { + res= 1; + mysql_mutex_unlock(&part->m_mutex); + break; + } + reset_dynamic(&locks); for (j= 0; j < part->m_locks.records; j++) { - if ((res= mdl_iterate_lock((MDL_lock*) my_hash_element(&part->m_locks, j), - callback, arg))) - break; + MDL_lock *lock= (MDL_lock*) my_hash_element(&part->m_locks, j); + lock->m_ref_usage++; + insert_dynamic(&locks, &lock); } mysql_mutex_unlock(&part->m_mutex); + + /* Now show them */ + for (j= 0; j < locks.elements; j++) + { + MDL_lock *lock= (MDL_lock*) *dynamic_element(&locks, j, MDL_lock**); + res= mdl_iterate_lock(lock, callback, arg); + + mysql_prlock_wrlock(&lock->m_rwlock); + uint ref_usage= lock->m_ref_usage; + uint ref_release= ++lock->m_ref_release; + bool is_destroyed= lock->m_is_destroyed; + mysql_prlock_unlock(&lock->m_rwlock); + + if (unlikely(is_destroyed && ref_usage == ref_release)) + MDL_lock::destroy(lock); + + if (res) + break; + } } + delete_dynamic(&locks); DBUG_RETURN(res); } diff --git a/sql/mdl.h b/sql/mdl.h index 1601d5a2634..639a8966b33 100644 --- a/sql/mdl.h +++ b/sql/mdl.h @@ -37,6 +37,7 @@ class THD; class MDL_context; class MDL_lock; class MDL_ticket; +bool ok_for_lower_case_names(const char *name); /** @def ENTER_COND(C, M, S, O) @@ -350,6 +351,7 @@ public: NAME_LEN) - m_ptr + 1); m_hash_value= my_hash_sort(&my_charset_bin, (uchar*) m_ptr + 1, m_length - 1); + DBUG_ASSERT(ok_for_lower_case_names(db)); } void mdl_key_init(const MDL_key *rhs) { diff --git a/sql/mysqld.cc b/sql/mysqld.cc index bdd5b59206e..89d0b0281b2 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. +/* Copyright (c) 2000, 2014, Oracle and/or its affiliates. Copyright (c) 2008, 2014, SkySQL Ab. This program is free software; you can redistribute it and/or modify @@ -344,6 +344,13 @@ static PSI_rwlock_key key_rwlock_openssl; volatile sig_atomic_t ld_assume_kernel_is_set= 0; #endif +/** + Statement instrumentation key for replication. +*/ +#ifdef HAVE_PSI_STATEMENT_INTERFACE +PSI_statement_info stmt_info_rpl; +#endif + /* the default log output is log tables */ static bool lower_case_table_names_used= 0; static bool max_long_data_size_used= false; @@ -2368,7 +2375,7 @@ static struct passwd *check_user(const char *user) } if (!user) { - if (!opt_bootstrap) + if (!opt_bootstrap && !opt_help) { sql_print_error("Fatal error: Please read \"Security\" section of the manual to find out how to run mysqld as root!\n"); unireg_abort(1); @@ -3940,7 +3947,7 @@ void init_com_statement_info() com_statement_info[index].m_flags= 0; } - /* "statement/com/query" can mutate into "statement/sql/..." */ + /* "statement/abstract/query" can mutate into "statement/sql/..." */ com_statement_info[(uint) COM_QUERY].m_flags= PSI_FLAG_MUTABLE; } #endif @@ -10129,6 +10136,8 @@ static PSI_file_info all_server_files[]= #endif /* HAVE_PSI_INTERFACE */ PSI_stage_info stage_after_create= { 0, "After create", 0}; +PSI_stage_info stage_after_opening_tables= { 0, "After opening tables", 0}; +PSI_stage_info stage_after_table_lock= { 0, "After table lock", 0}; PSI_stage_info stage_allocating_local_table= { 0, "allocating local table", 0}; PSI_stage_info stage_alter_inplace_prepare= { 0, "preparing for alter table", 0}; PSI_stage_info stage_alter_inplace= { 0, "altering table", 0}; @@ -10157,6 +10166,7 @@ PSI_stage_info stage_end= { 0, "end", 0}; PSI_stage_info stage_executing= { 0, "executing", 0}; PSI_stage_info stage_execution_of_init_command= { 0, "Execution of init_command", 0}; PSI_stage_info stage_explaining= { 0, "explaining", 0}; +PSI_stage_info stage_finding_key_cache= { 0, "Finding key cache", 0}; PSI_stage_info stage_finished_reading_one_binlog_switching_to_next_binlog= { 0, "Finished reading one binlog; switching to next binlog", 0}; PSI_stage_info stage_flushing_relay_log_and_master_info_repository= { 0, "Flushing relay log and master info repository.", 0}; PSI_stage_info stage_flushing_relay_log_info_file= { 0, "Flushing relay-log info file.", 0}; @@ -10181,6 +10191,7 @@ PSI_stage_info stage_purging_old_relay_logs= { 0, "Purging old relay logs", 0}; PSI_stage_info stage_query_end= { 0, "query end", 0}; PSI_stage_info stage_queueing_master_event_to_the_relay_log= { 0, "Queueing master event to the relay log", 0}; PSI_stage_info stage_reading_event_from_the_relay_log= { 0, "Reading event from the relay log", 0}; +PSI_stage_info stage_recreating_table= { 0, "recreating table", 0}; PSI_stage_info stage_registering_slave_on_master= { 0, "Registering slave on master", 0}; PSI_stage_info stage_removing_duplicates= { 0, "Removing duplicates", 0}; PSI_stage_info stage_removing_tmp_table= { 0, "removing tmp table", 0}; @@ -10249,6 +10260,8 @@ PSI_stage_info stage_gtid_wait_other_connection= { 0, "Waiting for other master PSI_stage_info *all_server_stages[]= { & stage_after_create, + & stage_after_opening_tables, + & stage_after_table_lock, & stage_allocating_local_table, & stage_alter_inplace, & stage_alter_inplace_commit, @@ -10280,6 +10293,7 @@ PSI_stage_info *all_server_stages[]= & stage_executing, & stage_execution_of_init_command, & stage_explaining, + & stage_finding_key_cache, & stage_finished_reading_one_binlog_switching_to_next_binlog, & stage_flushing_relay_log_and_master_info_repository, & stage_flushing_relay_log_info_file, @@ -10304,6 +10318,7 @@ PSI_stage_info *all_server_stages[]= & stage_query_end, & stage_queueing_master_event_to_the_relay_log, & stage_reading_event_from_the_relay_log, + & stage_recreating_table, & stage_registering_slave_on_master, & stage_removing_duplicates, & stage_removing_tmp_table, @@ -10411,23 +10426,49 @@ void init_server_psi_keys(void) category= "com"; init_com_statement_info(); - count= array_elements(com_statement_info); + + /* + Register [0 .. COM_QUERY - 1] as "statement/com/..." + */ + count= (int) COM_QUERY; mysql_statement_register(category, com_statement_info, count); /* + Register [COM_QUERY + 1 .. COM_END] as "statement/com/..." + */ + count= (int) COM_END - (int) COM_QUERY; + mysql_statement_register(category, & com_statement_info[(int) COM_QUERY + 1], count); + + category= "abstract"; + /* + Register [COM_QUERY] as "statement/abstract/com_query" + */ + mysql_statement_register(category, & com_statement_info[(int) COM_QUERY], 1); + + /* When a new packet is received, - it is instrumented as "statement/com/". + it is instrumented as "statement/abstract/new_packet". Based on the packet type found, it later mutates to the proper narrow type, for example - "statement/com/query" or "statement/com/ping". - In cases of "statement/com/query", SQL queries are given to + "statement/abstract/query" or "statement/com/ping". + In cases of "statement/abstract/query", SQL queries are given to the parser, which mutates the statement type to an even more narrow classification, for example "statement/sql/select". */ stmt_info_new_packet.m_key= 0; - stmt_info_new_packet.m_name= ""; + stmt_info_new_packet.m_name= "new_packet"; stmt_info_new_packet.m_flags= PSI_FLAG_MUTABLE; mysql_statement_register(category, &stmt_info_new_packet, 1); + + /* + Statements processed from the relay log are initially instrumented as + "statement/abstract/relay_log". The parser will mutate the statement type to + a more specific classification, for example "statement/sql/insert". + */ + stmt_info_rpl.m_key= 0; + stmt_info_rpl.m_name= "relay_log"; + stmt_info_rpl.m_flags= PSI_FLAG_MUTABLE; + mysql_statement_register(category, &stmt_info_rpl, 1); #endif } diff --git a/sql/mysqld.h b/sql/mysqld.h index f1dde5cc03e..dd774e0d565 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -338,6 +338,8 @@ void init_server_psi_keys(); Hint: grep PSI_stage_info | sort -u */ extern PSI_stage_info stage_after_create; +extern PSI_stage_info stage_after_opening_tables; +extern PSI_stage_info stage_after_table_lock; extern PSI_stage_info stage_allocating_local_table; extern PSI_stage_info stage_alter_inplace_prepare; extern PSI_stage_info stage_alter_inplace; @@ -366,6 +368,7 @@ extern PSI_stage_info stage_enabling_keys; extern PSI_stage_info stage_executing; extern PSI_stage_info stage_execution_of_init_command; extern PSI_stage_info stage_explaining; +extern PSI_stage_info stage_finding_key_cache; extern PSI_stage_info stage_finished_reading_one_binlog_switching_to_next_binlog; extern PSI_stage_info stage_flushing_relay_log_and_master_info_repository; extern PSI_stage_info stage_flushing_relay_log_info_file; @@ -390,6 +393,7 @@ extern PSI_stage_info stage_purging_old_relay_logs; extern PSI_stage_info stage_query_end; extern PSI_stage_info stage_queueing_master_event_to_the_relay_log; extern PSI_stage_info stage_reading_event_from_the_relay_log; +extern PSI_stage_info stage_recreating_table; extern PSI_stage_info stage_registering_slave_on_master; extern PSI_stage_info stage_removing_duplicates; extern PSI_stage_info stage_removing_tmp_table; @@ -466,6 +470,11 @@ extern PSI_statement_info sql_statement_info[(uint) SQLCOM_END + 1]; */ extern PSI_statement_info com_statement_info[(uint) COM_END + 1]; +/** + Statement instrumentation key for replication. +*/ +extern PSI_statement_info stmt_info_rpl; + void init_sql_statement_info(); void init_com_statement_info(); #endif /* HAVE_PSI_STATEMENT_INTERFACE */ @@ -632,7 +641,7 @@ extern my_atomic_rwlock_t statistics_lock; void unireg_end(void) __attribute__((noreturn)); /* increment query_id and return it. */ -inline query_id_t next_query_id() +inline __attribute__((warn_unused_result)) query_id_t next_query_id() { query_id_t id; my_atomic_rwlock_wrlock(&global_query_id_lock); diff --git a/sql/opt_range.cc b/sql/opt_range.cc index cbc3d285d0a..bfba74cf587 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -2181,7 +2181,7 @@ int QUICK_ROR_INTERSECT_SELECT::init_ror_merged_scan(bool reuse_handler, quick->record= head->record[0]; } - if (need_to_fetch_row && head->file->ha_rnd_init_with_error(1)) + if (need_to_fetch_row && head->file->ha_rnd_init_with_error(false)) { DBUG_PRINT("error", ("ROR index_merge rnd_init call failed")); DBUG_RETURN(1); @@ -2363,8 +2363,13 @@ int QUICK_ROR_UNION_SELECT::reset() quick->save_last_pos(); queue_insert(&queue, (uchar*)quick); } - - if ((error= head->file->ha_rnd_init(1))) + /* Prepare for ha_rnd_pos calls. */ + if (head->file->inited && (error= head->file->ha_rnd_end())) + { + DBUG_PRINT("error", ("ROR index_merge rnd_end call failed")); + DBUG_RETURN(error); + } + if ((error= head->file->ha_rnd_init(false))) { DBUG_PRINT("error", ("ROR index_merge rnd_init call failed")); DBUG_RETURN(error); @@ -3390,6 +3395,17 @@ double records_in_column_ranges(PARAM *param, uint idx, on the rows of 'table' in the processed query. The calculated selectivity is assigned to the field table->cond_selectivity. + Selectivity is calculated as a product of selectivities imposed by: + + 1. possible range accesses. (if multiple range accesses use the same + restrictions on the same field, we make adjustments for that) + 2. Sargable conditions on fields for which we have column statistics (if + a field is used in a possible range access, we assume that selectivity + is already provided by the range access' estimates) + 3. Reading a few records from the table pages and checking the condition + selectivity (this is used for conditions like "column LIKE '%val%'" + where approaches #1 and #2 do not provide selectivity data). + NOTE Currently the selectivities of range conditions over different columns are considered independent. @@ -3415,14 +3431,90 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item *cond) if (table->pos_in_table_list->schema_table) DBUG_RETURN(FALSE); + MY_BITMAP handled_columns; + my_bitmap_map* buf; + if (!(buf= (my_bitmap_map*)thd->alloc(table->s->column_bitmap_size))) + DBUG_RETURN(TRUE); + my_bitmap_init(&handled_columns, buf, table->s->fields, FALSE); + + /* + First, take into account possible range accesses. + range access estimates are the most precise, we prefer them to any other + estimate sources. + */ + + for (keynr= 0; keynr < table->s->keys; keynr++) + { + if (table->quick_keys.is_set(keynr)) + set_if_bigger(max_quick_key_parts, table->quick_key_parts[keynr]); + } + + /* + Walk through all indexes, indexes where range access uses more keyparts + go first. + */ + for (uint quick_key_parts= max_quick_key_parts; + quick_key_parts; quick_key_parts--) + { + for (keynr= 0; keynr < table->s->keys; keynr++) + { + if (table->quick_keys.is_set(keynr) && + table->quick_key_parts[keynr] == quick_key_parts) + { + uint i; + uint used_key_parts= table->quick_key_parts[keynr]; + double quick_cond_selectivity= table->quick_rows[keynr] / + table_records; + KEY *key_info= table->key_info + keynr; + KEY_PART_INFO* key_part= key_info->key_part; + /* + Suppose, there are range conditions on two keys + KEY1 (col1, col2) + KEY2 (col3, col2) + + we don't want to count selectivity of condition on col2 twice. + + First, find the longest key prefix that's made of columns whose + selectivity wasn't already accounted for. + */ + for (i= 0; i < used_key_parts; i++, key_part++) + { + if (bitmap_is_set(&handled_columns, key_part->fieldnr-1)) + break; + bitmap_set_bit(&handled_columns, key_part->fieldnr-1); + } + if (i) + { + /* + There is at least 1-column prefix of columns whose selectivity has + not yet been accounted for. + */ + table->cond_selectivity*= quick_cond_selectivity; + if (i != used_key_parts) + { + /* + Range access got us estimate for #used_key_parts. + We need estimate for #(i-1) key parts. + */ + double f1= key_info->actual_rec_per_key(i-1); + double f2= key_info->actual_rec_per_key(i); + table->cond_selectivity*= f1 / f2; + } + } + } + } + } + + /* + Second step: calculate the selectivity of the range conditions not + supported by any index + */ + bitmap_subtract(used_fields, &handled_columns); + /* no need to do: my_bitmap_free(&handled_columns); */ + if (thd->variables.optimizer_use_condition_selectivity > 2 && !bitmap_is_clear_all(used_fields)) { - /* - Calculate the selectivity of the range conditions not supported - by any index - */ - PARAM param; MEM_ROOT alloc; SEL_TREE *tree; @@ -3509,47 +3601,8 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item *cond) bitmap_clear_all(used_fields); - for (keynr= 0; keynr < table->s->keys; keynr++) - { - if (table->quick_keys.is_set(keynr)) - set_if_bigger(max_quick_key_parts, table->quick_key_parts[keynr]); - } - - for (uint quick_key_parts= max_quick_key_parts; - quick_key_parts; quick_key_parts--) - { - for (keynr= 0; keynr < table->s->keys; keynr++) - { - if (table->quick_keys.is_set(keynr) && - table->quick_key_parts[keynr] == quick_key_parts) - { - uint i; - uint used_key_parts= table->quick_key_parts[keynr]; - double quick_cond_selectivity= table->quick_rows[keynr] / - table_records; - KEY *key_info= table->key_info + keynr; - KEY_PART_INFO* key_part= key_info->key_part; - for (i= 0; i < used_key_parts; i++, key_part++) - { - if (bitmap_is_set(used_fields, key_part->fieldnr-1)) - break; - bitmap_set_bit(used_fields, key_part->fieldnr-1); - } - if (i) - { - table->cond_selectivity*= quick_cond_selectivity; - if (i != used_key_parts) - { - double f1= key_info->actual_rec_per_key(i-1); - double f2= key_info->actual_rec_per_key(i); - table->cond_selectivity*= f1 / f2; - } - } - } - } - } - /* Calculate selectivity of probably highly selective predicates */ + /* Check if we can improve selectivity estimates by using sampling */ ulong check_rows= MY_MIN(thd->variables.optimizer_selectivity_sampling_limit, (ulong) (table_records * SELECTIVITY_SAMPLING_SHARE)); @@ -13376,7 +13429,7 @@ SEL_ARG * get_index_range_tree(uint index, SEL_TREE* range_tree, PARAM *param, DESCRIPTION This method computes the access cost of a TRP_GROUP_MIN_MAX instance and - the number of rows returned. It updates this->read_cost and this->records. + the number of rows returned. NOTES The cost computation distinguishes several cases: @@ -13432,7 +13485,6 @@ void cost_group_min_max(TABLE* table, KEY *index_info, uint used_key_parts, double p_overlap; /* Probability that a sub-group overlaps two blocks. */ double quick_prefix_selectivity; double io_cost; - double cpu_cost= 0; /* TODO: CPU cost of index_read calls? */ DBUG_ENTER("cost_group_min_max"); table_records= table->stat_records(); @@ -13480,11 +13532,25 @@ void cost_group_min_max(TABLE* table, KEY *index_info, uint used_key_parts, (double) num_blocks; /* - TODO: If there is no WHERE clause and no other expressions, there should be - no CPU cost. We leave it here to make this cost comparable to that of index - scan as computed in SQL_SELECT::test_quick_select(). + CPU cost must be comparable to that of an index scan as computed + in SQL_SELECT::test_quick_select(). When the groups are small, + e.g. for a unique index, using index scan will be cheaper since it + reads the next record without having to re-position to it on every + group. To make the CPU cost reflect this, we estimate the CPU cost + as the sum of: + 1. Cost for evaluating the condition (similarly as for index scan). + 2. Cost for navigating the index structure (assuming a b-tree). + Note: We only add the cost for one comparision per block. For a + b-tree the number of comparisons will be larger. + TODO: This cost should be provided by the storage engine. */ - cpu_cost= (double) num_groups / TIME_FOR_COMPARE; + const double tree_traversal_cost= + ceil(log(static_cast<double>(table_records))/ + log(static_cast<double>(keys_per_block))) * + 1/double(2*TIME_FOR_COMPARE); + + const double cpu_cost= num_groups * + (tree_traversal_cost + 1/double(TIME_FOR_COMPARE)); *read_cost= io_cost + cpu_cost; *records= num_groups; @@ -13689,15 +13755,21 @@ int QUICK_GROUP_MIN_MAX_SELECT::init() { if (group_prefix) /* Already initialized. */ return 0; - - if (!(last_prefix= (uchar*) alloc_root(&alloc, group_prefix_len))) + + /* + 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. + 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 (!(group_prefix= (uchar*) alloc_root(&alloc, - real_prefix_len + min_max_arg_len))) + real_prefix_len+min_max_arg_len+1))) return 1; if (key_infix_len > 0) diff --git a/sql/protocol.cc b/sql/protocol.cc index 33c753b8321..af2accfd146 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -64,7 +64,7 @@ bool Protocol_binary::net_store_data(const uchar *from, size_t length) /* - net_store_data() - extended version with character set conversion. + net_store_data_cs() - extended version with character set conversion. It is optimized for short strings whose length after conversion is garanteed to be less than 251, which accupies @@ -76,8 +76,12 @@ bool Protocol_binary::net_store_data(const uchar *from, size_t length) */ #ifndef EMBEDDED_LIBRARY -bool Protocol::net_store_data(const uchar *from, size_t length, +bool Protocol::net_store_data_cs(const uchar *from, size_t length, CHARSET_INFO *from_cs, CHARSET_INFO *to_cs) +#else +bool Protocol_binary::net_store_data_cs(const uchar *from, size_t length, + CHARSET_INFO *from_cs, CHARSET_INFO *to_cs) +#endif { uint dummy_errors; /* Calculate maxumum possible result length */ @@ -117,7 +121,6 @@ bool Protocol::net_store_data(const uchar *from, size_t length, packet->length((uint) (to - packet->ptr())); return 0; } -#endif /** @@ -1015,7 +1018,7 @@ bool Protocol::store_string_aux(const char *from, size_t length, tocs != &my_charset_bin) { /* Store with conversion */ - return net_store_data((uchar*) from, length, fromcs, tocs); + return net_store_data_cs((uchar*) from, length, fromcs, tocs); } /* Store without conversion */ return net_store_data((uchar*) from, length); diff --git a/sql/protocol.h b/sql/protocol.h index 1c0a28560bd..c58de68289f 100644 --- a/sql/protocol.h +++ b/sql/protocol.h @@ -44,8 +44,12 @@ protected: uint field_count; #ifndef EMBEDDED_LIBRARY bool net_store_data(const uchar *from, size_t length); + bool net_store_data_cs(const uchar *from, size_t length, + CHARSET_INFO *fromcs, CHARSET_INFO *tocs); #else virtual bool net_store_data(const uchar *from, size_t length); + virtual bool net_store_data_cs(const uchar *from, size_t length, + CHARSET_INFO *fromcs, CHARSET_INFO *tocs); char **next_field; MYSQL_FIELD *next_mysql_field; MEM_ROOT *alloc; @@ -54,8 +58,6 @@ protected: The following two are low-level functions that are invoked from higher-level store_xxx() funcs. The data is stored into this->packet. */ - bool net_store_data(const uchar *from, size_t length, - CHARSET_INFO *fromcs, CHARSET_INFO *tocs); bool store_string_aux(const char *from, size_t length, CHARSET_INFO *fromcs, CHARSET_INFO *tocs); @@ -184,6 +186,8 @@ public: #ifdef EMBEDDED_LIBRARY virtual bool write(); bool net_store_data(const uchar *from, size_t length); + bool net_store_data_cs(const uchar *from, size_t length, + CHARSET_INFO *fromcs, CHARSET_INFO *tocs); #endif virtual bool store_null(); virtual bool store_tiny(longlong from); diff --git a/sql/rpl_mi.h b/sql/rpl_mi.h index a136af38356..af739b1dad4 100644 --- a/sql/rpl_mi.h +++ b/sql/rpl_mi.h @@ -216,6 +216,16 @@ public: bool stop_all_slaves(THD *thd); }; + +/* + The class rpl_io_thread_info is the THD::system_thread_info for the IO thread. +*/ +class rpl_io_thread_info +{ +public: +}; + + bool check_master_connection_name(LEX_STRING *name); void create_logfile_name_with_suffix(char *res_file_name, size_t length, const char *info_file, diff --git a/sql/rpl_parallel.cc b/sql/rpl_parallel.cc index 9c4c819c022..e72d3470a7f 100644 --- a/sql/rpl_parallel.cc +++ b/sql/rpl_parallel.cc @@ -33,7 +33,7 @@ rpt_handle_event(rpl_parallel_thread::queued_event *qev, THD *thd= rgi->thd; thd->rgi_slave= rgi; - thd->rpl_filter = rli->mi->rpl_filter; + thd->system_thread_info.rpl_sql_info->rpl_filter = rli->mi->rpl_filter; /* ToDo: Access to thd, and what about rli, split out a parallel part? */ mysql_mutex_lock(&rli->data_lock); @@ -212,6 +212,7 @@ handle_rpl_parallel_thread(void *arg) rpl_parallel_thread::queued_event *qevs_to_free; rpl_group_info *rgis_to_free; group_commit_orderer *gcos_to_free; + rpl_sql_thread_info sql_info(NULL); size_t total_event_size; int err; @@ -242,6 +243,7 @@ handle_rpl_parallel_thread(void *arg) thd_proc_info(thd, "Waiting for work from main SQL threads"); thd->set_time(); thd->variables.lock_wait_timeout= LONG_TIMEOUT; + thd->system_thread_info.rpl_sql_info= &sql_info; /* For now, we need to run the replication parallel worker threads in READ COMMITTED. This is needed because gap locks are not symmetric. @@ -1495,7 +1497,6 @@ rpl_parallel::do_event(rpl_group_info *serial_rgi, Log_event *ev, } else if (!is_group_event) { - my_off_t log_pos; int err; bool tmp; /* @@ -1509,7 +1510,13 @@ rpl_parallel::do_event(rpl_group_info *serial_rgi, Log_event *ev, serial_rgi->is_parallel_exec= true; err= rpt_handle_event(qev, NULL); serial_rgi->is_parallel_exec= tmp; - log_pos= ev->log_pos; + if (ev->is_relay_log_event()) + qev->future_event_master_log_pos= 0; + else if (typ == ROTATE_EVENT) + qev->future_event_master_log_pos= + (static_cast<Rotate_log_event *>(ev))->pos; + else + qev->future_event_master_log_pos= ev->log_pos; delete_or_keep_event_post_apply(serial_rgi, typ, ev); if (err) @@ -1532,7 +1539,6 @@ rpl_parallel::do_event(rpl_group_info *serial_rgi, Log_event *ev, the current point. */ qev->ev= NULL; - qev->future_event_master_log_pos= log_pos; } else { diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc index 0d0c8c9df70..a162d1d79f8 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -82,7 +82,6 @@ Relay_log_info::Relay_log_info(bool is_slave_recovery) max_relay_log_size= global_system_variables.max_relay_log_size; bzero((char*) &info_file, sizeof(info_file)); bzero((char*) &cache_buf, sizeof(cache_buf)); - cached_charset_invalidate(); mysql_mutex_init(key_relay_log_info_run_lock, &run_lock, MY_MUTEX_INIT_FAST); mysql_mutex_init(key_relay_log_info_data_lock, &data_lock, MY_MUTEX_INIT_FAST); @@ -1200,29 +1199,6 @@ bool Relay_log_info::is_until_satisfied(THD *thd, Log_event *ev) } -void Relay_log_info::cached_charset_invalidate() -{ - DBUG_ENTER("Relay_log_info::cached_charset_invalidate"); - - /* Full of zeroes means uninitialized. */ - bzero(cached_charset, sizeof(cached_charset)); - DBUG_VOID_RETURN; -} - - -bool Relay_log_info::cached_charset_compare(char *charset) const -{ - DBUG_ENTER("Relay_log_info::cached_charset_compare"); - - if (memcmp(cached_charset, charset, sizeof(cached_charset))) - { - memcpy(const_cast<char*>(cached_charset), charset, sizeof(cached_charset)); - DBUG_RETURN(1); - } - DBUG_RETURN(0); -} - - void Relay_log_info::stmt_done(my_off_t event_master_log_pos, time_t event_creation_time, THD *thd, rpl_group_info *rgi) @@ -1769,4 +1745,33 @@ rpl_group_info::mark_start_commit() } +rpl_sql_thread_info::rpl_sql_thread_info(Rpl_filter *filter) + : rpl_filter(filter) +{ + cached_charset_invalidate(); +} + + +void rpl_sql_thread_info::cached_charset_invalidate() +{ + DBUG_ENTER("rpl_group_info::cached_charset_invalidate"); + + /* Full of zeroes means uninitialized. */ + bzero(cached_charset, sizeof(cached_charset)); + DBUG_VOID_RETURN; +} + + +bool rpl_sql_thread_info::cached_charset_compare(char *charset) const +{ + DBUG_ENTER("rpl_group_info::cached_charset_compare"); + + if (memcmp(cached_charset, charset, sizeof(cached_charset))) + { + memcpy(const_cast<char*>(cached_charset), charset, sizeof(cached_charset)); + DBUG_RETURN(1); + } + DBUG_RETURN(0); +} + #endif diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h index 48193afce4d..137571ab820 100644 --- a/sql/rpl_rli.h +++ b/sql/rpl_rli.h @@ -26,6 +26,7 @@ struct RPL_TABLE_LIST; class Master_info; +class Rpl_filter; /**************************************************************************** @@ -295,7 +296,6 @@ public: /* Condition for UNTIL master_gtid_pos. */ slave_connection_state until_gtid_pos; - char cached_charset[6]; /* retried_trans is a cumulative counter: how many times the slave has retried a transaction (any) since slave started. @@ -371,15 +371,6 @@ public: group_relay_log_pos); } - /* - Last charset (6 bytes) seen by slave SQL thread is cached here; it helps - the thread save 3 get_charset() per Query_log_event if the charset is not - changing from event to event (common situation). - When the 6 bytes are equal to 0 is used to mean "cache is invalidated". - */ - void cached_charset_invalidate(); - bool cached_charset_compare(char *charset) const; - /** Helper function to do after statement completion. @@ -726,6 +717,30 @@ struct rpl_group_info }; +/* + The class rpl_sql_thread_info is the THD::system_thread_info for an SQL + thread; this is either the driver SQL thread or a worker thread for parallel + replication. +*/ +class rpl_sql_thread_info +{ +public: + char cached_charset[6]; + Rpl_filter* rpl_filter; + + rpl_sql_thread_info(Rpl_filter *filter); + + /* + Last charset (6 bytes) seen by slave SQL thread is cached here; it helps + the thread save 3 get_charset() per Query_log_event if the charset is not + changing from event to event (common situation). + When the 6 bytes are equal to 0 is used to mean "cache is invalidated". + */ + void cached_charset_invalidate(); + bool cached_charset_compare(char *charset) const; +}; + + // Defined in rpl_rli.cc int init_relay_log_info(Relay_log_info* rli, const char* info_fname); diff --git a/sql/set_var.h b/sql/set_var.h index de47c4646e7..19d2b4fd14c 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -340,6 +340,7 @@ bool fix_delay_key_write(sys_var *self, THD *thd, enum_var_type type); ulonglong expand_sql_mode(ulonglong sql_mode); bool sql_mode_string_representation(THD *thd, ulonglong sql_mode, LEX_STRING *ls); +int default_regex_flags_pcre(const THD *thd); extern sys_var *Sys_autocommit_ptr; diff --git a/sql/share/charsets/Index.xml b/sql/share/charsets/Index.xml index 3e402226a34..9764d629625 100644 --- a/sql/share/charsets/Index.xml +++ b/sql/share/charsets/Index.xml @@ -4,6 +4,7 @@ <copyright> Copyright (c) 2003-2005 MySQL AB + Use is subject to license terms 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 diff --git a/sql/share/charsets/ascii.xml b/sql/share/charsets/ascii.xml index 29336b3a665..c516a68516c 100644 --- a/sql/share/charsets/ascii.xml +++ b/sql/share/charsets/ascii.xml @@ -4,6 +4,7 @@ <copyright> Copyright (c) 2003, 2007 MySQL AB + Use is subject to license terms 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 diff --git a/sql/share/charsets/cp1250.xml b/sql/share/charsets/cp1250.xml index 1b4a71ef6d5..e6681a625a2 100644 --- a/sql/share/charsets/cp1250.xml +++ b/sql/share/charsets/cp1250.xml @@ -4,6 +4,7 @@ <copyright> Copyright (c) 2003, 2005 MySQL AB + Use is subject to license terms 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 diff --git a/sql/share/charsets/cp1256.xml b/sql/share/charsets/cp1256.xml index 806fef961f7..ab0ba855f3b 100644 --- a/sql/share/charsets/cp1256.xml +++ b/sql/share/charsets/cp1256.xml @@ -6,6 +6,7 @@ <copyright> Copyright (C) 2003 MySQL AB + Use is subject to license terms 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 diff --git a/sql/share/charsets/cp1257.xml b/sql/share/charsets/cp1257.xml index 8ae73fdf25a..61d1d276b0a 100644 --- a/sql/share/charsets/cp1257.xml +++ b/sql/share/charsets/cp1257.xml @@ -4,6 +4,7 @@ <copyright> Copyright (C) 2003 MySQL AB + Use is subject to license terms 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 diff --git a/sql/share/charsets/cp850.xml b/sql/share/charsets/cp850.xml index 198b336daef..06465540a75 100644 --- a/sql/share/charsets/cp850.xml +++ b/sql/share/charsets/cp850.xml @@ -4,6 +4,7 @@ <copyright> Copyright (C) 2003 MySQL AB + Use is subject to license terms 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 diff --git a/sql/share/charsets/cp866.xml b/sql/share/charsets/cp866.xml index d35f3d68b05..9cd8c8c504b 100644 --- a/sql/share/charsets/cp866.xml +++ b/sql/share/charsets/cp866.xml @@ -4,6 +4,7 @@ <copyright> Copyright (C) 2003 MySQL AB + Use is subject to license terms 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 diff --git a/sql/share/charsets/dec8.xml b/sql/share/charsets/dec8.xml index 66bb421b674..68949309ced 100644 --- a/sql/share/charsets/dec8.xml +++ b/sql/share/charsets/dec8.xml @@ -4,6 +4,7 @@ <copyright> Copyright (C) 2003 MySQL AB + Use is subject to license terms 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 diff --git a/sql/share/charsets/geostd8.xml b/sql/share/charsets/geostd8.xml index a789d07e6d8..822cc083724 100644 --- a/sql/share/charsets/geostd8.xml +++ b/sql/share/charsets/geostd8.xml @@ -4,6 +4,7 @@ <copyright> Copyright (C) 2003 MySQL AB + Use is subject to license terms 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 diff --git a/sql/share/charsets/greek.xml b/sql/share/charsets/greek.xml index 5b66a7ab442..cbbe22e675a 100644 --- a/sql/share/charsets/greek.xml +++ b/sql/share/charsets/greek.xml @@ -4,6 +4,7 @@ <copyright> Copyright (C) 2003 MySQL AB + Use is subject to license terms 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 diff --git a/sql/share/charsets/hebrew.xml b/sql/share/charsets/hebrew.xml index 0544b27ef4f..562fa4f4748 100644 --- a/sql/share/charsets/hebrew.xml +++ b/sql/share/charsets/hebrew.xml @@ -4,6 +4,7 @@ <copyright> Copyright (c) 2003, 2006 MySQL AB + Use is subject to license terms 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 diff --git a/sql/share/charsets/hp8.xml b/sql/share/charsets/hp8.xml index 83a076237f7..b17f75ed73e 100644 --- a/sql/share/charsets/hp8.xml +++ b/sql/share/charsets/hp8.xml @@ -4,6 +4,7 @@ <copyright> Copyright (C) 2003 MySQL AB + Use is subject to license terms 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 diff --git a/sql/share/charsets/keybcs2.xml b/sql/share/charsets/keybcs2.xml index a9f305deab8..7c2775ba5c3 100644 --- a/sql/share/charsets/keybcs2.xml +++ b/sql/share/charsets/keybcs2.xml @@ -4,6 +4,7 @@ <copyright> Copyright (C) 2003 MySQL AB + Use is subject to license terms 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 diff --git a/sql/share/charsets/koi8r.xml b/sql/share/charsets/koi8r.xml index 21ebf78b79e..25264d4f9ce 100644 --- a/sql/share/charsets/koi8r.xml +++ b/sql/share/charsets/koi8r.xml @@ -4,6 +4,7 @@ <copyright> Copyright (C) 2003 MySQL AB + Use is subject to license terms 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 diff --git a/sql/share/charsets/koi8u.xml b/sql/share/charsets/koi8u.xml index 65145c97593..a2f5de9feb2 100644 --- a/sql/share/charsets/koi8u.xml +++ b/sql/share/charsets/koi8u.xml @@ -4,6 +4,7 @@ <copyright> Copyright (C) 2003 MySQL AB + Use is subject to license terms 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 diff --git a/sql/share/charsets/languages.html b/sql/share/charsets/languages.html index 2b1c44421bf..3263d6a2ae2 100644 --- a/sql/share/charsets/languages.html +++ b/sql/share/charsets/languages.html @@ -1,6 +1,7 @@ #!/bin/sh # Copyright (C) 2003 MySQL AB +# Use is subject to license terms # # 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 diff --git a/sql/share/charsets/latin1.xml b/sql/share/charsets/latin1.xml index 4054eea8d33..68307847d91 100644 --- a/sql/share/charsets/latin1.xml +++ b/sql/share/charsets/latin1.xml @@ -4,6 +4,7 @@ <copyright> Copyright (c) 2003, 2005 MySQL AB + Use is subject to license terms 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 diff --git a/sql/share/charsets/latin2.xml b/sql/share/charsets/latin2.xml index a44ec7e0ec6..29ff4cb974b 100644 --- a/sql/share/charsets/latin2.xml +++ b/sql/share/charsets/latin2.xml @@ -4,6 +4,7 @@ <copyright> Copyright (c) 2003, 2005 MySQL AB + Use is subject to license terms 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 diff --git a/sql/share/charsets/latin5.xml b/sql/share/charsets/latin5.xml index 6b60e58cdda..ca7dd106de5 100644 --- a/sql/share/charsets/latin5.xml +++ b/sql/share/charsets/latin5.xml @@ -4,6 +4,7 @@ <copyright> Copyright (c) 2003, 2005 MySQL AB + Use is subject to license terms 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 diff --git a/sql/share/charsets/latin7.xml b/sql/share/charsets/latin7.xml index fb384b3a5ff..81866c23bbd 100644 --- a/sql/share/charsets/latin7.xml +++ b/sql/share/charsets/latin7.xml @@ -4,6 +4,7 @@ <copyright> Copyright (C) 2003 MySQL AB + Use is subject to license terms 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 diff --git a/sql/share/charsets/macce.xml b/sql/share/charsets/macce.xml index d7242f26297..4fa46301d2e 100644 --- a/sql/share/charsets/macce.xml +++ b/sql/share/charsets/macce.xml @@ -4,6 +4,7 @@ <copyright> Copyright (C) 2003 MySQL AB + Use is subject to license terms 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 diff --git a/sql/share/charsets/macroman.xml b/sql/share/charsets/macroman.xml index a2485cf9379..4ee8dc1f952 100644 --- a/sql/share/charsets/macroman.xml +++ b/sql/share/charsets/macroman.xml @@ -4,6 +4,7 @@ <copyright> Copyright (C) 2003 MySQL AB + Use is subject to license terms 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 diff --git a/sql/share/charsets/swe7.xml b/sql/share/charsets/swe7.xml index f12a2238718..d881f1e7d62 100644 --- a/sql/share/charsets/swe7.xml +++ b/sql/share/charsets/swe7.xml @@ -4,6 +4,7 @@ <copyright> Copyright (C) 2003 MySQL AB + Use is subject to license terms 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 diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index dc50c68bcdd..ed7976e2abd 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -6911,6 +6911,49 @@ ER_MTS_EVENT_BIGGER_PENDING_JOBS_SIZE_MAX ER_INNODB_NO_FT_USES_PARSER eng "Cannot CREATE FULLTEXT INDEX WITH PARSER on InnoDB table" +ER_BINLOG_LOGICAL_CORRUPTION + eng "The binary log file '%s' is logically corrupted: %s" + +ER_WARN_PURGE_LOG_IN_USE + eng "file %s was not purged because it was being read by %d thread(s), purged only %d out of %d files." + +ER_WARN_PURGE_LOG_IS_ACTIVE + eng "file %s was not purged because it is the active log file." + +ER_AUTO_INCREMENT_CONFLICT + eng "Auto-increment value in UPDATE conflicts with internally generated values" + +WARN_ON_BLOCKHOLE_IN_RBR + eng "Row events are not logged for %s statements that modify BLACKHOLE tables in row format. Table(s): '%-.192s'" + +ER_SLAVE_MI_INIT_REPOSITORY + eng "Slave failed to initialize master info structure from the repository" + +ER_SLAVE_RLI_INIT_REPOSITORY + eng "Slave failed to initialize relay log info structure from the repository" + +ER_ACCESS_DENIED_CHANGE_USER_ERROR 28000 + eng "Access denied trying to change to user '%-.48s'@'%-.64s' (using password: %s). Disconnecting." + bgn "Отказан достъп при опит за смяна към потребител %-.48s'@'%-.64s' (използвана парола: %s). Затваряне на връзката." + +ER_INNODB_READ_ONLY + eng "InnoDB is in read only mode." + +ER_STOP_SLAVE_SQL_THREAD_TIMEOUT + eng "STOP SLAVE command execution is incomplete: Slave SQL thread got the stop signal, thread is busy, SQL thread will stop once the current task is complete." + +ER_STOP_SLAVE_IO_THREAD_TIMEOUT + eng "STOP SLAVE command execution is incomplete: Slave IO thread got the stop signal, thread is busy, IO thread will stop once the current task is complete." + +ER_TABLE_CORRUPT + eng "Operation cannot be performed. The table '%-.64s.%-.64s' is missing, corrupt or contains bad data." + +ER_TEMP_FILE_WRITE_FAILURE + eng "Temporary file write failure." + +ER_INNODB_FT_AUX_NOT_HEX_ID + eng "Upgrade index name failed, please use create index(alter table) algorithm copy to rebuild index." + # # MariaDB error messages section starts here diff --git a/sql/slave.cc b/sql/slave.cc index 7d24093f27b..a43347743eb 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -321,7 +321,7 @@ handle_slave_init(void *arg __attribute__((unused))) mysql_mutex_lock(&LOCK_thread_count); slave_init_thread_running= false; - mysql_cond_signal(&COND_thread_count); + mysql_cond_broadcast(&COND_thread_count); mysql_mutex_unlock(&LOCK_thread_count); return 0; @@ -2882,7 +2882,7 @@ void set_slave_thread_options(THD* thd) DBUG_VOID_RETURN; } -void set_slave_thread_default_charset(THD* thd, Relay_log_info const *rli) +void set_slave_thread_default_charset(THD* thd, rpl_group_info *rgi) { DBUG_ENTER("set_slave_thread_default_charset"); @@ -2894,13 +2894,7 @@ void set_slave_thread_default_charset(THD* thd, Relay_log_info const *rli) global_system_variables.collation_server; thd->update_charset(); - /* - We use a const cast here since the conceptual (and externally - visible) behavior of the function is to set the default charset of - the thread. That the cache has to be invalidated is a secondary - effect. - */ - const_cast<Relay_log_info*>(rli)->cached_charset_invalidate(); + thd->system_thread_info.rpl_sql_info->cached_charset_invalidate(); DBUG_VOID_RETURN; } @@ -3777,6 +3771,7 @@ pthread_handler_t handle_slave_io(void *arg) uint retry_count; bool suppress_warnings; int ret; + rpl_io_thread_info io_info; #ifndef DBUG_OFF uint retry_count_reg= 0, retry_count_dump= 0, retry_count_event= 0; #endif @@ -3810,6 +3805,7 @@ pthread_handler_t handle_slave_io(void *arg) sql_print_error("Failed during slave I/O thread initialization"); goto err_during_init; } + thd->system_thread_info.rpl_io_info= &io_info; mysql_mutex_lock(&LOCK_thread_count); threads.append(thd); mysql_mutex_unlock(&LOCK_thread_count); @@ -4376,6 +4372,7 @@ pthread_handler_t handle_slave_sql(void *arg) Relay_log_info* rli = &mi->rli; const char *errmsg; rpl_group_info *serial_rgi; + rpl_sql_thread_info sql_info(mi->rpl_filter); // needs to call my_thread_init(), otherwise we get a coredump in DBUG_ stuff my_thread_init(); @@ -4387,7 +4384,7 @@ pthread_handler_t handle_slave_sql(void *arg) serial_rgi= new rpl_group_info(rli); thd = new THD; // note that contructor of THD uses DBUG_ ! thd->thread_stack = (char*)&thd; // remember where our stack is - thd->rpl_filter = mi->rpl_filter; + thd->system_thread_info.rpl_sql_info= &sql_info; DBUG_ASSERT(rli->inited); DBUG_ASSERT(rli->mi == mi); @@ -4690,7 +4687,7 @@ err_during_init: mysql_cond_broadcast(&rli->data_cond); rli->ignore_log_space_limit= 0; /* don't need any lock */ /* we die so won't remember charset - re-update them on next thread start */ - rli->cached_charset_invalidate(); + thd->system_thread_info.rpl_sql_info->cached_charset_invalidate(); /* TODO: see if we can do this conditionally in next_event() instead diff --git a/sql/slave.h b/sql/slave.h index 3981a9d4f2c..aa3976f6e6c 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -51,6 +51,7 @@ class Relay_log_info; class Master_info; class Master_info_index; +struct rpl_group_info; struct rpl_parallel_thread; int init_intvar_from_file(int* var, IO_CACHE* f, int default_val); @@ -226,7 +227,7 @@ int init_relay_log_pos(Relay_log_info* rli,const char* log,ulonglong pos, int purge_relay_logs(Relay_log_info* rli, THD *thd, bool just_reset, const char** errmsg); void set_slave_thread_options(THD* thd); -void set_slave_thread_default_charset(THD *thd, Relay_log_info const *rli); +void set_slave_thread_default_charset(THD *thd, rpl_group_info *rgi); int rotate_relay_log(Master_info* mi); int apply_event_and_update_pos(Log_event* ev, THD* thd, struct rpl_group_info *rgi, diff --git a/sql/sp.cc b/sql/sp.cc index d3d692b8251..b5b543ead0e 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -1437,6 +1437,8 @@ bool lock_db_routines(THD *thd, char *db) uchar keybuf[MAX_KEY_LENGTH]; DBUG_ENTER("lock_db_routines"); + DBUG_ASSERT(ok_for_lower_case_names(db)); + /* mysql.proc will be re-opened during deletion, so we can ignore errors when opening the table here. The error handler is diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 92f7ac020f5..8a9e8ddc816 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -493,6 +493,7 @@ sp_name::init_qname(THD *thd) (int) m_db.length, (m_db.length ? m_db.str : ""), dot, ".", (int) m_name.length, m_name.str); + DBUG_ASSERT(ok_for_lower_case_names(m_db.str)); } @@ -1156,6 +1157,7 @@ sp_head::execute(THD *thd, bool merge_da_on_success) LEX *old_lex; Item_change_list old_change_list; String old_packet; + uint old_server_status; Reprepare_observer *save_reprepare_observer= thd->m_reprepare_observer; Object_creation_ctx *saved_creation_ctx; Diagnostics_area *da= thd->get_stmt_da(); @@ -1289,6 +1291,7 @@ sp_head::execute(THD *thd, bool merge_da_on_success) It is probably safe to use same thd->convert_buff everywhere. */ old_packet.swap(thd->packet); + old_server_status= thd->server_status; /* Switch to per-instruction arena here. We can do it since we cleanup @@ -1409,6 +1412,7 @@ sp_head::execute(THD *thd, bool merge_da_on_success) thd->spcont->pop_all_cursors(); // To avoid memory leaks after an error /* Restore all saved */ + thd->server_status= old_server_status; old_packet.swap(thd->packet); DBUG_ASSERT(thd->change_list.is_empty()); old_change_list.move_elements_to(&thd->change_list); diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index cd7ad3f8665..a0917c18aa3 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -38,6 +38,7 @@ #include "records.h" // READ_RECORD, read_record_info, // init_read_record, end_read_record #include "rpl_filter.h" // rpl_filter +#include "rpl_rli.h" #include <m_ctype.h> #include <stdarg.h> #include "sp_head.h" @@ -2561,7 +2562,7 @@ bool change_password(THD *thd, const char *host, const char *user, { TABLE_LIST tables; TABLE *table; - Rpl_filter *rpl_filter= thd->rpl_filter; + Rpl_filter *rpl_filter; /* Buffer should be extended when password length is extended. */ char buff[512]; ulong query_length=0; @@ -2598,7 +2599,8 @@ bool change_password(THD *thd, const char *host, const char *user, GRANT and REVOKE are applied the slave in/exclusion rules as they are some kind of updates to the mysql.% tables. */ - if (thd->slave_thread && rpl_filter->is_on()) + if (thd->slave_thread && + (rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter)->is_on()) { /* The tables must be marked "updating" so that tables_ok() takes them into @@ -5425,7 +5427,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, TABLE_LIST tables[3]; bool create_new_users=0; char *db_name, *table_name; - Rpl_filter *rpl_filter= thd->rpl_filter; + Rpl_filter *rpl_filter; DBUG_ENTER("mysql_table_grant"); if (!initialized) @@ -5515,7 +5517,8 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, GRANT and REVOKE are applied the slave in/exclusion rules as they are some kind of updates to the mysql.% tables. */ - if (thd->slave_thread && rpl_filter->is_on()) + if (thd->slave_thread && + (rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter)->is_on()) { /* The tables must be marked "updating" so that tables_ok() takes them into @@ -5702,7 +5705,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, TABLE_LIST tables[2]; bool create_new_users=0, result=0; char *db_name, *table_name; - Rpl_filter *rpl_filter= thd->rpl_filter; + Rpl_filter *rpl_filter; DBUG_ENTER("mysql_routine_grant"); if (!initialized) @@ -5737,7 +5740,8 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, GRANT and REVOKE are applied the slave in/exclusion rules as they are some kind of updates to the mysql.% tables. */ - if (thd->slave_thread && rpl_filter->is_on()) + if (thd->slave_thread && + (rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter)->is_on()) { /* The tables must be marked "updating" so that tables_ok() takes them into @@ -6173,7 +6177,7 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list, char tmp_db[SAFE_NAME_LEN+1]; bool create_new_users=0; TABLE_LIST tables[2]; - Rpl_filter *rpl_filter= thd->rpl_filter; + Rpl_filter *rpl_filter; DBUG_ENTER("mysql_grant"); if (!initialized) @@ -6222,7 +6226,8 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list, GRANT and REVOKE are applied the slave in/exclusion rules as they are some kind of updates to the mysql.% tables. */ - if (thd->slave_thread && rpl_filter->is_on()) + if (thd->slave_thread && + (rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter)->is_on()) { /* The tables must be marked "updating" so that tables_ok() takes them into @@ -8255,7 +8260,7 @@ void get_mqh(const char *user, const char *host, USER_CONN *uc) #define GRANT_TABLES 7 static int open_grant_tables(THD *thd, TABLE_LIST *tables) { - Rpl_filter *rpl_filter= thd->rpl_filter; + Rpl_filter *rpl_filter; DBUG_ENTER("open_grant_tables"); if (!initialized) @@ -8299,7 +8304,8 @@ static int open_grant_tables(THD *thd, TABLE_LIST *tables) GRANT and REVOKE are applied the slave in/exclusion rules as they are some kind of updates to the mysql.% tables. */ - if (thd->slave_thread && rpl_filter->is_on()) + if (thd->slave_thread && + (rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter)->is_on()) { /* The tables must be marked "updating" so that tables_ok() takes them into @@ -10867,7 +10873,6 @@ struct MPVIO_EXT :public MYSQL_PLUGIN_VIO uint pkt_len; } cached_server_packet; int packets_read, packets_written; ///< counters for send/received packets - uint connect_errors; ///< if there were connect errors for this host bool make_it_fail; /** when plugin returns a failure this tells us what really happened */ enum { SUCCESS, FAILURE, RESTART } status; @@ -11418,9 +11423,6 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio, */ DBUG_ASSERT(net->read_pos[pkt_len] == 0); - if (mpvio->connect_errors) - reset_host_connect_errors(thd->main_security_ctx.ip); - ulong client_capabilities= uint2korr(net->read_pos); if (client_capabilities & CLIENT_PROTOCOL_41) { @@ -11998,8 +12000,6 @@ static int do_auth_once(THD *thd, const LEX_STRING *auth_plugin_name, Perform the handshake, authorize the client and update thd sctx variables. @param thd thread handle - @param connect_errors number of previous failed connect attemps - from this host @param com_change_user_pkt_len size of the COM_CHANGE_USER packet (without the first, command, byte) or 0 if it's not a COM_CHANGE_USER (that is, if @@ -12008,8 +12008,7 @@ static int do_auth_once(THD *thd, const LEX_STRING *auth_plugin_name, @retval 0 success, thd is updated. @retval 1 error */ -bool acl_authenticate(THD *thd, uint connect_errors, - uint com_change_user_pkt_len) +bool acl_authenticate(THD *thd, uint com_change_user_pkt_len) { int res= CR_OK; MPVIO_EXT mpvio; @@ -12023,7 +12022,6 @@ bool acl_authenticate(THD *thd, uint connect_errors, mpvio.write_packet= server_mpvio_write_packet; mpvio.info= server_mpvio_info; mpvio.thd= thd; - mpvio.connect_errors= connect_errors; mpvio.status= MPVIO_EXT::FAILURE; mpvio.make_it_fail= false; mpvio.auth_info.host_or_ip= thd->security_ctx->host_or_ip; diff --git a/sql/sql_acl.h b/sql/sql_acl.h index df523fae1ca..1aeb123153e 100644 --- a/sql/sql_acl.h +++ b/sql/sql_acl.h @@ -202,7 +202,7 @@ my_bool acl_reload(THD *thd); void acl_free(bool end=0); ulong acl_get(const char *host, const char *ip, const char *user, const char *db, my_bool db_is_pattern); -bool acl_authenticate(THD *thd, uint connect_errors, uint com_change_user_pkt_len); +bool acl_authenticate(THD *thd, uint com_change_user_pkt_len); bool acl_getroot(Security_context *sctx, char *user, char *host, char *ip, char *db); bool acl_check_host(const char *host, const char *ip); diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc index 518ebdc511d..ee70914d331 100644 --- a/sql/sql_admin.cc +++ b/sql/sql_admin.cc @@ -1,5 +1,5 @@ -/* Copyright (c) 2010, 2013, Oracle and/or its affiliates. - Copyright (c) 2012, 2013, Monty Program Ab. +/* Copyright (c) 2010, 2014, Oracle and/or its affiliates. + Copyright (c) 2012, 2014, Monty Program Ab. 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 @@ -55,7 +55,7 @@ static bool admin_recreate_table(THD *thd, TABLE_LIST *table_list) DEBUG_SYNC(thd, "ha_admin_try_alter"); tmp_disable_binlog(thd); // binlogging is done by caller if wanted result_code= (open_temporary_tables(thd, table_list) || - mysql_recreate_table(thd, table_list)); + mysql_recreate_table(thd, table_list, false)); reenable_binlog(thd); /* mysql_recreate_table() can push OK or ERROR. @@ -719,7 +719,9 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, if (result_code == HA_ADMIN_OK) { DBUG_PRINT("admin", ("calling operator_func '%s'", operator_name)); + THD_STAGE_INFO(thd, stage_executing); result_code = (table->table->file->*operator_func)(thd, check_opt); + THD_STAGE_INFO(thd, stage_sending_data); DBUG_PRINT("admin", ("operator_func returned: %d", result_code)); } @@ -862,7 +864,7 @@ send_result_message: } if (protocol->write()) goto err; - + THD_STAGE_INFO(thd, stage_recreating_table); DBUG_PRINT("info", ("HA_ADMIN_TRY_ALTER, trying analyze...")); TABLE_LIST *save_next_local= table->next_local, *save_next_global= table->next_global; @@ -1080,6 +1082,7 @@ bool mysql_assign_to_keycache(THD* thd, TABLE_LIST* tables, KEY_CACHE *key_cache; DBUG_ENTER("mysql_assign_to_keycache"); + THD_STAGE_INFO(thd, stage_finding_key_cache); check_opt.init(); mysql_mutex_lock(&LOCK_global_system_variables); if (!(key_cache= get_key_cache(key_cache_name))) @@ -1199,8 +1202,7 @@ bool Sql_cmd_optimize_table::execute(THD *thd) goto error; /* purecov: inspected */ thd->enable_slow_log= opt_log_slow_admin_statements; res= (specialflag & SPECIAL_NO_NEW_FUNC) ? - - mysql_recreate_table(thd, first_table) : + mysql_recreate_table(thd, first_table, true) : mysql_admin_table(thd, first_table, &m_lex->check_opt, "optimize", TL_WRITE, 1, 0, 0, 0, &handler::ha_optimize, 0); diff --git a/sql/sql_alter.h b/sql/sql_alter.h index f0c0a873a5c..526442e83e2 100644 --- a/sql/sql_alter.h +++ b/sql/sql_alter.h @@ -1,4 +1,5 @@ -/* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2010, 2014, Oracle and/or its affiliates. + Copyright (c) 2013, 2014, Monty Program Ab. 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 @@ -73,6 +74,7 @@ public: static const uint ALTER_CONVERT = 1L << 10; // Set for FORCE + // Set for ENGINE(same engine) // Set by mysql_recreate_table() static const uint ALTER_RECREATE = 1L << 11; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 2f304b21706..7304dd7c128 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -887,6 +887,8 @@ void close_thread_tables(THD *thd) TABLE *table; DBUG_ENTER("close_thread_tables"); + THD_STAGE_INFO(thd, stage_closing_tables); + #ifdef EXTRA_DEBUG DBUG_PRINT("tcache", ("open tables:")); for (table= thd->open_tables; table; table= table->next) @@ -4604,6 +4606,8 @@ restart: #endif err: + THD_STAGE_INFO(thd, stage_after_opening_tables); + #ifdef WITH_WSREP if (WSREP(thd)) thd_proc_info(thd, "exit open_tables()"); @@ -5066,6 +5070,8 @@ end: close_thread_tables(thd); } + THD_STAGE_INFO(thd, stage_after_opening_tables); + #ifdef WITH_WSREP if (WSREP(thd)) thd_proc_info(thd, "End opening table"); @@ -8401,6 +8407,75 @@ void wrap_ident(THD *thd, Item **conds) thd->restore_active_arena(arena, &backup); } +/** + Prepare ON expression + + @param thd Thread handle + @param table Pointer to table list + @param is_update Update flag + + @retval TRUE error. + @retval FALSE OK. +*/ + +bool setup_on_expr(THD *thd, TABLE_LIST *table, bool is_update) +{ + uchar buff[STACK_BUFF_ALLOC]; // Max argument in function + if (check_stack_overrun(thd, STACK_MIN_SIZE, buff)) + return TRUE; // Fatal error flag is set! + for(; table; table= table->next_local) + { + TABLE_LIST *embedded; /* The table at the current level of nesting. */ + TABLE_LIST *embedding= table; /* The parent nested table reference. */ + do + { + embedded= embedding; + DBUG_PRINT("XXX", ("check: %s", table->alias)); + if (embedded->on_expr) + { + thd->where="on clause"; + embedded->on_expr->mark_as_condition_AND_part(embedded); + if ((!embedded->on_expr->fixed && + embedded->on_expr->fix_fields(thd, &embedded->on_expr)) || + embedded->on_expr->check_cols(1)) + return TRUE; + } + /* + If it's a semi-join nest, fix its "left expression", as it is used by + the SJ-Materialization + */ + if (embedded->sj_subq_pred) + { + Item **left_expr= &embedded->sj_subq_pred->left_expr; + if (!(*left_expr)->fixed && (*left_expr)->fix_fields(thd, left_expr)) + return TRUE; + } + + embedding= embedded->embedding; + } + while (embedding && + embedding->nested_join->join_list.head() == embedded); + + if (table->is_merged_derived()) + { + SELECT_LEX *select_lex= table->get_single_select(); + setup_on_expr(thd, select_lex->get_table_list(), is_update); + } + + /* process CHECK OPTION */ + if (is_update) + { + TABLE_LIST *view= table->top_table(); + if (view->effective_with_check) + { + if (view->prepare_check_option(thd)) + return TRUE; + thd->change_item_tree(&table->check_option, view->check_option); + } + } + } + return FALSE; +} /* Fix all conditions and outer join expressions. @@ -8425,7 +8500,6 @@ int setup_conds(THD *thd, TABLE_LIST *tables, List<TABLE_LIST> &leaves, { SELECT_LEX *select_lex= thd->lex->current_select; TABLE_LIST *table= NULL; // For HP compilers - List_iterator<TABLE_LIST> ti(leaves); /* it_is_update set to TRUE when tables of primary SELECT_LEX (SELECT_LEX which belong to LEX, i.e. most up SELECT) will be updated by @@ -8484,51 +8558,8 @@ int setup_conds(THD *thd, TABLE_LIST *tables, List<TABLE_LIST> &leaves, Apply fix_fields() to all ON clauses at all levels of nesting, including the ones inside view definitions. */ - while ((table= ti++)) - { - TABLE_LIST *embedded; /* The table at the current level of nesting. */ - TABLE_LIST *embedding= table; /* The parent nested table reference. */ - do - { - embedded= embedding; - if (embedded->on_expr) - { - /* Make a join an a expression */ - thd->where="on clause"; - embedded->on_expr->mark_as_condition_AND_part(embedded); - if ((!embedded->on_expr->fixed && - embedded->on_expr->fix_fields(thd, &embedded->on_expr)) || - embedded->on_expr->check_cols(1)) - goto err_no_arena; - } - /* - If it's a semi-join nest, fix its "left expression", as it is used by - the SJ-Materialization - */ - if (embedded->sj_subq_pred) - { - Item **left_expr= &embedded->sj_subq_pred->left_expr; - if (!(*left_expr)->fixed && (*left_expr)->fix_fields(thd, left_expr)) - goto err_no_arena; - } - - embedding= embedded->embedding; - } - while (embedding && - embedding->nested_join->join_list.head() == embedded); - - /* process CHECK OPTION */ - if (it_is_update) - { - TABLE_LIST *view= table->top_table(); - if (view->effective_with_check) - { - if (view->prepare_check_option(thd)) - goto err_no_arena; - thd->change_item_tree(&table->check_option, view->check_option); - } - } - } + if (setup_on_expr(thd, tables, it_is_update)) + goto err_no_arena; if (!thd->stmt_arena->is_conventional()) { diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index c6c5418e0cf..cf68ba36997 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -2306,6 +2306,8 @@ void Query_cache::invalidate(THD *thd, char *db) if (is_disabled()) DBUG_VOID_RETURN; + DBUG_ASSERT(ok_for_lower_case_names(db)); + bool restart= FALSE; /* Lock the query cache and queue all invalidation attempts to avoid diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 6a9c5dacf5a..198a67ac712 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -1144,7 +1144,6 @@ THD::THD() connection_name.length= 0; bzero(&variables, sizeof(variables)); - one_shot_set= 0; file_id = 0; query_id= 0; query_name_consts= 0; @@ -1450,7 +1449,6 @@ Sql_condition* THD::raise_condition(uint sql_errno, got_warning= 1; break; case Sql_condition::WARN_LEVEL_ERROR: - mysql_audit_general(this, MYSQL_AUDIT_GENERAL_ERROR, sql_errno, msg); break; default: DBUG_ASSERT(FALSE); @@ -1461,6 +1459,8 @@ Sql_condition* THD::raise_condition(uint sql_errno, if (level == Sql_condition::WARN_LEVEL_ERROR) { + mysql_audit_general(this, MYSQL_AUDIT_GENERAL_ERROR, sql_errno, msg); + is_slave_error= 1; // needed to catch query errors during replication if (!da->is_error()) diff --git a/sql/sql_class.h b/sql/sql_class.h index 4e1caca581a..b64006d7b71 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -84,6 +84,8 @@ class Parser_state; class Rows_log_event; class Sroutine_hash_entry; class user_var_entry; +class rpl_io_thread_info; +class rpl_sql_thread_info; enum enum_ha_read_modes { RFIRST, RNEXT, RPREV, RLAST, RKEY, RNEXT_SAME }; enum enum_duplicates { DUP_ERROR, DUP_REPLACE, DUP_UPDATE }; @@ -528,6 +530,7 @@ typedef struct system_variables ulonglong join_buff_size; ulonglong sortbuff_size; ulonglong group_concat_max_len; + ulonglong default_regex_flags; ha_rows select_limit; ha_rows max_join_size; ha_rows expensive_subquery_limit; @@ -1826,8 +1829,10 @@ public: /* Slave applier execution context */ rpl_group_info* rgi_slave; - /* Used to SLAVE SQL thread */ - Rpl_filter* rpl_filter; + union { + rpl_io_thread_info *rpl_io_info; + rpl_sql_thread_info *rpl_sql_info; + } system_thread_info; void reset_for_next_command(); /* @@ -2604,7 +2609,7 @@ public: char default_master_connection_buff[MAX_CONNECTION_NAME+1]; uint8 password; /* 0, 1 or 2 */ uint8 failed_com_change_user; - bool slave_thread, one_shot_set; + bool slave_thread; bool extra_port; /* If extra connection */ bool no_errors; @@ -3121,8 +3126,11 @@ public: Clear the current error, if any. We do not clear is_fatal_error or is_fatal_sub_stmt_error since we assume this is never called if the fatal error is set. + @todo: To silence an error, one should use Internal_error_handler - mechanism. In future this function will be removed. + mechanism. Issuing an error that can be possibly later "cleared" is not + compatible with other installed error handlers and audit plugins. + In future this function will be removed. */ inline void clear_error() { diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc index 49d9ae5a76f..bea331fe8ee 100644 --- a/sql/sql_connect.cc +++ b/sql/sql_connect.cc @@ -1070,7 +1070,7 @@ static int check_connection(THD *thd) return 1; /* The error is set by alloc(). */ } - auth_rc= acl_authenticate(thd, connect_errors, 0); + auth_rc= acl_authenticate(thd, 0); if (auth_rc == 0 && connect_errors != 0) { /* diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 13cbcdd9f08..76288e94c75 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -2468,7 +2468,7 @@ struct LEX: public Query_tables_list uint16 create_view_algorithm; uint8 create_view_check; uint8 context_analysis_only; - bool drop_temporary, local_file, one_shot_set; + bool drop_temporary, local_file; bool check_exists; bool autocommit; bool verbose, no_write_to_binlog; diff --git a/sql/sql_locale.cc b/sql/sql_locale.cc index b2b112ed4ba..c7d21ffd424 100644 --- a/sql/sql_locale.cc +++ b/sql/sql_locale.cc @@ -3247,6 +3247,75 @@ MY_LOCALE my_locale_el_GR ); /***** LOCALE END el_GR *****/ + +/***** LOCALE BEGIN rm_CH: Romansh - Switzerland *****/ +static const char *my_locale_month_names_rm_CH[13]= +{ + "schaner", "favrer", "mars", "avrigl", "matg", "zercladur", + "fanadur", "avust", "settember", "october", "november", "december", NullS +}; + +static const char *my_locale_ab_month_names_rm_CH[13]= +{ + "schan", "favr", "mars", "avr", "matg", "zercl", + "fan", "avust", "sett", "oct", "nov", "dec", NullS +}; + +static const char *my_locale_day_names_rm_CH[8]= +{ + "glindesdi", "mardi", "mesemna", "gievgia", + "venderdi", "sonda", "dumengia", NullS +}; + +static const char *my_locale_ab_day_names_rm_CH[8]= +{ + "gli", "ma", "me", "gie", "ve", "so", "du", NullS +}; + +static TYPELIB my_locale_typelib_month_names_rm_CH= +{ + array_elements(my_locale_month_names_rm_CH) - 1, + "", my_locale_month_names_rm_CH, NULL +}; + +static TYPELIB my_locale_typelib_ab_month_names_rm_CH= +{ + array_elements(my_locale_ab_month_names_rm_CH) - 1, + "", my_locale_ab_month_names_rm_CH, NULL +}; + +static TYPELIB my_locale_typelib_day_names_rm_CH= +{ + array_elements(my_locale_day_names_rm_CH) - 1, + "", my_locale_day_names_rm_CH, NULL +}; + +static TYPELIB my_locale_typelib_ab_day_names_rm_CH= +{ + array_elements(my_locale_ab_day_names_rm_CH) - 1, + "", my_locale_ab_day_names_rm_CH, NULL +}; + +MY_LOCALE my_locale_rm_CH +( + 110, + "rm_CH", + "Romansh - Switzerland", + FALSE, + &my_locale_typelib_month_names_rm_CH, + &my_locale_typelib_ab_month_names_rm_CH, + &my_locale_typelib_day_names_rm_CH, + &my_locale_typelib_ab_day_names_rm_CH, + 9, /* max mon name length */ + 9, /* max day name length */ + ',', /* decimal point rm_CH */ + '\'', /* thousands_sep rm_CH */ + "\x03\x03", /* grouping rm_CH */ + &global_errmsgs[en_US] +); +/***** LOCALE END rm_CH *****/ + + /* The list of all locales. Note, locales must be ordered according to their @@ -3365,6 +3434,7 @@ MY_LOCALE *my_locales[]= &my_locale_sv_FI, &my_locale_zh_HK, &my_locale_el_GR, + &my_locale_rm_CH, NULL }; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 08e8b535f76..039e79c2acf 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -177,8 +177,9 @@ const char *xa_state_names[]={ */ inline bool all_tables_not_ok(THD *thd, TABLE_LIST *tables) { - return thd->rpl_filter->is_on() && tables && !thd->spcont && - !thd->rpl_filter->tables_ok(thd->db, tables); + Rpl_filter *rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter; + return rpl_filter->is_on() && tables && !thd->spcont && + !rpl_filter->tables_ok(thd->db, tables); } #endif @@ -1395,7 +1396,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, auth_rc= 1; } else - auth_rc= acl_authenticate(thd, 0, packet_length); + auth_rc= acl_authenticate(thd, packet_length); mysql_audit_notify_connection_change_user(thd); if (auth_rc) @@ -2238,23 +2239,6 @@ bool alloc_query(THD *thd, const char *packet, uint packet_length) return FALSE; } -static void reset_one_shot_variables(THD *thd) -{ - thd->variables.character_set_client= - global_system_variables.character_set_client; - thd->variables.collation_connection= - global_system_variables.collation_connection; - thd->variables.collation_database= - global_system_variables.collation_database; - thd->variables.collation_server= - global_system_variables.collation_server; - thd->update_charset(); - thd->variables.time_zone= - global_system_variables.time_zone; - thd->variables.lc_time_names= &my_locale_en_US; - thd->one_shot_set= 0; -} - bool sp_process_definer(THD *thd) { @@ -2448,7 +2432,7 @@ mysql_execute_command(THD *thd) /* have table map for update for multi-update statement (BUG#37051) */ bool have_table_map_for_update= FALSE; /* */ - Rpl_filter *rpl_filter= thd->rpl_filter; + Rpl_filter *rpl_filter; #endif DBUG_ENTER("mysql_execute_command"); @@ -2562,9 +2546,6 @@ mysql_execute_command(THD *thd) { /* we warn the slave SQL thread */ my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0)); - if (thd->one_shot_set) - reset_one_shot_variables(thd); - DBUG_RETURN(0); } for (table=all_tables; table; table=table->next_global) @@ -2592,23 +2573,6 @@ mysql_execute_command(THD *thd) { /* we warn the slave SQL thread */ my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0)); - if (thd->one_shot_set) - { - /* - It's ok to check thd->one_shot_set here: - - The charsets in a MySQL 5.0 slave can change by both a binlogged - SET ONE_SHOT statement and the event-internal charset setting, - and these two ways to change charsets do not seems to work - together. - - At least there seems to be problems in the rli cache for - charsets if we are using ONE_SHOT. Note that this is normally no - problem because either the >= 5.0 slave reads a 4.1 binlog (with - ONE_SHOT) *or* or 5.0 binlog (without ONE_SHOT) but never both." - */ - reset_one_shot_variables(thd); - } DBUG_RETURN(0); } /* @@ -4114,11 +4078,6 @@ end_with_restore_list: goto error; if (!(res= sql_set_variables(thd, lex_var_list))) { - /* - If the previous command was a SET ONE_SHOT, we don't want to forget - about the ONE_SHOT property of that SET. So we use a |= instead of = . - */ - thd->one_shot_set|= lex->one_shot_set; my_ok(thd); } else @@ -4218,12 +4177,15 @@ end_with_restore_list: above was not called. So we have to check rules again here. */ #ifdef HAVE_REPLICATION - if (thd->slave_thread && - (!rpl_filter->db_ok(lex->name.str) || - !rpl_filter->db_ok_with_wild_table(lex->name.str))) + if (thd->slave_thread) { - my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0)); - break; + rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter; + if (!rpl_filter->db_ok(lex->name.str) || + !rpl_filter->db_ok_with_wild_table(lex->name.str)) + { + my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0)); + break; + } } #endif if (check_access(thd, CREATE_ACL, lex->name.str, NULL, NULL, 1, 0)) @@ -4247,12 +4209,15 @@ end_with_restore_list: above was not called. So we have to check rules again here. */ #ifdef HAVE_REPLICATION - if (thd->slave_thread && - (!rpl_filter->db_ok(lex->name.str) || - !rpl_filter->db_ok_with_wild_table(lex->name.str))) + if (thd->slave_thread) { - my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0)); - break; + rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter; + if (!rpl_filter->db_ok(lex->name.str) || + !rpl_filter->db_ok_with_wild_table(lex->name.str)) + { + my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0)); + break; + } } #endif if (check_access(thd, DROP_ACL, lex->name.str, NULL, NULL, 1, 0)) @@ -4265,13 +4230,16 @@ end_with_restore_list: { LEX_STRING *db= & lex->name; #ifdef HAVE_REPLICATION - if (thd->slave_thread && - (!rpl_filter->db_ok(db->str) || - !rpl_filter->db_ok_with_wild_table(db->str))) + if (thd->slave_thread) { - res= 1; - my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0)); - break; + rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter; + if (!rpl_filter->db_ok(db->str) || + !rpl_filter->db_ok_with_wild_table(db->str)) + { + res= 1; + my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0)); + break; + } } #endif if (check_db_name(db)) @@ -4309,12 +4277,15 @@ end_with_restore_list: above was not called. So we have to check rules again here. */ #ifdef HAVE_REPLICATION - if (thd->slave_thread && - (!rpl_filter->db_ok(db->str) || - !rpl_filter->db_ok_with_wild_table(db->str))) + if (thd->slave_thread) { - my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0)); - break; + rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter; + if (!rpl_filter->db_ok(db->str) || + !rpl_filter->db_ok_with_wild_table(db->str)) + { + my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0)); + break; + } } #endif if (check_access(thd, ALTER_ACL, db->str, NULL, NULL, 1, 0)) @@ -5536,19 +5507,6 @@ create_sp_error: THD_STAGE_INFO(thd, stage_query_end); thd->update_stats(); - /* - Binlog-related cleanup: - Reset system variables temporarily modified by SET ONE SHOT. - - Exception: If this is a SET, do nothing. This is to allow - mysqlbinlog to print many SET commands (in this case we want the - charset temp setting to live until the real query). This is also - needed so that SET CHARACTER_SET_CLIENT... does not cancel itself - immediately. - */ - if (thd->one_shot_set && lex->sql_command != SQLCOM_SET_OPTION) - reset_one_shot_variables(thd); - goto finish; error: @@ -5593,7 +5551,6 @@ finish: } /* Free tables */ - THD_STAGE_INFO(thd, stage_closing_tables); close_thread_tables(thd); #ifdef WITH_WSREP thd->wsrep_consistency_check= NO_CONSISTENCY_CHECK; diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index df76c9f6417..1348ebec3c8 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -3213,15 +3213,21 @@ static void plugin_vars_free_values(sys_var *vars) static SHOW_TYPE pluginvar_show_type(st_mysql_sys_var *plugin_var) { - switch (plugin_var->flags & PLUGIN_VAR_TYPEMASK) { + switch (plugin_var->flags & (PLUGIN_VAR_TYPEMASK | PLUGIN_VAR_UNSIGNED)) { case PLUGIN_VAR_BOOL: return SHOW_MY_BOOL; case PLUGIN_VAR_INT: - return SHOW_INT; + return SHOW_SINT; + case PLUGIN_VAR_INT | PLUGIN_VAR_UNSIGNED: + return SHOW_UINT; case PLUGIN_VAR_LONG: - return SHOW_LONG; + return SHOW_SLONG; + case PLUGIN_VAR_LONG | PLUGIN_VAR_UNSIGNED: + return SHOW_ULONG; case PLUGIN_VAR_LONGLONG: - return SHOW_LONGLONG; + return SHOW_SLONGLONG; + case PLUGIN_VAR_LONGLONG | PLUGIN_VAR_UNSIGNED: + return SHOW_ULONGLONG; case PLUGIN_VAR_STR: return SHOW_CHAR_PTR; case PLUGIN_VAR_ENUM: diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 240fc953f1d..b97cedb5b05 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -549,6 +549,7 @@ fix_inner_refs(THD *thd, List<Item> &all_fields, SELECT_LEX *select, static void remove_redundant_subquery_clauses(st_select_lex *subq_select_lex) { + DBUG_ENTER("remove_redundant_subquery_clauses"); Item_subselect *subq_predicate= subq_select_lex->master_unit()->item; /* The removal should happen for IN, ALL, ANY and EXISTS subqueries, @@ -558,7 +559,7 @@ void remove_redundant_subquery_clauses(st_select_lex *subq_select_lex) b) SELECT a, (<single row subquery) FROM t1 */ if (subq_predicate->substype() == Item_subselect::SINGLEROW_SUBS) - return; + DBUG_VOID_RETURN; /* A subquery that is not single row should be one of IN/ALL/ANY/EXISTS. */ DBUG_ASSERT (subq_predicate->substype() == Item_subselect::EXISTS_SUBS || @@ -568,6 +569,7 @@ void remove_redundant_subquery_clauses(st_select_lex *subq_select_lex) { subq_select_lex->join->select_distinct= false; subq_select_lex->options&= ~SELECT_DISTINCT; + DBUG_PRINT("info", ("DISTINCT removed")); } /* @@ -577,8 +579,13 @@ void remove_redundant_subquery_clauses(st_select_lex *subq_select_lex) if (subq_select_lex->group_list.elements && !subq_select_lex->with_sum_func && !subq_select_lex->join->having) { + for (ORDER *ord= subq_select_lex->group_list.first; ord; ord= ord->next) + { + (*ord->item)->walk(&Item::eliminate_subselect_processor, FALSE, NULL); + } subq_select_lex->join->group_list= NULL; subq_select_lex->group_list.empty(); + DBUG_PRINT("info", ("GROUP BY removed")); } /* @@ -593,6 +600,7 @@ void remove_redundant_subquery_clauses(st_select_lex *subq_select_lex) subq_select_lex->group_list.empty(); } */ + DBUG_VOID_RETURN; } @@ -700,7 +708,9 @@ JOIN::prepare(Item ***rref_pointer_array, if (!(select_options & OPTION_SETUP_TABLES_DONE) && setup_tables_and_check_access(thd, &select_lex->context, join_list, tables_list, select_lex->leaf_tables, - FALSE, SELECT_ACL, SELECT_ACL, FALSE)) + FALSE, SELECT_ACL, SELECT_ACL, + (thd->lex->sql_command == + SQLCOM_UPDATE_MULTI))) DBUG_RETURN(-1); /* @@ -5274,7 +5284,8 @@ static bool sort_and_filter_keyuse(THD *thd, DYNAMIC_ARRAY *keyuse, { if (!use->is_for_hash_join()) { - if (!use->used_tables && use->optimize != KEY_OPTIMIZE_REF_OR_NULL) + if (!(use->used_tables & ~OUTER_REF_TABLE_BIT) && + use->optimize != KEY_OPTIMIZE_REF_OR_NULL) use->table->const_key_parts[use->key]|= use->keypart_map; if (use->keypart != FT_KEYPART) { @@ -5557,7 +5568,20 @@ void set_position(JOIN *join,uint idx,JOIN_TAB *table,KEYUSE *key) } -/* Estimate of the number matching candidates in the joined table */ +/* + Estimate how many records we will get if we read just this table and apply + a part of WHERE that can be checked for it. + + @detail + Estimate how many records we will get if we + - read the given table with its "independent" access method (either quick + select or full table/index scan), + - apply the part of WHERE that refers only to this table. + + @seealso + table_cond_selectivity() produces selectivity of condition that is checked + after joining rows from this table to rows from preceding tables. +*/ inline double matching_candidates_in_table(JOIN_TAB *s, bool with_found_constraint, @@ -7236,14 +7260,25 @@ double table_multi_eq_cond_selectivity(JOIN *join, uint idx, JOIN_TAB *s, /** @brief - Get the selectivity of conditions when joining a table + Get the selectivity of conditions when joining a table @param join The optimized join @param s The table to be joined for evaluation @param rem_tables The bitmap of tables to be joined later + @detail + Get selectivity of conditions that can be applied when joining this table + with previous tables. + + For quick selects and full table scans, selectivity of COND(this_table) + is accounted for in matching_candidates_in_table(). Here, we only count + selectivity of COND(this_table, previous_tables). + + For other access methods, we need to calculate selectivity of the whole + condition, "COND(this_table) AND COND(this_table, previous_tables)". + @retval - selectivity of the conditions imposed on the rows of s + selectivity of the conditions imposed on the rows of s */ static @@ -7255,26 +7290,84 @@ double table_cond_selectivity(JOIN *join, uint idx, JOIN_TAB *s, TABLE *table= s->table; MY_BITMAP *read_set= table->read_set; double sel= s->table->cond_selectivity; - double table_records= table->stat_records(); POSITION *pos= &join->positions[idx]; uint keyparts= 0; uint found_part_ref_or_null= 0; - /* Discount the selectivity of the access method used to join table s */ - if (s->quick && s->quick->index != MAX_KEY) + if (pos->key != 0) { - if (pos->key == 0 && table_records > 0) - { - sel/= table->quick_rows[s->quick->index]/table_records; - } - } - else if (pos->key != 0) - { - /* A ref/ access or hash join is used to join table */ + /* + A ref access or hash join is used for this table. ref access is created + from + + tbl.keypart1=expr1 AND tbl.keypart2=expr2 AND ... + + and it will only return rows for which this condition is satisified. + Suppose, certain expr{i} is a constant. Since ref access only returns + rows that satisfy + + tbl.keypart{i}=const (*) + + then selectivity of this equality should not be counted in return value + of this function. This function uses the value of + + table->cond_selectivity=selectivity(COND(tbl)) (**) + + as a starting point. This value includes selectivity of equality (*). We + should somehow discount it. + + Looking at calculate_cond_selectivity_for_table(), one can see that that + the value is not necessarily a direct multiplicand in + table->cond_selectivity + + There are three possible ways to discount + 1. There is a potential range access on t.keypart{i}=const. + (an important special case: the used ref access has a const prefix for + which a range estimate is available) + + 2. The field has a histogram. field[x]->cond_selectivity has the data. + + 3. Use index stats on this index: + rec_per_key[key_part+1]/rec_per_key[key_part] + + (TODO: more details about the "t.key=othertable.col" case) + */ KEYUSE *keyuse= pos->key; KEYUSE *prev_ref_keyuse= keyuse; uint key= keyuse->key; - do + + /* + Check if we have a prefix of key=const that matches a quick select. + */ + if (!is_hash_join_key_no(key)) + { + table_map quick_key_map= (table_map(1) << table->quick_key_parts[key]) - 1; + if (table->quick_rows[key] && + !(quick_key_map & ~table->const_key_parts[key])) + { + /* + Ok, there is an equality for each of the key parts used by the + quick select. This means, quick select's estimate can be reused to + discount the selectivity of a prefix of a ref access. + */ + for (; quick_key_map & 1 ; quick_key_map>>= 1) + { + while (keyuse->table == table && keyuse->key == key && + keyuse->keypart == keyparts) + { + keyuse++; + } + keyparts++; + } + sel /= (double)table->quick_rows[key] / (double) table->stat_records(); + } + } + + /* + Go through the "keypart{N}=..." equalities and find those that were + already taken into account in table->cond_selectivity. + */ + while (keyuse->table == table && keyuse->key == key) { if (!(keyuse->used_tables & (rem_tables | table->map))) { @@ -7288,22 +7381,35 @@ double table_cond_selectivity(JOIN *join, uint idx, JOIN_TAB *s, else { if (keyparts == keyuse->keypart && - !(~(keyuse->val->used_tables()) & pos->ref_depend_map) && + !((keyuse->val->used_tables()) & ~pos->ref_depend_map) && !(found_part_ref_or_null & keyuse->optimize)) { + /* Found a KEYUSE object that will be used by ref access */ keyparts++; found_part_ref_or_null|= keyuse->optimize & ~KEY_OPTIMIZE_EQ; } } + if (keyparts > keyuse->keypart) { + /* Ok this is the keyuse that will be used for ref access */ uint fldno; if (is_hash_join_key_no(key)) fldno= keyuse->keypart; else fldno= table->key_info[key].key_part[keyparts-1].fieldnr - 1; if (keyuse->val->const_item()) - sel*= table->field[fldno]->cond_selectivity; + { + sel /= table->field[fldno]->cond_selectivity; + /* + TODO: we could do better here: + 1. cond_selectivity might be =1 (the default) because quick + select on some index prevented us from analyzing + histogram for this column. + 2. we could get an estimate through this? + rec_per_key[key_part-1] / rec_per_key[key_part] + */ + } if (keyparts > 1) { ref_keyuse_steps[keyparts-2]= keyuse - prev_ref_keyuse; @@ -7313,13 +7419,18 @@ double table_cond_selectivity(JOIN *join, uint idx, JOIN_TAB *s, } } keyuse++; - } while (keyuse->table == table && keyuse->key == key); + } } else { + /* + The table is accessed with full table scan, or quick select. + Selectivity of COND(table) is already accounted for in + matching_candidates_in_table(). + */ sel= 1; } - + /* If the field f from the table is equal to a field from one the earlier joined tables then the selectivity of the range conditions @@ -14267,7 +14378,7 @@ optimize_cond(JOIN *join, COND *conds, conds= remove_eq_conds(thd, conds, cond_value); if (conds && conds->type() == Item::COND_ITEM && ((Item_cond*) conds)->functype() == Item_func::COND_AND_FUNC) - join->cond_equal= &((Item_cond_and*) conds)->cond_equal; + *cond_equal= &((Item_cond_and*) conds)->cond_equal; DBUG_EXECUTE("info",print_where(conds,"after remove", QT_ORDINARY);); } DBUG_RETURN(conds); @@ -14884,7 +14995,7 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value) static bool test_if_equality_guarantees_uniqueness(Item *l, Item *r) { - return r->const_item() && + return (r->const_item() || !(r->used_tables() & ~OUTER_REF_TABLE_BIT)) && item_cmp_type(l->cmp_type(), r->cmp_type()) == l->cmp_type() && (l->cmp_type() != STRING_RESULT || l->collation.collation == r->collation.collation); @@ -24498,7 +24609,7 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table, double fanout= 1; ha_rows table_records= table->stat_records(); bool group= join && join->group && order == join->group_list; - ha_rows ref_key_quick_rows= HA_POS_ERROR; + ha_rows refkey_rows_estimate= table->quick_condition_rows; const bool has_limit= (select_limit_arg != HA_POS_ERROR); /* @@ -24524,10 +24635,6 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table, else keys= usable_keys; - if (ref_key >= 0 && ref_key != MAX_KEY && - table->covering_keys.is_set(ref_key)) - ref_key_quick_rows= table->quick_rows[ref_key]; - if (join) { uint tablenr= tab - join->join_tab; @@ -24538,6 +24645,22 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table, else read_time= table->file->scan_time(); + /* + Calculate the selectivity of the ref_key for REF_ACCESS. For + RANGE_ACCESS we use table->quick_condition_rows. + */ + if (ref_key >= 0 && tab->type == JT_REF) + { + if (table->quick_keys.is_set(ref_key)) + refkey_rows_estimate= table->quick_rows[ref_key]; + else + { + const KEY *ref_keyinfo= table->key_info + ref_key; + refkey_rows_estimate= ref_keyinfo->rec_per_key[tab->ref.key_parts - 1]; + } + set_if_bigger(refkey_rows_estimate, 1); + } + for (nr=0; nr < table->s->keys ; nr++) { int direction; @@ -24654,17 +24777,17 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table, with ref_key. Thus, to select first N records we have to scan N/selectivity(ref_key) index entries. selectivity(ref_key) = #scanned_records/#table_records = - table->quick_condition_rows/table_records. + refkey_rows_estimate/table_records. In any case we can't select more than #table_records. - N/(table->quick_condition_rows/table_records) > table_records - <=> N > table->quick_condition_rows. - */ - if (select_limit > table->quick_condition_rows) + N/(refkey_rows_estimate/table_records) > table_records + <=> N > refkey_rows_estimate. + */ + if (select_limit > refkey_rows_estimate) select_limit= table_records; else select_limit= (ha_rows) (select_limit * (double) table_records / - table->quick_condition_rows); + refkey_rows_estimate); rec_per_key= keyinfo->actual_rec_per_key(keyinfo->user_defined_key_parts-1); set_if_bigger(rec_per_key, 1); /* @@ -24684,8 +24807,12 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table, index_scan_time < read_time) { ha_rows quick_records= table_records; + ha_rows refkey_select_limit= (ref_key >= 0 && + table->covering_keys.is_set(ref_key)) ? + refkey_rows_estimate : + HA_POS_ERROR; if ((is_best_covering && !is_covering) || - (is_covering && ref_key_quick_rows < select_limit)) + (is_covering && refkey_select_limit < select_limit)) continue; if (table->quick_keys.is_set(nr)) quick_records= table->quick_rows[nr]; diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 4f2fccd01b1..205697e4466 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -945,9 +945,12 @@ public: is_handled= TRUE; break; + case ER_BAD_FIELD_ERROR: + case ER_SP_DOES_NOT_EXIST: case ER_NO_SUCH_TABLE: case ER_NO_SUCH_TABLE_IN_ENGINE: - /* Established behavior: warn if underlying tables are missing. */ + /* Established behavior: warn if underlying tables, columns, or functions + are missing. */ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_VIEW_INVALID, ER(ER_VIEW_INVALID), @@ -956,15 +959,6 @@ public: is_handled= TRUE; break; - case ER_SP_DOES_NOT_EXIST: - /* Established behavior: warn if underlying functions are missing. */ - push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, - ER_VIEW_INVALID, - ER(ER_VIEW_INVALID), - m_top_view->get_db_name(), - m_top_view->get_table_name()); - is_handled= TRUE; - break; default: is_handled= FALSE; } @@ -3088,7 +3082,7 @@ static bool show_status_array(THD *thd, const char *wild, end= int10_to_str((long) *(uint*) value, buff, 10); break; case SHOW_SINT: - end= int10_to_str((long) *(uint*) value, buff, -10); + end= int10_to_str((long) *(int*) value, buff, -10); break; case SHOW_SLONG: end= int10_to_str(*(long*) value, buff, -10); @@ -4636,25 +4630,7 @@ end: } -/** - Trigger_error_handler is intended to intercept and silence SQL conditions - that might happen during trigger loading for SHOW statements. - The potential SQL conditions are: - - - ER_PARSE_ERROR -- this error is thrown if a trigger definition file - is damaged or contains invalid CREATE TRIGGER statement. That should - not happen in normal life. - - - ER_TRG_NO_DEFINER -- this warning is thrown when we're loading a - trigger created/imported in/from the version of MySQL, which does not - support trigger definers. - - - ER_TRG_NO_CREATION_CTX -- this warning is thrown when we're loading a - trigger created/imported in/from the version of MySQL, which does not - support trigger creation contexts. -*/ - -class Trigger_error_handler : public Internal_error_handler +class Warnings_only_error_handler : public Internal_error_handler { public: bool handle_condition(THD *thd, @@ -4669,12 +4645,16 @@ public: sql_errno == ER_TRG_NO_CREATION_CTX) return true; - return false; + if (level != Sql_condition::WARN_LEVEL_ERROR) + return false; + + if (!thd->get_stmt_da()->is_error()) + thd->get_stmt_da()->set_error_status(sql_errno, msg, sqlstate, *cond_hdl); + return true; // handled! } }; - /** @brief Fill I_S tables whose data are retrieved from frm files and storage engine @@ -4884,25 +4864,11 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) if (!(table_open_method & ~OPEN_FRM_ONLY) && db_name != &INFORMATION_SCHEMA_NAME) { - /* - Here we need to filter out warnings, which can happen - during loading of triggers in fill_schema_table_from_frm(), - because we don't need those warnings to pollute output of - SELECT from I_S / SHOW-statements. - */ - - Trigger_error_handler err_handler; - thd->push_internal_handler(&err_handler); - - int res= fill_schema_table_from_frm(thd, tables, schema_table, - db_name, table_name, - schema_table_idx, - &open_tables_state_backup, - can_deadlock); - - thd->pop_internal_handler(); - - if (!res) + if (!fill_schema_table_from_frm(thd, tables, schema_table, + db_name, table_name, + schema_table_idx, + &open_tables_state_backup, + can_deadlock)) continue; } @@ -8050,95 +8016,6 @@ int make_schema_select(THD *thd, SELECT_LEX *sel, } -/** - Fill INFORMATION_SCHEMA-table, leave correct Diagnostics_area / - Warning_info state after itself. - - This function is a wrapper around ST_SCHEMA_TABLE::fill_table(), which - may "partially silence" some errors. The thing is that during - fill_table() many errors might be emitted. These errors stem from the - nature of fill_table(). - - For example, SELECT ... FROM INFORMATION_SCHEMA.xxx WHERE TABLE_NAME = 'xxx' - results in a number of 'Table <db name>.xxx does not exist' errors, - because fill_table() tries to open the 'xxx' table in every possible - database. - - Those errors are cleared (the error status is cleared from - Diagnostics_area) inside fill_table(), but they remain in Warning_info - (Warning_info is not cleared because it may contain useful warnings). - - This function is responsible for making sure that Warning_info does not - contain warnings corresponding to the cleared errors. - - @note: THD::no_warnings_for_error used to be set before calling - fill_table(), thus those errors didn't go to Warning_info. This is not - the case now (THD::no_warnings_for_error was eliminated as a hack), so we - need to take care of those warnings here. - - @param thd Thread context. - @param table_list I_S table. - @param join_table JOIN/SELECT table. - - @return Error status. - @retval TRUE Error. - @retval FALSE Success. -*/ -static bool do_fill_table(THD *thd, - TABLE_LIST *table_list, - JOIN_TAB *join_table) -{ - // NOTE: fill_table() may generate many "useless" warnings, which will be - // ignored afterwards. On the other hand, there might be "useful" - // warnings, which should be presented to the user. Warning_info usually - // stores no more than THD::variables.max_error_count warnings. - // The problem is that "useless warnings" may occupy all the slots in the - // Warning_info, so "useful warnings" get rejected. In order to avoid - // that problem we create a Warning_info instance, which is capable of - // storing "unlimited" number of warnings. - Diagnostics_area *da= thd->get_stmt_da(); - Warning_info wi_tmp(thd->query_id, true, true); - - da->push_warning_info(&wi_tmp); - - Item *item= join_table->select_cond; - if (join_table->cache_select && - join_table->cache_select->cond) - { - /* - If join buffering is used, we should use the condition that is attached - to the join cache. Cache condition has a part of WHERE that can be - checked when we're populating this table. - join_tab->select_cond is of no interest, because it only has conditions - that depend on both this table and previous tables in the join order. - */ - item= join_table->cache_select->cond; - } - bool res= table_list->schema_table->fill_table(thd, table_list, item); - - da->pop_warning_info(); - - // Pass an error if any. - - if (da->is_error()) - { - da->push_warning(thd, - da->sql_errno(), - da->get_sqlstate(), - Sql_condition::WARN_LEVEL_ERROR, - da->message()); - } - - // Pass warnings (if any). - // - // Filter out warnings with WARN_LEVEL_ERROR level, because they - // correspond to the errors which were filtered out in fill_table(). - da->copy_non_errors_from_wi(thd, &wi_tmp); - - return res; -} - - /* Fill temporary schema tables before SELECT @@ -8158,8 +8035,13 @@ bool get_schema_tables_result(JOIN *join, THD *thd= join->thd; LEX *lex= thd->lex; bool result= 0; + const char *old_proc_info; DBUG_ENTER("get_schema_tables_result"); + Warnings_only_error_handler err_handler; + thd->push_internal_handler(&err_handler); + old_proc_info= thd_proc_info(thd, "Filling schema table"); + for (JOIN_TAB *tab= first_linear_tab(join, WITH_CONST_TABLES); tab; tab= next_linear_tab(join, tab, WITHOUT_BUSH_ROOTS)) @@ -8212,20 +8094,56 @@ bool get_schema_tables_result(JOIN *join, else table_list->table->file->stats.records= 0; - if (do_fill_table(thd, table_list, tab)) + + Item *cond= tab->select_cond; + if (tab->cache_select && tab->cache_select->cond) + { + /* + If join buffering is used, we should use the condition that is + attached to the join cache. Cache condition has a part of WHERE that + can be checked when we're populating this table. + join_tab->select_cond is of no interest, because it only has + conditions that depend on both this table and previous tables in the + join order. + */ + cond= tab->cache_select->cond; + } + + if (table_list->schema_table->fill_table(thd, table_list, cond)) { result= 1; join->error= 1; tab->read_record.table->file= table_list->table->file; table_list->schema_table_state= executed_place; - if (!thd->is_error()) - my_error(ER_UNKNOWN_ERROR, MYF(0)); break; } tab->read_record.table->file= table_list->table->file; table_list->schema_table_state= executed_place; } } + thd->pop_internal_handler(); + if (thd->is_error()) + { + /* + This hack is here, because I_S code uses thd->clear_error() a lot. + Which means, a Warnings_only_error_handler cannot handle the error + corectly as it does not know whether an error is real (e.g. caused + by tab->select_cond->val_int()) or will be cleared later. + Thus it ignores all errors, and the real one (that is, the error + that was not cleared) is pushed now. + + It also means that an audit plugin cannot process the error correctly + either. See also thd->clear_error() + */ + thd->get_stmt_da()->push_warning(thd, + thd->get_stmt_da()->sql_errno(), + thd->get_stmt_da()->get_sqlstate(), + Sql_condition::WARN_LEVEL_ERROR, + thd->get_stmt_da()->message()); + } + else if (result) + my_error(ER_UNKNOWN_ERROR, MYF(0)); + thd_proc_info(thd, old_proc_info); DBUG_RETURN(result); } diff --git a/sql/sql_state.c b/sql/sql_state.c index c733d4b37c0..2bfd61d6696 100644 --- a/sql/sql_state.c +++ b/sql/sql_state.c @@ -1,4 +1,5 @@ /* Copyright (C) 2000-2003 MySQL AB + Use is subject to license terms 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 diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 2ffe201e796..d9702472221 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -1,6 +1,6 @@ /* - Copyright (c) 2000, 2013, Oracle and/or its affiliates. - Copyright (c) 2010, 2013, Monty Program Ab. + Copyright (c) 2000, 2014, Oracle and/or its affiliates. + Copyright (c) 2010, 2014, Monty Program Ab. 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 @@ -6113,6 +6113,9 @@ static bool fill_alter_inplace_info(THD *thd, ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_REMOVE_PARTITIONING; if (alter_info->flags & Alter_info::ALTER_ALL_PARTITION) ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_ALL_PARTITION; + /* Check for: ALTER TABLE FORCE, ALTER TABLE ENGINE and OPTIMIZE TABLE. */ + if (alter_info->flags & Alter_info::ALTER_RECREATE) + ha_alter_info->handler_flags|= Alter_inplace_info::RECREATE_TABLE; /* If we altering table with old VARCHAR fields we will be automatically @@ -6796,12 +6799,8 @@ static bool is_inplace_alter_impossible(TABLE *table, if (table->s->tmp_table) DBUG_RETURN(true); - /* - We also test if OPTIMIZE TABLE was given and was mapped to alter table. - In that case we always do full copy (ALTER_RECREATE is set in this case). - - For the ALTER TABLE tbl_name ORDER BY ... we also always use copy + For the ALTER TABLE tbl_name ORDER BY ... we always use copy algorithm. In theory, this operation can be done in-place by some engine, but since a) no current engine does this and b) our current API lacks infrastructure for passing information about table ordering @@ -6811,26 +6810,17 @@ static bool is_inplace_alter_impossible(TABLE *table, not supported for in-place in combination with other operations. Alone, it will be done by simple_rename_or_index_change(). */ - if (alter_info->flags & (Alter_info::ALTER_RECREATE | - Alter_info::ALTER_ORDER | + if (alter_info->flags & (Alter_info::ALTER_ORDER | Alter_info::ALTER_KEYS_ONOFF)) DBUG_RETURN(true); /* - Test also that engine was not given during ALTER TABLE, or - we are force to run regular alter table (copy). - E.g. ALTER TABLE tbl_name ENGINE=MyISAM. - Note that in addition to checking flag in HA_CREATE_INFO we - also check HA_CREATE_INFO::db_type value. This is done - to cover cases in which engine is changed implicitly - (e.g. when non-partitioned table becomes partitioned). - - Note that we do copy even if the table is already using the - given engine. Many users and tools depend on using ENGINE - to force a table rebuild. + If the table engine is changed explicitly (using ENGINE clause) + or implicitly (e.g. when non-partitioned table becomes + partitioned) a regular alter table (copy) needs to be + performed. */ - if (create_info->db_type != table->s->db_type() || - create_info->used_fields & HA_CREATE_USED_ENGINE) + if (create_info->db_type != table->s->db_type()) DBUG_RETURN(true); /* @@ -7301,6 +7291,9 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, if (!(used_fields & HA_CREATE_USED_TRANSACTIONAL)) create_info->transactional= table->s->transactional; + if (!(used_fields & HA_CREATE_USED_CONNECTION)) + create_info->connect_string= table->s->connect_string; + restore_record(table, s->default_values); // Empty record for DEFAULT if ((create_info->fields_option_struct= (ha_field_option_struct**) @@ -8568,6 +8561,15 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, } /* + ALTER TABLE ... ENGINE to the same engine is a common way to + request table rebuild. Set ALTER_RECREATE flag to force table + rebuild. + */ + if (create_info->db_type == table->s->db_type() && + create_info->used_fields & HA_CREATE_USED_ENGINE) + alter_info->flags|= Alter_info::ALTER_RECREATE; + + /* If the old table had partitions and we are doing ALTER TABLE ... engine= <new_engine>, the new table must preserve the original partitioning. This means that the new engine is still the @@ -9535,12 +9537,14 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, mysql_recreate_table() thd Thread handler tables Tables to recreate + table_copy Recreate the table by using + ALTER TABLE COPY algorithm RETURN Like mysql_alter_table(). */ -bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list) +bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list, bool table_copy) { HA_CREATE_INFO create_info; Alter_info alter_info; @@ -9558,6 +9562,10 @@ bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list) /* Force alter table to recreate table */ alter_info.flags= (Alter_info::ALTER_CHANGE_COLUMN | Alter_info::ALTER_RECREATE); + + if (table_copy) + alter_info.requested_algorithm= Alter_info::ALTER_TABLE_ALGORITHM_COPY; + DBUG_RETURN(mysql_alter_table(thd, NullS, NullS, &create_info, table_list, &alter_info, 0, (ORDER *) 0, 0)); diff --git a/sql/sql_table.h b/sql/sql_table.h index cd1c4293c39..444626e0363 100644 --- a/sql/sql_table.h +++ b/sql/sql_table.h @@ -1,5 +1,5 @@ -/* Copyright (c) 2006, 2013, Oracle and/or its affiliates. - Copyright (c) 2011, 2013, Monty Program Ab. +/* Copyright (c) 2006, 2014, Oracle and/or its affiliates. + Copyright (c) 2011, 2014, Monty Program Ab. 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 @@ -222,7 +222,7 @@ bool mysql_compare_tables(TABLE *table, Alter_info *alter_info, HA_CREATE_INFO *create_info, bool *metadata_equal); -bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list); +bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list, bool table_copy); bool mysql_create_like_table(THD *thd, TABLE_LIST *table, TABLE_LIST *src_table, HA_CREATE_INFO *create_info); diff --git a/sql/sql_time.cc b/sql/sql_time.cc index 9b68aba5b30..c8a2c2daf85 100644 --- a/sql/sql_time.cc +++ b/sql/sql_time.cc @@ -1118,6 +1118,57 @@ int my_time_compare(const MYSQL_TIME *a, const MYSQL_TIME *b) } +/** + Convert TIME to DATETIME. + @param ltime The value to convert. + @return false on success, true of error (negative time). +*/ +bool time_to_datetime(MYSQL_TIME *ltime) +{ + DBUG_ASSERT(ltime->time_type == MYSQL_TIMESTAMP_TIME); + DBUG_ASSERT(ltime->year == 0); + DBUG_ASSERT(ltime->month == 0); + DBUG_ASSERT(ltime->day == 0); + if (ltime->neg) + return true; + uint day= ltime->hour / 24; + ltime->hour%= 24; + ltime->month= day / 31; + ltime->day= day % 31; + return false; +} + + +/** + Return a valid DATE or DATETIME value from an arbitrary MYSQL_TIME. + If ltime is TIME, it's first converted to DATETIME. + If ts_type is DATE, hhmmss is set to zero. + The date part of the result is checked against fuzzy_date. + + @param ltime The value to convert. + @param fuzzy_date Flags to check date. + @param ts_type The type to convert to. + @return false on success, true of error (negative time).*/ +bool +make_date_with_warn(MYSQL_TIME *ltime, ulonglong fuzzy_date, + timestamp_type ts_type) +{ + DBUG_ASSERT(ts_type == MYSQL_TIMESTAMP_DATE || + ts_type == MYSQL_TIMESTAMP_DATETIME); + if (ltime->time_type == MYSQL_TIMESTAMP_TIME && time_to_datetime(ltime)) + { + /* e.g. negative time */ + ErrConvTime str(ltime); + make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, + &str, ts_type, 0); + return true; + } + if ((ltime->time_type= ts_type) == MYSQL_TIMESTAMP_DATE) + ltime->hour= ltime->minute= ltime->second= ltime->second_part= 0; + return check_date_with_warn(ltime, fuzzy_date, ts_type); +} + + /* Convert a TIME value to DAY-TIME interval, e.g. for extraction: EXTRACT(DAY FROM x), EXTRACT(HOUR FROM x), etc. diff --git a/sql/sql_time.h b/sql/sql_time.h index 7513ca7c00a..5a468ef0649 100644 --- a/sql/sql_time.h +++ b/sql/sql_time.h @@ -33,6 +33,7 @@ typedef struct st_known_date_time_format KNOWN_DATE_TIME_FORMAT; ulong convert_period_to_month(ulong period); ulong convert_month_to_period(ulong month); +bool time_to_datetime(MYSQL_TIME *ltime); void time_to_daytime_interval(MYSQL_TIME *l_time); bool get_date_from_daynr(long daynr,uint *year, uint *month, uint *day); my_time_t TIME_to_timestamp(THD *thd, const MYSQL_TIME *t, uint *error_code); @@ -154,6 +155,8 @@ check_date(const MYSQL_TIME *ltime, ulonglong flags, int *was_cut) } bool check_date_with_warn(const MYSQL_TIME *ltime, ulonglong fuzzy_date, timestamp_type ts_type); +bool make_date_with_warn(MYSQL_TIME *ltime, + ulonglong fuzzy_date, timestamp_type ts_type); bool adjust_time_range_with_warn(MYSQL_TIME *ltime, uint dec); #endif /* SQL_TIME_INCLUDED */ diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index d137c2ab7aa..c3188305092 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -464,6 +464,13 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) */ thd->lex->sql_command= backup.sql_command; + if (opt_readonly && !(thd->security_ctx->master_access & SUPER_ACL) && + !thd->slave_thread) + { + my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only"); + goto end; + } + if (add_table_for_trigger(thd, thd->lex->spname, if_exists, & tables)) goto end; diff --git a/sql/sql_update.cc b/sql/sql_update.cc index e9e8033c4fd..f3a1ec18aaf 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -1429,11 +1429,11 @@ int mysql_multi_update_prepare(THD *thd) be write-locked (for example, trigger to be invoked might try to update this table). */ - tl->lock_type= read_lock_type_for_table(thd, lex, tl); + if (using_lock_tables) + tl->lock_type= read_lock_type_for_table(thd, lex, tl); + else + tl->set_lock_type(thd, read_lock_type_for_table(thd, lex, tl)); tl->updating= 0; - /* Update TABLE::lock_type accordingly. */ - if (!tl->placeholder() && !using_lock_tables) - tl->table->reginfo.lock_type= tl->lock_type; } } @@ -1921,6 +1921,13 @@ loop_end: TABLE *tbl= table; do { + /* + Signal each table (including tables referenced by WITH CHECK OPTION + clause) for which we will store row position in the temporary table + that we need a position to be read first. + */ + tbl->prepare_for_position(); + Field_string *field= new Field_string(tbl->file->ref_length, 0, tbl->alias.c_ptr(), &my_charset_bin); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index ceb4e247848..9ea72676b13 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1,6 +1,6 @@ /* - Copyright (c) 2000, 2013, Oracle and/or its affiliates. - Copyright (c) 2010, 2011, Monty Program Ab. + Copyright (c) 2000, 2014, Oracle and/or its affiliates. + Copyright (c) 2010, 2014, Monty Program Ab. 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 @@ -442,6 +442,13 @@ set_system_variable(THD *thd, struct sys_var_with_base *tmp, if (lex->spcont && tmp->var == Sys_autocommit_ptr) lex->sphead->m_flags|= sp_head::HAS_SET_AUTOCOMMIT_STMT; + if (val && val->type() == Item::FIELD_ITEM && + ((Item_field*)val)->table_name) + { + my_error(ER_WRONG_TYPE_FOR_VAR, MYF(0), tmp->var->name.str); + return TRUE; + } + if (! (var= new set_var(var_type, tmp->var, &tmp->base_name, val))) return TRUE; @@ -810,7 +817,6 @@ static void sp_create_assignment_lex(THD *thd, bool no_lookahead) lex->sql_command= SQLCOM_SET_OPTION; mysql_init_select(lex); lex->var_list.empty(); - lex->one_shot_set= 0; lex->autocommit= 0; /* get_ptr() is only correct with no lookahead. */ DBUG_ASSERT(no_lookahead); @@ -11305,7 +11311,10 @@ opt_limit_clause: limit_clause: LIMIT limit_options { - Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT); + SELECT_LEX *sel= Select; + if (!sel->select_limit->basic_const_item() || + sel->select_limit->val_int() > 0) + Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT); } | LIMIT limit_options ROWS_SYM EXAMINED_SYM limit_rows_option { @@ -14421,7 +14430,6 @@ set: mysql_init_select(lex); lex->option_type=OPT_SESSION; lex->var_list.empty(); - lex->one_shot_set= 0; lex->autocommit= 0; sp_create_assignment_lex(thd, yychar == YYEMPTY); } diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 3d283a5f32b..d9b43f4d5b6 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -287,14 +287,15 @@ static Sys_var_long Sys_pfs_events_stages_history_size( - 1 for "statement/com/new_packet", for unknown enum_server_command - 1 for "statement/com/Error", for invalid enum_server_command - SQLCOM_END for all regular "statement/sql/...", - - 1 for "statement/sql/error", for invalid enum_sql_command. + - 1 for "statement/sql/error", for invalid enum_sql_command + - 1 for "statement/rpl/relay_log", for replicated statements. */ static Sys_var_ulong Sys_pfs_max_statement_classes( "performance_schema_max_statement_classes", "Maximum number of statement instruments.", PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_statement_class_sizing), CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 256), - DEFAULT((ulong) SQLCOM_END + (ulong) COM_END + 3), + DEFAULT((ulong) SQLCOM_END + (ulong) COM_END + 4), BLOCK_SIZE(1)); static Sys_var_long Sys_pfs_events_statements_history_long_size( @@ -4833,6 +4834,46 @@ static Sys_var_set Sys_log_slow_filter( log_slow_filter_names, DEFAULT(MAX_SET(array_elements(log_slow_filter_names)-1))); +static const char *default_regex_flags_names[]= +{ + "DOTALL", // (?s) . matches anything including NL + "DUPNAMES", // (?J) Allow duplicate names for subpatterns + "EXTENDED", // (?x) Ignore white space and # comments + "EXTRA", // (?X) extra features (e.g. error on unknown escape character) + "MULTILINE", // (?m) ^ and $ match newlines within data + "UNGREEDY", // (?U) Invert greediness of quantifiers + 0 +}; +static const int default_regex_flags_to_pcre[]= +{ + PCRE_DOTALL, + PCRE_DUPNAMES, + PCRE_EXTENDED, + PCRE_EXTRA, + PCRE_MULTILINE, + PCRE_UNGREEDY, + 0 +}; +int default_regex_flags_pcre(const THD *thd) +{ + ulonglong src= thd->variables.default_regex_flags; + int i, res; + for (i= res= 0; default_regex_flags_to_pcre[i]; i++) + { + if (src & (1 << i)) + res|= default_regex_flags_to_pcre[i]; + } + return res; +} +static Sys_var_set Sys_default_regex_flags( + "default_regex_flags", + "Default flags for the regex library. " + "Syntax: default-regex-flags='[flag[,flag[,flag...]]]'. " + "See the manual for the complete list of valid flags", + SESSION_VAR(default_regex_flags), CMD_LINE(REQUIRED_ARG), + default_regex_flags_names, + DEFAULT(0)); + static Sys_var_ulong Sys_log_slow_rate_limit( "log_slow_rate_limit", "Write to slow log every #th slow query. Set to 1 to log everything. " diff --git a/sql/table.cc b/sql/table.cc index f7fe67c8437..ab943e1cf62 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -3439,6 +3439,24 @@ uint calculate_key_len(TABLE *table, uint key, const uchar *buf, return length; } +#ifndef DBUG_OFF +/** + Verifies that database/table name is in lowercase, when it should be + + This is supposed to be used only inside DBUG_ASSERT() +*/ +bool ok_for_lower_case_names(const char *name) +{ + if (!lower_case_table_names || !name) + return true; + + char buf[SAFE_NAME_LEN]; + strmake_buf(buf, name); + my_casedn_str(files_charset_info, buf); + return strcmp(name, buf) == 0; +} +#endif + /* Check if database name is valid @@ -4000,13 +4018,15 @@ void TABLE::init(THD *thd, TABLE_LIST *tl) status= STATUS_NO_RECORD; insert_values= 0; fulltext_searched= 0; - file->ha_start_of_new_statement(); + file->ft_handler= 0; +#if 0 #ifdef WITH_WSREP if (file->ht->db_type == DB_TYPE_PARTITION_DB) { ((ha_partition*)file)->wsrep_reset_files(); } #endif +#endif reginfo.impossible_range= 0; created= TRUE; cond_selectivity= 1.0; @@ -7052,6 +7072,27 @@ bool TABLE_LIST::change_refs_to_fields() } +void TABLE_LIST::set_lock_type(THD *thd, enum thr_lock_type lock) +{ + if (check_stack_overrun(thd, STACK_MIN_SIZE, (uchar *)&lock)) + return; + /* we call it only when table is opened and it is "leaf" table*/ + DBUG_ASSERT(table); + lock_type= lock; + /* table->file->get_table() can be 0 for derived tables */ + if (table->file && table->file->get_table()) + table->file->set_lock_type(lock); + if (is_merged_derived()) + { + for (TABLE_LIST *table= get_single_select()->get_table_list(); + table; + table= table->next_local) + { + table->set_lock_type(thd, lock); + } + } +} + uint TABLE_SHARE::actual_n_key_parts(THD *thd) { return use_ext_keys && diff --git a/sql/table.h b/sql/table.h index cd1b934dcdd..86e03cdaaf5 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1114,7 +1114,11 @@ public: */ ha_rows quick_rows[MAX_KEY]; - /* Bitmaps of key parts that =const for the entire join. */ + /* + Bitmaps of key parts that =const for the duration of join execution. If + we're in a subquery, then the constant may be different across subquery + re-executions. + */ key_part_map const_key_parts[MAX_KEY]; uint quick_key_parts[MAX_KEY]; @@ -2241,6 +2245,7 @@ struct TABLE_LIST } return false; } + void set_lock_type(THD* thd, enum thr_lock_type lock); private: bool prep_check_option(THD *thd, uint8 check_opt_type); @@ -2507,6 +2512,8 @@ static inline void dbug_tmp_restore_column_maps(MY_BITMAP *read_set, #endif } +bool ok_for_lower_case_names(const char *names); + enum get_table_share_flags { GTS_TABLE = 1, GTS_VIEW = 2, diff --git a/sql/transaction.cc b/sql/transaction.cc index 8770f729b88..72a8c47cdd8 100644 --- a/sql/transaction.cc +++ b/sql/transaction.cc @@ -616,6 +616,32 @@ bool trans_rollback_to_savepoint(THD *thd, LEX_STRING name) DBUG_RETURN(TRUE); } + /** + Checking whether it is safe to release metadata locks acquired after + savepoint, if rollback to savepoint is successful. + + Whether it is safe to release MDL after rollback to savepoint depends + on storage engines participating in transaction: + + - InnoDB doesn't release any row-locks on rollback to savepoint so it + is probably a bad idea to release MDL as well. + - Binary log implementation in some cases (e.g when non-transactional + tables involved) may choose not to remove events added after savepoint + from transactional cache, but instead will write them to binary + log accompanied with ROLLBACK TO SAVEPOINT statement. Since the real + write happens at the end of transaction releasing MDL on tables + mentioned in these events (i.e. acquired after savepoint and before + rollback ot it) can break replication, as concurrent DROP TABLES + statements will be able to drop these tables before events will get + into binary log, + + For backward-compatibility reasons we always release MDL if binary + logging is off. + */ + bool mdl_can_safely_rollback_to_savepoint= + (!(mysql_bin_log.is_open() && thd->variables.sql_log_bin) || + ha_rollback_to_savepoint_can_release_mdl(thd)); + if (ha_rollback_to_savepoint(thd, sv)) res= TRUE; else if (((thd->variables.option_bits & OPTION_KEEP_LOG) || @@ -627,14 +653,7 @@ bool trans_rollback_to_savepoint(THD *thd, LEX_STRING name) thd->transaction.savepoints= sv; - /* - Release metadata locks that were acquired during this savepoint unit - unless binlogging is on. Releasing locks with binlogging on can break - replication as it allows other connections to drop these tables before - rollback to savepoint is written to the binlog. - */ - bool binlog_on= mysql_bin_log.is_open() && thd->variables.sql_log_bin; - if (!res && !binlog_on) + if (!res && mdl_can_safely_rollback_to_savepoint) thd->mdl_context.rollback_to_savepoint(sv->mdl_savepoint); DBUG_RETURN(MY_TEST(res)); diff --git a/sql/unireg.cc b/sql/unireg.cc index aeeba6f4f85..b7ac8b17c38 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -46,9 +46,14 @@ static bool pack_fields(uchar *, List<Create_field> &, ulong); static size_t packed_fields_length(List<Create_field> &); static bool make_empty_rec(THD *, uchar *, uint, List<Create_field> &, uint, ulong); +/* + write the length as + if ( 0 < length <= 255) one byte + if (256 < length <= 65535) zero byte, then two bytes, low-endian +*/ static uchar *extra2_write_len(uchar *pos, size_t len) { - if (len < 255) + if (len <= 255) *pos++= len; else { |