diff --git a/mysql-test/include/mix1.inc b/mysql-test/include/mix1.inc index c33cc5be588..f4e9cf74b72 100644 --- a/mysql-test/include/mix1.inc +++ b/mysql-test/include/mix1.inc @@ -1103,6 +1103,24 @@ set @my_innodb_commit_concurrency=@@global.innodb_commit_concurrency; set global innodb_commit_concurrency=0; set global innodb_commit_concurrency=@my_innodb_commit_concurrency; +# +# Bug #37830: ORDER BY ASC/DESC - no difference +# + +CREATE TABLE t1 (a int, b int, c int, PRIMARY KEY (a), KEY t1_b (b)) + ENGINE=InnoDB; + +INSERT INTO t1 (a,b,c) VALUES (1,1,1), (2,1,1), (3,1,1), (4,1,1); +INSERT INTO t1 (a,b,c) SELECT a+4,b,c FROM t1; + +# should be range access +EXPLAIN SELECT a, b, c FROM t1 WHERE b = 1 ORDER BY a DESC LIMIT 5; + +# should produce '8 7 6 5 4' for a +SELECT a, b, c FROM t1 WHERE b = 1 ORDER BY a DESC LIMIT 5; + +DROP TABLE t1; + --echo End of 5.0 tests # Fix for BUG#19243 "wrong LAST_INSERT_ID() after ON DUPLICATE KEY diff --git a/mysql-test/r/innodb_mysql.result b/mysql-test/r/innodb_mysql.result index b6bcd7b0ada..61302be7e23 100644 --- a/mysql-test/r/innodb_mysql.result +++ b/mysql-test/r/innodb_mysql.result @@ -1362,6 +1362,21 @@ set global innodb_autoextend_increment=@my_innodb_autoextend_increment; set @my_innodb_commit_concurrency=@@global.innodb_commit_concurrency; set global innodb_commit_concurrency=0; set global innodb_commit_concurrency=@my_innodb_commit_concurrency; +CREATE TABLE t1 (a int, b int, c int, PRIMARY KEY (a), KEY t1_b (b)) +ENGINE=InnoDB; +INSERT INTO t1 (a,b,c) VALUES (1,1,1), (2,1,1), (3,1,1), (4,1,1); +INSERT INTO t1 (a,b,c) SELECT a+4,b,c FROM t1; +EXPLAIN SELECT a, b, c FROM t1 WHERE b = 1 ORDER BY a DESC LIMIT 5; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 index t1_b PRIMARY 4 NULL 8 Using where +SELECT a, b, c FROM t1 WHERE b = 1 ORDER BY a DESC LIMIT 5; +a b c +8 1 1 +7 1 1 +6 1 1 +5 1 1 +4 1 1 +DROP TABLE t1; End of 5.0 tests CREATE TABLE `t2` ( `k` int(11) NOT NULL auto_increment, diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 826b8bc871c..d0ca4ea7b23 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -8560,7 +8560,8 @@ bool QUICK_RANGE_SELECT::row_in_ranges() QUICK_SELECT_DESC::QUICK_SELECT_DESC(QUICK_RANGE_SELECT *q, uint used_key_parts_arg) - :QUICK_RANGE_SELECT(*q), rev_it(rev_ranges) + :QUICK_RANGE_SELECT(*q), rev_it(rev_ranges), + used_key_parts (used_key_parts_arg) { QUICK_RANGE *r; @@ -8602,10 +8603,11 @@ int QUICK_SELECT_DESC::get_next() int result; if (last_range) { // Already read through key - result = ((last_range->flag & EQ_RANGE) - ? file->index_next_same(record, last_range->min_key, - last_range->min_length) : - file->index_prev(record)); + result = ((last_range->flag & EQ_RANGE && + used_key_parts <= head->key_info[index].key_parts) ? + file->index_next_same(record, last_range->min_key, + last_range->min_length) : + file->index_prev(record)); if (!result) { if (cmp_prev(*rev_it.ref()) == 0) @@ -8629,7 +8631,9 @@ int QUICK_SELECT_DESC::get_next() continue; } - if (last_range->flag & EQ_RANGE) + if (last_range->flag & EQ_RANGE && + used_key_parts <= head->key_info[index].key_parts) + { result = file->index_read_map(record, last_range->max_key, last_range->max_keypart_map, @@ -8638,6 +8642,8 @@ int QUICK_SELECT_DESC::get_next() else { DBUG_ASSERT(last_range->flag & NEAR_MAX || + (last_range->flag & EQ_RANGE && + used_key_parts > head->key_info[index].key_parts) || range_reads_after_key(last_range)); result=file->index_read_map(record, last_range->max_key, last_range->max_keypart_map, @@ -8735,54 +8741,6 @@ bool QUICK_SELECT_DESC::range_reads_after_key(QUICK_RANGE *range_arg) } -/* TRUE if we are reading over a key that may have a NULL value */ - -#ifdef NOT_USED -bool QUICK_SELECT_DESC::test_if_null_range(QUICK_RANGE *range_arg, - uint used_key_parts) -{ - uint offset, end; - KEY_PART *key_part = key_parts, - *key_part_end= key_part+used_key_parts; - - for (offset= 0, end = min(range_arg->min_length, range_arg->max_length) ; - offset < end && key_part != key_part_end ; - offset+= key_part++->store_length) - { - if (!memcmp((char*) range_arg->min_key+offset, - (char*) range_arg->max_key+offset, - key_part->store_length)) - continue; - - if (key_part->null_bit && range_arg->min_key[offset]) - return 1; // min_key is null and max_key isn't - // Range doesn't cover NULL. This is ok if there is no more null parts - break; - } - /* - If the next min_range is > NULL, then we can use this, even if - it's a NULL key - Example: SELECT * FROM t1 WHERE a = 2 AND b >0 ORDER BY a DESC,b DESC; - - */ - if (key_part != key_part_end && key_part->null_bit) - { - if (offset >= range_arg->min_length || range_arg->min_key[offset]) - return 1; // Could be null - key_part++; - } - /* - If any of the key parts used in the ORDER BY could be NULL, we can't - use the key to sort the data. - */ - for (; key_part != key_part_end ; key_part++) - if (key_part->null_bit) - return 1; // Covers null part - return 0; -} -#endif - - void QUICK_RANGE_SELECT::add_info_string(String *str) { KEY *key_info= head->key_info + index; diff --git a/sql/opt_range.h b/sql/opt_range.h index 4f5cce28bf2..2a647c77686 100644 --- a/sql/opt_range.h +++ b/sql/opt_range.h @@ -686,12 +686,10 @@ public: int get_type() { return QS_TYPE_RANGE_DESC; } private: bool range_reads_after_key(QUICK_RANGE *range); -#ifdef NOT_USED - bool test_if_null_range(QUICK_RANGE *range, uint used_key_parts); -#endif int reset(void) { rev_it.rewind(); return QUICK_RANGE_SELECT::reset(); } List rev_ranges; List_iterator rev_it; + uint used_key_parts; }; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 8eeffe276f1..c5e8286409c 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -12508,6 +12508,9 @@ part_of_refkey(TABLE *table,Field *field) @note used_key_parts is set to correct key parts used if return value != 0 (On other cases, used_key_part may be changed) + Note that the value may actually be greater than the number of index + key parts. This can happen for storage engines that have the primary + key parts as a suffix for every secondary key. @retval 1 key is ok. @@ -12580,11 +12583,27 @@ static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx, reverse=flag; // Remember if reverse key_part++; } - *used_key_parts= on_primary_key ? table->key_info[idx].key_parts : - (uint) (key_part - table->key_info[idx].key_part); - if (reverse == -1 && !(table->file->index_flags(idx, *used_key_parts-1, 1) & - HA_READ_PREV)) - reverse= 0; // Index can't be used + if (on_primary_key) + { + uint used_key_parts_secondary= table->key_info[idx].key_parts; + uint used_key_parts_pk= + (uint) (key_part - table->key_info[table->s->primary_key].key_part); + *used_key_parts= used_key_parts_pk + used_key_parts_secondary; + + if (reverse == -1 && + (!(table->file->index_flags(idx, used_key_parts_secondary - 1, 1) & + HA_READ_PREV) || + !(table->file->index_flags(table->s->primary_key, + used_key_parts_pk - 1, 1) & HA_READ_PREV))) + reverse= 0; // Index can't be used + } + else + { + *used_key_parts= (uint) (key_part - table->key_info[idx].key_part); + if (reverse == -1 && + !(table->file->index_flags(idx, *used_key_parts-1, 1) & HA_READ_PREV)) + reverse= 0; // Index can't be used + } DBUG_RETURN(reverse); }