diff options
Diffstat (limited to 'sql/sql_select.h')
-rw-r--r-- | sql/sql_select.h | 290 |
1 files changed, 208 insertions, 82 deletions
diff --git a/sql/sql_select.h b/sql/sql_select.h index 54138712da4..d955d569ced 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -2,7 +2,7 @@ #define SQL_SELECT_INCLUDED /* Copyright (c) 2000, 2013, Oracle and/or its affiliates. - Copyright (c) 2008, 2013, Monty Program Ab. + Copyright (c) 2008, 2015, MariaDB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -29,33 +29,10 @@ #endif #include "procedure.h" -#include <myisam.h> #include "sql_array.h" /* Array */ #include "records.h" /* READ_RECORD */ #include "opt_range.h" /* SQL_SELECT, QUICK_SELECT_I */ - -#if defined(WITH_ARIA_STORAGE_ENGINE) -#include <maria.h> -#endif -#if defined(USE_ARIA_FOR_TMP_TABLES) -#define TMP_ENGINE_HTON maria_hton -inline uint tmp_table_max_key_length() { - return maria_max_key_length(); -} - -inline uint tmp_table_max_key_parts() { - return maria_max_key_segments(); -} -#else -#define TMP_ENGINE_HTON myisam_hton -inline uint tmp_table_max_key_length() { - return MI_MAX_KEY_LENGTH; -} -inline uint tmp_table_max_key_parts() { - return MI_MAX_KEY_SEG; -} -#endif /* Values in optimize */ #define KEY_OPTIMIZE_EXISTS 1 #define KEY_OPTIMIZE_REF_OR_NULL 2 @@ -114,6 +91,13 @@ typedef struct st_table_ref uchar *key_buff; ///< value to look for with key uchar *key_buff2; ///< key_buff+key_length store_key **key_copy; // + + /* + Bitmap of key parts which refer to constants. key_copy only has copiers for + non-const key parts. + */ + key_part_map const_ref_part_map; + Item **items; ///< val()'s for each keypart /* Array of pointers to trigger variables. Some/all of the pointers may be @@ -204,12 +188,17 @@ int rr_sequential(READ_RECORD *info); int rr_sequential_and_unpack(READ_RECORD *info); +#include "sql_explain.h" + +/************************************************************************************** + * New EXPLAIN structures END + *************************************************************************************/ + class JOIN_CACHE; class SJ_TMP_TABLE; class JOIN_TAB_RANGE; typedef struct st_join_table { - st_join_table() {} /* Remove gcc warning */ TABLE *table; KEYUSE *keyuse; /**< pointer to first used key */ KEY *hj_key; /**< descriptor of the used best hash join key @@ -258,7 +247,11 @@ typedef struct st_join_table { JOIN_TAB_RANGE *bush_children; /* Special content for EXPLAIN 'Extra' column or NULL if none */ - const char *info; + enum explain_extra_tag info; + + Table_access_tracker *tracker; + + Table_access_tracker *jbuf_tracker; /* Bitmap of TAB_INFO_* bits that encodes special line for EXPLAIN 'Extra' column, or 0 if there is no info. @@ -295,8 +288,11 @@ typedef struct st_join_table { */ double read_time; - /* psergey-todo: make the below have type double, like POSITION::records_read? */ - ha_rows records_read; + /* Copy of POSITION::records_read, set by get_best_combination() */ + double records_read; + + /* The selectivity of the conditions that can be pushed to the table */ + double cond_selectivity; /* Startup cost for execution */ double startup_cost; @@ -364,7 +360,12 @@ typedef struct st_join_table { SJ_TMP_TABLE *check_weed_out_table; /* for EXPLAIN only: */ SJ_TMP_TABLE *first_weedout_table; - + + /** + reference to saved plan and execution statistics + */ + Explain_table_access *explain_plan; + /* If set, means we should stop join enumeration after we've got the first match and return to the specified join tab. May point to @@ -529,7 +530,22 @@ typedef struct st_join_table { ha_rows get_examined_rows(); bool preread_init(); - bool is_sjm_nest() { return test(bush_children); } + bool is_sjm_nest() { return MY_TEST(bush_children); } + + /* + If this join_tab reads a non-merged semi-join (also called jtbm), return + the select's number. Otherwise, return 0. + */ + int get_non_merged_semijoin_select() const + { + Item_in_subselect *subq; + if (table->pos_in_table_list && + (subq= table->pos_in_table_list->jtbm_subselect)) + { + return subq->unit->first_select()->select_number; + } + return 0; /* Not a merged semi-join */ + } bool access_from_tables_is_allowed(table_map used_tables, table_map sjm_lookup_tables) @@ -542,6 +558,12 @@ typedef struct st_join_table { bool keyuse_is_valid_for_access_in_chosen_plan(JOIN *join, KEYUSE *keyuse); + void remove_redundant_bnl_scan_conds(); + + void save_explain_data(Explain_table_access *eta, table_map prefix_tables, + bool distinct, struct st_join_table *first_top_tab); + + void update_explain_data(uint idx); } JOIN_TAB; @@ -723,8 +745,7 @@ public: struct st_position *pos, struct st_position *loose_scan_pos); friend bool get_best_combination(JOIN *join); - friend int setup_semijoin_dups_elimination(JOIN *join, ulonglong options, - uint no_jbuf_after); + friend int setup_semijoin_loosescan(JOIN *join); friend void fix_semijoin_strategies_for_picked_join_order(JOIN *join); }; @@ -746,7 +767,7 @@ public: void set_empty() { sjm_scan_need_tables= 0; - LINT_INIT(sjm_scan_last_inner); + LINT_INIT_STRUCT(sjm_scan_last_inner); is_used= FALSE; } void set_from_prev(struct st_position *prev); @@ -769,7 +790,7 @@ public: Information about a position of table within a join order. Used in join optimization. */ -typedef struct st_position :public Sql_alloc +typedef struct st_position { /* The table that's put into join order */ JOIN_TAB *table; @@ -781,6 +802,9 @@ typedef struct st_position :public Sql_alloc */ double records_read; + /* The selectivity of the pushed down conditions */ + double cond_selectivity; + /* Cost accessing the table in course of the entire complete join execution, i.e. cost of one access method use (e.g. 'range' or 'ref' scan ) times @@ -789,7 +813,7 @@ typedef struct st_position :public Sql_alloc double read_time; /* Cumulative cost and record count for the join prefix */ - COST_VECT prefix_cost; + Cost_estimate prefix_cost; double prefix_record_count; /* @@ -860,6 +884,7 @@ public: JOIN_TAB *end; }; +class Pushdown_query; class JOIN :public Sql_alloc { @@ -889,6 +914,7 @@ protected: { keyuse.elements= 0; keyuse.buffer= NULL; + keyuse.malloc_flags= 0; best_positions= 0; /* To detect errors */ error= my_multi_malloc(MYF(MY_WME), &best_positions, @@ -913,7 +939,7 @@ protected: enum enum_reopt_result { REOPT_NEW_PLAN, /* there is a new reoptimized plan */ REOPT_OLD_PLAN, /* no new improved plan can be found, use the old one */ - REOPT_ERROR, /* an irrecovarable error occured during reoptimization */ + REOPT_ERROR, /* an irrecovarable error occurred during reoptimization */ REOPT_NONE /* not yet reoptimized */ }; @@ -951,7 +977,7 @@ public: */ JOIN_TAB *table_access_tabs; uint top_table_access_tabs_count; - + JOIN_TAB **map2table; ///< mapping between table indexes and JOIN_TABs JOIN_TAB *join_tab_save; ///< saved join_tab for subquery reexecution @@ -986,7 +1012,19 @@ public: */ uint top_join_tab_count; uint send_group_parts; - bool group; /**< If query contains GROUP BY clause */ + /* + This counts how many times do_select() was invoked for this JOIN. + It's used to restrict Pushdown_query::execute() only to the first + do_select() invocation. + */ + uint do_select_call_count; + /* + True if the query has GROUP BY. + (that is, if group_by != NULL. when DISTINCT is converted into GROUP BY, it + will set this, too. It is not clear why we need a separate var from + group_list) + */ + bool group; bool need_distinct; /** @@ -1025,8 +1063,24 @@ public: table_map outer_join; /* Bitmap of tables used in the select list items */ table_map select_list_used_tables; - ha_rows send_records, found_records, examined_rows, - row_limit, select_limit, duplicate_rows; + ha_rows send_records,found_records,join_examined_rows; + + /* + LIMIT for the JOIN operation. When not using aggregation or DISITNCT, this + is the same as select's LIMIT clause specifies. + Note that this doesn't take sql_calc_found_rows into account. + */ + ha_rows row_limit; + + /* + How many output rows should be produced after GROUP BY. + (if sql_calc_found_rows is used, LIMIT is ignored) + */ + ha_rows select_limit; + /* + Number of duplicate rows found in UNION. + */ + ha_rows duplicate_rows; /** Used to fetch no more than given amount of rows per one fetch operation of server side cursor. @@ -1035,11 +1089,17 @@ public: - fetch_limit= HA_POS_ERROR if there is no cursor. - when we open a cursor, we set fetch_limit to 0, - on each fetch iteration we add num_rows to fetch to fetch_limit + NOTE: currently always HA_POS_ERROR. */ ha_rows fetch_limit; + /* Finally picked QEP. This is result of join optimization */ POSITION *best_positions; + Pushdown_query *pushdown_query; + JOIN_TAB *original_join_tab; + uint original_table_count; + /******* Join optimization state members start *******/ /* pointer - we're doing optimization for a semi-join materialization nest. @@ -1084,7 +1144,7 @@ public: reexecutions. This value is equal to the multiplication of all join->positions[i].records_read of a JOIN. */ - double record_count; + double join_record_count; List<Item> *fields; List<Cached_item> group_fields, group_fields_cache; TABLE *tmp_table; @@ -1129,6 +1189,12 @@ public: restore_no_rows_in_result() in ::reinit() */ bool no_rows_in_result_called; + + /** + This is set if SQL_CALC_ROWS was calculated by filesort() + and should be taken from the appropriate JOIN_TAB + */ + bool filesort_found_rows; /** Copy of this JOIN to be used with temporary tables. @@ -1179,7 +1245,8 @@ public: /** Is set if we have a GROUP BY and we have ORDER BY on a constant. */ bool skip_sort_order; - bool need_tmp, hidden_group_fields; + bool need_tmp; + bool hidden_group_fields; /* TRUE if there was full cleunap of the JOIN */ bool cleaned; DYNAMIC_ARRAY keyuse; @@ -1229,9 +1296,18 @@ public: const char *zero_result_cause; ///< not 0 if exec must return zero result bool union_part; ///< this subselect is part of union - bool optimized; ///< flag to avoid double optimization in EXPLAIN + + enum join_optimization_state { NOT_OPTIMIZED=0, + OPTIMIZATION_IN_PROGRESS=1, + OPTIMIZATION_DONE=2}; + // state of JOIN optimization + enum join_optimization_state optimization_state; bool initialized; ///< flag to avoid double init_execution calls + Explain_select *explain; + + enum { QEP_NOT_PRESENT_YET, QEP_AVAILABLE, QEP_DELETED} have_query_plan; + /* Additional WHERE and HAVING predicates to be considered for IN=>EXISTS subquery transformation of a JOIN object. @@ -1274,6 +1350,7 @@ public: table_count= 0; top_join_tab_count= 0; const_tables= 0; + const_table_map= 0; eliminated_tables= 0; join_list= 0; implicit_grouping= FALSE; @@ -1283,7 +1360,7 @@ public: duplicate_rows= send_records= 0; found_records= 0; fetch_limit= HA_POS_ERROR; - examined_rows= 0; + join_examined_rows= 0; exec_tmp_table1= 0; exec_tmp_table2= 0; sortorder= 0; @@ -1298,7 +1375,7 @@ public: lock= thd_arg->lock; select_lex= 0; //for safety tmp_join= 0; - select_distinct= test(select_options & SELECT_DISTINCT); + select_distinct= MY_TEST(select_options & SELECT_DISTINCT); no_order= 0; simple_order= 0; simple_group= 0; @@ -1312,7 +1389,8 @@ public: ref_pointer_array= items0= items1= items2= items3= 0; ref_pointer_array_size= 0; zero_result_cause= 0; - optimized= 0; + optimization_state= JOIN::NOT_OPTIMIZED; + have_query_plan= QEP_NOT_PRESENT_YET; initialized= 0; cleaned= 0; cond_equal= 0; @@ -1321,6 +1399,11 @@ public: group_optimized_away= 0; no_rows_in_result_called= 0; positions= best_positions= 0; + pushdown_query= 0; + original_join_tab= 0; + do_select_call_count= 0; + + explain= NULL; all_fields= fields_arg; if (&fields_list != &fields_arg) /* Avoid valgrind-warning */ @@ -1339,6 +1422,7 @@ public: emb_sjm_nest= NULL; sjm_lookup_tables= 0; sjm_scan_tables= 0; + /* The following is needed because JOIN::cleanup(true) may be called for joins for which JOIN::optimize was aborted with an error before a proper @@ -1353,9 +1437,11 @@ public: SELECT_LEX_UNIT *unit); bool prepare_stage2(); int optimize(); + int optimize_inner(); int reinit(); int init_execution(); void exec(); + void exec_inner(); int destroy(); void restore_tmp(); bool alloc_func_list(); @@ -1401,7 +1487,7 @@ public: having_value != Item::COND_FALSE); } bool empty_result() { return (zero_result_cause && !implicit_grouping); } - bool change_result(select_result *result); + bool change_result(select_result *new_result, select_result *old_result); bool is_top_level_join() const { return (unit == &thd->lex->unit && (unit->fake_select_lex == 0 || @@ -1431,7 +1517,7 @@ public: void set_allowed_join_cache_types(); bool is_allowed_hash_join_access() { - return test(allowed_join_cache_types & JOIN_CACHE_HASHED_BIT) && + return MY_TEST(allowed_join_cache_types & JOIN_CACHE_HASHED_BIT) && max_allowed_join_cache_level > JOIN_CACHE_HASHED_BIT; } /* @@ -1450,7 +1536,7 @@ public: return ((const_tables != table_count && ((select_distinct || !simple_order || !simple_group) || (group_list && order) || - test(select_options & OPTION_BUFFER_RESULT))) || + MY_TEST(select_options & OPTION_BUFFER_RESULT))) || (rollup.state != ROLLUP::STATE_NONE && select_distinct)); } bool choose_subquery_plan(table_map join_tables); @@ -1469,6 +1555,13 @@ public: { return (unit->item && unit->item->is_in_predicate()); } + void save_explain_data(Explain_query *output, bool can_overwrite, + bool need_tmp_table, bool need_order, bool distinct); + int save_explain_data_intern(Explain_query *output, bool need_tmp_table, + bool need_order, bool distinct, + const char *message); + JOIN_TAB *first_breadth_first_optimization_tab() { return table_access_tabs; } + JOIN_TAB *first_breadth_first_execution_tab() { return join_tab; } private: /** TRUE if the query contains an aggregate function but has no GROUP @@ -1482,7 +1575,7 @@ private: enum enum_with_bush_roots { WITH_BUSH_ROOTS, WITHOUT_BUSH_ROOTS}; enum enum_with_const_tables { WITH_CONST_TABLES, WITHOUT_CONST_TABLES}; -JOIN_TAB *first_linear_tab(JOIN *join, +JOIN_TAB *first_linear_tab(JOIN *join, enum enum_with_bush_roots include_bush_roots, enum enum_with_const_tables const_tbls); JOIN_TAB *next_linear_tab(JOIN* join, JOIN_TAB* tab, @@ -1509,8 +1602,8 @@ bool copy_funcs(Item **func_ptr, const THD *thd); uint find_shortest_key(TABLE *table, const key_map *usable_keys); Field* create_tmp_field_from_field(THD *thd, Field* org_field, const char *name, TABLE *table, - Item_field *item, uint convert_blob_length); - + Item_field *item); + bool is_indexed_agg_distinct(JOIN *join, List<Item_field> *out_args); /* functions from opt_sum.cc */ @@ -1532,21 +1625,8 @@ public: store_key(THD *thd, Field *field_arg, uchar *ptr, uchar *null, uint length) :null_key(0), null_ptr(null), err(0) { - if (field_arg->type() == MYSQL_TYPE_BLOB - || field_arg->type() == MYSQL_TYPE_GEOMETRY) - { - /* - Key segments are always packed with a 2 byte length prefix. - See mi_rkey for details. - */ - to_field= new Field_varstring(ptr, length, 2, null, 1, - Field::NONE, field_arg->field_name, - field_arg->table->s, field_arg->charset()); - to_field->init(field_arg->table); - } - else - to_field=field_arg->new_key_field(thd->mem_root, field_arg->table, - ptr, null, 1); + to_field=field_arg->new_key_field(thd->mem_root, field_arg->table, + ptr, length, null, 1); } store_key(store_key &arg) :Sql_alloc(), null_key(arg.null_key), to_field(arg.to_field), @@ -1752,9 +1832,10 @@ bool cp_buffer_from_ref(THD *thd, TABLE *table, TABLE_REF *ref); bool error_if_full_join(JOIN *join); int report_error(TABLE *table, int error); int safe_index_read(JOIN_TAB *tab); -COND *remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value); int get_quick_record(SQL_SELECT *select); -SORT_FIELD * make_unireg_sortorder(ORDER *order, uint *length, +SORT_FIELD *make_unireg_sortorder(THD *thd, JOIN *join, + table_map first_table_map, + ORDER *order, uint *length, SORT_FIELD *sortorder); int setup_order(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables, List<Item> &fields, List <Item> &all_fields, ORDER *order); @@ -1782,12 +1863,7 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, Field **def_field, bool group, bool modify_item, bool table_cant_handle_bit_fields, - bool make_copy_field, - uint convert_blob_length); -bool create_internal_tmp_table(TABLE *table, KEY *keyinfo, - ENGINE_COLUMNDEF *start_recinfo, - ENGINE_COLUMNDEF **recinfo, - ulonglong options, my_bool big_tables); + bool make_copy_field); /* General routine to change field->ptr of a NULL-terminated array of Field @@ -1803,9 +1879,9 @@ int test_if_item_cache_changed(List<Cached_item> &list); int join_init_read_record(JOIN_TAB *tab); int join_read_record_no_init(JOIN_TAB *tab); void set_position(JOIN *join,uint idx,JOIN_TAB *table,KEYUSE *key); -inline Item * and_items(Item* cond, Item *item) +inline Item * and_items(THD *thd, Item* cond, Item *item) { - return (cond? (new Item_cond_and(cond, item)) : item); + return (cond ? (new (thd->mem_root) Item_cond_and(thd, cond, item)) : item); } bool choose_plan(JOIN *join, table_map join_tables); void optimize_wo_join_buffering(JOIN *join, uint first_tab, uint last_tab, @@ -1822,8 +1898,14 @@ inline bool optimizer_flag(THD *thd, uint flag) return (thd->variables.optimizer_switch & flag); } +/* +int print_fake_select_lex_join(select_result_sink *result, bool on_the_fly, + SELECT_LEX *select_lex, uint8 select_options); +*/ + uint get_index_for_order(ORDER *order, TABLE *table, SQL_SELECT *select, - ha_rows limit, bool *need_sort, bool *reverse); + ha_rows limit, ha_rows *scanned_limit, + bool *need_sort, bool *reverse); ORDER *simple_remove_const(ORDER *order, COND *where); bool const_expression_in_where(COND *cond, Item *comp_item, Field *comp_field= NULL, @@ -1837,6 +1919,19 @@ void eliminate_tables(JOIN *join); /* Index Condition Pushdown entry point function */ void push_index_cond(JOIN_TAB *tab, uint keyno); +#define OPT_LINK_EQUAL_FIELDS 1 + +/* EXPLAIN-related utility functions */ +int print_explain_message_line(select_result_sink *result, + uint8 options, bool is_analyze, + uint select_number, + const char *select_type, + ha_rows *rows, + const char *message); +void explain_append_mrr_info(QUICK_RANGE_SELECT *quick, String *res); +int append_possible_keys(MEM_ROOT *alloc, String_list &list, TABLE *table, + key_map possible_keys); + /**************************************************************************** Temporary table support for SQL Runtime ***************************************************************************/ @@ -1853,17 +1948,48 @@ TABLE *create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, bool keep_row_order= FALSE); void free_tmp_table(THD *thd, TABLE *entry); bool create_internal_tmp_table_from_heap(THD *thd, TABLE *table, - ENGINE_COLUMNDEF *start_recinfo, - ENGINE_COLUMNDEF **recinfo, + TMP_ENGINE_COLUMNDEF *start_recinfo, + TMP_ENGINE_COLUMNDEF **recinfo, int error, bool ignore_last_dupp_key_error, bool *is_duplicate); bool create_internal_tmp_table(TABLE *table, KEY *keyinfo, - ENGINE_COLUMNDEF *start_recinfo, - ENGINE_COLUMNDEF **recinfo, + TMP_ENGINE_COLUMNDEF *start_recinfo, + TMP_ENGINE_COLUMNDEF **recinfo, ulonglong options); bool open_tmp_table(TABLE *table); void setup_tmp_table_column_bitmaps(TABLE *table, uchar *bitmaps); double prev_record_reads(POSITION *positions, uint idx, table_map found_ref); void fix_list_after_tbl_changes(SELECT_LEX *new_parent, List<TABLE_LIST> *tlist); +struct st_cond_statistic +{ + Item *cond; + Field *field_arg; + ulong positive; +}; +typedef struct st_cond_statistic COND_STATISTIC; + +ulong check_selectivity(THD *thd, + ulong rows_to_read, + TABLE *table, + List<COND_STATISTIC> *conds); + +class Pushdown_query: public Sql_alloc +{ +public: + SELECT_LEX *select_lex; + bool store_data_in_temp_table; + group_by_handler *handler; + Item *having; + + Pushdown_query(SELECT_LEX *select_lex_arg, group_by_handler *handler_arg) + : select_lex(select_lex_arg), store_data_in_temp_table(0), + handler(handler_arg), having(0) {} + + ~Pushdown_query() { delete handler; } + + /* Function that calls the above scan functions */ + int execute(JOIN *join); +}; + #endif /* SQL_SELECT_INCLUDED */ |