diff options
Diffstat (limited to 'sql/sql_base.cc')
-rw-r--r-- | sql/sql_base.cc | 1551 |
1 files changed, 372 insertions, 1179 deletions
diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 1dbed8c55d1..f31c3f36aa8 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -49,6 +49,7 @@ #include "transaction.h" #include "sql_prepare.h" #include "sql_statistics.h" +#include "sql_cte.h" #include <m_ctype.h> #include <my_dir.h> #include <hash.h> @@ -67,7 +68,7 @@ bool No_such_table_error_handler::handle_condition(THD *, uint sql_errno, const char*, - Sql_condition::enum_warning_level level, + Sql_condition::enum_warning_level *level, const char*, Sql_condition ** cond_hdl) { @@ -78,7 +79,7 @@ No_such_table_error_handler::handle_condition(THD *, return TRUE; } - if (level == Sql_condition::WARN_LEVEL_ERROR) + if (*level == Sql_condition::WARN_LEVEL_ERROR) m_unhandled_errors++; return FALSE; } @@ -111,7 +112,7 @@ public: bool handle_condition(THD *thd, uint sql_errno, const char* sqlstate, - Sql_condition::enum_warning_level level, + Sql_condition::enum_warning_level *level, const char* msg, Sql_condition ** cond_hdl); @@ -141,7 +142,7 @@ bool Repair_mrg_table_error_handler::handle_condition(THD *, uint sql_errno, const char*, - Sql_condition::enum_warning_level level, + Sql_condition::enum_warning_level *level, const char*, Sql_condition ** cond_hdl) { @@ -171,47 +172,13 @@ static bool auto_repair_table(THD *thd, TABLE_LIST *table_list); /** - Create a table cache/table definition cache key - - @param thd Thread context - @param key Buffer for the key to be created (must be of - size MAX_DBKEY_LENGTH). - @param db_name Database name. - @param table_name Table name. - - @note - The table cache_key is created from: - db_name + \0 - table_name + \0 - - additionally we add the following to make each tmp table - unique on the slave: - - 4 bytes for master thread id - 4 bytes pseudo thread id - - @return Length of key. -*/ - -uint create_tmp_table_def_key(THD *thd, char *key, - const char *db, const char *table_name) -{ - uint key_length= tdc_create_key(key, db, table_name); - int4store(key + key_length, thd->variables.server_id); - int4store(key + key_length + 4, thd->variables.pseudo_thread_id); - key_length+= TMP_TABLE_KEY_EXTRA; - return key_length; -} - - -/** Get table cache key for a table list element. @param table_list[in] Table list element. @param key[out] On return points to table cache key for the table. @note Unlike create_table_def_key() call this function doesn't construct - key in a buffer provider by caller. Instead it relies on the fact + key in a buffer provided by caller. Instead it relies on the fact that table list element for which key is requested has properly initialized MDL_request object and the fact that table definition cache key is suffix of key used in MDL subsystem. So to get table @@ -303,7 +270,7 @@ static my_bool list_open_tables_callback(TDC_element *element, (*arg->start_list)->in_use= 0; mysql_mutex_lock(&element->LOCK_table_share); - TDC_element::All_share_tables_list::Iterator it(element->all_tables); + All_share_tables_list::Iterator it(element->all_tables); TABLE *table; while ((table= it++)) if (table->in_use) @@ -335,84 +302,6 @@ OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild) DBUG_RETURN(argument.open_list); } -/***************************************************************************** - * Functions to free open table cache - ****************************************************************************/ - - -void intern_close_table(TABLE *table) -{ // Free all structures - DBUG_ENTER("intern_close_table"); - DBUG_PRINT("tcache", ("table: '%s'.'%s' 0x%lx", - table->s ? table->s->db.str : "?", - table->s ? table->s->table_name.str : "?", - (long) table)); - - free_io_cache(table); - delete table->triggers; - if (table->file) // Not true if placeholder - (void) closefrm(table, 1); // close file - table->alias.free(); - my_free(table); - DBUG_VOID_RETURN; -} - - -/* Free resources allocated by filesort() and read_record() */ - -void free_io_cache(TABLE *table) -{ - DBUG_ENTER("free_io_cache"); - if (table->sort.io_cache) - { - close_cached_file(table->sort.io_cache); - my_free(table->sort.io_cache); - table->sort.io_cache=0; - } - DBUG_VOID_RETURN; -} - - -/** - Auxiliary function which allows to kill delayed threads for - particular table identified by its share. - - @param share Table share. - - @pre Caller should have TABLE_SHARE::tdc.LOCK_table_share mutex. -*/ - -void kill_delayed_threads_for_table(TDC_element *element) -{ - TDC_element::All_share_tables_list::Iterator it(element->all_tables); - TABLE *tab; - - mysql_mutex_assert_owner(&element->LOCK_table_share); - - if (!delayed_insert_threads) - return; - - while ((tab= it++)) - { - THD *in_use= tab->in_use; - - DBUG_ASSERT(in_use && tab->s->tdc->flushed); - if ((in_use->system_thread & SYSTEM_THREAD_DELAYED_INSERT) && - ! in_use->killed) - { - in_use->set_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); - } - } -} - /* Close all tables which aren't in use by any thread @@ -436,7 +325,7 @@ void kill_delayed_threads_for_table(TDC_element *element) struct close_cached_tables_arg { - ulong refresh_version; + tdc_version_t refresh_version; TDC_element *element; }; @@ -462,7 +351,7 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables, { bool result= FALSE; struct timespec abstime; - ulong refresh_version; + tdc_version_t refresh_version; DBUG_ENTER("close_cached_tables"); DBUG_ASSERT(thd || (!wait_for_refresh && !tables)); @@ -593,7 +482,8 @@ err_with_reopen: old locks. This should always succeed (unless some external process has removed the tables) */ - thd->locked_tables_list.reopen_tables(thd, false); + if (thd->locked_tables_list.reopen_tables(thd, false)) + result= true; /* Since downgrade_lock() won't do anything with shared metadata lock it is much simpler to go through all open tables rather @@ -680,85 +570,6 @@ bool close_cached_connection_tables(THD *thd, LEX_STRING *connection) } -/** - Mark all temporary tables which were used by the current statement or - substatement as free for reuse, but only if the query_id can be cleared. - - @param thd thread context - - @remark For temp tables associated with a open SQL HANDLER the query_id - is not reset until the HANDLER is closed. -*/ - -static void mark_temp_tables_as_free_for_reuse(THD *thd) -{ - DBUG_ENTER("mark_temp_tables_as_free_for_reuse"); - - if (thd->query_id == 0) - { - /* Thread has not executed any statement and has not used any tmp tables */ - DBUG_VOID_RETURN; - } - - if (thd->have_temporary_tables()) - { - thd->lock_temporary_tables(); - for (TABLE *table= thd->temporary_tables ; table ; table= table->next) - { - if ((table->query_id == thd->query_id) && ! table->open_by_handler) - mark_tmp_table_for_reuse(table); - } - thd->unlock_temporary_tables(1); - } - DBUG_VOID_RETURN; -} - - -/** - Reset a single temporary table. - Effectively this "closes" one temporary table, - in a session. - - @param table Temporary table. -*/ - -void mark_tmp_table_for_reuse(TABLE *table) -{ - DBUG_ENTER("mark_tmp_table_for_reuse"); - DBUG_ASSERT(table->s->tmp_table); - - table->query_id= 0; - table->file->ha_reset(); - - /* Detach temporary MERGE children from temporary parent. */ - DBUG_ASSERT(table->file); - table->file->extra(HA_EXTRA_DETACH_CHILDREN); - - /* - Reset temporary table lock type to it's default value (TL_WRITE). - - Statements such as INSERT INTO .. SELECT FROM tmp, CREATE TABLE - .. SELECT FROM tmp and UPDATE may under some circumstances modify - the lock type of the tables participating in the statement. This - isn't a problem for non-temporary tables since their lock type is - reset at every open, but the same does not occur for temporary - tables for historical reasons. - - Furthermore, the lock type of temporary tables is not really that - important because they can only be used by one query at a time and - not even twice in a query -- a temporary table is represented by - only one TABLE object. Nonetheless, it's safer from a maintenance - point of view to reset the lock type of this singleton TABLE object - as to not cause problems when the table is reused. - - Even under LOCK TABLES mode its okay to reset the lock type as - LOCK TABLES is allowed (but ignored) for a temporary table. - */ - table->reginfo.lock_type= TL_WRITE; - DBUG_VOID_RETURN; -} - - /* Mark all tables in the list which were used by current substatement as free for reuse. @@ -772,6 +583,12 @@ void mark_tmp_table_for_reuse(TABLE *table) Marks all tables in the list which were used by current substatement (they are marked by its query_id) as free for reuse. + Clear 'check_table_binlog_row_based_done' flag. For tables which were used + by current substatement the flag is cleared as part of 'ha_reset()' call. + For the rest of the open tables not used by current substament if this + flag is enabled as part of current substatement execution, clear the flag + explicitly. + NOTE The reason we reset query_id is that it's not enough to just test if table->query_id != thd->query_id to know if a table is in use. @@ -793,28 +610,13 @@ static void mark_used_tables_as_free_for_reuse(THD *thd, TABLE *table) table->query_id= 0; table->file->ha_reset(); } + else if (table->file->check_table_binlog_row_based_done) + table->file->clear_cached_table_binlog_row_based_flag(); } } /** - Auxiliary function to close all tables in the open_tables list. - - @param thd Thread context. - - @remark It should not ordinarily be called directly. -*/ - -static void close_open_tables(THD *thd) -{ - DBUG_PRINT("info", ("thd->open_tables: 0x%lx", (long) thd->open_tables)); - - while (thd->open_tables) - (void) close_thread_table(thd, &thd->open_tables); -} - - -/** Close all open instances of the table but keep the MDL lock. Works both under LOCK TABLES and in the normal mode. @@ -846,6 +648,8 @@ close_all_tables_for_name(THD *thd, TABLE_SHARE *share, ha_extra_function extra, TABLE *skip_table) { + DBUG_ASSERT(!share->tmp_table); + char key[MAX_DBKEY_LENGTH]; uint key_length= share->table_cache_key.length; const char *db= key; @@ -923,8 +727,8 @@ void close_thread_tables(THD *thd) #ifdef EXTRA_DEBUG DBUG_PRINT("tcache", ("open tables:")); for (table= thd->open_tables; table; table= table->next) - DBUG_PRINT("tcache", ("table: '%s'.'%s' 0x%lx", table->s->db.str, - table->s->table_name.str, (long) table)); + DBUG_PRINT("tcache", ("table: '%s'.'%s' %p", table->s->db.str, + table->s->table_name.str, table)); #endif #if defined(ENABLED_DEBUG_SYNC) @@ -976,10 +780,27 @@ void close_thread_tables(THD *thd) thd->derived_tables= 0; } + if (thd->rec_tables) + { + TABLE *next; + /* + Close all temporary tables created for recursive table references. + This action was postponed because the table could be used in the + statements like ANALYZE WITH r AS (...) SELECT * from r + where r is defined through recursion. + */ + for (table= thd->rec_tables ; table ; table= next) + { + next= table->next; + free_tmp_table(thd, table); + } + thd->rec_tables= 0; + } + /* Mark all temporary tables used by this statement as free for reuse. */ - mark_temp_tables_as_free_for_reuse(thd); + thd->mark_tmp_tables_as_free_for_reuse(); if (thd->locked_tables_mode) { @@ -1034,8 +855,8 @@ void close_thread_tables(THD *thd) Closing a MERGE child before the parent would be fatal if the other thread tries to abort the MERGE lock in between. */ - if (thd->open_tables) - close_open_tables(thd); + while (thd->open_tables) + (void) close_thread_table(thd, &thd->open_tables); DBUG_VOID_RETURN; } @@ -1047,9 +868,9 @@ void close_thread_table(THD *thd, TABLE **table_ptr) { TABLE *table= *table_ptr; DBUG_ENTER("close_thread_table"); - DBUG_PRINT("tcache", ("table: '%s'.'%s' 0x%lx", table->s->db.str, - table->s->table_name.str, (long) table)); - DBUG_ASSERT(table->key_read == 0); + DBUG_PRINT("tcache", ("table: '%s'.'%s' %p", table->s->db.str, + table->s->table_name.str, table)); + DBUG_ASSERT(!table->file->keyread_enabled()); DBUG_ASSERT(!table->file || table->file->inited == handler::NONE); /* @@ -1085,210 +906,13 @@ void close_thread_table(THD *thd, TABLE **table_ptr) Do this *before* entering the TABLE_SHARE::tdc.LOCK_table_share critical section. */ - if (table->file != NULL) - MYSQL_UNBIND_TABLE(table->file); + MYSQL_UNBIND_TABLE(table->file); tc_release_table(table); DBUG_VOID_RETURN; } -/* close_temporary_tables' internal, 4 is due to uint4korr definition */ -static inline uint tmpkeyval(THD *thd, TABLE *table) -{ - return uint4korr(table->s->table_cache_key.str + table->s->table_cache_key.length - 4); -} - - -/* - Close all temporary tables created by 'CREATE TEMPORARY TABLE' for thread - creates one DROP TEMPORARY TABLE binlog event for each pseudo-thread - - Temporary tables created in a sql slave is closed by - Relay_log_info::close_temporary_tables() - -*/ - -bool close_temporary_tables(THD *thd) -{ - DBUG_ENTER("close_temporary_tables"); - TABLE *table; - TABLE *next= NULL; - TABLE *prev_table; - /* Assume thd->variables.option_bits has OPTION_QUOTE_SHOW_CREATE */ - bool was_quote_show= TRUE; - bool error= 0; - - if (!thd->temporary_tables) - DBUG_RETURN(FALSE); - DBUG_ASSERT(!thd->rgi_slave); - - /* - Ensure we don't have open HANDLERs for tables we are about to close. - This is necessary when close_temporary_tables() is called as part - of execution of BINLOG statement (e.g. for format description event). - */ - mysql_ha_rm_temporary_tables(thd); - if (!mysql_bin_log.is_open()) - { - TABLE *tmp_next; - for (TABLE *t= thd->temporary_tables; t; t= tmp_next) - { - tmp_next= t->next; - mysql_lock_remove(thd, thd->lock, t); - close_temporary(t, 1, 1); - } - thd->temporary_tables= 0; - DBUG_RETURN(FALSE); - } - - /* Better add "if exists", in case a RESET MASTER has been done */ - const char stub[]= "DROP /*!40005 TEMPORARY */ TABLE IF EXISTS "; - char buf[FN_REFLEN]; - String s_query(buf, sizeof(buf), system_charset_info); - bool found_user_tables= FALSE; - - s_query.copy(stub, sizeof(stub)-1, system_charset_info); - - /* - Insertion sort of temp tables by pseudo_thread_id to build ordered list - of sublists of equal pseudo_thread_id - */ - - for (prev_table= thd->temporary_tables, table= prev_table->next; - table; - prev_table= table, table= table->next) - { - TABLE *prev_sorted /* same as for prev_table */, *sorted; - if (is_user_table(table)) - { - if (!found_user_tables) - found_user_tables= true; - for (prev_sorted= NULL, sorted= thd->temporary_tables; sorted != table; - prev_sorted= sorted, sorted= sorted->next) - { - if (!is_user_table(sorted) || - tmpkeyval(thd, sorted) > tmpkeyval(thd, table)) - { - /* move into the sorted part of the list from the unsorted */ - prev_table->next= table->next; - table->next= sorted; - if (prev_sorted) - { - prev_sorted->next= table; - } - else - { - thd->temporary_tables= table; - } - table= prev_table; - break; - } - } - } - } - - /* We always quote db,table names though it is slight overkill */ - if (found_user_tables && - !(was_quote_show= MY_TEST(thd->variables.option_bits & - OPTION_QUOTE_SHOW_CREATE))) - { - thd->variables.option_bits |= OPTION_QUOTE_SHOW_CREATE; - } - - /* scan sorted tmps to generate sequence of DROP */ - for (table= thd->temporary_tables; table; table= next) - { - if (is_user_table(table)) - { - bool save_thread_specific_used= thd->thread_specific_used; - my_thread_id save_pseudo_thread_id= thd->variables.pseudo_thread_id; - char db_buf[FN_REFLEN]; - String db(db_buf, sizeof(db_buf), system_charset_info); - bool at_least_one_create_logged; - /* Set pseudo_thread_id to be that of the processed table */ - thd->variables.pseudo_thread_id= tmpkeyval(thd, table); - - db.copy(table->s->db.str, table->s->db.length, system_charset_info); - /* Reset s_query() if changed by previous loop */ - s_query.length(sizeof(stub)-1); - - /* Loop forward through all tables that belong to a common database - within the sublist of common pseudo_thread_id to create single - DROP query - */ - for (at_least_one_create_logged= false; - table && is_user_table(table) && - tmpkeyval(thd, table) == thd->variables.pseudo_thread_id && - table->s->db.length == db.length() && - memcmp(table->s->db.str, db.ptr(), db.length()) == 0; - table= next) - { - if (table->s->table_creation_was_logged) - { - at_least_one_create_logged= true; - /* - We are going to add ` around the table names and possible more - due to special characters - */ - append_identifier(thd, &s_query, table->s->table_name.str, - strlen(table->s->table_name.str)); - s_query.append(','); - } - next= table->next; - mysql_lock_remove(thd, thd->lock, table); - close_temporary(table, 1, 1); - } - if (at_least_one_create_logged) - { - thd->clear_error(); - CHARSET_INFO *cs_save= thd->variables.character_set_client; - thd->variables.character_set_client= system_charset_info; - thd->thread_specific_used= TRUE; - Query_log_event qinfo(thd, s_query.ptr(), - s_query.length() - 1 /* to remove trailing ',' */, - FALSE, TRUE, FALSE, 0); - qinfo.db= db.ptr(); - qinfo.db_len= db.length(); - thd->variables.character_set_client= cs_save; - - thd->get_stmt_da()->set_overwrite_status(true); - thd->transaction.stmt.mark_dropped_temp_table(); - if ((error= (mysql_bin_log.write(&qinfo) || error))) - { - /* - If we're here following THD::cleanup, thence the connection - has been closed already. So lets print a message to the - error log instead of pushing yet another error into the - stmt_da. - - Also, we keep the error flag so that we propagate the error - up in the stack. This way, if we're the SQL thread we notice - that close_temporary_tables failed. (Actually, the SQL - thread only calls close_temporary_tables while applying old - Start_log_event_v3 events.) - */ - sql_print_error("Failed to write the DROP statement for " - "temporary tables to binary log"); - } - thd->get_stmt_da()->set_overwrite_status(false); - } - thd->variables.pseudo_thread_id= save_pseudo_thread_id; - thd->thread_specific_used= save_thread_specific_used; - } - else - { - next= table->next; - close_temporary(table, 1, 1); - } - } - if (!was_quote_show) - thd->variables.option_bits&= ~OPTION_QUOTE_SHOW_CREATE; /* restore option */ - thd->temporary_tables=0; - - DBUG_RETURN(error); -} - /* Find table in list. @@ -1315,8 +939,7 @@ TABLE_LIST *find_table_in_list(TABLE_LIST *table, { for (; table; table= table->*link ) { - if ((table->table == 0 || table->table->s->tmp_table == NO_TMP_TABLE) && - strcmp(table->db, db_name) == 0 && + if (strcmp(table->db, db_name) == 0 && strcmp(table->table_name, table_name) == 0) break; } @@ -1382,9 +1005,6 @@ TABLE_LIST* find_dup_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list, /* All MyISAMMRG children are plain MyISAM tables. */ DBUG_ASSERT(table->table->file->ht->db_type != DB_TYPE_MRG_MYISAM); - /* temporary table is always unique */ - if (table->table && table->table->s->tmp_table != NO_TMP_TABLE) - DBUG_RETURN(0); table= table->find_underlying_table(table->table); /* as far as we have table->table we have to find real TABLE_LIST of @@ -1421,6 +1041,12 @@ retry: if (res->table && (res->table == table->table)) continue; + /* Skip if table is tmp table */ + if (check_flag & CHECK_DUP_SKIP_TEMP_TABLE && + res->table && res->table->s->tmp_table != NO_TMP_TABLE) + { + continue; + } if (check_flag & CHECK_DUP_FOR_CREATE) DBUG_RETURN(res); @@ -1461,7 +1087,7 @@ retry: Try to fix by materializing the derived table */ TABLE_LIST *derived= res->belong_to_derived; - if (derived->is_merged_derived()) + if (derived->is_merged_derived() && !derived->derived->is_excluded()) { DBUG_PRINT("info", ("convert merged to materialization to resolve the conflict")); @@ -1571,292 +1197,6 @@ void update_non_unique_table_error(TABLE_LIST *update, /** - Find temporary table specified by database and table names in the - THD::temporary_tables list. - - @return TABLE instance if a temporary table has been found; NULL otherwise. -*/ - -TABLE *find_temporary_table(THD *thd, const char *db, const char *table_name) -{ - char key[MAX_DBKEY_LENGTH]; - uint key_length= create_tmp_table_def_key(thd, key, db, table_name); - return find_temporary_table(thd, key, key_length); -} - - -/** - Find a temporary table specified by TABLE_LIST instance in the - THD::temporary_tables list. - - @return TABLE instance if a temporary table has been found; NULL otherwise. -*/ - -TABLE *find_temporary_table(THD *thd, const TABLE_LIST *tl) -{ - const char *tmp_key; - char key[MAX_DBKEY_LENGTH]; - uint key_length; - - key_length= get_table_def_key(tl, &tmp_key); - memcpy(key, tmp_key, key_length); - int4store(key + key_length, thd->variables.server_id); - int4store(key + key_length + 4, thd->variables.pseudo_thread_id); - - return find_temporary_table(thd, key, key_length + TMP_TABLE_KEY_EXTRA); -} - - -static bool -use_temporary_table(THD *thd, TABLE *table, TABLE **out_table) -{ - *out_table= table; - if (!table) - return false; - /* - Temporary tables are not safe for parallel replication. They were - designed to be visible to one thread only, so have no table locking. - Thus there is no protection against two conflicting transactions - committing in parallel and things like that. - - So for now, anything that uses temporary tables will be serialised - with anything before it, when using parallel replication. - - ToDo: We might be able to introduce a reference count or something - on temp tables, and have slave worker threads wait for it to reach - zero before being allowed to use the temp table. Might not be worth - it though, as statement-based replication using temporary tables is - in any case rather fragile. - */ - if (thd->rgi_slave && thd->rgi_slave->is_parallel_exec && - thd->wait_for_prior_commit()) - return true; - /* - We need to set the THD as it may be different in case of - parallel replication - */ - if (table->in_use != thd) - { - table->in_use= thd; -#ifdef REMOVE_AFTER_MERGE_WITH_10 - if (thd->rgi_slave) - { - /* - We may be stealing an opened temporary tables from one slave - thread to another, we need to let the performance schema know that, - for aggregates per thread to work properly. - */ - MYSQL_UNBIND_TABLE(table->file); - MYSQL_REBIND_TABLE(table->file); - } -#endif - } - return false; -} - -bool -find_and_use_temporary_table(THD *thd, const char *db, const char *table_name, - TABLE **out_table) -{ - return use_temporary_table(thd, find_temporary_table(thd, db, table_name), - out_table); -} - - -bool -find_and_use_temporary_table(THD *thd, const TABLE_LIST *tl, TABLE **out_table) -{ - return use_temporary_table(thd, find_temporary_table(thd, tl), out_table); -} - - -/** - Find a temporary table specified by a key in the THD::temporary_tables list. - - @return TABLE instance if a temporary table has been found; NULL otherwise. -*/ - -TABLE *find_temporary_table(THD *thd, - const char *table_key, - uint table_key_length) -{ - TABLE *result= 0; - if (!thd->have_temporary_tables()) - return NULL; - - thd->lock_temporary_tables(); - for (TABLE *table= thd->temporary_tables; table; table= table->next) - { - if (table->s->table_cache_key.length == table_key_length && - !memcmp(table->s->table_cache_key.str, table_key, table_key_length)) - { - result= table; - break; - } - } - thd->unlock_temporary_tables(0); - return result; -} - - -/** - Drop a temporary table. - - Try to locate the table in the list of thd->temporary_tables. - If the table is found: - - if the table is being used by some outer statement, fail. - - if the table is locked with LOCK TABLES or by prelocking, - unlock it and remove it from the list of locked tables - (THD::lock). Currently only transactional temporary tables - are locked. - - Close the temporary table, remove its .FRM - - remove the table from the list of temporary tables - - This function is used to drop user temporary tables, as well as - internal tables created in CREATE TEMPORARY TABLE ... SELECT - or ALTER TABLE. Even though part of the work done by this function - is redundant when the table is internal, as long as we - link both internal and user temporary tables into the same - thd->temporary_tables list, it's impossible to tell here whether - we're dealing with an internal or a user temporary table. - - @param thd Thread handler - @param table Temporary table to be deleted - @param is_trans Is set to the type of the table: - transactional (e.g. innodb) as TRUE or non-transactional - (e.g. myisam) as FALSE. - - @retval 0 the table was found and dropped successfully. - @retval -1 the table is in use by a outer query -*/ - -int drop_temporary_table(THD *thd, TABLE *table, bool *is_trans) -{ - DBUG_ENTER("drop_temporary_table"); - DBUG_PRINT("tmptable", ("closing table: '%s'.'%s'", - table->s->db.str, table->s->table_name.str)); - - /* Table might be in use by some outer statement. */ - if (table->query_id && table->query_id != thd->query_id) - { - DBUG_PRINT("info", ("table->query_id: %lu thd->query_id: %lu", - (ulong) table->query_id, (ulong) thd->query_id)); - - my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias.c_ptr()); - DBUG_RETURN(-1); - } - - *is_trans= table->file->has_transactions(); - - /* - If LOCK TABLES list is not empty and contains this table, - unlock the table and remove the table from this list. - */ - mysql_lock_remove(thd, thd->lock, table); - close_temporary_table(thd, table, 1, 1); - DBUG_RETURN(0); -} - - -/* - unlink from thd->temporary tables and close temporary table -*/ - -void close_temporary_table(THD *thd, TABLE *table, - bool free_share, bool delete_table) -{ - DBUG_ENTER("close_temporary_table"); - DBUG_PRINT("tmptable", ("closing table: '%s'.'%s' 0x%lx alias: '%s'", - table->s->db.str, table->s->table_name.str, - (long) table, table->alias.c_ptr())); - - thd->lock_temporary_tables(); - if (table->prev) - { - table->prev->next= table->next; - if (table->prev->next) - table->next->prev= table->prev; - } - else - { - /* removing the item from the list */ - DBUG_ASSERT(table == thd->temporary_tables); - /* - slave must reset its temporary list pointer to zero to exclude - passing non-zero value to end_slave via rli->save_temporary_tables - when no temp tables opened, see an invariant below. - */ - thd->temporary_tables= table->next; - if (thd->temporary_tables) - table->next->prev= 0; - } - if (thd->rgi_slave) - { - /* natural invariant of temporary_tables */ - DBUG_ASSERT(slave_open_temp_tables || !thd->temporary_tables); - thread_safe_decrement32(&slave_open_temp_tables); - table->in_use= 0; // No statistics - } - thd->unlock_temporary_tables(0); - close_temporary(table, free_share, delete_table); - DBUG_VOID_RETURN; -} - - -/* - Close and delete a temporary table - - NOTE - This dosn't unlink table from thd->temporary - If this is needed, use close_temporary_table() -*/ - -void close_temporary(TABLE *table, bool free_share, bool delete_table) -{ - handlerton *table_type= table->s->db_type(); - DBUG_ENTER("close_temporary"); - DBUG_PRINT("tmptable", ("closing table: '%s'.'%s'", - table->s->db.str, table->s->table_name.str)); - - free_io_cache(table); - closefrm(table, 0); - if (delete_table) - rm_temporary_table(table_type, table->s->path.str); - if (free_share) - { - free_table_share(table->s); - my_free(table); - } - DBUG_VOID_RETURN; -} - - -/* - Used by ALTER TABLE when the table is a temporary one. It changes something - only if the ALTER contained a RENAME clause (otherwise, table_name is the old - name). - Prepares a table cache key, which is the concatenation of db, table_name and - thd->slave_proxy_id, separated by '\0'. -*/ - -bool rename_temporary_table(THD* thd, TABLE *table, const char *db, - const char *table_name) -{ - char *key; - uint key_length; - TABLE_SHARE *share= table->s; - DBUG_ENTER("rename_temporary_table"); - - if (!(key=(char*) alloc_root(&share->mem_root, MAX_DBKEY_LENGTH))) - DBUG_RETURN(1); /* purecov: inspected */ - - key_length= create_tmp_table_def_key(thd, key, db, table_name); - share->set_table_cache_key(key, key_length); - DBUG_RETURN(0); -} - - -/** Force all other threads to stop using the table by upgrading metadata lock on it and remove unused TABLE instances from cache. @@ -1878,8 +1218,9 @@ bool wait_while_table_is_used(THD *thd, TABLE *table, enum ha_extra_function function) { DBUG_ENTER("wait_while_table_is_used"); - DBUG_PRINT("enter", ("table: '%s' share: 0x%lx db_stat: %u version: %lu", - table->s->table_name.str, (ulong) table->s, + DBUG_ASSERT(!table->s->tmp_table); + DBUG_PRINT("enter", ("table: '%s' share: %p db_stat: %u version: %lld", + table->s->table_name.str, table->s, table->db_stat, table->s->tdc->version)); if (thd->mdl_context.upgrade_shared_lock( @@ -1921,7 +1262,7 @@ void drop_open_table(THD *thd, TABLE *table, const char *db_name, { DBUG_ENTER("drop_open_table"); if (table->s->tmp_table) - close_temporary_table(thd, table, 1, 1); + thd->drop_temporary_table(table, NULL, true); else { DBUG_ASSERT(table == thd->open_tables); @@ -1957,7 +1298,7 @@ public: virtual bool handle_condition(THD *thd, uint sql_errno, const char* sqlstate, - Sql_condition::enum_warning_level level, + Sql_condition::enum_warning_level *level, const char* msg, Sql_condition ** cond_hdl); @@ -1976,7 +1317,7 @@ private: bool MDL_deadlock_handler::handle_condition(THD *, uint sql_errno, const char*, - Sql_condition::enum_warning_level, + Sql_condition::enum_warning_level*, const char*, Sql_condition ** cond_hdl) { @@ -2177,7 +1518,7 @@ bool is_locked_view(THD *thd, TABLE_LIST *t) DBUG_RETURN(FALSE); } - if (!tdc_open_view(thd, t, t->alias, CHECK_METADATA_VERSION)) + if (!tdc_open_view(thd, t, CHECK_METADATA_VERSION)) { DBUG_ASSERT(t->view != 0); DBUG_RETURN(TRUE); // VIEW @@ -2258,7 +1599,7 @@ bool open_table(THD *thd, TABLE_LIST *table_list, Open_table_context *ot_ctx) Note that we allow write locks on log tables as otherwise logging to general/slow log would be disabled in read only transactions. */ - if (table_list->mdl_request.type >= MDL_SHARED_WRITE && + if (table_list->mdl_request.is_write_lock_request() && thd->tx_read_only && !(flags & (MYSQL_LOCK_LOG_TABLE | MYSQL_OPEN_HAS_MDL_LOCK))) { @@ -2266,6 +1607,12 @@ bool open_table(THD *thd, TABLE_LIST *table_list, Open_table_context *ot_ctx) DBUG_RETURN(true); } + if (!table_list->db) + { + my_error(ER_NO_DB_ERROR, MYF(0)); + DBUG_RETURN(true); + } + key_length= get_table_def_key(table_list, &key); /* @@ -2378,7 +1725,7 @@ bool open_table(THD *thd, TABLE_LIST *table_list, Open_table_context *ot_ctx) pre-acquiring metadata locks at the beggining of open_tables() call. */ - if (table_list->mdl_request.type >= MDL_SHARED_WRITE && + if (table_list->mdl_request.is_write_lock_request() && ! (flags & (MYSQL_OPEN_IGNORE_GLOBAL_READ_LOCK | MYSQL_OPEN_FORCE_SHARED_MDL | MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL | @@ -2446,10 +1793,7 @@ bool open_table(THD *thd, TABLE_LIST *table_list, Open_table_context *ot_ctx) retry_share: - share= tdc_acquire_share(thd, table_list->db, table_list->table_name, - key, key_length, - table_list->mdl_request.key.tc_hash_value(), - gts_flags, &table); + share= tdc_acquire_share(thd, table_list, gts_flags, &table); if (!share) { @@ -2518,7 +1862,7 @@ retry_share: { if (share->tdc->flushed) { - DBUG_PRINT("info", ("Found old share version: %lu current: %lu", + DBUG_PRINT("info", ("Found old share version: %lld current: %lld", share->tdc->version, tdc_refresh_version())); /* We already have an MDL lock. But we have encountered an old @@ -2582,12 +1926,8 @@ retry_share: goto err_lock; error= open_table_from_share(thd, share, alias, - (uint) (HA_OPEN_KEYFILE | - HA_OPEN_RNDFILE | - HA_GET_INDEX | - HA_TRY_READ_ONLY), - (READ_KEYINFO | COMPUTE_TYPES | - EXTRA_RECORD), + HA_OPEN_KEYFILE | HA_TRY_READ_ONLY, + EXTRA_RECORD, thd->open_options, table, FALSE); if (error) @@ -2609,7 +1949,7 @@ retry_share: } if (open_table_entry_fini(thd, share, table)) { - closefrm(table, 0); + closefrm(table); my_free(table); goto err_lock; } @@ -2830,6 +2170,9 @@ Locked_tables_list::init_locked_tables(THD *thd) return TRUE; } } + + TRANSACT_TRACKER(add_trx_state(thd, TX_LOCKED_TABLES)); + thd->enter_locked_tables_mode(LTM_LOCK_TABLES); return FALSE; @@ -2870,6 +2213,8 @@ Locked_tables_list::unlock_locked_tables(THD *thd) } thd->leave_locked_tables_mode(); + TRANSACT_TRACKER(clear_trx_state(thd, TX_LOCKED_TABLES)); + DBUG_ASSERT(thd->transaction.stmt.is_empty()); close_thread_tables(thd); @@ -3026,6 +2371,7 @@ unlink_all_closed_tables(THD *thd, MYSQL_LOCK *lock, size_t reopen_count) DBUG_ASSERT(thd->open_tables == m_reopen_array[reopen_count]); thd->open_tables->pos_in_locked_tables->table= NULL; + thd->open_tables->pos_in_locked_tables= 0; close_thread_table(thd, &thd->open_tables); } @@ -3080,9 +2426,17 @@ Locked_tables_list::reopen_tables(THD *thd, bool need_reopen) { if (!table_list->table || !table_list->table->needs_reopen()) continue; - /* no need to remove the table from the TDC here, thus (TABLE*)1 */ - close_all_tables_for_name(thd, table_list->table->s, - HA_EXTRA_NOT_USED, (TABLE*)1); + for (TABLE **prev= &thd->open_tables; *prev; prev= &(*prev)->next) + { + if (*prev == table_list->table) + { + thd->locked_tables_list.unlink_from_list(thd, table_list, false); + mysql_lock_remove(thd, thd->lock, *prev); + (*prev)->file->extra(HA_EXTRA_PREPARE_FOR_FORCED_CLOSE); + close_thread_table(thd, prev); + break; + } + } DBUG_ASSERT(table_list->table == NULL); } else @@ -3328,9 +2682,6 @@ check_and_update_routine_version(THD *thd, Sroutine_hash_entry *rt, @param thd Thread handle @param table_list TABLE_LIST with db, table_name & belong_to_view - @param alias Alias name - @param cache_key Key for table definition cache - @param cache_key_length Length of cache_key @param flags Flags which modify how we open the view @todo This function is needed for special handling of views under @@ -3339,16 +2690,13 @@ check_and_update_routine_version(THD *thd, Sroutine_hash_entry *rt, @return FALSE if success, TRUE - otherwise. */ -bool tdc_open_view(THD *thd, TABLE_LIST *table_list, const char *alias, - const char *cache_key, uint cache_key_length, - uint flags) +bool tdc_open_view(THD *thd, TABLE_LIST *table_list, uint flags) { TABLE not_used; TABLE_SHARE *share; bool err= TRUE; - if (!(share= tdc_acquire_share(thd, table_list->db, table_list->table_name, - cache_key, cache_key_length, GTS_VIEW))) + if (!(share= tdc_acquire_share(thd, table_list, GTS_VIEW))) return TRUE; DBUG_ASSERT(share->is_view); @@ -3435,16 +2783,14 @@ static bool auto_repair_table(THD *thd, TABLE_LIST *table_list) if (!(entry= (TABLE*)my_malloc(sizeof(TABLE), MYF(MY_WME)))) return result; - if (!(share= tdc_acquire_share_shortlived(thd, table_list, GTS_TABLE))) + if (!(share= tdc_acquire_share(thd, table_list, GTS_TABLE))) goto end_free; DBUG_ASSERT(! share->is_view); if (open_table_from_share(thd, share, table_list->alias, - (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE | - HA_GET_INDEX | - HA_TRY_READ_ONLY), - READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD, + HA_OPEN_KEYFILE | HA_TRY_READ_ONLY, + EXTRA_RECORD, ha_open_options | HA_OPEN_FOR_REPAIR, entry, FALSE) || ! entry->file || (entry->file->is_crashed() && entry->file->ha_check_and_repair(thd))) @@ -3455,12 +2801,12 @@ static bool auto_repair_table(THD *thd, TABLE_LIST *table_list) sql_print_error("Couldn't repair table: %s.%s", share->db.str, share->table_name.str); if (entry->file) - closefrm(entry, 0); + closefrm(entry); } else { thd->clear_error(); // Clear error message - closefrm(entry, 0); + closefrm(entry); result= FALSE; } @@ -3592,7 +2938,7 @@ public: virtual bool handle_condition(THD *thd, uint sql_errno, const char* sqlstate, - Sql_condition::enum_warning_level level, + Sql_condition::enum_warning_level *level, const char* msg, Sql_condition ** cond_hdl) { @@ -3654,8 +3000,7 @@ Open_table_context::recover_from_failed_open() if (open_if_exists) m_thd->push_internal_handler(&no_such_table_handler); - result= !tdc_acquire_share(m_thd, m_failed_table->db, - m_failed_table->table_name, + result= !tdc_acquire_share(m_thd, m_failed_table, GTS_TABLE | GTS_FORCE_DISCOVERY | GTS_NOLOCK); if (open_if_exists) { @@ -4025,6 +3370,58 @@ open_and_process_table(THD *thd, TABLE_LIST *tables, uint *counter, uint flags, tables->table_name= tables->view_name.str; tables->table_name_length= tables->view_name.length; } + else if (tables->select_lex) + { + /* + Check whether 'tables' refers to a table defined in a with clause. + If so set the reference to the definition in tables->with. + */ + if (!tables->with) + tables->with= tables->select_lex->find_table_def_in_with_clauses(tables); + /* + If 'tables' is defined in a with clause set the pointer to the + specification from its definition in tables->derived. + */ + if (tables->with) + { + if (tables->is_recursive_with_table() && + !tables->is_with_table_recursive_reference()) + { + tables->with->rec_outer_references++; + With_element *with_elem= tables->with; + while ((with_elem= with_elem->get_next_mutually_recursive()) != + tables->with) + with_elem->rec_outer_references++; + } + if (tables->set_as_with_table(thd, tables->with)) + DBUG_RETURN(1); + else + goto end; + } + } + + if (!tables->derived && + is_infoschema_db(tables->db, tables->db_length)) + { + /* + Check whether the information schema contains a table + whose name is tables->schema_table_name + */ + ST_SCHEMA_TABLE *schema_table= tables->schema_table; + if (!schema_table || + (schema_table->hidden && + ((sql_command_flags[lex->sql_command] & CF_STATUS_COMMAND) == 0 || + /* + this check is used for show columns|keys from I_S hidden table + */ + lex->sql_command == SQLCOM_SHOW_FIELDS || + lex->sql_command == SQLCOM_SHOW_KEYS))) + { + my_error(ER_UNKNOWN_TABLE, MYF(0), + tables->table_name, INFORMATION_SCHEMA_NAME.str); + DBUG_RETURN(1); + } + } /* If this TABLE_LIST object is a placeholder for an information_schema table, create a temporary table to represent the information_schema @@ -4110,7 +3507,7 @@ open_and_process_table(THD *thd, TABLE_LIST *tables, uint *counter, uint flags, of temporary tables we have to try to open temporary table for it. We can't simply skip this table list element and postpone opening of - temporary tabletill the execution of substatement for several reasons: + temporary table till the execution of substatement for several reasons: - Temporary table can be a MERGE table with base underlying tables, so its underlying tables has to be properly open and locked at prelocking stage. @@ -4126,7 +3523,7 @@ open_and_process_table(THD *thd, TABLE_LIST *tables, uint *counter, uint flags, The problem is that since those attributes are not set in merge children, another round of PREPARE will not help. */ - error= open_temporary_table(thd, tables); + error= thd->open_temporary_table(tables); if (!error && !tables->table) error= open_table(thd, tables, ot_ctx); @@ -4145,7 +3542,8 @@ open_and_process_table(THD *thd, TABLE_LIST *tables, uint *counter, uint flags, Repair_mrg_table_error_handler repair_mrg_table_handler; thd->push_internal_handler(&repair_mrg_table_handler); - error= open_temporary_table(thd, tables); + error= thd->open_temporary_table(tables); + if (!error && !tables->table) error= open_table(thd, tables, ot_ctx); @@ -4161,7 +3559,7 @@ open_and_process_table(THD *thd, TABLE_LIST *tables, uint *counter, uint flags, still might need to look for a temporary table if this table list element corresponds to underlying table of a merge table. */ - error= open_temporary_table(thd, tables); + error= thd->open_temporary_table(tables); } if (!error && !tables->table) @@ -4278,6 +3676,7 @@ end: new locks, so use open_tables_check_upgradable_mdl() instead. @param thd Thread context. + @param options DDL options. @param tables_start Start of list of tables on which upgradable locks should be acquired. @param tables_end End of list of tables. @@ -4320,6 +3719,7 @@ lock_table_names(THD *thd, const DDL_options_st &options, table= table->next_global) { if (table->mdl_request.type < MDL_SHARED_UPGRADABLE || + table->mdl_request.type == MDL_SHARED_READ_ONLY || table->open_type == OT_TEMPORARY_ONLY || (table->open_type == OT_TEMPORARY_OR_BASE && is_temporary_table(table))) { @@ -4448,6 +3848,11 @@ open_tables_check_upgradable_mdl(THD *thd, TABLE_LIST *tables_start, for (table= tables_start; table && table != tables_end; table= table->next_global) { + /* + Check below needs to be updated if this function starts + called for SRO locks. + */ + DBUG_ASSERT(table->mdl_request.type != MDL_SHARED_READ_ONLY); if (table->mdl_request.type < MDL_SHARED_UPGRADABLE || table->open_type == OT_TEMPORARY_ONLY || (table->open_type == OT_TEMPORARY_OR_BASE && is_temporary_table(table))) @@ -4485,6 +3890,7 @@ open_tables_check_upgradable_mdl(THD *thd, TABLE_LIST *tables_start, Open all tables in list @param[in] thd Thread context. + @param[in] options DDL options. @param[in,out] start List of tables to be open (it can be adjusted for statement that uses tables only implicitly, e.g. for "SELECT f1()"). @@ -4658,7 +4064,7 @@ restart: goto error; /* Re-open temporary tables after close_tables_for_reopen(). */ - if (open_temporary_tables(thd, *start)) + if (thd->open_temporary_tables(*start)) goto error; error= FALSE; @@ -4720,7 +4126,7 @@ restart: goto error; /* Re-open temporary tables after close_tables_for_reopen(). */ - if (open_temporary_tables(thd, *start)) + if (thd->open_temporary_tables(*start)) goto error; error= FALSE; @@ -4872,6 +4278,7 @@ 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) { @@ -4949,11 +4356,12 @@ handle_table(THD *thd, Query_tables_list *prelocking_ctx, while ((fk= fk_list_it++)) { // FK_OPTION_RESTRICT and FK_OPTION_NO_ACTION only need read access + static bool can_write[]= { true, false, true, true, false, true }; 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))) + if ((op & (1 << TRG_EVENT_DELETE) && can_write[fk->delete_method]) + || (op & (1 << TRG_EVENT_UPDATE) && can_write[fk->update_method])) lock_type= TL_WRITE_ALLOW_WRITE; else lock_type= TL_READ; @@ -5162,6 +4570,13 @@ static bool check_lock_and_start_stmt(THD *thd, table_list->table->file->print_error(error, MYF(0)); DBUG_RETURN(1); } + + /* + Record in transaction state tracking + */ + TRANSACT_TRACKER(add_trx_state(thd, lock_type, + table_list->table->file->has_transactions())); + DBUG_RETURN(0); } @@ -5261,7 +4676,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type, bool error; DBUG_ENTER("open_ltable"); - /* Ignore temporary tables as they have already ben opened*/ + /* Ignore temporary tables as they have already been opened. */ if (table_list->table) DBUG_RETURN(table_list->table); @@ -5348,8 +4763,9 @@ end: Open all tables in list, locks them and optionally process derived tables. @param thd Thread context. + @param options DDL options. @param tables List of tables for open and locking. - @param derived If to handle derived tables. + @param derived Whether to handle derived tables. @param flags Bitmap of options to be used to open and lock tables (see open_tables() and mysql_lock_tables() for details). @@ -5494,6 +4910,45 @@ static void mark_real_tables_as_free_for_reuse(TABLE_LIST *table_list) } +static bool fix_all_session_vcol_exprs(THD *thd, TABLE_LIST *tables) +{ + Security_context *save_security_ctx= thd->security_ctx; + TABLE_LIST *first_not_own= thd->lex->first_not_own_table(); + DBUG_ENTER("fix_session_vcol_expr"); + + for (TABLE_LIST *table= tables; table && table != first_not_own; + table= table->next_global) + { + TABLE *t= table->table; + if (!table->placeholder() && t->s->vcols_need_refixing && + table->lock_type >= TL_WRITE_ALLOW_WRITE) + { + if (table->security_ctx) + thd->security_ctx= table->security_ctx; + + for (Field **vf= t->vfield; vf && *vf; vf++) + if (fix_session_vcol_expr(thd, (*vf)->vcol_info)) + goto err; + + for (Field **df= t->default_field; df && *df; df++) + if ((*df)->default_value && + fix_session_vcol_expr(thd, (*df)->default_value)) + goto err; + + for (Virtual_column_info **cc= t->check_constraints; cc && *cc; cc++) + if (fix_session_vcol_expr(thd, (*cc))) + goto err; + + thd->security_ctx= save_security_ctx; + } + } + DBUG_RETURN(0); +err: + thd->security_ctx= save_security_ctx; + DBUG_RETURN(1); +} + + /** Lock all tables in a list. @@ -5655,7 +5110,11 @@ bool lock_tables(THD *thd, TABLE_LIST *tables, uint count, } } - DBUG_RETURN(thd->decide_logging_format(tables)); + bool res= fix_all_session_vcol_exprs(thd, tables); + if (!res) + res= thd->decide_logging_format(tables); + + DBUG_RETURN(res); } @@ -5744,176 +5203,6 @@ void close_tables_for_reopen(THD *thd, TABLE_LIST **tables, } -/** - Open a single table without table caching and don't add it to - THD::open_tables. Depending on the 'add_to_temporary_tables_list' value, - the opened TABLE instance will be addded to THD::temporary_tables list. - - @param thd Thread context. - @param hton Storage engine of the table, if known, - or NULL otherwise. - @param frm frm image - @param path Path (without .frm) - @param db Database name. - @param table_name Table name. - @param add_to_temporary_tables_list Specifies if the opened TABLE - instance should be linked into - THD::temporary_tables list. - @param open_in_engine Indicates that we need to open table - in storage engine in addition to - constructing TABLE object for it. - - @note This function is used: - - by alter_table() to open a temporary table; - - when creating a temporary table with CREATE TEMPORARY TABLE. - - @return TABLE instance for opened table. - @retval NULL on error. -*/ - -TABLE *open_table_uncached(THD *thd, handlerton *hton, - LEX_CUSTRING *frm, - const char *path, const char *db, - const char *table_name, - bool add_to_temporary_tables_list, - bool open_in_engine) -{ - TABLE *tmp_table; - TABLE_SHARE *share; - char cache_key[MAX_DBKEY_LENGTH], *saved_cache_key, *tmp_path; - uint key_length; - DBUG_ENTER("open_table_uncached"); - DBUG_PRINT("enter", - ("table: '%s'.'%s' path: '%s' server_id: %u " - "pseudo_thread_id: %lu", - db, table_name, path, - (uint) thd->variables.server_id, - (ulong) thd->variables.pseudo_thread_id)); - - if (add_to_temporary_tables_list) - { - /* Temporary tables are not safe for parallel replication. */ - if (thd->rgi_slave && thd->rgi_slave->is_parallel_exec && - thd->wait_for_prior_commit()) - DBUG_RETURN(NULL); - } - - /* Create the cache_key for temporary tables */ - key_length= create_tmp_table_def_key(thd, cache_key, db, table_name); - - if (!(tmp_table= (TABLE*) my_malloc(sizeof(*tmp_table) + sizeof(*share) + - strlen(path)+1 + key_length, - MYF(MY_WME)))) - DBUG_RETURN(0); /* purecov: inspected */ - - share= (TABLE_SHARE*) (tmp_table+1); - tmp_path= (char*) (share+1); - saved_cache_key= strmov(tmp_path, path)+1; - memcpy(saved_cache_key, cache_key, key_length); - - init_tmp_table_share(thd, share, saved_cache_key, key_length, - strend(saved_cache_key)+1, tmp_path); - share->db_plugin= ha_lock_engine(thd, hton); - - /* - Use the frm image, if possible, open the file otherwise. - - The image might be unavailable in ALTER TABLE, when the discovering - engine took over the ownership (see TABLE::read_frm_image). - */ - int res= frm->str - ? share->init_from_binary_frm_image(thd, false, frm->str, frm->length) - : open_table_def(thd, share, GTS_TABLE | GTS_USE_DISCOVERY); - - if (res) - { - /* No need to lock share->mutex as this is not needed for tmp tables */ - free_table_share(share); - my_free(tmp_table); - DBUG_RETURN(0); - } - - share->m_psi= PSI_CALL_get_table_share(true, share); - - if (open_table_from_share(thd, share, table_name, - open_in_engine ? - (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE | - HA_GET_INDEX) : 0, - READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD, - ha_open_options, - tmp_table, - /* - Set "is_create_table" if the table does not - exist in SE - */ - open_in_engine ? false : true)) - { - /* No need to lock share->mutex as this is not needed for tmp tables */ - free_table_share(share); - my_free(tmp_table); - DBUG_RETURN(0); - } - - tmp_table->reginfo.lock_type= TL_WRITE; // Simulate locked - tmp_table->grant.privilege= TMP_TABLE_ACLS; - share->tmp_table= (tmp_table->file->has_transactions() ? - TRANSACTIONAL_TMP_TABLE : NON_TRANSACTIONAL_TMP_TABLE); - - if (add_to_temporary_tables_list) - { - thd->lock_temporary_tables(); - /* growing temp list at the head */ - tmp_table->next= thd->temporary_tables; - if (tmp_table->next) - tmp_table->next->prev= tmp_table; - thd->temporary_tables= tmp_table; - thd->temporary_tables->prev= 0; - if (thd->rgi_slave) - { - thread_safe_increment32(&slave_open_temp_tables); - } - thd->unlock_temporary_tables(0); - } - tmp_table->pos_in_table_list= 0; - DBUG_PRINT("tmptable", ("opened table: '%s'.'%s' 0x%lx", tmp_table->s->db.str, - tmp_table->s->table_name.str, (long) tmp_table)); - DBUG_RETURN(tmp_table); -} - - -/** - Delete a temporary table. - - @param base Handlerton for table to be deleted. - @param path Path to the table to be deleted (i.e. path - to its .frm without an extension). - - @retval false - success. - @retval true - failure. -*/ - -bool rm_temporary_table(handlerton *base, const char *path) -{ - bool error=0; - handler *file; - char frm_path[FN_REFLEN + 1]; - DBUG_ENTER("rm_temporary_table"); - - strxnmov(frm_path, sizeof(frm_path) - 1, path, reg_ext, NullS); - if (mysql_file_delete(key_file_frm, frm_path, MYF(0))) - error=1; /* purecov: inspected */ - file= get_new_handler((TABLE_SHARE*) 0, current_thd->mem_root, base); - if (file && file->ha_delete_table(path)) - { - error=1; - sql_print_warning("Could not remove temporary table: '%s', error: %d", - path, my_errno); - } - delete file; - DBUG_RETURN(error); -} - - /***************************************************************************** * The following find_field_in_XXX procedures implement the core of the * name resolution functionality. The entry point to resolve a column name in a @@ -5942,7 +5231,6 @@ static void update_field_dependencies(THD *thd, Field *field, TABLE *table) */ table->covering_keys.intersect(field->part_of_key); - table->merge_keys.merge(field->part_of_key); if (field->vcol_info) table->mark_virtual_col(field); @@ -5982,164 +5270,6 @@ static void update_field_dependencies(THD *thd, Field *field, TABLE *table) } -/** - Find a temporary table specified by TABLE_LIST instance in the cache and - prepare its TABLE instance for use. - - This function tries to resolve this table in the list of temporary tables - of this thread. Temporary tables are thread-local and "shadow" base - tables with the same name. - - @note In most cases one should use open_temporary_tables() instead - of this call. - - @note One should finalize process of opening temporary table for table - list element by calling open_and_process_table(). This function - is responsible for table version checking and handling of merge - tables. - - @note We used to check global_read_lock before opening temporary tables. - However, that limitation was artificial and is removed now. - - @return Error status. - @retval FALSE On success. If a temporary table exists for the given - key, tl->table is set. - @retval TRUE On error. my_error() has been called. -*/ - -bool open_temporary_table(THD *thd, TABLE_LIST *tl) -{ - TABLE *table; - DBUG_ENTER("open_temporary_table"); - DBUG_PRINT("enter", ("table: '%s'.'%s'", tl->db, tl->table_name)); - - /* - Code in open_table() assumes that TABLE_LIST::table can - be non-zero only for pre-opened temporary tables. - */ - DBUG_ASSERT(tl->table == NULL); - - /* - This function should not be called for cases when derived or I_S - tables can be met since table list elements for such tables can - have invalid db or table name. - Instead open_temporary_tables() should be used. - */ - DBUG_ASSERT(!tl->derived && !tl->schema_table); - - if (tl->open_type == OT_BASE_ONLY || !thd->have_temporary_tables()) - { - DBUG_PRINT("info", ("skip_temporary is set or no temporary tables")); - DBUG_RETURN(FALSE); - } - - if (find_and_use_temporary_table(thd, tl, &table)) - DBUG_RETURN(TRUE); - if (!table) - { - if (tl->open_type == OT_TEMPORARY_ONLY && - tl->open_strategy == TABLE_LIST::OPEN_NORMAL) - { - my_error(ER_NO_SUCH_TABLE, MYF(0), tl->db, tl->table_name); - DBUG_RETURN(TRUE); - } - DBUG_RETURN(FALSE); - } - - /* - Temporary tables are not safe for parallel replication. They were - designed to be visible to one thread only, so have no table locking. - Thus there is no protection against two conflicting transactions - committing in parallel and things like that. - - So for now, anything that uses temporary tables will be serialised - with anything before it, when using parallel replication. - - ToDo: We might be able to introduce a reference count or something - on temp tables, and have slave worker threads wait for it to reach - zero before being allowed to use the temp table. Might not be worth - it though, as statement-based replication using temporary tables is - in any case rather fragile. - */ - if (thd->rgi_slave && thd->rgi_slave->is_parallel_exec && - thd->wait_for_prior_commit()) - DBUG_RETURN(true); - -#ifdef WITH_PARTITION_STORAGE_ENGINE - if (tl->partition_names) - { - /* Partitioned temporary tables is not supported. */ - DBUG_ASSERT(!table->part_info); - my_error(ER_PARTITION_CLAUSE_ON_NONPARTITIONED, MYF(0)); - DBUG_RETURN(true); - } -#endif - - if (table->query_id) - { - /* - We're trying to use the same temporary table twice in a query. - Right now we don't support this because a temporary table is always - represented by only one TABLE object in THD, and it can not be - cloned. Emit an error for an unsupported behaviour. - */ - - DBUG_PRINT("error", - ("query_id: %lu server_id: %u pseudo_thread_id: %lu", - (ulong) table->query_id, (uint) thd->variables.server_id, - (ulong) thd->variables.pseudo_thread_id)); - my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias.c_ptr()); - DBUG_RETURN(TRUE); - } - - table->query_id= thd->query_id; - thd->thread_specific_used= TRUE; - - tl->updatable= 1; // It is not derived table nor non-updatable VIEW. - tl->table= table; - - table->init(thd, tl); - - DBUG_PRINT("info", ("Using temporary table")); - DBUG_RETURN(FALSE); -} - - -/** - Pre-open temporary tables corresponding to table list elements. - - @note One should finalize process of opening temporary tables - by calling open_tables(). This function is responsible - for table version checking and handling of merge tables. - - @return Error status. - @retval FALSE On success. If a temporary tables exists for the - given element, tl->table is set. - @retval TRUE On error. my_error() has been called. -*/ - -bool open_temporary_tables(THD *thd, TABLE_LIST *tl_list) -{ - TABLE_LIST *first_not_own= thd->lex->first_not_own_table(); - DBUG_ENTER("open_temporary_tables"); - - for (TABLE_LIST *tl= tl_list; tl && tl != first_not_own; tl= tl->next_global) - { - if (tl->derived || tl->schema_table) - { - /* - Derived and I_S tables will be handled by a later call to open_tables(). - */ - continue; - } - - if (open_temporary_table(thd, tl)) - DBUG_RETURN(TRUE); - } - - DBUG_RETURN(FALSE); -} - /* Find a field by name in a view that uses merge algorithm. @@ -6169,8 +5299,8 @@ find_field_in_view(THD *thd, TABLE_LIST *table_list, { DBUG_ENTER("find_field_in_view"); DBUG_PRINT("enter", - ("view: '%s', field name: '%s', item name: '%s', ref 0x%lx", - table_list->alias, name, item_name, (ulong) ref)); + ("view: '%s', field name: '%s', item name: '%s', ref %p", + table_list->alias, name, item_name, ref)); Field_iterator_view field_it; field_it.set(table_list); Query_arena *arena= 0, backup; @@ -6214,9 +5344,9 @@ find_field_in_view(THD *thd, TABLE_LIST *table_list, } else { - item->set_name((*ref)->name, (*ref)->name_length, + item->set_name(thd, (*ref)->name, (*ref)->name_length, system_charset_info); - item->real_item()->set_name((*ref)->name, (*ref)->name_length, + item->real_item()->set_name(thd, (*ref)->name, (*ref)->name_length, system_charset_info); } } @@ -6271,8 +5401,8 @@ find_field_in_natural_join(THD *thd, TABLE_LIST *table_ref, const char *name, Field *UNINIT_VAR(found_field); Query_arena *UNINIT_VAR(arena), backup; DBUG_ENTER("find_field_in_natural_join"); - DBUG_PRINT("enter", ("field name: '%s', ref 0x%lx", - name, (ulong) ref)); + DBUG_PRINT("enter", ("field name: '%s', ref %p", + name, ref)); DBUG_ASSERT(table_ref->is_natural_join && table_ref->join_columns); DBUG_ASSERT(*actual_table == NULL); @@ -6313,9 +5443,9 @@ find_field_in_natural_join(THD *thd, TABLE_LIST *table_ref, const char *name, */ if (*ref && !(*ref)->is_autogenerated_name) { - item->set_name((*ref)->name, (*ref)->name_length, + item->set_name(thd, (*ref)->name, (*ref)->name_length, system_charset_info); - item->real_item()->set_name((*ref)->name, (*ref)->name_length, + item->real_item()->set_name(thd, (*ref)->name, (*ref)->name_length, system_charset_info); } if (register_tree_change && arena) @@ -6428,7 +5558,7 @@ find_field_in_table(THD *thd, TABLE *table, const char *name, uint length, if (field_ptr && *field_ptr) { - *cached_field_index_ptr= field_ptr - table->field; + *cached_field_index_ptr= (uint)(field_ptr - table->field); field= *field_ptr; } else @@ -6505,8 +5635,8 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list, DBUG_ASSERT(name); DBUG_ASSERT(item_name); DBUG_PRINT("enter", - ("table: '%s' field name: '%s' item name: '%s' ref 0x%lx", - table_list->alias, name, item_name, (ulong) ref)); + ("table: '%s' field name: '%s' item name: '%s' ref %p", + table_list->alias, name, item_name, ref)); /* Check that the table and database that qualify the current field name @@ -6621,9 +5751,9 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list, else { if (thd->mark_used_columns == MARK_COLUMNS_READ) - it->walk(&Item::register_field_in_read_map, 0, (uchar *) 0); + it->walk(&Item::register_field_in_read_map, 0, 0); else - it->walk(&Item::register_field_in_write_map, 0, (uchar *) 0); + it->walk(&Item::register_field_in_write_map, 0, 0); } } else @@ -6964,6 +6094,8 @@ find_field_in_tables(THD *thd, Item_ident *item, or as a field name without alias, or as a field hidden by alias, or ignoring alias) + limit How many items in the list to check + (if limit==0 then all items are to be checked) RETURN VALUES 0 Item is not found or item is not unique, @@ -6981,9 +6113,10 @@ Item **not_found_item= (Item**) 0x1; Item ** find_item_in_list(Item *find, List<Item> &items, uint *counter, find_item_error_report_type report_error, - enum_resolution_type *resolution) + enum_resolution_type *resolution, uint limit) { List_iterator<Item> li(items); + uint n_items= limit == 0 ? items.elements : limit; Item **found=0, **found_unaliased= 0, *item; const char *db_name=0; const char *field_name=0; @@ -7007,8 +6140,9 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter, db_name= ((Item_ident*) find)->db_name; } - for (uint i= 0; (item=li++); i++) + for (uint i= 0; i < n_items; i++) { + item= li++; if (field_name && (item->real_item()->type() == Item::FIELD_ITEM || ((item->type() == Item::REF_ITEM) && @@ -7448,7 +6582,6 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2, /* Mark field_1 used for table cache. */ bitmap_set_bit(table_1->read_set, field_1->field_index); table_1->covering_keys.intersect(field_1->part_of_key); - table_1->merge_keys.merge(field_1->part_of_key); } if (field_2) { @@ -7456,7 +6589,6 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2, /* Mark field_2 used for table cache. */ bitmap_set_bit(table_2->read_set, field_2->field_index); table_2->covering_keys.intersect(field_2->part_of_key); - table_2->merge_keys.merge(field_2->part_of_key); } if (using_fields != NULL) @@ -7901,13 +7033,15 @@ static bool setup_natural_join_row_types(THD *thd, int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields, List<Item> *sum_func_list, - uint wild_num) + uint wild_num, uint *hidden_bit_fields) { + if (!wild_num) + return(0); + Item *item; List_iterator<Item> it(fields); Query_arena *arena, backup; DBUG_ENTER("setup_wild"); - DBUG_ASSERT(wild_num != 0); /* Don't use arena if we are not in prepared statements or stored procedures @@ -7940,7 +7074,7 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields, else if (insert_fields(thd, ((Item_field*) item)->context, ((Item_field*) item)->db_name, ((Item_field*) item)->table_name, &it, - any_privileges)) + any_privileges, hidden_bit_fields)) { if (arena) thd->restore_active_arena(arena, &backup); @@ -7986,7 +7120,7 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields, ** Check that all given fields exists and fill struct with current data ****************************************************************************/ -bool setup_fields(THD *thd, Item **ref_pointer_array, +bool setup_fields(THD *thd, Ref_ptr_array ref_pointer_array, List<Item> &fields, enum_mark_columns mark_used_columns, List<Item> *sum_func_list, List<Item> *pre_fix, bool allow_sum_func) @@ -7998,7 +7132,7 @@ bool setup_fields(THD *thd, Item **ref_pointer_array, bool save_is_item_list_lookup; bool make_pre_fix= (pre_fix && (pre_fix->elements == 0)); DBUG_ENTER("setup_fields"); - DBUG_PRINT("enter", ("ref_pointer_array: %p", ref_pointer_array)); + DBUG_PRINT("enter", ("ref_pointer_array: %p", ref_pointer_array.array())); thd->mark_used_columns= mark_used_columns; DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns)); @@ -8020,8 +7154,11 @@ bool setup_fields(THD *thd, Item **ref_pointer_array, TODO: remove it when (if) we made one list for allfields and ref_pointer_array */ - if (ref_pointer_array) - bzero(ref_pointer_array, sizeof(Item *) * fields.elements); + if (!ref_pointer_array.is_null()) + { + DBUG_ASSERT(ref_pointer_array.size() >= fields.elements); + memset(ref_pointer_array.array(), 0, sizeof(Item *) * fields.elements); + } /* We call set_entry() there (before fix_fields() of the whole list of field @@ -8039,7 +7176,7 @@ bool setup_fields(THD *thd, Item **ref_pointer_array, while ((var= li++)) var->set_entry(thd, FALSE); - Item **ref= ref_pointer_array; + Ref_ptr_array ref= ref_pointer_array; thd->lex->current_select->cur_pos_in_select_list= 0; while ((item= it++)) { @@ -8055,12 +7192,23 @@ bool setup_fields(THD *thd, Item **ref_pointer_array, DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns)); DBUG_RETURN(TRUE); /* purecov: inspected */ } - if (ref) - *(ref++)= item; - if (item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM && - sum_func_list) + if (!ref.is_null()) + { + ref[0]= item; + ref.pop_front(); + } + /* + split_sum_func() must be called for Window Function items, see + Item_window_func::split_sum_func. + */ + if (sum_func_list && + ((item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM) || + item->with_window_func)) + { item->split_sum_func(thd, ref_pointer_array, *sum_func_list, SPLIT_SUM_SELECT); + } + thd->lex->current_select->select_list_tables|= item->used_tables(); thd->lex->used_tables|= item->used_tables(); thd->lex->current_select->cur_pos_in_select_list++; } @@ -8391,13 +7539,13 @@ bool get_key_map_from_key_list(key_map *map, TABLE *table, bool insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, const char *table_name, List_iterator<Item> *it, - bool any_privileges) + bool any_privileges, uint *hidden_bit_fields) { Field_iterator_table_ref field_iterator; bool found; char name_buff[SAFE_NAME_LEN+1]; DBUG_ENTER("insert_fields"); - DBUG_PRINT("arena", ("stmt arena: 0x%lx", (ulong)thd->stmt_arena)); + DBUG_PRINT("arena", ("stmt arena: %p",thd->stmt_arena)); if (db_name && lower_case_table_names) { @@ -8479,7 +7627,10 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, views and natural joins this update is performed inside the loop below. */ if (table) + { thd->lex->used_tables|= table->map; + thd->lex->current_select->select_list_tables|= table->map; + } /* Initialize a generic field iterator for the current table reference. @@ -8508,6 +7659,9 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, else it->after(item); /* Add 'item' to the SELECT list. */ + if (item->type() == Item::FIELD_ITEM && item->field_type() == MYSQL_TYPE_BIT) + (*hidden_bit_fields)++; + #ifndef NO_EMBEDDED_ACCESS_CHECKS /* Set privilege information for the fields of newly created views. @@ -8517,7 +7671,7 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, temporary table. Thus in this case we can be sure that 'item' is an Item_field. */ - if (any_privileges) + if (any_privileges && !tables->is_with_table() && !tables->is_derived()) { DBUG_ASSERT((tables->field_translation == NULL && table) || tables->is_natural_join); @@ -8554,7 +7708,6 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, if (table) { table->covering_keys.intersect(field->part_of_key); - table->merge_keys.merge(field->part_of_key); } if (tables->is_natural_join) { @@ -8571,8 +7724,9 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, if (field_table) { thd->lex->used_tables|= field_table->map; + thd->lex->current_select->select_list_tables|= + field_table->map; field_table->covering_keys.intersect(field->part_of_key); - field_table->merge_keys.merge(field->part_of_key); field_table->used_fields++; } } @@ -8819,11 +7973,14 @@ err_no_arena: @param fields Item_fields list to be filled @param values values to fill with @param ignore_errors TRUE if we should ignore errors + @param update TRUE if update query @details fill_record() may set table->auto_increment_field_not_null and a caller should make sure that it is reset after their last call to this function. + default functions are executed for inserts. + virtual fields are always updated @return Status @retval true An error occurred. @@ -8832,12 +7989,11 @@ err_no_arena: bool fill_record(THD *thd, TABLE *table_arg, List<Item> &fields, List<Item> &values, - bool ignore_errors) + bool ignore_errors, bool update) { List_iterator_fast<Item> f(fields),v(values); Item *value, *fld; Item_field *field; - TABLE *vcol_table= 0; bool save_abort_on_warning= thd->abort_on_warning; bool save_no_errors= thd->no_errors; DBUG_ENTER("fill_record"); @@ -8863,8 +8019,6 @@ fill_record(THD *thd, TABLE *table_arg, List<Item> &fields, List<Item> &values, table_arg->auto_increment_field_not_null= FALSE; f.rewind(); } - else if (thd->lex->unit.insert_table_with_stored_vcol) - vcol_table= thd->lex->unit.insert_table_with_stored_vcol; while ((fld= f++)) { @@ -8879,9 +8033,10 @@ fill_record(THD *thd, TABLE *table_arg, List<Item> &fields, List<Item> &values, if (table->next_number_field && rfield->field_index == table->next_number_field->field_index) table->auto_increment_field_not_null= TRUE; - if (rfield->vcol_info && - value->type() != Item::DEFAULT_VALUE_ITEM && - value->type() != Item::NULL_ITEM && + Item::Type type= value->type(); + if (rfield->vcol_info && + type != Item::DEFAULT_VALUE_ITEM && + type != Item::NULL_ITEM && table->s->table_category != TABLE_CATEGORY_TEMPORARY) { push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, @@ -8889,25 +8044,31 @@ fill_record(THD *thd, TABLE *table_arg, List<Item> &fields, List<Item> &values, ER_THD(thd, ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN), rfield->field_name, table->s->table_name.str); } - if ((!rfield->vcol_info || rfield->stored_in_db) && + if (rfield->stored_in_db() && (value->save_in_field(rfield, 0)) < 0 && !ignore_errors) { my_message(ER_UNKNOWN_ERROR, ER_THD(thd, ER_UNKNOWN_ERROR), MYF(0)); goto err; } - rfield->set_explicit_default(value); - DBUG_ASSERT(vcol_table == 0 || vcol_table == table); - vcol_table= table; + rfield->set_has_explicit_value(); } - /* Update virtual fields*/ - thd->abort_on_warning= FALSE; - if (vcol_table && vcol_table->vfield && - update_virtual_fields(thd, vcol_table, VCOL_UPDATE_FOR_WRITE)) + + if (update) + table_arg->evaluate_update_default_function(); + else + if (table_arg->default_field && + table_arg->update_default_fields(ignore_errors)) + goto err; + + /* Update virtual fields */ + if (table_arg->vfield && + table_arg->update_virtual_fields(table_arg->file, VCOL_UPDATE_FOR_WRITE)) goto err; thd->abort_on_warning= save_abort_on_warning; thd->no_errors= save_no_errors; DBUG_RETURN(thd->is_error()); err: + DBUG_PRINT("error",("got error")); thd->abort_on_warning= save_abort_on_warning; thd->no_errors= save_no_errors; if (fields.elements) @@ -8926,19 +8087,48 @@ void switch_to_nullable_trigger_fields(List<Item> &items, TABLE *table) { Field** field= table->field_to_fill(); + /* True if we have NOT NULL fields and BEFORE triggers */ if (field != table->field) { List_iterator_fast<Item> it(items); Item *item; while ((item= it++)) - item->walk(&Item::switch_to_nullable_fields_processor, 1, (uchar*)field); + item->walk(&Item::switch_to_nullable_fields_processor, 1, field); table->triggers->reset_extra_null_bitmap(); } } /** + Prepare Virtual fields and field with default expressions to use + trigger fields + + This means redirecting from table->field to + table->field_to_fill(), if needed. +*/ + +void switch_defaults_to_nullable_trigger_fields(TABLE *table) +{ + if (!table->default_field) + return; // no defaults + + Field **trigger_field= table->field_to_fill(); + + /* True if we have NOT NULL fields and BEFORE triggers */ + if (trigger_field != table->field) + { + for (Field **field_ptr= table->default_field; *field_ptr ; field_ptr++) + { + Field *field= (*field_ptr); + field->default_value->expr->walk(&Item::switch_to_nullable_fields_processor, 1, trigger_field); + *field_ptr= (trigger_field[field->field_index]); + } + } +} + + +/** Test NOT NULL constraint after BEFORE triggers */ static bool not_null_fields_have_null_values(TABLE *table) @@ -8992,36 +8182,37 @@ static bool not_null_fields_have_null_values(TABLE *table) */ bool -fill_record_n_invoke_before_triggers(THD *thd, TABLE *table, List<Item> &fields, +fill_record_n_invoke_before_triggers(THD *thd, TABLE *table, + List<Item> &fields, List<Item> &values, bool ignore_errors, enum trg_event_type event) { bool result; Table_triggers_list *triggers= table->triggers; - result= fill_record(thd, table, fields, values, ignore_errors); + result= fill_record(thd, table, fields, values, ignore_errors, + event == TRG_EVENT_UPDATE); if (!result && triggers) - result= triggers->process_triggers(thd, event, TRG_ACTION_BEFORE, TRUE) || - not_null_fields_have_null_values(table); - - /* - Re-calculate virtual fields to cater for cases when base columns are - updated by the triggers. - */ - if (!result && triggers) { - List_iterator_fast<Item> f(fields); - Item *fld; - Item_field *item_field; - if (fields.elements) + if (triggers->process_triggers(thd, event, TRG_ACTION_BEFORE, + TRUE) || + not_null_fields_have_null_values(table)) + return TRUE; + + /* + Re-calculate virtual fields to cater for cases when base columns are + updated by the triggers. + */ + if (table->vfield && fields.elements) { - fld= (Item_field*)f++; - item_field= fld->field_for_view_update(); - if (item_field && item_field->field && table && table->vfield) + Item *fld= (Item_field*) fields.head(); + Item_field *item_field= fld->field_for_view_update(); + if (item_field) { DBUG_ASSERT(table == item_field->field->table); - result= update_virtual_fields(thd, table, VCOL_UPDATE_FOR_WRITE); + result|= table->update_virtual_fields(table->file, + VCOL_UPDATE_FOR_WRITE); } } } @@ -9031,6 +8222,7 @@ fill_record_n_invoke_before_triggers(THD *thd, TABLE *table, List<Item> &fields, /** Fill the field buffer of a table with the values of an Item list + All fields are given a value @param thd thread handler @param table_arg the table that is being modified @@ -9088,15 +8280,18 @@ fill_record(THD *thd, TABLE *table, Field **ptr, List<Item> &values, value=v++; if (field->field_index == autoinc_index) table->auto_increment_field_not_null= TRUE; - if (field->vcol_info && - value->type() != Item::DEFAULT_VALUE_ITEM && - value->type() != Item::NULL_ITEM && - table->s->table_category != TABLE_CATEGORY_TEMPORARY) + if (field->vcol_info) { - push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, - ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN, - ER_THD(thd, ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN), - field->field_name, table->s->table_name.str); + Item::Type type= value->type(); + if (type != Item::DEFAULT_VALUE_ITEM && + type != Item::NULL_ITEM && + table->s->table_category != TABLE_CATEGORY_TEMPORARY) + { + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN, + ER_THD(thd, ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN), + field->field_name, table->s->table_name.str); + } } if (use_value) @@ -9104,12 +8299,12 @@ fill_record(THD *thd, TABLE *table, Field **ptr, List<Item> &values, else if (value->save_in_field(field, 0) < 0) goto err; - field->set_explicit_default(value); + field->set_has_explicit_value(); } - /* Update virtual fields*/ + /* Update virtual fields */ thd->abort_on_warning= FALSE; if (table->vfield && - update_virtual_fields(thd, table, VCOL_UPDATE_FOR_WRITE)) + table->update_virtual_fields(table->file, VCOL_UPDATE_FOR_WRITE)) goto err; thd->abort_on_warning= abort_on_warning_saved; DBUG_RETURN(thd->is_error()); @@ -9163,7 +8358,7 @@ fill_record_n_invoke_before_triggers(THD *thd, TABLE *table, Field **ptr, { DBUG_ASSERT(table == (*ptr)->table); if (table->vfield) - result= update_virtual_fields(thd, table, VCOL_UPDATE_FOR_WRITE); + result= table->update_virtual_fields(table->file, VCOL_UPDATE_FOR_WRITE); } return result; @@ -9180,7 +8375,7 @@ my_bool mysql_rm_tmp_tables(void) THD *thd; DBUG_ENTER("mysql_rm_tmp_tables"); - if (!(thd= new THD)) + if (!(thd= new THD(0))) DBUG_RETURN(1); thd->thread_stack= (char*) &thd; thd->store_globals(); @@ -9271,7 +8466,6 @@ int init_ftfuncs(THD *thd, SELECT_LEX *select_lex, bool no_order) DBUG_PRINT("info",("Performing FULLTEXT search")); while ((ifm=li++)) -#if MYSQL_VERSION_ID < 100213 if (unlikely(!ifm->fixed)) /* it mean that clause where was FT function was removed, so we have @@ -9279,7 +8473,6 @@ int init_ftfuncs(THD *thd, SELECT_LEX *select_lex, bool no_order) */ li.remove(); else -#endif ifm->init_search(thd, no_order); } return 0; |