diff options
-rw-r--r-- | mysql-test/r/stat_tables.result | 116 | ||||
-rw-r--r-- | mysql-test/r/stat_tables_innodb.result | 172 | ||||
-rw-r--r-- | mysql-test/t/stat_tables.test | 109 | ||||
-rw-r--r-- | sql/field.cc | 137 | ||||
-rw-r--r-- | sql/field.h | 83 | ||||
-rw-r--r-- | sql/mysqld.cc | 3 | ||||
-rw-r--r-- | sql/sql_admin.cc | 2 | ||||
-rw-r--r-- | sql/sql_base.cc | 28 | ||||
-rw-r--r-- | sql/sql_base.h | 3 | ||||
-rw-r--r-- | sql/sql_select.cc | 9 | ||||
-rw-r--r-- | sql/sql_statistics.cc | 731 | ||||
-rw-r--r-- | sql/sql_statistics.h | 155 | ||||
-rw-r--r-- | sql/structs.h | 41 | ||||
-rw-r--r-- | sql/table.cc | 30 | ||||
-rw-r--r-- | sql/table.h | 25 |
15 files changed, 1217 insertions, 427 deletions
diff --git a/mysql-test/r/stat_tables.result b/mysql-test/r/stat_tables.result index 26f2b602c6b..09486d4ce0b 100644 --- a/mysql-test/r/stat_tables.result +++ b/mysql-test/r/stat_tables.result @@ -334,6 +334,122 @@ and o_orderkey=l_orderkey and p_partkey=l_partkey; o_orderkey p_partkey 5895 200 set optimizer_switch=@save_optimizer_switch; +flush table lineitem; +set use_stat_tables='never'; +select sum(l_extendedprice*l_discount) as revenue +from lineitem +where l_shipdate >= date '1994-01-01' +and l_shipdate < date '1994-01-01' + interval '1' year +and l_discount between 0.06 - 0.01 and 0.06 + 0.01 +and l_quantity < 24; +revenue +77949.91860000002 +set debug_sync='statistics_mem_alloc_start1 WAIT_FOR second_thread_started_too'; +set debug_sync='statistics_mem_alloc_start2 SIGNAL first_thread_working'; +use dbt3_s001; +set use_stat_tables='preferably'; +select sum(l_extendedprice*l_discount) as revenue +from lineitem +where l_shipdate >= date '1994-01-01' +and l_shipdate < date '1994-01-01' + interval '1' year +and l_discount between 0.06 - 0.01 and 0.06 + 0.01 +and l_quantity < 24 ; +set debug_sync='statistics_mem_alloc_start1 SIGNAL second_thread_started_too'; +set debug_sync='statistics_mem_alloc_start2 WAIT_FOR first_thread_working'; +use dbt3_s001; +set use_stat_tables='preferably'; +select sum(l_extendedprice*l_discount) as revenue +from lineitem +where l_shipdate >= date '1994-01-01' +and l_shipdate < date '1994-01-01' + interval '1' year +and l_discount between 0.06 - 0.01 and 0.06 + 0.01 +and l_quantity < 24; +revenue +77949.91860000002 +revenue +77949.91860000002 +set use_stat_tables='preferably'; +select * from mysql.index_stat where table_name='lineitem' order by index_name; +db_name table_name index_name prefix_arity avg_frequency +dbt3_s001 lineitem PRIMARY 1 4.0033 +dbt3_s001 lineitem PRIMARY 2 1.0000 +dbt3_s001 lineitem i_l_commitdate 1 2.7160 +dbt3_s001 lineitem i_l_orderkey 1 4.0033 +dbt3_s001 lineitem i_l_orderkey_quantity 1 4.0033 +dbt3_s001 lineitem i_l_orderkey_quantity 2 1.0404 +dbt3_s001 lineitem i_l_partkey 1 30.0250 +dbt3_s001 lineitem i_l_receiptdate 1 2.6477 +dbt3_s001 lineitem i_l_shipdate 1 2.6500 +dbt3_s001 lineitem i_l_suppkey 1 600.5000 +dbt3_s001 lineitem i_l_suppkey_partkey 1 30.0250 +dbt3_s001 lineitem i_l_suppkey_partkey 2 8.5786 +delete from mysql.index_stat +where table_name='lineitem' and +index_name in ('i_l_shipdate', 'i_l_receiptdate'); +select * from mysql.index_stat where table_name='lineitem' order by index_name; +db_name table_name index_name prefix_arity avg_frequency +dbt3_s001 lineitem PRIMARY 1 4.0033 +dbt3_s001 lineitem PRIMARY 2 1.0000 +dbt3_s001 lineitem i_l_commitdate 1 2.7160 +dbt3_s001 lineitem i_l_orderkey 1 4.0033 +dbt3_s001 lineitem i_l_orderkey_quantity 1 4.0033 +dbt3_s001 lineitem i_l_orderkey_quantity 2 1.0404 +dbt3_s001 lineitem i_l_partkey 1 30.0250 +dbt3_s001 lineitem i_l_suppkey 1 600.5000 +dbt3_s001 lineitem i_l_suppkey_partkey 2 8.5786 +dbt3_s001 lineitem i_l_suppkey_partkey 1 30.0250 +analyze table lineitem persistent for columns() indexes (i_l_shipdate); +select * from mysql.index_stat where table_name='lineitem' order by index_name; +db_name table_name index_name prefix_arity avg_frequency +dbt3_s001 lineitem PRIMARY 1 4.0033 +dbt3_s001 lineitem PRIMARY 2 1.0000 +dbt3_s001 lineitem i_l_commitdate 1 2.7160 +dbt3_s001 lineitem i_l_orderkey 1 4.0033 +dbt3_s001 lineitem i_l_orderkey_quantity 1 4.0033 +dbt3_s001 lineitem i_l_orderkey_quantity 2 1.0404 +dbt3_s001 lineitem i_l_partkey 1 30.0250 +dbt3_s001 lineitem i_l_shipdate 1 2.6500 +dbt3_s001 lineitem i_l_suppkey 1 600.5000 +dbt3_s001 lineitem i_l_suppkey_partkey 2 8.5786 +dbt3_s001 lineitem i_l_suppkey_partkey 1 30.0250 +delete from mysql.index_stat +where table_name='lineitem' and index_name= 'i_l_shipdate'; +select * from mysql.index_stat where table_name='lineitem' order by index_name; +db_name table_name index_name prefix_arity avg_frequency +dbt3_s001 lineitem PRIMARY 1 4.0033 +dbt3_s001 lineitem PRIMARY 2 1.0000 +dbt3_s001 lineitem i_l_commitdate 1 2.7160 +dbt3_s001 lineitem i_l_orderkey 1 4.0033 +dbt3_s001 lineitem i_l_orderkey_quantity 1 4.0033 +dbt3_s001 lineitem i_l_orderkey_quantity 2 1.0404 +dbt3_s001 lineitem i_l_partkey 1 30.0250 +dbt3_s001 lineitem i_l_suppkey 1 600.5000 +dbt3_s001 lineitem i_l_suppkey_partkey 2 8.5786 +dbt3_s001 lineitem i_l_suppkey_partkey 1 30.0250 +set debug_sync='statistics_collection_start1 WAIT_FOR second_thread_started_too'; +set debug_sync='statistics_collection_start2 SIGNAL first_thread_working'; +use dbt3_s001; +set use_stat_tables='preferably'; +analyze table lineitem persistent for columns() indexes (i_l_shipdate); +set debug_sync='statistics_collection_start1 SIGNAL second_thread_started_too'; +set debug_sync='statistics_collection_start2 WAIT_FOR first_thread_working'; +use dbt3_s001; +set use_stat_tables='preferably'; +analyze table lineitem persistent for columns() indexes (i_l_receiptdate); +select * from mysql.index_stat where table_name='lineitem' order by index_name; +db_name table_name index_name prefix_arity avg_frequency +dbt3_s001 lineitem PRIMARY 1 4.0033 +dbt3_s001 lineitem PRIMARY 2 1.0000 +dbt3_s001 lineitem i_l_commitdate 1 2.7160 +dbt3_s001 lineitem i_l_orderkey 1 4.0033 +dbt3_s001 lineitem i_l_orderkey_quantity 1 4.0033 +dbt3_s001 lineitem i_l_orderkey_quantity 2 1.0404 +dbt3_s001 lineitem i_l_partkey 1 30.0250 +dbt3_s001 lineitem i_l_receiptdate 1 2.6477 +dbt3_s001 lineitem i_l_shipdate 1 2.6500 +dbt3_s001 lineitem i_l_suppkey 1 600.5000 +dbt3_s001 lineitem i_l_suppkey_partkey 1 30.0250 +dbt3_s001 lineitem i_l_suppkey_partkey 2 8.5786 DROP DATABASE dbt3_s001; use test; set use_stat_tables=@save_use_stat_tables; diff --git a/mysql-test/r/stat_tables_innodb.result b/mysql-test/r/stat_tables_innodb.result index 3d534dcab29..4cb1261b689 100644 --- a/mysql-test/r/stat_tables_innodb.result +++ b/mysql-test/r/stat_tables_innodb.result @@ -361,6 +361,178 @@ and o_orderkey=l_orderkey and p_partkey=l_partkey; o_orderkey p_partkey 5895 200 set optimizer_switch=@save_optimizer_switch; +flush table lineitem; +set use_stat_tables='never'; +select sum(l_extendedprice*l_discount) as revenue +from lineitem +where l_shipdate >= date '1994-01-01' +and l_shipdate < date '1994-01-01' + interval '1' year +and l_discount between 0.06 - 0.01 and 0.06 + 0.01 +and l_quantity < 24; +revenue +77949.91860000002 +set debug_sync='statistics_mem_alloc_start1 WAIT_FOR second_thread_started_too'; +set debug_sync='statistics_mem_alloc_start2 SIGNAL first_thread_working'; +use dbt3_s001; +set use_stat_tables='preferably'; +select sum(l_extendedprice*l_discount) as revenue +from lineitem +where l_shipdate >= date '1994-01-01' +and l_shipdate < date '1994-01-01' + interval '1' year +and l_discount between 0.06 - 0.01 and 0.06 + 0.01 +and l_quantity < 24 ; +set debug_sync='statistics_mem_alloc_start1 SIGNAL second_thread_started_too'; +set debug_sync='statistics_mem_alloc_start2 WAIT_FOR first_thread_working'; +use dbt3_s001; +set use_stat_tables='preferably'; +select sum(l_extendedprice*l_discount) as revenue +from lineitem +where l_shipdate >= date '1994-01-01' +and l_shipdate < date '1994-01-01' + interval '1' year +and l_discount between 0.06 - 0.01 and 0.06 + 0.01 +and l_quantity < 24; +revenue +77949.91860000002 +revenue +77949.91860000002 +set use_stat_tables='preferably'; +select * from mysql.index_stat where table_name='lineitem' order by index_name; +db_name table_name index_name prefix_arity avg_frequency +dbt3_s001 lineitem PRIMARY 1 4.0033 +dbt3_s001 lineitem PRIMARY 2 1.0000 +dbt3_s001 lineitem i_l_commitdate 2 1.0364 +dbt3_s001 lineitem i_l_commitdate 1 2.7160 +dbt3_s001 lineitem i_l_commitdate 3 1.0000 +dbt3_s001 lineitem i_l_orderkey 1 4.0033 +dbt3_s001 lineitem i_l_orderkey 2 1.0000 +dbt3_s001 lineitem i_l_orderkey_quantity 1 4.0033 +dbt3_s001 lineitem i_l_orderkey_quantity 2 1.0404 +dbt3_s001 lineitem i_l_orderkey_quantity 3 1.0000 +dbt3_s001 lineitem i_l_partkey 1 30.0250 +dbt3_s001 lineitem i_l_partkey 2 1.0089 +dbt3_s001 lineitem i_l_partkey 3 1.0000 +dbt3_s001 lineitem i_l_receiptdate 3 1.0000 +dbt3_s001 lineitem i_l_receiptdate 2 1.0152 +dbt3_s001 lineitem i_l_receiptdate 1 2.6477 +dbt3_s001 lineitem i_l_shipdate 1 2.6500 +dbt3_s001 lineitem i_l_shipdate 3 1.0000 +dbt3_s001 lineitem i_l_shipdate 2 1.0149 +dbt3_s001 lineitem i_l_suppkey 2 1.2073 +dbt3_s001 lineitem i_l_suppkey 3 1.0000 +dbt3_s001 lineitem i_l_suppkey 1 600.5000 +dbt3_s001 lineitem i_l_suppkey_partkey 4 1.0000 +dbt3_s001 lineitem i_l_suppkey_partkey 3 1.0030 +dbt3_s001 lineitem i_l_suppkey_partkey 2 8.5786 +dbt3_s001 lineitem i_l_suppkey_partkey 1 30.0250 +delete from mysql.index_stat +where table_name='lineitem' and +index_name in ('i_l_shipdate', 'i_l_receiptdate'); +select * from mysql.index_stat where table_name='lineitem' order by index_name; +db_name table_name index_name prefix_arity avg_frequency +dbt3_s001 lineitem PRIMARY 1 4.0033 +dbt3_s001 lineitem PRIMARY 2 1.0000 +dbt3_s001 lineitem i_l_commitdate 2 1.0364 +dbt3_s001 lineitem i_l_commitdate 1 2.7160 +dbt3_s001 lineitem i_l_commitdate 3 1.0000 +dbt3_s001 lineitem i_l_orderkey 1 4.0033 +dbt3_s001 lineitem i_l_orderkey 2 1.0000 +dbt3_s001 lineitem i_l_orderkey_quantity 3 1.0000 +dbt3_s001 lineitem i_l_orderkey_quantity 2 1.0404 +dbt3_s001 lineitem i_l_orderkey_quantity 1 4.0033 +dbt3_s001 lineitem i_l_partkey 1 30.0250 +dbt3_s001 lineitem i_l_partkey 2 1.0089 +dbt3_s001 lineitem i_l_partkey 3 1.0000 +dbt3_s001 lineitem i_l_suppkey 2 1.2073 +dbt3_s001 lineitem i_l_suppkey 3 1.0000 +dbt3_s001 lineitem i_l_suppkey 1 600.5000 +dbt3_s001 lineitem i_l_suppkey_partkey 3 1.0030 +dbt3_s001 lineitem i_l_suppkey_partkey 2 8.5786 +dbt3_s001 lineitem i_l_suppkey_partkey 1 30.0250 +dbt3_s001 lineitem i_l_suppkey_partkey 4 1.0000 +analyze table lineitem persistent for columns() indexes (i_l_shipdate); +select * from mysql.index_stat where table_name='lineitem' order by index_name; +db_name table_name index_name prefix_arity avg_frequency +dbt3_s001 lineitem PRIMARY 1 4.0033 +dbt3_s001 lineitem PRIMARY 2 1.0000 +dbt3_s001 lineitem i_l_commitdate 2 1.0364 +dbt3_s001 lineitem i_l_commitdate 1 2.7160 +dbt3_s001 lineitem i_l_commitdate 3 1.0000 +dbt3_s001 lineitem i_l_orderkey 1 4.0033 +dbt3_s001 lineitem i_l_orderkey 2 1.0000 +dbt3_s001 lineitem i_l_orderkey_quantity 1 4.0033 +dbt3_s001 lineitem i_l_orderkey_quantity 2 1.0404 +dbt3_s001 lineitem i_l_orderkey_quantity 3 1.0000 +dbt3_s001 lineitem i_l_partkey 3 1.0000 +dbt3_s001 lineitem i_l_partkey 2 1.0089 +dbt3_s001 lineitem i_l_partkey 1 30.0250 +dbt3_s001 lineitem i_l_shipdate 3 1.0000 +dbt3_s001 lineitem i_l_shipdate 2 1.0149 +dbt3_s001 lineitem i_l_shipdate 1 2.6500 +dbt3_s001 lineitem i_l_suppkey 3 1.0000 +dbt3_s001 lineitem i_l_suppkey 2 1.2073 +dbt3_s001 lineitem i_l_suppkey 1 600.5000 +dbt3_s001 lineitem i_l_suppkey_partkey 3 1.0030 +dbt3_s001 lineitem i_l_suppkey_partkey 2 8.5786 +dbt3_s001 lineitem i_l_suppkey_partkey 1 30.0250 +dbt3_s001 lineitem i_l_suppkey_partkey 4 1.0000 +delete from mysql.index_stat +where table_name='lineitem' and index_name= 'i_l_shipdate'; +select * from mysql.index_stat where table_name='lineitem' order by index_name; +db_name table_name index_name prefix_arity avg_frequency +dbt3_s001 lineitem PRIMARY 1 4.0033 +dbt3_s001 lineitem PRIMARY 2 1.0000 +dbt3_s001 lineitem i_l_commitdate 2 1.0364 +dbt3_s001 lineitem i_l_commitdate 1 2.7160 +dbt3_s001 lineitem i_l_commitdate 3 1.0000 +dbt3_s001 lineitem i_l_orderkey 1 4.0033 +dbt3_s001 lineitem i_l_orderkey 2 1.0000 +dbt3_s001 lineitem i_l_orderkey_quantity 3 1.0000 +dbt3_s001 lineitem i_l_orderkey_quantity 2 1.0404 +dbt3_s001 lineitem i_l_orderkey_quantity 1 4.0033 +dbt3_s001 lineitem i_l_partkey 1 30.0250 +dbt3_s001 lineitem i_l_partkey 2 1.0089 +dbt3_s001 lineitem i_l_partkey 3 1.0000 +dbt3_s001 lineitem i_l_suppkey 2 1.2073 +dbt3_s001 lineitem i_l_suppkey 3 1.0000 +dbt3_s001 lineitem i_l_suppkey 1 600.5000 +dbt3_s001 lineitem i_l_suppkey_partkey 3 1.0030 +dbt3_s001 lineitem i_l_suppkey_partkey 2 8.5786 +dbt3_s001 lineitem i_l_suppkey_partkey 1 30.0250 +dbt3_s001 lineitem i_l_suppkey_partkey 4 1.0000 +set debug_sync='statistics_collection_start1 WAIT_FOR second_thread_started_too'; +set debug_sync='statistics_collection_start2 SIGNAL first_thread_working'; +use dbt3_s001; +set use_stat_tables='preferably'; +analyze table lineitem persistent for columns() indexes (i_l_shipdate); +set debug_sync='statistics_collection_start1 SIGNAL second_thread_started_too'; +set debug_sync='statistics_collection_start2 WAIT_FOR first_thread_working'; +use dbt3_s001; +set use_stat_tables='preferably'; +analyze table lineitem persistent for columns() indexes (i_l_receiptdate); +select * from mysql.index_stat where table_name='lineitem' order by index_name; +db_name table_name index_name prefix_arity avg_frequency +dbt3_s001 lineitem PRIMARY 1 4.0033 +dbt3_s001 lineitem PRIMARY 2 1.0000 +dbt3_s001 lineitem i_l_commitdate 2 1.0364 +dbt3_s001 lineitem i_l_commitdate 1 2.7160 +dbt3_s001 lineitem i_l_commitdate 3 1.0000 +dbt3_s001 lineitem i_l_orderkey 1 4.0033 +dbt3_s001 lineitem i_l_orderkey 2 1.0000 +dbt3_s001 lineitem i_l_orderkey_quantity 1 4.0033 +dbt3_s001 lineitem i_l_orderkey_quantity 2 1.0404 +dbt3_s001 lineitem i_l_orderkey_quantity 3 1.0000 +dbt3_s001 lineitem i_l_partkey 3 1.0000 +dbt3_s001 lineitem i_l_partkey 2 1.0089 +dbt3_s001 lineitem i_l_partkey 1 30.0250 +dbt3_s001 lineitem i_l_receiptdate 1 2.6477 +dbt3_s001 lineitem i_l_shipdate 1 2.6500 +dbt3_s001 lineitem i_l_suppkey 3 1.0000 +dbt3_s001 lineitem i_l_suppkey 2 1.2073 +dbt3_s001 lineitem i_l_suppkey 1 600.5000 +dbt3_s001 lineitem i_l_suppkey_partkey 3 1.0030 +dbt3_s001 lineitem i_l_suppkey_partkey 2 8.5786 +dbt3_s001 lineitem i_l_suppkey_partkey 1 30.0250 +dbt3_s001 lineitem i_l_suppkey_partkey 4 1.0000 DROP DATABASE dbt3_s001; use test; set use_stat_tables=@save_use_stat_tables; diff --git a/mysql-test/t/stat_tables.test b/mysql-test/t/stat_tables.test index 640f9febc75..bdb689228b2 100644 --- a/mysql-test/t/stat_tables.test +++ b/mysql-test/t/stat_tables.test @@ -144,6 +144,115 @@ eval $QQ1; set optimizer_switch=@save_optimizer_switch; +# +# Test for parallel memory allocation for statistical data +# +# assumes that start the code of memory allocation for stats data has this line: +# +# DEBUG_SYNC(thd, "statistics_mem_alloc_start1"); +# DEBUG_SYNC(thd, "statistics_mem_alloc-start2"); +# + +let $Q6= +select sum(l_extendedprice*l_discount) as revenue +from lineitem +where l_shipdate >= date '1994-01-01' + and l_shipdate < date '1994-01-01' + interval '1' year + and l_discount between 0.06 - 0.01 and 0.06 + 0.01 + and l_quantity < 24; + +flush table lineitem; +set use_stat_tables='never'; +eval $Q6; + +connect (con1, localhost, root,,); +connect (con2, localhost, root,,); + +connection con1; +set debug_sync='statistics_mem_alloc_start1 WAIT_FOR second_thread_started_too'; +set debug_sync='statistics_mem_alloc_start2 SIGNAL first_thread_working'; +use dbt3_s001; +set use_stat_tables='preferably'; +--send_eval $Q6 + +connection con2; +set debug_sync='statistics_mem_alloc_start1 SIGNAL second_thread_started_too'; +set debug_sync='statistics_mem_alloc_start2 WAIT_FOR first_thread_working'; +use dbt3_s001; +set use_stat_tables='preferably'; +--send_eval $Q6 + +connection con1; +--reap + +connection con2; +--reap + +connection default; +set use_stat_tables='preferably'; +disconnect con1; +disconnect con2; + +# +# Test for parallel statistics collection +# +# assumes that start of stats collection code has this line: +# +# DEBUG_SYNC(thd, "statistics_collection_start1"); +# DEBUG_SYNC(thd, "statistics_collection_start2"); +# + +select * from mysql.index_stat where table_name='lineitem' order by index_name; +delete from mysql.index_stat + where table_name='lineitem' and + index_name in ('i_l_shipdate', 'i_l_receiptdate'); +select * from mysql.index_stat where table_name='lineitem' order by index_name; +--disable_result_log +--disable_warnings +analyze table lineitem persistent for columns() indexes (i_l_shipdate); +--enable_warnings +--enable_result_log +select * from mysql.index_stat where table_name='lineitem' order by index_name; +delete from mysql.index_stat + where table_name='lineitem' and index_name= 'i_l_shipdate'; +select * from mysql.index_stat where table_name='lineitem' order by index_name; + +connect (con1, localhost, root,,); +connect (con2, localhost, root,,); + +connection con1; +set debug_sync='statistics_collection_start1 WAIT_FOR second_thread_started_too'; +set debug_sync='statistics_collection_start2 SIGNAL first_thread_working'; +use dbt3_s001; +set use_stat_tables='preferably'; +--send analyze table lineitem persistent for columns() indexes (i_l_shipdate) + +connection con2; +set debug_sync='statistics_collection_start1 SIGNAL second_thread_started_too'; +set debug_sync='statistics_collection_start2 WAIT_FOR first_thread_working'; +use dbt3_s001; +set use_stat_tables='preferably'; +--send analyze table lineitem persistent for columns() indexes (i_l_receiptdate) + +connection con1; +--disable_result_log +--disable_warnings +--reap +--enable_warnings +--enable_result_log + +connection con2; +--disable_result_log +--disable_warnings +--reap +--enable_warnings +--enable_result_log + +connection default; +disconnect con1; +disconnect con2; + +select * from mysql.index_stat where table_name='lineitem' order by index_name; DROP DATABASE dbt3_s001; diff --git a/sql/field.cc b/sql/field.cc index d37679451fc..406128ba484 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -1180,11 +1180,11 @@ int Field_num::check_int(CHARSET_INFO *cs, const char *str, int length, if (str == int_end || error == MY_ERRNO_EDOM) { ErrConvString err(str, length, cs); - push_warning_printf(table->in_use, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(get_thd(), MYSQL_ERROR::WARN_LEVEL_WARN, ER_TRUNCATED_WRONG_VALUE_FOR_FIELD, ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD), "integer", err.ptr(), field_name, - (ulong) table->in_use->warning_info->current_row_for_warning()); + (ulong) get_thd()->warning_info->current_row_for_warning()); return 1; } /* Test if we have garbage at the end of the given string. */ @@ -1253,7 +1253,7 @@ bool Field_num::get_int(CHARSET_INFO *cs, const char *from, uint len, goto out_of_range; } } - if (table->in_use->count_cuted_fields && + if (get_thd()->count_cuted_fields && check_int(cs, from, len, end, error)) return 1; return 0; @@ -1319,12 +1319,14 @@ String *Field::val_int_as_str(String *val_buffer, bool unsigned_val) Field::Field(uchar *ptr_arg,uint32 length_arg,uchar *null_ptr_arg, uchar null_bit_arg, utype unireg_check_arg, const char *field_name_arg) - :ptr(ptr_arg), null_ptr(null_ptr_arg), table(0), orig_table(0), + :ptr(ptr_arg), null_ptr(null_ptr_arg), table(0), orig_table(0), thd(0), table_name(0), field_name(field_name_arg), option_list(0), option_struct(0), key_start(0), part_of_key(0), part_of_key_not_clustered(0), part_of_sortkey(0), unireg_check(unireg_check_arg), field_length(length_arg), - null_bit(null_bit_arg), is_created_from_null_item(FALSE), vcol_info(0), + null_bit(null_bit_arg), is_created_from_null_item(FALSE), + read_stats(NULL), collected_stats(0), + vcol_info(0), stored_in_db(TRUE) { flags=null_ptr ? 0: NOT_NULL_FLAG; @@ -1431,10 +1433,11 @@ int Field::store(const char *to, uint length, CHARSET_INFO *cs, enum_check_fields check_level) { int res; - enum_check_fields old_check_level= table->in_use->count_cuted_fields; - table->in_use->count_cuted_fields= check_level; + THD *thd= get_thd(); + enum_check_fields old_check_level= thd->count_cuted_fields; + thd->count_cuted_fields= check_level; res= store(to, length, cs); - table->in_use->count_cuted_fields= old_check_level; + thd->count_cuted_fields= old_check_level; return res; } @@ -1871,6 +1874,18 @@ Field *Field::clone(MEM_ROOT *root, TABLE *new_table, my_ptrdiff_t diff, } +Field *Field::clone(THD *thd_arg, MEM_ROOT *root, my_ptrdiff_t diff) +{ + Field *tmp; + if ((tmp= (Field*) memdup_root(root,(char*) this,size_of()))) + { + tmp->thd= thd_arg; + tmp->move_field_offset(diff); + } + return tmp; +} + + /**************************************************************************** Field_null, a field that always return NULL ****************************************************************************/ @@ -1985,7 +2000,7 @@ int Field_decimal::store(const char *from_arg, uint len, CHARSET_INFO *cs) uchar *left_wall,*right_wall; uchar tmp_char; /* - To remember if table->in_use->cuted_fields has already been incremented, + To remember if get_thd()->cuted_fields has already been incremented, to do that only once */ bool is_cuted_fields_incr=0; @@ -2076,7 +2091,7 @@ int Field_decimal::store(const char *from_arg, uint len, CHARSET_INFO *cs) it makes the code easer to read. */ - if (table->in_use->count_cuted_fields) + if (get_thd()->count_cuted_fields) { // Skip end spaces for (;from != end && my_isspace(&my_charset_bin, *from); from++) ; @@ -2228,7 +2243,7 @@ int Field_decimal::store(const char *from_arg, uint len, CHARSET_INFO *cs) /* Write digits of the frac_% parts ; - Depending on table->in_use->count_cutted_fields, we may also want + Depending on get_thd()->count_cutted_fields, we may also want to know if some non-zero tail of these parts will be truncated (for example, 0.002->0.00 will generate a warning, while 0.000->0.00 will not) @@ -2246,7 +2261,7 @@ int Field_decimal::store(const char *from_arg, uint len, CHARSET_INFO *cs) { if (pos == right_wall) { - if (table->in_use->count_cuted_fields && !is_cuted_fields_incr) + if (get_thd()->count_cuted_fields && !is_cuted_fields_incr) break; // Go on below to see if we lose non zero digits return 0; } @@ -2667,20 +2682,21 @@ int Field_new_decimal::store(const char *from, uint length, ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int err; my_decimal decimal_value; + THD *thd= get_thd(); DBUG_ENTER("Field_new_decimal::store(char*)"); if ((err= str2my_decimal(E_DEC_FATAL_ERROR & ~(E_DEC_OVERFLOW | E_DEC_BAD_NUM), from, length, charset_arg, &decimal_value)) && - table->in_use->abort_on_warning) + thd->abort_on_warning) { ErrConvString errmsg(from, length, &my_charset_bin); - push_warning_printf(table->in_use, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_TRUNCATED_WRONG_VALUE_FOR_FIELD, ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD), "decimal", errmsg.ptr(), field_name, - (ulong) table->in_use->warning_info->current_row_for_warning()); + (ulong) thd->warning_info->current_row_for_warning()); DBUG_RETURN(err); } @@ -2696,11 +2712,11 @@ int Field_new_decimal::store(const char *from, uint length, case E_DEC_BAD_NUM: { ErrConvString errmsg(from, length, &my_charset_bin); - push_warning_printf(table->in_use, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_TRUNCATED_WRONG_VALUE_FOR_FIELD, ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD), "decimal", errmsg.ptr(), field_name, - (ulong) table->in_use->warning_info-> + (ulong) thd->warning_info-> current_row_for_warning()); my_decimal_set_zero(&decimal_value); break; @@ -2728,6 +2744,7 @@ int Field_new_decimal::store(double nr) ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; my_decimal decimal_value; int err; + THD *thd= get_thd(); DBUG_ENTER("Field_new_decimal::store(double)"); err= double2my_decimal(E_DEC_FATAL_ERROR & ~E_DEC_OVERFLOW, nr, @@ -2737,11 +2754,11 @@ int Field_new_decimal::store(double nr) if (check_overflow(err)) set_value_on_overflow(&decimal_value, decimal_value.sign()); /* Only issue a warning if store_value doesn't issue an warning */ - table->in_use->got_warning= 0; + thd->got_warning= 0; } if (store_value(&decimal_value)) err= 1; - else if (err && !table->in_use->got_warning) + else if (err && !thd->got_warning) err= warn_if_overflow(err); DBUG_RETURN(err); } @@ -2759,11 +2776,11 @@ int Field_new_decimal::store(longlong nr, bool unsigned_val) if (check_overflow(err)) set_value_on_overflow(&decimal_value, decimal_value.sign()); /* Only issue a warning if store_value doesn't issue an warning */ - table->in_use->got_warning= 0; + get_thd()->got_warning= 0; } if (store_value(&decimal_value)) err= 1; - else if (err && !table->in_use->got_warning) + else if (err && !thd->got_warning) err= warn_if_overflow(err); return err; } @@ -3659,7 +3676,7 @@ longlong Field_long::val_int(void) ASSERT_COLUMN_MARKED_FOR_READ; int32 j; /* See the comment in Field_long::store(long long) */ - DBUG_ASSERT(table->in_use == current_thd); + DBUG_ASSERT(!table || table->in_use == current_thd); j=sint4korr(ptr); return unsigned_flag ? (longlong) (uint32) j : (longlong) j; } @@ -3741,7 +3758,7 @@ int Field_longlong::store(const char *from,uint len,CHARSET_INFO *cs) set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } - else if (table->in_use->count_cuted_fields && + else if (get_thd()->count_cuted_fields && check_int(cs, from, len, end, error)) error= 1; else @@ -3893,7 +3910,7 @@ int Field_float::store(const char *from,uint len,CHARSET_INFO *cs) char *end; double nr= my_strntod(cs,(char*) from,len,&end,&error); if (error || (!len || ((uint) (end-from) != len && - table->in_use->count_cuted_fields))) + get_thd()->count_cuted_fields))) { set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, (error ? ER_WARN_DATA_OUT_OF_RANGE : WARN_DATA_TRUNCATED), 1); @@ -4081,7 +4098,7 @@ int Field_double::store(const char *from,uint len,CHARSET_INFO *cs) char *end; double nr= my_strntod(cs,(char*) from, len, &end, &error); if (error || (!len || ((uint) (end-from) != len && - table->in_use->count_cuted_fields))) + get_thd()->count_cuted_fields))) { set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, (error ? ER_WARN_DATA_OUT_OF_RANGE : WARN_DATA_TRUNCATED), 1); @@ -4529,10 +4546,11 @@ int Field_timestamp::store_TIME_with_warning(THD *thd, MYSQL_TIME *l_time, int Field_timestamp::store_time_dec(MYSQL_TIME *ltime, uint dec) { - THD *thd= table->in_use; int unused; MYSQL_TIME l_time= *ltime; ErrConvTime str(ltime); + THD *thd= get_thd(); + bool valid= !check_date(&l_time, pack_time(&l_time) != 0, (thd->variables.sql_mode & MODE_NO_ZERO_DATE) | MODE_NO_ZERO_IN_DATE, &unused); @@ -4547,7 +4565,7 @@ int Field_timestamp::store(const char *from,uint len,CHARSET_INFO *cs) int error; int have_smth_to_conv; ErrConvString str(from, len, cs); - THD *thd= table->in_use; + THD *thd= get_thd(); /* We don't want to store invalid or fuzzy datetime values in TIMESTAMP */ have_smth_to_conv= (str_to_datetime(cs, from, len, &l_time, @@ -4564,7 +4582,7 @@ int Field_timestamp::store(double nr) MYSQL_TIME l_time; int error; ErrConvDouble str(nr); - THD *thd= table->in_use; + THD *thd= get_thd(); longlong tmp= double_to_datetime(nr, &l_time, (thd->variables.sql_mode & MODE_NO_ZERO_DATE) | @@ -4578,7 +4596,7 @@ int Field_timestamp::store(longlong nr, bool unsigned_val) MYSQL_TIME l_time; int error; ErrConvInteger str(nr); - THD *thd= table->in_use; + THD *thd= get_thd(); /* We don't want to store invalid or fuzzy datetime values in TIMESTAMP */ longlong tmp= number_to_datetime(nr, 0, &l_time, (thd->variables.sql_mode & @@ -4670,7 +4688,7 @@ String *Field_timestamp::val_str(String *val_buffer, String *val_ptr) bool Field_timestamp::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) { - THD *thd= table->in_use; + THD *thd= get_thd(); thd->time_zone_used= 1; ulong sec_part; my_time_t temp= get_timestamp(&sec_part); @@ -4723,7 +4741,7 @@ void Field_timestamp::sql_type(String &res) const int Field_timestamp::set_time() { - THD *thd= table->in_use; + THD *thd= get_thd(); set_notnull(); store_TIME(thd->query_start(), 0); return 0; @@ -4872,7 +4890,7 @@ int Field_timestamp_hires::store_decimal(const my_decimal *d) int error; MYSQL_TIME ltime; longlong tmp; - THD *thd= table->in_use; + THD *thd= get_thd(); ErrConvDecimal str(d); if (my_decimal2seconds(d, &nr, &sec_part)) @@ -4890,7 +4908,7 @@ int Field_timestamp_hires::store_decimal(const my_decimal *d) int Field_timestamp_hires::set_time() { - THD *thd= table->in_use; + THD *thd= get_thd(); set_notnull(); store_TIME(thd->query_start(), thd->query_start_sec_part()); return 0; @@ -5009,7 +5027,7 @@ int Field_temporal::store(const char *from,uint len,CHARSET_INFO *cs) MYSQL_TIME ltime; int error; enum enum_mysql_timestamp_type func_res; - THD *thd= table->in_use; + THD *thd= get_thd(); ErrConvString str(from, len, cs); func_res= str_to_datetime(cs, from, len, <ime, @@ -5026,7 +5044,7 @@ int Field_temporal::store(double nr) { int error= 0; MYSQL_TIME ltime; - THD *thd= table->in_use; + THD *thd= get_thd(); ErrConvDouble str(nr); longlong tmp= double_to_datetime(nr, <ime, @@ -5044,7 +5062,7 @@ int Field_temporal::store(longlong nr, bool unsigned_val) int error; MYSQL_TIME ltime; longlong tmp; - THD *thd= table->in_use; + THD *thd= get_thd(); ErrConvInteger str(nr); tmp= number_to_datetime(nr, 0, <ime, (TIME_FUZZY_DATE | @@ -5109,7 +5127,7 @@ int Field_time::store(const char *from,uint len,CHARSET_INFO *cs) int was_cut; int have_smth_to_conv= str_to_time(cs, from, len, <ime, - table->in_use->variables.sql_mode & + get_thd()->variables.sql_mode & (MODE_NO_ZERO_DATE | MODE_NO_ZERO_IN_DATE | MODE_INVALID_DATES), &was_cut) > MYSQL_TIMESTAMP_ERROR; @@ -5215,7 +5233,7 @@ String *Field_time::val_str(String *val_buffer, bool Field_time::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) { - THD *thd= table->in_use; + THD *thd= get_thd(); if (!(fuzzydate & (TIME_FUZZY_DATE|TIME_TIME_ONLY))) { push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, @@ -5405,7 +5423,7 @@ int Field_year::store(const char *from, uint len,CHARSET_INFO *cs) set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); return 1; } - if (table->in_use->count_cuted_fields && + if (get_thd()->count_cuted_fields && (error= check_int(cs, from, len, end, error))) { if (error == 1) /* empty or incorrect string */ @@ -5865,7 +5883,7 @@ int Field_datetime_hires::store_decimal(const my_decimal *d) int error; MYSQL_TIME ltime; longlong tmp; - THD *thd= table->in_use; + THD *thd= get_thd(); ErrConvDecimal str(d); if (my_decimal2seconds(d, &nr, &sec_part)) @@ -6002,7 +6020,9 @@ check_string_copy_error(Field_str *field, { const char *pos; char tmp[32]; - THD *thd= field->table->in_use; + THD *thd; + + thd= field->get_thd(); if (!(pos= well_formed_error_pos) && !(pos= cannot_convert_error_pos)) @@ -6044,11 +6064,12 @@ int Field_longstr::report_if_important_data(const char *pstr, const char *end, bool count_spaces) { - if ((pstr < end) && table->in_use->count_cuted_fields) + THD *thd= get_thd(); + if ((pstr < end) && thd->count_cuted_fields) { if (test_if_important_data(field_charset, pstr, end)) { - if (table->in_use->abort_on_warning) + if (thd->abort_on_warning) set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_DATA_TOO_LONG, 1); else set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); @@ -6075,7 +6096,7 @@ int Field_string::store(const char *from,uint length,CHARSET_INFO *cs) const char *from_end_pos; /* See the comment for Field_long::store(long long) */ - DBUG_ASSERT(table->in_use == current_thd); + DBUG_ASSERT(!table || table->in_use == current_thd); copy_length= well_formed_copy_nchars(field_charset, (char*) ptr, field_length, @@ -6121,7 +6142,7 @@ int Field_str::store(double nr) if (error) { - if (table->in_use->abort_on_warning) + if (get_thd()->abort_on_warning) set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_DATA_TOO_LONG, 1); else set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); @@ -6181,7 +6202,7 @@ double Field_string::val_real(void) double result; result= my_strntod(cs,(char*) ptr,field_length,&end,&error); - if (!table->in_use->no_errors && + if (!get_thd()->no_errors && (error || (field_length != (uint32)(end - (char*) ptr) && !check_if_only_end_space(cs, end, (char*) ptr + field_length)))) @@ -6205,7 +6226,7 @@ longlong Field_string::val_int(void) longlong result; result= my_strntoll(cs, (char*) ptr,field_length,10,&end,&error); - if (!table->in_use->no_errors && + if (!get_thd()->no_errors && (error || (field_length != (uint32)(end - (char*) ptr) && !check_if_only_end_space(cs, end, (char*) ptr + field_length)))) @@ -6225,9 +6246,9 @@ String *Field_string::val_str(String *val_buffer __attribute__((unused)), { ASSERT_COLUMN_MARKED_FOR_READ; /* See the comment for Field_long::store(long long) */ - DBUG_ASSERT(table->in_use == current_thd); + DBUG_ASSERT(!table || table->in_use == current_thd); uint length; - if (table->in_use->variables.sql_mode & + if (get_thd()->variables.sql_mode & MODE_PAD_CHAR_TO_FULL_LENGTH) length= my_charpos(field_charset, ptr, ptr + field_length, field_length / field_charset->mbmaxlen); @@ -6244,7 +6265,7 @@ my_decimal *Field_string::val_decimal(my_decimal *decimal_value) ASSERT_COLUMN_MARKED_FOR_READ; int err= str2my_decimal(E_DEC_FATAL_ERROR, (char*) ptr, field_length, charset(), decimal_value); - if (!table->in_use->no_errors && err) + if (!get_thd()->no_errors && err) { ErrConvString errmsg((char*) ptr, field_length, charset()); push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, @@ -6628,7 +6649,7 @@ double Field_varstring::val_real(void) uint length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr); result= my_strntod(cs, (char*)ptr+length_bytes, length, &end, &error); - if (!table->in_use->no_errors && + if (!get_thd()->no_errors && (error || (length != (uint)(end - (char*)ptr+length_bytes) && !check_if_only_end_space(cs, end, (char*)ptr+length_bytes+length)))) { @@ -6651,7 +6672,7 @@ longlong Field_varstring::val_int(void) longlong result= my_strntoll(cs, (char*) ptr+length_bytes, length, 10, &end, &error); - if (!table->in_use->no_errors && + if (!get_thd()->no_errors && (error || (length != (uint)(end - (char*)ptr+length_bytes) && !check_if_only_end_space(cs, end, (char*)ptr+length_bytes+length)))) { @@ -6680,7 +6701,7 @@ my_decimal *Field_varstring::val_decimal(my_decimal *decimal_value) int error= str2my_decimal(E_DEC_FATAL_ERROR, (char*) ptr+length_bytes, length, cs, decimal_value); - if (!table->in_use->no_errors && error) + if (!get_thd()->no_errors && error) { push_numerical_conversion_warning(current_thd, (char*)ptr+length_bytes, length, cs, "DECIMAL", @@ -7661,7 +7682,7 @@ int Field_enum::store(const char *from,uint length,CHARSET_INFO *cs) tmp=0; set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); } - if (!table->in_use->count_cuted_fields) + if (!get_thd()->count_cuted_fields) err= 0; } else @@ -7685,7 +7706,7 @@ int Field_enum::store(longlong nr, bool unsigned_val) if ((ulonglong) nr > typelib->count || nr == 0) { set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); - if (nr != 0 || table->in_use->count_cuted_fields) + if (nr != 0 || get_thd()->count_cuted_fields) { nr= 0; error= 1; @@ -8215,7 +8236,7 @@ int Field_bit::store(const char *from, uint length, CHARSET_INFO *cs) { set_rec_bits((1 << bit_len) - 1, bit_ptr, bit_ofs, bit_len); memset(ptr, 0xff, bytes_in_rec); - if (table->in_use->really_abort_on_warning()) + if (get_thd()->really_abort_on_warning()) set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_DATA_TOO_LONG, 1); else set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); @@ -8650,7 +8671,7 @@ int Field_bit_as_char::store(const char *from, uint length, CHARSET_INFO *cs) memset(ptr, 0xff, bytes_in_rec); if (bits) *ptr&= ((1 << bits) - 1); /* set first uchar */ - if (table->in_use->really_abort_on_warning()) + if (get_thd()->really_abort_on_warning()) set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_DATA_TOO_LONG, 1); else set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); @@ -9752,7 +9773,7 @@ void Field::set_datetime_warning(MYSQL_ERROR::enum_warning_level level, uint code, const ErrConv *str, timestamp_type ts_type, int cuted_increment) { - THD *thd= table->in_use; + THD *thd= get_thd(); if (thd->really_abort_on_warning() && level >= MYSQL_ERROR::WARN_LEVEL_WARN) make_truncated_value_warning(thd, level, str, ts_type, field_name); else diff --git a/sql/field.h b/sql/field.h index ec09353c3de..9d2fcd9ee58 100644 --- a/sql/field.h +++ b/sql/field.h @@ -36,6 +36,8 @@ class Protocol; class Create_field; class Relay_log_info; class Field; +class Column_statistics; +class Column_statistics_collected; enum enum_check_fields { @@ -173,6 +175,7 @@ public: */ TABLE *table; // Pointer for table TABLE *orig_table; // Pointer to original table + THD *thd; // Used when table == NULL const char * const *table_name; const char *field_name; /** reference to the list of options or NULL */ @@ -220,89 +223,16 @@ public: bool is_stat_field; /* TRUE in Field objects created for column min/max values */ - /* Statistical data on a column */ - class Column_statistics - { - private: - static const uint Scale_factor_nulls_ratio= 100000; - static const uint Scale_factor_avg_length= 100000; - static const uint Scale_factor_avg_frequency= 100000; - public: - /* - Bitmap indicating what statistical characteristics - are available for the column - */ - uint32 column_stat_nulls; - - /* Minimum value for the column */ - Field *min_value; - /* Maximum value for the column */ - Field *max_value; - private: - /* - The ratio Z/N multiplied by the scale factor Scale_factor_nulls_ratio, - where N is the total number of rows, - Z is the number of nulls in the column - */ - ulong nulls_ratio; - /* - Average number of bytes occupied by the representation of a - value of the column in memory buffers such as join buffer - multiplied by the scale factor Scale_factor_avg_length - CHAR values are stripped of trailing spaces - Flexible values are stripped of their length prefixes. - */ - ulong avg_length; - /* - The ratio N/D multiplied by the scale factor Scale_factor_avg_frequency, - where N is the number of rows with null value - in the column, D the number of distinct values among them - */ - ulong avg_frequency; - - public: - double get_nulls_ratio() - { - return (double) nulls_ratio / Scale_factor_nulls_ratio; - } - double get_avg_length() - { - return (double) avg_length / Scale_factor_avg_length; - } - double get_avg_frequency() - { - return (double) avg_frequency / Scale_factor_avg_frequency; - } - - void set_nulls_ratio (double val) - { - nulls_ratio= (ulong) (val * Scale_factor_nulls_ratio); - } - void set_avg_length (double val) - { - avg_length= (ulong) (val * Scale_factor_avg_length); - } - void set_avg_frequency (double val) - { - avg_frequency= (ulong) (val * Scale_factor_avg_frequency); - } - }; - /* This structure is used for statistical data on the column that has been read from the statistical table column_stat */ - Column_statistics read_stat; + Column_statistics *read_stats; /* This structure is used for statistical data on the column that is collected by the function collect_statistics_for_table */ - Column_statistics write_stat; - - /* These members are used only when collecting statistics on the column */ - ha_rows nulls; - ulonglong column_total_length; - Count_distinct_field *count_distinct; + Column_statistics_collected *collected_stats; /* This is additional data provided for any computed(virtual) field. @@ -522,6 +452,8 @@ public: */ inline bool real_maybe_null(void) { return null_ptr != 0; } + inline THD *get_thd() { return table ? table->in_use : thd; } + enum { LAST_NULL_BYTE_UNDEF= 0 }; @@ -560,6 +492,7 @@ public: Field *clone(MEM_ROOT *mem_root, TABLE *new_table); Field *clone(MEM_ROOT *mem_root, TABLE *new_table, my_ptrdiff_t diff, bool stat_flag= FALSE); + Field *clone(THD *thd_arg, MEM_ROOT *mem_root, my_ptrdiff_t diff); inline void move_field(uchar *ptr_arg,uchar *null_ptr_arg,uchar null_bit_arg) { ptr=ptr_arg; null_ptr=null_ptr_arg; null_bit=null_bit_arg; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index a25e496dddc..343b01dbf37 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -484,6 +484,7 @@ ulong refresh_version; /* Increments on each reload */ query_id_t global_query_id; my_atomic_rwlock_t global_query_id_lock; my_atomic_rwlock_t thread_running_lock; +my_atomic_rwlock_t statistics_lock; ulong aborted_threads, aborted_connects; ulong delayed_insert_timeout, delayed_insert_limit, delayed_queue_size; ulong delayed_insert_threads, delayed_insert_writes, delayed_rows_in_use; @@ -1852,6 +1853,7 @@ void clean_up(bool print_message) sys_var_end(); my_atomic_rwlock_destroy(&global_query_id_lock); my_atomic_rwlock_destroy(&thread_running_lock); + my_atomic_rwlock_destroy(&statistics_lock); mysql_mutex_lock(&LOCK_thread_count); DBUG_PRINT("quit", ("got thread count lock")); ready_to_exit=1; @@ -7275,6 +7277,7 @@ static int mysql_init_variables(void) global_query_id= thread_id= 1L; my_atomic_rwlock_init(&global_query_id_lock); my_atomic_rwlock_init(&thread_running_lock); + my_atomic_rwlock_init(&statistics_lock); strmov(server_version, MYSQL_SERVER_VERSION); threads.empty(); thread_cache.empty(); diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc index 8f811ab09dd..32f70125973 100644 --- a/sql/sql_admin.cc +++ b/sql/sql_admin.cc @@ -712,6 +712,8 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, thd->variables.use_stat_tables > 0) { if (!(compl_result_code= + alloc_statistics_for_table(thd, table->table)) && + !(compl_result_code= collect_statistics_for_table(thd, table->table))) compl_result_code= update_statistics_for_table(thd, table->table); } diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 79f1c32ab1d..db6b583f01f 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -3138,6 +3138,16 @@ retry_share: while (table_cache_count > table_cache_size && unused_tables) free_cache_entry(unused_tables); + if (thd->variables.use_stat_tables > 0) + { + if (share->table_category != TABLE_CATEGORY_SYSTEM) + { + if (!share->stats_can_be_read && + !alloc_statistics_for_table_share(thd, share, TRUE)) + share->stats_can_be_read= TRUE; + } + } + mysql_mutex_unlock(&LOCK_open); /* make a new table */ @@ -4632,11 +4642,21 @@ open_and_process_table(THD *thd, LEX *lex, TABLE_LIST *tables, goto end; } - if (thd->variables.use_stat_tables > 0) + if (thd->variables.use_stat_tables > 0 && tables->table) { - if (tables->table && tables->table->s && - tables->table->s->table_category != TABLE_CATEGORY_SYSTEM) - (void) read_statistics_for_table(thd, tables->table); + TABLE_SHARE *table_share= tables->table->s; + if (table_share && table_share->table_category != TABLE_CATEGORY_SYSTEM) + { + if (!table_share->stats_can_be_read && + !alloc_statistics_for_table_share(thd, table_share, FALSE)) + table_share->stats_can_be_read= TRUE; + + if (table_share->stats_can_be_read && !table_share->stats_is_read) + { + (void) read_statistics_for_table(thd, tables->table); + table_share->stats_is_read= TRUE; + } + } } process_view_routines: diff --git a/sql/sql_base.h b/sql/sql_base.h index 6c20022f7ee..a8d4951981e 100644 --- a/sql/sql_base.h +++ b/sql/sql_base.h @@ -314,6 +314,9 @@ int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived); int read_statistics_for_table(THD *thd, TABLE *table); int collect_statistics_for_table(THD *thd, TABLE *table); +int alloc_statistics_for_table_share(THD* thd, TABLE_SHARE *share, + bool is_safe); +int alloc_statistics_for_table(THD *thd, TABLE *table); int update_statistics_for_table(THD *thd, TABLE *table); int delete_statistics_for_table(THD *thd, LEX_STRING *db, LEX_STRING *tab); int delete_statistics_for_column(THD *thd, TABLE *tab, Field *col); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index d132f78e377..edb9f230a2e 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -51,6 +51,7 @@ #include "opt_subselect.h" #include "log_slow.h" #include "sql_derived.h" +#include "sql_statistics.h" #include "debug_sync.h" // DEBUG_SYNC #include <m_ctype.h> @@ -14252,7 +14253,6 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields, table->intersect_keys.init(); table->keys_in_use_for_query.init(); table->no_rows_with_nulls= param->force_not_null_cols; - table->read_stat.cardinality_is_null= TRUE; table->s= share; init_tmp_table_share(thd, share, "", 0, tmpname, tmpname); @@ -14690,8 +14690,8 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields, keyinfo->ext_key_parts= keyinfo->key_parts; keyinfo->key_length=0; keyinfo->rec_per_key=NULL; - keyinfo->read_stat.init_avg_frequency(NULL); - keyinfo->write_stat.init_avg_frequency(NULL); + keyinfo->read_stats= NULL; + keyinfo->collected_stats= NULL; keyinfo->algorithm= HA_KEY_ALG_UNDEF; keyinfo->is_statistics_from_stat_tables= FALSE; keyinfo->name= (char*) "group_key"; @@ -14808,7 +14808,8 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields, keyinfo->algorithm= HA_KEY_ALG_UNDEF; keyinfo->is_statistics_from_stat_tables= FALSE; keyinfo->rec_per_key=0; - keyinfo->read_stat.init_avg_frequency(NULL); + keyinfo->read_stats= NULL; + keyinfo->collected_stats= NULL; /* Create an extra field to hold NULL bits so that unique indexes on diff --git a/sql/sql_statistics.cc b/sql/sql_statistics.cc index 3e3815b6479..3964b08019c 100644 --- a/sql/sql_statistics.cc +++ b/sql/sql_statistics.cc @@ -26,6 +26,7 @@ #include "sql_base.h" #include "key.h" #include "sql_statistics.h" +#include "my_atomic.h" /* The system variable 'use_stat_tables' can take one of the @@ -79,6 +80,7 @@ static const LEX_STRING stat_tables_db_name= { C_STRING_WITH_LEN("mysql") }; otherwise it is set to TL_WRITE. */ +static inline void init_table_list_for_stat_tables(TABLE_LIST *tables, bool for_write) { uint i; @@ -109,6 +111,7 @@ inline void init_table_list_for_stat_tables(TABLE_LIST *tables, bool for_write) otherwise it is set to TL_WRITE. */ +static inline void init_table_list_for_single_stat_table(TABLE_LIST *tbl, const LEX_STRING *stat_tab_name, bool for_write) @@ -125,75 +128,61 @@ inline void init_table_list_for_single_stat_table(TABLE_LIST *tbl, /** @details - The function sets null bits stored in the bitmap table_field->write_stat - for all statistical values collected for a column. -*/ - -inline void set_nulls_for_write_column_stat_values(Field *table_field) -{ - table_field->write_stat.column_stat_nulls= - ((1 << (COLUMN_STAT_AVG_FREQUENCY-COLUMN_STAT_COLUMN_NAME))-1) << - (COLUMN_STAT_COLUMN_NAME+1); -} - - -/** - @details - The function sets null bits stored in the bitmap table_field->read_stat - for all statistical values collected for a column. -*/ + If the value of the parameter is_safe is TRUE then the function + just copies the address pointed by the parameter src into the memory + pointed by the parameter dest. Otherwise the function performs the + following statement as an atomic action: + if (*dest == NULL) { *dest= *src; } + i.e. the same copying is performed only if *dest is NULL. +*/ -inline void set_nulls_for_read_column_stat_values(Field *table_field) +static +inline void store_address_if_first(void **dest, void **src, bool is_safe) { - table_field->read_stat.column_stat_nulls= - ((1 << (COLUMN_STAT_AVG_FREQUENCY-COLUMN_STAT_COLUMN_NAME))-1) << - (COLUMN_STAT_COLUMN_NAME+1); + if (is_safe) + { + if (!*dest) + memcpy(dest, src, sizeof(void *)); + } + else + { + char *null= NULL; + my_atomic_rwlock_wrlock(statistics_lock); + my_atomic_casptr(dest, (void **) &null, *src) + my_atomic_rwlock_wrunlock(statistics_lock); + } } -/** - @details - The function removes the null bit stored in the bitmap - table_field->write_stat for the statistical value collected - on the statistical column number stat_field_no. -*/ - -inline void set_not_null_for_write_column_stat_value(Field *table_field, - uint stat_field_no) -{ - table_field->write_stat.column_stat_nulls&= ~(1 << stat_field_no); -} - - -/** - @details - The function removes the null bit stored in the bitmap - table_field->read_stat for the statistical value collected - on the statistical column number stat_field_no. -*/ +/* + The class Column_statistics_collected is a helper class used to collect + statistics on a table column. The class is derived directly from + the class Column_statistics, and, additionally to the fields of the + latter, it contains the fields to accumulate the results of aggregation + for the number of nulls in the column and for the size of the column + values. There is also a container for distinct column values used + to calculate the average number of records per distinct column value. +*/ -inline void set_not_null_for_read_column_stat_value(Field *table_field, - uint stat_field_no) +class Column_statistics_collected :public Column_statistics { - table_field->read_stat.column_stat_nulls&= ~(1 << stat_field_no); -} +private: + Field *column; /* The column to collect statistics on */ + ha_rows nulls; /* To accumulate the number of nulls in the column */ + ulonglong column_total_length; /* To accumulate the size of column values */ + Count_distinct_field *count_distinct; /* The container for distinct + column values */ -/** - @details - The function checks the null bit stored in the bitmap - table_field->read_stat for the statistical value collected - on the statistical column number stat_field_no. -*/ +public: -inline bool check_null_for_write_column_stat_value(Field *table_field, - uint stat_field_no) -{ - return table_field->write_stat.column_stat_nulls & (1 << stat_field_no); -} + inline void init(THD *thd, Field * table_field); + inline void add(ha_rows rowno); + inline void finish(ha_rows rows); +}; -/* +/** Stat_table is the base class for classes Table_stat, Column_stat and Index_stat. The methods of these classes allow us to read statistical data from statistical tables, write collected statistical data into @@ -215,7 +204,7 @@ inline bool check_null_for_write_column_stat_value(Field *table_field, In some cases the TABLE structure for table t may be undefined. Then the objects of the classes Table_stat, Column_stat and Index stat are created by the alternative constructor that require only the name - of the table t and the name of the database it belongs to. Now the + of the table t and the name of the database it belongs to. Currently the alternative constructors are used only in the cases when some records belonging to the table are to be deleted, or its keys are to be updated @@ -327,9 +316,10 @@ protected: KEY *stat_key_info; /* Structure for the index to access stat_table */ /* Table for which statistical data is read / updated */ - TABLE *table; - LEX_STRING *db_name; /* Name of the database containing 'table' */ - LEX_STRING *table_name; /* Name of the table 'table' */ + TABLE *table; + TABLE_SHARE *table_share; /* Table share for 'table */ + LEX_STRING *db_name; /* Name of the database containing 'table' */ + LEX_STRING *table_name; /* Name of the table 'table' */ void store_record_for_update() { @@ -362,11 +352,13 @@ public: statistics has been collected. */ - Stat_table(TABLE *stat, TABLE *tab) :stat_table(stat), table(tab) + Stat_table(TABLE *stat, TABLE *tab) + :stat_table(stat), table(tab) { + table_share= tab->s; common_init_stat_table(); - db_name= &table->s->db; - table_name= &table->s->table_name; + db_name= &table_share->db; + table_name= &table_share->table_name; } @@ -381,7 +373,7 @@ public: */ Stat_table(TABLE *stat, LEX_STRING *db, LEX_STRING *tab) - :stat_table(stat), table(NULL) + :stat_table(stat), table_share(NULL) { common_init_stat_table(); db_name= db; @@ -532,6 +524,7 @@ public: return FALSE; } + /** @brief Update the table name fields in the current record of stat_table @@ -683,12 +676,12 @@ public: void store_stat_fields() { Field *stat_field= stat_table->field[TABLE_STAT_CARDINALITY]; - if (table->write_stat.cardinality_is_null) + if (table->collected_stats->cardinality_is_null) stat_field->set_null(); else { stat_field->set_notnull(); - stat_field->store(table->write_stat.cardinality); + stat_field->store(table->collected_stats->cardinality); } } @@ -709,15 +702,15 @@ public: void get_stat_values() { - table->read_stat.cardinality_is_null= TRUE; - table->read_stat.cardinality= 0; + table_share->read_stats->cardinality_is_null= TRUE; + table_share->read_stats->cardinality= 0; if (find_stat()) { Field *stat_field= stat_table->field[TABLE_STAT_CARDINALITY]; if (!stat_field->is_null()) { - table->read_stat.cardinality_is_null= FALSE; - table->read_stat.cardinality= stat_field->val_int(); + table_share->read_stats->cardinality_is_null= FALSE; + table_share->read_stats->cardinality= stat_field->val_int(); } } } @@ -890,7 +883,7 @@ public: for (uint i= COLUMN_STAT_MIN_VALUE; i <= COLUMN_STAT_AVG_FREQUENCY; i++) { Field *stat_field= stat_table->field[i]; - if (check_null_for_write_column_stat_value(table_field, i)) + if (table_field->collected_stats->is_null(i)) stat_field->set_null(); else { @@ -898,30 +891,30 @@ public: switch (i) { case COLUMN_STAT_MIN_VALUE: if (table_field->type() == MYSQL_TYPE_BIT) - stat_field->store(table_field->write_stat.min_value->val_int()); + stat_field->store(table_field->collected_stats->min_value->val_int()); else { - table_field->write_stat.min_value->val_str(&val); + table_field->collected_stats->min_value->val_str(&val); stat_field->store(val.ptr(), val.length(), &my_charset_utf8_bin); } break; case COLUMN_STAT_MAX_VALUE: if (table_field->type() == MYSQL_TYPE_BIT) - stat_field->store(table_field->write_stat.max_value->val_int()); + stat_field->store(table_field->collected_stats->max_value->val_int()); else { - table_field->write_stat.max_value->val_str(&val); + table_field->collected_stats->max_value->val_str(&val); stat_field->store(val.ptr(), val.length(), &my_charset_utf8_bin); } break; case COLUMN_STAT_NULLS_RATIO: - stat_field->store(table_field->write_stat.get_nulls_ratio()); + stat_field->store(table_field->collected_stats->get_nulls_ratio()); break; case COLUMN_STAT_AVG_LENGTH: - stat_field->store(table_field->write_stat.get_avg_length()); + stat_field->store(table_field->collected_stats->get_avg_length()); break; case COLUMN_STAT_AVG_FREQUENCY: - stat_field->store(table_field->write_stat.get_avg_frequency()); + stat_field->store(table_field->collected_stats->get_avg_frequency()); break; } } @@ -947,12 +940,12 @@ public: void get_stat_values() { - set_nulls_for_read_column_stat_values(table_field); + table_field->read_stats->set_all_nulls(); - if (table_field->read_stat.min_value) - table_field->read_stat.min_value->set_null(); - if (table_field->read_stat.max_value) - table_field->read_stat.max_value->set_null(); + if (table_field->read_stats->min_value) + table_field->read_stats->min_value->set_null(); + if (table_field->read_stats->max_value) + table_field->read_stats->max_value->set_null(); if (find_stat()) { @@ -965,30 +958,32 @@ public: if (!stat_field->is_null() && (i > COLUMN_STAT_MAX_VALUE || - (i == COLUMN_STAT_MIN_VALUE && table_field->read_stat.min_value) || - (i == COLUMN_STAT_MAX_VALUE && table_field->read_stat.max_value))) + (i == COLUMN_STAT_MIN_VALUE && + table_field->read_stats->min_value) || + (i == COLUMN_STAT_MAX_VALUE && + table_field->read_stats->max_value))) { - set_not_null_for_read_column_stat_value(table_field, i); + table_field->read_stats->set_not_null(i); switch (i) { case COLUMN_STAT_MIN_VALUE: stat_field->val_str(&val); - table_field->read_stat.min_value->store(val.ptr(), val.length(), - &my_charset_utf8_bin); + table_field->read_stats->min_value->store(val.ptr(), val.length(), + &my_charset_utf8_bin); break; case COLUMN_STAT_MAX_VALUE: stat_field->val_str(&val); - table_field->read_stat.max_value->store(val.ptr(), val.length(), - &my_charset_utf8_bin); + table_field->read_stats->max_value->store(val.ptr(), val.length(), + &my_charset_utf8_bin); break; case COLUMN_STAT_NULLS_RATIO: - table_field->read_stat.set_nulls_ratio(stat_field->val_real()); + table_field->read_stats->set_nulls_ratio(stat_field->val_real()); break; case COLUMN_STAT_AVG_LENGTH: - table_field->read_stat.set_avg_length(stat_field->val_real()); + table_field->read_stats->set_avg_length(stat_field->val_real()); break; case COLUMN_STAT_AVG_FREQUENCY: - table_field->read_stat.set_avg_frequency(stat_field->val_real()); + table_field->read_stats->set_avg_frequency(stat_field->val_real()); break; } } @@ -1047,7 +1042,8 @@ public: The TABLE structure for the table index_stat must be passed as a value for the parameter 'stat'. */ - Index_stat(TABLE *stat, TABLE *tab) :Stat_table(stat, tab) + + Index_stat(TABLE *stat, TABLE*tab) :Stat_table(stat, tab) { common_init_index_stat_table(); } @@ -1155,7 +1151,7 @@ public: { Field *stat_field= stat_table->field[INDEX_STAT_AVG_FREQUENCY]; double avg_frequency= - table_key_info->write_stat.get_avg_frequency(prefix_arity-1); + table_key_info->collected_stats->get_avg_frequency(prefix_arity-1); if (avg_frequency == 0) stat_field->set_null(); else @@ -1191,7 +1187,7 @@ public: if (!stat_field->is_null()) avg_frequency= stat_field->val_real(); } - table_key_info->read_stat.set_avg_frequency(prefix_arity-1, avg_frequency); + table_key_info->read_stats->set_avg_frequency(prefix_arity-1, avg_frequency); } }; @@ -1464,7 +1460,7 @@ public: { double val= state->prefix_count == 0 ? 0 : (double) state->entry_count / state->prefix_count; - index_info->write_stat.set_avg_frequency(i, val); + index_info->collected_stats->set_avg_frequency(i, val); } } } @@ -1473,30 +1469,30 @@ public: /** @brief - Create fields for min/max values to collect/read column statistics + Create fields for min/max values to collect column statistics @param table Table the fields are created for - @param - for_write Those fields are created that are used to collect statistics - @note + @details The function first allocates record buffers to store min/max values for 'table's fields. Then for each table field f it creates Field structures that points to these buffers rather that to the record buffer as the Field object for f does. The pointers of the created fields are placed - either in the write_stat or in the read_stat structure of the Field - object for f, depending on the value of the 'for_write' parameter. + in the collected_stats structure of the Field object for f. + The function allocates the buffers for min/max values in the table + memory. @note The buffers allocated when min/max values are used to read statistics from the persistent statistical tables differ from those buffers that - are used when statistics on min/max values for column is collected. + are used when statistics on min/max values for column is collected + as they are allocated in different mem_roots. The same is true for the fields created for min/max values. */ static -void create_min_max_stistical_fields(TABLE *table, bool for_write) +void create_min_max_stistical_fields_for_table(TABLE *table) { Field *table_field; Field **field_ptr; @@ -1506,12 +1502,8 @@ void create_min_max_stistical_fields(TABLE *table, bool for_write) for (field_ptr= table->field; *field_ptr; field_ptr++) { table_field= *field_ptr; - if (for_write) - table_field->write_stat.max_value= - table_field->write_stat.min_value= NULL; - else - table_field->read_stat.max_value= - table_field->read_stat.min_value= NULL; + table_field->collected_stats->max_value= + table_field->collected_stats->min_value= NULL; } if ((record= (uchar *) alloc_root(&table->mem_root, 2*rec_buff_length))) @@ -1523,22 +1515,83 @@ void create_min_max_stistical_fields(TABLE *table, bool for_write) Field *fld; table_field= *field_ptr; my_ptrdiff_t diff= record-table->record[0]; + if (!bitmap_is_set(table->read_set, table_field->field_index)) + continue; if (!(fld= table_field->clone(&table->mem_root, table, diff, TRUE))) continue; if (i == 0) - { - if (for_write) - table_field->write_stat.min_value= fld; - else - table_field->read_stat.min_value= fld; - } + table_field->collected_stats->min_value= fld; else - { - if (for_write) - table_field->write_stat.max_value= fld; - else - table_field->read_stat.max_value= fld; - } + table_field->collected_stats->max_value= fld; + } + } + } +} + + +/** + @brief + Create fields for min/max values to read column statistics + + @param + thd Thread handler + @param + table_share Table share the fields are created for + @param + is_safe TRUE <-> at any time only one thread can perform the function + + @details + The function first allocates record buffers to store min/max values + for 'table_share's fields. Then for each field f it creates Field structures + that points to these buffers rather that to the record buffer as the + Field object for f does. The pointers of the created fields are placed + in the read_stats structure of the Field object for f. + The function allocates the buffers for min/max values in the table share + memory. + If the parameter is_safe is TRUE then it is guaranteed that at any given time + only one thread is executed the code of the function. + + @note + The buffers allocated when min/max values are used to collect statistics + from the persistent statistical tables differ from those buffers that + are used when statistics on min/max values for column is read as they + are allocated in different mem_roots. + The same is true for the fields created for min/max values. +*/ + +static +void create_min_max_stistical_fields_for_table_share(THD *thd, + TABLE_SHARE *table_share, + bool is_safe) +{ + Field *table_field; + Field **field_ptr; + uchar *record; + uint rec_buff_length= table_share->rec_buff_length; + + for (field_ptr= table_share->field; *field_ptr; field_ptr++) + { + table_field= *field_ptr; + table_field->read_stats->max_value= + table_field->read_stats->min_value= NULL; + } + + if ((record= (uchar *) alloc_root(&table_share->mem_root, 2*rec_buff_length))) + { + for (uint i=0; i < 2; i++, record+= rec_buff_length) + { + for (field_ptr= table_share->field; *field_ptr; field_ptr++) + { + Field *fld; + table_field= *field_ptr; + my_ptrdiff_t diff= record - table_share->default_values; + if (!(fld= table_field->clone(thd, &table_share->mem_root, diff))) + continue; + store_address_if_first(i == 0 ? + (void **) &table_field->read_stats->min_value : + (void **) &table_field->read_stats->max_value, + (void **) &fld, + is_safe); } } } @@ -1546,6 +1599,301 @@ void create_min_max_stistical_fields(TABLE *table, bool for_write) /** + @brief + Allocate memory for the table's statistical data to be collected + + @param + table Table for which the memory for statistical data is allocated + + @note + The function allocates the memory for the statistical data on 'table' with + the intention to collect the data there. The memory is allocated for + the statistics on the table, on the table's columns, and on the table's + indexes. The memory is allocated in the table's mem_root. + + @retval + 0 If the memory for all statistical data has been successfully allocated + @retval + 1 Otherwise + + @note + Each thread allocates its own memory to collect statistics on the table + It allows us, for example, to collect statistics on the different indexes + of the same table in parallel. +*/ + +int alloc_statistics_for_table(THD* thd, TABLE *table) +{ + Field **field_ptr; + uint cnt= 0; + + DBUG_ENTER("alloc_statistics_for_table"); + + Table_statistics *table_stats= + (Table_statistics *) alloc_root(&table->mem_root, + sizeof(Table_statistics)); + + for (field_ptr= table->field; *field_ptr; field_ptr++, cnt++) ; + Column_statistics_collected *column_stats= + (Column_statistics_collected *) alloc_root(&table->mem_root, + sizeof(Column_statistics_collected) * cnt); + + uint keys= table->s->keys; + Index_statistics *index_stats= + (Index_statistics *) alloc_root(&table->mem_root, + sizeof(Index_statistics) * keys); + + uint key_parts= table->s->ext_key_parts; + ulong *idx_avg_frequency= (ulong*) alloc_root(&table->mem_root, + sizeof(ulong) * key_parts); + + if (!table_stats || !column_stats || !index_stats || !idx_avg_frequency) + DBUG_RETURN(1); + + table->collected_stats= table_stats; + table_stats->column_stats= column_stats; + table_stats->index_stats= index_stats; + table_stats->idx_avg_frequency= idx_avg_frequency; + + bzero(column_stats, sizeof(Column_statistics) * cnt); + + for (field_ptr= table->field; *field_ptr; field_ptr++, column_stats++) + (*field_ptr)->collected_stats= column_stats; + + bzero(idx_avg_frequency, sizeof(ulong) * key_parts); + + KEY *key_info, *end; + for (key_info= table->key_info, end= key_info + table->s->keys; + key_info < end; + key_info++, index_stats++) + { + key_info->collected_stats= index_stats; + key_info->collected_stats->init_avg_frequency(idx_avg_frequency); + idx_avg_frequency+= key_info->ext_key_parts; + } + + create_min_max_stistical_fields_for_table(table); + + DBUG_RETURN(0); +} + + +/** + @brief + Allocate memory for the statistical data used by a table share + + @param + thd Thread handler + @param + table_share Table share for which the memory for statistical data is allocated + @param + is_safe TRUE <-> at any time only one thread can perform the function + + @note + The function allocates the memory for the statistical data on a table in the + table's share memory with the intention to read the statistics there from + the system persistent statistical tables mysql.table_stat, mysql.column_stat, + mysql.index_stat. The memory is allocated for the statistics on the table, + on the tables's columns, and on the table's indexes. The memory is allocated + in the table_share's mem_root. + If the parameter is_safe is TRUE then it is guaranteed that at any given time + only one thread is executed the code of the function. + + @retval + 0 If the memory for all statistical data has been successfully allocated + @retval + 1 Otherwise + + @note + The situation when more than one thread try to allocate memory for + statistical data is rare. It happens under the following scenario: + 1. One thread executes a query over table t with the system variable + 'use_stat_tables' set to 'never'. + 2. After this the second thread sets 'use_stat_tables' to 'preferably' + and executes a query over table t. + 3. Simultaneously the third thread sets 'use_stat_tables' to 'preferably' + and executes a query over table t. + Here the second and the third threads try to allocate the memory for + statistical data at the same time. The precautions are taken to + guarantee the correctness of the allocation. +*/ + +int alloc_statistics_for_table_share(THD* thd, TABLE_SHARE *table_share, + bool is_safe) +{ + + Field **field_ptr; + uint cnt= 0; + + DBUG_ENTER("alloc_statistics_for_table"); + + DEBUG_SYNC(thd, "statistics_mem_alloc_start1"); + DEBUG_SYNC(thd, "statistics_mem_alloc_start2"); + + Table_statistics *table_stats= + (Table_statistics *) alloc_root(&table_share->mem_root, + sizeof(Table_statistics)); + if (!table_stats) + DBUG_RETURN(1); + bzero(table_stats, sizeof(Table_statistics)); + store_address_if_first((void **) &table_share->read_stats, + (void **) &table_stats, is_safe); + table_stats= table_share->read_stats; + + for (field_ptr= table_share->field; *field_ptr; field_ptr++, cnt++) ; + Column_statistics *column_stats= + (Column_statistics *) alloc_root(&table_share->mem_root, + sizeof(Column_statistics) * cnt); + if (!column_stats) + DBUG_RETURN(1); + bzero(column_stats, sizeof(Column_statistics) * cnt); + store_address_if_first((void **) &table_stats->column_stats, + (void **) &column_stats, is_safe); + column_stats= table_stats->column_stats; + + for (field_ptr= table_share->field; *field_ptr; field_ptr++, column_stats++) + (*field_ptr)->read_stats= column_stats; + + uint keys= table_share->keys; + Index_statistics *index_stats= + (Index_statistics *) alloc_root(&table_share->mem_root, + sizeof(Index_statistics) * keys); + if (!index_stats) + DBUG_RETURN(1); + bzero(index_stats, sizeof(Index_statistics) * keys); + store_address_if_first((void **) &table_stats->index_stats, + (void **) &index_stats, is_safe); + index_stats= table_stats->index_stats; + + uint key_parts= table_share->ext_key_parts; + ulong *idx_avg_frequency= (ulong*) alloc_root(&table_share->mem_root, + sizeof(ulong) * key_parts); + if (!idx_avg_frequency) + DBUG_RETURN(1); + bzero(idx_avg_frequency, sizeof(ulong) * key_parts); + store_address_if_first((void **) &table_stats->idx_avg_frequency, + (void **) &idx_avg_frequency, is_safe); + idx_avg_frequency= table_stats->idx_avg_frequency; + + KEY *key_info, *end; + for (key_info= table_share->key_info, end= key_info + table_share->keys; + key_info < end; + key_info++, index_stats++) + { + key_info->read_stats= index_stats; + key_info->read_stats->init_avg_frequency(idx_avg_frequency); + idx_avg_frequency+= key_info->ext_key_parts; + } + + create_min_max_stistical_fields_for_table_share(thd, table_share, is_safe); + + DBUG_RETURN(0); +} + + +/** + @brief + Initialize the aggregation fields to collect statistics on a column + + @param + thd Thread handler + @param + table_field Column to collect statistics for +*/ + +inline +void Column_statistics_collected::init(THD *thd, Field *table_field) +{ + uint max_heap_table_size= thd->variables.max_heap_table_size; + + column= table_field; + + set_all_nulls(); + + nulls= 0; + column_total_length= 0; + if (table_field->flags & BLOB_FLAG) + count_distinct= NULL; + else + { + count_distinct= + table_field->type() == MYSQL_TYPE_BIT ? + new Count_distinct_field_bit(table_field, max_heap_table_size) : + new Count_distinct_field(table_field, max_heap_table_size); + } + if (count_distinct && !count_distinct->exists()) + count_distinct= NULL; +} + + +/** + @brief + Perform aggregation for a row when collecting statistics on a column + + @param + rowno The order number of the row +*/ + +inline +void Column_statistics_collected::add(ha_rows rowno) +{ + + if (column->is_null()) + nulls++; + else + { + column_total_length+= column->value_length(); + if (min_value && column->update_min(min_value, rowno == nulls)) + set_not_null(COLUMN_STAT_MIN_VALUE); + if (max_value && column->update_max(max_value, rowno == nulls)) + set_not_null(COLUMN_STAT_MAX_VALUE); + if (count_distinct) + count_distinct->add(); + } +} + + +/** + @brief + Get the results of aggregation when collecting the statistics on a column + + @param + rows The total number of rows in the table +*/ + +inline +void Column_statistics_collected::finish(ha_rows rows) +{ + double val; + + if (rows) + { + val= (double) nulls / rows; + set_nulls_ratio(val); + set_not_null(COLUMN_STAT_NULLS_RATIO); + } + if (rows - nulls) + { + val= (double) column_total_length / (rows - nulls); + set_avg_length(val); + set_not_null(COLUMN_STAT_AVG_LENGTH); + } + if (count_distinct) + { + ulonglong distincts= count_distinct->get_value(); + if (distincts) + { + val= (double) (rows - nulls) / distincts; + set_avg_frequency(val); + set_not_null(COLUMN_STAT_AVG_FREQUENCY); + } + delete count_distinct; + count_distinct= NULL; + } +} + + +/** @brief Collect statistical data on an index @@ -1586,6 +1934,9 @@ int collect_statistics_for_index(TABLE *table, uint index) Index_prefix_calc index_prefix_calc(table, key_info); DBUG_ENTER("collect_statistics_for_index"); + DEBUG_SYNC(table->in_use, "statistics_collection_start1"); + DEBUG_SYNC(table->in_use, "statistics_collection_start2"); + table->key_read= 1; table->file->extra(HA_EXTRA_KEYREAD); @@ -1673,32 +2024,15 @@ int collect_statistics_for_table(THD *thd, TABLE *table) DBUG_ENTER("collect_statistics_for_table"); - table->write_stat.cardinality_is_null= TRUE; - table->write_stat.cardinality= 0; + table->collected_stats->cardinality_is_null= TRUE; + table->collected_stats->cardinality= 0; - create_min_max_stistical_fields(table, TRUE); - for (field_ptr= table->field; *field_ptr; field_ptr++) { - table_field= *field_ptr; - uint max_heap_table_size= thd->variables.max_heap_table_size; + table_field= *field_ptr; if (!bitmap_is_set(table->read_set, table_field->field_index)) continue; - set_nulls_for_write_column_stat_values(table_field); - table_field->nulls= 0; - table_field->column_total_length= 0; - if (table_field->flags & BLOB_FLAG) - table_field->count_distinct= NULL; - else - { - table_field->count_distinct= - table_field->type() == MYSQL_TYPE_BIT ? - new Count_distinct_field_bit(table_field, max_heap_table_size) : - new Count_distinct_field(table_field, max_heap_table_size); - } - if (table_field->count_distinct && - !table_field->count_distinct->exists()) - table_field->count_distinct= NULL; + table_field->collected_stats->init(thd, table_field); } /* Perform a full table scan to collect statistics on 'table's columns */ @@ -1713,25 +2047,8 @@ int collect_statistics_for_table(THD *thd, TABLE *table) { table_field= *field_ptr; if (!bitmap_is_set(table->read_set, table_field->field_index)) - continue; - if (table_field->is_null()) - table_field->nulls++; - else - { - table_field->column_total_length+= table_field->value_length(); - if (table_field->write_stat.min_value && - table_field->update_min(table_field->write_stat.min_value, - rows == table_field->nulls)) - set_not_null_for_write_column_stat_value(table_field, - COLUMN_STAT_MIN_VALUE); - if (table_field->write_stat.max_value && - table_field->update_max(table_field->write_stat.max_value, - rows == table_field->nulls)) - set_not_null_for_write_column_stat_value(table_field, - COLUMN_STAT_MAX_VALUE); - if (table_field->count_distinct) - table_field->count_distinct->add(); - } + continue; + table_field->collected_stats->add(rows); } rows++; } @@ -1746,43 +2063,15 @@ int collect_statistics_for_table(THD *thd, TABLE *table) */ if (!rc) { - table->write_stat.cardinality_is_null= FALSE; - table->write_stat.cardinality= rows; + table->collected_stats->cardinality_is_null= FALSE; + table->collected_stats->cardinality= rows; for (field_ptr= table->field; *field_ptr; field_ptr++) { - double val; table_field= *field_ptr; if (!bitmap_is_set(table->read_set, table_field->field_index)) continue; - if (rows) - { - val= (double) table_field->nulls / rows; - table_field->write_stat.set_nulls_ratio(val); - set_not_null_for_write_column_stat_value(table_field, - COLUMN_STAT_NULLS_RATIO); - } - if (rows-table_field->nulls) - { - val= (double) table_field->column_total_length / (rows-table_field->nulls); - table_field->write_stat.set_avg_length(val); - set_not_null_for_write_column_stat_value(table_field, - COLUMN_STAT_AVG_LENGTH); - } - if (table_field->count_distinct) - { - ulonglong count_distinct= table_field->count_distinct->get_value(); - if (count_distinct) - { - val= (double) (rows-table_field->nulls) / count_distinct; - table_field->write_stat.set_avg_frequency(val); - set_not_null_for_write_column_stat_value(table_field, - COLUMN_STAT_AVG_FREQUENCY); - } - delete table_field->count_distinct; - table_field->count_distinct= NULL; - } - + table_field->collected_stats->finish(rows); } } @@ -1790,6 +2079,10 @@ int collect_statistics_for_table(THD *thd, TABLE *table) { uint key; key_map::Iterator it(table->keys_in_use_for_query); + + MY_BITMAP *save_read_set= table->read_set; + table->read_set= &table->tmp_set; + bitmap_set_all(table->read_set); /* Collect statistics for indexes */ while ((key= it++) != key_map::Iterator::BITMAP_END) @@ -1797,6 +2090,8 @@ int collect_statistics_for_table(THD *thd, TABLE *table) if ((rc= collect_statistics_for_index(table, key))) break; } + + table->read_set= save_read_set; } DBUG_RETURN(rc); @@ -1954,6 +2249,7 @@ int read_statistics_for_table(THD *thd, TABLE *table) KEY *key_info, *key_info_end; TABLE_LIST tables[STATISTICS_TABLES]; Open_tables_backup open_tables_backup; + TABLE_SHARE *table_share= table->s; DBUG_ENTER("read_statistics_for_table"); @@ -1966,8 +2262,6 @@ int read_statistics_for_table(THD *thd, TABLE *table) DBUG_RETURN(0); } - create_min_max_stistical_fields(table, FALSE); - /* Read statistics from the statistical table table_stat */ stat_table= tables[TABLE_STAT].table; Table_stat table_stat(stat_table, table); @@ -1977,7 +2271,7 @@ int read_statistics_for_table(THD *thd, TABLE *table) /* Read statistics from the statistical table column_stat */ stat_table= tables[COLUMN_STAT].table; Column_stat column_stat(stat_table, table); - for (field_ptr= table->field; *field_ptr; field_ptr++) + for (field_ptr= table_share->field; *field_ptr; field_ptr++) { table_field= *field_ptr; column_stat.set_key_fields(table_field); @@ -1987,10 +2281,11 @@ int read_statistics_for_table(THD *thd, TABLE *table) /* Read statistics from the statistical table index_stat */ stat_table= tables[INDEX_STAT].table; Index_stat index_stat(stat_table, table); - for (key_info= table->key_info, key_info_end= key_info+table->s->keys; + for (key_info= table_share->key_info, + key_info_end= key_info + table_share->keys; key_info < key_info_end; key_info++) { - uint key_parts= table->actual_n_key_parts(key_info); + uint key_parts= key_info->ext_key_parts; for (i= 0; i < key_parts; i++) { index_stat.set_key_fields(key_info, i+1); @@ -1999,13 +2294,13 @@ int read_statistics_for_table(THD *thd, TABLE *table) key_part_map ext_key_part_map= key_info->ext_key_part_map; if (key_info->key_parts != key_info->ext_key_parts && - key_info->read_stat.get_avg_frequency(key_info->key_parts) == 0) + key_info->read_stats->get_avg_frequency(key_info->key_parts) == 0) { - KEY *pk_key_info= table->key_info + table->s->primary_key; + KEY *pk_key_info= table_share->key_info + table_share->primary_key; uint k= key_info->key_parts; uint pk_parts= pk_key_info->key_parts; - ha_rows n_rows= table->read_stat.cardinality; - double k_dist= n_rows / key_info->read_stat.get_avg_frequency(k-1); + ha_rows n_rows= table_share->read_stats->cardinality; + double k_dist= n_rows / key_info->read_stats->get_avg_frequency(k-1); uint m= 0; for (uint j= 0; j < pk_parts; j++) { @@ -2013,32 +2308,33 @@ int read_statistics_for_table(THD *thd, TABLE *table) { for (uint l= k; l < k + m; l++) { - double avg_frequency= pk_key_info->read_stat.get_avg_frequency(j-1); + double avg_frequency= + pk_key_info->read_stats->get_avg_frequency(j-1); set_if_smaller(avg_frequency, 1); - double val= pk_key_info->read_stat.get_avg_frequency(j) / + double val= pk_key_info->read_stats->get_avg_frequency(j) / avg_frequency; - key_info->read_stat.set_avg_frequency (l, val); + key_info->read_stats->set_avg_frequency (l, val); } } else { - double avg_frequency= pk_key_info->read_stat.get_avg_frequency(j); - key_info->read_stat.set_avg_frequency(k + m, avg_frequency); + double avg_frequency= pk_key_info->read_stats->get_avg_frequency(j); + key_info->read_stats->set_avg_frequency(k + m, avg_frequency); m++; } } for (uint l= k; l < k + m; l++) { - double avg_frequency= key_info->read_stat.get_avg_frequency(l); + double avg_frequency= key_info->read_stats->get_avg_frequency(l); if (avg_frequency == 0 || - table->read_stat.cardinality_is_null) + table_share->read_stats->cardinality_is_null) avg_frequency= 1; else if (avg_frequency > 1) { avg_frequency/= k_dist; set_if_bigger(avg_frequency, 1); } - key_info->read_stat.set_avg_frequency(l, avg_frequency); + key_info->read_stats->set_avg_frequency(l, avg_frequency); } } } @@ -2431,15 +2727,16 @@ void set_statistics_for_table(THD *thd, TABLE *table) { uint use_stat_table_mode= thd->variables.use_stat_tables; table->used_stat_records= - (use_stat_table_mode <= 1 || table->read_stat.cardinality_is_null) ? - table->file->stats.records : table->read_stat.cardinality; + (use_stat_table_mode <= 1 || !table->s->read_stats || + table->s->read_stats->cardinality_is_null) ? + table->file->stats.records : table->s->read_stats->cardinality; 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++) { key_info->is_statistics_from_stat_tables= - (use_stat_table_mode > 1 && - key_info->read_stat.avg_frequency_is_set() && - key_info->read_stat.get_avg_frequency(0) > 0.5); + (use_stat_table_mode > 1 && key_info->read_stats && + key_info->read_stats->avg_frequency_is_inited() && + key_info->read_stats->get_avg_frequency(0) > 0.5); } } diff --git a/sql/sql_statistics.h b/sql/sql_statistics.h index 1a8cbf6c3f3..14a16170c3b 100644 --- a/sql/sql_statistics.h +++ b/sql/sql_statistics.h @@ -60,4 +60,159 @@ enum enum_index_stat_col INDEX_STAT_AVG_FREQUENCY }; + +class Columns_statistics; +class Index_statistics; + + +/* Statistical data on a table */ + +class Table_statistics +{ + +public: + my_bool cardinality_is_null; /* TRUE if the cardinality is unknown */ + ha_rows cardinality; /* Number of rows in the table */ + Column_statistics *column_stats; /* Array of statistical data for columns */ + Index_statistics *index_stats; /* Array of statistical data for indexes */ + ulong *idx_avg_frequency; /* Array of records per key for index prefixes */ + +}; + + +/* Statistical data on a column */ + +class Column_statistics +{ + +private: + static const uint Scale_factor_nulls_ratio= 100000; + static const uint Scale_factor_avg_length= 100000; + static const uint Scale_factor_avg_frequency= 100000; + +public: + /* + Bitmap indicating what statistical characteristics + are available for the column + */ + uint32 column_stat_nulls; + + /* Minimum value for the column */ + Field *min_value; + /* Maximum value for the column */ + Field *max_value; + +private: + + /* + The ratio Z/N multiplied by the scale factor Scale_factor_nulls_ratio, + where + N is the total number of rows, + Z is the number of nulls in the column + */ + ulong nulls_ratio; + + /* + Average number of bytes occupied by the representation of a + value of the column in memory buffers such as join buffer + multiplied by the scale factor Scale_factor_avg_length. + CHAR values are stripped of trailing spaces. + Flexible values are stripped of their length prefixes. + */ + ulong avg_length; + + /* + The ratio N/D multiplied by the scale factor Scale_factor_avg_frequency, + where + N is the number of rows with not null value in the column, + D the number of distinct values among them + */ + ulong avg_frequency; + +public: + + void set_all_nulls() + { + column_stat_nulls= + ((1 << (COLUMN_STAT_AVG_FREQUENCY-COLUMN_STAT_COLUMN_NAME))-1) << + (COLUMN_STAT_COLUMN_NAME+1); + } + + void set_not_null(uint stat_field_no) + { + column_stat_nulls&= ~(1 << stat_field_no); + } + + bool is_null(uint stat_field_no) + { + return test(column_stat_nulls & (1 << stat_field_no)); + } + + double get_nulls_ratio() + { + return (double) nulls_ratio / Scale_factor_nulls_ratio; + } + + double get_avg_length() + { + return (double) avg_length / Scale_factor_avg_length; + } + + double get_avg_frequency() + { + return (double) avg_frequency / Scale_factor_avg_frequency; + } + + void set_nulls_ratio (double val) + { + nulls_ratio= (ulong) (val * Scale_factor_nulls_ratio); + } + + void set_avg_length (double val) + { + avg_length= (ulong) (val * Scale_factor_avg_length); + } + + void set_avg_frequency (double val) + { + avg_frequency= (ulong) (val * Scale_factor_avg_frequency); + } + +}; + + +/* Statistical data on an index prefixes */ + +class Index_statistics +{ + +private: + static const uint Scale_factor_avg_frequency= 100000; + /* + The k-th element of this array contains the ratio N/D + multiplied by the scale factor Scale_factor_avg_frequency, + where N is the number of index entries without nulls + in the first k components, and D is the number of distinct + k-component prefixes among them + */ + ulong *avg_frequency; + +public: + + void init_avg_frequency(ulong *ptr) { avg_frequency= ptr; } + + bool avg_frequency_is_inited() { return avg_frequency != NULL; } + + double get_avg_frequency(uint i) + { + return (double) avg_frequency[i] / Scale_factor_avg_frequency; + } + + void set_avg_frequency(uint i, double val) + { + avg_frequency[i]= (ulong) (val * Scale_factor_avg_frequency); + } + +}; + #endif /* SQL_STATISTICS_H */ diff --git a/sql/structs.h b/sql/structs.h index 4a70820586d..13bb0574b24 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -29,6 +29,7 @@ struct TABLE; class Field; +class Index_statistics; class THD; @@ -121,43 +122,16 @@ typedef struct st_key { */ ulong *rec_per_key; - /* Statistical data on an index prefixes */ - class Index_statistics - { - private: - static const uint Scale_factor_avg_frequency= 100000; - /* - The k-th element of this array contains the ratio N/D - multiplied by the scale factor Scale_factor_avg_frequency, - where N is the number of index entries without nulls - in the first k components, and D is the number of distinct - k-component prefixes among them - */ - ulong *avg_frequency; - - public: - void init_avg_frequency(ulong *ptr) { avg_frequency= ptr; } - bool avg_frequency_is_set() { return avg_frequency != NULL; } - double get_avg_frequency(uint i) - { - return (double) avg_frequency[i] / Scale_factor_avg_frequency; - } - void set_avg_frequency(uint i, double val) - { - avg_frequency[i]= (ulong) (val * Scale_factor_avg_frequency); - } - }; - /* This structure is used for statistical data on the index that has been read from the statistical table index_stat */ - Index_statistics read_stat; + Index_statistics *read_stats; /* This structure is used for statistical data on the index that is collected by the function collect_statistics_for_table */ - Index_statistics write_stat; + Index_statistics *collected_stats; union { int bdb_return_if_eq; @@ -168,13 +142,8 @@ typedef struct st_key { engine_option_value *option_list; ha_index_option_struct *option_struct; /* structure with parsed options */ - inline double real_rec_per_key(uint i) - { - if (rec_per_key == 0) - return 0; - return (is_statistics_from_stat_tables ? - read_stat.get_avg_frequency(i) : (double) rec_per_key[i]); - } + double real_rec_per_key(uint i); + } KEY; diff --git a/sql/table.cc b/sql/table.cc index 43bb521670e..577b074a171 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -39,6 +39,7 @@ #include "my_bit.h" #include "sql_select.h" #include "sql_derived.h" +#include "sql_statistics.h" #include "mdl.h" // MDL_wait_for_graph_visitor /* INFORMATION_SCHEMA name */ @@ -762,8 +763,6 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, ulong pos, record_offset; ulong *rec_per_key= NULL; ulong rec_buff_length; - ulong *read_avg_frequency= NULL; - ulong *write_avg_frequency= NULL; handler *handler_file= 0; KEY *keyinfo; KEY_PART_INFO *key_part= NULL; @@ -946,14 +945,6 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, if (!(rec_per_key= (ulong*) alloc_root(&share->mem_root, sizeof(ulong) * ext_key_parts))) goto err; - if (!(read_avg_frequency= (ulong*) alloc_root(&share->mem_root, - sizeof(double) * - ext_key_parts))) - goto err; - if (!(write_avg_frequency= (ulong*) alloc_root(&share->mem_root, - sizeof(double) * - ext_key_parts))) - goto err; first_key_part= key_part; first_key_parts= first_keyinfo.key_parts; keyinfo->flags= first_keyinfo.flags; @@ -966,13 +957,9 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, keyinfo->key_part= key_part; keyinfo->rec_per_key= rec_per_key; - keyinfo->read_stat.init_avg_frequency(read_avg_frequency); - keyinfo->write_stat.init_avg_frequency(write_avg_frequency); for (j=keyinfo->key_parts ; j-- ; key_part++) { *rec_per_key++=0; - *read_avg_frequency++= 0; - *write_avg_frequency++= 0; key_part->fieldnr= (uint16) (uint2korr(strpos) & FIELD_NR_MASK); key_part->offset= (uint) uint2korr(strpos+2)-1; key_part->key_type= (uint) uint2korr(strpos+5); @@ -1019,8 +1006,6 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, { *key_part++= first_key_part[j]; *rec_per_key++= 0; - *read_avg_frequency++= 0; - *write_avg_frequency++= 0; keyinfo->ext_key_parts++; keyinfo->ext_key_part_map|= 1 << j; } @@ -2416,8 +2401,6 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias, } #endif - outparam->read_stat.cardinality_is_null= TRUE; - if (!(field_ptr = (Field **) alloc_root(&outparam->mem_root, (uint) ((share->fields+1)* sizeof(Field*))))) @@ -5965,7 +5948,8 @@ bool TABLE::add_tmp_key(uint key, uint key_parts, if (!keyinfo->rec_per_key) return TRUE; bzero(keyinfo->rec_per_key, sizeof(ulong)*key_parts); - keyinfo->read_stat.init_avg_frequency(NULL); + keyinfo->read_stats= NULL; + keyinfo->collected_stats= NULL; for (i= 0; i < key_parts; i++) { @@ -6753,6 +6737,14 @@ uint TABLE_SHARE::actual_n_key_parts(THD *thd) } +double KEY::real_rec_per_key(uint i) +{ + if (rec_per_key == 0) + return 0; + return (is_statistics_from_stat_tables ? + read_stats->get_avg_frequency(i) : (double) rec_per_key[i]); +} + /***************************************************************************** ** Instansiate templates *****************************************************************************/ diff --git a/sql/table.h b/sql/table.h index b9a256b132b..e52a1858916 100644 --- a/sql/table.h +++ b/sql/table.h @@ -45,6 +45,7 @@ struct TABLE_LIST; class ACL_internal_schema_access; class ACL_internal_table_access; class Field; +class Table_statistics; /* Used to identify NESTED_JOIN structures within a join (applicable only to @@ -577,6 +578,15 @@ struct TABLE_SHARE KEY *key_info; /* data of keys in database */ uint *blob_field; /* Index to blobs in Field arrray*/ + bool stats_can_be_read; /* Memory for statistical data is allocated */ + bool stats_is_read; /* Statistical data for table has been read + from statistical tables */ + /* + This structure is used for statistical data on the table + that has been read from the statistical table table_stat + */ + Table_statistics *read_stats; + uchar *default_values; /* row with default values */ LEX_STRING comment; /* Comment about table */ CHARSET_INFO *table_charset; /* Default charset of string fields */ @@ -1007,24 +1017,11 @@ public: */ query_id_t query_id; - /* Statistical data on a table */ - class Table_statistics - { - public: - my_bool cardinality_is_null; /* TRUE if the cardinality is unknown */ - ha_rows cardinality; /* Number of rows in the table */ - }; - - /* - This structure is used for statistical data on the table - that has been read from the statistical table table_stat - */ - Table_statistics read_stat; /* This structure is used for statistical data on the table that is collected by the function collect_statistics_for_table */ - Table_statistics write_stat; + Table_statistics *collected_stats; /* The estimate of the number of records in the table used by optimizer */ ha_rows used_stat_records; |