diff options
Diffstat (limited to 'sql')
51 files changed, 902 insertions, 180 deletions
diff --git a/sql/field.cc b/sql/field.cc index 6e1851d89d1..2084c661602 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -1,6 +1,6 @@ /* - Copyright (c) 2000, 2011, Oracle and/or its affiliates. - Copyright (c) 2008-2011 Monty Program Ab + Copyright (c) 2000, 2012, Oracle and/or its affiliates. + Copyright (c) 2008, 2011, Monty Program Ab This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/sql/field.h b/sql/field.h index c86c0975117..f22bab0409d 100644 --- a/sql/field.h +++ b/sql/field.h @@ -1,7 +1,7 @@ #ifndef FIELD_INCLUDED #define FIELD_INCLUDED -/* Copyright (c) 2000, 2011 Oracle and/or its affiliates. - Copyright (c) 2008-2011 Monty Program Ab +/* Copyright (c) 2000, 2012, Oracle and/or its affiliates. + Copyright (c) 2008, 2011, Monty Program Ab This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -63,6 +63,9 @@ enum Derivation #define my_charset_numeric my_charset_latin1 #define MY_REPERTOIRE_NUMERIC MY_REPERTOIRE_ASCII +/* The length of the header part for each virtual column in the .frm file */ +#define FRM_VCOL_HEADER_SIZE(b) (3 + test(b)) + struct ha_field_option_struct; struct st_cache_field; @@ -2244,6 +2247,10 @@ public: { return (flags & (BINCMP_FLAG | BINARY_FLAG)) != 0; } + uint virtual_col_expr_maxlen() + { + return 255 - FRM_VCOL_HEADER_SIZE(interval != NULL); + } private: const String empty_set_string; }; diff --git a/sql/field_conv.cc b/sql/field_conv.cc index 20da18a129c..6685b334d06 100644 --- a/sql/field_conv.cc +++ b/sql/field_conv.cc @@ -1,5 +1,7 @@ /* - Copyright (c) 2000, 2011, Oracle and/or its affiliates. + Copyright (c) 2000, 2012, Oracle and/or its affiliates. + Copyright (c) 2010, 2012, Monty Program Ab + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/sql/handler.cc b/sql/handler.cc index 2c9cc388006..b7d545a75dc 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -1276,6 +1276,7 @@ int ha_commit_trans(THD *thd, bool all) need_prepare_ordered|= (ht->prepare_ordered != NULL); need_commit_ordered|= (ht->commit_ordered != NULL); } + DEBUG_SYNC(thd, "ha_commit_trans_after_prepare"); DBUG_EXECUTE_IF("crash_commit_after_prepare", DBUG_SUICIDE();); if (!is_real_trans) diff --git a/sql/item.cc b/sql/item.cc index 3af415fe5c2..3031e90c9b2 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -1265,7 +1265,7 @@ err: bool Item::get_seconds(ulonglong *sec, ulong *sec_part) { - if (result_type() == INT_RESULT) + if (decimals == 0) { // optimize for an important special case longlong val= val_int(); bool neg= val < 0 && !unsigned_flag; diff --git a/sql/item.h b/sql/item.h index a9c1153d236..f7f3edda384 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1395,21 +1395,21 @@ public: { return cmp_context == IMPOSSIBLE_RESULT || item->cmp_context == cmp_context; } - /* + /** Test whether an expression is expensive to compute. Used during optimization to avoid computing expensive expressions during this phase. Also used to force temp tables when sorting on expensive functions. - TODO: + @todo Normally we should have a method: cost Item::execution_cost(), where 'cost' is either 'double' or some structure of various cost parameters. - NOTE - This function is now used to prevent evaluation of materialized IN - subquery predicates before it is allowed. grep for - DontEvaluateMaterializedSubqueryTooEarly to see the uses. + @note + This function is now used to prevent evaluation of expensive subquery + predicates during the optimization phase. It also prevents evaluation + of predicates that are not computable at this moment. */ virtual bool is_expensive() { diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 5f1a863d8fd..2a0ca19a4e9 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -1504,6 +1504,7 @@ bool Item_in_optimizer::fix_fields(THD *thd, Item **ref) } if (args[1]->maybe_null) maybe_null=1; + with_subselect= 1; with_sum_func= with_sum_func || args[1]->with_sum_func; with_field= with_field || args[1]->with_field; used_tables_cache|= args[1]->used_tables(); @@ -3032,6 +3033,11 @@ void Item_func_case::fix_length_and_dec() nagg++; if (!(found_types= collect_cmp_types(agg, nagg))) return; + + Item *date_arg= 0; + if (found_types & (1 << TIME_RESULT)) + date_arg= find_date_time_item(args, arg_count, 0); + if (found_types & (1 << STRING_RESULT)) { /* @@ -3071,16 +3077,12 @@ void Item_func_case::fix_length_and_dec() change_item_tree_if_needed(thd, &args[nagg * 2], agg[nagg + 1]); } - Item *date_arg= 0; for (i= 0; i <= (uint)TIME_RESULT; i++) { if (found_types & (1 << i) && !cmp_items[i]) { DBUG_ASSERT((Item_result)i != ROW_RESULT); - if ((Item_result)i == TIME_RESULT) - date_arg= find_date_time_item(args, arg_count, 0); - if (!(cmp_items[i]= cmp_item::get_comparator((Item_result)i, date_arg, cmp_collation.collation))) @@ -4051,15 +4053,15 @@ void Item_func_in::fix_length_and_dec() } else { + if (found_types & (1 << TIME_RESULT)) + date_arg= find_date_time_item(args, arg_count, 0); + if (found_types & (1 << STRING_RESULT) && + agg_arg_charsets_for_comparison(cmp_collation, args, arg_count)) + return; for (i= 0; i <= (uint) TIME_RESULT; i++) { if (found_types & (1 << i) && !cmp_items[i]) { - if ((Item_result)i == STRING_RESULT && - agg_arg_charsets_for_comparison(cmp_collation, args, arg_count)) - return; - if ((Item_result)i == TIME_RESULT) - date_arg= find_date_time_item(args, arg_count, 0); if (!cmp_items[i] && !(cmp_items[i]= cmp_item::get_comparator((Item_result)i, date_arg, cmp_collation.collation))) @@ -4255,6 +4257,22 @@ Item_cond::fix_fields(THD *thd, Item **ref) if (abort_on_null) item->top_level_item(); + /* + replace degraded condition: + was: <field> + become: <field> = 1 + */ + if (item->type() == FIELD_ITEM) + { + Query_arena backup, *arena; + Item *new_item; + arena= thd->activate_stmt_arena_if_needed(&backup); + if ((new_item= new Item_func_ne(item, new Item_int(0, 1)))) + li.replace(item= new_item); + if (arena) + thd->restore_active_arena(arena, &backup); + } + // item can be substituted in fix_fields if ((!item->fixed && item->fix_fields(thd, li.ref())) || @@ -4939,6 +4957,7 @@ Item_func_regex::fix_fields(THD *thd, Item **ref) return TRUE; /* purecov: inspected */ with_sum_func=args[0]->with_sum_func || args[1]->with_sum_func; with_field= args[0]->with_field || args[1]->with_field; + with_subselect|= args[0]->with_subselect | args[1]->with_subselect; max_length= 1; decimals= 0; @@ -5308,6 +5327,28 @@ Item *Item_func_not::neg_transformer(THD *thd) /* NOT(x) -> x */ } +bool Item_func_not::fix_fields(THD *thd, Item **ref) +{ + if (args[0]->type() == FIELD_ITEM) + { + /* replace "NOT <field>" with "<filed> == 0" */ + Query_arena backup, *arena; + Item *new_item; + bool rc= TRUE; + arena= thd->activate_stmt_arena_if_needed(&backup); + if ((new_item= new Item_func_eq(args[0], new Item_int(0, 1)))) + { + new_item->name= name; + rc= (*ref= new_item)->fix_fields(thd, ref); + } + if (arena) + thd->restore_active_arena(arena, &backup); + return rc; + } + return Item_func::fix_fields(thd, ref); +} + + Item *Item_bool_rowready_func2::neg_transformer(THD *thd) { Item *item= negated_item(); @@ -5540,7 +5581,15 @@ void Item_equal::add_const(Item *c, Item *f) else { Item_func_eq *func= new Item_func_eq(c, const_item); - func->set_cmp_func(); + if (func->set_cmp_func()) + { + /* + Setting a comparison function fails when trying to compare + incompatible charsets. Charset compatibility is checked earlier, + except for constant subqueries where we may do it here. + */ + return; + } func->quick_fix_field(); cond_false= !func->val_int(); } @@ -5730,6 +5779,7 @@ bool Item_equal::fix_fields(THD *thd, Item **ref) used_tables_cache|= item->used_tables(); tmp_table_map= item->not_null_tables(); not_null_tables_cache|= tmp_table_map; + DBUG_ASSERT(!item->with_sum_func && !item->with_subselect); if (item->maybe_null) maybe_null= 1; if (!item->get_item_equal()) diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index de62bc49930..34d1a0bd0ae 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -371,9 +371,9 @@ public: Item_bool_func2(Item *a,Item *b) :Item_int_func(a,b), cmp(tmp_arg, tmp_arg+1), abort_on_null(FALSE) {} void fix_length_and_dec(); - void set_cmp_func() + int set_cmp_func() { - cmp.set_cmp_func(this, tmp_arg, tmp_arg+1, TRUE); + return cmp.set_cmp_func(this, tmp_arg, tmp_arg+1, TRUE); } optimize_type select_optimize() const { return OPTIMIZE_OP; } virtual enum Functype rev_functype() const { return UNKNOWN_FUNC; } @@ -442,6 +442,7 @@ public: enum Functype functype() const { return NOT_FUNC; } const char *func_name() const { return "not"; } Item *neg_transformer(THD *thd); + bool fix_fields(THD *, Item **); virtual void print(String *str, enum_query_type query_type); }; @@ -508,6 +509,8 @@ public: longlong val_int(); enum Functype functype() const { return NOT_ALL_FUNC; } const char *func_name() const { return "<not>"; } + bool fix_fields(THD *thd, Item **ref) + {return Item_func::fix_fields(thd, ref);} virtual void print(String *str, enum_query_type query_type); void set_sum_test(Item_sum_hybrid *item) { test_sum_item= item; }; void set_sub_test(Item_maxmin_subselect *item) { test_sub_item= item; }; diff --git a/sql/item_func.cc b/sql/item_func.cc index 9b2e8e5e614..d65f13931e2 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -3338,6 +3338,7 @@ udf_handler::fix_fields(THD *thd, Item_result_field *func, func->maybe_null=1; func->with_sum_func= func->with_sum_func || item->with_sum_func; func->with_field= func->with_field || item->with_field; + func->with_subselect|= item->with_subselect; used_tables_cache|=item->used_tables(); const_item_cache&=item->const_item(); f_args.arg_type[i]=item->result_type(); diff --git a/sql/item_func.h b/sql/item_func.h index f91107c5445..cb9c1929d7d 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -1941,6 +1941,11 @@ public: return str; } + void update_null_value() + { + execute(); + } + virtual bool change_context_processor(uchar *cntx) { context= (Name_resolution_context *)cntx; return FALSE; } @@ -1955,6 +1960,7 @@ public: { return sp_result_field; } + bool check_vcol_func_processor(uchar *int_arg) { return trace_unsupported_by_check_vcol_func_processor(func_name()); diff --git a/sql/item_row.cc b/sql/item_row.cc index 530a40c55dc..2c4a628075e 100644 --- a/sql/item_row.cc +++ b/sql/item_row.cc @@ -95,6 +95,7 @@ bool Item_row::fix_fields(THD *thd, Item **ref) maybe_null|= item->maybe_null; with_sum_func= with_sum_func || item->with_sum_func; with_field= with_field || item->with_field; + with_subselect|= item->with_subselect; } fixed= 1; return FALSE; diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index ad854b02765..9ed2627a518 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -829,7 +829,7 @@ public: { DBUG_ASSERT(args[0]->fixed); conv_charset= cs; - if (cache_if_const && args[0]->const_item() && !args[0]->with_subselect) + if (cache_if_const && args[0]->const_item() && !args[0]->is_expensive()) { uint errors= 0; String tmp, *str= args[0]->val_str(&tmp); @@ -854,6 +854,30 @@ public: } } String *val_str(String *); + longlong val_int() + { + if (args[0]->result_type() == STRING_RESULT) + return Item_str_func::val_int(); + return args[0]->val_int(); + } + double val_real() + { + if (args[0]->result_type() == STRING_RESULT) + return Item_str_func::val_real(); + return args[0]->val_real(); + } + my_decimal *val_decimal(my_decimal *d) + { + if (args[0]->result_type() == STRING_RESULT) + return Item_str_func::val_decimal(d); + return args[0]->val_decimal(d); + } + bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + { + if (args[0]->result_type() == STRING_RESULT) + return Item_str_func::get_date(ltime, fuzzydate); + return args[0]->get_date(ltime, fuzzydate); + } void fix_length_and_dec(); const char *func_name() const { return "convert"; } virtual void print(String *str, enum_query_type query_type); diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 08d66431a56..c7a3ca445d6 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -1,5 +1,5 @@ -/* Copyright (c) 2002, 2011, Oracle and/or its affiliates. - Copyright (c) 2010, 2011, Monty Program Ab +/* Copyright (c) 2002, 2012, Oracle and/or its affiliates. + Copyright (c) 2010, 2012, Monty Program Ab This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -523,6 +523,48 @@ void Item_subselect::recalc_used_tables(st_select_lex *new_parent, */ } + +/** + Determine if a subquery is expensive to execute during query optimization. + + @details The cost of execution of a subquery is estimated based on an + estimate of the number of rows the subquery will access during execution. + This measure is used instead of JOIN::read_time, because it is considered + to be much more reliable than the cost estimate. + + @return true if the subquery is expensive + @return false otherwise +*/ +bool Item_subselect::is_expensive() +{ + double examined_rows= 0; + + for (SELECT_LEX *sl= unit->first_select(); sl; sl= sl->next_select()) + { + JOIN *cur_join= sl->join; + if (!cur_join) + continue; + + /* If a subquery is not optimized we cannot estimate its cost. */ + if (!cur_join->join_tab) + return true; + + if (sl->first_inner_unit()) + { + /* + Subqueries that contain subqueries are considered expensive. + @todo: accumulate the cost of subqueries. + */ + return true; + } + + examined_rows+= cur_join->get_examined_rows(); + } + + return (examined_rows > thd->variables.expensive_subquery_limit); +} + + bool Item_subselect::walk(Item_processor processor, bool walk_subquery, uchar *argument) { @@ -904,6 +946,15 @@ void Item_maxmin_subselect::print(String *str, enum_query_type query_type) void Item_maxmin_subselect::no_rows_in_result() { + /* + Subquery predicates outside of the SELECT list must be evaluated in order + to possibly filter the special result row generated for implicit grouping + if the subquery is in the HAVING clause. + If the predicate is constant, we need its actual value in the only result + row for queries with implicit grouping. + */ + if (parsing_place != SELECT_LIST || const_item()) + return; value= Item_cache::get_cache(new Item_null()); null_value= 0; was_values= 0; @@ -913,6 +964,15 @@ void Item_maxmin_subselect::no_rows_in_result() void Item_singlerow_subselect::no_rows_in_result() { + /* + Subquery predicates outside of the SELECT list must be evaluated in order + to possibly filter the special result row generated for implicit grouping + if the subquery is in the HAVING clause. + If the predicate is constant, we need its actual value in the only result + row for queries with implicit grouping. + */ + if (parsing_place != SELECT_LIST || const_item()) + return; value= Item_cache::get_cache(new Item_null()); reset(); make_const(); @@ -1375,6 +1435,15 @@ Item* Item_exists_subselect::expr_cache_insert_transformer(uchar *thd_arg) void Item_exists_subselect::no_rows_in_result() { + /* + Subquery predicates outside of the SELECT list must be evaluated in order + to possibly filter the special result row generated for implicit grouping + if the subquery is in the HAVING clause. + If the predicate is constant, we need its actual value in the only result + row for queries with implicit grouping. + */ + if (parsing_place != SELECT_LIST || const_item()) + return; value= 0; null_value= 0; make_const(); @@ -2719,6 +2788,15 @@ void Item_allany_subselect::print(String *str, enum_query_type query_type) void Item_allany_subselect::no_rows_in_result() { + /* + Subquery predicates outside of the SELECT list must be evaluated in order + to possibly filter the special result row generated for implicit grouping + if the subquery is in the HAVING clause. + If the predicate is constant, we need its actual value in the only result + row for queries with implicit grouping. + */ + if (parsing_place != SELECT_LIST || const_item()) + return; value= 0; null_value= 0; was_null= 0; diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 0e0f61aedd9..05c4528490f 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -159,8 +159,8 @@ public: null_value= 1; } /** - Set the subquery result to the default value for the predicate when the - subquery is known to produce an empty result. + Set the subquery result to a default value consistent with the semantics of + the result row produced for queries with implicit grouping. */ void no_rows_in_result()= 0; virtual bool select_transformer(JOIN *join); @@ -209,7 +209,7 @@ public: */ bool is_evaluated() const; bool is_uncacheable() const; - bool is_expensive() { return TRUE; } + bool is_expensive(); /* Used by max/min subquery to initialize value presence registration @@ -235,7 +235,7 @@ public: @retval TRUE if the predicate is expensive @retval FALSE otherwise */ - bool is_expensive_processor(uchar *arg) { return TRUE; } + bool is_expensive_processor(uchar *arg) { return is_expensive(); } /** Get the SELECT_LEX structure associated with this Item. @@ -581,6 +581,10 @@ public: bool fix_fields(THD *thd, Item **ref); void fix_length_and_dec(); void fix_after_pullout(st_select_lex *new_parent, Item **ref); + bool const_item() const + { + return Item_subselect::const_item() && left_expr->const_item(); + } void update_used_tables(); bool setup_mat_engine(); bool init_left_expr_cache(); diff --git a/sql/item_sum.cc b/sql/item_sum.cc index afe4f34bd05..0b21ba92558 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -1135,6 +1135,7 @@ Item_sum_num::fix_fields(THD *thd, Item **ref) if (args[i]->fix_fields(thd, args + i) || args[i]->check_cols(1)) return TRUE; set_if_bigger(decimals, args[i]->decimals); + with_subselect|= args[i]->with_subselect; } result_field=0; max_length=float_length(decimals); @@ -1165,6 +1166,7 @@ Item_sum_hybrid::fix_fields(THD *thd, Item **ref) (item= args[0])->check_cols(1)) return TRUE; decimals=item->decimals; + with_subselect= args[0]->with_subselect; switch (hybrid_type= item->result_type()) { case INT_RESULT: @@ -3319,6 +3321,7 @@ Item_func_group_concat::fix_fields(THD *thd, Item **ref) args[i]->fix_fields(thd, args + i)) || args[i]->check_cols(1)) return TRUE; + with_subselect|= args[i]->with_subselect; } /* skip charset aggregation for order columns */ diff --git a/sql/log.cc b/sql/log.cc index e80a2aaec51..801d945f3f0 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -2464,9 +2464,10 @@ void MYSQL_LOG::init_pthread_objects() SYNOPSIS close() - exiting Bitmask. For the slow and general logs the only used bit is - LOG_CLOSE_TO_BE_OPENED. This is used if we intend to call - open at once after close. + exiting Bitmask. LOG_CLOSE_TO_BE_OPENED is used if we intend to call + open at once after close. LOG_CLOSE_DELAYED_CLOSE is used for + binlog rotation, to delay actual close of the old file until + we have successfully created the new file. NOTES One can do an open on the object at once after doing a close. @@ -2487,7 +2488,8 @@ void MYSQL_LOG::close(uint exiting) sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno); } - if (mysql_file_close(log_file.file, MYF(MY_WME)) && ! write_error) + if (!(exiting & LOG_CLOSE_DELAYED_CLOSE) && + mysql_file_close(log_file.file, MYF(MY_WME)) && ! write_error) { write_error= 1; sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno); @@ -3189,6 +3191,10 @@ bool MYSQL_BIN_LOG::open(const char *log_name, if (write_file_name_to_index_file) { #ifdef HAVE_REPLICATION +#ifdef ENABLED_DEBUG_SYNC + if (current_thd) + DEBUG_SYNC(current_thd, "binlog_open_before_update_index"); +#endif DBUG_EXECUTE_IF("crash_create_critical_before_update_index", DBUG_SUICIDE();); #endif @@ -4297,6 +4303,10 @@ int MYSQL_BIN_LOG::new_file_impl(bool need_lock) { int error= 0, close_on_error= FALSE; char new_name[FN_REFLEN], *new_name_ptr, *old_name, *file_to_open; + uint close_flag; + bool delay_close= false; + File old_file; + LINT_INIT(old_file); DBUG_ENTER("MYSQL_BIN_LOG::new_file_impl"); if (!is_open()) @@ -4380,7 +4390,20 @@ int MYSQL_BIN_LOG::new_file_impl(bool need_lock) } old_name=name; name=0; // Don't free name - close(LOG_CLOSE_TO_BE_OPENED | LOG_CLOSE_INDEX); + close_flag= LOG_CLOSE_TO_BE_OPENED | LOG_CLOSE_INDEX; + if (!is_relay_log) + { + /* + We need to keep the old binlog file open (and marked as in-use) until + the new one is fully created and synced to disk and index. Otherwise we + leave a window where if we crash, there is no binlog file marked as + crashed for server restart to detect the need for recovery. + */ + old_file= log_file.file; + close_flag|= LOG_CLOSE_DELAYED_CLOSE; + delay_close= true; + } + close(close_flag); if (log_type == LOG_BIN && checksum_alg_reset != BINLOG_CHECKSUM_ALG_UNDEF) { DBUG_ASSERT(!is_relay_log); @@ -4423,6 +4446,12 @@ int MYSQL_BIN_LOG::new_file_impl(bool need_lock) end: + if (delay_close) + { + clear_inuse_flag_when_closing(old_file); + mysql_file_close(old_file, MYF(MY_WME)); + } + if (error && close_on_error /* rotate or reopen failed */) { /* @@ -6275,6 +6304,8 @@ int MYSQL_BIN_LOG::wait_for_update_bin_log(THD* thd, - LOG_CLOSE_TO_BE_OPENED : if we intend to call open at once after close. - LOG_CLOSE_STOP_EVENT : write a 'stop' event to the log + - LOG_CLOSE_DELAYED_CLOSE : do not yet close the file and clear the + LOG_EVENT_BINLOG_IN_USE_F flag @note One can do an open on the object at once after doing a close. @@ -6304,12 +6335,11 @@ void MYSQL_BIN_LOG::close(uint exiting) #endif /* HAVE_REPLICATION */ /* don't pwrite in a file opened with O_APPEND - it doesn't work */ - if (log_file.type == WRITE_CACHE && log_type == LOG_BIN) + if (log_file.type == WRITE_CACHE && log_type == LOG_BIN + && !(exiting & LOG_CLOSE_DELAYED_CLOSE)) { - my_off_t offset= BIN_LOG_HEADER_SIZE + FLAGS_OFFSET; my_off_t org_position= mysql_file_tell(log_file.file, MYF(0)); - uchar flags= 0; // clearing LOG_EVENT_BINLOG_IN_USE_F - mysql_file_pwrite(log_file.file, &flags, 1, offset, MYF(0)); + clear_inuse_flag_when_closing(log_file.file); /* Restore position so that anything we have in the IO_cache is written to the correct position. @@ -6344,6 +6374,18 @@ void MYSQL_BIN_LOG::close(uint exiting) } +/* + Clear the LOG_EVENT_BINLOG_IN_USE_F; this marks the binlog file as cleanly + closed and not needing crash recovery. +*/ +void MYSQL_BIN_LOG::clear_inuse_flag_when_closing(File file) +{ + my_off_t offset= BIN_LOG_HEADER_SIZE + FLAGS_OFFSET; + uchar flags= 0; // clearing LOG_EVENT_BINLOG_IN_USE_F + mysql_file_pwrite(file, &flags, 1, offset, MYF(0)); +} + + void MYSQL_BIN_LOG::set_max_size(ulong max_size_arg) { /* diff --git a/sql/log.h b/sql/log.h index e8f59801683..b350f3882fa 100644 --- a/sql/log.h +++ b/sql/log.h @@ -202,6 +202,7 @@ extern TC_LOG_DUMMY tc_log_dummy; #define LOG_CLOSE_INDEX 1 #define LOG_CLOSE_TO_BE_OPENED 2 #define LOG_CLOSE_STOP_EVENT 4 +#define LOG_CLOSE_DELAYED_CLOSE 8 /* Maximum unique log filename extension. @@ -222,6 +223,11 @@ class Relay_log_info; extern PSI_mutex_key key_LOG_INFO_lock; #endif +/* + Note that we destroy the lock mutex in the desctructor here. + This means that object instances cannot be destroyed/go out of scope, + until we have reset thd->current_linfo to NULL; + */ typedef struct st_log_info { char log_file_name[FN_REFLEN]; @@ -660,6 +666,7 @@ public: bool need_mutex); bool reset_logs(THD* thd); void close(uint exiting); + void clear_inuse_flag_when_closing(File file); // iterating through the log index file int find_log_pos(LOG_INFO* linfo, const char* log_name, diff --git a/sql/log_event.cc b/sql/log_event.cc index 5dc46031980..60e074deaca 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -1,5 +1,6 @@ /* - Copyright (c) 2000, 2011, Oracle and/or its affiliates. + Copyright (c) 2000, 2012, Oracle and/or its affiliates. + Copyright (c) 2010, 2012, Monty Program Ab This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -5838,11 +5839,12 @@ void Intvar_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) #endif +#if defined(HAVE_REPLICATION)&& !defined(MYSQL_CLIENT) + /* Intvar_log_event::do_apply_event() */ -#if defined(HAVE_REPLICATION)&& !defined(MYSQL_CLIENT) int Intvar_log_event::do_apply_event(Relay_log_info const *rli) { /* @@ -5851,6 +5853,9 @@ int Intvar_log_event::do_apply_event(Relay_log_info const *rli) */ const_cast<Relay_log_info*>(rli)->set_flag(Relay_log_info::IN_STMT); + if (rli->deferred_events_collecting) + return rli->deferred_events->add(this); + switch (type) { case LAST_INSERT_ID_EVENT: thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt= 1; @@ -5957,6 +5962,9 @@ int Rand_log_event::do_apply_event(Relay_log_info const *rli) */ const_cast<Relay_log_info*>(rli)->set_flag(Relay_log_info::IN_STMT); + if (rli->deferred_events_collecting) + return rli->deferred_events->add(this); + thd->rand.seed1= (ulong) seed1; thd->rand.seed2= (ulong) seed2; return 0; @@ -5983,6 +5991,29 @@ Rand_log_event::do_shall_skip(Relay_log_info *rli) return continue_group(rli); } +/** + Exec deferred Int-, Rand- and User- var events prefixing + a Query-log-event event. + + @param thd THD handle + + @return false on success, true if a failure in an event applying occurred. +*/ +bool slave_execute_deferred_events(THD *thd) +{ + bool res= false; + Relay_log_info *rli= thd->rli_slave; + + DBUG_ASSERT(rli && (!rli->deferred_events_collecting || rli->deferred_events)); + + if (!rli->deferred_events_collecting || rli->deferred_events->is_empty()) + return res; + + res= rli->deferred_events->execute(rli); + + return res; +} + #endif /* !MYSQL_CLIENT */ @@ -6421,6 +6452,10 @@ int User_var_log_event::do_apply_event(Relay_log_info const *rli) { Item *it= 0; CHARSET_INFO *charset; + + if (rli->deferred_events_collecting) + return rli->deferred_events->add(this); + if (!(charset= get_charset(charset_number, MYF(MY_WME)))) return 1; LEX_STRING user_var_name; diff --git a/sql/log_event.h b/sql/log_event.h index 78dccb3cac1..cccab93e0d5 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -4258,6 +4258,14 @@ private: const char* log_ident; uint ident_len; }; + +/** + The function is called by slave applier in case there are + active table filtering rules to force gathering events associated + with Query-log-event into an array to execute + them once the fate of the Query is determined for execution. +*/ +bool slave_execute_deferred_events(THD *thd); #endif int append_query_string(THD *thd, CHARSET_INFO *csinfo, diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc index 040b35d28f1..8f2c515e11c 100644 --- a/sql/log_event_old.cc +++ b/sql/log_event_old.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2007, 2010, Oracle and/or its affiliates. +/* Copyright (c) 2007, 2012, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/sql/mysql_install_db.cc b/sql/mysql_install_db.cc index 364dca9120a..bde81d915b2 100644 --- a/sql/mysql_install_db.cc +++ b/sql/mysql_install_db.cc @@ -119,8 +119,8 @@ static void die(const char *fmt, ...) fprintf(stderr, "http://kb.askmonty.org/v/installation-issues-on-windows contains some help\n" "for solving the most common problems. If this doesn't help you, please\n" - "leave a comment in the knowledge base or file a bug report at\n" - "https://bugs.launchpad.net/maria"); + "leave a comment in the Knowledgebase or file a bug report at\n" + "http://mariadb.org/jira"); } fflush(stderr); va_end(args); diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 8489b1e8371..f81c4127fde 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -3418,14 +3418,23 @@ static int init_common_variables() return 1; #ifdef HAVE_TZNAME - { - struct tm tm_tmp; - localtime_r(&server_start_time,&tm_tmp); - strmake(system_time_zone, tzname[tm_tmp.tm_isdst != 0 ? 1 : 0], - sizeof(system_time_zone)-1); + struct tm tm_tmp; + localtime_r(&server_start_time,&tm_tmp); + const char *tz_name= tzname[tm_tmp.tm_isdst != 0 ? 1 : 0]; +#ifdef _WIN32 + /* + Time zone name may be localized and contain non-ASCII characters, + Convert from ANSI encoding to UTF8. + */ + wchar_t wtz_name[sizeof(system_time_zone)]; + mbstowcs(wtz_name, tz_name, sizeof(system_time_zone)-1); + WideCharToMultiByte(CP_UTF8,0, wtz_name, -1, system_time_zone, + sizeof(system_time_zone) - 1, NULL, NULL); +#else + strmake(system_time_zone, tz_name, sizeof(system_time_zone)-1); +#endif /* _WIN32 */ +#endif /* HAVE_TZNAME */ - } -#endif /* We set SYSTEM time zone as reasonable default and also for failure of my_tz_init() and bootstrap mode. diff --git a/sql/opt_range.h b/sql/opt_range.h index 545e9e3c7b8..efb45f96345 100644 --- a/sql/opt_range.h +++ b/sql/opt_range.h @@ -990,7 +990,7 @@ class SQL_SELECT :public Sql_alloc { key_map quick_keys; // Possible quick keys key_map needed_reg; // Possible quick keys after prev tables. table_map const_tables,read_tables; - bool free_cond; + bool free_cond; /* Currently not used and always FALSE */ SQL_SELECT(); ~SQL_SELECT(); diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index a5a68d0d306..8d1cbeba5f4 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -4894,7 +4894,43 @@ static void remove_subq_pushed_predicates(JOIN *join, Item **where) bool JOIN::optimize_unflattened_subqueries() { - return select_lex->optimize_unflattened_subqueries(); + return select_lex->optimize_unflattened_subqueries(false); +} + +/** + Optimize all constant subqueries of a query that were not flattened into + a semijoin. + + @details + Similar to other constant conditions, constant subqueries can be used in + various constant optimizations. Having optimized constant subqueries before + these constant optimizations, makes it possible to estimate if a subquery + is "cheap" enough to be executed during the optimization phase. + + Constant subqueries can be optimized and evaluated independent of the outer + query, therefore if const_only = true, this method can be called early in + the optimization phase of the outer query. + + @return Operation status + @retval FALSE success. + @retval TRUE error occurred. +*/ + +bool JOIN::optimize_constant_subqueries() +{ + ulonglong save_options= select_lex->options; + bool res; + /* + Constant subqueries may be executed during the optimization phase. + In EXPLAIN mode the optimizer doesn't initialize many of the data structures + needed for execution. In order to make it possible to execute subqueries + during optimization, constant subqueries must be optimized for execution, + not for EXPLAIN. + */ + select_lex->options&= ~SELECT_DESCRIBE; + res= select_lex->optimize_unflattened_subqueries(true); + select_lex->options= save_options; + return res; } @@ -5295,7 +5331,14 @@ bool JOIN::choose_subquery_plan(table_map join_tables) by the IN predicate. */ outer_join= unit->outer_select() ? unit->outer_select()->join : NULL; - if (outer_join && outer_join->table_count > 0) + /* + Get the cost of the outer join if: + (1) It has at least one table, and + (2) It has been already optimized (if there is no join_tab, then the + outer join has not been optimized yet). + */ + if (outer_join && outer_join->table_count > 0 && // (1) + outer_join->join_tab) // (2) { /* TODO: diff --git a/sql/password.c b/sql/password.c index 1a12a81f7c6..947620ddf7a 100644 --- a/sql/password.c +++ b/sql/password.c @@ -1,5 +1,6 @@ /* Copyright (c) 2000, 2011, Oracle and/or its affiliates. + Copyright (c) 2012, Monty Program Ab This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc index 940fc201bae..252b4f3f5b9 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -52,8 +52,8 @@ Relay_log_info::Relay_log_info(bool is_slave_recovery) inited(0), abort_slave(0), slave_running(0), until_condition(UNTIL_NONE), until_log_pos(0), retried_trans(0), tables_to_lock(0), tables_to_lock_count(0), - last_event_start_time(0), m_flags(0), row_stmt_start_timestamp(0), - long_find_row_note_printed(false), + last_event_start_time(0), deferred_events(NULL),m_flags(0), + row_stmt_start_timestamp(0), long_find_row_note_printed(false), m_annotate_event(0) { DBUG_ENTER("Relay_log_info::Relay_log_info"); diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h index 958002561bc..b989283deb4 100644 --- a/sql/rpl_rli.h +++ b/sql/rpl_rli.h @@ -389,6 +389,41 @@ public: */ time_t last_event_start_time; + /* + A container to hold on Intvar-, Rand-, Uservar- log-events in case + the slave is configured with table filtering rules. + The withhold events are executed when their parent Query destiny is + determined for execution as well. + */ + Deferred_log_events *deferred_events; + + /* + State of the container: true stands for IRU events gathering, + false does for execution, either deferred or direct. + */ + bool deferred_events_collecting; + + /* + Returns true if the argument event resides in the containter; + more specifically, the checking is done against the last added event. + */ + bool is_deferred_event(Log_event * ev) + { + return deferred_events_collecting ? deferred_events->is_last(ev) : false; + }; + /* The general cleanup that slave applier may need at the end of query. */ + inline void cleanup_after_query() + { + if (deferred_events) + deferred_events->rewind(); + }; + /* The general cleanup that slave applier may need at the end of session. */ + void cleanup_after_session() + { + if (deferred_events) + delete deferred_events; + }; + /** Helper function to do after statement completion. diff --git a/sql/rpl_utility.cc b/sql/rpl_utility.cc index 71fa5c8909c..388a6c9f9bb 100644 --- a/sql/rpl_utility.cc +++ b/sql/rpl_utility.cc @@ -19,6 +19,7 @@ #ifndef MYSQL_CLIENT #include "unireg.h" // REQUIRED by later includes #include "rpl_rli.h" +#include "log_event.h" #include "sql_select.h" /** @@ -1057,6 +1058,7 @@ table_def::~table_def() #endif } + /** @param even_buf point to the buffer containing serialized event @param event_len length of the event accounting possible checksum alg @@ -1112,3 +1114,68 @@ bool event_checksum_test(uchar *event_buf, ulong event_len, uint8 alg) } return DBUG_EVALUATE_IF("simulate_checksum_test_failure", TRUE, res); } + + +#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION) + +Deferred_log_events::Deferred_log_events(Relay_log_info *rli) : last_added(NULL) +{ + my_init_dynamic_array(&array, sizeof(Log_event *), 32, 16); +} + +Deferred_log_events::~Deferred_log_events() +{ + delete_dynamic(&array); +} + +int Deferred_log_events::add(Log_event *ev) +{ + last_added= ev; + insert_dynamic(&array, (uchar*) &ev); + return 0; +} + +bool Deferred_log_events::is_empty() +{ + return array.elements == 0; +} + +bool Deferred_log_events::execute(Relay_log_info *rli) +{ + bool res= false; + + DBUG_ASSERT(rli->deferred_events_collecting); + + rli->deferred_events_collecting= false; + for (uint i= 0; !res && i < array.elements; i++) + { + Log_event *ev= (* (Log_event **) + dynamic_array_ptr(&array, i)); + res= ev->apply_event(rli); + } + rli->deferred_events_collecting= true; + return res; +} + +void Deferred_log_events::rewind() +{ + /* + Reset preceeding Query log event events which execution was + deferred because of slave side filtering. + */ + if (!is_empty()) + { + for (uint i= 0; i < array.elements; i++) + { + Log_event *ev= *(Log_event **) dynamic_array_ptr(&array, i); + delete ev; + } + if (array.elements > array.max_element) + freeze_size(&array); + reset_dynamic(&array); + } + last_added= NULL; +} + +#endif + diff --git a/sql/rpl_utility.h b/sql/rpl_utility.h index 1f5577d8b8b..9046891e27f 100644 --- a/sql/rpl_utility.h +++ b/sql/rpl_utility.h @@ -29,7 +29,7 @@ #include "mysql_com.h" class Relay_log_info; - +class Log_event; /** A table definition from the master. @@ -262,6 +262,24 @@ CPP_UNNAMED_NS_START }; CPP_UNNAMED_NS_END + +class Deferred_log_events +{ +private: + DYNAMIC_ARRAY array; + Log_event *last_added; + +public: + Deferred_log_events(Relay_log_info *rli); + ~Deferred_log_events(); + /* queue for exection at Query-log-event time prior the Query */; + int add(Log_event *ev); + bool is_empty(); + bool execute(Relay_log_info *rli); + void rewind(); + bool is_last(Log_event *ev) { return ev == last_added; }; +}; + #endif // NB. number of printed bit values is limited to sizeof(buf) - 1 diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index e55c28ec3fe..a762a06cfc1 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -6501,6 +6501,9 @@ ER_TABLE_IN_FK_CHECK ER_UNUSED_1 eng "You should never see it" +ER_BINLOG_UNSAFE_AUTOINC_NOT_FIRST + eng "INSERT into autoincrement field which is not the first part in the composed primary key is unsafe." + # # End of 5.5 error messages. # diff --git a/sql/slave.cc b/sql/slave.cc index 5f2a25d2c26..ec57f01c6ed 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -2800,7 +2800,8 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli) /* fall through */ default: DBUG_PRINT("info", ("Deleting the event after it has been executed")); - delete ev; + if (!rli->is_deferred_event(ev)) + delete ev; break; } @@ -3464,6 +3465,12 @@ pthread_handler_t handle_slave_sql(void *arg) goto err_during_init; } thd->init_for_queries(); + thd->rli_slave= rli; + if ((rli->deferred_events_collecting= rpl_filter->is_on())) + { + rli->deferred_events= new Deferred_log_events(rli); + } + thd->temporary_tables = rli->save_temporary_tables; // restore temp tables set_thd_in_use_temporary_tables(rli); // (re)set sql_thd in use for saved temp tables /* diff --git a/sql/spatial.cc b/sql/spatial.cc index 1616f93241d..e82eec26fdb 100644 --- a/sql/spatial.cc +++ b/sql/spatial.cc @@ -1,6 +1,6 @@ /* - Copyright (c) 2002, 2011, Oracle and/or its affiliates. - Copyright (c) 2011, Monty Program Ab + Copyright (c) 2002, 2012, Oracle and/or its affiliates. + Copyright (c) 2011, 2012, Monty Program Ab This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 688108555d6..acd330dd4d2 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -219,6 +219,7 @@ static bool has_write_table_with_auto_increment(TABLE_LIST *tables); static bool has_write_table_with_auto_increment_and_select(TABLE_LIST *tables); +static bool has_write_table_auto_increment_not_first_in_pk(TABLE_LIST *tables); uint cached_open_tables(void) { @@ -5745,6 +5746,12 @@ bool lock_tables(THD *thd, TABLE_LIST *tables, uint count, if (thd->variables.binlog_format != BINLOG_FORMAT_ROW && tables && has_write_table_with_auto_increment_and_select(tables)) thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_WRITE_AUTOINC_SELECT); + /* Todo: merge all has_write_table_auto_inc with decide_logging_format */ + if (thd->variables.binlog_format != BINLOG_FORMAT_ROW && tables) + { + if (has_write_table_auto_increment_not_first_in_pk(tables)) + thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_AUTOINC_NOT_FIRST); + } /* INSERT...ON DUPLICATE KEY UPDATE on a table with more than one unique keys @@ -9506,6 +9513,32 @@ has_write_table_with_auto_increment_and_select(TABLE_LIST *tables) return(has_select && has_auto_increment_tables); } +/* + Tells if there is a table whose auto_increment column is a part + of a compound primary key while is not the first column in + the table definition. + + @param tables Table list + + @return true if the table exists, fais if does not. +*/ + +static bool +has_write_table_auto_increment_not_first_in_pk(TABLE_LIST *tables) +{ + for (TABLE_LIST *table= tables; table; table= table->next_global) + { + /* we must do preliminary checks as table->table may be NULL */ + if (!table->placeholder() && + table->table->found_next_number_field && + (table->lock_type >= TL_WRITE_ALLOW_WRITE) + && table->table->s->next_number_keypart != 0) + return 1; + } + + return 0; +} + /* diff --git a/sql/sql_class.cc b/sql/sql_class.cc index a9375a4f05e..0165aa9f6e5 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -1,6 +1,6 @@ /* - Copyright (c) 2000, 2011, Oracle and/or its affiliates. - Copyright (c) 2008-2012 Monty Program Ab + Copyright (c) 2000, 2012, Oracle and/or its affiliates. + Copyright (c) 2008, 2012, Monty Program Ab This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -726,7 +726,7 @@ bool Drop_table_error_handler::handle_condition(THD *thd, THD::THD() :Statement(&main_lex, &main_mem_root, STMT_CONVENTIONAL_EXECUTION, /* statement id */ 0), - rli_fake(0), + rli_fake(0), rli_slave(NULL), in_sub_stmt(0), log_all_errors(0), binlog_unsafe_warning_flags(0), binlog_table_maps(0), @@ -1412,6 +1412,8 @@ THD::~THD() } mysql_audit_free_thd(this); + if (rli_slave) + rli_slave->cleanup_after_session(); #endif free_root(&main_mem_root, MYF(0)); @@ -1791,6 +1793,11 @@ void THD::cleanup_after_query() table_map_for_update= 0; m_binlog_invoker= FALSE; +#ifndef EMBEDDED_LIBRARY + if (rli_slave) + rli_slave->cleanup_after_query(); +#endif + DBUG_VOID_RETURN; } diff --git a/sql/sql_class.h b/sql/sql_class.h index c88d8211986..5b53f806ddb 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1,6 +1,6 @@ /* - Copyright (c) 2000, 2011, Oracle and/or its affiliates. - Copyright (c) 2009-2012, Monty Program Ab + Copyright (c) 2000, 2012, Oracle and/or its affiliates. + Copyright (c) 2009, 2012, Monty Program Ab This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -482,6 +482,7 @@ typedef struct system_variables ulonglong group_concat_max_len; ha_rows select_limit; ha_rows max_join_size; + ha_rows expensive_subquery_limit; ulong auto_increment_increment, auto_increment_offset; ulong lock_wait_timeout; ulong join_cache_level; @@ -1547,6 +1548,8 @@ public: /* Used to execute base64 coded binlog events in MySQL server */ Relay_log_info* rli_fake; + /* Slave applier execution context */ + Relay_log_info* rli_slave; void reset_for_next_command(bool calculate_userstat); /* @@ -3522,7 +3525,8 @@ public: if (copy_field) /* Fix for Intel compiler */ { delete [] copy_field; - save_copy_field= copy_field= 0; + save_copy_field= copy_field= NULL; + save_copy_field_end= copy_field_end= NULL; } } }; diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index cc358eca440..5128b1284dd 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -120,7 +120,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, } /* Apply the IN=>EXISTS transformation to all subqueries and optimize them. */ - if (select_lex->optimize_unflattened_subqueries()) + if (select_lex->optimize_unflattened_subqueries(false)) DBUG_RETURN(TRUE); const_cond= (!conds || conds->const_item()); diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 5ac53484aac..cf0a8674316 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -68,7 +68,8 @@ Query_tables_list::binlog_stmt_unsafe_errcode[BINLOG_STMT_UNSAFE_COUNT] = ER_BINLOG_UNSAFE_CREATE_REPLACE_SELECT, ER_BINLOG_UNSAFE_CREATE_SELECT_AUTOINC, ER_BINLOG_UNSAFE_UPDATE_IGNORE, - ER_BINLOG_UNSAFE_INSERT_TWO_KEYS + ER_BINLOG_UNSAFE_INSERT_TWO_KEYS, + ER_BINLOG_UNSAFE_AUTOINC_NOT_FIRST }; @@ -3414,7 +3415,23 @@ bool st_select_lex::add_index_hint (THD *thd, char *str, uint length) } -bool st_select_lex::optimize_unflattened_subqueries() +/** + Optimize all subqueries that have not been flattened into semi-joins. + + @details + This functionality is a method of SELECT_LEX instead of JOIN because + SQL statements as DELETE/UPDATE do not have a corresponding JOIN object. + + @see JOIN::optimize_unflattened_subqueries + + @param const_only Restrict subquery optimization to constant subqueries + + @return Operation status + @retval FALSE success. + @retval TRUE error occurred. +*/ + +bool st_select_lex::optimize_unflattened_subqueries(bool const_only) { for (SELECT_LEX_UNIT *un= first_inner_unit(); un; un= un->next_unit()) { @@ -3424,12 +3441,19 @@ bool st_select_lex::optimize_unflattened_subqueries() { if (subquery_predicate->substype() == Item_subselect::IN_SUBS) { - Item_in_subselect *in_subs=(Item_in_subselect*)subquery_predicate; + Item_in_subselect *in_subs= (Item_in_subselect*) subquery_predicate; if (in_subs->is_jtbm_merged) continue; } + if (const_only && !subquery_predicate->const_item()) + { + /* Skip non-constant subqueries if the caller asked so. */ + continue; + } + bool empty_union_result= true; + bool is_correlated_unit= false; /* If the subquery is a UNION, optimize all the subqueries in the UNION. If there is no UNION, then the loop will execute once for the subquery. @@ -3454,6 +3478,8 @@ bool st_select_lex::optimize_unflattened_subqueries() inner_join->select_options|= SELECT_DESCRIBE; } res= inner_join->optimize(); + sl->update_correlated_cache(); + is_correlated_unit|= sl->is_correlated; inner_join->select_options= save_options; un->thd->lex->current_select= save_select; if (empty_union_result) @@ -3469,6 +3495,9 @@ bool st_select_lex::optimize_unflattened_subqueries() } if (empty_union_result) subquery_predicate->no_rows_in_result(); + if (!is_correlated_unit) + un->uncacheable&= ~UNCACHEABLE_DEPENDENT; + subquery_predicate->is_correlated= is_correlated_unit; } } return FALSE; @@ -3838,6 +3867,61 @@ void SELECT_LEX::update_used_tables() /** + @brief + Update is_correlated cache for this select + + @details +*/ + +void st_select_lex::update_correlated_cache() +{ + TABLE_LIST *tl; + List_iterator<TABLE_LIST> ti(leaf_tables); + + is_correlated= false; + + while ((tl= ti++)) + { + if (tl->on_expr) + is_correlated|= test(tl->on_expr->used_tables() & OUTER_REF_TABLE_BIT); + for (TABLE_LIST *embedding= tl->embedding ; embedding ; + embedding= embedding->embedding) + { + if (embedding->on_expr) + is_correlated|= test(embedding->on_expr->used_tables() & + OUTER_REF_TABLE_BIT); + } + } + + if (join->conds) + is_correlated|= test(join->conds->used_tables() & OUTER_REF_TABLE_BIT); + + if (join->having) + is_correlated|= test(join->having->used_tables() & OUTER_REF_TABLE_BIT); + + if (join->tmp_having) + is_correlated|= test(join->tmp_having->used_tables() & OUTER_REF_TABLE_BIT); + + Item *item; + List_iterator_fast<Item> it(join->fields_list); + while ((item= it++)) + is_correlated|= test(item->used_tables() & OUTER_REF_TABLE_BIT); + + for (ORDER *order= group_list.first; order; order= order->next) + is_correlated|= test((*order->item)->used_tables() & OUTER_REF_TABLE_BIT); + + if (!master_unit()->is_union()) + { + for (ORDER *order= order_list.first; order; order= order->next) + is_correlated|= test((*order->item)->used_tables() & OUTER_REF_TABLE_BIT); + } + + if (!is_correlated) + uncacheable&= ~UNCACHEABLE_DEPENDENT; +} + + +/** Set the EXPLAIN type for this subquery. */ diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 7da0cc48298..2f3214646de 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -997,12 +997,7 @@ public: void clear_index_hints(void) { index_hints= NULL; } bool is_part_of_union() { return master_unit()->is_union(); } - /* - Optimize all subqueries that have not been flattened into semi-joins. - This functionality is a method of SELECT_LEX instead of JOIN because - some SQL statements as DELETE do not have a corresponding JOIN object. - */ - bool optimize_unflattened_subqueries(); + bool optimize_unflattened_subqueries(bool const_only); /* Set the EXPLAIN type for this subquery. */ void set_explain_type(); bool handle_derived(LEX *lex, uint phases); @@ -1023,6 +1018,7 @@ public: void mark_as_belong_to_derived(TABLE_LIST *derived); void increase_derived_records(ha_rows records); void update_used_tables(); + void update_correlated_cache(); void mark_const_derived(bool empty); bool save_leaf_tables(THD *thd); @@ -1433,6 +1429,12 @@ public: */ BINLOG_STMT_UNSAFE_INSERT_TWO_KEYS, + /** + INSERT into auto-inc field which is not the first part of composed + primary key. + */ + BINLOG_STMT_UNSAFE_AUTOINC_NOT_FIRST, + /* The last element of this enumeration type. */ BINLOG_STMT_UNSAFE_COUNT }; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 122d9289d36..e77ae68a5de 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2074,6 +2074,11 @@ mysql_execute_command(THD *thd) } DBUG_RETURN(0); } + /* + Execute deferred events first + */ + if (slave_execute_deferred_events(thd)) + DBUG_RETURN(-1); } else { @@ -2685,7 +2690,7 @@ end_with_restore_list: goto error; #else { - if (check_global_access(thd, SUPER_ACL)) + if (check_global_access(thd, SUPER_ACL | REPL_CLIENT_ACL)) goto error; res = show_binlogs(thd); break; @@ -6192,6 +6197,7 @@ TABLE_LIST *st_select_lex::end_nested_join(THD *thd) embedded->embedding= embedding; join_list->push_front(embedded); ptr= embedded; + embedded->lifted= 1; } else if (nested_join->join_list.elements == 0) { diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index b5c852f9b29..fafa7a4389e 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -1883,6 +1883,8 @@ bool mysql_show_binlog_events(THD* thd) File file = -1; MYSQL_BIN_LOG *binary_log= NULL; int old_max_allowed_packet= thd->variables.max_allowed_packet; + LOG_INFO linfo; + DBUG_ENTER("mysql_show_binlog_events"); Log_event::init_show_field_list(&field_list); @@ -1925,7 +1927,6 @@ bool mysql_show_binlog_events(THD* thd) char search_file_name[FN_REFLEN], *name; const char *log_file_name = lex_mi->log_file_name; mysql_mutex_t *log_lock = binary_log->get_log_lock(); - LOG_INFO linfo; Log_event* ev; unit->set_limit(thd->lex->current_select); @@ -2022,6 +2023,8 @@ bool mysql_show_binlog_events(THD* thd) mysql_mutex_unlock(log_lock); } + // Check that linfo is still on the function scope. + DEBUG_SYNC(thd, "after_show_binlog_events"); ret= FALSE; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index ca372f30368..5dfd4b6c9d3 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -1,5 +1,5 @@ -/* Copyright (c) 2000, 2010 Oracle and/or its affiliates. - 2009-2011 Monty Program Ab +/* Copyright (c) 2000, 2012 Oracle and/or its affiliates. + Copyright (c) 2009, 2012, Monty Program Ab This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -271,6 +271,8 @@ Item_equal *find_item_equal(COND_EQUAL *cond_equal, Field *field, bool *inherited_fl); JOIN_TAB *first_depth_first_tab(JOIN* join); JOIN_TAB *next_depth_first_tab(JOIN* join, JOIN_TAB* tab); +JOIN_TAB *first_breadth_first_tab(JOIN *join); +JOIN_TAB *next_breadth_first_tab(JOIN *join, JOIN_TAB *tab); /** This handles SELECT with and without UNION. @@ -987,7 +989,10 @@ JOIN::optimize() } eval_select_list_used_tables(); - + + if (optimize_constant_subqueries()) + DBUG_RETURN(1); + table_count= select_lex->leaf_tables.elements; if (setup_ftfuncs(select_lex)) /* should be after having->fix_fields */ @@ -1274,6 +1279,12 @@ JOIN::optimize() { conds= substitute_for_best_equal_field(NO_PARTICULAR_TAB, conds, cond_equal, map2table); + if (thd->is_error()) + { + error= 1; + DBUG_PRINT("error",("Error from substitute_for_best_equal")); + DBUG_RETURN(1); + } conds->update_used_tables(); DBUG_EXECUTE("where", print_where(conds, @@ -1294,6 +1305,12 @@ JOIN::optimize() *tab->on_expr_ref, tab->cond_equal, map2table); + if (thd->is_error()) + { + error= 1; + DBUG_PRINT("error",("Error from substitute_for_best_equal")); + DBUG_RETURN(1); + } (*tab->on_expr_ref)->update_used_tables(); } } @@ -6616,6 +6633,32 @@ void JOIN::get_prefix_cost_and_fanout(uint n_tables, /** + Estimate the number of rows that query execution will read. + + @todo This is a very pessimistic upper bound. Use join selectivity + when available to produce a more realistic number. +*/ + +double JOIN::get_examined_rows() +{ + ha_rows examined_rows; + double prev_fanout= 1; + JOIN_TAB *tab= first_breadth_first_tab(this); + JOIN_TAB *prev_tab= tab; + + examined_rows= tab->get_examined_rows(); + + while ((tab= next_breadth_first_tab(this, tab))) + { + prev_fanout *= prev_tab->records_read; + examined_rows+= tab->get_examined_rows() * prev_fanout; + prev_tab= tab; + } + return examined_rows; +} + + +/** Find a good, possibly optimal, query execution plan (QEP) by a possibly exhaustive search. @@ -8062,36 +8105,15 @@ JOIN::make_simple_join(JOIN *parent, TABLE *temp_table) row_limit= unit->select_limit_cnt; do_send_rows= row_limit ? 1 : 0; - join_tab->use_join_cache= FALSE; - join_tab->cache=0; /* No caching */ + bzero(join_tab, sizeof(JOIN_TAB)); join_tab->table=temp_table; - join_tab->cache_select= 0; - join_tab->select=0; - join_tab->select_cond= 0; // Avoid valgrind warning join_tab->set_select_cond(NULL, __LINE__); - join_tab->quick=0; join_tab->type= JT_ALL; /* Map through all records */ join_tab->keys.init(); join_tab->keys.set_all(); /* test everything in quick */ - join_tab->info=0; - join_tab->on_expr_ref=0; - join_tab->last_inner= 0; - join_tab->first_unmatched= 0; join_tab->ref.key = -1; - join_tab->not_used_in_distinct=0; join_tab->read_first_record= join_init_read_record; - join_tab->preread_init_done= FALSE; join_tab->join= this; - join_tab->ref.key_parts= 0; - join_tab->keep_current_rowid= FALSE; - join_tab->flush_weedout_table= join_tab->check_weed_out_table= NULL; - join_tab->do_firstmatch= NULL; - join_tab->loosescan_match_tab= NULL; - join_tab->emb_sj_nest= NULL; - join_tab->pre_idx_push_select_cond= NULL; - join_tab->bush_root_tab= NULL; - join_tab->bush_children= NULL; - join_tab->last_leaf_in_bush= FALSE; bzero((char*) &join_tab->read_record,sizeof(join_tab->read_record)); temp_table->status=0; temp_table->null_row=0; @@ -10289,6 +10311,51 @@ double JOIN_TAB::scan_time() return res; } + +/** + Estimate the number of rows that a an access method will read from a table. + + @todo: why not use JOIN_TAB::found_records +*/ + +ha_rows JOIN_TAB::get_examined_rows() +{ + ha_rows examined_rows; + + if (select && select->quick) + examined_rows= select->quick->records; + else if (type == JT_NEXT || type == JT_ALL || + type == JT_HASH || type ==JT_HASH_NEXT) + { + if (limit) + { + /* + @todo This estimate is wrong, a LIMIT query may examine much more rows + than the LIMIT itself. + */ + examined_rows= limit; + } + else + { + if (table->is_filled_at_execution()) + examined_rows= records; + else + { + /* + handler->info(HA_STATUS_VARIABLE) has been called in + make_join_statistics() + */ + examined_rows= table->file->stats.records; + } + } + } + else + examined_rows= (ha_rows) records_read; + + return examined_rows; +} + + /** Initialize the join_tab before reading. Currently only derived table/view materialization is done here. @@ -10592,6 +10659,22 @@ void JOIN::cleanup(bool full) tmp_join->tmp_table_param.save_copy_field= 0; } tmp_table_param.cleanup(); + + if (!join_tab) + { + List_iterator<TABLE_LIST> li(*join_list); + TABLE_LIST *table_ref; + while ((table_ref= li++)) + { + if (table_ref->table && + table_ref->jtbm_subselect && + table_ref->jtbm_subselect->is_jtbm_const_tab) + { + free_tmp_table(thd, table_ref->table); + table_ref->table= NULL; + } + } + } } DBUG_VOID_RETURN; } @@ -11268,9 +11351,9 @@ static bool check_simple_equality(Item *left_item, Item *right_item, if (!item) { Item_func_eq *eq_item; - if ((eq_item= new Item_func_eq(orig_left_item, orig_right_item))) + if (!(eq_item= new Item_func_eq(orig_left_item, orig_right_item)) || + eq_item->set_cmp_func()) return FALSE; - eq_item->set_cmp_func(); eq_item->quick_fix_field(); item= eq_item; } @@ -11363,9 +11446,9 @@ static bool check_row_equality(THD *thd, Item *left_row, Item_row *right_row, if (!is_converted) { Item_func_eq *eq_item; - if (!(eq_item= new Item_func_eq(left_item, right_item))) + if (!(eq_item= new Item_func_eq(left_item, right_item)) || + eq_item->set_cmp_func()) return FALSE; - eq_item->set_cmp_func(); eq_item->quick_fix_field(); eq_list->push_back(eq_item); } @@ -12051,9 +12134,8 @@ Item *eliminate_item_equal(COND *cond, COND_EQUAL *upper_levels, eq_item= new Item_func_eq(field_item->real_item(), head_item); - if (!eq_item) + if (!eq_item || eq_item->set_cmp_func()) return 0; - eq_item->set_cmp_func(); eq_item->quick_fix_field(); } current_sjm= field_sjm; @@ -12140,7 +12222,7 @@ Item *eliminate_item_equal(COND *cond, COND_EQUAL *upper_levels, Item_equal::get_first() for details. @return - The transformed condition + The transformed condition, or NULL in case of error */ static COND* substitute_for_best_equal_field(JOIN_TAB *context_tab, @@ -18595,8 +18677,6 @@ check_reverse_order: join_read_first:join_read_last; tab->type=JT_NEXT; // Read with index_first(), index_next() - if (table->covering_keys.is_set(best_key) && ! table->key_read) - table->enable_keyread(); if (tab->pre_idx_push_select_cond) { tab->set_cond(tab->pre_idx_push_select_cond); @@ -18607,6 +18687,7 @@ check_reverse_order: orig_cond= 0; orig_cond_saved= false; } + table->file->ha_index_or_rnd_end(); if (tab->join->select_options & SELECT_DESCRIBE) { @@ -18614,6 +18695,7 @@ check_reverse_order: tab->ref.key_parts= 0; if (select_limit < table->file->stats.records) tab->limit= select_limit; + table->disable_keyread(); } } else if (tab->type != JT_ALL) @@ -20215,6 +20297,8 @@ copy_fields(TMP_TABLE_PARAM *param) Copy_field *ptr=param->copy_field; Copy_field *end=param->copy_field_end; + DBUG_ASSERT((ptr != NULL && end >= ptr) || (ptr == NULL && end == NULL)); + for (; ptr != end; ptr++) (*ptr->do_copy)(ptr); @@ -21355,10 +21439,17 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, } else { - TABLE_LIST *real_table= table->pos_in_table_list; - item_list.push_back(new Item_string(real_table->alias, - strlen(real_table->alias), - cs)); + TABLE_LIST *real_table= table->pos_in_table_list; + /* + Internal temporary tables have no corresponding table reference + object. Such a table may appear in EXPLAIN when a subquery that needs + a temporary table has been executed, and JOIN::exec replaced the + original JOIN with a plan to access the data in the temp table + (made by JOIN::make_simple_join). + */ + const char *tab_name= real_table ? real_table->alias : + "internal_tmp_table"; + item_list.push_back(new Item_string(tab_name, strlen(tab_name), cs)); } /* "partitions" column */ if (join->thd->lex->describe & DESCRIBE_PARTITIONS) @@ -21516,32 +21607,8 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, } else { - ha_rows examined_rows; - if (tab->select && tab->select->quick) - examined_rows= tab->select->quick->records; - else if (tab->type == JT_NEXT || tab->type == JT_ALL || is_hj) - { - if (tab->limit) - examined_rows= tab->limit; - else - { - if (tab->table->is_filled_at_execution()) - { - examined_rows= tab->records; - } - else - { - /* - handler->info(HA_STATUS_VARIABLE) has been called in - make_join_statistics() - */ - examined_rows= tab->table->file->stats.records; - } - } - } - else - examined_rows=(ha_rows)tab->records_read; - + ha_rows examined_rows= tab->get_examined_rows(); + item_list.push_back(new Item_int((longlong) (ulonglong) examined_rows, MY_INT64_NUM_DECIMAL_DIGITS)); diff --git a/sql/sql_select.h b/sql/sql_select.h index c4553148cc6..0ed976ac36a 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -512,6 +512,7 @@ typedef struct st_join_table { return (is_hash_join_key_no(key) ? hj_key : table->key_info+key); } double scan_time(); + ha_rows get_examined_rows(); bool preread_init(); bool is_sjm_nest() { return test(bush_children); } @@ -1281,6 +1282,7 @@ public: bool alloc_func_list(); bool flatten_subqueries(); bool optimize_unflattened_subqueries(); + bool optimize_constant_subqueries(); bool make_sum_func_list(List<Item> &all_fields, List<Item> &send_fields, bool before_group_by, bool recompute= FALSE); @@ -1380,6 +1382,7 @@ public: void get_prefix_cost_and_fanout(uint n_tables, double *read_time_arg, double *record_count_arg); + double get_examined_rows(); /* defined in opt_subselect.cc */ bool transform_max_min_subquery(); /* True if this JOIN is a subquery under an IN predicate. */ diff --git a/sql/sql_show.cc b/sql/sql_show.cc index f714388eaee..1e2ed90e1b7 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1,5 +1,5 @@ /* Copyright (c) 2000, 2012, Oracle and/or its affiliates. - Copyright (c) 2009, 2011, Monty Program Ab + Copyright (c) 2009, 2012, Monty Program Ab This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 13411bea8a3..031932b4c06 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -238,10 +238,17 @@ uint explain_filename(THD* thd, { part_name_len= tmp_p - part_name - 1; subpart_name= tmp_p + 3; + tmp_p+= 3; + } + else if ((tmp_p[1] == 'Q' || tmp_p[1] == 'q') && + (tmp_p[2] == 'L' || tmp_p[2] == 'l') && + tmp_p[3] == '-') + { + name_type= TEMP; + tmp_p+= 4; /* sql- prefix found */ } else res= 2; - tmp_p+= 3; break; case 'T': case 't': @@ -6926,21 +6933,47 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, (void) quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP); } else if (mysql_rename_table(new_db_type, new_db, tmp_name, new_db, - new_alias, FN_FROM_IS_TMP) || - ((new_name != table_name || new_db != db) && // we also do rename - (need_copy_table != ALTER_TABLE_METADATA_ONLY || - mysql_rename_table(save_old_db_type, db, table_name, new_db, - new_alias, NO_FRM_RENAME)) && - Table_triggers_list::change_table_name(thd, db, alias, table_name, - new_db, new_alias))) + new_alias, FN_FROM_IS_TMP)) { /* Try to get everything back. */ - error=1; - (void) quick_rm_table(new_db_type,new_db,new_alias, 0); + error= 1; (void) quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP); (void) mysql_rename_table(old_db_type, db, old_name, db, alias, FN_FROM_IS_TMP); } + else if (new_name != table_name || new_db != db) + { + if (need_copy_table == ALTER_TABLE_METADATA_ONLY && + mysql_rename_table(save_old_db_type, db, table_name, new_db, + new_alias, NO_FRM_RENAME)) + { + /* Try to get everything back. */ + error= 1; + (void) quick_rm_table(new_db_type, new_db, new_alias, 0); + (void) mysql_rename_table(old_db_type, db, old_name, db, alias, + FN_FROM_IS_TMP); + } + else if (Table_triggers_list::change_table_name(thd, db, alias, + table_name, new_db, + new_alias)) + { + /* Try to get everything back. */ + error= 1; + (void) quick_rm_table(new_db_type, new_db, new_alias, 0); + (void) mysql_rename_table(old_db_type, db, old_name, db, + alias, FN_FROM_IS_TMP); + /* + If we were performing "fast"/in-place ALTER TABLE we also need + to restore old name of table in storage engine as a separate + step, as the above rename affects .FRM only. + */ + if (need_copy_table == ALTER_TABLE_METADATA_ONLY) + { + (void) mysql_rename_table(save_old_db_type, new_db, new_alias, + db, table_name, NO_FRM_RENAME); + } + } + } if (! error) (void) quick_rm_table(old_db_type, db, old_name, FN_IS_TMP); @@ -7360,7 +7393,10 @@ err: thd_progress_next_stage(thd); if (error > 0) + { + /* We are going to drop the temporary table */ to->file->extra(HA_EXTRA_PREPARE_FOR_DROP); + } if (errpos >= 3 && to->file->ha_end_bulk_insert() && error <= 0) { to->file->print_error(my_errno,MYF(0)); diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 7d5fe875d64..f2b6c5c9f92 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -368,7 +368,7 @@ int mysql_update(THD *thd, } /* Apply the IN=>EXISTS transformation to all subqueries and optimize them. */ - if (select_lex->optimize_unflattened_subqueries()) + if (select_lex->optimize_unflattened_subqueries(false)) DBUG_RETURN(TRUE); if (select_lex->inner_refs_list.elements && diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index e968dd12ca0..4e3629080be 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -9823,7 +9823,9 @@ table_factor: lex->nest_level--; } else if (($3->select_lex && - $3->select_lex->master_unit()->is_union()) || $5) + $3->select_lex->master_unit()->is_union() && + ($3->select_lex->master_unit()->first_select() == + $3->select_lex || !$3->lifted)) || $5) { /* simple nested joins cannot have aliases or unions */ my_parse_error(ER(ER_SYNTAX_ERROR)); diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index ba37f88fdf8..4df3773e5ce 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -2246,7 +2246,7 @@ static char *system_time_zone_ptr; static Sys_var_charptr Sys_system_time_zone( "system_time_zone", "The server system time zone", READ_ONLY GLOBAL_VAR(system_time_zone_ptr), NO_CMD_LINE, - IN_FS_CHARSET, DEFAULT(system_time_zone)); + IN_SYSTEM_CHARSET, DEFAULT(system_time_zone)); static Sys_var_ulong Sys_table_def_size( "table_definition_cache", @@ -3768,4 +3768,9 @@ static Sys_var_ulong Sys_debug_binlog_fsync_sleep( CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, UINT_MAX), DEFAULT(0), BLOCK_SIZE(1)); #endif - +static Sys_var_harows Sys_expensive_subquery_limit( + "expensive_subquery_limit", + "The maximum number of rows a subquery may examine in order to be " + "executed during optimization and used for constant optimization", + SESSION_VAR(expensive_subquery_limit), CMD_LINE(REQUIRED_ARG), + VALID_RANGE(0, HA_POS_ERROR), DEFAULT(100), BLOCK_SIZE(1)); diff --git a/sql/table.cc b/sql/table.cc index 40304dc6fdc..2a11098caed 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -1439,25 +1439,33 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, { /* Get virtual column data stored in the .frm file as follows: - byte 1 = 1 (always 1 to allow for future extensions) + byte 1 = 1 | 2 byte 2 = sql_type byte 3 = flags (as of now, 0 - no flags, 1 - field is physically stored) - byte 4-... = virtual column expression (text data) + [byte 4] = optional interval_id for sql_type (only if byte 1 == 2) + next byte ... = virtual column expression (text data) */ vcol_info= new Virtual_column_info(); - if ((uint)vcol_screen_pos[0] != 1) + bool opt_interval_id= (uint)vcol_screen_pos[0] == 2; + field_type= (enum_field_types) (uchar) vcol_screen_pos[1]; + if (opt_interval_id) + interval_nr= (uint)vcol_screen_pos[3]; + else if ((uint)vcol_screen_pos[0] != 1) { error= 4; goto err; } - field_type= (enum_field_types) (uchar) vcol_screen_pos[1]; fld_stored_in_db= (bool) (uint) vcol_screen_pos[2]; - vcol_expr_length= vcol_info_length-(uint)FRM_VCOL_HEADER_SIZE; + vcol_expr_length= vcol_info_length - + (uint)(FRM_VCOL_HEADER_SIZE(opt_interval_id)); if (!(vcol_info->expr_str.str= (char *)memdup_root(&share->mem_root, - vcol_screen_pos+(uint)FRM_VCOL_HEADER_SIZE, + vcol_screen_pos + + (uint) FRM_VCOL_HEADER_SIZE(opt_interval_id), vcol_expr_length))) goto err; + if (opt_interval_id) + interval_nr= (uint) vcol_screen_pos[3]; vcol_info->expr_str.length= vcol_expr_length; vcol_screen_pos+= vcol_info_length; share->vfields++; @@ -5985,7 +5993,13 @@ void TABLE::use_index(int key_to_save) bool TABLE::is_filled_at_execution() { - return test(pos_in_table_list->jtbm_subselect || + /* + pos_in_table_list == NULL for internal temporary tables because they + do not have a corresponding table reference. Such tables are filled + during execution. + */ + return test(!pos_in_table_list || + pos_in_table_list->jtbm_subselect || pos_in_table_list->is_active_sjm()); } diff --git a/sql/table.h b/sql/table.h index f3f9d5ac036..87affe984fc 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1800,6 +1800,8 @@ struct TABLE_LIST struct st_nested_join *nested_join; /* if the element is a nested join */ TABLE_LIST *embedding; /* nested join containing the table */ List<TABLE_LIST> *join_list;/* join list the table belongs to */ + bool lifted; /* set to true when the table is moved to + the upper level at the parsing stage */ bool cacheable_table; /* stop PS caching */ /* used in multi-upd/views privilege check */ bool table_in_first_from_clause; diff --git a/sql/unireg.cc b/sql/unireg.cc index c9b0f91d9f7..edcfe9eb934 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -726,18 +726,19 @@ static bool pack_header(uchar *forminfo, enum legacy_db_type table_type, } if (field->vcol_info) { + uint col_expr_maxlen= field->virtual_col_expr_maxlen(); tmp_len= system_charset_info->cset->charpos(system_charset_info, field->vcol_info->expr_str.str, field->vcol_info->expr_str.str + field->vcol_info->expr_str.length, - VIRTUAL_COLUMN_EXPRESSION_MAXLEN); + col_expr_maxlen); if (tmp_len < field->vcol_info->expr_str.length) { my_error(ER_WRONG_STRING_LENGTH, MYF(0), field->vcol_info->expr_str.str,"VIRTUAL COLUMN EXPRESSION", - (uint) VIRTUAL_COLUMN_EXPRESSION_MAXLEN); + col_expr_maxlen); DBUG_RETURN(1); } /* @@ -746,7 +747,7 @@ static bool pack_header(uchar *forminfo, enum legacy_db_type table_type, expressions saved in the frm file for virtual columns. */ vcol_info_length+= field->vcol_info->expr_str.length+ - (uint)FRM_VCOL_HEADER_SIZE; + FRM_VCOL_HEADER_SIZE(field->interval!=NULL); } totlength+= field->length; @@ -949,8 +950,9 @@ static bool pack_fields(File file, List<Create_field> &create_fields, the additional data saved for the virtual field */ buff[12]= cur_vcol_expr_len= field->vcol_info->expr_str.length + - (uint)FRM_VCOL_HEADER_SIZE; - vcol_info_length+= cur_vcol_expr_len+(uint)FRM_VCOL_HEADER_SIZE; + FRM_VCOL_HEADER_SIZE(field->interval!=NULL); + vcol_info_length+= cur_vcol_expr_len + + FRM_VCOL_HEADER_SIZE(field->interval!=NULL); buff[13]= (uchar) MYSQL_TYPE_VIRTUAL; } int2store(buff+15, field->comment.length); @@ -1055,17 +1057,20 @@ static bool pack_fields(File file, List<Create_field> &create_fields, { /* Pack each virtual field as follows: - byte 1 = 1 (always 1 to allow for future extensions) + byte 1 = interval_id == 0 ? 1 : 2 byte 2 = sql_type byte 3 = flags (as of now, 0 - no flags, 1 - field is physically stored) - byte 4-... = virtual column expression (text data) + [byte 4] = possible interval_id for sql_type + next byte ... = virtual column expression (text data) */ if (field->vcol_info && field->vcol_info->expr_str.length) { - buff[0]= (uchar)1; + buff[0]= (uchar)(1 + test(field->interval_id)); buff[1]= (uchar) field->sql_type; buff[2]= (uchar) field->stored_in_db; - if (my_write(file, buff, 3, MYF_RW)) + if (field->interval_id) + buff[3]= (uchar) field->interval_id; + if (my_write(file, buff, 3 + test(field->interval_id), MYF_RW)) DBUG_RETURN(1); if (my_write(file, (uchar*) field->vcol_info->expr_str.str, diff --git a/sql/unireg.h b/sql/unireg.h index f8317a89c8c..da510bb4e6d 100644 --- a/sql/unireg.h +++ b/sql/unireg.h @@ -161,12 +161,6 @@ typedef struct st_ha_create_information HA_CREATE_INFO; #define DEFAULT_KEY_CACHE_NAME "default" -/* The length of the header part for each virtual column in the .frm file */ -#define FRM_VCOL_HEADER_SIZE 3 - -/* Maximum length of the defining expression for a virtual columns */ -#define VIRTUAL_COLUMN_EXPRESSION_MAXLEN 255 - FRM_VCOL_HEADER_SIZE - /* Include prototypes for unireg */ |