diff options
author | Michael Widenius <monty@askmonty.org> | 2011-12-11 11:34:44 +0200 |
---|---|---|
committer | Michael Widenius <monty@askmonty.org> | 2011-12-11 11:34:44 +0200 |
commit | 6d4224a31c9d32c8f8067a4f7d16daa29bcdee6b (patch) | |
tree | 79e3143528495069ad232f673532573b30afe425 /sql/sql_base.cc | |
parent | 3e2cb35e11cb5ee6668d538a62a3b32e017944a5 (diff) | |
parent | 701c0f822abe4ee9eeafd244fa30dc2fcf067b81 (diff) | |
download | mariadb-git-6d4224a31c9d32c8f8067a4f7d16daa29bcdee6b.tar.gz |
Merge with 5.2.
no_error handling for select (used by INSERT ... SELECT) still needs to be fixed, but I will do that in a separate commit
Diffstat (limited to 'sql/sql_base.cc')
-rw-r--r-- | sql/sql_base.cc | 223 |
1 files changed, 163 insertions, 60 deletions
diff --git a/sql/sql_base.cc b/sql/sql_base.cc index dded0105be7..c625aaab9a0 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -98,6 +98,13 @@ static TABLE_SHARE *oldest_unused_share, end_of_unused_share; static pthread_mutex_t LOCK_table_share; static bool table_def_inited= 0; +/** + Dummy TABLE instance which is used in reopen_tables() and reattach_merge() + functions to mark MERGE tables and their children with which there is some + kind of problem and which therefore we need to close. +*/ +static TABLE bad_merge_marker; + static int open_unireg_entry(THD *thd, TABLE *entry, TABLE_LIST *table_list, const char *alias, char *cache_key, uint cache_key_length, @@ -786,6 +793,7 @@ void intern_close_table(TABLE *table) delete table->triggers; if (table->file) // Not true if name lock VOID(closefrm(table, 1)); // close file + table->alias.free(); DBUG_VOID_RETURN; } @@ -2303,7 +2311,12 @@ bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list, bool link_in) if (thd->killed || !table) DBUG_RETURN(TRUE); - orig_table= *table; + /* + make a copy. we may need to restore it later. + don't use orig_table=*table, because we need an exact replica, + not a C++ copy that may modify the data in the copy constructor. + */ + memcpy(&orig_table, table, sizeof(*table)); if (open_unireg_entry(thd, table, table_list, table_name, table->s->table_cache_key.str, @@ -2316,7 +2329,8 @@ bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list, bool link_in) properly release name-lock in this case we should restore this object to its original state. */ - *table= orig_table; + memcpy(table, &orig_table, sizeof(*table)); + bzero(&orig_table, sizeof(orig_table)); // Ensure alias is not freed DBUG_RETURN(TRUE); } @@ -3258,46 +3272,65 @@ void close_data_files_and_morph_locks(THD *thd, const char *db, /** + @brief Mark merge parent and children with bad_merge_marker + + @param[in,out] parent the TABLE object of the parent +*/ + +static void mark_merge_parent_and_children_as_bad(TABLE *parent) +{ + TABLE_LIST *child_l; + DBUG_ENTER("mark_merge_parent_and_children_as_bad"); + parent->parent= &bad_merge_marker; + for (child_l= parent->child_l; ; child_l= child_l->next_global) + { + child_l->table->parent= &bad_merge_marker; + child_l->table= NULL; + if (&child_l->next_global == parent->child_last_l) + break; + } + DBUG_VOID_RETURN; +} + + +/** Reattach MERGE children after reopen. @param[in] thd thread context - @param[in,out] err_tables_p pointer to pointer of tables in error + + @note If reattach failed for certain MERGE table, the table (and all + it's children) are marked with bad_merge_marker. @return status - @retval FALSE OK, err_tables_p unchanged - @retval TRUE Error, err_tables_p contains table(s) + @retval FALSE OK + @retval TRUE Error */ -static bool reattach_merge(THD *thd, TABLE **err_tables_p) +static bool reattach_merge(THD *thd) { TABLE *table; - TABLE *next; - TABLE **prv_p= &thd->open_tables; bool error= FALSE; DBUG_ENTER("reattach_merge"); - for (table= thd->open_tables; table; table= next) + for (table= thd->open_tables; table; table= table->next) { - next= table->next; - DBUG_PRINT("tcache", ("check table: '%s'.'%s' 0x%lx next: 0x%lx", + DBUG_PRINT("tcache", ("check table: '%s'.'%s' 0x%lx", table->s->db.str, table->s->table_name.str, - (long) table, (long) next)); - /* Reattach children for MERGE tables with "closed data files" only. */ - if (table->child_l && !table->children_attached) + (long) table)); + /* + Reattach children only for MERGE tables that had children or parent + with "closed data files" and were reopen. For extra safety skip MERGE + tables which we failed to reopen (should not happen with current code). + */ + if (table->child_l && table->parent != &bad_merge_marker && + !table->children_attached) { DBUG_PRINT("tcache", ("MERGE parent, attach children")); - if(table->file->extra(HA_EXTRA_ATTACH_CHILDREN)) + if (table->file->extra(HA_EXTRA_ATTACH_CHILDREN)) { my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias.c_ptr()); error= TRUE; - /* Remove table from open_tables. */ - *prv_p= next; - if (next) - prv_p= &next->next; - /* Stack table on error list. */ - table->next= *err_tables_p; - *err_tables_p= table; - continue; + mark_merge_parent_and_children_as_bad(table); } else { @@ -3307,7 +3340,6 @@ static bool reattach_merge(THD *thd, TABLE **err_tables_p) table->s->table_name.str, (long) table)); } } - prv_p= &table->next; } DBUG_RETURN(error); } @@ -3337,13 +3369,11 @@ bool reopen_tables(THD *thd, bool get_locks, bool mark_share_as_old) { TABLE *table,*next,**prev; TABLE **tables,**tables_ptr; // For locks - TABLE *err_tables= NULL; bool error=0, not_used; bool merge_table_found= FALSE; const uint flags= MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN | MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK | MYSQL_LOCK_IGNORE_FLUSH; - DBUG_ENTER("reopen_tables"); if (!thd->open_tables) @@ -3371,32 +3401,68 @@ bool reopen_tables(THD *thd, bool get_locks, bool mark_share_as_old) for (table=thd->open_tables; table ; table=next) { uint db_stat=table->db_stat; + TABLE *parent= table->child_l ? table : table->parent; next=table->next; DBUG_PRINT("tcache", ("open table: '%s'.'%s' 0x%lx " "parent: 0x%lx db_stat: %u", table->s->db.str, table->s->table_name.str, (long) table, (long) table->parent, db_stat)); - if (table->child_l && !db_stat) + /* + If we need to reopen child or parent table in a MERGE table, then + children in this MERGE table has to be already detached at this + point. + */ + DBUG_ASSERT(db_stat || !parent || !parent->children_attached); + /* + Thanks to the above assumption the below condition will guarantee that + merge_table_found is TRUE when we need to reopen child or parent table. + Note that it works even in situation when it is only a child and not a + parent that needs reopen (this can happen when get_locks == FALSE). + */ + if (table->child_l && !table->children_attached) merge_table_found= TRUE; - if (!tables || (!db_stat && reopen_table(table))) + if (!tables) { - my_error(ER_CANT_REOPEN_TABLE, MYF(0), - table->alias.ptr() ? table->alias.c_ptr() : - table->s->table_name.str); /* - If we could not allocate 'tables', we may close open tables - here. If a MERGE table is affected, detach the children first. - It is not necessary to clear the child or parent table reference - of this table because the TABLE is freed. But we need to clear - the child or parent references of the other belonging tables so - that they cannot be moved into the unused_tables chain with - these pointers set. + If we could not allocate 'tables' we close ALL open tables here. + Before closing MERGE child or parent we need to detach children + and/or clear references in/to them. */ - unlink_open_table(thd, table, 0); - /* Restart loop */ - prev= &thd->open_tables; - next= *prev; - error=1; + if (parent) + detach_merge_children(table, TRUE); + } + else if (table->parent == &bad_merge_marker) + { + /* + This is either a child or a parent of a MERGE table for which + we already decided that we are unable to reopen it. Close it. + + Reset parent reference, it may be used while freeing the table. + */ + table->parent= NULL; + } + else if (!db_stat && reopen_table(table)) + { + /* + If we fail to reopen a child or a parent in a MERGE table and the + MERGE table is affected for the first time, mark all relevant tables + invalid. Otherwise handle it as usual. + + All in all we must end up with: + - child tables are detached from parent. This was done earlier, + but child<->parent references were kept valid for reopen. + - parent is not in the to-be-locked tables + - all child tables and parent are not in the THD::open_tables. + - all child tables and parent are not in the open_cache. + + Please note that below we do additional pass through THD::open_tables + list to achieve the last three points. + */ + if (parent) + { + mark_merge_parent_and_children_as_bad(parent); + table->parent= NULL; + } } else { @@ -3412,21 +3478,59 @@ bool reopen_tables(THD *thd, bool get_locks, bool mark_share_as_old) table->s->version=0; table->open_placeholder= 0; } + continue; } + my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias.c_ptr()); + unlink_open_table(thd, table, 0); + /* Restart loop, as one of the used tables may now be closed */ + prev= &thd->open_tables; + next= *prev; + error=1; } *prev=0; /* When all tables are open again, we can re-attach MERGE children to - their parents. All TABLE objects are still present. + their parents. + + If there was an error while reopening a child or a parent of a MERGE + table, or while reattaching child tables to their parents, some tables + may have been kept open but marked for close with bad_merge_marker. + Close these tables now. */ - DBUG_PRINT("tcache", ("re-attaching MERGE tables: %d", merge_table_found)); - if (!error && merge_table_found && reattach_merge(thd, &err_tables)) + if (tables && merge_table_found && (error|= reattach_merge(thd))) { - while (err_tables) + prev= &thd->open_tables; + for (table= thd->open_tables; table; table= next) { - VOID(hash_delete(&open_cache, (uchar*) err_tables)); - err_tables= err_tables->next; + next= table->next; + if (table->parent == &bad_merge_marker) + { + /* Remove merge parent from to-be-locked tables array. */ + if (get_locks && table->child_l) + { + TABLE **t; + for (t= tables; t < tables_ptr; t++) + { + if (*t == table) + { + tables_ptr--; + memmove(t, t + 1, (tables_ptr - t) * sizeof(TABLE *)); + break; + } + } + } + /* Reset parent reference, it may be used while freeing the table. */ + table->parent= NULL; + /* Free table. */ + VOID(hash_delete(&open_cache, (uchar *) table)); + } + else + { + *prev= table; + prev= &table->next; + } } + *prev= 0; } DBUG_PRINT("tcache", ("open tables to lock: %u", (uint) (tables_ptr - tables))); @@ -3579,8 +3683,7 @@ bool table_is_used(TABLE *table, bool wait_for_name_lock) char *key= table->s->table_cache_key.str; uint key_length= table->s->table_cache_key.length; - DBUG_PRINT("loop", ("table_name: %s", - table->alias.ptr() ? table->alias.c_ptr() : "")); + DBUG_PRINT("loop", ("table_name: %s.%s", key, strend(key)+1)); HASH_SEARCH_STATE state; for (TABLE *search= (TABLE*) hash_first(&open_cache, (uchar*) key, key_length, &state); @@ -4066,7 +4169,7 @@ retry: { /* Give right error message */ thd->clear_error(); - my_error(ER_NOT_KEYFILE, MYF(0), share->table_name.str, my_errno); + my_error(ER_NOT_KEYFILE, MYF(0), share->table_name.str); sql_print_error("Couldn't repair table: %s.%s", share->db.str, share->table_name.str); if (entry->file) @@ -4627,9 +4730,6 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) */ for (tables= *start; tables ;tables= tables->next_global) { - DBUG_PRINT("tcache", ("opening table: '%s'.'%s' item: 0x%lx", - tables->db, tables->table_name, (long) tables)); - safe_to_ignore_table= FALSE; /* @@ -4642,8 +4742,11 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) { if (tables->view) goto process_view_routines; + DBUG_PRINT("tcache", ("ignoring placeholder for derived table")); continue; } + DBUG_PRINT("tcache", ("opening table: '%s'.'%s' item: 0x%lx", + tables->db, tables->table_name, (long) tables)); /* If this TABLE_LIST object is a placeholder for an information_schema table, create a temporary table to represent the information_schema @@ -7698,7 +7801,7 @@ bool setup_fields(THD *thd, Item **ref_pointer_array, if (item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM && sum_func_list) item->split_sum_func(thd, ref_pointer_array, *sum_func_list); - thd->used_tables|= item->used_tables(); + thd->lex->used_tables|= item->used_tables(); thd->lex->current_select->cur_pos_in_select_list++; } thd->lex->current_select->is_item_list_lookup= save_is_item_list_lookup; @@ -7844,7 +7947,7 @@ bool setup_tables(THD *thd, Name_resolution_context *context, } if (tablenr > MAX_TABLES) { - my_error(ER_TOO_MANY_TABLES,MYF(0),MAX_TABLES); + my_error(ER_TOO_MANY_TABLES,MYF(0), (int) MAX_TABLES); DBUG_RETURN(1); } } @@ -8118,7 +8221,7 @@ 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->used_tables|= table->map; + thd->lex->used_tables|= table->map; /* Initialize a generic field iterator for the current table reference. @@ -8209,7 +8312,7 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, field_table= nj_col->table_ref->table; if (field_table) { - thd->used_tables|= field_table->map; + thd->lex->used_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++; @@ -8217,7 +8320,7 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, } } else - thd->used_tables|= item->used_tables(); + thd->lex->used_tables|= item->used_tables(); thd->lex->current_select->cur_pos_in_select_list++; } /* |