diff options
Diffstat (limited to 'sql')
68 files changed, 1665 insertions, 759 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 597ca7d874b..21a7444bf3d 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -37,7 +37,9 @@ SET_SOURCE_FILES_PROPERTIES(${CMAKE_BINARY_DIR}/sql/sql_yacc.h PROPERTIES GENERATED 1) ADD_DEFINITIONS(-DMYSQL_SERVER -D_CONSOLE -DHAVE_DLOPEN -DHAVE_EVENT_SCHEDULER) - +IF(WITH_FEEDBACK_STORAGE_ENGINE) + ADD_DEFINITIONS(-DWITH_FEEDBACK_PLUGIN) +ENDIF() SET (SQL_SOURCE ../sql-common/client.c derror.cc des_key_file.cc diff --git a/sql/debug_sync.cc b/sql/debug_sync.cc index 322db38adf2..5e5e5a72850 100644 --- a/sql/debug_sync.cc +++ b/sql/debug_sync.cc @@ -1078,7 +1078,7 @@ static bool debug_sync_set_action(THD *thd, st_debug_sync_action *action) point decremented it to 0. In this case the following happened: - an error message was reported with my_error() and - - the statement was killed with thd->killed= THD::KILL_QUERY. + - the statement was killed with thd->killed= KILL_QUERY. If a statement reports an error, it must not call send_ok(). The calling functions will not call send_ok(), if we return TRUE @@ -1852,7 +1852,7 @@ static void debug_sync_execute(THD *thd, st_debug_sync_action *action) { if (!--action->hit_limit) { - thd->killed= THD::KILL_QUERY; + thd->killed= KILL_QUERY; my_error(ER_DEBUG_SYNC_HIT_LIMIT, MYF(0)); } DBUG_PRINT("debug_sync_exec", ("hit_limit: %lu at: '%s'", diff --git a/sql/event_scheduler.cc b/sql/event_scheduler.cc index ecddcb7ca46..240d2df8e65 100644 --- a/sql/event_scheduler.cc +++ b/sql/event_scheduler.cc @@ -642,7 +642,7 @@ Event_scheduler::stop() sql_print_information("Event Scheduler: Killing the scheduler thread, " "thread id %lu", scheduler_thd->thread_id); - scheduler_thd->awake(THD::KILL_CONNECTION); + scheduler_thd->awake(KILL_CONNECTION); pthread_mutex_unlock(&scheduler_thd->LOCK_thd_data); /* thd could be 0x0, when shutting down */ diff --git a/sql/field_conv.cc b/sql/field_conv.cc index afc4d8252ff..588a99f560f 100644 --- a/sql/field_conv.cc +++ b/sql/field_conv.cc @@ -204,6 +204,14 @@ static void do_skip(Copy_field *copy __attribute__((unused))) } +/* + Copy: (NULLable field) -> (NULLable field) + + note: if the record we're copying from is NULL-complemetned (i.e. + from_field->table->null_row==1), it will also have all NULLable columns to be + set to NULLs, so we dont need to check table->null_row here. +*/ + static void do_copy_null(Copy_field *copy) { if (*copy->from_null_ptr & copy->from_bit) @@ -218,6 +226,10 @@ static void do_copy_null(Copy_field *copy) } } +/* + Copy: (not-NULL field in table that can be NULL-complemented) -> (NULLable + field) +*/ static void do_outer_field_null(Copy_field *copy) { @@ -235,6 +247,7 @@ static void do_outer_field_null(Copy_field *copy) } +/* Copy: (NULL-able field) -> (not NULL-able field) */ static void do_copy_not_null(Copy_field *copy) { if (*copy->from_null_ptr & copy->from_bit) @@ -248,6 +261,7 @@ static void do_copy_not_null(Copy_field *copy) } +/* Copy: (non-NULLable field) -> (NULLable field) */ static void do_copy_maybe_null(Copy_field *copy) { *copy->to_null_ptr&= ~copy->to_bit; diff --git a/sql/filesort.cc b/sql/filesort.cc index 559f4f1dcf6..da275c1c14f 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -506,7 +506,7 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, my_off_t record; TABLE *sort_form; THD *thd= current_thd; - volatile THD::killed_state *killed= &thd->killed; + volatile killed_state *killed= &thd->killed; handler *file; MY_BITMAP *save_read_set, *save_write_set, *save_vcol_set; DBUG_ENTER("find_all_keys"); @@ -624,15 +624,21 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, SQL_SELECT::skip_record evaluates this condition. it may include a correlated subquery predicate, such that some field in the subquery refers to 'sort_form'. + + PSergey-todo: discuss the above with Timour. */ + MY_BITMAP *tmp_read_set= sort_form->read_set; + MY_BITMAP *tmp_write_set= sort_form->write_set; + MY_BITMAP *tmp_vcol_set= sort_form->vcol_set; + if (select->cond->with_subselect) sort_form->column_bitmaps_set(save_read_set, save_write_set, save_vcol_set); write_record= (select->skip_record(thd) > 0); if (select->cond->with_subselect) - sort_form->column_bitmaps_set(&sort_form->tmp_set, - &sort_form->tmp_set, - &sort_form->tmp_set); + sort_form->column_bitmaps_set(tmp_read_set, + tmp_write_set, + tmp_vcol_set); } else write_record= true; @@ -1233,9 +1239,9 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file, void *first_cmp_arg; element_count dupl_count= 0; uchar *src; - THD::killed_state not_killable; + killed_state not_killable; uchar *unique_buff= param->unique_buff; - volatile THD::killed_state *killed= ¤t_thd->killed; + volatile killed_state *killed= ¤t_thd->killed; DBUG_ENTER("merge_buffers"); status_var_increment(current_thd->status_var.filesort_merge_passes); @@ -1243,7 +1249,7 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file, if (param->not_killable) { killed= ¬_killable; - not_killable= THD::NOT_KILLED; + not_killable= NOT_KILLED; } error=0; diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc index 77851119e34..5a3365b29d2 100644 --- a/sql/ha_ndbcluster_binlog.cc +++ b/sql/ha_ndbcluster_binlog.cc @@ -1855,7 +1855,7 @@ static void ndb_binlog_query(THD *thd, Cluster_schema *schema) else thd->server_id= schema->any_value; thd->db= schema->db; - int errcode = query_error_code(thd, thd->killed == THD::NOT_KILLED); + int errcode = query_error_code(thd, thd->killed == NOT_KILLED); thd->binlog_query(THD::STMT_QUERY_TYPE, schema->query, schema->query_length, FALSE, schema->name[0] == 0 || thd->db[0] == 0, diff --git a/sql/handler.cc b/sql/handler.cc index 751e31fdf9f..66e22aa40dc 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -1403,9 +1403,13 @@ int ha_rollback_trans(THD *thd, bool all) slave SQL thread, it would not stop the thread but just be printed in the error log; but we don't want users to wonder why they have this message in the error log, so we don't send it. + + We don't have to test for thd->killed == KILL_SYSTEM_THREAD as + it doesn't matter if a warning is pushed to a system thread or not: + No one will see it... */ if (is_real_trans && thd->transaction.all.modified_non_trans_table && - !thd->slave_thread && thd->killed != THD::KILL_CONNECTION) + !thd->slave_thread && thd->killed < KILL_CONNECTION) push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARNING_NOT_COMPLETE_ROLLBACK, ER(ER_WARNING_NOT_COMPLETE_ROLLBACK)); @@ -2559,7 +2563,7 @@ int handler::update_auto_increment() /* first test if the query was aborted due to strict mode constraints */ - if (thd->killed == THD::KILL_BAD_DATA) + if (killed_mask_hard(thd->killed) == KILL_BAD_DATA) DBUG_RETURN(HA_ERR_AUTOINC_ERANGE); /* @@ -4716,7 +4720,7 @@ static bool check_table_binlog_row_based(THD *thd, TABLE *table) /** @brief Write table maps for all (manually or automatically) locked tables - to the binary log. Also, if binlog_annotate_rows_events is ON, + to the binary log. Also, if binlog_annotate_row_events is ON, write Annotate_rows event before the first table map. SYNOPSIS @@ -4754,7 +4758,7 @@ static int write_locked_table_maps(THD *thd) locks[0]= thd->extra_lock; locks[1]= thd->lock; locks[2]= thd->locked_tables; - my_bool with_annotate= thd->variables.binlog_annotate_rows_events && + my_bool with_annotate= thd->variables.binlog_annotate_row_events && thd->query() && thd->query_length(); for (uint i= 0 ; i < sizeof(locks)/sizeof(*locks) ; ++i ) diff --git a/sql/item.cc b/sql/item.cc index ca4d41fcbdb..f5ce0fbc7a7 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -2172,6 +2172,11 @@ bool Item_field::enumerate_field_refs_processor(uchar *arg) return FALSE; } +bool Item_field::update_table_bitmaps_processor(uchar *arg) +{ + update_table_bitmaps(); + return FALSE; +} const char *Item_ident::full_name() const { @@ -4648,6 +4653,7 @@ bool Item_field::fix_fields(THD *thd, Item **reference) if (!outer_fixed && cached_table && cached_table->select_lex && context->select_lex && cached_table->select_lex != context->select_lex && + !context->select_lex->is_merged_child_of(cached_table->select_lex) && is_outer_table(cached_table, context->select_lex)) { int ret; @@ -5191,6 +5197,10 @@ Field *Item::make_string_field(TABLE *table) { Field *field; DBUG_ASSERT(collation.collation); + /* + Note: the following check is repeated in + subquery_types_allow_materialization(): + */ if (max_length/collation.collation->mbmaxlen > CONVERT_IF_BIGGER_TO_BLOB) field= new Field_blob(max_length, maybe_null, name, collation.collation); @@ -5977,7 +5987,7 @@ bool Item::send(Protocol *protocol, String *buffer) case MYSQL_TYPE_TIMESTAMP: { MYSQL_TIME tm; - get_date(&tm, TIME_FUZZY_DATE); + get_date(&tm, TIME_FUZZY_DATE | sql_mode_for_dates()); if (!null_value) { if (f_type == MYSQL_TYPE_DATE) @@ -9102,6 +9112,12 @@ table_map Item_direct_view_ref::used_tables() const (view->merged ? (*ref)->used_tables() : view->table->map); } +table_map Item_direct_view_ref::not_null_tables() const +{ + return get_depended_from() ? + 0 : + (view->merged ? (*ref)->not_null_tables() : view->table->map); +} /* we add RAND_TABLE_BIT to prevent moving this item from HAVING to WHERE @@ -9114,7 +9130,22 @@ table_map Item_ref_null_helper::used_tables() const } +/* Debugger help function */ +static char dbug_item_print_buf[256]; +const char *dbug_print_item(Item *item) +{ + char *buf= dbug_item_print_buf; + String str(buf, sizeof(dbug_item_print_buf), &my_charset_bin); + str.length(0); + if (!item) + return "(Item*)NULL"; + item->print(&str ,QT_ORDINARY); + if (str.c_ptr() == buf) + return buf; + else + return "Couldn't fit into buffer"; +} /***************************************************************************** ** Instantiate templates *****************************************************************************/ diff --git a/sql/item.h b/sql/item.h index 319135ede9d..bfe0d78ed29 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1018,6 +1018,7 @@ public: virtual bool mark_as_eliminated_processor(uchar *arg) { return 0; } virtual bool eliminate_subselect_processor(uchar *arg) { return 0; } virtual bool set_fake_select_as_master_processor(uchar *arg) { return 0; } + virtual bool update_table_bitmaps_processor(uchar *arg) { return 0; } virtual bool view_used_tables_processor(uchar *arg) { return 0; } virtual bool eval_not_null_tables(uchar *opt_arg) { return 0; } virtual bool clear_sum_processor(uchar *opt_arg) { return 0; } @@ -1292,6 +1293,8 @@ public: be defined for Item_func. */ virtual void get_cache_parameters(List<Item> ¶meters) { }; + + virtual void mark_as_condition_AND_part(TABLE_LIST *embedding) {}; }; @@ -1814,6 +1817,20 @@ public: bool get_date_result(MYSQL_TIME *ltime,uint fuzzydate); bool is_null() { return field->is_null(); } void update_null_value(); + void update_table_bitmaps() + { + if (field && field->table) + { + TABLE *tab= field->table; + tab->covering_keys.intersect(field->part_of_key); + tab->merge_keys.merge(field->part_of_key); + if (tab->read_set) + bitmap_fast_test_and_set(tab->read_set, field->field_index); + if (field->vcol_info) + tab->mark_virtual_col(field); + } + } + void update_used_tables() { update_table_bitmaps(); } Item *get_tmp_table_item(THD *thd); bool collect_item_field_processor(uchar * arg); bool add_field_to_set_processor(uchar * arg); @@ -1825,6 +1842,7 @@ public: bool vcol_in_partition_func_processor(uchar *bool_arg); bool check_vcol_func_processor(uchar *arg) { return FALSE;} bool enumerate_field_refs_processor(uchar *arg); + bool update_table_bitmaps_processor(uchar *arg); void cleanup(); Item_equal *get_item_equal() { return item_equal; } void set_item_equal(Item_equal *item_eq) { item_equal= item_eq; } @@ -2917,6 +2935,7 @@ public: Item *equal_fields_propagator(uchar *arg); Item *replace_equal_field(uchar *arg); table_map used_tables() const; + table_map not_null_tables() const; bool walk(Item_processor processor, bool walk_subquery, uchar *arg) { return (*ref)->walk(processor, walk_subquery, arg) || diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 721c9b6a5f7..6f8e47264ad 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -29,8 +29,6 @@ #include <m_ctype.h> #include "sql_select.h" -static bool convert_const_to_int(THD *, Item_field *, Item **); - static Item_result item_store_type(Item_result a, Item *item, my_bool unsigned_flag) { @@ -515,7 +513,6 @@ static bool convert_const_to_int(THD *thd, Item_field *field_item, void Item_bool_func2::fix_length_and_dec() { max_length= 1; // Function returns 0 or 1 - THD *thd; /* As some compare functions are generated after sql_yacc, @@ -547,14 +544,14 @@ void Item_bool_func2::fix_length_and_dec() /* Make a special case of compare with fields to get nicer comparisons - of numbers with constant string. + of bigint numbers with constant string. This directly contradicts the manual (number and a string should be compared as doubles), but seems to provide more "intuitive" behavior in some cases (but less intuitive in others). But disable conversion in case of LIKE function. */ - thd= current_thd; + THD *thd= current_thd; if (functype() != LIKE_FUNC && !thd->lex->is_ps_or_view_context_analysis()) { int field; @@ -562,7 +559,8 @@ void Item_bool_func2::fix_length_and_dec() args[field= 1]->real_item()->type() == FIELD_ITEM) { Item_field *field_item= (Item_field*) (args[field]->real_item()); - if (field_item->cmp_type() == INT_RESULT && + if ((field_item->field_type() == MYSQL_TYPE_LONGLONG || + field_item->field_type() == MYSQL_TYPE_YEAR) && convert_const_to_int(thd, field_item, &args[!field])) args[0]->cmp_context= args[1]->cmp_context= INT_RESULT; } @@ -1399,6 +1397,36 @@ longlong Item_func_truth::val_int() } +bool Item_in_optimizer::is_top_level_item() +{ + return ((Item_in_subselect *)args[1])->is_top_level_item(); +} + + +void Item_in_optimizer::fix_after_pullout(st_select_lex *new_parent, Item **ref) +{ + /* This will re-calculate attributes of our Item_in_subselect: */ + Item_bool_func::fix_after_pullout(new_parent, ref); + + /* Then, re-calculate not_null_tables_cache: */ + eval_not_null_tables(NULL); +} + + +bool Item_in_optimizer::eval_not_null_tables(uchar *opt_arg) +{ + not_null_tables_cache= 0; + if (is_top_level_item()) + { + /* + It is possible to determine NULL-rejectedness of the left arguments + of IN only if it is a top-level predicate. + */ + not_null_tables_cache= args[0]->not_null_tables(); + } + return FALSE; +} + bool Item_in_optimizer::fix_left(THD *thd, Item **ref) { if ((!args[0]->fixed && args[0]->fix_fields(thd, args)) || @@ -1425,7 +1453,7 @@ bool Item_in_optimizer::fix_left(THD *thd, Item **ref) } used_tables_cache= args[0]->used_tables(); } - not_null_tables_cache= args[0]->not_null_tables(); + eval_not_null_tables(NULL); with_sum_func= args[0]->with_sum_func; with_field= args[0]->with_field; if ((const_item_cache= args[0]->const_item())) @@ -1458,7 +1486,6 @@ bool Item_in_optimizer::fix_fields(THD *thd, Item **ref) 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(); - not_null_tables_cache|= args[1]->not_null_tables(); const_item_cache&= args[1]->const_item(); fixed= 1; return FALSE; @@ -1785,7 +1812,7 @@ Item *Item_in_optimizer::transform(Item_transformer transformer, uchar *argument Item_subselect::ANY_SUBS)); Item_in_subselect *in_arg= (Item_in_subselect*)args[1]; - in_arg->left_expr= args[0]; + current_thd->change_item_tree(&in_arg->left_expr, args[0]); } return (this->*transformer)(argument); } @@ -2117,6 +2144,14 @@ bool Item_func_between::eval_not_null_tables(uchar *opt_arg) } +void Item_func_between::fix_after_pullout(st_select_lex *new_parent, Item **ref) +{ + /* This will re-calculate attributes of the arguments */ + Item_func_opt_neg::fix_after_pullout(new_parent, ref); + /* Then, re-calculate not_null_tables_cache according to our special rules */ + eval_not_null_tables(NULL); +} + void Item_func_between::fix_length_and_dec() { THD *thd= current_thd; @@ -2151,7 +2186,8 @@ void Item_func_between::fix_length_and_dec() !thd->lex->is_ps_or_view_context_analysis()) { Item_field *field_item= (Item_field*) (args[0]->real_item()); - if (field_item->cmp_type() == INT_RESULT) + if (field_item->field_type() == MYSQL_TYPE_LONGLONG || + field_item->field_type() == MYSQL_TYPE_YEAR) { /* The following can't be recoded with || as convert_const_to_int @@ -2497,6 +2533,16 @@ Item_func_if::eval_not_null_tables(uchar *opt_arg) return 0; } + +void Item_func_if::fix_after_pullout(st_select_lex *new_parent, Item **ref) +{ + /* This will re-calculate attributes of the arguments */ + Item_func::fix_after_pullout(new_parent, ref); + /* Then, re-calculate not_null_tables_cache according to our special rules */ + eval_not_null_tables(NULL); +} + + void Item_func_if::fix_length_and_dec() { @@ -2744,7 +2790,7 @@ Item *Item_func_case::find_item(String *str) { if (args[i]->real_item()->type() == NULL_ITEM) continue; - cmp_type= item_cmp_type(left_result_type, args[i]->result_type()); + cmp_type= item_cmp_type(left_result_type, args[i]->cmp_type()); DBUG_ASSERT(cmp_type != ROW_RESULT); DBUG_ASSERT(cmp_items[(uint)cmp_type]); if (!(value_added_map & (1<<(uint)cmp_type))) @@ -2909,7 +2955,7 @@ void Item_func_case::fix_length_and_dec() { uint i; agg[0]= args[first_expr_num]; - left_result_type= agg[0]->result_type(); + left_result_type= agg[0]->cmp_type(); for (nagg= 0; nagg < ncases/2 ; nagg++) agg[nagg+1]= args[nagg*2]; @@ -2927,17 +2973,21 @@ void Item_func_case::fix_length_and_dec() found_types |= (1 << item_cmp_type(left_result_type, STRING_RESULT)); } + 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); - DBUG_ASSERT((Item_result)i != TIME_RESULT); if ((Item_result)i == STRING_RESULT && agg_arg_charsets(cmp_collation, agg, nagg, MY_COLL_CMP_CONV, 1)) return; + + 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, 0, + cmp_item::get_comparator((Item_result)i, date_arg, cmp_collation.collation))) return; } @@ -3528,10 +3578,13 @@ void cmp_item_row::store_value(Item *item) for (uint i=0; i < n; i++) { if (!comparators[i]) + { + DBUG_ASSERT(item->element_index(i)->cmp_type() != TIME_RESULT); if (!(comparators[i]= cmp_item::get_comparator(item->element_index(i)->result_type(), 0, item->element_index(i)->collation.collation))) break; // new failed + } comparators[i]->store_value(item->element_index(i)); item->null_value|= item->element_index(i)->null_value; } @@ -3734,6 +3787,14 @@ Item_func_in::eval_not_null_tables(uchar *opt_arg) } +void Item_func_in::fix_after_pullout(st_select_lex *new_parent, Item **ref) +{ + /* This will re-calculate attributes of the arguments */ + Item_func_opt_neg::fix_after_pullout(new_parent, ref); + /* Then, re-calculate not_null_tables_cache according to our special rules */ + eval_not_null_tables(NULL); +} + static int srtcmp_in(CHARSET_INFO *cs, const String *x,const String *y) { return cs->coll->strnncollsp(cs, @@ -3832,7 +3893,8 @@ void Item_func_in::fix_length_and_dec() !thd->lex->is_ps_or_view_context_analysis() && cmp_type != INT_RESULT) { Item_field *field_item= (Item_field*) (args[0]->real_item()); - if (field_item->cmp_type() == INT_RESULT) + if (field_item->field_type() == MYSQL_TYPE_LONGLONG || + field_item->field_type() == MYSQL_TYPE_YEAR) { bool all_converted= TRUE; for (arg=args+1, arg_end=args+arg_count; arg != arg_end ; arg++) @@ -4057,15 +4119,12 @@ Item_cond::fix_fields(THD *thd, Item **ref) DBUG_ASSERT(fixed == 0); List_iterator<Item> li(list); Item *item; - TABLE_LIST *save_emb_on_expr_nest= thd->thd_marker.emb_on_expr_nest; #ifndef EMBEDDED_LIBRARY uchar buff[sizeof(char*)]; // Max local vars in function #endif not_null_tables_cache= used_tables_cache= 0; const_item_cache= 1; - if (functype() != COND_AND_FUNC) - thd->thd_marker.emb_on_expr_nest= NULL; /* and_table_cache is the value that Item_cond_or() returns for not_null_tables() @@ -4125,7 +4184,6 @@ Item_cond::fix_fields(THD *thd, Item **ref) maybe_null=1; } thd->lex->current_select->cond_count+= list.elements; - thd->thd_marker.emb_on_expr_nest= save_emb_on_expr_nest; fix_length_and_dec(); fixed= 1; return FALSE; @@ -4137,6 +4195,7 @@ Item_cond::eval_not_null_tables(uchar *opt_arg) { Item *item; List_iterator<Item> li(list); + not_null_tables_cache= (table_map) 0; and_tables_cache= ~(table_map) 0; while ((item=li++)) { @@ -4395,6 +4454,17 @@ void Item_cond::neg_arguments(THD *thd) } +void Item_cond_and::mark_as_condition_AND_part(TABLE_LIST *embedding) +{ + List_iterator<Item> li(list); + Item *item; + while ((item=li++)) + { + item->mark_as_condition_AND_part(embedding); + } +} + + /** Evaluation of AND(expr, expr, expr ...). @@ -5638,7 +5708,7 @@ longlong Item_equal::val_int() void Item_equal::fix_length_and_dec() { Item *item= get_first(NULL); - eval_item= cmp_item::get_comparator(item->result_type(), 0, + eval_item= cmp_item::get_comparator(item->cmp_type(), item, item->collation.collation); } @@ -5741,7 +5811,6 @@ Item* Item_equal::get_first(Item *field_item) { Item_equal_fields_iterator it(*this); Item *item; - JOIN_TAB *field_tab; if (!field_item) return (it++); Field *field= ((Item_field *) (field_item->real_item()))->field; @@ -5766,8 +5835,6 @@ Item* Item_equal::get_first(Item *field_item) in presense of SJM nests. */ - field_tab= field->table->reginfo.join_tab; - TABLE_LIST *emb_nest= field->table->pos_in_table_list->embedding; if (emb_nest && emb_nest->sj_mat_info && emb_nest->sj_mat_info->is_used) diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 6ae7309b241..81a18bb594e 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -260,6 +260,9 @@ public: void set_join_tab_idx(uint join_tab_idx_arg) { args[1]->set_join_tab_idx(join_tab_idx_arg); } virtual void get_cache_parameters(List<Item> ¶meters); + bool is_top_level_item(); + bool eval_not_null_tables(uchar *opt_arg); + void fix_after_pullout(st_select_lex *new_parent, Item **ref); }; class Comp_creator @@ -494,7 +497,7 @@ public: show(0) {} virtual void top_level_item() { abort_on_null= 1; } - bool top_level() { return abort_on_null; } + bool is_top_level_item() { return abort_on_null; } longlong val_int(); enum Functype functype() const { return NOT_ALL_FUNC; } const char *func_name() const { return "<not>"; } @@ -672,6 +675,7 @@ public: CHARSET_INFO *compare_collation() { return cmp_collation.collation; } uint decimal_precision() const { return 1; } bool eval_not_null_tables(uchar *opt_arg); + void fix_after_pullout(st_select_lex *new_parent, Item **ref); }; @@ -773,6 +777,7 @@ public: uint decimal_precision() const; const char *func_name() const { return "if"; } bool eval_not_null_tables(uchar *opt_arg); + void fix_after_pullout(st_select_lex *new_parent, Item **ref); }; @@ -1311,6 +1316,7 @@ public: bool is_bool_func() { return 1; } CHARSET_INFO *compare_collation() { return cmp_collation.collation; } bool eval_not_null_tables(uchar *opt_arg); + void fix_after_pullout(st_select_lex *new_parent, Item **ref); }; class cmp_item_row :public cmp_item @@ -1814,6 +1820,7 @@ public: return item; } Item *neg_transformer(THD *thd); + void mark_as_condition_AND_part(TABLE_LIST *embedding); }; inline bool is_cond_and(Item *item) diff --git a/sql/item_func.cc b/sql/item_func.cc index 2bca34f0a76..ed9eb512cf1 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -152,11 +152,9 @@ Item_func::fix_fields(THD *thd, Item **ref) { DBUG_ASSERT(fixed == 0); Item **arg,**arg_end; - TABLE_LIST *save_emb_on_expr_nest= thd->thd_marker.emb_on_expr_nest; #ifndef EMBEDDED_LIBRARY // Avoid compiler warning uchar buff[STACK_BUFF_ALLOC]; // Max argument in function #endif - thd->thd_marker.emb_on_expr_nest= NULL; used_tables_cache= not_null_tables_cache= 0; const_item_cache=1; @@ -210,7 +208,6 @@ Item_func::fix_fields(THD *thd, Item **ref) if (thd->is_error()) // An error inside fix_length_and_dec occured return TRUE; fixed= 1; - thd->thd_marker.emb_on_expr_nest= save_emb_on_expr_nest; return FALSE; } @@ -5066,12 +5063,16 @@ void Item_func_get_system_var::fix_length_and_dec() switch (var->show_type()) { case SHOW_LONG: - case SHOW_INT: case SHOW_HA_ROWS: unsigned_flag= TRUE; max_length= MY_INT64_NUM_DECIMAL_DIGITS; decimals=0; break; + case SHOW_INT: + unsigned_flag= FALSE; + max_length= MY_INT64_NUM_DECIMAL_DIGITS; + decimals=0; + break; case SHOW_LONGLONG: unsigned_flag= TRUE; max_length= MY_INT64_NUM_DECIMAL_DIGITS; @@ -5212,7 +5213,7 @@ longlong Item_func_get_system_var::val_int() switch (var->show_type()) { - case SHOW_INT: get_sys_var_safe (uint); + case SHOW_INT: get_sys_var_safe (int); case SHOW_LONG: get_sys_var_safe (ulong); case SHOW_LONGLONG: get_sys_var_safe (ulonglong); case SHOW_HA_ROWS: get_sys_var_safe (ha_rows); diff --git a/sql/item_row.cc b/sql/item_row.cc index 09977d71bb7..46d5f13f6fa 100644 --- a/sql/item_row.cc +++ b/sql/item_row.cc @@ -149,11 +149,13 @@ void Item_row::fix_after_pullout(st_select_lex *new_parent, Item **ref) { used_tables_cache= 0; const_item_cache= 1; + not_null_tables_cache= 0; for (uint i= 0; i < arg_count; i++) { items[i]->fix_after_pullout(new_parent, &items[i]); used_tables_cache|= items[i]->used_tables(); const_item_cache&= items[i]->const_item(); + not_null_tables_cache|= items[i]->not_null_tables(); } } diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index a2f600d8473..9abbdfcec4f 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -3606,40 +3606,40 @@ void Item_func_dyncol_create::prepare_arguments() DBUG_ASSERT(args[valpos]->field_type() == MYSQL_TYPE_NULL); break; case DYN_COL_INT: - vals[i].long_value= args[valpos]->val_int(); + vals[i].x.long_value= args[valpos]->val_int(); break; case DYN_COL_UINT: - vals[i].ulong_value= args[valpos]->val_int(); + vals[i].x.ulong_value= args[valpos]->val_int(); break; case DYN_COL_DOUBLE: - vals[i].double_value= args[valpos]->val_real(); + vals[i].x.double_value= args[valpos]->val_real(); break; case DYN_COL_STRING: res= args[valpos]->val_str(&tmp); if (res && - (vals[i].string_value.str= my_strndup(res->ptr(), res->length(), + (vals[i].x.string.value.str= my_strndup(res->ptr(), res->length(), MYF(MY_WME)))) { - vals[i].string_value.length= res->length(); - vals[i].charset= res->charset(); + vals[i].x.string.value.length= res->length(); + vals[i].x.string.charset= res->charset(); } else { args[valpos]->null_value= 1; // In case of out of memory - vals[i].string_value.str= NULL; - vals[i].string_value.length= 0; // just to be safe + vals[i].x.string.value.str= NULL; + vals[i].x.string.value.length= 0; // just to be safe } break; case DYN_COL_DECIMAL: if ((dres= args[valpos]->val_decimal(&dtmp))) { dynamic_column_prepare_decimal(&vals[i]); - DBUG_ASSERT(vals[i].decimal_value.len == dres->len); - vals[i].decimal_value.intg= dres->intg; - vals[i].decimal_value.frac= dres->frac; - vals[i].decimal_value.sign= dres->sign(); - memcpy(vals[i].decimal_buffer, dres->buf, - sizeof(vals[i].decimal_buffer)); + DBUG_ASSERT(vals[i].x.decimal.value.len == dres->len); + vals[i].x.decimal.value.intg= dres->intg; + vals[i].x.decimal.value.frac= dres->frac; + vals[i].x.decimal.value.sign= dres->sign(); + memcpy(vals[i].x.decimal.buffer, dres->buf, + sizeof(vals[i].x.decimal.buffer)); } else { @@ -3648,13 +3648,13 @@ void Item_func_dyncol_create::prepare_arguments() } break; case DYN_COL_DATETIME: - args[valpos]->get_date(&vals[i].time_value, TIME_FUZZY_DATE); + args[valpos]->get_date(&vals[i].x.time_value, TIME_FUZZY_DATE); break; case DYN_COL_DATE: - args[valpos]->get_date(&vals[i].time_value, TIME_FUZZY_DATE); + args[valpos]->get_date(&vals[i].x.time_value, TIME_FUZZY_DATE); break; case DYN_COL_TIME: - args[valpos]->get_time(&vals[i].time_value); + args[valpos]->get_time(&vals[i].x.time_value); break; default: DBUG_ASSERT(0); @@ -3663,7 +3663,7 @@ void Item_func_dyncol_create::prepare_arguments() if (vals[i].type != DYN_COL_NULL && args[valpos]->null_value) { if (vals[i].type == DYN_COL_STRING) - my_free(vals[i].string_value.str, MYF(MY_ALLOW_ZERO_PTR)); + my_free(vals[i].x.string.value.str, MYF(MY_ALLOW_ZERO_PTR)); vals[i].type= DYN_COL_NULL; } } @@ -3677,7 +3677,7 @@ void Item_func_dyncol_create::cleanup_arguments() for (i= 0; i < column_count; i++) { if (vals[i].type == DYN_COL_STRING) - my_free(vals[i].string_value.str, MYF(MY_ALLOW_ZERO_PTR)); + my_free(vals[i].x.string.value.str, MYF(MY_ALLOW_ZERO_PTR)); } } @@ -3894,19 +3894,19 @@ String *Item_dyncol_get::val_str(String *str_result) goto null; case DYN_COL_INT: case DYN_COL_UINT: - str_result->set_int(val.long_value, test(val.type == DYN_COL_UINT), + str_result->set_int(val.x.long_value, test(val.type == DYN_COL_UINT), &my_charset_latin1); break; case DYN_COL_DOUBLE: - str_result->set_real(val.double_value, NOT_FIXED_DEC, &my_charset_latin1); + str_result->set_real(val.x.double_value, NOT_FIXED_DEC, &my_charset_latin1); break; case DYN_COL_STRING: - if ((char*) tmp.ptr() <= val.string_value.str && - (char*) tmp.ptr() + tmp.length() >= val.string_value.str) + if ((char*) tmp.ptr() <= val.x.string.value.str && + (char*) tmp.ptr() + tmp.length() >= val.x.string.value.str) { /* value is allocated in tmp buffer; We have to make a copy */ - str_result->copy(val.string_value.str, val.string_value.length, - val.charset); + str_result->copy(val.x.string.value.str, val.x.string.value.length, + val.x.string.charset); } else { @@ -3915,24 +3915,24 @@ String *Item_dyncol_get::val_str(String *str_result) into a field or in a buffer for another item and this buffer is not going to be deleted during expression evaluation */ - str_result->set(val.string_value.str, val.string_value.length, - val.charset); + str_result->set(val.x.string.value.str, val.x.string.value.length, + val.x.string.charset); } break; case DYN_COL_DECIMAL: { int res; int length= - my_decimal_string_length((const my_decimal*)&val.decimal_value); + my_decimal_string_length((const my_decimal*)&val.x.decimal.value); if (str_result->alloc(length)) goto null; - if ((res= decimal2string(&val.decimal_value, (char*) str_result->ptr(), + if ((res= decimal2string(&val.x.decimal.value, (char*) str_result->ptr(), &length, 0, 0, ' ')) != E_DEC_OK) { char buff[40]; int len= sizeof(buff); DBUG_ASSERT(length < (int)sizeof(buff)); - decimal2string(&val.decimal_value, buff, &len, 0, 0, ' '); + decimal2string(&val.x.decimal.value, buff, &len, 0, 0, ' '); decimal_operation_results(res, buff, "CHAR"); } str_result->set_charset(&my_charset_latin1); @@ -3950,7 +3950,7 @@ String *Item_dyncol_get::val_str(String *str_result) asked to return the time argument as a string. */ if (str_result->alloc(MAX_DATE_STRING_REP_LENGTH) || - !(length= my_TIME_to_str(&val.time_value, (char*) str_result->ptr(), + !(length= my_TIME_to_str(&val.x.time_value, (char*) str_result->ptr(), AUTO_SEC_PART_DIGITS))) goto null; str_result->set_charset(&my_charset_latin1); @@ -3980,20 +3980,20 @@ longlong Item_dyncol_get::val_int() goto null; case DYN_COL_UINT: unsigned_flag= 1; // Make it possible for caller to detect sign - return val.long_value; + return val.x.long_value; case DYN_COL_INT: unsigned_flag= 0; // Make it possible for caller to detect sign - return val.long_value; + return val.x.long_value; case DYN_COL_DOUBLE: { bool error; longlong num; - num= double_to_longlong(val.double_value, unsigned_flag, &error); + num= double_to_longlong(val.x.double_value, unsigned_flag, &error); if (error) { char buff[30]; - sprintf(buff, "%lg", val.double_value); + sprintf(buff, "%lg", val.x.double_value); push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_DATA_OVERFLOW, ER(ER_DATA_OVERFLOW), @@ -4006,14 +4006,14 @@ longlong Item_dyncol_get::val_int() { int error; longlong num; - char *end= val.string_value.str + val.string_value.length, *org_end= end; + char *end= val.x.string.value.str + val.x.string.value.length, *org_end= end; - num= my_strtoll10(val.string_value.str, &end, &error); + num= my_strtoll10(val.x.string.value.str, &end, &error); if (end != org_end || error > 0) { char buff[80]; - strmake(buff, val.string_value.str, min(sizeof(buff)-1, - val.string_value.length)); + strmake(buff, val.x.string.value.str, min(sizeof(buff)-1, + val.x.string.value.length)); push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_BAD_DATA, ER(ER_BAD_DATA), @@ -4026,18 +4026,18 @@ longlong Item_dyncol_get::val_int() case DYN_COL_DECIMAL: { longlong num; - my_decimal2int(E_DEC_FATAL_ERROR, &val.decimal_value, unsigned_flag, + my_decimal2int(E_DEC_FATAL_ERROR, &val.x.decimal.value, unsigned_flag, &num); return num; } case DYN_COL_DATETIME: case DYN_COL_DATE: case DYN_COL_TIME: - unsigned_flag= !val.time_value.neg; + unsigned_flag= !val.x.time_value.neg; if (unsigned_flag) - return TIME_to_ulonglong(&val.time_value); + return TIME_to_ulonglong(&val.x.time_value); else - return -(longlong)TIME_to_ulonglong(&val.time_value); + return -(longlong)TIME_to_ulonglong(&val.x.time_value); } null: @@ -4059,24 +4059,24 @@ double Item_dyncol_get::val_real() case DYN_COL_NULL: goto null; case DYN_COL_UINT: - return ulonglong2double(val.ulong_value); + return ulonglong2double(val.x.ulong_value); case DYN_COL_INT: - return (double) val.long_value; + return (double) val.x.long_value; case DYN_COL_DOUBLE: - return (double) val.double_value; + return (double) val.x.double_value; case DYN_COL_STRING: { int error; char *end; - double res= my_strntod(val.charset, (char*) val.string_value.str, - val.string_value.length, &end, &error); + double res= my_strntod(val.x.string.charset, (char*) val.x.string.value.str, + val.x.string.value.length, &end, &error); - if (end != (char*) val.string_value.str + val.string_value.length || + if (end != (char*) val.x.string.value.str + val.x.string.value.length || error) { char buff[80]; - strmake(buff, val.string_value.str, min(sizeof(buff)-1, - val.string_value.length)); + strmake(buff, val.x.string.value.str, min(sizeof(buff)-1, + val.x.string.value.length)); push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_BAD_DATA, ER(ER_BAD_DATA), @@ -4088,13 +4088,13 @@ double Item_dyncol_get::val_real() { double res; /* This will always succeed */ - decimal2double(&val.decimal_value, &res); + decimal2double(&val.x.decimal.value, &res); return res; } case DYN_COL_DATETIME: case DYN_COL_DATE: case DYN_COL_TIME: - return TIME_to_double(&val.time_value); + return TIME_to_double(&val.x.time_value); } null: @@ -4116,22 +4116,22 @@ my_decimal *Item_dyncol_get::val_decimal(my_decimal *decimal_value) case DYN_COL_NULL: goto null; case DYN_COL_UINT: - int2my_decimal(E_DEC_FATAL_ERROR, val.long_value, TRUE, decimal_value); + int2my_decimal(E_DEC_FATAL_ERROR, val.x.long_value, TRUE, decimal_value); break; case DYN_COL_INT: - int2my_decimal(E_DEC_FATAL_ERROR, val.long_value, FALSE, decimal_value); + int2my_decimal(E_DEC_FATAL_ERROR, val.x.long_value, FALSE, decimal_value); break; case DYN_COL_DOUBLE: - double2my_decimal(E_DEC_FATAL_ERROR, val.double_value, decimal_value); + double2my_decimal(E_DEC_FATAL_ERROR, val.x.double_value, decimal_value); break; case DYN_COL_STRING: { int rc; - rc= str2my_decimal(0, val.string_value.str, val.string_value.length, - val.charset, decimal_value); + rc= str2my_decimal(0, val.x.string.value.str, val.x.string.value.length, + val.x.string.charset, decimal_value); char buff[80]; - strmake(buff, val.string_value.str, min(sizeof(buff)-1, - val.string_value.length)); + strmake(buff, val.x.string.value.str, min(sizeof(buff)-1, + val.x.string.value.length)); if (rc != E_DEC_OK) { push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, @@ -4142,14 +4142,14 @@ my_decimal *Item_dyncol_get::val_decimal(my_decimal *decimal_value) break; } case DYN_COL_DECIMAL: - decimal2my_decimal(&val.decimal_value, decimal_value); + decimal2my_decimal(&val.x.decimal.value, decimal_value); break; case DYN_COL_DATETIME: case DYN_COL_DATE: case DYN_COL_TIME: - decimal_value= seconds2my_decimal(val.time_value.neg, - TIME_to_ulonglong(&val.time_value), - val.time_value.second_part, + decimal_value= seconds2my_decimal(val.x.time_value.neg, + TIME_to_ulonglong(&val.x.time_value), + val.x.time_value.second_part, decimal_value); break; } @@ -4178,36 +4178,36 @@ bool Item_dyncol_get::get_date(MYSQL_TIME *ltime, uint fuzzy_date) signed_value= 1; // For error message /* fall_trough */ case DYN_COL_UINT: - if (signed_value || val.ulong_value <= LONGLONG_MAX) + if (signed_value || val.x.ulong_value <= LONGLONG_MAX) { - if (int_to_datetime_with_warn(val.ulong_value, ltime, fuzzy_date, + if (int_to_datetime_with_warn(val.x.ulong_value, ltime, fuzzy_date, 0 /* TODO */)) goto null; return 0; } /* let double_to_datetime_with_warn() issue the warning message */ - val.double_value= static_cast<double>(ULONGLONG_MAX); + val.x.double_value= static_cast<double>(ULONGLONG_MAX); /* fall_trough */ case DYN_COL_DOUBLE: - if (double_to_datetime_with_warn(val.double_value, ltime, fuzzy_date, + if (double_to_datetime_with_warn(val.x.double_value, ltime, fuzzy_date, 0 /* TODO */)) goto null; return 0; case DYN_COL_DECIMAL: - if (decimal_to_datetime_with_warn((my_decimal*)&val.decimal_value, ltime, + if (decimal_to_datetime_with_warn((my_decimal*)&val.x.decimal.value, ltime, fuzzy_date, 0 /* TODO */)) goto null; return 0; case DYN_COL_STRING: - if (str_to_datetime_with_warn(val.string_value.str, - val.string_value.length, + if (str_to_datetime_with_warn(val.x.string.value.str, + val.x.string.value.length, ltime, fuzzy_date) <= MYSQL_TIMESTAMP_ERROR) goto null; return 0; case DYN_COL_DATETIME: case DYN_COL_DATE: case DYN_COL_TIME: - *ltime= val.time_value; + *ltime= val.x.time_value; return 0; } diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 9281ebb22bb..22c62f0a6d7 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -229,7 +229,7 @@ bool Item_subselect::fix_fields(THD *thd_param, Item **ref) if (check_stack_overrun(thd, STACK_MIN_SIZE, (uchar*)&res)) return TRUE; - + if (!(res= engine->prepare())) { // all transformation is done (used by prepared statements) @@ -492,6 +492,7 @@ void Item_subselect::recalc_used_tables(st_select_lex *new_parent, upper->item->walk(&Item::enumerate_field_refs_processor, FALSE, (uchar*)&fixer); used_tables_cache |= fixer.used_tables; + upper->item->walk(&Item::update_table_bitmaps_processor, FALSE, NULL); /* if (after_pullout) upper->item->fix_after_pullout(new_parent, &(upper->item)); @@ -514,6 +515,20 @@ void Item_subselect::recalc_used_tables(st_select_lex *new_parent, bool Item_subselect::walk(Item_processor processor, bool walk_subquery, uchar *argument) { + if (!(unit->uncacheable & ~UNCACHEABLE_DEPENDENT) && engine->is_executed() && + !unit->describe) + { + /* + The subquery has already been executed (for real, it wasn't EXPLAIN's + fake execution) so it should not matter what it has inside. + + The actual reason for not walking inside is that parts of the subquery + (e.g. JTBM join nests and their IN-equality conditions may have been + invalidated by irreversible cleanups (those happen after an uncorrelated + subquery has been executed). + */ + return FALSE; + } if (walk_subquery) { @@ -822,7 +837,10 @@ Item_maxmin_subselect::Item_maxmin_subselect(THD *thd_param, { DBUG_ENTER("Item_maxmin_subselect::Item_maxmin_subselect"); max= max_arg; - init(select_lex, new select_max_min_finder_subselect(this, max_arg)); + init(select_lex, + new select_max_min_finder_subselect(this, max_arg, + parent->substype() == + Item_subselect::ALL_SUBS)); max_columns= 1; maybe_null= 1; max_columns= 1; @@ -1165,8 +1183,9 @@ bool Item_in_subselect::test_limit(st_select_lex_unit *unit_arg) Item_in_subselect::Item_in_subselect(Item * left_exp, st_select_lex *select_lex): - Item_exists_subselect(), left_expr_cache(0), first_execution(TRUE), - optimizer(0), pushed_cond_guards(NULL), in_strategy(0), + Item_exists_subselect(), + left_expr_cache(0), first_execution(TRUE), + optimizer(0), pushed_cond_guards(NULL), emb_on_expr_nest(NULL), in_strategy(0), is_jtbm_merged(FALSE), is_flattenable_semijoin(FALSE), is_registered_semijoin(FALSE), upper_item(0) @@ -1598,11 +1617,20 @@ bool Item_allany_subselect::transform_into_max_min(JOIN *join) */ DBUG_ASSERT(!substitution); - if (!select_lex->group_list.elements && - !select_lex->having && - !select_lex->with_sum_func && - !(select_lex->next_select()) && - select_lex->table_list.elements) + /* + Check if optimization with aggregate min/max possible + 1 There is no aggregate in the subquery + 2 It is not UNION + 3 There is tables + 4 It is not ALL subquery with possible NULLs in the SELECT list + */ + if (!select_lex->group_list.elements && /*1*/ + !select_lex->having && /*1*/ + !select_lex->with_sum_func && /*1*/ + !(select_lex->next_select()) && /*2*/ + select_lex->table_list.elements && /*3*/ + (!select_lex->ref_pointer_array[0]->maybe_null || /*4*/ + substype() != Item_subselect::ALL_SUBS)) /*4*/ { Item_sum_hybrid *item; nesting_map save_allow_sum_func; @@ -1701,7 +1729,7 @@ bool Item_allany_subselect::is_maxmin_applicable(JOIN *join) Check if max/min optimization applicable: It is top item of WHERE condition. */ - return (abort_on_null || (upper_item && upper_item->top_level())) && + return (abort_on_null || (upper_item && upper_item->is_top_level_item())) && !join->select_lex->master_unit()->uncacheable && !func->eqne_op(); } @@ -2251,7 +2279,12 @@ bool Item_in_subselect::inject_in_to_exists_cond(JOIN *join_arg) { /* The argument list of the top-level AND may change after fix fields. */ and_args= ((Item_cond*) join_arg->conds)->argument_list(); - and_args->concat((List<Item> *) &join_arg->cond_equal->current_level); + List_iterator<Item_equal> li(join_arg->cond_equal->current_level); + Item_equal *elem; + while ((elem= li++)) + { + and_args->push_back(elem); + } } } @@ -2395,6 +2428,7 @@ bool Item_in_subselect::fix_fields(THD *thd_arg, Item **ref) uint outer_cols_num; List<Item> *inner_cols; + if (in_strategy & SUBS_SEMI_JOIN) return !( (*ref)= new Item_int(1)); @@ -2449,7 +2483,6 @@ bool Item_in_subselect::fix_fields(THD *thd_arg, Item **ref) return TRUE; if (Item_subselect::fix_fields(thd_arg, ref)) return TRUE; - fixed= TRUE; return FALSE; } @@ -2459,6 +2492,7 @@ void Item_in_subselect::fix_after_pullout(st_select_lex *new_parent, Item **ref) { left_expr->fix_after_pullout(new_parent, &left_expr); Item_subselect::fix_after_pullout(new_parent, ref); + used_tables_cache |= left_expr->used_tables(); } void Item_in_subselect::update_used_tables() @@ -2960,7 +2994,7 @@ int subselect_single_select_engine::exec() executed= 1; thd->where= save_where; thd->lex->current_select= save_select; - DBUG_RETURN(join->error||thd->is_fatal_error); + DBUG_RETURN(join->error || thd->is_fatal_error || thd->is_error()); } thd->where= save_where; thd->lex->current_select= save_select; @@ -3898,6 +3932,8 @@ subselect_hash_sj_engine::get_strategy_using_data() } if (result_sink->get_null_count_of_col(i) == tmp_table->file->stats.records) ++count_null_only_columns; + if (result_sink->get_null_count_of_col(i)) + ++count_columns_with_nulls; } /* If no column contains NULLs use regular hash index lookups. */ @@ -4373,7 +4409,13 @@ double get_fanout_with_deps(JOIN *join, table_map tset) for (JOIN_TAB *tab= first_top_level_tab(join, WITHOUT_CONST_TABLES); tab; tab= next_top_level_tab(join, tab)) { - if ((tab->table->map & checked_deps) && !tab->emb_sj_nest && + /* + Ignore SJM nests. They have tab->table==NULL. There is no point to walk + inside them, because GROUP BY clause cannot refer to tables from within + subquery. + */ + if (!tab->is_sjm_nest() && (tab->table->map & checked_deps) && + !tab->emb_sj_nest && tab->records_read != 0) { fanout *= rows2double(tab->records_read); @@ -4563,7 +4605,8 @@ int subselect_hash_sj_engine::exec() /* The subquery should be optimized, and materialized only once. */ DBUG_ASSERT(materialize_join->optimized && !is_materialized); materialize_join->exec(); - if ((res= test(materialize_join->error || thd->is_fatal_error))) + if ((res= test(materialize_join->error || thd->is_fatal_error || + thd->is_error()))) goto err; /* @@ -4659,6 +4702,7 @@ int subselect_hash_sj_engine::exec() count_pm_keys, has_covering_null_row, has_covering_null_columns, + count_columns_with_nulls, item, result, semi_join_conds->argument_list()); if (!pm_engine || @@ -4684,7 +4728,8 @@ int subselect_hash_sj_engine::exec() item, result, semi_join_conds->argument_list(), has_covering_null_row, - has_covering_null_columns))) + has_covering_null_columns, + count_columns_with_nulls))) { /* This is an irrecoverable error. */ res= 1; @@ -5121,43 +5166,48 @@ subselect_partial_match_engine::subselect_partial_match_engine( select_result_interceptor *result_arg, List<Item> *equi_join_conds_arg, bool has_covering_null_row_arg, - bool has_covering_null_columns_arg) + bool has_covering_null_columns_arg, + uint count_columns_with_nulls_arg) :subselect_engine(thd_arg, item_arg, result_arg), tmp_table(tmp_table_arg), lookup_engine(engine_arg), equi_join_conds(equi_join_conds_arg), has_covering_null_row(has_covering_null_row_arg), - has_covering_null_columns(has_covering_null_columns_arg) + has_covering_null_columns(has_covering_null_columns_arg), + count_columns_with_nulls(count_columns_with_nulls_arg) {} int subselect_partial_match_engine::exec() { Item_in_subselect *item_in= (Item_in_subselect *) item; - int res; + int copy_res, lookup_res; /* Try to find a matching row by index lookup. */ - res= lookup_engine->copy_ref_key_simple(); - if (res == -1) + copy_res= lookup_engine->copy_ref_key_simple(); + if (copy_res == -1) { /* The result is FALSE based on the outer reference. */ item_in->value= 0; item_in->null_value= 0; return 0; } - else if (res == 0) + else if (copy_res == 0) { /* Search for a complete match. */ - if ((res= lookup_engine->index_lookup())) + if ((lookup_res= lookup_engine->index_lookup())) { /* An error occured during lookup(). */ item_in->value= 0; item_in->null_value= 0; - return res; + return lookup_res; } - else if (item_in->value) + else if (item_in->value || !count_columns_with_nulls) { /* A complete match was found, the result of IN is TRUE. + If no match was found, and there are no NULLs in the materialized + subquery, then the result is guaranteed to be false because this + branch is executed when the outer reference has no NULLs as well. Notice: (this->item == lookup_engine->item) */ return 0; @@ -5498,6 +5548,8 @@ bool subselect_rowid_merge_engine::partial_match() Ordered_key *cur_key; rownum_t cur_row_num; uint count_nulls_in_search_key= 0; + uint max_covering_null_row_len= + ((select_materialize_with_stats *) result)->get_max_nulls_in_row(); bool res= FALSE; /* If there is a non-NULL key, it must be the first key in the keys array. */ @@ -5563,8 +5615,16 @@ bool subselect_rowid_merge_engine::partial_match() If there is no NULL (sub)row that covers all NULL columns, and there is no single match for any of the NULL columns, the result is FALSE. */ - if (pq.elements - test(non_null_key) == 0) + if ((pq.elements == 1 && non_null_key && + max_covering_null_row_len < merge_keys_count - 1) || + pq.elements == 0) { + if (pq.elements == 0) + { + DBUG_ASSERT(!non_null_key); /* Must follow from the logic of this method */ + /* This case must be handled by subselect_partial_match_engine::exec() */ + DBUG_ASSERT(max_covering_null_row_len != tmp_table->s->fields); + } res= FALSE; goto end; } @@ -5608,6 +5668,7 @@ bool subselect_rowid_merge_engine::partial_match() { min_key= cur_key; min_row_num= cur_row_num; + bitmap_clear_all(&matching_keys); bitmap_set_bit(&matching_keys, min_key->get_keyid()); bitmap_union(&matching_keys, &matching_outer_cols); } @@ -5628,6 +5689,8 @@ bool subselect_rowid_merge_engine::partial_match() DBUG_ASSERT(FALSE); end: + if (!has_covering_null_columns) + bitmap_clear_all(&matching_keys); queue_remove_all(&pq); tmp_table->file->ha_rnd_end(); return res; @@ -5641,11 +5704,13 @@ subselect_table_scan_engine::subselect_table_scan_engine( select_result_interceptor *result_arg, List<Item> *equi_join_conds_arg, bool has_covering_null_row_arg, - bool has_covering_null_columns_arg) + bool has_covering_null_columns_arg, + uint count_columns_with_nulls_arg) :subselect_partial_match_engine(thd_arg, engine_arg, tmp_table_arg, item_arg, result_arg, equi_join_conds_arg, has_covering_null_row_arg, - has_covering_null_columns_arg) + has_covering_null_columns_arg, + count_columns_with_nulls_arg) {} diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 2c1e3bddb2d..85ce7745751 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -428,7 +428,6 @@ public: join nest pointer - the predicate is an AND-part of ON expression of a join nest NULL - for all other locations - See also THD::emb_on_expr_nest. */ TABLE_LIST *emb_on_expr_nest; /* @@ -447,7 +446,7 @@ public: /* A bitmap of possible execution strategies for an IN predicate. */ uchar in_strategy; - + bool is_jtbm_merged; /* @@ -459,7 +458,7 @@ public: TRUE<=>registered in the list of semijoins in outer select */ bool is_registered_semijoin; - + /* Used to determine how this subselect item is represented in the item tree, in case there is a need to locate it there and replace with something else. @@ -489,7 +488,8 @@ public: Item_in_subselect() :Item_exists_subselect(), left_expr_cache(0), first_execution(TRUE), abort_on_null(0), optimizer(0), - pushed_cond_guards(NULL), func(NULL), in_strategy(SUBS_NOT_TRANSFORMED), + pushed_cond_guards(NULL), func(NULL), emb_on_expr_nest(NULL), + in_strategy(SUBS_NOT_TRANSFORMED), is_jtbm_merged(FALSE), upper_item(0) {} @@ -533,6 +533,12 @@ public: user. */ int get_identifier(); + + void mark_as_condition_AND_part(TABLE_LIST *embedding) + { + emb_on_expr_nest= embedding; + } + friend class Item_ref_null_helper; friend class Item_is_not_null_test; friend class Item_in_optimizer; @@ -870,7 +876,7 @@ public: tmp_table(NULL), is_materialized(FALSE), materialize_engine(old_engine), materialize_join(NULL), semi_join_conds(NULL), lookup_engine(NULL), count_partial_match_columns(0), count_null_only_columns(0), - strategy(UNDEFINED) + count_columns_with_nulls(0), strategy(UNDEFINED) {} ~subselect_hash_sj_engine(); @@ -908,6 +914,7 @@ protected: MY_BITMAP partial_match_key_parts; uint count_partial_match_columns; uint count_null_only_columns; + uint count_columns_with_nulls; /* Possible execution strategies that can be used to compute hash semi-join.*/ enum exec_strategy { UNDEFINED, @@ -1145,6 +1152,7 @@ protected: guaranteed partial match. */ bool has_covering_null_columns; + uint count_columns_with_nulls; protected: virtual bool partial_match()= 0; @@ -1155,7 +1163,8 @@ public: select_result_interceptor *result_arg, List<Item> *equi_join_conds_arg, bool has_covering_null_row_arg, - bool has_covering_null_columns_arg); + bool has_covering_null_columns_arg, + uint count_columns_with_nulls_arg); int prepare() { return 0; } int exec(); void fix_length_and_dec(Item_cache**) {} @@ -1245,13 +1254,15 @@ public: TABLE *tmp_table_arg, uint merge_keys_count_arg, bool has_covering_null_row_arg, bool has_covering_null_columns_arg, + uint count_columns_with_nulls_arg, Item_subselect *item_arg, select_result_interceptor *result_arg, List<Item> *equi_join_conds_arg) :subselect_partial_match_engine(thd_arg, engine_arg, tmp_table_arg, item_arg, result_arg, equi_join_conds_arg, has_covering_null_row_arg, - has_covering_null_columns_arg), + has_covering_null_columns_arg, + count_columns_with_nulls_arg), merge_keys_count(merge_keys_count_arg), non_null_key(NULL) {} ~subselect_rowid_merge_engine(); @@ -1272,7 +1283,8 @@ public: select_result_interceptor *result_arg, List<Item> *equi_join_conds_arg, bool has_covering_null_row_arg, - bool has_covering_null_columns_arg); + bool has_covering_null_columns_arg, + uint count_columns_with_nulls_arg); void cleanup(); virtual enum_engine_type engine_type() { return TABLE_SCAN_ENGINE; } }; diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index b080f2e9707..3a1e3a93f72 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -1370,7 +1370,8 @@ longlong Item_temporal_func::val_int() MYSQL_TIME ltime; if (get_date(<ime, TIME_FUZZY_DATE)) return 0; - return (longlong)TIME_to_ulonglong(<ime); + longlong v= TIME_to_ulonglong(<ime); + return ltime.neg ? -v : v; } @@ -2303,7 +2304,17 @@ bool Item_date_typecast::get_date(MYSQL_TIME *ltime, uint fuzzy_date) return 1; ltime->hour= ltime->minute= ltime->second= ltime->second_part= 0; ltime->time_type= MYSQL_TIMESTAMP_DATE; - return 0; + + int unused; + if (check_date(ltime, ltime->year || ltime->month || ltime->day, + fuzzy_date, &unused)) + { + Lazy_string_time str(ltime); + make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + &str, MYSQL_TIMESTAMP_DATE, 0); + return (null_value= 1); + } + return (null_value= 0); } @@ -2425,7 +2436,6 @@ bool Item_func_add_time::get_date(MYSQL_TIME *ltime, uint fuzzy_date) long days, microseconds; longlong seconds; int l_sign= sign, was_cut= 0; - uint dec= decimals; if (is_date) // TIMESTAMP function { @@ -2467,10 +2477,6 @@ bool Item_func_add_time::get_date(MYSQL_TIME *ltime, uint fuzzy_date) ltime->time_type= is_time ? MYSQL_TIMESTAMP_TIME : MYSQL_TIMESTAMP_DATETIME; - if (cached_field_type == MYSQL_TYPE_STRING && - (l_time1.second_part || l_time2.second_part)) - dec= TIME_SECOND_PART_DIGITS; - if (!is_time) { get_date_from_daynr(days,<ime->year,<ime->month,<ime->day); diff --git a/sql/lex.h b/sql/lex.h index 6ab234f8d81..3041168e4fb 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -238,6 +238,7 @@ static SYMBOL symbols[] = { { "GRANTS", SYM(GRANTS)}, { "GROUP", SYM(GROUP_SYM)}, { "HANDLER", SYM(HANDLER_SYM)}, + { "HARD", SYM(HARD_SYM)}, { "HASH", SYM(HASH_SYM)}, { "HAVING", SYM(HAVING)}, { "HELP", SYM(HELP_SYM)}, @@ -496,6 +497,7 @@ static SYMBOL symbols[] = { { "SNAPSHOT", SYM(SNAPSHOT_SYM)}, { "SMALLINT", SYM(SMALLINT)}, { "SOCKET", SYM(SOCKET_SYM)}, + { "SOFT", SYM(SOFT_SYM)}, { "SOME", SYM(ANY_SYM)}, { "SONAME", SYM(SONAME_SYM)}, { "SOUNDS", SYM(SOUNDS_SYM)}, diff --git a/sql/log.cc b/sql/log.cc index d345d0f6102..332cdce803e 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -1062,17 +1062,6 @@ bool LOGGER::slow_log_print(THD *thd, const char *query, uint query_length, query_length= command_name[thd->command].length; } - if (!query_length) - { - /* - Not a real query; Reset counts for slow query logging - (QQ: Wonder if this is really needed) - */ - thd->sent_row_count= thd->examined_row_count= 0; - thd->query_plan_flags= QPLAN_INIT; - thd->query_plan_fsort_passes= 0; - } - for (current_handler= slow_log_handler_list; *current_handler ;) error= (*current_handler++)->log_slow(thd, current_time, user_host_buff, user_host_len, @@ -1809,7 +1798,7 @@ static int binlog_savepoint_set(handlerton *hton, THD *thd, void *sv) log_query.append(thd->lex->ident.str, thd->lex->ident.length) || log_query.append("`")) DBUG_RETURN(1); - int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED); + int errcode= query_error_code(thd, thd->killed == NOT_KILLED); Query_log_event qinfo(thd, log_query.ptr(), log_query.length(), TRUE, TRUE, errcode); DBUG_RETURN(mysql_bin_log.write(&qinfo)); @@ -1833,7 +1822,7 @@ static int binlog_savepoint_rollback(handlerton *hton, THD *thd, void *sv) log_query.append(thd->lex->ident.str, thd->lex->ident.length) || log_query.append("`")) DBUG_RETURN(1); - int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED); + int errcode= query_error_code(thd, thd->killed == NOT_KILLED); Query_log_event qinfo(thd, log_query.ptr(), log_query.length(), TRUE, TRUE, errcode); DBUG_RETURN(mysql_bin_log.write(&qinfo)); @@ -5166,7 +5155,7 @@ int query_error_code(THD *thd, bool not_killed) { int error; - if (not_killed || (thd->killed == THD::KILL_BAD_DATA)) + if (not_killed || (killed_mask_hard(thd->killed) == KILL_BAD_DATA)) { error= thd->is_error() ? thd->main_da.sql_errno() : 0; @@ -5175,7 +5164,8 @@ int query_error_code(THD *thd, bool not_killed) is not set to these errors when specified not_killed by the caller. */ - if (error == ER_SERVER_SHUTDOWN || error == ER_QUERY_INTERRUPTED) + if (error == ER_SERVER_SHUTDOWN || error == ER_QUERY_INTERRUPTED || + error == ER_NEW_ABORTING_CONNECTION || error == ER_CONNECTION_KILLED) error= 0; } else diff --git a/sql/log_event.cc b/sql/log_event.cc index 3dad0c87e00..ae7a37f2b3d 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -437,6 +437,7 @@ inline bool unexpected_error_code(int unexpected_error) case ER_NET_READ_ERROR: case ER_NET_ERROR_ON_WRITE: case ER_QUERY_INTERRUPTED: + case ER_CONNECTION_KILLED: case ER_SERVER_SHUTDOWN: case ER_NEW_ABORTING_CONNECTION: return(TRUE); @@ -3686,7 +3687,7 @@ Default database: '%s'. Query: '%s'", { DBUG_PRINT("info",("error ignored")); clear_all_errors(thd, const_cast<Relay_log_info*>(rli)); - thd->killed= THD::NOT_KILLED; + thd->killed= NOT_KILLED; /* When an error is expected and matches the actual error the slave does not report any error and by consequence changes diff --git a/sql/log_event.h b/sql/log_event.h index 4053db4e3e7..ab58aac5fd0 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -3082,7 +3082,7 @@ char *str_to_hex(char *to, const char *from, uint len); /** @class Annotate_rows_log_event - In row-based mode, if binlog_annotate_rows_events = ON, each group of + In row-based mode, if binlog_annotate_row_events = ON, each group of Table_map_log_events is preceded by an Annotate_rows_log_event which contains the query which caused the subsequent rows operations. diff --git a/sql/multi_range_read.cc b/sql/multi_range_read.cc index f0a1987c383..055a9268417 100644 --- a/sql/multi_range_read.cc +++ b/sql/multi_range_read.cc @@ -424,10 +424,10 @@ void Mrr_ordered_index_reader::interrupt_read() { DBUG_ASSERT(support_scan_interruptions); TABLE *table= file->get_table(); + KEY *used_index= &table->key_info[file->active_index]; /* Save the current key value */ key_copy(saved_key_tuple, table->record[0], - &table->key_info[file->active_index], - keypar.key_tuple_length); + used_index, used_index->key_length); if (saved_primary_key) { @@ -452,9 +452,9 @@ void Mrr_ordered_index_reader::position() void Mrr_ordered_index_reader::resume_read() { TABLE *table= file->get_table(); + KEY *used_index= &table->key_info[file->active_index]; key_restore(table->record[0], saved_key_tuple, - &table->key_info[file->active_index], - keypar.key_tuple_length); + used_index, used_index->key_length); if (saved_primary_key) { key_restore(table->record[0], saved_primary_key, @@ -531,7 +531,7 @@ int Mrr_ordered_index_reader::init(handler *h_arg, RANGE_SEQ_IF *seq_funcs, mrr_funcs= *seq_funcs; source_exhausted= FALSE; if (support_scan_interruptions) - bzero(saved_key_tuple, keypar.key_tuple_length); + bzero(saved_key_tuple, key_info->key_length); have_saved_rowid= FALSE; return 0; } @@ -848,12 +848,14 @@ int DsMrr_impl::dsmrr_init(handler *h_arg, RANGE_SEQ_IF *seq_funcs, if (h_idx->primary_key_is_clustered()) { uint pk= h_idx->get_table()->s->primary_key; - saved_pk_length= h_idx->get_table()->key_info[pk].key_length; + if (pk != MAX_KEY) + saved_pk_length= h_idx->get_table()->key_info[pk].key_length; } - + + KEY *used_index= &h_idx->get_table()->key_info[h_idx->active_index]; if (reader_factory.ordered_index_reader. set_interruption_temp_buffer(primary_file->ref_length, - keypar.key_tuple_length, + used_index->key_length, saved_pk_length, &full_buf, full_buf_end)) goto use_default_impl; @@ -1635,7 +1637,8 @@ bool DsMrr_impl::get_disk_sweep_mrr_cost(uint keynr, ha_rows rows, uint flags, uint *buffer_size, COST_VECT *cost) { ulong max_buff_entries, elem_size; - ha_rows rows_in_full_step, rows_in_last_step; + ha_rows rows_in_full_step; + ha_rows rows_in_last_step; uint n_full_steps; double index_read_cost; @@ -1660,7 +1663,7 @@ bool DsMrr_impl::get_disk_sweep_mrr_cost(uint keynr, ha_rows rows, uint flags, /* Adjust buffer size if we expect to use only part of the buffer */ if (n_full_steps) { - get_sort_and_sweep_cost(table, rows, cost); + get_sort_and_sweep_cost(table, rows_in_full_step, cost); cost->multiply(n_full_steps); } else diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index e20702b497d..5d497645a77 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -118,8 +118,6 @@ char *sql_strmake_with_convert(const char *str, size_t arg_length, CHARSET_INFO *from_cs, size_t max_res_length, CHARSET_INFO *to_cs, size_t *result_length); -uint kill_one_thread(THD *thd, ulong id, bool only_kill_query); -void sql_kill(THD *thd, ulong id, bool only_kill_query); bool net_request_file(NET* net, const char* fname); char* query_table_status(THD *thd,const char *db,const char *table_name); @@ -610,7 +608,8 @@ enabled by default, add OPTIMIZER_SWITCH_MATERIALIZATION OPTIMIZER_SWITCH_PARTIAL_MATCH_TABLE_SCAN|\ OPTIMIZER_SWITCH_JOIN_CACHE_INCREMENTAL | \ OPTIMIZER_SWITCH_JOIN_CACHE_HASHED | \ - OPTIMIZER_SWITCH_JOIN_CACHE_BKA) + OPTIMIZER_SWITCH_JOIN_CACHE_BKA | \ + OPTIMIZER_SWITCH_SUBQUERY_CACHE) /* Replication uses 8 bytes to store SQL_MODE in the binary log. The day you @@ -944,6 +943,7 @@ inline bool check_merge_table_access(THD *thd, char *db, TABLE_LIST *table_list) inline bool check_some_routine_access(THD *thd, const char *db, const char *name, bool is_proc) { return false; } +#define decrease_user_connections(X) do { } while(0) /* nothing */ #endif /*NO_EMBEDDED_ACCESS_CHECKS*/ bool multi_update_precheck(THD *thd, TABLE_LIST *tables); @@ -1074,6 +1074,10 @@ struct Query_cache_query_flags #define query_cache_is_cacheable_query(L) 0 #endif /*HAVE_QUERY_CACHE*/ +uint kill_one_thread(THD *thd, ulong id, killed_state kill_signal); +void sql_kill(THD *thd, ulong id, killed_state kill_signal); +void sql_kill_user(THD *thd, LEX_USER *str, killed_state kill_signal); + /* Error injector Macros to enable easy testing of recovery after failures in various error cases. @@ -1177,7 +1181,9 @@ bool init_new_connection_handler_thread(); void reset_mqh(LEX_USER *lu, bool get_them); bool check_mqh(THD *thd, uint check_command); void time_out_user_resource_limits(THD *thd, USER_CONN *uc); +#ifndef NO_EMBEDDED_ACCESS_CHECKS void decrease_user_connections(USER_CONN *uc); +#endif bool thd_init_client_charset(THD *thd, uint cs_number); inline bool is_supported_parser_charset(CHARSET_INFO *cs) { @@ -2148,7 +2154,8 @@ extern MYSQL_PLUGIN_IMPORT ulong max_connections; extern ulong max_connect_errors, connect_timeout; extern ulong extra_max_connections; extern ulong slave_net_timeout, slave_trans_retries; -extern uint max_user_connections; +extern int max_user_connections; +extern bool max_user_connections_checking; extern ulonglong denied_connections; extern ulong what_to_log,flush_time; extern ulong query_buff_size; @@ -2176,6 +2183,9 @@ extern uint test_flags,select_errors,ha_open_options; extern uint protocol_version, mysqld_port, mysqld_extra_port, dropping_tables; extern uint delay_key_write_options; extern ulong max_long_data_size; +extern uint internal_tmp_table_max_key_length; +extern uint internal_tmp_table_max_key_segments; + #endif /* MYSQL_SERVER */ #if defined MYSQL_SERVER || defined INNODB_COMPATIBILITY_HOOKS extern MYSQL_PLUGIN_IMPORT uint lower_case_table_names; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 04439b470ce..570236f1757 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -491,7 +491,7 @@ static const char *optimizer_switch_str="index_merge=on,index_merge_union=on," "semijoin=off," "partial_match_rowid_merge=on," "partial_match_table_scan=on," - "subquery_cache=off," + "subquery_cache=on," "mrr=off," "mrr_cost_based=off," "mrr_sort_keys=off," @@ -567,7 +567,7 @@ my_bool opt_local_infile, opt_slave_compressed_protocol; my_bool opt_safe_user_create = 0, opt_no_mix_types = 0; my_bool opt_show_slave_auth_info, opt_sql_bin_update = 0; my_bool opt_log_slave_updates= 0; -my_bool opt_replicate_annotate_rows_events= 0; +my_bool opt_replicate_annotate_row_events= 0; bool slave_warning_issued = false; /* @@ -669,7 +669,12 @@ ulong extra_max_connections; */ ulong max_long_data_size; -uint max_user_connections= 0; +/* Limits for internal temporary tables (MyISAM or Aria) */ +uint internal_tmp_table_max_key_length; +uint internal_tmp_table_max_key_segments; + +int max_user_connections= 0; +bool max_user_connections_checking=0; ulonglong denied_connections; /** Limit of the total number of prepared statements in the server. @@ -1082,7 +1087,7 @@ static void close_connections(void) if (tmp->slave_thread) continue; - tmp->killed= THD::KILL_CONNECTION; + tmp->killed= KILL_SERVER_HARD; thread_scheduler.post_kill_notification(tmp); pthread_mutex_lock(&tmp->LOCK_thd_data); if (tmp->mysys_var) @@ -1142,7 +1147,7 @@ static void close_connections(void) tmp->thread_id, (tmp->main_security_ctx.user ? tmp->main_security_ctx.user : "")); - close_connection(tmp,0,0); + close_connection(tmp,ER_SERVER_SHUTDOWN,0); } #endif DBUG_PRINT("quit",("Unlocking LOCK_thread_count")); @@ -2033,7 +2038,18 @@ void close_connection(THD *thd, uint errcode, bool lock) errcode ? ER(errcode) : "")); if (lock) (void) pthread_mutex_lock(&LOCK_thread_count); - thd->killed= THD::KILL_CONNECTION; + thd->killed= KILL_CONNECTION; + + if (global_system_variables.log_warnings > 3) + { + Security_context *sctx= &thd->main_security_ctx; + sql_print_warning(ER(ER_NEW_ABORTING_CONNECTION), + thd->thread_id,(thd->db ? thd->db : "unconnected"), + sctx->user ? sctx->user : "unauthenticated", + sctx->host_or_ip, + (errcode ? ER(errcode) : "CLOSE_CONNECTION")); + } + if ((vio= thd->net.vio) != 0) { if (errcode) @@ -2131,6 +2147,7 @@ static bool cache_thread() */ thd->mysys_var->abort= 0; thd->thr_create_utime= microsecond_interval_timer(); + thd->start_utime= thd->thr_create_utime; threads.append(thd); return(1); } @@ -2195,24 +2212,6 @@ void flush_thread_cache() } -#ifdef THREAD_SPECIFIC_SIGPIPE -/** - Aborts a thread nicely. Comes here on SIGPIPE. - - @todo - One should have to fix that thr_alarm know about this thread too. -*/ -extern "C" sig_handler abort_thread(int sig __attribute__((unused))) -{ - THD *thd=current_thd; - DBUG_ENTER("abort_thread"); - if (thd) - thd->killed= THD::KILL_CONNECTION; - DBUG_VOID_RETURN; -} -#endif - - /****************************************************************************** Setup a signal thread with handles all signals. Because Linux doesn't support schemas use a mutex to check that @@ -2690,6 +2689,8 @@ or misconfigured. This error can also be caused by malfunctioning hardware.\n", We will try our best to scrape up some info that will hopefully help diagnose\n\ the problem, but since we have already crashed, something is definitely wrong\n\ and this may fail.\n\n"); + set_server_version(); + fprintf(stderr, "Server version: %s\n", server_version); fprintf(stderr, "key_buffer_size=%lu\n", (ulong) dflt_key_cache->key_cache_mem_size); fprintf(stderr, "read_buffer_size=%ld\n", (long) global_system_variables.read_buff_size); @@ -2735,20 +2736,29 @@ the thread stack. Please read http://dev.mysql.com/doc/mysql/en/linux.html\n\n", { const char *kreason= "UNKNOWN"; switch (thd->killed) { - case THD::NOT_KILLED: + case NOT_KILLED: + case KILL_HARD_BIT: kreason= "NOT_KILLED"; break; - case THD::KILL_BAD_DATA: + case KILL_BAD_DATA: + case KILL_BAD_DATA_HARD: kreason= "KILL_BAD_DATA"; break; - case THD::KILL_CONNECTION: + case KILL_CONNECTION: + case KILL_CONNECTION_HARD: kreason= "KILL_CONNECTION"; break; - case THD::KILL_QUERY: + case KILL_QUERY: + case KILL_QUERY_HARD: kreason= "KILL_QUERY"; break; - case THD::KILLED_NO_VALUE: - kreason= "KILLED_NO_VALUE"; + case KILL_SYSTEM_THREAD: + case KILL_SYSTEM_THREAD_HARD: + kreason= "KILL_SYSTEM_THREAD"; + break; + case KILL_SERVER: + case KILL_SERVER_HARD: + kreason= "KILL_SERVER"; break; } fprintf(stderr, "\nTrying to get some variables.\n" @@ -2757,8 +2767,16 @@ the thread stack. Please read http://dev.mysql.com/doc/mysql/en/linux.html\n\n", my_safe_print_str(thd->query(), min(65536,thd->query_length())); fprintf(stderr, "Connection ID (thread ID): %lu\n", (ulong) thd->thread_id); fprintf(stderr, "Status: %s\n", kreason); - fprintf(stderr, "Optimizer switch: %s\n", optimizer_switch_str); - fputc('\n', stderr); + fprintf(stderr, "Optimizer switch: "); + ulonglong optsw= thd->variables.optimizer_switch; + for (uint i= 0; optimizer_switch_names[i+1]; i++, optsw >>= 1) + { + if (i) + fputc(',', stderr); + fprintf(stderr, "%s=%s", + optimizer_switch_names[i], optsw & 1 ? "on" : "off"); + } + fprintf(stderr, "\n\n"); } fprintf(stderr, "\ The manual page at http://dev.mysql.com/doc/mysql/en/crashing.html contains\n\ @@ -3099,7 +3117,7 @@ int my_message_sql(uint error, const char *str, myf MyFlags) MYSQL_ERROR::enum_warning_level level; sql_print_message_func func; DBUG_ENTER("my_message_sql"); - DBUG_PRINT("error", ("error: %u message: '%s'", error, str)); + DBUG_PRINT("error", ("error: %u message: '%s' Flag: %d", error, str, MyFlags)); DBUG_ASSERT(str != NULL); /* @@ -3143,7 +3161,10 @@ int my_message_sql(uint error, const char *str, myf MyFlags) this could be improved by having a common stack of handlers. */ if (thd->handle_error(error, str, level)) + { + DBUG_PRINT("info", ("error handled by handle_error()")); DBUG_RETURN(0); + } if (level == MYSQL_ERROR::WARN_LEVEL_WARN) push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, error, str); @@ -3222,7 +3243,7 @@ to_error_log: /* When simulating OOM, skip writing to error log to avoid mtr errors */ DBUG_EXECUTE_IF("simulate_out_of_memory", DBUG_RETURN(0);); - if (!thd || (MyFlags & ME_NOREFRESH)) + if (!thd || thd->log_all_errors || (MyFlags & ME_NOREFRESH)) (*func)("%s: %s", my_progname_short, str); /* purecov: inspected */ DBUG_RETURN(0); } @@ -4448,6 +4469,11 @@ a file name for --log-bin-index option", opt_binlog_index_name); sql_print_error("Aria engine is not enabled or did not start. The Aria engine must be enabled to continue as mysqld was configured with --with-aria-tmp-tables"); unireg_abort(1); } + internal_tmp_table_max_key_length= maria_max_key_length(); + internal_tmp_table_max_key_segments= maria_max_key_segments(); +#else + internal_tmp_table_max_key_length= myisam_max_key_length(); + internal_tmp_table_max_key_segments= myisam_max_key_segments(); #endif tc_log= (total_ha_2pc > 1 ? (opt_bin_log ? @@ -5259,7 +5285,7 @@ void create_thread_to_handle_connection(THD *thd) thread_created++; threads.append(thd); DBUG_PRINT("info",(("creating thread %lu"), thd->thread_id)); - thd->prior_thr_create_utime= thd->start_utime= microsecond_interval_timer(); + thd->prior_thr_create_utime= microsecond_interval_timer(); if ((error=pthread_create(&thd->real_id,&connection_attrib, handle_one_connection, (void*) thd))) @@ -5269,7 +5295,7 @@ void create_thread_to_handle_connection(THD *thd) ("Can't create thread to handle request (error %d)", error)); thread_count--; - thd->killed= THD::KILL_CONNECTION; // Safety + thd->killed= KILL_CONNECTION; // Safety (void) pthread_mutex_unlock(&LOCK_thread_count); pthread_mutex_lock(&LOCK_connection_count); @@ -5282,7 +5308,7 @@ void create_thread_to_handle_connection(THD *thd) ER(ER_CANT_CREATE_THREAD), error); net_send_error(thd, ER_CANT_CREATE_THREAD, error_message_buff); (void) pthread_mutex_lock(&LOCK_thread_count); - close_connection(thd,0,0); + close_connection(thd,ER_OUT_OF_RESOURCES,0); delete thd; (void) pthread_mutex_unlock(&LOCK_thread_count); return; @@ -6140,7 +6166,9 @@ enum options_mysqld OPT_SECURE_FILE_PRIV, OPT_MIN_EXAMINED_ROW_LIMIT, OPT_LOG_SLOW_SLAVE_STATEMENTS, - OPT_DEBUG_CRC, OPT_DEBUG_ON, OPT_DEBUG_ASSERT_IF_CRASHED_TABLE, OPT_OLD_MODE, + OPT_DEBUG_CRC, OPT_DEBUG_ON, OPT_DEBUG_ASSERT_IF_CRASHED_TABLE, + OPT_DEBUG_ASSERT_ON_ERROR, + OPT_OLD_MODE, OPT_TEST_IGNORE_WRONG_OPTIONS, OPT_TEST_RESTART, #if defined(ENABLED_DEBUG_SYNC) OPT_DEBUG_SYNC_TIMEOUT, @@ -6227,17 +6255,17 @@ struct my_option my_long_options[] = #endif , &opt_binlog_format, &opt_binlog_format, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"binlog-annotate-rows-events", OPT_BINLOG_ANNOTATE_ROWS_EVENTS, + {"binlog-annotate-row-events", OPT_BINLOG_ANNOTATE_ROWS_EVENTS, "Tells the master to annotate RBR events with the statement that " "caused these events.", - (uchar**) &global_system_variables.binlog_annotate_rows_events, - (uchar**) &max_system_variables.binlog_annotate_rows_events, + (uchar**) &global_system_variables.binlog_annotate_row_events, + (uchar**) &max_system_variables.binlog_annotate_row_events, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"replicate-annotate-rows-events", OPT_REPLICATE_ANNOTATE_ROWS_EVENTS, + {"replicate-annotate-row-events", OPT_REPLICATE_ANNOTATE_ROWS_EVENTS, "Tells the slave to write annotate rows events recieved from the master " "to its own binary log. Sensible only in pair with log-slave-updates option.", - (uchar**) &opt_replicate_annotate_rows_events, - (uchar**) &opt_replicate_annotate_rows_events, + (uchar**) &opt_replicate_annotate_row_events, + (uchar**) &opt_replicate_annotate_row_events, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"binlog-do-db", OPT_BINLOG_DO_DB, "Tells the master it should log updates for the specified database, " @@ -6330,6 +6358,10 @@ struct my_option my_long_options[] = "Do an assert in handler::print_error() if we get a crashed table", &debug_assert_if_crashed_table, &debug_assert_if_crashed_table, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"debug-assert-on-error", OPT_DEBUG_ASSERT_ON_ERROR, + "Do an assert in various functions if we get a fatal error", + &my_assert_on_error, &my_assert_on_error, + 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, #endif {"default-character-set", OPT_DEFAULT_CHARACTER_SET_OLD, "Set the default character set (deprecated option, use --character-set-server instead).", @@ -6417,8 +6449,8 @@ struct my_option my_long_options[] = &opt_debugging, &opt_debugging, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"general_log", OPT_GENERAL_LOG, - "Enable/disable general log. Filename can be specified with --general-log-file or --log-basename. Is 'hostname.err' by default.", - &opt_log, &opt_log, 0, GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0}, + "Enable/disable general log. Filename can be specified with --general-log-file or --log-basename. Is 'hostname.log' by default.", + &opt_log, &opt_log, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, #ifdef HAVE_LARGE_PAGES {"large-pages", OPT_ENABLE_LARGE_PAGES, "Enable support for large pages. " "Disable with --skip-large-pages.", &opt_large_pages, &opt_large_pages, @@ -6585,8 +6617,7 @@ each time the SQL thread starts.", "log and this option just turns on --log-bin instead.", &opt_update_logname, &opt_update_logname, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, - {"log-warnings", 'W', "Log some not critical warnings to the general log " - "file.", + {"log-warnings", 'W', "Log some not critical warnings to the general log file. Value can be between 0-11; The higher value, the more warnings", &global_system_variables.log_warnings, &max_system_variables.log_warnings, 0, GET_ULONG, OPT_ARG, 1, 0, 0, 0, 0, 0}, @@ -7423,9 +7454,9 @@ each time the SQL thread starts.", &max_system_variables.max_tmp_tables, 0, GET_ULONG, REQUIRED_ARG, 32, 1, (longlong) ULONG_MAX, 0, 1, 0}, {"max_user_connections", OPT_MAX_USER_CONNECTIONS, - "The maximum number of active connections for a single user (0 = no limit).", - &max_user_connections, &max_user_connections, 0, GET_UINT, - REQUIRED_ARG, 0, 0, UINT_MAX, 0, 1, 0}, + "The maximum number of active connections for a single user (0 = no limit. In addition global max_user_connections counting and checking is permanently disabled).", + &max_user_connections, &max_user_connections, 0, GET_INT, + REQUIRED_ARG, 0, 0, INT_MAX, 0, 1, 0}, {"max_write_lock_count", OPT_MAX_WRITE_LOCK_COUNT, "After this many write locks, allow some read locks to run in between.", &max_write_lock_count, &max_write_lock_count, 0, GET_ULONG, @@ -7786,7 +7817,7 @@ each time the SQL thread starts.", "Define threads usage for handling queries: " "one-thread-per-connection" #if HAVE_POOL_OF_THREADS == 1 - ", pool-of-threads" + ", pool-of-threads " #endif "or no-threads.", &opt_thread_handling, &opt_thread_handling, @@ -9578,6 +9609,19 @@ static int get_options(int *argc,char **argv) myisam_block_size=(uint) 1 << my_bit_log2(opt_myisam_block_size); my_crc_dbug_check= opt_my_crc_dbug_check; + /* + Log mysys errors when we don't have a thd or thd->log_all_errors is set + (recovery) to the log. This is mainly useful for debugging strange system + errors. + */ + if (global_system_variables.log_warnings >= 10) + my_global_flags= MY_WME | ME_JUST_INFO; + /* Log all errors not handled by thd->handle_error() to my_message_sql() */ + if (global_system_variables.log_warnings >= 11) + my_global_flags|= ME_NOREFRESH; + if (my_assert_on_error) + debug_assert_if_crashed_table= 1; + /* long_query_time is in microseconds */ global_system_variables.long_query_time= max_system_variables.long_query_time= (longlong) (long_query_time * 1000000.0); @@ -9619,6 +9663,8 @@ static int get_options(int *argc,char **argv) if (!max_long_data_size_used) max_long_data_size= global_system_variables.max_allowed_packet; + /* Rember if max_user_connections was 0 at startup */ + max_user_connections_checking= max_user_connections != 0; return 0; } diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 3ed975a59bb..1226144f5ad 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -13644,7 +13644,6 @@ print_key(KEY_PART *key_part, const uchar *key, uint used_length) { char buff[1024]; const uchar *key_end= key+used_length; - String tmp(buff,sizeof(buff),&my_charset_bin); uint store_length; TABLE *table= key_part->field->table; my_bitmap_map *old_sets[2]; @@ -13653,6 +13652,7 @@ print_key(KEY_PART *key_part, const uchar *key, uint used_length) for (; key < key_end; key+=store_length, key_part++) { + String tmp(buff,sizeof(buff),&my_charset_bin); Field *field= key_part->field; store_length= key_part->store_length; diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index f8b6f80eb14..6fd307787a2 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -210,6 +210,74 @@ end_sj_materialize(JOIN *join, JOIN_TAB *join_tab, bool end_of_records); /* + Check if Materialization strategy is allowed for given subquery predicate. + + @param thd Thread handle + @param in_subs The subquery predicate + @param child_select The select inside predicate (the function will + check it is the only one) + + @return TRUE - Materialization is applicable + FALSE - Otherwise +*/ + +bool is_materialization_applicable(THD *thd, Item_in_subselect *in_subs, + st_select_lex *child_select) +{ + st_select_lex_unit* parent_unit= child_select->master_unit(); + /* + Check if the subquery predicate can be executed via materialization. + The required conditions are: + 0. The materialization optimizer switch was set. + 1. Subquery is a single SELECT (not a UNION). + TODO: this is a limitation that can be fixed + 2. Subquery is not a table-less query. In this case there is no + point in materializing. + 2A The upper query is not a table-less SELECT ... FROM DUAL. We + can't do materialization for SELECT .. FROM DUAL because it + does not call setup_subquery_materialization(). We could make + SELECT ... FROM DUAL call that function but that doesn't seem + to be the case that is worth handling. + 3. Either the subquery predicate is a top-level predicate, or at + least one partial match strategy is enabled. If no partial match + strategy is enabled, then materialization cannot be used for + non-top-level queries because it cannot handle NULLs correctly. + 4. Subquery is non-correlated + TODO: + This condition is too restrictive (limitation). It can be extended to: + (Subquery is non-correlated || + Subquery is correlated to any query outer to IN predicate || + (Subquery is correlated to the immediate outer query && + Subquery !contains {GROUP BY, ORDER BY [LIMIT], + aggregate functions}) && subquery predicate is not under "NOT IN")) + + (*) The subquery must be part of a SELECT statement. The current + condition also excludes multi-table update statements. + A note about prepared statements: we want the if-branch to be taken on + PREPARE and each EXECUTE. The rewrites are only done once, but we need + select_lex->sj_subselects list to be populated for every EXECUTE. + + */ + if (optimizer_flag(thd, OPTIMIZER_SWITCH_MATERIALIZATION) && // 0 + !child_select->is_part_of_union() && // 1 + parent_unit->first_select()->leaf_tables.elements && // 2 + thd->lex->sql_command == SQLCOM_SELECT && // * + child_select->outer_select()->leaf_tables.elements && // 2A + subquery_types_allow_materialization(in_subs) && + (in_subs->is_top_level_item() || //3 + optimizer_flag(thd, + OPTIMIZER_SWITCH_PARTIAL_MATCH_ROWID_MERGE) || //3 + optimizer_flag(thd, + OPTIMIZER_SWITCH_PARTIAL_MATCH_TABLE_SCAN)) && //3 + !in_subs->is_correlated) //4 + { + return TRUE; + } + return FALSE; +} + + +/* Check if we need JOIN::prepare()-phase subquery rewrites and if yes, do them SYNOPSIS @@ -340,7 +408,7 @@ int check_and_do_in_subquery_rewrites(JOIN *join) !select_lex->is_part_of_union() && // 2 !select_lex->group_list.elements && !join->order && // 3 !join->having && !select_lex->with_sum_func && // 4 - thd->thd_marker.emb_on_expr_nest && // 5 + in_subs->emb_on_expr_nest && // 5 select_lex->outer_select()->join && // 6 parent_unit->first_select()->leaf_tables.elements && // 7 !in_subs->in_strategy && // 8 @@ -353,7 +421,6 @@ int check_and_do_in_subquery_rewrites(JOIN *join) (void)subquery_types_allow_materialization(in_subs); - in_subs->emb_on_expr_nest= thd->thd_marker.emb_on_expr_nest; in_subs->is_flattenable_semijoin= TRUE; /* Register the subquery for further processing in flatten_subqueries() */ @@ -382,62 +449,17 @@ int check_and_do_in_subquery_rewrites(JOIN *join) */ if (in_subs) { - /* - Check if the subquery predicate can be executed via materialization. - The required conditions are: - 0. The materialization optimizer switch was set. - 1. Subquery is a single SELECT (not a UNION). - TODO: this is a limitation that can be fixed - 2. Subquery is not a table-less query. In this case there is no - point in materializing. - 2A The upper query is not a table-less SELECT ... FROM DUAL. We - can't do materialization for SELECT .. FROM DUAL because it - does not call setup_subquery_materialization(). We could make - SELECT ... FROM DUAL call that function but that doesn't seem - to be the case that is worth handling. - 3. Either the subquery predicate is a top-level predicate, or at - least one partial match strategy is enabled. If no partial match - strategy is enabled, then materialization cannot be used for - non-top-level queries because it cannot handle NULLs correctly. - 4. Subquery is non-correlated - TODO: - This condition is too restrictive (limitation). It can be extended to: - (Subquery is non-correlated || - Subquery is correlated to any query outer to IN predicate || - (Subquery is correlated to the immediate outer query && - Subquery !contains {GROUP BY, ORDER BY [LIMIT], - aggregate functions}) && subquery predicate is not under "NOT IN")) - - (*) The subquery must be part of a SELECT statement. The current - condition also excludes multi-table update statements. - A note about prepared statements: we want the if-branch to be taken on - PREPARE and each EXECUTE. The rewrites are only done once, but we need - select_lex->sj_subselects list to be populated for every EXECUTE. - - */ - if (optimizer_flag(thd, OPTIMIZER_SWITCH_MATERIALIZATION) && // 0 - !select_lex->is_part_of_union() && // 1 - parent_unit->first_select()->leaf_tables.elements && // 2 - thd->lex->sql_command == SQLCOM_SELECT && // * - select_lex->outer_select()->leaf_tables.elements && // 2A - subquery_types_allow_materialization(in_subs) && - (in_subs->is_top_level_item() || //3 - optimizer_flag(thd, - OPTIMIZER_SWITCH_PARTIAL_MATCH_ROWID_MERGE) || //3 - optimizer_flag(thd, - OPTIMIZER_SWITCH_PARTIAL_MATCH_TABLE_SCAN)) && //3 - !in_subs->is_correlated) //4 - { + if (is_materialization_applicable(thd, in_subs, select_lex)) + { in_subs->in_strategy|= SUBS_MATERIALIZATION; /* If the subquery is an AND-part of WHERE register for being processed with jtbm strategy */ - if (thd->thd_marker.emb_on_expr_nest == NO_JOIN_NEST && + if (in_subs->emb_on_expr_nest == NO_JOIN_NEST && optimizer_flag(thd, OPTIMIZER_SWITCH_SEMIJOIN)) { - in_subs->emb_on_expr_nest= thd->thd_marker.emb_on_expr_nest; in_subs->is_flattenable_semijoin= FALSE; if (!in_subs->is_registered_semijoin) { @@ -561,6 +583,16 @@ bool subquery_types_allow_materialization(Item_in_subselect *in_subs) if (inner->field_type() == MYSQL_TYPE_BLOB || inner->field_type() == MYSQL_TYPE_GEOMETRY) DBUG_RETURN(FALSE); + /* + Materialization also is unable to work when create_tmp_table() will + create a blob column because item->max_length is too big. + The following check is copied from Item::make_string_field(): + */ + if (inner->max_length / inner->collation.collation->mbmaxlen > + CONVERT_IF_BIGGER_TO_BLOB) + { + DBUG_RETURN(FALSE); + } break; case TIME_RESULT: if (mysql_type_to_time_type(outer->field_type()) != @@ -826,14 +858,14 @@ bool convert_join_subqueries_to_semijoins(JOIN *join) in_subq->unit->first_select()->join->table_count >= MAX_TABLES) break; if (convert_subq_to_sj(join, in_subq)) - DBUG_RETURN(TRUE); + goto restore_arena_and_fail; } else { if (join->table_count + 1 >= MAX_TABLES) break; if (convert_subq_to_jtbm(join, in_subq, &remove_item)) - DBUG_RETURN(TRUE); + goto restore_arena_and_fail; } if (remove_item) { @@ -842,7 +874,7 @@ bool convert_join_subqueries_to_semijoins(JOIN *join) Item *replace_me= in_subq->original_item(); if (replace_where_subcondition(join, tree, replace_me, new Item_int(1), FALSE)) - DBUG_RETURN(TRUE); /* purecov: inspected */ + goto restore_arena_and_fail; } } //skip_conversion: @@ -878,12 +910,6 @@ bool convert_join_subqueries_to_semijoins(JOIN *join) do_fix_fields)) DBUG_RETURN(TRUE); in_subq->substitution= NULL; -#if 0 - /* - Don't do the following, because the simplify_join() call is after this - call, and that call will save to prep_wher/prep_on_expr. - */ - /* If this is a prepared statement, repeat the above operation for prep_where (or prep_on_expr). Subquery-to-semijoin conversion is @@ -894,12 +920,15 @@ bool convert_join_subqueries_to_semijoins(JOIN *join) tree= (in_subq->emb_on_expr_nest == NO_JOIN_NEST)? &join->select_lex->prep_where : &(in_subq->emb_on_expr_nest->prep_on_expr); - - if (replace_where_subcondition(join, tree, replace_me, substitute, + /* + prep_on_expr/ prep_where may be NULL in some cases. + If that is the case, do nothing - simplify_joins() will copy + ON/WHERE expression into prep_on_expr/prep_where. + */ + if (*tree && replace_where_subcondition(join, tree, replace_me, substitute, FALSE)) DBUG_RETURN(TRUE); } -#endif /* Revert to the IN->EXISTS strategy in the rare case when the subquery could not be flattened. @@ -909,6 +938,12 @@ bool convert_join_subqueries_to_semijoins(JOIN *join) check_and_do_in_subquery_rewrites. */ in_subq->in_strategy= SUBS_IN_TO_EXISTS; + if (is_materialization_applicable(thd, in_subq, + in_subq->unit->first_select())) + { + in_subq->in_strategy|= SUBS_MATERIALIZATION; + } + in_subq= li++; } @@ -916,6 +951,11 @@ bool convert_join_subqueries_to_semijoins(JOIN *join) thd->restore_active_arena(arena, &backup); join->select_lex->sj_subselects.empty(); DBUG_RETURN(FALSE); + +restore_arena_and_fail: + if (arena) + thd->restore_active_arena(arena, &backup); + DBUG_RETURN(TRUE); } @@ -1391,7 +1431,8 @@ static bool convert_subq_to_jtbm(JOIN *parent_join, DBUG_ENTER("convert_subq_to_jtbm"); subq_pred->in_strategy &= ~SUBS_IN_TO_EXISTS; - subq_pred->optimize(&rows, &read_time); + if (subq_pred->optimize(&rows, &read_time)) + DBUG_RETURN(TRUE); subq_pred->jtbm_read_time= read_time; subq_pred->jtbm_record_count=rows; @@ -1764,6 +1805,9 @@ int pull_out_semijoin_tables(JOIN *join) All obtained information is saved and will be used by the main join optimization pass. + + NOTES + Because of Join::reoptimize(), this function may be called multiple times. RETURN FALSE Ok @@ -2138,6 +2182,17 @@ void advance_sj_state(JOIN *join, table_map remaining_tables, pos->sj_strategy= SJ_OPT_NONE; pos->prefix_dups_producing_tables= join->cur_dups_producing_tables; + + /* We're performing optimization inside SJ-Materialization nest */ + if (join->emb_sjm_nest) + { + pos->invalidate_firstmatch_prefix(); + pos->first_loosescan_table= MAX_TABLES; + pos->dupsweedout_tables= 0; + pos->sjm_scan_need_tables= 0; + return; + } + /* Initialize the state or copy it from prev. tables */ if (idx == join->const_tables) { @@ -2644,6 +2699,7 @@ ulonglong get_bound_sj_equalities(TABLE_LIST *sj_nest, { res |= 1ULL << i; } + i++; } return res; } @@ -2940,6 +2996,11 @@ bool setup_sj_materialization_part1(JOIN_TAB *sjm_tab) DBUG_ENTER("setup_sj_materialization"); JOIN_TAB *tab= sjm_tab->bush_children->start; TABLE_LIST *emb_sj_nest= tab->table->pos_in_table_list->embedding; + + /* Walk out of outer join nests until we reach the semi-join nest we're in */ + while (!emb_sj_nest->sj_mat_info) + emb_sj_nest= emb_sj_nest->embedding; + SJ_MATERIALIZATION_INFO *sjm= emb_sj_nest->sj_mat_info; THD *thd= tab->join->thd; /* First the calls come to the materialization function */ @@ -2988,6 +3049,9 @@ bool setup_sj_materialization_part2(JOIN_TAB *sjm_tab) DBUG_ENTER("setup_sj_materialization_part2"); JOIN_TAB *tab= sjm_tab->bush_children->start; TABLE_LIST *emb_sj_nest= tab->table->pos_in_table_list->embedding; + /* Walk out of outer join nests until we reach the semi-join nest we're in */ + while (!emb_sj_nest->sj_mat_info) + emb_sj_nest= emb_sj_nest->embedding; SJ_MATERIALIZATION_INFO *sjm= emb_sj_nest->sj_mat_info; THD *thd= tab->join->thd; uint i; @@ -3324,7 +3388,7 @@ TABLE *create_duplicate_weedout_tmp_table(THD *thd, bool using_unique_constraint=FALSE; bool use_packed_rows= FALSE; Field *field, *key_field; - uint blob_count, null_pack_length, null_count; + uint null_pack_length, null_count; uchar *null_flags; uchar *pos; DBUG_ENTER("create_duplicate_weedout_tmp_table"); @@ -3405,8 +3469,6 @@ TABLE *create_duplicate_weedout_tmp_table(THD *thd, share->keys_for_keyread.init(); share->keys_in_use.init(); - blob_count= 0; - /* Create the field */ { /* @@ -3823,6 +3885,8 @@ int setup_semijoin_dups_elimination(JOIN *join, ulonglong options, { /* We jump from the last table to the first one */ tab->loosescan_match_tab= tab + pos->n_sj_tables - 1; + for (uint j= i; j < i + pos->n_sj_tables; j++) + join->join_tab[j].inside_loosescan_range= TRUE; /* Calculate key length */ keylen= 0; @@ -4484,20 +4548,6 @@ bool JOIN::choose_subquery_plan(table_map join_tables) if (outer_join && outer_join->table_count > 0) { /* - The index of the last JOIN_TAB in the outer JOIN where in_subs is - attached (pushed to). - */ - uint max_outer_join_tab_idx; - /* - Make_cond_for_table is called for predicates only in the WHERE/ON - clauses. In all other cases, predicates are not pushed to any - JOIN_TAB, and their join_tab_idx remains MAX_TABLES. Such predicates - are evaluated for each complete row of the outer join. - */ - max_outer_join_tab_idx= (in_subs->get_join_tab_idx() == MAX_TABLES) ? - outer_join->table_count - 1: - in_subs->get_join_tab_idx(); - /* TODO: Currently outer_lookup_keys is computed as the number of rows in the partial join including the JOIN_TAB where the IN predicate is @@ -4509,7 +4559,7 @@ bool JOIN::choose_subquery_plan(table_map join_tables) If the join order: t1, t2, the number of unique lookup keys is ~ to the number of unique values t2.c2 in the partial join t1 join t2. */ - outer_join->get_partial_cost_and_fanout(max_outer_join_tab_idx, + outer_join->get_partial_cost_and_fanout(in_subs->get_join_tab_idx(), table_map(-1), &dummy, &outer_lookup_keys); @@ -4644,17 +4694,12 @@ bool JOIN::choose_subquery_plan(table_map join_tables) const_tables != table_count) { /* - The subquery was not reoptimized either because the user allowed only - the IN-EXISTS strategy, or because materialization was not possible - based on semantic analysis. Cleanup the original plan and reoptimize. + The subquery was not reoptimized with the newly injected IN-EXISTS + conditions either because the user allowed only the IN-EXISTS strategy, + or because materialization was not possible based on semantic analysis. */ - for (uint i= 0; i < table_count; i++) - { - join_tab[i].keyuse= NULL; - join_tab[i].checked_keys.clear_all(); - } - if ((reopt_result= reoptimize(in_to_exists_where, join_tables, NULL)) == - REOPT_ERROR) + reopt_result= reoptimize(in_to_exists_where, join_tables, NULL); + if (reopt_result == REOPT_ERROR) return TRUE; } diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc index 43fbe17a25b..7b4c48497fa 100644 --- a/sql/opt_sum.cc +++ b/sql/opt_sum.cc @@ -625,6 +625,8 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo, /* Condition doesn't restrict the used table */ DBUG_RETURN(!cond->const_item()); } + else if (cond->is_expensive()) + DBUG_RETURN(FALSE); if (cond->type() == Item::COND_ITEM) { if (((Item_cond*) cond)->functype() == Item_func::COND_OR_FUNC) @@ -671,6 +673,8 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo, case Item_func::GE_FUNC: break; case Item_func::BETWEEN: + if (((Item_func_between*) cond)->negated) + DBUG_RETURN(FALSE); between= 1; break; case Item_func::MULT_EQUAL_FUNC: diff --git a/sql/records.cc b/sql/records.cc index 60c801f8977..b125c201621 100644 --- a/sql/records.cc +++ b/sql/records.cc @@ -59,7 +59,6 @@ void init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table, bzero((char*) info,sizeof(*info)); info->thd= thd; info->table= table; - info->file= table->file; info->record= table->record[0]; info->print_error= print_error; info->unlock_row= rr_unlock_row; @@ -169,7 +168,6 @@ bool init_read_record(READ_RECORD *info,THD *thd, TABLE *table, bzero((char*) info,sizeof(*info)); info->thd=thd; info->table=table; - info->file= table->file; info->forms= &info->table; /* Only one table */ if (table->s->tmp_table == NON_TRANSACTIONAL_TMP_TABLE && @@ -291,9 +289,9 @@ void end_read_record(READ_RECORD *info) { filesort_free_buffers(info->table,0); if (info->table->created) - (void) info->file->extra(HA_EXTRA_NO_CACHE); + (void) info->table->file->extra(HA_EXTRA_NO_CACHE); if (info->read_record != rr_quick) // otherwise quick_range does it - (void) info->file->ha_index_or_rnd_end(); + (void) info->table->file->ha_index_or_rnd_end(); info->table=0; } } @@ -352,7 +350,7 @@ static int rr_quick(READ_RECORD *info) static int rr_index_first(READ_RECORD *info) { - int tmp= info->file->ha_index_first(info->record); + int tmp= info->table->file->ha_index_first(info->record); info->read_record= rr_index; if (tmp) tmp= rr_handle_error(info, tmp); @@ -378,7 +376,7 @@ static int rr_index_first(READ_RECORD *info) static int rr_index(READ_RECORD *info) { - int tmp= info->file->ha_index_next(info->record); + int tmp= info->table->file->ha_index_next(info->record); if (tmp) tmp= rr_handle_error(info, tmp); return tmp; @@ -388,7 +386,7 @@ static int rr_index(READ_RECORD *info) int rr_sequential(READ_RECORD *info) { int tmp; - while ((tmp= info->file->ha_rnd_next(info->record))) + while ((tmp= info->table->file->ha_rnd_next(info->record))) { /* rnd_next can return RECORD_DELETED for MyISAM when one thread is @@ -413,7 +411,7 @@ static int rr_from_tempfile(READ_RECORD *info) { if (my_b_read(info->io_cache,info->ref_pos,info->ref_length)) return -1; /* End of file */ - if (!(tmp= info->file->ha_rnd_pos(info->record,info->ref_pos))) + if (!(tmp= info->table->file->ha_rnd_pos(info->record,info->ref_pos))) break; /* The following is extremely unlikely to happen */ if (tmp == HA_ERR_RECORD_DELETED || @@ -464,7 +462,7 @@ static int rr_from_pointers(READ_RECORD *info) cache_pos= info->cache_pos; info->cache_pos+= info->ref_length; - if (!(tmp= info->file->ha_rnd_pos(info->record,cache_pos))) + if (!(tmp= info->table->file->ha_rnd_pos(info->record,cache_pos))) break; /* The following is extremely unlikely to happen */ @@ -597,7 +595,7 @@ static int rr_from_cache(READ_RECORD *info) record=uint3korr(position); position+=3; record_pos=info->cache+record*info->reclength; - if ((error=(int16) info->file->ha_rnd_pos(record_pos,info->ref_pos))) + if ((error=(int16) info->table->file->ha_rnd_pos(record_pos,info->ref_pos))) { record_pos[info->error_offset]=1; shortstore(record_pos,error); diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h index 0ea3c23bfd8..a143f5251af 100644 --- a/sql/rpl_rli.h +++ b/sql/rpl_rli.h @@ -425,14 +425,14 @@ public: /** Save pointer to Annotate_rows event and switch on the - binlog_annotate_rows_events for this sql thread. + binlog_annotate_row_events for this sql thread. To be called when sql thread recieves an Annotate_rows event. */ inline void set_annotate_event(Annotate_rows_log_event *event) { free_annotate_event(); m_annotate_event= event; - sql_thd->variables.binlog_annotate_rows_events= 1; + sql_thd->variables.binlog_annotate_row_events= 1; } /** @@ -446,7 +446,7 @@ public: /** Delete saved Annotate_rows event (if any) and switch off the - binlog_annotate_rows_events for this sql thread. + binlog_annotate_row_events for this sql thread. To be called when sql thread has applied the last (i.e. with STMT_END_F flag) rbr event. */ @@ -454,7 +454,7 @@ public: { if (m_annotate_event) { - sql_thd->variables.binlog_annotate_rows_events= 0; + sql_thd->variables.binlog_annotate_row_events= 0; delete m_annotate_event; m_annotate_event= 0; } diff --git a/sql/scheduler.cc b/sql/scheduler.cc index 5b8f834aecc..5b83bb4753e 100644 --- a/sql/scheduler.cc +++ b/sql/scheduler.cc @@ -376,7 +376,7 @@ void libevent_kill_thd_callback(int Fd, short, void*) { THD *thd= (THD*)list->data; list= list_rest(list); - if (thd->killed == THD::KILL_CONNECTION) + if ((int) thd->killed >= (int) KILL_CONNECTION) { /* Delete from libevent and add to the processing queue. @@ -510,7 +510,7 @@ static void libevent_connection_close(THD *thd) DBUG_ENTER("libevent_connection_close"); DBUG_PRINT("enter", ("thd: %p", thd)); - thd->killed= THD::KILL_CONNECTION; // Avoid error messages + thd->killed= KILL_CONNECTION; // Avoid error messages if (thd->net.vio->sd >= 0) // not already closed { @@ -531,9 +531,9 @@ static void libevent_connection_close(THD *thd) static bool libevent_should_close_connection(THD* thd) { - return thd->net.error || - thd->net.vio == 0 || - thd->killed == THD::KILL_CONNECTION; + return (thd->net.error || + thd->net.vio == 0 || + (int) thd->killed >= (int) KILL_CONNECTION); } diff --git a/sql/set_var.cc b/sql/set_var.cc index 0a06b3254f6..289e82f20ee 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -189,8 +189,8 @@ static sys_var_const sys_back_log(&vars, "back_log", (uchar*) &back_log); static sys_var_const_os_str sys_basedir(&vars, "basedir", mysql_home); static sys_var_thd_bool -sys_binlog_annotate_rows_events(&vars, "binlog_annotate_rows_events", - &SV::binlog_annotate_rows_events); +sys_binlog_annotate_row_events(&vars, "binlog_annotate_row_events", + &SV::binlog_annotate_row_events); static sys_var_long_ptr sys_binlog_cache_size(&vars, "binlog_cache_size", &binlog_cache_size); static sys_var_thd_binlog_format sys_binlog_format(&vars, "binlog_format", @@ -3082,7 +3082,19 @@ void sys_var_thd_time_zone::set_default(THD *thd, enum_var_type type) bool sys_var_max_user_conn::check(THD *thd, set_var *var) { if (var->type == OPT_GLOBAL) + { + if (! max_user_connections_checking) + { + /* + We can't change the value of max_user_connections from 0 as then + connect counting would be wrong. + */ + my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), + "--max-user-connections=0"); + return TRUE; + } return sys_var_thd::check(thd, var); + } else { /* @@ -3098,7 +3110,7 @@ bool sys_var_max_user_conn::update(THD *thd, set_var *var) { DBUG_ASSERT(var->type == OPT_GLOBAL); pthread_mutex_lock(&LOCK_global_system_variables); - max_user_connections= (uint)var->save_result.ulonglong_value; + max_user_connections= (int) var->save_result.ulonglong_value; pthread_mutex_unlock(&LOCK_global_system_variables); return 0; } diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index 928a28aba24..035267b0b5d 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -6290,3 +6290,9 @@ ER_QUERY_CACHE_IS_GLOBALY_DISABLED eng "Query cache is globally disabled and you can't enable it only for this session" ER_VIEW_ORDERBY_IGNORED eng "View '%-.192s'.'%-.192s' ORDER BY clause ignored because there is other ORDER BY clause already." +ER_CONNECTION_KILLED 70100 + eng "Connection was killed" +ER_INTERNAL_ERROR + eng "Internal error: '%-.192s'" + + diff --git a/sql/slave.cc b/sql/slave.cc index cfa6c9fdc56..fa7fabb5d99 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -524,7 +524,7 @@ terminate_slave_thread(THD *thd, IF_DBUG(int err= ) pthread_kill(thd->real_id, thr_client_alarm); DBUG_ASSERT(err != EINVAL); #endif - thd->awake(THD::NOT_KILLED); + thd->awake(KILL_CONNECTION); pthread_mutex_unlock(&thd->LOCK_thd_data); /* @@ -837,6 +837,8 @@ bool is_network_error(uint errorno) errorno == CR_SERVER_GONE_ERROR || errorno == CR_SERVER_LOST || errorno == ER_CON_COUNT_ERROR || + errorno == ER_CONNECTION_KILLED || + errorno == ER_NEW_ABORTING_CONNECTION || errorno == ER_SERVER_SHUTDOWN) return TRUE; @@ -2012,7 +2014,7 @@ static int request_dump(MYSQL* mysql, Master_info* mi, *suppress_warnings= FALSE; - if (opt_log_slave_updates && opt_replicate_annotate_rows_events) + if (opt_log_slave_updates && opt_replicate_annotate_row_events) binlog_flags|= BINLOG_SEND_ANNOTATE_ROWS_EVENT; // TODO if big log files: Change next to int8store() @@ -3076,11 +3078,11 @@ pthread_handler_t handle_slave_sql(void *arg) 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 /* - binlog_annotate_rows_events must be TRUE only after an Annotate_rows event + binlog_annotate_row_events must be TRUE only after an Annotate_rows event has been recieved and only till the last corresponding rbr event has been applied. In all other cases it must be FALSE. */ - thd->variables.binlog_annotate_rows_events= 0; + thd->variables.binlog_annotate_row_events= 0; pthread_mutex_lock(&LOCK_thread_count); threads.append(thd); pthread_mutex_unlock(&LOCK_thread_count); diff --git a/sql/slave.h b/sql/slave.h index 3333e583559..bd4302ff3a1 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -106,7 +106,7 @@ extern MYSQL_PLUGIN_IMPORT char *relay_log_info_file; extern char *opt_relay_logname, *opt_relaylog_index_name; extern my_bool opt_skip_slave_start, opt_reckless_slave; extern my_bool opt_log_slave_updates; -extern my_bool opt_replicate_annotate_rows_events; +extern my_bool opt_replicate_annotate_row_events; extern ulonglong relay_log_space_limit; /* diff --git a/sql/sp.cc b/sql/sp.cc index d2c732c2100..57dbc04be4e 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -14,8 +14,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "mysql_priv.h" -#include "sp.h" #include "sp_head.h" +#include "sp.h" #include "sp_cache.h" #include "sql_trigger.h" @@ -23,7 +23,7 @@ static bool create_string(THD *thd, String *buf, - int sp_type, + stored_procedure_type sp_type, const char *db, ulong dblen, const char *name, ulong namelen, const char *params, ulong paramslen, @@ -33,7 +33,8 @@ create_string(THD *thd, String *buf, const LEX_STRING *definer_user, const LEX_STRING *definer_host); static int -db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, +db_load_routine(THD *thd, stored_procedure_type type, sp_name *name, + sp_head **sphp, ulong sql_mode, const char *params, const char *returns, const char *body, st_sp_chistics &chistics, const char *definer, longlong created, longlong modified, @@ -490,7 +491,8 @@ static TABLE *open_proc_table_for_update(THD *thd) */ static int -db_find_routine_aux(THD *thd, int type, sp_name *name, TABLE *table) +db_find_routine_aux(THD *thd, stored_procedure_type type, sp_name *name, + TABLE *table) { uchar key[MAX_KEY_LENGTH]; // db, name, optional key length type DBUG_ENTER("db_find_routine_aux"); @@ -543,7 +545,8 @@ db_find_routine_aux(THD *thd, int type, sp_name *name, TABLE *table) */ static int -db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp) +db_find_routine(THD *thd, stored_procedure_type type, sp_name *name, + sp_head **sphp) { TABLE *table; const char *params, *returns, *body; @@ -711,7 +714,8 @@ Silence_deprecated_warning::handle_error(uint sql_errno, const char *message, static int -db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, +db_load_routine(THD *thd, stored_procedure_type type, + sp_name *name, sp_head **sphp, ulong sql_mode, const char *params, const char *returns, const char *body, st_sp_chistics &chistics, const char *definer, longlong created, longlong modified, @@ -890,7 +894,7 @@ sp_returns_type(THD *thd, String &result, sp_head *sp) */ int -sp_create_routine(THD *thd, int type, sp_head *sp) +sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp) { int ret; TABLE *table; @@ -906,7 +910,8 @@ sp_create_routine(THD *thd, int type, sp_head *sp) bool save_binlog_row_based; DBUG_ENTER("sp_create_routine"); - DBUG_PRINT("enter", ("type: %d name: %.*s",type, (int) sp->m_name.length, + DBUG_PRINT("enter", ("type: %d name: %.*s", (int) type, + (int) sp->m_name.length, sp->m_name.str)); String retstr(64); retstr.set_charset(system_charset_info); @@ -1151,7 +1156,7 @@ done: */ int -sp_drop_routine(THD *thd, int type, sp_name *name) +sp_drop_routine(THD *thd, stored_procedure_type type, sp_name *name) { TABLE *table; int ret; @@ -1211,14 +1216,16 @@ sp_drop_routine(THD *thd, int type, sp_name *name) */ int -sp_update_routine(THD *thd, int type, sp_name *name, st_sp_chistics *chistics) +sp_update_routine(THD *thd, stored_procedure_type type, sp_name *name, + st_sp_chistics *chistics) { TABLE *table; int ret; bool save_binlog_row_based; DBUG_ENTER("sp_update_routine"); DBUG_PRINT("enter", ("type: %d name: %.*s", - type, (int) name->m_name.length, name->m_name.str)); + (int) type, + (int) name->m_name.length, name->m_name.str)); DBUG_ASSERT(type == TYPE_ENUM_PROCEDURE || type == TYPE_ENUM_FUNCTION); @@ -1346,7 +1353,7 @@ err: */ bool -sp_show_create_routine(THD *thd, int type, sp_name *name) +sp_show_create_routine(THD *thd, stored_procedure_type type, sp_name *name) { bool err_status= TRUE; sp_head *sp; @@ -1404,8 +1411,8 @@ sp_show_create_routine(THD *thd, int type, sp_name *name) */ sp_head * -sp_find_routine(THD *thd, int type, sp_name *name, sp_cache **cp, - bool cache_only) +sp_find_routine(THD *thd, stored_procedure_type type, sp_name *name, + sp_cache **cp, bool cache_only) { sp_head *sp; ulong depth= (type == TYPE_ENUM_PROCEDURE ? @@ -1562,7 +1569,7 @@ sp_exist_routines(THD *thd, TABLE_LIST *routines, bool any) */ int -sp_routine_exists_in_table(THD *thd, int type, sp_name *name) +sp_routine_exists_in_table(THD *thd, stored_procedure_type type, sp_name *name) { TABLE *table; int ret; @@ -1729,7 +1736,7 @@ static bool add_used_routine(LEX *lex, Query_arena *arena, */ void sp_add_used_routine(LEX *lex, Query_arena *arena, - sp_name *rt, char rt_type) + sp_name *rt, enum stored_procedure_type rt_type) { rt->set_routine_type(rt_type); (void)add_used_routine(lex, arena, &rt->m_sroutines_key, 0); @@ -1885,9 +1892,11 @@ sp_cache_routines_and_add_tables_aux(THD *thd, LEX *lex, for (Sroutine_hash_entry *rt= start; rt; rt= rt->next) { sp_name name(thd, rt->key.str, rt->key.length); - int type= rt->key.str[0]; + stored_procedure_type type= (stored_procedure_type) rt->key.str[0]; sp_head *sp; + if (type == TYPE_ENUM_TRIGGER) + continue; if (!(sp= sp_cache_lookup((type == TYPE_ENUM_FUNCTION ? &thd->sp_func_cache : &thd->sp_proc_cache), &name))) @@ -2076,7 +2085,7 @@ sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex, */ static bool create_string(THD *thd, String *buf, - int type, + stored_procedure_type type, const char *db, ulong dblen, const char *name, ulong namelen, const char *params, ulong paramslen, @@ -39,26 +39,28 @@ int sp_drop_db_routines(THD *thd, char *db); sp_head * -sp_find_routine(THD *thd, int type, sp_name *name, +sp_find_routine(THD *thd, stored_procedure_type type, sp_name *name, sp_cache **cp, bool cache_only); bool sp_exist_routines(THD *thd, TABLE_LIST *procs, bool any); int -sp_routine_exists_in_table(THD *thd, int type, sp_name *name); +sp_routine_exists_in_table(THD *thd, stored_procedure_type type, + sp_name *name); bool -sp_show_create_routine(THD *thd, int type, sp_name *name); +sp_show_create_routine(THD *thd, stored_procedure_type type, sp_name *name); int -sp_create_routine(THD *thd, int type, sp_head *sp); +sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp); int -sp_update_routine(THD *thd, int type, sp_name *name, st_sp_chistics *chistics); +sp_update_routine(THD *thd, stored_procedure_type type, sp_name *name, + st_sp_chistics *chistics); int -sp_drop_routine(THD *thd, int type, sp_name *name); +sp_drop_routine(THD *thd, stored_procedure_type type, sp_name *name); /* Procedures for pre-caching of stored routines and building table list @@ -67,7 +69,7 @@ sp_drop_routine(THD *thd, int type, sp_name *name); void sp_get_prelocking_info(THD *thd, bool *need_prelocking, bool *first_no_prelocking); void sp_add_used_routine(LEX *lex, Query_arena *arena, - sp_name *rt, char rt_type); + sp_name *rt, stored_procedure_type rt_type); void sp_remove_not_own_routines(LEX *lex); bool sp_update_sp_used_routines(HASH *dst, HASH *src); int sp_cache_routines_and_add_tables(THD *thd, LEX *lex, diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 93743c2985b..124abf587c6 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -375,8 +375,8 @@ sp_eval_expr(THD *thd, Field *result_field, Item **expr_item_ptr) thd->count_cuted_fields= CHECK_FIELD_ERROR_FOR_NULL; thd->abort_on_warning= - thd->variables.sql_mode & - (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES); + test(thd->variables.sql_mode & + (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES)); thd->transaction.stmt.modified_non_trans_table= FALSE; /* Save the value in the field. Convert the value if needed. */ @@ -1313,7 +1313,7 @@ sp_head::execute(THD *thd) ctx->enter_handler(hip); thd->clear_error(); thd->is_fatal_error= 0; - thd->killed= THD::NOT_KILLED; + thd->killed= NOT_KILLED; thd->mysys_var->abort= 0; continue; } @@ -1363,7 +1363,7 @@ sp_head::execute(THD *thd) If the DB has changed, the pointer has changed too, but the original thd->db will then have been freed */ - if (cur_db_changed && thd->killed != THD::KILL_CONNECTION) + if (cur_db_changed && thd->killed < KILL_CONNECTION) { /* Force switching back to the saved current database, because it may be @@ -1797,7 +1797,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, thd->options= binlog_save_options; if (thd->binlog_evt_union.unioned_events) { - int errcode = query_error_code(thd, thd->killed == THD::NOT_KILLED); + int errcode = query_error_code(thd, thd->killed == NOT_KILLED); Query_log_event qinfo(thd, binlog_buf.ptr(), binlog_buf.length(), thd->binlog_evt_union.unioned_events_trans, FALSE, errcode); if (mysql_bin_log.write(&qinfo) && diff --git a/sql/sp_head.h b/sql/sp_head.h index d422adc8927..dc237163716 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -28,11 +28,16 @@ @ingroup Runtime_Environment @{ */ -// Values for the type enum. This reflects the order of the enum declaration -// in the CREATE TABLE command. -#define TYPE_ENUM_FUNCTION 1 -#define TYPE_ENUM_PROCEDURE 2 -#define TYPE_ENUM_TRIGGER 3 +/* + Values for the type enum. This reflects the order of the enum declaration + in the CREATE TABLE command. +*/ +enum stored_procedure_type +{ + TYPE_ENUM_FUNCTION=1, + TYPE_ENUM_PROCEDURE=2, + TYPE_ENUM_TRIGGER=3 +}; Item_result sp_map_result_type(enum enum_field_types type); @@ -134,9 +139,9 @@ public: // Init. the qualified name from the db and name. void init_qname(THD *thd); // thd for memroot allocation - void set_routine_type(char type) + void set_routine_type(stored_procedure_type type) { - m_sroutines_key.str[0]= type; + m_sroutines_key.str[0]= (char) type; } ~sp_name() @@ -170,8 +175,7 @@ public: HAS_SQLCOM_FLUSH= 4096 }; - /** TYPE_ENUM_FUNCTION, TYPE_ENUM_PROCEDURE or TYPE_ENUM_TRIGGER */ - int m_type; + stored_procedure_type m_type; uint m_flags; // Boolean attributes of a stored routine Create_field m_return_field_def; /**< This is used for FUNCTIONs only. */ diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index dc1405a00a4..ae286878cea 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -2089,7 +2089,7 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo, table->field[next_field+2]->store((longlong) mqh.conn_per_hour, TRUE); if (table->s->fields >= 36 && (mqh.specified_limits & USER_RESOURCES::USER_CONNECTIONS)) - table->field[next_field+3]->store((longlong) mqh.user_conn, TRUE); + table->field[next_field+3]->store((longlong) mqh.user_conn, FALSE); mqh_used= mqh_used || mqh.questions || mqh.updates || mqh.conn_per_hour; next_field+=4; @@ -4578,7 +4578,8 @@ ulong get_column_grant(THD *thd, GRANT_INFO *grant, /* Help function for mysql_show_grants */ -static void add_user_option(String *grant, ulong value, const char *name) +static void add_user_option(String *grant, long value, const char *name, + my_bool is_signed) { if (value) { @@ -4586,7 +4587,7 @@ static void add_user_option(String *grant, ulong value, const char *name) grant->append(' '); grant->append(name, strlen(name)); grant->append(' '); - p=int10_to_str(value, buff, 10); + p=int10_to_str(value, buff, is_signed ? -10 : 10); grant->append(buff,p-buff); } } @@ -4768,13 +4769,13 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user) if (want_access & GRANT_ACL) global.append(STRING_WITH_LEN(" GRANT OPTION")); add_user_option(&global, acl_user->user_resource.questions, - "MAX_QUERIES_PER_HOUR"); + "MAX_QUERIES_PER_HOUR", 0); add_user_option(&global, acl_user->user_resource.updates, - "MAX_UPDATES_PER_HOUR"); + "MAX_UPDATES_PER_HOUR", 0); add_user_option(&global, acl_user->user_resource.conn_per_hour, - "MAX_CONNECTIONS_PER_HOUR"); + "MAX_CONNECTIONS_PER_HOUR", 0); add_user_option(&global, acl_user->user_resource.user_conn, - "MAX_USER_CONNECTIONS"); + "MAX_USER_CONNECTIONS", 1); } protocol->prepare_for_resend(); protocol->store(global.ptr(),global.length(),global.charset()); @@ -6978,7 +6979,6 @@ bool check_routine_level_acl(THD *thd, const char *db, const char *name, #undef HAVE_OPENSSL #ifdef NO_EMBEDDED_ACCESS_CHECKS #define initialized 0 -#define decrease_user_connections(X) /* nothing */ #define check_for_max_user_connections(X,Y) 0 #define get_or_create_user_conn(A,B,C,D) 0 #endif @@ -8148,10 +8148,16 @@ bool acl_authenticate(THD *thd, uint connect_errors, DBUG_RETURN(1); } - /* Don't allow the user to connect if he has done too many queries */ - if ((acl_user->user_resource.questions || acl_user->user_resource.updates || + /* + Don't allow the user to connect if he has done too many queries. + As we are testing max_user_connections == 0 here, it means that we + can't let the user change max_user_connections from 0 in the server + without a restart as it would lead to wrong connect counting. + */ + if ((acl_user->user_resource.questions || + acl_user->user_resource.updates || acl_user->user_resource.conn_per_hour || - acl_user->user_resource.user_conn || max_user_connections) && + acl_user->user_resource.user_conn || max_user_connections_checking) && get_or_create_user_conn(thd, (opt_old_style_user_limits ? sctx->user : sctx->priv_user), (opt_old_style_user_limits ? sctx->host_or_ip : sctx->priv_host), @@ -8164,9 +8170,11 @@ bool acl_authenticate(THD *thd, uint connect_errors, if (thd->user_connect && (thd->user_connect->user_resources.conn_per_hour || thd->user_connect->user_resources.user_conn || - max_user_connections) && + max_user_connections_checking) && check_for_max_user_connections(thd, thd->user_connect)) { + /* Ensure we don't decrement thd->user_connections->connections twice */ + thd->user_connect= 0; status_var_increment(denied_connections); DBUG_RETURN(1); // The error is set in check_for_max_user_connections() } @@ -8207,12 +8215,7 @@ bool acl_authenticate(THD *thd, uint connect_errors, if (mysql_change_db(thd, &mpvio.db, FALSE)) { /* mysql_change_db() has pushed the error message. */ - if (thd->user_connect) - { - status_var_increment(thd->status_var.access_denied_errors); - decrease_user_connections(thd->user_connect); - thd->user_connect= 0; - } + status_var_increment(thd->status_var.access_denied_errors); DBUG_RETURN(1); } } diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 5f5f8125c79..cacec989518 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -62,7 +62,7 @@ private: bool Prelock_error_handler::handle_error(uint sql_errno, const char * /* message */, - MYSQL_ERROR::enum_warning_level /* level */, + MYSQL_ERROR::enum_warning_level level, THD * /* thd */) { if (sql_errno == ER_NO_SUCH_TABLE) @@ -71,7 +71,8 @@ Prelock_error_handler::handle_error(uint sql_errno, return TRUE; } - m_unhandled_errors++; + if (level == MYSQL_ERROR::WARN_LEVEL_ERROR) + m_unhandled_errors++; return FALSE; } @@ -6446,11 +6447,25 @@ find_field_in_tables(THD *thd, Item_ident *item, { SELECT_LEX *current_sel= thd->lex->current_select; SELECT_LEX *last_select= table_ref->select_lex; + bool all_merged= TRUE; + for (SELECT_LEX *sl= current_sel; sl && sl!=last_select; + sl=sl->outer_select()) + { + Item *subs= sl->master_unit()->item; + if (subs->type() == Item::SUBSELECT_ITEM && + ((Item_subselect*)subs)->substype() == Item_subselect::IN_SUBS && + ((Item_in_subselect*)subs)->in_strategy & SUBS_SEMI_JOIN) + { + continue; + } + all_merged= FALSE; + break; + } /* If the field was an outer referencee, mark all selects using this sub query as dependent on the outer query */ - if (current_sel != last_select) + if (!all_merged && current_sel != last_select) { mark_select_range_as_dependent(thd, last_select, current_sel, found, *ref, item); @@ -7847,6 +7862,7 @@ bool setup_tables(THD *thd, Name_resolution_context *context, { table_list->table->tablenr= table_list->tablenr_exec; table_list->table->map= table_list->map_exec; + table_list->table->maybe_null= table_list->maybe_null_exec; table_list->table->pos_in_table_list= table_list; } select_lex->leaf_tables.push_back(table_list); @@ -8275,7 +8291,6 @@ int setup_conds(THD *thd, TABLE_LIST *tables, List<TABLE_LIST> &leaves, SELECT_LEX *select_lex= thd->lex->current_select; Query_arena *arena= thd->stmt_arena, backup; TABLE_LIST *table= NULL; // For HP compilers - TABLE_LIST *save_emb_on_expr_nest= thd->thd_marker.emb_on_expr_nest; List_iterator<TABLE_LIST> ti(leaves); /* it_is_update set to TRUE when tables of primary SELECT_LEX (SELECT_LEX @@ -8316,7 +8331,6 @@ int setup_conds(THD *thd, TABLE_LIST *tables, List<TABLE_LIST> &leaves, goto err_no_arena; } - thd->thd_marker.emb_on_expr_nest= NO_JOIN_NEST; if (*conds) { thd->where="where clause"; @@ -8330,11 +8344,11 @@ int setup_conds(THD *thd, TABLE_LIST *tables, List<TABLE_LIST> &leaves, */ if ((*conds)->type() == Item::FIELD_ITEM && !derived) wrap_ident(thd, conds); + (*conds)->mark_as_condition_AND_part(NO_JOIN_NEST); if ((!(*conds)->fixed && (*conds)->fix_fields(thd, conds)) || (*conds)->check_cols(1)) goto err_no_arena; } - thd->thd_marker.emb_on_expr_nest= save_emb_on_expr_nest; /* Apply fix_fields() to all ON clauses at all levels of nesting, @@ -8350,8 +8364,8 @@ int setup_conds(THD *thd, TABLE_LIST *tables, List<TABLE_LIST> &leaves, if (embedded->on_expr) { /* Make a join an a expression */ - thd->thd_marker.emb_on_expr_nest= embedded; thd->where="on clause"; + embedded->on_expr->mark_as_condition_AND_part(embedded); if ((!embedded->on_expr->fixed && embedded->on_expr->fix_fields(thd, &embedded->on_expr)) || embedded->on_expr->check_cols(1)) @@ -8375,7 +8389,6 @@ int setup_conds(THD *thd, TABLE_LIST *tables, List<TABLE_LIST> &leaves, } } } - thd->thd_marker.emb_on_expr_nest= save_emb_on_expr_nest; if (!thd->stmt_arena->is_conventional()) { @@ -8897,7 +8910,7 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name, { if (!in_use->killed) { - in_use->killed= THD::KILL_CONNECTION; + in_use->killed= KILL_SYSTEM_THREAD; pthread_mutex_lock(&in_use->mysys_var->mutex); if (in_use->mysys_var->current_cond) { @@ -9231,7 +9244,7 @@ void mysql_wait_completed_table(ALTER_PARTITION_PARAM_TYPE *lpt, TABLE *my_table if ((in_use->system_thread & SYSTEM_THREAD_DELAYED_INSERT) && ! in_use->killed) { - in_use->killed= THD::KILL_CONNECTION; + in_use->killed= KILL_SYSTEM_THREAD; pthread_mutex_lock(&in_use->mysys_var->mutex); if (in_use->mysys_var->current_cond) { diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index d7040a825fb..8631a13eae7 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -391,7 +391,7 @@ static void debug_wait_for_kill(const char *info) sql_print_information("%s", info); while(!thd->killed) my_sleep(1000); - thd->killed= THD::NOT_KILLED; + thd->killed= NOT_KILLED; /* Remove the set debug variable, to ensure we don't get stuck on it again This is needed as for MyISAM, invalidate_table() may be called twice @@ -491,11 +491,12 @@ static void make_base_query(String *new_query, continue; // Continue with next symbol case '/': // Start of comment ? /* - Comment of format /#!number #/, must be skipped. + Comment of format /#!number #/ or /#M!number #/, must be skipped. These may include '"' and other comments, but it should be safe to parse the content as a normal string. */ - if (query[0] != '*' || query[1] == '!') + if (query[0] != '*' || query[1] == '!' || + (query[1] == 'M' && query[2] == '!')) break; query++; // skip "/" @@ -4438,7 +4439,7 @@ void Query_cache::wreck(uint line, const char *message) DBUG_PRINT("warning", ("%5d QUERY CACHE WRECK => DISABLED",line)); DBUG_PRINT("warning", ("==================================")); if (thd) - thd->killed= THD::KILL_CONNECTION; + thd->killed= KILL_CONNECTION; cache_dump(); /* check_integrity(0); */ /* Can't call it here because of locks */ bins_dump(); diff --git a/sql/sql_class.cc b/sql/sql_class.cc index c9b99b5fd1f..f7d46ca57cd 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -664,7 +664,7 @@ THD::THD() Open_tables_state(refresh_version), rli_fake(0), lock_id(&main_lock_id), in_sub_stmt(0), - sql_log_bin_toplevel(false), + sql_log_bin_toplevel(false), log_all_errors(0), binlog_table_maps(0), binlog_flags(0UL), table_map_for_update(0), arg_of_last_insert_id_function(FALSE), @@ -1096,6 +1096,11 @@ void THD::cleanup(void) lock=locked_tables; locked_tables=0; close_thread_tables(this); } + if (user_connect) + { + decrease_user_connections(user_connect); + user_connect= 0; // Safety + } wt_thd_destroy(&transaction.wt); #if defined(ENABLED_DEBUG_SYNC) @@ -1263,15 +1268,24 @@ void add_diff_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var, #endif -void THD::awake(THD::killed_state state_to_set) +void THD::awake(killed_state state_to_set) { DBUG_ENTER("THD::awake"); DBUG_PRINT("enter", ("this: 0x%lx", (long) this)); THD_CHECK_SENTRY(this); safe_mutex_assert_owner(&LOCK_thd_data); + if (global_system_variables.log_warnings > 3) + { + Security_context *sctx= security_ctx; + sql_print_warning(ER(ER_NEW_ABORTING_CONNECTION), + thread_id,(db ? db : "unconnected"), + sctx->user ? sctx->user : "unauthenticated", + sctx->host_or_ip, + "KILLED"); + } killed= state_to_set; - if (state_to_set != THD::KILL_QUERY) + if (state_to_set >= KILL_CONNECTION) { thr_alarm_kill(thread_id); if (!slave_thread) @@ -1351,6 +1365,38 @@ void THD::awake(THD::killed_state state_to_set) DBUG_VOID_RETURN; } + +/* + Get error number for killed state + Note that the error message can't have any parameters. + See thd::kill_message() +*/ + +int killed_errno(killed_state killed) +{ + switch (killed) { + case NOT_KILLED: + case KILL_HARD_BIT: + return 0; // Probably wrong usage + case KILL_BAD_DATA: + case KILL_BAD_DATA_HARD: + return 0; // Not a real error + case KILL_CONNECTION: + case KILL_CONNECTION_HARD: + case KILL_SYSTEM_THREAD: + case KILL_SYSTEM_THREAD_HARD: + return ER_CONNECTION_KILLED; + case KILL_QUERY: + case KILL_QUERY_HARD: + return ER_QUERY_INTERRUPTED; + case KILL_SERVER: + case KILL_SERVER_HARD: + return ER_SERVER_SHUTDOWN; + } + return 0; // Keep compiler happy +} + + /* Remember the location of thread info, the structure needed for sql_alloc() and the structure for the net buffer @@ -2680,26 +2726,32 @@ bool select_max_min_finder_subselect::cmp_real() { Item *maxmin= ((Item_singlerow_subselect *)item)->element_index(0); double val1= cache->val_real(), val2= maxmin->val_real(); + + /* Ignore NULLs for ANY and keep them for ALL subqueries */ + if (cache->null_value) + return (is_all && !maxmin->null_value) || (!is_all && maxmin->null_value); + if (maxmin->null_value) + return !is_all; + if (fmax) - return (cache->null_value && !maxmin->null_value) || - (!cache->null_value && !maxmin->null_value && - val1 > val2); - return (maxmin->null_value && !cache->null_value) || - (!cache->null_value && !maxmin->null_value && - val1 < val2); + return(val1 > val2); + return (val1 < val2); } bool select_max_min_finder_subselect::cmp_int() { Item *maxmin= ((Item_singlerow_subselect *)item)->element_index(0); longlong val1= cache->val_int(), val2= maxmin->val_int(); + + /* Ignore NULLs for ANY and keep them for ALL subqueries */ + if (cache->null_value) + return (is_all && !maxmin->null_value) || (!is_all && maxmin->null_value); + if (maxmin->null_value) + return !is_all; + if (fmax) - return (cache->null_value && !maxmin->null_value) || - (!cache->null_value && !maxmin->null_value && - val1 > val2); - return (maxmin->null_value && !cache->null_value) || - (!cache->null_value && !maxmin->null_value && - val1 < val2); + return(val1 > val2); + return (val1 < val2); } bool select_max_min_finder_subselect::cmp_decimal() @@ -2707,13 +2759,16 @@ bool select_max_min_finder_subselect::cmp_decimal() Item *maxmin= ((Item_singlerow_subselect *)item)->element_index(0); my_decimal cval, *cvalue= cache->val_decimal(&cval); my_decimal mval, *mvalue= maxmin->val_decimal(&mval); + + /* Ignore NULLs for ANY and keep them for ALL subqueries */ + if (cache->null_value) + return (is_all && !maxmin->null_value) || (!is_all && maxmin->null_value); + if (maxmin->null_value) + return !is_all; + if (fmax) - return (cache->null_value && !maxmin->null_value) || - (!cache->null_value && !maxmin->null_value && - my_decimal_cmp(cvalue, mvalue) > 0) ; - return (maxmin->null_value && !cache->null_value) || - (!cache->null_value && !maxmin->null_value && - my_decimal_cmp(cvalue,mvalue) < 0); + return (my_decimal_cmp(cvalue, mvalue) > 0) ; + return (my_decimal_cmp(cvalue,mvalue) < 0); } bool select_max_min_finder_subselect::cmp_str() @@ -2726,13 +2781,16 @@ bool select_max_min_finder_subselect::cmp_str() */ val1= cache->val_str(&buf1); val2= maxmin->val_str(&buf1); + + /* Ignore NULLs for ANY and keep them for ALL subqueries */ + if (cache->null_value) + return (is_all && !maxmin->null_value) || (!is_all && maxmin->null_value); + if (maxmin->null_value) + return !is_all; + if (fmax) - return (cache->null_value && !maxmin->null_value) || - (!cache->null_value && !maxmin->null_value && - sortcmp(val1, val2, cache->collation.collation) > 0) ; - return (maxmin->null_value && !cache->null_value) || - (!cache->null_value && !maxmin->null_value && - sortcmp(val1, val2, cache->collation.collation) < 0); + return (sortcmp(val1, val2, cache->collation.collation) > 0) ; + return (sortcmp(val1, val2, cache->collation.collation) < 0); } int select_exists_subselect::send_data(List<Item> &items) @@ -3491,11 +3549,15 @@ void THD::restore_backup_open_tables_state(Open_tables_state *backup) @param thd user thread @retval 0 the user thread is active @retval 1 the user thread has been killed + + This is used to signal a storage engine if it should be killed. */ extern "C" int thd_killed(const MYSQL_THD thd) { - return(thd->killed); + if (!(thd->killed & KILL_HARD_BIT)) + return 0; + return thd->killed; } diff --git a/sql/sql_class.h b/sql/sql_class.h index cec37de6a61..278c3d7c0cc 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -362,6 +362,38 @@ public: LEX_COLUMN (const String& x,const uint& y ): column (x),rights (y) {} }; + +/* Note: these states are actually bit coded with HARD */ +enum killed_state +{ + NOT_KILLED= 0, + KILL_HARD_BIT= 1, /* Bit for HARD KILL */ + KILL_BAD_DATA= 2, + KILL_BAD_DATA_HARD= 3, + KILL_QUERY= 4, + KILL_QUERY_HARD= 5, + /* + All of the following killed states will kill the connection + KILL_CONNECTION must be the first of these! + */ + KILL_CONNECTION= 6, + KILL_CONNECTION_HARD= 7, + KILL_SYSTEM_THREAD= 8, + KILL_SYSTEM_THREAD_HARD= 9, + KILL_SERVER= 10, + KILL_SERVER_HARD= 11 +}; + +extern int killed_errno(killed_state killed); +#define killed_mask_hard(killed) ((killed_state) ((killed) & ~KILL_HARD_BIT)) + +enum killed_type +{ + KILL_TYPE_ID, + KILL_TYPE_USER +}; + + #include "sql_lex.h" /* Must be here */ class Delayed_insert; @@ -460,7 +492,7 @@ struct system_variables ulong ndb_index_stat_update_freq; ulong binlog_format; // binlog format for this thd (see enum_binlog_format) ulong progress_report_time; - my_bool binlog_annotate_rows_events; + my_bool binlog_annotate_row_events; my_bool binlog_direct_non_trans_update; /* In slave thread we need to know in behalf of which @@ -1609,19 +1641,12 @@ public: bool sql_log_bin_toplevel; /* True when opt_userstat_running is set at start of query */ bool userstat_running; + /* True if we want to log all errors */ + bool log_all_errors; /* container for handler's private per-connection data */ Ha_data ha_data[MAX_HA]; - /* Place to store various things */ - union - { - /* - Used by subquery optimizations, see Item_in_subselect::emb_on_expr_nest. - */ - TABLE_LIST *emb_on_expr_nest; - } thd_marker; - bool prepare_derived_at_open; /* @@ -1993,14 +2018,6 @@ public: DYNAMIC_ARRAY user_var_events; /* For user variables replication */ MEM_ROOT *user_var_events_alloc; /* Allocate above array elements here */ - enum killed_state - { - NOT_KILLED=0, - KILL_BAD_DATA=1, - KILL_CONNECTION=ER_SERVER_SHUTDOWN, - KILL_QUERY=ER_QUERY_INTERRUPTED, - KILLED_NO_VALUE /* means neither of the states */ - }; killed_state volatile killed; bool check_killed(); @@ -2185,7 +2202,7 @@ public: } void close_active_vio(); #endif - void awake(THD::killed_state state_to_set); + void awake(killed_state state_to_set); /* @@ -2430,18 +2447,13 @@ public: void end_statement(); inline int killed_errno() const { - killed_state killed_val; /* to cache the volatile 'killed' */ - return (killed_val= killed) != KILL_BAD_DATA ? killed_val : 0; + return ::killed_errno(killed); } inline void send_kill_message() const { int err= killed_errno(); if (err) - { - if ((err == KILL_CONNECTION) && !shutdown_in_progress) - err = KILL_QUERY; my_message(err, ER(err), MYF(0)); - } } /* return TRUE if we will abort query if we make a warning now */ inline bool really_abort_on_warning() @@ -3309,9 +3321,11 @@ class select_max_min_finder_subselect :public select_subselect Item_cache *cache; bool (select_max_min_finder_subselect::*op)(); bool fmax; + bool is_all; public: - select_max_min_finder_subselect(Item_subselect *item_arg, bool mx) - :select_subselect(item_arg), cache(0), fmax(mx) + select_max_min_finder_subselect(Item_subselect *item_arg, bool mx, + bool all) + :select_subselect(item_arg), cache(0), fmax(mx), is_all(all) {} void cleanup(); int send_data(List<Item> &items); diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc index debae2dd74d..cd51fd25558 100644 --- a/sql/sql_connect.cc +++ b/sql/sql_connect.cc @@ -49,6 +49,7 @@ int get_or_create_user_conn(THD *thd, const char *user, DBUG_ASSERT(user != 0); DBUG_ASSERT(host != 0); + DBUG_ASSERT(thd->user_connect == 0); user_len= strlen(user); temp_len= (strmov(strmov(temp_user, user)+1, host) - temp_user)+1; @@ -108,15 +109,17 @@ end: int check_for_max_user_connections(THD *thd, USER_CONN *uc) { - int error=0; + int error= 1; DBUG_ENTER("check_for_max_user_connections"); (void) pthread_mutex_lock(&LOCK_user_conn); + + /* Root is not affected by the value of max_user_connections */ if (max_user_connections && !uc->user_resources.user_conn && - max_user_connections < (uint) uc->connections) + max_user_connections < uc->connections && + !(thd->security_ctx->master_access & SUPER_ACL)) { my_error(ER_TOO_MANY_USER_CONNECTIONS, MYF(0), uc->user); - error=1; goto end; } time_out_user_resource_limits(thd, uc); @@ -126,7 +129,6 @@ int check_for_max_user_connections(THD *thd, USER_CONN *uc) my_error(ER_USER_LIMIT_REACHED, MYF(0), uc->user, "max_user_connections", (long) uc->user_resources.user_conn); - error= 1; goto end; } if (uc->user_resources.conn_per_hour && @@ -135,10 +137,10 @@ int check_for_max_user_connections(THD *thd, USER_CONN *uc) my_error(ER_USER_LIMIT_REACHED, MYF(0), uc->user, "max_connections_per_hour", (long) uc->user_resources.conn_per_hour); - error=1; goto end; } uc->conn_per_hour++; + error= 0; end: if (error) @@ -203,7 +205,7 @@ void time_out_user_resource_limits(THD *thd, USER_CONN *uc) /* If more than a hour since last check, reset resource checking */ if (check_time - uc->reset_utime >= LL(3600000000)) { - uc->questions=1; + uc->questions=0; uc->updates=0; uc->conn_per_hour=0; uc->reset_utime= check_time; @@ -232,7 +234,7 @@ bool check_mqh(THD *thd, uint check_command) if (uc->user_resources.questions && uc->questions++ >= uc->user_resources.questions) { - my_error(ER_USER_LIMIT_REACHED, MYF(0), uc->user, "max_questions", + my_error(ER_USER_LIMIT_REACHED, MYF(0), uc->user, "max_queries_per_hour", (long) uc->user_resources.questions); error=1; goto end; @@ -244,7 +246,7 @@ bool check_mqh(THD *thd, uint check_command) (sql_command_flags[check_command] & CF_CHANGES_DATA) && uc->updates++ >= uc->user_resources.updates) { - my_error(ER_USER_LIMIT_REACHED, MYF(0), uc->user, "max_updates", + my_error(ER_USER_LIMIT_REACHED, MYF(0), uc->user, "max_updates_per_hour", (long) uc->user_resources.updates); error=1; goto end; @@ -1028,8 +1030,17 @@ void end_connection(THD *thd) { NET *net= &thd->net; plugin_thdvar_cleanup(thd); + if (thd->user_connect) + { + /* + We decrease this variable early to make it easy to log again quickly. + This code is not critical as we will in any case do this test + again in thd->cleanup() + */ decrease_user_connections(thd->user_connect); + thd->user_connect= 0; + } if (thd->killed || (net->error && net->vio != 0)) { @@ -1087,7 +1098,7 @@ void prepare_new_connection_state(THD* thd) execute_init_command(thd, &sys_init_connect, &LOCK_sys_init_connect); if (thd->is_error()) { - thd->killed= THD::KILL_CONNECTION; + thd->killed= KILL_CONNECTION; sql_print_warning(ER(ER_NEW_ABORTING_CONNECTION), thd->thread_id,(thd->db ? thd->db : "unconnected"), sctx->user ? sctx->user : "unauthenticated", @@ -1123,6 +1134,8 @@ pthread_handler_t handle_one_connection(void *arg) THD *thd= (THD*) arg; thd->thr_create_utime= microsecond_interval_timer(); + /* We need to set this because of time_out_user_resource_limits */ + thd->start_utime= thd->thr_create_utime; if (thread_scheduler.init_new_connection_thread()) { @@ -1173,7 +1186,7 @@ pthread_handler_t handle_one_connection(void *arg) prepare_new_connection_state(thd); while (!net->error && net->vio != 0 && - !(thd->killed == THD::KILL_CONNECTION)) + thd->killed < KILL_CONNECTION) { if (do_command(thd)) break; diff --git a/sql/sql_db.cc b/sql/sql_db.cc index c788d324743..d5579827d1e 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -18,6 +18,7 @@ #include "mysql_priv.h" #include <mysys_err.h> +#include "sp_head.h" #include "sp.h" #include "events.h" #include "sql_handler.h" diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index becc1ada7ae..98d034a4d5c 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -49,7 +49,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, bool triggers_applicable; uint usable_index= MAX_KEY; SELECT_LEX *select_lex= &thd->lex->select_lex; - THD::killed_state killed_status= THD::NOT_KILLED; + killed_state killed_status= NOT_KILLED; DBUG_ENTER("mysql_delete"); bool save_binlog_row_based; @@ -383,7 +383,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, table->file->unlock_row(); // Row failed selection, release lock on it } killed_status= thd->killed; - if (killed_status != THD::NOT_KILLED || thd->is_error()) + if (killed_status != NOT_KILLED || thd->is_error()) error= 1; // Aborted if (will_batch && (loc_error= table->file->end_bulk_delete())) { @@ -450,7 +450,7 @@ cleanup: if (error < 0) thd->clear_error(); else - errcode= query_error_code(thd, killed_status == THD::NOT_KILLED); + errcode= query_error_code(thd, killed_status == NOT_KILLED); /* [binlog]: If 'handler::delete_all_rows()' was called and the @@ -916,7 +916,7 @@ void multi_delete::abort() */ if (mysql_bin_log.is_open()) { - int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED); + int errcode= query_error_code(thd, thd->killed == NOT_KILLED); /* possible error of writing binary log is ignored deliberately */ (void) thd->binlog_query(THD::ROW_QUERY_TYPE, thd->query(), thd->query_length(), @@ -1066,7 +1066,7 @@ int multi_delete::do_table_deletes(TABLE *table, bool ignore) bool multi_delete::send_eof() { - THD::killed_state killed_status= THD::NOT_KILLED; + killed_state killed_status= NOT_KILLED; thd_proc_info(thd, "deleting from reference tables"); /* Does deletes for the last n - 1 tables, returns 0 if ok */ @@ -1074,7 +1074,7 @@ bool multi_delete::send_eof() /* compute a total error to know if something failed */ local_error= local_error || error; - killed_status= (local_error == 0)? THD::NOT_KILLED : thd->killed; + killed_status= (local_error == 0)? NOT_KILLED : thd->killed; /* reset used flags */ thd_proc_info(thd, "end"); @@ -1094,7 +1094,7 @@ bool multi_delete::send_eof() if (local_error == 0) thd->clear_error(); else - errcode= query_error_code(thd, killed_status == THD::NOT_KILLED); + errcode= query_error_code(thd, killed_status == NOT_KILLED); if (thd->binlog_query(THD::ROW_QUERY_TYPE, thd->query(), thd->query_length(), transactional_tables, FALSE, errcode) && diff --git a/sql/sql_error.cc b/sql/sql_error.cc index 835e60cd6ba..1db4a110d01 100644 --- a/sql/sql_error.cc +++ b/sql/sql_error.cc @@ -131,7 +131,7 @@ MYSQL_ERROR *push_warning(THD *thd, MYSQL_ERROR::enum_warning_level level, thd->no_warnings_for_error= 1; thd->spcont= NULL; - thd->killed= THD::KILL_BAD_DATA; + thd->killed= KILL_BAD_DATA; my_message(code, msg, MYF(0)); thd->spcont= spcont; diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 05a30ab49a4..cf199c27518 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -943,7 +943,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, thd->clear_error(); } else - errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED); + errcode= query_error_code(thd, thd->killed == NOT_KILLED); /* bug#22725: @@ -957,7 +957,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, routines did not result in any error due to the KILLED. In such case the flag is ignored for constructing binlog event. */ - DBUG_ASSERT(thd->killed != THD::KILL_BAD_DATA || error > 0); + DBUG_ASSERT(thd->killed != KILL_BAD_DATA || error > 0); if (thd->binlog_query(THD::ROW_QUERY_TYPE, thd->query(), thd->query_length(), transactional_table, FALSE, @@ -2010,7 +2010,7 @@ bool delayed_get_table(THD *thd, TABLE_LIST *table_list) /* Annotating delayed inserts is not supported. */ - di->thd.variables.binlog_annotate_rows_events= 0; + di->thd.variables.binlog_annotate_row_events= 0; di->thd.set_db(table_list->db, (uint) strlen(table_list->db)); di->thd.set_query(my_strdup(table_list->table_name, MYF(MY_WME)), 0); @@ -2362,7 +2362,7 @@ void kill_delayed_threads(void) Delayed_insert *di; while ((di= it++)) { - di->thd.killed= THD::KILL_CONNECTION; + di->thd.killed= KILL_SYSTEM_THREAD; pthread_mutex_lock(&di->thd.LOCK_thd_data); if (di->thd.mysys_var) { @@ -2447,7 +2447,7 @@ static void handle_delayed_insert_impl(THD *thd, Delayed_insert *di) for (;;) { - if (thd->killed == THD::KILL_CONNECTION) + if (thd->killed >= KILL_CONNECTION) { uint lock_count; /* @@ -2495,7 +2495,7 @@ static void handle_delayed_insert_impl(THD *thd, Delayed_insert *di) break; if (error == ETIMEDOUT || error == ETIME) { - thd->killed= THD::KILL_CONNECTION; + thd->killed= KILL_SYSTEM_THREAD; break; } } @@ -2528,7 +2528,7 @@ static void handle_delayed_insert_impl(THD *thd, Delayed_insert *di) { /* Fatal error */ di->dead= 1; - thd->killed= THD::KILL_CONNECTION; + thd->killed= KILL_SYSTEM_THREAD; } pthread_cond_broadcast(&di->cond_client); } @@ -2538,7 +2538,7 @@ static void handle_delayed_insert_impl(THD *thd, Delayed_insert *di) { /* Some fatal error */ di->dead= 1; - thd->killed= THD::KILL_CONNECTION; + thd->killed= KILL_SYSTEM_THREAD; } } di->status=0; @@ -2598,7 +2598,7 @@ pthread_handler_t handle_delayed_insert(void *arg) thd->thread_id= thd->variables.pseudo_thread_id= thread_id++; thd->set_current_time(); threads.append(thd); - thd->killed=abort_loop ? THD::KILL_CONNECTION : THD::NOT_KILLED; + thd->killed=abort_loop ? KILL_SYSTEM_THREAD : NOT_KILLED; pthread_mutex_unlock(&LOCK_thread_count); /* @@ -2632,7 +2632,7 @@ end: di->table=0; di->dead= 1; // If error - thd->killed= THD::KILL_CONNECTION; // If error + thd->killed= KILL_SYSTEM_THREAD; // If error pthread_mutex_unlock(&di->mutex); close_thread_tables(thd); // Free the table @@ -2711,7 +2711,7 @@ bool Delayed_insert::handle_inserts(void) max_rows= delayed_insert_limit; if (thd.killed || table->needs_reopen_or_name_lock()) { - thd.killed= THD::KILL_CONNECTION; + thd.killed= KILL_SYSTEM_THREAD; max_rows= ULONG_MAX; // Do as much as possible } @@ -2824,7 +2824,7 @@ bool Delayed_insert::handle_inserts(void) /* if the delayed insert was killed, the killed status is ignored while binlogging */ int errcode= 0; - if (thd.killed == THD::NOT_KILLED) + if (thd.killed == NOT_KILLED) errcode= query_error_code(&thd, TRUE); /* @@ -3015,6 +3015,7 @@ bool mysql_insert_select_prepare(THD *thd) select_lex->leaf_tables_exec.push_back(table); table->tablenr_exec= table->table->tablenr; table->map_exec= table->table->map; + table->maybe_null_exec= table->table->maybe_null; } if (arena) thd->restore_active_arena(arena, &backup); @@ -3352,7 +3353,7 @@ bool select_insert::send_eof() bool const trans_table= table->file->has_transactions(); ulonglong id; bool changed; - THD::killed_state killed_status= thd->killed; + killed_state killed_status= thd->killed; DBUG_ENTER("select_insert::send_eof"); DBUG_PRINT("enter", ("trans_table=%d, table_type='%s'", trans_table, table->file->table_type())); @@ -3387,7 +3388,7 @@ bool select_insert::send_eof() if (!error) thd->clear_error(); else - errcode= query_error_code(thd, killed_status == THD::NOT_KILLED); + errcode= query_error_code(thd, killed_status == NOT_KILLED); if (write_to_binlog(trans_table, errcode)) { @@ -3461,7 +3462,7 @@ void select_insert::abort() { { if (mysql_bin_log.is_open()) { - int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED); + int errcode= query_error_code(thd, thd->killed == NOT_KILLED); /* error of writing binary log is ignored */ write_to_binlog(transactional_table, errcode); } @@ -3822,7 +3823,7 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u) !table->s->tmp_table && !ptr->get_create_info()->table_existed) { - int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED); + int errcode= query_error_code(thd, thd->killed == NOT_KILLED); if (int error= ptr->binlog_show_create_table(tables, count, errcode)) return error; } @@ -3865,7 +3866,7 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u) create_table->table_name); if (thd->current_stmt_binlog_row_based) { - int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED); + int errcode= query_error_code(thd, thd->killed == NOT_KILLED); binlog_show_create_table(&(create_table->table), 1, errcode); } table= create_table->table; diff --git a/sql/sql_join_cache.cc b/sql/sql_join_cache.cc index f292375ee6f..f53bf738b86 100644 --- a/sql/sql_join_cache.cc +++ b/sql/sql_join_cache.cc @@ -225,8 +225,6 @@ void JOIN_CACHE::calc_record_fields() flag_fields+= test(tab->table->maybe_null); fields+= tab->used_fields; blobs+= tab->used_blobs; - - fields+= tab->check_rowid_field(); } if ((with_match_flag= join_tab->use_match_flag())) flag_fields++; @@ -620,7 +618,12 @@ void JOIN_CACHE::create_remaining_fields() copy->type= CACHE_ROWID; copy->field= 0; copy->referenced_field_no= 0; - length+= copy->length; + /* + Note: this may seem odd, but at this point we have + table->file->ref==NULL while table->file->ref_length is already set + to correct value. + */ + length += table->file->ref_length; data_field_count++; copy++; } @@ -1297,6 +1300,7 @@ uint JOIN_CACHE::write_record_data(uchar * link, bool *is_full) if (with_length) { rec_len_ptr= cp; + DBUG_ASSERT(cp + size_of_rec_len <= buff + buff_size); cp+= size_of_rec_len; } @@ -1306,6 +1310,7 @@ uint JOIN_CACHE::write_record_data(uchar * link, bool *is_full) */ if (prev_cache) { + DBUG_ASSERT(cp + prev_cache->get_size_of_rec_offset() <= buff + buff_size); cp+= prev_cache->get_size_of_rec_offset(); prev_cache->store_rec_ref(cp, link); } @@ -1322,6 +1327,7 @@ uint JOIN_CACHE::write_record_data(uchar * link, bool *is_full) flags_pos= cp; for ( ; copy < copy_end; copy++) { + DBUG_ASSERT(cp + copy->length <= buff + buff_size); memcpy(cp, copy->str, copy->length); cp+= copy->length; } @@ -1348,6 +1354,7 @@ uint JOIN_CACHE::write_record_data(uchar * link, bool *is_full) { last_rec_blob_data_is_in_rec_buff= 1; /* Put down the length of the blob and the pointer to the data */ + DBUG_ASSERT(cp + copy->length + sizeof(char*) <= buff + buff_size); blob_field->get_image(cp, copy->length+sizeof(char*), blob_field->charset()); cp+= copy->length+sizeof(char*); @@ -1357,6 +1364,7 @@ uint JOIN_CACHE::write_record_data(uchar * link, bool *is_full) /* First put down the length of the blob and then copy the data */ blob_field->get_image(cp, copy->length, blob_field->charset()); + DBUG_ASSERT(cp + copy->length + copy->blob_length <= buff + buff_size); memcpy(cp+copy->length, copy->str, copy->blob_length); cp+= copy->length+copy->blob_length; } @@ -1367,12 +1375,14 @@ uint JOIN_CACHE::write_record_data(uchar * link, bool *is_full) case CACHE_VARSTR1: /* Copy the significant part of the short varstring field */ len= (uint) copy->str[0] + 1; + DBUG_ASSERT(cp + len <= buff + buff_size); memcpy(cp, copy->str, len); cp+= len; break; case CACHE_VARSTR2: /* Copy the significant part of the long varstring field */ len= uint2korr(copy->str) + 2; + DBUG_ASSERT(cp + len <= buff + buff_size); memcpy(cp, copy->str, len); cp+= len; break; @@ -1387,6 +1397,7 @@ uint JOIN_CACHE::write_record_data(uchar * link, bool *is_full) end > str && end[-1] == ' '; end--) ; len=(uint) (end-str); + DBUG_ASSERT(cp + len + 2 <= buff + buff_size); int2store(cp, len); memcpy(cp+2, str, len); cp+= len+2; @@ -1402,11 +1413,22 @@ uint JOIN_CACHE::write_record_data(uchar * link, bool *is_full) TABLE *table= (TABLE *) copy->str; copy->str= table->file->ref; copy->length= table->file->ref_length; + if (!copy->str) + { + /* + If table is an empty inner table of an outer join and it is + a materialized derived table then table->file->ref == NULL. + */ + cp+= copy->length; + break; + } } /* fall through */ default: /* Copy the entire image of the field from the record buffer */ - memcpy(cp, copy->str, copy->length); + DBUG_ASSERT(cp + copy->length <= buff + buff_size); + if (copy->str) + memcpy(cp, copy->str, copy->length); cp+= copy->length; } } @@ -1425,6 +1447,7 @@ uint JOIN_CACHE::write_record_data(uchar * link, bool *is_full) cnt++; } } + DBUG_ASSERT(cp + size_of_fld_ofs*cnt <= buff + buff_size); cp+= size_of_fld_ofs*cnt; } @@ -1798,6 +1821,13 @@ uint JOIN_CACHE::read_record_field(CACHE_FIELD *copy, bool blob_in_rec_buff) memset(copy->str+len, ' ', copy->length-len); len+= 2; break; + case CACHE_ROWID: + if (!copy->str) + { + len= copy->length; + break; + } + /* fall through */ default: /* Copy the entire image of the field from the record buffer */ len= copy->length; @@ -2021,6 +2051,7 @@ enum_nested_loop_state JOIN_CACHE::join_records(bool skip_last) JOIN_TAB *tab; enum_nested_loop_state rc= NESTED_LOOP_OK; bool outer_join_first_inner= join_tab->is_first_inner_for_outer_join(); + DBUG_ENTER("JOIN_CACHE::join_records"); if (outer_join_first_inner && !join_tab->first_unmatched) join_tab->not_null_compl= TRUE; @@ -2102,7 +2133,8 @@ enum_nested_loop_state JOIN_CACHE::join_records(bool skip_last) finish: restore_last_record(); reset(TRUE); - return rc; + DBUG_PRINT("exit", ("rc: %d", rc)); + DBUG_RETURN(rc); } @@ -2164,10 +2196,11 @@ enum_nested_loop_state JOIN_CACHE::join_matching_records(bool skip_last) join_tab->table->null_row= 0; bool check_only_first_match= join_tab->check_only_first_match(); bool outer_join_first_inner= join_tab->is_first_inner_for_outer_join(); + DBUG_ENTER("JOIN_CACHE::join_matching_records"); /* Return at once if there are no records in the join buffer */ if (!records) - return NESTED_LOOP_OK; + DBUG_RETURN(NESTED_LOOP_OK); /* When joining we read records from the join buffer back into record buffers. @@ -2241,7 +2274,7 @@ finish: rc= error < 0 ? NESTED_LOOP_NO_MORE_ROWS: NESTED_LOOP_ERROR; finish2: join_tab_scan->close(); - return rc; + DBUG_RETURN(rc); } @@ -2323,6 +2356,7 @@ bool JOIN_CACHE::set_match_flag_if_none(JOIN_TAB *first_inner, enum_nested_loop_state JOIN_CACHE::generate_full_extensions(uchar *rec_ptr) { enum_nested_loop_state rc= NESTED_LOOP_OK; + DBUG_ENTER("JOIN_CACHE::generate_full_extensions"); /* Check whether the extended partial join record meets @@ -2340,16 +2374,18 @@ enum_nested_loop_state JOIN_CACHE::generate_full_extensions(uchar *rec_ptr) if (rc != NESTED_LOOP_OK && rc != NESTED_LOOP_NO_MORE_ROWS) { reset(TRUE); - return rc; + DBUG_RETURN(rc); } } if (res == -1) { rc= NESTED_LOOP_ERROR; - return rc; + DBUG_RETURN(rc); } } - return rc; + else if (join->thd->is_error()) + rc= NESTED_LOOP_ERROR; + DBUG_RETURN(rc); } @@ -2374,16 +2410,20 @@ enum_nested_loop_state JOIN_CACHE::generate_full_extensions(uchar *rec_ptr) RETURN VALUE TRUE there is a match FALSE there is no match + In this case the caller must also check thd->is_error() to see + if there was a fatal error for the query. */ inline bool JOIN_CACHE::check_match(uchar *rec_ptr) { /* Check whether pushdown conditions are satisfied */ + DBUG_ENTER("JOIN_CACHE:check_match"); + if (join_tab->select && join_tab->select->skip_record(join->thd) <= 0) - return FALSE; + DBUG_RETURN(FALSE); if (!join_tab->is_last_inner_table()) - return TRUE; + DBUG_RETURN(TRUE); /* This is the last inner table of an outer join, @@ -2396,7 +2436,7 @@ inline bool JOIN_CACHE::check_match(uchar *rec_ptr) set_match_flag_if_none(first_inner, rec_ptr); if (first_inner->check_only_first_match() && !join_tab->first_inner) - return TRUE; + DBUG_RETURN(TRUE); /* This is the first match for the outer table row. The function set_match_flag_if_none has turned the flag @@ -2410,13 +2450,12 @@ inline bool JOIN_CACHE::check_match(uchar *rec_ptr) for (JOIN_TAB *tab= first_inner; tab <= join_tab; tab++) { if (tab->select && tab->select->skip_record(join->thd) <= 0) - return FALSE; + DBUG_RETURN(FALSE); } } while ((first_inner= first_inner->first_upper) && first_inner->last_inner == join_tab); - - return TRUE; + DBUG_RETURN(TRUE); } @@ -2451,10 +2490,11 @@ enum_nested_loop_state JOIN_CACHE::join_null_complements(bool skip_last) ulonglong cnt; enum_nested_loop_state rc= NESTED_LOOP_OK; bool is_first_inner= join_tab == join_tab->first_unmatched; + DBUG_ENTER("JOIN_CACHE::join_null_complements"); /* Return at once if there are no records in the join buffer */ if (!records) - return NESTED_LOOP_OK; + DBUG_RETURN(NESTED_LOOP_OK); cnt= records - (is_key_access() ? 0 : test(skip_last)); @@ -2484,7 +2524,7 @@ enum_nested_loop_state JOIN_CACHE::join_null_complements(bool skip_last) } finish: - return rc; + DBUG_RETURN(rc); } diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 917502f2b5b..9aee5caeb64 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -21,8 +21,8 @@ #include "item_create.h" #include <m_ctype.h> #include <hash.h> -#include "sp.h" #include "sp_head.h" +#include "sp.h" #include "sql_select.h" /* @@ -1272,39 +1272,39 @@ int MYSQLlex(void *arg, void *yythd) lip->save_in_comment_state(); + if (lip->yyPeekn(2) == 'M' && lip->yyPeekn(3) == '!') + { + /* Skip MariaDB unique marker */ + lip->set_echo(FALSE); + lip->yySkip(); + /* The following if will be true */ + } if (lip->yyPeekn(2) == '!') { lip->in_comment= DISCARD_COMMENT; /* Accept '/' '*' '!', but do not keep this marker. */ lip->set_echo(FALSE); - lip->yySkip(); - lip->yySkip(); - lip->yySkip(); + lip->yySkipn(3); /* The special comment format is very strict: - '/' '*' '!', followed by exactly + '/' '*' '!', followed by an optional 'M' and exactly 1 digit (major), 2 digits (minor), then 2 digits (dot). 32302 -> 3.23.02 50032 -> 5.0.32 50114 -> 5.1.14 */ - char version_str[6]; - version_str[0]= lip->yyPeekn(0); - version_str[1]= lip->yyPeekn(1); - version_str[2]= lip->yyPeekn(2); - version_str[3]= lip->yyPeekn(3); - version_str[4]= lip->yyPeekn(4); - version_str[5]= 0; - if ( my_isdigit(cs, version_str[0]) - && my_isdigit(cs, version_str[1]) - && my_isdigit(cs, version_str[2]) - && my_isdigit(cs, version_str[3]) - && my_isdigit(cs, version_str[4]) + if ( my_isdigit(cs, lip->yyPeekn(0)) + && my_isdigit(cs, lip->yyPeekn(1)) + && my_isdigit(cs, lip->yyPeekn(2)) + && my_isdigit(cs, lip->yyPeekn(3)) + && my_isdigit(cs, lip->yyPeekn(4)) ) { ulong version; - version=strtol(version_str, NULL, 10); + char *end_ptr= (char*) lip->get_ptr()+5; + int error; + version= (ulong) my_strtoll10(lip->get_ptr(), &end_ptr, &error); if (version <= MYSQL_VERSION_ID) { @@ -3221,18 +3221,19 @@ void st_select_lex::append_table_to_list(TABLE_LIST *TABLE_LIST::*link, tl->*link= table; } + /* @brief - Remove given table from the leaf_tables list. + Replace given table from the leaf_tables list for a list of tables - @param link Offset to which list in table structure to use - @param table Table to remove + @param table Table to replace + @param list List to substititute the table for @details - Remove 'table' from the leaf_tables list using the 'link' offset. + Replace 'table' from the leaf_tables list for a list of tables 'tbl_list'. */ -void st_select_lex::remove_table_from_list(TABLE_LIST *table) +void st_select_lex::replace_leaf_table(TABLE_LIST *table, List<TABLE_LIST> &tbl_list) { TABLE_LIST *tl; List_iterator<TABLE_LIST> ti(leaf_tables); @@ -3240,7 +3241,7 @@ void st_select_lex::remove_table_from_list(TABLE_LIST *table) { if (tl == table) { - ti.remove(); + ti.replace(tbl_list); break; } } @@ -3345,8 +3346,6 @@ bool SELECT_LEX::merge_subquery(THD *thd, TABLE_LIST *derived, uint table_no, table_map map) { derived->wrap_into_nested_join(subq_select->top_join_list); - /* Reconnect the next_leaf chain. */ - leaf_tables.concat(&subq_select->leaf_tables); ftfunc_list->concat(subq_select->ftfunc_list); if (join || @@ -3362,18 +3361,14 @@ bool SELECT_LEX::merge_subquery(THD *thd, TABLE_LIST *derived, in_subq->emb_on_expr_nest= derived; } } - /* - Remove merged table from chain. - When merge_subquery is called at a subquery-to-semijoin transformation - the derived isn't in the leaf_tables list, so in this case the call of - remove_table_from_list does not cause any actions. - */ - remove_table_from_list(derived); /* Walk through child's tables and adjust table map, tablenr, * parent_lex */ subq_select->remap_tables(derived, map, table_no, this); subq_select->merged_into= this; + + replace_leaf_table(derived, subq_select->leaf_tables); + return FALSE; } @@ -3414,10 +3409,33 @@ void SELECT_LEX::update_used_tables() { TABLE_LIST *tl; List_iterator<TABLE_LIST> ti(leaf_tables); + + while ((tl= ti++)) + { + if (tl->table && !tl->is_view_or_derived()) + { + TABLE_LIST *embedding= tl->embedding; + for (embedding= tl->embedding; embedding; embedding=embedding->embedding) + { + if (embedding->is_view_or_derived()) + { + DBUG_ASSERT(embedding->is_merged_derived()); + TABLE *tab= tl->table; + tab->covering_keys= tab->s->keys_for_keyread; + tab->covering_keys.intersect(tab->keys_in_use_for_query); + tab->merge_keys.clear_all(); + bitmap_clear_all(tab->read_set); + bitmap_clear_all(tab->vcol_set); + break; + } + } + } + } + + ti.rewind(); while ((tl= ti++)) { - TABLE_LIST *embedding; - embedding= tl; + TABLE_LIST *embedding= tl; do { bool maybe_null; @@ -3446,6 +3464,7 @@ void SELECT_LEX::update_used_tables() embedding= tl->embedding; } } + if (join->conds) { join->conds->update_used_tables(); @@ -3597,6 +3616,10 @@ bool st_select_lex::save_leaf_tables(THD *thd) return 1; table->tablenr_exec= table->table->tablenr; table->map_exec= table->table->map; + if (join && (join->select_options & SELECT_DESCRIBE)) + table->maybe_null_exec= 0; + else + table->maybe_null_exec= table->table->maybe_null; } if (arena) thd->restore_active_arena(arena, &backup); @@ -3632,6 +3655,47 @@ bool st_select_lex::save_prep_leaf_tables(THD *thd) } +/* + Return true if this select_lex has been converted into a semi-join nest + within 'ancestor'. + + We need a loop to check this because there could be several nested + subselects, like + + SELECT ... FROM grand_parent + WHERE expr1 IN (SELECT ... FROM parent + WHERE expr2 IN ( SELECT ... FROM child) + + which were converted into: + + SELECT ... + FROM grand_parent SEMI_JOIN (parent JOIN child) + WHERE + expr1 AND expr2 + + In this case, both parent and child selects were merged into the parent. +*/ + +bool st_select_lex::is_merged_child_of(st_select_lex *ancestor) +{ + bool all_merged= TRUE; + for (SELECT_LEX *sl= this; sl && sl!=ancestor; + sl=sl->outer_select()) + { + Item *subs= sl->master_unit()->item; + if (subs && subs->type() == Item::SUBSELECT_ITEM && + ((Item_subselect*)subs)->substype() == Item_subselect::IN_SUBS && + ((Item_in_subselect*)subs)->in_strategy & SUBS_SEMI_JOIN) + { + continue; + } + all_merged= FALSE; + break; + } + return all_merged; +} + + int st_select_lex::print_explain(select_result_sink *output) { int res; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index b5131ded136..dd867399d06 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -898,7 +898,7 @@ public: bool handle_derived(struct st_lex *lex, uint phases); void append_table_to_list(TABLE_LIST *TABLE_LIST::*link, TABLE_LIST *table); bool get_free_table_map(table_map *map, uint *tablenr); - void remove_table_from_list(TABLE_LIST *table); + void replace_leaf_table(TABLE_LIST *table, List<TABLE_LIST> &tbl_list); void remap_tables(TABLE_LIST *derived, table_map map, uint tablenr, st_select_lex *parent_lex); bool merge_subquery(THD *thd, TABLE_LIST *derived, st_select_lex *subq_lex, @@ -918,8 +918,8 @@ public: bool save_leaf_tables(THD *thd); bool save_prep_leaf_tables(THD *thd); + bool is_merged_child_of(st_select_lex *ancestor); int print_explain(select_result_sink *output); - private: /* current index hint kind. used in filling up index_hints */ enum index_hint_type current_index_hint_type; @@ -1747,6 +1747,9 @@ typedef struct st_lex : public Query_tables_list LEX_SERVER_OPTIONS server_options; USER_RESOURCES mqh; ulong type; + /* The following is used by KILL */ + killed_state kill_signal; + killed_type kill_type; /* This variable is used in post-parse stage to declare that sum-functions, or functions which have sense only if GROUP BY is present, are allowed. diff --git a/sql/sql_list.h b/sql/sql_list.h index 50673921aeb..4655b4e3577 100644 --- a/sql/sql_list.h +++ b/sql/sql_list.h @@ -310,6 +310,26 @@ public: friend class error_list; friend class error_list_iterator; + /* + Debugging help: return N-th element in the list, or NULL if the list has + less than N elements. + */ + inline void *nth_element(int n) + { + list_node *node= first; + void *data= NULL; + for (int i=0; i <= n; i++) + { + if (node == &end_of_list) + { + data= NULL; + break; + } + data= node->info; + node= node->next; + } + return data; + } #ifdef LIST_EXTRA_DEBUG /* Check list invariants and print results into trace. Invariants are: @@ -488,6 +508,7 @@ public: } empty(); } + inline T *nth_element(int n) { return (T*)base_list::nth_element(n); } }; diff --git a/sql/sql_load.cc b/sql/sql_load.cc index cc0527591e1..3df6305f620 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -130,7 +130,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, bool is_fifo=0; #ifndef EMBEDDED_LIBRARY LOAD_FILE_INFO lf_info; - THD::killed_state killed_status; + killed_state killed_status; #endif char *db = table_list->db; // This is never null /* @@ -471,11 +471,11 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, DBUG_EXECUTE_IF("simulate_kill_bug27571", { error=1; - thd->killed= THD::KILL_QUERY; + thd->killed= KILL_QUERY; };); #ifndef EMBEDDED_LIBRARY - killed_status= (error == 0) ? THD::NOT_KILLED : thd->killed; + killed_status= (error == 0) ? NOT_KILLED : thd->killed; #endif /* @@ -519,7 +519,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, /* If the file was not empty, wrote_create_file is true */ if (lf_info.wrote_create_file) { - int errcode= query_error_code(thd, killed_status == THD::NOT_KILLED); + int errcode= query_error_code(thd, killed_status == NOT_KILLED); /* since there is already an error, the possible error of writing binary log will be ignored */ @@ -570,7 +570,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, read_info.end_io_cache(); if (lf_info.wrote_create_file) { - int errcode= query_error_code(thd, killed_status == THD::NOT_KILLED); + int errcode= query_error_code(thd, killed_status == NOT_KILLED); error= write_execute_load_query_log_event(thd, ex, table_list->db, table_list->table_name, handle_duplicates, ignore, diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 8e36da5f285..81eedf493a2 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -793,7 +793,17 @@ int end_trans(THD *thd, enum enum_mysql_completiontype completion) if (res < 0) my_error(thd->killed_errno(), MYF(0)); else if ((res == 0) && do_release) - thd->killed= THD::KILL_CONNECTION; + { + thd->killed= KILL_CONNECTION; + if (global_system_variables.log_warnings > 3) + { + Security_context *sctx= &thd->main_security_ctx; + sql_print_warning(ER(ER_NEW_ABORTING_CONNECTION), + thd->thread_id,(thd->db ? thd->db : "unconnected"), + sctx->user ? sctx->user : "unauthenticated", + sctx->host_or_ip, "RELEASE"); + } + } DBUG_RETURN(res); } @@ -1133,12 +1143,15 @@ bool dispatch_command(enum enum_server_command command, THD *thd, /* Ensure we don't free security_ctx->user in case we have to revert */ thd->security_ctx->user= 0; + thd->user_connect= 0; if (acl_authenticate(thd, 0, packet_length)) { /* Free user if allocated by acl_authenticate */ x_free(thd->security_ctx->user); *thd->security_ctx= save_security_ctx; + if (thd->user_connect) + decrease_user_connections(thd->user_connect); thd->user_connect= save_user_connect; thd->reset_db(save_db, save_db_length); thd->variables.character_set_client= save_character_set_client; @@ -1518,7 +1531,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, { status_var_increment(thd->status_var.com_stat[SQLCOM_KILL]); ulong id=(ulong) uint4korr(packet); - sql_kill(thd,id,false); + sql_kill(thd,id, KILL_CONNECTION_HARD); break; } case COM_SET_OPTION: @@ -1560,15 +1573,18 @@ bool dispatch_command(enum enum_server_command command, THD *thd, } /* report error issued during command execution */ - if (thd->killed_errno()) - { - if (! thd->main_da.is_set()) - thd->send_kill_message(); - } - if (thd->killed == THD::KILL_QUERY || thd->killed == THD::KILL_BAD_DATA) + if (thd->killed) { - thd->killed= THD::NOT_KILLED; - thd->mysys_var->abort= 0; + if (thd->killed_errno()) + { + if (! thd->main_da.is_set()) + thd->send_kill_message(); + } + if (thd->killed < KILL_CONNECTION) + { + thd->killed= NOT_KILLED; + thd->mysys_var->abort= 0; + } } /* If commit fails, we should be able to reset the OK status. */ @@ -1612,6 +1628,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd, thd_proc_info(thd, 0); thd->packet.shrink(thd->variables.net_buffer_length); // Reclaim some memory free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC)); + + /* Check that some variables are reset properly */ + DBUG_ASSERT(thd->abort_on_warning == 0); DBUG_RETURN(error); } @@ -4049,8 +4068,6 @@ end_with_restore_list: } case SQLCOM_KILL: { - Item *it= (Item *)lex->value_list.head(); - if (lex->table_or_sp_used()) { my_error(ER_NOT_SUPPORTED_YET, MYF(0), "Usage of subqueries or stored " @@ -4058,13 +4075,20 @@ end_with_restore_list: break; } - if ((!it->fixed && it->fix_fields(lex->thd, &it)) || it->check_cols(1)) + if (lex->kill_type == KILL_TYPE_ID) { - my_message(ER_SET_CONSTANTS_ONLY, ER(ER_SET_CONSTANTS_ONLY), - MYF(0)); - goto error; + Item *it= (Item *)lex->value_list.head(); + if ((!it->fixed && it->fix_fields(lex->thd, &it)) || it->check_cols(1)) + { + my_message(ER_SET_CONSTANTS_ONLY, ER(ER_SET_CONSTANTS_ONLY), + MYF(0)); + goto error; + } + sql_kill(thd, (ulong) it->val_int(), lex->kill_signal); } - sql_kill(thd, (ulong)it->val_int(), lex->type & ONLY_KILL_QUERY); + else + sql_kill_user(thd, get_current_user(thd, lex->users_list.head()), + lex->kill_signal); break; } #ifndef NO_EMBEDDED_ACCESS_CHECKS @@ -4518,9 +4542,10 @@ create_sp_error: */ /* Conditionally writes to binlog */ - int type= lex->sql_command == SQLCOM_ALTER_PROCEDURE ? - TYPE_ENUM_PROCEDURE : - TYPE_ENUM_FUNCTION; + stored_procedure_type type; + type= (lex->sql_command == SQLCOM_ALTER_PROCEDURE ? + TYPE_ENUM_PROCEDURE : + TYPE_ENUM_FUNCTION); sp_result= sp_update_routine(thd, type, @@ -4548,8 +4573,8 @@ create_sp_error: case SQLCOM_DROP_FUNCTION: { int sp_result; - int type= (lex->sql_command == SQLCOM_DROP_PROCEDURE ? - TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION); + stored_procedure_type type= (lex->sql_command == SQLCOM_DROP_PROCEDURE ? + TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION); sp_result= sp_routine_exists_in_table(thd, type, lex->spname); mysql_reset_errors(thd, 0); @@ -4576,9 +4601,10 @@ create_sp_error: #endif /* Conditionally writes to binlog */ - int type= lex->sql_command == SQLCOM_DROP_PROCEDURE ? - TYPE_ENUM_PROCEDURE : - TYPE_ENUM_FUNCTION; + stored_procedure_type type; + type= (lex->sql_command == SQLCOM_DROP_PROCEDURE ? + TYPE_ENUM_PROCEDURE : + TYPE_ENUM_FUNCTION); sp_result= sp_drop_routine(thd, type, lex->spname); } @@ -5076,7 +5102,6 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables) param->select_limit= new Item_int((ulonglong) thd->variables.select_limit); } - thd->thd_marker.emb_on_expr_nest= NULL; if (!(res= open_and_lock_tables(thd, all_tables))) { if (lex->describe) @@ -5850,7 +5875,6 @@ void mysql_reset_thd_for_next_command(THD *thd, my_bool calculate_userstat) thd->query_plan_flags= QPLAN_INIT; thd->query_plan_fsort_passes= 0; - thd->thd_marker.emb_on_expr_nest= NULL; /* Because we come here only for start of top-statements, binlog format is @@ -7187,12 +7211,13 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, This is written such that we have a short lock on LOCK_thread_count */ -uint kill_one_thread(THD *thd, ulong id, bool only_kill_query) +uint kill_one_thread(THD *thd, ulong id, killed_state kill_signal) { THD *tmp; uint error=ER_NO_SUCH_THREAD; DBUG_ENTER("kill_one_thread"); - DBUG_PRINT("enter", ("id=%lu only_kill=%d", id, only_kill_query)); + DBUG_PRINT("enter", ("id: %lu signal: %u", id, (uint) kill_signal)); + VOID(pthread_mutex_lock(&LOCK_thread_count)); // For unlink from list I_List_iterator<THD> it(threads); while ((tmp=it++)) @@ -7223,12 +7248,16 @@ uint kill_one_thread(THD *thd, ulong id, bool only_kill_query) If user of both killer and killee are non-NULL, proceed with slayage if both are string-equal. + + It's ok to also kill DELAYED threads with KILL_CONNECTION instead of + KILL_SYSTEM_THREAD; The difference is that KILL_CONNECTION may be + faster and do a harder kill than KILL_SYSTEM_THREAD; */ if ((thd->security_ctx->master_access & SUPER_ACL) || thd->security_ctx->user_matches(tmp->security_ctx)) { - tmp->awake(only_kill_query ? THD::KILL_QUERY : THD::KILL_CONNECTION); + tmp->awake(kill_signal); error=0; } else @@ -7240,6 +7269,76 @@ uint kill_one_thread(THD *thd, ulong id, bool only_kill_query) } +/** + kill all threads from one user + + @param thd Thread class + @param user_name User name for threads we should kill + @param only_kill_query Should it kill the query or the connection + + @note + This is written such that we have a short lock on LOCK_thread_count + + If we can't kill all threads because of security issues, no threads + are killed. +*/ + +static uint kill_threads_for_user(THD *thd, LEX_USER *user, + killed_state kill_signal, ha_rows *rows) +{ + THD *tmp; + List<THD> threads_to_kill; + DBUG_ENTER("kill_threads_for_user"); + + *rows= 0; + + if (thd->is_fatal_error) // If we run out of memory + DBUG_RETURN(ER_OUT_OF_RESOURCES); + + DBUG_PRINT("enter", ("user: %s signal: %u", user->user.str, + (uint) kill_signal)); + + VOID(pthread_mutex_lock(&LOCK_thread_count)); // For unlink from list + I_List_iterator<THD> it(threads); + while ((tmp=it++)) + { + if (tmp->command == COM_DAEMON) + continue; + /* + Check that hostname (if given) and user name matches. + + host.str[0] == '%' means that host name was not given. See sql_yacc.yy + */ + if (((user->host.str[0] == '%' && !user->host.str[1]) || + !strcmp(tmp->security_ctx->host, user->host.str)) && + !strcmp(tmp->security_ctx->user, user->user.str)) + { + if (!(thd->security_ctx->master_access & SUPER_ACL) && + !thd->security_ctx->user_matches(tmp->security_ctx)) + { + VOID(pthread_mutex_unlock(&LOCK_thread_count)); + DBUG_RETURN(ER_KILL_DENIED_ERROR); + } + if (!threads_to_kill.push_back(tmp, tmp->mem_root)) + pthread_mutex_lock(&tmp->LOCK_thd_data); // Lock from delete + } + } + VOID(pthread_mutex_unlock(&LOCK_thread_count)); + if (!threads_to_kill.is_empty()) + { + List_iterator_fast<THD> it(threads_to_kill); + THD *ptr; + while ((ptr= it++)) + { + ptr->awake(kill_signal); + pthread_mutex_unlock(&ptr->LOCK_thd_data); + (*rows)++; + } + } + DBUG_RETURN(0); +} + + /* kills a thread and sends response @@ -7250,16 +7349,33 @@ uint kill_one_thread(THD *thd, ulong id, bool only_kill_query) only_kill_query Should it kill the query or the connection */ -void sql_kill(THD *thd, ulong id, bool only_kill_query) +void sql_kill(THD *thd, ulong id, killed_state state) { uint error; - if (!(error= kill_one_thread(thd, id, only_kill_query))) + if (!(error= kill_one_thread(thd, id, state))) my_ok(thd); else my_error(error, MYF(0), id); } +void sql_kill_user(THD *thd, LEX_USER *user, killed_state state) +{ + uint error; + ha_rows rows; + if (!(error= kill_threads_for_user(thd, user, state, &rows))) + my_ok(thd, rows); + else + { + /* + This is probably ER_OUT_OF_RESOURCES, but in the future we may + want to write the name of the user we tried to kill + */ + my_error(error, MYF(0), user->host.str, user->user.str); + } +} + + /** If pointer is not a null pointer, append filename to it. */ bool append_file_to_dir(THD *thd, const char **filename_ptr, diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index 58eddf42f26..8e6f065c1c4 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -3384,6 +3384,19 @@ static int construct_options(MEM_ROOT *mem_root, struct st_plugin_int *tmp, opt->name, plugin_name); } } + /* + PLUGIN_VAR_STR command-line options without PLUGIN_VAR_MEMALLOC, point + directly to values in the argv[] array. For plugins started at the + server startup, argv[] array is allocated with load_defaults(), and + freed when the server is shut down. But for plugins loaded with + INSTALL PLUGIN, the memory allocated with load_defaults() is freed with + freed() at the end of mysql_install_plugin(). Which means we cannot + allow any pointers into that area. + Thus, for all plugins loaded after the server was started, + we force all command-line options to be PLUGIN_VAR_MEMALLOC + */ + if (mysqld_server_started && !(opt->flags & PLUGIN_VAR_NOCMDOPT)) + opt->flags|= PLUGIN_VAR_MEMALLOC; break; case PLUGIN_VAR_ENUM: if (!opt->check) @@ -3540,6 +3553,10 @@ static int test_plugin_options(MEM_ROOT *tmp_root, struct st_plugin_int *tmp, if (!my_strcasecmp(&my_charset_latin1, tmp->name.str, "ndbcluster")) plugin_load_policy= PLUGIN_OFF; #endif +#ifdef WITH_FEEDBACK_PLUGIN + if (!my_strcasecmp(&my_charset_latin1, tmp->name.str, "feedback")) + plugin_load_policy= PLUGIN_OFF; +#endif for (opt= tmp->plugin->system_vars; opt && *opt; opt++) count+= 2; /* --{plugin}-{optname} and --plugin-{plugin}-{optname} */ diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 465a026ad6e..850365b5403 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1413,7 +1413,6 @@ static int mysql_test_select(Prepared_statement *stmt, goto error; thd->used_tables= 0; // Updated by setup_fields - thd->thd_marker.emb_on_expr_nest= 0; /* JOIN::prepare calls diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index fc472c17e57..e7363bffa9a 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -1277,9 +1277,9 @@ err: idle, then this could last long, and if the slave reconnects, we could have 2 Binlog_dump threads in SHOW PROCESSLIST, until a query is written to the binlog. To avoid this, when the slave reconnects and sends COM_BINLOG_DUMP, - the master kills any existing thread with the slave's server id (if this id is - not zero; it will be true for real slaves, but false for mysqlbinlog when it - sends COM_BINLOG_DUMP to get a remote binlog dump). + the master kills any existing thread with the slave's server id (if this id + is not zero; it will be true for real slaves, but false for mysqlbinlog when + it sends COM_BINLOG_DUMP to get a remote binlog dump). SYNOPSIS kill_zombie_dump_threads() @@ -1311,7 +1311,7 @@ void kill_zombie_dump_threads(uint32 slave_server_id) it will be slow because it will iterate through the list again. We just to do kill the thread ourselves. */ - tmp->awake(THD::KILL_QUERY); + tmp->awake(KILL_QUERY); pthread_mutex_unlock(&tmp->LOCK_thd_data); } } @@ -1978,10 +1978,10 @@ static sys_var_const sys_log_slave_updates(&vars, "log_slave_updates", OPT_GLOBAL, SHOW_MY_BOOL, (uchar*) &opt_log_slave_updates); static sys_var_const -sys_replicate_annotate_rows_events(&vars, - "replicate_annotate_rows_events", +sys_replicate_annotate_row_events(&vars, + "replicate_annotate_row_events", OPT_GLOBAL, SHOW_MY_BOOL, - (uchar*) &opt_replicate_annotate_rows_events); + (uchar*) &opt_replicate_annotate_row_events); static sys_var_const sys_relay_log(&vars, "relay_log", OPT_GLOBAL, SHOW_CHAR_PTR, (uchar*) &opt_relay_logname); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index ee06ce0c8a6..4dc99cf006c 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -175,14 +175,14 @@ int join_read_always_key_or_null(JOIN_TAB *tab); int join_read_next_same_or_null(READ_RECORD *info); static COND *make_cond_for_table(THD *thd, Item *cond,table_map table, table_map used_table, - uint join_tab_idx_arg, + int join_tab_idx_arg, bool exclude_expensive_cond, bool retain_ref_cond); static COND *make_cond_for_table_from_pred(THD *thd, Item *root_cond, Item *cond, table_map tables, table_map used_table, - uint join_tab_idx_arg, + int join_tab_idx_arg, bool exclude_expensive_cond, bool retain_ref_cond); @@ -950,9 +950,6 @@ JOIN::optimize_inner() /* dump_TABLE_LIST_graph(select_lex, select_lex->leaf_tables); */ select_lex->update_used_tables(); - /* Save this info for the next executions */ - if (select_lex->save_leaf_tables(thd)) - DBUG_RETURN(1); } eval_select_list_used_tables(); @@ -1012,17 +1009,22 @@ JOIN::optimize_inner() /* Convert all outer joins to inner joins if possible */ conds= simplify_joins(this, join_list, conds, TRUE, FALSE); + if (select_lex->save_leaf_tables(thd)) + DBUG_RETURN(1); build_bitmap_for_nested_joins(join_list, 0); sel->prep_where= conds ? conds->copy_andor_structure(thd) : 0; + sel->where= conds; + if (arena) thd->restore_active_arena(arena, &backup); } inject_jtbm_conds(this, join_list, &conds); - conds= optimize_cond(this, conds, join_list, &cond_value, &cond_equal); + conds= optimize_cond(this, conds, join_list, &cond_value, &cond_equal); + if (thd->is_error()) { error= 1; @@ -1039,10 +1041,17 @@ JOIN::optimize_inner() DBUG_RETURN(1); } if (select_lex->where) + { select_lex->cond_value= cond_value; + if (sel->where != conds && cond_value == Item::COND_OK) + thd->change_item_tree(&sel->where, conds); + } if (select_lex->having) + { select_lex->having_value= having_value; - + if (sel->having != having && having_value == Item::COND_OK) + thd->change_item_tree(&sel->having, having); + } if (cond_value == Item::COND_FALSE || having_value == Item::COND_FALSE || (!unit->select_limit_cnt && !(select_options & OPTION_FOUND_ROWS))) { /* Impossible cond */ @@ -1130,7 +1139,7 @@ JOIN::optimize_inner() if (conds && !(thd->lex->describe & DESCRIBE_EXTENDED)) { COND *table_independent_conds= - make_cond_for_table(thd, conds, PSEUDO_TABLE_BITS, 0, MAX_TABLES, + make_cond_for_table(thd, conds, PSEUDO_TABLE_BITS, 0, -1, FALSE, FALSE); DBUG_EXECUTE("where", print_where(table_independent_conds, @@ -2445,7 +2454,6 @@ void JOIN::exec_inner() thd_proc_info(thd, "Copying to group table"); DBUG_PRINT("info", ("%s", thd->proc_info)); - tmp_error= -1; if (curr_join != this) { if (sum_funcs2) @@ -2470,6 +2478,7 @@ void JOIN::exec_inner() JOIN_TAB *first_tab= curr_join->join_tab + curr_join->const_tables; first_tab->sorted= test(first_tab->loosescan_match_tab); } + tmp_error= -1; if (setup_sum_funcs(curr_join->thd, curr_join->sum_funcs) || (tmp_error= do_select(curr_join, (List<Item> *) 0, curr_tmp_table, 0))) @@ -2596,7 +2605,7 @@ void JOIN::exec_inner() Item* sort_table_cond= make_cond_for_table(thd, curr_join->tmp_having, used_tables, - (table_map)0, MAX_TABLES, + (table_map)0, -1, FALSE, FALSE); if (sort_table_cond) { @@ -2634,7 +2643,7 @@ void JOIN::exec_inner() QT_ORDINARY);); curr_join->tmp_having= make_cond_for_table(thd, curr_join->tmp_having, ~ (table_map) 0, - ~used_tables, MAX_TABLES, + ~used_tables, -1, FALSE, FALSE); DBUG_EXECUTE("where",print_where(curr_join->tmp_having, "having after sort", @@ -3053,6 +3062,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list, uint i,table_count,const_count,key; table_map found_const_table_map, all_table_map, found_ref, refs; key_map const_ref, eq_part; + bool has_expensive_keyparts; TABLE **table_vector; JOIN_TAB *stat,*stat_end,*s,**stat_ref; KEYUSE *keyuse,*start_keyuse; @@ -3374,12 +3384,17 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list, refs=0; const_ref.clear_all(); eq_part.clear_all(); + has_expensive_keyparts= false; do { if (keyuse->val->type() != Item::NULL_ITEM && !keyuse->optimize) { if (!((~found_const_table_map) & keyuse->used_tables)) + { const_ref.set_bit(keyuse->keypart); + if (keyuse->val->is_expensive()) + has_expensive_keyparts= true; + } else refs|=keyuse->used_tables; eq_part.set_bit(keyuse->keypart); @@ -3401,6 +3416,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list, if (table->key_info[key].flags & HA_NOSAME) { if (const_ref == eq_part && + !has_expensive_keyparts && !((outer_join & table->map) && (*s->on_expr_ref)->is_expensive())) { // Found everything for ref. @@ -3881,7 +3897,9 @@ add_key_field(JOIN *join, uint optimize= 0; if (eq_func && ((join->is_allowed_hash_join_access() && - field->hash_join_is_possible()) || + field->hash_join_is_possible() && + !(field->table->pos_in_table_list->is_materialized_derived() && + field->table->created)) || (field->table->pos_in_table_list->is_materialized_derived() && !field->table->created))) { @@ -5231,7 +5249,7 @@ best_access_path(JOIN *join, tmp= table->file->keyread_time(key, 1, (ha_rows) tmp); else tmp= table->file->read_time(key, 1, - (ha_rows) min(tmp,s->worst_seeks)-1); + (ha_rows) min(tmp,s->worst_seeks)); tmp*= record_count; } } @@ -5395,13 +5413,14 @@ best_access_path(JOIN *join, tmp= table->file->keyread_time(key, 1, (ha_rows) tmp); else tmp= table->file->read_time(key, 1, - (ha_rows) min(tmp,s->worst_seeks)-1); + (ha_rows) min(tmp,s->worst_seeks)); tmp*= record_count; } else tmp= best_time; // Do nothing } + DBUG_ASSERT(tmp > 0 || record_count == 0); tmp += s->startup_cost; loose_scan_opt.check_ref_access_part2(key, start_key, records, tmp); } /* not ft_key */ @@ -6101,7 +6120,7 @@ greedy_search(JOIN *join, read_time_arg and record_count_arg contain the computed cost and fanout */ -void JOIN::get_partial_cost_and_fanout(uint end_tab_idx, +void JOIN::get_partial_cost_and_fanout(int end_tab_idx, table_map filter_map, double *read_time_arg, double *record_count_arg) @@ -6111,14 +6130,14 @@ void JOIN::get_partial_cost_and_fanout(uint end_tab_idx, double sj_inner_fanout= 1.0; JOIN_TAB *end_tab= NULL; JOIN_TAB *tab; - uint i; - uint last_sj_table= MAX_TABLES; + int i; + int last_sj_table= MAX_TABLES; /* Handle a special case where the join is degenerate, and produces no records */ - if (table_count == 0) + if (table_count == const_tables) { *read_time_arg= 0.0; /* @@ -6128,6 +6147,7 @@ void JOIN::get_partial_cost_and_fanout(uint end_tab_idx, calculations. */ *record_count_arg=1.0; + return; } for (tab= first_depth_first_tab(this), i= const_tables; @@ -6140,19 +6160,17 @@ void JOIN::get_partial_cost_and_fanout(uint end_tab_idx, } for (tab= first_depth_first_tab(this), i= const_tables; - (i <= end_tab_idx && tab); + ; tab= next_depth_first_tab(this, tab), i++) { - /* - We've entered the SJM nest that contains the end_tab. The caller is - actually - - interested in fanout inside the nest (because that's how many times - we'll invoke the attached WHERE conditions) - - not interested in cost - */ if (end_tab->bush_root_tab && end_tab->bush_root_tab == tab) { - /* Ok, end_tab is inside SJM nest and we're entering that nest now */ + /* + We've entered the SJM nest that contains the end_tab. The caller is + - interested in fanout inside the nest (because that's how many times + we'll invoke the attached WHERE conditions) + - not interested in cost + */ record_count= 1.0; read_time= 0.0; } @@ -6166,8 +6184,18 @@ void JOIN::get_partial_cost_and_fanout(uint end_tab_idx, sj_inner_fanout= 1.0; last_sj_table= i + tab->n_sj_tables; } - - if (tab->records_read && (tab->table->map & filter_map)) + + table_map cur_table_map; + if (tab->table) + cur_table_map= tab->table->map; + else + { + /* This is a SJ-Materialization nest. Check all of its tables */ + TABLE *first_child= tab->bush_children->start->table; + TABLE_LIST *sjm_nest= first_child->pos_in_table_list->embedding; + cur_table_map= sjm_nest->nested_join->used_tables; + } + if (tab->records_read && (cur_table_map & filter_map)) { record_count *= tab->records_read; read_time += tab->read_time; @@ -6181,6 +6209,9 @@ void JOIN::get_partial_cost_and_fanout(uint end_tab_idx, sj_inner_fanout= 1.0; last_sj_table= MAX_TABLES; } + + if (tab == end_tab) + break; } *read_time_arg= read_time;// + record_count / TIME_FOR_COMPARE; *record_count_arg= record_count; @@ -6613,6 +6644,16 @@ void JOIN_TAB::calc_used_field_length(bool max_fl) rec_length+=(table->s->null_fields+7)/8; if (table->maybe_null) rec_length+=sizeof(my_bool); + + /* Take into account that DuplicateElimination may need to store rowid */ + uint rowid_add_size= 0; + if (keep_current_rowid) + { + rowid_add_size= table->file->ref_length; + rec_length += rowid_add_size; + fields++; + } + if (max_fl) { // TODO: to improve this estimate for max expected length @@ -6626,13 +6667,9 @@ void JOIN_TAB::calc_used_field_length(bool max_fl) } max_used_fieldlength= rec_length; } - else if (table->file->stats.mean_rec_length) - set_if_smaller(rec_length, table->file->stats.mean_rec_length); + else if (table->file->stats.mean_rec_length) + set_if_smaller(rec_length, table->file->stats.mean_rec_length + rowid_add_size); - /* - TODO: why we don't count here rowid that we might need to store when - using DuplicateElimination? - */ used_fields=fields; used_fieldlength=rec_length; used_blobs=blobs; @@ -6669,7 +6706,7 @@ int JOIN_TAB::make_scan_filter() if (cond && (tmp= make_cond_for_table(join->thd, cond, join->const_table_map | table->map, - table->map, MAX_TABLES, FALSE, TRUE))) + table->map, -1, FALSE, TRUE))) { DBUG_EXECUTE("where",print_where(tmp,"cache", QT_ORDINARY);); if (!(cache_select= @@ -7141,6 +7178,7 @@ get_best_combination(JOIN *join) goto loop_end; // Handled in make_join_stat.. j->loosescan_match_tab= NULL; //non-nulls will be set later + j->inside_loosescan_range= FALSE; j->ref.key = -1; j->ref.key_parts=0; @@ -7274,7 +7312,8 @@ static bool are_tables_local(JOIN_TAB *jtab, table_map used_tables) except the const tables. */ table_map local_tables= jtab->emb_sj_nest->nested_join->used_tables | - jtab->join->const_table_map; + jtab->join->const_table_map | + OUTER_REF_TABLE_BIT; return !test(used_tables & ~local_tables); } @@ -7391,7 +7430,8 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, while (((~used_tables) & keyuse->used_tables) || (keyuse->keypart != (is_hash_join_key_no(key) ? - keyinfo->key_part[i].field->field_index : i))) + keyinfo->key_part[i].field->field_index : i)) || + !are_tables_local(j, keyuse->val->used_tables())) keyuse++; /* Skip other parts */ uint maybe_null= test(keyinfo->key_part[i].null_bit); @@ -7955,7 +7995,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) join->exec_const_cond= make_cond_for_table(thd, cond, join->const_table_map, - (table_map) 0, MAX_TABLES, FALSE, FALSE); + (table_map) 0, -1, FALSE, FALSE); /* Add conditions added by add_not_null_conds(). */ for (uint i= 0 ; i < join->const_tables ; i++) add_cond_and_fix(thd, &join->exec_const_cond, @@ -7974,7 +8014,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) COND *outer_ref_cond= make_cond_for_table(thd, cond, OUTER_REF_TABLE_BIT, OUTER_REF_TABLE_BIT, - MAX_TABLES, FALSE, FALSE); + -1, FALSE, FALSE); if (outer_ref_cond) { add_cond_and_fix(thd, &outer_ref_cond, join->outer_ref_cond); @@ -8002,7 +8042,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) */ JOIN_TAB *first_inner_tab= tab->first_inner; - if (tab->table) + if (!tab->bush_children) current_map= tab->table->map; else current_map= tab->bush_children->start->emb_sj_nest->sj_inner_tables; @@ -8144,7 +8184,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) { COND *push_cond= make_cond_for_table(thd, tmp, current_map, current_map, - MAX_TABLES, FALSE, FALSE); + -1, FALSE, FALSE); if (push_cond) { /* Push condition to handler */ @@ -8316,7 +8356,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) JOIN_TAB *cond_tab= join_tab->first_inner; COND *tmp= make_cond_for_table(thd, *join_tab->on_expr_ref, join->const_table_map, - (table_map) 0, MAX_TABLES, FALSE, FALSE); + (table_map) 0, -1, FALSE, FALSE); if (!tmp) continue; tmp= new Item_func_trig_cond(tmp, &cond_tab->not_null_compl); @@ -8362,10 +8402,10 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) current_map= tab->table->map; used_tables2|= current_map; /* - psergey: have put the MAX_TABLES below. It's bad, will need to fix it. + psergey: have put the -1 below. It's bad, will need to fix it. */ COND *tmp_cond= make_cond_for_table(thd, on_expr, used_tables2, - current_map, /*(tab - first_tab)*/ MAX_TABLES, + current_map, /*(tab - first_tab)*/ -1, FALSE, FALSE); if (tab == first_inner_tab && tab->on_precond) add_cond_and_fix(thd, &tmp_cond, tab->on_precond); @@ -8446,17 +8486,17 @@ bool generate_derived_keys_for_table(KEYUSE *keyuse, uint count, uint keys) TABLE *table= keyuse->table; if (table->alloc_keys(keys)) return TRUE; - uint keyno= 0; + uint key_count= 0; KEYUSE *first_keyuse= keyuse; uint prev_part= keyuse->keypart; uint parts= 0; uint i= 0; - for ( ; i < count && keyno < keys; ) + for ( ; i < count && key_count < keys; ) { do { - keyuse->key= keyno; + keyuse->key= table->s->keys; keyuse->keypart_map= (key_part_map) (1 << parts); keyuse++; i++; @@ -8470,14 +8510,14 @@ bool generate_derived_keys_for_table(KEYUSE *keyuse, uint count, uint keys) } else { - if (table->add_tmp_key(keyno, parts, + if (table->add_tmp_key(table->s->keys, parts, get_next_field_for_derived_key, (uchar *) &first_keyuse, FALSE)) return TRUE; - table->reginfo.join_tab->keys.set_bit(keyno); + table->reginfo.join_tab->keys.set_bit(table->s->keys); first_keyuse= keyuse; - keyno++; + key_count++; parts= 0; prev_part= keyuse->keypart; } @@ -8504,12 +8544,23 @@ bool generate_derived_keys(DYNAMIC_ARRAY *keyuse_array) TABLE_LIST *derived= NULL; if (keyuse->table != prev_table) derived= keyuse->table->pos_in_table_list; - while (derived && derived->is_materialized_derived() && - keyuse->key == MAX_KEY) + while (derived && derived->is_materialized_derived()) { if (keyuse->table != prev_table) { prev_table= keyuse->table; + while (keyuse->table == prev_table && keyuse->key != MAX_KEY) + { + keyuse++; + i++; + } + if (keyuse->table != prev_table) + { + keyuse--; + i--; + derived= NULL; + continue; + } first_table_keyuse= keyuse; last_used_tables= keyuse->used_tables; count= 0; @@ -8522,11 +8573,13 @@ bool generate_derived_keys(DYNAMIC_ARRAY *keyuse_array) } count++; keyuse++; + i++; if (keyuse->table != prev_table) { if (generate_derived_keys_for_table(first_table_keyuse, count, ++keys)) return TRUE; keyuse--; + i--; derived= NULL; } } @@ -8557,12 +8610,13 @@ void JOIN::drop_unused_derived_keys() TABLE *table=tab->table; if (!table) continue; - if (!table->pos_in_table_list->is_materialized_derived() || - table->max_keys <= 1) + if (!table->pos_in_table_list->is_materialized_derived()) continue; - table->use_index(tab->ref.key); - if (table->s->keys) + if (table->max_keys > 1) + table->use_index(tab->ref.key); + if (table->s->keys && tab->ref.key >= 0) tab->ref.key= 0; + tab->keys= (key_map) (table->s->keys ? 1 : 0); } } @@ -8691,7 +8745,7 @@ void set_join_cache_denial(JOIN_TAB *join_tab) void rr_unlock_row(st_join_table *tab) { READ_RECORD *info= &tab->read_record; - info->file->unlock_row(); + info->table->file->unlock_row(); } @@ -9025,6 +9079,15 @@ uint check_join_cache_usage(JOIN_TAB *tab, if (tab->use_quick == 2) goto no_join_cache; + + /* + Don't use join cache if we're inside a join tab range covered by LooseScan + strategy (TODO: LooseScan is very similar to FirstMatch so theoretically it + should be possible to use join buffering in the same way we're using it for + multi-table firstmatch ranges). + */ + if (tab->inside_loosescan_range) + goto no_join_cache; if (tab->is_inner_table_of_semi_join_with_first_match() && !join->allowed_semijoin_with_cache) @@ -9106,6 +9169,11 @@ uint check_join_cache_usage(JOIN_TAB *tab, case JT_EQ_REF: if (cache_level <=2 || (no_hashed_cache && no_bka_cache)) goto no_join_cache; + for (uint i= 0; i < tab->ref.key_parts; i++) + { + if (tab->ref.cond_guards[i]) + goto no_join_cache; + } if (!tab->is_ref_for_hash_join()) { flags= HA_MRR_NO_NULL_ENDPOINTS | HA_MRR_SINGLE_POINT; @@ -9351,7 +9419,6 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after) TABLE *table=tab->table; uint jcl= tab->used_join_cache_level; tab->read_record.table= table; - tab->read_record.file=table->file; tab->read_record.unlock_row= rr_unlock_row; tab->sorted= sorted; sorted= 0; // only first must be sorted @@ -9633,7 +9700,6 @@ void JOIN_TAB::cleanup() table->pos_in_table_list->jtbm_subselect) { end_read_record(&read_record); - //psergey-merge: table->pos_in_table_list->jtbm_subselect->cleanup(); DBUG_VOID_RETURN; } @@ -11687,7 +11753,7 @@ propagate_cond_constants(THD *thd, I_List<COND_CMP> *save_list, bool left_const= args[0]->const_item() && !args[0]->is_expensive(); bool right_const= args[1]->const_item() && !args[1]->is_expensive(); if (!(left_const && right_const) && - args[0]->result_type() == args[1]->result_type()) + args[0]->cmp_type() == args[1]->cmp_type()) { if (right_const) { @@ -11998,6 +12064,18 @@ simplify_joins(JOIN *join, List<TABLE_LIST> *join_list, COND *conds, bool top, leave it intact (otherwise it is flattened) */ join->select_lex->sj_nests.push_back(table); + + /* + Also, walk through semi-join children and mark those that are now + top-level + */ + TABLE_LIST *tbl; + List_iterator<TABLE_LIST> it(nested_join->join_list); + while ((tbl= it++)) + { + if (!tbl->on_expr && tbl->table) + tbl->table->maybe_null= FALSE; + } } else if (nested_join && !table->on_expr) { @@ -13757,6 +13835,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields, bool maybe_null=(*cur_group->item)->maybe_null; key_part_info->null_bit=0; key_part_info->field= field; + key_part_info->fieldnr= field->field_index + 1; if (cur_group == group) field->key_start.set_bit(0); key_part_info->offset= field->offset(table->record[0]); @@ -13880,6 +13959,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields, key_part_info->field->init(table); key_part_info->key_type=FIELDFLAG_BINARY; key_part_info->type= HA_KEYTYPE_BINARY; + key_part_info->fieldnr= key_part_info->field->field_index + 1; key_part_info++; } /* Create a distinct key over the columns we are going to return */ @@ -13897,6 +13977,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields, key_part_info->offset= (*reg_field)->offset(table->record[0]); key_part_info->length= (uint16) (*reg_field)->pack_length(); + key_part_info->fieldnr= (*reg_field)->field_index + 1; /* TODO: The below method of computing the key format length of the key part is a copy/paste from opt_range.cc, and table.cc. @@ -14160,6 +14241,13 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo, keyinfo->key_parts > table->file->max_key_parts() || share->uniques) { + if (!share->uniques && !(keyinfo->flags & HA_NOSAME)) + { + my_error(ER_INTERNAL_ERROR, MYF(0), + "Using too big key for internal temp tables"); + DBUG_RETURN(1); + } + /* Can't create a key; Make a unique constraint instead of a key */ share->keys= 0; share->uniques= 1; @@ -14178,9 +14266,9 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo, } else { - /* Create an unique key */ + /* Create a key */ bzero((char*) &keydef,sizeof(keydef)); - keydef.flag=HA_NOSAME; + keydef.flag= keyinfo->flags & HA_NOSAME; keydef.keysegs= keyinfo->key_parts; keydef.seg= seg; } @@ -14356,7 +14444,8 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo, { /* Create an unique key */ bzero((char*) &keydef,sizeof(keydef)); - keydef.flag=HA_NOSAME | HA_BINARY_PACK_KEY | HA_PACK_KEY; + keydef.flag= ((keyinfo->flags & HA_NOSAME) | HA_BINARY_PACK_KEY | + HA_PACK_KEY); keydef.keysegs= keyinfo->key_parts; keydef.seg= seg; } @@ -14877,10 +14966,12 @@ sub_select_cache(JOIN *join, JOIN_TAB *join_tab, bool end_of_records) { enum_nested_loop_state rc; JOIN_CACHE *cache= join_tab->cache; - DBUG_ENTER("sub_select_cache"); - /* This function cannot be called if join_tab has no associated join buffer */ + /* + This function cannot be called if join_tab has no associated join + buffer + */ DBUG_ASSERT(cache != NULL); join_tab->cache->reset_join(join); @@ -15697,7 +15788,7 @@ int join_read_key2(THD *thd, JOIN_TAB *tab, TABLE *table, TABLE_REF *table_ref) */ if (tab && tab->ref.has_record && tab->ref.use_count == 0) { - tab->read_record.file->unlock_row(); + tab->read_record.table->file->unlock_row(); table_ref->has_record= FALSE; } error=table->file->ha_index_read_map(table->record[0], @@ -15883,7 +15974,7 @@ join_init_quick_read_record(JOIN_TAB *tab) int init_read_record_seq(JOIN_TAB *tab) { tab->read_record.read_record= rr_sequential; - if (tab->read_record.file->ha_rnd_init_with_error(1)) + if (tab->read_record.table->file->ha_rnd_init_with_error(1)) return 1; return (*tab->read_record.read_record)(&tab->read_record); } @@ -15950,7 +16041,6 @@ join_read_first(JOIN_TAB *tab) tab->table->status=0; tab->read_record.read_record=join_read_next; tab->read_record.table=table; - tab->read_record.file=table->file; tab->read_record.index=tab->index; tab->read_record.record=table->record[0]; if (!table->file->inited) @@ -15971,7 +16061,7 @@ static int join_read_next(READ_RECORD *info) { int error; - if ((error= info->file->ha_index_next(info->record))) + if ((error= info->table->file->ha_index_next(info->record))) return report_error(info->table, error); return 0; @@ -15989,7 +16079,6 @@ join_read_last(JOIN_TAB *tab) tab->table->status=0; tab->read_record.read_record=join_read_prev; tab->read_record.table=table; - tab->read_record.file=table->file; tab->read_record.index=tab->index; tab->read_record.record=table->record[0]; if (!table->file->inited) @@ -16007,7 +16096,7 @@ static int join_read_prev(READ_RECORD *info) { int error; - if ((error= info->file->ha_index_prev(info->record))) + if ((error= info->table->file->ha_index_prev(info->record))) return report_error(info->table, error); return 0; } @@ -16041,7 +16130,7 @@ static int join_ft_read_next(READ_RECORD *info) { int error; - if ((error= info->file->ha_ft_read(info->table->record[0]))) + if ((error= info->table->file->ha_ft_read(info->table->record[0]))) return report_error(info->table, error); return 0; } @@ -16721,7 +16810,7 @@ bool test_if_ref(Item *root_cond, Item_field *left_item,Item *right_item) static Item * make_cond_for_table(THD *thd, Item *cond, table_map tables, table_map used_table, - uint join_tab_idx_arg, + int join_tab_idx_arg, bool exclude_expensive_cond __attribute__((unused)), bool retain_ref_cond) { @@ -16735,7 +16824,7 @@ make_cond_for_table(THD *thd, Item *cond, table_map tables, static Item * make_cond_for_table_from_pred(THD *thd, Item *root_cond, Item *cond, table_map tables, table_map used_table, - uint join_tab_idx_arg, + int join_tab_idx_arg, bool exclude_expensive_cond __attribute__ ((unused)), bool retain_ref_cond) @@ -17971,12 +18060,6 @@ skipped_filesort: delete save_quick; save_quick= NULL; } - /* - orig_cond is a part of pre_idx_push_cond, - no need to restore it. - */ - orig_cond= 0; - orig_cond_saved= false; if (orig_cond_saved && !changed_key) tab->set_cond(orig_cond); DBUG_RETURN(1); @@ -19917,7 +20000,6 @@ static bool add_ref_to_table_cond(THD *thd, JOIN_TAB *join_tab) } if (thd->is_fatal_error) DBUG_RETURN(TRUE); - if (!cond->fixed) { Item *tmp_item= (Item*) cond; @@ -19926,11 +20008,20 @@ static bool add_ref_to_table_cond(THD *thd, JOIN_TAB *join_tab) } if (join_tab->select) { + Item *cond_copy; + UNINIT_VAR(cond_copy); // used when pre_idx_push_select_cond!=NULL + if (join_tab->select->pre_idx_push_select_cond) + cond_copy= cond->copy_andor_structure(thd); if (join_tab->select->cond) error=(int) cond->add(join_tab->select->cond); join_tab->select->cond= cond; if (join_tab->select->pre_idx_push_select_cond) - join_tab->select->pre_idx_push_select_cond= cond; + { + Item *new_cond= and_conds(cond_copy, join_tab->select->pre_idx_push_select_cond); + if (!new_cond->fixed && new_cond->fix_fields(thd, &new_cond)) + error= 1; + join_tab->select->pre_idx_push_select_cond= new_cond; + } join_tab->set_select_cond(cond, __LINE__); } else if ((join_tab->select= make_select(join_tab->table, 0, 0, cond, 0, @@ -21622,6 +21713,28 @@ void JOIN::save_query_plan(Join_plan_state *save_to) memcpy((uchar*) save_to->best_positions, (uchar*) best_positions, sizeof(POSITION) * (table_count + 1)); memset(best_positions, 0, sizeof(POSITION) * (table_count + 1)); + + /* Save SJM nests */ + List_iterator<TABLE_LIST> it(select_lex->sj_nests); + TABLE_LIST *tlist; + SJ_MATERIALIZATION_INFO **p_info= save_to->sj_mat_info; + while ((tlist= it++)) + { + *(p_info++)= tlist->sj_mat_info; + } +} + + +/** + Reset a query execution plan so that it can be reoptimized in-place. +*/ +void JOIN::reset_query_plan() +{ + for (uint i= 0; i < table_count; i++) + { + join_tab[i].keyuse= NULL; + join_tab[i].checked_keys.clear_all(); + } } @@ -21649,6 +21762,14 @@ void JOIN::restore_query_plan(Join_plan_state *restore_from) } memcpy((uchar*) best_positions, (uchar*) restore_from->best_positions, sizeof(POSITION) * (table_count + 1)); + /* Restore SJM nests */ + List_iterator<TABLE_LIST> it(select_lex->sj_nests); + TABLE_LIST *tlist; + SJ_MATERIALIZATION_INFO **p_info= restore_from->sj_mat_info; + while ((tlist= it++)) + { + tlist->sj_mat_info= *(p_info++); + } } @@ -21658,7 +21779,8 @@ void JOIN::restore_query_plan(Join_plan_state *restore_from) @param added_where An extra conjunct to the WHERE clause to reoptimize with @param join_tables The set of tables to reoptimize - @param save_to If != NULL, save here the state of the current query plan + @param save_to If != NULL, save here the state of the current query plan, + otherwise reuse the existing query plan structures. @notes Given a query plan that was already optimized taking into account some WHERE @@ -21702,6 +21824,8 @@ JOIN::reoptimize(Item *added_where, table_map join_tables, if (save_to) save_query_plan(save_to); + else + reset_query_plan(); if (!keyuse.buffer && my_init_dynamic_array(&keyuse, sizeof(KEYUSE), 20, 64)) @@ -21735,6 +21859,9 @@ JOIN::reoptimize(Item *added_where, table_map join_tables, return REOPT_ERROR; optimize_keyuse(this, &keyuse); + if (optimize_semijoin_nests(this, join_tables)) + return REOPT_ERROR; + /* Re-run the join optimizer to compute a new query plan. */ if (choose_plan(this, join_tables)) return REOPT_ERROR; diff --git a/sql/sql_select.h b/sql/sql_select.h index d6379f3ea92..78decaae039 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -288,7 +288,6 @@ typedef struct st_join_table { ulong max_used_fieldlength; uint used_blobs; uint used_null_fields; - uint used_rowid_fields; uint used_uneven_bit_fields; enum join_type type; bool cached_eq_ref_table,eq_ref_table,not_used_in_distinct; @@ -347,6 +346,9 @@ typedef struct st_join_table { NULL - Not doing a loose scan on this join tab. */ struct st_join_table *loosescan_match_tab; + + /* TRUE <=> we are inside LooseScan range */ + bool inside_loosescan_range; /* Buffer to save index tuple to be able to skip duplicates */ uchar *loosescan_buf; @@ -384,15 +386,6 @@ typedef struct st_join_table { (select->quick->get_type() == QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX)); } - bool check_rowid_field() - { - if (keep_current_rowid && !used_rowid_fields) - { - used_rowid_fields= 1; - used_fieldlength+= table->file->ref_length; - } - return test(used_rowid_fields); - } bool is_inner_table_of_semi_join_with_first_match() { return first_sj_inner_tab != NULL; @@ -673,6 +666,7 @@ protected: KEYUSE *join_tab_keyuse[MAX_TABLES]; /* Copies of JOIN_TAB::checked_keys for each JOIN_TAB. */ key_map join_tab_checked_keys[MAX_TABLES]; + SJ_MATERIALIZATION_INFO *sj_mat_info[MAX_TABLES]; public: Join_plan_state() { @@ -698,6 +692,7 @@ protected: enum_reopt_result reoptimize(Item *added_where, table_map join_tables, Join_plan_state *save_to); void save_query_plan(Join_plan_state *save_to); + void reset_query_plan(); void restore_query_plan(Join_plan_state *restore_from); /* Choose a subquery plan for a table-less subquery. */ bool choose_tableless_subquery_plan(); @@ -1155,7 +1150,7 @@ public: max_allowed_join_cache_level > JOIN_CACHE_HASHED_BIT; } bool choose_subquery_plan(table_map join_tables); - void get_partial_cost_and_fanout(uint end_tab_idx, + void get_partial_cost_and_fanout(int end_tab_idx, table_map filter_map, double *read_time_arg, double *record_count_arg); diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 770e4610fd8..b35e91f7138 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -22,8 +22,8 @@ #include "create_options.h" #include "sql_show.h" #include "repl_failsafe.h" -#include "sp.h" #include "sp_head.h" +#include "sp.h" #include "sql_trigger.h" #include "authors.h" #include "contributors.h" @@ -1274,7 +1274,7 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, handler *file= table->file; TABLE_SHARE *share= table->s; HA_CREATE_INFO create_info; - bool show_table_options= FALSE; + bool show_table_options __attribute__ ((unused))= FALSE; bool foreign_db_mode= (thd->variables.sql_mode & (MODE_POSTGRESQL | MODE_ORACLE | MODE_MSSQL | @@ -1507,7 +1507,9 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, packet->append(STRING_WITH_LEN("\n)")); if (!(thd->variables.sql_mode & MODE_NO_TABLE_OPTIONS) && !foreign_db_mode) { +#ifdef WITH_PARTITION_STORAGE_ENGINE show_table_options= TRUE; +#endif /* Get possible table space definitions and append them to the CREATE TABLE statement @@ -1943,7 +1945,8 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose) pthread_mutex_lock(&tmp->LOCK_thd_data); if ((mysys_var= tmp->mysys_var)) pthread_mutex_lock(&mysys_var->mutex); - thd_info->proc_info= (char*) (tmp->killed == THD::KILL_CONNECTION? "Killed" : 0); + thd_info->proc_info= (char*) (tmp->killed >= KILL_QUERY ? + "Killed" : 0); #ifndef EMBEDDED_LIBRARY thd_info->state_info= (char*) (tmp->net.reading_or_writing ? (tmp->net.reading_or_writing == 2 ? @@ -2173,7 +2176,8 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond) if ((mysys_var= tmp->mysys_var)) pthread_mutex_lock(&mysys_var->mutex); /* COMMAND */ - if ((val= (char *) (tmp->killed == THD::KILL_CONNECTION? "Killed" : 0))) + if ((val= (char *) ((tmp->killed >= KILL_QUERY ? + "Killed" : 0)))) table->field[4]->store(val, strlen(val), cs); else table->field[4]->store(command_name[tmp->command].str, @@ -2528,7 +2532,7 @@ static bool show_status_array(THD *thd, const char *wild, end= strmov(buff, *(my_bool*) value ? "ON" : "OFF"); break; case SHOW_INT: - end= int10_to_str((long) *(uint32*) value, buff, 10); + end= int10_to_str((long) *(int*) value, buff, -10); break; case SHOW_HAVE: { @@ -6834,11 +6838,11 @@ bool get_schema_tables_result(JOIN *join, { result= 1; join->error= 1; - tab->read_record.file= table_list->table->file; + tab->read_record.table->file= table_list->table->file; table_list->schema_table_state= executed_place; break; } - tab->read_record.file= table_list->table->file; + tab->read_record.table->file= table_list->table->file; table_list->schema_table_state= executed_place; } } diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 299e88107d7..e6ec80bbd8d 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -59,7 +59,7 @@ static void wait_for_kill_signal(THD *thd) while (thd->killed == 0) sleep(1); // Reset signal and continue as if nothing happend - thd->killed= THD::NOT_KILLED; + thd->killed= NOT_KILLED; } #endif diff --git a/sql/sql_union.cc b/sql/sql_union.cc index d0ec95efc27..50de42d153c 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -371,6 +371,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, ulonglong create_options; uint save_tablenr= 0; table_map save_map= 0; + uint save_maybe_null= 0; while ((type= tp++)) { @@ -429,6 +430,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, { save_tablenr= result_table_list.tablenr_exec; save_map= result_table_list.map_exec; + save_maybe_null= result_table_list.maybe_null_exec; } bzero((char*) &result_table_list, sizeof(result_table_list)); result_table_list.db= (char*) ""; @@ -438,6 +440,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, { result_table_list.tablenr_exec= save_tablenr; result_table_list.map_exec= save_map; + result_table_list.maybe_null_exec= save_maybe_null; } thd_arg->lex->current_select= lex_select_save; @@ -642,6 +645,7 @@ bool st_select_lex_unit::exec() if (!saved_error) { examined_rows+= thd->examined_row_count; + thd->examined_row_count= 0; if (union_result->flush()) { thd->lex->current_select= lex_select_save; diff --git a/sql/sql_update.cc b/sql/sql_update.cc index b516065eff7..80eb823c346 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -221,7 +221,7 @@ int mysql_update(THD *thd, bool need_reopen; ulonglong id; List<Item> all_fields; - THD::killed_state killed_status= THD::NOT_KILLED; + killed_state killed_status= NOT_KILLED; DBUG_ENTER("mysql_update"); for ( ; ; ) @@ -788,9 +788,9 @@ int mysql_update(THD *thd, // simulated killing after the loop must be ineffective for binlogging DBUG_EXECUTE_IF("simulate_kill_bug27571", { - thd->killed= THD::KILL_QUERY; + thd->killed= KILL_QUERY; };); - error= (killed_status == THD::NOT_KILLED)? error : 1; + error= (killed_status == NOT_KILLED)? error : 1; if (error && will_batch && @@ -851,7 +851,7 @@ int mysql_update(THD *thd, if (error < 0) thd->clear_error(); else - errcode= query_error_code(thd, killed_status == THD::NOT_KILLED); + errcode= query_error_code(thd, killed_status == NOT_KILLED); if (thd->binlog_query(THD::ROW_QUERY_TYPE, thd->query(), thd->query_length(), @@ -1937,7 +1937,7 @@ void multi_update::abort() got caught and if happens later the killed error is written into repl event. */ - int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED); + int errcode= query_error_code(thd, thd->killed == NOT_KILLED); /* the error of binary logging is ignored */ (void)thd->binlog_query(THD::ROW_QUERY_TYPE, thd->query(), thd->query_length(), @@ -2151,7 +2151,7 @@ bool multi_update::send_eof() { char buff[STRING_BUFFER_USUAL_SIZE]; ulonglong id; - THD::killed_state killed_status= THD::NOT_KILLED; + killed_state killed_status= NOT_KILLED; DBUG_ENTER("multi_update::send_eof"); thd_proc_info(thd, "updating reference tables"); @@ -2164,7 +2164,7 @@ bool multi_update::send_eof() if local_error is not set ON until after do_updates() then later carried out killing should not affect binlogging. */ - killed_status= (local_error == 0)? THD::NOT_KILLED : thd->killed; + killed_status= (local_error == 0) ? NOT_KILLED : thd->killed; thd_proc_info(thd, "end"); /* We must invalidate the query cache before binlog writing and @@ -2193,7 +2193,7 @@ bool multi_update::send_eof() if (local_error == 0) thd->clear_error(); else - errcode= query_error_code(thd, killed_status == THD::NOT_KILLED); + errcode= query_error_code(thd, killed_status == NOT_KILLED); if (thd->binlog_query(THD::ROW_QUERY_TYPE, thd->query(), thd->query_length(), transactional_tables, FALSE, errcode)) diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 535cc30e3e8..9d71a61f411 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -18,8 +18,8 @@ #include "mysql_priv.h" #include "sql_select.h" #include "parse_file.h" -#include "sp.h" #include "sp_head.h" +#include "sp.h" #include "sp_cache.h" #define MD5_BUFF_LENGTH 33 diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 879c502a25f..8a72c983bbd 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -682,10 +682,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %pure_parser /* We have threads */ /* - Currently there are 171 shift/reduce conflicts. + Currently there are 174 shift/reduce conflicts. We should not introduce new conflicts any more. */ -%expect 171 +%expect 174 /* Comments for TOKENS. @@ -901,6 +901,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token GROUP_CONCAT_SYM %token GT_SYM /* OPERATOR */ %token HANDLER_SYM +%token HARD_SYM %token HASH_SYM %token HAVING /* SQL-2003-R */ %token HELP_SYM @@ -1169,6 +1170,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token SMALLINT /* SQL-2003-R */ %token SNAPSHOT_SYM %token SOCKET_SYM +%token SOFT_SYM %token SONAME_SYM %token SOUNDS_SYM %token SOURCE_SYM @@ -1348,7 +1350,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); opt_ev_status opt_ev_on_completion ev_on_completion opt_ev_comment ev_alter_on_schedule_completion opt_ev_rename_to opt_ev_sql_stmt optional_flush_tables_arguments opt_dyncol_type dyncol_type - opt_time_precision + opt_time_precision kill_type kill_option int_num %type <ulong_num> ulong_num real_ulong_num merge_insert_types @@ -1380,7 +1382,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); function_call_keyword function_call_nonkeyword function_call_generic - function_call_conflict + function_call_conflict kill_expr %type <item_num> NUM_literal @@ -9674,6 +9676,12 @@ delete_limit_clause: } ; +int_num: + NUM { int error; $$= (int) my_strtoll10($1.str, (char**) 0, &error); } + | '-' NUM { int error; $$= -(int) my_strtoll10($2.str, (char**) 0, &error); } + | '-' LONG_NUM { int error; $$= -(int) my_strtoll10($2.str, (char**) 0, &error); } + ; + ulong_num: NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } | HEX_NUM { $$= (ulong) strtol($1.str, (char**) 0, 16); } @@ -11087,19 +11095,41 @@ purge_option: /* kill threads */ kill: - KILL_SYM kill_option expr + KILL_SYM { LEX *lex=Lex; lex->value_list.empty(); - lex->value_list.push_front($3); + lex->users_list.empty(); lex->sql_command= SQLCOM_KILL; } + kill_type kill_option kill_expr + { + Lex->kill_signal= (killed_state) ($3 | $4); + } ; +kill_type: + /* Empty */ { $$= (int) KILL_HARD_BIT; } + | HARD_SYM { $$= (int) KILL_HARD_BIT; } + | SOFT_SYM { $$= 0; } + kill_option: - /* empty */ { Lex->type= 0; } - | CONNECTION_SYM { Lex->type= 0; } - | QUERY_SYM { Lex->type= ONLY_KILL_QUERY; } + /* empty */ { $$= (int) KILL_CONNECTION; } + | CONNECTION_SYM { $$= (int) KILL_CONNECTION; } + | QUERY_SYM { $$= (int) KILL_QUERY; } + ; + +kill_expr: + expr + { + Lex->value_list.push_front($$); + Lex->kill_type= KILL_TYPE_ID; + } + | USER user + { + Lex->users_list.push_back($2); + Lex->kill_type= KILL_TYPE_USER; + } ; /* change database */ @@ -12214,6 +12244,7 @@ keyword_sp: | GRANTS {} | GLOBAL_SYM {} | HASH_SYM {} + | HARD_SYM {} | HOSTS_SYM {} | HOUR_SYM {} | IDENTIFIED_SYM {} @@ -12345,6 +12376,7 @@ keyword_sp: | SHUTDOWN {} | SLOW_SYM {} | SNAPSHOT_SYM {} + | SOFT_SYM {} | SOUNDS_SYM {} | SOURCE_SYM {} | SQL_CACHE_SYM {} @@ -13414,7 +13446,7 @@ grant_option: lex->mqh.conn_per_hour= $2; lex->mqh.specified_limits|= USER_RESOURCES::CONNECTIONS_PER_HOUR; } - | MAX_USER_CONNECTIONS_SYM ulong_num + | MAX_USER_CONNECTIONS_SYM int_num { LEX *lex=Lex; lex->mqh.user_conn= $2; diff --git a/sql/structs.h b/sql/structs.h index 29ccde3fb03..61354cbd01c 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -149,7 +149,7 @@ struct READ_RECORD { /* Parameter to read_record */ typedef int (*Read_func)(READ_RECORD*); typedef void (*Unlock_row_func)(st_join_table *); struct st_table *table; /* Head-form */ - handler *file; + // handler *file_; struct st_table **forms; /* head and ref forms */ Read_func read_record; @@ -222,8 +222,11 @@ typedef struct user_resources { uint updates; /* Maximum number of connections established per hour. */ uint conn_per_hour; - /* Maximum number of concurrent connections. */ - uint user_conn; + /* + Maximum number of concurrent connections. If -1 then no new + connections allowed + */ + int user_conn; /* Values of this enum and specified_limits member are used by the parser to store which user limits were specified in GRANT statement. @@ -256,7 +259,7 @@ typedef struct user_conn { /* Total length of the key. */ uint len; /* Current amount of concurrent connections for this account. */ - uint connections; + int connections; /* Current number of connections per hour, number of updating statements per hour and total number of statements per hour for this account. diff --git a/sql/table.cc b/sql/table.cc index ca958f6257a..23c43f91d22 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -989,14 +989,13 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, #endif next_chunk+= 5 + partition_info_len; } - if (share->mysql_version >= 50110) + if (share->mysql_version >= 50110 && next_chunk < buff_end) { /* New auto_partitioned indicator introduced in 5.1.11 */ #ifdef WITH_PARTITION_STORAGE_ENGINE share->auto_partitioned= *next_chunk; #endif next_chunk++; - DBUG_ASSERT(next_chunk <= buff_end); } keyinfo= share->key_info; for (i= 0; i < keys; i++, keyinfo++) @@ -5221,10 +5220,11 @@ void st_table::mark_virtual_columns_for_write(bool insert_fl) @brief Allocate space for keys - @param key_count number of keys to allocate + @param key_count number of keys to allocate additionally @details - The function allocates memory to fit 'key_count' keys for this table. + The function allocates memory to fit additionally 'key_count' keys + for this table. @return FALSE space was successfully allocated @return TRUE an error occur @@ -5232,9 +5232,11 @@ void st_table::mark_virtual_columns_for_write(bool insert_fl) bool TABLE::alloc_keys(uint key_count) { - key_info= s->key_info= (KEY*) alloc_root(&mem_root, sizeof(KEY)*key_count); - s->keys= 0; - max_keys= key_count; + key_info= (KEY*) alloc_root(&mem_root, sizeof(KEY)*(s->keys+key_count)); + if (s->keys) + memmove(key_info, s->key_info, sizeof(KEY)*s->keys); + s->key_info= key_info; + max_keys= s->keys+key_count; return !(key_info); } @@ -5379,7 +5381,7 @@ void TABLE::use_index(int key_to_save) /* Drop all keys; */ i= 0; - s->keys= (key_to_save < 0) ? 0 : 1; + s->keys= i; } /** @@ -5690,7 +5692,7 @@ int update_virtual_fields(THD *thd, TABLE *table, bool for_write) { DBUG_ENTER("update_virtual_fields"); Field **vfield_ptr, *vfield; - int error= 0; + int error __attribute__ ((unused))= 0; if (!table || !table->vfield) DBUG_RETURN(0); diff --git a/sql/table.h b/sql/table.h index c38a15df476..5dd464ac876 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1448,6 +1448,7 @@ struct TABLE_LIST table_map map_exec; /* TODO: check if this can be joined with jtbm_table_no */ uint tablenr_exec; + uint maybe_null_exec; /* Ptr to parent MERGE table list item. See top comment in ha_myisammrg.cc */ TABLE_LIST *parent_l; |