Commit be0be7af authored by Sergey Petrunya's avatar Sergey Petrunya

# MDEV-3817: Wrong result with index_merge+index_merge_intersection, InnoDB...

# MDEV-3817: Wrong result with index_merge+index_merge_intersection, InnoDB table, join, AND and OR conditions
Reconcile the fixes from:
#
# guilhem.bichot@oracle.com-20110805143029-ywrzuz15uzgontr0
# Fix for BUG#12698916 - "JOIN QUERY GIVES WRONG RESULT AT 2ND EXEC. OR
# AFTER FLUSH TABLES [-INT VS NULL]"
#
# guilhem.bichot@oracle.com-20111209150650-tzx3ldzxe1yfwji6
# Fix for BUG#12912171 - ASSERTION FAILED: QUICK->HEAD->READ_SET == SAVE_READ_SET
# and
#
and related fixes from: BUG#1006164, MDEV-376:

Now, ROR-merged QUICK_RANGE_SELECT objects make no assumptions about the values
of table->read_set and table->write_set.
Each QUICK_ROR_SELECT has (and had before) its own column bitmap, but now, all 
QUICK_ROR_SELECT's functions that care: reset(), init_ror_merged_scan(), and 
get_next()  will set table->read_set when invoked and restore it back to what 
it was before the call before they return.

This allows to avoid the mess when somebody else modifies table->read_set for 
some reason.

parent 40e94a37
......@@ -149,3 +149,25 @@ set @@join_cache_level= @save_join_cache_level;
set storage_engine=@save_storage_engine;
set optimizer_switch=@innodb_mrr_cpk_tmp;
drop table t0;
#
# MDEV-3817: Wrong result with index_merge+index_merge_intersection, InnoDB table, join, AND and OR conditions
#
set @tmp_mdev3817=@@optimizer_switch;
SET optimizer_switch='index_merge=on,index_merge_intersection=on';
CREATE TABLE t1 (
a INT PRIMARY KEY,
b INT,
c VARCHAR(1024) CHARACTER SET utf8,
d INT,
KEY (b)
) ENGINE=InnoDB;
INSERT INTO t1 VALUES
(1, 9, 'one', 11), (2, 6, 'two', 12), (3, 2, 'three', 13), (4, 5, 'four', 14);
CREATE TABLE t2 (e INT, g INT) ENGINE=InnoDB;
INSERT INTO t2 VALUES (1,9), (2,6) ;
SELECT * FROM t1, t2 WHERE g = b AND ( a < 7 OR a > e );
a b c d e g
1 9 one 11 1 9
2 6 two 12 2 6
DROP TABLE t1, t2;
set optimizer_switch=@tmp_mdev3817;
......@@ -576,4 +576,24 @@ a b
1 2
0 1
DROP TABLE t1;
#
# BUG#12912171 - ASSERTION FAILED: QUICK->HEAD->READ_SET ==
# SAVE_READ_SET
#
CREATE TABLE t1 (
a INT,
b INT,
c INT,
PRIMARY KEY (c,a), KEY (a),KEY (a)
) ENGINE=INNODB PARTITION BY KEY () PARTITIONS 2;
INSERT INTO t1 VALUES (1,5,1),(2,4,1),(3,3,1),(4,2,1),(5,1,1);
UPDATE t1 SET b = 0, c=1 WHERE a <=>0;
SELECT * FROM t1;
a b c
1 5 1
2 4 1
3 3 1
4 2 1
5 1 1
DROP TABLE t1;
set global default_storage_engine=default;
......@@ -1953,3 +1953,49 @@ a b c d
14 1 1 q
9 7 1 s
DROP TABLE t1;
#
# BUG#13256446 - ASSERTION QUICK->HEAD->READ_SET ==
# SAVE_READ_SET' FAILED IN OPT_RANGE.CC:1606
#
CREATE TABLE t1 (
f1 INT AUTO_INCREMENT,
f2 INT,
f3 INT,
f4 INT,
PRIMARY KEY (f1),KEY(f2)
) ENGINE=INNODB;
CREATE ALGORITHM=MERGE VIEW v2 AS SELECT f1,f2,f3,f4 FROM t1 WHERE f2=103;
INSERT INTO t1 VALUES (154,0,NULL,0),(0,NULL,9,0),
(NULL,102,NULL,3),(0,3,NULL,0), (9,0,NULL,0),(0,9,NULL,157);
SELECT * FROM v2;
f1 f2 f3 f4
UPDATE v2 SET f4=0, f2=NULL, f1=NULL WHERE f1 > 16 ORDER BY f1;
SELECT * FROM v2;
f1 f2 f3 f4
DROP TABLE t1;
DROP VIEW v2;
CREATE TABLE t1 (
f1 INT AUTO_INCREMENT,
f2 INT,
f3 INT,
f4 INT,
PRIMARY KEY (f1),KEY(f2)
) ENGINE=INNODB;
INSERT INTO t1 VALUES(1,NULL,NULL,0), (2,2,0,3), (9,0,107,18),
(10,0,0,0), (231,0,0,0), (232,0,8,0), (234,0,0,NULL), (235,8,0,3);
CREATE ALGORITHM=MERGE VIEW v3 AS SELECT f1,f2,f3,f4 FROM t1 WHERE f1<=85 ;
SELECT * FROM v3;
f1 f2 f3 f4
1 NULL NULL 0
2 2 0 3
9 0 107 18
10 0 0 0
UPDATE v3 SET f3=0, f4=4 WHERE f2=68 ORDER BY f1;
SELECT * FROM v3;
f1 f2 f3 f4
1 NULL NULL 0
2 2 0 3
9 0 107 18
10 0 0 0
DROP TABLE t1;
DROP VIEW v3;
......@@ -1955,4 +1955,50 @@ a b c d
9 7 1 s
14 1 1 q
DROP TABLE t1;
#
# BUG#13256446 - ASSERTION QUICK->HEAD->READ_SET ==
# SAVE_READ_SET' FAILED IN OPT_RANGE.CC:1606
#
CREATE TABLE t1 (
f1 INT AUTO_INCREMENT,
f2 INT,
f3 INT,
f4 INT,
PRIMARY KEY (f1),KEY(f2)
) ENGINE=INNODB;
CREATE ALGORITHM=MERGE VIEW v2 AS SELECT f1,f2,f3,f4 FROM t1 WHERE f2=103;
INSERT INTO t1 VALUES (154,0,NULL,0),(0,NULL,9,0),
(NULL,102,NULL,3),(0,3,NULL,0), (9,0,NULL,0),(0,9,NULL,157);
SELECT * FROM v2;
f1 f2 f3 f4
UPDATE v2 SET f4=0, f2=NULL, f1=NULL WHERE f1 > 16 ORDER BY f1;
SELECT * FROM v2;
f1 f2 f3 f4
DROP TABLE t1;
DROP VIEW v2;
CREATE TABLE t1 (
f1 INT AUTO_INCREMENT,
f2 INT,
f3 INT,
f4 INT,
PRIMARY KEY (f1),KEY(f2)
) ENGINE=INNODB;
INSERT INTO t1 VALUES(1,NULL,NULL,0), (2,2,0,3), (9,0,107,18),
(10,0,0,0), (231,0,0,0), (232,0,8,0), (234,0,0,NULL), (235,8,0,3);
CREATE ALGORITHM=MERGE VIEW v3 AS SELECT f1,f2,f3,f4 FROM t1 WHERE f1<=85 ;
SELECT * FROM v3;
f1 f2 f3 f4
1 NULL NULL 0
2 2 0 3
9 0 107 18
10 0 0 0
UPDATE v3 SET f3=0, f4=4 WHERE f2=68 ORDER BY f1;
SELECT * FROM v3;
f1 f2 f3 f4
1 NULL NULL 0
2 2 0 3
9 0 107 18
10 0 0 0
DROP TABLE t1;
DROP VIEW v3;
set optimizer_switch=@mrr_icp_extra_tmp;
......@@ -561,3 +561,45 @@ ERROR HY000: View 'test.v1' references invalid table(s) or column(s) or function
DROP VIEW v1;
DROP FUNCTION f1;
DROP TABLE t1;
#
# Verify that UPDATE does the same number of handler_update
# operations, no matter if there is ORDER BY or not.
#
CREATE TABLE t1 (i INT) ENGINE=INNODB;
INSERT INTO t1 VALUES (10),(11),(12),(13),(14),(15),(16),(17),(18),(19),
(20),(21),(22),(23),(24),(25),(26),(27),(28),(29),
(30),(31),(32),(33),(34),(35);
CREATE TABLE t2 (a CHAR(2), b CHAR(2), c CHAR(2), d CHAR(2),
INDEX idx (a,b(1),c)) ENGINE=INNODB;
INSERT INTO t2 SELECT i, i, i, i FROM t1;
FLUSH STATUS;
START TRANSACTION;
UPDATE t2 SET d = 10 WHERE b = 10 LIMIT 5;
SHOW STATUS LIKE 'HANDLER_UPDATE';
Variable_name Value
Handler_update 1
ROLLBACK;
FLUSH STATUS;
START TRANSACTION;
UPDATE t2 SET d = 10 WHERE b = 10 ORDER BY a, c LIMIT 5;
SHOW STATUS LIKE 'HANDLER_UPDATE';
Variable_name Value
Handler_update 1
ROLLBACK;
Same test with a different UPDATE.
ALTER TABLE t2 DROP INDEX idx, ADD INDEX idx2 (a, b);
FLUSH STATUS;
START TRANSACTION;
UPDATE t2 SET c = 10 LIMIT 5;
SHOW STATUS LIKE 'HANDLER_UPDATE';
Variable_name Value
Handler_update 5
ROLLBACK;
FLUSH STATUS;
START TRANSACTION;
UPDATE t2 SET c = 10 ORDER BY a, b DESC LIMIT 5;
SHOW STATUS LIKE 'HANDLER_UPDATE';
Variable_name Value
Handler_update 5
ROLLBACK;
DROP TABLE t1, t2;
......@@ -139,3 +139,29 @@ set storage_engine=@save_storage_engine;
set optimizer_switch=@innodb_mrr_cpk_tmp;
drop table t0;
--echo #
--echo # MDEV-3817: Wrong result with index_merge+index_merge_intersection, InnoDB table, join, AND and OR conditions
--echo #
set @tmp_mdev3817=@@optimizer_switch;
SET optimizer_switch='index_merge=on,index_merge_intersection=on';
CREATE TABLE t1 (
a INT PRIMARY KEY,
b INT,
c VARCHAR(1024) CHARACTER SET utf8,
d INT,
KEY (b)
) ENGINE=InnoDB;
INSERT INTO t1 VALUES
(1, 9, 'one', 11), (2, 6, 'two', 12), (3, 2, 'three', 13), (4, 5, 'four', 14);
CREATE TABLE t2 (e INT, g INT) ENGINE=InnoDB;
INSERT INTO t2 VALUES (1,9), (2,6) ;
SELECT * FROM t1, t2 WHERE g = b AND ( a < 7 OR a > e );
DROP TABLE t1, t2;
set optimizer_switch=@tmp_mdev3817;
......@@ -665,4 +665,19 @@ SHOW CREATE TABLE t1;
SELECT * FROM t1;
DROP TABLE t1;
--echo #
--echo # BUG#12912171 - ASSERTION FAILED: QUICK->HEAD->READ_SET ==
--echo # SAVE_READ_SET
--echo #
CREATE TABLE t1 (
a INT,
b INT,
c INT,
PRIMARY KEY (c,a), KEY (a),KEY (a)
) ENGINE=INNODB PARTITION BY KEY () PARTITIONS 2;
INSERT INTO t1 VALUES (1,5,1),(2,4,1),(3,3,1),(4,2,1),(5,1,1);
UPDATE t1 SET b = 0, c=1 WHERE a <=>0;
SELECT * FROM t1;
DROP TABLE t1;
set global default_storage_engine=default;
#
# Problem with range optimizer
#
--source include/have_innodb.inc
--disable_warnings
drop table if exists t1, t2, t3, t10, t100;
......@@ -1549,3 +1550,58 @@ SELECT * FROM t1 ignore index(d) WHERE d = 'q' OR d >= 'q' OR (d IN ( 'j' , 's'
SELECT * FROM t1 force index(d) WHERE d = 'q' OR d >= 'q' OR (d IN ( 'j' , 's' , 'i' ) AND ( b = 102 ));
DROP TABLE t1;
--disable_parsing
# MariaDB: Moved the following to partition.test
--echo #
--echo # BUG#12912171 - ASSERTION FAILED: QUICK->HEAD->READ_SET ==
--echo # SAVE_READ_SET
--echo #
CREATE TABLE t1 (
a INT,
b INT,
c INT,
PRIMARY KEY (c,a), KEY (a),KEY (a)
) ENGINE=INNODB PARTITION BY KEY () PARTITIONS 2;
INSERT INTO t1 VALUES (1,5,1),(2,4,1),(3,3,1),(4,2,1),(5,1,1);
UPDATE t1 SET b = 0, c=1 WHERE a <=>0;
SELECT * FROM t1;
DROP TABLE t1;
--enable_parsing
--echo #
--echo # BUG#13256446 - ASSERTION QUICK->HEAD->READ_SET ==
--echo # SAVE_READ_SET' FAILED IN OPT_RANGE.CC:1606
--echo #
CREATE TABLE t1 (
f1 INT AUTO_INCREMENT,
f2 INT,
f3 INT,
f4 INT,
PRIMARY KEY (f1),KEY(f2)
) ENGINE=INNODB;
CREATE ALGORITHM=MERGE VIEW v2 AS SELECT f1,f2,f3,f4 FROM t1 WHERE f2=103;
INSERT INTO t1 VALUES (154,0,NULL,0),(0,NULL,9,0),
(NULL,102,NULL,3),(0,3,NULL,0), (9,0,NULL,0),(0,9,NULL,157);
SELECT * FROM v2;
UPDATE v2 SET f4=0, f2=NULL, f1=NULL WHERE f1 > 16 ORDER BY f1;
SELECT * FROM v2;
DROP TABLE t1;
DROP VIEW v2;
CREATE TABLE t1 (
f1 INT AUTO_INCREMENT,
f2 INT,
f3 INT,
f4 INT,
PRIMARY KEY (f1),KEY(f2)
) ENGINE=INNODB;
INSERT INTO t1 VALUES(1,NULL,NULL,0), (2,2,0,3), (9,0,107,18),
(10,0,0,0), (231,0,0,0), (232,0,8,0), (234,0,0,NULL), (235,8,0,3);
CREATE ALGORITHM=MERGE VIEW v3 AS SELECT f1,f2,f3,f4 FROM t1 WHERE f1<=85 ;
SELECT * FROM v3;
UPDATE v3 SET f3=0, f4=4 WHERE f2=68 ORDER BY f1;
SELECT * FROM v3;
DROP TABLE t1;
DROP VIEW v3;
......@@ -2,6 +2,8 @@
# test of updating of keys
#
-- source include/have_innodb.inc
--disable_warnings
drop table if exists t1,t2;
--enable_warnings
......@@ -503,3 +505,42 @@ UPDATE v1 SET pk = 7 WHERE pk > 0;
DROP VIEW v1;
DROP FUNCTION f1;
DROP TABLE t1;
--echo #
--echo # Verify that UPDATE does the same number of handler_update
--echo # operations, no matter if there is ORDER BY or not.
--echo #
CREATE TABLE t1 (i INT) ENGINE=INNODB;
INSERT INTO t1 VALUES (10),(11),(12),(13),(14),(15),(16),(17),(18),(19),
(20),(21),(22),(23),(24),(25),(26),(27),(28),(29),
(30),(31),(32),(33),(34),(35);
CREATE TABLE t2 (a CHAR(2), b CHAR(2), c CHAR(2), d CHAR(2),
INDEX idx (a,b(1),c)) ENGINE=INNODB;
INSERT INTO t2 SELECT i, i, i, i FROM t1;
FLUSH STATUS; # FLUSH is autocommit, so we put it outside of transaction
START TRANSACTION;
UPDATE t2 SET d = 10 WHERE b = 10 LIMIT 5;
SHOW STATUS LIKE 'HANDLER_UPDATE';
ROLLBACK;
FLUSH STATUS;
START TRANSACTION;
UPDATE t2 SET d = 10 WHERE b = 10 ORDER BY a, c LIMIT 5;
SHOW STATUS LIKE 'HANDLER_UPDATE';
ROLLBACK;
--echo Same test with a different UPDATE.
ALTER TABLE t2 DROP INDEX idx, ADD INDEX idx2 (a, b);
FLUSH STATUS;
START TRANSACTION;
UPDATE t2 SET c = 10 LIMIT 5;
SHOW STATUS LIKE 'HANDLER_UPDATE';
ROLLBACK;
FLUSH STATUS;
START TRANSACTION;
UPDATE t2 SET c = 10 ORDER BY a, b DESC LIMIT 5;
SHOW STATUS LIKE 'HANDLER_UPDATE';
ROLLBACK;
DROP TABLE t1, t2;
......@@ -1788,8 +1788,6 @@ QUICK_RANGE_SELECT::QUICK_RANGE_SELECT(THD *thd, TABLE *table, uint key_nr,
bzero((char*) &alloc,sizeof(alloc));
file= head->file;
record= head->record[0];
save_read_set= head->read_set;
save_write_set= head->write_set;
/* Allocate a bitmap for used columns (Q: why not on MEM_ROOT?) */
if (!(bitmap= (my_bitmap_map*) my_malloc(head->s->column_bitmap_size,
......@@ -1859,7 +1857,6 @@ QUICK_RANGE_SELECT::~QUICK_RANGE_SELECT()
free_root(&alloc,MYF(0));
my_free(column_bitmap.bitmap);
}
head->column_bitmaps_set(save_read_set, save_write_set);
my_free(mrr_buf_desc);
DBUG_VOID_RETURN;
}
......@@ -2002,6 +1999,8 @@ int QUICK_RANGE_SELECT::init_ror_merged_scan(bool reuse_handler)
handler *save_file= file, *org_file;
my_bool org_key_read;
THD *thd= head->in_use;
MY_BITMAP * const save_read_set= head->read_set;
MY_BITMAP * const save_write_set= head->write_set;
DBUG_ENTER("QUICK_RANGE_SELECT::init_ror_merged_scan");
in_ror_merged_scan= 1;
......@@ -2052,6 +2051,7 @@ int QUICK_RANGE_SELECT::init_ror_merged_scan(bool reuse_handler)
last_rowid= file->ref;
end:
DBUG_ASSERT(head->read_set == &column_bitmap);
/*
We are only going to read key fields and call position() on 'file'
The following sets head->tmp_set to only use this key and then updates
......@@ -2065,7 +2065,8 @@ int QUICK_RANGE_SELECT::init_ror_merged_scan(bool reuse_handler)
if (!head->no_keyread)
{
doing_key_read= 1;
head->mark_columns_used_by_index(index);
head->mark_columns_used_by_index_no_reset(index, head->read_set);
head->enable_keyread();
}
head->prepare_for_position();
......@@ -2087,8 +2088,9 @@ int QUICK_RANGE_SELECT::init_ror_merged_scan(bool reuse_handler)
head->file= org_file;
head->key_read= org_key_read;
bitmap_copy(&column_bitmap, head->read_set);
head->column_bitmaps_set(&column_bitmap, &column_bitmap);
/* Restore head->read_set (and write_set) to what they had before the call */
head->column_bitmaps_set(save_read_set, save_write_set);
if (reset())
{
......@@ -2145,9 +2147,19 @@ int QUICK_ROR_INTERSECT_SELECT::init_ror_merged_scan(bool reuse_handler)
while ((cur= quick_it++))
{
quick= cur->quick;
#ifndef DBUG_OFF
const MY_BITMAP * const save_read_set= quick->head->read_set;
const MY_BITMAP * const save_write_set= quick->head->write_set;
#endif
if (quick->init_ror_merged_scan(FALSE))
DBUG_RETURN(1);
quick->file->extra(HA_EXTRA_KEYREAD_PRESERVE_FIELDS);
// Sets are shared by all members of "quick_selects" so must not change
#ifndef DBUG_OFF
DBUG_ASSERT(quick->head->read_set == save_read_set);
DBUG_ASSERT(quick->head->write_set == save_write_set);
#endif
/* All merged scans share the same record buffer in intersection. */
quick->record= head->record[0];
}
......@@ -10941,16 +10953,20 @@ int QUICK_RANGE_SELECT::reset()
uchar *mrange_buff;
int error;
HANDLER_BUFFER empty_buf;
MY_BITMAP * const save_read_set= head->read_set;
MY_BITMAP * const save_write_set= head->write_set;
DBUG_ENTER("QUICK_RANGE_SELECT::reset");
last_range= NULL;
cur_range= (QUICK_RANGE**) ranges.buffer;
RANGE_SEQ_IF seq_funcs= {NULL, quick_range_seq_init, quick_range_seq_next, 0, 0};
if (file->inited == handler::NONE)
{
if (in_ror_merged_scan)
head->column_bitmaps_set_no_signal(&column_bitmap, &column_bitmap);
if (file->inited == handler::NONE)
{
if ((error= file->ha_index_init(index,1)))
DBUG_RETURN(error);
goto err;
}
/* Allocate buffer if we need one but haven't allocated it yet */
......@@ -10985,10 +11001,14 @@ int QUICK_RANGE_SELECT::reset()
if (!mrr_buf_desc)
empty_buf.buffer= empty_buf.buffer_end= empty_buf.end_of_used_area= NULL;
RANGE_SEQ_IF seq_funcs= {NULL, quick_range_seq_init, quick_range_seq_next, 0, 0};
error= file->multi_range_read_init(&seq_funcs, (void*)this, ranges.elements,
mrr_flags, mrr_buf_desc? mrr_buf_desc:
&empty_buf);
err:
/* Restore bitmaps set on entry */
if (in_ror_merged_scan)
head->column_bitmaps_set_no_signal(save_read_set, save_write_set);
DBUG_RETURN(error);
}
......@@ -11011,6 +11031,9 @@ int QUICK_RANGE_SELECT::reset()
int QUICK_RANGE_SELECT::get_next()
{
range_id_t dummy;
MY_BITMAP * const save_read_set= head->read_set;
MY_BITMAP * const save_write_set= head->write_set;
DBUG_ENTER("QUICK_RANGE_SELECT::get_next");
if (in_ror_merged_scan)
{
......
......@@ -430,7 +430,7 @@ class QUICK_RANGE_SELECT : public QUICK_SELECT_I
/* Members to deal with case when this quick select is a ROR-merged scan */
bool in_ror_merged_scan;
MY_BITMAP column_bitmap, *save_read_set, *save_write_set;
MY_BITMAP column_bitmap;
bool free_file; /* TRUE <=> this->file is "owned" by this quick select */
/* Range pointers to be used when not using MRR interface */
......
......@@ -480,12 +480,15 @@ int mysql_update(THD *thd,
We can't update table directly; We must first search after all
matching rows before updating the table!
*/
// Verify that table->restore_column_maps_after_mark_index() will work
DBUG_ASSERT(table->read_set == &table->def_read_set);
DBUG_ASSERT(table->write_set == &table->def_write_set);
if (used_index < MAX_KEY && old_covering_keys.is_set(used_index))
table->add_read_columns_used_by_index(used_index);
else
{
table->use_all_columns();
}
/* note: We avoid sorting if we sort on the used index */
if (order && (need_sort || used_key_is_modified))
......@@ -619,7 +622,10 @@ int mysql_update(THD *thd,
if (error >= 0)
goto err;
}
if (table->key_read)
/*
This restore bitmaps, works for add_read_columns_used_by_index() and
use_all_columns():
*/
table->restore_column_maps_after_mark_index();
}
......
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