Commit 4b4dfd57 authored by Igor Babaev's avatar Igor Babaev

Merge.

parents 21627048 adc1f2f4
...@@ -103,6 +103,8 @@ extern HA_KEYSEG *ha_find_null(HA_KEYSEG *keyseg, const uchar *a); ...@@ -103,6 +103,8 @@ extern HA_KEYSEG *ha_find_null(HA_KEYSEG *keyseg, const uchar *a);
if we're scanning "t.key BETWEEN 10 AND 20" and got a if we're scanning "t.key BETWEEN 10 AND 20" and got a
"t.key=21" tuple (the engine should stop scanning and "t.key=21" tuple (the engine should stop scanning and
return HA_ERR_END_OF_FILE right away). return HA_ERR_END_OF_FILE right away).
3=ICP_ABORTED_BY_USER - engine must stop scanning and should return
HA_ERR_ABORTED_BY_USER right away
-1= ICP_ERROR - Reserved for internal errors in engines. Should not be -1= ICP_ERROR - Reserved for internal errors in engines. Should not be
returned by index_cond_func_xxx returned by index_cond_func_xxx
*/ */
...@@ -111,7 +113,8 @@ typedef enum icp_result { ...@@ -111,7 +113,8 @@ typedef enum icp_result {
ICP_ERROR=-1, ICP_ERROR=-1,
ICP_NO_MATCH=0, ICP_NO_MATCH=0,
ICP_MATCH=1, ICP_MATCH=1,
ICP_OUT_OF_RANGE=2 ICP_OUT_OF_RANGE=2,
ICP_ABORTED_BY_USER=3,
} ICP_RESULT; } ICP_RESULT;
......
...@@ -86,6 +86,152 @@ SELECT * FROM t1 WHERE c2 <=> NULL ORDER BY c1,c2 LIMIT 2; ...@@ -86,6 +86,152 @@ SELECT * FROM t1 WHERE c2 <=> NULL ORDER BY c1,c2 LIMIT 2;
--echo --echo
DROP TABLE t1; DROP TABLE t1;
--echo #
--echo # Bug#43617 - Innodb returns wrong results with timestamp's range value
--echo # in IN clause
--echo # (Note: Fixed by patch for BUG#42580)
--echo #
CREATE TABLE t1(
c1 TIMESTAMP NOT NULL,
c2 TIMESTAMP NULL,
c3 DATE,
c4 DATETIME,
PRIMARY KEY(c1),
UNIQUE INDEX(c2)
);
INSERT INTO t1 VALUES
('0000-00-00 00:00:00','0000-00-00 00:00:00','2008-01-04','2008-01-05 00:00:00'),
('1971-01-01 00:00:01','1980-01-01 00:00:01','2009-01-01','2009-01-02 00:00:00'),
('1999-01-01 00:00:00','1999-01-01 00:00:00', NULL, NULL),
('2007-05-23 09:15:28','2007-05-23 09:15:28','2007-05-24','2007-05-24 09:15:28'),
('2007-05-27 00:00:00','2007-05-25 00:00:00','2007-05-26','2007-05-26 00:00:00'),
('2008-01-01 00:00:00', NULL, '2008-01-02','2008-01-03 00:00:00'),
('2009-01-29 11:11:27','2009-01-29 11:11:27','2009-01-29','2009-01-29 11:11:27'),
('2038-01-09 03:14:07','2038-01-09 03:14:07','2009-01-05','2009-01-06 00:00:00');
--echo
SELECT *
FROM t1
WHERE c2 IN ('1971-01-01 00:00:01','2038-01-09 03:14:07')
ORDER BY c2;
--echo
SELECT *
FROM t1
WHERE c2 IN ('1971-01-01 00:00:01','2038-01-09 03:14:07')
ORDER BY c2 LIMIT 2;
--echo
SELECT *
FROM t1
WHERE c2 IN ('1971-01-01 00:00:01','2038-01-09 03:14:07')
ORDER BY c2 DESC;
--echo
SELECT *
FROM t1
WHERE c2 IN ('1971-01-01 00:00:01','2038-01-09 03:14:07')
ORDER BY c2 DESC LIMIT 2;
--echo
DROP TABLE t1;
--echo #
--echo # BUG#43618: MyISAM&Maria returns wrong results with 'between'
--echo # on timestamp
--echo #
CREATE TABLE t1(
ts TIMESTAMP NOT NULL,
c char NULL,
PRIMARY KEY(ts)
);
INSERT INTO t1 VALUES
('1971-01-01','a'),
('2007-05-25','b'),
('2008-01-01','c'),
('2038-01-09','d');
--disable_warnings
--echo
--echo # Execute select with invalid timestamp, desc ordering
SELECT *
FROM t1
WHERE ts BETWEEN '0000-00-00' AND '2010-00-01 00:00:00'
ORDER BY ts DESC
LIMIT 2;
--echo
--echo # Should use index condition
EXPLAIN
SELECT *
FROM t1
WHERE ts BETWEEN '0000-00-00' AND '2010-00-01 00:00:00'
ORDER BY ts DESC
LIMIT 2;
--echo
--enable_warnings
DROP TABLE t1;
--echo #
--echo # BUG#49906: Assertion failed - Field_varstring::val_str in field.cc
--echo # (Note: Fixed by patch for LP BUG#625841)
--echo #
CREATE TABLE t1 (
f1 VARCHAR(1024),
f2 VARCHAR(10),
INDEX test_idx USING BTREE (f2,f1(5))
);
INSERT INTO t1 VALUES ('a','c'), ('b','d');
SELECT f1
FROM t1
WHERE f2 LIKE 'd'
ORDER BY f1;
DROP TABLE t1;
--echo #
--echo # Bug#52660 - "Perf. regr. using ICP for MyISAM on range queries on
--echo # an index containing TEXT"
--echo #
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
CREATE TABLE t2 (a INT);
INSERT INTO t2 SELECT A.a + 10*(B.a) FROM t1 A, t1 B;
CREATE TABLE t3 (
c1 TINYTEXT NOT NULL,
i1 INT NOT NULL,
KEY (c1(6),i1)
);
INSERT INTO t3 SELECT CONCAT('c-',1000+t2.a,'=w'), 1 FROM t2;
EXPLAIN
SELECT c1 FROM t3 WHERE c1 >= 'c-1004=w' and c1 <= 'c-1006=w';
SELECT c1 FROM t3 WHERE c1 >= 'c-1004=w' and c1 <= 'c-1006=w';
EXPLAIN
SELECT c1 FROM t3 WHERE c1 >= 'c-1004=w' and c1 <= 'c-1006=w' and i1 > 2;
SELECT c1 FROM t3 WHERE c1 >= 'c-1004=w' and c1 <= 'c-1006=w' and i1 > 2;
EXPLAIN
SELECT c1 FROM t3 WHERE c1 >= 'c-1004=w' and c1 <= 'c-1006=w' or i1 > 2;
SELECT c1 FROM t3 WHERE c1 >= 'c-1004=w' and c1 <= 'c-1006=w' or i1 > 2;
DROP TABLE t1, t2, t3;
--echo # --echo #
--echo # Bug#40992 - InnoDB: Crash when engine_condition_pushdown is on --echo # Bug#40992 - InnoDB: Crash when engine_condition_pushdown is on
--echo # --echo #
...@@ -102,6 +248,7 @@ SELECT * FROM t WHERE a > 2 FOR UPDATE; ...@@ -102,6 +248,7 @@ SELECT * FROM t WHERE a > 2 FOR UPDATE;
DROP TABLE t; DROP TABLE t;
--echo # --echo #
--echo # Bug#35080 - Innodb crash at mem_block_get_len line 72 --echo # Bug#35080 - Innodb crash at mem_block_get_len line 72
--echo # --echo #
...@@ -226,6 +373,100 @@ SELECT COUNT(*) FROM t3; ...@@ -226,6 +373,100 @@ SELECT COUNT(*) FROM t3;
DROP PROCEDURE insert_data; DROP PROCEDURE insert_data;
DROP TABLE t1, t2, t3; DROP TABLE t1, t2, t3;
--echo #
--echo # Bug#57372 "Multi-table updates and deletes fail when running with ICP
--echo # against InnoDB"
--echo #
CREATE TABLE t1 (
a INT KEY,
b INT
);
CREATE TABLE t2 (
a INT KEY,
b INT
);
INSERT INTO t1 VALUES (1, 101), (2, 102), (3, 103), (4, 104), (5, 105);
INSERT INTO t2 VALUES (1, 1), (2, 2), (3, 3), (4, 4), (5, 5);
UPDATE t1, t2
SET t1.a = t1.a + 100, t2.b = t1.a + 10
WHERE t1.a BETWEEN 2 AND 4 AND t2.a = t1.b - 100;
--sorted_result
SELECT * FROM t1;
--sorted_result
SELECT * FROM t2;
DROP TABLE t1, t2;
--echo #
--echo # Bug#52605 - "Adding LIMIT 1 clause to query with complex range
--echo # predicate causes wrong results"
--echo #
CREATE TABLE t1 (
pk INT NOT NULL,
c1 INT,
PRIMARY KEY (pk),
KEY k1 (c1)
);
INSERT INTO t1 VALUES (1,NULL);
INSERT INTO t1 VALUES (2,6);
INSERT INTO t1 VALUES (3,NULL);
INSERT INTO t1 VALUES (4,6);
INSERT INTO t1 VALUES (5,NULL);
INSERT INTO t1 VALUES (6,NULL);
INSERT INTO t1 VALUES (7,9);
INSERT INTO t1 VALUES (8,0);
SELECT pk, c1
FROM t1
WHERE (pk BETWEEN 4 AND 5 OR pk < 2) AND c1 < 240
ORDER BY c1
LIMIT 1;
EXPLAIN SELECT pk, c1
FROM t1
WHERE (pk BETWEEN 4 AND 5 OR pk < 2) AND c1 < 240
ORDER BY c1
LIMIT 1;
DROP TABLE t1;
--echo #
--echo # Bug#59259 "Incorrect rows returned for a correlated subquery
--echo # when ICP is on"
--echo #
CREATE TABLE t1 (pk INTEGER PRIMARY KEY, i INTEGER NOT NULL) ENGINE=InnoDB;
INSERT INTO t1 VALUES (11,0);
INSERT INTO t1 VALUES (12,5);
INSERT INTO t1 VALUES (15,0);
CREATE TABLE t2 (pk INTEGER PRIMARY KEY, i INTEGER NOT NULL) ENGINE=InnoDB;
INSERT INTO t2 VALUES (11,1);
INSERT INTO t2 VALUES (12,2);
INSERT INTO t2 VALUES (15,4);
set @save_optimizer_switch= @@optimizer_switch;
set optimizer_switch='semijoin=off';
EXPLAIN
SELECT * FROM t1
WHERE pk IN (SELECT it.pk FROM t2 JOIN t2 AS it ON it.i=it.i WHERE it.pk-t1.i<10);
SELECT * FROM t1
WHERE pk IN (SELECT it.pk FROM t2 JOIN t2 AS it ON it.i=it.i WHERE it.pk-t1.i<10);
set optimizer_switch=@save_optimizer_switch;
DROP TABLE t1, t2;
--echo # --echo #
--echo # BUG#778434 Wrong result with in_to_exists=on in maria-5.3-mwl89 --echo # BUG#778434 Wrong result with in_to_exists=on in maria-5.3-mwl89
--echo # --echo #
......
...@@ -783,7 +783,7 @@ create table t1 (a int primary key,b int, c int, d int, e int, f int, g int, h ...@@ -783,7 +783,7 @@ create table t1 (a int primary key,b int, c int, d int, e int, f int, g int, h
insert into t1 values (1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1); insert into t1 values (1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1);
explain select * from t1 where a > 0 and a < 50; explain select * from t1 where a > 0 and a < 50;
id select_type table type possible_keys key key_len ref rows Extra id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL # Using where 1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL # Using index condition
drop table t1; drop table t1;
create table t1 (id int NOT NULL,id2 int NOT NULL,id3 int NOT NULL,dummy1 char(30),primary key (id,id2),index index_id3 (id3)) engine=innodb; create table t1 (id int NOT NULL,id2 int NOT NULL,id3 int NOT NULL,dummy1 char(30),primary key (id,id2),index index_id3 (id3)) engine=innodb;
insert into t1 values (0,0,0,'ABCDEFGHIJ'),(2,2,2,'BCDEFGHIJK'),(1,1,1,'CDEFGHIJKL'); insert into t1 values (0,0,0,'ABCDEFGHIJ'),(2,2,2,'BCDEFGHIJK'),(1,1,1,'CDEFGHIJKL');
......
...@@ -83,6 +83,150 @@ c1 c2 c3 c4 ...@@ -83,6 +83,150 @@ c1 c2 c3 c4
DROP TABLE t1; DROP TABLE t1;
# #
# Bug#43617 - Innodb returns wrong results with timestamp's range value
# in IN clause
# (Note: Fixed by patch for BUG#42580)
#
CREATE TABLE t1(
c1 TIMESTAMP NOT NULL,
c2 TIMESTAMP NULL,
c3 DATE,
c4 DATETIME,
PRIMARY KEY(c1),
UNIQUE INDEX(c2)
);
INSERT INTO t1 VALUES
('0000-00-00 00:00:00','0000-00-00 00:00:00','2008-01-04','2008-01-05 00:00:00'),
('1971-01-01 00:00:01','1980-01-01 00:00:01','2009-01-01','2009-01-02 00:00:00'),
('1999-01-01 00:00:00','1999-01-01 00:00:00', NULL, NULL),
('2007-05-23 09:15:28','2007-05-23 09:15:28','2007-05-24','2007-05-24 09:15:28'),
('2007-05-27 00:00:00','2007-05-25 00:00:00','2007-05-26','2007-05-26 00:00:00'),
('2008-01-01 00:00:00', NULL, '2008-01-02','2008-01-03 00:00:00'),
('2009-01-29 11:11:27','2009-01-29 11:11:27','2009-01-29','2009-01-29 11:11:27'),
('2038-01-09 03:14:07','2038-01-09 03:14:07','2009-01-05','2009-01-06 00:00:00');
SELECT *
FROM t1
WHERE c2 IN ('1971-01-01 00:00:01','2038-01-09 03:14:07')
ORDER BY c2;
c1 c2 c3 c4
2038-01-09 03:14:07 2038-01-09 03:14:07 2009-01-05 2009-01-06 00:00:00
SELECT *
FROM t1
WHERE c2 IN ('1971-01-01 00:00:01','2038-01-09 03:14:07')
ORDER BY c2 LIMIT 2;
c1 c2 c3 c4
2038-01-09 03:14:07 2038-01-09 03:14:07 2009-01-05 2009-01-06 00:00:00
SELECT *
FROM t1
WHERE c2 IN ('1971-01-01 00:00:01','2038-01-09 03:14:07')
ORDER BY c2 DESC;
c1 c2 c3 c4
2038-01-09 03:14:07 2038-01-09 03:14:07 2009-01-05 2009-01-06 00:00:00
SELECT *
FROM t1
WHERE c2 IN ('1971-01-01 00:00:01','2038-01-09 03:14:07')
ORDER BY c2 DESC LIMIT 2;
c1 c2 c3 c4
2038-01-09 03:14:07 2038-01-09 03:14:07 2009-01-05 2009-01-06 00:00:00
DROP TABLE t1;
#
# BUG#43618: MyISAM&Maria returns wrong results with 'between'
# on timestamp
#
CREATE TABLE t1(
ts TIMESTAMP NOT NULL,
c char NULL,
PRIMARY KEY(ts)
);
INSERT INTO t1 VALUES
('1971-01-01','a'),
('2007-05-25','b'),
('2008-01-01','c'),
('2038-01-09','d');
# Execute select with invalid timestamp, desc ordering
SELECT *
FROM t1
WHERE ts BETWEEN '0000-00-00' AND '2010-00-01 00:00:00'
ORDER BY ts DESC
LIMIT 2;
ts c
2008-01-01 00:00:00 c
2007-05-25 00:00:00 b
# Should use index condition
EXPLAIN
SELECT *
FROM t1
WHERE ts BETWEEN '0000-00-00' AND '2010-00-01 00:00:00'
ORDER BY ts DESC
LIMIT 2;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 2 Using index condition
DROP TABLE t1;
#
# BUG#49906: Assertion failed - Field_varstring::val_str in field.cc
# (Note: Fixed by patch for LP BUG#625841)
#
CREATE TABLE t1 (
f1 VARCHAR(1024),
f2 VARCHAR(10),
INDEX test_idx USING BTREE (f2,f1(5))
);
INSERT INTO t1 VALUES ('a','c'), ('b','d');
SELECT f1
FROM t1
WHERE f2 LIKE 'd'
ORDER BY f1;
f1
b
DROP TABLE t1;
#
# Bug#52660 - "Perf. regr. using ICP for MyISAM on range queries on
# an index containing TEXT"
#
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
CREATE TABLE t2 (a INT);
INSERT INTO t2 SELECT A.a + 10*(B.a) FROM t1 A, t1 B;
CREATE TABLE t3 (
c1 TINYTEXT NOT NULL,
i1 INT NOT NULL,
KEY (c1(6),i1)
);
INSERT INTO t3 SELECT CONCAT('c-',1000+t2.a,'=w'), 1 FROM t2;
EXPLAIN
SELECT c1 FROM t3 WHERE c1 >= 'c-1004=w' and c1 <= 'c-1006=w';
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t3 range c1 c1 8 NULL 3 Using where
SELECT c1 FROM t3 WHERE c1 >= 'c-1004=w' and c1 <= 'c-1006=w';
c1
c-1004=w
c-1005=w
c-1006=w
EXPLAIN
SELECT c1 FROM t3 WHERE c1 >= 'c-1004=w' and c1 <= 'c-1006=w' and i1 > 2;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t3 range c1 c1 12 NULL 2 Using index condition; Using where
SELECT c1 FROM t3 WHERE c1 >= 'c-1004=w' and c1 <= 'c-1006=w' and i1 > 2;
c1
EXPLAIN
SELECT c1 FROM t3 WHERE c1 >= 'c-1004=w' and c1 <= 'c-1006=w' or i1 > 2;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t3 ALL c1 NULL NULL NULL 100 Using where
SELECT c1 FROM t3 WHERE c1 >= 'c-1004=w' and c1 <= 'c-1006=w' or i1 > 2;
c1
c-1004=w
c-1005=w
c-1006=w
DROP TABLE t1, t2, t3;
#
# Bug#40992 - InnoDB: Crash when engine_condition_pushdown is on # Bug#40992 - InnoDB: Crash when engine_condition_pushdown is on
# #
CREATE TABLE t ( CREATE TABLE t (
...@@ -203,6 +347,98 @@ COUNT(*) ...@@ -203,6 +347,98 @@ COUNT(*)
DROP PROCEDURE insert_data; DROP PROCEDURE insert_data;
DROP TABLE t1, t2, t3; DROP TABLE t1, t2, t3;
# #
# Bug#57372 "Multi-table updates and deletes fail when running with ICP
# against InnoDB"
#
CREATE TABLE t1 (
a INT KEY,
b INT
);
CREATE TABLE t2 (
a INT KEY,
b INT
);
INSERT INTO t1 VALUES (1, 101), (2, 102), (3, 103), (4, 104), (5, 105);
INSERT INTO t2 VALUES (1, 1), (2, 2), (3, 3), (4, 4), (5, 5);
UPDATE t1, t2
SET t1.a = t1.a + 100, t2.b = t1.a + 10
WHERE t1.a BETWEEN 2 AND 4 AND t2.a = t1.b - 100;
SELECT * FROM t1;
a b
1 101
102 102
103 103
104 104
5 105
SELECT * FROM t2;
a b
1 1
2 12
3 13
4 14
5 5
DROP TABLE t1, t2;
#
# Bug#52605 - "Adding LIMIT 1 clause to query with complex range
# predicate causes wrong results"
#
CREATE TABLE t1 (
pk INT NOT NULL,
c1 INT,
PRIMARY KEY (pk),
KEY k1 (c1)
);
INSERT INTO t1 VALUES (1,NULL);
INSERT INTO t1 VALUES (2,6);
INSERT INTO t1 VALUES (3,NULL);
INSERT INTO t1 VALUES (4,6);
INSERT INTO t1 VALUES (5,NULL);
INSERT INTO t1 VALUES (6,NULL);
INSERT INTO t1 VALUES (7,9);
INSERT INTO t1 VALUES (8,0);
SELECT pk, c1
FROM t1
WHERE (pk BETWEEN 4 AND 5 OR pk < 2) AND c1 < 240
ORDER BY c1
LIMIT 1;
pk c1
4 6
EXPLAIN SELECT pk, c1
FROM t1
WHERE (pk BETWEEN 4 AND 5 OR pk < 2) AND c1 < 240
ORDER BY c1
LIMIT 1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 range PRIMARY,k1 k1 5 NULL 3 Using where; Using index
DROP TABLE t1;
#
# Bug#59259 "Incorrect rows returned for a correlated subquery
# when ICP is on"
#
CREATE TABLE t1 (pk INTEGER PRIMARY KEY, i INTEGER NOT NULL) ENGINE=InnoDB;
INSERT INTO t1 VALUES (11,0);
INSERT INTO t1 VALUES (12,5);
INSERT INTO t1 VALUES (15,0);
CREATE TABLE t2 (pk INTEGER PRIMARY KEY, i INTEGER NOT NULL) ENGINE=InnoDB;
INSERT INTO t2 VALUES (11,1);
INSERT INTO t2 VALUES (12,2);
INSERT INTO t2 VALUES (15,4);
set @save_optimizer_switch= @@optimizer_switch;
set optimizer_switch='semijoin=off';
EXPLAIN
SELECT * FROM t1
WHERE pk IN (SELECT it.pk FROM t2 JOIN t2 AS it ON it.i=it.i WHERE it.pk-t1.i<10);
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 3 Using where
2 DEPENDENT SUBQUERY t2 index NULL PRIMARY 4 NULL 3 Using index
2 DEPENDENT SUBQUERY it eq_ref PRIMARY PRIMARY 4 func 1 Using index condition
SELECT * FROM t1
WHERE pk IN (SELECT it.pk FROM t2 JOIN t2 AS it ON it.i=it.i WHERE it.pk-t1.i<10);
pk i
12 5
set optimizer_switch=@save_optimizer_switch;
DROP TABLE t1, t2;
#
# BUG#778434 Wrong result with in_to_exists=on in maria-5.3-mwl89 # BUG#778434 Wrong result with in_to_exists=on in maria-5.3-mwl89
# #
CREATE TABLE t1 ( f11 int) ; CREATE TABLE t1 ( f11 int) ;
......
...@@ -728,6 +728,7 @@ JA USA ...@@ -728,6 +728,7 @@ JA USA
DROP TABLE t1,t2; DROP TABLE t1,t2;
# #
# Testcase backport: Bug#43249 # Testcase backport: Bug#43249
# (Note: Fixed by patch for BUG#42580)
# #
CREATE TABLE t1(c1 TIME NOT NULL, c2 TIME NULL, c3 DATE, PRIMARY KEY(c1), UNIQUE INDEX(c2)) engine=innodb; CREATE TABLE t1(c1 TIME NOT NULL, c2 TIME NULL, c3 DATE, PRIMARY KEY(c1), UNIQUE INDEX(c2)) engine=innodb;
INSERT INTO t1 VALUES('8:29:45',NULL,'2009-02-01'); INSERT INTO t1 VALUES('8:29:45',NULL,'2009-02-01');
......
...@@ -786,7 +786,7 @@ create table t1 (a int primary key,b int, c int, d int, e int, f int, g int, h ...@@ -786,7 +786,7 @@ create table t1 (a int primary key,b int, c int, d int, e int, f int, g int, h
insert into t1 values (1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1); insert into t1 values (1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1);
explain select * from t1 where a > 0 and a < 50; explain select * from t1 where a > 0 and a < 50;
id select_type table type possible_keys key key_len ref rows Extra id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL # Using where 1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL # Using index condition
drop table t1; drop table t1;
create table t1 (id int NOT NULL,id2 int NOT NULL,id3 int NOT NULL,dummy1 char(30),primary key (id,id2),index index_id3 (id3)) engine=innodb; create table t1 (id int NOT NULL,id2 int NOT NULL,id3 int NOT NULL,dummy1 char(30),primary key (id,id2),index index_id3 (id3)) engine=innodb;
insert into t1 values (0,0,0,'ABCDEFGHIJ'),(2,2,2,'BCDEFGHIJK'),(1,1,1,'CDEFGHIJKL'); insert into t1 values (0,0,0,'ABCDEFGHIJ'),(2,2,2,'BCDEFGHIJK'),(1,1,1,'CDEFGHIJKL');
......
...@@ -83,6 +83,150 @@ c1 c2 c3 c4 ...@@ -83,6 +83,150 @@ c1 c2 c3 c4
DROP TABLE t1; DROP TABLE t1;
# #
# Bug#43617 - Innodb returns wrong results with timestamp's range value
# in IN clause
# (Note: Fixed by patch for BUG#42580)
#
CREATE TABLE t1(
c1 TIMESTAMP NOT NULL,
c2 TIMESTAMP NULL,
c3 DATE,
c4 DATETIME,
PRIMARY KEY(c1),
UNIQUE INDEX(c2)
);
INSERT INTO t1 VALUES
('0000-00-00 00:00:00','0000-00-00 00:00:00','2008-01-04','2008-01-05 00:00:00'),
('1971-01-01 00:00:01','1980-01-01 00:00:01','2009-01-01','2009-01-02 00:00:00'),
('1999-01-01 00:00:00','1999-01-01 00:00:00', NULL, NULL),
('2007-05-23 09:15:28','2007-05-23 09:15:28','2007-05-24','2007-05-24 09:15:28'),
('2007-05-27 00:00:00','2007-05-25 00:00:00','2007-05-26','2007-05-26 00:00:00'),
('2008-01-01 00:00:00', NULL, '2008-01-02','2008-01-03 00:00:00'),
('2009-01-29 11:11:27','2009-01-29 11:11:27','2009-01-29','2009-01-29 11:11:27'),
('2038-01-09 03:14:07','2038-01-09 03:14:07','2009-01-05','2009-01-06 00:00:00');
SELECT *
FROM t1
WHERE c2 IN ('1971-01-01 00:00:01','2038-01-09 03:14:07')
ORDER BY c2;
c1 c2 c3 c4
2038-01-09 03:14:07 2038-01-09 03:14:07 2009-01-05 2009-01-06 00:00:00
SELECT *
FROM t1
WHERE c2 IN ('1971-01-01 00:00:01','2038-01-09 03:14:07')
ORDER BY c2 LIMIT 2;
c1 c2 c3 c4
2038-01-09 03:14:07 2038-01-09 03:14:07 2009-01-05 2009-01-06 00:00:00
SELECT *
FROM t1
WHERE c2 IN ('1971-01-01 00:00:01','2038-01-09 03:14:07')
ORDER BY c2 DESC;
c1 c2 c3 c4
2038-01-09 03:14:07 2038-01-09 03:14:07 2009-01-05 2009-01-06 00:00:00
SELECT *
FROM t1
WHERE c2 IN ('1971-01-01 00:00:01','2038-01-09 03:14:07')
ORDER BY c2 DESC LIMIT 2;
c1 c2 c3 c4
2038-01-09 03:14:07 2038-01-09 03:14:07 2009-01-05 2009-01-06 00:00:00
DROP TABLE t1;
#
# BUG#43618: MyISAM&Maria returns wrong results with 'between'
# on timestamp
#
CREATE TABLE t1(
ts TIMESTAMP NOT NULL,
c char NULL,
PRIMARY KEY(ts)
);
INSERT INTO t1 VALUES
('1971-01-01','a'),
('2007-05-25','b'),
('2008-01-01','c'),
('2038-01-09','d');
# Execute select with invalid timestamp, desc ordering
SELECT *
FROM t1
WHERE ts BETWEEN '0000-00-00' AND '2010-00-01 00:00:00'
ORDER BY ts DESC
LIMIT 2;
ts c
2008-01-01 00:00:00 c
2007-05-25 00:00:00 b
# Should use index condition
EXPLAIN
SELECT *
FROM t1
WHERE ts BETWEEN '0000-00-00' AND '2010-00-01 00:00:00'
ORDER BY ts DESC
LIMIT 2;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 4 Using index condition
DROP TABLE t1;
#
# BUG#49906: Assertion failed - Field_varstring::val_str in field.cc
# (Note: Fixed by patch for LP BUG#625841)
#
CREATE TABLE t1 (
f1 VARCHAR(1024),
f2 VARCHAR(10),
INDEX test_idx USING BTREE (f2,f1(5))
);
INSERT INTO t1 VALUES ('a','c'), ('b','d');
SELECT f1
FROM t1
WHERE f2 LIKE 'd'
ORDER BY f1;
f1
b
DROP TABLE t1;
#
# Bug#52660 - "Perf. regr. using ICP for MyISAM on range queries on
# an index containing TEXT"
#
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
CREATE TABLE t2 (a INT);
INSERT INTO t2 SELECT A.a + 10*(B.a) FROM t1 A, t1 B;
CREATE TABLE t3 (
c1 TINYTEXT NOT NULL,
i1 INT NOT NULL,
KEY (c1(6),i1)
);
INSERT INTO t3 SELECT CONCAT('c-',1000+t2.a,'=w'), 1 FROM t2;
EXPLAIN
SELECT c1 FROM t3 WHERE c1 >= 'c-1004=w' and c1 <= 'c-1006=w';
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t3 range c1 c1 8 NULL 3 Using where
SELECT c1 FROM t3 WHERE c1 >= 'c-1004=w' and c1 <= 'c-1006=w';
c1
c-1004=w
c-1005=w
c-1006=w
EXPLAIN
SELECT c1 FROM t3 WHERE c1 >= 'c-1004=w' and c1 <= 'c-1006=w' and i1 > 2;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t3 range c1 c1 12 NULL 2 Using index condition; Using where
SELECT c1 FROM t3 WHERE c1 >= 'c-1004=w' and c1 <= 'c-1006=w' and i1 > 2;
c1
EXPLAIN
SELECT c1 FROM t3 WHERE c1 >= 'c-1004=w' and c1 <= 'c-1006=w' or i1 > 2;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t3 ALL c1 NULL NULL NULL 100 Using where
SELECT c1 FROM t3 WHERE c1 >= 'c-1004=w' and c1 <= 'c-1006=w' or i1 > 2;
c1
c-1004=w
c-1005=w
c-1006=w
DROP TABLE t1, t2, t3;
#
# Bug#40992 - InnoDB: Crash when engine_condition_pushdown is on # Bug#40992 - InnoDB: Crash when engine_condition_pushdown is on
# #
CREATE TABLE t ( CREATE TABLE t (
...@@ -203,6 +347,104 @@ COUNT(*) ...@@ -203,6 +347,104 @@ COUNT(*)
DROP PROCEDURE insert_data; DROP PROCEDURE insert_data;
DROP TABLE t1, t2, t3; DROP TABLE t1, t2, t3;
# #
# Bug#57372 "Multi-table updates and deletes fail when running with ICP
# against InnoDB"
#
CREATE TABLE t1 (
a INT KEY,
b INT
);
CREATE TABLE t2 (
a INT KEY,
b INT
);
INSERT INTO t1 VALUES (1, 101), (2, 102), (3, 103), (4, 104), (5, 105);
INSERT INTO t2 VALUES (1, 1), (2, 2), (3, 3), (4, 4), (5, 5);
UPDATE t1, t2
SET t1.a = t1.a + 100, t2.b = t1.a + 10
WHERE t1.a BETWEEN 2 AND 4 AND t2.a = t1.b - 100;
SELECT * FROM t1;
a b
1 101
102 102
103 103
104 104
5 105
SELECT * FROM t2;
a b
1 1
2 12
3 13
4 14
5 5
DROP TABLE t1, t2;
#
# Bug#52605 - "Adding LIMIT 1 clause to query with complex range
# predicate causes wrong results"
#
CREATE TABLE t1 (
pk INT NOT NULL,
c1 INT,
PRIMARY KEY (pk),
KEY k1 (c1)
);
INSERT INTO t1 VALUES (1,NULL);
INSERT INTO t1 VALUES (2,6);
INSERT INTO t1 VALUES (3,NULL);
INSERT INTO t1 VALUES (4,6);
INSERT INTO t1 VALUES (5,NULL);
INSERT INTO t1 VALUES (6,NULL);
INSERT INTO t1 VALUES (7,9);
INSERT INTO t1 VALUES (8,0);
SELECT pk, c1
FROM t1
WHERE (pk BETWEEN 4 AND 5 OR pk < 2) AND c1 < 240
ORDER BY c1
LIMIT 1;
pk c1
4 6
EXPLAIN SELECT pk, c1
FROM t1
WHERE (pk BETWEEN 4 AND 5 OR pk < 2) AND c1 < 240
ORDER BY c1
LIMIT 1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 range PRIMARY,k1 k1 5 NULL 4 Using where
DROP TABLE t1;
#
# Bug#59259 "Incorrect rows returned for a correlated subquery
# when ICP is on"
#
CREATE TABLE t1 (pk INTEGER PRIMARY KEY, i INTEGER NOT NULL) ENGINE=InnoDB;
Warnings:
Warning 1286 Unknown table engine 'InnoDB'
Warning 1266 Using storage engine Aria for table 't1'
INSERT INTO t1 VALUES (11,0);
INSERT INTO t1 VALUES (12,5);
INSERT INTO t1 VALUES (15,0);
CREATE TABLE t2 (pk INTEGER PRIMARY KEY, i INTEGER NOT NULL) ENGINE=InnoDB;
Warnings:
Warning 1286 Unknown table engine 'InnoDB'
Warning 1266 Using storage engine Aria for table 't2'
INSERT INTO t2 VALUES (11,1);
INSERT INTO t2 VALUES (12,2);
INSERT INTO t2 VALUES (15,4);
set @save_optimizer_switch= @@optimizer_switch;
set optimizer_switch='semijoin=off';
EXPLAIN
SELECT * FROM t1
WHERE pk IN (SELECT it.pk FROM t2 JOIN t2 AS it ON it.i=it.i WHERE it.pk-t1.i<10);
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 3 Using where
2 DEPENDENT SUBQUERY t2 index NULL PRIMARY 4 NULL 3 Using index
2 DEPENDENT SUBQUERY it eq_ref PRIMARY PRIMARY 4 func 1 Using index condition
SELECT * FROM t1
WHERE pk IN (SELECT it.pk FROM t2 JOIN t2 AS it ON it.i=it.i WHERE it.pk-t1.i<10);
pk i
12 5
set optimizer_switch=@save_optimizer_switch;
DROP TABLE t1, t2;
#
# BUG#778434 Wrong result with in_to_exists=on in maria-5.3-mwl89 # BUG#778434 Wrong result with in_to_exists=on in maria-5.3-mwl89
# #
CREATE TABLE t1 ( f11 int) ; CREATE TABLE t1 ( f11 int) ;
......
...@@ -105,7 +105,7 @@ id select_type table type possible_keys key key_len ref rows Extra ...@@ -105,7 +105,7 @@ id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 range FieldKey FieldKey 38 NULL 4 Using index condition; Rowid-ordered scan; Using filesort 1 SIMPLE t1 range FieldKey FieldKey 38 NULL 4 Using index condition; Rowid-ordered scan; Using filesort
EXPLAIN SELECT * FROM t1 IGNORE INDEX (FieldKey, LongField) WHERE FieldKey > '2' ORDER BY LongVal; EXPLAIN SELECT * FROM t1 IGNORE INDEX (FieldKey, LongField) WHERE FieldKey > '2' ORDER BY LongVal;
id select_type table type possible_keys key key_len ref rows Extra id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 range StringField StringField 38 NULL 4 Using where; Using filesort 1 SIMPLE t1 range StringField StringField 38 NULL 4 Using index condition; Using filesort
SELECT * FROM t1 WHERE FieldKey > '2' ORDER BY LongVal; SELECT * FROM t1 WHERE FieldKey > '2' ORDER BY LongVal;
FieldKey LongVal StringVal FieldKey LongVal StringVal
3 1 2 3 1 2
......
...@@ -79,6 +79,150 @@ c1 c2 c3 c4 ...@@ -79,6 +79,150 @@ c1 c2 c3 c4
DROP TABLE t1; DROP TABLE t1;
# #
# Bug#43617 - Innodb returns wrong results with timestamp's range value
# in IN clause
# (Note: Fixed by patch for BUG#42580)
#
CREATE TABLE t1(
c1 TIMESTAMP NOT NULL,
c2 TIMESTAMP NULL,
c3 DATE,
c4 DATETIME,
PRIMARY KEY(c1),
UNIQUE INDEX(c2)
);
INSERT INTO t1 VALUES
('0000-00-00 00:00:00','0000-00-00 00:00:00','2008-01-04','2008-01-05 00:00:00'),
('1971-01-01 00:00:01','1980-01-01 00:00:01','2009-01-01','2009-01-02 00:00:00'),
('1999-01-01 00:00:00','1999-01-01 00:00:00', NULL, NULL),
('2007-05-23 09:15:28','2007-05-23 09:15:28','2007-05-24','2007-05-24 09:15:28'),
('2007-05-27 00:00:00','2007-05-25 00:00:00','2007-05-26','2007-05-26 00:00:00'),
('2008-01-01 00:00:00', NULL, '2008-01-02','2008-01-03 00:00:00'),
('2009-01-29 11:11:27','2009-01-29 11:11:27','2009-01-29','2009-01-29 11:11:27'),
('2038-01-09 03:14:07','2038-01-09 03:14:07','2009-01-05','2009-01-06 00:00:00');
SELECT *
FROM t1
WHERE c2 IN ('1971-01-01 00:00:01','2038-01-09 03:14:07')
ORDER BY c2;
c1 c2 c3 c4
2038-01-09 03:14:07 2038-01-09 03:14:07 2009-01-05 2009-01-06 00:00:00
SELECT *
FROM t1
WHERE c2 IN ('1971-01-01 00:00:01','2038-01-09 03:14:07')
ORDER BY c2 LIMIT 2;
c1 c2 c3 c4
2038-01-09 03:14:07 2038-01-09 03:14:07 2009-01-05 2009-01-06 00:00:00
SELECT *
FROM t1
WHERE c2 IN ('1971-01-01 00:00:01','2038-01-09 03:14:07')
ORDER BY c2 DESC;
c1 c2 c3 c4
2038-01-09 03:14:07 2038-01-09 03:14:07 2009-01-05 2009-01-06 00:00:00
SELECT *
FROM t1
WHERE c2 IN ('1971-01-01 00:00:01','2038-01-09 03:14:07')
ORDER BY c2 DESC LIMIT 2;
c1 c2 c3 c4
2038-01-09 03:14:07 2038-01-09 03:14:07 2009-01-05 2009-01-06 00:00:00
DROP TABLE t1;
#
# BUG#43618: MyISAM&Maria returns wrong results with 'between'
# on timestamp
#
CREATE TABLE t1(
ts TIMESTAMP NOT NULL,
c char NULL,
PRIMARY KEY(ts)
);
INSERT INTO t1 VALUES
('1971-01-01','a'),
('2007-05-25','b'),
('2008-01-01','c'),
('2038-01-09','d');
# Execute select with invalid timestamp, desc ordering
SELECT *
FROM t1
WHERE ts BETWEEN '0000-00-00' AND '2010-00-01 00:00:00'
ORDER BY ts DESC
LIMIT 2;
ts c
2008-01-01 00:00:00 c
2007-05-25 00:00:00 b
# Should use index condition
EXPLAIN
SELECT *
FROM t1
WHERE ts BETWEEN '0000-00-00' AND '2010-00-01 00:00:00'
ORDER BY ts DESC
LIMIT 2;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 4 Using where
DROP TABLE t1;
#
# BUG#49906: Assertion failed - Field_varstring::val_str in field.cc
# (Note: Fixed by patch for LP BUG#625841)
#
CREATE TABLE t1 (
f1 VARCHAR(1024),
f2 VARCHAR(10),
INDEX test_idx USING BTREE (f2,f1(5))
);
INSERT INTO t1 VALUES ('a','c'), ('b','d');
SELECT f1
FROM t1
WHERE f2 LIKE 'd'
ORDER BY f1;
f1
b
DROP TABLE t1;
#
# Bug#52660 - "Perf. regr. using ICP for MyISAM on range queries on
# an index containing TEXT"
#
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
CREATE TABLE t2 (a INT);
INSERT INTO t2 SELECT A.a + 10*(B.a) FROM t1 A, t1 B;
CREATE TABLE t3 (
c1 TINYTEXT NOT NULL,
i1 INT NOT NULL,
KEY (c1(6),i1)
);
INSERT INTO t3 SELECT CONCAT('c-',1000+t2.a,'=w'), 1 FROM t2;
EXPLAIN
SELECT c1 FROM t3 WHERE c1 >= 'c-1004=w' and c1 <= 'c-1006=w';
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t3 range c1 c1 8 NULL 3 Using where
SELECT c1 FROM t3 WHERE c1 >= 'c-1004=w' and c1 <= 'c-1006=w';
c1
c-1004=w
c-1005=w
c-1006=w
EXPLAIN
SELECT c1 FROM t3 WHERE c1 >= 'c-1004=w' and c1 <= 'c-1006=w' and i1 > 2;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t3 range c1 c1 12 NULL 2 Using where
SELECT c1 FROM t3 WHERE c1 >= 'c-1004=w' and c1 <= 'c-1006=w' and i1 > 2;
c1
EXPLAIN
SELECT c1 FROM t3 WHERE c1 >= 'c-1004=w' and c1 <= 'c-1006=w' or i1 > 2;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t3 ALL c1 NULL NULL NULL 100 Using where
SELECT c1 FROM t3 WHERE c1 >= 'c-1004=w' and c1 <= 'c-1006=w' or i1 > 2;
c1
c-1004=w
c-1005=w
c-1006=w
DROP TABLE t1, t2, t3;
#
# Bug#40992 - InnoDB: Crash when engine_condition_pushdown is on # Bug#40992 - InnoDB: Crash when engine_condition_pushdown is on
# #
CREATE TABLE t ( CREATE TABLE t (
...@@ -199,6 +343,104 @@ COUNT(*) ...@@ -199,6 +343,104 @@ COUNT(*)
DROP PROCEDURE insert_data; DROP PROCEDURE insert_data;
DROP TABLE t1, t2, t3; DROP TABLE t1, t2, t3;
# #
# Bug#57372 "Multi-table updates and deletes fail when running with ICP
# against InnoDB"
#
CREATE TABLE t1 (
a INT KEY,
b INT
);
CREATE TABLE t2 (
a INT KEY,
b INT
);
INSERT INTO t1 VALUES (1, 101), (2, 102), (3, 103), (4, 104), (5, 105);
INSERT INTO t2 VALUES (1, 1), (2, 2), (3, 3), (4, 4), (5, 5);
UPDATE t1, t2
SET t1.a = t1.a + 100, t2.b = t1.a + 10
WHERE t1.a BETWEEN 2 AND 4 AND t2.a = t1.b - 100;
SELECT * FROM t1;
a b
1 101
102 102
103 103
104 104
5 105
SELECT * FROM t2;
a b
1 1
2 12
3 13
4 14
5 5
DROP TABLE t1, t2;
#
# Bug#52605 - "Adding LIMIT 1 clause to query with complex range
# predicate causes wrong results"
#
CREATE TABLE t1 (
pk INT NOT NULL,
c1 INT,
PRIMARY KEY (pk),
KEY k1 (c1)
);
INSERT INTO t1 VALUES (1,NULL);
INSERT INTO t1 VALUES (2,6);
INSERT INTO t1 VALUES (3,NULL);
INSERT INTO t1 VALUES (4,6);
INSERT INTO t1 VALUES (5,NULL);
INSERT INTO t1 VALUES (6,NULL);
INSERT INTO t1 VALUES (7,9);
INSERT INTO t1 VALUES (8,0);
SELECT pk, c1
FROM t1
WHERE (pk BETWEEN 4 AND 5 OR pk < 2) AND c1 < 240
ORDER BY c1
LIMIT 1;
pk c1
4 6
EXPLAIN SELECT pk, c1
FROM t1
WHERE (pk BETWEEN 4 AND 5 OR pk < 2) AND c1 < 240
ORDER BY c1
LIMIT 1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 range PRIMARY,k1 k1 5 NULL 4 Using where
DROP TABLE t1;
#
# Bug#59259 "Incorrect rows returned for a correlated subquery
# when ICP is on"
#
CREATE TABLE t1 (pk INTEGER PRIMARY KEY, i INTEGER NOT NULL) ENGINE=InnoDB;
Warnings:
Warning 1286 Unknown table engine 'InnoDB'
Warning 1266 Using storage engine MyISAM for table 't1'
INSERT INTO t1 VALUES (11,0);
INSERT INTO t1 VALUES (12,5);
INSERT INTO t1 VALUES (15,0);
CREATE TABLE t2 (pk INTEGER PRIMARY KEY, i INTEGER NOT NULL) ENGINE=InnoDB;
Warnings:
Warning 1286 Unknown table engine 'InnoDB'
Warning 1266 Using storage engine MyISAM for table 't2'
INSERT INTO t2 VALUES (11,1);
INSERT INTO t2 VALUES (12,2);
INSERT INTO t2 VALUES (15,4);
set @save_optimizer_switch= @@optimizer_switch;
set optimizer_switch='semijoin=off';
EXPLAIN
SELECT * FROM t1
WHERE pk IN (SELECT it.pk FROM t2 JOIN t2 AS it ON it.i=it.i WHERE it.pk-t1.i<10);
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 3 Using where
2 DEPENDENT SUBQUERY t2 index NULL PRIMARY 4 NULL 3 Using index
2 DEPENDENT SUBQUERY it eq_ref PRIMARY PRIMARY 4 func 1 Using where
SELECT * FROM t1
WHERE pk IN (SELECT it.pk FROM t2 JOIN t2 AS it ON it.i=it.i WHERE it.pk-t1.i<10);
pk i
12 5
set optimizer_switch=@save_optimizer_switch;
DROP TABLE t1, t2;
#
# BUG#778434 Wrong result with in_to_exists=on in maria-5.3-mwl89 # BUG#778434 Wrong result with in_to_exists=on in maria-5.3-mwl89
# #
CREATE TABLE t1 ( f11 int) ; CREATE TABLE t1 ( f11 int) ;
......
...@@ -365,7 +365,7 @@ update t1 set b=repeat(char(65+a), 20) where a < 25; ...@@ -365,7 +365,7 @@ update t1 set b=repeat(char(65+a), 20) where a < 25;
This must show range + using index condition: This must show range + using index condition:
explain select * from t1 where a < 10 and b = repeat(char(65+a), 20); explain select * from t1 where a < 10 and b = repeat(char(65+a), 20);
id select_type table type possible_keys key key_len ref rows Extra id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 range a a 5 NULL 19 Using where 1 SIMPLE t1 range a a 5 NULL 19 Using index condition; Using where
select * from t1 where a < 10 and b = repeat(char(65+a), 20); select * from t1 where a < 10 and b = repeat(char(65+a), 20);
a b filler a b filler
0 AAAAAAAAAAAAAAAAAAAA filler 0 AAAAAAAAAAAAAAAAAAAA filler
......
...@@ -420,6 +420,7 @@ DROP TABLE t1,t2; ...@@ -420,6 +420,7 @@ DROP TABLE t1,t2;
--echo # --echo #
--echo # Testcase backport: Bug#43249 --echo # Testcase backport: Bug#43249
--echo # (Note: Fixed by patch for BUG#42580)
--echo # --echo #
CREATE TABLE t1(c1 TIME NOT NULL, c2 TIME NULL, c3 DATE, PRIMARY KEY(c1), UNIQUE INDEX(c2)) engine=innodb; CREATE TABLE t1(c1 TIME NOT NULL, c2 TIME NULL, c3 DATE, PRIMARY KEY(c1), UNIQUE INDEX(c2)) engine=innodb;
INSERT INTO t1 VALUES('8:29:45',NULL,'2009-02-01'); INSERT INTO t1 VALUES('8:29:45',NULL,'2009-02-01');
......
...@@ -546,12 +546,11 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, ...@@ -546,12 +546,11 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
/* Temporary set for register_used_fields and register_field_in_read_map */ /* Temporary set for register_used_fields and register_field_in_read_map */
sort_form->read_set= &sort_form->tmp_set; sort_form->read_set= &sort_form->tmp_set;
register_used_fields(param); register_used_fields(param);
if (select && select->cond) Item *sort_cond= !select ?
select->cond->walk(&Item::register_field_in_read_map, 1, 0 : !select->pre_idx_push_select_cond ?
(uchar*) sort_form); select->cond : select->pre_idx_push_select_cond;
if (select && select->pre_idx_push_select_cond) if (sort_cond)
select->pre_idx_push_select_cond->walk(&Item::register_field_in_read_map, sort_cond->walk(&Item::register_field_in_read_map, 1, (uchar*) sort_form);
1, (uchar*) sort_form);
sort_form->column_bitmaps_set(&sort_form->tmp_set, &sort_form->tmp_set, sort_form->column_bitmaps_set(&sort_form->tmp_set, &sort_form->tmp_set,
&sort_form->tmp_set); &sort_form->tmp_set);
......
...@@ -1723,6 +1723,7 @@ class handler :public Sql_alloc ...@@ -1723,6 +1723,7 @@ class handler :public Sql_alloc
DBUG_ENTER("ha_rnd_init"); DBUG_ENTER("ha_rnd_init");
DBUG_ASSERT(inited==NONE || (inited==RND && scan)); DBUG_ASSERT(inited==NONE || (inited==RND && scan));
inited= (result= rnd_init(scan)) ? NONE: RND; inited= (result= rnd_init(scan)) ? NONE: RND;
end_range= NULL;
DBUG_RETURN(result); DBUG_RETURN(result);
} }
int ha_rnd_end() int ha_rnd_end()
...@@ -1730,6 +1731,7 @@ class handler :public Sql_alloc ...@@ -1730,6 +1731,7 @@ class handler :public Sql_alloc
DBUG_ENTER("ha_rnd_end"); DBUG_ENTER("ha_rnd_end");
DBUG_ASSERT(inited==RND); DBUG_ASSERT(inited==RND);
inited=NONE; inited=NONE;
end_range= NULL;
DBUG_RETURN(rnd_end()); DBUG_RETURN(rnd_end());
} }
int ha_rnd_init_with_error(bool scan) __attribute__ ((warn_unused_result)); int ha_rnd_init_with_error(bool scan) __attribute__ ((warn_unused_result));
......
...@@ -83,15 +83,44 @@ bool uses_index_fields_only(Item *item, TABLE *tbl, uint keyno, ...@@ -83,15 +83,44 @@ bool uses_index_fields_only(Item *item, TABLE *tbl, uint keyno,
case Item::FIELD_ITEM: case Item::FIELD_ITEM:
{ {
Item_field *item_field= (Item_field*)item; Item_field *item_field= (Item_field*)item;
if (item_field->field->table != tbl) Field *field= item_field->field;
if (field->table != tbl)
return TRUE; return TRUE;
/* /*
The below is probably a repetition - the first part checks the The below is probably a repetition - the first part checks the
other two, but let's play it safe: other two, but let's play it safe:
*/ */
return item_field->field->part_of_key.is_set(keyno) && if(!field->part_of_key.is_set(keyno) ||
item_field->field->type() != MYSQL_TYPE_GEOMETRY && field->type() == MYSQL_TYPE_GEOMETRY ||
item_field->field->type() != MYSQL_TYPE_BLOB; field->type() == MYSQL_TYPE_BLOB)
return FALSE;
KEY *key_info= tbl->key_info + keyno;
KEY_PART_INFO *key_part= key_info->key_part;
KEY_PART_INFO *key_part_end= key_part + key_info->key_parts;
for ( ; key_part < key_part_end; key_part++)
{
if (field->eq(key_part->field))
return !(key_part->key_part_flag & HA_PART_KEY_SEG);
}
if ((tbl->file->ha_table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX) &&
tbl->s->primary_key != MAX_KEY &&
tbl->s->primary_key != keyno)
{
key_info= tbl->key_info + tbl->s->primary_key;
key_part= key_info->key_part;
key_part_end= key_part + key_info->key_parts;
for ( ; key_part < key_part_end; key_part++)
{
/*
It does not make sense to use the fact that the engine can read in
a full field if the key if the index is built only over a part
of this field.
*/
if (field->eq(key_part->field))
return !(key_part->key_part_flag & HA_PART_KEY_SEG);
}
}
return FALSE;
} }
case Item::REF_ITEM: case Item::REF_ITEM:
return uses_index_fields_only(item->real_item(), tbl, keyno, return uses_index_fields_only(item->real_item(), tbl, keyno,
...@@ -288,30 +317,12 @@ void push_index_cond(JOIN_TAB *tab, uint keyno) ...@@ -288,30 +317,12 @@ void push_index_cond(JOIN_TAB *tab, uint keyno)
{ {
DBUG_ENTER("push_index_cond"); DBUG_ENTER("push_index_cond");
Item *idx_cond; Item *idx_cond;
bool do_index_cond_pushdown=
((tab->table->file->index_flags(keyno, 0, 1) &
HA_DO_INDEX_COND_PUSHDOWN) &&
optimizer_flag(tab->join->thd, OPTIMIZER_SWITCH_INDEX_COND_PUSHDOWN));
/*
Do not try index condition pushdown on indexes which have partially-covered
columns. Unpacking from a column prefix into index tuple is not a supported
operation in some engines, see e.g. MySQL BUG#42991.
TODO: a better solution would be not to consider partially-covered columns
as parts of the index and still produce/check index condition for
fully-covered index columns.
*/
KEY *key_info= tab->table->key_info + keyno;
for (uint kp= 0; kp < key_info->key_parts; kp++)
{
if ((key_info->key_part[kp].key_part_flag & HA_PART_KEY_SEG))
{
do_index_cond_pushdown= FALSE;
break;
}
}
if (do_index_cond_pushdown) if ((tab->table->file->index_flags(keyno, 0, 1) &
HA_DO_INDEX_COND_PUSHDOWN) &&
optimizer_flag(tab->join->thd, OPTIMIZER_SWITCH_INDEX_COND_PUSHDOWN) &&
tab->join->thd->lex->sql_command != SQLCOM_UPDATE_MULTI &&
tab->join->thd->lex->sql_command != SQLCOM_DELETE_MULTI)
{ {
DBUG_EXECUTE("where", DBUG_EXECUTE("where",
print_where(tab->select_cond, "full cond", QT_ORDINARY);); print_where(tab->select_cond, "full cond", QT_ORDINARY););
......
...@@ -472,10 +472,12 @@ Looks for column n in an index. ...@@ -472,10 +472,12 @@ Looks for column n in an index.
ULINT_UNDEFINED if not contained */ ULINT_UNDEFINED if not contained */
UNIV_INTERN UNIV_INTERN
ulint ulint
dict_index_get_nth_col_pos( dict_index_get_nth_col_or_prefix_pos(
/*=======================*/ /*=================================*/
const dict_index_t* index, /*!< in: index */ const dict_index_t* index, /*!< in: index */
ulint n) /*!< in: column number */ ulint n, /*!< in: column number */
ibool inc_prefix) /*!< in: TRUE=consider
column prefixes too */
{ {
const dict_field_t* field; const dict_field_t* field;
const dict_col_t* col; const dict_col_t* col;
...@@ -497,7 +499,8 @@ dict_index_get_nth_col_pos( ...@@ -497,7 +499,8 @@ dict_index_get_nth_col_pos(
for (pos = 0; pos < n_fields; pos++) { for (pos = 0; pos < n_fields; pos++) {
field = dict_index_get_nth_field(index, pos); field = dict_index_get_nth_field(index, pos);
if (col == field->col && field->prefix_len == 0) { if (col == field->col
&& (inc_prefix || field->prefix_len == 0)) {
return(pos); return(pos);
} }
......
...@@ -126,11 +126,6 @@ static pthread_cond_t commit_cond; ...@@ -126,11 +126,6 @@ static pthread_cond_t commit_cond;
static pthread_mutex_t commit_cond_m; static pthread_mutex_t commit_cond_m;
static bool innodb_inited = 0; static bool innodb_inited = 0;
C_MODE_START
static xtradb_icp_result_t index_cond_func_innodb(void *arg);
C_MODE_END
#define INSIDE_HA_INNOBASE_CC #define INSIDE_HA_INNOBASE_CC
...@@ -2008,14 +2003,21 @@ trx_is_strict( ...@@ -2008,14 +2003,21 @@ trx_is_strict(
/**************************************************************//** /**************************************************************//**
Resets some fields of a prebuilt struct. The template is used in fast Resets some fields of a prebuilt struct. The template is used in fast
retrieval of just those column values MySQL needs in its processing. */ retrieval of just those column values MySQL needs in its processing. */
static
void void
reset_template( inline
/*===========*/ ha_innobase::reset_template(void)
row_prebuilt_t* prebuilt) /*!< in/out: prebuilt struct */ /*=============================*/
{ {
ut_ad(prebuilt->magic_n == ROW_PREBUILT_ALLOCATED);
ut_ad(prebuilt->magic_n2 == prebuilt->magic_n);
prebuilt->keep_other_fields_on_keyread = 0; prebuilt->keep_other_fields_on_keyread = 0;
prebuilt->read_just_key = 0; prebuilt->read_just_key = 0;
/* Reset index condition pushdown state */
prebuilt->idx_cond = NULL;
prebuilt->idx_cond_n_cols = 0;
pushed_idx_cond = NULL;
pushed_idx_cond_keyno = MAX_KEY;
} }
/*****************************************************************//** /*****************************************************************//**
...@@ -2077,7 +2079,7 @@ ha_innobase::init_table_handle_for_HANDLER(void) ...@@ -2077,7 +2079,7 @@ ha_innobase::init_table_handle_for_HANDLER(void)
we???? */ we???? */
prebuilt->used_in_HANDLER = TRUE; prebuilt->used_in_HANDLER = TRUE;
reset_template(prebuilt); reset_template();
} }
/*********************************************************************//** /*********************************************************************//**
...@@ -4203,8 +4205,8 @@ static inline ...@@ -4203,8 +4205,8 @@ static inline
uint uint
get_field_offset( get_field_offset(
/*=============*/ /*=============*/
TABLE* table, /*!< in: MySQL table object */ const TABLE* table, /*!< in: MySQL table object */
Field* field) /*!< in: MySQL field object */ const Field* field) /*!< in: MySQL field object */
{ {
return((uint) (field->ptr - table->record[0])); return((uint) (field->ptr - table->record[0]));
} }
...@@ -4766,45 +4768,171 @@ ha_innobase::store_key_val_for_row( ...@@ -4766,45 +4768,171 @@ ha_innobase::store_key_val_for_row(
DBUG_RETURN((uint)(buff - buff_start)); DBUG_RETURN((uint)(buff - buff_start));
} }
/**************************************************************//**
Determines if a field is needed in a prebuilt struct 'template'.
@return field to use, or NULL if the field is not needed */
static
const Field*
build_template_needs_field(
/*=======================*/
ibool index_contains, /*!< in:
dict_index_contains_col_or_prefix(
index, i) */
ibool read_just_key, /*!< in: TRUE when MySQL calls
ha_innobase::extra with the
argument HA_EXTRA_KEYREAD; it is enough
to read just columns defined in
the index (i.e., no read of the
clustered index record necessary) */
ibool fetch_all_in_key,
/*!< in: true=fetch all fields in
the index */
ibool fetch_primary_key_cols,
/*!< in: true=fetch the
primary key columns */
dict_index_t* index, /*!< in: InnoDB index to use */
const TABLE* table, /*!< in: MySQL table object */
ulint i, /*!< in: field index in InnoDB table */
ulint sql_idx) /*!< in: field index in SQL table */
{
const Field* field = table->field[sql_idx];
ut_ad(index_contains == dict_index_contains_col_or_prefix(index, i));
if (!index_contains) {
if (read_just_key) {
/* If this is a 'key read', we do not need
columns that are not in the key */
return(NULL);
}
} else if (fetch_all_in_key) {
/* This field is needed in the query */
return(field);
}
if (bitmap_is_set(table->read_set, sql_idx)
|| bitmap_is_set(table->write_set, sql_idx)) {
/* This field is needed in the query */
return(field);
}
if (fetch_primary_key_cols
&& dict_table_col_in_clustered_key(index->table, i)) {
/* This field is needed in the query */
return(field);
}
/* This field is not needed in the query, skip it */
return(NULL);
}
/**************************************************************//**
Adds a field is to a prebuilt struct 'template'.
@return the field template */
static
mysql_row_templ_t*
build_template_field(
/*=================*/
row_prebuilt_t* prebuilt, /*!< in/out: template */
dict_index_t* clust_index, /*!< in: InnoDB clustered index */
dict_index_t* index, /*!< in: InnoDB index to use */
TABLE* table, /*!< in: MySQL table object */
const Field* field, /*!< in: field in MySQL table */
ulint i) /*!< in: field index in InnoDB table */
{
mysql_row_templ_t* templ;
const dict_col_t* col;
ut_ad(field == table->field[i]);
ut_ad(clust_index->table == index->table);
col = dict_table_get_nth_col(index->table, i);
templ = prebuilt->mysql_template + prebuilt->n_template++;
UNIV_MEM_INVALID(templ, sizeof *templ);
templ->col_no = i;
templ->clust_rec_field_no = dict_col_get_clust_pos(col, clust_index);
ut_a(templ->clust_rec_field_no != ULINT_UNDEFINED);
if (dict_index_is_clust(index)) {
templ->rec_field_no = templ->clust_rec_field_no;
} else {
templ->rec_field_no = dict_index_get_nth_col_pos(index, i);
}
if (field->null_ptr) {
templ->mysql_null_byte_offset =
(ulint) ((char*) field->null_ptr
- (char*) table->record[0]);
templ->mysql_null_bit_mask = (ulint) field->null_bit;
} else {
templ->mysql_null_bit_mask = 0;
}
templ->mysql_col_offset = (ulint) get_field_offset(table, field);
templ->mysql_col_len = (ulint) field->pack_length();
templ->type = col->mtype;
templ->mysql_type = (ulint)field->type();
if (templ->mysql_type == DATA_MYSQL_TRUE_VARCHAR) {
templ->mysql_length_bytes = (ulint)
(((Field_varstring*)field)->length_bytes);
}
templ->charset = dtype_get_charset_coll(col->prtype);
templ->mbminlen = col->mbminlen;
templ->mbmaxlen = col->mbmaxlen;
templ->is_unsigned = col->prtype & DATA_UNSIGNED;
if (!dict_index_is_clust(index)
&& templ->rec_field_no == ULINT_UNDEFINED) {
prebuilt->need_to_access_clustered = TRUE;
}
if (prebuilt->mysql_prefix_len < templ->mysql_col_offset
+ templ->mysql_col_len) {
prebuilt->mysql_prefix_len = templ->mysql_col_offset
+ templ->mysql_col_len;
}
if (templ->type == DATA_BLOB) {
prebuilt->templ_contains_blob = TRUE;
}
return(templ);
}
/**************************************************************//** /**************************************************************//**
Builds a 'template' to the prebuilt struct. The template is used in fast Builds a 'template' to the prebuilt struct. The template is used in fast
retrieval of just those column values MySQL needs in its processing. */ retrieval of just those column values MySQL needs in its processing. */
static UNIV_INTERN
void void
build_template( ha_innobase::build_template(
/*===========*/ /*========================*/
row_prebuilt_t* prebuilt, /*!< in/out: prebuilt struct */ bool whole_row) /*!< in: true=ROW_MYSQL_WHOLE_ROW,
THD* thd, /*!< in: current user thread, used false=ROW_MYSQL_REC_FIELDS */
only if templ_type is
ROW_MYSQL_REC_FIELDS */
TABLE* table, /* in: MySQL table */
ha_innobase* file, /* in: ha_innobase handler */
uint templ_type) /* in: ROW_MYSQL_WHOLE_ROW or
ROW_MYSQL_REC_FIELDS */
{ {
dict_index_t* index; dict_index_t* index;
dict_index_t* clust_index; dict_index_t* clust_index;
mysql_row_templ_t* templ;
Field* field;
ulint n_fields, n_stored_fields; ulint n_fields, n_stored_fields;
ulint n_requested_fields = 0;
ibool fetch_all_in_key = FALSE; ibool fetch_all_in_key = FALSE;
ibool fetch_primary_key_cols = FALSE; ibool fetch_primary_key_cols = FALSE;
ulint sql_idx, innodb_idx=0; ulint i, sql_idx;
/* byte offset of the end of last requested column */
ulint mysql_prefix_len = 0;
ibool do_idx_cond_push= FALSE;
ibool need_second_pass= FALSE;
if (prebuilt->select_lock_type == LOCK_X) { if (prebuilt->select_lock_type == LOCK_X) {
/* We always retrieve the whole clustered index record if we /* We always retrieve the whole clustered index record if we
use exclusive row level locks, for example, if the read is use exclusive row level locks, for example, if the read is
done in an UPDATE statement. */ done in an UPDATE statement. */
templ_type = ROW_MYSQL_WHOLE_ROW; whole_row = true;
} } else if (!whole_row) {
if (templ_type == ROW_MYSQL_REC_FIELDS) {
if (prebuilt->hint_need_to_fetch_extra_cols if (prebuilt->hint_need_to_fetch_extra_cols
== ROW_RETRIEVE_ALL_COLS) { == ROW_RETRIEVE_ALL_COLS) {
...@@ -4821,7 +4949,7 @@ build_template( ...@@ -4821,7 +4949,7 @@ build_template(
fetch_all_in_key = TRUE; fetch_all_in_key = TRUE;
} else { } else {
templ_type = ROW_MYSQL_WHOLE_ROW; whole_row = true;
} }
} else if (prebuilt->hint_need_to_fetch_extra_cols } else if (prebuilt->hint_need_to_fetch_extra_cols
== ROW_RETRIEVE_PRIMARY_KEY) { == ROW_RETRIEVE_PRIMARY_KEY) {
...@@ -4838,19 +4966,12 @@ build_template( ...@@ -4838,19 +4966,12 @@ build_template(
clust_index = dict_table_get_first_index(prebuilt->table); clust_index = dict_table_get_first_index(prebuilt->table);
if (templ_type == ROW_MYSQL_REC_FIELDS) { index = whole_row ? clust_index : prebuilt->index;
index = prebuilt->index;
} else {
index = clust_index;
}
if (index == clust_index) { prebuilt->need_to_access_clustered = (index == clust_index);
prebuilt->need_to_access_clustered = TRUE;
} else { /* Below we check column by column if we need to access
prebuilt->need_to_access_clustered = FALSE; the clustered index. */
/* Below we check column by column if we need to access
the clustered index */
}
n_fields = (ulint)table->s->fields; /* number of columns */ n_fields = (ulint)table->s->fields; /* number of columns */
n_stored_fields= (ulint)table->s->stored_fields; /* number of stored columns */ n_stored_fields= (ulint)table->s->stored_fields; /* number of stored columns */
...@@ -4860,160 +4981,206 @@ build_template( ...@@ -4860,160 +4981,206 @@ build_template(
mem_alloc(n_stored_fields * sizeof(mysql_row_templ_t)); mem_alloc(n_stored_fields * sizeof(mysql_row_templ_t));
} }
prebuilt->template_type = templ_type; prebuilt->template_type = whole_row
? ROW_MYSQL_WHOLE_ROW : ROW_MYSQL_REC_FIELDS;
prebuilt->null_bitmap_len = table->s->null_bytes; prebuilt->null_bitmap_len = table->s->null_bytes;
/* Prepare to build prebuilt->mysql_template[]. */
prebuilt->templ_contains_blob = FALSE; prebuilt->templ_contains_blob = FALSE;
prebuilt->mysql_prefix_len = 0;
prebuilt->n_template = 0;
prebuilt->idx_cond_n_cols = 0;
/* Note that in InnoDB, i is the column number in the table.
MySQL calls columns 'fields'. */
if (active_index != MAX_KEY && active_index == pushed_idx_cond_keyno) {
/* Push down an index condition or an end_range check. */
for (i = 0, sql_idx = 0; i < n_stored_fields; i++, sql_idx++) {
while (!table->field[sql_idx]->stored_in_db) {
sql_idx++;
}
const ibool index_contains
= dict_index_contains_col_or_prefix(index, i);
/* Test if an end_range or an index condition
refers to the field. Note that "index" and
"index_contains" may refer to the clustered index.
Index condition pushdown is relative to prebuilt->index
(the index that is being looked up first). */
/* When join_read_always_key() invokes this
code via handler::ha_index_init() and
ha_innobase::index_init(), end_range is not
yet initialized. Because of that, we must
always check for index_contains, instead of
the subset
field->part_of_key.is_set(active_index)
which would be acceptable if end_range==NULL. */
if (index == prebuilt->index
? index_contains
: dict_index_contains_col_or_prefix(
prebuilt->index, i)) {
/* Needed in ICP */
const Field* field;
mysql_row_templ_t* templ;
if (whole_row) {
field = table->field[sql_idx];
} else {
field = build_template_needs_field(
index_contains,
prebuilt->read_just_key,
fetch_all_in_key,
fetch_primary_key_cols,
index, table, i, sql_idx);
if (!field) {
continue;
}
}
templ = build_template_field(
/* prebuilt, clust_index, index,
Setup index condition pushdown (note: we don't need to check if table, field, i);
this is a scan on primary key as that is checked in idx_cond_push) prebuilt->idx_cond_n_cols++;
*/ ut_ad(prebuilt->idx_cond_n_cols
if (file->active_index == file->pushed_idx_cond_keyno && == prebuilt->n_template);
file->active_index != MAX_KEY &&
templ_type == ROW_MYSQL_REC_FIELDS) if (index == prebuilt->index) {
do_idx_cond_push= need_second_pass= TRUE; templ->icp_rec_field_no
= templ->rec_field_no;
/* Note that in InnoDB, i is the column number. MySQL calls columns } else {
'fields'. */ templ->icp_rec_field_no
for (sql_idx = 0; sql_idx < n_fields; sql_idx++) { = dict_index_get_nth_col_pos(
templ = prebuilt->mysql_template + n_requested_fields; prebuilt->index, i);
field = table->field[sql_idx]; }
if (!field->stored_in_db)
goto skip_field;
if (UNIV_LIKELY(templ_type == ROW_MYSQL_REC_FIELDS)) {
/* Decide which columns we should fetch
and which we can skip. */
register const ibool index_contains_field =
dict_index_contains_col_or_prefix(index, innodb_idx);
register const ibool index_covers_field =
field->part_of_key.is_set(file->active_index);
if (!index_contains_field && prebuilt->read_just_key) {
/* If this is a 'key read', we do not need
columns that are not in the key */
goto skip_field;
}
if (index_contains_field && fetch_all_in_key) {
/* This field is needed in the query */
goto include_field;
}
if (bitmap_is_set(table->read_set, sql_idx) ||
bitmap_is_set(table->write_set, sql_idx)) {
/* This field is needed in the query */
goto include_field;
}
if (fetch_primary_key_cols if (dict_index_is_clust(prebuilt->index)) {
&& dict_table_col_in_clustered_key( ut_ad(templ->icp_rec_field_no
index->table, innodb_idx)) { != ULINT_UNDEFINED);
/* This field is needed in the query */ /* If the primary key includes
a column prefix, use it in
index condition pushdown,
because the condition is
evaluated before fetching any
off-page (externally stored)
columns. */
if (templ->icp_rec_field_no
< prebuilt->index->n_uniq) {
/* This is a key column;
all set. */
continue;
}
} else if (templ->icp_rec_field_no
!= ULINT_UNDEFINED) {
continue;
}
goto include_field; /* This is a column prefix index.
The column prefix can be used in
an end_range comparison. */
templ->icp_rec_field_no
= dict_index_get_nth_col_or_prefix_pos(
prebuilt->index, i, TRUE);
ut_ad(templ->icp_rec_field_no
!= ULINT_UNDEFINED);
/* Index condition pushdown can be used on
all columns of a secondary index, and on
the PRIMARY KEY columns. */
/* TODO: enable this assertion
(but first ensure that end_range is
valid here and use an accurate condition
for end_range)
ut_ad(!dict_index_is_clust(prebuilt->index)
|| templ->rec_field_no
< prebuilt->index->n_uniq);
*/
} }
/* This field is not needed in the query, skip it */
goto skip_field;
include_field:
if (do_idx_cond_push &&
((need_second_pass && !index_covers_field) ||
(!need_second_pass && index_covers_field)))
goto skip_field;
} }
n_requested_fields++;
templ->col_no = innodb_idx; ut_ad(prebuilt->idx_cond_n_cols > 0);
templ->clust_rec_field_no = dict_col_get_clust_pos( ut_ad(prebuilt->idx_cond_n_cols == prebuilt->n_template);
&index->table->cols[innodb_idx], clust_index);
ut_ad(templ->clust_rec_field_no != ULINT_UNDEFINED); /* Include the fields that are not needed in index condition
pushdown. */
for (i = 0, sql_idx = 0; i < n_stored_fields; i++, sql_idx++) {
while (!table->field[sql_idx]->stored_in_db) {
sql_idx++;
}
const ibool index_contains
= dict_index_contains_col_or_prefix(index, i);
if (index == prebuilt->index
? !index_contains
: !dict_index_contains_col_or_prefix(
prebuilt->index, i)) {
/* Not needed in ICP */
const Field* field;
if (whole_row) {
field = table->field[sql_idx];
} else {
field = build_template_needs_field(
index_contains,
prebuilt->read_just_key,
fetch_all_in_key,
fetch_primary_key_cols,
index, table, i, sql_idx);
if (!field) {
continue;
}
}
if (index == clust_index) { build_template_field(prebuilt,
templ->rec_field_no = templ->clust_rec_field_no; clust_index, index,
} else { table, field, i);
templ->rec_field_no = dict_index_get_nth_col_pos(
index, innodb_idx);
if (templ->rec_field_no == ULINT_UNDEFINED) {
prebuilt->need_to_access_clustered = TRUE;
} }
} }
if (field->null_ptr) { prebuilt->idx_cond = this;
templ->mysql_null_byte_offset = } else {
(ulint) ((char*) field->null_ptr /* No index condition pushdown */
- (char*) table->record[0]); prebuilt->idx_cond = NULL;
templ->mysql_null_bit_mask = (ulint) field->null_bit; for (i = 0, sql_idx = 0; i < n_stored_fields; i++, sql_idx++) {
} else { const Field* field;
templ->mysql_null_bit_mask = 0;
}
templ->mysql_col_offset = (ulint)
get_field_offset(table, field);
templ->mysql_col_len = (ulint) field->pack_length(); while (!table->field[sql_idx]->stored_in_db) {
if (mysql_prefix_len < templ->mysql_col_offset sql_idx++;
+ templ->mysql_col_len) { }
mysql_prefix_len = templ->mysql_col_offset
+ templ->mysql_col_len;
}
templ->type = index->table->cols[innodb_idx].mtype;
templ->mysql_type = (ulint)field->type();
if (templ->mysql_type == DATA_MYSQL_TRUE_VARCHAR) { if (whole_row) {
templ->mysql_length_bytes = (ulint) field = table->field[sql_idx];
(((Field_varstring*)field)->length_bytes); } else {
} field = build_template_needs_field(
dict_index_contains_col_or_prefix(
index, i),
prebuilt->read_just_key,
fetch_all_in_key,
fetch_primary_key_cols,
index, table, i, sql_idx);
if (!field) {
continue;
}
}
templ->charset = dtype_get_charset_coll( build_template_field(prebuilt, clust_index, index,
index->table->cols[innodb_idx].prtype); table, field, i);
templ->mbminlen = index->table->cols[innodb_idx].mbminlen;
templ->mbmaxlen = index->table->cols[innodb_idx].mbmaxlen;
templ->is_unsigned = index->table->cols[innodb_idx].prtype
& DATA_UNSIGNED;
if (templ->type == DATA_BLOB) {
prebuilt->templ_contains_blob = TRUE;
} }
skip_field:
if (need_second_pass && (sql_idx+1 == n_fields))
{
prebuilt->n_index_fields= n_requested_fields;
need_second_pass= FALSE;
sql_idx= (~(ulint)0); /* to start from 0 */
innodb_idx= (~(ulint)0); /* to start from 0 */ ///psergey-merge-merge-last-change
}
if (field->stored_in_db) {
innodb_idx++;
}
}
prebuilt->n_template = n_requested_fields;
prebuilt->mysql_prefix_len = mysql_prefix_len;
if (do_idx_cond_push)
{
prebuilt->idx_cond_func= index_cond_func_innodb;
prebuilt->idx_cond_func_arg= file;
}
else
{
prebuilt->idx_cond_func= NULL;
prebuilt->n_index_fields= n_requested_fields;
} }
if (index != clust_index && prebuilt->need_to_access_clustered) { if (index != clust_index && prebuilt->need_to_access_clustered) {
/* Change rec_field_no's to correspond to the clustered index /* Change rec_field_no's to correspond to the clustered index
record */ record */
for (ulint i = do_idx_cond_push? prebuilt->n_index_fields : 0; for (i = 0; i < prebuilt->n_template; i++) {
i < n_requested_fields; i++) { mysql_row_templ_t* templ
templ = prebuilt->mysql_template + i; = &prebuilt->mysql_template[i];
templ->rec_field_no = templ->clust_rec_field_no; templ->rec_field_no = templ->clust_rec_field_no;
} }
} }
...@@ -5276,7 +5443,7 @@ ha_innobase::write_row( ...@@ -5276,7 +5443,7 @@ ha_innobase::write_row(
/* Build the template used in converting quickly between /* Build the template used in converting quickly between
the two database formats */ the two database formats */
build_template(prebuilt, NULL, table, this, ROW_MYSQL_WHOLE_ROW); build_template(true);
} }
innodb_srv_conc_enter_innodb(prebuilt->trx); innodb_srv_conc_enter_innodb(prebuilt->trx);
...@@ -5977,8 +6144,7 @@ ha_innobase::index_read( ...@@ -5977,8 +6144,7 @@ ha_innobase::index_read(
necessarily prebuilt->index, but can also be the clustered index */ necessarily prebuilt->index, but can also be the clustered index */
if (prebuilt->sql_stat_start) { if (prebuilt->sql_stat_start) {
build_template(prebuilt, user_thd, table, this, build_template(false);
ROW_MYSQL_REC_FIELDS);
} }
if (key_ptr) { if (key_ptr) {
...@@ -6193,7 +6359,7 @@ ha_innobase::change_active_index( ...@@ -6193,7 +6359,7 @@ ha_innobase::change_active_index(
the flag ROW_MYSQL_WHOLE_ROW below, but that caused unnecessary the flag ROW_MYSQL_WHOLE_ROW below, but that caused unnecessary
copying. Starting from MySQL-4.1 we use a more efficient flag here. */ copying. Starting from MySQL-4.1 we use a more efficient flag here. */
build_template(prebuilt, user_thd, table, this, ROW_MYSQL_REC_FIELDS); build_template(false);
DBUG_RETURN(0); DBUG_RETURN(0);
} }
...@@ -8588,7 +8754,7 @@ ha_innobase::check( ...@@ -8588,7 +8754,7 @@ ha_innobase::check(
/* Build the template; we will use a dummy template /* Build the template; we will use a dummy template
in index scans done in checking */ in index scans done in checking */
build_template(prebuilt, NULL, table, this, ROW_MYSQL_WHOLE_ROW); build_template(true);
} }
if (prebuilt->table->ibd_file_missing) { if (prebuilt->table->ibd_file_missing) {
...@@ -9080,12 +9246,7 @@ ha_innobase::extra( ...@@ -9080,12 +9246,7 @@ ha_innobase::extra(
} }
break; break;
case HA_EXTRA_RESET_STATE: case HA_EXTRA_RESET_STATE:
reset_template(prebuilt); reset_template();
/* Reset index condition pushdown state */
pushed_idx_cond= FALSE;
pushed_idx_cond_keyno= MAX_KEY;
prebuilt->idx_cond_func= NULL;
in_range_check_pushed_down= FALSE;
break; break;
case HA_EXTRA_NO_KEYREAD: case HA_EXTRA_NO_KEYREAD:
prebuilt->read_just_key = 0; prebuilt->read_just_key = 0;
...@@ -9131,14 +9292,8 @@ ha_innobase::reset() ...@@ -9131,14 +9292,8 @@ ha_innobase::reset()
row_mysql_prebuilt_free_blob_heap(prebuilt); row_mysql_prebuilt_free_blob_heap(prebuilt);
} }
reset_template(prebuilt); reset_template();
/* Reset index condition pushdown state */
pushed_idx_cond_keyno= MAX_KEY;
pushed_idx_cond= NULL;
in_range_check_pushed_down= FALSE;
ds_mrr.dsmrr_close(); ds_mrr.dsmrr_close();
prebuilt->idx_cond_func= NULL;
/* TODO: This should really be reset in reset_template() but for now /* TODO: This should really be reset in reset_template() but for now
it's safer to do it explicitly here. */ it's safer to do it explicitly here. */
...@@ -9188,7 +9343,7 @@ ha_innobase::start_stmt( ...@@ -9188,7 +9343,7 @@ ha_innobase::start_stmt(
prebuilt->sql_stat_start = TRUE; prebuilt->sql_stat_start = TRUE;
prebuilt->hint_need_to_fetch_extra_cols = 0; prebuilt->hint_need_to_fetch_extra_cols = 0;
reset_template(prebuilt); reset_template();
if (!prebuilt->mysql_has_locked) { if (!prebuilt->mysql_has_locked) {
/* This handle is for a temporary table created inside /* This handle is for a temporary table created inside
...@@ -9307,7 +9462,7 @@ ha_innobase::external_lock( ...@@ -9307,7 +9462,7 @@ ha_innobase::external_lock(
prebuilt->sql_stat_start = TRUE; prebuilt->sql_stat_start = TRUE;
prebuilt->hint_need_to_fetch_extra_cols = 0; prebuilt->hint_need_to_fetch_extra_cols = 0;
reset_template(prebuilt); reset_template();
if (lock_type == F_WRLCK) { if (lock_type == F_WRLCK) {
...@@ -9490,7 +9645,7 @@ ha_innobase::transactional_table_lock( ...@@ -9490,7 +9645,7 @@ ha_innobase::transactional_table_lock(
prebuilt->sql_stat_start = TRUE; prebuilt->sql_stat_start = TRUE;
prebuilt->hint_need_to_fetch_extra_cols = 0; prebuilt->hint_need_to_fetch_extra_cols = 0;
reset_template(prebuilt); reset_template();
if (lock_type == F_WRLCK) { if (lock_type == F_WRLCK) {
prebuilt->select_lock_type = LOCK_X; prebuilt->select_lock_type = LOCK_X;
...@@ -12306,39 +12461,47 @@ bool ha_innobase::is_thd_killed() ...@@ -12306,39 +12461,47 @@ bool ha_innobase::is_thd_killed()
* Index Condition Pushdown interface implementation * Index Condition Pushdown interface implementation
*/ */
C_MODE_START /*************************************************************//**
InnoDB index push-down condition check
/* @return ICP_NO_MATCH, ICP_MATCH, or ICP_OUT_OF_RANGE */
Index condition check function to be called from within Innobase. extern "C" UNIV_INTERN
See note on ICP_RESULT for return values description. enum icp_result
*/ innobase_index_cond(
/*================*/
static xtradb_icp_result_t index_cond_func_innodb(void *arg) void* file) /*!< in/out: pointer to ha_innobase */
{ {
ha_innobase *h= (ha_innobase*)arg; ha_innobase *h= (ha_innobase*) file;
if (h->is_thd_killed()) if (h->is_thd_killed())
return XTRADB_ICP_ABORTED_BY_USER; return ICP_ABORTED_BY_USER;
if (h->end_range) if (h->end_range)
{ {
if (h->compare_key2(h->end_range) > 0) if (h->compare_key2(h->end_range) > 0)
return XTRADB_ICP_OUT_OF_RANGE; /* caller should return HA_ERR_END_OF_FILE already */ return ICP_OUT_OF_RANGE; /* caller should return HA_ERR_END_OF_FILE already */
} }
return h->pushed_idx_cond->val_int()? XTRADB_ICP_MATCH : XTRADB_ICP_NO_MATCH; return h->pushed_idx_cond->val_int()? ICP_MATCH : ICP_NO_MATCH;
} }
C_MODE_END /** Attempt to push down an index condition.
* @param[in] keyno MySQL key number
* @param[in] idx_cond Index condition to be checked
Item *ha_innobase::idx_cond_push(uint keyno_arg, Item* idx_cond_arg) * @return idx_cond if pushed; NULL if not pushed
{ */
if (keyno_arg != primary_key && prebuilt->select_lock_type != LOCK_X) UNIV_INTERN
{ class Item*
pushed_idx_cond_keyno= keyno_arg; ha_innobase::idx_cond_push(
pushed_idx_cond= idx_cond_arg; uint keyno,
in_range_check_pushed_down= TRUE; class Item* idx_cond)
return NULL; /* Table handler will check the entire condition */ {
} DBUG_ENTER("ha_innobase::idx_cond_push");
return idx_cond_arg; /* Table handler will not make any checks */ DBUG_ASSERT(keyno != MAX_KEY);
DBUG_ASSERT(idx_cond != NULL);
pushed_idx_cond = idx_cond;
pushed_idx_cond_keyno = keyno;
in_range_check_pushed_down = TRUE;
/* Table handler will check the entire condition */
DBUG_RETURN(NULL);
} }
...@@ -223,27 +223,81 @@ class ha_innobase: public handler ...@@ -223,27 +223,81 @@ class ha_innobase: public handler
bool check_if_incompatible_data(HA_CREATE_INFO *info, bool check_if_incompatible_data(HA_CREATE_INFO *info,
uint table_changes); uint table_changes);
bool check_if_supported_virtual_columns(void) { return TRUE; } bool check_if_supported_virtual_columns(void) { return TRUE; }
private:
/** Builds a 'template' to the prebuilt struct.
The template is used in fast retrieval of just those column
values MySQL needs in its processing.
@param whole_row true if access is needed to a whole row,
false if accessing individual fields is enough */
void build_template(bool whole_row);
/** Resets a query execution 'template'.
@see build_template() */
inline void reset_template();
public: public:
/** /** @name Multi Range Read interface @{ */
* Multi Range Read interface /** Initialize multi range read @see DsMrr_impl::dsmrr_init
*/ * @param seq
int multi_range_read_init(RANGE_SEQ_IF *seq, void *seq_init_param, * @param seq_init_param
uint n_ranges, uint mode, HANDLER_BUFFER *buf); * @param n_ranges
int multi_range_read_next(range_id_t *range_info); * @param mode
ha_rows multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq, * @param buf
void *seq_init_param, */
uint n_ranges, uint *bufsz, int multi_range_read_init(RANGE_SEQ_IF* seq,
void* seq_init_param,
uint n_ranges, uint mode,
HANDLER_BUFFER *buf);
/** Process next multi range read @see DsMrr_impl::dsmrr_next
* @param range_info
*/
int multi_range_read_next(range_id_t *range_info);
/** Initialize multi range read and get information.
* @see ha_myisam::multi_range_read_info_const
* @see DsMrr_impl::dsmrr_info_const
* @param keyno
* @param seq
* @param seq_init_param
* @param n_ranges
* @param bufsz
* @param flags
* @param cost
*/
ha_rows multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq,
void *seq_init_param,
uint n_ranges, uint *bufsz,
uint *flags, COST_VECT *cost);
/** Initialize multi range read and get information.
* @see DsMrr_impl::dsmrr_info
* @param keyno
* @param n_ranges
* @param keys
* @param key_parts
* @param bufsz
* @param flags
* @param cost
*/
ha_rows multi_range_read_info(uint keyno, uint n_ranges, uint keys,
uint key_parts, uint *bufsz,
uint *flags, COST_VECT *cost); uint *flags, COST_VECT *cost);
ha_rows multi_range_read_info(uint keyno, uint n_ranges, uint keys, int multi_range_read_explain_info(uint mrr_mode,
uint key_parts, uint *bufsz, char *str, size_t size);
uint *flags, COST_VECT *cost);
int multi_range_read_explain_info(uint mrr_mode, char *str, size_t size); /** Attempt to push down an index condition.
DsMrr_impl ds_mrr; * @param[in] keyno MySQL key number
* @param[in] idx_cond Index condition to be checked
* @return idx_cond if pushed; NULL if not pushed
*/
class Item* idx_cond_push(uint keyno, class Item* idx_cond);
/* An helper function for index_cond_func_innodb: */
bool is_thd_killed();
Item *idx_cond_push(uint keyno, Item* idx_cond); private:
/** The multi range read session object */
DsMrr_impl ds_mrr;
/* An helper function for index_cond_func_innodb: */ /* @} */
bool is_thd_killed();
}; };
/* Some accessor functions which the InnoDB plugin needs, but which /* Some accessor functions which the InnoDB plugin needs, but which
......
...@@ -839,6 +839,18 @@ dict_index_get_nth_col_pos( ...@@ -839,6 +839,18 @@ dict_index_get_nth_col_pos(
const dict_index_t* index, /*!< in: index */ const dict_index_t* index, /*!< in: index */
ulint n); /*!< in: column number */ ulint n); /*!< in: column number */
/********************************************************************//** /********************************************************************//**
Looks for column n in an index.
@return position in internal representation of the index;
ULINT_UNDEFINED if not contained */
UNIV_INTERN
ulint
dict_index_get_nth_col_or_prefix_pos(
/*=================================*/
const dict_index_t* index, /*!< in: index */
ulint n, /*!< in: column number */
ibool inc_prefix); /*!< in: TRUE=consider
column prefixes too */
/********************************************************************//**
Returns TRUE if the index contains a column or a prefix of that column. Returns TRUE if the index contains a column or a prefix of that column.
@return TRUE if contains the column or its prefix */ @return TRUE if contains the column or its prefix */
UNIV_INTERN UNIV_INTERN
......
...@@ -656,6 +656,20 @@ dict_index_get_nth_col_no( ...@@ -656,6 +656,20 @@ dict_index_get_nth_col_no(
return(dict_col_get_no(dict_index_get_nth_col(index, pos))); return(dict_col_get_no(dict_index_get_nth_col(index, pos)));
} }
/********************************************************************//**
Looks for column n in an index.
@return position in internal representation of the index;
ULINT_UNDEFINED if not contained */
UNIV_INLINE
ulint
dict_index_get_nth_col_pos(
/*=======================*/
const dict_index_t* index, /*!< in: index */
ulint n) /*!< in: column number */
{
return(dict_index_get_nth_col_or_prefix_pos(index, n, FALSE));
}
#ifndef UNIV_HOTBACKUP #ifndef UNIV_HOTBACKUP
/********************************************************************//** /********************************************************************//**
Returns the minimum data size of an index record. Returns the minimum data size of an index record.
......
...@@ -247,6 +247,15 @@ innobase_get_at_most_n_mbchars( ...@@ -247,6 +247,15 @@ innobase_get_at_most_n_mbchars(
ulint data_len, /*!< in: length of the string in bytes */ ulint data_len, /*!< in: length of the string in bytes */
const char* str); /*!< in: character string */ const char* str); /*!< in: character string */
/*************************************************************//**
InnoDB index push-down condition check
@return ICP_NO_MATCH, ICP_MATCH, or ICP_OUT_OF_RANGE */
UNIV_INTERN
enum icp_result
innobase_index_cond(
/*================*/
void* file) /*!< in/out: pointer to ha_innobase */
__attribute__((nonnull, warn_unused_result));
/******************************************************************//** /******************************************************************//**
Returns true if the thread supports XA, Returns true if the thread supports XA,
global value of innodb_supports_xa if thd is NULL. global value of innodb_supports_xa if thd is NULL.
......
...@@ -547,6 +547,10 @@ struct mysql_row_templ_struct { ...@@ -547,6 +547,10 @@ struct mysql_row_templ_struct {
Innobase record in the clustered index; Innobase record in the clustered index;
not defined if template_type is not defined if template_type is
ROW_MYSQL_WHOLE_ROW */ ROW_MYSQL_WHOLE_ROW */
ulint icp_rec_field_no; /*!< field number of the column in an
Innobase record in the current index;
not defined unless
index condition pushdown is used */
ulint mysql_col_offset; /*!< offset of the column in the MySQL ulint mysql_col_offset; /*!< offset of the column in the MySQL
row format */ row format */
ulint mysql_col_len; /*!< length of the column in the MySQL ulint mysql_col_len; /*!< length of the column in the MySQL
...@@ -585,16 +589,6 @@ struct mysql_row_templ_struct { ...@@ -585,16 +589,6 @@ struct mysql_row_templ_struct {
#define ROW_PREBUILT_ALLOCATED 78540783 #define ROW_PREBUILT_ALLOCATED 78540783
#define ROW_PREBUILT_FREED 26423527 #define ROW_PREBUILT_FREED 26423527
typedef enum xtradb_icp_result {
XTRADB_ICP_ERROR=-1,
XTRADB_ICP_NO_MATCH=0,
XTRADB_ICP_MATCH=1,
XTRADB_ICP_OUT_OF_RANGE=2,
XTRADB_ICP_ABORTED_BY_USER=3,
} xtradb_icp_result_t;
typedef xtradb_icp_result_t (*index_cond_func_t)(void *param);
/** A struct for (sometimes lazily) prebuilt structures in an Innobase table /** A struct for (sometimes lazily) prebuilt structures in an Innobase table
handle used within MySQL; these are used to save CPU time. */ handle used within MySQL; these are used to save CPU time. */
...@@ -792,16 +786,15 @@ struct row_prebuilt_struct { ...@@ -792,16 +786,15 @@ struct row_prebuilt_struct {
store it here so that we can return store it here so that we can return
it to MySQL */ it to MySQL */
/*----------------------*/ /*----------------------*/
void* idx_cond; /*!< In ICP, pointer to a ha_innobase,
passed to innobase_index_cond().
NULL if index condition pushdown is
not used. */
ulint idx_cond_n_cols;/*!< Number of fields in idx_cond_cols.
0 if and only if idx_cond == NULL. */
/*----------------------*/
ulint magic_n2; /*!< this should be the same as ulint magic_n2; /*!< this should be the same as
magic_n */ magic_n */
/*----------------------*/
index_cond_func_t idx_cond_func;/* Index Condition Pushdown function,
or NULL if there is none set */
void* idx_cond_func_arg;/* ICP function argument */
ulint n_index_fields; /* Number of fields at the start of
mysql_template. Valid only when using
ICP. */
/*----------------------*/
}; };
#define ROW_PREBUILT_FETCH_MAGIC_N 465765687 #define ROW_PREBUILT_FETCH_MAGIC_N 465765687
......
...@@ -58,6 +58,8 @@ Created 12/19/1997 Heikki Tuuri ...@@ -58,6 +58,8 @@ Created 12/19/1997 Heikki Tuuri
#include "buf0lru.h" #include "buf0lru.h"
#include "ha_prototypes.h" #include "ha_prototypes.h"
#include "my_compare.h" /* enum icp_result */
/* Maximum number of rows to prefetch; MySQL interface has another parameter */ /* Maximum number of rows to prefetch; MySQL interface has another parameter */
#define SEL_MAX_N_PREFETCH 16 #define SEL_MAX_N_PREFETCH 16
...@@ -2674,144 +2676,96 @@ row_sel_field_store_in_mysql_format( ...@@ -2674,144 +2676,96 @@ row_sel_field_store_in_mysql_format(
} }
/**************************************************************//** /**************************************************************//**
Convert a row in the Innobase format to a row in the MySQL format. Convert a field in the Innobase format to a field in the MySQL format. */
Note that the template in prebuilt may advise us to copy only a few
columns to mysql_rec, other columns are left blank. All columns may not
be needed in the query.
@return TRUE on success, FALSE if not all columns could be retrieved */
static __attribute__((warn_unused_result)) static __attribute__((warn_unused_result))
ibool ibool
row_sel_store_mysql_rec( row_sel_store_mysql_field(
/*====================*/ /*======================*/
byte* mysql_rec, /*!< out: row in the MySQL format */ byte* mysql_rec, /*!< out: record in the
row_prebuilt_t* prebuilt, /*!< in: prebuilt struct */ MySQL format */
const rec_t* rec, /*!< in: Innobase record in the index row_prebuilt_t* prebuilt, /*!< in/out: prebuilt struct */
which was described in prebuilt's const rec_t* rec, /*!< in: InnoDB record;
template, or in the clustered index; must be protected by
must be protected by a page latch */ a page latch */
ibool rec_clust, /*!< in: TRUE if rec is in the const ulint* offsets, /*!< in: array returned by
clustered index instead of rec_get_offsets() */
prebuilt->index */ ulint field_no, /*!< in: templ->rec_field_no or
const ulint* offsets, /* in: array returned by templ->clust_rec_field_no */
rec_get_offsets() */ const mysql_row_templ_t*templ) /*!< in: row template */
ulint start_field_no, /* in: start from this field */
ulint end_field_no) /* in: end at this field */
{ {
mem_heap_t* extern_field_heap = NULL; const byte* data;
mem_heap_t* heap; ulint len;
ulint i;
ut_ad(prebuilt->mysql_template);
ut_ad(prebuilt->default_rec); ut_ad(prebuilt->default_rec);
ut_ad(templ);
ut_ad(templ >= prebuilt->mysql_template);
ut_ad(templ < &prebuilt->mysql_template[prebuilt->n_template]);
ut_ad(field_no == templ->clust_rec_field_no
|| field_no == templ->rec_field_no
|| field_no == templ->icp_rec_field_no);
ut_ad(rec_offs_validate(rec, NULL, offsets)); ut_ad(rec_offs_validate(rec, NULL, offsets));
ut_ad(!rec_get_deleted_flag(rec, rec_offs_comp(offsets)));
if (UNIV_LIKELY_NULL(prebuilt->blob_heap)) { if (UNIV_UNLIKELY(rec_offs_nth_extern(offsets, field_no))) {
mem_heap_free(prebuilt->blob_heap);
prebuilt->blob_heap = NULL;
}
for (i = start_field_no; i < end_field_no /* prebuilt->n_template */ ; i++) {
const mysql_row_templ_t*templ = prebuilt->mysql_template + i;
const byte* data;
ulint len;
ulint field_no;
field_no = rec_clust
? templ->clust_rec_field_no : templ->rec_field_no;
if (UNIV_UNLIKELY(rec_offs_nth_extern(offsets, field_no))) { mem_heap_t* heap;
/* Copy an externally stored field to a temporary heap */
/* Copy an externally stored field to the temporary ut_a(!prebuilt->trx->has_search_latch);
heap */ ut_ad(field_no == templ->clust_rec_field_no);
ut_a(!prebuilt->trx->has_search_latch); if (UNIV_UNLIKELY(templ->type == DATA_BLOB)) {
if (prebuilt->blob_heap == NULL) {
prebuilt->blob_heap = mem_heap_create(
UNIV_PAGE_SIZE);
}
if (UNIV_UNLIKELY(templ->type == DATA_BLOB)) { heap = prebuilt->blob_heap;
if (prebuilt->blob_heap == NULL) { } else {
prebuilt->blob_heap = mem_heap_create( heap = mem_heap_create(UNIV_PAGE_SIZE);
UNIV_PAGE_SIZE); }
}
heap = prebuilt->blob_heap; /* NOTE: if we are retrieving a big BLOB, we may
} else { already run out of memory in the next call, which
extern_field_heap causes an assert */
= mem_heap_create(UNIV_PAGE_SIZE);
heap = extern_field_heap; data = btr_rec_copy_externally_stored_field(
} rec, offsets,
dict_table_zip_size(prebuilt->table),
field_no, &len, heap);
/* NOTE: if we are retrieving a big BLOB, we may if (UNIV_UNLIKELY(!data)) {
already run out of memory in the next call, which /* The externally stored field was not written
causes an assert */ yet. This record should only be seen by
recv_recovery_rollback_active() or any
data = btr_rec_copy_externally_stored_field( TRX_ISO_READ_UNCOMMITTED transactions. */
rec, offsets,
dict_table_zip_size(prebuilt->table),
field_no, &len, heap);
if (UNIV_UNLIKELY(!data)) {
/* The externally stored field
was not written yet. This
record should only be seen by
recv_recovery_rollback_active()
or any TRX_ISO_READ_UNCOMMITTED
transactions. */
if (extern_field_heap) {
mem_heap_free(extern_field_heap);
}
return(FALSE); if (heap != prebuilt->blob_heap) {
mem_heap_free(heap);
} }
ut_a(len != UNIV_SQL_NULL); ut_a(prebuilt->trx->isolation_level
} else { == TRX_ISO_READ_UNCOMMITTED);
/* Field is stored in the row. */ return(FALSE);
}
data = rec_get_nth_field(rec, offsets, field_no, &len);
if (UNIV_UNLIKELY(templ->type == DATA_BLOB) ut_a(len != UNIV_SQL_NULL);
&& len != UNIV_SQL_NULL) {
/* It is a BLOB field locally stored in the row_sel_field_store_in_mysql_format(
InnoDB record: we MUST copy its contents to mysql_rec + templ->mysql_col_offset,
prebuilt->blob_heap here because later code templ, data, len);
assumes all BLOB values have been copied to a
safe place. */
if (prebuilt->blob_heap == NULL) { if (heap != prebuilt->blob_heap) {
prebuilt->blob_heap = mem_heap_create( mem_heap_free(heap);
UNIV_PAGE_SIZE);
}
data = memcpy(mem_heap_alloc(
prebuilt->blob_heap, len),
data, len);
}
} }
} else {
/* Field is stored in the row. */
if (len != UNIV_SQL_NULL) { data = rec_get_nth_field(rec, offsets, field_no, &len);
row_sel_field_store_in_mysql_format(
mysql_rec + templ->mysql_col_offset,
templ, data, len);
/* Cleanup */
if (extern_field_heap) {
mem_heap_free(extern_field_heap);
extern_field_heap = NULL;
}
if (templ->mysql_null_bit_mask) { if (len == UNIV_SQL_NULL) {
/* It is a nullable column with a non-NULL
value */
mysql_rec[templ->mysql_null_byte_offset]
&= ~(byte) templ->mysql_null_bit_mask;
}
} else {
/* MySQL assumes that the field for an SQL /* MySQL assumes that the field for an SQL
NULL value is set to the default value. */ NULL value is set to the default value. */
ut_ad(templ->mysql_null_bit_mask);
UNIV_MEM_ASSERT_RW(prebuilt->default_rec UNIV_MEM_ASSERT_RW(prebuilt->default_rec
+ templ->mysql_col_offset, + templ->mysql_col_offset,
...@@ -2822,6 +2776,85 @@ row_sel_store_mysql_rec( ...@@ -2822,6 +2776,85 @@ row_sel_store_mysql_rec(
(const byte*) prebuilt->default_rec (const byte*) prebuilt->default_rec
+ templ->mysql_col_offset, + templ->mysql_col_offset,
templ->mysql_col_len); templ->mysql_col_len);
return(TRUE);
}
if (UNIV_UNLIKELY(templ->type == DATA_BLOB)) {
/* It is a BLOB field locally stored in the
InnoDB record: we MUST copy its contents to
prebuilt->blob_heap here because
row_sel_field_store_in_mysql_format() stores a
pointer to the data, and the data passed to us
will be invalid as soon as the
mini-transaction is committed and the page
latch on the clustered index page is
released. */
if (prebuilt->blob_heap == NULL) {
prebuilt->blob_heap = mem_heap_create(
UNIV_PAGE_SIZE);
}
data = mem_heap_dup(prebuilt->blob_heap, data, len);
}
row_sel_field_store_in_mysql_format(
mysql_rec + templ->mysql_col_offset,
templ, data, len);
}
ut_ad(len != UNIV_SQL_NULL);
if (templ->mysql_null_bit_mask) {
/* It is a nullable column with a non-NULL
value */
mysql_rec[templ->mysql_null_byte_offset]
&= ~(byte) templ->mysql_null_bit_mask;
}
return(TRUE);
}
/**************************************************************//**
Convert a row in the Innobase format to a row in the MySQL format.
Note that the template in prebuilt may advise us to copy only a few
columns to mysql_rec, other columns are left blank. All columns may not
be needed in the query.
@return TRUE on success, FALSE if not all columns could be retrieved */
static __attribute__((warn_unused_result))
ibool
row_sel_store_mysql_rec(
/*====================*/
byte* mysql_rec, /*!< out: row in the MySQL format */
row_prebuilt_t* prebuilt, /*!< in: prebuilt struct */
const rec_t* rec, /*!< in: Innobase record in the index
which was described in prebuilt's
template, or in the clustered index;
must be protected by a page latch */
ibool rec_clust, /*!< in: TRUE if rec is in the
clustered index instead of
prebuilt->index */
const ulint* offsets) /*!< in: array returned by
rec_get_offsets(rec) */
{
ulint i;
if (UNIV_LIKELY_NULL(prebuilt->blob_heap)) {
mem_heap_free(prebuilt->blob_heap);
prebuilt->blob_heap = NULL;
}
for (i = 0; i < prebuilt->n_template; i++) {
const mysql_row_templ_t*templ = &prebuilt->mysql_template[i];
if (!row_sel_store_mysql_field(mysql_rec, prebuilt,
rec, offsets,
rec_clust
? templ->clust_rec_field_no
: templ->rec_field_no,
templ)) {
return(FALSE);
} }
} }
...@@ -3192,31 +3225,19 @@ UNIV_INLINE __attribute__((warn_unused_result)) ...@@ -3192,31 +3225,19 @@ UNIV_INLINE __attribute__((warn_unused_result))
ibool ibool
row_sel_push_cache_row_for_mysql( row_sel_push_cache_row_for_mysql(
/*=============================*/ /*=============================*/
row_prebuilt_t* prebuilt, /*!< in: prebuilt struct */ byte* mysql_rec, /*!< in/out: MySQL record */
const rec_t* rec, /*!< in: record to push, in the index row_prebuilt_t* prebuilt) /*!< in/out: prebuilt struct */
which was described in prebuilt's
template, or in the clustered index;
must be protected by a page latch */
ibool rec_clust, /*!< in: TRUE if rec is in the
clustered index instead of
prebuilt->index */
const ulint* offsets, /* in: rec_get_offsets() */
ulint start_field_no, /* in: start from this field */
byte* remainder_buf) /* in: if start_field_no !=0,
where to take prev fields */
{ {
byte* buf;
ulint i;
ut_ad(prebuilt->n_fetch_cached < MYSQL_FETCH_CACHE_SIZE); ut_ad(prebuilt->n_fetch_cached < MYSQL_FETCH_CACHE_SIZE);
ut_ad(rec_offs_validate(rec, NULL, offsets));
ut_ad(!rec_get_deleted_flag(rec, rec_offs_comp(offsets)));
ut_a(!prebuilt->templ_contains_blob); ut_a(!prebuilt->templ_contains_blob);
if (prebuilt->fetch_cache[0] == NULL) { if (UNIV_UNLIKELY(prebuilt->fetch_cache[0] == NULL)) {
ulint i;
/* Allocate memory for the fetch cache */ /* Allocate memory for the fetch cache */
ut_ad(prebuilt->n_fetch_cached == 0);
for (i = 0; i < MYSQL_FETCH_CACHE_SIZE; i++) { for (i = 0; i < MYSQL_FETCH_CACHE_SIZE; i++) {
byte* buf;
/* A user has reported memory corruption in these /* A user has reported memory corruption in these
buffers in Linux. Put magic numbers there to help buffers in Linux. Put magic numbers there to help
...@@ -3236,46 +3257,14 @@ row_sel_push_cache_row_for_mysql( ...@@ -3236,46 +3257,14 @@ row_sel_push_cache_row_for_mysql(
UNIV_MEM_INVALID(prebuilt->fetch_cache[prebuilt->n_fetch_cached], UNIV_MEM_INVALID(prebuilt->fetch_cache[prebuilt->n_fetch_cached],
prebuilt->mysql_row_len); prebuilt->mysql_row_len);
if (UNIV_UNLIKELY(!row_sel_store_mysql_rec( memcpy(prebuilt->fetch_cache[prebuilt->n_fetch_cached],
prebuilt->fetch_cache[ mysql_rec, prebuilt->mysql_row_len);
prebuilt->n_fetch_cached],
prebuilt,
rec,
rec_clust,
offsets,
start_field_no,
prebuilt->n_template))) {
return(FALSE);
}
if (start_field_no) {
for (i=0; i < start_field_no; i++) {
register ulint offs;
mysql_row_templ_t* templ;
register byte * null_byte;
templ = prebuilt->mysql_template + i;
if (templ->mysql_null_bit_mask) { if (++prebuilt->n_fetch_cached < MYSQL_FETCH_CACHE_SIZE) {
offs = templ->mysql_null_byte_offset; return(FALSE);
null_byte= prebuilt->fetch_cache[
prebuilt->n_fetch_cached]+offs;
(*null_byte)&= ~templ->mysql_null_bit_mask;
(*null_byte)|= (*(remainder_buf + offs) &
templ->mysql_null_bit_mask);
}
offs = templ->mysql_col_offset;
memcpy(prebuilt->fetch_cache[prebuilt->n_fetch_cached]
+ offs,
remainder_buf + offs,
templ->mysql_col_len);
}
} }
prebuilt->n_fetch_cached++; row_sel_pop_cached_row_for_mysql(mysql_rec, prebuilt);
return(TRUE); return(TRUE);
} }
...@@ -3353,6 +3342,81 @@ row_sel_try_search_shortcut_for_mysql( ...@@ -3353,6 +3342,81 @@ row_sel_try_search_shortcut_for_mysql(
return(SEL_FOUND); return(SEL_FOUND);
} }
/*********************************************************************//**
Check a pushed-down index condition.
@return ICP_NO_MATCH, ICP_MATCH, or ICP_OUT_OF_RANGE */
static
enum icp_result
row_search_idx_cond_check(
/*======================*/
byte* mysql_rec, /*!< out: record
in MySQL format (invalid unless
prebuilt->idx_cond!=NULL and
we return ICP_MATCH) */
row_prebuilt_t* prebuilt, /*!< in/out: prebuilt struct
for the table handle */
const rec_t* rec, /*!< in: InnoDB record */
const ulint* offsets) /*!< in: rec_get_offsets() */
{
enum icp_result result;
ulint i;
ut_ad(rec_offs_validate(rec, prebuilt->index, offsets));
if (!prebuilt->idx_cond) {
return(ICP_MATCH);
}
/* Convert to MySQL format those fields that are needed for
evaluating the index condition. */
if (UNIV_LIKELY_NULL(prebuilt->blob_heap)) {
mem_heap_empty(prebuilt->blob_heap);
}
for (i = 0; i < prebuilt->idx_cond_n_cols; i++) {
const mysql_row_templ_t*templ = &prebuilt->mysql_template[i];
if (!row_sel_store_mysql_field(mysql_rec, prebuilt,
rec, offsets,
templ->icp_rec_field_no,
templ)) {
return(ICP_NO_MATCH);
}
}
/* We assume that the index conditions on
case-insensitive columns are case-insensitive. The
case of such columns may be wrong in a secondary
index, if the case of the column has been updated in
the past, or a record has been deleted and a record
inserted in a different case. */
result = innobase_index_cond(prebuilt->idx_cond);
switch (result) {
case ICP_MATCH:
/* Convert the remaining fields to MySQL format.
If this is a secondary index record, we must defer
this until we have fetched the clustered index record. */
if (!prebuilt->need_to_access_clustered
|| dict_index_is_clust(prebuilt->index)) {
if (!row_sel_store_mysql_rec(mysql_rec, prebuilt,
rec,
FALSE, offsets)) {
ut_ad(dict_index_is_clust(prebuilt->index));
result = ICP_NO_MATCH;
}
}
/* fall through */
case ICP_NO_MATCH:
case ICP_OUT_OF_RANGE:
case ICP_ABORTED_BY_USER:
return(result);
default: ;
}
ut_error;
}
/********************************************************************//** /********************************************************************//**
Searches for rows in the database. This is used in the interface to Searches for rows in the database. This is used in the interface to
MySQL. This function opens a cursor, and also implements fetch next MySQL. This function opens a cursor, and also implements fetch next
...@@ -3415,10 +3479,8 @@ row_search_for_mysql( ...@@ -3415,10 +3479,8 @@ row_search_for_mysql(
mem_heap_t* heap = NULL; mem_heap_t* heap = NULL;
ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint offsets_[REC_OFFS_NORMAL_SIZE];
ulint* offsets = offsets_; ulint* offsets = offsets_;
ibool some_fields_in_buffer;
ibool table_lock_waited = FALSE; ibool table_lock_waited = FALSE;
ibool problematic_use = FALSE; ibool problematic_use = FALSE;
ibool get_clust_rec = 0;
rec_offs_init(offsets_); rec_offs_init(offsets_);
...@@ -3681,10 +3743,24 @@ row_search_for_mysql( ...@@ -3681,10 +3743,24 @@ row_search_for_mysql(
mtr_commit(&mtr). */ mtr_commit(&mtr). */
ut_ad(!rec_get_deleted_flag(rec, comp)); ut_ad(!rec_get_deleted_flag(rec, comp));
if (prebuilt->idx_cond) {
switch (row_search_idx_cond_check(
buf, prebuilt,
rec, offsets)) {
case ICP_NO_MATCH:
case ICP_OUT_OF_RANGE:
case ICP_ABORTED_BY_USER:
goto shortcut_mismatch;
case ICP_MATCH:
goto shortcut_match;
default: ;
}
ut_error;
}
if (!row_sel_store_mysql_rec(buf, prebuilt, if (!row_sel_store_mysql_rec(buf, prebuilt,
rec, FALSE, rec, FALSE,
offsets, 0, offsets)) {
prebuilt->n_template)) {
/* Only fresh inserts may contain /* Only fresh inserts may contain
incomplete externally stored incomplete externally stored
columns. Pretend that such columns. Pretend that such
...@@ -3695,13 +3771,12 @@ row_search_for_mysql( ...@@ -3695,13 +3771,12 @@ row_search_for_mysql(
rolling back a recovered rolling back a recovered
transaction. Rollback happens transaction. Rollback happens
at a lower level, not here. */ at a lower level, not here. */
ut_a(trx->isolation_level
== TRX_ISO_READ_UNCOMMITTED);
/* Proceed as in case SEL_RETRY. */ /* Proceed as in case SEL_RETRY. */
break; break;
} }
shortcut_match:
mtr_commit(&mtr); mtr_commit(&mtr);
/* ut_print_name(stderr, index->name); /* ut_print_name(stderr, index->name);
...@@ -3713,6 +3788,7 @@ row_search_for_mysql( ...@@ -3713,6 +3788,7 @@ row_search_for_mysql(
goto release_search_latch_if_needed; goto release_search_latch_if_needed;
case SEL_EXHAUSTED: case SEL_EXHAUSTED:
shortcut_mismatch:
mtr_commit(&mtr); mtr_commit(&mtr);
/* ut_print_name(stderr, index->name); /* ut_print_name(stderr, index->name);
...@@ -3804,8 +3880,9 @@ row_search_for_mysql( ...@@ -3804,8 +3880,9 @@ row_search_for_mysql(
if (!prebuilt->sql_stat_start) { if (!prebuilt->sql_stat_start) {
/* No need to set an intention lock or assign a read view */ /* No need to set an intention lock or assign a read view */
if (trx->read_view == NULL if (UNIV_UNLIKELY
&& prebuilt->select_lock_type == LOCK_NONE) { (trx->read_view == NULL
&& prebuilt->select_lock_type == LOCK_NONE)) {
fputs("InnoDB: Error: MySQL is trying to" fputs("InnoDB: Error: MySQL is trying to"
" perform a consistent read\n" " perform a consistent read\n"
...@@ -4265,6 +4342,16 @@ row_search_for_mysql( ...@@ -4265,6 +4342,16 @@ row_search_for_mysql(
if (UNIV_LIKELY(trx->wait_lock != NULL)) { if (UNIV_LIKELY(trx->wait_lock != NULL)) {
lock_cancel_waiting_and_release( lock_cancel_waiting_and_release(
trx->wait_lock); trx->wait_lock);
mutex_exit(&kernel_mutex);
if (old_vers == NULL) {
/* The row was not yet committed */
goto next_rec;
}
did_semi_consistent_read = TRUE;
rec = old_vers;
} else { } else {
mutex_exit(&kernel_mutex); mutex_exit(&kernel_mutex);
...@@ -4275,19 +4362,7 @@ row_search_for_mysql( ...@@ -4275,19 +4362,7 @@ row_search_for_mysql(
offsets = rec_get_offsets(rec, index, offsets, offsets = rec_get_offsets(rec, index, offsets,
ULINT_UNDEFINED, ULINT_UNDEFINED,
&heap); &heap);
err = DB_SUCCESS;
break;
} }
mutex_exit(&kernel_mutex);
if (old_vers == NULL) {
/* The row was not yet committed */
goto next_rec;
}
did_semi_consistent_read = TRUE;
rec = old_vers;
break; break;
default: default:
...@@ -4346,8 +4421,27 @@ row_search_for_mysql( ...@@ -4346,8 +4421,27 @@ row_search_for_mysql(
if (!lock_sec_rec_cons_read_sees( if (!lock_sec_rec_cons_read_sees(
rec, trx->read_view)) { rec, trx->read_view)) {
get_clust_rec = TRUE; /* We should look at the clustered index.
goto idx_cond_check; However, as this is a non-locking read,
we can skip the clustered index lookup if
the condition does not match the secondary
index entry. */
switch (row_search_idx_cond_check(
buf, prebuilt, rec, offsets)) {
case ICP_NO_MATCH:
goto next_rec;
case ICP_OUT_OF_RANGE:
err = DB_RECORD_NOT_FOUND;
goto idx_cond_failed;
case ICP_ABORTED_BY_USER:
err = DB_SEARCH_ABORTED_BY_USER;
goto idx_cond_failed;
case ICP_MATCH:
goto requires_clust_rec;
default: ;
}
ut_error;
} }
} }
} }
...@@ -4392,38 +4486,31 @@ row_search_for_mysql( ...@@ -4392,38 +4486,31 @@ row_search_for_mysql(
goto next_rec; goto next_rec;
} }
/* Check if the record matches the index condition. */
idx_cond_check: switch (row_search_idx_cond_check(buf, prebuilt, rec, offsets)) {
if (prebuilt->idx_cond_func) { case ICP_NO_MATCH:
int res; if (did_semi_consistent_read) {
ibool ib_res __attribute__ ((unused)); row_unlock_for_mysql(prebuilt, TRUE);
ut_ad(prebuilt->template_type != ROW_MYSQL_DUMMY_TEMPLATE); }
offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &heap); goto next_rec;
ib_res= row_sel_store_mysql_rec(buf, prebuilt, rec, FALSE, case ICP_ABORTED_BY_USER:
offsets, 0, prebuilt->n_index_fields); err = DB_SEARCH_ABORTED_BY_USER;
/* goto idx_cond_failed;
The above call will fail and return FALSE when requested to case ICP_OUT_OF_RANGE:
store an "externally stored column" (afaiu, a blob). Index err = DB_RECORD_NOT_FOUND;
Condition Pushdown is not supported for indexes with blob goto idx_cond_failed;
columns, so we should never get this error. case ICP_MATCH:
*/ break;
ut_ad(ib_res); default:
res= prebuilt->idx_cond_func(prebuilt->idx_cond_func_arg); ut_error;
if (res == XTRADB_ICP_NO_MATCH) }
goto next_rec;
else if (res != XTRADB_ICP_MATCH) {
err= (res == XTRADB_ICP_ABORTED_BY_USER ?
DB_SEARCH_ABORTED_BY_USER :
DB_RECORD_NOT_FOUND);
goto idx_cond_failed;
}
/* res == XTRADB_ICP_MATCH */
}
/* Get the clustered index record if needed, if we did not do the /* Get the clustered index record if needed, if we did not do the
search using the clustered index. */ search using the clustered index. */
if (get_clust_rec || (index != clust_index if (index != clust_index && prebuilt->need_to_access_clustered) {
&& prebuilt->need_to_access_clustered)) {
requires_clust_rec:
ut_ad(index != clust_index);
/* We use a 'goto' to the preceding label if a consistent /* We use a 'goto' to the preceding label if a consistent
read of a secondary index record requires us to look up old read of a secondary index record requires us to look up old
...@@ -4487,6 +4574,19 @@ row_search_for_mysql( ...@@ -4487,6 +4574,19 @@ row_search_for_mysql(
result_rec = clust_rec; result_rec = clust_rec;
ut_ad(rec_offs_validate(result_rec, clust_index, offsets)); ut_ad(rec_offs_validate(result_rec, clust_index, offsets));
if (prebuilt->idx_cond) {
/* Convert the remaining fields to
MySQL format. We were unable to do
this in row_search_idx_cond_check(),
because the condition is on the
secondary index and the requested
column is in the clustered index. */
if (!row_sel_store_mysql_rec(buf, prebuilt, result_rec,
TRUE, offsets)) {
goto next_rec;
}
}
} else { } else {
result_rec = rec; result_rec = rec;
} }
...@@ -4520,15 +4620,10 @@ row_search_for_mysql( ...@@ -4520,15 +4620,10 @@ row_search_for_mysql(
are BLOBs in the fields to be fetched. In HANDLER we do are BLOBs in the fields to be fetched. In HANDLER we do
not cache rows because there the cursor is a scrollable not cache rows because there the cursor is a scrollable
cursor. */ cursor. */
some_fields_in_buffer = (index != clust_index
&& prebuilt->idx_cond_func); if (!prebuilt->idx_cond
&& !row_sel_store_mysql_rec(buf, prebuilt, result_rec,
if (!row_sel_push_cache_row_for_mysql(prebuilt, result_rec, result_rec != rec, offsets)) {
result_rec != rec,
offsets,
some_fields_in_buffer?
prebuilt->n_index_fields : 0,
buf)) {
/* Only fresh inserts may contain incomplete /* Only fresh inserts may contain incomplete
externally stored columns. Pretend that such externally stored columns. Pretend that such
records do not exist. Such records may only be records do not exist. Such records may only be
...@@ -4536,14 +4631,10 @@ row_search_for_mysql( ...@@ -4536,14 +4631,10 @@ row_search_for_mysql(
level or when rolling back a recovered level or when rolling back a recovered
transaction. Rollback happens at a lower transaction. Rollback happens at a lower
level, not here. */ level, not here. */
ut_a(trx->isolation_level == TRX_ISO_READ_UNCOMMITTED); goto next_rec;
} else if (prebuilt->n_fetch_cached } else if (row_sel_push_cache_row_for_mysql(buf, prebuilt)) {
== MYSQL_FETCH_CACHE_SIZE) { goto next_rec;
goto got_row;
} }
goto next_rec;
} else { } else {
if (UNIV_UNLIKELY if (UNIV_UNLIKELY
(prebuilt->template_type == ROW_MYSQL_DUMMY_TEMPLATE)) { (prebuilt->template_type == ROW_MYSQL_DUMMY_TEMPLATE)) {
...@@ -4564,15 +4655,11 @@ row_search_for_mysql( ...@@ -4564,15 +4655,11 @@ row_search_for_mysql(
rec_offs_size(offsets)); rec_offs_size(offsets));
mach_write_to_4(buf, mach_write_to_4(buf,
rec_offs_extra_size(offsets) + 4); rec_offs_extra_size(offsets) + 4);
} else { } else if (!prebuilt->idx_cond) {
/* Returning a row to MySQL */ /* The record was not yet converted to MySQL format. */
if (!row_sel_store_mysql_rec(
if (!row_sel_store_mysql_rec(buf, prebuilt, result_rec, buf, prebuilt,
result_rec != rec, result_rec, result_rec != rec, offsets)) {
offsets,
prebuilt->idx_cond_func?
prebuilt->n_index_fields: 0,
prebuilt->n_template)) {
/* Only fresh inserts may contain /* Only fresh inserts may contain
incomplete externally stored incomplete externally stored
columns. Pretend that such records do columns. Pretend that such records do
...@@ -4581,8 +4668,6 @@ row_search_for_mysql( ...@@ -4581,8 +4668,6 @@ row_search_for_mysql(
isolation level or when rolling back a isolation level or when rolling back a
recovered transaction. Rollback recovered transaction. Rollback
happens at a lower level, not here. */ happens at a lower level, not here. */
ut_a(trx->isolation_level
== TRX_ISO_READ_UNCOMMITTED);
goto next_rec; goto next_rec;
} }
} }
...@@ -4600,7 +4685,6 @@ row_search_for_mysql( ...@@ -4600,7 +4685,6 @@ row_search_for_mysql(
/* From this point on, 'offsets' are invalid. */ /* From this point on, 'offsets' are invalid. */
got_row:
/* We have an optimization to save CPU time: if this is a consistent /* We have an optimization to save CPU time: if this is a consistent
read on a unique condition on the clustered index, then we do not read on a unique condition on the clustered index, then we do not
store the pcur position, because any fetch next or prev will anyway store the pcur position, because any fetch next or prev will anyway
...@@ -4624,7 +4708,6 @@ row_search_for_mysql( ...@@ -4624,7 +4708,6 @@ row_search_for_mysql(
next_rec: next_rec:
/* Reset the old and new "did semi-consistent read" flags. */ /* Reset the old and new "did semi-consistent read" flags. */
get_clust_rec = FALSE;
if (UNIV_UNLIKELY(prebuilt->row_read_type if (UNIV_UNLIKELY(prebuilt->row_read_type
== ROW_READ_DID_SEMI_CONSISTENT)) { == ROW_READ_DID_SEMI_CONSISTENT)) {
prebuilt->row_read_type = ROW_READ_TRY_SEMI_CONSISTENT; prebuilt->row_read_type = ROW_READ_TRY_SEMI_CONSISTENT;
...@@ -4635,6 +4718,7 @@ row_search_for_mysql( ...@@ -4635,6 +4718,7 @@ row_search_for_mysql(
/*-------------------------------------------------------------*/ /*-------------------------------------------------------------*/
/* PHASE 5: Move the cursor to the next index record */ /* PHASE 5: Move the cursor to the next index record */
/*TODO: with ICP, do this when switching pages, every N pages */
if (UNIV_UNLIKELY(mtr_has_extra_clust_latch)) { if (UNIV_UNLIKELY(mtr_has_extra_clust_latch)) {
/* We must commit mtr if we are moving to the next /* We must commit mtr if we are moving to the next
non-clustered index record, because we could break the non-clustered index record, because we could break the
......
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