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==--