diff options
author | Jan Lindström <jan.lindstrom@mariadb.com> | 2018-10-31 08:46:37 +0200 |
---|---|---|
committer | Jan Lindström <jan.lindstrom@mariadb.com> | 2018-10-31 08:46:37 +0200 |
commit | d6ee7ab1a1cec0143e66f3ac75d8cee7c60f69b0 (patch) | |
tree | 3db0ed5372812c7eaa6754db689f1baa6418c572 /sql | |
parent | b0fe082b365d989fcf905e5c40c3fe60fd756858 (diff) | |
parent | a737135ae39dafe8b1136386ce23dfa8bed877f9 (diff) | |
download | mariadb-git-d6ee7ab1a1cec0143e66f3ac75d8cee7c60f69b0.tar.gz |
Merge remote-tracking branch 'origin/10.0' into bb-10.0-galera
Diffstat (limited to 'sql')
-rw-r--r-- | sql/contributors.h | 4 | ||||
-rw-r--r-- | sql/field.cc | 17 | ||||
-rw-r--r-- | sql/ha_partition.cc | 9 | ||||
-rw-r--r-- | sql/ha_partition.h | 1 | ||||
-rw-r--r-- | sql/item_cmpfunc.cc | 15 | ||||
-rw-r--r-- | sql/item_subselect.cc | 2 | ||||
-rw-r--r-- | sql/lex.h | 6 | ||||
-rw-r--r-- | sql/lock.cc | 17 | ||||
-rw-r--r-- | sql/lock.h | 1 | ||||
-rw-r--r-- | sql/mysqld.cc | 11 | ||||
-rw-r--r-- | sql/opt_range.cc | 27 | ||||
-rw-r--r-- | sql/sp_head.cc | 33 | ||||
-rw-r--r-- | sql/sql_acl.cc | 1 | ||||
-rw-r--r-- | sql/sql_alter.h | 2 | ||||
-rw-r--r-- | sql/sql_base.cc | 150 | ||||
-rw-r--r-- | sql/sql_base.h | 2 | ||||
-rw-r--r-- | sql/sql_class.cc | 3 | ||||
-rw-r--r-- | sql/sql_class.h | 2 | ||||
-rw-r--r-- | sql/sql_insert.cc | 2 | ||||
-rw-r--r-- | sql/sql_lex.h | 4 | ||||
-rw-r--r-- | sql/sql_select.cc | 2 | ||||
-rw-r--r-- | sql/sql_show.cc | 9 | ||||
-rw-r--r-- | sql/sql_statistics.cc | 3 | ||||
-rw-r--r-- | sql/sql_statistics.h | 29 | ||||
-rw-r--r-- | sql/sql_table.cc | 64 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 38 | ||||
-rw-r--r-- | sql/table.cc | 20 | ||||
-rw-r--r-- | sql/table.h | 49 | ||||
-rw-r--r-- | sql/table_cache.cc | 3 |
29 files changed, 328 insertions, 198 deletions
diff --git a/sql/contributors.h b/sql/contributors.h index a0d05af3fa6..69f8fa6bd4c 100644 --- a/sql/contributors.h +++ b/sql/contributors.h @@ -45,12 +45,14 @@ struct show_table_contributors_st show_table_contributors[]= { {"Visma", "https://visma.com", "Gold Sponsor of the MariaDB Foundation"}, {"DBS", "https://dbs.com", "Gold Sponsor of the MariaDB Foundation"}, {"IBM", "https://www.ibm.com", "Gold Sponsor of the MariaDB Foundation"}, + {"Tencent Games", "http://game.qq.com/", "Gold Sponsor of the MariaDB Foundation"}, {"Nexedi", "https://www.nexedi.com", "Silver Sponsor of the MariaDB Foundation"}, - {"Acronis", "http://www.acronis.com", "Silver Sponsor of the MariaDB Foundation"}, + {"Acronis", "https://www.acronis.com", "Silver Sponsor of the MariaDB Foundation"}, {"Verkkokauppa.com", "https://www.verkkokauppa.com", "Bronze Sponsor of the MariaDB Foundation"}, {"Virtuozzo", "https://virtuozzo.com", "Bronze Sponsor of the MariaDB Foundation"}, {"Tencent Game DBA", "http://tencentdba.com/about", "Bronze Sponsor of the MariaDB Foundation"}, {"Tencent TDSQL", "http://tdsql.org", "Bronze Sponsor of the MariaDB Foundation"}, + {"Percona", "https://www.percona.com/", "Bronze Sponsor of the MariaDB Foundation"}, /* Sponsors of important features */ {"Google", "USA", "Sponsoring encryption, parallel replication and GTID"}, diff --git a/sql/field.cc b/sql/field.cc index 2bc090e8942..2a3394be411 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -9066,13 +9066,18 @@ void Create_field::create_length_to_internal_length(void) } break; case MYSQL_TYPE_NEWDECIMAL: - key_length= pack_length= - my_decimal_get_binary_size(my_decimal_length_to_precision(length, - decimals, - flags & - UNSIGNED_FLAG), - decimals); + { + /* + This code must be identical to code in + Field_new_decimal::Field_new_decimal as otherwise the record layout + gets out of sync. + */ + uint precision= my_decimal_length_to_precision(length, decimals, + flags & UNSIGNED_FLAG); + set_if_smaller(precision, DECIMAL_MAX_PRECISION); + key_length= pack_length= my_decimal_get_binary_size(precision, decimals); break; + } default: key_length= pack_length= calc_pack_length(sql_type, length); break; diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 7bffb68c6de..9ceee47bcd0 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -3913,9 +3913,14 @@ THR_LOCK_DATA **ha_partition::store_lock(THD *thd, } else { - for (i= bitmap_get_first_set(&(m_part_info->lock_partitions)); + MY_BITMAP *used_partitions= lock_type == TL_UNLOCK || + lock_type == TL_IGNORE ? + &m_locked_partitions : + &m_part_info->lock_partitions; + + for (i= bitmap_get_first_set(used_partitions); i < m_tot_parts; - i= bitmap_get_next_set(&m_part_info->lock_partitions, i)) + i= bitmap_get_next_set(used_partitions, i)) { DBUG_PRINT("info", ("store lock %d iteration", i)); to= m_file[i]->store_lock(thd, to, lock_type); diff --git a/sql/ha_partition.h b/sql/ha_partition.h index 33a1a8ab43b..98c987b3589 100644 --- a/sql/ha_partition.h +++ b/sql/ha_partition.h @@ -251,7 +251,6 @@ private: /* Variables for lock structures. */ - THR_LOCK_DATA lock; /* MySQL lock */ bool auto_increment_lock; /**< lock reading/updating auto_inc */ /** diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 49bbee9edd2..bdef3c1a89f 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -4172,11 +4172,20 @@ void Item_func_in::fix_length_and_dec() if (field_item->field_type() == MYSQL_TYPE_LONGLONG || field_item->field_type() == MYSQL_TYPE_YEAR) { - bool all_converted= TRUE; + bool all_converted= true; for (arg=args+1, arg_end=args+arg_count; arg != arg_end ; arg++) { - if (!convert_const_to_int(thd, field_item, &arg[0])) - all_converted= FALSE; + /* + Explicit NULLs should not affect data cmp_type resolution: + - we ignore NULLs when calling collect_cmp_type() + - we ignore NULLs here + So this expression: + year_column IN (DATE'2001-01-01', NULL) + switches from TIME_RESULT to INT_RESULT. + */ + if (arg[0]->type() != Item::NULL_ITEM && + !convert_const_to_int(thd, field_item, &arg[0])) + all_converted= false; } if (all_converted) cmp_type= INT_RESULT; diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index a8dfdff9809..14b2ccd3985 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -575,7 +575,7 @@ bool Item_subselect::is_expensive() */ if (cur_join->optimized && (cur_join->zero_result_cause || !cur_join->tables_list)) - return false; + continue; /* If a subquery is not optimized we cannot estimate its cost. A subquery is diff --git a/sql/lex.h b/sql/lex.h index 591f0def43e..868f19ed9c4 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -630,7 +630,7 @@ static SYMBOL symbols[] = { { "UPGRADE", SYM(UPGRADE_SYM)}, { "USAGE", SYM(USAGE)}, { "USE", SYM(USE_SYM)}, - { "USER", SYM(USER)}, + { "USER", SYM(USER_SYM)}, { "USER_RESOURCES", SYM(RESOURCES)}, { "USER_STATISTICS", SYM(USER_STATS_SYM)}, { "USE_FRM", SYM(USE_FRM)}, @@ -688,7 +688,7 @@ static SYMBOL sql_functions[] = { { "MIN", SYM(MIN_SYM)}, { "NOW", SYM(NOW_SYM)}, { "POSITION", SYM(POSITION_SYM)}, - { "SESSION_USER", SYM(USER)}, + { "SESSION_USER", SYM(USER_SYM)}, { "STD", SYM(STD_SYM)}, { "STDDEV", SYM(STD_SYM)}, { "STDDEV_POP", SYM(STD_SYM)}, @@ -698,7 +698,7 @@ static SYMBOL sql_functions[] = { { "SUBSTRING", SYM(SUBSTRING)}, { "SUM", SYM(SUM_SYM)}, { "SYSDATE", SYM(SYSDATE)}, - { "SYSTEM_USER", SYM(USER)}, + { "SYSTEM_USER", SYM(USER_SYM)}, { "TRIM", SYM(TRIM)}, { "VARIANCE", SYM(VARIANCE_SYM)}, { "VAR_POP", SYM(VARIANCE_SYM)}, diff --git a/sql/lock.cc b/sql/lock.cc index 965f7dcab99..28aa39d96ae 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -557,23 +557,6 @@ void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table) } -/** Abort all other threads waiting to get lock in table. */ - -void mysql_lock_abort(THD *thd, TABLE *table, bool upgrade_lock) -{ - MYSQL_LOCK *locked; - DBUG_ENTER("mysql_lock_abort"); - - if ((locked= get_lock_data(thd, &table, 1, GET_LOCK_UNLOCK))) - { - for (uint i=0; i < locked->lock_count; i++) - thr_abort_locks(locked->locks[i]->lock, upgrade_lock); - my_free(locked); - } - DBUG_VOID_RETURN; -} - - /** Abort one thread / table combination. diff --git a/sql/lock.h b/sql/lock.h index a4833cdc38e..a9183bf8580 100644 --- a/sql/lock.h +++ b/sql/lock.h @@ -32,7 +32,6 @@ void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock, bool free_lock= 1); void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock); void mysql_unlock_some_tables(THD *thd, TABLE **table,uint count); void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table); -void mysql_lock_abort(THD *thd, TABLE *table, bool upgrade_lock); bool mysql_lock_abort_for_thread(THD *thd, TABLE *table); MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b); /* Lock based on name */ diff --git a/sql/mysqld.cc b/sql/mysqld.cc index ab58928273a..6194f539674 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -1774,7 +1774,14 @@ static void close_connections(void) tmp->thread_id, (tmp->main_security_ctx.user ? tmp->main_security_ctx.user : "")); + /* + close_connection() might need a valid current_thd + for memory allocation tracking. + */ + THD* save_thd= current_thd; + set_current_thd(tmp); close_connection(tmp,ER_SERVER_SHUTDOWN); + set_current_thd(save_thd); } #endif #ifdef WITH_WSREP @@ -4246,6 +4253,10 @@ static int init_common_variables() /* MyISAM requires two file handles per table. */ wanted_files= (10 + max_connections + extra_max_connections + tc_size * 2); +#if defined(HAVE_POOL_OF_THREADS) && !defined(__WIN__) + // add epoll or kevent fd for each threadpool group, in case pool of threads is used + wanted_files+= (thread_handling > SCHEDULER_NO_THREADS) ? 0 : threadpool_size; +#endif /* We are trying to allocate no less than max_connections*5 file handles (i.e. we are trying to set the limit so that they will diff --git a/sql/opt_range.cc b/sql/opt_range.cc index f1d84e5c623..0fd2cd267fc 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -3326,13 +3326,19 @@ bool create_key_parts_for_pseudo_indexes(RANGE_OPT_PARAM *param, for (field_ptr= table->field; *field_ptr; field_ptr++) { - if (bitmap_is_set(used_fields, (*field_ptr)->field_index)) + Column_statistics* col_stats= (*field_ptr)->read_stats; + if (bitmap_is_set(used_fields, (*field_ptr)->field_index) + && col_stats && !col_stats->no_stat_values_provided() + && !((*field_ptr)->type() == MYSQL_TYPE_GEOMETRY)) parts++; } KEY_PART *key_part; uint keys= 0; + if (!parts) + return TRUE; + if (!(key_part= (KEY_PART *) alloc_root(param->mem_root, sizeof(KEY_PART) * parts))) return TRUE; @@ -3344,6 +3350,9 @@ bool create_key_parts_for_pseudo_indexes(RANGE_OPT_PARAM *param, if (bitmap_is_set(used_fields, (*field_ptr)->field_index)) { Field *field= *field_ptr; + if (field->type() == MYSQL_TYPE_GEOMETRY) + continue; + uint16 store_length; uint16 max_key_part_length= (uint16) table->file->max_key_part_length(); key_part->key= keys; @@ -3501,7 +3510,18 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item *cond) table->cond_selectivity= 1.0; - if (!cond || table_records == 0) + if (table_records == 0) + DBUG_RETURN(FALSE); + + QUICK_SELECT_I *quick; + if ((quick=table->reginfo.join_tab->quick) && + quick->get_type() == QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX) + { + table->cond_selectivity*= (quick->records/table_records); + DBUG_RETURN(FALSE); + } + + if (!cond) DBUG_RETURN(FALSE); if (table->pos_in_table_list->schema_table) @@ -3617,7 +3637,8 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item *cond) */ if (thd->variables.optimizer_use_condition_selectivity > 2 && - !bitmap_is_clear_all(used_fields)) + !bitmap_is_clear_all(used_fields) && + thd->variables.use_stat_tables > 0) { PARAM param; MEM_ROOT alloc; diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 164ceb677ee..701036d10d0 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -4219,7 +4219,7 @@ sp_head::add_used_tables_to_table_list(THD *thd, if (stab->temp) continue; - if (!(tab_buff= (char *)thd->calloc(ALIGN_SIZE(sizeof(TABLE_LIST)) * + if (!(tab_buff= (char *)thd->alloc(ALIGN_SIZE(sizeof(TABLE_LIST)) * stab->lock_count)) || !(key_buff= (char*)thd->memdup(stab->qname.str, stab->qname.length))) @@ -4228,32 +4228,11 @@ sp_head::add_used_tables_to_table_list(THD *thd, for (uint j= 0; j < stab->lock_count; j++) { table= (TABLE_LIST *)tab_buff; - - table->db= key_buff; - table->db_length= stab->db_length; - table->table_name= table->db + table->db_length + 1; - table->table_name_length= stab->table_name_length; - table->alias= table->table_name + table->table_name_length + 1; - table->lock_type= stab->lock_type; - table->cacheable_table= 1; - table->prelocking_placeholder= 1; - table->belong_to_view= belong_to_view; - table->trg_event_map= stab->trg_event_map; - /* - Since we don't allow DDL on base tables in prelocked mode it - is safe to infer the type of metadata lock from the type of - table lock. - */ - table->mdl_request.init(MDL_key::TABLE, table->db, table->table_name, - table->lock_type >= TL_WRITE_ALLOW_WRITE ? - MDL_SHARED_WRITE : MDL_SHARED_READ, - MDL_TRANSACTION); - - /* Everyting else should be zeroed */ - - **query_tables_last_ptr= table; - table->prev_global= *query_tables_last_ptr; - *query_tables_last_ptr= &table->next_global; + table->init_one_table_for_prelocking(key_buff, stab->db_length, + key_buff + stab->db_length + 1, stab->table_name_length, + key_buff + stab->db_length + stab->table_name_length + 2, + stab->lock_type, true, belong_to_view, stab->trg_event_map, + query_tables_last_ptr); tab_buff+= ALIGN_SIZE(sizeof(TABLE_LIST)); result= TRUE; diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index ecce9ad2b39..a47f94cfc7f 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -9990,7 +9990,6 @@ bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name, combo->user.str= (char *) sctx->priv_user; mysql_mutex_lock(&acl_cache->lock); - if ((au= find_user_exact(combo->host.str= (char *) sctx->priv_host, combo->user.str))) goto found_acl; diff --git a/sql/sql_alter.h b/sql/sql_alter.h index 7114694124b..a4505f1d6c1 100644 --- a/sql/sql_alter.h +++ b/sql/sql_alter.h @@ -163,7 +163,7 @@ public: // Columns and keys to be dropped. List<Alter_drop> drop_list; - // Columns for ALTER_COLUMN_CHANGE_DEFAULT. + // Columns for ALTER_CHANGE_COLUMN_DEFAULT. List<Alter_column> alter_list; // List of keys, used by both CREATE and ALTER TABLE. List<Key> key_list; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 36c5b06c378..b260034bbac 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -810,6 +810,7 @@ close_all_tables_for_name(THD *thd, TABLE_SHARE *share, uint key_length= share->table_cache_key.length; const char *db= key; const char *table_name= db + share->db.length + 1; + bool remove_from_locked_tables= extra != HA_EXTRA_NOT_USED; memcpy(key, share->table_cache_key.str, key_length); @@ -823,7 +824,7 @@ close_all_tables_for_name(THD *thd, TABLE_SHARE *share, { thd->locked_tables_list.unlink_from_list(thd, table->pos_in_locked_tables, - extra != HA_EXTRA_NOT_USED); + remove_from_locked_tables); /* Inform handler that there is a drop table or a rename going on */ if (extra != HA_EXTRA_NOT_USED && table->db_stat) { @@ -4835,6 +4836,25 @@ handle_routine(THD *thd, Query_tables_list *prelocking_ctx, } +/* + @note this can be changed to use a hash, instead of scanning the linked + list, if the performance of this function will ever become an issue +*/ +bool table_already_fk_prelocked(TABLE_LIST *tl, LEX_STRING *db, + LEX_STRING *table, thr_lock_type lock_type) +{ + for (; tl; tl= tl->next_global ) + { + if (tl->lock_type >= lock_type && + tl->prelocking_placeholder == TABLE_LIST::FK && + strcmp(tl->db, db->str) == 0 && + strcmp(tl->table_name, table->str) == 0) + return true; + } + return false; +} + + /** Defines how prelocking algorithm for DML statements should handle table list elements: @@ -4874,6 +4894,52 @@ handle_table(THD *thd, Query_tables_list *prelocking_ctx, add_tables_and_routines_for_triggers(thd, prelocking_ctx, table_list)) return TRUE; } + + if (table_list->table->file->referenced_by_foreign_key()) + { + List <FOREIGN_KEY_INFO> fk_list; + List_iterator<FOREIGN_KEY_INFO> fk_list_it(fk_list); + FOREIGN_KEY_INFO *fk; + Query_arena *arena, backup; + + arena= thd->activate_stmt_arena_if_needed(&backup); + + table_list->table->file->get_parent_foreign_key_list(thd, &fk_list); + if (thd->is_error()) + { + if (arena) + thd->restore_active_arena(arena, &backup); + return TRUE; + } + + *need_prelocking= TRUE; + + while ((fk= fk_list_it++)) + { + // FK_OPTION_RESTRICT and FK_OPTION_NO_ACTION only need read access + uint8 op= table_list->trg_event_map; + thr_lock_type lock_type; + + if ((op & (1 << TRG_EVENT_DELETE) && fk_modifies_child(fk->delete_method)) + || (op & (1 << TRG_EVENT_UPDATE) && fk_modifies_child(fk->update_method))) + lock_type= TL_WRITE_ALLOW_WRITE; + else + lock_type= TL_READ; + + if (table_already_fk_prelocked(prelocking_ctx->query_tables, + fk->foreign_db, fk->foreign_table, + lock_type)) + continue; + + TABLE_LIST *tl= (TABLE_LIST *) thd->alloc(sizeof(TABLE_LIST)); + tl->init_one_table_for_prelocking(fk->foreign_db->str, fk->foreign_db->length, + fk->foreign_table->str, fk->foreign_table->length, + NULL, lock_type, false, table_list->belong_to_view, + op, &prelocking_ctx->query_tables_last); + } + if (arena) + thd->restore_active_arena(arena, &backup); + } } return FALSE; @@ -9078,88 +9144,6 @@ my_bool mysql_rm_tmp_tables(void) unireg support functions *****************************************************************************/ -/** - A callback to the server internals that is used to address - special cases of the locking protocol. - Invoked when acquiring an exclusive lock, for each thread that - has a conflicting shared metadata lock. - - This function: - - aborts waiting of the thread on a data lock, to make it notice - the pending exclusive lock and back off. - - if the thread is an INSERT DELAYED thread, sends it a KILL - signal to terminate it. - - @note This function does not wait for the thread to give away its - locks. Waiting is done outside for all threads at once. - - @param thd Current thread context - @param in_use The thread to wake up - @param needs_thr_lock_abort Indicates that to wake up thread - this call needs to abort its waiting - on table-level lock. - - @retval TRUE if the thread was woken up - @retval FALSE otherwise. - - @note It is one of two places where border between MDL and the - rest of the server is broken. -*/ - -bool mysql_notify_thread_having_shared_lock(THD *thd, THD *in_use, - bool needs_thr_lock_abort) -{ - bool signalled= FALSE; - if ((in_use->system_thread & SYSTEM_THREAD_DELAYED_INSERT) && - !in_use->killed) - { - in_use->killed= KILL_SYSTEM_THREAD; - mysql_mutex_lock(&in_use->mysys_var->mutex); - if (in_use->mysys_var->current_cond) - { - mysql_mutex_lock(in_use->mysys_var->current_mutex); - mysql_cond_broadcast(in_use->mysys_var->current_cond); - mysql_mutex_unlock(in_use->mysys_var->current_mutex); - } - mysql_mutex_unlock(&in_use->mysys_var->mutex); - signalled= TRUE; - } - - if (needs_thr_lock_abort) - { - mysql_mutex_lock(&in_use->LOCK_thd_data); - for (TABLE *thd_table= in_use->open_tables; - thd_table ; - thd_table= thd_table->next) - { - /* - Check for TABLE::needs_reopen() is needed since in some places we call - handler::close() for table instance (and set TABLE::db_stat to 0) - and do not remove such instances from the THD::open_tables - for some time, during which other thread can see those instances - (e.g. see partitioning code). - */ - if (!thd_table->needs_reopen()) -#ifdef WITH_WSREP - { - signalled|= mysql_lock_abort_for_thread(thd, thd_table); - if (thd && WSREP(thd) && wsrep_thd_is_BF((void *)thd, true)) - { - WSREP_DEBUG("remove_table_from_cache: %llu", - (unsigned long long) thd->real_id); - wsrep_abort_thd((void *)thd, (void *)in_use, FALSE); - } - } -#else - signalled|= mysql_lock_abort_for_thread(thd, thd_table); -#endif - } - mysql_mutex_unlock(&in_use->LOCK_thd_data); - } - return signalled; -} - - int setup_ftfuncs(SELECT_LEX *select_lex) { List_iterator<Item_func_match> li(*(select_lex->ftfunc_list)), diff --git a/sql/sql_base.h b/sql/sql_base.h index af42a15ab29..43b78833efb 100644 --- a/sql/sql_base.h +++ b/sql/sql_base.h @@ -148,6 +148,8 @@ my_bool mysql_rm_tmp_tables(void); bool rm_temporary_table(handlerton *base, const char *path); void close_tables_for_reopen(THD *thd, TABLE_LIST **tables, const MDL_savepoint &start_of_statement_svp); +bool table_already_fk_prelocked(TABLE_LIST *tl, LEX_STRING *db, + LEX_STRING *table, thr_lock_type lock_type); TABLE_LIST *find_table_in_list(TABLE_LIST *table, TABLE_LIST *TABLE_LIST::*link, const char *db_name, diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 385ced114c8..34a696b3cc6 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -5377,7 +5377,8 @@ has_write_table_with_auto_increment_and_select(TABLE_LIST *tables) for(TABLE_LIST *table= tables; table; table= table->next_global) { if (!table->placeholder() && - (table->lock_type <= TL_READ_NO_INSERT)) + table->lock_type <= TL_READ_NO_INSERT && + table->prelocking_placeholder != TABLE_LIST::FK) { has_select= true; break; diff --git a/sql/sql_class.h b/sql/sql_class.h index b6fde5f6875..3631112d7e7 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -338,8 +338,6 @@ class Foreign_key: public Key { public: enum fk_match_opt { FK_MATCH_UNDEF, FK_MATCH_FULL, FK_MATCH_PARTIAL, FK_MATCH_SIMPLE}; - enum fk_option { FK_OPTION_UNDEF, FK_OPTION_RESTRICT, FK_OPTION_CASCADE, - FK_OPTION_SET_NULL, FK_OPTION_NO_ACTION, FK_OPTION_DEFAULT}; LEX_STRING ref_db; LEX_STRING ref_table; diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 18eb42734e3..f202a76306b 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -2357,7 +2357,7 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd) The thread could be killed with an error message if di->handle_inserts() or di->open_and_lock_table() fails. The thread could be killed without an error message if - killed using mysql_notify_thread_having_shared_lock() or + killed using THD::notify_shared_lock() or kill_delayed_threads_for_table(). */ if (!thd.is_error()) diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 3fed9808f57..d58be8b336a 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -2482,8 +2482,8 @@ struct LEX: public Query_tables_list uint uint_geom_type; uint grant, grant_tot_col, which_columns; enum Foreign_key::fk_match_opt fk_match_option; - enum Foreign_key::fk_option fk_update_opt; - enum Foreign_key::fk_option fk_delete_opt; + enum_fk_option fk_update_opt; + enum_fk_option fk_delete_opt; uint slave_thd_opt, start_transaction_opt; int nest_level; /* diff --git a/sql/sql_select.cc b/sql/sql_select.cc index f7f34e3048f..ca9a6a46fda 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -24585,7 +24585,7 @@ void TABLE_LIST::print(THD *thd, table_map eliminated_tables, String *str, const char *t_alias= alias; str->append(' '); - if (lower_case_table_names== 1) + if (lower_case_table_names == 1) { if (alias && alias[0]) { diff --git a/sql/sql_show.cc b/sql/sql_show.cc index badeda50597..c8985ef76b5 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -7586,6 +7586,7 @@ get_referential_constraints_record(THD *thd, TABLE_LIST *tables, LEX_STRING *db_name, LEX_STRING *table_name) { CHARSET_INFO *cs= system_charset_info; + LEX_CSTRING *s; DBUG_ENTER("get_referential_constraints_record"); if (res) @@ -7630,10 +7631,10 @@ get_referential_constraints_record(THD *thd, TABLE_LIST *tables, else table->field[5]->set_null(); table->field[6]->store(STRING_WITH_LEN("NONE"), cs); - table->field[7]->store(f_key_info->update_method->str, - f_key_info->update_method->length, cs); - table->field[8]->store(f_key_info->delete_method->str, - f_key_info->delete_method->length, cs); + s= fk_option_name(f_key_info->update_method); + table->field[7]->store(s->str, s->length, cs); + s= fk_option_name(f_key_info->delete_method); + table->field[8]->store(s->str, s->length, cs); if (schema_table_store_record(thd, table)) DBUG_RETURN(1); } diff --git a/sql/sql_statistics.cc b/sql/sql_statistics.cc index 537ede91710..cb75a5c2176 100644 --- a/sql/sql_statistics.cc +++ b/sql/sql_statistics.cc @@ -3129,6 +3129,9 @@ int read_statistics_for_tables_if_needed(THD *thd, TABLE_LIST *tables) if (!tl->is_view_or_derived() && !is_temporary_table(tl) && tl->table) { TABLE_SHARE *table_share= tl->table->s; + if (table_share && !(table_share->table_category == TABLE_CATEGORY_USER)) + continue; + if (table_share && table_share->stats_cb.stats_can_be_read && !table_share->stats_cb.stats_is_read) diff --git a/sql/sql_statistics.h b/sql/sql_statistics.h index 6a43e42ab96..f28d56e4a69 100644 --- a/sql/sql_statistics.h +++ b/sql/sql_statistics.h @@ -342,12 +342,17 @@ private: public: Histogram histogram; + + uint32 no_values_provided_bitmap() + { + return + ((1 << (COLUMN_STAT_HISTOGRAM-COLUMN_STAT_COLUMN_NAME))-1) << + (COLUMN_STAT_COLUMN_NAME+1); + } void set_all_nulls() { - column_stat_nulls= - ((1 << (COLUMN_STAT_HISTOGRAM-COLUMN_STAT_COLUMN_NAME))-1) << - (COLUMN_STAT_COLUMN_NAME+1); + column_stat_nulls= no_values_provided_bitmap(); } void set_not_null(uint stat_field_no) @@ -393,8 +398,22 @@ public: bool min_max_values_are_provided() { return !is_null(COLUMN_STAT_MIN_VALUE) && - !is_null(COLUMN_STAT_MIN_VALUE); - } + !is_null(COLUMN_STAT_MAX_VALUE); + } + /* + This function checks whether the values for the fields of the statistical + tables that were NULL by DEFAULT for a column have changed or not. + + @retval + TRUE: Statistics are not present for a column + FALSE: Statisitics are present for a column + */ + bool no_stat_values_provided() + { + if (column_stat_nulls == no_values_provided_bitmap()) + return true; + return false; + } }; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index d9e00602dfc..dc4c20f6698 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -3340,6 +3340,10 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, } } + /* Virtual fields are always NULL */ + if (sql_field->vcol_info) + sql_field->flags&= ~NOT_NULL_FLAG; + if (sql_field->sql_type == MYSQL_TYPE_SET || sql_field->sql_type == MYSQL_TYPE_ENUM) { @@ -5095,7 +5099,7 @@ err: /* Write log if no error or if we already deleted a table */ if (!result || thd->log_current_statement) { - if (result && create_info->table_was_deleted) + if (result && create_info->table_was_deleted && pos_in_locked_tables) { /* Possible locked table was dropped. We should remove meta data locks @@ -9150,8 +9154,10 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, alter_ctx.tmp_name, strlen(alter_ctx.tmp_name), alter_ctx.tmp_name, TL_READ_NO_INSERT); /* Table is in thd->temporary_tables */ - (void) open_temporary_table(thd, &tbl); + if (open_temporary_table(thd, &tbl)) + goto err_new_table_cleanup; new_table= tbl.table; + DBUG_ASSERT(new_table); } else { @@ -9160,9 +9166,59 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, new_table= open_table_uncached(thd, new_db_type, alter_ctx.get_tmp_path(), alter_ctx.new_db, alter_ctx.tmp_name, true, true); + if (!new_table) + goto err_new_table_cleanup; + + /* + Normally, an attempt to modify an FK parent table will cause + FK children to be prelocked, so the table-being-altered cannot + be modified by a cascade FK action, because ALTER holds a lock + and prelocking will wait. + + But if a new FK is being added by this very ALTER, then the target + table is not locked yet (it's a temporary table). So, we have to + lock FK parents explicitly. + */ + if (alter_info->flags & Alter_info::ADD_FOREIGN_KEY) + { + List <FOREIGN_KEY_INFO> fk_list; + List_iterator<FOREIGN_KEY_INFO> fk_list_it(fk_list); + FOREIGN_KEY_INFO *fk; + + /* tables_opened can be > 1 only for MERGE tables */ + DBUG_ASSERT(tables_opened == 1); + DBUG_ASSERT(&table_list->next_global == thd->lex->query_tables_last); + + new_table->file->get_foreign_key_list(thd, &fk_list); + while ((fk= fk_list_it++)) + { + if (lower_case_table_names) + { + char buf[NAME_LEN]; + uint len; + strmake_buf(buf, fk->referenced_db->str); + len = my_casedn_str(files_charset_info, buf); + thd->make_lex_string(fk->referenced_db, buf, len); + strmake_buf(buf, fk->referenced_table->str); + len = my_casedn_str(files_charset_info, buf); + thd->make_lex_string(fk->referenced_table, buf, len); + } + if (table_already_fk_prelocked(table_list, fk->referenced_db, + fk->referenced_table, TL_READ_NO_INSERT)) + continue; + + TABLE_LIST *tl= (TABLE_LIST *) thd->alloc(sizeof(TABLE_LIST)); + tl->init_one_table_for_prelocking(fk->referenced_db->str, fk->referenced_db->length, + fk->referenced_table->str, fk->referenced_table->length, + NULL, TL_READ_NO_INSERT, false, NULL, 0, + &thd->lex->query_tables_last); + } + + if (open_tables(thd, &table_list->next_global, &tables_opened, 0, + &alter_prelocking_strategy)) + goto err_new_table_cleanup; + } } - if (!new_table) - goto err_new_table_cleanup; /* Note: In case of MERGE table, we do not attach children. We do not copy data for MERGE tables. Only the children have data. diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 0e222bf3504..919e061fc85 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -939,7 +939,7 @@ static bool sp_create_assignment_instr(THD *thd, bool no_lookahead) struct p_elem_val *p_elem_value; enum index_hint_type index_hint; enum enum_filetype filetype; - enum Foreign_key::fk_option m_fk_option; + enum enum_fk_option m_fk_option; enum enum_yes_no_unknown m_yes_no_unk; Diag_condition_item_name diag_condition_item_name; Diagnostics_information::Which_area diag_area; @@ -1584,7 +1584,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token UPDATE_SYM /* SQL-2003-R */ %token UPGRADE_SYM %token USAGE /* SQL-2003-N */ -%token USER /* SQL-2003-R */ +%token USER_SYM /* SQL-2003-R */ %token USER_STATS_SYM %token USE_FRM %token USE_SYM @@ -2455,7 +2455,7 @@ create: MYSQL_YYABORT; } } - | CREATE USER clear_privileges grant_list + | CREATE USER_SYM clear_privileges grant_list { Lex->sql_command = SQLCOM_CREATE_USER; } @@ -2496,7 +2496,7 @@ server_options_list: ; server_option: - USER TEXT_STRING_sys + USER_SYM TEXT_STRING_sys { Lex->server_options.username= $2.str; } @@ -6752,19 +6752,19 @@ opt_on_update_delete: /* empty */ { LEX *lex= Lex; - lex->fk_update_opt= Foreign_key::FK_OPTION_UNDEF; - lex->fk_delete_opt= Foreign_key::FK_OPTION_UNDEF; + lex->fk_update_opt= FK_OPTION_UNDEF; + lex->fk_delete_opt= FK_OPTION_UNDEF; } | ON UPDATE_SYM delete_option { LEX *lex= Lex; lex->fk_update_opt= $3; - lex->fk_delete_opt= Foreign_key::FK_OPTION_UNDEF; + lex->fk_delete_opt= FK_OPTION_UNDEF; } | ON DELETE_SYM delete_option { LEX *lex= Lex; - lex->fk_update_opt= Foreign_key::FK_OPTION_UNDEF; + lex->fk_update_opt= FK_OPTION_UNDEF; lex->fk_delete_opt= $3; } | ON UPDATE_SYM delete_option @@ -6784,11 +6784,11 @@ opt_on_update_delete: ; delete_option: - RESTRICT { $$= Foreign_key::FK_OPTION_RESTRICT; } - | CASCADE { $$= Foreign_key::FK_OPTION_CASCADE; } - | SET NULL_SYM { $$= Foreign_key::FK_OPTION_SET_NULL; } - | NO_SYM ACTION { $$= Foreign_key::FK_OPTION_NO_ACTION; } - | SET DEFAULT { $$= Foreign_key::FK_OPTION_DEFAULT; } + RESTRICT { $$= FK_OPTION_RESTRICT; } + | CASCADE { $$= FK_OPTION_CASCADE; } + | SET NULL_SYM { $$= FK_OPTION_SET_NULL; } + | NO_SYM ACTION { $$= FK_OPTION_NO_ACTION; } + | SET DEFAULT { $$= FK_OPTION_SET_DEFAULT; } ; normal_key_type: @@ -8157,7 +8157,7 @@ rename: } table_to_table_list {} - | RENAME USER clear_privileges rename_list + | RENAME USER_SYM clear_privileges rename_list { Lex->sql_command = SQLCOM_RENAME_USER; } @@ -9476,7 +9476,7 @@ function_call_keyword: if ($$ == NULL) MYSQL_YYABORT; } - | USER '(' ')' + | USER_SYM '(' ')' { $$= new (thd->mem_root) Item_func_user(); if ($$ == NULL) @@ -11766,7 +11766,7 @@ drop: lex->check_exists= $3; lex->spname= $4; } - | DROP USER clear_privileges user_list + | DROP USER_SYM clear_privileges user_list { Lex->sql_command = SQLCOM_DROP_USER; } @@ -13024,7 +13024,7 @@ kill_expr: { Lex->value_list.push_front($$); } - | USER user + | USER_SYM user { Lex->users_list.push_back($2); Lex->kill_type= KILL_TYPE_USER; @@ -14372,7 +14372,7 @@ keyword_sp: | UNDOFILE_SYM {} | UNKNOWN_SYM {} | UNTIL_SYM {} - | USER {} + | USER_SYM {} | USER_STATS_SYM {} | USE_FRM {} | VARIABLES {} @@ -15263,7 +15263,7 @@ object_privilege: | SHOW VIEW_SYM { Lex->grant |= SHOW_VIEW_ACL; } | CREATE ROUTINE_SYM { Lex->grant |= CREATE_PROC_ACL; } | ALTER ROUTINE_SYM { Lex->grant |= ALTER_PROC_ACL; } - | CREATE USER { Lex->grant |= CREATE_USER_ACL; } + | CREATE USER_SYM { Lex->grant |= CREATE_USER_ACL; } | EVENT_SYM { Lex->grant |= EVENT_ACL;} | TRIGGER_SYM { Lex->grant |= TRIGGER_ACL; } | CREATE TABLESPACE { Lex->grant |= CREATE_TABLESPACE_ACL; } diff --git a/sql/table.cc b/sql/table.cc index 757e1fa444e..15c7617eccc 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -413,6 +413,7 @@ void TABLE_SHARE::destroy() ha_share= NULL; // Safety } + delete_stat_values_for_table_share(this); free_root(&stats_cb.mem_root, MYF(0)); stats_cb.stats_can_be_read= FALSE; stats_cb.stats_is_read= FALSE; @@ -7254,3 +7255,22 @@ double KEY::actual_rec_per_key(uint i) read_stats->get_avg_frequency(i) : (double) rec_per_key[i]); } +LEX_CSTRING *fk_option_name(enum_fk_option opt) +{ + static LEX_CSTRING names[]= + { + { STRING_WITH_LEN("???") }, + { STRING_WITH_LEN("RESTRICT") }, + { STRING_WITH_LEN("CASCADE") }, + { STRING_WITH_LEN("SET NULL") }, + { STRING_WITH_LEN("NO ACTION") }, + { STRING_WITH_LEN("SET DEFAULT") } + }; + return names + opt; +} + +bool fk_modifies_child(enum_fk_option opt) +{ + static bool can_write[]= { false, false, true, true, false, true }; + return can_write[opt]; +} diff --git a/sql/table.h b/sql/table.h index 05596b605a5..4a1552f8c0d 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1429,6 +1429,9 @@ enum enum_schema_table_state PROCESSED_BY_JOIN_EXEC }; +enum enum_fk_option { FK_OPTION_UNDEF, FK_OPTION_RESTRICT, FK_OPTION_CASCADE, + FK_OPTION_SET_NULL, FK_OPTION_NO_ACTION, FK_OPTION_SET_DEFAULT}; + typedef struct st_foreign_key_info { LEX_STRING *foreign_id; @@ -1436,13 +1439,16 @@ typedef struct st_foreign_key_info LEX_STRING *foreign_table; LEX_STRING *referenced_db; LEX_STRING *referenced_table; - LEX_STRING *update_method; - LEX_STRING *delete_method; + enum_fk_option update_method; + enum_fk_option delete_method; LEX_STRING *referenced_key_name; List<LEX_STRING> foreign_fields; List<LEX_STRING> referenced_fields; } FOREIGN_KEY_INFO; +LEX_CSTRING *fk_option_name(enum_fk_option opt); +bool fk_modifies_child(enum_fk_option opt); + #define MY_I_S_MAYBE_NULL 1 #define MY_I_S_UNSIGNED 2 @@ -1689,6 +1695,14 @@ struct TABLE_LIST const char *alias_arg, enum thr_lock_type lock_type_arg) { + enum enum_mdl_type mdl_type; + if (lock_type_arg >= TL_WRITE_ALLOW_WRITE) + mdl_type= MDL_SHARED_WRITE; + else if (lock_type_arg == TL_READ_NO_INSERT) + mdl_type= MDL_SHARED_NO_WRITE; + else + mdl_type= MDL_SHARED_READ; + bzero((char*) this, sizeof(*this)); db= (char*) db_name_arg; db_length= db_length_arg; @@ -1696,10 +1710,31 @@ struct TABLE_LIST table_name_length= table_name_length_arg; alias= (char*) (alias_arg ? alias_arg : table_name_arg); lock_type= lock_type_arg; - mdl_request.init(MDL_key::TABLE, db, table_name, - (lock_type >= TL_WRITE_ALLOW_WRITE) ? - MDL_SHARED_WRITE : MDL_SHARED_READ, - MDL_TRANSACTION); + mdl_request.init(MDL_key::TABLE, db, table_name, mdl_type, MDL_TRANSACTION); + } + + inline void init_one_table_for_prelocking(const char *db_name_arg, + size_t db_length_arg, + const char *table_name_arg, + size_t table_name_length_arg, + const char *alias_arg, + enum thr_lock_type lock_type_arg, + bool routine, + TABLE_LIST *belong_to_view_arg, + uint8 trg_event_map_arg, + TABLE_LIST ***last_ptr) + { + init_one_table(db_name_arg, db_length_arg, table_name_arg, + table_name_length_arg, alias_arg, lock_type_arg); + cacheable_table= 1; + prelocking_placeholder= routine ? ROUTINE : FK; + open_type= routine ? OT_TEMPORARY_OR_BASE : OT_BASE_ONLY; + belong_to_view= belong_to_view_arg; + trg_event_map= trg_event_map_arg; + + **last_ptr= this; + prev_global= *last_ptr; + *last_ptr= &next_global; } /* @@ -1977,7 +2012,7 @@ struct TABLE_LIST This TABLE_LIST object is just placeholder for prelocking, it will be used for implicit LOCK TABLES only and won't be used in real statement. */ - bool prelocking_placeholder; + enum { USER, ROUTINE, FK } prelocking_placeholder; /** Indicates that if TABLE_LIST object corresponds to the table/view which requires special handling. diff --git a/sql/table_cache.cc b/sql/table_cache.cc index a31068c9bc3..f13d7183a99 100644 --- a/sql/table_cache.cc +++ b/sql/table_cache.cc @@ -52,7 +52,6 @@ #include "hash.h" #include "table.h" #include "sql_base.h" -#include "sql_statistics.h" /** Configuration. */ ulong tdc_size; /**< Table definition cache threshold for LRU eviction. */ @@ -377,6 +376,7 @@ bool tc_release_table(TABLE *table) { DBUG_ASSERT(table->in_use); DBUG_ASSERT(table->file); + DBUG_ASSERT(!table->pos_in_locked_tables); if (table->needs_reopen() || tc_records() > tc_size) { @@ -870,7 +870,6 @@ void tdc_release_share(TABLE_SHARE *share) mysql_mutex_lock(&share->tdc.LOCK_table_share); if (share->tdc.flushed) { - delete_stat_values_for_table_share(share); mysql_mutex_unlock(&share->tdc.LOCK_table_share); mysql_mutex_unlock(&LOCK_unused_shares); tdc_delete_share_from_hash(share); |