From 9ce6a2ac2cb15ca91deb9012f9a1dda5cd8fe713 Mon Sep 17 00:00:00 2001 From: Michael Okoko Date: Wed, 9 Jun 2021 20:39:50 +0100 Subject: [PATCH 01/12] Prepare JSON as valid histogram_type Signed-off-by: Michael Okoko --- scripts/mysql_system_tables.sql | 2 +- sql/item_strfunc.cc | 2 +- sql/sql_statistics.cc | 10 +++++++--- sql/sql_statistics.h | 12 +++++++++--- 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/scripts/mysql_system_tables.sql b/scripts/mysql_system_tables.sql index cee47a0e5d656..353f665844744 100644 --- a/scripts/mysql_system_tables.sql +++ b/scripts/mysql_system_tables.sql @@ -314,7 +314,7 @@ DROP TABLE tmp_proxies_priv; CREATE TABLE IF NOT EXISTS table_stats (db_name varchar(64) NOT NULL, table_name varchar(64) NOT NULL, cardinality bigint(21) unsigned DEFAULT NULL, PRIMARY KEY (db_name,table_name) ) engine=Aria transactional=0 CHARACTER SET utf8 COLLATE utf8_bin comment='Statistics on Tables'; -CREATE TABLE IF NOT EXISTS column_stats (db_name varchar(64) NOT NULL, table_name varchar(64) NOT NULL, column_name varchar(64) NOT NULL, min_value varbinary(255) DEFAULT NULL, max_value varbinary(255) DEFAULT NULL, nulls_ratio decimal(12,4) DEFAULT NULL, avg_length decimal(12,4) DEFAULT NULL, avg_frequency decimal(12,4) DEFAULT NULL, hist_size tinyint unsigned, hist_type enum('SINGLE_PREC_HB','DOUBLE_PREC_HB'), histogram varbinary(255), PRIMARY KEY (db_name,table_name,column_name) ) engine=Aria transactional=0 CHARACTER SET utf8 COLLATE utf8_bin comment='Statistics on Columns'; +CREATE TABLE IF NOT EXISTS column_stats (db_name varchar(64) NOT NULL, table_name varchar(64) NOT NULL, column_name varchar(64) NOT NULL, min_value varbinary(255) DEFAULT NULL, max_value varbinary(255) DEFAULT NULL, nulls_ratio decimal(12,4) DEFAULT NULL, avg_length decimal(12,4) DEFAULT NULL, avg_frequency decimal(12,4) DEFAULT NULL, hist_size tinyint unsigned, hist_type enum('SINGLE_PREC_HB','DOUBLE_PREC_HB','JSON'), histogram varbinary(255), PRIMARY KEY (db_name,table_name,column_name) ) engine=Aria transactional=0 CHARACTER SET utf8 COLLATE utf8_bin comment='Statistics on Columns'; CREATE TABLE IF NOT EXISTS index_stats (db_name varchar(64) NOT NULL, table_name varchar(64) NOT NULL, index_name varchar(64) NOT NULL, prefix_arity int(11) unsigned NOT NULL, avg_frequency decimal(12,4) DEFAULT NULL, PRIMARY KEY (db_name,table_name,index_name,prefix_arity) ) engine=Aria transactional=0 CHARACTER SET utf8 COLLATE utf8_bin comment='Statistics on Indexes'; diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 7e8ff667e75a6..072d775dcaa7d 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -496,7 +496,7 @@ String *Item_func_from_base64::val_str(String *str) const char *histogram_types[] = - {"SINGLE_PREC_HB", "DOUBLE_PREC_HB", 0}; + {"SINGLE_PREC_HB", "DOUBLE_PREC_HB", "JSON", 0}; static TYPELIB hystorgam_types_typelib= { array_elements(histogram_types), "histogram_types", diff --git a/sql/sql_statistics.cc b/sql/sql_statistics.cc index 1f034f490c8e2..5e78738c18419 100644 --- a/sql/sql_statistics.cc +++ b/sql/sql_statistics.cc @@ -178,7 +178,7 @@ TABLE_FIELD_TYPE column_stat_fields[COLUMN_STAT_N_FIELDS] = }, { { STRING_WITH_LEN("hist_type") }, - { STRING_WITH_LEN("enum('SINGLE_PREC_HB','DOUBLE_PREC_HB')") }, + { STRING_WITH_LEN("enum('SINGLE_PREC_HB','DOUBLE_PREC_HB','JSON')") }, { STRING_WITH_LEN("utf8mb3") } }, { @@ -1070,8 +1070,12 @@ class Column_stat: public Stat_table stat_field->store(stats->histogram.get_type() + 1); break; case COLUMN_STAT_HISTOGRAM: - stat_field->store((char *)stats->histogram.get_values(), - stats->histogram.get_size(), &my_charset_bin); + if (stats->histogram.get_type() == JSON) { + stat_field->store((char *) "hello_world", 11, &my_charset_bin); + } else { + stat_field->store((char *) stats->histogram.get_values(), + stats->histogram.get_size(), &my_charset_bin); + } break; } } diff --git a/sql/sql_statistics.h b/sql/sql_statistics.h index 35b3aa33accf5..555d03cfa2232 100644 --- a/sql/sql_statistics.h +++ b/sql/sql_statistics.h @@ -42,7 +42,8 @@ typedef enum enum_histogram_type { SINGLE_PREC_HB, - DOUBLE_PREC_HB + DOUBLE_PREC_HB, + JSON } Histogram_type; enum enum_stat_tables @@ -154,6 +155,7 @@ class Histogram case SINGLE_PREC_HB: return ((uint) (1 << 8) - 1); case DOUBLE_PREC_HB: + case JSON: return ((uint) (1 << 16) - 1); } return 1; @@ -166,6 +168,7 @@ class Histogram case SINGLE_PREC_HB: return size; case DOUBLE_PREC_HB: + case JSON: return size / 2; } return 0; @@ -179,6 +182,7 @@ class Histogram case SINGLE_PREC_HB: return (uint) (((uint8 *) values)[i]); case DOUBLE_PREC_HB: + case JSON: return (uint) uint2korr(values + i * 2); } return 0; @@ -253,7 +257,8 @@ class Histogram void set_value(uint i, double val) { switch (type) { - case SINGLE_PREC_HB: + case SINGLE_PREC_HB: + case JSON: ((uint8 *) values)[i]= (uint8) (val * prec_factor()); return; case DOUBLE_PREC_HB: @@ -265,7 +270,8 @@ class Histogram void set_prev_value(uint i) { switch (type) { - case SINGLE_PREC_HB: + case SINGLE_PREC_HB: + case JSON: ((uint8 *) values)[i]= ((uint8 *) values)[i-1]; return; case DOUBLE_PREC_HB: From 69a66da91b8df0849e57a11a50f4e791bc9ddf3a Mon Sep 17 00:00:00 2001 From: Michael Okoko Date: Mon, 14 Jun 2021 17:14:11 +0100 Subject: [PATCH 02/12] add json statistics test and change histogram column type to blob --- mysql-test/main/statistics_json.test | 28 ++++++++++++++++++++++++++++ scripts/mysql_system_tables.sql | 2 +- sql/sql_statistics.cc | 5 +++-- 3 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 mysql-test/main/statistics_json.test diff --git a/mysql-test/main/statistics_json.test b/mysql-test/main/statistics_json.test new file mode 100644 index 0000000000000..0af7d1f6d1d00 --- /dev/null +++ b/mysql-test/main/statistics_json.test @@ -0,0 +1,28 @@ +--source include/have_stat_tables.inc +--echo # +--echo # Test that JSON is a valid histogram type and we can store JSON strings in mysql.column_stats +--echo # +--disable_warnings +drop table if exists t1; +--enable_warnings + +set @save_histogram_type=@@histogram_type; + +CREATE TABLE t1 ( + a int NOT NULL PRIMARY KEY, + b varchar(32) +) ENGINE=MYISAM; + +SET histogram_type='JSON'; +SELECT @@histogram_type; + +INSERT INTO t1 VALUES + (7, 'xxxxxxxxxxxxxxxxxxxxxxxxxx'), + (17, 'vvvvvvvvvvvvv'); + +ANALYZE TABLE t1 PERSISTENT FOR COLUMNS(b) INDEXES(); +DESCRIBE mysql.column_stats; +SELECT * FROM mysql.column_stats; + +set histogram_type=@save_histogram_type; +DROP TABLE t1; \ No newline at end of file diff --git a/scripts/mysql_system_tables.sql b/scripts/mysql_system_tables.sql index 353f665844744..e31f3372b5f7e 100644 --- a/scripts/mysql_system_tables.sql +++ b/scripts/mysql_system_tables.sql @@ -314,7 +314,7 @@ DROP TABLE tmp_proxies_priv; CREATE TABLE IF NOT EXISTS table_stats (db_name varchar(64) NOT NULL, table_name varchar(64) NOT NULL, cardinality bigint(21) unsigned DEFAULT NULL, PRIMARY KEY (db_name,table_name) ) engine=Aria transactional=0 CHARACTER SET utf8 COLLATE utf8_bin comment='Statistics on Tables'; -CREATE TABLE IF NOT EXISTS column_stats (db_name varchar(64) NOT NULL, table_name varchar(64) NOT NULL, column_name varchar(64) NOT NULL, min_value varbinary(255) DEFAULT NULL, max_value varbinary(255) DEFAULT NULL, nulls_ratio decimal(12,4) DEFAULT NULL, avg_length decimal(12,4) DEFAULT NULL, avg_frequency decimal(12,4) DEFAULT NULL, hist_size tinyint unsigned, hist_type enum('SINGLE_PREC_HB','DOUBLE_PREC_HB','JSON'), histogram varbinary(255), PRIMARY KEY (db_name,table_name,column_name) ) engine=Aria transactional=0 CHARACTER SET utf8 COLLATE utf8_bin comment='Statistics on Columns'; +CREATE TABLE IF NOT EXISTS column_stats (db_name varchar(64) NOT NULL, table_name varchar(64) NOT NULL, column_name varchar(64) NOT NULL, min_value varbinary(255) DEFAULT NULL, max_value varbinary(255) DEFAULT NULL, nulls_ratio decimal(12,4) DEFAULT NULL, avg_length decimal(12,4) DEFAULT NULL, avg_frequency decimal(12,4) DEFAULT NULL, hist_size tinyint unsigned, hist_type enum('SINGLE_PREC_HB','DOUBLE_PREC_HB','JSON'), histogram blob, PRIMARY KEY (db_name,table_name,column_name) ) engine=Aria transactional=0 CHARACTER SET utf8 COLLATE utf8_bin comment='Statistics on Columns'; CREATE TABLE IF NOT EXISTS index_stats (db_name varchar(64) NOT NULL, table_name varchar(64) NOT NULL, index_name varchar(64) NOT NULL, prefix_arity int(11) unsigned NOT NULL, avg_frequency decimal(12,4) DEFAULT NULL, PRIMARY KEY (db_name,table_name,index_name,prefix_arity) ) engine=Aria transactional=0 CHARACTER SET utf8 COLLATE utf8_bin comment='Statistics on Indexes'; diff --git a/sql/sql_statistics.cc b/sql/sql_statistics.cc index 5e78738c18419..16e3e8b166474 100644 --- a/sql/sql_statistics.cc +++ b/sql/sql_statistics.cc @@ -183,7 +183,7 @@ TABLE_FIELD_TYPE column_stat_fields[COLUMN_STAT_N_FIELDS] = }, { { STRING_WITH_LEN("histogram") }, - { STRING_WITH_LEN("varbinary(255)") }, + { STRING_WITH_LEN("blob") }, { NULL, 0 } } }; @@ -1071,7 +1071,8 @@ class Column_stat: public Stat_table break; case COLUMN_STAT_HISTOGRAM: if (stats->histogram.get_type() == JSON) { - stat_field->store((char *) "hello_world", 11, &my_charset_bin); + const char* val = "{'hello': 'world'}"; + stat_field->store(val, strlen(val), &my_charset_bin); } else { stat_field->store((char *) stats->histogram.get_values(), stats->histogram.get_size(), &my_charset_bin); From 323c0d2694db98c001f19f9fe4116f0674cca95e Mon Sep 17 00:00:00 2001 From: Michael Okoko Date: Wed, 16 Jun 2021 09:54:34 +0100 Subject: [PATCH 03/12] record statistics_json test Signed-off-by: Michael Okoko --- mysql-test/main/statistics_json.result | 38 ++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 mysql-test/main/statistics_json.result diff --git a/mysql-test/main/statistics_json.result b/mysql-test/main/statistics_json.result new file mode 100644 index 0000000000000..3e7d3a307fa02 --- /dev/null +++ b/mysql-test/main/statistics_json.result @@ -0,0 +1,38 @@ +# +# Test that JSON is a valid histogram type and we can store JSON strings in mysql.column_stats +# +drop table if exists t1; +set @save_histogram_type=@@histogram_type; +CREATE TABLE t1 ( +a int NOT NULL PRIMARY KEY, +b varchar(32) +) ENGINE=MYISAM; +SET histogram_type='JSON'; +SELECT @@histogram_type; +@@histogram_type +JSON +INSERT INTO t1 VALUES +(7, 'xxxxxxxxxxxxxxxxxxxxxxxxxx'), +(17, 'vvvvvvvvvvvvv'); +ANALYZE TABLE t1 PERSISTENT FOR COLUMNS(b) INDEXES(); +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze status OK +DESCRIBE mysql.column_stats; +Field Type Null Key Default Extra +db_name varchar(64) NO PRI NULL +table_name varchar(64) NO PRI NULL +column_name varchar(64) NO PRI NULL +min_value varbinary(255) YES NULL +max_value varbinary(255) YES NULL +nulls_ratio decimal(12,4) YES NULL +avg_length decimal(12,4) YES NULL +avg_frequency decimal(12,4) YES NULL +hist_size tinyint(3) unsigned YES NULL +hist_type enum('SINGLE_PREC_HB','DOUBLE_PREC_HB','JSON') YES NULL +histogram blob YES NULL +SELECT * FROM mysql.column_stats; +db_name table_name column_name min_value max_value nulls_ratio avg_length avg_frequency hist_size hist_type histogram +test t1 b vvvvvvvvvvvvv xxxxxxxxxxxxxxxxxxxxxxxxxx 0.0000 19.5000 1.0000 254 JSON {'hello': 'world'} +set histogram_type=@save_histogram_type; +DROP TABLE t1; From ff8a56cd16a70f504c10a25a04d7c92556a58904 Mon Sep 17 00:00:00 2001 From: Michael Okoko Date: Tue, 22 Jun 2021 18:41:37 +0100 Subject: [PATCH 04/12] Update test results to match updated system tables Signed-off-by: Michael Okoko --- mysql-test/main/system_mysql_db.result | 4 ++-- mysql-test/main/system_mysql_db_fix40123.result | 4 ++-- mysql-test/main/system_mysql_db_fix50030.result | 4 ++-- mysql-test/main/system_mysql_db_fix50117.result | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/mysql-test/main/system_mysql_db.result b/mysql-test/main/system_mysql_db.result index d0d1e7c11757b..b756dfcf45e43 100644 --- a/mysql-test/main/system_mysql_db.result +++ b/mysql-test/main/system_mysql_db.result @@ -234,8 +234,8 @@ column_stats CREATE TABLE `column_stats` ( `avg_length` decimal(12,4) DEFAULT NULL, `avg_frequency` decimal(12,4) DEFAULT NULL, `hist_size` tinyint(3) unsigned DEFAULT NULL, - `hist_type` enum('SINGLE_PREC_HB','DOUBLE_PREC_HB') COLLATE utf8mb3_bin DEFAULT NULL, - `histogram` varbinary(255) DEFAULT NULL, + `hist_type` enum('SINGLE_PREC_HB','DOUBLE_PREC_HB','JSON') COLLATE utf8mb3_bin DEFAULT NULL, + `histogram` blob DEFAULT NULL, PRIMARY KEY (`db_name`,`table_name`,`column_name`) ) ENGINE=Aria DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_bin PAGE_CHECKSUM=1 TRANSACTIONAL=0 COMMENT='Statistics on Columns' show create table index_stats; diff --git a/mysql-test/main/system_mysql_db_fix40123.result b/mysql-test/main/system_mysql_db_fix40123.result index 127e2c49642f5..ec972058d54e0 100644 --- a/mysql-test/main/system_mysql_db_fix40123.result +++ b/mysql-test/main/system_mysql_db_fix40123.result @@ -272,8 +272,8 @@ column_stats CREATE TABLE `column_stats` ( `avg_length` decimal(12,4) DEFAULT NULL, `avg_frequency` decimal(12,4) DEFAULT NULL, `hist_size` tinyint(3) unsigned DEFAULT NULL, - `hist_type` enum('SINGLE_PREC_HB','DOUBLE_PREC_HB') COLLATE utf8mb3_bin DEFAULT NULL, - `histogram` varbinary(255) DEFAULT NULL, + `hist_type` enum('SINGLE_PREC_HB','DOUBLE_PREC_HB','JSON') COLLATE utf8mb3_bin DEFAULT NULL, + `histogram` blob DEFAULT NULL, PRIMARY KEY (`db_name`,`table_name`,`column_name`) ) ENGINE=Aria DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_bin PAGE_CHECKSUM=1 TRANSACTIONAL=0 COMMENT='Statistics on Columns' show create table index_stats; diff --git a/mysql-test/main/system_mysql_db_fix50030.result b/mysql-test/main/system_mysql_db_fix50030.result index fd0074c7f54f5..4e038849cf672 100644 --- a/mysql-test/main/system_mysql_db_fix50030.result +++ b/mysql-test/main/system_mysql_db_fix50030.result @@ -276,8 +276,8 @@ column_stats CREATE TABLE `column_stats` ( `avg_length` decimal(12,4) DEFAULT NULL, `avg_frequency` decimal(12,4) DEFAULT NULL, `hist_size` tinyint(3) unsigned DEFAULT NULL, - `hist_type` enum('SINGLE_PREC_HB','DOUBLE_PREC_HB') COLLATE utf8mb3_bin DEFAULT NULL, - `histogram` varbinary(255) DEFAULT NULL, + `hist_type` enum('SINGLE_PREC_HB','DOUBLE_PREC_HB','JSON') COLLATE utf8mb3_bin DEFAULT NULL, + `histogram` blob DEFAULT NULL, PRIMARY KEY (`db_name`,`table_name`,`column_name`) ) ENGINE=Aria DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_bin PAGE_CHECKSUM=1 TRANSACTIONAL=0 COMMENT='Statistics on Columns' show create table index_stats; diff --git a/mysql-test/main/system_mysql_db_fix50117.result b/mysql-test/main/system_mysql_db_fix50117.result index a7d705c4158c1..7d540477d51ac 100644 --- a/mysql-test/main/system_mysql_db_fix50117.result +++ b/mysql-test/main/system_mysql_db_fix50117.result @@ -256,8 +256,8 @@ column_stats CREATE TABLE `column_stats` ( `avg_length` decimal(12,4) DEFAULT NULL, `avg_frequency` decimal(12,4) DEFAULT NULL, `hist_size` tinyint(3) unsigned DEFAULT NULL, - `hist_type` enum('SINGLE_PREC_HB','DOUBLE_PREC_HB') COLLATE utf8mb3_bin DEFAULT NULL, - `histogram` varbinary(255) DEFAULT NULL, + `hist_type` enum('SINGLE_PREC_HB','DOUBLE_PREC_HB','JSON') COLLATE utf8mb3_bin DEFAULT NULL, + `histogram` blob DEFAULT NULL, PRIMARY KEY (`db_name`,`table_name`,`column_name`) ) ENGINE=Aria DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_bin PAGE_CHECKSUM=1 TRANSACTIONAL=0 COMMENT='Statistics on Columns' show create table index_stats; From 691905a5317033e8c152a9b56e2ef256e6267bd9 Mon Sep 17 00:00:00 2001 From: Michael Okoko Date: Thu, 24 Jun 2021 07:41:09 +0100 Subject: [PATCH 05/12] rough base for json histogram builder Signed-off-by: Michael Okoko --- mysql-test/main/statistics_json.result | Bin 1365 -> 3493 bytes mysql-test/main/statistics_json.test | 6 ++ sql/sql_statistics.cc | 92 ++++++++++++++++++++++--- 3 files changed, 87 insertions(+), 11 deletions(-) diff --git a/mysql-test/main/statistics_json.result b/mysql-test/main/statistics_json.result index 3e7d3a307fa02f184d2c36efc35e5a060634c35f..3e7a9a43634820375c226f93045309fec268058c 100644 GIT binary patch literal 3493 zcmeHK&2HO95Ec>y!S2;PG&v38gUEt4EGJG82nH-m>qNCg8YG=W2?}B*u4Mw`%Hb{* zsh2)NpQ88PgWUTFeT2M4yStPnI+2rHf*?P0;Lmb)=jWUGcDac+A!I^=JfjkR4LlbT z0TiY*O=37pgq)0Xnt`03GoU<%mkdUfgBs6SsV`)n@Ueh|LpB#L(yh@XEixVnNu|J> zIL;^MAn7n=kemXWsWk=2MiUDrVPDWGiHB^~KnPQ$Dbn#8N@KB^!ux zn$CY`NMlk6MEE%2?B|5X?3}5K%B23HkV!@Y6n-fiNfuu<-8;e^614s90a*ii6CWll zjZvteL~fC$=!nfxhn>o$`qHY_+c#&E4pzA*Dk?7bb z?dZ`#-KlrH6V)}90aNRj{9d*j8m5zBb@fF<`~-)-*Ix_v1(X%Us=%!Bzshnomz9+1 z$5m!l30OtA^wU%(QiVN4)gGFnE4yxnwzsyGpvHr(U2`vAcXsa~oe=aK1Khc^*d6EH zA^h84pIH7}$c^hYBY)ql{qyRNTJ7I!<-5M>_aATFG~@dl<$YuE`StA|o@f_0YBy?c ztfza|3@O)2cnl}k)4X@$mFNUKGDVn`3%^8j7P^e940x^~Bg5|&NqQ2br zXjMF)t0^T2fA_R-S=uIL#;7S`w^~-*T$v t_K#T=D;j$Xtdgp>o$q%|+22kc8fH)WcH;eB-)rj|cYN=7`Lol+{{wRFJh=b> delta 46 zcmZ1~eU)ni7pqXUdPZtaPQJR8f_iy=QBI0_?PLx%Eq<3E{{V#$M<*Xwg%U$+E&x#^ B4Lkq< diff --git a/mysql-test/main/statistics_json.test b/mysql-test/main/statistics_json.test index 0af7d1f6d1d00..9ffb27b621d78 100644 --- a/mysql-test/main/statistics_json.test +++ b/mysql-test/main/statistics_json.test @@ -25,4 +25,10 @@ DESCRIBE mysql.column_stats; SELECT * FROM mysql.column_stats; set histogram_type=@save_histogram_type; + +## Remove against Milestone-2 +ANALYZE TABLE t1 PERSISTENT FOR COLUMNS(b) INDEXES(); +SELECT * FROM mysql.column_stats; +select table_name, hist_type, decode_histogram(hist_type, histogram ) from mysql.column_stats; + DROP TABLE t1; \ No newline at end of file diff --git a/sql/sql_statistics.cc b/sql/sql_statistics.cc index 16e3e8b166474..d2cad99f13022 100644 --- a/sql/sql_statistics.cc +++ b/sql/sql_statistics.cc @@ -32,6 +32,9 @@ #include "uniques.h" #include "sql_show.h" #include "sql_partition.h" +#include "my_json_writer.h" + +#include /* The system variable 'use_stat_tables' can take one of the @@ -1070,13 +1073,8 @@ class Column_stat: public Stat_table stat_field->store(stats->histogram.get_type() + 1); break; case COLUMN_STAT_HISTOGRAM: - if (stats->histogram.get_type() == JSON) { - const char* val = "{'hello': 'world'}"; - stat_field->store(val, strlen(val), &my_charset_bin); - } else { stat_field->store((char *) stats->histogram.get_values(), stats->histogram.get_size(), &my_charset_bin); - } break; } } @@ -1524,6 +1522,7 @@ class Stat_table_write_iter class Histogram_builder { +protected: Field *column; /* table field for which the histogram is built */ uint col_length; /* size of this field */ ha_rows records; /* number of records the histogram is built for */ @@ -1554,13 +1553,15 @@ class Histogram_builder count_distinct_single_occurence= 0; } + virtual ~Histogram_builder() = default; + ulonglong get_count_distinct() const { return count_distinct; } ulonglong get_count_single_occurence() const { return count_distinct_single_occurence; } - int next(void *elem, element_count elem_cnt) + virtual int next(void *elem, element_count elem_cnt) { count_distinct++; if (elem_cnt == 1) @@ -1572,7 +1573,7 @@ class Histogram_builder { column->store_field_value((uchar *) elem, col_length); histogram->set_value(curr_bucket, - column->pos_in_interval(min_value, max_value)); + column->pos_in_interval(min_value, max_value)); curr_bucket++; while (curr_bucket != hist_width && count > bucket_capacity * (curr_bucket + 1)) @@ -1585,6 +1586,57 @@ class Histogram_builder } }; +class Histogram_builder_json : public Histogram_builder +{ +std::vector bucket_bounds; + +public: + Histogram_builder_json(Field *col, uint col_len, ha_rows rows) + : Histogram_builder(col, col_len, rows) + { + Column_statistics *col_stats= col->collected_stats; + min_value= col_stats->min_value; + max_value= col_stats->max_value; + histogram= &col_stats->histogram; + hist_width= histogram->get_width(); + bucket_capacity= (double) records / (hist_width + 1); + curr_bucket= 0; + count= 0; + count_distinct= 0; + count_distinct_single_occurence= 0; + bucket_bounds = {}; + } + + ~Histogram_builder_json() override = default; + + int next(void *elem, element_count elem_cnt) override + { + count_distinct++; + if (elem_cnt == 1) + count_distinct_single_occurence++; + count+= elem_cnt; + if (curr_bucket == hist_width) + return 0; + if (count > bucket_capacity * (curr_bucket + 1)) + { + auto *val= new StringBuffer; + column->val_str(val); + bucket_bounds.emplace_back(String(val->ptr(), val->length(), &my_charset_bin)); + curr_bucket++; + } + return 0; + } + + void build() { + Json_writer *writer = new Json_writer(); + writer->start_array(); + for(auto& value: bucket_bounds) { + writer->add_str(value); + } + writer->end_array(); + histogram->set_values((uchar *) writer->output.get_string()->ptr()); + } +}; C_MODE_START @@ -1594,6 +1646,12 @@ int histogram_build_walk(void *elem, element_count elem_cnt, void *arg) return hist_builder->next(elem, elem_cnt); } +int json_histogram_build_walk(void *elem, element_count elem_cnt, void *arg) +{ + Histogram_builder_json *hist_builder= (Histogram_builder_json *) arg; + return hist_builder->next(elem, elem_cnt); +} + static int count_distinct_single_occurence_walk(void *elem, @@ -1699,10 +1757,22 @@ class Count_distinct_field: public Sql_alloc */ void walk_tree_with_histogram(ha_rows rows) { - Histogram_builder hist_builder(table_field, tree_key_length, rows); - tree->walk(table_field->table, histogram_build_walk, (void *) &hist_builder); - distincts= hist_builder.get_count_distinct(); - distincts_single_occurence= hist_builder.get_count_single_occurence(); + if(table_field->collected_stats->histogram.get_type() == JSON) + { + Histogram_builder_json hist_builder(table_field, tree_key_length, rows); + tree->walk(table_field->table, json_histogram_build_walk, + (void *) &hist_builder); + hist_builder.build(); + distincts= hist_builder.get_count_distinct(); + distincts_single_occurence= hist_builder.get_count_single_occurence(); + } else + { + Histogram_builder hist_builder(table_field, tree_key_length, rows); + tree->walk(table_field->table, histogram_build_walk, + (void *) &hist_builder); + distincts= hist_builder.get_count_distinct(); + distincts_single_occurence= hist_builder.get_count_single_occurence(); + } } ulonglong get_count_distinct() From 9a8caf89fec0215b7ad46d1e2e8c167c30a777cd Mon Sep 17 00:00:00 2001 From: Michael Okoko Date: Wed, 30 Jun 2021 05:51:08 +0100 Subject: [PATCH 06/12] Store bucket bounds and extend test cases for JSON histogram This fixes the memory allocation for json histogram builder and add more column types for testing. Some challenges at the moment include: * Garbage value at the end of JSON array still persists. * Garbage value also gets appended to bucket values if the column is a primary key. * There's a memory leak resulting in a "Warning: Memory not freed" message at the end of tests. Signed-off-by: Michael Okoko --- mysql-test/main/statistics_json.result | Bin 3493 -> 24466 bytes mysql-test/main/statistics_json.test | 56 +++++++++++++++++-------- sql/sql_statistics.cc | 21 +++++++--- sql/sql_statistics.h | 6 +-- 4 files changed, 57 insertions(+), 26 deletions(-) diff --git a/mysql-test/main/statistics_json.result b/mysql-test/main/statistics_json.result index 3e7a9a43634820375c226f93045309fec268058c..a6b9458823ea055d35cedfc0ec6b5d77778b9b46 100644 GIT binary patch literal 24466 zcmeHP&2QVt6-N#QN*;<{dnhnT4oS9NgERbwkws)#b`(ppD#=NcATUPKSYnqV9e&tR z4&7}RJ@?vUi=sXC(nF8EY|z^lXzx9=i=z84^w>vo_#w;oVi!?FIpRT_H#401y^nb_ zdNbs0@iyu)KS05l2I!2T0dvasdrZ<_cp`< ziqlbYh=y(m_{4i#V!hR^b$Y1Y>K#N+cWaGWwTFE6a%1)f9eeI1$~wYFfqO*MEwNkc z<)TVBxf99YPJ%z@DMpT^EwR$7G@d>yfNj@0-FmlIYxU6XK?hYDjV-YkiB32;5t{xe zZZ%1;SAiIS`ZfbBP>0&n*G!0X5&A;S8Ceed&~k?Cgu#y!fL>_;g0}`f0=?O65U}B8 zk00y2(2pxw?v^CEped06X!cq3>>4xO<3cLEH24*M}BSsUvMvQD_Ue;D zP1E=^rwE>*5j?qE@?Q%cU4owHtNgB`Ti=#}dGNy-yD=_De_n>M6=8|%g9{!j zoYwf=oMY+RLIpO(;5ZJ$tw%hCWM^ zkvGm=RP4a7@LrL>oAq8*H*^ATiY{_t+?^C$!3N5F0VGprun{AO2RctiIFe*C0bR;% zq3sLZ%+(&G>JFJu*?uBCds2&BNbV{sohFK}L&pN2iMrrOnU9*5<9g^I4D6}pj8NUT zsWbGGn(zKMF|h4gV`ZQ=o~;VhYGrq2pdRe43{+V4#8>%;RjpHB6{z4HR|V=`b9-f= z9zI+pSeGfX3s>sz7b-@2m{e!|tj;ZPlx*0<~RPnPJt2kWhS; zf4S1F9Ig!1W@lxfK3$n%)oZp_MXHDUD`NnU>#G1&e38b7*7vmp-ZKgO_^#Luy<<9H z6^M00Bwc|3x-RJ|MA9LqI~&)NHOS%tS~Uu@^B}F3r)47w1t<9$&eIr6QG`1wfVQK? zc}tRM7AS-0(Iy0MS=ZIPOoj&Y7FCQ*J*%l>UCC>jvLxpe#zZ4|?^uI|@=_ujvY9hL z3|TGmBO6#zvu+f{G_@R`YUq-ZKdYOXWagEDXGJb@swoC8loOpOW>PM`NT#2q{)%n) zB($SVB}V`&ARv6zh<1f$g1Z=UZe?3OQTZ1K&kwo2R3Ni5ZH6)52 zr6mzPg34_bYjLBT(lCiwf#LK))(TNgtw5QYo>MUFjRK`h=8vN1*2}u(FJ*oy@*U1>pK!)f zwmUrV7L0Ci(|gFn8(GfIv-5T8JLl`vTRbY3J@u%4zD~3Dlf<60I_-x%?wjRw(vVe_ zbJ#8Pe&rx-clqWFJX@Sd6#}CpnkAx>3B_R&%|IkGaHs=ogLFQ`*pP{qQ8JOSkx?p< zO+BOFseE=MB$~R?GGf5oDW{Zd8ZgbJl%`8WirQQcljRSSn_w}_8^D<(I%3gj7Qr+( z&v{j8lOL6kHq$c_oBYrONfYuWKQ^Il^7AcbR$1TVM|NN}%L@%1Qs3ZMMJgyPwxWYc zmE+NvI&jE{I>Iw+(x?41HoIJ?cq8D+a_E&a$#M!4f3r=!J{`JCJA`l*?;N2U_-Ald z?W&r~D*Fb@g(X4c2;Bjw`0hSqHXJ;;x9pQAGqT_aU$em&b=V0Usk8<&6_!pU2x~U# zQu`DJi??({=K&-LcRlJ1*!i+@&%)sKw6EUI?!TlxkQf z2s`cs7U87=mZ>X2*rgMJ1T`mU!au&p!VXMRyxCr|t=FKKt_1KX_w!_jP7pVw-uxY4GM3fBmg>dcj=!_OgmQ zCdvF)wV4O&u;9a82JcbBN@r2meAjz$>;cyI;i7uX+MVyZ9?%muV4gLg2-YXvi1)1e z+f2-Ny&q14@Len;=eurN@b=%kSTW9beGn{0zH_>o@4Djx(;vTsAmx15-4oji<{&O0 zW4`M{+kZDfyP2-nJDa7Rp#WUv#@Cz=*yH>9ZPyFJQOMxU>$iOU?Tf}oSH8Btz{0*}2#x^{oCvf;Xl4ALua`KKck>BeX4l{5JpWmW9Iwu*Sty77q8A>y4~MoTDoX zhY+&1fx)+Fo+Z;^!=1p_T^DhcMTbq-qo)f|Vp%wBht5xF-&%kY%feyT9W7)wM5rtr z_NU48s5b literal 3493 zcmeHK&2HO95Ec>y!S2;PG&v38gUEt4EGJG82nH-m>qNCg8YG=W2?}B*u4Mw`%Hb{* zsh2)NpQ88PgWUTFeT2M4yStPnI+2rHf*?P0;Lmb)=jWUGcDac+A!I^=JfjkR4LlbT z0TiY*O=37pgq)0Xnt`03GoU<%mkdUfgBs6SsV`)n@Ueh|LpB#L(yh@XEixVnNu|J> zIL;^MAn7n=kemXWsWk=2MiUDrVPDWGiHB^~KnPQ$Dbn#8N@KB^!ux zn$CY`NMlk6MEE%2?B|5X?3}5K%B23HkV!@Y6n-fiNfuu<-8;e^614s90a*ii6CWll zjZvteL~fC$=!nfxhn>o$`qHY_+c#&E4pzA*Dk?7bb z?dZ`#-KlrH6V)}90aNRj{9d*j8m5zBb@fF<`~-)-*Ix_v1(X%Us=%!Bzshnomz9+1 z$5m!l30OtA^wU%(QiVN4)gGFnE4yxnwzsyGpvHr(U2`vAcXsa~oe=aK1Khc^*d6EH zA^h84pIH7}$c^hYBY)ql{qyRNTJ7I!<-5M>_aATFG~@dl<$YuE`StA|o@f_0YBy?c ztfza|3@O)2cnl}k)4X@$mFNUKGDVn`3%^8j7P^e940x^~Bg5|&NqQ2br zXjMF)t0^T2fA_R-S=uIL#;7S`w^~-*T$v t_K#T=D;j$Xtdgp>o$q%|+22kc8fH)WcH;eB-)rj|cYN=7`Lol+{{wRFJh=b> diff --git a/mysql-test/main/statistics_json.test b/mysql-test/main/statistics_json.test index 9ffb27b621d78..acc44456d8f23 100644 --- a/mysql-test/main/statistics_json.test +++ b/mysql-test/main/statistics_json.test @@ -1,34 +1,56 @@ --source include/have_stat_tables.inc +--source include/have_sequence.inc +--source include/analyze-format.inc --echo # ---echo # Test that JSON is a valid histogram type and we can store JSON strings in mysql.column_stats +--echo # Test that we can store JSON arrays in histogram field mysql.column_stats when histogram_type=JSON --echo # --disable_warnings drop table if exists t1; --enable_warnings set @save_histogram_type=@@histogram_type; +set @save_histogram_size=@@histogram_size; CREATE TABLE t1 ( - a int NOT NULL PRIMARY KEY, - b varchar(32) -) ENGINE=MYISAM; + a int, + b varchar(32), + c char(2), + d double +); + +--disable_result_log +INSERT INTO t1 SELECT seq, seq, seq, seq from seq_1_to_25; +--enable_result_log SET histogram_type='JSON'; -SELECT @@histogram_type; +# set histogram size to be < row count (25 in this case) to see how histogram behaves +set histogram_size=10; + +ANALYZE TABLE t1 PERSISTENT FOR ALL; +SELECT * FROM mysql.column_stats WHERE table_name='t1'; +DELETE FROM mysql.column_stats; +DROP TABLE t1; + +create schema world; +use world; +--disable_query_log +--disable_result_log +--disable_warnings +--source include/world_schema_utf8.inc +--source include/world.inc +--enable_warnings +--enable_result_log +--enable_query_log -INSERT INTO t1 VALUES - (7, 'xxxxxxxxxxxxxxxxxxxxxxxxxx'), - (17, 'vvvvvvvvvvvvv'); +set histogram_type='JSON'; +set histogram_size=25; +--disable_result_log +ANALYZE TABLE Country PERSISTENT FOR ALL; +--enable_result_log -ANALYZE TABLE t1 PERSISTENT FOR COLUMNS(b) INDEXES(); -DESCRIBE mysql.column_stats; -SELECT * FROM mysql.column_stats; +SELECT column_name, min_value, max_value, hist_size, hist_type, histogram FROM mysql.column_stats; set histogram_type=@save_histogram_type; +set histogram_size=@save_histogram_size; -## Remove against Milestone-2 -ANALYZE TABLE t1 PERSISTENT FOR COLUMNS(b) INDEXES(); -SELECT * FROM mysql.column_stats; -select table_name, hist_type, decode_histogram(hist_type, histogram ) from mysql.column_stats; - -DROP TABLE t1; \ No newline at end of file +DROP SCHEMA world; \ No newline at end of file diff --git a/sql/sql_statistics.cc b/sql/sql_statistics.cc index d2cad99f13022..cbbd7d30b4239 100644 --- a/sql/sql_statistics.cc +++ b/sql/sql_statistics.cc @@ -1073,9 +1073,16 @@ class Column_stat: public Stat_table stat_field->store(stats->histogram.get_type() + 1); break; case COLUMN_STAT_HISTOGRAM: + if (stats->histogram.get_type() == JSON) + { + stat_field->store((char *) stats->histogram.get_values(), + strlen((char *) stats->histogram.get_values()), &my_charset_bin); + } else + { stat_field->store((char *) stats->histogram.get_values(), stats->histogram.get_size(), &my_charset_bin); - break; + } + break; } } } @@ -1588,7 +1595,7 @@ class Histogram_builder class Histogram_builder_json : public Histogram_builder { -std::vector bucket_bounds; +std::vector bucket_bounds; public: Histogram_builder_json(Field *col, uint col_len, ha_rows rows) @@ -1619,9 +1626,10 @@ std::vector bucket_bounds; return 0; if (count > bucket_capacity * (curr_bucket + 1)) { - auto *val= new StringBuffer; - column->val_str(val); - bucket_bounds.emplace_back(String(val->ptr(), val->length(), &my_charset_bin)); + column->store_field_value((uchar *) elem, col_length); + StringBuffer val; + column->val_str(&val); + bucket_bounds.emplace_back(val.ptr()); curr_bucket++; } return 0; @@ -1631,9 +1639,10 @@ std::vector bucket_bounds; Json_writer *writer = new Json_writer(); writer->start_array(); for(auto& value: bucket_bounds) { - writer->add_str(value); + writer->add_str(value.c_str()); } writer->end_array(); + histogram->set_size(bucket_bounds.size()); histogram->set_values((uchar *) writer->output.get_string()->ptr()); } }; diff --git a/sql/sql_statistics.h b/sql/sql_statistics.h index 555d03cfa2232..a554721d50b13 100644 --- a/sql/sql_statistics.h +++ b/sql/sql_statistics.h @@ -153,9 +153,9 @@ class Histogram { switch (type) { case SINGLE_PREC_HB: + case JSON: return ((uint) (1 << 8) - 1); case DOUBLE_PREC_HB: - case JSON: return ((uint) (1 << 16) - 1); } return 1; @@ -166,9 +166,9 @@ class Histogram { switch (type) { case SINGLE_PREC_HB: + case JSON: return size; case DOUBLE_PREC_HB: - case JSON: return size / 2; } return 0; @@ -180,9 +180,9 @@ class Histogram DBUG_ASSERT(i < get_width()); switch (type) { case SINGLE_PREC_HB: + case JSON: return (uint) (((uint8 *) values)[i]); case DOUBLE_PREC_HB: - case JSON: return (uint) uint2korr(values + i * 2); } return 0; From 40966e5c049e59f257992a4946452cffbb893160 Mon Sep 17 00:00:00 2001 From: Michael Okoko Date: Fri, 2 Jul 2021 10:31:25 +0100 Subject: [PATCH 07/12] Fix garbage null values at end of json array elements Signed-off-by: Michael Okoko --- mysql-test/main/statistics_json.result | 363 ++++++++++++------------- sql/sql_statistics.cc | 2 +- 2 files changed, 182 insertions(+), 183 deletions(-) diff --git a/mysql-test/main/statistics_json.result b/mysql-test/main/statistics_json.result index a6b9458823ea0..7164198f2aeca 100644 --- a/mysql-test/main/statistics_json.result +++ b/mysql-test/main/statistics_json.result @@ -23,14 +23,14 @@ test t1 a 1 25 0.0000 4.0000 1.0000 10 JSON [ "3", "5", "7", - "10x~��", - "12�~��", - "14P�罣", - "16x��", - "198���", - "218���", - "23����" -]������������������������ + "10", + "12", + "14", + "16", + "19", + "21", + "23" +]������������������������������������������������������������������ test t1 b 1 9 0.0000 1.6400 1.0000 10 JSON [ "11", "13", @@ -48,13 +48,13 @@ test t1 c 1 9 0.0000 2.0000 1.0000 10 JSON [ "13", "15", "18", - "2 ", + "2", "21", "23", - "3 ", - "5 ", - "7 " -]��������������������������������������������������������������� + "3", + "5", + "7" +]������������������������������������������������������������������� test t1 d 1 25 0.0000 8.0000 1.0000 10 JSON [ "3", "5", @@ -77,32 +77,59 @@ ANALYZE TABLE Country PERSISTENT FOR ALL; SELECT column_name, min_value, max_value, hist_size, hist_type, histogram FROM mysql.column_stats; column_name min_value max_value hist_size hist_type histogram Code ABW ZWE 25 JSON [ - "ARM United States Minor Outlying Islands ", - "BEL United States Minor Outlying Islands ", - "BLZ United States Minor Outlying Islands ", - "CAF United States Minor Outlying Islands ", - "COG United States Minor Outlying Islands ", - "CZE United States Minor Outlying Islands ", - "ERI United States Minor Outlying Islands ", - "FRO United States Minor Outlying Islands ", - "GMB United States Minor Outlying Islands ", - "GUY United States Minor Outlying Islands ", - "IRL United States Minor Outlying Islands ", - "KAZ United States Minor Outlying Islands ", - "LBN United States Minor Outlying Islands ", - "LVA United States Minor Outlying Islands ", - "MKD United States Minor Outlying Islands ", - "MUS United States Minor Outlying Islands ", - "NIC United States Minor Outlying Islands ", - "PAN United States Minor Outlying Islands ", - "PRT United States Minor Outlying Islands ", - "SAU United States Minor Outlying Islands ", - "SMR United States Minor Outlying Islands ", - "SYC United States Minor Outlying Islands ", - "TMP United States Minor Outlying Islands ", - "UKR United States Minor Outlying Islands ", - "VIR United States Minor Outlying Islands " -]�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Q( + "ARM", + "BEL", + "BLZ", + "CAF", + "COG", + "CZE", + "ERI", + "FRO", + "GMB", + "GUY", + "IRL", + "KAZ", + "LBN", + "LVA", + "MKD", + "MUS", + "NIC", + "PAN", + "PRT", + "SAU", + "SMR", + "SYC", + "TMP", + "UKR", + "VIR" +]�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0 +Name Afghanistan Zimbabwe 25 JSON [ + "Argentina", + "Barbados", + "Botswana", + "Cameroon", + "Cocos (Keeling) Islands", + "Czech Republic", + "El Salvador", + "France", + "Gibraltar", + "Guyana", + "Iran", + "Kenya", + "Libyan Arab Jamahiriya", + "Maldives", + "Micronesia, Federated States of", + "Nepal", + "Norfolk Island", + "Papua New Guinea", + "Romania", + "Samoa", + "Solomon Islands", + "Svalbard and Jan Mayen", + "Togo", + "Uganda", + "Venezuela" +]�����������������������������������������������������������������������������������������������������a SurfaceArea 0.40 17075400.00 25 JSON [ "36.00", "151.00", @@ -129,25 +156,25 @@ SurfaceArea 0.40 17075400.00 25 JSON [ "1104300.00", "1648195.00", "2724900.00" -] +]�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� Population 0 1277558000 25 JSON [ - "1000"��", - "8000�O[U", - "25000��", - "65000��", - "94000��", - "181000�", - "293000�", - "453000�", - "817000�", - "1439200", - "2542000", - "3337000", - "3869000", - "4807000", - "5496000", - "7430000", - "8861400", + "1000", + "8000", + "25000", + "65000", + "94000", + "181000", + "293000", + "453000", + "817000", + "1439200", + "2542000", + "3337000", + "3869000", + "4807000", + "5496000", + "7430000", + "8861400", "10278100", "11669000", "16125000", @@ -156,71 +183,106 @@ Population 0 1277558000 25 JSON [ "40377000", "61399000", "111506000" -] +]����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� Capital 1 4074 25 JSON [ - "62h�#��", - "149�#��", - "201�#��", - "553�#��", - "645�#��", - "902�#��", - "922�#��", - "1109#��", - "1530#��", - "1859#��", - "2296#��", - "2430#��", - "2452#��", - "2482#��", - "2689#��", - "2729#��", - "2831#��", - "2919#��", - "3047#��", - "3162#��", - "3209#��", - "3250#��", - "3336#��", - "3426#��", - "3538#��" -] ", - "Cameroon � + "62", + "149", + "201", + "553", + "645", + "902", + "922", + "1109", + "1530", + "1859", + "2296", + "2430", + "2452", + "2482", + "2689", + "2729", + "2831", + "2919", + "3047", + "3162", + "3209", + "3250", + "3336", + "3426", + "3538" +]��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ID 1 4079 10 JSON [ - "371r!��", - "742�!��", - "1113!��", - "1484!��", - "1855"��", - "2225*��", - "2596*��", - "2967*��", - "3338*��", - "3709+��" -]��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� + "371", + "742", + "1113", + "1484", + "1855", + "2225", + "2596", + "2967", + "3338", + "3709" +]��������������������������������������������� +Name A Coruña (La Coruña) Ã…rhus 10 JSON [ + "Berdytšiv", + "Clermont-Ferrand", + "Gatineau", + "Itapecerica da Serra", + "Kuytun", + "Milano", + "Ota", + "Roanoke", + "Split", + "Ulsan" +]������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� Country ABW ZWE 10 JSON [ - "BRA tg", - "CHN tg", - "DZA tg", - "IDN tg", - "IND tg", - "JPN tg", - "MEX tg", - "PHL tg", - "RUS tg", - "USA tg" -] PSE tg + "BRA", + "CHN", + "DZA", + "IDN", + "IND", + "JPN", + "MEX", + "PHL", + "RUS", + "USA" +]����������������������������������������������������� Population 42 10500000 10 JSON [ - "96002��", - "105819�", - "118326�", - "132318�", - "152397�", - "181900�", - "221400�", - "288173�", - "398300�", - "670208�" -]��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������! + "96002", + "105819", + "118326", + "132318", + "152397", + "181900", + "221400", + "288173", + "398300", + "670208" +]������������������������ +Country ABW ZWE 10 JSON [ + "BGD", + "CMR", + "ESP", + "GUF", + "KAZ", + "MDA", + "NAM", + "PNG", + "SVK", + "UGA" +]����������������������������������������������������� +Language Abhyasi [South]Mande 10 JSON [ + "Bariba", + "Creole English", + "English", + "Georgiana", + "Italian", + "Lomwe", + "Moravian", + "Punjabi", + "Soga", + "Tigrinja" +] Percentage 0.0 99.9 10 JSON [ "0.2", "0.6", @@ -232,70 +294,7 @@ Percentage 0.0 99.9 10 JSON [ "17.7", "45.5", "87.5" -]�������������������������������������������������1 -Name Afghanistan Zimbabwe 25 JSON [ - "Argentina ", - "Barbados ", - "Botswana ", - "Cameroon ", - "Cocos (Keeling) Islands ", - "Czech Republic ", - "El Salvador ", - "France ", - "Gibraltar ", - "Guyana ", - "Iran ", - "Kenya ", - "Libyan Arab Jamahiriya ", - "Maldives ", - "Micronesia, Federated States of ", - "Nepal ", - "Norfolk Island ", - "Papua New Guinea ", - "Romania ", - "Samoa ", - "Solomon Islands ", - "Svalbard and Jan Mayen ", - "Togo ", - "Uganda ", - "Venezuela " -]������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������! -Name A Coruña (La Coruña) Ã…rhus 10 JSON [ - "Berdytšiv PSE tg", - "Clermont-Ferrand PSE tg", - "Gatineau PSE tg", - "Itapecerica da Serra PSE tg", - "Kuytun PSE tg", - "Milano PSE tg", - "Ota PSE tg", - "Roanoke PSE tg", - "Split PSE tg", - "Ulsan PSE tg" -]��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������A -Country ABW ZWE 10 JSON [ - "BGD Portuguese ��L>����������������������������������������������������������������������������������������������������������������������������", - "CMR Portuguese ��L>����������������������������������������������������������������������������������������������������������������������������", - "ESP Portuguese ��L>����������������������������������������������������������������������������������������������������������������������������", - "GUF Portuguese ��L>����������������������������������������������������������������������������������������������������������������������������", - "KAZ Portuguese ��L>����������������������������������������������������������������������������������������������������������������������������", - "MDA Portuguese ��L>����������������������������������������������������������������������������������������������������������������������������", - "NAM Portuguese ��L>����������������������������������������������������������������������������������������������������������������������������", - "PNG Portuguese ��L>����������������������������������������������������������������������������������������������������������������������������", - "SVK Portuguese ��L>����������������������������������������������������������������������������������������������������������������������������", - "UGA Portuguese ��L>����������������������������������������������������������������������������������������������������������������������������" -]�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� -Language Abhyasi [South]Mande 10 JSON [ - "Bariba ��L>����������������������������������������������������������������������������������������������������������������������������", - "Creole English ��L>����������������������������������������������������������������������������������������������������������������������������", - "English ��L>����������������������������������������������������������������������������������������������������������������������������", - "Georgiana ��L>����������������������������������������������������������������������������������������������������������������������������", - "Italian ��L>����������������������������������������������������������������������������������������������������������������������������", - "Lomwe ��L>����������������������������������������������������������������������������������������������������������������������������", - "Moravian ��L>����������������������������������������������������������������������������������������������������������������������������", - "Punjabi ��L>����������������������������������������������������������������������������������������������������������������������������", - "Soga ��L>����������������������������������������������������������������������������������������������������������������������������", - "Tigrinja ��L>����������������������������������������������������������������������������������������������������������������������������" -]������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +]�������������������������������������������������q set histogram_type=@save_histogram_type; set histogram_size=@save_histogram_size; DROP SCHEMA world; diff --git a/sql/sql_statistics.cc b/sql/sql_statistics.cc index cbbd7d30b4239..15736778cc047 100644 --- a/sql/sql_statistics.cc +++ b/sql/sql_statistics.cc @@ -1629,7 +1629,7 @@ std::vector bucket_bounds; column->store_field_value((uchar *) elem, col_length); StringBuffer val; column->val_str(&val); - bucket_bounds.emplace_back(val.ptr()); + bucket_bounds.emplace_back(val.c_ptr()); curr_bucket++; } return 0; From 4456bf7ff3084fe72ff4e8f201c7c63524a6db4c Mon Sep 17 00:00:00 2001 From: Michael Okoko Date: Fri, 2 Jul 2021 10:50:51 +0100 Subject: [PATCH 08/12] Fix garbage null values at end of histogram json Signed-off-by: Michael Okoko --- mysql-test/main/statistics_json.result | 30 +++++++++++++------------- sql/sql_statistics.cc | 3 ++- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/mysql-test/main/statistics_json.result b/mysql-test/main/statistics_json.result index 7164198f2aeca..e53a6f72f12b5 100644 --- a/mysql-test/main/statistics_json.result +++ b/mysql-test/main/statistics_json.result @@ -30,7 +30,7 @@ test t1 a 1 25 0.0000 4.0000 1.0000 10 JSON [ "19", "21", "23" -]������������������������������������������������������������������ +] test t1 b 1 9 0.0000 1.6400 1.0000 10 JSON [ "11", "13", @@ -42,7 +42,7 @@ test t1 b 1 9 0.0000 1.6400 1.0000 10 JSON [ "3", "5", "7" -]������������������������������������������������������������������� +] test t1 c 1 9 0.0000 2.0000 1.0000 10 JSON [ "11", "13", @@ -54,7 +54,7 @@ test t1 c 1 9 0.0000 2.0000 1.0000 10 JSON [ "3", "5", "7" -]������������������������������������������������������������������� +] test t1 d 1 25 0.0000 8.0000 1.0000 10 JSON [ "3", "5", @@ -66,7 +66,7 @@ test t1 d 1 25 0.0000 8.0000 1.0000 10 JSON [ "19", "21", "23" -]�����������������������������������������������������������������e +] DELETE FROM mysql.column_stats; DROP TABLE t1; create schema world; @@ -102,7 +102,7 @@ Code ABW ZWE 25 JSON [ "TMP", "UKR", "VIR" -]�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0 +] Name Afghanistan Zimbabwe 25 JSON [ "Argentina", "Barbados", @@ -129,7 +129,7 @@ Name Afghanistan Zimbabwe 25 JSON [ "Togo", "Uganda", "Venezuela" -]�����������������������������������������������������������������������������������������������������a +] SurfaceArea 0.40 17075400.00 25 JSON [ "36.00", "151.00", @@ -156,7 +156,7 @@ SurfaceArea 0.40 17075400.00 25 JSON [ "1104300.00", "1648195.00", "2724900.00" -]�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +] Population 0 1277558000 25 JSON [ "1000", "8000", @@ -183,7 +183,7 @@ Population 0 1277558000 25 JSON [ "40377000", "61399000", "111506000" -]����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +] Capital 1 4074 25 JSON [ "62", "149", @@ -210,7 +210,7 @@ Capital 1 4074 25 JSON [ "3336", "3426", "3538" -]��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +] ID 1 4079 10 JSON [ "371", "742", @@ -222,7 +222,7 @@ ID 1 4079 10 JSON [ "2967", "3338", "3709" -]��������������������������������������������� +] Name A Coruña (La Coruña) Ã…rhus 10 JSON [ "Berdytšiv", "Clermont-Ferrand", @@ -234,7 +234,7 @@ Name A Coruña (La Coruña) Ã…rhus 10 JSON [ "Roanoke", "Split", "Ulsan" -]������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� +] Country ABW ZWE 10 JSON [ "BRA", "CHN", @@ -246,7 +246,7 @@ Country ABW ZWE 10 JSON [ "PHL", "RUS", "USA" -]����������������������������������������������������� +] Population 42 10500000 10 JSON [ "96002", "105819", @@ -258,7 +258,7 @@ Population 42 10500000 10 JSON [ "288173", "398300", "670208" -]������������������������ +] Country ABW ZWE 10 JSON [ "BGD", "CMR", @@ -270,7 +270,7 @@ Country ABW ZWE 10 JSON [ "PNG", "SVK", "UGA" -]����������������������������������������������������� +] Language Abhyasi [South]Mande 10 JSON [ "Bariba", "Creole English", @@ -294,7 +294,7 @@ Percentage 0.0 99.9 10 JSON [ "17.7", "45.5", "87.5" -]�������������������������������������������������q +] set histogram_type=@save_histogram_type; set histogram_size=@save_histogram_size; DROP SCHEMA world; diff --git a/sql/sql_statistics.cc b/sql/sql_statistics.cc index 15736778cc047..85ee5a9060989 100644 --- a/sql/sql_statistics.cc +++ b/sql/sql_statistics.cc @@ -1643,7 +1643,8 @@ std::vector bucket_bounds; } writer->end_array(); histogram->set_size(bucket_bounds.size()); - histogram->set_values((uchar *) writer->output.get_string()->ptr()); + Binary_string *json_string = (Binary_string *) writer->output.get_string(); + histogram->set_values((uchar *) json_string->c_ptr()); } }; From c6e4dc36affe465a1bd46ec9ff7ffe52492333df Mon Sep 17 00:00:00 2001 From: Michael Okoko Date: Wed, 7 Jul 2021 22:45:43 +0100 Subject: [PATCH 09/12] Properly initialize bucket bounds vector Signed-off-by: Michael Okoko --- sql/sql_statistics.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sql/sql_statistics.cc b/sql/sql_statistics.cc index 85ee5a9060989..56e20ecf48e08 100644 --- a/sql/sql_statistics.cc +++ b/sql/sql_statistics.cc @@ -35,6 +35,7 @@ #include "my_json_writer.h" #include +#include /* The system variable 'use_stat_tables' can take one of the @@ -1595,7 +1596,7 @@ class Histogram_builder class Histogram_builder_json : public Histogram_builder { -std::vector bucket_bounds; +std::vector bucket_bounds = {}; public: Histogram_builder_json(Field *col, uint col_len, ha_rows rows) @@ -1611,7 +1612,6 @@ std::vector bucket_bounds; count= 0; count_distinct= 0; count_distinct_single_occurence= 0; - bucket_bounds = {}; } ~Histogram_builder_json() override = default; From caf14c7455d444238679776b312749b7d3149084 Mon Sep 17 00:00:00 2001 From: Michael Okoko Date: Wed, 21 Jul 2021 07:23:01 +0100 Subject: [PATCH 10/12] Add parser to read JSON array (of histograms) into string vector Signed-off-by: Michael Okoko --- include/json_lib.h | 8 ++++ sql/sql_statistics.cc | 85 ++++++++++++++++++++++++++++++++++++++++++- strings/json_lib.c | 4 +- 3 files changed, 93 insertions(+), 4 deletions(-) diff --git a/include/json_lib.h b/include/json_lib.h index bb649928eaaa4..83262df8b012d 100644 --- a/include/json_lib.h +++ b/include/json_lib.h @@ -280,6 +280,14 @@ int json_key_matches(json_engine_t *je, json_string_t *k); */ int json_read_value(json_engine_t *j); +/* + * smart_read_value() reads parses a scalar value and value length from the json engine, + * and copies them into `value` and `value_length` respectively. + * It should only be called when the json_engine state is JST_VALUE. + * If it encounters a non-scalar value (say object or array) before getting to value_len, + * such value is also read and copied into value. + */ +enum json_types smart_read_value(json_engine_t *je, const char **value, int *value_len); /* json_skip_key() makes parser skip the content of the current diff --git a/sql/sql_statistics.cc b/sql/sql_statistics.cc index 56e20ecf48e08..3ab23e6ff1d5c 100644 --- a/sql/sql_statistics.cc +++ b/sql/sql_statistics.cc @@ -1635,7 +1635,7 @@ std::vector bucket_bounds = {}; return 0; } - void build() { + void build_json_from_histogram() { Json_writer *writer = new Json_writer(); writer->start_array(); for(auto& value: bucket_bounds) { @@ -1645,6 +1645,87 @@ std::vector bucket_bounds = {}; histogram->set_size(bucket_bounds.size()); Binary_string *json_string = (Binary_string *) writer->output.get_string(); histogram->set_values((uchar *) json_string->c_ptr()); + + std::vector buckets = parse_histogram_from_json(json_string->c_ptr()); + printf("%zu", buckets.size()); + + test_parse_histogram_from_json(); + } + + static std::vector parse_histogram_from_json(const char *json) + { + std::vector hist_buckets= {}; + enum json_types vt = json_get_array_items(json, json + strlen(json), hist_buckets); + printf("%d", vt); + printf("%zu", hist_buckets.size()); + + return hist_buckets; + } + + static void test_parse_histogram_from_json() + { + std::vector bucket = {}; + std::string json; + std::string tests[7] = { + R"(["aabbb", "ccccdd", "eeefff"])", + R"(["aabbb", "ccc{}dd", "eeefff"])", + R"(["aabbb", {"a": "b"}, "eeefff"])", + R"({})", + R"([1,2,3, null])", + R"([null])", + R"([])" + }; + + for(const auto& test : tests) { + json = test; + bucket = parse_histogram_from_json(json.c_str()); + printf("%zu", bucket.size()); + } + } + + /* + * json_get_array_items expects a JSON array as argument, + * and pushes the elements of the array into the `container` vector. + * It only works if all the elements in the original JSON array + * are scalar values (i.e., strings, numbers, true or false), and returns JSV_BAD_JSON if: + * the original JSON is not an array OR the JSON array contains non-scalar elements. + */ + static json_types json_get_array_items(const char *json, const char *json_end, std::vector &container) { + json_engine_t je; + enum json_types value_type; + int vl; + const char *v; + + json_scan_start(&je, &my_charset_utf8mb4_bin, (const uchar *)json, (const uchar *)json_end); + + if (json_read_value(&je) || je.value_type != JSON_VALUE_ARRAY) + { + return JSV_BAD_JSON; + } + value_type = static_cast(je.value_type); + + std::string val; + while(!json_scan_next(&je)) + { + switch(je.state) + { + case JST_VALUE: + if (je.value_type != JSON_VALUE_STRING && + je.value_type != JSON_VALUE_NUMBER && + je.value_type != JSON_VALUE_TRUE && + je.value_type != JSON_VALUE_FALSE) + { + return JSV_BAD_JSON; + } + value_type = smart_read_value(&je, &v, &vl); + val = std::string(v, vl); + container.emplace_back(val); + case JST_ARRAY_END: + break; + } + } + + return value_type; } }; @@ -1772,7 +1853,7 @@ class Count_distinct_field: public Sql_alloc Histogram_builder_json hist_builder(table_field, tree_key_length, rows); tree->walk(table_field->table, json_histogram_build_walk, (void *) &hist_builder); - hist_builder.build(); + hist_builder.build_json_from_histogram(); distincts= hist_builder.get_count_distinct(); distincts_single_occurence= hist_builder.get_count_single_occurence(); } else diff --git a/strings/json_lib.c b/strings/json_lib.c index 49f29903ed518..5df5e157141e9 100644 --- a/strings/json_lib.c +++ b/strings/json_lib.c @@ -1868,7 +1868,7 @@ int json_path_compare(const json_path_t *a, const json_path_t *b, } -static enum json_types smart_read_value(json_engine_t *je, +enum json_types smart_read_value(json_engine_t *je, const char **value, int *value_len) { if (json_read_value(je)) @@ -1952,7 +1952,6 @@ enum json_types json_get_array_item(const char *js, const char *js_end, return JSV_BAD_JSON; } - /** Simple json lookup for a value by the key. Expects JSON object. @@ -2029,6 +2028,7 @@ enum json_types json_get_object_nkey(const char *js __attribute__((unused)), } + /** Check if json is valid (well-formed) @retval 0 - success, json is well-formed From 00758d26441f5c09ff99418d570727d966315e9b Mon Sep 17 00:00:00 2001 From: Michael Okoko Date: Fri, 23 Jul 2021 08:57:41 +0100 Subject: [PATCH 11/12] Extract json parser functions from class Signed-off-by: Michael Okoko --- include/json_lib.h | 4 +- sql/sql_statistics.cc | 143 +++++++++++++++++++++++------------------- strings/json_lib.c | 8 +-- 3 files changed, 83 insertions(+), 72 deletions(-) diff --git a/include/json_lib.h b/include/json_lib.h index 83262df8b012d..8f5e2776242b1 100644 --- a/include/json_lib.h +++ b/include/json_lib.h @@ -281,13 +281,13 @@ int json_key_matches(json_engine_t *je, json_string_t *k); int json_read_value(json_engine_t *j); /* - * smart_read_value() reads parses a scalar value and value length from the json engine, + * json_smart_read_value() reads parses a scalar value and value length from the json engine, * and copies them into `value` and `value_length` respectively. * It should only be called when the json_engine state is JST_VALUE. * If it encounters a non-scalar value (say object or array) before getting to value_len, * such value is also read and copied into value. */ -enum json_types smart_read_value(json_engine_t *je, const char **value, int *value_len); +enum json_types json_smart_read_value(json_engine_t *je, const char **value, int *value_len); /* json_skip_key() makes parser skip the content of the current diff --git a/sql/sql_statistics.cc b/sql/sql_statistics.cc index 3ab23e6ff1d5c..684e0a57da332 100644 --- a/sql/sql_statistics.cc +++ b/sql/sql_statistics.cc @@ -61,8 +61,21 @@ the collected statistics in the persistent statistical tables only when the value of the variable 'use_stat_tables' is not equal to "never". -*/ - +*/ + +/* + * json_get_array_items expects a JSON array as argument, + * and pushes the elements of the array into the `container` vector. + * It only works if all the elements in the original JSON array + * are scalar values (i.e., strings, numbers, true or false), and returns JSV_BAD_JSON if: + * the original JSON is not an array OR the JSON array contains non-scalar elements. + */ +bool json_get_array_items(const char *json, const char *json_end, int *value_type, std::vector &container); + +std::vector parse_histogram_from_json(const char *json); + +void test_parse_histogram_from_json(); + /* Currently there are only 3 persistent statistical tables */ static const uint STATISTICS_TABLES= 3; @@ -1651,83 +1664,81 @@ std::vector bucket_bounds = {}; test_parse_histogram_from_json(); } +}; - static std::vector parse_histogram_from_json(const char *json) - { - std::vector hist_buckets= {}; - enum json_types vt = json_get_array_items(json, json + strlen(json), hist_buckets); - printf("%d", vt); - printf("%zu", hist_buckets.size()); +void test_parse_histogram_from_json() +{ + std::vector bucket = {}; + std::string json; + std::string tests[7] = { + R"(["aabbb", "ccccdd", "eeefff"])", + R"(["aabbb", "ccc{}dd", "eeefff"])", + R"(["aabbb", {"a": "b"}, "eeefff"])", + R"({})", + R"([1,2,3, null])", + R"([null])", + R"([])" + }; - return hist_buckets; + for(const auto& test : tests) { + json = test; + bucket = parse_histogram_from_json(json.c_str()); } +} - static void test_parse_histogram_from_json() - { - std::vector bucket = {}; - std::string json; - std::string tests[7] = { - R"(["aabbb", "ccccdd", "eeefff"])", - R"(["aabbb", "ccc{}dd", "eeefff"])", - R"(["aabbb", {"a": "b"}, "eeefff"])", - R"({})", - R"([1,2,3, null])", - R"([null])", - R"([])" - }; - - for(const auto& test : tests) { - json = test; - bucket = parse_histogram_from_json(json.c_str()); - printf("%zu", bucket.size()); - } - } +std::vector parse_histogram_from_json(const char *json) +{ + std::vector hist_buckets= {}; + int vt; + bool result = json_get_array_items(json, json + strlen(json), &vt, hist_buckets); + fprintf(stderr,"==============\n"); + fprintf(stderr,"histogram: %s\n", json); + fprintf(stderr, "json_get_array_items() returned %s\n", result ? "true" : "false"); + fprintf(stderr, "value type after json_get_array_items() is %d\n", vt); + fprintf(stderr, " JSV_BAD_JSON=%d, JSON_VALUE_ARRAY=%d\n", (int)JSV_BAD_JSON, (int)JSON_VALUE_ARRAY); + fprintf(stderr, "hist_buckets.size()=%zu\n", hist_buckets.size()); + + return hist_buckets; +} - /* - * json_get_array_items expects a JSON array as argument, - * and pushes the elements of the array into the `container` vector. - * It only works if all the elements in the original JSON array - * are scalar values (i.e., strings, numbers, true or false), and returns JSV_BAD_JSON if: - * the original JSON is not an array OR the JSON array contains non-scalar elements. - */ - static json_types json_get_array_items(const char *json, const char *json_end, std::vector &container) { - json_engine_t je; - enum json_types value_type; - int vl; - const char *v; +bool json_get_array_items(const char *json, const char *json_end, int *value_type, std::vector &container) { + json_engine_t je; + int vl; + const char *v; - json_scan_start(&je, &my_charset_utf8mb4_bin, (const uchar *)json, (const uchar *)json_end); + json_scan_start(&je, &my_charset_utf8mb4_bin, (const uchar *)json, (const uchar *)json_end); - if (json_read_value(&je) || je.value_type != JSON_VALUE_ARRAY) - { - return JSV_BAD_JSON; - } - value_type = static_cast(je.value_type); + if (json_read_value(&je) || je.value_type != JSON_VALUE_ARRAY) + { + *value_type = JSV_BAD_JSON; + return false; + } + *value_type = je.value_type; - std::string val; - while(!json_scan_next(&je)) + std::string val; + while(!json_scan_next(&je)) + { + switch(je.state) { - switch(je.state) + case JST_VALUE: + *value_type = json_smart_read_value(&je, &v, &vl); + if (je.value_type != JSON_VALUE_STRING && + je.value_type != JSON_VALUE_NUMBER && + je.value_type != JSON_VALUE_TRUE && + je.value_type != JSON_VALUE_FALSE) { - case JST_VALUE: - if (je.value_type != JSON_VALUE_STRING && - je.value_type != JSON_VALUE_NUMBER && - je.value_type != JSON_VALUE_TRUE && - je.value_type != JSON_VALUE_FALSE) - { - return JSV_BAD_JSON; - } - value_type = smart_read_value(&je, &v, &vl); - val = std::string(v, vl); - container.emplace_back(val); - case JST_ARRAY_END: - break; + *value_type = JSV_BAD_JSON; + return false; } + val = std::string(v, vl); + container.emplace_back(val); + case JST_ARRAY_END: + break; } - - return value_type; } -}; + + return true; +} C_MODE_START diff --git a/strings/json_lib.c b/strings/json_lib.c index 5df5e157141e9..efa716461d453 100644 --- a/strings/json_lib.c +++ b/strings/json_lib.c @@ -1868,7 +1868,7 @@ int json_path_compare(const json_path_t *a, const json_path_t *b, } -enum json_types smart_read_value(json_engine_t *je, +enum json_types json_smart_read_value(json_engine_t *je, const char **value, int *value_len) { if (json_read_value(je)) @@ -1909,7 +1909,7 @@ enum json_types json_type(const char *js, const char *js_end, json_scan_start(&je, &my_charset_utf8mb4_bin,(const uchar *) js, (const uchar *) js_end); - return smart_read_value(&je, value, value_len); + return json_smart_read_value(&je, value, value_len); } @@ -1933,7 +1933,7 @@ enum json_types json_get_array_item(const char *js, const char *js_end, { case JST_VALUE: if (c_item == n_item) - return smart_read_value(&je, value, value_len); + return json_smart_read_value(&je, value, value_len); if (json_skip_key(&je)) goto err_return; @@ -1997,7 +1997,7 @@ enum json_types json_get_object_key(const char *js, const char *js_end, json_string_set_str(&key_name, (const uchar *) key, (const uchar *) key_end); if (json_key_matches(&je, &key_name)) - return smart_read_value(&je, value, value_len); + return json_smart_read_value(&je, value, value_len); if (json_skip_key(&je)) goto err_return; From 7192b32e54bce65f0c352c0d25cac10b8b38c495 Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Fri, 23 Jul 2021 01:26:50 +0300 Subject: [PATCH 12/12] MDEV-21130: Histograms: use JSON as on-disk format Preparation for handling different kinds of histograms: - In Column_statistics, change "Histogram histogram" into "Histogram *histogram_". This allows for different kinds of Histogram classes with virtual functions. - [Almost] remove the usage of Histogram->set_values and Histogram->set_size. The code outside the histogram should not make any assumptions about what/how is stored in the Histogram. - Introduce drafts of methods to read/save histograms to/from disk. --- sql/sql_statistics.cc | 214 ++++++++++++++++++++++++++++++------------ sql/sql_statistics.h | 90 ++++++++++++------ sql/table.h | 10 +- 3 files changed, 226 insertions(+), 88 deletions(-) diff --git a/sql/sql_statistics.cc b/sql/sql_statistics.cc index 684e0a57da332..e4fee5ae7cd08 100644 --- a/sql/sql_statistics.cc +++ b/sql/sql_statistics.cc @@ -324,7 +324,7 @@ class Column_statistics_collected :public Column_statistics inline void init(THD *thd, Field * table_field); inline bool add(); - inline void finish(ha_rows rows, double sample_fraction); + inline void finish(MEM_ROOT *mem_root, ha_rows rows, double sample_fraction); inline void cleanup(); }; @@ -1081,21 +1081,22 @@ class Column_stat: public Stat_table stat_field->store(stats->get_avg_frequency()); break; case COLUMN_STAT_HIST_SIZE: - stat_field->store(stats->histogram.get_size()); + // Note: this is dumb. the histogram size is stored with the + // histogram! + stat_field->store(stats->histogram_? + stats->histogram_->get_size() : 0); break; case COLUMN_STAT_HIST_TYPE: - stat_field->store(stats->histogram.get_type() + 1); + if (stats->histogram_) + stat_field->store(stats->histogram_->get_type() + 1); + else + stat_field->set_null(); break; case COLUMN_STAT_HISTOGRAM: - if (stats->histogram.get_type() == JSON) - { - stat_field->store((char *) stats->histogram.get_values(), - strlen((char *) stats->histogram.get_values()), &my_charset_bin); - } else - { - stat_field->store((char *) stats->histogram.get_values(), - stats->histogram.get_size(), &my_charset_bin); - } + if (stats->histogram_) + stats->histogram_->serialize(stat_field); + else + stat_field->set_null(); break; } } @@ -1124,6 +1125,7 @@ class Column_stat: public Stat_table void get_stat_values() { table_field->read_stats->set_all_nulls(); + table_field->read_stats->histogram_type_on_disk= INVALID_HISTOGRAM; if (table_field->read_stats->min_value) table_field->read_stats->min_value->set_null(); @@ -1135,7 +1137,7 @@ class Column_stat: public Stat_table char buff[MAX_FIELD_WIDTH]; String val(buff, sizeof(buff), &my_charset_bin); - for (uint i= COLUMN_STAT_MIN_VALUE; i <= COLUMN_STAT_HIST_TYPE; i++) + for (uint i= COLUMN_STAT_MIN_VALUE; i <= COLUMN_STAT_HISTOGRAM; i++) { Field *stat_field= stat_table->field[i]; @@ -1179,13 +1181,22 @@ class Column_stat: public Stat_table table_field->read_stats->set_avg_frequency(stat_field->val_real()); break; case COLUMN_STAT_HIST_SIZE: - table_field->read_stats->histogram.set_size(stat_field->val_int()); + //TODO: ignore this. The size is a part of histogram! + //table_field->read_stats->histogram.set_size(stat_field->val_int()); break; case COLUMN_STAT_HIST_TYPE: - Histogram_type hist_type= (Histogram_type) (stat_field->val_int() - - 1); - table_field->read_stats->histogram.set_type(hist_type); - break; + // TODO: save this next to histogram. + // For some reason, the histogram itself is read in + // read_histograms_for_table + { + Histogram_type hist_type= (Histogram_type) (stat_field->val_int() - + 1); + table_field->read_stats->histogram_type_on_disk= hist_type; + break; + } + case COLUMN_STAT_HISTOGRAM: + //TODO: if stat_field->length() == 0 then histogram_type_on_disk is set to INVALID_HISTOGRAM + break; } } } @@ -1208,7 +1219,7 @@ class Column_stat: public Stat_table of read_stats->histogram. */ - void get_histogram_value() + Histogram * load_histogram(MEM_ROOT *mem_root) { if (find_stat()) { @@ -1218,13 +1229,54 @@ class Column_stat: public Stat_table Field *stat_field= stat_table->field[fldno]; table_field->read_stats->set_not_null(fldno); stat_field->val_str(&val); - memcpy(table_field->read_stats->histogram.get_values(), - val.ptr(), table_field->read_stats->histogram.get_size()); + // histogram-todo: here, create the histogram of appropriate type. + Histogram *hist= new (mem_root) Histogram(); + if (!hist->parse(mem_root, table_field->read_stats->histogram_type_on_disk, + (const uchar*)val.ptr(), val.length())) + { + table_field->read_stats->histogram_= hist; + return hist; + } + //memcpy(table_field->read_stats->histogram_.get_values(), + // val.ptr(), table_field->read_stats->histogram.get_size()); } + return NULL; } - }; +bool Histogram::parse(MEM_ROOT *mem_root, Histogram_type type_arg, const uchar *ptr_arg, uint size_arg) +{ + // Just copy the data + size = (uint8) size_arg; + type = type_arg; + values = (uchar*)alloc_root(mem_root, size_arg); + memcpy(values, ptr_arg, size_arg); + return false; +} + + +/* + Save the histogram data info a table field. +*/ +void Histogram::serialize(Field *field) +{ + if (get_type() == JSON) + { + field->store((char*)get_values(), strlen((char*)get_values()), + &my_charset_bin); + } + else + field->store((char*)get_values(), get_size(), &my_charset_bin); +} + +void Histogram::init_for_collection(MEM_ROOT *mem_root, + Histogram_type htype_arg, + ulonglong size_arg) +{ + type= htype_arg; + values = (uchar*)alloc_root(mem_root, size_arg); + size= (uint8) size_arg; +} /* An object of the class Index_stat is created to read statistical @@ -1565,7 +1617,7 @@ class Histogram_builder Column_statistics *col_stats= col->collected_stats; min_value= col_stats->min_value; max_value= col_stats->max_value; - histogram= &col_stats->histogram; + histogram= col_stats->histogram_; hist_width= histogram->get_width(); bucket_capacity= (double) records / (hist_width + 1); curr_bucket= 0; @@ -1618,7 +1670,7 @@ std::vector bucket_bounds = {}; Column_statistics *col_stats= col->collected_stats; min_value= col_stats->min_value; max_value= col_stats->max_value; - histogram= &col_stats->histogram; + histogram= col_stats->histogram_; hist_width= histogram->get_width(); bucket_capacity= (double) records / (hist_width + 1); curr_bucket= 0; @@ -1857,9 +1909,9 @@ class Count_distinct_field: public Sql_alloc @brief Calculate a histogram of the tree */ - void walk_tree_with_histogram(ha_rows rows) + void walk_tree_with_histogram(ha_rows rows) { - if(table_field->collected_stats->histogram.get_type() == JSON) + if (table_field->collected_stats->histogram_->get_type() == JSON) { Histogram_builder_json hist_builder(table_field, tree_key_length, rows); tree->walk(table_field->table, json_histogram_build_walk, @@ -1867,7 +1919,8 @@ class Count_distinct_field: public Sql_alloc hist_builder.build_json_from_histogram(); distincts= hist_builder.get_count_distinct(); distincts_single_occurence= hist_builder.get_count_single_occurence(); - } else + } + else { Histogram_builder hist_builder(table_field, tree_key_length, rows); tree->walk(table_field->table, histogram_build_walk, @@ -1891,18 +1944,19 @@ class Count_distinct_field: public Sql_alloc @brief Get the size of the histogram in bytes built for table_field */ + /* uint get_hist_size() { return table_field->collected_stats->histogram.get_size(); - } + }*/ /* @brief Get the pointer to the histogram built for table_field */ - uchar *get_histogram() + Histogram *get_histogram() { - return table_field->collected_stats->histogram.get_values(); + return table_field->collected_stats->histogram_; } }; @@ -2301,7 +2355,7 @@ int alloc_statistics_for_table(THD* thd, TABLE *table) uint key_parts= table->s->ext_key_parts; ulonglong *idx_avg_frequency= (ulonglong*) alloc_root(&table->mem_root, sizeof(ulonglong) * key_parts); - +/* uint hist_size= thd->variables.histogram_size; Histogram_type hist_type= (Histogram_type) (thd->variables.histogram_type); uchar *histogram= NULL; @@ -2312,16 +2366,16 @@ int alloc_statistics_for_table(THD* thd, TABLE *table) bzero(histogram, hist_size * columns); } - - if (!table_stats || !column_stats || !index_stats || !idx_avg_frequency || - (hist_size && !histogram)) +*/ + if (!table_stats || !column_stats || !index_stats || !idx_avg_frequency) + //|| (hist_size && !histogram)) 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; - table_stats->histograms= histogram; + //table_stats->histograms= histogram; memset(column_stats, 0, sizeof(Column_statistics) * columns); @@ -2329,10 +2383,12 @@ int alloc_statistics_for_table(THD* thd, TABLE *table) { if (bitmap_is_set(table->read_set, (*field_ptr)->field_index)) { + column_stats->histogram_ = NULL; + /* column_stats->histogram.set_size(hist_size); column_stats->histogram.set_type(hist_type); column_stats->histogram.set_values(histogram); - histogram+= hist_size; + histogram+= hist_size;*/ (*field_ptr)->collected_stats= column_stats++; } } @@ -2551,6 +2607,25 @@ bool Column_statistics_collected::add() } +/* + Create an empty Histogram object from histogram_type. + + Note: it is not yet clear whether collection-time histogram should be the same + as lookup-time histogram. At the moment, they are. +*/ + +Histogram* get_histogram_by_type(MEM_ROOT *mem_root, Histogram_type hist_type) { + switch (hist_type) { + case SINGLE_PREC_HB: + case DOUBLE_PREC_HB: + case JSON: + return new Histogram(); + default: + DBUG_ASSERT(0); + } + return NULL; +}; + /** @brief Get the results of aggregation when collecting the statistics on a column @@ -2560,7 +2635,7 @@ bool Column_statistics_collected::add() */ inline -void Column_statistics_collected::finish(ha_rows rows, double sample_fraction) +void Column_statistics_collected::finish(MEM_ROOT *mem_root, ha_rows rows, double sample_fraction) { double val; @@ -2578,10 +2653,19 @@ void Column_statistics_collected::finish(ha_rows rows, double sample_fraction) } if (count_distinct) { - uint hist_size= count_distinct->get_hist_size(); + //uint hist_size= count_distinct->get_hist_size(); + uint hist_size= current_thd->variables.histogram_size; + Histogram_type hist_type= (Histogram_type) (current_thd->variables.histogram_type); + bool have_histogram= false; + if (hist_size != 0 && hist_type != INVALID_HISTOGRAM) + { + have_histogram= true; + histogram_= new Histogram; + histogram_->init_for_collection(mem_root, hist_type, hist_size); + } /* Compute cardinality statistics and optionally histogram. */ - if (hist_size == 0) + if (!have_histogram) count_distinct->walk_tree(); else count_distinct->walk_tree_with_histogram(rows - nulls); @@ -2619,13 +2703,14 @@ void Column_statistics_collected::finish(ha_rows rows, double sample_fraction) set_not_null(COLUMN_STAT_AVG_FREQUENCY); } else - hist_size= 0; - histogram.set_size(hist_size); + have_histogram= false ; // TODO: need this? + //histogram.set_size(hist_size); set_not_null(COLUMN_STAT_HIST_SIZE); - if (hist_size && distincts) + if (have_histogram && distincts) { set_not_null(COLUMN_STAT_HIST_TYPE); - histogram.set_values(count_distinct->get_histogram()); + //histogram.set_values(count_distinct->get_histogram()); + histogram_= count_distinct->get_histogram(); set_not_null(COLUMN_STAT_HISTOGRAM); } delete count_distinct; @@ -2887,7 +2972,7 @@ int collect_statistics_for_table(THD *thd, TABLE *table) continue; bitmap_set_bit(table->write_set, table_field->field_index); if (!rc) - table_field->collected_stats->finish(rows, sample_fraction); + table_field->collected_stats->finish(&table->mem_root, rows, sample_fraction); else table_field->collected_stats->cleanup(); } @@ -3093,16 +3178,19 @@ int read_statistics_for_table(THD *thd, TABLE *table, TABLE_LIST *stat_tables) /* Read statistics from the statistical table column_stats */ stat_table= stat_tables[COLUMN_STAT].table; - ulong total_hist_size= 0; + //ulong total_hist_size= 0; + bool have_histograms= false; Column_stat column_stat(stat_table, table); for (field_ptr= table_share->field; *field_ptr; field_ptr++) { table_field= *field_ptr; column_stat.set_key_fields(table_field); column_stat.get_stat_values(); - total_hist_size+= table_field->read_stats->histogram.get_size(); + //total_hist_size+= table_field->read_stats->histogram.get_size(); + if (table_field->read_stats->histogram_type_on_disk != INVALID_HISTOGRAM) + have_histograms= true; } - table_share->stats_cb.total_hist_size= total_hist_size; + table_share->stats_cb.total_hist_size= have_histograms? 1:0; // total_hist_size /* Read statistics from the statistical table index_stats */ stat_table= stat_tables[INDEX_STAT].table; @@ -3239,28 +3327,36 @@ int read_histograms_for_table(THD *thd, TABLE *table, TABLE_LIST *stat_tables) { TABLE_STATISTICS_CB *stats_cb= &table->s->stats_cb; DBUG_ENTER("read_histograms_for_table"); - + + // histograms-todo: why do we use synchronization here, when we load + // histogram for the TABLE object, not TABLE_SHARE? + // is it because of the use of stats_cb->mem_root? if (stats_cb->start_histograms_load()) { - uchar *histogram= (uchar *) alloc_root(&stats_cb->mem_root, - stats_cb->total_hist_size); + //uchar *histogram= (uchar *) alloc_root(&stats_cb->mem_root, + // stats_cb->total_hist_size); + /* if (!histogram) { stats_cb->abort_histograms_load(); DBUG_RETURN(1); } - memset(histogram, 0, stats_cb->total_hist_size); + */ + //memset(histogram, 0, stats_cb->total_hist_size); Column_stat column_stat(stat_tables[COLUMN_STAT].table, table); for (Field **field_ptr= table->s->field; *field_ptr; field_ptr++) { Field *table_field= *field_ptr; - if (uint hist_size= table_field->read_stats->histogram.get_size()) + //if (uint hist_size= table_field->read_stats->histogram.get_size()) + if (table_field->read_stats->histogram_type_on_disk != INVALID_HISTOGRAM) { column_stat.set_key_fields(table_field); - table_field->read_stats->histogram.set_values(histogram); - column_stat.get_histogram_value(); - histogram+= hist_size; + //table_field->read_stats->histogram.set_values(histogram); + + table_field->read_stats->histogram_= + column_stat.load_histogram(&stats_cb->mem_root); + //histogram+= hist_size; } } stats_cb->end_histograms_load(); @@ -3952,8 +4048,8 @@ double get_column_range_cardinality(Field *field, if (avg_frequency > 1.0 + 0.000001 && col_stats->min_max_values_are_provided()) { - Histogram *hist= &col_stats->histogram; - if (hist->is_usable(thd)) + Histogram *hist= col_stats->histogram_; + if (hist && hist->is_usable(thd)) { store_key_image_to_rec(field, (uchar *) min_endp->key, field->key_length()); @@ -3996,8 +4092,8 @@ double get_column_range_cardinality(Field *field, else max_mp_pos= 1.0; - Histogram *hist= &col_stats->histogram; - if (hist->is_usable(thd)) + Histogram *hist= col_stats->histogram_; + if (hist && hist->is_usable(thd)) sel= hist->range_selectivity(min_mp_pos, max_mp_pos); else sel= (max_mp_pos - min_mp_pos); diff --git a/sql/sql_statistics.h b/sql/sql_statistics.h index a554721d50b13..178bc11a2784f 100644 --- a/sql/sql_statistics.h +++ b/sql/sql_statistics.h @@ -43,7 +43,8 @@ enum enum_histogram_type { SINGLE_PREC_HB, DOUBLE_PREC_HB, - JSON + JSON, + INVALID_HISTOGRAM } Histogram_type; enum enum_stat_tables @@ -141,40 +142,70 @@ double get_column_range_cardinality(Field *field, bool is_stat_table(const LEX_CSTRING *db, LEX_CSTRING *table); bool is_eits_usable(Field* field); -class Histogram +/* + Common base for all histograms +*/ +class Histogram_base : public Sql_alloc { +public: + virtual bool parse(MEM_ROOT *mem_root, Histogram_type type_arg, + const uchar *ptr, uint size)= 0; + virtual void serialize(Field *to_field)= 0; -private: - Histogram_type type; - uint8 size; /* Size of values array, in bytes */ - uchar *values; + virtual Histogram_type get_type()=0; + + // Legacy: return the size of the histogram on disk. + // This will be stored in mysql.column_stats.hist_size column. + // Newer, JSON-based histograms may return 0. + virtual uint get_size()=0; - uint prec_factor() + virtual ~Histogram_base(){} +}; + +class Histogram : public Histogram_base +{ +public: + bool parse(MEM_ROOT *mem_root, Histogram_type type_arg, + const uchar *ptr_arg, uint size_arg) override; + void serialize(Field *to_field) override; + Histogram_type get_type() override { return type; } + + uint get_size() override { return (uint) size; } + + // returns number of buckets in the histogram + uint get_width() { switch (type) { case SINGLE_PREC_HB: case JSON: - return ((uint) (1 << 8) - 1); + return size; case DOUBLE_PREC_HB: - return ((uint) (1 << 16) - 1); + return size / 2; + default: + DBUG_ASSERT(0); } - return 1; + return 0; } -public: - uint get_width() +private: + Histogram_type type; + uint8 size; /* Size of values array, in bytes */ + uchar *values; + + uint prec_factor() { switch (type) { case SINGLE_PREC_HB: case JSON: - return size; + return ((uint) (1 << 8) - 1); case DOUBLE_PREC_HB: - return size / 2; + return ((uint) (1 << 16) - 1); + default: + DBUG_ASSERT(0); } - return 0; + return 1; } -private: uint get_value(uint i) { DBUG_ASSERT(i < get_width()); @@ -184,6 +215,8 @@ class Histogram return (uint) (((uint8 *) values)[i]); case DOUBLE_PREC_HB: return (uint) uint2korr(values + i * 2); + default: + DBUG_ASSERT(0); } return 0; } @@ -227,19 +260,13 @@ class Histogram return i; } -public: - - uint get_size() { return (uint) size; } - - Histogram_type get_type() { return type; } - uchar *get_values() { return (uchar *) values; } +public: + void init_for_collection(MEM_ROOT *mem_root, Histogram_type htype_arg, ulonglong size); - void set_size (ulonglong sz) { size= (uint8) sz; } - - void set_type (Histogram_type t) { type= t; } - + // Note: these two are used only for saving the JSON text: void set_values (uchar *vals) { values= (uchar *) vals; } + void set_size (ulonglong sz) { size= (uint8) sz; } bool is_available() { return get_size() > 0 && get_values(); } @@ -264,6 +291,9 @@ class Histogram case DOUBLE_PREC_HB: int2store(values + i * 2, val * prec_factor()); return; + default: + DBUG_ASSERT(0); + return; } } @@ -277,6 +307,9 @@ class Histogram case DOUBLE_PREC_HB: int2store(values + i * 2, uint2korr(values + i * 2 - 2)); return; + default: + DBUG_ASSERT(0); + return; } } @@ -314,7 +347,7 @@ class Table_statistics /* Array of records per key for index prefixes */ ulonglong *idx_avg_frequency; - uchar *histograms; /* Sequence of histograms */ + //uchar *histograms; /* Sequence of histograms */ }; @@ -377,7 +410,8 @@ class Column_statistics public: - Histogram histogram; + Histogram_type histogram_type_on_disk; + Histogram *histogram_; uint32 no_values_provided_bitmap() { diff --git a/sql/table.h b/sql/table.h index 2e074abcea0ef..f557f4ca59e9b 100644 --- a/sql/table.h +++ b/sql/table.h @@ -679,7 +679,15 @@ class TABLE_STATISTICS_CB public: MEM_ROOT mem_root; /* MEM_ROOT to allocate statistical data for the table */ Table_statistics *table_stats; /* Structure to access the statistical data */ - ulong total_hist_size; /* Total size of all histograms */ + + /* + Total size of all histograms. A value of 0 means historams are not present, + and histograms_are_ready() can finish sooner. + + Currently we just set it to 1 when we expect to load histograms. + histogram-todo: rename this or even remove? + */ + ulong total_hist_size; bool histograms_are_ready() const {