Commit df963fd3 authored by Sergey Petrunya's avatar Sergey Petrunya

MDEV-5177: ha_partition and innodb index intersection produce fewer rows (MySQL Bug#70703)

MDEV-5555: Incorrect index_merge on BTREE indices
- In ha_partition, make ordered index reads return rows in rowid order
  when index columns are the same.
parent e2a99f18
...@@ -2493,3 +2493,70 @@ i ...@@ -2493,3 +2493,70 @@ i
3 3
4 4
DROP TABLE t1; DROP TABLE t1;
#
# MDEV-5177: ha_partition and innodb index intersection produce fewer rows (MySQL Bug#70703)
#
create table t1 (
a int not null,
b int not null,
pk int not null,
primary key (pk),
key(a),
key(b)
) partition by hash(pk) partitions 10;
insert into t1 values (1,2,4);
insert into t1 values (1,0,17);
insert into t1 values (1,2,25);
insert into t1 values (10,20,122);
insert into t1 values (10,20,123);
create table t2 (a int);
insert into t2 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
insert into t1 select 1,2, 200 + A.a + 10*B.a + 100*C.a from t2 A, t2 B, t2 C;
insert into t1 select 10+A.a + 10*B.a + 100*C.a + 1000*D.a,
10+A.a + 10*B.a + 100*C.a + 1000*D.a,
2000 + A.a + 10*B.a + 100*C.a + 1000*D.a
from t2 A, t2 B, t2 C ,t2 D;
explain select * from t1 where a=1 and b=2 and pk between 1 and 999999 ;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ref PRIMARY,a,b b 4 const 982 Using where
create temporary table t3 as
select * from t1 where a=1 and b=2 and pk between 1 and 999 ;
select count(*) from t3;
count(*)
802
drop table t3;
create temporary table t3 as
select * from t1 ignore index(a,b) where a=1 and b=2 and pk between 1 and 999 ;
select count(*) from t3;
count(*)
802
drop table t3;
drop table t1,t2;
#
# MDEV-5555: Incorrect index_merge on BTREE indices
#
CREATE TABLE t1 (
id bigint(20) unsigned NOT NULL,
id2 bigint(20) unsigned NOT NULL,
dob date DEFAULT NULL,
address char(100) DEFAULT NULL,
city char(35) DEFAULT NULL,
hours_worked_per_week smallint(5) unsigned DEFAULT NULL,
weeks_worked_last_year tinyint(3) unsigned DEFAULT NULL,
KEY dob (dob),
KEY address (address),
KEY city (city),
KEY hours_worked_per_week (hours_worked_per_week),
KEY weeks_worked_last_year (weeks_worked_last_year)
) ENGINE=MyISAM DEFAULT CHARSET=latin1
PARTITION BY KEY (id) PARTITIONS 5;
# Insert some rows
select * from t1 where hours_worked_per_week = 40 and weeks_worked_last_year = 52 and dob < '1949-11-21';
id id2 dob address city hours_worked_per_week weeks_worked_last_year
16 16 1949-11-07 address16 city16 40 52
50 50 1923-09-08 address50 city50 40 52
select * from t1 IGNORE INDEX(dob, weeks_worked_last_year, hours_worked_per_week) where hours_worked_per_week = 40 and weeks_worked_last_year = 52 and dob < '1949-11-21';
id id2 dob address city hours_worked_per_week weeks_worked_last_year
16 16 1949-11-07 address16 city16 40 52
50 50 1923-09-08 address50 city50 40 52
drop table t1;
...@@ -752,8 +752,8 @@ select * from t1 force index (b) where b < 10 ORDER BY b DESC; ...@@ -752,8 +752,8 @@ select * from t1 force index (b) where b < 10 ORDER BY b DESC;
a b a b
6 6 6 6
4 5 4 5
2 4
30 4 30 4
2 4
3 3 3 3
35 2 35 2
7 1 7 1
......
This diff is collapsed.
...@@ -80,6 +80,7 @@ static handler *partition_create_handler(handlerton *hton, ...@@ -80,6 +80,7 @@ static handler *partition_create_handler(handlerton *hton,
static uint partition_flags(); static uint partition_flags();
static uint alter_table_flags(uint flags); static uint alter_table_flags(uint flags);
extern "C" int cmp_key_then_part_id(void *key_p, uchar *ref1, uchar *ref2);
static int partition_initialize(void *p) static int partition_initialize(void *p)
{ {
...@@ -4540,8 +4541,8 @@ bool ha_partition::init_record_priority_queue() ...@@ -4540,8 +4541,8 @@ bool ha_partition::init_record_priority_queue()
} while (++i < m_tot_parts); } while (++i < m_tot_parts);
m_start_key.key= (const uchar*)ptr; m_start_key.key= (const uchar*)ptr;
/* Initialize priority queue, initialized to reading forward. */ /* Initialize priority queue, initialized to reading forward. */
if (init_queue(&m_queue, used_parts, (uint) PARTITION_BYTES_IN_POS, if (init_queue(&m_queue, used_parts, 0,
0, key_rec_cmp, (void*)m_curr_key_info, 0, 0)) 0, cmp_key_then_part_id, (void*)m_curr_key_info, 0, 0))
{ {
my_free(m_ordered_rec_buffer); my_free(m_ordered_rec_buffer);
m_ordered_rec_buffer= NULL; m_ordered_rec_buffer= NULL;
...@@ -4737,6 +4738,52 @@ int ha_partition::index_read_map(uchar *buf, const uchar *key, ...@@ -4737,6 +4738,52 @@ int ha_partition::index_read_map(uchar *buf, const uchar *key,
} }
/*
@brief
Provide ordering by (key_value, partition_id).
@detail
Ordering by partition id is required so that key scans on key=const
return rows in rowid order (this is required for some variants of
index_merge to work).
In ha_partition, rowid is a (partition_id, underlying_table_rowid).
handle_ordered_index_scan must return rows ordered by (key, rowid).
If two rows have the same key value and come from different partitions,
it is sufficient to return them in the order of their partition_id.
*/
extern "C" int cmp_key_then_part_id(void *key_p, uchar *ref1, uchar *ref2)
{
my_ptrdiff_t diff1, diff2;
int res;
if ((res= key_rec_cmp(key_p, ref1 + PARTITION_BYTES_IN_POS,
ref2 + PARTITION_BYTES_IN_POS)))
{
return res;
}
/* The following was taken from ha_partition::cmp_ref */
diff1= ref2[1] - ref1[1];
diff2= ref2[0] - ref1[0];
if (!diff1 && !diff2)
return 0;
if (diff1 > 0)
return(-1);
if (diff1 < 0)
return(+1);
if (diff2 > 0)
return(-1);
return(+1);
}
/** /**
Common routine for a number of index_read variants Common routine for a number of index_read variants
...@@ -7647,6 +7694,16 @@ uint ha_partition::min_record_length(uint options) const ...@@ -7647,6 +7694,16 @@ uint ha_partition::min_record_length(uint options) const
If they belong to different partitions we decide that they are not If they belong to different partitions we decide that they are not
the same record. Otherwise we use the particular handler to decide if the same record. Otherwise we use the particular handler to decide if
they are the same. Sort in partition id order if not equal. they are the same. Sort in partition id order if not equal.
MariaDB note:
Please don't merge the code from MySQL that does this:
We get two references and need to check if those records are the same.
If they belong to different partitions we decide that they are not
the same record. Otherwise we use the particular handler to decide if
they are the same. Sort in partition id order if not equal.
It is incorrect, MariaDB has an alternative fix.
*/ */
int ha_partition::cmp_ref(const uchar *ref1, const uchar *ref2) int ha_partition::cmp_ref(const uchar *ref1, const uchar *ref2)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment