mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 03:52:35 +01:00
Bug#13694811: THE OPTIMIZER WRONGLY USES THE FIRST INNODB
PARTITION STATISTICS Problem was the fix for bug#11756867; It always used the first partitions, and stopped after it checked 10 [sub]partitions. (or until it found a partition which would contain a match). This results in bad statistics for tables where the first 10 partitions don't represent the majority of the data (like when the first 10 partitions only contained a few rows in total). The solution was to take statisics from the partitions containing the most rows instead: Added an array of partition ids which is sorted by number of records in descending order. this array is used in records_in_range to cover as many records as possible in as few calls as possible. Also changed the limit of how many partitions to use for the statistics from a static max of 10 partitions, into a dynamic model: Maximum number of partitions is now log2(total number of partitions) taken from the ordered array. It will continue calling partitions records_in_range until it has checked: (total rows in matching partitions) * (maximum number of partitions) / (number of used partitions) Also reverted the changes for ha_partition::scan_time() and ha_partition::estimate_rows_upper_bound() to before the fix of bug#11756867. Since they are not as slow as records_in_range.
This commit is contained in:
parent
b566d9a512
commit
8325fe02b3
5 changed files with 304 additions and 156 deletions
|
@ -1,5 +1,34 @@
|
|||
drop table if exists t1, t2;
|
||||
#
|
||||
# Bug#13694811: THE OPTIMIZER WRONGLY USES THE FIRST
|
||||
# INNODB PARTITION STATISTICS
|
||||
#
|
||||
CREATE TABLE t1
|
||||
(a INT,
|
||||
b varchar(64),
|
||||
PRIMARY KEY (a),
|
||||
KEY (b))
|
||||
ENGINE = InnoDB
|
||||
PARTITION BY RANGE (a)
|
||||
SUBPARTITION BY HASH (a) SUBPARTITIONS 10
|
||||
(PARTITION pNeg VALUES LESS THAN (0),
|
||||
PARTITION p0 VALUES LESS THAN (1000),
|
||||
PARTITION pMAX VALUES LESS THAN MAXVALUE);
|
||||
# Only one row in the first 10 subpartitions
|
||||
INSERT INTO t1 VALUES (-1, 'Only negative pk value');
|
||||
INSERT INTO t1 VALUES (0, 'Mod Zero'), (1, 'One'), (2, 'Two'), (3, 'Three'),
|
||||
(10, 'Zero'), (11, 'Mod One'), (12, 'Mod Two'), (13, 'Mod Three'),
|
||||
(20, '0'), (21, '1'), (22, '2'), (23, '3'),
|
||||
(4, '4'), (5, '5'), (6, '6'), (7, '7'), (8, '8'), (9, '9');
|
||||
INSERT INTO t1 SELECT a + 30, b FROM t1 WHERE a >= 0;
|
||||
ANALYZE TABLE t1;
|
||||
Table Op Msg_type Msg_text
|
||||
test.t1 analyze status OK
|
||||
EXPLAIN SELECT b FROM t1 WHERE b between 'L' and 'N' AND a > -100;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 range PRIMARY,b b 67 NULL 18 Using where; Using index
|
||||
DROP TABLE t1;
|
||||
#
|
||||
# Bug#56287: crash when using Partition datetime in sub in query
|
||||
#
|
||||
CREATE TABLE t1
|
||||
|
|
|
@ -18,7 +18,7 @@ id select_type table partitions type possible_keys key key_len ref rows Extra
|
|||
# # # # # # # # # 3 #
|
||||
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a < 7;
|
||||
id select_type table partitions type possible_keys key key_len ref rows Extra
|
||||
# # # # # # # # # 9 #
|
||||
# # # # # # # # # 10 #
|
||||
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a <= 1;
|
||||
id select_type table partitions type possible_keys key key_len ref rows Extra
|
||||
# # # # # # # # # 3 #
|
||||
|
@ -105,7 +105,7 @@ a
|
|||
6
|
||||
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a < 7;
|
||||
id select_type table partitions type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 p0,p1,p2,p3,p4,p5,max range PRIMARY PRIMARY 4 NULL 9 Using where; Using index
|
||||
1 SIMPLE t1 p0,p1,p2,p3,p4,p5,max index PRIMARY PRIMARY 4 NULL 10 Using where; Using index
|
||||
SELECT * FROM t1 WHERE a <= 1;
|
||||
a
|
||||
-1
|
||||
|
@ -168,7 +168,7 @@ a
|
|||
6
|
||||
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a <= 6;
|
||||
id select_type table partitions type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 p0,p1,p2,p3,p4,p5,max range PRIMARY PRIMARY 4 NULL 9 Using where; Using index
|
||||
1 SIMPLE t1 p0,p1,p2,p3,p4,p5,max index PRIMARY PRIMARY 4 NULL 10 Using where; Using index
|
||||
SELECT * FROM t1 WHERE a <= 7;
|
||||
a
|
||||
-1
|
||||
|
@ -182,7 +182,7 @@ a
|
|||
7
|
||||
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a <= 7;
|
||||
id select_type table partitions type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 p0,p1,p2,p3,p4,p5,max range PRIMARY PRIMARY 4 NULL 9 Using where; Using index
|
||||
1 SIMPLE t1 p0,p1,p2,p3,p4,p5,max index PRIMARY PRIMARY 4 NULL 10 Using where; Using index
|
||||
SELECT * FROM t1 WHERE a = 1;
|
||||
a
|
||||
1
|
||||
|
@ -424,7 +424,7 @@ a
|
|||
5
|
||||
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a < 6;
|
||||
id select_type table partitions type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 p0,p1,p2,p3,p4,max range PRIMARY PRIMARY 4 NULL 8 Using where; Using index
|
||||
1 SIMPLE t1 p0,p1,p2,p3,p4,max index PRIMARY PRIMARY 4 NULL 9 Using where; Using index
|
||||
SELECT * FROM t1 WHERE a <= 1;
|
||||
a
|
||||
-1
|
||||
|
@ -474,7 +474,7 @@ a
|
|||
5
|
||||
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a <= 5;
|
||||
id select_type table partitions type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 p0,p1,p2,p3,p4,max range PRIMARY PRIMARY 4 NULL 8 Using where; Using index
|
||||
1 SIMPLE t1 p0,p1,p2,p3,p4,max index PRIMARY PRIMARY 4 NULL 9 Using where; Using index
|
||||
SELECT * FROM t1 WHERE a <= 6;
|
||||
a
|
||||
-1
|
||||
|
@ -487,7 +487,7 @@ a
|
|||
6
|
||||
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a <= 6;
|
||||
id select_type table partitions type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 p0,p1,p2,p3,p4,max range PRIMARY PRIMARY 4 NULL 8 Using where; Using index
|
||||
1 SIMPLE t1 p0,p1,p2,p3,p4,max index PRIMARY PRIMARY 4 NULL 9 Using where; Using index
|
||||
SELECT * FROM t1 WHERE a = 1;
|
||||
a
|
||||
1
|
||||
|
@ -744,13 +744,13 @@ a
|
|||
1001-01-01
|
||||
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a < '1001-01-01';
|
||||
id select_type table partitions type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 pNULL,p0001-01-01,p1001-01-01 range a a 4 NULL 3 Using where; Using index
|
||||
1 SIMPLE t1 pNULL,p0001-01-01,p1001-01-01 index a a 4 NULL 6 Using where; Using index
|
||||
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a <= '1001-01-01';
|
||||
id select_type table partitions type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 pNULL,p0001-01-01,p1001-01-01 range a a 4 NULL 3 Using where; Using index
|
||||
1 SIMPLE t1 pNULL,p0001-01-01,p1001-01-01 index a a 4 NULL 6 Using where; Using index
|
||||
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a >= '1001-01-01';
|
||||
id select_type table partitions type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 pNULL,p1001-01-01,p2001-01-01 range a a 4 NULL 4 Using where; Using index
|
||||
1 SIMPLE t1 pNULL,p1001-01-01,p2001-01-01 range a a 4 NULL 3 Using where; Using index
|
||||
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a > '1001-01-01';
|
||||
id select_type table partitions type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 pNULL,p2001-01-01 range a a 4 NULL 3 Using where; Using index
|
||||
|
@ -759,26 +759,26 @@ id select_type table partitions type possible_keys key key_len ref rows Extra
|
|||
1 SIMPLE t1 p1001-01-01 system a NULL NULL NULL 1
|
||||
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a < '1001-00-00';
|
||||
id select_type table partitions type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 pNULL,p0001-01-01,p1001-01-01 range a a 4 NULL 3 Using where; Using index
|
||||
1 SIMPLE t1 pNULL,p0001-01-01,p1001-01-01 index a a 4 NULL 6 Using where; Using index
|
||||
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a <= '1001-00-00';
|
||||
id select_type table partitions type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 pNULL,p0001-01-01,p1001-01-01 range a a 4 NULL 3 Using where; Using index
|
||||
1 SIMPLE t1 pNULL,p0001-01-01,p1001-01-01 index a a 4 NULL 6 Using where; Using index
|
||||
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a >= '1001-00-00';
|
||||
id select_type table partitions type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 pNULL,p1001-01-01,p2001-01-01 range a a 4 NULL 4 Using where; Using index
|
||||
1 SIMPLE t1 pNULL,p1001-01-01,p2001-01-01 range a a 4 NULL 3 Using where; Using index
|
||||
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a > '1001-00-00';
|
||||
id select_type table partitions type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 pNULL,p1001-01-01,p2001-01-01 range a a 4 NULL 4 Using where; Using index
|
||||
1 SIMPLE t1 pNULL,p1001-01-01,p2001-01-01 range a a 4 NULL 3 Using where; Using index
|
||||
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a = '1001-00-00';
|
||||
id select_type table partitions type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 pNULL ref a a 4 const 1 Using where; Using index
|
||||
# Disabling warnings for the invalid date
|
||||
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a < '1999-02-31';
|
||||
id select_type table partitions type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 pNULL,p0001-01-01,p1001-01-01,p2001-01-01 range a a 4 NULL 5 Using where; Using index
|
||||
1 SIMPLE t1 pNULL,p0001-01-01,p1001-01-01,p2001-01-01 range a a 4 NULL 4 Using where; Using index
|
||||
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a <= '1999-02-31';
|
||||
id select_type table partitions type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 pNULL,p0001-01-01,p1001-01-01,p2001-01-01 range a a 4 NULL 5 Using where; Using index
|
||||
1 SIMPLE t1 pNULL,p0001-01-01,p1001-01-01,p2001-01-01 range a a 4 NULL 4 Using where; Using index
|
||||
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a >= '1999-02-31';
|
||||
id select_type table partitions type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 pNULL,p2001-01-01 index a a 4 NULL 4 Using where; Using index
|
||||
|
@ -790,16 +790,16 @@ id select_type table partitions type possible_keys key key_len ref rows Extra
|
|||
1 SIMPLE t1 pNULL ref a a 4 const 1 Using where; Using index
|
||||
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a BETWEEN '0000-00-00' AND '1002-00-00';
|
||||
id select_type table partitions type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 pNULL,p0001-01-01,p1001-01-01,p2001-01-01 range a a 4 NULL 5 Using where; Using index
|
||||
1 SIMPLE t1 pNULL,p0001-01-01,p1001-01-01,p2001-01-01 range a a 4 NULL 4 Using where; Using index
|
||||
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a BETWEEN '0000-00-00' AND '1001-01-01';
|
||||
id select_type table partitions type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 pNULL,p0001-01-01,p1001-01-01 range a a 4 NULL 3 Using where; Using index
|
||||
1 SIMPLE t1 pNULL,p0001-01-01,p1001-01-01 index a a 4 NULL 6 Using where; Using index
|
||||
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a BETWEEN '0001-01-02' AND '1002-00-00';
|
||||
id select_type table partitions type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 pNULL,p1001-01-01,p2001-01-01 range a a 4 NULL 3 Using where; Using index
|
||||
1 SIMPLE t1 pNULL,p1001-01-01,p2001-01-01 index a a 4 NULL 5 Using where; Using index
|
||||
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a BETWEEN '0001-01-01' AND '1001-01-01';
|
||||
id select_type table partitions type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 pNULL,p0001-01-01,p1001-01-01 range a a 4 NULL 3 Using where; Using index
|
||||
1 SIMPLE t1 pNULL,p0001-01-01,p1001-01-01 index a a 4 NULL 6 Using where; Using index
|
||||
# test without index
|
||||
ALTER TABLE t1 DROP KEY a;
|
||||
SELECT * FROM t1 WHERE a < '1001-01-01';
|
||||
|
@ -1076,7 +1076,7 @@ id select_type table partitions type possible_keys key key_len ref rows Extra
|
|||
1 SIMPLE t1 p0001-01-01,pNULL,p0000-01-02 range a a 4 NULL 3 Using where; Using index
|
||||
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a <= '1001-01-01';
|
||||
id select_type table partitions type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 p0001-01-01,pNULL,p0000-01-02,p1001-01-01 range a a 4 NULL 4 Using where; Using index
|
||||
1 SIMPLE t1 p0001-01-01,pNULL,p0000-01-02,p1001-01-01 range a a 4 NULL 3 Using where; Using index
|
||||
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a >= '1001-01-01';
|
||||
id select_type table partitions type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 p2001-01-01,pNULL,p1001-01-01 range a a 4 NULL 4 Using where; Using index
|
||||
|
@ -1104,10 +1104,10 @@ id select_type table partitions type possible_keys key key_len ref rows Extra
|
|||
# Disabling warnings for the invalid date
|
||||
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a < '1999-02-31';
|
||||
id select_type table partitions type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 p0001-01-01,pNULL,p0000-01-02,p1001-01-01 range a a 4 NULL 5 Using where; Using index
|
||||
1 SIMPLE t1 p0001-01-01,pNULL,p0000-01-02,p1001-01-01 range a a 4 NULL 4 Using where; Using index
|
||||
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a <= '1999-02-31';
|
||||
id select_type table partitions type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 p0001-01-01,pNULL,p0000-01-02,p1001-01-01 range a a 4 NULL 5 Using where; Using index
|
||||
1 SIMPLE t1 p0001-01-01,pNULL,p0000-01-02,p1001-01-01 range a a 4 NULL 4 Using where; Using index
|
||||
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a >= '1999-02-31';
|
||||
id select_type table partitions type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 p2001-01-01,pNULL index a a 4 NULL 4 Using where; Using index
|
||||
|
@ -1119,10 +1119,10 @@ id select_type table partitions type possible_keys key key_len ref rows Extra
|
|||
1 SIMPLE t1 pNULL ref a a 4 const 1 Using where; Using index
|
||||
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a BETWEEN '0000-00-00' AND '1002-00-00';
|
||||
id select_type table partitions type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 p0001-01-01,pNULL,p0000-01-02,p1001-01-01 range a a 4 NULL 5 Using where; Using index
|
||||
1 SIMPLE t1 p0001-01-01,pNULL,p0000-01-02,p1001-01-01 range a a 4 NULL 4 Using where; Using index
|
||||
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a BETWEEN '0000-00-00' AND '1001-01-01';
|
||||
id select_type table partitions type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 p0001-01-01,pNULL,p0000-01-02,p1001-01-01 range a a 4 NULL 4 Using where; Using index
|
||||
1 SIMPLE t1 p0001-01-01,pNULL,p0000-01-02,p1001-01-01 range a a 4 NULL 3 Using where; Using index
|
||||
EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a BETWEEN '0001-01-02' AND '1002-00-00';
|
||||
id select_type table partitions type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 pNULL,p1001-01-01 index a a 4 NULL 4 Using where; Using index
|
||||
|
@ -2537,18 +2537,18 @@ id select_type table partitions type possible_keys key key_len ref rows Extra
|
|||
1 SIMPLE t2 p0,p1,p2 ALL NULL NULL NULL NULL 510 Using where
|
||||
explain partitions select * from t2 where b = 4;
|
||||
id select_type table partitions type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t2 p0,p1,p2,p3,p4 ref b b 5 const 76 Using where
|
||||
1 SIMPLE t2 p0,p1,p2,p3,p4 ref b b 5 const 96 Using where
|
||||
explain extended select * from t2 where b = 6;
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t2 ref b b 5 const 76 100.00 Using where
|
||||
1 SIMPLE t2 ref b b 5 const 96 100.00 Using where
|
||||
Warnings:
|
||||
Note 1003 select `test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b` from `test`.`t2` where (`test`.`t2`.`b` = 6)
|
||||
explain partitions select * from t2 where b = 6;
|
||||
id select_type table partitions type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t2 p0,p1,p2,p3,p4 ref b b 5 const 76 Using where
|
||||
1 SIMPLE t2 p0,p1,p2,p3,p4 ref b b 5 const 96 Using where
|
||||
explain extended select * from t2 where b in (1,3,5);
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t2 ALL b NULL NULL NULL 910 40.66 Using where
|
||||
1 SIMPLE t2 ALL b NULL NULL NULL 910 51.65 Using where
|
||||
Warnings:
|
||||
Note 1003 select `test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b` from `test`.`t2` where (`test`.`t2`.`b` in (1,3,5))
|
||||
explain partitions select * from t2 where b in (1,3,5);
|
||||
|
@ -2556,7 +2556,7 @@ id select_type table partitions type possible_keys key key_len ref rows Extra
|
|||
1 SIMPLE t2 p0,p1,p2,p3,p4 ALL b NULL NULL NULL 910 Using where
|
||||
explain extended select * from t2 where b in (2,4,6);
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t2 ALL b NULL NULL NULL 910 25.05 Using where
|
||||
1 SIMPLE t2 ALL b NULL NULL NULL 910 31.65 Using where
|
||||
Warnings:
|
||||
Note 1003 select `test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b` from `test`.`t2` where (`test`.`t2`.`b` in (2,4,6))
|
||||
explain partitions select * from t2 where b in (2,4,6);
|
||||
|
@ -2564,7 +2564,7 @@ id select_type table partitions type possible_keys key key_len ref rows Extra
|
|||
1 SIMPLE t2 p0,p1,p2,p3,p4 ALL b NULL NULL NULL 910 Using where
|
||||
explain extended select * from t2 where b in (7,8,9);
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t2 ALL b NULL NULL NULL 910 36.70 Using where
|
||||
1 SIMPLE t2 ALL b NULL NULL NULL 910 19.12 Using where
|
||||
Warnings:
|
||||
Note 1003 select `test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b` from `test`.`t2` where (`test`.`t2`.`b` in (7,8,9))
|
||||
explain partitions select * from t2 where b in (7,8,9);
|
||||
|
@ -2572,7 +2572,7 @@ id select_type table partitions type possible_keys key key_len ref rows Extra
|
|||
1 SIMPLE t2 p0,p1,p2,p3,p4 ALL b NULL NULL NULL 910 Using where
|
||||
explain extended select * from t2 where b > 5;
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t2 ALL b NULL NULL NULL 910 44.84 Using where
|
||||
1 SIMPLE t2 ALL b NULL NULL NULL 910 29.23 Using where
|
||||
Warnings:
|
||||
Note 1003 select `test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b` from `test`.`t2` where (`test`.`t2`.`b` > 5)
|
||||
explain partitions select * from t2 where b > 5;
|
||||
|
@ -2580,7 +2580,7 @@ id select_type table partitions type possible_keys key key_len ref rows Extra
|
|||
1 SIMPLE t2 p0,p1,p2,p3,p4 ALL b NULL NULL NULL 910 Using where
|
||||
explain extended select * from t2 where b > 5 and b < 8;
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t2 ALL b NULL NULL NULL 910 22.09 Using where
|
||||
1 SIMPLE t2 ALL b NULL NULL NULL 910 28.13 Using where
|
||||
Warnings:
|
||||
Note 1003 select `test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b` from `test`.`t2` where ((`test`.`t2`.`b` > 5) and (`test`.`t2`.`b` < 8))
|
||||
explain partitions select * from t2 where b > 5 and b < 8;
|
||||
|
@ -2588,15 +2588,15 @@ id select_type table partitions type possible_keys key key_len ref rows Extra
|
|||
1 SIMPLE t2 p0,p1,p2,p3,p4 ALL b NULL NULL NULL 910 Using where
|
||||
explain extended select * from t2 where b > 5 and b < 7;
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t2 range b b 5 NULL 76 100.00 Using where
|
||||
1 SIMPLE t2 range b b 5 NULL 96 100.00 Using where
|
||||
Warnings:
|
||||
Note 1003 select `test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b` from `test`.`t2` where ((`test`.`t2`.`b` > 5) and (`test`.`t2`.`b` < 7))
|
||||
explain partitions select * from t2 where b > 5 and b < 7;
|
||||
id select_type table partitions type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t2 p0,p1,p2,p3,p4 range b b 5 NULL 76 Using where
|
||||
1 SIMPLE t2 p0,p1,p2,p3,p4 range b b 5 NULL 96 Using where
|
||||
explain extended select * from t2 where b > 0 and b < 5;
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t2 ALL b NULL NULL NULL 910 41.65 Using where
|
||||
1 SIMPLE t2 ALL b NULL NULL NULL 910 53.19 Using where
|
||||
Warnings:
|
||||
Note 1003 select `test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b` from `test`.`t2` where ((`test`.`t2`.`b` > 0) and (`test`.`t2`.`b` < 5))
|
||||
explain partitions select * from t2 where b > 0 and b < 5;
|
||||
|
@ -2630,10 +2630,10 @@ flush status;
|
|||
delete from t2 where b = 7;
|
||||
show status like 'Handler_read_rnd_next';
|
||||
Variable_name Value
|
||||
Handler_read_rnd_next 0
|
||||
Handler_read_rnd_next 1215
|
||||
show status like 'Handler_read_key';
|
||||
Variable_name Value
|
||||
Handler_read_key 5
|
||||
Handler_read_key 0
|
||||
flush status;
|
||||
delete from t2 where b > 5;
|
||||
show status like 'Handler_read_rnd_next';
|
||||
|
|
|
@ -7,6 +7,35 @@ drop table if exists t1, t2;
|
|||
|
||||
let $MYSQLD_DATADIR= `SELECT @@datadir`;
|
||||
|
||||
--echo #
|
||||
--echo # Bug#13694811: THE OPTIMIZER WRONGLY USES THE FIRST
|
||||
--echo # INNODB PARTITION STATISTICS
|
||||
--echo #
|
||||
|
||||
CREATE TABLE t1
|
||||
(a INT,
|
||||
b varchar(64),
|
||||
PRIMARY KEY (a),
|
||||
KEY (b))
|
||||
ENGINE = InnoDB
|
||||
PARTITION BY RANGE (a)
|
||||
SUBPARTITION BY HASH (a) SUBPARTITIONS 10
|
||||
(PARTITION pNeg VALUES LESS THAN (0),
|
||||
PARTITION p0 VALUES LESS THAN (1000),
|
||||
PARTITION pMAX VALUES LESS THAN MAXVALUE);
|
||||
|
||||
--echo # Only one row in the first 10 subpartitions
|
||||
INSERT INTO t1 VALUES (-1, 'Only negative pk value');
|
||||
|
||||
INSERT INTO t1 VALUES (0, 'Mod Zero'), (1, 'One'), (2, 'Two'), (3, 'Three'),
|
||||
(10, 'Zero'), (11, 'Mod One'), (12, 'Mod Two'), (13, 'Mod Three'),
|
||||
(20, '0'), (21, '1'), (22, '2'), (23, '3'),
|
||||
(4, '4'), (5, '5'), (6, '6'), (7, '7'), (8, '8'), (9, '9');
|
||||
INSERT INTO t1 SELECT a + 30, b FROM t1 WHERE a >= 0;
|
||||
ANALYZE TABLE t1;
|
||||
EXPLAIN SELECT b FROM t1 WHERE b between 'L' and 'N' AND a > -100;
|
||||
DROP TABLE t1;
|
||||
|
||||
--echo #
|
||||
--echo # Bug#56287: crash when using Partition datetime in sub in query
|
||||
--echo #
|
||||
|
|
|
@ -286,6 +286,7 @@ void ha_partition::init_handler_variables()
|
|||
m_is_sub_partitioned= 0;
|
||||
m_is_clone_of= NULL;
|
||||
m_clone_mem_root= NULL;
|
||||
m_part_ids_sorted_by_num_of_records= NULL;
|
||||
|
||||
#ifdef DONT_HAVE_TO_BE_INITALIZED
|
||||
m_start_key.flag= 0;
|
||||
|
@ -321,6 +322,8 @@ ha_partition::~ha_partition()
|
|||
delete m_file[i];
|
||||
}
|
||||
my_free((char*) m_ordered_rec_buffer, MYF(MY_ALLOW_ZERO_PTR));
|
||||
my_free((char*) m_part_ids_sorted_by_num_of_records,
|
||||
MYF(MY_ALLOW_ZERO_PTR));
|
||||
|
||||
clear_handler_file();
|
||||
DBUG_VOID_RETURN;
|
||||
|
@ -2638,6 +2641,16 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked)
|
|||
m_start_key.key= (const uchar*)ptr;
|
||||
}
|
||||
}
|
||||
if (!m_part_ids_sorted_by_num_of_records)
|
||||
{
|
||||
if (!(m_part_ids_sorted_by_num_of_records=
|
||||
(uint32*) my_malloc(m_tot_parts * sizeof(uint32), MYF(MY_WME))))
|
||||
DBUG_RETURN(error);
|
||||
uint32 i;
|
||||
/* Initialize it with all partition ids. */
|
||||
for (i= 0; i < m_tot_parts; i++)
|
||||
m_part_ids_sorted_by_num_of_records[i]= i;
|
||||
}
|
||||
|
||||
/* Initialize the bitmap we use to minimize ha_start_bulk_insert calls */
|
||||
if (bitmap_init(&m_bulk_insert_started, NULL, m_tot_parts + 1, FALSE))
|
||||
|
@ -5146,6 +5159,24 @@ int ha_partition::handle_ordered_prev(uchar *buf)
|
|||
and read_time calls
|
||||
*/
|
||||
|
||||
/**
|
||||
Helper function for sorting according to number of rows in descending order.
|
||||
*/
|
||||
|
||||
int ha_partition::compare_number_of_records(ha_partition *me,
|
||||
const uint32 *a,
|
||||
const uint32 *b)
|
||||
{
|
||||
handler **file= me->m_file;
|
||||
/* Note: sorting in descending order! */
|
||||
if (file[*a]->stats.records > file[*b]->stats.records)
|
||||
return -1;
|
||||
if (file[*a]->stats.records < file[*b]->stats.records)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
General method to gather info from handler
|
||||
|
||||
|
@ -5387,6 +5418,15 @@ int ha_partition::info(uint flag)
|
|||
}
|
||||
i++;
|
||||
} while (*(++file_array));
|
||||
/*
|
||||
Sort the array of part_ids by number of records in
|
||||
in descending order.
|
||||
*/
|
||||
my_qsort2((void*) m_part_ids_sorted_by_num_of_records,
|
||||
m_tot_parts,
|
||||
sizeof(uint32),
|
||||
(qsort2_cmp) compare_number_of_records,
|
||||
this);
|
||||
|
||||
file= m_file[handler_instance];
|
||||
file->info(HA_STATUS_CONST);
|
||||
|
@ -6124,21 +6164,72 @@ const key_map *ha_partition::keys_to_use_for_scanning()
|
|||
DBUG_RETURN(m_file[0]->keys_to_use_for_scanning());
|
||||
}
|
||||
|
||||
#define MAX_PARTS_FOR_OPTIMIZER_CALLS 10
|
||||
/*
|
||||
Prepare start variables for estimating optimizer costs.
|
||||
|
||||
@param[out] num_used_parts Number of partitions after pruning.
|
||||
@param[out] check_min_num Number of partitions to call.
|
||||
@param[out] first first used partition.
|
||||
/**
|
||||
Minimum number of rows to base optimizer estimate on.
|
||||
*/
|
||||
void ha_partition::partitions_optimizer_call_preparations(uint *first,
|
||||
uint *num_used_parts,
|
||||
uint *check_min_num)
|
||||
|
||||
ha_rows ha_partition::min_rows_for_estimate()
|
||||
{
|
||||
*first= bitmap_get_first_set(&(m_part_info->used_partitions));
|
||||
*num_used_parts= bitmap_bits_set(&(m_part_info->used_partitions));
|
||||
*check_min_num= min(MAX_PARTS_FOR_OPTIMIZER_CALLS, *num_used_parts);
|
||||
uint i, max_used_partitions, tot_used_partitions;
|
||||
DBUG_ENTER("ha_partition::partitions_optimizer_call_preparations");
|
||||
|
||||
tot_used_partitions= bitmap_bits_set(&m_part_info->used_partitions);
|
||||
DBUG_ASSERT(tot_used_partitions);
|
||||
|
||||
/*
|
||||
Allow O(log2(tot_partitions)) increase in number of used partitions.
|
||||
This gives O(1/log2(tot_partitions)) of rows to base the estimate on.
|
||||
I.e when the total number of partitions doubles, allow one more
|
||||
partition to be checked.
|
||||
*/
|
||||
i= 2;
|
||||
max_used_partitions= 1;
|
||||
while (i < m_tot_parts)
|
||||
{
|
||||
max_used_partitions++;
|
||||
i= i << 1;
|
||||
}
|
||||
if (max_used_partitions > tot_used_partitions)
|
||||
max_used_partitions= tot_used_partitions;
|
||||
|
||||
/* stats.records is already updated by the info(HA_STATUS_VARIABLE) call. */
|
||||
DBUG_PRINT("info", ("max_used_partitions: %u tot_rows: %lu",
|
||||
max_used_partitions,
|
||||
(ulong) stats.records));
|
||||
DBUG_PRINT("info", ("tot_used_partitions: %u min_rows_to_check: %lu",
|
||||
tot_used_partitions,
|
||||
(ulong) stats.records * max_used_partitions
|
||||
/ tot_used_partitions));
|
||||
DBUG_RETURN(stats.records * max_used_partitions / tot_used_partitions);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Get the biggest used partition.
|
||||
|
||||
Starting at the N:th biggest partition and skips all non used
|
||||
partitions, returning the biggest used partition found
|
||||
|
||||
@param[in,out] part_index Skip the *part_index biggest partitions
|
||||
|
||||
@return The biggest used partition with index not lower than *part_index.
|
||||
@retval NO_CURRENT_PART_ID No more partition used.
|
||||
@retval != NO_CURRENT_PART_ID partition id of biggest used partition with
|
||||
index >= *part_index supplied. Note that
|
||||
*part_index will be updated to the next
|
||||
partition index to use.
|
||||
*/
|
||||
|
||||
uint ha_partition::get_biggest_used_partition(uint *part_index)
|
||||
{
|
||||
uint part_id;
|
||||
while ((*part_index) < m_tot_parts)
|
||||
{
|
||||
part_id= m_part_ids_sorted_by_num_of_records[(*part_index)++];
|
||||
if (bitmap_is_set(&m_part_info->used_partitions, part_id))
|
||||
return part_id;
|
||||
}
|
||||
return NO_CURRENT_PART_ID;
|
||||
}
|
||||
|
||||
|
||||
|
@ -6154,86 +6245,32 @@ void ha_partition::partitions_optimizer_call_preparations(uint *first,
|
|||
|
||||
double ha_partition::scan_time()
|
||||
{
|
||||
double scan_time= 0.0;
|
||||
uint first, part_id, num_used_parts, check_min_num, partitions_called= 0;
|
||||
double scan_time= 0;
|
||||
handler **file;
|
||||
DBUG_ENTER("ha_partition::scan_time");
|
||||
|
||||
partitions_optimizer_call_preparations(&first, &num_used_parts, &check_min_num);
|
||||
for (part_id= first; partitions_called < num_used_parts ; part_id++)
|
||||
{
|
||||
if (!bitmap_is_set(&(m_part_info->used_partitions), part_id))
|
||||
continue;
|
||||
scan_time+= m_file[part_id]->scan_time();
|
||||
partitions_called++;
|
||||
if (partitions_called >= check_min_num && scan_time != 0.0)
|
||||
{
|
||||
DBUG_RETURN(scan_time *
|
||||
(double) num_used_parts / (double) partitions_called);
|
||||
}
|
||||
}
|
||||
for (file= m_file; *file; file++)
|
||||
if (bitmap_is_set(&(m_part_info->used_partitions), (file - m_file)))
|
||||
scan_time+= (*file)->scan_time();
|
||||
DBUG_RETURN(scan_time);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Estimate rows for records_in_range or estimate_rows_upper_bound.
|
||||
/**
|
||||
Find number of records in a range.
|
||||
@param inx Index number
|
||||
@param min_key Start of range
|
||||
@param max_key End of range
|
||||
|
||||
@param is_records_in_range call records_in_range instead of
|
||||
estimate_rows_upper_bound.
|
||||
@param inx (only for records_in_range) index to use.
|
||||
@param min_key (only for records_in_range) start of range.
|
||||
@param max_key (only for records_in_range) end of range.
|
||||
@return Number of rows in range.
|
||||
|
||||
@return Number of rows or HA_POS_ERROR.
|
||||
*/
|
||||
ha_rows ha_partition::estimate_rows(bool is_records_in_range, uint inx,
|
||||
key_range *min_key, key_range *max_key)
|
||||
{
|
||||
ha_rows rows, estimated_rows= 0;
|
||||
uint first, part_id, num_used_parts, check_min_num, partitions_called= 0;
|
||||
DBUG_ENTER("ha_partition::records_in_range");
|
||||
Given a starting key, and an ending key estimate the number of rows that
|
||||
will exist between the two. end_key may be empty which in case determine
|
||||
if start_key matches any rows.
|
||||
|
||||
partitions_optimizer_call_preparations(&first, &num_used_parts, &check_min_num);
|
||||
for (part_id= first; partitions_called < num_used_parts ; part_id++)
|
||||
{
|
||||
if (!bitmap_is_set(&(m_part_info->used_partitions), part_id))
|
||||
continue;
|
||||
if (is_records_in_range)
|
||||
rows= m_file[part_id]->records_in_range(inx, min_key, max_key);
|
||||
else
|
||||
rows= m_file[part_id]->estimate_rows_upper_bound();
|
||||
if (rows == HA_POS_ERROR)
|
||||
DBUG_RETURN(HA_POS_ERROR);
|
||||
estimated_rows+= rows;
|
||||
partitions_called++;
|
||||
if (partitions_called >= check_min_num && estimated_rows)
|
||||
{
|
||||
DBUG_RETURN(estimated_rows * num_used_parts / partitions_called);
|
||||
}
|
||||
}
|
||||
DBUG_RETURN(estimated_rows);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Find number of records in a range
|
||||
|
||||
SYNOPSIS
|
||||
records_in_range()
|
||||
inx Index number
|
||||
min_key Start of range
|
||||
max_key End of range
|
||||
|
||||
RETURN VALUE
|
||||
Number of rows in range
|
||||
|
||||
DESCRIPTION
|
||||
Given a starting key, and an ending key estimate the number of rows that
|
||||
will exist between the two. end_key may be empty which in case determine
|
||||
if start_key matches any rows.
|
||||
|
||||
Called from opt_range.cc by check_quick_keys().
|
||||
Called from opt_range.cc by check_quick_keys().
|
||||
|
||||
@note
|
||||
monty: MUST be called for each range and added.
|
||||
Note that MySQL will assume that if this returns 0 there is no
|
||||
matching rows for the range!
|
||||
|
@ -6242,27 +6279,80 @@ ha_rows ha_partition::estimate_rows(bool is_records_in_range, uint inx,
|
|||
ha_rows ha_partition::records_in_range(uint inx, key_range *min_key,
|
||||
key_range *max_key)
|
||||
{
|
||||
ha_rows min_rows_to_check, rows, estimated_rows=0, checked_rows= 0;
|
||||
uint partition_index= 0, part_id;
|
||||
DBUG_ENTER("ha_partition::records_in_range");
|
||||
|
||||
DBUG_RETURN(estimate_rows(TRUE, inx, min_key, max_key));
|
||||
min_rows_to_check= min_rows_for_estimate();
|
||||
|
||||
while ((part_id= get_biggest_used_partition(&partition_index))
|
||||
!= NO_CURRENT_PART_ID)
|
||||
{
|
||||
rows= m_file[part_id]->records_in_range(inx, min_key, max_key);
|
||||
|
||||
DBUG_PRINT("info", ("part %u match %lu rows of %lu", part_id, (ulong) rows,
|
||||
(ulong) m_file[part_id]->stats.records));
|
||||
|
||||
if (rows == HA_POS_ERROR)
|
||||
DBUG_RETURN(HA_POS_ERROR);
|
||||
estimated_rows+= rows;
|
||||
checked_rows+= m_file[part_id]->stats.records;
|
||||
/*
|
||||
Returning 0 means no rows can be found, so we must continue
|
||||
this loop as long as we have estimated_rows == 0.
|
||||
Also many engines return 1 to indicate that there may exist
|
||||
a matching row, we do not normalize this by dividing by number of
|
||||
used partitions, but leave it to be returned as a sum, which will
|
||||
reflect that we will need to scan each partition's index.
|
||||
|
||||
Note that this statistics may not always be correct, so we must
|
||||
continue even if the current partition has 0 rows, since we might have
|
||||
deleted rows from the current partition, or inserted to the next
|
||||
partition.
|
||||
*/
|
||||
if (estimated_rows && checked_rows &&
|
||||
checked_rows >= min_rows_to_check)
|
||||
{
|
||||
DBUG_PRINT("info",
|
||||
("records_in_range(inx %u): %lu (%lu * %lu / %lu)",
|
||||
inx,
|
||||
(ulong) (estimated_rows * stats.records / checked_rows),
|
||||
(ulong) estimated_rows,
|
||||
(ulong) stats.records,
|
||||
(ulong) checked_rows));
|
||||
DBUG_RETURN(estimated_rows * stats.records / checked_rows);
|
||||
}
|
||||
}
|
||||
DBUG_PRINT("info", ("records_in_range(inx %u): %lu",
|
||||
inx,
|
||||
(ulong) estimated_rows));
|
||||
DBUG_RETURN(estimated_rows);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Estimate upper bound of number of rows
|
||||
/**
|
||||
Estimate upper bound of number of rows.
|
||||
|
||||
SYNOPSIS
|
||||
estimate_rows_upper_bound()
|
||||
|
||||
RETURN VALUE
|
||||
Number of rows
|
||||
@return Number of rows.
|
||||
*/
|
||||
|
||||
ha_rows ha_partition::estimate_rows_upper_bound()
|
||||
{
|
||||
ha_rows rows, tot_rows= 0;
|
||||
handler **file= m_file;
|
||||
DBUG_ENTER("ha_partition::estimate_rows_upper_bound");
|
||||
|
||||
DBUG_RETURN(estimate_rows(FALSE, 0, NULL, NULL));
|
||||
do
|
||||
{
|
||||
if (bitmap_is_set(&(m_part_info->used_partitions), (file - m_file)))
|
||||
{
|
||||
rows= (*file)->estimate_rows_upper_bound();
|
||||
if (rows == HA_POS_ERROR)
|
||||
DBUG_RETURN(HA_POS_ERROR);
|
||||
tot_rows+= rows;
|
||||
}
|
||||
} while (*(++file));
|
||||
DBUG_RETURN(tot_rows);
|
||||
}
|
||||
|
||||
|
||||
|
@ -6494,20 +6584,20 @@ int ha_partition::add_index(TABLE *table_arg, KEY *key_info, uint num_of_keys)
|
|||
return ret;
|
||||
err:
|
||||
if (file > m_file)
|
||||
{
|
||||
uint *key_numbers= (uint*) ha_thd()->alloc(sizeof(uint) * num_of_keys);
|
||||
KEY *old_key_info= table_arg->key_info;
|
||||
uint i;
|
||||
/* Use the newly added key_info as table->key_info to remove them. */
|
||||
for (i= 0; i < num_of_keys; i++)
|
||||
key_numbers[i]= i;
|
||||
table_arg->key_info= key_info;
|
||||
while (--file >= m_file)
|
||||
{
|
||||
(void) (*file)->prepare_drop_index(table_arg, key_numbers, num_of_keys);
|
||||
(void) (*file)->final_drop_index(table_arg);
|
||||
}
|
||||
table_arg->key_info= old_key_info;
|
||||
{
|
||||
uint *key_numbers= (uint*) ha_thd()->alloc(sizeof(uint) * num_of_keys);
|
||||
KEY *old_key_info= table_arg->key_info;
|
||||
uint i;
|
||||
/* Use the newly added key_info as table->key_info to remove them. */
|
||||
for (i= 0; i < num_of_keys; i++)
|
||||
key_numbers[i]= i;
|
||||
table_arg->key_info= key_info;
|
||||
while (--file >= m_file)
|
||||
{
|
||||
(void) (*file)->prepare_drop_index(table_arg, key_numbers, num_of_keys);
|
||||
(void) (*file)->final_drop_index(table_arg);
|
||||
}
|
||||
table_arg->key_info= old_key_info;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -199,6 +199,12 @@ private:
|
|||
ha_rows m_bulk_inserted_rows;
|
||||
/** used for prediction of start_bulk_insert rows */
|
||||
enum_monotonicity_info m_part_func_monotonicity_info;
|
||||
/** Sorted array of partition ids in descending order of number of rows. */
|
||||
uint32 *m_part_ids_sorted_by_num_of_records;
|
||||
/* Compare function for my_qsort2, for reversed order. */
|
||||
static int compare_number_of_records(ha_partition *me,
|
||||
const uint32 *a,
|
||||
const uint32 *b);
|
||||
public:
|
||||
handler *clone(const char *name, MEM_ROOT *mem_root);
|
||||
virtual void set_part_info(partition_info *part_info)
|
||||
|
@ -219,9 +225,9 @@ public:
|
|||
*/
|
||||
ha_partition(handlerton *hton, TABLE_SHARE * table);
|
||||
ha_partition(handlerton *hton, partition_info * part_info);
|
||||
ha_partition(handlerton *hton, TABLE_SHARE *share,
|
||||
partition_info *part_info_arg,
|
||||
ha_partition *clone_arg,
|
||||
ha_partition(handlerton *hton, TABLE_SHARE *share,
|
||||
partition_info *part_info_arg,
|
||||
ha_partition *clone_arg,
|
||||
MEM_ROOT *clone_mem_root_arg);
|
||||
~ha_partition();
|
||||
/*
|
||||
|
@ -582,15 +588,9 @@ public:
|
|||
*/
|
||||
|
||||
private:
|
||||
/*
|
||||
Helper function to get the minimum number of partitions to use for
|
||||
the optimizer hints/cost calls.
|
||||
*/
|
||||
void partitions_optimizer_call_preparations(uint *num_used_parts,
|
||||
uint *check_min_num,
|
||||
uint *first);
|
||||
ha_rows estimate_rows(bool is_records_in_range, uint inx,
|
||||
key_range *min_key, key_range *max_key);
|
||||
/* Helper functions for optimizer hints. */
|
||||
ha_rows min_rows_for_estimate();
|
||||
uint get_biggest_used_partition(uint *part_index);
|
||||
public:
|
||||
|
||||
/*
|
||||
|
|
Loading…
Reference in a new issue