From: Ole John Aske Date: February 2 2011 2:51pm Subject: bzr commit into mysql-trunk branch (ole.john.aske:3592) Bug#59308 List-Archive: http://lists.mysql.com/commits/130247 X-Bug: 59308 Message-Id: <20110202145117.46538223@fimafeng09.norway.sun.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============5960079979970171821==" --===============5960079979970171821== MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Content-Disposition: inline #At file:///net/fimafeng09/export/home/tmp/oleja/mysql/mysql-trunk/ based on revid:marc.alff@stripped 3592 Ole John Aske 2011-02-02 Updated Fix for bug#59308: Incorrect result for SELECT DISTINCT ... ORDER BY DESC. Rebased to mysql-trunk which has changed considerably in the areas affected by the original bugfix. (based on mysql-5.1) See http://lists.mysql.com/commits/130245 for original commit comments. modified: mysql-test/include/order_by.inc mysql-test/r/order_by_icp_mrr.result mysql-test/r/order_by_none.result sql/sql_select.cc === modified file 'mysql-test/include/order_by.inc' --- a/mysql-test/include/order_by.inc 2010-12-17 09:41:21 +0000 +++ b/mysql-test/include/order_by.inc 2011-02-02 14:51:11 +0000 @@ -1686,8 +1686,25 @@ GROUP BY t1.a ORDER by c LIMIT 2; -DROP TABLE t1, t2; + DROP TABLE t1, t2; + + +--echo # +--echo # Bug #59110: Memory leak of QUICK_SELECT_I allocated memory +--echo # and +--echo # Bug #59308: Incorrect result for +--echo SELECT DISTINCT ... ORDER BY DESC +--echo +--echo # Use Valgrind to detect #59110! +--echo # + +CREATE TABLE t1 (a INT,KEY (a)); +INSERT INTO t1 VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10); + +EXPLAIN SELECT DISTINCT a,1 FROM t1 WHERE a <> 1 ORDER BY a DESC; +SELECT DISTINCT a,1 FROM t1 WHERE a <> 1 ORDER BY a DESC; +DROP TABLE t1; --echo End of 5.1 tests === modified file 'mysql-test/r/order_by_icp_mrr.result' --- a/mysql-test/r/order_by_icp_mrr.result 2010-12-17 09:41:21 +0000 +++ b/mysql-test/r/order_by_icp_mrr.result 2011-02-02 14:51:11 +0000 @@ -2523,6 +2523,31 @@ id select_type table type possible_keys 1 SIMPLE t1 index NULL a 8 NULL 10 Using index; Using temporary; Using filesort 1 SIMPLE t2 eq_ref PRIMARY PRIMARY 4 test.t1.b 1 Using where DROP TABLE t1, t2; +# +# Bug #59110: Memory leak of QUICK_SELECT_I allocated memory +# and +# Bug #59308: Incorrect result for +SELECT DISTINCT ... ORDER BY DESC + +# Use Valgrind to detect #59110! +# +CREATE TABLE t1 (a INT,KEY (a)); +INSERT INTO t1 VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10); +EXPLAIN SELECT DISTINCT a,1 FROM t1 WHERE a <> 1 ORDER BY a DESC; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 index a a 5 NULL 10 Using where; Using index; Using filesort +SELECT DISTINCT a,1 FROM t1 WHERE a <> 1 ORDER BY a DESC; +a 1 +10 1 +9 1 +8 1 +7 1 +6 1 +5 1 +4 1 +3 1 +2 1 +DROP TABLE t1; End of 5.1 tests # # Bug #38745: MySQL 5.1 optimizer uses filesort for ORDER BY === modified file 'mysql-test/r/order_by_none.result' --- a/mysql-test/r/order_by_none.result 2010-12-17 09:41:21 +0000 +++ b/mysql-test/r/order_by_none.result 2011-02-02 14:51:11 +0000 @@ -2522,6 +2522,31 @@ id select_type table type possible_keys 1 SIMPLE t1 index NULL a 8 NULL 10 Using index; Using temporary; Using filesort 1 SIMPLE t2 eq_ref PRIMARY PRIMARY 4 test.t1.b 1 Using where DROP TABLE t1, t2; +# +# Bug #59110: Memory leak of QUICK_SELECT_I allocated memory +# and +# Bug #59308: Incorrect result for +SELECT DISTINCT ... ORDER BY DESC + +# Use Valgrind to detect #59110! +# +CREATE TABLE t1 (a INT,KEY (a)); +INSERT INTO t1 VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10); +EXPLAIN SELECT DISTINCT a,1 FROM t1 WHERE a <> 1 ORDER BY a DESC; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 index a a 5 NULL 10 Using where; Using index; Using filesort +SELECT DISTINCT a,1 FROM t1 WHERE a <> 1 ORDER BY a DESC; +a 1 +10 1 +9 1 +8 1 +7 1 +6 1 +5 1 +4 1 +3 1 +2 1 +DROP TABLE t1; End of 5.1 tests # # Bug #38745: MySQL 5.1 optimizer uses filesort for ORDER BY === modified file 'sql/sql_select.cc' --- a/sql/sql_select.cc 2011-02-01 14:24:57 +0000 +++ b/sql/sql_select.cc 2011-02-02 14:51:11 +0000 @@ -19878,11 +19878,12 @@ test_if_skip_sort_order(JOIN_TAB *tab,OR { int ref_key; uint ref_key_parts; - int order_direction; + int order_direction= 0; uint used_key_parts; TABLE *table=tab->table; SQL_SELECT *select=tab->select; QUICK_SELECT_I *save_quick= 0; + int best_key= -1; Item *orig_select_cond= 0; bool orig_select_cond_saved= false; bool changed_key= false; @@ -20001,6 +20002,7 @@ test_if_skip_sort_order(JOIN_TAB *tab,OR key_map new_ref_key_map; // Force the creation of quick select new_ref_key_map.set_bit(new_ref_key); // only for new_ref_key. + select->quick= 0; if (select->test_quick_select(tab->join->thd, new_ref_key_map, 0, (tab->join->select_options & OPTION_FOUND_ROWS) ? @@ -20024,7 +20026,6 @@ test_if_skip_sort_order(JOIN_TAB *tab,OR uint best_key_parts= 0; uint saved_best_key_parts= 0; int best_key_direction= 0; - int best_key= -1; JOIN *join= tab->join; ha_rows table_records= table->file->stats.records; @@ -20048,77 +20049,16 @@ test_if_skip_sort_order(JOIN_TAB *tab,OR if (best_key >= 0) { - bool quick_created= FALSE; if (table->quick_keys.is_set(best_key) && best_key != ref_key) { key_map map; // Force the creation of quick select map.set_bit(best_key); // only best_key. - quick_created= - select->test_quick_select(join->thd, map, 0, - join->select_options & OPTION_FOUND_ROWS ? - HA_POS_ERROR : - join->unit->select_limit_cnt, - TRUE, FALSE) > 0; - } - if (!no_changes) - { - /* - If ref_key used index tree reading only ('Using index' in EXPLAIN), - and best_key doesn't, then revert the decision. - */ - if (!table->covering_keys.is_set(best_key)) - table->set_keyread(FALSE); - if (!quick_created) - { - tab->index= best_key; - tab->read_first_record= best_key_direction > 0 ? - join_read_first:join_read_last; - tab->type=JT_NEXT; // Read with index_first(), index_next() - if (select && select->quick) - { - delete select->quick; - select->quick= 0; - } - if (table->covering_keys.is_set(best_key)) - table->set_keyread(TRUE); - if (tab->pre_idx_push_select_cond) - { - tab->set_cond(tab->pre_idx_push_select_cond, __LINE__); - /* - orig_select_cond is a part of pre_idx_push_select_cond, - no need to restore it. - */ - orig_select_cond= 0; - orig_select_cond_saved= false; - } - table->file->ha_index_or_rnd_end(); - if (join->select_options & SELECT_DESCRIBE) - { - tab->ref.key= -1; - tab->ref.key_parts= 0; - if (select_limit < table_records) - tab->limit= select_limit; - } - } - else if (tab->type != JT_ALL) - { - /* - We're about to use a quick access to the table. - We need to change the access method so as the quick access - method is actually used. - */ - DBUG_ASSERT(tab->select->quick); - tab->type=JT_ALL; - tab->use_quick=QS_RANGE; - tab->ref.key= -1; - tab->ref.key_parts=0; // Don't use ref key. - tab->read_first_record= join_init_read_record; - if (tab->is_using_loose_index_scan()) - join->tmp_table_param.precomputed_group_by= TRUE; - /* - TODO: update the number of records in join->best_positions[tablenr] - */ - } + select->quick= 0; + select->test_quick_select(join->thd, map, 0, + join->select_options & OPTION_FOUND_ROWS ? + HA_POS_ERROR : + join->unit->select_limit_cnt, + TRUE, FALSE); } order_direction= best_key_direction; /* @@ -20136,6 +20076,8 @@ test_if_skip_sort_order(JOIN_TAB *tab,OR } check_reverse_order: + DBUG_ASSERT(order_direction != 0); + if (order_direction == -1) // If ORDER BY ... DESC { if (select && select->quick) @@ -20144,9 +20086,10 @@ check_reverse_order: Don't reverse the sort order, if it's already done. (In some cases test_if_order_by_key() can be called multiple times */ - if (!select->quick->reverse_sorted()) + if (select->quick->reverse_sorted()) + goto skiped_sort_order; + else { - QUICK_SELECT_I *tmp; int quick_type= select->quick->get_type(); if (quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE || quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT || @@ -20154,36 +20097,128 @@ check_reverse_order: quick_type == QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX) { tab->limit= 0; - select->quick= save_quick; goto use_filesort; // Use filesort } - - /* ORDER BY range_key DESC */ - tmp= select->quick->make_reverse(used_key_parts); - if (!tmp) - { - select->quick= save_quick; - tab->limit= 0; - goto use_filesort; // Reverse sort not supported - } - select->set_quick(tmp); } } - else if (tab->type != JT_NEXT && tab->type != JT_REF_OR_NULL && - tab->ref.key >= 0 && tab->ref.key_parts <= used_key_parts) + } + + /* + Update query plan with access pattern for doing + ordered access according to what we have decided + above. + */ + if (!no_changes) // We are allowed to update QEP + { + if (best_key >= 0) { - /* - SELECT * FROM t1 WHERE a=1 ORDER BY a DESC,b DESC + bool quick_created= + (select && select->quick && select->quick!=save_quick); - Use a traversal function that starts by reading the last row - with key part (A) and then traverse the index backwards. + /* + If ref_key used index tree reading only ('Using index' in EXPLAIN), + and best_key doesn't, then revert the decision. */ - tab->read_first_record= join_read_last_key; - tab->read_record.read_record= join_read_prev_same; + if (!table->covering_keys.is_set(best_key)) + table->set_keyread(FALSE); + if (!quick_created) + { + if (select) // Throw any existing quick select + select->quick= 0; // Cleanup either reset to save_quick, + // or 'delete save_quick' + tab->index= best_key; + tab->read_first_record= order_direction > 0 ? + join_read_first:join_read_last; + tab->type=JT_NEXT; // Read with index_first(), index_next() + + if (table->covering_keys.is_set(best_key)) + table->set_keyread(TRUE); + if (tab->pre_idx_push_select_cond) + { + tab->set_cond(tab->pre_idx_push_select_cond, __LINE__); + /* + orig_select_cond is a part of pre_idx_push_select_cond, + no need to restore it. + */ + orig_select_cond= 0; + orig_select_cond_saved= false; + } + table->file->ha_index_or_rnd_end(); + if (tab->join->select_options & SELECT_DESCRIBE) + { + tab->ref.key= -1; + tab->ref.key_parts= 0; + if (select_limit < table->file->stats.records) + tab->limit= select_limit; + } + } + else if (tab->type != JT_ALL) + { + /* + We're about to use a quick access to the table. + We need to change the access method so as the quick access + method is actually used. + */ + DBUG_ASSERT(tab->select->quick); + tab->type=JT_ALL; + tab->use_quick=QS_RANGE; + tab->ref.key= -1; + tab->ref.key_parts=0; // Don't use ref key. + tab->read_first_record= join_init_read_record; + if (tab->is_using_loose_index_scan()) + tab->join->tmp_table_param.precomputed_group_by= TRUE; + /* + TODO: update the number of records in join->best_positions[tablenr] + */ + } + } // best_key >= 0 + + if (order_direction == -1) // If ORDER BY ... DESC + { + if (select && select->quick) + { + /* ORDER BY range_key DESC */ + QUICK_SELECT_I *tmp= select->quick->make_reverse(used_key_parts); + if (!tmp) + { + tab->limit= 0; + goto use_filesort; // Reverse sort failed -> filesort + } + if (select->quick == save_quick) + save_quick= 0; // make_reverse() consumed it + select->set_quick(tmp); + } + else if (tab->type != JT_NEXT && tab->type != JT_REF_OR_NULL && + tab->ref.key >= 0 && tab->ref.key_parts <= used_key_parts) + { + /* + SELECT * FROM t1 WHERE a=1 ORDER BY a DESC,b DESC + + Use a traversal function that starts by reading the last row + with key part (A) and then traverse the index backwards. + */ + tab->read_first_record= join_read_last_key; + tab->read_record.read_record= join_read_prev_same; + } } + else if (select && select->quick) + select->quick->need_sorted_output(); + + } // QEP has been modified + + /* + Cleanup: + We may have both a 'select->quick' and 'save_quick' (original) + at this point. Delete the one that we wan't use. + */ + +skiped_sort_order: + // Keep current (ordered) select->quick + if (select && save_quick != select->quick) + { + delete save_quick; + save_quick= NULL; } - else if (select && select->quick) - select->quick->need_sorted_output(); /* Restore condition only if we didn't chose index different to what we used for ICP. @@ -20193,6 +20228,12 @@ check_reverse_order: DBUG_RETURN(1); use_filesort: + // Restore original save_quick + if (select && select->quick != save_quick) + { + delete select->quick; + select->quick= save_quick; + } if (orig_select_cond_saved) tab->set_cond(orig_select_cond, __LINE__); DBUG_RETURN(0); --===============5960079979970171821== MIME-Version: 1.0 Content-Type: text/bzr-bundle; charset="us-ascii"; name="bzr/ole.john.aske@stripped" Content-Transfer-Encoding: 7bit Content-Disposition: inline # Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: ole.john.aske@stripped\ # m2uuz6ywsxe9vfke # target_branch: file:///net/fimafeng09/export/home/tmp/oleja/mysql\ # /mysql-trunk/ # testament_sha1: df456c8a5054bc3584ab54c0c952d142bbdd37b0 # timestamp: 2011-02-02 15:51:17 +0100 # source_branch: file:///net/fimafeng09/export/home/tmp/oleja/mysql\ # /mysql-5.5/ # base_revision_id: marc.alff@stripped\ # p5azio9gpjr5oig6 # # Begin bundle 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 TkNZadPd1rpJUkkyiVEv/xdyRThQkEhMsks= --===============5960079979970171821==--