mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
Merge.
This commit is contained in:
@ -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
|
||||
"t.key=21" tuple (the engine should stop scanning and
|
||||
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
|
||||
returned by index_cond_func_xxx
|
||||
*/
|
||||
@ -111,7 +113,8 @@ typedef enum icp_result {
|
||||
ICP_ERROR=-1,
|
||||
ICP_NO_MATCH=0,
|
||||
ICP_MATCH=1,
|
||||
ICP_OUT_OF_RANGE=2
|
||||
ICP_OUT_OF_RANGE=2,
|
||||
ICP_ABORTED_BY_USER=3,
|
||||
} ICP_RESULT;
|
||||
|
||||
|
||||
|
@ -86,6 +86,152 @@ SELECT * FROM t1 WHERE c2 <=> NULL ORDER BY c1,c2 LIMIT 2;
|
||||
--echo
|
||||
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 # Bug#40992 - InnoDB: Crash when engine_condition_pushdown is on
|
||||
--echo #
|
||||
@ -102,6 +248,7 @@ SELECT * FROM t WHERE a > 2 FOR UPDATE;
|
||||
|
||||
DROP TABLE t;
|
||||
|
||||
|
||||
--echo #
|
||||
--echo # Bug#35080 - Innodb crash at mem_block_get_len line 72
|
||||
--echo #
|
||||
@ -226,6 +373,100 @@ SELECT COUNT(*) FROM t3;
|
||||
DROP PROCEDURE insert_data;
|
||||
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 # BUG#778434 Wrong result with in_to_exists=on in maria-5.3-mwl89
|
||||
--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
|
||||
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;
|
||||
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;
|
||||
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');
|
||||
|
@ -83,6 +83,150 @@ c1 c2 c3 c4
|
||||
|
||||
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
|
||||
#
|
||||
CREATE TABLE t (
|
||||
@ -203,6 +347,98 @@ COUNT(*)
|
||||
DROP PROCEDURE insert_data;
|
||||
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
|
||||
#
|
||||
CREATE TABLE t1 ( f11 int) ;
|
||||
|
@ -728,6 +728,7 @@ JA USA
|
||||
DROP TABLE t1,t2;
|
||||
#
|
||||
# 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;
|
||||
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
|
||||
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;
|
||||
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;
|
||||
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');
|
||||
|
@ -83,6 +83,150 @@ c1 c2 c3 c4
|
||||
|
||||
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
|
||||
#
|
||||
CREATE TABLE t (
|
||||
@ -203,6 +347,104 @@ COUNT(*)
|
||||
DROP PROCEDURE insert_data;
|
||||
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
|
||||
#
|
||||
CREATE TABLE t1 ( f11 int) ;
|
||||
|
@ -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
|
||||
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
|
||||
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;
|
||||
FieldKey LongVal StringVal
|
||||
3 1 2
|
||||
|
@ -79,6 +79,150 @@ c1 c2 c3 c4
|
||||
|
||||
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
|
||||
#
|
||||
CREATE TABLE t (
|
||||
@ -199,6 +343,104 @@ COUNT(*)
|
||||
DROP PROCEDURE insert_data;
|
||||
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
|
||||
#
|
||||
CREATE TABLE t1 ( f11 int) ;
|
||||
|
@ -365,7 +365,7 @@ update t1 set b=repeat(char(65+a), 20) where a < 25;
|
||||
This must show range + using index condition:
|
||||
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
|
||||
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);
|
||||
a b filler
|
||||
0 AAAAAAAAAAAAAAAAAAAA filler
|
||||
|
@ -420,6 +420,7 @@ DROP TABLE t1,t2;
|
||||
|
||||
--echo #
|
||||
--echo # Testcase backport: Bug#43249
|
||||
--echo # (Note: Fixed by patch for BUG#42580)
|
||||
--echo #
|
||||
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');
|
||||
|
@ -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 */
|
||||
sort_form->read_set= &sort_form->tmp_set;
|
||||
register_used_fields(param);
|
||||
if (select && select->cond)
|
||||
select->cond->walk(&Item::register_field_in_read_map, 1,
|
||||
(uchar*) sort_form);
|
||||
if (select && select->pre_idx_push_select_cond)
|
||||
select->pre_idx_push_select_cond->walk(&Item::register_field_in_read_map,
|
||||
1, (uchar*) sort_form);
|
||||
Item *sort_cond= !select ?
|
||||
0 : !select->pre_idx_push_select_cond ?
|
||||
select->cond : select->pre_idx_push_select_cond;
|
||||
if (sort_cond)
|
||||
sort_cond->walk(&Item::register_field_in_read_map, 1, (uchar*) sort_form);
|
||||
sort_form->column_bitmaps_set(&sort_form->tmp_set, &sort_form->tmp_set,
|
||||
&sort_form->tmp_set);
|
||||
|
||||
|
@ -1723,6 +1723,7 @@ public:
|
||||
DBUG_ENTER("ha_rnd_init");
|
||||
DBUG_ASSERT(inited==NONE || (inited==RND && scan));
|
||||
inited= (result= rnd_init(scan)) ? NONE: RND;
|
||||
end_range= NULL;
|
||||
DBUG_RETURN(result);
|
||||
}
|
||||
int ha_rnd_end()
|
||||
@ -1730,6 +1731,7 @@ public:
|
||||
DBUG_ENTER("ha_rnd_end");
|
||||
DBUG_ASSERT(inited==RND);
|
||||
inited=NONE;
|
||||
end_range= NULL;
|
||||
DBUG_RETURN(rnd_end());
|
||||
}
|
||||
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,
|
||||
case 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;
|
||||
/*
|
||||
The below is probably a repetition - the first part checks the
|
||||
other two, but let's play it safe:
|
||||
*/
|
||||
return item_field->field->part_of_key.is_set(keyno) &&
|
||||
item_field->field->type() != MYSQL_TYPE_GEOMETRY &&
|
||||
item_field->field->type() != MYSQL_TYPE_BLOB;
|
||||
if(!field->part_of_key.is_set(keyno) ||
|
||||
field->type() == MYSQL_TYPE_GEOMETRY ||
|
||||
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:
|
||||
return uses_index_fields_only(item->real_item(), tbl, keyno,
|
||||
@ -288,30 +317,12 @@ void push_index_cond(JOIN_TAB *tab, uint keyno)
|
||||
{
|
||||
DBUG_ENTER("push_index_cond");
|
||||
Item *idx_cond;
|
||||
bool do_index_cond_pushdown=
|
||||
((tab->table->file->index_flags(keyno, 0, 1) &
|
||||
|
||||
if ((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)
|
||||
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",
|
||||
print_where(tab->select_cond, "full cond", QT_ORDINARY););
|
||||
|
@ -472,10 +472,12 @@ Looks for column n in an index.
|
||||
ULINT_UNDEFINED if not contained */
|
||||
UNIV_INTERN
|
||||
ulint
|
||||
dict_index_get_nth_col_pos(
|
||||
/*=======================*/
|
||||
dict_index_get_nth_col_or_prefix_pos(
|
||||
/*=================================*/
|
||||
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_col_t* col;
|
||||
@ -497,7 +499,8 @@ dict_index_get_nth_col_pos(
|
||||
for (pos = 0; pos < n_fields; 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);
|
||||
}
|
||||
|
@ -126,11 +126,6 @@ static pthread_cond_t commit_cond;
|
||||
static pthread_mutex_t commit_cond_m;
|
||||
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
|
||||
|
||||
@ -2008,14 +2003,21 @@ trx_is_strict(
|
||||
/**************************************************************//**
|
||||
Resets some fields of a prebuilt struct. The template is used in fast
|
||||
retrieval of just those column values MySQL needs in its processing. */
|
||||
static
|
||||
void
|
||||
reset_template(
|
||||
/*===========*/
|
||||
row_prebuilt_t* prebuilt) /*!< in/out: prebuilt struct */
|
||||
inline
|
||||
ha_innobase::reset_template(void)
|
||||
/*=============================*/
|
||||
{
|
||||
ut_ad(prebuilt->magic_n == ROW_PREBUILT_ALLOCATED);
|
||||
ut_ad(prebuilt->magic_n2 == prebuilt->magic_n);
|
||||
|
||||
prebuilt->keep_other_fields_on_keyread = 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)
|
||||
we???? */
|
||||
|
||||
prebuilt->used_in_HANDLER = TRUE;
|
||||
reset_template(prebuilt);
|
||||
reset_template();
|
||||
}
|
||||
|
||||
/*********************************************************************//**
|
||||
@ -4203,8 +4205,8 @@ static inline
|
||||
uint
|
||||
get_field_offset(
|
||||
/*=============*/
|
||||
TABLE* table, /*!< in: MySQL table object */
|
||||
Field* field) /*!< in: MySQL field object */
|
||||
const TABLE* table, /*!< in: MySQL table object */
|
||||
const Field* field) /*!< in: MySQL field object */
|
||||
{
|
||||
return((uint) (field->ptr - table->record[0]));
|
||||
}
|
||||
@ -4766,45 +4768,171 @@ ha_innobase::store_key_val_for_row(
|
||||
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
|
||||
retrieval of just those column values MySQL needs in its processing. */
|
||||
static
|
||||
UNIV_INTERN
|
||||
void
|
||||
build_template(
|
||||
/*===========*/
|
||||
row_prebuilt_t* prebuilt, /*!< in/out: prebuilt struct */
|
||||
THD* thd, /*!< in: current user thread, used
|
||||
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 */
|
||||
ha_innobase::build_template(
|
||||
/*========================*/
|
||||
bool whole_row) /*!< in: true=ROW_MYSQL_WHOLE_ROW,
|
||||
false=ROW_MYSQL_REC_FIELDS */
|
||||
{
|
||||
dict_index_t* index;
|
||||
dict_index_t* clust_index;
|
||||
mysql_row_templ_t* templ;
|
||||
Field* field;
|
||||
ulint n_fields, n_stored_fields;
|
||||
ulint n_requested_fields = 0;
|
||||
ibool fetch_all_in_key = FALSE;
|
||||
ibool fetch_primary_key_cols = FALSE;
|
||||
ulint sql_idx, innodb_idx=0;
|
||||
/* 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;
|
||||
ulint i, sql_idx;
|
||||
|
||||
if (prebuilt->select_lock_type == LOCK_X) {
|
||||
/* We always retrieve the whole clustered index record if we
|
||||
use exclusive row level locks, for example, if the read is
|
||||
done in an UPDATE statement. */
|
||||
|
||||
templ_type = ROW_MYSQL_WHOLE_ROW;
|
||||
}
|
||||
|
||||
if (templ_type == ROW_MYSQL_REC_FIELDS) {
|
||||
whole_row = true;
|
||||
} else if (!whole_row) {
|
||||
if (prebuilt->hint_need_to_fetch_extra_cols
|
||||
== ROW_RETRIEVE_ALL_COLS) {
|
||||
|
||||
@ -4821,7 +4949,7 @@ build_template(
|
||||
|
||||
fetch_all_in_key = TRUE;
|
||||
} else {
|
||||
templ_type = ROW_MYSQL_WHOLE_ROW;
|
||||
whole_row = true;
|
||||
}
|
||||
} else if (prebuilt->hint_need_to_fetch_extra_cols
|
||||
== ROW_RETRIEVE_PRIMARY_KEY) {
|
||||
@ -4838,19 +4966,12 @@ build_template(
|
||||
|
||||
clust_index = dict_table_get_first_index(prebuilt->table);
|
||||
|
||||
if (templ_type == ROW_MYSQL_REC_FIELDS) {
|
||||
index = prebuilt->index;
|
||||
} else {
|
||||
index = clust_index;
|
||||
}
|
||||
index = whole_row ? clust_index : prebuilt->index;
|
||||
|
||||
prebuilt->need_to_access_clustered = (index == clust_index);
|
||||
|
||||
if (index == clust_index) {
|
||||
prebuilt->need_to_access_clustered = TRUE;
|
||||
} else {
|
||||
prebuilt->need_to_access_clustered = FALSE;
|
||||
/* Below we check column by column if we need to access
|
||||
the clustered index */
|
||||
}
|
||||
the clustered index. */
|
||||
|
||||
n_fields = (ulint)table->s->fields; /* number of columns */
|
||||
n_stored_fields= (ulint)table->s->stored_fields; /* number of stored columns */
|
||||
@ -4860,160 +4981,206 @@ build_template(
|
||||
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;
|
||||
|
||||
/* Prepare to build prebuilt->mysql_template[]. */
|
||||
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'. */
|
||||
|
||||
/*
|
||||
Setup index condition pushdown (note: we don't need to check if
|
||||
this is a scan on primary key as that is checked in idx_cond_push)
|
||||
*/
|
||||
if (file->active_index == file->pushed_idx_cond_keyno &&
|
||||
file->active_index != MAX_KEY &&
|
||||
templ_type == ROW_MYSQL_REC_FIELDS)
|
||||
do_idx_cond_push= need_second_pass= TRUE;
|
||||
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++) {
|
||||
|
||||
/* Note that in InnoDB, i is the column number. MySQL calls columns
|
||||
'fields'. */
|
||||
for (sql_idx = 0; sql_idx < n_fields; sql_idx++) {
|
||||
templ = prebuilt->mysql_template + n_requested_fields;
|
||||
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];
|
||||
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
|
||||
&& dict_table_col_in_clustered_key(
|
||||
index->table, innodb_idx)) {
|
||||
/* This field is needed in the query */
|
||||
|
||||
goto include_field;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
templ->clust_rec_field_no = dict_col_get_clust_pos(
|
||||
&index->table->cols[innodb_idx], clust_index);
|
||||
ut_ad(templ->clust_rec_field_no != ULINT_UNDEFINED);
|
||||
|
||||
if (index == clust_index) {
|
||||
templ->rec_field_no = templ->clust_rec_field_no;
|
||||
} else {
|
||||
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;
|
||||
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 (field->null_ptr) {
|
||||
templ->mysql_null_byte_offset =
|
||||
(ulint) ((char*) field->null_ptr
|
||||
- (char*) table->record[0]);
|
||||
templ = build_template_field(
|
||||
prebuilt, clust_index, index,
|
||||
table, field, i);
|
||||
prebuilt->idx_cond_n_cols++;
|
||||
ut_ad(prebuilt->idx_cond_n_cols
|
||||
== prebuilt->n_template);
|
||||
|
||||
templ->mysql_null_bit_mask = (ulint) field->null_bit;
|
||||
if (index == prebuilt->index) {
|
||||
templ->icp_rec_field_no
|
||||
= templ->rec_field_no;
|
||||
} else {
|
||||
templ->mysql_null_bit_mask = 0;
|
||||
templ->icp_rec_field_no
|
||||
= dict_index_get_nth_col_pos(
|
||||
prebuilt->index, i);
|
||||
}
|
||||
|
||||
templ->mysql_col_offset = (ulint)
|
||||
get_field_offset(table, field);
|
||||
|
||||
templ->mysql_col_len = (ulint) field->pack_length();
|
||||
if (mysql_prefix_len < templ->mysql_col_offset
|
||||
+ templ->mysql_col_len) {
|
||||
mysql_prefix_len = templ->mysql_col_offset
|
||||
+ templ->mysql_col_len;
|
||||
if (dict_index_is_clust(prebuilt->index)) {
|
||||
ut_ad(templ->icp_rec_field_no
|
||||
!= ULINT_UNDEFINED);
|
||||
/* 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;
|
||||
}
|
||||
templ->type = index->table->cols[innodb_idx].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);
|
||||
} else if (templ->icp_rec_field_no
|
||||
!= ULINT_UNDEFINED) {
|
||||
continue;
|
||||
}
|
||||
|
||||
templ->charset = dtype_get_charset_coll(
|
||||
index->table->cols[innodb_idx].prtype);
|
||||
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++;
|
||||
/* 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);
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
prebuilt->n_template = n_requested_fields;
|
||||
prebuilt->mysql_prefix_len = mysql_prefix_len;
|
||||
ut_ad(prebuilt->idx_cond_n_cols > 0);
|
||||
ut_ad(prebuilt->idx_cond_n_cols == prebuilt->n_template);
|
||||
|
||||
if (do_idx_cond_push)
|
||||
{
|
||||
prebuilt->idx_cond_func= index_cond_func_innodb;
|
||||
prebuilt->idx_cond_func_arg= file;
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
|
||||
build_template_field(prebuilt,
|
||||
clust_index, index,
|
||||
table, field, i);
|
||||
}
|
||||
}
|
||||
|
||||
prebuilt->idx_cond = this;
|
||||
} else {
|
||||
/* No index condition pushdown */
|
||||
prebuilt->idx_cond = NULL;
|
||||
|
||||
for (i = 0, sql_idx = 0; i < n_stored_fields; i++, sql_idx++) {
|
||||
const Field* field;
|
||||
|
||||
while (!table->field[sql_idx]->stored_in_db) {
|
||||
sql_idx++;
|
||||
}
|
||||
|
||||
if (whole_row) {
|
||||
field = table->field[sql_idx];
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
||||
build_template_field(prebuilt, clust_index, index,
|
||||
table, field, i);
|
||||
}
|
||||
else
|
||||
{
|
||||
prebuilt->idx_cond_func= NULL;
|
||||
prebuilt->n_index_fields= n_requested_fields;
|
||||
}
|
||||
|
||||
if (index != clust_index && prebuilt->need_to_access_clustered) {
|
||||
/* Change rec_field_no's to correspond to the clustered index
|
||||
record */
|
||||
for (ulint i = do_idx_cond_push? prebuilt->n_index_fields : 0;
|
||||
i < n_requested_fields; i++) {
|
||||
templ = prebuilt->mysql_template + i;
|
||||
for (i = 0; i < prebuilt->n_template; i++) {
|
||||
mysql_row_templ_t* templ
|
||||
= &prebuilt->mysql_template[i];
|
||||
templ->rec_field_no = templ->clust_rec_field_no;
|
||||
}
|
||||
}
|
||||
@ -5276,7 +5443,7 @@ no_commit:
|
||||
/* Build the template used in converting quickly between
|
||||
the two database formats */
|
||||
|
||||
build_template(prebuilt, NULL, table, this, ROW_MYSQL_WHOLE_ROW);
|
||||
build_template(true);
|
||||
}
|
||||
|
||||
innodb_srv_conc_enter_innodb(prebuilt->trx);
|
||||
@ -5977,8 +6144,7 @@ ha_innobase::index_read(
|
||||
necessarily prebuilt->index, but can also be the clustered index */
|
||||
|
||||
if (prebuilt->sql_stat_start) {
|
||||
build_template(prebuilt, user_thd, table, this,
|
||||
ROW_MYSQL_REC_FIELDS);
|
||||
build_template(false);
|
||||
}
|
||||
|
||||
if (key_ptr) {
|
||||
@ -6193,7 +6359,7 @@ ha_innobase::change_active_index(
|
||||
the flag ROW_MYSQL_WHOLE_ROW below, but that caused unnecessary
|
||||
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);
|
||||
}
|
||||
@ -8588,7 +8754,7 @@ ha_innobase::check(
|
||||
/* Build the template; we will use a dummy template
|
||||
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) {
|
||||
@ -9080,12 +9246,7 @@ ha_innobase::extra(
|
||||
}
|
||||
break;
|
||||
case HA_EXTRA_RESET_STATE:
|
||||
reset_template(prebuilt);
|
||||
/* 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;
|
||||
reset_template();
|
||||
break;
|
||||
case HA_EXTRA_NO_KEYREAD:
|
||||
prebuilt->read_just_key = 0;
|
||||
@ -9131,14 +9292,8 @@ ha_innobase::reset()
|
||||
row_mysql_prebuilt_free_blob_heap(prebuilt);
|
||||
}
|
||||
|
||||
reset_template(prebuilt);
|
||||
|
||||
/* Reset index condition pushdown state */
|
||||
pushed_idx_cond_keyno= MAX_KEY;
|
||||
pushed_idx_cond= NULL;
|
||||
in_range_check_pushed_down= FALSE;
|
||||
reset_template();
|
||||
ds_mrr.dsmrr_close();
|
||||
prebuilt->idx_cond_func= NULL;
|
||||
|
||||
/* TODO: This should really be reset in reset_template() but for now
|
||||
it's safer to do it explicitly here. */
|
||||
@ -9188,7 +9343,7 @@ ha_innobase::start_stmt(
|
||||
|
||||
prebuilt->sql_stat_start = TRUE;
|
||||
prebuilt->hint_need_to_fetch_extra_cols = 0;
|
||||
reset_template(prebuilt);
|
||||
reset_template();
|
||||
|
||||
if (!prebuilt->mysql_has_locked) {
|
||||
/* This handle is for a temporary table created inside
|
||||
@ -9307,7 +9462,7 @@ ha_innobase::external_lock(
|
||||
prebuilt->sql_stat_start = TRUE;
|
||||
prebuilt->hint_need_to_fetch_extra_cols = 0;
|
||||
|
||||
reset_template(prebuilt);
|
||||
reset_template();
|
||||
|
||||
if (lock_type == F_WRLCK) {
|
||||
|
||||
@ -9490,7 +9645,7 @@ ha_innobase::transactional_table_lock(
|
||||
prebuilt->sql_stat_start = TRUE;
|
||||
prebuilt->hint_need_to_fetch_extra_cols = 0;
|
||||
|
||||
reset_template(prebuilt);
|
||||
reset_template();
|
||||
|
||||
if (lock_type == F_WRLCK) {
|
||||
prebuilt->select_lock_type = LOCK_X;
|
||||
@ -12306,39 +12461,47 @@ bool ha_innobase::is_thd_killed()
|
||||
* Index Condition Pushdown interface implementation
|
||||
*/
|
||||
|
||||
C_MODE_START
|
||||
|
||||
/*
|
||||
Index condition check function to be called from within Innobase.
|
||||
See note on ICP_RESULT for return values description.
|
||||
*/
|
||||
|
||||
static xtradb_icp_result_t index_cond_func_innodb(void *arg)
|
||||
/*************************************************************//**
|
||||
InnoDB index push-down condition check
|
||||
@return ICP_NO_MATCH, ICP_MATCH, or ICP_OUT_OF_RANGE */
|
||||
extern "C" UNIV_INTERN
|
||||
enum icp_result
|
||||
innobase_index_cond(
|
||||
/*================*/
|
||||
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())
|
||||
return XTRADB_ICP_ABORTED_BY_USER;
|
||||
return ICP_ABORTED_BY_USER;
|
||||
|
||||
if (h->end_range)
|
||||
{
|
||||
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
|
||||
|
||||
|
||||
Item *ha_innobase::idx_cond_push(uint keyno_arg, Item* idx_cond_arg)
|
||||
/** Attempt to push down an index condition.
|
||||
* @param[in] keyno MySQL key number
|
||||
* @param[in] idx_cond Index condition to be checked
|
||||
* @return idx_cond if pushed; NULL if not pushed
|
||||
*/
|
||||
UNIV_INTERN
|
||||
class Item*
|
||||
ha_innobase::idx_cond_push(
|
||||
uint keyno,
|
||||
class Item* idx_cond)
|
||||
{
|
||||
if (keyno_arg != primary_key && prebuilt->select_lock_type != LOCK_X)
|
||||
{
|
||||
pushed_idx_cond_keyno= keyno_arg;
|
||||
pushed_idx_cond= idx_cond_arg;
|
||||
DBUG_ENTER("ha_innobase::idx_cond_push");
|
||||
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;
|
||||
return NULL; /* Table handler will check the entire condition */
|
||||
}
|
||||
return idx_cond_arg; /* Table handler will not make any checks */
|
||||
/* Table handler will check the entire condition */
|
||||
DBUG_RETURN(NULL);
|
||||
}
|
||||
|
||||
|
@ -223,27 +223,81 @@ class ha_innobase: public handler
|
||||
bool check_if_incompatible_data(HA_CREATE_INFO *info,
|
||||
uint table_changes);
|
||||
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:
|
||||
/**
|
||||
* Multi Range Read interface
|
||||
/** @name Multi Range Read interface @{ */
|
||||
/** Initialize multi range read @see DsMrr_impl::dsmrr_init
|
||||
* @param seq
|
||||
* @param seq_init_param
|
||||
* @param n_ranges
|
||||
* @param mode
|
||||
* @param buf
|
||||
*/
|
||||
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_init(RANGE_SEQ_IF *seq, void *seq_init_param,
|
||||
uint n_ranges, uint mode, HANDLER_BUFFER *buf);
|
||||
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);
|
||||
int multi_range_read_explain_info(uint mrr_mode, char *str, size_t size);
|
||||
DsMrr_impl ds_mrr;
|
||||
int multi_range_read_explain_info(uint mrr_mode,
|
||||
char *str, size_t size);
|
||||
|
||||
Item *idx_cond_push(uint keyno, Item* idx_cond);
|
||||
/** Attempt to push down an index condition.
|
||||
* @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();
|
||||
|
||||
private:
|
||||
/** The multi range read session object */
|
||||
DsMrr_impl ds_mrr;
|
||||
|
||||
/* @} */
|
||||
};
|
||||
|
||||
/* Some accessor functions which the InnoDB plugin needs, but which
|
||||
|
@ -839,6 +839,18 @@ dict_index_get_nth_col_pos(
|
||||
const dict_index_t* index, /*!< in: index */
|
||||
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.
|
||||
@return TRUE if contains the column or its prefix */
|
||||
UNIV_INTERN
|
||||
|
@ -656,6 +656,20 @@ dict_index_get_nth_col_no(
|
||||
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
|
||||
/********************************************************************//**
|
||||
Returns the minimum data size of an index record.
|
||||
|
@ -247,6 +247,15 @@ innobase_get_at_most_n_mbchars(
|
||||
ulint data_len, /*!< in: length of the string in bytes */
|
||||
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,
|
||||
global value of innodb_supports_xa if thd is NULL.
|
||||
|
@ -547,6 +547,10 @@ struct mysql_row_templ_struct {
|
||||
Innobase record in the clustered index;
|
||||
not defined if template_type is
|
||||
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
|
||||
row format */
|
||||
ulint mysql_col_len; /*!< length of the column in the MySQL
|
||||
@ -585,16 +589,6 @@ struct mysql_row_templ_struct {
|
||||
#define ROW_PREBUILT_ALLOCATED 78540783
|
||||
#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
|
||||
|
||||
handle used within MySQL; these are used to save CPU time. */
|
||||
@ -792,16 +786,15 @@ struct row_prebuilt_struct {
|
||||
store it here so that we can return
|
||||
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
|
||||
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
|
||||
|
@ -58,6 +58,8 @@ Created 12/19/1997 Heikki Tuuri
|
||||
#include "buf0lru.h"
|
||||
#include "ha_prototypes.h"
|
||||
|
||||
#include "my_compare.h" /* enum icp_result */
|
||||
|
||||
/* Maximum number of rows to prefetch; MySQL interface has another parameter */
|
||||
#define SEL_MAX_N_PREFETCH 16
|
||||
|
||||
@ -2673,6 +2675,147 @@ row_sel_field_store_in_mysql_format(
|
||||
}
|
||||
}
|
||||
|
||||
/**************************************************************//**
|
||||
Convert a field in the Innobase format to a field in the MySQL format. */
|
||||
static __attribute__((warn_unused_result))
|
||||
ibool
|
||||
row_sel_store_mysql_field(
|
||||
/*======================*/
|
||||
byte* mysql_rec, /*!< out: record in the
|
||||
MySQL format */
|
||||
row_prebuilt_t* prebuilt, /*!< in/out: prebuilt struct */
|
||||
const rec_t* rec, /*!< in: InnoDB record;
|
||||
must be protected by
|
||||
a page latch */
|
||||
const ulint* offsets, /*!< in: array returned by
|
||||
rec_get_offsets() */
|
||||
ulint field_no, /*!< in: templ->rec_field_no or
|
||||
templ->clust_rec_field_no */
|
||||
const mysql_row_templ_t*templ) /*!< in: row template */
|
||||
{
|
||||
const byte* data;
|
||||
ulint len;
|
||||
|
||||
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));
|
||||
|
||||
if (UNIV_UNLIKELY(rec_offs_nth_extern(offsets, field_no))) {
|
||||
|
||||
mem_heap_t* heap;
|
||||
/* Copy an externally stored field to a temporary heap */
|
||||
|
||||
ut_a(!prebuilt->trx->has_search_latch);
|
||||
ut_ad(field_no == templ->clust_rec_field_no);
|
||||
|
||||
if (UNIV_UNLIKELY(templ->type == DATA_BLOB)) {
|
||||
if (prebuilt->blob_heap == NULL) {
|
||||
prebuilt->blob_heap = mem_heap_create(
|
||||
UNIV_PAGE_SIZE);
|
||||
}
|
||||
|
||||
heap = prebuilt->blob_heap;
|
||||
} else {
|
||||
heap = mem_heap_create(UNIV_PAGE_SIZE);
|
||||
}
|
||||
|
||||
/* NOTE: if we are retrieving a big BLOB, we may
|
||||
already run out of memory in the next call, which
|
||||
causes an assert */
|
||||
|
||||
data = btr_rec_copy_externally_stored_field(
|
||||
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 (heap != prebuilt->blob_heap) {
|
||||
mem_heap_free(heap);
|
||||
}
|
||||
|
||||
ut_a(prebuilt->trx->isolation_level
|
||||
== TRX_ISO_READ_UNCOMMITTED);
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
ut_a(len != UNIV_SQL_NULL);
|
||||
|
||||
row_sel_field_store_in_mysql_format(
|
||||
mysql_rec + templ->mysql_col_offset,
|
||||
templ, data, len);
|
||||
|
||||
if (heap != prebuilt->blob_heap) {
|
||||
mem_heap_free(heap);
|
||||
}
|
||||
} else {
|
||||
/* Field is stored in the row. */
|
||||
|
||||
data = rec_get_nth_field(rec, offsets, field_no, &len);
|
||||
|
||||
if (len == UNIV_SQL_NULL) {
|
||||
/* MySQL assumes that the field for an SQL
|
||||
NULL value is set to the default value. */
|
||||
ut_ad(templ->mysql_null_bit_mask);
|
||||
|
||||
UNIV_MEM_ASSERT_RW(prebuilt->default_rec
|
||||
+ templ->mysql_col_offset,
|
||||
templ->mysql_col_len);
|
||||
mysql_rec[templ->mysql_null_byte_offset]
|
||||
|= (byte) templ->mysql_null_bit_mask;
|
||||
memcpy(mysql_rec + templ->mysql_col_offset,
|
||||
(const byte*) prebuilt->default_rec
|
||||
+ templ->mysql_col_offset,
|
||||
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
|
||||
@ -2692,137 +2835,27 @@ row_sel_store_mysql_rec(
|
||||
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() */
|
||||
ulint start_field_no, /* in: start from this field */
|
||||
ulint end_field_no) /* in: end at this field */
|
||||
const ulint* offsets) /*!< in: array returned by
|
||||
rec_get_offsets(rec) */
|
||||
{
|
||||
mem_heap_t* extern_field_heap = NULL;
|
||||
mem_heap_t* heap;
|
||||
ulint i;
|
||||
|
||||
ut_ad(prebuilt->mysql_template);
|
||||
ut_ad(prebuilt->default_rec);
|
||||
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)) {
|
||||
mem_heap_free(prebuilt->blob_heap);
|
||||
prebuilt->blob_heap = NULL;
|
||||
}
|
||||
|
||||
for (i = start_field_no; i < end_field_no /* prebuilt->n_template */ ; i++) {
|
||||
for (i = 0; i < prebuilt->n_template; i++) {
|
||||
const mysql_row_templ_t*templ = &prebuilt->mysql_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))) {
|
||||
|
||||
/* Copy an externally stored field to the temporary
|
||||
heap */
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
heap = prebuilt->blob_heap;
|
||||
} else {
|
||||
extern_field_heap
|
||||
= mem_heap_create(UNIV_PAGE_SIZE);
|
||||
|
||||
heap = extern_field_heap;
|
||||
}
|
||||
|
||||
/* NOTE: if we are retrieving a big BLOB, we may
|
||||
already run out of memory in the next call, which
|
||||
causes an assert */
|
||||
|
||||
data = btr_rec_copy_externally_stored_field(
|
||||
if (!row_sel_store_mysql_field(mysql_rec, prebuilt,
|
||||
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);
|
||||
}
|
||||
|
||||
rec_clust
|
||||
? templ->clust_rec_field_no
|
||||
: templ->rec_field_no,
|
||||
templ)) {
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
ut_a(len != UNIV_SQL_NULL);
|
||||
} else {
|
||||
/* Field is stored in the row. */
|
||||
|
||||
data = rec_get_nth_field(rec, offsets, field_no, &len);
|
||||
|
||||
if (UNIV_UNLIKELY(templ->type == DATA_BLOB)
|
||||
&& len != UNIV_SQL_NULL) {
|
||||
|
||||
/* It is a BLOB field locally stored in the
|
||||
InnoDB record: we MUST copy its contents to
|
||||
prebuilt->blob_heap here because later code
|
||||
assumes all BLOB values have been copied to a
|
||||
safe place. */
|
||||
|
||||
if (prebuilt->blob_heap == NULL) {
|
||||
prebuilt->blob_heap = mem_heap_create(
|
||||
UNIV_PAGE_SIZE);
|
||||
}
|
||||
|
||||
data = memcpy(mem_heap_alloc(
|
||||
prebuilt->blob_heap, len),
|
||||
data, len);
|
||||
}
|
||||
}
|
||||
|
||||
if (len != UNIV_SQL_NULL) {
|
||||
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) {
|
||||
/* 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
|
||||
NULL value is set to the default value. */
|
||||
|
||||
UNIV_MEM_ASSERT_RW(prebuilt->default_rec
|
||||
+ templ->mysql_col_offset,
|
||||
templ->mysql_col_len);
|
||||
mysql_rec[templ->mysql_null_byte_offset]
|
||||
|= (byte) templ->mysql_null_bit_mask;
|
||||
memcpy(mysql_rec + templ->mysql_col_offset,
|
||||
(const byte*) prebuilt->default_rec
|
||||
+ templ->mysql_col_offset,
|
||||
templ->mysql_col_len);
|
||||
}
|
||||
}
|
||||
|
||||
return(TRUE);
|
||||
@ -3192,31 +3225,19 @@ UNIV_INLINE __attribute__((warn_unused_result))
|
||||
ibool
|
||||
row_sel_push_cache_row_for_mysql(
|
||||
/*=============================*/
|
||||
row_prebuilt_t* prebuilt, /*!< in: prebuilt struct */
|
||||
const rec_t* rec, /*!< in: record to push, 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: 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* mysql_rec, /*!< in/out: MySQL record */
|
||||
row_prebuilt_t* prebuilt) /*!< in/out: prebuilt struct */
|
||||
{
|
||||
byte* buf;
|
||||
ulint i;
|
||||
|
||||
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);
|
||||
|
||||
if (prebuilt->fetch_cache[0] == NULL) {
|
||||
if (UNIV_UNLIKELY(prebuilt->fetch_cache[0] == NULL)) {
|
||||
ulint i;
|
||||
/* Allocate memory for the fetch cache */
|
||||
ut_ad(prebuilt->n_fetch_cached == 0);
|
||||
|
||||
for (i = 0; i < MYSQL_FETCH_CACHE_SIZE; i++) {
|
||||
byte* buf;
|
||||
|
||||
/* A user has reported memory corruption in these
|
||||
buffers in Linux. Put magic numbers there to help
|
||||
@ -3236,46 +3257,14 @@ row_sel_push_cache_row_for_mysql(
|
||||
UNIV_MEM_INVALID(prebuilt->fetch_cache[prebuilt->n_fetch_cached],
|
||||
prebuilt->mysql_row_len);
|
||||
|
||||
if (UNIV_UNLIKELY(!row_sel_store_mysql_rec(
|
||||
prebuilt->fetch_cache[
|
||||
prebuilt->n_fetch_cached],
|
||||
prebuilt,
|
||||
rec,
|
||||
rec_clust,
|
||||
offsets,
|
||||
start_field_no,
|
||||
prebuilt->n_template))) {
|
||||
memcpy(prebuilt->fetch_cache[prebuilt->n_fetch_cached],
|
||||
mysql_rec, prebuilt->mysql_row_len);
|
||||
|
||||
if (++prebuilt->n_fetch_cached < MYSQL_FETCH_CACHE_SIZE) {
|
||||
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) {
|
||||
offs = templ->mysql_null_byte_offset;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@ -3353,6 +3342,81 @@ row_sel_try_search_shortcut_for_mysql(
|
||||
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
|
||||
MySQL. This function opens a cursor, and also implements fetch next
|
||||
@ -3415,10 +3479,8 @@ row_search_for_mysql(
|
||||
mem_heap_t* heap = NULL;
|
||||
ulint offsets_[REC_OFFS_NORMAL_SIZE];
|
||||
ulint* offsets = offsets_;
|
||||
ibool some_fields_in_buffer;
|
||||
ibool table_lock_waited = FALSE;
|
||||
ibool problematic_use = FALSE;
|
||||
ibool get_clust_rec = 0;
|
||||
|
||||
rec_offs_init(offsets_);
|
||||
|
||||
@ -3681,10 +3743,24 @@ row_search_for_mysql(
|
||||
mtr_commit(&mtr). */
|
||||
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,
|
||||
rec, FALSE,
|
||||
offsets, 0,
|
||||
prebuilt->n_template)) {
|
||||
offsets)) {
|
||||
/* Only fresh inserts may contain
|
||||
incomplete externally stored
|
||||
columns. Pretend that such
|
||||
@ -3695,13 +3771,12 @@ row_search_for_mysql(
|
||||
rolling back a recovered
|
||||
transaction. Rollback happens
|
||||
at a lower level, not here. */
|
||||
ut_a(trx->isolation_level
|
||||
== TRX_ISO_READ_UNCOMMITTED);
|
||||
|
||||
/* Proceed as in case SEL_RETRY. */
|
||||
break;
|
||||
}
|
||||
|
||||
shortcut_match:
|
||||
mtr_commit(&mtr);
|
||||
|
||||
/* ut_print_name(stderr, index->name);
|
||||
@ -3713,6 +3788,7 @@ row_search_for_mysql(
|
||||
goto release_search_latch_if_needed;
|
||||
|
||||
case SEL_EXHAUSTED:
|
||||
shortcut_mismatch:
|
||||
mtr_commit(&mtr);
|
||||
|
||||
/* ut_print_name(stderr, index->name);
|
||||
@ -3804,8 +3880,9 @@ retry_check:
|
||||
if (!prebuilt->sql_stat_start) {
|
||||
/* No need to set an intention lock or assign a read view */
|
||||
|
||||
if (trx->read_view == NULL
|
||||
&& prebuilt->select_lock_type == LOCK_NONE) {
|
||||
if (UNIV_UNLIKELY
|
||||
(trx->read_view == NULL
|
||||
&& prebuilt->select_lock_type == LOCK_NONE)) {
|
||||
|
||||
fputs("InnoDB: Error: MySQL is trying to"
|
||||
" perform a consistent read\n"
|
||||
@ -4265,19 +4342,6 @@ no_gap_lock:
|
||||
if (UNIV_LIKELY(trx->wait_lock != NULL)) {
|
||||
lock_cancel_waiting_and_release(
|
||||
trx->wait_lock);
|
||||
} else {
|
||||
mutex_exit(&kernel_mutex);
|
||||
|
||||
/* The lock was granted while we were
|
||||
searching for the last committed version.
|
||||
Do a normal locking read. */
|
||||
|
||||
offsets = rec_get_offsets(rec, index, offsets,
|
||||
ULINT_UNDEFINED,
|
||||
&heap);
|
||||
err = DB_SUCCESS;
|
||||
break;
|
||||
}
|
||||
mutex_exit(&kernel_mutex);
|
||||
|
||||
if (old_vers == NULL) {
|
||||
@ -4288,6 +4352,17 @@ no_gap_lock:
|
||||
|
||||
did_semi_consistent_read = TRUE;
|
||||
rec = old_vers;
|
||||
} else {
|
||||
mutex_exit(&kernel_mutex);
|
||||
|
||||
/* The lock was granted while we were
|
||||
searching for the last committed version.
|
||||
Do a normal locking read. */
|
||||
|
||||
offsets = rec_get_offsets(rec, index, offsets,
|
||||
ULINT_UNDEFINED,
|
||||
&heap);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
||||
@ -4346,8 +4421,27 @@ no_gap_lock:
|
||||
|
||||
if (!lock_sec_rec_cons_read_sees(
|
||||
rec, trx->read_view)) {
|
||||
get_clust_rec = TRUE;
|
||||
goto idx_cond_check;
|
||||
/* We should look at the clustered index.
|
||||
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 @@ no_gap_lock:
|
||||
goto next_rec;
|
||||
}
|
||||
|
||||
|
||||
idx_cond_check:
|
||||
if (prebuilt->idx_cond_func) {
|
||||
int res;
|
||||
ibool ib_res __attribute__ ((unused));
|
||||
ut_ad(prebuilt->template_type != ROW_MYSQL_DUMMY_TEMPLATE);
|
||||
offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &heap);
|
||||
ib_res= row_sel_store_mysql_rec(buf, prebuilt, rec, FALSE,
|
||||
offsets, 0, prebuilt->n_index_fields);
|
||||
/*
|
||||
The above call will fail and return FALSE when requested to
|
||||
store an "externally stored column" (afaiu, a blob). Index
|
||||
Condition Pushdown is not supported for indexes with blob
|
||||
columns, so we should never get this error.
|
||||
*/
|
||||
ut_ad(ib_res);
|
||||
res= prebuilt->idx_cond_func(prebuilt->idx_cond_func_arg);
|
||||
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;
|
||||
/* Check if the record matches the index condition. */
|
||||
switch (row_search_idx_cond_check(buf, prebuilt, rec, offsets)) {
|
||||
case ICP_NO_MATCH:
|
||||
if (did_semi_consistent_read) {
|
||||
row_unlock_for_mysql(prebuilt, TRUE);
|
||||
}
|
||||
/* res == XTRADB_ICP_MATCH */
|
||||
goto next_rec;
|
||||
case ICP_ABORTED_BY_USER:
|
||||
err = DB_SEARCH_ABORTED_BY_USER;
|
||||
goto idx_cond_failed;
|
||||
case ICP_OUT_OF_RANGE:
|
||||
err = DB_RECORD_NOT_FOUND;
|
||||
goto idx_cond_failed;
|
||||
case ICP_MATCH:
|
||||
break;
|
||||
default:
|
||||
ut_error;
|
||||
}
|
||||
|
||||
/* Get the clustered index record if needed, if we did not do the
|
||||
search using the clustered index. */
|
||||
if (get_clust_rec || (index != clust_index
|
||||
&& prebuilt->need_to_access_clustered)) {
|
||||
if (index != clust_index && prebuilt->need_to_access_clustered) {
|
||||
|
||||
requires_clust_rec:
|
||||
ut_ad(index != clust_index);
|
||||
|
||||
/* We use a 'goto' to the preceding label if a consistent
|
||||
read of a secondary index record requires us to look up old
|
||||
@ -4487,6 +4574,19 @@ idx_cond_check:
|
||||
|
||||
result_rec = clust_rec;
|
||||
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 {
|
||||
result_rec = rec;
|
||||
}
|
||||
@ -4520,15 +4620,10 @@ idx_cond_check:
|
||||
are BLOBs in the fields to be fetched. In HANDLER we do
|
||||
not cache rows because there the cursor is a scrollable
|
||||
cursor. */
|
||||
some_fields_in_buffer = (index != clust_index
|
||||
&& prebuilt->idx_cond_func);
|
||||
|
||||
if (!row_sel_push_cache_row_for_mysql(prebuilt, result_rec,
|
||||
result_rec != rec,
|
||||
offsets,
|
||||
some_fields_in_buffer?
|
||||
prebuilt->n_index_fields : 0,
|
||||
buf)) {
|
||||
if (!prebuilt->idx_cond
|
||||
&& !row_sel_store_mysql_rec(buf, prebuilt, result_rec,
|
||||
result_rec != rec, offsets)) {
|
||||
/* Only fresh inserts may contain incomplete
|
||||
externally stored columns. Pretend that such
|
||||
records do not exist. Such records may only be
|
||||
@ -4536,14 +4631,10 @@ idx_cond_check:
|
||||
level or when rolling back a recovered
|
||||
transaction. Rollback happens at a lower
|
||||
level, not here. */
|
||||
ut_a(trx->isolation_level == TRX_ISO_READ_UNCOMMITTED);
|
||||
} else if (prebuilt->n_fetch_cached
|
||||
== MYSQL_FETCH_CACHE_SIZE) {
|
||||
|
||||
goto got_row;
|
||||
}
|
||||
|
||||
goto next_rec;
|
||||
} else if (row_sel_push_cache_row_for_mysql(buf, prebuilt)) {
|
||||
goto next_rec;
|
||||
}
|
||||
} else {
|
||||
if (UNIV_UNLIKELY
|
||||
(prebuilt->template_type == ROW_MYSQL_DUMMY_TEMPLATE)) {
|
||||
@ -4564,15 +4655,11 @@ idx_cond_check:
|
||||
rec_offs_size(offsets));
|
||||
mach_write_to_4(buf,
|
||||
rec_offs_extra_size(offsets) + 4);
|
||||
} else {
|
||||
/* Returning a row to MySQL */
|
||||
|
||||
if (!row_sel_store_mysql_rec(buf, prebuilt, result_rec,
|
||||
result_rec != rec,
|
||||
offsets,
|
||||
prebuilt->idx_cond_func?
|
||||
prebuilt->n_index_fields: 0,
|
||||
prebuilt->n_template)) {
|
||||
} else if (!prebuilt->idx_cond) {
|
||||
/* The record was not yet converted to MySQL format. */
|
||||
if (!row_sel_store_mysql_rec(
|
||||
buf, prebuilt,
|
||||
result_rec, result_rec != rec, offsets)) {
|
||||
/* Only fresh inserts may contain
|
||||
incomplete externally stored
|
||||
columns. Pretend that such records do
|
||||
@ -4581,8 +4668,6 @@ idx_cond_check:
|
||||
isolation level or when rolling back a
|
||||
recovered transaction. Rollback
|
||||
happens at a lower level, not here. */
|
||||
ut_a(trx->isolation_level
|
||||
== TRX_ISO_READ_UNCOMMITTED);
|
||||
goto next_rec;
|
||||
}
|
||||
}
|
||||
@ -4600,7 +4685,6 @@ idx_cond_check:
|
||||
|
||||
/* From this point on, 'offsets' are invalid. */
|
||||
|
||||
got_row:
|
||||
/* 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
|
||||
store the pcur position, because any fetch next or prev will anyway
|
||||
@ -4624,7 +4708,6 @@ idx_cond_failed:
|
||||
|
||||
next_rec:
|
||||
/* Reset the old and new "did semi-consistent read" flags. */
|
||||
get_clust_rec = FALSE;
|
||||
if (UNIV_UNLIKELY(prebuilt->row_read_type
|
||||
== ROW_READ_DID_SEMI_CONSISTENT)) {
|
||||
prebuilt->row_read_type = ROW_READ_TRY_SEMI_CONSISTENT;
|
||||
@ -4635,6 +4718,7 @@ next_rec:
|
||||
/*-------------------------------------------------------------*/
|
||||
/* 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)) {
|
||||
/* We must commit mtr if we are moving to the next
|
||||
non-clustered index record, because we could break the
|
||||
|
Reference in New Issue
Block a user