diff options
Diffstat (limited to 'sql/sql_admin.cc')
-rw-r--r-- | sql/sql_admin.cc | 379 |
1 files changed, 220 insertions, 159 deletions
diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc index 4afaff58223..ab95fdc340c 100644 --- a/sql/sql_admin.cc +++ b/sql/sql_admin.cc @@ -54,7 +54,7 @@ static bool admin_recreate_table(THD *thd, TABLE_LIST *table_list) DEBUG_SYNC(thd, "ha_admin_try_alter"); tmp_disable_binlog(thd); // binlogging is done by caller if wanted - result_code= (open_temporary_tables(thd, table_list) || + result_code= (thd->open_temporary_tables(table_list) || mysql_recreate_table(thd, table_list, false)); reenable_binlog(thd); /* @@ -130,7 +130,7 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, DBUG_RETURN(0); has_mdl_lock= TRUE; - share= tdc_acquire_share_shortlived(thd, table_list, GTS_TABLE); + share= tdc_acquire_share(thd, table_list, GTS_TABLE); if (share == NULL) DBUG_RETURN(0); // Can't open frm file @@ -162,7 +162,7 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, - Run a normal repair using the new index file and the old data file */ - if (table->s->frm_version != FRM_VER_TRUE_VARCHAR && + if (table->s->frm_version < FRM_VER_TRUE_VARCHAR && table->s->varchar_fields) { error= send_check_errmsg(thd, table_list, "repair", @@ -260,7 +260,10 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, end: thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0); if (table == &tmp_table) - closefrm(table, 1); // Free allocated memory + { + closefrm(table); + tdc_release_share(table->s); + } /* In case of a temporary table there will be no metadata lock. */ if (error && has_mdl_lock) thd->mdl_context.release_transactional_locks(); @@ -291,6 +294,127 @@ static inline bool table_not_corrupt_error(uint sql_errno) sql_errno == ER_WRONG_OBJECT); } +#ifndef DBUG_OFF +// It is counter for debugging fail on second call of open_only_one_table +static int debug_fail_counter= 0; +#endif + +static bool open_only_one_table(THD* thd, TABLE_LIST* table, + bool repair_table_use_frm, + bool is_view_operator_func) +{ + LEX *lex= thd->lex; + SELECT_LEX *select= &lex->select_lex; + TABLE_LIST *save_next_global, *save_next_local; + bool open_error; + save_next_global= table->next_global; + table->next_global= 0; + save_next_local= table->next_local; + table->next_local= 0; + select->table_list.first= table; + /* + Time zone tables and SP tables can be add to lex->query_tables list, + so it have to be prepared. + TODO: Investigate if we can put extra tables into argument instead of + using lex->query_tables + */ + lex->query_tables= table; + lex->query_tables_last= &table->next_global; + lex->query_tables_own_last= 0; + + DBUG_EXECUTE_IF("fail_2call_open_only_one_table", { + if (debug_fail_counter) + { + open_error= TRUE; + goto dbug_err; + } + else + debug_fail_counter++; + }); + + /* + CHECK TABLE command is allowed for views as well. Check on alter flags + to differentiate from ALTER TABLE...CHECK PARTITION on which view is not + allowed. + */ + if (lex->alter_info.flags & Alter_info::ALTER_ADMIN_PARTITION || + !is_view_operator_func) + { + table->required_type=FRMTYPE_TABLE; + DBUG_ASSERT(!lex->only_view); + } + else if (lex->only_view) + { + table->required_type= FRMTYPE_VIEW; + } + else if (!lex->only_view && lex->sql_command == SQLCOM_REPAIR) + { + table->required_type= FRMTYPE_TABLE; + } + + if (lex->sql_command == SQLCOM_CHECK || + lex->sql_command == SQLCOM_REPAIR || + lex->sql_command == SQLCOM_ANALYZE || + lex->sql_command == SQLCOM_OPTIMIZE) + thd->prepare_derived_at_open= TRUE; + if (!thd->locked_tables_mode && repair_table_use_frm) + { + /* + If we're not under LOCK TABLES and we're executing REPAIR TABLE + USE_FRM, we need to ignore errors from open_and_lock_tables(). + REPAIR TABLE USE_FRM is a heavy weapon used when a table is + critically damaged, so open_and_lock_tables() will most likely + report errors. Those errors are not interesting for the user + because it's already known that the table is badly damaged. + */ + + Diagnostics_area *da= thd->get_stmt_da(); + Warning_info tmp_wi(thd->query_id, false, true); + + da->push_warning_info(&tmp_wi); + + open_error= (thd->open_temporary_tables(table) || + open_and_lock_tables(thd, table, TRUE, 0)); + + da->pop_warning_info(); + } + else + { + /* + It's assumed that even if it is REPAIR TABLE USE_FRM, the table + can be opened if we're under LOCK TABLES (otherwise LOCK TABLES + would fail). Thus, the only errors we could have from + open_and_lock_tables() are logical ones, like incorrect locking + mode. It does make sense for the user to see such errors. + */ + + open_error= (thd->open_temporary_tables(table) || + open_and_lock_tables(thd, table, TRUE, 0)); + } +#ifndef DBUG_OFF +dbug_err: +#endif + + thd->prepare_derived_at_open= FALSE; + + /* + MERGE engine may adjust table->next_global chain, thus we have to + append save_next_global after merge children. + */ + if (save_next_global) + { + TABLE_LIST *table_list_iterator= table; + while (table_list_iterator->next_global) + table_list_iterator= table_list_iterator->next_global; + table_list_iterator->next_global= save_next_global; + save_next_global->prev_global= &table_list_iterator->next_global; + } + + table->next_local= save_next_local; + + return open_error; +} + /* RETURN VALUES @@ -313,7 +437,6 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT *)) { TABLE_LIST *table; - SELECT_LEX *select= &thd->lex->select_lex; List<Item> field_list; Item *item; Protocol *protocol= thd->protocol; @@ -389,97 +512,9 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, /* open only one table from local list of command */ while (1) { - TABLE_LIST *save_next_global, *save_next_local; - save_next_global= table->next_global; - table->next_global= 0; - save_next_local= table->next_local; - table->next_local= 0; - select->table_list.first= table; - /* - Time zone tables and SP tables can be add to lex->query_tables list, - so it have to be prepared. - TODO: Investigate if we can put extra tables into argument instead of - using lex->query_tables - */ - lex->query_tables= table; - lex->query_tables_last= &table->next_global; - lex->query_tables_own_last= 0; - - /* - CHECK TABLE command is allowed for views as well. Check on alter flags - to differentiate from ALTER TABLE...CHECK PARTITION on which view is - not allowed. - */ - if (lex->alter_info.flags & Alter_info::ALTER_ADMIN_PARTITION || - view_operator_func == NULL) - { - table->required_type=FRMTYPE_TABLE; - DBUG_ASSERT(!lex->only_view); - } - else if (lex->only_view) - { - table->required_type= FRMTYPE_VIEW; - } - else if (!lex->only_view && lex->sql_command == SQLCOM_REPAIR) - { - table->required_type= FRMTYPE_TABLE; - } - - if (lex->sql_command == SQLCOM_CHECK || - lex->sql_command == SQLCOM_REPAIR || - lex->sql_command == SQLCOM_ANALYZE || - lex->sql_command == SQLCOM_OPTIMIZE) - thd->prepare_derived_at_open= TRUE; - if (!thd->locked_tables_mode && repair_table_use_frm) - { - /* - If we're not under LOCK TABLES and we're executing REPAIR TABLE - USE_FRM, we need to ignore errors from open_and_lock_tables(). - REPAIR TABLE USE_FRM is a heavy weapon used when a table is - critically damaged, so open_and_lock_tables() will most likely - report errors. Those errors are not interesting for the user - because it's already known that the table is badly damaged. - */ - - Diagnostics_area *da= thd->get_stmt_da(); - Warning_info tmp_wi(thd->query_id, false, true); - - da->push_warning_info(&tmp_wi); - - open_error= (open_temporary_tables(thd, table) || - open_and_lock_tables(thd, table, TRUE, 0)); - - da->pop_warning_info(); - } - else - { - /* - It's assumed that even if it is REPAIR TABLE USE_FRM, the table - can be opened if we're under LOCK TABLES (otherwise LOCK TABLES - would fail). Thus, the only errors we could have from - open_and_lock_tables() are logical ones, like incorrect locking - mode. It does make sense for the user to see such errors. - */ - - open_error= (open_temporary_tables(thd, table) || - open_and_lock_tables(thd, table, TRUE, 0)); - } - thd->prepare_derived_at_open= FALSE; - - /* - MERGE engine may adjust table->next_global chain, thus we have to - append save_next_global after merge children. - */ - if (save_next_global) - { - TABLE_LIST *table_list_iterator= table; - while (table_list_iterator->next_global) - table_list_iterator= table_list_iterator->next_global; - table_list_iterator->next_global= save_next_global; - save_next_global->prev_global= &table_list_iterator->next_global; - } - - table->next_local= save_next_local; + open_error= open_only_one_table(thd, table, + repair_table_use_frm, + (view_operator_func != NULL)); thd->open_options&= ~extra_open_options; /* @@ -552,7 +587,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, } } #endif - DBUG_PRINT("admin", ("table: 0x%lx", (long) table->table)); + DBUG_PRINT("admin", ("table: %p", table->table)); if (prepare_func) { @@ -662,19 +697,23 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, MDL_SHARED_NO_READ_WRITE lock (MDL_SHARED_WRITE cannot be upgraded) by *not* having HA_CONCURRENT_OPTIMIZE table_flag. */ - if (lock_type == TL_WRITE && !table->table->s->tmp_table && - table->mdl_request.type > MDL_SHARED_WRITE) + if (lock_type == TL_WRITE && table->mdl_request.type > MDL_SHARED_WRITE) { - if (wait_while_table_is_used(thd, table->table, HA_EXTRA_NOT_USED)) - goto err; - DEBUG_SYNC(thd, "after_admin_flush"); - /* Flush entries in the query cache involving this table. */ - query_cache_invalidate3(thd, table->table, 0); - /* - XXX: hack: switch off open_for_modify to skip the - flush that is made later in the execution flow. - */ - open_for_modify= 0; + if (table->table->s->tmp_table) + thd->close_unused_temporary_table_instances(tables); + else + { + if (wait_while_table_is_used(thd, table->table, HA_EXTRA_NOT_USED)) + goto err; + DEBUG_SYNC(thd, "after_admin_flush"); + /* Flush entries in the query cache involving this table. */ + query_cache_invalidate3(thd, table->table, 0); + /* + XXX: hack: switch off open_for_modify to skip the + flush that is made later in the execution flow. + */ + open_for_modify= 0; + } } if (table->table->s->crashed && operator_func == &handler::ha_check) @@ -720,7 +759,6 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, if (operator_func == &handler::ha_analyze) { TABLE *tab= table->table; - Field **field_ptr= tab->field; if (lex->with_persistent_for_clause && tab->s->table_category != TABLE_CATEGORY_USER) @@ -732,8 +770,69 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, (get_use_stat_tables_mode(thd) > NEVER || lex->with_persistent_for_clause)); - if (collect_eis) + + if (!lex->index_list) + { + tab->keys_in_use_for_query.init(tab->s->keys); + } + else + { + int pos; + LEX_STRING *index_name; + List_iterator_fast<LEX_STRING> it(*lex->index_list); + + tab->keys_in_use_for_query.clear_all(); + while ((index_name= it++)) + { + if (tab->s->keynames.type_names == 0 || + (pos= find_type(&tab->s->keynames, index_name->str, + index_name->length, 1)) <= 0) + { + compl_result_code= result_code= HA_ADMIN_INVALID; + break; + } + tab->keys_in_use_for_query.set_bit(--pos); + } + } + } + + if (result_code == HA_ADMIN_OK) + { + DBUG_PRINT("admin", ("calling operator_func '%s'", operator_name)); + THD_STAGE_INFO(thd, stage_executing); + result_code = (table->table->file->*operator_func)(thd, check_opt); + THD_STAGE_INFO(thd, stage_sending_data); + DBUG_PRINT("admin", ("operator_func returned: %d", result_code)); + } + + if (compl_result_code == HA_ADMIN_OK && collect_eis) + { + /* + Here we close and reopen table in read mode because operation of + collecting statistics is long and it will be better do not block + the table completely. + InnoDB/XtraDB will allow read/write and MyISAM read/insert. + */ + trans_commit_stmt(thd); + trans_commit(thd); + thd->open_options|= extra_open_options; + close_thread_tables(thd); + table->table= NULL; + thd->mdl_context.release_transactional_locks(); + table->mdl_request.init(MDL_key::TABLE, table->db, table->table_name, + MDL_SHARED_NO_READ_WRITE, MDL_TRANSACTION); + table->mdl_request.set_type(MDL_SHARED_READ); + + table->lock_type= TL_READ; + DBUG_ASSERT(view_operator_func == NULL); + open_error= open_only_one_table(thd, table, + repair_table_use_frm, FALSE); + thd->open_options&= ~extra_open_options; + + if (!open_error) { + TABLE *tab= table->table; + Field **field_ptr= tab->field; if (!lex->column_list) { bitmap_clear_all(tab->read_set); @@ -743,7 +842,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, if (type < MYSQL_TYPE_MEDIUM_BLOB || type > MYSQL_TYPE_BLOB) bitmap_set_bit(tab->read_set, fields); - else if (collect_eis) + else push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_NO_EIS_FOR_FIELD, ER_THD(thd, ER_NO_EIS_FOR_FIELD), @@ -771,61 +870,23 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, if (type < MYSQL_TYPE_MEDIUM_BLOB || type > MYSQL_TYPE_BLOB) bitmap_set_bit(tab->read_set, pos); - else if (collect_eis) + else push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_NO_EIS_FOR_FIELD, ER_THD(thd, ER_NO_EIS_FOR_FIELD), column_name->str); } - tab->file->column_bitmaps_signal(); + tab->file->column_bitmaps_signal(); } + if (!(compl_result_code= + alloc_statistics_for_table(thd, table->table)) && + !(compl_result_code= + collect_statistics_for_table(thd, table->table))) + compl_result_code= update_statistics_for_table(thd, table->table); } else - { - DBUG_ASSERT(!lex->column_list); - } + compl_result_code= HA_ADMIN_FAILED; - if (!lex->index_list) - { - tab->keys_in_use_for_query.init(tab->s->keys); - } - else - { - int pos; - LEX_STRING *index_name; - List_iterator_fast<LEX_STRING> it(*lex->index_list); - - tab->keys_in_use_for_query.clear_all(); - while ((index_name= it++)) - { - if (tab->s->keynames.type_names == 0 || - (pos= find_type(&tab->s->keynames, index_name->str, - index_name->length, 1)) <= 0) - { - compl_result_code= result_code= HA_ADMIN_INVALID; - break; - } - tab->keys_in_use_for_query.set_bit(--pos); - } - } - } - - if (result_code == HA_ADMIN_OK) - { - DBUG_PRINT("admin", ("calling operator_func '%s'", operator_name)); - THD_STAGE_INFO(thd, stage_executing); - result_code = (table->table->file->*operator_func)(thd, check_opt); - THD_STAGE_INFO(thd, stage_sending_data); - DBUG_PRINT("admin", ("operator_func returned: %d", result_code)); - } - - if (compl_result_code == HA_ADMIN_OK && collect_eis) - { - if (!(compl_result_code= - alloc_statistics_for_table(thd, table->table)) && - !(compl_result_code= - collect_statistics_for_table(thd, table->table))) - compl_result_code= update_statistics_for_table(thd, table->table); if (compl_result_code) result_code= HA_ADMIN_FAILED; else @@ -978,7 +1039,7 @@ send_result_message: table->mdl_request.ticket= NULL; DEBUG_SYNC(thd, "ha_admin_open_ltable"); table->mdl_request.set_type(MDL_SHARED_WRITE); - if (!open_temporary_tables(thd, table) && + if (!thd->open_temporary_tables(table) && (table->table= open_ltable(thd, table, lock_type, 0))) { uint save_flags; |