Loading mysql-test/include/mix1.inc +18 −0 Original line number Diff line number Diff line Loading @@ -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 Loading mysql-test/r/innodb_mysql.result +15 −0 Original line number Diff line number Diff line Loading @@ -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, Loading sql/opt_range.cc +12 −54 Original line number Diff line number Diff line Loading @@ -8556,7 +8556,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; Loading Loading @@ -8598,8 +8599,9 @@ 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, 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) Loading @@ -8625,7 +8627,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, Loading @@ -8634,6 +8638,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, Loading Loading @@ -8731,54 +8737,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; Loading sql/opt_range.h +1 −3 Original line number Diff line number Diff line Loading @@ -686,12 +686,10 @@ class QUICK_SELECT_DESC: public QUICK_RANGE_SELECT 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<QUICK_RANGE> rev_ranges; List_iterator<QUICK_RANGE> rev_it; uint used_key_parts; }; Loading sql/sql_select.cc +24 −5 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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)) 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); } Loading Loading
mysql-test/include/mix1.inc +18 −0 Original line number Diff line number Diff line Loading @@ -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 Loading
mysql-test/r/innodb_mysql.result +15 −0 Original line number Diff line number Diff line Loading @@ -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, Loading
sql/opt_range.cc +12 −54 Original line number Diff line number Diff line Loading @@ -8556,7 +8556,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; Loading Loading @@ -8598,8 +8599,9 @@ 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, 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) Loading @@ -8625,7 +8627,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, Loading @@ -8634,6 +8638,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, Loading Loading @@ -8731,54 +8737,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; Loading
sql/opt_range.h +1 −3 Original line number Diff line number Diff line Loading @@ -686,12 +686,10 @@ class QUICK_SELECT_DESC: public QUICK_RANGE_SELECT 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<QUICK_RANGE> rev_ranges; List_iterator<QUICK_RANGE> rev_it; uint used_key_parts; }; Loading
sql/sql_select.cc +24 −5 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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)) 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); } Loading