From: Ole John Aske Date: January 5 2011 1:48pm Subject: bzr commit into mysql-5.1 branch (ole.john.aske:3534) Bug#59308 List-Archive: http://lists.mysql.com/commits/127976 X-Bug: 59308 Message-Id: <20110105134859.2BB15223@fimafeng09.norway.sun.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============5646953537066667289==" --===============5646953537066667289== 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-5.1/ based on revid:nirbhay.choubey@stripped 3534 Ole John Aske 2011-01-05 Fix for bug#59308: Incorrect result for SELECT DISTINCT ... ORDER BY DESC. Also fix bug#59110: Memory leak of QUICK_SELECT_I allocated memory. Root cause of these bugs are that test_if_skip_sort_order() decided to revert the 'skip_sort_order' descision (and use filesort) after the query plan has been updated to reflect a 'skip' of the sort order. This might happen in 'check_reverse_order:' if we have a select->quick which could not be made descending by appending a QUICK_SELECT_DESC. (). The original 'save_quick' was then restored after the QEP has been modified, which caused: - An incorrect 'precomputed_group_by= TRUE' may have been set, and not reverted, as part of the already modifified QEP (Bug#59398) - A 'select->quick' might have been created which we fail to delete (bug#59110). This fix is a refactorication of test_if_skip_sort_order() where all logic related to modification of QEP (controlled by argument 'bool no_changes'), is moved to the end of test_if_skip_sort_order(), and done after *all* 'test_if_skip' checks has been performed - including the 'check_reverse_order:' checks. The refactorication above contains now intentional changes to the logic which has been moved to the end of the function. Furthermore, a smaller part of the fix address the handling of the select->quick objects which may already exists when we call 'test_if_skip_sort_order()' (save_quick) - and new select->quick's created during test_if_skip_sort_order(): - Before new select->quick may be created by calling ::test_quick_select(), we set 'select->quick= 0' to avoid that ::test_quick_select() prematurely delete the save_quick's. (After this call we may have both a 'save_quick' and 'select->quick') - All returns from ::test_if_skip_sort_order() where we may have both a 'save_quick' and a 'select->quick' has been changed to goto's to the exit points 'skiped_sort_order:' or 'need_filesort:' where we decide which of the QUICK_SELECT's to keep, and delete the other. modified: mysql-test/r/order_by.result mysql-test/t/order_by.test sql/sql_select.cc === modified file 'mysql-test/r/order_by.result' --- a/mysql-test/r/order_by.result 2010-09-13 11:33:19 +0000 +++ b/mysql-test/r/order_by.result 2011-01-05 13:48:53 +0000 @@ -1638,4 +1638,29 @@ 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 === modified file 'mysql-test/t/order_by.test' --- a/mysql-test/t/order_by.test 2010-09-13 11:33:19 +0000 +++ b/mysql-test/t/order_by.test 2011-01-05 13:48:53 +0000 @@ -1492,4 +1492,21 @@ LIMIT 2; 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 'sql/sql_select.cc' --- a/sql/sql_select.cc 2010-12-28 23:47:05 +0000 +++ b/sql/sql_select.cc 2011-01-05 13:48:53 +0000 @@ -13338,12 +13338,14 @@ 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; key_map usable_keys; QUICK_SELECT_I *save_quick= 0; + int best_key= -1; + DBUG_ENTER("test_if_skip_sort_order"); LINT_INIT(ref_key_parts); @@ -13447,13 +13449,14 @@ test_if_skip_sort_order(JOIN_TAB *tab,OR new_ref_key_map.clear_all(); // 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) ? HA_POS_ERROR : tab->join->unit->select_limit_cnt,0) <= 0) - DBUG_RETURN(0); + goto need_filesort; } ref_key= new_ref_key; } @@ -13478,7 +13481,6 @@ test_if_skip_sort_order(JOIN_TAB *tab,OR int best_key_direction= 0; ha_rows best_records= 0; double read_time; - int best_key= -1; bool is_best_covering= FALSE; double fanout= 1; JOIN *join= tab->join; @@ -13655,72 +13657,21 @@ test_if_skip_sort_order(JOIN_TAB *tab,OR tab->join->tables > tab->join->const_tables + 1) && ((unsigned) best_key != table->s->primary_key || !table->file->primary_key_is_clustered())) - DBUG_RETURN(0); + goto need_filesort; if (best_key >= 0) { - bool quick_created= FALSE; if (table->quick_keys.is_set(best_key) && best_key != ref_key) { key_map map; map.clear_all(); // 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, - 0) > 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); - 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=1; - 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, + 0) > 0; } order_direction= best_key_direction; /* @@ -13733,10 +13684,12 @@ test_if_skip_sort_order(JOIN_TAB *tab,OR saved_best_key_parts : best_key_parts; } else - DBUG_RETURN(0); + goto need_filesort; } check_reverse_order: + DBUG_ASSERT(order_direction != 0); + if (order_direction == -1) // If ORDER BY ... DESC { if (select && select->quick) @@ -13755,39 +13708,133 @@ check_reverse_order: quick_type == QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX) { tab->limit= 0; - select->quick= save_quick; - DBUG_RETURN(0); // Use filesort + goto need_filesort; // Use filesort } - + } + } + } + + /* + 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) + { + bool quick_created= + (select && select->quick && select->quick!=save_quick); + + /* + 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) + { + if (select) + select->quick= 0; // Throw any existing quick select + + 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); + 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=1; + 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) + { + QUICK_SELECT_DESC *tmp; /* ORDER BY range_key DESC */ - tmp= new QUICK_SELECT_DESC((QUICK_RANGE_SELECT*)(select->quick), + tmp= new QUICK_SELECT_DESC((QUICK_RANGE_SELECT*)(select->quick), used_key_parts); - if (!tmp || tmp->error) + if (tmp && select->quick==save_quick) + save_quick= 0; // ::QUICK_SELECT_DESC consumed it + + if (!tmp || tmp->error) { - delete tmp; - select->quick= save_quick; + delete tmp; tab->limit= 0; - DBUG_RETURN(0); // Reverse sort not supported + goto need_filesort; // Reverse sort not supported -> filesort } - select->quick=tmp; + select->quick=tmp; + DBUG_ASSERT(select->quick->sorted); } - } - 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 + 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; + 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->sorted= 1; + + } // 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->sorted= 1; DBUG_RETURN(1); + +need_filesort: + // Restore original save_quick + if (select && select->quick != save_quick) + { + delete select->quick; + select->quick= save_quick; + } + DBUG_RETURN(0); } --===============5646953537066667289== 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\ # ovqjrladtcxb11rx # target_branch: file:///net/fimafeng09/export/home/tmp/oleja/mysql\ # /mysql-5.1/ # testament_sha1: ab4ebce1e04d21e52639ba4f46b80790f9266988 # timestamp: 2011-01-05 14:48:58 +0100 # base_revision_id: nirbhay.choubey@stripped\ # kl5tqdbqh70k9cu1 # # Begin bundle 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 --===============5646953537066667289==--