diff options
Diffstat (limited to 'sql/sql_base.cc')
-rw-r--r-- | sql/sql_base.cc | 207 |
1 files changed, 121 insertions, 86 deletions
diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 3f0c1ee8a80..d25057789ac 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -345,26 +345,9 @@ TABLE_SHARE *get_table_share(THD *thd, TABLE_LIST *table_list, char *key, if (!(share= alloc_table_share(table_list, key, key_length))) { -#ifdef WAITING_FOR_TABLE_DEF_CACHE_STAGE_3 - pthread_mutex_unlock(&LOCK_open); -#endif DBUG_RETURN(0); } -#ifdef WAITING_FOR_TABLE_DEF_CACHE_STAGE_3 - // We need a write lock to be able to add a new entry - pthread_mutex_unlock(&LOCK_open); - pthread_mutex_lock(&LOCK_open); - /* Check that another thread didn't insert the same table in between */ - if ((old_share= hash_search(&table_def_cache, (uchar*) key, key_length))) - { - (void) pthread_mutex_lock(&share->mutex); - free_table_share(share); - share= old_share; - goto found; - } -#endif - /* Lock mutex to be able to read table definition from file without conflicts @@ -388,29 +371,11 @@ TABLE_SHARE *get_table_share(THD *thd, TABLE_LIST *table_list, char *key, if (my_hash_insert(&table_def_cache, (uchar*) share)) { -#ifdef WAITING_FOR_TABLE_DEF_CACHE_STAGE_3 - pthread_mutex_unlock(&LOCK_open); - (void) pthread_mutex_unlock(&share->mutex); -#endif free_table_share(share); DBUG_RETURN(0); // return error } -#ifdef WAITING_FOR_TABLE_DEF_CACHE_STAGE_3 - pthread_mutex_unlock(&LOCK_open); -#endif if (open_table_def(thd, share, db_flags)) { -#ifdef WAITING_FOR_TABLE_DEF_CACHE_STAGE_3 - /* - No such table or wrong table definition file - Lock first the table cache and then the mutex. - This will ensure that no other thread is using the share - structure. - */ - (void) pthread_mutex_unlock(&share->mutex); - (void) pthread_mutex_lock(&LOCK_open); - (void) pthread_mutex_lock(&share->mutex); -#endif *error= share->error; (void) hash_delete(&table_def_cache, (uchar*) share); DBUG_RETURN(0); @@ -429,9 +394,6 @@ found: /* We must do a lock to ensure that the structure is initialized */ (void) pthread_mutex_lock(&share->mutex); -#ifdef WAITING_FOR_TABLE_DEF_CACHE_STAGE_3 - pthread_mutex_unlock(&LOCK_open); -#endif if (share->error) { /* Table definition contained an error */ @@ -618,52 +580,6 @@ void release_table_share(TABLE_SHARE *share, enum release_type type) } pthread_mutex_unlock(&share->mutex); DBUG_VOID_RETURN; - - -#ifdef WAITING_FOR_TABLE_DEF_CACHE_STAGE_3 - if (to_be_deleted) - { - /* - We must try again with new locks as we must get LOCK_open - before share->mutex - */ - pthread_mutex_unlock(&share->mutex); - pthread_mutex_lock(&LOCK_open); - pthread_mutex_lock(&share->mutex); - if (!share->ref_count) - { // No one is using this now - TABLE_SHARE *name_lock; - if (share->replace_with_name_lock && (name_lock=get_name_lock(share))) - { - /* - This code is execured when someone does FLUSH TABLES while on has - locked tables. - */ - (void) hash_search(&def_cache,(uchar*) key,key_length); - hash_replace(&def_cache, def_cache.current_record,(uchar*) name_lock); - } - else - { - /* Remove table definition */ - hash_delete(&def_cache,(uchar*) share); - } - pthread_mutex_unlock(&LOCK_open); - free_table_share(share); - } - else - { - pthread_mutex_unlock(&LOCK_open); - if (type == RELEASE_WAIT_FOR_DROP) - wait_for_table(share, "Waiting for close"); - else - pthread_mutex_unlock(&share->mutex); - } - } - else if (type == RELEASE_WAIT_FOR_DROP) - wait_for_table(share, "Waiting for close"); - else - pthread_mutex_unlock(&share->mutex); -#endif } @@ -3801,6 +3717,73 @@ void assign_new_table_id(TABLE_SHARE *share) DBUG_VOID_RETURN; } + +/** + Compare metadata versions of an element obtained from the table + definition cache and its corresponding node in the parse tree. + + @details If the new and the old values mismatch, invoke + Metadata_version_observer. + At prepared statement prepare, all TABLE_LIST version values are + NULL and we always have a mismatch. But there is no observer set + in THD, and therefore no error is reported. Instead, we update + the value in the parse tree, effectively recording the original + version. + At prepared statement execute, an observer may be installed. If + there is a version mismatch, we push an error and return TRUE. + + For conventional execution (no prepared statements), the + observer is never installed. + + @sa Execute_observer + @sa check_prepared_statement() to see cases when an observer is installed + @sa TABLE_LIST::is_table_ref_id_equal() + @sa TABLE_SHARE::get_table_ref_id() + + @param[in] thd used to report errors + @param[in,out] tables TABLE_LIST instance created by the parser + Metadata version information in this object + is updated upon success. + @param[in] table_share an element from the table definition cache + + @retval TRUE an error, which has been reported + @retval FALSE success, version in TABLE_LIST has been updated +*/ + +bool +check_and_update_table_version(THD *thd, + TABLE_LIST *tables, TABLE_SHARE *table_share) +{ + if (! tables->is_table_ref_id_equal(table_share)) + { + if (thd->m_reprepare_observer && + thd->m_reprepare_observer->report_error(thd)) + { + /* + Version of the table share is different from the + previous execution of the prepared statement, and it is + unacceptable for this SQLCOM. Error has been reported. + */ + DBUG_ASSERT(thd->is_error()); + return TRUE; + } + /* Always maintain the latest version and type */ + tables->set_table_ref_id(table_share); + } + +#ifndef DBUG_OFF + /* Spuriously reprepare each statement. */ + if (_db_strict_keyword_("reprepare_each_statement") && + thd->m_reprepare_observer && thd->stmt_arena->is_reprepared == FALSE) + { + thd->m_reprepare_observer->report_error(thd); + return TRUE; + } +#endif + + return FALSE; +} + /* Load a table definition from file and open unireg table @@ -3846,6 +3829,12 @@ retry: if (share->is_view) { + /* + This table is a view. Validate its metadata version: in particular, + that it was a view when the statement was prepared. + */ + if (check_and_update_table_version(thd, table_list, share)) + goto err; if (table_list->i_s_requested_object & OPEN_TABLE_ONLY) goto err; @@ -3863,6 +3852,26 @@ retry: release_table_share(share, RELEASE_NORMAL); DBUG_RETURN((flags & OPEN_VIEW_NO_PARSE)? -1 : 0); } + else if (table_list->view) + { + /* + We're trying to open a table for what was a view. + This can only happen during (re-)execution. + At prepared statement prepare the view has been opened and + merged into the statement parse tree. After that, someone + performed a DDL and replaced the view with a base table. + Don't try to open the table inside a prepared statement, + invalidate it instead. + + Note, the assert below is known to fail inside stored + procedures (Bug#27011). + */ + DBUG_ASSERT(thd->m_reprepare_observer); + check_and_update_table_version(thd, table_list, share); + /* Always an error. */ + DBUG_ASSERT(thd->is_error()); + goto err; + } if (table_list->i_s_requested_object & OPEN_VIEW_ONLY) goto err; @@ -4367,6 +4376,11 @@ bool fix_merge_after_open(TABLE_LIST *old_child_list, TABLE_LIST **old_last, prelocking it won't do such precaching and will simply reuse table list which is already built. + If any table has a trigger and start->trg_event_map is non-zero + the final lock will end up in thd->locked_tables, otherwise, the + lock will be placed in thd->lock. See also comments in + st_lex::set_trg_event_type_for_tables(). + RETURN 0 - OK -1 - error @@ -4459,8 +4473,18 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) */ if (tables->schema_table) { - if (!mysql_schema_table(thd, thd->lex, tables)) + /* + If this information_schema table is merged into a mergeable + view, ignore it for now -- it will be filled when its respective + TABLE_LIST is processed. This code works only during re-execution. + */ + if (tables->view) + goto process_view_routines; + if (!mysql_schema_table(thd, thd->lex, tables) && + !check_and_update_table_version(thd, tables, tables->table->s)) + { continue; + } DBUG_RETURN(-1); } (*counter)++; @@ -4579,7 +4603,7 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) process its triggers since they never will be activated. */ if (!thd->prelocked_mode && !thd->lex->requires_prelocking() && - tables->table->triggers && + tables->trg_event_map && tables->table->triggers && tables->lock_type >= TL_WRITE_ALLOW_WRITE) { if (!query_tables_last_own) @@ -4608,6 +4632,13 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) } tables->table->grant= tables->grant; + /* Check and update metadata version of a base table. */ + if (check_and_update_table_version(thd, tables, tables->table->s)) + { + result= -1; + goto err; + } + /* Attach MERGE children if not locked already. */ DBUG_PRINT("tcache", ("is parent: %d is child: %d", test(tables->table->child_l), @@ -4666,7 +4697,11 @@ process_view_routines: error happens on a MERGE child, clear the parents TABLE reference. */ if (tables->parent_l) + { + if (tables->parent_l->next_global == tables->parent_l->table->child_l) + tables->parent_l->next_global= *tables->parent_l->table->child_last_l; tables->parent_l->table= NULL; + } tables->table= NULL; } DBUG_PRINT("tcache", ("returning: %d", result)); |