From: Ole John Aske
Date: January 6 2011 8:01am
Subject: bzr commit into mysql-5.1 branch (ole.john.aske:3534) Bug#59308
List-Archive: http://lists.mysql.com/commits/128047
X-Bug: 59308
Message-Id: <20110106080158.F01E2223@fimafeng09.norway.sun.com>
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="===============5661707771542271276=="
--===============5661707771542271276==
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-06
Updated 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-06 08:01:54 +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-06 08:01:54 +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-06 08:01:54 +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);
}
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)
@@ -13747,7 +13700,6 @@ check_reverse_order:
*/
if (!select->quick->reverse_sorted())
{
- QUICK_SELECT_DESC *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 ||
@@ -13755,39 +13707,132 @@ 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;
}
- }
- 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);
}
--===============5661707771542271276==
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\
# 1w303zmr8w393zj0
# target_branch: file:///net/fimafeng09/export/home/tmp/oleja/mysql\
# /mysql-5.1/
# testament_sha1: cd35ccb8d16ea09707153bf2fd9a546840b9009c
# timestamp: 2011-01-06 09:01:58 +0100
# base_revision_id: nirbhay.choubey@stripped\
# kl5tqdbqh70k9cu1
#
# Begin bundle
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
woSGCz6ncA==
--===============5661707771542271276==--