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