diff --git a/mysql-test/r/partition.result b/mysql-test/r/partition.result index 53f89c9cd557b..d2b3d06b2f2e5 100644 --- a/mysql-test/r/partition.result +++ b/mysql-test/r/partition.result @@ -2646,6 +2646,106 @@ Note 1517 Duplicate partition name p2 DEALLOCATE PREPARE stmt; DROP TABLE t1; # +# MDEV-17032: Estimates are higher for partitions of a table with @@use_stat_tables= PREFERABLY +# +create table t0(a int); +insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); +create table t1(a int); +insert into t1 select A.a + B.a* 10 + C.a * 100 from t0 A, t0 B, t0 C; +create table t2 ( +part_key int, +a int, +b int +) partition by list(part_key) ( +partition p0 values in (0), +partition p1 values in (1), +partition p2 values in (2), +partition p3 values in (3), +partition p4 values in (4) +); +insert into t2 +select mod(a,5), a/100, mod(a,5) from t1; +set @save_use_stat_tables= @@use_stat_tables; +set @save_optimizer_use_condition_selectivity=@@optimizer_use_condition_selectivity; +# +# Tests using stats provided by the storage engine +# +explain extended select * from t2 where part_key=1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t2 ALL NULL NULL NULL NULL 200 100.00 Using where +Warnings: +Note 1003 select `test`.`t2`.`part_key` AS `part_key`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b` from `test`.`t2` where (`test`.`t2`.`part_key` = 1) +explain partitions select * from t2 where part_key=1; +id select_type table partitions type possible_keys key key_len ref rows Extra +1 SIMPLE t2 p1 ALL NULL NULL NULL NULL 200 Using where +explain extended select * from t2 where part_key in (1,2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t2 ALL NULL NULL NULL NULL 400 100.00 Using where +Warnings: +Note 1003 select `test`.`t2`.`part_key` AS `part_key`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b` from `test`.`t2` where (`test`.`t2`.`part_key` in (1,2)) +explain partitions select * from t2 where part_key in (1,2); +id select_type table partitions type possible_keys key key_len ref rows Extra +1 SIMPLE t2 p1,p2 ALL NULL NULL NULL NULL 400 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 NULL NULL NULL NULL 1000 100.00 Using where +Warnings: +Note 1003 select `test`.`t2`.`part_key` AS `part_key`,`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; +id select_type table partitions type possible_keys key key_len ref rows Extra +1 SIMPLE t2 p0,p1,p2,p3,p4 ALL NULL NULL NULL NULL 1000 Using where +explain extended select * from t2 partition(p0) where b=1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t2 ALL NULL NULL NULL NULL 200 100.00 Using where +Warnings: +Note 1003 select `test`.`t2`.`part_key` AS `part_key`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b` from `test`.`t2` PARTITION (`p0`) where (`test`.`t2`.`b` = 1) +set @save_histogram_size=@@histogram_size; +set @@histogram_size=100; +set @@use_stat_tables= PREFERABLY; +set @@optimizer_use_condition_selectivity=4; +analyze table t2; +Table Op Msg_type Msg_text +test.t2 analyze status Engine-independent statistics collected +test.t2 analyze status OK +# +# Tests using EITS +# +# filtered should be 100 +explain extended select * from t2 where part_key=1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t2 ALL NULL NULL NULL NULL 200 100.00 Using where +Warnings: +Note 1003 select `test`.`t2`.`part_key` AS `part_key`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b` from `test`.`t2` where (`test`.`t2`.`part_key` = 1) +explain partitions select * from t2 where part_key=1; +id select_type table partitions type possible_keys key key_len ref rows Extra +1 SIMPLE t2 p1 ALL NULL NULL NULL NULL 200 Using where +# filtered should be 100 +explain extended select * from t2 where part_key in (1,2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t2 ALL NULL NULL NULL NULL 400 100.00 Using where +Warnings: +Note 1003 select `test`.`t2`.`part_key` AS `part_key`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b` from `test`.`t2` where (`test`.`t2`.`part_key` in (1,2)) +explain partitions select * from t2 where part_key in (1,2); +id select_type table partitions type possible_keys key key_len ref rows Extra +1 SIMPLE t2 p1,p2 ALL NULL NULL NULL NULL 400 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 NULL NULL NULL NULL 1000 19.80 Using where +Warnings: +Note 1003 select `test`.`t2`.`part_key` AS `part_key`,`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; +id select_type table partitions type possible_keys key key_len ref rows Extra +1 SIMPLE t2 p0,p1,p2,p3,p4 ALL NULL NULL NULL NULL 1000 Using where +explain extended select * from t2 partition(p0) where b=1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t2 ALL NULL NULL NULL NULL 200 19.80 Using where +Warnings: +Note 1003 select `test`.`t2`.`part_key` AS `part_key`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b` from `test`.`t2` PARTITION (`p0`) where (`test`.`t2`.`b` = 1) +set @@use_stat_tables= @save_use_stat_tables; +set @@optimizer_use_condition_selectivity= @save_optimizer_use_condition_selectivity; +set @@histogram_size= @save_histogram_size; +drop table t0,t1,t2; +# # End of 10.0 tests # # diff --git a/mysql-test/suite/innodb/r/innodb-index.result b/mysql-test/suite/innodb/r/innodb-index.result index 2b72f38a9d293..52344e1e4719d 100644 --- a/mysql-test/suite/innodb/r/innodb-index.result +++ b/mysql-test/suite/innodb/r/innodb-index.result @@ -1180,3 +1180,36 @@ t2c CREATE TABLE `t2c` ( KEY `t2a` (`a`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 DROP TABLE t1,t2,t2c,t2i; +SET @save_format = @@GLOBAL.innodb_file_format; +SET @save_prefix = @@GLOBAL.innodb_large_prefix; +SET GLOBAL innodb_file_format=barracuda; +SET GLOBAL innodb_large_prefix=ON; +CREATE TABLE t1 (c VARCHAR(1024), +c1 CHAR(255) NOT NULL,c2 CHAR(255) NOT NULL,c3 CHAR(255) NOT NULL, +c4 CHAR(255) NOT NULL,c5 CHAR(255) NOT NULL,c6 CHAR(255) NOT NULL, +c7 CHAR(255) NOT NULL,c8 CHAR(255) NOT NULL,c9 CHAR(255) NOT NULL, +ca CHAR(255) NOT NULL,cb CHAR(255) NOT NULL,cc CHAR(255) NOT NULL, +cd CHAR(255) NOT NULL,ce CHAR(255) NOT NULL,cf CHAR(255) NOT NULL, +d0 CHAR(255) NOT NULL,d1 CHAR(255) NOT NULL,d2 CHAR(255) NOT NULL, +d3 CHAR(255) NOT NULL,d4 CHAR(255) NOT NULL,d5 CHAR(255) NOT NULL, +d6 CHAR(255) NOT NULL,d7 CHAR(255) NOT NULL,d8 CHAR(255) NOT NULL, +d9 CHAR(255) NOT NULL,da CHAR(255) NOT NULL,db CHAR(255) NOT NULL, +dc CHAR(255) NOT NULL,dd CHAR(255) NOT NULL,de CHAR(255) NOT NULL, +UNIQUE KEY(c)) +ENGINE=InnoDB ROW_FORMAT=DYNAMIC; +INSERT INTO t1 VALUES +(repeat('a',999),'','','','','','','','','','','','','','','','','','','','','','','','','','','','','',''), +(CONCAT(repeat('a',999),'b'),'','','','','','','','','','','','','','','','','','','','','','','','','','','','','',''); +ALTER TABLE t1 ROW_FORMAT=REDUNDANT, algorithm=inplace; +ERROR HY000: Index column size too large. The maximum column size is 767 bytes. +ALTER TABLE t1 ROW_FORMAT=REDUNDANT, algorithm=copy; +ERROR HY000: Index column size too large. The maximum column size is 767 bytes. +SELECT COUNT(*) FROM t1; +COUNT(*) +2 +CHECK TABLE t1; +Table Op Msg_type Msg_text +test.t1 check status OK +DROP TABLE t1; +SET GLOBAL innodb_file_format=@save_format; +SET GLOBAL innodb_large_prefix=@save_prefix; diff --git a/mysql-test/suite/innodb/t/innodb-index.test b/mysql-test/suite/innodb/t/innodb-index.test index 8598647de6631..d28930de8152b 100644 --- a/mysql-test/suite/innodb/t/innodb-index.test +++ b/mysql-test/suite/innodb/t/innodb-index.test @@ -563,3 +563,33 @@ DROP TABLE t1,t2,t2c,t2i; eval SET GLOBAL innodb_file_format=$innodb_file_format_orig; eval SET GLOBAL innodb_file_format_max=$innodb_file_format_max_orig; --enable_query_log + +SET @save_format = @@GLOBAL.innodb_file_format; +SET @save_prefix = @@GLOBAL.innodb_large_prefix; +SET GLOBAL innodb_file_format=barracuda; +SET GLOBAL innodb_large_prefix=ON; +CREATE TABLE t1 (c VARCHAR(1024), +c1 CHAR(255) NOT NULL,c2 CHAR(255) NOT NULL,c3 CHAR(255) NOT NULL, +c4 CHAR(255) NOT NULL,c5 CHAR(255) NOT NULL,c6 CHAR(255) NOT NULL, +c7 CHAR(255) NOT NULL,c8 CHAR(255) NOT NULL,c9 CHAR(255) NOT NULL, +ca CHAR(255) NOT NULL,cb CHAR(255) NOT NULL,cc CHAR(255) NOT NULL, +cd CHAR(255) NOT NULL,ce CHAR(255) NOT NULL,cf CHAR(255) NOT NULL, +d0 CHAR(255) NOT NULL,d1 CHAR(255) NOT NULL,d2 CHAR(255) NOT NULL, +d3 CHAR(255) NOT NULL,d4 CHAR(255) NOT NULL,d5 CHAR(255) NOT NULL, +d6 CHAR(255) NOT NULL,d7 CHAR(255) NOT NULL,d8 CHAR(255) NOT NULL, +d9 CHAR(255) NOT NULL,da CHAR(255) NOT NULL,db CHAR(255) NOT NULL, +dc CHAR(255) NOT NULL,dd CHAR(255) NOT NULL,de CHAR(255) NOT NULL, +UNIQUE KEY(c)) +ENGINE=InnoDB ROW_FORMAT=DYNAMIC; +INSERT INTO t1 VALUES +(repeat('a',999),'','','','','','','','','','','','','','','','','','','','','','','','','','','','','',''), +(CONCAT(repeat('a',999),'b'),'','','','','','','','','','','','','','','','','','','','','','','','','','','','','',''); +--error ER_INDEX_COLUMN_TOO_LONG +ALTER TABLE t1 ROW_FORMAT=REDUNDANT, algorithm=inplace; +--error ER_INDEX_COLUMN_TOO_LONG +ALTER TABLE t1 ROW_FORMAT=REDUNDANT, algorithm=copy; +SELECT COUNT(*) FROM t1; +CHECK TABLE t1; +DROP TABLE t1; +SET GLOBAL innodb_file_format=@save_format; +SET GLOBAL innodb_large_prefix=@save_prefix; diff --git a/mysql-test/t/partition.test b/mysql-test/t/partition.test index 35dfdead91607..3b2445ad83dcd 100644 --- a/mysql-test/t/partition.test +++ b/mysql-test/t/partition.test @@ -2897,6 +2897,68 @@ EXECUTE stmt; DEALLOCATE PREPARE stmt; DROP TABLE t1; +--echo # +--echo # MDEV-17032: Estimates are higher for partitions of a table with @@use_stat_tables= PREFERABLY +--echo # + +create table t0(a int); +insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); + +create table t1(a int); +insert into t1 select A.a + B.a* 10 + C.a * 100 from t0 A, t0 B, t0 C; + + +create table t2 ( + part_key int, + a int, + b int +) partition by list(part_key) ( + partition p0 values in (0), + partition p1 values in (1), + partition p2 values in (2), + partition p3 values in (3), + partition p4 values in (4) +); +insert into t2 +select mod(a,5), a/100, mod(a,5) from t1; + +set @save_use_stat_tables= @@use_stat_tables; +set @save_optimizer_use_condition_selectivity=@@optimizer_use_condition_selectivity; +--echo # +--echo # Tests using stats provided by the storage engine +--echo # +explain extended select * from t2 where part_key=1; +explain partitions select * from t2 where part_key=1; +explain extended select * from t2 where part_key in (1,2); +explain partitions select * from t2 where part_key in (1,2); +explain extended select * from t2 where b=5; +explain partitions select * from t2 where b=5; +explain extended select * from t2 partition(p0) where b=1; + + +set @save_histogram_size=@@histogram_size; +set @@histogram_size=100; +set @@use_stat_tables= PREFERABLY; +set @@optimizer_use_condition_selectivity=4; +analyze table t2; +--echo # +--echo # Tests using EITS +--echo # +--echo # filtered should be 100 +explain extended select * from t2 where part_key=1; +explain partitions select * from t2 where part_key=1; +--echo # filtered should be 100 +explain extended select * from t2 where part_key in (1,2); +explain partitions select * from t2 where part_key in (1,2); +explain extended select * from t2 where b=5; +explain partitions select * from t2 where b=5; +explain extended select * from t2 partition(p0) where b=1; + +set @@use_stat_tables= @save_use_stat_tables; +set @@optimizer_use_condition_selectivity= @save_optimizer_use_condition_selectivity; +set @@histogram_size= @save_histogram_size; +drop table t0,t1,t2; + --echo # --echo # End of 10.0 tests --echo # diff --git a/sql/opt_range.cc b/sql/opt_range.cc index f3740a08fc682..41d74611abc1e 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -2734,14 +2734,17 @@ bool create_key_parts_for_pseudo_indexes(RANGE_OPT_PARAM *param, { Field **field_ptr; TABLE *table= param->table; + partition_info *part_info= NULL; + #ifdef WITH_PARTITION_STORAGE_ENGINE + part_info= table->part_info; + #endif uint parts= 0; for (field_ptr= table->field; *field_ptr; field_ptr++) { - Column_statistics* col_stats= (*field_ptr)->read_stats; - if (bitmap_is_set(used_fields, (*field_ptr)->field_index) - && col_stats && !col_stats->no_stat_values_provided() - && !((*field_ptr)->type() == MYSQL_TYPE_GEOMETRY)) + Field *field= *field_ptr; + if (bitmap_is_set(used_fields, field->field_index) && + is_eits_usable(field)) parts++; } @@ -2759,12 +2762,10 @@ bool create_key_parts_for_pseudo_indexes(RANGE_OPT_PARAM *param, uint max_key_len= 0; for (field_ptr= table->field; *field_ptr; field_ptr++) { - if (bitmap_is_set(used_fields, (*field_ptr)->field_index)) + Field *field= *field_ptr; + if (bitmap_is_set(used_fields, field->field_index)) { - Field *field= *field_ptr; - Column_statistics* col_stats= field->read_stats; - if (field->type() == MYSQL_TYPE_GEOMETRY || - !col_stats || col_stats->no_stat_values_provided()) + if (!is_eits_usable(field)) continue; uint16 store_length; diff --git a/sql/partition_info.cc b/sql/partition_info.cc index df0a733f1214c..74a1c2a267ad4 100644 --- a/sql/partition_info.cc +++ b/sql/partition_info.cc @@ -3143,6 +3143,23 @@ void partition_info::print_debug(const char *str, uint *value) DBUG_PRINT("info", ("parser: %s", str)); DBUG_VOID_RETURN; } + +bool partition_info::field_in_partition_expr(Field *field) const +{ + uint i; + for (i= 0; i < num_part_fields; i++) + { + if (field->eq(part_field_array[i])) + return TRUE; + } + for (i= 0; i < num_subpart_fields; i++) + { + if (field->eq(subpart_field_array[i])) + return TRUE; + } + return FALSE; +} + #else /* WITH_PARTITION_STORAGE_ENGINE */ /* For builds without partitioning we need to define these functions diff --git a/sql/partition_info.h b/sql/partition_info.h index b271ff08e82e1..0f38c87dd4e31 100644 --- a/sql/partition_info.h +++ b/sql/partition_info.h @@ -384,6 +384,7 @@ class partition_info : public Sql_alloc bool is_full_part_expr_in_fields(List &fields); public: bool has_unique_name(partition_element *element); + bool field_in_partition_expr(Field *field) const; }; uint32 get_next_partition_id_range(struct st_partition_iter* part_iter); diff --git a/sql/sql_statistics.cc b/sql/sql_statistics.cc index 739930f46fccb..7d73a894bdc2e 100644 --- a/sql/sql_statistics.cc +++ b/sql/sql_statistics.cc @@ -30,6 +30,7 @@ #include "opt_range.h" #include "my_atomic.h" #include "sql_show.h" +#include "sql_partition.h" /* The system variable 'use_stat_tables' can take one of the @@ -3722,6 +3723,22 @@ void set_statistics_for_table(THD *thd, TABLE *table) (use_stat_table_mode <= COMPLEMENTARY || !table->stats_is_read || read_stats->cardinality_is_null) ? table->file->stats.records : read_stats->cardinality; + + /* + For partitioned table, EITS statistics is based on data from all partitions. + + On the other hand, Partition Pruning figures which partitions will be + accessed and then computes the estimate of rows in used_partitions. + + Use the estimate from Partition Pruning as it is typically more precise. + Ideally, EITS should provide per-partition statistics but this is not + implemented currently. + */ + #ifdef WITH_PARTITION_STORAGE_ENGINE + if (table->part_info) + table->used_stat_records= table->file->stats.records; + #endif + KEY *key_info, *key_info_end; for (key_info= table->key_info, key_info_end= key_info+table->s->keys; key_info < key_info_end; key_info++) @@ -4037,3 +4054,31 @@ bool is_stat_table(const char *db, const char *table) } return false; } + +/* + Check wheter we can use EITS statistics for a field or not + + TRUE : Use EITS for the columns + FALSE: Otherwise +*/ + +bool is_eits_usable(Field *field) +{ + partition_info *part_info= NULL; + #ifdef WITH_PARTITION_STORAGE_ENGINE + part_info= field->table->part_info; + #endif + /* + (1): checks if we have EITS statistics for a particular column + (2): Don't use EITS for GEOMETRY columns + (3): Disabling reading EITS statistics for columns involved in the + partition list of a table. We assume the selecticivity for + such columns would be handled during partition pruning. + */ + Column_statistics* col_stats= field->read_stats; + if (col_stats && !col_stats->no_stat_values_provided() && //(1) + field->type() != MYSQL_TYPE_GEOMETRY && //(2) + (!part_info || !part_info->field_in_partition_expr(field))) //(3) + return TRUE; + return FALSE; +} diff --git a/sql/sql_statistics.h b/sql/sql_statistics.h index d30691c32f075..6530c2d6c8f5b 100644 --- a/sql/sql_statistics.h +++ b/sql/sql_statistics.h @@ -112,6 +112,7 @@ double get_column_range_cardinality(Field *field, key_range *max_endp, uint range_flag); bool is_stat_table(const char *db, const char *table); +bool is_eits_usable(Field* field); class Histogram { diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc index c32d8a3403894..1bfeaaa4ececa 100644 --- a/storage/innobase/handler/handler0alter.cc +++ b/storage/innobase/handler/handler0alter.cc @@ -3687,9 +3687,8 @@ ha_innobase::prepare_inplace_alter_table( /* Check each index's column length to make sure they do not exceed limit */ - for (ulint i = 0; i < ha_alter_info->index_add_count; i++) { - const KEY* key = &ha_alter_info->key_info_buffer[ - ha_alter_info->index_add_buffer[i]]; + for (ulint i = 0; i < ha_alter_info->key_count; i++) { + const KEY* key = &ha_alter_info->key_info_buffer[i]; if (key->flags & HA_FULLTEXT) { /* The column length does not matter for diff --git a/storage/xtradb/handler/handler0alter.cc b/storage/xtradb/handler/handler0alter.cc index 8f5d20565e7aa..7ea30bee8ae2d 100644 --- a/storage/xtradb/handler/handler0alter.cc +++ b/storage/xtradb/handler/handler0alter.cc @@ -3703,9 +3703,8 @@ ha_innobase::prepare_inplace_alter_table( /* Check each index's column length to make sure they do not exceed limit */ - for (ulint i = 0; i < ha_alter_info->index_add_count; i++) { - const KEY* key = &ha_alter_info->key_info_buffer[ - ha_alter_info->index_add_buffer[i]]; + for (ulint i = 0; i < ha_alter_info->key_count; i++) { + const KEY* key = &ha_alter_info->key_info_buffer[i]; if (key->flags & HA_FULLTEXT) { /* The column length does not matter for